Selection visualization is now fixed.

This commit is contained in:
Justin Marshall
2026-05-15 11:44:01 -07:00
parent f6b489b2ff
commit 079442e7ba
+167 -53
View File
@@ -48,7 +48,7 @@ static char THIS_FILE[] = __FILE__;
extern void DrawPathLines();
extern qertrace_t Test_Ray(const idVec3& origin, const idVec3& dir, int flags);
extern brush_t *Brush_CreateFaceExtrusion(brush_t *sourceBrush, face_t *sourceFace, float distance);
extern brush_t* Brush_CreateFaceExtrusion(brush_t* sourceBrush, face_t* sourceFace, float distance);
extern void Select_ShiftTexture(float x, float y);
extern void Select_ScaleTexture(float x, float y);
extern void Select_RotateTexture(float amt, bool absolute);
@@ -1609,11 +1609,12 @@ from the selected face and linked to the same entity as the source brush.
struct camFaceExtrudeState_t {
bool active;
bool moved;
CCamWnd* cam;
brush_t* sourceBrush;
face_t* sourceFace;
idWinding* sourceWinding;
CCamWnd* cam;
brush_t* sourceBrush;
face_t* sourceFace;
idWinding* sourceWinding;
idVec3 normal;
idVec3 hitPoint;
CPoint startPoint;
float distance;
};
@@ -1717,6 +1718,7 @@ static bool CamWnd_FaceExtrudeBegin(CCamWnd* cam, const CPoint& point) {
s_faceExtrudeState.sourceFace = trace.face;
s_faceExtrudeState.sourceWinding = trace.face->face_winding->Copy();
s_faceExtrudeState.normal = normal;
s_faceExtrudeState.hitPoint = cam->Camera().origin + dir * trace.dist;
s_faceExtrudeState.startPoint = point;
s_faceExtrudeState.distance = 0.0f;
@@ -1727,17 +1729,53 @@ static bool CamWnd_FaceExtrudeBegin(CCamWnd* cam, const CPoint& point) {
return true;
}
static bool CamWnd_FaceExtrudeDistanceFromRay(CCamWnd* cam, const CPoint& point, float& distance) {
idVec3 rayDir;
CamWnd_FaceExtrudeBuildRay(cam, point, rayDir);
const idVec3 rayOrigin = cam->Camera().origin;
const idVec3 lineOrigin = s_faceExtrudeState.hitPoint;
const idVec3 lineDir = s_faceExtrudeState.normal;
// Find the closest point between the current mouse ray and the extrusion
// normal through the original hit point. This makes the drag distance match
// the world-space preview instead of treating one screen pixel as one unit.
const float b = DotProduct(rayDir, lineDir);
const float denom = 1.0f - b * b;
if (denom < 0.0001f) {
return false;
}
idVec3 w = rayOrigin - lineOrigin;
const float d = DotProduct(rayDir, w);
const float e = DotProduct(lineDir, w);
const float t = (e - b * d) / denom;
const float rayT = b * t - d;
if (rayT < 0.0f) {
return false;
}
distance = t;
return true;
}
static float CamWnd_FaceExtrudeMouseDistance(CCamWnd* cam, const CPoint& point) {
float distance = 0.0f;
if (CamWnd_FaceExtrudeDistanceFromRay(cam, point, distance)) {
return CamWnd_FaceExtrudeSnapDistance(distance);
}
const int dx = point.x - s_faceExtrudeState.startPoint.x;
const int dy = point.y - s_faceExtrudeState.startPoint.y;
camera_t& camera = cam->Camera();
idVec3 screenMove = camera.vright * (float)dx - camera.vup * (float)dy;
float distance = DotProduct(screenMove, s_faceExtrudeState.normal);
distance = DotProduct(screenMove, s_faceExtrudeState.normal);
// When the selected face is nearly screen-facing, projection onto the normal
// is too small to be useful. In that case use vertical mouse motion, with
// the sign chosen so dragging up extrudes toward the camera-facing normal.
// When the normal projects poorly onto the screen, fall back to vertical
// mouse motion, with the sign chosen so dragging up extrudes toward the
// camera-facing normal.
float verticalDistance = (float)-dy;
if (DotProduct(camera.vpn, s_faceExtrudeState.normal) > 0.0f) {
verticalDistance = -verticalDistance;
@@ -1795,79 +1833,104 @@ static void CamWnd_FaceExtrudeEnd(CCamWnd* cam, bool commit) {
Sys_UpdateWindows(W_ALL);
}
static void CamWnd_DrawFaceWindingFilled(face_t* face) {
if (face == NULL || face->face_winding == NULL || face->face_winding->GetNumPoints() < 3) {
return;
}
glBegin(GL_QUADS);
for (int i = 0; i < face->face_winding->GetNumPoints(); i++) {
glVertex3fv((*face->face_winding)[i].ToFloatPtr());
}
glEnd();
}
static void CamWnd_DrawFaceWindingOutline(face_t* face) {
if (face == NULL || face->face_winding == NULL || face->face_winding->GetNumPoints() < 3) {
return;
}
glBegin(GL_LINE_LOOP);
for (int i = 0; i < face->face_winding->GetNumPoints(); i++) {
glVertex3fv((*face->face_winding)[i].ToFloatPtr());
}
glEnd();
}
static void CamWnd_DrawTransparentFace(face_t* face, float r, float g, float b, float a) {
if (face == NULL || face->face_winding == NULL) {
return;
}
glPushAttrib(GL_CURRENT_BIT);
globalImages->BindNull();
glDisable(GL_LIGHTING);
glDisable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glColor4f(r, g, b, a);
CamWnd_DrawFaceWindingFilled(face);
glPopAttrib();
}
static void CamWnd_FaceExtrudeDrawPreview(CCamWnd* cam) {
if (!CamWnd_FaceExtrudeIsActive(cam) || s_faceExtrudeState.sourceWinding == NULL) {
return;
}
// Always tint the source side while it is selected for extrusion, even before
// the mouse has moved far enough to create a non-zero snapped preview.
CamWnd_DrawTransparentFace(s_faceExtrudeState.sourceFace, 1.0f, 0.0f, 0.0f, 0.25f);
if (idMath::Fabs(s_faceExtrudeState.distance) < CAMWND_FACE_EXTRUDE_EPSILON) {
return;
}
idWinding* w = s_faceExtrudeState.sourceWinding;
const int numPoints = w->GetNumPoints();
if (numPoints < 3) {
brush_t* previewBrush = Brush_CreateFaceExtrusion(
s_faceExtrudeState.sourceBrush,
s_faceExtrudeState.sourceFace,
s_faceExtrudeState.distance);
if (previewBrush == NULL) {
return;
}
idVec3 offset = s_faceExtrudeState.normal * s_faceExtrudeState.distance;
Brush_Build(previewBrush, true, false, false, false);
Brush_RemoveEmptyFaces(previewBrush);
if (previewBrush->brush_faces == NULL) {
Brush_Free(previewBrush);
return;
}
glPushAttrib(GL_CURRENT_BIT);
globalImages->BindNull();
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glDisable(GL_CULL_FACE);
glDisable(GL_BLEND);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glLineWidth(2.0f);
glColor3f(0.1f, 0.9f, 1.0f);
glBegin(GL_LINES);
for (int i = 0; i < numPoints; i++) {
const int j = (i + 1) % numPoints;
idVec3 a = (*w)[i].ToVec3();
idVec3 b = (*w)[j].ToVec3();
idVec3 a2 = a + offset;
idVec3 b2 = b + offset;
glVertex3fv(a.ToFloatPtr());
glVertex3fv(b.ToFloatPtr());
glVertex3fv(a2.ToFloatPtr());
glVertex3fv(b2.ToFloatPtr());
glVertex3fv(a.ToFloatPtr());
glVertex3fv(a2.ToFloatPtr());
for (face_t* face = previewBrush->brush_faces; face; face = face->next) {
CamWnd_DrawFaceWindingOutline(face);
}
glEnd();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glColor4f(0.1f, 0.9f, 1.0f, 0.18f);
glBegin(GL_QUADS);
for (int i = 0; i < numPoints; i++) {
idVec3 p = (*w)[i].ToVec3() + offset;
glVertex3fv(p.ToFloatPtr());
for (face_t* face = previewBrush->brush_faces; face; face = face->next) {
CamWnd_DrawFaceWindingFilled(face);
}
glEnd();
glBegin(GL_QUADS);
for (int i = 0; i < numPoints; i++) {
const int j = (i + 1) % numPoints;
idVec3 a = (*w)[i].ToVec3();
idVec3 b = (*w)[j].ToVec3();
idVec3 a2 = a + offset;
idVec3 b2 = b + offset;
glVertex3fv(a.ToFloatPtr());
glVertex3fv(b.ToFloatPtr());
glVertex3fv(b2.ToFloatPtr());
glVertex3fv(a2.ToFloatPtr());
}
glEnd();
glPopAttrib();
Brush_Free(previewBrush);
}
static COLORREF CamWnd_LerpColor(COLORREF a, COLORREF b, float t) {
const int ar = GetRValue(a);
const int ag = GetGValue(a);
@@ -2896,6 +2959,56 @@ void setGLMode(int mode) {
}
}
static bool CamWnd_FaceHasAllPlanePointsSelected(face_t* face) {
if (face == NULL || face->face_winding == NULL) {
return false;
}
for (int i = 0; i < 3; i++) {
if (PointInMoveList(&face->planepts[i]) < 0) {
return false;
}
}
return true;
}
static void CamWnd_DrawMoveSelectedBrushSidesForList(CCamWnd* cam, brush_t* list) {
if (list == NULL || list->next == NULL) {
return;
}
for (brush_t* brush = list->next; brush != list; brush = brush->next) {
if (CamWnd_MenuFilterBrush(cam, brush)) {
continue;
}
if (brush->pPatch || brush->modelHandle > 0 || brush->entityModel) {
continue;
}
for (face_t* face = brush->brush_faces; face; face = face->next) {
if (CamWnd_FaceHasAllPlanePointsSelected(face)) {
CamWnd_DrawFaceWindingFilled(face);
}
}
}
}
static void CamWnd_DrawMoveSelectedBrushSides(CCamWnd* cam) {
glPushAttrib(GL_CURRENT_BIT);
globalImages->BindNull();
glDisable(GL_LIGHTING);
glDisable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glColor4f(1.0f, 0.0f, 0.0f, 0.25f);
CamWnd_DrawMoveSelectedBrushSidesForList(cam, &active_brushes);
CamWnd_DrawMoveSelectedBrushSidesForList(cam, &selected_brushes);
glPopAttrib();
}
extern void glLabeledPoint(idVec4& color, idVec3& point, float size, const char* label);
void DrawAxial(face_t* selFace) {
@@ -3115,19 +3228,19 @@ void CCamWnd::Cam_Draw() {
}
// non-zbuffered outline
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//glDisable(GL_BLEND);
//glDisable(GL_DEPTH_TEST);
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
if (renderMode) {
glColor3f(1, 0, 0);
glColor4f(1.0f, 0.0f, 0.0f, 0.25f);
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);
glColor4f(1.0f, 0.0f, 0.0f, 0.25f);
for (brush = pList->next; brush != pList; brush = brush->next) {
if (CamWnd_MenuFilterBrush(this, brush)) {
continue;
@@ -3141,6 +3254,7 @@ void CCamWnd::Cam_Draw() {
}
}
CamWnd_DrawMoveSelectedBrushSides(this);
CamWnd_FaceExtrudeDrawPreview(this);
// edge / vertex flags