#include "EmbreeRenderer.h" // Implementation //IGL viewing parts #include "../unproject.h" #include "../look_at.h" #include "../frustum.h" #include "../ortho.h" // color map #include "../jet.h" #include "../PI.h" IGL_INLINE void igl::embree::EmbreeRenderer::init_view() { camera_base_zoom = 1.0f; camera_zoom = 1.0f; camera_view_angle = 45.0; camera_dnear = 1.0; camera_dfar = 100.0; camera_base_translation << 0, 0, 0; camera_translation << 0, 0, 0; camera_eye << 0, 0, 5; camera_center << 0, 0, 0; camera_up << 0, 1, 0; rot_matrix = Eigen::Matrix3f::Identity(); view = Eigen::Matrix4f::Identity(); proj = Eigen::Matrix4f::Identity(); norm = Eigen::Matrix4f::Identity(); orthographic = false; } IGL_INLINE igl::embree::EmbreeRenderer::EmbreeRenderer() : scene(NULL), geomID(0), initialized(false), device(igl::embree::EmbreeDevice::get_device()) { init_view(); uC << 1,0,0; double_sided = false; } IGL_INLINE igl::embree::EmbreeRenderer::EmbreeRenderer( const EmbreeRenderer &) :// To make -Weffc++ happy scene(NULL), geomID(0), initialized(false) { assert(false && "Embree: Copying EmbreeRenderer is not allowed"); } IGL_INLINE igl::embree::EmbreeRenderer & igl::embree::EmbreeRenderer::operator=( const EmbreeRenderer &) { assert(false && "Embree: Assigning an EmbreeRenderer is not allowed"); return *this; } IGL_INLINE void igl::embree::EmbreeRenderer::init( const PointMatrixType& V, const FaceMatrixType& F, bool isStatic) { std::vector Vtemp; std::vector Ftemp; std::vector masks; Vtemp.push_back(&V); Ftemp.push_back(&F); masks.push_back(0xFFFFFFFF); init(Vtemp,Ftemp,masks,isStatic); } IGL_INLINE void igl::embree::EmbreeRenderer::init( const std::vector& V, const std::vector& F, const std::vector& masks, bool isStatic) { if(initialized) deinit(); using namespace std; if(V.size() == 0 || F.size() == 0) { std::cerr << "Embree: No geometry specified!"; return; } RTCBuildQuality buildQuality = isStatic ? RTC_BUILD_QUALITY_HIGH : RTC_BUILD_QUALITY_MEDIUM; // create a scene scene = rtcNewScene(device); rtcSetSceneFlags(scene, RTC_SCENE_FLAG_ROBUST); rtcSetSceneBuildQuality(scene, buildQuality); for(int g=0;g<(int)V.size();g++) { // create triangle mesh geometry in that scene RTCGeometry geom_0 = rtcNewGeometry (device, RTC_GEOMETRY_TYPE_TRIANGLE); rtcSetGeometryBuildQuality(geom_0, buildQuality); rtcSetGeometryTimeStepCount(geom_0,1); geomID = rtcAttachGeometry(scene,geom_0); rtcReleaseGeometry(geom_0); // fill vertex buffer, have to be 16 byte wide( sizeof(float)*4 ) Eigen::Map> vertices( (float*)rtcSetNewGeometryBuffer(geom_0,RTC_BUFFER_TYPE_VERTEX,0,RTC_FORMAT_FLOAT3,4*sizeof(float),V[g]->rows()), V[g]->rows(),4 ); vertices.block(0,0,V[g]->rows(),3) = V[g]->cast(); // fill triangle buffer Eigen::Map> triangles( (unsigned int*) rtcSetNewGeometryBuffer(geom_0,RTC_BUFFER_TYPE_INDEX,0,RTC_FORMAT_UINT3,3*sizeof(unsigned int), F[g]->rows()), F[g]->rows(),3 ); triangles = F[g]->cast(); //TODO: store vertices and triangles in array for whatever reason? rtcSetGeometryMask(geom_0, masks[g]); rtcCommitGeometry(geom_0); } rtcCommitScene(scene); if(rtcGetDeviceError (device) != RTC_ERROR_NONE) std::cerr << "Embree: An error occurred while initializing the provided geometry!" << endl; #ifdef IGL_VERBOSE else std::cerr << "Embree: geometry added." << endl; #endif initialized = true; } IGL_INLINE igl::embree::EmbreeRenderer::~EmbreeRenderer() { if(initialized) deinit(); igl::embree::EmbreeDevice::release_device(); } IGL_INLINE void igl::embree::EmbreeRenderer::deinit() { if(scene) { rtcReleaseScene(scene); if(rtcGetDeviceError (device) != RTC_ERROR_NONE) { std::cerr << "Embree: An error occurred while resetting!" << std::endl; } #ifdef IGL_VERBOSE else { std::cerr << "Embree: geometry removed." << std::endl; } #endif } } IGL_INLINE bool igl::embree::EmbreeRenderer::intersect_ray( const Eigen::RowVector3f& origin, const Eigen::RowVector3f& direction, Hit& hit, float tnear, float tfar, int mask) const { RTCRayHit ray; ray.ray.flags = 0; create_ray(ray, origin,direction,tnear,tfar,mask); // shot ray { RTCIntersectContext context; rtcInitIntersectContext(&context); rtcIntersect1(scene, &context, &ray); ray.hit.Ng_x = -ray.hit.Ng_x; // EMBREE_FIXME: only correct for triangles,quads, and subdivision surfaces ray.hit.Ng_y = -ray.hit.Ng_y; ray.hit.Ng_z = -ray.hit.Ng_z; } #ifdef IGL_VERBOSE if(rtcGetDeviceError (device) != RTC_ERROR_NONE) std::cerr << "Embree: An error occurred while resetting!" << std::endl; #endif if((unsigned)ray.hit.geomID != RTC_INVALID_GEOMETRY_ID) { hit.id = ray.hit.primID; hit.gid = ray.hit.geomID; hit.u = ray.hit.u; hit.v = ray.hit.v; hit.t = ray.ray.tfar; hit.N = Vec3f(ray.hit.Ng_x, ray.hit.Ng_y, ray.hit.Ng_z); return true; } return false; } IGL_INLINE void igl::embree::EmbreeRenderer ::create_ray(RTCRayHit& ray, const Eigen::RowVector3f& origin, const Eigen::RowVector3f& direction, float tnear, float tfar, int mask) const { ray.ray.org_x = origin[0]; ray.ray.org_y = origin[1]; ray.ray.org_z = origin[2]; ray.ray.dir_x = direction[0]; ray.ray.dir_y = direction[1]; ray.ray.dir_z = direction[2]; ray.ray.tnear = tnear; ray.ray.tfar = tfar; ray.ray.id = RTC_INVALID_GEOMETRY_ID; ray.ray.mask = mask; ray.ray.time = 0.0f; ray.hit.geomID = RTC_INVALID_GEOMETRY_ID; ray.hit.instID[0] = RTC_INVALID_GEOMETRY_ID; ray.hit.primID = RTC_INVALID_GEOMETRY_ID; } template IGL_INLINE void igl::embree::EmbreeRenderer ::set_mesh(const Eigen::MatrixBase & MV, const Eigen::MatrixBase & MF, bool is_static) { V = MV.template cast(); F = MF; this->init(V,F,is_static); auto min_point = V.colwise().minCoeff(); auto max_point = V.colwise().maxCoeff(); auto centroid = (0.5*(min_point + max_point)).eval(); camera_base_translation.setConstant(0); camera_base_translation.head(centroid.size()) = -centroid.cast(); camera_base_zoom = 2.0 / (max_point-min_point).array().abs().maxCoeff(); } IGL_INLINE void igl::embree::EmbreeRenderer ::render_buffer(PixelMatrixType& R, PixelMatrixType&G, PixelMatrixType &B,PixelMatrixType &A) { assert(R.rows()==G.rows());assert(R.rows()==B.rows());assert(R.rows()==A.rows()); assert(R.cols()==G.cols());assert(R.cols()==B.cols());assert(R.cols()==A.cols()); Eigen::Vector4f viewport(0,0,R.rows(),R.cols()); float width = R.rows(); float height = R.cols(); // update view matrix igl::look_at( camera_eye, camera_center, camera_up, view); view = view * (rot_matrix * Eigen::Scaling(camera_zoom * camera_base_zoom) * Eigen::Translation3f(camera_translation + camera_base_translation)).matrix(); if (orthographic) { float length = (camera_eye - camera_center).norm(); float h = tan(camera_view_angle/360.0 * igl::PI) * (length); igl::ortho(-h*width/height, h*width/height, -h, h, camera_dnear, camera_dfar, proj); } else { float fH = tan(camera_view_angle / 360.0 * igl::PI) * camera_dnear; float fW = fH * (double)width/(double)height; igl::frustum(-fW, fW, -fH, fH, camera_dnear, camera_dfar, proj); } // go over all pixels in the "view" for(int x=0;x<(int)width;++x) { for(int y=0;y<(int)height;++y) { Vec3f s,d,dir; igl::embree::EmbreeRenderer::Hit hit; // look into the screen Vec3f win_s(x,y,0); Vec3f win_d(x,y,1); // Source, destination and direction in world igl::unproject(win_s,this->view,this->proj,viewport,s); igl::unproject(win_d,this->view,this->proj,viewport,d); dir = d-s; dir.normalize(); auto clamp=[](float x)->unsigned char {return (unsigned char)(x<0?0:x>1.0?255:x*255);}; if(this->intersect_ray(s,dir,hit)) { if ( double_sided || dir.dot(hit.N) > 0.0f ) { // TODO: interpolate normals ? hit.N.normalize(); // cos between ray and face normal // negative projection will indicate back side float face_proj = fabs(dir.dot(hit.N)); Eigen::RowVector3f c; if(this->uniform_color) { // same color for the whole mesh c=this->uC; } else if(this->face_based) { // flat color per face c=this->C.row(hit.id); } else { //use barycentric coordinates to interpolate colour c=this->C.row(F(hit.id,1))*hit.u+ this->C.row(F(hit.id,2))*hit.v+ this->C.row(F(hit.id,0))*(1.0-hit.u-hit.v); } R(x,y) = clamp(face_proj*c(0)); G(x,y) = clamp(face_proj*c(1)); B(x,y) = clamp(face_proj*c(2)); } else { // backface? R(x,y)=0; G(x,y)=0; B(x,y)=0; } // give the same alpha to all points with something behind A(x,y)=255; } else { R(x,y)=0; G(x,y)=0; B(x,y)=0; A(x,y)=0; } } } } template IGL_INLINE void igl::embree::EmbreeRenderer ::set_colors(const Eigen::MatrixBase & C) { if(C.rows()==V.rows()) // per vertex color { face_based = false; this->C = C.template cast(); this->uniform_color=false; } else if (C.rows()==F.rows()) { face_based = true; this->C = C.template cast(); this->uniform_color=false; } else if (C.rows()==1) { face_based = true; this->uC = C.template cast(); this->uniform_color=true; }else { // don't know what to do this->uniform_color=true; assert(false); //? } } template IGL_INLINE void igl::embree::EmbreeRenderer ::set_data(const Eigen::MatrixBase & D, igl::ColorMapType cmap) { const auto caxis_min = D.minCoeff(); const auto caxis_max = D.maxCoeff(); return set_data(D,caxis_min,caxis_max,cmap); } template IGL_INLINE void igl::embree::EmbreeRenderer::set_data( const Eigen::MatrixBase & D, T caxis_min, T caxis_max, igl::ColorMapType cmap) { Eigen::Matrix C; igl::colormap(cmap,D,caxis_min,caxis_max,C); set_colors(C); } template IGL_INLINE void igl::embree::EmbreeRenderer::set_rot(const Eigen::MatrixBase &r) { this->rot_matrix = r.template cast(); } template IGL_INLINE void igl::embree::EmbreeRenderer::set_zoom(T zoom) { this->camera_zoom=zoom; } template IGL_INLINE void igl::embree::EmbreeRenderer::set_translation(const Eigen::MatrixBase &tr) { this->camera_translation=tr.template cast(); } IGL_INLINE void igl::embree::EmbreeRenderer::set_face_based(bool _f) { this->face_based=_f; } IGL_INLINE void igl::embree::EmbreeRenderer::set_orthographic(bool o) { this->orthographic=o; } IGL_INLINE void igl::embree::EmbreeRenderer::set_double_sided(bool d) { this->double_sided=d; } #ifdef IGL_STATIC_LIBRARY template void igl::embree::EmbreeRenderer::set_rot >(Eigen::MatrixBase > const&); template void igl::embree::EmbreeRenderer::set_data >(Eigen::MatrixBase > const&, igl::ColorMapType); template void igl::embree::EmbreeRenderer::set_mesh, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, bool); template void igl::embree::EmbreeRenderer::set_zoom(double); #endif //IGL_STATIC_LIBRARY