Shader hair_geo_curl

Click on the filename to display or download the file.

hair_geo_curl.mi
declare shader 
    geometry "hair_geo_curl" ( 
	string "name", 
        integer "count" default 1, 
	scalar "root_radius" default .01, 
	scalar "tip_radius" default .0001, 
	vector "center" default 0 0 0, 
	scalar "length_min" default .5, 
	scalar "length_max" default 1, 
	scalar "spiral_turns_min" default 2, 
	scalar "spiral_turns_max" default 3, 
	scalar "spiral_radius_min" default .01, 
	scalar "spiral_radius_max" default .03, 
	scalar "segment_length" default .05, 
	integer "random_seed" default 1955 ) 
    version 1 
    apply material 
end declare 

hair_geo_curl.c
#include "shader.h" 
#include "geoshader.h" 
#include "miaux.h" 
 
typedef struct { 
    miTag name; 
    miInteger count; 
    miScalar root_radius; 
    miScalar tip_radius; 
    miVector center; 
    miScalar length_min; 
    miScalar length_max; 
    miScalar spiral_turns_min; 
    miScalar spiral_turns_max; 
    miScalar spiral_radius_min; 
    miScalar spiral_radius_max; 
    miScalar segment_length; 
    miInteger random_seed; 
} hair_geo_curl_t; 
 
DLLEXPORT 
int hair_geo_curl_version(void) { return 1; } 
 
void hair_geo_curl_bbox(miObject *obj, void* params) 
{ 
   hair_geo_curl_t *p = (hair_geo_curl_t*)params; 
   obj->bbox_min.x = p->center.x - p->length_max; 
   obj->bbox_min.y = p->center.y - p->length_max; 
   obj->bbox_min.z = p->center.z - p->length_max; 
   obj->bbox_max.x = p->center.x + p->length_max; 
   obj->bbox_max.y = p->center.y + p->length_max; 
   obj->bbox_max.z = p->center.z + p->length_max; 
} 
 
miBoolean hair_geo_curl_callback(miTag tag, void *ptr); 
 
DLLEXPORT 
miBoolean hair_geo_curl ( 
    miTag *result, miState *state, hair_geo_curl_t *params  ) 
{ 
    hair_geo_curl_t *p =  
        (hair_geo_curl_t*)mi_mem_allocate(sizeof(hair_geo_curl_t)); 
    p->name              = *mi_eval_tag(&params->name); 
    p->count             = *mi_eval_integer(&params->count); 
    p->root_radius       = *mi_eval_scalar(&params->root_radius); 
    p->tip_radius        = *mi_eval_scalar(&params->tip_radius); 
    p->center            = *mi_eval_vector(&params->center); 
    p->length_min        = *mi_eval_scalar(&params->length_min); 
    p->length_max        = *mi_eval_scalar(&params->length_max); 
    p->spiral_turns_min  = *mi_eval_scalar(&params->spiral_turns_min); 
    p->spiral_turns_max  = *mi_eval_scalar(&params->spiral_turns_max); 
    p->spiral_radius_min = *mi_eval_scalar(&params->spiral_radius_min); 
    p->spiral_radius_max = *mi_eval_scalar(&params->spiral_radius_max); 
    p->segment_length    = *mi_eval_scalar(&params->segment_length); 
    p->random_seed       = *mi_eval_integer(&params->random_seed); 
 
    miaux_define_hair_object( 
        p->name, hair_geo_curl_bbox, p, result, hair_geo_curl_callback); 
 
    return miTRUE; 
} 
 
