erebus

changeset 31:53a98c148bf8

- introduced SurfaceGeometry to carry all the geometric information input to BRDF sampling and evaluation functions. - made Reflectance keep an (optional) pointer to its material - simplified PhongRefl::sample_dir, with the help of SurfaceGeometry - worked around microsoft's broken std::thread implementation's deadlock on join
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 07 Jun 2014 09:14:17 +0300
parents e78f68d03ae9
children b1fc96c71bcc
files liberebus/src/brdf.cc liberebus/src/brdf.h liberebus/src/erebus.cc liberebus/src/erebus_impl.h liberebus/src/rt.cc liberebus/src/threadpool.cc
diffstat 6 files changed, 204 insertions(+), 75 deletions(-) [+]
line diff
     1.1 --- a/liberebus/src/brdf.cc	Sat Jun 07 06:10:21 2014 +0300
     1.2 +++ b/liberebus/src/brdf.cc	Sat Jun 07 09:14:17 2014 +0300
     1.3 @@ -1,16 +1,85 @@
     1.4 +#include <math.h>
     1.5  #include <algorithm>
     1.6  #include "brdf.h"
     1.7 +#include "material.h"
     1.8  #include "erebus_impl.h"
     1.9  
    1.10 +// --- class SurfaceGeometry ---
    1.11 +SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, VecLength st)
    1.12 +	: SurfaceGeometry(norm, Vector2(0, 0), st)
    1.13 +{
    1.14 +}
    1.15 +
    1.16 +SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, const Vector2 &uvarg, VecLength st)
    1.17 +	: normal(norm), uv(uvarg)
    1.18 +{
    1.19 +	if(st == unknown) {
    1.20 +		normal.normalize();
    1.21 +	}
    1.22 +
    1.23 +	tangent = Vector3(1.0f, 0.0f, 0.0f);
    1.24 +	if(fabs(dot_product(normal, tangent)) - 1.0f < 1e-4f) {
    1.25 +		tangent = Vector3(0.0f, 0.0f, 1.0f);
    1.26 +	}
    1.27 +	Vector3 bitan = cross_product(normal, tangent);
    1.28 +	tangent = cross_product(bitan, normal);
    1.29 +}
    1.30 +
    1.31 +SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, const Vector3 &tang, VecLength st)
    1.32 +	: SurfaceGeometry(norm, tang, Vector2(0, 0), st)
    1.33 +{
    1.34 +}
    1.35 +
    1.36 +SurfaceGeometry::SurfaceGeometry(const Vector3 &norm, const Vector3 &tang, const Vector2 &uvarg, VecLength st)
    1.37 +	: normal(norm), tangent(tang), uv(uvarg)
    1.38 +{
    1.39 +	if(st == unknown) {
    1.40 +		normal.normalize();
    1.41 +		tangent.normalize();
    1.42 +	}
    1.43 +}
    1.44 +
    1.45 +Vector3 SurfaceGeometry::spherical_to_world(float theta, float phi) const
    1.46 +{
    1.47 +	float x = cos(theta) * sin(phi);
    1.48 +	float y = sin(theta) * sin(phi);
    1.49 +	float z = cos(phi);
    1.50 +	return sample_to_world(Vector3(x, y, z));
    1.51 +}
    1.52 +
    1.53 +Vector3 SurfaceGeometry::sample_to_world(const Vector3 &v) const
    1.54 +{
    1.55 +	Matrix3x3 xform;
    1.56 +	xform.set_column_vector(tangent, 0);
    1.57 +	xform.set_column_vector(cross_product(normal, tangent), 1);
    1.58 +	xform.set_column_vector(normal, 2);
    1.59 +	return v.transformed(xform);
    1.60 +}
    1.61 +
    1.62 +Vector3 SurfaceGeometry::world_to_sample(const Vector3 &v) const
    1.63 +{
    1.64 +	Matrix3x3 xform;
    1.65 +	xform.set_row_vector(tangent, 0);
    1.66 +	xform.set_row_vector(cross_product(normal, tangent), 1);
    1.67 +	xform.set_row_vector(normal, 2);
    1.68 +	return v.transformed(xform);
    1.69 +}
    1.70 +
    1.71  // ---- class Reflectance ----
    1.72  Reflectance::Reflectance()
    1.73  {
    1.74 +	mtl = 0;
    1.75  }
    1.76  
    1.77 -float Reflectance::sample(const Vector3 &norm, const Vector3 &outdir, Vector3 *indir) const
    1.78 +Reflectance::Reflectance(const Material *mtl)
    1.79  {
    1.80 -	*indir = sample_dir(norm, outdir);
    1.81 -	return eval(norm, outdir, *indir);
    1.82 +	this->mtl = mtl;
    1.83 +}
    1.84 +
    1.85 +float Reflectance::sample(const SurfaceGeometry &geom, const Vector3 &outdir, Vector3 *indir) const
    1.86 +{
    1.87 +	*indir = sample_dir(geom, outdir);
    1.88 +	return eval(geom, outdir, *indir);
    1.89  }
    1.90  
    1.91  // --- class CompositeRefl ----
    1.92 @@ -19,7 +88,13 @@
    1.93  	valid_checked = false;
    1.94  }
    1.95  
    1.96 -int CompositeRefl::pick_brdf(const Vector3 &norm, const Vector3 &outdir) const
    1.97 +CompositeRefl::CompositeRefl(const Material *mtl)
    1.98 +	: Reflectance(mtl)
    1.99 +{
   1.100 +	valid_checked = false;
   1.101 +}
   1.102 +
   1.103 +int CompositeRefl::pick_brdf(const SurfaceGeometry &geom, const Vector3 &outdir) const
   1.104  {
   1.105  	if(sub_brdf.empty()) {
   1.106  		return -1;
   1.107 @@ -34,7 +109,7 @@
   1.108  	float *cdf = (float*)alloca(brdf_count * sizeof *cdf);
   1.109  	for(int i=0; i<brdf_count; i++) {
   1.110  		const SubRefl &sub = sub_brdf[i];
   1.111 -		float w = sub.weight_func ? sub.weight_func(norm, outdir) : sub.weight;
   1.112 +		float w = sub.weight_func ? sub.weight_func(geom, outdir) : sub.weight;
   1.113  		cdf[i] = i > 0 ? cdf[i - 1] + w : w;
   1.114  	}
   1.115  
   1.116 @@ -83,93 +158,74 @@
   1.117  	"warning: don't call CompositeRefl's sample_dir and eval separately\n"
   1.118  	"         it'll pick different brdfs each time! use sample instead\n";
   1.119  
   1.120 -Vector3 CompositeRefl::sample_dir(const Vector3 &norm, const Vector3 &outdir) const
   1.121 +Vector3 CompositeRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const
   1.122  {
   1.123  	if(!warned_composite_usage) {
   1.124  		fputs(composite_usage_warnstr, stderr);
   1.125  		warned_composite_usage = true;
   1.126  	}
   1.127 -	int bidx = pick_brdf(norm, outdir);
   1.128 -	return sub_brdf[bidx].brdf->sample_dir(norm, outdir);
   1.129 +	int bidx = pick_brdf(geom, outdir);
   1.130 +	return sub_brdf[bidx].brdf->sample_dir(geom, outdir);
   1.131  }
   1.132  
   1.133 -float CompositeRefl::sample(const Vector3 &norm, const Vector3 &outdir, Vector3 *indir) const
   1.134 +float CompositeRefl::sample(const SurfaceGeometry &geom, const Vector3 &outdir, Vector3 *indir) const
   1.135  {
   1.136 -	int bidx = pick_brdf(norm, outdir);
   1.137 -	return sub_brdf[bidx].brdf->sample(norm, outdir, indir);
   1.138 +	int bidx = pick_brdf(geom, outdir);
   1.139 +	return sub_brdf[bidx].brdf->sample(geom, outdir, indir);
   1.140  }
   1.141  
   1.142 -float CompositeRefl::eval(const Vector3 &norm, const Vector3 &outdir, const Vector3 &indir) const
   1.143 +float CompositeRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const
   1.144  {
   1.145  	if(!warned_composite_usage) {
   1.146  		fputs(composite_usage_warnstr, stderr);
   1.147  		warned_composite_usage = true;
   1.148  	}
   1.149 -	int bidx = pick_brdf(norm, outdir);
   1.150 -	return sub_brdf[bidx].brdf->eval(norm, outdir, indir);
   1.151 +	int bidx = pick_brdf(geom, outdir);
   1.152 +	return sub_brdf[bidx].brdf->eval(geom, outdir, indir);
   1.153  }
   1.154  
   1.155  // --- class LambertRefl ---
   1.156 -Vector3 LambertRefl::sample_dir(const Vector3 &norm, const Vector3 &outdir) const
   1.157 +Vector3 LambertRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const
   1.158  {
   1.159  	Vector3 dir = Vector3{randf(-1, 1), randf(-1, 1), randf(-1, 1)}.normalized();
   1.160 -	return dot_product(dir, norm) < 0.0 ? -dir : dir;
   1.161 +	return dot_product(dir, geom.normal) < 0.0 ? -dir : dir;
   1.162  }
   1.163  
   1.164 -float LambertRefl::eval(const Vector3 &norm, const Vector3 &outdir, const Vector3 &indir) const
   1.165 +float LambertRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const
   1.166  {
   1.167 -	return dot_product(norm, outdir);
   1.168 +	return dot_product(geom.normal, outdir);
   1.169  }
   1.170  
   1.171  // --- class MirrorRefl ---
   1.172 -Vector3 MirrorRefl::sample_dir(const Vector3 &norm, const Vector3 &outdir) const
   1.173 +Vector3 MirrorRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const
   1.174  {
   1.175 -	return outdir.reflection(norm);
   1.176 +	return outdir.reflection(geom.normal);
   1.177  }
   1.178  
   1.179 -float MirrorRefl::eval(const Vector3 &norm, const Vector3 &outdir, const Vector3 &indir) const
   1.180 +float MirrorRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const
   1.181  {
   1.182  	return 1.0f;
   1.183  }
   1.184  
   1.185  // --- class PhongRefl ---
   1.186 -PhongRefl::PhongRefl()
   1.187 +Vector3 PhongRefl::sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const
   1.188  {
   1.189 -	shininess = 42.0;
   1.190 -}
   1.191 +	Vector3 refl = outdir.reflection(geom.normal).normalized();
   1.192 +	SurfaceGeometry refl_geom(refl, SurfaceGeometry::unit);
   1.193  
   1.194 -Vector3 PhongRefl::sample_dir(const Vector3 &norm, const Vector3 &outdir) const
   1.195 -{
   1.196 -	// construct orthonormal basis with k being the reflection dir
   1.197 -	Vector3 refl = outdir.reflection(norm).normalized();
   1.198 -	Vector3 up = Vector3(0, 0, 1);
   1.199 -	if(fabs(dot_product(refl, up)) - 1.0 < 0.001) {
   1.200 -		up = Vector3(0, 1, 0);
   1.201 -	}
   1.202 -	Vector3 right = cross_product(up, refl);
   1.203 -	up = cross_product(refl, right);
   1.204 -
   1.205 -	// construct the matrix transposed to move the sample from reflection
   1.206 -	// space to world space
   1.207 -	Matrix3x3 xform;
   1.208 -	xform.set_column_vector(right, 0);
   1.209 -	xform.set_column_vector(up, 1);
   1.210 -	xform.set_column_vector(refl, 2);
   1.211 +	float shininess = mtl ? mtl->get_attrib_value("shininess", geom.uv.x, geom.uv.y) : 42.0;
   1.212  
   1.213  	float phi = acos(pow(randf(), 1.0 / (shininess + 1)));
   1.214  	float theta = 2.0 * M_PI * randf();
   1.215  
   1.216 -	Vector3 v;
   1.217 -	v.x = cos(theta) * sin(phi);
   1.218 -	v.y = sin(theta) * sin(phi);
   1.219 -	v.z = cos(phi);
   1.220 -	v.transform(xform);
   1.221 -	return v;
   1.222 +	return refl_geom.spherical_to_world(theta, phi);
   1.223  }
   1.224  
   1.225 -float PhongRefl::eval(const Vector3 &norm, const Vector3 &outdir, const Vector3 &indir) const
   1.226 +float PhongRefl::eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const
   1.227  {
   1.228 -	Vector3 refl = outdir.reflection(norm);
   1.229 +	float shininess = mtl ? mtl->get_attrib_value("shininess", geom.uv.x, geom.uv.y) : 42.0;
   1.230 +
   1.231 +	Vector3 refl = outdir.reflection(geom.normal);
   1.232  	float dot = std::max<float>(dot_product(indir, refl), 0.0f);
   1.233  	return pow(dot, shininess);
   1.234  }
     2.1 --- a/liberebus/src/brdf.h	Sat Jun 07 06:10:21 2014 +0300
     2.2 +++ b/liberebus/src/brdf.h	Sat Jun 07 09:14:17 2014 +0300
     2.3 @@ -4,18 +4,56 @@
     2.4  #include <vector>
     2.5  #include "texture.h"
     2.6  
     2.7 +class Material;
     2.8 +
     2.9 +class SurfaceGeometry {
    2.10 +public:
    2.11 +	Vector3 normal, tangent;
    2.12 +	Vector2 uv;
    2.13 +
    2.14 +	enum VecLength { unit, unknown };
    2.15 +
    2.16 +	explicit SurfaceGeometry(const Vector3 &norm, VecLength st = unknown);
    2.17 +	SurfaceGeometry(const Vector3 &norm, const Vector2 &uv, VecLength st = unknown);
    2.18 +	SurfaceGeometry(const Vector3 &norm, const Vector3 &tang, VecLength st = unknown);
    2.19 +	SurfaceGeometry(const Vector3 &norm, const Vector3 &tang, const Vector2 &uv, VecLength st = unknown);
    2.20 +
    2.21 +	/** create a cartesian direction vector in sample space (zenith = +Z) and
    2.22 +	 * transform it to world space.
    2.23 +	 * \param theta the horizontal angle (azimuth, in radians) around the Z axis [0, 2pi]
    2.24 +	 * \param phi the vertical angle (elevation, in radians) away from the Z axis [0, pi]
    2.25 +	 */
    2.26 +	Vector3 spherical_to_world(float theta, float phi) const;
    2.27 +	/// transforms a direction vector from sample space (centered around Z) to world space
    2.28 +	Vector3 sample_to_world(const Vector3 &v) const;
    2.29 +	/// transforms a direction vector from world space to sample space (centered around Z)
    2.30 +	Vector3 world_to_sample(const Vector3 &v) const;
    2.31 +};
    2.32 +
    2.33 +/// abstract bidirection reflectance distribution function base class
    2.34  class Reflectance {
    2.35 +protected:
    2.36 +	const Material *mtl;	// pointer to the material we belong to
    2.37 +
    2.38  public:
    2.39  	Reflectance();
    2.40 +	explicit Reflectance(const Material *mtl);
    2.41  	virtual ~Reflectance() = default;
    2.42  
    2.43 -	virtual Vector3 sample_dir(const Vector3 &norm, const Vector3 &outdir) const = 0;
    2.44 -	virtual float sample(const Vector3 &norm, const Vector3 &outdir, Vector3 *indir) const;
    2.45 -	virtual float eval(const Vector3 &norm, const Vector3 &outdir, const Vector3 &indir) const = 0;
    2.46 +	void set_material(const Material *mtl);
    2.47 +	const Material *get_material() const;
    2.48 +
    2.49 +	/// given an outgoing light direction generate an incidence direction to sample the BRDF
    2.50 +	virtual Vector3 sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const = 0;
    2.51 +	/// given an outgoing direction, generate an incidence direction, and evaluate its probability
    2.52 +	virtual float sample(const SurfaceGeometry &geom, const Vector3 &outdir, Vector3 *indir) const;
    2.53 +	/// given an outgoing direction and an incidence direction, evaluate the probability of this path
    2.54 +	virtual float eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const = 0;
    2.55  };
    2.56  
    2.57 -typedef float (*CompReflWeightFunc)(const Vector3 &norm, const Vector3 &outdir);
    2.58 +typedef float (*CompReflWeightFunc)(const SurfaceGeometry &geom, const Vector3 &outdir);
    2.59  
    2.60 +/// composite BRDF, with multiple weighted sub-reflectances.
    2.61  class CompositeRefl : public Reflectance {
    2.62  private:
    2.63  	struct SubRefl {
    2.64 @@ -26,41 +64,41 @@
    2.65  	std::vector<SubRefl> sub_brdf;
    2.66  	mutable bool valid_checked;
    2.67  
    2.68 -	int pick_brdf(const Vector3 &norm, const Vector3 &outdir) const;
    2.69 +	int pick_brdf(const SurfaceGeometry &geom, const Vector3 &outdir) const;
    2.70  
    2.71  public:
    2.72  	CompositeRefl();
    2.73 +	explicit CompositeRefl(const Material *mtl);
    2.74  
    2.75  	virtual void add_brdf(Reflectance *brdf, float weight);
    2.76  	virtual void add_brdf(Reflectance *brdf, CompReflWeightFunc weight_func);
    2.77  
    2.78  	bool check_valid() const;
    2.79  
    2.80 -	Vector3 sample_dir(const Vector3 &norm, const Vector3 &outdir) const override;
    2.81 -	float sample(const Vector3 &norm, const Vector3 &outdir, Vector3 *indir) const override;
    2.82 -	float eval(const Vector3 &norm, const Vector3 &outdir, const Vector3 &indir) const override;
    2.83 +	Vector3 sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const override;
    2.84 +	float sample(const SurfaceGeometry &geom, const Vector3 &outdir, Vector3 *indir) const override;
    2.85 +	float eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const override;
    2.86  };
    2.87  
    2.88 +/// lambertian perfect diffuse reflectance
    2.89  class LambertRefl : public Reflectance {
    2.90  public:
    2.91 -	Vector3 sample_dir(const Vector3 &norm, const Vector3 &outdir) const override;
    2.92 -	float eval(const Vector3 &norm, const Vector3 &outdir, const Vector3 &indir) const override;
    2.93 +	Vector3 sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const override;
    2.94 +	float eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const override;
    2.95  };
    2.96  
    2.97 +/// perfect specular reflectance
    2.98  class MirrorRefl : public Reflectance {
    2.99  public:
   2.100 -	Vector3 sample_dir(const Vector3 &norm, const Vector3 &outdir) const override;
   2.101 -	float eval(const Vector3 &norm, const Vector3 &outdir, const Vector3 &indir) const override;
   2.102 +	Vector3 sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const override;
   2.103 +	float eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const override;
   2.104  };
   2.105  
   2.106 +/// glossy phong reflectance with lafortune sampling
   2.107  class PhongRefl : public Reflectance {
   2.108  public:
   2.109 -	float shininess;
   2.110 -
   2.111 -	PhongRefl();
   2.112 -
   2.113 -	Vector3 sample_dir(const Vector3 &norm, const Vector3 &outdir) const override;
   2.114 -	float eval(const Vector3 &norm, const Vector3 &outdir, const Vector3 &indir) const override;
   2.115 +	Vector3 sample_dir(const SurfaceGeometry &geom, const Vector3 &outdir) const override;
   2.116 +	float eval(const SurfaceGeometry &geom, const Vector3 &outdir, const Vector3 &indir) const override;
   2.117  };
   2.118  
   2.119  #endif	// BRDF_H_
     3.1 --- a/liberebus/src/erebus.cc	Sat Jun 07 06:10:21 2014 +0300
     3.2 +++ b/liberebus/src/erebus.cc	Sat Jun 07 09:14:17 2014 +0300
     3.3 @@ -22,11 +22,13 @@
     3.4  
     3.5  struct erebus *erb_init(void)
     3.6  {
     3.7 -	struct erebus *ctx;
     3.8 +	struct erebus *ctx = 0;
     3.9  	try {
    3.10  		ctx = new struct erebus;
    3.11 +		ctx->tpool = new ThreadPool;
    3.12  	}
    3.13  	catch(...) {
    3.14 +		delete ctx;
    3.15  		return 0;
    3.16  	}
    3.17  
    3.18 @@ -47,7 +49,11 @@
    3.19  
    3.20  void erb_destroy(struct erebus *ctx)
    3.21  {
    3.22 -	delete ctx;
    3.23 +	if(ctx) {
    3.24 +		// make sure the threadpool stops BEFORE destroying the framebuffers etc in ctx
    3.25 +		delete ctx->tpool;
    3.26 +		delete ctx;
    3.27 +	}
    3.28  }
    3.29  
    3.30  void erb_setopti(struct erebus *ctx, enum erb_option opt, int val)
    3.31 @@ -154,9 +160,9 @@
    3.32  
    3.33  int erb_render_rect(struct erebus *ctx, int x, int y, int width, int height, long timeout)
    3.34  {
    3.35 -	while(ctx->tpool.pending()) {
    3.36 +	while(ctx->tpool->pending()) {
    3.37  		if(timeout > 0) {
    3.38 -			long wait_interval = ctx->tpool.wait(timeout);
    3.39 +			long wait_interval = ctx->tpool->wait(timeout);
    3.40  			timeout -= wait_interval;
    3.41  		} else {
    3.42  			return 1;
    3.43 @@ -180,7 +186,7 @@
    3.44  			blk.sample = ctx->cur_sample;
    3.45  			blk.frame = ctx->cur_frame;
    3.46  
    3.47 -			ctx->tpool.add_work(std::bind(render_block, ctx, blk));
    3.48 +			ctx->tpool->add_work(std::bind(render_block, ctx, blk));
    3.49  
    3.50  			x += BLKSZ;
    3.51  		}
    3.52 @@ -188,7 +194,7 @@
    3.53  	}
    3.54  
    3.55  	++ctx->cur_sample;
    3.56 -	ctx->tpool.wait(timeout);	// wait for completion
    3.57 +	ctx->tpool->wait(timeout);	// wait for completion
    3.58  	return ctx->cur_sample > erb_getopti(ctx, ERB_OPT_MAX_SAMPLES) ? 0 : 1;
    3.59  }
    3.60  
     4.1 --- a/liberebus/src/erebus_impl.h	Sat Jun 07 06:10:21 2014 +0300
     4.2 +++ b/liberebus/src/erebus_impl.h	Sat Jun 07 09:14:17 2014 +0300
     4.3 @@ -28,7 +28,7 @@
     4.4  	Image<float> accum;	// sample accumulator per pixel
     4.5  	Option options[ERB_NUM_OPTIONS];
     4.6  
     4.7 -	ThreadPool tpool;
     4.8 +	ThreadPool *tpool;
     4.9  
    4.10  	// render state
    4.11  	float inv_gamma;
     5.1 --- a/liberebus/src/rt.cc	Sat Jun 07 06:10:21 2014 +0300
     5.2 +++ b/liberebus/src/rt.cc	Sat Jun 07 09:14:17 2014 +0300
     5.3 @@ -46,7 +46,7 @@
     5.4  	float shininess = mtl->get_attrib_value("shininess");
     5.5  
     5.6  	Vector3 sample_dir;
     5.7 -	float prob = brdf->sample(norm, -hit.world_ray.dir, &sample_dir);
     5.8 +	float prob = brdf->sample(SurfaceGeometry(norm), -hit.world_ray.dir, &sample_dir);
     5.9  	if(iter < max_iter && randf() <= prob && ray.energy * prob > 0.001) {
    5.10  		Ray sample_ray;
    5.11  		sample_ray.origin = ray.origin + ray.dir * hit.dist;
     6.1 --- a/liberebus/src/threadpool.cc	Sat Jun 07 06:10:21 2014 +0300
     6.2 +++ b/liberebus/src/threadpool.cc	Sat Jun 07 09:14:17 2014 +0300
     6.3 @@ -19,23 +19,52 @@
     6.4  	thread = new std::thread[num_threads];
     6.5  	for(int i=0; i<num_threads; i++) {
     6.6  		thread[i] = std::thread(&ThreadPool::thread_func, this);
     6.7 +
     6.8 +#ifdef _MSC_VER
     6.9 +		/* detach the thread to avoid having to join them in the destructor, which
    6.10 +		 * causes a deadlock in msvc implementation when called after main returns
    6.11 +		 */
    6.12 +		thread[i].detach();
    6.13 +#endif
    6.14  	}
    6.15  	this->num_threads = num_threads;
    6.16  }
    6.17  
    6.18  ThreadPool::~ThreadPool()
    6.19  {
    6.20 +#ifdef _MSC_VER
    6.21 +	workq_mutex.lock();
    6.22 +	workq.clear();
    6.23 +	qsize = 0;
    6.24 +	workq_mutex.unlock();
    6.25 +#endif
    6.26 +
    6.27  	quit = true;
    6.28  	workq_condvar.notify_all();
    6.29  
    6.30  	printf("ThreadPool: waiting for %d worker threads to stop ", num_threads);
    6.31  	fflush(stdout);
    6.32 +#ifndef _MSC_VER
    6.33  	for(int i=0; i<num_threads; i++) {
    6.34  		thread[i].join();
    6.35  		putchar('.');
    6.36  		fflush(stdout);
    6.37  	}
    6.38 +
    6.39 +#else
    6.40 +	// spin until all threads are done...
    6.41 +	std::unique_lock<std::mutex> lock(workq_mutex);
    6.42 +	while(nactive > 0) {
    6.43 +		lock.unlock();
    6.44 +		std::this_thread::sleep_for(std::chrono::milliseconds(128));
    6.45 +		putchar('.');
    6.46 +		fflush(stdout);
    6.47 +		lock.lock();
    6.48 +	}
    6.49 +#endif	// _MSC_VER
    6.50 +
    6.51  	putchar('\n');
    6.52 +	delete [] thread;
    6.53  }
    6.54  
    6.55  void ThreadPool::add_work(std::function<void ()> func)