#ifndef __VCGFOREMBREE_H #define __VCGFOREMBREE_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace vcg; using namespace std; /* @Author: Paolo Fasano @Description: This class aims to integrate intel embree3 with the vcglib giving some basic methods that can be used to build more complex features. */ namespace vcg{ template class EmbreeAdaptor{ RTCDevice device = rtcNewDevice(NULL); RTCScene scene = rtcNewScene(device); RTCGeometry geometry = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_TRIANGLE); int threads; public: EmbreeAdaptor(MeshType &m){ loadVCGMeshInScene(m); } /* @Author: Paolo Fasano @Parameter: Point3f rayDirection, direction the rays are shoot towards @Description: foreach face the barycenter is found and a single ray is shoot. If the ray intersect with something the face color is set to black else is set to white. */ public: void selectVisibleFaces(MeshType &m, Point3f rayDirection, bool incrementalSelect){ if (incrementalSelect == false){ //deselect all previously selected faces for(int i = 0;i unifDirVec; Point3f dir = normalizedDir; rayhit = setRayValues(b, dir, 4); RTCRayQueryContext context; rtcInitRayQueryContext(&context); RTCIntersectArguments intersectArgs; rtcInitIntersectArguments(&intersectArgs); intersectArgs.context = &context; rtcIntersect1(scene, &rayhit, &intersectArgs); //if (rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID) if (rayhit.ray.tfar == std::numeric_limits::infinity()) m.face[i].SetS(); } rtcReleaseScene(scene); rtcReleaseDevice(device); } /* @Author: Paolo Fasano @Parameter: MeshType &m, reference to a mesh @Description: this method apply some preprocessing over it using standard vcglib methods. Than the mesh is loaded as a new embree geometry inside a new embree scene. The new embree variables are global to the class in order to be used with the other methods. */ public: void loadVCGMeshInScene(MeshType &m){ //a little mesh preprocessing before adding it to a RTCScene tri::RequirePerVertexNormal(m); tri::UpdateNormal::PerVertexNormalized(m); tri::UpdateNormal::PerFaceNormalized(m); tri::UpdateBounding::Box(m); tri::UpdateFlags::FaceClearV(m); float* vb = (float*) rtcSetNewGeometryBuffer(geometry, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, 3*sizeof(float), m.VN()); for (int i = 0;i unifDirVec; GenNormal::Fibonacci(nRay,unifDirVec); computeAmbientOcclusion(inputM, unifDirVec); } /* @Author: Paolo Fasano @Parameter: MeshType &m,reference to a mesh. @Parameter: std::vector unifDirVec, vector of direction specified by the user. @Description: for each face from the barycenter this method shoots n rays towards some user generated directions(to infinity). If the ray direction is not pointing inside than the ray is actually shoot. If the ray intersect something than the face quality of the mesh is updated with the normal of the fica multiplied by the direction. One more operation done in the AmbientOcclusion is to calculate the bent normal foreach face and save it in an attribute named "BentNormal" */ public: void computeAmbientOcclusion(MeshType &inputM, std::vector unifDirVec){ tri::UpdateQuality::FaceConstant(inputM,0); typename MeshType::template PerFaceAttributeHandle bentNormal = vcg::tri::Allocator:: template GetPerFaceAttribute(inputM,string("BentNormal")); #pragma omp parallel shared(inputM) { #pragma omp for for(int i = 0;i0){ updateRayDirection(rayhit, dir); rayhit.ray.tfar = std::numeric_limits::infinity(); rayhit.hit.geomID = RTC_INVALID_GEOMETRY_ID; RTCRayQueryContext context; rtcInitRayQueryContext(&context); RTCIntersectArguments intersectArgs; rtcInitIntersectArguments(&intersectArgs); intersectArgs.context = &context; rtcIntersect1(scene, &rayhit, &intersectArgs); if (rayhit.hit.geomID == RTC_INVALID_GEOMETRY_ID){ bN+=dir; accRays++; inputM.face[i].Q()+=scalarP; } } } bentNormal[i] = bN/accRays; } } tri::UpdateColor::PerFaceQualityGray(inputM); rtcReleaseScene(scene); rtcReleaseDevice(device); } /* @Author: Paolo Fasano @Parameter: MeshType &m, reference to a mesh. @Parameter: int nRay, number of rays that must be generated and shoot. @Parameter: float Tau, the grater this value is the grater the influence of the rays that intersect with some face @Description: for each face from the barycenter this method shoots n rays towards a generated direction(to infinity). If the ray direction is not pointing inside than the ray is actually shoot. If the ray intersect something than the face quality of the mesh is updated with the normal of the fica multiplied by the direction; else, if there are no hits, the face get updated of 1-distanceHit^tau */ public: void computeObscurance(MeshType &inputM, int nRay, float tau){ std::vector unifDirVec; GenNormal::Fibonacci(nRay,unifDirVec); computeObscurance(inputM, unifDirVec, tau); } /* @Author: Paolo Fasano @Parameter: MeshType &m, reference to a mesh. @Parameter: std::vector unifDirVec, vector of direction specified by the user. @Parameter: float Tau, the grater this value is the grater the influence of the rays that intersect with some face @Description: for each face from the barycenter this method shoots n rays towards a generated direction(to infinity). If the ray direction is not pointing inside than the ray is actually shoot. If the ray intersect something than the face quality of the mesh is updated with the normal of the fica multiplied by the direction; else, if there are no hits, the face get updated of 1-distanceHit^tau */ public: void computeObscurance(MeshType &inputM, std::vector unifDirVec, float tau){ tri::UpdateQuality::FaceConstant(inputM,0); #pragma omp parallel { #pragma omp for for(int i = 0;i0){ updateRayDirection(rayhit, dir); rayhit.ray.tfar = std::numeric_limits::infinity(); rayhit.hit.geomID = RTC_INVALID_GEOMETRY_ID; RTCRayQueryContext context; rtcInitRayQueryContext(&context); RTCIntersectArguments intersectArgs; rtcInitIntersectArguments(&intersectArgs); intersectArgs.context = &context; rtcIntersect1(scene, &rayhit, &intersectArgs); if (rayhit.hit.geomID == RTC_INVALID_GEOMETRY_ID) inputM.face[i].Q()+=scalarP; else inputM.face[i].Q()+=(1-powf(rayhit.ray.tfar,tau)); } } } } tri::UpdateColor::PerFaceQualityGray(inputM); rtcReleaseScene(scene); rtcReleaseDevice(device); } /* @Author: Paolo Fasano @Parameter: MeshType &m, reference to a mesh. @Parameter: int nRay, number of rays that must be generated and shoot. @Parameter: float degree, this variable represents the angle of the cone for which we consider a point as a valid direction @Description: - Use a cone centered around the inward-normal direction of a point on a surface mesh. - Send rays inside the cone to the other side of the mesh. - Calculate the SDF at a point as the weighted average of all ray lengths within one standard deviation from the median of all lengths. - Use inverse angle between the ray and the center of the cone as the weight. - The SDF is invariant to rigid body transformations of the whole mesh and oblivious to local deformations. - Small cone angles are too sensitive to local features, while large opening angles close to 180◦ expose the SDF measure to noise and errors. */ public: void computeSDF(MeshType &inputM, int nRay, float degree){ if (degree >= 180) degree = 120; tri::UpdateQuality::FaceConstant(inputM,0); //first step is to generate the cone of rays, i will generate a sphear and for each face take only the //the ones that fall in the desired degree (in respect to the opposite of face norm) std::vector unifDirVec; GenNormal::Fibonacci(nRay,unifDirVec); for (int i = 0; i < inputM.FN(); i++) { RTCRayHit rayhit = initRayValues(); Point3f b = vcg::Barycenter(inputM.face[i]); updateRayOrigin(rayhit, b); rayhit.ray.tnear = 1e-4; float weight = 0; float weight_sum = 0; float weighted_sum = 0; for(int r = 0; r::infinity(); rayhit.hit.geomID = RTC_INVALID_GEOMETRY_ID; RTCRayQueryContext context; rtcInitRayQueryContext(&context); RTCIntersectArguments intersectArgs; rtcInitIntersectArguments(&intersectArgs); intersectArgs.context = &context; rtcIntersect1(scene, &rayhit, &intersectArgs); if (rayhit.ray.tfar != std::numeric_limits::infinity()) { weight = 1/angle_dir_b; //nominator and denominator for weigthed avarage weighted_sum += weight * rayhit.ray.tfar; weight_sum += weight; } } } //we assign the result of the weighted average to the quality of the face inputM.face[i].Q() = (weighted_sum / weight_sum); } tri::UpdateColor::PerFaceQualityRamp(inputM); rtcReleaseScene(scene); rtcReleaseDevice(device); } /* @Author: Paolo Fasano @Parameter: MeshType &m, reference to a mesh. @Parameter: int nRay, number of rays that must be generated and shoot. @Description: Given a mesh for each face, for each ray, it detects all the intersections with all the facets (i.e., without stopping at the first intersection), and accumulates the number. The rays are shoot two times each, one inside and one outside the mesh. After shooting all the rays, the facet is flipped if frontHit>backHit. For more informations read: Kenshi Takayama, Alec Jacobson, Ladislav Kavan, and Olga Sorkine-Hornung, A Simple Method for Correcting Facet Orientations in Polygon Meshes Based on Ray Casting, Journal of Computer Graphics Techniques (JCGT), vol. 3, no. 4, 53-63, 2014 Available online http://jcgt.org/published/0003/04/02/ */ public: void computeNormalAnalysis(MeshType &inputM, int nRay,bool parity_computation){ //bool fast_computation = false; std::vector unifDirVec; GenNormal::Fibonacci(nRay,unifDirVec); std::vector unifDirVec_EXPAND; for (Point3f p : unifDirVec) { unifDirVec_EXPAND.push_back(p); unifDirVec_EXPAND.push_back(p*-1); } tri::UpdateSelection::FaceClear(inputM); if (parity_computation){ paritySampling(inputM,unifDirVec_EXPAND); } else{ visibilitySamplig(inputM, unifDirVec_EXPAND); } // Iterate over the selected faces and flip them for (auto& face : inputM.face) { if (face.IsS()) { std::swap(face.V(1), face.V(2)); // Swap the second and third vertices } } tri::UpdateSelection::FaceClear(inputM); } /* @Author: Paolo Fasano @Parameter: Point3f origin, the origin point to search for intersections to @Description: given an origin point this methos counts how many intersection there are starting from there (only the number not the positions coordinates) */ public: int findInterceptNumber(Point3f origin, Point3f direction){ int totInterception = 0; //float totDistance = 0; //float previous_distance = 0; RTCRayHit rayhit; rayhit = setRayValues(origin, direction, 0.5); RTCRayQueryContext context; rtcInitRayQueryContext(&context); RTCIntersectArguments intersectArgs; rtcInitIntersectArguments(&intersectArgs); intersectArgs.context = &context; while(true){ rtcIntersect1(scene, &rayhit, &intersectArgs); if (rayhit.ray.tfar != std::numeric_limits::infinity()){ totInterception++; //totDistance += rayhit.ray.tfar - previous_distance; //previous_distance = rayhit.ray.tfar; // we keep the same origin point and direction but update how far from the face the ray starts shooting rayhit.ray.tnear += rayhit.ray.tfar ;//+ rayhit.ray.tfar * 0.05f; rayhit.ray.tfar = std::numeric_limits::infinity(); } else return totInterception; } return totInterception; } public: void visibilitySamplig(MeshType &inputM, std::vector unifDirVec_EXPAND){ #pragma omp parallel { #pragma omp for for(int i = 0;i::infinity()) { if (scalarP > 0){ frontHit++; frontDistance += rayhit.ray.tfar; } else{ backHit++; backDistance += rayhit.ray.tfar; } } } //std::cout<< "face "<< i <<"front hit: " << frontHit << " backhit "<< backHit << " frontDistance " << frontDistance << " backDistance " << backDistance << endl; if(frontHit < backHit || (frontHit == backHit && frontDistance < backDistance)) inputM.face[i].SetS(); } } //tri::Clean::FlipMesh(inputM,true); rtcReleaseScene(scene); rtcReleaseDevice(device); return; } public: void paritySampling(MeshType &inputM, std::vector unifDirVec_EXPAND){ #pragma omp parallel { #pragma omp for for(int i = 0;i::infinity()) { int n_hits = findInterceptNumber(b,dir); if (scalarP > 0){ frontHit += (n_hits % 2); } else{ backHit += (n_hits % 2); } } } if(frontHit > backHit ) inputM.face[i].SetS(); } } //tri::Clean::FlipMesh(inputM,true); rtcReleaseScene(scene); rtcReleaseDevice(device); return; } public: inline RTCRayHit initRayValues(){ RTCRayHit rayhit; rayhit.ray.mask = -1; rayhit.ray.flags = 0; rayhit.hit.geomID = RTC_INVALID_GEOMETRY_ID; rayhit.hit.instID[0] = RTC_INVALID_GEOMETRY_ID; return rayhit; } //given a ray and a direction expressed as point3f, this method modifies the ray direction of the ray tp the given direction public: inline void updateRayDirection(RTCRayHit& rayhit, Point3f direction){ //setting the ray direction rayhit.ray.dir_x = direction[0]; rayhit.ray.dir_y = direction[1]; rayhit.ray.dir_z = direction[2]; } //given a ray and a point of origin expressed as point3f, this method modifies the origin point of the ray tp the origin point given public: inline void updateRayOrigin(RTCRayHit& rayhit, Point3f origin){ //setting the ray point of origin rayhit.ray.org_x = origin[0]; rayhit.ray.org_y = origin[1]; rayhit.ray.org_z = origin[2]; } public: inline RTCRayHit setRayValues(Point3f origin, Point3f direction, float tnear, float tfar = std::numeric_limits::infinity()){ RTCRayHit rayhit = initRayValues(); updateRayOrigin(rayhit, origin); updateRayDirection(rayhit, direction); rayhit.ray.tnear = tnear; rayhit.ray.tfar = tfar; return rayhit; } }; } #endif