예제 #1
0
bool Intersect::sameSideOfPlane(const Vec3f& v1, const Vec3f& v2, const Vec3f& v3, const Vec3f& n, FCL_REAL t)
{
  FCL_REAL dist1 = distanceToPlane(n, t, v1);
  FCL_REAL dist2 = dist1 * distanceToPlane(n, t, v2);
  FCL_REAL dist3 = dist1 * distanceToPlane(n, t, v3);
  if((dist2 > 0) && (dist3 > 0))
    return true;
  return false;
}
예제 #2
0
void Intersect::clipSegmentByPlane(const Vec3f& v1, const Vec3f& v2, const Vec3f& n, FCL_REAL t, Vec3f* clipped_point)
{
  FCL_REAL dist1 = distanceToPlane(n, t, v1);
  Vec3f tmp = v2 - v1;
  FCL_REAL dist2 = tmp.dot(n);
  *clipped_point = tmp * (-dist1 / dist2) + v1;
}
예제 #3
0
bool reduceSphereToPlane(const glm::vec4& sphere, const glm::vec4& plane, glm::vec4& reducedSphere) {
    float distance = distanceToPlane(glm::vec3(sphere), plane);

    if (std::abs(distance) <= sphere.w) {
        reducedSphere = glm::vec4(sphere.x - distance * plane.x, sphere.y - distance * plane.y, sphere.z - distance * plane.z, sqrt(sphere.w * sphere.w - distance * distance));
        return true;
    }

    return false;
}
예제 #4
0
/**
 * Check that the point is on the plane AND that it is inside or on@brief
 * MeshObject2D::isOnSide one of the triangles that defines the plane. Both must
 * be true.
 * @param point : Point to test
 * @return : True only if the point is valid
 */
