4coder/non-source/test_data/lots_of_files/tree_gen.cpp

527 lines
14 KiB
C++

/*
* Allen's Fancy Tree Generator
* 22.08.2015 (dd.mm.yyyy)
*/
// TOP
internal
split_point(real32 base_radius, real32 branch_radius, i32 branch_n, i32 i){
real32 branch_dist = branch_radius*2.f;
Vec2 result = {};
switch (branch_n){
case 1:
{
if (i == 0) result.x = -base_radius;
else result.x = base_radius;
}break;
case 2:
{
real32 a,b,c;
a = base_radius;
c = branch_dist;
a *= a;
c *= c;
b = c - a;
b = SQRT(b);
switch (i){
case 0: result.x = -base_radius; result.y = b*.5f; break;
case 1: result.x = 0; result.y = -b*.5f; break;
case 2: result.x = base_radius; result.y = b*.5f; break;
}
}break;
case 3:
{
real32 a,b,c,aa,cc;
a = base_radius - branch_dist*.5f;
c = branch_dist;
aa = a*a;
cc = c*c;
b = cc - aa;
b = SQRT(b);
switch (i){
case 0: result.x = -base_radius; result.y = b*.5f; break;
case 1: result.x = a-base_radius; result.y = -b*.5f; break;
case 2: result.x = base_radius-a; result.y = -b*.5f; break;
case 3: result.x = base_radius; result.y = b*.5f; break;
}
}break;
}
return result;
}
struct Hot_Spot{
Vec2 spot;
};
struct Tree_Parameters{
Vec2 base;
i32 levels;
real32 initial_step;
real32 step_decay;
real32 initial_thickness;
real32 thickness_decay;
real32 dtheta;
real32 min_weight_threshold;
real32 center_branch_weight;
real32 heat_half_distance;
Hot_Spot *hot_spots;
i32 hot_spot_count;
};
struct Tree_Skeleton_Vert{
Vec2 pos;
i32 child_count;
i32 children[3];
real32 theta;
real32 width;
Vec4 paint;
};
struct Skeleton_Branch_Point{
i32 vert_id;
real32 step;
real32 theta;
real32 width;
i32 depth;
};
internal Skeleton_Branch_Point
SBP(i32 id, real32 step, real32 theta, real32 width, i32 depth){
Skeleton_Branch_Point result;
result.vert_id = id;
result.step = step;
result.theta = theta;
result.width = width;
result.depth = depth;
return result;
}
struct r32_Range{
real32 start, end;
};
inline r32_Range
get_range(real32 a, real32 b){
r32_Range result;
if (a < b){
result.start = a;
result.end = b;
}
else{
result.start = b;
result.end = a;
}
return result;
}
persist Vec4 brown = {.5f, .3333f, 0.f, 1.f};
persist Vec4 silver = {.7f, .7f, .7f, 1.f};
persist Vec4 faded_gold = {.7f, .4f, .2f, 0.5f};
persist Vec4 sky_blue = {0.f, .7f, 1.f, 1.f};
internal bool32
segments_collide(Vec2 a1, Vec2 a2, Vec2 b1, Vec2 b2, Vec2 *p_out = 0){
bool32 result = 1;
Vec3 r1, r2;
if (a2.y != a1.y){
r1.x = (a2.y - a1.y);
r1.y = (a1.x - a2.x);
r1.z = (a1.x*r1.x + a1.y*r1.y);
r2.x = (b2.y - b1.y);
r2.y = (b1.x - b2.x);
r2.z = (b1.x*r2.x + b1.y*r2.y);
}
else{
r1.x = (b2.y - b1.y);
r1.y = (b1.x - b2.x);
r1.z = (b1.x*r1.x + b1.y*r1.y);
r2.x = (a2.y - a1.y);
r2.y = (a1.x - a2.x);
r2.z = (a1.x*r2.x + a1.y*r2.y);
}
if (r1.x == 0){
result = (a1.y == b1.y) && ((b1.x <= a1.x && a1.x < b2.x) || (a1.x <= b1.x && b1.x < a2.x));
return result;
}
if (r2.x != 0){
r2.y /= r2.x;
r2.z /= r2.x;
r2.y *= r1.x;
r2.z *= r1.x;
r2.y -= r1.y;
r2.z -= r1.z;
r2.x = 0;
}
if (r2.y == 0){
result = (r2.z == 0) && ((b1.y <= a1.y && a1.y < b2.y) || (a1.y <= b1.y && b1.y < a2.y));
return result;
}
if (r1.y != 0){
r1.x /= r1.y;
r1.z /= r1.y;
r1.x *= r2.y;
r1.z *= r2.y;
r1.x -= r2.x;
r1.z -= r2.z;
r1.y = 0;
}
assert(!(r1.x == 0 || r2.y == 0));
r1 *= 1.f / r1.x;
r2 *= 1.f / r2.y;
Vec2 cl;
cl.x = r1.z;
cl.y = r2.z;
if (p_out) *p_out = cl;
real32 epsilon = .0001f;
real32 neg_epsilon = -epsilon;
real32 xdif;
xdif = a1.x - a2.x;
if (xdif < neg_epsilon && xdif > epsilon){
r32_Range range = get_range(a1.x, a2.x);
if (cl.x < range.start || cl.x > range.end){
result = 0;
}
}
else{
r32_Range range = get_range(a1.y, a2.y);
if (cl.y < range.start || cl.y > range.end){
result = 0;
}
}
xdif = a1.x - a2.x;
if (xdif < neg_epsilon && xdif > epsilon){
r32_Range range = get_range(b1.x, b2.x);
if (cl.x < range.start || cl.x > range.end){
result = 0;
}
}
else{
r32_Range range = get_range(b1.y, b2.y);
if (cl.y < range.start || cl.y > range.end){
result = 0;
}
}
return result;
}
internal bool32
no_collisions(Vec2 base, Vec2 new_pos, Tree_Skeleton_Vert *vertices){
bool32 result = 1;
i32 vert_id_stack[16];
i32 top = 0;
vert_id_stack[top++] = 0;
while (top > 0){
Tree_Skeleton_Vert *vert = vertices + vert_id_stack[--top];
i32 count = vert->child_count;
i32 *children = vert->children;
Vec2 c = vert->pos;
for (i32 i = 0; i < count; ++i){
i32 child_id = children[i];
Tree_Skeleton_Vert *other_vert = vertices + child_id;
Vec2 d = other_vert->pos;
bool32 exempt = (base.x == d.x) && (base.y == d.y);
if (!exempt) exempt = (base.x == c.x) && (base.y == c.y);
if (!exempt && segments_collide(base, new_pos, c, d)){
other_vert->paint = sky_blue;
result = 0;
goto no_collisions_end;
}
else{
vert_id_stack[top++] = child_id;
}
}
}
no_collisions_end:
return result;
}
struct Branch_Info{
i32 which_branch;
real32 step;
real32 length;
real32 contribution_list[3];
i32 contribution_count;
};
enum Info_To_Post{
Step_Info,
Length_Info,
Contribution_Info,
Count_Info
};
internal void
post_branch_info(Branch_Info *info, i32 count, i32 index, Info_To_Post type, real32 x){
i32 i;
for (i = 0; i < count; ++i, ++info){
if (index == info->which_branch){
switch (type){
case Step_Info: info->step = x; break;
case Length_Info: info->length = x; break;
case Contribution_Info:
if (info->contribution_count < ArrayCount(info->contribution_list)){
info->contribution_list[info->contribution_count++] = x;
}break;
}
break;
}
}
}
internal i32
tree_skeleton_gen(Tree_Skeleton_Vert *vertices, i32 vertex_count, i32 *vertex_count_out,
Tree_Parameters params, i32 limit, bool32 allow_colliding,
Branch_Info *opt_output, i32 opt_output_count){
Skeleton_Branch_Point queue[730];
i32 queue_wrap = ArrayCount(queue);
i32 write_pos = 0;
i32 read_pos = 0;
i32 k = 0;
vertices[k].pos = params.base;
queue[write_pos++] =
SBP(k, params.initial_step, 270.f, params.initial_thickness, 0);
if (opt_output){
post_branch_info(opt_output, opt_output_count, k, Step_Info, params.initial_step);
post_branch_info(opt_output, opt_output_count, k, Length_Info, params.initial_step);
}
++k;
i32 I = 0;
for (; read_pos != write_pos; ){
Skeleton_Branch_Point *bp = queue + read_pos;
++read_pos;
read_pos = read_pos % queue_wrap;
Tree_Skeleton_Vert *vert = vertices + bp->vert_id;
vert->theta = bp->theta;
vert->width = bp->width;
if (vert->paint.r == 0 && vert->paint.g == 0 && vert->paint.b == 0){
if (allow_colliding) vert->paint = faded_gold;
else vert->paint = silver;
}
i32 *index_order = 0;
persist i32 index_order_case_1[] = {0};
persist i32 index_order_case_3[] = {1, 0, 2};
i32 branch_count;
if (bp->depth == 0){
branch_count = 1;
index_order = index_order_case_1;
}else if (bp->depth < params.levels){
branch_count = 3;
index_order = index_order_case_3;
}else{
branch_count = 0;
}
Vec2 p = vert->pos;
real32 theta_base = bp->theta - (branch_count - 1) * params.dtheta * .5f;
i32 actual_branch_count = 0;
_Assert(vert->children[0] == 0);
_Assert(vert->children[1] == 0);
_Assert(vert->children[2] == 0);
bool32 block_others = 0;
for (i32 ii = 0; ii < branch_count && !block_others; ++ii){
i32 i = index_order[ii];
real32 theta = theta_base + params.dtheta * i;
real32 default_step = bp->step;
#if 0
Vec2 towards_new_p = polar_to_cartesian(theta, default_step);
f32 weighted_theta = theta;
f32 weight = 0.f;
i32 contribution_count = 0;
for (i32 j = 0; j < params.hot_spot_count; ++j){
Vec2 towards_hot_spot = params.hot_spots[j] - p;
real32 projection = vector_projection(towards_new_p, towards_hot_spot);
if (projection > 0.f){
weight += projection;
++contribution_count;
}
if (opt_output){
post_branch_info(opt_output, opt_output_count, k, Contribution_Info, weight);
}
}
if (theta >= bp->theta - 0.001f && theta <= bp->theta + 0.001f){
//weight *= params.center_branch_weight;
}
if (contribution_count != 0){
weight /= contribution_count;
}
#elif 1
f32 weight = default_step;
f32 weighted_theta = theta;
f32 average_arg = 0.f, total_weight = 0.f;
Vec2 d_vec;
f32 arg, d, local_weight;
Hot_Spot *spot = params.hot_spots;
for (i32 j = 0; j < params.hot_spot_count; ++j, ++spot){
d_vec = spot->spot - p;
arg = argument_degrees(d_vec);
d = SQRT(dot(d_vec, d_vec));
d -= default_step;
if (d > 0 && d < params.heat_half_distance * 5.f){
local_weight = POW(2.f, -d / params.heat_half_distance);
total_weight += local_weight;
average_arg += (arg*local_weight);
}
}
if (total_weight > 0){
average_arg /= total_weight;
weighted_theta = average_arg;
if (weighted_theta < 0) weighted_theta += 360.f;
f32 max_theta, min_theta;
max_theta = theta + 20.f;
min_theta = theta - 20.f;
if (min_theta < 0) min_theta += 360.f;
if (max_theta < min_theta){
max_theta += 360.f;
}
weighted_theta = Max(min_theta, Min(max_theta, weighted_theta));
if (weighted_theta >= 360.f) weighted_theta -= 360.f;
}
#else
f32 weight = default_step;
f32 weighted_theta = theta;
#endif
if (weight > params.min_weight_threshold){
Vec2 new_p = p + polar_to_cartesian(weighted_theta, weight);
if (I < limit && (allow_colliding || no_collisions(p, new_p, vertices))){
++I;
_Assert(k < vertex_count);
vert->children[i] = k;
++actual_branch_count;
vertices[k].pos = new_p;
// TODO(allen): should the new length be based on bp->step or on this branch's specific weight?
queue[write_pos++] =
SBP(k, bp->step * params.step_decay, theta, bp->width * params.thickness_decay, bp->depth + 1);
write_pos = write_pos % queue_wrap;
_Assert(write_pos != read_pos);
if (opt_output){
post_branch_info(opt_output, opt_output_count, k, Step_Info, bp->step);
post_branch_info(opt_output, opt_output_count, k, Length_Info, weight);
}
++k;
}
else{
if (ii == 0) block_others = 1;
}
}
else{
if (ii == 0) block_others = 1;
}
}
vert->child_count = actual_branch_count;
i32 i, j;
for (i = 0, j = 0; i < 3; ++i){
if (vert->children[i] != 0){
vert->children[j] = vert->children[i];
++j;
}
}
_Assert(j == actual_branch_count);
for (; j < 3; ++j){
vert->children[j] = 0;
}
}
*vertex_count_out = k;
return I;
}
struct Thickness_Point{
i32 id;
i32 next_child;
};
internal Thickness_Point
TP(i32 id){
Thickness_Point result;
result.id = id;
result.next_child = 0;
return result;
}
internal void
tree_gen(Vec2 *vertices, i32 vertex_count, i32 *vertex_count_out, Tree_Parameters params,
Tree_Skeleton_Vert *skeleton){
Thickness_Point stack[16];
i32 top = 0;
i32 k = 0;
stack[top++] = TP(0);
while (top > 0){
Thickness_Point *tp = stack + top - 1;
Tree_Skeleton_Vert *vert = skeleton + tp->id;
{
Vec2 p = vert->pos;
Vec2 q = split_point(vert->width, vert->width * params.thickness_decay, vert->child_count, tp->next_child);
vertices[k++] = p + rotate(q, vert->theta - 270.f);
}
bool32 done = 0;
if (tp->next_child == 3){
done = 1;
}
else{
i32 child = vert->children[tp->next_child];
if (child){
stack[top++] = TP(child);
++tp->next_child;
}
else{
done = 1;
}
}
if (done){
--top;
}
}
*vertex_count_out = k;
}
// BOTTOM