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