mirror of
https://github.com/lubosz/overte.git
synced 2025-08-07 17:41:12 +02:00
reinserting culling, lod and depth sort to the rendering
This commit is contained in:
commit
3f8bdc14c4
42 changed files with 1120 additions and 257 deletions
|
@ -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
|
// Make sure the WorldBox is in the scene
|
||||||
if (WorldBoxRenderData::_item == 0) {
|
if (WorldBoxRenderData::_item == 0) {
|
||||||
|
@ -3415,18 +3415,23 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
||||||
pendingChanges.resetItem(WorldBoxRenderData::_item, worldBoxRenderPayload);
|
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
|
// FOr now every frame pass the renderCOntext
|
||||||
render::RenderContext renderContext;
|
{
|
||||||
renderContext.args = renderArgs;
|
PerformanceTimer perfTimer("EngineRun");
|
||||||
_renderEngine->setRenderContext(renderContext);
|
render::RenderContext renderContext;
|
||||||
|
renderContext.args = renderArgs;
|
||||||
|
_renderEngine->setRenderContext(renderContext);
|
||||||
|
|
||||||
// Before the deferred pass, let's try to use the render engine
|
// Before the deferred pass, let's try to use the render engine
|
||||||
_renderEngine->run();
|
_renderEngine->run();
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
DependencyManager::get<DeferredLightingEffect>()->setAmbientLightMode(getRenderAmbientLight());
|
DependencyManager::get<DeferredLightingEffect>()->setAmbientLightMode(getRenderAmbientLight());
|
||||||
|
|
|
@ -40,13 +40,13 @@ void AudioToolBox::render(int x, int y, int padding, bool boxed) {
|
||||||
glEnable(GL_TEXTURE_2D);
|
glEnable(GL_TEXTURE_2D);
|
||||||
|
|
||||||
if (!_micTexture) {
|
if (!_micTexture) {
|
||||||
_micTexture = DependencyManager::get<TextureCache>()->getImageTexture(PathUtils::resourcesPath() + "images/mic.svg");
|
_micTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/mic.svg");
|
||||||
}
|
}
|
||||||
if (!_muteTexture) {
|
if (!_muteTexture) {
|
||||||
_muteTexture = DependencyManager::get<TextureCache>()->getImageTexture(PathUtils::resourcesPath() + "images/mic-mute.svg");
|
_muteTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/mic-mute.svg");
|
||||||
}
|
}
|
||||||
if (_boxTexture) {
|
if (_boxTexture) {
|
||||||
_boxTexture = DependencyManager::get<TextureCache>()->getImageTexture(PathUtils::resourcesPath() + "images/audio-box.svg");
|
_boxTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/audio-box.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto audioIO = DependencyManager::get<AudioClient>();
|
auto audioIO = DependencyManager::get<AudioClient>();
|
||||||
|
|
|
@ -67,7 +67,7 @@ void AvatarManager::init() {
|
||||||
auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload);
|
auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload);
|
||||||
static_cast<Avatar*>(_myAvatar.get())->_renderItemID = scene->allocateID();
|
static_cast<Avatar*>(_myAvatar.get())->_renderItemID = scene->allocateID();
|
||||||
|
|
||||||
render::Scene::PendingChanges pendingChanges;
|
render::PendingChanges pendingChanges;
|
||||||
pendingChanges.resetItem(static_cast<Avatar*>(_myAvatar.get())->_renderItemID, avatarPayloadPointer);
|
pendingChanges.resetItem(static_cast<Avatar*>(_myAvatar.get())->_renderItemID, avatarPayloadPointer);
|
||||||
|
|
||||||
scene->enqueuePendingChanges(pendingChanges);
|
scene->enqueuePendingChanges(pendingChanges);
|
||||||
|
@ -152,7 +152,7 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe
|
||||||
auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload);
|
auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload);
|
||||||
static_cast<Avatar*>(avatar.get())->_renderItemID = scene->allocateID();
|
static_cast<Avatar*>(avatar.get())->_renderItemID = scene->allocateID();
|
||||||
|
|
||||||
render::Scene::PendingChanges pendingChanges;
|
render::PendingChanges pendingChanges;
|
||||||
pendingChanges.resetItem(static_cast<Avatar*>(avatar.get())->_renderItemID, avatarPayloadPointer);
|
pendingChanges.resetItem(static_cast<Avatar*>(avatar.get())->_renderItemID, avatarPayloadPointer);
|
||||||
|
|
||||||
scene->enqueuePendingChanges(pendingChanges);
|
scene->enqueuePendingChanges(pendingChanges);
|
||||||
|
@ -186,7 +186,7 @@ void AvatarManager::removeAvatar(const QUuid& sessionUUID) {
|
||||||
}
|
}
|
||||||
|
|
||||||
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
|
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
|
||||||
render::Scene::PendingChanges pendingChanges;
|
render::PendingChanges pendingChanges;
|
||||||
pendingChanges.removeItem(avatar->_renderItemID);
|
pendingChanges.removeItem(avatar->_renderItemID);
|
||||||
scene->enqueuePendingChanges(pendingChanges);
|
scene->enqueuePendingChanges(pendingChanges);
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,10 +76,10 @@ void CameraToolBox::render(int x, int y, bool boxed) {
|
||||||
glEnable(GL_TEXTURE_2D);
|
glEnable(GL_TEXTURE_2D);
|
||||||
|
|
||||||
if (!_enabledTexture) {
|
if (!_enabledTexture) {
|
||||||
_enabledTexture = DependencyManager::get<TextureCache>()->getImageTexture(PathUtils::resourcesPath() + "images/face.svg");
|
_enabledTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/face.svg");
|
||||||
}
|
}
|
||||||
if (!_mutedTexture) {
|
if (!_mutedTexture) {
|
||||||
_mutedTexture = DependencyManager::get<TextureCache>()->getImageTexture(PathUtils::resourcesPath() + "images/face-mute.svg");
|
_mutedTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/face-mute.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
const int MUTE_ICON_SIZE = 24;
|
const int MUTE_ICON_SIZE = 24;
|
||||||
|
|
|
@ -423,8 +423,8 @@ void ApplicationOverlay::displayOverlayTextureStereo(Camera& whichCamera, float
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!_crosshairTexture) {
|
if (!_crosshairTexture) {
|
||||||
_crosshairTexture = DependencyManager::get<TextureCache>()->
|
_crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() +
|
||||||
getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png");
|
"images/sixense-reticle.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
//draw the mouse pointer
|
//draw the mouse pointer
|
||||||
|
@ -564,8 +564,7 @@ bool ApplicationOverlay::calculateRayUICollisionPoint(const glm::vec3& position,
|
||||||
void ApplicationOverlay::renderPointers() {
|
void ApplicationOverlay::renderPointers() {
|
||||||
//lazily load crosshair texture
|
//lazily load crosshair texture
|
||||||
if (_crosshairTexture == 0) {
|
if (_crosshairTexture == 0) {
|
||||||
_crosshairTexture = DependencyManager::get<TextureCache>()->
|
_crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png");
|
||||||
getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png");
|
|
||||||
}
|
}
|
||||||
glEnable(GL_TEXTURE_2D);
|
glEnable(GL_TEXTURE_2D);
|
||||||
|
|
||||||
|
|
|
@ -34,11 +34,10 @@ RearMirrorTools::RearMirrorTools(QRect& bounds) :
|
||||||
_windowed(false),
|
_windowed(false),
|
||||||
_fullScreen(false)
|
_fullScreen(false)
|
||||||
{
|
{
|
||||||
auto textureCache = DependencyManager::get<TextureCache>();
|
_closeTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/close.svg");
|
||||||
_closeTexture = textureCache->getImageTexture(PathUtils::resourcesPath() + "images/close.svg");
|
|
||||||
|
|
||||||
_zoomHeadTexture = textureCache->getImageTexture(PathUtils::resourcesPath() + "images/plus.svg");
|
_zoomHeadTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/plus.svg");
|
||||||
_zoomBodyTexture = textureCache->getImageTexture(PathUtils::resourcesPath() + "images/minus.svg");
|
_zoomBodyTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/minus.svg");
|
||||||
|
|
||||||
_shrinkIconRect = QRect(ICON_PADDING, ICON_PADDING, ICON_SIZE, ICON_SIZE);
|
_shrinkIconRect = QRect(ICON_PADDING, ICON_PADDING, ICON_SIZE, ICON_SIZE);
|
||||||
_closeIconRect = QRect(_bounds.left() + ICON_PADDING, _bounds.top() + ICON_PADDING, ICON_SIZE, ICON_SIZE);
|
_closeIconRect = QRect(_bounds.left() + ICON_PADDING, _bounds.top() + ICON_PADDING, ICON_SIZE, ICON_SIZE);
|
||||||
|
|
|
@ -24,6 +24,7 @@ TextOverlay::TextOverlay() :
|
||||||
_topMargin(DEFAULT_MARGIN),
|
_topMargin(DEFAULT_MARGIN),
|
||||||
_fontSize(DEFAULT_FONTSIZE)
|
_fontSize(DEFAULT_FONTSIZE)
|
||||||
{
|
{
|
||||||
|
_textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextOverlay::TextOverlay(const TextOverlay* textOverlay) :
|
TextOverlay::TextOverlay(const TextOverlay* textOverlay) :
|
||||||
|
@ -35,6 +36,7 @@ TextOverlay::TextOverlay(const TextOverlay* textOverlay) :
|
||||||
_topMargin(textOverlay->_topMargin),
|
_topMargin(textOverlay->_topMargin),
|
||||||
_fontSize(textOverlay->_fontSize)
|
_fontSize(textOverlay->_fontSize)
|
||||||
{
|
{
|
||||||
|
_textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextOverlay::~TextOverlay() {
|
TextOverlay::~TextOverlay() {
|
||||||
|
|
|
@ -60,7 +60,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
TextRenderer* _textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT);
|
TextRenderer* _textRenderer = nullptr;
|
||||||
|
|
||||||
QString _text;
|
QString _text;
|
||||||
xColor _backgroundColor;
|
xColor _backgroundColor;
|
||||||
|
|
|
@ -96,9 +96,13 @@ void EntityTreeRenderer::clear() {
|
||||||
OctreeRenderer::clear();
|
OctreeRenderer::clear();
|
||||||
_entityScripts.clear();
|
_entityScripts.clear();
|
||||||
|
|
||||||
// TODO/FIXME - this needs to be fixed... we need to clear all items out of the scene in this case.
|
auto scene = _viewState->getMain3DScene();
|
||||||
qDebug() << "EntityTreeRenderer::clear() need to clear the scene... ";
|
render::PendingChanges pendingChanges;
|
||||||
|
foreach(auto entity, _entitiesInScene) {
|
||||||
|
entity->removeFromScene(entity, scene, pendingChanges);
|
||||||
|
}
|
||||||
|
scene->enqueuePendingChanges(pendingChanges);
|
||||||
|
_entitiesInScene.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::init() {
|
void EntityTreeRenderer::init() {
|
||||||
|
@ -704,8 +708,8 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// hack for models. :(
|
// hack for models and other entities that don't yet play well with others. :(
|
||||||
if (entityItem->getType() == EntityTypes::Model) {
|
if (!entityItem->canRenderInScene()) {
|
||||||
// render entityItem
|
// render entityItem
|
||||||
AABox entityBox = entityItem->getAABox();
|
AABox entityBox = entityItem->getAABox();
|
||||||
|
|
||||||
|
@ -1063,12 +1067,13 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
|
||||||
_entityScripts.remove(entityID);
|
_entityScripts.remove(entityID);
|
||||||
|
|
||||||
// here's where we remove the entity payload from the scene
|
// here's where we remove the entity payload from the scene
|
||||||
|
auto entity = static_cast<EntityTree*>(_tree)->findEntityByID(entityID);
|
||||||
render::Scene::PendingChanges pendingChanges;
|
if (entity && _entitiesInScene.contains(entity)) {
|
||||||
if (_entityToSceneItems.contains(entityID)) {
|
render::PendingChanges pendingChanges;
|
||||||
render::ItemID renderItem = _entityToSceneItems[entityID];
|
auto scene = _viewState->getMain3DScene();
|
||||||
pendingChanges.removeItem(renderItem);
|
entity->removeFromScene(entity, scene, pendingChanges);
|
||||||
_viewState->getMain3DScene()->enqueuePendingChanges(pendingChanges);
|
scene->enqueuePendingChanges(pendingChanges);
|
||||||
|
_entitiesInScene.remove(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1076,19 +1081,15 @@ void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) {
|
||||||
checkAndCallPreload(entityID);
|
checkAndCallPreload(entityID);
|
||||||
|
|
||||||
// here's where we add the entity payload to the scene
|
// here's where we add the entity payload to the scene
|
||||||
|
auto entity = static_cast<EntityTree*>(_tree)->findEntityByID(entityID);
|
||||||
render::Scene::PendingChanges pendingChanges;
|
if (entity && entity->canRenderInScene()) {
|
||||||
render::ItemID renderItem = _viewState->getMain3DScene()->allocateID();
|
render::PendingChanges pendingChanges;
|
||||||
_entityToSceneItems[entityID] = renderItem;
|
auto scene = _viewState->getMain3DScene();
|
||||||
EntityItemPointer entity = static_cast<EntityTree*>(_tree)->findEntityByID(entityID);
|
if (entity->addToScene(entity, scene, pendingChanges)) {
|
||||||
|
_entitiesInScene.insert(entity);
|
||||||
auto renderData = RenderableEntityItem::Pointer(new RenderableEntityItem(entity));
|
}
|
||||||
auto renderPayload = render::PayloadPointer(new RenderableEntityItem::Payload(renderData));
|
scene->enqueuePendingChanges(pendingChanges);
|
||||||
|
}
|
||||||
pendingChanges.resetItem(renderItem, renderPayload);
|
|
||||||
|
|
||||||
_viewState->getMain3DScene()->enqueuePendingChanges(pendingChanges);
|
|
||||||
_viewState->getMain3DScene()->processPendingChangesQueue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::entitySciptChanging(const EntityItemID& entityID) {
|
void EntityTreeRenderer::entitySciptChanging(const EntityItemID& entityID) {
|
||||||
|
|
|
@ -187,7 +187,7 @@ private:
|
||||||
float _previousStageHour;
|
float _previousStageHour;
|
||||||
int _previousStageDay;
|
int _previousStageDay;
|
||||||
|
|
||||||
QHash<EntityItemID, render::ItemID> _entityToSceneItems;
|
QSet<EntityItemPointer> _entitiesInScene;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include <BoxEntityItem.h>
|
#include <BoxEntityItem.h>
|
||||||
#include "RenderableDebugableEntityItem.h"
|
#include "RenderableDebugableEntityItem.h"
|
||||||
|
#include "RenderableEntityItem.h"
|
||||||
|
|
||||||
class RenderableBoxEntityItem : public BoxEntityItem {
|
class RenderableBoxEntityItem : public BoxEntityItem {
|
||||||
public:
|
public:
|
||||||
|
@ -24,6 +25,8 @@ public:
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
virtual void render(RenderArgs* args);
|
virtual void render(RenderArgs* args);
|
||||||
|
|
||||||
|
SIMPLE_RENDERABLE()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include "RenderableEntityItem.h"
|
#include "RenderableEntityItem.h"
|
||||||
|
|
||||||
namespace render {
|
namespace render {
|
||||||
template <> const ItemKey payloadGetKey(const RenderableEntityItem::Pointer& payload) {
|
template <> const ItemKey payloadGetKey(const RenderableEntityItemProxy::Pointer& payload) {
|
||||||
if (payload && payload->entity) {
|
if (payload && payload->entity) {
|
||||||
if (payload->entity->getType() == EntityTypes::Light) {
|
if (payload->entity->getType() == EntityTypes::Light) {
|
||||||
return ItemKey::Builder::light();
|
return ItemKey::Builder::light();
|
||||||
|
@ -22,13 +22,13 @@ namespace render {
|
||||||
return ItemKey::Builder::opaqueShape();
|
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) {
|
if (payload && payload->entity) {
|
||||||
return payload->entity->getAABox();
|
return payload->entity->getAABox();
|
||||||
}
|
}
|
||||||
return render::Item::Bound();
|
return render::Item::Bound();
|
||||||
}
|
}
|
||||||
template <> void payloadRender(const RenderableEntityItem::Pointer& payload, RenderArgs* args) {
|
template <> void payloadRender(const RenderableEntityItemProxy::Pointer& payload, RenderArgs* args) {
|
||||||
if (args) {
|
if (args) {
|
||||||
args->_elementsTouched++;
|
args->_elementsTouched++;
|
||||||
if (payload && payload->entity) {
|
if (payload && payload->entity) {
|
||||||
|
|
|
@ -15,19 +15,53 @@
|
||||||
#include <render/Scene.h>
|
#include <render/Scene.h>
|
||||||
#include <EntityItem.h>
|
#include <EntityItem.h>
|
||||||
|
|
||||||
class RenderableEntityItem {
|
|
||||||
|
class RenderableEntityItemProxy {
|
||||||
public:
|
public:
|
||||||
RenderableEntityItem(EntityItemPointer entity) : entity(entity) { }
|
RenderableEntityItemProxy(EntityItemPointer entity) : entity(entity) { }
|
||||||
typedef render::Payload<RenderableEntityItem> Payload;
|
typedef render::Payload<RenderableEntityItemProxy> Payload;
|
||||||
typedef Payload::DataPointer Pointer;
|
typedef Payload::DataPointer Pointer;
|
||||||
|
|
||||||
EntityItemPointer entity;
|
EntityItemPointer entity;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace render {
|
namespace render {
|
||||||
template <> const ItemKey payloadGetKey(const RenderableEntityItem::Pointer& payload);
|
template <> const ItemKey payloadGetKey(const RenderableEntityItemProxy::Pointer& payload);
|
||||||
template <> const Item::Bound payloadGetBound(const RenderableEntityItem::Pointer& payload);
|
template <> const Item::Bound payloadGetBound(const RenderableEntityItemProxy::Pointer& payload);
|
||||||
template <> void payloadRender(const RenderableEntityItem::Pointer& payload, RenderArgs* args);
|
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<render::Scene> 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<render::Scene> 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<render::Scene> scene, render::PendingChanges& pendingChanges) { return _renderHelper.addToScene(self, scene, pendingChanges); } \
|
||||||
|
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) { _renderHelper.removeFromScene(self, scene, pendingChanges); } \
|
||||||
|
private: \
|
||||||
|
SimpleRenderableEntityItem _renderHelper;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // hifi_RenderableEntityItem_h
|
#endif // hifi_RenderableEntityItem_h
|
||||||
|
|
|
@ -22,6 +22,7 @@ public:
|
||||||
LightEntityItem(entityItemID, properties)
|
LightEntityItem(entityItemID, properties)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
virtual bool canRenderInScene() { return false; } // we don't yet play well with others
|
||||||
virtual void render(RenderArgs* args);
|
virtual void render(RenderArgs* args);
|
||||||
virtual bool supportsDetailedRayIntersection() const { return true; }
|
virtual bool supportsDetailedRayIntersection() const { return true; }
|
||||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include <LineEntityItem.h>
|
#include <LineEntityItem.h>
|
||||||
#include "RenderableDebugableEntityItem.h"
|
#include "RenderableDebugableEntityItem.h"
|
||||||
|
#include "RenderableEntityItem.h"
|
||||||
|
|
||||||
class RenderableLineEntityItem : public LineEntityItem {
|
class RenderableLineEntityItem : public LineEntityItem {
|
||||||
public:
|
public:
|
||||||
|
@ -23,6 +24,8 @@ public:
|
||||||
LineEntityItem(entityItemID, properties) { }
|
LineEntityItem(entityItemID, properties) { }
|
||||||
|
|
||||||
virtual void render(RenderArgs* args);
|
virtual void render(RenderArgs* args);
|
||||||
|
|
||||||
|
SIMPLE_RENDERABLE()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ public:
|
||||||
|
|
||||||
virtual void somethingChangedNotification() { _needsInitialSimulation = true; }
|
virtual void somethingChangedNotification() { _needsInitialSimulation = true; }
|
||||||
|
|
||||||
|
virtual bool canRenderInScene() { return false; } // we don't yet play well with others
|
||||||
virtual void render(RenderArgs* args);
|
virtual void render(RenderArgs* args);
|
||||||
virtual bool supportsDetailedRayIntersection() const { return true; }
|
virtual bool supportsDetailedRayIntersection() const { return true; }
|
||||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#include <ParticleEffectEntityItem.h>
|
#include <ParticleEffectEntityItem.h>
|
||||||
#include <TextureCache.h>
|
#include <TextureCache.h>
|
||||||
|
#include "RenderableEntityItem.h"
|
||||||
|
|
||||||
class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem {
|
class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem {
|
||||||
public:
|
public:
|
||||||
|
@ -22,6 +23,8 @@ public:
|
||||||
|
|
||||||
void updateQuads(RenderArgs* args, bool textured);
|
void updateQuads(RenderArgs* args, bool textured);
|
||||||
|
|
||||||
|
SIMPLE_RENDERABLE()
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
int _cacheID;
|
int _cacheID;
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
#include <SphereEntityItem.h>
|
#include <SphereEntityItem.h>
|
||||||
|
|
||||||
|
#include "RenderableEntityItem.h"
|
||||||
|
|
||||||
class RenderableSphereEntityItem : public SphereEntityItem {
|
class RenderableSphereEntityItem : public SphereEntityItem {
|
||||||
public:
|
public:
|
||||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||||
|
@ -23,6 +25,8 @@ public:
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
virtual void render(RenderArgs* args);
|
virtual void render(RenderArgs* args);
|
||||||
|
|
||||||
|
SIMPLE_RENDERABLE()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,52 +20,41 @@
|
||||||
#include "RenderableTextEntityItem.h"
|
#include "RenderableTextEntityItem.h"
|
||||||
#include "GLMHelpers.h"
|
#include "GLMHelpers.h"
|
||||||
|
|
||||||
|
|
||||||
EntityItemPointer RenderableTextEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
EntityItemPointer RenderableTextEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||||
return EntityItemPointer(new RenderableTextEntityItem(entityID, properties));
|
return EntityItemPointer(new RenderableTextEntityItem(entityID, properties));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderableTextEntityItem::render(RenderArgs* args) {
|
void RenderableTextEntityItem::render(RenderArgs* args) {
|
||||||
PerformanceTimer perfTimer("RenderableTextEntityItem::render");
|
PerformanceTimer perfTimer("RenderableTextEntityItem::render");
|
||||||
assert(getType() == EntityTypes::Text);
|
Q_ASSERT(getType() == EntityTypes::Text);
|
||||||
glm::vec3 position = getPosition();
|
|
||||||
|
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 dimensions = getDimensions();
|
||||||
glm::vec3 halfDimensions = dimensions / 2.0f;
|
|
||||||
glm::quat rotation = getRotation();
|
Transform transformToTopLeft = getTransformToCenter();
|
||||||
float leftMargin = 0.1f;
|
transformToTopLeft.postTranslate(glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left
|
||||||
float topMargin = 0.1f;
|
transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed
|
||||||
|
|
||||||
//qCDebug(entitytree) << "RenderableTextEntityItem::render() id:" << getEntityItemID() << "text:" << getText();
|
// Batch render calls
|
||||||
|
Q_ASSERT(args->_batch);
|
||||||
glPushMatrix();
|
gpu::Batch& batch = *args->_batch;
|
||||||
{
|
batch.setModelTransform(transformToTopLeft);
|
||||||
glTranslatef(position.x, position.y, position.z);
|
|
||||||
glm::vec3 axis = glm::axis(rotation);
|
// Render background
|
||||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
glm::vec3 minCorner = glm::vec3(0.0f, -dimensions.y, SLIGHTLY_BEHIND);
|
||||||
|
glm::vec3 maxCorner = glm::vec3(dimensions.x, 0.0f, SLIGHTLY_BEHIND);
|
||||||
float alpha = 1.0f; //getBackgroundAlpha();
|
DependencyManager::get<DeferredLightingEffect>()->renderQuad(batch, minCorner, maxCorner, backgroundColor);
|
||||||
static const float SLIGHTLY_BEHIND = -0.005f;
|
|
||||||
|
float scale = _lineHeight / _textRenderer->getRowHeight();
|
||||||
glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND);
|
transformToTopLeft.setScale(scale); // Scale to have the correct line height
|
||||||
glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND);
|
batch.setModelTransform(transformToTopLeft);
|
||||||
|
|
||||||
// TODO: Determine if we want these entities to have the deferred lighting effect? I think we do, so that the color
|
float leftMargin = 0.5f * _lineHeight, topMargin = 0.5f * _lineHeight;
|
||||||
// used for a sphere, or box have the same look as those used on a text entity.
|
glm::vec2 bounds = glm::vec2(dimensions.x - 2.0f * leftMargin, dimensions.y - 2.0f * topMargin);
|
||||||
//DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram();
|
_textRenderer->draw(batch, leftMargin / scale, -topMargin / scale, _text, textColor, bounds / scale);
|
||||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, glm::vec4(toGlm(getBackgroundColorX()), alpha));
|
|
||||||
//DependencyManager::get<DeferredLightingEffect>()->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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#define hifi_RenderableTextEntityItem_h
|
#define hifi_RenderableTextEntityItem_h
|
||||||
|
|
||||||
#include <TextEntityItem.h>
|
#include <TextEntityItem.h>
|
||||||
#include <TextRenderer.h>
|
#include <TextRenderer3D.h>
|
||||||
|
|
||||||
const int FIXED_FONT_POINT_SIZE = 40;
|
const int FIXED_FONT_POINT_SIZE = 40;
|
||||||
|
|
||||||
|
@ -27,9 +27,10 @@ public:
|
||||||
~RenderableTextEntityItem() { delete _textRenderer; }
|
~RenderableTextEntityItem() { delete _textRenderer; }
|
||||||
|
|
||||||
virtual void render(RenderArgs* args);
|
virtual void render(RenderArgs* args);
|
||||||
|
virtual bool canRenderInScene() { return false; } // we don't yet play well with others
|
||||||
|
|
||||||
private:
|
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ public:
|
||||||
|
|
||||||
virtual void render(RenderArgs* args);
|
virtual void render(RenderArgs* args);
|
||||||
virtual void setSourceUrl(const QString& value);
|
virtual void setSourceUrl(const QString& value);
|
||||||
|
virtual bool canRenderInScene() { return false; } // we don't yet play well with others
|
||||||
|
|
||||||
private:
|
private:
|
||||||
OffscreenQmlSurface* _webSurface{ nullptr };
|
OffscreenQmlSurface* _webSurface{ nullptr };
|
||||||
|
|
|
@ -14,3 +14,4 @@ target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${BULLET_INCLUDE_DIRS})
|
||||||
target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES})
|
target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES})
|
||||||
|
|
||||||
link_hifi_libraries(avatars shared octree gpu model fbx networking animation environment)
|
link_hifi_libraries(avatars shared octree gpu model fbx networking animation environment)
|
||||||
|
include_hifi_library_headers(render)
|
||||||
|
|
|
@ -1005,6 +1005,13 @@ void EntityItem::setTranformToCenter(const Transform& transform) {
|
||||||
setTransform(copy);
|
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.
|
/// The maximum bounding cube for the entity, independent of it's rotation.
|
||||||
/// This accounts for the registration point (upon which rotation occurs around).
|
/// This accounts for the registration point (upon which rotation occurs around).
|
||||||
///
|
///
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#ifndef hifi_EntityItem_h
|
#ifndef hifi_EntityItem_h
|
||||||
#define hifi_EntityItem_h
|
#define hifi_EntityItem_h
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
@ -33,6 +34,11 @@ class EntitySimulation;
|
||||||
class EntityTreeElement;
|
class EntityTreeElement;
|
||||||
class EntityTreeElementExtraEncodeData;
|
class EntityTreeElementExtraEncodeData;
|
||||||
|
|
||||||
|
namespace render {
|
||||||
|
class Scene;
|
||||||
|
class PendingChanges;
|
||||||
|
}
|
||||||
|
|
||||||
// these thesholds determine what updates will be ignored (client and server)
|
// these thesholds determine what updates will be ignored (client and server)
|
||||||
const float IGNORE_POSITION_DELTA = 0.0001f;
|
const float IGNORE_POSITION_DELTA = 0.0001f;
|
||||||
const float IGNORE_DIMENSIONS_DELTA = 0.0005f;
|
const float IGNORE_DIMENSIONS_DELTA = 0.0005f;
|
||||||
|
@ -151,6 +157,11 @@ public:
|
||||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData)
|
EntityPropertyFlags& propertyFlags, bool overwriteLocalData)
|
||||||
{ return 0; }
|
{ 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<render::Scene> scene,
|
||||||
|
render::PendingChanges& pendingChanges) { return false; } // by default entity items don't add to scene
|
||||||
|
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> 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
|
virtual void render(RenderArgs* args) { } // by default entity items don't know how to render
|
||||||
|
|
||||||
static int expectedBytes();
|
static int expectedBytes();
|
||||||
|
@ -195,7 +206,7 @@ public:
|
||||||
|
|
||||||
/// Dimensions in meters (0.0 - TREE_SCALE)
|
/// Dimensions in meters (0.0 - TREE_SCALE)
|
||||||
inline const glm::vec3& getDimensions() const { return _transform.getScale(); }
|
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; }
|
float getGlowLevel() const { return _glowLevel; }
|
||||||
|
|
|
@ -66,22 +66,23 @@ void GLBackend::updateInput() {
|
||||||
newActivation.set(attrib._slot);
|
newActivation.set(attrib._slot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manage Activation what was and what is expected now
|
// Manage Activation what was and what is expected now
|
||||||
for (unsigned int i = 0; i < newActivation.size(); i++) {
|
for (unsigned int i = 0; i < newActivation.size(); i++) {
|
||||||
bool newState = newActivation[i];
|
bool newState = newActivation[i];
|
||||||
if (newState != _input._attributeActivation[i]) {
|
if (newState != _input._attributeActivation[i]) {
|
||||||
#if defined(SUPPORT_LEGACY_OPENGL)
|
#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) {
|
if (newState) {
|
||||||
glEnableClientState(attributeSlotToClassicAttribName[i]);
|
glEnableClientState(attributeSlotToClassicAttribName[i]);
|
||||||
} else {
|
} else {
|
||||||
glDisableClientState(attributeSlotToClassicAttribName[i]);
|
glDisableClientState(attributeSlotToClassicAttribName[i]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
#else
|
|
||||||
{
|
|
||||||
#endif
|
|
||||||
if (newState) {
|
if (newState) {
|
||||||
glEnableVertexAttribArray(i);
|
glEnableVertexAttribArray(i);
|
||||||
} else {
|
} else {
|
||||||
|
@ -89,7 +90,7 @@ void GLBackend::updateInput() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(void) CHECK_GL_ERROR();
|
(void) CHECK_GL_ERROR();
|
||||||
|
|
||||||
_input._attributeActivation.flip(i);
|
_input._attributeActivation.flip(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,29 +124,30 @@ void GLBackend::updateInput() {
|
||||||
GLenum type = _elementTypeToGLType[attrib._element.getType()];
|
GLenum type = _elementTypeToGLType[attrib._element.getType()];
|
||||||
GLuint stride = strides[bufferNum];
|
GLuint stride = strides[bufferNum];
|
||||||
GLuint pointer = attrib._offset + offsets[bufferNum];
|
GLuint pointer = attrib._offset + offsets[bufferNum];
|
||||||
#if defined(SUPPORT_LEGACY_OPENGL)
|
#if defined(SUPPORT_LEGACY_OPENGL)
|
||||||
if (slot < NUM_CLASSIC_ATTRIBS) {
|
const bool useClientState = slot < NUM_CLASSIC_ATTRIBS;
|
||||||
|
#else
|
||||||
|
const bool useClientState = false;
|
||||||
|
#endif
|
||||||
|
if (useClientState) {
|
||||||
switch (slot) {
|
switch (slot) {
|
||||||
case Stream::POSITION:
|
case Stream::POSITION:
|
||||||
glVertexPointer(count, type, stride, reinterpret_cast<GLvoid*>(pointer));
|
glVertexPointer(count, type, stride, reinterpret_cast<GLvoid*>(pointer));
|
||||||
break;
|
break;
|
||||||
case Stream::NORMAL:
|
case Stream::NORMAL:
|
||||||
glNormalPointer(type, stride, reinterpret_cast<GLvoid*>(pointer));
|
glNormalPointer(type, stride, reinterpret_cast<GLvoid*>(pointer));
|
||||||
break;
|
break;
|
||||||
case Stream::COLOR:
|
case Stream::COLOR:
|
||||||
glColorPointer(count, type, stride, reinterpret_cast<GLvoid*>(pointer));
|
glColorPointer(count, type, stride, reinterpret_cast<GLvoid*>(pointer));
|
||||||
break;
|
break;
|
||||||
case Stream::TEXCOORD:
|
case Stream::TEXCOORD:
|
||||||
glTexCoordPointer(count, type, stride, reinterpret_cast<GLvoid*>(pointer));
|
glTexCoordPointer(count, type, stride, reinterpret_cast<GLvoid*>(pointer));
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
#else
|
|
||||||
{
|
|
||||||
#endif
|
|
||||||
GLboolean isNormalized = attrib._element.isNormalized();
|
GLboolean isNormalized = attrib._element.isNormalized();
|
||||||
glVertexAttribPointer(slot, count, type, isNormalized, stride,
|
glVertexAttribPointer(slot, count, type, isNormalized, stride,
|
||||||
reinterpret_cast<GLvoid*>(pointer));
|
reinterpret_cast<GLvoid*>(pointer));
|
||||||
}
|
}
|
||||||
(void) CHECK_GL_ERROR();
|
(void) CHECK_GL_ERROR();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,8 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "Stream.h"
|
#include "Stream.h"
|
||||||
|
|
||||||
#include <algorithm> //min max and more
|
#include <algorithm> //min max and more
|
||||||
|
|
||||||
using namespace gpu;
|
using namespace gpu;
|
||||||
|
|
||||||
|
|
|
@ -17,11 +17,11 @@
|
||||||
|
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
|
||||||
// THe spherical harmonics is a nice tool for cubemap, so if required, the irradiance SH can be automatically generated
|
// THe spherical harmonics is a nice tool for cubemap, so if required, the irradiance SH can be automatically generated
|
||||||
// with the cube texture
|
// with the cube texture
|
||||||
class Texture;
|
class Texture;
|
||||||
class SphericalHarmonics {
|
class SphericalHarmonics {
|
||||||
public:
|
public:
|
||||||
glm::vec3 L00 ; float spare0;
|
glm::vec3 L00 ; float spare0;
|
||||||
glm::vec3 L1m1 ; float spare1;
|
glm::vec3 L1m1 ; float spare1;
|
||||||
glm::vec3 L10 ; float spare2;
|
glm::vec3 L10 ; float spare2;
|
||||||
|
@ -44,15 +44,15 @@ public:
|
||||||
VINE_STREET_KITCHEN,
|
VINE_STREET_KITCHEN,
|
||||||
BREEZEWAY,
|
BREEZEWAY,
|
||||||
CAMPUS_SUNSET,
|
CAMPUS_SUNSET,
|
||||||
FUNSTON_BEACH_SUNSET,
|
FUNSTON_BEACH_SUNSET,
|
||||||
|
|
||||||
NUM_PRESET,
|
NUM_PRESET,
|
||||||
};
|
};
|
||||||
|
|
||||||
void assignPreset(int p);
|
void assignPreset(int p);
|
||||||
|
|
||||||
void evalFromTexture(const Texture& texture);
|
void evalFromTexture(const Texture& texture);
|
||||||
};
|
};
|
||||||
typedef std::shared_ptr< SphericalHarmonics > SHPointer;
|
typedef std::shared_ptr< SphericalHarmonics > SHPointer;
|
||||||
|
|
||||||
class Sampler {
|
class Sampler {
|
||||||
|
@ -438,7 +438,7 @@ public:
|
||||||
explicit operator bool() const { return bool(_texture); }
|
explicit operator bool() const { return bool(_texture); }
|
||||||
bool operator !() const { return (!_texture); }
|
bool operator !() const { return (!_texture); }
|
||||||
};
|
};
|
||||||
typedef std::vector<TextureView> TextureViews;
|
typedef std::vector<TextureView> TextureViews;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -62,8 +62,8 @@ void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) {
|
||||||
state->setCullMode(gpu::State::CULL_BACK);
|
state->setCullMode(gpu::State::CULL_BACK);
|
||||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||||
state->setBlendFunction(false,
|
state->setBlendFunction(false,
|
||||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
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::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||||
_simpleProgram = gpu::PipelinePointer(gpu::Pipeline::create(program, state));
|
_simpleProgram = gpu::PipelinePointer(gpu::Pipeline::create(program, state));
|
||||||
|
|
||||||
_viewState = viewState;
|
_viewState = viewState;
|
||||||
|
|
|
@ -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 VERTEX_STRIDE = sizeof(GLfloat) * FLOATS_PER_VERTEX * 2; // vertices and normals
|
||||||
const int NORMALS_OFFSET = sizeof(GLfloat) * FLOATS_PER_VERTEX;
|
const int NORMALS_OFFSET = sizeof(GLfloat) * FLOATS_PER_VERTEX;
|
||||||
|
|
||||||
if (!_solidCubeVerticies.contains(size)) {
|
if (!_solidCubeVertices.contains(size)) {
|
||||||
gpu::BufferPointer verticesBuffer(new gpu::Buffer());
|
gpu::BufferPointer verticesBuffer(new gpu::Buffer());
|
||||||
_solidCubeVerticies[size] = verticesBuffer;
|
_solidCubeVertices[size] = verticesBuffer;
|
||||||
|
|
||||||
GLfloat* vertexData = new GLfloat[vertexPoints * 2]; // vertices and normals
|
GLfloat* vertexData = new GLfloat[vertexPoints * 2]; // vertices and normals
|
||||||
GLfloat* vertex = vertexData;
|
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);
|
colorBuffer->append(sizeof(colors), (gpu::Byte*) colors);
|
||||||
}
|
}
|
||||||
gpu::BufferPointer verticesBuffer = _solidCubeVerticies[size];
|
gpu::BufferPointer verticesBuffer = _solidCubeVertices[size];
|
||||||
gpu::BufferPointer colorBuffer = _solidCubeColors[colorKey];
|
gpu::BufferPointer colorBuffer = _solidCubeColors[colorKey];
|
||||||
|
|
||||||
const int VERTICES_SLOT = 0;
|
const int VERTICES_SLOT = 0;
|
||||||
|
|
|
@ -270,7 +270,7 @@ private:
|
||||||
QHash<Vec2Pair, gpu::BufferPointer> _cubeColors;
|
QHash<Vec2Pair, gpu::BufferPointer> _cubeColors;
|
||||||
gpu::BufferPointer _wireCubeIndexBuffer;
|
gpu::BufferPointer _wireCubeIndexBuffer;
|
||||||
|
|
||||||
QHash<float, gpu::BufferPointer> _solidCubeVerticies;
|
QHash<float, gpu::BufferPointer> _solidCubeVertices;
|
||||||
QHash<Vec2Pair, gpu::BufferPointer> _solidCubeColors;
|
QHash<Vec2Pair, gpu::BufferPointer> _solidCubeColors;
|
||||||
gpu::BufferPointer _solidCubeIndexBuffer;
|
gpu::BufferPointer _solidCubeIndexBuffer;
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,6 @@
|
||||||
#ifndef hifi_RenderUtil_h
|
#ifndef hifi_RenderUtil_h
|
||||||
#define hifi_RenderUtil_h
|
#define hifi_RenderUtil_h
|
||||||
|
|
||||||
#include <MatrixStack.h>
|
|
||||||
|
|
||||||
/// Renders a quad from (-1, -1, 0) to (1, 1, 0) with texture coordinates from (sMin, tMin) to (sMax, tMax).
|
/// 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);
|
void renderFullscreenQuad(float sMin = 0.0f, float sMax = 1.0f, float tMin = 0.0f, float tMax = 1.0f);
|
||||||
|
|
||||||
|
|
498
libraries/render-utils/src/TextRenderer3D.cpp
Normal file
498
libraries/render-utils/src/TextRenderer3D.cpp
Normal file
|
@ -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 <gpu/GPUConfig.h>
|
||||||
|
#include <gpu/GLBackend.h>
|
||||||
|
#include <gpu/Shader.h>
|
||||||
|
|
||||||
|
#include <QBuffer>
|
||||||
|
#include <QImage>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
|
#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<class T>
|
||||||
|
void readStream(QIODevice& in, T& t) {
|
||||||
|
in.read((char*) &t, sizeof(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t N>
|
||||||
|
void readStream(QIODevice& in, T (&t)[N]) {
|
||||||
|
in.read((char*) t, N);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, size_t N>
|
||||||
|
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<QChar, Glyph3D> _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<QString, Font3D*> 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<Glyph3D> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
77
libraries/render-utils/src/TextRenderer3D.h
Normal file
77
libraries/render-utils/src/TextRenderer3D.h
Normal file
|
@ -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 <glm/glm.hpp>
|
||||||
|
#include <QColor>
|
||||||
|
|
||||||
|
// 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
|
|
@ -296,7 +296,7 @@ GLuint TextureCache::getShadowDepthTextureID() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a texture version of an image file
|
/// 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);
|
QImage image = QImage(path).mirrored(false, true);
|
||||||
gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB);
|
gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB);
|
||||||
gpu::Element formatMip = 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<Resource>& texture, TextureType type
|
||||||
_content(content) {
|
_content(content) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::once_flag onceListSuppoertedFormatsflag;
|
std::once_flag onceListSupportedFormatsflag;
|
||||||
void listSupportedImageFormats() {
|
void listSupportedImageFormats() {
|
||||||
std::call_once(onceListSuppoertedFormatsflag, [](){
|
std::call_once(onceListSupportedFormatsflag, [](){
|
||||||
auto supportedFormats = QImageReader::supportedImageFormats();
|
auto supportedFormats = QImageReader::supportedImageFormats();
|
||||||
QString formats;
|
QString formats;
|
||||||
foreach(const QByteArray& f, supportedFormats) {
|
foreach(const QByteArray& f, supportedFormats) {
|
||||||
|
|
|
@ -56,7 +56,7 @@ public:
|
||||||
const gpu::TexturePointer& getBlueTexture();
|
const gpu::TexturePointer& getBlueTexture();
|
||||||
|
|
||||||
/// Returns a texture version of an image file
|
/// 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.
|
/// Loads a texture from the specified URL.
|
||||||
NetworkTexturePointer getTexture(const QUrl& url, TextureType type = DEFAULT_TEXTURE, bool dilatable = false,
|
NetworkTexturePointer getTexture(const QUrl& url, TextureType type = DEFAULT_TEXTURE, bool dilatable = false,
|
||||||
|
|
47
libraries/render-utils/src/sdf_text3D.slf
Normal file
47
libraries/render-utils/src/sdf_text3D.slf
Normal file
|
@ -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);
|
||||||
|
}
|
23
libraries/render-utils/src/sdf_text3D.slv
Normal file
23
libraries/render-utils/src/sdf_text3D.slv
Normal file
|
@ -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)$>
|
||||||
|
}
|
|
@ -16,77 +16,14 @@
|
||||||
#include "gpu/Batch.h"
|
#include "gpu/Batch.h"
|
||||||
#include "gpu/Context.h"
|
#include "gpu/Context.h"
|
||||||
|
|
||||||
|
#include "ViewFrustum.h"
|
||||||
|
#include "RenderArgs.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
using namespace render;
|
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() {
|
DrawSceneTask::DrawSceneTask() : Task() {
|
||||||
|
|
||||||
_jobs.push_back(Job(DrawOpaque()));
|
_jobs.push_back(Job(DrawOpaque()));
|
||||||
|
@ -104,8 +41,234 @@ void DrawSceneTask::run(const SceneContextPointer& sceneContext, const RenderCon
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Is it possible that we render without a viewFrustum ?
|
||||||
|
if (!(renderContext->args && renderContext->args->_viewFrustum)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (auto job : _jobs) {
|
for (auto job : _jobs) {
|
||||||
job.run(sceneContext, renderContext);
|
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<float, float>::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<ItemBound> 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;
|
||||||
|
}
|
|
@ -57,11 +57,13 @@ protected:
|
||||||
|
|
||||||
typedef std::vector<Job> Jobs;
|
typedef std::vector<Job> 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 {
|
class DrawOpaque {
|
||||||
public:
|
public:
|
||||||
Jobs _jobs;
|
|
||||||
};
|
};
|
||||||
template <> void jobRun(const DrawOpaque& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
|
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);
|
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 {
|
class DrawSceneTask : public Task {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -99,6 +88,7 @@ public:
|
||||||
Jobs _jobs;
|
Jobs _jobs;
|
||||||
|
|
||||||
virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
|
virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
_resetItems.push_back(id);
|
||||||
_resetPayloads.push_back(payload);
|
_resetPayloads.push_back(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::PendingChanges::removeItem(ItemID id) {
|
void PendingChanges::removeItem(ItemID id) {
|
||||||
_removedItems.push_back(id);
|
_removedItems.push_back(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::PendingChanges::moveItem(ItemID id) {
|
void PendingChanges::moveItem(ItemID id) {
|
||||||
_movedItems.push_back(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());
|
_resetItems.insert(_resetItems.end(), changes._resetItems.begin(), changes._resetItems.end());
|
||||||
_resetPayloads.insert(_resetPayloads.end(), changes._resetPayloads.begin(), changes._resetPayloads.end());
|
_resetPayloads.insert(_resetPayloads.end(), changes._resetPayloads.begin(), changes._resetPayloads.end());
|
||||||
_removedItems.insert(_removedItems.end(), changes._removedItems.begin(), changes._removedItems.end());
|
_removedItems.insert(_removedItems.end(), changes._removedItems.begin(), changes._removedItems.end());
|
||||||
|
@ -108,7 +108,7 @@ void Scene::enqueuePendingChanges(const PendingChanges& pendingChanges) {
|
||||||
_changeQueueMutex.unlock();
|
_changeQueueMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void consolidateChangeQueue(Scene::PendingChangesQueue& queue, Scene::PendingChanges& singleBatch) {
|
void consolidateChangeQueue(PendingChangesQueue& queue, PendingChanges& singleBatch) {
|
||||||
while (!queue.empty()) {
|
while (!queue.empty()) {
|
||||||
auto pendingChanges = queue.front();
|
auto pendingChanges = queue.front();
|
||||||
singleBatch.merge(pendingChanges);
|
singleBatch.merge(pendingChanges);
|
||||||
|
|
|
@ -305,6 +305,27 @@ public:
|
||||||
class Engine;
|
class Engine;
|
||||||
class Observer;
|
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<PendingChanges> PendingChangesQueue;
|
||||||
|
|
||||||
|
|
||||||
// Scene is a container for Items
|
// Scene is a container for Items
|
||||||
// Items are introduced, modified or erased in the scene through PendingChanges
|
// Items are introduced, modified or erased in the scene through PendingChanges
|
||||||
// Once per Frame, the PendingChanges are all flushed
|
// Once per Frame, the PendingChanges are all flushed
|
||||||
|
@ -342,26 +363,6 @@ public:
|
||||||
typedef std::shared_ptr< Observer > ObserverPointer;
|
typedef std::shared_ptr< Observer > ObserverPointer;
|
||||||
typedef std::vector< ObserverPointer > Observers;
|
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<PendingChanges> PendingChangesQueue;
|
|
||||||
|
|
||||||
Scene();
|
Scene();
|
||||||
~Scene() {}
|
~Scene() {}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "TextRenderer.h"
|
#include "TextRenderer.h"
|
||||||
#include "MatrixStack.h"
|
|
||||||
|
|
||||||
#include <QWindow>
|
#include <QWindow>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
|
Loading…
Reference in a new issue