From 3f81a8a3d48ba0131fc7914cb37dcaf3977b2973 Mon Sep 17 00:00:00 2001
From: D-AIRY <admin@ds-servers.com>
Date: Thu, 16 Jan 2025 20:44:19 +0300
Subject: [PATCH] Allow using pivot to transform

---
 source/terrax/mainWindow.cpp        |  21 ++++++++++--
 source/terrax/resource.h            | Bin 24580 -> 24580 bytes
 source/terrax/resource/toolbar1.bmp | Bin 1080 -> 1300 bytes
 source/terrax/resource/toolbar2.bmp | Bin 1080 -> 1320 bytes
 source/terrax/terrax.cpp            |  51 ++++++++++++++++++++++++++++
 source/terrax/terrax.h              |   3 ++
 6 files changed, 72 insertions(+), 3 deletions(-)

diff --git a/source/terrax/mainWindow.cpp b/source/terrax/mainWindow.cpp
index 2b9b8270d..ab6b56873 100644
--- a/source/terrax/mainWindow.cpp
+++ b/source/terrax/mainWindow.cpp
@@ -2085,6 +2085,18 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
 			XSetXformType(X2DXF_ROTATE);
 			break;
 
+		case ID_XFORM_MODE_CENTER:
+			g_xConfig.m_bUsePivot = false;
+			CheckToolbarButton(ID_XFORM_MODE_PIVOT, g_xConfig.m_bUsePivot);
+			CheckToolbarButton(ID_XFORM_MODE_CENTER, !g_xConfig.m_bUsePivot);
+
+			break;
+		case ID_XFORM_MODE_PIVOT:
+			g_xConfig.m_bUsePivot = true;
+			CheckToolbarButton(ID_XFORM_MODE_PIVOT, g_xConfig.m_bUsePivot);
+			CheckToolbarButton(ID_XFORM_MODE_CENTER, !g_xConfig.m_bUsePivot);
+			break;
+
 		case IDC_TIE_TO_OBJECT:
 			if(IsWindowEnabled(g_hButtonToEntityWnd))
 			{
@@ -3418,7 +3430,7 @@ LRESULT CALLBACK RenderWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lP
 							{
 								// create rotate command
 								s_pRotateCmd = new CCommandRotate(GetKeyState(VK_SHIFT) < 0);
-								s_pRotateCmd->setStartOrigin((g_xState.vSelectionBoundMax + g_xState.vSelectionBoundMin) * 0.5f * vMask, float3(1.0f) - vMask);
+								s_pRotateCmd->setStartOrigin((g_xConfig.m_bUsePivot ? g_xState.vSelectionPivot : (g_xState.vSelectionBoundMax + g_xState.vSelectionBoundMin) * 0.5f) * vMask, float3(1.0f) - vMask);
 								s_pRotateCmd->setStartPos(vStartPos);
 								XEnumerateObjects([&](IXEditorObject *pObj, bool isProxy, ICompoundObject *pParent){
 									if(pObj->isSelected()/* && (g_xConfig.m_bIgnoreGroups ? !isProxy : !pParent)*/)
@@ -4729,7 +4741,7 @@ void XUpdateGizmos()
 {
 	if(Button_GetCheck(g_hABArrowButton) && g_xState.bHasSelection)
 	{
-		float3 vPos = (g_xState.vSelectionBoundMin + g_xState.vSelectionBoundMax) * 0.5f;
+		float3 vPos = g_xConfig.m_bUsePivot ? g_xState.vSelectionPivot : (g_xState.vSelectionBoundMin + g_xState.vSelectionBoundMax) * 0.5f;
 		g_pGizmoMove->setPos(vPos);
 		if(!g_gizmoRotateCallback.isActive())
 		{
@@ -4849,7 +4861,7 @@ HWND CreateToolbar(HWND hWndParent)
 {
 	// Declare and initialize local constants.
 	const int ImageListID = 0;
-	const int numButtons = 8;
+	const int numButtons = 10;
 	const int bitmapSize = 16;
 
 	const DWORD buttonStyles = BTNS_AUTOSIZE;
@@ -4909,6 +4921,9 @@ HWND CreateToolbar(HWND hWndParent)
 		{MAKELONG(1, ImageListID), ID_XFORM_TRANSLATE, TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)"Move [W]"},
 		{MAKELONG(2, ImageListID), ID_XFORM_ROTATE, TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)"Rotate [R]"},
 		{0, 0, TBSTATE_ENABLED, BTNS_SEP, 0L, 0},
+		{MAKELONG(8, ImageListID), ID_XFORM_MODE_CENTER, TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)"Use center"},
+		{MAKELONG(9, ImageListID), ID_XFORM_MODE_PIVOT, TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)"Use pivot"},
+		{0, 0, TBSTATE_ENABLED, BTNS_SEP, 0L, 0},
 		{MAKELONG(6, ImageListID), ID_TOOLS_GROUP, TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)"Group selected [Ctrl+G]"},
 		{MAKELONG(7, ImageListID), ID_TOOLS_UNGROUP, TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)"Ungroup selected [Ctrl+U]"},
 		{MAKELONG(5, ImageListID), ID_IGNORE_GROUPS, TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)"Toggle group ignore [Ctrl+W]"},
diff --git a/source/terrax/resource.h b/source/terrax/resource.h
index bdbd2937659c1c2521c71074b5341ca191f4b3a6..8b0c2331f8a92e4071aab850ebda31aae84af055 100644
GIT binary patch
delta 78
zcmZoUz}Rwtaf4mVWPxBFL0<-c1{VfbhIobm22X}C2LH()g=Hr@__Ix}i;+W9;mqL5
S;KvX$`Mv~L#pXLP8q5GcJ`}eA

