コード例 #1
0
ファイル: Facade.c プロジェクト: qarl/lumetools
miScalar lume_plane_ray_intersection(miVector *intersection,
				      miVector *plane_point, 
				      miVector *plane_dir,
				      miVector *ray_point,
				      miVector *ray_dir)
{
  miScalar q;
  miScalar t;

  q = mi_vector_dot(plane_dir, ray_dir);

  if (fabs(q) < 0.0001)
    return 0;  /* parallel */
    
  t = (mi_vector_dot(plane_point, plane_dir) -
       mi_vector_dot(plane_dir, ray_point)) / q;

  *intersection = *ray_dir;

  mi_vector_mul(intersection, t);
  mi_vector_add(intersection, intersection, ray_point);

  return t;

}
コード例 #2
0
ファイル: contourshade.c プロジェクト: OpenXRay/xray
miBoolean contour_shader_widthfromlightdir(
	miContour_endpoint  *result,
	miStdInfo	    *info_near,
	miStdInfo	    *info_far,
	miState		    *state,
	Lightdir_Parameters *paras)
{
	double		    d;
	miVector	    dir;
	miScalar	    max_width;
	miScalar	    min_width;

	/* Contour color given by a parameter */
	result->color = *mi_eval_color(&paras->color);

	/* Normalize light direction (just in case user didn't) */
	dir = *mi_eval_vector(&paras->light_dir);
	mi_vector_normalize(&dir);

	/* The contour gets wider the more the normal differs from the light
	   source direction */
	d = mi_vector_dot(&dir, &info_near->normal);

	min_width     = *mi_eval_scalar(&paras->min_width);
	max_width     = *mi_eval_scalar(&paras->max_width);
	result->width = min_width + 0.5 * (max_width - min_width) * (1.0 - d);

	miASSERT(min_width <= result->width && result->width <= max_width);
	return(miTRUE);
}
コード例 #3
0
ファイル: contourshade.c プロジェクト: OpenXRay/xray
miBoolean contour_shader_curvature(
	miContour_endpoint	  *result,
	miStdInfo		  *info_near,
	miStdInfo		  *info_far,
	miState			  *state,
	Widthfromcolor_Parameters *paras)
{
	double			  d;
	miScalar		  min_width;
	miScalar		  max_width;
	miASSERT(info_near || info_far);

	/* Constant contour color */
	result->color = *mi_eval_color(&paras->color);
	max_width     = *mi_eval_scalar(&paras->max_width);
	min_width     = *mi_eval_scalar(&paras->min_width);

	if ((info_near == NULL) != (info_far == NULL)) {
		/* Max contour width if one point hit background */
		result->width = max_width;

	} else if (fabs(info_near->point.z - info_far->point.z) > 1.0) {
		/* Max contour width if large difference in depth */
		result->width = max_width;
	} else {
		/* Otherwise, the contour width depends on the curvature */
		d = mi_vector_dot(&info_near->normal, &info_far->normal);
		miASSERT(-1.0 <= d && d <= 1.0);
		result->width = min_width + 
				0.5 * (1.0 - d) * (max_width-min_width);
	}
	miASSERT(min_width <= result->width && result->width <= max_width);
	return(miTRUE);
}
コード例 #4
0
void do_diffuse_and_spec(miColor *result,miState *state,miColor *diffuse, miColor *specular,miScalar specular_exponent){	
	// Lights stuff
	miTag *light = (miTag *) malloc(sizeof(miTag *));
	
	int light_sample_count = 0;
	int light_count = 0;
	miScalar dot_normal_light;	
	miColor light_color;
	
	// TMP variables
	miColor sum;
	miVector light_direction;
	miScalar spec_amount;
	
	//resultValues
	miColor *diffuse_out = (miColor *) malloc(sizeof(miColor *));
	miColor *specular_out = (miColor *) malloc(sizeof(miColor *));
	
	mi_inclusive_lightlist(&light_count, &light, state);
	for (mi::shader::LightIterator iter(state, light, light_count);
			     !iter.at_end(); ++iter) {
			
		light_sample_count = 0;
		sum.r = sum.g = sum.b = 0;
		
		while(iter->sample()){
			
			dot_normal_light = iter->get_dot_nl();
			iter->get_contribution(&light_color);
			light_direction = iter->get_direction();
			miVector normal = state->normal;
			spec_amount = pow(mi_vector_dot(&normal,&light_direction),specular_exponent);
			diffuse_out->r = (dot_normal_light * diffuse->r * light_color.r);
			diffuse_out->g = (dot_normal_light * diffuse->g * light_color.g);
			diffuse_out->b = (dot_normal_light * diffuse->b * light_color.b);
			
			specular_out->r = (specular->r * light_color.r * spec_amount);
			specular_out->g = (specular->g * light_color.g * spec_amount);
			specular_out->b = (specular->b * light_color.b * spec_amount);
			
			sum.r += diffuse_out->r + specular_out->r;
			sum.g += diffuse_out->g + specular_out->g;
			sum.b += diffuse_out->b + specular_out->b;
			
			light_sample_count++;
		}
		
		if (light_sample_count){
			result->r += sum.r*(1.0/light_sample_count);
			result->g += sum.g*(1.0/light_sample_count);
			result->b += sum.b*(1.0/light_sample_count);
			
		}
		
	}
}
コード例 #5
0
ファイル: contourshade.c プロジェクト: OpenXRay/xray
miBoolean contour_shader_widthfromlight(
	miContour_endpoint *result,
	miStdInfo	   *info_near,
	miStdInfo	   *info_far,
	miState		   *state,
	Light_Parameters   *paras)
{
	miVector	   orgp;	/* light source origin */
	miVector	   dirp;	/* light source direction */
	miVector	   dir;
	double		   d;
	miScalar	   min_width;
	miScalar	   max_width;

	/* Contour color given by a parameter */
	result->color = *mi_eval_color(&paras->color);

	/* Get light origin or direction */
	mi_light_info(*mi_eval_tag(&paras->light), &orgp, &dirp, 0);
	/* Now orgp or dirp is different from the null vector */

	/* Compute direction from light to point */
	if (mi_vector_dot(&orgp, &orgp) > miEPS) {   /* point light source */
		mi_vector_sub(&dir, &info_near->point, &orgp);

	} else {				/* directional light source */
		miASSERT(mi_vector_dot(&dirp, &dirp) > miEPS);
		dir = dirp;
	}
	mi_vector_normalize(&dir);

	/* The contour gets wider the more the normal is pointing in the same
	   direction as the light source */
	d = mi_vector_dot(&dir, &info_near->normal);
	min_width = *mi_eval_scalar(&paras->min_width);
	max_width = *mi_eval_scalar(&paras->max_width);
	result->width = min_width + 0.5 * (max_width - min_width) * (d+1.0);

	miASSERT(min_width <= result->width && result->width <= max_width);
	return(miTRUE);
}
コード例 #6
0
static void build_rot_matrix(
    miVector *old_dir,
    miVector *new_dir,
    miMatrix rot_transform)
{
    miVector rot_axis;
    mi_vector_prod(&rot_axis, old_dir, new_dir);
    mi_vector_normalize(&rot_axis);

    float cos_theta = mi_vector_dot(old_dir, new_dir);
    float theta = (float) acos(cos_theta);

    mi_matrix_rotate_axis(rot_transform, &rot_axis, theta);
}
コード例 #7
0
ファイル: common.c プロジェクト: mruegenberg/miWorleyNoise
miScalar dist_linear_squared3(miVector *v1, miVector *v2) {
  miVector s; mi_vector_sub(&s,v1,v2);
  return mi_vector_dot(&s,&s);
}
コード例 #8
0
ファイル: contourshade.c プロジェクト: OpenXRay/xray
miBoolean contour_shader_combi(
	miContour_endpoint *result,
	miStdInfo	   *info_near,
	miStdInfo	   *info_far,
	miState		   *state,
	Combi_Parameters   *paras)
{
	miScalar	   depth = info_near->point.z;
	double		   near_z, far_z, w_near, w_far;
	miVector	   orgp;		/* light source origin */
	miVector	   dirp;		/* light source direction */
	miVector	   dir;
	double		   d;
	double		   factor;
	miTag		   light;

	/* Ensure that near_z and far_z are negative as they should be */
	near_z = -fabs(*mi_eval_scalar(&paras->near_z));
	far_z  = -fabs(*mi_eval_scalar(&paras->far_z));

	if (depth > near_z) {		/* contour is closer than near_z */
		result->color = *mi_eval_color(&paras->near_color);
		result->width = *mi_eval_scalar(&paras->near_width);

	} else if (depth < far_z) {	/* contour is more distant than far_z*/
		result->color = *mi_eval_color(&paras->far_color);
		result->width = *mi_eval_scalar(&paras->far_width);
	} else {			/* contour is betwn near_z and far_z */
		miColor near_color = *mi_eval_color(&paras->near_color);
		miColor far_color  = *mi_eval_color(&paras->far_color);
		/* Weights w_near and w_far depend on depth */
		w_near = (depth - far_z) / (near_z - far_z);
		miASSERT(0.0 <= w_near && w_near <= 1.0);
		w_far = 1.0 - w_near;

		/* Mix of near_color and far_color according to weights */
		result->color.r = w_near * near_color.r + w_far * far_color.r;
		result->color.g = w_near * near_color.g + w_far * far_color.g;
		result->color.b = w_near * near_color.b + w_far * far_color.b;
		result->color.a = w_near * near_color.a + w_far * far_color.a;

		/* Width depending on weights */
		result->width   = w_near * *mi_eval_scalar(&paras->near_width) 
				+ w_far  * *mi_eval_scalar(&paras->far_width);
	}

	/* Width decreases by factor for each refraction_level */
	factor = *mi_eval_scalar(&paras->factor);
	if (factor > miEPS)
		result->width *= pow(factor, (double)info_near->level - 1.0);

	light = *mi_eval_tag(&paras->light);
	if (light) {
		miScalar light_min_factor = 
				*mi_eval_scalar(&paras->light_min_factor);
		/* Get light origin or direction */
		mi_light_info(light, &orgp, &dirp, 0);
		/* Now orgp or dirp is different from the null vector */

		/* Compute direction from light to point */
		if (mi_vector_dot(&orgp, &orgp) > miEPS) /* point light */
			mi_vector_sub(&dir, &info_near->point, &orgp);

		else {				/* directional light source */
			miASSERT(mi_vector_dot(&dirp, &dirp) > miEPS);
			dir = dirp;
		}
		mi_vector_normalize(&dir);

		/* The contour gets wider the more the normal is pointing in
		   the same direction as the light source */
		d = mi_vector_dot(&dir, &info_near->normal);
		result->width *= 0.5 * (d + 1.0) * (1.0 - light_min_factor) +
						light_min_factor;
	}
	miASSERT(result->width <= *mi_eval_scalar(&paras->near_width));
	return(miTRUE);
}
コード例 #9
0
ファイル: contourshade.c プロジェクト: OpenXRay/xray
miBoolean contour_contrast_function_levels(
	miStdInfo	*info1,
	miStdInfo	*info2,
	int		level,
	miState		*state,
	Contour_Contrast_Parameters_Levels *paras)
{

	/*
	 * No contour if level too near or too deep
	 */
	if (level < *mi_eval_integer(&paras->min_level) || 
			*mi_eval_integer(&paras->max_level) < level)
		return(miFALSE);

	/*
	 * Contour if one ray intersected an object and one ray hit background
	 */
	miASSERT(info1 || info2);
	if ((info1 == NULL) != (info2 == NULL))
		return(miTRUE);

	miASSERT(info1 && info2);

	/*
	 * Contour if sufficiently large difference in depth
	 */
	if (fabs(info1->point.z - info2->point.z) > 
				*mi_eval_scalar(&paras->zdelta))
		return(miTRUE);

	/*
	 * Contour if sufficiently large change in normal
	 */
	if (mi_vector_dot(&info1->normal, &info2->normal) <
			cos(*mi_eval_scalar(&paras->ndelta) * M_PI/180.0))
		return(miTRUE);

	/*
	 * Contour if different materials (if specified)
	 */
	if (*mi_eval_boolean(&paras->diff_mat)
				&& info1->material != info2->material)
		return(miTRUE);

	/*
	 * Contour if different object labels (if specified)
	 */
	if (*mi_eval_boolean(&paras->diff_label) 
				&& info1->label != info2->label)
		return(miTRUE);

	/*
	 * Contour if different triangle indices (if specified)
	 */
	if (*mi_eval_boolean(&paras->diff_index) && 
				(info1->index != info2->index ||
				  mi_vector_dot(&info1->normal_geom,
						&info2->normal_geom) < 0.9999))
		return(miTRUE);

	/*
	 * Contour if color contrast (if specified) --- doesn't work properly
	 * on objects behind semitransparent objects since there can be a
	 * contrast on the semistransparent object caused by the color
	 * difference behind it (as a result, it looks like the contrast
	 * shader of the semitransparent object is used on the object behind
	 * even though it isn't).
	 */
	if (*mi_eval_boolean(&paras->contrast) &&
	   (fabs(info1->color.r-info2->color.r) > state->options->contrast.r ||
	    fabs(info1->color.g-info2->color.g) > state->options->contrast.g ||
	    fabs(info1->color.b-info2->color.b) > state->options->contrast.b))
		return(miTRUE);

	/*
	 * No contour otherwise
	 */
	return(miFALSE);
}
コード例 #10
0
ファイル: Facade.c プロジェクト: qarl/lumetools
miBoolean Facade_tex_coord(miState *state, miScalar size, 
			       miBoolean cylindrical,
			       miVector *tex_point)
{
  miVector facing, facade_center, observer_center;
  miVector intersection;
  miVector fx, fy, fz;

  /* first - determine the center of the facade,
             the center of the observer, 
             and the direction of the facade */
  {

    mi_point_from_object(state, &facade_center, &zero);

    observer_center = state->org;

    mi_vector_sub(&facing, &observer_center, &facade_center);

    if (cylindrical)
      {
	miVector fix;

	mi_vector_to_object(state, &fix, &facing);
	fix.y = 0;
	mi_vector_from_object(state, &facing, &fix);
      }

    mi_vector_normalize(&facing);
  }



  /* second - compute the intersection of the ray and the facade */

  {
    miScalar l;

    l = lume_plane_ray_intersection(&intersection, 
				       &facade_center, &facing,
				       &state->point, &state->dir);



    if (l <= 0)  /* no intersection */
      return miFALSE;

  }



  /* third - build a coordinate system for the texture */
  {
    miVector object_up;

    fz = facing;

    mi_vector_from_object(state, &object_up, &up);
    mi_vector_prod(&fx, &object_up, &fz);
    mi_vector_normalize(&fx);
 
    mi_vector_prod(&fy, &fz, &fx);
    mi_vector_normalize(&fy);
 
  }


  
  /* forth - convert the intersection to the texture coordinate system */
  {
    mi_vector_sub(&intersection, &intersection, &facade_center);

    tex_point->x = mi_vector_dot(&intersection, &fx);
    tex_point->y = mi_vector_dot(&intersection, &fy);
    tex_point->z = mi_vector_dot(&intersection, &fz);

    mi_vector_div(tex_point, size);

    tex_point->x += 0.5;
    tex_point->y += 0.5;
  }
 


  
  return miTRUE;
}
コード例 #11
0
ファイル: physlight.cpp プロジェクト: OpenXRay/xray
extern "C" DLLEXPORT miBoolean physical_light(
	miColor			*result,
	miState			*state,
	struct physical_light	*parms)
{
	struct physical_light	p, *paras = &p;	/* mi_eval'ed parameters */
	miTag			light = 0;
	miColor			visibility = {1.0, 1.0, 1.0};
	miVector		normal, u, v, axis, spotdir, dir;
	miScalar		cosine, r=state->dist, r2, f, d, area, radius;
	miScalar		exponent, spread;
	miScalar		maxcolor;
	int			type, areatype;

