Merge pull request #5009 from ZappoMan/team-teaching-scene-api

TEAM TEACHING - hacking on rendering mesh parts
This commit is contained in:
samcake 2015-06-01 12:24:27 -07:00
commit 98c0cd7d23
15 changed files with 636 additions and 104 deletions

View file

@ -844,7 +844,7 @@ void Application::paintGL() {
_glWidget->makeCurrent();
auto lodManager = DependencyManager::get<LODManager>();
gpu::Context context;
gpu::Context context(new gpu::GLBackend());
RenderArgs renderArgs(&context, nullptr, getViewFrustum(), lodManager->getOctreeSizeScale(),
lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE,
RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE);
@ -3183,12 +3183,12 @@ namespace render {
if (args->_renderMode != CAMERA_MODE_MIRROR && Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
PerformanceTimer perfTimer("worldBox");
renderWorldBox();
// FIXME: there's currently a bug in the new render engine, if this origin dot is rendered out of view it will
// screw up the state of textures on models so they all end up rendering in the incorrect tint/color/texture
float originSphereRadius = 0.05f;
DependencyManager::get<GeometryCache>()->renderSphere(originSphereRadius, 15, 15, glm::vec4(1.0f, 0.0f, 0.0f, 1.0f));
}
// never the less
float originSphereRadius = 0.05f;
DependencyManager::get<GeometryCache>()->renderSphere(originSphereRadius, 15, 15, glm::vec4(1.0f, 0.0f, 0.0f, 1.0f));
}
}

View file