typedef struct { 
    miVector hair_end; 
    int vertex_count; 
    miScalar turns; 
    miScalar spiral_radius; 
} hair_spec_t;     
 
 
int create_hair_specifications( 
    hair_spec_t **hair_specs, hair_geo_curl_t *p) 
{ 
    float pi_2 = M_PI * 2.0; 
    int scalars_per_vertex = 4, total_scalar_count = 0, i; 
    *hair_specs =  
        (hair_spec_t*)mi_mem_allocate(p->count * sizeof(hair_spec_t)); 
    for (i = 0; i < p->count; i++) { 
        miScalar distance, length; 
        hair_spec_t *spec = &((*hair_specs)[i]); 
        spec->turns =  
            miaux_random_range(p->spiral_turns_min, p->spiral_turns_max); 
        spec->spiral_radius =  
            miaux_random_range(p->spiral_radius_min, p->spiral_radius_max); 
        distance = miaux_random_range(p->length_min, p->length_max); 
        length = distance + pi_2 * spec->spiral_radius * spec->turns; 
        miaux_random_point_on_sphere( 
            &(spec->hair_end), &p->center, distance); 
        spec->vertex_count = (int)(ceil(length / p->segment_length)); 
        total_scalar_count += spec->vertex_count * scalars_per_vertex; 
    } 
    return total_scalar_count; 
} 
 
miBoolean hair_geo_curl_callback(miTag tag, void *ptr) 
{ 
    miHair_list *hair; 
    miGeoIndex  *harray; 
    hair_geo_curl_t *p = (hair_geo_curl_t*)ptr; 
    hair_spec_t *hair_specs; 
    int i, total_scalar_count, hair_array_position; 
    miScalar *hair_scalars; 
 
    mi_srandom(p->random_seed); 
    mi_api_incremental(miTRUE); 
    miaux_define_hair_object(p->name, hair_geo_curl_bbox, p, NULL, NULL); 
    hair = mi_api_hair_begin(); 
    hair->approx = hair->degree = 1; 
    mi_api_hair_info(1, 'r', 1); 
 
    total_scalar_count = create_hair_specifications(&hair_specs, p); 
    hair_scalars = mi_api_hair_scalars_begin(total_scalar_count); 
 
    for (i = 0; i < p->count; i++) { 
        float angle_offset = miaux_random_range(0, M_PI * 2); 
        miaux_hair_spiral( 
            &hair_scalars, &p->center, &hair_specs[i].hair_end,  
            hair_specs[i].turns, hair_specs[i].vertex_count, angle_offset, 
            hair_specs[i].spiral_radius, p->root_radius, p->tip_radius); 
    } 
    mi_api_hair_scalars_end(total_scalar_count); 
 
    harray = mi_api_hair_hairs_begin(p->count + 1); 
    hair_array_position = 0; 
    for (i = 0; i < p->count + 1; i++) { 
        harray[i] = hair_array_position; 
        if (i < p->count) 
            hair_array_position += hair_specs[i].vertex_count * 4; 
    } 
    mi_api_hair_hairs_end(); 
 
    mi_api_hair_end(); 
    mi_api_object_end(); 
    mi_mem_release(hair_specs); 
 
    return miTRUE; 
} 

hair_geo_curl_util.c
void miaux_define_hair_object( 
    miTag name_tag, miaux_bbox_function bbox_function, void *params, 
    miTag *geoshader_result, miApi_object_callback callback) 
{ 
    miTag tag; 
    miObject *obj; 
    char *name = miaux_tag_to_string(name_tag, "::hair"); 
    obj = mi_api_object_begin(mi_mem_strdup(name)); 
    obj->visible = miTRUE; 
    obj->shadow = obj->reflection = obj->refraction = 3; 
    bbox_function(obj, params); 
    if (geoshader_result != NULL && callback != NULL) { 
	mi_api_object_callback(callback, params); 
	tag = mi_api_object_end(); 
	mi_geoshader_add_result(geoshader_result, tag); 
	obj = (miObject *)mi_scene_edit(tag); 
	obj->geo.placeholder_list.type = miOBJECT_HAIR; 
	mi_scene_edit_end(tag); 
    } 
} 
 
