Merge branch 'master' of https://github.com/highfidelity/hifi into minimum-edit-entity-filter

This commit is contained in:
howard-stearns 2017-01-19 10:30:18 -08:00
commit c9c9b30c50
34 changed files with 782 additions and 207 deletions

View file

@ -476,8 +476,6 @@ Rectangle {
// Set the userName appropriately // Set the userName appropriately
userModel.setProperty(userIndex, "userName", userName); userModel.setProperty(userIndex, "userName", userName);
userModelData[userIndex].userName = userName; // Defensive programming userModelData[userIndex].userName = userName; // Defensive programming
} else {
console.log("updateUsername() called with unknown UUID: ", userId);
} }
} }
break; break;
@ -493,8 +491,6 @@ Rectangle {
if (userIndex != -1) { if (userIndex != -1) {
userModel.setProperty(userIndex, "audioLevel", audioLevel); userModel.setProperty(userIndex, "audioLevel", audioLevel);
userModelData[userIndex].audioLevel = audioLevel; // Defensive programming userModelData[userIndex].audioLevel = audioLevel; // Defensive programming
} else {
console.log("updateUsername() called with unknown UUID: ", userId);
} }
} }
} }

View file

@ -3398,6 +3398,8 @@ void Application::idle(float nsecsElapsed) {
PROFILE_COUNTER(app, "cpuSystem", { { "system", kernelUserAndSystem.z } }); PROFILE_COUNTER(app, "cpuSystem", { { "system", kernelUserAndSystem.z } });
#endif #endif
auto displayPlugin = getActiveDisplayPlugin(); auto displayPlugin = getActiveDisplayPlugin();
if (displayPlugin) { if (displayPlugin) {
PROFILE_COUNTER_IF_CHANGED(app, "present", float, displayPlugin->presentRate()); PROFILE_COUNTER_IF_CHANGED(app, "present", float, displayPlugin->presentRate());
@ -3407,9 +3409,15 @@ void Application::idle(float nsecsElapsed) {
PROFILE_COUNTER_IF_CHANGED(app, "pendingDownloads", int, ResourceCache::getPendingRequestCount()); PROFILE_COUNTER_IF_CHANGED(app, "pendingDownloads", int, ResourceCache::getPendingRequestCount());
PROFILE_COUNTER_IF_CHANGED(app, "currentProcessing", int, DependencyManager::get<StatTracker>()->getStat("Processing").toInt()); PROFILE_COUNTER_IF_CHANGED(app, "currentProcessing", int, DependencyManager::get<StatTracker>()->getStat("Processing").toInt());
PROFILE_COUNTER_IF_CHANGED(app, "pendingProcessing", int, DependencyManager::get<StatTracker>()->getStat("PendingProcessing").toInt()); PROFILE_COUNTER_IF_CHANGED(app, "pendingProcessing", int, DependencyManager::get<StatTracker>()->getStat("PendingProcessing").toInt());
auto renderConfig = _renderEngine->getConfiguration();
PROFILE_COUNTER_IF_CHANGED(render, "gpuTime", float, (float)_gpuContext->getFrameTimerGPUAverage());
PROFILE_COUNTER(render_detail, "gpuTimes", {
{ "OpaqueRangeTimer", renderConfig->getConfig("OpaqueRangeTimer")->property("gpuRunTime") },
{ "LinearDepth", renderConfig->getConfig("LinearDepth")->property("gpuRunTime") },
{ "SurfaceGeometry", renderConfig->getConfig("SurfaceGeometry")->property("gpuRunTime") },
{ "RenderDeferred", renderConfig->getConfig("RenderDeferred")->property("gpuRunTime") },
{ "ToneAndPostRangeTimer", renderConfig->getConfig("ToneAndPostRangeTimer")->property("gpuRunTime") }
});
PROFILE_RANGE(app, __FUNCTION__); PROFILE_RANGE(app, __FUNCTION__);

View file

@ -62,7 +62,7 @@ const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f);
namespace render { namespace render {
template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) { template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) {
return ItemKey::Builder::opaqueShape(); return ItemKey::Builder::opaqueShape().withTypeMeta();
} }
template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar) { template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar) {
return static_pointer_cast<Avatar>(avatar)->getBounds(); return static_pointer_cast<Avatar>(avatar)->getBounds();
@ -74,6 +74,15 @@ namespace render {
avatarPtr->render(args, qApp->getCamera()->getPosition()); avatarPtr->render(args, qApp->getCamera()->getPosition());
} }
} }
template <> uint32_t metaFetchMetaSubItems(const AvatarSharedPointer& avatar, ItemIDs& subItems) {
auto avatarPtr = static_pointer_cast<Avatar>(avatar);
if (avatarPtr->getSkeletonModel()) {
auto metaSubItems = avatarPtr->getSkeletonModel()->fetchRenderItemIDs();
subItems.insert(subItems.end(), metaSubItems.begin(), metaSubItems.end());
return (uint32_t) metaSubItems.size();
}
return 0;
}
} }
static uint64_t timeProcessingJoints = 0; static uint64_t timeProcessingJoints = 0;

View file

@ -32,6 +32,7 @@ namespace render {
template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar); template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar);
template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar); template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar);
template <> void payloadRender(const AvatarSharedPointer& avatar, RenderArgs* args); template <> void payloadRender(const AvatarSharedPointer& avatar, RenderArgs* args);
template <> uint32_t metaFetchMetaSubItems(const AvatarSharedPointer& avatar, ItemIDs& subItems);
} }
static const float SCALING_RATIO = .05f; static const float SCALING_RATIO = .05f;

View file

@ -482,6 +482,7 @@ void OpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) {
} }
void OpenGLDisplayPlugin::updateFrameData() { void OpenGLDisplayPlugin::updateFrameData() {
PROFILE_RANGE(render, __FUNCTION__)
if (_lockCurrentTexture) { if (_lockCurrentTexture) {
return; return;
} }
@ -596,12 +597,16 @@ void OpenGLDisplayPlugin::internalPresent() {
} }
void OpenGLDisplayPlugin::present() { void OpenGLDisplayPlugin::present() {
PROFILE_RANGE_EX(render, __FUNCTION__, 0xffffff00, (uint64_t)presentCount()) auto frameId = (uint64_t)presentCount();
updateFrameData(); PROFILE_RANGE_EX(render, __FUNCTION__, 0xffffff00, frameId)
{
PROFILE_RANGE_EX(render, "updateFrameData", 0xff00ff00, frameId)
updateFrameData();
}
incrementPresentCount(); incrementPresentCount();
{ {
PROFILE_RANGE_EX(render, "recycle", 0xff00ff00, (uint64_t)presentCount()) PROFILE_RANGE_EX(render, "recycle", 0xff00ff00, frameId)
_gpuContext->recycle(); _gpuContext->recycle();
} }
@ -615,19 +620,19 @@ void OpenGLDisplayPlugin::present() {
_lastFrame = _currentFrame.get(); _lastFrame = _currentFrame.get();
}); });
// Execute the frame rendering commands // Execute the frame rendering commands
PROFILE_RANGE_EX(render, "execute", 0xff00ff00, (uint64_t)presentCount()) PROFILE_RANGE_EX(render, "execute", 0xff00ff00, frameId)
_gpuContext->executeFrame(_currentFrame); _gpuContext->executeFrame(_currentFrame);
} }
// Write all layers to a local framebuffer // Write all layers to a local framebuffer
{ {
PROFILE_RANGE_EX(render, "composite", 0xff00ffff, (uint64_t)presentCount()) PROFILE_RANGE_EX(render, "composite", 0xff00ffff, frameId)
compositeLayers(); compositeLayers();
} }
// Take the composite framebuffer and send it to the output device // Take the composite framebuffer and send it to the output device
{ {
PROFILE_RANGE_EX(render, "internalPresent", 0xff00ffff, (uint64_t)presentCount()) PROFILE_RANGE_EX(render, "internalPresent", 0xff00ffff, frameId)
internalPresent(); internalPresent();
} }

View file

