Added rotation and fixed decal placement bugs.

This commit is contained in:
Justin Marshall
2026-05-22 08:58:41 -07:00
parent 686422ec83
commit aefe08a1fc
7 changed files with 434 additions and 34 deletions
+1
View File
@@ -832,6 +832,7 @@ public:
// Screen-space world decals consumed by the path tracer.
virtual void ClearWorldDecals() = 0;
virtual int AddWorldDecal(const renderWorldDecal_t& decal) = 0;
virtual bool UpdateWorldDecal(int decalIndex, const renderWorldDecal_t& decal) = 0;
virtual void RemoveWorldDecal(int decalIndex) = 0;
virtual int NumWorldDecals() const = 0;
virtual const renderWorldDecal_t* GetWorldDecal(int decalIndex) const = 0;
+10
View File
@@ -142,6 +142,16 @@ int idRenderWorldLocal::AddWorldDecal(const renderWorldDecal_t& inDecal) {
return worldDecals.Append(decal);
}
bool idRenderWorldLocal::UpdateWorldDecal(int decalIndex, const renderWorldDecal_t& inDecal) {
if (decalIndex < 0 || decalIndex >= worldDecals.Num()) {
return false;
}
renderWorldDecal_t decal = inDecal;
R_NormalizeDecalBasis(decal);
worldDecals[decalIndex] = decal;
return true;
}
void idRenderWorldLocal::RemoveWorldDecal(int decalIndex) {
if (decalIndex >= 0 && decalIndex < worldDecals.Num()) {
worldDecals.RemoveIndex(decalIndex);
+1
View File
@@ -125,6 +125,7 @@ public:
virtual bool FastWorldTrace( modelTrace_t &trace, const idVec3 &start, const idVec3 &end ) const;
virtual void ClearWorldDecals();
virtual int AddWorldDecal( const renderWorldDecal_t &decal );
virtual bool UpdateWorldDecal( int decalIndex, const renderWorldDecal_t &decal );
virtual void RemoveWorldDecal( int decalIndex );
virtual int NumWorldDecals() const;
virtual const renderWorldDecal_t *GetWorldDecal( int decalIndex ) const;
+320 -21
View File
@@ -120,6 +120,9 @@ struct cameraNavState_t {
static cameraNavConfig_t s_cameraNavConfig;
static cameraNavState_t s_cameraNavState;
static void CameraNav_GetIniPath(char* path, int pathSize);
static bool s_camWndDecalPreviewValid = false;
static HWND s_camWndDecalPreviewOwner = NULL;
static renderWorldDecal_t s_camWndDecalPreview;
static bool CamWnd_DecalPlacementEnabled() {
char iniPath[MAX_PATH];
@@ -135,13 +138,31 @@ static float CamWnd_DecalFloat(const char* key, const char* defaultValue) {
return (float)atof(value);
}
static void CamWnd_DecalMaterial(idStr& material) {
static float s_camWndDecalPreviewRotation = 0.0f;
static void CamWnd_RotateDecalBasis(renderWorldDecal_t& decal, float degrees) {
const float radians = degrees * idMath::M_DEG2RAD;
const float s = sin(radians);
const float c = cos(radians);
const idVec3 oldU = decal.axisU;
const idVec3 oldV = decal.axisV;
decal.axisU = oldU * c + oldV * s;
decal.axisV = oldV * c - oldU * s;
}
static bool CamWnd_DecalMaterial(idStr& material) {
const char* selected = g_qeglobals.d_texturewin.texdef.name;
if (selected && selected[0] && selected[0] != '(') {
material = selected;
return;
if (!selected || !selected[0] || selected[0] == '(') {
return false;
}
material = "textures/decals/default";
const idMaterial* mat = declManager->FindMaterial(selected, false);
if (!mat || mat->TestMaterialFlag(MF_DEFAULTED)) {
return false;
}
material = selected;
return true;
}
static void CamWnd_DecalColorFromSelectedMaterial(const idStr& material, idVec4& color) {
@@ -1074,6 +1095,10 @@ static const char* CAMWND_PROP_DECAL_EDITING = "QER_CamWnd_DecalEditing";
#define CAMWND_MENU_CMD_SHOW_TRIGGER_BRUSHES 62108
#define CAMWND_MENU_CMD_CUBIC_CLIPPING 62109
#define CAMWND_MENU_CMD_DECAL_EDITING 62110
#define CAMWND_MENU_CMD_DECAL_SCALE_UP 62111
#define CAMWND_MENU_CMD_DECAL_SCALE_DOWN 62112
#define CAMWND_MENU_CMD_DECAL_ROTATE_CCW 62113
#define CAMWND_MENU_CMD_DECAL_ROTATE_CW 62114
#define CAMWND_FILTER_HIDE_WORLD 0x00000001
#define CAMWND_FILTER_HIDE_PATCHES 0x00000002
@@ -1232,6 +1257,8 @@ static void CamWnd_ToggleRealtimeLighting(CCamWnd* cam) {
CamWnd_SetRealtimeLighting(cam, !CamWnd_RealtimeLightingEnabled(cam));
}
static void CamWnd_ClearDecalPreview(CCamWnd* cam);
static bool CamWnd_DecalEditingEnabled(CCamWnd* cam) {
if (!cam || !cam->GetSafeHwnd()) {
return false;
@@ -1243,6 +1270,9 @@ static void CamWnd_SetDecalEditing(CCamWnd* cam, bool enabled) {
if (!cam || !cam->GetSafeHwnd()) {
return;
}
if (!enabled) {
CamWnd_ClearDecalPreview(cam);
}
CamWnd_SetIntProp(cam->GetSafeHwnd(), CAMWND_PROP_DECAL_EDITING, enabled ? 1 : 0);
CamWnd_RequestRedraw(cam);
}
@@ -1251,6 +1281,74 @@ static void CamWnd_ToggleDecalEditing(CCamWnd* cam) {
CamWnd_SetDecalEditing(cam, !CamWnd_DecalEditingEnabled(cam));
}
static void CamWnd_ClearDecalPreview(CCamWnd* cam = NULL) {
const bool wasValid = s_camWndDecalPreviewValid;
if (!cam || s_camWndDecalPreviewOwner == cam->GetSafeHwnd()) {
s_camWndDecalPreviewValid = false;
s_camWndDecalPreviewOwner = NULL;
if (wasValid && cam && cam->GetSafeHwnd()) {
cam->InvalidateRect(NULL, FALSE);
}
}
}
static void CamWnd_ScaleSelectedDecal(CCamWnd* cam, float scale) {
if (Map_ScaleSelectedDecal(scale)) {
CamWnd_RequestRedraw(cam);
}
}
static void CamWnd_RotateStencil(CCamWnd* cam, float degrees) {
if (Map_GetSelectedDecal() >= 0) {
if (Map_RotateSelectedDecal(degrees)) {
CamWnd_RequestRedraw(cam);
}
return;
}
s_camWndDecalPreviewRotation += degrees;
if (s_camWndDecalPreviewRotation >= 360.0f || s_camWndDecalPreviewRotation <= -360.0f) {
s_camWndDecalPreviewRotation = fmod(s_camWndDecalPreviewRotation, 360.0f);
}
CamWnd_ClearDecalPreview(cam);
if (cam && cam->GetSafeHwnd()) {
CamWnd_RequestRedraw(cam);
}
}
static bool CamWnd_HandleDecalTransformChar(CCamWnd* cam, UINT nChar) {
if (!CamWnd_DecalEditingEnabled(cam)) {
return false;
}
switch (nChar) {
case '+':
case '=':
if (Map_GetSelectedDecal() >= 0) {
CamWnd_ScaleSelectedDecal(cam, 1.25f);
return true;
}
break;
case '-':
case '_':
if (Map_GetSelectedDecal() >= 0) {
CamWnd_ScaleSelectedDecal(cam, 0.8f);
return true;
}
break;
case 'q':
case 'Q':
CamWnd_RotateStencil(cam, -15.0f);
return true;
case 'e':
case 'E':
CamWnd_RotateStencil(cam, 15.0f);
return true;
}
return false;
}
static void CamWnd_ToggleFilterBit(CCamWnd* cam, int bit) {
int mask = CamWnd_GetFilterMask(cam);
mask ^= bit;
@@ -1286,6 +1384,18 @@ static void CamWnd_HandleMenuCommand(CCamWnd* cam, int command) {
case CAMWND_MENU_CMD_DECAL_EDITING:
CamWnd_ToggleDecalEditing(cam);
break;
case CAMWND_MENU_CMD_DECAL_SCALE_UP:
CamWnd_ScaleSelectedDecal(cam, 1.25f);
break;
case CAMWND_MENU_CMD_DECAL_SCALE_DOWN:
CamWnd_ScaleSelectedDecal(cam, 0.8f);
break;
case CAMWND_MENU_CMD_DECAL_ROTATE_CCW:
CamWnd_RotateStencil(cam, -15.0f);
break;
case CAMWND_MENU_CMD_DECAL_ROTATE_CW:
CamWnd_RotateStencil(cam, 15.0f);
break;
case CAMWND_MENU_CMD_SHOW_WORLD:
CamWnd_ToggleFilterBit(cam, CAMWND_FILTER_HIDE_WORLD);
break;
@@ -1356,6 +1466,14 @@ static void CamWnd_ShowDecalsMenu(HWND hWnd, CCamWnd* cam) {
menu = CreatePopupMenu();
AppendMenu(menu, MF_STRING | (CamWnd_DecalEditingEnabled(cam) ? MF_CHECKED : 0), CAMWND_MENU_CMD_DECAL_EDITING, "Enable decal editing");
AppendMenu(menu, MF_SEPARATOR, 0, NULL);
UINT scaleFlags = MF_STRING | (Map_GetSelectedDecal() >= 0 ? 0 : MF_GRAYED);
AppendMenu(menu, scaleFlags, CAMWND_MENU_CMD_DECAL_SCALE_UP, "Scale selected up (+)");
AppendMenu(menu, scaleFlags, CAMWND_MENU_CMD_DECAL_SCALE_DOWN, "Scale selected down (-)");
AppendMenu(menu, MF_SEPARATOR, 0, NULL);
UINT rotateFlags = MF_STRING | (CamWnd_DecalEditingEnabled(cam) ? 0 : MF_GRAYED);
AppendMenu(menu, rotateFlags, CAMWND_MENU_CMD_DECAL_ROTATE_CCW, "Rotate stencil left (Q)");
AppendMenu(menu, rotateFlags, CAMWND_MENU_CMD_DECAL_ROTATE_CW, "Rotate stencil right (E)");
command = TrackPopupMenu(menu, TPM_RETURNCMD | TPM_LEFTALIGN | TPM_TOPALIGN, pt.x, pt.y, 0, hWnd, NULL);
DestroyMenu(menu);
@@ -1816,29 +1934,24 @@ static void CamWnd_FaceExtrudeBuildRay(CCamWnd* cam, const CPoint& point, idVec3
dir.Normalize();
}
static bool CamWnd_DecalClick(CCamWnd* cam, const CPoint& point, UINT nFlags) {
if (!CamWnd_DecalEditingEnabled(cam) || cam == NULL) {
static bool CamWnd_BuildDecalAtPoint(CCamWnd* cam, const CPoint& point, renderWorldDecal_t& decal, const char** hitSource) {
if (cam == NULL) {
return false;
}
idVec3 dir;
CamWnd_FaceExtrudeBuildRay(cam, point, dir);
if (nFlags & MK_SHIFT) {
Map_SelectDecalByRay(cam->Camera().origin, dir);
return true;
}
idVec3 hitPoint;
idVec3 hitNormal;
float hitDist = HUGE_DISTANCE;
bool hitBrush = false;
const char* hitSource = "none";
const char* localHitSource = "none";
qertrace_t trace = Test_Ray(cam->Camera().origin, dir, 0);
if (trace.brush != NULL && trace.face != NULL && trace.dist > 0.0f && trace.dist < hitDist) {
hitBrush = true;
hitSource = "brush";
localHitSource = "brush";
hitDist = trace.dist;
hitPoint = cam->Camera().origin + dir * trace.dist;
hitNormal = trace.face->plane.Normal();
@@ -1850,7 +1963,7 @@ static bool CamWnd_DecalClick(CCamWnd* cam, const CPoint& point, UINT nFlags) {
if (g_qeglobals.rw->Trace(modelTrace, cam->Camera().origin, end, 0.0f, false, false)) {
float modelDist = (modelTrace.point - cam->Camera().origin) * dir;
if (!hitBrush && modelDist > 0.0f && modelDist < hitDist) {
hitSource = "render";
localHitSource = "render";
hitDist = modelDist;
hitPoint = modelTrace.point;
hitNormal = modelTrace.normal;
@@ -1859,11 +1972,12 @@ static bool CamWnd_DecalClick(CCamWnd* cam, const CPoint& point, UINT nFlags) {
}
if (hitDist <= 0.0f || hitDist >= HUGE_DISTANCE || hitNormal.LengthSqr() < 0.0001f) {
return true;
return false;
}
renderWorldDecal_t decal;
CamWnd_DecalMaterial(decal.material);
if (!CamWnd_DecalMaterial(decal.material)) {
return false;
}
CamWnd_DecalColorFromSelectedMaterial(decal.material, decal.color);
decal.origin = hitPoint;
decal.normal = hitNormal;
@@ -1875,9 +1989,147 @@ static bool CamWnd_DecalClick(CCamWnd* cam, const CPoint& point, UINT nFlags) {
decal.axisU = decal.normal.Cross(cam->Camera().vright);
}
decal.axisV = decal.normal.Cross(decal.axisU);
CamWnd_RotateDecalBasis(decal, s_camWndDecalPreviewRotation);
decal.size = CamWnd_DecalFloat("size", "64");
decal.depth = CamWnd_DecalFloat("depth", "8");
decal.opacity = CamWnd_DecalFloat("opacity", "1");
if (hitSource) {
*hitSource = localHitSource;
}
return true;
}
static void CamWnd_UpdateDecalPreview(CCamWnd* cam, const CPoint& point) {
if (!CamWnd_DecalEditingEnabled(cam) || cam == NULL || !cam->GetSafeHwnd()) {
CamWnd_ClearDecalPreview(cam);
return;
}
if (Map_GetSelectedDecal() >= 0) {
CamWnd_ClearDecalPreview(cam);
return;
}
renderWorldDecal_t preview;
if (!CamWnd_BuildDecalAtPoint(cam, point, preview, NULL)) {
CamWnd_ClearDecalPreview(cam);
return;
}
s_camWndDecalPreview = preview;
s_camWndDecalPreviewOwner = cam->GetSafeHwnd();
s_camWndDecalPreviewValid = true;
cam->InvalidateRect(NULL, FALSE);
}
static bool CamWnd_HasDecalPreview(CCamWnd* cam) {
return cam && cam->GetSafeHwnd() && s_camWndDecalPreviewValid && s_camWndDecalPreviewOwner == cam->GetSafeHwnd();
}
static void CamWnd_SubmitWorldDecalsWithPreview(CCamWnd* cam) {
if (!g_qeglobals.rw) {
return;
}
if (!CamWnd_HasDecalPreview(cam)) {
g_qeglobals.rw->SubmitWorldDecalsToScreenSpace();
return;
}
const int previewIndex = g_qeglobals.rw->AddWorldDecal(s_camWndDecalPreview);
g_qeglobals.rw->SubmitWorldDecalsToScreenSpace();
if (previewIndex >= 0) {
g_qeglobals.rw->RemoveWorldDecal(previewIndex);
}
}
static void CamWnd_DecalCorners(const renderWorldDecal_t& decal, idVec3 corners[4]) {
const idVec3 axisU = decal.axisU * (decal.size * 0.5f);
const idVec3 axisV = decal.axisV * (decal.size * 0.5f);
corners[0] = decal.origin - axisU - axisV;
corners[1] = decal.origin + axisU - axisV;
corners[2] = decal.origin + axisU + axisV;
corners[3] = decal.origin - axisU + axisV;
}
static void CamWnd_DrawDecalOutlineGL(const renderWorldDecal_t& decal, const idVec4& color, float lineWidth) {
idVec3 corners[4];
CamWnd_DecalCorners(decal, corners);
const GLboolean depthEnabled = glIsEnabled(GL_DEPTH_TEST);
glPushAttrib(GL_CURRENT_BIT);
glDisable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
globalImages->BindNull();
glLineWidth(lineWidth);
glColor4f(color.x, color.y, color.z, color.w);
glBegin(GL_LINE_LOOP);
for (int i = 0; i < 4; ++i) {
glVertex3fv(corners[i].ToFloatPtr());
}
glEnd();
glBegin(GL_LINES);
glVertex3fv(decal.origin.ToFloatPtr());
idVec3 normalEnd = decal.origin + decal.normal * idMath::ClampFloat(4.0f, 32.0f, decal.size * 0.2f);
glVertex3fv(normalEnd.ToFloatPtr());
glEnd();
glLineWidth(1.0f);
if (depthEnabled) {
glEnable(GL_DEPTH_TEST);
}
glPopAttrib();
}
static void CamWnd_DrawDecalSelectionOverlayGL(CCamWnd* cam) {
if (!cam || !g_qeglobals.rw || !CamWnd_DecalEditingEnabled(cam)) {
return;
}
const int selected = Map_GetSelectedDecal();
if (selected >= 0) {
const renderWorldDecal_t* decal = g_qeglobals.rw->GetWorldDecal(selected);
if (decal) {
CamWnd_DrawDecalOutlineGL(*decal, idVec4(1.0f, 0.85f, 0.05f, 1.0f), 3.0f);
}
}
if (CamWnd_HasDecalPreview(cam)) {
CamWnd_DrawDecalOutlineGL(s_camWndDecalPreview, idVec4(0.1f, 0.8f, 1.0f, 1.0f), 1.5f);
}
}
static void CamWnd_DebugDecalOutline(const renderWorldDecal_t& decal, const idVec4& color) {
if (!g_qeglobals.rw) {
return;
}
idVec3 corners[4];
CamWnd_DecalCorners(decal, corners);
for (int i = 0; i < 4; ++i) {
g_qeglobals.rw->DebugLine(color, corners[i], corners[(i + 1) & 3], 0, false);
}
g_qeglobals.rw->DebugLine(color, decal.origin, decal.origin + decal.normal * idMath::ClampFloat(4.0f, 32.0f, decal.size * 0.2f), 0, false);
}
static bool CamWnd_DecalClick(CCamWnd* cam, const CPoint& point, UINT nFlags) {
if (!CamWnd_DecalEditingEnabled(cam) || cam == NULL) {
return false;
}
idVec3 dir;
CamWnd_FaceExtrudeBuildRay(cam, point, dir);
if (nFlags & MK_SHIFT) {
const int selected = Map_SelectDecalByRay(cam->Camera().origin, dir);
if (selected >= 0) {
CamWnd_ClearDecalPreview(cam);
}
return true;
}
renderWorldDecal_t decal;
const char* hitSource = "none";
if (!CamWnd_BuildDecalAtPoint(cam, point, decal, &hitSource)) {
return true;
}
Map_AddDecal(decal);
Sys_Status(va("Placed decal using %s on %s at %.2f %.2f %.2f\n", decal.material.c_str(), hitSource, decal.origin.x, decal.origin.y, decal.origin.z), 0);
@@ -2913,6 +3165,7 @@ CCamWnd::~CCamWnd() {
BEGIN_MESSAGE_MAP(CCamWnd, CWnd)
//{{AFX_MSG_MAP(CCamWnd)
ON_WM_KEYDOWN()
ON_WM_CHAR()
ON_WM_PAINT()
ON_WM_DESTROY()
ON_WM_CLOSE()
@@ -3024,6 +3277,10 @@ void CCamWnd::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
CamWnd_FaceExtrudeEnd(this, false);
return;
}
if (nChar == VK_ESCAPE && CamWnd_DecalEditingEnabled(this) && Map_GetSelectedDecal() >= 0) {
Map_ClearSelectedDecal();
return;
}
if (nChar == VK_DELETE && CamWnd_DecalEditingEnabled(this) && Map_GetSelectedDecal() >= 0) {
Map_DeleteSelectedDecal();
return;
@@ -3034,6 +3291,17 @@ void CCamWnd::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
g_pParentWnd->HandleKey(nChar, nRepCnt, nFlags);
}
/*
=======================================================================================================================
=======================================================================================================================
*/
void CCamWnd::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) {
if (CamWnd_HandleDecalTransformChar(this, nChar)) {
return;
}
CWnd::OnChar(nChar, nRepCnt, nFlags);
}
brush_t* g_pSplitList = NULL;
/*
@@ -3075,6 +3343,7 @@ void CCamWnd::SetXYFriend(CXYWnd* pWnd) {
void CCamWnd::OnDestroy() {
CamWnd_FaceExtrudeEnd(this, false);
CamWnd_GizmoEnd(this, false);
CamWnd_ClearDecalPreview(this);
CamWnd_DestroyMenuBar(this);
CWnd::OnDestroy();
}
@@ -3097,6 +3366,10 @@ void CCamWnd::OnMouseMove(UINT nFlags, CPoint point) {
CRect r;
GetClientRect(r);
if (GetCapture() != this) {
CamWnd_UpdateDecalPreview(this, point);
}
if (CamWnd_FaceExtrudeMouseMove(this, point)) {
m_ptLastCursor = point;
return;
@@ -4265,8 +4538,16 @@ void CCamWnd::Cam_Draw() {
}
CamWnd_DrawMoveSelectedBrushSides(brushFilter);
if (!renderMode && g_qeglobals.rw && CamWnd_DecalEditingEnabled(this)) {
g_qeglobals.rw->SubmitWorldDecalsToScreenSpace();
if (!renderMode && g_qeglobals.rw) {
if (CamWnd_DecalEditingEnabled(this)) {
CamWnd_UpdateDecalPreview(this, m_ptLastCursor);
CamWnd_SubmitWorldDecalsWithPreview(this);
CamWnd_DrawDecalSelectionOverlayGL(this);
}
else {
CamWnd_ClearDecalPreview(this);
g_qeglobals.rw->SubmitWorldDecalsToScreenSpace();
}
}
CamWnd_FaceExtrudeDrawPreview(this);
CamWnd_GizmoDraw(this);
@@ -5866,14 +6147,28 @@ void CCamWnd::Cam_Render() {
}
Cam_SetGlobalFogFromWorldspawn(refdef);
if (CamWnd_DecalEditingEnabled(this)) {
CamWnd_UpdateDecalPreview(this, m_ptLastCursor);
}
int previewDecalIndex = -1;
if (g_qeglobals.rw && CamWnd_HasDecalPreview(this)) {
previewDecalIndex = g_qeglobals.rw->AddWorldDecal(s_camWndDecalPreview);
}
if (g_qeglobals.rw && CamWnd_DecalEditingEnabled(this)) {
for (int i = 0; i < g_qeglobals.rw->NumWorldDecals(); ++i) {
const renderWorldDecal_t* decal = g_qeglobals.rw->GetWorldDecal(i);
if (!decal) {
continue;
}
idVec4 color = (i == Map_GetSelectedDecal()) ? idVec4(1.0f, 0.85f, 0.1f, 1.0f) : idVec4(0.1f, 0.8f, 1.0f, 1.0f);
const bool selected = (i == Map_GetSelectedDecal());
const bool preview = (i == previewDecalIndex);
idVec4 color = selected ? idVec4(1.0f, 0.85f, 0.1f, 1.0f) : idVec4(0.1f, 0.8f, 1.0f, 1.0f);
g_qeglobals.rw->DebugCircle(color, decal->origin, decal->normal, decal->size * 0.5f, 32, 0, false);
if (selected || preview) {
CamWnd_DebugDecalOutline(*decal, color);
}
}
}
@@ -5884,6 +6179,10 @@ void CCamWnd::Cam_Render() {
renderSystem->EndFrame(&frontEnd, &backEnd, false);
//common->Printf( "front:%i back:%i\n", frontEnd, backEnd );
if (g_qeglobals.rw && previewDecalIndex >= 0) {
g_qeglobals.rw->RemoveWorldDecal(previewDecalIndex);
}
//glPopAttrib();
//DrawEntityData();
+1
View File
@@ -180,6 +180,7 @@ protected:
void OriginalMouseUp(UINT nFlags, CPoint point);
//{{AFX_MSG(CCamWnd)
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnPaint();
afx_msg void OnDestroy();
afx_msg void OnClose();
+98 -13
View File
@@ -132,6 +132,15 @@ int Map_GetSelectedDecal(void) {
return s_selectedWorldDecal;
}
void Map_ClearSelectedDecal(void) {
if (s_selectedWorldDecal < 0) {
return;
}
s_selectedWorldDecal = -1;
Sys_UpdateWindows(W_CAMERA);
Sys_Status("Deselected decal\n", 0);
}
void Map_DeleteSelectedDecal(void) {
if (!g_qeglobals.rw || s_selectedWorldDecal < 0) {
return;
@@ -142,6 +151,64 @@ void Map_DeleteSelectedDecal(void) {
Sys_UpdateWindows(W_ALL);
}
bool Map_ScaleSelectedDecal(float scale) {
if (!g_qeglobals.rw || s_selectedWorldDecal < 0 || scale <= 0.0f) {
return false;
}
const renderWorldDecal_t* existing = g_qeglobals.rw->GetWorldDecal(s_selectedWorldDecal);
if (!existing) {
return false;
}
renderWorldDecal_t decal = *existing;
decal.size *= scale;
if (decal.size < 1.0f) {
decal.size = 1.0f;
}
else if (decal.size > 4096.0f) {
decal.size = 4096.0f;
}
if (!g_qeglobals.rw->UpdateWorldDecal(s_selectedWorldDecal, decal)) {
return false;
}
mapModified = 1;
Sys_UpdateWindows(W_CAMERA);
Sys_Status(va("Scaled decal to %.2f\n", decal.size), 0);
return true;
}
bool Map_RotateSelectedDecal(float degrees) {
if (!g_qeglobals.rw || s_selectedWorldDecal < 0) {
return false;
}
const renderWorldDecal_t* existing = g_qeglobals.rw->GetWorldDecal(s_selectedWorldDecal);
if (!existing) {
return false;
}
renderWorldDecal_t decal = *existing;
const float radians = degrees * idMath::M_DEG2RAD;
const float s = sin(radians);
const float c = cos(radians);
const idVec3 oldU = decal.axisU;
const idVec3 oldV = decal.axisV;
decal.axisU = oldU * c + oldV * s;
decal.axisV = oldV * c - oldU * s;
if (!g_qeglobals.rw->UpdateWorldDecal(s_selectedWorldDecal, decal)) {
return false;
}
mapModified = 1;
Sys_UpdateWindows(W_CAMERA);
Sys_Status(va("Rotated decal %.1f degrees\n", degrees), 0);
return true;
}
int Map_SelectDecalByRay(const idVec3& origin, const idVec3& dir) {
if (!g_qeglobals.rw) {
return -1;
@@ -154,25 +221,43 @@ int Map_SelectDecalByRay(const idVec3& origin, const idVec3& dir) {
if (!decal) {
continue;
}
const float halfSize = decal->size * 0.5f;
const float tolerance = idMath::ClampFloat(4.0f, 24.0f, decal->size * 0.125f);
const float depthTolerance = (decal->depth > 1.0f ? decal->depth : 1.0f) + tolerance;
float denom = dir * decal->normal;
if (fabs(denom) < 0.0001f) {
continue;
if (fabs(denom) >= 0.0001f) {
float t = ((decal->origin - origin) * decal->normal) / denom;
if (t > 0.0f && t < bestT) {
idVec3 hit = origin + dir * t;
idVec3 rel = hit - decal->origin;
float u = rel * decal->axisU;
float v = rel * decal->axisV;
if (fabs(u) <= halfSize + tolerance && fabs(v) <= halfSize + tolerance) {
bestT = t;
bestIndex = i;
}
}
}
float t = ((decal->origin - origin) * decal->normal) / denom;
if (t <= 0.0f || t >= bestT) {
continue;
}
idVec3 hit = origin + dir * t;
idVec3 rel = hit - decal->origin;
float u = rel * decal->axisU;
float v = rel * decal->axisV;
if (fabs(u) <= decal->size * 0.5f && fabs(v) <= decal->size * 0.5f) {
bestT = t;
bestIndex = i;
float centerT = (decal->origin - origin) * dir;
if (centerT > 0.0f && centerT < bestT) {
idVec3 closest = origin + dir * centerT;
idVec3 rel = closest - decal->origin;
float planeDist = fabs(rel * decal->normal);
float u = rel * decal->axisU;
float v = rel * decal->axisV;
if (planeDist <= depthTolerance && fabs(u) <= halfSize + tolerance && fabs(v) <= halfSize + tolerance) {
bestT = centerT;
bestIndex = i;
}
}
}
s_selectedWorldDecal = bestIndex;
Sys_UpdateWindows(W_CAMERA);
if (bestIndex >= 0) {
Sys_Status(va("Selected decal %i\n", bestIndex), 0);
}
return bestIndex;
}
+3
View File
@@ -61,6 +61,9 @@ void Map_LoadDecals(const char *filename);
void Map_SaveDecals(const char *filename);
int Map_AddDecal(const renderWorldDecal_t& decal);
void Map_DeleteSelectedDecal(void);
bool Map_ScaleSelectedDecal(float scale);
bool Map_RotateSelectedDecal(float degrees);
void Map_ClearSelectedDecal(void);
int Map_SelectDecalByRay(const idVec3& origin, const idVec3& dir);
int Map_GetSelectedDecal(void);