Merge branch 'master' of https://github.com/highfidelity/hifi into orange

This commit is contained in:
samcake 2017-06-19 15:48:46 -07:00
commit 653ce89e08
151 changed files with 1365 additions and 922 deletions

View file

@ -23,7 +23,6 @@
#include <EntityEditPacketSender.h>
#include <EntityTree.h>
#include <EntityTreeHeadlessViewer.h>
#include <ScriptEngine.h>
#include <ThreadedAssignment.h>
@ -31,6 +30,7 @@
#include "AudioGate.h"
#include "MixedAudioStream.h"
#include "entities/EntityTreeHeadlessViewer.h"
#include "avatars/ScriptableAvatar.h"
class Agent : public ThreadedAssignment {

View file

@ -16,9 +16,9 @@
#include <SharedUtil.h>
#include <Octree.h>
#include <OctreePacketData.h>
#include <OctreeHeadlessViewer.h>
#include <ViewFrustum.h>
#include "../octree/OctreeHeadlessViewer.h"
#include "EntityTree.h"
class EntitySimulation;

View file

@ -9,17 +9,14 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <NodeList.h>
#include "OctreeLogging.h"
#include "OctreeHeadlessViewer.h"
OctreeHeadlessViewer::OctreeHeadlessViewer() : OctreeRenderer() {
_viewFrustum.setProjection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), DEFAULT_ASPECT_RATIO, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP));
}
#include <NodeList.h>
#include <OctreeLogging.h>
void OctreeHeadlessViewer::init() {
OctreeRenderer::init();
OctreeHeadlessViewer::OctreeHeadlessViewer() {
_viewFrustum.setProjection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), DEFAULT_ASPECT_RATIO, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP));
}
void OctreeHeadlessViewer::queryOctree() {

View file

@ -12,28 +12,17 @@
#ifndef hifi_OctreeHeadlessViewer_h
#define hifi_OctreeHeadlessViewer_h
#include <udt/PacketHeaders.h>
#include <SharedUtil.h>
#include <ViewFrustum.h>
#include <OctreeProcessor.h>
#include <JurisdictionListener.h>
#include <OctreeQuery.h>
#include "JurisdictionListener.h"
#include "Octree.h"
#include "OctreeConstants.h"
#include "OctreeQuery.h"
#include "OctreeRenderer.h"
#include "OctreeSceneStats.h"
#include "Octree.h"
// Generic client side Octree renderer class.
class OctreeHeadlessViewer : public OctreeRenderer {
class OctreeHeadlessViewer : public OctreeProcessor {
Q_OBJECT
public:
OctreeHeadlessViewer();
virtual ~OctreeHeadlessViewer() {};
virtual void renderElement(OctreeElementPointer element, RenderArgs* args) override { /* swallow these */ }
virtual void init() override ;
virtual void render(RenderArgs* renderArgs) override { /* swallow these */ }
void setJurisdictionListener(JurisdictionListener* jurisdictionListener) { _jurisdictionListener = jurisdictionListener; }
@ -71,6 +60,7 @@ private:
JurisdictionListener* _jurisdictionListener = nullptr;
OctreeQuery _octreeQuery;
ViewFrustum _viewFrustum;
float _voxelSizeScale { DEFAULT_OCTREE_SIZE_SCALE };
int _boundaryLevelAdjust { 0 };
int _maxPacketsPerSecond { DEFAULT_MAX_OCTREE_PPS };

View file

@ -19,10 +19,10 @@
#include <QtCore/QUuid>
#include <EntityEditPacketSender.h>
#include <EntityTreeHeadlessViewer.h>
#include <plugins/CodecPlugin.h>
#include <ScriptEngine.h>
#include <ThreadedAssignment.h>
#include "../entities/EntityTreeHeadlessViewer.h"
class EntityScriptServer : public ThreadedAssignment {
Q_OBJECT

View file

@ -7,16 +7,18 @@
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(LINK_HIFI_LIBRARIES)
function(LINK_HIFI_LIBRARIES)
file(RELATIVE_PATH RELATIVE_LIBRARY_DIR_PATH ${CMAKE_CURRENT_SOURCE_DIR} "${HIFI_LIBRARY_DIR}")
set(LIBRARIES_TO_LINK ${ARGN})
foreach(HIFI_LIBRARY ${LIBRARIES_TO_LINK})
foreach(HIFI_LIBRARY ${LIBRARIES_TO_LINK})
if (NOT TARGET ${HIFI_LIBRARY})
add_subdirectory("${RELATIVE_LIBRARY_DIR_PATH}/${HIFI_LIBRARY}" "${RELATIVE_LIBRARY_DIR_PATH}/${HIFI_LIBRARY}")
endif ()
endforeach()
foreach(HIFI_LIBRARY ${LIBRARIES_TO_LINK})
include_directories("${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src")
include_directories("${CMAKE_BINARY_DIR}/libraries/${HIFI_LIBRARY}/shaders")
@ -29,4 +31,4 @@ macro(LINK_HIFI_LIBRARIES)
setup_memory_debugger()
endmacro(LINK_HIFI_LIBRARIES)
endfunction()

View file

@ -147,6 +147,7 @@
{
"name": "security",
"label": "Security",
"restart": false,
"settings": [
{
"name": "http_username",

View file

@ -26,7 +26,7 @@
</ul>
<button id="advanced-toggle-button" class="btn btn-info advanced-toggle">Show advanced</button>
<button class="btn btn-success save-button">Save and restart</button>
<button class="btn btn-success save-button">Save</button>
</div>
</div>
@ -77,7 +77,7 @@
</div>
<div class="col-xs-12 hidden-sm hidden-md hidden-lg">
<button class="btn btn-success save-button" id="small-save-button">Save and restart</button>
<button class="btn btn-success save-button" id="small-save-button">Save</button>
</div>
</div>

View file

@ -1328,6 +1328,18 @@ function makeTableCategoryInput(setting, numVisibleColumns) {
return html;
}
function getDescriptionForKey(key) {
for (var i in Settings.data.descriptions) {
if (Settings.data.descriptions[i].name === key) {
return Settings.data.descriptions[i];
}
}
}
var SAVE_BUTTON_LABEL_SAVE = "Save";
var SAVE_BUTTON_LABEL_RESTART = "Save and restart";
var reasonsForRestart = [];
function badgeSidebarForDifferences(changedElement) {
// figure out which group this input is in
var panelParentID = changedElement.closest('.panel').attr('id');
@ -1350,13 +1362,24 @@ function badgeSidebarForDifferences(changedElement) {
}
var badgeValue = 0
var description = getDescriptionForKey(panelParentID);
// badge for any settings we have that are not the same or are not present in initialValues
for (var setting in panelJSON) {
if ((!_.has(initialPanelJSON, setting) && panelJSON[setting] !== "") ||
(!_.isEqual(panelJSON[setting], initialPanelJSON[setting])
&& (panelJSON[setting] !== "" || _.has(initialPanelJSON, setting)))) {
badgeValue += 1
badgeValue += 1;
// add a reason to restart
if (description && description.restart != false) {
reasonsForRestart.push(setting);
}
} else {
// remove a reason to restart
if (description && description.restart != false) {
reasonsForRestart = $.grep(reasonsForRestart, function(v) { return v != setting; });
}
}
}
@ -1365,6 +1388,7 @@ function badgeSidebarForDifferences(changedElement) {
badgeValue = ""
}
$(".save-button").html(reasonsForRestart.length > 0 ? SAVE_BUTTON_LABEL_RESTART : SAVE_BUTTON_LABEL_SAVE);
$("a[href='#" + panelParentID + "'] .badge").html(badgeValue);
}

View file

@ -115,6 +115,7 @@
#include <RenderDeferredTask.h>
#include <RenderForwardTask.h>
#include <RenderViewTask.h>
#include <SecondaryCamera.h>
#include <ResourceCache.h>
#include <ResourceRequest.h>
#include <SandboxUtils.h>
@ -580,7 +581,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
_undoStackScriptingInterface(&_undoStack),
_entitySimulation(new PhysicalEntitySimulation()),
_physicsEngine(new PhysicsEngine(Vectors::ZERO)),
_entityClipboardRenderer(false, this, this),
_entityClipboard(new EntityTree()),
_lastQueriedTime(usecTimestampNow()),
_previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION),
@ -1874,6 +1874,7 @@ void Application::initializeGL() {
render::CullFunctor cullFunctor = LODManager::shouldRender;
static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD";
bool isDeferred = !QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD);
_renderEngine->addJob<SecondaryCameraRenderTask>("SecondaryCameraFrame", cullFunctor);
_renderEngine->addJob<RenderViewTask>("RenderMainView", cullFunctor, isDeferred);
_renderEngine->load();
_renderEngine->registerScene(_main3DScene);
@ -3987,11 +3988,6 @@ void Application::init() {
DependencyManager::get<NodeList>()->sendDomainServerCheckIn();
getEntities()->init();
{
QMutexLocker viewLocker(&_viewMutex);
getEntities()->setViewFrustum(_viewFrustum);
}
getEntities()->setEntityLoadingPriorityFunction([this](const EntityItem& item) {
auto dims = item.getDimensions();
auto maxSize = glm::compMax(dims);
@ -4021,13 +4017,6 @@ void Application::init() {
// of events related clicking, hovering over, and entering entities
getEntities()->connectSignalsToSlots(entityScriptingInterface.data());
_entityClipboardRenderer.init();
{
QMutexLocker viewLocker(&_viewMutex);
_entityClipboardRenderer.setViewFrustum(_viewFrustum);
}
_entityClipboardRenderer.setTree(_entityClipboard);
// Make sure any new sounds are loaded as soon as know about them.
connect(tree.get(), &EntityTree::newCollisionSoundURL, this, [this](QUrl newURL, EntityItemID id) {
EntityTreePointer tree = getEntities()->getTree();
@ -5020,9 +5009,6 @@ QRect Application::getDesirableApplicationGeometry() const {
return applicationGeometry;
}
// FIXME, preprocessor guard this check to occur only in DEBUG builds
static QThread * activeRenderingThread = nullptr;
PickRay Application::computePickRay(float x, float y) const {
vec2 pickPoint { x, y };
PickRay result;
@ -5093,7 +5079,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
auto myAvatar = getMyAvatar();
myAvatar->preDisplaySide(renderArgs);
activeRenderingThread = QThread::currentThread();
PROFILE_RANGE(render, __FUNCTION__);
PerformanceTimer perfTimer("display");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()");
@ -5163,8 +5148,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
// Before the deferred pass, let's try to use the render engine
_renderEngine->run();
}
activeRenderingThread = nullptr;
}
void Application::resetSensors(bool andReload) {

View file

@ -181,7 +181,6 @@ public:
QUndoStack* getUndoStack() { return &_undoStack; }
MainWindow* getWindow() const { return _window; }
EntityTreePointer getEntityClipboard() const { return _entityClipboard; }
EntityTreeRenderer* getEntityClipboardRenderer() { return &_entityClipboardRenderer; }
EntityEditPacketSender* getEntityEditPacketSender() { return &_entityEditSender; }
ivec2 getMouse() const;
@ -532,7 +531,6 @@ private:
PhysicalEntitySimulationPointer _entitySimulation;
PhysicsEnginePointer _physicsEngine;
EntityTreeRenderer _entityClipboardRenderer;
EntityTreePointer _entityClipboard;
mutable QMutex _viewMutex { QMutex::Recursive };

View file

@ -17,6 +17,7 @@
#include <OctreeConstants.h>
#include <PIDController.h>
#include <SimpleMovingAverage.h>
#include <render/Args.h>
const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 20.0;
const float DEFAULT_HMD_LOD_DOWN_FPS = 20.0;
@ -45,7 +46,6 @@ const float ADJUST_LOD_MAX_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE;
// This controls how low the auto-adjust LOD will go. We want a minimum vision of ~20:500 or 0.04 of default
const float ADJUST_LOD_MIN_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE * 0.04f;
class RenderArgs;
class AABox;
class LODManager : public QObject, public Dependency {

View file

@ -0,0 +1,125 @@
//
// SecondaryCamera.cpp
// interface/src
//
// Created by Samuel Gateau, Howard Stearns, and Zach Fox on 2017-06-08.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "SecondaryCamera.h"
#include <TextureCache.h>
#include <gpu/Context.h>
using RenderArgsPointer = std::shared_ptr<RenderArgs>;
void MainRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) {
task.addJob<RenderShadowTask>("RenderShadowTask", cullFunctor);
const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor);
assert(items.canCast<RenderFetchCullSortTask::Output>());
if (!isDeferred) {
task.addJob<RenderForwardTask>("Forward", items);
} else {
task.addJob<RenderDeferredTask>("RenderDeferredTask", items);
}
}
void SecondaryCameraRenderTaskConfig::resetSize(int width, int height) { // FIXME: Add an arg here for "destinationFramebuffer"
bool wasEnabled = isEnabled();
setEnabled(false);
auto textureCache = DependencyManager::get<TextureCache>();
textureCache->resetSpectatorCameraFramebuffer(width, height); // FIXME: Call the correct reset function based on the "destinationFramebuffer" arg
setEnabled(wasEnabled);
}
void SecondaryCameraRenderTaskConfig::resetSizeSpectatorCamera(int width, int height) { // Carefully adjust the framebuffer / texture.
resetSize(width, height);
}
class BeginSecondaryCameraFrame { // Changes renderContext for our framebuffer and and view.
glm::vec3 _position{};
glm::quat _orientation{};
float _vFoV{};
float _nearClipPlaneDistance{};
float _farClipPlaneDistance{};
public:
using Config = BeginSecondaryCameraFrameConfig;
using JobModel = render::Job::ModelO<BeginSecondaryCameraFrame, RenderArgsPointer, Config>;
BeginSecondaryCameraFrame() {
_cachedArgsPointer = std::make_shared<RenderArgs>(_cachedArgs);
}
void configure(const Config& config) {
if (config.enabled || config.alwaysEnabled) {
_position = config.position;
_orientation = config.orientation;
_vFoV = config.vFoV;
_nearClipPlaneDistance = config.nearClipPlaneDistance;
_farClipPlaneDistance = config.farClipPlaneDistance;
}
}
void run(const render::RenderContextPointer& renderContext, RenderArgsPointer& cachedArgs) {
auto args = renderContext->args;
auto textureCache = DependencyManager::get<TextureCache>();
gpu::FramebufferPointer destFramebuffer;
destFramebuffer = textureCache->getSpectatorCameraFramebuffer(); // FIXME: Change the destination based on some unimplemented config var
if (destFramebuffer) {
_cachedArgsPointer->_blitFramebuffer = args->_blitFramebuffer;
_cachedArgsPointer->_viewport = args->_viewport;
_cachedArgsPointer->_displayMode = args->_displayMode;
_cachedArgsPointer->_renderMode = args->_renderMode;
args->_blitFramebuffer = destFramebuffer;
args->_viewport = glm::ivec4(0, 0, destFramebuffer->getWidth(), destFramebuffer->getHeight());
args->_displayMode = RenderArgs::MONO;
args->_renderMode = RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE;
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
batch.disableContextStereo();
});
auto srcViewFrustum = args->getViewFrustum();
srcViewFrustum.setPosition(_position);
srcViewFrustum.setOrientation(_orientation);
srcViewFrustum.setProjection(glm::perspective(glm::radians(_vFoV), ((float)args->_viewport.z / (float)args->_viewport.w), _nearClipPlaneDistance, _farClipPlaneDistance));
// Without calculating the bound planes, the secondary camera will use the same culling frustum as the main camera,
// which is not what we want here.
srcViewFrustum.calculate();
args->pushViewFrustum(srcViewFrustum);
cachedArgs = _cachedArgsPointer;
}
}
protected:
RenderArgs _cachedArgs;
RenderArgsPointer _cachedArgsPointer;
};
class EndSecondaryCameraFrame { // Restores renderContext.
public:
using JobModel = render::Job::ModelI<EndSecondaryCameraFrame, RenderArgsPointer>;
void run(const render::RenderContextPointer& renderContext, const RenderArgsPointer& cachedArgs) {
auto args = renderContext->args;
args->_blitFramebuffer = cachedArgs->_blitFramebuffer;
args->_viewport = cachedArgs->_viewport;
args->popViewFrustum();
args->_displayMode = cachedArgs->_displayMode;
args->_renderMode = cachedArgs->_renderMode;
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
batch.restoreContextStereo();
});
}
};
void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor) {
const auto cachedArg = task.addJob<BeginSecondaryCameraFrame>("BeginSecondaryCamera");
const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor);
assert(items.canCast<RenderFetchCullSortTask::Output>());
task.addJob<RenderDeferredTask>("RenderDeferredTask", items);
task.addJob<EndSecondaryCameraFrame>("EndSecondaryCamera", cachedArg);
}

View file

@ -0,0 +1,70 @@
//
// SecondaryCamera.h
// interface/src
//
// Created by Samuel Gateau, Howard Stearns, and Zach Fox on 2017-06-08.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#ifndef hifi_SecondaryCamera_h
#define hifi_SecondaryCamera_h
#include <RenderShadowTask.h>
#include <render/RenderFetchCullSortTask.h>
#include <RenderDeferredTask.h>
#include <RenderForwardTask.h>
class MainRenderTask {
public:
using JobModel = render::Task::Model<MainRenderTask>;
MainRenderTask() {}
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred = true);
};
class BeginSecondaryCameraFrameConfig : public render::Task::Config { // Exposes secondary camera parameters to JavaScript.
Q_OBJECT
Q_PROPERTY(glm::vec3 position MEMBER position NOTIFY dirty) // of viewpoint to render from
Q_PROPERTY(glm::quat orientation MEMBER orientation NOTIFY dirty) // of viewpoint to render from
Q_PROPERTY(float vFoV MEMBER vFoV NOTIFY dirty) // Secondary camera's vertical field of view. In degrees.
Q_PROPERTY(float nearClipPlaneDistance MEMBER nearClipPlaneDistance NOTIFY dirty) // Secondary camera's near clip plane distance. In meters.
Q_PROPERTY(float farClipPlaneDistance MEMBER farClipPlaneDistance NOTIFY dirty) // Secondary camera's far clip plane distance. In meters.
public:
glm::vec3 position{};
glm::quat orientation{};
float vFoV{ 45.0f };
float nearClipPlaneDistance{ 0.1f };
float farClipPlaneDistance{ 100.0f };
BeginSecondaryCameraFrameConfig() : render::Task::Config(false) {}
signals:
void dirty();
};
class SecondaryCameraRenderTaskConfig : public render::Task::Config {
Q_OBJECT
public:
SecondaryCameraRenderTaskConfig() : render::Task::Config(false) {}
private:
void resetSize(int width, int height);
signals:
void dirty();
public slots:
void resetSizeSpectatorCamera(int width, int height);
};
class SecondaryCameraRenderTask {
public:
using Config = SecondaryCameraRenderTaskConfig;
using JobModel = render::Task::Model<SecondaryCameraRenderTask, Config>;
SecondaryCameraRenderTask() {}
void configure(const Config& config) {}
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor);
};
#endif

View file

@ -19,7 +19,6 @@
#include <DependencyManager.h>
#include <gpu/Batch.h>
#include <RenderArgs.h>
class AudioScope : public QObject, public Dependency {

View file

@ -1881,15 +1881,14 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.3f;
bool MyAvatar::cameraInsideHead() const {
const glm::vec3 cameraPosition = qApp->getCamera().getPosition();
bool MyAvatar::cameraInsideHead(const glm::vec3& cameraPosition) const {
return glm::length(cameraPosition - getHeadPosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getUniformScale());
}
bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const {
bool defaultMode = renderArgs->_renderMode == RenderArgs::DEFAULT_RENDER_MODE;
bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON;
bool insideHead = cameraInsideHead();
bool insideHead = cameraInsideHead(renderArgs->getViewFrustum().getPosition());
return !defaultMode || !firstPerson || !insideHead;
}

View file

@ -615,7 +615,7 @@ private:
float scale = 1.0f, bool isSoft = false,
bool allowDuplicates = false, bool useSaved = true) override;
bool cameraInsideHead() const;
bool cameraInsideHead(const glm::vec3& cameraPosition) const;
void updateEyeContactTarget(float deltaTime);

View file

@ -152,9 +152,13 @@ void MenuScriptingInterface::closeInfoView(const QString& path) {
}
bool MenuScriptingInterface::isInfoViewVisible(const QString& path) {
if (QThread::currentThread() == qApp->thread()) {
return Menu::getInstance()->isInfoViewVisible(path);
}
bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "isInfoViewVisible", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, result), Q_ARG(const QString&, path));
Q_RETURN_ARG(bool, result), Q_ARG(const QString&, path));
return result;
}

View file

@ -284,6 +284,11 @@ void WindowScriptingInterface::copyToClipboard(const QString& text) {
QApplication::clipboard()->setText(text);
}
bool WindowScriptingInterface::setDisplayTexture(const QString& name) {
return qApp->getActiveDisplayPlugin()->setDisplayTexture(name); // Plugins that don't know how, answer false.
}
void WindowScriptingInterface::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) {
qApp->takeSnapshot(notify, includeAnimated, aspectRatio);
}

