Add render::Shape to abstract ModelRender

This commit is contained in:
Zach Pomerantz 2015-12-31 15:52:28 -08:00
parent 722fe46a27
commit 16bb861046
2 changed files with 270 additions and 0 deletions

View file

@ -0,0 +1,101 @@
//
// Shape.cpp
// render/src/render
//
// Created by Zach Pomerantz on 12/31/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Shape.h"
#include <PerfStat.h>
using namespace render;
Shape::PipelineLib _pipelineLip;
const Shape::Pipeline& Shape::_pickPipeline(RenderArgs* args, const Key& key) {
assert(!_pipelineLib.empty());
assert(args);
assert(args->_batch);
PerformanceTimer perfTimer("Shape::getPipeline");
const auto& pipelineIterator = _pipelineLib.find(key);
if (pipelineIterator == _pipelineLib.end()) {
qDebug() << "Couldn't find a pipeline from ShapeKey ?" << key;
return Shape::Pipeline(); // uninitialized _pipeline == nullptr
}
const auto& shapePipeline = pipelineIterator->second;
auto& batch = args->_batch;
// Setup the one pipeline (to rule them all)
batch->setPipeline(shapePipeline.pipeline);
return shapePipeline;
}
void Shape::PipelineLib::addPipeline(Key key, gpu::ShaderPointer& vertexShader, gpu::ShaderPointer& pixelShader) {
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding(std::string("skinClusterBuffer"), Shape::Slots::SKINNING_GPU));
slotBindings.insert(gpu::Shader::Binding(std::string("materialBuffer"), Shape::Slots::MATERIAL_GPU));
slotBindings.insert(gpu::Shader::Binding(std::string("diffuseMap"), Shape::Slots::DIFFUSE_MAP));
slotBindings.insert(gpu::Shader::Binding(std::string("normalMap"), Shape::Slots::NORMAL_MAP));
slotBindings.insert(gpu::Shader::Binding(std::string("specularMap"), Shape::Slots::SPECULAR_MAP));
slotBindings.insert(gpu::Shader::Binding(std::string("emissiveMap"), Shape::Slots::LIGHTMAP_MAP));
slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), Shape::Slots::LIGHT_BUFFER));
slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), Shape::Slots::NORMAL_FITTING_MAP));
gpu::ShaderPointer program = gpu::Shader::createProgram(vertexShader, pixelShader);
gpu::Shader::makeProgram(*program, slotBindings);
auto locations = std::make_shared<Locations>();
locations->texcoordMatrices = program->getUniforms().findLocation("texcoordMatrices");
locations->emissiveParams = program->getUniforms().findLocation("emissiveParams");
locations->normalFittingMapUnit = program->getTextures().findLocation("normalFittingMap");
locations->diffuseTextureUnit = program->getTextures().findLocation("diffuseMap");
locations->normalTextureUnit = program->getTextures().findLocation("normalMap");
locations->specularTextureUnit = program->getTextures().findLocation("specularMap");
locations->emissiveTextureUnit = program->getTextures().findLocation("emissiveMap");
locations->skinClusterBufferUnit = program->getBuffers().findLocation("skinClusterBuffer");
locations->materialBufferUnit = program->getBuffers().findLocation("materialBuffer");
locations->lightBufferUnit = program->getBuffers().findLocation("lightBuffer");
auto state = std::make_shared<gpu::State>();
// Backface on shadow
if (key.isShadow()) {
state->setCullMode(gpu::State::CULL_FRONT);
state->setDepthBias(1.0f);
state->setDepthBiasSlopeScale(4.0f);
} else {
state->setCullMode(gpu::State::CULL_BACK);
}
// Z test depends on transparency
state->setDepthTest(true, !key.isTranslucent(), gpu::LESS_EQUAL);
// Blend if transparent
state->setBlendFunction(key.isTranslucent(),
gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, // For transparent only, this keep the highlight intensity
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
// Add the brand new pipeline and cache its location in the lib
auto pipeline = gpu::Pipeline::create(program, state);
insert(value_type(key, Shape::Pipeline(pipeline, locations)));
// Add a wireframe version
if (!key.isWireFrame()) {
ShapeKey wireframeKey(ShapeKey::Builder(key).withWireframe());
auto wireframeState = std::make_shared<gpu::State>(state->getValues());
wireframeState->setFillMode(gpu::State::FILL_LINE);
auto wireframePipeline = gpu::Pipeline::create(program, wireframeState);
insert(value_type(wireframeKey, Shape::Pipeline(wireframePipeline, locations)));
}
}

View file

