/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define _USE_MATH_DEFINES #include "patch.h" #include #include "preferences.h" #include "brush_primit.h" #include "signal/signal.h" Signal0 g_patchTextureChangedCallbacks; void Patch_addTextureChangedCallback(const SignalHandler &handler) { g_patchTextureChangedCallbacks.connectLast(handler); } void Patch_textureChanged() { g_patchTextureChangedCallbacks(); } Shader *PatchInstance::m_state_selpoint; Shader *Patch::m_state_ctrl; Shader *Patch::m_state_lattice; EPatchType Patch::m_type; std::size_t MAX_PATCH_WIDTH = 0; std::size_t MAX_PATCH_HEIGHT = 0; int g_PatchSubdivideThreshold = 4; void BezierCurveTree_Delete(BezierCurveTree *pCurve) { if (pCurve) { BezierCurveTree_Delete(pCurve->left); BezierCurveTree_Delete(pCurve->right); delete pCurve; } } std::size_t BezierCurveTree_Setup(BezierCurveTree *pCurve, std::size_t index, std::size_t stride) { if (pCurve) { if (pCurve->left && pCurve->right) { index = BezierCurveTree_Setup(pCurve->left, index, stride); pCurve->index = index * stride; index++; index = BezierCurveTree_Setup(pCurve->right, index, stride); } else { pCurve->index = BEZIERCURVETREE_MAX_INDEX; } } return index; } bool BezierCurve_IsCurved(BezierCurve *pCurve) { Vector3 vTemp(vector3_subtracted(pCurve->right, pCurve->left)); Vector3 v1(vector3_subtracted(pCurve->crd, pCurve->left)); Vector3 v2(vector3_subtracted(pCurve->right, pCurve->crd)); if (vector3_equal(v1, g_vector3_identity) || vector3_equal(vTemp, v1)) { // return 0 if 1->2 == 0 or 1->2 == 1->3 return false; } vector3_normalise(v1); vector3_normalise(v2); if (vector3_equal(v1, v2)) { return false; } Vector3 v3(vTemp); const double width = vector3_length(v3); vector3_scale(v3, 1.0 / width); if (vector3_equal(v1, v3) && vector3_equal(v2, v3)) { return false; } const double angle = acos(vector3_dot(v1, v2)) / c_pi; const double index = width * angle; if (index > static_cast( g_PatchSubdivideThreshold )) { return true; } return false; } void BezierInterpolate(BezierCurve *pCurve) { pCurve->left = vector3_mid(pCurve->left, pCurve->crd); pCurve->right = vector3_mid(pCurve->crd, pCurve->right); pCurve->crd = vector3_mid(pCurve->left, pCurve->right); } const std::size_t PATCH_MAX_SUBDIVISION_DEPTH = 16; void BezierCurveTree_FromCurveList(BezierCurveTree *pTree, GSList *pCurveList, std::size_t depth = 0) { GSList *pLeftList = 0; GSList *pRightList = 0; BezierCurve *pCurve, *pLeftCurve, *pRightCurve; bool bSplit = false; for (GSList *l = pCurveList; l; l = l->next) { pCurve = (BezierCurve *) (l->data); if (bSplit || BezierCurve_IsCurved(pCurve)) { bSplit = true; pLeftCurve = new BezierCurve; pRightCurve = new BezierCurve; pLeftCurve->left = pCurve->left; pRightCurve->right = pCurve->right; BezierInterpolate(pCurve); pLeftCurve->crd = pCurve->left; pRightCurve->crd = pCurve->right; pLeftCurve->right = pCurve->crd; pRightCurve->left = pCurve->crd; pLeftList = g_slist_prepend(pLeftList, pLeftCurve); pRightList = g_slist_prepend(pRightList, pRightCurve); } } if (pLeftList != 0 && pRightList != 0 && depth != PATCH_MAX_SUBDIVISION_DEPTH) { pTree->left = new BezierCurveTree; pTree->right = new BezierCurveTree; BezierCurveTree_FromCurveList(pTree->left, pLeftList, depth + 1); BezierCurveTree_FromCurveList(pTree->right, pRightList, depth + 1); for (GSList *l = pLeftList; l != 0; l = g_slist_next(l)) { delete (BezierCurve *) l->data; } for (GSList *l = pRightList; l != 0; l = g_slist_next(l)) { delete (BezierCurve *) l->data; } g_slist_free(pLeftList); g_slist_free(pRightList); } else { pTree->left = 0; pTree->right = 0; } } int Patch::m_CycleCapIndex = 0; void Patch::setDims(std::size_t w, std::size_t h) { if ((w % 2) == 0) { w -= 1; } ASSERT_MESSAGE(w <= MAX_PATCH_WIDTH, "patch too wide"); if (w > MAX_PATCH_WIDTH) { w = MAX_PATCH_WIDTH; } else if (w < MIN_PATCH_WIDTH) { w = MIN_PATCH_WIDTH; } if ((h % 2) == 0) { m_height -= 1; } ASSERT_MESSAGE(h <= MAX_PATCH_HEIGHT, "patch too tall"); if (h > MAX_PATCH_HEIGHT) { h = MAX_PATCH_HEIGHT; } else if (h < MIN_PATCH_HEIGHT) { h = MIN_PATCH_HEIGHT; } m_width = w; m_height = h; if (m_width * m_height != m_ctrl.size()) { m_ctrl.resize(m_width * m_height); onAllocate(m_ctrl.size()); } } inline const Colour4b &colour_for_index(std::size_t i, std::size_t width) { return (i % 2 || (i / width) % 2) ? colour_inside : colour_corner; } inline bool float_valid(float f) { return f == f; } bool Patch::isValid() const { if (!m_width || !m_height) { return false; } for (const_iterator i = m_ctrl.begin(); i != m_ctrl.end(); ++i) { if (!float_valid((*i).m_vertex.x()) || !float_valid((*i).m_vertex.y()) || !float_valid((*i).m_vertex.z()) || !float_valid((*i).m_texcoord.x()) || !float_valid((*i).m_texcoord.y())) { globalErrorStream() << "patch has invalid control points\n"; return false; } } return true; } void Patch::UpdateCachedData() { m_ctrl_vertices.clear(); m_lattice_indices.clear(); if (!isValid()) { m_tess.m_numStrips = 0; m_tess.m_lenStrips = 0; m_tess.m_nArrayHeight = 0; m_tess.m_nArrayWidth = 0; m_tess.m_curveTreeU.resize(0); m_tess.m_curveTreeV.resize(0); m_tess.m_indices.resize(0); m_tess.m_vertices.resize(0); m_tess.m_arrayHeight.resize(0); m_tess.m_arrayWidth.resize(0); m_aabb_local = AABB(); return; } BuildTesselationCurves(ROW); BuildTesselationCurves(COL); BuildVertexArray(); AccumulateBBox(); IndexBuffer ctrl_indices; m_lattice_indices.reserve(((m_width * (m_height - 1)) + (m_height * (m_width - 1))) << 1); ctrl_indices.reserve(m_ctrlTransformed.size()); { UniqueVertexBuffer inserter(m_ctrl_vertices); for (iterator i = m_ctrlTransformed.begin(); i != m_ctrlTransformed.end(); ++i) { ctrl_indices.insert(inserter.insert(pointvertex_quantised( PointVertex(reinterpret_cast((*i).m_vertex ), colour_for_index(i - m_ctrlTransformed.begin(), m_width))))); } } { for (IndexBuffer::iterator i = ctrl_indices.begin(); i != ctrl_indices.end(); ++i) { if (std::size_t(i - ctrl_indices.begin()) % m_width) { m_lattice_indices.insert(*(i - 1)); m_lattice_indices.insert(*i); } if (std::size_t(i - ctrl_indices.begin()) >= m_width) { m_lattice_indices.insert(*(i - m_width)); m_lattice_indices.insert(*i); } } } #if 0 { Array::iterator first = m_tess.m_indices.begin(); for ( std::size_t s = 0; s < m_tess.m_numStrips; s++ ) { Array::iterator last = first + m_tess.m_lenStrips; for ( Array::iterator i( first ); i + 2 != last; i += 2 ) { ArbitraryMeshTriangle_sumTangents( m_tess.m_vertices[*( i + 0 )], m_tess.m_vertices[*( i + 1 )], m_tess.m_vertices[*( i + 2 )] ); ArbitraryMeshTriangle_sumTangents( m_tess.m_vertices[*( i + 2 )], m_tess.m_vertices[*( i + 1 )], m_tess.m_vertices[*( i + 3 )] ); } first = last; } for ( Array::iterator i = m_tess.m_vertices.begin(); i != m_tess.m_vertices.end(); ++i ) { vector3_normalise( reinterpret_cast( ( *i ).tangent ) ); vector3_normalise( reinterpret_cast( ( *i ).bitangent ) ); } } #endif SceneChangeNotify(); } void Patch::InvertMatrix() { undoSave(); PatchControlArray_invert(m_ctrl, m_width, m_height); controlPointsChanged(); } void Patch::TransposeMatrix() { undoSave(); { Array tmp(m_width * m_height); copy_ctrl(tmp.data(), m_ctrl.data(), m_ctrl.data() + m_width * m_height); PatchControlIter from = tmp.data(); for (std::size_t h = 0; h != m_height; ++h) { PatchControlIter to = m_ctrl.data() + h; for (std::size_t w = 0; w != m_width; ++w, ++from, to += m_height) { *to = *from; } } } { std::size_t tmp = m_width; m_width = m_height; m_height = tmp; } controlPointsChanged(); } void Patch::Redisperse(EMatrixMajor mt) { std::size_t w, h, width, height, row_stride, col_stride; PatchControl *p1, *p2, *p3; undoSave(); switch (mt) { case COL: width = (m_width - 1) >> 1; height = m_height; col_stride = 1; row_stride = m_width; break; case ROW: width = (m_height - 1) >> 1; height = m_width; col_stride = m_width; row_stride = 1; break; default: ERROR_MESSAGE("neither row-major nor column-major"); return; } for (h = 0; h < height; h++) { p1 = m_ctrl.data() + (h * row_stride); for (w = 0; w < width; w++) { p2 = p1 + col_stride; p3 = p2 + col_stride; p2->m_vertex = vector3_mid(p1->m_vertex, p3->m_vertex); p1 = p3; } } controlPointsChanged(); } void Patch::Smooth(EMatrixMajor mt) { std::size_t w, h, width, height, row_stride, col_stride; bool wrap; PatchControl *p1, *p2, *p3, *p2b; undoSave(); switch (mt) { case COL: width = (m_width - 1) >> 1; height = m_height; col_stride = 1; row_stride = m_width; break; case ROW: width = (m_height - 1) >> 1; height = m_width; col_stride = m_width; row_stride = 1; break; default: ERROR_MESSAGE("neither row-major nor column-major"); return; } wrap = true; for (h = 0; h < height; h++) { p1 = m_ctrl.data() + (h * row_stride); p2 = p1 + (2 * width) * col_stride; //globalErrorStream() << "compare " << p1->m_vertex << " and " << p2->m_vertex << "\n"; if (vector3_length_squared(vector3_subtracted(p1->m_vertex, p2->m_vertex)) > 1.0) { //globalErrorStream() << "too far\n"; wrap = false; break; } } for (h = 0; h < height; h++) { p1 = m_ctrl.data() + (h * row_stride) + col_stride; for (w = 0; w < width - 1; w++) { p2 = p1 + col_stride; p3 = p2 + col_stride; p2->m_vertex = vector3_mid(p1->m_vertex, p3->m_vertex); p1 = p3; } if (wrap) { p1 = m_ctrl.data() + (h * row_stride) + (2 * width - 1) * col_stride; p2 = m_ctrl.data() + (h * row_stride); p2b = m_ctrl.data() + (h * row_stride) + (2 * width) * col_stride; p3 = m_ctrl.data() + (h * row_stride) + col_stride; p2->m_vertex = p2b->m_vertex = vector3_mid(p1->m_vertex, p3->m_vertex); } } controlPointsChanged(); } void Patch::InsertRemove(bool bInsert, bool bColumn, bool bFirst) { undoSave(); if (bInsert) { if (bColumn && (m_width + 2 <= MAX_PATCH_WIDTH)) { InsertPoints(COL, bFirst); } else if (m_height + 2 <= MAX_PATCH_HEIGHT) { InsertPoints(ROW, bFirst); } } else { if (bColumn && (m_width - 2 >= MIN_PATCH_WIDTH)) { RemovePoints(COL, bFirst); } else if (m_height - 2 >= MIN_PATCH_HEIGHT) { RemovePoints(ROW, bFirst); } } controlPointsChanged(); } Patch *Patch::MakeCap(Patch *patch, EPatchCap eType, EMatrixMajor mt, bool bFirst) { std::size_t i, width, height; switch (mt) { case ROW: width = m_width; height = m_height; break; case COL: width = m_height; height = m_width; break; default: ERROR_MESSAGE("neither row-major nor column-major"); return 0; } Array p(width); std::size_t nIndex = (bFirst) ? 0 : height - 1; if (mt == ROW) { for (i = 0; i < width; i++) { p[(bFirst) ? i : (width - 1) - i] = ctrlAt(nIndex, i).m_vertex; } } else { for (i = 0; i < width; i++) { p[(bFirst) ? i : (width - 1) - i] = ctrlAt(i, nIndex).m_vertex; } } patch->ConstructSeam(eType, p.data(), width); return patch; } void Patch::FlipTexture(int nAxis) { undoSave(); for (PatchControlIter i = m_ctrl.data(); i != m_ctrl.data() + m_ctrl.size(); ++i) { (*i).m_texcoord[nAxis] = -(*i).m_texcoord[nAxis]; } controlPointsChanged(); } void Patch::TranslateTexture(float s, float t) { undoSave(); s = -1 * s / m_state->getTexture().width; t = t / m_state->getTexture().height; for (PatchControlIter i = m_ctrl.data(); i != m_ctrl.data() + m_ctrl.size(); ++i) { (*i).m_texcoord[0] += s; (*i).m_texcoord[1] += t; } controlPointsChanged(); } void Patch::ScaleTexture(float s, float t) { undoSave(); for (PatchControlIter i = m_ctrl.data(); i != m_ctrl.data() + m_ctrl.size(); ++i) { (*i).m_texcoord[0] *= s; (*i).m_texcoord[1] *= t; } controlPointsChanged(); } void Patch::RotateTexture(float angle) { undoSave(); const float s = static_cast( sin(degrees_to_radians(angle))); const float c = static_cast( cos(degrees_to_radians(angle))); for (PatchControlIter i = m_ctrl.data(); i != m_ctrl.data() + m_ctrl.size(); ++i) { const float x = (*i).m_texcoord[0]; const float y = (*i).m_texcoord[1]; (*i).m_texcoord[0] = (x * c) - (y * s); (*i).m_texcoord[1] = (y * c) + (x * s); } controlPointsChanged(); } void Patch::SetTextureRepeat(float s, float t) { std::size_t w, h; float si, ti, sc, tc; PatchControl *pDest; undoSave(); si = s / (float) (m_width - 1); ti = t / (float) (m_height - 1); pDest = m_ctrl.data(); for (h = 0, tc = 0.0f; h < m_height; h++, tc += ti) { for (w = 0, sc = 0.0f; w < m_width; w++, sc += si) { pDest->m_texcoord[0] = sc; pDest->m_texcoord[1] = tc; pDest++; } } controlPointsChanged(); } /* void Patch::SetTextureInfo(texdef_t *pt) { if(pt->getShift()[0] || pt->getShift()[1]) TranslateTexture (pt->getShift()[0], pt->getShift()[1]); else if(pt->getScale()[0] || pt->getScale()[1]) { if(pt->getScale()[0] == 0.0f) pt->setScale(0, 1.0f); if(pt->getScale()[1] == 0.0f) pt->setScale(1, 1.0f); ScaleTexture (pt->getScale()[0], pt->getScale()[1]); } else if(pt->rotate) RotateTexture (pt->rotate); } */ inline int texture_axis(const Vector3 &normal) { // axis dominance order: Z, X, Y return (normal.x() >= normal.y()) ? (normal.x() > normal.z()) ? 0 : 2 : (normal.y() > normal.z()) ? 1 : 2; } void Patch::CapTexture() { const PatchControl &p1 = m_ctrl[m_width]; const PatchControl &p2 = m_ctrl[m_width * (m_height - 1)]; const PatchControl &p3 = m_ctrl[(m_width * m_height) - 1]; Vector3 normal(g_vector3_identity); { Vector3 tmp(vector3_cross( vector3_subtracted(p2.m_vertex, m_ctrl[0].m_vertex), vector3_subtracted(p3.m_vertex, m_ctrl[0].m_vertex) )); if (!vector3_equal(tmp, g_vector3_identity)) { vector3_add(normal, tmp); } } { Vector3 tmp(vector3_cross( vector3_subtracted(p1.m_vertex, p3.m_vertex), vector3_subtracted(m_ctrl[0].m_vertex, p3.m_vertex) )); if (!vector3_equal(tmp, g_vector3_identity)) { vector3_add(normal, tmp); } } ProjectTexture(texture_axis(normal)); } // uses longest parallel chord to calculate texture coords for each row/col void Patch::NaturalTexture() { undoSave(); { float fSize = (float) m_state->getTexture().width * Texdef_getDefaultTextureScale(); double texBest = 0; double tex = 0; PatchControl *pWidth = m_ctrl.data(); for (std::size_t w = 0; w < m_width; w++, pWidth++) { { PatchControl *pHeight = pWidth; for (std::size_t h = 0; h < m_height; h++, pHeight += m_width) { pHeight->m_texcoord[0] = static_cast( tex ); } } if (w + 1 == m_width) { break; } { PatchControl *pHeight = pWidth; for (std::size_t h = 0; h < m_height; h++, pHeight += m_width) { Vector3 v(vector3_subtracted(pHeight->m_vertex, (pHeight + 1)->m_vertex)); double length = tex + (vector3_length(v) / fSize); if (fabs(length) > texBest) { texBest = length; } } } tex = texBest; } } { float fSize = -(float) m_state->getTexture().height * Texdef_getDefaultTextureScale(); double texBest = 0; double tex = 0; PatchControl *pHeight = m_ctrl.data(); for (std::size_t h = 0; h < m_height; h++, pHeight += m_width) { { PatchControl *pWidth = pHeight; for (std::size_t w = 0; w < m_width; w++, pWidth++) { pWidth->m_texcoord[1] = static_cast( tex ); } } if (h + 1 == m_height) { break; } { PatchControl *pWidth = pHeight; for (std::size_t w = 0; w < m_width; w++, pWidth++) { Vector3 v(vector3_subtracted(pWidth->m_vertex, (pWidth + m_width)->m_vertex)); double length = tex + (vector3_length(v) / fSize); if (fabs(length) > texBest) { texBest = length; } } } tex = texBest; } } controlPointsChanged(); } void Patch::IdentityColour() { PatchControl *pCtrl = m_ctrl.data(); for (std::size_t h = 0; h < m_height; h++) { for (std::size_t w = 0; w < m_width; w++, ++pCtrl) { pCtrl->m_color = Vector4(1,1,1,1); } } } // private: void Patch::AccumulateBBox() { m_aabb_local = AABB(); for (PatchControlArray::iterator i = m_ctrlTransformed.begin(); i != m_ctrlTransformed.end(); ++i) { aabb_extend_by_point_safe(m_aabb_local, (*i).m_vertex); } m_boundsChanged(); m_lightsChanged(); } void Patch::InsertPoints(EMatrixMajor mt, bool bFirst) { std::size_t width, height, row_stride, col_stride; switch (mt) { case ROW: col_stride = 1; row_stride = m_width; width = m_width; height = m_height; break; case COL: col_stride = m_width; row_stride = 1; width = m_height; height = m_width; break; default: ERROR_MESSAGE("neither row-major nor column-major"); return; } std::size_t pos = 0; { PatchControl *p1 = m_ctrl.data(); /* if(GlobalSelectionSystem().countSelected() != 0) { scene::Instance& instance = GlobalSelectionSystem().ultimateSelected(); PatchInstance* patch = Instance_getPatch(instance); patch->m_selectable.isSelected(); } */ for (std::size_t w = 0; w != width; ++w, p1 += col_stride) { { PatchControl *p2 = p1; for (std::size_t h = 1; h < height; h += 2, p2 += 2 * row_stride) { if (0) { //p2->m_selectable.isSelected()) pos = h; break; } } if (pos != 0) { break; } } { PatchControl *p2 = p1; for (std::size_t h = 0; h < height; h += 2, p2 += 2 * row_stride) { if (0) { //p2->m_selectable.isSelected()) pos = h; break; } } if (pos != 0) { break; } } } } Array tmp(m_ctrl); std::size_t row_stride2, col_stride2; switch (mt) { case ROW: setDims(m_width, m_height + 2); col_stride2 = 1; row_stride2 = m_width; break; case COL: setDims(m_width + 2, m_height); col_stride2 = m_width; row_stride2 = 1; break; default: ERROR_MESSAGE("neither row-major nor column-major"); return; } if (bFirst) { pos = height - 1; } else { pos = 2; } if (pos >= height) { if (bFirst) { pos = height - 1; } else { pos = 2; } } else if (pos == 0) { pos = 2; } else if (pos % 2) { ++pos; } for (std::size_t w = 0; w != width; ++w) { PatchControl *p1 = tmp.data() + (w * col_stride); PatchControl *p2 = m_ctrl.data() + (w * col_stride2); for (std::size_t h = 0; h != height; ++h, p2 += row_stride2, p1 += row_stride) { if (h == pos) { p2 += 2 * row_stride2; } *p2 = *p1; } p1 = tmp.data() + (w * col_stride + pos * row_stride); p2 = m_ctrl.data() + (w * col_stride2 + pos * row_stride2); PatchControl *r2a = (p2 + row_stride2); PatchControl *r2b = (p2 - row_stride2); PatchControl *c2a = (p1 - 2 * row_stride); PatchControl *c2b = (p1 - row_stride); // set two new row points *(p2 + 2 * row_stride2) = *p1; *r2a = *c2b; for (std::size_t i = 0; i != 3; ++i) { r2a->m_vertex[i] = float_mid(c2b->m_vertex[i], p1->m_vertex[i]); r2b->m_vertex[i] = float_mid(c2a->m_vertex[i], c2b->m_vertex[i]); p2->m_vertex[i] = float_mid(r2a->m_vertex[i], r2b->m_vertex[i]); } for (std::size_t i = 0; i != 2; ++i) { r2a->m_texcoord[i] = float_mid(c2b->m_texcoord[i], p1->m_texcoord[i]); r2b->m_texcoord[i] = float_mid(c2a->m_texcoord[i], c2b->m_texcoord[i]); p2->m_texcoord[i] = float_mid(r2a->m_texcoord[i], r2b->m_texcoord[i]); } for (std::size_t i = 0; i != 4; ++i) { r2a->m_color[i] = float_mid(c2b->m_color[i], p1->m_color[i]); r2b->m_color[i] = float_mid(c2a->m_color[i], c2b->m_color[i]); p2->m_color[i] = float_mid(r2a->m_color[i], r2b->m_color[i]); } } } void Patch::RemovePoints(EMatrixMajor mt, bool bFirst) { std::size_t width, height, row_stride, col_stride; switch (mt) { case ROW: col_stride = 1; row_stride = m_width; width = m_width; height = m_height; break; case COL: col_stride = m_width; row_stride = 1; width = m_height; height = m_width; break; default: ERROR_MESSAGE("neither row-major nor column-major"); return; } std::size_t pos = 0; { PatchControl *p1 = m_ctrl.data(); for (std::size_t w = 0; w != width; ++w, p1 += col_stride) { { PatchControl *p2 = p1; for (std::size_t h = 1; h < height; h += 2, p2 += 2 * row_stride) { if (0) { //p2->m_selectable.isSelected()) pos = h; break; } } if (pos != 0) { break; } } { PatchControl *p2 = p1; for (std::size_t h = 0; h < height; h += 2, p2 += 2 * row_stride) { if (0) { //p2->m_selectable.isSelected()) pos = h; break; } } if (pos != 0) { break; } } } } Array tmp(m_ctrl); std::size_t row_stride2, col_stride2; switch (mt) { case ROW: setDims(m_width, m_height - 2); col_stride2 = 1; row_stride2 = m_width; break; case COL: setDims(m_width - 2, m_height); col_stride2 = m_width; row_stride2 = 1; break; default: ERROR_MESSAGE("neither row-major nor column-major"); return; } if (bFirst) { pos = height - 3; } else { pos = 2; } if (pos >= height) { if (bFirst) { pos = height - 3; } else { pos = 2; } } else if (pos == 0) { pos = 2; } else if (pos > height - 3) { pos = height - 3; } else if (pos % 2) { ++pos; } for (std::size_t w = 0; w != width; w++) { PatchControl *p1 = tmp.data() + (w * col_stride); PatchControl *p2 = m_ctrl.data() + (w * col_stride2); for (std::size_t h = 0; h != height; ++h, p2 += row_stride2, p1 += row_stride) { if (h == pos) { p1 += 2 * row_stride2; h += 2; } *p2 = *p1; } p1 = tmp.data() + (w * col_stride + pos * row_stride); p2 = m_ctrl.data() + (w * col_stride2 + pos * row_stride2); for (std::size_t i = 0; i < 3; i++) { (p2 - row_stride2)->m_vertex[i] = ((p1 + 2 * row_stride)->m_vertex[i] + (p1 - 2 * row_stride)->m_vertex[i]) * 0.5f; (p2 - row_stride2)->m_vertex[i] = (p2 - row_stride2)->m_vertex[i] + (2.0f * ((p1)->m_vertex[i] - (p2 - row_stride2)->m_vertex[i])); } for (std::size_t i = 0; i < 2; i++) { (p2 - row_stride2)->m_texcoord[i] = ((p1 + 2 * row_stride)->m_texcoord[i] + (p1 - 2 * row_stride)->m_texcoord[i]) * 0.5f; (p2 - row_stride2)->m_texcoord[i] = (p2 - row_stride2)->m_texcoord[i] + (2.0f * ((p1)->m_texcoord[i] - (p2 - row_stride2)->m_texcoord[i])); } } } void Patch::ConstructSeam(EPatchCap eType, Vector3 *p, std::size_t width) { switch (eType) { case eCapIBevel: { setDims(3, 3); m_ctrl[0].m_vertex = p[0]; m_ctrl[1].m_vertex = p[1]; m_ctrl[2].m_vertex = p[1]; m_ctrl[3].m_vertex = p[1]; m_ctrl[4].m_vertex = p[1]; m_ctrl[5].m_vertex = p[1]; m_ctrl[6].m_vertex = p[2]; m_ctrl[7].m_vertex = p[1]; m_ctrl[8].m_vertex = p[1]; } break; case eCapBevel: { setDims(3, 3); Vector3 p3(vector3_added(p[2], vector3_subtracted(p[0], p[1]))); m_ctrl[0].m_vertex = p3; m_ctrl[1].m_vertex = p3; m_ctrl[2].m_vertex = p[2]; m_ctrl[3].m_vertex = p3; m_ctrl[4].m_vertex = p3; m_ctrl[5].m_vertex = p[1]; m_ctrl[6].m_vertex = p3; m_ctrl[7].m_vertex = p3; m_ctrl[8].m_vertex = p[0]; } break; case eCapEndCap: { Vector3 p5(vector3_mid(p[0], p[4])); setDims(3, 3); m_ctrl[0].m_vertex = p[0]; m_ctrl[1].m_vertex = p5; m_ctrl[2].m_vertex = p[4]; m_ctrl[3].m_vertex = p[1]; m_ctrl[4].m_vertex = p[2]; m_ctrl[5].m_vertex = p[3]; m_ctrl[6].m_vertex = p[2]; m_ctrl[7].m_vertex = p[2]; m_ctrl[8].m_vertex = p[2]; } break; case eCapIEndCap: { setDims(5, 3); m_ctrl[0].m_vertex = p[4]; m_ctrl[1].m_vertex = p[3]; m_ctrl[2].m_vertex = p[2]; m_ctrl[3].m_vertex = p[1]; m_ctrl[4].m_vertex = p[0]; m_ctrl[5].m_vertex = p[3]; m_ctrl[6].m_vertex = p[3]; m_ctrl[7].m_vertex = p[2]; m_ctrl[8].m_vertex = p[1]; m_ctrl[9].m_vertex = p[1]; m_ctrl[10].m_vertex = p[3]; m_ctrl[11].m_vertex = p[3]; m_ctrl[12].m_vertex = p[2]; m_ctrl[13].m_vertex = p[1]; m_ctrl[14].m_vertex = p[1]; } break; case eCapCylinder: { std::size_t mid = (width - 1) >> 1; bool degenerate = (mid % 2) != 0; std::size_t newHeight = mid + (degenerate ? 2 : 1); setDims(3, newHeight); if (degenerate) { ++mid; for (std::size_t i = width; i != width + 2; ++i) { p[i] = p[width - 1]; } } { PatchControl *pCtrl = m_ctrl.data(); for (std::size_t i = 0; i != m_height; ++i, pCtrl += m_width) { pCtrl->m_vertex = p[i]; } } { PatchControl *pCtrl = m_ctrl.data() + 2; std::size_t h = m_height - 1; for (std::size_t i = 0; i != m_height; ++i, pCtrl += m_width) { pCtrl->m_vertex = p[h + (h - i)]; } } Redisperse(COL); } break; default: ERROR_MESSAGE("invalid patch-cap type"); return; } CapTexture(); controlPointsChanged(); } void Patch::ProjectTexture(int nAxis) { undoSave(); int s, t; switch (nAxis) { case 2: s = 0; t = 1; break; case 0: s = 1; t = 2; break; case 1: s = 0; t = 2; break; default: ERROR_MESSAGE("invalid axis"); return; } float fWidth = 1 / (m_state->getTexture().width * Texdef_getDefaultTextureScale()); float fHeight = 1 / (m_state->getTexture().height * -Texdef_getDefaultTextureScale()); for (PatchControlIter i = m_ctrl.data(); i != m_ctrl.data() + m_ctrl.size(); ++i) { (*i).m_texcoord[0] = (*i).m_vertex[s] * fWidth; (*i).m_texcoord[1] = (*i).m_vertex[t] * fHeight; } controlPointsChanged(); } void Patch::constructPlane(const AABB &aabb, int axis, std::size_t width, std::size_t height) { setDims(width, height); int x, y, z; switch (axis) { case 2: x = 0; y = 1; z = 2; break; case 1: x = 0; y = 2; z = 1; break; case 0: x = 1; y = 2; z = 0; break; default: ERROR_MESSAGE("invalid view-type"); return; } if (m_width < MIN_PATCH_WIDTH || m_width > MAX_PATCH_WIDTH) { m_width = 3; } if (m_height < MIN_PATCH_HEIGHT || m_height > MAX_PATCH_HEIGHT) { m_height = 3; } Vector3 vStart; vStart[x] = aabb.origin[x] - aabb.extents[x]; vStart[y] = aabb.origin[y] - aabb.extents[y]; vStart[z] = aabb.origin[z]; float xAdj = fabsf((vStart[x] - (aabb.origin[x] + aabb.extents[x])) / (float) (m_width - 1)); float yAdj = fabsf((vStart[y] - (aabb.origin[y] + aabb.extents[y])) / (float) (m_height - 1)); Vector3 vTmp; vTmp[z] = vStart[z]; PatchControl *pCtrl = m_ctrl.data(); vTmp[y] = vStart[y]; for (std::size_t h = 0; h < m_height; h++) { vTmp[x] = vStart[x]; for (std::size_t w = 0; w < m_width; w++, ++pCtrl) { pCtrl->m_vertex = vTmp; vTmp[x] += xAdj; } vTmp[y] += yAdj; } IdentityColour(); NaturalTexture(); } void Patch::ConstructPrefab(const AABB &aabb, EPatchPrefab eType, int axis, std::size_t width, std::size_t height) { Vector3 vPos[3]; if (eType != ePlane) { vPos[0] = vector3_subtracted(aabb.origin, aabb.extents); vPos[1] = aabb.origin; vPos[2] = vector3_added(aabb.origin, aabb.extents); } if (eType == ePlane) { constructPlane(aabb, axis, width, height); } else if (eType == eSqCylinder || eType == eCylinder || eType == eDenseCylinder || eType == eVeryDenseCylinder || eType == eCone || eType == eSphere) { unsigned char *pIndex; unsigned char pCylIndex[] = { 0, 0, 1, 0, 2, 0, 2, 1, 2, 2, 1, 2, 0, 2, 0, 1, 0, 0 }; PatchControl *pStart; switch (eType) { case eSqCylinder: setDims(9, 3); pStart = m_ctrl.data(); break; case eDenseCylinder: case eVeryDenseCylinder: case eCylinder: setDims(9, 3); pStart = m_ctrl.data() + 1; break; case eCone: setDims(9, 3); pStart = m_ctrl.data() + 1; break; case eSphere: setDims(9, 5); pStart = m_ctrl.data() + (9 + 1); break; default: ERROR_MESSAGE("this should be unreachable"); return; } for (std::size_t h = 0; h < 3; h++, pStart += 9) { pIndex = pCylIndex; PatchControl *pCtrl = pStart; for (std::size_t w = 0; w < 8; w++, pCtrl++) { pCtrl->m_vertex[0] = vPos[pIndex[0]][0]; pCtrl->m_vertex[1] = vPos[pIndex[1]][1]; pCtrl->m_vertex[2] = vPos[h][2]; pIndex += 2; } } switch (eType) { case eSqCylinder: { PatchControl *pCtrl = m_ctrl.data(); for (std::size_t h = 0; h < 3; h++, pCtrl += 9) { pCtrl[8].m_vertex = pCtrl[0].m_vertex; } } break; case eDenseCylinder: case eVeryDenseCylinder: case eCylinder: { PatchControl *pCtrl = m_ctrl.data(); for (std::size_t h = 0; h < 3; h++, pCtrl += 9) { pCtrl[0].m_vertex = pCtrl[8].m_vertex; } } break; case eCone: { PatchControl *pCtrl = m_ctrl.data(); for (std::size_t h = 0; h < 2; h++, pCtrl += 9) { pCtrl[0].m_vertex = pCtrl[8].m_vertex; } } { PatchControl *pCtrl = m_ctrl.data() + 9 * 2; for (std::size_t w = 0; w < 9; w++, pCtrl++) { pCtrl->m_vertex[0] = vPos[1][0]; pCtrl->m_vertex[1] = vPos[1][1]; pCtrl->m_vertex[2] = vPos[2][2]; } } break; case eSphere: { PatchControl *pCtrl = m_ctrl.data() + 9; for (std::size_t h = 0; h < 3; h++, pCtrl += 9) { pCtrl[0].m_vertex = pCtrl[8].m_vertex; } } { PatchControl *pCtrl = m_ctrl.data(); for (std::size_t w = 0; w < 9; w++, pCtrl++) { pCtrl->m_vertex[0] = vPos[1][0]; pCtrl->m_vertex[1] = vPos[1][1]; pCtrl->m_vertex[2] = vPos[0][2]; } } { PatchControl *pCtrl = m_ctrl.data() + (9 * 4); for (std::size_t w = 0; w < 9; w++, pCtrl++) { pCtrl->m_vertex[0] = vPos[1][0]; pCtrl->m_vertex[1] = vPos[1][1]; pCtrl->m_vertex[2] = vPos[2][2]; } } break; default: ERROR_MESSAGE("this should be unreachable"); return; } } else if (eType == eXactCylinder) { int n = (width - 1) / 2; // n = number of segments setDims(width, height); // vPos[0] = vector3_subtracted(aabb.origin, aabb.extents); // vPos[1] = aabb.origin; // vPos[2] = vector3_added(aabb.origin, aabb.extents); float f = 1 / cos(M_PI / n); for (std::size_t i = 0; i < width; ++i) { float angle = (M_PI * i) / n; // 0 to 2pi float x = vPos[1][0] + (vPos[2][0] - vPos[1][0]) * cos(angle) * ((i & 1) ? f : 1.0f); float y = vPos[1][1] + (vPos[2][1] - vPos[1][1]) * sin(angle) * ((i & 1) ? f : 1.0f); for (std::size_t j = 0; j < height; ++j) { float z = vPos[0][2] + (vPos[2][2] - vPos[0][2]) * (j / (float) (height - 1)); PatchControl *v; v = &m_ctrl.data()[j * width + i]; v->m_vertex[0] = x; v->m_vertex[1] = y; v->m_vertex[2] = z; } } } else if (eType == eXactCone) { int n = (width - 1) / 2; // n = number of segments setDims(width, height); // vPos[0] = vector3_subtracted(aabb.origin, aabb.extents); // vPos[1] = aabb.origin; // vPos[2] = vector3_added(aabb.origin, aabb.extents); float f = 1 / cos(M_PI / n); for (std::size_t i = 0; i < width; ++i) { float angle = (M_PI * i) / n; for (std::size_t j = 0; j < height; ++j) { float x = vPos[1][0] + (1.0f - (j / (float) (height - 1))) * (vPos[2][0] - vPos[1][0]) * cos(angle) * ((i & 1) ? f : 1.0f); float y = vPos[1][1] + (1.0f - (j / (float) (height - 1))) * (vPos[2][1] - vPos[1][1]) * sin(angle) * ((i & 1) ? f : 1.0f); float z = vPos[0][2] + (vPos[2][2] - vPos[0][2]) * (j / (float) (height - 1)); PatchControl *v; v = &m_ctrl.data()[j * width + i]; v->m_vertex[0] = x; v->m_vertex[1] = y; v->m_vertex[2] = z; } } } else if (eType == eXactSphere) { int n = (width - 1) / 2; // n = number of segments (yaw) int m = (height - 1) / 2; // m = number of segments (pitch) setDims(width, height); // vPos[0] = vector3_subtracted(aabb.origin, aabb.extents); // vPos[1] = aabb.origin; // vPos[2] = vector3_added(aabb.origin, aabb.extents); float f = 1 / cos(M_PI / n); float g = 1 / cos(M_PI / (2 * m)); for (std::size_t i = 0; i < width; ++i) { float angle = (M_PI * i) / n; for (std::size_t j = 0; j < height; ++j) { float angle2 = (M_PI * j) / (2 * m); float x = vPos[1][0] + (vPos[2][0] - vPos[1][0]) * sin(angle2) * ((j & 1) ? g : 1.0f) * cos(angle) * ((i & 1) ? f : 1.0f); float y = vPos[1][1] + (vPos[2][1] - vPos[1][1]) * sin(angle2) * ((j & 1) ? g : 1.0f) * sin(angle) * ((i & 1) ? f : 1.0f); float z = vPos[1][2] + (vPos[2][2] - vPos[1][2]) * -cos(angle2) * ((j & 1) ? g : 1.0f); PatchControl *v; v = &m_ctrl.data()[j * width + i]; v->m_vertex[0] = x; v->m_vertex[1] = y; v->m_vertex[2] = z; } } } else if (eType == eBevel) { unsigned char *pIndex; unsigned char pBevIndex[] = { 0, 0, 2, 0, 2, 2, }; setDims(3, 3); PatchControl *pCtrl = m_ctrl.data(); for (std::size_t h = 0; h < 3; h++) { pIndex = pBevIndex; for (std::size_t w = 0; w < 3; w++, pIndex += 2, pCtrl++) { pCtrl->m_vertex[0] = vPos[pIndex[0]][0]; pCtrl->m_vertex[1] = vPos[pIndex[1]][1]; pCtrl->m_vertex[2] = vPos[h][2]; } } } else if (eType == eEndCap) { unsigned char *pIndex; unsigned char pEndIndex[] = { 2, 0, 2, 2, 1, 2, 0, 2, 0, 0, }; setDims(5, 3); PatchControl *pCtrl = m_ctrl.data(); for (std::size_t h = 0; h < 3; h++) { pIndex = pEndIndex; for (std::size_t w = 0; w < 5; w++, pIndex += 2, pCtrl++) { pCtrl->m_vertex[0] = vPos[pIndex[0]][0]; pCtrl->m_vertex[1] = vPos[pIndex[1]][1]; pCtrl->m_vertex[2] = vPos[h][2]; } } } if (eType == eDenseCylinder) { InsertRemove(true, false, true); } if (eType == eVeryDenseCylinder) { InsertRemove(true, false, false); InsertRemove(true, false, true); } IdentityColour(); NaturalTexture(); } void Patch::RenderDebug(RenderStateFlags state) const { for (std::size_t i = 0; i < m_tess.m_numStrips; i++) { glBegin(GL_QUAD_STRIP); for (std::size_t j = 0; j < m_tess.m_lenStrips; j++) { glNormal3fv(normal3f_to_array( (m_tess.m_vertices.data() + m_tess.m_indices[i * m_tess.m_lenStrips + j])->normal)); glTexCoord2fv(texcoord2f_to_array( (m_tess.m_vertices.data() + m_tess.m_indices[i * m_tess.m_lenStrips + j])->texcoord)); glVertex3fv(vertex3f_to_array( (m_tess.m_vertices.data() + m_tess.m_indices[i * m_tess.m_lenStrips + j])->vertex)); } glEnd(); } } #include "patchdialog.h" bool PatchInspector_IsSelected(int x, int y); void patch_draw_sphere(const Vector3 origin, float radius, int sides) { if (radius <= 0) { return; } const double dt = c_2pi / static_cast( sides ); const double dp = c_pi / static_cast( sides ); for (int i = 0; i <= sides - 1; ++i) { for (int j = 0; j <= sides - 2; ++j) { const double t = i * dt; const double p = (j * dp) - (c_pi / 2.0); { Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t, p), radius))); glVertex3fv(vector3_to_array(v)); } { Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t, p + dp), radius))); glVertex3fv(vector3_to_array(v)); } { Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t + dt, p + dp), radius))); glVertex3fv(vector3_to_array(v)); } { Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t, p), radius))); glVertex3fv(vector3_to_array(v)); } { Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t + dt, p + dp), radius))); glVertex3fv(vector3_to_array(v)); } { Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t + dt, p), radius))); glVertex3fv(vector3_to_array(v)); } } } { const double p = (sides - 1) * dp - (c_pi / 2.0); for (int i = 0; i <= sides - 1; ++i) { const double t = i * dt; { Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t, p), radius))); glVertex3fv(vector3_to_array(v)); } { Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t + dt, p + dp), radius))); glVertex3fv(vector3_to_array(v)); } { Vector3 v(vector3_added(origin, vector3_scaled(vector3_for_spherical(t + dt, p), radius))); glVertex3fv(vector3_to_array(v)); } } } } void RenderablePatchFixedSolid::RenderNormals() const { const std::size_t width = m_tess.m_numStrips + 1; const std::size_t height = m_tess.m_lenStrips >> 1; glBegin(GL_TRIANGLES); for (std::size_t i = 0; i < width; i++) { for (std::size_t j = 0; j < height; j++) { Vector3 pos = (m_tess.m_vertices.data() + (j * width + i))->vertex; Vector4 colour = (m_tess.m_vertices.data() + (j * width + i))->colour; /* color the currently selected bit */ if (PatchInspector_IsSelected((int)i, (int)j)) { glColor3f(1,0,0); patch_draw_sphere(pos, 8, 4); } else { glColor3f(1,1,1); patch_draw_sphere(pos, 2, 4); } /*{ Vector3 vNormal( vector3_added( vertex3f_to_vector3((m_tess.m_vertices.data() + (j * width + i))->vertex), vector3_scaled( normal3f_to_vector3((m_tess.m_vertices.data() + (j * width + i))->normal), 8) ) ); glVertex3fv(vertex3f_to_array((m_tess.m_vertices.data() + (j * width + i))->vertex)); glVertex3fv(&vNormal[0]); } { Vector3 vNormal( vector3_added( vertex3f_to_vector3((m_tess.m_vertices.data() + (j * width + i))->vertex), vector3_scaled( normal3f_to_vector3((m_tess.m_vertices.data() + (j * width + i))->tangent), 8) ) ); glVertex3fv(vertex3f_to_array((m_tess.m_vertices.data() + (j * width + i))->vertex)); glVertex3fv(&vNormal[0]); } { Vector3 vNormal( vector3_added( vertex3f_to_vector3(), vector3_scaled( normal3f_to_vector3((m_tess.m_vertices.data() + (j * width + i))->bitangent), 8) ) ); glVertex3fv(vertex3f_to_array((m_tess.m_vertices.data() + (j * width + i))->vertex)); glVertex3fv(&vNormal[0]); }*/ } } glEnd(); glColor3f( 1, 1, 1 ); } const int DEGEN_0a = 0x01; const int DEGEN_1a = 0x02; const int DEGEN_2a = 0x04; const int DEGEN_0b = 0x08; const int DEGEN_1b = 0x10; const int DEGEN_2b = 0x20; const int SPLIT = 0x40; const int AVERAGE = 0x80; unsigned int subarray_get_degen(PatchControlIter subarray, std::size_t strideU, std::size_t strideV) { unsigned int nDegen = 0; const PatchControl *p1; const PatchControl *p2; p1 = subarray; p2 = p1 + strideU; if (vector3_equal(p1->m_vertex, p2->m_vertex)) { nDegen |= DEGEN_0a; } p1 = p2; p2 = p1 + strideU; if (vector3_equal(p1->m_vertex, p2->m_vertex)) { nDegen |= DEGEN_0b; } p1 = subarray + strideV; p2 = p1 + strideU; if (vector3_equal(p1->m_vertex, p2->m_vertex)) { nDegen |= DEGEN_1a; } p1 = p2; p2 = p1 + strideU; if (vector3_equal(p1->m_vertex, p2->m_vertex)) { nDegen |= DEGEN_1b; } p1 = subarray + (strideV << 1); p2 = p1 + strideU; if (vector3_equal(p1->m_vertex, p2->m_vertex)) { nDegen |= DEGEN_2a; } p1 = p2; p2 = p1 + strideU; if (vector3_equal(p1->m_vertex, p2->m_vertex)) { nDegen |= DEGEN_2b; } return nDegen; } inline void deCasteljau3(const Vector3 &P0, const Vector3 &P1, const Vector3 &P2, Vector3 &P01, Vector3 &P12, Vector3 &P012) { P01 = vector3_mid(P0, P1); P12 = vector3_mid(P1, P2); P012 = vector3_mid(P01, P12); } inline void BezierInterpolate4(const Vector4 &start, Vector4 &left, Vector4 &mid, Vector4 &right, const Vector4 &end) { left = vector4_mid(start, mid); right = vector4_mid(mid, end); mid = vector4_mid(left, right); } inline void BezierInterpolate3(const Vector3 &start, Vector3 &left, Vector3 &mid, Vector3 &right, const Vector3 &end) { left = vector3_mid(start, mid); right = vector3_mid(mid, end); mid = vector3_mid(left, right); } inline void BezierInterpolate2(const Vector2 &start, Vector2 &left, Vector2 &mid, Vector2 &right, const Vector2 &end) { left[0] = float_mid(start[0], mid[0]); left[1] = float_mid(start[1], mid[1]); right[0] = float_mid(mid[0], end[0]); right[1] = float_mid(mid[1], end[1]); mid[0] = float_mid(left[0], right[0]); mid[1] = float_mid(left[1], right[1]); } inline Vector2 &texcoord_for_index(Array &vertices, std::size_t index) { return reinterpret_cast( vertices[index].texcoord ); } inline Vector4 &colour_for_index(Array &vertices, std::size_t index) { return reinterpret_cast( vertices[index].colour ); } inline Vector3 &vertex_for_index(Array &vertices, std::size_t index) { return reinterpret_cast( vertices[index].vertex ); } inline Vector3 &normal_for_index(Array &vertices, std::size_t index) { return reinterpret_cast( vertices[index].normal ); } inline Vector3 &tangent_for_index(Array &vertices, std::size_t index) { return reinterpret_cast( vertices[index].tangent ); } inline Vector3 &bitangent_for_index(Array &vertices, std::size_t index) { return reinterpret_cast( vertices[index].bitangent ); } inline const Vector2 &texcoord_for_index(const Array &vertices, std::size_t index) { return reinterpret_cast( vertices[index].texcoord ); } inline const Vector4 &colour_for_index(const Array &vertices, std::size_t index) { return reinterpret_cast( vertices[index].colour ); } inline const Vector3 &vertex_for_index(const Array &vertices, std::size_t index) { return reinterpret_cast( vertices[index].vertex ); } inline const Vector3 &normal_for_index(const Array &vertices, std::size_t index) { return reinterpret_cast( vertices[index].normal ); } inline const Vector3 &tangent_for_index(const Array &vertices, std::size_t index) { return reinterpret_cast( vertices[index].tangent ); } inline const Vector3 &bitangent_for_index(const Array &vertices, std::size_t index) { return reinterpret_cast( vertices[index].bitangent ); } #include "math/curve.h" inline PatchControl QuadraticBezier_evaluate(const PatchControl *firstPoint, double t) { PatchControl result = {Vector3(0, 0, 0), Vector2(0, 0), Vector4(0, 0, 0, 0)}; double denominator = 0; { double weight = BernsteinPolynomial::apply(t); vector3_add(result.m_vertex, vector3_scaled(firstPoint[0].m_vertex, weight)); vector2_add(result.m_texcoord, vector2_scaled(firstPoint[0].m_texcoord, weight)); denominator += weight; } { double weight = BernsteinPolynomial::apply(t); vector3_add(result.m_vertex, vector3_scaled(firstPoint[1].m_vertex, weight)); vector2_add(result.m_texcoord, vector2_scaled(firstPoint[1].m_texcoord, weight)); denominator += weight; } { double weight = BernsteinPolynomial::apply(t); vector3_add(result.m_vertex, vector3_scaled(firstPoint[2].m_vertex, weight)); vector2_add(result.m_texcoord, vector2_scaled(firstPoint[2].m_texcoord, weight)); denominator += weight; } vector3_divide(result.m_vertex, denominator); vector2_divide(result.m_texcoord, denominator); return result; } inline Vector3 vector3_linear_interpolated(const Vector3 &a, const Vector3 &b, double t) { return vector3_added(vector3_scaled(a, 1.0 - t), vector3_scaled(b, t)); } inline Vector2 vector2_linear_interpolated(const Vector2 &a, const Vector2 &b, double t) { return vector2_added(vector2_scaled(a, 1.0 - t), vector2_scaled(b, t)); } void normalise_safe(Vector3 &normal) { if (!vector3_equal(normal, g_vector3_identity)) { vector3_normalise(normal); } } inline void QuadraticBezier_evaluate(const PatchControl &a, const PatchControl &b, const PatchControl &c, double t, PatchControl &point, PatchControl &left, PatchControl &right) { left.m_vertex = vector3_linear_interpolated(a.m_vertex, b.m_vertex, t); left.m_texcoord = vector2_linear_interpolated(a.m_texcoord, b.m_texcoord, t); right.m_vertex = vector3_linear_interpolated(b.m_vertex, c.m_vertex, t); right.m_texcoord = vector2_linear_interpolated(b.m_texcoord, c.m_texcoord, t); point.m_vertex = vector3_linear_interpolated(left.m_vertex, right.m_vertex, t); point.m_texcoord = vector2_linear_interpolated(left.m_texcoord, right.m_texcoord, t); } void Patch::TesselateSubMatrixFixed(ArbitraryMeshVertex *vertices, std::size_t strideX, std::size_t strideY, unsigned int nFlagsX, unsigned int nFlagsY, PatchControl *subMatrix[3][3]) { double incrementU = 1.0 / m_subdivisions_x; double incrementV = 1.0 / m_subdivisions_y; const std::size_t width = m_subdivisions_x + 1; const std::size_t height = m_subdivisions_y + 1; for (std::size_t i = 0; i != width; ++i) { double tU = (i + 1 == width) ? 1 : i * incrementU; PatchControl pointX[3]; PatchControl leftX[3]; PatchControl rightX[3]; QuadraticBezier_evaluate(*subMatrix[0][0], *subMatrix[0][1], *subMatrix[0][2], tU, pointX[0], leftX[0], rightX[0]); QuadraticBezier_evaluate(*subMatrix[1][0], *subMatrix[1][1], *subMatrix[1][2], tU, pointX[1], leftX[1], rightX[1]); QuadraticBezier_evaluate(*subMatrix[2][0], *subMatrix[2][1], *subMatrix[2][2], tU, pointX[2], leftX[2], rightX[2]); ArbitraryMeshVertex *p = vertices + i * strideX; for (std::size_t j = 0; j != height; ++j) { if ((j == 0 || j + 1 == height) && (i == 0 || i + 1 == width)) { } else { double tV = (j + 1 == height) ? 1 : j * incrementV; PatchControl pointY[3]; PatchControl leftY[3]; PatchControl rightY[3]; QuadraticBezier_evaluate(*subMatrix[0][0], *subMatrix[1][0], *subMatrix[2][0], tV, pointY[0], leftY[0], rightY[0]); QuadraticBezier_evaluate(*subMatrix[0][1], *subMatrix[1][1], *subMatrix[2][1], tV, pointY[1], leftY[1], rightY[1]); QuadraticBezier_evaluate(*subMatrix[0][2], *subMatrix[1][2], *subMatrix[2][2], tV, pointY[2], leftY[2], rightY[2]); PatchControl point; PatchControl left; PatchControl right; QuadraticBezier_evaluate(pointX[0], pointX[1], pointX[2], tV, point, left, right); PatchControl up; PatchControl down; QuadraticBezier_evaluate(pointY[0], pointY[1], pointY[2], tU, point, up, down); vertex3f_to_vector3(p->vertex) = point.m_vertex; texcoord2f_to_vector2(p->texcoord) = point.m_texcoord; colour4f_to_vector4(p->colour) = point.m_color; ArbitraryMeshVertex a, b, c; a.vertex = vertex3f_for_vector3(left.m_vertex); a.texcoord = texcoord2f_for_vector2(left.m_texcoord); b.vertex = vertex3f_for_vector3(right.m_vertex); b.texcoord = texcoord2f_for_vector2(right.m_texcoord); if (i != 0) { c.vertex = vertex3f_for_vector3(up.m_vertex); c.texcoord = texcoord2f_for_vector2(up.m_texcoord); } else { c.vertex = vertex3f_for_vector3(down.m_vertex); c.texcoord = texcoord2f_for_vector2(down.m_texcoord); } Vector3 normal = vector3_normalised( vector3_cross(right.m_vertex - left.m_vertex, up.m_vertex - down.m_vertex)); Vector3 tangent, bitangent; ArbitraryMeshTriangle_calcTangents(a, b, c, tangent, bitangent); vector3_normalise(tangent); vector3_normalise(bitangent); if (((nFlagsX & AVERAGE) != 0 && i == 0) || ((nFlagsY & AVERAGE) != 0 && j == 0)) { normal3f_to_vector3(p->normal) = vector3_normalised( vector3_added(normal3f_to_vector3(p->normal), normal)); normal3f_to_vector3(p->tangent) = vector3_normalised( vector3_added(normal3f_to_vector3(p->tangent), tangent)); normal3f_to_vector3(p->bitangent) = vector3_normalised( vector3_added(normal3f_to_vector3(p->bitangent), bitangent)); } else { normal3f_to_vector3(p->normal) = normal; normal3f_to_vector3(p->tangent) = tangent; normal3f_to_vector3(p->bitangent) = bitangent; } } p += strideY; } } } void Patch::TesselateSubMatrix(const BezierCurveTree *BX, const BezierCurveTree *BY, std::size_t offStartX, std::size_t offStartY, std::size_t offEndX, std::size_t offEndY, std::size_t nFlagsX, std::size_t nFlagsY, Vector3 &left, Vector3 &mid, Vector3 &right, Vector2 &texLeft, Vector2 &texMid, Vector2 &texRight, Vector4 &colLeft, Vector4 &colMid, Vector4 &colRight, bool bTranspose) { int newFlagsX, newFlagsY; Vector3 tmp; Vector3 vertex_0_0, vertex_0_1, vertex_1_0, vertex_1_1, vertex_2_0, vertex_2_1; Vector2 texTmp; Vector2 texcoord_0_0, texcoord_0_1, texcoord_1_0, texcoord_1_1, texcoord_2_0, texcoord_2_1; Vector4 colTmp, colour_0_0, colour_0_1, colour_1_0, colour_1_1, colour_2_0, colour_2_1; { // texcoords BezierInterpolate2(texcoord_for_index(m_tess.m_vertices, offStartX + offStartY), texcoord_0_0, texcoord_for_index(m_tess.m_vertices, BX->index + offStartY), texcoord_0_1, texcoord_for_index(m_tess.m_vertices, offEndX + offStartY)); BezierInterpolate2(texcoord_for_index(m_tess.m_vertices, offStartX + offEndY), texcoord_2_0, texcoord_for_index(m_tess.m_vertices, BX->index + offEndY), texcoord_2_1, texcoord_for_index(m_tess.m_vertices, offEndX + offEndY)); texTmp = texMid; BezierInterpolate2(texLeft, texcoord_1_0, texTmp, texcoord_1_1, texRight); if (!BezierCurveTree_isLeaf(BY)) { texcoord_for_index(m_tess.m_vertices, BX->index + BY->index) = texTmp; } if (!BezierCurveTree_isLeaf(BX->left)) { texcoord_for_index(m_tess.m_vertices, BX->left->index + offStartY) = texcoord_0_0; texcoord_for_index(m_tess.m_vertices, BX->left->index + offEndY) = texcoord_2_0; if (!BezierCurveTree_isLeaf(BY)) { texcoord_for_index(m_tess.m_vertices, BX->left->index + BY->index) = texcoord_1_0; } } if (!BezierCurveTree_isLeaf(BX->right)) { texcoord_for_index(m_tess.m_vertices, BX->right->index + offStartY) = texcoord_0_1; texcoord_for_index(m_tess.m_vertices, BX->right->index + offEndY) = texcoord_2_1; if (!BezierCurveTree_isLeaf(BY)) { texcoord_for_index(m_tess.m_vertices, BX->right->index + BY->index) = texcoord_1_1; } } // colours BezierInterpolate4(colour_for_index(m_tess.m_vertices, offStartX + offStartY), colour_0_0, colour_for_index(m_tess.m_vertices, BX->index + offStartY), colour_0_1, colour_for_index(m_tess.m_vertices, offEndX + offStartY)); BezierInterpolate4(colour_for_index(m_tess.m_vertices, offStartX + offEndY), colour_2_0, colour_for_index(m_tess.m_vertices, BX->index + offEndY), colour_2_1, colour_for_index(m_tess.m_vertices, offEndX + offEndY)); colTmp = colMid; BezierInterpolate4(colLeft, colour_1_0, colTmp, colour_1_1, colRight); if (!BezierCurveTree_isLeaf(BY)) { colour_for_index(m_tess.m_vertices, BX->index + BY->index) = colTmp; } if (!BezierCurveTree_isLeaf(BX->left)) { colour_for_index(m_tess.m_vertices, BX->left->index + offStartY) = colour_0_0; colour_for_index(m_tess.m_vertices, BX->left->index + offEndY) = colour_2_0; if (!BezierCurveTree_isLeaf(BY)) { colour_for_index(m_tess.m_vertices, BX->left->index + BY->index) = colour_1_0; } } if (!BezierCurveTree_isLeaf(BX->right)) { colour_for_index(m_tess.m_vertices, BX->right->index + offStartY) = colour_0_1; colour_for_index(m_tess.m_vertices, BX->right->index + offEndY) = colour_2_1; if (!BezierCurveTree_isLeaf(BY)) { colour_for_index(m_tess.m_vertices, BX->right->index + BY->index) = colour_1_1; } } // verts BezierInterpolate3(vertex_for_index(m_tess.m_vertices, offStartX + offStartY), vertex_0_0, vertex_for_index(m_tess.m_vertices, BX->index + offStartY), vertex_0_1, vertex_for_index(m_tess.m_vertices, offEndX + offStartY)); BezierInterpolate3(vertex_for_index(m_tess.m_vertices, offStartX + offEndY), vertex_2_0, vertex_for_index(m_tess.m_vertices, BX->index + offEndY), vertex_2_1, vertex_for_index(m_tess.m_vertices, offEndX + offEndY)); tmp = mid; BezierInterpolate3(left, vertex_1_0, tmp, vertex_1_1, right); if (!BezierCurveTree_isLeaf(BY)) { vertex_for_index(m_tess.m_vertices, BX->index + BY->index) = tmp; } if (!BezierCurveTree_isLeaf(BX->left)) { vertex_for_index(m_tess.m_vertices, BX->left->index + offStartY) = vertex_0_0; vertex_for_index(m_tess.m_vertices, BX->left->index + offEndY) = vertex_2_0; if (!BezierCurveTree_isLeaf(BY)) { vertex_for_index(m_tess.m_vertices, BX->left->index + BY->index) = vertex_1_0; } } if (!BezierCurveTree_isLeaf(BX->right)) { vertex_for_index(m_tess.m_vertices, BX->right->index + offStartY) = vertex_0_1; vertex_for_index(m_tess.m_vertices, BX->right->index + offEndY) = vertex_2_1; if (!BezierCurveTree_isLeaf(BY)) { vertex_for_index(m_tess.m_vertices, BX->right->index + BY->index) = vertex_1_1; } } // normals if (nFlagsX & SPLIT) { ArbitraryMeshVertex a, b, c; Vector3 tangentU; if (!(nFlagsX & DEGEN_0a) || !(nFlagsX & DEGEN_0b)) { tangentU = vector3_subtracted(vertex_0_1, vertex_0_0); a.vertex = vertex3f_for_vector3(vertex_0_0); a.texcoord = texcoord2f_for_vector2(texcoord_0_0); c.vertex = vertex3f_for_vector3(vertex_0_1); c.texcoord = texcoord2f_for_vector2(texcoord_0_1); } else if (!(nFlagsX & DEGEN_1a) || !(nFlagsX & DEGEN_1b)) { tangentU = vector3_subtracted(vertex_1_1, vertex_1_0); a.vertex = vertex3f_for_vector3(vertex_1_0); a.texcoord = texcoord2f_for_vector2(texcoord_1_0); c.vertex = vertex3f_for_vector3(vertex_1_1); c.texcoord = texcoord2f_for_vector2(texcoord_1_1); } else { tangentU = vector3_subtracted(vertex_2_1, vertex_2_0); a.vertex = vertex3f_for_vector3(vertex_2_0); a.texcoord = texcoord2f_for_vector2(texcoord_2_0); c.vertex = vertex3f_for_vector3(vertex_2_1); c.texcoord = texcoord2f_for_vector2(texcoord_2_1); } Vector3 tangentV; if ((nFlagsY & DEGEN_0a) && (nFlagsY & DEGEN_1a) && (nFlagsY & DEGEN_2a)) { tangentV = vector3_subtracted(vertex_for_index(m_tess.m_vertices, BX->index + offEndY), tmp); b.vertex = vertex3f_for_vector3(tmp); //m_tess.m_vertices[BX->index + offEndY].vertex; b.texcoord = texcoord2f_for_vector2(texTmp); //m_tess.m_vertices[BX->index + offEndY].texcoord; } else { tangentV = vector3_subtracted(tmp, vertex_for_index(m_tess.m_vertices, BX->index + offStartY)); b.vertex = vertex3f_for_vector3(tmp); //m_tess.m_vertices[BX->index + offStartY].vertex; b.texcoord = texcoord2f_for_vector2(texTmp); //m_tess.m_vertices[BX->index + offStartY].texcoord; } Vector3 normal, s, t; ArbitraryMeshVertex &v = m_tess.m_vertices[offStartY + BX->index]; Vector3 &p = normal3f_to_vector3(v.normal); Vector3 &ps = normal3f_to_vector3(v.tangent); Vector3 &pt = normal3f_to_vector3(v.bitangent); if (bTranspose) { normal = vector3_cross(tangentV, tangentU); } else { normal = vector3_cross(tangentU, tangentV); } normalise_safe(normal); ArbitraryMeshTriangle_calcTangents(a, b, c, s, t); normalise_safe(s); normalise_safe(t); if (nFlagsX & AVERAGE) { p = vector3_normalised(vector3_added(p, normal)); ps = vector3_normalised(vector3_added(ps, s)); pt = vector3_normalised(vector3_added(pt, t)); } else { p = normal; ps = s; pt = t; } } { ArbitraryMeshVertex a, b, c; Vector3 tangentU; if (!(nFlagsX & DEGEN_2a) || !(nFlagsX & DEGEN_2b)) { tangentU = vector3_subtracted(vertex_2_1, vertex_2_0); a.vertex = vertex3f_for_vector3(vertex_2_0); a.texcoord = texcoord2f_for_vector2(texcoord_2_0); c.vertex = vertex3f_for_vector3(vertex_2_1); c.texcoord = texcoord2f_for_vector2(texcoord_2_1); } else if (!(nFlagsX & DEGEN_1a) || !(nFlagsX & DEGEN_1b)) { tangentU = vector3_subtracted(vertex_1_1, vertex_1_0); a.vertex = vertex3f_for_vector3(vertex_1_0); a.texcoord = texcoord2f_for_vector2(texcoord_1_0); c.vertex = vertex3f_for_vector3(vertex_1_1); c.texcoord = texcoord2f_for_vector2(texcoord_1_1); } else { tangentU = vector3_subtracted(vertex_0_1, vertex_0_0); a.vertex = vertex3f_for_vector3(vertex_0_0); a.texcoord = texcoord2f_for_vector2(texcoord_0_0); c.vertex = vertex3f_for_vector3(vertex_0_1); c.texcoord = texcoord2f_for_vector2(texcoord_0_1); } Vector3 tangentV; if ((nFlagsY & DEGEN_0b) && (nFlagsY & DEGEN_1b) && (nFlagsY & DEGEN_2b)) { tangentV = vector3_subtracted(tmp, vertex_for_index(m_tess.m_vertices, BX->index + offStartY)); b.vertex = vertex3f_for_vector3(tmp); //m_tess.m_vertices[BX->index + offStartY].vertex; b.texcoord = texcoord2f_for_vector2(texTmp); //m_tess.m_vertices[BX->index + offStartY].texcoord; } else { tangentV = vector3_subtracted(vertex_for_index(m_tess.m_vertices, BX->index + offEndY), tmp); b.vertex = vertex3f_for_vector3(tmp); //m_tess.m_vertices[BX->index + offEndY].vertex; b.texcoord = texcoord2f_for_vector2(texTmp); //m_tess.m_vertices[BX->index + offEndY].texcoord; } ArbitraryMeshVertex &v = m_tess.m_vertices[offEndY + BX->index]; Vector3 &p = normal3f_to_vector3(v.normal); Vector3 &ps = normal3f_to_vector3(v.tangent); Vector3 &pt = normal3f_to_vector3(v.bitangent); if (bTranspose) { p = vector3_cross(tangentV, tangentU); } else { p = vector3_cross(tangentU, tangentV); } normalise_safe(p); ArbitraryMeshTriangle_calcTangents(a, b, c, ps, pt); normalise_safe(ps); normalise_safe(pt); } } newFlagsX = newFlagsY = 0; if ((nFlagsX & DEGEN_0a) && (nFlagsX & DEGEN_0b)) { newFlagsX |= DEGEN_0a; newFlagsX |= DEGEN_0b; } if ((nFlagsX & DEGEN_1a) && (nFlagsX & DEGEN_1b)) { newFlagsX |= DEGEN_1a; newFlagsX |= DEGEN_1b; } if ((nFlagsX & DEGEN_2a) && (nFlagsX & DEGEN_2b)) { newFlagsX |= DEGEN_2a; newFlagsX |= DEGEN_2b; } if ((nFlagsY & DEGEN_0a) && (nFlagsY & DEGEN_1a) && (nFlagsY & DEGEN_2a)) { newFlagsY |= DEGEN_0a; newFlagsY |= DEGEN_1a; newFlagsY |= DEGEN_2a; } if ((nFlagsY & DEGEN_0b) && (nFlagsY & DEGEN_1b) && (nFlagsY & DEGEN_2b)) { newFlagsY |= DEGEN_0b; newFlagsY |= DEGEN_1b; newFlagsY |= DEGEN_2b; } //if((nFlagsX & DEGEN_0a) && (nFlagsX & DEGEN_1a) && (nFlagsX & DEGEN_2a)) { newFlagsX |= DEGEN_0a; newFlagsX |= DEGEN_1a; newFlagsX |= DEGEN_2a; } //if((nFlagsX & DEGEN_0b) && (nFlagsX & DEGEN_1b) && (nFlagsX & DEGEN_2b)) { newFlagsX |= DEGEN_0b; newFlagsX |= DEGEN_1b; newFlagsX |= DEGEN_2b; } newFlagsX |= (nFlagsX & SPLIT); newFlagsX |= (nFlagsX & AVERAGE); if (!BezierCurveTree_isLeaf(BY)) { { int nTemp = newFlagsY; if ((nFlagsY & DEGEN_0a) && (nFlagsY & DEGEN_0b)) { newFlagsY |= DEGEN_0a; newFlagsY |= DEGEN_0b; } newFlagsY |= (nFlagsY & SPLIT); newFlagsY |= (nFlagsY & AVERAGE); Vector3 &p = vertex_for_index(m_tess.m_vertices, BX->index + BY->index); Vector3 vTemp(p); Vector2 &p2 = texcoord_for_index(m_tess.m_vertices, BX->index + BY->index); Vector2 stTemp(p2); TesselateSubMatrix(BY, BX->left, offStartY, offStartX, offEndY, BX->index, newFlagsY, newFlagsX, vertex_0_0, vertex_1_0, vertex_2_0, texcoord_0_0, texcoord_1_0, texcoord_2_0, colour_0_0, colour_1_0, colour_2_0, !bTranspose); newFlagsY = nTemp; p = vTemp; p2 = stTemp; } if ((nFlagsY & DEGEN_2a) && (nFlagsY & DEGEN_2b)) { newFlagsY |= DEGEN_2a; newFlagsY |= DEGEN_2b; } TesselateSubMatrix(BY, BX->right, offStartY, BX->index, offEndY, offEndX, newFlagsY, newFlagsX, vertex_0_1, vertex_1_1, vertex_2_1, texcoord_0_1, texcoord_1_1, texcoord_2_1, colour_0_1, colour_1_1, colour_2_1, !bTranspose); } else { if (!BezierCurveTree_isLeaf(BX->left)) { TesselateSubMatrix(BX->left, BY, offStartX, offStartY, BX->index, offEndY, newFlagsX, newFlagsY, left, vertex_1_0, tmp, texLeft, texcoord_1_0, texTmp, colLeft, colour_1_0, colTmp, bTranspose); } if (!BezierCurveTree_isLeaf(BX->right)) { TesselateSubMatrix(BX->right, BY, BX->index, offStartY, offEndX, offEndY, newFlagsX, newFlagsY, tmp, vertex_1_1, right, texTmp, texcoord_1_1, texRight, colTmp, colour_1_1, colRight, bTranspose); } } } void Patch::BuildTesselationCurves(EMatrixMajor major) { std::size_t nArrayStride, length, cross, strideU, strideV; switch (major) { case ROW: nArrayStride = 1; length = (m_width - 1) >> 1; cross = m_height; strideU = 1; strideV = m_width; if (!m_patchDef3) { BezierCurveTreeArray_deleteAll(m_tess.m_curveTreeU); } break; case COL: nArrayStride = m_tess.m_nArrayWidth; length = (m_height - 1) >> 1; cross = m_width; strideU = m_width; strideV = 1; if (!m_patchDef3) { BezierCurveTreeArray_deleteAll(m_tess.m_curveTreeV); } break; default: ERROR_MESSAGE("neither row-major nor column-major"); return; } Array arrayLength(length); Array pCurveTree(length); std::size_t nArrayLength = 1; if (m_patchDef3) { if (!m_subdivisions_x && !m_subdivisions_y) { for (Array::iterator i = arrayLength.begin(); i != arrayLength.end(); ++i) { *i = 2; nArrayLength += *i; } } else { for (Array::iterator i = arrayLength.begin(); i != arrayLength.end(); ++i) { *i = Array::value_type((major == ROW) ? m_subdivisions_x : m_subdivisions_y); nArrayLength += *i; } } } else { // create a list of the horizontal control curves in each column of sub-patches // adaptively tesselate each horizontal control curve in the list // create a binary tree representing the combined tesselation of the list for (std::size_t i = 0; i != length; ++i) { PatchControl *p1 = m_ctrlTransformed.data() + (i * 2 * strideU); GSList *pCurveList = 0; for (std::size_t j = 0; j < cross; j += 2) { PatchControl *p2 = p1 + strideV; PatchControl *p3 = p2 + strideV; // directly taken from one row of control points { BezierCurve *pCurve = new BezierCurve; pCurve->crd = (p1 + strideU)->m_vertex; pCurve->left = p1->m_vertex; pCurve->right = (p1 + (strideU << 1))->m_vertex; pCurveList = g_slist_prepend(pCurveList, pCurve); } if (j + 2 >= cross) { break; } // interpolated from three columns of control points { BezierCurve *pCurve = new BezierCurve; pCurve->crd = vector3_mid((p1 + strideU)->m_vertex, (p3 + strideU)->m_vertex); pCurve->left = vector3_mid(p1->m_vertex, p3->m_vertex); pCurve->right = vector3_mid((p1 + (strideU << 1))->m_vertex, (p3 + (strideU << 1))->m_vertex); pCurve->crd = vector3_mid(pCurve->crd, (p2 + strideU)->m_vertex); pCurve->left = vector3_mid(pCurve->left, p2->m_vertex); pCurve->right = vector3_mid(pCurve->right, (p2 + (strideU << 1))->m_vertex); pCurveList = g_slist_prepend(pCurveList, pCurve); } p1 = p3; } pCurveTree[i] = new BezierCurveTree; BezierCurveTree_FromCurveList(pCurveTree[i], pCurveList); for (GSList *l = pCurveList; l != 0; l = g_slist_next(l)) { delete static_cast((*l).data ); } g_slist_free(pCurveList); // set up array indices for binary tree // accumulate subarray width arrayLength[i] = Array::value_type( BezierCurveTree_Setup(pCurveTree[i], nArrayLength, nArrayStride) - (nArrayLength - 1)); // accumulate total array width nArrayLength += arrayLength[i]; } } switch (major) { case ROW: m_tess.m_nArrayWidth = nArrayLength; std::swap(m_tess.m_arrayWidth, arrayLength); if (!m_patchDef3) { std::swap(m_tess.m_curveTreeU, pCurveTree); } break; case COL: m_tess.m_nArrayHeight = nArrayLength; std::swap(m_tess.m_arrayHeight, arrayLength); if (!m_patchDef3) { std::swap(m_tess.m_curveTreeV, pCurveTree); } break; } } inline void vertex_assign_ctrl(ArbitraryMeshVertex &vertex, const PatchControl &ctrl) { vertex.vertex = vertex3f_for_vector3(ctrl.m_vertex); vertex.texcoord = texcoord2f_for_vector2(ctrl.m_texcoord); } inline void vertex_clear_normal(ArbitraryMeshVertex &vertex) { vertex.normal = Normal3f(0, 0, 0); vertex.tangent = Normal3f(0, 0, 0); vertex.bitangent = Normal3f(0, 0, 0); } inline void tangents_remove_degenerate(Vector3 tangents[6], Vector2 textureTangents[6], unsigned int flags) { if (flags & DEGEN_0a) { const std::size_t i = (flags & DEGEN_0b) ? (flags & DEGEN_1a) ? (flags & DEGEN_1b) ? (flags & DEGEN_2a) ? 5 : 4 : 3 : 2 : 1; tangents[0] = tangents[i]; textureTangents[0] = textureTangents[i]; } if (flags & DEGEN_0b) { const std::size_t i = (flags & DEGEN_0a) ? (flags & DEGEN_1b) ? (flags & DEGEN_1a) ? (flags & DEGEN_2b) ? 4 : 5 : 2 : 3 : 0; tangents[1] = tangents[i]; textureTangents[1] = textureTangents[i]; } if (flags & DEGEN_2a) { const std::size_t i = (flags & DEGEN_2b) ? (flags & DEGEN_1a) ? (flags & DEGEN_1b) ? (flags & DEGEN_0a) ? 1 : 0 : 3 : 2 : 5; tangents[4] = tangents[i]; textureTangents[4] = textureTangents[i]; } if (flags & DEGEN_2b) { const std::size_t i = (flags & DEGEN_2a) ? (flags & DEGEN_1b) ? (flags & DEGEN_1a) ? (flags & DEGEN_0b) ? 0 : 1 : 2 : 3 : 4; tangents[5] = tangents[i]; textureTangents[5] = textureTangents[i]; } } void bestTangents00(unsigned int degenerateFlags, double dot, double length, std::size_t &index0, std::size_t &index1) { if (fabs(dot + length) < 0.001) { // opposing direction = degenerate if (!(degenerateFlags & DEGEN_1a)) { // if this tangent is degenerate we cannot use it index0 = 2; index1 = 0; } else if (!(degenerateFlags & DEGEN_0b)) { index0 = 0; index1 = 1; } else { index0 = 1; index1 = 0; } } else if (fabs(dot - length) < 0.001) { // same direction = degenerate if (degenerateFlags & DEGEN_0b) { index0 = 0; index1 = 1; } else { index0 = 1; index1 = 0; } } } void bestTangents01(unsigned int degenerateFlags, double dot, double length, std::size_t &index0, std::size_t &index1) { if (fabs(dot - length) < 0.001) { // same direction = degenerate if (!(degenerateFlags & DEGEN_1a)) { // if this tangent is degenerate we cannot use it index0 = 2; index1 = 1; } else if (!(degenerateFlags & DEGEN_2b)) { index0 = 4; index1 = 0; } else { index0 = 5; index1 = 1; } } else if (fabs(dot + length) < 0.001) { // opposing direction = degenerate if (degenerateFlags & DEGEN_2b) { index0 = 4; index1 = 0; } else { index0 = 5; index1 = 1; } } } void bestTangents10(unsigned int degenerateFlags, double dot, double length, std::size_t &index0, std::size_t &index1) { if (fabs(dot - length) < 0.001) { // same direction = degenerate if (!(degenerateFlags & DEGEN_1b)) { // if this tangent is degenerate we cannot use it index0 = 3; index1 = 4; } else if (!(degenerateFlags & DEGEN_0a)) { index0 = 1; index1 = 5; } else { index0 = 0; index1 = 4; } } else if (fabs(dot + length) < 0.001) { // opposing direction = degenerate if (degenerateFlags & DEGEN_0a) { index0 = 1; index1 = 5; } else { index0 = 0; index1 = 4; } } } void bestTangents11(unsigned int degenerateFlags, double dot, double length, std::size_t &index0, std::size_t &index1) { if (fabs(dot + length) < 0.001) { // opposing direction = degenerate if (!(degenerateFlags & DEGEN_1b)) { // if this tangent is degenerate we cannot use it index0 = 3; index1 = 5; } else if (!(degenerateFlags & DEGEN_2a)) { index0 = 5; index1 = 4; } else { index0 = 4; index1 = 5; } } else if (fabs(dot - length) < 0.001) { // same direction = degenerate if (degenerateFlags & DEGEN_2a) { index0 = 5; index1 = 4; } else { index0 = 4; index1 = 5; } } } void Patch::accumulateVertexTangentSpace(std::size_t index, Vector3 tangentX[6], Vector3 tangentY[6], Vector2 tangentS[6], Vector2 tangentT[6], std::size_t index0, std::size_t index1) { { Vector3 normal(vector3_cross(tangentX[index0], tangentY[index1])); if (!vector3_equal(normal, g_vector3_identity)) { vector3_add(normal_for_index(m_tess.m_vertices, index), vector3_normalised(normal)); } } { ArbitraryMeshVertex a, b, c; a.vertex = Vertex3f(0, 0, 0); a.texcoord = TexCoord2f(0, 0); b.vertex = vertex3f_for_vector3(tangentX[index0]); b.texcoord = texcoord2f_for_vector2(tangentS[index0]); c.vertex = vertex3f_for_vector3(tangentY[index1]); c.texcoord = texcoord2f_for_vector2(tangentT[index1]); Vector3 s, t; ArbitraryMeshTriangle_calcTangents(a, b, c, s, t); if (!vector3_equal(s, g_vector3_identity)) { vector3_add(tangent_for_index(m_tess.m_vertices, index), vector3_normalised(s)); } if (!vector3_equal(t, g_vector3_identity)) { vector3_add(bitangent_for_index(m_tess.m_vertices, index), vector3_normalised(t)); } } } const std::size_t PATCH_MAX_VERTEX_ARRAY = 1048576; void Patch::BuildVertexArray() { const std::size_t strideU = 1; const std::size_t strideV = m_width; const std::size_t numElems = m_tess.m_nArrayWidth * m_tess.m_nArrayHeight; // total number of elements in vertex array const bool bWidthStrips = (m_tess.m_nArrayWidth >= m_tess.m_nArrayHeight); // decide if horizontal strips are longer than vertical // allocate vertex, normal, texcoord and primitive-index arrays m_tess.m_vertices.resize(numElems); m_tess.m_indices.resize(m_tess.m_nArrayWidth * 2 * (m_tess.m_nArrayHeight - 1)); // set up strip indices if (bWidthStrips) { m_tess.m_numStrips = m_tess.m_nArrayHeight - 1; m_tess.m_lenStrips = m_tess.m_nArrayWidth * 2; for (std::size_t i = 0; i < m_tess.m_nArrayWidth; i++) { for (std::size_t j = 0; j < m_tess.m_numStrips; j++) { m_tess.m_indices[(j * m_tess.m_lenStrips) + i * 2] = RenderIndex(j * m_tess.m_nArrayWidth + i); m_tess.m_indices[(j * m_tess.m_lenStrips) + i * 2 + 1] = RenderIndex( (j + 1) * m_tess.m_nArrayWidth + i); // reverse because radiant uses CULL_FRONT //m_tess.m_indices[(j*m_tess.m_lenStrips)+i*2+1] = RenderIndex(j*m_tess.m_nArrayWidth+i); //m_tess.m_indices[(j*m_tess.m_lenStrips)+i*2] = RenderIndex((j+1)*m_tess.m_nArrayWidth+i); } } } else { m_tess.m_numStrips = m_tess.m_nArrayWidth - 1; m_tess.m_lenStrips = m_tess.m_nArrayHeight * 2; for (std::size_t i = 0; i < m_tess.m_nArrayHeight; i++) { for (std::size_t j = 0; j < m_tess.m_numStrips; j++) { m_tess.m_indices[(j * m_tess.m_lenStrips) + i * 2] = RenderIndex( ((m_tess.m_nArrayHeight - 1) - i) * m_tess.m_nArrayWidth + j); m_tess.m_indices[(j * m_tess.m_lenStrips) + i * 2 + 1] = RenderIndex( ((m_tess.m_nArrayHeight - 1) - i) * m_tess.m_nArrayWidth + j + 1); // reverse because radiant uses CULL_FRONT //m_tess.m_indices[(j*m_tess.m_lenStrips)+i*2+1] = RenderIndex(((m_tess.m_nArrayHeight-1)-i)*m_tess.m_nArrayWidth+j); //m_tess.m_indices[(j*m_tess.m_lenStrips)+i*2] = RenderIndex(((m_tess.m_nArrayHeight-1)-i)*m_tess.m_nArrayWidth+j+1); } } } if (m_patchDef3 && !m_subdivisions_x && !m_subdivisions_y) { for (std::size_t i = 0; i < m_width*m_height; i++) { ArbitraryMeshVertex &p = m_tess.m_vertices[i]; PatchControl &cp = m_ctrlTransformed[i]; p.vertex = vertex3f_for_vector3(cp.m_vertex); p.texcoord = texcoord2f_for_vector2(cp.m_texcoord); p.colour = colour4f_for_vector4(cp.m_color); } } else { 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, subMatrix[1][0]->m_color, subMatrix[1][1]->m_color, subMatrix[1][2]->m_color, 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, subMatrix[0][1]->m_color, subMatrix[1][1]->m_color, subMatrix[2][1]->m_color, true); } } offStartX = offEndX; } offStartY = offEndY; } } } class PatchFilterWrapper : public Filter { bool m_active; bool m_invert; PatchFilter &m_filter; public: PatchFilterWrapper(PatchFilter &filter, bool invert) : m_invert(invert), m_filter(filter) { } void setActive(bool active) { m_active = active; } bool active() { return m_active; } bool filter(const Patch &patch) { return m_invert ^ m_filter.filter(patch); } }; typedef std::list PatchFilters; PatchFilters g_patchFilters; void add_patch_filter(PatchFilter &filter, int mask, bool invert) { g_patchFilters.push_back(PatchFilterWrapper(filter, invert)); GlobalFilterSystem().addFilter(g_patchFilters.back(), mask); } bool patch_filtered(Patch &patch) { for (PatchFilters::iterator i = g_patchFilters.begin(); i != g_patchFilters.end(); ++i) { if ((*i).active() && (*i).filter(patch)) { return true; } } return false; }