View file

@ -62,6 +62,7 @@ public slots:
void displayAnnouncement(const QString& message);
void shareSnapshot(const QString& path, const QUrl& href = QUrl(""));
bool isPhysicsEnabled();
bool setDisplayTexture(const QString& name);
int openMessageBox(QString title, QString text, int buttons, int defaultButton);
void updateMessageBox(int id, QString title, QString text, int buttons, int defaultButton);

View file

@ -16,6 +16,7 @@
#include <glm/gtx/quaternion.hpp>
#include <glm/gtx/vector_angle.hpp>
#include <render/Args.h>
#include <avatar/AvatarManager.h>
#include <Application.h>
#include <AudioClient.h>
@ -33,6 +34,7 @@
#include "SequenceNumberStats.h"
#include "StatTracker.h"
HIFI_QML_DEF(Stats)
using namespace std;
@ -454,7 +456,7 @@ void Stats::updateStats(bool force) {
}
}
void Stats::setRenderDetails(const RenderDetails& details) {
void Stats::setRenderDetails(const render::RenderDetails& details) {
STAT_UPDATE(triangles, details._trianglesRendered);
STAT_UPDATE(materialSwitches, details._materialSwitches);
if (_expanded) {

View file

@ -9,10 +9,11 @@
#ifndef hifi_Stats_h
#define hifi_Stats_h
#include <QtGui/QVector3D>
#include <OffscreenQmlElement.h>
#include <RenderArgs.h>
#include <QVector3D>
#include <AudioIOStats.h>
#include <render/Args.h>
#define STATS_PROPERTY(type, name, initialValue) \
Q_PROPERTY(type name READ name NOTIFY name##Changed) \
@ -138,7 +139,7 @@ public:
Stats(QQuickItem* parent = nullptr);
bool includeTimingRecord(const QString& name);
void setRenderDetails(const RenderDetails& details);
void setRenderDetails(const render::RenderDetails& details);
const QString& monospaceFont() {
return _monospaceFont;
}

View file

@ -112,7 +112,7 @@ void Image3DOverlay::render(RenderArgs* args) {
_geometryId
);
batch->setResourceTexture(0, args->_whiteTexture); // restore default white color after me
batch->setResourceTexture(0, nullptr); // restore default white color after me
}
const render::ShapeKey Image3DOverlay::getShapeKey() {

View file

@ -1,50 +0,0 @@
//
// LocalModelsOverlay.cpp
// interface/src/ui/overlays
//
// Created by Ryan Huffman on 07/08/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "LocalModelsOverlay.h"
#include <EntityTreeRenderer.h>
#include <gpu/Batch.h>
QString const LocalModelsOverlay::TYPE = "localmodels";
LocalModelsOverlay::LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer) :
Volume3DOverlay(),
_entityTreeRenderer(entityTreeRenderer) {
}
LocalModelsOverlay::LocalModelsOverlay(const LocalModelsOverlay* localModelsOverlay) :
Volume3DOverlay(localModelsOverlay),
_entityTreeRenderer(localModelsOverlay->_entityTreeRenderer)
{
}
void LocalModelsOverlay::update(float deltatime) {
_entityTreeRenderer->update();
}
void LocalModelsOverlay::render(RenderArgs* args) {
if (_visible) {
auto batch = args ->_batch;
Transform transform = Transform();
transform.setTranslation(args->getViewFrustum().getPosition() + getPosition());
batch->setViewTransform(transform);
_entityTreeRenderer->render(args);
transform.setTranslation(args->getViewFrustum().getPosition());
batch->setViewTransform(transform);
}
}
LocalModelsOverlay* LocalModelsOverlay::createClone() const {
return new LocalModelsOverlay(this);
}

View file

@ -1,37 +0,0 @@
//
// LocalModelsOverlay.h
// interface/src/ui/overlays
//
// Created by Ryan Huffman on 07/08/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_LocalModelsOverlay_h
#define hifi_LocalModelsOverlay_h
#include "Volume3DOverlay.h"
class EntityTreeRenderer;
class LocalModelsOverlay : public Volume3DOverlay {
Q_OBJECT
public:
static QString const TYPE;
virtual QString getType() const override { return TYPE; }
LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer);
LocalModelsOverlay(const LocalModelsOverlay* localModelsOverlay);
virtual void update(float deltatime) override;
virtual void render(RenderArgs* args) override;
virtual LocalModelsOverlay* createClone() const override;
private:
EntityTreeRenderer* _entityTreeRenderer;
};
#endif // hifi_LocalModelsOverlay_h

View file

@ -26,7 +26,6 @@
#include "Shape3DOverlay.h"
#include "ImageOverlay.h"
#include "Line3DOverlay.h"
#include "LocalModelsOverlay.h"
#include "ModelOverlay.h"
#include "Rectangle3DOverlay.h"
#include "Sphere3DOverlay.h"
@ -171,8 +170,6 @@ OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties)
thisOverlay = std::make_shared<Line3DOverlay>();
} else if (type == Grid3DOverlay::TYPE) {
thisOverlay = std::make_shared<Grid3DOverlay>();
} else if (type == LocalModelsOverlay::TYPE) {
thisOverlay = std::make_shared<LocalModelsOverlay>(qApp->getEntityClipboardRenderer());
} else if (type == ModelOverlay::TYPE) {
thisOverlay = std::make_shared<ModelOverlay>();
} else if (type == Web3DOverlay::TYPE) {

View file

@ -21,7 +21,6 @@
#include "Cube3DOverlay.h"
#include "ImageOverlay.h"
#include "Line3DOverlay.h"
#include "LocalModelsOverlay.h"
#include "ModelOverlay.h"
#include "Overlays.h"
#include "Rectangle3DOverlay.h"

View file

@ -318,7 +318,7 @@ void Web3DOverlay::render(RenderArgs* args) {
geometryCache->bindOpaqueWebBrowserProgram(batch, _isAA);
}
geometryCache->renderQuad(batch, halfSize * -1.0f, halfSize, vec2(0), vec2(1), color, _geometryId);
batch.setResourceTexture(0, args->_whiteTexture); // restore default white color after me
batch.setResourceTexture(0, nullptr); // restore default white color after me
}
const render::ShapeKey Web3DOverlay::getShapeKey() {

View file

@ -1,5 +1,7 @@
set(TARGET_NAME animation)
setup_hifi_library(Network Script)
link_hifi_libraries(shared model fbx)
include_hifi_library_headers(networking)
include_hifi_library_headers(gpu)
target_nsight()

View file

@ -1,6 +1,8 @@
set(TARGET_NAME audio-client)
setup_hifi_library(Network Multimedia)
link_hifi_libraries(audio plugins)
include_hifi_library_headers(shared)
include_hifi_library_headers(networking)
# append audio includes to our list of includes to bubble
target_include_directories(${TARGET_NAME} PUBLIC "${HIFI_LIBRARY_DIR}/audio/src")

View file

@ -2,5 +2,17 @@ set(TARGET_NAME avatars-renderer)
AUTOSCRIBE_SHADER_LIB(gpu model render render-utils)
setup_hifi_library(Widgets Network Script)
link_hifi_libraries(shared gpu model animation model-networking script-engine render image render-utils)
include_hifi_library_headers(avatars)
include_hifi_library_headers(networking)
include_hifi_library_headers(fbx)
include_hifi_library_headers(recording)
include_hifi_library_headers(trackers)
include_hifi_library_headers(ktx)
include_hifi_library_headers(procedural)
include_hifi_library_headers(physics)
include_hifi_library_headers(entities-renderer)
include_hifi_library_headers(audio)
include_hifi_library_headers(entities)
include_hifi_library_headers(octree)
target_bullet()

View file

@ -288,6 +288,13 @@ void Avatar::updateAvatarEntities() {
properties.setScript(noScript);
}
// When grabbing avatar entities, they are parented to the joint moving them, then when un-grabbed
// they go back to the default parent (null uuid). When un-gripped, others saw the entity disappear.
// The thinking here is the local position was noticed as changing, but not the parentID (since it is now
// back to the default), and the entity flew off somewhere. Marking all changed definitely fixes this,
// and seems safe (per Seth).
properties.markAllChanged();
// try to build the entity
EntityItemPointer entity = entityTree->findEntityByEntityItemID(EntityItemID(entityID));
bool success = true;
@ -1067,15 +1074,15 @@ void Avatar::setModelURLFinished(bool success) {
const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: we don't want to be as generous as ResourceCache is, we only want 4 attempts
if (_skeletonModel->getResourceDownloadAttemptsRemaining() <= 0 ||
_skeletonModel->getResourceDownloadAttempts() > MAX_SKELETON_DOWNLOAD_ATTEMPTS) {
qCWarning(avatars_renderer) << "Using default after failing to load Avatar model: " << _skeletonModelURL
qCWarning(avatars_renderer) << "Using default after failing to load Avatar model: " << _skeletonModelURL
<< "after" << _skeletonModel->getResourceDownloadAttempts() << "attempts.";
// call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that
// we don't redo this every time we receive an identity packet from the avatar with the bad url.
QMetaObject::invokeMethod(_skeletonModel.get(), "setURL",
Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl()));
} else {
qCWarning(avatars_renderer) << "Avatar model: " << _skeletonModelURL
<< "failed to load... attempts:" << _skeletonModel->getResourceDownloadAttempts()
qCWarning(avatars_renderer) << "Avatar model: " << _skeletonModelURL
<< "failed to load... attempts:" << _skeletonModel->getResourceDownloadAttempts()
<< "out of:" << MAX_SKELETON_DOWNLOAD_ATTEMPTS;
}
}

View file

@ -5,6 +5,7 @@ setup_hifi_library(Script Qml)
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
link_hifi_libraries(shared)
include_hifi_library_headers(networking)
GroupSources("src/controllers")

View file

@ -2,6 +2,14 @@ set(TARGET_NAME display-plugins)
AUTOSCRIBE_SHADER_LIB(gpu display-plugins)
setup_hifi_library(OpenGL)
link_hifi_libraries(shared plugins ui-plugins gl gpu-gl ui render-utils)
include_hifi_library_headers(gpu)
include_hifi_library_headers(model-networking)
include_hifi_library_headers(networking)
include_hifi_library_headers(model)
include_hifi_library_headers(fbx)
include_hifi_library_headers(image)
include_hifi_library_headers(ktx)
include_hifi_library_headers(render)
target_opengl()

