gtkradiant/radiant/drag.cpp

846 lines
20 KiB
C++

/*
Copyright (C) 1999-2007 id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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 2 of the License, or
(at your option) any later version.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "stdafx.h"
//#include "qe3.h"
/*
drag either multiple brushes, or select plane points from
a single brush.
*/
extern int g_nPatchClickedView;
qboolean drag_ok;
vec3_t drag_xvec;
vec3_t drag_yvec;
//static int buttonstate;
int pressx, pressy;
static vec3_t pressdelta;
static vec3_t vPressStart;
//static int buttonx, buttony;
//int num_move_points;
//float *move_points[1024];
int lastx, lasty;
qboolean drag_first;
void AxializeVector (vec3_t v)
{
vec3_t a;
float o;
int i;
if (!v[0] && !v[1])
return;
if (!v[1] && !v[2])
return;
if (!v[0] && !v[2])
return;
for (i=0 ; i<3 ; i++)
a[i] = fabs(v[i]);
if (a[0] > a[1] && a[0] > a[2])
i = 0;
else if (a[1] > a[0] && a[1] > a[2])
i = 1;
else
i = 2;
o = v[i];
VectorCopy (vec3_origin, v);
if (o<0)
v[i] = -1;
else
v[i] = 1;
}
/*
===========
Drag_Setup
===========
*/
extern void SelectCurvePointByRay (vec3_t org, vec3_t dir, int buttons);
void Drag_Setup (int x, int y, int buttons,
vec3_t xaxis, vec3_t yaxis,
vec3_t origin, vec3_t dir)
{
trace_t t;
face_t *f;
drag_first = true;
VectorCopy (vec3_origin, pressdelta);
pressx = x;
pressy = y;
// snap to nearest axis for camwindow drags
VectorCopy (xaxis, drag_xvec);
AxializeVector (drag_xvec);
VectorCopy (yaxis, drag_yvec);
AxializeVector (drag_yvec);
if (g_qeglobals.d_select_mode == sel_curvepoint)
{
SelectCurvePointByRay (origin, dir, buttons);
if(g_qeglobals.d_select_mode == sel_area)
{
drag_ok = true;
if(g_nPatchClickedView == W_CAMERA ) {
VectorSet( g_qeglobals.d_vAreaTL, x, y, 0 );
VectorSet( g_qeglobals.d_vAreaBR, x, y, 0 );
}
}
else if (g_qeglobals.d_num_move_points) // don't add an undo if there are no points selected
{
drag_ok = true;
Sys_UpdateWindows(W_ALL);
Undo_Start("drag curve point");
Undo_AddBrushList(&selected_brushes);
}
return;
}
else
{
g_qeglobals.d_num_move_points = 0;
}
if (g_qeglobals.d_select_mode == sel_areatall)
{
VectorCopy(origin, g_qeglobals.d_vAreaTL);
VectorCopy(origin, g_qeglobals.d_vAreaBR);
Sys_UpdateWindows(W_ALL);
drag_ok = true;
return;
}
if (selected_brushes.next == &selected_brushes)
{
//in this case a new brush is created when the dragging
//takes place in the XYWnd, An useless undo is created
//when the dragging takes place in the CamWnd
Undo_Start("create brush");
Sys_Status("No selection to drag", 0);
return;
}
if (g_qeglobals.d_select_mode == sel_vertex)
{
SelectVertexByRay (origin, dir);
if (g_qeglobals.d_num_move_points)
{
drag_ok = true;
Undo_Start("drag vertex");
Undo_AddBrushList(&selected_brushes);
// Need an update here for highlighting selected vertices
Sys_UpdateWindows(W_XY | W_CAMERA);
return;
}
}
if (g_qeglobals.d_select_mode == sel_edge)
{
SelectEdgeByRay (origin, dir);
if (g_qeglobals.d_num_move_points)
{
drag_ok = true;
Undo_Start("drag edge");
Undo_AddBrushList(&selected_brushes);
return;
}
}
//
// check for direct hit first
//
t = Test_Ray (origin, dir, true);
if (t.selected)
{
drag_ok = true;
Undo_Start("drag selection");
Undo_AddBrushList(&selected_brushes);
if (buttons == (MK_LBUTTON|MK_CONTROL) )
{
Sys_Printf ("Shear dragging face\n");
Brush_SelectFaceForDragging (t.brush, t.face, true);
}
else if (buttons == (MK_LBUTTON|MK_CONTROL|MK_SHIFT) )
{
Sys_Printf ("Sticky dragging brush\n");
for (f=t.brush->brush_faces ; f ; f=f->next)
Brush_SelectFaceForDragging (t.brush, f, false);
}
else
Sys_Printf ("Dragging entire selection\n");
return;
}
if (g_qeglobals.d_select_mode == sel_vertex || g_qeglobals.d_select_mode == sel_edge)
return;
//
// check for side hit
//
// multiple brushes selected?
if (selected_brushes.next->next != &selected_brushes)
{
// yes, special handling
bool bOK = (g_PrefsDlg.m_bALTEdge) ? Sys_AltDown() : true;
if (bOK)
{
for (brush_t* pBrush = selected_brushes.next ; pBrush != &selected_brushes ; pBrush = pBrush->next)
{
if (buttons & MK_CONTROL)
Brush_SideSelect (pBrush, origin, dir, true);
else
Brush_SideSelect (pBrush, origin, dir, false);
}
}
else
{
Sys_Printf ("press ALT to drag multiple edges\n");
return;
}
}
else
{
// single select.. trying to drag fixed entities handle themselves and just move
if (buttons & MK_CONTROL)
Brush_SideSelect (selected_brushes.next, origin, dir, true);
else
Brush_SideSelect (selected_brushes.next, origin, dir, false);
}
Sys_Printf ("Side stretch\n");
drag_ok = true;
Undo_Start("side stretch");
Undo_AddBrushList(&selected_brushes);
}
entity_t *peLink;
void UpdateTarget(vec3_t origin, vec3_t dir)
{
trace_t t;
entity_t *pe;
int i;
char sz[128];
t = Test_Ray (origin, dir, 0);
if (!t.brush)
return;
pe = t.brush->owner;
if (pe == NULL)
return;
// is this the first?
if (peLink != NULL)
{
// Get the target id from out current target
// if there is no id, make one
i = IntForKey(pe, "target");
if (i <= 0)
{
i = GetUniqueTargetId(1);
sprintf(sz, "%d", i);
SetKeyValue(pe, "target", sz);
}
// set the target # into our src
sprintf(sz, "%d", i);
SetKeyValue(peLink, "targetname", sz);
Sys_UpdateWindows(W_ENTITY);
}
// promote the target to the src
peLink = pe;
}
/*
===========
Drag_Begin
//++timo test three button mouse and three button emulation here ?
===========
*/
void Drag_Begin (int x, int y, int buttons,
vec3_t xaxis, vec3_t yaxis,
vec3_t origin, vec3_t dir, bool sf_camera)
{
trace_t t;
bool altdown;
int nFlag;
drag_ok = false;
VectorCopy (vec3_origin, pressdelta);
VectorCopy (vec3_origin, vPressStart);
drag_first = true;
peLink = NULL;
altdown = Sys_AltDown();
// shift-LBUTTON = select entire brush
// shift-alt-LBUTTON = drill select
if (buttons == (MK_LBUTTON | MK_SHIFT) && g_qeglobals.d_select_mode != sel_curvepoint)
{
nFlag = altdown ? SF_CYCLE : 0;
if (sf_camera)
nFlag |= SF_CAMERA;
else
nFlag |= SF_ENTITIES_FIRST;
Select_Ray(origin, dir, nFlag);
return;
}
// (shift-)alt-LBUTTON = area select completely tall
if ( !sf_camera &&
( g_PrefsDlg.m_bALTEdge ? buttons == (MK_LBUTTON | MK_CONTROL | MK_SHIFT) : (buttons == MK_LBUTTON || buttons == (MK_LBUTTON | MK_CONTROL | MK_SHIFT)) ) &&
altdown && g_qeglobals.d_select_mode != sel_curvepoint)
{
if (g_pParentWnd->ActiveXY()->AreaSelectOK())
{
g_qeglobals.d_select_mode = sel_areatall;
Drag_Setup (x, y, buttons, xaxis, yaxis, origin, dir);
return;
}
}
// ctrl-alt-LBUTTON = multiple brush select without selecting whole entities
if (buttons == (MK_LBUTTON | MK_CONTROL) && altdown && g_qeglobals.d_select_mode != sel_curvepoint)
{
nFlag = 0;
if (sf_camera)
nFlag |= SF_CAMERA;
else
nFlag |= SF_ENTITIES_FIRST;
Select_Ray (origin, dir, nFlag);
UpdateSurfaceDialog();
return;
}
// ctrl-shift LBUTTON = select single face
if (sf_camera && buttons == (MK_LBUTTON | MK_CONTROL | MK_SHIFT) && g_qeglobals.d_select_mode != sel_curvepoint)
{
if(Sys_AltDown())
{
brush_t *b;
for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
{
if(b->pPatch)
continue;
for (face_t* pFace = b->brush_faces; pFace; pFace = pFace->next)
{
g_ptrSelectedFaces.Add(pFace);
g_ptrSelectedFaceBrushes.Add(b);
}
}
for (b = selected_brushes.next; b != &selected_brushes; )
{
brush_t *pb = b;
b = b->next;
Brush_RemoveFromList (pb);
Brush_AddToList (pb, &active_brushes);
}
}
else
Select_Deselect (true);
Select_Ray (origin, dir, (SF_SINGLEFACE|SF_CAMERA));
return;
}
// LBUTTON + all other modifiers = manipulate selection
if (buttons & MK_LBUTTON)
{
Drag_Setup (x, y, buttons, xaxis, yaxis, origin, dir);
return;
}
int nMouseButton = g_PrefsDlg.m_nMouseButtons == 2 ? MK_RBUTTON : MK_MBUTTON;
// middle button = grab texture
if (buttons == nMouseButton)
{
t = Test_Ray (origin, dir, false);
if (t.face)
{
UpdateWorkzone_ForBrush( t.brush );
// use a local brushprimit_texdef fitted to a default 2x2 texture
brushprimit_texdef_t bp_local;
ConvertTexMatWithQTexture( &t.face->brushprimit_texdef, t.face->d_texture, &bp_local, NULL );
Texture_SetTexture ( &t.face->texdef, &bp_local, false, NULL);
UpdateSurfaceDialog();
UpdatePatchInspector();
}
else
Sys_Printf ("Did not select a texture\n");
return;
}
// ctrl-middle button = set entire brush to texture
if (buttons == (nMouseButton|MK_CONTROL) )
{
t = Test_Ray (origin, dir, false);
if (t.brush)
{
if (t.brush->brush_faces->texdef.GetName()[0] == '(')
Sys_Printf ("Can't change an entity texture\n");
else
{
Brush_SetTexture (t.brush, &g_qeglobals.d_texturewin.texdef, &g_qeglobals.d_texturewin.brushprimit_texdef, false, static_cast<IPluginTexdef *>( g_qeglobals.d_texturewin.pTexdef ) );
Sys_UpdateWindows (W_ALL);
}
}
else
Sys_Printf ("Didn't hit a btrush\n");
return;
}
// ctrl-shift-middle button = set single face to texture
if (buttons == (nMouseButton|MK_SHIFT|MK_CONTROL) )
{
t = Test_Ray (origin, dir, false);
if (t.brush)
{
if (t.brush->brush_faces->texdef.GetName()[0] == '(')
Sys_Printf ("Can't change an entity texture\n");
else
{
SetFaceTexdef (t.face, &g_qeglobals.d_texturewin.texdef, &g_qeglobals.d_texturewin.brushprimit_texdef);
Brush_Build( t.brush );
Sys_UpdateWindows (W_ALL);
}
}
else
Sys_Printf ("Didn't hit a btrush\n");
return;
}
if (buttons == (nMouseButton | MK_SHIFT))
{
Sys_Printf("Set brush face texture info\n");
t = Test_Ray (origin, dir, false);
if (t.brush)
{
if (t.brush->brush_faces->texdef.GetName()[0] == '(')
{
if (t.brush->owner->eclass->nShowFlags & ECLASS_LIGHT)
{
CString strBuff;
qtexture_t* pTex = g_qeglobals.d_texturewin.pShader->getTexture();
if (pTex)
{
vec3_t vColor;
VectorCopy(pTex->color, vColor);
float fLargest = 0.0f;
for (int i = 0; i < 3; i++)
{
if (vColor[i] > fLargest)
fLargest = vColor[i];
}
if (fLargest == 0.0f)
{
vColor[0] = vColor[1] = vColor[2] = 1.0f;
}
else
{
float fScale = 1.0f / fLargest;
for (int i = 0; i < 3; i++)
{
vColor[i] *= fScale;
}
}
strBuff.Format("%f %f %f",pTex->color[0], pTex->color[1], pTex->color[2]);
SetKeyValue(t.brush->owner, "_color", strBuff.GetBuffer());
Sys_UpdateWindows (W_ALL);
}
}
else
{
Sys_Printf ("Can't select an entity brush face\n");
}
}
else
{
// http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=425
Face_SetShader(t.face, g_qeglobals.d_texturewin.texdef.GetName());
Brush_Build(t.brush);
Sys_UpdateWindows (W_ALL);
}
}
else
Sys_Printf ("Didn't hit a brush\n");
return;
}
}
//
//===========
//MoveSelection
//===========
//
void MoveSelection (vec3_t move)
{
int i, success;
brush_t *b;
CString strStatus;
vec3_t vTemp, vTemp2, end;
if (!move[0] && !move[1] && !move[2])
{
return;
}
if (!(g_qeglobals.d_select_mode == sel_area || g_qeglobals.d_select_mode == sel_areatall))
{
move[0] = (g_nScaleHow & SCALE_X) ? 0 : move[0];
move[1] = (g_nScaleHow & SCALE_Y) ? 0 : move[1];
move[2] = (g_nScaleHow & SCALE_Z) ? 0 : move[2];
}
if (g_pParentWnd->ActiveXY()->RotateMode() || g_bPatchBendMode)
{
float fDeg = -move[2];
float fAdj = move[2];
int nAxis = 0;
if (g_pParentWnd->ActiveXY()->GetViewType() == XY)
{
fDeg = -move[1];
fAdj = move[1];
nAxis = 2;
}
else
if (g_pParentWnd->ActiveXY()->GetViewType() == XZ)
{
fDeg = move[2];
fAdj = move[2];
nAxis = 1;
}
else
nAxis = 0;
g_pParentWnd->ActiveXY()->Rotation()[nAxis] += fAdj;
strStatus.Format("%s x:: %.1f y:: %.1f z:: %.1f", (g_bPatchBendMode) ? "Bend angle" : "Rotation", g_pParentWnd->ActiveXY()->Rotation()[0], g_pParentWnd->ActiveXY()->Rotation()[1], g_pParentWnd->ActiveXY()->Rotation()[2]);
g_pParentWnd->SetStatusText(2, strStatus);
if (g_bPatchBendMode)
{
Patch_SelectBendNormal();
Select_RotateAxis(nAxis, fDeg*2, false, true);
Patch_SelectBendAxis();
Select_RotateAxis(nAxis, fDeg, false, true);
}
else
{
Select_RotateAxis(nAxis, fDeg, false, true);
}
return;
}
if (g_pParentWnd->ActiveXY()->ScaleMode())
{
vec3_t v;
v[0] = v[1] = v[2] = 1.0f;
if (move[1] > 0)
{
v[0] = 1.1f;
v[1] = 1.1f;
v[2] = 1.1f;
}
else
if (move[1] < 0)
{
v[0] = 0.9f;
v[1] = 0.9f;
v[2] = 0.9f;
}
Select_Scale((g_nScaleHow & SCALE_X) ? 1.0f : v[0],
(g_nScaleHow & SCALE_Y) ? 1.0f : v[1],
(g_nScaleHow & SCALE_Z) ? 1.0f : v[2]);
// is that really necessary???
Sys_UpdateWindows (W_ALL);
return;
}
vec3_t vDistance;
VectorSubtract(pressdelta, vPressStart, vDistance);
strStatus.Format("Distance x: %.1f y: %.1f z: %.1f", vDistance[0], vDistance[1], vDistance[2]);
g_pParentWnd->SetStatusText(3, strStatus);
//
// dragging only a part of the selection
//
// this is fairly crappy way to deal with curvepoint and area selection
// but it touches the smallest amount of code this way
//
if (g_qeglobals.d_num_move_points || g_qeglobals.d_select_mode == sel_vertex || g_qeglobals.d_select_mode == sel_area || g_qeglobals.d_select_mode == sel_areatall)
{
//area selection
if (g_qeglobals.d_select_mode == sel_area || g_qeglobals.d_select_mode == sel_areatall)
{
VectorAdd(g_qeglobals.d_vAreaBR, move, g_qeglobals.d_vAreaBR);
return;
}
//curve point selection
if (g_qeglobals.d_select_mode == sel_curvepoint)
{
Patch_UpdateSelected(move);
return;
}
//vertex selection
if (g_qeglobals.d_select_mode == sel_vertex && g_PrefsDlg.m_bVertexSplit)
{
if(g_qeglobals.d_num_move_points) {
success = true;
for (b = selected_brushes.next; b != &selected_brushes; b = b->next)
{
success &= Brush_MoveVertex(b, g_qeglobals.d_move_points[0], move, end, true);
}
if (success)
VectorCopy(end, g_qeglobals.d_move_points[0]);
}
return;
}
//all other selection types
for (i=0 ; i<g_qeglobals.d_num_move_points ; i++)
VectorAdd (g_qeglobals.d_move_points[i], move, g_qeglobals.d_move_points[i]);
for (b = selected_brushes.next; b != &selected_brushes; b = b->next)
{
bool bMoved = false;
for(face_t *f = b->brush_faces; !bMoved && f!=NULL; f=f->next)
for(int p=0; !bMoved && p<3; p++)
for (i=0 ; !bMoved && i<g_qeglobals.d_num_move_points ; i++)
if(f->planepts[p] == g_qeglobals.d_move_points[i])
bMoved = true;
if(!bMoved) continue;
VectorCopy(b->maxs, vTemp);
VectorSubtract(vTemp, b->mins, vTemp);
Brush_Build(b,true,true,false,false); // don't filter
for (i=0 ; i<3 ; i++)
{
if (b->mins[i] > b->maxs[i]
|| b->maxs[i] - b->mins[i] > g_MaxBrushSize)
break; // dragged backwards or fucked up
}
if (i != 3)
break;
if (b->patchBrush)
{
VectorCopy(b->maxs, vTemp2);
VectorSubtract(vTemp2, b->mins, vTemp2);
VectorSubtract(vTemp2, vTemp, vTemp2);
//if (!Patch_DragScale(b->nPatchID, vTemp2, move))
if (!Patch_DragScale(b->pPatch, vTemp2, move))
{
b = NULL;
break;
}
}
}
// if any of the brushes were crushed out of existance
// calcel the entire move
if (b != &selected_brushes)
{
Sys_Printf ("Brush dragged backwards, move canceled\n");
for (i=0 ; i<g_qeglobals.d_num_move_points ; i++)
VectorSubtract (g_qeglobals.d_move_points[i], move, g_qeglobals.d_move_points[i]);
for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
Brush_Build(b,true,true,false,false); // don't filter
}
}
else
{
// reset face originals from vertex edit mode
// this is dirty, but unfortunately necessary because Brush_Build
// can remove windings
for (b = selected_brushes.next; b != &selected_brushes; b = b->next)
{
Brush_ResetFaceOriginals(b);
}
//
// if there are lots of brushes selected, just translate instead
// of rebuilding the brushes
// NOTE: this is not actually done, but would be a good idea
//
Select_Move (move);
}
}
/*
===========
Drag_MouseMoved
===========
*/
void Drag_MouseMoved (int x, int y, int buttons)
{
vec3_t move, delta;
int i;
if (!buttons)
{
drag_ok = false;
return;
}
if (!drag_ok)
return;
// clear along one axis
if (buttons & MK_SHIFT && (g_PrefsDlg.m_bALTEdge && g_qeglobals.d_select_mode != sel_areatall))
{
drag_first = false;
if (abs(x-pressx) > abs(y-pressy))
y = pressy;
else
x = pressx;
}
if (g_qeglobals.d_select_mode == sel_area && g_nPatchClickedView == W_CAMERA)
{
camera_t *m_pCamera = g_pParentWnd->GetCamWnd()->Camera();
// snap to window
if( y > m_pCamera->height ) y = m_pCamera->height - 1; else if( y < 0 ) y = 0;
if( x > m_pCamera->width ) x = m_pCamera->width - 1; else if( x < 0 ) x = 0;
VectorSet (move, x - pressx, y - pressy, 0);
} else
{
for (i=0 ; i<3 ; i++)
{
move[i] = drag_xvec[i]*(x - pressx) + drag_yvec[i]*(y - pressy);
if (!g_PrefsDlg.m_bNoClamp)
{
move[i] = floor(move[i]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize;
}
}
}
VectorSubtract (move, pressdelta, delta);
VectorCopy (move, pressdelta);
MoveSelection (delta);
}
/*
===========
Drag_MouseUp
===========
*/
void Drag_MouseUp (int nButtons)
{
Sys_Status ("Drag completed.", 0);
if (g_qeglobals.d_select_mode == sel_area)
{
Patch_SelectAreaPoints(nButtons & MK_CONTROL); // adds to selection and/or deselects selected points if ctrl is held
g_qeglobals.d_select_mode = sel_curvepoint;
Sys_UpdateWindows (W_ALL);
}
if (g_qeglobals.d_select_mode == sel_areatall)
{
vec3_t mins, maxs;
int nDim1 = (g_pParentWnd->ActiveXY()->GetViewType() == YZ) ? 1 : 0;
int nDim2 = (g_pParentWnd->ActiveXY()->GetViewType() == XY) ? 1 : 2;
// get our rectangle
mins[nDim1] = MIN( g_qeglobals.d_vAreaTL[nDim1], g_qeglobals.d_vAreaBR[nDim1] );
mins[nDim2] = MIN( g_qeglobals.d_vAreaTL[nDim2], g_qeglobals.d_vAreaBR[nDim2] );
maxs[nDim1] = MAX( g_qeglobals.d_vAreaTL[nDim1], g_qeglobals.d_vAreaBR[nDim1] );
maxs[nDim2] = MAX( g_qeglobals.d_vAreaTL[nDim2], g_qeglobals.d_vAreaBR[nDim2] );
// deselect current selection
if( !(nButtons & (MK_CONTROL|MK_SHIFT)) )
Select_Deselect();
// select new selection
Select_RealCompleteTall( mins, maxs );
Sys_UpdateWindows (W_ALL);
}
if (g_qeglobals.d_select_translate[0] || g_qeglobals.d_select_translate[1] || g_qeglobals.d_select_translate[2])
{
Select_Move (g_qeglobals.d_select_translate);
VectorCopy (vec3_origin, g_qeglobals.d_select_translate);
Sys_UpdateWindows (W_CAMERA);
}
/* note: added cleanup here, since an edge drag will leave selected vertices
in g_qeglobals.d_num_move_points
*/
if ( g_qeglobals.d_select_mode != sel_vertex &&
g_qeglobals.d_select_mode != sel_curvepoint &&
g_qeglobals.d_select_mode != sel_edge)
g_qeglobals.d_num_move_points = 0;
g_pParentWnd->SetStatusText(3, "");
Undo_EndBrushList(&selected_brushes);
Undo_End();
UpdateSurfaceDialog();
}