diff --git a/interface/src/devices/Joystick.cpp b/interface/src/devices/Joystick.cpp index e110027e1a..5446ca1330 100644 --- a/interface/src/devices/Joystick.cpp +++ b/interface/src/devices/Joystick.cpp @@ -15,9 +15,9 @@ #include "Joystick.h" -const float MAX_AXIS = 32768.0f; #ifdef HAVE_SDL2 +const float MAX_AXIS = 32768.0f; Joystick::Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController) : _instanceId(instanceId), diff --git a/interface/src/entities/RenderableModelEntityItem.cpp b/interface/src/entities/RenderableModelEntityItem.cpp index e2a0933fab..ec4c5b4008 100644 --- a/interface/src/entities/RenderableModelEntityItem.cpp +++ b/interface/src/entities/RenderableModelEntityItem.cpp @@ -59,7 +59,7 @@ int RenderableModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned c void RenderableModelEntityItem::render(RenderArgs* args) { - PerformanceTimer perfTimer("RenderableModelEntityItem::render"); + PerformanceTimer perfTimer("RMEIrender"); assert(getType() == EntityTypes::Model); bool drawAsModel = hasModel(); @@ -119,7 +119,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) { // TODO: this is the majority of model render time. And rendering of a cube model vs the basic Box render // is significantly more expensive. Is there a way to call this that doesn't cost us as much? PerformanceTimer perfTimer("model->render"); - _model->render(alpha, modelRenderMode); + _model->render(alpha, modelRenderMode, args); } else { // if we couldn't get a model, then just draw a cube glColor3ub(getColor()[RED_INDEX],getColor()[GREEN_INDEX],getColor()[BLUE_INDEX]); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 55eefb0bde..3a6d88e307 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -44,7 +45,8 @@ Model::Model(QObject* parent) : _pupilDilation(0.0f), _url("http://invalid.com"), _blendNumber(0), - _appliedBlendNumber(0) { + _appliedBlendNumber(0), + _calculatedMeshBoxesValid(false) { // we may have been created in the network thread, but we live in the main thread moveToThread(Application::getInstance()->thread()); @@ -384,7 +386,7 @@ void Model::setJointStates(QVector states) { _boundingRadius = radius; } -bool Model::render(float alpha, RenderMode mode) { +bool Model::render(float alpha, RenderMode mode, RenderArgs* args) { // render the attachments foreach (Model* attachment, _attachments) { attachment->render(alpha, mode); @@ -392,6 +394,23 @@ bool Model::render(float alpha, RenderMode mode) { if (_meshStates.isEmpty()) { return false; } + + // if we don't have valid mesh boxes, calculate them now, this only matters in cases + // where our caller has passed RenderArgs which will include a view frustum we can cull + // against. We cache the results of these calculations so long as the model hasn't been + // simulated and the mesh hasn't changed. + if (args && !_calculatedMeshBoxesValid) { + PerformanceTimer perfTimer("calculatedMeshBoxes"); + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + int numberOfMeshes = geometry.meshes.size(); + _calculatedMeshBoxes.resize(numberOfMeshes); + for (int i = 0; i < numberOfMeshes; i++) { + const FBXMesh& mesh = geometry.meshes.at(i); + Extents scaledMeshExtents = calculateScaledOffsetExtents(mesh.meshExtents); + _calculatedMeshBoxes[i] = AABox(scaledMeshExtents); + } + _calculatedMeshBoxesValid = true; + } // set up dilated textures on first render after load/simulate const FBXGeometry& geometry = _geometry->getFBXGeometry(); @@ -431,11 +450,12 @@ bool Model::render(float alpha, RenderMode mode) { mode == DEFAULT_RENDER_MODE || mode == NORMAL_RENDER_MODE, mode == DEFAULT_RENDER_MODE); - renderMeshes(mode, false); + const float DEFAULT_ALPHA_THRESHOLD = 0.5f; + renderMeshes(mode, false, DEFAULT_ALPHA_THRESHOLD, args); // render translucent meshes afterwards Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(false, true, true); - renderMeshes(mode, true, 0.75f); + renderMeshes(mode, true, 0.75f, args); glDisable(GL_ALPHA_TEST); glEnable(GL_BLEND); @@ -445,7 +465,7 @@ bool Model::render(float alpha, RenderMode mode) { Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true); if (mode == DEFAULT_RENDER_MODE || mode == DIFFUSE_RENDER_MODE) { - renderMeshes(mode, true, 0.0f); + renderMeshes(mode, true, 0.0f, args); } glDepthMask(true); @@ -511,6 +531,22 @@ Extents Model::getUnscaledMeshExtents() const { return scaledExtents; } +Extents Model::calculateScaledOffsetExtents(const Extents& extents) const { + // we need to include any fst scaling, translation, and rotation, which is captured in the offset matrix + glm::vec3 minimum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f)); + glm::vec3 maximum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f)); + + Extents scaledOffsetExtents = { ((minimum + _offset) * _scale), + ((maximum + _offset) * _scale) }; + + Extents rotatedExtents = scaledOffsetExtents.getRotated(_rotation); + + Extents translatedExtents = { rotatedExtents.minimum + _translation, + rotatedExtents.maximum + _translation }; + return translatedExtents; +} + + bool Model::getJointState(int index, glm::quat& rotation) const { if (index == -1 || index >= _jointStates.size()) { return false; @@ -790,6 +826,8 @@ void Model::simulate(float deltaTime, bool fullUpdate) { || (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint); if (isActive() && fullUpdate) { + _calculatedMeshBoxesValid = false; // if we have to simulate, we need to assume our mesh boxes are all invalid + // check for scale to fit if (_scaleToFit && !_scaledToFit) { scaleToFit(); @@ -1200,10 +1238,12 @@ void Model::deleteGeometry() { _blendedBlendshapeCoefficients.clear(); } -void Model::renderMeshes(RenderMode mode, bool translucent, float alphaThreshold) { +void Model::renderMeshes(RenderMode mode, bool translucent, float alphaThreshold, RenderArgs* args) { updateVisibleJointStates(); const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& networkMeshes = _geometry->getMeshes(); + + bool cullMeshParts = args && !Menu::getInstance()->isOptionChecked(MenuOption::DontCullMeshParts); for (int i = 0; i < networkMeshes.size(); i++) { // exit early if the translucency doesn't match what we're drawing @@ -1221,6 +1261,23 @@ void Model::renderMeshes(RenderMode mode, bool translucent, float alphaThreshold continue; } + // if we got here, then check to see if this mesh is in view + if (args) { + bool shouldRender = true; + args->_meshesConsidered++; + + if (cullMeshParts && args->_viewFrustum) { + shouldRender = args->_viewFrustum->boxInFrustum(_calculatedMeshBoxes.at(i)) != ViewFrustum::OUTSIDE; + } + + if (shouldRender) { + args->_meshesRendered++; + } else { + args->_meshesOutOfView++; + continue; // skip this mesh + } + } + const_cast(networkMesh.vertexBuffer).bind(); ProgramObject* program = &_program; @@ -1372,6 +1429,13 @@ void Model::renderMeshes(RenderMode mode, bool translucent, float alphaThreshold glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertexCount - 1, part.triangleIndices.size(), GL_UNSIGNED_INT, (void*)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; + } } if (!mesh.colors.isEmpty()) { diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index b82db73624..2e11d2b75e 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -16,9 +16,9 @@ #include #include -#include - +#include #include +#include #include "GeometryCache.h" #include "InterfaceConfig.h" @@ -30,6 +30,7 @@ class QScriptEngine; class AnimationHandle; class Shape; +class RenderArgs; typedef QSharedPointer AnimationHandlePointer; typedef QWeakPointer WeakAnimationHandlePointer; @@ -84,7 +85,7 @@ public: enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE }; - bool render(float alpha = 1.0f, RenderMode mode = DEFAULT_RENDER_MODE); + bool render(float alpha = 1.0f, RenderMode mode = DEFAULT_RENDER_MODE, RenderArgs* args = NULL); /// 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 @@ -107,6 +108,9 @@ public: /// Returns the unscaled extents of the model's mesh Extents getUnscaledMeshExtents() const; + /// Returns the scaled equivalent of some extents in model space. + Extents calculateScaledOffsetExtents(const Extents& extents) const; + /// Returns a reference to the shared geometry. const QSharedPointer& getGeometry() const { return _geometry; } @@ -247,7 +251,7 @@ private: void applyNextGeometry(); void deleteGeometry(); - void renderMeshes(RenderMode mode, bool translucent, float alphaThreshold = 0.5f); + void renderMeshes(RenderMode mode, bool translucent, float alphaThreshold = 0.5f, RenderArgs* args = NULL); QVector createJointStates(const FBXGeometry& geometry); void initJointTransforms(); @@ -325,6 +329,9 @@ private: static SkinLocations _skinTranslucentLocations; static void initSkinProgram(ProgramObject& program, SkinLocations& locations, int specularTextureUnit = 1); + + QVector _calculatedMeshBoxes; + bool _calculatedMeshBoxesValid; }; Q_DECLARE_METATYPE(QPointer)