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(¶ms->name);
p->count = *mi_eval_integer(¶ms->count);
p->root_radius = *mi_eval_scalar(¶ms->root_radius);
p->tip_radius = *mi_eval_scalar(¶ms->tip_radius);
p->center = *mi_eval_vector(¶ms->center);
p->length_min = *mi_eval_scalar(¶ms->length_min);
p->length_max = *mi_eval_scalar(¶ms->length_max);
p->spiral_turns_min = *mi_eval_scalar(¶ms->spiral_turns_min);
p->spiral_turns_max = *mi_eval_scalar(¶ms->spiral_turns_max);
p->spiral_radius_min = *mi_eval_scalar(¶ms->spiral_radius_min);
p->spiral_radius_max = *mi_eval_scalar(¶ms->spiral_radius_max);
p->segment_length = *mi_eval_scalar(¶ms->segment_length);
p->random_seed = *mi_eval_integer(¶ms->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