@ -15,21 +15,21 @@
namespace render { namespace render {
template <> const ItemKey payloadGetKey(const RenderableEntityItemProxy::Pointer& payload) { template <> const ItemKey payloadGetKey(const RenderableEntityItemProxy::Pointer& payload) {
if (payload && payload->entity) { if (payload && payload->_entity) {
if (payload->entity->getType() == EntityTypes::Light) { if (payload->_entity->getType() == EntityTypes::Light) {
return ItemKey::Builder::light(); return ItemKey::Builder::light().withTypeMeta();
} }
if (payload && payload->entity->isTransparent()) { if (payload && payload->_entity->isTransparent()) {
return ItemKey::Builder::transparentShape(); return ItemKey::Builder::transparentShape().withTypeMeta();
} }
} }
return ItemKey::Builder::opaqueShape(); return ItemKey::Builder::opaqueShape().withTypeMeta();
} }
template <> const Item::Bound payloadGetBound(const RenderableEntityItemProxy::Pointer& payload) { template <> const Item::Bound payloadGetBound(const RenderableEntityItemProxy::Pointer& payload) {
if (payload && payload->entity) { if (payload && payload->_entity) {
bool success; bool success;
auto result = payload->entity->getAABox(success); auto result = payload->_entity->getAABox(success);
if (!success) { if (!success) {
return render::Item::Bound(); return render::Item::Bound();
} }
@ -39,11 +39,19 @@ namespace render {
} }
template <> void payloadRender(const RenderableEntityItemProxy::Pointer& payload, RenderArgs* args) { template <> void payloadRender(const RenderableEntityItemProxy::Pointer& payload, RenderArgs* args) {
if (args) { if (args) {
if (payload && payload->entity && payload->entity->getVisible()) { if (payload && payload->_entity && payload->_entity->getVisible()) {
payload->entity->render(args); payload->_entity->render(args);
} }
} }
} }
template <> uint32_t metaFetchMetaSubItems(const RenderableEntityItemProxy::Pointer& payload, ItemIDs& subItems) {
auto metaID = payload->_metaID;
if (Item::isValidID(metaID)) {
subItems.emplace_back(metaID);
return 1;
}
return 0;
}
} }
void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status::Getters& statusGetters) { void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status::Getters& statusGetters) {

View file

@ -36,17 +36,19 @@ void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status:
class RenderableEntityItemProxy { class RenderableEntityItemProxy {
public: public:
RenderableEntityItemProxy(EntityItemPointer entity) : entity(entity) { } RenderableEntityItemProxy(EntityItemPointer entity, render::ItemID metaID) : _entity(entity), _metaID(metaID) { }
typedef render::Payload<RenderableEntityItemProxy> Payload; typedef render::Payload<RenderableEntityItemProxy> Payload;
typedef Payload::DataPointer Pointer; typedef Payload::DataPointer Pointer;
EntityItemPointer entity; EntityItemPointer _entity;
render::ItemID _metaID;
}; };
namespace render { namespace render {
template <> const ItemKey payloadGetKey(const RenderableEntityItemProxy::Pointer& payload); template <> const ItemKey payloadGetKey(const RenderableEntityItemProxy::Pointer& payload);
template <> const Item::Bound payloadGetBound(const RenderableEntityItemProxy::Pointer& payload); template <> const Item::Bound payloadGetBound(const RenderableEntityItemProxy::Pointer& payload);
template <> void payloadRender(const RenderableEntityItemProxy::Pointer& payload, RenderArgs* args); template <> void payloadRender(const RenderableEntityItemProxy::Pointer& payload, RenderArgs* args);
template <> uint32_t metaFetchMetaSubItems(const RenderableEntityItemProxy::Pointer& payload, ItemIDs& subItems);
} }
// Mixin class for implementing basic single item rendering // Mixin class for implementing basic single item rendering
@ -55,7 +57,7 @@ public:
bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) { bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
_myItem = scene->allocateID(); _myItem = scene->allocateID();
auto renderData = std::make_shared<RenderableEntityItemProxy>(self); auto renderData = std::make_shared<RenderableEntityItemProxy>(self, _myItem);
auto renderPayload = std::make_shared<RenderableEntityItemProxy::Payload>(renderData); auto renderPayload = std::make_shared<RenderableEntityItemProxy::Payload>(renderData);
render::Item::Status::Getters statusGetters; render::Item::Status::Getters statusGetters;

View file

@ -194,7 +194,7 @@ public:
namespace render { namespace render {
template <> const ItemKey payloadGetKey(const RenderableModelEntityItemMeta::Pointer& payload) { template <> const ItemKey payloadGetKey(const RenderableModelEntityItemMeta::Pointer& payload) {
return ItemKey::Builder::opaqueShape(); return ItemKey::Builder::opaqueShape().withTypeMeta();
} }
template <> const Item::Bound payloadGetBound(const RenderableModelEntityItemMeta::Pointer& payload) { template <> const Item::Bound payloadGetBound(const RenderableModelEntityItemMeta::Pointer& payload) {
@ -216,6 +216,15 @@ namespace render {
} }
} }
} }
template <> uint32_t metaFetchMetaSubItems(const RenderableModelEntityItemMeta::Pointer& payload, ItemIDs& subItems) {
auto modelEntity = std::static_pointer_cast<RenderableModelEntityItem>(payload->entity);
if (modelEntity->hasModel()) {
auto metaSubItems = modelEntity->getModelNotSafe()->fetchRenderItemIDs();
subItems.insert(subItems.end(), metaSubItems.begin(), metaSubItems.end());
return (uint32_t) metaSubItems.size();
}
return 0;
}
} }
bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
@ -473,6 +482,10 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
} }
} }
ModelPointer RenderableModelEntityItem::getModelNotSafe() {
return _model;
}
ModelPointer RenderableModelEntityItem::getModel(QSharedPointer<EntityTreeRenderer> renderer) { ModelPointer RenderableModelEntityItem::getModel(QSharedPointer<EntityTreeRenderer> renderer) {
if (!renderer) { if (!renderer) {
return nullptr; return nullptr;

View file

@ -52,6 +52,7 @@ public:
BoxFace& face, glm::vec3& surfaceNormal, BoxFace& face, glm::vec3& surfaceNormal,
void** intersectedObject, bool precisionPicking) const override; void** intersectedObject, bool precisionPicking) const override;
ModelPointer getModel(QSharedPointer<EntityTreeRenderer> renderer); ModelPointer getModel(QSharedPointer<EntityTreeRenderer> renderer);
ModelPointer getModelNotSafe();
virtual bool needsToCallUpdate() const override; virtual bool needsToCallUpdate() const override;
virtual void update(const quint64& now) override; virtual void update(const quint64& now) override;

View file

@ -673,6 +673,7 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene,
hasTransparent = hasTransparent || renderItem.get()->getShapeKey().isTranslucent(); hasTransparent = hasTransparent || renderItem.get()->getShapeKey().isTranslucent();
verticesCount += renderItem.get()->getVerticesCount(); verticesCount += renderItem.get()->getVerticesCount();
_modelMeshRenderItems.insert(item, renderPayload); _modelMeshRenderItems.insert(item, renderPayload);
_modelMeshRenderItemIDs.emplace_back(item);
} }
somethingAdded = !_modelMeshRenderItems.empty(); somethingAdded = !_modelMeshRenderItems.empty();
@ -695,6 +696,7 @@ void Model::removeFromScene(std::shared_ptr<render::Scene> scene, render::Pendin
foreach (auto item, _modelMeshRenderItems.keys()) { foreach (auto item, _modelMeshRenderItems.keys()) {
pendingChanges.removeItem(item); pendingChanges.removeItem(item);
} }
_modelMeshRenderItemIDs.clear();
_modelMeshRenderItems.clear(); _modelMeshRenderItems.clear();
_modelMeshRenderItemsSet.clear(); _modelMeshRenderItemsSet.clear();
@ -1301,6 +1303,10 @@ AABox Model::getRenderableMeshBound() const {
} }
} }
const render::ItemIDs& Model::fetchRenderItemIDs() const {
return _modelMeshRenderItemIDs;
}
void Model::createRenderItemSet() { void Model::createRenderItemSet() {
if (_collisionGeometry) { if (_collisionGeometry) {
if (_collisionRenderItemsSet.empty()) { if (_collisionRenderItemsSet.empty()) {
@ -1479,6 +1485,7 @@ void ModelBlender::noteRequiresBlend(ModelPointer model) {
{ {
Lock lock(_mutex); Lock lock(_mutex);
_modelsRequiringBlends.insert(model); _modelsRequiringBlends.insert(model);
} }
} }

View file

@ -105,6 +105,7 @@ public:
void setRenderItemsNeedUpdate() { _renderItemsNeedUpdate = true; } void setRenderItemsNeedUpdate() { _renderItemsNeedUpdate = true; }
bool getRenderItemsNeedUpdate() { return _renderItemsNeedUpdate; } bool getRenderItemsNeedUpdate() { return _renderItemsNeedUpdate; }
AABox getRenderableMeshBound() const; AABox getRenderableMeshBound() const;
const render::ItemIDs& fetchRenderItemIDs() const;
bool maybeStartBlender(); bool maybeStartBlender();
@ -389,6 +390,8 @@ protected:
QSet<std::shared_ptr<ModelMeshPartPayload>> _modelMeshRenderItemsSet; QSet<std::shared_ptr<ModelMeshPartPayload>> _modelMeshRenderItemsSet;
QMap<render::ItemID, render::PayloadPointer> _modelMeshRenderItems; QMap<render::ItemID, render::PayloadPointer> _modelMeshRenderItems;
render::ItemIDs _modelMeshRenderItemIDs;
bool _addedToScene { false }; // has been added to scene bool _addedToScene { false }; // has been added to scene
bool _needsFixupInScene { true }; // needs to be removed/re-added to scene bool _needsFixupInScene { true }; // needs to be removed/re-added to scene
bool _needsReload { true }; bool _needsReload { true };

View file

@ -53,14 +53,15 @@ RenderDeferredTask::RenderDeferredTask(RenderFetchCullSortTask::Output items) {
ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>(); ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>();
initDeferredPipelines(*shapePlumber); initDeferredPipelines(*shapePlumber);
// Extract opaques / transparents / lights / overlays // Extract opaques / transparents / lights / metas / overlays / background
const auto opaques = items[0]; const auto opaques = items[RenderFetchCullSortTask::OPAQUE_SHAPE];
const auto transparents = items[1]; const auto transparents = items[RenderFetchCullSortTask::TRANSPARENT_SHAPE];
const auto lights = items[2]; const auto lights = items[RenderFetchCullSortTask::LIGHT];
const auto overlayOpaques = items[3]; const auto metas = items[RenderFetchCullSortTask::META];
const auto overlayTransparents = items[4]; const auto overlayOpaques = items[RenderFetchCullSortTask::OVERLAY_OPAQUE_SHAPE];
const auto background = items[5]; const auto overlayTransparents = items[RenderFetchCullSortTask::OVERLAY_TRANSPARENT_SHAPE];
const auto spatialSelection = items[6]; const auto background = items[RenderFetchCullSortTask::BACKGROUND];
const auto spatialSelection = items[RenderFetchCullSortTask::SPATIAL_SELECTION];
// Prepare deferred, generate the shared Deferred Frame Transform // Prepare deferred, generate the shared Deferred Frame Transform
const auto deferredFrameTransform = addJob<GenerateDeferredFrameTransform>("DeferredFrameTransform"); const auto deferredFrameTransform = addJob<GenerateDeferredFrameTransform>("DeferredFrameTransform");
@ -158,6 +159,11 @@ RenderDeferredTask::RenderDeferredTask(RenderFetchCullSortTask::Output items) {
// Debugging stages // Debugging stages
{ {
// Bounds do not draw on stencil buffer, so they must come last
addJob<DrawBounds>("DrawMetaBounds", metas);
// Debugging Deferred buffer job // Debugging Deferred buffer job
const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer)); const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer));
addJob<DebugDeferredBuffer>("DebugDeferredBuffer", debugFramebuffers); addJob<DebugDeferredBuffer>("DebugDeferredBuffer", debugFramebuffers);

View file

@ -24,8 +24,6 @@
#include <gpu/StandardShaderLib.h> #include <gpu/StandardShaderLib.h>
#include <render/drawItemBounds_vert.h>
#include <render/drawItemBounds_frag.h>
#include "nop_frag.h" #include "nop_frag.h"
using namespace render; using namespace render;
@ -36,13 +34,15 @@ RenderForwardTask::RenderForwardTask(RenderFetchCullSortTask::Output items) {
ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>(); ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>();
initForwardPipelines(*shapePlumber); initForwardPipelines(*shapePlumber);
// Extract opaques / transparents / lights / overlays // Extract opaques / transparents / lights / metas / overlays / background
const auto opaques = items[0]; const auto opaques = items[RenderFetchCullSortTask::OPAQUE_SHAPE];
const auto transparents = items[1]; const auto transparents = items[RenderFetchCullSortTask::TRANSPARENT_SHAPE];
const auto lights = items[2]; const auto lights = items[RenderFetchCullSortTask::LIGHT];
const auto overlayOpaques = items[3]; const auto metas = items[RenderFetchCullSortTask::META];
const auto overlayTransparents = items[4]; const auto overlayOpaques = items[RenderFetchCullSortTask::OVERLAY_OPAQUE_SHAPE];
const auto background = items[5]; const auto overlayTransparents = items[RenderFetchCullSortTask::OVERLAY_TRANSPARENT_SHAPE];
const auto background = items[RenderFetchCullSortTask::BACKGROUND];
const auto spatialSelection = items[RenderFetchCullSortTask::SPATIAL_SELECTION];
const auto framebuffer = addJob<PrepareFramebuffer>("PrepareFramebuffer"); const auto framebuffer = addJob<PrepareFramebuffer>("PrepareFramebuffer");
@ -180,57 +180,4 @@ void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderCo
args->_batch = nullptr; args->_batch = nullptr;
} }
const gpu::PipelinePointer DrawBounds::getPipeline() {
if (!_boundsPipeline) {
auto vs = gpu::Shader::createVertex(std::string(drawItemBounds_vert));
auto ps = gpu::Shader::createPixel(std::string(drawItemBounds_frag));
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::BindingSet slotBindings;
gpu::Shader::makeProgram(*program, slotBindings);
_cornerLocation = program->getUniforms().findLocation("inBoundPos");
_scaleLocation = program->getUniforms().findLocation("inBoundDim");
auto state = std::make_shared<gpu::State>();
state->setDepthTest(true, false, gpu::LESS_EQUAL);
state->setBlendFunction(true,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO);
_boundsPipeline = gpu::Pipeline::create(program, state);
}
return _boundsPipeline;
}
void DrawBounds::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext,
const Inputs& items) {
RenderArgs* args = renderContext->args;
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
args->_batch = &batch;
// Setup projection
glm::mat4 projMat;
Transform viewMat;
args->getViewFrustum().evalProjectionMatrix(projMat);
args->getViewFrustum().evalViewTransform(viewMat);
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
batch.setModelTransform(Transform());
// Bind program
batch.setPipeline(getPipeline());
assert(_cornerLocation >= 0);
assert(_scaleLocation >= 0);
// Render bounds
for (const auto& item : items) {
batch._glUniform3fv(_cornerLocation, 1, (const float*)(&item.bound.getCorner()));
batch._glUniform3fv(_scaleLocation, 1, (const float*)(&item.bound.getScale()));
static const int NUM_VERTICES_PER_CUBE = 24;
batch.draw(gpu::LINES, NUM_VERTICES_PER_CUBE, 0);
}
});
}

View file

@ -68,25 +68,4 @@ public:
const Inputs& background); const Inputs& background);
}; };
class DrawBounds {
public:
class Config : public render::JobConfig {
public:
Config() : JobConfig(false) {}
};
using Inputs = render::ItemBounds;
using JobModel = render::Job::ModelI<DrawBounds, Inputs, Config>;
void configure(const Config& configuration) {}
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext,
const Inputs& items);
private:
const gpu::PipelinePointer getPipeline();
gpu::PipelinePointer _boundsPipeline;
int _cornerLocation { -1 };
int _scaleLocation { -1 };
};
#endif // hifi_RenderForwardTask_h #endif // hifi_RenderForwardTask_h

