// g++ -std=c++11 main.cpp -lglut -lGLEW
#include <GL/glew.h>
#include <GL/glut.h>
/* Using the standard output for fprintf */
#include <iostream>
#include <functional>
#include <memory>
#include <vector>
#include <stdexcept>
#include <map>
#include <vector>
#include <cmath>
#include <algorithm>
#include <iterator>
#include <time.h>
using namespace std;
GLint attribute_coord3d, attribute_v_color;
GLuint vbo_triangle, vbo_triangle_colors;
GLuint program;
const int NUM_ITERATIONS = 5;
const int NUM_TRIANGLES = pow( 4.0, NUM_ITERATIONS );
int modelDrawn = 0;
struct vec3
{
float x;
float y;
float z;
vec3( float x1, float y1, float z1 ) : x( x1 ), y( y1 ), z( z1 )
{
}
vec3( ) : x( 0 ), y( 0 ), z( 0 )
{
}
vec3& operator+=(const vec3 & other )
{
x += other.x;
y += other.y;
z += other.z;
}
vec3 operator/=(const float)
{
}
vec3 operator+(const vec3 & other ) const
{
return vec3( x + other.x, y + other.y, z + other.z );
}
vec3 operator/(const float &denom ) const
{
return vec3( x / denom, y / denom, z / denom );
}
vec3 operator*(const float mult)
{
return vec3( x*mult, y*mult, z * mult );
}
bool operator<(const vec3 & n ) const
{
if( x != n.x )
return x < n.x;
else if( y != n.y )
return y < n.y;
else if( z != n.z )
return z < n.z;
return length( ) < n.length( );
}
bool operator==(const vec3 & n ) const
{
return x == n.x && y == n.y && z == n.z;
}
float length( ) const
{
return sqrt( pow( x, 2 ) + pow( y, 2 ) + pow( z, 2 ) );
}
void Print( ) const
{
std::cout << "(" << x << "," << y << "," << z << ")" << std::endl;
}
};
//container for gasket verts
std::vector<vec3> triangle_vertices;
std::vector<vec3> triangle_colors_vec;
float getRandom( float max, float min )
{
return ((float(rand( ) ) / float(RAND_MAX ) ) * ( max - ( min ) ) ) + ( min );
}
float getVectorDistance( vec3 a, vec3 b )
{
return sqrt( pow( ( a.x - b.x ), 2 ) + pow( ( a.y - b.y ), 2 ) + pow( ( a.z - b.z ), 2 ) );
}
vec3 randVector( );
struct Triangle
{
vec3 a;
vec3 b;
vec3 c;
Triangle( vec3 a1, vec3 b1, vec3 c1 ) : a( a1 ), b( b1 ), c( c1 )
{
}
};
vec3 getMidpoint( const vec3& a, const vec3& b )
{
const float SCALE = 0.1;
static std::map<std::pair<vec3, vec3>, vec3> memo;
auto foundResult = memo.find( std::make_pair( a, b ) );
if( foundResult != memo.end( ) )
{
return foundResult->second;
}
auto result = ( a + b ) / 2;
auto variance = 0.1 * getVectorDistance( a, b );
auto randX = getRandom( result.x + variance, result.x - variance );
auto randY = getRandom( result.y + variance, result.y - variance );
auto randZ = getRandom( result.z + variance, result.z - variance );
result = vec3( randX, randY, randZ );
memo.insert( std::make_pair( std::make_pair( a, b ), result ) );
memo.insert( std::make_pair( std::make_pair( b, a ), result ) );
return result;
}
void makeGasket( Triangle t, int count )
{
if( count > 0 )
{
auto d = getMidpoint( t.a, t.b );
auto e = getMidpoint( t.b, t.c );
auto f = getMidpoint( t.a, t.c );
makeGasket( Triangle( t.a, d, f ), count - 1 );
makeGasket( Triangle( d, t.b, e ), count - 1 );
makeGasket( Triangle( f, d, e ), count - 1 );
makeGasket( Triangle( f, e, t.c ), count - 1 );
}
else
{
triangle_vertices.push_back( t.a );
triangle_vertices.push_back( t.b );
triangle_vertices.push_back( t.c );
}
}
void createColors( )
{
for( int i = 0; i < triangle_vertices.size( ); i++ )
{
float random = getRandom( 0.5, 1.0 );
static std::map<vec3, float> randColor;
auto foundResult = randColor.find( triangle_vertices[i] );
if( foundResult != randColor.end( ) )
{
random = foundResult->second;
}
else
randColor.insert( std::make_pair( triangle_vertices[i], random ) );
triangle_colors_vec.push_back( vec3( random, random, random ) );
}
}
vec3 triangle_verts[15000];
vec3 triangle_colors[15000];
void onDisplay( )
{
srand( time( NULL ) );
/* Clear the background as white */
glClearColor( 0.0, 0.0, 0.0, 0.0 );
glEnable( GL_DEPTH_TEST );
glDepthFunc( GL_LESS );
glCullFace( GL_FRONT );
glEnable( GL_CULL_FACE );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
/* Tell OpenGL which program to use*/
glUseProgram( program );
if( modelDrawn == 0 )
{
modelDrawn = 1;
vec3 verts[] ={
vec3( 0.0, 1.0, 0.0 ),
vec3( 0.943, -0.333, 0.0 ),
vec3( -0.471, -0.333, 0.816 ),
vec3( -0.471, -0.333, -0.816 )
};
Triangle initialTri = Triangle(
verts[0],
verts[2],
verts[1]
);
Triangle initialTri2 = Triangle(
verts[0],
verts[1],
verts[3]
);
Triangle initialTri3 = Triangle(
verts[0],
verts[3],
verts[2]
);
Triangle initialTri4 = Triangle(
verts[1],
verts[2],
verts[3]
);
makeGasket( initialTri, NUM_ITERATIONS );
makeGasket( initialTri2, NUM_ITERATIONS );
makeGasket( initialTri3, NUM_ITERATIONS );
makeGasket( initialTri4, NUM_ITERATIONS );
std::copy( triangle_vertices.begin( ), triangle_vertices.end( ), triangle_verts );
createColors( );
std::copy( triangle_colors_vec.begin( ), triangle_colors_vec.end( ), triangle_colors );
}
/* Setup vertex data */
glBindBuffer( GL_ARRAY_BUFFER, vbo_triangle );
glBufferData( GL_ARRAY_BUFFER, sizeof (triangle_verts ), triangle_verts, GL_STATIC_DRAW );
auto attribute_coord3d = glGetAttribLocation( program, "coord3d" );
glEnableVertexAttribArray( attribute_coord3d );
/* Describe our vertices array to OpenGL (it can't guess its format automatically) */
glVertexAttribPointer(
attribute_coord3d, // attribute
3, // number of elements per vertex, here (x,y,z)
GL_FLOAT, // the type of each element
GL_FALSE, // take our values as-is
0, // no extra data between each position
0 // vec3er to the C array
);
/* Push each element in buffer_vertices to the vertex shader */
glBindBuffer( GL_ARRAY_BUFFER, vbo_triangle_colors );
glBufferData( GL_ARRAY_BUFFER, sizeof (triangle_colors ), triangle_colors, GL_STATIC_DRAW );
auto attribute_v_color = glGetAttribLocation( program, "v_color" );
glEnableVertexAttribArray( attribute_v_color );
glVertexAttribPointer(
attribute_v_color, // attribute
3, // number of elements per vertex, here (r,g,b)
GL_FLOAT, // the type of each element
GL_FALSE, // take our values as-is
0, // no extra data between each position
0 // offset of first element
);
glDrawArrays( GL_TRIANGLES, 0, NUM_TRIANGLES * 3 * 4 );
glDisableVertexAttribArray( attribute_coord3d );
glDisableVertexAttribArray( attribute_v_color );
/* Display the result */
glutSwapBuffers( );
}
void rotate( int value )
{
float angle = 0.0175; // 1 degree
//float angle = glutGet(GLUT_ELAPSED_TIME) / 1000.0 * 0.001;
float rotation_matrix[] ={
cosf( angle ), 0, sinf( angle ),
0, 1, 0,
-1.0 * sinf( angle ), 0, cosf( angle ),
};
float rotation_matrix_x[] ={
1, 0, 0,
0, cosf( angle ), -1.0 * sinf( angle ),
0, sinf( angle ), cosf( angle ),
};
float rotation_matrix_z[] ={
cosf( angle ), -1.0 * sin( angle ), 0,
sinf( angle ), cosf( angle ), 0,
0, 0, 1,
};
vec3 tempVec;
for( int i = 0; i < triangle_vertices.size( ); i++ )
{
tempVec.x = rotation_matrix[0] * triangle_verts[i].x + rotation_matrix[1] * triangle_verts[i].y + rotation_matrix[2] * triangle_verts[i].z;
tempVec.y = rotation_matrix[3] * triangle_verts[i].x + rotation_matrix[4] * triangle_verts[i].y + rotation_matrix[5] * triangle_verts[i].z;
tempVec.z = rotation_matrix[6] * triangle_verts[i].x + rotation_matrix[7] * triangle_verts[i].y + rotation_matrix[8] * triangle_verts[i].z;
triangle_verts[i] = tempVec;
}
glutTimerFunc( 10, rotate, 1 );
glutPostRedisplay( );
}
void CheckStatus( GLuint obj )
{
GLint status = 0;
if( glIsShader( obj ) ) glGetShaderiv( obj, GL_COMPILE_STATUS, &status );
if( glIsProgram( obj ) ) glGetProgramiv( obj, GL_LINK_STATUS, &status );
if( status == GL_TRUE ) return;
GLint len = 0;
if( glIsShader( obj ) ) glGetShaderiv( obj, GL_INFO_LOG_LENGTH, &len );
if( glIsProgram( obj ) ) glGetProgramiv( obj, GL_INFO_LOG_LENGTH, &len );
vector< char > log( len );
if( glIsShader( obj ) ) glGetShaderInfoLog( obj, len, NULL, &log[0] );
if( glIsProgram( obj ) ) glGetProgramInfoLog( obj, len, NULL, &log[0] );
if( glIsShader( obj ) ) glDeleteShader( obj );
if( glIsProgram( obj ) ) glDeleteProgram( obj );
throw runtime_error( string( log.begin( ), log.end( ) ) );
}
GLuint LoadShader( GLenum type, const string& aShaderSrc )
{
GLuint shader = glCreateShader( type );
const char* srcPtr = aShaderSrc.c_str( );
glShaderSource( shader, 1, &srcPtr, NULL );
glCompileShader( shader );
CheckStatus( shader );
return shader;
}
GLuint LoadProgram( const string& aVertSrc, const string& aFragSrc )
{
GLuint vert = LoadShader( GL_VERTEX_SHADER, aVertSrc );
GLuint frag = LoadShader( GL_FRAGMENT_SHADER, aFragSrc );
GLuint program = glCreateProgram( );
glAttachShader( program, vert );
glAttachShader( program, frag );
glLinkProgram( program );
CheckStatus( program );
return program;
}
#define GLSL(version, shader) "#version " #version "\n" #shader
const GLchar* vert = GLSL
(
120,
attribute vec3 coord3d;
attribute vec3 v_color;
varying vec3 f_color;
void main( void )
{
gl_Position = vec4( coord3d, 1.0 );
f_color = v_color;
}
);
const GLchar* frag = GLSL
(
120,
varying vec3 f_color;
void main( void )
{
gl_FragColor = vec4( f_color.x, f_color.y, f_color.z, 1.0 );
}
);
int main( int argc, char* argv[] )
{
/* Glut-related initialising functions */
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH );
glutInitWindowSize( 640, 480 );
glutCreateWindow( "Hello Triangle!" );
/* Extension wrangler initialising */
GLenum glew_status = glewInit( );
if( glew_status != GLEW_OK )
{
fprintf( stderr, "Error: %s\n", glewGetErrorString( glew_status ) );
return EXIT_FAILURE;
}
/* When all init functions runs without errors,
the program can initialise the resources */
try
{
program = LoadProgram( vert, frag );
glGenBuffers( 1, &vbo_triangle );
glGenBuffers( 1, &vbo_triangle_colors );
glutDisplayFunc( onDisplay );
//glutIdleFunc( idle );
glutTimerFunc( 10, rotate, 1 );
glutMainLoop( );
}
catch( std::exception& e )
{
std::cerr << e.what( );
}
return EXIT_SUCCESS;
}