/****************************************************************************** * Copyright 1986-2004 by mental images GmbH, Fasanenstr. 81, D-10623 Berlin, * Germany. All rights reserved. ****************************************************************************** * Created: 13.2.98 * Module: baseshader * Purpose: base shaders for Phenomenon writers * * Exports: * mib_light_point * mib_light_point_version * mib_light_spot * mib_light_spot_version * mib_light_infinite * mib_light_infinite_version * mib_light_photometric * mib_light_photometric_version * * History: * * Description: *****************************************************************************/ #include #include #include #define EPS 1e-4 #define BLACK(C) ((C).r==0 && (C).g==0 && (C).b==0) /* * Point light source. Must have an origin in the input file. Ignores the * direction. If this light shader is called because the light source is a * visible area light source and was hit, the result is simply paras->color * (the color of the light source). */ struct mib_light_point { miColor color; /* color of light source */ miBoolean shadow; /* light casts shadows */ miScalar factor; /* makes opaque objects transparent */ miBoolean atten; /* distance attenuation */ miScalar start, stop; /* if atten, distance range */ }; DLLEXPORT int mib_light_point_version(void) {return(1);} DLLEXPORT miBoolean mib_light_point( register miColor *result, register miState *state, register struct mib_light_point *paras) { register miScalar d, t, start, stop; *result = *mi_eval_color(¶s->color); if (state->type != miRAY_LIGHT) /* visible area light*/ return(miTRUE); if (*mi_eval_boolean(¶s->atten)) { /* dist atten*/ stop = *mi_eval_scalar(¶s->stop); if (state->dist >= stop) return(miFALSE); start = *mi_eval_scalar(¶s->start); if (state->dist > start && fabs(stop - start) > EPS) { t = 1 - (state->dist - start) / (stop - start); result->r *= t; result->g *= t; result->b *= t; } } if (*mi_eval_boolean(¶s->shadow)) { /* shadows: */ d = *mi_eval_scalar(¶s->factor); if (d < 1) { miColor filter; filter.r = filter.g = filter.b = filter.a = 1; /* opaque */ if (!mi_trace_shadow(&filter,state) || BLACK(filter)) { result->r *= d; result->g *= d; result->b *= d; if (d == 0) return(miFALSE); } else { /* transparnt*/ double omf = 1 - d; result->r *= d + omf * filter.r; result->g *= d + omf * filter.g; result->b *= d + omf * filter.b; } } } return(miTRUE); } /* * Spot light source. Must have an origin and direction in the input file. * Takes a variable number of parameters. The first is a boolean which says * whether the light casts a shadow or not. The second is the shadow factor. */ struct mib_light_spot { miColor color; /* color of light source */ miBoolean shadow; /* light casts shadows */ miScalar factor; /* makes opaque objects transparent */ miBoolean atten; /* distance attenuation */ miScalar start, stop; /* if atten, distance range */ miScalar cone; /* inner solid cone */ }; DLLEXPORT int mib_light_spot_version(void) {return(1);} DLLEXPORT miBoolean mib_light_spot( register miColor *result, register miState *state, register struct mib_light_spot *paras) { register miScalar d, t, start, stop, cone; miScalar spread; miVector ldir, dir; miTag ltag; *result = *mi_eval_color(¶s->color); if (state->type != miRAY_LIGHT) /* visible area light*/ return(miTRUE); /*angle atten*/ ltag = ((miInstance *)mi_db_access(state->light_instance))->item; mi_db_unpin(state->light_instance); mi_query(miQ_LIGHT_DIRECTION, state, ltag, &ldir); mi_vector_to_light(state, &dir, &state->dir); mi_vector_normalize(&dir); d = mi_vector_dot(&dir, &ldir); if (d <= 0) return(miFALSE); mi_query(miQ_LIGHT_SPREAD, state, ltag, &spread); if (d < spread) return(miFALSE); cone = *mi_eval_scalar(¶s->cone); if (d < cone) { t = 1 - (d - cone) / (spread - cone); result->r *= t; result->g *= t; result->b *= t; } if (*mi_eval_boolean(¶s->atten)) { /* dist atten*/ stop = *mi_eval_scalar(¶s->stop); if (state->dist >= stop) return(miFALSE); start = *mi_eval_scalar(¶s->start); if (state->dist > start && fabs(stop - start) > EPS) { t = 1 - (state->dist - start) / (stop - start); result->r *= t; result->g *= t; result->b *= t; } } if (*mi_eval_boolean(¶s->shadow)) { /* shadows: */ d = *mi_eval_scalar(¶s->factor); if (d < 1) { miColor filter; filter.r = filter.g = filter.b = filter.a = 1; /* opaque */ if (!mi_trace_shadow(&filter,state) || BLACK(filter)) { result->r *= d; result->g *= d; result->b *= d; if (d == 0) return(miFALSE); } else { /* transparnt*/ double omf = 1 - d; result->r *= d + omf * filter.r; result->g *= d + omf * filter.g; result->b *= d + omf * filter.b; } } } return(miTRUE); } /* * Infinite light source. Must have a direction in the input file. * Takes two parameters. The first is a boolean which says whether the * light casts a shadow or not. The second is the shadow factor. */ struct mib_light_infinite { miColor color; /* color of light source */ miBoolean shadow; /* light casts shadows */ miScalar factor; /* makes opaque objects transparent */ }; DLLEXPORT int mib_light_infinite_version(void) {return(1);} DLLEXPORT miBoolean mib_light_infinite( register miColor *result, register miState *state, register struct mib_light_infinite *paras) { register miScalar d; *result = *mi_eval_color(¶s->color); if (state->type != miRAY_LIGHT) /* visible area light*/ return(miFALSE); /* shadows: */ d = *mi_eval_scalar(¶s->factor); if (*mi_eval_boolean(¶s->shadow) && d < 1) { miColor filter; filter.r = filter.g = filter.b = filter.a = 1; /* opaque */ if (!mi_trace_shadow(&filter,state) || BLACK(filter)) { result->r *= d; result->g *= d; result->b *= d; if (d == 0) return(miFALSE); } else { /* transparnt*/ double omf = 1 - d; result->r *= d + omf * filter.r; result->g *= d + omf * filter.g; result->b *= d + omf * filter.b; } } return(miTRUE); } /* * A simple photometric light source. * Behaves like a point light source. The light distribution is * given by the light profile. The color argument is the brightest * color in the profile. start and stop allow to limit the * attenuation given by the light exponent to a distance interval. */ typedef struct { miColor color; /* color of light source */ miBoolean shadow; /* light casts shadows? */ miScalar factor; /* makes opaque objects transparent */ miScalar start; /* start attenuation */ miScalar stop; /* end attenuation */ miTag profile; /* light profile to use */ } simple_photometric_light_t; DLLEXPORT int mib_light_photometric_version(void) { return 1; } DLLEXPORT miBoolean mib_light_photometric( miColor *result, miState *state, simple_photometric_light_t *paras) { miScalar intensity = 1.0f; miTag lp_tag = *mi_eval_tag(¶s->profile); /* light profile tag */ miScalar d; miTag ltag = 0; /* light tag */ miScalar exponent; /* light decay */ miScalar sa = *mi_eval_scalar(¶s->start); miScalar ea = *mi_eval_scalar(¶s->stop); miBoolean atten = (miBoolean) (ea > sa && ea > 0); *result = *mi_eval_color(¶s->color); if (state->type != miRAY_LIGHT) return(miTRUE); mi_query(miQ_INST_ITEM, state, state->light_instance, <ag); mi_query(miQ_LIGHT_EXPONENT, state, ltag, &exponent); if(exponent > 0.0f) { if (!atten || (atten && sadist && state->distdist*state->dist); } else if (exponent == 1.0f) { intensity = 1.0/state->dist; } else { intensity = 1.0f/(miScalar)pow( (double)state->dist, (double)exponent); } } } #ifdef RAY31 /* needs at least mental ray 3.1, otherwise do a dumb point light */ /* note that dir.y is cos(Theta) */ if(lp_tag) { intensity *= mi_lightprofile_sample(state, lp_tag, miTRUE); } #endif result->r *= intensity; result->g *= intensity; result->b *= intensity; /* shadows: */ d = *mi_eval_scalar(¶s->factor); if (*mi_eval_boolean(¶s->shadow) && d < 1) { miColor filter; filter.r = filter.g = filter.b = filter.a = 1; /* opaque */ if (!mi_trace_shadow(&filter,state) || BLACK(filter)) { result->r *= d; result->g *= d; result->b *= d; if (d == 0) return(miFALSE); } else { /* transparent*/ double omf = 1 - d; result->r *= d + omf * filter.r; result->g *= d + omf * filter.g; result->b *= d + omf * filter.b; } } return(miTRUE); }