Ongoing port of Three.cpp

Standard

This is an ongoing process of porting Three.js to C++ / OpenGL. The main idea is not to create the fastest 3D engine out there, but to create a 3D engine that’s simple enough to instantiate, so that other field of computing could use it rapidly for prototyping. Thus reducing the unnecessary hoops for OpenGL newcomers when it comes to thing such as context creation, loading shaders, etc.

I’m not a 3D programmer, I wanted to have a 3D library that I can use to show the results of SLAM / PTAM or Bundle Adjustment, while most sample codes out there use pre OpenGL 3.0 for visualization, and most of them implement their own 3D viewers.

Inspired by three.js’s simplistic style, I decided to port three.js to c++, hopefully retain all the simplicity of three.js. It’s still in a very early stage, but here’s a sample of rendering a sphere, and a textured cube with multiple lights and using Blinn-Phong shader. Shadow is not implemented yet.

Below is the source code that’s used to create the video above using my Three.cpp library. As mentioned before, it’s aimed to recreate the simplicity of three.js.

int main(int argc, const char * argv[])
{
    Renderer renderer;
    renderer.init( "", 1600 * 3 / 4, 900 * 3 / 4 );
    renderer.shaderLib = make_shared<ShaderLib::Shader>(ShaderLib::phong);
    
    /* Create scene */
    auto scene = Scene::create();
    scene->fog = Fog::create( Color(0x72645b / 2), 2.0, 15.0 );
    
    /* Create camera */
    auto camera = PerspectiveCamera::create( 50.0, renderer.aspectRatio, 0.001, 15.0 );
    camera->position = glm::vec3(0.0, 1.5, 5.5);
    camera->lookAt( 0.0, 1.5, 0.0 );
    
    
    /* Add our composite object */
    auto composite = createCompositeObject();
    scene->add( composite );
    
    
    /* Create ground plane */
    auto plane_mesh = Mesh::create( PlaneGeometry::create(20.0f, 1),
                                    MeshPhongMaterial::create(0x101010, 0x666666, 0x000000, 0x101010 ) );
    plane_mesh->rotateX(-90.0);
    scene->add( plane_mesh );
    
    
    /* Create directional light */
    auto dir_light = DirectionalLight::create(Color(0x99CCFF), 1.35, glm::vec3(3.0, 1.0, 3.0) );
    scene->add( dir_light );
    
    
    /* Create an ambient light */
    scene->add( AmbientLight::create(Color(0x101010) ));
    
    /* Create a spot light */
    auto spot_light = SpotLight::create( Color(0xFFFFFF), 3.0, 10.0, 20.0, 1.0 );
    spot_light->translate(0.0f, 5.0f, 0.0f);
    scene->add( spot_light );
    
    /* Create a post render callback function that allow us to rotate the light and objects */
    float light_rotation_1 = 0.0;
    bool rotate_objects = false;
    
    renderer.setPostRenderCallbackHandler( [&](){
        /* rotate the directional light */
        dir_light->position.x = 3.0 * cosf( light_rotation_1 );
        dir_light->position.z = 3.0 * sinf( light_rotation_1 );
        
        light_rotation_1 += 0.01;
        
        if( rotate_objects )
            composite->rotateY(-1.0f);
        
        /* Widen the spot light */
        spot_light->angle = Math::clamp(spot_light->angle + 0.01f, 15.0, 300.0);
    });
    
    /* Override key callback handler */
    renderer.setKeyCallbackHandler([&](GLFWwindow *window, int key, int scancode, int action, int mod) {
        if( action == GLFW_PRESS ) {
            switch ( key) {
                case GLFW_KEY_ESCAPE: case GLFW_KEY_Q:
                    glfwSetWindowShouldClose( window, GL_TRUE );
                    return;
                    
                case GLFW_KEY_W: { /* Toggle wireframe */
                    PTR(Mesh) sphere = DOWNCAST(composite->getObjectByID(sphere_id, true), Mesh);
                    sphere->material->wireframe = !sphere->material->wireframe;
                    
                    PTR(Mesh) cube = DOWNCAST(composite->getObjectByID(cube_id, true), Mesh);
                    cube->material->wireframe = !cube->material->wireframe;
                    
                    break;
                }
                    
                case GLFW_KEY_B: { /* Toggle bounding box visibility */
                    PTR(Mesh) bounding = DOWNCAST(composite->getObjectByID(bounding_box_id, true), Mesh);
                    bounding->visible = !bounding->visible;
                    break;
                }
                    
                case GLFW_KEY_R: /* Toggle rotation */
                    rotate_objects = !rotate_objects;
                    break;
                    
                default:
                    break;
            }
        }
    });
    
    
    renderer.gammaInput  = true;
    renderer.gammaOutput = true;
    renderer.clearColor = scene->fog->color;
    renderer.render(scene, camera );
    
    return 0;
}

PTR(Object3D) createCompositeObject() {
    /*Create a composite object*/
    auto composite = make_shared<Object3D>();
    
    /* Main part is a sphere */
    auto sphere = Mesh::create( SphereGeometry::create(30, 15, 0.66f ),
                                MeshPhongMaterial::create( 0x990099, 0xFFFFFF, 0x000000, 0x111111, 30.0, false ) );
    composite->add( sphere );
    
    
    /* But a cube is attached to the sphere (not to composite directly), thus transformation is relative to sphere */
    auto cube = Mesh::create( CubeGeometry::create( 1.0f ),
                              MeshPhongMaterial::create( 0xFFFFFF, 0xFFFFFF, 0x000000, 0x111111, 50.0, true ) );
    
    cube->translate( 2.0, 0.0, 0.0 );
    sphere->add( cube );
    
    /* Try to add a texture on to it */
    string path = "/Users/saburookita/Desktop/Dropbox/OpenGL-master/Binaries/";
    string filename = "crate.tga";
    cube->texture = TextureUtils::loadImageAsTexture( path, filename );
    
    /* Just for testing, add the bounding box to the composite object, it should cover both sphere and cube */
    auto bound_geom = composite->computeBoundingBox();
    auto bound_mat  = MeshPhongMaterial::create();
    bound_mat->wireframe = true;
    
    auto bound_mesh = Mesh::create( CubeGeometry::create(1.0f), bound_mat );
    bound_mesh->translate( bound_geom->center() - composite->position ) ;
    bound_mesh->scale     = bound_geom->size();
    bound_mesh->visible   = true;
    
    composite->add( bound_mesh );
    composite->translate(0.0f, 1.5f, 0.0f);
    
    sphere_id       = sphere->id;
    cube_id         = cube->id;
    bounding_box_id = bound_mesh->id;
    
    return composite;
}


If you’re interested in checking out the code, or contributing to it, you can view the repository here: https://github.com/subokita/Three.cpp-Rev-2

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s