2017-03-09 63 views
0

我已经尝试了很多来自openframeworks论坛和文档的代码示例,但我无法得到一个来做我想做的事情。检查ofRay是否与MeshFace相交

我有一个ofRay(来自ofxRay)和一个of3dPrimitive的列表。我试图弄清楚光线是否与原始图像相交,如果是的话,要知道光线与“第一个”相交的原始图像(如哪一个与屏幕最接近)。

void renderer::selectPrimitive(int x, int y, bool shiftHeld) 
{ 
    ofVec3f screenToWorld = (**cam).screenToWorld(ofVec3f(x, y, 0.0)); 

    primitive* intersectPrim = nullptr; 
    int distanceClosest = std::numeric_limits<int>::max(); 

    ofVec3f vectNow = (screenToWorld - (**cam).getPosition()); 

    vectNow = vectNow.normalize(); 

    ofRay ray((**cam).getPosition(), vectNow, true); 
    // To draw the ray on screen, for debugging 
    // rays.push_back(ray); 

    for (primitive& p : *scn) 
    { 
     if (!shiftHeld) 
     { 
      p.setSelected(false); 
     } 

     float* distance = new float(0); 

     bool found = p.checkIntersectionPlaneAndLine(ray, distance); 
     if (found)// && *distance >= 0 && *distance < distanceClosest) 
     { 
      intersectPrim = &p; 
      //distanceClosest = *distance; 
     } 
    } 

    if (distanceClosest < (std::numeric_limits<int>::max() - 1)) 
    { 
     intersectPrim->setSelected(!intersectPrim->getSelected()); 
     std::cout << "Selected Primitive" << std::endl; 
    } 
    else 
    { 
     std::cout << "Selected Nothing" << std::endl; 
    } 
} 

这里有不同的方法我试过,从很多例子在许多网站拼凑起来的,但他们没有正常工作。

第一次尝试:

bool primitive3d::calcTriangleIntersection(ofRay ray, float *result) const { 

    ofMesh mesh = prim->getMesh(); 
    std::vector<ofMeshFace> indices = mesh.getUniqueFaces(); 

    for (std::vector<ofMeshFace>::iterator i = indices.begin(); i != indices.end(); ++i) { 

     ofMeshFace face = *i; 

     ofVec3f edge1, edge2, tvec, pvec, qvec; 
     float det; 
     float u, v; 
     const float EPSILON = 0.000001f; 

     edge1 = face.getVertex(1) - face.getVertex(0); 
     edge2 = face.getVertex(2) - face.getVertex(0); 

     pvec = ray.t.getCrossed(edge2); 
     det = edge1.dot(pvec); 

#if 0 // we don't want to backface cull 
     if (det >= EPSILON) 
     { 
      tvec = getOrigin() - vert0; 

      u = tvec.dot(pvec); 
      if (!((u < 0.0f) || (u > det))) 
      { 

       qvec = tvec.getCrossed(edge1); 
       v = getDirection().dot(qvec); 
       if (!(v < 0.0f || u + v > det)) 
       { 

        *result = edge2.dot(qvec)/det; 
        return true; 
       } 
      } 
     } 
#else 
     if (!(det > -EPSILON && det < EPSILON)) 
     { 
      float inv_det = 1.0f/det; 
      tvec = ray.s - face.getVertex(0); 
      u = tvec.dot(pvec) * inv_det; 
      if (!(u < 0.0f || u > 1.0f)) 
      { 

       qvec = tvec.getCrossed(edge1); 

       v = ray.t.dot(qvec) * inv_det; 
       if (!(v < 0.0f || u + v > 1.0f)) 
       { 

        *result = edge2.dot(qvec) * inv_det; 
        return true; 
       } 
      } 
     } 
#endif 
    } 
    return false; 
} 

第二次尝试:

bool primitive3d::checkIntersectionPlaneAndLine(ofRay ray, float *result) const { 

    ofMesh mesh = prim->getMesh(); 
    std::vector<ofMeshFace> indices = mesh.getUniqueFaces(); 

    for (std::vector<ofMeshFace>::iterator i = indices.begin(); i != indices.end(); ++i) 
    { 

     ofMeshFace face = *i; 

     ofVec3f P1, P2; 
     P1 = ray.getStart(); 
     P2 = ray.getEnd(); 

     ofVec3f p1, p2, p3; 
     p1 = face.getVertex(0); 
     p2 = face.getVertex(1); 
     p3 = face.getVertex(2); 

     ofVec3f v1 = p1 - p2; 
     ofVec3f v2 = p3 - p2; 

     float a, b, c, d; 

     a = v1.y * v2.z - v1.z * v2.y; 
     b = -(v1.x * v2.z - v1.z * v2.x); 
     c = v1.x * v2.y - v1.y * v2.x; 
     d = -(a * p1.x + b * p1.y + c * p1.z); 

     ofVec3f O = P1; 
     ofVec3f V = P2 - P1; 

     float t; 

     t = -(a * O.x + b * O.y + c * O.z + d)/(a * V.x + b * V.y + c * V.z); 

     ofVec3f p = O + V * t; 

     float xmin = std::min(P1.x, P2.x); 
     float ymin = std::min(P1.y, P2.y); 
     float zmin = std::min(P1.z, P2.z); 

     float xmax = std::max(P1.x, P2.x); 
     float ymax = std::max(P1.y, P2.y); 
     float zmax = std::max(P1.z, P2.z); 


     if (inside(p, xmin, xmax, ymin, ymax, zmin, zmax)) { 
      *result = p.length(); 
      return true; 
     } 
    } 
    return false; 
} 

