#include <gp/coord.h>
#include <gp/colors.h>
#include "frame_buffer.h"
enum Topology { Topology_Individual, Topology_Strip, Topology_Fan };
class Our_3D {
public:
Our_3D(pFrame_Buffer& fbp)
:frame_buffer(fbp)
{
vtx_colors = nullptr;
vtx_coors = nullptr;
vtx_normals = nullptr;
topology = Topology_Individual;
opt_tri_normals_always = false;
};
Our_3D& transform_eye_from_object_set(const pMatrix& m)
{
eye_from_object = m;
return *this;
}
Our_3D& transform_clip_from_eye_set(const pMatrix& m)
{
clip_from_eye = m;
return *this;
}
Our_3D& light_location_set(const pCoor c)
{
light_location = c;
return *this;
}
Our_3D& light_color_set(const pColor c)
{
light_color = c;
return *this;
}
Our_3D& vtx_normals_set()
{
vtx_normals = nullptr;
return *this;
}
Our_3D& vtx_normals_set(vector<pVect4>& c)
{
vtx_normals = c.data();
return *this;
}
Our_3D& vtx_coors_set(vector<pCoor>& c)
{
vtx_coors = &c;
return *this;
}
Our_3D& vtx_colors_set(vector<pColor>& c)
{
vtx_colors = &c;
return *this;
}
Our_3D& topology_set
( Topology top = Topology_Individual, int n_vertices = 0 )
{
topology = top;
topology_n_vertices = top == Topology_Individual ? 3 : n_vertices;
return *this;
}
Our_3D& rendering_stats_reset()
{
stats_n_triangles = stats_n_vertices = stats_n_fragments = 0;
return *this;
}
Our_3D& draw_rasterization();
pFrame_Buffer& frame_buffer;
pMatrix eye_from_object, clip_from_eye;
pCoor light_location;
pColor light_color;
vector<pCoor> *vtx_coors;
pVect4 *vtx_normals;
vector<pColor> *vtx_colors;
Topology topology;
int topology_n_vertices;
int64_t stats_n_triangles, stats_n_vertices, stats_n_fragments;
bool opt_tri_normals_always;
bool opt_tryout1, opt_tryout2;
};
enum HW3_Variation { HW3_Triangles, HW3_Fans, HW3_ENUM_SIZE };
class World : public World_Base {
public:
World(pFrame_Buffer& fb):World_Base(fb),gc(fb){}
Our_3D gc;
virtual void render(bool first_time)
{
if ( first_time ) init_scene();
render_scene();
}
void init_scene();
void render_scene();
pCoor eye_location, light_location;
pColor light_color;
pCoor *adj_location;
pVect eye_direction;
bool opt_tryout1,opt_tryout2;
int opt_hw3_variation;
bool opt_hw3_p1_strips;
float dod_angle1, dod_angle2;
};
const char* const hw3_variation_str[] =
{ "P2: TRIANGLES", "P3: FANS" };
void
World::init_scene()
{
eye_location = pCoor(-2.6,.5,7.9);
light_location = pCoor(-4.1,.9,5.1);
eye_direction = pVect(.1,0,-1);
adj_location = &eye_location;
opt_tryout1 = opt_tryout2 = false;
gc.opt_tryout1 = opt_tryout1;
gc.opt_tryout2 = opt_tryout2;
light_color = color_white * 100;
dod_angle1 = dod_angle2 = 0;
opt_hw3_variation = 0;
opt_hw3_p1_strips = false;
}
pVect vec_rand() {return pVect(drand48(),drand48(),drand48());}
void
World::render_scene()
{
pFrame_Buffer& frame_buffer = gc.frame_buffer;
switch ( frame_buffer.keyboard_key ) {
case FB_KEY_LEFT: adj_location->x -= 0.1; break;
case FB_KEY_RIGHT: adj_location->x += 0.1; break;
case FB_KEY_PAGE_UP: adj_location->y += 0.1; break;
case FB_KEY_PAGE_DOWN: adj_location->y -= 0.1; break;
case FB_KEY_UP: adj_location->z -= 0.1; break;
case FB_KEY_DOWN: adj_location->z += 0.1; break;
case 'a': case 'A': opt_hw3_variation++;
if ( opt_hw3_variation == HW3_ENUM_SIZE ) opt_hw3_variation = 0;
break;
case '1': opt_hw3_p1_strips = !opt_hw3_p1_strips; break;
case 'e': case 'E': adj_location = &eye_location; break;
case 'l': case 'L': adj_location = &light_location; break;
case 'v': case 'V': gc.opt_tri_normals_always = !gc.opt_tri_normals_always;
break;
case 'y': gc.opt_tryout1 = opt_tryout1 = !opt_tryout1; break;
case 'Y': gc.opt_tryout2 = opt_tryout2 = !opt_tryout2; break;
case 'w': dod_angle1 += numbers::pi/30; break;
case 'W': dod_angle1 -= numbers::pi/30; break;
case 'q': dod_angle2 += numbers::pi/30; break;
case 'Q': dod_angle2 -= numbers::pi/30; break;
case '+': case '=':
if ( !frame_buffer.keyboard_control ) light_color *= 1.1;
break;
case '-': case '_':
if ( !frame_buffer.keyboard_control ) light_color *= 1/1.1;
break;
}
gc.frame_buffer.fbprintf
( "Light Location [ %.2f, %.2f, %.2f ], Light Intensity %.2f ('+-') "
"Eye Location [ %.2f, %.2f, %.2f ]\n",
light_location.x, light_location.y, light_location.z,
light_color.r,
eye_location.x, eye_location.y, eye_location.z );
frame_buffer.fbprintf
("HW3 P1: %s ('1') HW3 Dodec %s ('a') Spin ('wWqQ') "
"Tryout1 %s ('y') Tryout2 %s ('Y')\n",
opt_hw3_p1_strips ? "STR" : "TRI",
hw3_variation_str[opt_hw3_variation],
opt_tryout1 ? "ON " : "OFF", opt_tryout2 ? "ON " : "OFF");
gc.light_location_set( light_location ).light_color_set( light_color );
const float width_m = 1.6; const float qn = 1; const uint win_width = frame_buffer.width_get();
const uint win_height = frame_buffer.height_get();
const float aspect = 1.0 * win_width / win_height;
const float height_m = width_m / aspect;
pMatrix_Translate center_eye(-eye_location);
pMatrix_Rotation rotate_eye(eye_direction,pVect(0,0,-1));
pMatrix eye_from_object = rotate_eye * center_eye;
gc.transform_eye_from_object_set(eye_from_object);
pMatrix_Frustum clip_from_eye
( -width_m/2, width_m/2, -height_m/2, height_m/2, qn, 5000 );
gc.transform_clip_from_eye_set(clip_from_eye);
vector<pCoor> coors_os;
vector<pColor> colors;
pVect light_to_eye( light_location, eye_location );
float l_sz = 0.1;
pVect lax = cross( pVect( 0, 1, 0 ), light_to_eye ).normal() * l_sz;
pVect lay = cross( light_to_eye, lax ).normal() * l_sz;
coors_os << light_location
<< light_location - lax - lay << light_location + lax - lay;
colors << color_yellow << color_yellow << color_yellow;
coors_os << pCoor(-7,2,-2) << pCoor(-7,4,-2) << pCoor(-5,2,-2);
colors << color_red << color_green << color_blue;
coors_os << pCoor(-7,4,-2) << pCoor(-5,4,-2) << pCoor(-5,2,-2);
colors << color_lsu_spirit_gold << color_lsu_spirit_gold
<< color_lsu_spirit_gold;
gc.vtx_normals_set(); gc.topology_set(Topology_Individual); gc.vtx_coors_set(coors_os); gc.vtx_colors_set(colors); gc.draw_rasterization();
coors_os.clear(); colors.clear();
if ( opt_hw3_p1_strips )
{
gc.topology_set( Topology_Strip );
coors_os << pCoor(-5,1,-2) << pCoor(-7,1,-2);
colors << color_lsu_spirit_purple << color_lsu_spirit_purple;
coors_os << pCoor(-5,-1,-2);
colors << color_lsu_spirit_purple;
coors_os << pCoor(-7,-1,-2);
colors << color_lsu_spirit_gold;
coors_os << pCoor(-6,-2,-2);
colors << color_green;
coors_os << pCoor(-7,-2,-2);
colors << color_salmon;
#if 0
coors_os << pCoor(-6,-2,-2) << pCoor(-7,-2,-2) << pCoor(-7,-1,-2);
colors << color_salmon << color_salmon << color_salmon;
coors_os << pCoor(-7,1,-2) << pCoor(-5,1,-2) << pCoor(-5,-1,-2);
colors << color_lsu_spirit_purple << color_lsu_spirit_purple
<< color_lsu_spirit_purple;
coors_os << pCoor(-7,-1,-2) << pCoor(-7,1,-2) << pCoor(-5,-1,-2);
colors << color_lsu_spirit_gold << color_lsu_spirit_gold
<< color_lsu_spirit_gold;
coors_os << pCoor(-7,-1,-2) << pCoor(-6,-2,-2) << pCoor(-5,-1,-2);
colors << color_green << color_green << color_green;
#endif
}
else
{
gc.topology_set( Topology_Individual );
coors_os << pCoor(-6,-2,-2) << pCoor(-7,-2,-2) << pCoor(-7,-1,-2);
colors << color_salmon << color_salmon << color_salmon;
coors_os << pCoor(-7,1,-2) << pCoor(-5,1,-2) << pCoor(-5,-1,-2);
colors << color_lsu_spirit_purple << color_lsu_spirit_purple
<< color_lsu_spirit_purple;
coors_os << pCoor(-7,-1,-2) << pCoor(-7,1,-2) << pCoor(-5,-1,-2);
colors << color_lsu_spirit_gold << color_lsu_spirit_gold
<< color_lsu_spirit_gold;
coors_os << pCoor(-7,-1,-2) << pCoor(-6,-2,-2) << pCoor(-5,-1,-2);
colors << color_green << color_green << color_green;
}
gc.vtx_normals_set(); gc.vtx_coors_set(coors_os); gc.vtx_colors_set(colors); gc.rendering_stats_reset();
gc.draw_rasterization(); pStringF
p1stats("P1 Shape: %ld vertices, %ld triangles, %ld fragments.",
gc.stats_n_vertices, gc.stats_n_triangles, gc.stats_n_fragments);
coors_os.clear(); colors.clear();
vector<pVect4> normals_os;
pCoor cyl_center(1,-1,-3);
pVect cyl_axis( 0, 0.5, 0.05 );
float cyl_radius = 4;
pNorm ax(0,-cyl_axis.z,cyl_axis.y); pNorm ay = cross( cyl_axis, ax );
int n_segs = 20; float delta_theta = 2 * M_PI / n_segs;
for ( int i=0; i<=n_segs; i++ )
{
float theta = i * delta_theta;
pVect4 n = ax * cosf(theta) + ay * sinf(theta);
pCoor c = cyl_center + cyl_radius * n;
coors_os << c + cyl_axis << c;
normals_os << n << n;
colors << color_cyan << color_cyan;
}
gc.vtx_normals_set(normals_os); gc.topology_set(Topology_Strip); gc.vtx_coors_set(coors_os);
gc.vtx_colors_set(colors);
gc.draw_rasterization();
gc.rendering_stats_reset();
{
pCoor center = cyl_center + pVect(0,1.5,0);
pMatrix_Rotation dod_spin1( pNorm(0.1,1,0.1), dod_angle1 );
pMatrix_Rotation dod_spin2( pNorm(1,-0.1,0), dod_angle2 );
pMatrix dod_spin = dod_spin1 * dod_spin2;
pVect ax = dod_spin.col_get(0);
pVect ay = dod_spin.col_get(1);
pVect az = dod_spin.col_get(2);
const float len_edge = 2;
const float rc_pentagon = len_edge / ( 2 * sinf(numbers::pi/5) );
float ri = len_edge * sqrtf( sqrt(5) * pow(numbers::phi,5) / 20 );
pCoor center_pentagon_0 = center - ri * az;
float theta_dihedral [[maybe_unused]] = 2 * atanf( numbers::phi );
vector< pCoor > coors;
const float d_theta = 2 * numbers::pi / 5;
for ( int i=0; i<5; i++ )
{
const float theta = d_theta * i;
pVect dir = cosf(theta) * ax + sinf(theta) * ay;
pCoor pos = center_pentagon_0 + rc_pentagon * dir;
coors << pos;
}
for ( int i=0; i<5; i++ )
{
pCoor p0 = coors[i], p1 = coors[(i+1)%5];
pMatrix_Translate to_p0( p0 );
pMatrix_Rotation rot2( pNorm(p0,p1), theta_dihedral );
pMatrix_Translate to_Origin( pVect( p0, pCoor(0,0,0) ) );
pMatrix m = to_p0 * rot2 * to_Origin;
for ( int j=0; j<5; j++ ) coors << m * coors[j];
}
pMatrix_Translate from_ctr( pVect(center,pCoor(0,0,0)) );
pMatrix_Translate to_ctr( center );
pMatrix_Rotation rot3(ax,numbers::pi);
pMatrix_Rotation rot4(az,0.2*numbers::pi);
pMatrix ms = to_ctr * rot4 * rot3 * from_ctr;
const int n_vtx_demi = coors.size();
for ( int i=0; i<n_vtx_demi; i++ ) coors << ms * coors[i];
coors_os.clear();
colors.clear();
gc.vtx_normals_set();
switch ( opt_hw3_variation ){
case HW3_Triangles:
for ( size_t i=0; i<coors.size(); i+=5 )
for ( int j=1; j<4; j++ )
coors_os << coors[ i ] << coors[ i + j ] << coors[ i + j + 1 ];
gc.topology_set(Topology_Individual);
break;
case HW3_Fans:
gc.topology_set(Topology_Fan,5); coors_os = coors;
break;
default:
assert( false );
}
auto_ for ( auto _: coors_os ) colors << color_light_slate_gray;
gc.vtx_coors_set(coors_os);
gc.vtx_colors_set(colors);
gc.draw_rasterization();
}
gc.frame_buffer.fbprintf
("%s Dodecahedron: %ld vertices, %ld triangles, %ld fragments.\n",
p1stats.s,
gc.stats_n_vertices, gc.stats_n_triangles, gc.stats_n_fragments);
}
Our_3D&
Our_3D::draw_rasterization()
{
const uint win_width = frame_buffer.width_get();
const uint win_height = frame_buffer.height_get();
pMatrix_Translate recenter(pVect(1,1,0));
pMatrix_Scale scale( win_width/2, win_height/2, 1 );
pMatrix window_from_clip = scale * recenter;
pMatrix ws_from_os = window_from_clip * clip_from_eye * eye_from_object;
float* const zbuffer = frame_buffer.buffer_depth_get();
auto& coors_os = *vtx_coors;
auto& colors = *vtx_colors;
const bool topology_strip = topology == Topology_Strip;
const bool topology_fan [[maybe_unused]] = topology == Topology_Fan;
const ssize_t stop = coors_os.size() - 2;
size_t inc = topology == Topology_Individual ? 3 : 1;
stats_n_vertices += coors_os.size();
for ( ssize_t i=0; i<stop; i += inc )
{
ssize_t idx_0 =
topology_fan ? i / topology_n_vertices * topology_n_vertices : i;
ssize_t idx_1 = i + 1;
ssize_t idx_2 = i + 2;
if ( topology_fan && idx_2 - idx_0 >= topology_n_vertices ) continue;
pCoor o0 = coors_os[idx_0], o1 = coors_os[idx_1], o2 = coors_os[idx_2];
pCoor_Homogenized_Keep_w w0( ws_from_os * o0 );
pCoor_Homogenized_Keep_w w1( ws_from_os * o1 );
pCoor_Homogenized_Keep_w w2( ws_from_os * o2 );
pColor c0 = colors[idx_0], c1 = colors[idx_1], c2 = colors[idx_2];
stats_n_triangles++;
pNorm tn = cross(o0,o1,o2);
vector<pVect4> npd;
const bool use_normals = !opt_tri_normals_always && vtx_normals;
if ( use_normals )
for ( auto idx: { idx_0, idx_1, idx_2 } ) npd << vtx_normals[idx];
pVect4* np = use_normals ? &npd[0] : nullptr;
pVect w20(w2,w0), w21(w2,w1);
const float db0 = 0.5/max(fabs(w20.x),fabs(w20.y));
const float db1 = 0.5/max(fabs(w21.x),fabs(w21.y));
if ( db0 * db1 < 1e-7 ) continue;
const float w0ow2 = w0.w/w2.w, w0ow1 = w0.w/w1.w;
const float w1ow2 = w1.w/w2.w, w1ow0 = w1.w/w0.w;
for ( float b0=0; b0<=1; b0 += db0 )
for ( float b1=0; b1<=1-b0; b1 += db1 )
{
const float b2 = 1 - b0 - b1;
pCoor wf = b0 * w0 + b1 * w1 + b2 * w2;
stats_n_fragments++;
if ( uint(wf.x) >= win_width || uint(wf.y) >= win_height ) continue;
const size_t idx = wf.x + int(wf.y) * win_width;
float zinv = wf.z;
if ( zinv < 0 || zinv > 1 ) continue;
if ( zbuffer[ idx ] < zinv ) continue;
zbuffer[ idx ] = zinv;
float bc0 = b0 / ( b2 * w0ow2 + b1 * w0ow1 + b0 );
float bc1 = b1 / ( b2 * w1ow2 + b0 * w1ow0 + b1 );
float bc2 = 1 - bc0 - bc1;
pCoor of = bc0 * o0 + bc1 * o1 + bc2 * o2;
pColor color = topology_strip ? c2 : bc0 * c0 + bc1 * c1 + bc2 * c2;
pVect4 n = np ? bc0 * np[0] + bc1 * np[1] + bc2 * np[2] : tn;
pNorm f_to_l(of,light_location);
float phase = fabs(dot(n,f_to_l));
pColor lighted_color = phase / f_to_l.mag_sq * light_color * color;
frame_buffer[ idx ] = lighted_color.int_rgb();
}
}
return *this;
}
int
main(int argc, char **argv)
{
pFrame_Buffer demo_frame_buffer(argc,argv);
WRender render( demo_frame_buffer );
World our_world( demo_frame_buffer );
WRENDER_INSERT(render, our_world);
render.run();
return 0;
}