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 "UVOffsetTool.h"
21 
22 #include "Model/BrushFace.h"
23 #include "Model/BrushGeometry.h"
24 #include "Model/ChangeBrushFaceAttributesRequest.h"
25 #include "View/InputState.h"
26 #include "View/MapDocument.h"
27 #include "View/UVView.h"
28 
29 #include <cassert>
30 
31 namespace TrenchBroom {
32     namespace View {
UVOffsetTool(MapDocumentWPtr document,const UVViewHelper & helper)33         UVOffsetTool::UVOffsetTool(MapDocumentWPtr document, const UVViewHelper& helper) :
34         ToolControllerBase(),
35         Tool(true),
36         m_document(document),
37         m_helper(helper) {}
38 
doGetTool()39         Tool* UVOffsetTool::doGetTool() {
40             return this;
41         }
42 
doStartMouseDrag(const InputState & inputState)43         bool UVOffsetTool::doStartMouseDrag(const InputState& inputState) {
44             assert(m_helper.valid());
45 
46             if (!inputState.modifierKeysPressed(ModifierKeys::MKNone) ||
47                 !inputState.mouseButtonsPressed(MouseButtons::MBLeft))
48                 return false;
49 
50             m_lastPoint = computeHitPoint(inputState.pickRay());
51 
52             MapDocumentSPtr document = lock(m_document);
53             document->beginTransaction("Move Texture");
54             return true;
55         }
56 
doMouseDrag(const InputState & inputState)57         bool UVOffsetTool::doMouseDrag(const InputState& inputState) {
58             assert(m_helper.valid());
59 
60             const Vec2f curPoint = computeHitPoint(inputState.pickRay());
61             const Vec2f delta    = curPoint - m_lastPoint;
62             const Vec2f snapped  = snapDelta(delta);
63 
64             const Model::BrushFace* face = m_helper.face();
65             const Vec2f corrected = (face->offset() - snapped).corrected(4, 0.0f);
66 
67             if (corrected == face->offset())
68                 return true;
69 
70             Model::ChangeBrushFaceAttributesRequest request;
71             request.setOffset(corrected);
72 
73             MapDocumentSPtr document = lock(m_document);
74             document->setFaceAttributes(request);
75 
76             m_lastPoint += snapped;
77             return true;
78         }
79 
doEndMouseDrag(const InputState & inputState)80         void UVOffsetTool::doEndMouseDrag(const InputState& inputState) {
81             MapDocumentSPtr document = lock(m_document);
82             document->commitTransaction();
83         }
84 
doCancelMouseDrag()85         void UVOffsetTool::doCancelMouseDrag() {
86             MapDocumentSPtr document = lock(m_document);
87             document->cancelTransaction();
88         }
89 
computeHitPoint(const Ray3 & ray) const90         Vec2f UVOffsetTool::computeHitPoint(const Ray3& ray) const {
91             const Model::BrushFace* face = m_helper.face();
92             const Plane3& boundary = face->boundary();
93             const FloatType distance = boundary.intersectWithRay(ray);
94             const Vec3 hitPoint = ray.pointAtDistance(distance);
95 
96             const Mat4x4 transform = face->toTexCoordSystemMatrix(Vec2f::Null, face->scale(), true);
97             return Vec2f(transform * hitPoint);
98         }
99 
snapDelta(const Vec2f & delta) const100         Vec2f UVOffsetTool::snapDelta(const Vec2f& delta) const {
101             const Model::BrushFace* face = m_helper.face();
102             assert(face != NULL);
103 
104             const Assets::Texture* texture = face->texture();
105             if (texture == NULL)
106                 return delta.rounded();
107 
108             const Mat4x4 transform = face->toTexCoordSystemMatrix(face->offset() - delta, face->scale(), true);
109 
110             Vec2f distance = Vec2f::Max;
111             const Model::BrushFace::VertexList vertices = face->vertices();
112             Model::BrushFace::VertexList::const_iterator it, end;
113             for (it = vertices.begin(), end = vertices.end(); it != end; ++it) {
114                 const Model::BrushVertex* vertex = *it;
115                 const Vec2f temp = m_helper.computeDistanceFromTextureGrid(transform * vertex->position());
116                 distance = absMin(distance, temp);
117             }
118 
119             return m_helper.snapDelta(delta, -distance);
120         }
121 
doCancel()122         bool UVOffsetTool::doCancel() {
123             return false;
124         }
125     }
126 }
127