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

This commit is contained in:
samcake 2016-09-13 17:44:42 -07:00
commit 11c6bb7d80
22 changed files with 566 additions and 108 deletions

View file

@ -214,7 +214,6 @@ Setting::Handle<int> maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTRE
static const QString MARKETPLACE_CDN_HOSTNAME = "mpassets.highfidelity.com";
const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensions {
{ SNAPSHOT_EXTENSION, &Application::acceptSnapshot },
{ SVO_EXTENSION, &Application::importSVOFromURL },
{ SVO_JSON_EXTENSION, &Application::importSVOFromURL },
{ AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl },
@ -2857,6 +2856,8 @@ void Application::dragEnterEvent(QDragEnterEvent* event) {
event->acceptProposedAction();
}
// This is currently not used, but could be invoked if the user wants to go to the place embedded in an
// Interface-taken snapshot. (It was developed for drag and drop, before we had asset-server loading or in-world browsers.)
bool Application::acceptSnapshot(const QString& urlString) {
QUrl url(urlString);
QString snapshotPath = url.toLocalFile();

View file

@ -182,3 +182,16 @@ ViewFrustum Camera::toViewFrustum() const {
loadViewFrustum(result);
return result;
}
QVariantMap Camera::getViewFrustum() {
ViewFrustum frustum;
loadViewFrustum(frustum);
QVariantMap result;
result["position"].setValue(frustum.getPosition());
result["orientation"].setValue(frustum.getOrientation());
result["projection"].setValue(frustum.getProjection());
result["centerRadius"].setValue(frustum.getCenterRadius());
return result;
}

View file

@ -42,6 +42,8 @@ class Camera : public QObject {
Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation)
Q_PROPERTY(QString mode READ getModeString WRITE setModeString)
Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity)
Q_PROPERTY(QVariantMap frustum READ getViewFrustum CONSTANT)
public:
Camera();
@ -63,6 +65,8 @@ public:
const glm::mat4& getProjection() const { return _projection; }
void setProjection(const glm::mat4& projection);
QVariantMap getViewFrustum();
public slots:
QString getModeString() const;
void setModeString(const QString& mode);

View file

@ -1215,6 +1215,8 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn
void AudioClient::outputNotify() {
int recentUnfulfilled = _audioOutputIODevice.getRecentUnfulfilledReads();
if (recentUnfulfilled > 0) {
qCInfo(audioclient, "Starve detected, %d new unfulfilled reads", recentUnfulfilled);
if (_outputStarveDetectionEnabled.get()) {
quint64 now = usecTimestampNow() / 1000;
int dt = (int)(now - _outputStarveDetectionStartTimeMsec);
@ -1224,14 +1226,15 @@ void AudioClient::outputNotify() {
} else {
_outputStarveDetectionCount += recentUnfulfilled;
if (_outputStarveDetectionCount > _outputStarveDetectionThreshold.get()) {
_outputStarveDetectionStartTimeMsec = now;
_outputStarveDetectionCount = 0;
int oldOutputBufferSizeFrames = _sessionOutputBufferSizeFrames;
int newOutputBufferSizeFrames = setOutputBufferSize(oldOutputBufferSizeFrames + 1, false);
if (newOutputBufferSizeFrames > oldOutputBufferSizeFrames) {
qCDebug(audioclient) << "Starve detection threshold met, increasing buffer size to " << newOutputBufferSizeFrames;
qCInfo(audioclient, "Starve threshold surpassed (%d starves in %d ms)", _outputStarveDetectionCount, dt);
}
_outputStarveDetectionStartTimeMsec = now;
_outputStarveDetectionCount = 0;
}
}
}
@ -1319,7 +1322,7 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
int AudioClient::setOutputBufferSize(int numFrames, bool persist) {
numFrames = std::min(std::max(numFrames, MIN_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES), MAX_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES);
if (numFrames != _sessionOutputBufferSizeFrames) {
qCDebug(audioclient) << "Audio output buffer size (frames): " << numFrames;
qCInfo(audioclient, "Audio output buffer set to %d frames", numFrames);
_sessionOutputBufferSizeFrames = numFrames;
if (persist) {
_outputBufferSizeFrames.set(numFrames);
@ -1424,24 +1427,20 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
auto samplesRequested = maxSize / sizeof(int16_t);
int samplesPopped;
int bytesWritten;
if ((samplesPopped = _receivedAudioStream.popSamples((int)samplesRequested, false)) > 0) {
AudioRingBuffer::ConstIterator lastPopOutput = _receivedAudioStream.getLastPopOutput();
lastPopOutput.readSamples((int16_t*)data, samplesPopped);
bytesWritten = samplesPopped * sizeof(int16_t);
} else {
// nothing on network, don't grab anything from injectors, and just
// return 0s
// nothing on network, don't grab anything from injectors, and just return 0s
// this will flood the log: qCDebug(audioclient, "empty/partial network buffer");
memset(data, 0, maxSize);
bytesWritten = maxSize;
}
int bytesAudioOutputUnplayed = _audio->_audioOutput->bufferSize() - _audio->_audioOutput->bytesFree();
if (!bytesAudioOutputUnplayed) {
qCDebug(audioclient) << "empty audio buffer";
}
if (bytesAudioOutputUnplayed == 0 && bytesWritten == 0) {
bool wasBufferStarved = _audio->_audioOutput->bufferSize() == _audio->_audioOutput->bytesFree();
if (wasBufferStarved) {
_unfulfilledReads++;
}

View file

@ -590,11 +590,9 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
renderInfo.setProperty("texturesSize", (int)getRenderInfoTextureSize()); // FIXME - theoretically the size of textures could be > max int
renderInfo.setProperty("hasTransparent", getRenderInfoHasTransparent());
renderInfo.setProperty("drawCalls", getRenderInfoDrawCalls());
renderInfo.setProperty("texturesCount", getRenderInfoTextureCount());
}
if (_type == EntityTypes::Model || _type == EntityTypes::PolyLine || _type == EntityTypes::ParticleEffect) {
renderInfo.setProperty("texturesCount", QScriptValue(_textureNames.count()));
}
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(renderInfo, renderInfo); // Gettable but not settable
}
@ -771,7 +769,7 @@ void EntityItemPropertiesFromScriptValueHonorReadOnly(const QScriptValue &object
QScriptValue EntityPropertyFlagsToScriptValue(QScriptEngine* engine, const EntityPropertyFlags& flags) {
return EntityItemProperties::entityPropertyFlagsToScriptValue(engine, flags);
QScriptValue result = engine->newObject();
return result;
return result;
}
void EntityPropertyFlagsFromScriptValue(const QScriptValue& object, EntityPropertyFlags& flags) {

View file

@ -21,6 +21,7 @@
#include "EntityTree.h"
#include "LightEntityItem.h"
#include "ModelEntityItem.h"
#include "QVariantGLM.h"
#include "SimulationOwner.h"
#include "ZoneEntityItem.h"
@ -566,6 +567,48 @@ QVector<QUuid> EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corn
return result;
}
QVector<QUuid> EntityScriptingInterface::findEntitiesInFrustum(QVariantMap frustum) const {
QVector<QUuid> result;
const QString POSITION_PROPERTY = "position";
bool positionOK = frustum.contains(POSITION_PROPERTY);
glm::vec3 position = positionOK ? qMapToGlmVec3(frustum[POSITION_PROPERTY]) : glm::vec3();
const QString ORIENTATION_PROPERTY = "orientation";
bool orientationOK = frustum.contains(ORIENTATION_PROPERTY);
glm::quat orientation = orientationOK ? qMapToGlmQuat(frustum[ORIENTATION_PROPERTY]) : glm::quat();
const QString PROJECTION_PROPERTY = "projection";
bool projectionOK = frustum.contains(PROJECTION_PROPERTY);
glm::mat4 projection = projectionOK ? qMapToGlmMat4(frustum[PROJECTION_PROPERTY]) : glm::mat4();
const QString CENTER_RADIUS_PROPERTY = "centerRadius";
bool centerRadiusOK = frustum.contains(CENTER_RADIUS_PROPERTY);
float centerRadius = centerRadiusOK ? frustum[CENTER_RADIUS_PROPERTY].toFloat() : 0.0f;
if (positionOK && orientationOK && projectionOK && centerRadiusOK) {
ViewFrustum viewFrustum;
viewFrustum.setPosition(position);
viewFrustum.setOrientation(orientation);
viewFrustum.setProjection(projection);
viewFrustum.setCenterRadius(centerRadius);
viewFrustum.calculate();
if (_entityTree) {
QVector<EntityItemPointer> entities;
_entityTree->withReadLock([&] {
_entityTree->findEntities(viewFrustum, entities);
});
foreach(EntityItemPointer entity, entities) {
result << entity->getEntityItemID();
}
}
}
return result;
}
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking,
const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard) {

View file

@ -127,10 +127,19 @@ public slots:
/// this function will not find any models in script engine contexts which don't have access to models
Q_INVOKABLE QVector<QUuid> findEntities(const glm::vec3& center, float radius) const;
/// finds models within the search sphere specified by the center point and radius
/// finds models within the box specified by the corner and dimensions
/// this function will not find any models in script engine contexts which don't have access to models
Q_INVOKABLE QVector<QUuid> findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const;
/// finds models within the frustum
/// the frustum must have the following properties:
/// - position
/// - orientation
/// - projection
/// - centerRadius
/// this function will not find any models in script engine contexts which don't have access to models
Q_INVOKABLE QVector<QUuid> findEntitiesInFrustum(QVariantMap frustum) const;
/// If the scripting context has visible entities, this will determine a ray intersection, the results
/// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate
/// will be false.

View file

@ -688,6 +688,31 @@ void EntityTree::findEntities(const AABox& box, QVector<EntityItemPointer>& foun
foundEntities.swap(args._foundEntities);
}
class FindInFrustumArgs {
public:
ViewFrustum frustum;
QVector<EntityItemPointer> entities;
};
bool EntityTree::findInFrustumOperation(OctreeElementPointer element, void* extraData) {
FindInFrustumArgs* args = static_cast<FindInFrustumArgs*>(extraData);
if (element->isInView(args->frustum)) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
entityTreeElement->getEntities(args->frustum, args->entities);
return true;
}
return false;
}
// NOTE: assumes caller has handled locking
void EntityTree::findEntities(const ViewFrustum& frustum, QVector<EntityItemPointer>& foundEntities) {
FindInFrustumArgs args = { frustum, QVector<EntityItemPointer>() };
// NOTE: This should use recursion, since this is a spatial operation
recurseTreeWithOperation(findInFrustumOperation, &args);
// swap the two lists of entity pointers instead of copy
foundEntities.swap(args.entities);
}
EntityItemPointer EntityTree::findEntityByID(const QUuid& id) {
EntityItemID entityID(id);
return findEntityByEntityItemID(entityID);

View file

@ -153,6 +153,11 @@ public:
/// \remark Side effect: any initial contents in entities will be lost
void findEntities(const AABox& box, QVector<EntityItemPointer>& foundEntities);
/// finds all entities within a frustum
/// \parameter frustum the query frustum
/// \param foundEntities[out] vector of EntityItemPointer
void findEntities(const ViewFrustum& frustum, QVector<EntityItemPointer>& foundEntities);
void addNewlyCreatedHook(NewlyCreatedEntityHook* hook);
void removeNewlyCreatedHook(NewlyCreatedEntityHook* hook);
@ -276,6 +281,7 @@ protected:
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);
void notifyNewlyCreatedEntity(const EntityItem& newEntity, const SharedNodePointer& senderNode);

View file

@ -796,6 +796,17 @@ void EntityTreeElement::getEntities(const AABox& box, QVector<EntityItemPointer>
});
}
void EntityTreeElement::getEntities(const ViewFrustum& frustum, QVector<EntityItemPointer>& foundEntities) {
forEachEntity([&](EntityItemPointer entity) {
bool success;
AABox entityBox = entity->getAABox(success);
// FIXME - See FIXMEs for similar methods above.
if (!success || frustum.boxIntersectsFrustum(entityBox) || frustum.boxIntersectsKeyhole(entityBox)) {
foundEntities.push_back(entity);
}
});
}
EntityItemPointer EntityTreeElement::getEntityWithEntityItemID(const EntityItemID& id) const {
EntityItemPointer foundEntity = NULL;
withReadLock([&] {

View file

@ -194,6 +194,11 @@ public:
/// \param entities[out] vector of non-const EntityItemPointer
void getEntities(const AABox& box, QVector<EntityItemPointer>& foundEntities);
/// finds all entities that touch a frustum
/// \param frustum the query frustum
/// \param entities[out] vector of non-const EntityItemPointer
void getEntities(const ViewFrustum& frustum, QVector<EntityItemPointer>& foundEntities);
EntityItemPointer getEntityWithID(uint32_t id) const;
EntityItemPointer getEntityWithEntityItemID(const EntityItemID& id) const;
void getEntitiesInside(const AACube& box, QVector<EntityItemPointer>& foundEntities);

View file

@ -77,6 +77,7 @@ void MeshPartPayload::updateMaterial(model::MaterialPointer drawMaterial) {
bool MeshPartPayload::calculateMaterialSize() {
bool allTextures = true; // assume we got this...
_materialTextureSize = 0;
_materialTextureCount = 0;
auto textureMaps = _drawMaterial->getTextureMaps();
for (auto const &textureMapItem : textureMaps) {
auto textureMap = textureMapItem.second;
@ -88,6 +89,7 @@ bool MeshPartPayload::calculateMaterialSize() {
//auto storedSize = texture->getStoredSize();
auto size = texture->getSize();
_materialTextureSize += size;
_materialTextureCount++;
} else {
allTextures = false;
}

View file

@ -67,10 +67,12 @@ public:
size_t getVerticesCount() const { return _drawMesh ? _drawMesh->getNumVertices() : 0; }
size_t getMaterialTextureSize() { return _materialTextureSize; }
int getMaterialTextureCount() { return _materialTextureCount; }
bool calculateMaterialSize();
protected:
size_t _materialTextureSize { 0 };
int _materialTextureCount { 0 };
};
namespace render {

View file

@ -161,22 +161,33 @@ void Model::setOffset(const glm::vec3& offset) {
_snappedToRegistrationPoint = false;
}
size_t Model::getRenderInfoTextureSize() {
if (!_hasCalculatedTextureSize && isLoaded() && getGeometry()->areTexturesLoaded()) {
void Model::calculateTextureInfo() {
if (!_hasCalculatedTextureInfo && isLoaded() && getGeometry()->areTexturesLoaded() && !_modelMeshRenderItems.isEmpty()) {
size_t textureSize = 0;
int textureCount = 0;
bool allTexturesLoaded = true;
foreach(auto renderItem, _modelMeshRenderItemsSet) {
auto meshPart = renderItem.get();
bool allTexturesForThisMesh = meshPart->calculateMaterialSize();
allTexturesLoaded = allTexturesLoaded & allTexturesForThisMesh;
textureSize += meshPart->getMaterialTextureSize();
textureCount += meshPart->getMaterialTextureCount();
}
_renderInfoTextureSize = textureSize;
_hasCalculatedTextureSize = allTexturesLoaded; // only do this once
_renderInfoTextureCount = textureCount;
_hasCalculatedTextureInfo = allTexturesLoaded; // only do this once
}
}
size_t Model::getRenderInfoTextureSize() {
calculateTextureInfo();
return _renderInfoTextureSize;
}
int Model::getRenderInfoTextureCount() {
calculateTextureInfo();
return _renderInfoTextureCount;
}
void Model::updateRenderItems() {
if (!_addedToScene) {

View file

@ -233,8 +233,8 @@ public:
void setLoadingPriority(float priority) { _loadingPriority = priority; }
size_t getRenderInfoVertexCount() const { return _renderInfoVertexCount; }
int getRenderInfoTextureCount() const { return _renderInfoTextureCount; }
size_t getRenderInfoTextureSize();
int getRenderInfoTextureCount();
int getRenderInfoDrawCalls() const { return _renderInfoDrawCalls; }
bool getRenderInfoHasTransparent() const { return _renderInfoHasTransparent; }
@ -409,13 +409,14 @@ protected:
size_t _renderInfoVertexCount { 0 };
int _renderInfoTextureCount { 0 };
size_t _renderInfoTextureSize { 0 };
bool _hasCalculatedTextureSize { false };
bool _hasCalculatedTextureInfo { false };
int _renderInfoDrawCalls { 0 };
int _renderInfoHasTransparent { false };
private:
float _loadingPriority { 0.0f };
void calculateTextureInfo();
};
Q_DECLARE_METATYPE(ModelPointer)

View file

@ -62,3 +62,48 @@ void qListtoRgbColor(const QVariant& q, rgbColor& returnValue) {
returnValue[GREEN_INDEX] = qList[GREEN_INDEX].toInt();
returnValue[BLUE_INDEX] = qList[BLUE_INDEX].toInt();
}
glm::vec3 qMapToGlmVec3(const QVariant& q) {
QVariantMap qMap = q.toMap();
if (qMap.contains("x") && qMap.contains("y") && qMap.contains("y")) {
return glm::vec3(
qMap["x"].toFloat(),
qMap["y"].toFloat(),
qMap["z"].toFloat()
);
} else {
return glm::vec3();
}
}
glm::quat qMapToGlmQuat(const QVariant& q) {
QVariantMap qMap = q.toMap();
if (qMap.contains("w") && qMap.contains("x") && qMap.contains("y") && qMap.contains("z")) {
return glm::quat(
qMap["w"].toFloat(),
qMap["x"].toFloat(),
qMap["y"].toFloat(),
qMap["z"].toFloat()
);
} else {
return glm::quat();
}
}
glm::mat4 qMapToGlmMat4(const QVariant& q) {
QVariantMap qMap = q.toMap();
if (qMap.contains("r0c0") && qMap.contains("r1c0") && qMap.contains("r2c0") && qMap.contains("r3c0")
&& qMap.contains("r0c1") && qMap.contains("r1c1") && qMap.contains("r2c1") && qMap.contains("r3c1")
&& qMap.contains("r0c2") && qMap.contains("r1c2") && qMap.contains("r2c2") && qMap.contains("r3c2")
&& qMap.contains("r0c3") && qMap.contains("r1c3") && qMap.contains("r2c3") && qMap.contains("r3c3")) {
return glm::mat4(
qMap["r0c0"].toFloat(), qMap["r1c0"].toFloat(), qMap["r2c0"].toFloat(), qMap["r3c0"].toFloat(),
qMap["r0c1"].toFloat(), qMap["r1c1"].toFloat(), qMap["r2c1"].toFloat(), qMap["r3c1"].toFloat(),
qMap["r0c2"].toFloat(), qMap["r1c2"].toFloat(), qMap["r2c2"].toFloat(), qMap["r3c2"].toFloat(),
qMap["r0c3"].toFloat(), qMap["r1c3"].toFloat(), qMap["r2c3"].toFloat(), qMap["r3c3"].toFloat()
);
} else {
return glm::mat4();
}
}

View file

@ -27,3 +27,7 @@ QVariantMap glmToQMap(const glm::quat& glmQuat);
glm::vec3 qListToGlmVec3(const QVariant& q);
glm::quat qListToGlmQuat(const QVariant& q);
void qListtoRgbColor(const QVariant& q, rgbColor& returnValue);
glm::vec3 qMapToGlmVec3(const QVariant& q);
glm::quat qMapToGlmQuat(const QVariant& q);
glm::mat4 qMapToGlmMat4(const QVariant& q);

View file

@ -366,6 +366,10 @@ input[type=button]:disabled {
background: linear-gradient(#575757 20%, #252525 100%);
}
input[type=button][pressed=pressed] {
color: #00b4ef;
}
input[type=checkbox] {
display: none;
}
@ -861,13 +865,8 @@ textarea:enabled[scrolling="true"]::-webkit-resizer {
position: relative; /* New positioning context. */
}
#entity-list .glyph {
font-family: HiFi-Glyphs;
font-size: 14px;
}
#search-area {
padding-right: 148px;
padding-right: 168px;
padding-bottom: 24px;
}
@ -875,13 +874,23 @@ textarea:enabled[scrolling="true"]::-webkit-resizer {
width: 98%;
}
#in-view {
position: absolute;
right: 126px;
}
#radius-and-unit {
float: right;
margin-right: -148px;
margin-right: -168px;
position: relative;
top: -17px;
}
#radius-and-unit label {
margin-left: 2px;
}
#radius-and-unit input {
width: 120px;
}
#entity-table-scroll {
/* Height is set by JavaScript. */
@ -896,6 +905,10 @@ textarea:enabled[scrolling="true"]::-webkit-resizer {
background-color: #1c1c1c;
}
#entity-table-scroll .glyph {
font-family: HiFi-Glyphs;
font-size: 15px;
}
#entity-table {
margin-top: -28px;
@ -905,19 +918,6 @@ textarea:enabled[scrolling="true"]::-webkit-resizer {
background-color: #1c1c1c;
}
#col-type {
width: 16%;
}
#col-name {
width: 34%;
}
#col-url {
width: 34%;
}
#col-locked, #col-visible {
width: 8%;
}
#entity-table thead tr, #entity-table thead tr th,
#entity-table tfoot tr, #entity-table tfoot tr td {
background: none;
@ -938,12 +938,22 @@ textarea:enabled[scrolling="true"]::-webkit-resizer {
top: 49px;
left: 0;
width: 100%;
word-wrap: nowrap;
white-space: nowrap;
overflow: hidden;
}
#entity-table thead th {
.verticesCount, .texturesCount, .texturesSize, .drawCalls {
text-align: right;
}
#entity-table th {
display: inline-block;
box-sizing: border-box;
padding: 0 0 0 8px;
padding: 5px 0 0 0;
vertical-align: middle;
overflow: hidden;
text-overflow: ellipsis;
}
#entity-table th:focus {
@ -952,17 +962,54 @@ textarea:enabled[scrolling="true"]::-webkit-resizer {
#entity-table th .glyph {
position: relative;
left: 0;
left: 4px;
}
#entity-table th .glyph + .sort-order {
position: relative;
left: 4px;
}
#entity-table th#entity-hasScript {
overflow: visible;
}
#entity-table th#entity-hasScript .glyph {
text-transform: none;
}
#entity-table thead .sort-order {
display: inline-block;
width: 8px;
margin: -5px 0 -3px 0;
text-align: right;
vertical-align: middle;
}
#entity-table th #info-toggle {
display: inline-block;
position: absolute;
left: initial;
right: 0;
width: 11px;
background-color: #1c1c1c;
z-index: 100;
}
#entity-table th #info-toggle span {
position: relative;
left: -2px;
}
th#entity-hasTransparent .glyph {
font-weight: normal;
font-size: 24px !important;
margin: -6px;
position: relative;
top: -6px;
}
th#entity-hasTransparent .sort-order {
position: relative;
top: -4px;
}
#entity-table td {
box-sizing: border-box;
}
@ -971,6 +1018,11 @@ textarea:enabled[scrolling="true"]::-webkit-resizer {
text-align: center;
padding: 0;
}
#entity-table td.hasTransparent.glyph {
font-size: 22px;
position: relative;
top: -1px;
}
#entity-table tfoot {
box-sizing: border-box;
@ -984,6 +1036,82 @@ textarea:enabled[scrolling="true"]::-webkit-resizer {
width: 100%;
}
#col-type {
width: 16%;
}
#col-name {
width: 34%;
}
#col-url {
width: 34%;
}
#col-locked, #col-visible {
width: 9%;
}
#col-verticesCount, #col-texturesCount, #col-texturesSize, #col-hasTransparent, #col-drawCalls, #col-hasScript {
width: 0;
}
.showExtraInfo #col-type {
width: 10%;
}
.showExtraInfo #col-name {
width: 19%;
}
.showExtraInfo #col-url {
width: 19%;
}
.showExtraInfo #col-locked, .showExtraInfo #col-visible {
width: 4%;
}
.showExtraInfo #col-verticesCount {
width: 8%;
}
.showExtraInfo #col-texturesCount {
width: 8%;
}
.showExtraInfo #col-texturesSize {
width: 10%;
}
.showExtraInfo #col-hasTransparent {
width: 4%;
}
.showExtraInfo #col-drawCalls {
width: 8%;
}
.showExtraInfo #col-hasScript {
width: 6%;
}
th#entity-verticesCount, th#entity-texturesCount, th#entity-texturesSize, th#entity-hasTransparent, th#entity-drawCalls,
th#entity-hasScript {
display: none;
}
.verticesCount, .texturesCount, .texturesSize, .hasTransparent, .drawCalls, .hasScript {
display: none;
}
#entity-visible {
border: none;
}
.showExtraInfo #entity-verticesCount, .showExtraInfo #entity-texturesCount, .showExtraInfo #entity-texturesSize,
.showExtraInfo #entity-hasTransparent, .showExtraInfo #entity-drawCalls, .showExtraInfo #entity-hasScript {
display: inline-block;
}
.showExtraInfo .verticesCount, .showExtraInfo .texturesCount, .showExtraInfo .texturesSize, .showExtraInfo .hasTransparent,
.showExtraInfo .drawCalls, .showExtraInfo .hasScript {
display: table-cell;
}
.showExtraInfo #entity-visible {
border-right: 1px solid #575757;
}
#no-entities {
display: none;
position: absolute;
@ -1102,4 +1230,4 @@ input#reset-to-natural-dimensions {
margin-top:5px;
font-size:16px;
display:none;
}
}

