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.

450 lines
12 KiB

#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<const PointMatrixType*> Vtemp;
std::vector<const FaceMatrixType*> Ftemp;
std::vector<int> 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<const PointMatrixType*>& V,
const std::vector<const FaceMatrixType*>& F,
const std::vector<int>& 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<Eigen::Matrix<float,-1,4,Eigen::RowMajor>> 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<float>();
// fill triangle buffer
Eigen::Map<Eigen::Matrix<unsigned int,-1,3,Eigen::RowMajor>> 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<unsigned int>();
//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 <typename DerivedV, typename DerivedF>
IGL_INLINE void
igl::embree::EmbreeRenderer
::set_mesh(const Eigen::MatrixBase<DerivedV> & MV,
const Eigen::MatrixBase<DerivedF> & MF,
bool is_static)
{
V = MV.template cast<float>();
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<float>();
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 <typename DerivedC>
IGL_INLINE void
igl::embree::EmbreeRenderer
::set_colors(const Eigen::MatrixBase<DerivedC> & C)
{
if(C.rows()==V.rows()) // per vertex color
{
face_based = false;
this->C = C.template cast<float>();
this->uniform_color=false;
} else if (C.rows()==F.rows()) {
face_based = true;
this->C = C.template cast<float>();
this->uniform_color=false;
} else if (C.rows()==1) {
face_based = true;
this->uC = C.template cast<float>();
this->uniform_color=true;
}else {
// don't know what to do
this->uniform_color=true;
assert(false); //?
}
}
template <typename DerivedD>
IGL_INLINE void
igl::embree::EmbreeRenderer
::set_data(const Eigen::MatrixBase<DerivedD> & 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 <typename DerivedD, typename T>
IGL_INLINE void igl::embree::EmbreeRenderer::set_data(
const Eigen::MatrixBase<DerivedD> & D,
T caxis_min,
T caxis_max,
igl::ColorMapType cmap)
{
Eigen::Matrix<T, -1, -1> C;
igl::colormap(cmap,D,caxis_min,caxis_max,C);
set_colors(C);
}
template <typename Derivedr>
IGL_INLINE void
igl::embree::EmbreeRenderer::set_rot(const Eigen::MatrixBase<Derivedr> &r)
{
this->rot_matrix = r.template cast<float>();
}
template <typename T>
IGL_INLINE void
igl::embree::EmbreeRenderer::set_zoom(T zoom)
{
this->camera_zoom=zoom;
}
template <typename Derivedtr>
IGL_INLINE void
igl::embree::EmbreeRenderer::set_translation(const Eigen::MatrixBase<Derivedtr> &tr)
{
this->camera_translation=tr.template cast<float>();
}
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::Matrix<double, 3, 3, 0, 3, 3> >(Eigen::MatrixBase<Eigen::Matrix<double, 3, 3, 0, 3, 3> > const&);
template void igl::embree::EmbreeRenderer::set_data<Eigen::Matrix<double, -1, 1, 0, -1, 1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, igl::ColorMapType);
template void igl::embree::EmbreeRenderer::set_mesh<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, bool);
template void igl::embree::EmbreeRenderer::set_zoom<double>(double);
#endif //IGL_STATIC_LIBRARY