Files
embree/tutorials/common/tutorial/tutorial.cpp
T
2017-08-29 23:33:31 +03:00

1055 lines
40 KiB
C++

// ======================================================================== //
// Copyright 2009-2017 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.h"
#include "scene.h"
#include "statistics.h"
/* include GLUT for display */
#if defined(__MACOSX__)
# include <OpenGL/gl.h>
# include <GLUT/glut.h>
#elif defined(__WIN32__)
# include <windows.h>
# include <GL/gl.h>
# include <GL/glut.h>
#else
# include <GL/gl.h>
# include <GL/glut.h>
#endif
/* include tutorial_device.h after GLUT, as otherwise we may get
* warnings of redefined GLUT_KEY_F1, etc. */
#include "tutorial_device.h"
#include "../scenegraph/scenegraph.h"
#include "../scenegraph/geometry_creation.h"
#include "../scenegraph/obj_loader.h"
#include "../scenegraph/xml_loader.h"
#include "../image/image.h"
namespace embree
{
extern "C"
{
float g_debug = 0.0f;
Mode g_mode = MODE_NORMAL;
ISPCScene* g_ispc_scene = nullptr;
/* intensity scaling for traversal cost visualization */
float scale = 1.0f / 1000000.0f;
bool g_changed = false;
int64_t get_tsc() { return read_tsc(); }
unsigned int g_numThreads = 0;
RTCIntersectFlags g_iflags_coherent = RTC_INTERSECT_COHERENT;
RTCIntersectFlags g_iflags_incoherent = RTC_INTERSECT_INCOHERENT;
RayStats* g_stats = nullptr;
}
extern "C" int g_instancing_mode;
TutorialApplication* TutorialApplication::instance = nullptr;
TutorialApplication::TutorialApplication (const std::string& tutorialName, int features)
: Application(features),
tutorialName(tutorialName),
shader(SHADER_DEFAULT),
width(512),
height(512),
pixels(nullptr),
outputImageFilename(""),
skipBenchmarkFrames(0),
numBenchmarkFrames(0),
numBenchmarkRepetitions(1),
interactive(true),
fullscreen(false),
window_width(512),
window_height(512),
windowID(0),
time0(getSeconds()),
debug_int0(0),
debug_int1(0),
mouseMode(0),
clickX(0), clickY(0),
speed(1.0f),
moveDelta(zero),
command_line_camera(false),
print_frame_rate(false),
avg_render_time(64,1.0),
avg_frame_time(64,1.0),
avg_mrayps(64,1.0),
print_camera(false),
debug0(0),
debug1(0),
debug2(0),
debug3(0),
iflags_coherent(RTC_INTERSECT_COHERENT),
iflags_incoherent(RTC_INTERSECT_INCOHERENT)
{
/* only a single instance of this class is supported */
assert(instance == nullptr);
instance = this;
/* for best performance set FTZ and DAZ flags in MXCSR control and status register */
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
registerOption("c", [this] (Ref<ParseStream> cin, const FileName& path) {
FileName file = path + cin->getFileName();
parseCommandLine(new ParseStream(new LineCommentFilter(file, "#")), file.path());
}, "-c <filename>: parses command line option from <filename>");
registerOption("o", [this] (Ref<ParseStream> cin, const FileName& path) {
outputImageFilename = cin->getFileName();
interactive = false;
}, "-o <filename>: output image filename");
/* camera settings */
registerOption("vp", [this] (Ref<ParseStream> cin, const FileName& path) {
camera.from = cin->getVec3fa();
command_line_camera = true;
}, "--vp <float> <float> <float>: camera position");
registerOption("vi", [this] (Ref<ParseStream> cin, const FileName& path) {
camera.to = cin->getVec3fa();
command_line_camera = true;
}, "--vi <float> <float> <float>: camera lookat position");
registerOption("vd", [this] (Ref<ParseStream> cin, const FileName& path) {
camera.to = camera.from + cin->getVec3fa();
command_line_camera = true;
}, "--vd <float> <float> <float>: camera direction vector");
registerOption("vu", [this] (Ref<ParseStream> cin, const FileName& path) {
camera.up = cin->getVec3fa();
command_line_camera = true;
}, "--vu <float> <float> <float>: camera up vector");
registerOption("fov", [this] (Ref<ParseStream> cin, const FileName& path) {
camera.fov = cin->getFloat();
command_line_camera = true;
}, "--fov <float>: vertical field of view");
/* framebuffer settings */
registerOption("size", [this] (Ref<ParseStream> cin, const FileName& path) {
width = cin->getInt();
height = cin->getInt();
}, "--size <width> <height>: sets image size");
registerOption("fullscreen", [this] (Ref<ParseStream> cin, const FileName& path) {
fullscreen = true;
}, "--fullscreen: starts in fullscreen mode");
registerOption("benchmark", [this] (Ref<ParseStream> cin, const FileName& path) {
skipBenchmarkFrames = cin->getInt();
numBenchmarkFrames = cin->getInt();
if (cin->peek() != "" && cin->peek()[0] != '-')
numBenchmarkRepetitions = cin->getInt();
interactive = false;
rtcore += ",benchmark=1,start_threads=1";
}, "--benchmark <N> <M> <R>: enabled benchmark mode, builds scene, skips N frames, renders M frames, and repeats this R times");
registerOption("nodisplay", [this] (Ref<ParseStream> cin, const FileName& path) {
skipBenchmarkFrames = 0;
numBenchmarkFrames = 2048;
interactive = false;
}, "--nodisplay: enabled benchmark mode, continously renders frames");
registerOption("print-frame-rate", [this] (Ref<ParseStream> cin, const FileName& path) {
print_frame_rate = true;
}, "--print-frame-rate: prints framerate for each frame on console");
registerOption("print-camera", [this] (Ref<ParseStream> cin, const FileName& path) {
print_camera = true;
}, "--print-camera: prints camera for each frame on console");
registerOption("debug0", [this] (Ref<ParseStream> cin, const FileName& path) {
debug0 = cin->getInt();
}, "--debug0: sets internal debugging value");
registerOption("debug1", [this] (Ref<ParseStream> cin, const FileName& path) {
debug1 = cin->getInt();
}, "--debug1: sets internal debugging value");
registerOption("debug2", [this] (Ref<ParseStream> cin, const FileName& path) {
debug2 = cin->getInt();
}, "--debug2: sets internal debugging value");
registerOption("debug3", [this] (Ref<ParseStream> cin, const FileName& path) {
debug3 = cin->getInt();
}, "--debug3: sets internal debugging value");
/* output filename */
registerOption("shader", [this] (Ref<ParseStream> cin, const FileName& path) {
std::string mode = cin->getString();
if (mode == "default" ) shader = SHADER_DEFAULT;
else if (mode == "eyelight") shader = SHADER_EYELIGHT;
else if (mode == "occlusion") shader = SHADER_OCCLUSION;
else if (mode == "uv" ) shader = SHADER_UV;
else if (mode == "texcoords") shader = SHADER_TEXCOORDS;
else if (mode == "texcoords-grid") shader = SHADER_TEXCOORDS_GRID;
else if (mode == "Ng" ) shader = SHADER_NG;
else if (mode == "cycles" ) { shader = SHADER_CYCLES; scale = cin->getFloat(); }
else if (mode == "geomID" ) shader = SHADER_GEOMID;
else if (mode == "primID" ) shader = SHADER_GEOMID_PRIMID;
else if (mode == "ao" ) shader = SHADER_AMBIENT_OCCLUSION;
else throw std::runtime_error("invalid shader:" +mode);
},
"--shader <string>: sets shader to use at startup\n"
" default: default tutorial shader\n"
" eyelight: eyelight shading\n"
" occlusion: occlusion shading\n"
" uv: uv debug shader\n"
" texcoords: texture coordinate debug shader\n"
" texcoords-grid: grid texture debug shader\n"
" Ng: visualization of shading normal\n"
" cycles <float>: CPU cycle visualization\n"
" geomID: visualization of geometry ID\n"
" primID: visualization of geometry and primitive ID\n"
" ao: ambient occlusion shader");
if (features & FEATURE_STREAM)
{
/* register parsing of stream mode */
registerOption("mode", [this] (Ref<ParseStream> cin, const FileName& path) {
std::string mode = cin->getString();
if (mode == "normal") g_mode = MODE_NORMAL;
else if (mode == "stream") g_mode = MODE_STREAM;
else throw std::runtime_error("invalid mode:" +mode);
},
"--mode: sets rendering mode\n"
" normal : normal mode\n"
" stream : stream mode\n");
}
registerOption("coherent", [this] (Ref<ParseStream> cin, const FileName& path) {
g_iflags_coherent = iflags_coherent = RTC_INTERSECT_COHERENT;
g_iflags_incoherent = iflags_incoherent = RTC_INTERSECT_COHERENT;
}, "--coherent: force using RTC_INTERSECT_COHERENT hint when tracing rays");
registerOption("incoherent", [this] (Ref<ParseStream> cin, const FileName& path) {
g_iflags_coherent = iflags_coherent = RTC_INTERSECT_INCOHERENT;
g_iflags_incoherent = iflags_incoherent = RTC_INTERSECT_INCOHERENT;
}, "--incoherent: force using RTC_INTERSECT_INCOHERENT hint when tracing rays");
}
TutorialApplication::~TutorialApplication()
{
g_ispc_scene = nullptr;
device_cleanup();
alignedFree(pixels);
pixels = nullptr;
width = 0;
height = 0;
alignedFree(g_stats);
g_stats = nullptr;
}
SceneLoadingTutorialApplication::SceneLoadingTutorialApplication (const std::string& tutorialName, int features)
: TutorialApplication(tutorialName, features),
scene(new SceneGraph::GroupNode),
convert_tris_to_quads(false),
convert_tris_to_quads_prop(inf),
convert_bezier_to_lines(false),
convert_hair_to_curves(false),
convert_bezier_to_bspline(false),
convert_bspline_to_bezier(false),
remove_mblur(false),
remove_non_mblur(false),
sceneFilename(""),
instancing_mode(SceneGraph::INSTANCING_NONE),
print_scene_cameras(false)
{
registerOption("i", [this] (Ref<ParseStream> cin, const FileName& path) {
sceneFilename = path + cin->getFileName();
}, "-i <filename>: parses scene from <filename>");
registerOption("animlist", [this] (Ref<ParseStream> cin, const FileName& path) {
FileName listFilename = path + cin->getFileName();
std::ifstream listFile;
listFile.open(listFilename.c_str());
if (!listFile.is_open()) {
THROW_RUNTIME_ERROR("cannot open " + listFilename.str());
}
else
{
while (!listFile.eof())
{
std::string line;
listFile >> line;
if (line != "")
keyFramesFilenames.push_back(listFilename.path() + line);
}
}
}, "-animlist <filename>: parses a sequence of .obj/.xml files listed in <filename> and adds them to the scene");
registerOption("convert-triangles-to-quads", [this] (Ref<ParseStream> cin, const FileName& path) {
convert_tris_to_quads = true;
convert_tris_to_quads_prop = inf;
}, "--convert-triangles-to-quads: converts all triangles to quads when loading");
registerOption("convert-triangles-to-triangles-and-quads", [this] (Ref<ParseStream> cin, const FileName& path) {
convert_tris_to_quads = true;
convert_tris_to_quads_prop = 0.5f;
}, "--convert-triangles-to-triangles-and-quads: converts to mixed triangle/quad scene");
registerOption("convert-bezier-to-lines", [this] (Ref<ParseStream> cin, const FileName& path) {
convert_bezier_to_lines = true;
}, "--convert-bezier-to-lines: converts all bezier curves to line segments when loading");
registerOption("convert-hair-to-curves", [this] (Ref<ParseStream> cin, const FileName& path) {
convert_hair_to_curves = true;
}, "--convert-hair-to-curves: converts all hair geometry to curves when loading");
registerOption("convert-bezier-to-bspline", [this] (Ref<ParseStream> cin, const FileName& path) {
convert_bezier_to_bspline = true;
}, "--convert-bezier-to-bspline: converts all bezier curves to bsplines curves");
registerOption("convert-bspline-to-bezier", [this] (Ref<ParseStream> cin, const FileName& path) {
convert_bspline_to_bezier = true;
}, "--convert-bspline-to-bezier: converts all bsplines curves to bezier curves");
registerOption("remove-mblur", [this] (Ref<ParseStream> cin, const FileName& path) {
remove_mblur = true;
}, "--remove-mblur: removes all motion blur geometry");
registerOption("remove-non-mblur", [this] (Ref<ParseStream> cin, const FileName& path) {
remove_non_mblur = true;
}, "--remove-non-mblur: removes all non-motion blur geometry");
registerOption("instancing", [this] (Ref<ParseStream> cin, const FileName& path) {
std::string mode = cin->getString();
if (mode == "none" ) instancing_mode = SceneGraph::INSTANCING_NONE;
else if (mode == "geometry") instancing_mode = SceneGraph::INSTANCING_GEOMETRY;
else if (mode == "geometry_group") instancing_mode = SceneGraph::INSTANCING_GEOMETRY_GROUP;
else if (mode == "scene_geometry") instancing_mode = SceneGraph::INSTANCING_SCENE_GEOMETRY;
else if (mode == "scene_group" ) instancing_mode = SceneGraph::INSTANCING_SCENE_GROUP;
else throw std::runtime_error("unknown instancing mode: "+mode);
g_instancing_mode = instancing_mode;
}, "--instancing: set instancing mode\n"
" none: no instancing\n"
" geometry: instance individual geometries\n"
" geometry_group: instance geometry groups\n"
" scene_geometry: instance individual geometries as scenes\n"
" scene_group: instance geometry groups as scenes");
registerOption("ambientlight", [this] (Ref<ParseStream> cin, const FileName& path) {
const Vec3fa L = cin->getVec3fa();
scene->add(new SceneGraph::LightNode(new SceneGraph::AmbientLight(L)));
}, "--ambientlight r g b: adds an ambient light with intensity rgb");
registerOptionAlias("ambientlight","ambient");
registerOption("pointlight", [this] (Ref<ParseStream> cin, const FileName& path) {
const Vec3fa P = cin->getVec3fa();
const Vec3fa I = cin->getVec3fa();
scene->add(new SceneGraph::LightNode(new SceneGraph::PointLight(P,I)));
}, "--pointlight x y z r g b: adds a point light at position xyz with intensity rgb");
registerOption("directionallight", [this] (Ref<ParseStream> cin, const FileName& path) {
const Vec3fa D = cin->getVec3fa();
const Vec3fa E = cin->getVec3fa();
scene->add(new SceneGraph::LightNode(new SceneGraph::DirectionalLight(D,E)));
}, "--directionallight x y z r g b: adds a directional light with direction xyz and intensity rgb");
registerOptionAlias("directionallight","dirlight");
registerOption("distantlight", [this] (Ref<ParseStream> cin, const FileName& path) {
const Vec3fa D = cin->getVec3fa();
const Vec3fa L = cin->getVec3fa();
const float halfAngle = cin->getFloat();
scene->add(new SceneGraph::LightNode(new SceneGraph::DistantLight(D,L,halfAngle)));
}, "--distantlight x y z r g b a: adds a distant light with direction xyz, intensity rgb, and opening angle a");
registerOption("triangle-plane", [this] (Ref<ParseStream> cin, const FileName& path) {
const Vec3fa p0 = cin->getVec3fa();
const Vec3fa dx = cin->getVec3fa();
const Vec3fa dy = cin->getVec3fa();
const size_t width = cin->getInt();
const size_t height = cin->getInt();
scene->add(SceneGraph::createTrianglePlane(p0,dx,dy,width,height,new OBJMaterial));
}, "--triangle-plane p.x p.y p.z dx.x dx.y dx.z dy.x dy.y dy.z width height: adds a plane build of triangles originated at p0 and spanned by the vectors dx and dy with a tesselation width/height.");
registerOption("quad-plane", [this] (Ref<ParseStream> cin, const FileName& path) {
const Vec3fa p0 = cin->getVec3fa();
const Vec3fa dx = cin->getVec3fa();
const Vec3fa dy = cin->getVec3fa();
const size_t width = cin->getInt();
const size_t height = cin->getInt();
scene->add(SceneGraph::createQuadPlane(p0,dx,dy,width,height,new OBJMaterial));
}, "--quad-plane p.x p.y p.z dx.x dx.y dx.z dy.x dy.y dy.z width height: adds a plane build of quadrilaterals originated at p0 and spanned by the vectors dx and dy with a tesselation width/height.");
registerOption("subdiv-plane", [this] (Ref<ParseStream> cin, const FileName& path) {
const Vec3fa p0 = cin->getVec3fa();
const Vec3fa dx = cin->getVec3fa();
const Vec3fa dy = cin->getVec3fa();
const size_t width = cin->getInt();
const size_t height = cin->getInt();
const float tessellationRate = cin->getFloat();
scene->add(SceneGraph::createSubdivPlane(p0,dx,dy,width,height,tessellationRate,new OBJMaterial));
}, "--subdiv-plane p.x p.y p.z dx.x dx.y dx.z dy.x dy.y dy.z width height tessellationRate: adds a plane build as a Catmull Clark subdivision surface originated at p0 and spanned by the vectors dx and dy. The plane consists of widt x height many patches, and each patch has the specified tesselation rate.");
registerOption("hair-plane", [this] (Ref<ParseStream> cin, const FileName& path) {
const Vec3fa p0 = cin->getVec3fa();
const Vec3fa dx = cin->getVec3fa();
const Vec3fa dy = cin->getVec3fa();
const float len = cin->getFloat();
const float r = cin->getFloat();
const size_t N = cin->getInt();
scene->add(SceneGraph::createHairyPlane(0,p0,dx,dy,len,r,N,SceneGraph::HairSetNode::HAIR,new OBJMaterial));
}, "--hair-plane p.x p.y p.z dx.x dx.y dx.z dy.x dy.y dy.z length radius num: adds a hair plane originated at p0 and spanned by the vectors dx and dy. num hairs are generated with speficied length and radius.");
registerOption("curve-plane", [this] (Ref<ParseStream> cin, const FileName& path) {
const Vec3fa p0 = cin->getVec3fa();
const Vec3fa dx = cin->getVec3fa();
const Vec3fa dy = cin->getVec3fa();
const float len = cin->getFloat();
const float r = cin->getFloat();
const size_t N = cin->getInt();
scene->add(SceneGraph::createHairyPlane(0,p0,dx,dy,len,r,N,SceneGraph::HairSetNode::CURVE,new OBJMaterial));
}, "--curve-plane p.x p.y p.z dx.x dx.y dx.z dy.x dy.y dy.z length radius: adds a plane build of bezier curves originated at p0 and spanned by the vectors dx and dy. num curves are generated with speficied length and radius.");
registerOption("triangle-sphere", [this] (Ref<ParseStream> cin, const FileName& path) {
const Vec3fa p = cin->getVec3fa();
const float r = cin->getFloat();
const size_t numPhi = cin->getInt();
scene->add(SceneGraph::createTriangleSphere(p,r,numPhi,new OBJMaterial));
}, "--triangle-sphere p.x p.y p.z r numPhi: adds a sphere at position p with radius r and tesselation numPhi build of triangles.");
registerOption("quad-sphere", [this] (Ref<ParseStream> cin, const FileName& path) {
const Vec3fa p = cin->getVec3fa();
const float r = cin->getFloat();
const size_t numPhi = cin->getInt();
scene->add(SceneGraph::createQuadSphere(p,r,numPhi,new OBJMaterial));
}, "--quad-sphere p.x p.y p.z r numPhi: adds a sphere at position p with radius r and tesselation numPhi build of quadrilaterals.");
registerOption("quad-sphere-mblur", [this] (Ref<ParseStream> cin, const FileName& path) {
const Vec3fa p = cin->getVec3fa();
const Vec3fa dp = cin->getVec3fa();
const float r = cin->getFloat();
const size_t numPhi = cin->getInt();
Ref<SceneGraph::Node> mesh = SceneGraph::createQuadSphere(p,r,numPhi,new OBJMaterial);
mesh->set_motion_vector(dp);
scene->add(mesh);
}, "--quad-sphere-mb p.x p.y p.z d.x d.y d.z r numPhi : adds a motion blurred sphere build of quadrilaterals at position p, with motion vector d, radius r, and tesselation numPhi.");
registerOption("subdiv-sphere", [this] (Ref<ParseStream> cin, const FileName& path) {
const Vec3fa p = cin->getVec3fa();
const float r = cin->getFloat();
const size_t numPhi = cin->getInt();
const float tessellationRate = cin->getFloat();
scene->add(SceneGraph::createSubdivSphere(p,r,numPhi,tessellationRate,new OBJMaterial));
}, "--subdiv-sphere p.x p.y p.z r numPhi: adds a sphere at position p with radius r build of Catmull Clark subdivision surfaces. The sphere consists of numPhi x numPhi many patches and each path has the specified tessellation rate.");
registerOption("cache", [this] (Ref<ParseStream> cin, const FileName& path) {
subdiv_mode = ",subdiv_accel=bvh4.subdivpatch1cached";
rtcore += subdiv_mode;
}, "--cache: enabled cached subdiv mode");
registerOption("pregenerate", [this] (Ref<ParseStream> cin, const FileName& path) {
subdiv_mode = ",subdiv_accel=bvh4.grid.eager";
rtcore += subdiv_mode;
}, "--pregenerate: enabled pregenerate subdiv mode");
registerOption("print-cameras", [this] (Ref<ParseStream> cin, const FileName& path) {
print_scene_cameras = true;
}, "--print-cameras: prints all camera names of the scene");
registerOption("camera", [this] (Ref<ParseStream> cin, const FileName& path) {
camera_name = cin->getString();
}, "--camera: use camera with specified name");
}
void TutorialApplication::initRayStats()
{
if (!g_stats)
g_stats = (RayStats*)alignedMalloc(TaskScheduler::threadCount() * sizeof(RayStats), sizeof(RayStats));
for (size_t i = 0; i < TaskScheduler::threadCount(); i++)
g_stats[i].numRays = 0;
}
int64_t TutorialApplication::getNumRays()
{
int64_t numRays = 0;
for (size_t i = 0; i < TaskScheduler::threadCount(); i++)
numRays += g_stats[i].numRays;
return numRays;
}
void TutorialApplication::renderBenchmark()
{
IOStreamStateRestorer cout_state(std::cout);
std::cout.setf(std::ios::fixed, std::ios::floatfield);
std::cout.precision(4);
resize(width,height);
ISPCCamera ispccamera = camera.getISPCCamera(width,height);
//Statistics stat;
FilteredStatistics fpsStat(0.5f,0.0f);
FilteredStatistics mraypsStat(0.5f,0.0f);
for (size_t j=0; j<numBenchmarkRepetitions; j++)
{
size_t numTotalFrames = skipBenchmarkFrames + numBenchmarkFrames;
for (size_t i=0; i<skipBenchmarkFrames; i++)
{
initRayStats();
double t0 = getSeconds();
device_render(pixels,width,height,0.0f,ispccamera);
double t1 = getSeconds();
std::cout << "frame [" << std::setw(3) << i << " / " << std::setw(3) << numTotalFrames << "]: " << std::setw(8) << 1.0/(t1-t0) << " fps (skipped)" << std::endl << std::flush;
}
for (size_t i=skipBenchmarkFrames; i<numTotalFrames; i++)
{
initRayStats();
double t0 = getSeconds();
device_render(pixels,width,height,0.0f,ispccamera);
double t1 = getSeconds();
float fps = float(1.0/(t1-t0));
fpsStat.add(fps);
float mrayps = float(double(getNumRays())/(1000000.0*(t1-t0)));
mraypsStat.add(mrayps);
if (numTotalFrames >= 1024 && (i % 64 == 0))
{
std::cout << "frame [" << std::setw(3) << i << " / " << std::setw(3) << numTotalFrames << "]: "
<< std::setw(8) << fps << " fps, "
<< "min = " << std::setw(8) << fpsStat.getMin() << " fps, "
<< "avg = " << std::setw(8) << fpsStat.getAvg() << " fps, "
<< "max = " << std::setw(8) << fpsStat.getMax() << " fps, "
<< "sigma = " << std::setw(6) << fpsStat.getSigma() << " (" << 100.0f*fpsStat.getSigma()/fpsStat.getAvg() << "%)" << std::endl << std::flush;
}
}
/* rebuild scene between repetitions */
if (numBenchmarkRepetitions)
{
device_cleanup();
device_init(rtcore.c_str());
resize(width,height);
}
std::cout << "frame [" << std::setw(3) << skipBenchmarkFrames << " - " << std::setw(3) << numTotalFrames << "]: "
<< " "
<< "min = " << std::setw(8) << fpsStat.getMin() << " fps, "
<< "avg = " << std::setw(8) << fpsStat.getAvg() << " fps, "
<< "max = " << std::setw(8) << fpsStat.getMax() << " fps, "
<< "sigma = " << std::setw(6) << fpsStat.getAvgSigma() << " (" << 100.0f*fpsStat.getAvgSigma()/fpsStat.getAvg() << "%)" << std::endl;
}
std::cout << "BENCHMARK_RENDER_MIN " << fpsStat.getMin() << std::endl;
std::cout << "BENCHMARK_RENDER_AVG " << fpsStat.getAvg() << std::endl;
std::cout << "BENCHMARK_RENDER_MAX " << fpsStat.getMax() << std::endl;
std::cout << "BENCHMARK_RENDER_SIGMA " << fpsStat.getSigma() << std::endl;
std::cout << "BENCHMARK_RENDER_AVG_SIGMA " << fpsStat.getAvgSigma() << std::endl;
#if defined(RAY_STATS)
std::cout << "BENCHMARK_RENDER_MRAYPS_MIN " << mraypsStat.getMin() << std::endl;
std::cout << "BENCHMARK_RENDER_MRAYPS_AVG " << mraypsStat.getAvg() << std::endl;
std::cout << "BENCHMARK_RENDER_MRAYPS_MAX " << mraypsStat.getMax() << std::endl;
std::cout << "BENCHMARK_RENDER_MRAYPS_SIGMA " << mraypsStat.getSigma() << std::endl;
std::cout << "BENCHMARK_RENDER_MRAYPS_AVG_SIGMA " << mraypsStat.getAvgSigma() << std::endl;
#endif
std::cout << std::flush;
}
void TutorialApplication::renderToFile(const FileName& fileName)
{
resize(width,height);
ISPCCamera ispccamera = camera.getISPCCamera(width,height);
initRayStats();
device_render(pixels,width,height,0.0f,ispccamera);
Ref<Image> image = new Image4uc(width, height, (Col4uc*)pixels);
storeImage(image, fileName);
}
void TutorialApplication::set_parameter(size_t parm, ssize_t val) {
rtcDeviceSetParameter1i(nullptr,(RTCParameter)parm,val);
}
void TutorialApplication::resize(unsigned width, unsigned height)
{
if (width == this->width && height == this->height && pixels)
return;
if (pixels) alignedFree(pixels);
this->width = width;
this->height = height;
pixels = (unsigned*) alignedMalloc(width*height*sizeof(unsigned),64);
}
void TutorialApplication::set_scene (TutorialScene* in)
{
ispc_scene.reset(new ISPCScene(in));
g_ispc_scene = ispc_scene.get();
}
void TutorialApplication::keyboardFunc(unsigned char key, int x, int y)
{
/* call tutorial keyboard handler */
device_key_pressed(key);
switch (key)
{
case 'w' : moveDelta.z = +1.0f; break;
case 's' : moveDelta.z = -1.0f; break;
case 'a' : moveDelta.x = -1.0f; break;
case 'd' : moveDelta.x = +1.0f; break;
case 'f' :
if (fullscreen) {
fullscreen = false;
glutReshapeWindow(window_width,window_height);
} else {
fullscreen = true;
window_width = width;
window_height = height;
glutFullScreen();
}
break;
case 'c' : std::cout << camera.str() << std::endl; break;
case '+' : g_debug=clamp(g_debug+0.01f); PRINT(g_debug); break;
case '-' : g_debug=clamp(g_debug-0.01f); PRINT(g_debug); break;
case ' ' : {
Ref<Image> image = new Image4uc(width, height, (Col4uc*)pixels, true, "", true);
storeImage(image, "screenshot.tga");
break;
}
case '\033': case 'q': case 'Q':
glutDestroyWindow(windowID);
#if defined(__MACOSX__) // FIXME: why only on MacOSX
exit(1);
#endif
break;
}
}
void TutorialApplication::keyboardUpFunc(unsigned char key, int x, int y)
{
switch (key)
{
case 'w' : moveDelta.z = 0.0f; break;
case 's' : moveDelta.z = 0.0f; break;
case 'a' : moveDelta.x = 0.0f; break;
case 'd' : moveDelta.x = 0.0f; break;
}
}
void TutorialApplication::specialFunc(int key, int x, int y)
{
device_key_pressed(key);
if (glutGetModifiers() == GLUT_ACTIVE_CTRL)
{
switch (key) {
case GLUT_KEY_UP : debug_int0++; set_parameter(1000000,debug_int0); PRINT(debug_int0); break;
case GLUT_KEY_DOWN : debug_int0--; set_parameter(1000000,debug_int0); PRINT(debug_int0); break;
case GLUT_KEY_LEFT : debug_int1--; set_parameter(1000001,debug_int1); PRINT(debug_int1); break;
case GLUT_KEY_RIGHT : debug_int1++; set_parameter(1000001,debug_int1); PRINT(debug_int1); break;
}
}
else
{
switch (key) {
case GLUT_KEY_LEFT : camera.rotate(-0.02f,0.0f); break;
case GLUT_KEY_RIGHT : camera.rotate(+0.02f,0.0f); break;
case GLUT_KEY_UP : camera.move(0.0f,0.0f,+speed); break;
case GLUT_KEY_DOWN : camera.move(0.0f,0.0f,-speed); break;
case GLUT_KEY_PAGE_UP : speed *= 1.2f; break;
case GLUT_KEY_PAGE_DOWN : speed /= 1.2f; break;
}
}
}
void TutorialApplication::clickFunc(int button, int state, int x, int y)
{
if (state == GLUT_UP)
{
mouseMode = 0;
}
else
{
if (button == GLUT_RIGHT_BUTTON)
{
ISPCCamera ispccamera = camera.getISPCCamera(width,height);
Vec3fa p; bool hit = device_pick(float(x),float(y),ispccamera,p);
if (hit) {
Vec3fa delta = p - camera.to;
Vec3fa right = normalize(ispccamera.xfm.l.vx);
Vec3fa up = normalize(ispccamera.xfm.l.vy);
camera.to = p;
camera.from += dot(delta,right)*right + dot(delta,up)*up;
}
}
else
{
clickX = x; clickY = y;
int modifiers = glutGetModifiers();
if (button == GLUT_LEFT_BUTTON && modifiers == GLUT_ACTIVE_SHIFT) mouseMode = 1;
else if (button == GLUT_LEFT_BUTTON && modifiers == GLUT_ACTIVE_CTRL ) mouseMode = 3;
else if (button == GLUT_LEFT_BUTTON) mouseMode = 4;
}
}
}
void TutorialApplication::motionFunc(int x, int y)
{
float dClickX = float(clickX - x), dClickY = float(clickY - y);
clickX = x; clickY = y;
switch (mouseMode) {
case 1: camera.rotateOrbit(-0.005f*dClickX,0.005f*dClickY); break;
case 2: break;
case 3: camera.dolly(-dClickY); break;
case 4: camera.rotate(-0.005f*dClickX,0.005f*dClickY); break;
}
}
void TutorialApplication::displayFunc()
{
/* update camera */
camera.move(moveDelta.x*speed, moveDelta.y*speed, moveDelta.z*speed);
ISPCCamera ispccamera = camera.getISPCCamera(width,height,true);
/* render image using ISPC */
initRayStats();
double t0 = getSeconds();
device_render(pixels,width,height,float(time0-t0),ispccamera);
double dt0 = getSeconds()-t0;
avg_render_time.add(dt0);
double mrayps = double(getNumRays())/(1000000.0*dt0);
avg_mrayps.add(mrayps);
/* draw pixels to screen */
glDrawPixels(width,height,GL_RGBA,GL_UNSIGNED_BYTE,pixels);
if (fullscreen || !print_frame_rate)
{
glMatrixMode( GL_PROJECTION );
glPushMatrix();
glLoadIdentity();
gluOrtho2D( 0, width, 0, height );
glMatrixMode( GL_MODELVIEW );
glPushMatrix();
glLoadIdentity();
/* print frame rate */
glColor3f(1.0f, 0.25f, 0.0f);
std::ostringstream stream;
stream.setf(std::ios::fixed, std::ios::floatfield);
stream.precision(2);
stream << 1.0f/avg_render_time.get() << " fps";
std::string str = stream.str();
glRasterPos2i(6, height - 24);
for (size_t i=0; i<str.size(); i++)
glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, str[i]);
#if defined(RAY_STATS)
stream.str("");
stream << avg_mrayps.get() << " Mray/s";
str = stream.str();
glRasterPos2i(6, height - 52);
for (size_t i=0; i<str.size(); i++)
glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, str[i]);
#endif
glRasterPos2i( 0, 0 );
glPopMatrix();
glMatrixMode( GL_PROJECTION );
glPopMatrix();
glMatrixMode( GL_MODELVIEW );
}
glutSwapBuffers();
double dt1 = getSeconds()-t0;
avg_frame_time.add(dt1);
if (print_frame_rate)
{
std::ostringstream stream;
stream.setf(std::ios::fixed, std::ios::floatfield);
stream.precision(2);
stream << "render: ";
stream << 1.0f/dt0 << " fps, ";
stream << dt0*1000.0f << " ms, ";
#if defined(RAY_STATS)
stream << mrayps << " Mray/s, ";
#endif
stream << "display: ";
stream << 1.0f/dt1 << " fps, ";
stream << dt1*1000.0f << " ms, ";
stream << width << "x" << height << " pixels";
std::cout << stream.str() << std::endl;
}
if (print_camera)
std::cout << camera.str() << std::endl;
}
void TutorialApplication::reshapeFunc(int width, int height)
{
resize(width,height);
glViewport(0, 0, width, height);
this->width = width; this->height = height;
}
void TutorialApplication::idleFunc() {
glutPostRedisplay();
}
void keyboardFunc(unsigned char key, int x, int y) {
TutorialApplication::instance->keyboardFunc(key,x,y);
}
void keyboardUpFunc(unsigned char key, int x, int y) {
TutorialApplication::instance->keyboardUpFunc(key,x,y);
}
void specialFunc(int key, int x, int y) {
TutorialApplication::instance->specialFunc(key,x,y);
}
void clickFunc(int button, int state, int x, int y) {
TutorialApplication::instance->clickFunc(button,state,x,y);
}
void motionFunc(int x, int y) {
TutorialApplication::instance->motionFunc(x,y);
}
void displayFunc() {
TutorialApplication::instance->displayFunc();
}
void reshapeFunc(int width, int height) {
TutorialApplication::instance->reshapeFunc(width,height);
}
void idleFunc() {
TutorialApplication::instance->idleFunc();
}
void TutorialApplication::run(int argc, char** argv)
{
/* set debug values */
rtcDeviceSetParameter1i(nullptr,(RTCParameter) 1000000, debug0);
rtcDeviceSetParameter1i(nullptr,(RTCParameter) 1000001, debug1);
rtcDeviceSetParameter1i(nullptr,(RTCParameter) 1000002, debug2);
rtcDeviceSetParameter1i(nullptr,(RTCParameter) 1000003, debug3);
/* initialize ray tracing core */
device_init(rtcore.c_str());
/* set shader mode */
switch (shader) {
case SHADER_DEFAULT : break;
case SHADER_EYELIGHT : device_key_pressed(GLUT_KEY_F2); break;
case SHADER_OCCLUSION: device_key_pressed(GLUT_KEY_F3); break;
case SHADER_UV : device_key_pressed(GLUT_KEY_F4); break;
case SHADER_TEXCOORDS: device_key_pressed(GLUT_KEY_F8); break;
case SHADER_TEXCOORDS_GRID: device_key_pressed(GLUT_KEY_F8); device_key_pressed(GLUT_KEY_F8); break;
case SHADER_NG : device_key_pressed(GLUT_KEY_F5); break;
case SHADER_CYCLES : device_key_pressed(GLUT_KEY_F9); break;
case SHADER_GEOMID : device_key_pressed(GLUT_KEY_F6); break;
case SHADER_GEOMID_PRIMID: device_key_pressed(GLUT_KEY_F7); break;
case SHADER_AMBIENT_OCCLUSION: device_key_pressed(GLUT_KEY_F11); break;
};
/* benchmark mode */
if (numBenchmarkFrames)
{
renderBenchmark();
}
/* render to disk */
if (outputImageFilename.str() != "")
renderToFile(outputImageFilename);
/* interactive mode */
if (interactive)
{
resize(width,height);
glutInit(&argc, argv);
glutInitWindowSize((GLsizei)width, (GLsizei)height);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowPosition(0, 0);
windowID = glutCreateWindow(tutorialName.c_str());
if (fullscreen) glutFullScreen();
glutDisplayFunc(embree::displayFunc);
glutIdleFunc(embree::idleFunc);
glutKeyboardFunc(embree::keyboardFunc);
glutKeyboardUpFunc(embree::keyboardUpFunc);
glutSpecialFunc(embree::specialFunc);
glutMouseFunc(embree::clickFunc);
glutMotionFunc(embree::motionFunc);
glutReshapeFunc(embree::reshapeFunc);
glutMainLoop();
}
}
int TutorialApplication::main(int argc, char** argv) try
{
/* parse command line options */
parseCommandLine(argc,argv);
/* callback */
postParseCommandLine();
/* start tutorial */
run(argc,argv);
return 0;
}
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;
}
int SceneLoadingTutorialApplication::main(int argc, char** argv) try
{
/* parse command line options */
parseCommandLine(argc,argv);
/* callback */
try {
postParseCommandLine();
}
catch (const std::exception& e) {
std::cout << "Error: " << e.what() << std::endl;
}
/* load scene */
if (sceneFilename != "")
{
if (toLowerCase(sceneFilename.ext()) == std::string("obj"))
scene->add(loadOBJ(sceneFilename,subdiv_mode != ""));
else if (sceneFilename.ext() != "")
scene->add(SceneGraph::load(sceneFilename));
}
/* load key frames for animation */
for (size_t i=0;i<keyFramesFilenames.size();i++)
{
std::cout << "Adding ["<< keyFramesFilenames[i] << "] to scene..." << std::flush;
if (toLowerCase(keyFramesFilenames[i].ext()) == std::string("obj"))
scene->add(loadOBJ(keyFramesFilenames[i],subdiv_mode != "",true));
else if (keyFramesFilenames[i].ext() != "")
scene->add(SceneGraph::load(keyFramesFilenames[i]));
std::cout << "done" << std::endl << std::flush;
}
/* clear texture cache */
Texture::clearTextureCache();
/* perform removals */
if (remove_mblur) scene->remove_mblur(true);
if (remove_non_mblur) scene->remove_mblur(false);
/* perform conversions */
if (convert_tris_to_quads ) scene->triangles_to_quads(convert_tris_to_quads_prop);
if (convert_bezier_to_lines ) scene->bezier_to_lines();
if (convert_hair_to_curves ) scene->hair_to_curves();
if (convert_bezier_to_bspline) scene->bezier_to_bspline();
if (convert_bspline_to_bezier) scene->bspline_to_bezier();
/* convert model */
obj_scene.add(SceneGraph::flatten(scene,instancing_mode));
scene = nullptr;
/* print all cameras */
if (print_scene_cameras) {
obj_scene.print_camera_names();
return 0;
}
/* use specified camera */
if (camera_name != "") {
Ref<SceneGraph::PerspectiveCameraNode> c = obj_scene.getCamera(camera_name);
camera = Camera(c->from,c->to,c->up,c->fov);
}
/* otherwise use default camera */
else if (!command_line_camera) {
Ref<SceneGraph::PerspectiveCameraNode> c = obj_scene.getDefaultCamera();
if (c) camera = Camera(c->from,c->to,c->up,c->fov);
}
/* send model */
set_scene(&obj_scene);
/* start tutorial */
run(argc,argv);
return 0;
}
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;
}
/* draws progress bar */
static int progressWidth = 0;
static std::atomic<size_t> progressDots(0);
extern "C" void progressStart()
{
progressDots = 0;
progressWidth = max(3,getTerminalWidth());
std::cout << "[" << std::flush;
}
extern "C" bool progressMonitor(void* ptr, const double n)
{
size_t olddots = progressDots;
size_t maxdots = progressWidth-2;
size_t newdots = max(olddots,min(size_t(maxdots),size_t(n*double(maxdots))));
if (progressDots.compare_exchange_strong(olddots,newdots))
for (size_t i=olddots; i<newdots; i++) std::cout << "." << std::flush;
return true;
}
extern "C" void progressEnd() {
std::cout << "]" << std::endl;
}
}