bool primitive3d::inside(ofVec3f p, float xmin, float xmax, float ymin, float ymax, float zmin, float zmax) const { 

    if (p.x >= xmin && p.x <= xmax && p.y >= ymin && p.y <= ymax && p.z >= zmin && p.z <= zmax) 
     return true; 

    return false; 

} 

第三次尝试:

#define SMALL_NUM 0.00000001 // anything that avoids division overflow 
// dot product (3D) which allows vector operations in arguments 
#define dot(u,v) ((u).x * (v).x + (u).y * (v).y + (u).z * (v).z) 

bool primitive3d::checkIntersectionTriangleRay(ofRay ray, ofPoint* inter) 
{ 
    ofMesh mesh = prim->getMesh(); 
    std::vector<ofMeshFace> indices = mesh.getUniqueFaces(); 

    for (std::vector<ofMeshFace>::iterator i = indices.begin(); i != indices.end(); ++i) 
    { 
     ofMeshFace triangle = *i; 

     ofVec3f u, v, n;    // Vecs of triangle 
     ofVec3f dir, w0, w;   // Vecs of ofRay 
     float  r, a, b;    // params to calc ray-plane intersect 

             // get triangle edge vectors and plane normal 
     u = triangle.getVertex(1) - triangle.getVertex(0); 
     v = triangle.getVertex(2) - triangle.getVertex(0); 
     n = u * v;    // cross product 
     if (!(n == ofVec3f(0, 0, 0)))   // if triangle is not degenerate 
     { 

      dir = ray.getEnd() - ray.getStart();    // ray direction vector 
      w0 = ray.getStart() - triangle.getVertex(0); 
      a = -dot(n, w0); 
      b = dot(n, dir); 
      if (!(fabs(b) < SMALL_NUM)) 
      {  // if ray is not parallel to triangle 

       // get intersect point of ray with triangle plane 
       r = a/b; 
       if (!(r < 0.0))     // ray goes toward the triangle 
       { 
        // for a segment, also test if (r > 1.0) => no intersect 

        *inter = ray.getStart() + r * dir;   // intersect point of ray and plane 

                // is I inside T? 
        float uu, uv, vv, wu, wv, D; 
        uu = dot(u, u); 
        uv = dot(u, v); 
        vv = dot(v, v); 
        w = *inter - triangle.getVertex(0); 
        wu = dot(w, u); 
        wv = dot(w, v); 
        D = uv * uv - uu * vv; 

        // get and test parametric coords 
        float s, t; 
        s = (uv * wv - vv * wu)/D; 
        if (!(s < 0.0 || s > 1.0))   // I is inside T 
        { 
         t = (uv * wu - uu * wv)/D; 
         if (!(t < 0.0 || (s + t) > 1.0)) // I is inside T 
          return true;      // I is in T 
        } 
       } 
      } 
     } 
    } 
    return false; 
} 

我试过这么多的事情,但他们没有工作。我也在画我的光线到屏幕上,所以我知道一个事实,他们确实创建了正确的方向,走向无限远处

为了清楚起见,我删除了很多代码使其易于阅读。我只在第二种方法中缺少 //这里检测 部分,因为我不知道如何使其工作。

+0

问题已编辑,以添加我所有失败的尝试 –

回答

1

假设在互联网上有大量的三角形/网格光线投射代码,你尝试过就能解决你的问题,我认为你的问题在第一种方法中,你设置一个“找到”变量,但不要从方法如果找到或选择最小距离的对象。

您提供的代码将仅返回最后一个基元的命中测试结果。

如果您的代码过于简化,请再次发布更多详细信息。

编辑:

你从网格的脸上得到的坐标是对象空间。在进行任何计算之前,您需要将它们转换为世界空间,或者更好地将光线转换为对象空间。

的工作实现见下面的代码: https://github.com/mrdoob/three.js/blob/master/src/objects/Mesh.js

注意,应用世界矩阵的applyMatrix4呼吁给他们带来同样的空间。

+0

行“bool found = findintersection()”基本上只是为了调试目的。我知道(从绘制到屏幕的原始和光线)光线穿过原始。但在调试中,这个布尔值保持为假。因此,我并不需要添加任何更多的代码,因为它会出现在“if(found){}”中,所以它不会被触发。我需要确认布尔值在应用之前是否为真。 –

+0

我编辑了这个问题,添加了所有失败的尝试 –

+0

我试过了,但在openframeworks中找不到任何方法将网格的面的坐标转换为世界空间,或将射线转换为对象空间。你怎么做到这一点? –