View file

@ -29,6 +29,7 @@
<div id="entity-list">
<div id="search-area">
<span class="icon-input"><input type="text" class="search" id="filter" placeholder="Filter" /><span>Y</span></span>
<input type="button" id="in-view" class="glyph" value="&#xe007;" />
<div id="radius-and-unit" class="number">
<label for="radius">Search radius <span class="unit">m</span></label>
<input type="number" id="radius" value="100" />
@ -37,39 +38,57 @@
<div id="entity-table-scroll">
<table id="entity-table">
<colgroup>
<col span="1" id="col-type" />
<col span="1" id="col-name" />
<col span="1" id="col-url" />
<col span="1" id="col-locked" />
<col span="1" id="col-visible" />
<col span="1" id="col-type" />
<col span="1" id="col-name" />
<col span="1" id="col-url" />
<col span="1" id="col-locked" />
<col span="1" id="col-visible" />
<col span="1" id="col-verticesCount" />
<col span="1" id="col-texturesCount" />
<col span="1" id="col-texturesSize" />
<col span="1" id="col-hasTransparent" />
<col span="1" id="col-drawCalls" />
<col span="1" id="col-hasScript" />
</colgroup>
<thead>
<tr>
<th id="entity-type" data-sort="type">Type<span class="sort-order"></span></th>
<th id="entity-name" data-sort="type">Name<span class="sort-order"></span></th>
<th id="entity-url" data-sort="url">File<span class="sort-order"></span></th>
<th id="entity-locked" data-sort="locked"><span class="glyph">&#xe006;</span><span class="sort-order"></span></th>
<th colspan="2" id="entity-visible" data-sort="visible"><span class="glyph">&#xe007;</span><span class="sort-order"></span></th>
<th id="entity-type">Type<span class="sort-order"></span><span id="info-toggle"><span class="glyph">D</span></span></th>
<th id="entity-name">Name<span class="sort-order"></span></th>
<th id="entity-url">File<span class="sort-order"></span></th>
<th id="entity-locked"><span class="glyph">&#xe006;</span><span class="sort-order"></span></th>
<th id="entity-visible"><span class="glyph">&#xe007;</span><span class="sort-order"></span></th>
<th id="entity-verticesCount">Verts<span class="sort-order"></span></th>
<th id="entity-texturesCount">Texts<span class="sort-order"></span></th>
<th id="entity-texturesSize">Text MB<span class="sort-order"></span></th>
<th id="entity-hasTransparent"><span class="glyph">&#xe00b;</span><span class="sort-order"></span></th>
<th id="entity-drawCalls">Draws<span class="sort-order"></span></th>
<th colspan="1" id="entity-hasScript"><span class="glyph">k</span><span class="sort-order"></span></th>
</tr>
</thead>
<tbody class="list" id="entity-table-body">
<tr>
<td class="type">Type</td>
<td class="name">Name</td>
<td class="url"><div class='outer'><div class='inner'>URL</div></div></td>
<td class="locked glyph">?</td>
<td class="visible glyph">?</td>
<td class="id" style="display: none">ID</td>
<td class="type"></td>
<td class="name"></td>
<td class="url"><div class='outer'><div class='inner'></div></div></td>
<td class="locked glyph"></td>
<td class="visible glyph"></td>
<td class="verticesCount"></td>
<td class="texturesCount"></td>
<td class="texturesSize"></td>
<td class="hasTransparent glyph"></td>
<td class="drawCalls"></td>
<td class="hasScript glyph"></td>
<td class="id" style="display: none"></td>
</tr>
</tbody>
<tfoot>
<tr>
<td id="footer-text" colspan="5"> </td>
</tr>
<tr>
<td id="footer-text" colspan="11"> </td>
</tr>
</tfoot>
</table>
<div id="no-entities">
No entities found within a <span id="no-entities-radius">100</span> meter radius. Try moving to a different location and refreshing.
No entities found <span id="no-entities-in-view">in view</span> within a <span id="no-entities-radius">100</span> meter radius. Try moving to a different location and refreshing.
</div>
</div>
</div>