delta 50
zcmZoUz}Rwtaf4mV<PQ-d?CuQy3_%Q`lOI~iPj>KUn_L$o2NIub<f#VYZ@v?w!3+S^
CR}tv|

diff --git a/source/terrax/resource/toolbar1.bmp b/source/terrax/resource/toolbar1.bmp
index dbb1f0b437a3419ade41cafc9834107c00921d22..eb7529bf301a6746730ae4c975acc9601860c34a 100644
GIT binary patch
literal 1300
zcmZ?r6=7ungCqt91`P%Vh6M}^4EziX42%pcVDU{X3=Bft5X{ZMzyQJ^iU0rqGyG>@
zU}ykg5CP-N|IcvXzyXGah6V<ZnhpeLhA^8Am@ObIbdrIAqoXY&4U9TEIvN-lIyyjh
zF)%Q2Fq9X7m>eKI4Pdgv7;H?lWk<(k5UUj=1=hv^QP9BA!O)Qb5pC<}@Mus{=IDTE
z?ciYVXa~uFxlrXG>pD7&LBb%SH3G!y=wRsRfG7Z&&5;3dS6fF%OM{ZKK?euegbt31
zii(Pk4v6s#V2z#4ogf<Iv<{HhTSGxyxOrf|fei+`s|99W16UPDMSBO-JdiKIVjx30
znrDF6ofaJ(t)UPHLfyvzawP-UeM-s(Z5=Kh5chR(lvi{hyN>~EX$NOVb4Mo!*kv6Z
z9boey4uqQrW@IohFo4bL=xFHZFzDduXa|KHQuu*lt)rtE91hGNMIZ-qfZPXhAlMj?
zw;>EBB?C~nfg+cqqoISN0~~T39OV@t6F49--qB&qY~0*z%xnP(4p6wZhK7P22r;IE
z0TMA8OiBhF9R_WnK!lhH4mnV?fV4u)<3P9%9EdRgf&&o4JY$ZI29S9M5RX9J2Z>yW
zc_0Z`_{{(XAtXM)&TC*`0H;5w`#PAEj2RdhAd%7G;sFakkY^jf=>-yg9UUNZIzZ`f
z0)&YpemTHF4RN0b*Z@$-f&2^NfZYdjUPlKs{Y|!H1{n&94~RJ2za0z=4ICK|N3?+>
z7h)bLrXl8mqNjra<Wx}FhvbhA17m1@1BV_cVnHbnVm63``Ue!bEg&a>#5f@4fpZHa
zcYu5f&KjM}#-P~k=z!&4kX0aWfb?}B@-HZIA#nhbfSL!=33VPM&^kH{j6s<fqy`m3
Vt%VjZX)sApp#m;nAPf!$1^`%q@Av=!

literal 1080
zcmZ?rwP0ZYgE9sN1`P%Vh6V-(27U$x21W*m#35z|1|e<;1}lUU4G_f*4PZSWvY~;2
z;lP0d3=JR#!+!=4V)*|bLNWXYF&X~<|Ibj403{G+i35u}goRErFtC>wsVjj|d3kvO
z15|ebJ6NR)SX+rld3g(nRRtCVDPxByC}1yVC|8Gw7L}K$7o0f)(Fm1>80P{~03xcE
zgIML|4CUnz1t2Tg)j<X_loyqkmld2j;s90xaYjW+1&9Z-x*X({s#PE^Tt7p3IRgXO
zAh5k<aQz@xfmMPum6x=G*cI;O<yEU74uIOv0CEKb*#0v|9E!@*%Axi{3@zs<FDb8J
z2isg;UJlj|aR6LDn4!+VzyQ`?US3dM4st&zRLjdtz(K(R5(GJb9b`Ym0bp$)Z$KD4
zXB<HO1_eGlST!gSl$X1(xRjK*u(%h1beESy^n)D$(N@j?2}pIGGY;kD4n?3q0GR;_
z6$W;M{op`=xgQ(=82Vk<%L_pIL5?p6>j&8h^M5-i2q57Hw!VOY0UZBO`^$OGxG*p<
zKm#u=9i$)Z^>UED^73+*@&*VKNBFaYg9>7QI>=&3vMDcz#(#?k3&;>q_<>|W0SAgX
zknch9uU-HSoFZ`GL-a$fhlECXxq}Nd{epcD*AFoa>|d}3P~ew=1R<#p<Z^J*t6*^f
tg?@QCEd7I%E!Y^4YIypGSqD)Jl7a-ig9|9}gZQWzY7I2|DZwNe7yyqTjnDu9

diff --git a/source/terrax/resource/toolbar2.bmp b/source/terrax/resource/toolbar2.bmp
index 5051572bc72dbbd0c0b1a1af2b48dcd827182f2d..e581a1f306259eb0cd1bb90037e55cad7a7a2a8e 100644
GIT binary patch
delta 352
zcmdnNv4Ts*$ybAw0Sw9*7#K7d7#J2XFfi~lFfcGOuz<xku}oB%BmibYBtT65iQ60m
z`2T|eKg0hIAO`=$Zw><d5J7$r`2YVu|70^p2LXPt{C|G_5C1{Z{FBQ-g8%s$`2YV0
zDTD}a0}Flt83s3U@;9&`NYQ_Oeg?4V{FBX?K$e3|1Brl4=bu~#5#;CR{|^!b*~CA2
z8<PV+|9^;?AX`5C|3CRFlOxFKAj7~4|MP>K$qX{`|Ns9VAi6>H<T98b$dw>3{^#eP
YybUf0RSELtH)bbD5P?Dt96$^V0Q(M`WB>pF

delta 99
zcmZ3%wSz;&$=8B~0Sw9*7#K7d7#JED7#R2&7#J8CAQFd|C#p=Ec+6q47^B1FG)9NX
x+ZY`tvoSeL4r6kdyo||V@;4@j$!^RJlczB|On%1fFxia7VR9Rb!{lo$P5{l57>)n{

diff --git a/source/terrax/terrax.cpp b/source/terrax/terrax.cpp
index 3b1fa6d3f..fe5e84fee 100644
--- a/source/terrax/terrax.cpp
+++ b/source/terrax/terrax.cpp
@@ -736,6 +736,8 @@ int main(int argc, char **argv)
 	g_pXformEventChannel = pEngine->getCore()->getEventChannel<XEventEditorXformType>(EVENT_EDITOR_XFORM_TYPE_GUID);
 
 	XSetXformType(X2DXF_SCALE);
+	CheckToolbarButton(ID_XFORM_MODE_PIVOT, g_xConfig.m_bUsePivot);
+	CheckToolbarButton(ID_XFORM_MODE_CENTER, !g_xConfig.m_bUsePivot);
 
 	RECT rcTopLeft;
 	GetClientRect(g_hTopLeftWnd, &rcTopLeft);
@@ -945,6 +947,20 @@ int main(int argc, char **argv)
 						}
 					}
 
+					szVal = pCfg->getKey("terrax", "xform_use_pivot");
+					if(szVal)
+					{
+						int iVal = 0;
+						if(sscanf(szVal, "%d", &iVal))
+						{
+							g_xConfig.m_bUsePivot = iVal != 0;
+							CheckToolbarButton(ID_XFORM_MODE_PIVOT, g_xConfig.m_bUsePivot);
+							CheckToolbarButton(ID_XFORM_MODE_CENTER, !g_xConfig.m_bUsePivot);
+						}
+					}
+
+					
+
 					for(UINT i = 0; i < 4; ++i)
 					{
 						float3 vec;
@@ -1094,6 +1110,9 @@ int main(int argc, char **argv)
 
 				sprintf_s(szVal, "%d", g_xConfig.m_bIgnoreGroups ? 1 : 0);
 				pCfg->set("terrax", "ignore_groups", szVal);
+
+				sprintf_s(szVal, "%d", g_xConfig.m_bUsePivot ? 1 : 0);
+				pCfg->set("terrax", "xform_use_pivot", szVal);
 				
 				pCfg->save();
 				mem_release(pCfg);
@@ -2633,21 +2652,53 @@ void XUpdateSelectionBound()
 	g_xState.bHasSelection = false;
 	float3 vMin, vMax;
 
+	extern HWND g_hABArrowButton;
+	bool bTrackPivotObject = g_xConfig.m_bUsePivot && g_xState.activeWindow == XWP_TOP_LEFT && Button_GetCheck(g_hABArrowButton);
+	float fBestDist = FLT_MAX;
+
 	XEnumerateObjects([&](IXEditorObject *pObj, bool isProxy, ICompoundObject *pParent){
 		if(pObj->isSelected()/* && !(g_xConfig.m_bIgnoreGroups && isProxy)*/)
 		{
 			pObj->getBound(&vMin, &vMax);
+
+			if(bTrackPivotObject)
+			{
+				// raytest
+				g_xState.vWorldRayStart;
+				g_xState.vWorldRayDir;
+				if(pObj->hasVisualModel())
+				{
+					float3 vPos;
+					if(pObj->rayTest(g_xState.vWorldRayStart, g_xState.vWorldRayStart + g_xState.vWorldRayDir * 1000.0f, &vPos))
+					{
+						float fDist2 = SMVector3Length2(g_xState.vWorldRayStart - vPos);
+						if(fDist2 < fBestDist)
+						{
+							fBestDist = fDist2;
+							g_xState.pPivotSource = pObj;
+						}
+					}
+				}
+			}
+
 			if(!g_xState.bHasSelection)
 			{
 				g_xState.bHasSelection = true;
 				g_xState.vSelectionBoundMax = vMax;
 				g_xState.vSelectionBoundMin = vMin;
+				g_xState.vSelectionPivot = pObj->getPos();
 			}
 			else
 			{
 				g_xState.vSelectionBoundMax = (float3)SMVectorMax(g_xState.vSelectionBoundMax, vMax);
 				g_xState.vSelectionBoundMin = (float3)SMVectorMin(g_xState.vSelectionBoundMin, vMin);
 			}
+
+			if(g_xState.pPivotSource == pObj)
+			{
+				g_xState.vSelectionPivot = pObj->getPos();
+			}
+
 			return(XEOR_SKIP_CHILDREN);
 		}
 		return(XEOR_CONTINUE);
diff --git a/source/terrax/terrax.h b/source/terrax/terrax.h
index 5b96dbd41..0185bbc4a 100644
--- a/source/terrax/terrax.h
+++ b/source/terrax/terrax.h
@@ -92,6 +92,7 @@ struct CTerraXConfig
 	bool m_bDottedGrid = false;
 	float m_fGridOpacity = 0.5f;
 	bool m_bIgnoreGroups = false;
+	bool m_bUsePivot = false;
 
 	X_VIEWPORT_LAYOUT m_xViewportLayout = XVIEW_2X2;
 };
@@ -108,6 +109,8 @@ struct CTerraXState: public TerraXState
 	bool bHasSelection = false;
 	float3_t vSelectionBoundMin;
 	float3_t vSelectionBoundMax;
+	float3_t vSelectionPivot;
+	IXEditorObject *pPivotSource = NULL;
 
 	X_2DXFORM_TYPE xformType = X2DXF_SCALE;
 
-- 
GitLab