From eacf47618f3c1a40e7e748ace3da4abeb8358e58 Mon Sep 17 00:00:00 2001 From: Justin Marshall Date: Sat, 25 Apr 2026 09:04:00 -0700 Subject: [PATCH] DoomRadiant will now cache instead of rebuild the preview window world. --- neo/tools/radiant/CamWnd.cpp | 1498 +++++++++++++++++++++++----------- 1 file changed, 1006 insertions(+), 492 deletions(-) diff --git a/neo/tools/radiant/CamWnd.cpp b/neo/tools/radiant/CamWnd.cpp index 0e4b969c..9b02d94d 100644 --- a/neo/tools/radiant/CamWnd.cpp +++ b/neo/tools/radiant/CamWnd.cpp @@ -2,9 +2,9 @@ =========================================================================== Doom 3 GPL Source Code -Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. +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?). +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 @@ -40,8 +40,8 @@ If you have questions concerning this license or the applicable additional terms #include "../../renderer/model_local.h" // for idRenderModelMD5 #ifdef _DEBUG - #define new DEBUG_NEW - #undef THIS_FILE +#define new DEBUG_NEW +#undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif extern void DrawPathLines(); @@ -53,14 +53,15 @@ 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)); + face_t* selFace = reinterpret_cast (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 { + } + else { g_axialDest = 0; g_axialAnchor = 0; } @@ -80,6 +81,7 @@ CCamWnd::CCamWnd() { m_bClipMode = false; worldDirty = true; worldModel = NULL; + worldModelDef = -1; renderMode = false; rebuildMode = false; entityMode = false; @@ -98,7 +100,7 @@ CCamWnd::~CCamWnd() { } BEGIN_MESSAGE_MAP(CCamWnd, CWnd) -//{{AFX_MSG_MAP(CCamWnd) + //{{AFX_MSG_MAP(CCamWnd) ON_WM_KEYDOWN() ON_WM_PAINT() ON_WM_DESTROY() @@ -130,14 +132,14 @@ INT_PTR WINAPI CamWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { - case WM_KILLFOCUS: - case WM_SETFOCUS: - SendMessage(hWnd, WM_NCACTIVATE, uMsg == WM_SETFOCUS, 0); - return 0; + 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; + case WM_NCCALCSIZE: // don't let windows copy pixels + DefWindowProc(hWnd, uMsg, wParam, lParam); + return WVR_REDRAW; } return DefWindowProc(hWnd, uMsg, wParam, lParam); @@ -148,7 +150,7 @@ INT_PTR WINAPI CamWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { // CCamWnd message handlers // ======================================================================================================================= // -BOOL CCamWnd::PreCreateWindow(CREATESTRUCT &cs) { +BOOL CCamWnd::PreCreateWindow(CREATESTRUCT& cs) { WNDCLASS wc; HINSTANCE hInstance = AfxGetInstanceHandle(); if (::GetClassInfo(hInstance, CAMERA_WINDOW_CLASS, &wc) == FALSE) { @@ -188,7 +190,7 @@ void CCamWnd::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { g_pParentWnd->HandleKey(nChar, nRepCnt, nFlags); } -brush_t *g_pSplitList = NULL; +brush_t* g_pSplitList = NULL; /* ======================================================================================================================= @@ -216,7 +218,7 @@ void CCamWnd::OnPaint() { ======================================================================================================================= ======================================================================================================================= */ -void CCamWnd::SetXYFriend(CXYWnd *pWnd) { +void CCamWnd::SetXYFriend(CXYWnd* pWnd) { m_pXYFriend = pWnd; } @@ -245,7 +247,7 @@ 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 (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); } @@ -321,27 +323,27 @@ int CCamWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) { return -1; } - CDC *pDC = GetDC(); + 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 - ); + 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"); @@ -352,24 +354,24 @@ int CCamWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) { if ((hglrc = (HGLRC)wglCreateContext(hDC)) == 0) Error("wglCreateContext failed"); - wglMakeCurrent (hDC, hglrc); + wglMakeCurrent(hDC, hglrc); if ((g_qeglobals.d_font_list = glGenLists(256)) == 0) { - common->Warning( "couldn't create font dlists" ); + common->Warning("couldn't create font dlists"); } // create the bitmap display lists we're making images of glyphs 0 thru 255 - if ( !wglUseFontBitmaps(hDC, 0, 255, g_qeglobals.d_font_list) ) { - common->Warning( "wglUseFontBitmaps failed (%d). Trying again.", GetLastError() ); + if (!wglUseFontBitmaps(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 ( !wglUseFontBitmaps(hDC, 0, 255, g_qeglobals.d_font_list) ) { - common->Warning( "wglUseFontBitmaps failed again (%d). Trying outlines.", GetLastError() ); + if (!wglUseFontBitmaps(hDC, 0, 255, g_qeglobals.d_font_list)) { + common->Warning("wglUseFontBitmaps failed again (%d). Trying outlines.", GetLastError()); - // if (!wglUseFontOutlines(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() ); - // } + // if (!wglUseFontOutlines(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() ); + // } } } @@ -468,7 +470,7 @@ void CCamWnd::Cam_BuildMatrix() { */ void CCamWnd::Cam_ChangeFloor(bool up) { - brush_t *b; + brush_t* b; float d, bestd, current; idVec3 start, dir; @@ -483,7 +485,7 @@ void CCamWnd::Cam_ChangeFloor(bool up) { bestd = 0; } else { - bestd = HUGE_DISTANCE*2; + bestd = HUGE_DISTANCE * 2; } for (b = active_brushes.next; b != &active_brushes; b = b->next) { @@ -500,7 +502,7 @@ void CCamWnd::Cam_ChangeFloor(bool up) { } } - if (bestd == 0 || bestd == HUGE_DISTANCE*2) { + if (bestd == 0 || bestd == HUGE_DISTANCE * 2) { return; } @@ -581,15 +583,15 @@ void CCamWnd::Cam_MouseControl(float dtime) { #endif { xf *= 1.0f - idMath::Fabs(yf); - if ( xf < 0.0f ) { + if (xf < 0.0f) { xf += 0.1f; - if ( xf > 0.0f ) { + if (xf > 0.0f) { xf = 0.0f; } } else { xf -= 0.1f; - if ( xf < 0.0f ) { + if (xf < 0.0f) { xf = 0.0f; } } @@ -637,18 +639,18 @@ void CCamWnd::Cam_MouseDown(int x, int y, int buttons) { // 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)) - ) { + ( + (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 ); + Cam_MouseControl(0.1f); } else { // something global needs to track which window is responsible for stuff @@ -659,8 +661,8 @@ void CCamWnd::Cam_MouseDown(int x, int y, int buttons) { return; } - if ( buttons == MK_RBUTTON ) { - Cam_MouseControl( 0.1f ); + if (buttons == MK_RBUTTON) { + Cam_MouseControl(0.1f); return; } } @@ -692,7 +694,7 @@ void CCamWnd::Cam_MouseMoved(int x, int y, int buttons) { Sys_UpdateWindows(W_XY | W_CAMERA | W_Z); return; } - else if ( buttons == (MK_RBUTTON | MK_CONTROL | MK_SHIFT) ) { + else if (buttons == (MK_RBUTTON | MK_CONTROL | MK_SHIFT)) { Cam_MouseLook(); Sys_UpdateWindows(W_XY | W_CAMERA | W_Z); return; @@ -737,17 +739,17 @@ void CCamWnd::InitCull() { ======================================================================================================================= ======================================================================================================================= */ -bool CCamWnd::CullBrush(brush_t *b, bool cubicOnly) { +bool CCamWnd::CullBrush(brush_t* b, bool cubicOnly) { int i; idVec3 point; float d; - if ( b->forceVisibile ) { + if (b->forceVisibile) { return false; } if (g_PrefsDlg.m_bCubicClipping) { - + float distance = g_PrefsDlg.m_nCubicScale * 64; idVec3 mid; @@ -793,7 +795,7 @@ bool CCamWnd::CullBrush(brush_t *b, bool cubicOnly) { ======================================================================================================================= ======================================================================================================================= */ -void CCamWnd::DrawLightRadius(brush_t *pBrush) { +void CCamWnd::DrawLightRadius(brush_t* pBrush) { // if lighting int nRadius = Brush_LightRadius(pBrush); if (nRadius > 0) { @@ -814,71 +816,71 @@ void CCamWnd::DrawLightRadius(brush_t *pBrush) { void setGLMode(int mode) { switch (mode) { - case cd_wire: - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - globalImages->BindNull(); - glDisable(GL_BLEND); - glDisable(GL_DEPTH_TEST); - glColor3f( 1.0f, 1.0f, 1.0f ); - break; + case cd_wire: + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + globalImages->BindNull(); + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glColor3f(1.0f, 1.0f, 1.0f); + break; - case cd_solid: - glCullFace(GL_FRONT); - glEnable(GL_CULL_FACE); - glShadeModel(GL_FLAT); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - globalImages->BindNull(); - glDisable(GL_BLEND); - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); - break; + case cd_solid: + glCullFace(GL_FRONT); + glEnable(GL_CULL_FACE); + glShadeModel(GL_FLAT); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + globalImages->BindNull(); + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + break; - case cd_texture: - glCullFace(GL_FRONT); - glEnable(GL_CULL_FACE); - glShadeModel(GL_FLAT); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glDisable(GL_BLEND); - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); - break; + case cd_texture: + glCullFace(GL_FRONT); + glEnable(GL_CULL_FACE); + glShadeModel(GL_FLAT); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + break; - case cd_blend: - glCullFace(GL_FRONT); - glEnable(GL_CULL_FACE); - glShadeModel(GL_FLAT); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glDisable(GL_DEPTH_TEST); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - break; + case cd_blend: + glCullFace(GL_FRONT); + glEnable(GL_CULL_FACE); + glShadeModel(GL_FLAT); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(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) { +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]); + 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]); + 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"); - glBegin (GL_LINE_STRIP); - glVertex3fv( points[0].ToFloatPtr() ); - glVertex3fv( points[1].ToFloatPtr() ); - glVertex3fv( points[2].ToFloatPtr() ); - glVertex3fv( points[3].ToFloatPtr() ); + glBegin(GL_LINE_STRIP); + glVertex3fv(points[0].ToFloatPtr()); + glVertex3fv(points[1].ToFloatPtr()); + glVertex3fv(points[2].ToFloatPtr()); + glVertex3fv(points[3].ToFloatPtr()); glEnd(); } } @@ -886,7 +888,7 @@ void DrawAxial(face_t *selFace) { /* ======================================================================================================================= - Cam_Draw + Cam_Draw ======================================================================================================================= */ void CCamWnd::SetProjectionMatrix() { @@ -905,12 +907,12 @@ void CCamWnd::SetProjectionMatrix() { // // set up projection matrix // - zNear = r_znear.GetFloat(); + zNear = r_znear.GetFloat(); - ymax = zNear * tan( yfov * idMath::PI / 360.0f ); + ymax = zNear * tan(yfov * idMath::PI / 360.0f); ymin = -ymax; - xmax = zNear * tan( xfov * idMath::PI / 360.0f ); + xmax = zNear * tan(xfov * idMath::PI / 360.0f); xmin = -xmax; width = xmax - xmin; @@ -918,12 +920,12 @@ void CCamWnd::SetProjectionMatrix() { projectionMatrix[0] = 2 * zNear / width; projectionMatrix[4] = 0; - projectionMatrix[8] = ( xmax + xmin ) / width; // normally 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[9] = (ymax + ymin) / height; // normally 0 projectionMatrix[13] = 0; // this is the far-plane-at-infinity formulation @@ -937,13 +939,13 @@ void CCamWnd::SetProjectionMatrix() { projectionMatrix[11] = -1; projectionMatrix[15] = 0; - glLoadMatrixf( projectionMatrix ); + glLoadMatrixf(projectionMatrix); #endif } void CCamWnd::Cam_Draw() { - brush_t *brush; - face_t *face; + brush_t* brush; + face_t* face; // float yfov; int i; @@ -954,8 +956,8 @@ void CCamWnd::Cam_Draw() { // 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" ); + 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(); @@ -982,14 +984,14 @@ void CCamWnd::Cam_Draw() { glTranslatef(-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) ) { + if (CullBrush(brush, false)) { continue; } - if ( FilterBrush(brush) ) { + if (FilterBrush(brush)) { continue; } @@ -1007,9 +1009,9 @@ void CCamWnd::Cam_Draw() { //glDepthMask ( 1 ); // Ok, write now glMatrixMode(GL_PROJECTION); - glTranslatef(g_qeglobals.d_select_translate[0],g_qeglobals.d_select_translate[1],g_qeglobals.d_select_translate[2]); + glTranslatef(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; + brush_t* pList = (g_bClipMode && g_pSplitList) ? g_pSplitList : &selected_brushes; if (!renderMode) { // draw normally @@ -1026,7 +1028,7 @@ void CCamWnd::Cam_Draw() { setGLMode(m_Camera.draw_mode); glDisable(GL_LIGHTING); - glColor4f( 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 ); + glColor4f(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); glEnable(GL_BLEND); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -1038,14 +1040,14 @@ void CCamWnd::Cam_Draw() { // DHM - Nerve:: patch display lists/models mess with the state glEnable(GL_BLEND); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glColor4f( 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 ); + glColor4f(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; - } + if (brush->owner->eclass->entityModel) { + continue; + } for (face = brush->brush_faces; face; face = face->next) { Face_Draw(face); @@ -1056,11 +1058,11 @@ void CCamWnd::Cam_Draw() { if (!renderMode) { for (int i = 0; i < nCount; i++) { - face_t *selFace = reinterpret_cast < face_t * > (g_ptrSelectedFaces.GetAt(i)); + face_t* selFace = reinterpret_cast (g_ptrSelectedFaces.GetAt(i)); Face_Draw(selFace); DrawAxial(selFace); } - } + } // non-zbuffered outline glDisable(GL_BLEND); @@ -1070,7 +1072,7 @@ void CCamWnd::Cam_Draw() { if (renderMode) { glColor3f(1, 0, 0); for (int i = 0; i < nCount; i++) { - face_t *selFace = reinterpret_cast < face_t * > (g_ptrSelectedFaces.GetAt(i)); + face_t* selFace = reinterpret_cast (g_ptrSelectedFaces.GetAt(i)); Face_Draw(selFace); } } @@ -1091,14 +1093,14 @@ void CCamWnd::Cam_Draw() { glColor3f(0, 1, 0); glBegin(GL_POINTS); for (i = 0; i < g_qeglobals.d_numpoints; i++) { - glVertex3fv( g_qeglobals.d_points[i].ToFloatPtr() ); + glVertex3fv(g_qeglobals.d_points[i].ToFloatPtr()); } glEnd(); glPointSize(1); } else if (g_qeglobals.d_select_mode == sel_edge) { - float *v1, *v2; + float* v1, * v2; glPointSize(4); glColor3f(0, 0, 1); @@ -1106,16 +1108,16 @@ void CCamWnd::Cam_Draw() { 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(); - glVertex3f( (v1[0] + v2[0]) * 0.5f, (v1[1] + v2[1]) * 0.5f, (v1[2] + v2[2]) * 0.5f ); + glVertex3f((v1[0] + v2[0]) * 0.5f, (v1[1] + v2[1]) * 0.5f, (v1[2] + v2[2]) * 0.5f); } glEnd(); glPointSize(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_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(); } @@ -1171,55 +1173,55 @@ void CCamWnd::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) { // 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); +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 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); + // 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; - } + // 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; + // 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); -*/ + // call actual texture shifting code + Select_ShiftTexture_BrushPrimit(f, outS, outT, false); + */ } -bool IsBModel(brush_t *b) { - const char *v = ValueForKey( b->owner, "model" ); +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); + const char* n = ValueForKey(b->owner, "name"); + return (stricmp(n, v) == 0); } return false; } @@ -1231,35 +1233,578 @@ BuildEntityRenderState Creates or updates modelDef and lightDef for an entity ================ */ -int Brush_ToTris(brush_t *brush, idTriList *tris, idMatList *mats, bool models, bool bmodel); +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 ); +/* +================ +Incremental editor render state - // 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; +The old editor render path rebuilt one giant EditorWorldModel and reinitialized +the renderWorld whenever worldDirty was set. That is especially expensive for +ray traced backends because every edit invalidates the entire acceleration +structure. The editor now keeps one render entity/model per world brush and +small caches for editor entities. BuildRendererState() is therefore a +reconcile pass: + - add render defs for new brushes/entities, + - update only transforms for moved brushes, + - rebuild only the model whose geometry/material hash changed, + - free only defs/models that disappeared or became filtered. +================ +*/ + +#define EDITOR_RENDER_HASH_INIT 2166136261u +#define EDITOR_RENDER_HASH_MUL 16777619u + +struct editorRenderWorldBrushState_t { + brush_t* brush; + idRenderModel* model; + int modelDef; + unsigned int geometryHash; + idVec3 origin; + bool touched; +}; + +struct editorRenderEntityState_t { + entity_t* ent; + int modelDef; + int lightDef; + bool touched; +}; + +struct editorRenderBModelState_t { + entity_t* ent; + idRenderModel* model; + idStr modelName; + unsigned int geometryHash; + bool touched; +}; + +static bool s_editorRenderWorldInitialized = false; +static idList s_editorWorldBrushStates; +static idList s_editorEntityStates; +static idList s_editorBModelStates; + +static unsigned int EditorHashBytes(unsigned int hash, const void* data, int numBytes) { + const unsigned char* bytes = reinterpret_cast(data); + for (int i = 0; i < numBytes; i++) { + hash ^= bytes[i]; + hash *= EDITOR_RENDER_HASH_MUL; + } + return hash; +} + +static unsigned int EditorHashInt(unsigned int hash, int value) { + return EditorHashBytes(hash, &value, sizeof(value)); +} + +static int EditorQuantizeFloatForHash(float value) { + const float scale = 10000.0f; + return (int)(value * scale + (value >= 0.0f ? 0.5f : -0.5f)); +} + +static unsigned int EditorHashFloat(unsigned int hash, float value) { + const int quantized = EditorQuantizeFloatForHash(value); + return EditorHashInt(hash, quantized); +} + +static unsigned int EditorHashString(unsigned int hash, const char* value) { + if (value == NULL) { + value = ""; + } + while (*value) { + hash ^= static_cast(*value++); + hash *= EDITOR_RENDER_HASH_MUL; + } + hash ^= 0; + hash *= EDITOR_RENDER_HASH_MUL; + return hash; +} + +static unsigned int EditorHashMaterial(unsigned int hash, const idMaterial* material) { + return EditorHashString(hash, material ? material->GetName() : ""); +} + +static unsigned int EditorHashVec3(unsigned int hash, const idVec3& value) { + hash = EditorHashFloat(hash, value.x); + hash = EditorHashFloat(hash, value.y); + hash = EditorHashFloat(hash, value.z); + return hash; +} + +static bool EditorVec3Changed(const idVec3& a, const idVec3& b) { + const float epsilon = 0.001f; + return idMath::Fabs(a.x - b.x) > epsilon || + idMath::Fabs(a.y - b.y) > epsilon || + idMath::Fabs(a.z - b.z) > epsilon; +} + +static void EditorBrushRenderOrigin(brush_t* brush, idVec3& origin) { + origin[0] = (brush->mins[0] + brush->maxs[0]) * 0.5f; + origin[1] = (brush->mins[1] + brush->maxs[1]) * 0.5f; + origin[2] = (brush->mins[2] + brush->maxs[2]) * 0.5f; +} + +static unsigned int EditorHashBrushGeometry(brush_t* brush, const idVec3& origin) { + unsigned int hash = EDITOR_RENDER_HASH_INIT; + + hash = EditorHashInt(hash, brush->pPatch ? 1 : 0); + hash = EditorHashInt(hash, brush->modelHandle > 0 ? 1 : 0); + hash = EditorHashInt(hash, brush->entityModel ? 1 : 0); + + if (brush->pPatch) { + patchMesh_t* pm = brush->pPatch; + + hash = EditorHashInt(hash, pm->width); + hash = EditorHashInt(hash, pm->height); + hash = EditorHashInt(hash, pm->explicitSubdivisions ? 1 : 0); + hash = EditorHashInt(hash, pm->horzSubdivisions); + hash = EditorHashInt(hash, pm->vertSubdivisions); + hash = EditorHashMaterial(hash, pm->d_texture); + + for (int i = 0; i < pm->width; i++) { + for (int j = 0; j < pm->height; j++) { + hash = EditorHashVec3(hash, pm->ctrl(i, j).xyz - origin); + hash = EditorHashFloat(hash, pm->ctrl(i, j).st.x); + hash = EditorHashFloat(hash, pm->ctrl(i, j).st.y); + } + } + return hash; + } + + for (face_t* face = brush->brush_faces; face; face = face->next) { + idWinding* w = face->face_winding; + if (!w) { + continue; } - if ( ent->modelDef >= 0 ) { - g_qeglobals.rw->FreeEntityDef( ent->modelDef ); - ent->modelDef = -1; + hash = EditorHashMaterial(hash, face->d_texture); + hash = EditorHashVec3(hash, face->plane.Normal()); + hash = EditorHashInt(hash, w->GetNumPoints()); + + for (int i = 0; i < w->GetNumPoints(); i++) { + hash = EditorHashFloat(hash, (*w)[i][0] - origin[0]); + hash = EditorHashFloat(hash, (*w)[i][1] - origin[1]); + hash = EditorHashFloat(hash, (*w)[i][2] - origin[2]); + hash = EditorHashFloat(hash, (*w)[i][3]); + hash = EditorHashFloat(hash, (*w)[i][4]); } } - // if an entity doesn't have any brushes at all, don't do anything - if ( ent->brushes.onext == &ent->brushes ) { + return hash; +} + +static unsigned int EditorHashBModelGeometry(entity_t* ent) { + unsigned int hash = EDITOR_RENDER_HASH_INIT; + hash = EditorHashString(hash, ValueForKey(ent, "name")); + + for (brush_t* brush = ent->brushes.onext; brush != &ent->brushes; brush = brush->onext) { + const unsigned int brushHash = EditorHashBrushGeometry(brush, ent->origin); + hash = EditorHashInt(hash, brushHash); + } + + return hash; +} + +static void EditorInitRenderEntityForModel(renderEntity_t& refent, idRenderModel* model, const idVec3& origin) { + memset(&refent, 0, sizeof(refent)); + refent.hModel = model; + refent.origin = origin; + refent.axis = mat3_default; + refent.shaderParms[0] = 1; + refent.shaderParms[1] = 1; + refent.shaderParms[2] = 1; + refent.shaderParms[3] = 1; +} + +static void EditorLocalizeTriSurfaces(idTriList& tris, const idVec3& origin) { + for (int i = 0; i < tris.Num(); i++) { + srfTriangles_t* tri = tris[i]; + for (int j = 0; j < tri->numVerts; j++) { + tri->verts[j].xyz -= origin; + } + } +} + +static idRenderModel* EditorBuildSingleBrushModel(brush_t* brush, const idVec3& origin) { + idTriList tris(1024); + idMatList mats(1024); + + if (Brush_ToTris(brush, &tris, &mats, false, false) <= 0 || tris.Num() <= 0) { + return NULL; + } + + EditorLocalizeTriSurfaces(tris, origin); + + idRenderModel* model = renderModelManager->AllocModel(); + model->InitEmpty(va("EditorBrushModel_%p", brush)); + + modelSurface_t surf; + for (int i = 0; i < tris.Num(); i++) { + surf.geometry = tris[i]; + surf.shader = mats[i]; + model->AddSurface(surf); + } + + model->FinishSurfaces(); + return model; +} + +static idRenderModel* EditorBuildBModel(entity_t* ent, const char* name) { + idTriList tris(1024); + idMatList mats(1024); + + for (brush_t* brush = ent->brushes.onext; brush != &ent->brushes; brush = brush->onext) { + Brush_ToTris(brush, &tris, &mats, false, true); + } + + if (tris.Num() <= 0) { + return NULL; + } + + idRenderModel* model = renderModelManager->AllocModel(); + model->InitEmpty(name); + + modelSurface_t surf; + for (int i = 0; i < tris.Num(); i++) { + surf.geometry = tris[i]; + surf.shader = mats[i]; + model->AddSurface(surf); + } + + model->FinishSurfaces(); + return model; +} + +static void EditorFreeRenderEntityAuxData(int modelDef) { + if (modelDef < 0) { return; } - // if the brush isn't displayed (filtered or culled), don't do anything - if (FilterBrush(ent->brushes.onext)) { + renderEntity_t* refent = const_cast(g_qeglobals.rw->GetRenderEntity(modelDef)); + if (!refent) { + return; + } + + if (refent->callbackData) { + Mem_Free(refent->callbackData); + refent->callbackData = NULL; + } + + if (refent->joints) { + Mem_Free16(refent->joints); + refent->joints = NULL; + } +} + +static void EditorFreeRenderEntityDef(int& modelDef) { + if (modelDef < 0) { + return; + } + EditorFreeRenderEntityAuxData(modelDef); + g_qeglobals.rw->FreeEntityDef(modelDef); + modelDef = -1; +} + +static void EditorFreeRenderLightDef(int& lightDef) { + if (lightDef < 0) { + return; + } + g_qeglobals.rw->FreeLightDef(lightDef); + lightDef = -1; +} + +static void EditorUpdateOrAddEntityDef(int& modelDef, const renderEntity_t* refent) { + if (modelDef >= 0) { + EditorFreeRenderEntityAuxData(modelDef); + g_qeglobals.rw->UpdateEntityDef(modelDef, refent); + } + else { + modelDef = g_qeglobals.rw->AddEntityDef(refent); + } +} + +static int EditorFindWorldBrushState(brush_t* brush) { + for (int i = 0; i < s_editorWorldBrushStates.Num(); i++) { + if (s_editorWorldBrushStates[i].brush == brush) { + return i; + } + } + return -1; +} + +static void EditorFreeWorldBrushState(int index) { + editorRenderWorldBrushState_t& state = s_editorWorldBrushStates[index]; + + EditorFreeRenderEntityDef(state.modelDef); + + if (state.model) { + renderModelManager->FreeModel(state.model); + state.model = NULL; + } + + s_editorWorldBrushStates.RemoveIndex(index); +} + +static bool EditorWorldBrushRenderable(CCamWnd* cam, brush_t* brush) { + if (brush->hiddenBrush) { + return false; + } + if (FilterBrush(brush)) { + return false; + } + if (cam->CullBrush(brush, true)) { + return false; + } + if (IsBModel(brush)) { + return false; + } + if (brush->modelHandle > 0) { + return false; + } + if (brush->owner->eclass->fixedsize && !brush->entityModel) { + return false; + } + return true; +} + +static void EditorReconcileWorldBrushState(CCamWnd* cam, brush_t* brush) { + if (!EditorWorldBrushRenderable(cam, brush)) { + return; + } + + idVec3 origin; + EditorBrushRenderOrigin(brush, origin); + + const unsigned int geometryHash = EditorHashBrushGeometry(brush, origin); + int index = EditorFindWorldBrushState(brush); + + if (index < 0) { + editorRenderWorldBrushState_t newState; + newState.brush = brush; + newState.model = NULL; + newState.modelDef = -1; + newState.geometryHash = 0; + newState.origin = origin; + newState.touched = true; + s_editorWorldBrushStates.Append(newState); + index = s_editorWorldBrushStates.Num() - 1; + } + + editorRenderWorldBrushState_t& state = s_editorWorldBrushStates[index]; + state.touched = true; + + const bool geometryChanged = (state.model == NULL || state.geometryHash != geometryHash); + + if (geometryChanged) { + idRenderModel* newModel = EditorBuildSingleBrushModel(brush, origin); + if (!newModel) { + EditorFreeWorldBrushState(index); + return; + } + + idRenderModel* oldModel = state.model; + state.model = newModel; + state.geometryHash = geometryHash; + + renderEntity_t refent; + EditorInitRenderEntityForModel(refent, state.model, origin); + EditorUpdateOrAddEntityDef(state.modelDef, &refent); + + if (oldModel) { + renderModelManager->FreeModel(oldModel); + } + + state.origin = origin; + return; + } + + if (EditorVec3Changed(state.origin, origin)) { + renderEntity_t refent; + EditorInitRenderEntityForModel(refent, state.model, origin); + EditorUpdateOrAddEntityDef(state.modelDef, &refent); + state.origin = origin; + } +} + +static void EditorBeginWorldBrushReconcile() { + for (int i = 0; i < s_editorWorldBrushStates.Num(); i++) { + s_editorWorldBrushStates[i].touched = false; + } +} + +static void EditorPurgeUntouchedWorldBrushStates() { + for (int i = s_editorWorldBrushStates.Num() - 1; i >= 0; i--) { + if (!s_editorWorldBrushStates[i].touched) { + EditorFreeWorldBrushState(i); + } + } +} + +static int EditorFindEntityState(entity_t* ent) { + for (int i = 0; i < s_editorEntityStates.Num(); i++) { + if (s_editorEntityStates[i].ent == ent) { + return i; + } + } + return -1; +} + +static editorRenderEntityState_t* EditorTouchEntityState(entity_t* ent) { + int index = EditorFindEntityState(ent); + + if (index < 0) { + editorRenderEntityState_t newState; + newState.ent = ent; + newState.modelDef = ent->modelDef; + newState.lightDef = ent->lightDef; + newState.touched = true; + s_editorEntityStates.Append(newState); + index = s_editorEntityStates.Num() - 1; + } + + editorRenderEntityState_t& state = s_editorEntityStates[index]; + state.touched = true; + + if (state.modelDef < 0 && ent->modelDef >= 0) { + state.modelDef = ent->modelDef; + } + if (state.lightDef < 0 && ent->lightDef >= 0) { + state.lightDef = ent->lightDef; + } + + ent->modelDef = state.modelDef; + ent->lightDef = state.lightDef; + + return &state; +} + +static void EditorFreeEntityModelDef(editorRenderEntityState_t* state, entity_t* ent) { + EditorFreeRenderEntityDef(state->modelDef); + if (ent) { + ent->modelDef = state->modelDef; + } +} + +static void EditorFreeEntityLightDef(editorRenderEntityState_t* state, entity_t* ent) { + EditorFreeRenderLightDef(state->lightDef); + if (ent) { + ent->lightDef = state->lightDef; + } +} + +static void EditorUpdateOrAddEntityModelDef(editorRenderEntityState_t* state, entity_t* ent, const renderEntity_t* refent) { + EditorUpdateOrAddEntityDef(state->modelDef, refent); + ent->modelDef = state->modelDef; +} + +static void EditorUpdateOrAddEntityLightDef(editorRenderEntityState_t* state, entity_t* ent, const renderLight_t* lightParms) { + if (state->lightDef >= 0) { + g_qeglobals.rw->UpdateLightDef(state->lightDef, lightParms); + } + else { + state->lightDef = g_qeglobals.rw->AddLightDef(lightParms); + } + ent->lightDef = state->lightDef; +} + +static void EditorBeginEntityReconcile() { + for (int i = 0; i < s_editorEntityStates.Num(); i++) { + s_editorEntityStates[i].touched = false; + } + for (int i = 0; i < s_editorBModelStates.Num(); i++) { + s_editorBModelStates[i].touched = false; + } +} + +static void EditorPurgeUntouchedEntityStates() { + for (int i = s_editorEntityStates.Num() - 1; i >= 0; i--) { + if (s_editorEntityStates[i].touched) { + continue; + } + + EditorFreeRenderEntityDef(s_editorEntityStates[i].modelDef); + EditorFreeRenderLightDef(s_editorEntityStates[i].lightDef); + s_editorEntityStates.RemoveIndex(i); + } +} + +static int EditorFindBModelState(entity_t* ent) { + for (int i = 0; i < s_editorBModelStates.Num(); i++) { + if (s_editorBModelStates[i].ent == ent) { + return i; + } + } + return -1; +} + +static editorRenderBModelState_t* EditorTouchBModelState(entity_t* ent) { + int index = EditorFindBModelState(ent); + + if (index < 0) { + editorRenderBModelState_t newState; + newState.ent = ent; + newState.model = NULL; + newState.modelName = ""; + newState.geometryHash = 0; + newState.touched = true; + s_editorBModelStates.Append(newState); + index = s_editorBModelStates.Num() - 1; + } + + s_editorBModelStates[index].touched = true; + return &s_editorBModelStates[index]; +} + +static void EditorFreeBModelState(int index) { + editorRenderBModelState_t& state = s_editorBModelStates[index]; + + if (state.model) { + renderModelManager->RemoveModel(state.model); + renderModelManager->FreeModel(state.model); + state.model = NULL; + } + + s_editorBModelStates.RemoveIndex(index); +} + +static void EditorFreeBModelStateForEntity(entity_t* ent) { + const int index = EditorFindBModelState(ent); + if (index >= 0) { + EditorFreeBModelState(index); + } +} + +static void EditorPurgeUntouchedBModelStates() { + for (int i = s_editorBModelStates.Num() - 1; i >= 0; i--) { + if (!s_editorBModelStates[i].touched) { + EditorFreeBModelState(i); + } + } +} + + +void CCamWnd::BuildEntityRenderState(entity_t* ent, bool update) { + const char* v; + idDict spawnArgs; + const char* name = NULL; + editorRenderEntityState_t* entityState = EditorTouchEntityState(ent); + + // The old code used update=false as "tear down and recreate". The + // incremental path keeps the handles and uses UpdateEntityDef / + // UpdateLightDef when possible. + (void)update; + + Entity_UpdateSoundEmitter(ent); + + // If the entity is no longer renderable, remove only this entity's defs. + if (ent->brushes.onext == &ent->brushes || + FilterBrush(ent->brushes.onext) || + CullBrush(ent->brushes.onext, true) || + Map_IsBrushFiltered(ent->brushes.onext)) { + EditorFreeEntityModelDef(entityState, ent); + EditorFreeEntityLightDef(entityState, ent); + EditorFreeBModelStateForEntity(ent); return; } @@ -1269,113 +1814,106 @@ void CCamWnd::BuildEntityRenderState( entity_t *ent, bool update) { } // any entity can have a model - name = ValueForKey( ent, "name" ); + name = ValueForKey(ent, "name"); v = spawnArgs.GetString("model"); - if ( v && *v ) { + if (v && *v) { renderEntity_t refent; + memset(&refent, 0, sizeof(refent)); - refent.referenceSound = ent->soundEmitter; + if (!stricmp(name, v)) { + // Brush model entity. Rebuild the renderModel only when the + // entity-local brush geometry/materials changed. Plain movement + // is handled by UpdateEntityDef below. + const unsigned int geometryHash = EditorHashBModelGeometry(ent); + editorRenderBModelState_t* bmodelState = EditorTouchBModelState(ent); + idRenderModel* oldModel = NULL; - if ( !stricmp( name, v ) ) { - // build the model from brushes - idTriList tris(1024); - idMatList mats(1024); + const bool geometryChanged = + (bmodelState->model == NULL || + bmodelState->geometryHash != geometryHash || + idStr::Icmp(bmodelState->modelName.c_str(), name) != 0); - for (brush_t *b = ent->brushes.onext; b != &ent->brushes; b = b->onext) { - Brush_ToTris( b, &tris, &mats, false, true); + if (geometryChanged) { + idRenderModel* newModel = EditorBuildBModel(ent, name); + if (newModel) { + oldModel = bmodelState->model; + bmodelState->model = newModel; + bmodelState->modelName = name; + bmodelState->geometryHash = geometryHash; + } + else { + EditorFreeEntityModelDef(entityState, ent); + EditorFreeBModelStateForEntity(ent); + bmodelState = NULL; + } } - if ( ent->modelDef >= 0 ) { - g_qeglobals.rw->FreeEntityDef( ent->modelDef ); - ent->modelDef = -1; + if (bmodelState && bmodelState->model) { + gameEdit->ParseSpawnArgsToRenderEntity(&spawnArgs, &refent); + refent.hModel = bmodelState->model; + refent.referenceSound = ent->soundEmitter; + EditorUpdateOrAddEntityModelDef(entityState, ent, &refent); + + if (geometryChanged) { + if (oldModel) { + renderModelManager->RemoveModel(oldModel); + renderModelManager->FreeModel(oldModel); + } + renderModelManager->AddModel(bmodelState->model); + } } - 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 { + } + 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 ); + EditorFreeBModelStateForEntity(ent); + + gameEdit->ParseSpawnArgsToRenderEntity(&spawnArgs, &refent); + refent.referenceSound = ent->soundEmitter; + + 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); + 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 ) { + 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 ); + 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 ); + + EditorUpdateOrAddEntityModelDef(entityState, ent, &refent); } } + else { + EditorFreeEntityModelDef(entityState, ent); + EditorFreeBModelStateForEntity(ent); + } // check for lightdefs - if (!(ent->eclass->nShowFlags & ECLASS_LIGHT)) { + if (!(ent->eclass->nShowFlags & ECLASS_LIGHT) || spawnArgs.GetBool("start_off")) { + EditorFreeEntityLightDef(entityState, ent); 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 ); + 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 ); - } - + EditorUpdateOrAddEntityLightDef(entityState, ent, &lightParms); } -void Tris_ToOBJ(const char *outFile, idTriList *tris, idMatList *mats) { - idFile *f = fileSystem->OpenExplicitFileWrite( outFile ); - if ( f ) { + +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); @@ -1386,77 +1924,77 @@ void Tris_ToOBJ(const char *outFile, idTriList *tris, idMatList *mats) { idStr lastMaterial(""); int matCount = 0; //idStr basePath = cvarSystem->GetCVarString( "fs_savepath" ); - f->Printf( "mtllib %s.mtl\n", out ); + f->Printf("mtllib %s.mtl\n", out); for (i = 0; i < tris->Num(); i++) { - srfTriangles_t *tri = (*tris)[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 ); + 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 ); + 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 ); + 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)) { + 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 ) { + 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() ); + 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() ); + f->Printf("usemtl %s\n", lastMaterial.c_str()); matNames.Append(new idStr(lastMaterial)); } } for (int 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 ); + 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 ); + fileSystem->CloseFile(f); strcat(out, ".mtl"); - f = fileSystem->OpenExplicitFileWrite( out ); + 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() ); + 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 ); + fileSystem->CloseFile(f); } } } -int Brush_TransformModel(brush_t *brush, idTriList *tris, idMatList *mats) { +int Brush_TransformModel(brush_t* brush, idTriList* tris, idMatList* mats) { int ret = 0; - if (brush->modelHandle > 0 ) { - idRenderModel *model = brush->modelHandle; + if (brush->modelHandle > 0) { + idRenderModel* model = brush->modelHandle; if (model) { float a = FloatForKey(brush->owner, "angle"); float s, c; //FIXME: support full rotation matrix bool matrix = false; if (a) { - s = sin( DEG2RAD(a) ); - c = cos( DEG2RAD(a) ); + s = sin(DEG2RAD(a)); + c = cos(DEG2RAD(a)); } idMat3 mat; if (GetMatrixForKey(brush->owner, "rotation", mat)) { @@ -1464,15 +2002,16 @@ int Brush_TransformModel(brush_t *brush, idTriList *tris, idMatList *mats) { } - 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 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 { + } + else { v = tri2->verts[j].xyz; VectorAdd(v, brush->owner->origin, v); float x = v[0]; @@ -1489,7 +2028,7 @@ int Brush_TransformModel(brush_t *brush, idTriList *tris, idMatList *mats) { tri2->verts[j].xyz = v; } tris->Append(tri2); - mats->Append( surf->shader ); + mats->Append(surf->shader); } return model->NumSurfaces(); } @@ -1499,47 +2038,49 @@ int Brush_TransformModel(brush_t *brush, idTriList *tris, idMatList *mats) { #define MAX_TRI_SURFACES 16384 -int Brush_ToTris(brush_t *brush, idTriList *tris, idMatList *mats, bool models, bool bmodel) { +int Brush_ToTris(brush_t* brush, idTriList* tris, idMatList* mats, bool models, bool bmodel) { int i, j; - srfTriangles_t *tri; + srfTriangles_t* tri; // // patches // - if (brush->modelHandle > 0 ) { + if (brush->modelHandle > 0) { if (!models) { return 0; - } else { + } + else { return Brush_TransformModel(brush, tris, mats); } } int numSurfaces = 0; - if ( brush->owner->eclass->fixedsize && !brush->entityModel) { + if (brush->owner->eclass->fixedsize && !brush->entityModel) { return NULL; } - if ( brush->pPatch ) { - patchMesh_t *pm; + 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; + 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 ); + 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(); @@ -1547,10 +2088,10 @@ int Brush_ToTris(brush_t *brush, idTriList *tris, idMatList *mats, bool models, // 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->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; @@ -1558,14 +2099,14 @@ int Brush_ToTris(brush_t *brush, idTriList *tris, idMatList *mats, bool models, } 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; + 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 - 1) * width + i; tri->indexes[tri->numIndexes++] = j * width + i - 1; } } @@ -1582,8 +2123,8 @@ int Brush_ToTris(brush_t *brush, idTriList *tris, idMatList *mats, bool models, // // normal brush // - for ( face_t *face = brush->brush_faces ; face; face = face->next ) { - idWinding *w; + for (face_t* face = brush->brush_faces; face; face = face->next) { + idWinding* w; w = face->face_winding; if (!w) { @@ -1592,11 +2133,11 @@ int Brush_ToTris(brush_t *brush, idTriList *tris, idMatList *mats, bool models, tri = R_AllocStaticTriSurf(); tri->numVerts = w->GetNumPoints(); - tri->numIndexes = ( w->GetNumPoints() - 2 ) * 3; - R_AllocStaticTriSurfVerts( tri, tri->numVerts ); - R_AllocStaticTriSurfIndexes( tri, tri->numIndexes ); + tri->numIndexes = (w->GetNumPoints() - 2) * 3; + R_AllocStaticTriSurfVerts(tri, tri->numVerts); + R_AllocStaticTriSurfIndexes(tri, tri->numIndexes); - for ( i = 0 ; i < tri->numVerts ; i++ ) { + for (i = 0; i < tri->numVerts; i++) { tri->verts[i].Clear(); @@ -1604,7 +2145,7 @@ int Brush_ToTris(brush_t *brush, idTriList *tris, idMatList *mats, bool models, tri->verts[i].xyz[1] = (*w)[i][1]; tri->verts[i].xyz[2] = (*w)[i][2]; - if ( bmodel ) { + if (bmodel) { tri->verts[i].xyz -= brush->owner->origin; } @@ -1615,9 +2156,9 @@ int Brush_ToTris(brush_t *brush, idTriList *tris, idMatList *mats, bool models, } tri->numIndexes = 0; - for ( i = 2 ; i < w->GetNumPoints() ; i++ ) { + for (i = 2; i < w->GetNumPoints(); i++) { tri->indexes[tri->numIndexes++] = 0; - tri->indexes[tri->numIndexes++] = i-1; + tri->indexes[tri->numIndexes++] = i - 1; tri->indexes[tri->numIndexes++] = i; } @@ -1625,7 +2166,7 @@ int Brush_ToTris(brush_t *brush, idTriList *tris, idMatList *mats, bool models, mats->Append(face->d_texture); numSurfaces++; } - + return numSurfaces; } @@ -1636,9 +2177,9 @@ void Select_ToOBJ() { idTriList tris(1024); idMatList mats(1024); - for (brush_t *b = selected_brushes.next; b != &selected_brushes; b = b->next) { + for (brush_t* b = selected_brushes.next; b != &selected_brushes; b = b->next) { - if ( b->hiddenBrush ) { + if (b->hiddenBrush) { continue; } @@ -1651,44 +2192,44 @@ void Select_ToOBJ() { Tris_ToOBJ(dlgFile.GetPathName().GetBuffer(0), &tris, &mats); - for( i = 0; i < tris.Num(); i++ ) { - R_FreeStaticTriSurf( tris[i] ); + 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 ); + CFileDialog dlgFile(FALSE, "lwo, ase", NULL, 0, "(*.lwo)|*.lwo|(*.ase)|*.ase|(*.ma)|*.ma||", g_pParentWnd); - if ( dlgFile.DoModal() == IDOK ) { - idMapEntity *mapEnt; - idMapPrimitive *p; + if (dlgFile.DoModal() == IDOK) { + idMapEntity* mapEnt; + idMapPrimitive* p; idStr name; - name = fileSystem->OSPathToRelativePath( dlgFile.GetPathName() ); + name = fileSystem->OSPathToRelativePath(dlgFile.GetPathName()); name.BackSlashesToSlashes(); mapEnt = new idMapEntity(); - mapEnt->epairs.Set( "name", name.c_str() ); + mapEnt->epairs.Set("name", name.c_str()); - for ( brush_t *b = selected_brushes.next; b != &selected_brushes; b = b->next ) { + for (brush_t* b = selected_brushes.next; b != &selected_brushes; b = b->next) { - if ( b->hiddenBrush ) { + if (b->hiddenBrush) { continue; } - if ( FilterBrush( b ) ) { + if (FilterBrush(b)) { continue; } - p = BrushToMapPrimitive( b, b->owner->origin ); - if ( p ) { - mapEnt->AddPrimitive( p ); + p = BrushToMapPrimitive(b, b->owner->origin); + if (p) { + mapEnt->AddPrimitive(p); } } - collisionModelManager->WriteCollisionModelForMapEntity( mapEnt, name.c_str() ); + collisionModelManager->WriteCollisionModelForMapEntity(mapEnt, name.c_str()); delete mapEnt; } @@ -1704,91 +2245,51 @@ so it can be rendered by the game renderSystem ================= */ void CCamWnd::BuildRendererState() { - renderEntity_t worldEntity; - entity_t *ent; - brush_t *brush; + entity_t* ent; + brush_t* brush; - FreeRendererState(); + if (!s_editorRenderWorldInitialized) { + // First build, or after FreeRendererState(). Do not do this on every + // dirty frame; it drops all defs from the renderWorld. + g_qeglobals.rw->InitFromMap(NULL); + s_editorRenderWorldInitialized = true; + } - // the renderWorld holds all the references and defs - g_qeglobals.rw->InitFromMap( NULL ); + // Compatibility cleanup for any old monolithic world model that may still + // exist from a previous build path. + if (worldModelDef >= 0) { + g_qeglobals.rw->FreeEntityDef(worldModelDef); + worldModelDef = -1; + } - // create the raw model for all the brushes - int numBrushes = 0; - int numSurfaces = 0; + if (worldModel) { + renderModelManager->FreeModel(worldModel); + worldModel = NULL; + } - // the renderModel for the world holds all the geometry that isn't in an entity - worldModel = renderModelManager->AllocModel(); - worldModel->InitEmpty( "EditorWorldModel" ); + EditorBeginWorldBrushReconcile(); - for ( brush_t *brushList = &active_brushes ; brushList ; - brushList = (brushList == &active_brushes) ? &selected_brushes : NULL ) { + 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++; + EditorReconcileWorldBrushState(this, brush); } } - // bound and clean the triangles - worldModel->FinishSurfaces(); + EditorPurgeUntouchedWorldBrushStates(); - // 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; + // Create/update/remove the light and model entities exactly the way the + // game code would, but without destroying unrelated renderWorld defs. + EditorBeginEntityReconcile(); - 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 ); + for (ent = entities.next; ent != &entities; ent = ent->next) { + BuildEntityRenderState(ent, true); } - //common->Printf("Render data used %d brushes\n", numBrushes); + EditorPurgeUntouchedEntityStates(); + EditorPurgeUntouchedBModelStates(); + worldDirty = false; } @@ -1806,13 +2307,24 @@ bool CCamWnd::UpdateRenderEntities() { return false; } + if (!s_editorRenderWorldInitialized) { + g_qeglobals.rw->InitFromMap(NULL); + s_editorRenderWorldInitialized = true; + } + 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) { + EditorBeginEntityReconcile(); + + for (entity_t* ent = entities.next; ent != &entities; ent = ent->next) { + BuildEntityRenderState(ent, true); + if (!ret && (ent->modelDef >= 0 || ent->lightDef >= 0)) { ret = true; } } + + EditorPurgeUntouchedEntityStates(); + EditorPurgeUntouchedBModelStates(); + return ret; } @@ -1825,37 +2337,40 @@ CCamWnd::FreeRendererState */ 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; - } + for (int i = s_editorEntityStates.Num() - 1; i >= 0; i--) { + EditorFreeRenderEntityDef(s_editorEntityStates[i].modelDef); + EditorFreeRenderLightDef(s_editorEntityStates[i].lightDef); + s_editorEntityStates.RemoveIndex(i); } - if ( worldModel ) { - renderModelManager->FreeModel( worldModel ); + for (entity_t* ent = entities.next; ent != &entities; ent = ent->next) { + ent->modelDef = -1; + ent->lightDef = -1; + } + + for (int i = s_editorBModelStates.Num() - 1; i >= 0; i--) { + EditorFreeBModelState(i); + } + + for (int i = s_editorWorldBrushStates.Num() - 1; i >= 0; i--) { + EditorFreeWorldBrushState(i); + } + + if (worldModelDef >= 0) { + g_qeglobals.rw->FreeEntityDef(worldModelDef); + worldModelDef = -1; + } + + if (worldModel) { + renderModelManager->FreeModel(worldModel); worldModel = NULL; } + s_editorRenderWorldInitialized = false; } + /* ======================== CCamWnd::UpdateCaption @@ -1864,7 +2379,7 @@ CCamWnd::UpdateCaption ======================== */ void CCamWnd::UpdateCaption() { - + idStr strCaption; if (worldDirty) { @@ -1928,9 +2443,10 @@ CCamWnd::ToggleRenderMode */ void CCamWnd::ToggleAnimationMode() { animationMode ^= 1; - if (animationMode) { + if (animationMode) { SetTimer(0, 10, NULL); - } else { + } + else { KillTimer(0); } UpdateCaption(); @@ -1948,8 +2464,8 @@ void CCamWnd::ToggleSoundMode() { UpdateCaption(); - for ( entity_t *ent = entities.next ; ent != &entities ; ent = ent->next ) { - Entity_UpdateSoundEmitter( ent ); + for (entity_t* ent = entities.next; ent != &entities; ent = ent->next) { + Entity_UpdateSoundEmitter(ent); } } @@ -1985,13 +2501,13 @@ CCamWnd::DrawEntityData Draws entity data ( experimental ) ========================= */ -extern void glBox(idVec4 &color, idVec3 &point, float size); +extern void glBox(idVec4& color, idVec3& point, float size); void CCamWnd::DrawEntityData() { - glMatrixMode( GL_MODELVIEW ); + glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - glMatrixMode( GL_PROJECTION ); + glMatrixMode(GL_PROJECTION); glLoadIdentity(); SetProjectionMatrix(); @@ -2013,12 +2529,12 @@ void CCamWnd::DrawEntityData() { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); globalImages->BindNull(); idVec3 color(0, 1, 0); - glColor3fv( color.ToFloatPtr() ); + glColor3fv(color.ToFloatPtr()); - brush_t *brushList = &active_brushes; + brush_t* brushList = &active_brushes; int pass = 0; while (brushList) { - for (brush_t *brush = brushList->next; brush != brushList; brush = brush->next) { + for (brush_t* brush = brushList->next; brush != brushList; brush = brush->next) { if (CullBrush(brush, true)) { continue; @@ -2037,7 +2553,7 @@ void CCamWnd::DrawEntityData() { color.x = 1; color.y = 0; pass++; - glColor3fv( color.ToFloatPtr() ); + glColor3fv(color.ToFloatPtr()); } } @@ -2045,7 +2561,7 @@ void CCamWnd::DrawEntityData() { /* ======================================================================================================================= - Cam_Render + Cam_Render This used the renderSystem to draw a fully lit view of the world ======================================================================================================================= @@ -2062,26 +2578,24 @@ void CCamWnd::Cam_Render() { // save the editor state //glPushAttrib( GL_ALL_ATTRIB_BITS ); - glClearColor( 0.1f, 0.1f, 0.1f, 0.0f ); - glScissor( 0, 0, m_Camera.width, m_Camera.height ); - glClear( GL_COLOR_BUFFER_BIT ); + glClearColor(0.1f, 0.1f, 0.1f, 0.0f); + glScissor(0, 0, m_Camera.width, m_Camera.height); + glClear(GL_COLOR_BUFFER_BIT); // wglSwapBuffers(dc.m_hDC); // create the model, using explicit normals - if ( rebuildMode && worldDirty ) { - BuildRendererState(); - } + BuildRendererState(); // render it - renderSystem->BeginFrame( m_Camera.width, m_Camera.height ); + renderSystem->BeginFrame(m_Camera.width, m_Camera.height); - memset( &refdef, 0, sizeof( refdef ) ); + 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.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; @@ -2092,19 +2606,19 @@ void CCamWnd::Cam_Render() { refdef.time = eventLoop->Milliseconds(); } - g_qeglobals.rw->RenderScene( &refdef ); + g_qeglobals.rw->RenderScene(&refdef); int frontEnd, backEnd; - renderSystem->EndFrame( &frontEnd, &backEnd, false ); -//common->Printf( "front:%i back:%i\n", frontEnd, backEnd ); + renderSystem->EndFrame(&frontEnd, &backEnd, false); + //common->Printf( "front:%i back:%i\n", frontEnd, backEnd ); - //glPopAttrib(); - //DrawEntityData(); + //glPopAttrib(); + //DrawEntityData(); - //wglSwapBuffers(dc.m_hDC); - // get back to the editor state - glMatrixMode( GL_MODELVIEW ); + //wglSwapBuffers(dc.m_hDC); + // get back to the editor state + glMatrixMode(GL_MODELVIEW); glLoadIdentity(); Cam_BuildMatrix(); } @@ -2119,7 +2633,7 @@ void CCamWnd::OnTimer(UINT_PTR nIDEvent) KillTimer(1); } - if (!animationMode ) { + if (!animationMode) { KillTimer(0); } } @@ -2127,11 +2641,11 @@ void CCamWnd::OnTimer(UINT_PTR nIDEvent) void CCamWnd::UpdateCameraView() { if (QE_SingleBrush(true, true)) { - brush_t *b = selected_brushes.next; + 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); + const char* name = ValueForKey(b->owner, "name"); + entity_t* ent = FindEntity("target", name); if (ent) { if (!saveValid) { saveOrg = m_Camera.origin; @@ -2143,9 +2657,9 @@ void CCamWnd::UpdateCameraView() { idAngles ang = v.ToMat3().ToAngles(); ang.pitch = -ang.pitch; ang.roll = 0.0f; - SetView( ent->origin, ang ); + SetView(ent->origin, ang); Cam_BuildMatrix(); - Sys_UpdateWindows( W_CAMERA ); + Sys_UpdateWindows(W_CAMERA); return; } }