// g++ -std=c++11 main.cpp -lglut -lGLEW #include #include /* Using the standard output for fprintf */ #include #include #include #include #include #include #include #include #include #include #include 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 triangle_vertices; std::vector 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, 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 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; }