/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see .
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "tools/edit_gui_common.h"
#include "qe3.h"
#include "splines.h"
/* drag either multiple brushes, or select plane points from a single brush. */
bool g_moveOnly = false;
bool drag_ok;
idVec3 drag_xvec;
idVec3 drag_yvec;
static int buttonstate;
int pressx, pressy;
static idVec3 pressdelta;
static idVec3 vPressStart;
static int buttonx, buttony;
// int num_move_points; float *move_points[1024];
int lastx, lasty;
bool drag_first;
/*
================
AxializeVector
================
*/
static void AxializeVector( idVec3 &v ) {
idVec3 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] = idMath::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;
}
}
extern bool UpdateActiveDragPoint(const idVec3 &move);
extern void SetActiveDrag(CDragPoint *p);
/*
================
Draw_Setup
================
*/
static void Drag_Setup( int x, int y, int buttons,
const idVec3 &xaxis, const idVec3 &yaxis, const idVec3 &origin, const idVec3 &dir ) {
qertrace_t t;
face_t *f;
drag_first = true;
VectorCopy(vec3_origin, pressdelta);
pressx = x;
pressy = y;
VectorCopy(xaxis, drag_xvec);
AxializeVector(drag_xvec);
VectorCopy(yaxis, drag_yvec);
AxializeVector(drag_yvec);
if (g_qeglobals.d_select_mode == sel_addpoint) {
if (g_qeglobals.selectObject) {
g_qeglobals.selectObject->addPoint(origin);
}
else {
g_qeglobals.d_select_mode = sel_brush;
}
return;
}
if (g_qeglobals.d_select_mode == sel_editpoint) {
g_Inspectors->entityDlg.SelectCurvePointByRay( origin, dir, buttons );
if ( g_qeglobals.d_num_move_points ) {
drag_ok = true;
}
Sys_UpdateWindows(W_ALL);
return;
}
if (g_qeglobals.d_select_mode == sel_curvepoint) {
SelectCurvePointByRay(origin, dir, buttons);
if (g_qeglobals.d_num_move_points || g_qeglobals.d_select_mode == sel_area) {
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 (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\n", 0);
return;
}
if (g_qeglobals.d_select_mode == sel_vertex) {
if ( radiant_entityMode.GetBool() ) {
return;
}
SelectVertexByRay(origin, dir);
if (g_qeglobals.d_num_move_points) {
drag_ok = true;
Undo_Start("drag vertex");
Undo_AddBrushList(&selected_brushes);
return;
}
}
if (g_qeglobals.d_select_mode == sel_edge) {
if ( radiant_entityMode.GetBool() ) {
return;
}
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);
SetActiveDrag(t.point);
if (t.point) {
drag_ok = true;
// point was hit
return;
}
if (t.selected) {
drag_ok = true;
Undo_Start("drag selection");
Undo_AddBrushList(&selected_brushes);
if (buttons == (MK_LBUTTON | MK_CONTROL)) {
Sys_Status("Shear dragging face\n");
Brush_SelectFaceForDragging(t.brush, t.face, true);
}
else if (buttons == (MK_LBUTTON | MK_CONTROL | MK_SHIFT)) {
Sys_Status("Sticky dragging brush\n");
for (f = t.brush->brush_faces; f; f = f->next) {
Brush_SelectFaceForDragging(t.brush, f, false);
}
}
else {
Sys_Status("Dragging entire selection\n");
}
return;
}
if (g_qeglobals.d_select_mode == sel_vertex || g_qeglobals.d_select_mode == sel_edge) {
return;
}
if ( radiant_entityMode.GetBool() ) {
return;
}
// check for side hit multiple brushes selected?
if (selected_brushes.next->next != &selected_brushes) {
// yes, special handling
bool bOK = ( g_PrefsDlg.m_bALTEdge ) ? ( ::GetAsyncKeyState( VK_MENU ) != 0 ) : 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_Status("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_Status("Side stretch\n");
drag_ok = true;
Undo_Start("side stretch");
Undo_AddBrushList(&selected_brushes);
}
extern void Face_GetScale_BrushPrimit(face_t *face, float *s, float *t, float *rot);
/*
================
Drag_Begin
================
*/
void Drag_Begin( int x, int y, int buttons,
const idVec3 &xaxis, const idVec3 &yaxis, const idVec3 &origin, const idVec3 &dir ) {
qertrace_t t;
drag_ok = false;
VectorCopy(vec3_origin, pressdelta);
VectorCopy(vec3_origin, vPressStart);
drag_first = true;
// shift LBUTTON = select entire brush
if (buttons == (MK_LBUTTON | MK_SHIFT) && g_qeglobals.d_select_mode != sel_curvepoint) {
int nFlag = ( ::GetAsyncKeyState( VK_MENU ) != 0 ) ? SF_CYCLE : 0;
if (dir[0] == 0 || dir[1] == 0 || dir[2] == 0) { // extremely low chance of this happening from camera
Select_Ray(origin, dir, nFlag | SF_ENTITIES_FIRST); // hack for XY
}
else {
Select_Ray(origin, dir, nFlag);
}
return;
}
// ctrl-shift LBUTTON = select single face
if (buttons == (MK_LBUTTON | MK_CONTROL | MK_SHIFT) && g_qeglobals.d_select_mode != sel_curvepoint) {
if ( radiant_entityMode.GetBool() ) {
return;
}
// _D3XP disabled
//Select_Deselect( ( ::GetAsyncKeyState( VK_MENU ) == 0 ) );
Select_Ray(origin, dir, SF_SINGLEFACE);
return;
}
// LBUTTON + all other modifiers = manipulate selection
if (buttons & MK_LBUTTON) {
Drag_Setup(x, y, buttons, xaxis, yaxis, origin, dir);
return;
}
if ( radiant_entityMode.GetBool() ) {
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) {
g_qeglobals.d_new_brush_bottom = t.brush->mins;
g_qeglobals.d_new_brush_top = t.brush->maxs;
// use a local brushprimit_texdef fitted to a default 2x2 texture
brushprimit_texdef_t bp_local;
if (t.brush && t.brush->pPatch) {
texdef_t localtd;
memset(&bp_local.coords, 0, sizeof(bp_local.coords));
bp_local.coords[0][0] = 1.0f;
bp_local.coords[1][1] = 1.0f;
localtd.SetName(t.brush->pPatch->d_texture->GetName());
Texture_SetTexture(&localtd, &bp_local, false, true);
Select_CopyPatchTextureCoords ( t.brush->pPatch );
} else {
Select_ProjectFaceOntoPatch( t.face );
ConvertTexMatWithQTexture(&t.face->brushprimit_texdef, t.face->d_texture, &bp_local, NULL);
Texture_SetTexture(&t.face->texdef, &bp_local, false, true);
}
UpdateSurfaceDialog();
UpdatePatchInspector();
UpdateLightInspector();
}
else {
Sys_Status("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.name[0] == '(') {
Sys_Status("Can't change an entity texture\n");
}
else {
Brush_SetTexture
(
t.brush,
&g_qeglobals.d_texturewin.texdef,
&g_qeglobals.d_texturewin.brushprimit_texdef,
false
);
Sys_UpdateWindows(W_ALL);
}
}
else {
Sys_Status("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.name[0] == '(') {
Sys_Status("Can't change an entity texture\n");
}
else {
SetFaceTexdef
(
t.brush,
t.face,
&g_qeglobals.d_texturewin.texdef,
&g_qeglobals.d_texturewin.brushprimit_texdef
);
Brush_Build(t.brush);
Sys_UpdateWindows(W_ALL);
}
}
else {
Sys_Status("Didn't hit a btrush\n");
}
return;
}
if (buttons == (nMouseButton | MK_SHIFT)) {
Sys_Status("Set brush face texture info\n");
t = Test_Ray(origin, dir, false);
if (t.brush && !t.brush->owner->eclass->fixedsize) {
/*
if (t.brush->brush_faces->texdef.name[0] == '(') {
if (t.brush->owner->eclass->nShowFlags & ECLASS_LIGHT) {
CString strBuff;
idMaterial *pTex = declManager->FindMaterial(g_qeglobals.d_texturewin.texdef.name);
if (pTex) {
idVec3 vColor = pTex->getColor();
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->getColor().x, pTex->getColor().y, pTex->getColor().z);
SetKeyValue(t.brush->owner, "_color", strBuff.GetBuffer(0));
Sys_UpdateWindows(W_ALL);
}
}
else {
Sys_Status("Can't select an entity brush face\n");
}
}
else {
*/
// strcpy(t.face->texdef.name,g_qeglobals.d_texturewin.texdef.name);
t.face->texdef.SetName(g_qeglobals.d_texturewin.texdef.name);
Brush_Build(t.brush);
Sys_UpdateWindows(W_ALL);
// }
}
else {
Sys_Status("Didn't hit a brush\n");
}
return;
}
}
void Brush_GetBounds(brush_t *b, idVec3 &mins, idVec3 &maxs) {
int i;
for (i = 0; i < 3; i++) {
mins[i] = 999999;
maxs[i] = -999999;
}
for (i = 0; i < 3; i++) {
if (b->mins[i] < mins[i]) {
mins[i] = b->mins[i];
}
if (b->maxs[i] > maxs[i]) {
maxs[i] = b->maxs[i];
}
}
}
/*
================
MoveSelection
================
*/
static void MoveSelection( const idVec3 &orgMove ) {
int i, success;
brush_t *b;
CString strStatus;
idVec3 vTemp, vTemp2, end, move;
move = orgMove;
if (!move[0] && !move[1] && !move[2]) {
return;
}
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 axis = 0;
if (g_pParentWnd->ActiveXY()->GetViewType() == XY) {
fDeg = -move[1];
fAdj = move[1];
axis = 2;
}
else if (g_pParentWnd->ActiveXY()->GetViewType() == XZ) {
fDeg = move[2];
fAdj = move[2];
axis = 1;
}
g_pParentWnd->ActiveXY()->Rotation()[g_qeglobals.rotateAxis] += 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(axis, fDeg * 2, false, true);
Patch_SelectBendAxis();
Select_RotateAxis(axis, fDeg, false, true);
}
else {
Select_RotateAxis(g_qeglobals.rotateAxis, fDeg, false, true);
}
return;
}
if (g_pParentWnd->ActiveXY()->ScaleMode()) {
idVec3 v;
v[0] = v[1] = v[2] = 1.0f;
for (i = 0; i < 3; i++) {
if ( move[i] > 0.0f ) {
v[i] = 1.1f;
} else if ( move[i] < 0.0f ) {
v[i] = 0.9f;
}
}
Select_Scale(v.x, v.y, v.z);
Sys_UpdateWindows(W_ALL);
return;
}
idVec3 vDistance;
VectorSubtract(pressdelta, vPressStart, vDistance);
strStatus.Format("Distance x: %.3f y: %.3f z: %.3f", vDistance[0], vDistance[1], vDistance[2]);
g_pParentWnd->SetStatusText(3, strStatus);
// dragging only a part of the selection
if (UpdateActiveDragPoint(move)) {
UpdateLightInspector();
return;
}
//
// 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_num_move_planes || g_qeglobals.d_select_mode == sel_area) {
// area selection
if (g_qeglobals.d_select_mode == sel_area) {
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) {
success = true;
for (b = selected_brushes.next; b != &selected_brushes; b = b->next) {
success &= Brush_MoveVertex(selected_brushes.next, *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]);
}
if ( g_qeglobals.d_select_mode == sel_editpoint ) {
g_Inspectors->entityDlg.UpdateEntityCurve();
}
//
// VectorScale(move, .5, move); for (i=0 ; inext) {
VectorCopy(b->maxs, vTemp);
VectorSubtract(vTemp, b->mins, vTemp);
Brush_Build(b);
for (i = 0; i < 3; i++) {
if
(
b->mins[i] > b->maxs[i] ||
b->maxs[i] - b->mins[i] > MAX_WORLD_SIZE ||
b->maxs[i] - b->mins[i] == 0.0f
) {
break; // dragged backwards or messed up
}
}
if (i != 3) {
break;
}
if (b->pPatch) {
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_Status("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);
}
}
}
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);
}
Select_Move(move);
}
}
/*
================
Drag_MouseMoved
================
*/
void Drag_MouseMoved(int x, int y, int buttons) {
idVec3 move, delta;
int i;
if (!buttons || !drag_ok) {
drag_ok = false;
return;
}
// clear along one axis
if (buttons & MK_SHIFT) {
drag_first = false;
if (abs(x - pressx) > abs(y - pressy)) {
y = pressy;
}
else {
x = pressx;
}
}
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);
if (buttons & MK_CONTROL && g_pParentWnd->ActiveXY()->RotateMode()) {
for (i = 0; i < 3; i++) {
if (delta[i] != 0) {
if (delta[i] > 0) {
delta[i] = 15;
}
else {
delta[i] = -15;
}
}
}
}
MoveSelection(delta);
}
/*
================
Drag_MouseUp
================
*/
void Drag_MouseUp(int nButtons) {
Sys_Status("drag completed.", 0);
if (g_qeglobals.d_select_mode == sel_area) {
Patch_SelectAreaPoints();
g_qeglobals.d_select_mode = sel_curvepoint;
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);
}
g_pParentWnd->SetStatusText(3, "");
/*
if (g_pParentWnd->GetCamera()->UpdateRenderEntities()) {
Sys_UpdateWindows(W_CAMERA);
}
*/
Undo_EndBrushList(&selected_brushes);
Undo_End();
}