void GeneratePipe(const PipeDescriptor& desc, TriangleMesh& mesh) { const auto segsHorz = std::max(3u, desc.mantleSegments.x); const auto segsVert = std::max(1u, desc.mantleSegments.y); const auto invHorz = Gs::Real(1) / static_cast<Gs::Real>(segsHorz); const auto invVert = Gs::Real(1) / static_cast<Gs::Real>(segsVert); const auto angleSteps = invHorz * pi_2; const auto halfHeight = desc.height*Gs::Real(0.5); /* Generate outer- and inner mantle vertices */ Gs::Vector3 coord, normal, coordAlt; Gs::Vector2 texCoord; auto angle = Gs::Real(0); const Gs::Vector2 radii[2] = { desc.outerRadius, desc.innerRadius }; const Gs::Real faceSide[2] = { 1, -1 }; VertexIndex mantleIndexOffset[2] = { 0 }; for (std::size_t i = 0; i < 2; ++i) { mantleIndexOffset[i] = mesh.vertices.size(); angle = Gs::Real(0); for (unsigned int u = 0; u <= segsHorz; ++u) { /* Compute X- and Z coordinates */ texCoord.x = std::sin(angle); texCoord.y = std::cos(angle); coord.x = texCoord.x * radii[i].x; coord.z = texCoord.y * radii[i].y; /* Compute normal vector */ normal.x = texCoord.x; normal.y = 0; normal.z = texCoord.y; normal.Normalize(); /* Add top and bottom vertex */ texCoord.x = static_cast<Gs::Real>(segsHorz - u) * invHorz; for (unsigned int v = 0; v <= segsVert; ++v) { texCoord.y = static_cast<Gs::Real>(v) * invVert; coord.y = Gs::Lerp(halfHeight, -halfHeight, texCoord.y); mesh.AddVertex(coord, normal * faceSide[i], texCoord); } /* Increase angle for the next iteration */ angle += angleSteps; } } /* Generate bottom and top cover vertices */ const unsigned int segsCov[2] = { desc.bottomCoverSegments, desc.topCoverSegments }; const Gs::Real coverSide[2] = { -1, 1 }; VertexIndex coverIndexOffset[2] = { 0 }; for (std::size_t i = 0; i < 2; ++i) { if (segsCov[i] == 0) continue; angle = Gs::Real(0); const auto invCov = Gs::Real(1) / static_cast<Gs::Real>(segsCov[i]); const auto invRadius = Gs::Vector2(1) / (desc.outerRadius * Gs::Real(2.0)); coord.y = halfHeight * coverSide[i]; coordAlt.y = halfHeight * coverSide[i]; coverIndexOffset[i] = mesh.vertices.size(); for (unsigned int u = 0; u <= segsHorz; ++u) { /* Compute X- and Z coordinates */ texCoord.x = std::sin(angle); texCoord.y = std::cos(angle); coord.x = texCoord.x * desc.outerRadius.x; coord.z = texCoord.y * desc.outerRadius.y; coordAlt.x = texCoord.x * desc.innerRadius.x; coordAlt.z = texCoord.y * desc.innerRadius.y; /* Add vertex around the top and bottom */ for (unsigned int v = 0; v <= segsCov[i]; ++v) { auto interp = static_cast<Gs::Real>(v) * invCov; auto texCoordA = Gs::Vector2(Gs::Real(0.5)) + Gs::Vector2(coordAlt.x, coordAlt.z) * invRadius; auto texCoordB = Gs::Vector2(Gs::Real(0.5)) + Gs::Vector2(coord.x, coord.z) * invRadius; if (i == 1) { texCoordA.y = Gs::Real(1) - texCoordA.y; texCoordB.y = Gs::Real(1) - texCoordB.y; } mesh.AddVertex( Gs::Lerp(coordAlt, coord, interp), Gs::Vector3(0, coverSide[i], 0), Gs::Lerp(texCoordA, texCoordB, interp) ); } /* Increase angle for the next iteration */ angle += angleSteps; } } /* Generate indices for the outer mantle */ VertexIndex idxOffset = 0; for (std::size_t i = 0; i < 2; ++i) { idxOffset = mantleIndexOffset[i]; for (unsigned int u = 0; u < segsHorz; ++u) { for (unsigned int v = 0; v < segsVert; ++v) { auto i0 = v + 1 + segsVert; auto i1 = v; auto i2 = v + 1; auto i3 = v + 2 + segsVert; if (i == 0) AddTriangulatedQuad(mesh, desc.alternateGrid, u, v, i0, i1, i2, i3, idxOffset); else AddTriangulatedQuad(mesh, desc.alternateGrid, u, v, i1, i0, i3, i2, idxOffset); } idxOffset += (1 + segsVert); } } /* Generate indices for the bottom and top */ for (std::size_t i = 0; i < 2; ++i) { if (segsCov[i] == 0) continue; idxOffset = coverIndexOffset[i]; for (unsigned int u = 0; u < segsHorz; ++u) { for (unsigned int v = 0; v < segsCov[i]; ++v) { auto i1 = v + 1 + segsCov[i]; auto i0 = v; auto i3 = v + 1; auto i2 = v + 2 + segsCov[i]; if (i == 0) AddTriangulatedQuad(mesh, desc.alternateGrid, u, v, i0, i1, i2, i3, idxOffset); else AddTriangulatedQuad(mesh, desc.alternateGrid, u, v, i1, i0, i3, i2, idxOffset); } idxOffset += segsCov[i] + 1; } } }
void GenerateBezierPatch(const BezierPatchDescriptor& desc, TriangleMesh& mesh) { auto idxOffset = mesh.vertices.size(); const auto segsHorz = std::max(1u, desc.segments.x); const auto segsVert = std::max(1u, desc.segments.y); const auto invHorz = Gs::Real(1) / static_cast<Gs::Real>(segsHorz); const auto invVert = Gs::Real(1) / static_cast<Gs::Real>(segsVert); auto AddQuad = [&](unsigned int u, unsigned int v, VertexIndex i0, VertexIndex i1, VertexIndex i2, VertexIndex i3) { if (desc.backFacing) AddTriangulatedQuad(mesh, desc.alternateGrid, u, v, i1, i0, i3, i2, idxOffset); else AddTriangulatedQuad(mesh, desc.alternateGrid, u, v, i0, i1, i2, i3, idxOffset); }; /* Generate vertices */ static const Gs::Real delta = Gs::Real(0.01); Gs::Vector3 coord, normal; Gs::Vector2 texCoord; for (unsigned int i = 0; i <= segsVert; ++i) { for (unsigned int j = 0; j <= segsHorz; ++j) { /* Compute coordinate and texture-coordinate */ texCoord.x = static_cast<Gs::Real>(j) * invHorz; texCoord.y = static_cast<Gs::Real>(i) * invVert; coord = desc.bezierPatch(texCoord.x, texCoord.y); /* Sample bezier patch to approximate normal */ auto uOffset = (desc.bezierPatch(texCoord.x + delta, texCoord.y) - coord); auto vOffset = (desc.bezierPatch(texCoord.x, texCoord.y + delta) - coord); normal = Gs::Cross(uOffset, vOffset).Normalized(); /* Add vertex */ if (!desc.backFacing) { texCoord.y = Gs::Real(1) - texCoord.y; normal = -normal; } mesh.AddVertex(coord, normal, texCoord); } } /* Generate indices */ const auto strideHorz = segsHorz + 1; for (unsigned int v = 0; v < segsVert; ++v) { for (unsigned int u = 0; u < segsHorz; ++u) { AddQuad( u, v, ( v *strideHorz + u ), ( (v+1)*strideHorz + u ), ( (v+1)*strideHorz + u+1 ), ( v *strideHorz + u+1 ) ); } } }