+ {
+ PatchControlIter pCtrl = m_ctrlTransformed.data();
+ for (std::size_t j = 0, offStartY = 0; j + 1 < m_height; j += 2, pCtrl += (strideU + strideV)) {
+ // set up array offsets for this sub-patch
+ const bool leafY = (m_patchDef3) ? false : BezierCurveTree_isLeaf(m_tess.m_curveTreeV[j >> 1]);
+ const std::size_t offMidY = (m_patchDef3) ? 0 : m_tess.m_curveTreeV[j >> 1]->index;
+ const std::size_t widthY = m_tess.m_arrayHeight[j >> 1] * m_tess.m_nArrayWidth;
+ const std::size_t offEndY = offStartY + widthY;
+
+ for (std::size_t i = 0, offStartX = 0; i + 1 < m_width; i += 2, pCtrl += (strideU << 1)) {
+ const bool leafX = (m_patchDef3) ? false : BezierCurveTree_isLeaf(m_tess.m_curveTreeU[i >> 1]);
+ const std::size_t offMidX = (m_patchDef3) ? 0 : m_tess.m_curveTreeU[i >> 1]->index;
+ const std::size_t widthX = m_tess.m_arrayWidth[i >> 1];
+ const std::size_t offEndX = offStartX + widthX;
+
+ PatchControl *subMatrix[3][3];
+ subMatrix[0][0] = pCtrl;
+ subMatrix[0][1] = subMatrix[0][0] + strideU;
+ subMatrix[0][2] = subMatrix[0][1] + strideU;
+ subMatrix[1][0] = subMatrix[0][0] + strideV;
+ subMatrix[1][1] = subMatrix[1][0] + strideU;
+ subMatrix[1][2] = subMatrix[1][1] + strideU;
+ subMatrix[2][0] = subMatrix[1][0] + strideV;
+ subMatrix[2][1] = subMatrix[2][0] + strideU;
+ subMatrix[2][2] = subMatrix[2][1] + strideU;
+
+ // assign on-patch control points to vertex array
+ if (i == 0 && j == 0) {
+ vertex_clear_normal(m_tess.m_vertices[offStartX + offStartY]);
+ }
+ vertex_assign_ctrl(m_tess.m_vertices[offStartX + offStartY], *subMatrix[0][0]);
+ if (j == 0) {
+ vertex_clear_normal(m_tess.m_vertices[offEndX + offStartY]);
+ }
+ vertex_assign_ctrl(m_tess.m_vertices[offEndX + offStartY], *subMatrix[0][2]);
+ if (i == 0) {
+ vertex_clear_normal(m_tess.m_vertices[offStartX + offEndY]);
+ }
+ vertex_assign_ctrl(m_tess.m_vertices[offStartX + offEndY], *subMatrix[2][0]);
+
+ vertex_clear_normal(m_tess.m_vertices[offEndX + offEndY]);
+ vertex_assign_ctrl(m_tess.m_vertices[offEndX + offEndY], *subMatrix[2][2]);
+
+ if (!m_patchDef3) {
+ // assign remaining control points to vertex array
+ if (!leafX) {
+ vertex_assign_ctrl(m_tess.m_vertices[offMidX + offStartY], *subMatrix[0][1]);
+ vertex_assign_ctrl(m_tess.m_vertices[offMidX + offEndY], *subMatrix[2][1]);
+ }
+ if (!leafY) {
+ vertex_assign_ctrl(m_tess.m_vertices[offStartX + offMidY], *subMatrix[1][0]);
+ vertex_assign_ctrl(m_tess.m_vertices[offEndX + offMidY], *subMatrix[1][2]);
+
+ if (!leafX) {
+ vertex_assign_ctrl(m_tess.m_vertices[offMidX + offMidY], *subMatrix[1][1]);
+ }
+ }
+ }
+
+ // test all 12 edges for degeneracy
+ unsigned int nFlagsX = subarray_get_degen(pCtrl, strideU, strideV);
+ unsigned int nFlagsY = subarray_get_degen(pCtrl, strideV, strideU);
+ Vector3 tangentX[6], tangentY[6];
+ Vector2 tangentS[6], tangentT[6];
+
+ // set up tangents for each of the 12 edges if they were not degenerate
+ if (!(nFlagsX & DEGEN_0a)) {
+ tangentX[0] = vector3_subtracted(subMatrix[0][1]->m_vertex, subMatrix[0][0]->m_vertex);
+ tangentS[0] = vector2_subtracted(subMatrix[0][1]->m_texcoord, subMatrix[0][0]->m_texcoord);
+ }
+ if (!(nFlagsX & DEGEN_0b)) {
+ tangentX[1] = vector3_subtracted(subMatrix[0][2]->m_vertex, subMatrix[0][1]->m_vertex);
+ tangentS[1] = vector2_subtracted(subMatrix[0][2]->m_texcoord, subMatrix[0][1]->m_texcoord);
+ }
+ if (!(nFlagsX & DEGEN_1a)) {
+ tangentX[2] = vector3_subtracted(subMatrix[1][1]->m_vertex, subMatrix[1][0]->m_vertex);
+ tangentS[2] = vector2_subtracted(subMatrix[1][1]->m_texcoord, subMatrix[1][0]->m_texcoord);
+ }
+ if (!(nFlagsX & DEGEN_1b)) {
+ tangentX[3] = vector3_subtracted(subMatrix[1][2]->m_vertex, subMatrix[1][1]->m_vertex);
+ tangentS[3] = vector2_subtracted(subMatrix[1][2]->m_texcoord, subMatrix[1][1]->m_texcoord);
+ }
+ if (!(nFlagsX & DEGEN_2a)) {
+ tangentX[4] = vector3_subtracted(subMatrix[2][1]->m_vertex, subMatrix[2][0]->m_vertex);
+ tangentS[4] = vector2_subtracted(subMatrix[2][1]->m_texcoord, subMatrix[2][0]->m_texcoord);
+ }
+ if (!(nFlagsX & DEGEN_2b)) {
+ tangentX[5] = vector3_subtracted(subMatrix[2][2]->m_vertex, subMatrix[2][1]->m_vertex);
+ tangentS[5] = vector2_subtracted(subMatrix[2][2]->m_texcoord, subMatrix[2][1]->m_texcoord);
+ }
+
+ if (!(nFlagsY & DEGEN_0a)) {
+ tangentY[0] = vector3_subtracted(subMatrix[1][0]->m_vertex, subMatrix[0][0]->m_vertex);
+ tangentT[0] = vector2_subtracted(subMatrix[1][0]->m_texcoord, subMatrix[0][0]->m_texcoord);
+ }
+ if (!(nFlagsY & DEGEN_0b)) {
+ tangentY[1] = vector3_subtracted(subMatrix[2][0]->m_vertex, subMatrix[1][0]->m_vertex);
+ tangentT[1] = vector2_subtracted(subMatrix[2][0]->m_texcoord, subMatrix[1][0]->m_texcoord);
+ }
+ if (!(nFlagsY & DEGEN_1a)) {
+ tangentY[2] = vector3_subtracted(subMatrix[1][1]->m_vertex, subMatrix[0][1]->m_vertex);
+ tangentT[2] = vector2_subtracted(subMatrix[1][1]->m_texcoord, subMatrix[0][1]->m_texcoord);
+ }
+ if (!(nFlagsY & DEGEN_1b)) {
+ tangentY[3] = vector3_subtracted(subMatrix[2][1]->m_vertex, subMatrix[1][1]->m_vertex);
+ tangentT[3] = vector2_subtracted(subMatrix[2][1]->m_texcoord, subMatrix[1][1]->m_texcoord);
+ }
+ if (!(nFlagsY & DEGEN_2a)) {
+ tangentY[4] = vector3_subtracted(subMatrix[1][2]->m_vertex, subMatrix[0][2]->m_vertex);
+ tangentT[4] = vector2_subtracted(subMatrix[1][2]->m_texcoord, subMatrix[0][2]->m_texcoord);
+ }
+ if (!(nFlagsY & DEGEN_2b)) {
+ tangentY[5] = vector3_subtracted(subMatrix[2][2]->m_vertex, subMatrix[1][2]->m_vertex);
+ tangentT[5] = vector2_subtracted(subMatrix[2][2]->m_texcoord, subMatrix[1][2]->m_texcoord);
+ }
+
+ // set up remaining edge tangents by borrowing the tangent from the closest parallel non-degenerate edge
+ tangents_remove_degenerate(tangentX, tangentS, nFlagsX);
+ tangents_remove_degenerate(tangentY, tangentT, nFlagsY);
+
+ {
+ // x=0, y=0
+ std::size_t index = offStartX + offStartY;
+ std::size_t index0 = 0;
+ std::size_t index1 = 0;
+
+ double dot = vector3_dot(tangentX[index0], tangentY[index1]);
+ double length = vector3_length(tangentX[index0]) * vector3_length(tangentY[index1]);
+
+ bestTangents00(nFlagsX, dot, length, index0, index1);
+
+ accumulateVertexTangentSpace(index, tangentX, tangentY, tangentS, tangentT, index0, index1);
+ }
+
+ {
+ // x=1, y=0
+ std::size_t index = offEndX + offStartY;
+ std::size_t index0 = 1;
+ std::size_t index1 = 4;
+
+ double dot = vector3_dot(tangentX[index0], tangentY[index1]);
+ double length = vector3_length(tangentX[index0]) * vector3_length(tangentY[index1]);
+
+ bestTangents10(nFlagsX, dot, length, index0, index1);
+
+ accumulateVertexTangentSpace(index, tangentX, tangentY, tangentS, tangentT, index0, index1);
+ }
+
+ {
+ // x=0, y=1
+ std::size_t index = offStartX + offEndY;
+ std::size_t index0 = 4;
+ std::size_t index1 = 1;
+
+ double dot = vector3_dot(tangentX[index0], tangentY[index1]);
+ double length = vector3_length(tangentX[index1]) * vector3_length(tangentY[index1]);
+
+ bestTangents01(nFlagsX, dot, length, index0, index1);
+
+ accumulateVertexTangentSpace(index, tangentX, tangentY, tangentS, tangentT, index0, index1);
+ }
+
+ {
+ // x=1, y=1
+ std::size_t index = offEndX + offEndY;
+ std::size_t index0 = 5;
+ std::size_t index1 = 5;
+
+ double dot = vector3_dot(tangentX[index0], tangentY[index1]);
+ double length = vector3_length(tangentX[index0]) * vector3_length(tangentY[index1]);
+
+ bestTangents11(nFlagsX, dot, length, index0, index1);
+
+ accumulateVertexTangentSpace(index, tangentX, tangentY, tangentS, tangentT, index0, index1);
+ }
+
+ //normalise normals that won't be accumulated again
+ if (i != 0 || j != 0) {
+ normalise_safe(normal_for_index(m_tess.m_vertices, offStartX + offStartY));
+ normalise_safe(tangent_for_index(m_tess.m_vertices, offStartX + offStartY));
+ normalise_safe(bitangent_for_index(m_tess.m_vertices, offStartX + offStartY));
+ }
+ if (i + 3 == m_width) {
+ normalise_safe(normal_for_index(m_tess.m_vertices, offEndX + offStartY));
+ normalise_safe(tangent_for_index(m_tess.m_vertices, offEndX + offStartY));
+ normalise_safe(bitangent_for_index(m_tess.m_vertices, offEndX + offStartY));
+ }
+ if (j + 3 == m_height) {
+ normalise_safe(normal_for_index(m_tess.m_vertices, offStartX + offEndY));
+ normalise_safe(tangent_for_index(m_tess.m_vertices, offStartX + offEndY));
+ normalise_safe(bitangent_for_index(m_tess.m_vertices, offStartX + offEndY));
+ }
+ if (i + 3 == m_width && j + 3 == m_height) {
+ normalise_safe(normal_for_index(m_tess.m_vertices, offEndX + offEndY));
+ normalise_safe(tangent_for_index(m_tess.m_vertices, offEndX + offEndY));
+ normalise_safe(bitangent_for_index(m_tess.m_vertices, offEndX + offEndY));
+ }
+
+ // set flags to average normals between shared edges
+ if (j != 0) {
+ nFlagsX |= AVERAGE;
+ }
+ if (i != 0) {
+ nFlagsY |= AVERAGE;
+ }
+ // set flags to save evaluating shared edges twice
+ nFlagsX |= SPLIT;
+ nFlagsY |= SPLIT;
+
+ // if the patch is curved.. tesselate recursively
+ // use the relevant control curves for this sub-patch
+ if (m_patchDef3) {
+ TesselateSubMatrixFixed(m_tess.m_vertices.data() + offStartX + offStartY, 1, m_tess.m_nArrayWidth,
+ nFlagsX, nFlagsY, subMatrix);
+ } else {
+ if (!leafX) {
+ TesselateSubMatrix(m_tess.m_curveTreeU[i >> 1], m_tess.m_curveTreeV[j >> 1],
+ offStartX, offStartY, offEndX, offEndY, // array offsets
+ nFlagsX, nFlagsY,
+ subMatrix[1][0]->m_vertex, subMatrix[1][1]->m_vertex,
+ subMatrix[1][2]->m_vertex,
+ subMatrix[1][0]->m_texcoord, subMatrix[1][1]->m_texcoord,
+ subMatrix[1][2]->m_texcoord,
+ false);
+ } else if (!leafY) {
+ TesselateSubMatrix(m_tess.m_curveTreeV[j >> 1], m_tess.m_curveTreeU[i >> 1],
+ offStartY, offStartX, offEndY, offEndX, // array offsets
+ nFlagsY, nFlagsX,
+ subMatrix[0][1]->m_vertex, subMatrix[1][1]->m_vertex,
+ subMatrix[2][1]->m_vertex,
+ subMatrix[0][1]->m_texcoord, subMatrix[1][1]->m_texcoord,
+ subMatrix[2][1]->m_texcoord,
+ true);
+ }
+ }
+
+ offStartX = offEndX;
+ }
+ offStartY = offEndY;