From eb19b934332bd99abecfb561d8fe22476a91fa5b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 28 May 2015 17:19:30 -0700 Subject: [PATCH] render models in scene checkpoint --- .../src/EntityTreeRenderer.cpp | 41 ++- .../src/EntityTreeRenderer.h | 4 + .../src/RenderableModelEntityItem.cpp | 34 ++- .../src/RenderableModelEntityItem.h | 7 +- libraries/entities/src/EntityItem.cpp | 2 +- libraries/entities/src/EntityItem.h | 1 + libraries/render-utils/src/Model.cpp | 269 ++++++++++++++++++ libraries/render-utils/src/Model.h | 26 ++ 8 files changed, 373 insertions(+), 11 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 130f99cf4e..5f20e55298 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -489,6 +489,10 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr_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(); @@ -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(_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 addedToScene; + foreach (auto entity, _pendingAddToScene) { + if (entity->readyToAddToScene(renderArgs)) { + addEntityToScene(entity); + addedToScene << entity; + } + } + foreach (auto addedEntity, addedToScene) { + _pendingAddToScene.remove(addedEntity); } } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 234e590b7d..bb9bff5d10 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -124,6 +124,10 @@ protected: virtual Octree* createTree() { return new EntityTree(true); } private: + void checkPendingAddToScene(RenderArgs* renderArgs); + void addEntityToScene(EntityItemPointer entity); + QSet _pendingAddToScene; + void applyZonePropertiesToScene(std::shared_ptr zone); void renderElementProxy(EntityTreeElement* entityTreeElement, RenderArgs* args); void checkAndCallPreload(const EntityItemID& entityID); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 5b56b79d5e..62a9eb8887 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -108,7 +108,35 @@ void RenderableModelEntityItem::remapTextures() { _currentTextures = _textures; } +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(renderArgs->_renderer); + qDebug() << "RenderableModelEntityItem::readyToAddToScene().... renderer:" << renderer; + getModel(renderer); + } + bool ready = (bool)_model; + qDebug() << "RenderableModelEntityItem::readyToAddToScene().... id:" << getEntityItemID() + << "ready:" << ready << "renderArgs:" << renderArgs; + return ready; +} + +bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_ptr scene, + render::PendingChanges& pendingChanges) { + qDebug() << "RenderableModelEntityItem::addToScene().... id:" << getEntityItemID(); + return false; +} + +void RenderableModelEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr scene, + render::PendingChanges& pendingChanges) { + qDebug() << "RenderableModelEntityItem::removeFromScene().... id:" << getEntityItemID(); + + +} + void RenderableModelEntityItem::render(RenderArgs* args) { + qDebug() << "RenderableModelEntityItem::render().... id:" << getEntityItemID(); PerformanceTimer perfTimer("RMEIrender"); assert(getType() == EntityTypes::Model); @@ -199,6 +227,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 +238,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; } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 335f3a4d25..55044e5d4d 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -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 scene, render::PendingChanges& pendingChanges); + virtual void removeFromScene(EntityItemPointer self, std::shared_ptr 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, diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 6073f2150b..b911dd5964 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -973,7 +973,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; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 5cbd29cfa8..93d2a7f035 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -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 scene, render::PendingChanges& pendingChanges) { return false; } // by default entity items don't add to scene virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index e304e6ee05..d6c8e8e588 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -765,6 +765,29 @@ void Model::renderSetup(RenderArgs* args) { } } +bool Model::addToScene(std::shared_ptr 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; + } + + // TODO --- need to do something here + + return somethingAdded; +} + +void Model::removeFromScene(std::shared_ptr scene, render::PendingChanges& pendingChanges) { + // allow the attachments to remove to scene + foreach (Model* attachment, _attachments) { + attachment->removeFromScene(scene, pendingChanges); + } + + // TODO --- need to do something here + +} + bool Model::render(RenderArgs* renderArgs, float alpha) { PROFILE_RANGE(__FUNCTION__); @@ -2068,7 +2091,235 @@ bool Model::renderInScene(float alpha, RenderArgs* args) { return true; } +class TransparentMeshPart { +public: + TransparentMeshPart(Model* model, int meshIndex, int partIndex) : model(model), meshIndex(meshIndex), partIndex(partIndex) { } + typedef render::Payload 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++; + 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 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); + } + } +} + +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); + return partBox; +} + +void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool translucent) { + auto textureCache = DependencyManager::get(); + + const float DEFAULT_ALPHA_THRESHOLD = 0.5f; + + int i = meshIndex; + int j = partIndex; + gpu::Batch& batch = *(args->_batch); + auto mode = args->_renderMode; + auto alphaThreshold = DEFAULT_ALPHA_THRESHOLD; // FIX ME + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const QVector& networkMeshes = _geometry->getMeshes(); + + const NetworkMesh& networkMesh = networkMeshes.at(meshIndex); + const FBXMesh& mesh = geometry.meshes.at(meshIndex); + const MeshState& state = _meshStates.at(meshIndex); + + int vertexCount = mesh.vertices.size(); // NOTE: This seems wrong, shouldn't it be the part's vertex count? + + + bool translucentMesh = 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); + + 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[i], 0, sizeof(glm::vec3)); + batch.setInputBuffer(1, _blendedVertexBuffers[i], 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(j); + const FBXMeshPart& part = mesh.parts.at(j); + model::MaterialPointer material = part._material; + if ((networkPart.isTranslucent() || part.opacity != 1.0f) != translucent) { + offset += (part.quadIndices.size() + part.triangleIndices.size()) * sizeof(int); + //continue; + // FIX ME!!! + } + + // apply material properties + if (mode == RenderArgs::SHADOW_RENDER_MODE) { + /// GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0); + } else { + const bool wantDebug = false; + if (wantDebug) { + qCDebug(renderutils) << "Material Changed ---------------------------------------------"; + qCDebug(renderutils) << "part INDEX:" << j; + qCDebug(renderutils) << "NEW part.materialID:" << part.materialID; + } + + if (locations->materialBufferUnit >= 0) { + batch.setUniformBuffer(locations->materialBufferUnit, material->getSchemaBuffer()); + } + + Texture* diffuseMap = networkPart.diffuseTexture.data(); + if (mesh.isEye && diffuseMap) { + diffuseMap = (_dilatedTextures[i][j] = + static_cast(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 unkwon 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()); + } + + } + + 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() { + + //qDebug() << "Model::segregateMeshGroups() ------------------------------------------------"; + _renderBuckets.clear(); const FBXGeometry& geometry = _geometry->getFBXGeometry(); @@ -2086,6 +2337,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 +2349,23 @@ void Model::segregateMeshGroups() { if (wireframe) { translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false; } + + // Debug... + //qDebug() << "Mesh parts..." << mesh._mesh.getNumParts(); + int totalParts = mesh._mesh.getNumParts(); + for (int partIndex = 0; partIndex < totalParts; partIndex++) { + AABox boxPart = mesh._mesh.evalPartBound(partIndex); + + // this is a good place to create our renderPayloads + if (translucentMesh) { + //qDebug() << "Transparent Mesh parts[" << partIndex << "].box=" << boxPart; + _transparentRenderItems << std::shared_ptr(new TransparentMeshPart(this, i, partIndex)); + } else { + //qDebug() << "Opaque Mesh parts[" << partIndex << "].box=" << boxPart; + _opaqueRenderItems << std::shared_ptr(new OpaqueMeshPart(this, i, partIndex)); + } + } + QString materialID; diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 66a1a87d1f..a8248172be 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -45,6 +45,21 @@ class Shape; #include "RenderArgs.h" class ViewFrustum; +namespace render { + class Scene; + class PendingChanges; +} +class OpaqueMeshPart; +class TransparentMeshPart; + +inline uint qHash(const std::shared_ptr& a, uint seed) { + return qHash(a.get(), seed); +} +inline uint qHash(const std::shared_ptr& a, uint seed) { + return qHash(a.get(), seed); +} + + /// A generic 3D model displaying geometry loaded from a URL. class Model : public QObject, public PhysicsEntity { @@ -105,6 +120,10 @@ public: bool renderInScene(float alpha = 1.0f, RenderArgs* args = NULL); static void endScene(RenderArgs* args); + // new Scene/Engine rendering support + bool addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); + void removeFromScene(std::shared_ptr 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 +238,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 _geometry; @@ -511,6 +533,10 @@ private: RenderBucketMap _renderBuckets; bool _renderCollisionHull; + + + QSet> _transparentRenderItems; + QSet> _opaqueRenderItems; }; Q_DECLARE_METATYPE(QPointer)