View file

@ -496,6 +496,17 @@ void OpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) {
_newFrameQueue.push(newFrame);
});
}
void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor) {
batch.enableStereo(false);
batch.resetViewTransform();
batch.setFramebuffer(gpu::FramebufferPointer());
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0));
batch.setStateScissorRect(scissor);
batch.setViewportTransform(viewport);
batch.setResourceTexture(0, texture);
batch.setPipeline(_presentPipeline);
batch.draw(gpu::TRIANGLE_STRIP, 4);
}
void OpenGLDisplayPlugin::updateFrameData() {
PROFILE_RANGE(render, __FUNCTION__)
@ -605,14 +616,11 @@ void OpenGLDisplayPlugin::compositeLayers() {
void OpenGLDisplayPlugin::internalPresent() {
render([&](gpu::Batch& batch) {
batch.enableStereo(false);
batch.resetViewTransform();
batch.setFramebuffer(gpu::FramebufferPointer());
batch.setViewportTransform(ivec4(uvec2(0), getSurfacePixels()));
batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0));
batch.setPipeline(_presentPipeline);
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
// Note: _displayTexture must currently be the same size as the display.
uvec2 dims = _displayTexture ? uvec2(_displayTexture->getDimensions()) : getSurfacePixels();
auto viewport = ivec4(uvec2(0), dims);
renderFromTexture(batch, _displayTexture ? _displayTexture : _compositeFramebuffer->getRenderBuffer(0), viewport, viewport);
});
swapBuffers();
_presentRate.increment();
}
@ -694,6 +702,22 @@ void OpenGLDisplayPlugin::withMainThreadContext(std::function<void()> f) const {
_container->makeRenderingContextCurrent();
}
bool OpenGLDisplayPlugin::setDisplayTexture(const QString& name) {
// Note: it is the caller's responsibility to keep the network texture in cache.
if (name.isEmpty()) {
_displayTexture.reset();
onDisplayTextureReset();
return true;
}
auto textureCache = DependencyManager::get<TextureCache>();
auto displayNetworkTexture = textureCache->getTexture(name);
if (!displayNetworkTexture) {
return false;
}
_displayTexture = displayNetworkTexture->getGPUTexture();
return !!_displayTexture;
}
QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const {
auto size = _compositeFramebuffer->getSize();
if (isHmd()) {

View file

@ -57,6 +57,8 @@ public:
return getSurfaceSize();
}
virtual bool setDisplayTexture(const QString& name) override;
virtual bool onDisplayTextureReset() { return false; };
QImage getScreenshot(float aspectRatio = 0.0f) const override;
float presentRate() const override;
@ -109,6 +111,7 @@ protected:
// Plugin specific functionality to send the composed scene to the output window or device
virtual void internalPresent();
void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor);
virtual void updateFrameData();
void withMainThreadContext(std::function<void()> f) const;
@ -134,6 +137,7 @@ protected:
gpu::PipelinePointer _simplePipeline;
gpu::PipelinePointer _presentPipeline;
gpu::PipelinePointer _cursorPipeline;
gpu::TexturePointer _displayTexture{};
float _compositeOverlayAlpha { 1.0f };
struct CursorData {

View file

@ -10,7 +10,6 @@
#include <QtCore/QProcessEnvironment>
#include <ViewFrustum.h>
#include <controllers/Pose.h>
#include <gpu/Frame.h>
const QString DebugHmdDisplayPlugin::NAME("HMD Simulator");

View file

@ -27,6 +27,7 @@
#include <gpu/StandardShaderLib.h>
#include <gpu/gl/GLBackend.h>
#include <TextureCache.h>
#include <PathUtils.h>
#include "../Logging.h"
@ -211,7 +212,15 @@ void HmdDisplayPlugin::internalPresent() {
// Composite together the scene, overlay and mouse cursor
hmdPresent();
if (!_disablePreview) {
if (_displayTexture) {
// Note: _displayTexture must currently be the same size as the display.
uvec2 dims = uvec2(_displayTexture->getDimensions());
auto viewport = ivec4(uvec2(0), dims);
render([&](gpu::Batch& batch) {
renderFromTexture(batch, _displayTexture, viewport, viewport);
});
swapBuffers();
} else if (!_disablePreview) {
// screen preview mirroring
auto sourceSize = _renderTargetSize;
if (_monoPreview) {
@ -278,16 +287,7 @@ void HmdDisplayPlugin::internalPresent() {
viewport.z *= 2;
}
batch.enableStereo(false);
batch.resetViewTransform();
batch.setFramebuffer(gpu::FramebufferPointer());
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0));
batch.setStateScissorRect(scissor); // was viewport
batch.setViewportTransform(viewport);
batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0));
batch.setPipeline(_presentPipeline);
batch.draw(gpu::TRIANGLE_STRIP, 4);
renderFromTexture(batch, _compositeFramebuffer->getRenderBuffer(0), viewport, scissor);
});
swapBuffers();
} else if (_clearPreviewFlag) {
@ -316,15 +316,7 @@ void HmdDisplayPlugin::internalPresent() {
auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions()));
render([&](gpu::Batch& batch) {
batch.enableStereo(false);
batch.resetViewTransform();
batch.setFramebuffer(gpu::FramebufferPointer());
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0));
batch.setStateScissorRect(viewport);
batch.setViewportTransform(viewport);
batch.setResourceTexture(0, _previewTexture);
batch.setPipeline(_presentPipeline);
batch.draw(gpu::TRIANGLE_STRIP, 4);
renderFromTexture(batch, _previewTexture, viewport, viewport);
});
_clearPreviewFlag = false;
swapBuffers();

View file

@ -46,6 +46,8 @@ public:
float stutterRate() const override;
virtual bool onDisplayTextureReset() override { _clearPreviewFlag = true; return true; };
protected:
virtual void hmdPresent() = 0;
virtual bool isHmdMounted() const = 0;

View file

@ -2,6 +2,16 @@ set(TARGET_NAME entities-renderer)
AUTOSCRIBE_SHADER_LIB(gpu model procedural render render-utils)
setup_hifi_library(Widgets Network Script)
link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils image)
include_hifi_library_headers(networking)
include_hifi_library_headers(gl)
include_hifi_library_headers(ktx)
include_hifi_library_headers(octree)
include_hifi_library_headers(audio)
include_hifi_library_headers(physics)
include_hifi_library_headers(animation)
include_hifi_library_headers(fbx)
include_hifi_library_headers(entities)
include_hifi_library_headers(avatars)
target_bullet()

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "EntityTreeRenderer.h"
#include <glm/gtx/quaternion.hpp>
#include <QEventLoop>
@ -24,7 +26,6 @@
#include <SceneScriptingInterface.h>
#include <ScriptEngine.h>
#include "EntityTreeRenderer.h"
#include "RenderableEntityItem.h"
@ -130,7 +131,12 @@ void EntityTreeRenderer::clear() {
if (scene) {
render::Transaction transaction;
foreach(auto entity, _entitiesInScene) {
entity->removeFromScene(entity, scene, transaction);
auto renderable = entity->getRenderableInterface();
if (!renderable) {
qCWarning(entitiesrenderer) << "EntityTreeRenderer::deletingEntity(), trying to remove non-renderable entity";
continue;
}
renderable->removeFromScene(entity, scene, transaction);
}
scene->enqueueTransaction(transaction);
} else {
@ -141,7 +147,7 @@ void EntityTreeRenderer::clear() {
// reset the zone to the default (while we load the next scene)
_layeredZones.clear();
OctreeRenderer::clear();
OctreeProcessor::clear();
}
void EntityTreeRenderer::reloadEntityScripts() {
@ -155,7 +161,7 @@ void EntityTreeRenderer::reloadEntityScripts() {
}
void EntityTreeRenderer::init() {
OctreeRenderer::init();
OctreeProcessor::init();
EntityTreePointer entityTree = std::static_pointer_cast<EntityTree>(_tree);
entityTree->setFBXService(this);
@ -181,7 +187,7 @@ void EntityTreeRenderer::shutdown() {
}
void EntityTreeRenderer::setTree(OctreePointer newTree) {
OctreeRenderer::setTree(newTree);
OctreeProcessor::setTree(newTree);
std::static_pointer_cast<EntityTree>(_tree)->setFBXService(this);
}
@ -791,24 +797,33 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) {
}
void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
if (!_entitiesInScene.contains(entityID)) {
return;
}
if (_tree && !_shuttingDown && _entitiesScriptEngine) {
_entitiesScriptEngine->unloadEntityScript(entityID, true);
}
auto scene = _viewState->getMain3DScene();
if (!scene) {
qCWarning(entitiesrenderer) << "EntityTreeRenderer::deletingEntity(), Unexpected null scene, possibly during application shutdown";
return;
}
auto entity = _entitiesInScene.take(entityID);
auto renderable = entity->getRenderableInterface();
if (!renderable) {
qCWarning(entitiesrenderer) << "EntityTreeRenderer::deletingEntity(), trying to remove non-renderable entity";
return;
}
forceRecheckEntities(); // reset our state to force checking our inside/outsideness of entities
// here's where we remove the entity payload from the scene
if (_entitiesInScene.contains(entityID)) {
auto entity = _entitiesInScene.take(entityID);
render::Transaction transaction;
auto scene = _viewState->getMain3DScene();
if (scene) {
entity->removeFromScene(entity, scene, transaction);
scene->enqueueTransaction(transaction);
} else {
qCWarning(entitiesrenderer) << "EntityTreeRenderer::deletingEntity(), Unexpected null scene, possibly during application shutdown";
}
}
render::Transaction transaction;
renderable->removeFromScene(entity, scene, transaction);
scene->enqueueTransaction(transaction);
}
void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) {
@ -820,18 +835,25 @@ void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) {
}
}
void EntityTreeRenderer::addEntityToScene(EntityItemPointer entity) {
void EntityTreeRenderer::addEntityToScene(const EntityItemPointer& entity) {
// here's where we add the entity payload to the scene
render::Transaction transaction;
auto scene = _viewState->getMain3DScene();
if (scene) {
if (entity->addToScene(entity, scene, transaction)) {
_entitiesInScene.insert(entity->getEntityItemID(), entity);
}
scene->enqueueTransaction(transaction);
} else {
if (!scene) {
qCWarning(entitiesrenderer) << "EntityTreeRenderer::addEntityToScene(), Unexpected null scene, possibly during application shutdown";
return;
}
auto renderable = entity->getRenderableInterface();
if (!renderable) {
qCWarning(entitiesrenderer) << "EntityTreeRenderer::addEntityToScene(), Unexpected non-renderable entity";
return;
}
render::Transaction transaction;
if (renderable->addToScene(entity, scene, transaction)) {
_entitiesInScene.insert(entity->getEntityItemID(), entity);
}
scene->enqueueTransaction(transaction);
}
@ -1050,3 +1072,4 @@ bool EntityTreeRenderer::LayeredZones::contains(const LayeredZones& other) {
}
return result;
}

View file

@ -20,9 +20,10 @@
#include <EntityTree.h>
#include <QMouseEvent>
#include <PointerEvent.h>
#include <OctreeRenderer.h>
#include <ScriptCache.h>
#include <TextureCache.h>
#include <OctreeProcessor.h>
#include <render/Forward.h>
class AbstractScriptingServicesInterface;
class AbstractViewStateInterface;
@ -38,7 +39,7 @@ using ModelWeakPointer = std::weak_ptr<Model>;
using CalculateEntityLoadingPriority = std::function<float(const EntityItem& item)>;
// Generic client side Octree renderer class.
class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService, public Dependency {
class EntityTreeRenderer : public OctreeProcessor, public EntityItemFBXService, public Dependency {
Q_OBJECT
public:
EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
@ -144,7 +145,7 @@ protected:
private:
void resetEntitiesScriptEngine();
void addEntityToScene(EntityItemPointer entity);
void addEntityToScene(const EntityItemPointer& entity);
bool findBestZoneAndMaybeContainingEntities(QVector<EntityItemID>* entitiesContainingAvatar = nullptr);
bool applyLayeredZones();

View file

@ -40,7 +40,7 @@ namespace render {
template <> void payloadRender(const RenderableEntityItemProxy::Pointer& payload, RenderArgs* args) {
if (args) {
if (payload && payload->_entity && payload->_entity->getVisible()) {
payload->_entity->render(args);
payload->_entity->getRenderableInterface()->render(args);
}
}
}

View file

@ -34,13 +34,23 @@ enum class RenderItemStatusIcon {
void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status::Getters& statusGetters);
// Renderable entity item interface
class RenderableEntityInterface {
public:
virtual void render(RenderArgs* args) {};
virtual bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) = 0;
virtual void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) = 0;
virtual RenderableEntityInterface* getRenderableInterface() { return nullptr; }
};
class RenderableEntityItemProxy {
public:
RenderableEntityItemProxy(EntityItemPointer entity, render::ItemID metaID) : _entity(entity), _metaID(metaID) { }
RenderableEntityItemProxy(const EntityItemPointer& entity, render::ItemID metaID)
: _entity(entity), _metaID(metaID) {}
typedef render::Payload<RenderableEntityItemProxy> Payload;
typedef Payload::DataPointer Pointer;
EntityItemPointer _entity;
const EntityItemPointer _entity;
render::ItemID _metaID;
};
@ -51,10 +61,11 @@ namespace render {
template <> uint32_t metaFetchMetaSubItems(const RenderableEntityItemProxy::Pointer& payload, ItemIDs& subItems);
}
// Mixin class for implementing basic single item rendering
class SimpleRenderableEntityItem {
class SimplerRenderableEntitySupport : public RenderableEntityInterface {
public:
bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override {
_myItem = scene->allocateID();
auto renderData = std::make_shared<RenderableEntityItemProxy>(self, _myItem);
@ -69,7 +80,7 @@ public:
return true;
}
void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override {
transaction.removeItem(_myItem);
render::Item::clearID(_myItem);
}
@ -91,7 +102,6 @@ public:
qCWarning(entitiesrenderer) << "SimpleRenderableEntityItem::notifyChanged(), Unexpected null scene, possibly during application shutdown";
}
}
private:
render::ItemID _myItem { render::Item::INVALID_ITEM_ID };
};
@ -99,20 +109,18 @@ private:
#define SIMPLE_RENDERABLE() \
public: \
virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override { return _renderHelper.addToScene(self, scene, transaction); } \
virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override { _renderHelper.removeFromScene(self, scene, transaction); } \
virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); _renderHelper.notifyChanged(); } \
virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); _renderHelper.notifyChanged(); } \
virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); notifyChanged(); } \
virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); notifyChanged(); } \
virtual RenderableEntityInterface* getRenderableInterface() override { return this; } \
void checkFading() { \
bool transparent = isTransparent(); \
if (transparent != _prevIsTransparent) { \
_renderHelper.notifyChanged(); \
notifyChanged(); \
_isFading = false; \
_prevIsTransparent = transparent; \
} \
} \
private: \
SimpleRenderableEntityItem _renderHelper; \
bool _prevIsTransparent { isTransparent() };

View file

@ -27,7 +27,7 @@ RenderableLightEntityItem::RenderableLightEntityItem(const EntityItemID& entityI
{
}
bool RenderableLightEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
bool RenderableLightEntityItem::addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) {
_myItem = scene->allocateID();
auto renderItem = std::make_shared<LightPayload>();
@ -51,7 +51,7 @@ void RenderableLightEntityItem::somethingChangedNotification() {
LightEntityItem::somethingChangedNotification();
}
void RenderableLightEntityItem::removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
void RenderableLightEntityItem::removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) {
transaction.removeItem(_myItem);
render::Item::clearID(_myItem);
}

