Files
DoomRTX/neo/tools/radiant/CamWnd.cpp
T

2679 lines
71 KiB
C++

/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "precompiled.h"
#pragma hdrstop
#include "qe3.h"
#include "Radiant.h"
#include "XYWnd.h"
#include "CamWnd.h"
#include "splines.h"
#include <GL/glu.h>
#include "../../renderer/tr_local.h"
#include "../../renderer/model_local.h" // for idRenderModelMD5
#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;
worldModelDef = -1;
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()
/*
=======================================================================================================================
=======================================================================================================================
*/
INT_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
bool bPaint = true;
UpdateCaption();
idGraphicsDeviceContextHelper context(dc.m_hDC, hglrc);
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();
}
/*
=======================================================================================================================
=======================================================================================================================
*/
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);
if ((hglrc = (HGLRC)wglCreateContext(hDC)) == 0)
Error("wglCreateContext failed");
wglMakeCurrent(hDC, hglrc);
if ((g_qeglobals.d_font_list = glGenLists(256)) == 0) {
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());
// 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 (!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() );
// }
}
}
SelectObject(hDC, hOldFont);
ReleaseDC(pDC);
// indicate start of glyph display lists
glListBase(g_qeglobals.d_font_list);
// report OpenGL information
common->Printf("GL_VENDOR: %s\n", glGetString(GL_VENDOR));
common->Printf("GL_RENDERER: %s\n", glGetString(GL_RENDERER));
common->Printf("GL_VERSION: %s\n", glGetString(GL_VERSION));
common->Printf("GL_EXTENSIONS: %s\n", glGetString(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];
glGetFloatv(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(&current);
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 (int 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);
glEnable(GL_BLEND);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
}
#endif
/*
=======================================================================================================================
=======================================================================================================================
*/
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_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_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) {
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");
glBegin(GL_LINE_STRIP);
glVertex3fv(points[0].ToFloatPtr());
glVertex3fv(points[1].ToFloatPtr());
glVertex3fv(points[2].ToFloatPtr());
glVertex3fv(points[3].ToFloatPtr());
glEnd();
}
}
/*
=======================================================================================================================
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;
glLoadIdentity();
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;
glLoadMatrixf(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();
}
glViewport(0, 0, m_Camera.width, m_Camera.height);
glScissor(0, 0, m_Camera.width, m_Camera.height);
glClearColor(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) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
glDisable(GL_LIGHTING);
glMatrixMode(GL_PROJECTION);
SetProjectionMatrix();
glRotatef(-90, 1, 0, 0); // put Z going up
glRotatef(90, 0, 0, 1); // put Z going up
glRotatef(m_Camera.angles[0], 0, 1, 0);
glRotatef(-m_Camera.angles[1], 0, 0, 1);
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)) {
continue;
}
if (FilterBrush(brush)) {
continue;
}
if (renderMode) {
if (!(entityMode && brush->owner->eclass->fixedsize)) {
continue;
}
}
setGLMode(m_Camera.draw_mode);
Brush_Draw(brush);
}
//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]);
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);
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);
glEnable(GL_BLEND);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glBlendFunc(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
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);
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 (int i = 0; i < nCount; i++) {
face_t* selFace = reinterpret_cast <face_t*>(g_ptrSelectedFaces.GetAt(i));
Face_Draw(selFace);
DrawAxial(selFace);
}
}
// non-zbuffered outline
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
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_Draw(selFace);
}
}
glColor3f(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) {
glPointSize(4);
glColor3f(0, 1, 0);
glBegin(GL_POINTS);
for (i = 0; i < g_qeglobals.d_numpoints; i++) {
glVertex3fv(g_qeglobals.d_points[i].ToFloatPtr());
}
glEnd();
glPointSize(1);
}
else if (g_qeglobals.d_select_mode == sel_edge) {
float* v1, * v2;
glPointSize(4);
glColor3f(0, 0, 1);
glBegin(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();
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<bool>(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
glEnable(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();
glFinish();
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);
/*
================
Incremental editor render state
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<editorRenderWorldBrushState_t> s_editorWorldBrushStates;
static idList<editorRenderEntityState_t> s_editorEntityStates;
static idList<editorRenderBModelState_t> s_editorBModelStates;
static unsigned int EditorHashBytes(unsigned int hash, const void* data, int numBytes) {
const unsigned char* bytes = reinterpret_cast<const unsigned char*>(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<unsigned char>(*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;
}
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]);
}
}
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;
}
renderEntity_t* refent = const_cast<renderEntity_t*>(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;
}
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;
memset(&refent, 0, sizeof(refent));
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;
const bool geometryChanged =
(bmodelState->model == NULL ||
bmodelState->geometryHash != geometryHash ||
idStr::Icmp(bmodelState->modelName.c_str(), name) != 0);
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 (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);
}
}
}
else {
// use the game's epair parsing code so
// we can use the same renderEntity generation
EditorFreeBModelStateForEntity(ent);
gameEdit->ParseSpawnArgsToRenderEntity(&spawnArgs, &refent);
refent.referenceSound = ent->soundEmitter;
idRenderModelMD5* md5 = dynamic_cast<idRenderModelMD5*>(refent.hModel);
if (md5) {
idStr str;
spawnArgs.GetString("anim", "idle", str);
refent.numJoints = md5->NumJoints();
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);
}
EditorUpdateOrAddEntityModelDef(entityState, ent, &refent);
}
}
else {
EditorFreeEntityModelDef(entityState, ent);
EditorFreeBModelStateForEntity(ent);
}
// check for lightdefs
if (!(ent->eclass->nShowFlags & ECLASS_LIGHT) || spawnArgs.GetBool("start_off")) {
EditorFreeEntityLightDef(entityState, ent);
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;
EditorUpdateOrAddEntityLightDef(entityState, ent, &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<idStr*> matNames;
int i, j, k;
int indexBase = 1;
idStr lastMaterial("");
int matCount = 0;
//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 (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);
}
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, c;
//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() {
entity_t* ent;
brush_t* brush;
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;
}
// 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;
}
if (worldModel) {
renderModelManager->FreeModel(worldModel);
worldModel = NULL;
}
EditorBeginWorldBrushReconcile();
for (brush_t* brushList = &active_brushes; brushList;
brushList = (brushList == &active_brushes) ? &selected_brushes : NULL) {
for (brush = brushList->next; brush != brushList; brush = brush->next) {
EditorReconcileWorldBrushState(this, brush);
}
}
EditorPurgeUntouchedWorldBrushStates();
// Create/update/remove the light and model entities exactly the way the
// game code would, but without destroying unrelated renderWorld defs.
EditorBeginEntityReconcile();
for (ent = entities.next; ent != &entities; ent = ent->next) {
BuildEntityRenderState(ent, true);
}
EditorPurgeUntouchedEntityStates();
EditorPurgeUntouchedBModelStates();
worldDirty = false;
}
/*
===============================
CCamWnd::UpdateRenderEntities
Creates a new entity state list
returns true if a repaint is needed
===============================
*/
bool CCamWnd::UpdateRenderEntities() {
if (rebuildMode) {
return false;
}
if (!s_editorRenderWorldInitialized) {
g_qeglobals.rw->InitFromMap(NULL);
s_editorRenderWorldInitialized = true;
}
bool ret = false;
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;
}
/*
============================
CCamWnd::FreeRendererState
Frees the render state data
============================
*/
void CCamWnd::FreeRendererState() {
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 (entities.next != NULL)
{
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
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() {
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
SetProjectionMatrix();
glRotatef(-90, 1, 0, 0); // put Z going up
glRotatef(90, 0, 0, 1); // put Z going up
glRotatef(m_Camera.angles[0], 0, 1, 0);
glRotatef(-m_Camera.angles[1], 0, 0, 1);
glTranslatef(-m_Camera.origin[0], -m_Camera.origin[1], -m_Camera.origin[2]);
Cam_BuildMatrix();
if (!(entityMode || selectMode)) {
return;
}
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
globalImages->BindNull();
idVec3 color(0, 1, 0);
glColor3fv(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++;
glColor3fv(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
}
// 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);
// wglSwapBuffers(dc.m_hDC);
// create the model, using explicit normals
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, false);
//common->Printf( "front:%i back:%i\n", frontEnd, backEnd );
//glPopAttrib();
//DrawEntityData();
//wglSwapBuffers(dc.m_hDC);
// get back to the editor state
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
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;
}
}