View file

@ -34,6 +34,13 @@
#include "model_normal_map_frag.h" #include "model_normal_map_frag.h"
#include "model_normal_specular_map_frag.h" #include "model_normal_specular_map_frag.h"
#include "model_specular_map_frag.h" #include "model_specular_map_frag.h"
#include "forward_model_frag.h"
#include "forward_model_unlit_frag.h"
#include "forward_model_normal_map_frag.h"
#include "forward_model_normal_specular_map_frag.h"
#include "forward_model_specular_map_frag.h"
#include "model_lightmap_frag.h" #include "model_lightmap_frag.h"
#include "model_lightmap_normal_map_frag.h" #include "model_lightmap_normal_map_frag.h"
#include "model_lightmap_normal_specular_map_frag.h" #include "model_lightmap_normal_specular_map_frag.h"
@ -227,11 +234,11 @@ void initForwardPipelines(render::ShapePlumber& plumber) {
auto skinModelNormalMapVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_vert)); auto skinModelNormalMapVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_vert));
// Pixel shaders // Pixel shaders
auto modelPixel = gpu::Shader::createPixel(std::string(model_frag)); auto modelPixel = gpu::Shader::createPixel(std::string(forward_model_frag));
auto modelUnlitPixel = gpu::Shader::createPixel(std::string(model_unlit_frag)); auto modelUnlitPixel = gpu::Shader::createPixel(std::string(forward_model_unlit_frag));
auto modelNormalMapPixel = gpu::Shader::createPixel(std::string(model_normal_map_frag)); auto modelNormalMapPixel = gpu::Shader::createPixel(std::string(forward_model_normal_map_frag));
auto modelSpecularMapPixel = gpu::Shader::createPixel(std::string(model_specular_map_frag)); auto modelSpecularMapPixel = gpu::Shader::createPixel(std::string(forward_model_specular_map_frag));
auto modelNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(model_normal_specular_map_frag)); auto modelNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(forward_model_normal_specular_map_frag));
using Key = render::ShapeKey; using Key = render::ShapeKey;
auto addPipeline = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, _3); auto addPipeline = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, _3);

View file

