From b9e1ca3150cb9553bfbb7a15c90761e9a400f6b9 Mon Sep 17 00:00:00 2001
From: McCabe Maxsted
Date: Fri, 30 Apr 2010 04:43:58 -0700
Subject: Applied patch for SNOW-586 by Thickbrick Sleaford: Add option to
 align textures across (planar-mapped, co-planar) faces

Was accidentally left out of a rebase
---
 linden/indra/newview/llface.cpp                    |  66 +++++++++
 linden/indra/newview/llface.h                      |   3 +
 linden/indra/newview/llpanelface.cpp               | 161 ++++++++++++++++++++-
 linden/indra/newview/llpanelface.h                 |   1 +
 .../skins/default/xui/en-us/floater_tools.xml      |   4 +
 5 files changed, 233 insertions(+), 2 deletions(-)

diff --git a/linden/indra/newview/llface.cpp b/linden/indra/newview/llface.cpp
index dab1dac..aa8cd15 100644
--- a/linden/indra/newview/llface.cpp
+++ b/linden/indra/newview/llface.cpp
@@ -801,6 +801,72 @@ LLVector2 LLFace::surfaceToTexture(LLVector2 surface_coord, LLVector3 position,
 	return tc;
 }
 
+// Returns scale compared to default texgen, and face orientation as calculated
+// by planarProjection(). This is needed to match planar texgen parameters.
+void LLFace::getPlanarProjectedParams(LLQuaternion* face_rot, LLVector3* face_pos, F32* scale) const
+{
+	const LLVolumeFace& vf = getViewerObject()->getVolume()->getVolumeFace(mTEOffset);
+	LLVector3 normal = vf.mVertices[0].mNormal;
+	LLVector3 binormal = vf.mVertices[0].mBinormal;
+	LLVector2 projected_binormal;
+	planarProjection(projected_binormal, normal, vf.mCenter, binormal);
+	projected_binormal -= LLVector2(0.5f, 0.5f); // this normally happens in xform()
+	*scale = projected_binormal.length();
+	// rotate binormal to match what planarProjection() thinks it is,
+	// then find face's rotation from normal and rotated binormal:
+	projected_binormal.normalize();
+	F32 ang = acos(projected_binormal.mV[VY]);
+	ang = (projected_binormal.mV[VX] < 0.f) ? -ang : ang;
+	binormal.rotVec(ang, normal);
+	LLQuaternion local_rot( binormal % normal, binormal, normal );
+	*face_rot = local_rot * mXform->getWorldRotation();
+	*face_pos = mXform->getWorldPosition();
+}
+
+// Returns the necessary texture transform to align this face's TE to align_to's TE
+bool LLFace::calcAlignedPlanarTE(const LLFace* align_to,  LLVector2* res_st_offset,
+								 LLVector2* res_st_scale, F32* res_st_rot) const
+{
+	if (!align_to)
+	{
+		return false;
+	}
+	const LLTextureEntry *orig_tep = align_to->getTextureEntry();
+	if ((orig_tep->getTexGen() != LLTextureEntry::TEX_GEN_PLANAR) ||
+		(getTextureEntry()->getTexGen() != LLTextureEntry::TEX_GEN_PLANAR))
+	{
+		return false;
+	}
+
+	LLVector3 orig_pos, this_pos;
+	LLQuaternion orig_face_rot, this_face_rot;
+	F32 orig_proj_scale, this_proj_scale;
+	align_to->getPlanarProjectedParams(&orig_face_rot, &orig_pos, &orig_proj_scale);
+	getPlanarProjectedParams(&this_face_rot, &this_pos, &this_proj_scale);
+
+	// The rotation of "this face's" texture:
+	LLQuaternion orig_st_rot = LLQuaternion(orig_tep->getRotation(), LLVector3::z_axis) * orig_face_rot;
+	LLQuaternion this_st_rot = orig_st_rot * ~this_face_rot;
+	F32 x_ang, y_ang, z_ang;
+	this_st_rot.getEulerAngles(&x_ang, &y_ang, &z_ang);
+	*res_st_rot = z_ang;
+
+	// Offset and scale of "this face's" texture:
+	LLVector3 centers_dist = (this_pos - orig_pos) * ~orig_st_rot;
+	LLVector3 st_scale(orig_tep->mScaleS, orig_tep->mScaleT, 1.f);
+	st_scale *= orig_proj_scale;
+	centers_dist.scaleVec(st_scale);
+	LLVector2 orig_st_offset(orig_tep->mOffsetS, orig_tep->mOffsetT);
+
+	*res_st_offset = orig_st_offset + (LLVector2)centers_dist;
+	res_st_offset->mV[VX] -= (S32)res_st_offset->mV[VX];
+	res_st_offset->mV[VY] -= (S32)res_st_offset->mV[VY];
+
+	st_scale /= this_proj_scale;
+	*res_st_scale = (LLVector2)st_scale;
+	return true;
+}
+
 void LLFace::updateRebuildFlags()
 {
 	if (!mDrawablep->isState(LLDrawable::REBUILD_VOLUME))
diff --git a/linden/indra/newview/llface.h b/linden/indra/newview/llface.h
index 5e422b4..4893e82 100644
--- a/linden/indra/newview/llface.h
+++ b/linden/indra/newview/llface.h
@@ -93,6 +93,9 @@ public:
 	BOOL			hasGeometry()		const	{ return mGeomCount > 0; }
 	LLVector3		getPositionAgent()	const;
 	LLVector2       surfaceToTexture(LLVector2 surface_coord, LLVector3 position, LLVector3 normal);
+	void 			getPlanarProjectedParams(LLQuaternion* face_rot, LLVector3* face_pos, F32* scale) const;
+	bool			calcAlignedPlanarTE(const LLFace* align_to, LLVector2* st_offset,
+										LLVector2* st_scale, F32* st_rot) const;
 	
 	U32				getState()			const	{ return mState; }
 	void			setState(U32 state)			{ mState |= state; }
diff --git a/linden/indra/newview/llpanelface.cpp b/linden/indra/newview/llpanelface.cpp
index 6ae2ffe..bd9edec 100644
--- a/linden/indra/newview/llpanelface.cpp
+++ b/linden/indra/newview/llpanelface.cpp
@@ -48,7 +48,9 @@
 #include "llcheckboxctrl.h"
 #include "llcolorswatch.h"
 #include "llcombobox.h"
+#include "lldrawable.h"
 #include "lldrawpoolbump.h"
+#include "llface.h"
 #include "lllineeditor.h"
 #include "llresmgr.h"
 #include "llselectmgr.h"
@@ -173,6 +175,7 @@ BOOL	LLPanelFace::postBuild()
 	
 	childSetCommitCallback("combobox shininess",&LLPanelFace::onCommitShiny,this);
 	childSetCommitCallback("combobox bumpiness",&LLPanelFace::onCommitBump,this);
+	childSetCommitCallback("checkbox planar align",&LLPanelFace::onCommitPlanarAlign, this);
 	childSetCommitCallback("TexScaleU",&LLPanelFace::onCommitTextureInfo, this);
 	childSetCommitCallback("checkbox flip s",&LLPanelFace::onCommitTextureInfo, this);
 	childSetCommitCallback("TexScaleV",&LLPanelFace::onCommitTextureInfo, this);
@@ -364,6 +367,93 @@ private:
 	LLPanelFace* mPanel;
 };
 