@ -489,6 +489,10 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityIt
void EntityTreeRenderer::render(RenderArgs* renderArgs) {
if (_tree && !_shuttingDown) {
renderArgs->_renderer = this;
checkPendingAddToScene(renderArgs);
Model::startScene(renderArgs->_renderSide);
ViewFrustum* frustum = (renderArgs->_renderMode == RenderArgs::SHADOW_RENDER_MODE) ?
@ -503,7 +507,6 @@ void EntityTreeRenderer::render(RenderArgs* renderArgs) {
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
renderArgs->_renderer = this;
renderArgs->_batch = &batch;
_tree->lockForRead();
@ -521,7 +524,7 @@ void EntityTreeRenderer::render(RenderArgs* renderArgs) {
_tree->unlock();
glPushMatrix();
renderArgs->_context->enqueueBatch(batch);
renderArgs->_context->render(batch);
glPopMatrix();
renderArgs->_batch = nullptr;
@ -1081,16 +1084,38 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) {
checkAndCallPreload(entityID);
// here's where we add the entity payload to the scene
auto entity = static_cast<EntityTree*>(_tree)->findEntityByID(entityID);
addEntityToScene(entity);
}
void EntityTreeRenderer::addEntityToScene(EntityItemPointer entity) {
// here's where we add the entity payload to the scene
if (entity && entity->canRenderInScene()) {
render::PendingChanges pendingChanges;
auto scene = _viewState->getMain3DScene();
if (entity->addToScene(entity, scene, pendingChanges)) {
_entitiesInScene.insert(entity);
if (entity->readyToAddToScene()) {
render::PendingChanges pendingChanges;
auto scene = _viewState->getMain3DScene();
if (entity->addToScene(entity, scene, pendingChanges)) {
_entitiesInScene.insert(entity);
}
scene->enqueuePendingChanges(pendingChanges);
} else {
if (!_pendingAddToScene.contains(entity)) {
_pendingAddToScene << entity;
}
}
scene->enqueuePendingChanges(pendingChanges);
}
}
void EntityTreeRenderer::checkPendingAddToScene(RenderArgs* renderArgs) {
QSet<EntityItemPointer> addedToScene;
foreach (auto entity, _pendingAddToScene) {
if (entity->readyToAddToScene(renderArgs)) {
addEntityToScene(entity);
addedToScene << entity;
}
}
foreach (auto addedEntity, addedToScene) {
_pendingAddToScene.remove(addedEntity);
}
}

View file

@ -123,6 +123,10 @@ protected:
virtual Octree* createTree() { return new EntityTree(true); }
private:
void checkPendingAddToScene(RenderArgs* renderArgs);
void addEntityToScene(EntityItemPointer entity);
QSet<EntityItemPointer> _pendingAddToScene;
void applyZonePropertiesToScene(std::shared_ptr<ZoneEntityItem> zone);
void renderElementProxy(EntityTreeElement* entityTreeElement, RenderArgs* args);
void checkAndCallPreload(const EntityItemID& entityID);

View file

@ -108,6 +108,49 @@ void RenderableModelEntityItem::remapTextures() {
_currentTextures = _textures;
}
// TODO: we need a solution for changes to the postion/rotation/etc of a model...
// this current code path only addresses that in this setup case... not the changing/moving case
bool RenderableModelEntityItem::readyToAddToScene(RenderArgs* renderArgs) {
if (!_model && renderArgs) {
// TODO: this getModel() appears to be about 3% of model render time. We should optimize
PerformanceTimer perfTimer("getModel");
EntityTreeRenderer* renderer = static_cast<EntityTreeRenderer*>(renderArgs->_renderer);
getModel(renderer);
}
if (renderArgs && _model && _needsInitialSimulation && _model->isActive() && _model->isLoadedWithTextures()) {
_model->setScaleToFit(true, getDimensions());
_model->setSnapModelToRegistrationPoint(true, getRegistrationPoint());
_model->setRotation(getRotation());
_model->setTranslation(getPosition());
// make sure to simulate so everything gets set up correctly for rendering
{
PerformanceTimer perfTimer("_model->simulate");
_model->simulate(0.0f);
}
_needsInitialSimulation = false;
_model->renderSetup(renderArgs);
}
bool ready = !_needsInitialSimulation && _model && _model->readyToAddToScene(renderArgs);
return ready;
}
bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges) {
if (_model) {
return _model->addToScene(scene, pendingChanges);
}
return false;
}
void RenderableModelEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges) {
if (_model) {
_model->removeFromScene(scene, pendingChanges);
}
}
void RenderableModelEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RMEIrender");
assert(getType() == EntityTypes::Model);
@ -199,6 +242,10 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) {
Model* result = NULL;
if (!renderer) {
return result;
}
// make sure our renderer is setup
if (!_myRenderer) {
@ -206,7 +253,7 @@ Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) {
}
assert(_myRenderer == renderer); // you should only ever render on one renderer
if (QThread::currentThread() != _myRenderer->thread()) {
if (!_myRenderer || QThread::currentThread() != _myRenderer->thread()) {
return _model;
}

View file

@ -43,7 +43,12 @@ public:
virtual void somethingChangedNotification() { _needsInitialSimulation = true; }
virtual bool canRenderInScene() { return false; } // we don't yet play well with others
virtual bool canRenderInScene() { return true; }
virtual bool readyToAddToScene(RenderArgs* renderArgs = nullptr);
virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
virtual void render(RenderArgs* args);
virtual bool supportsDetailedRayIntersection() const { return true; }
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,

View file

@ -653,15 +653,15 @@ void EntityItem::adjustEditPacketForClockSkew(unsigned char* editPacketBuffer, s
// lastEdited
quint64 lastEditedInLocalTime;
memcpy(&lastEditedInLocalTime, dataAt, sizeof(lastEditedInLocalTime));
assert(lastEditedInLocalTime > 0);
quint64 lastEditedInServerTime = lastEditedInLocalTime + clockSkew;
memcpy(dataAt, &lastEditedInServerTime, sizeof(lastEditedInServerTime));
#ifdef WANT_DEBUG
#if 1 //def WANT_DEBUG
qCDebug(entities, "EntityItem::adjustEditPacketForClockSkew()...");
qCDebug(entities) << " lastEditedInLocalTime: " << lastEditedInLocalTime;
qCDebug(entities) << " clockSkew: " << clockSkew;
qCDebug(entities) << " lastEditedInServerTime: " << lastEditedInServerTime;
#endif
//assert(lastEditedInLocalTime > (quint64)0);
}
float EntityItem::computeMass() const {
@ -980,7 +980,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
}
void EntityItem::recordCreationTime() {
assert(_created == UNKNOWN_CREATED_TIME);
//assert(_created == UNKNOWN_CREATED_TIME);
_created = usecTimestampNow();
_lastEdited = _created;
_lastUpdated = _created;

View file

@ -158,6 +158,7 @@ public:
{ return 0; }
virtual bool canRenderInScene() { return false; } // does your entity property render using Render Items and Payloads
virtual bool readyToAddToScene(RenderArgs* renderArgs = nullptr) { return true; } // we assume you're ready to add
virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges) { return false; } // by default entity items don't add to scene
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,

View file

@ -15,7 +15,8 @@
using namespace gpu;
Context::Context() {
Context::Context(Backend* backend) :
_backend(backend) {
}
Context::Context(const Context& context) {
@ -31,6 +32,7 @@ bool Context::makeProgram(Shader& shader, const Shader::BindingSet& bindings) {
return false;
}
void Context::enqueueBatch(Batch& batch) {
GLBackend::renderBatch(batch, true);
void Context::render(Batch& batch) {
_backend->render(batch);
// GLBackend::renderBatch(batch, true);
}

View file

@ -31,6 +31,9 @@ class Batch;
class Backend {
public:
virtual~ Backend() {};
virtual void render(Batch& batch) = 0;
class TransformObject {
public:
Mat4 _model;
@ -107,15 +110,14 @@ protected:
class Context {
public:
Context();
Context(const Context& context);
Context(Backend* backend);
~Context();
void enqueueBatch(Batch& batch);
void render(Batch& batch);
protected:
Context(const Context& context);
// This function can only be called by "static Shader::makeProgram()"
// makeProgramShader(...) make a program shader ready to be used in a Batch.
@ -123,6 +125,8 @@ protected:
// If the shader passed is not a program, nothing happens.
static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet());
std::unique_ptr<Backend> _backend;
friend class Shader;
};

View file

@ -442,6 +442,8 @@ void Batch::_glUniform1f(GLint location, GLfloat v0) {
}
void GLBackend::do_glUniform1f(Batch& batch, uint32 paramOffset) {
if (_pipeline._program == 0) {
// We should call updatePipeline() to bind the program but we are not doing that
// because these uniform setters are deprecated and we don;t want to create side effect
return;
}
glUniform1f(
@ -460,6 +462,11 @@ void Batch::_glUniform2f(GLint location, GLfloat v0, GLfloat v1) {
DO_IT_NOW(_glUniform2f, 1);
}
void GLBackend::do_glUniform2f(Batch& batch, uint32 paramOffset) {
if (_pipeline._program == 0) {
// We should call updatePipeline() to bind the program but we are not doing that
// because these uniform setters are deprecated and we don;t want to create side effect
return;
}
glUniform2f(
batch._params[paramOffset + 2]._int,
batch._params[paramOffset + 1]._float,
@ -478,6 +485,11 @@ void Batch::_glUniform4fv(GLint location, GLsizei count, const GLfloat* value) {
DO_IT_NOW(_glUniform4fv, 3);
}
void GLBackend::do_glUniform4fv(Batch& batch, uint32 paramOffset) {
if (_pipeline._program == 0) {
// We should call updatePipeline() to bind the program but we are not doing that
// because these uniform setters are deprecated and we don;t want to create side effect
return;
}
glUniform4fv(
batch._params[paramOffset + 2]._int,
batch._params[paramOffset + 1]._uint,
@ -498,6 +510,11 @@ void Batch::_glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpo
DO_IT_NOW(_glUniformMatrix4fv, 4);
}
void GLBackend::do_glUniformMatrix4fv(Batch& batch, uint32 paramOffset) {
if (_pipeline._program == 0) {
// We should call updatePipeline() to bind the program but we are not doing that
// because these uniform setters are deprecated and we don;t want to create side effect
return;
}
glUniformMatrix4fv(
batch._params[paramOffset + 3]._int,
batch._params[paramOffset + 2]._uint,

View file

@ -26,9 +26,9 @@ public:
explicit GLBackend(bool syncCache);
GLBackend();
~GLBackend();
virtual ~GLBackend();
void render(Batch& batch);
virtual void render(Batch& batch);
// Render Batch create a local Context and execute the batch with it
// WARNING:

View file

@ -107,7 +107,7 @@ void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) {
}
void DeferredLightingEffect::bindSimpleProgram(gpu::Batch& batch, bool textured) {
DependencyManager::get<TextureCache>()->setPrimaryDrawBuffers(batch, true, true, true);
// DependencyManager::get<TextureCache>()->setPrimaryDrawBuffers(batch, true, true, true);
if (textured) {
batch.setPipeline(_simpleProgramTextured);
@ -117,7 +117,7 @@ void DeferredLightingEffect::bindSimpleProgram(gpu::Batch& batch, bool textured)
}
void DeferredLightingEffect::releaseSimpleProgram(gpu::Batch& batch) {
DependencyManager::get<TextureCache>()->setPrimaryDrawBuffers(batch, true, false, false);
// DependencyManager::get<TextureCache>()->setPrimaryDrawBuffers(batch, true, false, false);
}
void DeferredLightingEffect::renderSolidSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec4& color) {

View file

@ -765,6 +765,109 @@ void Model::renderSetup(RenderArgs* args) {
}
}
class TransparentMeshPart {
public:
TransparentMeshPart(Model* model, int meshIndex, int partIndex) : model(model), meshIndex(meshIndex), partIndex(partIndex) { }
typedef render::Payload<TransparentMeshPart> Payload;
typedef Payload::DataPointer Pointer;
Model* model;
int meshIndex;
int partIndex;
};
namespace render {
template <> const ItemKey payloadGetKey(const TransparentMeshPart::Pointer& payload) {
return ItemKey::Builder::transparentShape();
}
template <> const Item::Bound payloadGetBound(const TransparentMeshPart::Pointer& payload) {
if (payload) {
//return payload->model->getPartBounds(payload->meshIndex, payload->partIndex);
}
return render::Item::Bound();
}
template <> void payloadRender(const TransparentMeshPart::Pointer& payload, RenderArgs* args) {
if (args) {
args->_elementsTouched++;
//qDebug() << "would be TransparentMeshPart: " << payload->meshIndex << "," << payload->partIndex;
return payload->model->renderPart(args, payload->meshIndex, payload->partIndex, true);
}
}
}
class OpaqueMeshPart {
public:
OpaqueMeshPart(Model* model, int meshIndex, int partIndex) : model(model), meshIndex(meshIndex), partIndex(partIndex) { }
typedef render::Payload<OpaqueMeshPart> Payload;
typedef Payload::DataPointer Pointer;
Model* model;
int meshIndex;
int partIndex;
};
namespace render {
template <> const ItemKey payloadGetKey(const OpaqueMeshPart::Pointer& payload) {
return ItemKey::Builder::opaqueShape();
}
template <> const Item::Bound payloadGetBound(const OpaqueMeshPart::Pointer& payload) {
if (payload) {
//return payload->model->getPartBounds(payload->meshIndex, payload->partIndex);
}
return render::Item::Bound();
}
template <> void payloadRender(const OpaqueMeshPart::Pointer& payload, RenderArgs* args) {
if (args) {
args->_elementsTouched++;
return payload->model->renderPart(args, payload->meshIndex, payload->partIndex, false);
}
}
}
bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
bool somethingAdded = false;
// allow the attachments to add to scene
foreach (Model* attachment, _attachments) {
bool attachementSomethingAdded = attachment->addToScene(scene, pendingChanges);
somethingAdded = somethingAdded || attachementSomethingAdded;
}
foreach (auto renderItem, _transparentRenderItems) {
auto item = scene->allocateID();
auto renderData = TransparentMeshPart::Pointer(renderItem);
auto renderPayload = render::PayloadPointer(new TransparentMeshPart::Payload(renderData));
pendingChanges.resetItem(item, renderPayload);
_renderItems << item;
somethingAdded = true;
}
foreach (auto renderItem, _opaqueRenderItems) {
auto item = scene->allocateID();
auto renderData = OpaqueMeshPart::Pointer(renderItem);
auto renderPayload = render::PayloadPointer(new OpaqueMeshPart::Payload(renderData));
pendingChanges.resetItem(item, renderPayload);
_renderItems << item;
somethingAdded = true;
}
return somethingAdded;
}
void Model::removeFromScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
// allow the attachments to remove to scene
foreach (Model* attachment, _attachments) {
attachment->removeFromScene(scene, pendingChanges);
}
foreach (auto item, _renderItems) {
pendingChanges.removeItem(item);
}
_renderItems.clear();
}
bool Model::render(RenderArgs* renderArgs, float alpha) {
PROFILE_RANGE(__FUNCTION__);
@ -1847,9 +1950,12 @@ void Model::setupBatchTransform(gpu::Batch& batch, RenderArgs* args) {
}
void Model::endScene(RenderArgs* args) {
// Now that we migrated everything to the new RENDER/SCENE no more work to do!
return;
PROFILE_RANGE(__FUNCTION__);
#if (GPU_TRANSFORM_PROFILE == GPU_LEGACY)
// with legacy transform profile, we still to protect that transform stack...
glPushMatrix();
@ -1862,6 +1968,7 @@ void Model::endScene(RenderArgs* args) {
renderSide = args->_renderSide;
}
gpu::GLBackend backend;
backend.syncCache(); // force sync with gl state here
@ -2068,6 +2175,212 @@ bool Model::renderInScene(float alpha, RenderArgs* args) {
return true;
}
AABox Model::getPartBounds(int meshIndex, int partIndex) {
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const FBXMesh& mesh = geometry.meshes.at(meshIndex);
AABox partBox = mesh._mesh.evalPartBound(partIndex);
// FIX ME!
// 1) needs to translate to world space, these values are in model space
// 2) mesh._mesh.parts doesn't always have the correct values in it... so we
// need to just use mesh.parts or find/fix whatever is causing mesh._mesh
// to not contain data
return partBox;
}
void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool translucent) {
renderSetup(args);
/*
if (translucent) {
renderCore(args, 1.0f);
return;
}
*/
auto textureCache = DependencyManager::get<TextureCache>();
gpu::Batch& batch = *(args->_batch);
auto mode = args->_renderMode;
// Capture the view matrix once for the rendering of this model
if (_transforms.empty()) {
_transforms.push_back(Transform());
}
_transforms[0] = _viewState->getViewTransform();
// apply entity translation offset to the viewTransform in one go (it's a preTranslate because viewTransform goes from world to eye space)
_transforms[0].preTranslate(-_translation);
batch.setViewTransform(_transforms[0]);
const float OPAQUE_ALPHA_THRESHOLD = 0.5f;
const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f;
auto alphaThreshold = translucent ? TRANSPARENT_ALPHA_THRESHOLD : OPAQUE_ALPHA_THRESHOLD; // FIX ME
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const QVector<NetworkMesh>& networkMeshes = _geometry->getMeshes();
const NetworkMesh& networkMesh = networkMeshes.at(meshIndex);
const FBXMesh& mesh = geometry.meshes.at(meshIndex);
const MeshState& state = _meshStates.at(meshIndex);
bool translucentMesh = translucent; // networkMesh.getTranslucentPartCount(mesh) == networkMesh.parts.size();
bool hasTangents = !mesh.tangents.isEmpty();
bool hasSpecular = mesh.hasSpecularTexture();
bool hasLightmap = mesh.hasEmissiveTexture();
bool isSkinned = state.clusterMatrices.size() > 1;
bool wireframe = isWireframe();
if (wireframe) {
translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false;
}
Locations* locations = nullptr;
pickPrograms(batch, mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe,
args, locations);
int meshPartsRendered = 0;
updateVisibleJointStates();
// if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown
// to false to rebuild out mesh groups.
if (meshIndex < 0 || meshIndex >= networkMeshes.size() || meshIndex > geometry.meshes.size()) {
_meshGroupsKnown = false; // regenerate these lists next time around.
return; // FIXME!
}
batch.setIndexBuffer(gpu::UINT32, (networkMesh._indexBuffer), 0);
int vertexCount = mesh.vertices.size();
if (vertexCount == 0) {
// sanity check
return; // FIXME!
}
if (state.clusterMatrices.size() > 1) {
GLBATCH(glUniformMatrix4fv)(locations->clusterMatrices, state.clusterMatrices.size(), false,
(const float*)state.clusterMatrices.constData());
batch.setModelTransform(Transform());
} else {
batch.setModelTransform(Transform(state.clusterMatrices[0]));
}
if (mesh.blendshapes.isEmpty()) {
batch.setInputFormat(networkMesh._vertexFormat);
batch.setInputStream(0, *networkMesh._vertexStream);
} else {
batch.setInputFormat(networkMesh._vertexFormat);
batch.setInputBuffer(0, _blendedVertexBuffers[meshIndex], 0, sizeof(glm::vec3));
batch.setInputBuffer(1, _blendedVertexBuffers[meshIndex], vertexCount * sizeof(glm::vec3), sizeof(glm::vec3));
batch.setInputStream(2, *networkMesh._vertexStream);
}
if (mesh.colors.isEmpty()) {
GLBATCH(glColor4f)(1.0f, 1.0f, 1.0f, 1.0f);
}
qint64 offset = 0;
const NetworkMeshPart& networkPart = networkMesh.parts.at(partIndex);
const FBXMeshPart& part = mesh.parts.at(partIndex);
model::MaterialPointer material = part._material;
if (material == nullptr) {
qCDebug(renderutils) << "WARNING: material == nullptr!!!";
}
if (material != nullptr) {
// apply material properties
if (mode != RenderArgs::SHADOW_RENDER_MODE) {
#ifdef WANT_DEBUG
qCDebug(renderutils) << "Material Changed ---------------------------------------------";
qCDebug(renderutils) << "part INDEX:" << partIndex;
qCDebug(renderutils) << "NEW part.materialID:" << part.materialID;
#endif //def WANT_DEBUG
if (locations->materialBufferUnit >= 0) {
batch.setUniformBuffer(locations->materialBufferUnit, material->getSchemaBuffer());
}
Texture* diffuseMap = networkPart.diffuseTexture.data();
if (mesh.isEye && diffuseMap) {
diffuseMap = (_dilatedTextures[meshIndex][partIndex] =
static_cast<DilatableNetworkTexture*>(diffuseMap)->getDilatedTexture(_pupilDilation)).data();
}
static bool showDiffuse = true;
if (showDiffuse && diffuseMap) {
batch.setUniformTexture(0, diffuseMap->getGPUTexture());
} else {
batch.setUniformTexture(0, textureCache->getWhiteTexture());
}
if (locations->texcoordMatrices >= 0) {
glm::mat4 texcoordTransform[2];
if (!part.diffuseTexture.transform.isIdentity()) {
part.diffuseTexture.transform.getMatrix(texcoordTransform[0]);
}
if (!part.emissiveTexture.transform.isIdentity()) {
part.emissiveTexture.transform.getMatrix(texcoordTransform[1]);
}
GLBATCH(glUniformMatrix4fv)(locations->texcoordMatrices, 2, false, (const float*) &texcoordTransform);
}
if (!mesh.tangents.isEmpty()) {
Texture* normalMap = networkPart.normalTexture.data();
batch.setUniformTexture(1, !normalMap ?
textureCache->getBlueTexture() : normalMap->getGPUTexture());
}
if (locations->specularTextureUnit >= 0) {
Texture* specularMap = networkPart.specularTexture.data();
batch.setUniformTexture(locations->specularTextureUnit, !specularMap ?
textureCache->getWhiteTexture() : specularMap->getGPUTexture());
}
if (args) {
args->_materialSwitches++;
}
// HACK: For unknown reason (yet!) this code that should be assigned only if the material changes need to be called for every
// drawcall with an emissive, so let's do it for now.
if (locations->emissiveTextureUnit >= 0) {
// assert(locations->emissiveParams >= 0); // we should have the emissiveParams defined in the shader
float emissiveOffset = part.emissiveParams.x;
float emissiveScale = part.emissiveParams.y;
GLBATCH(glUniform2f)(locations->emissiveParams, emissiveOffset, emissiveScale);
Texture* emissiveMap = networkPart.emissiveTexture.data();
batch.setUniformTexture(locations->emissiveTextureUnit, !emissiveMap ?
textureCache->getWhiteTexture() : emissiveMap->getGPUTexture());
}
}
}
meshPartsRendered++;
if (part.quadIndices.size() > 0) {
batch.drawIndexed(gpu::QUADS, part.quadIndices.size(), offset);
offset += part.quadIndices.size() * sizeof(int);
}
if (part.triangleIndices.size() > 0) {
batch.drawIndexed(gpu::TRIANGLES, part.triangleIndices.size(), offset);
offset += part.triangleIndices.size() * sizeof(int);
}
if (args) {
const int INDICES_PER_TRIANGLE = 3;
const int INDICES_PER_QUAD = 4;
args->_trianglesRendered += part.triangleIndices.size() / INDICES_PER_TRIANGLE;
args->_quadsRendered += part.quadIndices.size() / INDICES_PER_QUAD;
}
}
void Model::segregateMeshGroups() {
_renderBuckets.clear();
@ -2086,6 +2399,7 @@ void Model::segregateMeshGroups() {
const NetworkMesh& networkMesh = networkMeshes.at(i);
const FBXMesh& mesh = geometry.meshes.at(i);
const MeshState& state = _meshStates.at(i);
bool translucentMesh = networkMesh.getTranslucentPartCount(mesh) == networkMesh.parts.size();
bool hasTangents = !mesh.tangents.isEmpty();
@ -2097,6 +2411,18 @@ void Model::segregateMeshGroups() {
if (wireframe) {
translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false;
}
// Debug...
int totalParts = mesh.parts.size();
for (int partIndex = 0; partIndex < totalParts; partIndex++) {
// this is a good place to create our renderPayloads
if (translucentMesh) {
_transparentRenderItems << std::shared_ptr<TransparentMeshPart>(new TransparentMeshPart(this, i, partIndex));
} else {
_opaqueRenderItems << std::shared_ptr<OpaqueMeshPart>(new OpaqueMeshPart(this, i, partIndex));
}
}
QString materialID;

View file

@ -45,6 +45,22 @@ class Shape;
#include "RenderArgs.h"
class ViewFrustum;
namespace render {
class Scene;
class PendingChanges;
typedef unsigned int ItemID;
}
class OpaqueMeshPart;
class TransparentMeshPart;
inline uint qHash(const std::shared_ptr<TransparentMeshPart>& a, uint seed) {
return qHash(a.get(), seed);
}
inline uint qHash(const std::shared_ptr<OpaqueMeshPart>& a, uint seed) {
return qHash(a.get(), seed);
}
/// A generic 3D model displaying geometry loaded from a URL.
class Model : public QObject, public PhysicsEntity {
@ -99,12 +115,18 @@ public:
virtual void simulate(float deltaTime, bool fullUpdate = true);
bool render(RenderArgs* renderArgs, float alpha = 1.0f);
void renderSetup(RenderArgs* args);
// Scene rendering support
static void startScene(RenderArgs::RenderSide renderSide);
bool renderInScene(float alpha = 1.0f, RenderArgs* args = NULL);
static void endScene(RenderArgs* args);
// new Scene/Engine rendering support
bool readyToAddToScene(RenderArgs* renderArgs = nullptr) { return isRenderable() && isActive() && isLoadedWithTextures(); }
bool addToScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
void removeFromScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
/// Sets the URL of the model to render.
/// \param fallback the URL of a fallback model to render if the requested model fails to load
/// \param retainCurrent if true, keep rendering the current model until the new one is loaded
@ -219,6 +241,9 @@ public:
BoxFace& face, QString& extraInfo, bool pickAgainstTriangles = false);
bool convexHullContains(glm::vec3 point);
AABox getPartBounds(int meshIndex, int partIndex);
void renderPart(RenderArgs* args, int meshIndex, int partIndex, bool translucent);
protected:
QSharedPointer<NetworkGeometry> _geometry;
@ -370,7 +395,6 @@ private:
static void endSceneSplitPass(RenderArgs::RenderMode mode = RenderArgs::DEFAULT_RENDER_MODE, RenderArgs* args = NULL);
// helper functions used by render() or renderInScene()
void renderSetup(RenderArgs* args);
bool renderCore(RenderArgs* args, float alpha);
int renderMeshes(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold,
bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args = NULL,
@ -511,6 +535,11 @@ private:
RenderBucketMap _renderBuckets;
bool _renderCollisionHull;
QSet<std::shared_ptr<TransparentMeshPart>> _transparentRenderItems;
QSet<std::shared_ptr<OpaqueMeshPart>> _opaqueRenderItems;
QSet<render::ItemID> _renderItems;
};
Q_DECLARE_METATYPE(QPointer<Model>)

View file

@ -55,73 +55,6 @@ void DrawSceneTask::run(const SceneContextPointer& sceneContext, const RenderCon
Job::~Job() {
}
template <> void render::jobRun(const DrawOpaque& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
// render opaques
auto& scene = sceneContext->_scene;
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape());
ItemIDs inItems;
inItems.reserve(items.size());
for (auto id : items) {
inItems.push_back(id);
}
ItemIDs culledItems;
cullItems(sceneContext, renderContext, inItems, culledItems);
ItemIDs sortedItems;
depthSortItems(sceneContext, renderContext, true, culledItems, sortedItems); // Sort Front to back opaque items!
renderItems(sceneContext, renderContext, sortedItems);
}
template <> void render::jobRun(const DrawTransparent& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
// render transparents
auto& scene = sceneContext->_scene;
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::transparentShape());
ItemIDs inItems;
inItems.reserve(items.size());
for (auto id : items) {
inItems.push_back(id);
}
ItemIDs culledItems;
cullItems(sceneContext, renderContext, inItems, culledItems);
ItemIDs sortedItems;
depthSortItems(sceneContext, renderContext, false, culledItems, sortedItems); // Sort Back to front transparent items!
renderItems(sceneContext, renderContext, sortedItems);
}
template <> void render::jobRun(const DrawLight& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
// render lights
auto& scene = sceneContext->_scene;
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::light());
ItemIDs inItems;
inItems.reserve(items.size());
for (auto id : items) {
inItems.push_back(id);
}
ItemIDs culledItems;
cullItems(sceneContext, renderContext, inItems, culledItems);
renderItems(sceneContext, renderContext, culledItems);
}
/*
bool LODManager::shouldRenderMesh(float largestDimension, float distanceToCamera) {
const float octreeToMeshRatio = 4.0f; // must be this many times closer to a mesh than a voxel to see it.
@ -263,16 +196,155 @@ void render::depthSortItems(const SceneContextPointer& sceneContext, const Rende
void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDs& inItems) {
auto& scene = sceneContext->_scene;
RenderArgs* args = renderContext->args;
gpu::Batch theBatch;
args->_batch = &theBatch;
// render
for (auto id : inItems) {
auto item = scene->getItem(id);
item.render(args);
}
}
args->_context->enqueueBatch((*args->_batch));
void addClearStateCommands(gpu::Batch& batch) {
batch._glDepthMask(true);
batch._glDepthFunc(GL_LESS);
batch._glDisable(GL_CULL_FACE);
batch._glActiveTexture(GL_TEXTURE0 + 1);
batch._glBindTexture(GL_TEXTURE_2D, 0);
batch._glActiveTexture(GL_TEXTURE0 + 2);
batch._glBindTexture(GL_TEXTURE_2D, 0);
batch._glActiveTexture(GL_TEXTURE0 + 3);
batch._glBindTexture(GL_TEXTURE_2D, 0);
batch._glActiveTexture(GL_TEXTURE0);
batch._glBindTexture(GL_TEXTURE_2D, 0);
// deactivate vertex arrays after drawing
batch._glDisableClientState(GL_NORMAL_ARRAY);
batch._glDisableClientState(GL_VERTEX_ARRAY);
batch._glDisableClientState(GL_TEXTURE_COORD_ARRAY);
batch._glDisableClientState(GL_COLOR_ARRAY);
batch._glDisableVertexAttribArray(gpu::Stream::TANGENT);
batch._glDisableVertexAttribArray(gpu::Stream::SKIN_CLUSTER_INDEX);
batch._glDisableVertexAttribArray(gpu::Stream::SKIN_CLUSTER_WEIGHT);
// bind with 0 to switch back to normal operation
batch._glBindBuffer(GL_ARRAY_BUFFER, 0);
batch._glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
batch._glBindTexture(GL_TEXTURE_2D, 0);
// Back to no program
batch._glUseProgram(0);
}
template <> void render::jobRun(const DrawOpaque& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
// render opaques
auto& scene = sceneContext->_scene;
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape());
ItemIDs inItems;
inItems.reserve(items.size());
for (auto id : items) {
inItems.push_back(id);
}
ItemIDs culledItems;
cullItems(sceneContext, renderContext, inItems, culledItems);
ItemIDs sortedItems;
depthSortItems(sceneContext, renderContext, true, culledItems, sortedItems); // Sort Front to back opaque items!
RenderArgs* args = renderContext->args;
gpu::Batch theBatch;
args->_batch = &theBatch;
glm::mat4 proj;
args->_viewFrustum->evalProjectionMatrix(proj);
theBatch.setProjectionTransform(proj);
renderContext->args->_renderMode = RenderArgs::NORMAL_RENDER_MODE;
{
GLenum buffers[3];
int bufferCount = 0;
buffers[bufferCount++] = GL_COLOR_ATTACHMENT0;
buffers[bufferCount++] = GL_COLOR_ATTACHMENT1;
buffers[bufferCount++] = GL_COLOR_ATTACHMENT2;
theBatch._glDrawBuffers(bufferCount, buffers);
}
renderItems(sceneContext, renderContext, sortedItems);
addClearStateCommands((*args->_batch));
args->_context->render((*args->_batch));
args->_batch = nullptr;
}
}
template <> void render::jobRun(const DrawTransparent& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
// render transparents
auto& scene = sceneContext->_scene;
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::transparentShape());
ItemIDs inItems;
inItems.reserve(items.size());
for (auto id : items) {
inItems.push_back(id);
}
ItemIDs culledItems;
cullItems(sceneContext, renderContext, inItems, culledItems);
ItemIDs sortedItems;
depthSortItems(sceneContext, renderContext, false, culledItems, sortedItems); // Sort Back to front transparent items!
RenderArgs* args = renderContext->args;
gpu::Batch theBatch;
args->_batch = &theBatch;
renderContext->args->_renderMode = RenderArgs::NORMAL_RENDER_MODE;
{
GLenum buffers[3];
int bufferCount = 0;
buffers[bufferCount++] = GL_COLOR_ATTACHMENT0;
theBatch._glDrawBuffers(bufferCount, buffers);
}
renderItems(sceneContext, renderContext, sortedItems);
addClearStateCommands((*args->_batch));
args->_context->render((*args->_batch));
args->_batch = nullptr;
}
template <> void render::jobRun(const DrawLight& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
assert(renderContext->args);
assert(renderContext->args->_viewFrustum);
// render lights
auto& scene = sceneContext->_scene;
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::light());
ItemIDs inItems;
inItems.reserve(items.size());
for (auto id : items) {
inItems.push_back(id);
}
ItemIDs culledItems;
cullItems(sceneContext, renderContext, inItems, culledItems);
RenderArgs* args = renderContext->args;
gpu::Batch theBatch;
args->_batch = &theBatch;
renderItems(sceneContext, renderContext, culledItems);
args->_context->render((*args->_batch));
args->_batch = nullptr;
}