//--------------------------------------------------------------------------- // CameraView.cpp : implementation of the CameraView class // // (C) COPYRIGHT John Henckel, mailto:henckel@iname.com Aug 1998 // // Permission to use, copy, modify, distribute and sell this software // and its documentation for any purpose is hereby granted without fee, // provided that the above copyright notice appear in all copies. // // Visit my website! http://www.GeoCities.com/Paris/6502 #include "stdafx.h" #include "Poetry.h" #include "WorldDoc.h" #include "CameraView.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif //--------------------------------------------------------------------------- // CameraView IMPLEMENT_DYNCREATE(CameraView, CView) BEGIN_MESSAGE_MAP(CameraView, CView) //{{AFX_MSG_MAP(CameraView) ON_WM_MOUSEWHEEL() ON_WM_LBUTTONDBLCLK() ON_WM_LBUTTONDOWN() ON_WM_RBUTTONDOWN() ON_WM_RBUTTONUP() ON_WM_MOUSEMOVE() ON_WM_LBUTTONUP() ON_WM_SIZE() //}}AFX_MSG_MAP // Standard printing commands ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview) END_MESSAGE_MAP() //--------------------------------------------------------------------------- // CameraView construction/destruction CameraView::CameraView() { app = (MainApp*) AfxGetApp(); lastCount = 3; needRefresh = moving = FALSE; cam = new Camera(); palette = 0; } //--------------------------------------------------------------------------- CameraView::~CameraView() { app->IdleRestart(); } //--------------------------------------------------------------------------- // NOTE: Don't change this. Change ChildFrame instead. BOOL CameraView::PreCreateWindow(CREATESTRUCT& cs) { return CView::PreCreateWindow(cs); } //--------------------------------------------------------------------------- // This refreshes the memoryDC by calling OpenGL to set the camera // perspective, and then calling the document to draw the world picture. void CameraView::RefreshImage(CSize& size) { GLdouble aspect = size.cx; if (size.cy) aspect /= size.cy; if (aspect < 0.001) aspect = 0.001; if (aspect > 1000.0) aspect = 1000.0; glViewport(0, 0, 512, 512); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (cam->fov > EPSILON) gluPerspective(cam->fov*2/(aspect+1), aspect, cam->clipnear, cam->clipfar); else { double sz = cam->location.Length(); glOrtho(-sz*aspect, sz*aspect, -sz, sz, cam->clipnear, cam->clipfar); } glMatrixMode(GL_MODELVIEW); glLoadIdentity(); GLfloat lightPos[4] = { 1, 1, 2, 0 }; glLightfv( GL_LIGHT0, GL_POSITION, lightPos ); glLoadMatrixd(cam->rotation.AsMatrix()); glTranslated(-cam->location.x, -cam->location.y, -cam->location.z); glShadeModel( GL_SMOOTH ); glPolygonMode( GL_FRONT, GL_FILL ); glCullFace( GL_BACK ); glEnable( GL_CULL_FACE ); glClearColor(cam->background.r, cam->background.g, cam->background.b, cam->background.a); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_LIGHT0); glEnable(GL_LIGHTING); glEnable(GL_DEPTH_TEST); // depth sorting WorldDoc* pDoc = GetDocument(); pDoc->Draw(); glFlush(); glFinish(); } //-------------------------------------------------------------------- BOOL CameraView::IsStale() { if (!needRefresh) { WorldDoc* pDoc = GetDocument(); int count = pDoc->FrameCount(); needRefresh = (count != lastCount); lastCount = count; } return needRefresh; } //-------------------------------------------------------------------- // Build a color palette with RGB 332 bits HPALETTE ColorPalette332() { LOGPALETTE *logical; int n = 256; // allocate a bunch of memory for the logical palette logical = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * n); // set the entries in the logical palette logical->palVersion = 0x300; logical->palNumEntries = n; int redMask = 7; int greenMask = 7; int blueMask = 3; int i; // fill in an RGBA color palette for (i = 0; i < n; ++i) { logical->palPalEntry[i].peRed = (((i >> 0) & redMask) * 255) / redMask; logical->palPalEntry[i].peGreen = (((i >> 3) & greenMask) * 255) / greenMask; logical->palPalEntry[i].peBlue = (((i >> 6) & blueMask) * 255) / blueMask; logical->palPalEntry[i].peFlags = 0; } HPALETTE palette = CreatePalette(logical); free(logical); return palette; } //-------------------------------------------------------------------- // Build a color palette with RGB 332 bits HPALETTE GrayPalette() { LOGPALETTE *logical; int n = 256; // allocate a bunch of memory for the logical palette logical = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * n); // set the entries in the logical palette logical->palVersion = 0x300; logical->palNumEntries = n; int i; // fill in a gray palette for (i = 0; i < n; ++i) { logical->palPalEntry[i].peRed = i; logical->palPalEntry[i].peGreen = i; logical->palPalEntry[i].peBlue = i; logical->palPalEntry[i].peFlags = 0; } HPALETTE palette = CreatePalette(logical); free(logical); return palette; } //-------------------------------------------------------------------- // CameraView drawing // // Note, I assume that the framework will not call this function // multiple times concurrently on different threads. void CameraView::OnDraw(CDC* pDC) { WorldDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); CSize size; int tech = pDC->GetDeviceCaps(TECHNOLOGY); if (tech == DT_RASDISPLAY) { // if this is a window CRect rect; GetClientRect(&rect); size.cx = rect.Width(); size.cy = rect.Height(); } else { size.cx = pDC->GetDeviceCaps(HORZRES); size.cy = pDC->GetDeviceCaps(VERTRES); } HDC sourceDC = app->glMemDC; HDC targetDC = pDC->GetSafeHdc(); if (needRefresh) { cam->Step(); RefreshImage(size); } if (!moving) needRefresh = FALSE; if (!palette) { palette = CreateHalftonePalette(targetDC); // this has color and gray // palette = ColorPalette332(); // palette = GrayPalette(); } HPALETTE old = SelectPalette(targetDC, palette, FALSE); if (old != palette) DeleteObject(old); // else // AfxMessageBox("SelectPalette is redundant."); RealizePalette(targetDC); // SetStretchBltMode(targetDC, COLORONCOLOR); SetStretchBltMode(targetDC, HALFTONE); // Render to a fixed size surface, then stretch to fit the window. StretchBlt(targetDC, 0, 0, size.cx, size.cy, sourceDC, 0, 0, 512, 512, SRCCOPY ); } //--------------------------------------------------------------------------- // CameraView printing BOOL CameraView::OnPreparePrinting(CPrintInfo* pInfo) { // default preparation return DoPreparePrinting(pInfo); } //--------------------------------------------------------------------------- void CameraView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: add extra initialization before printing } //--------------------------------------------------------------------------- void CameraView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: add cleanup after printing } //--------------------------------------------------------------------------- // CameraView diagnostics #ifdef _DEBUG void CameraView::AssertValid() const { CView::AssertValid(); } //--------------------------------------------------------------------------- void CameraView::Dump(CDumpContext& dc) const { CView::Dump(dc); } //--------------------------------------------------------------------------- WorldDoc* CameraView::GetDocument() // non-debug version is inline { ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(WorldDoc))); return (WorldDoc*)m_pDocument; } #endif //_DEBUG //--------------------------------------------------------------------------- // CameraView message handlers BOOL CameraView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) { // TODO: Add your message handler code here and/or call default return CView::OnMouseWheel(nFlags, zDelta, pt); } void CameraView::OnLButtonDblClk(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CView::OnLButtonDblClk(nFlags, point); } void CameraView::OnLButtonDown(UINT nFlags, CPoint point) { anchor = point; if (!(nFlags & MK_SHIFT)) { cam->spin = Quat(); cam->speed = Vect(); moving = FALSE; } SetCapture(); CView::OnLButtonDown(nFlags, point); } void CameraView::OnLButtonUp(UINT nFlags, CPoint point) { if (!(nFlags & MK_RBUTTON)) ReleaseCapture(); CView::OnLButtonUp(nFlags, point); } void CameraView::OnRButtonDown(UINT nFlags, CPoint point) { anchor = point; cam->spin = Quat(); cam->speed = Vect(); moving = FALSE; SetCapture(); CView::OnRButtonDown(nFlags, point); } void CameraView::OnRButtonUp(UINT nFlags, CPoint point) { if (!(nFlags & MK_LBUTTON)) ReleaseCapture(); CView::OnRButtonUp(nFlags, point); } void CameraView::OnMouseMove(UINT nFlags, CPoint point) { static const double AF = .001; static const double TF = .005; double x = anchor.x - point.x; double y = anchor.y - point.y; if (nFlags & MK_LBUTTON && nFlags & MK_SHIFT) { cam->speed = Vect(x*TF, y*TF, 0); cam->speed.Rotate(cam->rotation); /* Vect axis(y, x, 0); axis.Rotate(cam->rotation); double r = sqrt(x*x + y*y); if (r) spin = Quat(r*TF/350, axis.x, axis.y, axis.z); */ } else if (nFlags & MK_LBUTTON) { if (cam->look) { cam->speed = Vect(x*TF, 0, 0); cam->speed.Rotate(cam->rotation); } else cam->spin = Quat(x*AF, 0, 1, 0); // yaw cam->speed = Vect(0, 0, -y*TF); cam->speed.Rotate(cam->rotation); } else if (nFlags & MK_RBUTTON && cam->look) { cam->speed = Vect(x*TF, y*TF, 0); cam->speed.Rotate(cam->rotation); double s = cam->speed.Length(); Vect rad = cam->lookat - cam->location; double r = rad.Length(); if (fabs(s*r) > EPSILON) { Vect ax = rad.Cross(cam->speed); ax.Normalize(); cam->spin = Quat(s/r, ax.x, ax.y, ax.z); } } else if (nFlags & MK_RBUTTON) { if (fabs(x) > fabs(y)) cam->spin = Quat(-x*AF, 0, 0, 1); // roll else cam->spin = Quat(y*AF, 1, 0, 0); // pitch } else return; needRefresh = TRUE; moving = TRUE; // CView::OnMouseMove(nFlags, point); } //---------------------------------------------------------------------------- void CameraView::OnSize(UINT nType, int cx, int cy) { CView::OnSize(nType, cx, cy); needRefresh = TRUE; } //---------------------------------------------------------------------------- void CameraView::OnInitialUpdate() { CView::OnInitialUpdate(); }