<NME> vertex_data.cpp <BEF> #include <babylon/meshes/vertex_data.h> #include <babylon/babylon_stl_util.h> #include <babylon/core/json_util.h> #include <babylon/core/logging.h> #include <babylon/engines/engine.h> #include <babylon/maths/axis.h> #include <babylon/maths/vector2.h> #include <babylon/maths/vector3.h> #include <babylon/meshes/builders/mesh_builder_options.h> #include <babylon/meshes/facet_parameters.h> #include <babylon/meshes/geometry.h> #include <babylon/meshes/vertex_buffer.h> #include <babylon/misc/tools.h> namespace BABYLON { VertexData::VertexData() = default; VertexData::~VertexData() = default; void VertexData::set(const Float32Array& data, const std::string& kind) { if (data.empty()) { BABYLON_LOGF_WARN("VertexData", "Setting vertex data kind '%s' with an empty array", kind.c_str()) } if (kind == VertexBuffer::PositionKind) { positions = data; } else if (kind == VertexBuffer::NormalKind) { normals = data; } else if (kind == VertexBuffer::TangentKind) { tangents = data; } else if (kind == VertexBuffer::UVKind) { uvs = data; } else if (kind == VertexBuffer::UV2Kind) { uvs2 = data; } else if (kind == VertexBuffer::UV3Kind) { uvs3 = data; } else if (kind == VertexBuffer::UV4Kind) { uvs4 = data; } else if (kind == VertexBuffer::UV5Kind) { uvs5 = data; } else if (kind == VertexBuffer::UV6Kind) { uvs6 = data; } else if (kind == VertexBuffer::ColorKind) { colors = data; } else if (kind == VertexBuffer::MatricesIndicesKind) { matricesIndices = data; } else if (kind == VertexBuffer::MatricesWeightsKind) { matricesWeights = data; } else if (kind == VertexBuffer::MatricesIndicesExtraKind) { matricesIndicesExtra = data; } else if (kind == VertexBuffer::MatricesWeightsExtraKind) { matricesWeightsExtra = data; } } VertexData& VertexData::applyToMesh(Mesh& mesh, const std::optional<bool>& updatable) { _applyTo(mesh, updatable); return *this; } VertexData& VertexData::applyToGeometry(Geometry& geometry, bool updatable) { return *this; } VertexData& VertexData::updateMesh(Mesh* mesh, bool updateExtends, bool makeItUnique) { _update(mesh, updateExtends, makeItUnique); return *this; } VertexData& VertexData::updateGeometry(Geometry* geometry, bool updateExtends, bool makeItUnique) { _update(geometry, updateExtends, makeItUnique); return *this; } VertexData& VertexData::_applyTo(IGetSetVerticesData& meshOrGeometry, const std::optional<bool>& iUpdatable) { const auto updatable = iUpdatable.value_or(false); if (!positions.empty()) { meshOrGeometry.setVerticesData(VertexBuffer::PositionKind, positions, updatable); } if (!normals.empty()) { meshOrGeometry.setVerticesData(VertexBuffer::NormalKind, normals, updatable); } if (!tangents.empty()) { meshOrGeometry.setVerticesData(VertexBuffer::TangentKind, tangents, updatable); } if (!uvs.empty()) { meshOrGeometry.setVerticesData(VertexBuffer::UVKind, uvs, updatable); } if (!uvs2.empty()) { meshOrGeometry.setVerticesData(VertexBuffer::UV2Kind, uvs2, updatable); } if (!uvs3.empty()) { meshOrGeometry.setVerticesData(VertexBuffer::UV3Kind, uvs3, updatable); } if (!uvs4.empty()) { meshOrGeometry.setVerticesData(VertexBuffer::UV4Kind, uvs4, updatable); } if (!uvs5.empty()) { meshOrGeometry.setVerticesData(VertexBuffer::UV5Kind, uvs5, updatable); } if (!uvs6.empty()) { meshOrGeometry.setVerticesData(VertexBuffer::UV6Kind, uvs6, updatable); } if (!colors.empty()) { meshOrGeometry.setVerticesData(VertexBuffer::ColorKind, colors, updatable); } if (!matricesIndices.empty()) { meshOrGeometry.setVerticesData(VertexBuffer::MatricesIndicesKind, matricesIndices, updatable); } if (!matricesWeights.empty()) { meshOrGeometry.setVerticesData(VertexBuffer::MatricesWeightsKind, matricesWeights, updatable); } if (!matricesIndicesExtra.empty()) { meshOrGeometry.setVerticesData(VertexBuffer::MatricesIndicesExtraKind, matricesIndicesExtra, updatable); } if (!matricesWeightsExtra.empty()) { meshOrGeometry.setVerticesData(VertexBuffer::MatricesWeightsExtraKind, matricesWeightsExtra, updatable); } if (!indices.empty()) { meshOrGeometry.setIndices(indices, 0, updatable); } else { meshOrGeometry.setIndices({}, 0); } return *this; } VertexData& VertexData::_update(IGetSetVerticesData* meshOrGeometry, bool updateExtends, bool makeItUnique) { if (!positions.empty()) { meshOrGeometry->updateVerticesData(VertexBuffer::PositionKind, positions, updateExtends, makeItUnique); } if (!normals.empty()) { meshOrGeometry->updateVerticesData(VertexBuffer::NormalKind, normals, updateExtends, makeItUnique); } if (!tangents.empty()) { meshOrGeometry->updateVerticesData(VertexBuffer::TangentKind, tangents, updateExtends, makeItUnique); } if (!uvs.empty()) { meshOrGeometry->updateVerticesData(VertexBuffer::UVKind, uvs, updateExtends, makeItUnique); } if (!uvs2.empty()) { meshOrGeometry->updateVerticesData(VertexBuffer::UV2Kind, uvs2, updateExtends, makeItUnique); } if (!uvs3.empty()) { meshOrGeometry->updateVerticesData(VertexBuffer::UV3Kind, uvs3, updateExtends, makeItUnique); } if (!uvs4.empty()) { meshOrGeometry->updateVerticesData(VertexBuffer::UV4Kind, uvs4, updateExtends, makeItUnique); } if (!uvs5.empty()) { meshOrGeometry->updateVerticesData(VertexBuffer::UV5Kind, uvs5, updateExtends, makeItUnique); } if (!uvs6.empty()) { meshOrGeometry->updateVerticesData(VertexBuffer::UV6Kind, uvs6, updateExtends, makeItUnique); } if (!colors.empty()) { meshOrGeometry->updateVerticesData(VertexBuffer::ColorKind, colors, updateExtends, makeItUnique); } if (!matricesIndices.empty()) { meshOrGeometry->updateVerticesData(VertexBuffer::MatricesIndicesKind, matricesIndices, updateExtends, makeItUnique); } if (!matricesWeights.empty()) { meshOrGeometry->updateVerticesData(VertexBuffer::MatricesWeightsKind, matricesWeights, updateExtends, makeItUnique); } if (!matricesIndicesExtra.empty()) { meshOrGeometry->updateVerticesData(VertexBuffer::MatricesIndicesExtraKind, matricesIndicesExtra, updateExtends, makeItUnique); } if (!matricesWeightsExtra.empty()) { meshOrGeometry->updateVerticesData(VertexBuffer::MatricesWeightsExtraKind, matricesWeightsExtra, updateExtends, makeItUnique); } if (!indices.empty()) { meshOrGeometry->setIndices(indices, 0); } return *this; } VertexData& VertexData::transform(const Matrix& matrix) { const auto flip = matrix.determinant() < 0.f; auto transformed = Vector3::Zero(); if (!positions.empty()) { auto position = Vector3::Zero(); for (uint32_t index = 0; index < positions.size(); index += 3) { Vector3::FromArrayToRef(positions, index, position); Vector3::TransformCoordinatesToRef(position, matrix, transformed); positions[index + 0] = transformed.x; positions[index + 1] = transformed.y; positions[index + 2] = transformed.z; } } if (!normals.empty()) { auto normal = Vector3::Zero(); for (uint32_t index = 0; index < normals.size(); index += 3) { Vector3::FromArrayToRef(normals, index, normal); Vector3::TransformNormalToRef(normal, matrix, transformed); normals[index + 0] = transformed.x; normals[index + 1] = transformed.y; normals[index + 2] = transformed.z; } } if (!tangents.empty()) { auto tangent = Vector4::Zero(); auto tangentTransformed = Vector4::Zero(); for (uint32_t index = 0; index < tangents.size(); index += 4) { Vector4::FromArrayToRef(tangents, index, tangent); Vector4::TransformNormalToRef(tangent, matrix, tangentTransformed); tangents[index] = tangentTransformed.x; tangents[index + 1] = tangentTransformed.y; tangents[index + 2] = tangentTransformed.z; tangents[index + 3] = tangentTransformed.w; } } if (flip && !indices.empty()) { for (size_t index = 0; index < indices.size(); index += 3) { std::swap(indices[index + 1], indices[index + 2]); } } return *this; } VertexData& VertexData::merge(VertexData& other, bool /*use32BitsIndices*/) { _validate(); other._validate(); if (!other.indices.empty()) { uint32_t offset = (!positions.empty()) ? static_cast<unsigned>(positions.size()) / 3 : 0; for (auto& index : other.indices) { indices.emplace_back(static_cast<int32_t>(index + offset)); } } positions = _mergeElement(positions, other.positions); normals = _mergeElement(normals, other.normals); tangents = _mergeElement(tangents, other.tangents); uvs = _mergeElement(uvs, other.uvs); uvs2 = _mergeElement(uvs2, other.uvs2); uvs3 = _mergeElement(uvs3, other.uvs3); uvs4 = _mergeElement(uvs4, other.uvs4); uvs5 = _mergeElement(uvs5, other.uvs5); uvs6 = _mergeElement(uvs6, other.uvs6); colors = _mergeElement(colors, other.colors); matricesIndices = _mergeElement(matricesIndices, other.matricesIndices); matricesWeights = _mergeElement(matricesWeights, other.matricesWeights); matricesIndicesExtra = _mergeElement(matricesIndicesExtra, other.matricesIndicesExtra); matricesWeightsExtra = _mergeElement(matricesWeightsExtra, other.matricesWeightsExtra); return *this; } Float32Array VertexData::_mergeElement(const Float32Array& source, const Float32Array& other) const { if (source.empty()) { return other; } if (other.empty()) { return source; } // use non-loop method when the source is Float32Array Float32Array ret32 = source; stl_util::concat(ret32, other); return ret32; } void VertexData::_validate() { if (positions.empty()) { throw std::runtime_error("Positions are required"); } const auto getElementCount = [](const std::string& kind, const Float32Array& values) -> size_t { const auto stride = VertexBuffer::DeduceStride(kind); if ((values.size() % stride) != 0) { throw std::runtime_error("The " + kind + "s array count must be a multiple of " + std::to_string(stride)); } return values.size() / stride; }; const auto positionsElementCount = getElementCount(VertexBuffer::PositionKind, positions); const auto validateElementCount = [&](const std::string& kind, const Float32Array& values) { const auto elementCount = getElementCount(kind, values); if (elementCount != positionsElementCount) { throw std::runtime_error("The " + kind + "s element count (" + std::to_string(elementCount) + ") does not match the positions count (" + std::to_string(positionsElementCount) + ")"); } }; if (!normals.empty()) { validateElementCount(VertexBuffer::NormalKind, normals); } if (!tangents.empty()) { validateElementCount(VertexBuffer::TangentKind, tangents); } if (!uvs.empty()) { validateElementCount(VertexBuffer::UVKind, uvs); } if (!uvs2.empty()) { validateElementCount(VertexBuffer::UV2Kind, uvs2); } if (!uvs3.empty()) { validateElementCount(VertexBuffer::UV3Kind, uvs3); } if (!uvs4.empty()) { validateElementCount(VertexBuffer::UV4Kind, uvs4); } if (!uvs5.empty()) { validateElementCount(VertexBuffer::UV5Kind, uvs5); } if (!uvs6.empty()) { validateElementCount(VertexBuffer::UV6Kind, uvs6); } if (!colors.empty()) { validateElementCount(VertexBuffer::ColorKind, colors); } if (!matricesIndices.empty()) { validateElementCount(VertexBuffer::MatricesIndicesKind, matricesIndices); } if (!matricesWeights.empty()) { validateElementCount(VertexBuffer::MatricesWeightsKind, matricesWeights); } if (!matricesIndicesExtra.empty()) { validateElementCount(VertexBuffer::MatricesIndicesExtraKind, matricesIndicesExtra); } if (!matricesWeightsExtra.empty()) { validateElementCount(VertexBuffer::MatricesWeightsExtraKind, matricesWeightsExtra); } } json VertexData::serialize() const { return nullptr; } std::unique_ptr<VertexData> VertexData::ExtractFromMesh(Mesh* mesh, bool copyWhenShared, bool forceCopy) { return VertexData::_ExtractFrom(mesh, copyWhenShared, forceCopy); } std::unique_ptr<VertexData> VertexData::ExtractFromGeometry(Geometry* geometry, bool copyWhenShared, bool forceCopy) { return VertexData::_ExtractFrom(geometry, copyWhenShared, forceCopy); } std::unique_ptr<VertexData> VertexData::_ExtractFrom(IGetSetVerticesData* meshOrGeometry, bool copyWhenShared, bool forceCopy) { auto result = std::make_unique<VertexData>(); if (meshOrGeometry->isVerticesDataPresent(VertexBuffer::PositionKind)) { result->positions = meshOrGeometry->getVerticesData(VertexBuffer::PositionKind, copyWhenShared, forceCopy); } if (meshOrGeometry->isVerticesDataPresent(VertexBuffer::NormalKind)) { result->normals = meshOrGeometry->getVerticesData(VertexBuffer::NormalKind, copyWhenShared, forceCopy); } if (meshOrGeometry->isVerticesDataPresent(VertexBuffer::TangentKind)) { result->tangents = meshOrGeometry->getVerticesData(VertexBuffer::TangentKind, copyWhenShared, forceCopy); } if (meshOrGeometry->isVerticesDataPresent(VertexBuffer::UVKind)) { result->uvs = meshOrGeometry->getVerticesData(VertexBuffer::UVKind, copyWhenShared, forceCopy); } if (meshOrGeometry->isVerticesDataPresent(VertexBuffer::UV2Kind)) { result->uvs2 = meshOrGeometry->getVerticesData(VertexBuffer::UV2Kind, copyWhenShared, forceCopy); } if (meshOrGeometry->isVerticesDataPresent(VertexBuffer::UV3Kind)) { result->uvs3 = meshOrGeometry->getVerticesData(VertexBuffer::UV3Kind, copyWhenShared, forceCopy); } if (meshOrGeometry->isVerticesDataPresent(VertexBuffer::UV4Kind)) { result->uvs4 = meshOrGeometry->getVerticesData(VertexBuffer::UV4Kind, copyWhenShared, forceCopy); } if (meshOrGeometry->isVerticesDataPresent(VertexBuffer::UV5Kind)) { result->uvs5 = meshOrGeometry->getVerticesData(VertexBuffer::UV5Kind, copyWhenShared, forceCopy); } if (meshOrGeometry->isVerticesDataPresent(VertexBuffer::UV6Kind)) { result->uvs6 = meshOrGeometry->getVerticesData(VertexBuffer::UV6Kind, copyWhenShared, forceCopy); } if (meshOrGeometry->isVerticesDataPresent(VertexBuffer::ColorKind)) { result->colors = meshOrGeometry->getVerticesData(VertexBuffer::ColorKind, copyWhenShared, forceCopy); } if (meshOrGeometry->isVerticesDataPresent(VertexBuffer::MatricesIndicesKind)) { result->matricesIndices = meshOrGeometry->getVerticesData(VertexBuffer::MatricesIndicesKind, copyWhenShared, forceCopy); } if (meshOrGeometry->isVerticesDataPresent(VertexBuffer::MatricesWeightsKind)) { result->matricesWeights = meshOrGeometry->getVerticesData(VertexBuffer::MatricesWeightsKind, copyWhenShared, forceCopy); } if (meshOrGeometry->isVerticesDataPresent(VertexBuffer::MatricesIndicesExtraKind)) { result->matricesIndicesExtra = meshOrGeometry->getVerticesData( VertexBuffer::MatricesIndicesExtraKind, copyWhenShared, forceCopy); } if (meshOrGeometry->isVerticesDataPresent(VertexBuffer::MatricesWeightsExtraKind)) { result->matricesWeightsExtra = meshOrGeometry->getVerticesData( VertexBuffer::MatricesWeightsExtraKind, copyWhenShared, forceCopy); } result->indices = meshOrGeometry->getIndices(copyWhenShared, forceCopy); return result; } std::unique_ptr<VertexData> VertexData::CreateRibbon(RibbonOptions& options) { auto& pathArray = options.pathArray; const auto closeArray = options.closeArray.value_or(false); const auto closePath = options.closePath.value_or(false); const auto invertUV = options.invertUV.value_or(false); const auto defaultOffset = static_cast<size_t>(std::floor(pathArray[0].size() / 2)); auto offset = options.offset.value_or(defaultOffset); // offset max allowed : defaultOffset offset = offset > defaultOffset ? defaultOffset : static_cast<size_t>(std::floor(offset)); const auto sideOrientation = options.sideOrientation.value_or(VertexData::DEFAULTSIDE); const auto& customUV = options.uvs; const auto& customColors = options.colors; Float32Array positions; Uint32Array indices; Float32Array normals; Float32Array uvs; // us[path_id] = [uDist1, uDist2, uDist3 ... ] distances between points on path path_id std::vector<Float32Array> us; // vs[i] = [vDist1, vDist2, vDist3, ... ] distances between points i of consecutive paths from // pathArray std::vector<Float32Array> vs; // uTotalDistance[p] : total distance of path p Float32Array uTotalDistance; // vTotalDistance[i] : total distance between points i of first and last path from pathArray Float32Array vTotalDistance; // minimal length among all paths from pathArray size_t minlg; // array of path lengths : nb of vertex per path Uint32Array lg; lg.resize(pathArray.size()); // array of path indexes : index of each path (first vertex) in the total vertex number Uint32Array idx; idx.resize(pathArray.size()); size_t ul; size_t p; // path iterator size_t i; // point iterator size_t j; // point iterator // if single path in pathArray if (pathArray.size() < 2) { std::vector<Vector3> ar1; std::vector<Vector3> ar2; for (i = 0; i < pathArray[0].size() - offset; ++i) { ar1.emplace_back(pathArray[0][i]); ar2.emplace_back(pathArray[0][i + offset]); } pathArray = {ar1, ar2}; } // positions and horizontal distances (u) size_t idc = 0; // the final index will be +1 if closePath size_t closePathCorr = (closePath) ? 1 : 0; std::vector<Vector3> path; size_t l; minlg = pathArray[0].size(); float vectlg; float dist; for (p = 0; p < pathArray.size(); ++p) { uTotalDistance.emplace_back(0.f); us.emplace_back(0); path = pathArray[p]; l = path.size(); minlg = (minlg < l) ? minlg : l; j = 0; while (j < l) { stl_util::concat(positions, {path[j].x, path[j].y, path[j].z}); if (j > 0) { vectlg = path[j].subtract(path[j - 1]).length(); dist = vectlg + uTotalDistance[p]; us[p].emplace_back(dist); uTotalDistance[p] = dist; } ++j; } if (closePath) { // an extra hidden vertex is added in the "positions" array --j; stl_util::concat(positions, {path[0].x, path[0].y, path[0].z}); vectlg = path[j].subtract(path[0]).length(); dist = vectlg + uTotalDistance[p]; us[p].emplace_back(dist); uTotalDistance[p] = dist; } lg[p] = static_cast<uint32_t>(l + closePathCorr); idx[p] = static_cast<uint32_t>(idc); idc += (l + closePathCorr); } // vertical distances (v) std::vector<Vector3> path1; std::vector<Vector3> path2; std::optional<Vector3> vertex1 = std::nullopt; std::optional<Vector3> vertex2 = std::nullopt; for (i = 0, ul = minlg + closePathCorr; i < ul; ++i) { vTotalDistance.emplace_back(0.f); vs.emplace_back(0); for (p = 0; p < pathArray.size() - 1; ++p) { path1 = pathArray[p]; path2 = pathArray[p + 1]; if (i == minlg) { // closePath vertex1 = path1[0]; vertex2 = path2[0]; } else { vertex1 = path1[i]; vertex2 = path2[i]; } vectlg = (*vertex2).subtract(*vertex1).length(); dist = vectlg + vTotalDistance[i]; vs[i].emplace_back(dist); vTotalDistance[i] = dist; } if (closeArray && vertex2 && vertex1) { path1 = pathArray[p]; path2 = pathArray[0]; if (i == minlg) { // closePath vertex2 = path2[0]; } vectlg = (*vertex2).subtract(*vertex1).length(); dist = vectlg + vTotalDistance[i]; vTotalDistance[i] = dist; } } // uvs float u; float v; if (!customUV.empty()) { for (const auto& customUVi : customUV) { stl_util::concat(uvs, {customUVi.x, customUVi.y}); } } else { for (p = 0; p < pathArray.size(); ++p) { for (i = 0; i < minlg + closePathCorr; ++i) { u = (uTotalDistance[p] != 0.f) ? us[p][i % us[p].size()] / uTotalDistance[p] : 0.f; v = (vTotalDistance[i] != 0.f) ? vs[i][p % vs[i].size()] / vTotalDistance[i] : 0.f; if (invertUV) { stl_util::concat(uvs, {v, u}); } else { stl_util::concat(uvs, {u, v}); } } } } // indices p = 0; uint32_t pi = 0; uint32_t l1 = lg[p] - 1; // path1 length uint32_t l2 = lg[p + 1] - 1; // path2 length uint32_t min = (l1 < l2) ? l1 : l2; // current path stop index uint32_t shft = idx[1] - idx[0]; // shift size_t path1nb = closeArray ? lg.size() : lg.size() - 1; // number of path1 to iterate on while (pi <= min && p < path1nb) { // stay under min and don't go over next to last path // draw two triangles between path1 (p1) and path2 (p2) : (p1.pi, p2.pi, // p1.pi+1) and (p2.pi+1, p1.pi+1, p2.pi) clockwise stl_util::concat(indices, {pi, pi + shft, pi + 1}); stl_util::concat(indices, {pi + shft + 1, pi + 1, pi + shft}); pi += 1; if (pi == min) { // if end of one of two consecutive paths reached, go to next existing // path ++p; if (p == lg.size()) continue; // this is not in the js code, but is a probable js code bug if (p == lg.size() - 1) { // last path of pathArray reached <=> closeArray == true shft = idx[0] - idx[p]; l1 = lg[p] - 1; l2 = lg[0] - 1; } else { shft = idx[p + 1] - idx[p]; l1 = lg[p] - 1; l2 = lg[p + 1] - 1; } pi = idx[p]; min = (l1 < l2) ? l1 + pi : l2 + pi; } } // normals VertexData::ComputeNormals(positions, indices, normals); // update both the first and last vertex normals to their average value if (closePath) { size_t indexFirst = 0; size_t indexLast = 0; for (p = 0; p < pathArray.size(); ++p) { indexFirst = static_cast<size_t>(idx[p] * 3); if (p + 1 < pathArray.size()) { indexLast = static_cast<size_t>((idx[p + 1] - 1) * 3); } else { indexLast = normals.size() - 3; } normals[indexFirst] = (normals[indexFirst] + normals[indexLast]) * 0.5f; normals[indexFirst + 1] = (normals[indexFirst + 1] + normals[indexLast + 1]) * 0.5f; normals[indexFirst + 2] = (normals[indexFirst + 2] + normals[indexLast + 2]) * 0.5f; normals[indexLast] = normals[indexFirst]; normals[indexLast + 1] = normals[indexFirst + 1]; normals[indexLast + 2] = normals[indexFirst + 2]; } } // sides VertexData::_ComputeSides(sideOrientation, positions, indices, normals, uvs, options.frontUVs, options.backUVs); // Colors Float32Array colors; if (!customColors.empty()) { colors.resize(customColors.size() * 4); for (uint32_t c = 0; c < customColors.size(); ++c) { colors[c * 4] = customColors[c].r; colors[c * 4 + 1] = customColors[c].g; colors[c * 4 + 2] = customColors[c].b; colors[c * 4 + 3] = customColors[c].a; } } // Result auto vertexData = std::make_unique<VertexData>(); vertexData->indices = std::move(indices); vertexData->positions = std::move(positions); vertexData->normals = std::move(normals); vertexData->uvs = std::move(uvs); if (!colors.empty()) { vertexData->set(colors, VertexBuffer::ColorKind); } if (closePath) { vertexData->_idx = std::move(idx); } return vertexData; } std::unique_ptr<VertexData> VertexData::CreateBox(BoxOptions& options) { uint32_t nbFaces = 6; IndicesArray indices{0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23}; Float32Array normals{0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0}; Float32Array uvs; Float32Array positions; const auto width = options.width.value_or(options.size.value_or(1.f)); const auto height = options.height.value_or(options.size.value_or(1.f)); const auto depth = options.depth.value_or(options.size.value_or(1.f)); const auto wrap = options.wrap.value_or(false); auto topBaseAt = options.topBaseAt.value_or(1u); auto bottomBaseAt = options.bottomBaseAt.value_or(0u); topBaseAt = (topBaseAt + 4) % 4; // places values as 0 to 3 bottomBaseAt = (bottomBaseAt + 4) % 4; // places values as 0 to 3 Int32Array topOrder{2, 0, 3, 1}; Int32Array bottomOrder{2, 0, 1, 3}; auto topIndex = topOrder[topBaseAt]; auto bottomIndex = bottomOrder[bottomBaseAt]; Float32Array basePositions{1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1}; if (wrap) { indices = {2, 3, 0, 2, 0, 1, 4, 5, 6, 4, 6, 7, 9, 10, 11, 9, 11, 8, 12, 14, 15, 12, 13, 14}; basePositions = {-1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1}; std::vector<Float32Array> topFaceBase{{1, 1, 1}, {-1, 1, 1}, {-1, 1, -1}, {1, 1, -1}}; std::vector<Float32Array> bottomFaceBase{{-1, -1, 1}, {1, -1, 1}, {1, -1, -1}, {-1, -1, -1}}; IndicesArray topFaceOrder{17, 18, 19, 16}; IndicesArray bottomFaceOrder{22, 23, 20, 21}; while (topIndex > 0) { stl_util::push_front(topFaceBase, topFaceBase.back()); topFaceBase.pop_back(); stl_util::push_front(topFaceOrder, topFaceOrder.back()); topFaceOrder.pop_back(); --topIndex; } while (bottomIndex > 0) { stl_util::push_front(bottomFaceBase, bottomFaceBase.back()); bottomFaceBase.pop_back(); stl_util::push_front(bottomFaceOrder, bottomFaceOrder.back()); bottomFaceOrder.pop_back(); --bottomIndex; } auto topFaceBaseFlat = stl_util::flatten(topFaceBase); auto bottomFaceBaseFlat = stl_util::flatten(bottomFaceBase); stl_util::concat(basePositions, stl_util::concat(topFaceBaseFlat, bottomFaceBaseFlat)); stl_util::concat(indices, {topFaceOrder[0], topFaceOrder[2], topFaceOrder[3], topFaceOrder[0], topFaceOrder[1], topFaceOrder[2]}); stl_util::concat(indices, {bottomFaceOrder[0], bottomFaceOrder[2], bottomFaceOrder[3], bottomFaceOrder[0], bottomFaceOrder[1], bottomFaceOrder[2]}); } Float32Array scaleArray{width / 2, height / 2.f, depth / 2.f}; for (auto currentIndex = 0ull; currentIndex < basePositions.size(); ++currentIndex) { positions.emplace_back(basePositions[currentIndex] * scaleArray[currentIndex % 3]); } auto sideOrientation = options.sideOrientation.value_or(VertexData::DEFAULTSIDE); auto& faceUV = options.faceUV; auto& faceColors = options.faceColors; Float32Array colors; // default face colors and UV if undefined for (unsigned f = 0; f < 6; ++f) { if (!faceUV[f].has_value()) { faceUV[f] = Vector4(0.f, 0.f, 1.f, 1.f); } if (faceColors && !(*faceColors)[f].has_value()) { (*faceColors)[f] = Color4(1.f, 1.f, 1.f, 1.f); } } // Create each face in turn. for (auto index = 0u; index < nbFaces; index++) { stl_util::concat(uvs, {faceUV[index]->z, faceUV[index]->w}); stl_util::concat(uvs, {faceUV[index]->x, faceUV[index]->w}); stl_util::concat(uvs, {faceUV[index]->x, faceUV[index]->y}); stl_util::concat(uvs, {faceUV[index]->z, faceUV[index]->y}); if (faceColors) { const auto& _faceColors = *faceColors; for (auto c = 0u; c < 4; c++) { stl_util::concat(colors, {_faceColors[index]->r, _faceColors[index]->g, _faceColors[index]->b, _faceColors[index]->a}); } } } // sides VertexData::_ComputeSides(sideOrientation, positions, indices, normals, uvs, options.frontUVs, options.backUVs); // Result auto vertexData = std::make_unique<VertexData>(); vertexData->indices = std::move(indices); vertexData->positions = std::move(positions); vertexData->normals = std::move(normals); vertexData->uvs = std::move(uvs); if (faceColors) { auto totalColors = (sideOrientation == VertexData::DOUBLESIDE) ? stl_util::concat(colors, colors) : colors; vertexData->colors = totalColors; } return vertexData; } std::unique_ptr<VertexData> VertexData::CreateTiledBox(TiledBoxOptions& options) { const auto nbFaces = 6u; const auto& faceUV = options.faceUV; const auto& faceColors = options.faceColors; const auto flipTile = options.pattern.value_or(Mesh::NO_FLIP); const auto width = options.width.value_or(options.size.value_or(1.f)); const auto height = options.height.value_or(options.size.value_or(1.f)); const auto depth = options.depth.value_or(options.size.value_or(1.f)); const auto tileWidth = options.tileWidth || options.tileSize || 1; const auto tileHeight = options.tileHeight.value_or(options.tileSize.value_or(1.f)); const auto alignH = options.alignHorizontal.value_or(0u); const auto alignV = options.alignVertical.value_or(0u); const auto sideOrientation = options.sideOrientation.value_or(VertexData::DEFAULTSIDE); const auto halfWidth = width / 2.f; const auto halfHeight = height / 2.f; const auto halfDepth = depth / 2.f; std::vector<std::unique_ptr<VertexData>> faceVertexData; for (auto f = 0; f < 2; ++f) { // front and back TiledPlaneOptions tiledPlaneOptions; tiledPlaneOptions.pattern = flipTile; tiledPlaneOptions.tileWidth = tileWidth; tiledPlaneOptions.tileHeight = tileHeight; tiledPlaneOptions.width = width; tiledPlaneOptions.height = height; tiledPlaneOptions.alignVertical = alignV; tiledPlaneOptions.alignHorizontal = alignH; tiledPlaneOptions.sideOrientation = sideOrientation; faceVertexData.emplace_back(VertexData::CreateTiledPlane(tiledPlaneOptions)); } for (auto f = 2; f < 4; ++f) { // sides TiledPlaneOptions tiledPlaneOptions; tiledPlaneOptions.pattern = flipTile; tiledPlaneOptions.tileWidth = tileWidth; tiledPlaneOptions.tileHeight = tileHeight; tiledPlaneOptions.width = depth; tiledPlaneOptions.height = height; tiledPlaneOptions.alignVertical = alignV; tiledPlaneOptions.alignHorizontal = alignH; tiledPlaneOptions.sideOrientation = sideOrientation; faceVertexData.emplace_back(VertexData::CreateTiledPlane(tiledPlaneOptions)); } auto baseAlignV = alignV; if (alignV == Mesh::BOTTOM) { baseAlignV = Mesh::TOP; } else if (alignV == Mesh::TOP) { baseAlignV = Mesh::BOTTOM; } for (auto f = 4; f < 6; ++f) { // top and bottom TiledPlaneOptions tiledPlaneOptions; tiledPlaneOptions.pattern = flipTile; tiledPlaneOptions.tileWidth = tileWidth; tiledPlaneOptions.tileHeight = tileHeight; tiledPlaneOptions.width = width; tiledPlaneOptions.height = depth; tiledPlaneOptions.alignVertical = baseAlignV; tiledPlaneOptions.alignHorizontal = alignH; tiledPlaneOptions.sideOrientation = sideOrientation; faceVertexData.emplace_back(VertexData::CreateTiledPlane(tiledPlaneOptions)); } Float32Array positions; Float32Array normals; Float32Array uvs; Uint32Array indices; Float32Array colors; std::vector<std::vector<Vector3>> facePositions; std::vector<std::vector<Vector3>> faceNormals; std::vector<Float32Array> newFaceUV; auto lu = 0ull; size_t li = 0u; for (auto f = 0u; f < nbFaces; ++f) { const auto len = faceVertexData[f]->positions.size(); facePositions.emplace_back(std::vector<Vector3>{}); faceNormals.emplace_back(std::vector<Vector3>{}); for (auto p = 0u; p < len / 3; ++p) { stl_util::concat(facePositions[f], {Vector3(faceVertexData[f]->positions[3 * p], faceVertexData[f]->positions[3 * p + 1], faceVertexData[f]->positions[3 * p + 2])}); stl_util::concat(faceNormals[f], {Vector3(faceVertexData[f]->normals[3 * p], faceVertexData[f]->normals[3 * p + 1], faceVertexData[f]->normals[3 * p + 2])}); } // uvs lu = faceVertexData[f]->uvs.size(); newFaceUV.emplace_back(Float32Array{}); for (auto i = 0u; i < lu; i += 2) { stl_util::concat(newFaceUV[f], {faceUV[f].x + (faceUV[f].z - faceUV[f].x) * faceVertexData[f]->uvs[i], faceUV[f].y + (faceUV[f].w - faceUV[f].y) * faceVertexData[f]->uvs[i + 1]}); } stl_util::concat(uvs, newFaceUV[f]); for (const auto x : faceVertexData[f]->indices) { indices.emplace_back(static_cast<unsigned int>(x + li)); } li += facePositions[f].size(); if (!faceColors.empty()) { for (auto c = 0; c < 4; c++) { stl_util::concat(colors, {faceColors[f].r, faceColors[f].g, faceColors[f].b, faceColors[f].a}); } } } Vector3 vec0{0.f, 0.f, halfDepth}; const auto mtrx0 = Matrix::RotationY(Math::PI); for (const auto& entry : facePositions[0]) { const auto entryTmp = Vector3::TransformNormal(entry, mtrx0).add(vec0); stl_util::concat(positions, {entryTmp.x, entryTmp.y, entryTmp.z}); } for (const auto& entry : faceNormals[0]) { const auto entryTmp = Vector3::TransformNormal(entry, mtrx0); stl_util::concat(normals, {entryTmp.x, entryTmp.y, entryTmp.z}); } for (const auto& entry : facePositions[1]) { const auto entryTmp = entry.subtract(vec0); stl_util::concat(positions, {entryTmp.x, entryTmp.y, entryTmp.z}); } for (const auto& entry : faceNormals[1]) { stl_util::concat(normals, {entry.x, entry.y, entry.z}); } Vector3 vec2{halfWidth, 0.f, 0.f}; const auto mtrx2 = Matrix::RotationY(-Math::PI_2); for (const auto& entry : facePositions[2]) { const auto entryTmp = Vector3::TransformNormal(entry, mtrx2).add(vec2); stl_util::concat(positions, {entryTmp.x, entryTmp.y, entryTmp.z}); } for (const auto& entry : faceNormals[2]) { const auto entryTmp = Vector3::TransformNormal(entry, mtrx2); stl_util::concat(normals, {entryTmp.x, entryTmp.y, entryTmp.z}); } const auto mtrx3 = Matrix::RotationY(Math::PI_2); for (const auto& entry : facePositions[3]) { const auto entryTmp = Vector3::TransformNormal(entry, mtrx3).subtract(vec2); stl_util::concat(positions, {entryTmp.x, entryTmp.y, entryTmp.z}); } for (const auto& entry : faceNormals[3]) { const auto entryTmp = Vector3::TransformNormal(entry, mtrx3); stl_util::concat(normals, {entryTmp.x, entryTmp.y, entryTmp.z}); } Vector3 vec4{0.f, halfHeight, 0.f}; const auto mtrx4 = Matrix::RotationX(Math::PI_2); for (const auto& entry : facePositions[4]) { const auto entryTmp = Vector3::TransformNormal(entry, mtrx4).add(vec4); stl_util::concat(positions, {entryTmp.x, entryTmp.y, entryTmp.z}); } for (const auto& entry : faceNormals[4]) { const auto entryTmp = Vector3::TransformNormal(entry, mtrx4); stl_util::concat(normals, {entryTmp.x, entryTmp.y, entryTmp.z}); } const auto mtrx5 = Matrix::RotationX(-Math::PI_2); for (const auto& entry : facePositions[5]) { const auto entryTmp = Vector3::TransformNormal(entry, mtrx5).subtract(vec4); stl_util::concat(positions, {entryTmp.x, entryTmp.y, entryTmp.z}); } for (const auto& entry : faceNormals[5]) { const auto entryTmp = Vector3::TransformNormal(entry, mtrx5); stl_util::concat(normals, {entryTmp.x, entryTmp.y, entryTmp.z}); } // sides VertexData::_ComputeSides(sideOrientation, positions, indices, normals, uvs); // Result auto vertexData = std::make_unique<VertexData>(); vertexData->indices = std::move(indices); vertexData->positions = std::move(positions); vertexData->normals = std::move(normals); vertexData->uvs = std::move(uvs); if (!faceColors.empty()) { if (sideOrientation == VertexData::DOUBLESIDE) { stl_util::concat(colors, colors); } vertexData->colors = std::move(colors); } return vertexData; } std::unique_ptr<VertexData> VertexData::CreateTiledPlane(TiledPlaneOptions& options) { const auto flipTile = options.pattern.value_or(Mesh::NO_FLIP); const auto tileWidth = options.tileWidth.value_or(options.tileSize.value_or(1.f)); const auto tileHeight = options.tileHeight.value_or(options.tileSize.value_or(1.f)); const auto alignH = options.alignHorizontal.value_or(0u); const auto alignV = options.alignVertical.value_or(0u); const auto width = options.width.value_or(options.size.value_or(1.f)); const auto tilesX = static_cast<unsigned>(std::floor(width / tileWidth)); auto offsetX = width - tilesX * tileWidth; const auto height = options.height.value_or(options.size.value_or(1.f)); const auto tilesY = static_cast<unsigned>(std::floor(height / tileHeight)); auto offsetY = height - tilesY * tileHeight; const auto halfWidth = tileWidth * tilesX / 2.f; const auto halfHeight = tileHeight * tilesY / 2.f; auto adjustX = 0.f; auto adjustY = 0.f; auto startX = 0.f; auto startY = 0.f; auto endX = 0.f; auto endY = 0.f; // Part Tiles if (offsetX > 0 || offsetY > 0) { startX = -halfWidth; startY = -halfHeight; endX = halfWidth; endY = halfHeight; switch (alignH) { case Mesh::CENTER: offsetX /= 2.f; startX -= offsetX; endX += offsetX; break; case Mesh::LEFT: endX += offsetX; adjustX = -offsetX / 2.f; break; case Mesh::RIGHT: startX -= offsetX; adjustX = offsetX / 2.f; break; } switch (alignV) { case Mesh::CENTER: offsetY /= 2.f; startY -= offsetY; endY += offsetY; break; case Mesh::BOTTOM: endY += offsetY; adjustY = -offsetY / 2.f; break; case Mesh::TOP: startY -= offsetY; adjustY = offsetY / 2.f; break; } } Float32Array positions; Float32Array normals; std::array<Float32Array, 2> uvBase; uvBase[0] = {0.f, 0.f, 1.f, 0.f, 1.f, 1.f, 0.f, 1.f}; uvBase[1] = {0.f, 0.f, 1.f, 0.f, 1.f, 1.f, 0.f, 1.f}; if (flipTile == Mesh::ROTATE_TILE || flipTile == Mesh::ROTATE_ROW) { uvBase[1] = {1.f, 1.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f}; } if (flipTile == Mesh::FLIP_TILE || flipTile == Mesh::FLIP_ROW) { uvBase[1] = {1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 1.f, 1.f}; } if (flipTile == Mesh::FLIP_N_ROTATE_TILE || flipTile == Mesh::FLIP_N_ROTATE_ROW) { uvBase[1] = {0.f, 1.f, 1.f, 1.f, 1.f, 0.f, 0.f, 0.f}; } Float32Array uvs; Float32Array colors; Uint32Array indices; auto index = 0u; for (auto y = 0u; y < tilesY; ++y) { for (auto x = 0u; x < tilesX; ++x) { stl_util::concat(positions, {-halfWidth + x * tileWidth + adjustX, -halfHeight + y * tileHeight + adjustY, 0.f}); stl_util::concat(positions, {-halfWidth + (x + 1) * tileWidth + adjustX, -halfHeight + y * tileHeight + adjustY, 0.f}); stl_util::concat(positions, {-halfWidth + (x + 1) * tileWidth + adjustX, -halfHeight + (y + 1) * tileHeight + adjustY, 0.f}); stl_util::concat(positions, {-halfWidth + x * tileWidth + adjustX, -halfHeight + (y + 1) * tileHeight + adjustY, 0.f}); stl_util::concat(indices, {index, index + 1, index + 3, index + 1, index + 2, index + 3}); if (flipTile == Mesh::FLIP_TILE || flipTile == Mesh::ROTATE_TILE || flipTile == Mesh::FLIP_N_ROTATE_TILE) { stl_util::concat(uvs, uvBase[(x % 2 + y % 2) % 2]); } else if (flipTile == Mesh::FLIP_ROW || flipTile == Mesh::ROTATE_ROW || flipTile == Mesh::FLIP_N_ROTATE_ROW) { stl_util::concat(uvs, uvBase[y % 2]); } else { stl_util::concat(uvs, uvBase[0]); } stl_util::concat( colors, {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}); stl_util::concat(normals, {0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f}); index += 4; } } // Part Tiles if (offsetX > 0 || offsetY > 0) { const auto partialBottomRow = (offsetY > 0 && (alignV == Mesh::CENTER || alignV == Mesh::TOP)); const auto partialTopRow = (offsetY > 0 && (alignV == Mesh::CENTER || alignV == Mesh::BOTTOM)); const auto partialLeftCol = (offsetX > 0 && (alignH == Mesh::CENTER || alignH == Mesh::RIGHT)); const auto partialRightCol = (offsetX > 0 && (alignH == Mesh::CENTER || alignH == Mesh::LEFT)); Float32Array uvPart; auto a = 0.f, b = 0.f, c = 0.f, d = 0.f; // corners if (partialBottomRow && partialLeftCol) { // bottom left corner stl_util::concat(positions, {startX + adjustX, startY + adjustY, 0.f}); stl_util::concat(positions, {-halfWidth + adjustX, startY + adjustY, 0.f}); stl_util::concat(positions, {-halfWidth + adjustX, startY + offsetY + adjustY, 0.f}); stl_util::concat(positions, {startX + adjustX, startY + offsetY + adjustY, 0.f}); stl_util::concat(indices, {index, index + 1, index + 3, index + 1, index + 2, index + 3}); index += 4; a = 1.f - offsetX / tileWidth; b = 1.f - offsetY / tileHeight; c = 1.f; d = 1.f; uvPart = {a, b, c, b, c, d, a, d}; if (flipTile == Mesh::ROTATE_ROW) { uvPart = {1.f - a, 1.f - b, 1.f - c, 1.f - b, 1.f - c, 1.f - d, 1.f - a, 1.f - d}; } if (flipTile == Mesh::FLIP_ROW) { uvPart = {1.f - a, b, 1.f - c, b, 1.f - c, d, 1.f - a, d}; } if (flipTile == Mesh::FLIP_N_ROTATE_ROW) { uvPart = {a, 1.f - b, c, 1.f - b, c, 1.f - d, a, 1.f - d}; } stl_util::concat(uvs, uvPart); stl_util::concat( colors, {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}); stl_util::concat(normals, {0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f}); } if (partialBottomRow && partialRightCol) { // bottom right corner stl_util::concat(positions, {halfWidth + adjustX, startY + adjustY, 0.f}); stl_util::concat(positions, {endX + adjustX, startY + adjustY, 0.f}); stl_util::concat(positions, {endX + adjustX, startY + offsetY + adjustY, 0.f}); stl_util::concat(positions, {halfWidth + adjustX, startY + offsetY + adjustY, 0.f}); stl_util::concat(indices, {index, index + 1, index + 3, index + 1, index + 2, index + 3}); index += 4; a = 0.f; b = 1.f - offsetY / tileHeight; c = offsetX / tileWidth; d = 1.f; uvPart = {a, b, c, b, c, d, a, d}; if (flipTile == Mesh::ROTATE_ROW || (flipTile == Mesh::ROTATE_TILE && (tilesX % 2) == 0)) { uvPart = {1.f - a, 1.f - b, 1.f - c, 1.f - b, 1.f - c, 1.f - d, 1.f - a, 1.f - d}; } if (flipTile == Mesh::FLIP_ROW || (flipTile == Mesh::FLIP_TILE && (tilesX % 2) == 0)) { uvPart = {1.f - a, b, 1.f - c, b, 1.f - c, d, 1.f - a, d}; } if (flipTile == Mesh::FLIP_N_ROTATE_ROW || (flipTile == Mesh::FLIP_N_ROTATE_TILE && (tilesX % 2) == 0)) { uvPart = {a, 1.f - b, c, 1.f - b, c, 1.f - d, a, 1.f - d}; } stl_util::concat(uvs, uvPart); stl_util::concat( colors, {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}); stl_util::concat(normals, {0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f}); } if (partialTopRow && partialLeftCol) { // top left corner stl_util::concat(positions, {startX + adjustX, halfHeight + adjustY, 0.f}); stl_util::concat(positions, {-halfWidth + adjustX, halfHeight + adjustY, 0.f}); stl_util::concat(positions, {-halfWidth + adjustX, endY + adjustY, 0.f}); stl_util::concat(positions, {startX + adjustX, endY + adjustY, 0.f}); stl_util::concat(indices, {index, index + 1, index + 3, index + 1, index + 2, index + 3}); index += 4; a = 1.f - offsetX / tileWidth; b = 0.f; c = 1.f; d = offsetY / tileHeight; uvPart = {a, b, c, b, c, d, a, d}; if ((flipTile == Mesh::ROTATE_ROW && (tilesY % 2) == 1) || (flipTile == Mesh::ROTATE_TILE && (tilesY % 1) == 0)) { uvPart = {1.f - a, 1.f - b, 1.f - c, 1.f - b, 1.f - c, 1.f - d, 1.f - a, 1.f - d}; } if ((flipTile == Mesh::FLIP_ROW && (tilesY % 2) == 1) || (flipTile == Mesh::FLIP_TILE && (tilesY % 2) == 0)) { uvPart = {1.f - a, b, 1.f - c, b, 1.f - c, d, 1.f - a, d}; } if ((flipTile == Mesh::FLIP_N_ROTATE_ROW && (tilesY % 2) == 1) || (flipTile == Mesh::FLIP_N_ROTATE_TILE && (tilesY % 2) == 0)) { uvPart = {a, 1.f - b, c, 1.f - b, c, 1.f - d, a, 1.f - d}; } stl_util::concat(uvs, uvPart); stl_util::concat( colors, {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}); stl_util::concat(normals, {0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f}); } if (partialTopRow && partialRightCol) { // top right corner stl_util::concat(positions, {halfWidth + adjustX, halfHeight + adjustY, 0.f}); stl_util::concat(positions, {endX + adjustX, halfHeight + adjustY, 0.f}); stl_util::concat(positions, {endX + adjustX, endY + adjustY, 0.f}); stl_util::concat(positions, {halfWidth + adjustX, endY + adjustY, 0.f}); stl_util::concat(indices, {index, index + 1, index + 3, index + 1, index + 2, index + 3}); index += 4; a = 0.f; b = 0.f; c = offsetX / tileWidth; d = offsetY / tileHeight; uvPart = {a, b, c, b, c, d, a, d}; if ((flipTile == Mesh::ROTATE_ROW && (tilesY % 2) == 1) || (flipTile == Mesh::ROTATE_TILE && (tilesY + tilesX) % 2 == 1)) { uvPart = {1.f - a, 1.f - b, 1.f - c, 1.f - b, 1.f - c, 1.f - d, 1.f - a, 1.f - d}; } if ((flipTile == Mesh::FLIP_ROW && (tilesY % 2) == 1) || (flipTile == Mesh::FLIP_TILE && (tilesY + tilesX) % 2 == 1)) { uvPart = {1.f - a, b, 1.f - c, b, 1.f - c, d, 1.f - a, d}; } if ((flipTile == Mesh::FLIP_N_ROTATE_ROW && (tilesY % 2) == 1) || (flipTile == Mesh::FLIP_N_ROTATE_TILE && (tilesY + tilesX) % 2 == 1)) { uvPart = {a, 1.f - b, c, 1.f - b, c, 1.f - d, a, 1.f - d}; } stl_util::concat(uvs, uvPart); stl_util::concat( colors, {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}); stl_util::concat(normals, {0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f}); } // part rows if (partialBottomRow) { std::array<Float32Array, 2> uvBaseBR; a = 0.f; b = 1.f - offsetY / tileHeight; c = 1.f; d = 1.f; uvBaseBR[0] = {a, b, c, b, c, d, a, d}; uvBaseBR[1] = {a, b, c, b, c, d, a, d}; if (flipTile == Mesh::ROTATE_TILE || flipTile == Mesh::ROTATE_ROW) { uvBaseBR[1] = {1.f - a, 1.f - b, 1.f - c, 1.f - b, 1.f - c, 1.f - d, 1.f - a, 1.f - d}; } if (flipTile == Mesh::FLIP_TILE || flipTile == Mesh::FLIP_ROW) { uvBaseBR[1] = {1.f - a, b, 1.f - c, b, 1.f - c, d, 1.f - a, d}; } if (flipTile == Mesh::FLIP_N_ROTATE_TILE || flipTile == Mesh::FLIP_N_ROTATE_ROW) { uvBaseBR[1] = {a, 1.f - b, c, 1.f - b, c, 1.f - d, a, 1.f - d}; } for (auto x = 0u; x < tilesX; ++x) { stl_util::concat(positions, {-halfWidth + x * tileWidth + adjustX, startY + adjustY, 0.f}); stl_util::concat(positions, {-halfWidth + (x + 1) * tileWidth + adjustX, startY + adjustY, 0.f}); stl_util::concat( positions, {-halfWidth + (x + 1) * tileWidth + adjustX, startY + offsetY + adjustY, 0.f}); stl_util::concat(positions, {-halfWidth + x * tileWidth + adjustX, startY + offsetY + adjustY, 0.f}); stl_util::concat(indices, {index, index + 1, index + 3, index + 1, index + 2, index + 3}); index += 4; if (flipTile == Mesh::FLIP_TILE || flipTile == Mesh::ROTATE_TILE || flipTile == Mesh::FLIP_N_ROTATE_TILE) { stl_util::concat(uvs, uvBaseBR[(x + 1) % 2]); } else if (flipTile == Mesh::FLIP_ROW || flipTile == Mesh::ROTATE_ROW || flipTile == Mesh::FLIP_N_ROTATE_ROW) { stl_util::concat(uvs, uvBaseBR[1]); } else { stl_util::concat(uvs, uvBaseBR[0]); } stl_util::concat( colors, {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}); stl_util::concat(normals, {0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f}); } } if (partialTopRow) { std::array<Float32Array, 2> uvBaseTR; a = 0; b = 0; c = 1; d = offsetY / tileHeight; uvBaseTR[0] = {a, b, c, b, c, d, a, d}; uvBaseTR[1] = {a, b, c, b, c, d, a, d}; if (flipTile == Mesh::ROTATE_TILE || flipTile == Mesh::ROTATE_ROW) { uvBaseTR[1] = {1.f - a, 1.f - b, 1.f - c, 1.f - b, 1.f - c, 1.f - d, 1.f - a, 1.f - d}; } if (flipTile == Mesh::FLIP_TILE || flipTile == Mesh::FLIP_ROW) { uvBaseTR[1] = {1.f - a, b, 1.f - c, b, 1.f - c, d, 1.f - a, d}; } if (flipTile == Mesh::FLIP_N_ROTATE_TILE || flipTile == Mesh::FLIP_N_ROTATE_ROW) { uvBaseTR[1] = {a, 1.f - b, c, 1.f - b, c, 1.f - d, a, 1.f - d}; } for (auto x = 0u; x < tilesX; ++x) { stl_util::concat(positions, {-halfWidth + x * tileWidth + adjustX, endY - offsetY + adjustY, 0.f}); stl_util::concat( positions, {-halfWidth + (x + 1) * tileWidth + adjustX, endY - offsetY + adjustY, 0.f}); stl_util::concat(positions, {-halfWidth + (x + 1) * tileWidth + adjustX, endY + adjustY, 0.f}); stl_util::concat(positions, {-halfWidth + x * tileWidth + adjustX, endY + adjustY, 0.f}); stl_util::concat(indices, {index, index + 1, index + 3, index + 1, index + 2, index + 3}); index += 4; if (flipTile == Mesh::FLIP_TILE || flipTile == Mesh::ROTATE_TILE || flipTile == Mesh::FLIP_N_ROTATE_TILE) { stl_util::concat(uvs, uvBaseTR[(x + tilesY) % 2]); } else if (flipTile == Mesh::FLIP_ROW || flipTile == Mesh::ROTATE_ROW || flipTile == Mesh::FLIP_N_ROTATE_ROW) { stl_util::concat(uvs, uvBaseTR[tilesY % 2]); } else { stl_util::concat(uvs, uvBaseTR[0]); } stl_util::concat( colors, {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}); stl_util::concat(normals, {0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f}); } } if (partialLeftCol) { std::array<Float32Array, 2> uvBaseLC; a = 1.f - offsetX / tileWidth; b = 0.f; c = 1.f; d = 1.f; uvBaseLC[0] = {a, b, c, b, c, d, a, d}; uvBaseLC[1] = {a, b, c, b, c, d, a, d}; if (flipTile == Mesh::ROTATE_TILE || flipTile == Mesh::ROTATE_ROW) { uvBaseLC[1] = {1.f - a, 1.f - b, 1.f - c, 1.f - b, 1.f - c, 1.f - d, 1.f - a, 1.f - d}; } if (flipTile == Mesh::FLIP_TILE || flipTile == Mesh::FLIP_ROW) { uvBaseLC[1] = {1.f - a, b, 1.f - c, b, 1.f - c, d, 1.f - a, d}; } if (flipTile == Mesh::FLIP_N_ROTATE_TILE || flipTile == Mesh::FLIP_N_ROTATE_ROW) { uvBaseLC[1] = {a, 1.f - b, c, 1.f - b, c, 1.f - d, a, 1.f - d}; } for (auto y = 0u; y < tilesY; y++) { stl_util::concat(positions, {startX + adjustX, -halfHeight + y * tileHeight + adjustY, 0.f}); stl_util::concat(positions, {startX + offsetX + adjustX, -halfHeight + y * tileHeight + adjustY, 0.f}); stl_util::concat(positions, {startX + offsetX + adjustX, -halfHeight + (y + 1) * tileHeight + adjustY, 0.f}); stl_util::concat(positions, {startX + adjustX, -halfHeight + (y + 1) * tileHeight + adjustY, 0.f}); stl_util::concat(indices, {index, index + 1, index + 3, index + 1, index + 2, index + 3}); index += 4; if (flipTile == Mesh::FLIP_TILE || flipTile == Mesh::ROTATE_TILE || flipTile == Mesh::FLIP_N_ROTATE_TILE) { stl_util::concat(uvs, uvBaseLC[(y + 1) % 2]); } else if (flipTile == Mesh::FLIP_ROW || flipTile == Mesh::ROTATE_ROW || flipTile == Mesh::FLIP_N_ROTATE_ROW) { stl_util::concat(uvs, uvBaseLC[y % 2]); } else { stl_util::concat(uvs, uvBaseLC[0]); } stl_util::concat( colors, {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}); stl_util::concat(normals, {0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f}); } } if (partialRightCol) { std::array<Float32Array, 2> uvBaseRC; a = 0.f; b = 0.f; c = offsetX / tileHeight; d = 1.f; uvBaseRC[0] = {a, b, c, b, c, d, a, d}; uvBaseRC[1] = {a, b, c, b, c, d, a, d}; if (flipTile == Mesh::ROTATE_TILE || flipTile == Mesh::ROTATE_ROW) { uvBaseRC[1] = {1.f - a, 1.f - b, 1 - c, 1.f - b, 1.f - c, 1 - d, 1.f - a, 1.f - d}; } if (flipTile == Mesh::FLIP_TILE || flipTile == Mesh::FLIP_ROW) { uvBaseRC[1] = {1.f - a, b, 1.f - c, b, 1.f - c, d, 1.f - a, d}; } if (flipTile == Mesh::FLIP_N_ROTATE_TILE || flipTile == Mesh::FLIP_N_ROTATE_ROW) { uvBaseRC[1] = {a, 1.f - b, c, 1.f - b, c, 1.f - d, a, 1.f - d}; } for (auto y = 0u; y < tilesY; ++y) { stl_util::concat(positions, {endX - offsetX + adjustX, -halfHeight + y * tileHeight + adjustY, 0.f}); stl_util::concat(positions, {endX + adjustX, -halfHeight + y * tileHeight + adjustY, 0.f}); stl_util::concat(positions, {endX + adjustX, -halfHeight + (y + 1) * tileHeight + adjustY, 0.f}); stl_util::concat( positions, {endX - offsetX + adjustX, -halfHeight + (y + 1) * tileHeight + adjustY, 0.f}); stl_util::concat(indices, {index, index + 1, index + 3, index + 1, index + 2, index + 3}); index += 4; if (flipTile == Mesh::FLIP_TILE || flipTile == Mesh::ROTATE_TILE || flipTile == Mesh::FLIP_N_ROTATE_TILE) { stl_util::concat(uvs, uvBaseRC[(y + tilesX) % 2]); } else if (flipTile == Mesh::FLIP_ROW || flipTile == Mesh::ROTATE_ROW || flipTile == Mesh::FLIP_N_ROTATE_ROW) { stl_util::concat(uvs, uvBaseRC[y % 2]); } else { stl_util::concat(uvs, uvBaseRC[0]); } stl_util::concat( colors, {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}); stl_util::concat(normals, {0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f}); } } } const auto sideOrientation = options.sideOrientation.value_or(VertexData::DEFAULTSIDE); // sides VertexData::_ComputeSides(sideOrientation, positions, indices, normals, uvs, options.frontUVs, options.backUVs); // Result auto vertexData = std::make_unique<VertexData>(); vertexData->indices = std::move(indices); vertexData->positions = std::move(positions); vertexData->normals = std::move(normals); vertexData->uvs = std::move(uvs); if (sideOrientation == VertexData::DOUBLESIDE) { stl_util::concat(colors, colors); } vertexData->colors = std::move(colors); return vertexData; } std::unique_ptr<VertexData> VertexData::CreateSphere(SphereOptions& options) { const auto segments = options.segments.value_or(32); const auto diameterX = options.diameterX.value_or(options.diameter.value_or(1.f)); const auto diameterY = options.diameterY.value_or(options.diameter.value_or(1.f)); const auto diameterZ = options.diameterZ.value_or(options.diameter.value_or(1.f)); const auto arc = options.arc.has_value() ? ((*options.arc <= 0.f || options.arc > 1.f) ? 1.f : *options.arc) : 1.f; const auto slice = options.slice.has_value() ? ((*options.slice <= 0.f) ? 1.f : *options.slice) : 1.f; const auto sideOrientation = options.sideOrientation.value_or(VertexData::DEFAULTSIDE); const auto dedupTopBottomIndices = options.dedupTopBottomIndices.value_or(false); Vector3 radius(diameterX / 2.f, diameterY / 2.f, diameterZ / 2.f); const uint32_t totalZRotationSteps = 2 + segments; const uint32_t totalYRotationSteps = 2 * totalZRotationSteps; IndicesArray indices; Float32Array positions; Float32Array normals; Float32Array uvs; for (auto zRotationStep = 0u; zRotationStep <= totalZRotationSteps; ++zRotationStep) { auto normalizedZ = static_cast<float>(zRotationStep) / static_cast<float>(totalZRotationSteps); auto angleZ = normalizedZ * Math::PI * slice; for (auto yRotationStep = 0u; yRotationStep <= totalYRotationSteps; ++yRotationStep) { auto normalizedY = static_cast<float>(yRotationStep) / static_cast<float>(totalYRotationSteps); auto angleY = normalizedY * Math::PI * 2.f * arc; auto rotationZ = Matrix::RotationZ(-angleZ); auto rotationY = Matrix::RotationY(angleY); auto afterRotZ = Vector3::TransformCoordinates(Vector3::Up(), rotationZ); auto complete = Vector3::TransformCoordinates(afterRotZ, rotationY); auto vertex = complete.multiply(radius); auto normal = complete.divide(radius).normalize(); stl_util::concat(positions, {vertex.x, vertex.y, vertex.z}); stl_util::concat(normals, {normal.x, normal.y, normal.z}); stl_util::concat(uvs, {normalizedY, normalizedZ}); } if (zRotationStep > 0u) { auto verticesCount = static_cast<uint32_t>(positions.size() / 3); auto _totalYRotationSteps = static_cast<uint32_t>(totalYRotationSteps); for (uint32_t firstIndex = verticesCount - 2 * (_totalYRotationSteps + 1); (firstIndex + _totalYRotationSteps + 2) < verticesCount; ++firstIndex) { if (dedupTopBottomIndices) { if (zRotationStep > 1u) { indices.emplace_back((firstIndex)); indices.emplace_back((firstIndex + 1)); indices.emplace_back(firstIndex + _totalYRotationSteps + 1); } if (zRotationStep < totalZRotationSteps || slice < 1.f) { indices.emplace_back((firstIndex + _totalYRotationSteps + 1)); indices.emplace_back((firstIndex + 1)); indices.emplace_back((firstIndex + _totalYRotationSteps + 2)); } } else { indices.emplace_back(firstIndex); indices.emplace_back(firstIndex + 1); indices.emplace_back(firstIndex + totalYRotationSteps + 1); indices.emplace_back(firstIndex + totalYRotationSteps + 1); indices.emplace_back(firstIndex + 1); indices.emplace_back(firstIndex + totalYRotationSteps + 2); } } } } // Sides VertexData::_ComputeSides(sideOrientation, positions, indices, normals, uvs, options.frontUVs, options.backUVs); // Result auto vertexData = std::make_unique<VertexData>(); vertexData->indices = std::move(indices); vertexData->positions = std::move(positions); vertexData->normals = std::move(normals); vertexData->uvs = std::move(uvs); return vertexData; } // Cylinder and cone std::unique_ptr<VertexData> VertexData::CreateCylinder(CylinderOptions& options) { const auto height = options.height.value_or(2.f); const auto diameterTop = options.diameterTop.value_or(options.diameter.value_or(1.f)); const auto diameterBottom = options.diameterBottom.value_or(options.diameter.value_or(1.f)); const auto tessellation = options.tessellation.value_or(24); const auto subdivisions = options.subdivisions.value_or(1.f); const auto hasRings = options.hasRings.value_or(false); const auto enclose = options.enclose.value_or(false); const auto cap = options.cap.value_or(Mesh::CAP_ALL); const auto arc = options.arc.has_value() ? ((*options.arc <= 0.f || options.arc > 1.f) ? 1.f : *options.arc) : 1.f; const auto sideOrientation = options.sideOrientation.value_or(VertexData::DEFAULTSIDE); auto& faceUV = options.faceUV; auto& faceColors = options.faceColors; // default face colors and UV if undefined uint32_t quadNb = (arc != 1.f && enclose) ? 2 : 0; uint32_t ringNb = (hasRings) ? subdivisions : 1; uint32_t surfaceNb = 2 + (1 + quadNb) * ringNb; uint32_t f; for (f = 0; f < surfaceNb; ++f) { if (!faceColors.empty() && f >= faceColors.size()) { faceColors.emplace_back(Color4(1.f, 1.f, 1.f, 1.f)); } } for (f = 0; f < surfaceNb; ++f) { if (f >= faceUV.size()) { faceUV.emplace_back(Vector4(0.f, 0.f, 1.f, 1.f)); } } Uint32Array indices; Float32Array positions; Float32Array normals; Float32Array uvs; Float32Array colors; auto angle_step = Math::PI2 * arc / static_cast<float>(tessellation); auto angle = 0.f; auto h = 0.f; auto radius = 0.f; auto _tan = (diameterBottom - diameterTop) / 2.f / height; auto ringVertex = Vector3::Zero(); auto ringNormal = Vector3::Zero(); auto ringFirstVertex = Vector3::Zero(); auto ringFirstNormal = Vector3::Zero(); auto quadNormal = Vector3::Zero(); auto Y = Axis::Y(); // positions, normals, uvs uint32_t i; uint32_t j; uint32_t r; uint32_t ringIdx = 1; uint32_t s = 1; // surface index uint32_t cs = 0; float v = 0.f; for (i = 0; i <= subdivisions; ++i) { h = static_cast<float>(i) / static_cast<float>(subdivisions); radius = (h * (diameterTop - diameterBottom) + diameterBottom) / 2.f; ringIdx = (hasRings && i != 0 && i != subdivisions) ? 2 : 1; for (r = 0; r < ringIdx; ++r) { if (hasRings) { s += r; } if (enclose) { s += 2 * r; } for (j = 0; j <= tessellation; ++j) { angle = static_cast<float>(j) * angle_step; // position ringVertex.x = std::cos(-angle) * radius; ringVertex.y = -height / 2.f + h * height; ringVertex.z = std::sin(-angle) * radius; if (stl_util::almost_equal(diameterTop, 0.f) && i == subdivisions) { // if no top cap, reuse former normals ringNormal.x = normals[normals.size() - (tessellation + 1) * 3 + 0]; ringNormal.y = normals[normals.size() - (tessellation + 1) * 3 + 1]; ringNormal.z = normals[normals.size() - (tessellation + 1) * 3 + 2]; } else { ringNormal.x = ringVertex.x; ringNormal.z = ringVertex.z; ringNormal.y = std::sqrt(ringNormal.x * ringNormal.x + ringNormal.z * ringNormal.z) * _tan; ringNormal.normalize(); } // keep first ring vertex values for enclose if (j == 0) { ringFirstVertex.copyFrom(ringVertex); ringFirstNormal.copyFrom(ringNormal); } stl_util::concat(positions, {ringVertex.x, ringVertex.y, ringVertex.z}); stl_util::concat(normals, {ringNormal.x, ringNormal.y, ringNormal.z}); if (hasRings) { v = (cs != s) ? faceUV[s].y : faceUV[s].w; } else { v = faceUV[s].y + (faceUV[s].w - faceUV[s].y) * h; } stl_util::concat(uvs, {faceUV[s].x + (faceUV[s].z - faceUV[s].x) * static_cast<float>(j) / static_cast<float>(tessellation), v}); if (!faceColors.empty()) { stl_util::concat(colors, {faceColors[s].r, faceColors[s].g, faceColors[s].b, faceColors[s].a}); } } // if enclose, add four vertices and their dedicated normals if (!stl_util::almost_equal(arc, 1.f) && enclose) { stl_util::concat(positions, {ringVertex.x, ringVertex.y, ringVertex.z}); stl_util::concat(positions, {0.f, ringVertex.y, 0.f}); stl_util::concat(positions, {0.f, ringVertex.y, 0.f}); stl_util::concat(positions, {ringFirstVertex.x, ringFirstVertex.y, ringFirstVertex.z}); Vector3::CrossToRef(Y, ringNormal, quadNormal); quadNormal.normalize(); stl_util::concat(normals, {quadNormal.x, quadNormal.y, quadNormal.z, quadNormal.x, quadNormal.y, quadNormal.z}); Vector3::CrossToRef(ringFirstNormal, Y, quadNormal); quadNormal.normalize(); stl_util::concat(normals, {quadNormal.x, quadNormal.y, quadNormal.z, quadNormal.x, quadNormal.y, quadNormal.z}); if (hasRings) { v = (cs != s) ? faceUV[s + 1].y : faceUV[s + 1].w; } else { v = faceUV[s + 1].y + (faceUV[s + 1].w - faceUV[s + 1].y) * h; } stl_util::concat(uvs, {faceUV[s + 1].x, v}); stl_util::concat(uvs, {faceUV[s + 1].z, v}); if (hasRings) { v = (cs != s) ? faceUV[s + 2].y : faceUV[s + 2].w; } else { v = faceUV[s + 2].y + (faceUV[s + 2].w - faceUV[s + 2].y) * h; } stl_util::concat(uvs, {faceUV[s + 2].x, v}); stl_util::concat(uvs, {faceUV[s + 2].z, v}); if (!faceColors.empty()) { stl_util::concat(colors, {faceColors[s + 1].r, faceColors[s + 1].g, faceColors[s + 1].b, faceColors[s + 1].a}); stl_util::concat(colors, {faceColors[s + 1].r, faceColors[s + 1].g, faceColors[s + 1].b, faceColors[s + 1].a}); stl_util::concat(colors, {faceColors[s + 2].r, faceColors[s + 2].g, faceColors[s + 2].b, faceColors[s + 2].a}); stl_util::concat(colors, {faceColors[s + 2].r, faceColors[s + 2].g, faceColors[s + 2].b, faceColors[s + 2].a}); } } if (cs != s) { cs = s; } } } // indices // correction of number of iteration if enclose uint32_t e = (!stl_util::almost_equal(arc, 1.f) && enclose) ? tessellation + 4 : tessellation; i = 0; for (s = 0; s < subdivisions; ++s) { uint32_t i0 = 0, i1 = 0, i2 = 0, i3 = 0; for (j = 0; j < tessellation; ++j) { i0 = i * (e + 1) + j; i1 = (i + 1) * (e + 1) + j; i2 = i * (e + 1) + (j + 1); i3 = (i + 1) * (e + 1) + (j + 1); stl_util::concat(indices, {i0, i1, i2}); stl_util::concat(indices, {i3, i2, i1}); } if (!stl_util::almost_equal(arc, 1.f) && enclose) { // if enclose, add two quads stl_util::concat(indices, {i0 + 2, i1 + 2, i2 + 2}); stl_util::concat(indices, {i3 + 2, i2 + 2, i1 + 2}); stl_util::concat(indices, {i0 + 4, i1 + 4, i2 + 4}); stl_util::concat(indices, {i3 + 4, i2 + 4, i1 + 4}); } i = (hasRings) ? (i + 2) : (i + 1); } // Caps auto createCylinderCap = [&](bool isTop) -> void { float _radius = isTop ? diameterTop / 2.f : diameterBottom / 2.f; if (stl_util::almost_equal(_radius, 0.f)) { return; } // Cap positions, normals & uvs float _angle; Vector3 circleVector; Vector2 textureCoordinate; Vector4 u = isTop ? faceUV[surfaceNb - 1] : faceUV[0]; std::optional<Color4> c = std::nullopt; if (!faceColors.empty()) { c = (isTop) ? faceColors[surfaceNb - 1] : faceColors[0]; } // cap center auto vbase = static_cast<uint32_t>(positions.size() / 3); float offset = isTop ? height / 2.f : -height / 2.f; Vector3 center(0.f, offset, 0.f); stl_util::concat(positions, {center.x, center.y, center.z}); stl_util::concat(normals, {0.f, isTop ? 1.f : -1.f, 0.f}); stl_util::concat(uvs, {u.x + (u.z - u.x) * 0.5f, u.y + (u.w - u.y) * 0.5f}); if (c) { const auto& _c = *c; stl_util::concat(colors, {_c.r, _c.g, _c.b, _c.a}); } Vector2 textureScale(0.5f, 0.5f); for (i = 0; i <= tessellation; ++i) { _angle = Math::PI2 * static_cast<float>(i) * arc / static_cast<float>(tessellation); auto _cos = std::cos(-_angle); auto _sin = std::sin(-_angle); circleVector = Vector3(_cos * _radius, offset, _sin * _radius); textureCoordinate = Vector2(_cos * textureScale.x + 0.5f, _sin * textureScale.y + 0.5f); stl_util::concat(positions, {circleVector.x, circleVector.y, circleVector.z}); stl_util::concat(normals, {0.f, isTop ? 1.f : -1.f, 0.f}); stl_util::concat( uvs, {u.x + (u.z - u.x) * textureCoordinate.x, u.y + (u.w - u.y) * textureCoordinate.y}); if (c) { const auto& _c = *c; stl_util::concat(colors, {_c.r, _c.g, _c.b, _c.a}); } } // Cap indices for (i = 0; i < tessellation; ++i) { if (!isTop) { indices.emplace_back(vbase); indices.emplace_back(vbase + (i + 1)); indices.emplace_back(vbase + (i + 2)); } else { indices.emplace_back(vbase); indices.emplace_back(vbase + (i + 2)); indices.emplace_back(vbase + (i + 1)); } } }; // add caps to geometry based on cap parameter if ((cap == Mesh::CAP_START) || (cap == Mesh::CAP_ALL)) { createCylinderCap(false); } if ((cap == Mesh::CAP_END) || (cap == Mesh::CAP_ALL)) { createCylinderCap(true); } // Sides VertexData::_ComputeSides(sideOrientation, positions, indices, normals, uvs, options.frontUVs, options.backUVs); auto vertexData = std::make_unique<VertexData>(); vertexData->indices = std::move(indices); vertexData->positions = std::move(positions); vertexData->normals = std::move(normals); vertexData->uvs = std::move(uvs); if (!faceColors.empty()) { vertexData->colors = std::move(colors); } return vertexData; } std::unique_ptr<VertexData> VertexData::CreateTorus(TorusOptions& options) { Uint32Array indices; Float32Array positions; Float32Array normals; Float32Array uvs; const auto diameter = options.diameter.value_or(1.f); const auto thickness = options.thickness.value_or(0.5f); const auto tessellation = options.tessellation.value_or(16); const auto sideOrientation = options.sideOrientation.value_or(VertexData::DEFAULTSIDE); auto stride = tessellation + 1; auto tessellationf = static_cast<float>(tessellation); for (uint32_t i = 0; i <= tessellation; ++i) { auto u = static_cast<float>(i) / tessellationf; auto outerAngle = static_cast<float>(i) * Math::PI * 2.f / tessellationf - Math::PI / 2.f; auto rotationY = Matrix::RotationY(outerAngle); auto transform = Matrix::Translation(diameter / 2.f, 0.f, 0.f).multiply(rotationY); for (uint32_t j = 0; j <= tessellation; ++j) { auto v = 1.f - static_cast<float>(j) / tessellationf; auto innerAngle = static_cast<float>(j) * Math::PI * 2.f / tessellationf + Math::PI; auto dx = std::cos(innerAngle); auto dy = std::sin(innerAngle); // Create a vertex. Vector3 normal(dx, dy, 0.f); Vector3 position = normal.scale(thickness / 2.f); Vector2 textureCoordinate(u, v); position = Vector3::TransformCoordinates(position, transform); normal = Vector3::TransformNormal(normal, transform); stl_util::concat(positions, {position.x, position.y, position.z}); stl_util::concat(normals, {normal.x, normal.y, normal.z}); stl_util::concat(uvs, {textureCoordinate.x, textureCoordinate.y}); // And create indices for two triangles. uint32_t nextI = (i + 1) % stride; uint32_t nextJ = (j + 1) % stride; indices.emplace_back(i * stride + j); indices.emplace_back(i * stride + nextJ); indices.emplace_back(nextI * stride + j); indices.emplace_back(i * stride + nextJ); indices.emplace_back(nextI * stride + nextJ); indices.emplace_back(nextI * stride + j); } } // Sides VertexData::_ComputeSides(sideOrientation, positions, indices, normals, uvs, options.frontUVs, options.backUVs); // Result auto vertexData = std::make_unique<VertexData>(); vertexData->indices = std::move(indices); vertexData->positions = std::move(positions); vertexData->normals = std::move(normals); vertexData->uvs = std::move(uvs); return vertexData; } std::unique_ptr<VertexData> VertexData::CreateLineSystem(LineSystemOptions& options) { Uint32Array indices; Float32Array positions; const auto& lines = options.lines; const auto& colors = options.colors; Float32Array vertexColors; uint32_t idx = 0; for (size_t l = 0; l < lines.size(); ++l) { const auto& points = lines[l]; for (size_t index = 0; index < points.size(); ++index) { stl_util::concat(positions, {points[index].x, points[index].y, points[index].z}); if (!colors.empty()) { const auto& color = colors[l]; stl_util::concat(vertexColors, {color[index].r, color[index].g, color[index].b, color[index].a}); } if (index > 0) { indices.emplace_back(idx - 1); indices.emplace_back(idx); } ++idx; } } // Result auto vertexData = std::make_unique<VertexData>(); vertexData->indices = std::move(indices); vertexData->positions = std::move(positions); if (!colors.empty()) { vertexData->colors = vertexColors; } return
{'addition_count': 10, 'commit_subject': 'Updating meshed module to v4.0.0', 'deletion_count': 11, 'file_extension': '.cpp', 'lang': 'cpp', 'license': 'apache-2.0', 'repo_name': 'samdauwe/BabylonCpp'}