void IntersectVisitor::apply(Billboard& node)
{
    if (!enterNode(node)) return;

    // IntersectVisitor doesn't have getEyeLocal(), can we use NodeVisitor::getEyePoint()?
    const Vec3& eye_local = getEyePoint();

    for(unsigned int i = 0; i < node.getNumDrawables(); i++ )
    {
        const Vec3& pos = node.getPosition(i);
        osg::ref_ptr<RefMatrix> billboard_matrix = new RefMatrix;
        node.computeMatrix(*billboard_matrix,eye_local,pos);

        pushMatrix(billboard_matrix.get(), osg::Transform::RELATIVE_RF);

        intersect(*node.getDrawable(i));

        popMatrix();

    }

    leaveNode();
}
void ScreenMVCullVisitor::apply(Billboard& node)
{
    bool status = _cullingStatus;
    bool firstStatus = _firstCullStatus;
    if(isCulled(node))
    {
        _firstCullStatus = firstStatus;
        _cullingStatus = status;
        return;
    }

    // push the node's state.
    StateSet* node_state = node.getStateSet();
    if(node_state)
        pushStateSet(node_state);

    // traverse any call callbacks and traverse any children.
    handle_cull_callbacks_and_traverse(node);

    const Vec3& eye_local = getEyeLocal();
    const RefMatrix& modelview = *getModelViewMatrix();

    for(unsigned int i = 0; i < node.getNumDrawables(); ++i)
    {
        const Vec3& pos = node.getPosition(i);

        Drawable* drawable = node.getDrawable(i);
        // need to modify isCulled to handle the billboard offset.
        // if (isCulled(drawable->getBound())) continue;

        if(drawable->getCullCallback())
        {
            if(drawable->getCullCallback()->cull(this,drawable,&_renderInfo)
                    == true)
                continue;
        }

        RefMatrix* billboard_matrix = createOrReuseMatrix(modelview);

        node.computeMatrix(*billboard_matrix,eye_local,pos);

        if(_computeNearFar && drawable->getBound().valid())
            updateCalculatedNearFar(*billboard_matrix,*drawable,true);
        float depth = distance(pos,modelview);
        /*
         if (_computeNearFar)
         {
         if (d<_computed_znear)
         {
         if (d<0.0) OSG_NOTIFY(osg::WARN)<<"Alerting billboard handling ="<<d<< std::endl;
         _computed_znear = d;
         }
         if (d>_computed_zfar) _computed_zfar = d;
         }
         */
        StateSet* stateset = drawable->getStateSet();
        if(stateset)
            pushStateSet(stateset);

        if(osg::isNaN(depth))
        {
            /*OSG_NOTIFY(osg::NOTICE)<<"CullVisitor::apply(Billboard&) detected NaN,"<<std::endl
             <<"    depth="<<depth<<", pos=("<<pos<<"),"<<std::endl
             <<"    *billboard_matrix="<<*billboard_matrix<<std::endl;
             OSG_NOTIFY(osg::DEBUG_INFO) << "    NodePath:" << std::endl;
             for (NodePath::const_iterator i = getNodePath().begin(); i != getNodePath().end(); ++i)
             {
             OSG_NOTIFY(osg::DEBUG_INFO) << "        \"" << (*i)->getName() << "\"" << std::endl;
             }*/
        }
        else
        {
            addDrawableAndDepth(drawable,billboard_matrix,depth);
        }

        if(stateset)
            popStateSet();

    }

    // pop the node's state off the geostate stack.    
    if(node_state)
        popStateSet();

    _firstCullStatus = firstStatus;
    _cullingStatus = status;
}
void Display()
{
    DrawShadowMap();
    /* Clear window; color specified in 'Initialize()' */
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glUseProgram(ShaderProgram);
    /* Associate program with shader matrices */
    GLint projectionUniform = glGetUniformLocation(ShaderProgram, "ProjectionMatrix");
    if (projectionUniform == -1)
    {
        cerr << "Could not bind uniform ProjectionMatrix" << endl;
        exit(-1);
    }
    glUniformMatrix4fv(projectionUniform, 1, GL_TRUE, ProjectionMatrix.matrix);

    GLint ViewUniform = glGetUniformLocation(ShaderProgram, "ViewMatrix");
    if (ViewUniform == -1)
    {
        cerr << "Could not bind uniform ViewMatrix" << endl;
        exit(-1);
    }
    glUniformMatrix4fv(ViewUniform, 1, GL_FALSE, glm::value_ptr(camera.viewMatrix()));

	/* light */
	GLint LightPos1Uniform = glGetUniformLocation(ShaderProgram, "lightPos1");
	if (LightPos1Uniform == -1){
		cerr << "Could not bind uniform lightPos1" << endl;
		exit(-1);
	}
	glUniform3fv(LightPos1Uniform, 1, glm::value_ptr(camera.viewMatrix() * glm::vec4(lights[0]->pos, 1.0)));
	
	GLint LightColor1Uniform = glGetUniformLocation(ShaderProgram, "lightColor1");
	if (LightColor1Uniform == -1){
		cerr << "Could not bind uniform lightColor1" << endl;
		exit(-1);
	}
	glUniform3fv(LightColor1Uniform, 1, glm::value_ptr(lights[0]->rgb));

	/* green moving light */
	GLint LightPos2Uniform = glGetUniformLocation(ShaderProgram, "lightPos2");
	if (LightPos2Uniform == -1){
		cerr << "Could not bind uniform lightPos2" << endl;
		exit(-1);
	}
	glUniform3fv(LightPos2Uniform, 1, glm::value_ptr(camera.viewMatrix() * glm::transpose(glm::make_mat4(ModelMatrix[6].matrix)) * glm::vec4(lights[1]->pos, 1)));
	GLint LightColor2Uniform = glGetUniformLocation(ShaderProgram, "lightColor2");
	if (LightColor2Uniform == -1){
		cerr << "Could not bind uniform lightColor2" << endl;
		exit(-1);
	}
	glUniform3fv(LightColor2Uniform, 1, glm::value_ptr(lights[1]->rgb));

    GLint kA = glGetUniformLocation(ShaderProgram, "kA");
    GLint kD = glGetUniformLocation(ShaderProgram, "kD");
    GLint kS = glGetUniformLocation(ShaderProgram, "kS");
    if (kA == -1 || kD == -1 || kS == -1)
    {
        cout << kA << kD << kS << endl;
        cerr << "Could not bind uniform light constants" << endl;
        exit(-1);
    }

    const glm::mat4 scale_bias_matrix =
            glm::mat4(glm::vec4(0.5f, 0.0f, 0.0f, 0.0f),
                      glm::vec4(0.0f, 0.5f, 0.0f, 0.0f),
                      glm::vec4(0.0f, 0.0f, 0.5f, 0.0f),
                      glm::vec4(0.5f, 0.5f, 0.5f, 1.0f));
    glUseProgram(ShaderProgram);
    glUniformMatrix4fv(glGetUniformLocation(ShaderProgram, "ShadowMatrix"), 1, GL_FALSE,
                       glm::value_ptr(scale_bias_matrix * light_projection_matrix * light_view_matrix));

    /* Get texture uniform handle from fragment shader */
    GLuint ShadowUniform  = glGetUniformLocation(ShaderProgram, "depth_texture");

    /* Get texture uniform handle from fragment shader */
    GLuint TextureUniform  = glGetUniformLocation(ShaderProgram, "myTextureSampler");

    glUniform1i(TextureUniform, 0);
    glUniform1i(ShadowUniform, 1);

    glActiveTexture(GL_TEXTURE1);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, depth_texture);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, crackles->TextureID);



    glEnableVertexAttribArray(vPosition);
    glEnableVertexAttribArray(vColor);
    glEnableVertexAttribArray(vNormal);
    glEnableVertexAttribArray(vUV);

    for (int i = 0; i < 7; i++) {

        glUniform1f(kA, objects[i]->kA * ky);
        glUniform1f(kD, objects[i]->kD * kx);
        glUniform1f(kS, objects[i]->kS * kc);


        glBindBuffer(GL_ARRAY_BUFFER, VBO[i]);
        glVertexAttribPointer(vPosition, 3, GL_FLOAT, GL_FALSE, 0, 0);

        glBindBuffer(GL_ARRAY_BUFFER, CBO[i]);
        glVertexAttribPointer(vColor, 3, GL_FLOAT, GL_FALSE, 0, 0);

        glBindBuffer(GL_ARRAY_BUFFER, TCBO[i]);
        glVertexAttribPointer(vUV, 2, GL_FLOAT, GL_FALSE, 0, 0);

        glBindBuffer(GL_ARRAY_BUFFER, NBO[i]);
        glVertexAttribPointer(vNormal, 3, GL_FLOAT, GL_FALSE, 0, 0);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO[i]);

        GLint size;
        glGetBufferParameteriv(GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &size);
        GLint RotationUniform = glGetUniformLocation(ShaderProgram, "ModelMatrix");
        if (RotationUniform == -1) {
            cerr << "Could not bind uniform ModelMatrix" << endl;
            exit(-1);
        }
        glUniformMatrix4fv(RotationUniform, 1, GL_TRUE, ModelMatrix[i].matrix);

        glDrawElements(GL_TRIANGLES, size / sizeof(GLushort), GL_UNSIGNED_SHORT, 0);
    }

    for (int i = 0; i < 2; i++) {

        glUniform1f(kA, room_components[i]->kA * ky);
        glUniform1f(kD, room_components[i]->kD * kx);
        glUniform1f(kS, room_components[i]->kS * kc);

        glBindBuffer(GL_ARRAY_BUFFER, VBR[i]);
        glVertexAttribPointer(vPosition, 3, GL_FLOAT, GL_FALSE, 0, 0);

        glBindBuffer(GL_ARRAY_BUFFER, CBR[i]);
        glVertexAttribPointer(vColor, 3, GL_FLOAT,GL_FALSE, 0, 0);

        glBindBuffer(GL_ARRAY_BUFFER, NBR[i]);
        glVertexAttribPointer(vNormal, 3, GL_FLOAT, GL_FALSE, 0, 0);

        glBindBuffer(GL_ARRAY_BUFFER, TCBR[i]);
        glVertexAttribPointer(vUV, 2, GL_FLOAT, GL_FALSE, 0, 0);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBR[i]);

        GLint size;
        glGetBufferParameteriv(GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &size);

        GLint RoomUniform = glGetUniformLocation(ShaderProgram, "ModelMatrix");
        if (RoomUniform == -1){
            cerr << "Could not bind uniform ModelMatrix" << endl;
            exit(-1);
        }
        glUniformMatrix4fv(RoomUniform, 1, GL_FALSE, RoomMatrix[0].matrix);

        if (room_components[i]->texture != NULL && room_components[i]->texture->TextureID > 0) {

            /* Bind current texture */
            glBindTexture(GL_TEXTURE_2D, room_components[i]->texture->TextureID);

        }

        /* Issue draw command, using indexed triangle list */
        glDrawElements(GL_TRIANGLES, size/sizeof(GLushort), GL_UNSIGNED_SHORT, 0);
    }
    /* Draw billboard */
    glUniform1f(kA, billboard.kA * ky);
    glUniform1f(kD, billboard.kD * kx);
    glUniform1f(kS, billboard.kS * kc);


    glBindBuffer(GL_ARRAY_BUFFER, VBB);
    glVertexAttribPointer(vPosition, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, CBB);
    glVertexAttribPointer(vColor, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, TCBB);
    glVertexAttribPointer(vUV, 2, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, NBB);
    glVertexAttribPointer(vNormal, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBB);

    GLint size;
    glGetBufferParameteriv(GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &size);

    GLint RotationUniform = glGetUniformLocation(ShaderProgram, "ModelMatrix");
    if (RotationUniform == -1) {
        cerr << "Could not bind uniform ModelMatrix" << endl;
        exit(-1);
    }
    glUniformMatrix4fv(RotationUniform, 1, GL_FALSE, glm::value_ptr(billboard.getPosition()));

    glBindTexture(GL_TEXTURE_2D, billboard.texture->TextureID);

    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE, GL_ONE);
    glDrawElements(GL_TRIANGLES, size / sizeof(GLushort), GL_UNSIGNED_SHORT, 0);
    glDisable(GL_BLEND);


    /* Disable attributes */
    glDisableVertexAttribArray(vPosition);
    glDisableVertexAttribArray(vColor);
    glDisableVertexAttribArray(vNormal);
    glDisableVertexAttribArray(vUV);
    
    /* Swap between front and back buffer */ 
    glutSwapBuffers();
}