diff --git a/doomsday/sdk/libgui/include/de/graphics/modeldrawable.h b/doomsday/sdk/libgui/include/de/graphics/modeldrawable.h index dbcb2d333e..ad9c3aaf48 100644 --- a/doomsday/sdk/libgui/include/de/graphics/modeldrawable.h +++ b/doomsday/sdk/libgui/include/de/graphics/modeldrawable.h @@ -59,6 +59,9 @@ class LIBGUI_PUBLIC ModelDrawable : public AssetGroup /// An error occurred during the loading of the model data. @ingroup errors DENG2_ERROR(LoadError); + /// There was a shader program related problem. @ingroup errors + DENG2_ERROR(ProgramError); + enum TextureMap // note: enum values used as indices internally { Diffuse = 0, ///< Surface color and opacity. @@ -248,8 +251,7 @@ class LIBGUI_PUBLIC ModelDrawable : public AssetGroup { enum Flag { - Enabled = 0x1, - DefaultFlags = Enabled + DefaultFlags = 0 }; Q_DECLARE_FLAGS(Flags, Flag) @@ -422,8 +424,22 @@ class LIBGUI_PUBLIC ModelDrawable : public AssetGroup GLProgram *program() const; + /** + * Draws the model. + * + * @param animation Animation state. + * @param drawPasses Rendering passes. If omitted, all meshes are drawn + * with normal alpha blending. + * @param passMask Sets a mask that specifies which rendering passes are + * enabled. Each bit in the array corresponds to an + * element in @a drawPasses. An empty mask (size zero) + * means that all passes are enabled. + * @param programCallback + * @param passCallback + */ void draw(Animator const *animation = nullptr, Passes const *drawPasses = nullptr, + QBitArray const &passMask = QBitArray(), ProgramBindingFunc programCallback = ProgramBindingFunc(), RenderingPassFunc passCallback = RenderingPassFunc()) const; diff --git a/doomsday/sdk/libgui/src/graphics/modeldrawable.cpp b/doomsday/sdk/libgui/src/graphics/modeldrawable.cpp index c4adae5d32..b7c7031e1b 100644 --- a/doomsday/sdk/libgui/src/graphics/modeldrawable.cpp +++ b/doomsday/sdk/libgui/src/graphics/modeldrawable.cpp @@ -690,7 +690,7 @@ DENG2_PIMPL(ModelDrawable) needMakeBuffer = true; } - // Bone & Mesh Setup ---------------------------------------------------------------- +//- Bone & Mesh Setup ------------------------------------------------------------------- void clearBones() { @@ -921,7 +921,7 @@ DENG2_PIMPL(ModelDrawable) buffer->setIndices(gl::Triangles, indx, gl::Static); } - // Animation ------------------------------------------------------------------------ +//- Animation --------------------------------------------------------------------------- struct AccumData { @@ -1059,10 +1059,11 @@ DENG2_PIMPL(ModelDrawable) anim.mNumPositionKeys)); } - // Drawing -------------------------------------------------------------------------- +//- Drawing ----------------------------------------------------------------------------- GLProgram *drawProgram = nullptr; Pass const *drawPass = nullptr; + QBitArray passMask; ProgramBindingFunc programCallback; RenderingPassFunc passCallback; @@ -1102,7 +1103,6 @@ DENG2_PIMPL(ModelDrawable) { if(drawProgram) { - drawProgram->endUse(); drawProgram->unbind(uBoneMatrices); if(programCallback) { @@ -1119,7 +1119,6 @@ DENG2_PIMPL(ModelDrawable) programCallback(*drawProgram, AboutToBind); } drawProgram->bind(uBoneMatrices); - drawProgram->beginUse(); } } @@ -1157,39 +1156,59 @@ DENG2_PIMPL(ModelDrawable) { preDraw(animation); - GLBuffer::DrawRanges ranges; - for(Pass const &pass : passes) + try { - if(!pass.flags.testFlag(Pass::Enabled)) + GLBuffer::DrawRanges ranges; + for(int i = 0; i < passes.size(); ++i) { - continue; - } + Pass const &pass = passes.at(i); - drawPass = &pass; - setDrawProgram(pass.program? pass.program : program); + // Is this pass disabled? + if(!passMask.isEmpty() && !passMask.testBit(i)) + { + continue; + } - DENG2_ASSERT(drawProgram != nullptr); + drawPass = &pass; + setDrawProgram(pass.program? pass.program : program); + if(!drawProgram) + { + throw ProgramError("ModelDrawable::draw", + QString("Rendering pass %1 (\"%2\") has no shader program") + .arg(i).arg(pass.name)); + } - if(passCallback) - { - passCallback(pass, PassBegun); - } + if(passCallback) + { + passCallback(pass, PassBegun); + } - ranges.clear(); - initRanges(ranges, pass.meshes); + drawProgram->beginUse(); - GLState::push() - .setBlendFunc(pass.blendFunc) - .setBlendOp(pass.blendOp) - .apply(); - buffer->draw(&ranges); - GLState::pop(); + ranges.clear(); + initRanges(ranges, pass.meshes); - if(passCallback) - { - passCallback(pass, PassEnded); + GLState::push() + .setBlendFunc(pass.blendFunc) + .setBlendOp(pass.blendOp) + .apply(); + buffer->draw(&ranges); + GLState::pop(); + + drawProgram->endUse(); + + if(passCallback) + { + passCallback(pass, PassEnded); + } } } + catch(Error const &er) + { + LOG_GL_ERROR("Failed to draw model \"%s\": %s") + << sourcePath + << er.asText(); + } postDraw(); } @@ -1396,13 +1415,15 @@ GLProgram *ModelDrawable::program() const void ModelDrawable::draw(Animator const *animation, Passes const *passes, + QBitArray const &passMask, ProgramBindingFunc programCallback, RenderingPassFunc passCallback) const { const_cast(this)->glInit(); - if(isReady() && d->program && d->atlas) + if(isReady() && d->atlas) { + d->passMask = passMask; d->programCallback = programCallback; d->passCallback = passCallback;