quadrilateralcowboy/tools/comafx/VectorCtl.cpp
2020-06-12 14:06:25 -07:00

424 lines
13 KiB
C++

/*
===========================================================================
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 <http://www.gnu.org/licenses/>.
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 <math.h>
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<m_iWidth; x++) // Scan all columns
for (int y=0; y<m_iHeight; y++) { // Scan all rows
xf = x-m_iXCenter; // Find distance from center
yf = y-m_iYCenter;
if (xf*xf + yf*yf <= m_iSqrRadius) { // Point on ball surface
double vx = double(xf) / double(m_iRadius),
vy = double(yf) / double(m_iRadius),
vz = sqrt (1.0 - vx*vx - vy*vy); // Find ball's normal
m_dcMem.SetPixelV (x,y, CalcLight (vx,vy,vz));
}
}
}
// Normalize a vector to unit size
BOOL CVectorCtl::Normalize ()
{
double Norm = m_dVec[0] * m_dVec[0] + m_dVec[1] * m_dVec[1] + m_dVec[2] * m_dVec[2];
if (Norm > 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<m_iHeight; j++) {
for (int i=0; i<m_iWidth; i++)
m_dcMem.SetPixelV (i,j, RGB (BYTE(r),BYTE(g),BYTE(b)));
r+=rd; g+=gd; b+=bd;
}
Redraw (TRUE);
return;
}
// Bitmap used : tile it in back
CDC DCtmp;
BITMAP tmpBitmap;
m_bmpBack.GetBitmap (&tmpBitmap);
int iTmpWidth = tmpBitmap.bmWidth,
iTmpHeight = tmpBitmap.bmHeight;
DCtmp.CreateCompatibleDC (&m_dcMem);
m_pOldBitmap = DCtmp.SelectObject (&m_bmpBack);
for (int i=0; i<m_iWidth; i++)
for (int j=0; j<m_iHeight; j++)
m_dcMem.SetPixelV (i,j, DCtmp.GetPixel (i % iTmpWidth, j % iTmpHeight));
DCtmp.SelectObject (m_pOldBitmap);
Redraw (TRUE);
}
void CVectorCtl::ClearBackgroundBitmap ()
{
if (!m_bBackgroundBitmapUsed)
return;
m_bmpBack.DeleteObject ();
m_bBackgroundBitmapUsed = FALSE;
}
BOOL CVectorCtl::SetSensitivity (UINT uSens)
{
if (uSens == 0)
return FALSE;
m_dSensitivity = double(uSens);
return TRUE;
}
void CVectorCtl::Redraw (BOOL bErase) {
m_bImageChange = TRUE;
UpdateAxisControls();
Invalidate (bErase);
}
void CVectorCtl::OnMouseMove(UINT nFlags, CPoint point) {
if ( CWnd::GetCapture() != this ) {
return;
}
float curX = ( float )( 2 * point.x - 64 ) / 64;
float curY = ( float )( 2 * point.y - 64 ) / 64;
idVec3 to( -curX, -curY, 0.0f );
to.ProjectSelfOntoSphere( radius );
lastPress.ProjectSelfOntoSphere( radius );
idVec3 axis;
axis.Cross( to, lastPress );
float len = ( lastPress - to ).Length() / ( 2.0f * radius );
len = idMath::ClampFloat( -1.0f, 1.0f, len );
float phi = 2.0f * asin ( len ) ;
axis.Normalize();
axis *= sin( phi / 2.0f );
idQuat rot( axis.z, axis.y, axis.x, cos( phi / 2.0f ) );
rot.Normalize();
rotationQuat *= rot;
rotationQuat.Normalize();
lastPress = to;
lastPress.z = 0.0f;
m_dVec = rotationQuat.ToMat3()[2];
m_dVec.Normalize();
Redraw();
}
void CVectorCtl::OnLButtonDown(UINT nFlags, CPoint point) {
float curX = ( float )( 2 * point.x - 64 ) / 64;
float curY = ( float )( 2 * point.y - 64 ) / 64;
lastPress.Set( -curX, -curY, 0.0f );
SetCapture();
}
void CVectorCtl::OnLButtonUp(UINT nFlags, CPoint point) {
ReleaseCapture();
}