+// Functor that aligns a face to mCenterFace
+struct LLPanelFaceSetAlignedTEFunctor : public LLSelectedTEFunctor
+{
+	LLPanelFaceSetAlignedTEFunctor(LLPanelFace* panel, LLFace* center_face) :
+		mPanel(panel),
+		mCenterFace(center_face) {}
+
+	virtual bool apply(LLViewerObject* object, S32 te)
+	{
+		LLFace* facep = object->mDrawable->getFace(te);
+		if (!facep)
+		{
+			return true;
+		}
+
+		bool set_aligned = true;
+		if (facep == mCenterFace)
+		{
+			set_aligned = false;
+		}
+		if (set_aligned)
+		{
+			LLVector2 uv_offset, uv_scale;
+			F32 uv_rot;
+			set_aligned = facep->calcAlignedPlanarTE(mCenterFace, &uv_offset, &uv_scale, &uv_rot);
+			if (set_aligned)
+			{
+				object->setTEOffset(te, uv_offset.mV[VX], uv_offset.mV[VY]);
+				object->setTEScale(te, uv_scale.mV[VX], uv_scale.mV[VY]);
+				object->setTERotation(te, uv_rot);
+			}
+		}
+		if (!set_aligned)
+		{
+			LLPanelFaceSetTEFunctor setfunc(mPanel);
+			setfunc.apply(object, te);
+		}
+		return true;
+	}
+private:
+	LLPanelFace* mPanel;
+	LLFace* mCenterFace;
+};
+
+// Functor that tests if a face is aligned to mCenterFace
+struct LLPanelFaceGetIsAlignedTEFunctor : public LLSelectedTEFunctor
+{
+	LLPanelFaceGetIsAlignedTEFunctor(LLFace* center_face) :
+		mCenterFace(center_face) {}
+
+	virtual bool apply(LLViewerObject* object, S32 te)
+	{
+		LLFace* facep = object->mDrawable->getFace(te);
+		if (!facep)
+		{
+			return false;
+		}
+		if (facep == mCenterFace)
+		{
+			return true;
+		}
+
+		LLVector2 aligned_st_offset, aligned_st_scale;
+		F32 aligned_st_rot;
+		if ( facep->calcAlignedPlanarTE(mCenterFace, &aligned_st_offset, &aligned_st_scale, &aligned_st_rot) )
+		{
+			const LLTextureEntry* tep = facep->getTextureEntry();
+			LLVector2 st_offset, st_scale;
+			tep->getOffset(&st_offset.mV[VX], &st_offset.mV[VY]);
+			tep->getScale(&st_scale.mV[VX], &st_scale.mV[VY]);
+			F32 st_rot = tep->getRotation();
+			// needs a fuzzy comparison, because of fp errors
+			if (is_approx_equal_fraction(st_offset.mV[VX], aligned_st_offset.mV[VX], 14) &&
+				is_approx_equal_fraction(st_offset.mV[VY], aligned_st_offset.mV[VY], 14) &&
+				is_approx_equal_fraction(st_scale.mV[VX], aligned_st_scale.mV[VX], 14) &&
+				is_approx_equal_fraction(st_scale.mV[VY], aligned_st_scale.mV[VY], 14) &&
+				is_approx_equal_fraction(st_rot, aligned_st_rot, 10))
+			{
+				return true;
+			}
+		}
+		return false;
+	}
+private:
+	LLFace* mCenterFace;
+};
+
 struct LLPanelFaceSendFunctor : public LLSelectedObjectFunctor
 {
 	virtual bool apply(LLViewerObject* object)
@@ -375,8 +465,26 @@ struct LLPanelFaceSendFunctor : public LLSelectedObjectFunctor
 
 void LLPanelFace::sendTextureInfo()
 {
-	LLPanelFaceSetTEFunctor setfunc(this);
-	LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc);
+	if ((bool)childGetValue("checkbox planar align").asBoolean())
+	{
+		struct f1 : public LLSelectedTEGetFunctor<LLFace *>
+		{
+			LLFace* get(LLViewerObject* object, S32 te)
+			{
+				return (object->mDrawable) ? object->mDrawable->getFace(te): NULL;
+			}
+		} get_last_face_func;
+		LLFace* last_face;
+		LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&get_last_face_func, last_face);
+
+		LLPanelFaceSetAlignedTEFunctor setfunc(this, last_face);
+		LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc);
+	}
+	else
+	{
+		LLPanelFaceSetTEFunctor setfunc(this);
+		LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc);
+	}
 
 	LLPanelFaceSendFunctor sendfunc;
 	LLSelectMgr::getInstance()->getSelection()->applyToObjects(&sendfunc);
