#define MAIN_INCLUDE
#include <vhelper.h>
#include <gp/coord.h>
#include <gp/pstring.h>
#include <gp/misc.h>
#include <gp/colors.h>
#include <vutil-texture.h>
#include <vutil-pipeline.h>
#include "shapes.h"
struct Uni_Light_Simple {
vec4 position;
vec4 color;
};
struct Uni_Misc {
ivec4 opt_tryout;
};
constexpr int ncolors = 10;
struct HW03_Colors {
pColor front[ncolors], back[ncolors];
};
struct Fan_Info {
pCoor pos_axis_top;
pCoor pos_axis_bottom;
pVect axis_to_cyl;
float outer_radius;
int n_blades;
pColor blade_0_front_upper, blade_0_front_lower;
pColor blade_0_back_upper, blade_0_back_lower;
pColor blade_i_front_upper, blade_i_front_lower;
pColor blade_i_back_upper, blade_i_back_lower;
int serial;
};
class World {
public:
World(pVulkan_Helper &vh)
:vh(vh),ff_state(vh.qs),frame_timer(vh.frame_timer),shapes(ff_state),
transform(vh.qs){}
void setup_and_run();
void render(vk::CommandBuffer& cb);
void keyboard_handle();
pVulkan_Helper& vh;
VFixed_Function_State_Manager ff_state;
pFrame_Timer& frame_timer;
Shapes shapes;
pVariable_Control variable_control;
pCoor light_location;
float opt_light_intensity;
enum { MI_Eye, MI_Light, MI_Ball, MI_Ball_V, MI_COUNT } opt_move_item;
pCoor eye_location, eye_initial_location;
pVect eye_direction;
pNorm eye_initial_direction;
VTransform transform;
VBufferV<Uni_Light_Simple> uni_light_simple;
VBufferV<Uni_Misc> uni_misc;
VPipeline pipe_plain;
VVertex_Buffer_Set bset_plain;
bool opt_tryout1, opt_tryout2;
Fan_Info fan_info;
vector<pMatrix> fans_xforms;
void fan_setup(bool rand);
void scene_setup_1();
void scene_setup_2();
double world_time, time_last_update;
bool opt_pause;
VPipeline pipe_hw03;
VVertex_Buffer_Set bset_hw03_p1;
VVertex_Buffer_Set bset_hw03_p2;
enum Pipeline_Variant { PV_Plain, PV_HW03_P1, PV_HW03_P2, PV_SIZE };
int pipeline_variant;
VBufferV<HW03_Colors> uni_hw03_colors;
int bset_p2_serial;
};
const char* const pipeline_variant_str[] =
{ "PLAIN", "HW03-P1", "HW03-P2" };
void
World::scene_setup_1()
{
fan_setup(true);
}
void
World::scene_setup_2()
{
fan_setup(false);
}
float
prand(float min, float max)
{
double r = random() * (1.0 / RAND_MAX );
return min + r * ( max - min );
}
void
World::fan_setup(bool rand)
{
world_time = 0;
time_last_update = time_wall_fp();
fan_info.serial++;
auto prand = [=](float min, float max)
{ return rand ? ::prand(min,max) : min + (max-min)/2; };
int n_fans = prand(7,14);
float arr_r = 10;
pCoor pos_ref = eye_initial_location
+ ( 20 + arr_r ) * eye_initial_direction - pVect(0,4,0);
pNorm ax = cross( pVect(0,1,0), eye_initial_direction );
pNorm ay(0,1,prand(0,.4));
pNorm az = cross(ax,ay);
float cyl_ht = min( 2.0, M_PI * arr_r / n_fans );
float delta_theta = 2 * M_PI / n_fans;
fan_info.pos_axis_bottom = pCoor(0,0,0);
fan_info.pos_axis_top = fan_info.pos_axis_bottom + pVect(0,cyl_ht,0);
fan_info.axis_to_cyl = pVect(cyl_ht * .3,0,0);
fan_info.outer_radius = cyl_ht * .7;
fan_info.n_blades = 12;
if ( rand && false )
{
}
else
{
fan_info.blade_0_front_upper = color_lsu_spirit_purple;
fan_info.blade_0_front_lower = color_lsu_business_purple;
fan_info.blade_0_back_upper = color_lsu_spirit_gold;
fan_info.blade_0_back_lower = color_gold;
fan_info.blade_i_front_upper = color_salmon;
fan_info.blade_i_front_lower = color_salmon;
fan_info.blade_i_back_upper = color_aquamarine;
fan_info.blade_i_back_lower = color_aquamarine;
}
fans_xforms.clear();
for ( int i=0; i<n_fans; i++ )
{
float theta = i * delta_theta;
pCoor pos_axis_top =
pos_ref + az * arr_r * cosf(theta) + ax * arr_r * sin(theta);
pCoor pos_axis_bottom = pos_axis_top - ay * cyl_ht;
pMatrix_Translate trans_to_global(pos_axis_bottom);
pMatrix_Cols rot_to_global(ax,ay,az);
fans_xforms << trans_to_global * rot_to_global;
}
}
void
World::setup_and_run()
{
vh.init();
vh.display_cb_set([&](){});
vh.cbs_cmd_record.push_back( [&](vk::CommandBuffer& cb){ render(cb); });
pipeline_variant = 0;
opt_tryout1 = opt_tryout2 = false;
eye_initial_location = pCoor( 3, .5, 8.6) ;
eye_location = eye_initial_location;
eye_initial_direction = pVect(0,0,-1);
eye_direction = eye_initial_direction;
opt_light_intensity = 22;
light_location = pCoor( -6.6, 8.0, 2.5 );
variable_control.insert(opt_light_intensity,"Light Intensity");
opt_move_item = MI_Eye;
uni_misc.init(vh.qs,vk::BufferUsageFlagBits::eUniformBuffer);
uni_misc->opt_tryout = ivec4(opt_tryout1,opt_tryout2,0,0);
uni_light_simple.init(vh.qs,vk::BufferUsageFlagBits::eUniformBuffer);
fan_info.serial = 0;
uni_hw03_colors.init(vh.qs,vk::BufferUsageFlagBits::eUniformBuffer);
bset_p2_serial = 0;
opt_pause = false;
scene_setup_1();
vh.message_loop_spin();
uni_light_simple.destroy();
uni_misc.destroy();
pipe_plain.destroy();
pipe_hw03.destroy();
bset_plain.destroy();
bset_hw03_p1.destroy();
bset_hw03_p2.destroy();
shapes.destroy();
transform.destroy();
uni_hw03_colors.destroy();
vh.finish();
}
void
World::render(vk::CommandBuffer& cb)
{
keyboard_handle();
vh.fbprintf("%s\n",frame_timer.frame_rate_text_get());
const double time_now = time_wall_fp();
const bool blink_visible = int64_t(time_now*2) & 1;
# define BLINK(txt,pad) ( blink_visible ? txt : pad )
vh.fbprintf
("Compiled: %s\n",
#ifdef __OPTIMIZE__
"WITH OPTIMIZATION"
#else
BLINK("WITHOUT OPTIMIZATION","")
#endif
);
vh.fbprintf
("Eye location: [%5.1f, %5.1f, %5.1f] "
"Eye direction: [%+.2f, %+.2f, %+.2f]\n",
eye_location.x, eye_location.y, eye_location.z,
eye_direction.x, eye_direction.y, eye_direction.z);
vh.fbprintf
("Light location: [%5.1f, %5.1f, %5.1f]\n",
light_location.x, light_location.y, light_location.z);
vh.fbprintf
("Pipeline Variant: %s ('v') Tryout 1: %s ('y') Tryout 2: %s ('Y') \n",
pipeline_variant_str[pipeline_variant],
opt_tryout1 ? BLINK("ON "," ") : "OFF",
opt_tryout2 ? BLINK("ON "," ") : "OFF");
pVariable_Control_Elt* const cvar = variable_control.current;
vh.fbprintf("VAR %s = %.5f (TAB or '`' to change, +/- to adjust)\n",
cvar->name,cvar->get_val());
pMatrix eye_from_global =
pMatrix_Rotation(eye_direction,pVect(0,0,-1))
* pMatrix_Translate(-eye_location);
transform.eye_from_global_set( eye_from_global );
const int win_width = vh.s_extent.width;
const int win_height = vh.s_extent.height;
const float aspect = float(win_width) / win_height;
const float n_dist = 0.01;
const float xr = .8 * n_dist;
transform.clip_from_eye_set
( pMatrix_Frustum( -xr, xr, -xr/aspect, xr/aspect, n_dist, 5000 ) );
uni_light_simple->color = color_white * opt_light_intensity;
uni_light_simple->position = transform.eye_from_global * light_location;
uni_light_simple.to_dev();
uni_misc.to_dev();
if ( !pipe_plain )
pipe_plain
.init( vh.qs )
.ds_uniform_use( "BIND_LIGHT_SIMPLE", uni_light_simple )
.ds_uniform_use( "BIND_MISC", uni_misc )
.shader_inputs_info_set<pCoor,pNorm,pColor>()
.shader_code_set
("demo-03-shdr.cc", "vs_main();", nullptr, "fs_main();")
.topology_set( vk::PrimitiveTopology::eTriangleList )
.create();
if ( !pipe_hw03 )
pipe_hw03
.init( vh.qs )
.ds_uniform_use( "BIND_LIGHT_SIMPLE", uni_light_simple )
.ds_uniform_use( "BIND_MISC", uni_misc )
.ds_uniform_use( "BIND_HW03", uni_hw03_colors ) .shader_inputs_info_set<pCoor,pNorm,int>() .shader_code_set
("hw03-shdr-sol.cc", "vs_main();", nullptr, "fs_main();")
.topology_set( vk::PrimitiveTopology::eTriangleList )
.create();
bset_plain.reset(pipe_plain);
pCoor p0 = { 0, 0, 0 };
pCoor p1 = { 9, 6, -9 };
pCoor p2 = { 0, 5, -5 };
pNorm tri_norm = cross( p0, p1, p2 );
pColor color_tri( .2, .900, .1 );
bset_plain << p0 << p1 << p2;
bset_plain << color_tri << color_red << color_blue;
bset_plain << tri_norm << tri_norm << tri_norm;
bset_plain << pCoor(-2,0,-2) << pCoor(-4,2,-2) << pCoor(-4,0,-2);
bset_plain << color_red << color_red << color_red;
bset_plain << pCoor(-2,0,-2) << pCoor(-2,2,-2) << pCoor(-4,2,-2);
bset_plain << color_green << color_green << color_green;
pNorm snorm = cross( pCoor(-4,0,-2), pCoor(-4,2,-2), pCoor(-2,0,-2) );
bset_plain << snorm << snorm << snorm << snorm << snorm << snorm;
if ( !opt_pause ) world_time += time_now - time_last_update;
time_last_update = time_now;
const float omega = 1;
const float theta_0 = world_time * omega;
const auto& f = fan_info;
int n_slices = 20; float delta_theta = 2 * M_PI / f.n_blades;
float delta_eta = delta_theta / n_slices;
pVect delta_vz = pVect(f.pos_axis_top,f.pos_axis_bottom)/n_slices;
pNorm ax(f.axis_to_cyl);
pNorm az(f.pos_axis_bottom,f.pos_axis_top);
pVect ay = cross(az,ax);
float r1 = ax.magnitude;
pColor b0_fu [[maybe_unused]] = f.blade_0_front_upper;
pColor b0_fl [[maybe_unused]] = f.blade_0_front_lower;
pColor b0_bu [[maybe_unused]] = f.blade_0_back_upper;
pColor b0_bl [[maybe_unused]] = f.blade_0_back_lower;
pColor bi_fu [[maybe_unused]] = f.blade_i_front_upper;
pColor bi_fl [[maybe_unused]] = f.blade_i_front_lower;
pColor bi_bu [[maybe_unused]] = f.blade_i_back_upper;
pColor bi_bl [[maybe_unused]] = f.blade_i_back_lower;
if ( pipeline_variant != PV_Plain )
{
uni_hw03_colors->front[0] = f.blade_0_front_upper;
uni_hw03_colors->back[0] = f.blade_0_back_upper;
uni_hw03_colors->front[1] = f.blade_0_front_lower;
uni_hw03_colors->back[1] = f.blade_0_back_lower;
uni_hw03_colors->front[2] = f.blade_i_front_upper;
uni_hw03_colors->back[2] = f.blade_i_back_upper;
uni_hw03_colors->front[3] = f.blade_i_front_lower;
uni_hw03_colors->back[3] = f.blade_i_back_lower;
uni_hw03_colors.to_dev();
}
switch ( pipeline_variant ) {
case PV_Plain:
for ( auto& m: fans_xforms )
for ( int i=0; i<f.n_blades; i++ )
{
float theta = theta_0 + i * delta_theta;
pCoor p1_last, p2_last;
for ( int j=0; j<=n_slices; j++ )
{
float eta = theta + j * delta_eta;
pVect v = ax * cosf(eta) + ay * sinf(eta);
pCoor pa = f.pos_axis_top + delta_vz * j;
pCoor p3 = m * ( pa + r1 * v );
pCoor p4 = m * ( pa + f.outer_radius * v );
if ( j )
{
pNorm n = cross(v,pVect(p3,p1_last));
bset_plain << p1_last << p3 << p2_last;
bset_plain << p2_last << p3 << p4;
bset_plain << n << n << n << n << n << n;
if ( !i )
bset_plain
<< f.blade_0_front_upper << f.blade_0_front_upper
<< f.blade_0_front_upper
<< f.blade_0_front_lower << f.blade_0_front_lower
<< f.blade_0_front_lower;
else
bset_plain
<< f.blade_i_front_upper << f.blade_i_front_upper
<< f.blade_i_front_upper
<< f.blade_i_front_lower << f.blade_i_front_lower
<< f.blade_i_front_lower;
}
p1_last = p3;
p2_last = p4;
}
}
break;
case PV_HW03_P1:
bset_hw03_p1.reset(pipe_hw03);
for ( auto& m: fans_xforms )
for ( int i=0; i<f.n_blades; i++ )
{
float theta = theta_0 + i * delta_theta;
pCoor p1_last, p2_last;
for ( int j=0; j<=n_slices; j++ )
{
float eta = theta + j * delta_eta;
pVect v = ax * cosf(eta) + ay * sinf(eta);
pCoor pa = f.pos_axis_top + delta_vz * j;
pCoor p3 = m * ( pa + r1 * v );
pCoor p4 = m * ( pa + f.outer_radius * v );
if ( j )
{
pNorm n = cross(p1_last,p3,p2_last);
bset_hw03_p1 << p1_last << p3 << p2_last;
bset_hw03_p1 << p2_last << p3 << p4;
bset_hw03_p1 << n << n << n << n << n << n;
if ( !i ) bset_hw03_p1 << 0 << 0 << 0 << 1 << 1 << 1;
else bset_hw03_p1 << 2 << 2 << 2 << 3 << 3 << 3;
}
p1_last = p3;
p2_last = p4;
}
}
bset_hw03_p1.to_dev();
pipe_hw03.ds_set( transform );
pipe_hw03.record_draw(cb, bset_hw03_p1);
break;
case PV_HW03_P2:
{
if ( bset_p2_serial != fan_info.serial )
{
bset_p2_serial = fan_info.serial;
bset_hw03_p2.reset(pipe_hw03);
for ( int i=0; i<f.n_blades; i++ )
{
float theta = i * delta_theta;
pCoor p1_last, p2_last;
for ( int j=0; j<=n_slices; j++ )
{
float eta = theta + j * delta_eta;
pVect v = ax * cosf(eta) + ay * sinf(eta);
pCoor pa = f.pos_axis_top + delta_vz * j;
pCoor p3 = pa + r1 * v;
pCoor p4 = pa + f.outer_radius * v;
if ( j )
{
pNorm n = cross(p1_last,p3,p2_last);
bset_hw03_p2 << p1_last << p3 << p2_last;
bset_hw03_p2 << p2_last << p3 << p4;
bset_hw03_p2 << n << n << n << n << n << n;
if ( i ) bset_hw03_p2 << 2 << 2 << 2 << 3 << 3 << 3;
else bset_hw03_p2 << 0 << 0 << 0 << 1 << 1 << 1;
}
p1_last = p3;
p2_last = p4;
}
}
bset_hw03_p2.to_dev();
}
pMatrix_Rotation rot(az,theta_0);
for ( auto& m: fans_xforms )
{
pipe_hw03.ds_set( transform * m * rot );
pipe_hw03.record_draw(cb, bset_hw03_p2);
}
}
break;
default: assert( false );
}
bset_plain.to_dev();
pipe_plain.ds_set( transform );
pipe_plain.record_draw(cb, bset_plain);
shapes.record_tetrahedron(cb,transform,light_location,0.2);
}
void
World::keyboard_handle()
{
const int key = vh.keyboard_key_get();
if ( !key ) return;
pVect adjustment(0,0,0);
pVect user_rot_axis(0,0,0);
const bool kb_mod_s = vh.keyboard_shift;
const bool kb_mod_c = vh.keyboard_control;
const float move_amt = kb_mod_s ? 2.0 : kb_mod_c ? 0.08 : 0.4;
switch ( key ) {
case FB_KEY_LEFT: adjustment.x = -move_amt; break;
case FB_KEY_RIGHT: adjustment.x = move_amt; break;
case FB_KEY_PAGE_UP: adjustment.y = move_amt; break;
case FB_KEY_PAGE_DOWN: adjustment.y = -move_amt; break;
case FB_KEY_DOWN: adjustment.z = move_amt; break;
case FB_KEY_UP: adjustment.z = -move_amt; break;
case FB_KEY_DELETE: user_rot_axis.y = 1; break;
case FB_KEY_INSERT: user_rot_axis.y = -1; break;
case FB_KEY_HOME: user_rot_axis.x = 1; break;
case FB_KEY_END: user_rot_axis.x = -1; break;
case '1': scene_setup_1(); break;
case '2': scene_setup_2(); break;
case 'b': case 'B': opt_move_item = MI_Ball; break;
case 'e': case 'E': opt_move_item = MI_Eye; break;
case 'l': case 'L': opt_move_item = MI_Light; break;
case 'p': case 'P': opt_pause = !opt_pause; break;
case 'v': case 'V':
if ( ++pipeline_variant >= PV_SIZE ) pipeline_variant = 0;
break;
case 'y': opt_tryout1 = !opt_tryout1;
uni_misc->opt_tryout.x = int(opt_tryout1);
break;
case 'Y': opt_tryout2 = !opt_tryout2;
uni_misc->opt_tryout.y = int(opt_tryout2);
break;
case FB_KEY_TAB:
if ( !kb_mod_s ) { variable_control.switch_var_right(); break; }
case 96: variable_control.switch_var_left(); break;
case '-':case '_': variable_control.adjust_lower(); break;
case '+':case '=': variable_control.adjust_higher(); break;
default: printf("Unknown key, %d\n",key); break;
}
if ( user_rot_axis.x || user_rot_axis.y )
{
pMatrix_Rotation rotall(eye_direction,pVect(0,0,-1));
user_rot_axis *= invert(rotall);
eye_direction *= pMatrix_Rotation(user_rot_axis, M_PI * 0.03);
}
if ( adjustment.x || adjustment.y || adjustment.z )
{
const double angle =
fabs(eye_direction.y) > 0.99
? 0 : atan2(eye_direction.x,-eye_direction.z);
pMatrix_Rotation rotall(pVect(0,1,0),-angle);
adjustment *= rotall;
switch ( opt_move_item ){
case MI_Light: light_location += adjustment; break;
case MI_Eye: eye_location += adjustment; break;
default: break;
}
}
}
int
main(int argv, char **argc)
{
pVulkan_Helper pvulkan_helper(argv,argc);
World world(pvulkan_helper);
world.setup_and_run();
return 0;
}