@ -0,0 +1,59 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
// model.frag
// fragment shader
//
// Created by Andrzej Kapolka on 10/14/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include DeferredBufferWrite.slh@>
<@include model/Material.slh@>
<@include MaterialTextures.slh@>
<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$>
in vec4 _position;
in vec3 _normal;
in vec3 _color;
in vec2 _texCoord0;
in vec2 _texCoord1;
void main(void) {
Material mat = getMaterial();
int matKey = getMaterialKey(mat);
<$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, _SCRIBE_NULL, emissiveTex)$>
<$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$>
float opacity = 1.0;
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
<$discardTransparent(opacity)$>;
vec3 albedo = getMaterialAlbedo(mat);
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
albedo *= _color;
float roughness = getMaterialRoughness(mat);
<$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>;
vec3 emissive = getMaterialEmissive(mat);
<$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>;
float scattering = getMaterialScattering(mat);
packDeferredFragment(
normalize(_normal.xyz),
opacity,
albedo,
roughness,
getMaterialMetallic(mat),
emissive,
occlusionTex,
scattering);
}

View file

@ -0,0 +1,64 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// model_normal_map.frag
// fragment shader
//
// Created by Andrzej Kapolka on 10/29/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include DeferredBufferWrite.slh@>
<@include model/Material.slh@>
<@include MaterialTextures.slh@>
<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, _SCRIBE_NULL, EMISSIVE, OCCLUSION, SCATTERING)$>
in vec4 _position;
in vec2 _texCoord0;
in vec2 _texCoord1;
in vec3 _normal;
in vec3 _tangent;
in vec3 _color;
void main(void) {
Material mat = getMaterial();
int matKey = getMaterialKey(mat);
<$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, _SCRIBE_NULL, emissiveTex, scatteringTex)$>
<$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$>
float opacity = 1.0;
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
<$discardTransparent(opacity)$>;
vec3 albedo = getMaterialAlbedo(mat);
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
albedo *= _color;
float roughness = getMaterialRoughness(mat);
<$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>;
vec3 emissive = getMaterialEmissive(mat);
<$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>;
vec3 viewNormal;
<$tangentToViewSpace(normalTex, _normal, _tangent, viewNormal)$>
float scattering = getMaterialScattering(mat);
<$evalMaterialScattering(scatteringTex, scattering, matKey, scattering)$>;
packDeferredFragment(
viewNormal,
opacity,
albedo,
roughness,
getMaterialMetallic(mat),
emissive,
occlusionTex,
scattering);
}

View file

@ -0,0 +1,66 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// model_normal_specular_map.frag
// fragment shader
//
// Created by Andrzej Kapolka on 5/6/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 DeferredBufferWrite.slh@>
<@include model/Material.slh@>
<@include MaterialTextures.slh@>
<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, METALLIC, EMISSIVE, OCCLUSION)$>
in vec4 _position;
in vec2 _texCoord0;
in vec2 _texCoord1;
in vec3 _normal;
in vec3 _tangent;
in vec3 _color;
void main(void) {
Material mat = getMaterial();
int matKey = getMaterialKey(mat);
<$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, metallicTex, emissiveTex)$>
<$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$>
float opacity = 1.0;
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)&>;
<$discardTransparent(opacity)$>;
vec3 albedo = getMaterialAlbedo(mat);
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
albedo *= _color;
float roughness = getMaterialRoughness(mat);
<$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>;
vec3 emissive = getMaterialEmissive(mat);
<$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>;
vec3 viewNormal;
<$tangentToViewSpace(normalTex, _normal, _tangent, viewNormal)$>
float metallic = getMaterialMetallic(mat);
<$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>;
float scattering = getMaterialScattering(mat);
packDeferredFragment(
normalize(viewNormal.xyz),
opacity,
albedo,
roughness,
metallic,
emissive,
occlusionTex,
scattering);
}

View file

@ -0,0 +1,63 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// model_specular_map.frag
// fragment shader
//
// Created by Andrzej Kapolka on 5/6/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 DeferredBufferWrite.slh@>
<@include model/Material.slh@>
<@include MaterialTextures.slh@>
<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC, EMISSIVE, OCCLUSION)$>
in vec4 _position;
in vec2 _texCoord0;
in vec2 _texCoord1;
in vec3 _normal;
in vec3 _color;
void main(void) {
Material mat = getMaterial();
int matKey = getMaterialKey(mat);
<$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, metallicTex, emissiveTex)$>
<$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$>
float opacity = 1.0;
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
<$discardTransparent(opacity)$>;
vec3 albedo = getMaterialAlbedo(mat);
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
albedo *= _color;
float roughness = getMaterialRoughness(mat);
<$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>;
vec3 emissive = getMaterialEmissive(mat);
<$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>;
float metallic = getMaterialMetallic(mat);
<$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>;
float scattering = getMaterialScattering(mat);
packDeferredFragment(
normalize(_normal),
opacity,
albedo,
roughness,
metallic,
emissive,
occlusionTex,
scattering);
}

View file

@ -0,0 +1,45 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// material_opaque_unlit.frag
// fragment shader
//
// Created by Sam Gateau on 5/5/2016.
// Copyright 2016 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 DeferredBufferWrite.slh@>
<@include LightingModel.slh@>
<@include model/Material.slh@>
<@include MaterialTextures.slh@>
<$declareMaterialTextures(ALBEDO)$>
in vec2 _texCoord0;
in vec3 _normal;
in vec3 _color;
in float _alpha;
void main(void) {
Material mat = getMaterial();
int matKey = getMaterialKey(mat);
<$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex)$>
float opacity = 1.0;
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
<$discardTransparent(opacity)$>;
vec3 albedo = getMaterialAlbedo(mat);
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
albedo *= _color;
packDeferredFragmentUnlit(
normalize(_normal),
opacity,
albedo * isUnlitEnabled());
}

View file

@ -170,7 +170,6 @@ namespace render {
for (size_t i = 0; i < NUM_FILTERS; i++) { for (size_t i = 0; i < NUM_FILTERS; i++) {
if (_filters[i].test(itemKey)) { if (_filters[i].test(itemKey)) {
outItems[i].template edit<ItemBounds>().emplace_back(itemBound); outItems[i].template edit<ItemBounds>().emplace_back(itemBound);
break;
} }
} }
} }

View file

@ -19,6 +19,10 @@
#include <ViewFrustum.h> #include <ViewFrustum.h>
#include <gpu/Context.h> #include <gpu/Context.h>
#include <drawItemBounds_vert.h>
#include <drawItemBounds_frag.h>
using namespace render; using namespace render;
void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, int maxDrawnItems) { void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, int maxDrawnItems) {
@ -134,3 +138,59 @@ void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContext
auto config = std::static_pointer_cast<Config>(renderContext->jobConfig); auto config = std::static_pointer_cast<Config>(renderContext->jobConfig);
config->setNumDrawn((int)inLights.size()); config->setNumDrawn((int)inLights.size());
} }
const gpu::PipelinePointer DrawBounds::getPipeline() {
if (!_boundsPipeline) {
auto vs = gpu::Shader::createVertex(std::string(drawItemBounds_vert));
auto ps = gpu::Shader::createPixel(std::string(drawItemBounds_frag));
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::BindingSet slotBindings;
gpu::Shader::makeProgram(*program, slotBindings);
_cornerLocation = program->getUniforms().findLocation("inBoundPos");
_scaleLocation = program->getUniforms().findLocation("inBoundDim");
auto state = std::make_shared<gpu::State>();
state->setDepthTest(true, false, gpu::LESS_EQUAL);
state->setBlendFunction(true,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO);
_boundsPipeline = gpu::Pipeline::create(program, state);
}
return _boundsPipeline;
}
void DrawBounds::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext,
const Inputs& items) {
RenderArgs* args = renderContext->args;
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
args->_batch = &batch;
// Setup projection
glm::mat4 projMat;
Transform viewMat;
args->getViewFrustum().evalProjectionMatrix(projMat);
args->getViewFrustum().evalViewTransform(viewMat);
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
batch.setModelTransform(Transform());
// Bind program
batch.setPipeline(getPipeline());
assert(_cornerLocation >= 0);
assert(_scaleLocation >= 0);
// Render bounds
for (const auto& item : items) {
batch._glUniform3fv(_cornerLocation, 1, (const float*)(&item.bound.getCorner()));
batch._glUniform3fv(_scaleLocation, 1, (const float*)(&item.bound.getScale()));
static const int NUM_VERTICES_PER_CUBE = 24;
batch.draw(gpu::LINES, NUM_VERTICES_PER_CUBE, 0);
}
});
}

View file

