+ int n1 = (xywnd->GetViewType() == XY) ? 1 : 2;
+ int n2 = (xywnd->GetViewType() == YZ) ? 1 : 0;
+ int nAngle = (xywnd->GetViewType() == XY) ? CAMERA_YAW : CAMERA_PITCH;
+ if (point[n1] || point[n2]) {
+ Vector3 angles(Camera_getAngles(camwnd));
+ angles[nAngle] = static_cast<float>( radians_to_degrees(atan2(point[n1], point[n2])));
+ Camera_setAngles(camwnd, angles);
+ }
+}
+
+/*
+ ==============
+ NewBrushDrag
+ ==============
+ */
+unsigned int NewBrushDrag_buttons()
+{
+ return RAD_LBUTTON;
+}
+
+void XYWnd::NewBrushDrag_Begin(int x, int y)
+{
+ m_NewBrushDrag = 0;
+ m_nNewBrushPressx = x;
+ m_nNewBrushPressy = y;
+
+ m_bNewBrushDrag = true;
+ GlobalUndoSystem().start();
+}
+
+void XYWnd::NewBrushDrag_End(int x, int y)
+{
+ if (m_NewBrushDrag != 0) {
+ GlobalUndoSystem().finish("brushDragNew");
+ }
+}
+
+void XYWnd::NewBrushDrag(int x, int y)
+{
+ Vector3 mins, maxs;
+ XY_ToPoint(m_nNewBrushPressx, m_nNewBrushPressy, mins);
+ XY_SnapToGrid(mins);
+ XY_ToPoint(x, y, maxs);
+ XY_SnapToGrid(maxs);
+
+ int nDim = (m_viewType == XY) ? 2 : (m_viewType == YZ) ? 0 : 1;
+
+ mins[nDim] = float_snapped(Select_getWorkZone().d_work_min[nDim], GetSnapGridSize());
+ maxs[nDim] = float_snapped(Select_getWorkZone().d_work_max[nDim], GetSnapGridSize());
+
+ if (maxs[nDim] <= mins[nDim]) {
+ maxs[nDim] = mins[nDim] + GetGridSize();
+ }
+
+ for (int i = 0; i < 3; i++) {
+ if (mins[i] == maxs[i]) {
+ return; // don't create a degenerate brush
+ }
+ if (mins[i] > maxs[i]) {
+ float temp = mins[i];
+ mins[i] = maxs[i];
+ maxs[i] = temp;
+ }
+ }
+
+ if (m_NewBrushDrag == 0) {
+ NodeSmartReference node(GlobalBrushCreator().createBrush());
+ Node_getTraversable(Map_FindOrInsertWorldspawn(g_map))->insert(node);
+
+ scene::Path brushpath(makeReference(GlobalSceneGraph().root()));
+ brushpath.push(makeReference(*Map_GetWorldspawn(g_map)));
+ brushpath.push(makeReference(node.get()));
+ selectPath(brushpath, true);
+
+ m_NewBrushDrag = node.get_pointer();
+ }
+
+ // d1223m
+ //Scene_BrushResize_Selected(GlobalSceneGraph(), aabb_for_minmax(mins, maxs), TextureBrowser_GetSelectedShader(GlobalTextureBrowser()));
+ Scene_BrushResize_Selected(GlobalSceneGraph(), aabb_for_minmax(mins, maxs),
+ g_brush_always_caulk ?
+ "textures/common/caulk" : TextureBrowser_GetSelectedShader(GlobalTextureBrowser()));
+}
+
+void entitycreate_activated(ui::Widget item)
+{
+ scene::Node *world_node = Map_FindWorldspawn(g_map);
+ const char *entity_name = gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(item))));
+
+ if (!(world_node && string_equal(entity_name, "worldspawn"))) {
+ g_pParentWnd->ActiveXY()->OnEntityCreate(entity_name);
+ } else {
+ GlobalRadiant().m_pfnMessageBox(MainFrame_getWindow(), "There's already a worldspawn in your map!"
+ "",
+ "Info",
+ eMB_OK,
+ eMB_ICONDEFAULT);
+ }
+}
+
+void EntityClassMenu_addItem(ui::Menu menu, const char *name)
+{
+ auto item = ui::MenuItem(name);
+ item.connect("activate", G_CALLBACK(entitycreate_activated), item);
+ item.show();
+ menu_add_item(menu, item);
+}
+
+class EntityClassMenuInserter : public EntityClassVisitor {
+ typedef std::pair<ui::Menu, CopiedString> MenuPair;
+ typedef std::vector<MenuPair> MenuStack;
+ MenuStack m_stack;
+ CopiedString m_previous;
+public:
+ EntityClassMenuInserter(ui::Menu menu)
+ {
+ m_stack.reserve(2);
+ m_stack.push_back(MenuPair(menu, ""));
+ }
+
+ ~EntityClassMenuInserter()
+ {
+ if (!string_empty(m_previous.c_str())) {
+ addItem(m_previous.c_str(), "");
+ }
+ }
+
+ void visit(EntityClass *e)
+ {
+ ASSERT_MESSAGE(!string_empty(e->name()), "entity-class has no name");
+ if (!string_empty(m_previous.c_str())) {
+ addItem(m_previous.c_str(), e->name());
+ }
+ m_previous = e->name();
+ }
+
+ void pushMenu(const CopiedString &name)
+ {
+ auto item = ui::MenuItem(name.c_str());
+ item.show();
+ m_stack.back().first.add(item);
+
+ auto submenu = ui::Menu(ui::New);
+ gtk_menu_item_set_submenu(item, submenu);
+
+ m_stack.push_back(MenuPair(submenu, name));
+ }
+
+ void popMenu()
+ {
+ m_stack.pop_back();
+ }
+
+ void addItem(const char *name, const char *next)
+ {
+ const char *underscore = strchr(name, '_');
+
+ if (underscore != 0 && underscore != name) {
+ bool nextEqual = string_equal_n(name, next, (underscore + 1) - name);
+ const char *parent = m_stack.back().second.c_str();
+
+ if (!string_empty(parent)
+ && string_length(parent) == std::size_t(underscore - name)
+ && string_equal_n(name, parent, underscore - name)) { // this is a child
+ } else if (nextEqual) {
+ if (m_stack.size() == 2) {
+ popMenu();
+ }
+ pushMenu(CopiedString(StringRange(name, underscore)));
+ } else if (m_stack.size() == 2) {
+ popMenu();
+ }
+ } else if (m_stack.size() == 2) {
+ popMenu();
+ }
+
+ EntityClassMenu_addItem(m_stack.back().first, name);
+ }
+};
+
+void XYWnd::OnContextMenu()
+{
+ if (g_xywindow_globals.m_bRightClick == false) {
+ return;
+ }
+
+ if (!m_mnuDrop) { // first time, load it up
+ auto menu = m_mnuDrop = ui::Menu(ui::New);
+
+ EntityClassMenuInserter inserter(menu);
+ GlobalEntityClassManager().forEach(inserter);
+ }
+
+ gtk_menu_popup(m_mnuDrop, 0, 0, 0, 0, 1, GDK_CURRENT_TIME);
+}
+
+FreezePointer g_xywnd_freezePointer;
+
+unsigned int Move_buttons()
+{
+ return RAD_RBUTTON;
+}
+
+void XYWnd_moveDelta(int x, int y, unsigned int state, void *data)
+{
+ reinterpret_cast<XYWnd *>( data )->EntityCreate_MouseMove(x, y);
+ reinterpret_cast<XYWnd *>( data )->Scroll(-x, y);
+}
+
+gboolean XYWnd_Move_focusOut(ui::Widget widget, GdkEventFocus *event, XYWnd *xywnd)
+{
+ xywnd->Move_End();
+ return FALSE;
+}
+
+void XYWnd::Move_Begin()
+{
+ if (m_move_started) {
+ Move_End();
+ }
+ m_move_started = true;
+ g_xywnd_freezePointer.freeze_pointer(m_parent ? m_parent : MainFrame_getWindow(), XYWnd_moveDelta, this);
+ m_move_focusOut = m_gl_widget.connect("focus_out_event", G_CALLBACK(XYWnd_Move_focusOut), this);
+}
+
+void XYWnd::Move_End()
+{
+ m_move_started = false;
+ g_xywnd_freezePointer.unfreeze_pointer(m_parent ? m_parent : MainFrame_getWindow());
+ g_signal_handler_disconnect(G_OBJECT(m_gl_widget), m_move_focusOut);
+}
+
+unsigned int Zoom_buttons()
+{
+ return RAD_RBUTTON | RAD_SHIFT;
+}
+
+int g_dragZoom = 0;
+
+void XYWnd_zoomDelta(int x, int y, unsigned int state, void *data)
+{
+ if (y != 0) {
+ g_dragZoom += y;
+
+ while (abs(g_dragZoom) > 8) {
+ if (g_dragZoom > 0) {
+ XYWnd_ZoomOut(reinterpret_cast<XYWnd *>( data ));
+ g_dragZoom -= 8;
+ } else {
+ XYWnd_ZoomIn(reinterpret_cast<XYWnd *>( data ));
+ g_dragZoom += 8;
+ }
+ }
+ }
+}
+
+gboolean XYWnd_Zoom_focusOut(ui::Widget widget, GdkEventFocus *event, XYWnd *xywnd)
+{
+ xywnd->Zoom_End();
+ return FALSE;
+}
+
+void XYWnd::Zoom_Begin()
+{
+ if (m_zoom_started) {
+ Zoom_End();
+ }
+ m_zoom_started = true;
+ g_dragZoom = 0;
+ g_xywnd_freezePointer.freeze_pointer(m_parent ? m_parent : MainFrame_getWindow(), XYWnd_zoomDelta, this);
+ m_zoom_focusOut = m_gl_widget.connect("focus_out_event", G_CALLBACK(XYWnd_Zoom_focusOut), this);
+}
+
+void XYWnd::Zoom_End()
+{
+ m_zoom_started = false;
+ g_xywnd_freezePointer.unfreeze_pointer(m_parent ? m_parent : MainFrame_getWindow());
+ g_signal_handler_disconnect(G_OBJECT(m_gl_widget), m_zoom_focusOut);
+}
+
+// makes sure the selected brush or camera is in view
+void XYWnd::PositionView(const Vector3 &position)
+{
+ int nDim1 = (m_viewType == YZ) ? 1 : 0;
+ int nDim2 = (m_viewType == XY) ? 1 : 2;
+
+ m_vOrigin[nDim1] = position[nDim1];
+ m_vOrigin[nDim2] = position[nDim2];
+
+ updateModelview();
+
+ XYWnd_Update(*this);
+}
+
+void XYWnd::SetViewType(VIEWTYPE viewType)
+{
+ m_viewType = viewType;
+ updateModelview();
+
+ if (m_parent) {
+ gtk_window_set_title(m_parent, ViewType_getTitle(m_viewType));
+ }
+}
+
+
+inline WindowVector WindowVector_forInteger(int x, int y)
+{
+ return WindowVector(static_cast<float>( x ), static_cast<float>( y ));
+}
+
+void XYWnd::mouseDown(const WindowVector &position, ButtonIdentifier button, ModifierFlags modifiers)
+{
+ XY_MouseDown(static_cast<int>( position.x()), static_cast<int>( position.y()),
+ buttons_for_button_and_modifiers(button, modifiers));
+}
+
+void XYWnd::XY_MouseDown(int x, int y, unsigned int buttons)
+{
+ if (buttons == Move_buttons()) {
+ Move_Begin();
+ EntityCreate_MouseDown(x, y);
+ } else if (buttons == Zoom_buttons()) {
+ Zoom_Begin();
+ } else if (ClipMode() && buttons == Clipper_buttons()) {
+ Clipper_OnLButtonDown(x, y);
+ } else if (buttons == NewBrushDrag_buttons() && GlobalSelectionSystem().countSelected() == 0) {
+ NewBrushDrag_Begin(x, y);
+ }
+ // control mbutton = move camera
+ else if (buttons == MoveCamera_buttons()) {
+ XYWnd_PositionCamera(this, x, y, *g_pParentWnd->GetCamWnd());
+ }
+ // mbutton = angle camera
+ else if (buttons == OrientCamera_buttons()) {
+ XYWnd_OrientCamera(this, x, y, *g_pParentWnd->GetCamWnd());
+ } else {
+ m_window_observer->onMouseDown(WindowVector_forInteger(x, y), button_for_flags(buttons),
+ modifiers_for_flags(buttons));
+ }
+}
+
+void XYWnd::XY_MouseUp(int x, int y, unsigned int buttons)
+{
+ if (m_move_started) {
+ Move_End();
+ EntityCreate_MouseUp(x, y);
+ } else if (m_zoom_started) {
+ Zoom_End();
+ } else if (ClipMode() && buttons == Clipper_buttons()) {
+ Clipper_OnLButtonUp(x, y);
+ } else if (m_bNewBrushDrag) {
+ m_bNewBrushDrag = false;
+ NewBrushDrag_End(x, y);
+ } else {
+ m_window_observer->onMouseUp(WindowVector_forInteger(x, y), button_for_flags(buttons),
+ modifiers_for_flags(buttons));
+ }
+}
+
+void XYWnd::XY_MouseMoved(int x, int y, unsigned int buttons)
+{
+ // rbutton = drag xy origin
+ if (m_move_started) {
+ }
+ // zoom in/out
+ else if (m_zoom_started) {
+ } else if (ClipMode() && g_pMovingClip != 0) {
+ Clipper_OnMouseMoved(x, y);
+ }
+ // lbutton without selection = drag new brush
+ else if (m_bNewBrushDrag) {
+ NewBrushDrag(x, y);
+ }
+
+ // control mbutton = move camera
+ else if (getButtonState() == MoveCamera_buttons()) {
+ XYWnd_PositionCamera(this, x, y, *g_pParentWnd->GetCamWnd());
+ }
+
+ // mbutton = angle camera
+ else if (getButtonState() == OrientCamera_buttons()) {
+ XYWnd_OrientCamera(this, x, y, *g_pParentWnd->GetCamWnd());
+ } else {
+ m_window_observer->onMouseMotion(WindowVector_forInteger(x, y), modifiers_for_flags(buttons));
+
+ m_mousePosition[0] = m_mousePosition[1] = m_mousePosition[2] = 0.0;
+ XY_ToPoint(x, y, m_mousePosition);
+ XY_SnapToGrid(m_mousePosition);
+
+ StringOutputStream status(64);
+ status << "x:: " << FloatFormat(m_mousePosition[0], 6, 1)
+ << " y:: " << FloatFormat(m_mousePosition[1], 6, 1)
+ << " z:: " << FloatFormat(m_mousePosition[2], 6, 1);
+ g_pParentWnd->SetStatusText(g_pParentWnd->m_position_status, status.c_str());
+
+ if (g_bCrossHairs) {
+ XYWnd_Update(*this);
+ }
+
+ Clipper_Crosshair_OnMouseMoved(x, y);
+ }
+}
+
+void XYWnd::EntityCreate_MouseDown(int x, int y)
+{
+ m_entityCreate = true;
+ m_entityCreate_x = x;
+ m_entityCreate_y = y;
+}
+
+void XYWnd::EntityCreate_MouseMove(int x, int y)
+{
+ if (m_entityCreate && (m_entityCreate_x != x || m_entityCreate_y != y)) {
+ m_entityCreate = false;
+ }
+}
+
+void XYWnd::EntityCreate_MouseUp(int x, int y)
+{
+ if (m_entityCreate) {
+ m_entityCreate = false;
+ OnContextMenu();
+ }
+}
+
+inline float screen_normalised(int pos, unsigned int size)
+{
+ return ((2.0f * pos) / size) - 1.0f;
+}
+
+inline float normalised_to_world(float normalised, float world_origin, float normalised2world_scale)
+{
+ return world_origin + normalised * normalised2world_scale;
+}
+
+
+// TTimo: watch it, this doesn't init one of the 3 coords
+void XYWnd::XY_ToPoint(int x, int y, Vector3 &point)
+{
+ float normalised2world_scale_x = m_nWidth / 2 / m_fScale;
+ float normalised2world_scale_y = m_nHeight / 2 / m_fScale;
+ if (m_viewType == XY) {
+ point[0] = normalised_to_world(screen_normalised(x, m_nWidth), m_vOrigin[0], normalised2world_scale_x);
+ point[1] = normalised_to_world(-screen_normalised(y, m_nHeight), m_vOrigin[1], normalised2world_scale_y);
+ } else if (m_viewType == YZ) {
+ point[1] = normalised_to_world(screen_normalised(x, m_nWidth), m_vOrigin[1], normalised2world_scale_x);
+ point[2] = normalised_to_world(-screen_normalised(y, m_nHeight), m_vOrigin[2], normalised2world_scale_y);
+ } else {
+ point[0] = normalised_to_world(screen_normalised(x, m_nWidth), m_vOrigin[0], normalised2world_scale_x);
+ point[2] = normalised_to_world(-screen_normalised(y, m_nHeight), m_vOrigin[2], normalised2world_scale_y);
+ }
+}
+
+void XYWnd::XY_SnapToGrid(Vector3 &point)
+{
+ if (m_viewType == XY) {
+ point[0] = float_snapped(point[0], GetSnapGridSize());
+ point[1] = float_snapped(point[1], GetSnapGridSize());
+ } else if (m_viewType == YZ) {
+ point[1] = float_snapped(point[1], GetSnapGridSize());
+ point[2] = float_snapped(point[2], GetSnapGridSize());
+ } else {
+ point[0] = float_snapped(point[0], GetSnapGridSize());
+ point[2] = float_snapped(point[2], GetSnapGridSize());
+ }
+}
+
+void XYWnd::XY_LoadBackgroundImage(const char *name)
+{
+ const char *relative = path_make_relative(name, GlobalFileSystem().findRoot(name));
+ if (relative == name) {
+ globalOutputStream() << "WARNING: could not extract the relative path, using full path instead\n";
+ }
+
+ char fileNameWithoutExt[512];
+ strncpy(fileNameWithoutExt, relative, sizeof(fileNameWithoutExt) - 1);
+ fileNameWithoutExt[512 - 1] = '\0';
+ fileNameWithoutExt[strlen(fileNameWithoutExt) - 4] = '\0';
+
+ Image *image = QERApp_LoadImage(0, fileNameWithoutExt);
+ if (!image) {
+ globalOutputStream() << "Could not load texture " << fileNameWithoutExt << "\n";
+ return;
+ }
+ g_pParentWnd->ActiveXY()->m_tex = (qtexture_t *) malloc(sizeof(qtexture_t));
+ LoadTextureRGBA(g_pParentWnd->ActiveXY()->XYWnd::m_tex, image->getRGBAPixels(), image->getWidth(),
+ image->getHeight());
+ globalOutputStream() << "Loaded background texture " << relative << "\n";
+ g_pParentWnd->ActiveXY()->m_backgroundActivated = true;
+
+ int m_ix, m_iy;
+ switch (g_pParentWnd->ActiveXY()->m_viewType) {
+ case XY:
+ m_ix = 0;
+ m_iy = 1;
+ break;
+ case XZ:
+ m_ix = 0;
+ m_iy = 2;
+ break;
+ case YZ:
+ m_ix = 1;
+ m_iy = 2;
+ break;
+ }
+
+ Vector3 min, max;
+ Select_GetBounds(min, max);
+ g_pParentWnd->ActiveXY()->m_xmin = min[m_ix];
+ g_pParentWnd->ActiveXY()->m_ymin = min[m_iy];
+ g_pParentWnd->ActiveXY()->m_xmax = max[m_ix];
+ g_pParentWnd->ActiveXY()->m_ymax = max[m_iy];
+}
+
+void XYWnd::XY_DisableBackground(void)
+{
+ g_pParentWnd->ActiveXY()->m_backgroundActivated = false;
+ if (g_pParentWnd->ActiveXY()->m_tex) {
+ free(g_pParentWnd->ActiveXY()->m_tex);
+ }
+ g_pParentWnd->ActiveXY()->m_tex = NULL;
+}
+
+void WXY_BackgroundSelect(void)
+{
+ bool brushesSelected = Scene_countSelectedBrushes(GlobalSceneGraph()) != 0;
+ if (!brushesSelected) {
+ ui::alert(ui::root, "You have to select some brushes to get the bounding box for.\n",
+ "No selection", ui::alert_type::OK, ui::alert_icon::Error);
+ return;
+ }
+
+ const char *filename = MainFrame_getWindow().file_dialog(TRUE, "Background Image", NULL, NULL);
+ g_pParentWnd->ActiveXY()->XY_DisableBackground();
+ if (filename) {
+ g_pParentWnd->ActiveXY()->XY_LoadBackgroundImage(filename);
+ }
+}
+
+/*
+ ============================================================================
+
+ DRAWING
+
+ ============================================================================
+ */
+
+/*
+ ==============
+ XY_DrawGrid
+ ==============
+ */
+
+double two_to_the_power(int power)
+{
+ return pow(2.0f, power);
+}
+
+void XYWnd::XY_DrawAxis(void)
+{
+ if (g_xywindow_globals_private.show_axis) {
+ const char g_AxisName[3] = {'X', 'Y', 'Z'};
+ const int nDim1 = (m_viewType == YZ) ? 1 : 0;
+ const int nDim2 = (m_viewType == XY) ? 1 : 2;
+ const int w = (m_nWidth / 2 / m_fScale);
+ const int h = (m_nHeight / 2 / m_fScale);
+
+ const Vector3 &colourX = (m_viewType == YZ) ? g_xywindow_globals.AxisColorY : g_xywindow_globals.AxisColorX;
+ const Vector3 &colourY = (m_viewType == XY) ? g_xywindow_globals.AxisColorY : g_xywindow_globals.AxisColorZ;
+
+ // draw two lines with corresponding axis colors to highlight current view
+ // horizontal line: nDim1 color
+ glLineWidth(2);
+ glBegin(GL_LINES);
+ glColor3fv(vector3_to_array(colourX));
+ glVertex2f(m_vOrigin[nDim1] - w + 40 / m_fScale, m_vOrigin[nDim2] + h - 45 / m_fScale);
+ glVertex2f(m_vOrigin[nDim1] - w + 65 / m_fScale, m_vOrigin[nDim2] + h - 45 / m_fScale);
+ glVertex2f(0, 0);
+ glVertex2f(32 / m_fScale, 0);
+ glColor3fv(vector3_to_array(colourY));
+ glVertex2f(m_vOrigin[nDim1] - w + 40 / m_fScale, m_vOrigin[nDim2] + h - 45 / m_fScale);
+ glVertex2f(m_vOrigin[nDim1] - w + 40 / m_fScale, m_vOrigin[nDim2] + h - 20 / m_fScale);
+ glVertex2f(0, 0);
+ glVertex2f(0, 32 / m_fScale);
+ glEnd();
+ glLineWidth(1);
+ // now print axis symbols
+ glColor3fv(vector3_to_array(colourX));
+ glRasterPos2f(m_vOrigin[nDim1] - w + 55 / m_fScale, m_vOrigin[nDim2] + h - 55 / m_fScale);
+ GlobalOpenGL().drawChar(g_AxisName[nDim1]);
+ glRasterPos2f(28 / m_fScale, -10 / m_fScale);
+ GlobalOpenGL().drawChar(g_AxisName[nDim1]);
+ glColor3fv(vector3_to_array(colourY));
+ glRasterPos2f(m_vOrigin[nDim1] - w + 25 / m_fScale, m_vOrigin[nDim2] + h - 30 / m_fScale);
+ GlobalOpenGL().drawChar(g_AxisName[nDim2]);
+ glRasterPos2f(-10 / m_fScale, 28 / m_fScale);
+ GlobalOpenGL().drawChar(g_AxisName[nDim2]);
+ }
+}
+
+void XYWnd::XY_DrawBackground(void)
+{
+ glPushAttrib(GL_ALL_ATTRIB_BITS);
+
+ glEnable(GL_TEXTURE_2D);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+
+ glPolygonMode(GL_FRONT, GL_FILL);
+
+ glBindTexture(GL_TEXTURE_2D, m_tex->texture_number);
+ glBegin(GL_QUADS);
+
+ glColor4f(1.0, 1.0, 1.0, m_alpha);
+ glTexCoord2f(0.0, 1.0);
+ glVertex2f(m_xmin, m_ymin);
+
+ glTexCoord2f(1.0, 1.0);
+ glVertex2f(m_xmax, m_ymin);
+
+ glTexCoord2f(1.0, 0.0);
+ glVertex2f(m_xmax, m_ymax);
+
+ glTexCoord2f(0.0, 0.0);
+ glVertex2f(m_xmin, m_ymax);
+
+ glEnd();
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ glPopAttrib();
+}
+
+void XYWnd::XY_DrawGrid(void)
+{
+ float x, y, xb, xe, yb, ye;
+ float w, h, a;
+ char text[32];
+ float step, minor_step, stepx, stepy;
+ step = minor_step = stepx = stepy = GetGridSize();
+
+ int minor_power = Grid_getPower();
+ int mask;
+
+ while ((minor_step * m_fScale) <= 4.0f) { // make sure minor grid spacing is at least 4 pixels on the screen
+ ++minor_power;
+ minor_step *= 2;
+ }
+ int power = minor_power;
+ while ((power % 3) != 0 ||
+ (step * m_fScale) <= 32.0f) { // make sure major grid spacing is at least 32 pixels on the screen
+ ++power;
+ step = float(two_to_the_power(power));
+ }
+ mask = (1 << (power - minor_power)) - 1;
+ while ((stepx * m_fScale) <= 32.0f) { // text step x must be at least 32
+ stepx *= 2;
+ }
+ while ((stepy * m_fScale) <= 32.0f) { // text step y must be at least 32
+ stepy *= 2;
+ }
+
+ a = ((GetSnapGridSize() > 0.0f) ? 1.0f : 0.3f);
+
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_TEXTURE_1D);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_BLEND);
+ glLineWidth(1);
+
+ w = (m_nWidth / 2 / m_fScale);
+ h = (m_nHeight / 2 / m_fScale);
+
+ const int nDim1 = (m_viewType == YZ) ? 1 : 0;
+ const int nDim2 = (m_viewType == XY) ? 1 : 2;
+
+ xb = m_vOrigin[nDim1] - w;
+ if (xb < region_mins[nDim1]) {
+ xb = region_mins[nDim1];
+ }
+ xb = step * floor(xb / step);
+
+ xe = m_vOrigin[nDim1] + w;
+ if (xe > region_maxs[nDim1]) {
+ xe = region_maxs[nDim1];
+ }
+ xe = step * ceil(xe / step);
+
+ yb = m_vOrigin[nDim2] - h;
+ if (yb < region_mins[nDim2]) {
+ yb = region_mins[nDim2];
+ }
+ yb = step * floor(yb / step);
+
+ ye = m_vOrigin[nDim2] + h;
+ if (ye > region_maxs[nDim2]) {
+ ye = region_maxs[nDim2];
+ }
+ ye = step * ceil(ye / step);
+
+#define COLORS_DIFFER(a, b) \
+ ( ( a )[0] != ( b )[0] || \
+ ( a )[1] != ( b )[1] || \
+ ( a )[2] != ( b )[2] )
+
+ // djbob
+ // draw minor blocks
+ if (g_xywindow_globals_private.d_showgrid || a < 1.0f) {
+ if (a < 1.0f) {
+ glEnable(GL_BLEND);
+ }
+
+ if (COLORS_DIFFER(g_xywindow_globals.color_gridminor, g_xywindow_globals.color_gridback)) {
+ glColor4fv(vector4_to_array(Vector4(g_xywindow_globals.color_gridminor, a)));
+
+ glBegin(GL_LINES);
+ int i = 0;
+ for (x = xb; x < xe; x += minor_step, ++i) {
+ if ((i & mask) != 0) {
+ glVertex2f(x, yb);
+ glVertex2f(x, ye);
+ }
+ }
+ i = 0;
+ for (y = yb; y < ye; y += minor_step, ++i) {
+ if ((i & mask) != 0) {
+ glVertex2f(xb, y);
+ glVertex2f(xe, y);
+ }
+ }
+ glEnd();
+ }
+
+ // draw major blocks
+ if (COLORS_DIFFER(g_xywindow_globals.color_gridmajor, g_xywindow_globals.color_gridminor)) {
+ glColor4fv(vector4_to_array(Vector4(g_xywindow_globals.color_gridmajor, a)));
+
+ glBegin(GL_LINES);
+ for (x = xb; x <= xe; x += step) {
+ glVertex2f(x, yb);
+ glVertex2f(x, ye);
+ }
+ for (y = yb; y <= ye; y += step) {
+ glVertex2f(xb, y);
+ glVertex2f(xe, y);
+ }
+ glEnd();
+ }
+
+ if (a < 1.0f) {
+ glDisable(GL_BLEND);
+ }
+ }
+
+ // draw coordinate text if needed
+ if (g_xywindow_globals_private.show_coordinates) {
+ glColor4fv(vector4_to_array(Vector4(g_xywindow_globals.color_gridtext, 1.0f)));
+ float offx = m_vOrigin[nDim2] + h - (4 + GlobalOpenGL().m_font->getPixelAscent()) / m_fScale;
+ float offy = m_vOrigin[nDim1] - w + 4 / m_fScale;
+ for (x = xb - fmod(xb, stepx); x <= xe; x += stepx) {
+ glRasterPos2f(x, offx);
+ sprintf(text, "%g", x);
+ GlobalOpenGL().drawString(text);
+ }
+ for (y = yb - fmod(yb, stepy); y <= ye; y += stepy) {
+ glRasterPos2f(offy, y);
+ sprintf(text, "%g", y);
+ GlobalOpenGL().drawString(text);
+ }
+
+ if (Active()) {
+ glColor3fv(vector3_to_array(g_xywindow_globals.color_viewname));
+ }
+
+ // we do this part (the old way) only if show_axis is disabled
+ if (!g_xywindow_globals_private.show_axis) {
+ glRasterPos2f(m_vOrigin[nDim1] - w + 35 / m_fScale, m_vOrigin[nDim2] + h - 20 / m_fScale);
+
+ GlobalOpenGL().drawString(ViewType_getTitle(m_viewType));
+ }
+ }
+
+ XYWnd::XY_DrawAxis();
+
+ // show current work zone?
+ // the work zone is used to place dropped points and brushes
+ if (g_xywindow_globals_private.d_show_work) {
+ glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
+ glBegin(GL_LINES);
+ glVertex2f(xb, Select_getWorkZone().d_work_min[nDim2]);
+ glVertex2f(xe, Select_getWorkZone().d_work_min[nDim2]);
+ glVertex2f(xb, Select_getWorkZone().d_work_max[nDim2]);
+ glVertex2f(xe, Select_getWorkZone().d_work_max[nDim2]);
+ glVertex2f(Select_getWorkZone().d_work_min[nDim1], yb);
+ glVertex2f(Select_getWorkZone().d_work_min[nDim1], ye);
+ glVertex2f(Select_getWorkZone().d_work_max[nDim1], yb);
+ glVertex2f(Select_getWorkZone().d_work_max[nDim1], ye);
+ glEnd();
+ }
+}
+
+/*
+ ==============
+ XY_DrawBlockGrid
+ ==============
+ */
+void XYWnd::XY_DrawBlockGrid()
+{
+ if (Map_FindWorldspawn(g_map) == 0) {
+ return;
+ }
+ const char *value = Node_getEntity(*Map_GetWorldspawn(g_map))->getKeyValue("_blocksize");
+ if (strlen(value)) {
+ sscanf(value, "%i", &g_xywindow_globals_private.blockSize);
+ }
+
+ if (!g_xywindow_globals_private.blockSize || g_xywindow_globals_private.blockSize > 65536 ||
+ g_xywindow_globals_private.blockSize < 1024) {
+ // don't use custom blocksize if it is less than the default, or greater than the maximum world coordinate
+ g_xywindow_globals_private.blockSize = 1024;
+ }
+
+ float x, y, xb, xe, yb, ye;
+ float w, h;
+ char text[32];
+
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_TEXTURE_1D);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_BLEND);
+
+ w = (m_nWidth / 2 / m_fScale);
+ h = (m_nHeight / 2 / m_fScale);
+
+ int nDim1 = (m_viewType == YZ) ? 1 : 0;
+ int nDim2 = (m_viewType == XY) ? 1 : 2;
+
+ xb = m_vOrigin[nDim1] - w;
+ if (xb < region_mins[nDim1]) {
+ xb = region_mins[nDim1];
+ }
+ xb = static_cast<float>( g_xywindow_globals_private.blockSize * floor(xb / g_xywindow_globals_private.blockSize));
+
+ xe = m_vOrigin[nDim1] + w;
+ if (xe > region_maxs[nDim1]) {
+ xe = region_maxs[nDim1];
+ }
+ xe = static_cast<float>( g_xywindow_globals_private.blockSize * ceil(xe / g_xywindow_globals_private.blockSize));
+
+ yb = m_vOrigin[nDim2] - h;
+ if (yb < region_mins[nDim2]) {
+ yb = region_mins[nDim2];
+ }
+ yb = static_cast<float>( g_xywindow_globals_private.blockSize * floor(yb / g_xywindow_globals_private.blockSize));
+
+ ye = m_vOrigin[nDim2] + h;
+ if (ye > region_maxs[nDim2]) {
+ ye = region_maxs[nDim2];
+ }
+ ye = static_cast<float>( g_xywindow_globals_private.blockSize * ceil(ye / g_xywindow_globals_private.blockSize));
+
+ // draw major blocks
+
+ glColor3fv(vector3_to_array(g_xywindow_globals.color_gridblock));
+ glLineWidth(2);
+
+ glBegin(GL_LINES);
+
+ for (x = xb; x <= xe; x += g_xywindow_globals_private.blockSize) {
+ glVertex2f(x, yb);
+ glVertex2f(x, ye);
+ }
+
+ if (m_viewType == XY) {
+ for (y = yb; y <= ye; y += g_xywindow_globals_private.blockSize) {
+ glVertex2f(xb, y);
+ glVertex2f(xe, y);
+ }
+ }
+
+ glEnd();
+ glLineWidth(1);
+
+ // draw coordinate text if needed
+
+ if (m_viewType == XY && m_fScale > .1) {
+ for (x = xb; x < xe; x += g_xywindow_globals_private.blockSize) {
+ for (y = yb; y < ye; y += g_xywindow_globals_private.blockSize) {
+ glRasterPos2f(x + (g_xywindow_globals_private.blockSize / 2),
+ y + (g_xywindow_globals_private.blockSize / 2));
+ sprintf(text, "%i,%i", (int) floor(x / g_xywindow_globals_private.blockSize),
+ (int) floor(y / g_xywindow_globals_private.blockSize));
+ GlobalOpenGL().drawString(text);
+ }
+ }
+ }
+
+ glColor4f(0, 0, 0, 0);
+}
+
+void XYWnd::DrawCameraIcon(const Vector3 &origin, const Vector3 &angles)
+{
+ float x, y, fov, box;
+ double a;
+
+ fov = 48 / m_fScale;
+ box = 16 / m_fScale;
+
+ if (m_viewType == XY) {
+ x = origin[0];
+ y = origin[1];
+ a = degrees_to_radians(angles[CAMERA_YAW]);
+ } else if (m_viewType == YZ) {
+ x = origin[1];
+ y = origin[2];
+ a = degrees_to_radians(angles[CAMERA_PITCH]);
+ } else {
+ x = origin[0];
+ y = origin[2];
+ a = degrees_to_radians(angles[CAMERA_PITCH]);
+ }
+
+ glColor3f(0.0, 0.0, 1.0);
+ glBegin(GL_LINE_STRIP);
+ glVertex3f(x - box, y, 0);
+ glVertex3f(x, y + (box / 2), 0);
+ glVertex3f(x + box, y, 0);
+ glVertex3f(x, y - (box / 2), 0);
+ glVertex3f(x - box, y, 0);
+ glVertex3f(x + box, y, 0);
+ glEnd();
+
+ glBegin(GL_LINE_STRIP);
+ glVertex3f(x + static_cast<float>( fov * cos(a + c_pi / 4)), y + static_cast<float>( fov * sin(a + c_pi / 4)), 0);
+ glVertex3f(x, y, 0);
+ glVertex3f(x + static_cast<float>( fov * cos(a - c_pi / 4)), y + static_cast<float>( fov * sin(a - c_pi / 4)), 0);
+ glEnd();
+
+}
+
+
+float Betwixt(float f1, float f2)
+{
+ if (f1 > f2) {
+ return f2 + ((f1 - f2) / 2);
+ } else {
+ return f1 + ((f2 - f1) / 2);
+ }
+}
+
+
+// can be greatly simplified but per usual i am in a hurry
+// which is not an excuse, just a fact
+void XYWnd::PaintSizeInfo(int nDim1, int nDim2, Vector3 &vMinBounds, Vector3 &vMaxBounds)
+{
+ if (vector3_equal(vMinBounds, vMaxBounds)) {
+ return;
+ }
+ const char *g_pDimStrings[] = {"x:", "y:", "z:"};
+ typedef const char *OrgStrings[2];
+ const OrgStrings g_pOrgStrings[] = {{"x:", "y:",},
+ {"x:", "z:",},
+ {"y:", "z:",}};
+
+ Vector3 vSize(vector3_subtracted(vMaxBounds, vMinBounds));
+
+ glColor3f(g_xywindow_globals.color_selbrushes[0] * .65f,
+ g_xywindow_globals.color_selbrushes[1] * .65f,
+ g_xywindow_globals.color_selbrushes[2] * .65f);
+
+ StringOutputStream dimensions(16);
+
+ if (m_viewType == XY) {
+ glBegin(GL_LINES);
+
+ glVertex3f(vMinBounds[nDim1], vMinBounds[nDim2] - 6.0f / m_fScale, 0.0f);
+ glVertex3f(vMinBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale, 0.0f);
+
+ glVertex3f(vMinBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale, 0.0f);
+ glVertex3f(vMaxBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale, 0.0f);
+
+ glVertex3f(vMaxBounds[nDim1], vMinBounds[nDim2] - 6.0f / m_fScale, 0.0f);
+ glVertex3f(vMaxBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale, 0.0f);
+
+
+ glVertex3f(vMaxBounds[nDim1] + 6.0f / m_fScale, vMinBounds[nDim2], 0.0f);
+ glVertex3f(vMaxBounds[nDim1] + 10.0f / m_fScale, vMinBounds[nDim2], 0.0f);
+
+ glVertex3f(vMaxBounds[nDim1] + 10.0f / m_fScale, vMinBounds[nDim2], 0.0f);
+ glVertex3f(vMaxBounds[nDim1] + 10.0f / m_fScale, vMaxBounds[nDim2], 0.0f);
+
+ glVertex3f(vMaxBounds[nDim1] + 6.0f / m_fScale, vMaxBounds[nDim2], 0.0f);
+ glVertex3f(vMaxBounds[nDim1] + 10.0f / m_fScale, vMaxBounds[nDim2], 0.0f);
+
+ glEnd();
+
+ glRasterPos3f(Betwixt(vMinBounds[nDim1], vMaxBounds[nDim1]), vMinBounds[nDim2] - 20.0f / m_fScale, 0.0f);
+ dimensions << g_pDimStrings[nDim1] << vSize[nDim1];
+ GlobalOpenGL().drawString(dimensions.c_str());
+ dimensions.clear();
+
+ glRasterPos3f(vMaxBounds[nDim1] + 16.0f / m_fScale, Betwixt(vMinBounds[nDim2], vMaxBounds[nDim2]), 0.0f);
+ dimensions << g_pDimStrings[nDim2] << vSize[nDim2];
+ GlobalOpenGL().drawString(dimensions.c_str());
+ dimensions.clear();
+
+ glRasterPos3f(vMinBounds[nDim1] + 4, vMaxBounds[nDim2] + 8 / m_fScale, 0.0f);
+ dimensions << "(" << g_pOrgStrings[0][0] << vMinBounds[nDim1] << " " << g_pOrgStrings[0][1]
+ << vMaxBounds[nDim2] << ")";
+ GlobalOpenGL().drawString(dimensions.c_str());
+ } else if (m_viewType == XZ) {
+ glBegin(GL_LINES);
+
+ glVertex3f(vMinBounds[nDim1], 0, vMinBounds[nDim2] - 6.0f / m_fScale);
+ glVertex3f(vMinBounds[nDim1], 0, vMinBounds[nDim2] - 10.0f / m_fScale);
+
+ glVertex3f(vMinBounds[nDim1], 0, vMinBounds[nDim2] - 10.0f / m_fScale);
+ glVertex3f(vMaxBounds[nDim1], 0, vMinBounds[nDim2] - 10.0f / m_fScale);
+
+ glVertex3f(vMaxBounds[nDim1], 0, vMinBounds[nDim2] - 6.0f / m_fScale);
+ glVertex3f(vMaxBounds[nDim1], 0, vMinBounds[nDim2] - 10.0f / m_fScale);
+
+
+ glVertex3f(vMaxBounds[nDim1] + 6.0f / m_fScale, 0, vMinBounds[nDim2]);
+ glVertex3f(vMaxBounds[nDim1] + 10.0f / m_fScale, 0, vMinBounds[nDim2]);
+
+ glVertex3f(vMaxBounds[nDim1] + 10.0f / m_fScale, 0, vMinBounds[nDim2]);
+ glVertex3f(vMaxBounds[nDim1] + 10.0f / m_fScale, 0, vMaxBounds[nDim2]);
+
+ glVertex3f(vMaxBounds[nDim1] + 6.0f / m_fScale, 0, vMaxBounds[nDim2]);
+ glVertex3f(vMaxBounds[nDim1] + 10.0f / m_fScale, 0, vMaxBounds[nDim2]);
+
+ glEnd();
+
+ glRasterPos3f(Betwixt(vMinBounds[nDim1], vMaxBounds[nDim1]), 0, vMinBounds[nDim2] - 20.0f / m_fScale);
+ dimensions << g_pDimStrings[nDim1] << vSize[nDim1];
+ GlobalOpenGL().drawString(dimensions.c_str());
+ dimensions.clear();
+
+ glRasterPos3f(vMaxBounds[nDim1] + 16.0f / m_fScale, 0, Betwixt(vMinBounds[nDim2], vMaxBounds[nDim2]));
+ dimensions << g_pDimStrings[nDim2] << vSize[nDim2];
+ GlobalOpenGL().drawString(dimensions.c_str());
+ dimensions.clear();
+
+ glRasterPos3f(vMinBounds[nDim1] + 4, 0, vMaxBounds[nDim2] + 8 / m_fScale);
+ dimensions << "(" << g_pOrgStrings[1][0] << vMinBounds[nDim1] << " " << g_pOrgStrings[1][1]
+ << vMaxBounds[nDim2] << ")";
+ GlobalOpenGL().drawString(dimensions.c_str());
+ } else {
+ glBegin(GL_LINES);
+
+ glVertex3f(0, vMinBounds[nDim1], vMinBounds[nDim2] - 6.0f / m_fScale);
+ glVertex3f(0, vMinBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale);
+
+ glVertex3f(0, vMinBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale);
+ glVertex3f(0, vMaxBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale);
+
+ glVertex3f(0, vMaxBounds[nDim1], vMinBounds[nDim2] - 6.0f / m_fScale);
+ glVertex3f(0, vMaxBounds[nDim1], vMinBounds[nDim2] - 10.0f / m_fScale);
+
+
+ glVertex3f(0, vMaxBounds[nDim1] + 6.0f / m_fScale, vMinBounds[nDim2]);
+ glVertex3f(0, vMaxBounds[nDim1] + 10.0f / m_fScale, vMinBounds[nDim2]);
+
+ glVertex3f(0, vMaxBounds[nDim1] + 10.0f / m_fScale, vMinBounds[nDim2]);
+ glVertex3f(0, vMaxBounds[nDim1] + 10.0f / m_fScale, vMaxBounds[nDim2]);
+
+ glVertex3f(0, vMaxBounds[nDim1] + 6.0f / m_fScale, vMaxBounds[nDim2]);
+ glVertex3f(0, vMaxBounds[nDim1] + 10.0f / m_fScale, vMaxBounds[nDim2]);
+
+ glEnd();
+
+ glRasterPos3f(0, Betwixt(vMinBounds[nDim1], vMaxBounds[nDim1]), vMinBounds[nDim2] - 20.0f / m_fScale);
+ dimensions << g_pDimStrings[nDim1] << vSize[nDim1];
+ GlobalOpenGL().drawString(dimensions.c_str());
+ dimensions.clear();
+
+ glRasterPos3f(0, vMaxBounds[nDim1] + 16.0f / m_fScale, Betwixt(vMinBounds[nDim2], vMaxBounds[nDim2]));
+ dimensions << g_pDimStrings[nDim2] << vSize[nDim2];
+ GlobalOpenGL().drawString(dimensions.c_str());
+ dimensions.clear();
+
+ glRasterPos3f(0, vMinBounds[nDim1] + 4.0f, vMaxBounds[nDim2] + 8 / m_fScale);
+ dimensions << "(" << g_pOrgStrings[2][0] << vMinBounds[nDim1] << " " << g_pOrgStrings[2][1]
+ << vMaxBounds[nDim2] << ")";
+ GlobalOpenGL().drawString(dimensions.c_str());
+ }
+}
+
+class XYRenderer : public Renderer {
+ struct state_type {
+ state_type() :
+ m_highlight(0),
+ m_state(0)
+ {
+ }