Пример #1
0
//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
void RenderEngine::render(OpenGLContext* oglContext, RenderQueue* renderQueue, size_t maxNumPartsToDraw, const Camera& camera, const UniformSet* globalUniformSet)
{
    CVF_ASSERT(oglContext);
    CVF_ASSERT(renderQueue);

    m_renderedPartCount = 0;
    m_renderedVertexCount = 0;
    m_renderedTriangleCount = 0;
    m_renderedOpenGLPrimitiveCount = 0;
    m_applyRenderStateCount = 0;
    m_shaderProgramChangesCount = 0;

    m_renderStateTracker.resetAndApplyDefaultRenderStates(oglContext);

    // Use immediate mode either if forced or if buffer objects are not supported (OpenGL 1.5 and below)
    // This is because the current fixed function rendering relies on the buffer object binding points being present
    const bool renderUsingImmediateMode = (m_forceImmediateMode || !BufferObjectManaged::supportedOpenGL(oglContext));

    // Does the current OpenGL context support OpenGL shaders 
    const bool contextSupportsShaders = ShaderProgram::supportedOpenGL(oglContext);

    // Init matrix state tracker
    MatrixState matrixState(camera);
    uint lastAppliedMatrixStateVersionTick = matrixState.versionTick() - 1;
    uint lastAppliedMatrixStateVersionTickFixedFunction = matrixState.versionTick() - 1;

    const Effect* prevEffect = NULL;
    const RenderStateSet* prevRenderStateSet = NULL;
    const ShaderProgram* prevShaderProgram = NULL;

    // Track the current shader program being used. 
    // Will be NULL when effect has no shader program or if usage of shader program fails
    ShaderProgram* shaderProgramInUse = NULL;
    if (contextSupportsShaders) 
    {
        // Start out with fixed function
        ShaderProgram::useNoProgram(oglContext);
    }

    // Determine number of parts we'll draw
    size_t numPartsInQueue = renderQueue->count();
    size_t numPartsToDraw = std::min(numPartsInQueue, maxNumPartsToDraw);

    bool debugLogging = CVF_SHOULD_LOG_RENDER_DEBUG(oglContext);
    if (debugLogging)
    {
        CVF_LOG_RENDER_DEBUG(oglContext, "RenderEngine::render(), numParts=" + String(static_cast<uint>(numPartsInQueue)));
    }

    size_t i;
    for (i = 0; i < numPartsToDraw; i++)
    {
        RenderItem* item = renderQueue->item(i);
        CVF_ASSERT(item);
        
        Drawable* drawable = item->drawable();
        Effect* effect = item->effect();
        Part* part = item->part();
        CVF_ASSERT(drawable);
        CVF_ASSERT(effect);
        CVF_ASSERT(part);
        
        if (debugLogging)
        {
            CVF_LOG_RENDER_DEBUG(oglContext, String("part#=%1, partName='%2'").arg(static_cast<uint>(i)).arg(part->name()));
        }

        // Update matrix state to reflect any part transformations
        // Register if the pass modifies the view matrix so that we can reset it at the end of this part
        bool lastPartModifiedViewMatrix = false;
        if (part->transform())
        {
            const Transform* trans = part->transform();
            if (trans->eyeLiftFactor() != 0)
            {
                // Eye lifting done by scaling the vertex coordinates after transforming them to eye space. See Transform::setEyeLiftFactor()
                // An eye lift factor of 1.0 results in scaling by 0.995. Might have to tweak the value below somewhat
                double scaleFactor = 1 - (trans->eyeLiftFactor()*0.005);
                Mat4d scaleMatrix = Mat4d::fromScaling(Vec3d(scaleFactor, scaleFactor, scaleFactor));
                matrixState.setViewMatrix(scaleMatrix*camera.viewMatrix());
                lastPartModifiedViewMatrix = true;
            }

            matrixState.setModelMatrix(trans->worldTransform());
        }
        else
        {
            matrixState.clearModelMatrix();
        }

        if (effect != prevEffect && !m_disableApplyEffects)
        {
            const RenderStateSet* const renderStateSet = effect->renderStateSet();
            if (renderStateSet != prevRenderStateSet)
            {
                // This apply function is designed to work with NULL pointers
                m_renderStateTracker.applyRenderStates(oglContext, renderStateSet, prevRenderStateSet);
                prevRenderStateSet = renderStateSet;
                m_applyRenderStateCount++;
            }

            if (contextSupportsShaders)
            {
                ShaderProgram* shaderProgThisEffect = effect->shaderProgram();
                if (shaderProgThisEffect != prevShaderProgram)
                {
                    // Reset the 'in use' pointer, and only set it if we have a shader program AND we manage to use it
                    shaderProgramInUse = NULL;
                    if (shaderProgThisEffect)
                    {
                        // Will fail if program hasn't been linked
                        if (shaderProgThisEffect->useProgram(oglContext))
                        {
                            shaderProgramInUse = shaderProgThisEffect;
                        }
                    }

                    if (!shaderProgramInUse)
                    {
                        ShaderProgram::useNoProgram(oglContext);
                    }

                    prevShaderProgram = shaderProgThisEffect;
                    m_shaderProgramChangesCount++;
                }

                if (shaderProgramInUse)
                {
                    // Apply uniforms for this shader program
                    // Note order of application here.
                    //  - shader program default uniforms
                    //  - global dynamic uniforms
                    //  - (texture binding uniforms)
                    //  - effect uniforms
                    //  - fixed uniforms
                    // We would really like the effect's uniforms to be at the very end, but that would give inconsistent 
                    // behavior since the fixed uniforms may get applied also when the effect does NOT change.
                    shaderProgramInUse->clearUniformApplyTracking();

                    shaderProgramInUse->applyDefaultUniforms(oglContext);

                    if (globalUniformSet)
                    {
                        shaderProgramInUse->applyActiveUniformsOnly(oglContext, *globalUniformSet);
                    }

                    const RenderStateTextureBindings* textureBindings = static_cast<const RenderStateTextureBindings*>(effect->renderStateOfType(RenderState::TEXTURE_BINDINGS));
                    if (textureBindings)
                    {
                        textureBindings->applySamplerTextureUnitUniforms(oglContext, shaderProgramInUse);
                    }

                    // Apply the effect's uniform last so that they win in case of clashes
                    const UniformSet* uniformSet = effect->uniformSet();
                    if (uniformSet)
                    {
                        shaderProgramInUse->applyUniforms(oglContext, *uniformSet);
                    }

                    // Still, the fixed ones have to go at the end for consistency
                    shaderProgramInUse->applyFixedUniforms(oglContext, matrixState);
                    lastAppliedMatrixStateVersionTick = matrixState.versionTick();
                }
            }

            prevEffect = effect;
        }


        if (shaderProgramInUse)
        {
            if (lastAppliedMatrixStateVersionTick != matrixState.versionTick())
            {
                shaderProgramInUse->applyFixedUniforms(oglContext, matrixState);
                lastAppliedMatrixStateVersionTick = matrixState.versionTick();
            }
            
            // Will check that all active uniforms in shader program have been set since last call to >clearUniformApplyTracking()
            // Reports error to log if any uniforms are missing.
            shaderProgramInUse->checkReportAllUniformsApplied(oglContext);
        }
        else
        {
#ifndef CVF_OPENGL_ES
            if (lastAppliedMatrixStateVersionTickFixedFunction != matrixState.versionTick())
            {
                glLoadMatrixf(matrixState.modelViewMatrix().ptr());
                lastAppliedMatrixStateVersionTickFixedFunction = matrixState.versionTick();
            }
#endif // CVF_OPENGL_ES
        }


        // Do actual rendering
        if (!m_disableDrawableRender) 
        {
            if (renderUsingImmediateMode)
            {
                drawable->renderImmediateMode(oglContext, matrixState);
            }
            else
            {
                if (shaderProgramInUse)
                {
                    drawable->render(oglContext, shaderProgramInUse, matrixState);
                }
                else
                {
                    drawable->renderFixedFunction(oglContext, matrixState);
                }
            }

            m_renderedPartCount++;
        }

        // Must reset the view matrix if it was modified during drawing of this part
        if (lastPartModifiedViewMatrix)
        {
            matrixState.setViewMatrix(camera.viewMatrix());
        }
        
        // Update counters
        if (m_enableItemCountUpdate)
        {
            m_renderedVertexCount           += drawable->vertexCount();
            m_renderedTriangleCount         += drawable->triangleCount();
            m_renderedOpenGLPrimitiveCount  += drawable->faceCount();
        }
    }

    // Do an apply with an empty new render state set to get defaults back before leaving
    // This really isn't the optimal place to do this, but until we expose application of render states it's the only place
    m_renderStateTracker.applyRenderStates(oglContext, NULL, prevRenderStateSet);

    // For now, set back to fixed function
    if (contextSupportsShaders) 
    {
        ShaderProgram::useNoProgram(oglContext);
    }

    // Set back VBO settings (do we need this?)
    if (BufferObjectManaged::supportedOpenGL(oglContext))
    {
        BufferObjectManaged::unbindAllBuffers(oglContext);
    }
}