bool MeshObject2D::isValid(const Kernel::V3D &point) const {

  static const double tolerance = 1e-9;
  if (distanceToPlane(point) < tolerance) {
    for (size_t i = 0; i < m_vertices.size(); i += 3) {

      if (MeshObjectCommon::isOnTriangle(point, m_vertices[i],
                                         m_vertices[i + 1], m_vertices[i + 2]))
        return true;
    }
  }
  return false;
}
예제 #5
0
Point getPointOnPlane(Point point, Plane plane){
	Point pointOnPlane;

	double dist = distanceToPlane(point, plane);
	Vector normal = plane.normal;

	Vector v;
	v.dx = dist*normal.dx;
	v.dy = dist*normal.dy;
	v.dz = dist*normal.dz;

	pointOnPlane.x = point.x - v.dx;
	pointOnPlane.y = point.y - v.dy;
	pointOnPlane.z = point.z - v.dz;

	return pointOnPlane;
}
예제 #6
0
void Intersect::computeDeepestPoints(Vec3f* clipped_points, unsigned int num_clipped_points, const Vec3f& n, FCL_REAL t, FCL_REAL* penetration_depth, Vec3f* deepest_points, unsigned int* num_deepest_points)
{
  *num_deepest_points = 0;
  FCL_REAL max_depth = -std::numeric_limits<FCL_REAL>::max();
  unsigned int num_deepest_points_ = 0;
  unsigned int num_neg = 0;
  unsigned int num_pos = 0;
  unsigned int num_zero = 0;

  for(unsigned int i = 0; i < num_clipped_points; ++i)
  {
    FCL_REAL dist = -distanceToPlane(n, t, clipped_points[i]);
    if(dist > EPSILON) num_pos++;
    else if(dist < -EPSILON) num_neg++;
    else num_zero++;
    if(dist > max_depth)
    {
      max_depth = dist;
      num_deepest_points_ = 1;
      deepest_points[num_deepest_points_ - 1] = clipped_points[i];
    }
    else if(dist + 1e-6 >= max_depth)
    {
      num_deepest_points_++;
      deepest_points[num_deepest_points_ - 1] = clipped_points[i];
    }
  }

  if(max_depth < -EPSILON)
    num_deepest_points_ = 0;

  if(num_zero == 0 && ((num_neg == 0) || (num_pos == 0)))
    num_deepest_points_ = 0;

  *penetration_depth = max_depth;
  *num_deepest_points = num_deepest_points_;
}
예제 #7
0
double Vector3D::distanceToPlane(const Vector3D &plane1, const Vector3D &plane2, const Vector3D &plane3) const
{
    Vector3D normal = Vector3D::normal(plane1,plane2,plane3);
    return distanceToPlane(plane1, normal);
}
예제 #8
0
void Intersect::clipPolygonByPlane(Vec3f* polygon_points, unsigned int num_polygon_points, const Vec3f& n, FCL_REAL t, Vec3f clipped_points[], unsigned int* num_clipped_points)
{
  *num_clipped_points = 0;

  unsigned int num_clipped_points_ = 0;
  unsigned int vi;
  unsigned int prev_classify = 2;
  unsigned int classify;
  for(unsigned int i = 0; i <= num_polygon_points; ++i)
  {
    vi = (i % num_polygon_points);
    FCL_REAL d = distanceToPlane(n, t, polygon_points[i]);
    classify = ((d > EPSILON) ? 1 : 0);
    if(classify == 0)
    {
      if(prev_classify == 1)
      {
        if(num_clipped_points_ < MAX_TRIANGLE_CLIPS)
        {
          Vec3f tmp;
          clipSegmentByPlane(polygon_points[i - 1], polygon_points[vi], n, t, &tmp);
          if(num_clipped_points_ > 0)
          {
            if((tmp - clipped_points[num_clipped_points_ - 1]).sqrLength() > EPSILON)
            {
              clipped_points[num_clipped_points_] = tmp;
              num_clipped_points_++;
            }
          }
          else
          {
            clipped_points[num_clipped_points_] = tmp;
            num_clipped_points_++;
          }
        }
      }

      if(num_clipped_points_ < MAX_TRIANGLE_CLIPS && i < num_polygon_points)
      {
        clipped_points[num_clipped_points_] = polygon_points[vi];
        num_clipped_points_++;
      }
    }
    else
    {
      if(prev_classify == 0)
      {
        if(num_clipped_points_ < MAX_TRIANGLE_CLIPS)
        {
          Vec3f tmp;
          clipSegmentByPlane(polygon_points[i - 1], polygon_points[vi], n, t, &tmp);
          if(num_clipped_points_ > 0)
          {
            if((tmp - clipped_points[num_clipped_points_ - 1]).sqrLength() > EPSILON)
            {
              clipped_points[num_clipped_points_] = tmp;
              num_clipped_points_++;
            }
          }
          else
          {
            clipped_points[num_clipped_points_] = tmp;
            num_clipped_points_++;
          }
        }
      }
    }

    prev_classify = classify;
  }

  if(num_clipped_points_ > 2)
  {
    if((clipped_points[0] - clipped_points[num_clipped_points_ - 1]).sqrLength() < EPSILON)
    {
      num_clipped_points_--;
    }
  }

  *num_clipped_points = num_clipped_points_;
}
예제 #9
0
glm::ivec3 LightClusters::updateClusters() {
    // Make sure resource are in good shape
    updateClusterResource();

    // Clean up last info
    uint32_t numClusters = (uint32_t)_clusterGrid.size();

    std::vector< std::vector< LightIndex > > clusterGridPoint(numClusters);
    std::vector< std::vector< LightIndex > > clusterGridSpot(numClusters);

    _clusterGrid.clear();
    _clusterGrid.resize(numClusters, EMPTY_CLUSTER);

    uint32_t maxNumIndices = (uint32_t)_clusterContent.size();
    _clusterContent.clear();
    _clusterContent.resize(maxNumIndices, INVALID_LIGHT);


    auto theFrustumGrid(_frustumGridBuffer.get());

    glm::ivec3 gridPosToOffset(1, theFrustumGrid.dims.x, theFrustumGrid.dims.x * theFrustumGrid.dims.y);

    uint32_t numClusterTouched = 0;
    uint32_t numLightsIn = _visibleLightIndices[0];
    uint32_t numClusteredLights = 0;
    for (size_t lightNum = 1; lightNum < _visibleLightIndices.size(); ++lightNum) {
        auto lightId = _visibleLightIndices[lightNum];
        auto light = _lightStage->getLight(lightId);
        if (!light) {
            continue;
        }

        auto worldOri = light->getPosition();
        auto radius = light->getMaximumRadius();
        bool isSpot = light->isSpot();

        // Bring into frustum eye space
        auto eyeOri = theFrustumGrid.frustumGrid_worldToEye(glm::vec4(worldOri, 1.0f));

        // Remove light that slipped through and is not in the z range
        float eyeZMax = eyeOri.z - radius;
        if (eyeZMax > -theFrustumGrid.rangeNear) {
            continue;
        }
        float eyeZMin = eyeOri.z + radius;
        bool beyondFar = false;
        if (eyeZMin < -theFrustumGrid.rangeFar) {
            beyondFar = true;
        }

        // Get z slices
        int zMin = theFrustumGrid.frustumGrid_eyeDepthToClusterLayer(eyeZMin);
        int zMax = theFrustumGrid.frustumGrid_eyeDepthToClusterLayer(eyeZMax);
        // That should never happen
        if (zMin == -2 && zMax == -2) {
            continue;
        }

        // Before Range NEar just apss, range neatr == true near for now
        if ((zMin == -1) && (zMax == -1)) {
            continue;
        }

        // CLamp the z range 
        zMin = std::max(0, zMin);

        auto xLeftDistance = radius - distanceToPlane(eyeOri, _gridPlanes[0][0]);
        auto xRightDistance = radius + distanceToPlane(eyeOri, _gridPlanes[0].back());

        auto yBottomDistance = radius - distanceToPlane(eyeOri, _gridPlanes[1][0]);
        auto yTopDistance = radius + distanceToPlane(eyeOri, _gridPlanes[1].back());

        if ((xLeftDistance < 0.f) || (xRightDistance < 0.f) || (yBottomDistance < 0.f) || (yTopDistance < 0.f)) {
            continue;
        }

        // find 2D corners of the sphere in grid
        int xMin { 0 };
        int xMax { theFrustumGrid.dims.x - 1 };
        int yMin { 0 };
        int yMax { theFrustumGrid.dims.y - 1 };

        float radius2 = radius * radius;

        auto eyeOriH = glm::vec3(eyeOri);
        auto eyeOriV = glm::vec3(eyeOri);

        eyeOriH.y = 0.0f;
        eyeOriV.x = 0.0f;

        float eyeOriLen2H = glm::length2(eyeOriH);
        float eyeOriLen2V = glm::length2(eyeOriV);

        if ((eyeOriLen2H > radius2)) {
            float eyeOriLenH = sqrt(eyeOriLen2H);

            auto eyeOriDirH = glm::vec3(eyeOriH) / eyeOriLenH;

            float eyeToTangentCircleLenH = sqrt(eyeOriLen2H - radius2);

            float eyeToTangentCircleCosH = eyeToTangentCircleLenH / eyeOriLenH;

            float eyeToTangentCircleSinH = radius / eyeOriLenH;


            // rotate the eyeToOriDir (H & V) in both directions
            glm::vec3 leftDir(eyeOriDirH.x * eyeToTangentCircleCosH + eyeOriDirH.z * eyeToTangentCircleSinH, 0.0f, eyeOriDirH.x * -eyeToTangentCircleSinH + eyeOriDirH.z * eyeToTangentCircleCosH);
            glm::vec3 rightDir(eyeOriDirH.x * eyeToTangentCircleCosH - eyeOriDirH.z * eyeToTangentCircleSinH, 0.0f, eyeOriDirH.x * eyeToTangentCircleSinH + eyeOriDirH.z * eyeToTangentCircleCosH);

            auto lc = theFrustumGrid.frustumGrid_eyeToClusterDirH(leftDir);
            if (lc > xMax) {
                lc = xMin;
            }
            auto rc = theFrustumGrid.frustumGrid_eyeToClusterDirH(rightDir);
            if (rc < 0) {
                rc = xMax;
            }
            xMin = std::max(xMin, lc);
            xMax = std::min(rc, xMax);
            assert(xMin <= xMax);
        }

        if ((eyeOriLen2V > radius2)) {
            float eyeOriLenV = sqrt(eyeOriLen2V);

            auto eyeOriDirV = glm::vec3(eyeOriV) / eyeOriLenV;

            float eyeToTangentCircleLenV = sqrt(eyeOriLen2V - radius2);

            float eyeToTangentCircleCosV = eyeToTangentCircleLenV / eyeOriLenV;

            float eyeToTangentCircleSinV = radius / eyeOriLenV;


            // rotate the eyeToOriDir (H & V) in both directions
            glm::vec3 bottomDir(0.0f, eyeOriDirV.y * eyeToTangentCircleCosV + eyeOriDirV.z * eyeToTangentCircleSinV, eyeOriDirV.y * -eyeToTangentCircleSinV + eyeOriDirV.z * eyeToTangentCircleCosV);
            glm::vec3 topDir(0.0f, eyeOriDirV.y * eyeToTangentCircleCosV - eyeOriDirV.z * eyeToTangentCircleSinV, eyeOriDirV.y * eyeToTangentCircleSinV + eyeOriDirV.z * eyeToTangentCircleCosV);

            auto bc = theFrustumGrid.frustumGrid_eyeToClusterDirV(bottomDir);
            auto tc = theFrustumGrid.frustumGrid_eyeToClusterDirV(topDir);
            if (bc > yMax) {
                bc = yMin;
            }
            if (tc < 0) {
                tc = yMax;
            }
            yMin = std::max(yMin, bc);
            yMax =std::min(tc, yMax);
            assert(yMin <= yMax);
        }

        // now voxelize
        auto& clusterGrid = (isSpot ? clusterGridSpot : clusterGridPoint);
        if (beyondFar) {
            numClusterTouched += scanLightVolumeBoxSlice(theFrustumGrid, _gridPlanes, zMin, yMin, yMax, xMin, xMax, lightId, glm::vec4(glm::vec3(eyeOri), radius), clusterGrid);
        } else {
            numClusterTouched += scanLightVolumeSphere(theFrustumGrid, _gridPlanes, zMin, zMax, yMin, yMax, xMin, xMax, lightId, glm::vec4(glm::vec3(eyeOri), radius), clusterGrid);
        }

        numClusteredLights++;
    }

    // Lights have been gathered now reexpress in terms of 2 sequential buffers
    // Start filling from near to far and stops if it overflows
    bool checkBudget = false;
    if (numClusterTouched > maxNumIndices) {
        checkBudget = true;
    }
    uint16_t indexOffset = 0;
    for (int i = 0; i < (int) clusterGridPoint.size(); i++) {
        auto& clusterPoint = clusterGridPoint[i];
        auto& clusterSpot = clusterGridSpot[i];

        uint8_t numLightsPoint = ((uint8_t)clusterPoint.size());
        uint8_t numLightsSpot = ((uint8_t)clusterSpot.size());
        uint16_t numLights = numLightsPoint + numLightsSpot;
        uint16_t offset = indexOffset;

        // Check for overflow
        if (checkBudget) {
            if ((indexOffset + numLights) > (uint16_t) maxNumIndices) {
                break;
            }
        }

        // Encode the cluster grid: [ ContentOffset - 16bits, Num Point LIghts - 8bits, Num Spot Lights - 8bits] 
        _clusterGrid[i] = (uint32_t)((0xFF000000 & (numLightsSpot << 24)) | (0x00FF0000 & (numLightsPoint << 16)) | (0x0000FFFF & offset));


        if (numLightsPoint) {
            memcpy(_clusterContent.data() + indexOffset, clusterPoint.data(), numLightsPoint * sizeof(LightIndex));
            indexOffset += numLightsPoint;
        }
        if (numLightsSpot) {
            memcpy(_clusterContent.data() + indexOffset, clusterSpot.data(), numLightsSpot * sizeof(LightIndex));
            indexOffset += numLightsSpot;
        }
    }

    // update the buffers
    _clusterGridBuffer._buffer->setData(_clusterGridBuffer._size, (gpu::Byte*) _clusterGrid.data());
    _clusterContentBuffer._buffer->setSubData(0, indexOffset * sizeof(LightIndex), (gpu::Byte*) _clusterContent.data());
    
    return glm::ivec3(numLightsIn, numClusteredLights, numClusterTouched);
}
예제 #10
0
uint32_t scanLightVolumeSphere(FrustumGrid& grid, const FrustumGrid::Planes planes[3], int zMin, int zMax, int yMin, int yMax, int xMin, int xMax, LightClusters::LightID lightId, const glm::vec4& eyePosRadius,
    std::vector< std::vector<LightClusters::LightIndex>>& clusterGrid) {
    glm::ivec3 gridPosToOffset(1, grid.dims.x, grid.dims.x * grid.dims.y);
    uint32_t numClustersTouched = 0;
    const auto& xPlanes = planes[0];
    const auto& yPlanes = planes[1];
    const auto& zPlanes = planes[2];

    // FInd the light origin cluster
    auto centerCluster = grid.frustumGrid_eyeToClusterPos(glm::vec3(eyePosRadius));

    int center_z = centerCluster.z;
    int center_y = centerCluster.y;

    for (auto z = zMin; (z <= zMax); z++) {
        auto zSphere = eyePosRadius;
        if (z != center_z) {
            auto plane = (z < center_z) ? zPlanes[z + 1] : -zPlanes[z];
            if (!reduceSphereToPlane(zSphere, plane, zSphere)) {
                // pass this slice!
                continue;
            }
        }
        for (auto y = yMin; (y <= yMax); y++) {
            auto ySphere = zSphere;
            if (y != center_y) {
                auto plane = (y < center_y) ? yPlanes[y + 1] : -yPlanes[y];
                if (!reduceSphereToPlane(ySphere, plane, ySphere)) {
                    // pass this slice!
                    continue;
                }
            }

            glm::vec3 spherePoint(ySphere);

            auto x = xMin;
            for (; (x < xMax); ++x) {
                const auto& plane = xPlanes[x + 1];
                auto testDistance = distanceToPlane(spherePoint, plane) + ySphere.w;
                if (testDistance >= 0.0f) {
                    break;
                }
            }
            auto xs = xMax;
            for (; (xs >= x); --xs) {
                auto plane = -xPlanes[xs];
                auto testDistance = distanceToPlane(spherePoint, plane) + ySphere.w;
                if (testDistance >= 0.0f) {
                    break;
                }
            }

            for (; (x <= xs); x++) {
                auto index = grid.frustumGrid_clusterToIndex(ivec3(x, y, z));
                if (index < (int)clusterGrid.size()) {
                    clusterGrid[index].emplace_back(lightId);
                    numClustersTouched++;
                } else {
                    qCDebug(renderutils) << "WARNING: LightClusters::scanLightVolumeSphere invalid index found ? numClusters = " << clusterGrid.size() << " index = " << index << " found from cluster xyz = " << x << " " << y << " " << z;
                }
            }
        }
    }

    return numClustersTouched;
}
    campvis::FaceGeometry FaceGeometry::clipAgainstPlane(float p, const cgt::vec3& pNormal, float epsilon /*= 1e-4f*/) const {
        cgtAssert(epsilon >= 0, "Epsilon must be positive.");

        std::vector<cgt::vec3> verts, texCoords, norms;
        std::vector<cgt::vec4> cols;
        std::vector<cgt::col4> picks;
        size_t lastIndex = _vertices.size() - 1;
        float lastDistance = distanceToPlane(_vertices.back(), p, pNormal, epsilon);

        // Implementation of Sutherland-Hodgman polygon clipping:
        for (size_t i = 0; i < _vertices.size(); ++i) {
            float currrentDistance = distanceToPlane(_vertices[i], p, pNormal, epsilon);

            // case 1: last vertex outside, this vertex inside clip region => clip
            if (lastDistance > 0 && currrentDistance <= 0) {
                float t = lastDistance / (lastDistance - currrentDistance);
                
                verts.push_back(cgt::mix(_vertices[lastIndex], _vertices[i], t));
                if (!_textureCoordinates.empty())
                    texCoords.push_back(cgt::mix(_textureCoordinates[lastIndex], _textureCoordinates[i], t));
                if (!_colors.empty())
                    cols.push_back(cgt::mix(_colors[lastIndex], _colors[i], t));
                if (!_normals.empty())
                    norms.push_back(cgt::mix(_normals[lastIndex], _normals[i], t));
                if (!_pickingInformation.empty())
                    picks.push_back(_pickingInformation[i]);
            }
            // case 2: last vertex inside, this vertex outside clip region => clip
            else if (lastDistance <= 0 && currrentDistance > 0) {
                float t = lastDistance / (lastDistance - currrentDistance);

                verts.push_back(cgt::mix(_vertices[lastIndex], _vertices[i], t));
                if (!_textureCoordinates.empty())
                    texCoords.push_back(cgt::mix(_textureCoordinates[lastIndex], _textureCoordinates[i], t));
                if (!_colors.empty())
                    cols.push_back(cgt::mix(_colors[lastIndex], _colors[i], t));
                if (!_normals.empty())
                    norms.push_back(cgt::mix(_normals[lastIndex], _normals[i], t));
                if (!_pickingInformation.empty())
                    picks.push_back(_pickingInformation[lastIndex]);
            }

            // case 1.2 + case 3: current vertix in front of plane => keep
            if (currrentDistance <= 0) {
                verts.push_back(_vertices[i]);
                if (!_textureCoordinates.empty())
                    texCoords.push_back(_textureCoordinates[i]);
                if (!_colors.empty())
                    cols.push_back(_colors[i]);
                if (!_normals.empty())
                    norms.push_back(_normals[i]);
                if (!_pickingInformation.empty())
                    picks.push_back(_pickingInformation[i]);
            }

            lastIndex = i;
            lastDistance = currrentDistance;
        }

        FaceGeometry toReturn(verts, texCoords, cols, norms);
        toReturn.setPickingInformation(picks);
        return toReturn;
    }