View file

@ -17,11 +17,13 @@
#include "RenderableEntityItem.h"
class RenderableLightEntityItem : public LightEntityItem {
class RenderableLightEntityItem : public LightEntityItem, public RenderableEntityInterface {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
RenderableLightEntityItem(const EntityItemID& entityItemID);
RenderableEntityInterface* getRenderableInterface() override { return this; }
virtual bool supportsDetailedRayIntersection() const override { return true; }
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElementPointer& element, float& distance,
@ -30,10 +32,10 @@ public:
void updateLightFromEntity(render::Transaction& transaction);
virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual void somethingChangedNotification() override;
virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual void locationChanged(bool tellPhysics = true) override;

View file

@ -16,7 +16,7 @@
#include "RenderableEntityItem.h"
#include <GeometryCache.h>
class RenderableLineEntityItem : public LineEntityItem {
class RenderableLineEntityItem : public LineEntityItem, public SimplerRenderableEntitySupport {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
RenderableLineEntityItem(const EntityItemID& entityItemID) :

View file

@ -213,7 +213,7 @@ namespace render {
if (args) {
if (payload && payload->entity) {
PROFILE_RANGE(render_detail, "MetaModelRender");
payload->entity->render(args);
payload->entity->getRenderableInterface()->render(args);
}
}
}
@ -228,7 +228,7 @@ namespace render {
}
}
bool RenderableModelEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene,
bool RenderableModelEntityItem::addToScene(const EntityItemPointer& self, const render::ScenePointer& scene,
render::Transaction& transaction) {
_myMetaItem = scene->allocateID();
@ -249,7 +249,7 @@ bool RenderableModelEntityItem::addToScene(EntityItemPointer self, const render:
return true;
}
void RenderableModelEntityItem::removeFromScene(EntityItemPointer self, const render::ScenePointer& scene,
void RenderableModelEntityItem::removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene,
render::Transaction& transaction) {
transaction.removeItem(_myMetaItem);
render::Item::clearID(_myMetaItem);
@ -390,7 +390,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
if (!_model || _needsModelReload) {
// TODO: this getModel() appears to be about 3% of model render time. We should optimize
PerformanceTimer perfTimer("getModel");
auto renderer = qSharedPointerCast<EntityTreeRenderer>(args->_renderer);
auto renderer = qSharedPointerCast<EntityTreeRenderer>(args->_renderData);
getModel(renderer);
// Remap textures immediately after loading to avoid flicker

View file

@ -20,7 +20,7 @@
class Model;
class EntityTreeRenderer;
class RenderableModelEntityItem : public ModelEntityItem {
class RenderableModelEntityItem : public ModelEntityItem, RenderableEntityInterface {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
@ -28,6 +28,8 @@ public:
virtual ~RenderableModelEntityItem();
RenderableEntityInterface* getRenderableInterface() override { return this; }
virtual void setDimensions(const glm::vec3& value) override;
virtual void setModelURL(const QString& url) override;
@ -40,8 +42,8 @@ public:
void doInitialModelSimulation();
virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override;
void updateModelBounds();

View file

@ -161,7 +161,7 @@ RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const Ent
}
}
bool RenderableParticleEffectEntityItem::addToScene(EntityItemPointer self,
bool RenderableParticleEffectEntityItem::addToScene(const EntityItemPointer& self,
const render::ScenePointer& scene,
render::Transaction& transaction) {
_scene = scene;
@ -176,7 +176,7 @@ bool RenderableParticleEffectEntityItem::addToScene(EntityItemPointer self,
return true;
}
void RenderableParticleEffectEntityItem::removeFromScene(EntityItemPointer self,
void RenderableParticleEffectEntityItem::removeFromScene(const EntityItemPointer& self,
const render::ScenePointer& scene,
render::Transaction& transaction) {
transaction.removeItem(_renderItemId);

View file

@ -15,18 +15,20 @@
#include <TextureCache.h>
#include "RenderableEntityItem.h"
class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem {
class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem, public RenderableEntityInterface {
friend class ParticlePayloadData;
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
RenderableParticleEffectEntityItem(const EntityItemID& entityItemID);
RenderableEntityInterface* getRenderableInterface() override { return this; }
virtual void update(const quint64& now) override;
void updateRenderItem();
virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override;
protected:
virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); notifyBoundChanged(); }

View file

@ -206,7 +206,7 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) {
if (_texture->isLoaded()) {
batch.setResourceTexture(PAINTSTROKE_TEXTURE_SLOT, _texture->getGPUTexture());
} else {
batch.setResourceTexture(PAINTSTROKE_TEXTURE_SLOT, args->_whiteTexture);
batch.setResourceTexture(PAINTSTROKE_TEXTURE_SLOT, nullptr);
}
batch.setInputFormat(_format);

View file

@ -22,7 +22,7 @@
#include <QReadWriteLock>
class RenderablePolyLineEntityItem : public PolyLineEntityItem {
class RenderablePolyLineEntityItem : public PolyLineEntityItem, public SimplerRenderableEntitySupport {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
static void createPipeline();

View file

@ -820,7 +820,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
batch.drawIndexed(gpu::TRIANGLES, (gpu::uint32)mesh->getNumIndices(), 0);
}
bool RenderablePolyVoxEntityItem::addToScene(EntityItemPointer self,
bool RenderablePolyVoxEntityItem::addToScene(const EntityItemPointer& self,
const render::ScenePointer& scene,
render::Transaction& transaction) {
_myItem = scene->allocateID();
@ -838,7 +838,7 @@ bool RenderablePolyVoxEntityItem::addToScene(EntityItemPointer self,
return true;
}
void RenderablePolyVoxEntityItem::removeFromScene(EntityItemPointer self,
void RenderablePolyVoxEntityItem::removeFromScene(const EntityItemPointer& self,
const render::ScenePointer& scene,
render::Transaction& transaction) {
transaction.removeItem(_myItem);
@ -865,7 +865,7 @@ namespace render {
template <> void payloadRender(const PolyVoxPayload::Pointer& payload, RenderArgs* args) {
if (args && payload && payload->_owner) {
payload->_owner->render(args);
payload->_owner->getRenderableInterface()->render(args);
}
}
}

View file

@ -41,13 +41,15 @@ namespace render {
}
class RenderablePolyVoxEntityItem : public PolyVoxEntityItem {
class RenderablePolyVoxEntityItem : public PolyVoxEntityItem, public RenderableEntityInterface {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
RenderablePolyVoxEntityItem(const EntityItemID& entityItemID);
virtual ~RenderablePolyVoxEntityItem();
RenderableEntityInterface* getRenderableInterface() override { return this; }
void initializePolyVox();
virtual void somethingChangedNotification() override {
@ -105,10 +107,10 @@ public:
virtual void setYTextureURL(const QString& yTextureURL) override;
virtual void setZTextureURL(const QString& zTextureURL) override;
virtual bool addToScene(EntityItemPointer self,
virtual bool addToScene(const EntityItemPointer& self,
const render::ScenePointer& scene,
render::Transaction& transaction) override;
virtual void removeFromScene(EntityItemPointer self,
virtual void removeFromScene(const EntityItemPointer& self,
const render::ScenePointer& scene,
render::Transaction& transaction) override;

View file

@ -14,7 +14,7 @@
#include "RenderableEntityItem.h"
class RenderableShapeEntityItem : public ShapeEntityItem {
class RenderableShapeEntityItem : public ShapeEntityItem, private SimplerRenderableEntitySupport {
using Pointer = std::shared_ptr<RenderableShapeEntityItem>;
static Pointer baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
public:

View file

@ -19,7 +19,7 @@
const int FIXED_FONT_POINT_SIZE = 40;
class RenderableTextEntityItem : public TextEntityItem {
class RenderableTextEntityItem : public TextEntityItem, public SimplerRenderableEntitySupport {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
RenderableTextEntityItem(const EntityItemID& entityItemID) : TextEntityItem(entityItemID) { }

View file

@ -198,7 +198,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
#endif
if (!_webSurface) {
auto renderer = qSharedPointerCast<EntityTreeRenderer>(args->_renderer);
auto renderer = qSharedPointerCast<EntityTreeRenderer>(args->_renderData);
if (!buildWebSurface(renderer)) {
return;
}

View file

@ -27,7 +27,7 @@ class EntityTreeRenderer;
class RenderableWebEntityItem;
class RenderableWebEntityItem : public WebEntityItem {
class RenderableWebEntityItem : public WebEntityItem, SimplerRenderableEntitySupport {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
RenderableWebEntityItem(const EntityItemID& entityItemID);

View file

@ -257,7 +257,7 @@ bool RenderableZoneEntityItem::contains(const glm::vec3& point) const {
return false;
}
bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene,
bool RenderableZoneEntityItem::addToScene(const EntityItemPointer& self, const render::ScenePointer& scene,
render::Transaction& transaction) {
_myMetaItem = scene->allocateID();
@ -277,7 +277,7 @@ bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, const render::
return true;
}
void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, const render::ScenePointer& scene,
void RenderableZoneEntityItem::removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene,
render::Transaction& transaction) {
transaction.removeItem(_myMetaItem);
render::Item::clearID(_myMetaItem);

View file

@ -14,13 +14,14 @@
#include <Model.h>
#include <ZoneEntityItem.h>
#include "RenderableEntityItem.h"
class NetworkGeometry;
class KeyLightPayload;
class RenderableZoneEntityItemMeta;
class RenderableZoneEntityItem : public ZoneEntityItem {
class RenderableZoneEntityItem : public ZoneEntityItem, public RenderableEntityInterface {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
@ -30,6 +31,8 @@ public:
_needsInitialSimulation(true)
{ }
RenderableEntityInterface* getRenderableInterface() override { return this; }
virtual bool setProperties(const EntityItemProperties& properties) override;
virtual void somethingChangedNotification() override;
@ -41,8 +44,8 @@ public:
virtual void render(RenderArgs* args) override;
virtual bool contains(const glm::vec3& point) const override;
virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override;
virtual void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) override;
render::ItemID getRenderItemID() const { return _myMetaItem; }

View file

@ -1,6 +1,8 @@
set(TARGET_NAME entities)
setup_hifi_library(Network Script)
link_hifi_libraries(avatars shared audio octree model model-networking fbx networking animation)
include_hifi_library_headers(networking)
include_hifi_library_headers(gpu)
target_bullet()

View file

@ -30,7 +30,7 @@ AddEntityOperator::AddEntityOperator(EntityTreePointer tree, EntityItemPointer n
_newEntityBox = queryCube.clamp((float)(-HALF_TREE_SCALE), (float)HALF_TREE_SCALE);
}
bool AddEntityOperator::preRecursion(OctreeElementPointer element) {
bool AddEntityOperator::preRecursion(const OctreeElementPointer& element) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
// In Pre-recursion, we're generally deciding whether or not we want to recurse this
@ -60,7 +60,7 @@ bool AddEntityOperator::preRecursion(OctreeElementPointer element) {
return keepSearching; // if we haven't yet found it, keep looking
}
bool AddEntityOperator::postRecursion(OctreeElementPointer element) {
bool AddEntityOperator::postRecursion(const OctreeElementPointer& element) {
// Post-recursion is the unwinding process. For this operation, while we
// unwind we want to mark the path as being dirty if we changed it below.
// We might have two paths, one for the old entity and one for the new entity.
@ -74,7 +74,7 @@ bool AddEntityOperator::postRecursion(OctreeElementPointer element) {
return keepSearching; // if we haven't yet found it, keep looking
}
OctreeElementPointer AddEntityOperator::possiblyCreateChildAt(OctreeElementPointer element, int childIndex) {
OctreeElementPointer AddEntityOperator::possiblyCreateChildAt(const OctreeElementPointer& element, int childIndex) {
// If we're getting called, it's because there was no child element at this index while recursing.
// We only care if this happens while still searching for the new entity location.
// Check to see if

View file

@ -16,9 +16,9 @@ class AddEntityOperator : public RecurseOctreeOperator {
public:
AddEntityOperator(EntityTreePointer tree, EntityItemPointer newEntity);
virtual bool preRecursion(OctreeElementPointer element) override;
virtual bool postRecursion(OctreeElementPointer element) override;
virtual OctreeElementPointer possiblyCreateChildAt(OctreeElementPointer element, int childIndex) override;
virtual bool preRecursion(const OctreeElementPointer& element) override;
virtual bool postRecursion(const OctreeElementPointer& element) override;
virtual OctreeElementPointer possiblyCreateChildAt(const OctreeElementPointer& element, int childIndex) override;
private:
EntityTreePointer _tree;
EntityItemPointer _newEntity;

View file

@ -55,7 +55,7 @@ void DeleteEntityOperator::addEntityIDToDeleteList(const EntityItemID& searchEnt
// does this entity tree element contain the old entity
bool DeleteEntityOperator::subTreeContainsSomeEntitiesToDelete(OctreeElementPointer element) {
bool DeleteEntityOperator::subTreeContainsSomeEntitiesToDelete(const OctreeElementPointer& element) {
bool containsEntity = false;
// If we don't have an old entity, then we don't contain the entity, otherwise
@ -72,7 +72,7 @@ bool DeleteEntityOperator::subTreeContainsSomeEntitiesToDelete(OctreeElementPoin
return containsEntity;
}
bool DeleteEntityOperator::preRecursion(OctreeElementPointer element) {
bool DeleteEntityOperator::preRecursion(const OctreeElementPointer& element) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
// In Pre-recursion, we're generally deciding whether or not we want to recurse this
@ -108,7 +108,7 @@ bool DeleteEntityOperator::preRecursion(OctreeElementPointer element) {
return keepSearching; // if we haven't yet found it, keep looking
}
bool DeleteEntityOperator::postRecursion(OctreeElementPointer element) {
bool DeleteEntityOperator::postRecursion(const OctreeElementPointer& element) {
// Post-recursion is the unwinding process. For this operation, while we
// unwind we want to mark the path as being dirty if we changed it below.
// We might have two paths, one for the old entity and one for the new entity.

View file

@ -36,8 +36,8 @@ public:
~DeleteEntityOperator();
void addEntityIDToDeleteList(const EntityItemID& searchEntityID);
virtual bool preRecursion(OctreeElementPointer element) override;
virtual bool postRecursion(OctreeElementPointer element) override;
virtual bool preRecursion(const OctreeElementPointer& element) override;
virtual bool postRecursion(const OctreeElementPointer& element) override;
const RemovedEntities& getEntities() const { return _entitiesToDelete; }
private:
@ -46,7 +46,7 @@ private:
quint64 _changeTime;
int _foundCount;
int _lookingCount;
bool subTreeContainsSomeEntitiesToDelete(OctreeElementPointer element);
bool subTreeContainsSomeEntitiesToDelete(const OctreeElementPointer& element);
};
#endif // hifi_DeleteEntityOperator_h

View file

@ -51,10 +51,6 @@ typedef std::shared_ptr<EntityTreeElement> EntityTreeElementPointer;
using EntityTreeElementExtraEncodeDataPointer = std::shared_ptr<EntityTreeElementExtraEncodeData>;
namespace render {
class Scene;
class Transaction;
}
#define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0;
#define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() override { };
@ -65,6 +61,8 @@ namespace render {
class MeshProxyList;
class RenderableEntityInterface;
/// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available
/// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate
@ -83,6 +81,8 @@ public:
EntityItem(const EntityItemID& entityItemID);
virtual ~EntityItem();
virtual RenderableEntityInterface* getRenderableInterface() { return nullptr; }
inline EntityItemPointer getThisPointer() const {
return std::static_pointer_cast<EntityItem>(std::const_pointer_cast<SpatiallyNestable>(shared_from_this()));
}
@ -150,13 +150,6 @@ public:
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
bool& somethingChanged)
{ somethingChanged = false; return 0; }
virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene,
render::Transaction& transaction) { return false; } // by default entity items don't add to scene
virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene,
render::Transaction& transaction) { } // by default entity items don't add to scene
virtual void render(RenderArgs* args) { } // by default entity items don't know how to render
static int expectedBytes();
static void adjustEditPacketForClockSkew(QByteArray& buffer, qint64 clockSkew);

View file

@ -554,7 +554,7 @@ public:
};
bool EntityTree::findNearPointOperation(OctreeElementPointer element, void* extraData) {
bool EntityTree::findNearPointOperation(const OctreeElementPointer& element, void* extraData) {
FindNearPointArgs* args = static_cast<FindNearPointArgs*>(extraData);
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
@ -589,7 +589,7 @@ bool EntityTree::findNearPointOperation(OctreeElementPointer element, void* extr
return false;
}
bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) {
bool findRayIntersectionOp(const OctreeElementPointer& element, void* extraData) {
RayArgs* args = static_cast<RayArgs*>(extraData);
bool keepSearching = true;
EntityTreeElementPointer entityTreeElementPointer = std::dynamic_pointer_cast<EntityTreeElement>(element);
@ -625,7 +625,7 @@ bool EntityTree::findRayIntersection(const glm::vec3& origin, const glm::vec3& d
}
EntityItemPointer EntityTree::findClosestEntity(glm::vec3 position, float targetRadius) {
EntityItemPointer EntityTree::findClosestEntity(const glm::vec3& position, float targetRadius) {
FindNearPointArgs args = { position, targetRadius, false, NULL, FLT_MAX };
withReadLock([&] {
// NOTE: This should use recursion, since this is a spatial operation
@ -642,7 +642,7 @@ public:
};
bool EntityTree::findInSphereOperation(OctreeElementPointer element, void* extraData) {
bool EntityTree::findInSphereOperation(const OctreeElementPointer& element, void* extraData) {
FindAllNearPointArgs* args = static_cast<FindAllNearPointArgs*>(extraData);
glm::vec3 penetration;
bool sphereIntersection = element->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration);
@ -678,7 +678,7 @@ public:
QVector<EntityItemPointer> _foundEntities;
};
bool EntityTree::findInCubeOperation(OctreeElementPointer element, void* extraData) {
bool EntityTree::findInCubeOperation(const OctreeElementPointer& element, void* extraData) {
FindEntitiesInCubeArgs* args = static_cast<FindEntitiesInCubeArgs*>(extraData);
if (element->getAACube().touches(args->_cube)) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
@ -707,7 +707,7 @@ public:
QVector<EntityItemPointer> _foundEntities;
};
bool EntityTree::findInBoxOperation(OctreeElementPointer element, void* extraData) {
bool EntityTree::findInBoxOperation(const OctreeElementPointer& element, void* extraData) {
FindEntitiesInBoxArgs* args = static_cast<FindEntitiesInBoxArgs*>(extraData);
if (element->getAACube().touches(args->_box)) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
@ -732,7 +732,7 @@ public:
QVector<EntityItemPointer> entities;
};
bool EntityTree::findInFrustumOperation(OctreeElementPointer element, void* extraData) {
bool EntityTree::findInFrustumOperation(const OctreeElementPointer& element, void* extraData) {
FindInFrustumArgs* args = static_cast<FindInFrustumArgs*>(extraData);
if (element->isInView(args->frustum)) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
@ -1527,15 +1527,15 @@ void EntityTree::debugDumpMap() {
class ContentsDimensionOperator : public RecurseOctreeOperator {
public:
virtual bool preRecursion(OctreeElementPointer element) override;
virtual bool postRecursion(OctreeElementPointer element) override { return true; }
virtual bool preRecursion(const OctreeElementPointer& element) override;
virtual bool postRecursion(const OctreeElementPointer& element) override { return true; }
glm::vec3 getDimensions() const { return _contentExtents.size(); }
float getLargestDimension() const { return _contentExtents.largestDimension(); }
private:
Extents _contentExtents;
};
bool ContentsDimensionOperator::preRecursion(OctreeElementPointer element) {
bool ContentsDimensionOperator::preRecursion(const OctreeElementPointer& element) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
entityTreeElement->expandExtentsToContents(_contentExtents);
return true;
@ -1555,11 +1555,11 @@ float EntityTree::getContentsLargestDimension() {
class DebugOperator : public RecurseOctreeOperator {
public:
virtual bool preRecursion(OctreeElementPointer element) override;
virtual bool postRecursion(OctreeElementPointer element) override { return true; }
virtual bool preRecursion(const OctreeElementPointer& element) override;
virtual bool postRecursion(const OctreeElementPointer& element) override { return true; }
};
bool DebugOperator::preRecursion(OctreeElementPointer element) {
bool DebugOperator::preRecursion(const OctreeElementPointer& element) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
qCDebug(entities) << "EntityTreeElement [" << entityTreeElement.get() << "]";
entityTreeElement->debugDump();
@ -1573,11 +1573,11 @@ void EntityTree::dumpTree() {
class PruneOperator : public RecurseOctreeOperator {
public:
virtual bool preRecursion(OctreeElementPointer element) override { return true; }
virtual bool postRecursion(OctreeElementPointer element) override;
virtual bool preRecursion(const OctreeElementPointer& element) override { return true; }
virtual bool postRecursion(const OctreeElementPointer& element) override;
};
bool PruneOperator::postRecursion(OctreeElementPointer element) {
bool PruneOperator::postRecursion(const OctreeElementPointer& element) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
entityTreeElement->pruneChildren();
return true;
@ -1639,6 +1639,7 @@ QVector<EntityItemID> EntityTree::sendEntities(EntityEditPacketSender* packetSen
// If this is called repeatedly (e.g., multiple pastes with the same data), the new elements will clash unless we
// use new identifiers. We need to keep a map so that we can map parent identifiers correctly.
QHash<EntityItemID, EntityItemID> map;
args.map = &map;
withReadLock([&] {
recurseTreeWithOperation(sendEntitiesOperation, &args);
@ -1692,7 +1693,7 @@ QVector<EntityItemID> EntityTree::sendEntities(EntityEditPacketSender* packetSen
return map.values().toVector();
}
bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extraData) {
bool EntityTree::sendEntitiesOperation(const OctreeElementPointer& element, void* extraData) {
SendEntitiesOperationArgs* args = static_cast<SendEntitiesOperationArgs*>(extraData);
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);

View file

@ -136,7 +136,7 @@ public:
/// \param position point of query in world-frame (meters)
/// \param targetRadius radius of query (meters)
EntityItemPointer findClosestEntity(glm::vec3 position, float targetRadius);
EntityItemPointer findClosestEntity(const glm::vec3& position, float targetRadius);
EntityItemPointer findEntityByID(const QUuid& id);
EntityItemPointer findEntityByEntityItemID(const EntityItemID& entityID);
virtual SpatiallyNestablePointer findByID(const QUuid& id) override { return findEntityByID(id); }
@ -294,12 +294,12 @@ protected:
bool updateEntityWithElement(EntityItemPointer entity, const EntityItemProperties& properties,
EntityTreeElementPointer containingElement,
const SharedNodePointer& senderNode = SharedNodePointer(nullptr));
static bool findNearPointOperation(OctreeElementPointer element, void* extraData);
static bool findInSphereOperation(OctreeElementPointer element, void* extraData);
static bool findInCubeOperation(OctreeElementPointer element, void* extraData);
static bool findInBoxOperation(OctreeElementPointer element, void* extraData);
static bool findInFrustumOperation(OctreeElementPointer element, void* extraData);
static bool sendEntitiesOperation(OctreeElementPointer element, void* extraData);
static bool findNearPointOperation(const OctreeElementPointer& element, void* extraData);
static bool findInSphereOperation(const OctreeElementPointer& element, void* extraData);
static bool findInCubeOperation(const OctreeElementPointer& element, void* extraData);
static bool findInBoxOperation(const OctreeElementPointer& element, void* extraData);
static bool findInFrustumOperation(const OctreeElementPointer& element, void* extraData);
static bool sendEntitiesOperation(const OctreeElementPointer& element, void* extraData);
static void bumpTimestamp(EntityItemProperties& properties);
void notifyNewlyCreatedEntity(const EntityItem& newEntity, const SharedNodePointer& senderNode);

View file

@ -17,8 +17,6 @@
#include <QHash>
#include <QString>
#include <OctreeRenderer.h> // for RenderArgs
#include "EntitiesLogging.h"
class EntityItem;

View file

@ -109,7 +109,7 @@ void MovingEntitiesOperator::addEntityToMoveList(EntityItemPointer entity, const
}
// does this entity tree element contain the old entity
bool MovingEntitiesOperator::shouldRecurseSubTree(OctreeElementPointer element) {
bool MovingEntitiesOperator::shouldRecurseSubTree(const OctreeElementPointer& element) {
bool containsEntity = false;
// If we don't have an old entity, then we don't contain the entity, otherwise
@ -141,7 +141,7 @@ bool MovingEntitiesOperator::shouldRecurseSubTree(OctreeElementPointer element)
return containsEntity;
}
bool MovingEntitiesOperator::preRecursion(OctreeElementPointer element) {
bool MovingEntitiesOperator::preRecursion(const OctreeElementPointer& element) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
// In Pre-recursion, we're generally deciding whether or not we want to recurse this
@ -221,7 +221,7 @@ bool MovingEntitiesOperator::preRecursion(OctreeElementPointer element) {
return keepSearching; // if we haven't yet found it, keep looking
}
bool MovingEntitiesOperator::postRecursion(OctreeElementPointer element) {
bool MovingEntitiesOperator::postRecursion(const OctreeElementPointer& element) {
// Post-recursion is the unwinding process. For this operation, while we
// unwind we want to mark the path as being dirty if we changed it below.
// We might have two paths, one for the old entity and one for the new entity.
@ -261,7 +261,7 @@ bool MovingEntitiesOperator::postRecursion(OctreeElementPointer element) {
return keepSearching; // if we haven't yet found it, keep looking
}
OctreeElementPointer MovingEntitiesOperator::possiblyCreateChildAt(OctreeElementPointer element, int childIndex) {
OctreeElementPointer MovingEntitiesOperator::possiblyCreateChildAt(const OctreeElementPointer& element, int childIndex) {
// If we're getting called, it's because there was no child element at this index while recursing.
// We only care if this happens while still searching for the new entity locations.
if (_foundNewCount < _lookingCount) {

View file

@ -38,9 +38,9 @@ public:
~MovingEntitiesOperator();
void addEntityToMoveList(EntityItemPointer entity, const AACube& newCube);
virtual bool preRecursion(OctreeElementPointer element) override;
virtual bool postRecursion(OctreeElementPointer element) override;
virtual OctreeElementPointer possiblyCreateChildAt(OctreeElementPointer element, int childIndex) override;
virtual bool preRecursion(const OctreeElementPointer& element) override;
virtual bool postRecursion(const OctreeElementPointer& element) override;
virtual OctreeElementPointer possiblyCreateChildAt(const OctreeElementPointer& element, int childIndex) override;
bool hasMovingEntities() const { return _entitiesToMove.size() > 0; }
private:
EntityTreePointer _tree;
@ -49,7 +49,7 @@ private:
int _foundOldCount;
int _foundNewCount;
int _lookingCount;
bool shouldRecurseSubTree(OctreeElementPointer element);
bool shouldRecurseSubTree(const OctreeElementPointer& element);
bool _wantDebug;
};

View file

@ -14,7 +14,7 @@
#include "EntityItemProperties.h"
RecurseOctreeToMapOperator::RecurseOctreeToMapOperator(QVariantMap& map,
OctreeElementPointer top,
const OctreeElementPointer& top,
QScriptEngine* engine,
bool skipDefaultValues,
bool skipThoseWithBadParents) :
@ -34,14 +34,14 @@ RecurseOctreeToMapOperator::RecurseOctreeToMapOperator(QVariantMap& map,
}
};
bool RecurseOctreeToMapOperator::preRecursion(OctreeElementPointer element) {
bool RecurseOctreeToMapOperator::preRecursion(const OctreeElementPointer& element) {
if (element == _top) {
_withinTop = true;
}
return true;
}
bool RecurseOctreeToMapOperator::postRecursion(OctreeElementPointer element) {
bool RecurseOctreeToMapOperator::postRecursion(const OctreeElementPointer& element) {
EntityItemProperties defaultProperties;

View file

@ -13,10 +13,10 @@
class RecurseOctreeToMapOperator : public RecurseOctreeOperator {
public:
RecurseOctreeToMapOperator(QVariantMap& map, OctreeElementPointer top, QScriptEngine* engine, bool skipDefaultValues,
RecurseOctreeToMapOperator(QVariantMap& map, const OctreeElementPointer& top, QScriptEngine* engine, bool skipDefaultValues,
bool skipThoseWithBadParents);
bool preRecursion(OctreeElementPointer element) override;
bool postRecursion(OctreeElementPointer element) override;
bool preRecursion(const OctreeElementPointer& element) override;
bool postRecursion(const OctreeElementPointer& element) override;
private:
QVariantMap& _map;
OctreeElementPointer _top;

View file

@ -77,7 +77,7 @@ UpdateEntityOperator::~UpdateEntityOperator() {
// does this entity tree element contain the old entity
bool UpdateEntityOperator::subTreeContainsOldEntity(OctreeElementPointer element) {
bool UpdateEntityOperator::subTreeContainsOldEntity(const OctreeElementPointer& element) {
// We've found cases where the old entity might be placed in an element that is not actually the best fit
// so when we're searching the tree for the old element, we use the known cube for the known containing element
@ -95,7 +95,7 @@ bool UpdateEntityOperator::subTreeContainsOldEntity(OctreeElementPointer element
return elementContainsOldBox;
}
bool UpdateEntityOperator::subTreeContainsNewEntity(OctreeElementPointer element) {
bool UpdateEntityOperator::subTreeContainsNewEntity(const OctreeElementPointer& element) {
bool elementContainsNewBox = element->getAACube().contains(_newEntityBox);
if (_wantDebug) {
@ -112,7 +112,7 @@ bool UpdateEntityOperator::subTreeContainsNewEntity(OctreeElementPointer element
}
bool UpdateEntityOperator::preRecursion(OctreeElementPointer element) {
bool UpdateEntityOperator::preRecursion(const OctreeElementPointer& element) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
// In Pre-recursion, we're generally deciding whether or not we want to recurse this
@ -238,7 +238,7 @@ bool UpdateEntityOperator::preRecursion(OctreeElementPointer element) {
return keepSearching; // if we haven't yet found it, keep looking
}
bool UpdateEntityOperator::postRecursion(OctreeElementPointer element) {
bool UpdateEntityOperator::postRecursion(const OctreeElementPointer& element) {
// Post-recursion is the unwinding process. For this operation, while we
// unwind we want to mark the path as being dirty if we changed it below.
// We might have two paths, one for the old entity and one for the new entity.
@ -270,7 +270,7 @@ bool UpdateEntityOperator::postRecursion(OctreeElementPointer element) {
return keepSearching; // if we haven't yet found it, keep looking
}
OctreeElementPointer UpdateEntityOperator::possiblyCreateChildAt(OctreeElementPointer element, int childIndex) {
OctreeElementPointer UpdateEntityOperator::possiblyCreateChildAt(const OctreeElementPointer& element, int childIndex) {
// If we're getting called, it's because there was no child element at this index while recursing.
// We only care if this happens while still searching for the new entity location.
// Check to see if

View file

@ -25,9 +25,9 @@ public:
~UpdateEntityOperator();
virtual bool preRecursion(OctreeElementPointer element) override;
virtual bool postRecursion(OctreeElementPointer element) override;
virtual OctreeElementPointer possiblyCreateChildAt(OctreeElementPointer element, int childIndex) override;
virtual bool preRecursion(const OctreeElementPointer& element) override;
virtual bool postRecursion(const OctreeElementPointer& element) override;
virtual OctreeElementPointer possiblyCreateChildAt(const OctreeElementPointer& element, int childIndex) override;
private:
EntityTreePointer _tree;
EntityItemPointer _existingEntity;
@ -45,8 +45,8 @@ private:
AABox _oldEntityBox; // clamped to domain
AABox _newEntityBox; // clamped to domain
bool subTreeContainsOldEntity(OctreeElementPointer element);
bool subTreeContainsNewEntity(OctreeElementPointer element);
bool subTreeContainsOldEntity(const OctreeElementPointer& element);
bool subTreeContainsNewEntity(const OctreeElementPointer& element);
bool _wantDebug;
};

View file

@ -1,3 +1,4 @@
set(TARGET_NAME fbx)
setup_hifi_library()
link_hifi_libraries(shared model networking)
include_hifi_library_headers(gpu)

View file

@ -115,6 +115,9 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
(&::gpu::gl::GLBackend::do_resetStages),
(&::gpu::gl::GLBackend::do_disableContextStereo),
(&::gpu::gl::GLBackend::do_restoreContextStereo),
(&::gpu::gl::GLBackend::do_runLambda),
(&::gpu::gl::GLBackend::do_startNamedCall),
@ -224,6 +227,14 @@ void GLBackend::renderPassTransfer(const Batch& batch) {
_transform.preUpdate(_commandIndex, _stereo);
break;
case Batch::COMMAND_disableContextStereo:
_stereo._contextDisable = true;
break;
case Batch::COMMAND_restoreContextStereo:
_stereo._contextDisable = false;
break;
case Batch::COMMAND_setViewportTransform:
case Batch::COMMAND_setViewTransform:
case Batch::COMMAND_setProjectionTransform: {
@ -308,16 +319,16 @@ void GLBackend::render(const Batch& batch) {
}
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
if (_stereo._enable) {
if (_stereo.isStereo()) {
glEnable(GL_CLIP_DISTANCE0);
}
#endif
{
PROFILE_RANGE(render_gpu_gl_detail, _stereo._enable ? "Render Stereo" : "Render");
PROFILE_RANGE(render_gpu_gl_detail, _stereo.isStereo() ? "Render Stereo" : "Render");
renderPassDraw(batch);
}
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
if (_stereo._enable) {
if (_stereo.isStereo()) {
glDisable(GL_CLIP_DISTANCE0);
}
#endif
@ -358,6 +369,15 @@ void GLBackend::do_resetStages(const Batch& batch, size_t paramOffset) {
resetStages();
}
void GLBackend::do_disableContextStereo(const Batch& batch, size_t paramOffset) {
}
void GLBackend::do_restoreContextStereo(const Batch& batch, size_t paramOffset) {
}
void GLBackend::do_runLambda(const Batch& batch, size_t paramOffset) {
std::function<void()> f = batch._lambdas.get(batch._params[paramOffset]._uint);
f();

View file

@ -143,6 +143,9 @@ public:
// Reset stages
virtual void do_resetStages(const Batch& batch, size_t paramOffset) final;
virtual void do_disableContextStereo(const Batch& batch, size_t paramOffset) final;
virtual void do_restoreContextStereo(const Batch& batch, size_t paramOffset) final;
virtual void do_runLambda(const Batch& batch, size_t paramOffset) final;
virtual void do_startNamedCall(const Batch& batch, size_t paramOffset) final;

View file

@ -48,7 +48,7 @@ void GLBackend::do_setFramebuffer(const Batch& batch, size_t paramOffset) {
}
void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) {
if (_stereo._enable && !_pipeline._stateCache.scissorEnable) {
if (_stereo.isStereo() && !_pipeline._stateCache.scissorEnable) {
qWarning("Clear without scissor in stereo mode");
}

View file

@ -322,7 +322,7 @@ void GLBackend::do_setStateScissorRect(const Batch& batch, size_t paramOffset) {
Vec4i rect;
memcpy(&rect, batch.readData(batch._params[paramOffset]._uint), sizeof(Vec4i));
if (_stereo._enable) {
if (_stereo.isStereo()) {
rect.z /= 2;
if (_stereo._pass) {
rect.x += rect.z;

View file

@ -37,7 +37,7 @@ void GLBackend::do_setViewportTransform(const Batch& batch, size_t paramOffset)
glViewport(vp.x, vp.y, vp.z, vp.w);
// Where we assign the GL viewport
if (_stereo._enable) {
if (_stereo.isStereo()) {
vp.z /= 2;
if (_stereo._pass) {
vp.x += vp.z;
@ -119,7 +119,7 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo
size_t offset = _cameraUboSize * _cameras.size();
_cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset));
if (stereo._enable) {
if (stereo.isStereo()) {
#ifdef GPU_STEREO_CAMERA_BUFFER
_cameras.push_back(CameraBufferElement(_camera.getEyeCamera(0, stereo, _view), _camera.getEyeCamera(1, stereo, _view)));
#else
@ -151,7 +151,7 @@ void GLBackend::TransformStageState::update(size_t commandIndex, const StereoSta
#ifdef GPU_STEREO_CAMERA_BUFFER
bindCurrentCamera(0);
#else
if (!stereo._enable) {
if (!stereo.isStereo()) {
bindCurrentCamera(0);
}
#endif

View file

@ -223,12 +223,21 @@ TransferJob::TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t t
// Buffering can invoke disk IO, so it should be off of the main and render threads
_bufferingLambda = [=] {
_mipData = _parent._gpuObject.accessStoredMipFace(sourceMip, face)->createView(_transferSize, _transferOffset);
auto mipStorage = _parent._gpuObject.accessStoredMipFace(sourceMip, face);
if (mipStorage) {
_mipData = mipStorage->createView(_transferSize, _transferOffset);
} else {
qCWarning(gpugllogging) << "Buffering failed because mip could not be retrieved from texture " << _parent._source.c_str() ;
}
};
_transferLambda = [=] {
_parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, internalFormat, format, type, _mipData->size(), _mipData->readData());
_mipData.reset();
if (_mipData) {
_parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, internalFormat, format, type, _mipData->size(), _mipData->readData());
_mipData.reset();
} else {
qCWarning(gpugllogging) << "Transfer failed because mip could not be retrieved from texture " << _parent._source.c_str();
}
};
}

View file

@ -123,7 +123,7 @@ GLuint GL45Texture::allocate(const Texture& texture) {
glCreateTextures(getGLTextureType(texture), 1, &result);
#ifdef DEBUG
auto source = texture.source();
glObjectLabel(GL_TEXTURE, result, source.length(), source.data());
glObjectLabel(GL_TEXTURE, result, (GLsizei)source.length(), source.data());
#endif
return result;
}

View file

@ -390,6 +390,15 @@ void Batch::resetStages() {
ADD_COMMAND(resetStages);
}
void Batch::disableContextStereo() {
ADD_COMMAND(disableContextStereo);
}
void Batch::restoreContextStereo() {
ADD_COMMAND(restoreContextStereo);
}
void Batch::runLambda(std::function<void()> f) {
ADD_COMMAND(runLambda);
_params.emplace_back(_lambdas.cache(f));

View file

@ -217,6 +217,9 @@ public:
// Reset the stage caches and states
void resetStages();
void disableContextStereo();
void restoreContextStereo();
// Debugging
void pushProfileRange(const char* name);
void popProfileRange();
@ -301,6 +304,9 @@ public:
COMMAND_resetStages,
COMMAND_disableContextStereo,
COMMAND_restoreContextStereo,
COMMAND_runLambda,
COMMAND_startNamedCall,
@ -467,7 +473,7 @@ public:
NamedBatchDataMap _namedData;
bool _enableStereo{ true };
bool _enableSkybox{ false };
bool _enableSkybox { false };
protected:
friend class Context;

View file

@ -145,7 +145,7 @@ void Context::enableStereo(bool enable) {
}
bool Context::isStereo() {
return _stereo._enable;
return _stereo.isStereo();
}
void Context::setStereoProjections(const mat4 eyeProjections[2]) {

View file

@ -118,7 +118,7 @@ public:
protected:
virtual bool isStereo() {
return _stereo._enable;
return _stereo.isStereo();
}
void getStereoProjections(mat4* eyeProjections) const {

View file

@ -93,7 +93,11 @@ namespace gpu {
using TextureViews = std::vector<TextureView>;
struct StereoState {
bool isStereo() const {
return _enable && !_contextDisable;
}
bool _enable{ false };
bool _contextDisable { false };
bool _skybox{ false };
// 0 for left eye, 1 for right eye
uint8 _pass{ 0 };

View file

@ -18,7 +18,7 @@
#include <QUrl>
#include <shared/Storage.h>
#include <shared/FileCache.h>
#include "Forward.h"
#include "Resource.h"
#include "Metric.h"
@ -311,6 +311,7 @@ public:
class KtxStorage : public Storage {
public:
KtxStorage(const std::string& filename);
KtxStorage(const cache::FilePointer& file);
PixelsPointer getMipFace(uint16 level, uint8 face = 0) const override;
Size getMipFaceSize(uint16 level, uint8 face = 0) const override;
bool isMipAvailable(uint16 level, uint8 face = 0) const override;
@ -328,6 +329,7 @@ public:
mutable std::weak_ptr<storage::FileStorage> _cacheFile;
std::string _filename;
cache::FilePointer _cacheEntry;
std::atomic<uint8_t> _minMipLevelAvailable;
size_t _offsetToMinMipKV;
@ -499,6 +501,7 @@ public:
void setStorage(std::unique_ptr<Storage>& newStorage);
void setKtxBacking(const std::string& filename);
void setKtxBacking(const cache::FilePointer& cacheEntry);
// Usage is a a set of flags providing Semantic about the usage of the Texture.
void setUsage(const Usage& usage) { _usage = usage; }
@ -529,8 +532,9 @@ public:
// Serialize a texture into a KTX file
static ktx::KTXUniquePointer serialize(const Texture& texture);
static TexturePointer build(const ktx::KTXDescriptor& descriptor);
static TexturePointer unserialize(const std::string& ktxFile);
static TexturePointer unserialize(const std::string& ktxFile, const ktx::KTXDescriptor& descriptor);
static TexturePointer unserialize(const cache::FilePointer& cacheEntry);
static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header);
static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat);

View file

@ -154,6 +154,10 @@ struct IrradianceKTXPayload {
};
const std::string IrradianceKTXPayload::KEY{ "hifi.irradianceSH" };
KtxStorage::KtxStorage(const cache::FilePointer& cacheEntry) : KtxStorage(cacheEntry->getFilepath()) {
_cacheEntry = cacheEntry;
}
KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) {
{
// We are doing a lot of work here just to get descriptor data
@ -295,20 +299,35 @@ void KtxStorage::assignMipFaceData(uint16 level, uint8 face, const storage::Stor
throw std::runtime_error("Invalid call");
}
bool validKtx(const std::string& filename) {
ktx::StoragePointer storage { new storage::FileStorage(filename.c_str()) };
auto ktxPointer = ktx::KTX::create(storage);
if (!ktxPointer) {
return false;
}
return true;
}
void Texture::setKtxBacking(const std::string& filename) {
// Check the KTX file for validity before using it as backing storage
{
ktx::StoragePointer storage { new storage::FileStorage(filename.c_str()) };
auto ktxPointer = ktx::KTX::create(storage);
if (!ktxPointer) {
return;
}
if (!validKtx(filename)) {
return;
}
auto newBacking = std::unique_ptr<Storage>(new KtxStorage(filename));
setStorage(newBacking);
}
void Texture::setKtxBacking(const cache::FilePointer& cacheEntry) {
// Check the KTX file for validity before using it as backing storage
if (!validKtx(cacheEntry->getFilepath())) {
return;
}
auto newBacking = std::unique_ptr<Storage>(new KtxStorage(cacheEntry));
setStorage(newBacking);
}
ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
ktx::Header header;
@ -442,21 +461,10 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) {
return ktxBuffer;
}
TexturePointer Texture::unserialize(const std::string& ktxfile) {
std::unique_ptr<ktx::KTX> ktxPointer = ktx::KTX::create(std::make_shared<storage::FileStorage>(ktxfile.c_str()));
if (!ktxPointer) {
return nullptr;
}
ktx::KTXDescriptor descriptor { ktxPointer->toDescriptor() };
return unserialize(ktxfile, ktxPointer->toDescriptor());
}
TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDescriptor& descriptor) {
const auto& header = descriptor.header;
TexturePointer Texture::build(const ktx::KTXDescriptor& descriptor) {
Format mipFormat = Format::COLOR_BGRA_32;
Format texelFormat = Format::COLOR_SRGBA_32;
const auto& header = descriptor.header;
if (!Texture::evalTextureFormat(header, mipFormat, texelFormat)) {
return nullptr;
@ -485,20 +493,19 @@ TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDe
}
auto texture = create(gpuktxKeyValue._usageType,
type,
texelFormat,
header.getPixelWidth(),
header.getPixelHeight(),
header.getPixelDepth(),
1, // num Samples
header.getNumberOfSlices(),
header.getNumberOfLevels(),
gpuktxKeyValue._samplerDesc);
type,
texelFormat,
header.getPixelWidth(),
header.getPixelHeight(),
header.getPixelDepth(),
1, // num Samples
header.getNumberOfSlices(),
header.getNumberOfLevels(),
gpuktxKeyValue._samplerDesc);
texture->setUsage(gpuktxKeyValue._usage);
// Assing the mips availables
texture->setStoredMipFormat(mipFormat);
texture->setKtxBacking(ktxfile);
IrradianceKTXPayload irradianceKtxKeyValue;
if (IrradianceKTXPayload::findInKeyValues(descriptor.keyValues, irradianceKtxKeyValue)) {
@ -508,6 +515,36 @@ TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDe
return texture;
}
TexturePointer Texture::unserialize(const cache::FilePointer& cacheEntry) {
std::unique_ptr<ktx::KTX> ktxPointer = ktx::KTX::create(std::make_shared<storage::FileStorage>(cacheEntry->getFilepath().c_str()));
if (!ktxPointer) {
return nullptr;
}
auto texture = build(ktxPointer->toDescriptor());
if (texture) {
texture->setKtxBacking(cacheEntry);
}
return texture;
}
TexturePointer Texture::unserialize(const std::string& ktxfile) {
std::unique_ptr<ktx::KTX> ktxPointer = ktx::KTX::create(std::make_shared<storage::FileStorage>(ktxfile.c_str()));
if (!ktxPointer) {
return nullptr;
}
auto texture = build(ktxPointer->toDescriptor());
if (texture) {
texture->setKtxBacking(ktxfile);
}
return texture;
}
bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header) {
if (texelFormat == Format::COLOR_RGBA_32 && mipFormat == Format::COLOR_BGRA_32) {
header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::BGRA, ktx::GLInternalFormat::RGBA8, ktx::GLBaseInternalFormat::RGBA);

View file

@ -1,3 +1,3 @@
set(TARGET_NAME ktx)
setup_hifi_library()
link_hifi_libraries()
include_hifi_library_headers(shared)

View file

@ -16,6 +16,8 @@
using namespace ktx;
int ktxDescriptorMetaTypeId = qRegisterMetaType<KTXDescriptor*>();
const Header::Identifier ktx::Header::IDENTIFIER {{
0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A
}};

View file

@ -387,4 +387,6 @@ namespace ktx {
}
Q_DECLARE_METATYPE(ktx::KTXDescriptor*);
#endif // hifi_ktx_KTX_h

View file

@ -1,4 +1,4 @@
set(TARGET_NAME model-networking)
setup_hifi_library()
link_hifi_libraries(shared networking model fbx ktx image)
include_hifi_library_headers(gpu)

View file

@ -14,7 +14,7 @@
#include <QUrl>
#include <FileCache.h>
#include <shared/FileCache.h>
namespace ktx {
class KTX;

View file

@ -13,6 +13,8 @@
#include <mutex>
#include <QtConcurrent/QtConcurrentRun>
#include <QCryptographicHash>
#include <QImageReader>
#include <QRunnable>
@ -50,6 +52,9 @@ Q_LOGGING_CATEGORY(trace_resource_parse_image_ktx, "trace.resource.parse.image.k
const std::string TextureCache::KTX_DIRNAME { "ktx_cache" };
const std::string TextureCache::KTX_EXT { "ktx" };
static const QString RESOURCE_SCHEME = "resource";
static const QUrl SPECTATOR_CAMERA_FRAME_URL("resource://spectatorCameraFrame");
static const float SKYBOX_LOAD_PRIORITY { 10.0f }; // Make sure skybox loads first
static const float HIGH_MIPS_LOAD_PRIORITY { 9.0f }; // Make sure high mips loads after skybox but before models
@ -180,6 +185,9 @@ ScriptableResource* TextureCache::prefetch(const QUrl& url, int type, int maxNum
}
NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) {
if (url.scheme() == RESOURCE_SCHEME) {
return getResourceTexture(url);
}
TextureExtra extra = { type, content, maxNumPixels };
return ResourceCache::getResource(url, QUrl(), &extra).staticCast<NetworkTexture>();
}
@ -265,6 +273,18 @@ QSharedPointer<Resource> TextureCache::createResource(const QUrl& url, const QSh
return QSharedPointer<Resource>(texture, &Resource::deleter);
}
NetworkTexture::NetworkTexture(const QUrl& url) :
Resource(url),
_type(),
_sourceIsKTX(false),
_maxNumPixels(100)
{
_textureSource = std::make_shared<gpu::TextureSource>();
_lowestRequestedMipLevel = 0;
_loaded = true;
}
NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) :
Resource(url),
_type(type),
@ -303,14 +323,12 @@ void NetworkTexture::setImage(gpu::TexturePointer texture, int originalWidth,
_width = texture->getWidth();
_height = texture->getHeight();
setSize(texture->getStoredSize());
finishedLoading(true);
} else {
// FIXME: If !gpuTexture, we failed to load!
_width = _height = 0;
qWarning() << "Texture did not load";
finishedLoading(false);
}
finishedLoading(true);
emit networkTextureCreated(qWeakPointerCast<NetworkTexture, Resource> (_self));
}
@ -382,8 +400,7 @@ void NetworkTexture::makeRequest() {
emit loading();
connect(_ktxHeaderRequest, &ResourceRequest::progress, this, &NetworkTexture::ktxHeaderRequestProgress);
connect(_ktxHeaderRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxHeaderRequestFinished);
connect(_ktxHeaderRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxInitialDataRequestFinished);
_bytesReceived = _bytesTotal = _bytes = 0;
@ -407,18 +424,18 @@ void NetworkTexture::makeRequest() {
}
void NetworkTexture::startRequestForNextMipLevel() {
if (_lowestKnownPopulatedMip == 0) {
qWarning(networking) << "Requesting next mip level but all have been fulfilled: " << _lowestKnownPopulatedMip
<< " " << _textureSource->getGPUTexture()->minAvailableMipLevel() << " " << _url;
auto self = _self.lock();
if (!self) {
return;
}
if (_ktxResourceState == WAITING_FOR_MIP_REQUEST) {
auto self = _self.lock();
if (!self) {
return;
}
auto texture = _textureSource->getGPUTexture();
if (!texture || _ktxResourceState != WAITING_FOR_MIP_REQUEST) {
return;
}
_lowestKnownPopulatedMip = texture->minAvailableMipLevel();
if (_lowestRequestedMipLevel < _lowestKnownPopulatedMip) {
_ktxResourceState = PENDING_MIP_REQUEST;
init(false);
@ -453,6 +470,8 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) {
ByteRange range;
range.fromInclusive = -HIGH_MIP_MAX_SIZE;
_ktxMipRequest->setByteRange(range);
connect(_ktxMipRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxInitialDataRequestFinished);
} else {
ByteRange range;
range.fromInclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData
@ -460,228 +479,315 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) {
range.toExclusive = ktx::KTX_HEADER_SIZE + _originalKtxDescriptor->header.bytesOfKeyValueData
+ _originalKtxDescriptor->images[high + 1]._imageOffset;
_ktxMipRequest->setByteRange(range);
}
connect(_ktxMipRequest, &ResourceRequest::progress, this, &NetworkTexture::ktxMipRequestProgress);
connect(_ktxMipRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxMipRequestFinished);
connect(_ktxMipRequest, &ResourceRequest::finished, this, &NetworkTexture::ktxMipRequestFinished);
}
_ktxMipRequest->send();
}
void NetworkTexture::ktxHeaderRequestFinished() {
Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA);
if (!_ktxHeaderRequest) {
// This is called when the header or top mips have been loaded
void NetworkTexture::ktxInitialDataRequestFinished() {
if (!_ktxHeaderRequest || _ktxHeaderRequest->getState() != ResourceRequest::Finished ||
!_ktxMipRequest || _ktxMipRequest->getState() != ResourceRequest::Finished) {
// Wait for both request to be finished
return;
}
_ktxHeaderRequestFinished = true;
maybeHandleFinishedInitialLoad();
Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA);
Q_ASSERT_X(_ktxHeaderRequest && _ktxMipRequest, __FUNCTION__, "Request should not be null while in ktxInitialDataRequestFinished");
PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID), {
{ "from_cache", _ktxHeaderRequest->loadedFromCache() },
{ "size_mb", _bytesTotal / 1000000.0 }
});
PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } });
setSize(_bytesTotal);
TextureCache::requestCompleted(_self);
auto result = _ktxHeaderRequest->getResult();
if (result == ResourceRequest::Success) {
result = _ktxMipRequest->getResult();
}
if (result == ResourceRequest::Success) {
auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString());
qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo);
_ktxHeaderData = _ktxHeaderRequest->getData();
_ktxHighMipData = _ktxMipRequest->getData();
handleFinishedInitialLoad();
} else {
if (handleFailedRequest(result)) {
_ktxResourceState = PENDING_INITIAL_LOAD;
} else {
_ktxResourceState = FAILED_TO_LOAD;
}
}
_ktxHeaderRequest->disconnect(this);
_ktxHeaderRequest->deleteLater();
_ktxHeaderRequest = nullptr;
_ktxMipRequest->disconnect(this);
_ktxMipRequest->deleteLater();
_ktxMipRequest = nullptr;
}
void NetworkTexture::ktxMipRequestFinished() {
Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA || _ktxResourceState == REQUESTING_MIP);
Q_ASSERT_X(_ktxMipRequest, __FUNCTION__, "Request should not be null while in ktxMipRequestFinished");
Q_ASSERT(_ktxResourceState == REQUESTING_MIP);
if (!_ktxMipRequest) {
PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID), {
{ "from_cache", _ktxMipRequest->loadedFromCache() },
{ "size_mb", _bytesTotal / 1000000.0 }
});
PROFILE_RANGE_EX(resource_parse_image, __FUNCTION__, 0xffff0000, 0, { { "url", _url.toString() } });
setSize(_bytesTotal);
if (!_ktxMipRequest || _ktxMipRequest != sender()) {
// This can happen in the edge case that a request is timed out, but a `finished` signal is emitted before it is deleted.
qWarning(networking) << "Received signal NetworkTexture::ktxMipRequestFinished from ResourceRequest that is not the current"
<< " request: " << sender() << ", " << _ktxMipRequest;
return;
}
if (_ktxResourceState == LOADING_INITIAL_DATA) {
_ktxHighMipRequestFinished = true;
maybeHandleFinishedInitialLoad();
} else if (_ktxResourceState == REQUESTING_MIP) {
Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL);
TextureCache::requestCompleted(_self);
TextureCache::requestCompleted(_self);
if (_ktxMipRequest->getResult() == ResourceRequest::Success) {
auto result = _ktxMipRequest->getResult();
if (result == ResourceRequest::Success) {
auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString());
qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo);
if (_ktxResourceState == REQUESTING_MIP) {
Q_ASSERT(_ktxMipLevelRangeInFlight.first != NULL_MIP_LEVEL);
Q_ASSERT(_ktxMipLevelRangeInFlight.second - _ktxMipLevelRangeInFlight.first == 0);
_ktxResourceState = WAITING_FOR_MIP_REQUEST;
auto self = _self;
auto url = _url;
auto data = _ktxMipRequest->getData();
auto mipLevel = _ktxMipLevelRangeInFlight.first;
auto texture = _textureSource->getGPUTexture();
if (texture) {
texture->assignStoredMip(_ktxMipLevelRangeInFlight.first,
_ktxMipRequest->getData().size(), reinterpret_cast<uint8_t*>(_ktxMipRequest->getData().data()));
DependencyManager::get<StatTracker>()->incrementStat("PendingProcessing");
QtConcurrent::run(QThreadPool::globalInstance(), [self, data, mipLevel, url, texture] {
PROFILE_RANGE_EX(resource_parse_image, "NetworkTexture - Processing Mip Data", 0xffff0000, 0, { { "url", url.toString() } });
DependencyManager::get<StatTracker>()->decrementStat("PendingProcessing");
CounterStat counter("Processing");
if (texture->minAvailableMipLevel() <= _ktxMipLevelRangeInFlight.first) {
_lowestKnownPopulatedMip = texture->minAvailableMipLevel();
_ktxResourceState = WAITING_FOR_MIP_REQUEST;
} else {
qWarning(networking) << "Failed to load mip: " << _url << ":" << _ktxMipLevelRangeInFlight.first;
_ktxResourceState = FAILED_TO_LOAD;
auto originalPriority = QThread::currentThread()->priority();
if (originalPriority == QThread::InheritPriority) {
originalPriority = QThread::NormalPriority;
}
} else {
_ktxResourceState = WAITING_FOR_MIP_REQUEST;
qWarning(networking) << "Trying to update mips but texture is null";
}
finishedLoading(true);
QThread::currentThread()->setPriority(QThread::LowPriority);
Finally restorePriority([originalPriority] { QThread::currentThread()->setPriority(originalPriority); });
auto resource = self.lock();
if (!resource) {
// Resource no longer exists, bail
return;
}
Q_ASSERT_X(texture, "Async - NetworkTexture::ktxMipRequestFinished", "NetworkTexture should have been assigned a GPU texture by now.");
texture->assignStoredMip(mipLevel, data.size(), reinterpret_cast<const uint8_t*>(data.data()));
QMetaObject::invokeMethod(resource.data(), "setImage",
Q_ARG(gpu::TexturePointer, texture),
Q_ARG(int, texture->getWidth()),
Q_ARG(int, texture->getHeight()));
QMetaObject::invokeMethod(resource.data(), "startRequestForNextMipLevel");
});
} else {
qWarning(networking) << "Mip request finished in an unexpected state: " << _ktxResourceState;
finishedLoading(false);
if (handleFailedRequest(_ktxMipRequest->getResult())) {
_ktxResourceState = PENDING_MIP_REQUEST;
} else {
qWarning(networking) << "Failed to load mip: " << _url;
_ktxResourceState = FAILED_TO_LOAD;
}
}
_ktxMipRequest->deleteLater();
_ktxMipRequest = nullptr;
if (_ktxResourceState == WAITING_FOR_MIP_REQUEST && _lowestRequestedMipLevel < _lowestKnownPopulatedMip) {
startRequestForNextMipLevel();
}
} else {
qWarning() << "Mip request finished in an unexpected state: " << _ktxResourceState;
if (handleFailedRequest(result)) {
_ktxResourceState = PENDING_MIP_REQUEST;
} else {
_ktxResourceState = FAILED_TO_LOAD;
}
}
_ktxMipRequest->disconnect(this);
_ktxMipRequest->deleteLater();
_ktxMipRequest = nullptr;
}
// This is called when the header or top mips have been loaded
void NetworkTexture::maybeHandleFinishedInitialLoad() {
// This is called when the header and top mips have been loaded
void NetworkTexture::handleFinishedInitialLoad() {
Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA);
Q_ASSERT(!_ktxHeaderData.isEmpty() && !_ktxHighMipData.isEmpty());
if (_ktxHeaderRequestFinished && _ktxHighMipRequestFinished) {
// create ktx...
auto ktxHeaderData = _ktxHeaderData;
auto ktxHighMipData = _ktxHighMipData;
_ktxHeaderData.clear();
_ktxHighMipData.clear();
TextureCache::requestCompleted(_self);
_ktxResourceState = WAITING_FOR_MIP_REQUEST;
if (_ktxHeaderRequest->getResult() != ResourceRequest::Success || _ktxMipRequest->getResult() != ResourceRequest::Success) {
if (handleFailedRequest(_ktxMipRequest->getResult())) {
_ktxResourceState = PENDING_INITIAL_LOAD;
}
else {
_ktxResourceState = FAILED_TO_LOAD;
}
auto self = _self;
auto url = _url;
DependencyManager::get<StatTracker>()->incrementStat("PendingProcessing");
QtConcurrent::run(QThreadPool::globalInstance(), [self, ktxHeaderData, ktxHighMipData, url] {
PROFILE_RANGE_EX(resource_parse_image, "NetworkTexture - Processing Initial Data", 0xffff0000, 0, { { "url", url.toString() } });
DependencyManager::get<StatTracker>()->decrementStat("PendingProcessing");
CounterStat counter("Processing");
_ktxHeaderRequest->deleteLater();
_ktxHeaderRequest = nullptr;
_ktxMipRequest->deleteLater();
_ktxMipRequest = nullptr;
} else {
// create ktx...
auto ktxHeaderData = _ktxHeaderRequest->getData();
auto ktxHighMipData = _ktxMipRequest->getData();
auto header = reinterpret_cast<const ktx::Header*>(ktxHeaderData.data());
if (!ktx::checkIdentifier(header->identifier)) {
qWarning() << "Cannot load " << _url << ", invalid header identifier";
_ktxResourceState = FAILED_TO_LOAD;
finishedLoading(false);
return;
}
auto kvSize = header->bytesOfKeyValueData;
if (kvSize > (ktxHeaderData.size() - ktx::KTX_HEADER_SIZE)) {
qWarning() << "Cannot load " << _url << ", did not receive all kv data with initial request";
_ktxResourceState = FAILED_TO_LOAD;
finishedLoading(false);
return;
}
auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast<const ktx::Byte*>(ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE);
auto imageDescriptors = header->generateImageDescriptors();
if (imageDescriptors.size() == 0) {
qWarning(networking) << "Failed to process ktx file " << _url;
_ktxResourceState = FAILED_TO_LOAD;
finishedLoading(false);
}
_originalKtxDescriptor.reset(new ktx::KTXDescriptor(*header, keyValues, imageDescriptors));
// Create bare ktx in memory
auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool {
return val._key.compare(gpu::SOURCE_HASH_KEY) == 0;
});
std::string filename;
std::string hash;
if (found == keyValues.end() || found->_value.size() != gpu::SOURCE_HASH_BYTES) {
qWarning("Invalid source hash key found, bailing");
_ktxResourceState = FAILED_TO_LOAD;
finishedLoading(false);
return;
} else {
// at this point the source hash is in binary 16-byte form
// and we need it in a hexadecimal string
auto binaryHash = QByteArray(reinterpret_cast<char*>(found->_value.data()), gpu::SOURCE_HASH_BYTES);
hash = filename = binaryHash.toHex().toStdString();
}
auto textureCache = DependencyManager::get<TextureCache>();
gpu::TexturePointer texture = textureCache->getTextureByHash(hash);
if (!texture) {
KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash);
if (ktxFile) {
texture = gpu::Texture::unserialize(ktxFile->getFilepath());
if (texture) {
texture = textureCache->cacheTextureByHash(hash, texture);
}
}
}
if (!texture) {
auto memKtx = ktx::KTX::createBare(*header, keyValues);
if (!memKtx) {
qWarning() << " Ktx could not be created, bailing";
finishedLoading(false);
return;
}
// Move ktx to file
const char* data = reinterpret_cast<const char*>(memKtx->_storage->data());
size_t length = memKtx->_storage->size();
KTXFilePointer file;
auto& ktxCache = textureCache->_ktxCache;
if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) {
qCWarning(modelnetworking) << _url << " failed to write cache file";
_ktxResourceState = FAILED_TO_LOAD;
finishedLoading(false);
return;
} else {
_file = file;
}
auto newKtxDescriptor = memKtx->toDescriptor();
texture = gpu::Texture::unserialize(_file->getFilepath(), newKtxDescriptor);
texture->setKtxBacking(file->getFilepath());
texture->setSource(filename);
auto& images = _originalKtxDescriptor->images;
size_t imageSizeRemaining = ktxHighMipData.size();
uint8_t* ktxData = reinterpret_cast<uint8_t*>(ktxHighMipData.data());
ktxData += ktxHighMipData.size();
// TODO Move image offset calculation to ktx ImageDescriptor
for (int level = static_cast<int>(images.size()) - 1; level >= 0; --level) {
auto& image = images[level];
if (image._imageSize > imageSizeRemaining) {
break;
}
ktxData -= image._imageSize;
texture->assignStoredMip(static_cast<gpu::uint16>(level), image._imageSize, ktxData);
ktxData -= ktx::IMAGE_SIZE_WIDTH;
imageSizeRemaining -= (image._imageSize + ktx::IMAGE_SIZE_WIDTH);
}
// We replace the texture with the one stored in the cache. This deals with the possible race condition of two different
// images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will
// be the winner
texture = textureCache->cacheTextureByHash(filename, texture);
}
_lowestKnownPopulatedMip = texture->minAvailableMipLevel();
_ktxResourceState = WAITING_FOR_MIP_REQUEST;
setImage(texture, header->getPixelWidth(), header->getPixelHeight());
_ktxHeaderRequest->deleteLater();
_ktxHeaderRequest = nullptr;
_ktxMipRequest->deleteLater();
_ktxMipRequest = nullptr;
auto originalPriority = QThread::currentThread()->priority();
if (originalPriority == QThread::InheritPriority) {
originalPriority = QThread::NormalPriority;
}
startRequestForNextMipLevel();
}
QThread::currentThread()->setPriority(QThread::LowPriority);
Finally restorePriority([originalPriority] { QThread::currentThread()->setPriority(originalPriority); });
auto resource = self.lock();
if (!resource) {
// Resource no longer exists, bail
return;
}
auto header = reinterpret_cast<const ktx::Header*>(ktxHeaderData.data());
if (!ktx::checkIdentifier(header->identifier)) {
qWarning() << "Cannot load " << url << ", invalid header identifier";
QMetaObject::invokeMethod(resource.data(), "setImage",
Q_ARG(gpu::TexturePointer, nullptr),
Q_ARG(int, 0),
Q_ARG(int, 0));
return;
}
auto kvSize = header->bytesOfKeyValueData;
if (kvSize > (ktxHeaderData.size() - ktx::KTX_HEADER_SIZE)) {
qWarning() << "Cannot load " << url << ", did not receive all kv data with initial request";
QMetaObject::invokeMethod(resource.data(), "setImage",
Q_ARG(gpu::TexturePointer, nullptr),
Q_ARG(int, 0),
Q_ARG(int, 0));
return;
}
auto keyValues = ktx::KTX::parseKeyValues(header->bytesOfKeyValueData, reinterpret_cast<const ktx::Byte*>(ktxHeaderData.data()) + ktx::KTX_HEADER_SIZE);
auto imageDescriptors = header->generateImageDescriptors();
if (imageDescriptors.size() == 0) {
qWarning(networking) << "Failed to process ktx file " << url;
QMetaObject::invokeMethod(resource.data(), "setImage",
Q_ARG(gpu::TexturePointer, nullptr),
Q_ARG(int, 0),
Q_ARG(int, 0));
return;
}
auto originalKtxDescriptor = new ktx::KTXDescriptor(*header, keyValues, imageDescriptors);
QMetaObject::invokeMethod(resource.data(), "setOriginalDescriptor",
Q_ARG(ktx::KTXDescriptor*, originalKtxDescriptor));
// Create bare ktx in memory
auto found = std::find_if(keyValues.begin(), keyValues.end(), [](const ktx::KeyValue& val) -> bool {
return val._key.compare(gpu::SOURCE_HASH_KEY) == 0;
});
std::string filename;
std::string hash;
if (found == keyValues.end() || found->_value.size() != gpu::SOURCE_HASH_BYTES) {
qWarning("Invalid source hash key found, bailing");
QMetaObject::invokeMethod(resource.data(), "setImage",
Q_ARG(gpu::TexturePointer, nullptr),
Q_ARG(int, 0),
Q_ARG(int, 0));
return;
} else {
// at this point the source hash is in binary 16-byte form
// and we need it in a hexadecimal string
auto binaryHash = QByteArray(reinterpret_cast<char*>(found->_value.data()), gpu::SOURCE_HASH_BYTES);
hash = filename = binaryHash.toHex().toStdString();
}
auto textureCache = DependencyManager::get<TextureCache>();
gpu::TexturePointer texture = textureCache->getTextureByHash(hash);
if (!texture) {
KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash);
if (ktxFile) {
texture = gpu::Texture::unserialize(ktxFile);
if (texture) {
texture = textureCache->cacheTextureByHash(hash, texture);
}
}
}
if (!texture) {
auto memKtx = ktx::KTX::createBare(*header, keyValues);
if (!memKtx) {
qWarning() << " Ktx could not be created, bailing";
QMetaObject::invokeMethod(resource.data(), "setImage",
Q_ARG(gpu::TexturePointer, nullptr),
Q_ARG(int, 0),
Q_ARG(int, 0));
return;
}
// Move ktx to file
const char* data = reinterpret_cast<const char*>(memKtx->_storage->data());
size_t length = memKtx->_storage->size();
KTXFilePointer file;
auto& ktxCache = textureCache->_ktxCache;
if (!memKtx || !(file = ktxCache.writeFile(data, KTXCache::Metadata(filename, length)))) {
qCWarning(modelnetworking) << url << " failed to write cache file";
QMetaObject::invokeMethod(resource.data(), "setImage",
Q_ARG(gpu::TexturePointer, nullptr),
Q_ARG(int, 0),
Q_ARG(int, 0));
return;
}
auto newKtxDescriptor = memKtx->toDescriptor();
texture = gpu::Texture::build(newKtxDescriptor);
texture->setKtxBacking(file);
texture->setSource(filename);
auto& images = originalKtxDescriptor->images;
size_t imageSizeRemaining = ktxHighMipData.size();
const uint8_t* ktxData = reinterpret_cast<const uint8_t*>(ktxHighMipData.data());
ktxData += ktxHighMipData.size();
// TODO Move image offset calculation to ktx ImageDescriptor
for (int level = static_cast<int>(images.size()) - 1; level >= 0; --level) {
auto& image = images[level];
if (image._imageSize > imageSizeRemaining) {
break;
}
ktxData -= image._imageSize;
texture->assignStoredMip(static_cast<gpu::uint16>(level), image._imageSize, ktxData);
ktxData -= ktx::IMAGE_SIZE_WIDTH;
imageSizeRemaining -= (image._imageSize + ktx::IMAGE_SIZE_WIDTH);
}
// We replace the texture with the one stored in the cache. This deals with the possible race condition of two different
// images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will
// be the winner
texture = textureCache->cacheTextureByHash(filename, texture);
}
QMetaObject::invokeMethod(resource.data(), "setImage",
Q_ARG(gpu::TexturePointer, texture),
Q_ARG(int, texture->getWidth()),
Q_ARG(int, texture->getHeight()));
QMetaObject::invokeMethod(resource.data(), "startRequestForNextMipLevel");
});
}
void NetworkTexture::downloadFinished(const QByteArray& data) {
@ -796,7 +902,7 @@ void ImageReader::read() {
if (!texture) {
KTXFilePointer ktxFile = textureCache->_ktxCache.getFile(hash);
if (ktxFile) {
texture = gpu::Texture::unserialize(ktxFile->getFilepath());
texture = gpu::Texture::unserialize(ktxFile);
if (texture) {
texture = textureCache->cacheTextureByHash(hash, texture);
} else {
@ -844,11 +950,11 @@ void ImageReader::read() {
const char* data = reinterpret_cast<const char*>(memKtx->_storage->data());
size_t length = memKtx->_storage->size();
auto& ktxCache = textureCache->_ktxCache;
networkTexture->_file = ktxCache.writeFile(data, KTXCache::Metadata(hash, length)); //
if (!networkTexture->_file) {
auto file = ktxCache.writeFile(data, KTXCache::Metadata(hash, length));
if (!file) {
qCWarning(modelnetworking) << _url << "file cache failed";
} else {
texture->setKtxBacking(networkTexture->_file->getFilepath());
texture->setKtxBacking(file);
}
} else {
qCWarning(modelnetworking) << "Unable to serialize texture to KTX " << _url;
@ -865,3 +971,32 @@ void ImageReader::read() {
Q_ARG(int, texture->getWidth()),
Q_ARG(int, texture->getHeight()));
}
NetworkTexturePointer TextureCache::getResourceTexture(QUrl resourceTextureUrl) {
gpu::TexturePointer texture;
if (resourceTextureUrl == SPECTATOR_CAMERA_FRAME_URL) {
if (!_spectatorCameraNetworkTexture) {
_spectatorCameraNetworkTexture.reset(new NetworkTexture(resourceTextureUrl));
}
texture = _spectatorCameraFramebuffer->getRenderBuffer(0);
if (texture) {
_spectatorCameraNetworkTexture->setImage(texture, texture->getWidth(), texture->getHeight());
return _spectatorCameraNetworkTexture;
}
}
return NetworkTexturePointer();
}
const gpu::FramebufferPointer& TextureCache::getSpectatorCameraFramebuffer() {
if (!_spectatorCameraFramebuffer) {
resetSpectatorCameraFramebuffer(2048, 1024);
}
return _spectatorCameraFramebuffer;
}
void TextureCache::resetSpectatorCameraFramebuffer(int width, int height) {
_spectatorCameraFramebuffer.reset(gpu::Framebuffer::create("spectatorCamera", gpu::Element::COLOR_SRGBA_32, width, height));
_spectatorCameraNetworkTexture.reset();
}

View file

@ -43,6 +43,7 @@ class NetworkTexture : public Resource, public Texture {
Q_OBJECT
public:
NetworkTexture(const QUrl& url);
NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels);
~NetworkTexture() override;
@ -58,14 +59,13 @@ public:
void refresh() override;
Q_INVOKABLE void setOriginalDescriptor(ktx::KTXDescriptor* descriptor) { _originalKtxDescriptor.reset(descriptor); }
signals:
void networkTextureCreated(const QWeakPointer<NetworkTexture>& self);
public slots:
void ktxHeaderRequestProgress(uint64_t bytesReceived, uint64_t bytesTotal) { }
void ktxHeaderRequestFinished();
void ktxMipRequestProgress(uint64_t bytesReceived, uint64_t bytesTotal) { }
void ktxInitialDataRequestFinished();
void ktxMipRequestFinished();
protected:
@ -74,14 +74,14 @@ protected:
virtual bool isCacheable() const override { return _loaded; }
virtual void downloadFinished(const QByteArray& data) override;
Q_INVOKABLE void loadContent(const QByteArray& content);
Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight);
void startRequestForNextMipLevel();
Q_INVOKABLE void startRequestForNextMipLevel();
void startMipRangeRequest(uint16_t low, uint16_t high);
void maybeHandleFinishedInitialLoad();
void handleFinishedInitialLoad();
private:
friend class KTXReader;
@ -102,16 +102,13 @@ private:
bool _sourceIsKTX { false };
KTXResourceState _ktxResourceState { PENDING_INITIAL_LOAD };
// TODO Can this be removed?
KTXFilePointer _file;
// The current mips that are currently being requested w/ _ktxMipRequest
std::pair<uint16_t, uint16_t> _ktxMipLevelRangeInFlight{ NULL_MIP_LEVEL, NULL_MIP_LEVEL };
ResourceRequest* _ktxHeaderRequest { nullptr };
ResourceRequest* _ktxMipRequest { nullptr };
bool _ktxHeaderRequestFinished{ false };
bool _ktxHighMipRequestFinished{ false };
QByteArray _ktxHeaderData;
QByteArray _ktxHighMipData;
uint16_t _lowestRequestedMipLevel { NULL_MIP_LEVEL };
uint16_t _lowestKnownPopulatedMip { NULL_MIP_LEVEL };
@ -128,6 +125,8 @@ private:
int _width { 0 };
int _height { 0 };
int _maxNumPixels { ABSOLUTE_MAX_TEXTURE_NUM_PIXELS };
friend class TextureCache;
};
using NetworkTexturePointer = QSharedPointer<NetworkTexture>;
@ -166,6 +165,12 @@ public:
gpu::TexturePointer getTextureByHash(const std::string& hash);
gpu::TexturePointer cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture);
/// SpectatorCamera rendering targets.
NetworkTexturePointer getResourceTexture(QUrl resourceTextureUrl);
const gpu::FramebufferPointer& getSpectatorCameraFramebuffer();
void resetSpectatorCameraFramebuffer(int width, int height);
protected:
// Overload ResourceCache::prefetch to allow specifying texture type for loads
Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS);
@ -183,6 +188,7 @@ private:
static const std::string KTX_DIRNAME;
static const std::string KTX_EXT;
KTXCache _ktxCache;
// Map from image hashes to texture weak pointers
std::unordered_map<std::string, std::weak_ptr<gpu::Texture>> _texturesByHashes;
@ -193,6 +199,9 @@ private:
gpu::TexturePointer _grayTexture;
gpu::TexturePointer _blueTexture;
gpu::TexturePointer _blackTexture;
NetworkTexturePointer _spectatorCameraNetworkTexture;
gpu::FramebufferPointer _spectatorCameraFramebuffer;
};
#endif // hifi_TextureCache_h

View file

@ -13,10 +13,10 @@
#include <glm/glm.hpp>
#include "AABox.h"
#include <AABox.h>
#include "gpu/Resource.h"
#include "gpu/Stream.h"
#include <gpu/Resource.h>
#include <gpu/Stream.h>
namespace model {
typedef gpu::BufferView::Index Index;

Some files were not shown because too many files have changed in this diff Show more