Skip to content

Commit

Permalink
Model Renderer: Enabling and disabling rendering passes
Browse files Browse the repository at this point in the history
Each rendering pass has a variable called "enabled" in its record.
This can be set to True or False.

Also improved error handling when loading a model.
  • Loading branch information
skyjake committed Oct 15, 2015
1 parent 7ddee3e commit f7d7491
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 21 deletions.
3 changes: 3 additions & 0 deletions doomsday/apps/client/include/render/modelrenderer.h
Expand Up @@ -19,6 +19,7 @@
#ifndef DENG_CLIENT_MODELRENDERER_H
#define DENG_CLIENT_MODELRENDERER_H

#include <de/Function>
#include <de/ModelDrawable>
#include <de/ModelBank>
#include <de/GLState>
Expand Down Expand Up @@ -118,6 +119,8 @@ class ModelRenderer
void render(vispsprite_t const &pspr);

public:
static void initBindings(de::Binder &binder, de::Record &module);

static int identifierFromText(de::String const &text,
std::function<int (de::String const &)> resolver);

Expand Down
6 changes: 6 additions & 0 deletions doomsday/apps/client/include/render/stateanimator.h
Expand Up @@ -51,6 +51,12 @@ 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;

enum BindOperation { Bind, Unbind };

/**
Expand Down
41 changes: 27 additions & 14 deletions doomsday/apps/client/src/render/modelrenderer.cpp
Expand Up @@ -47,7 +47,6 @@ static String const DEF_RENDER ("render");
static String const DEF_TEXTURE_MAPPING("textureMapping");
static String const DEF_SHADER ("shader");
static String const DEF_PASS ("pass");
static String const DEF_ENABLED ("enabled");
static String const DEF_MESHES ("meshes");
static String const DEF_BLENDFUNC ("blendFunc");
static String const DEF_BLENDOP ("blendOp");
Expand Down Expand Up @@ -388,12 +387,13 @@ DENG2_PIMPL(ModelRenderer)
}

ModelDrawable::Mapping textureMapping;
String modelShader = DEFAULT_SHADER;

// Rendering passes.
if(asset.has(DEF_RENDER))
{
Record const &renderBlock = asset.subrecord(DEF_RENDER);
String const modelShader = renderBlock.gets(DEF_SHADER, DEFAULT_SHADER);
modelShader = renderBlock.gets(DEF_SHADER, modelShader);

auto passes = ScriptedInfo::subrecordsOfType(DEF_PASS, renderBlock);
for(String key : ScriptedInfo::sortRecordsBySource(passes))
Expand All @@ -403,13 +403,9 @@ DENG2_PIMPL(ModelRenderer)
auto const &def = *passes[key];

ModelDrawable::Pass pass;
pass.meshes.resize(model.meshCount());
pass.name = key;
applyFlagOperation(pass.flags,
ModelDrawable::Pass::Enabled,
ScriptedInfo::isFalse(def, DEF_ENABLED, false)?
UnsetFlags : SetFlags);

pass.meshes.resize(model.meshCount());
for(Value const *value : def.geta(DEF_MESHES).elements())
{
int meshId = identifierFromText(value->asText(), [&model] (String const &text) {
Expand All @@ -433,10 +429,10 @@ DENG2_PIMPL(ModelRenderer)

aux->passes.append(pass);
}
catch(DefinitionError const &er)
catch(Error const &er)
{
LOG_RES_ERROR("Error in rendering pass definition of asset \"%s\": %s")
<< path << er.asText();
LOG_RES_ERROR("Rendering pass \"%s\" in asset \"%s\" is invalid: %s")
<< key << path << er.asText();
}
}
}
Expand All @@ -446,10 +442,18 @@ DENG2_PIMPL(ModelRenderer)
// shader for the entire model.
if(aux->passes.isEmpty())
{
// Use the default shader (use count not incremented).
model.setProgram(programs[DEFAULT_SHADER]);
composeTextureMappings(textureMapping,
ClientApp::shaders()[DEFAULT_SHADER]);
try
{
// Use the default shader (use count not incremented).
model.setProgram(programs[modelShader]);
composeTextureMappings(textureMapping,
ClientApp::shaders()[modelShader]);
}
catch(Error const &er)
{
LOG_RES_ERROR("Asset \"%s\" cannot use shader \"%s\": %s")
<< path << modelShader << er.asText();
}
}

// Configure the texture mapping. Shaders used with the model must
Expand Down Expand Up @@ -565,6 +569,7 @@ DENG2_PIMPL(ModelRenderer)
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)
Expand Down Expand Up @@ -711,6 +716,14 @@ int ModelRenderer::identifierFromText(String const &text,
return id;
}

void ModelRenderer::initBindings(Binder &binder, Record &module) // static
{
DENG2_UNUSED(binder);
DENG2_UNUSED(module);
}

//-----------------------------------------------------------------------------

ModelRenderer::AnimSequence::AnimSequence(String const &name, Record const &def)
: name(name)
, def(&def)
Expand Down
61 changes: 54 additions & 7 deletions doomsday/apps/client/src/render/stateanimator.cpp
Expand Up @@ -35,16 +35,19 @@ static String const DEF_PRIORITY ("priority");
static String const DEF_PASS ("pass");
static String const DEF_VARIABLE ("variable");
static String const DEF_WRAP ("wrap");
static String const DEF_ENABLED ("enabled");

static String const VAR_SELF ("self");
static String const VAR_ID ("__id__");
static String const VAR_ASSET("asset");
static String const VAR_SELF ("self");
static String const VAR_ID ("__id__");
static String const VAR_ASSET ("asset");
static String const VAR_ENABLED("enabled");

static String const PASS_GLOBAL("");

static int const ANIM_DEFAULT_PRIORITY = 1;

DENG2_PIMPL(StateAnimator)
, DENG2_OBSERVES(Variable, Change)
{
/**
* Specialized animation sequence state for a running animation.
Expand Down Expand Up @@ -112,6 +115,8 @@ DENG2_PIMPL(StateAnimator)
QHash<String, Sequence> pendingAnimForNode;
String currentStateName;
Record names; ///< Local context for scripts.
QBitArray passMask;
QHash<String, int> passIndexLookup;

/**
* Animatable variable bound to a GL uniform. The value can have 1...4 float
Expand Down Expand Up @@ -214,23 +219,40 @@ DENG2_PIMPL(StateAnimator)

void initShaderVariables()
{
int passIndex = 0;
passIndexLookup.clear();

auto const &def = names[VAR_ASSET].valueAsRecord();
if(def.has("render"))
{
Record const &renderBlock = def.subrecord("render");

parseVariables(renderBlock);
initVariablesForPass(renderBlock);

// Check variables in each rendering pass.
// Each rendering pass is represented by a subrecord, named
// according the to the pass names.
auto passes = ScriptedInfo::subrecordsOfType(DEF_PASS, renderBlock);
DENG2_FOR_EACH_CONST(Record::Subrecords, i, passes)
{
parseVariables(*i.value(), i.key());
passIndexLookup[i.key()] = passIndex++;

Record &passRec = names.addRecord(i.key());
passRec.addBoolean(VAR_ENABLED,
ScriptedInfo::isTrue(*i.value(), DEF_ENABLED, true))
.audienceForChange() += this;

initVariablesForPass(*i.value(), i.key());
}
}

DENG2_ASSERT(passIndex == auxData->passes.size());
DENG2_ASSERT(passIndexLookup.size() == auxData->passes.size());

passMask.resize(auxData->passes.size());
updatePassMask();
}

void parseVariables(Record const &block, String const &passName = PASS_GLOBAL)
void initVariablesForPass(Record const &block, String const &passName = PASS_GLOBAL)
{
static char const *componentNames[] = { "x", "y", "z", "w" };

Expand Down Expand Up @@ -328,6 +350,26 @@ DENG2_PIMPL(StateAnimator)
passVars.clear();
}

void variableValueChanged(Variable &, Value const &)
{
// This is called when one of the "(pass).enabled" variables is modified.
updatePassMask();
}

void updatePassMask()
{
Record::Subrecords enabledPasses = names.subrecords([] (Record const &sub) {
return sub.getb(DEF_ENABLED, false);
});

passMask.fill(false);
for(String name : enabledPasses.keys())
{
DENG2_ASSERT(passIndexLookup.contains(name));
passMask.setBit(passIndexLookup[name], true);
}
}

int animationId(String const &name) const
{
return ModelRenderer::identifierFromText(name, [this] (String const &name) {
Expand Down Expand Up @@ -523,6 +565,11 @@ ddouble StateAnimator::currentTime(int index) const
return ModelDrawable::Animator::currentTime(index); // + frameTimePos;
}

QBitArray StateAnimator::passMask() const
{
return d->passMask;
}

void StateAnimator::bindUniforms(GLProgram &program, BindOperation operation) const
{
bindPassUniforms(program, PASS_GLOBAL, operation);
Expand Down

0 comments on commit f7d7491

Please sign in to comment.