/****************************************************************************** * Aligns the modifier's slicing plane to the three selected particles. ******************************************************************************/ void PickParticlePlaneInputMode::alignPlane(SliceModifier* mod) { OVITO_ASSERT(_pickedParticles.size() == 3); try { Plane3 worldPlane(_pickedParticles[0].worldPos, _pickedParticles[1].worldPos, _pickedParticles[2].worldPos, true); if(worldPlane.normal.equals(Vector3::Zero(), FLOATTYPE_EPSILON)) throw Exception(tr("Cannot set the new slicing plane. The three selected particle are colinear.")); // Get the object to world transformation for the currently selected node. ObjectNode* node = _pickedParticles[0].objNode; TimeInterval interval; const AffineTransformation& nodeTM = node->getWorldTransform(mod->dataset()->animationSettings()->time(), interval); // Transform new plane from world to object space. Plane3 localPlane = nodeTM.inverse() * worldPlane; // Flip new plane orientation if necessary to align it with old orientation. if(localPlane.normal.dot(mod->normal()) < 0) localPlane = -localPlane; localPlane.normalizePlane(); UndoableTransaction::handleExceptions(mod->dataset()->undoStack(), tr("Align plane to particles"), [mod, &localPlane]() { mod->setNormal(localPlane.normal); mod->setDistance(localPlane.dist); }); } catch(const Exception& ex) { ex.showError(); } }
bool ConvexPolyhedron3<Real>::ComputeSilhouette (V3Array& rkTerminator, const Vector3<Real>& rkEye, const Plane3<Real>& rkPlane, const Vector3<Real>& rkU, const Vector3<Real>& rkV, V2Array& rkSilhouette) { Real fEDist = rkPlane.DistanceTo(rkEye); // assert: fEDist > 0 // closest planar point to E is K = E-dist*N Vector3<Real> kClosest = rkEye - fEDist*rkPlane.GetNormal(); // project polyhedron points onto plane for (int i = 0; i < (int)rkTerminator.size(); i++) { Vector3<Real>& rkPoint = rkTerminator[i]; Real fVDist = rkPlane.DistanceTo(rkPoint); if ( fVDist >= fEDist ) { // cannot project vertex onto plane return false; } // compute projected point Q Real fRatio = fEDist/(fEDist-fVDist); Vector3<Real> kProjected = rkEye + fRatio*(rkPoint - rkEye); // compute (x,y) so that Q = K+x*U+y*V+z*N Vector3<Real> kDiff = kProjected - kClosest; rkSilhouette.push_back(Vector2<Real>(rkU.Dot(kDiff),rkV.Dot(kDiff))); } return true; }
// TODO: 最优分割面选择需要考虑到二叉树的平衡性 // weight = fabs(左数目-右数目)+分割数目 // 越小越优先考虑 int BspTree::BspNode::BestIndex( std::vector< BspTriangle >& polyList ) { /** * The current hueristic is blind least-split */ // run through the list, searching for the best one. // the highest BspTriangle we'll bother testing (10% of total) int maxCheck; maxCheck = (int)(polyList.size() * percentageToCheck); if( !maxCheck ) maxCheck = 1; int bestSplits = 100000; int bestIndex = -1; int currSplits; // 这个平面总共分割了多少平面 int frontCount, backCount; Plane3 currPlane; for(int i=0; i<maxCheck; i++ ) { currSplits = 0; frontCount = backCount = 0; currPlane = Plane3( polyList[i] ); PointListLoc res; for(unsigned int i2=0; i2< polyList.size(); i2++ ) { if( i == i2 ) continue; res = currPlane.TestPoly( polyList[i2] ); switch(res) { case plistSplit: currSplits++; break; case plistFront: frontCount++; break; case plistBack: backCount++; break; } } int weight = abs(frontCount - backCount) + currSplits; //int weight = currSplits; if( weight < bestSplits ) { bestSplits = weight; bestIndex = i; } } assert( bestIndex >= 0 ); return bestIndex; }
void FaceInstance::addLight(const Matrix4& localToWorld, const RendererLight& light) { const Plane3& facePlane = getFace().plane3(); Plane3 tmp = Plane3(facePlane.normal(), -facePlane.dist()) .transformed(localToWorld); if (!tmp.testPoint(light.worldOrigin()) || !tmp.testPoint(light.getLightOrigin())) { m_lights.addLight(light); } }
bool Wml::Culled (const Plane3<Real>& rkPlane, const Box3<Real>& rkBox) { Real fTmp[3] = { rkBox.Extent(0)*(rkPlane.GetNormal().Dot(rkBox.Axis(0))), rkBox.Extent(1)*(rkPlane.GetNormal().Dot(rkBox.Axis(1))), rkBox.Extent(2)*(rkPlane.GetNormal().Dot(rkBox.Axis(2))) }; Real fRadius = Math<Real>::FAbs(fTmp[0]) + Math<Real>::FAbs(fTmp[1]) + Math<Real>::FAbs(fTmp[2]); Real fPseudoDistance = rkPlane.DistanceTo(rkBox.Center()); return fPseudoDistance <= -fRadius; }
void TextureProjection::transformLocked (std::size_t width, std::size_t height, const Plane3& plane, const Matrix4& identity2transformed) { Vector3 normalTransformed(matrix4_transformed_direction(identity2transformed, plane.normal())); // identity: identity space // transformed: transformation // stIdentity: base st projection space before transformation // stTransformed: base st projection space after transformation // stOriginal: original texdef space // stTransformed2stOriginal = stTransformed -> transformed -> identity -> stIdentity -> stOriginal Matrix4 identity2stIdentity; basisForNormal(plane.normal(), identity2stIdentity); Matrix4 transformed2stTransformed; basisForNormal(normalTransformed, transformed2stTransformed); Matrix4 stTransformed2identity(matrix4_affine_inverse(matrix4_multiplied_by_matrix4(transformed2stTransformed, identity2transformed))); Vector3 originalProjectionAxis(matrix4_affine_inverse(identity2stIdentity).z().getVector3()); Vector3 transformedProjectionAxis(stTransformed2identity.z().getVector3()); Matrix4 stIdentity2stOriginal = m_texdef.getTransform((float) width, (float) height); Matrix4 identity2stOriginal(matrix4_multiplied_by_matrix4(stIdentity2stOriginal, identity2stIdentity)); double dot = originalProjectionAxis.dot(transformedProjectionAxis); if (dot == 0) { // The projection axis chosen for the transformed normal is at 90 degrees // to the transformed projection axis chosen for the original normal. // This happens when the projection axis is ambiguous - e.g. for the plane // 'X == Y' the projection axis could be either X or Y. Matrix4 identityCorrected = matrix4_reflection_for_plane45(plane, originalProjectionAxis, transformedProjectionAxis); identity2stOriginal = matrix4_multiplied_by_matrix4(identity2stOriginal, identityCorrected); } Matrix4 stTransformed2stOriginal = matrix4_multiplied_by_matrix4(identity2stOriginal, stTransformed2identity); setTransform((float) width, (float) height, stTransformed2stOriginal); m_texdef.normalise((float) width, (float) height); }
Vector3 Winding::centroid(const Plane3& plane) const { Vector3 centroid(0,0,0); float area2 = 0, x_sum = 0, y_sum = 0; const ProjectionAxis axis = projectionaxis_for_normal(plane.normal()); const indexremap_t remap = indexremap_for_projectionaxis(axis); for (std::size_t i = size() - 1, j = 0; j < size(); i = j, ++j) { const float ai = (*this)[i].vertex[remap.x] * (*this)[j].vertex[remap.y] - (*this)[j].vertex[remap.x] * (*this)[i].vertex[remap.y]; area2 += ai; x_sum += ((*this)[j].vertex[remap.x] + (*this)[i].vertex[remap.x]) * ai; y_sum += ((*this)[j].vertex[remap.y] + (*this)[i].vertex[remap.y]) * ai; } centroid[remap.x] = x_sum / (3 * area2); centroid[remap.y] = y_sum / (3 * area2); { Ray ray(Vector3(0, 0, 0), Vector3(0, 0, 0)); ray.origin[remap.x] = centroid[remap.x]; ray.origin[remap.y] = centroid[remap.y]; ray.direction[remap.z] = 1; centroid[remap.z] = ray.getDistance(plane); } return centroid; }
bool Wml::Culled (const Plane3<Real>& rkPlane, const Ellipsoid3<Real>& rkEllipsoid, bool bUnitNormal) { Vector3<Real> kNormal = rkPlane.GetNormal(); Real fConstant = rkPlane.GetConstant(); if ( !bUnitNormal ) { Real fLength = kNormal.Normalize(); fConstant /= fLength; } Real fDiscr = kNormal.Dot(rkEllipsoid.InverseA()*kNormal); Real fRoot = Math<Real>::Sqrt(Math<Real>::FAbs(fDiscr)); Real fSDist = kNormal.Dot(rkEllipsoid.Center()) - fConstant; return fSDist <= -fRoot; }
bool PlaneCullTest(const Plane3& plane, const AABB3& box){ Vector3 testVertex; // find the vertex with the greatest distance value if(plane.n.x >= 0.f){ if(plane.n.y >= 0.f){ if(plane.n.z >= 0.f){ testVertex = box.max; }else{ testVertex = MakeVector3(box.max.x, box.max.y, box.min.z); } }else{ if(plane.n.z >= 0.f){ testVertex = MakeVector3(box.max.x, box.min.y, box.max.z); }else{ testVertex = MakeVector3(box.max.x, box.min.y, box.min.z); } } }else{ if(plane.n.y >= 0.f){ if(plane.n.z >= 0.f){ testVertex = MakeVector3(box.min.x, box.max.y, box.max.z); }else{ testVertex = MakeVector3(box.min.x, box.max.y, box.min.z); } }else{ if(plane.n.z >= 0.f){ testVertex = MakeVector3(box.min.x, box.min.y, box.max.z); }else{ testVertex = box.min; } } } return plane.GetDistanceTo(testVertex) >= 0.f; }
/// \brief Returns the point at which \p line intersects \p plane, or an undefined value if there is no intersection. inline DoubleVector3 line_intersect_plane(const DoubleLine& line, const Plane3& plane) { return line.origin + vector3_scaled( line.direction, -plane3_distance_to_point(plane, line.origin) / vector3_dot(line.direction, plane.normal()) ); }
/* ------------------------------------------------------- */ int SphereVolume::whichSide(const Plane3& plane) const { float d = plane.distanceTo(m_center); if (d > m_radius) return 1; else if (d < -m_radius) return -1; else return 0; }
/* see https://github.com/kduske/TrenchBroom/issues/1033 commented out because it breaks the release build process */ TEST(PlaneTest, planePointFinder) { Plane3 plane; const Vec3 points[3] = {Vec3(48, 16, 28), Vec3(16.0, 16.0, 27.9980487823486328125), Vec3(48, 18, 22)}; ASSERT_FALSE(points[1].isInteger()); ASSERT_TRUE(setPlanePoints(plane, points[0], points[1], points[2])); // Some verts that should lie (very close to) on the plane std::vector<Vec3> verts; verts.push_back(Vec3(48, 18, 22)); verts.push_back(Vec3(48, 16, 28)); verts.push_back(Vec3(16, 16, 28)); verts.push_back(Vec3(16, 18, 22)); for (size_t i=0; i<verts.size(); i++) { FloatType dist = Math::abs(plane.pointDistance(verts[i])); ASSERT_LT(dist, 0.01); } // Now find a similar plane with integer points Vec3 intpoints[3]; for (size_t i=0; i<3; i++) intpoints[i] = points[i]; TrenchBroom::Model::PlanePointFinder::findPoints(plane, intpoints, 3); ASSERT_TRUE(intpoints[0].isInteger()); ASSERT_TRUE(intpoints[1].isInteger()); ASSERT_TRUE(intpoints[2].isInteger()); Plane3 intplane; ASSERT_TRUE(setPlanePoints(intplane, intpoints[0], intpoints[1], intpoints[2])); // ASSERT_FALSE(intplane.equals(plane)); no longer fails // Check that the verts are still close to the new integer plane for (size_t i=0; i<verts.size(); i++) { FloatType dist = Math::abs(intplane.pointDistance(verts[i])); ASSERT_LT(dist, 0.01); } }
BrushSplitType Winding::classifyPlane(const Plane3& plane) const { BrushSplitType split; for (const_iterator i = begin(); i != end(); ++i) { ++split.counts[classifyDistance(plane.distanceToPoint(i->vertex), ON_EPSILON)]; } return split; }
void updatePolyhedron(const Vec3& current) { const Grid& grid = m_tool->grid(); const Math::Axis::Type axis = m_plane.normal.firstComponent(); const Plane3 swizzledPlane(swizzle(m_plane.anchor(), axis), swizzle(m_plane.normal, axis)); const Vec3 theMin = swizzle(grid.snapDown(min(m_initialPoint, current)), axis); const Vec3 theMax = swizzle(grid.snapUp (max(m_initialPoint, current)), axis); const Vec2 topLeft2(theMin.x(), theMin.y()); const Vec2 topRight2(theMax.x(), theMin.y()); const Vec2 bottomLeft2(theMin.x(), theMax.y()); const Vec2 bottomRight2(theMax.x(), theMax.y()); const Vec3 topLeft3 = unswizzle(Vec3(topLeft2, swizzledPlane.zAt(topLeft2)), axis); const Vec3 topRight3 = unswizzle(Vec3(topRight2, swizzledPlane.zAt(topRight2)), axis); const Vec3 bottomLeft3 = unswizzle(Vec3(bottomLeft2, swizzledPlane.zAt(bottomLeft2)), axis); const Vec3 bottomRight3 = unswizzle(Vec3(bottomRight2, swizzledPlane.zAt(bottomRight2)), axis); Polyhedron3 polyhedron = m_oldPolyhedron; polyhedron.addPoint(topLeft3); polyhedron.addPoint(bottomLeft3); polyhedron.addPoint(bottomRight3); polyhedron.addPoint(topRight3); m_tool->update(polyhedron); }
/****************************************************************************** * Aligns the current viewing direction to the slicing plane. ******************************************************************************/ void SliceModifierEditor::onAlignViewToPlane() { TimeInterval interval; Viewport* vp = dataset()->viewportConfig()->activeViewport(); if(!vp) return; // Get the object to world transformation for the currently selected object. ObjectNode* node = dynamic_object_cast<ObjectNode>(dataset()->selection()->front()); if(!node) return; const AffineTransformation& nodeTM = node->getWorldTransform(dataset()->animationSettings()->time(), interval); // Transform the current slicing plane to the world coordinate system. SliceModifier* mod = static_object_cast<SliceModifier>(editObject()); if(!mod) return; Plane3 planeLocal = mod->slicingPlane(dataset()->animationSettings()->time(), interval); Plane3 planeWorld = nodeTM * planeLocal; // Calculate the intersection point of the current viewing direction with the current slicing plane. Ray3 viewportRay(vp->cameraPosition(), vp->cameraDirection()); FloatType t = planeWorld.intersectionT(viewportRay); Point3 intersectionPoint; if(t != FLOATTYPE_MAX) intersectionPoint = viewportRay.point(t); else intersectionPoint = Point3::Origin() + nodeTM.translation(); if(vp->isPerspectiveProjection()) { FloatType distance = (vp->cameraPosition() - intersectionPoint).length(); vp->setViewType(Viewport::VIEW_PERSPECTIVE); vp->setCameraDirection(-planeWorld.normal); vp->setCameraPosition(intersectionPoint + planeWorld.normal * distance); } else { vp->setViewType(Viewport::VIEW_ORTHO); vp->setCameraDirection(-planeWorld.normal); } vp->zoomToSelectionExtents(); }
/// \brief Keep the value of \p infinity as small as possible to improve precision in Winding_Clip. void Winding_createInfinite(FixedWinding& winding, const Plane3& plane, double infinity) { double max = -infinity; int x = -1; for (int i=0 ; i<3; i++) { double d = fabs(plane.normal()[i]); if (d > max) { x = i; max = d; } } if(x == -1) { globalErrorStream() << "invalid plane\n"; return; } DoubleVector3 vup = g_vector3_identity; switch (x) { case 0: case 1: vup[2] = 1; break; case 2: vup[0] = 1; break; } vector3_add(vup, vector3_scaled(plane.normal(), -vector3_dot(vup, plane.normal()))); vector3_normalise(vup); DoubleVector3 org = vector3_scaled(plane.normal(), plane.dist()); DoubleVector3 vright = vector3_cross(vup, plane.normal()); vector3_scale(vup, infinity); vector3_scale(vright, infinity); // project a really big axis aligned box onto the plane DoubleLine r1, r2, r3, r4; r1.origin = vector3_added(vector3_subtracted(org, vright), vup); r1.direction = vector3_normalised(vright); winding.push_back(FixedWindingVertex(r1.origin, r1, c_brush_maxFaces)); r2.origin = vector3_added(vector3_added(org, vright), vup); r2.direction = vector3_normalised(vector3_negated(vup)); winding.push_back(FixedWindingVertex(r2.origin, r2, c_brush_maxFaces)); r3.origin = vector3_subtracted(vector3_added(org, vright), vup); r3.direction = vector3_normalised(vector3_negated(vright)); winding.push_back(FixedWindingVertex(r3.origin, r3, c_brush_maxFaces)); r4.origin = vector3_subtracted(vector3_subtracted(org, vright), vup); r4.direction = vector3_normalised(vup); winding.push_back(FixedWindingVertex(r4.origin, r4, c_brush_maxFaces)); }
bool Winding::testPlane(const Plane3& plane, bool flipped) const { const int test = (flipped) ? ePlaneBack : ePlaneFront; for (const_iterator i = begin(); i != end(); ++i) { if (test == classifyDistance(plane.distanceToPoint(i->vertex), ON_EPSILON)) { return false; } } return true; }
void CreateEntityTool::updateEntityPosition2D(const Ray3& pickRay) { assert(m_entity != NULL); MapDocumentSPtr document = lock(m_document); const Vec3 toMin = m_referenceBounds.min - pickRay.origin; const Vec3 toMax = m_referenceBounds.max - pickRay.origin; const Vec3 anchor = toMin.dot(pickRay.direction) > toMax.dot(pickRay.direction) ? m_referenceBounds.min : m_referenceBounds.max; const Plane3 dragPlane(anchor, -pickRay.direction); const FloatType distance = dragPlane.intersectWithRay(pickRay); if (Math::isnan(distance)) return; const Vec3 hitPoint = pickRay.pointAtDistance(distance); const Grid& grid = document->grid(); const Vec3 delta = grid.moveDeltaForBounds(dragPlane, m_entity->bounds(), document->worldBounds(), pickRay, hitPoint); if (delta.null()) return; document->translateObjects(delta); }
int BVaabb::WhichSide(const Plane3& plane) const { if (mAlwaysPass) return 1; Real fDistance = plane.DistanceTo(mCenter); if (fDistance <= -mRadius) { return -1; } if (fDistance >= mRadius) { return +1; } return 0; }
/****************************************************************************** * Renders the plane in the viewports. ******************************************************************************/ Box3 SliceModifier::renderPlane(SceneRenderer* renderer, const Plane3& plane, const Box3& bb, const ColorA& color) const { // Compute intersection lines of slicing plane and bounding box. QVector<Point3> vertices; Point3 corners[8]; for(int i = 0; i < 8; i++) corners[i] = bb[i]; planeQuadIntersection(corners, {{0, 1, 5, 4}}, plane, vertices); planeQuadIntersection(corners, {{1, 3, 7, 5}}, plane, vertices); planeQuadIntersection(corners, {{3, 2, 6, 7}}, plane, vertices); planeQuadIntersection(corners, {{2, 0, 4, 6}}, plane, vertices); planeQuadIntersection(corners, {{4, 5, 7, 6}}, plane, vertices); planeQuadIntersection(corners, {{0, 2, 3, 1}}, plane, vertices); // If there is not intersection with the simulation box then // project the simulation box onto the plane. if(vertices.empty()) { const static int edges[12][2] = { {0,1},{1,3},{3,2},{2,0}, {4,5},{5,7},{7,6},{6,4}, {0,4},{1,5},{3,7},{2,6} }; for(int edge = 0; edge < 12; edge++) { vertices.push_back(plane.projectPoint(corners[edges[edge][0]])); vertices.push_back(plane.projectPoint(corners[edges[edge][1]])); } } if(renderer) { // Render plane-box intersection lines. std::shared_ptr<LinePrimitive> buffer = renderer->createLinePrimitive(); buffer->setVertexCount(vertices.size()); buffer->setVertexPositions(vertices.constData()); buffer->setLineColor(color); buffer->render(renderer); } // Compute bounding box. Box3 vertexBoundingBox; vertexBoundingBox.addPoints(vertices.constData(), vertices.size()); return vertexBoundingBox; }
/****************************************************************************** * Computes the intersection lines of a plane and a quad. ******************************************************************************/ void SliceModifier::planeQuadIntersection(const Point3 corners[8], const std::array<int,4>& quadVerts, const Plane3& plane, QVector<Point3>& vertices) const { Point3 p1; bool hasP1 = false; for(int i = 0; i < 4; i++) { Ray3 edge(corners[quadVerts[i]], corners[quadVerts[(i+1)%4]]); FloatType t = plane.intersectionT(edge, FLOATTYPE_EPSILON); if(t < 0 || t > 1) continue; if(!hasP1) { p1 = edge.point(t); hasP1 = true; } else { Point3 p2 = edge.point(t); if(!p2.equals(p1)) { vertices.push_back(p1); vertices.push_back(p2); return; } } } }
// Convert a plane equation to a rigid transform. static void planeToCFrame(Plane3 const & plane, Vector3 const & centroid, CoordinateFrame3 & cframe) { Vector3 n; Real d; plane.getEquation(n.x(), n.y(), n.z(), d); Matrix3 rot; if (n.y() < -0.999) { rot = Matrix3(1, 0, 0, 0, -1, 0, 0, 0, -1); } else { Quat quat = rotationArc(Vector3::unitY(), n); rot = quat.toRotationMatrix(); } cframe = CoordinateFrame3::_fromAffine(AffineTransform3(rot, centroid)); }
void PerspectiveProjectEllipsoid (const Ellipsoid3<Real>& ellipsoid, const Vector3<Real>& eye, const Plane3<Real>& plane, const Vector3<Real>& U, const Vector3<Real>& V, Vector3<Real>& P, Ellipse2<Real>& ellipse) { // Get coefficients for ellipsoid as X^T*A*X + B^T*X + C = 0. Matrix3<Real> A; Vector3<Real> B; Real C; ellipsoid.ToCoefficients(A, B, C); // Compute matrix M (see PerspectiveProjectionEllipsoid.pdf). Vector3<Real> AE = A*eye; Real EAE = eye.Dot(AE); Real BE = B.Dot(eye); Real QuadE = ((Real)4)*(EAE + BE + C); Vector3<Real> Bp2AE = B + ((Real)2)*AE; Matrix3<Real> mat = Matrix3<Real>(Bp2AE, Bp2AE) - QuadE*A; // Compute coefficients for projected ellipse. Vector3<Real> MU = mat*U; Vector3<Real> MV = mat*V; Vector3<Real> MN = mat*plane.Normal; Real DmNdE = -plane.DistanceTo(eye); P = eye + DmNdE*plane.Normal; Matrix2<Real> AOut; Vector2<Real> BOut; Real COut; AOut[0][0] = U.Dot(MU); AOut[0][1] = U.Dot(MV); AOut[1][1] = V.Dot(MV); AOut[1][0] = AOut[0][1]; BOut[0] = ((Real)2)*DmNdE*(U.Dot(MN)); BOut[1] = ((Real)2)*DmNdE*(V.Dot(MN)); COut = DmNdE*DmNdE*(plane.Normal.Dot(MN)); ellipse.FromCoefficients(AOut, BOut, COut); }
/// \brief Calculate the \p centroid of the polygon defined by \p winding which lies on plane \p plane. void Winding_Centroid(const Winding& winding, const Plane3& plane, Vector3& centroid) { double area2 = 0, x_sum = 0, y_sum = 0; const ProjectionAxis axis = projectionaxis_for_normal(plane.normal()); const indexremap_t remap = indexremap_for_projectionaxis(axis); for(std::size_t i = winding.numpoints-1, j = 0; j < winding.numpoints; i = j, ++j) { const double ai = winding[i].vertex[remap.x] * winding[j].vertex[remap.y] - winding[j].vertex[remap.x] * winding[i].vertex[remap.y]; area2 += ai; x_sum += (winding[j].vertex[remap.x] + winding[i].vertex[remap.x]) * ai; y_sum += (winding[j].vertex[remap.y] + winding[i].vertex[remap.y]) * ai; } centroid[remap.x] = static_cast<float>(x_sum / (3 * area2)); centroid[remap.y] = static_cast<float>(y_sum / (3 * area2)); { Ray ray(Vector3(0, 0, 0), Vector3(0, 0, 0)); ray.origin[remap.x] = centroid[remap.x]; ray.origin[remap.y] = centroid[remap.y]; ray.direction[remap.z] = 1; centroid[remap.z] = static_cast<float>(ray_distance_to_plane(ray, plane)); } }
scene::INodePtr BrushDef3Parser::parse(parser::DefTokeniser& tok) const { // Create a new brush scene::INodePtr node = GlobalBrushCreator().createBrush(); // Cast the node, this must succeed IBrushNodePtr brushNode = boost::dynamic_pointer_cast<IBrushNode>(node); assert(brushNode != NULL); IBrush& brush = brushNode->getIBrush(); tok.assertNextToken("{"); // Parse face tokens until a closing brace is encountered while (1) { std::string token = tok.nextToken(); // Token should be either a "(" (start of face) or "}" (end of brush) if (token == "}") { break; // end of brush } else if (token == "(") // FACE { // Construct a plane and parse its values Plane3 plane; plane.normal().x() = string::to_float(tok.nextToken()); plane.normal().y() = string::to_float(tok.nextToken()); plane.normal().z() = string::to_float(tok.nextToken()); plane.dist() = -string::to_float(tok.nextToken()); // negate d tok.assertNextToken(")"); // Parse TexDef Matrix4 texdef; tok.assertNextToken("("); tok.assertNextToken("("); texdef.xx() = string::to_float(tok.nextToken()); texdef.yx() = string::to_float(tok.nextToken()); texdef.tx() = string::to_float(tok.nextToken()); tok.assertNextToken(")"); tok.assertNextToken("("); texdef.xy() = string::to_float(tok.nextToken()); texdef.yy() = string::to_float(tok.nextToken()); texdef.ty() = string::to_float(tok.nextToken()); tok.assertNextToken(")"); tok.assertNextToken(")"); // Parse Shader std::string shader = tok.nextToken(); // Parse Flags (usually each brush has all faces detail or all faces structural) IBrush::DetailFlag flag = static_cast<IBrush::DetailFlag>( string::convert<std::size_t>(tok.nextToken(), IBrush::Structural)); brush.setDetailFlag(flag); // Ignore the other two flags tok.skipTokens(2); // Finally, add the new face to the brush /*IFace& face = */brush.addFace(plane, texdef, shader); } else { std::string text = (boost::format(_("BrushDef3Parser: invalid token '%s'")) % token).str(); throw parser::ParseException(text); } } // Final outer "}" tok.assertNextToken("}"); return node; }
int ConvexClipper<Real>::Clip (const Plane3<Real>& plane) { // Sompute signed distances from vertices to plane. int numPositive = 0, numNegative = 0; const int numVertices = (int)mVertices.size(); for (int v = 0; v < numVertices; ++v) { Vertex& vertex = mVertices[v]; if (vertex.Visible) { vertex.Distance = plane.DistanceTo(vertex.Point); if (vertex.Distance >= mEpsilon) { ++numPositive; } else if (vertex.Distance <= -mEpsilon) { ++numNegative; vertex.Visible = false; } else { // The point is on the plane (within floating point // tolerance). vertex.Distance = (Real)0; } } } if (numPositive == 0) { // Mesh is in negative half-space, fully clipped. return -1; } if (numNegative == 0) { // Mesh is in positive half-space, fully visible. return +1; } // Clip the visible edges. const int numEdges = (int)mEdges.size(); for (int e = 0; e < numEdges; ++e) { Edge& edge = mEdges[e]; if (edge.Visible) { int v0 = edge.Vertex[0]; int v1 = edge.Vertex[1]; int f0 = edge.Face[0]; int f1 = edge.Face[1]; Face& face0 = mFaces[f0]; Face& face1 = mFaces[f1]; Real d0 = mVertices[v0].Distance; Real d1 = mVertices[v1].Distance; if (d0 <= (Real)0 && d1 <= (Real)0) { // The edge is culled. If the edge is exactly on the clip // plane, it is possible that a visible triangle shares it. // The edge will be re-added during the face loop. face0.Edges.erase(e); if (face0.Edges.empty()) { face0.Visible = false; } face1.Edges.erase(e); if (face1.Edges.empty()) { face1.Visible = false; } edge.Visible = false; continue; } if (d0 >= (Real)0 && d1 >= (Real)0) { // Face retains the edge. continue; } // The edge is split by the plane. Compute the point of // intersection. If the old edge is <V0,V1> and I is the // intersection point, the new edge is <V0,I> when d0 > 0 or // <I,V1> when d1 > 0. int vNew = (int)mVertices.size(); mVertices.push_back(Vertex()); Vertex& vertexNew = mVertices[vNew]; Vector3<Real>& point0 = mVertices[v0].Point; Vector3<Real>& point1 = mVertices[v1].Point; vertexNew.Point = point0 + (d0/(d0 - d1))*(point1 - point0); if (d0 > (Real)0) { edge.Vertex[1] = vNew; } else { edge.Vertex[0] = vNew; } } } // The mesh straddles the plane. A new convex polygonal face will be // generated. Add it now and insert edges when they are visited. int fNew = (int)mFaces.size(); mFaces.push_back(Face()); Face& faceNew = mFaces[fNew]; faceNew.Plane = plane; // Process the faces. for (int f = 0; f < fNew; ++f) { Face& face = mFaces[f]; if (face.Visible) { // Determine if the face is on the negative side, the positive // side, or split by the clipping plane. The Occurs members // are set to zero to help find the end points of the polyline // that results from clipping a face. assertion(face.Edges.size() >= 2, "Unexpected condition.\n"); std::set<int>::iterator iter = face.Edges.begin(); std::set<int>::iterator end = face.Edges.end(); while (iter != end) { int e = *iter++; Edge& edge = mEdges[e]; assertion(edge.Visible, "Unexpected condition.\n"); mVertices[edge.Vertex[0]].Occurs = 0; mVertices[edge.Vertex[1]].Occurs = 0; } int vStart, vFinal; if (GetOpenPolyline(face, vStart, vFinal)) { // Polyline is open, close it up. int eNew = (int)mEdges.size(); mEdges.push_back(Edge()); Edge& edgeNew = mEdges[eNew]; edgeNew.Vertex[0] = vStart; edgeNew.Vertex[1] = vFinal; edgeNew.Face[0] = f; edgeNew.Face[1] = fNew; // Add new edge to polygons. face.Edges.insert(eNew); faceNew.Edges.insert(eNew); } } } // Process 'faceNew' to make sure it is a simple polygon (theoretically // convex, but numerically may be slightly not convex). Floating-point // round-off errors can cause the new face from the last loop to be // needle-like with a collapse of two edges into a single edge. This // block guarantees the invariant "face always a simple polygon". Postprocess(fNew, faceNew); if (faceNew.Edges.size() < 3) { // Face is completely degenerate, remove it from mesh. mFaces.pop_back(); } return 0; }
void IntrTetrahedron3Tetrahedron3<Real>::SplitAndDecompose ( Tetrahedron3<Real> tetra, const Plane3<Real>& plane, std::vector<Tetrahedron3<Real> >& inside) { // Determine on which side of the plane the points of the tetrahedron lie. Real C[4]; int i, pos[4], neg[4], zer[4]; int positive = 0, negative = 0, zero = 0; for (i = 0; i < 4; ++i) { C[i] = plane.DistanceTo(tetra.V[i]); if (C[i] > (Real)0) { pos[positive++] = i; } else if (C[i] < (Real)0) { neg[negative++] = i; } else { zer[zero++] = i; } } // For a split to occur, one of the c_i must be positive and one must // be negative. if (negative == 0) { // Tetrahedron is completely on the positive side of plane, full clip. return; } if (positive == 0) { // Tetrahedron is completely on the negative side of plane. inside.push_back(tetra); return; } // Tetrahedron is split by plane. Determine how it is split and how to // decompose the negative-side portion into tetrahedra (6 cases). Real w0, w1, invCDiff; Vector3<Real> intp[4]; if (positive == 3) { // +++- for (i = 0; i < positive; ++i) { invCDiff = ((Real)1)/(C[pos[i]] - C[neg[0]]); w0 = -C[neg[0]]*invCDiff; w1 = +C[pos[i]]*invCDiff; tetra.V[pos[i]] = w0*tetra.V[pos[i]] + w1*tetra.V[neg[0]]; } inside.push_back(tetra); } else if (positive == 2) { if (negative == 2) { // ++-- for (i = 0; i < positive; ++i) { invCDiff = ((Real)1)/(C[pos[i]] - C[neg[0]]); w0 = -C[neg[0]]*invCDiff; w1 = +C[pos[i]]*invCDiff; intp[i] = w0*tetra.V[pos[i]] + w1*tetra.V[neg[0]]; } for (i = 0; i < negative; ++i) { invCDiff = ((Real)1)/(C[pos[i]] - C[neg[1]]); w0 = -C[neg[1]]*invCDiff; w1 = +C[pos[i]]*invCDiff; intp[i+2] = w0*tetra.V[pos[i]] + w1*tetra.V[neg[1]]; } tetra.V[pos[0]] = intp[2]; tetra.V[pos[1]] = intp[1]; inside.push_back(tetra); inside.push_back(Tetrahedron3<Real>(tetra.V[neg[1]], intp[3], intp[2], intp[1])); inside.push_back(Tetrahedron3<Real>(tetra.V[neg[0]], intp[0], intp[1], intp[2])); } else { // ++-0 for (i = 0; i < positive; ++i) { invCDiff = ((Real)1)/(C[pos[i]] - C[neg[0]]); w0 = -C[neg[0]]*invCDiff; w1 = +C[pos[i]]*invCDiff; tetra.V[pos[i]] = w0*tetra.V[pos[i]] + w1*tetra.V[neg[0]]; } inside.push_back(tetra); } } else if (positive == 1) { if (negative == 3) { // +--- for (i = 0; i < negative; ++i) { invCDiff = ((Real)1)/(C[pos[0]] - C[neg[i]]); w0 = -C[neg[i]]*invCDiff; w1 = +C[pos[0]]*invCDiff; intp[i] = w0*tetra.V[pos[0]] + w1*tetra.V[neg[i]]; } tetra.V[pos[0]] = intp[0]; inside.push_back(tetra); inside.push_back(Tetrahedron3<Real>(intp[0], tetra.V[neg[1]], tetra.V[neg[2]], intp[1])); inside.push_back(Tetrahedron3<Real>(tetra.V[neg[2]], intp[1], intp[2], intp[0])); } else if (negative == 2) { // +--0 for (i = 0; i < negative; ++i) { invCDiff = ((Real)1)/(C[pos[0]] - C[neg[i]]); w0 = -C[neg[i]]*invCDiff; w1 = +C[pos[0]]*invCDiff; intp[i] = w0*tetra.V[pos[0]] + w1*tetra.V[neg[i]]; } tetra.V[pos[0]] = intp[0]; inside.push_back(tetra); inside.push_back(Tetrahedron3<Real>(intp[1], tetra.V[zer[0]], tetra.V[neg[1]], intp[0])); } else { // +-00 invCDiff = ((Real)1)/(C[pos[0]] - C[neg[0]]); w0 = -C[neg[0]]*invCDiff; w1 = +C[pos[0]]*invCDiff; tetra.V[pos[0]] = w0*tetra.V[pos[0]] + w1*tetra.V[neg[0]]; inside.push_back(tetra); } } }
Vector3 reflectVector(Vector3 const & v, Plane3 const & mirror_plane) { // Assume Plane3::normal() is unit return v - 2 * v.dot(mirror_plane.getNormal()) * mirror_plane.getNormal(); }
Vector3 reflectPoint(Vector3 const & p, Plane3 const & mirror_plane) { // Assume Plane3::normal() is unit return p - 2 * (p - mirror_plane.getPoint()).dot(mirror_plane.getNormal()) * mirror_plane.getNormal(); }
void ParaxialTexCoordSystem::doTransform(const Plane3& oldBoundary, const Mat4x4& transformation, BrushFaceAttributes& attribs, bool lockTexture, const Vec3& oldInvariant) { const Vec3 offset = transformation * Vec3::Null; const Vec3& oldNormal = oldBoundary.normal; Vec3 newNormal = transformation * oldNormal - offset; assert(Math::eq(newNormal.length(), 1.0)); // fix some rounding errors - if the old and new texture axes are almost the same, use the old axis if (newNormal.equals(oldNormal, 0.01)) newNormal = oldNormal; if (!lockTexture || attribs.xScale() == 0.0f || attribs.yScale() == 0.0f) { setRotation(newNormal, attribs.rotation(), attribs.rotation()); return; } // calculate the current texture coordinates of the origin const Vec2f oldInvariantTexCoords = computeTexCoords(oldInvariant, attribs.scale()) + attribs.offset(); // project the texture axes onto the boundary plane along the texture Z axis const Vec3 boundaryOffset = oldBoundary.project(Vec3::Null, getZAxis()); const Vec3 oldXAxisOnBoundary = oldBoundary.project(m_xAxis * attribs.xScale(), getZAxis()) - boundaryOffset; const Vec3 oldYAxisOnBoundary = oldBoundary.project(m_yAxis * attribs.yScale(), getZAxis()) - boundaryOffset; // transform the projected texture axes and compensate the translational component const Vec3 transformedXAxis = transformation * oldXAxisOnBoundary - offset; const Vec3 transformedYAxis = transformation * oldYAxisOnBoundary - offset; const Vec2f textureSize = attribs.textureSize(); const bool preferX = textureSize.x() >= textureSize.y(); /* const FloatType dotX = transformedXAxis.normalized().dot(oldXAxisOnBoundary.normalized()); const FloatType dotY = transformedYAxis.normalized().dot(oldYAxisOnBoundary.normalized()); const bool preferX = Math::abs(dotX) < Math::abs(dotY); */ // obtain the new texture plane norm and the new base texture axes Vec3 newBaseXAxis, newBaseYAxis, newProjectionAxis; const size_t newIndex = planeNormalIndex(newNormal); axes(newIndex, newBaseXAxis, newBaseYAxis, newProjectionAxis); const Plane3 newTexturePlane(0.0, newProjectionAxis); // project the transformed texture axes onto the new texture projection plane const Vec3 projectedTransformedXAxis = newTexturePlane.project(transformedXAxis); const Vec3 projectedTransformedYAxis = newTexturePlane.project(transformedYAxis); assert(!projectedTransformedXAxis.nan() && !projectedTransformedYAxis.nan()); const Vec3 normalizedXAxis = projectedTransformedXAxis.normalized(); const Vec3 normalizedYAxis = projectedTransformedYAxis.normalized(); // determine the rotation angle from the dot product of the new base axes and the transformed, projected and normalized texture axes float cosX = static_cast<float>(newBaseXAxis.dot(normalizedXAxis.normalized())); float cosY = static_cast<float>(newBaseYAxis.dot(normalizedYAxis.normalized())); assert(!Math::isnan(cosX)); assert(!Math::isnan(cosY)); float radX = std::acos(cosX); if (crossed(newBaseXAxis, normalizedXAxis).dot(newProjectionAxis) < 0.0) radX *= -1.0f; float radY = std::acos(cosY); if (crossed(newBaseYAxis, normalizedYAxis).dot(newProjectionAxis) < 0.0) radY *= -1.0f; // TODO: be smarter about choosing between the X and Y axis rotations - sometimes either // one can be better float rad = preferX ? radX : radY; // for some reason, when the texture plane normal is the Y axis, we must rotation clockwise if (newIndex == 4) rad *= -1.0f; const float newRotation = Math::correct(Math::normalizeDegrees(Math::degrees(rad)), 4); doSetRotation(newNormal, newRotation, newRotation); // finally compute the scaling factors Vec2f newScale = Vec2f(projectedTransformedXAxis.length(), projectedTransformedYAxis.length()).corrected(4); // the sign of the scaling factors depends on the angle between the new texture axis and the projected transformed axis if (m_xAxis.dot(normalizedXAxis) < 0.0) newScale[0] *= -1.0f; if (m_yAxis.dot(normalizedYAxis) < 0.0) newScale[1] *= -1.0f; // compute the parameters of the transformed texture coordinate system const Vec3 newInvariant = transformation * oldInvariant; // determine the new texture coordinates of the transformed center of the face, sans offsets const Vec2f newInvariantTexCoords = computeTexCoords(newInvariant, newScale); // since the center should be invariant, the offsets are determined by the difference of the current and // the original texture coordiknates of the center const Vec2f newOffset = attribs.modOffset(oldInvariantTexCoords - newInvariantTexCoords).corrected(4); assert(!newOffset.nan()); assert(!newScale.nan()); assert(!Math::isnan(newRotation)); assert(!Math::zero(newScale.x())); assert(!Math::zero(newScale.y())); attribs.setOffset(newOffset); attribs.setScale(newScale); attribs.setRotation(newRotation); }