	p.color     = *mi_eval_color (&parms->color);
	p.cone      = *mi_eval_scalar(&parms->cone);
	p.threshold = *mi_eval_scalar(&parms->threshold);
	p.cos_exp   = *mi_eval_scalar(&parms->cos_exp);

	miASSERT(paras->color.r >= 0.0 && paras->color.g >= 0.0 &&
		 paras->color.b >= 0.0);   /* no 'light suckers', please */

	mi_query(miQ_INST_ITEM,  state, state->light_instance,	&light);
	mi_query(miQ_LIGHT_TYPE, state, light,			&type);
	mi_query(miQ_LIGHT_AREA, state, light,			&areatype);

	if (state->type == miRAY_LIGHT) {
		/*
		 * compute irradiance from this light source at the surface of
		 * object
		 */
		mi_query(miQ_LIGHT_EXPONENT, state, light, &exponent);
		r2 = exponent == 0 || exponent == 2 ? r*r : pow(r, exponent);

		switch(areatype) {
		  case miLIGHT_NONE:		/* point, spot or directional*/
			/*
			 * distance attenuation: 4 Pi r^2 is the area of a
			 * sphere around the point. Same normalization as for
			 * spherical light
			 */
			f = type==miLIGHT_DIRECTION ? 1 : 1 / (4 * M_PI * r2);
			break;

		  case miLIGHT_RECTANGLE:	/* rectangular area light */
			mi_query(miQ_LIGHT_AREA_R_EDGE_U, state, light, &u);
			mi_query(miQ_LIGHT_AREA_R_EDGE_V, state, light, &v);
			mi_vector_prod(&normal, &u, &v);
			mi_vector_normalize(&normal);
			mi_vector_to_light(state, &dir, &state->dir);
			mi_vector_normalize(&dir);
			/*
			 * Compute area-to-point form factor (except cos at
			 * the receiver). <cosine> is cos at sender. Returning
			 * 2 means "no color and stop sampling".
			 */
			cosine = mi_vector_dot(&normal, &dir);
			if (cosine <= 0)
				return((miBoolean)2);
			if (paras->cos_exp != 0 && paras->cos_exp != 1)
				cosine = pow(cosine, paras->cos_exp);
			/*
			 * cos term and distance attenuation. No area term
			 * since "color" of the light is energy, not radiance.
			 */
			f = cosine / (M_PI * r2);
			break;

		  case miLIGHT_DISC:		/* disc area light */
			mi_query(miQ_LIGHT_AREA_D_NORMAL, state,light,&normal);
			mi_vector_to_light(state, &dir, &state->dir);
			mi_vector_normalize(&dir);

			cosine = mi_vector_dot(&normal, &dir);
			if (cosine <= 0)
				return((miBoolean)2);
			if (paras->cos_exp != 0 && paras->cos_exp != 1)
				cosine = pow(cosine, paras->cos_exp);
			f = cosine / (M_PI * r2);
			break;

		  case miLIGHT_SPHERE:		/* spherical area light */
		  case miLIGHT_CYLINDER:	/* cylindrical area light */
			f = 1 / (4 * M_PI * r2);
			break;

		  case miLIGHT_OBJECT:
		        /* two sided */
			/*
			 * this is legal for object lights only: state->child
			 * is set according to the intersection with the light
			 * geometry itself
			 */
		        cosine = -state->child->dot_nd;
			if (cosine <= 0)
				return(miFALSE);
			if (paras->cos_exp != 0 && paras->cos_exp != 1)
				cosine = pow(cosine, paras->cos_exp);
		        f = cosine / (4 * M_PI * r2);
			break;

		  default:
			mi_error("physical_light: unknown light source type");
		}
		if (type == miLIGHT_SPOT) {	/* spot light source */
			mi_query(miQ_LIGHT_DIRECTION, state, light, &spotdir);
			miASSERT(fabs(mi_vector_norm(&spotdir) - 1.) < miEPS);
			mi_vector_to_light(state, &dir, &state->dir);
			mi_vector_normalize(&dir);
			d = mi_vector_dot(&dir, &spotdir);
			if (d <= 0)
				return(miFALSE);
			mi_query(miQ_LIGHT_SPREAD, state, light, &spread);
			if (d < spread)
				return(miFALSE);
			if (d < paras->cone)
				f *= 1 - (d - paras->cone) /
					 (spread - paras->cone);
		}

		/*
		 * Return false without checking occlusion (shadow ray) if
		 * color is very dark. (This introduces bias which could be
		 * avoided if probabilities were used to decide whether to
		 * carry on or return here.)
		 */
		maxcolor = mi_MAX3(paras->color.r, paras->color.g,
							paras->color.b);
		if (f * maxcolor < paras->threshold)
			return(miFALSE);

		/*
		 * Check for occlusion by an opaque object and compute
		 * visibility
		 */
		if (!mi_trace_shadow(&visibility, state) || BLACK(visibility))
			return(miFALSE);

		/*
		 * Compute result. Visibility is always (1 1 1) here for
		 * dgs_material() so the multiplication by visibility could be
		 * avoided. But for base light shaders visibility can be less.
		 */
		result->r = f * paras->color.r * visibility.r;
		result->g = f * paras->color.g * visibility.g;
		result->b = f * paras->color.b * visibility.b;
	} else {		/* Visible area light source: return radiance*/
		switch (areatype) {
		  case miLIGHT_RECTANGLE:	/* rectangular area light */
			mi_query(miQ_LIGHT_AREA_R_EDGE_U, state, light, &u);
			mi_query(miQ_LIGHT_AREA_R_EDGE_V, state, light, &v);
			mi_vector_prod(&normal, &u, &v);
			/* Compute radiance: result = paras->color / area */
			mi_normal_to_light(state, &dir, &state->normal);
			if (mi_vector_dot(&normal, &dir) > 0) {
				area = 4.0 * mi_vector_norm(&normal);
				miASSERT(area > 0.0);
				result->r = paras->color.r / area;
				result->g = paras->color.g / area;
				result->b = paras->color.b / area;
			} else
				*result = black;   /* back side is black */
			break;

		  case miLIGHT_DISC:		/* disc area light source */
			mi_query(miQ_LIGHT_AREA_D_NORMAL, state,light,&normal);
			mi_normal_to_light(state, &dir, &state->normal);
			if (mi_vector_dot(&normal, &dir) > 0) {
				mi_query(miQ_LIGHT_AREA_D_RADIUS, state,
							light, &radius);
				area = M_PI * radius * radius;
				miASSERT(area > 0.0);
				result->r = paras->color.r / area;
				result->g = paras->color.g / area;
				result->b = paras->color.b / area;
			} else
				*result = black;
			break;

		  case miLIGHT_SPHERE:		/* spherical area light */
			mi_query(miQ_LIGHT_AREA_S_RADIUS, state,light,&radius);
			area = 4.0 * M_PI * radius * radius;
			miASSERT(area > 0.0);
			result->r = paras->color.r / area;
			result->g = paras->color.g / area;
			result->b = paras->color.b / area;
			break;

		  case miLIGHT_CYLINDER:	/* cylindrical area light */
			mi_query(miQ_LIGHT_AREA_C_RADIUS, state,light,&radius);
			mi_query(miQ_LIGHT_AREA_C_AXIS, state, light, &axis);
			/* area = pi*radius^2 * h = pi*radius^2 * 2 * |axis| */
			area = 2 * M_PI * radius * radius *
						mi_vector_norm(&axis);
			miASSERT(area > 0.0);
			result->r = paras->color.r / area;
			result->g = paras->color.g / area;
			result->b = paras->color.b / area;
			break;

		  case miLIGHT_OBJECT:
		        mi_query(miQ_INST_AREA, state,
				 state->light_instance, &area);
			result->r = paras->color.r / area;
			result->g = paras->color.g / area;
			result->b = paras->color.b / area;
			break;

		  case miLIGHT_NONE:		/* point, spot or directional*/
			miASSERT(0);
			break;

		  default:
			mi_error("physical_light: unknown light source type");
		}
	}
	miASSERT(result->r >= 0 && result->g >= 0 && result->b >= 0);
	return(miTRUE);
}
コード例 #12
0
ファイル: baseocc.cpp プロジェクト: OpenXRay/xray
extern "C" DLLEXPORT miBoolean mib_amb_occlusion(
	miColor     *result,
	miState     *state,
	struct mib_amb_occlusion_p *paras)
{
	double sample[3], near_clip, far_clip;
	int	  counter  = 0;
	miUint	  samples  = *mi_eval_integer(&paras->samples);
	miScalar  clipdist = *mi_eval_scalar(&paras->max_distance);
	miBoolean reflecto = *mi_eval_boolean(&paras->reflective);
	miBoolean ret_type = *mi_eval_integer(&paras->return_type);