View file

@ -12,10 +12,12 @@ var currentSortColumn = 'type';
var currentSortOrder = 'des';
var entityList = null;
var refreshEntityListTimer = null;
const ASCENDING_STRING = '&#x25BE;';
const DESCENDING_STRING = '&#x25B4;';
const ASCENDING_STRING = '&#x25B4;';
const DESCENDING_STRING = '&#x25BE;';
const LOCKED_GLYPH = "&#xe006;";
const VISIBLE_GLYPH = "&#xe007;";
const TRANSPARENCY_GLYPH = "&#xe00b;";
const SCRIPT_GLYPH = "k";
const DELETE = 46; // Key code for the delete key.
const MAX_ITEMS = Number.MAX_VALUE; // Used to set the max length of the list of discovered entities.
@ -33,10 +35,16 @@ function loaded() {
elToggleLocked = document.getElementById("locked");
elToggleVisible = document.getElementById("visible");
elDelete = document.getElementById("delete");
elTeleport = document.getElementById("teleport");
elFilter = document.getElementById("filter");
elInView = document.getElementById("in-view")
elRadius = document.getElementById("radius");
elTeleport = document.getElementById("teleport");
elEntityTable = document.getElementById("entity-table");
elInfoToggle = document.getElementById("info-toggle");
elInfoToggleGlyph = elInfoToggle.firstChild;
elFooter = document.getElementById("footer-text");
elNoEntitiesMessage = document.getElementById("no-entities");
elNoEntitiesInView = document.getElementById("no-entities-in-view");
elNoEntitiesRadius = document.getElementById("no-entities-radius");
elEntityTableScroll = document.getElementById("entity-table-scroll");
@ -55,7 +63,25 @@ function loaded() {
document.getElementById("entity-visible").onclick = function () {
setSortColumn('visible');
};
document.getElementById("entity-verticesCount").onclick = function () {
setSortColumn('verticesCount');
};
document.getElementById("entity-texturesCount").onclick = function () {
setSortColumn('texturesCount');
};
document.getElementById("entity-texturesSize").onclick = function () {
setSortColumn('texturesSize');
};
document.getElementById("entity-hasTransparent").onclick = function () {
setSortColumn('hasTransparent');
};
document.getElementById("entity-drawCalls").onclick = function () {
setSortColumn('drawCalls');
};
document.getElementById("entity-hasScript").onclick = function () {
setSortColumn('hasScript');
};
function onRowClicked(clickEvent) {
var id = this.dataset.entityId;
var selection = [this.dataset.entityId];
@ -106,12 +132,29 @@ function loaded() {
}));
}
function addEntity(id, name, type, url, locked, visible) {
const BYTES_PER_MEGABYTE = 1024 * 1024;
function decimalMegabytes(number) {
return number ? (number / BYTES_PER_MEGABYTE).toFixed(1) : "";
}
function displayIfNonZero(number) {
return number ? number : "";
}
function addEntity(id, name, type, url, locked, visible, verticesCount, texturesCount, texturesSize, hasTransparent,
drawCalls, hasScript) {
var urlParts = url.split('/');
var filename = urlParts[urlParts.length - 1];
if (entities[id] === undefined) {
entityList.add([{ id: id, name: name, type: type, url: filename, locked: locked, visible: visible }],
entityList.add([{
id: id, name: name, type: type, url: filename, locked: locked, visible: visible,
verticesCount: displayIfNonZero(verticesCount), texturesCount: displayIfNonZero(texturesCount),
texturesSize: decimalMegabytes(texturesSize), hasTransparent: hasTransparent,
drawCalls: displayIfNonZero(drawCalls), hasScript: hasScript
}],
function (items) {
var currentElement = items[0].elm;
var id = items[0]._values.id;
@ -148,7 +191,13 @@ function loaded() {
type: document.querySelector('#entity-type .sort-order'),
url: document.querySelector('#entity-url .sort-order'),
locked: document.querySelector('#entity-locked .sort-order'),
visible: document.querySelector('#entity-visible .sort-order')
visible: document.querySelector('#entity-visible .sort-order'),
verticesCount: document.querySelector('#entity-verticesCount .sort-order'),
texturesCount: document.querySelector('#entity-texturesCount .sort-order'),
texturesSize: document.querySelector('#entity-texturesSize .sort-order'),
hasTransparent: document.querySelector('#entity-hasTransparent .sort-order'),
drawCalls: document.querySelector('#entity-drawCalls .sort-order'),
hasScript: document.querySelector('#entity-hasScript .sort-order'),
}
function setSortColumn(column) {
if (currentSortColumn == column) {
@ -168,10 +217,23 @@ function loaded() {
EventBridge.emitWebEvent(JSON.stringify({ type: 'refresh' }));
}
function refreshFooter() {
if (selectedEntities.length > 1) {
elFooter.firstChild.nodeValue = selectedEntities.length + " entities selected";
} else if (selectedEntities.length === 1) {
elFooter.firstChild.nodeValue = "1 entity selected";
} else if (entityList.visibleItems.length === 1) {
elFooter.firstChild.nodeValue = "1 entity found";
} else {
elFooter.firstChild.nodeValue = entityList.visibleItems.length + " entities found";
}
}
function refreshEntityListObject() {
refreshEntityListTimer = null;
entityList.sort(currentSortColumn, { order: currentSortOrder });
entityList.search(document.getElementById("filter").value);
entityList.search(elFilter.value);
refreshFooter();
}
function updateSelectedEntities(selectedEntities) {
@ -189,16 +251,6 @@ function loaded() {
}
}
if (selectedEntities.length > 1) {
elFooter.firstChild.nodeValue = selectedEntities.length + " entities selected";
} else if (selectedEntities.length === 1) {
elFooter.firstChild.nodeValue = "1 entity selected";
} else if (entityList.visibleItems.length === 1) {
elFooter.firstChild.nodeValue = "1 entity found";
} else {
elFooter.firstChild.nodeValue = entityList.visibleItems.length + " entities found";
}
// HACK: Fixes the footer and header text sometimes not displaying after adding or deleting entities.
// The problem appears to be a bug in the Qt HTML/CSS rendering (Qt 5.5).
document.getElementById("radius").focus();
@ -235,13 +287,29 @@ function loaded() {
}
}, false);
var isFilterInView = false;
var FILTER_IN_VIEW_ATTRIBUTE = "pressed";
elNoEntitiesInView.style.display = "none";
elInView.onclick = function () {
isFilterInView = !isFilterInView;
if (isFilterInView) {
elInView.setAttribute(FILTER_IN_VIEW_ATTRIBUTE, FILTER_IN_VIEW_ATTRIBUTE);
elNoEntitiesInView.style.display = "inline";
} else {
elInView.removeAttribute(FILTER_IN_VIEW_ATTRIBUTE);
elNoEntitiesInView.style.display = "none";
}
EventBridge.emitWebEvent(JSON.stringify({ type: "filterInView", filterInView: isFilterInView }));
refreshEntities();
}
elRadius.onchange = function () {
elRadius.value = Math.max(elRadius.value, 0);
EventBridge.emitWebEvent(JSON.stringify({ type: 'radius', radius: elRadius.value }));
refreshEntities();
elNoEntitiesRadius.firstChild.nodeValue = elRadius.value;
}
if (window.EventBridge !== undefined) {
EventBridge.scriptEventReceived.connect(function(data) {
data = JSON.parse(data);
@ -264,7 +332,11 @@ function loaded() {
var id = newEntities[i].id;
addEntity(id, newEntities[i].name, newEntities[i].type, newEntities[i].url,
newEntities[i].locked ? LOCKED_GLYPH : null,
newEntities[i].visible ? VISIBLE_GLYPH : null);
newEntities[i].visible ? VISIBLE_GLYPH : null,
newEntities[i].verticesCount, newEntities[i].texturesCount, newEntities[i].texturesSize,
newEntities[i].hasTransparent ? TRANSPARENCY_GLYPH : null,
newEntities[i].drawCalls,
newEntities[i].hasScript ? SCRIPT_GLYPH : null);
}
updateSelectedEntities(data.selectedIDs);
resize();
@ -278,6 +350,7 @@ function loaded() {
// Take up available window space
elEntityTableScroll.style.height = window.innerHeight - 207;
var SCROLLABAR_WIDTH = 21;
var tds = document.querySelectorAll("#entity-table-body tr:first-child td");
var ths = document.querySelectorAll("#entity-table thead th");
if (tds.length >= ths.length) {
@ -287,16 +360,53 @@ function loaded() {
}
} else {
// Reasonable widths if nothing is displayed
var tableWidth = document.getElementById("entity-table").offsetWidth;
ths[0].width = 0.16 * tableWidth;
ths[1].width = 0.34 * tableWidth;
ths[2].width = 0.34 * tableWidth;
ths[3].width = 0.08 * tableWidth;
ths[4].width = 0.08 * tableWidth;
var tableWidth = document.getElementById("entity-table").offsetWidth - SCROLLABAR_WIDTH;
if (showExtraInfo) {
ths[0].width = 0.10 * tableWidth;
ths[1].width = 0.20 * tableWidth;
ths[2].width = 0.20 * tableWidth;
ths[3].width = 0.04 * tableWidth;
ths[4].width = 0.04 * tableWidth;
ths[5].width = 0.08 * tableWidth;
ths[6].width = 0.08 * tableWidth;
ths[7].width = 0.10 * tableWidth;
ths[8].width = 0.04 * tableWidth;
ths[9].width = 0.08 * tableWidth;
ths[10].width = 0.04 * tableWidth + SCROLLABAR_WIDTH;
} else {
ths[0].width = 0.16 * tableWidth;
ths[1].width = 0.34 * tableWidth;
ths[2].width = 0.34 * tableWidth;
ths[3].width = 0.08 * tableWidth;
ths[4].width = 0.08 * tableWidth;
}
}
};
window.onresize = resize;
elFilter.onchange = resize;
elFilter.onblur = refreshFooter;
var showExtraInfo = false;
var COLLAPSE_EXTRA_INFO = "E";
var EXPAND_EXTRA_INFO = "D";
function toggleInfo(event) {
showExtraInfo = !showExtraInfo;
if (showExtraInfo) {
elEntityTable.className = "showExtraInfo";
elInfoToggleGlyph.innerHTML = COLLAPSE_EXTRA_INFO;
} else {
elEntityTable.className = "";
elInfoToggleGlyph.innerHTML = EXPAND_EXTRA_INFO;
}
resize();
event.stopPropagation();
}
elInfoToggle.addEventListener("click", toggleInfo, true);
resize();
});

View file

@ -9,7 +9,7 @@ EntityListTool = function(opts) {
});
var filterInView = false;
var searchRadius = 100;
var visible = false;
@ -48,20 +48,41 @@ EntityListTool = function(opts) {
webView.emitScriptEvent(JSON.stringify(data));
};
function valueIfDefined(value) {
return value !== undefined ? value : "";
}
that.sendUpdate = function() {
var entities = [];
var ids = Entities.findEntities(MyAvatar.position, searchRadius);
var ids;
if (filterInView) {
ids = Entities.findEntitiesInFrustum(Camera.frustum);
} else {
ids = Entities.findEntities(MyAvatar.position, searchRadius);
}
var cameraPosition = Camera.position;
for (var i = 0; i < ids.length; i++) {
var id = ids[i];
var properties = Entities.getEntityProperties(id);
entities.push({
id: id,
name: properties.name,
type: properties.type,
url: properties.type == "Model" ? properties.modelURL : "",
locked: properties.locked,
visible: properties.visible
});
if (!filterInView || Vec3.distance(properties.position, cameraPosition) <= searchRadius) {
entities.push({
id: id,
name: properties.name,
type: properties.type,
url: properties.type == "Model" ? properties.modelURL : "",
locked: properties.locked,
visible: properties.visible,
verticesCount: valueIfDefined(properties.renderInfo.verticesCount),
texturesCount: valueIfDefined(properties.renderInfo.texturesCount),
texturesSize: valueIfDefined(properties.renderInfo.texturesSize),
hasTransparent: valueIfDefined(properties.renderInfo.hasTransparent),
drawCalls: valueIfDefined(properties.renderInfo.drawCalls),
hasScript: properties.script !== ""
});
}
}
var selectedIDs = [];
@ -105,9 +126,10 @@ EntityListTool = function(opts) {
toggleSelectedEntitiesLocked();
} else if (data.type == "toggleVisible") {
toggleSelectedEntitiesVisible();
} else if (data.type === "filterInView") {
filterInView = data.filterInView === true;
} else if (data.type === "radius") {
searchRadius = data.radius;
that.sendUpdate();
}
});