293 lines · 11.9 KB · C++
RawDownload
////  RenderableModelEntityItem.cpp//  interface/src////  Created by Brad Hefta-Gaub on 8/6/14.//  Copyright 2014 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 <glm/gtx/quaternion.hpp> #include <FBXReader.h> #include "InterfaceConfig.h" #include <BoxEntityItem.h>#include <ModelEntityItem.h>#include <PerfStat.h>  #include "Menu.h"#include "EntityTreeRenderer.h"#include "RenderableModelEntityItem.h" EntityItem* RenderableModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {    return new RenderableModelEntityItem(entityID, properties);} RenderableModelEntityItem::~RenderableModelEntityItem() {    assert(_myRenderer || !_model); // if we have a model, we need to know our renderer    if (_myRenderer && _model) {        _myRenderer->releaseModel(_model);        _model = NULL;    }} bool RenderableModelEntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) {    QString oldModelURL = getModelURL();    bool somethingChanged = ModelEntityItem::setProperties(properties, forceCopy);    if (somethingChanged && oldModelURL != getModelURL()) {        _needsModelReload = true;    }    return somethingChanged;} int RenderableModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,                                                 ReadBitstreamToTreeParams& args,                                                EntityPropertyFlags& propertyFlags, bool overwriteLocalData) {    QString oldModelURL = getModelURL();    int bytesRead = ModelEntityItem::readEntitySubclassDataFromBuffer(data, bytesLeftToRead,                                                                         args, propertyFlags, overwriteLocalData);    if (oldModelURL != getModelURL()) {        _needsModelReload = true;    }    return bytesRead;} void RenderableModelEntityItem::remapTextures() {    if (!_model) {        return; // nothing to do if we don't have a model    }        if (!_model->isLoadedWithTextures()) {        return; // nothing to do if the model has not yet loaded it's default textures    }        if (!_originalTexturesRead && _model->isLoadedWithTextures()) {        const QSharedPointer<NetworkGeometry>& networkGeometry = _model->getGeometry();        if (networkGeometry) {            _originalTextures = networkGeometry->getTextureNames();            _originalTexturesRead = true;        }    }        if (_currentTextures == _textures) {        return; // nothing to do if our recently mapped textures match our desired textures    }        // since we're changing here, we need to run through our current texture map    // and any textures in the recently mapped texture, that is not in our desired    // textures, we need to "unset"    QJsonDocument currentTexturesAsJson = QJsonDocument::fromJson(_currentTextures.toUtf8());    QJsonObject currentTexturesAsJsonObject = currentTexturesAsJson.object();    QVariantMap currentTextureMap = currentTexturesAsJsonObject.toVariantMap();     QJsonDocument texturesAsJson = QJsonDocument::fromJson(_textures.toUtf8());    QJsonObject texturesAsJsonObject = texturesAsJson.object();    QVariantMap textureMap = texturesAsJsonObject.toVariantMap();     foreach(const QString& key, currentTextureMap.keys()) {        // if the desired texture map (what we're setting the textures to) doesn't        // contain this texture, then remove it by setting the URL to null        if (!textureMap.contains(key)) {            QUrl noURL;            qDebug() << "Removing texture named" << key << "by replacing it with no URL";            _model->setTextureWithNameToURL(key, noURL);        }    }     // here's where we remap any textures if needed...    foreach(const QString& key, textureMap.keys()) {        QUrl newTextureURL = textureMap[key].toUrl();        qDebug() << "Updating texture named" << key << "to texture at URL" << newTextureURL;        _model->setTextureWithNameToURL(key, newTextureURL);    }        _currentTextures = _textures;}  void RenderableModelEntityItem::render(RenderArgs* args) {    PerformanceTimer perfTimer("RMEIrender");    assert(getType() == EntityTypes::Model);        bool drawAsModel = hasModel();     glm::vec3 position = getPosition() * (float)TREE_SCALE;    float size = getSize() * (float)TREE_SCALE;    glm::vec3 dimensions = getDimensions() * (float)TREE_SCALE;        if (drawAsModel) {        remapTextures();        glPushMatrix();        {            float alpha = getLocalRenderAlpha();             if (!_model || _needsModelReload) {                // TODO: this getModel() appears to be about 3% of model render time. We should optimize                PerformanceTimer perfTimer("getModel");                EntityTreeRenderer* renderer = static_cast<EntityTreeRenderer*>(args->_renderer);                getModel(renderer);            }                        if (_model) {                // handle animations..                if (hasAnimation()) {                    if (!jointsMapped()) {                        QStringList modelJointNames = _model->getJointNames();                        mapJoints(modelJointNames);                    }                     if (jointsMapped()) {                        QVector<glm::quat> frameData = getAnimationFrame();                        for (int i = 0; i < frameData.size(); i++) {                            _model->setJointState(i, true, frameData[i]);                        }                    }                }                 glm::quat rotation = getRotation();                if (needsToCallUpdate() && _model->isActive()) {                    _model->setScaleToFit(true, dimensions);                    _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint());                    _model->setRotation(rotation);                    _model->setTranslation(position);                                        // make sure to simulate so everything gets set up correctly for rendering                    {                        PerformanceTimer perfTimer("_model->simulate");                        _model->simulate(0.0f);                    }                    _needsInitialSimulation = false;                }                 // TODO: should we allow entityItems to have alpha on their models?                Model::RenderMode modelRenderMode = args->_renderMode == RenderArgs::SHADOW_RENDER_MODE                                                        ? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;                        if (_model->isActive()) {                    // 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");                    bool dontRenderAsScene = Menu::getInstance()->isOptionChecked(MenuOption::DontRenderEntitiesAsScene);                    bool displayModelTriangles = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelTriangles);                    bool rendered = false;                    if (displayModelTriangles) {                        rendered = _model->renderTriangleProxies();                    }                                        if (!rendered) {                        if (dontRenderAsScene) {                            _model->render(alpha, modelRenderMode, args);                        } else {                            _model->renderInScene(alpha, args);                        }                    }                } else {                    // if we couldn't get a model, then just draw a cube                    glColor3ub(getColor()[RED_INDEX],getColor()[GREEN_INDEX],getColor()[BLUE_INDEX]);                    glPushMatrix();                        glTranslatef(position.x, position.y, position.z);                        Application::getInstance()->getDeferredLightingEffect()->renderWireCube(size);                    glPopMatrix();                }            } else {                // if we couldn't get a model, then just draw a cube                glColor3ub(getColor()[RED_INDEX],getColor()[GREEN_INDEX],getColor()[BLUE_INDEX]);                glPushMatrix();                    glTranslatef(position.x, position.y, position.z);                    Application::getInstance()->getDeferredLightingEffect()->renderWireCube(size);                glPopMatrix();            }        }        glPopMatrix();    } else {        glColor3ub(getColor()[RED_INDEX],getColor()[GREEN_INDEX],getColor()[BLUE_INDEX]);        glPushMatrix();        glTranslatef(position.x, position.y, position.z);        Application::getInstance()->getDeferredLightingEffect()->renderWireCube(size);        glPopMatrix();    }} Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) {    Model* result = NULL;     // make sure our renderer is setup    if (!_myRenderer) {        _myRenderer = renderer;    }    assert(_myRenderer == renderer); // you should only ever render on one renderer        if (QThread::currentThread() != _myRenderer->thread()) {        return _model;    }        _needsModelReload = false; // this is the reload     // if we have a URL, then we will want to end up returning a model...    if (!getModelURL().isEmpty()) {            // if we have a previously allocated model, but it's URL doesn't match        // then we need to let our renderer update our model for us.        if (_model && QUrl(getModelURL()) != _model->getURL()) {            result = _model = _myRenderer->updateModel(_model, getModelURL());            _needsInitialSimulation = true;        } else if (!_model) { // if we don't yet have a model, then we want our renderer to allocate one            result = _model = _myRenderer->allocateModel(getModelURL());            _needsInitialSimulation = true;        } else { // we already have the model we want...            result = _model;        }    } else { // if our desired URL is empty, we may need to delete our existing model        if (_model) {            _myRenderer->releaseModel(_model);            result = _model = NULL;            _needsInitialSimulation = true;        }    }        return result;} bool RenderableModelEntityItem::needsToCallUpdate() const {    return _needsInitialSimulation || ModelEntityItem::needsToCallUpdate();} EntityItemProperties RenderableModelEntityItem::getProperties() const {    EntityItemProperties properties = ModelEntityItem::getProperties(); // get the properties from our base class    if (_originalTexturesRead) {        properties.setTextureNames(_originalTextures);    }    return properties;} bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,                         bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,                          void** intersectedObject, bool precisionPicking) const {    if (!_model) {        return true;    }        glm::vec3 originInMeters = origin * (float)TREE_SCALE;    QString extraInfo;    float localDistance;        //qDebug() << "RenderableModelEntityItem::findDetailedRayIntersection() precisionPicking:" << precisionPicking;        bool intersectsModel = _model->findRayIntersectionAgainstSubMeshes(originInMeters, direction,                                             localDistance, face, extraInfo, precisionPicking);        if (intersectsModel) {        // NOTE: findRayIntersectionAgainstSubMeshes() does work in meters, but we're expected to return        // results in tree scale.        distance = localDistance / (float)TREE_SCALE;    }     return intersectsModel; // we only got here if we intersected our non-aabox                         }  

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t