1 /*
2  Copyright (C) 2010-2014 Kristian Duske
3 
4  This file is part of TrenchBroom.
5 
6  TrenchBroom is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  TrenchBroom is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with TrenchBroom. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "UVShearTool.h"
21 
22 #include "Model/BrushFace.h"
23 #include "Model/ChangeBrushFaceAttributesRequest.h"
24 #include "Model/PickResult.h"
25 #include "View/InputState.h"
26 #include "View/MapDocument.h"
27 #include "View/UVViewHelper.h"
28 
29 namespace TrenchBroom {
30     namespace View {
31         const Model::Hit::HitType UVShearTool::XHandleHit = Model::Hit::freeHitType();
32         const Model::Hit::HitType UVShearTool::YHandleHit = Model::Hit::freeHitType();
33 
UVShearTool(MapDocumentWPtr document,UVViewHelper & helper)34         UVShearTool::UVShearTool(MapDocumentWPtr document, UVViewHelper& helper) :
35         ToolControllerBase(),
36         Tool(true),
37         m_document(document),
38         m_helper(helper) {}
39 
doGetTool()40         Tool* UVShearTool::doGetTool() {
41             return this;
42         }
43 
doPick(const InputState & inputState,Model::PickResult & pickResult)44         void UVShearTool::doPick(const InputState& inputState, Model::PickResult& pickResult) {
45             static const Model::Hit::HitType HitTypes[] = { XHandleHit, YHandleHit };
46             if (m_helper.valid())
47                 m_helper.pickTextureGrid(inputState.pickRay(), HitTypes, pickResult);
48         }
49 
doStartMouseDrag(const InputState & inputState)50         bool UVShearTool::doStartMouseDrag(const InputState& inputState) {
51             assert(m_helper.valid());
52 
53             if (!inputState.modifierKeysPressed(ModifierKeys::MKAlt) ||
54                 !inputState.mouseButtonsPressed(MouseButtons::MBLeft))
55                 return false;
56 
57             const Model::PickResult& pickResult = inputState.pickResult();
58             const Model::Hit& xHit = pickResult.query().type(XHandleHit).occluded().first();
59             const Model::Hit& yHit = pickResult.query().type(YHandleHit).occluded().first();
60 
61             if (!(xHit.isMatch() ^ yHit.isMatch()))
62                 return false;
63 
64             m_selector = Vec2b(xHit.isMatch(), yHit.isMatch());
65 
66             const Model::BrushFace* face = m_helper.face();
67             m_xAxis = face->textureXAxis();
68             m_yAxis = face->textureYAxis();
69             m_initialHit = m_lastHit = getHit(inputState.pickRay());
70 
71             MapDocumentSPtr document = lock(m_document);
72             document->beginTransaction("Shear Texture");
73             return true;
74         }
75 
doMouseDrag(const InputState & inputState)76         bool UVShearTool::doMouseDrag(const InputState& inputState) {
77             const Vec2f currentHit = getHit(inputState.pickRay());
78             const Vec2f delta = currentHit - m_lastHit;
79 
80             Model::BrushFace* face = m_helper.face();
81             const Vec3 origin = m_helper.origin();
82             const Vec2f oldCoords = face->toTexCoordSystemMatrix(Vec2f::Null, face->scale(), true) * origin;
83 
84             MapDocumentSPtr document = lock(m_document);
85             if (m_selector[0]) {
86                 const Vec2f factors = Vec2f(-delta.y() / m_initialHit.x(), 0.0f);
87                 if (!factors.null())
88                     document->shearTextures(factors);
89             } else if (m_selector[1]) {
90                 const Vec2f factors = Vec2f(0.0f, -delta.x() / m_initialHit.y());
91                 if (!factors.null())
92                     document->shearTextures(factors);
93             }
94 
95             const Vec2f newCoords = face->toTexCoordSystemMatrix(Vec2f::Null, face->scale(), true) * origin;
96             const Vec2f newOffset = face->offset() + oldCoords - newCoords;
97 
98             Model::ChangeBrushFaceAttributesRequest request;
99             request.setOffset(newOffset);
100             document->setFaceAttributes(request);
101 
102             const Vec3 newOrigin = face->toTexCoordSystemMatrix(Vec2f::Null, Vec2f::One, true) * origin;
103             m_helper.setOrigin(newOrigin);
104 
105             m_lastHit = currentHit;
106             return true;
107         }
108 
doEndMouseDrag(const InputState & inputState)109         void UVShearTool::doEndMouseDrag(const InputState& inputState) {
110             MapDocumentSPtr document = lock(m_document);
111             document->commitTransaction();
112         }
113 
doCancelMouseDrag()114         void UVShearTool::doCancelMouseDrag() {
115             MapDocumentSPtr document = lock(m_document);
116             document->cancelTransaction();
117         }
118 
getHit(const Ray3 & pickRay) const119         Vec2f UVShearTool::getHit(const Ray3& pickRay) const {
120             const Model::BrushFace* face = m_helper.face();
121             const Plane3& boundary = face->boundary();
122             const FloatType hitPointDist = boundary.intersectWithRay(pickRay);
123             const Vec3 hitPoint = pickRay.pointAtDistance(hitPointDist);
124             const Vec3 hitVec = hitPoint - m_helper.origin();
125 
126             return Vec2f(hitVec.dot(m_xAxis),
127                          hitVec.dot(m_yAxis));
128         }
129 
doCancel()130         bool UVShearTool::doCancel() {
131             return false;
132         }
133     }
134 }
135