/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. 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 */ //----------------------------------------------------------------------------- // // DESCRIPTION: // a class to handle control points in a 2D view // TODO: this one can be placed under an interface, and provided to the editor as service // #include "StdAfx.h" void CControlPointsManagerBFace::Init(int iPts, CtrlPts_t *Pts, C2DView *p2DView, int TexSize[2], _QERFaceData *pFaceData, OpenGLBinding *pQglTable) { ManagerState = Idle; m_NumPoints = iPts; m_pPts = Pts; // store the initial config memcpy(&m_RefPts, Pts, sizeof(CtrlPts_t)); // init TM memset(m_TM, 0, sizeof(float[2][3])); m_TM[0][0] = 1.0f; m_TM[1][1] = 1.0f; m_bGotAnchor = false; m_TransOffset[0] = 0.0f; m_TransOffset[1] = 0.0f; m_TexSize[0] = TexSize[0]; m_TexSize[1] = TexSize[1]; m_pFaceData = pFaceData; CControlPointsManager::Init(p2DView, pQglTable); } bool CControlPointsManagerBFace::OnLButtonDown(int xPos, int yPos) { if (ManagerState == Idle) { int i; // scan the point list to see if we selected something for (i = 0; i < m_NumPoints; i++) { if (m_p2DView->DoesSelect(xPos, yPos, m_pPts->data[i])) { m_iDragPoint = i; ManagerState = Drag; if (m_bGotAnchor && i == m_iAnchorPoint) { // this means we selected the Anchor, so we'll translate m_bGotAnchor = false; } // perhaps we won't use translation, but we can compute it anyway ComputeTransOffset(i); if (m_bGotAnchor) { // we have an Anchor and selected another point m_Anchor[0] = m_pPts->data[m_iAnchorPoint][0]; m_Anchor[1] = m_pPts->data[m_iAnchorPoint][1]; } } } // send a repaint message g_pToolWnd->Redraw(); return true; } return false; } bool CControlPointsManagerBFace::OnMouseMove(int xPos, int yPos) { if (ManagerState == Drag) { if (m_bGotAnchor) { // there's an anchor, we are rotating the shape // we need to work in XY space for orthonormality float Pt[2]; vec3_t V1, V2; vec3_t cross; float c, s; // used in XY space float XYTM[2][3]; float XYRefAnchor[2]; float XYAnchor[2]; m_p2DView->GridForWindow(Pt, xPos, yPos); V2[0] = Pt[0] - m_Anchor[0]; V2[1] = Pt[1] - m_Anchor[1]; V2[2] = 0.0f; V1[0] = m_RefPts.data[m_iDragPoint][0] - m_RefPts.data[m_iAnchorPoint][0]; V1[1] = m_RefPts.data[m_iDragPoint][1] - m_RefPts.data[m_iAnchorPoint][1]; V1[2] = 0.0f; // compute transformation from V1 to V2 // we need to work in XY orthonormal space XYSpaceForSTSpace(V1, V1); XYSpaceForSTSpace(V2, V2); VectorNormalize(V2, V2); VectorNormalize(V1, V1); c = DotProduct(V1, V2); CrossProduct(V1, V2, cross); s = VectorLength(cross); // we compute the transformation matrix in XY space // reference position of the Anchor in XY space XYSpaceForSTSpace(XYRefAnchor, m_RefPts.data[m_iAnchorPoint]); // current position of the Anchor in XY space XYSpaceForSTSpace(XYAnchor, m_Anchor); // compute transformation matrix XYTM[0][0] = c; XYTM[1][1] = c; if (cross[2] > 0) { s *= -1.0f; } XYTM[0][1] = s; XYTM[1][0] = -s; XYTM[0][2] = -c * XYRefAnchor[0] - s * XYRefAnchor[1] + XYAnchor[0]; XYTM[1][2] = s * XYRefAnchor[0] - c * XYRefAnchor[1] + XYAnchor[1]; // express this transformation matrix in ST space m_TM[0][0] = XYTM[0][0]; m_TM[1][0] = XYTM[1][0] * (float) m_TexSize[0] / (float) m_TexSize[1]; m_TM[0][1] = XYTM[0][1] * (float) m_TexSize[1] / (float) m_TexSize[0]; m_TM[1][1] = XYTM[1][1]; m_TM[0][2] = XYTM[0][2] / (float) m_TexSize[0]; m_TM[1][2] = XYTM[1][2] / (float) m_TexSize[1]; // update all points UpdateCtrlPts(); } else { // no Anchor point is defined, we translate all points m_p2DView->GridForWindow(m_pPts->data[m_iDragPoint], xPos, yPos); m_TM[0][2] = m_pPts->data[m_iDragPoint][0] + m_TransOffset[0]; m_TM[1][2] = m_pPts->data[m_iDragPoint][1] + m_TransOffset[1]; // update all points UpdateCtrlPts(); } // send a repaint message g_pToolWnd->Redraw(); return true; } return false; } bool CControlPointsManagerBFace::OnLButtonUp(int x, int y) { if (ManagerState == Drag) { // this button is gonna become our Anchor m_bGotAnchor = true; m_iAnchorPoint = m_iDragPoint; // let's get out of Drag mode ManagerState = Idle; // send a repaint message g_pToolWnd->Redraw(); return true; } return false; } void CControlPointsManagerBFace::render() { int i; m_pQglTable->m_pfn_qglColor3f(0, 1, 0); m_pQglTable->m_pfn_qglPointSize(6); m_pQglTable->m_pfn_qglBegin(GL_POINTS); for (i = 0; i < m_NumPoints; i++) { if (ManagerState == Drag && i == m_iDragPoint) { m_pQglTable->m_pfn_qglColor3f(1, 0, 0); } else if (m_bGotAnchor && i == m_iAnchorPoint) { m_pQglTable->m_pfn_qglColor3f(0, 0, 1); } m_pQglTable->m_pfn_qglVertex2f(m_pPts->data[i][0], m_pPts->data[i][1]); m_pQglTable->m_pfn_qglColor3f(0, 1, 0); } m_pQglTable->m_pfn_qglEnd(); } void CControlPointsManagerBFace::UpdateCtrlPts() { int i; // update all points for (i = 0; i < m_NumPoints; i++) { m_pPts->data[i][0] = m_RefPts.data[i][0] * m_TM[0][0] + m_RefPts.data[i][1] * m_TM[0][1] + m_TM[0][2]; m_pPts->data[i][1] = m_RefPts.data[i][0] * m_TM[1][0] + m_RefPts.data[i][1] * m_TM[1][1] + m_TM[1][2]; } if (g_bPrefsUpdateCameraView) { Commit(); // tell Radiant to update // NOTE: little speed optimisation, disable window updates, and only update camera view g_FuncTable.m_pfnSetScreenUpdate(false); g_SelectedFaceTable.m_pfnSetFaceInfo(0, m_pFaceData); g_FuncTable.m_pfnSetScreenUpdate(true); g_FuncTable.m_pfnSysUpdateWindows(W_CAMERA); } } //++timo FIXME: we are using a global for the reference data, use a m_pCancelFaceData instead void CControlPointsManagerBFace::Commit() { brushprimit_texdef_t aux; aux.coords[0][0] = m_TM[0][0] * g_CancelFaceData.brushprimit_texdef.coords[0][0] + m_TM[0][1] * g_CancelFaceData.brushprimit_texdef.coords[1][0]; aux.coords[0][1] = m_TM[0][0] * g_CancelFaceData.brushprimit_texdef.coords[0][1] + m_TM[0][1] * g_CancelFaceData.brushprimit_texdef.coords[1][1]; aux.coords[0][2] = m_TM[0][0] * g_CancelFaceData.brushprimit_texdef.coords[0][2] + m_TM[0][1] * g_CancelFaceData.brushprimit_texdef.coords[1][2] + m_TM[0][2]; aux.coords[1][0] = m_TM[1][0] * g_CancelFaceData.brushprimit_texdef.coords[0][0] + m_TM[1][1] * g_CancelFaceData.brushprimit_texdef.coords[1][0]; aux.coords[1][1] = m_TM[1][0] * g_CancelFaceData.brushprimit_texdef.coords[0][1] + m_TM[1][1] * g_CancelFaceData.brushprimit_texdef.coords[1][1]; aux.coords[1][2] = m_TM[1][0] * g_CancelFaceData.brushprimit_texdef.coords[0][2] + m_TM[1][1] * g_CancelFaceData.brushprimit_texdef.coords[1][2] + m_TM[1][2]; memcpy(&m_pFaceData->brushprimit_texdef, &aux, sizeof(brushprimit_texdef_t)); } void CControlPointsManagerBFace::ComputeTransOffset(int i) { // compute the translation offset used to counteract rotation m_TransOffset[0] = -m_TM[0][0] * m_RefPts.data[i][0] - m_TM[0][1] * m_RefPts.data[i][1]; m_TransOffset[1] = -m_TM[1][0] * m_RefPts.data[i][0] - m_TM[1][1] * m_RefPts.data[i][1]; } void CControlPointsManagerBFace::XYSpaceForSTSpace(float xy[2], const float st[2]) { xy[0] = st[0] * (float) m_TexSize[0]; xy[1] = st[1] * (float) m_TexSize[1]; } /* ====================================================================== patch manager ====================================================================== */ void CControlPointsManagerPatch::Init(patchMesh_t *pWorkPatch, C2DView *p2DView, OpenGLBinding *pQglTable, patchMesh_t *pPatch) { CControlPointsManager::Init(p2DView, pQglTable); m_pPatch = pPatch; m_pWorkPatch = pWorkPatch; } bool CControlPointsManagerPatch::OnLButtonDown(int xPos, int yPos) { if (ManagerState == Idle) { int i, j; // scan the point list to see if we selected something for (i = 0; i < m_pPatch->width; i++) { for (j = 0; j < m_pPatch->height; j++) { if (m_p2DView->DoesSelect(xPos, yPos, m_pWorkPatch->ctrl[i][j].st)) { m_iDragPoint[0] = i; m_iDragPoint[1] = j; ManagerState = Drag; } } } // send a repaint message g_pToolWnd->Redraw(); return true; } return false; } bool CControlPointsManagerPatch::OnMouseMove(int xPos, int yPos) { if (ManagerState == Drag) { m_p2DView->GridForWindow(m_pWorkPatch->ctrl[m_iDragPoint[0]][m_iDragPoint[1]].st, xPos, yPos); if (g_bPrefsUpdateCameraView) { Commit(); // ask to rebuild the patch display data m_pPatch->bDirty = true; // send a repaint to the camera window as well g_FuncTable.m_pfnSysUpdateWindows(W_CAMERA); } // send a repaint message g_pToolWnd->Redraw(); return true; } return false; } bool CControlPointsManagerPatch::OnLButtonUp(int x, int y) { if (ManagerState == Drag) { ManagerState = Idle; // send a repaint message g_pToolWnd->Redraw(); } return false; } void CControlPointsManagerPatch::render() { int i, j; m_pQglTable->m_pfn_qglColor3f(0, 1, 0); m_pQglTable->m_pfn_qglPointSize(6); m_pQglTable->m_pfn_qglBegin(GL_POINTS); for (i = 0; i < m_pPatch->width; i++) { for (j = 0; j < m_pPatch->height; j++) { if (ManagerState == Drag && i == m_iDragPoint[0] && j == m_iDragPoint[1]) { m_pQglTable->m_pfn_qglColor3f(1, 0, 0); } m_pQglTable->m_pfn_qglVertex2f(m_pWorkPatch->ctrl[i][j].st[0], m_pWorkPatch->ctrl[i][j].st[1]); m_pQglTable->m_pfn_qglColor3f(0, 1, 0); } } m_pQglTable->m_pfn_qglEnd(); } void CControlPointsManagerPatch::Commit() { int i, j; for (i = 0; i < m_pPatch->width; i++) { for (j = 0; j < m_pPatch->height; j++) { m_pPatch->ctrl[i][j].st[0] = m_pWorkPatch->ctrl[i][j].st[0]; m_pPatch->ctrl[i][j].st[1] = m_pWorkPatch->ctrl[i][j].st[1]; } } }