#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 );
:Def: Stencil Buffer:Def: Stencil Test:Def: Stencil Operation
glClearStencil(7);
glClear( GL_STENCIL_BUFFER_BIT );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
glStencilFunc(FUNC, REF, MASK);
glStencilFuncSeparate(FACE, FUNC, REF, MASK);
0x0x 0x0x
:Example: glStencilFunc(GL_EQUAL, 4, 5 );
glStencilOp(SFAIL, DFAIL, DPASS);
glStencilOpSeparate(FACE, SFAIL, DFAIL, DPASS);
:Example: glStencilFunc(GL_NEVER, -1, 5); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
#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;
};
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,{}}
};
#if 0
GLuint
rpBuild_Texture_File
(const char *name, bool invert = false, int transp = 256 )
{
P_Image_Read image(name,transp);
if ( !image.image_loaded ) return 0;
if ( invert ) image.color_invert();
GLuint tid;
glGenTextures(1,&tid);
glBindTexture(GL_TEXTURE_2D,tid);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, 1);
glTexImage2D
(GL_TEXTURE_2D,
0, GL_RGBA, image.width, image.height,
0, image.gl_fmt, image.gl_type, (void*)image.data);
pError_Check();
return tid;
}
#endif
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;
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;
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); });
frame_timer.work_unit_set("Steps / s");
#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 = 2;
variable_control
.insert(tri_tilt,"Triangle Tilt")
.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->tex_env_color = vec4(0,0,0,0);
opt_texture_min_filter = 0;
opt_texture_mag_filter = 0;
opt_texture_mipmap_mode = 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) );
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();
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();
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')\n",
texture_env_modes[opt_texture_env_mode].label);
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_Scale(1,-1,1)
* pMatrix_Frustum(-.8,.8,-.8/aspect,.8/aspect,1,5000) );
pColor white(1,1,1);
pColor red(1,0,0);
pColor gray(0x303030);
pColor dark(0);
pColor ambient_color(0x555555);
const pColor lsu_spirit_gold(0xf9b237);
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 0
if ( opt_blend )
{
glEnable(GL_BLEND);
}
else
{
glDisable(GL_BLEND);
}
if ( opt_alpha )
{
glEnable(GL_ALPHA_TEST);
}
else
{
glDisable(GL_ALPHA_TEST);
}
glAlphaFunc(GL_GREATER,0.1);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
#endif
if ( sampler_dirty )
{
sampler_dirty = false;
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,
false, 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.destroy();
if ( pipe_sphere ) pipe_sphere.destroy();
}
if ( !pipe_lonely )
{
pipe_lonely
.init( vh.qs )
.texture_on()
.use_uni_light( uni_light )
.uniform_bind( uni_texture_mode, "BIND_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)
.topology_set( vk::PrimitiveTopology::eTriangleList )
.create();
}
if ( !bset_lonely || lonely_triangle_stale )
{
bset_lonely.reset( pipe_lonely );
lonely_triangle_stale = false;
pCoor p1( 9.5, -4, -12. + tri_tilt );
pCoor p2( 9, 6, -7 );
pCoor p3( 0, 5, -7 );
pTCoor tc1( tex_scale * 1.0, tex_scale * 1.0 );
pTCoor tc2( tex_scale * 1.0, tex_scale * 0.0 );
pTCoor tc3( tex_scale * 0.0, tex_scale * 0.0 );
bset_lonely << p1 << p2 << p3;
bset_lonely << tc1 << tc2 << tc3;
bset_lonely << color_green << color_red << color_blue;
pNorm tnorm = cross(p1,p2,p3);
bset_lonely << tnorm << tnorm << tnorm;
bset_lonely.to_dev();
}
transform.use_global_for(pipe_lonely);
pipe_lonely.use_texture(sampler, texture_id_syllabus );
pipe_lonely.record_draw(cb, bset_lonely);
if ( !pipe_sphere )
{
pipe_sphere
.init( vh.qs )
.texture_on()
.color_uniform_set(lsu_spirit_gold,color_red)
.use_uni_light( uni_light )
.shader_inputs_info_set<pCoor,pTCoor>()
.uniform_bind( uni_texture_mode, "BIND_TEXTURE_MODE" )
.shader_code_set
("demo-08-shdr.cc", "vs_main_sphere();", nullptr, "fs_main();",
shader_gl_defines)
.topology_set( vk::PrimitiveTopology::eTriangleStrip )
.create();
}
if ( !bset_sphere )
{
bset_sphere.reset( pipe_sphere );
const int slices = 40;
const double delta_eta = M_PI / slices;
for ( double eta = 0; eta < M_PI - 0.0001 - delta_eta; eta += delta_eta )
{
const double eta1 = eta + delta_eta;
const float y0 = cos(eta), y1 = cos(eta1);
const double slice_r0 = sin(eta), slice_r1 = sin(eta1);
const double delta_theta = delta_eta;
const float t0 = eta / M_PI;
const float t1 = eta1 / M_PI;
bset_sphere << pCoor( slice_r1, y1, 0 )
<< pTCoor( 1, t1 );
bset_sphere << pCoor( slice_r0, y0, 0 )
<< pTCoor( 1, t0 );
for ( double theta = 0; theta < 2 * M_PI; theta += delta_theta )
{
const double theta1 = theta + delta_theta;
bset_sphere
<< pCoor( slice_r1 * cos(theta1), y1, slice_r1 * sin(theta1) )
<< pTCoor( 1 - theta1 / ( 2 * M_PI ), t1 );
bset_sphere
<< pCoor( slice_r0 * cos(theta1), y0, slice_r0 * sin(theta1) )
<< pTCoor( 1 - theta1 / ( 2 * M_PI ), t0 );
}
}
bset_sphere.to_dev();
}
pipe_sphere.use_texture(sampler, texture_id_image );
transform.global_from_local_set_for
( pMatrix_Translate( sphere_location )
* pMatrix_Scale(sphere_size)
* pMatrix_Rotation( pVect(0,1,0), M_PI / 3 ),
pipe_sphere );
pipe_sphere.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': 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 'r': case 'R': opt_recompute = !opt_recompute; 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;
}