	miTag	   org_env = state->environment; /* Original environ. */

	miVector orig_normal, trace_dir;
	miScalar output      = 0.0, 
		 samplesdone = 0.0;
	miScalar spread     = *mi_eval_scalar(&paras->spread);
	miScalar o_m_spread = 1.0f - spread;
	
	miColor   env_total;	/* environment Total */ 	
	miVector  norm_total;	/* Used for adding up normals */
	miBoolean occ_alpha = *mi_eval_boolean(&paras->occlusion_in_alpha);

        miScalar  falloff   = 1.0;
        miao_trace_info ti;
        int       version = 1;

	/* If called as user area light source, return "no more samples" for	
	   any call beyond the first */
	if (state->type == miRAY_LIGHT && state->count > 0) 
		return (miBoolean)2;

        /* Figure out the call version */
        mi_query(miQ_DECL_VERSION, 0, state->shader->function_decl, &version); 
        if (version >= 2)
        {
            falloff = *mi_eval_scalar(&paras->falloff);
            if (falloff <= 0.0) 
                falloff = 1.0;
            ti.id_inclexcl   = *mi_eval_integer(&paras->id_includeexclude);
            ti.id_nonself    = *mi_eval_integer(&paras->id_nonself);

            /* None of these options used, go into compatible mode */
            ti.compatible = (ti.id_inclexcl   == 0 && 
                             ti.id_nonself    == 0 
                             );
        }
        else
        {
            ti.compatible = miTRUE;
        }

