diff --git a/doomsday/apps/client/include/render/stateanimator.h b/doomsday/apps/client/include/render/stateanimator.h index c7bcf64599..cdc3d99ae2 100644 --- a/doomsday/apps/client/include/render/stateanimator.h +++ b/doomsday/apps/client/include/render/stateanimator.h @@ -51,21 +51,7 @@ class StateAnimator : public de::ModelDrawable::Animator de::ddouble currentTime(int index) const; - /** - * Returns a bit mask that specifies which rendering passes are currently - * enabled for this object. This should be passed to ModelDrawable::draw(). - */ - QBitArray passMask() const; - - /** - * Determines the material to use during a rendering pass. These are - * determined by the "material" variables in the object's namespace. - * - * @param passName Name of the rendering pass. - * - * @return Material index. - */ - de::duint materialForPass(de::String const &passName) const; + de::ModelDrawable::Appearance const &appearance() const; enum BindOperation { Bind, Unbind }; diff --git a/doomsday/apps/client/src/render/modelrenderer.cpp b/doomsday/apps/client/src/render/modelrenderer.cpp index 0b345ef15d..6672446e94 100644 --- a/doomsday/apps/client/src/render/modelrenderer.cpp +++ b/doomsday/apps/client/src/render/modelrenderer.cpp @@ -607,29 +607,7 @@ DENG2_PIMPL(ModelRenderer) { DENG2_ASSERT(p.auxData != nullptr); - p.model->draw( - p.animator, - p.auxData->passes.isEmpty()? nullptr : &p.auxData->passes, - p.animator->passMask(), - - // Callback for when the program changes: - [&p] (GLProgram &program, ModelDrawable::ProgramBinding binding) - { - p.animator->bindUniforms(program, - binding == ModelDrawable::AboutToBind? StateAnimator::Bind : - StateAnimator::Unbind); - }, - - // Callback for each rendering pass: - [&p] (ModelDrawable::Pass const &pass, ModelDrawable::PassState state) - { - p.model->setMaterial(p.animator->materialForPass(pass.name)); - p.animator->bindPassUniforms(*p.model->currentProgram(), - pass.name, - state == ModelDrawable::PassBegun? StateAnimator::Bind : - StateAnimator::Unbind); - } - ); + p.model->draw(&p.animator->appearance(), p.animator); } }; diff --git a/doomsday/apps/client/src/render/stateanimator.cpp b/doomsday/apps/client/src/render/stateanimator.cpp index f035c5f45b..1bb79f1a49 100644 --- a/doomsday/apps/client/src/render/stateanimator.cpp +++ b/doomsday/apps/client/src/render/stateanimator.cpp @@ -32,6 +32,7 @@ static String const DEF_PROBABILITY("prob"); static String const DEF_ROOT_NODE ("node"); static String const DEF_LOOPING ("looping"); static String const DEF_PRIORITY ("priority"); +static String const DEF_RENDER ("render"); static String const DEF_PASS ("pass"); static String const DEF_VARIABLE ("variable"); static String const DEF_WRAP ("wrap"); @@ -117,9 +118,11 @@ DENG2_PIMPL(StateAnimator) ModelRenderer::AuxiliaryData const *auxData; QHash pendingAnimForNode; String currentStateName; - Record names; ///< Local context for scripts. - QBitArray passMask; - QHash passIndexLookup; + Record names; ///< Local context for scripts, i.e., per-object model state. + + ModelDrawable::Appearance appearance; + QHash indexForPassName; + QHash passForMaterialVariable; /** * Animatable variable bound to a GL uniform. The value can have 1...4 float @@ -203,8 +206,6 @@ DENG2_PIMPL(StateAnimator) typedef QHash RenderVars; QHash passVars; - QHash passMaterials; - Instance(Public *i, DotPath const &id) : Base(i) , auxData(ClientApp::renderSystem().modelRenderer().auxiliaryData(id)) @@ -212,25 +213,55 @@ DENG2_PIMPL(StateAnimator) names.addText(VAR_ID, id).setReadOnly(); names.add(VAR_ASSET).set(new RecordValue(App::asset(id).accessedRecord())).setReadOnly(); - // VAR_SELF should point to the thing's namespace, or player's for psprites + /// @todo VAR_SELF should point to the thing's namespace, or player's + /// namespace for psprites. -jk - initShaderVariables(); + initVariables(); + + // Set up the appearance. + appearance.programCallback = [this] (GLProgram &program, ModelDrawable::ProgramBinding binding) + { + self.bindUniforms(program, + binding == ModelDrawable::AboutToBind? StateAnimator::Bind : + StateAnimator::Unbind); + }; + appearance.passCallback = [this] (ModelDrawable::Pass const &pass, ModelDrawable::PassState state) + { + self.bindPassUniforms(*self.model().currentProgram(), + pass.name, + state == ModelDrawable::PassBegun? StateAnimator::Bind : + StateAnimator::Unbind); + }; } ~Instance() { - deinitShaderVariables(); + deinitVariables(); } - void initShaderVariables() + void initVariables() { - int passIndex = 0; - passIndexLookup.clear(); + int const passCount = auxData->passes.size(); + + // Clear lookups affected by the variables. + indexForPassName.clear(); + appearance.passMaterial.clear(); + if(!passCount) + { + // Material to be used with the default pass. + appearance.passMaterial << 0; + } + else + { + for(int i = 0; i < passCount; ++i) appearance.passMaterial << 0; + } + appearance.passMask.resize(passCount); + int passIndex = 0; auto const &def = names[VAR_ASSET].valueAsRecord(); - if(def.has("render")) + if(def.has(DEF_RENDER)) { - Record const &renderBlock = def.subrecord("render"); + Record const &renderBlock = def.subrecord(DEF_RENDER); initVariablesForPass(renderBlock); @@ -239,7 +270,7 @@ DENG2_PIMPL(StateAnimator) auto passes = ScriptedInfo::subrecordsOfType(DEF_PASS, renderBlock); DENG2_FOR_EACH_CONST(Record::Subrecords, i, passes) { - passIndexLookup[i.key()] = passIndex++; + indexForPassName[i.key()] = passIndex++; Record &passRec = names.addRecord(i.key()); passRec.addBoolean(VAR_ENABLED, @@ -250,13 +281,11 @@ DENG2_PIMPL(StateAnimator) } } - DENG2_ASSERT(passIndex == auxData->passes.size()); - DENG2_ASSERT(passIndexLookup.size() == auxData->passes.size()); + DENG2_ASSERT(passIndex == passCount); + DENG2_ASSERT(indexForPassName.size() == passCount); - passMask.resize(auxData->passes.size()); updatePassMask(); - - qDebug() << "Namespaces:\n" << names.asText(); + updatePassMaterials(); } void initVariablesForPass(Record const &block, String const &passName = PASS_GLOBAL) @@ -265,13 +294,15 @@ DENG2_PIMPL(StateAnimator) // Each pass has a variable for selecting the material. // The default value is optionally specified in the definition. - passMaterials.insert(passName, - &names.addText(passName.concatenateMember(VAR_MATERIAL), - block.gets(DEF_MATERIAL, DEFAULT_MATERIAL))); + Variable &passMaterialVar = names.addText(passName.concatenateMember(VAR_MATERIAL), + block.gets(DEF_MATERIAL, DEFAULT_MATERIAL)); + passMaterialVar.audienceForChange() += this; + passForMaterialVariable.insert(&passMaterialVar, auxData->passes.findName(passName)); /// @todo Should observe if the variable above is deleted unexpectedly. -jk - // Look up the variable declarations. + // Create the animated variables to be used with the shader based + // on the pass definitions. auto vars = ScriptedInfo::subrecordsOfType(DEF_VARIABLE, block); DENG2_FOR_EACH_CONST(Record::Subrecords, i, vars) { @@ -345,6 +376,8 @@ DENG2_PIMPL(StateAnimator) // Uniform to be passed to the shader. var->uniform = new GLUniform(i.key().toLatin1(), uniformType); + // Compose a lookup for quickly finding the variables of each pass + // (by pass name). passVars[passName][i.key()] = var.release(); } } @@ -356,20 +389,80 @@ DENG2_PIMPL(StateAnimator) .setReadOnly(); } - void deinitShaderVariables() + void deinitVariables() { for(RenderVars const &vars : passVars.values()) { qDeleteAll(vars.values()); } passVars.clear(); - passMaterials.clear(); + appearance.passMaterial.clear(); } - void variableValueChanged(Variable &, Value const &) + Variable const &materialVariableForPass(duint passIndex) const { - // This is called when one of the "(pass).enabled" variables is modified. - updatePassMask(); + if(!auxData->passes.isEmpty()) + { + String const varName = auxData->passes.at(passIndex).name.concatenateMember(VAR_MATERIAL); + if(names.has(varName)) + { + return names[varName]; + } + } + return names[VAR_MATERIAL]; + } + + void updatePassMaterials() + { + for(int i = 0; i < appearance.passMaterial.size(); ++i) + { + appearance.passMaterial[i] = materialForUserProvidedName( + materialVariableForPass(i).value().asText()); + } + } + + void variableValueChanged(Variable &var, Value const &newValue) + { + if(var.name() == VAR_MATERIAL) + { + // Update the corresponding pass material. + int passIndex = passForMaterialVariable[&var]; + if(passIndex < 0) + { + updatePassMaterials(); + } + else + { + appearance.passMaterial[passIndex] = materialForUserProvidedName(newValue.asText()); + } + } + else + { + DENG2_ASSERT(var.name() == VAR_ENABLED); + + // This is called when one of the "(pass).enabled" variables is modified. + updatePassMask(); + } + } + + /** + * Determines the material to use during a rendering pass. These are + * determined by the "material" variables in the object's namespace. + * + * @param materialName Name of the material to use. + * + * @return Material index. + */ + duint materialForUserProvidedName(String const &materialName) const + { + auto const matIndex = auxData->materialIndexForName.constFind(materialName); + if(matIndex != auxData->materialIndexForName.constEnd()) + { + return matIndex.value(); + } + LOG_RES_WARNING("Asset \"%s\" does not have a material called '%s'") + << names.gets(VAR_ID) << materialName; + return 0; // default material } void updatePassMask() @@ -378,11 +471,11 @@ DENG2_PIMPL(StateAnimator) return sub.getb(DEF_ENABLED, false); }); - passMask.fill(false); + appearance.passMask.fill(false); for(String name : enabledPasses.keys()) { - DENG2_ASSERT(passIndexLookup.contains(name)); - passMask.setBit(passIndexLookup[name], true); + DENG2_ASSERT(indexForPassName.contains(name)); + appearance.passMask.setBit(indexForPassName[name], true); } } @@ -581,29 +674,9 @@ ddouble StateAnimator::currentTime(int index) const return ModelDrawable::Animator::currentTime(index); // + frameTimePos; } -QBitArray StateAnimator::passMask() const +ModelDrawable::Appearance const &StateAnimator::appearance() const { - return d->passMask; -} - -duint StateAnimator::materialForPass(String const &passName) const -{ - auto const iter = d->passMaterials.constFind(passName); - if(iter != d->passMaterials.constEnd()) - { - Variable const *material = iter.value(); - DENG2_ASSERT(material != nullptr); - auto const matIndex = d->auxData->materialIndexForName.constFind(material->value().asText()); - if(matIndex != d->auxData->materialIndexForName.constEnd()) - { - return matIndex.value(); - } - } - else - { - return materialForPass(PASS_GLOBAL); - } - return 0; // default material + return d->appearance; } void StateAnimator::bindUniforms(GLProgram &program, BindOperation operation) const diff --git a/doomsday/sdk/libgui/include/de/graphics/modeldrawable.h b/doomsday/sdk/libgui/include/de/graphics/modeldrawable.h index 98c9512db7..4ca43c37b5 100644 --- a/doomsday/sdk/libgui/include/de/graphics/modeldrawable.h +++ b/doomsday/sdk/libgui/include/de/graphics/modeldrawable.h @@ -269,17 +269,69 @@ class LIBGUI_PUBLIC ModelDrawable : public AssetGroup */ struct LIBGUI_PUBLIC Pass { - enum Flag { DefaultFlags = 0 }; - Q_DECLARE_FLAGS(Flags, Flag) - String name; - Flags flags = DefaultFlags; QBitArray meshes; ///< One bit per model mesh. GLProgram *program = nullptr; ///< Shading program. gl::BlendFunc blendFunc { gl::SrcAlpha, gl::OneMinusSrcAlpha }; gl::BlendOp blendOp = gl::Add; }; - typedef QList Passes; + + struct LIBGUI_PUBLIC Passes : public QList + { + /** + * Finds the pass with a given name. Performance is O(n) (i.e., suitable + * for non-repeated use). The lookup is done case-sensitively. + * + * @param name Pass name. + * + * @return Index of the pass. If not found, returns -1. + */ + int findName(String const &name) const; + }; + + enum ProgramBinding { AboutToBind, Unbound }; + typedef std::function ProgramBindingFunc; + + enum PassState { PassBegun, PassEnded }; + typedef std::function RenderingPassFunc; + + /** + * Per-instance appearance parameters. + */ + struct LIBGUI_PUBLIC Appearance + { + enum Flag { DefaultFlags = 0 }; + Q_DECLARE_FLAGS(Flags, Flag) + + Flags flags = DefaultFlags; + + /** + * Rendering passes. If omitted, all meshes are drawn with normal + * alpha blending. + */ + Passes const *drawPasses = nullptr; + + /** + * Specifies the material used for each rendering pass. Size of the + * list must equal the number of passes. If it doesn't, the default + * material (0) is used during drawing. + * + * Switching the material doesn't change the internal GL resources + * of the model. It only determines which vertex buffer is selected + * for drawing the pass. + */ + QList passMaterial; + + /** + * 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. + */ + QBitArray passMask = QBitArray(); + + ProgramBindingFunc programCallback = ProgramBindingFunc(); + RenderingPassFunc passCallback = RenderingPassFunc(); + }; /** * Identifies a mesh. @@ -289,10 +341,8 @@ class LIBGUI_PUBLIC ModelDrawable : public AssetGroup duint index; duint material; - MeshId(duint index, duint material = 0) - : index(index) - , material(material) - {} + MeshId(duint index, duint material = 0 /*default material*/) + : index(index), material(material) {} }; typedef QList Mapping; @@ -300,12 +350,6 @@ class LIBGUI_PUBLIC ModelDrawable : public AssetGroup // Audiences: DENG2_DEFINE_AUDIENCE2(AboutToGLInit, void modelAboutToGLInit(ModelDrawable &)) - enum ProgramBinding { AboutToBind, Unbound }; - typedef std::function ProgramBindingFunc; - - enum PassState { PassBegun, PassEnded }; - typedef std::function RenderingPassFunc; - public: ModelDrawable(); @@ -452,21 +496,15 @@ class LIBGUI_PUBLIC ModelDrawable : public AssetGroup /** * Draws the model. * + * @param appearance Appearance parameters. * @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; + void draw(Appearance const *appearance = nullptr, + Animator const *animation = nullptr) const; + + void draw(Animator const *animation) const { + draw(nullptr, animation); + } void drawInstanced(GLBuffer const &instanceAttribs, Animator const *animation = nullptr) const; @@ -483,18 +521,6 @@ class LIBGUI_PUBLIC ModelDrawable : public AssetGroup */ GLProgram *currentProgram() const; - /** - * Changes the material used for drawing. This can also be called during a - * draw operation, e.g., from the pass callback. - * - * This method is const because it can be called during drawing, and it - * doesn't change the internal GL resources of the model. It only affects - * which vertex buffer is selected for drawing. - * - * @param index Material to use for drawing. - */ - void setMaterial(duint index) const; - /** * Dimensions of the default pose, in model space. */ @@ -509,7 +535,7 @@ class LIBGUI_PUBLIC ModelDrawable : public AssetGroup DENG2_PRIVATE(d) }; -Q_DECLARE_OPERATORS_FOR_FLAGS(ModelDrawable::Pass::Flags) +Q_DECLARE_OPERATORS_FOR_FLAGS(ModelDrawable::Appearance::Flags) Q_DECLARE_OPERATORS_FOR_FLAGS(ModelDrawable::Animator::OngoingSequence::Flags) } // namespace de diff --git a/doomsday/sdk/libgui/src/graphics/modeldrawable.cpp b/doomsday/sdk/libgui/src/graphics/modeldrawable.cpp index d61401f672..9332d9c4b6 100644 --- a/doomsday/sdk/libgui/src/graphics/modeldrawable.cpp +++ b/doomsday/sdk/libgui/src/graphics/modeldrawable.cpp @@ -443,25 +443,25 @@ DENG2_PIMPL(ModelDrawable) { qDebug() << " material #" << i << "variant:" << varIdx; - MaterialId const matId(i, varIdx); + MeshId const mesh(i, varIdx); auto &mat = variants[varIdx]->materials[i]; // Load all known types of textures, falling back to defaults. - loadTextureImage(matId, aiTextureType_DIFFUSE); + loadTextureImage(mesh, aiTextureType_DIFFUSE); fallBackToDefaultTexture(mat, Diffuse); - loadTextureImage(matId, aiTextureType_NORMALS); + loadTextureImage(mesh, aiTextureType_NORMALS); if(!mat.texIds[Normals]) { // Try a height field instead. This will be converted to a normal map. - loadTextureImage(matId, aiTextureType_HEIGHT); + loadTextureImage(mesh, aiTextureType_HEIGHT); } fallBackToDefaultTexture(mat, Normals); - loadTextureImage(matId, aiTextureType_SPECULAR); + loadTextureImage(mesh, aiTextureType_SPECULAR); fallBackToDefaultTexture(mat, Specular); - loadTextureImage(matId, aiTextureType_EMISSIVE); + loadTextureImage(mesh, aiTextureType_EMISSIVE); fallBackToDefaultTexture(mat, Emissive); } } @@ -471,16 +471,16 @@ DENG2_PIMPL(ModelDrawable) * Attempts to load a texture image specified in the material. Also checks if * an overridden custom path is provided, though. * - * @param materialId Material index. - * @param type AssImp texture type. + * @param mesh Identifies the mesh whose texture is being loaded. + * @param type AssImp texture type. */ - void loadTextureImage(MaterialId const &matId, aiTextureType type) + void loadTextureImage(MeshId const &mesh, aiTextureType type) { DENG2_ASSERT(imageLoader != 0); TextureMap map = textureMapType(type); - aiMaterial const &material = *scene->mMaterials[matId.id]; - auto const &materialData = variants.at(matId.variant)->materials[matId.id]; + aiMaterial const &material = *scene->mMaterials[mesh.index]; + auto const &materialData = variants.at(mesh.material)->materials[mesh.index]; try { @@ -488,7 +488,7 @@ DENG2_PIMPL(ModelDrawable) if(materialData.custom.contains(map)) { qDebug() << "loading custom path" << materialData.custom[map]; - return setTexture(matId, map, materialData.custom[map]); + return setTexture(mesh, map, materialData.custom[map]); } } catch(Error const &er) @@ -510,7 +510,7 @@ DENG2_PIMPL(ModelDrawable) try { - setTexture(matId, map, sourcePath.fileNamePath() / NativePath(texPath.C_Str())); + setTexture(mesh, map, sourcePath.fileNamePath() / NativePath(texPath.C_Str())); break; } catch(Error const &er) @@ -522,17 +522,19 @@ DENG2_PIMPL(ModelDrawable) } } - void setTexture(MaterialId const &matId, TextureMap map, String contentPath) + void setTexture(MeshId const &mesh, TextureMap map, String contentPath) { if(!scene) return; - if(matId.variant >= duint(variants.size())) return; - if(matId.id >= scene->mNumMaterials) return; + if(mesh.material >= duint(variants.size())) return; + if(mesh.index >= scene->mNumMaterials) return; if(map == Unknown) return; + DENG2_ASSERT(scene->mNumMaterials == scene->mNumMeshes); DENG2_ASSERT(textureBank.atlas()); - Variant &variant = *variants[matId.variant]; - auto &materialData = variant.materials[matId.id]; + Variant &variant = *variants[mesh.material]; + auto &materialData = variant.materials[mesh.index]; + Id::Type &destId = (map == Height? materialData.texIds[Normals] : materialData.texIds[map]); @@ -579,16 +581,16 @@ DENG2_PIMPL(ModelDrawable) * @param tex Texture map. * @param path Image file path. */ - void setCustomTexturePath(MaterialId const &matId, TextureMap map, String const &path) + void setCustomTexturePath(MeshId const &mesh, TextureMap map, String const &path) { DENG2_ASSERT(!textureBank.atlas()); - DENG2_ASSERT(matId.variant < duint(variants.size())); + DENG2_ASSERT(mesh.material < duint(variants.size())); - Variant &variant = *variants[matId.variant]; + Variant &variant = *variants[mesh.material]; - DENG2_ASSERT(matId.id < duint(variant.materials.size())); + DENG2_ASSERT(mesh.index < duint(variant.materials.size())); - variant.materials[matId.id].custom.insert(map, path); + variant.materials[mesh.index].custom.insert(map, path); } }; @@ -656,7 +658,6 @@ DENG2_PIMPL(ModelDrawable) scene = importer.GetScene(); glData.initVariants(scene); - drawMaterial = 0; initBones(); @@ -1156,15 +1157,6 @@ DENG2_PIMPL(ModelDrawable) anim.mNumPositionKeys)); } -//- Drawing ----------------------------------------------------------------------------- - - GLProgram *drawProgram = nullptr; - Pass const *drawPass = nullptr; - QBitArray passMask; - int drawMaterial = 0; - ProgramBindingFunc programCallback; - RenderingPassFunc passCallback; - void updateMatricesFromAnimation(Animator const *animation) const { if(!scene->HasAnimations() || !animation) return; @@ -1184,6 +1176,11 @@ DENG2_PIMPL(ModelDrawable) } } +//- Drawing ----------------------------------------------------------------------------- + + GLProgram *drawProgram = nullptr; + Pass const *drawPass = nullptr; + void preDraw(Animator const *animation) { if(glData.needMakeBuffer) makeBuffer(); @@ -1196,14 +1193,14 @@ DENG2_PIMPL(ModelDrawable) GLState::current().apply(); } - void setDrawProgram(GLProgram *prog) + void setDrawProgram(GLProgram *prog, Appearance const *appearance = nullptr) { if(drawProgram) { drawProgram->unbind(uBoneMatrices); - if(programCallback) + if(appearance && appearance->programCallback) { - programCallback(*drawProgram, Unbound); + appearance->programCallback(*drawProgram, Unbound); } } @@ -1211,9 +1208,9 @@ DENG2_PIMPL(ModelDrawable) if(drawProgram) { - if(programCallback) + if(appearance && appearance->programCallback) { - programCallback(*drawProgram, AboutToBind); + appearance->programCallback(*drawProgram, AboutToBind); } drawProgram->bind(uBoneMatrices); } @@ -1249,25 +1246,28 @@ DENG2_PIMPL(ModelDrawable) } } - void draw(Animator const *animation, Passes const &passes) + void draw(Appearance const *appearance, Animator const *animation) { + Passes const *passes = appearance->drawPasses? appearance->drawPasses : + &defaultPasses; preDraw(animation); try { GLBuffer::DrawRanges ranges; - for(int i = 0; i < passes.size(); ++i) + for(int i = 0; i < passes->size(); ++i) { - Pass const &pass = passes.at(i); + Pass const &pass = passes->at(i); // Is this pass disabled? - if(!passMask.isEmpty() && !passMask.testBit(i)) + if(appearance && !appearance->passMask.isEmpty() && + !appearance->passMask.testBit(i)) { continue; } drawPass = &pass; - setDrawProgram(pass.program? pass.program : program); + setDrawProgram(pass.program? pass.program : program, appearance); if(!drawProgram) { throw ProgramError("ModelDrawable::draw", @@ -1275,9 +1275,15 @@ DENG2_PIMPL(ModelDrawable) .arg(i).arg(pass.name)); } - if(passCallback) + if(appearance && appearance->passCallback) { - passCallback(pass, PassBegun); + appearance->passCallback(pass, PassBegun); + } + + duint material = 0; + if(appearance && appearance->passMaterial.size() >= passes->size()) + { + material = appearance->passMaterial.at(i); } ranges.clear(); @@ -1289,14 +1295,14 @@ DENG2_PIMPL(ModelDrawable) .apply(); { drawProgram->beginUse(); - glData.variants.at(drawMaterial)->buffer->draw(&ranges); + glData.variants.at(material)->buffer->draw(&ranges); drawProgram->endUse(); } GLState::pop(); - if(passCallback) + if(appearance && appearance->passCallback) { - passCallback(pass, PassEnded); + appearance->passCallback(pass, PassEnded); } } } @@ -1312,9 +1318,11 @@ DENG2_PIMPL(ModelDrawable) void drawInstanced(GLBuffer const &attribs, Animator const *animation) { + /// @todo Rendering passes for instanced drawing. -jk + preDraw(animation); - setDrawProgram(program); /// @todo Rendering passes for instanced drawing. -jk - glData.variants.at(drawMaterial)->buffer->drawInstanced(attribs); + setDrawProgram(program); + glData.variants.at(0)->buffer->drawInstanced(attribs); postDraw(); } @@ -1498,17 +1506,17 @@ int ModelDrawable::materialId(String const &name) const return d->findMaterial(name); } -void ModelDrawable::setTexturePath(MaterialId const &material, TextureMap tex, String const &path) +void ModelDrawable::setTexturePath(MeshId const &mesh, TextureMap tex, String const &path) { if(d->glData.textureBank.atlas()) { // Load immediately. - d->glData.setTexture(material, tex, path); + d->glData.setTexture(mesh, tex, path); } else { // This will override what the model specifies. - d->glData.setCustomTexturePath(material, tex, path); + d->glData.setCustomTexturePath(mesh, tex, path); } } @@ -1522,24 +1530,14 @@ GLProgram *ModelDrawable::program() const return d->program; } -void ModelDrawable::draw(Animator const *animation, - Passes const *passes, - QBitArray const &passMask, - ProgramBindingFunc programCallback, - RenderingPassFunc passCallback) const +void ModelDrawable::draw(Appearance const *appearance, + Animator const *animation) const { const_cast(this)->glInit(); if(isReady() && d->glData.textureBank.atlas()) { - d->passMask = passMask; - d->programCallback = programCallback; - d->passCallback = passCallback; - - d->draw(animation, passes? *passes : d->defaultPasses); - - d->programCallback = ProgramBindingFunc(); - d->passCallback = RenderingPassFunc(); + d->draw(appearance, animation); } } @@ -1564,14 +1562,6 @@ GLProgram *ModelDrawable::currentProgram() const return d->drawProgram; } -void ModelDrawable::setMaterial(duint index) const -{ - if(index < duint(d->glData.variants.size())) - { - d->drawMaterial = index; - } -} - Vector3f ModelDrawable::dimensions() const { return d->maxPoint - d->minPoint; @@ -1582,6 +1572,16 @@ Vector3f ModelDrawable::midPoint() const return (d->maxPoint + d->minPoint) / 2.f; } +int ModelDrawable::Passes::findName(String const &name) const +{ + for(int i = 0; i < size(); ++i) + { + if(at(i).name == name) // case sensitive + return i; + } + return -1; +} + } // namespace de //---------------------------------------------------------------------------------------