@ -50,6 +50,27 @@ protected:
int _maxDrawn; // initialized by Config int _maxDrawn; // initialized by Config
}; };
class DrawBounds {
public:
class Config : public render::JobConfig {
public:
Config() : JobConfig(false) {}
};
using Inputs = render::ItemBounds;
using JobModel = render::Job::ModelI<DrawBounds, Inputs, Config>;
void configure(const Config& configuration) {}
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext,
const Inputs& items);
private:
const gpu::PipelinePointer getPipeline();
gpu::PipelinePointer _boundsPipeline;
int _cornerLocation { -1 };
int _scaleLocation { -1 };
};
} }
#endif // hifi_render_DrawTask_h #endif // hifi_render_DrawTask_h

View file

@ -38,6 +38,7 @@ public:
enum FlagBit { enum FlagBit {
TYPE_SHAPE = 0, // Item is a Shape TYPE_SHAPE = 0, // Item is a Shape
TYPE_LIGHT, // Item is a Light TYPE_LIGHT, // Item is a Light
TYPE_META, // Item is a Meta: meanning it s used to represent a higher level object, potentially represented by other render items
TRANSLUCENT, // Transparent and not opaque, for some odd reason TRANSPARENCY doesn't work... TRANSLUCENT, // Transparent and not opaque, for some odd reason TRANSPARENCY doesn't work...
VIEW_SPACE, // Transformed in view space, and not in world space VIEW_SPACE, // Transformed in view space, and not in world space
DYNAMIC, // Dynamic and bound will change unlike static item DYNAMIC, // Dynamic and bound will change unlike static item
@ -72,6 +73,7 @@ public:
Builder& withTypeShape() { _flags.set(TYPE_SHAPE); return (*this); } Builder& withTypeShape() { _flags.set(TYPE_SHAPE); return (*this); }
Builder& withTypeLight() { _flags.set(TYPE_LIGHT); return (*this); } Builder& withTypeLight() { _flags.set(TYPE_LIGHT); return (*this); }
Builder& withTypeMeta() { _flags.set(TYPE_META); return (*this); }
Builder& withTransparent() { _flags.set(TRANSLUCENT); return (*this); } Builder& withTransparent() { _flags.set(TRANSLUCENT); return (*this); }
Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); } Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); }
Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); } Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); }
@ -91,6 +93,7 @@ public:
bool isShape() const { return _flags[TYPE_SHAPE]; } bool isShape() const { return _flags[TYPE_SHAPE]; }
bool isLight() const { return _flags[TYPE_LIGHT]; } bool isLight() const { return _flags[TYPE_LIGHT]; }
bool isMeta() const { return _flags[TYPE_META]; }
bool isOpaque() const { return !_flags[TRANSLUCENT]; } bool isOpaque() const { return !_flags[TRANSLUCENT]; }
bool isTransparent() const { return _flags[TRANSLUCENT]; } bool isTransparent() const { return _flags[TRANSLUCENT]; }
@ -150,6 +153,7 @@ public:
Builder& withTypeShape() { _value.set(ItemKey::TYPE_SHAPE); _mask.set(ItemKey::TYPE_SHAPE); return (*this); } Builder& withTypeShape() { _value.set(ItemKey::TYPE_SHAPE); _mask.set(ItemKey::TYPE_SHAPE); return (*this); }
Builder& withTypeLight() { _value.set(ItemKey::TYPE_LIGHT); _mask.set(ItemKey::TYPE_LIGHT); return (*this); } Builder& withTypeLight() { _value.set(ItemKey::TYPE_LIGHT); _mask.set(ItemKey::TYPE_LIGHT); return (*this); }
Builder& withTypeMeta() { _value.set(ItemKey::TYPE_META); _mask.set(ItemKey::TYPE_META); return (*this); }
Builder& withOpaque() { _value.reset(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); } Builder& withOpaque() { _value.reset(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); }
Builder& withTransparent() { _value.set(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); } Builder& withTransparent() { _value.set(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); }
@ -179,6 +183,7 @@ public:
static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); } static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); }
static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); } static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); }
static Builder light() { return Builder().withTypeLight(); } static Builder light() { return Builder().withTypeLight(); }
static Builder meta() { return Builder().withTypeMeta(); }
static Builder background() { return Builder().withViewSpace().withLayered(); } static Builder background() { return Builder().withViewSpace().withLayered(); }
static Builder opaqueShapeLayered() { return Builder().withTypeShape().withOpaque().withWorldSpace().withLayered(); } static Builder opaqueShapeLayered() { return Builder().withTypeShape().withOpaque().withWorldSpace().withLayered(); }
static Builder transparentShapeLayered() { return Builder().withTypeShape().withTransparent().withWorldSpace().withLayered(); } static Builder transparentShapeLayered() { return Builder().withTypeShape().withTransparent().withWorldSpace().withLayered(); }
@ -210,6 +215,25 @@ inline QDebug operator<<(QDebug debug, const ItemFilter& me) {
using ItemID = uint32_t; using ItemID = uint32_t;
using ItemCell = int32_t; using ItemCell = int32_t;
// A few typedefs for standard containers of ItemIDs
using ItemIDs = std::vector<ItemID>;
using ItemIDSet = std::set<ItemID>;
// Handy type to just pass the ID and the bound of an item
class ItemBound {
public:
ItemBound(ItemID id) : id(id) { }
ItemBound(ItemID id, const AABox& bound) : id(id), bound(bound) { }
ItemID id;
AABox bound;
};
// many Item Bounds in a vector
using ItemBounds = std::vector<ItemBound>;
// Item is the proxy to a bounded "object" in the scene
// An item is described by its Key
class Item { class Item {
public: public:
typedef std::vector<Item> Vector; typedef std::vector<Item> Vector;
@ -295,6 +319,8 @@ public:
virtual const ShapeKey getShapeKey() const = 0; virtual const ShapeKey getShapeKey() const = 0;
virtual uint32_t fetchMetaSubItems(ItemIDs& subItems) const = 0;
~PayloadInterface() {} ~PayloadInterface() {}
// Status interface is local to the base class // Status interface is local to the base class
@ -313,6 +339,9 @@ public:
Item() {} Item() {}
~Item() {} ~Item() {}
// Item exists if it has a valid payload
bool exist() const { return (bool)(_payload); }
// Main scene / item managment interface reset/update/kill // Main scene / item managment interface reset/update/kill
void resetPayload(const PayloadPointer& payload); void resetPayload(const PayloadPointer& payload);
void resetCell(ItemCell cell = INVALID_CELL, bool _small = false) { _cell = cell; _key.setSmaller(_small); } void resetCell(ItemCell cell = INVALID_CELL, bool _small = false) { _cell = cell; _key.setSmaller(_small); }
@ -339,6 +368,9 @@ public:
// Shape Type Interface // Shape Type Interface
const ShapeKey getShapeKey() const { return _payload->getShapeKey(); } const ShapeKey getShapeKey() const { return _payload->getShapeKey(); }
// Meta Type Interface
uint32_t fetchMetaSubItems(ItemIDs& subItems) const { return _payload->fetchMetaSubItems(subItems); }
// Access the status // Access the status
const StatusPointer& getStatus() const { return _payload->getStatus(); } const StatusPointer& getStatus() const { return _payload->getStatus(); }
@ -370,10 +402,7 @@ inline QDebug operator<<(QDebug debug, const Item& item) {
return debug; return debug;
} }
// THe Payload class is the real Payload to be used // Item shared interface supported by the payload
// THis allow anything to be turned into a Payload as long as the required interface functions are available
// When creating a new kind of payload from a new "stuff" class then you need to create specialized version for "stuff"
// of the Payload interface
template <class T> const ItemKey payloadGetKey(const std::shared_ptr<T>& payloadData) { return ItemKey(); } template <class T> const ItemKey payloadGetKey(const std::shared_ptr<T>& payloadData) { return ItemKey(); }
template <class T> const Item::Bound payloadGetBound(const std::shared_ptr<T>& payloadData) { return Item::Bound(); } template <class T> const Item::Bound payloadGetBound(const std::shared_ptr<T>& payloadData) { return Item::Bound(); }
template <class T> int payloadGetLayer(const std::shared_ptr<T>& payloadData) { return 0; } template <class T> int payloadGetLayer(const std::shared_ptr<T>& payloadData) { return 0; }
@ -385,6 +414,14 @@ template <class T> void payloadRender(const std::shared_ptr<T>& payloadData, Ren
// implying that the shape will setup its own pipeline without the use of the ShapeKey. // implying that the shape will setup its own pipeline without the use of the ShapeKey.
template <class T> const ShapeKey shapeGetShapeKey(const std::shared_ptr<T>& payloadData) { return ShapeKey::Builder::ownPipeline(); } template <class T> const ShapeKey shapeGetShapeKey(const std::shared_ptr<T>& payloadData) { return ShapeKey::Builder::ownPipeline(); }
// Meta Type Interface
// Meta items act as the grouping object for several sub items (typically shapes).
template <class T> uint32_t metaFetchMetaSubItems(const std::shared_ptr<T>& payloadData, ItemIDs& subItems) { return 0; }
// THe Payload class is the real Payload to be used
// THis allow anything to be turned into a Payload as long as the required interface functions are available
// When creating a new kind of payload from a new "stuff" class then you need to create specialized version for "stuff"
// of the Payload interface
template <class T> class Payload : public Item::PayloadInterface { template <class T> class Payload : public Item::PayloadInterface {
public: public:
typedef std::shared_ptr<T> DataPointer; typedef std::shared_ptr<T> DataPointer;
@ -403,6 +440,9 @@ public:
// Shape Type interface // Shape Type interface
virtual const ShapeKey getShapeKey() const override { return shapeGetShapeKey<T>(_data); } virtual const ShapeKey getShapeKey() const override { return shapeGetShapeKey<T>(_data); }
// Meta Type Interface
virtual uint32_t fetchMetaSubItems(ItemIDs& subItems) const override { return metaFetchMetaSubItems<T>(_data, subItems); }
protected: protected:
DataPointer _data; DataPointer _data;
@ -450,22 +490,6 @@ template <> const Item::Bound payloadGetBound(const FooPointer& foo) {
typedef Item::PayloadPointer PayloadPointer; typedef Item::PayloadPointer PayloadPointer;
typedef std::vector< PayloadPointer > Payloads; typedef std::vector< PayloadPointer > Payloads;
// A few typedefs for standard containers of ItemIDs
using ItemIDs = std::vector<ItemID>;
using ItemIDSet = std::set<ItemID>;
// Handy type to just pass the ID and the bound of an item
class ItemBound {
public:
ItemBound(ItemID id) : id(id) { }
ItemBound(ItemID id, const AABox& bound) : id(id), bound(bound) { }
ItemID id;
AABox bound;
};
// many Item Bounds in a vector
using ItemBounds = std::vector<ItemBound>;
// A map of items by ShapeKey to optimize rendering pipeline assignments // A map of items by ShapeKey to optimize rendering pipeline assignments
using ShapeBounds = std::unordered_map<ShapeKey, ItemBounds, ShapeKey::Hash, ShapeKey::KeyEqual>; using ShapeBounds = std::unordered_map<ShapeKey, ItemBounds, ShapeKey::Hash, ShapeKey::KeyEqual>;

View file

@ -29,33 +29,41 @@ RenderFetchCullSortTask::RenderFetchCullSortTask(CullFunctor cullFunctor) {
const auto nonspatialSelection = addJob<FetchNonspatialItems>("FetchOverlaySelection"); const auto nonspatialSelection = addJob<FetchNonspatialItems>("FetchOverlaySelection");
// Multi filter visible items into different buckets // Multi filter visible items into different buckets
const int NUM_FILTERS = 3; const int NUM_SPATIAL_FILTERS = 4;
const int NUM_NON_SPATIAL_FILTERS = 3;
const int OPAQUE_SHAPE_BUCKET = 0; const int OPAQUE_SHAPE_BUCKET = 0;
const int TRANSPARENT_SHAPE_BUCKET = 1; const int TRANSPARENT_SHAPE_BUCKET = 1;
const int LIGHT_BUCKET = 2; const int LIGHT_BUCKET = 2;
const int META_BUCKET = 3;
const int BACKGROUND_BUCKET = 2; const int BACKGROUND_BUCKET = 2;
MultiFilterItem<NUM_FILTERS>::ItemFilterArray spatialFilters = { { MultiFilterItem<NUM_SPATIAL_FILTERS>::ItemFilterArray spatialFilters = { {
ItemFilter::Builder::opaqueShape(), ItemFilter::Builder::opaqueShape(),
ItemFilter::Builder::transparentShape(), ItemFilter::Builder::transparentShape(),
ItemFilter::Builder::light() ItemFilter::Builder::light(),
ItemFilter::Builder::meta()
} }; } };
MultiFilterItem<NUM_FILTERS>::ItemFilterArray nonspatialFilters = { { MultiFilterItem<NUM_NON_SPATIAL_FILTERS>::ItemFilterArray nonspatialFilters = { {
ItemFilter::Builder::opaqueShape(), ItemFilter::Builder::opaqueShape(),
ItemFilter::Builder::transparentShape(), ItemFilter::Builder::transparentShape(),
ItemFilter::Builder::background() ItemFilter::Builder::background()
} }; } };
const auto filteredSpatialBuckets = addJob<MultiFilterItem<NUM_FILTERS>>("FilterSceneSelection", culledSpatialSelection, spatialFilters).get<MultiFilterItem<NUM_FILTERS>::ItemBoundsArray>(); const auto filteredSpatialBuckets =
const auto filteredNonspatialBuckets = addJob<MultiFilterItem<NUM_FILTERS>>("FilterOverlaySelection", nonspatialSelection, nonspatialFilters).get<MultiFilterItem<NUM_FILTERS>::ItemBoundsArray>(); addJob<MultiFilterItem<NUM_SPATIAL_FILTERS>>("FilterSceneSelection", culledSpatialSelection, spatialFilters)
.get<MultiFilterItem<NUM_SPATIAL_FILTERS>::ItemBoundsArray>();
const auto filteredNonspatialBuckets =
addJob<MultiFilterItem<NUM_NON_SPATIAL_FILTERS>>("FilterOverlaySelection", nonspatialSelection, nonspatialFilters)
.get<MultiFilterItem<NUM_NON_SPATIAL_FILTERS>::ItemBoundsArray>();
// Extract opaques / transparents / lights / overlays // Extract opaques / transparents / lights / overlays
const auto opaques = addJob<DepthSortItems>("DepthSortOpaque", filteredSpatialBuckets[OPAQUE_SHAPE_BUCKET]); const auto opaques = addJob<DepthSortItems>("DepthSortOpaque", filteredSpatialBuckets[OPAQUE_SHAPE_BUCKET]);
const auto transparents = addJob<DepthSortItems>("DepthSortTransparent", filteredSpatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); const auto transparents = addJob<DepthSortItems>("DepthSortTransparent", filteredSpatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false));
const auto lights = filteredSpatialBuckets[LIGHT_BUCKET]; const auto lights = filteredSpatialBuckets[LIGHT_BUCKET];
const auto metas = filteredSpatialBuckets[META_BUCKET];
const auto overlayOpaques = addJob<DepthSortItems>("DepthSortOverlayOpaque", filteredNonspatialBuckets[OPAQUE_SHAPE_BUCKET]); const auto overlayOpaques = addJob<DepthSortItems>("DepthSortOverlayOpaque", filteredNonspatialBuckets[OPAQUE_SHAPE_BUCKET]);
const auto overlayTransparents = addJob<DepthSortItems>("DepthSortOverlayTransparent", filteredNonspatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false)); const auto overlayTransparents = addJob<DepthSortItems>("DepthSortOverlayTransparent", filteredNonspatialBuckets[TRANSPARENT_SHAPE_BUCKET], DepthSortItems(false));
const auto background = filteredNonspatialBuckets[BACKGROUND_BUCKET]; const auto background = filteredNonspatialBuckets[BACKGROUND_BUCKET];
setOutput(Output{{ setOutput(Output{{
opaques, transparents, lights, overlayOpaques, overlayTransparents, background, spatialSelection }}); opaques, transparents, lights, metas, overlayOpaques, overlayTransparents, background, spatialSelection }});
} }

View file

@ -19,7 +19,21 @@
class RenderFetchCullSortTask : public render::Task { class RenderFetchCullSortTask : public render::Task {
public: public:
using Output = std::array<render::Varying, 7>;
enum Buckets {
OPAQUE_SHAPE = 0,
TRANSPARENT_SHAPE,
LIGHT,
META,
OVERLAY_OPAQUE_SHAPE,
OVERLAY_TRANSPARENT_SHAPE,
BACKGROUND,
SPATIAL_SELECTION,
NUM_BUCKETS
};
using Output = std::array<render::Varying, Buckets::NUM_BUCKETS>;
using JobModel = ModelO<RenderFetchCullSortTask>; using JobModel = ModelO<RenderFetchCullSortTask>;
RenderFetchCullSortTask(render::CullFunctor cullFunctor); RenderFetchCullSortTask(render::CullFunctor cullFunctor);

View file

@ -58,7 +58,7 @@ ItemID Scene::allocateID() {
return _IDAllocator.fetch_add(1); return _IDAllocator.fetch_add(1);
} }
bool Scene::isAllocatedID(const ItemID& id) { bool Scene::isAllocatedID(const ItemID& id) const {
return Item::isValidID(id) && (id < _numAllocatedItems.load()); return Item::isValidID(id) && (id < _numAllocatedItems.load());
} }

View file

@ -61,7 +61,7 @@ public:
ItemID allocateID(); ItemID allocateID();
// Check that the ID is valid and allocated for this scene, this a threadsafe call // Check that the ID is valid and allocated for this scene, this a threadsafe call
bool isAllocatedID(const ItemID& id); bool isAllocatedID(const ItemID& id) const;
// THis is the total number of allocated items, this a threadsafe call // THis is the total number of allocated items, this a threadsafe call
size_t getNumItems() const { return _numAllocatedItems.load(); } size_t getNumItems() const { return _numAllocatedItems.load(); }
@ -78,6 +78,9 @@ public:
// WARNING, There is No check on the validity of the ID, so this could return a bad Item // WARNING, There is No check on the validity of the ID, so this could return a bad Item
const Item& getItem(const ItemID& id) const { return _items[id]; } const Item& getItem(const ItemID& id) const { return _items[id]; }
// Same as getItem, checking if the id is valid
const Item getItemSafe(const ItemID& id) const { if (isAllocatedID(id)) { return _items[id]; } else { return Item(); } }
// Access the spatialized items // Access the spatialized items
const ItemSpatialTree& getSpatialTree() const { return _masterSpatialTree; } const ItemSpatialTree& getSpatialTree() const { return _masterSpatialTree; }

View file

@ -0,0 +1,19 @@
var WAVE = 'http://cdn.rawgit.com/ambisonictoolkit/atk-sounds/aa31005c/stereo/Aurora_Surgit-Lux_Aeterna.wav';
var uuid = Entities.addEntity({
type: "Shape",
shape: "Icosahedron",
dimensions: Vec3.HALF,
script: Script.resolvePath('../../tutorials/entity_scripts/ambientSound.js'),
position: Vec3.sum(Vec3.multiply(5, Quat.getFront(MyAvatar.orientation)), MyAvatar.position),
userData: JSON.stringify({
soundURL: WAVE,
maxVolume: 0.1,
range: 25,
disabled: true,
grabbableKey: { wantsTrigger: true },
}),
lifetime: 600,
});
Script.scriptEnding.connect(function() {
Entities.deleteEntity(uuid);
});

View file

@ -13,7 +13,7 @@ var qml = Script.resolvePath('deferredLighting.qml');
var window = new OverlayWindow({ var window = new OverlayWindow({
title: 'Lighting', title: 'Lighting',
source: qml, source: qml,
width: 400, height:220, width: 400, height:280,
}); });
window.setPosition(Window.innerWidth - 420, 50); window.setPosition(Window.innerWidth - 420, 50);
window.closed.connect(function() { Script.stop(); }); window.closed.connect(function() { Script.stop(); });

View file

@ -74,7 +74,7 @@ Column {
Column { Column {
spacing: 10 spacing: 10
Repeater { Repeater {
model: [ "Tone Mapping exposure:ToneMapping:exposure:5.0:-5.0" model: [ "Tone Mapping Exposure:ToneMapping:exposure:5.0:-5.0"
] ]
ConfigSlider { ConfigSlider {
label: qsTr(modelData.split(":")[0]) label: qsTr(modelData.split(":")[0])
@ -88,7 +88,7 @@ Column {
Row { Row {
Label { Label {
text: "Debug Framebuffer" text: "Tone Mapping Curve"
anchors.left: root.left anchors.left: root.left
} }
@ -109,6 +109,7 @@ Column {
} }
Row { Row {
id: framebuffer id: framebuffer
spacing: 10
Label { Label {
text: "Debug Framebuffer" text: "Debug Framebuffer"
@ -156,5 +157,14 @@ Column {
onCurrentIndexChanged: { framebuffer.setDebugMode(currentIndex) } onCurrentIndexChanged: { framebuffer.setDebugMode(currentIndex) }
} }
} }
Row {
id: metas
CheckBox {
text: "Draw Meta Bounds"
checked: Render.getConfig("DrawMetaBounds")["enabled"]
onCheckedChanged: { Render.getConfig("DrawMetaBounds")["enabled"] = checked }
}
}
} }

View file

@ -148,7 +148,6 @@
if (!canWriteAssets) { if (!canWriteAssets) {
console.log("ERROR: Clara.io FBX: File download cancelled because no permissions to write to Asset Server"); console.log("ERROR: Clara.io FBX: File download cancelled because no permissions to write to Asset Server");
EventBridge.emitWebEvent(WARN_USER_NO_PERMISSIONS); EventBridge.emitWebEvent(WARN_USER_NO_PERMISSIONS);
event.stopPropagation();
return; return;
} }

View file

@ -498,7 +498,6 @@ function getAudioLevel(id) {
var audioLevel = 0.0; var audioLevel = 0.0;
var data = id ? ExtendedOverlay.get(id) : myData; var data = id ? ExtendedOverlay.get(id) : myData;
if (!data) { if (!data) {
print('no data for', id);
return audioLevel; return audioLevel;
} }

View file

@ -1,34 +1,48 @@
// ambientSound.js // ambientSound.js
// //
// This entity script will allow you to create an ambient sound that loops when a person is within a given // This entity script will allow you to create an ambient sound that loops when a person is within a given
// range of this entity. Great way to add one or more ambisonic soundfields to your environment. // range of this entity. Great way to add one or more ambisonic soundfields to your environment.
// //
// In the userData section for the entity, add/edit three values: // In the userData section for the entity, add/edit three values:
// userData.soundURL should be a string giving the URL to the sound file. Defaults to 100 meters if not set. // userData.soundURL should be a string giving the URL to the sound file. Defaults to 100 meters if not set.
// userData.range should be an integer for the max distance away from the entity where the sound will be audible. // userData.range should be an integer for the max distance away from the entity where the sound will be audible.
// userData.volume is the max volume at which the clip should play. Defaults to 1.0 full volume) // userData.maxVolume is the max volume at which the clip should play. Defaults to 1.0 full volume.
// userData.disabled is an optionanl boolean flag which can be used to disable the ambient sound. Defaults to false.
//
// The rotation of the entity is copied to the ambisonic field, so by rotating the entity you will rotate the
// direction in-which a certain sound comes from.
// //
// The rotation of the entity is copied to the ambisonic field, so by rotating the entity you will rotate the
// direction in-which a certain sound comes from.
//
// Remember that the entity has to be visible to the user for the sound to play at all, so make sure the entity is // Remember that the entity has to be visible to the user for the sound to play at all, so make sure the entity is
// large enough to be loaded at the range you set, particularly for large ranges. // large enough to be loaded at the range you set, particularly for large ranges.
// //
// Copyright 2016 High Fidelity, Inc. // Copyright 2016 High Fidelity, Inc.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
(function(){ (function(){
// This sample clip and range will be used if you don't add userData to the entity (see above) // This sample clip and range will be used if you don't add userData to the entity (see above)
var DEFAULT_RANGE = 100; var DEFAULT_RANGE = 100;
var DEFAULT_URL = "http://hifi-content.s3.amazonaws.com/ken/samples/forest_ambiX.wav"; var DEFAULT_URL = "http://hifi-content.s3.amazonaws.com/ken/samples/forest_ambiX.wav";
var DEFAULT_VOLUME = 1.0; var DEFAULT_VOLUME = 1.0;
var DEFAULT_USERDATA = {
soundURL: DEFAULT_URL,
range: DEFAULT_RANGE,
maxVolume: DEFAULT_VOLUME,
disabled: true,
grabbableKey: { wantsTrigger: true },
};
var soundURL = ""; var soundURL = "";
var soundOptions = {
loop: true,
localOnly: true,
};
var range = DEFAULT_RANGE; var range = DEFAULT_RANGE;
var maxVolume = DEFAULT_VOLUME; var maxVolume = DEFAULT_VOLUME;
var disabled = false;
var UPDATE_INTERVAL_MSECS = 100; var UPDATE_INTERVAL_MSECS = 100;
var rotation; var rotation;
@ -39,14 +53,13 @@
var checkTimer = false; var checkTimer = false;
var _this; var _this;
var WANT_COLOR_CHANGE = false;
var COLOR_OFF = { red: 128, green: 128, blue: 128 }; var COLOR_OFF = { red: 128, green: 128, blue: 128 };
var COLOR_ON = { red: 255, green: 0, blue: 0 }; var COLOR_ON = { red: 255, green: 0, blue: 0 };
var WANT_DEBUG = false; var WANT_DEBUG = false;
function debugPrint(string) { function debugPrint(string) {
if (WANT_DEBUG) { if (WANT_DEBUG) {
print(string); print("ambientSound | " + string);
} }
} }
@ -55,84 +68,201 @@
var oldSoundURL = soundURL; var oldSoundURL = soundURL;
var props = Entities.getEntityProperties(entity, [ "userData" ]); var props = Entities.getEntityProperties(entity, [ "userData" ]);
if (props.userData) { if (props.userData) {
var data = JSON.parse(props.userData); try {
var data = JSON.parse(props.userData);
} catch(e) {
debugPrint("unable to parse userData JSON string: " + props.userData);
this.cleanup();
return;
}
if (data.soundURL && !(soundURL === data.soundURL)) { if (data.soundURL && !(soundURL === data.soundURL)) {
soundURL = data.soundURL; soundURL = data.soundURL;
debugPrint("Read ambient sound URL: " + soundURL); debugPrint("Read ambient sound URL: " + soundURL);
} else if (!data.soundURL) {
soundURL = DEFAULT_URL;
} }
if (data.range && !(range === data.range)) { if (data.range && !(range === data.range)) {
range = data.range; range = data.range;
debugPrint("Read ambient sound range: " + range); debugPrint("Read ambient sound range: " + range);
} }
if (data.volume && !(maxVolume === data.volume)) { // Check known aliases for the "volume" setting (which allows for inplace upgrade of existing marketplace entities)
maxVolume = data.volume; data.maxVolume = data.maxVolume || data.soundVolume || data.volume;
if (data.maxVolume && !(maxVolume === data.maxVolume)) {
maxVolume = data.maxVolume;
debugPrint("Read ambient sound volume: " + maxVolume); debugPrint("Read ambient sound volume: " + maxVolume);
} }
if ("disabled" in data && !(disabled === data.disabled)) {
disabled = data.disabled;
debugPrint("Read ambient disabled state: " + disabled);
this._updateColor(disabled);
}
}
if (disabled) {
this.cleanup();
soundURL = "";
return;
} else if (!checkTimer) {
checkTimer = Script.setInterval(_this.maybeUpdate, UPDATE_INTERVAL_MSECS);
} }
if (!(soundURL === oldSoundURL) || (soundURL === "")) { if (!(soundURL === oldSoundURL) || (soundURL === "")) {
debugPrint("Loading ambient sound into cache"); if (soundURL) {
ambientSound = SoundCache.getSound(soundURL); debugPrint("Loading ambient sound into cache");
// Use prefetch to detect URL loading errors
var resource = SoundCache.prefetch(soundURL);
function onStateChanged() {
if (resource.state === Resource.State.FINISHED) {
resource.stateChanged.disconnect(onStateChanged);
ambientSound = SoundCache.getSound(soundURL);
} else if (resource.state === Resource.State.FAILED) {
resource.stateChanged.disconnect(onStateChanged);
debugPrint("Failed to download ambient sound: " + soundURL);
}
}
resource.stateChanged.connect(onStateChanged);
onStateChanged(resource.state);
}
if (soundPlaying && soundPlaying.playing) { if (soundPlaying && soundPlaying.playing) {
debugPrint("URL changed, stopping current ambient sound");
soundPlaying.stop(); soundPlaying.stop();
soundPlaying = false; soundPlaying = false;
if (WANT_COLOR_CHANGE) {
Entities.editEntity(entity, { color: COLOR_OFF });
}
debugPrint("Restarting ambient sound");
} }
} }
} }
this.preload = function(entityID) { this.clickDownOnEntity = function(entityID, mouseEvent) {
if (mouseEvent.isPrimaryButton) {
this._toggle("primary click");
}
};
this.startFarTrigger = function() {
this._toggle("far click");
};
this._toggle = function(hint) {
// Toggle between ON/OFF state, but only if not in edit mode
if (Settings.getValue("io.highfidelity.isEditting")) {
return;
}
var props = Entities.getEntityProperties(entity, [ "userData" ]);
if (!props.userData) {
debugPrint("userData is empty; ignoring " + hint);
this.cleanup();
return;
}
var data = JSON.parse(props.userData);
data.disabled = !data.disabled;
debugPrint(hint + " -- triggering ambient sound " + (data.disabled ? "OFF" : "ON") + " (" + data.soundURL + ")");
this.cleanup();
// Save the userData and notify nearby listeners of the change
Entities.editEntity(entity, {
userData: JSON.stringify(data)
});
Messages.sendMessage(entity, "toggled");
};
this._updateColor = function(disabled) {
// Update Shape or Text Entity color based on ON/OFF status
var props = Entities.getEntityProperties(entity, [ "color", "textColor" ]);
var targetColor = disabled ? COLOR_OFF : COLOR_ON;
var currentColor = props.textColor || props.color;
var newProps = props.textColor ? { textColor: targetColor } : { color: targetColor };
if (currentColor.red !== targetColor.red ||
currentColor.green !== targetColor.green ||
currentColor.blue !== targetColor.blue) {
Entities.editEntity(entity, newProps);
}
};
this.preload = function(entityID) {
// Load the sound and range from the entity userData fields, and note the position of the entity. // Load the sound and range from the entity userData fields, and note the position of the entity.
debugPrint("Ambient sound preload"); debugPrint("Ambient sound preload " + entityID);
entity = entityID; entity = entityID;
_this = this; _this = this;
checkTimer = Script.setInterval(this.maybeUpdate, UPDATE_INTERVAL_MSECS);
}; var props = Entities.getEntityProperties(entity, [ "userData" ]);
var data = {};
if (props.userData) {
data = JSON.parse(props.userData);
}
var changed = false;
for(var p in DEFAULT_USERDATA) {
if (!(p in data)) {
data[p] = DEFAULT_USERDATA[p];
changed = true;
}
}
if (!data.grabbableKey.wantsTrigger) {
data.grabbableKey.wantsTrigger = true;
changed = true;
}
if (changed) {
debugPrint("applying default values to userData");
Entities.editEntity(entity, { userData: JSON.stringify(data) });
}
this._updateColor(data.disabled);
this.updateSettings();
// Subscribe to toggle notifications using entity ID as a channel name
Messages.subscribe(entity);
Messages.messageReceived.connect(this, "_onMessageReceived");
};
this._onMessageReceived = function(channel, message, sender, local) {
// Handle incoming toggle notifications
if (channel === entity && message === "toggled") {
debugPrint("received " + message + " from " + sender);
this.updateSettings();
}
};
this.maybeUpdate = function() { this.maybeUpdate = function() {
// Every UPDATE_INTERVAL_MSECS, update the volume of the ambient sound based on distance from my avatar // Every UPDATE_INTERVAL_MSECS, update the volume of the ambient sound based on distance from my avatar
_this.updateSettings(); _this.updateSettings();
var HYSTERESIS_FRACTION = 0.1; var HYSTERESIS_FRACTION = 0.1;
var props = Entities.getEntityProperties(entity, [ "position", "rotation" ]); var props = Entities.getEntityProperties(entity, [ "position", "rotation" ]);
if (disabled || !props.position) {
_this.cleanup();
return;
}
center = props.position; center = props.position;
rotation = props.rotation; rotation = props.rotation;
var distance = Vec3.length(Vec3.subtract(MyAvatar.position, center)); var distance = Vec3.length(Vec3.subtract(MyAvatar.position, center));
if (distance <= range) { if (distance <= range) {
var volume = (1.0 - distance / range) * maxVolume; var volume = (1.0 - distance / range) * maxVolume;
if (!soundPlaying && ambientSound.downloaded) { soundOptions.orientation = rotation;
soundPlaying = Audio.playSound(ambientSound, { loop: true, soundOptions.volume = volume;
localOnly: true, if (!soundPlaying && ambientSound && ambientSound.downloaded) {
orientation: rotation, debugPrint("Starting ambient sound: " + soundURL + " (duration: " + ambientSound.duration + ")");
volume: volume }); soundPlaying = Audio.playSound(ambientSound, soundOptions);
debugPrint("Starting ambient sound, volume: " + volume);
if (WANT_COLOR_CHANGE) {
Entities.editEntity(entity, { color: COLOR_ON });
}
} else if (soundPlaying && soundPlaying.playing) { } else if (soundPlaying && soundPlaying.playing) {
soundPlaying.setOptions( { volume: volume, orientation: rotation } ); soundPlaying.setOptions(soundOptions);
} }
} else if (soundPlaying && soundPlaying.playing && (distance > range * HYSTERESIS_FRACTION)) { } else if (soundPlaying && soundPlaying.playing && (distance > range * HYSTERESIS_FRACTION)) {
soundPlaying.stop(); soundPlaying.stop();
soundPlaying = false; soundPlaying = false;
Entities.editEntity(entity, { color: { red: 128, green: 128, blue: 128 }}); debugPrint("Out of range, stopping ambient sound: " + soundURL);
debugPrint("Out of range, stopping ambient sound");
} }
} };
this.unload = function(entityID) { this.unload = function(entityID) {
debugPrint("Ambient sound unload"); debugPrint("Ambient sound unload");
this.cleanup();
Messages.unsubscribe(entity);
Messages.messageReceived.disconnect(this, "_onMessageReceived");
};
this.cleanup = function() {
if (checkTimer) { if (checkTimer) {
Script.clearInterval(checkTimer); Script.clearInterval(checkTimer);
checkTimer = false;
} }
if (soundPlaying && soundPlaying.playing) { if (soundPlaying && soundPlaying.playing) {
soundPlaying.stop(); soundPlaying.stop();
soundPlaying = false;
} }
}; };
}) })