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