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 "BrushFace.h"
21 
22 #include "Exceptions.h"
23 #include "VecMath.h"
24 #include "Assets/Texture.h"
25 #include "Assets/TextureManager.h"
26 #include "Model/Brush.h"
27 #include "Model/BrushFaceSnapshot.h"
28 #include "Model/PlanePointFinder.h"
29 #include "Model/ParallelTexCoordSystem.h"
30 #include "Model/ParaxialTexCoordSystem.h"
31 #include "Renderer/IndexRangeMap.h"
32 #include "Renderer/TexturedIndexArrayBuilder.h"
33 
34 namespace TrenchBroom {
35     namespace Model {
36         const String BrushFace::NoTextureName = "__TB_empty";
37 
project(BrushHalfEdge * halfEdge)38         BrushFace::ProjectToVertex::Type BrushFace::ProjectToVertex::project(BrushHalfEdge* halfEdge) {
39             return halfEdge->origin();
40         }
41 
project(BrushHalfEdge * halfEdge)42         BrushFace::ProjectToEdge::Type BrushFace::ProjectToEdge::project(BrushHalfEdge* halfEdge) {
43             return halfEdge->edge();
44         }
45 
BrushFace(const Vec3 & point0,const Vec3 & point1,const Vec3 & point2,const BrushFaceAttributes & attribs,TexCoordSystem * texCoordSystem)46         BrushFace::BrushFace(const Vec3& point0, const Vec3& point1, const Vec3& point2, const BrushFaceAttributes& attribs, TexCoordSystem* texCoordSystem) :
47         m_brush(NULL),
48         m_lineNumber(0),
49         m_lineCount(0),
50         m_selected(false),
51         m_texCoordSystem(texCoordSystem),
52         m_geometry(NULL),
53         m_vertexIndex(0),
54         m_cachedVertices(0),
55         m_verticesValid(false),
56         m_attribs(attribs) {
57             assert(m_texCoordSystem != NULL);
58             setPoints(point0, point1, point2);
59         }
60 
61         class PlaneWeightOrder {
62         private:
63             bool m_deterministic;
64         public:
PlaneWeightOrder(const bool deterministic)65             PlaneWeightOrder(const bool deterministic) :
66             m_deterministic(deterministic) {}
67 
68             template <typename T, size_t S>
operator ()(const Plane<T,S> & lhs,const Plane<T,S> & rhs) const69             bool operator()(const Plane<T,S>& lhs, const Plane<T,S>& rhs) const {
70                 int result = lhs.normal.weight() - rhs.normal.weight();
71                 if (m_deterministic)
72                     result += static_cast<int>(1000.0 * (lhs.distance - lhs.distance));
73                 return result < 0;
74             }
75         };
76 
77         class FaceWeightOrder {
78         private:
79             const PlaneWeightOrder& m_planeOrder;
80         public:
FaceWeightOrder(const PlaneWeightOrder & planeOrder)81             FaceWeightOrder(const PlaneWeightOrder& planeOrder) :
82             m_planeOrder(planeOrder) {}
83 
operator ()(const Model::BrushFace * lhs,const Model::BrushFace * rhs) const84             bool operator()(const Model::BrushFace* lhs, const Model::BrushFace* rhs) const {
85                 return m_planeOrder(lhs->boundary(), rhs->boundary());
86             }
87         };
88 
createParaxial(const Vec3 & point0,const Vec3 & point1,const Vec3 & point2,const String & textureName)89         BrushFace* BrushFace::createParaxial(const Vec3& point0, const Vec3& point1, const Vec3& point2, const String& textureName) {
90             const BrushFaceAttributes attribs(textureName);
91             return new BrushFace(point0, point1, point2, attribs, new ParaxialTexCoordSystem(point0, point1, point2, attribs));
92         }
93 
createParallel(const Vec3 & point0,const Vec3 & point1,const Vec3 & point2,const String & textureName)94         BrushFace* BrushFace::createParallel(const Vec3& point0, const Vec3& point1, const Vec3& point2, const String& textureName) {
95             const BrushFaceAttributes attribs(textureName);
96             return new BrushFace(point0, point1, point2, attribs, new ParallelTexCoordSystem(point0, point1, point2, attribs));
97         }
98 
sortFaces(BrushFaceList & faces)99         void BrushFace::sortFaces(BrushFaceList& faces) {
100             std::sort(faces.begin(), faces.end(), FaceWeightOrder(PlaneWeightOrder(true)));
101             std::sort(faces.begin(), faces.end(), FaceWeightOrder(PlaneWeightOrder(false)));
102         }
103 
~BrushFace()104         BrushFace::~BrushFace() {
105             for (size_t i = 0; i < 3; ++i)
106                 m_points[i] = Vec3::Null;
107             m_brush = NULL;
108             m_lineNumber = 0;
109             m_lineCount = 0;
110             m_selected = false;
111             delete m_texCoordSystem;
112             m_texCoordSystem = NULL;
113             m_geometry = NULL;
114         }
115 
clone() const116         BrushFace* BrushFace::clone() const {
117             BrushFace* result = new BrushFace(points()[0], points()[1], points()[2], textureName(), m_texCoordSystem->clone());
118             result->m_attribs = m_attribs;
119             result->setFilePosition(m_lineNumber, m_lineCount);
120             if (m_selected)
121                 result->select();
122             return result;
123         }
124 
takeSnapshot()125         BrushFaceSnapshot* BrushFace::takeSnapshot() {
126             return new BrushFaceSnapshot(this, m_texCoordSystem);
127         }
128 
brush() const129         Brush* BrushFace::brush() const {
130             return m_brush;
131         }
132 
setBrush(Brush * brush)133         void BrushFace::setBrush(Brush* brush) {
134             assert((m_brush == NULL) ^ (brush == NULL));
135             m_brush = brush;
136         }
137 
points() const138         const BrushFace::Points& BrushFace::points() const {
139             return m_points;
140         }
141 
arePointsOnPlane(const Plane3 & plane) const142         bool BrushFace::arePointsOnPlane(const Plane3& plane) const {
143             for (size_t i = 0; i < 3; i++)
144                 if (plane.pointStatus(m_points[i]) != Math::PointStatus::PSInside)
145                     return false;
146             return true;
147         }
148 
boundary() const149         const Plane3& BrushFace::boundary() const {
150             return m_boundary;
151         }
152 
center() const153         Vec3 BrushFace::center() const {
154             assert(m_geometry != NULL);
155             const BrushHalfEdgeList& boundary = m_geometry->boundary();
156             return Vec3::center(boundary.begin(), boundary.end(), BrushGeometry::GetVertexPosition());
157         }
158 
boundsCenter() const159         Vec3 BrushFace::boundsCenter() const {
160             assert(m_geometry != NULL);
161 
162             const Mat4x4 toPlane = planeProjectionMatrix(m_boundary.distance, m_boundary.normal);
163             const Mat4x4 fromPlane = invertedMatrix(toPlane);
164 
165             const BrushHalfEdge* first = m_geometry->boundary().front();
166             const BrushHalfEdge* current = first;
167 
168             BBox3 bounds;
169             bounds.min = bounds.max = toPlane * current->origin()->position();
170 
171             current = current->next();
172             while (current != first) {
173                 bounds.mergeWith(toPlane * current->origin()->position());
174                 current = current->next();
175             }
176             return fromPlane * bounds.center();
177         }
178 
area(const Math::Axis::Type axis) const179         FloatType BrushFace::area(const Math::Axis::Type axis) const {
180             const BrushHalfEdge* first = m_geometry->boundary().front();
181             const BrushHalfEdge* current = first;
182 
183             FloatType c1 = 0.0;
184             FloatType c2 = 0.0;
185             switch (axis) {
186                 case Math::Axis::AX:
187                     do {
188                         c1 += current->origin()->position().y() * current->next()->origin()->position().z();
189                         c2 += current->origin()->position().z() * current->next()->origin()->position().y();
190                         current = current->next();
191                     } while (current != first);
192                     break;
193                 case Math::Axis::AY:
194                     do {
195                         c1 += current->origin()->position().z() * current->next()->origin()->position().x();
196                         c2 += current->origin()->position().x() * current->next()->origin()->position().z();
197                         current = current->next();
198                     } while (current != first);
199                     break;
200                 case Math::Axis::AZ:
201                     do {
202                         c1 += current->origin()->position().x() * current->next()->origin()->position().y();
203                         c2 += current->origin()->position().y() * current->next()->origin()->position().x();
204                         current = current->next();
205                     } while (current != first);
206                     break;
207             };
208             return Math::abs((c1 - c2) / 2.0);
209         }
210 
attribs() const211         const BrushFaceAttributes& BrushFace::attribs() const {
212             return m_attribs;
213         }
214 
setAttribs(const BrushFaceAttributes & attribs)215         void BrushFace::setAttribs(const BrushFaceAttributes& attribs) {
216             const float oldRotation = m_attribs.rotation();
217             m_attribs = attribs;
218             m_texCoordSystem->setRotation(m_boundary.normal, oldRotation, m_attribs.rotation());
219 
220             if (m_brush != NULL)
221                 m_brush->faceDidChange();
222 
223             invalidateVertexCache();
224         }
225 
textureName() const226         const String& BrushFace::textureName() const {
227             return m_attribs.textureName();
228         }
229 
texture() const230         Assets::Texture* BrushFace::texture() const {
231             return m_attribs.texture();
232         }
233 
textureSize() const234         Vec2f BrushFace::textureSize() const {
235             return m_attribs.textureSize();
236         }
237 
offset() const238         const Vec2f& BrushFace::offset() const {
239             return m_attribs.offset();
240         }
241 
xOffset() const242         float BrushFace::xOffset() const {
243             return m_attribs.xOffset();
244         }
245 
yOffset() const246         float BrushFace::yOffset() const {
247             return m_attribs.yOffset();
248         }
249 
modOffset(const Vec2f & offset) const250         Vec2f BrushFace::modOffset(const Vec2f& offset) const {
251             return m_attribs.modOffset(offset);
252         }
253 
scale() const254         const Vec2f& BrushFace::scale() const {
255             return m_attribs.scale();
256         }
257 
xScale() const258         float BrushFace::xScale() const {
259             return m_attribs.xScale();
260         }
261 
yScale() const262         float BrushFace::yScale() const {
263             return m_attribs.yScale();
264         }
265 
rotation() const266         float BrushFace::rotation() const {
267             return m_attribs.rotation();
268         }
269 
surfaceContents() const270         int BrushFace::surfaceContents() const {
271             return m_attribs.surfaceContents();
272         }
273 
surfaceFlags() const274         int BrushFace::surfaceFlags() const {
275             return m_attribs.surfaceFlags();
276         }
277 
surfaceValue() const278         float BrushFace::surfaceValue() const {
279             return m_attribs.surfaceValue();
280         }
281 
hasSurfaceAttributes() const282         bool BrushFace::hasSurfaceAttributes() const {
283             return surfaceContents() != 0 || surfaceFlags() != 0 || surfaceValue() != 0.0f;
284         }
285 
updateTexture(Assets::TextureManager * textureManager)286         void BrushFace::updateTexture(Assets::TextureManager* textureManager) {
287             assert(textureManager != NULL);
288             Assets::Texture* texture = textureManager->texture(textureName());
289             setTexture(texture);
290             invalidateVertexCache();
291         }
292 
setTexture(Assets::Texture * texture)293         void BrushFace::setTexture(Assets::Texture* texture) {
294             if (texture == m_attribs.texture())
295                 return;
296             m_attribs.setTexture(texture);
297             if (m_brush != NULL)
298                 m_brush->faceDidChange();
299             invalidateVertexCache();
300         }
301 
setXOffset(const float i_xOffset)302         void BrushFace::setXOffset(const float i_xOffset) {
303             if (i_xOffset == xOffset())
304                 return;
305             m_attribs.setXOffset(i_xOffset);
306             invalidateVertexCache();
307         }
308 
setYOffset(const float i_yOffset)309         void BrushFace::setYOffset(const float i_yOffset) {
310             if (i_yOffset == yOffset())
311                 return;
312             m_attribs.setYOffset(i_yOffset);
313             invalidateVertexCache();
314         }
315 
setXScale(const float i_xScale)316         void BrushFace::setXScale(const float i_xScale) {
317             if (i_xScale == xScale())
318                 return;
319             m_attribs.setXScale(i_xScale);
320             invalidateVertexCache();
321         }
322 
setYScale(const float i_yScale)323         void BrushFace::setYScale(const float i_yScale) {
324             if (i_yScale == yScale())
325                 return;
326             m_attribs.setYScale(i_yScale);
327             invalidateVertexCache();
328         }
329 
setRotation(const float rotation)330         void BrushFace::setRotation(const float rotation) {
331             if (rotation == m_attribs.rotation())
332                 return;
333 
334             const float oldRotation = m_attribs.rotation();
335             m_attribs.setRotation(rotation);
336             m_texCoordSystem->setRotation(m_boundary.normal, oldRotation, rotation);
337             invalidateVertexCache();
338         }
339 
setSurfaceContents(const int surfaceContents)340         void BrushFace::setSurfaceContents(const int surfaceContents) {
341             if (surfaceContents == m_attribs.surfaceContents())
342                 return;
343             m_attribs.setSurfaceContents(surfaceContents);
344             if (m_brush != NULL)
345                 m_brush->faceDidChange();
346         }
347 
setSurfaceFlags(const int surfaceFlags)348         void BrushFace::setSurfaceFlags(const int surfaceFlags) {
349             if (surfaceFlags == m_attribs.surfaceFlags())
350                 return;
351             m_attribs.setSurfaceFlags(surfaceFlags);
352         }
353 
setSurfaceValue(const float surfaceValue)354         void BrushFace::setSurfaceValue(const float surfaceValue) {
355             if (surfaceValue == m_attribs.surfaceValue())
356                 return;
357             m_attribs.setSurfaceValue(surfaceValue);
358         }
359 
setAttributes(const BrushFace * other)360         void BrushFace::setAttributes(const BrushFace* other) {
361             setTexture(other->texture());
362             setXOffset(other->xOffset());
363             setYOffset(other->yOffset());
364             setRotation(other->rotation());
365             setXScale(other->xScale());
366             setYScale(other->yScale());
367             setSurfaceContents(other->surfaceContents());
368             setSurfaceFlags(other->surfaceFlags());
369             setSurfaceValue(other->surfaceValue());
370         }
371 
textureXAxis() const372         Vec3 BrushFace::textureXAxis() const {
373             return m_texCoordSystem->xAxis();
374         }
375 
textureYAxis() const376         Vec3 BrushFace::textureYAxis() const {
377             return m_texCoordSystem->yAxis();
378         }
379 
resetTextureAxes()380         void BrushFace::resetTextureAxes() {
381             m_texCoordSystem->resetTextureAxes(m_boundary.normal);
382             invalidateVertexCache();
383         }
384 
moveTexture(const Vec3 & up,const Vec3 & right,const Vec2f & offset)385         void BrushFace::moveTexture(const Vec3& up, const Vec3& right, const Vec2f& offset) {
386             m_texCoordSystem->moveTexture(m_boundary.normal, up, right, offset, m_attribs);
387             invalidateVertexCache();
388         }
389 
rotateTexture(const float angle)390         void BrushFace::rotateTexture(const float angle) {
391             const float oldRotation = m_attribs.rotation();
392             m_texCoordSystem->rotateTexture(m_boundary.normal, angle, m_attribs);
393             m_texCoordSystem->setRotation(m_boundary.normal, oldRotation, m_attribs.rotation());
394             invalidateVertexCache();
395         }
396 
shearTexture(const Vec2f & factors)397         void BrushFace::shearTexture(const Vec2f& factors) {
398             m_texCoordSystem->shearTexture(m_boundary.normal, factors);
399             invalidateVertexCache();
400         }
401 
transform(const Mat4x4 & transform,const bool lockTexture)402         void BrushFace::transform(const Mat4x4& transform, const bool lockTexture) {
403             using std::swap;
404 
405             const Vec3 invariant = m_geometry != NULL ? center() : m_boundary.anchor();
406             m_texCoordSystem->transform(m_boundary, transform, m_attribs, lockTexture, invariant);
407 
408             m_boundary.transform(transform);
409             for (size_t i = 0; i < 3; ++i)
410                 m_points[i] = transform * m_points[i];
411             if (crossed(m_points[2] - m_points[0], m_points[1] - m_points[0]).dot(m_boundary.normal) < 0.0)
412                 swap(m_points[1], m_points[2]);
413             correctPoints();
414             invalidateVertexCache();
415         }
416 
invert()417         void BrushFace::invert() {
418             using std::swap;
419 
420             m_boundary.flip();
421             swap(m_points[1], m_points[2]);
422             invalidateVertexCache();
423         }
424 
updatePointsFromVertices()425         void BrushFace::updatePointsFromVertices() {
426             assert(m_geometry != NULL);
427 
428             // Find a triple of consecutive vertices s.t. the (normalized) vectors from the mid vertex to the other two
429             // have the smallest dot value of all such triples. This is to have better precision when computing the
430             // boundary plane normal from these vectors.
431             FloatType bestDot = 1.0;
432             const BrushHalfEdge* best = NULL;
433 
434             const BrushHalfEdge* first = m_geometry->boundary().front();
435             const BrushHalfEdge* current = first;
436 
437             do {
438                 m_points[2] = current->next()->origin()->position();
439                 m_points[0] = current->origin()->position();
440                 m_points[1] = current->previous()->origin()->position();
441 
442                 const Vec3 v1 = (m_points[2] - m_points[0]).normalized();
443                 const Vec3 v2 = (m_points[1] - m_points[0]).normalized();
444                 const FloatType dot = std::abs(v1.dot(v2));
445                 if (dot < bestDot) {
446                     bestDot = dot;
447                     best = current;
448                 }
449 
450                 current = current->next();
451             } while (current != first && bestDot > 0.0);
452 
453             assert(best != NULL);
454             const Vec3 oldNormal = m_boundary.normal;
455             setPoints(best->origin()->position(),
456                       best->previous()->origin()->position(),
457                       best->next()->origin()->position());
458 
459             m_texCoordSystem->updateNormal(oldNormal, m_boundary.normal, m_attribs);
460         }
461 
snapPlanePointsToInteger()462         void BrushFace::snapPlanePointsToInteger() {
463             for (size_t i = 0; i < 3; ++i)
464                 m_points[i].round();
465             setPoints(m_points[0], m_points[1], m_points[2]);
466         }
467 
findIntegerPlanePoints()468         void BrushFace::findIntegerPlanePoints() {
469             PlanePointFinder::findPoints(m_boundary, m_points, 3);
470             setPoints(m_points[0], m_points[1], m_points[2]);
471         }
472 
projectToBoundaryMatrix() const473         Mat4x4 BrushFace::projectToBoundaryMatrix() const {
474             const Vec3 texZAxis = m_texCoordSystem->fromMatrix(Vec2f::Null, Vec2f::One) * Vec3::PosZ;
475             const Mat4x4 worldToPlaneMatrix = planeProjectionMatrix(m_boundary.distance, m_boundary.normal, texZAxis);
476             const Mat4x4 planeToWorldMatrix = invertedMatrix(worldToPlaneMatrix);
477             return planeToWorldMatrix * Mat4x4::ZerZ * worldToPlaneMatrix;
478         }
479 
toTexCoordSystemMatrix(const Vec2f & offset,const Vec2f & scale,const bool project) const480         Mat4x4 BrushFace::toTexCoordSystemMatrix(const Vec2f& offset, const Vec2f& scale, const bool project) const {
481             if (project)
482                 return Mat4x4::ZerZ * m_texCoordSystem->toMatrix(offset, scale);
483             else
484                 return m_texCoordSystem->toMatrix(offset, scale);
485         }
486 
fromTexCoordSystemMatrix(const Vec2f & offset,const Vec2f & scale,const bool project) const487         Mat4x4 BrushFace::fromTexCoordSystemMatrix(const Vec2f& offset, const Vec2f& scale, const bool project) const {
488             if (project)
489                 return projectToBoundaryMatrix() * m_texCoordSystem->fromMatrix(offset, scale);
490             return m_texCoordSystem->fromMatrix(offset, scale);
491         }
492 
measureTextureAngle(const Vec2f & center,const Vec2f & point) const493         float BrushFace::measureTextureAngle(const Vec2f& center, const Vec2f& point) const {
494             return m_texCoordSystem->measureAngle(m_attribs.rotation(), center, point);
495         }
496 
vertexCount() const497         size_t BrushFace::vertexCount() const {
498             assert(m_geometry != NULL);
499             return m_geometry->boundary().size();
500         }
501 
edges() const502         BrushFace::EdgeList BrushFace::edges() const {
503             assert(m_geometry != NULL);
504             return EdgeList(m_geometry->boundary());
505         }
506 
vertices() const507         BrushFace::VertexList BrushFace::vertices() const {
508             assert(m_geometry != NULL);
509             return VertexList(m_geometry->boundary());
510         }
511 
geometry() const512         BrushFaceGeometry* BrushFace::geometry() const {
513             return m_geometry;
514         }
515 
setGeometry(BrushFaceGeometry * geometry)516         void BrushFace::setGeometry(BrushFaceGeometry* geometry) {
517             if (m_geometry == geometry)
518                 return;
519             m_geometry = geometry;
520             invalidateVertexCache();
521         }
522 
invalidate()523         void BrushFace::invalidate() {
524             invalidateVertexCache();
525         }
526 
setFilePosition(const size_t lineNumber,const size_t lineCount)527         void BrushFace::setFilePosition(const size_t lineNumber, const size_t lineCount) {
528             m_lineNumber = lineNumber;
529             m_lineCount = lineCount;
530         }
531 
selected() const532         bool BrushFace::selected() const {
533             return m_selected;
534         }
535 
select()536         void BrushFace::select() {
537             assert(!m_selected);
538             m_selected = true;
539             if (m_brush != NULL)
540                 m_brush->childWasSelected();
541         }
542 
deselect()543         void BrushFace::deselect() {
544             assert(m_selected);
545             m_selected = false;
546             if (m_brush != NULL)
547                 m_brush->childWasDeselected();
548         }
549 
getVertices(Renderer::VertexListBuilder<VertexSpec> & builder) const550         void BrushFace::getVertices(Renderer::VertexListBuilder<VertexSpec>& builder) const {
551             validateVertexCache();
552             m_vertexIndex = builder.addPolygon(m_cachedVertices).index;
553 
554             GLuint index = static_cast<GLuint>(m_vertexIndex);
555             // set the vertex indices
556             const BrushHalfEdge* first = m_geometry->boundary().front();
557             const BrushHalfEdge* current = first;
558             do {
559                 BrushVertex* vertex = current->origin();
560                 vertex->setPayload(index++);
561 
562                 // The boundary is in CCW order, but the renderer expects CW order:
563                 current = current->previous();
564             } while (current != first);
565         }
566 
countIndices(Renderer::TexturedIndexArrayMap::Size & size) const567         void BrushFace::countIndices(Renderer::TexturedIndexArrayMap::Size& size) const {
568             if (vertexCount() == 4)
569                 size.inc(texture(), GL_QUADS, 4);
570             else
571                 size.inc(texture(), GL_TRIANGLES, 3 * (vertexCount() - 2));
572         }
573 
getFaceIndices(Renderer::TexturedIndexArrayBuilder & builder) const574         void BrushFace::getFaceIndices(Renderer::TexturedIndexArrayBuilder& builder) const {
575             if (vertexCount() == 4)
576                 builder.addQuads(texture(), static_cast<GLuint>(m_vertexIndex), vertexCount());
577             else
578                 builder.addPolygon(texture(), static_cast<GLuint>(m_vertexIndex), vertexCount());
579         }
580 
textureCoords(const Vec3 & point) const581         Vec2f BrushFace::textureCoords(const Vec3& point) const {
582             return m_texCoordSystem->getTexCoords(point, m_attribs);
583         }
584 
containsPoint(const Vec3 & point) const585         bool BrushFace::containsPoint(const Vec3& point) const {
586             const Vec3 toPoint = point - m_boundary.anchor();
587             if (!Math::zero(toPoint.dot(m_boundary.normal)))
588                 return false;
589 
590             const Ray3 ray(point + m_boundary.normal, -m_boundary.normal);
591             return !Math::isnan(intersectWithRay(ray));
592         }
593 
intersectWithRay(const Ray3 & ray) const594         FloatType BrushFace::intersectWithRay(const Ray3& ray) const {
595             assert(m_geometry != NULL);
596 
597             const FloatType dot = m_boundary.normal.dot(ray.direction);
598             if (!Math::neg(dot))
599                 return Math::nan<FloatType>();
600 
601             return intersectPolygonWithRay(ray, m_boundary, m_geometry->boundary().begin(), m_geometry->boundary().end(), BrushGeometry::GetVertexPosition());
602         }
603 
setPoints(const Vec3 & point0,const Vec3 & point1,const Vec3 & point2)604         void BrushFace::setPoints(const Vec3& point0, const Vec3& point1, const Vec3& point2) {
605             m_points[0] = point0;
606             m_points[1] = point1;
607             m_points[2] = point2;
608             correctPoints();
609 
610             if (!setPlanePoints(m_boundary, m_points)) {
611                 GeometryException e;
612                 e << "Colinear face points: (" <<
613                 m_points[0].asString() << ") (" <<
614                 m_points[1].asString() << ") (" <<
615                 m_points[2].asString() << ")";
616                 throw e;
617             }
618 
619             invalidateVertexCache();
620         }
621 
correctPoints()622         void BrushFace::correctPoints() {
623             for (size_t i = 0; i < 3; ++i)
624                 m_points[i].correct();
625         }
626 
vertexCacheValid() const627         bool BrushFace::vertexCacheValid() const {
628             return m_verticesValid;
629         }
630 
invalidateVertexCache()631         void BrushFace::invalidateVertexCache() {
632             m_verticesValid = false;
633         }
634 
validateVertexCache() const635         void BrushFace::validateVertexCache() const {
636             if (!m_verticesValid) {
637                 m_cachedVertices.clear();
638                 m_cachedVertices.reserve(vertexCount());
639 
640                 const BrushHalfEdge* first = m_geometry->boundary().front();
641                 const BrushHalfEdge* current = first;
642                 do {
643                     const Vec3& position = current->origin()->position();
644                     m_cachedVertices.push_back(Vertex(position, m_boundary.normal, textureCoords(position)));
645 
646                     // The boundary is in CCW order, but the renderer expects CW order:
647                     current = current->previous();
648                 } while (current != first);
649 
650                 m_verticesValid = true;
651             }
652         }
653     }
654 }
655