/* =========================================================================== Doom 3 GPL Source Code Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). Doom 3 Source Code 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 3 of the License, or (at your option) any later version. Doom 3 Source Code 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 Doom 3 Source Code. If not, see . In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #include "../../idlib/precompiled.h" #pragma hdrstop #include "VectorCtl.h" #include BEGIN_MESSAGE_MAP(CVectorCtl, CButton) //{{AFX_MSG_MAP(idGLWidget) ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_MOUSEMOVE() //}}AFX_MSG_MAP END_MESSAGE_MAP() CVectorCtl::CVectorCtl () : m_bBmpCreated (FALSE), m_bImageChange (TRUE), m_bBackgroundBitmapUsed (FALSE), m_clrDiffuse (DEFAULT_DIFFUSE), m_clrAmbient (DEFAULT_AMBIENT), m_clrLight (DEFAULT_LIGHT), m_clrBackgroundStart (DEFAULT_START_BACKGROUND_COLOR), m_clrBackgroundEnd (DEFAULT_END_BACKGROUND_COLOR), m_dSpecularExponent (DEFAULT_SPEC_EXP), m_bHasFocus (FALSE), m_bSelected (FALSE), m_bFrontVector (FALSE), m_dSensitivity (20.0), m_procVectorChanging (NULL), m_procVectorChanged (NULL) { double DefaultVec[3] = DEFAULT_VEC; for (int i=0; i<3; i++) { m_dVec[i] = DefaultVec[i]; pCtl[i] = NULL; } rotationQuat.Set( 0.0f, 0.0f, 0.0f, 1.0f ); lastPress.Zero(); radius = 0.6f; } CVectorCtl::~CVectorCtl () { if (m_bBmpCreated) m_dcMem.SelectObject (m_pOldBitmap); ClearBackgroundBitmap (); } // Owner-drawn control service function: void CVectorCtl::DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct ) { CDC *pDC = CDC::FromHandle (lpDrawItemStruct->hDC); // Get CDC to draw if (!m_bSelected && lpDrawItemStruct->itemState & ODS_SELECTED) { // Just got re-selected (user starts a new mouse dragging session) } else if (m_bSelected && // Last state was selected !(lpDrawItemStruct->itemState & ODS_SELECTED) && // New state is NOT selected (lpDrawItemStruct->itemState & ODS_FOCUS) && // New state is still in focus m_procVectorChanged) // User asked for a callback // User has left the track-ball and asked for a callback. m_procVectorChanged ( rotationQuat ); m_bHasFocus = lpDrawItemStruct->itemState & ODS_FOCUS; // Update focus status m_bSelected = lpDrawItemStruct->itemState & ODS_SELECTED; // Update selection status if (!m_bBmpCreated) // 1st time InitBitmap (lpDrawItemStruct, pDC); if (m_bImageChange) { // Image has changes - recalc it! if (m_procVectorChanging) // User has specified a callback m_procVectorChanging ( rotationQuat ); // Call it! BuildImage (lpDrawItemStruct); m_bImageChange = FALSE; } pDC->BitBlt (0,0,m_iWidth, m_iHeight, &m_dcMem, 0, 0, SRCCOPY); // Update screen } // Mouse was dragged void CVectorCtl::OnMouseDrag (int ixMove, int iyMove) { RotateByXandY (double(-iyMove) / m_dSensitivity, double(ixMove) / m_dSensitivity); } // Recalc ball image void CVectorCtl::BuildImage (LPDRAWITEMSTRUCT lpDrawItemStruct) { int xf, yf; for (int x=0; x EPS) { Norm = sqrt (Norm); m_dVec[0] /= Norm; m_dVec[1] /= Norm; m_dVec[2] /= Norm; return TRUE; } else { // Reset to defualt vector double DefaultVec[3] = DEFAULT_VEC; for (int i=0; i<3; i++) m_dVec[i] = DefaultVec[i]; return FALSE; } } // Calculate lightning effect for specific pixel on ball's surface COLORREF CVectorCtl::CalcLight (double dx, double dy, double dz) { double NL = dx * m_dVec[0] + dy * m_dVec[1] + dz * m_dVec[2], RV = 2.0 * NL, rx = m_dVec[0] - (dx * RV), ry = m_dVec[1] - (dy * RV), rz = m_dVec[2] - (dz * RV); if (NL < 0.0) // Diffuse coefficient NL = 0.0; RV = max (0.0, -rz); RV = double(pow (RV, m_dSpecularExponent)); int r = int ( double(GetRValue(m_clrDiffuse)) * NL + // Diffuse double(GetRValue(m_clrLight)) * RV + // Specular double(GetRValue(m_clrAmbient))), // Ambient g = int ( double(GetGValue(m_clrDiffuse)) * NL + // Diffuse double(GetGValue(m_clrLight)) * RV + // Specular double(GetGValue(m_clrAmbient))), // Ambient b = int ( double(GetBValue(m_clrDiffuse)) * NL + // Diffuse double(GetBValue(m_clrLight)) * RV + // Specular double(GetBValue(m_clrAmbient))); // Ambient r = min (255, r); // Cutoff highlight g = min (255, g); b = min (255, b); return RGB(BYTE(r),BYTE(g),BYTE(b)); } // Start memory buffer bitmap and measure it void CVectorCtl::InitBitmap (LPDRAWITEMSTRUCT lpDrawItemStruct, CDC *pDC) { m_iWidth = lpDrawItemStruct->rcItem.right - lpDrawItemStruct->rcItem.left; m_iHeight = lpDrawItemStruct->rcItem.bottom - lpDrawItemStruct->rcItem.top; m_bmpBuffer.CreateCompatibleBitmap (pDC, m_iWidth, m_iHeight); m_bBmpCreated = TRUE; m_dcMem.CreateCompatibleDC (pDC); m_pOldBitmap = m_dcMem.SelectObject (&m_bmpBuffer); SetRadius (max (min (m_iWidth, m_iHeight) - 2, 0) / 2); SetCenter (m_iWidth / 2, m_iHeight / 2); CreateBackground (); } // Set new specular intensity BOOL CVectorCtl::SetSpecularExponent (double dExp) { if (dExp < 1.0 || dExp > 200.0) return FALSE; m_dSpecularExponent = dExp; Redraw (); return TRUE; } // Rotate our vector around the X and Y axis void CVectorCtl::RotateByXandY (double XRot, double YRot) { // Angles are in radians if (XRot == 0.0 && YRot == 0.0) { return; } double cx = cos(XRot), sx = sin(XRot), cy = cos(YRot), sy = sin(YRot), dx = m_dVec[0] * cy + m_dVec[1] * sx * sy + m_dVec[2] * cx * sy, dy = m_dVec[1] * cx - m_dVec[2] * sx, dz = -m_dVec[0] * sy + m_dVec[1] * sx * cy + m_dVec[2] * cx * cy; if (!m_bFrontVector || dz >= 0.0) { // Vector is bounds free m_dVec[0] = dx; m_dVec[1] = dy; m_dVec[2] = dz; } else { // Otherwise, do not allow Z to be negative (light shines from behind) m_dVec[2] = 0.0; m_dVec[0] = dx; m_dVec[1] = dy; Normalize (); } Redraw (); } void CVectorCtl::UpdateAxisControls () { CString cs; for (int i=0; i<3; i++) if (pCtl[i]) { cs.Format ("%+1.5f",m_dVec[i]); pCtl[i]->SetWindowText (cs); } } void CVectorCtl::SetAxisControl (int nXCtl, int nYCtl, int nZCtl) { pCtl[0] = GetParent()->GetDlgItem(nXCtl); pCtl[1] = GetParent()->GetDlgItem(nYCtl); pCtl[2] = GetParent()->GetDlgItem(nZCtl); } void CVectorCtl::SetRadius (UINT uRadius) { m_iRadius = uRadius; m_iSqrRadius = m_iRadius * m_iRadius; CreateBackground (); Redraw (TRUE); } void CVectorCtl::SetCenter (UINT uHorizPos, UINT uVertPos) { m_iXCenter = uHorizPos; m_iYCenter = uVertPos; CreateBackground (); Redraw (TRUE); } void CVectorCtl::SetAxis (double d, int nAxis) { if (fabs(d)>=1.0) { m_dVec[nAxis]=d > 1.0 ? 1.0 : -1.0; m_dVec[(nAxis+1) %3]=m_dVec[(nAxis+2) %3]=0.0; Redraw (); return; } m_dVec[nAxis] = d; Normalize (); Redraw (); } void CVectorCtl::SetVector (double dx, double dy, double dz) { m_dVec[0] = dx; m_dVec[1] = dy; m_dVec[2] = dz; Normalize (); Redraw (); } void CVectorCtl::SetBackgroundColor (COLORREF clrStart, COLORREF clrEnd) { ClearBackgroundBitmap (); m_clrBackgroundStart = clrStart; m_clrBackgroundEnd = clrEnd; CreateBackground (); } BOOL CVectorCtl::SetBackgroundImage (UINT uBackgroundBitmapID) { if (m_bBackgroundBitmapUsed) { ClearBackgroundBitmap (); CreateBackground (); } if (!m_bmpBack.LoadBitmap (uBackgroundBitmapID)) return FALSE; m_bBackgroundBitmapUsed = TRUE; CreateBackground (); return TRUE; } void CVectorCtl::CreateBackground () { if (!m_bBmpCreated) return; // No image yet if (!m_bBackgroundBitmapUsed) { // No background used - fill with gradient color double r = GetRValue (m_clrBackgroundStart), g = GetGValue (m_clrBackgroundStart), b = GetBValue (m_clrBackgroundStart), rd = double (GetRValue (m_clrBackgroundEnd) - r) / double (m_iHeight), gd = double (GetGValue (m_clrBackgroundEnd) - g) / double (m_iHeight), bd = double (GetBValue (m_clrBackgroundEnd) - b) / double (m_iHeight); for (int j=0; j