1792 lines
47 KiB
C++
1792 lines
47 KiB
C++
/*
|
|
** Command & Conquer Renegade(tm)
|
|
** Copyright 2025 Electronic Arts Inc.
|
|
**
|
|
** This program 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.
|
|
**
|
|
** This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
// GraphicView.cpp : implementation file
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include "w3dview.h"
|
|
#include "graphicview.h"
|
|
#include "ww3d.h"
|
|
#include "globals.h"
|
|
#include "w3dviewdoc.h"
|
|
#include <process.h>
|
|
#include "quat.h"
|
|
#include "mainfrm.h"
|
|
#include "utils.h"
|
|
#include "mmsystem.h"
|
|
#include "light.h"
|
|
#include "viewerassetmgr.h"
|
|
#include "rcfile.h"
|
|
#include "part_emt.h"
|
|
#include "part_buf.h"
|
|
#include "hlod.h"
|
|
#include "viewerscene.h"
|
|
#include "screencursor.h"
|
|
#include "mesh.h"
|
|
#include "coltest.h"
|
|
#include "mpu.h"
|
|
#include "dazzle.h"
|
|
#include "soundscene.h"
|
|
#include "wwaudio.h"
|
|
#include "metalmap.h"
|
|
#include "dx8wrapper.h"
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// Local Prototypes
|
|
/////////////////////////////////////////////////////////////////////////
|
|
void CALLBACK fnTimerCallback (UINT, UINT, DWORD, DWORD, DWORD);
|
|
|
|
|
|
IMPLEMENT_DYNCREATE(CGraphicView, CView)
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CGraphicView
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
CGraphicView::CGraphicView (void)
|
|
: m_bInitialized (FALSE),
|
|
m_pCamera (NULL),
|
|
m_TimerID (0),
|
|
m_bMouseDown (FALSE),
|
|
m_bRMouseDown (FALSE),
|
|
m_bActive (TRUE),
|
|
m_animationSpeed (1.0F),
|
|
m_dwLastFrameUpdate (0),
|
|
m_iWindowed (1),
|
|
m_animationState (AnimInvalid),
|
|
m_objectRotation (NoRotation),
|
|
m_LightRotation (NoRotation),
|
|
m_bLightMeshInScene (false),
|
|
m_pLightMesh (NULL),
|
|
m_ParticleCountUpdate (0),
|
|
m_CameraBonePosX (false),
|
|
m_UpdateCounter (0),
|
|
m_allowedCameraRotation (FreeRotation),
|
|
m_ObjectCenter (0.0f, 0.0f, 0.0f)
|
|
{
|
|
// Get the windowed mode from the registry
|
|
CString string_windowed = theApp.GetProfileString ("Config", "Windowed", "1");
|
|
m_iWindowed = ::atoi ((LPCTSTR)string_windowed);
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ~CGraphicView
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
CGraphicView::~CGraphicView ()
|
|
{
|
|
return ;
|
|
}
|
|
|
|
|
|
BEGIN_MESSAGE_MAP(CGraphicView, CView)
|
|
//{{AFX_MSG_MAP(CGraphicView)
|
|
ON_WM_CREATE()
|
|
ON_WM_SIZE()
|
|
ON_WM_DESTROY()
|
|
ON_WM_LBUTTONDOWN()
|
|
ON_WM_LBUTTONUP()
|
|
ON_WM_MOUSEMOVE()
|
|
ON_WM_RBUTTONUP()
|
|
ON_WM_RBUTTONDOWN()
|
|
ON_WM_GETMINMAXINFO()
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// OnDraw
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::OnDraw (CDC* pDC)
|
|
{
|
|
// Get the document to display
|
|
CW3DViewDoc* doc = (CW3DViewDoc *)GetDocument();
|
|
|
|
// Are we in a valid state?
|
|
if (!pDC->IsPrinting ())
|
|
{
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
#ifdef _DEBUG
|
|
void CGraphicView::AssertValid() const
|
|
{
|
|
CView::AssertValid();
|
|
}
|
|
|
|
void CGraphicView::Dump(CDumpContext& dc) const
|
|
{
|
|
CView::Dump(dc);
|
|
}
|
|
#endif //_DEBUG
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PreCreateWindow
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
int
|
|
CGraphicView::OnCreate (LPCREATESTRUCT lpCreateStruct)
|
|
{
|
|
// Allow the base class to process this message
|
|
if (CView::OnCreate(lpCreateStruct) == -1)
|
|
return -1;
|
|
|
|
m_dwLastFrameUpdate = timeGetTime ();//::GetTickCount ();
|
|
return 0;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// InitializeGraphicView
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
BOOL
|
|
CGraphicView::InitializeGraphicView (void)
|
|
{
|
|
// Assume failure
|
|
BOOL bReturn = FALSE;
|
|
if (g_iDeviceIndex < 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
m_bInitialized = FALSE;
|
|
|
|
// Initialize the rendering engine with the information from
|
|
// this window.
|
|
RECT rect;
|
|
GetClientRect (&rect);
|
|
|
|
int cx = rect.right-rect.left;
|
|
int cy = rect.bottom-rect.top;
|
|
if (m_iWindowed == 0) {
|
|
cx = g_iWidth;
|
|
cy = g_iHeight;
|
|
((CW3DViewDoc *)GetDocument())->Show_Cursor (true);
|
|
} else {
|
|
((CW3DViewDoc *)GetDocument())->Show_Cursor (false);
|
|
}
|
|
|
|
bReturn = (WW3D::Set_Render_Device (g_iDeviceIndex,
|
|
cx,
|
|
cy,
|
|
g_iBitsPerPixel,
|
|
m_iWindowed) == WW3D_ERROR_OK);
|
|
|
|
ASSERT (bReturn);
|
|
if (bReturn && (m_pCamera == NULL))
|
|
{
|
|
// Instantiate a new camera class
|
|
m_pCamera = new CameraClass ();
|
|
bReturn = (m_pCamera != NULL);
|
|
|
|
// Were we successful in creating a camera?
|
|
ASSERT (m_pCamera);
|
|
if (m_pCamera)
|
|
{
|
|
// Create a transformation matrix
|
|
Matrix3D transform (1);
|
|
transform.Translate (Vector3 (0.0F, 0.0F, 35.0F));
|
|
|
|
// Point the camera in this direction (I think)
|
|
m_pCamera->Set_Transform (transform);
|
|
}
|
|
|
|
//
|
|
// Attach the 'listener' to the camera
|
|
//
|
|
WWAudioClass::Get_Instance ()->Get_Sound_Scene ()->Attach_Listener_To_Obj (m_pCamera);
|
|
}
|
|
|
|
Reset_FOV ();
|
|
|
|
if (m_pLightMesh == NULL)
|
|
{
|
|
ResourceFileClass light_mesh_file (NULL, "Light.w3d");
|
|
WW3DAssetManager::Get_Instance()->Load_3D_Assets (light_mesh_file);
|
|
|
|
m_pLightMesh = WW3DAssetManager::Get_Instance()->Create_Render_Obj ("LIGHT");
|
|
ASSERT (m_pLightMesh != NULL);
|
|
m_bLightMeshInScene = false;
|
|
}
|
|
|
|
|
|
// Remember whether or not we are initialized
|
|
m_bInitialized = bReturn;
|
|
|
|
if (m_bInitialized && (m_TimerID == 0))
|
|
{
|
|
// Kick off a timer that we can use to update
|
|
// the display (kinda like a game loop iterator)
|
|
TIMECAPS caps = { 0 };
|
|
::timeGetDevCaps (&caps, sizeof (TIMECAPS));
|
|
UINT freq = max (caps.wPeriodMin, 16U);
|
|
m_TimerID = (UINT)::timeSetEvent (freq,
|
|
freq,
|
|
fnTimerCallback,
|
|
(DWORD)m_hWnd,
|
|
TIME_PERIODIC);
|
|
}
|
|
|
|
// Return the TRUE/FALSE result code
|
|
return bReturn;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// OnSize
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::OnSize
|
|
(
|
|
UINT nType,
|
|
int cx,
|
|
int cy
|
|
)
|
|
{
|
|
// Allow the base class to process this message
|
|
CView::OnSize (nType, cx, cy);
|
|
|
|
if (m_bInitialized) {
|
|
|
|
if (m_iWindowed == 0) {
|
|
cx = g_iWidth;
|
|
cy = g_iHeight;
|
|
}
|
|
|
|
// Change the resolution of the rendering device to
|
|
// match that of the view's current dimensions
|
|
if (m_iWindowed == 1) {
|
|
WW3D::Set_Device_Resolution (cx, cy, g_iBitsPerPixel, m_iWindowed);
|
|
}
|
|
|
|
// Force a repaint of the screen
|
|
Reset_FOV ();
|
|
RepaintView ();
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// OnDestroy
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::OnDestroy (void)
|
|
{
|
|
// Allow the base class to process this message
|
|
CView::OnDestroy ();
|
|
|
|
//
|
|
// Remove the listener from the camera
|
|
//
|
|
WWAudioClass::Get_Instance ()->Get_Sound_Scene ()->Attach_Listener_To_Obj (NULL);
|
|
|
|
//
|
|
// Free the camera object
|
|
//
|
|
MEMBER_RELEASE (m_pCamera);
|
|
MEMBER_RELEASE (m_pLightMesh);
|
|
|
|
// Is there an update thread running?
|
|
if (m_TimerID == 0) {
|
|
|
|
// Stop the timer
|
|
::timeKillEvent ((UINT)m_TimerID);
|
|
m_TimerID = 0;
|
|
}
|
|
|
|
// Cache this information in the registry
|
|
TCHAR temp_string[10];
|
|
::itoa (m_iWindowed, temp_string, 10);
|
|
theApp.WriteProfileString ("Config", "Windowed", temp_string);
|
|
|
|
// We are no longer initialized
|
|
m_bInitialized = FALSE;
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// OnInitialUpdate
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::OnInitialUpdate (void)
|
|
{
|
|
// Allow the base class to process this message
|
|
CView::OnInitialUpdate ();
|
|
|
|
CW3DViewDoc* doc = (CW3DViewDoc *)GetDocument();
|
|
if (doc)
|
|
{
|
|
// Ask the document to initialize the scene (if it hasn't
|
|
// already done so)
|
|
doc->InitScene ();
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Set_Lowest_LOD
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
Set_Lowest_LOD (RenderObjClass *render_obj)
|
|
{
|
|
if (render_obj != NULL) {
|
|
for (int index = 0; index < render_obj->Get_Num_Sub_Objects (); index ++) {
|
|
RenderObjClass *psub_obj = render_obj->Get_Sub_Object (index);
|
|
if (psub_obj != NULL) {
|
|
Set_Lowest_LOD (psub_obj);
|
|
}
|
|
MEMBER_RELEASE (psub_obj);
|
|
}
|
|
|
|
//
|
|
// Switcht this LOD to its lowest level
|
|
//
|
|
if (render_obj->Class_ID () == RenderObjClass::CLASSID_HLOD) {
|
|
((HLodClass *)render_obj)->Set_LOD_Level (0);
|
|
}
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Allow_Update
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::Allow_Update (bool onoff)
|
|
{
|
|
if (onoff) {
|
|
m_UpdateCounter --;
|
|
} else {
|
|
m_UpdateCounter ++;
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// RepaintView
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::RepaintView
|
|
(
|
|
BOOL bUpdateAnimation,
|
|
DWORD ticks_to_use
|
|
)
|
|
{
|
|
//
|
|
// Simple check to avoid re-entrance
|
|
//
|
|
static bool _already_painting = false;
|
|
if (_already_painting) return;
|
|
_already_painting = true;
|
|
|
|
//
|
|
// Are we in a valid state?
|
|
//
|
|
CW3DViewDoc *doc = (CW3DViewDoc *)GetDocument();
|
|
if (doc->Is_Initialized () && doc->GetScene () && m_UpdateCounter == 0) {
|
|
|
|
// Only update the frame if the animation is
|
|
// supposed to be playing
|
|
int cur_ticks = timeGetTime();
|
|
int ticks_elapsed = cur_ticks - m_dwLastFrameUpdate;
|
|
m_dwLastFrameUpdate = cur_ticks;
|
|
|
|
// Update the W3D frame times according to our elapsed tick count
|
|
if (ticks_to_use == 0) {
|
|
WW3D::Sync (WW3D::Get_Sync_Time() + (ticks_elapsed * m_animationSpeed));
|
|
} else {
|
|
WW3D::Sync (WW3D::Get_Sync_Time() + ticks_to_use);
|
|
}
|
|
|
|
// Do we need to update the current animation?
|
|
if ((m_animationState == AnimPlaying) &&
|
|
bUpdateAnimation)
|
|
{
|
|
float animationSpeed = ((float)ticks_elapsed) / 1000.00F;
|
|
animationSpeed = (animationSpeed * m_animationSpeed);
|
|
doc->UpdateFrame (animationSpeed);
|
|
}
|
|
|
|
// Perform the object rotation if necessary
|
|
if ((m_objectRotation != NoRotation) &&
|
|
(bUpdateAnimation == TRUE))
|
|
{
|
|
Rotate_Object ();
|
|
}
|
|
|
|
// Perform the light rotation if necessary
|
|
if ((m_LightRotation != NoRotation) &&
|
|
(bUpdateAnimation == TRUE))
|
|
{
|
|
Rotate_Light ();
|
|
}
|
|
|
|
// Reset the current lod to be the lowest possible LOD...
|
|
RenderObjClass *prender_obj = doc->GetDisplayedObject ();
|
|
if ((prender_obj != NULL) &&
|
|
(doc->GetScene ()->Are_LODs_Switching ()))
|
|
{
|
|
Set_Lowest_LOD (prender_obj);
|
|
}
|
|
|
|
// Update the metal map
|
|
// assuming object is at origin
|
|
MetalMapManagerClass *metal=_TheAssetMgr->Peek_Metal_Map_Manager();
|
|
if (metal)
|
|
{
|
|
LightClass *pscene_light = doc->GetSceneLight();
|
|
Vector3 ambient,diffuse,l,v;
|
|
ambient=doc->GetScene()->Get_Ambient_Light();
|
|
pscene_light->Get_Diffuse(&diffuse);
|
|
l=pscene_light->Get_Position();
|
|
l.Normalize();
|
|
v=m_pCamera->Get_Position();
|
|
v.Normalize();
|
|
metal->Update_Lighting(ambient,diffuse,l,v);
|
|
metal->Update_Textures();
|
|
}
|
|
|
|
//
|
|
// Render the background BMP
|
|
//
|
|
WW3D::Begin_Render (TRUE, TRUE, doc->GetBackgroundColor ());
|
|
WW3D::Render (doc->Get2DScene (), doc->Get2DCamera (), FALSE, FALSE);
|
|
|
|
//
|
|
// Render the background scene
|
|
//
|
|
if (doc->GetBackgroundObjectName ().GetLength () > 0) {
|
|
WW3D::Render (doc->GetBackObjectScene (), doc->GetBackObjectCamera (), FALSE, FALSE);
|
|
}
|
|
|
|
//
|
|
// Render the main scene
|
|
//
|
|
DWORD pt_high = 0L;
|
|
|
|
// Wait for all previous rendering to complete before starting benchmark.
|
|
DWORD profile_time = ::Get_CPU_Clock (pt_high);
|
|
|
|
WW3D::Render (doc->GetScene (), m_pCamera, FALSE, FALSE);
|
|
|
|
// Wait for all rendering to complete before stopping benchmark.
|
|
DWORD milliseconds = (::Get_CPU_Clock (pt_high) - profile_time) / 1000;
|
|
|
|
//
|
|
// Render the cursor
|
|
//
|
|
WW3D::Render (doc->GetCursorScene (), doc->Get2DCamera (), FALSE, FALSE);
|
|
|
|
// Render the dazzles
|
|
doc->Render_Dazzles(m_pCamera);
|
|
|
|
// Finish out the rendering process
|
|
WW3D::End_Render ();
|
|
|
|
//
|
|
// Let the audio class think
|
|
//
|
|
WWAudioClass::Get_Instance ()->On_Frame_Update ();
|
|
|
|
//
|
|
// Update the count of particles and polys in the status bar
|
|
//
|
|
if ((cur_ticks - m_ParticleCountUpdate > 250)) {
|
|
m_ParticleCountUpdate = cur_ticks;
|
|
doc->Update_Particle_Count ();
|
|
|
|
int polys = (prender_obj != NULL) ? prender_obj->Get_Num_Polys () : 0;
|
|
((CMainFrame *)::AfxGetMainWnd ())->UpdatePolygonCount (polys);
|
|
}
|
|
|
|
//
|
|
// Update the frame time in the status bar
|
|
//
|
|
((CMainFrame *)::AfxGetMainWnd ())->Update_Frame_Time (milliseconds);
|
|
}
|
|
|
|
_already_painting = false;
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// UpdateDisplay
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::UpdateDisplay (void)
|
|
{
|
|
// Get the document to display
|
|
CW3DViewDoc* doc = (CW3DViewDoc *)GetDocument();
|
|
|
|
// Are we in a valid state?
|
|
/*if (m_bInitialized && doc->GetScene ())
|
|
{
|
|
RenderObjClass *pCRenderObj = doc->GetDisplayedObject ();
|
|
if (pCRenderObj)
|
|
{
|
|
Matrix3D transform = pCRenderObj->Get_Transform ();
|
|
transform.Rotate_X (0.05F);
|
|
transform.Rotate_Y (0.05F);
|
|
transform.Rotate_Z (0.05F);
|
|
|
|
pCRenderObj->Set_Transform (transform);
|
|
}
|
|
|
|
// Render the current view inside the frame
|
|
WW3D::Begin_Render (TRUE, TRUE, Vector3 (0.2,0.4,0.6));
|
|
WW3D::Render (doc->GetScene (), m_pCamera, FALSE, FALSE);
|
|
WW3D::End_Render ();
|
|
} */
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// WindowProc
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
LRESULT
|
|
CGraphicView::WindowProc
|
|
(
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
// Is this the repaint message we are expecting?
|
|
if (message == WM_USER+101) {
|
|
|
|
//
|
|
// Force the repaint...
|
|
//
|
|
RepaintView ();
|
|
RemoveProp (m_hWnd, "WaitingToProcess");
|
|
|
|
} else if (message == WM_PAINT) {
|
|
|
|
// If we are in fullscreen mode, then erase the window background
|
|
if (m_iWindowed == 0) {
|
|
|
|
// Get the client rectangle of the window
|
|
RECT rect;
|
|
GetClientRect (&rect);
|
|
|
|
// Get the window's DC
|
|
HDC hDC = ::GetDC (m_hWnd);
|
|
if (hDC) {
|
|
|
|
// Erase the background
|
|
::FillRect (hDC, &rect, (HBRUSH)(COLOR_WINDOW + 1));
|
|
::ReleaseDC (m_hWnd, hDC);
|
|
}
|
|
}
|
|
|
|
RepaintView (FALSE);
|
|
ValidateRect (NULL);
|
|
return 0;
|
|
|
|
} else if (message == WM_KEYDOWN) {
|
|
|
|
if ((wParam == VK_CONTROL) && (m_bLightMeshInScene == false)) {
|
|
CW3DViewDoc* doc = (CW3DViewDoc *)GetDocument();
|
|
m_pLightMesh->Add (doc->GetScene ());
|
|
m_bLightMeshInScene = true;
|
|
}
|
|
|
|
} else if (message == WM_KEYUP) {
|
|
|
|
if ((wParam == VK_CONTROL) && (m_bLightMeshInScene == true)) {
|
|
CW3DViewDoc* doc = (CW3DViewDoc *)GetDocument();
|
|
m_pLightMesh->Remove ();
|
|
m_bLightMeshInScene = false;
|
|
}
|
|
}
|
|
|
|
// Allow the base class to process this message
|
|
return CView::WindowProc(message, wParam, lParam);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// fnTimerCallback
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void CALLBACK
|
|
fnTimerCallback
|
|
(
|
|
UINT uID,
|
|
UINT uMsg,
|
|
DWORD dwUser,
|
|
DWORD dw1,
|
|
DWORD dw2
|
|
)
|
|
{
|
|
HWND hwnd = (HWND)dwUser;
|
|
if (hwnd != NULL) {
|
|
|
|
// Send this event off to the view to process (hackish, but fine for now)
|
|
if ((GetProp (hwnd, "WaitingToProcess") == NULL) &&
|
|
(GetProp (hwnd, "Inactive") == NULL)) {
|
|
|
|
SetProp (hwnd, "WaitingToProcess", (HANDLE)1);
|
|
|
|
// Send the message to the view so it will be in the
|
|
// same thread (Surrender doesn't seem to be thread-safe)
|
|
::PostMessage (hwnd, WM_USER + 101, 0, 0L);
|
|
}
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// OnLButtonDown
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::OnLButtonDown
|
|
(
|
|
UINT nFlags,
|
|
CPoint point
|
|
)
|
|
{
|
|
// Capture all mouse messages
|
|
SetCapture ();
|
|
|
|
// Mouse button is down
|
|
m_bMouseDown = TRUE;
|
|
m_lastPoint = point;
|
|
|
|
if (m_bRMouseDown) {
|
|
::SetCursor (::LoadCursor (::AfxGetResourceHandle (), MAKEINTRESOURCE (IDC_CURSOR_GRAB)));
|
|
((CW3DViewDoc *)GetDocument())->Set_Cursor ("grab.tga");
|
|
} else {
|
|
((CW3DViewDoc *)GetDocument())->Set_Cursor ("orbit.tga");
|
|
}
|
|
|
|
CView::OnLButtonDown (nFlags, point);
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// OnLButtonUp
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::OnLButtonUp
|
|
(
|
|
UINT nFlags,
|
|
CPoint point
|
|
)
|
|
{
|
|
if (!m_bRMouseDown)
|
|
{
|
|
// Release the mouse capture
|
|
ReleaseCapture ();
|
|
}
|
|
|
|
// Mouse button is up
|
|
m_bMouseDown = FALSE;
|
|
|
|
if (m_bRMouseDown == TRUE)
|
|
{
|
|
::SetCursor (::LoadCursor (::AfxGetResourceHandle (), MAKEINTRESOURCE (IDC_CURSOR_ZOOM)));
|
|
((CW3DViewDoc *)GetDocument())->Set_Cursor ("zoom.tga");
|
|
}
|
|
else
|
|
{
|
|
::SetCursor (::LoadCursor (NULL, MAKEINTRESOURCE (IDC_ARROW)));
|
|
((CW3DViewDoc *)GetDocument())->Set_Cursor ("cursor.tga");
|
|
}
|
|
|
|
// Allow the base class to process this message
|
|
CView::OnLButtonUp (nFlags, point);
|
|
return ;
|
|
}
|
|
|
|
float minZoomAdjust = 0.0F;
|
|
Vector3 sphereCenter;
|
|
Quaternion rotation;
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// OnMouseMove
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::OnMouseMove
|
|
(
|
|
UINT nFlags,
|
|
CPoint point
|
|
)
|
|
{
|
|
int iDeltaX = m_lastPoint.x-point.x;
|
|
int iDeltaY = m_lastPoint.y-point.y;
|
|
|
|
// Get the document to display
|
|
CW3DViewDoc* doc = (CW3DViewDoc *)GetDocument();
|
|
|
|
if (!(nFlags & MK_CONTROL) && m_bLightMeshInScene) {
|
|
m_pLightMesh->Remove ();
|
|
m_bLightMeshInScene = false;
|
|
} else if ((nFlags & MK_CONTROL) && (m_bLightMeshInScene == false)) {
|
|
m_pLightMesh->Add (doc->GetScene ());
|
|
m_bLightMeshInScene = true;
|
|
}
|
|
|
|
// Is the mouse button down?
|
|
if (m_bMouseDown && m_bRMouseDown)
|
|
{
|
|
// Get the transformation matrix for the camera and its inverse
|
|
Matrix3D transform = m_pCamera->Get_Transform ();
|
|
|
|
RECT rect;
|
|
GetClientRect (&rect);
|
|
|
|
float midPointX = float(rect.right >> 1);
|
|
float midPointY = float(rect.bottom >> 1);
|
|
|
|
float lastPointX = ((float)m_lastPoint.x - midPointX) / midPointX;
|
|
float lastPointY = (midPointY - (float)m_lastPoint.y) / midPointY;
|
|
|
|
float pointX = ((float)point.x - midPointX) / midPointX;
|
|
float pointY = (midPointY - (float)point.y) / midPointY;
|
|
|
|
|
|
Vector3 cameraPan = Vector3(-1.00F*m_CameraDistance*(pointX - lastPointX), -1.00F*m_CameraDistance*(pointY - lastPointY), 0.00F);
|
|
|
|
transform.Translate (cameraPan);
|
|
|
|
Matrix3 view = Build_Matrix3 (rotation);
|
|
Vector3 move = view * cameraPan;
|
|
sphereCenter += move;
|
|
|
|
// Move the camera back to get a good view of the object
|
|
m_pCamera->Set_Transform (transform);
|
|
|
|
m_lastPoint = point;
|
|
}
|
|
// Is the mouse button down?
|
|
else if ((nFlags & MK_CONTROL) && m_bMouseDown)
|
|
{
|
|
LightClass *pSceneLight = doc->GetSceneLight ();
|
|
if ((pSceneLight != NULL) && (m_pLightMesh != NULL))
|
|
{
|
|
RECT rect;
|
|
GetClientRect (&rect);
|
|
|
|
Vector3 point_in_view;
|
|
Vector3 lastpoint_in_view;
|
|
|
|
float midPointX = float(rect.right >> 1);
|
|
float midPointY = float(rect.bottom >> 1);
|
|
|
|
float lastPointX = ((float)m_lastPoint.x - midPointX) / midPointX;
|
|
float lastPointY = (midPointY - (float)m_lastPoint.y) / midPointY;
|
|
|
|
float pointX = ((float)point.x - midPointX) / midPointX;
|
|
float pointY = (midPointY - (float)point.y) / midPointY;
|
|
|
|
Quaternion mouse_motion = Inverse(::Trackball(lastPointX, lastPointY, pointX, pointY, 0.8F));
|
|
Quaternion light_orientation;
|
|
Quaternion camera = Build_Quaternion(m_pCamera->Get_Transform());
|
|
Quaternion cur_light = Build_Quaternion(pSceneLight->Get_Transform());
|
|
|
|
light_orientation = camera;
|
|
light_orientation = light_orientation * mouse_motion;
|
|
light_orientation = light_orientation * Inverse(camera);
|
|
light_orientation = light_orientation * cur_light;
|
|
light_orientation.Normalize();
|
|
|
|
Vector3 to_center;
|
|
Matrix3D matrix = pSceneLight->Get_Transform();
|
|
Matrix3D::Inverse_Transform_Vector(matrix,sphereCenter,&to_center);
|
|
|
|
Matrix3D light_tm(light_orientation, sphereCenter);
|
|
light_tm.Translate(-to_center);
|
|
|
|
m_pLightMesh->Set_Transform(light_tm);
|
|
pSceneLight->Set_Transform(light_tm);
|
|
}
|
|
|
|
m_lastPoint = point;
|
|
}
|
|
// Is the mouse button down?
|
|
else if ((nFlags & MK_CONTROL) && m_bRMouseDown)
|
|
{
|
|
// Get the currently displayed object
|
|
CW3DViewDoc *doc= (CW3DViewDoc *)GetDocument();
|
|
LightClass *pscene_light = doc->GetSceneLight ();
|
|
RenderObjClass *prender_obj = doc->GetDisplayedObject ();
|
|
if ((pscene_light != NULL) && (prender_obj != NULL)) {
|
|
|
|
// Calculate a light adjustment factor
|
|
CRect rect;
|
|
GetClientRect (&rect);
|
|
float deltay = (float(iDeltaY))/(float(rect.bottom - rect.top));
|
|
float adjustment = deltay * (m_ViewedSphere.Radius * 3.0F);
|
|
|
|
// Determine the light's new position based on this factor
|
|
Matrix3D transform = pscene_light->Get_Transform ();
|
|
transform.Translate (Vector3 (0, 0, adjustment));
|
|
|
|
// Determine what the distance from the light to the object
|
|
// would be with this new position
|
|
Vector3 light_pos = transform.Get_Translation ();
|
|
Vector3 obj_pos = prender_obj->Get_Position ();
|
|
float distance = (light_pos - obj_pos).Length ();
|
|
|
|
// If the new position is acceptable, move the light
|
|
if (distance > m_ViewedSphere.Radius) {
|
|
m_pLightMesh->Set_Transform (transform);
|
|
pscene_light->Set_Transform (transform);
|
|
}
|
|
}
|
|
|
|
m_lastPoint = point;
|
|
}
|
|
// Is the mouse button down?
|
|
else if (m_bMouseDown)
|
|
{
|
|
// Get the document to display
|
|
CW3DViewDoc* doc = (CW3DViewDoc *)GetDocument();
|
|
|
|
// Are we in a valid state?
|
|
if (m_bInitialized && doc->GetScene ())
|
|
{
|
|
RenderObjClass *pCRenderObj = doc->GetDisplayedObject ();
|
|
if (pCRenderObj)
|
|
{
|
|
RECT rect;
|
|
GetClientRect (&rect);
|
|
|
|
float midPointX = float(rect.right >> 1);
|
|
float midPointY = float(rect.bottom >> 1);
|
|
|
|
float lastPointX = ((float)m_lastPoint.x - midPointX) / midPointX;
|
|
float lastPointY = (midPointY - (float)m_lastPoint.y) / midPointY;
|
|
|
|
float pointX = ((float)point.x - midPointX) / midPointX;
|
|
float pointY = (midPointY - (float)point.y) / midPointY;
|
|
|
|
// Rotate around the object (orbit) using a 0.00F - 1.00F percentage of
|
|
// the mouse coordinates
|
|
rotation = ::Trackball (lastPointX, lastPointY, pointX, pointY, 0.8F);
|
|
|
|
// Do we want to 'lock-out' all rotation except X?
|
|
if (m_allowedCameraRotation == OnlyRotateX)
|
|
{
|
|
Matrix3D tempMatrix = Build_Matrix3D (rotation);
|
|
Matrix3D tempMatrix2 (1);
|
|
|
|
tempMatrix2.Rotate_X (tempMatrix.Get_X_Rotation ());
|
|
tempMatrix2.Set_Translation (tempMatrix.Get_Translation ());
|
|
|
|
rotation = Build_Quaternion (tempMatrix2);
|
|
}
|
|
// Do we want to 'lock-out' all rotation except Y?
|
|
else if (m_allowedCameraRotation == OnlyRotateY)
|
|
{
|
|
Matrix3D tempMatrix = Build_Matrix3D (rotation);
|
|
Matrix3D tempMatrix2 (1);
|
|
|
|
tempMatrix2.Rotate_Y (tempMatrix.Get_Y_Rotation ());
|
|
tempMatrix2.Set_Translation (tempMatrix.Get_Translation ());
|
|
|
|
rotation = Build_Quaternion (tempMatrix2);
|
|
}
|
|
// Do we want to 'lock-out' all rotation except Z?
|
|
else if (m_allowedCameraRotation == OnlyRotateZ)
|
|
{
|
|
Matrix3D tempMatrix = Build_Matrix3D (rotation);
|
|
Matrix3D tempMatrix2 (1);
|
|
|
|
tempMatrix2.Rotate_Z (tempMatrix.Get_Z_Rotation ());
|
|
tempMatrix2.Set_Translation (tempMatrix.Get_Translation ());
|
|
|
|
rotation = Build_Quaternion (tempMatrix2);
|
|
}
|
|
|
|
// Get the transformation matrix for the camera and its inverse
|
|
Matrix3D transform = m_pCamera->Get_Transform ();
|
|
Matrix3D inverseMatrix;
|
|
transform.Get_Orthogonal_Inverse (inverseMatrix);
|
|
|
|
Vector3 to_object = inverseMatrix * sphereCenter;
|
|
|
|
transform.Translate (to_object);
|
|
|
|
Matrix3D::Multiply (transform, Build_Matrix3D (rotation), &transform);
|
|
|
|
transform.Translate (-to_object);
|
|
|
|
// Rotate and translate the camera
|
|
m_pCamera->Set_Transform (transform);
|
|
|
|
doc->GetBackObjectCamera ()->Set_Transform (transform);
|
|
doc->GetBackObjectCamera ()->Set_Position (Vector3 (0.00F, 0.00F, 0.00F));
|
|
}
|
|
}
|
|
|
|
m_lastPoint = point;
|
|
}
|
|
else if (m_bRMouseDown)
|
|
{
|
|
m_lastPoint = point;
|
|
|
|
// Get the transformation matrix for the camera and its inverse
|
|
Matrix3D transform = m_pCamera->Get_Transform ();
|
|
|
|
Vector3 distanceVectorZ = transform.Get_Z_Vector ();
|
|
if (iDeltaY != 0)
|
|
{
|
|
|
|
// Get the bouding rectangle of the main view
|
|
CRect rect;
|
|
GetClientRect (&rect);
|
|
|
|
float deltay = (float(iDeltaY))/(float(rect.bottom - rect.top));
|
|
float adjustment = deltay * m_CameraDistance * 3.0F;
|
|
|
|
if ((adjustment < minZoomAdjust) && (adjustment >= 0.00F))
|
|
{
|
|
adjustment = minZoomAdjust;
|
|
}
|
|
|
|
if ((adjustment > -minZoomAdjust) && (adjustment <= 0.00F))
|
|
{
|
|
adjustment = -minZoomAdjust;
|
|
}
|
|
|
|
if ((m_CameraDistance + adjustment) > 0.00F)
|
|
{
|
|
m_CameraDistance += adjustment;
|
|
transform.Translate (Vector3 (0.0F, 0.0F, adjustment));
|
|
|
|
// Move the camera back to get a good view of the object
|
|
m_pCamera->Set_Transform (transform);
|
|
|
|
// Get the main window of our app
|
|
CMainFrame *pCMainWnd = (CMainFrame *)::AfxGetMainWnd ();
|
|
if (pCMainWnd != NULL)
|
|
{
|
|
// Ensure the background camera matches the main camera
|
|
CW3DViewDoc *doc = (CW3DViewDoc *)GetDocument();
|
|
doc->GetBackObjectCamera ()->Set_Transform (transform);
|
|
doc->GetBackObjectCamera ()->Set_Position (Vector3 (0.00F, 0.00F, 0.00F));
|
|
|
|
// Update the current object if necessary
|
|
RenderObjClass *prender_obj = doc->GetDisplayedObject ();
|
|
if (prender_obj != NULL) {
|
|
|
|
// Ensure the status bar is updated with the correct poly count
|
|
pCMainWnd->UpdatePolygonCount (prender_obj->Get_Num_Polys ());
|
|
}
|
|
|
|
// Ensure the status bar is updated with the correct camera distance
|
|
pCMainWnd->UpdateCameraDistance (m_CameraDistance);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
m_lastPoint = point;
|
|
}
|
|
|
|
// Allow the base class to process this message
|
|
CView::OnMouseMove (nFlags, point);
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Reset_Camera_To_Display_Emitter
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::Reset_Camera_To_Display_Emitter (ParticleEmitterClass &emitter)
|
|
{
|
|
// Get some of the emitter settings
|
|
Vector3 velocity = emitter.Get_Start_Velocity ();
|
|
const Vector3 &acceleration = emitter.Get_Acceleration ();
|
|
float lifetime = emitter.Get_Lifetime ();
|
|
|
|
// If the velocity is 0, then use the randomizer as the default velocity
|
|
bool use_vel_rand = false;
|
|
if ((velocity.X == 0) && (velocity.Y == 0) && (velocity.Z == 0)) {
|
|
//velocity.Set (emitter.Get_Velocity_Random (), emitter.Get_Velocity_Random (), emitter.Get_Velocity_Random ());
|
|
//use_vel_rand = true;
|
|
}
|
|
|
|
// Determine what the max extent covered by a particle will be.
|
|
Vector3 distance = (velocity * lifetime) + ((acceleration * (lifetime * lifetime)) / 2.0F);
|
|
|
|
// Do we need to take into account acceleration?
|
|
Vector3 distance_maxima (0, 0, 0);
|
|
if ((acceleration.X != 0) || (acceleration.Y != 0) || (acceleration.Z != 0)) {
|
|
|
|
// Determine at what time (for each x,y,z) a maxima will occur.
|
|
Vector3 time_max (0, 0, 0);
|
|
time_max.X = (acceleration.X != 0) ? ((-velocity.X) / acceleration.X) : 0.00F;
|
|
time_max.Y = (acceleration.Y != 0) ? ((-velocity.Y) / acceleration.Y) : 0.00F;
|
|
time_max.Z = (acceleration.Z != 0) ? ((-velocity.Z) / acceleration.Z) : 0.00F;
|
|
|
|
// Is there a maxima for the X direction?
|
|
if ((time_max.X >= 0.0F) && (time_max.X < lifetime)) {
|
|
distance_maxima.X = (velocity.X * time_max.X) + ((acceleration.X * (time_max.X * time_max.X)) / 2.0F);
|
|
distance_maxima.X = fabs (distance_maxima.X);
|
|
}
|
|
|
|
// Is there a maxima for the Y direction?
|
|
if ((time_max.Y >= 0.0F) && (time_max.Y < lifetime)) {
|
|
distance_maxima.Y = (velocity.Y * time_max.Y) + ((acceleration.Y * (time_max.Y * time_max.Y)) / 2.0F);
|
|
distance_maxima.Y = fabs (distance_maxima.Y);
|
|
}
|
|
|
|
// Is there a maxima for the Z direction?
|
|
if ((time_max.Z >= 0.0F) && (time_max.Z < lifetime)) {
|
|
distance_maxima.Z = (velocity.Z * time_max.Z) + ((acceleration.Z * (time_max.Z * time_max.Z)) / 2.0F);
|
|
distance_maxima.Z = fabs (distance_maxima.Z);
|
|
}
|
|
}
|
|
|
|
distance.X = fabs (distance.X);
|
|
distance.Y = fabs (distance.Y);
|
|
distance.Z = fabs (distance.Z);
|
|
|
|
// Determine what the maximum distance convered in a single direction is
|
|
float max_dist = max (distance.X, distance.Y);
|
|
max_dist = max (max_dist, distance.Z);
|
|
max_dist = max (max_dist, distance_maxima.X);
|
|
max_dist = max (max_dist, distance_maxima.Y);
|
|
max_dist = max (max_dist, distance_maxima.Z);
|
|
|
|
Vector3 center = distance / 2.00F;
|
|
center.X = max (center.X, distance_maxima.X / 2.00F);
|
|
center.Y = max (center.Y, distance_maxima.Y / 2.00F);
|
|
center.Z = max (center.Z, distance_maxima.Z / 2.00F);
|
|
|
|
if (use_vel_rand) {
|
|
center.Set (0, 0, 0);
|
|
}
|
|
|
|
// Build a logical sphere from the emitters settings
|
|
// that should provide a good viewing distance for the emitter.
|
|
SphereClass sphere;
|
|
sphere.Center = center;
|
|
sphere.Radius = max (emitter.Get_Particle_Size () * 5, (max_dist * 3.0F) / 5.0F);
|
|
|
|
// View this sphere
|
|
Reset_Camera_To_Display_Sphere (sphere);
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Reset_Camera_To_Display_Sphere
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::Reset_Camera_To_Display_Sphere (SphereClass &sphere)
|
|
{
|
|
// Calculate a default camera distance to view this sphere
|
|
m_CameraDistance = sphere.Radius * 3.00F;
|
|
m_CameraDistance = (m_CameraDistance < 1.0F) ? 1.0F : m_CameraDistance;
|
|
|
|
// Calculate a transform that is the appropriate distance
|
|
// from the sphere center and is looking at the center
|
|
Matrix3D transform (1);
|
|
transform.Look_At (sphere.Center + Vector3 (m_CameraDistance, 0, 0), sphere.Center, 0);
|
|
|
|
// Record some variables for later use
|
|
sphereCenter = sphere.Center;
|
|
m_ObjectCenter = sphereCenter;
|
|
minZoomAdjust = m_CameraDistance / 190.0F;
|
|
rotation = Build_Quaternion (transform);
|
|
|
|
// Move the camera back to get a good view of the object
|
|
m_pCamera->Set_Transform (transform);
|
|
|
|
// Make the same adjustment for the scene light
|
|
CW3DViewDoc* doc = (CW3DViewDoc *)GetDocument();
|
|
LightClass *pSceneLight = doc->GetSceneLight ();
|
|
if ((m_pLightMesh != NULL) && (pSceneLight != NULL)) {
|
|
|
|
// Reposition the light and its 'mesh' as appropriate
|
|
transform.Make_Identity ();
|
|
transform.Set_Translation (sphereCenter);
|
|
transform.Translate (0, 0, 0.7F * m_CameraDistance);
|
|
pSceneLight->Set_Transform (transform);
|
|
m_pLightMesh->Set_Transform (transform);
|
|
|
|
// Scale the light's mesh appropriately
|
|
static float last_scale = 1.0F;
|
|
m_pLightMesh->Scale (m_CameraDistance / (14 * last_scale));
|
|
|
|
last_scale = m_CameraDistance / 14;
|
|
}
|
|
|
|
float max_dist = m_CameraDistance * 60.0F;
|
|
float min_dist = max (0.2F, minZoomAdjust / 2);
|
|
|
|
// Set the clipping planes so objects are clipped correctly
|
|
if (doc->Are_Clip_Planes_Manual () == false) {
|
|
m_pCamera->Set_Clip_Planes (min_dist, max_dist);
|
|
|
|
// Adjust the fog near clipping plane to the new value, but
|
|
// leave the far clip plane alone (since it is scene dependant
|
|
// not camera dependant).
|
|
float fog_near, fog_far;
|
|
doc->GetScene()->Get_Fog_Range(&fog_near, &fog_far);
|
|
doc->GetScene()->Set_Fog_Range(min_dist, fog_far);
|
|
doc->GetScene()->Recalculate_Fog_Planes();
|
|
}
|
|
|
|
// Reset the background camera to match the main camera
|
|
doc->GetBackObjectCamera ()->Set_Transform (transform);
|
|
doc->GetBackObjectCamera ()->Set_Position (Vector3 (0.00F, 0.00F, 0.00F));
|
|
|
|
// Update the camera distance in the status bar
|
|
CMainFrame *pCMainWnd = (CMainFrame *)::AfxGetMainWnd ();
|
|
if (pCMainWnd != NULL) {
|
|
pCMainWnd->UpdateCameraDistance (m_CameraDistance);
|
|
pCMainWnd->UpdateFrameCount (0, 0, 0);
|
|
}
|
|
|
|
// Record the sphere we are viewing for later
|
|
m_ViewedSphere = sphere;
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Reset_Camera_To_Display_Object
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::Reset_Camera_To_Display_Object (RenderObjClass &render_object)
|
|
{
|
|
// Reset the camera to get a good look at this object's bounding sphere
|
|
SphereClass sp = render_object.Get_Bounding_Sphere ();
|
|
Reset_Camera_To_Display_Sphere (sp);
|
|
|
|
// Should we update the camera's position as well?
|
|
int index = render_object.Get_Bone_Index ("CAMERA");
|
|
if (index > 0) {
|
|
|
|
// Convert the bone's transform into a camera transform
|
|
Matrix3D transform = render_object.Get_Bone_Transform (index);
|
|
if (m_CameraBonePosX) {
|
|
Matrix3D tmp = transform;
|
|
Matrix3D cam_transform (Vector3 (0, -1, 0), Vector3 (0, 0, 1), Vector3 (-1, 0, 0), Vector3 (0, 0, 0));
|
|
transform = tmp * cam_transform;
|
|
}
|
|
|
|
// Pass the new transform onto the camera
|
|
CameraClass *camera = GetCamera ();
|
|
camera->Set_Transform (transform);
|
|
}
|
|
|
|
// Update the polygon count in the main window
|
|
CMainFrame *pCMainWnd = (CMainFrame *)::AfxGetMainWnd ();
|
|
if (pCMainWnd != NULL) {
|
|
pCMainWnd->UpdatePolygonCount (render_object.Get_Num_Polys ());
|
|
}
|
|
|
|
// Load the settings in the default.dat if its in the local directory.
|
|
Load_Default_Dat ();
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Load_Default_Dat
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::Load_Default_Dat (void)
|
|
{
|
|
// Get the directory where this executable was run from
|
|
TCHAR filename[MAX_PATH];
|
|
::GetModuleFileName (NULL, filename, sizeof (filename));
|
|
|
|
// Strip the filename from the path
|
|
LPTSTR ppath = ::strrchr (filename, '\\');
|
|
if (ppath != NULL) {
|
|
ppath[0] = 0;
|
|
}
|
|
|
|
// Concat the default.dat filename onto the path
|
|
::strcat (filename, "\\default.dat");
|
|
|
|
// Does the file exist in the directory?
|
|
if (::GetFileAttributes (filename) != 0xFFFFFFFF) {
|
|
|
|
// Ask the document to load the settings from this data file
|
|
CW3DViewDoc *pCDoc = (CW3DViewDoc *)GetDocument ();
|
|
if (pCDoc != NULL) {
|
|
pCDoc->LoadSettings (filename);
|
|
}
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// OnRButtonUp
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::OnRButtonUp
|
|
(
|
|
UINT nFlags,
|
|
CPoint point
|
|
)
|
|
{
|
|
// Mouse button is up
|
|
m_bRMouseDown = FALSE;
|
|
|
|
if (m_bMouseDown) {
|
|
((CW3DViewDoc *)GetDocument())->Set_Cursor ("orbit.tga");
|
|
} else {
|
|
::SetCursor (::LoadCursor (NULL, MAKEINTRESOURCE (IDC_ARROW)));
|
|
((CW3DViewDoc *)GetDocument())->Set_Cursor ("cursor.tga");
|
|
ReleaseCapture ();
|
|
}
|
|
|
|
// Allow the base class to process this message
|
|
CView::OnRButtonUp(nFlags, point);
|
|
return ;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// OnRButtonDown
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::OnRButtonDown
|
|
(
|
|
UINT nFlags,
|
|
CPoint point
|
|
)
|
|
{
|
|
// Capture all mouse messages
|
|
SetCapture ();
|
|
|
|
// Mouse button is down
|
|
m_bRMouseDown = TRUE;
|
|
m_lastPoint = point;
|
|
|
|
if (m_bMouseDown)
|
|
{
|
|
::SetCursor (::LoadCursor (::AfxGetResourceHandle (), MAKEINTRESOURCE (IDC_CURSOR_GRAB)));
|
|
((CW3DViewDoc *)GetDocument())->Set_Cursor ("grab.tga");
|
|
}
|
|
else
|
|
{
|
|
::SetCursor (::LoadCursor (::AfxGetResourceHandle (), MAKEINTRESOURCE (IDC_CURSOR_ZOOM)));
|
|
((CW3DViewDoc *)GetDocument())->Set_Cursor ("zoom.tga");
|
|
}
|
|
|
|
// Allow the base class to process this message
|
|
CView::OnRButtonDown(nFlags, point);
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SetAnimationState
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::SetAnimationState (ANIMATION_STATE animationState)
|
|
{
|
|
// Has the state changed?
|
|
if (m_animationState != animationState)
|
|
{
|
|
switch (animationState)
|
|
{
|
|
// We want to stop the animation
|
|
case AnimStopped:
|
|
{
|
|
// Get the document so we can get our current object
|
|
CW3DViewDoc *doc = (CW3DViewDoc *)GetDocument();
|
|
ASSERT_VALID (doc);
|
|
|
|
// Get the currently displayed object
|
|
RenderObjClass *pCRenderObj = doc->GetDisplayedObject ();
|
|
if (pCRenderObj)
|
|
{
|
|
// Reset the animation to frame 0
|
|
|
|
if (doc->GetCurrentAnimation()) {
|
|
pCRenderObj->Set_Animation (doc->GetCurrentAnimation (), 0);
|
|
}
|
|
}
|
|
|
|
// Reset the animation to frame 0
|
|
doc->ResetAnimation ();
|
|
}
|
|
break;
|
|
|
|
case AnimPlaying:
|
|
{
|
|
CW3DViewDoc *doc = (CW3DViewDoc *)GetDocument ();
|
|
doc->Play_Animation_Sound ();
|
|
|
|
// Reset the frame timer
|
|
m_dwLastFrameUpdate = timeGetTime ();
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Save the new state
|
|
m_animationState = animationState;
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SetCameraPos
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::SetCameraPos (CAMERA_POS cameraPos)
|
|
{
|
|
// Get the document so we can get our current object
|
|
CW3DViewDoc *doc = (CW3DViewDoc *)GetDocument();
|
|
ASSERT_VALID (doc);
|
|
|
|
// Get the currently displayed object
|
|
RenderObjClass *pCRenderObj = doc->GetDisplayedObject ();
|
|
if (pCRenderObj)
|
|
{
|
|
SphereClass sphere = m_ViewedSphere;
|
|
|
|
m_CameraDistance = sphere.Radius * 3.00F;
|
|
m_CameraDistance = (m_CameraDistance < 1.0F) ? 1.0F : m_CameraDistance;
|
|
m_CameraDistance = (m_CameraDistance > 400.0F) ? 400.0F : m_CameraDistance;
|
|
|
|
Matrix3D transform (1);
|
|
|
|
switch (cameraPos)
|
|
{
|
|
case CameraFront:
|
|
{
|
|
transform.Look_At (sphere.Center + Vector3 (m_CameraDistance, 0.00F, 0.00F), sphere.Center, 0);
|
|
}
|
|
break;
|
|
|
|
case CameraBack:
|
|
{
|
|
transform.Look_At (sphere.Center + Vector3 (-m_CameraDistance, 0.00F, 0.00F), sphere.Center, 0);
|
|
}
|
|
break;
|
|
|
|
case CameraLeft:
|
|
{
|
|
transform.Look_At (sphere.Center + Vector3 (0.00F, -m_CameraDistance, 0.00F), sphere.Center, 0);
|
|
}
|
|
break;
|
|
|
|
case CameraRight:
|
|
{
|
|
transform.Look_At (sphere.Center + Vector3 (0.00F, m_CameraDistance, 0.00F), sphere.Center, 0);
|
|
}
|
|
break;
|
|
|
|
case CameraTop:
|
|
{
|
|
transform.Look_At (sphere.Center + Vector3 (0.00F, 0.00F, m_CameraDistance), sphere.Center, 3.1415926535F);
|
|
}
|
|
break;
|
|
|
|
case CameraBottom:
|
|
{
|
|
transform.Look_At (sphere.Center + Vector3 (0.00F, 0.00F, -m_CameraDistance), sphere.Center, 3.1415926535F);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Move the camera back to get a good view of the object
|
|
m_pCamera->Set_Transform (transform);
|
|
|
|
// Get the main window of our app
|
|
CMainFrame *pCMainWnd = (CMainFrame *)::AfxGetMainWnd ();
|
|
if (pCMainWnd != NULL)
|
|
{
|
|
CW3DViewDoc* doc = (CW3DViewDoc *)GetDocument();
|
|
|
|
doc->GetBackObjectCamera ()->Set_Transform (transform);
|
|
doc->GetBackObjectCamera ()->Set_Position (Vector3 (0.00F, 0.00F, 0.00F));
|
|
|
|
RenderObjClass *pCRenderObj = doc->GetDisplayedObject ();
|
|
if (pCRenderObj)
|
|
{
|
|
pCMainWnd->UpdatePolygonCount (pCRenderObj->Get_Num_Polys ());
|
|
}
|
|
|
|
pCMainWnd->UpdateCameraDistance(m_CameraDistance);
|
|
}
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// RotateObject
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::RotateObject (OBJECT_ROTATION rotation)
|
|
{
|
|
// Is this rotation different?
|
|
if (m_objectRotation != rotation)
|
|
{
|
|
// Save the rotation state
|
|
m_objectRotation = rotation;
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SetAllowedCameraRotation
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::SetAllowedCameraRotation (CAMERA_ROTATION cameraRotation)
|
|
{
|
|
// Store this for later reference
|
|
m_allowedCameraRotation = cameraRotation;
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ResetObject
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::ResetObject (void)
|
|
{
|
|
// Get the current document
|
|
CW3DViewDoc *doc = ::GetCurrentDocument ();
|
|
|
|
ASSERT (doc);
|
|
if (doc)
|
|
{
|
|
// Get the currently displayed object
|
|
RenderObjClass *pCRenderObj = doc->GetDisplayedObject ();
|
|
if (pCRenderObj)
|
|
{
|
|
// Reset the rotation of the object
|
|
pCRenderObj->Set_Transform (Matrix3D(1));
|
|
}
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// OnGetMinMaxInfo
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::OnGetMinMaxInfo (MINMAXINFO FAR* lpMMI)
|
|
{
|
|
CView::OnGetMinMaxInfo (lpMMI);
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Rotate_Object
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::Rotate_Object (void)
|
|
{
|
|
// Get the document to display
|
|
CW3DViewDoc *doc = (CW3DViewDoc *)GetDocument();
|
|
|
|
// Get the currently displayed object
|
|
RenderObjClass *prender_obj = doc->GetDisplayedObject ();
|
|
if (prender_obj != NULL)
|
|
{
|
|
// Get the current transform for the object
|
|
Matrix3D transform = prender_obj->Get_Transform ();
|
|
|
|
if ((m_objectRotation & RotateX) == RotateX) {
|
|
transform.Rotate_X (0.05F);
|
|
} else if ((m_objectRotation & RotateXBack) == RotateXBack) {
|
|
transform.Rotate_X (-0.05F);
|
|
}
|
|
|
|
if ((m_objectRotation & RotateY) == RotateY) {
|
|
transform.Rotate_Y (-0.05F);
|
|
} else if ((m_objectRotation & RotateYBack) == RotateYBack) {
|
|
transform.Rotate_Y (0.05F);
|
|
}
|
|
|
|
if ((m_objectRotation & RotateZ) == RotateZ) {
|
|
transform.Rotate_Z (0.05F);
|
|
} else if ((m_objectRotation & RotateZBack) == RotateZBack) {
|
|
transform.Rotate_Z (-0.05F);
|
|
}
|
|
|
|
if (!transform.Is_Orthogonal()) {
|
|
transform.Re_Orthogonalize();
|
|
}
|
|
|
|
// Set the new transform for the object
|
|
prender_obj->Set_Transform (transform);
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Rotate_Light
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::Rotate_Light (void)
|
|
{
|
|
// Get the document to display
|
|
CW3DViewDoc *doc = (CW3DViewDoc *)GetDocument();
|
|
|
|
// Get the currently displayed object
|
|
LightClass *pscene_light = doc->GetSceneLight ();
|
|
RenderObjClass *prender_obj = doc->GetDisplayedObject ();
|
|
if ((pscene_light != NULL) && (prender_obj != NULL)) {
|
|
Matrix3D rotation_matrix (1);
|
|
|
|
// Build a rotation matrix that contains the x,y,z
|
|
// rotations we want to apply to the light
|
|
if ((m_LightRotation & RotateX) == RotateX) {
|
|
rotation_matrix.Rotate_X (0.05F);
|
|
} else if ((m_LightRotation & RotateXBack) == RotateXBack) {
|
|
rotation_matrix.Rotate_X (-0.05F);
|
|
}
|
|
|
|
if ((m_LightRotation & RotateY) == RotateY) {
|
|
rotation_matrix.Rotate_Y (-0.05F);
|
|
} else if ((m_LightRotation & RotateYBack) == RotateYBack) {
|
|
rotation_matrix.Rotate_Y (0.05F);
|
|
}
|
|
|
|
if ((m_LightRotation & RotateZ) == RotateZ) {
|
|
rotation_matrix.Rotate_Z (0.05F);
|
|
} else if ((m_LightRotation & RotateZBack) == RotateZBack) {
|
|
rotation_matrix.Rotate_Z (-0.05F);
|
|
}
|
|
|
|
//
|
|
// Now, use the rotation matrix to rotate the
|
|
// light 'around' the displayed object (in its coordinate system)
|
|
//
|
|
Matrix3D coord_inv;
|
|
Matrix3D coord_to_obj;
|
|
Matrix3D coord_system = prender_obj->Get_Transform ();
|
|
coord_system.Get_Orthogonal_Inverse (coord_inv);
|
|
|
|
Matrix3D transform = pscene_light->Get_Transform ();
|
|
Matrix3D::Multiply (coord_inv, transform, &coord_to_obj);
|
|
|
|
Matrix3D::Multiply (coord_system, rotation_matrix, &transform);
|
|
Matrix3D::Multiply (transform, coord_to_obj, &transform);
|
|
|
|
// Ensure the matrix hasn't degenerated
|
|
if (!transform.Is_Orthogonal ()) {
|
|
transform.Re_Orthogonalize ();
|
|
}
|
|
|
|
// Pass the new transform onto the light
|
|
m_pLightMesh->Set_Transform (transform);
|
|
pscene_light->Set_Transform (transform);
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Set_FOV
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::Set_FOV (double hfov, double vfov, bool force)
|
|
{
|
|
CW3DViewDoc *doc = (CW3DViewDoc *)GetDocument();
|
|
|
|
if (force || (doc->Is_FOV_Manual () == false)) {
|
|
m_pCamera->Set_View_Plane (hfov, vfov);
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Reset_FOV
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::Reset_FOV (void)
|
|
{
|
|
int cx = 0;
|
|
int cy = 0;
|
|
|
|
if (m_iWindowed == 0) {
|
|
cx = g_iWidth;
|
|
cy = g_iHeight;
|
|
} else {
|
|
CRect rect;
|
|
GetClientRect (&rect);
|
|
cx = rect.Width ();
|
|
cy = rect.Height ();
|
|
}
|
|
|
|
// update the camera FOV settings
|
|
// take the larger of the two dimensions, give it the
|
|
// full desired FOV, then give the other dimension an
|
|
// FOV proportional to its relative size
|
|
double hfov,vfov;
|
|
if (cy > cx) {
|
|
|
|
vfov = (float)DEG_TO_RAD(45.0f);
|
|
hfov = (double)cx / (double)cy * vfov;
|
|
} else {
|
|
hfov = (float)DEG_TO_RAD(45.0f);
|
|
vfov = (double)cy / (double)cx * hfov;
|
|
}
|
|
|
|
// Reset the field of view
|
|
Set_FOV (hfov, vfov);
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Set_Camera_Distance
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CGraphicView::Set_Camera_Distance (float dist)
|
|
{
|
|
m_CameraDistance = dist;
|
|
|
|
//
|
|
// Reposition the camera
|
|
//
|
|
Matrix3D new_tm(1);
|
|
new_tm.Look_At (m_ViewedSphere.Center + Vector3 (m_CameraDistance, 0.00F, 0.00F), m_ViewedSphere.Center, 0);
|
|
m_pCamera->Set_Transform (new_tm);
|
|
|
|
//
|
|
// Update the status bar
|
|
//
|
|
CMainFrame *main_wnd = (CMainFrame *)::AfxGetMainWnd ();
|
|
if (main_wnd != NULL) {
|
|
main_wnd->UpdateCameraDistance (m_CameraDistance);
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|