#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;
vec4 opt_tryoutf4;
float radius_inner;
float p1, p2, p3;
mat4 rot;
};
constexpr int ncolors = 10;
struct MT_Colors {
pColor front[ncolors], back[ncolors];
};
struct Gear_Info {
float radius_inner;
float width;
int n_teeth;
float land_bot_frac;
float land_top_frac;
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;
pCoor object_location;
pVect object_direction;
pMatrix object_orientation;
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; float opt_tryoutf;
Gear_Info gear_info;
VBufferVV<mat4> buf_gears_xforms;
void gear_setup(bool rand);
void scene_setup_1();
void scene_setup_2();
void scene_setup_3();
double world_time, time_last_update;
bool opt_pause;
VPipeline pipe_gear_base;
VPipeline pipe_gear_prob1;
VPipeline pipe_gear_prob2;
VVertex_Buffer_Set bset_gear_strip;
int bset_size_tooth;
VBufferV<MT_Colors> uni_colors;
int opt_shader;
enum HW_Shader
{ HW_Shader_Base, HW_Shader_Prob1, HW_Shader_Prob2, HW_Shader_SIZE };
int opt_n_teeth;
int opt_n_gears;
int colors_serial;
};
static const char* const hw_shader_str[] = { "BASE", "HW05-P1", "HW05-P2" };
void
World::scene_setup_1()
{
}
void
World::scene_setup_2()
{
}
void
World::scene_setup_3()
{
vh.clearValues[0].color =
vk::ClearColorValue( array<float, 4>( { { 0.4, .4, .4, 1.0 } } ) );
eye_location = pCoor(9.4,1.3,16.6);
eye_direction = pVect(0,-.28,-.96);
opt_light_intensity = 14.51;
}
float
prand(float min, float max)
{
double r = random() * (1.0 / RAND_MAX );
return min + r * ( max - min );
}
void
World::gear_setup(bool rand)
{
world_time = 0;
time_last_update = time_wall_fp();
gear_info.serial++;
gear_info.n_teeth = opt_n_teeth;
gear_info.radius_inner = 4;
gear_info.width = 1.5;
gear_info.land_bot_frac = 0.3;
gear_info.land_top_frac = 0.1;
uni_misc->radius_inner = gear_info.radius_inner * 0.5;
float pitch_theta = 2 * M_PI / gear_info.n_teeth;
float face_frac =
( 1 - gear_info.land_bot_frac - gear_info.land_top_frac ) / 2;
float width_theta_face = face_frac * pitch_theta;
float width_it = 1; float tan_dtf = tanf(width_theta_face);
for ( int n_iter = 0; n_iter < 20; n_iter++ )
{
float t1 = cosf(width_it) + width_it * sinf(width_it);
float tanth = ( sinf(width_it) - width_it * cosf(width_it) ) / t1;
float err = tan_dtf - tanth;
if ( fabs(err) < 1e-6 ) break;
width_it += err * powf( t1 / width_it, 2 );
}
float r_outer = gear_info.radius_inner * sqrt( 1 + width_it * width_it );
int n_gears = opt_n_gears;
float delta_theta = 2 * M_PI / ( ( n_gears + 1 ) & 0xfffffe );
float gears_rad =
1.001 * ( r_outer + gear_info.radius_inner ) / ( 2 * sinf(delta_theta/2) );
buf_gears_xforms.clear();
pMatrix m_flip_flip( pVect(1,0,0), pVect(0,-1,0), pVect(0,0,-1) );
pMatrix_Rotation m_rot_tooth( pVect(0,0,1), pitch_theta/2 );
pMatrix m_flip =
pMatrix_Translate(0,0,-gear_info.width) * m_rot_tooth * m_flip_flip;
for ( int i=0; i<n_gears; i++ )
{
float theta = M_PI + i * delta_theta;
pMatrix_Rotation rot( pVect(0,0,1), theta );
pMatrix_Translate trans_to_global( rot * pVect(gears_rad,0,0) );
pMatrix m = object_orientation * trans_to_global * rot;
if ( i & 1 ) m = m * m_flip;
buf_gears_xforms << m;
}
buf_gears_xforms.to_dev();
}
void
World::setup_and_run()
{
vh.init();
vh.display_cb_set([&](){});
vh.cbs_cmd_record.push_back( [&](vk::CommandBuffer& cb){ render(cb); });
vh.clearValues[0].color =
vk::ClearColorValue( array<float, 4>( { { 0.3, 0.3, 0.3, 1.0 } } ) );
opt_tryout1 = opt_tryout2 = false;
eye_initial_location = pCoor(9.4,1.3,16.6);
eye_location = eye_initial_location;
eye_initial_direction = pVect(0,-.28,-.96);
eye_direction = eye_initial_direction;
{
pCoor pos_ref(12.2,-2.3,-1);
pNorm ax(1,0,0);
pNorm ay(0,0,-1); pNorm az = cross(ax,ay);
object_orientation = pMatrix_Translate(pos_ref) * pMatrix_Cols(ax,ay,az);
}
opt_tryoutf = 1;
variable_control.insert(opt_tryoutf,"opt_tryoutf",1.2);
opt_n_gears = 6;
opt_n_teeth = 16;
gear_info.n_teeth = 0;
variable_control.insert(opt_n_gears,"Num Gears",1,2);
variable_control.insert(opt_n_teeth,"Num Teeth",1,3);
opt_light_intensity = 22;
light_location = pCoor( 12.6, 10.8,25.3);
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_misc->opt_tryoutf4.x = opt_tryoutf;
uni_light_simple.init(vh.qs,vk::BufferUsageFlagBits::eUniformBuffer);
gear_info.serial = 0;
uni_colors.init(vh.qs,vk::BufferUsageFlagBits::eUniformBuffer);
buf_gears_xforms.init(vh.qs,vk::BufferUsageFlagBits::eStorageBuffer);
opt_pause = false;
opt_shader = 0;
colors_serial = -1;
scene_setup_2();
vh.message_loop_spin();
uni_light_simple.destroy();
uni_misc.destroy();
pipe_plain.destroy();
pipe_gear_base.destroy();
pipe_gear_prob1.destroy();
pipe_gear_prob2.destroy();
bset_plain.destroy();
bset_gear_strip.destroy();
shapes.destroy();
transform.destroy();
uni_colors.destroy();
buf_gears_xforms.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
("Object location: [%5.1f, %5.1f, %5.1f] "
"Object direction: [%+.2f, %+.2f, %+.2f]\n",
object_orientation.rc_get(0,3),
object_orientation.rc_get(1,3),
object_orientation.rc_get(2,3),
0.0,0.0,0.0);
vh.fbprintf
("Shader: %s ('v') "
"Tryout 1: %s ('y') Tryout 2: %s ('Y') \n",
hw_shader_str[opt_shader],
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->opt_tryoutf4.x = opt_tryoutf;
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_gear_base )
pipe_gear_base
.init( vh.qs )
.ds_uniform_use( "BIND_LIGHT_SIMPLE", uni_light_simple )
.ds_uniform_use( "BIND_MISC", uni_misc )
.ds_uniform_use( "BIND_HW05", uni_colors )
.shader_inputs_info_set<pCoor,pNorm,int>()
.shader_code_set
("gear-base-shdr.cc",
"vs_main_list();", "gs_main_list();", "fs_main_list();")
.topology_set( vk::PrimitiveTopology::eTriangleStrip )
.create();
if ( !pipe_gear_prob1 )
pipe_gear_prob1
.init( vh.qs )
.ds_uniform_use( "BIND_LIGHT_SIMPLE", uni_light_simple )
.ds_uniform_use( "BIND_MISC", uni_misc )
.ds_uniform_use( "BIND_HW05", uni_colors )
.shader_inputs_info_set<pCoor,pNorm,int>()
.shader_code_set
("hw05-shdr-sol.cc",
"vs_main_prob1();", "gs_main_prob1();", "fs_main();",
"#define HW05_PROB1\n")
.topology_set( vk::PrimitiveTopology::eTriangleStrip )
.create();
if ( !pipe_gear_prob2 )
pipe_gear_prob2
.init( vh.qs )
.ds_uniform_use( "BIND_LIGHT_SIMPLE", uni_light_simple )
.ds_uniform_use( "BIND_MISC", uni_misc )
.ds_uniform_use( "BIND_HW05", uni_colors )
.ds_storage_follow( "BIND_XF", buf_gears_xforms )
.shader_inputs_info_set<pCoor,pNorm,int>()
.shader_code_set
("hw05-shdr-sol.cc",
"vs_main_prob2();", "gs_main_prob2();", "fs_main();",
"#define HW05_PROB2\n")
.topology_set( vk::PrimitiveTopology::eTriangleStrip )
.create();
bset_plain.reset(pipe_plain);
pCoor p0 = { 9, 0, -10 };
pCoor p1 = { 18, 6, -4 };
pCoor p2 = { 9, 5, -9 };
pNorm tri_norm = cross( p0, p1, p2 );
bset_plain << p0 << p1 << p2;
bset_plain << color_green << color_red << color_blue;
bset_plain << tri_norm << tri_norm << tri_norm;
bset_plain << pCoor(-2,0,-4) << pCoor(-4,2,-4) << pCoor(-4,0,-4);
bset_plain << color_red << color_red << color_red;
bset_plain << pCoor(-2,0,-4) << pCoor(-2,2,-4) << pCoor(-4,2,-4);
bset_plain << color_green << color_green << color_green;
pNorm snorm = cross( pCoor(-4,0,-4), pCoor(-4,2,-4), pCoor(-2,0,-4) );
bset_plain << snorm << snorm << snorm << snorm << snorm << snorm;
bset_plain.to_dev();
pipe_plain.ds_set( transform );
pipe_plain.record_draw(cb, bset_plain);
if ( !opt_pause ) world_time += time_now - time_last_update;
time_last_update = time_now;
const float omega = 0.5;
const float theta_0 = world_time * omega;
const bool gear_stale =
opt_n_teeth != gear_info.n_teeth || opt_n_gears != buf_gears_xforms.size();
if ( gear_stale )
{
gear_info.n_teeth = opt_n_teeth;
gear_setup(false);
gear_info.serial++;
}
const bool gear_bset_stale = gear_stale;
const auto& f = gear_info;
if ( colors_serial != f.serial )
{
colors_serial = f.serial;
uni_colors->front[0] = color_lsu_spirit_purple;
uni_colors->back[0] = color_lsu_business_purple;
uni_colors->front[1] = color_lsu_spirit_gold;
uni_colors->back[1] = color_gold;
uni_colors->front[2] = color_aquamarine;
uni_colors->back[2] = color_salmon;
uni_colors->front[3] = color_aquamarine;
uni_colors->back[3] = color_salmon;
uni_colors->front[4] = color_gray;
uni_colors->back[4] = color_red;
uni_colors->front[5] = color_light_gray;
uni_colors->back[5] = color_orange_red;
uni_colors->front[6] = color_light_steel_blue;
uni_colors->back[6] = color_light_steel_blue;
uni_colors.to_dev();
}
if ( gear_bset_stale )
{
bset_gear_strip.reset(pipe_gear_base);
float n_teeth = f.n_teeth;
float pitch_theta = 2 * M_PI / n_teeth;
float th_resolution_target = 2 * M_PI / 300;
float face_frac = ( 1 - f.land_bot_frac - f.land_top_frac ) / 2;
float width_theta_face = face_frac * pitch_theta;
float width_theta_top = f.land_top_frac * pitch_theta;
float width_theta_bot = f.land_bot_frac * pitch_theta;
float off_theta_top = width_theta_face;
float off_land_bot = pitch_theta * ( 2*face_frac + f.land_top_frac );
int n_slices_top =
max(1, int(0.5 + width_theta_top / th_resolution_target));
float delta_theta_top = width_theta_top / n_slices_top;
int n_slices_bot =
max(1, int(0.5 + width_theta_bot / th_resolution_target));
float delta_theta_bot = width_theta_bot / n_slices_bot;
const int n_slices_invo = max(5, int(pitch_theta/th_resolution_target) );
pVect vz(0,0,-f.width);
float r_inner = f.radius_inner;
pCoor p1(0,0,0);
pCoor p4 = p1 + vz;
float width_it = 1; float tan_dtf = tanf(width_theta_face);
for ( int n_iter = 0; n_iter < 20; n_iter++ )
{
float t1 = cosf(width_it) + width_it * sinf(width_it);
float tanth = ( sinf(width_it) - width_it * cosf(width_it) ) / t1;
float err = tan_dtf - tanth;
if ( fabs(err) < 1e-6 ) break;
width_it += err * powf( t1 / width_it, 2 );
}
float delta_it_invo = width_it / n_slices_invo;
float r_outer = r_inner * sqrt( 1 + width_it * width_it );
bset_size_tooth = -1;
for ( int i=0; i<n_teeth; i++ )
{
float theta = i * pitch_theta;
for ( int j=0; j<=n_slices_invo; j++ )
{
float d_theta_a = j * delta_it_invo;
float theta_invo = theta + d_theta_a;
pVect v_invo = r_inner *
pVect( cosf(theta_invo) + d_theta_a * sinf(theta_invo),
sinf(theta_invo) - d_theta_a * cosf(theta_invo), 0 );
pVect d_invo( cosf(theta_invo), sinf(theta_invo), 0);
pNorm np = cross( vz, d_invo );
if ( i < 1 ) bset_gear_strip << 0 << 1;
else bset_gear_strip << 2 << 3;
bset_gear_strip << p1 + v_invo << p4 + v_invo;
bset_gear_strip << np << np;
}
for ( int j=0; j<=n_slices_top; j++ )
{
float theta_top = theta + off_theta_top + j * delta_theta_top;
pVect n_land( cosf(theta_top), sinf(theta_top), 0 );
pVect v_land = r_outer * n_land;
if ( i < 1 ) bset_gear_strip << 4 << 5;
else bset_gear_strip << 2 << 3;
bset_gear_strip << p1 + v_land << p4 + v_land;
bset_gear_strip << n_land << n_land;
}
for ( int j=n_slices_invo; j>=0; j-- )
{
float d_theta_a = -j * delta_it_invo;
float theta_invo = theta + off_land_bot + d_theta_a;
pVect v_invo = r_inner *
pVect( cosf(theta_invo) + d_theta_a * sinf(theta_invo),
sinf(theta_invo) - d_theta_a * cosf(theta_invo), 0 );
pVect d_invo( cosf(theta_invo), sinf(theta_invo), 0);
pNorm np = cross(vz,d_invo);
if ( i < 1 ) bset_gear_strip << 0 << 1;
else bset_gear_strip << 2 << 3;
bset_gear_strip << p1 + v_invo << p4 + v_invo;
bset_gear_strip << np << np;
}
for ( int j=0; j<=n_slices_bot; j++ )
{
float theta_bot = theta + off_land_bot + j * delta_theta_bot;
pVect n_land( cosf(theta_bot), sinf(theta_bot), 0 );
pVect v_land = r_inner * n_land;
if ( i < 1 ) bset_gear_strip << 4 << 5;
else bset_gear_strip << 2 << 3;
bset_gear_strip << p1 + v_land << p4 + v_land;
bset_gear_strip << n_land << n_land;
}
if ( i == 0 ) bset_size_tooth = bset_gear_strip.size();
}
bset_gear_strip.to_dev();
}
pMatrix_Rotation rot(pVect(0,0,1),theta_0);
switch ( opt_shader ) {
case HW_Shader_Base:
for ( auto m: buf_gears_xforms )
{
pipe_gear_base.ds_set( transform * m * rot );
pipe_gear_base.record_draw(cb, bset_gear_strip );
}
break;
case HW_Shader_Prob1:
for ( auto m: buf_gears_xforms )
{
pipe_gear_prob1.ds_set( transform * m * rot );
pipe_gear_prob1.record_draw(cb, bset_gear_strip );
}
break;
case HW_Shader_Prob2:
uni_misc->rot = rot;
pipe_gear_prob2.ds_set( transform );
pipe_gear_prob2.record_draw_instanced
( cb, bset_gear_strip, buf_gears_xforms.size() );
break;
pipe_gear_prob2.ds_set( transform * buf_gears_xforms[0] * rot );
pipe_gear_prob2.record_draw_instanced( cb, bset_gear_strip, 1 );
break;
default:
assert( false );
};
uni_misc.to_dev();
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 '3': scene_setup_3(); 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':
opt_shader++; if ( opt_shader == HW_Shader_SIZE ) opt_shader = 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 )
{
if ( opt_move_item == MI_Ball )
{
pMatrix_Rotation rot(user_rot_axis, M_PI * 0.03);
pMMultiply3x3(object_orientation, rot, object_orientation);
gear_setup(false);
}
else
{
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;
case MI_Ball:
object_orientation = pMatrix_Translate(adjustment) * object_orientation;
gear_setup(false);
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;
}