char* miaux_tag_to_string(miTag tag, char *default_value) 
{ 
    char *result = default_value; 
    if (tag != 0) { 
	result = (char*)mi_db_access(tag); 
	mi_db_unpin(tag); 
    } 
    return result; 
} 
 
result->r += miaux_random_range(-red_variance, red_variance); 
    result->r = miaux_clamp(result->r, 0.0, 1.0); 
    result->g += miaux_random_range(-green_variance, green_variance); 
    result->g = miaux_clamp(result->g, 0.0, 1.0); 
    result->b += miaux_random_range(-blue_variance, blue_variance); 
    result->b = miaux_clamp(result->b, 0.0, 1.0); 
} 
 
double miaux_fit( 
    double v, double oldmin, double oldmax, double newmin, double newmax)     
{ 
    return newmin + ((v - oldmin) / (oldmax - oldmin)) * (newmax - newmin); 
} 
 
void miaux_random_point_on_sphere( 
    miVector *result, miVector *center, miScalar radius) 
{ 
    miMatrix transform; 
    result->x = radius; 
    result->y = result->z = 0.0; 
    mi_matrix_rotate(transform, 0, 
		     miaux_random_range(0, M_PI * 2), 
		     miaux_random_range(0, M_PI * 2)); 
    mi_vector_transform(result, result, transform); 
    mi_vector_add(result, result, center); 
} 
 
void miaux_hair_spiral( 
    miScalar** scalar_array, miVector *start_point, miVector *end_point,  
    float turns, int point_count, float angle_offset, float spiral_radius, 
    miScalar root_radius, miScalar tip_radius) 
{ 
    miVector base_point, spiral_axis, spiral_point; 
    miVector axis_point; 
    miMatrix matrix; 
    float angle, pi_2 = 2 * M_PI, max_index = point_count - 1; 
    int i; 
 
    mi_vector_sub(&spiral_axis, end_point, start_point); 
    mi_vector_normalize(&spiral_axis); 
    miaux_perpendicular_point(&axis_point, &spiral_axis, spiral_radius); 
 
    for (i = 0; i < point_count; i++) { 
	float fraction = i / max_index; 
	miaux_point_between(&base_point, start_point, end_point, fraction); 
	angle = angle_offset + fraction * turns * pi_2; 
	mi_matrix_rotate_axis(matrix, &spiral_axis, angle); 
	mi_point_transform(&spiral_point, &axis_point, matrix); 
	mi_vector_add(&spiral_point, &spiral_point, &base_point); 
 
	*(*scalar_array)++ = spiral_point.x; 
	*(*scalar_array)++ = spiral_point.y; 
	*(*scalar_array)++ = spiral_point.z; 
	*(*scalar_array)++ = 
	    miaux_fit_clamp(i, 0, max_index, root_radius, tip_radius); 
    } 
} 
 
void miaux_perpendicular_point(miVector *result, miVector *v, float distance) 
{ 
    result->x = -v->y; 
    result->y = v->x; 
    result->z = 0; 
    mi_vector_normalize(result); 
    mi_vector_mul(result, distance); 
} 
 
void miaux_point_between( 
    miVector *result, miVector *u, miVector *v, float fraction) 
{ 
    result->x = miaux_fit(fraction, 0, 1, u->x, v->x); 
    result->y = miaux_fit(fraction, 0, 1, u->y, v->y); 
    result->z = miaux_fit(fraction, 0, 1, u->z, v->z); 
} 
 
double miaux_fit_clamp( 
    double v, double oldmin, double oldmax, double newmin, double newmax) 
{ 
    if (oldmin > oldmax) { 
	double temp = oldmin; 
        oldmin = oldmax; 
	oldmax = oldmin; 
	temp = newmin; 
        newmin = newmax; 
	newmax = newmin; 
    } 
    if (v < oldmin) 
	return newmin; 
    else if (v > oldmax) 
	return newmax; 
    else  
	return miaux_fit(v, oldmin, oldmax, newmin, newmax); 
} 

22 April 2008 23:40:44