927 lines · 40.2 KB · C++
RawDownload
////  EntityTreeRenderer.cpp//  interface/src////  Created by Brad Hefta-Gaub on 12/6/13.//  Copyright 2013 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 <QScriptSyntaxCheckResult> #include <FBXReader.h> #include "InterfaceConfig.h" #include <BoxEntityItem.h>#include <ModelEntityItem.h>#include <MouseEvent.h>#include <PerfStat.h>#include <RenderArgs.h>  #include "Menu.h"#include "NetworkAccessManager.h"#include "EntityTreeRenderer.h" #include "devices/OculusManager.h" #include "RenderableBoxEntityItem.h"#include "RenderableLightEntityItem.h"#include "RenderableModelEntityItem.h"#include "RenderableSphereEntityItem.h"#include "RenderableTextEntityItem.h" #include "renderer/GlowEffect.h"  QThread* EntityTreeRenderer::getMainThread() {    return Application::getInstance()->getEntities()->thread();}   EntityTreeRenderer::EntityTreeRenderer(bool wantScripts) :    OctreeRenderer(),    _wantScripts(wantScripts),    _entitiesScriptEngine(NULL),    _lastMouseEventValid(false){    REGISTER_ENTITY_TYPE_WITH_FACTORY(Model, RenderableModelEntityItem::factory)    REGISTER_ENTITY_TYPE_WITH_FACTORY(Box, RenderableBoxEntityItem::factory)    REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, RenderableSphereEntityItem::factory)    REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory)    REGISTER_ENTITY_TYPE_WITH_FACTORY(Text, RenderableTextEntityItem::factory)        _currentHoverOverEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID    _currentClickingOnEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID} EntityTreeRenderer::~EntityTreeRenderer() {    // do we need to delete the _entitiesScriptEngine?? or is it deleted by default} void EntityTreeRenderer::clear() {    foreach (const EntityItemID& entityID, _entityScripts.keys()) {        checkAndCallUnload(entityID);    }    OctreeRenderer::clear();    _entityScripts.clear();} void EntityTreeRenderer::init() {    OctreeRenderer::init();    EntityTree* entityTree = static_cast<EntityTree*>(_tree);    entityTree->setFBXService(this);     if (_wantScripts) {        _entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities",                                         Application::getInstance()->getControllerScriptingInterface());        Application::getInstance()->registerScriptEngineWithApplicationServices(_entitiesScriptEngine);    }     // make sure our "last avatar position" is something other than our current position, so that on our    // first chance, we'll check for enter/leave entity events.        glm::vec3 avatarPosition = Application::getInstance()->getAvatar()->getPosition();    _lastAvatarPosition = avatarPosition + glm::vec3(1.0f, 1.0f, 1.0f);        connect(entityTree, &EntityTree::deletingEntity, this, &EntityTreeRenderer::deletingEntity);    connect(entityTree, &EntityTree::addingEntity, this, &EntityTreeRenderer::checkAndCallPreload);    connect(entityTree, &EntityTree::entityScriptChanging, this, &EntityTreeRenderer::entitySciptChanging);    connect(entityTree, &EntityTree::changingEntityID, this, &EntityTreeRenderer::changingEntityID);} QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID) {    EntityItem* entity = static_cast<EntityTree*>(_tree)->findEntityByEntityItemID(entityItemID);    return loadEntityScript(entity);}  QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorText) {    QUrl url(scriptMaybeURLorText);        // If the url is not valid, this must be script text...    if (!url.isValid()) {        return scriptMaybeURLorText;    }     QString scriptContents; // assume empty        // if the scheme length is one or lower, maybe they typed in a file, let's try    const int WINDOWS_DRIVE_LETTER_SIZE = 1;    if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) {        url = QUrl::fromLocalFile(scriptMaybeURLorText);    }     // ok, let's see if it's valid... and if so, load it    if (url.isValid()) {        if (url.scheme() == "file") {            QString fileName = url.toLocalFile();            QFile scriptFile(fileName);            if (scriptFile.open(QFile::ReadOnly | QFile::Text)) {                qDebug() << "Loading file:" << fileName;                QTextStream in(&scriptFile);                scriptContents = in.readAll();            } else {                qDebug() << "ERROR Loading file:" << fileName;            }        } else {            QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();            QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url));            qDebug() << "Downloading script at" << url;            QEventLoop loop;            QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));            loop.exec();            if (reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 200) {                scriptContents = reply->readAll();            } else {                qDebug() << "ERROR Loading file:" << url.toString();            }        }    }        return scriptContents;}  QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) {    if (!entity) {        return QScriptValue(); // no entity...    }        // NOTE: we keep local variables for the entityID and the script because    // below in loadScriptContents() it's possible for us to execute the    // application event loop, which may cause our entity to be deleted on    // us. We don't really need access the entity after this point, can    // can accomplish all we need to here with just the script "text" and the ID.    EntityItemID entityID = entity->getEntityItemID();    QString entityScript = entity->getScript();        if (_entityScripts.contains(entityID)) {        EntityScriptDetails details = _entityScripts[entityID];                // check to make sure our script text hasn't changed on us since we last loaded it        if (details.scriptText == entityScript) {            return details.scriptObject; // previously loaded        }                // if we got here, then we previously loaded a script, but the entity's script value        // has changed and so we need to reload it.        _entityScripts.remove(entityID);    }    if (entityScript.isEmpty()) {        return QScriptValue(); // no script    }        QString scriptContents = loadScriptContents(entityScript);        QScriptSyntaxCheckResult syntaxCheck = QScriptEngine::checkSyntax(scriptContents);    if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {        qDebug() << "EntityTreeRenderer::loadEntityScript() entity:" << entityID;        qDebug() << "   " << syntaxCheck.errorMessage() << ":"                          << syntaxCheck.errorLineNumber() << syntaxCheck.errorColumnNumber();        qDebug() << "    SCRIPT:" << entityScript;        return QScriptValue(); // invalid script    }        QScriptValue entityScriptConstructor = _entitiesScriptEngine->evaluate(scriptContents);        if (!entityScriptConstructor.isFunction()) {        qDebug() << "EntityTreeRenderer::loadEntityScript() entity:" << entityID;        qDebug() << "    NOT CONSTRUCTOR";        qDebug() << "    SCRIPT:" << entityScript;        return QScriptValue(); // invalid script    }     QScriptValue entityScriptObject = entityScriptConstructor.construct();    EntityScriptDetails newDetails = { entityScript, entityScriptObject };    _entityScripts[entityID] = newDetails;     return entityScriptObject; // newly constructed} QScriptValue EntityTreeRenderer::getPreviouslyLoadedEntityScript(const EntityItemID& entityID) {    if (_entityScripts.contains(entityID)) {        EntityScriptDetails details = _entityScripts[entityID];        return details.scriptObject; // previously loaded    }    return QScriptValue(); // no script} void EntityTreeRenderer::setTree(Octree* newTree) {    OctreeRenderer::setTree(newTree);    static_cast<EntityTree*>(_tree)->setFBXService(this);} void EntityTreeRenderer::update() {    if (_tree) {        EntityTree* tree = static_cast<EntityTree*>(_tree);        tree->update();                // check to see if the avatar has moved and if we need to handle enter/leave entity logic        checkEnterLeaveEntities();         // Even if we're not moving the mouse, if we started clicking on an entity and we have        // not yet released the hold then this is still considered a holdingClickOnEntity event        // and we want to simulate this message here as well as in mouse move        if (_lastMouseEventValid && !_currentClickingOnEntityID.isInvalidID()) {            emit holdingClickOnEntity(_currentClickingOnEntityID, _lastMouseEvent);            QScriptValueList currentClickingEntityArgs = createMouseEventArgs(_currentClickingOnEntityID, _lastMouseEvent);            QScriptValue currentClickingEntity = loadEntityScript(_currentClickingOnEntityID);            if (currentClickingEntity.property("holdingClickOnEntity").isValid()) {                currentClickingEntity.property("holdingClickOnEntity").call(currentClickingEntity, currentClickingEntityArgs);            }        }     }} void EntityTreeRenderer::checkEnterLeaveEntities() {    if (_tree) {        _tree->lockForWrite(); // so that our scripts can do edits if they want        glm::vec3 avatarPosition = Application::getInstance()->getAvatar()->getPosition() / (float) TREE_SCALE;                if (avatarPosition != _lastAvatarPosition) {            float radius = 1.0f / (float) TREE_SCALE; // for now, assume 1 meter radius            QVector<const EntityItem*> foundEntities;            QVector<EntityItemID> entitiesContainingAvatar;                        // find the entities near us            static_cast<EntityTree*>(_tree)->findEntities(avatarPosition, radius, foundEntities);             // create a list of entities that actually contain the avatar's position            foreach(const EntityItem* entity, foundEntities) {                if (entity->contains(avatarPosition)) {                    entitiesContainingAvatar << entity->getEntityItemID();                }            }             // for all of our previous containing entities, if they are no longer containing then send them a leave event            foreach(const EntityItemID& entityID, _currentEntitiesInside) {                if (!entitiesContainingAvatar.contains(entityID)) {                    emit leaveEntity(entityID);                    QScriptValueList entityArgs = createEntityArgs(entityID);                    QScriptValue entityScript = loadEntityScript(entityID);                    if (entityScript.property("leaveEntity").isValid()) {                        entityScript.property("leaveEntity").call(entityScript, entityArgs);                    }                 }            }             // for all of our new containing entities, if they weren't previously containing then send them an enter event            foreach(const EntityItemID& entityID, entitiesContainingAvatar) {                if (!_currentEntitiesInside.contains(entityID)) {                    emit enterEntity(entityID);                    QScriptValueList entityArgs = createEntityArgs(entityID);                    QScriptValue entityScript = loadEntityScript(entityID);                    if (entityScript.property("enterEntity").isValid()) {                        entityScript.property("enterEntity").call(entityScript, entityArgs);                    }                }            }            _currentEntitiesInside = entitiesContainingAvatar;            _lastAvatarPosition = avatarPosition;        }        _tree->unlock();    }} void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode, RenderArgs::RenderSide renderSide) {    bool dontRenderAsScene = Menu::getInstance()->isOptionChecked(MenuOption::DontRenderEntitiesAsScene);        if (dontRenderAsScene) {        OctreeRenderer::render(renderMode, renderSide);    } else {        if (_tree) {            Model::startScene(renderSide);            RenderArgs args = { this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode, renderSide,                                                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };            _tree->lockForRead();            _tree->recurseTreeWithOperation(renderOperation, &args);             Model::RenderMode modelRenderMode = renderMode == RenderArgs::SHADOW_RENDER_MODE                                                ? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;             // we must call endScene while we still have the tree locked so that no one deletes a model            // on us while rendering the scene                Model::endScene(modelRenderMode, &args);            _tree->unlock();                    // stats...            _meshesConsidered = args._meshesConsidered;            _meshesRendered = args._meshesRendered;            _meshesOutOfView = args._meshesOutOfView;            _meshesTooSmall = args._meshesTooSmall;             _elementsTouched = args._elementsTouched;            _itemsRendered = args._itemsRendered;            _itemsOutOfView = args._itemsOutOfView;            _itemsTooSmall = args._itemsTooSmall;             _materialSwitches = args._materialSwitches;            _trianglesRendered = args._trianglesRendered;            _quadsRendered = args._quadsRendered;             _translucentMeshPartsRendered = args._translucentMeshPartsRendered;            _opaqueMeshPartsRendered = args._opaqueMeshPartsRendered;        }    }    deleteReleasedModels(); // seems like as good as any other place to do some memory cleanup} const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(const EntityItem* entityItem) {    const FBXGeometry* result = NULL;        if (entityItem->getType() == EntityTypes::Model) {        const RenderableModelEntityItem* constModelEntityItem = dynamic_cast<const RenderableModelEntityItem*>(entityItem);        RenderableModelEntityItem* modelEntityItem = const_cast<RenderableModelEntityItem*>(constModelEntityItem);        assert(modelEntityItem); // we need this!!!        Model* model = modelEntityItem->getModel(this);        if (model) {            result = &model->getGeometry()->getFBXGeometry();        }    }    return result;} const Model* EntityTreeRenderer::getModelForEntityItem(const EntityItem* entityItem) {    const Model* result = NULL;    if (entityItem->getType() == EntityTypes::Model) {        const RenderableModelEntityItem* constModelEntityItem = dynamic_cast<const RenderableModelEntityItem*>(entityItem);        RenderableModelEntityItem* modelEntityItem = const_cast<RenderableModelEntityItem*>(constModelEntityItem);        assert(modelEntityItem); // we need this!!!            result = modelEntityItem->getModel(this);    }    return result;} void renderElementProxy(EntityTreeElement* entityTreeElement) {    glm::vec3 elementCenter = entityTreeElement->getAACube().calcCenter() * (float) TREE_SCALE;    float elementSize = entityTreeElement->getScale() * (float) TREE_SCALE;    glColor3f(1.0f, 0.0f, 0.0f);    glPushMatrix();        glTranslatef(elementCenter.x, elementCenter.y, elementCenter.z);        glutWireCube(elementSize);    glPopMatrix();     bool displayElementChildProxies = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelElementChildProxies);     if (displayElementChildProxies) {        // draw the children        float halfSize = elementSize / 2.0f;        float quarterSize = elementSize / 4.0f;        glColor3f(1.0f, 1.0f, 0.0f);        glPushMatrix();            glTranslatef(elementCenter.x - quarterSize, elementCenter.y - quarterSize, elementCenter.z - quarterSize);            glutWireCube(halfSize);        glPopMatrix();         glColor3f(1.0f, 0.0f, 1.0f);        glPushMatrix();            glTranslatef(elementCenter.x + quarterSize, elementCenter.y - quarterSize, elementCenter.z - quarterSize);            glutWireCube(halfSize);        glPopMatrix();         glColor3f(0.0f, 1.0f, 0.0f);        glPushMatrix();            glTranslatef(elementCenter.x - quarterSize, elementCenter.y + quarterSize, elementCenter.z - quarterSize);            glutWireCube(halfSize);        glPopMatrix();         glColor3f(0.0f, 0.0f, 1.0f);        glPushMatrix();            glTranslatef(elementCenter.x - quarterSize, elementCenter.y - quarterSize, elementCenter.z + quarterSize);            glutWireCube(halfSize);        glPopMatrix();         glColor3f(1.0f, 1.0f, 1.0f);        glPushMatrix();            glTranslatef(elementCenter.x + quarterSize, elementCenter.y + quarterSize, elementCenter.z + quarterSize);            glutWireCube(halfSize);        glPopMatrix();         glColor3f(0.0f, 0.5f, 0.5f);        glPushMatrix();            glTranslatef(elementCenter.x - quarterSize, elementCenter.y + quarterSize, elementCenter.z + quarterSize);            glutWireCube(halfSize);        glPopMatrix();         glColor3f(0.5f, 0.0f, 0.0f);        glPushMatrix();            glTranslatef(elementCenter.x + quarterSize, elementCenter.y - quarterSize, elementCenter.z + quarterSize);            glutWireCube(halfSize);        glPopMatrix();         glColor3f(0.0f, 0.5f, 0.0f);        glPushMatrix();            glTranslatef(elementCenter.x + quarterSize, elementCenter.y + quarterSize, elementCenter.z - quarterSize);            glutWireCube(halfSize);        glPopMatrix();    }} void EntityTreeRenderer::renderProxies(const EntityItem* entity, RenderArgs* args) {    bool isShadowMode = args->_renderMode == RenderArgs::SHADOW_RENDER_MODE;    bool displayModelBounds = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelBounds);    if (!isShadowMode && displayModelBounds) {        PerformanceTimer perfTimer("renderProxies");         AACube maxCube = entity->getMaximumAACube();        AACube minCube = entity->getMinimumAACube();        AABox entityBox = entity->getAABox();         maxCube.scale((float) TREE_SCALE);        minCube.scale((float) TREE_SCALE);        entityBox.scale((float) TREE_SCALE);         glm::vec3 maxCenter = maxCube.calcCenter();        glm::vec3 minCenter = minCube.calcCenter();        glm::vec3 entityBoxCenter = entityBox.calcCenter();        glm::vec3 entityBoxScale = entityBox.getScale();         // draw the max bounding cube        glColor4f(1.0f, 1.0f, 0.0f, 1.0f);        glPushMatrix();            glTranslatef(maxCenter.x, maxCenter.y, maxCenter.z);            glutWireCube(maxCube.getScale());        glPopMatrix();         // draw the min bounding cube        glColor4f(0.0f, 1.0f, 0.0f, 1.0f);        glPushMatrix();            glTranslatef(minCenter.x, minCenter.y, minCenter.z);            glutWireCube(minCube.getScale());        glPopMatrix();                // draw the entityBox bounding box        glColor4f(0.0f, 0.0f, 1.0f, 1.0f);        glPushMatrix();            glTranslatef(entityBoxCenter.x, entityBoxCenter.y, entityBoxCenter.z);            glScalef(entityBoxScale.x, entityBoxScale.y, entityBoxScale.z);            glutWireCube(1.0f);        glPopMatrix();          glm::vec3 position = entity->getPosition() * (float) TREE_SCALE;        glm::vec3 center = entity->getCenter() * (float) TREE_SCALE;        glm::vec3 dimensions = entity->getDimensions() * (float) TREE_SCALE;        glm::quat rotation = entity->getRotation();         glColor4f(1.0f, 0.0f, 1.0f, 1.0f);        glPushMatrix();            glTranslatef(position.x, position.y, position.z);            glm::vec3 axis = glm::axis(rotation);            glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);            glPushMatrix();                glm::vec3 positionToCenter = center - position;                glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);                glScalef(dimensions.x, dimensions.y, dimensions.z);                glutWireCube(1.0f);            glPopMatrix();        glPopMatrix();    }} void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) {    bool wantDebug = false;     args->_elementsTouched++;    // actually render it here...    // we need to iterate the actual entityItems of the element    EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);     QList<EntityItem*>& entityItems = entityTreeElement->getEntities();    

Lines 1–500 of 927.

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