#if 0
:Def: Texture:Def: Texel
:Practice Problems:
:Def: Texel Fetch vec4 texel = texture( tex_unit_0, texture_coor );
:Example:
:Def: Texture Filtering
glEnable(GL_DEPTH_TEST);
glDisable(GL_DEPTH_TEST);
glDepthFunc(FUNC);
glDepthFunc(GL_LESS);
glEnable(GL_BLEND);
glDisable(GL_BLEND);
glBlendEquation( EQ );
glBlendFunc( SRC_BF, DST_BF );
#endif
#define MAIN_INCLUDE
#include <vhelper.h>
#include <vstroke.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"
enum { PT_Invert = 1, PT_To_Alpha = 2 };
const int FL_NONE = 0;
#define ENUM_LABEL(c) { #c, c }
template <typename T>
struct pEnum_Label { const char *label; T value; };
pEnum_Label<int> texture_env_modes[] = {
ENUM_LABEL(FL_NONE),
ENUM_LABEL(GL_REPLACE),
ENUM_LABEL(GL_MODULATE),
ENUM_LABEL(GL_DECAL), ENUM_LABEL(GL_BLEND), ENUM_LABEL(GL_ADD), {NULL,0}
};
struct Uni_TMode {
vec4 tex_env_color;
int texture_mode;
int texture_smooth;
};
pEnum_Label<vk::SamplerMipmapMode> texture_mipmap_mode[] = {
ENUM_LABEL( vk::SamplerMipmapMode::eNearest ),
ENUM_LABEL( vk::SamplerMipmapMode::eLinear ),
{NULL,{}}
};
pEnum_Label<vk::Filter> texture_min_filters[] = {
ENUM_LABEL( vk::Filter::eNearest ),
ENUM_LABEL( vk::Filter::eLinear ),
{NULL,{}}
};
pEnum_Label<vk::Filter> texture_mag_filters[] = {
ENUM_LABEL( vk::Filter::eNearest ),
ENUM_LABEL( vk::Filter::eLinear ),
{NULL,{}}
};
class World {
public:
World(pVulkan_Helper &vh)
:vh(vh),ff_state(vh.qs),shapes(ff_state),frame_timer(vh.frame_timer),
transform(vh.qs){ init(); }
void init();
void run();
void render(vk::CommandBuffer& cb);
void cb_keyboard();
void modelview_update();
pVulkan_Helper& vh;
VFixed_Function_State_Manager ff_state;
Shapes shapes;
pVariable_Control variable_control;
pFrame_Timer& frame_timer;
pCoor light_location;
float opt_light_intensity;
enum { MI_Eye, MI_Light, MI_Ball, MI_Ball_V, MI_COUNT } opt_move_item;
bool global_transform_stale;
VTransform transform;
VBufferV<Uni_Lighting> uni_light;
VBufferV<Uni_Material> uni_material_color_sphere;
VPipeline pipe_lonely;
VPipeline pipe_sphere;
VVertex_Buffer_Set bset_lonely, bset_sphere;
pCoor sphere_location;
float sphere_size;
pCoor eye_location;
pVect eye_direction;
pMatrix modelview;
int opt_method;
bool opt_recompute;
bool lonely_triangle_stale;
float tex_scale; float tri_tilt;
vk::Sampler sampler;
VTexture texture_id_syllabus;
VTexture texture_id_image;
string shader_gl_defines;
VBufferV<Uni_TMode> uni_texture_mode;
bool opt_blend;
bool opt_alpha;
int opt_texture_env_mode;
int opt_texture_min_filter;
int opt_texture_mag_filter;
int opt_texture_mipmap_mode;
int opt_perm;
int opt_texture_smooth;
bool opt_lod_zero;
bool sampler_dirty;
};
void
World::init()
{
vh.init();
vh.opt_record_every_time = true;
vh.display_cb_set([&](){});
vh.cbs_cmd_record.push_back( [&](vk::CommandBuffer& cb){ render(cb); });
#define ENUM_TO_INT(e) \
shader_gl_defines += pStringF("const int %s = %d;\n", #e, e).ss();
ENUM_TO_INT(FL_NONE);
ENUM_TO_INT(GL_REPLACE);
ENUM_TO_INT(GL_MODULATE);
ENUM_TO_INT(GL_DECAL);
ENUM_TO_INT(GL_BLEND);
ENUM_TO_INT(GL_ADD);
ENUM_TO_INT(GL_COMBINE);
#undef ENUM_TO_INT
opt_method = 0;
opt_recompute = false;
eye_location = pCoor(2.6,0.5,9);
eye_direction = pVect(0,0,-1);
opt_light_intensity = 7.2;
light_location = pCoor(7,4.0,-0.3);
sphere_location = pCoor(0,0,-5);
sphere_size = 5;
lonely_triangle_stale = true;
tex_scale = 1;
variable_control
.insert(tex_scale,"Texture Scaling")
.set_on_change(lonely_triangle_stale);
tri_tilt = 0;
variable_control
.insert(tri_tilt,"Triangle Tilt",.2,false)
.set_on_change(lonely_triangle_stale);
variable_control.insert(opt_light_intensity,"Light Intensity");
opt_move_item = MI_Eye;
uni_light.init(vh.qs,vk::BufferUsageFlagBits::eUniformBuffer);
uni_texture_mode.init(vh.qs,vk::BufferUsageFlagBits::eUniformBuffer);
uni_texture_mode->texture_mode = opt_texture_env_mode = 0;
uni_texture_mode->texture_smooth = opt_texture_smooth;
uni_texture_mode->tex_env_color = vec4(0,0,0,0);
opt_texture_min_filter = 0;
opt_texture_mag_filter = 0;
opt_texture_mipmap_mode = 0;
opt_perm = 0;
opt_texture_smooth = 0;
opt_lod_zero = false;
sampler = nullptr;
sampler_dirty = true;
opt_blend = false;
opt_alpha = false;
texture_id_syllabus.init( vh.qs, P_Image_Read("gpup.png",255) );
texture_id_image.init( vh.qs, P_Image_Read("mult.png",255) );
uni_material_color_sphere
.init(vh.qs, vk::BufferUsageFlagBits::eUniformBuffer )
.set_val({ color_lsu_spirit_gold, color_red })
.to_dev();
modelview_update();
}
void
World::modelview_update()
{
pMatrix_Translate center_eye(-eye_location);
pMatrix_Rotation rotate_eye(eye_direction,pVect(0,0,-1));
modelview = rotate_eye * center_eye;
}
void
World::run()
{
vh.message_loop_spin();
uni_light.destroy();
uni_texture_mode.destroy();
uni_material_color_sphere.destroy();
bset_sphere.destroy();
bset_lonely.destroy();
pipe_sphere.destroy();
pipe_lonely.destroy();
texture_id_syllabus.destroy();
texture_id_image.destroy();
vh.dev.destroySampler(sampler);
shapes.destroy();
transform.destroy();
vh.finish();
}
void
World::render(vk::CommandBuffer& cb)
{
cb_keyboard();
if ( opt_recompute ) bset_sphere.reset();
vh.fbprintf("%s\n",frame_timer.frame_rate_text_get());
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] "
"Sphere Location[%5.1f, %5.1f, %5.1f]\n",
light_location.x, light_location.y, light_location.z,
sphere_location.x, sphere_location.y, sphere_location.z
);
vh.fbprintf("Texture Mode: %s ('m') Tri TC Permutation: %d ('p')\n",
texture_env_modes[opt_texture_env_mode].label,
opt_perm);
vh.fbprintf("Use Mipmap Levels: %s ('d')\n",
opt_lod_zero ? " NO" : "YES" );
vh.fbprintf("Min Filter: %s ('i')\n",
texture_min_filters[opt_texture_min_filter].label);
vh.fbprintf("Mipmap Mode: %s ('I')\n",
texture_mipmap_mode[opt_texture_mipmap_mode].label);
vh.fbprintf("Mag Filter: %s ('a')\n",
texture_mag_filters[opt_texture_mag_filter].label);
vh.fbprintf("Blending %s ('b') Alpha Test %s ('p')\n",
opt_blend ? "ON" : "OFF", opt_alpha ? "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->var[0]);
const int win_width = vh.get_width();
const int win_height = vh.get_height();
const float aspect = float(win_width) / win_height;
transform.eye_from_global_set( modelview );
transform.clip_from_eye_set
( pMatrix_Frustum(-.8,.8,-.8/aspect,.8/aspect,1,5000) );
pColor white(1,1,1);
pColor ambient_color(0x555555);
auto& light0 = uni_light->cgl_LightSource[0];
uni_light->cgl_LightModel.ambient = ambient_color;
light0.diffuse = white * opt_light_intensity;
light0.position = transform.eye_from_global * light_location;
light0.constantAttenuation = 0;
light0.linearAttenuation = 1;
light0.quadraticAttenuation = 0.25;
light0.ambient = color_black;
uni_light.to_dev();
uni_texture_mode.to_dev();
if ( sampler_dirty )
{
sampler_dirty = false;
vh.frame_serial_need();
if ( sampler ) vh.dev.destroySampler(sampler);
sampler = vh.qs.dev.createSampler
( { {},
texture_mag_filters[opt_texture_mag_filter].value,
texture_min_filters[opt_texture_min_filter].value,
texture_mipmap_mode[opt_texture_mipmap_mode].value,
vk::SamplerAddressMode::eRepeat,
vk::SamplerAddressMode::eRepeat,
vk::SamplerAddressMode::eRepeat,
0.0f, true, 16.0f,
false,
vk::CompareOp::eNever,
0.0f, opt_lod_zero ? 0.0f : VK_LOD_CLAMP_NONE,
vk::BorderColor::eFloatOpaqueBlack } );
}
if ( !pipe_lonely )
{
pipe_lonely
.init( vh.qs )
.ds_follow( transform )
.ds_use_texture()
.ds_uniform_follow( "BIND_LIGHT", uni_light )
.ds_uniform_follow( "BIND_TEXTURE_MODE", uni_texture_mode )
.shader_inputs_info_set<pCoor,pNorm,pTCoor,pColor>()
.shader_code_set
("demo-08-shdr.cc", "vs_main();", nullptr, "fs_main();",
shader_gl_defines + "#define PIPE_LONELY\n")
.topology_set( vk::PrimitiveTopology::eTriangleList )
.create();
}
if ( !bset_lonely || lonely_triangle_stale )
{
bset_lonely.reset( pipe_lonely );
lonely_triangle_stale = false;
{
noperspective
pCoor floor_center = sphere_location - pVect(0,1.5*sphere_size,0);
pCoor p0 = floor_center + pVect(-sphere_size,0,sphere_size);
pCoor p1 = p0 + pVect(2*sphere_size,0,0);
pCoor p2 = p0 + pVect(0,0,-2*sphere_size);
pCoor p3 = p2 + pVect(2*sphere_size,0,0);
bset_lonely << p0 << p1 << p2
<< p2 << p1 << p3;
bset_lonely << pTCoor(1,0) << pTCoor(0,0) << pTCoor(1,1)
<< pTCoor(1,1) << pTCoor(0,0) << pTCoor(0,1);
for ( int i=0; i<3; i++ ) bset_lonely << pVect(0,1,0) << color_white;
for ( int i=0; i<3; i++ ) bset_lonely << pVect(0,1,0) << color_yellow;
}
pCoor p1( 11, -4, -2.8 );
pTCoor tc1( tex_scale * 1.0, tex_scale * 1.0 );
bset_lonely << p1 << tc1 << color_green;
pCoor p2( 11 + tri_tilt, 5, -2.8 );
pTCoor tc2( tex_scale * 1.0, tex_scale * 0.0 );
bset_lonely << p2 << tc2 << color_red;
pCoor p3( 2, 5, -2.8 );
pTCoor tc3( tex_scale * 0.0, tex_scale * 0.0 );
bset_lonely << p3 << tc3 << color_blue;
pNorm tnorm = cross(p1,p2,p3);
bset_lonely << tnorm << tnorm << tnorm;
bset_lonely.to_dev();
}
pipe_lonely
.ds_use(sampler, texture_id_syllabus )
.record_draw(cb, bset_lonely);
if ( !pipe_sphere )
{
pipe_sphere
.init( vh.qs )
.ds_use_texture()
.ds_uniform_use( "BIND_MAT_COLOR", uni_material_color_sphere )
.ds_uniform_follow( "BIND_LIGHT", uni_light )
.ds_uniform_follow( "BIND_TEXTURE_MODE", uni_texture_mode )
.shader_inputs_info_set<pCoor,pTCoor>()
.shader_code_set
("demo-08-shdr.cc", "vs_main_sphere();", nullptr, "fs_main();",
shader_gl_defines + "#define PIPE_SPHERE\n")
.topology_set( vk::PrimitiveTopology::eTriangleStrip )
.create();
}
if ( !bset_sphere )
{
bset_sphere.reset( pipe_sphere );
const int slices = 40;
const float delta_eta = M_PI / slices;
const float delta_theta = delta_eta;
const float delta_ty = 1.0 / slices;
const float delta_tx = 1.0 / ( 2 * slices );
for ( int i=0; i<slices-1; i++ )
{
const float eta = i * delta_eta, eta1 = eta + delta_eta;
const float y0 = cosf(eta), y1 = cosf(eta1);
const float slice_r0 = sinf(eta), slice_r1 = sinf(eta1);
const float ty0 = i * delta_ty, ty1 = ty0 + delta_ty;
bset_sphere << pCoor( slice_r0, y0, 0 )
<< pTCoor( 0, ty0 );
bset_sphere << pCoor( slice_r1, y1, 0 )
<< pTCoor( 0, ty1 );
for ( int j=1; j<=2*slices; j++ )
{
const float theta = j * delta_theta;
const float tx = j * delta_tx;
bset_sphere
<< pCoor( slice_r0 * sinf(theta), y0, slice_r0 * cosf(theta) )
<< pTCoor( tx, ty0 );
bset_sphere
<< pCoor( slice_r1 * sinf(theta), y1, slice_r1 * cosf(theta) )
<< pTCoor( tx, ty1 );
}
}
bset_sphere.to_dev();
}
pipe_sphere
.ds_use(sampler, texture_id_image )
.ds_set( transform
* pMatrix_Translate( sphere_location )
* pMatrix_Scale(sphere_size)
* pMatrix_Rotation( pVect(0,1,0), 2.6 ) )
.record_draw( cb, bset_sphere );
shapes.record_tetrahedron(cb,transform,light_location,0.5);
}
void
World::cb_keyboard()
{
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 'b': case 'B': opt_blend = !opt_blend; break;
case 'P': opt_alpha = !opt_alpha; break;
case 'd': case 'D': opt_lod_zero = !opt_lod_zero; sampler_dirty = true; break;
case 'm': opt_texture_env_mode++;
if ( !texture_env_modes[opt_texture_env_mode].label )
opt_texture_env_mode = 0;
uni_texture_mode->texture_mode
= texture_env_modes[opt_texture_env_mode].value;
break;
case 'i': opt_texture_min_filter++;
sampler_dirty = true;
if ( !texture_min_filters[opt_texture_min_filter].label )
opt_texture_min_filter = 0;
break;
case 'I': opt_texture_mipmap_mode++;
sampler_dirty = true;
if ( !texture_mipmap_mode[opt_texture_mipmap_mode].label )
opt_texture_mipmap_mode = 0;
break;
case 'a': opt_texture_mag_filter++;
sampler_dirty = true;
if ( !texture_mag_filters[opt_texture_mag_filter].label )
opt_texture_mag_filter = 0;
break;
case 's': case 'S': 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': opt_perm++; if ( opt_perm > 5 ) opt_perm = 0;
case 'r': case 'R': opt_recompute = !opt_recompute; break;
case 'x': case 'X':
opt_texture_smooth = !opt_texture_smooth;
uni_texture_mode->texture_smooth = opt_texture_smooth;
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);
modelview_update();
}
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: sphere_location += adjustment; break;
default: break;
}
modelview_update();
}
}
int
main(int argv, char **argc)
{
pVulkan_Helper pvulkan_helper(argv,argc);
World world(pvulkan_helper);
world.run();
return 0;
}