	/* Used for adding up environment */
	env_total.r = env_total.g = env_total.b = env_total.a = 0;

	far_clip = near_clip = clipdist;

	orig_normal  = state->normal;
	norm_total   = state->normal; /* Begin by standard normal */

	/* Displacement? Shadow? Makes no sense */
	if (state->type == miRAY_DISPLACE ||
            state->type == miRAY_SHADOW) {
		result->r = result->g = result->b = result->a = 0.0;
		return (miTRUE); 
	}

	if (clipdist > 0.0) 
		mi_ray_falloff(state, &near_clip, &far_clip);

	/* Avoid recursion: If we are designated as environment shader,
	   we will be called with rays of type miRAY_ENVIRON, and if so
	   we switch the environment to the global environment */

	if (state->type == miRAY_ENVIRONMENT)
	    state->environment = state->camera->environment;

	while (mi_sample(sample, &counter, state, 2, &samples)) {
		mi_reflection_dir_diffuse_x(&trace_dir, state, sample);
		trace_dir.x = orig_normal.x*o_m_spread + trace_dir.x*spread;
		trace_dir.y = orig_normal.y*o_m_spread + trace_dir.y*spread;
		trace_dir.z = orig_normal.z*o_m_spread + trace_dir.z*spread;
	
		mi_vector_normalize(&trace_dir);

		if (reflecto) {
			miVector ref;
			miScalar nd = state->dot_nd;
			/* Calculate the reflection direction */
			state->normal = trace_dir;
			state->dot_nd = mi_vector_dot(
						&state->dir,
						&state->normal);
			/* Bugfix: mi_reflection_dir(&ref, state);
			   for some reason gives me the wrong result,
			   doing it "manually" works better */
			ref    = state->dir;
			ref.x -= state->normal.x*state->dot_nd*2.0f;
			ref.y -= state->normal.y*state->dot_nd*2.0f;
			ref.z -= state->normal.z*state->dot_nd*2.0f;
			state->normal = orig_normal;
			state->dot_nd = nd;
			trace_dir = ref;
		}

		if (mi_vector_dot(&trace_dir, &state->normal_geom) < 0.0) 
			continue;

		output	    += 1.0; /* Add one */
		samplesdone += 1.0;

		if (state->options->shadow &&
		    miao_trace_the_ray(state, &trace_dir, &state->point, &ti)) {
			/* we hit something */
    
			if (clipdist == 0.0) 
				output -= 1.0;
			else if (state->child->dist < clipdist) {
				miScalar f = pow(state->child->dist / clipdist,
                                                 (double) falloff);

				output -= (1.0 - f);

				norm_total.x += trace_dir.x * f;
				norm_total.y += trace_dir.y * f;
				norm_total.z += trace_dir.z * f;

				switch (ret_type) {
					case 1: 
					  { 
					    /* Environment sampling */
					    miColor envsample;
					    mi_trace_environment(&envsample, 
						state, &trace_dir);

					    env_total.r += envsample.r * f;
					    env_total.g += envsample.g * f;
					    env_total.b += envsample.b * f;
					  }
					  break;
					
					default: 
					  /* Most return types need no 
						special stuff */
					  break;
				}
			}
		}
		else {
			/* We hit nothing */
			norm_total.x += trace_dir.x;
			norm_total.y += trace_dir.y;
			norm_total.z += trace_dir.z;

			switch (ret_type) {
				case 1: /* Environment sampling */
					{
					   miColor envsample;

					   mi_trace_environment(&envsample, 
						   state, &trace_dir);

					   env_total.r += envsample.r;
					   env_total.g += envsample.g;
					   env_total.b += envsample.b;
					}
					break;
				default:
					/* Most return types need no 
						special treatment */
					break; 
			}
		}
	}