@@ -483,6 +591,43 @@ void LLPanelFace::getState()
 			}
 		}
 
+		// planar align
+		bool align_planar = false;
+		bool identical_planar_aligned = false;
+		{
+			LLCheckBoxCtrl*	cb_planar_align = getChild<LLCheckBoxCtrl>("checkbox planar align");
+			align_planar = (cb_planar_align && cb_planar_align->get());
+			struct f1 : public LLSelectedTEGetFunctor<bool>
+			{
+				bool get(LLViewerObject* object, S32 face)
+				{
+					return (object->getTE(face)->getTexGen() == LLTextureEntry::TEX_GEN_PLANAR);
+				}
+			} func;
+
+			bool is_planar;
+			bool texgens_identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, is_planar );
+			bool enabled = (editable && texgens_identical && is_planar);
+			childSetValue("checkbox planar align", align_planar && enabled);
+			childSetEnabled("checkbox planar align", enabled);
+
+			if (align_planar && enabled)
+			{
+				struct f2 : public LLSelectedTEGetFunctor<LLFace *>
+				{
+					LLFace* get(LLViewerObject* object, S32 te)
+					{
+						return (object->mDrawable) ? object->mDrawable->getFace(te): NULL;
+					}
+				} get_te_face_func;
+				LLFace* last_face;
+				LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&get_te_face_func, last_face);
+				LLPanelFaceGetIsAlignedTEFunctor get_is_aligend_func(last_face);
+				// this will determine if the texture param controls are tentative:
+				identical_planar_aligned = LLSelectMgr::getInstance()->getSelection()->applyToTEs(&get_is_aligend_func);
+			}
+		}
+
 		// Texture scale
 		{
 			childSetEnabled("tex scale",editable);
@@ -496,6 +641,7 @@ void LLPanelFace::getState()
 				}
 			} func;
 			identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, scale_s );
