/* =========================================================================== 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 "tools/edit_gui_common.h" #include "qe3.h" #include "Radiant.h" #include "XYWnd.h" #include "CamWnd.h" #include "splines.h" #include #include "../../renderer/tr_local.h" #include "../../renderer/model_local.h" // for idRenderModelMD5 // TODO: DG: could merge SteelStorm2 "new 3D view navigation" improvements #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif extern void DrawPathLines(); int g_axialAnchor = -1; int g_axialDest = -1; bool g_bAxialMode = false; void ValidateAxialPoints() { int faceCount = g_ptrSelectedFaces.GetSize(); if (faceCount > 0) { face_t *selFace = reinterpret_cast < face_t * > (g_ptrSelectedFaces.GetAt(0)); if (g_axialAnchor >= selFace->face_winding->GetNumPoints()) { g_axialAnchor = 0; } if (g_axialDest >= selFace->face_winding->GetNumPoints()) { g_axialDest = 0; } } else { g_axialDest = 0; g_axialAnchor = 0; } } // CCamWnd IMPLEMENT_DYNCREATE(CCamWnd, CWnd); /* ======================================================================================================================= ======================================================================================================================= */ CCamWnd::CCamWnd() { m_pXYFriend = NULL; memset(&m_Camera, 0, sizeof(camera_t)); m_pSide_select = NULL; m_bClipMode = false; worldDirty = true; worldModel = NULL; renderMode = false; rebuildMode = false; entityMode = false; animationMode = false; selectMode = false; soundMode = false; saveValid = false; Cam_Init(); } /* ======================================================================================================================= ======================================================================================================================= */ CCamWnd::~CCamWnd() { } BEGIN_MESSAGE_MAP(CCamWnd, CWnd) //{{AFX_MSG_MAP(CCamWnd) ON_WM_KEYDOWN() ON_WM_PAINT() ON_WM_DESTROY() ON_WM_CLOSE() ON_WM_MOUSEMOVE() ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_MBUTTONDOWN() ON_WM_MBUTTONUP() ON_WM_RBUTTONDOWN() ON_WM_RBUTTONUP() ON_WM_CREATE() ON_WM_SIZE() ON_WM_KEYUP() ON_WM_NCCALCSIZE() ON_WM_KILLFOCUS() ON_WM_SETFOCUS() ON_WM_TIMER() //}}AFX_MSG_MAP END_MESSAGE_MAP() /* ======================================================================================================================= ======================================================================================================================= */ LONG_PTR WINAPI CamWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { RECT rect; GetClientRect(hWnd, &rect); switch (uMsg) { case WM_KILLFOCUS: case WM_SETFOCUS: SendMessage(hWnd, WM_NCACTIVATE, uMsg == WM_SETFOCUS, 0); return 0; case WM_NCCALCSIZE: // don't let windows copy pixels DefWindowProc(hWnd, uMsg, wParam, lParam); return WVR_REDRAW; } return DefWindowProc(hWnd, uMsg, wParam, lParam); } // // ======================================================================================================================= // CCamWnd message handlers // ======================================================================================================================= // BOOL CCamWnd::PreCreateWindow(CREATESTRUCT &cs) { WNDCLASS wc; HINSTANCE hInstance = AfxGetInstanceHandle(); if (::GetClassInfo(hInstance, CAMERA_WINDOW_CLASS, &wc) == FALSE) { // Register a new class memset(&wc, 0, sizeof(wc)); // wc.style = CS_NOCLOSE | CS_OWNDC; wc.style = CS_NOCLOSE; wc.lpszClassName = CAMERA_WINDOW_CLASS; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.lpfnWndProc = CamWndProc; if (AfxRegisterClass(&wc) == FALSE) { Error("CCamWnd RegisterClass: failed"); } } cs.lpszClass = CAMERA_WINDOW_CLASS; cs.lpszName = "CAM"; if (cs.style != QE3_CHILDSTYLE) { cs.style = QE3_SPLITTER_STYLE; } BOOL bResult = CWnd::PreCreateWindow(cs); // // See if the class already exists and if not then we need to register our new // window class. // return bResult; } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { g_pParentWnd->HandleKey(nChar, nRepCnt, nFlags); } brush_t *g_pSplitList = NULL; /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::OnPaint() { CPaintDC dc(this); // device context for painting if (!qwglMakeCurrent(dc.m_hDC, win32.hGLRC)) { common->Printf("ERROR: wglMakeCurrent failed..\n "); common->Printf("Please restart " EDITOR_WINDOWTEXT " if the camera view is not working\n"); } else { QE_CheckOpenGLForErrors(); g_pSplitList = NULL; if (g_bClipMode) { if (g_Clip1.Set() && g_Clip2.Set()) { g_pSplitList = ((g_pParentWnd->ActiveXY()->GetViewType() == XZ) ? !g_bSwitch : g_bSwitch) ? &g_brBackSplits : &g_brFrontSplits; } } Cam_Draw(); QE_CheckOpenGLForErrors(); qwglSwapBuffers(dc.m_hDC); } } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::SetXYFriend(CXYWnd *pWnd) { m_pXYFriend = pWnd; } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::OnDestroy() { CWnd::OnDestroy(); } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::OnClose() { CWnd::OnClose(); } extern void Select_RotateTexture(float amt, bool absolute); /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::OnMouseMove(UINT nFlags, CPoint point) { CRect r; GetClientRect(r); if (GetCapture() == this && (GetAsyncKeyState(VK_MENU) & 0x8000) && !((GetAsyncKeyState(VK_SHIFT) & 0x8000) || (GetAsyncKeyState(VK_CONTROL) & 0x8000))) { if (GetAsyncKeyState(VK_CONTROL) & 0x8000) { Select_RotateTexture((float)point.y - m_ptLastCursor.y); } else if (GetAsyncKeyState(VK_SHIFT) & 0x8000) { Select_ScaleTexture((float)point.x - m_ptLastCursor.x, (float)m_ptLastCursor.y - point.y); } else { Select_ShiftTexture((float)point.x - m_ptLastCursor.x, (float)m_ptLastCursor.y - point.y); } } else { Cam_MouseMoved(point.x, r.bottom - 1 - point.y, nFlags); } m_ptLastCursor = point; } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::OnLButtonDown(UINT nFlags, CPoint point) { m_ptLastCursor = point; OriginalMouseDown(nFlags, point); } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::OnLButtonUp(UINT nFlags, CPoint point) { OriginalMouseUp(nFlags, point); } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::OnMButtonDown(UINT nFlags, CPoint point) { OriginalMouseDown(nFlags, point); } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::OnMButtonUp(UINT nFlags, CPoint point) { OriginalMouseUp(nFlags, point); } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::OnRButtonDown(UINT nFlags, CPoint point) { OriginalMouseDown(nFlags, point); } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::OnRButtonUp(UINT nFlags, CPoint point) { OriginalMouseUp(nFlags, point); } /* ======================================================================================================================= ======================================================================================================================= */ int CCamWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CWnd::OnCreate(lpCreateStruct) == -1) { return -1; } CDC *pDC = GetDC(); HDC hDC = pDC->GetSafeHdc(); QEW_SetupPixelFormat(hDC, true); HFONT hfont = CreateFont( 12, // logical height of font 0, // logical average character width 0, // angle of escapement 0, // base-line orientation angle 0, // font weight 0, // italic attribute flag 0, // underline attribute flag 0, // strikeout attribute flag 0, // character set identifier 0, // output precision 0, // clipping precision 0, // output quality FIXED_PITCH | FF_MODERN, // pitch and family "Lucida Console" // pointer to typeface name string ); if (!hfont) { Error("couldn't create font"); } HFONT hOldFont = (HFONT)SelectObject(hDC, hfont); //qwglMakeCurrent (hDC, win32.hGLRC); if( qwglMakeCurrent ( hDC, win32.hGLRC ) == FALSE ) { common->Warning("wglMakeCurrent failed: %d", ::GetLastError()); if ( r_multiSamples.GetInteger() > 0 ) { common->Warning("\n!!! Remember to set r_multiSamples 0 when using the editor !!!\n"); } } if ((g_qeglobals.d_font_list = qglGenLists(256)) == 0) { common->Warning( "couldn't create font dlists" ); } // create the bitmap display lists we're making images of glyphs 0 thru 255 if ( !qwglUseFontBitmaps(hDC, 0, 255, g_qeglobals.d_font_list) ) { common->Warning( "wglUseFontBitmaps failed (%d). Trying again.", GetLastError() ); // FIXME: This is really wacky, sometimes the first call fails, but calling it again makes it work // This probably indicates there's something wrong somewhere else in the code, but I'm not sure what if ( !qwglUseFontBitmaps(hDC, 0, 255, g_qeglobals.d_font_list) ) { common->Warning( "wglUseFontBitmaps failed again (%d). Trying outlines.", GetLastError() ); if (!qwglUseFontOutlines(hDC, 0, 255, g_qeglobals.d_font_list, 0.0f, 0.1f, WGL_FONT_LINES, NULL)) { common->Warning( "wglUseFontOutlines also failed (%d), no coordinate text will be visible.", GetLastError() ); } } } SelectObject(hDC, hOldFont); ReleaseDC(pDC); // indicate start of glyph display lists qglListBase(g_qeglobals.d_font_list); // report OpenGL information common->Printf("GL_VENDOR: %s\n", qglGetString(GL_VENDOR)); common->Printf("GL_RENDERER: %s\n", qglGetString(GL_RENDERER)); common->Printf("GL_VERSION: %s\n", qglGetString(GL_VERSION)); common->Printf("GL_EXTENSIONS: %s\n", qglGetString(GL_EXTENSIONS)); return 0; } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::OriginalMouseUp(UINT nFlags, CPoint point) { CRect r; GetClientRect(r); Cam_MouseUp(point.x, r.bottom - 1 - point.y, nFlags); if (!(nFlags & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON))) { ReleaseCapture(); } } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::OriginalMouseDown(UINT nFlags, CPoint point) { // if (GetTopWindow()->GetSafeHwnd() != GetSafeHwnd()) BringWindowToTop(); CRect r; GetClientRect(r); SetFocus(); SetCapture(); // if (!(GetAsyncKeyState(VK_MENU) & 0x8000)) Cam_MouseDown(point.x, r.bottom - 1 - point.y, nFlags); } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::Cam_Init() { // m_Camera.draw_mode = cd_texture; m_Camera.origin[0] = 0.0f; m_Camera.origin[1] = 20.0f; m_Camera.origin[2] = 72.0f; m_Camera.color[0] = 0.3f; m_Camera.color[1] = 0.3f; m_Camera.color[2] = 0.3f; } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::Cam_BuildMatrix() { float xa, ya; float matrix[4][4]; int i; xa = ((renderMode) ? -m_Camera.angles[PITCH] : m_Camera.angles[PITCH]) * idMath::M_DEG2RAD; ya = m_Camera.angles[YAW] * idMath::M_DEG2RAD; // the movement matrix is kept 2d m_Camera.forward[0] = cos(ya); m_Camera.forward[1] = sin(ya); m_Camera.right[0] = m_Camera.forward[1]; m_Camera.right[1] = -m_Camera.forward[0]; qglGetFloatv(GL_PROJECTION_MATRIX, &matrix[0][0]); for (i = 0; i < 3; i++) { m_Camera.vright[i] = matrix[i][0]; m_Camera.vup[i] = matrix[i][1]; m_Camera.vpn[i] = matrix[i][2]; } m_Camera.vright.Normalize(); m_Camera.vup.Normalize(); m_Camera.vpn.Normalize(); InitCull(); } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::Cam_ChangeFloor(bool up) { brush_t *b; float d, bestd, current; idVec3 start, dir; start[0] = m_Camera.origin[0]; start[1] = m_Camera.origin[1]; start[2] = HUGE_DISTANCE; dir[0] = dir[1] = 0; dir[2] = -1; current = HUGE_DISTANCE - (m_Camera.origin[2] - 72); if (up) { bestd = 0; } else { bestd = HUGE_DISTANCE*2; } for (b = active_brushes.next; b != &active_brushes; b = b->next) { if (!Brush_Ray(start, dir, b, &d)) { continue; } if (up && d < current && d > bestd) { bestd = d; } if (!up && d > current && d < bestd) { bestd = d; } } if (bestd == 0 || bestd == HUGE_DISTANCE*2) { return; } m_Camera.origin[2] += current - bestd; Sys_UpdateWindows(W_CAMERA | W_Z_OVERLAY); } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::Cam_PositionDrag() { int x, y; Sys_GetCursorPos(&x, &y); if (x != m_ptCursor.x || y != m_ptCursor.y) { x -= m_ptCursor.x; VectorMA(m_Camera.origin, x, m_Camera.vright, m_Camera.origin); y -= m_ptCursor.y; m_Camera.origin[2] -= y; SetCursorPos(m_ptCursor.x, m_ptCursor.y); Sys_UpdateWindows(W_CAMERA | W_XY_OVERLAY); } } void CCamWnd::Cam_MouseLook() { CPoint current; GetCursorPos(¤t); if (current.x != m_ptCursor.x || current.y != m_ptCursor.y) { current.x -= m_ptCursor.x; current.y -= m_ptCursor.y; m_Camera.angles[PITCH] -= (float)((float)current.y * 0.25f); m_Camera.angles[YAW] -= (float)((float)current.x * 0.25f); SetCursorPos(m_ptCursor.x, m_ptCursor.y); Cam_BuildMatrix(); } } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::Cam_MouseControl(float dtime) { int xl, xh; int yl, yh; float xf, yf; if (g_PrefsDlg.m_nMouseButtons == 2) { if (m_nCambuttonstate != (MK_RBUTTON | MK_SHIFT)) { return; } } else { if (m_nCambuttonstate != MK_RBUTTON) { return; } } xf = (float)(m_ptButton.x - m_Camera.width / 2) / (m_Camera.width / 2); yf = (float)(m_ptButton.y - m_Camera.height / 2) / (m_Camera.height / 2); xl = m_Camera.width / 3; xh = xl * 2; yl = m_Camera.height / 3; yh = yl * 2; // common->Printf("xf-%f yf-%f xl-%i xh-i% yl-i% yh-i%\n",xf,yf,xl,xh,yl,yh); #if 0 // strafe if (buttony < yl && (buttonx < xl || buttonx > xh)) { VectorMA(camera.origin, xf * dtime * g_nMoveSpeed, camera.right, camera.origin); } else #endif { xf *= 1.0f - idMath::Fabs(yf); if ( xf < 0.0f ) { xf += 0.1f; if ( xf > 0.0f ) { xf = 0.0f; } } else { xf -= 0.1f; if ( xf < 0.0f ) { xf = 0.0f; } } VectorMA(m_Camera.origin, yf * dtime * g_PrefsDlg.m_nMoveSpeed, m_Camera.forward, m_Camera.origin); m_Camera.angles[YAW] += xf * -dtime * g_PrefsDlg.m_nAngleSpeed; } Cam_BuildMatrix(); int nUpdate = (g_PrefsDlg.m_bCamXYUpdate) ? (W_CAMERA | W_XY) : (W_CAMERA); Sys_UpdateWindows(nUpdate); g_pParentWnd->PostMessage(WM_TIMER, 0, 0); } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::Cam_MouseDown(int x, int y, int buttons) { idVec3 dir; float f, r, u; int i; // calc ray direction u = (float)(y - m_Camera.height / 2) / (m_Camera.width / 2); r = (float)(x - m_Camera.width / 2) / (m_Camera.width / 2); f = 1; for (i = 0; i < 3; i++) { dir[i] = m_Camera.vpn[i] * f + m_Camera.vright[i] * r + m_Camera.vup[i] * u; } dir.Normalize(); GetCursorPos(&m_ptCursor); m_nCambuttonstate = buttons; m_ptButton.x = x; m_ptButton.y = y; // // LBUTTON = manipulate selection shift-LBUTTON = select middle button = grab // texture ctrl-middle button = set entire brush to texture ctrl-shift-middle // button = set single face to texture // int nMouseButton = g_PrefsDlg.m_nMouseButtons == 2 ? MK_RBUTTON : MK_MBUTTON; if ( (buttons == MK_LBUTTON) || (buttons == (MK_LBUTTON | MK_SHIFT)) || (buttons == (MK_LBUTTON | MK_CONTROL)) || (buttons == (MK_LBUTTON | MK_CONTROL | MK_SHIFT)) || (buttons == nMouseButton) || (buttons == (nMouseButton | MK_SHIFT)) || (buttons == (nMouseButton | MK_CONTROL)) || (buttons == (nMouseButton | MK_SHIFT | MK_CONTROL)) ) { if (g_PrefsDlg.m_nMouseButtons == 2 && (buttons == (MK_RBUTTON | MK_SHIFT))) { Cam_MouseControl( 0.1f ); } else { // something global needs to track which window is responsible for stuff Patch_SetView(W_CAMERA); Drag_Begin(x, y, buttons, m_Camera.vright, m_Camera.vup, m_Camera.origin, dir); } return; } if ( buttons == MK_RBUTTON ) { Cam_MouseControl( 0.1f ); return; } } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::Cam_MouseUp(int x, int y, int buttons) { m_nCambuttonstate = 0; Drag_MouseUp(buttons); } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::Cam_MouseMoved(int x, int y, int buttons) { m_nCambuttonstate = buttons; if (!buttons) { return; } m_ptButton.x = x; m_ptButton.y = y; if (buttons == (MK_RBUTTON | MK_CONTROL)) { Cam_PositionDrag(); Sys_UpdateWindows(W_XY | W_CAMERA | W_Z); return; } else if ( buttons == (MK_RBUTTON | MK_CONTROL | MK_SHIFT) ) { Cam_MouseLook(); Sys_UpdateWindows(W_XY | W_CAMERA | W_Z); return; } GetCursorPos(&m_ptCursor); if (buttons & (MK_LBUTTON | MK_MBUTTON)) { Drag_MouseMoved(x, y, buttons); Sys_UpdateWindows(W_XY | W_CAMERA | W_Z); } } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::InitCull() { int i; VectorSubtract(m_Camera.vpn, m_Camera.vright, m_vCull1); VectorAdd(m_Camera.vpn, m_Camera.vright, m_vCull2); for (i = 0; i < 3; i++) { if (m_vCull1[i] > 0) { m_nCullv1[i] = 3 + i; } else { m_nCullv1[i] = i; } if (m_vCull2[i] > 0) { m_nCullv2[i] = 3 + i; } else { m_nCullv2[i] = i; } } } /* ======================================================================================================================= ======================================================================================================================= */ bool CCamWnd::CullBrush(brush_t *b, bool cubicOnly) { int i; idVec3 point; float d; if ( b->forceVisibile ) { return false; } if (g_PrefsDlg.m_bCubicClipping) { float distance = g_PrefsDlg.m_nCubicScale * 64; idVec3 mid; for (i = 0; i < 3; i++) { mid[i] = (b->mins[i] + ((b->maxs[i] - b->mins[i]) / 2)); } point = mid - m_Camera.origin; if (point.Length() > distance) { return true; } } if (cubicOnly) { return false; } for (i = 0; i < 3; i++) { point[i] = b->mins[m_nCullv1[i]] - m_Camera.origin[i]; } d = DotProduct(point, m_vCull1); if (d < -1) { return true; } for (i = 0; i < 3; i++) { point[i] = b->mins[m_nCullv2[i]] - m_Camera.origin[i]; } d = DotProduct(point, m_vCull2); if (d < -1) { return true; } return false; } #if 0 /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::DrawLightRadius(brush_t *pBrush) { // if lighting int nRadius = Brush_LightRadius(pBrush); if (nRadius > 0) { Brush_SetLightColor(pBrush); qglEnable(GL_BLEND); qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE); qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); qglDisable(GL_BLEND); qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } } #endif /* ======================================================================================================================= ======================================================================================================================= */ void setGLMode(int mode) { switch (mode) { case cd_wire: qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE); globalImages->BindNull(); qglDisable(GL_BLEND); qglDisable(GL_DEPTH_TEST); qglColor3f( 1.0f, 1.0f, 1.0f ); break; case cd_solid: qglCullFace(GL_FRONT); qglEnable(GL_CULL_FACE); qglShadeModel(GL_FLAT); qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); globalImages->BindNull(); qglDisable(GL_BLEND); qglEnable(GL_DEPTH_TEST); qglDepthFunc(GL_LEQUAL); break; case cd_texture: qglCullFace(GL_FRONT); qglEnable(GL_CULL_FACE); qglShadeModel(GL_FLAT); qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); qglDisable(GL_BLEND); qglEnable(GL_DEPTH_TEST); qglDepthFunc(GL_LEQUAL); break; case cd_blend: qglCullFace(GL_FRONT); qglEnable(GL_CULL_FACE); qglShadeModel(GL_FLAT); qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); qglDisable(GL_DEPTH_TEST); qglEnable(GL_BLEND); qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; } } extern void glLabeledPoint(idVec4 &color, idVec3 &point, float size, const char *label); void DrawAxial(face_t *selFace) { if (g_bAxialMode) { idVec3 points[4]; for (int j = 0; j < selFace->face_winding->GetNumPoints(); j++) { glLabeledPoint(idVec4(1, 1, 1, 1), (*selFace->face_winding)[j].ToVec3(), 3, va("%i", j)); } ValidateAxialPoints(); points[0] = (*selFace->face_winding)[g_axialAnchor].ToVec3(); VectorMA (points[0], 1, selFace->plane, points[0]); VectorMA (points[0], 4, selFace->plane, points[1]); points[3] = (*selFace->face_winding)[g_axialDest].ToVec3(); VectorMA (points[3], 1, selFace->plane, points[3]); VectorMA (points[3], 4, selFace->plane, points[2]); glLabeledPoint(idVec4(1, 0, 0, 1), points[1], 3, "Anchor"); glLabeledPoint(idVec4(1, 1, 0, 1), points[2], 3, "Dest"); qglBegin (GL_LINE_STRIP); qglVertex3fv( points[0].ToFloatPtr() ); qglVertex3fv( points[1].ToFloatPtr() ); qglVertex3fv( points[2].ToFloatPtr() ); qglVertex3fv( points[3].ToFloatPtr() ); qglEnd(); } } /* ======================================================================================================================= Cam_Draw ======================================================================================================================= */ void CCamWnd::SetProjectionMatrix() { float xfov = 90; float yfov = 2 * atan((float)m_Camera.height / m_Camera.width) * idMath::M_RAD2DEG; #if 0 float screenaspect = (float)m_Camera.width / m_Camera.height; qglLoadIdentity(); gluPerspective(yfov, screenaspect, 2, 8192); #else float xmin, xmax, ymin, ymax; float width, height; float zNear; float projectionMatrix[16]; // // set up projection matrix // zNear = r_znear.GetFloat(); ymax = zNear * tan( yfov * idMath::PI / 360.0f ); ymin = -ymax; xmax = zNear * tan( xfov * idMath::PI / 360.0f ); xmin = -xmax; width = xmax - xmin; height = ymax - ymin; projectionMatrix[0] = 2 * zNear / width; projectionMatrix[4] = 0; projectionMatrix[8] = ( xmax + xmin ) / width; // normally 0 projectionMatrix[12] = 0; projectionMatrix[1] = 0; projectionMatrix[5] = 2 * zNear / height; projectionMatrix[9] = ( ymax + ymin ) / height; // normally 0 projectionMatrix[13] = 0; // this is the far-plane-at-infinity formulation projectionMatrix[2] = 0; projectionMatrix[6] = 0; projectionMatrix[10] = -1; projectionMatrix[14] = -2 * zNear; projectionMatrix[3] = 0; projectionMatrix[7] = 0; projectionMatrix[11] = -1; projectionMatrix[15] = 0; qglLoadMatrixf( projectionMatrix ); #endif } void CCamWnd::Cam_Draw() { brush_t *brush; face_t *face; // float yfov; int i; if (!active_brushes.next) { return; // not valid yet } // set the sound origin for both simple draw and rendered mode // the editor uses opposite pitch convention idMat3 axis = idAngles( -m_Camera.angles.pitch, m_Camera.angles.yaw, m_Camera.angles.roll ).ToMat3(); g_qeglobals.sw->PlaceListener( m_Camera.origin, axis, 0, Sys_Milliseconds(), "Undefined" ); if (renderMode) { Cam_Render(); } qglViewport(0, 0, m_Camera.width, m_Camera.height); qglScissor(0, 0, m_Camera.width, m_Camera.height); qglClearColor(g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][0], g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][1], g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][2], 0); if (!renderMode) { qglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } qglDisable(GL_LIGHTING); qglMatrixMode(GL_PROJECTION); SetProjectionMatrix(); qglRotatef(-90, 1, 0, 0); // put Z going up qglRotatef(90, 0, 0, 1); // put Z going up qglRotatef(m_Camera.angles[0], 0, 1, 0); qglRotatef(-m_Camera.angles[1], 0, 0, 1); qglTranslatef(-m_Camera.origin[0], -m_Camera.origin[1], -m_Camera.origin[2]); Cam_BuildMatrix(); for (brush = active_brushes.next; brush != &active_brushes; brush = brush->next) { if ( CullBrush(brush, false) ) { continue; } if ( FilterBrush(brush) ) { continue; } if (renderMode) { if (!(entityMode && brush->owner->eclass->fixedsize)) { continue; } } setGLMode(m_Camera.draw_mode); Brush_Draw(brush); } //qglDepthMask ( 1 ); // Ok, write now qglMatrixMode(GL_PROJECTION); qglTranslatef(g_qeglobals.d_select_translate[0],g_qeglobals.d_select_translate[1],g_qeglobals.d_select_translate[2]); brush_t *pList = (g_bClipMode && g_pSplitList) ? g_pSplitList : &selected_brushes; if (!renderMode) { // draw normally for (brush = pList->next; brush != pList; brush = brush->next) { if (brush->pPatch) { continue; } setGLMode(m_Camera.draw_mode); Brush_Draw(brush, true); } } // blend on top setGLMode(m_Camera.draw_mode); qglDisable(GL_LIGHTING); qglColor4f( g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][0],g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][1],g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][2], 0.25f ); qglEnable(GL_BLEND); qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); globalImages->BindNull(); for (brush = pList->next; brush != pList; brush = brush->next) { if (brush->pPatch || brush->modelHandle > 0) { Brush_Draw(brush, true); // DHM - Nerve:: patch display lists/models mess with the state qglEnable(GL_BLEND); qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); qglColor4f( g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][0],g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][1],g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES][2], 0.25f ); globalImages->BindNull(); continue; } if ( brush->owner->eclass->entityModel ) { continue; } for (face = brush->brush_faces; face; face = face->next) { Face_Draw(face); } } int nCount = g_ptrSelectedFaces.GetSize(); if (!renderMode) { for (i = 0; i < nCount; i++) { face_t *selFace = reinterpret_cast < face_t * > (g_ptrSelectedFaces.GetAt(i)); Face_Draw(selFace); DrawAxial(selFace); } } // non-zbuffered outline qglDisable(GL_BLEND); qglDisable(GL_DEPTH_TEST); qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE); if (renderMode) { qglColor3f(1, 0, 0); for (i = 0; i < nCount; i++) { face_t *selFace = reinterpret_cast < face_t * > (g_ptrSelectedFaces.GetAt(i)); Face_Draw(selFace); } } qglColor3f(1, 1, 1); for (brush = pList->next; brush != pList; brush = brush->next) { if (brush->pPatch || brush->modelHandle > 0) { continue; } for (face = brush->brush_faces; face; face = face->next) { Face_Draw(face); } } // edge / vertex flags if (g_qeglobals.d_select_mode == sel_vertex) { qglPointSize(4); qglColor3f(0, 1, 0); qglBegin(GL_POINTS); for (i = 0; i < g_qeglobals.d_numpoints; i++) { qglVertex3fv( g_qeglobals.d_points[i].ToFloatPtr() ); } qglEnd(); qglPointSize(1); } else if (g_qeglobals.d_select_mode == sel_edge) { float *v1, *v2; qglPointSize(4); qglColor3f(0, 0, 1); qglBegin(GL_POINTS); for (i = 0; i < g_qeglobals.d_numedges; i++) { v1 = g_qeglobals.d_points[g_qeglobals.d_edges[i].p1].ToFloatPtr(); v2 = g_qeglobals.d_points[g_qeglobals.d_edges[i].p2].ToFloatPtr(); qglVertex3f( (v1[0] + v2[0]) * 0.5f, (v1[1] + v2[1]) * 0.5f, (v1[2] + v2[2]) * 0.5f ); } qglEnd(); qglPointSize(1); } g_splineList->draw (static_cast(g_qeglobals.d_select_mode == sel_addpoint || g_qeglobals.d_select_mode == sel_editpoint)); if ( g_qeglobals.selectObject && (g_qeglobals.d_select_mode == sel_addpoint || g_qeglobals.d_select_mode == sel_editpoint) ) { g_qeglobals.selectObject->drawSelection(); } // draw pointfile qglEnable(GL_DEPTH_TEST); DrawPathLines(); if (g_qeglobals.d_pointfile_display_list) { Pointfile_Draw(); } // // bind back to the default texture so that we don't have problems elsewhere // using/modifying texture maps between contexts // globalImages->BindNull(); qglFinish(); QE_CheckOpenGLForErrors(); if (!renderMode) { // clean up any deffered tri's R_ToggleSmpFrame(); } } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::OnSize(UINT nType, int cx, int cy) { CWnd::OnSize(nType, cx, cy); CRect rect; GetClientRect(rect); m_Camera.width = rect.right; m_Camera.height = rect.bottom; InvalidateRect(NULL, false); } /* ======================================================================================================================= ======================================================================================================================= */ void CCamWnd::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) { g_pParentWnd->HandleKey(nChar, nRepCnt, nFlags, false); } // // ======================================================================================================================= // Timo brush primitive texture shifting, using camera view to select translations:: // ======================================================================================================================= // void CCamWnd::ShiftTexture_BrushPrimit(face_t *f, int x, int y) { /* idVec3 texS, texT; idVec3 viewX, viewY; int XS, XT, YS, YT; int outS, outT; #ifdef _DEBUG if (!g_qeglobals.m_bBrushPrimitMode) { common->Printf("Warning : unexpected call to CCamWnd::ShiftTexture_BrushPrimit with brush primitive mode disbaled\n"); return; } #endif // compute face axis base //ComputeAxisBase(f->plane.Normal(), texS, texT); // compute camera view vectors VectorCopy(m_Camera.vup, viewY); VectorCopy(m_Camera.vright, viewX); // compute best vectors //ComputeBest2DVector(viewX, texS, texT, XS, XT); //ComputeBest2DVector(viewY, texS, texT, YS, YT); // check this is not a degenerate case if ((XS == YS) && (XT == YT)) { #ifdef _DEBUG common->Printf("Warning : degenerate best vectors axis base in CCamWnd::ShiftTexture_BrushPrimit\n"); #endif // forget it Select_ShiftTexture_BrushPrimit(f, x, y, false); return; } // compute best fitted translation in face axis base outS = XS * x + YS * y; outT = XT * x + YT * y; // call actual texture shifting code Select_ShiftTexture_BrushPrimit(f, outS, outT, false); */ } bool IsBModel(brush_t *b) { const char *v = ValueForKey( b->owner, "model" ); if (v && *v) { const char *n = ValueForKey( b->owner, "name"); return (stricmp( n, v ) == 0); } return false; } /* ================ BuildEntityRenderState Creates or updates modelDef and lightDef for an entity ================ */ int Brush_ToTris(brush_t *brush, idTriList *tris, idMatList *mats, bool models, bool bmodel); void CCamWnd::BuildEntityRenderState( entity_t *ent, bool update) { const char *v; idDict spawnArgs; const char *name = NULL; Entity_UpdateSoundEmitter( ent ); // delete the existing def if we aren't creating a brand new world if ( !update ) { if ( ent->lightDef >= 0 ) { g_qeglobals.rw->FreeLightDef( ent->lightDef ); ent->lightDef = -1; } if ( ent->modelDef >= 0 ) { g_qeglobals.rw->FreeEntityDef( ent->modelDef ); ent->modelDef = -1; } } // if an entity doesn't have any brushes at all, don't do anything if ( ent->brushes.onext == &ent->brushes ) { return; } // if the brush isn't displayed (filtered or culled), don't do anything if (FilterBrush(ent->brushes.onext)) { return; } spawnArgs = ent->epairs; if (ent->eclass->defArgs.FindKey("model")) { spawnArgs.Set("model", ent->eclass->defArgs.GetString("model")); } // any entity can have a model name = ValueForKey( ent, "name" ); v = spawnArgs.GetString("model"); if ( v && *v ) { renderEntity_t refent; refent.referenceSound = ent->soundEmitter; if ( !stricmp( name, v ) ) { // build the model from brushes idTriList tris(1024); idMatList mats(1024); for (brush_t *b = ent->brushes.onext; b != &ent->brushes; b = b->onext) { Brush_ToTris( b, &tris, &mats, false, true); } if ( ent->modelDef >= 0 ) { g_qeglobals.rw->FreeEntityDef( ent->modelDef ); ent->modelDef = -1; } idRenderModel *bmodel = renderModelManager->FindModel( name ); if ( bmodel ) { renderModelManager->RemoveModel( bmodel ); renderModelManager->FreeModel( bmodel ); } bmodel = renderModelManager->AllocModel(); bmodel->InitEmpty( name ); // add the surfaces to the renderModel modelSurface_t surf; for ( int i = 0 ; i < tris.Num() ; i++ ) { surf.geometry = tris[i]; surf.shader = mats[i]; bmodel->AddSurface( surf ); } bmodel->FinishSurfaces(); renderModelManager->AddModel( bmodel ); // FIXME: brush entities gameEdit->ParseSpawnArgsToRenderEntity( &spawnArgs, &refent ); ent->modelDef = g_qeglobals.rw->AddEntityDef( &refent ); } else { // use the game's epair parsing code so // we can use the same renderEntity generation gameEdit->ParseSpawnArgsToRenderEntity( &spawnArgs, &refent ); idRenderModelMD5 *md5 = dynamic_cast( refent.hModel ); if (md5) { idStr str; spawnArgs.GetString("anim", "idle", str); refent.numJoints = md5->NumJoints(); if ( update && refent.joints ) { Mem_Free16( refent.joints ); } refent.joints = ( idJointMat * )Mem_Alloc16( refent.numJoints * sizeof( *refent.joints ) ); const idMD5Anim *anim = gameEdit->ANIM_GetAnimFromEntityDef(spawnArgs.GetString("classname"), str); int frame = spawnArgs.GetInt("frame") + 1; if ( frame < 1 ) { frame = 1; } const idVec3 &offset = gameEdit->ANIM_GetModelOffsetFromEntityDef( spawnArgs.GetString("classname") ); gameEdit->ANIM_CreateAnimFrame( md5, anim, refent.numJoints, refent.joints, ( frame * 1000 ) / 24, offset, false ); } if (ent->modelDef >= 0) { g_qeglobals.rw->FreeEntityDef( ent->modelDef ); } ent->modelDef = g_qeglobals.rw->AddEntityDef( &refent ); } } // check for lightdefs if (!(ent->eclass->nShowFlags & ECLASS_LIGHT)) { return; } if ( spawnArgs.GetBool( "start_off" ) ) { return; } // use the game's epair parsing code so // we can use the same renderLight generation renderLight_t lightParms; gameEdit->ParseSpawnArgsToRenderLight( &spawnArgs, &lightParms ); lightParms.referenceSound = ent->soundEmitter; if (update && ent->lightDef >= 0) { g_qeglobals.rw->UpdateLightDef( ent->lightDef, &lightParms ); } else { if (ent->lightDef >= 0) { g_qeglobals.rw->FreeLightDef(ent->lightDef); } ent->lightDef = g_qeglobals.rw->AddLightDef( &lightParms ); } } void Tris_ToOBJ(const char *outFile, idTriList *tris, idMatList *mats) { idFile *f = fileSystem->OpenExplicitFileWrite( outFile ); if ( f ) { char out[1024]; strcpy(out, outFile); StripExtension(out); idList matNames; int i, j, k; int indexBase = 1; idStr lastMaterial(""); //idStr basePath = cvarSystem->GetCVarString( "fs_savepath" ); f->Printf( "mtllib %s.mtl\n", out ); for (i = 0; i < tris->Num(); i++) { srfTriangles_t *tri = (*tris)[i]; for (j = 0; j < tri->numVerts; j++) { f->Printf( "v %f %f %f\n", tri->verts[j].xyz.x, tri->verts[j].xyz.z, -tri->verts[j].xyz.y ); } for (j = 0; j < tri->numVerts; j++) { f->Printf( "vt %f %f\n", tri->verts[j].st.x, 1.0f - tri->verts[j].st.y ); } for (j = 0; j < tri->numVerts; j++) { f->Printf( "vn %f %f %f\n", tri->verts[j].normal.x, tri->verts[j].normal.y, tri->verts[j].normal.z ); } if (stricmp( (*mats)[i]->GetName(), lastMaterial)) { lastMaterial = (*mats)[i]->GetName(); bool found = false; for (k = 0; k < matNames.Num(); k++) { if ( idStr::Icmp(matNames[k]->c_str(), lastMaterial.c_str()) == 0 ) { found = true; // f->Printf( "usemtl m%i\n", k ); f->Printf( "usemtl %s\n", lastMaterial.c_str() ); break; } } if (!found) { // f->Printf( "usemtl m%i\n", matCount++ ); f->Printf( "usemtl %s\n", lastMaterial.c_str() ); matNames.Append(new idStr(lastMaterial)); } } for (j = 0; j < tri->numIndexes; j += 3) { int i1, i2, i3; i1 = tri->indexes[j+2] + indexBase; i2 = tri->indexes[j+1] + indexBase; i3 = tri->indexes[j] + indexBase; f->Printf( "f %i/%i/%i %i/%i/%i %i/%i/%i\n", i1,i1,i1, i2,i2,i2, i3,i3,i3 ); } indexBase += tri->numVerts; } fileSystem->CloseFile( f ); strcat(out, ".mtl"); f = fileSystem->OpenExplicitFileWrite( out ); if (f) { for (k = 0; k < matNames.Num(); k++) { // This presumes the diffuse tga name matches the material name f->Printf( "newmtl %s\n\tNs 0\n\td 1\n\tillum 2\n\tKd 0 0 0 \n\tKs 0.22 0.22 0.22 \n\tKa 0 0 0 \n\tmap_Kd %s/base/%s.tga\n\n\n", matNames[k]->c_str(), "z:/d3xp", matNames[k]->c_str() ); } fileSystem->CloseFile( f ); } } } int Brush_TransformModel(brush_t *brush, idTriList *tris, idMatList *mats) { int ret = 0; if (brush->modelHandle > 0 ) { idRenderModel *model = brush->modelHandle; if (model) { float a = FloatForKey(brush->owner, "angle"); float s = 0.0f, c = 0.0f; //FIXME: support full rotation matrix bool matrix = false; if (a) { s = sin( DEG2RAD(a) ); c = cos( DEG2RAD(a) ); } idMat3 mat; if (GetMatrixForKey(brush->owner, "rotation", mat)) { matrix = true; } for (int i = 0; i < model->NumSurfaces() ; i++) { const modelSurface_t *surf = model->Surface( i ); srfTriangles_t *tri = surf->geometry; srfTriangles_t *tri2 = R_CopyStaticTriSurf(tri); for (int j = 0; j < tri2->numVerts; j++) { idVec3 v; if (matrix) { v = tri2->verts[j].xyz * brush->owner->rotation + brush->owner->origin; } else { v = tri2->verts[j].xyz; VectorAdd(v, brush->owner->origin, v); float x = v[0]; float y = v[1]; if (a) { float x2 = (((x - brush->owner->origin[0]) * c) - ((y - brush->owner->origin[1]) * s)) + brush->owner->origin[0]; float y2 = (((x - brush->owner->origin[0]) * s) + ((y - brush->owner->origin[1]) * c)) + brush->owner->origin[1]; x = x2; y = y2; } v[0] = x; v[1] = y; } tri2->verts[j].xyz = v; } tris->Append(tri2); mats->Append( surf->shader ); } return model->NumSurfaces(); } } return ret; } #define MAX_TRI_SURFACES 16384 int Brush_ToTris(brush_t *brush, idTriList *tris, idMatList *mats, bool models, bool bmodel) { int i, j; srfTriangles_t *tri; // // patches // if (brush->modelHandle > 0 ) { if (!models) { return 0; } else { return Brush_TransformModel(brush, tris, mats); } } int numSurfaces = 0; if ( brush->owner->eclass->fixedsize && !brush->entityModel) { return NULL; } if ( brush->pPatch ) { patchMesh_t *pm; int width, height; pm = brush->pPatch; // build a patch mesh idSurface_Patch *cp = new idSurface_Patch( pm->width * 6, pm->height * 6 ); cp->SetSize( pm->width, pm->height ); for ( i = 0; i < pm->width; i++ ) { for ( j = 0; j < pm->height; j++ ) { (*cp)[j*cp->GetWidth()+i].xyz = pm->ctrl(i, j).xyz; (*cp)[j*cp->GetWidth()+i].st = pm->ctrl(i, j).st; } } // subdivide it if ( pm->explicitSubdivisions ) { cp->SubdivideExplicit( pm->horzSubdivisions, pm->vertSubdivisions, true ); } else { cp->Subdivide( DEFAULT_CURVE_MAX_ERROR, DEFAULT_CURVE_MAX_ERROR, DEFAULT_CURVE_MAX_LENGTH, true ); } width = cp->GetWidth(); height = cp->GetHeight(); // convert to srfTriangles tri = R_AllocStaticTriSurf(); tri->numVerts = width * height; tri->numIndexes = 6 * ( width - 1 ) * ( height - 1 ); R_AllocStaticTriSurfVerts( tri, tri->numVerts ); R_AllocStaticTriSurfIndexes( tri, tri->numIndexes ); for ( i = 0 ; i < tri->numVerts ; i++ ) { tri->verts[i] = (*cp)[i]; if (bmodel) { tri->verts[i].xyz -= brush->owner->origin; } } tri->numIndexes = 0; for ( i = 1 ; i < width ; i++ ) { for ( j = 1 ; j < height ; j++ ) { tri->indexes[tri->numIndexes++] = ( j - 1 ) * width + i; tri->indexes[tri->numIndexes++] = ( j - 1 ) * width + i - 1; tri->indexes[tri->numIndexes++] = j * width + i - 1; tri->indexes[tri->numIndexes++] = j * width + i; tri->indexes[tri->numIndexes++] = ( j - 1 ) * width + i; tri->indexes[tri->numIndexes++] = j * width + i - 1; } } delete cp; tris->Append(tri); mats->Append(pm->d_texture); //surfaces[numSurfaces] = tri; //materials[numSurfaces] = pm->d_texture; return 1; } // // normal brush // for ( face_t *face = brush->brush_faces ; face; face = face->next ) { idWinding *w; w = face->face_winding; if (!w) { continue; // freed or degenerate face } tri = R_AllocStaticTriSurf(); tri->numVerts = w->GetNumPoints(); tri->numIndexes = ( w->GetNumPoints() - 2 ) * 3; R_AllocStaticTriSurfVerts( tri, tri->numVerts ); R_AllocStaticTriSurfIndexes( tri, tri->numIndexes ); for ( i = 0 ; i < tri->numVerts ; i++ ) { tri->verts[i].Clear(); tri->verts[i].xyz[0] = (*w)[i][0]; tri->verts[i].xyz[1] = (*w)[i][1]; tri->verts[i].xyz[2] = (*w)[i][2]; if ( bmodel ) { tri->verts[i].xyz -= brush->owner->origin; } tri->verts[i].st[0] = (*w)[i][3]; tri->verts[i].st[1] = (*w)[i][4]; tri->verts[i].normal = face->plane.Normal(); } tri->numIndexes = 0; for ( i = 2 ; i < w->GetNumPoints() ; i++ ) { tri->indexes[tri->numIndexes++] = 0; tri->indexes[tri->numIndexes++] = i-1; tri->indexes[tri->numIndexes++] = i; } tris->Append(tri); mats->Append(face->d_texture); numSurfaces++; } return numSurfaces; } void Select_ToOBJ() { int i; CFileDialog dlgFile(FALSE, "obj", NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "Wavefront object files (*.obj)|*.obj||", g_pParentWnd); if (dlgFile.DoModal() == IDOK) { idTriList tris(1024); idMatList mats(1024); for (brush_t *b = selected_brushes.next; b != &selected_brushes; b = b->next) { if ( b->hiddenBrush ) { continue; } if (FilterBrush(b)) { continue; } Brush_ToTris(b, &tris, &mats, true, false); } Tris_ToOBJ(dlgFile.GetPathName().GetBuffer(0), &tris, &mats); for( i = 0; i < tris.Num(); i++ ) { R_FreeStaticTriSurf( tris[i] ); } tris.Clear(); } } void Select_ToCM() { CFileDialog dlgFile( FALSE, "lwo, ase", NULL, 0, "(*.lwo)|*.lwo|(*.ase)|*.ase|(*.ma)|*.ma||", g_pParentWnd ); if ( dlgFile.DoModal() == IDOK ) { idMapEntity *mapEnt; idMapPrimitive *p; idStr name; name = fileSystem->OSPathToRelativePath( dlgFile.GetPathName() ); name.BackSlashesToSlashes(); mapEnt = new idMapEntity(); mapEnt->epairs.Set( "name", name.c_str() ); for ( brush_t *b = selected_brushes.next; b != &selected_brushes; b = b->next ) { if ( b->hiddenBrush ) { continue; } if ( FilterBrush( b ) ) { continue; } p = BrushToMapPrimitive( b, b->owner->origin ); if ( p ) { mapEnt->AddPrimitive( p ); } } collisionModelManager->WriteCollisionModelForMapEntity( mapEnt, name.c_str() ); delete mapEnt; } } /* ================= BuildRendererState Builds models, lightdefs, and modeldefs for the current editor data so it can be rendered by the game renderSystem ================= */ void CCamWnd::BuildRendererState() { renderEntity_t worldEntity; entity_t *ent; brush_t *brush; FreeRendererState(); // the renderWorld holds all the references and defs g_qeglobals.rw->InitFromMap( NULL ); // create the raw model for all the brushes int numBrushes = 0; int numSurfaces = 0; // the renderModel for the world holds all the geometry that isn't in an entity worldModel = renderModelManager->AllocModel(); worldModel->InitEmpty( "EditorWorldModel" ); for ( brush_t *brushList = &active_brushes ; brushList ; brushList = (brushList == &active_brushes) ? &selected_brushes : NULL ) { for (brush = brushList->next; brush != brushList; brush = brush->next) { if ( brush->hiddenBrush ) { continue; } if (FilterBrush(brush)) { continue; } if (CullBrush(brush, true)) { continue; } idTriList tris(1024); idMatList mats(1024); if (!IsBModel(brush)) { numSurfaces += Brush_ToTris( brush, &tris, &mats, false, false ); } // add the surfaces to the renderModel modelSurface_t surf; for ( int i = 0 ; i < tris.Num() ; i++ ) { surf.geometry = tris[i]; surf.shader = mats[i]; worldModel->AddSurface( surf ); } numBrushes++; } } // bound and clean the triangles worldModel->FinishSurfaces(); // the worldEntity just has the handle for the worldModel memset( &worldEntity, 0, sizeof( worldEntity ) ); worldEntity.hModel = worldModel; worldEntity.axis = mat3_default; worldEntity.shaderParms[0] = 1; worldEntity.shaderParms[1] = 1; worldEntity.shaderParms[2] = 1; worldEntity.shaderParms[3] = 1; worldModelDef = g_qeglobals.rw->AddEntityDef( &worldEntity ); // create the light and model entities exactly the way the game code would for ( ent = entities.next ; ent != &entities ; ent = ent->next ) { if ( ent->brushes.onext == &ent->brushes ) { continue; } if (CullBrush(ent->brushes.onext, true)) { continue; } if (Map_IsBrushFiltered(ent->brushes.onext)) { continue; } BuildEntityRenderState( ent, false ); } //common->Printf("Render data used %d brushes\n", numBrushes); worldDirty = false; UpdateCaption(); } /* =============================== CCamWnd::UpdateRenderEntities Creates a new entity state list returns true if a repaint is needed =============================== */ bool CCamWnd::UpdateRenderEntities() { if (rebuildMode) { return false; } bool ret = false; for ( entity_t *ent = entities.next ; ent != &entities ; ent = ent->next ) { BuildEntityRenderState( ent, (ent->lightDef != -1 || ent->modelDef != -1 || ent->soundEmitter ) ? true : false ); if (ret == false && ent->modelDef || ent->lightDef) { ret = true; } } return ret; } /* ============================ CCamWnd::FreeRendererState Frees the render state data ============================ */ void CCamWnd::FreeRendererState() { for ( entity_t *ent = entities.next ; ent != &entities ; ent = ent->next ) { if (ent->lightDef >= 0) { g_qeglobals.rw->FreeLightDef( ent->lightDef ); ent->lightDef = -1; } if (ent->modelDef >= 0) { renderEntity_t *refent = const_cast(g_qeglobals.rw->GetRenderEntity( ent->modelDef )); if ( refent ) { if ( refent->callbackData ) { Mem_Free( refent->callbackData ); refent->callbackData = NULL; } if ( refent->joints ) { Mem_Free16(refent->joints); refent->joints = NULL; } } g_qeglobals.rw->FreeEntityDef( ent->modelDef ); ent->modelDef = -1; } } if ( worldModel ) { renderModelManager->FreeModel( worldModel ); worldModel = NULL; } } /* ======================== CCamWnd::UpdateCaption updates the caption based on rendermode and whether the render mode needs updated ======================== */ void CCamWnd::UpdateCaption() { idStr strCaption; if (worldDirty) { strCaption = "*"; } // FIXME: strCaption += (renderMode) ? "RENDER" : "CAM"; if (renderMode) { strCaption += (rebuildMode) ? " (Realtime)" : ""; strCaption += (entityMode) ? " +lights" : ""; strCaption += (selectMode) ? " +selected" : ""; strCaption += (animationMode) ? " +anim" : ""; } strCaption += (soundMode) ? " +snd" : ""; SetWindowText(strCaption); } /* =========================== CCamWnd::ToggleRenderMode Toggles the render mode =========================== */ void CCamWnd::ToggleRenderMode() { renderMode ^= 1; UpdateCaption(); } /* =========================== CCamWnd::ToggleRebuildMode Toggles the rebuild mode =========================== */ void CCamWnd::ToggleRebuildMode() { rebuildMode ^= 1; UpdateCaption(); } /* =========================== CCamWnd::ToggleEntityMode Toggles the entity mode =========================== */ void CCamWnd::ToggleEntityMode() { entityMode ^= 1; UpdateCaption(); } /* =========================== CCamWnd::ToggleRenderMode Toggles the render mode =========================== */ void CCamWnd::ToggleAnimationMode() { animationMode ^= 1; if (animationMode) { SetTimer(0, 10, NULL); } else { KillTimer(0); } UpdateCaption(); } /* =========================== CCamWnd::ToggleSoundMode Toggles the sound mode =========================== */ void CCamWnd::ToggleSoundMode() { soundMode ^= 1; UpdateCaption(); for ( entity_t *ent = entities.next ; ent != &entities ; ent = ent->next ) { Entity_UpdateSoundEmitter( ent ); } } /* =========================== CCamWnd::ToggleRenderMode Toggles the render mode =========================== */ void CCamWnd::ToggleSelectMode() { selectMode ^= 1; UpdateCaption(); } /* ========================= CCamWnd::MarkWorldDirty marks the render world as dirty ========================= */ void CCamWnd::MarkWorldDirty() { worldDirty = true; UpdateCaption(); } /* ========================= CCamWnd::DrawEntityData Draws entity data ( experimental ) ========================= */ extern void glBox(idVec4 &color, idVec3 &point, float size); void CCamWnd::DrawEntityData() { qglMatrixMode( GL_MODELVIEW ); qglLoadIdentity(); qglMatrixMode( GL_PROJECTION ); qglLoadIdentity(); SetProjectionMatrix(); qglRotatef(-90, 1, 0, 0); // put Z going up qglRotatef(90, 0, 0, 1); // put Z going up qglRotatef(m_Camera.angles[0], 0, 1, 0); qglRotatef(-m_Camera.angles[1], 0, 0, 1); qglTranslatef(-m_Camera.origin[0], -m_Camera.origin[1], -m_Camera.origin[2]); Cam_BuildMatrix(); if (!(entityMode || selectMode)) { return; } qglDisable(GL_BLEND); qglDisable(GL_DEPTH_TEST); qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE); globalImages->BindNull(); idVec3 color(0, 1, 0); qglColor3fv( color.ToFloatPtr() ); brush_t *brushList = &active_brushes; int pass = 0; while (brushList) { for (brush_t *brush = brushList->next; brush != brushList; brush = brush->next) { if (CullBrush(brush, true)) { continue; } if (FilterBrush(brush)) { continue; } if ((pass == 1 && selectMode) || (entityMode && pass == 0 && brush->owner->lightDef >= 0)) { Brush_DrawXY(brush, 0, true, true); } } brushList = (brushList == &active_brushes) ? &selected_brushes : NULL; color.x = 1; color.y = 0; pass++; qglColor3fv( color.ToFloatPtr() ); } } /* ======================================================================================================================= Cam_Render This used the renderSystem to draw a fully lit view of the world ======================================================================================================================= */ void CCamWnd::Cam_Render() { renderView_t refdef; CPaintDC dc(this); // device context for painting if (!active_brushes.next) { return; // not valid yet } // DG: from SteelStorm2 // Jmarshal23 recommended to disable this to fix lighting render in the Cam window /* if (!qwglMakeCurrent(dc.m_hDC, win32.hGLRC)) { common->Printf("ERROR: wglMakeCurrent failed..\n "); common->Printf("Please restart " EDITOR_WINDOWTEXT " if the camera view is not working\n"); return; } */ // save the editor state //qglPushAttrib( GL_ALL_ATTRIB_BITS ); qglClearColor( 0.1f, 0.1f, 0.1f, 0.0f ); qglScissor( 0, 0, m_Camera.width, m_Camera.height ); qglClear( GL_COLOR_BUFFER_BIT ); // qwglSwapBuffers(dc.m_hDC); // create the model, using explicit normals if ( rebuildMode && worldDirty ) { BuildRendererState(); } // render it renderSystem->BeginFrame( m_Camera.width, m_Camera.height ); memset( &refdef, 0, sizeof( refdef ) ); refdef.vieworg = m_Camera.origin; // the editor uses opposite pitch convention refdef.viewaxis = idAngles( -m_Camera.angles.pitch, m_Camera.angles.yaw, m_Camera.angles.roll ).ToMat3(); refdef.width = SCREEN_WIDTH; refdef.height = SCREEN_HEIGHT; refdef.fov_x = 90; refdef.fov_y = 2 * atan((float)m_Camera.height / m_Camera.width) * idMath::M_RAD2DEG; // only set in animation mode to give a consistent look if (animationMode) { refdef.time = eventLoop->Milliseconds(); } g_qeglobals.rw->RenderScene( &refdef ); int frontEnd, backEnd; renderSystem->EndFrame( &frontEnd, &backEnd ); //common->Printf( "front:%i back:%i\n", frontEnd, backEnd ); //qglPopAttrib(); //DrawEntityData(); //qwglSwapBuffers(dc.m_hDC); // get back to the editor state qglMatrixMode( GL_MODELVIEW ); qglLoadIdentity(); Cam_BuildMatrix(); } void CCamWnd::OnTimer(UINT_PTR nIDEvent) { if (animationMode || nIDEvent == 1) { Sys_UpdateWindows(W_CAMERA); } if (nIDEvent == 1) { KillTimer(1); } if (!animationMode ) { KillTimer(0); } } void CCamWnd::UpdateCameraView() { if (QE_SingleBrush(true, true)) { brush_t *b = selected_brushes.next; if (b->owner->eclass->nShowFlags & ECLASS_CAMERAVIEW) { // find the entity that targets this const char *name = ValueForKey(b->owner, "name"); entity_t *ent = FindEntity("target", name); if (ent) { if (!saveValid) { saveOrg = m_Camera.origin; saveAng = m_Camera.angles; saveValid = true; } idVec3 v = b->owner->origin - ent->origin; v.Normalize(); idAngles ang = v.ToMat3().ToAngles(); ang.pitch = -ang.pitch; ang.roll = 0.0f; SetView( ent->origin, ang ); Cam_BuildMatrix(); Sys_UpdateWindows( W_CAMERA ); return; } } } if (saveValid) { SetView(saveOrg, saveAng); Cam_BuildMatrix(); Sys_UpdateWindows(W_CAMERA); saveValid = false; } }