	if (clipdist > 0.0) 
		mi_ray_falloff(state, &near_clip, &far_clip);
    
	if (samplesdone <= 0.0) /* No samples? */
		samplesdone = 1.0;  /* 1.0 to not to break divisons below */

	switch (ret_type) {
		case -1:  /* Plain old occlusion with untouched normal*/
		case 0:   /* Plain old occlusion */
		default:  /* (also the default for out-of-bounds values) */
		{
			miVector old_dir = state->dir;
			output /= (miScalar) samplesdone;

			if (ret_type == -1)
				norm_total = state->normal;
			else {
				mi_vector_normalize(&norm_total);

				/* If the color shaders use the normal....
					give them the bent one... */
				state->normal = norm_total;  
				state->dir    = norm_total;
			}
			if (output == 0.0)
				*result = *mi_eval_color(&paras->dark);
			else if (output >= 1.0)
				*result = *mi_eval_color(&paras->bright);
			else {
				miColor bright, dark;
				bright = *mi_eval_color(&paras->bright);
				dark   = *mi_eval_color(&paras->dark);

				result->r = bright.r * output + dark.r * 
					(1.0 - output);
				result->g = bright.g * output + dark.g * 
					(1.0 - output);
				result->b = bright.b * output + dark.b * 
					(1.0 - output);

				if (occ_alpha)
					result->a = output;
				else
					result->a = bright.a * output 
						   + dark.a * (1.0 - output);
			}

			state->normal = orig_normal;
			state->dir    = old_dir;
		}
		break;
		case 1: /* Sampled environment */
		{
			miColor br	= *mi_eval_color(&paras->bright),
				drk	= *mi_eval_color(&paras->dark);

			result->r = drk.r + (br.r * env_total.r / samplesdone);
			result->g = drk.g + (br.g * env_total.g / samplesdone);
			result->b = drk.b + (br.b * env_total.b / samplesdone);
			if (occ_alpha)
				result->a = output/ samplesdone;
			else
				result->a = 1.0;
		}
		break;
		case 2: /* Bent normals, world */
		case 3: /* Bent normals, camera */
		case 4: /* Bent normals, object */
		{
			miVector retn; /* returned Normal */

			mi_vector_normalize(&norm_total);

			if (ret_type == 2)
				mi_normal_to_world(state, &retn, &norm_total);
			if (ret_type == 3)
				mi_normal_to_camera(state, &retn, &norm_total);
			if (ret_type == 4)
				mi_normal_to_object(state, &retn, &norm_total);

			result->r = (retn.x + 1.0) / 2.0;
			result->g = (retn.y + 1.0) / 2.0;
			result->b = (retn.z + 1.0) / 2.0;
		
			if (occ_alpha)
				result->a = output/ samplesdone;
			else
				result->a = 1.0;
		}
		break;
	}

	if (state->type == miRAY_LIGHT) {
		 /* Are we a light shader? */
		int type;
		mi_query(miQ_FUNC_CALLTYPE, state, 0, &type);

		/* Make sure we are called as light shader */
		if (type == miSHADER_LIGHT) {
			/* If so, move ourselves to above the point... */
			state->org.x = state->point.x + state->normal.x;
			state->org.y = state->point.y + state->normal.y;
			state->org.z = state->point.z + state->normal.z;
			/* ...and set dot_nd to 1.0 to illuminate fully */
			state->dot_nd = 1.0;
		}
	}
	/* Reset environment, if we changed it */
	state->environment = org_env;
	return miTRUE;
}