+			identical = align_planar ? identical_planar_aligned : identical;
 			childSetValue("TexScaleU",editable ? llabs(scale_s) : 0);
 			childSetTentative("TexScaleU",LLSD((BOOL)(!identical)));
 			childSetEnabled("TexScaleU",editable);
@@ -514,6 +660,7 @@ void LLPanelFace::getState()
 				}
 			} func;
 			identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, scale_t );
+			identical = align_planar ? identical_planar_aligned : identical;
 
 			childSetValue("TexScaleV",llabs(editable ? llabs(scale_t) : 0));
 			childSetTentative("TexScaleV",LLSD((BOOL)(!identical)));
@@ -535,6 +682,7 @@ void LLPanelFace::getState()
 				}
 			} func;
 			identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, offset_s );
+			identical = align_planar ? identical_planar_aligned : identical;
 			childSetValue("TexOffsetU", editable ? offset_s : 0);
 			childSetTentative("TexOffsetU",!identical);
 			childSetEnabled("TexOffsetU",editable);
@@ -550,6 +698,7 @@ void LLPanelFace::getState()
 				}
 			} func;
 			identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, offset_t );
+			identical = align_planar ? identical_planar_aligned : identical;
 			childSetValue("TexOffsetV", editable ? offset_t : 0);
 			childSetTentative("TexOffsetV",!identical);
 			childSetEnabled("TexOffsetV",editable);
@@ -567,6 +716,7 @@ void LLPanelFace::getState()
 				}
 			} func;
 			identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, rotation );
+			identical = align_planar ? identical_planar_aligned : identical;
 			childSetValue("TexRot", editable ? rotation * RAD_TO_DEG : 0);
 			childSetTentative("TexRot",!identical);
 			childSetEnabled("TexRot",editable);
@@ -999,6 +1149,13 @@ void LLPanelFace::onClickAutoFix(void* userdata)
 }
 
 // static
+void LLPanelFace::onCommitPlanarAlign(LLUICtrl* ctrl, void* userdata)
+{
+	LLPanelFace* self = (LLPanelFace*) userdata;
+	self->getState();
+}
+
+// static
 void LLPanelFace::onClickTextureConstants(void *)
 {
 	LLNotifications::instance().add("ClickTextureConstants");
diff --git a/linden/indra/newview/llpanelface.h b/linden/indra/newview/llpanelface.h
index 33c84c8..8e4d4db 100644
--- a/linden/indra/newview/llpanelface.h
+++ b/linden/indra/newview/llpanelface.h
@@ -86,6 +86,7 @@ protected:
 	static void		onCommitShiny(			LLUICtrl* ctrl, void* userdata);
 	static void		onCommitFullbright(		LLUICtrl* ctrl, void* userdata);
 	static void     onCommitGlow(           LLUICtrl* ctrl, void *userdata);
+	static void		onCommitPlanarAlign(	LLUICtrl* ctrl, void* userdata);
 
 	static void		onClickApply(void*);
 	static void		onClickAutoFix(void*);
diff --git a/linden/indra/newview/skins/default/xui/en-us/floater_tools.xml b/linden/indra/newview/skins/default/xui/en-us/floater_tools.xml
index ce08d4c..78eacf4 100644
--- a/linden/indra/newview/skins/default/xui/en-us/floater_tools.xml
+++ b/linden/indra/newview/skins/default/xui/en-us/floater_tools.xml
@@ -1157,6 +1157,10 @@
 					weave
 				</combo_item>
 			</combo_box>
+			<check_box bottom="-148" follows="left|top" font="SansSerifSmall" height="16"
+			     initial_value="false" enabled="false" label="Align across planar faces"
+			     left="8" mouse_opaque="true" name="checkbox planar align" width="160"
+			     tool_tip="When checked, aligns textures on all selected faces to the last selected face. Requires Planar texture mapping." />
 			<text bg_visible="false" border_drop_shadow_visible="false" border_visible="false"
 			     bottom="-158" drop_shadow_visible="true" follows="left|top"
 			     font="SansSerifSmall" h_pad="0" halign="left" height="10" left="10"
-- 
cgit v1.1