예제 #12
0
void ntlTree::intersectX(const ntlRay &ray, gfxReal &distance, 
		ntlVec3Gfx &normal, 
		ntlTriangle *&tri, 
		int flags, bool forceNonsmooth) const
{
  gfxReal mint = GFX_REAL_MAX;  /* current minimal t */
  ntlVec3Gfx  retnormal;       /* intersection (interpolated) normal */
	gfxReal mintu=0.0, mintv=0.0;    /* u,v for min t intersection */

  BSPNode *curr, *nearChild, *farChild; /* current node and children */
  gfxReal  planedist, mindist, maxdist;
  ntlVec3Gfx   pos;

	ntlTriangle *hit = NULL;
	tri = NULL;

  ray.intersectCompleteAABB(mStart,mEnd,mindist,maxdist); // +X

  if((maxdist < 0.0) ||
		 (!mpRoot) ||
     (mindist == GFX_REAL_MAX) ||
     (maxdist == GFX_REAL_MAX) ) {
    distance = -1.0;
    return;
  }
  mindist -= getVecEpsilon();
  maxdist += getVecEpsilon();

  /* stack init */
  mpNodeStack->elem[0].node = NULL;
  mpNodeStack->stackPtr = 1;

  curr = mpRoot;  
  mint = GFX_REAL_MAX;
  while(curr != NULL) { // +X

    while( !curr->isLeaf() ) {
      planedist = distanceToPlane(curr, curr->child[0]->max, ray );
      getChildren(curr, ray.getOrigin(), nearChild, farChild );

			// check ray direction for small plane distances
      if( (planedist>-getVecEpsilon() )&&(planedist< getVecEpsilon() ) ) {
				// ray origin on intersection plane
				planedist = 0.0;
				if(ray.getDirection()[curr->axis]>getVecEpsilon() ) {
					// larger coords
					curr = curr->child[1];
				} else if(ray.getDirection()[curr->axis]<-getVecEpsilon() ) {
					// smaller coords
					curr = curr->child[0];
				} else {
					// paralell, order doesnt really matter are min/max/plane ok?
					mpNodeStack->elem[ mpNodeStack->stackPtr ].node    = curr->child[0];
					mpNodeStack->elem[ mpNodeStack->stackPtr ].mindist = planedist;
					mpNodeStack->elem[ mpNodeStack->stackPtr ].maxdist = maxdist;
					(mpNodeStack->stackPtr)++;
					curr    = curr->child[1];
					maxdist = planedist;
				}
			} else {
				// normal ray
				if( (planedist>maxdist) || (planedist<0.0-getVecEpsilon() ) ) {
					curr = nearChild;
				} else if(planedist < mindist) {
					curr = farChild;
				} else {
					mpNodeStack->elem[ mpNodeStack->stackPtr ].node    = farChild;
					mpNodeStack->elem[ mpNodeStack->stackPtr ].mindist = planedist;
					mpNodeStack->elem[ mpNodeStack->stackPtr ].maxdist = maxdist;
					(mpNodeStack->stackPtr)++;

					curr    = nearChild;
					maxdist = planedist;
				}
			} 
    } // +X
	
    
    /* intersect with current node */
    for (vector<ntlTriangle *>::iterator iter = curr->members->begin();
				 iter != curr->members->end(); iter++ ) {

			/* check for triangle flags before intersecting */
			if((!flags) || ( ((*iter)->getFlags() & flags) > 0 )) {

				if( ((*iter)->getLastRay() == ray.getID() )&&((*iter)->getLastRay()>0) ) {
					// was already intersected...
				} else {
					// we still need to intersect this triangle
					gfxReal u=0.0,v=0.0, t=-1.0;
					ray.intersectTriangleX( mpVertices, (*iter), t,u,v);
					(*iter)->setLastRay( ray.getID() );
					
					if( (t > 0.0) && (t<mint) )  {
						mint = t;	  
						hit = (*iter);
						mintu = u; mintv = v;
					}
				}

			} // flags check
    } // +X

    /* check if intersection is valid */
    if( (mint>0.0) && (mint < GFX_REAL_MAX) ) {
      pos = ray.getOrigin() + ray.getDirection()*mint;

      if( (pos[0] >= curr->min[0]) && (pos[0] <= curr->max[0]) &&
					(pos[1] >= curr->min[1]) && (pos[1] <= curr->max[1]) &&
					(pos[2] >= curr->min[2]) && (pos[2] <= curr->max[2]) ) 
			{

				if(forceNonsmooth) {
					// calculate triangle normal
					ntlVec3Gfx e0,e1,e2;
					e0 = (*mpVertices)[ hit->getPoints()[0] ];
					e1 = (*mpVertices)[ hit->getPoints()[1] ];
					e2 = (*mpVertices)[ hit->getPoints()[2] ];
					retnormal = cross( -(e2-e0), (e1-e0) );
				} else {
					// calculate interpolated normal
					retnormal = (*mpVertNormals)[ hit->getPoints()[0] ] * (1.0-mintu-mintv)+
						(*mpVertNormals)[ hit->getPoints()[1] ]*mintu +
						(*mpVertNormals)[ hit->getPoints()[2] ]*mintv;
				}
				normalize(retnormal);
				normal = retnormal;
				distance = mint;
				tri = hit;
				return;
      }
    }     // +X

    (mpNodeStack->stackPtr)--;
    curr    = mpNodeStack->elem[ mpNodeStack->stackPtr ].node;
    mindist = mpNodeStack->elem[ mpNodeStack->stackPtr ].mindist;
    maxdist = mpNodeStack->elem[ mpNodeStack->stackPtr ].maxdist;
  } /* traverse tree */

	if(mint == GFX_REAL_MAX) {
		distance = -1.0;
	} else {

		// intersection outside the BSP bounding volumes might occur due to roundoff...
		if(forceNonsmooth) {
			// calculate triangle normal
			ntlVec3Gfx e0,e1,e2;
			e0 = (*mpVertices)[ hit->getPoints()[0] ];
			e1 = (*mpVertices)[ hit->getPoints()[1] ];
			e2 = (*mpVertices)[ hit->getPoints()[2] ];
			retnormal = cross( -(e2-e0), (e1-e0) );
		} else {
			// calculate interpolated normal
			retnormal = (*mpVertNormals)[ hit->getPoints()[0] ] * (1.0-mintu-mintv)+
				(*mpVertNormals)[ hit->getPoints()[1] ]*mintu +
				(*mpVertNormals)[ hit->getPoints()[2] ]*mintv;
		}

		normalize(retnormal);
		normal = retnormal;
		distance = mint;
		tri = hit;
	} // +X
	return;
}