@ -0,0 +1,169 @@
//
// Shape.h
// render/src/render
//
// Created by Zach Pomerantz on 12/31/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_render_Shape_h
#define hifi_render_Shape_h
#include <gpu/Batch.h>
#include <RenderArgs.h>
namespace render {
class ShapeKey {
public:
enum FlagBit {
TRANSLUCENT = 0,
LIGHTMAP,
TANGENTS,
SPECULAR,
EMISSIVE,
SKINNED,
STEREO,
DEPTH_ONLY,
SHADOW,
WIREFRAME,
NUM_FLAGS, // Not a valid flag
};
using Flags = std::bitset<NUM_FLAGS>;
Flags _flags;
ShapeKey() : _flags{ 0 } {}
ShapeKey(const Flags& flags) : _flags(flags) {}
class Builder {
friend class ShapeKey;
Flags _flags{ 0 };
public:
Builder() {}
Builder(ShapeKey key) : _flags{ key._flags } {}
ShapeKey build() const { return ShapeKey(_flags); }
Builder& withTranslucent() { _flags.set(TRANSLUCENT); return (*this); }
Builder& withLightmap() { _flags.set(LIGHTMAP); return (*this); }
Builder& withTangents() { _flags.set(TANGENTS); return (*this); }
Builder& withSpecular() { _flags.set(SPECULAR); return (*this); }
Builder& withEmissive() { _flags.set(EMISSIVE); return (*this); }
Builder& withSkinned() { _flags.set(SKINNED); return (*this); }
Builder& withStereo() { _flags.set(STEREO); return (*this); }
Builder& withDepthOnly() { _flags.set(DEPTH_ONLY); return (*this); }
Builder& withShadow() { _flags.set(SHADOW); return (*this); }
Builder& withWireframe() { _flags.set(WIREFRAME); return (*this); }
};
ShapeKey(const Builder& builder) : ShapeKey(builder._flags) {}
bool hasLightmap() const { return _flags[LIGHTMAP]; }
bool hasTangents() const { return _flags[TANGENTS]; }
bool hasSpecular() const { return _flags[SPECULAR]; }
bool hasEmissive() const { return _flags[EMISSIVE]; }
bool isTranslucent() const { return _flags[TRANSLUCENT]; }
bool isSkinned() const { return _flags[SKINNED]; }
bool isStereo() const { return _flags[STEREO]; }
bool isDepthOnly() const { return _flags[DEPTH_ONLY]; }
bool isShadow() const { return _flags[SHADOW]; }
bool isWireFrame() const { return _flags[WIREFRAME]; }
// Hasher for use in unordered_maps
class Hash {
public:
size_t operator() (const ShapeKey& key) {
return std::hash<ShapeKey::Flags>()(key._flags);
}
};
// Comparator for use in unordered_maps
class KeyEqual {
public:
bool operator()(const ShapeKey& lhs, const ShapeKey& rhs) const { return lhs._flags == rhs._flags; }
};
};
inline QDebug operator<<(QDebug debug, const ShapeKey& renderKey) {
debug << "[ShapeKey:"
<< "hasLightmap:" << renderKey.hasLightmap()
<< "hasTangents:" << renderKey.hasTangents()
<< "hasSpecular:" << renderKey.hasSpecular()
<< "hasEmissive:" << renderKey.hasEmissive()
<< "isTranslucent:" << renderKey.isTranslucent()
<< "isSkinned:" << renderKey.isSkinned()
<< "isStereo:" << renderKey.isStereo()
<< "isDepthOnly:" << renderKey.isDepthOnly()
<< "isShadow:" << renderKey.isShadow()
<< "isWireFrame:" << renderKey.isWireFrame()
<< "]";
return debug;
}
// Meta-information (pipeline and locations) to render a shape
class Shape {
public:
using Key = ShapeKey;
class Slots {
public:
static const int SKINNING_GPU = 2;
static const int MATERIAL_GPU = 3;
static const int DIFFUSE_MAP = 0;
static const int NORMAL_MAP = 1;
static const int SPECULAR_MAP = 2;
static const int LIGHTMAP_MAP = 3;
static const int LIGHT_BUFFER = 4;
static const int NORMAL_FITTING_MAP = 10;
};
class Locations {
public:
int texcoordMatrices;
int diffuseTextureUnit;
int normalTextureUnit;
int specularTextureUnit;
int emissiveTextureUnit;
int emissiveParams;
int normalFittingMapUnit;
int skinClusterBufferUnit;
int materialBufferUnit;
int lightBufferUnit;
};
// Rendering abstraction over gpu::Pipeline and map locations
class Pipeline {
public:
gpu::PipelinePointer pipeline;
std::shared_ptr<Locations> locations;
Pipeline() : Pipeline(nullptr, nullptr) {}
Pipeline(gpu::PipelinePointer pipeline, std::shared_ptr<Locations> locations) :
pipeline(pipeline), locations(locations) {}
};
using PipelineMap = std::unordered_map<ShapeKey, Pipeline, ShapeKey::Hash, ShapeKey::KeyEqual>;
class PipelineLib : public PipelineMap {
public:
void addPipeline(Key key, gpu::ShaderPointer& vertexShader, gpu::ShaderPointer& pixelShader);
};
static void addPipeline(Key key, gpu::ShaderPointer& vertexShader, gpu::ShaderPointer& pixelShader) {
_pipelineLib.addPipeline(key, vertexShader, pixelShader);
}
virtual const Pipeline& pickPipeline(RenderArgs* args, const Key& key) {
return Shape::_pickPipeline(args, key);
}
protected:
static const Pipeline& _pickPipeline(RenderArgs* args, const Key& key);
static PipelineLib _pipelineLib;
};
}
#endif // hifi_render_Shape_h