// ======================================================================== // // Copyright 2009-2014 Intel Corporation // // // // Licensed under the Apache License, Version 2.0 (the "License"); // // you may not use this file except in compliance with the License. // // You may obtain a copy of the License at // // // // http://www.apache.org/licenses/LICENSE-2.0 // // // // Unless required by applicable law or agreed to in writing, software // // distributed under the License is distributed on an "AS IS" BASIS, // // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // // See the License for the specific language governing permissions and // // limitations under the License. // // ======================================================================== // #include "tutorial/tutorial.h" #include "tutorial/obj_loader.h" #include "tutorial/hair_loader.h" #include "tutorial/cy_hair_loader.h" #include "sys/taskscheduler.h" #include "image/image.h" extern "C" embree::Vec3fa g_dirlight_direction = embree::normalize(embree::Vec3fa(1,-1,1)); extern "C" embree::Vec3fa g_dirlight_intensity = embree::Vec3fa(4.0f); extern "C" embree::Vec3fa g_ambient_intensity = embree::Vec3fa(1.0f); namespace embree { /* name of the tutorial */ const char* tutorialName = "tutorial07"; /* configuration */ static std::string g_rtcore = ""; /* output settings */ static size_t g_width = 512; static size_t g_height = 512; static bool g_fullscreen = false; static size_t g_numThreads = 0; static int tessellate_subdivisions = -1; static int tessellate_strips = -1; extern float g_reduce_hair_segment_error; /* scene */ OBJScene g_obj_scene; OBJScene g_obj_scene2; static FileName objFilename = ""; static FileName objFilename2 = ""; static FileName hairFilename = ""; static FileName hairFilename2 = ""; static FileName cy_hairFilename = ""; static FileName outFilename = ""; static int g_skipBenchmarkFrames = 0; static int g_numBenchmarkFrames = 0; static bool g_interactive = true; static bool hairy_triangles = false; static float hairy_triangles_length = 1.0f; static float hairy_triangles_thickness = 0.1f; static int hairy_triangles_strands_per_triangle = 1; Vec3fa offset = 0.0f; Vec3fa offset_mb = 0.0f; void addHairSegment(OBJScene::Mesh& mesh, int materialID, const Vec3fa& p0, const Vec3fa& p1) { const size_t N = tessellate_strips; if (length(p1-p0) <= 1E-6f) return; const LinearSpace3fa xfm = frame(p1-p0); const int base = mesh.v.size(); for (size_t i=0; i 0) { const Vec3fa p10 = 0.5f*(p00+p01); const Vec3fa p11 = 0.5f*(p01+p02); const Vec3fa p12 = 0.5f*(p02+p03); const Vec3fa p20 = 0.5f*(p10+p11); const Vec3fa p21 = 0.5f*(p11+p12); const Vec3fa p30 = 0.5f*(p20+p21); tessellateHair(mesh,materialID,p00,p10,p20,p30,depth-1); tessellateHair(mesh,materialID,p30,p21,p12,p03,depth-1); } else { addHairSegment(mesh,materialID,p00,p03); } } void tessellateHair(OBJScene::Mesh& mesh, int materialID, const OBJScene::HairSet& hairSet) { for (size_t i=0; itriangles.size(); t++) { const size_t i0 = mesh->triangles[t].v0; const size_t i1 = mesh->triangles[t].v1; const size_t i2 = mesh->triangles[t].v2; const Vec3fa& v0 = mesh->v[ i0 ]; const Vec3fa& v1 = mesh->v[ i1 ]; const Vec3fa& v2 = mesh->v[ i2 ]; Vec3fa n0,n1,n2; if (mesh->vn.size() != 0) { n0 = mesh->vn[ i0 ]; n1 = mesh->vn[ i1 ]; n2 = mesh->vn[ i2 ]; } else { const Vec3fa edge0 = v1-v0; const Vec3fa edge1 = v2-v0; Vec3fa normal = cross(edge0,edge1); n0 = n1 = n2 = normal; } //if (length(normal) < 1E-30) continue; //const float thickness = thickness; for (size_t i=0; iv.size(); hairset->v.push_back(l0); hairset->v.push_back(l1); hairset->v.push_back(l2); hairset->v.push_back(l3); hairset->hairs.push_back( OBJScene::Hair(v_index,hairset->hairs.size()) ); } } scene.hairsets.push_back(hairset); } void generateHairOnTriangles(OBJScene& scene) { for (size_t m=0; mv. push_back(p+r*dp); mesh->vn.push_back(dp); } if (phi == 0) continue; for (int theta=1; theta<=numTheta; theta++) { int p00 = (phi-1)*numTheta+theta-1; int p01 = (phi-1)*numTheta+theta%numTheta; int p10 = phi*numTheta+theta-1; int p11 = phi*numTheta+theta%numTheta; if (phi > 1) mesh->triangles.push_back(OBJScene::Triangle(p10,p00,p01,materialID)); if (phi < numPhi) mesh->triangles.push_back(OBJScene::Triangle(p11,p10,p01,materialID)); } } scene.meshes.push_back(mesh); //generateHairOnTriangleMesh(scene,mesh,0.5f*r,0.001f*r,80); #if 0 const float thickness = 0.01f*r; OBJScene::HairSet* hairset = new OBJScene::HairSet; srand48(123456789); for (size_t t=0; t<16; t++) { Vec3fa dp = uniformSampleSphere(drand48(),drand48()); Vec3fa l0 = p + r* (dp + 0.00f*dp); l0.w = thickness; Vec3fa l1 = p + r* (dp + 0.25f*dp); l1.w = thickness; Vec3fa l2 = p + r*noise3D(dp + 0.50f*dp); l2.w = thickness; Vec3fa l3 = p + r*noise3D(dp + 0.75f*dp); l3.w = thickness; const unsigned int v_index = hairset->v.size(); hairset->v.push_back(l0); hairset->v.push_back(l1); hairset->v.push_back(l2); hairset->v.push_back(l3); hairset->hairs.push_back( OBJScene::Hair(v_index,hairset->hairs.size()) ); } scene.hairsets.push_back(hairset); #else const float thickness = 0.001f*r; OBJScene::HairSet* hairset = new OBJScene::HairSet; for (size_t t=0; t<100000; t++) { Vec3fa dp = uniformSampleSphere(drand48(),drand48()); Vec3fa l0 = p + r* (dp + 0.00f*dp); l0.w = thickness; Vec3fa l1 = p + r* (dp + 0.25f*dp); l1.w = thickness; Vec3fa l2 = p + r*noise3D(dp + 0.50f*dp); l2.w = thickness; Vec3fa l3 = p + r*noise3D(dp + 0.75f*dp); l3.w = thickness; const unsigned int v_index = hairset->v.size(); hairset->v.push_back(l0); hairset->v.push_back(l1); hairset->v.push_back(l2); hairset->v.push_back(l3); hairset->hairs.push_back( OBJScene::Hair(v_index,hairset->hairs.size()) ); } scene.hairsets.push_back(hairset); #endif } void addGroundPlane (OBJScene& scene, const Vec3fa& p00, const Vec3fa& p01, const Vec3fa& p10, const Vec3fa& p11) { OBJScene::Mesh* mesh = new OBJScene::Mesh; OBJScene::Material material; int materialID = scene.materials.size(); scene.materials.push_back(material); mesh->v.push_back(p00); mesh->v.push_back(p01); mesh->v.push_back(p10); mesh->v.push_back(p11); mesh->triangles.push_back(OBJScene::Triangle(0,1,2,materialID)); mesh->triangles.push_back(OBJScene::Triangle(2,1,3,materialID)); scene.meshes.push_back(mesh); } static void parseCommandLine(Ref cin, const FileName& path) { while (true) { std::string tag = cin->getString(); if (tag == "") return; /* parse command line parameters from a file */ if (tag == "-c") { FileName file = path + cin->getFileName(); parseCommandLine(new ParseStream(new LineCommentFilter(file, "#")), file.path()); } /* load OBJ model */ else if (tag == "-i") { objFilename = path + cin->getFileName(); } /* load motion blur OBJ model */ else if (tag == "-i_mb") { objFilename = path + cin->getFileName(); objFilename2 = path + cin->getFileName(); } /* load hair model */ else if (tag == "--hair") { hairFilename = path + cin->getFileName(); } /* motion blur hair model */ else if (tag == "--hair_mb") { hairFilename = path + cin->getFileName(); hairFilename2 = path + cin->getFileName(); } /* load hair model */ else if (tag == "--cy_hair") { cy_hairFilename = path + cin->getFileName(); } /* scene offset */ else if (tag == "--offset") { offset = cin->getVec3fa(); } /* scene offset */ else if (tag == "--offset_mb") { offset_mb = cin->getVec3fa(); } /* directional light */ else if (tag == "--dirlight") { g_dirlight_direction = normalize(cin->getVec3fa()); g_dirlight_intensity = cin->getVec3fa(); } /* ambient light */ else if (tag == "--ambient") { g_ambient_intensity = cin->getVec3fa(); } /* tessellation flags */ else if (tag == "--tessellate-hair") { tessellate_subdivisions = cin->getInt(); tessellate_strips = cin->getInt(); } /* create hairy spheres */ else if (tag == "--hairy-sphere") { const Vec3fa p = cin->getVec3fa(); const float r = cin->getFloat(); addHairySphere(g_obj_scene,p,r); } /* create hair on geometry */ else if (tag == "--hairy-triangles") { hairy_triangles_strands_per_triangle = cin->getInt(); hairy_triangles_length = cin->getFloat(); hairy_triangles_thickness = cin->getFloat(); hairy_triangles = true; } /* reduce number of hair segments */ else if (tag == "--reduce-hair-segment-error") { g_reduce_hair_segment_error = cin->getFloat(); } /* output filename */ else if (tag == "-o") { outFilename = cin->getFileName(); g_interactive = false; } /* number of frames to render in benchmark mode */ else if (tag == "-benchmark") { g_skipBenchmarkFrames = cin->getInt(); g_numBenchmarkFrames = cin->getInt(); g_interactive = false; } /* parse camera parameters */ else if (tag == "-vp") { g_camera.from = cin->getVec3fa(); } else if (tag == "-vi") g_camera.to = cin->getVec3fa(); else if (tag == "-vd") g_camera.to = g_camera.from + cin->getVec3fa(); else if (tag == "-vu") g_camera.up = cin->getVec3fa(); else if (tag == "-fov") g_camera.fov = cin->getFloat(); /* frame buffer size */ else if (tag == "-size") { g_width = cin->getInt(); g_height = cin->getInt(); } /* full screen mode */ else if (tag == "-fullscreen") g_fullscreen = true; /* rtcore configuration */ else if (tag == "-rtcore") g_rtcore = cin->getString(); /* number of threads to use */ else if (tag == "-threads") g_numThreads = cin->getInt(); /* skip unknown command line parameter */ else { std::cerr << "unknown command line parameter: " << tag << " "; while (cin->peek() != "" && cin->peek()[0] != '-') std::cerr << cin->getString() << " "; std::cerr << std::endl; } } } void renderBenchmark(const FileName& fileName) { resize(g_width,g_height); AffineSpace3fa pixel2world = g_camera.pixel2world(g_width,g_height); double dt = 0.0f; size_t numTotalFrames = g_skipBenchmarkFrames + g_numBenchmarkFrames; for (size_t i=0; i= g_skipBenchmarkFrames) dt += t1-t0; } std::cout << "frame [" << g_skipBenchmarkFrames << " - " << numTotalFrames << "] " << std::flush; std::cout << double(g_numBenchmarkFrames)/dt << "fps " << std::endl; std::cout << "BENCHMARK_RENDER " << double(g_numBenchmarkFrames)/dt << std::endl; } void renderToFile(const FileName& fileName) { resize(g_width,g_height); AffineSpace3fa pixel2world = g_camera.pixel2world(g_width,g_height); render(0.0f, pixel2world.l.vx, pixel2world.l.vy, pixel2world.l.vz, pixel2world.p); void* ptr = map(); Ref image = new Image4c(g_width, g_height, (Col4c*)ptr); storeImage(image, fileName); unmap(); cleanup(); } /* main function in embree namespace */ int main(int argc, char** argv) { g_camera.from = Vec3fa(3.21034f,0.320831f,-0.162478f); g_camera.to = Vec3fa(2.57003f,0.524887f, 0.163145f); g_camera.from = Vec3fa(-3,3,3); g_camera.to = Vec3fa(0,1,0); g_camera.up = Vec3fa(0,1,0); /* create stream for parsing */ Ref stream = new ParseStream(new CommandLineStream(argc, argv)); /* parse command line */ parseCommandLine(stream, FileName()); //if (g_numThreads) // FIXME //g_rtcore += ",threads=" + std::stringOf(g_numThreads); /* initialize task scheduler */ #if !defined(RTCORE_EXPORT_ALL_SYMBOLS) TaskScheduler::create(g_numThreads); #endif /* initialize ray tracing core */ init(g_rtcore.c_str()); /* load scene */ if (objFilename.str() != "" && objFilename.str() != "none") { loadOBJ(objFilename,AffineSpace3f::translate(-offset),g_obj_scene); if (objFilename2.str() != "") loadOBJ(objFilename2,AffineSpace3f::translate(-offset_mb),g_obj_scene2); } /* load hair */ if (hairFilename.str() != "" && hairFilename.str() != "none") { loadHair(hairFilename,g_obj_scene,offset); if (hairFilename2.str() != "") loadHair(hairFilename2,g_obj_scene2,offset_mb); } if (!g_obj_scene2.empty()) { g_obj_scene.set_motion_blur(g_obj_scene2); } /* load cy_hair */ if (cy_hairFilename.str() != "") { loadCYHair(cy_hairFilename,g_obj_scene,offset); } /* generate hair on triangles */ if (hairy_triangles) generateHairOnTriangles(g_obj_scene); /* tessellate hair */ if (tessellate_strips > 0) tessellateHair(g_obj_scene); /* if scene is empty, create default scene */ if (g_obj_scene.meshes.size() + g_obj_scene.hairsets.size() == 0) { addHairySphere(g_obj_scene,Vec3fa(0,1.5f,0),1.5f); addGroundPlane(g_obj_scene,Vec3fa(-10,0,-10),Vec3fa(-10,0,+10),Vec3fa(+10,0,-10),Vec3fa(+10,0,+10)); } /* send model */ set_scene(&g_obj_scene); /* benchmark mode */ if (g_numBenchmarkFrames) renderBenchmark(outFilename); /* render to disk */ if (outFilename.str() != "") renderToFile(outFilename); /* interactive mode */ if (g_interactive) { initWindowState(argc,argv,tutorialName, g_width, g_height, g_fullscreen); enterWindowRunLoop(); } return 0; } } int main(int argc, char** argv) { try { return embree::main(argc, argv); } catch (const std::exception& e) { std::cout << "Error: " << e.what() << std::endl; return 1; } catch (...) { std::cout << "Error: unknown exception caught." << std::endl; return 1; } }