Skip to content

Instantly share code, notes, and snippets.

@sergey-shambir
Created May 28, 2018 06:16
Show Gist options
  • Save sergey-shambir/5c02afde96bd728e04ce76a7a7866638 to your computer and use it in GitHub Desktop.
Save sergey-shambir/5c02afde96bd728e04ce76a7a7866638 to your computer and use it in GitHub Desktop.
Correct Utah Teapot normals calculation
MeshDataP3N3 utils::tesselateTeapot(const Material &material, unsigned latitudeDivisions, unsigned longitudeDivisions)
{
const size_t pointCount = std::size(kTeapotPatches) * (latitudeDivisions + 1) * (longitudeDivisions + 1);
const size_t vertexCount = 6 * std::size(kTeapotPatches) * latitudeDivisions * longitudeDivisions;
// TODO: (cg14.3) замените тип элемента в массиве на VertexP3N3.
std::vector<VertexP3N3> points(pointCount);
// Veticies
for (size_t p = 0; p < std::size(kTeapotPatches); p++)
{
vec3 controlPonts[kTeapotPatchOrder + 1][kTeapotPatchOrder + 1];
buildControlPoints(p, controlPonts);
for (size_t ru = 0; ru <= latitudeDivisions - 1; ru++)
{
float u = 1.0f * ru / (latitudeDivisions - 1);
for (size_t rv = 0; rv <= longitudeDivisions - 1; rv++)
{
float v = 1.0f * rv / (longitudeDivisions - 1);
size_t index = p * latitudeDivisions * longitudeDivisions + ru * longitudeDivisions + rv;
points[index].position = computePosition(controlPonts, u, v);
}
}
}
const auto getPoint = [&](unsigned patchIndex, unsigned ru, unsigned rv) -> VertexP3N3& {
size_t index = patchIndex * latitudeDivisions * longitudeDivisions + ru * longitudeDivisions + rv;
return points.at(index);
};
// TODO: (cg14.3) добавьте цикл тройной вложенности, подобный циклу ниже, для расчёта нормалей
// Добавьте отдельную обработку точек в случаях:
// 1) "ru = latitudeDivisions - 1"
// 2) "rv = longitudeDivisions - 1"
// 3) "ru = latitudeDivisions - 1" и "rv = longitudeDivisions - 1"
for (unsigned p = 0; p < std::size(kTeapotPatches); p++)
{
for (unsigned ru = 0; ru < latitudeDivisions - 1; ru++)
{
for (unsigned rv = 0; rv < longitudeDivisions - 1; rv++)
{
const VertexP3N3& pointA = getPoint(p, ru, rv);
const VertexP3N3& pointB = getPoint(p, ru, rv + 1);
const VertexP3N3& pointC = getPoint(p, ru + 1, rv);
VertexP3N3& target = getPoint(p, ru, rv);
target.normal = GetTriangleNormal(pointA.position, pointB.position, pointC.position);
}
}
{
unsigned ru = latitudeDivisions - 2;
for (unsigned rv = 0; rv < longitudeDivisions - 1; rv++)
{
const VertexP3N3& pointA = getPoint(p, ru - 1, rv);
const VertexP3N3& pointB = getPoint(p, ru - 1, rv + 1);
const VertexP3N3& pointC = getPoint(p, ru, rv);
VertexP3N3& target = getPoint(p, ru + 1, rv);
target.normal = GetTriangleNormal(pointA.position, pointB.position, pointC.position);
}
}
{
unsigned rv = longitudeDivisions - 2;
for (unsigned ru = 0; ru < latitudeDivisions - 1; ru++)
{
const VertexP3N3& pointA = getPoint(p, ru, rv);
const VertexP3N3& pointB = getPoint(p, ru, rv + 1);
const VertexP3N3& pointC = getPoint(p, ru + 1, rv);
VertexP3N3& target = getPoint(p, ru, rv + 1);
target.normal = GetTriangleNormal(pointA.position, pointB.position, pointC.position);
}
}
{
unsigned ru = latitudeDivisions - 2;
unsigned rv = longitudeDivisions - 2;
const VertexP3N3& pointA = getPoint(p, ru, rv);
const VertexP3N3& pointB = getPoint(p, ru, rv + 1);
const VertexP3N3& pointC = getPoint(p, ru + 1, rv);
VertexP3N3& target = getPoint(p, ru + 1, rv + 1);
target.normal = GetTriangleNormal(pointA.position, pointB.position, pointC.position);
}
}
MeshDataP3N3 result;
result.primitive = gl::GL_TRIANGLES;
result.material = material;
// Каждая вершина будет уникальной из-за различных нормалей, поэтому
// индексы будут изменяться последовательно от 0 до vertexCount-1
result.indicies.resize(vertexCount);
std::iota(result.indicies.begin(), result.indicies.end(), 0);
// На основе точек и вычисленных нормалей формируем вершины отдельных треугольников.
result.vertexes.reserve(vertexCount);
for (unsigned p = 0; p < std::size(kTeapotPatches); p++)
{
for (unsigned ru = 0; ru < latitudeDivisions - 1; ru++)
{
for (unsigned rv = 0; rv < longitudeDivisions - 1; rv++)
{
// Один фрагмент ABCD делится на два треугольника ABC + CDA
// На каждой итерации мы добавляем два треугольника.
// Выделяем точки фрагмента из массива "points".
const VertexP3N3 pointA = getPoint(p, ru, rv);
const VertexP3N3 pointB = getPoint(p, ru, rv + 1);
const VertexP3N3 pointC = getPoint(p, ru + 1, rv + 1);
const VertexP3N3 pointD = getPoint(p, ru + 1, rv);
// Формируем треугольник ABC
result.vertexes.push_back(pointA);
result.vertexes.push_back(pointB);
result.vertexes.push_back(pointC);
// Формируем треугольник CDA
result.vertexes.push_back(pointC);
result.vertexes.push_back(pointD);
result.vertexes.push_back(pointA);
}
}
}
#if !defined(NDEBUG)
// Проверяем все вершины - значения координат должны действительными числами.
for (const VertexP3N3& v : result.vertexes)
{
assert(std::isfinite(v.position.x));
assert(std::isfinite(v.position.y));
assert(std::isfinite(v.position.z));
assert(std::isfinite(v.normal.x));
assert(std::isfinite(v.normal.y));
assert(std::isfinite(v.normal.z));
}
#endif
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment