diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f564cf952a..f381f0e4e7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3403,7 +3403,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se } } - render::Scene::PendingChanges pendingChanges; + render::PendingChanges pendingChanges; // Make sure the WorldBox is in the scene if (WorldBoxRenderData::_item == 0) { @@ -3415,18 +3415,23 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se pendingChanges.resetItem(WorldBoxRenderData::_item, worldBoxRenderPayload); } + { + PerformanceTimer perfTimer("SceneProcessPendingChanges"); + _main3DScene->enqueuePendingChanges(pendingChanges); - _main3DScene->enqueuePendingChanges(pendingChanges); - - _main3DScene->processPendingChangesQueue(); + _main3DScene->processPendingChangesQueue(); + } // FOr now every frame pass the renderCOntext - render::RenderContext renderContext; - renderContext.args = renderArgs; - _renderEngine->setRenderContext(renderContext); + { + PerformanceTimer perfTimer("EngineRun"); + render::RenderContext renderContext; + renderContext.args = renderArgs; + _renderEngine->setRenderContext(renderContext); - // Before the deferred pass, let's try to use the render engine - _renderEngine->run(); + // Before the deferred pass, let's try to use the render engine + _renderEngine->run(); + } { DependencyManager::get()->setAmbientLightMode(getRenderAmbientLight()); diff --git a/interface/src/audio/AudioToolBox.cpp b/interface/src/audio/AudioToolBox.cpp index 85b8b19788..68328e151e 100644 --- a/interface/src/audio/AudioToolBox.cpp +++ b/interface/src/audio/AudioToolBox.cpp @@ -40,13 +40,13 @@ void AudioToolBox::render(int x, int y, int padding, bool boxed) { glEnable(GL_TEXTURE_2D); if (!_micTexture) { - _micTexture = DependencyManager::get()->getImageTexture(PathUtils::resourcesPath() + "images/mic.svg"); + _micTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/mic.svg"); } if (!_muteTexture) { - _muteTexture = DependencyManager::get()->getImageTexture(PathUtils::resourcesPath() + "images/mic-mute.svg"); + _muteTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/mic-mute.svg"); } if (_boxTexture) { - _boxTexture = DependencyManager::get()->getImageTexture(PathUtils::resourcesPath() + "images/audio-box.svg"); + _boxTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/audio-box.svg"); } auto audioIO = DependencyManager::get(); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index a4b437c009..8f8a3a8ef3 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -67,7 +67,7 @@ void AvatarManager::init() { auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload); static_cast(_myAvatar.get())->_renderItemID = scene->allocateID(); - render::Scene::PendingChanges pendingChanges; + render::PendingChanges pendingChanges; pendingChanges.resetItem(static_cast(_myAvatar.get())->_renderItemID, avatarPayloadPointer); scene->enqueuePendingChanges(pendingChanges); @@ -152,7 +152,7 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload); static_cast(avatar.get())->_renderItemID = scene->allocateID(); - render::Scene::PendingChanges pendingChanges; + render::PendingChanges pendingChanges; pendingChanges.resetItem(static_cast(avatar.get())->_renderItemID, avatarPayloadPointer); scene->enqueuePendingChanges(pendingChanges); @@ -186,7 +186,7 @@ void AvatarManager::removeAvatar(const QUuid& sessionUUID) { } render::ScenePointer scene = Application::getInstance()->getMain3DScene(); - render::Scene::PendingChanges pendingChanges; + render::PendingChanges pendingChanges; pendingChanges.removeItem(avatar->_renderItemID); scene->enqueuePendingChanges(pendingChanges); } diff --git a/interface/src/devices/CameraToolBox.cpp b/interface/src/devices/CameraToolBox.cpp index a1e00d7052..27cee5185b 100644 --- a/interface/src/devices/CameraToolBox.cpp +++ b/interface/src/devices/CameraToolBox.cpp @@ -76,10 +76,10 @@ void CameraToolBox::render(int x, int y, bool boxed) { glEnable(GL_TEXTURE_2D); if (!_enabledTexture) { - _enabledTexture = DependencyManager::get()->getImageTexture(PathUtils::resourcesPath() + "images/face.svg"); + _enabledTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/face.svg"); } if (!_mutedTexture) { - _mutedTexture = DependencyManager::get()->getImageTexture(PathUtils::resourcesPath() + "images/face-mute.svg"); + _mutedTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/face-mute.svg"); } const int MUTE_ICON_SIZE = 24; diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 678fa096c3..0778e5be6b 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -423,8 +423,8 @@ void ApplicationOverlay::displayOverlayTextureStereo(Camera& whichCamera, float }); if (!_crosshairTexture) { - _crosshairTexture = DependencyManager::get()-> - getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png"); + _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + + "images/sixense-reticle.png"); } //draw the mouse pointer @@ -564,8 +564,7 @@ bool ApplicationOverlay::calculateRayUICollisionPoint(const glm::vec3& position, void ApplicationOverlay::renderPointers() { //lazily load crosshair texture if (_crosshairTexture == 0) { - _crosshairTexture = DependencyManager::get()-> - getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png"); + _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png"); } glEnable(GL_TEXTURE_2D); diff --git a/interface/src/ui/RearMirrorTools.cpp b/interface/src/ui/RearMirrorTools.cpp index 94dd614edf..33f3e1b487 100644 --- a/interface/src/ui/RearMirrorTools.cpp +++ b/interface/src/ui/RearMirrorTools.cpp @@ -34,11 +34,10 @@ RearMirrorTools::RearMirrorTools(QRect& bounds) : _windowed(false), _fullScreen(false) { - auto textureCache = DependencyManager::get(); - _closeTexture = textureCache->getImageTexture(PathUtils::resourcesPath() + "images/close.svg"); + _closeTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/close.svg"); - _zoomHeadTexture = textureCache->getImageTexture(PathUtils::resourcesPath() + "images/plus.svg"); - _zoomBodyTexture = textureCache->getImageTexture(PathUtils::resourcesPath() + "images/minus.svg"); + _zoomHeadTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/plus.svg"); + _zoomBodyTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/minus.svg"); _shrinkIconRect = QRect(ICON_PADDING, ICON_PADDING, ICON_SIZE, ICON_SIZE); _closeIconRect = QRect(_bounds.left() + ICON_PADDING, _bounds.top() + ICON_PADDING, ICON_SIZE, ICON_SIZE); diff --git a/interface/src/ui/overlays/TextOverlay.cpp b/interface/src/ui/overlays/TextOverlay.cpp index 54156d5d2b..e709bbd9fc 100644 --- a/interface/src/ui/overlays/TextOverlay.cpp +++ b/interface/src/ui/overlays/TextOverlay.cpp @@ -24,6 +24,7 @@ TextOverlay::TextOverlay() : _topMargin(DEFAULT_MARGIN), _fontSize(DEFAULT_FONTSIZE) { + _textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT); } TextOverlay::TextOverlay(const TextOverlay* textOverlay) : @@ -35,6 +36,7 @@ TextOverlay::TextOverlay(const TextOverlay* textOverlay) : _topMargin(textOverlay->_topMargin), _fontSize(textOverlay->_fontSize) { + _textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT); } TextOverlay::~TextOverlay() { diff --git a/interface/src/ui/overlays/TextOverlay.h b/interface/src/ui/overlays/TextOverlay.h index 5b77be0cac..5a715ebfdf 100644 --- a/interface/src/ui/overlays/TextOverlay.h +++ b/interface/src/ui/overlays/TextOverlay.h @@ -60,7 +60,7 @@ public: private: - TextRenderer* _textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT); + TextRenderer* _textRenderer = nullptr; QString _text; xColor _backgroundColor; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index b59ea7e7af..c44f70c74e 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -96,9 +96,13 @@ void EntityTreeRenderer::clear() { OctreeRenderer::clear(); _entityScripts.clear(); - // TODO/FIXME - this needs to be fixed... we need to clear all items out of the scene in this case. - qDebug() << "EntityTreeRenderer::clear() need to clear the scene... "; - + auto scene = _viewState->getMain3DScene(); + render::PendingChanges pendingChanges; + foreach(auto entity, _entitiesInScene) { + entity->removeFromScene(entity, scene, pendingChanges); + } + scene->enqueuePendingChanges(pendingChanges); + _entitiesInScene.clear(); } void EntityTreeRenderer::init() { @@ -704,8 +708,8 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) } } - // hack for models. :( - if (entityItem->getType() == EntityTypes::Model) { + // hack for models and other entities that don't yet play well with others. :( + if (!entityItem->canRenderInScene()) { // render entityItem AABox entityBox = entityItem->getAABox(); @@ -1063,12 +1067,13 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) { _entityScripts.remove(entityID); // here's where we remove the entity payload from the scene - - render::Scene::PendingChanges pendingChanges; - if (_entityToSceneItems.contains(entityID)) { - render::ItemID renderItem = _entityToSceneItems[entityID]; - pendingChanges.removeItem(renderItem); - _viewState->getMain3DScene()->enqueuePendingChanges(pendingChanges); + auto entity = static_cast(_tree)->findEntityByID(entityID); + if (entity && _entitiesInScene.contains(entity)) { + render::PendingChanges pendingChanges; + auto scene = _viewState->getMain3DScene(); + entity->removeFromScene(entity, scene, pendingChanges); + scene->enqueuePendingChanges(pendingChanges); + _entitiesInScene.remove(entity); } } @@ -1076,19 +1081,15 @@ void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) { checkAndCallPreload(entityID); // here's where we add the entity payload to the scene - - render::Scene::PendingChanges pendingChanges; - render::ItemID renderItem = _viewState->getMain3DScene()->allocateID(); - _entityToSceneItems[entityID] = renderItem; - EntityItemPointer entity = static_cast(_tree)->findEntityByID(entityID); - - auto renderData = RenderableEntityItem::Pointer(new RenderableEntityItem(entity)); - auto renderPayload = render::PayloadPointer(new RenderableEntityItem::Payload(renderData)); - - pendingChanges.resetItem(renderItem, renderPayload); - - _viewState->getMain3DScene()->enqueuePendingChanges(pendingChanges); - _viewState->getMain3DScene()->processPendingChangesQueue(); + auto entity = static_cast(_tree)->findEntityByID(entityID); + if (entity && entity->canRenderInScene()) { + render::PendingChanges pendingChanges; + auto scene = _viewState->getMain3DScene(); + if (entity->addToScene(entity, scene, pendingChanges)) { + _entitiesInScene.insert(entity); + } + scene->enqueuePendingChanges(pendingChanges); + } } void EntityTreeRenderer::entitySciptChanging(const EntityItemID& entityID) { diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 22d6703069..234e590b7d 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -187,7 +187,7 @@ private: float _previousStageHour; int _previousStageDay; - QHash _entityToSceneItems; + QSet _entitiesInScene; }; diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.h b/libraries/entities-renderer/src/RenderableBoxEntityItem.h index 6ae76b0315..06a62706b9 100644 --- a/libraries/entities-renderer/src/RenderableBoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.h @@ -14,6 +14,7 @@ #include #include "RenderableDebugableEntityItem.h" +#include "RenderableEntityItem.h" class RenderableBoxEntityItem : public BoxEntityItem { public: @@ -24,6 +25,8 @@ public: { } virtual void render(RenderArgs* args); + + SIMPLE_RENDERABLE() }; diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 7cf98b861d..0161df5c6f 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -13,7 +13,7 @@ #include "RenderableEntityItem.h" namespace render { - template <> const ItemKey payloadGetKey(const RenderableEntityItem::Pointer& payload) { + template <> const ItemKey payloadGetKey(const RenderableEntityItemProxy::Pointer& payload) { if (payload && payload->entity) { if (payload->entity->getType() == EntityTypes::Light) { return ItemKey::Builder::light(); @@ -22,13 +22,13 @@ namespace render { return ItemKey::Builder::opaqueShape(); } - template <> const Item::Bound payloadGetBound(const RenderableEntityItem::Pointer& payload) { + template <> const Item::Bound payloadGetBound(const RenderableEntityItemProxy::Pointer& payload) { if (payload && payload->entity) { return payload->entity->getAABox(); } return render::Item::Bound(); } - template <> void payloadRender(const RenderableEntityItem::Pointer& payload, RenderArgs* args) { + template <> void payloadRender(const RenderableEntityItemProxy::Pointer& payload, RenderArgs* args) { if (args) { args->_elementsTouched++; if (payload && payload->entity) { diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 028bc5efa5..92eef7e8ea 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -15,19 +15,53 @@ #include #include -class RenderableEntityItem { + +class RenderableEntityItemProxy { public: - RenderableEntityItem(EntityItemPointer entity) : entity(entity) { } - typedef render::Payload Payload; + RenderableEntityItemProxy(EntityItemPointer entity) : entity(entity) { } + typedef render::Payload Payload; typedef Payload::DataPointer Pointer; EntityItemPointer entity; }; namespace render { - template <> const ItemKey payloadGetKey(const RenderableEntityItem::Pointer& payload); - template <> const Item::Bound payloadGetBound(const RenderableEntityItem::Pointer& payload); - template <> void payloadRender(const RenderableEntityItem::Pointer& payload, RenderArgs* args); + template <> const ItemKey payloadGetKey(const RenderableEntityItemProxy::Pointer& payload); + template <> const Item::Bound payloadGetBound(const RenderableEntityItemProxy::Pointer& payload); + template <> void payloadRender(const RenderableEntityItemProxy::Pointer& payload, RenderArgs* args); } +// Mixin class for implementing basic single item rendering +class SimpleRenderableEntityItem { +public: + bool addToScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { + _myItem = scene->allocateID(); + + auto renderData = RenderableEntityItemProxy::Pointer(new RenderableEntityItemProxy(self)); + auto renderPayload = render::PayloadPointer(new RenderableEntityItemProxy::Payload(renderData)); + + pendingChanges.resetItem(_myItem, renderPayload); + + return true; + } + + void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { + pendingChanges.removeItem(_myItem); + } + +private: + render::ItemID _myItem; +}; + + +#define SIMPLE_RENDERABLE() \ +public: \ + virtual bool canRenderInScene() { return true; } \ + virtual bool addToScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { return _renderHelper.addToScene(self, scene, pendingChanges); } \ + virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { _renderHelper.removeFromScene(self, scene, pendingChanges); } \ +private: \ + SimpleRenderableEntityItem _renderHelper; + + + #endif // hifi_RenderableEntityItem_h diff --git a/libraries/entities-renderer/src/RenderableLightEntityItem.h b/libraries/entities-renderer/src/RenderableLightEntityItem.h index 427557432c..a90bb0baad 100644 --- a/libraries/entities-renderer/src/RenderableLightEntityItem.h +++ b/libraries/entities-renderer/src/RenderableLightEntityItem.h @@ -22,6 +22,7 @@ public: LightEntityItem(entityItemID, properties) { } + virtual bool canRenderInScene() { return false; } // we don't yet play well with others 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-renderer/src/RenderableLineEntityItem.h b/libraries/entities-renderer/src/RenderableLineEntityItem.h index 8f04ca9e9c..eb23b3ee48 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.h @@ -14,6 +14,7 @@ #include #include "RenderableDebugableEntityItem.h" +#include "RenderableEntityItem.h" class RenderableLineEntityItem : public LineEntityItem { public: @@ -23,6 +24,8 @@ public: LineEntityItem(entityItemID, properties) { } virtual void render(RenderArgs* args); + + SIMPLE_RENDERABLE() }; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index f504019e67..335f3a4d25 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -43,6 +43,7 @@ public: virtual void somethingChangedNotification() { _needsInitialSimulation = true; } + virtual bool canRenderInScene() { return false; } // we don't yet play well with others 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-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h index a449278895..66505a2cd2 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -13,6 +13,7 @@ #include #include +#include "RenderableEntityItem.h" class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem { public: @@ -22,6 +23,8 @@ public: void updateQuads(RenderArgs* args, bool textured); + SIMPLE_RENDERABLE() + protected: int _cacheID; diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.h b/libraries/entities-renderer/src/RenderableSphereEntityItem.h index 3b02541061..b6f7ff996e 100644 --- a/libraries/entities-renderer/src/RenderableSphereEntityItem.h +++ b/libraries/entities-renderer/src/RenderableSphereEntityItem.h @@ -14,6 +14,8 @@ #include +#include "RenderableEntityItem.h" + class RenderableSphereEntityItem : public SphereEntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); @@ -23,6 +25,8 @@ public: { } virtual void render(RenderArgs* args); + + SIMPLE_RENDERABLE() }; diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 057749c25f..8a67bb99f2 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -20,52 +20,41 @@ #include "RenderableTextEntityItem.h" #include "GLMHelpers.h" - EntityItemPointer RenderableTextEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { return EntityItemPointer(new RenderableTextEntityItem(entityID, properties)); } void RenderableTextEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderableTextEntityItem::render"); - assert(getType() == EntityTypes::Text); - glm::vec3 position = getPosition(); + Q_ASSERT(getType() == EntityTypes::Text); + + static const float SLIGHTLY_BEHIND = -0.005f; + glm::vec4 textColor = glm::vec4(toGlm(getTextColorX()), 1.0f); + glm::vec4 backgroundColor = glm::vec4(toGlm(getBackgroundColorX()), 1.0f); glm::vec3 dimensions = getDimensions(); - glm::vec3 halfDimensions = dimensions / 2.0f; - glm::quat rotation = getRotation(); - float leftMargin = 0.1f; - float topMargin = 0.1f; - - //qCDebug(entitytree) << "RenderableTextEntityItem::render() id:" << getEntityItemID() << "text:" << getText(); - - 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); - - float alpha = 1.0f; //getBackgroundAlpha(); - static const float SLIGHTLY_BEHIND = -0.005f; - - glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND); - glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND); - - // TODO: Determine if we want these entities to have the deferred lighting effect? I think we do, so that the color - // used for a sphere, or box have the same look as those used on a text entity. - //DependencyManager::get()->bindSimpleProgram(); - DependencyManager::get()->renderQuad(topLeft, bottomRight, glm::vec4(toGlm(getBackgroundColorX()), alpha)); - //DependencyManager::get()->releaseSimpleProgram(); - - glTranslatef(-(halfDimensions.x - leftMargin), halfDimensions.y - topMargin, 0.0f); - glm::vec4 textColor(toGlm(getTextColorX()), alpha); - // this is a ratio determined through experimentation - const float scaleFactor = 0.08f * _lineHeight; - glScalef(scaleFactor, -scaleFactor, scaleFactor); - glm::vec2 bounds(dimensions.x / scaleFactor, dimensions.y / scaleFactor); - _textRenderer->draw(0, 0, _text, textColor, bounds); - } - glPopMatrix(); + + Transform transformToTopLeft = getTransformToCenter(); + transformToTopLeft.postTranslate(glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left + transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed + + // Batch render calls + Q_ASSERT(args->_batch); + gpu::Batch& batch = *args->_batch; + batch.setModelTransform(transformToTopLeft); + + // Render background + glm::vec3 minCorner = glm::vec3(0.0f, -dimensions.y, SLIGHTLY_BEHIND); + glm::vec3 maxCorner = glm::vec3(dimensions.x, 0.0f, SLIGHTLY_BEHIND); + DependencyManager::get()->renderQuad(batch, minCorner, maxCorner, backgroundColor); + + float scale = _lineHeight / _textRenderer->getRowHeight(); + transformToTopLeft.setScale(scale); // Scale to have the correct line height + batch.setModelTransform(transformToTopLeft); + + float leftMargin = 0.5f * _lineHeight, topMargin = 0.5f * _lineHeight; + glm::vec2 bounds = glm::vec2(dimensions.x - 2.0f * leftMargin, dimensions.y - 2.0f * topMargin); + _textRenderer->draw(batch, leftMargin / scale, -topMargin / scale, _text, textColor, bounds / scale); } - diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h index aa761461ac..5db22f3045 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.h +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h @@ -13,7 +13,7 @@ #define hifi_RenderableTextEntityItem_h #include -#include +#include const int FIXED_FONT_POINT_SIZE = 40; @@ -27,9 +27,10 @@ public: ~RenderableTextEntityItem() { delete _textRenderer; } virtual void render(RenderArgs* args); + virtual bool canRenderInScene() { return false; } // we don't yet play well with others private: - TextRenderer* _textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE / 2.0f); + TextRenderer3D* _textRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE / 2.0f); }; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 8dad2a0855..2ad4d799b6 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -24,6 +24,7 @@ public: virtual void render(RenderArgs* args); virtual void setSourceUrl(const QString& value); + virtual bool canRenderInScene() { return false; } // we don't yet play well with others private: OffscreenQmlSurface* _webSurface{ nullptr }; diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index d21906fa3f..926fd8b4b2 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -14,3 +14,4 @@ target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${BULLET_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES}) link_hifi_libraries(avatars shared octree gpu model fbx networking animation environment) +include_hifi_library_headers(render) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index b4941b833b..c2c0e5603f 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1005,6 +1005,13 @@ void EntityItem::setTranformToCenter(const Transform& transform) { setTransform(copy); } +void EntityItem::setDimensions(const glm::vec3& value) { + if (value.x == 0.0f || value.y == 0.0f || value.z == 0.0f) { + return; + } + _transform.setScale(value); +} + /// The maximum bounding cube for the entity, independent of it's rotation. /// This accounts for the registration point (upon which rotation occurs around). /// diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 8f88b6de07..5cbd29cfa8 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -12,6 +12,7 @@ #ifndef hifi_EntityItem_h #define hifi_EntityItem_h +#include #include #include @@ -33,6 +34,11 @@ class EntitySimulation; class EntityTreeElement; class EntityTreeElementExtraEncodeData; +namespace render { + class Scene; + class PendingChanges; +} + // these thesholds determine what updates will be ignored (client and server) const float IGNORE_POSITION_DELTA = 0.0001f; const float IGNORE_DIMENSIONS_DELTA = 0.0005f; @@ -151,6 +157,11 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData) { return 0; } + virtual bool canRenderInScene() { return false; } // does your entity property render using Render Items and Payloads + 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, + render::PendingChanges& pendingChanges) { } // by default entity items don't add to scene virtual void render(RenderArgs* args) { } // by default entity items don't know how to render static int expectedBytes(); @@ -195,7 +206,7 @@ public: /// Dimensions in meters (0.0 - TREE_SCALE) inline const glm::vec3& getDimensions() const { return _transform.getScale(); } - inline virtual void setDimensions(const glm::vec3& value) { _transform.setScale(glm::abs(value)); } + virtual void setDimensions(const glm::vec3& value); float getGlowLevel() const { return _glowLevel; } diff --git a/libraries/gpu/src/gpu/GLBackendInput.cpp b/libraries/gpu/src/gpu/GLBackendInput.cpp index fde6ac40d0..aac7b56bc2 100755 --- a/libraries/gpu/src/gpu/GLBackendInput.cpp +++ b/libraries/gpu/src/gpu/GLBackendInput.cpp @@ -66,22 +66,23 @@ void GLBackend::updateInput() { newActivation.set(attrib._slot); } } - + // Manage Activation what was and what is expected now for (unsigned int i = 0; i < newActivation.size(); i++) { bool newState = newActivation[i]; if (newState != _input._attributeActivation[i]) { #if defined(SUPPORT_LEGACY_OPENGL) - if (i < NUM_CLASSIC_ATTRIBS) { + const bool useClientState = i < NUM_CLASSIC_ATTRIBS; +#else + const bool useClientState = false; +#endif + if (useClientState) { if (newState) { glEnableClientState(attributeSlotToClassicAttribName[i]); } else { glDisableClientState(attributeSlotToClassicAttribName[i]); } } else { -#else - { -#endif if (newState) { glEnableVertexAttribArray(i); } else { @@ -89,7 +90,7 @@ void GLBackend::updateInput() { } } (void) CHECK_GL_ERROR(); - + _input._attributeActivation.flip(i); } } @@ -123,29 +124,30 @@ void GLBackend::updateInput() { GLenum type = _elementTypeToGLType[attrib._element.getType()]; GLuint stride = strides[bufferNum]; GLuint pointer = attrib._offset + offsets[bufferNum]; - #if defined(SUPPORT_LEGACY_OPENGL) - if (slot < NUM_CLASSIC_ATTRIBS) { +#if defined(SUPPORT_LEGACY_OPENGL) + const bool useClientState = slot < NUM_CLASSIC_ATTRIBS; +#else + const bool useClientState = false; +#endif + if (useClientState) { switch (slot) { - case Stream::POSITION: - glVertexPointer(count, type, stride, reinterpret_cast(pointer)); - break; - case Stream::NORMAL: - glNormalPointer(type, stride, reinterpret_cast(pointer)); - break; - case Stream::COLOR: - glColorPointer(count, type, stride, reinterpret_cast(pointer)); - break; - case Stream::TEXCOORD: - glTexCoordPointer(count, type, stride, reinterpret_cast(pointer)); - break; + case Stream::POSITION: + glVertexPointer(count, type, stride, reinterpret_cast(pointer)); + break; + case Stream::NORMAL: + glNormalPointer(type, stride, reinterpret_cast(pointer)); + break; + case Stream::COLOR: + glColorPointer(count, type, stride, reinterpret_cast(pointer)); + break; + case Stream::TEXCOORD: + glTexCoordPointer(count, type, stride, reinterpret_cast(pointer)); + break; }; } else { - #else - { - #endif GLboolean isNormalized = attrib._element.isNormalized(); glVertexAttribPointer(slot, count, type, isNormalized, stride, - reinterpret_cast(pointer)); + reinterpret_cast(pointer)); } (void) CHECK_GL_ERROR(); } diff --git a/libraries/gpu/src/gpu/Stream.cpp b/libraries/gpu/src/gpu/Stream.cpp index e23a730370..634545b4dd 100644 --- a/libraries/gpu/src/gpu/Stream.cpp +++ b/libraries/gpu/src/gpu/Stream.cpp @@ -10,8 +10,8 @@ // #include "Stream.h" - -#include //min max and more + +#include //min max and more using namespace gpu; diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 0d9664f1ab..9036f0f6db 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -17,11 +17,11 @@ namespace gpu { -// THe spherical harmonics is a nice tool for cubemap, so if required, the irradiance SH can be automatically generated -// with the cube texture -class Texture; -class SphericalHarmonics { -public: +// THe spherical harmonics is a nice tool for cubemap, so if required, the irradiance SH can be automatically generated +// with the cube texture +class Texture; +class SphericalHarmonics { +public: glm::vec3 L00 ; float spare0; glm::vec3 L1m1 ; float spare1; glm::vec3 L10 ; float spare2; @@ -44,15 +44,15 @@ public: VINE_STREET_KITCHEN, BREEZEWAY, CAMPUS_SUNSET, - FUNSTON_BEACH_SUNSET, - - NUM_PRESET, + FUNSTON_BEACH_SUNSET, + + NUM_PRESET, }; void assignPreset(int p); void evalFromTexture(const Texture& texture); -}; +}; typedef std::shared_ptr< SphericalHarmonics > SHPointer; class Sampler { @@ -438,7 +438,7 @@ public: explicit operator bool() const { return bool(_texture); } bool operator !() const { return (!_texture); } }; -typedef std::vector TextureViews; +typedef std::vector TextureViews; }; diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 8a9ee4bf6d..00c9a13387 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -62,8 +62,8 @@ void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) { state->setCullMode(gpu::State::CULL_BACK); state->setDepthTest(true, true, gpu::LESS_EQUAL); state->setBlendFunction(false, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); _simpleProgram = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); _viewState = viewState; diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index f7280fc0ec..cf5a0d58da 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -815,9 +815,9 @@ void GeometryCache::renderSolidCube(gpu::Batch& batch, float size, const glm::ve const int VERTEX_STRIDE = sizeof(GLfloat) * FLOATS_PER_VERTEX * 2; // vertices and normals const int NORMALS_OFFSET = sizeof(GLfloat) * FLOATS_PER_VERTEX; - if (!_solidCubeVerticies.contains(size)) { + if (!_solidCubeVertices.contains(size)) { gpu::BufferPointer verticesBuffer(new gpu::Buffer()); - _solidCubeVerticies[size] = verticesBuffer; + _solidCubeVertices[size] = verticesBuffer; GLfloat* vertexData = new GLfloat[vertexPoints * 2]; // vertices and normals GLfloat* vertex = vertexData; @@ -892,7 +892,7 @@ void GeometryCache::renderSolidCube(gpu::Batch& batch, float size, const glm::ve colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); } - gpu::BufferPointer verticesBuffer = _solidCubeVerticies[size]; + gpu::BufferPointer verticesBuffer = _solidCubeVertices[size]; gpu::BufferPointer colorBuffer = _solidCubeColors[colorKey]; const int VERTICES_SLOT = 0; diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 9b21eab2d6..b438eb2d3b 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -270,7 +270,7 @@ private: QHash _cubeColors; gpu::BufferPointer _wireCubeIndexBuffer; - QHash _solidCubeVerticies; + QHash _solidCubeVertices; QHash _solidCubeColors; gpu::BufferPointer _solidCubeIndexBuffer; diff --git a/libraries/render-utils/src/RenderUtil.h b/libraries/render-utils/src/RenderUtil.h index cc823dc177..8c1b1e12e7 100644 --- a/libraries/render-utils/src/RenderUtil.h +++ b/libraries/render-utils/src/RenderUtil.h @@ -12,8 +12,6 @@ #ifndef hifi_RenderUtil_h #define hifi_RenderUtil_h -#include - /// Renders a quad from (-1, -1, 0) to (1, 1, 0) with texture coordinates from (sMin, tMin) to (sMax, tMax). void renderFullscreenQuad(float sMin = 0.0f, float sMax = 1.0f, float tMin = 0.0f, float tMax = 1.0f); diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp new file mode 100644 index 0000000000..d081c0480a --- /dev/null +++ b/libraries/render-utils/src/TextRenderer3D.cpp @@ -0,0 +1,498 @@ +// +// TextRenderer3D.cpp +// interface/src/ui +// +// Created by Andrzej Kapolka on 4/24/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 "TextRenderer3D.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include "GLMHelpers.h" +#include "MatrixStack.h" +#include "RenderUtilsLogging.h" + +#include "sdf_text3D_vert.h" +#include "sdf_text3D_frag.h" + +#include "GeometryCache.h" +#include "DeferredLightingEffect.h" + +// FIXME support the shadow effect, or remove it from the API +// FIXME figure out how to improve the anti-aliasing on the +// interior of the outline fonts +const float DEFAULT_POINT_SIZE = 12; + +// Helper functions for reading binary data from an IO device +template +void readStream(QIODevice& in, T& t) { + in.read((char*) &t, sizeof(t)); +} + +template +void readStream(QIODevice& in, T (&t)[N]) { + in.read((char*) t, N); +} + +template +void fillBuffer(QBuffer& buffer, T (&t)[N]) { + buffer.setData((const char*) t, N); +} + +// stores the font metrics for a single character +struct Glyph3D { + QChar c; + glm::vec2 texOffset; + glm::vec2 texSize; + glm::vec2 size; + glm::vec2 offset; + float d; // xadvance - adjusts character positioning + size_t indexOffset; + + // We adjust bounds because offset is the bottom left corner of the font but the top left corner of a QRect + QRectF bounds() const { return glmToRect(offset, size).translated(0.0f, -size.y); } + QRectF textureBounds() const { return glmToRect(texOffset, texSize); } + + void read(QIODevice& in); +}; + +void Glyph3D::read(QIODevice& in) { + uint16_t charcode; + readStream(in, charcode); + c = charcode; + readStream(in, texOffset); + readStream(in, size); + readStream(in, offset); + readStream(in, d); + texSize = size; +} + +struct TextureVertex { + glm::vec2 pos; + glm::vec2 tex; + TextureVertex() {} + TextureVertex(const glm::vec2& pos, const glm::vec2& tex) : pos(pos), tex(tex) {} +}; + +struct QuadBuilder { + TextureVertex vertices[4]; + QuadBuilder(const glm::vec2& min, const glm::vec2& size, + const glm::vec2& texMin, const glm::vec2& texSize) { + // min = bottomLeft + vertices[0] = TextureVertex(min, + texMin + glm::vec2(0.0f, texSize.y)); + vertices[1] = TextureVertex(min + glm::vec2(size.x, 0.0f), + texMin + texSize); + vertices[2] = TextureVertex(min + size, + texMin + glm::vec2(texSize.x, 0.0f)); + vertices[3] = TextureVertex(min + glm::vec2(0.0f, size.y), + texMin); + } + QuadBuilder(const Glyph3D& glyph, const glm::vec2& offset) : + QuadBuilder(offset + glyph.offset - glm::vec2(0.0f, glyph.size.y), glyph.size, + glyph.texOffset, glyph.texSize) {} + +}; + +class Font3D { +public: + Font3D(); + + void read(QIODevice& path); + + glm::vec2 computeExtent(const QString& str) const; + float getRowHeight() const { return _rowHeight; } + + // Render string to batch + void drawString(gpu::Batch& batch, float x, float y, const QString& str, + const glm::vec4& color, TextRenderer3D::EffectType effectType, + const glm::vec2& bound); + +private: + QStringList tokenizeForWrapping(const QString& str) const; + QStringList splitLines(const QString& str) const; + glm::vec2 computeTokenExtent(const QString& str) const; + + const Glyph3D& getGlyph(const QChar& c) const; + + void setupGPU(); + + // maps characters to cached glyph info + // HACK... the operator[] const for QHash returns a + // copy of the value, not a const value reference, so + // we declare the hash as mutable in order to avoid such + // copies + mutable QHash _glyphs; + + // Font characteristics + QString _family; + float _fontSize = 0.0f; + float _rowHeight = 0.0f; + float _leading = 0.0f; + float _ascent = 0.0f; + float _descent = 0.0f; + float _spaceWidth = 0.0f; + + bool _initialized = false; + + // gpu structures + gpu::PipelinePointer _pipeline; + gpu::TexturePointer _texture; + gpu::Stream::FormatPointer _format; + gpu::BufferPointer _verticesBuffer; + gpu::BufferStreamPointer _stream; + unsigned int _numVertices = 0; + + int _fontLoc = -1; + int _outlineLoc = -1; + int _colorLoc = -1; + + // last string render characteristics + QString _lastStringRendered; + glm::vec2 _lastBounds; +}; + +static QHash LOADED_FONTS; + +Font3D* loadFont3D(QIODevice& fontFile) { + Font3D* result = new Font3D(); + result->read(fontFile); + return result; +} + +Font3D* loadFont3D(const QString& family) { + if (!LOADED_FONTS.contains(family)) { + + const QString SDFF_COURIER_PRIME_FILENAME = ":/CourierPrime.sdff"; + const QString SDFF_INCONSOLATA_MEDIUM_FILENAME = ":/InconsolataMedium.sdff"; + const QString SDFF_ROBOTO_FILENAME = ":/Roboto.sdff"; + const QString SDFF_TIMELESS_FILENAME = ":/Timeless.sdff"; + + QString loadFilename; + + if (family == MONO_FONT_FAMILY) { + loadFilename = SDFF_COURIER_PRIME_FILENAME; + } else if (family == INCONSOLATA_FONT_FAMILY) { + loadFilename = SDFF_INCONSOLATA_MEDIUM_FILENAME; + } else if (family == SANS_FONT_FAMILY) { + loadFilename = SDFF_ROBOTO_FILENAME; + } else { + if (!LOADED_FONTS.contains(SERIF_FONT_FAMILY)) { + loadFilename = SDFF_TIMELESS_FILENAME; + } else { + LOADED_FONTS[family] = LOADED_FONTS[SERIF_FONT_FAMILY]; + } + } + + if (!loadFilename.isEmpty()) { + QFile fontFile(loadFilename); + fontFile.open(QIODevice::ReadOnly); + + qCDebug(renderutils) << "Loaded font" << loadFilename << "from Qt Resource System."; + + LOADED_FONTS[family] = loadFont3D(fontFile); + } + } + return LOADED_FONTS[family]; +} + +Font3D::Font3D() { + static bool fontResourceInitComplete = false; + if (!fontResourceInitComplete) { + Q_INIT_RESOURCE(fonts); + fontResourceInitComplete = true; + } +} + +// NERD RAGE: why doesn't QHash have a 'const T & operator[] const' member +const Glyph3D& Font3D::getGlyph(const QChar& c) const { + if (!_glyphs.contains(c)) { + return _glyphs[QChar('?')]; + } + return _glyphs[c]; +} + +QStringList Font3D::splitLines(const QString& str) const { + return str.split('\n'); +} + +QStringList Font3D::tokenizeForWrapping(const QString& str) const { + QStringList tokens; + for(auto line : splitLines(str)) { + if (!tokens.empty()) { + tokens << QString('\n'); + } + tokens << line.split(' '); + } + return tokens; +} + +glm::vec2 Font3D::computeTokenExtent(const QString& token) const { + glm::vec2 advance(0, _fontSize); + foreach(QChar c, token) { + Q_ASSERT(c != '\n'); + advance.x += (c == ' ') ? _spaceWidth : getGlyph(c).d; + } + return advance; +} + +glm::vec2 Font3D::computeExtent(const QString& str) const { + glm::vec2 extent = glm::vec2(0.0f, 0.0f); + + QStringList tokens = splitLines(str); + foreach(const QString& token, tokens) { + glm::vec2 tokenExtent = computeTokenExtent(token); + extent.x = std::max(tokenExtent.x, extent.x); + } + extent.y = tokens.count() * _rowHeight; + + return extent; +} + +void Font3D::read(QIODevice& in) { + uint8_t header[4]; + readStream(in, header); + if (memcmp(header, "SDFF", 4)) { + qFatal("Bad SDFF file"); + } + + uint16_t version; + readStream(in, version); + + // read font name + _family = ""; + if (version > 0x0001) { + char c; + readStream(in, c); + while (c) { + _family += c; + readStream(in, c); + } + } + + // read font data + readStream(in, _leading); + readStream(in, _ascent); + readStream(in, _descent); + readStream(in, _spaceWidth); + _fontSize = _ascent + _descent; + _rowHeight = _fontSize + _leading; + + // Read character count + uint16_t count; + readStream(in, count); + // read metrics data for each character + QVector glyphs(count); + // std::for_each instead of Qt foreach because we need non-const references + std::for_each(glyphs.begin(), glyphs.end(), [&](Glyph3D& g) { + g.read(in); + }); + + // read image data + QImage image; + if (!image.loadFromData(in.readAll(), "PNG")) { + qFatal("Failed to read SDFF image"); + } + + _glyphs.clear(); + glm::vec2 imageSize = toGlm(image.size()); + foreach(Glyph3D g, glyphs) { + // Adjust the pixel texture coordinates into UV coordinates, + g.texSize /= imageSize; + g.texOffset /= imageSize; + // store in the character to glyph hash + _glyphs[g.c] = g; + }; + + image = image.convertToFormat(QImage::Format_RGBA8888); + + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB); + if (image.hasAlphaChannel()) { + formatGPU = gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA); + formatMip = gpu::Element(gpu::VEC4, gpu::UINT8, gpu::BGRA); + } + _texture = gpu::TexturePointer(gpu::Texture::create2D(formatGPU, image.width(), image.height(), + gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR))); + _texture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); + _texture->autoGenerateMips(-1); +} + +void Font3D::setupGPU() { + if (!_initialized) { + _initialized = true; + + // Setup render pipeline + auto vertexShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(sdf_text3D_vert))); + auto pixelShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(sdf_text3D_frag))); + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vertexShader, pixelShader)); + + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + + _fontLoc = program->getTextures().findLocation("Font"); + _outlineLoc = program->getUniforms().findLocation("Outline"); + _colorLoc = program->getUniforms().findLocation("Color"); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setCullMode(gpu::State::CULL_BACK); + state->setDepthTest(true, true, gpu::LESS_EQUAL); + state->setBlendFunction(false, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + _pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); + + // Sanity checks + static const int OFFSET = offsetof(TextureVertex, tex); + assert(OFFSET == sizeof(glm::vec2)); + assert(sizeof(glm::vec2) == 2 * sizeof(float)); + assert(sizeof(TextureVertex) == 2 * sizeof(glm::vec2)); + assert(sizeof(QuadBuilder) == 4 * sizeof(TextureVertex)); + + // Setup rendering structures + _format.reset(new gpu::Stream::Format()); + _format->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ), 0); + _format->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), OFFSET); + } +} + +void Font3D::drawString(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color, + TextRenderer3D::EffectType effectType, const glm::vec2& bounds) { + if (str == "") { + return; + } + + if (str != _lastStringRendered || bounds != _lastBounds) { + _verticesBuffer.reset(new gpu::Buffer()); + _numVertices = 0; + _lastStringRendered = str; + _lastBounds = bounds; + + // Top left of text + glm::vec2 advance = glm::vec2(x, y); + foreach(const QString& token, tokenizeForWrapping(str)) { + bool isNewLine = (token == QString('\n')); + bool forceNewLine = false; + + // Handle wrapping + if (!isNewLine && (bounds.x != -1) && (advance.x + computeExtent(token).x > x + bounds.x)) { + // We are out of the x bound, force new line + forceNewLine = true; + } + if (isNewLine || forceNewLine) { + // Character return, move the advance to a new line + advance = glm::vec2(x, advance.y - _rowHeight); + + if (isNewLine) { + // No need to draw anything, go directly to next token + continue; + } else if (computeExtent(token).x > bounds.x) { + // token will never fit, stop drawing + break; + } + } + if ((bounds.y != -1) && (advance.y - _fontSize < -y - bounds.y)) { + // We are out of the y bound, stop drawing + break; + } + + // Draw the token + if (!isNewLine) { + for (auto c : token) { + auto glyph = _glyphs[c]; + + QuadBuilder qd(glyph, advance - glm::vec2(0.0f, _fontSize)); + _verticesBuffer->append(sizeof(QuadBuilder), (const gpu::Byte*)&qd); + _numVertices += 4; + + // Advance by glyph size + advance.x += glyph.d; + } + + // Add space after all non return tokens + advance.x += _spaceWidth; + } + } + } + + setupGPU(); + batch.setPipeline(_pipeline); + batch.setUniformTexture(_fontLoc, _texture); + batch._glUniform1f(_outlineLoc, (effectType == TextRenderer3D::OUTLINE_EFFECT) ? 1.0f : 0.0f); + batch._glUniform4fv(_colorLoc, 1, (const GLfloat*)&color); + + batch.setInputFormat(_format); + batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride); + batch.draw(gpu::QUADS, _numVertices, 0); +} + +TextRenderer3D* TextRenderer3D::getInstance(const char* family, float pointSize, + int weight, bool italic, EffectType effect, int effectThickness, + const QColor& color) { + if (pointSize < 0) { + pointSize = DEFAULT_POINT_SIZE; + } + return new TextRenderer3D(family, pointSize, weight, italic, effect, + effectThickness, color); +} + +TextRenderer3D::TextRenderer3D(const char* family, float pointSize, int weight, bool italic, + EffectType effect, int effectThickness, const QColor& color) : + _effectType(effect), + _effectThickness(effectThickness), + _pointSize(pointSize), + _color(toGlm(color)), + _font(loadFont3D(family)) { + if (!_font) { + qWarning() << "Unable to load font with family " << family; + _font = loadFont3D("Courier"); + } + if (1 != _effectThickness) { + qWarning() << "Effect thickness not current supported"; + } + if (NO_EFFECT != _effectType && OUTLINE_EFFECT != _effectType) { + qWarning() << "Effect thickness not current supported"; + } +} + +TextRenderer3D::~TextRenderer3D() { +} + +glm::vec2 TextRenderer3D::computeExtent(const QString& str) const { + if (_font) { + return _font->computeExtent(str); + } + return glm::vec2(0.0f, 0.0f); +} + +float TextRenderer3D::getRowHeight() const { + if (_font) { + return _font->getRowHeight(); + } + return 0.0f; +} + +void TextRenderer3D::draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color, + const glm::vec2& bounds) { + // The font does all the OpenGL work + if (_font) { + glm::vec4 actualColor(color); + if (actualColor.r < 0) { + actualColor = _color; + } + _font->drawString(batch, x, y, str, actualColor, _effectType, bounds); + } +} + diff --git a/libraries/render-utils/src/TextRenderer3D.h b/libraries/render-utils/src/TextRenderer3D.h new file mode 100644 index 0000000000..8f55d0c977 --- /dev/null +++ b/libraries/render-utils/src/TextRenderer3D.h @@ -0,0 +1,77 @@ +// +// TextRenderer3D.h +// interface/src/ui +// +// Created by Andrzej Kapolka on 4/26/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 +// + +#ifndef hifi_TextRenderer3D_h +#define hifi_TextRenderer3D_h + +#include +#include + +// the standard sans serif font family +#define SANS_FONT_FAMILY "Helvetica" + +// the standard sans serif font family +#define SERIF_FONT_FAMILY "Timeless" + +// the standard mono font family +#define MONO_FONT_FAMILY "Courier" + +// the Inconsolata font family +#ifdef Q_OS_WIN +#define INCONSOLATA_FONT_FAMILY "Fixedsys" +#define INCONSOLATA_FONT_WEIGHT QFont::Normal +#else +#define INCONSOLATA_FONT_FAMILY "Inconsolata" +#define INCONSOLATA_FONT_WEIGHT QFont::Bold +#endif + +namespace gpu { +class Batch; +} +class Font3D; + +// TextRenderer3D is actually a fairly thin wrapper around a Font class +// defined in the cpp file. +class TextRenderer3D { +public: + enum EffectType { NO_EFFECT, SHADOW_EFFECT, OUTLINE_EFFECT }; + + static TextRenderer3D* getInstance(const char* family, float pointSize = -1, int weight = -1, bool italic = false, + EffectType effect = NO_EFFECT, int effectThickness = 1, const QColor& color = QColor(255, 255, 255)); + + ~TextRenderer3D(); + + glm::vec2 computeExtent(const QString& str) const; + float getRowHeight() const; + + void draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color = glm::vec4(-1.0f), + const glm::vec2& bounds = glm::vec2(-1.0f)); + +private: + TextRenderer3D(const char* family, float pointSize = -1, int weight = -1, bool italic = false, + EffectType effect = NO_EFFECT, int effectThickness = 1, const QColor& color = QColor(255, 255, 255)); + + // the type of effect to apply + const EffectType _effectType; + + // the thickness of the effect + const int _effectThickness; + + const float _pointSize; + + // text color + const glm::vec4 _color; + + Font3D* _font; +}; + + +#endif // hifi_TextRenderer3D_h diff --git a/libraries/render-utils/src/TextureCache.cpp b/libraries/render-utils/src/TextureCache.cpp index 6facd99ff0..97385cb060 100644 --- a/libraries/render-utils/src/TextureCache.cpp +++ b/libraries/render-utils/src/TextureCache.cpp @@ -296,7 +296,7 @@ GLuint TextureCache::getShadowDepthTextureID() { } /// Returns a texture version of an image file -gpu::TexturePointer TextureCache::getImageTexture(const QString & path) { +gpu::TexturePointer TextureCache::getImageTexture(const QString& path) { QImage image = QImage(path).mirrored(false, true); gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB); gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB); @@ -383,9 +383,9 @@ ImageReader::ImageReader(const QWeakPointer& texture, TextureType type _content(content) { } -std::once_flag onceListSuppoertedFormatsflag; +std::once_flag onceListSupportedFormatsflag; void listSupportedImageFormats() { - std::call_once(onceListSuppoertedFormatsflag, [](){ + std::call_once(onceListSupportedFormatsflag, [](){ auto supportedFormats = QImageReader::supportedImageFormats(); QString formats; foreach(const QByteArray& f, supportedFormats) { diff --git a/libraries/render-utils/src/TextureCache.h b/libraries/render-utils/src/TextureCache.h index 603ab3a807..ba7176b2a4 100644 --- a/libraries/render-utils/src/TextureCache.h +++ b/libraries/render-utils/src/TextureCache.h @@ -56,7 +56,7 @@ public: const gpu::TexturePointer& getBlueTexture(); /// Returns a texture version of an image file - gpu::TexturePointer getImageTexture(const QString & path); + static gpu::TexturePointer getImageTexture(const QString& path); /// Loads a texture from the specified URL. NetworkTexturePointer getTexture(const QUrl& url, TextureType type = DEFAULT_TEXTURE, bool dilatable = false, diff --git a/libraries/render-utils/src/sdf_text3D.slf b/libraries/render-utils/src/sdf_text3D.slf new file mode 100644 index 0000000000..3980045d08 --- /dev/null +++ b/libraries/render-utils/src/sdf_text3D.slf @@ -0,0 +1,47 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// sdf_text.frag +// fragment shader +// +// Created by Bradley Austin Davis on 2015-02-04 +// Based on fragment shader code from +// https://github.com/paulhoux/Cinder-Samples/blob/master/TextRendering/include/text/Text.cpp +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +uniform sampler2D Font; +uniform float Outline; +uniform vec4 Color; + +const float gamma = 2.6; +const float smoothing = 100.0; +const float interiorCutoff = 0.8; +const float outlineExpansion = 0.2; + +void main() { + // retrieve signed distance + float sdf = texture2D(Font, gl_TexCoord[0].xy).g; + if (Outline == 1.0f) { + if (sdf > interiorCutoff) { + sdf = 1.0 - sdf; + } else { + sdf += outlineExpansion; + } + } + // perform adaptive anti-aliasing of the edges + // The larger we're rendering, the less anti-aliasing we need + float s = smoothing * length(fwidth(gl_TexCoord[0])); + float w = clamp( s, 0.0, 0.5); + float a = smoothstep(0.5 - w, 0.5 + w, sdf); + + // gamma correction for linear attenuation + a = pow(a, 1.0 / gamma); + + if (a < 0.01) { + discard; + } + + // final color + gl_FragColor = vec4(Color.rgb, a); +} \ No newline at end of file diff --git a/libraries/render-utils/src/sdf_text3D.slv b/libraries/render-utils/src/sdf_text3D.slv new file mode 100644 index 0000000000..f7c35a257c --- /dev/null +++ b/libraries/render-utils/src/sdf_text3D.slv @@ -0,0 +1,23 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// sdf_text.vert +// vertex shader +// +// Created by Brad Davis on 10/14/13. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +<@include gpu/Transform.slh@> + +<$declareStandardTransform()$> + +void main() { + gl_TexCoord[0] = gl_MultiTexCoord0; + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$> +} \ No newline at end of file diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index bbee0d3cc8..d4ac31ecb4 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -16,77 +16,14 @@ #include "gpu/Batch.h" #include "gpu/Context.h" +#include "ViewFrustum.h" +#include "RenderArgs.h" + +#include +#include using namespace render; -Job::~Job() { -} - -template <> void render::jobRun(const FilterItems& filterItems, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - auto& scene = sceneContext->_scene; - -} - -template <> void render::jobRun(const RenderItems& renderItems, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - auto& scene = sceneContext->_scene; - RenderArgs* args = renderContext->args; - // render - for (auto id : renderItems._items) { - auto item = scene->getItem(id); - item.render(args); - } -} - - - - -template <> void render::jobRun(const DrawOpaque& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - // render opaques - auto& scene = sceneContext->_scene; - auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape()); - - RenderArgs* args = renderContext->args; - gpu::Batch theBatch; - args->_batch = &theBatch; - for (auto id : items) { - auto item = scene->getItem(id); - item.render(args); - } - - args->_context->enqueueBatch((*args->_batch)); - args->_batch = nullptr; -} - - -template <> void render::jobRun(const DrawTransparent& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - // render transparents - auto& scene = sceneContext->_scene; - auto& items = scene->getMasterBucket().at(ItemFilter::Builder::transparentShape()); - - RenderArgs* args = renderContext->args; - gpu::Batch theBatch; - args->_batch = &theBatch; - for (auto id : items) { - auto item = scene->getItem(id); - item.render(args); - } - - args->_context->enqueueBatch((*args->_batch)); - args->_batch = nullptr; -} - -template <> void render::jobRun(const DrawLight& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - // render lights - auto& scene = sceneContext->_scene; - auto& items = scene->getMasterBucket().at(ItemFilter::Builder::light()); - - RenderArgs* args = renderContext->args; - for (auto id : items) { - auto item = scene->getItem(id); - item.render(args); - } -} - DrawSceneTask::DrawSceneTask() : Task() { _jobs.push_back(Job(DrawOpaque())); @@ -104,8 +41,234 @@ void DrawSceneTask::run(const SceneContextPointer& sceneContext, const RenderCon return; } + + // Is it possible that we render without a viewFrustum ? + if (!(renderContext->args && renderContext->args->_viewFrustum)) { + return; + } + for (auto job : _jobs) { job.run(sceneContext, renderContext); } }; +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. + float octreeSizeScale = getOctreeSizeScale(); + int boundaryLevelAdjust = getBoundaryLevelAdjust(); + float maxScale = (float)TREE_SCALE; + float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / octreeToMeshRatio; + + if (_shouldRenderTableNeedsRebuilding) { + _shouldRenderTable.clear(); + + float SMALLEST_SCALE_IN_TABLE = 0.001f; // 1mm is plenty small + float scale = maxScale; + float visibleDistanceAtScale = visibleDistanceAtMaxScale; + + while (scale > SMALLEST_SCALE_IN_TABLE) { + scale /= 2.0f; + visibleDistanceAtScale /= 2.0f; + _shouldRenderTable[scale] = visibleDistanceAtScale; + } + _shouldRenderTableNeedsRebuilding = false; + } + + float closestScale = maxScale; + float visibleDistanceAtClosestScale = visibleDistanceAtMaxScale; + QMap::const_iterator lowerBound = _shouldRenderTable.lowerBound(largestDimension); + if (lowerBound != _shouldRenderTable.constEnd()) { + closestScale = lowerBound.key(); + visibleDistanceAtClosestScale = lowerBound.value(); + } + + if (closestScale < largestDimension) { + visibleDistanceAtClosestScale *= 2.0f; + } + + return (distanceToCamera <= visibleDistanceAtClosestScale); +}*/ + +void render::cullItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDs& inItems, ItemIDs& outItems) { + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + + auto& scene = sceneContext->_scene; + RenderArgs* args = renderContext->args; + + // Culling / LOD + for (auto id : inItems) { + auto item = scene->getItem(id); + auto bound = item.getBound(); + + if (bound.isNull()) { + outItems.push_back(id); // One more Item to render + args->_itemsRendered++; + continue; + } + + // TODO: some entity types (like lights) might want to be rendered even + // when they are outside of the view frustum... + + float distance = args->_viewFrustum->distanceToCamera(bound.calcCenter()); + + bool outOfView = args->_viewFrustum->boxInFrustum(bound) == ViewFrustum::OUTSIDE; + if (!outOfView) { + bool bigEnoughToRender = true; //_viewState->shouldRenderMesh(bound.getLargestDimension(), distance); + + if (bigEnoughToRender) { + outItems.push_back(id); // One more Item to render + args->_itemsRendered++; + } else { + args->_itemsTooSmall++; + } + } else { + args->_itemsOutOfView++; + } + } + +} + + +void render::depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemIDs& inItems, ItemIDs& outItems) { + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + + auto& scene = sceneContext->_scene; + RenderArgs* args = renderContext->args; + + // Allocate and simply copy + outItems.reserve(inItems.size()); + + + // Make a local dataset of the center distance and closest point distance + struct ItemBound { + float _centerDepth = 0.0f; + float _nearDepth = 0.0f; + float _farDepth = 0.0f; + ItemID _id = 0; + + ItemBound() {} + ItemBound(float centerDepth, float nearDepth, float farDepth, ItemID id) : _centerDepth(centerDepth), _nearDepth(nearDepth), _farDepth(farDepth), _id(id) {} + }; + std::vector itemBounds; + itemBounds.reserve(outItems.size()); + + for (auto id : inItems) { + auto item = scene->getItem(id); + auto bound = item.getBound(); + float distance = args->_viewFrustum->distanceToCamera(bound.calcCenter()); + + itemBounds.emplace_back(ItemBound(distance, distance, distance, id)); + } + + // sort against Z + if (frontToBack) { + struct FrontToBackSort { + bool operator() (ItemBound& left, ItemBound& right) { + return (left._centerDepth < right._centerDepth); + } + } frontToBackSort; + std::sort (itemBounds.begin(), itemBounds.end(), frontToBackSort); + } else { + struct BackToFrontSort { + bool operator() (ItemBound& left, ItemBound& right) { + return (left._centerDepth > right._centerDepth); + } + } backToFrontSort; + std::sort (itemBounds.begin(), itemBounds.end(), backToFrontSort); + } + + // FInally once sorted result to a list of itemID + for (auto& itemBound : itemBounds) { + outItems.emplace_back(itemBound._id); + } +} + +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)); + args->_batch = nullptr; +} \ No newline at end of file diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index 8556129b3f..78d4d1bd8b 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -57,11 +57,13 @@ protected: typedef std::vector Jobs; +void cullItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDs& inItems, ItemIDs& outITems); +void depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemIDs& inItems, ItemIDs& outITems); +void renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDs& inItems); class DrawOpaque { public: - Jobs _jobs; }; template <> void jobRun(const DrawOpaque& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); @@ -77,19 +79,6 @@ public: template <> void jobRun(const DrawLight& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); -class FilterItems { -public: - ItemIDs _items; -}; -template <> void jobRun(const FilterItems& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); - -class RenderItems { -public: - ItemIDs _items; -}; -template <> void jobRun(const RenderItems& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); - - class DrawSceneTask : public Task { public: @@ -99,6 +88,7 @@ public: Jobs _jobs; virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); + }; } diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index d653563dd1..e53e33b4e8 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -70,21 +70,21 @@ void Item::move() { } -void Scene::PendingChanges::resetItem(ItemID id, const PayloadPointer& payload) { +void PendingChanges::resetItem(ItemID id, const PayloadPointer& payload) { _resetItems.push_back(id); _resetPayloads.push_back(payload); } -void Scene::PendingChanges::removeItem(ItemID id) { +void PendingChanges::removeItem(ItemID id) { _removedItems.push_back(id); } -void Scene::PendingChanges::moveItem(ItemID id) { +void PendingChanges::moveItem(ItemID id) { _movedItems.push_back(id); } -void Scene::PendingChanges::merge(PendingChanges& changes) { +void PendingChanges::merge(PendingChanges& changes) { _resetItems.insert(_resetItems.end(), changes._resetItems.begin(), changes._resetItems.end()); _resetPayloads.insert(_resetPayloads.end(), changes._resetPayloads.begin(), changes._resetPayloads.end()); _removedItems.insert(_removedItems.end(), changes._removedItems.begin(), changes._removedItems.end()); @@ -108,7 +108,7 @@ void Scene::enqueuePendingChanges(const PendingChanges& pendingChanges) { _changeQueueMutex.unlock(); } -void consolidateChangeQueue(Scene::PendingChangesQueue& queue, Scene::PendingChanges& singleBatch) { +void consolidateChangeQueue(PendingChangesQueue& queue, PendingChanges& singleBatch) { while (!queue.empty()) { auto pendingChanges = queue.front(); singleBatch.merge(pendingChanges); diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index c1771db52f..c93d9685fb 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -305,6 +305,27 @@ public: class Engine; class Observer; +class PendingChanges { +public: + PendingChanges() {} + ~PendingChanges() {} + + void resetItem(ItemID id, const PayloadPointer& payload); + void removeItem(ItemID id); + void moveItem(ItemID id); + + void merge(PendingChanges& changes); + + Payloads _resetPayloads; + ItemIDs _resetItems; + ItemIDs _removedItems; + ItemIDs _movedItems; + +protected: +}; +typedef std::queue PendingChangesQueue; + + // Scene is a container for Items // Items are introduced, modified or erased in the scene through PendingChanges // Once per Frame, the PendingChanges are all flushed @@ -342,26 +363,6 @@ public: typedef std::shared_ptr< Observer > ObserverPointer; typedef std::vector< ObserverPointer > Observers; - class PendingChanges { - public: - PendingChanges() {} - ~PendingChanges() {} - - void resetItem(ItemID id, const PayloadPointer& payload); - void removeItem(ItemID id); - void moveItem(ItemID id); - - void merge(PendingChanges& changes); - - Payloads _resetPayloads; - ItemIDs _resetItems; - ItemIDs _removedItems; - ItemIDs _movedItems; - - protected: - }; - typedef std::queue PendingChangesQueue; - Scene(); ~Scene() {} diff --git a/tests/render-utils/src/main.cpp b/tests/render-utils/src/main.cpp index 0ba7416b28..87338e414b 100644 --- a/tests/render-utils/src/main.cpp +++ b/tests/render-utils/src/main.cpp @@ -9,7 +9,6 @@ // #include "TextRenderer.h" -#include "MatrixStack.h" #include #include