mirror of
https://github.com/lubosz/overte.git
synced 2025-08-07 16:41:02 +02:00
commit
ecf67cc331
50 changed files with 2140 additions and 822 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -64,6 +64,7 @@ gvr-interface/libs/*
|
||||||
# ignore files for various dev environments
|
# ignore files for various dev environments
|
||||||
TAGS
|
TAGS
|
||||||
*.sw[po]
|
*.sw[po]
|
||||||
|
*.qmlc
|
||||||
|
|
||||||
# ignore node files for the console
|
# ignore node files for the console
|
||||||
node_modules
|
node_modules
|
||||||
|
|
|
@ -71,6 +71,12 @@ bool SelectionScriptingInterface::removeFromSelectedItemsList(const QString& lis
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SelectionScriptingInterface::clearSelectedItemsList(const QString& listName) {
|
||||||
|
_selectedItemsListMap.insert(listName, GameplayObjects());
|
||||||
|
emit selectedItemsListChanged(listName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
template <class T> bool SelectionScriptingInterface::addToGameplayObjects(const QString& listName, T idToAdd) {
|
template <class T> bool SelectionScriptingInterface::addToGameplayObjects(const QString& listName, T idToAdd) {
|
||||||
GameplayObjects currentList = _selectedItemsListMap.value(listName);
|
GameplayObjects currentList = _selectedItemsListMap.value(listName);
|
||||||
currentList.addToGameplayObjects(idToAdd);
|
currentList.addToGameplayObjects(idToAdd);
|
||||||
|
|
|
@ -56,11 +56,45 @@ public:
|
||||||
|
|
||||||
GameplayObjects getList(const QString& listName);
|
GameplayObjects getList(const QString& listName);
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Prints out the list of avatars, entities and overlays stored in a particular selection.
|
||||||
|
* @function Selection.printList
|
||||||
|
* @param listName {string} name of the selection
|
||||||
|
*/
|
||||||
Q_INVOKABLE void printList(const QString& listName);
|
Q_INVOKABLE void printList(const QString& listName);
|
||||||
|
/**jsdoc
|
||||||
|
* Removes a named selection from the list of selections.
|
||||||
|
* @function Selection.removeListFromMap
|
||||||
|
* @param listName {string} name of the selection
|
||||||
|
* @returns {bool} true if the selection existed and was successfully removed.
|
||||||
|
*/
|
||||||
Q_INVOKABLE bool removeListFromMap(const QString& listName);
|
Q_INVOKABLE bool removeListFromMap(const QString& listName);
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Add an item in a selection.
|
||||||
|
* @function Selection.addToSelectedItemsList
|
||||||
|
* @param listName {string} name of the selection
|
||||||
|
* @param itemType {string} the type of the item (one of "avatar", "entity" or "overlay")
|
||||||
|
* @param id {EntityID} the Id of the item to add to the selection
|
||||||
|
* @returns {bool} true if the item was successfully added.
|
||||||
|
*/
|
||||||
Q_INVOKABLE bool addToSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id);
|
Q_INVOKABLE bool addToSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id);
|
||||||
|
/**jsdoc
|
||||||
|
* Remove an item from a selection.
|
||||||
|
* @function Selection.removeFromSelectedItemsList
|
||||||
|
* @param listName {string} name of the selection
|
||||||
|
* @param itemType {string} the type of the item (one of "avatar", "entity" or "overlay")
|
||||||
|
* @param id {EntityID} the Id of the item to remove
|
||||||
|
* @returns {bool} true if the item was successfully removed.
|
||||||
|
*/
|
||||||
Q_INVOKABLE bool removeFromSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id);
|
Q_INVOKABLE bool removeFromSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id);
|
||||||
|
/**jsdoc
|
||||||
|
* Remove all items from a selection.
|
||||||
|
* @function Selection.clearSelectedItemsList
|
||||||
|
* @param listName {string} name of the selection
|
||||||
|
* @returns {bool} true if the item was successfully cleared.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE bool clearSelectedItemsList(const QString& listName);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void selectedItemsListChanged(const QString& listName);
|
void selectedItemsListChanged(const QString& listName);
|
||||||
|
|
|
@ -38,8 +38,6 @@ ContextOverlayInterface::ContextOverlayInterface() {
|
||||||
_tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
_tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||||
_selectionScriptingInterface = DependencyManager::get<SelectionScriptingInterface>();
|
_selectionScriptingInterface = DependencyManager::get<SelectionScriptingInterface>();
|
||||||
|
|
||||||
_selectionToSceneHandler.initialize("contextOverlayHighlightList");
|
|
||||||
|
|
||||||
_entityPropertyFlags += PROP_POSITION;
|
_entityPropertyFlags += PROP_POSITION;
|
||||||
_entityPropertyFlags += PROP_ROTATION;
|
_entityPropertyFlags += PROP_ROTATION;
|
||||||
_entityPropertyFlags += PROP_MARKETPLACE_ID;
|
_entityPropertyFlags += PROP_MARKETPLACE_ID;
|
||||||
|
@ -66,12 +64,20 @@ ContextOverlayInterface::ContextOverlayInterface() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
connect(entityScriptingInterface, &EntityScriptingInterface::deletingEntity, this, &ContextOverlayInterface::deletingEntity);
|
connect(entityScriptingInterface, &EntityScriptingInterface::deletingEntity, this, &ContextOverlayInterface::deletingEntity);
|
||||||
|
|
||||||
connect(&qApp->getOverlays(), &Overlays::mousePressOnOverlay, this, &ContextOverlayInterface::contextOverlays_mousePressOnOverlay);
|
connect(&qApp->getOverlays(), &Overlays::mousePressOnOverlay, this, &ContextOverlayInterface::contextOverlays_mousePressOnOverlay);
|
||||||
connect(&qApp->getOverlays(), &Overlays::hoverEnterOverlay, this, &ContextOverlayInterface::contextOverlays_hoverEnterOverlay);
|
connect(&qApp->getOverlays(), &Overlays::hoverEnterOverlay, this, &ContextOverlayInterface::contextOverlays_hoverEnterOverlay);
|
||||||
connect(&qApp->getOverlays(), &Overlays::hoverLeaveOverlay, this, &ContextOverlayInterface::contextOverlays_hoverLeaveOverlay);
|
connect(&qApp->getOverlays(), &Overlays::hoverLeaveOverlay, this, &ContextOverlayInterface::contextOverlays_hoverLeaveOverlay);
|
||||||
|
|
||||||
connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &_selectionToSceneHandler, &SelectionToSceneHandler::selectedItemsListChanged);
|
{
|
||||||
|
render::Transaction transaction;
|
||||||
|
initializeSelectionToSceneHandler(_selectionToSceneHandlers[0], "contextOverlayHighlightList", transaction);
|
||||||
|
for (auto i = 1; i < MAX_SELECTION_COUNT; i++) {
|
||||||
|
auto selectionName = QString("highlightList") + QString::number(i);
|
||||||
|
initializeSelectionToSceneHandler(_selectionToSceneHandlers[i], selectionName, transaction);
|
||||||
|
}
|
||||||
|
const render::ScenePointer& scene = qApp->getMain3DScene();
|
||||||
|
scene->enqueueTransaction(transaction);
|
||||||
|
}
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||||
|
@ -79,6 +85,12 @@ ContextOverlayInterface::ContextOverlayInterface() {
|
||||||
_challengeOwnershipTimeoutTimer.setSingleShot(true);
|
_challengeOwnershipTimeoutTimer.setSingleShot(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ContextOverlayInterface::initializeSelectionToSceneHandler(SelectionToSceneHandler& handler, const QString& selectionName, render::Transaction& transaction) {
|
||||||
|
handler.initialize(selectionName);
|
||||||
|
connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &handler, &SelectionToSceneHandler::selectedItemsListChanged);
|
||||||
|
transaction.resetSelectionHighlight(selectionName.toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
static const uint32_t MOUSE_HW_ID = 0;
|
static const uint32_t MOUSE_HW_ID = 0;
|
||||||
static const uint32_t LEFT_HAND_HW_ID = 1;
|
static const uint32_t LEFT_HAND_HW_ID = 1;
|
||||||
static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 };
|
static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 };
|
||||||
|
|
|
@ -47,6 +47,7 @@ class ContextOverlayInterface : public QObject, public Dependency {
|
||||||
OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID };
|
OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID };
|
||||||
std::shared_ptr<Image3DOverlay> _contextOverlay { nullptr };
|
std::shared_ptr<Image3DOverlay> _contextOverlay { nullptr };
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ContextOverlayInterface();
|
ContextOverlayInterface();
|
||||||
|
|
||||||
Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; }
|
Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; }
|
||||||
|
@ -75,6 +76,11 @@ private slots:
|
||||||
void handleChallengeOwnershipReplyPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
void handleChallengeOwnershipReplyPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MAX_SELECTION_COUNT = 16
|
||||||
|
};
|
||||||
|
|
||||||
bool _verboseLogging { true };
|
bool _verboseLogging { true };
|
||||||
bool _enabled { true };
|
bool _enabled { true };
|
||||||
EntityItemID _currentEntityWithContextOverlay{};
|
EntityItemID _currentEntityWithContextOverlay{};
|
||||||
|
@ -90,8 +96,9 @@ private:
|
||||||
void disableEntityHighlight(const EntityItemID& entityItemID);
|
void disableEntityHighlight(const EntityItemID& entityItemID);
|
||||||
|
|
||||||
void deletingEntity(const EntityItemID& entityItemID);
|
void deletingEntity(const EntityItemID& entityItemID);
|
||||||
|
void initializeSelectionToSceneHandler(SelectionToSceneHandler& handler, const QString& selectionName, render::Transaction& transaction);
|
||||||
|
|
||||||
SelectionToSceneHandler _selectionToSceneHandler;
|
SelectionToSceneHandler _selectionToSceneHandlers[MAX_SELECTION_COUNT];
|
||||||
|
|
||||||
Q_INVOKABLE void startChallengeOwnershipTimer();
|
Q_INVOKABLE void startChallengeOwnershipTimer();
|
||||||
QTimer _challengeOwnershipTimeoutTimer;
|
QTimer _challengeOwnershipTimeoutTimer;
|
||||||
|
|
|
@ -110,6 +110,7 @@ static const GLenum ELEMENT_TYPE_TO_GL[gpu::NUM_TYPES] = {
|
||||||
GL_SHORT,
|
GL_SHORT,
|
||||||
GL_UNSIGNED_SHORT,
|
GL_UNSIGNED_SHORT,
|
||||||
GL_BYTE,
|
GL_BYTE,
|
||||||
|
GL_UNSIGNED_BYTE,
|
||||||
GL_UNSIGNED_BYTE
|
GL_UNSIGNED_BYTE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -212,6 +212,9 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
|
||||||
case gpu::NUINT8:
|
case gpu::NUINT8:
|
||||||
result = GL_RGBA8;
|
result = GL_RGBA8;
|
||||||
break;
|
break;
|
||||||
|
case gpu::NUINT2:
|
||||||
|
result = GL_RGBA2;
|
||||||
|
break;
|
||||||
case gpu::NINT8:
|
case gpu::NINT8:
|
||||||
result = GL_RGBA8_SNORM;
|
result = GL_RGBA8_SNORM;
|
||||||
break;
|
break;
|
||||||
|
@ -498,6 +501,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case gpu::COMPRESSED:
|
case gpu::COMPRESSED:
|
||||||
|
case gpu::NUINT2:
|
||||||
case gpu::NUM_TYPES: { // quiet compiler
|
case gpu::NUM_TYPES: { // quiet compiler
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
@ -548,6 +552,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case gpu::COMPRESSED:
|
case gpu::COMPRESSED:
|
||||||
|
case gpu::NUINT2:
|
||||||
case gpu::NUM_TYPES: { // quiet compiler
|
case gpu::NUM_TYPES: { // quiet compiler
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
@ -660,6 +665,10 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
|
||||||
texel.format = GL_RGBA;
|
texel.format = GL_RGBA;
|
||||||
texel.internalFormat = GL_RGBA8_SNORM;
|
texel.internalFormat = GL_RGBA8_SNORM;
|
||||||
break;
|
break;
|
||||||
|
case gpu::NUINT2:
|
||||||
|
texel.format = GL_RGBA;
|
||||||
|
texel.internalFormat = GL_RGBA2;
|
||||||
|
break;
|
||||||
case gpu::NUINT32:
|
case gpu::NUINT32:
|
||||||
case gpu::NINT32:
|
case gpu::NINT32:
|
||||||
case gpu::COMPRESSED:
|
case gpu::COMPRESSED:
|
||||||
|
|
|
@ -19,6 +19,8 @@ const Element Element::COLOR_SRGBA_32{ VEC4, NUINT8, SRGBA };
|
||||||
const Element Element::COLOR_BGRA_32{ VEC4, NUINT8, BGRA };
|
const Element Element::COLOR_BGRA_32{ VEC4, NUINT8, BGRA };
|
||||||
const Element Element::COLOR_SBGRA_32{ VEC4, NUINT8, SBGRA };
|
const Element Element::COLOR_SBGRA_32{ VEC4, NUINT8, SBGRA };
|
||||||
|
|
||||||
|
const Element Element::COLOR_RGBA_2{ VEC4, NUINT2, RGBA };
|
||||||
|
|
||||||
const Element Element::COLOR_COMPRESSED_RED{ TILE4x4, COMPRESSED, COMPRESSED_BC4_RED };
|
const Element Element::COLOR_COMPRESSED_RED{ TILE4x4, COMPRESSED, COMPRESSED_BC4_RED };
|
||||||
const Element Element::COLOR_COMPRESSED_SRGB { TILE4x4, COMPRESSED, COMPRESSED_BC1_SRGB };
|
const Element Element::COLOR_COMPRESSED_SRGB { TILE4x4, COMPRESSED, COMPRESSED_BC1_SRGB };
|
||||||
const Element Element::COLOR_COMPRESSED_SRGBA_MASK { TILE4x4, COMPRESSED, COMPRESSED_BC1_SRGBA };
|
const Element Element::COLOR_COMPRESSED_SRGBA_MASK { TILE4x4, COMPRESSED, COMPRESSED_BC1_SRGBA };
|
||||||
|
|
|
@ -38,6 +38,7 @@ enum Type : uint8_t {
|
||||||
NUINT16,
|
NUINT16,
|
||||||
NINT8,
|
NINT8,
|
||||||
NUINT8,
|
NUINT8,
|
||||||
|
NUINT2,
|
||||||
|
|
||||||
COMPRESSED,
|
COMPRESSED,
|
||||||
|
|
||||||
|
@ -309,6 +310,7 @@ public:
|
||||||
static const Element COLOR_SRGBA_32;
|
static const Element COLOR_SRGBA_32;
|
||||||
static const Element COLOR_BGRA_32;
|
static const Element COLOR_BGRA_32;
|
||||||
static const Element COLOR_SBGRA_32;
|
static const Element COLOR_SBGRA_32;
|
||||||
|
static const Element COLOR_RGBA_2;
|
||||||
static const Element COLOR_R11G11B10;
|
static const Element COLOR_R11G11B10;
|
||||||
static const Element COLOR_RGB9E5;
|
static const Element COLOR_RGB9E5;
|
||||||
static const Element COLOR_COMPRESSED_RED;
|
static const Element COLOR_COMPRESSED_RED;
|
||||||
|
|
|
@ -220,7 +220,7 @@ uint32 Framebuffer::getRenderBufferSubresource(uint32 slot) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Framebuffer::setDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource) {
|
bool Framebuffer::assignDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource) {
|
||||||
if (isSwapchain()) {
|
if (isSwapchain()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -244,20 +244,59 @@ bool Framebuffer::setDepthStencilBuffer(const TexturePointer& texture, const For
|
||||||
// assign the new one
|
// assign the new one
|
||||||
_depthStencilBuffer = TextureView(texture, subresource, format);
|
_depthStencilBuffer = TextureView(texture, subresource, format);
|
||||||
|
|
||||||
_bufferMask = ( _bufferMask & ~BUFFER_DEPTHSTENCIL);
|
|
||||||
if (texture) {
|
|
||||||
if (format.getSemantic() == gpu::DEPTH) {
|
|
||||||
_bufferMask |= BUFFER_DEPTH;
|
|
||||||
} else if (format.getSemantic() == gpu::STENCIL) {
|
|
||||||
_bufferMask |= BUFFER_STENCIL;
|
|
||||||
} else if (format.getSemantic() == gpu::DEPTH_STENCIL) {
|
|
||||||
_bufferMask |= BUFFER_DEPTHSTENCIL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Framebuffer::setDepthBuffer(const TexturePointer& texture, const Format& format, uint32 subresource) {
|
||||||
|
if (assignDepthStencilBuffer(texture, format, subresource)) {
|
||||||
|
_bufferMask = (_bufferMask & ~BUFFER_DEPTHSTENCIL);
|
||||||
|
if (texture) {
|
||||||
|
if (format.getSemantic() == gpu::DEPTH || format.getSemantic() == gpu::DEPTH_STENCIL) {
|
||||||
|
_bufferMask |= BUFFER_DEPTH;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Framebuffer::setStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource) {
|
||||||
|
if (assignDepthStencilBuffer(texture, format, subresource)) {
|
||||||
|
_bufferMask = (_bufferMask & ~BUFFER_DEPTHSTENCIL);
|
||||||
|
if (texture) {
|
||||||
|
if (format.getSemantic() == gpu::STENCIL || format.getSemantic() == gpu::DEPTH_STENCIL) {
|
||||||
|
_bufferMask |= BUFFER_STENCIL;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Framebuffer::setDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource) {
|
||||||
|
if (assignDepthStencilBuffer(texture, format, subresource)) {
|
||||||
|
_bufferMask = (_bufferMask & ~BUFFER_DEPTHSTENCIL);
|
||||||
|
if (texture) {
|
||||||
|
if (format.getSemantic() == gpu::DEPTH) {
|
||||||
|
_bufferMask |= BUFFER_DEPTH;
|
||||||
|
} else if (format.getSemantic() == gpu::STENCIL) {
|
||||||
|
_bufferMask |= BUFFER_STENCIL;
|
||||||
|
} else if (format.getSemantic() == gpu::DEPTH_STENCIL) {
|
||||||
|
_bufferMask |= BUFFER_DEPTHSTENCIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
TexturePointer Framebuffer::getDepthStencilBuffer() const {
|
TexturePointer Framebuffer::getDepthStencilBuffer() const {
|
||||||
if (isSwapchain()) {
|
if (isSwapchain()) {
|
||||||
return TexturePointer();
|
return TexturePointer();
|
||||||
|
|
|
@ -107,6 +107,8 @@ public:
|
||||||
TexturePointer getRenderBuffer(uint32 slot) const;
|
TexturePointer getRenderBuffer(uint32 slot) const;
|
||||||
uint32 getRenderBufferSubresource(uint32 slot) const;
|
uint32 getRenderBufferSubresource(uint32 slot) const;
|
||||||
|
|
||||||
|
bool setDepthBuffer(const TexturePointer& texture, const Format& format, uint32 subresource = 0);
|
||||||
|
bool setStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource = 0);
|
||||||
bool setDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource = 0);
|
bool setDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource = 0);
|
||||||
TexturePointer getDepthStencilBuffer() const;
|
TexturePointer getDepthStencilBuffer() const;
|
||||||
uint32 getDepthStencilBufferSubresource() const;
|
uint32 getDepthStencilBufferSubresource() const;
|
||||||
|
@ -168,6 +170,7 @@ protected:
|
||||||
uint16 _numSamples = 0;
|
uint16 _numSamples = 0;
|
||||||
|
|
||||||
void updateSize(const TexturePointer& texture);
|
void updateSize(const TexturePointer& texture);
|
||||||
|
bool assignDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource);
|
||||||
|
|
||||||
// Non exposed
|
// Non exposed
|
||||||
Framebuffer(const Framebuffer& framebuffer) = delete;
|
Framebuffer(const Framebuffer& framebuffer) = delete;
|
||||||
|
|
|
@ -193,13 +193,17 @@ TransformObject getTransformObject() {
|
||||||
}
|
}
|
||||||
<@endfunc@>
|
<@endfunc@>
|
||||||
|
|
||||||
<@func transformModelToClipPos(cameraTransform, objectTransform, modelPos, clipPos)@>
|
<@func transformModelToMonoClipPos(cameraTransform, objectTransform, modelPos, clipPos)@>
|
||||||
{ // transformModelToClipPos
|
{ // transformModelToMonoClipPos
|
||||||
vec4 eyeWAPos;
|
vec4 eyeWAPos;
|
||||||
<$transformModelToEyeWorldAlignedPos($cameraTransform$, $objectTransform$, $modelPos$, eyeWAPos)$>
|
<$transformModelToEyeWorldAlignedPos($cameraTransform$, $objectTransform$, $modelPos$, eyeWAPos)$>
|
||||||
|
|
||||||
<$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * eyeWAPos;
|
<$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * eyeWAPos;
|
||||||
|
}
|
||||||
|
<@endfunc@>
|
||||||
|
|
||||||
|
<@func transformModelToClipPos(cameraTransform, objectTransform, modelPos, clipPos)@>
|
||||||
|
{ // transformModelToClipPos
|
||||||
|
<$transformModelToMonoClipPos($cameraTransform$, $objectTransform$, $modelPos$, $clipPos$)$>
|
||||||
<$transformStereoClipsSpace($cameraTransform$, $clipPos$)$>
|
<$transformStereoClipsSpace($cameraTransform$, $clipPos$)$>
|
||||||
}
|
}
|
||||||
<@endfunc@>
|
<@endfunc@>
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <gpu/Context.h>
|
#include <gpu/Context.h>
|
||||||
|
|
||||||
std::string BackgroundStage::_stageName { "BACKGROUND_STAGE"};
|
std::string BackgroundStage::_stageName { "BACKGROUND_STAGE"};
|
||||||
|
const BackgroundStage::Index BackgroundStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
|
||||||
|
|
||||||
BackgroundStage::Index BackgroundStage::findBackground(const BackgroundPointer& background) const {
|
BackgroundStage::Index BackgroundStage::findBackground(const BackgroundPointer& background) const {
|
||||||
auto found = _backgroundMap.find(background);
|
auto found = _backgroundMap.find(background);
|
||||||
|
|
|
@ -27,7 +27,7 @@ public:
|
||||||
static const std::string& getName() { return _stageName; }
|
static const std::string& getName() { return _stageName; }
|
||||||
|
|
||||||
using Index = render::indexed_container::Index;
|
using Index = render::indexed_container::Index;
|
||||||
static const Index INVALID_INDEX { render::indexed_container::INVALID_INDEX };
|
static const Index INVALID_INDEX;
|
||||||
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
|
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
|
||||||
|
|
||||||
using BackgroundPointer = model::SunSkyStagePointer;
|
using BackgroundPointer = model::SunSkyStagePointer;
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <gpu/Context.h>
|
#include <gpu/Context.h>
|
||||||
|
|
||||||
std::string HazeStage::_stageName { "HAZE_STAGE"};
|
std::string HazeStage::_stageName { "HAZE_STAGE"};
|
||||||
|
const HazeStage::Index HazeStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
|
||||||
|
|
||||||
FetchHazeStage::FetchHazeStage() {
|
FetchHazeStage::FetchHazeStage() {
|
||||||
_haze = std::make_shared<model::Haze>();
|
_haze = std::make_shared<model::Haze>();
|
||||||
|
|
|
@ -28,7 +28,7 @@ public:
|
||||||
static const std::string& getName() { return _stageName; }
|
static const std::string& getName() { return _stageName; }
|
||||||
|
|
||||||
using Index = render::indexed_container::Index;
|
using Index = render::indexed_container::Index;
|
||||||
static const Index INVALID_INDEX { render::indexed_container::INVALID_INDEX };
|
static const Index INVALID_INDEX;
|
||||||
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
|
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
|
||||||
|
|
||||||
using HazePointer = model::HazePointer;
|
using HazePointer = model::HazePointer;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Outline.slf
|
// Highlight.slf
|
||||||
// Add outline effect based on two zbuffers : one containing the total scene z and another
|
// Add highlight effect based on two zbuffers : one containing the total scene z and another
|
||||||
// with the z of only the objects to be outlined.
|
// with the z of only the objects to be outlined.
|
||||||
// This is the version without the fill effect inside the silhouette.
|
// This is the version without the fill effect inside the silhouette.
|
||||||
//
|
//
|
||||||
|
@ -9,5 +9,5 @@
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
<@include Outline.slh@>
|
<@include Highlight.slh@>
|
||||||
<$main(0)$>
|
<$main(0)$>
|
|
@ -1,7 +1,7 @@
|
||||||
<@include gpu/Config.slh@>
|
<@include gpu/Config.slh@>
|
||||||
<$VERSION_HEADER$>
|
<$VERSION_HEADER$>
|
||||||
<!
|
<!
|
||||||
// Outline.slh
|
// Highlight.slh
|
||||||
// fragment shader
|
// fragment shader
|
||||||
//
|
//
|
||||||
// Created by Olivier Prat on 9/7/17.
|
// Created by Olivier Prat on 9/7/17.
|
||||||
|
@ -13,14 +13,14 @@
|
||||||
<@include DeferredTransform.slh@>
|
<@include DeferredTransform.slh@>
|
||||||
<$declareDeferredFrameTransform()$>
|
<$declareDeferredFrameTransform()$>
|
||||||
|
|
||||||
<@include Outline_shared.slh@>
|
<@include Highlight_shared.slh@>
|
||||||
|
|
||||||
uniform outlineParamsBuffer {
|
uniform highlightParamsBuffer {
|
||||||
OutlineParameters params;
|
HighlightParameters params;
|
||||||
};
|
};
|
||||||
|
|
||||||
uniform sampler2D sceneDepthMap;
|
uniform sampler2D sceneDepthMap;
|
||||||
uniform sampler2D outlinedDepthMap;
|
uniform sampler2D highlightedDepthMap;
|
||||||
|
|
||||||
in vec2 varTexCoord0;
|
in vec2 varTexCoord0;
|
||||||
out vec4 outFragColor;
|
out vec4 outFragColor;
|
||||||
|
@ -35,30 +35,26 @@ void main(void) {
|
||||||
// We offset by half a texel to be centered on the depth sample. If we don't do this
|
// We offset by half a texel to be centered on the depth sample. If we don't do this
|
||||||
// the blur will have a different width between the left / right sides and top / bottom
|
// the blur will have a different width between the left / right sides and top / bottom
|
||||||
// sides of the silhouette
|
// sides of the silhouette
|
||||||
vec2 halfTexel = getInvWidthHeight() / 2;
|
float highlightedDepth = texture(highlightedDepthMap, varTexCoord0).x;
|
||||||
vec2 texCoord0 = varTexCoord0+halfTexel;
|
|
||||||
float outlinedDepth = texture(outlinedDepthMap, texCoord0).x;
|
|
||||||
float intensity = 0.0;
|
float intensity = 0.0;
|
||||||
|
|
||||||
if (outlinedDepth < FAR_Z) {
|
if (highlightedDepth < FAR_Z) {
|
||||||
// We're not on the far plane so we are on the outlined object, thus no outline to do!
|
// We're not on the far plane so we are on the highlighted object, thus no outline to do!
|
||||||
<@if IS_FILLED@>
|
<@if IS_FILLED@>
|
||||||
// But we need to fill the interior
|
// But we need to fill the interior
|
||||||
float sceneDepth = texture(sceneDepthMap, texCoord0).x;
|
float sceneDepth = texture(sceneDepthMap, varTexCoord0).x;
|
||||||
// Transform to linear depth for better precision
|
// Transform to linear depth for better precision
|
||||||
outlinedDepth = -evalZeyeFromZdb(outlinedDepth);
|
highlightedDepth = -evalZeyeFromZdb(highlightedDepth);
|
||||||
sceneDepth = -evalZeyeFromZdb(sceneDepth);
|
sceneDepth = -evalZeyeFromZdb(sceneDepth);
|
||||||
|
|
||||||
// Are we occluded?
|
// Are we occluded?
|
||||||
if (sceneDepth < (outlinedDepth-LINEAR_DEPTH_BIAS)) {
|
intensity = sceneDepth < (highlightedDepth-LINEAR_DEPTH_BIAS) ? params._occludedFillOpacity : params._unoccludedFillOpacity;
|
||||||
intensity = params._fillOpacityOccluded;
|
|
||||||
} else {
|
|
||||||
intensity = params._fillOpacityUnoccluded;
|
|
||||||
}
|
|
||||||
<@else@>
|
<@else@>
|
||||||
discard;
|
discard;
|
||||||
<@endif@>
|
<@endif@>
|
||||||
} else {
|
} else {
|
||||||
|
vec2 halfTexel = getInvWidthHeight() / 2;
|
||||||
|
vec2 texCoord0 = varTexCoord0+halfTexel;
|
||||||
float weight = 0.0;
|
float weight = 0.0;
|
||||||
vec2 deltaUv = params._size / params._blurKernelSize;
|
vec2 deltaUv = params._size / params._blurKernelSize;
|
||||||
vec2 lineStartUv = texCoord0 - params._size / 2.0;
|
vec2 lineStartUv = texCoord0 - params._size / 2.0;
|
||||||
|
@ -74,9 +70,9 @@ void main(void) {
|
||||||
for (x=0 ; x<params._blurKernelSize ; x++) {
|
for (x=0 ; x<params._blurKernelSize ; x++) {
|
||||||
if (uv.x>=0.0 && uv.x<=1.0)
|
if (uv.x>=0.0 && uv.x<=1.0)
|
||||||
{
|
{
|
||||||
outlinedDepth = texture(outlinedDepthMap, uv).x;
|
highlightedDepth = texture(highlightedDepthMap, uv).x;
|
||||||
intensity += (outlinedDepth < FAR_Z) ? 1.0 : 0.0;
|
intensity += (highlightedDepth < FAR_Z) ? 1.0 : 0.0;
|
||||||
weight += 1.f;
|
weight += 1.0;
|
||||||
}
|
}
|
||||||
uv.x += deltaUv.x;
|
uv.x += deltaUv.x;
|
||||||
}
|
}
|
562
libraries/render-utils/src/HighlightEffect.cpp
Normal file
562
libraries/render-utils/src/HighlightEffect.cpp
Normal file
|
@ -0,0 +1,562 @@
|
||||||
|
//
|
||||||
|
// HighlightEffect.cpp
|
||||||
|
// render-utils/src/
|
||||||
|
//
|
||||||
|
// Created by Olivier Prat on 08/08/17.
|
||||||
|
// Copyright 2017 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 "HighlightEffect.h"
|
||||||
|
|
||||||
|
#include "GeometryCache.h"
|
||||||
|
|
||||||
|
#include "CubeProjectedPolygon.h"
|
||||||
|
|
||||||
|
#include <render/FilterTask.h>
|
||||||
|
#include <render/SortTask.h>
|
||||||
|
|
||||||
|
#include "gpu/Context.h"
|
||||||
|
#include "gpu/StandardShaderLib.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "surfaceGeometry_copyDepth_frag.h"
|
||||||
|
#include "debug_deferred_buffer_vert.h"
|
||||||
|
#include "debug_deferred_buffer_frag.h"
|
||||||
|
#include "Highlight_frag.h"
|
||||||
|
#include "Highlight_filled_frag.h"
|
||||||
|
#include "Highlight_aabox_vert.h"
|
||||||
|
#include "nop_frag.h"
|
||||||
|
|
||||||
|
using namespace render;
|
||||||
|
|
||||||
|
#define OUTLINE_STENCIL_MASK 1
|
||||||
|
|
||||||
|
HighlightRessources::HighlightRessources() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void HighlightRessources::update(const gpu::FramebufferPointer& primaryFrameBuffer) {
|
||||||
|
auto newFrameSize = glm::ivec2(primaryFrameBuffer->getSize());
|
||||||
|
|
||||||
|
// If the buffer size changed, we need to delete our FBOs and recreate them at the
|
||||||
|
// new correct dimensions.
|
||||||
|
if (_frameSize != newFrameSize) {
|
||||||
|
_frameSize = newFrameSize;
|
||||||
|
allocateDepthBuffer(primaryFrameBuffer);
|
||||||
|
allocateColorBuffer(primaryFrameBuffer);
|
||||||
|
} else {
|
||||||
|
if (!_depthFrameBuffer) {
|
||||||
|
allocateDepthBuffer(primaryFrameBuffer);
|
||||||
|
}
|
||||||
|
if (!_colorFrameBuffer) {
|
||||||
|
allocateColorBuffer(primaryFrameBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HighlightRessources::allocateColorBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) {
|
||||||
|
_colorFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("primaryWithStencil"));
|
||||||
|
_colorFrameBuffer->setRenderBuffer(0, primaryFrameBuffer->getRenderBuffer(0));
|
||||||
|
_colorFrameBuffer->setStencilBuffer(_depthStencilTexture, _depthStencilTexture->getTexelFormat());
|
||||||
|
}
|
||||||
|
|
||||||
|
void HighlightRessources::allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) {
|
||||||
|
auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL);
|
||||||
|
_depthStencilTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, _frameSize.x, _frameSize.y));
|
||||||
|
_depthFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("highlightDepth"));
|
||||||
|
_depthFrameBuffer->setDepthStencilBuffer(_depthStencilTexture, depthFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
gpu::FramebufferPointer HighlightRessources::getDepthFramebuffer() {
|
||||||
|
assert(_depthFrameBuffer);
|
||||||
|
return _depthFrameBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpu::TexturePointer HighlightRessources::getDepthTexture() {
|
||||||
|
return _depthStencilTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpu::FramebufferPointer HighlightRessources::getColorFramebuffer() {
|
||||||
|
assert(_colorFrameBuffer);
|
||||||
|
return _colorFrameBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
HighlightSharedParameters::HighlightSharedParameters() {
|
||||||
|
_highlightIds.fill(render::HighlightStage::INVALID_INDEX);
|
||||||
|
}
|
||||||
|
|
||||||
|
float HighlightSharedParameters::getBlurPixelWidth(const render::HighlightStyle& style, int frameBufferHeight) {
|
||||||
|
return ceilf(style.outlineWidth * frameBufferHeight / 400.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
PrepareDrawHighlight::PrepareDrawHighlight() {
|
||||||
|
_ressources = std::make_shared<HighlightRessources>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrepareDrawHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
|
||||||
|
auto destinationFrameBuffer = inputs;
|
||||||
|
|
||||||
|
_ressources->update(destinationFrameBuffer);
|
||||||
|
outputs = _ressources;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpu::PipelinePointer DrawHighlightMask::_stencilMaskPipeline;
|
||||||
|
gpu::PipelinePointer DrawHighlightMask::_stencilMaskFillPipeline;
|
||||||
|
|
||||||
|
DrawHighlightMask::DrawHighlightMask(unsigned int highlightIndex,
|
||||||
|
render::ShapePlumberPointer shapePlumber, HighlightSharedParametersPointer parameters) :
|
||||||
|
_highlightPassIndex{ highlightIndex },
|
||||||
|
_shapePlumber { shapePlumber },
|
||||||
|
_sharedParameters{ parameters } {
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
|
||||||
|
assert(renderContext->args);
|
||||||
|
assert(renderContext->args->hasViewFrustum());
|
||||||
|
auto& inShapes = inputs.get0();
|
||||||
|
|
||||||
|
if (!_stencilMaskPipeline || !_stencilMaskFillPipeline) {
|
||||||
|
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||||
|
state->setDepthTest(true, false, gpu::LESS_EQUAL);
|
||||||
|
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_ZERO, gpu::State::STENCIL_OP_REPLACE));
|
||||||
|
state->setColorWriteMask(false, false, false, false);
|
||||||
|
state->setCullMode(gpu::State::CULL_FRONT);
|
||||||
|
|
||||||
|
gpu::StatePointer fillState = gpu::StatePointer(new gpu::State());
|
||||||
|
fillState->setDepthTest(false, false, gpu::LESS_EQUAL);
|
||||||
|
fillState->setStencilTest(true, 0xFF, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_REPLACE));
|
||||||
|
fillState->setColorWriteMask(false, false, false, false);
|
||||||
|
fillState->setCullMode(gpu::State::CULL_FRONT);
|
||||||
|
|
||||||
|
auto vs = gpu::Shader::createVertex(std::string(Highlight_aabox_vert));
|
||||||
|
auto ps = gpu::Shader::createPixel(std::string(nop_frag));
|
||||||
|
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||||
|
|
||||||
|
gpu::Shader::BindingSet slotBindings;
|
||||||
|
gpu::Shader::makeProgram(*program, slotBindings);
|
||||||
|
|
||||||
|
_stencilMaskPipeline = gpu::Pipeline::create(program, state);
|
||||||
|
_stencilMaskFillPipeline = gpu::Pipeline::create(program, fillState);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_boundsBuffer) {
|
||||||
|
_boundsBuffer = std::make_shared<gpu::Buffer>(sizeof(render::ItemBound));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto highlightStage = renderContext->_scene->getStage<render::HighlightStage>(render::HighlightStage::getName());
|
||||||
|
auto highlightId = _sharedParameters->_highlightIds[_highlightPassIndex];
|
||||||
|
|
||||||
|
if (!inShapes.empty() && !render::HighlightStage::isIndexInvalid(highlightId)) {
|
||||||
|
auto ressources = inputs.get1();
|
||||||
|
auto& highlight = highlightStage->getHighlight(highlightId);
|
||||||
|
|
||||||
|
RenderArgs* args = renderContext->args;
|
||||||
|
ShapeKey::Builder defaultKeyBuilder;
|
||||||
|
|
||||||
|
// Render full screen
|
||||||
|
outputs = args->_viewport;
|
||||||
|
|
||||||
|
// Clear the framebuffer without stereo
|
||||||
|
// Needs to be distinct from the other batch because using the clear call
|
||||||
|
// while stereo is enabled triggers a warning
|
||||||
|
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||||
|
batch.enableStereo(false);
|
||||||
|
batch.setFramebuffer(ressources->getDepthFramebuffer());
|
||||||
|
batch.clearDepthStencilFramebuffer(1.0f, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
glm::mat4 projMat;
|
||||||
|
Transform viewMat;
|
||||||
|
args->getViewFrustum().evalProjectionMatrix(projMat);
|
||||||
|
args->getViewFrustum().evalViewTransform(viewMat);
|
||||||
|
|
||||||
|
render::ItemBounds itemBounds;
|
||||||
|
|
||||||
|
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||||
|
args->_batch = &batch;
|
||||||
|
|
||||||
|
auto maskPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder);
|
||||||
|
auto maskSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned());
|
||||||
|
|
||||||
|
// Setup camera, projection and viewport for all items
|
||||||
|
batch.setViewportTransform(args->_viewport);
|
||||||
|
batch.setProjectionTransform(projMat);
|
||||||
|
batch.setViewTransform(viewMat);
|
||||||
|
|
||||||
|
std::vector<ShapeKey> skinnedShapeKeys{};
|
||||||
|
|
||||||
|
// Iterate through all inShapes and render the unskinned
|
||||||
|
args->_shapePipeline = maskPipeline;
|
||||||
|
batch.setPipeline(maskPipeline->pipeline);
|
||||||
|
for (const auto& items : inShapes) {
|
||||||
|
itemBounds.insert(itemBounds.end(), items.second.begin(), items.second.end());
|
||||||
|
if (items.first.isSkinned()) {
|
||||||
|
skinnedShapeKeys.push_back(items.first);
|
||||||
|
} else {
|
||||||
|
renderItems(renderContext, items.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reiterate to render the skinned
|
||||||
|
args->_shapePipeline = maskSkinnedPipeline;
|
||||||
|
batch.setPipeline(maskSkinnedPipeline->pipeline);
|
||||||
|
for (const auto& key : skinnedShapeKeys) {
|
||||||
|
renderItems(renderContext, inShapes.at(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
args->_shapePipeline = nullptr;
|
||||||
|
args->_batch = nullptr;
|
||||||
|
});
|
||||||
|
|
||||||
|
_boundsBuffer->setData(itemBounds.size() * sizeof(render::ItemBound), (const gpu::Byte*) itemBounds.data());
|
||||||
|
|
||||||
|
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||||
|
// Setup camera, projection and viewport for all items
|
||||||
|
batch.setViewportTransform(args->_viewport);
|
||||||
|
batch.setProjectionTransform(projMat);
|
||||||
|
batch.setViewTransform(viewMat);
|
||||||
|
|
||||||
|
// Draw stencil mask with object bounding boxes
|
||||||
|
const auto highlightWidthLoc = _stencilMaskPipeline->getProgram()->getUniforms().findLocation("outlineWidth");
|
||||||
|
const auto securityMargin = 2.0f;
|
||||||
|
const float blurPixelWidth = 2.0f * securityMargin * HighlightSharedParameters::getBlurPixelWidth(highlight._style, args->_viewport.w);
|
||||||
|
const auto framebufferSize = ressources->getSourceFrameSize();
|
||||||
|
|
||||||
|
auto stencilPipeline = highlight._style.isFilled() ? _stencilMaskFillPipeline : _stencilMaskPipeline;
|
||||||
|
batch.setPipeline(stencilPipeline);
|
||||||
|
batch.setResourceBuffer(0, _boundsBuffer);
|
||||||
|
batch._glUniform2f(highlightWidthLoc, blurPixelWidth / framebufferSize.x, blurPixelWidth / framebufferSize.y);
|
||||||
|
static const int NUM_VERTICES_PER_CUBE = 36;
|
||||||
|
batch.draw(gpu::TRIANGLES, NUM_VERTICES_PER_CUBE * (gpu::uint32) itemBounds.size(), 0);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Highlight rect should be null as there are no highlighted shapes
|
||||||
|
outputs = glm::ivec4(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gpu::PipelinePointer DrawHighlight::_pipeline;
|
||||||
|
gpu::PipelinePointer DrawHighlight::_pipelineFilled;
|
||||||
|
|
||||||
|
DrawHighlight::DrawHighlight(unsigned int highlightIndex, HighlightSharedParametersPointer parameters) :
|
||||||
|
_highlightPassIndex{ highlightIndex },
|
||||||
|
_sharedParameters{ parameters } {
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
|
||||||
|
auto highlightFrameBuffer = inputs.get1();
|
||||||
|
auto highlightRect = inputs.get3();
|
||||||
|
|
||||||
|
if (highlightFrameBuffer && highlightRect.z>0 && highlightRect.w>0) {
|
||||||
|
auto sceneDepthBuffer = inputs.get2();
|
||||||
|
const auto frameTransform = inputs.get0();
|
||||||
|
auto highlightedDepthTexture = highlightFrameBuffer->getDepthTexture();
|
||||||
|
auto destinationFrameBuffer = highlightFrameBuffer->getColorFramebuffer();
|
||||||
|
auto framebufferSize = glm::ivec2(highlightedDepthTexture->getDimensions());
|
||||||
|
|
||||||
|
if (sceneDepthBuffer) {
|
||||||
|
auto args = renderContext->args;
|
||||||
|
|
||||||
|
auto highlightStage = renderContext->_scene->getStage<render::HighlightStage>(render::HighlightStage::getName());
|
||||||
|
auto highlightId = _sharedParameters->_highlightIds[_highlightPassIndex];
|
||||||
|
if (!render::HighlightStage::isIndexInvalid(highlightId)) {
|
||||||
|
auto& highlight = highlightStage->getHighlight(highlightId);
|
||||||
|
auto pipeline = getPipeline(highlight._style);
|
||||||
|
{
|
||||||
|
auto& shaderParameters = _configuration.edit();
|
||||||
|
|
||||||
|
shaderParameters._color = highlight._style.color;
|
||||||
|
shaderParameters._intensity = highlight._style.outlineIntensity * (highlight._style.isOutlineSmooth ? 2.0f : 1.0f);
|
||||||
|
shaderParameters._unoccludedFillOpacity = highlight._style.unoccludedFillOpacity;
|
||||||
|
shaderParameters._occludedFillOpacity = highlight._style.occludedFillOpacity;
|
||||||
|
shaderParameters._threshold = highlight._style.isOutlineSmooth ? 1.0f : 1e-3f;
|
||||||
|
shaderParameters._blurKernelSize = std::min(7, std::max(2, (int)floorf(highlight._style.outlineWidth * 3 + 0.5f)));
|
||||||
|
// Size is in normalized screen height. We decide that for highlight width = 1, this is equal to 1/400.
|
||||||
|
auto size = highlight._style.outlineWidth / 400.0f;
|
||||||
|
shaderParameters._size.x = (size * framebufferSize.y) / framebufferSize.x;
|
||||||
|
shaderParameters._size.y = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||||
|
batch.enableStereo(false);
|
||||||
|
batch.setFramebuffer(destinationFrameBuffer);
|
||||||
|
|
||||||
|
batch.setViewportTransform(args->_viewport);
|
||||||
|
batch.setProjectionTransform(glm::mat4());
|
||||||
|
batch.resetViewTransform();
|
||||||
|
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport));
|
||||||
|
batch.setPipeline(pipeline);
|
||||||
|
|
||||||
|
batch.setUniformBuffer(HIGHLIGHT_PARAMS_SLOT, _configuration);
|
||||||
|
batch.setUniformBuffer(FRAME_TRANSFORM_SLOT, frameTransform->getFrameTransformBuffer());
|
||||||
|
batch.setResourceTexture(SCENE_DEPTH_MAP_SLOT, sceneDepthBuffer->getPrimaryDepthTexture());
|
||||||
|
batch.setResourceTexture(HIGHLIGHTED_DEPTH_MAP_SLOT, highlightedDepthTexture);
|
||||||
|
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const gpu::PipelinePointer& DrawHighlight::getPipeline(const render::HighlightStyle& style) {
|
||||||
|
if (!_pipeline) {
|
||||||
|
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||||
|
state->setDepthTest(gpu::State::DepthTest(false, false));
|
||||||
|
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||||
|
state->setStencilTest(true, 0, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::EQUAL));
|
||||||
|
|
||||||
|
auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS();
|
||||||
|
auto ps = gpu::Shader::createPixel(std::string(Highlight_frag));
|
||||||
|
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||||
|
|
||||||
|
gpu::Shader::BindingSet slotBindings;
|
||||||
|
slotBindings.insert(gpu::Shader::Binding("highlightParamsBuffer", HIGHLIGHT_PARAMS_SLOT));
|
||||||
|
slotBindings.insert(gpu::Shader::Binding("deferredFrameTransformBuffer", FRAME_TRANSFORM_SLOT));
|
||||||
|
slotBindings.insert(gpu::Shader::Binding("sceneDepthMap", SCENE_DEPTH_MAP_SLOT));
|
||||||
|
slotBindings.insert(gpu::Shader::Binding("highlightedDepthMap", HIGHLIGHTED_DEPTH_MAP_SLOT));
|
||||||
|
gpu::Shader::makeProgram(*program, slotBindings);
|
||||||
|
|
||||||
|
_pipeline = gpu::Pipeline::create(program, state);
|
||||||
|
|
||||||
|
ps = gpu::Shader::createPixel(std::string(Highlight_filled_frag));
|
||||||
|
program = gpu::Shader::createProgram(vs, ps);
|
||||||
|
gpu::Shader::makeProgram(*program, slotBindings);
|
||||||
|
_pipelineFilled = gpu::Pipeline::create(program, state);
|
||||||
|
}
|
||||||
|
return style.isFilled() ? _pipelineFilled : _pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugHighlight::DebugHighlight() {
|
||||||
|
_geometryDepthId = DependencyManager::get<GeometryCache>()->allocateID();
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugHighlight::~DebugHighlight() {
|
||||||
|
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||||
|
if (geometryCache) {
|
||||||
|
geometryCache->releaseID(_geometryDepthId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugHighlight::configure(const Config& config) {
|
||||||
|
_isDisplayEnabled = config.viewMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& input) {
|
||||||
|
const auto highlightRessources = input.get0();
|
||||||
|
const auto highlightRect = input.get1();
|
||||||
|
|
||||||
|
if (_isDisplayEnabled && highlightRessources && highlightRect.z>0 && highlightRect.w>0) {
|
||||||
|
assert(renderContext->args);
|
||||||
|
assert(renderContext->args->hasViewFrustum());
|
||||||
|
RenderArgs* args = renderContext->args;
|
||||||
|
|
||||||
|
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||||
|
batch.setViewportTransform(args->_viewport);
|
||||||
|
batch.setFramebuffer(highlightRessources->getColorFramebuffer());
|
||||||
|
|
||||||
|
const auto geometryBuffer = DependencyManager::get<GeometryCache>();
|
||||||
|
|
||||||
|
glm::mat4 projMat;
|
||||||
|
Transform viewMat;
|
||||||
|
args->getViewFrustum().evalProjectionMatrix(projMat);
|
||||||
|
args->getViewFrustum().evalViewTransform(viewMat);
|
||||||
|
batch.setProjectionTransform(projMat);
|
||||||
|
batch.setViewTransform(viewMat, true);
|
||||||
|
batch.setModelTransform(Transform());
|
||||||
|
|
||||||
|
const glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
|
||||||
|
batch.setPipeline(getDepthPipeline());
|
||||||
|
batch.setResourceTexture(0, highlightRessources->getDepthTexture());
|
||||||
|
const glm::vec2 bottomLeft(-1.0f, -1.0f);
|
||||||
|
const glm::vec2 topRight(1.0f, 1.0f);
|
||||||
|
geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryDepthId);
|
||||||
|
|
||||||
|
batch.setResourceTexture(0, nullptr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugHighlight::initializePipelines() {
|
||||||
|
static const std::string VERTEX_SHADER{ debug_deferred_buffer_vert };
|
||||||
|
static const std::string FRAGMENT_SHADER{ debug_deferred_buffer_frag };
|
||||||
|
static const std::string SOURCE_PLACEHOLDER{ "//SOURCE_PLACEHOLDER" };
|
||||||
|
static const auto SOURCE_PLACEHOLDER_INDEX = FRAGMENT_SHADER.find(SOURCE_PLACEHOLDER);
|
||||||
|
Q_ASSERT_X(SOURCE_PLACEHOLDER_INDEX != std::string::npos, Q_FUNC_INFO,
|
||||||
|
"Could not find source placeholder");
|
||||||
|
|
||||||
|
auto state = std::make_shared<gpu::State>();
|
||||||
|
state->setDepthTest(gpu::State::DepthTest(false, false));
|
||||||
|
state->setStencilTest(true, 0, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::EQUAL));
|
||||||
|
|
||||||
|
const auto vs = gpu::Shader::createVertex(VERTEX_SHADER);
|
||||||
|
|
||||||
|
// Depth shader
|
||||||
|
{
|
||||||
|
static const std::string DEPTH_SHADER{
|
||||||
|
"vec4 getFragmentColor() {"
|
||||||
|
" float Zdb = texelFetch(depthMap, ivec2(gl_FragCoord.xy), 0).x;"
|
||||||
|
" Zdb = 1.0-(1.0-Zdb)*100;"
|
||||||
|
" return vec4(Zdb, Zdb, Zdb, 1.0); "
|
||||||
|
"}"
|
||||||
|
};
|
||||||
|
|
||||||
|
auto fragmentShader = FRAGMENT_SHADER;
|
||||||
|
fragmentShader.replace(SOURCE_PLACEHOLDER_INDEX, SOURCE_PLACEHOLDER.size(), DEPTH_SHADER);
|
||||||
|
|
||||||
|
const auto ps = gpu::Shader::createPixel(fragmentShader);
|
||||||
|
const auto program = gpu::Shader::createProgram(vs, ps);
|
||||||
|
|
||||||
|
gpu::Shader::BindingSet slotBindings;
|
||||||
|
slotBindings.insert(gpu::Shader::Binding("depthMap", 0));
|
||||||
|
gpu::Shader::makeProgram(*program, slotBindings);
|
||||||
|
|
||||||
|
_depthPipeline = gpu::Pipeline::create(program, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const gpu::PipelinePointer& DebugHighlight::getDepthPipeline() {
|
||||||
|
if (!_depthPipeline) {
|
||||||
|
initializePipelines();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _depthPipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SelectionToHighlight::run(const render::RenderContextPointer& renderContext, Outputs& outputs) {
|
||||||
|
auto scene = renderContext->_scene;
|
||||||
|
auto highlightStage = scene->getStage<render::HighlightStage>(render::HighlightStage::getName());
|
||||||
|
|
||||||
|
outputs.clear();
|
||||||
|
_sharedParameters->_highlightIds.fill(render::HighlightStage::INVALID_INDEX);
|
||||||
|
|
||||||
|
for (auto i = 0; i < HighlightSharedParameters::MAX_PASS_COUNT; i++) {
|
||||||
|
std::ostringstream stream;
|
||||||
|
if (i > 0) {
|
||||||
|
stream << "highlightList" << i;
|
||||||
|
} else {
|
||||||
|
stream << "contextOverlayHighlightList";
|
||||||
|
}
|
||||||
|
auto selectionName = stream.str();
|
||||||
|
if (!scene->isSelectionEmpty(selectionName)) {
|
||||||
|
auto highlightId = highlightStage->getHighlightIdBySelection(selectionName);
|
||||||
|
if (!render::HighlightStage::isIndexInvalid(highlightId)) {
|
||||||
|
_sharedParameters->_highlightIds[outputs.size()] = highlightId;
|
||||||
|
outputs.emplace_back(selectionName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExtractSelectionName::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
|
||||||
|
if (_highlightPassIndex < inputs.size()) {
|
||||||
|
outputs = inputs[_highlightPassIndex];
|
||||||
|
} else {
|
||||||
|
outputs.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawHighlightTask::DrawHighlightTask() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawHighlightTask::configure(const Config& config) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) {
|
||||||
|
const auto items = inputs.getN<Inputs>(0).get<RenderFetchCullSortTask::BucketList>();
|
||||||
|
const auto sceneFrameBuffer = inputs.getN<Inputs>(1);
|
||||||
|
const auto primaryFramebuffer = inputs.getN<Inputs>(2);
|
||||||
|
const auto deferredFrameTransform = inputs.getN<Inputs>(3);
|
||||||
|
|
||||||
|
// Prepare the ShapePipeline
|
||||||
|
auto shapePlumber = std::make_shared<ShapePlumber>();
|
||||||
|
{
|
||||||
|
auto state = std::make_shared<gpu::State>();
|
||||||
|
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||||
|
state->setColorWriteMask(false, false, false, false);
|
||||||
|
|
||||||
|
initMaskPipelines(*shapePlumber, state);
|
||||||
|
}
|
||||||
|
auto sharedParameters = std::make_shared<HighlightSharedParameters>();
|
||||||
|
|
||||||
|
const auto highlightSelectionNames = task.addJob<SelectionToHighlight>("SelectionToHighlight", sharedParameters);
|
||||||
|
|
||||||
|
// Prepare for highlight group rendering.
|
||||||
|
const auto highlightRessources = task.addJob<PrepareDrawHighlight>("PrepareHighlight", primaryFramebuffer);
|
||||||
|
render::Varying highlight0Rect;
|
||||||
|
|
||||||
|
for (auto i = 0; i < HighlightSharedParameters::MAX_PASS_COUNT; i++) {
|
||||||
|
const auto selectionName = task.addJob<ExtractSelectionName>("ExtractSelectionName", highlightSelectionNames, i);
|
||||||
|
const auto groupItems = addSelectItemJobs(task, selectionName, items);
|
||||||
|
const auto highlightedItemIDs = task.addJob<render::MetaToSubItems>("HighlightMetaToSubItemIDs", groupItems);
|
||||||
|
const auto highlightedItems = task.addJob<render::IDsToBounds>("HighlightMetaToSubItems", highlightedItemIDs);
|
||||||
|
|
||||||
|
// Sort
|
||||||
|
const auto sortedPipelines = task.addJob<render::PipelineSortShapes>("HighlightPipelineSort", highlightedItems);
|
||||||
|
const auto sortedBounds = task.addJob<render::DepthSortShapes>("HighlightDepthSort", sortedPipelines);
|
||||||
|
|
||||||
|
// Draw depth of highlighted objects in separate buffer
|
||||||
|
std::string name;
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << "HighlightMask" << i;
|
||||||
|
name = stream.str();
|
||||||
|
}
|
||||||
|
const auto drawMaskInputs = DrawHighlightMask::Inputs(sortedBounds, highlightRessources).asVarying();
|
||||||
|
const auto highlightedRect = task.addJob<DrawHighlightMask>(name, drawMaskInputs, i, shapePlumber, sharedParameters);
|
||||||
|
if (i == 0) {
|
||||||
|
highlight0Rect = highlightedRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw highlight
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << "HighlightEffect" << i;
|
||||||
|
name = stream.str();
|
||||||
|
}
|
||||||
|
const auto drawHighlightInputs = DrawHighlight::Inputs(deferredFrameTransform, highlightRessources, sceneFrameBuffer, highlightedRect).asVarying();
|
||||||
|
task.addJob<DrawHighlight>(name, drawHighlightInputs, i, sharedParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug highlight
|
||||||
|
const auto debugInputs = DebugHighlight::Inputs(highlightRessources, const_cast<const render::Varying&>(highlight0Rect)).asVarying();
|
||||||
|
task.addJob<DebugHighlight>("HighlightDebug", debugInputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
const render::Varying DrawHighlightTask::addSelectItemJobs(JobModel& task, const render::Varying& selectionName,
|
||||||
|
const RenderFetchCullSortTask::BucketList& items) {
|
||||||
|
const auto& opaques = items[RenderFetchCullSortTask::OPAQUE_SHAPE];
|
||||||
|
const auto& transparents = items[RenderFetchCullSortTask::TRANSPARENT_SHAPE];
|
||||||
|
const auto& metas = items[RenderFetchCullSortTask::META];
|
||||||
|
|
||||||
|
const auto selectMetaInput = SelectItems::Inputs(metas, Varying(), selectionName).asVarying();
|
||||||
|
const auto selectedMetas = task.addJob<SelectItems>("MetaSelection", selectMetaInput);
|
||||||
|
const auto selectMetaAndOpaqueInput = SelectItems::Inputs(opaques, selectedMetas, selectionName).asVarying();
|
||||||
|
const auto selectedMetasAndOpaques = task.addJob<SelectItems>("OpaqueSelection", selectMetaAndOpaqueInput);
|
||||||
|
const auto selectItemInput = SelectItems::Inputs(transparents, selectedMetasAndOpaques, selectionName).asVarying();
|
||||||
|
return task.addJob<SelectItems>("TransparentSelection", selectItemInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "model_shadow_vert.h"
|
||||||
|
#include "skin_model_shadow_vert.h"
|
||||||
|
|
||||||
|
#include "model_shadow_frag.h"
|
||||||
|
|
||||||
|
void DrawHighlightTask::initMaskPipelines(render::ShapePlumber& shapePlumber, gpu::StatePointer state) {
|
||||||
|
auto modelVertex = gpu::Shader::createVertex(std::string(model_shadow_vert));
|
||||||
|
auto modelPixel = gpu::Shader::createPixel(std::string(model_shadow_frag));
|
||||||
|
gpu::ShaderPointer modelProgram = gpu::Shader::createProgram(modelVertex, modelPixel);
|
||||||
|
shapePlumber.addPipeline(
|
||||||
|
ShapeKey::Filter::Builder().withoutSkinned(),
|
||||||
|
modelProgram, state);
|
||||||
|
|
||||||
|
auto skinVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_vert));
|
||||||
|
gpu::ShaderPointer skinProgram = gpu::Shader::createProgram(skinVertex, modelPixel);
|
||||||
|
shapePlumber.addPipeline(
|
||||||
|
ShapeKey::Filter::Builder().withSkinned(),
|
||||||
|
skinProgram, state);
|
||||||
|
}
|
226
libraries/render-utils/src/HighlightEffect.h
Normal file
226
libraries/render-utils/src/HighlightEffect.h
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
//
|
||||||
|
// HighlightEffect.h
|
||||||
|
// render-utils/src/
|
||||||
|
//
|
||||||
|
// Created by Olivier Prat on 08/08/17.
|
||||||
|
// Copyright 2017 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_render_utils_HighlightEffect_h
|
||||||
|
#define hifi_render_utils_HighlightEffect_h
|
||||||
|
|
||||||
|
#include <render/Engine.h>
|
||||||
|
#include <render/HighlightStage.h>
|
||||||
|
#include <render/RenderFetchCullSortTask.h>
|
||||||
|
|
||||||
|
#include "DeferredFramebuffer.h"
|
||||||
|
#include "DeferredFrameTransform.h"
|
||||||
|
|
||||||
|
class HighlightRessources {
|
||||||
|
public:
|
||||||
|
HighlightRessources();
|
||||||
|
|
||||||
|
gpu::FramebufferPointer getDepthFramebuffer();
|
||||||
|
gpu::TexturePointer getDepthTexture();
|
||||||
|
|
||||||
|
gpu::FramebufferPointer getColorFramebuffer();
|
||||||
|
|
||||||
|
// Update the source framebuffer size which will drive the allocation of all the other resources.
|
||||||
|
void update(const gpu::FramebufferPointer& primaryFrameBuffer);
|
||||||
|
const glm::ivec2& getSourceFrameSize() const { return _frameSize; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
gpu::FramebufferPointer _depthFrameBuffer;
|
||||||
|
gpu::FramebufferPointer _colorFrameBuffer;
|
||||||
|
gpu::TexturePointer _depthStencilTexture;
|
||||||
|
|
||||||
|
glm::ivec2 _frameSize;
|
||||||
|
|
||||||
|
void allocateColorBuffer(const gpu::FramebufferPointer& primaryFrameBuffer);
|
||||||
|
void allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer);
|
||||||
|
};
|
||||||
|
|
||||||
|
using HighlightRessourcesPointer = std::shared_ptr<HighlightRessources>;
|
||||||
|
|
||||||
|
class HighlightSharedParameters {
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MAX_PASS_COUNT = 8
|
||||||
|
};
|
||||||
|
|
||||||
|
HighlightSharedParameters();
|
||||||
|
|
||||||
|
std::array<render::HighlightStage::Index, MAX_PASS_COUNT> _highlightIds;
|
||||||
|
|
||||||
|
static float getBlurPixelWidth(const render::HighlightStyle& style, int frameBufferHeight);
|
||||||
|
};
|
||||||
|
|
||||||
|
using HighlightSharedParametersPointer = std::shared_ptr<HighlightSharedParameters>;
|
||||||
|
|
||||||
|
class PrepareDrawHighlight {
|
||||||
|
public:
|
||||||
|
using Inputs = gpu::FramebufferPointer;
|
||||||
|
using Outputs = HighlightRessourcesPointer;
|
||||||
|
using JobModel = render::Job::ModelIO<PrepareDrawHighlight, Inputs, Outputs>;
|
||||||
|
|
||||||
|
PrepareDrawHighlight();
|
||||||
|
|
||||||
|
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
HighlightRessourcesPointer _ressources;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class SelectionToHighlight {
|
||||||
|
public:
|
||||||
|
|
||||||
|
using Outputs = std::vector<std::string>;
|
||||||
|
using JobModel = render::Job::ModelO<SelectionToHighlight, Outputs>;
|
||||||
|
|
||||||
|
SelectionToHighlight(HighlightSharedParametersPointer parameters) : _sharedParameters{ parameters } {}
|
||||||
|
|
||||||
|
void run(const render::RenderContextPointer& renderContext, Outputs& outputs);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
HighlightSharedParametersPointer _sharedParameters;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExtractSelectionName {
|
||||||
|
public:
|
||||||
|
|
||||||
|
using Inputs = SelectionToHighlight::Outputs;
|
||||||
|
using Outputs = std::string;
|
||||||
|
using JobModel = render::Job::ModelIO<ExtractSelectionName, Inputs, Outputs>;
|
||||||
|
|
||||||
|
ExtractSelectionName(unsigned int highlightIndex) : _highlightPassIndex{ highlightIndex } {}
|
||||||
|
|
||||||
|
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
unsigned int _highlightPassIndex;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class DrawHighlightMask {
|
||||||
|
public:
|
||||||
|
|
||||||
|
using Inputs = render::VaryingSet2<render::ShapeBounds, HighlightRessourcesPointer>;
|
||||||
|
using Outputs = glm::ivec4;
|
||||||
|
using JobModel = render::Job::ModelIO<DrawHighlightMask, Inputs, Outputs>;
|
||||||
|
|
||||||
|
DrawHighlightMask(unsigned int highlightIndex, render::ShapePlumberPointer shapePlumber, HighlightSharedParametersPointer parameters);
|
||||||
|
|
||||||
|
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
unsigned int _highlightPassIndex;
|
||||||
|
render::ShapePlumberPointer _shapePlumber;
|
||||||
|
HighlightSharedParametersPointer _sharedParameters;
|
||||||
|
gpu::BufferPointer _boundsBuffer;
|
||||||
|
|
||||||
|
static gpu::PipelinePointer _stencilMaskPipeline;
|
||||||
|
static gpu::PipelinePointer _stencilMaskFillPipeline;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DrawHighlight {
|
||||||
|
public:
|
||||||
|
|
||||||
|
using Inputs = render::VaryingSet4<DeferredFrameTransformPointer, HighlightRessourcesPointer, DeferredFramebufferPointer, glm::ivec4>;
|
||||||
|
using Config = render::Job::Config;
|
||||||
|
using JobModel = render::Job::ModelI<DrawHighlight, Inputs, Config>;
|
||||||
|
|
||||||
|
DrawHighlight(unsigned int highlightIndex, HighlightSharedParametersPointer parameters);
|
||||||
|
|
||||||
|
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
#include "Highlight_shared.slh"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SCENE_DEPTH_MAP_SLOT = 0,
|
||||||
|
HIGHLIGHTED_DEPTH_MAP_SLOT,
|
||||||
|
|
||||||
|
HIGHLIGHT_PARAMS_SLOT = 0,
|
||||||
|
FRAME_TRANSFORM_SLOT,
|
||||||
|
};
|
||||||
|
|
||||||
|
using HighlightConfigurationBuffer = gpu::StructBuffer<HighlightParameters>;
|
||||||
|
|
||||||
|
static const gpu::PipelinePointer& getPipeline(const render::HighlightStyle& style);
|
||||||
|
|
||||||
|
static gpu::PipelinePointer _pipeline;
|
||||||
|
static gpu::PipelinePointer _pipelineFilled;
|
||||||
|
|
||||||
|
unsigned int _highlightPassIndex;
|
||||||
|
HighlightParameters _parameters;
|
||||||
|
HighlightSharedParametersPointer _sharedParameters;
|
||||||
|
HighlightConfigurationBuffer _configuration;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DebugHighlightConfig : public render::Job::Config {
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(bool viewMask MEMBER viewMask NOTIFY dirty)
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
bool viewMask{ false };
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void dirty();
|
||||||
|
};
|
||||||
|
|
||||||
|
class DebugHighlight {
|
||||||
|
public:
|
||||||
|
using Inputs = render::VaryingSet2<HighlightRessourcesPointer, glm::ivec4>;
|
||||||
|
using Config = DebugHighlightConfig;
|
||||||
|
using JobModel = render::Job::ModelI<DebugHighlight, Inputs, Config>;
|
||||||
|
|
||||||
|
DebugHighlight();
|
||||||
|
~DebugHighlight();
|
||||||
|
|
||||||
|
void configure(const Config& config);
|
||||||
|
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
gpu::PipelinePointer _depthPipeline;
|
||||||
|
int _geometryDepthId{ 0 };
|
||||||
|
bool _isDisplayEnabled{ false };
|
||||||
|
|
||||||
|
const gpu::PipelinePointer& getDepthPipeline();
|
||||||
|
void initializePipelines();
|
||||||
|
};
|
||||||
|
|
||||||
|
class DrawHighlightTask {
|
||||||
|
public:
|
||||||
|
|
||||||
|
using Inputs = render::VaryingSet4<RenderFetchCullSortTask::BucketList, DeferredFramebufferPointer, gpu::FramebufferPointer, DeferredFrameTransformPointer>;
|
||||||
|
using Config = render::Task::Config;
|
||||||
|
using JobModel = render::Task::ModelI<DrawHighlightTask, Inputs, Config>;
|
||||||
|
|
||||||
|
DrawHighlightTask();
|
||||||
|
|
||||||
|
void configure(const Config& config);
|
||||||
|
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
static void initMaskPipelines(render::ShapePlumber& plumber, gpu::StatePointer state);
|
||||||
|
static const render::Varying addSelectItemJobs(JobModel& task, const render::Varying& selectionName, const RenderFetchCullSortTask::BucketList& items);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_render_utils_HighlightEffect_h
|
||||||
|
|
||||||
|
|
104
libraries/render-utils/src/Highlight_aabox.slv
Normal file
104
libraries/render-utils/src/Highlight_aabox.slv
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
<@include gpu/Config.slh@>
|
||||||
|
<$VERSION_HEADER$>
|
||||||
|
// Generated on <$_SCRIBE_DATE$>
|
||||||
|
//
|
||||||
|
// Draw and transform the fed vertex position with the standard MVP stack
|
||||||
|
// and offset the vertices by a certain amount in the vertex direction
|
||||||
|
//
|
||||||
|
// Created by Olivier Prat on 11/02/2017
|
||||||
|
// Copyright 2017 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 gpu/Transform.slh@>
|
||||||
|
|
||||||
|
<$declareStandardTransform()$>
|
||||||
|
|
||||||
|
struct ItemBound {
|
||||||
|
vec4 id_boundPos;
|
||||||
|
vec4 boundDim_s;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(GPU_GL410)
|
||||||
|
uniform samplerBuffer ssbo0Buffer;
|
||||||
|
ItemBound getItemBound(int i) {
|
||||||
|
int offset = 2 * i;
|
||||||
|
ItemBound bound;
|
||||||
|
bound.id_boundPos = texelFetch(ssbo0Buffer, offset);
|
||||||
|
bound.boundDim_s = texelFetch(ssbo0Buffer, offset + 1);
|
||||||
|
return bound;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
layout(std140) buffer ssbo0Buffer {
|
||||||
|
ItemBound bounds[];
|
||||||
|
};
|
||||||
|
ItemBound getItemBound(int i) {
|
||||||
|
ItemBound bound = bounds[i];
|
||||||
|
return bound;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uniform vec2 outlineWidth;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
const vec3 UNIT_BOX_VERTICES[8] = vec3[8](
|
||||||
|
vec3(0.0, 1.0, 0.0),
|
||||||
|
vec3(1.0, 1.0, 0.0),
|
||||||
|
vec3(1.0, 0.0, 0.0),
|
||||||
|
vec3(0.0, 0.0, 0.0),
|
||||||
|
vec3(0.0, 1.0, 1.0),
|
||||||
|
vec3(1.0, 1.0, 1.0),
|
||||||
|
vec3(1.0, 0.0, 1.0),
|
||||||
|
vec3(0.0, 0.0, 1.0)
|
||||||
|
);
|
||||||
|
const vec3 UNIT_BOX_NORMALS[8] = vec3[8](
|
||||||
|
vec3(-1.0, 1.0, -1.0),
|
||||||
|
vec3(1.0, 1.0, -1.0),
|
||||||
|
vec3(1.0, -1.0, -1.0),
|
||||||
|
vec3(-1.0, -1.0, -1.0),
|
||||||
|
vec3(-1.0, 1.0, 1.0),
|
||||||
|
vec3(1.0, 1.0, 1.0),
|
||||||
|
vec3(1.0, -1.0, 1.0),
|
||||||
|
vec3(-1.0, -1.0, 1.0)
|
||||||
|
);
|
||||||
|
const int NUM_VERTICES_PER_CUBE = 36;
|
||||||
|
const int UNIT_BOX_TRIANGLE_INDICES[NUM_VERTICES_PER_CUBE] = int[NUM_VERTICES_PER_CUBE](
|
||||||
|
0, 1, 2,
|
||||||
|
0, 2, 3,
|
||||||
|
3, 2, 6,
|
||||||
|
3, 6, 7,
|
||||||
|
7, 6, 5,
|
||||||
|
7, 5, 4,
|
||||||
|
4, 5, 1,
|
||||||
|
4, 1, 0,
|
||||||
|
1, 5, 6,
|
||||||
|
1, 6, 2,
|
||||||
|
4, 0, 3,
|
||||||
|
4, 3, 7
|
||||||
|
);
|
||||||
|
|
||||||
|
int boundID = gl_VertexID / NUM_VERTICES_PER_CUBE;
|
||||||
|
int vertexID = gl_VertexID - boundID * NUM_VERTICES_PER_CUBE;
|
||||||
|
int triangleIndex = UNIT_BOX_TRIANGLE_INDICES[vertexID];
|
||||||
|
vec3 cubeVec = UNIT_BOX_VERTICES[triangleIndex];
|
||||||
|
|
||||||
|
ItemBound bound = getItemBound(boundID);
|
||||||
|
vec3 boundPos = bound.id_boundPos.yzw;
|
||||||
|
vec3 boundDim = bound.boundDim_s.xyz;
|
||||||
|
|
||||||
|
vec4 pos = vec4(boundPos + boundDim * cubeVec.xyz, 1.0);
|
||||||
|
|
||||||
|
// standard transform
|
||||||
|
TransformCamera cam = getTransformCamera();
|
||||||
|
TransformObject obj = getTransformObject();
|
||||||
|
<$transformModelToMonoClipPos(cam, obj, pos, gl_Position)$>
|
||||||
|
|
||||||
|
// Offset the vertex to take into account the outline width
|
||||||
|
pos.xyz += UNIT_BOX_NORMALS[triangleIndex];
|
||||||
|
vec4 offsetPosition;
|
||||||
|
<$transformModelToMonoClipPos(cam, obj, pos, offsetPosition)$>
|
||||||
|
gl_Position.xy += normalize(offsetPosition.xy-gl_Position.xy) * outlineWidth * gl_Position.w;
|
||||||
|
<$transformStereoClipsSpace(cam, gl_Position)$>
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
// Outline_filled.slf
|
// Highlight_filled.slf
|
||||||
// Add outline effect based on two zbuffers : one containing the total scene z and another
|
// Add highlight effect based on two zbuffers : one containing the total scene z and another
|
||||||
// with the z of only the objects to be outlined.
|
// with the z of only the objects to be outlined.
|
||||||
// This is the version with the fill effect inside the silhouette.
|
// This is the version with the fill effect inside the silhouette.
|
||||||
//
|
//
|
||||||
|
@ -9,5 +9,5 @@
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
<@include Outline.slh@>
|
<@include Highlight.slh@>
|
||||||
<$main(1)$>
|
<$main(1)$>
|
30
libraries/render-utils/src/Highlight_shared.slh
Normal file
30
libraries/render-utils/src/Highlight_shared.slh
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// glsl / C++ compatible source as interface for highlight
|
||||||
|
#ifdef __cplusplus
|
||||||
|
# define TVEC2 glm::vec2
|
||||||
|
# define TVEC3 glm::vec3
|
||||||
|
# define TVEC4 glm::vec4
|
||||||
|
#else
|
||||||
|
# define TVEC2 vec2
|
||||||
|
# define TVEC3 vec3
|
||||||
|
# define TVEC4 vec4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct HighlightParameters
|
||||||
|
{
|
||||||
|
TVEC3 _color;
|
||||||
|
float _intensity;
|
||||||
|
|
||||||
|
TVEC2 _size;
|
||||||
|
float _unoccludedFillOpacity;
|
||||||
|
float _occludedFillOpacity;
|
||||||
|
|
||||||
|
float _threshold;
|
||||||
|
int _blurKernelSize;
|
||||||
|
float padding2;
|
||||||
|
float padding3;
|
||||||
|
};
|
||||||
|
|
||||||
|
// <@if 1@>
|
||||||
|
// Trigger Scribe include
|
||||||
|
// <@endif@> <!def that !>
|
||||||
|
//
|
|
@ -14,6 +14,7 @@
|
||||||
#include "LightStage.h"
|
#include "LightStage.h"
|
||||||
|
|
||||||
std::string LightStage::_stageName { "LIGHT_STAGE"};
|
std::string LightStage::_stageName { "LIGHT_STAGE"};
|
||||||
|
const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
|
||||||
|
|
||||||
LightStage::LightStage() {
|
LightStage::LightStage() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ public:
|
||||||
static const std::string& getName() { return _stageName; }
|
static const std::string& getName() { return _stageName; }
|
||||||
|
|
||||||
using Index = render::indexed_container::Index;
|
using Index = render::indexed_container::Index;
|
||||||
static const Index INVALID_INDEX { render::indexed_container::INVALID_INDEX };
|
static const Index INVALID_INDEX;
|
||||||
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
|
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
|
||||||
|
|
||||||
using LightPointer = model::LightPointer;
|
using LightPointer = model::LightPointer;
|
||||||
|
|
|
@ -1,371 +0,0 @@
|
||||||
//
|
|
||||||
// OutlineEffect.cpp
|
|
||||||
// render-utils/src/
|
|
||||||
//
|
|
||||||
// Created by Olivier Prat on 08/08/17.
|
|
||||||
// Copyright 2017 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 "OutlineEffect.h"
|
|
||||||
|
|
||||||
#include "GeometryCache.h"
|
|
||||||
|
|
||||||
#include <render/FilterTask.h>
|
|
||||||
#include <render/SortTask.h>
|
|
||||||
|
|
||||||
#include "gpu/Context.h"
|
|
||||||
#include "gpu/StandardShaderLib.h"
|
|
||||||
|
|
||||||
|
|
||||||
#include "surfaceGeometry_copyDepth_frag.h"
|
|
||||||
#include "debug_deferred_buffer_vert.h"
|
|
||||||
#include "debug_deferred_buffer_frag.h"
|
|
||||||
#include "Outline_frag.h"
|
|
||||||
#include "Outline_filled_frag.h"
|
|
||||||
|
|
||||||
using namespace render;
|
|
||||||
|
|
||||||
extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state);
|
|
||||||
|
|
||||||
OutlineFramebuffer::OutlineFramebuffer() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void OutlineFramebuffer::update(const gpu::TexturePointer& colorBuffer) {
|
|
||||||
// If the depth buffer or size changed, we need to delete our FBOs and recreate them at the
|
|
||||||
// new correct dimensions.
|
|
||||||
if (_depthTexture) {
|
|
||||||
auto newFrameSize = glm::ivec2(colorBuffer->getDimensions());
|
|
||||||
if (_frameSize != newFrameSize) {
|
|
||||||
_frameSize = newFrameSize;
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OutlineFramebuffer::clear() {
|
|
||||||
_depthFramebuffer.reset();
|
|
||||||
_depthTexture.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OutlineFramebuffer::allocate() {
|
|
||||||
|
|
||||||
auto width = _frameSize.x;
|
|
||||||
auto height = _frameSize.y;
|
|
||||||
auto format = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH);
|
|
||||||
|
|
||||||
_depthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(format, width, height));
|
|
||||||
_depthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("outlineDepth"));
|
|
||||||
_depthFramebuffer->setDepthStencilBuffer(_depthTexture, format);
|
|
||||||
}
|
|
||||||
|
|
||||||
gpu::FramebufferPointer OutlineFramebuffer::getDepthFramebuffer() {
|
|
||||||
if (!_depthFramebuffer) {
|
|
||||||
allocate();
|
|
||||||
}
|
|
||||||
return _depthFramebuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
gpu::TexturePointer OutlineFramebuffer::getDepthTexture() {
|
|
||||||
if (!_depthTexture) {
|
|
||||||
allocate();
|
|
||||||
}
|
|
||||||
return _depthTexture;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DrawOutlineDepth::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output) {
|
|
||||||
assert(renderContext->args);
|
|
||||||
assert(renderContext->args->hasViewFrustum());
|
|
||||||
auto& inShapes = inputs.get0();
|
|
||||||
auto& deferredFrameBuffer = inputs.get1();
|
|
||||||
|
|
||||||
if (!inShapes.empty()) {
|
|
||||||
RenderArgs* args = renderContext->args;
|
|
||||||
ShapeKey::Builder defaultKeyBuilder;
|
|
||||||
|
|
||||||
if (!_outlineFramebuffer) {
|
|
||||||
_outlineFramebuffer = std::make_shared<OutlineFramebuffer>();
|
|
||||||
}
|
|
||||||
_outlineFramebuffer->update(deferredFrameBuffer->getDeferredColorTexture());
|
|
||||||
|
|
||||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
|
||||||
args->_batch = &batch;
|
|
||||||
|
|
||||||
batch.setFramebuffer(_outlineFramebuffer->getDepthFramebuffer());
|
|
||||||
// Clear it
|
|
||||||
batch.clearFramebuffer(
|
|
||||||
gpu::Framebuffer::BUFFER_DEPTH,
|
|
||||||
vec4(vec3(1.0, 1.0, 1.0), 0.0), 1.0, 0, false);
|
|
||||||
|
|
||||||
// Setup camera, projection and viewport for all items
|
|
||||||
batch.setViewportTransform(args->_viewport);
|
|
||||||
batch.setStateScissorRect(args->_viewport);
|
|
||||||
|
|
||||||
glm::mat4 projMat;
|
|
||||||
Transform viewMat;
|
|
||||||
args->getViewFrustum().evalProjectionMatrix(projMat);
|
|
||||||
args->getViewFrustum().evalViewTransform(viewMat);
|
|
||||||
|
|
||||||
batch.setProjectionTransform(projMat);
|
|
||||||
batch.setViewTransform(viewMat);
|
|
||||||
|
|
||||||
auto shadowPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder);
|
|
||||||
auto shadowSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned());
|
|
||||||
|
|
||||||
std::vector<ShapeKey> skinnedShapeKeys{};
|
|
||||||
|
|
||||||
// Iterate through all inShapes and render the unskinned
|
|
||||||
args->_shapePipeline = shadowPipeline;
|
|
||||||
batch.setPipeline(shadowPipeline->pipeline);
|
|
||||||
for (auto items : inShapes) {
|
|
||||||
if (items.first.isSkinned()) {
|
|
||||||
skinnedShapeKeys.push_back(items.first);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
renderItems(renderContext, items.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reiterate to render the skinned
|
|
||||||
args->_shapePipeline = shadowSkinnedPipeline;
|
|
||||||
batch.setPipeline(shadowSkinnedPipeline->pipeline);
|
|
||||||
for (const auto& key : skinnedShapeKeys) {
|
|
||||||
renderItems(renderContext, inShapes.at(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
args->_shapePipeline = nullptr;
|
|
||||||
args->_batch = nullptr;
|
|
||||||
});
|
|
||||||
|
|
||||||
output = _outlineFramebuffer;
|
|
||||||
} else {
|
|
||||||
output = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DrawOutline::DrawOutline() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void DrawOutline::configure(const Config& config) {
|
|
||||||
_color = config.color;
|
|
||||||
_blurKernelSize = std::min(10, std::max(2, (int)floorf(config.width*2 + 0.5f)));
|
|
||||||
// Size is in normalized screen height. We decide that for outline width = 1, this is equal to 1/400.
|
|
||||||
_size = config.width / 400.f;
|
|
||||||
_fillOpacityUnoccluded = config.fillOpacityUnoccluded;
|
|
||||||
_fillOpacityOccluded = config.fillOpacityOccluded;
|
|
||||||
_threshold = config.glow ? 1.f : 1e-3f;
|
|
||||||
_intensity = config.intensity * (config.glow ? 2.f : 1.f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DrawOutline::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
|
|
||||||
auto outlineFrameBuffer = inputs.get1();
|
|
||||||
|
|
||||||
if (outlineFrameBuffer) {
|
|
||||||
auto sceneDepthBuffer = inputs.get2();
|
|
||||||
const auto frameTransform = inputs.get0();
|
|
||||||
auto outlinedDepthTexture = outlineFrameBuffer->getDepthTexture();
|
|
||||||
auto destinationFrameBuffer = inputs.get3();
|
|
||||||
auto framebufferSize = glm::ivec2(outlinedDepthTexture->getDimensions());
|
|
||||||
|
|
||||||
if (!_primaryWithoutDepthBuffer || framebufferSize!=_frameBufferSize) {
|
|
||||||
// Failing to recreate this frame buffer when the screen has been resized creates a bug on Mac
|
|
||||||
_primaryWithoutDepthBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("primaryWithoutDepth"));
|
|
||||||
_primaryWithoutDepthBuffer->setRenderBuffer(0, destinationFrameBuffer->getRenderBuffer(0));
|
|
||||||
_frameBufferSize = framebufferSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sceneDepthBuffer) {
|
|
||||||
const auto OPACITY_EPSILON = 5e-3f;
|
|
||||||
auto pipeline = getPipeline(_fillOpacityUnoccluded>OPACITY_EPSILON || _fillOpacityOccluded>OPACITY_EPSILON);
|
|
||||||
auto args = renderContext->args;
|
|
||||||
{
|
|
||||||
auto& configuration = _configuration.edit();
|
|
||||||
configuration._color = _color;
|
|
||||||
configuration._intensity = _intensity;
|
|
||||||
configuration._fillOpacityUnoccluded = _fillOpacityUnoccluded;
|
|
||||||
configuration._fillOpacityOccluded = _fillOpacityOccluded;
|
|
||||||
configuration._threshold = _threshold;
|
|
||||||
configuration._blurKernelSize = _blurKernelSize;
|
|
||||||
configuration._size.x = _size * _frameBufferSize.y / _frameBufferSize.x;
|
|
||||||
configuration._size.y = _size;
|
|
||||||
}
|
|
||||||
|
|
||||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
|
||||||
batch.enableStereo(false);
|
|
||||||
batch.setFramebuffer(_primaryWithoutDepthBuffer);
|
|
||||||
|
|
||||||
batch.setViewportTransform(args->_viewport);
|
|
||||||
batch.setProjectionTransform(glm::mat4());
|
|
||||||
batch.resetViewTransform();
|
|
||||||
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(_frameBufferSize, args->_viewport));
|
|
||||||
batch.setPipeline(pipeline);
|
|
||||||
|
|
||||||
batch.setUniformBuffer(OUTLINE_PARAMS_SLOT, _configuration);
|
|
||||||
batch.setUniformBuffer(FRAME_TRANSFORM_SLOT, frameTransform->getFrameTransformBuffer());
|
|
||||||
batch.setResourceTexture(SCENE_DEPTH_SLOT, sceneDepthBuffer->getPrimaryDepthTexture());
|
|
||||||
batch.setResourceTexture(OUTLINED_DEPTH_SLOT, outlinedDepthTexture);
|
|
||||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
|
||||||
|
|
||||||
// Restore previous frame buffer
|
|
||||||
batch.setFramebuffer(destinationFrameBuffer);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const gpu::PipelinePointer& DrawOutline::getPipeline(bool isFilled) {
|
|
||||||
if (!_pipeline) {
|
|
||||||
auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS();
|
|
||||||
auto ps = gpu::Shader::createPixel(std::string(Outline_frag));
|
|
||||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
|
||||||
|
|
||||||
gpu::Shader::BindingSet slotBindings;
|
|
||||||
slotBindings.insert(gpu::Shader::Binding("outlineParamsBuffer", OUTLINE_PARAMS_SLOT));
|
|
||||||
slotBindings.insert(gpu::Shader::Binding("deferredFrameTransformBuffer", FRAME_TRANSFORM_SLOT));
|
|
||||||
slotBindings.insert(gpu::Shader::Binding("sceneDepthMap", SCENE_DEPTH_SLOT));
|
|
||||||
slotBindings.insert(gpu::Shader::Binding("outlinedDepthMap", OUTLINED_DEPTH_SLOT));
|
|
||||||
gpu::Shader::makeProgram(*program, slotBindings);
|
|
||||||
|
|
||||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
|
||||||
state->setDepthTest(gpu::State::DepthTest(false, false));
|
|
||||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
|
||||||
_pipeline = gpu::Pipeline::create(program, state);
|
|
||||||
|
|
||||||
ps = gpu::Shader::createPixel(std::string(Outline_filled_frag));
|
|
||||||
program = gpu::Shader::createProgram(vs, ps);
|
|
||||||
gpu::Shader::makeProgram(*program, slotBindings);
|
|
||||||
_pipelineFilled = gpu::Pipeline::create(program, state);
|
|
||||||
}
|
|
||||||
return isFilled ? _pipelineFilled : _pipeline;
|
|
||||||
}
|
|
||||||
|
|
||||||
DebugOutline::DebugOutline() {
|
|
||||||
_geometryId = DependencyManager::get<GeometryCache>()->allocateID();
|
|
||||||
}
|
|
||||||
|
|
||||||
DebugOutline::~DebugOutline() {
|
|
||||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
|
||||||
if (geometryCache) {
|
|
||||||
geometryCache->releaseID(_geometryId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DebugOutline::configure(const Config& config) {
|
|
||||||
_isDisplayDepthEnabled = config.viewOutlinedDepth;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DebugOutline::run(const render::RenderContextPointer& renderContext, const Inputs& input) {
|
|
||||||
const auto outlineFramebuffer = input;
|
|
||||||
|
|
||||||
if (_isDisplayDepthEnabled && outlineFramebuffer) {
|
|
||||||
assert(renderContext->args);
|
|
||||||
assert(renderContext->args->hasViewFrustum());
|
|
||||||
RenderArgs* args = renderContext->args;
|
|
||||||
|
|
||||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
|
||||||
batch.enableStereo(false);
|
|
||||||
batch.setViewportTransform(args->_viewport);
|
|
||||||
|
|
||||||
const auto geometryBuffer = DependencyManager::get<GeometryCache>();
|
|
||||||
|
|
||||||
glm::mat4 projMat;
|
|
||||||
Transform viewMat;
|
|
||||||
args->getViewFrustum().evalProjectionMatrix(projMat);
|
|
||||||
args->getViewFrustum().evalViewTransform(viewMat);
|
|
||||||
batch.setProjectionTransform(projMat);
|
|
||||||
batch.setViewTransform(viewMat, true);
|
|
||||||
batch.setModelTransform(Transform());
|
|
||||||
|
|
||||||
batch.setPipeline(getDebugPipeline());
|
|
||||||
batch.setResourceTexture(0, outlineFramebuffer->getDepthTexture());
|
|
||||||
|
|
||||||
const glm::vec4 color(1.0f, 0.5f, 0.2f, 1.0f);
|
|
||||||
const glm::vec2 bottomLeft(-1.0f, -1.0f);
|
|
||||||
const glm::vec2 topRight(1.0f, 1.0f);
|
|
||||||
geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryId);
|
|
||||||
|
|
||||||
batch.setResourceTexture(0, nullptr);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const gpu::PipelinePointer& DebugOutline::getDebugPipeline() {
|
|
||||||
if (!_debugPipeline) {
|
|
||||||
static const std::string VERTEX_SHADER{ debug_deferred_buffer_vert };
|
|
||||||
static const std::string FRAGMENT_SHADER{ debug_deferred_buffer_frag };
|
|
||||||
static const std::string SOURCE_PLACEHOLDER{ "//SOURCE_PLACEHOLDER" };
|
|
||||||
static const auto SOURCE_PLACEHOLDER_INDEX = FRAGMENT_SHADER.find(SOURCE_PLACEHOLDER);
|
|
||||||
Q_ASSERT_X(SOURCE_PLACEHOLDER_INDEX != std::string::npos, Q_FUNC_INFO,
|
|
||||||
"Could not find source placeholder");
|
|
||||||
static const std::string DEFAULT_DEPTH_SHADER{
|
|
||||||
"vec4 getFragmentColor() {"
|
|
||||||
" float Zdb = texelFetch(depthMap, ivec2(gl_FragCoord.xy), 0).x;"
|
|
||||||
" Zdb = 1.0-(1.0-Zdb)*100;"
|
|
||||||
" return vec4(Zdb, Zdb, Zdb, 1.0);"
|
|
||||||
" }"
|
|
||||||
};
|
|
||||||
|
|
||||||
auto bakedFragmentShader = FRAGMENT_SHADER;
|
|
||||||
bakedFragmentShader.replace(SOURCE_PLACEHOLDER_INDEX, SOURCE_PLACEHOLDER.size(), DEFAULT_DEPTH_SHADER);
|
|
||||||
|
|
||||||
static const auto vs = gpu::Shader::createVertex(VERTEX_SHADER);
|
|
||||||
const auto ps = gpu::Shader::createPixel(bakedFragmentShader);
|
|
||||||
const auto program = gpu::Shader::createProgram(vs, ps);
|
|
||||||
|
|
||||||
gpu::Shader::BindingSet slotBindings;
|
|
||||||
slotBindings.insert(gpu::Shader::Binding("depthMap", 0));
|
|
||||||
gpu::Shader::makeProgram(*program, slotBindings);
|
|
||||||
|
|
||||||
auto state = std::make_shared<gpu::State>();
|
|
||||||
state->setDepthTest(gpu::State::DepthTest(false));
|
|
||||||
_debugPipeline = gpu::Pipeline::create(program, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _debugPipeline;
|
|
||||||
}
|
|
||||||
|
|
||||||
DrawOutlineTask::DrawOutlineTask() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void DrawOutlineTask::configure(const Config& config) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) {
|
|
||||||
const auto input = inputs.get<Inputs>();
|
|
||||||
const auto selectedMetas = inputs.getN<Inputs>(0);
|
|
||||||
const auto shapePlumber = input.get1();
|
|
||||||
const auto sceneFrameBuffer = inputs.getN<Inputs>(2);
|
|
||||||
const auto primaryFramebuffer = inputs.getN<Inputs>(3);
|
|
||||||
const auto deferredFrameTransform = inputs.getN<Inputs>(4);
|
|
||||||
|
|
||||||
// Prepare the ShapePipeline
|
|
||||||
ShapePlumberPointer shapePlumberZPass = std::make_shared<ShapePlumber>();
|
|
||||||
{
|
|
||||||
auto state = std::make_shared<gpu::State>();
|
|
||||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
|
||||||
state->setColorWriteMask(false, false, false, false);
|
|
||||||
|
|
||||||
initZPassPipelines(*shapePlumberZPass, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto outlinedItemIDs = task.addJob<render::MetaToSubItems>("OutlineMetaToSubItemIDs", selectedMetas);
|
|
||||||
const auto outlinedItems = task.addJob<render::IDsToBounds>("OutlineMetaToSubItems", outlinedItemIDs, true);
|
|
||||||
|
|
||||||
// Sort
|
|
||||||
const auto sortedPipelines = task.addJob<render::PipelineSortShapes>("OutlinePipelineSort", outlinedItems);
|
|
||||||
const auto sortedShapes = task.addJob<render::DepthSortShapes>("OutlineDepthSort", sortedPipelines);
|
|
||||||
|
|
||||||
// Draw depth of outlined objects in separate buffer
|
|
||||||
const auto drawOutlineDepthInputs = DrawOutlineDepth::Inputs(sortedShapes, sceneFrameBuffer).asVarying();
|
|
||||||
const auto outlinedFrameBuffer = task.addJob<DrawOutlineDepth>("OutlineDepth", drawOutlineDepthInputs, shapePlumberZPass);
|
|
||||||
|
|
||||||
// Draw outline
|
|
||||||
const auto drawOutlineInputs = DrawOutline::Inputs(deferredFrameTransform, outlinedFrameBuffer, sceneFrameBuffer, primaryFramebuffer).asVarying();
|
|
||||||
task.addJob<DrawOutline>("OutlineEffect", drawOutlineInputs);
|
|
||||||
|
|
||||||
// Debug outline
|
|
||||||
task.addJob<DebugOutline>("OutlineDebug", outlinedFrameBuffer);
|
|
||||||
}
|
|
|
@ -1,183 +0,0 @@
|
||||||
//
|
|
||||||
// OutlineEffect.h
|
|
||||||
// render-utils/src/
|
|
||||||
//
|
|
||||||
// Created by Olivier Prat on 08/08/17.
|
|
||||||
// Copyright 2017 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_render_utils_OutlineEffect_h
|
|
||||||
#define hifi_render_utils_OutlineEffect_h
|
|
||||||
|
|
||||||
#include <render/Engine.h>
|
|
||||||
#include "DeferredFramebuffer.h"
|
|
||||||
#include "DeferredFrameTransform.h"
|
|
||||||
|
|
||||||
class OutlineFramebuffer {
|
|
||||||
public:
|
|
||||||
OutlineFramebuffer();
|
|
||||||
|
|
||||||
gpu::FramebufferPointer getDepthFramebuffer();
|
|
||||||
gpu::TexturePointer getDepthTexture();
|
|
||||||
|
|
||||||
// Update the source framebuffer size which will drive the allocation of all the other resources.
|
|
||||||
void update(const gpu::TexturePointer& colorBuffer);
|
|
||||||
const glm::ivec2& getSourceFrameSize() const { return _frameSize; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
void clear();
|
|
||||||
void allocate();
|
|
||||||
|
|
||||||
gpu::FramebufferPointer _depthFramebuffer;
|
|
||||||
gpu::TexturePointer _depthTexture;
|
|
||||||
|
|
||||||
glm::ivec2 _frameSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
using OutlineFramebufferPointer = std::shared_ptr<OutlineFramebuffer>;
|
|
||||||
|
|
||||||
class DrawOutlineDepth {
|
|
||||||
public:
|
|
||||||
|
|
||||||
using Inputs = render::VaryingSet2<render::ShapeBounds, DeferredFramebufferPointer>;
|
|
||||||
// Output will contain outlined objects only z-depth texture and the input primary buffer but without the primary depth buffer
|
|
||||||
using Outputs = OutlineFramebufferPointer;
|
|
||||||
using JobModel = render::Job::ModelIO<DrawOutlineDepth, Inputs, Outputs>;
|
|
||||||
|
|
||||||
DrawOutlineDepth(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {}
|
|
||||||
|
|
||||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
render::ShapePlumberPointer _shapePlumber;
|
|
||||||
OutlineFramebufferPointer _outlineFramebuffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DrawOutlineConfig : public render::Job::Config {
|
|
||||||
Q_OBJECT
|
|
||||||
Q_PROPERTY(bool glow MEMBER glow NOTIFY dirty)
|
|
||||||
Q_PROPERTY(float width MEMBER width NOTIFY dirty)
|
|
||||||
Q_PROPERTY(float intensity MEMBER intensity NOTIFY dirty)
|
|
||||||
Q_PROPERTY(float colorR READ getColorR WRITE setColorR NOTIFY dirty)
|
|
||||||
Q_PROPERTY(float colorG READ getColorG WRITE setColorG NOTIFY dirty)
|
|
||||||
Q_PROPERTY(float colorB READ getColorB WRITE setColorB NOTIFY dirty)
|
|
||||||
Q_PROPERTY(float fillOpacityUnoccluded MEMBER fillOpacityUnoccluded NOTIFY dirty)
|
|
||||||
Q_PROPERTY(float fillOpacityOccluded MEMBER fillOpacityOccluded NOTIFY dirty)
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
void setColorR(float value) { color.r = value; emit dirty(); }
|
|
||||||
float getColorR() const { return color.r; }
|
|
||||||
|
|
||||||
void setColorG(float value) { color.g = value; emit dirty(); }
|
|
||||||
float getColorG() const { return color.g; }
|
|
||||||
|
|
||||||
void setColorB(float value) { color.b = value; emit dirty(); }
|
|
||||||
float getColorB() const { return color.b; }
|
|
||||||
|
|
||||||
glm::vec3 color{ 1.f, 0.7f, 0.2f };
|
|
||||||
float width{ 2.0f };
|
|
||||||
float intensity{ 0.9f };
|
|
||||||
float fillOpacityUnoccluded{ 0.0f };
|
|
||||||
float fillOpacityOccluded{ 0.0f };
|
|
||||||
bool glow{ false };
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void dirty();
|
|
||||||
};
|
|
||||||
|
|
||||||
class DrawOutline {
|
|
||||||
public:
|
|
||||||
using Inputs = render::VaryingSet4<DeferredFrameTransformPointer, OutlineFramebufferPointer, DeferredFramebufferPointer, gpu::FramebufferPointer>;
|
|
||||||
using Config = DrawOutlineConfig;
|
|
||||||
using JobModel = render::Job::ModelI<DrawOutline, Inputs, Config>;
|
|
||||||
|
|
||||||
DrawOutline();
|
|
||||||
|
|
||||||
void configure(const Config& config);
|
|
||||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
enum {
|
|
||||||
SCENE_DEPTH_SLOT = 0,
|
|
||||||
OUTLINED_DEPTH_SLOT,
|
|
||||||
|
|
||||||
OUTLINE_PARAMS_SLOT = 0,
|
|
||||||
FRAME_TRANSFORM_SLOT
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "Outline_shared.slh"
|
|
||||||
|
|
||||||
using OutlineConfigurationBuffer = gpu::StructBuffer<OutlineParameters>;
|
|
||||||
|
|
||||||
const gpu::PipelinePointer& getPipeline(bool isFilled);
|
|
||||||
|
|
||||||
gpu::FramebufferPointer _primaryWithoutDepthBuffer;
|
|
||||||
glm::ivec2 _frameBufferSize {0, 0};
|
|
||||||
gpu::PipelinePointer _pipeline;
|
|
||||||
gpu::PipelinePointer _pipelineFilled;
|
|
||||||
OutlineConfigurationBuffer _configuration;
|
|
||||||
glm::vec3 _color;
|
|
||||||
float _size;
|
|
||||||
int _blurKernelSize;
|
|
||||||
float _intensity;
|
|
||||||
float _fillOpacityUnoccluded;
|
|
||||||
float _fillOpacityOccluded;
|
|
||||||
float _threshold;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DebugOutlineConfig : public render::Job::Config {
|
|
||||||
Q_OBJECT
|
|
||||||
Q_PROPERTY(bool viewOutlinedDepth MEMBER viewOutlinedDepth NOTIFY dirty)
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
bool viewOutlinedDepth{ false };
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void dirty();
|
|
||||||
};
|
|
||||||
|
|
||||||
class DebugOutline {
|
|
||||||
public:
|
|
||||||
using Inputs = OutlineFramebufferPointer;
|
|
||||||
using Config = DebugOutlineConfig;
|
|
||||||
using JobModel = render::Job::ModelI<DebugOutline, Inputs, Config>;
|
|
||||||
|
|
||||||
DebugOutline();
|
|
||||||
~DebugOutline();
|
|
||||||
|
|
||||||
void configure(const Config& config);
|
|
||||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
const gpu::PipelinePointer& getDebugPipeline();
|
|
||||||
|
|
||||||
gpu::PipelinePointer _debugPipeline;
|
|
||||||
int _geometryId{ 0 };
|
|
||||||
bool _isDisplayDepthEnabled{ false };
|
|
||||||
};
|
|
||||||
|
|
||||||
class DrawOutlineTask {
|
|
||||||
public:
|
|
||||||
using Inputs = render::VaryingSet5<render::ItemBounds, render::ShapePlumberPointer, DeferredFramebufferPointer, gpu::FramebufferPointer, DeferredFrameTransformPointer>;
|
|
||||||
using Config = render::Task::Config;
|
|
||||||
using JobModel = render::Task::ModelI<DrawOutlineTask, Inputs, Config>;
|
|
||||||
|
|
||||||
DrawOutlineTask();
|
|
||||||
|
|
||||||
void configure(const Config& config);
|
|
||||||
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // hifi_render_utils_OutlineEffect_h
|
|
||||||
|
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
// glsl / C++ compatible source as interface for Outline
|
|
||||||
#ifdef __cplusplus
|
|
||||||
# define VEC2 glm::vec2
|
|
||||||
# define VEC3 glm::vec3
|
|
||||||
#else
|
|
||||||
# define VEC2 vec2
|
|
||||||
# define VEC3 vec3
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct OutlineParameters
|
|
||||||
{
|
|
||||||
VEC3 _color;
|
|
||||||
float _intensity;
|
|
||||||
|
|
||||||
VEC2 _size;
|
|
||||||
float _fillOpacityUnoccluded;
|
|
||||||
float _fillOpacityOccluded;
|
|
||||||
|
|
||||||
float _threshold;
|
|
||||||
int _blurKernelSize;
|
|
||||||
float padding2;
|
|
||||||
float padding3;
|
|
||||||
};
|
|
||||||
|
|
||||||
// <@if 1@>
|
|
||||||
// Trigger Scribe include
|
|
||||||
// <@endif@> <!def that !>
|
|
||||||
//
|
|
|
@ -35,16 +35,18 @@
|
||||||
#include "TextureCache.h"
|
#include "TextureCache.h"
|
||||||
#include "ZoneRenderer.h"
|
#include "ZoneRenderer.h"
|
||||||
#include "FadeEffect.h"
|
#include "FadeEffect.h"
|
||||||
|
#include "RenderUtilsLogging.h"
|
||||||
|
|
||||||
#include "AmbientOcclusionEffect.h"
|
#include "AmbientOcclusionEffect.h"
|
||||||
#include "AntialiasingEffect.h"
|
#include "AntialiasingEffect.h"
|
||||||
#include "ToneMappingEffect.h"
|
#include "ToneMappingEffect.h"
|
||||||
#include "SubsurfaceScattering.h"
|
#include "SubsurfaceScattering.h"
|
||||||
#include "DrawHaze.h"
|
#include "DrawHaze.h"
|
||||||
#include "OutlineEffect.h"
|
#include "HighlightEffect.h"
|
||||||
|
|
||||||
#include <gpu/StandardShaderLib.h>
|
#include <gpu/StandardShaderLib.h>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
using namespace render;
|
using namespace render;
|
||||||
extern void initOverlay3DPipelines(render::ShapePlumber& plumber, bool depthTest = false);
|
extern void initOverlay3DPipelines(render::ShapePlumber& plumber, bool depthTest = false);
|
||||||
|
@ -58,6 +60,18 @@ void RenderDeferredTask::configure(const Config& config)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const render::Varying RenderDeferredTask::addSelectItemJobs(JobModel& task, const char* selectionName,
|
||||||
|
const render::Varying& metas,
|
||||||
|
const render::Varying& opaques,
|
||||||
|
const render::Varying& transparents) {
|
||||||
|
const auto selectMetaInput = SelectItems::Inputs(metas, Varying(), std::string()).asVarying();
|
||||||
|
const auto selectedMetas = task.addJob<SelectItems>("MetaSelection", selectMetaInput, selectionName);
|
||||||
|
const auto selectMetaAndOpaqueInput = SelectItems::Inputs(opaques, selectedMetas, std::string()).asVarying();
|
||||||
|
const auto selectedMetasAndOpaques = task.addJob<SelectItems>("OpaqueSelection", selectMetaAndOpaqueInput, selectionName);
|
||||||
|
const auto selectItemInput = SelectItems::Inputs(transparents, selectedMetasAndOpaques, std::string()).asVarying();
|
||||||
|
return task.addJob<SelectItems>("TransparentSelection", selectItemInput, selectionName);
|
||||||
|
}
|
||||||
|
|
||||||
void RenderDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output) {
|
void RenderDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output) {
|
||||||
const auto& items = input.get<Input>();
|
const auto& items = input.get<Input>();
|
||||||
auto fadeEffect = DependencyManager::get<FadeEffect>();
|
auto fadeEffect = DependencyManager::get<FadeEffect>();
|
||||||
|
@ -95,15 +109,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
||||||
// draw a stencil mask in hidden regions of the framebuffer.
|
// draw a stencil mask in hidden regions of the framebuffer.
|
||||||
task.addJob<PrepareStencil>("PrepareStencil", primaryFramebuffer);
|
task.addJob<PrepareStencil>("PrepareStencil", primaryFramebuffer);
|
||||||
|
|
||||||
// Select items that need to be outlined
|
|
||||||
const auto selectionName = "contextOverlayHighlightList";
|
|
||||||
const auto selectMetaInput = SelectItems::Inputs(metas, Varying()).asVarying();
|
|
||||||
const auto selectedMetas = task.addJob<SelectItems>("PassTestMetaSelection", selectMetaInput, selectionName);
|
|
||||||
const auto selectMetaAndOpaqueInput = SelectItems::Inputs(opaques, selectedMetas).asVarying();
|
|
||||||
const auto selectedMetasAndOpaques = task.addJob<SelectItems>("PassTestOpaqueSelection", selectMetaAndOpaqueInput, selectionName);
|
|
||||||
const auto selectItemInput = SelectItems::Inputs(transparents, selectedMetasAndOpaques).asVarying();
|
|
||||||
const auto selectedItems = task.addJob<SelectItems>("PassTestTransparentSelection", selectItemInput, selectionName);
|
|
||||||
|
|
||||||
// Render opaque objects in DeferredBuffer
|
// Render opaque objects in DeferredBuffer
|
||||||
const auto opaqueInputs = DrawStateSortDeferred::Inputs(opaques, lightingModel).asVarying();
|
const auto opaqueInputs = DrawStateSortDeferred::Inputs(opaques, lightingModel).asVarying();
|
||||||
task.addJob<DrawStateSortDeferred>("DrawOpaqueDeferred", opaqueInputs, shapePlumber);
|
task.addJob<DrawStateSortDeferred>("DrawOpaqueDeferred", opaqueInputs, shapePlumber);
|
||||||
|
@ -178,10 +183,15 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
||||||
const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer).asVarying();
|
const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer).asVarying();
|
||||||
task.addJob<ToneMappingDeferred>("ToneMapping", toneMappingInputs);
|
task.addJob<ToneMappingDeferred>("ToneMapping", toneMappingInputs);
|
||||||
|
|
||||||
const auto outlineRangeTimer = task.addJob<BeginGPURangeTimer>("BeginOutlineRangeTimer", "Outline");
|
const auto outlineRangeTimer = task.addJob<BeginGPURangeTimer>("BeginHighlightRangeTimer", "Highlight");
|
||||||
const auto outlineInputs = DrawOutlineTask::Inputs(selectedItems, shapePlumber, deferredFramebuffer, primaryFramebuffer, deferredFrameTransform).asVarying();
|
// Select items that need to be outlined
|
||||||
task.addJob<DrawOutlineTask>("DrawOutline", outlineInputs);
|
const auto selectionBaseName = "contextOverlayHighlightList";
|
||||||
task.addJob<EndGPURangeTimer>("EndOutlineRangeTimer", outlineRangeTimer);
|
const auto selectedItems = addSelectItemJobs(task, selectionBaseName, metas, opaques, transparents);
|
||||||
|
|
||||||
|
const auto outlineInputs = DrawHighlightTask::Inputs(items.get0(), deferredFramebuffer, primaryFramebuffer, deferredFrameTransform).asVarying();
|
||||||
|
task.addJob<DrawHighlightTask>("DrawHighlight", outlineInputs);
|
||||||
|
|
||||||
|
task.addJob<EndGPURangeTimer>("HighlightRangeTimer", outlineRangeTimer);
|
||||||
|
|
||||||
{ // DEbug the bounds of the rendered items, still look at the zbuffer
|
{ // DEbug the bounds of the rendered items, still look at the zbuffer
|
||||||
task.addJob<DrawBounds>("DrawMetaBounds", metas);
|
task.addJob<DrawBounds>("DrawMetaBounds", metas);
|
||||||
|
@ -190,6 +200,9 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
||||||
|
|
||||||
task.addJob<DrawBounds>("DrawLightBounds", lights);
|
task.addJob<DrawBounds>("DrawLightBounds", lights);
|
||||||
task.addJob<DrawBounds>("DrawZones", zones);
|
task.addJob<DrawBounds>("DrawZones", zones);
|
||||||
|
|
||||||
|
// Render.getConfig("RenderMainView.DrawSelectionBounds").enabled = true
|
||||||
|
task.addJob<DrawBounds>("DrawSelectionBounds", selectedItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Layered Overlays
|
// Layered Overlays
|
||||||
|
@ -236,9 +249,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
||||||
}
|
}
|
||||||
|
|
||||||
task.addJob<DebugZoneLighting>("DrawZoneStack", deferredFrameTransform);
|
task.addJob<DebugZoneLighting>("DrawZoneStack", deferredFrameTransform);
|
||||||
|
|
||||||
// Render.getConfig("RenderMainView.DrawSelectionBounds").enabled = true
|
|
||||||
task.addJob<DrawBounds>("DrawSelectionBounds", selectedItems);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AA job to be revisited
|
// AA job to be revisited
|
||||||
|
@ -454,6 +464,7 @@ void Blit::run(const RenderContextPointer& renderContext, const gpu::Framebuffer
|
||||||
auto blitFbo = renderArgs->_blitFramebuffer;
|
auto blitFbo = renderArgs->_blitFramebuffer;
|
||||||
|
|
||||||
if (!blitFbo) {
|
if (!blitFbo) {
|
||||||
|
qCWarning(renderutils) << "Blit::run - no blit frame buffer.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -198,6 +198,10 @@ public:
|
||||||
void configure(const Config& config);
|
void configure(const Config& config);
|
||||||
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs);
|
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
static const render::Varying addSelectItemJobs(JobModel& task, const char* selectionName,
|
||||||
|
const render::Varying& metas, const render::Varying& opaques, const render::Varying& transparents);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_RenderDeferredTask_h
|
#endif // hifi_RenderDeferredTask_h
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "BackgroundStage.h"
|
#include "BackgroundStage.h"
|
||||||
#include "HazeStage.h"
|
#include "HazeStage.h"
|
||||||
#include <render/TransitionStage.h>
|
#include <render/TransitionStage.h>
|
||||||
|
#include <render/HighlightStage.h>
|
||||||
#include "DeferredLightingEffect.h"
|
#include "DeferredLightingEffect.h"
|
||||||
|
|
||||||
void UpdateSceneTask::build(JobModel& task, const render::Varying& input, render::Varying& output) {
|
void UpdateSceneTask::build(JobModel& task, const render::Varying& input, render::Varying& output) {
|
||||||
|
@ -22,6 +23,7 @@ void UpdateSceneTask::build(JobModel& task, const render::Varying& input, render
|
||||||
task.addJob<BackgroundStageSetup>("BackgroundStageSetup");
|
task.addJob<BackgroundStageSetup>("BackgroundStageSetup");
|
||||||
task.addJob<HazeStageSetup>("HazeStageSetup");
|
task.addJob<HazeStageSetup>("HazeStageSetup");
|
||||||
task.addJob<render::TransitionStageSetup>("TransitionStageSetup");
|
task.addJob<render::TransitionStageSetup>("TransitionStageSetup");
|
||||||
|
task.addJob<render::HighlightStageSetup>("HighlightStageSetup");
|
||||||
|
|
||||||
task.addJob<DefaultLightingSetup>("DefaultLightingSetup");
|
task.addJob<DefaultLightingSetup>("DefaultLightingSetup");
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,12 @@ void SliceItems::run(const RenderContextPointer& renderContext, const ItemBounds
|
||||||
}
|
}
|
||||||
|
|
||||||
void SelectItems::run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems) {
|
void SelectItems::run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems) {
|
||||||
auto selection = renderContext->_scene->getSelection(_name);
|
auto selectionName{ _name };
|
||||||
|
if (!inputs.get2().empty()) {
|
||||||
|
selectionName = inputs.get2();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto selection = renderContext->_scene->getSelection(selectionName);
|
||||||
const auto& selectedItems = selection.getItems();
|
const auto& selectedItems = selection.getItems();
|
||||||
const auto& inItems = inputs.get0();
|
const auto& inItems = inputs.get0();
|
||||||
const auto itemsToAppend = inputs[1];
|
const auto itemsToAppend = inputs[1];
|
||||||
|
@ -118,8 +123,9 @@ void MetaToSubItems::run(const RenderContextPointer& renderContext, const ItemBo
|
||||||
|
|
||||||
for (auto idBound : inItems) {
|
for (auto idBound : inItems) {
|
||||||
auto& item = scene->getItem(idBound.id);
|
auto& item = scene->getItem(idBound.id);
|
||||||
|
if (item.exist()) {
|
||||||
item.fetchMetaSubItems(outItems);
|
item.fetchMetaSubItems(outItems);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,8 +138,9 @@ void IDsToBounds::run(const RenderContextPointer& renderContext, const ItemIDs&
|
||||||
if (!_disableAABBs) {
|
if (!_disableAABBs) {
|
||||||
for (auto id : inItems) {
|
for (auto id : inItems) {
|
||||||
auto& item = scene->getItem(id);
|
auto& item = scene->getItem(id);
|
||||||
|
if (item.exist()) {
|
||||||
outItems.emplace_back(ItemBound{ id, item.getBound() });
|
outItems.emplace_back(ItemBound{ id, item.getBound() });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (auto id : inItems) {
|
for (auto id : inItems) {
|
||||||
|
|
|
@ -113,12 +113,13 @@ namespace render {
|
||||||
// Keep items belonging to the job selection
|
// Keep items belonging to the job selection
|
||||||
class SelectItems {
|
class SelectItems {
|
||||||
public:
|
public:
|
||||||
using Inputs = VaryingSet2<ItemBounds, ItemBounds>;
|
using Inputs = VaryingSet3<ItemBounds, ItemBounds, std::string>;
|
||||||
using JobModel = Job::ModelIO<SelectItems, Inputs, ItemBounds>;
|
using JobModel = Job::ModelIO<SelectItems, Inputs, ItemBounds>;
|
||||||
|
|
||||||
std::string _name;
|
std::string _name;
|
||||||
|
SelectItems() {}
|
||||||
SelectItems(const Selection::Name& name) : _name(name) {}
|
SelectItems(const Selection::Name& name) : _name(name) {}
|
||||||
|
|
||||||
void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems);
|
void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
130
libraries/render/src/render/HighlightStage.cpp
Normal file
130
libraries/render/src/render/HighlightStage.cpp
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
#include "HighlightStage.h"
|
||||||
|
|
||||||
|
using namespace render;
|
||||||
|
|
||||||
|
std::string HighlightStage::_name("Highlight");
|
||||||
|
const HighlightStage::Index HighlightStage::INVALID_INDEX{ render::indexed_container::INVALID_INDEX };
|
||||||
|
|
||||||
|
HighlightStage::Index HighlightStage::addHighlight(const std::string& selectionName, const HighlightStyle& style) {
|
||||||
|
Highlight outline{ selectionName, style };
|
||||||
|
Index id;
|
||||||
|
|
||||||
|
id = _highlights.newElement(outline);
|
||||||
|
_activeHighlightIds.push_back(id);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HighlightStage::removeHighlight(Index index) {
|
||||||
|
HighlightIdList::iterator idIterator = std::find(_activeHighlightIds.begin(), _activeHighlightIds.end(), index);
|
||||||
|
if (idIterator != _activeHighlightIds.end()) {
|
||||||
|
_activeHighlightIds.erase(idIterator);
|
||||||
|
}
|
||||||
|
if (!_highlights.isElementFreed(index)) {
|
||||||
|
_highlights.freeElement(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Index HighlightStage::getHighlightIdBySelection(const std::string& selectionName) const {
|
||||||
|
for (auto outlineId : _activeHighlightIds) {
|
||||||
|
const auto& outline = _highlights.get(outlineId);
|
||||||
|
if (outline._selectionName == selectionName) {
|
||||||
|
return outlineId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return INVALID_INDEX;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HighlightStyle& HighlightStageConfig::getStyle() const {
|
||||||
|
auto styleIterator = _styles.find(_selectionName);
|
||||||
|
if (styleIterator != _styles.end()) {
|
||||||
|
return styleIterator->second;
|
||||||
|
} else {
|
||||||
|
auto insertion = _styles.insert(SelectionStyles::value_type{ _selectionName, HighlightStyle{} });
|
||||||
|
return insertion.first->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HighlightStyle& HighlightStageConfig::editStyle() {
|
||||||
|
auto styleIterator = _styles.find(_selectionName);
|
||||||
|
if (styleIterator != _styles.end()) {
|
||||||
|
return styleIterator->second;
|
||||||
|
} else {
|
||||||
|
auto insertion = _styles.insert(SelectionStyles::value_type{ _selectionName, HighlightStyle{} });
|
||||||
|
return insertion.first->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HighlightStageConfig::setSelectionName(const QString& name) {
|
||||||
|
_selectionName = name.toStdString();
|
||||||
|
emit dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HighlightStageConfig::setOutlineSmooth(bool isSmooth) {
|
||||||
|
editStyle().isOutlineSmooth = isSmooth;
|
||||||
|
emit dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HighlightStageConfig::setColorRed(float value) {
|
||||||
|
editStyle().color.r = value;
|
||||||
|
emit dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HighlightStageConfig::setColorGreen(float value) {
|
||||||
|
editStyle().color.g = value;
|
||||||
|
emit dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HighlightStageConfig::setColorBlue(float value) {
|
||||||
|
editStyle().color.b = value;
|
||||||
|
emit dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HighlightStageConfig::setOutlineWidth(float value) {
|
||||||
|
editStyle().outlineWidth = value;
|
||||||
|
emit dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HighlightStageConfig::setOutlineIntensity(float value) {
|
||||||
|
editStyle().outlineIntensity = value;
|
||||||
|
emit dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HighlightStageConfig::setUnoccludedFillOpacity(float value) {
|
||||||
|
editStyle().unoccludedFillOpacity = value;
|
||||||
|
emit dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HighlightStageConfig::setOccludedFillOpacity(float value) {
|
||||||
|
editStyle().occludedFillOpacity = value;
|
||||||
|
emit dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
HighlightStageSetup::HighlightStageSetup() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void HighlightStageSetup::configure(const Config& config) {
|
||||||
|
// Copy the styles here but update the stage with the new styles in run to be sure everything is
|
||||||
|
// thread safe...
|
||||||
|
_styles = config._styles;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HighlightStageSetup::run(const render::RenderContextPointer& renderContext) {
|
||||||
|
auto stage = renderContext->_scene->getStage<HighlightStage>(HighlightStage::getName());
|
||||||
|
if (!stage) {
|
||||||
|
stage = std::make_shared<HighlightStage>();
|
||||||
|
renderContext->_scene->resetStage(HighlightStage::getName(), stage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_styles.empty()) {
|
||||||
|
render::Transaction transaction;
|
||||||
|
for (const auto& selection : _styles) {
|
||||||
|
auto& selectionName = selection.first;
|
||||||
|
auto& selectionStyle = selection.second;
|
||||||
|
transaction.resetSelectionHighlight(selectionName, selectionStyle);
|
||||||
|
}
|
||||||
|
renderContext->_scene->enqueueTransaction(transaction);
|
||||||
|
_styles.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
135
libraries/render/src/render/HighlightStage.h
Normal file
135
libraries/render/src/render/HighlightStage.h
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
//
|
||||||
|
// HighlightStage.h
|
||||||
|
|
||||||
|
// Created by Olivier Prat on 07/07/2017.
|
||||||
|
// Copyright 2017 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_render_utils_HighlightStage_h
|
||||||
|
#define hifi_render_utils_HighlightStage_h
|
||||||
|
|
||||||
|
#include "Stage.h"
|
||||||
|
#include "Engine.h"
|
||||||
|
#include "IndexedContainer.h"
|
||||||
|
#include "HighlightStyle.h"
|
||||||
|
|
||||||
|
namespace render {
|
||||||
|
|
||||||
|
// Highlight stage to set up HighlightStyle-related effects
|
||||||
|
class HighlightStage : public Stage {
|
||||||
|
public:
|
||||||
|
|
||||||
|
class Highlight {
|
||||||
|
public:
|
||||||
|
|
||||||
|
Highlight(const std::string& selectionName, const HighlightStyle& style) : _selectionName{ selectionName }, _style{ style } { }
|
||||||
|
|
||||||
|
std::string _selectionName;
|
||||||
|
HighlightStyle _style;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::string& getName() { return _name; }
|
||||||
|
|
||||||
|
using Index = render::indexed_container::Index;
|
||||||
|
static const Index INVALID_INDEX;
|
||||||
|
using HighlightIdList = render::indexed_container::Indices;
|
||||||
|
|
||||||
|
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
|
||||||
|
|
||||||
|
bool checkHighlightId(Index index) const { return _highlights.checkIndex(index); }
|
||||||
|
|
||||||
|
const Highlight& getHighlight(Index index) const { return _highlights.get(index); }
|
||||||
|
Highlight& editHighlight(Index index) { return _highlights.edit(index); }
|
||||||
|
|
||||||
|
Index addHighlight(const std::string& selectionName, const HighlightStyle& style = HighlightStyle());
|
||||||
|
Index getHighlightIdBySelection(const std::string& selectionName) const;
|
||||||
|
void removeHighlight(Index index);
|
||||||
|
|
||||||
|
HighlightIdList::iterator begin() { return _activeHighlightIds.begin(); }
|
||||||
|
HighlightIdList::iterator end() { return _activeHighlightIds.end(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
using Highlights = render::indexed_container::IndexedVector<Highlight>;
|
||||||
|
|
||||||
|
static std::string _name;
|
||||||
|
|
||||||
|
Highlights _highlights;
|
||||||
|
HighlightIdList _activeHighlightIds;
|
||||||
|
};
|
||||||
|
using HighlightStagePointer = std::shared_ptr<HighlightStage>;
|
||||||
|
|
||||||
|
class HighlightStageConfig : public render::Job::Config {
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QString selectionName READ getSelectionName WRITE setSelectionName NOTIFY dirty)
|
||||||
|
Q_PROPERTY(bool isOutlineSmooth READ isOutlineSmooth WRITE setOutlineSmooth NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float colorR READ getColorRed WRITE setColorRed NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float colorG READ getColorGreen WRITE setColorGreen NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float colorB READ getColorBlue WRITE setColorBlue NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float outlineWidth READ getOutlineWidth WRITE setOutlineWidth NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float outlineIntensity READ getOutlineIntensity WRITE setOutlineIntensity NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float unoccludedFillOpacity READ getUnoccludedFillOpacity WRITE setUnoccludedFillOpacity NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float occludedFillOpacity READ getOccludedFillOpacity WRITE setOccludedFillOpacity NOTIFY dirty)
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
using SelectionStyles = std::map<std::string, HighlightStyle>;
|
||||||
|
|
||||||
|
QString getSelectionName() const { return QString(_selectionName.c_str()); }
|
||||||
|
void setSelectionName(const QString& name);
|
||||||
|
|
||||||
|
bool isOutlineSmooth() const { return getStyle().isOutlineSmooth; }
|
||||||
|
void setOutlineSmooth(bool isSmooth);
|
||||||
|
|
||||||
|
float getColorRed() const { return getStyle().color.r; }
|
||||||
|
void setColorRed(float value);
|
||||||
|
|
||||||
|
float getColorGreen() const { return getStyle().color.g; }
|
||||||
|
void setColorGreen(float value);
|
||||||
|
|
||||||
|
float getColorBlue() const { return getStyle().color.b; }
|
||||||
|
void setColorBlue(float value);
|
||||||
|
|
||||||
|
float getOutlineWidth() const { return getStyle().outlineWidth; }
|
||||||
|
void setOutlineWidth(float value);
|
||||||
|
|
||||||
|
float getOutlineIntensity() const { return getStyle().outlineIntensity; }
|
||||||
|
void setOutlineIntensity(float value);
|
||||||
|
|
||||||
|
float getUnoccludedFillOpacity() const { return getStyle().unoccludedFillOpacity; }
|
||||||
|
void setUnoccludedFillOpacity(float value);
|
||||||
|
|
||||||
|
float getOccludedFillOpacity() const { return getStyle().occludedFillOpacity; }
|
||||||
|
void setOccludedFillOpacity(float value);
|
||||||
|
|
||||||
|
std::string _selectionName{ "contextOverlayHighlightList" };
|
||||||
|
mutable SelectionStyles _styles;
|
||||||
|
|
||||||
|
const HighlightStyle& getStyle() const;
|
||||||
|
HighlightStyle& editStyle();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void dirty();
|
||||||
|
};
|
||||||
|
|
||||||
|
class HighlightStageSetup {
|
||||||
|
public:
|
||||||
|
using Config = HighlightStageConfig;
|
||||||
|
using JobModel = render::Job::Model<HighlightStageSetup, Config>;
|
||||||
|
|
||||||
|
HighlightStageSetup();
|
||||||
|
|
||||||
|
void configure(const Config& config);
|
||||||
|
void run(const RenderContextPointer& renderContext);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
HighlightStageConfig::SelectionStyles _styles;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif // hifi_render_utils_HighlightStage_h
|
38
libraries/render/src/render/HighlightStyle.h
Normal file
38
libraries/render/src/render/HighlightStyle.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
//
|
||||||
|
// HighlightStyle.h
|
||||||
|
|
||||||
|
// Created by Olivier Prat on 11/06/2017.
|
||||||
|
// Copyright 2017 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_render_utils_HighlightStyle_h
|
||||||
|
#define hifi_render_utils_HighlightStyle_h
|
||||||
|
|
||||||
|
#include <glm/vec3.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace render {
|
||||||
|
|
||||||
|
// This holds the configuration for a particular outline style
|
||||||
|
class HighlightStyle {
|
||||||
|
public:
|
||||||
|
|
||||||
|
bool isFilled() const {
|
||||||
|
return unoccludedFillOpacity > 5e-3f || occludedFillOpacity > 5e-3f;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 color{ 1.f, 0.7f, 0.2f };
|
||||||
|
float outlineWidth{ 2.0f };
|
||||||
|
float outlineIntensity{ 0.9f };
|
||||||
|
float unoccludedFillOpacity{ 0.0f };
|
||||||
|
float occludedFillOpacity{ 0.0f };
|
||||||
|
bool isOutlineSmooth{ false };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // hifi_render_utils_HighlightStyle_h
|
|
@ -14,6 +14,7 @@
|
||||||
#include <gpu/Batch.h>
|
#include <gpu/Batch.h>
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
#include "TransitionStage.h"
|
#include "TransitionStage.h"
|
||||||
|
#include "HighlightStage.h"
|
||||||
|
|
||||||
using namespace render;
|
using namespace render;
|
||||||
|
|
||||||
|
@ -54,6 +55,18 @@ void Transaction::resetSelection(const Selection& selection) {
|
||||||
_resetSelections.emplace_back(selection);
|
_resetSelections.emplace_back(selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Transaction::resetSelectionHighlight(const std::string& selectionName, const HighlightStyle& style) {
|
||||||
|
_highlightResets.emplace_back(HighlightReset{ selectionName, style });
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transaction::removeHighlightFromSelection(const std::string& selectionName) {
|
||||||
|
_highlightRemoves.emplace_back(selectionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transaction::querySelectionHighlight(const std::string& selectionName, SelectionHighlightQueryFunc func) {
|
||||||
|
_highlightQueries.emplace_back(HighlightQuery{ selectionName, func });
|
||||||
|
}
|
||||||
|
|
||||||
void Transaction::merge(const Transaction& transaction) {
|
void Transaction::merge(const Transaction& transaction) {
|
||||||
_resetItems.insert(_resetItems.end(), transaction._resetItems.begin(), transaction._resetItems.end());
|
_resetItems.insert(_resetItems.end(), transaction._resetItems.begin(), transaction._resetItems.end());
|
||||||
_removedItems.insert(_removedItems.end(), transaction._removedItems.begin(), transaction._removedItems.end());
|
_removedItems.insert(_removedItems.end(), transaction._removedItems.begin(), transaction._removedItems.end());
|
||||||
|
@ -62,6 +75,9 @@ void Transaction::merge(const Transaction& transaction) {
|
||||||
_addedTransitions.insert(_addedTransitions.end(), transaction._addedTransitions.begin(), transaction._addedTransitions.end());
|
_addedTransitions.insert(_addedTransitions.end(), transaction._addedTransitions.begin(), transaction._addedTransitions.end());
|
||||||
_queriedTransitions.insert(_queriedTransitions.end(), transaction._queriedTransitions.begin(), transaction._queriedTransitions.end());
|
_queriedTransitions.insert(_queriedTransitions.end(), transaction._queriedTransitions.begin(), transaction._queriedTransitions.end());
|
||||||
_reAppliedTransitions.insert(_reAppliedTransitions.end(), transaction._reAppliedTransitions.begin(), transaction._reAppliedTransitions.end());
|
_reAppliedTransitions.insert(_reAppliedTransitions.end(), transaction._reAppliedTransitions.begin(), transaction._reAppliedTransitions.end());
|
||||||
|
_highlightResets.insert(_highlightResets.end(), transaction._highlightResets.begin(), transaction._highlightResets.end());
|
||||||
|
_highlightRemoves.insert(_highlightRemoves.end(), transaction._highlightRemoves.begin(), transaction._highlightRemoves.end());
|
||||||
|
_highlightQueries.insert(_highlightQueries.end(), transaction._highlightQueries.begin(), transaction._highlightQueries.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -176,6 +192,10 @@ void Scene::processTransactionFrame(const Transaction& transaction) {
|
||||||
// resets and potential NEW items
|
// resets and potential NEW items
|
||||||
resetSelections(transaction._resetSelections);
|
resetSelections(transaction._resetSelections);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetHighlights(transaction._highlightResets);
|
||||||
|
removeHighlights(transaction._highlightRemoves);
|
||||||
|
queryHighlights(transaction._highlightQueries);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::resetItems(const Transaction::Resets& transactions) {
|
void Scene::resetItems(const Transaction::Resets& transactions) {
|
||||||
|
@ -316,6 +336,50 @@ void Scene::queryTransitionItems(const Transaction::TransitionQueries& transacti
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Scene::resetHighlights(const Transaction::HighlightResets& transactions) {
|
||||||
|
auto outlineStage = getStage<HighlightStage>(HighlightStage::getName());
|
||||||
|
|
||||||
|
for (auto& transaction : transactions) {
|
||||||
|
const auto& selectionName = std::get<0>(transaction);
|
||||||
|
const auto& newStyle = std::get<1>(transaction);
|
||||||
|
auto outlineId = outlineStage->getHighlightIdBySelection(selectionName);
|
||||||
|
|
||||||
|
if (HighlightStage::isIndexInvalid(outlineId)) {
|
||||||
|
outlineStage->addHighlight(selectionName, newStyle);
|
||||||
|
} else {
|
||||||
|
outlineStage->editHighlight(outlineId)._style = newStyle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::removeHighlights(const Transaction::HighlightRemoves& transactions) {
|
||||||
|
auto outlineStage = getStage<HighlightStage>(HighlightStage::getName());
|
||||||
|
|
||||||
|
for (auto& selectionName : transactions) {
|
||||||
|
auto outlineId = outlineStage->getHighlightIdBySelection(selectionName);
|
||||||
|
|
||||||
|
if (!HighlightStage::isIndexInvalid(outlineId)) {
|
||||||
|
outlineStage->removeHighlight(outlineId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::queryHighlights(const Transaction::HighlightQueries& transactions) {
|
||||||
|
auto outlineStage = getStage<HighlightStage>(HighlightStage::getName());
|
||||||
|
|
||||||
|
for (auto& transaction : transactions) {
|
||||||
|
const auto& selectionName = std::get<0>(transaction);
|
||||||
|
const auto& func = std::get<1>(transaction);
|
||||||
|
auto outlineId = outlineStage->getHighlightIdBySelection(selectionName);
|
||||||
|
|
||||||
|
if (!HighlightStage::isIndexInvalid(outlineId)) {
|
||||||
|
func(&outlineStage->editHighlight(outlineId)._style);
|
||||||
|
} else {
|
||||||
|
func(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Scene::collectSubItems(ItemID parentId, ItemIDs& subItems) const {
|
void Scene::collectSubItems(ItemID parentId, ItemIDs& subItems) const {
|
||||||
// Access the true item
|
// Access the true item
|
||||||
auto& item = _items[parentId];
|
auto& item = _items[parentId];
|
||||||
|
@ -362,7 +426,7 @@ void Scene::resetItemTransition(ItemID itemId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// THis fucntion is thread safe
|
// This function is thread safe
|
||||||
Selection Scene::getSelection(const Selection::Name& name) const {
|
Selection Scene::getSelection(const Selection::Name& name) const {
|
||||||
std::unique_lock<std::mutex> lock(_selectionsMutex);
|
std::unique_lock<std::mutex> lock(_selectionsMutex);
|
||||||
auto found = _selections.find(name);
|
auto found = _selections.find(name);
|
||||||
|
@ -373,6 +437,17 @@ Selection Scene::getSelection(const Selection::Name& name) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This function is thread safe
|
||||||
|
bool Scene::isSelectionEmpty(const Selection::Name& name) const {
|
||||||
|
std::unique_lock<std::mutex> lock(_selectionsMutex);
|
||||||
|
auto found = _selections.find(name);
|
||||||
|
if (found == _selections.end()) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return (*found).second.isEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Scene::resetSelections(const Transaction::SelectionResets& transactions) {
|
void Scene::resetSelections(const Transaction::SelectionResets& transactions) {
|
||||||
for (auto selection : transactions) {
|
for (auto selection : transactions) {
|
||||||
auto found = _selections.find(selection.getName());
|
auto found = _selections.find(selection.getName());
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "Stage.h"
|
#include "Stage.h"
|
||||||
#include "Selection.h"
|
#include "Selection.h"
|
||||||
#include "Transition.h"
|
#include "Transition.h"
|
||||||
|
#include "HighlightStyle.h"
|
||||||
|
|
||||||
namespace render {
|
namespace render {
|
||||||
|
|
||||||
|
@ -37,6 +38,7 @@ class Transaction {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
typedef std::function<void(ItemID, const Transition*)> TransitionQueryFunc;
|
typedef std::function<void(ItemID, const Transition*)> TransitionQueryFunc;
|
||||||
|
typedef std::function<void(HighlightStyle const*)> SelectionHighlightQueryFunc;
|
||||||
|
|
||||||
Transaction() {}
|
Transaction() {}
|
||||||
~Transaction() {}
|
~Transaction() {}
|
||||||
|
@ -61,6 +63,10 @@ public:
|
||||||
// Selection transactions
|
// Selection transactions
|
||||||
void resetSelection(const Selection& selection);
|
void resetSelection(const Selection& selection);
|
||||||
|
|
||||||
|
void resetSelectionHighlight(const std::string& selectionName, const HighlightStyle& style = HighlightStyle());
|
||||||
|
void removeHighlightFromSelection(const std::string& selectionName);
|
||||||
|
void querySelectionHighlight(const std::string& selectionName, SelectionHighlightQueryFunc func);
|
||||||
|
|
||||||
void merge(const Transaction& transaction);
|
void merge(const Transaction& transaction);
|
||||||
|
|
||||||
// Checkers if there is work to do when processing the transaction
|
// Checkers if there is work to do when processing the transaction
|
||||||
|
@ -75,6 +81,9 @@ protected:
|
||||||
using TransitionQuery = std::tuple<ItemID, TransitionQueryFunc>;
|
using TransitionQuery = std::tuple<ItemID, TransitionQueryFunc>;
|
||||||
using TransitionReApply = ItemID;
|
using TransitionReApply = ItemID;
|
||||||
using SelectionReset = Selection;
|
using SelectionReset = Selection;
|
||||||
|
using HighlightReset = std::tuple<std::string, HighlightStyle>;
|
||||||
|
using HighlightRemove = std::string;
|
||||||
|
using HighlightQuery = std::tuple<std::string, SelectionHighlightQueryFunc>;
|
||||||
|
|
||||||
using Resets = std::vector<Reset>;
|
using Resets = std::vector<Reset>;
|
||||||
using Removes = std::vector<Remove>;
|
using Removes = std::vector<Remove>;
|
||||||
|
@ -83,6 +92,9 @@ protected:
|
||||||
using TransitionQueries = std::vector<TransitionQuery>;
|
using TransitionQueries = std::vector<TransitionQuery>;
|
||||||
using TransitionReApplies = std::vector<TransitionReApply>;
|
using TransitionReApplies = std::vector<TransitionReApply>;
|
||||||
using SelectionResets = std::vector<SelectionReset>;
|
using SelectionResets = std::vector<SelectionReset>;
|
||||||
|
using HighlightResets = std::vector<HighlightReset>;
|
||||||
|
using HighlightRemoves = std::vector<HighlightRemove>;
|
||||||
|
using HighlightQueries = std::vector<HighlightQuery>;
|
||||||
|
|
||||||
Resets _resetItems;
|
Resets _resetItems;
|
||||||
Removes _removedItems;
|
Removes _removedItems;
|
||||||
|
@ -91,6 +103,9 @@ protected:
|
||||||
TransitionQueries _queriedTransitions;
|
TransitionQueries _queriedTransitions;
|
||||||
TransitionReApplies _reAppliedTransitions;
|
TransitionReApplies _reAppliedTransitions;
|
||||||
SelectionResets _resetSelections;
|
SelectionResets _resetSelections;
|
||||||
|
HighlightResets _highlightResets;
|
||||||
|
HighlightRemoves _highlightRemoves;
|
||||||
|
HighlightQueries _highlightQueries;
|
||||||
};
|
};
|
||||||
typedef std::queue<Transaction> TransactionQueue;
|
typedef std::queue<Transaction> TransactionQueue;
|
||||||
|
|
||||||
|
@ -102,6 +117,7 @@ typedef std::queue<Transaction> TransactionQueue;
|
||||||
// Items are notified accordingly on any update message happening
|
// Items are notified accordingly on any update message happening
|
||||||
class Scene {
|
class Scene {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Scene(glm::vec3 origin, float size);
|
Scene(glm::vec3 origin, float size);
|
||||||
~Scene();
|
~Scene();
|
||||||
|
|
||||||
|
@ -127,6 +143,10 @@ public:
|
||||||
// Thread safe
|
// Thread safe
|
||||||
Selection getSelection(const Selection::Name& name) const;
|
Selection getSelection(const Selection::Name& name) const;
|
||||||
|
|
||||||
|
// Check if a particular selection is empty (returns true if doesn't exist)
|
||||||
|
// Thread safe
|
||||||
|
bool isSelectionEmpty(const Selection::Name& name) const;
|
||||||
|
|
||||||
// This next call are NOT threadsafe, you have to call them from the correct thread to avoid any potential issues
|
// This next call are NOT threadsafe, you have to call them from the correct thread to avoid any potential issues
|
||||||
|
|
||||||
// Access a particular item form its ID
|
// Access a particular item form its ID
|
||||||
|
@ -187,6 +207,9 @@ protected:
|
||||||
void transitionItems(const Transaction::TransitionAdds& transactions);
|
void transitionItems(const Transaction::TransitionAdds& transactions);
|
||||||
void reApplyTransitions(const Transaction::TransitionReApplies& transactions);
|
void reApplyTransitions(const Transaction::TransitionReApplies& transactions);
|
||||||
void queryTransitionItems(const Transaction::TransitionQueries& transactions);
|
void queryTransitionItems(const Transaction::TransitionQueries& transactions);
|
||||||
|
void resetHighlights(const Transaction::HighlightResets& transactions);
|
||||||
|
void removeHighlights(const Transaction::HighlightRemoves& transactions);
|
||||||
|
void queryHighlights(const Transaction::HighlightQueries& transactions);
|
||||||
|
|
||||||
void collectSubItems(ItemID parentId, ItemIDs& subItems) const;
|
void collectSubItems(ItemID parentId, ItemIDs& subItems) const;
|
||||||
|
|
||||||
|
|
|
@ -74,9 +74,13 @@ void render::depthSortItems(const RenderContextPointer& renderContext, bool fron
|
||||||
std::sort(itemBoundSorts.begin(), itemBoundSorts.end(), backToFrontSort);
|
std::sort(itemBoundSorts.begin(), itemBoundSorts.end(), backToFrontSort);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally once sorted result to a list of itemID
|
// Finally once sorted result to a list of itemID and keep uniques
|
||||||
|
render::ItemID previousID = Item::INVALID_ITEM_ID;
|
||||||
for (auto& item : itemBoundSorts) {
|
for (auto& item : itemBoundSorts) {
|
||||||
outItems.emplace_back(ItemBound(item._id, item._bounds));
|
if (item._id != previousID) {
|
||||||
|
outItems.emplace_back(ItemBound(item._id, item._bounds));
|
||||||
|
previousID = item._id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
using namespace render;
|
using namespace render;
|
||||||
|
|
||||||
std::string TransitionStage::_name("Transition");
|
std::string TransitionStage::_name("Transition");
|
||||||
|
const TransitionStage::Index TransitionStage::INVALID_INDEX{ indexed_container::INVALID_INDEX };
|
||||||
|
|
||||||
TransitionStage::Index TransitionStage::addTransition(ItemID itemId, Transition::Type type, ItemID boundId) {
|
TransitionStage::Index TransitionStage::addTransition(ItemID itemId, Transition::Type type, ItemID boundId) {
|
||||||
Transition transition;
|
Transition transition;
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace render {
|
||||||
static const std::string& getName() { return _name; }
|
static const std::string& getName() { return _name; }
|
||||||
|
|
||||||
using Index = indexed_container::Index;
|
using Index = indexed_container::Index;
|
||||||
static const Index INVALID_INDEX{ indexed_container::INVALID_INDEX };
|
static const Index INVALID_INDEX;
|
||||||
using TransitionIdList = indexed_container::Indices;
|
using TransitionIdList = indexed_container::Indices;
|
||||||
|
|
||||||
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
|
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtx/quaternion.hpp>
|
#include <glm/gtx/quaternion.hpp>
|
||||||
|
@ -504,16 +505,17 @@ const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_PROJECTED_POLYGON_VERT
|
||||||
{6, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR}, // back, top, left
|
{6, TOP_RIGHT_NEAR, TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR}, // back, top, left
|
||||||
};
|
};
|
||||||
|
|
||||||
CubeProjectedPolygon ViewFrustum::getProjectedPolygon(const AACube& box) const {
|
template <typename TBOX>
|
||||||
|
CubeProjectedPolygon ViewFrustum::computeProjectedPolygon(const TBOX& box) const {
|
||||||
const glm::vec3& bottomNearRight = box.getCorner();
|
const glm::vec3& bottomNearRight = box.getCorner();
|
||||||
glm::vec3 topFarLeft = box.calcTopFarLeft();
|
glm::vec3 topFarLeft = box.calcTopFarLeft();
|
||||||
|
|
||||||
int lookUp = ((_position.x < bottomNearRight.x) ) // 1 = right | compute 6-bit
|
int lookUp = ((_position.x < bottomNearRight.x)) // 1 = right | compute 6-bit
|
||||||
+ ((_position.x > topFarLeft.x ) << 1) // 2 = left | code to
|
+ ((_position.x > topFarLeft.x) << 1) // 2 = left | code to
|
||||||
+ ((_position.y < bottomNearRight.y) << 2) // 4 = bottom | classify camera
|
+ ((_position.y < bottomNearRight.y) << 2) // 4 = bottom | classify camera
|
||||||
+ ((_position.y > topFarLeft.y ) << 3) // 8 = top | with respect to
|
+ ((_position.y > topFarLeft.y) << 3) // 8 = top | with respect to
|
||||||
+ ((_position.z < bottomNearRight.z) << 4) // 16 = front/near | the 6 defining
|
+ ((_position.z < bottomNearRight.z) << 4) // 16 = front/near | the 6 defining
|
||||||
+ ((_position.z > topFarLeft.z ) << 5); // 32 = back/far | planes
|
+ ((_position.z > topFarLeft.z) << 5); // 32 = back/far | planes
|
||||||
|
|
||||||
int vertexCount = hullVertexLookup[lookUp][0]; //look up number of vertices
|
int vertexCount = hullVertexLookup[lookUp][0]; //look up number of vertices
|
||||||
|
|
||||||
|
@ -524,8 +526,8 @@ CubeProjectedPolygon ViewFrustum::getProjectedPolygon(const AACube& box) const {
|
||||||
bool anyPointsInView = false; // assume the worst!
|
bool anyPointsInView = false; // assume the worst!
|
||||||
if (vertexCount) {
|
if (vertexCount) {
|
||||||
allPointsInView = true; // assume the best!
|
allPointsInView = true; // assume the best!
|
||||||
for(int i = 0; i < vertexCount; i++) {
|
for (int i = 0; i < vertexCount; i++) {
|
||||||
int vertexNum = hullVertexLookup[lookUp][i+1];
|
int vertexNum = hullVertexLookup[lookUp][i + 1];
|
||||||
glm::vec3 point = box.getVertex((BoxVertex)vertexNum);
|
glm::vec3 point = box.getVertex((BoxVertex)vertexNum);
|
||||||
glm::vec2 projectedPoint = projectPoint(point, pointInView);
|
glm::vec2 projectedPoint = projectPoint(point, pointInView);
|
||||||
allPointsInView = allPointsInView && pointInView;
|
allPointsInView = allPointsInView && pointInView;
|
||||||
|
@ -538,24 +540,24 @@ CubeProjectedPolygon ViewFrustum::getProjectedPolygon(const AACube& box) const {
|
||||||
// NOTE: This clipping does not improve our overall performance. It basically causes more polygons to
|
// NOTE: This clipping does not improve our overall performance. It basically causes more polygons to
|
||||||
// end up in the same quad/half and so the polygon lists get longer, and that's more calls to polygon.occludes()
|
// end up in the same quad/half and so the polygon lists get longer, and that's more calls to polygon.occludes()
|
||||||
if ( (projectedPolygon.getMaxX() > PolygonClip::RIGHT_OF_CLIPPING_WINDOW ) ||
|
if ( (projectedPolygon.getMaxX() > PolygonClip::RIGHT_OF_CLIPPING_WINDOW ) ||
|
||||||
(projectedPolygon.getMaxY() > PolygonClip::TOP_OF_CLIPPING_WINDOW ) ||
|
(projectedPolygon.getMaxY() > PolygonClip::TOP_OF_CLIPPING_WINDOW ) ||
|
||||||
(projectedPolygon.getMaxX() < PolygonClip::LEFT_OF_CLIPPING_WINDOW ) ||
|
(projectedPolygon.getMaxX() < PolygonClip::LEFT_OF_CLIPPING_WINDOW ) ||
|
||||||
(projectedPolygon.getMaxY() < PolygonClip::BOTTOM_OF_CLIPPING_WINDOW) ) {
|
(projectedPolygon.getMaxY() < PolygonClip::BOTTOM_OF_CLIPPING_WINDOW) ) {
|
||||||
|
|
||||||
CoverageRegion::_clippedPolygons++;
|
CoverageRegion::_clippedPolygons++;
|
||||||
|
|
||||||
glm::vec2* clippedVertices;
|
glm::vec2* clippedVertices;
|
||||||
int clippedVertexCount;
|
int clippedVertexCount;
|
||||||
PolygonClip::clipToScreen(projectedPolygon.getVertices(), vertexCount, clippedVertices, clippedVertexCount);
|
PolygonClip::clipToScreen(projectedPolygon.getVertices(), vertexCount, clippedVertices, clippedVertexCount);
|
||||||
|
|
||||||
// Now reset the vertices of our projectedPolygon object
|
// Now reset the vertices of our projectedPolygon object
|
||||||
projectedPolygon.setVertexCount(clippedVertexCount);
|
projectedPolygon.setVertexCount(clippedVertexCount);
|
||||||
for(int i = 0; i < clippedVertexCount; i++) {
|
for(int i = 0; i < clippedVertexCount; i++) {
|
||||||
projectedPolygon.setVertex(i, clippedVertices[i]);
|
projectedPolygon.setVertex(i, clippedVertices[i]);
|
||||||
}
|
}
|
||||||
delete[] clippedVertices;
|
delete[] clippedVertices;
|
||||||
|
|
||||||
lookUp += PROJECTION_CLIPPED;
|
lookUp += PROJECTION_CLIPPED;
|
||||||
}
|
}
|
||||||
***/
|
***/
|
||||||
}
|
}
|
||||||
|
@ -568,6 +570,97 @@ CubeProjectedPolygon ViewFrustum::getProjectedPolygon(const AACube& box) const {
|
||||||
return projectedPolygon;
|
return projectedPolygon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CubeProjectedPolygon ViewFrustum::getProjectedPolygon(const AACube& box) const {
|
||||||
|
return computeProjectedPolygon(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
CubeProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const {
|
||||||
|
return computeProjectedPolygon(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ViewFrustum::getProjectedRect(const AABox& box, glm::vec2& bottomLeft, glm::vec2& topRight) const {
|
||||||
|
using Edge = std::pair<int, int>;
|
||||||
|
|
||||||
|
const int VERTEX_COUNT = 8;
|
||||||
|
const int EDGE_COUNT = 12;
|
||||||
|
// In theory, after clipping a box with a plane, only 4 new vertices at max
|
||||||
|
// should be created but due to potential imprecisions (edge almost parallel to
|
||||||
|
// near plane for instance) there might be more
|
||||||
|
const int MAX_VERTEX_COUNT = VERTEX_COUNT + 4 + 2;
|
||||||
|
|
||||||
|
std::array<glm::vec3, MAX_VERTEX_COUNT> vertices;
|
||||||
|
std::array<Edge, EDGE_COUNT> boxEdges{ {
|
||||||
|
Edge{BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR},
|
||||||
|
Edge{TOP_LEFT_NEAR, TOP_RIGHT_NEAR},
|
||||||
|
Edge{BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR},
|
||||||
|
Edge{TOP_LEFT_FAR, TOP_RIGHT_FAR},
|
||||||
|
Edge{BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR},
|
||||||
|
Edge{BOTTOM_LEFT_FAR, TOP_LEFT_FAR},
|
||||||
|
Edge{BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR},
|
||||||
|
Edge{BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR},
|
||||||
|
Edge{BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR},
|
||||||
|
Edge{TOP_LEFT_NEAR, TOP_LEFT_FAR},
|
||||||
|
Edge{BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR},
|
||||||
|
Edge{TOP_RIGHT_NEAR, TOP_RIGHT_FAR}
|
||||||
|
} };
|
||||||
|
std::array<float, VERTEX_COUNT> distancesToNearPlane;
|
||||||
|
std::bitset<MAX_VERTEX_COUNT> areVerticesInside;
|
||||||
|
int vertexCount = VERTEX_COUNT;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// Clip the hull with the near plane.
|
||||||
|
const auto& nearPlane = _planes[NEAR_PLANE];
|
||||||
|
|
||||||
|
for (i = 0; i < VERTEX_COUNT; i++) {
|
||||||
|
vertices[i] = box.getVertex(static_cast<BoxVertex>(i));
|
||||||
|
distancesToNearPlane[i] = nearPlane.distance(vertices[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < EDGE_COUNT; i++) {
|
||||||
|
const auto& edgeVertexIndices = boxEdges[i];
|
||||||
|
const auto& startVertex = vertices[edgeVertexIndices.first];
|
||||||
|
const auto& endVertex = vertices[edgeVertexIndices.second];
|
||||||
|
float startVertexDistance = distancesToNearPlane[edgeVertexIndices.first];
|
||||||
|
float endVertexDistance = distancesToNearPlane[edgeVertexIndices.second];
|
||||||
|
bool isStartPointInside = startVertexDistance >= 0.0f;
|
||||||
|
bool isEndPointInside = endVertexDistance >= 0.0f;
|
||||||
|
|
||||||
|
areVerticesInside.set(edgeVertexIndices.first, isStartPointInside);
|
||||||
|
areVerticesInside.set(edgeVertexIndices.second, isEndPointInside);
|
||||||
|
|
||||||
|
if (isStartPointInside != isEndPointInside) {
|
||||||
|
// One of the two vertices is behind the near plane so add a new clipped vertex
|
||||||
|
// add tag it as projectable.
|
||||||
|
vertices[vertexCount] = startVertex + (endVertex - startVertex) * (startVertexDistance / (startVertexDistance - endVertexDistance));
|
||||||
|
areVerticesInside.set(vertexCount);
|
||||||
|
vertexCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Project points that are inside
|
||||||
|
bottomLeft.x = std::numeric_limits<float>::max();
|
||||||
|
bottomLeft.y = std::numeric_limits<float>::max();
|
||||||
|
topRight.x = -std::numeric_limits<float>::max();
|
||||||
|
topRight.y = -std::numeric_limits<float>::max();
|
||||||
|
for (i = 0; i < vertexCount; i++) {
|
||||||
|
if (areVerticesInside[i]) {
|
||||||
|
bool isPointInside;
|
||||||
|
auto projectedPoint = projectPoint(vertices[i], isPointInside);
|
||||||
|
bottomLeft.x = std::min(bottomLeft.x, projectedPoint.x);
|
||||||
|
bottomLeft.y = std::min(bottomLeft.y, projectedPoint.y);
|
||||||
|
topRight.x = std::max(topRight.x, projectedPoint.x);
|
||||||
|
topRight.y = std::max(topRight.y, projectedPoint.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bottomLeft.x = glm::clamp(bottomLeft.x, -1.0f, 1.0f);
|
||||||
|
bottomLeft.y = glm::clamp(bottomLeft.y, -1.0f, 1.0f);
|
||||||
|
topRight.x = glm::clamp(topRight.x, -1.0f, 1.0f);
|
||||||
|
topRight.y = glm::clamp(topRight.y, -1.0f, 1.0f);
|
||||||
|
|
||||||
|
return areVerticesInside.any();
|
||||||
|
}
|
||||||
|
|
||||||
// Similar strategy to getProjectedPolygon() we use the knowledge of camera position relative to the
|
// Similar strategy to getProjectedPolygon() we use the knowledge of camera position relative to the
|
||||||
// axis-aligned voxels to determine which of the voxels vertices must be the furthest. No need for
|
// axis-aligned voxels to determine which of the voxels vertices must be the furthest. No need for
|
||||||
// squares and square-roots. Just compares.
|
// squares and square-roots. Just compares.
|
||||||
|
|
|
@ -119,6 +119,8 @@ public:
|
||||||
|
|
||||||
glm::vec2 projectPoint(glm::vec3 point, bool& pointInView) const;
|
glm::vec2 projectPoint(glm::vec3 point, bool& pointInView) const;
|
||||||
CubeProjectedPolygon getProjectedPolygon(const AACube& box) const;
|
CubeProjectedPolygon getProjectedPolygon(const AACube& box) const;
|
||||||
|
CubeProjectedPolygon getProjectedPolygon(const AABox& box) const;
|
||||||
|
bool getProjectedRect(const AABox& box, glm::vec2& bottomLeft, glm::vec2& topRight) const;
|
||||||
void getFurthestPointFromCamera(const AACube& box, glm::vec3& furthestPoint) const;
|
void getFurthestPointFromCamera(const AACube& box, glm::vec3& furthestPoint) const;
|
||||||
|
|
||||||
float distanceToCamera(const glm::vec3& point) const;
|
float distanceToCamera(const glm::vec3& point) const;
|
||||||
|
@ -169,6 +171,10 @@ private:
|
||||||
|
|
||||||
// Used to project points
|
// Used to project points
|
||||||
glm::mat4 _ourModelViewProjectionMatrix;
|
glm::mat4 _ourModelViewProjectionMatrix;
|
||||||
|
|
||||||
|
template <typename TBOX>
|
||||||
|
CubeProjectedPolygon computeProjectedPolygon(const TBOX& box) const;
|
||||||
|
|
||||||
};
|
};
|
||||||
using ViewFrustumPointer = std::shared_ptr<ViewFrustum>;
|
using ViewFrustumPointer = std::shared_ptr<ViewFrustum>;
|
||||||
|
|
||||||
|
|
160
scripts/developer/utilities/render/debugHighlight.js
Normal file
160
scripts/developer/utilities/render/debugHighlight.js
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
//
|
||||||
|
// debugHighlight.js
|
||||||
|
// developer/utilities/render
|
||||||
|
//
|
||||||
|
// Olivier Prat, created on 08/08/2017.
|
||||||
|
// Copyright 2017 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
|
||||||
|
//
|
||||||
|
|
||||||
|
// Set up the qml ui
|
||||||
|
var qml = Script.resolvePath('highlight.qml');
|
||||||
|
var window = new OverlayWindow({
|
||||||
|
title: 'Highlight',
|
||||||
|
source: qml,
|
||||||
|
width: 400,
|
||||||
|
height: 400,
|
||||||
|
});
|
||||||
|
window.closed.connect(function() { Script.stop(); });
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Created by Sam Gondelman on 9/7/2017
|
||||||
|
// Copyright 2017 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
|
||||||
|
|
||||||
|
(function() { // BEGIN LOCAL_SCOPE
|
||||||
|
|
||||||
|
var END_DIMENSIONS = {
|
||||||
|
x: 0.15,
|
||||||
|
y: 0.15,
|
||||||
|
z: 0.15
|
||||||
|
};
|
||||||
|
var COLOR = {red: 97, green: 247, blue: 255};
|
||||||
|
var end = {
|
||||||
|
type: "sphere",
|
||||||
|
dimensions: END_DIMENSIONS,
|
||||||
|
color: COLOR,
|
||||||
|
ignoreRayIntersection: true,
|
||||||
|
alpha: 1.0,
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
|
||||||
|
var COLOR2 = {red: 247, green: 97, blue: 255};
|
||||||
|
var end2 = {
|
||||||
|
type: "sphere",
|
||||||
|
dimensions: END_DIMENSIONS,
|
||||||
|
color: COLOR2,
|
||||||
|
ignoreRayIntersection: true,
|
||||||
|
alpha: 1.0,
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
|
||||||
|
var highlightGroupIndex = 0
|
||||||
|
var isSelectionAddEnabled = false
|
||||||
|
var isSelectionEnabled = false
|
||||||
|
var renderStates = [{name: "test", end: end}];
|
||||||
|
var defaultRenderStates = [{name: "test", distance: 20.0, end: end2}];
|
||||||
|
var time = 0
|
||||||
|
|
||||||
|
var ray = LaserPointers.createLaserPointer({
|
||||||
|
joint: "Mouse",
|
||||||
|
filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS | RayPick.PICK_AVATARS | RayPick.PICK_INVISIBLE | RayPick.PICK_NONCOLLIDABLE,
|
||||||
|
renderStates: renderStates,
|
||||||
|
defaultRenderStates: defaultRenderStates,
|
||||||
|
enabled: false
|
||||||
|
});
|
||||||
|
|
||||||
|
function getSelectionName() {
|
||||||
|
var selectionName = "contextOverlayHighlightList"
|
||||||
|
|
||||||
|
if (highlightGroupIndex>0) {
|
||||||
|
selectionName += highlightGroupIndex
|
||||||
|
}
|
||||||
|
return selectionName
|
||||||
|
}
|
||||||
|
|
||||||
|
function fromQml(message) {
|
||||||
|
tokens = message.split(' ')
|
||||||
|
print("Received '"+message+"' from hightlight.qml")
|
||||||
|
if (tokens[0]=="highlight") {
|
||||||
|
highlightGroupIndex = parseInt(tokens[1])
|
||||||
|
print("Switching to highlight group "+highlightGroupIndex)
|
||||||
|
} else if (tokens[0]=="pick") {
|
||||||
|
isSelectionEnabled = tokens[1]=='true'
|
||||||
|
print("Ray picking set to "+isSelectionEnabled.toString())
|
||||||
|
if (isSelectionEnabled) {
|
||||||
|
LaserPointers.enableLaserPointer(ray)
|
||||||
|
} else {
|
||||||
|
LaserPointers.disableLaserPointer(ray)
|
||||||
|
}
|
||||||
|
time = 0
|
||||||
|
} else if (tokens[0]=="add") {
|
||||||
|
isSelectionAddEnabled = tokens[1]=='true'
|
||||||
|
print("Add to selection set to "+isSelectionAddEnabled.toString())
|
||||||
|
if (!isSelectionAddEnabled) {
|
||||||
|
Selection.clearSelectedItemsList(getSelectionName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.fromQml.connect(fromQml);
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
LaserPointers.removeLaserPointer(ray);
|
||||||
|
}
|
||||||
|
Script.scriptEnding.connect(cleanup);
|
||||||
|
|
||||||
|
var prevID = 0
|
||||||
|
var prevType = ""
|
||||||
|
var selectedID = 0
|
||||||
|
var selectedType = ""
|
||||||
|
function update(deltaTime) {
|
||||||
|
|
||||||
|
// you have to do this repeatedly because there's a bug but I'll fix it
|
||||||
|
LaserPointers.setRenderState(ray, "test");
|
||||||
|
|
||||||
|
var result = LaserPointers.getPrevRayPickResult(ray);
|
||||||
|
var selectionName = getSelectionName()
|
||||||
|
|
||||||
|
if (isSelectionEnabled && result.type != RayPick.INTERSECTED_NONE) {
|
||||||
|
time += deltaTime
|
||||||
|
if (result.objectID != prevID) {
|
||||||
|
var typeName = ""
|
||||||
|
if (result.type == RayPick.INTERSECTED_ENTITY) {
|
||||||
|
typeName = "entity"
|
||||||
|
} else if (result.type == RayPick.INTERSECTED_OVERLAY) {
|
||||||
|
typeName = "overlay"
|
||||||
|
} else if (result.type == RayPick.INTERSECTED_AVATAR) {
|
||||||
|
typeName = "avatar"
|
||||||
|
}
|
||||||
|
|
||||||
|
prevID = result.objectID;
|
||||||
|
prevType = typeName;
|
||||||
|
time = 0
|
||||||
|
} else if (time>1.0 && prevID!=selectedID) {
|
||||||
|
if (prevID != 0 && !isSelectionAddEnabled) {
|
||||||
|
Selection.removeFromSelectedItemsList(selectionName, selectedType, selectedID)
|
||||||
|
}
|
||||||
|
selectedID = prevID
|
||||||
|
selectedType = prevType
|
||||||
|
Selection.addToSelectedItemsList(selectionName, selectedType, selectedID)
|
||||||
|
print("HIGHLIGHT " + highlightGroupIndex + " picked type: " + result.type + ", id: " + result.objectID);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (prevID != 0 && !isSelectionAddEnabled) {
|
||||||
|
Selection.removeFromSelectedItemsList(selectionName, prevType, prevID)
|
||||||
|
}
|
||||||
|
prevID = 0
|
||||||
|
selectedID = 0
|
||||||
|
time = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Script.update.connect(update);
|
||||||
|
|
||||||
|
}()); // END LOCAL_SCOPE
|
|
@ -1,20 +0,0 @@
|
||||||
//
|
|
||||||
// debugOutline.js
|
|
||||||
// developer/utilities/render
|
|
||||||
//
|
|
||||||
// Olivier Prat, created on 08/08/2017.
|
|
||||||
// Copyright 2017 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
|
|
||||||
//
|
|
||||||
|
|
||||||
// Set up the qml ui
|
|
||||||
var qml = Script.resolvePath('outline.qml');
|
|
||||||
var window = new OverlayWindow({
|
|
||||||
title: 'Outline',
|
|
||||||
source: qml,
|
|
||||||
width: 285,
|
|
||||||
height: 370,
|
|
||||||
});
|
|
||||||
window.closed.connect(function() { Script.stop(); });
|
|
177
scripts/developer/utilities/render/highlight.qml
Normal file
177
scripts/developer/utilities/render/highlight.qml
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
//
|
||||||
|
// highlight.qml
|
||||||
|
// developer/utilities/render
|
||||||
|
//
|
||||||
|
// Olivier Prat, created on 08/08/2017.
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
import QtQuick 2.7
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
|
||||||
|
import "qrc:///qml/styles-uit"
|
||||||
|
import "qrc:///qml/controls-uit" as HifiControls
|
||||||
|
import "configSlider"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
HifiConstants { id: hifi;}
|
||||||
|
color: hifi.colors.baseGray;
|
||||||
|
anchors.margins: hifi.dimensions.contentMargin.x
|
||||||
|
|
||||||
|
property var debugConfig: Render.getConfig("RenderMainView.HighlightDebug")
|
||||||
|
property var highlightConfig: Render.getConfig("UpdateScene.HighlightStageSetup")
|
||||||
|
|
||||||
|
signal sendToScript(var message);
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: col
|
||||||
|
spacing: 10
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.margins: hifi.dimensions.contentMargin.x
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: 10
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
HifiControls.CheckBox {
|
||||||
|
id: debug
|
||||||
|
text: "View Mask"
|
||||||
|
checked: root.debugConfig["viewMask"]
|
||||||
|
onCheckedChanged: {
|
||||||
|
root.debugConfig["viewMask"] = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HifiControls.CheckBox {
|
||||||
|
text: "Hover select"
|
||||||
|
checked: false
|
||||||
|
onCheckedChanged: {
|
||||||
|
sendToScript("pick "+checked.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HifiControls.CheckBox {
|
||||||
|
text: "Add to selection"
|
||||||
|
checked: false
|
||||||
|
onCheckedChanged: {
|
||||||
|
sendToScript("add "+checked.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HifiControls.ComboBox {
|
||||||
|
id: box
|
||||||
|
width: 350
|
||||||
|
z: 999
|
||||||
|
editable: true
|
||||||
|
colorScheme: hifi.colorSchemes.dark
|
||||||
|
model: [
|
||||||
|
"contextOverlayHighlightList",
|
||||||
|
"highlightList1",
|
||||||
|
"highlightList2",
|
||||||
|
"highlightList3",
|
||||||
|
"highlightList4"]
|
||||||
|
label: ""
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: postpone
|
||||||
|
interval: 100; running: false; repeat: false
|
||||||
|
onTriggered: { paramWidgetLoader.sourceComponent = paramWidgets }
|
||||||
|
}
|
||||||
|
onCurrentIndexChanged: {
|
||||||
|
root.highlightConfig["selectionName"] = model[currentIndex];
|
||||||
|
sendToScript("highlight "+currentIndex)
|
||||||
|
// This is a hack to be sure the widgets below properly reflect the change of category: delete the Component
|
||||||
|
// by setting the loader source to Null and then recreate it 100ms later
|
||||||
|
paramWidgetLoader.sourceComponent = undefined;
|
||||||
|
postpone.interval = 100
|
||||||
|
postpone.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: paramWidgetLoader
|
||||||
|
sourceComponent: paramWidgets
|
||||||
|
width: 350
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: paramWidgets
|
||||||
|
|
||||||
|
Column {
|
||||||
|
spacing: 10
|
||||||
|
anchors.margins: hifi.dimensions.contentMargin.x
|
||||||
|
|
||||||
|
HifiControls.Label {
|
||||||
|
text: "Outline"
|
||||||
|
}
|
||||||
|
Column {
|
||||||
|
spacing: 10
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
HifiControls.CheckBox {
|
||||||
|
text: "Smooth"
|
||||||
|
checked: root.highlightConfig["isOutlineSmooth"]
|
||||||
|
onCheckedChanged: {
|
||||||
|
root.highlightConfig["isOutlineSmooth"] = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Repeater {
|
||||||
|
model: ["Width:outlineWidth:5.0:0.0",
|
||||||
|
"Intensity:outlineIntensity:1.0:0.0"
|
||||||
|
]
|
||||||
|
ConfigSlider {
|
||||||
|
label: qsTr(modelData.split(":")[0])
|
||||||
|
integral: false
|
||||||
|
config: root.highlightConfig
|
||||||
|
property: modelData.split(":")[1]
|
||||||
|
max: modelData.split(":")[2]
|
||||||
|
min: modelData.split(":")[3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {}
|
||||||
|
HifiControls.Label {
|
||||||
|
text: "Color"
|
||||||
|
}
|
||||||
|
Repeater {
|
||||||
|
model: ["Red:colorR:1.0:0.0",
|
||||||
|
"Green:colorG:1.0:0.0",
|
||||||
|
"Blue:colorB:1.0:0.0"
|
||||||
|
]
|
||||||
|
ConfigSlider {
|
||||||
|
label: qsTr(modelData.split(":")[0])
|
||||||
|
integral: false
|
||||||
|
config: root.highlightConfig
|
||||||
|
property: modelData.split(":")[1]
|
||||||
|
max: modelData.split(":")[2]
|
||||||
|
min: modelData.split(":")[3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {}
|
||||||
|
HifiControls.Label {
|
||||||
|
text: "Fill Opacity"
|
||||||
|
}
|
||||||
|
Repeater {
|
||||||
|
model: ["Unoccluded:unoccludedFillOpacity:1.0:0.0",
|
||||||
|
"Occluded:occludedFillOpacity:1.0:0.0"
|
||||||
|
]
|
||||||
|
ConfigSlider {
|
||||||
|
label: qsTr(modelData.split(":")[0])
|
||||||
|
integral: false
|
||||||
|
config: root.highlightConfig
|
||||||
|
property: modelData.split(":")[1]
|
||||||
|
max: modelData.split(":")[2]
|
||||||
|
min: modelData.split(":")[3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
//
|
||||||
|
// highlightPage.qml
|
||||||
|
// developer/utilities/render
|
||||||
|
//
|
||||||
|
// Olivier Prat, created on 08/08/2017.
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
import QtQuick 2.7
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import "../configSlider"
|
||||||
|
import "qrc:///qml/styles-uit"
|
||||||
|
import "qrc:///qml/controls-uit" as HifiControls
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
property var highlightIndex: 0
|
||||||
|
property var drawConfig: Render.getConfig("RenderMainView.HighlightEffect"+highlightIndex)
|
||||||
|
|
||||||
|
HifiConstants { id: hifi;}
|
||||||
|
anchors.margins: hifi.dimensions.contentMargin.x
|
||||||
|
|
||||||
|
Column {
|
||||||
|
spacing: 5
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.margins: hifi.dimensions.contentMargin.x
|
||||||
|
|
||||||
|
HifiControls.CheckBox {
|
||||||
|
id: glow
|
||||||
|
text: "Glow"
|
||||||
|
checked: root.drawConfig["glow"]
|
||||||
|
onCheckedChanged: {
|
||||||
|
root.drawConfig["glow"] = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Repeater {
|
||||||
|
model: ["Width:width:5.0:0.0",
|
||||||
|
"Intensity:intensity:1.0:0.0"
|
||||||
|
]
|
||||||
|
ConfigSlider {
|
||||||
|
label: qsTr(modelData.split(":")[0])
|
||||||
|
integral: false
|
||||||
|
config: root.drawConfig
|
||||||
|
property: modelData.split(":")[1]
|
||||||
|
max: modelData.split(":")[2]
|
||||||
|
min: modelData.split(":")[3]
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupBox {
|
||||||
|
title: "Color"
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
Column {
|
||||||
|
spacing: 10
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.margins: hifi.dimensions.contentMargin.x
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: ["Red:colorR:1.0:0.0",
|
||||||
|
"Green:colorG:1.0:0.0",
|
||||||
|
"Blue:colorB:1.0:0.0"
|
||||||
|
]
|
||||||
|
ConfigSlider {
|
||||||
|
label: qsTr(modelData.split(":")[0])
|
||||||
|
integral: false
|
||||||
|
config: root.drawConfig
|
||||||
|
property: modelData.split(":")[1]
|
||||||
|
max: modelData.split(":")[2]
|
||||||
|
min: modelData.split(":")[3]
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupBox {
|
||||||
|
title: "Fill Opacity"
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
Column {
|
||||||
|
spacing: 10
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.margins: hifi.dimensions.contentMargin.x
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: ["Unoccluded:unoccludedFillOpacity:1.0:0.0",
|
||||||
|
"Occluded:occludedFillOpacity:1.0:0.0"
|
||||||
|
]
|
||||||
|
ConfigSlider {
|
||||||
|
label: qsTr(modelData.split(":")[0])
|
||||||
|
integral: false
|
||||||
|
config: root.drawConfig
|
||||||
|
property: modelData.split(":")[1]
|
||||||
|
max: modelData.split(":")[2]
|
||||||
|
min: modelData.split(":")[3]
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
scripts/developer/utilities/render/highlightPage/qmldir
Normal file
1
scripts/developer/utilities/render/highlightPage/qmldir
Normal file
|
@ -0,0 +1 @@
|
||||||
|
HighlightPage 1.0 HighlightPage.qml
|
|
@ -1,119 +0,0 @@
|
||||||
//
|
|
||||||
// outline.qml
|
|
||||||
// developer/utilities/render
|
|
||||||
//
|
|
||||||
// Olivier Prat, created on 08/08/2017.
|
|
||||||
// Copyright 2017 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
import QtQuick 2.5
|
|
||||||
import QtQuick.Controls 1.4
|
|
||||||
import "configSlider"
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
property var debugConfig: Render.getConfig("RenderMainView.OutlineDebug")
|
|
||||||
property var drawConfig: Render.getConfig("RenderMainView.OutlineEffect")
|
|
||||||
|
|
||||||
Column {
|
|
||||||
spacing: 8
|
|
||||||
|
|
||||||
CheckBox {
|
|
||||||
text: "View Outlined Depth"
|
|
||||||
checked: root.debugConfig["viewOutlinedDepth"]
|
|
||||||
onCheckedChanged: {
|
|
||||||
root.debugConfig["viewOutlinedDepth"] = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CheckBox {
|
|
||||||
text: "Glow"
|
|
||||||
checked: root.drawConfig["glow"]
|
|
||||||
onCheckedChanged: {
|
|
||||||
root.drawConfig["glow"] = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSlider {
|
|
||||||
label: "Width"
|
|
||||||
integral: false
|
|
||||||
config: root.drawConfig
|
|
||||||
property: "width"
|
|
||||||
max: 15.0
|
|
||||||
min: 0.0
|
|
||||||
width: 280
|
|
||||||
}
|
|
||||||
ConfigSlider {
|
|
||||||
label: "Intensity"
|
|
||||||
integral: false
|
|
||||||
config: root.drawConfig
|
|
||||||
property: "intensity"
|
|
||||||
max: 1.0
|
|
||||||
min: 0.0
|
|
||||||
width: 280
|
|
||||||
}
|
|
||||||
|
|
||||||
GroupBox {
|
|
||||||
title: "Color"
|
|
||||||
width: 280
|
|
||||||
Column {
|
|
||||||
spacing: 8
|
|
||||||
|
|
||||||
ConfigSlider {
|
|
||||||
label: "Red"
|
|
||||||
integral: false
|
|
||||||
config: root.drawConfig
|
|
||||||
property: "colorR"
|
|
||||||
max: 1.0
|
|
||||||
min: 0.0
|
|
||||||
width: 270
|
|
||||||
}
|
|
||||||
ConfigSlider {
|
|
||||||
label: "Green"
|
|
||||||
integral: false
|
|
||||||
config: root.drawConfig
|
|
||||||
property: "colorG"
|
|
||||||
max: 1.0
|
|
||||||
min: 0.0
|
|
||||||
width: 270
|
|
||||||
}
|
|
||||||
ConfigSlider {
|
|
||||||
label: "Blue"
|
|
||||||
integral: false
|
|
||||||
config: root.drawConfig
|
|
||||||
property: "colorB"
|
|
||||||
max: 1.0
|
|
||||||
min: 0.0
|
|
||||||
width: 270
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GroupBox {
|
|
||||||
title: "Fill Opacity"
|
|
||||||
width: 280
|
|
||||||
Column {
|
|
||||||
spacing: 8
|
|
||||||
|
|
||||||
ConfigSlider {
|
|
||||||
label: "Unoccluded"
|
|
||||||
integral: false
|
|
||||||
config: root.drawConfig
|
|
||||||
property: "fillOpacityUnoccluded"
|
|
||||||
max: 1.0
|
|
||||||
min: 0.0
|
|
||||||
width: 270
|
|
||||||
}
|
|
||||||
ConfigSlider {
|
|
||||||
label: "Occluded"
|
|
||||||
integral: false
|
|
||||||
config: root.drawConfig
|
|
||||||
property: "fillOpacityOccluded"
|
|
||||||
max: 1.0
|
|
||||||
min: 0.0
|
|
||||||
width: 270
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -65,6 +65,13 @@ Item {
|
||||||
label: "tone and post",
|
label: "tone and post",
|
||||||
color: "#FF0000"
|
color: "#FF0000"
|
||||||
}
|
}
|
||||||
|
,
|
||||||
|
{
|
||||||
|
object: Render.getConfig("RenderMainView.OutlineRangeTimer"),
|
||||||
|
prop: "gpuRunTime",
|
||||||
|
label: "outline",
|
||||||
|
color: "#FFFF00"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
PlotPerf {
|
PlotPerf {
|
||||||
|
@ -105,6 +112,13 @@ Item {
|
||||||
label: "tone and post",
|
label: "tone and post",
|
||||||
color: "#FF0000"
|
color: "#FF0000"
|
||||||
}
|
}
|
||||||
|
,
|
||||||
|
{
|
||||||
|
object: Render.getConfig("RenderMainView.OutlineRangeTimer"),
|
||||||
|
prop: "batchRunTime",
|
||||||
|
label: "outline",
|
||||||
|
color: "#FFFF00"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue