You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

613 lines
24 KiB

#ifndef __VCGFOREMBREE_H
#define __VCGFOREMBREE_H
#include <iostream>
#include <vcg/complex/complex.h>
#include <vcg/simplex/face/pos.h>
#include <vcg/complex/algorithms/update/topology.h>
#include <vcg/complex/algorithms/closest.h>
#include <vcg/complex/algorithms/update/color.h>
#include <vcg/complex/algorithms/update/quality.h>
#include <wrap/callback.h>
#include <embree4/rtcore.h>
#include <vcg/math/gen_normal.h>
#include <limits>
#include <math.h>
#include <time.h>
#include <omp.h>
#include <tuple>
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 MeshType>
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<m.FN(); i++){
if(m.face[i].IsS()){
m.face[i].ClearS();
}
}
}
Point3f normalizedDir = rayDirection;
RTCRayHit rayhit = initRayValues();
for(int i = 0;i<m.FN(); i++)
{
Point3f b = vcg::Barycenter(m.face[i]);
std::vector<Point3f> 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<float>::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<MeshType>::PerVertexNormalized(m);
tri::UpdateNormal<MeshType>::PerFaceNormalized(m);
tri::UpdateBounding<MeshType>::Box(m);
tri::UpdateFlags<MeshType>::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<m.VN(); i++){
vb[i*3]=m.vert[i].P()[0];
vb[i*3+1]=m.vert[i].P()[1];
vb[i*3+2]=m.vert[i].P()[2];
}
unsigned* ib = (unsigned*) rtcSetNewGeometryBuffer(geometry, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, 3*sizeof(unsigned), m.FN());
for(int i = 0;i<m.FN(); i++){
ib[i*3] = tri::Index(m,m.face[i].V(0));
ib[i*3+1] = tri::Index(m,m.face[i].V(1));
ib[i*3+2] = tri::Index(m,m.face[i].V(2));
}
rtcCommitGeometry(geometry);
rtcAttachGeometry(scene, geometry);
rtcReleaseGeometry(geometry);
rtcCommitScene(scene);
}
/*
@Author: Paolo Fasano
@Parameter: MeshType &m, reference to a mesh.
@Parameter: int nRay, number of rays that must be generated and shoot.
@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.
*/
public:
void computeAmbientOcclusion(MeshType &inputM, int nRay){
std::vector<Point3f> unifDirVec;
GenNormal<float>::Fibonacci(nRay,unifDirVec);
computeAmbientOcclusion(inputM, unifDirVec);
}
/*
@Author: Paolo Fasano
@Parameter: MeshType &m,reference to a mesh.
@Parameter: std::vector<Point3f> 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<Point3f> unifDirVec){
tri::UpdateQuality<MeshType>::FaceConstant(inputM,0);
typename MeshType::template PerFaceAttributeHandle<Point3f> bentNormal = vcg::tri::Allocator<MeshType>:: template GetPerFaceAttribute<Point3f>(inputM,string("BentNormal"));
#pragma omp parallel shared(inputM)
{
#pragma omp for
for(int i = 0;i<inputM.FN(); i++)
{
RTCRayHit rayhit = initRayValues();
Point3f b = vcg::Barycenter(inputM.face[i]);
updateRayOrigin(rayhit, b);
rayhit.ray.tnear = 0.00001f;
Point3f bN;
int accRays=0;
for(int r = 0; r<unifDirVec.size(); r++){
Point3f dir = unifDirVec.at(r);
float scalarP = inputM.face[i].N()*dir;
if(scalarP>0){
updateRayDirection(rayhit, dir);
rayhit.ray.tfar = std::numeric_limits<float>::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<MeshType>::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<Point3f> unifDirVec;
GenNormal<float>::Fibonacci(nRay,unifDirVec);
computeObscurance(inputM, unifDirVec, tau);
}
/*
@Author: Paolo Fasano
@Parameter: MeshType &m, reference to a mesh.
@Parameter: std::vector<Point3f> 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<Point3f> unifDirVec, float tau){
tri::UpdateQuality<MeshType>::FaceConstant(inputM,0);
#pragma omp parallel
{
#pragma omp for
for(int i = 0;i<inputM.FN(); i++)
{
RTCRayHit rayhit = initRayValues();
Point3f b = vcg::Barycenter(inputM.face[i]);
updateRayOrigin(rayhit, b);
rayhit.ray.tnear = 0.00001f;
for(int r = 0; r<unifDirVec.size(); r++){
Point3f dir = unifDirVec.at(r);
float scalarP = inputM.face[i].N()*dir;
if(scalarP>0){
updateRayDirection(rayhit, dir);
rayhit.ray.tfar = std::numeric_limits<float>::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<MeshType>::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<MeshType>::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<Point3f> unifDirVec;
GenNormal<float>::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<unifDirVec.size(); r++){
Point3f dir = unifDirVec.at(r);
float scalarP = inputM.face[i].N()*dir;
float angle_dir_b = Angle(b, dir);
if (scalarP < 0 && vcg::math::ToRad(angle_dir_b) <= degree){
updateRayDirection(rayhit, dir);
rayhit.ray.tfar = std::numeric_limits<float>::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<float>::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<MeshType>::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<Point3f> unifDirVec;
GenNormal<float>::Fibonacci(nRay,unifDirVec);
std::vector<Point3f> unifDirVec_EXPAND;
for (Point3f p : unifDirVec)
{
unifDirVec_EXPAND.push_back(p);
unifDirVec_EXPAND.push_back(p*-1);
}
tri::UpdateSelection<MeshType>::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<MeshType>::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<float>::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<float>::infinity();
}
else
return totInterception;
}
return totInterception;
}
public:
void visibilitySamplig(MeshType &inputM, std::vector<Point3f> unifDirVec_EXPAND){
#pragma omp parallel
{
#pragma omp for
for(int i = 0;i<inputM.FN(); i++)
{
RTCRayHit rayhit;
Point3f b = vcg::Barycenter(inputM.face[i]);
Point3f dir(0.0f, 0.0f, 0.0f);
rayhit = setRayValues(b, dir, 1e-4f);
int frontHit = 0;
int backHit = 0;
float frontDistance = 0;
float backDistance = 0;
for(int r = 0; r<unifDirVec_EXPAND.size(); r++){
dir = unifDirVec_EXPAND.at(r);
float scalarP = inputM.face[i].N()*dir;
rayhit = setRayValues(b, dir, 1e-4f);
RTCRayQueryContext context;
rtcInitRayQueryContext(&context);
RTCIntersectArguments intersectArgs;
rtcInitIntersectArguments(&intersectArgs);
intersectArgs.context = &context;
rtcIntersect1(scene, &rayhit, &intersectArgs);
if (rayhit.ray.tfar == std::numeric_limits<float>::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<MeshType>::FlipMesh(inputM,true);
rtcReleaseScene(scene);
rtcReleaseDevice(device);
return;
}
public:
void paritySampling(MeshType &inputM, std::vector<Point3f> unifDirVec_EXPAND){
#pragma omp parallel
{
#pragma omp for
for(int i = 0;i<inputM.FN(); i++)
{
RTCRayHit rayhit;
Point3f b = vcg::Barycenter(inputM.face[i]);
Point3f dir(0.0f, 0.0f, 0.0f);
rayhit = setRayValues(b, dir, 1e-4f);
int frontHit = 0;
int backHit = 0;
for(int r = 0; r<unifDirVec_EXPAND.size(); r++){
dir = unifDirVec_EXPAND.at(r);
float scalarP = inputM.face[i].N()*dir;
rayhit = setRayValues(b, dir, 1e-4f);
RTCRayQueryContext context;
rtcInitRayQueryContext(&context);
RTCIntersectArguments intersectArgs;
rtcInitIntersectArguments(&intersectArgs);
intersectArgs.context = &context;
rtcIntersect1(scene, &rayhit, &intersectArgs);
if (rayhit.ray.tfar != std::numeric_limits<float>::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<MeshType>::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<float>::infinity()){
RTCRayHit rayhit = initRayValues();
updateRayOrigin(rayhit, origin);
updateRayDirection(rayhit, direction);
rayhit.ray.tnear = tnear;
rayhit.ray.tfar = tfar;
return rayhit;
}
};
}
#endif