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

This commit is contained in:
Sam Gateau 2018-06-27 15:31:00 +02:00
commit 24c5525a90
45 changed files with 836 additions and 536 deletions

View file

@ -126,10 +126,12 @@ Item {
activeFocusOnPress: true
ShortcutText {
z: 10
anchors {
verticalCenter: usernameField.textFieldLabel.verticalCenter
left: usernameField.textFieldLabel.right
leftMargin: 10
left: usernameField.left
top: usernameField.top
leftMargin: usernameField.textFieldLabel.contentWidth + 10
topMargin: -19
}
text: "<a href='https://highfidelity.com/users/password/new'>Forgot Username?</a>"
@ -154,10 +156,12 @@ Item {
activeFocusOnPress: true
ShortcutText {
z: 10
anchors {
verticalCenter: passwordField.textFieldLabel.verticalCenter
left: passwordField.textFieldLabel.right
leftMargin: 10
left: passwordField.left
top: passwordField.top
leftMargin: passwordField.textFieldLabel.contentWidth + 10
topMargin: -19
}
text: "<a href='https://highfidelity.com/users/password/new'>Forgot Password?</a>"
@ -168,6 +172,7 @@ Item {
onLinkActivated: loginDialog.openUrl(link)
}
onFocusChanged: {
root.text = "";
root.isPassword = true;

View file

@ -11,25 +11,16 @@
#include "SecondaryCamera.h"
#include <RenderDeferredTask.h>
#include <RenderForwardTask.h>
#include <glm/gtx/transform.hpp>
#include <gpu/Context.h>
#include <TextureCache.h>
#include "Application.h"
using RenderArgsPointer = std::shared_ptr<RenderArgs>;
void MainRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) {
task.addJob<RenderShadowTask>("RenderShadowTask", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1);
const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1);
assert(items.canCast<RenderFetchCullSortTask::Output>());
if (!isDeferred) {
task.addJob<RenderForwardTask>("Forward", items);
} else {
task.addJob<RenderDeferredTask>("RenderDeferredTask", items);
}
}
class SecondaryCameraJob { // Changes renderContext for our framebuffer and view.
public:
using Config = SecondaryCameraJobConfig;
@ -215,10 +206,10 @@ void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inp
const auto cachedArg = task.addJob<SecondaryCameraJob>("SecondaryCamera");
const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1);
assert(items.canCast<RenderFetchCullSortTask::Output>());
if (!isDeferred) {
task.addJob<RenderForwardTask>("Forward", items);
if (isDeferred) {
task.addJob<RenderDeferredTask>("RenderDeferredTask", items, false);
} else {
task.addJob<RenderDeferredTask>("RenderDeferredTask", items);
task.addJob<RenderForwardTask>("Forward", items);
}
task.addJob<EndSecondaryCameraFrame>("EndSecondaryCamera", cachedArg);
}

View file

@ -12,23 +12,11 @@
#pragma once
#ifndef hifi_SecondaryCamera_h
#define hifi_SecondaryCamera_h
#include <RenderShadowTask.h>
#include <render/RenderFetchCullSortTask.h>
#include <RenderDeferredTask.h>
#include <RenderForwardTask.h>
#include <TextureCache.h>
#include <ViewFrustum.h>
class MainRenderTask {
public:
using JobModel = render::Task::Model<MainRenderTask>;
MainRenderTask() {}
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred = true);
};
class SecondaryCameraJobConfig : public render::Task::Config { // Exposes secondary camera parameters to JavaScript.
Q_OBJECT
Q_PROPERTY(QUuid attachedEntityId MEMBER attachedEntityId NOTIFY dirty) // entity whose properties define camera position and orientation

View file

@ -127,31 +127,40 @@ EC_KEY* readKeys(const char* filename) {
bool Wallet::writeBackupInstructions() {
QString inputFilename(PathUtils::resourcesPath() + "html/commerce/backup_instructions.html");
QString outputFilename = PathUtils::getAppDataFilePath(INSTRUCTIONS_FILE);
QFile inputFile(inputFilename);
QFile outputFile(outputFilename);
bool retval = false;
if (QFile::exists(outputFilename) || getKeyFilePath() == "")
if (getKeyFilePath() == "")
{
return false;
}
QFile::copy(inputFilename, outputFilename);
if (QFile::exists(outputFilename) && outputFile.open(QIODevice::ReadWrite)) {
if (QFile::exists(inputFilename) && inputFile.open(QIODevice::ReadOnly)) {
if (outputFile.open(QIODevice::ReadWrite)) {
// Read the data from the original file, then close it
QByteArray fileData = inputFile.readAll();
inputFile.close();
QByteArray fileData = outputFile.readAll();
QString text(fileData);
// Translate the data from the original file into a QString
QString text(fileData);
text.replace(QString("HIFIKEY_PATH_REPLACEME"), keyFilePath());
// Replace the necessary string
text.replace(QString("HIFIKEY_PATH_REPLACEME"), keyFilePath());
outputFile.seek(0); // go to the beginning of the file
outputFile.write(text.toUtf8()); // write the new text back to the file
// Write the new text back to the file
outputFile.write(text.toUtf8());
outputFile.close(); // close the file handle.
// Close the output file
outputFile.close();
retval = true;
qCDebug(commerce) << "wrote html file successfully";
retval = true;
qCDebug(commerce) << "wrote html file successfully";
} else {
qCDebug(commerce) << "failed to open output html file" << outputFilename;
}
} else {
qCDebug(commerce) << "failed to open output html file" << outputFilename;
qCDebug(commerce) << "failed to open input html file" << inputFilename;
}
return retval;
}

View file

@ -363,6 +363,14 @@ bool EntityRenderer::needsRenderUpdateFromEntity(const EntityItemPointer& entity
return false;
}
void EntityRenderer::updateModelTransform() {
bool success = false;
auto newModelTransform = _entity->getTransformToCenter(success);
if (success) {
_modelTransform = newModelTransform;
}
}
void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) {
DETAILED_PROFILE_RANGE(simulation_physics, __FUNCTION__);
withWriteLock([&] {
@ -419,4 +427,4 @@ void EntityRenderer::addMaterial(graphics::MaterialLayer material, const std::st
void EntityRenderer::removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) {
std::lock_guard<std::mutex> lock(_materialsLock);
_materials[parentMaterialName].remove(material);
}
}

View file

@ -97,6 +97,7 @@ protected:
virtual void doRender(RenderArgs* args) = 0;
bool isFading() const { return _isFading; }
void updateModelTransform();
virtual bool isTransparent() const { return _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f : false; }
inline bool isValidRenderItem() const { return _renderItemID != Item::INVALID_ITEM_ID; }
@ -140,6 +141,7 @@ protected:
bool _needsRenderUpdate { false };
// Only touched on the rendering thread
bool _renderUpdateQueued{ false };
Transform _renderTransform;
std::unordered_map<std::string, graphics::MultiMaterial> _materials;
std::mutex _materialsLock;

View file

@ -35,7 +35,6 @@ private:
glm::vec2 _materialMappingPos;
glm::vec2 _materialMappingScale;
float _materialMappingRot;
Transform _renderTransform;
std::shared_ptr<NetworkMaterial> _drawMaterial;
};

View file

@ -122,6 +122,14 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
});
}
}
void* key = (void*)this;
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this] () {
withWriteLock([&] {
updateModelTransform();
_renderTransform = getModelTransform();
});
});
}
void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
@ -319,7 +327,10 @@ void ParticleEffectEntityRenderer::doRender(RenderArgs* args) {
// In trail mode, the particles are created in world space.
// so we only set a transform if they're not in trail mode
if (!_particleProperties.emission.shouldTrail) {
transform = getModelTransform();
withReadLock([&] {
transform = _renderTransform;
});
transform.setScale(vec3(1));
}
batch.setModelTransform(transform);

View file

@ -97,16 +97,25 @@ void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
addMaterial(graphics::MaterialLayer(_material, 0), "0");
_shape = entity->getShape();
_position = entity->getWorldPosition();
_dimensions = entity->getScaledDimensions();
_orientation = entity->getWorldOrientation();
_renderTransform = getModelTransform();
});
if (_shape == entity::Sphere) {
_renderTransform.postScale(SPHERE_ENTITY_SCALE);
}
void* key = (void*)this;
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this] () {
withWriteLock([&] {
auto entity = getEntity();
_position = entity->getWorldPosition();
_dimensions = entity->getScaledDimensions();
_orientation = entity->getWorldOrientation();
bool success = false;
auto newModelTransform = entity->getTransformToCenter(success);
_renderTransform = success ? newModelTransform : getModelTransform();
_renderTransform.postScale(_dimensions);
if (_shape == entity::Sphere) {
_renderTransform.postScale(SPHERE_ENTITY_SCALE);
}
_renderTransform.postScale(_dimensions);
});;
});
}

View file

@ -40,7 +40,6 @@ private:
Procedural _procedural;
QString _lastUserData;
Transform _renderTransform;
entity::Shape _shape { entity::Sphere };
std::shared_ptr<graphics::Material> _material;
glm::vec3 _position;

View file

@ -20,6 +20,7 @@
#include "GLMHelpers.h"
using namespace render;
using namespace render::entities;
static const int FIXED_FONT_POINT_SIZE = 40;
@ -64,10 +65,20 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
return false;
}
void TextEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
void* key = (void*)this;
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] () {
withWriteLock([&] {
_dimensions = entity->getScaledDimensions();
updateModelTransform();
_renderTransform = getModelTransform();
});
});
}
void TextEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
_textColor = toGlm(entity->getTextColorX());
_backgroundColor = toGlm(entity->getBackgroundColorX());
_dimensions = entity->getScaledDimensions();
_faceCamera = entity->getFaceCamera();
_lineHeight = entity->getLineHeight();
_text = entity->getText();
@ -76,24 +87,28 @@ void TextEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointe
void TextEntityRenderer::doRender(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableTextEntityItem::render");
Transform modelTransform;
glm::vec3 dimensions;
withReadLock([&] {
modelTransform = _renderTransform;
dimensions = _dimensions;
});
static const float SLIGHTLY_BEHIND = -0.005f;
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
bool transparent = fadeRatio < 1.0f;
glm::vec4 textColor = glm::vec4(_textColor, fadeRatio);
glm::vec4 backgroundColor = glm::vec4(_backgroundColor, fadeRatio);
const glm::vec3& dimensions = _dimensions;
// Render background
glm::vec3 minCorner = glm::vec3(0.0f, -dimensions.y, SLIGHTLY_BEHIND);
glm::vec3 maxCorner = glm::vec3(dimensions.x, 0.0f, SLIGHTLY_BEHIND);
// Batch render calls
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
const auto& modelTransform = getModelTransform();
auto transformToTopLeft = modelTransform;
if (_faceCamera) {
//rotate about vertical to face the camera
@ -105,7 +120,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) {
}
transformToTopLeft.postTranslate(dimensions * glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left
transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed
batch.setModelTransform(transformToTopLeft);
auto geometryCache = DependencyManager::get<GeometryCache>();
if (!_geometryID) {
@ -113,11 +128,11 @@ void TextEntityRenderer::doRender(RenderArgs* args) {
}
geometryCache->bindSimpleProgram(batch, false, transparent, false, false, false);
geometryCache->renderQuad(batch, minCorner, maxCorner, backgroundColor, _geometryID);
float scale = _lineHeight / _textRenderer->getFontSize();
transformToTopLeft.setScale(scale); // Scale to have the correct line height
batch.setModelTransform(transformToTopLeft);
float leftMargin = 0.1f * _lineHeight, topMargin = 0.1f * _lineHeight;
glm::vec2 bounds = glm::vec2(dimensions.x - 2.0f * leftMargin,
dimensions.y - 2.0f * topMargin);

View file

@ -27,6 +27,7 @@ public:
~TextEntityRenderer();
private:
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
virtual void doRender(RenderArgs* args) override;
int _geometryID{ 0 };
@ -39,6 +40,6 @@ private:
float _lineHeight;
};
} }
} }
#endif // hifi_RenderableTextEntityItem_h

View file

@ -149,8 +149,8 @@ void WebEntityRenderer::onTimeout() {
}
void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
// If the content type has changed, or the old content type was QML, we need to
// destroy the existing surface (because surfaces don't support changing the root
// If the content type has changed, or the old content type was QML, we need to
// destroy the existing surface (because surfaces don't support changing the root
// object, so subsequent loads of content just overlap the existing content
bool urlChanged = false;
{
@ -187,24 +187,30 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
if (!hasWebSurface() && !buildWebSurface(entity)) {
return;
}
if (urlChanged && _contentType == ContentType::HtmlContent) {
_webSurface->getRootItem()->setProperty(URL_PROPERTY, _lastSourceUrl);
}
if (_contextPosition != entity->getWorldPosition()) {
// update globalPosition
_contextPosition = entity->getWorldPosition();
_webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(_contextPosition));
}
void* key = (void*)this;
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity] () {
withWriteLock([&] {
if (_contextPosition != entity->getWorldPosition()) {
// update globalPosition
_contextPosition = entity->getWorldPosition();
_webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(_contextPosition));
}
_lastDPI = entity->getDPI();
_lastLocked = entity->getLocked();
_lastDPI = entity->getDPI();
_lastLocked = entity->getLocked();
glm::vec2 windowSize = getWindowSize(entity);
_webSurface->resize(QSize(windowSize.x, windowSize.y));
_renderTransform = getModelTransform();
_renderTransform.postScale(entity->getScaledDimensions());
glm::vec2 windowSize = getWindowSize(entity);
_webSurface->resize(QSize(windowSize.x, windowSize.y));
updateModelTransform();
_renderTransform = getModelTransform();
_renderTransform.postScale(entity->getScaledDimensions());
});
});
});
}
@ -297,7 +303,7 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
if (_contentType == ContentType::HtmlContent) {
// We special case YouTube URLs since we know they are videos that we should play with at least 30 FPS.
// FIXME this doesn't handle redirects or shortened URLs, consider using a signaling method from the
// FIXME this doesn't handle redirects or shortened URLs, consider using a signaling method from the
// web entity
if (QUrl(_lastSourceUrl).host().endsWith("youtube.com", Qt::CaseInsensitive)) {
_webSurface->setMaxFps(YOUTUBE_MAX_FPS);

View file

@ -68,7 +68,6 @@ private:
bool _lastLocked;
QTimer _timer;
uint64_t _lastRenderTime { 0 };
Transform _renderTransform;
};
} } // namespace

View file

@ -123,7 +123,6 @@ private:
bool _pendingSkyboxTexture{ false };
QString _proceduralUserData;
Transform _renderTransform;
};
} } // namespace

View file

@ -185,6 +185,7 @@ public:
/// Dimensions in meters (0.0 - TREE_SCALE)
glm::vec3 getScaledDimensions() const;
virtual void setScaledDimensions(const glm::vec3& value);
virtual glm::vec3 getRaycastDimensions() const { return getScaledDimensions(); }
inline const glm::vec3 getUnscaledDimensions() const { return _unscaledDimensions; }
virtual void setUnscaledDimensions(const glm::vec3& value);
@ -239,7 +240,7 @@ public:
// position, size, and bounds related helpers
virtual AACube getMaximumAACube(bool& success) const override;
AACube getMinimumAACube(bool& success) const;
AABox getAABox(bool& success) const; /// axis aligned bounding box in world-frame (meters)
virtual AABox getAABox(bool& success) const; /// axis aligned bounding box in world-frame (meters)
using SpatiallyNestable::getQueryAACube;
virtual AACube getQueryAACube(bool& success) const override;

View file

@ -214,7 +214,7 @@ EntityItemID EntityTreeElement::findDetailedRayIntersection(const glm::vec3& ori
glm::mat4 entityToWorldMatrix = translation * rotation;
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
glm::vec3 dimensions = entity->getScaledDimensions();
glm::vec3 dimensions = entity->getRaycastDimensions();
glm::vec3 registrationPoint = entity->getRegistrationPoint();
glm::vec3 corner = -(dimensions * registrationPoint);
@ -312,7 +312,7 @@ void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searc
glm::vec3 penetration;
if (!success || entityBox.findSpherePenetration(searchPosition, searchRadius, penetration)) {
glm::vec3 dimensions = entity->getScaledDimensions();
glm::vec3 dimensions = entity->getRaycastDimensions();
// FIXME - consider allowing the entity to determine penetration so that
// entities could presumably dull actuall hull testing if they wanted to

View file

@ -278,6 +278,7 @@ protected:
struct InputStageState {
bool _invalidFormat { true };
bool _lastUpdateStereoState{ false };
bool _hadColorAttribute{ true };
Stream::FormatPointer _format;
std::string _formatKey;

View file

@ -156,6 +156,14 @@ void GLBackend::do_setIndirectBuffer(const Batch& batch, size_t paramOffset) {
}
void GLBackend::updateInput() {
bool isStereoNow = isStereo();
// track stereo state change potentially happening wihtout changing the input format
// this is a rare case requesting to invalid the format
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
_input._invalidFormat |= (isStereoNow != _input._lastUpdateStereoState);
#endif
_input._lastUpdateStereoState = isStereoNow;
if (_input._invalidFormat) {
InputStageState::ActivationCache newActivation;
@ -213,7 +221,7 @@ void GLBackend::updateInput() {
(void)CHECK_GL_ERROR();
}
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
glVertexBindingDivisor(bufferChannelNum, frequency * (isStereo() ? 2 : 1));
glVertexBindingDivisor(bufferChannelNum, frequency * (isStereoNow ? 2 : 1));
#else
glVertexBindingDivisor(bufferChannelNum, frequency);
#endif

View file

@ -25,6 +25,14 @@ void GL41Backend::resetInputStage() {
}
void GL41Backend::updateInput() {
bool isStereoNow = isStereo();
// track stereo state change potentially happening wihtout changing the input format
// this is a rare case requesting to invalid the format
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
_input._invalidFormat |= (isStereoNow != _input._lastUpdateStereoState);
#endif
_input._lastUpdateStereoState = isStereoNow;
if (_input._invalidFormat || _input._invalidBuffers.any()) {
if (_input._invalidFormat) {
@ -111,7 +119,7 @@ void GL41Backend::updateInput() {
reinterpret_cast<GLvoid*>(pointer + perLocationStride * (GLuint)locNum));
}
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
glVertexAttribDivisor(slot + (GLuint)locNum, attrib._frequency * (isStereo() ? 2 : 1));
glVertexAttribDivisor(slot + (GLuint)locNum, attrib._frequency * (isStereoNow ? 2 : 1));
#else
glVertexAttribDivisor(slot + (GLuint)locNum, attrib._frequency);
#endif

View file

@ -27,6 +27,14 @@ void GL45Backend::resetInputStage() {
}
void GL45Backend::updateInput() {
bool isStereoNow = isStereo();
// track stereo state change potentially happening wihtout changing the input format
// this is a rare case requesting to invalid the format
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
_input._invalidFormat |= (isStereoNow != _input._lastUpdateStereoState);
#endif
_input._lastUpdateStereoState = isStereoNow;
if (_input._invalidFormat) {
InputStageState::ActivationCache newActivation;
@ -84,7 +92,7 @@ void GL45Backend::updateInput() {
(void)CHECK_GL_ERROR();
}
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
glVertexBindingDivisor(bufferChannelNum, frequency * (isStereo() ? 2 : 1));
glVertexBindingDivisor(bufferChannelNum, frequency * (isStereoNow ? 2 : 1));
#else
glVertexBindingDivisor(bufferChannelNum, frequency);
#endif

View file

@ -472,7 +472,8 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext,
const graphics::HazePointer& haze,
const SurfaceGeometryFramebufferPointer& surfaceGeometryFramebuffer,
const AmbientOcclusionFramebufferPointer& ambientOcclusionFramebuffer,
const SubsurfaceScatteringResourcePointer& subsurfaceScatteringResource) {
const SubsurfaceScatteringResourcePointer& subsurfaceScatteringResource,
bool renderShadows) {
auto args = renderContext->args;
auto& batch = (*args->_batch);
@ -554,7 +555,7 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext,
// Check if keylight casts shadows
bool keyLightCastShadows { false };
if (lightStage && lightStage->_currentFrame._sunLights.size()) {
if (renderShadows && lightStage && lightStage->_currentFrame._sunLights.size()) {
graphics::LightPointer keyLight = lightStage->getLight(lightStage->_currentFrame._sunLights.front());
if (keyLight) {
keyLightCastShadows = keyLight->getCastShadows();
@ -711,11 +712,6 @@ void RenderDeferredCleanup::run(const render::RenderContextPointer& renderContex
}
}
RenderDeferred::RenderDeferred() {
}
void RenderDeferred::configure(const Config& config) {
}
@ -742,7 +738,7 @@ void RenderDeferred::run(const RenderContextPointer& renderContext, const Inputs
args->_batch = &batch;
_gpuTimer->begin(batch);
setupJob.run(renderContext, deferredTransform, deferredFramebuffer, lightingModel, haze, surfaceGeometryFramebuffer, ssaoFramebuffer, subsurfaceScatteringResource);
setupJob.run(renderContext, deferredTransform, deferredFramebuffer, lightingModel, haze, surfaceGeometryFramebuffer, ssaoFramebuffer, subsurfaceScatteringResource, _renderShadows);
lightsJob.run(renderContext, deferredTransform, deferredFramebuffer, lightingModel, surfaceGeometryFramebuffer, lightClusters);

View file

@ -127,7 +127,8 @@ public:
const graphics::HazePointer& haze,
const SurfaceGeometryFramebufferPointer& surfaceGeometryFramebuffer,
const AmbientOcclusionFramebufferPointer& ambientOcclusionFramebuffer,
const SubsurfaceScatteringResourcePointer& subsurfaceScatteringResource);
const SubsurfaceScatteringResourcePointer& subsurfaceScatteringResource,
bool renderShadows);
};
class RenderDeferredLocals {
@ -166,7 +167,8 @@ public:
using Config = RenderDeferredConfig;
using JobModel = render::Job::ModelI<RenderDeferred, Inputs, Config>;
RenderDeferred();
RenderDeferred() {}
RenderDeferred(bool renderShadows) : _renderShadows(renderShadows) {}
void configure(const Config& config);
@ -178,6 +180,9 @@ public:
protected:
gpu::RangeTimerPointer _gpuTimer;
private:
bool _renderShadows { false };
};
class DefaultLightingSetup {

View file

@ -401,26 +401,34 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.0f));
glm::vec3 meshFrameDirection = glm::vec3(worldToMeshMatrix * glm::vec4(direction, 0.0f));
for (auto& triangleSet : _modelSpaceMeshTriangleSets) {
float triangleSetDistance = 0.0f;
BoxFace triangleSetFace;
Triangle triangleSetTriangle;
if (triangleSet.findRayIntersection(meshFrameOrigin, meshFrameDirection, triangleSetDistance, triangleSetFace, triangleSetTriangle, pickAgainstTriangles, allowBackface)) {
int shapeID = 0;
for (auto& meshTriangleSets : _modelSpaceMeshTriangleSets) {
int partIndex = 0;
for (auto &partTriangleSet : meshTriangleSets) {
float triangleSetDistance = 0.0f;
BoxFace triangleSetFace;
Triangle triangleSetTriangle;
if (partTriangleSet.findRayIntersection(meshFrameOrigin, meshFrameDirection, triangleSetDistance, triangleSetFace, triangleSetTriangle, pickAgainstTriangles, allowBackface)) {
glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * triangleSetDistance);
glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f));
float worldDistance = glm::distance(origin, worldIntersectionPoint);
glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * triangleSetDistance);
glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f));
float worldDistance = glm::distance(origin, worldIntersectionPoint);
if (worldDistance < bestDistance) {
bestDistance = worldDistance;
intersectedSomething = true;
face = triangleSetFace;
bestModelTriangle = triangleSetTriangle;
bestWorldTriangle = triangleSetTriangle * meshToWorldMatrix;
extraInfo["worldIntersectionPoint"] = vec3toVariant(worldIntersectionPoint);
extraInfo["meshIntersectionPoint"] = vec3toVariant(meshIntersectionPoint);
bestSubMeshIndex = subMeshIndex;
if (worldDistance < bestDistance) {
bestDistance = worldDistance;
intersectedSomething = true;
face = triangleSetFace;
bestModelTriangle = triangleSetTriangle;
bestWorldTriangle = triangleSetTriangle * meshToWorldMatrix;
extraInfo["worldIntersectionPoint"] = vec3toVariant(worldIntersectionPoint);
extraInfo["meshIntersectionPoint"] = vec3toVariant(meshIntersectionPoint);
extraInfo["partIndex"] = partIndex;
extraInfo["shapeID"] = shapeID;
bestSubMeshIndex = subMeshIndex;
}
}
partIndex++;
shapeID++;
}
subMeshIndex++;
}
@ -485,12 +493,14 @@ bool Model::convexHullContains(glm::vec3 point) {
glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix);
glm::vec3 meshFramePoint = glm::vec3(worldToMeshMatrix * glm::vec4(point, 1.0f));
for (const auto& triangleSet : _modelSpaceMeshTriangleSets) {
const AABox& box = triangleSet.getBounds();
if (box.contains(meshFramePoint)) {
if (triangleSet.convexHullContains(meshFramePoint)) {
// It's inside this mesh, return true.
return true;
for (auto& meshTriangleSets : _modelSpaceMeshTriangleSets) {
for (auto &partTriangleSet : meshTriangleSets) {
const AABox& box = partTriangleSet.getBounds();
if (box.contains(meshFramePoint)) {
if (partTriangleSet.convexHullContains(meshFramePoint)) {
// It's inside this mesh, return true.
return true;
}
}
}
}
@ -653,9 +663,15 @@ void Model::calculateTriangleSets(const FBXGeometry& geometry) {
for (int i = 0; i < numberOfMeshes; i++) {
const FBXMesh& mesh = geometry.meshes.at(i);
for (int j = 0; j < mesh.parts.size(); j++) {
const int numberOfParts = mesh.parts.size();
auto& meshTriangleSets = _modelSpaceMeshTriangleSets[i];
meshTriangleSets.resize(numberOfParts);
for (int j = 0; j < numberOfParts; j++) {
const FBXMeshPart& part = mesh.parts.at(j);
auto& partTriangleSet = meshTriangleSets[j];
const int INDICES_PER_TRIANGLE = 3;
const int INDICES_PER_QUAD = 4;
const int TRIANGLES_PER_QUAD = 2;
@ -664,7 +680,7 @@ void Model::calculateTriangleSets(const FBXGeometry& geometry) {
int numberOfQuads = part.quadIndices.size() / INDICES_PER_QUAD;
int numberOfTris = part.triangleIndices.size() / INDICES_PER_TRIANGLE;
int totalTriangles = (numberOfQuads * TRIANGLES_PER_QUAD) + numberOfTris;
_modelSpaceMeshTriangleSets[i].reserve(totalTriangles);
partTriangleSet.reserve(totalTriangles);
auto meshTransform = geometry.offset * mesh.modelTransform;
@ -686,8 +702,8 @@ void Model::calculateTriangleSets(const FBXGeometry& geometry) {
Triangle tri1 = { v0, v1, v3 };
Triangle tri2 = { v1, v2, v3 };
_modelSpaceMeshTriangleSets[i].insert(tri1);
_modelSpaceMeshTriangleSets[i].insert(tri2);
partTriangleSet.insert(tri1);
partTriangleSet.insert(tri2);
}
}
@ -706,7 +722,7 @@ void Model::calculateTriangleSets(const FBXGeometry& geometry) {
glm::vec3 v2 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i2], 1.0f));
Triangle tri = { v0, v1, v2 };
_modelSpaceMeshTriangleSets[i].insert(tri);
partTriangleSet.insert(tri);
}
}
}
@ -876,56 +892,58 @@ void Model::renderDebugMeshBoxes(gpu::Batch& batch) {
DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, false, false, true, true);
for (const auto& triangleSet : _modelSpaceMeshTriangleSets) {
auto box = triangleSet.getBounds();
for (auto& meshTriangleSets : _modelSpaceMeshTriangleSets) {
for (auto &partTriangleSet : meshTriangleSets) {
auto box = partTriangleSet.getBounds();
if (_debugMeshBoxesID == GeometryCache::UNKNOWN_ID) {
_debugMeshBoxesID = DependencyManager::get<GeometryCache>()->allocateID();
if (_debugMeshBoxesID == GeometryCache::UNKNOWN_ID) {
_debugMeshBoxesID = DependencyManager::get<GeometryCache>()->allocateID();
}
QVector<glm::vec3> points;
glm::vec3 brn = box.getCorner();
glm::vec3 bln = brn + glm::vec3(box.getDimensions().x, 0, 0);
glm::vec3 brf = brn + glm::vec3(0, 0, box.getDimensions().z);
glm::vec3 blf = brn + glm::vec3(box.getDimensions().x, 0, box.getDimensions().z);
glm::vec3 trn = brn + glm::vec3(0, box.getDimensions().y, 0);
glm::vec3 tln = bln + glm::vec3(0, box.getDimensions().y, 0);
glm::vec3 trf = brf + glm::vec3(0, box.getDimensions().y, 0);
glm::vec3 tlf = blf + glm::vec3(0, box.getDimensions().y, 0);
points << brn << bln;
points << brf << blf;
points << brn << brf;
points << bln << blf;
points << trn << tln;
points << trf << tlf;
points << trn << trf;
points << tln << tlf;
points << brn << trn;
points << brf << trf;
points << bln << tln;
points << blf << tlf;
glm::vec4 color[] = {
{ 0.0f, 1.0f, 0.0f, 1.0f }, // green
{ 1.0f, 0.0f, 0.0f, 1.0f }, // red
{ 0.0f, 0.0f, 1.0f, 1.0f }, // blue
{ 1.0f, 0.0f, 1.0f, 1.0f }, // purple
{ 1.0f, 1.0f, 0.0f, 1.0f }, // yellow
{ 0.0f, 1.0f, 1.0f, 1.0f }, // cyan
{ 1.0f, 1.0f, 1.0f, 1.0f }, // white
{ 0.0f, 0.5f, 0.0f, 1.0f },
{ 0.0f, 0.0f, 0.5f, 1.0f },
{ 0.5f, 0.0f, 0.5f, 1.0f },
{ 0.5f, 0.5f, 0.0f, 1.0f },
{ 0.0f, 0.5f, 0.5f, 1.0f } };
DependencyManager::get<GeometryCache>()->updateVertices(_debugMeshBoxesID, points, color[colorNdx]);
DependencyManager::get<GeometryCache>()->renderVertices(batch, gpu::LINES, _debugMeshBoxesID);
colorNdx++;
}
QVector<glm::vec3> points;
glm::vec3 brn = box.getCorner();
glm::vec3 bln = brn + glm::vec3(box.getDimensions().x, 0, 0);
glm::vec3 brf = brn + glm::vec3(0, 0, box.getDimensions().z);
glm::vec3 blf = brn + glm::vec3(box.getDimensions().x, 0, box.getDimensions().z);
glm::vec3 trn = brn + glm::vec3(0, box.getDimensions().y, 0);
glm::vec3 tln = bln + glm::vec3(0, box.getDimensions().y, 0);
glm::vec3 trf = brf + glm::vec3(0, box.getDimensions().y, 0);
glm::vec3 tlf = blf + glm::vec3(0, box.getDimensions().y, 0);
points << brn << bln;
points << brf << blf;
points << brn << brf;
points << bln << blf;
points << trn << tln;
points << trf << tlf;
points << trn << trf;
points << tln << tlf;
points << brn << trn;
points << brf << trf;
points << bln << tln;
points << blf << tlf;
glm::vec4 color[] = {
{ 0.0f, 1.0f, 0.0f, 1.0f }, // green
{ 1.0f, 0.0f, 0.0f, 1.0f }, // red
{ 0.0f, 0.0f, 1.0f, 1.0f }, // blue
{ 1.0f, 0.0f, 1.0f, 1.0f }, // purple
{ 1.0f, 1.0f, 0.0f, 1.0f }, // yellow
{ 0.0f, 1.0f, 1.0f, 1.0f }, // cyan
{ 1.0f, 1.0f, 1.0f, 1.0f }, // white
{ 0.0f, 0.5f, 0.0f, 1.0f },
{ 0.0f, 0.0f, 0.5f, 1.0f },
{ 0.5f, 0.0f, 0.5f, 1.0f },
{ 0.5f, 0.5f, 0.0f, 1.0f },
{ 0.0f, 0.5f, 0.5f, 1.0f } };
DependencyManager::get<GeometryCache>()->updateVertices(_debugMeshBoxesID, points, color[colorNdx]);
DependencyManager::get<GeometryCache>()->renderVertices(batch, gpu::LINES, _debugMeshBoxesID);
colorNdx++;
}
_mutex.unlock();
}

View file

@ -423,8 +423,7 @@ protected:
bool _overrideModelTransform { false };
bool _triangleSetsValid { false };
void calculateTriangleSets(const FBXGeometry& geometry);
QVector<TriangleSet> _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes
std::vector<std::vector<TriangleSet>> _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes
virtual void createRenderItemSet();

View file

@ -61,7 +61,6 @@ protected:
class DrawOverlay3D {
public:
using Inputs = render::VaryingSet3<render::ItemBounds, LightingModelPointer, glm::vec2>;
using Config = DrawOverlay3DConfig;
using JobModel = render::Job::ModelI<DrawOverlay3D, Inputs, Config>;
@ -73,7 +72,7 @@ public:
protected:
render::ShapePlumberPointer _shapePlumber;
int _maxDrawn; // initialized by Config
bool _opaquePass{ true };
bool _opaquePass { true };
};
class CompositeHUD {

View file

@ -64,8 +64,8 @@ void RenderDeferredTask::configure(const Config& config)
}
const render::Varying RenderDeferredTask::addSelectItemJobs(JobModel& task, const char* selectionName,
const render::Varying& metas,
const render::Varying& opaques,
const render::Varying& metas,
const render::Varying& opaques,
const render::Varying& transparents) {
const auto selectMetaInput = SelectItems::Inputs(metas, Varying(), std::string()).asVarying();
const auto selectedMetas = task.addJob<SelectItems>("MetaSelection", selectMetaInput, selectionName);
@ -75,7 +75,7 @@ const render::Varying RenderDeferredTask::addSelectItemJobs(JobModel& task, cons
return task.addJob<SelectItems>("TransparentSelection", selectItemInput, selectionName);
}
void RenderDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output) {
void RenderDeferredTask::build(JobModel& task, const render::Varying& input, render::Varying& output, bool renderShadows) {
const auto& items = input.get<Input>();
auto fadeEffect = DependencyManager::get<FadeEffect>();
@ -168,7 +168,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
const auto deferredLightingInputs = RenderDeferred::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel,
surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, scatteringResource, lightClusters, hazeModel).asVarying();
task.addJob<RenderDeferred>("RenderDeferred", deferredLightingInputs);
task.addJob<RenderDeferred>("RenderDeferred", deferredLightingInputs, renderShadows);
// Similar to light stage, background stage has been filled by several potential render items and resolved for the frame in this job

View file

@ -126,7 +126,7 @@ public:
RenderDeferredTask();
void configure(const Config& config);
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs);
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, bool renderShadows);
private:
static const render::Varying addSelectItemJobs(JobModel& task,

View file

@ -27,7 +27,7 @@ void RenderViewTask::build(JobModel& task, const render::Varying& input, render:
assert(items.canCast<RenderFetchCullSortTask::Output>());
if (isDeferred) {
task.addJob<RenderDeferredTask>("RenderDeferredTask", items);
task.addJob<RenderDeferredTask>("RenderDeferredTask", items, true);
} else {
task.addJob<RenderForwardTask>("Forward", items);
}

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#include <vector>
#include "AABox.h"

View file

@ -0,0 +1,73 @@
// raypickTester.js
//
// display intersection details (including material) when hovering over entities/avatars/overlays
//
/* eslint-disable comma-dangle, no-empty, no-magic-numbers */
var PICK_FILTERS = Picks.PICK_ENTITIES | Picks.PICK_OVERLAYS | Picks.PICK_AVATARS | Picks.PICK_INCLUDE_NONCOLLIDABLE;
var HAND_JOINT = '_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND'.replace('RIGHT', MyAvatar.getDominantHand().toUpperCase());
var JOINT_NAME = HMD.active ? HAND_JOINT : 'Mouse';
var UPDATE_MS = 1000/30;
// create tect3d overlay to display hover results
var overlayID = Overlays.addOverlay('text3d', {
text: 'hover',
visible: false,
backgroundAlpha: 0,
isFacingAvatar: true,
lineHeight: 0.05,
dimensions: Vec3.HALF,
});
Script.scriptEnding.connect(function() {
Overlays.deleteOverlay(overlayID);
});
// create raycast picker
var pickID = Picks.createPick(PickType.Ray, {
joint: JOINT_NAME,
filter: PICK_FILTERS,
enabled: true,
});
var blacklist = [ overlayID ]; // exclude hover text from ray pick results
Picks.setIgnoreItems(pickID, blacklist);
Script.scriptEnding.connect(function() {
Picks.removePick(pickID);
});
// query object materials (using the Graphics.* API)
function getSubmeshMaterial(objectID, shapeID) {
try {
var materialLayers = Graphics.getModel(objectID).materialLayers;
var shapeMaterialLayers = materialLayers[shapeID];
return shapeMaterialLayers[0].material;
} catch (e) {
return { name: '<unknown>' };
}
}
// refresh hover overlay text based on intersection results
function updateOverlay(overlayID, result) {
var material = this.getSubmeshMaterial(result.objectID, result.extraInfo.shapeID);
var position = Vec3.mix(result.searchRay.origin, result.intersection, 0.5);
var extraInfo = result.extraInfo;
var text = [
'mesh: ' + extraInfo.subMeshName,
'materialName: ' + material.name,
'type: ' + Entities.getNestableType(result.objectID),
'distance: ' + result.distance.toFixed(2)+'m',
['submesh: ' + extraInfo.subMeshIndex, 'part: '+extraInfo.partIndex, 'shape: '+extraInfo.shapeID].join(' | '),
].filter(Boolean).join('\n');
Overlays.editOverlay(overlayID, {
text: text,
position: position,
visible: result.intersects,
});
}
// monitor for enw results at 30fps
Script.setInterval(function() {
var result = Picks.getPrevPickResult(pickID);
updateOverlay(overlayID, result);
}, UPDATE_MS);

View file

@ -260,7 +260,7 @@ function Grabber() {
this.mouseRayOverlays = Picks.createPick(PickType.Ray, {
joint: "Mouse",
filter: Picks.PICK_OVERLAYS,
filter: Picks.PICK_OVERLAYS | Picks.PICK_INCLUDE_NONCOLLIDABLE,
enabled: true
});
var tabletItems = getMainTabletIDs();
@ -270,7 +270,7 @@ function Grabber() {
var renderStates = [{name: "grabbed", end: beacon}];
this.mouseRayEntities = Pointers.createPointer(PickType.Ray, {
joint: "Mouse",
filter: Picks.PICK_ENTITIES,
filter: Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_NONCOLLIDABLE,
faceAvatar: true,
scaleWithAvatar: true,
enabled: true,

View file

@ -475,15 +475,16 @@ var toolBar = (function () {
var DELETE_ENTITY_TIMER_TIMEOUT = 100;
function checkDeletedEntityAndUpdate(entityID) {
// Allow for multiple entity deletes before updating the entity list.
// Allow for multiple entity deletes before updating the entities selected.
entitiesToDelete.push(entityID);
if (deletedEntityTimer !== null) {
Script.clearTimeout(deletedEntityTimer);
}
deletedEntityTimer = Script.setTimeout(function () {
selectionManager.removeEntities(entitiesToDelete);
entityListTool.clearEntityList();
entityListTool.sendUpdate();
if (entitiesToDelete.length > 0) {
selectionManager.removeEntities(entitiesToDelete);
}
entityListTool.removeEntities(entitiesToDelete, selectionManager.selections);
entitiesToDelete = [];
deletedEntityTimer = null;
}, DELETE_ENTITY_TIMER_TIMEOUT);
@ -2027,12 +2028,8 @@ var PropertiesTool = function (opts) {
} else if (data.type === "update") {
selectionManager.saveProperties();
if (selectionManager.selections.length > 1) {
properties = {
locked: data.properties.locked,
visible: data.properties.visible
};
for (i = 0; i < selectionManager.selections.length; i++) {
Entities.editEntity(selectionManager.selections[i], properties);
Entities.editEntity(selectionManager.selections[i], data.properties);
}
} else if (data.properties) {
if (data.properties.dynamic === false) {

View file

@ -269,6 +269,9 @@ For usage and examples: colpick.com/plugin
},
// Show/hide the color picker
show = function (ev) {
if ($(this).attr('disabled')) {
return;
}
// Prevent the trigger of any direct parent
ev.stopPropagation();
var cal = $('#' + $(this).data('colpickId'));

View file

@ -184,17 +184,27 @@ function loaded() {
currentElement.onclick = onRowClicked;
currentElement.ondblclick = onRowDoubleClicked;
});
if (refreshEntityListTimer) {
clearTimeout(refreshEntityListTimer);
}
refreshEntityListTimer = setTimeout(refreshEntityListObject, 50);
} else {
var item = entities[id].item;
item.values({ name: name, url: filename, locked: locked, visible: visible });
}
}
function removeEntities(deletedIDs) {
for (i = 0, length = deletedIDs.length; i < length; i++) {
delete entities[deletedIDs[i]];
entityList.remove("id", deletedIDs[i]);
}
}
function scheduleRefreshEntityList() {
var REFRESH_DELAY = 50;
if (refreshEntityListTimer) {
clearTimeout(refreshEntityListTimer);
}
refreshEntityListTimer = setTimeout(refreshEntityListObject, REFRESH_DELAY);
}
function clearEntities() {
entities = {};
entityList.clear();
@ -346,7 +356,7 @@ function loaded() {
if (notFound) {
refreshEntities();
}
} else if (data.type == "update") {
} else if (data.type === "update" && data.selectedIDs !== undefined) {
var newEntities = data.entities;
if (newEntities && newEntities.length == 0) {
elNoEntitiesMessage.style.display = "block";
@ -365,13 +375,15 @@ function loaded() {
newEntities[i].hasScript ? SCRIPT_GLYPH : null);
}
updateSelectedEntities(data.selectedIDs);
scheduleRefreshEntityList();
resize();
}
} else if (data.type === "deleted") {
for (i = 0, length = data.ids.length; i < length; i++) {
delete entities[data.ids[i]];
entityList.remove("id", data.ids[i]);
}
} else if (data.type === "removeEntities" && data.deletedIDs !== undefined && data.selectedIDs !== undefined) {
removeEntities(data.deletedIDs);
updateSelectedEntities(data.selectedIDs);
scheduleRefreshEntityList();
} else if (data.type === "deleted" && data.ids) {
removeEntities(data.ids);
refreshFooter();
}
});

View file

@ -893,9 +893,9 @@ function loaded() {
} else {
elServerScriptStatus.innerText = "Not running";
}
} else if (data.type === "update") {
} else if (data.type === "update" && data.selections) {
if (!data.selections || data.selections.length === 0) {
if (data.selections.length === 0) {
if (lastEntityID !== null) {
if (editor !== null) {
saveJSONUserData(true);
@ -911,7 +911,7 @@ function loaded() {
elID.value = "";
elPropertiesList.className = '';
disableProperties();
} else if (data.selections && data.selections.length > 1) {
} else if (data.selections.length > 1) {
deleteJSONEditor();
deleteJSONMaterialEditor();
var selections = data.selections;

View file

@ -57,6 +57,15 @@ EntityListTool = function(opts) {
webView.emitScriptEvent(JSON.stringify(data));
};
that.removeEntities = function (deletedIDs, selectedIDs) {
var data = {
type: 'removeEntities',
deletedIDs: deletedIDs,
selectedIDs: selectedIDs
};
webView.emitScriptEvent(JSON.stringify(data));
};
function valueIfDefined(value) {
return value !== undefined ? value : "";
}

View file

@ -13,11 +13,12 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
/* global SPACE_LOCAL, SelectionManager */
/* global SelectionManager, grid, rayPlaneIntersection, rayPlaneIntersection2, pushCommandForSelections,
getMainTabletIDs, getControllerWorldLocation */
SPACE_LOCAL = "local";
SPACE_WORLD = "world";
HIGHLIGHT_LIST_NAME = "editHandleHighlightList";
var SPACE_LOCAL = "local";
var SPACE_WORLD = "world";
var HIGHLIGHT_LIST_NAME = "editHandleHighlightList";
Script.include([
"./controllers.js",
@ -84,7 +85,7 @@ SelectionManager = (function() {
outlineWidth: 3,
isOutlineSmooth: true
};
//disabling this for now as it is causing rendering issues with the other handle overlays
// disabling this for now as it is causing rendering issues with the other handle overlays
//Selection.enableListHighlight(HIGHLIGHT_LIST_NAME, editHandleOutlineStyle);
that.savedProperties = {};
@ -192,7 +193,7 @@ SelectionManager = (function() {
}
});
return duplicatedEntityIDs;
}
};
that._update = function(selectionUpdated) {
var properties = null;
@ -270,11 +271,12 @@ SelectionManager = (function() {
// Normalize degrees to be in the range (-180, 180)
function normalizeDegrees(degrees) {
degrees = ((degrees + 180) % 360) - 180;
if (degrees <= -180) {
degrees += 360;
var maxDegrees = 360;
var halfMaxDegrees = maxDegrees / 2;
degrees = ((degrees + halfMaxDegrees) % maxDegrees) - halfMaxDegrees;
if (degrees <= -halfMaxDegrees) {
degrees += maxDegrees;
}
return degrees;
}
@ -284,14 +286,14 @@ SelectionDisplay = (function() {
var NEGATE_VECTOR = -1;
var COLOR_GREEN = { red:31, green:198, blue:166 };
var COLOR_BLUE = { red:0, green:147, blue:197 };
var COLOR_RED = { red:226, green:51, blue:77 };
var COLOR_HOVER = { red:227, green:227, blue:227 };
var COLOR_GREEN = { red: 31, green: 198, blue: 166 };
var COLOR_BLUE = { red: 0, green: 147, blue: 197 };
var COLOR_RED = { red: 226, green: 51, blue: 77 };
var COLOR_HOVER = { red: 227, green: 227, blue: 227 };
var COLOR_ROTATE_CURRENT_RING = { red: 255, green: 99, blue: 9 };
var COLOR_SCALE_EDGE = { red:87, green:87, blue:87 };
var COLOR_SCALE_CUBE = { red:106, green:106, blue:106 };
var COLOR_SCALE_CUBE_SELECTED = { red:18, green:18, blue:18 };
var COLOR_SCALE_EDGE = { red: 87, green: 87, blue: 87 };
var COLOR_SCALE_CUBE = { red: 106, green: 106, blue: 106 };
var COLOR_SCALE_CUBE_SELECTED = { red: 18, green: 18, blue: 18 };
var TRANSLATE_ARROW_CYLINDER_OFFSET = 0.1;
var TRANSLATE_ARROW_CYLINDER_CAMERA_DISTANCE_MULTIPLE = 0.005;
@ -320,41 +322,41 @@ SelectionDisplay = (function() {
var STRETCH_PANEL_WIDTH = 0.01;
var SCALE_CUBE_OFFSET = 0.5;
var SCALE_CUBE_CAMERA_DISTANCE_MULTIPLE = 0.015;
var SCALE_CUBE_CAMERA_DISTANCE_MULTIPLE = 0.0125;
var CLONER_OFFSET = { x:0.9, y:-0.9, z:0.9 };
var CLONER_OFFSET = { x: 0.9, y: -0.9, z: 0.9 };
var CTRL_KEY_CODE = 16777249;
var TRANSLATE_DIRECTION = {
X : 0,
Y : 1,
Z : 2
}
X: 0,
Y: 1,
Z: 2
};
var STRETCH_DIRECTION = {
X : 0,
Y : 1,
Z : 2,
ALL : 3
}
X: 0,
Y: 1,
Z: 2,
ALL: 3
};
var SCALE_DIRECTION = {
LBN : 0,
RBN : 1,
LBF : 2,
RBF : 3,
LTN : 4,
RTN : 5,
LTF : 6,
RTF : 7
}
LBN: 0,
RBN: 1,
LBF: 2,
RBF: 3,
LTN: 4,
RTN: 5,
LTF: 6,
RTF: 7
};
var ROTATE_DIRECTION = {
PITCH : 0,
YAW : 1,
ROLL : 2
}
PITCH: 0,
YAW: 1,
ROLL: 2
};
var spaceMode = SPACE_LOCAL;
var overlayNames = [];
@ -397,16 +399,16 @@ SelectionDisplay = (function() {
};
var handleTranslateXCone = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCones);
var handleTranslateXCylinder = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCylinders);
Overlays.editOverlay(handleTranslateXCone, { color : COLOR_RED });
Overlays.editOverlay(handleTranslateXCylinder, { color : COLOR_RED });
Overlays.editOverlay(handleTranslateXCone, { color: COLOR_RED });
Overlays.editOverlay(handleTranslateXCylinder, { color: COLOR_RED });
var handleTranslateYCone = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCones);
var handleTranslateYCylinder = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCylinders);
Overlays.editOverlay(handleTranslateYCone, { color : COLOR_GREEN });
Overlays.editOverlay(handleTranslateYCylinder, { color : COLOR_GREEN });
Overlays.editOverlay(handleTranslateYCone, { color: COLOR_GREEN });
Overlays.editOverlay(handleTranslateYCylinder, { color: COLOR_GREEN });
var handleTranslateZCone = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCones);
var handleTranslateZCylinder = Overlays.addOverlay("shape", handlePropertiesTranslateArrowCylinders);
Overlays.editOverlay(handleTranslateZCone, { color : COLOR_BLUE });
Overlays.editOverlay(handleTranslateZCylinder, { color : COLOR_BLUE });
Overlays.editOverlay(handleTranslateZCone, { color: COLOR_BLUE });
Overlays.editOverlay(handleTranslateZCylinder, { color: COLOR_BLUE });
var handlePropertiesRotateRings = {
alpha: 1,
@ -422,18 +424,18 @@ SelectionDisplay = (function() {
};
var handleRotatePitchRing = Overlays.addOverlay("circle3d", handlePropertiesRotateRings);
Overlays.editOverlay(handleRotatePitchRing, {
color : COLOR_RED,
majorTickMarksColor: COLOR_RED,
color: COLOR_RED,
majorTickMarksColor: COLOR_RED
});
var handleRotateYawRing = Overlays.addOverlay("circle3d", handlePropertiesRotateRings);
Overlays.editOverlay(handleRotateYawRing, {
color : COLOR_GREEN,
majorTickMarksColor: COLOR_GREEN,
color: COLOR_GREEN,
majorTickMarksColor: COLOR_GREEN
});
var handleRotateRollRing = Overlays.addOverlay("circle3d", handlePropertiesRotateRings);
Overlays.editOverlay(handleRotateRollRing, {
color : COLOR_BLUE,
majorTickMarksColor: COLOR_BLUE,
color: COLOR_BLUE,
majorTickMarksColor: COLOR_BLUE
});
var handleRotateCurrentRing = Overlays.addOverlay("circle3d", {
@ -472,11 +474,11 @@ SelectionDisplay = (function() {
drawInFront: true
};
var handleStretchXSphere = Overlays.addOverlay("shape", handlePropertiesStretchSpheres);
Overlays.editOverlay(handleStretchXSphere, { color : COLOR_RED });
Overlays.editOverlay(handleStretchXSphere, { color: COLOR_RED });
var handleStretchYSphere = Overlays.addOverlay("shape", handlePropertiesStretchSpheres);
Overlays.editOverlay(handleStretchYSphere, { color : COLOR_GREEN });
Overlays.editOverlay(handleStretchYSphere, { color: COLOR_GREEN });
var handleStretchZSphere = Overlays.addOverlay("shape", handlePropertiesStretchSpheres);
Overlays.editOverlay(handleStretchZSphere, { color : COLOR_BLUE });
Overlays.editOverlay(handleStretchZSphere, { color: COLOR_BLUE });
var handlePropertiesStretchPanel = {
shape: "Quad",
@ -485,13 +487,13 @@ SelectionDisplay = (function() {
visible: false,
ignoreRayIntersection: true,
drawInFront: true
}
};
var handleStretchXPanel = Overlays.addOverlay("shape", handlePropertiesStretchPanel);
Overlays.editOverlay(handleStretchXPanel, { color : COLOR_RED });
Overlays.editOverlay(handleStretchXPanel, { color: COLOR_RED });
var handleStretchYPanel = Overlays.addOverlay("shape", handlePropertiesStretchPanel);
Overlays.editOverlay(handleStretchYPanel, { color : COLOR_GREEN });
Overlays.editOverlay(handleStretchYPanel, { color: COLOR_GREEN });
var handleStretchZPanel = Overlays.addOverlay("shape", handlePropertiesStretchPanel);
Overlays.editOverlay(handleStretchZPanel, { color : COLOR_BLUE });
Overlays.editOverlay(handleStretchZPanel, { color: COLOR_BLUE });
var handlePropertiesScaleCubes = {
size: 0.025,
@ -517,7 +519,7 @@ SelectionDisplay = (function() {
ignoreRayIntersection: true,
drawInFront: true,
lineWidth: 0.2
}
};
var handleScaleTREdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge);
var handleScaleTLEdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge);
var handleScaleTFEdge = Overlays.addOverlay("line3d", handlePropertiesScaleEdge);
@ -785,11 +787,11 @@ SelectionDisplay = (function() {
};
that.resetPreviousHandleColor = function() {
if (previousHandle != null) {
if (previousHandle !== null) {
Overlays.editOverlay(previousHandle, { color: previousHandleColor });
previousHandle = null;
}
if (previousHandleHelper != null) {
if (previousHandleHelper !== null) {
Overlays.editOverlay(previousHandleHelper, { color: previousHandleColor });
previousHandleHelper = null;
}
@ -886,7 +888,7 @@ SelectionDisplay = (function() {
Overlays.editOverlay(result.overlayID, { color: COLOR_HOVER });
previousHandle = result.overlayID;
previousHandleHelper = that.getHandleHelper(result.overlayID);
if (previousHandleHelper != null) {
if (previousHandleHelper !== null) {
Overlays.editOverlay(previousHandleHelper, { color: COLOR_HOVER });
}
previousHandleColor = pickedColor;
@ -944,7 +946,7 @@ SelectionDisplay = (function() {
ctrlPressed = false;
that.updateActiveRotateRing();
}
}
};
// Triggers notification on specific key driven events
that.keyPressEvent = function(key) {
@ -952,7 +954,7 @@ SelectionDisplay = (function() {
ctrlPressed = true;
that.updateActiveRotateRing();
}
}
};
// NOTE: mousePressEvent and mouseMoveEvent from the main script should call us., so we don't hook these:
// Controller.mousePressEvent.connect(that.mousePressEvent);
@ -994,6 +996,11 @@ SelectionDisplay = (function() {
var toCameraDistance = Vec3.length(Vec3.subtract(cameraPosition, position));
return toCameraDistance;
}
function usePreviousPickRay(pickRayDirection, previousPickRayDirection, normal) {
return (Vec3.dot(pickRayDirection, normal) > 0 && Vec3.dot(previousPickRayDirection, normal) < 0) ||
(Vec3.dot(pickRayDirection, normal) < 0 && Vec3.dot(previousPickRayDirection, normal) > 0);
}
// @return string - The mode of the currently active tool;
// otherwise, "UNKNOWN" if there's no active tool.
@ -1014,8 +1021,6 @@ SelectionDisplay = (function() {
lastCameraOrientation = Camera.getOrientation();
if (event !== false) {
pickRay = generalComputePickRay(event.x, event.y);
var wantDebug = false;
if (wantDebug) {
print("select() with EVENT...... ");
@ -1041,7 +1046,8 @@ SelectionDisplay = (function() {
spaceMode = newSpaceMode;
that.updateHandles();
} else if (wantDebug) {
print("WARNING: entitySelectionTool.setSpaceMode - Can't update SpaceMode. CurrentMode: " + spaceMode + " DesiredMode: " + newSpaceMode);
print("WARNING: entitySelectionTool.setSpaceMode - Can't update SpaceMode. CurrentMode: " +
spaceMode + " DesiredMode: " + newSpaceMode);
}
if (wantDebug) {
print("====== SetSpaceMode called. <========");
@ -1091,7 +1097,8 @@ SelectionDisplay = (function() {
}
if (!handleTools.hasOwnProperty(toolHandle)) {
print("WARNING: entitySelectionTool.isActiveTool - Encountered unknown grabberToolHandle: " + toolHandle + ". Tools should be registered via addHandleTool.");
print("WARNING: entitySelectionTool.isActiveTool - Encountered unknown grabberToolHandle: " +
toolHandle + ". Tools should be registered via addHandleTool.");
// EARLY EXIT
return false;
}
@ -1121,13 +1128,14 @@ SelectionDisplay = (function() {
var rotationInverse = Quat.inverse(rotation);
var toCameraDistance = getDistanceToCamera(position);
var localRotationX = Quat.fromPitchYawRollDegrees(0, 0, -90);
var rotationDegrees = 90;
var localRotationX = Quat.fromPitchYawRollDegrees(0, 0, -rotationDegrees);
var rotationX = Quat.multiply(rotation, localRotationX);
worldRotationX = rotationX;
var localRotationY = Quat.fromPitchYawRollDegrees(0, 90, 0);
var localRotationY = Quat.fromPitchYawRollDegrees(0, rotationDegrees, 0);
var rotationY = Quat.multiply(rotation, localRotationY);
worldRotationY = rotationY;
var localRotationZ = Quat.fromPitchYawRollDegrees(90, 0, 0);
var localRotationZ = Quat.fromPitchYawRollDegrees(rotationDegrees, 0, 0);
var rotationZ = Quat.multiply(rotation, localRotationZ);
worldRotationZ = rotationZ;
@ -1140,7 +1148,7 @@ SelectionDisplay = (function() {
// UPDATE ROTATION RINGS
// rotateDimension is used as the base dimension for all overlays
var rotateDimension = Math.max(maxHandleDimension, toCameraDistance * ROTATE_RING_CAMERA_DISTANCE_MULTIPLE);
var rotateDimensions = { x:rotateDimension, y:rotateDimension, z:rotateDimension };
var rotateDimensions = { x: rotateDimension, y: rotateDimension, z: rotateDimension };
if (!isActiveTool(handleRotatePitchRing)) {
Overlays.editOverlay(handleRotatePitchRing, {
position: position,
@ -1172,16 +1180,16 @@ SelectionDisplay = (function() {
var arrowCylinderDimension = rotateDimension * TRANSLATE_ARROW_CYLINDER_CAMERA_DISTANCE_MULTIPLE /
ROTATE_RING_CAMERA_DISTANCE_MULTIPLE;
var arrowCylinderDimensions = {
x:arrowCylinderDimension,
y:arrowCylinderDimension * TRANSLATE_ARROW_CYLINDER_Y_MULTIPLE,
z:arrowCylinderDimension
x: arrowCylinderDimension,
y: arrowCylinderDimension * TRANSLATE_ARROW_CYLINDER_Y_MULTIPLE,
z: arrowCylinderDimension
};
var arrowConeDimension = rotateDimension * TRANSLATE_ARROW_CONE_CAMERA_DISTANCE_MULTIPLE /
ROTATE_RING_CAMERA_DISTANCE_MULTIPLE;
var arrowConeDimensions = { x:arrowConeDimension, y:arrowConeDimension, z:arrowConeDimension };
var arrowConeDimensions = { x: arrowConeDimension, y: arrowConeDimension, z: arrowConeDimension };
var arrowCylinderOffset = rotateDimension * TRANSLATE_ARROW_CYLINDER_OFFSET / ROTATE_RING_CAMERA_DISTANCE_MULTIPLE;
var arrowConeOffset = arrowCylinderDimensions.y * TRANSLATE_ARROW_CONE_OFFSET_CYLINDER_DIMENSION_MULTIPLE;
var cylinderXPosition = { x:arrowCylinderOffset, y:0, z:0 };
var cylinderXPosition = { x: arrowCylinderOffset, y: 0, z: 0 };
cylinderXPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, cylinderXPosition));
Overlays.editOverlay(handleTranslateXCylinder, {
position: cylinderXPosition,
@ -1195,7 +1203,7 @@ SelectionDisplay = (function() {
rotation: rotationX,
dimensions: arrowConeDimensions
});
var cylinderYPosition = { x:0, y:arrowCylinderOffset, z:0 };
var cylinderYPosition = { x: 0, y: arrowCylinderOffset, z: 0 };
cylinderYPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, cylinderYPosition));
Overlays.editOverlay(handleTranslateYCylinder, {
position: cylinderYPosition,
@ -1209,7 +1217,7 @@ SelectionDisplay = (function() {
rotation: rotationY,
dimensions: arrowConeDimensions
});
var cylinderZPosition = { x:0, y:0, z:arrowCylinderOffset };
var cylinderZPosition = { x: 0, y: 0, z: arrowCylinderOffset };
cylinderZPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, cylinderZPosition));
Overlays.editOverlay(handleTranslateZCylinder, {
position: cylinderZPosition,
@ -1228,61 +1236,73 @@ SelectionDisplay = (function() {
var scaleCubeOffsetX = SCALE_CUBE_OFFSET * dimensions.x;
var scaleCubeOffsetY = SCALE_CUBE_OFFSET * dimensions.y;
var scaleCubeOffsetZ = SCALE_CUBE_OFFSET * dimensions.z;
var scaleCubeDimension = rotateDimension * SCALE_CUBE_CAMERA_DISTANCE_MULTIPLE /
ROTATE_RING_CAMERA_DISTANCE_MULTIPLE;
var scaleCubeDimensions = { x:scaleCubeDimension, y:scaleCubeDimension, z:scaleCubeDimension };
var scaleCubeRotation = spaceMode === SPACE_LOCAL ? rotation : Quat.IDENTITY;
var scaleLBNCubePosition = { x:-scaleCubeOffsetX, y:-scaleCubeOffsetY, z:-scaleCubeOffsetZ };
var scaleLBNCubePosition = { x: -scaleCubeOffsetX, y: -scaleCubeOffsetY, z: -scaleCubeOffsetZ };
scaleLBNCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleLBNCubePosition));
var scaleLBNCubeToCamera = getDistanceToCamera(scaleLBNCubePosition);
var scaleRBNCubePosition = { x: scaleCubeOffsetX, y: -scaleCubeOffsetY, z: -scaleCubeOffsetZ };
scaleRBNCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleRBNCubePosition));
var scaleRBNCubeToCamera = getDistanceToCamera(scaleRBNCubePosition);
var scaleLBFCubePosition = { x: -scaleCubeOffsetX, y: -scaleCubeOffsetY, z: scaleCubeOffsetZ };
scaleLBFCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleLBFCubePosition));
var scaleLBFCubeToCamera = getDistanceToCamera(scaleLBFCubePosition);
var scaleRBFCubePosition = { x: scaleCubeOffsetX, y: -scaleCubeOffsetY, z: scaleCubeOffsetZ };
scaleRBFCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleRBFCubePosition));
var scaleRBFCubeToCamera = getDistanceToCamera(scaleRBFCubePosition);
var scaleLTNCubePosition = { x: -scaleCubeOffsetX, y: scaleCubeOffsetY, z: -scaleCubeOffsetZ };
scaleLTNCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleLTNCubePosition));
var scaleLTNCubeToCamera = getDistanceToCamera(scaleLTNCubePosition);
var scaleRTNCubePosition = { x: scaleCubeOffsetX, y: scaleCubeOffsetY, z: -scaleCubeOffsetZ };
scaleRTNCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleRTNCubePosition));
var scaleRTNCubeToCamera = getDistanceToCamera(scaleRTNCubePosition);
var scaleLTFCubePosition = { x: -scaleCubeOffsetX, y: scaleCubeOffsetY, z: scaleCubeOffsetZ };
scaleLTFCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleLTFCubePosition));
var scaleLTFCubeToCamera = getDistanceToCamera(scaleLTFCubePosition);
var scaleRTFCubePosition = { x: scaleCubeOffsetX, y: scaleCubeOffsetY, z: scaleCubeOffsetZ };
scaleRTFCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleRTFCubePosition));
var scaleRTFCubeToCamera = getDistanceToCamera(scaleRTFCubePosition);
var scaleCubeToCamera = Math.min(scaleLBNCubeToCamera, scaleRBNCubeToCamera, scaleLBFCubeToCamera,
scaleRBFCubeToCamera, scaleLTNCubeToCamera, scaleRTNCubeToCamera,
scaleLTFCubeToCamera, scaleRTFCubeToCamera);
var scaleCubeDimension = scaleCubeToCamera * SCALE_CUBE_CAMERA_DISTANCE_MULTIPLE;
var scaleCubeDimensions = { x: scaleCubeDimension, y: scaleCubeDimension, z: scaleCubeDimension };
Overlays.editOverlay(handleScaleLBNCube, {
position: scaleLBNCubePosition,
rotation: scaleCubeRotation,
dimensions: scaleCubeDimensions
});
var scaleRBNCubePosition = { x:scaleCubeOffsetX, y:-scaleCubeOffsetY, z:-scaleCubeOffsetZ };
scaleRBNCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleRBNCubePosition));
Overlays.editOverlay(handleScaleRBNCube, {
position: scaleRBNCubePosition,
rotation: scaleCubeRotation,
dimensions: scaleCubeDimensions
});
var scaleLBFCubePosition = { x:-scaleCubeOffsetX, y:-scaleCubeOffsetY, z:scaleCubeOffsetZ };
scaleLBFCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleLBFCubePosition));
Overlays.editOverlay(handleScaleLBFCube, {
position: scaleLBFCubePosition,
rotation: scaleCubeRotation,
dimensions: scaleCubeDimensions
});
var scaleRBFCubePosition = { x:scaleCubeOffsetX, y:-scaleCubeOffsetY, z:scaleCubeOffsetZ };
scaleRBFCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleRBFCubePosition));
Overlays.editOverlay(handleScaleRBFCube, {
position: scaleRBFCubePosition,
rotation: scaleCubeRotation,
dimensions: scaleCubeDimensions
});
var scaleLTNCubePosition = { x:-scaleCubeOffsetX, y:scaleCubeOffsetY, z:-scaleCubeOffsetZ };
scaleLTNCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleLTNCubePosition));
Overlays.editOverlay(handleScaleLTNCube, {
position: scaleLTNCubePosition,
rotation: scaleCubeRotation,
dimensions: scaleCubeDimensions
});
var scaleRTNCubePosition = { x:scaleCubeOffsetX, y:scaleCubeOffsetY, z:-scaleCubeOffsetZ };
scaleRTNCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleRTNCubePosition));
Overlays.editOverlay(handleScaleRTNCube, {
position: scaleRTNCubePosition,
rotation: scaleCubeRotation,
dimensions: scaleCubeDimensions
});
var scaleLTFCubePosition = { x:-scaleCubeOffsetX, y:scaleCubeOffsetY, z:scaleCubeOffsetZ };
scaleLTFCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleLTFCubePosition));
Overlays.editOverlay(handleScaleLTFCube, {
position: scaleLTFCubePosition,
rotation: scaleCubeRotation,
dimensions: scaleCubeDimensions
});
var scaleRTFCubePosition = { x:scaleCubeOffsetX, y:scaleCubeOffsetY, z:scaleCubeOffsetZ };
scaleRTFCubePosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, scaleRTFCubePosition));
Overlays.editOverlay(handleScaleRTFCube, {
position: scaleRTFCubePosition,
rotation: scaleCubeRotation,
@ -1306,21 +1326,21 @@ SelectionDisplay = (function() {
// UPDATE STRETCH SPHERES
var stretchSphereDimension = rotateDimension * STRETCH_SPHERE_CAMERA_DISTANCE_MULTIPLE /
ROTATE_RING_CAMERA_DISTANCE_MULTIPLE;
var stretchSphereDimensions = { x:stretchSphereDimension, y:stretchSphereDimension, z:stretchSphereDimension };
var stretchSphereDimensions = { x: stretchSphereDimension, y: stretchSphereDimension, z: stretchSphereDimension };
var stretchSphereOffset = rotateDimension * STRETCH_SPHERE_OFFSET / ROTATE_RING_CAMERA_DISTANCE_MULTIPLE;
var stretchXPosition = { x:stretchSphereOffset, y:0, z:0 };
var stretchXPosition = { x: stretchSphereOffset, y: 0, z: 0 };
stretchXPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, stretchXPosition));
Overlays.editOverlay(handleStretchXSphere, {
position: stretchXPosition,
dimensions: stretchSphereDimensions
});
var stretchYPosition = { x:0, y:stretchSphereOffset, z:0 };
var stretchYPosition = { x: 0, y: stretchSphereOffset, z: 0 };
stretchYPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, stretchYPosition));
Overlays.editOverlay(handleStretchYSphere, {
position: stretchYPosition,
dimensions: stretchSphereDimensions
});
var stretchZPosition = { x:0, y:0, z:stretchSphereOffset };
var stretchZPosition = { x: 0, y: 0, z: stretchSphereOffset };
stretchZPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, stretchZPosition));
Overlays.editOverlay(handleStretchZSphere, {
position: stretchZPosition,
@ -1337,27 +1357,29 @@ SelectionDisplay = (function() {
stretchPanelXDimensions.x = STRETCH_PANEL_WIDTH;
stretchPanelXDimensions.y = Math.abs(stretchPanelXDimensions.z);
stretchPanelXDimensions.z = tempY;
var stretchPanelXPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:dimensions.x / 2, y:0, z:0 }));
var stretchPanelXPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x: dimensions.x / 2, y: 0, z: 0 }));
Overlays.editOverlay(handleStretchXPanel, {
position: stretchPanelXPosition,
rotation: rotationZ,
dimensions: stretchPanelXDimensions
});
var stretchPanelYDimensions = Vec3.subtract(scaleLTNCubePositionRotated, scaleRTFCubePositionRotated);
var tempX = Math.abs(stretchPanelYDimensions.x);
stretchPanelYDimensions.x = Math.abs(stretchPanelYDimensions.z);
stretchPanelYDimensions.y = STRETCH_PANEL_WIDTH;
stretchPanelYDimensions.z = Math.abs(stretchPanelYDimensions.x);
var stretchPanelYPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:0, y:dimensions.y / 2, z:0 }));
stretchPanelYDimensions.z = tempX;
var stretchPanelYPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x: 0, y: dimensions.y / 2, z: 0 }));
Overlays.editOverlay(handleStretchYPanel, {
position: stretchPanelYPosition,
rotation: rotationY,
dimensions: stretchPanelYDimensions
});
var stretchPanelZDimensions = Vec3.subtract(scaleLTNCubePositionRotated, scaleRBFCubePositionRotated);
tempX = Math.abs(stretchPanelZDimensions.x);
stretchPanelZDimensions.x = Math.abs(stretchPanelZDimensions.y);
stretchPanelZDimensions.y = Math.abs(stretchPanelZDimensions.x);
stretchPanelZDimensions.y = tempX;
stretchPanelZDimensions.z = STRETCH_PANEL_WIDTH;
var stretchPanelZPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:0, y:0, z:dimensions.z / 2 }));
var stretchPanelZPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x: 0, y: 0, z: dimensions.z / 2 }));
Overlays.editOverlay(handleStretchZPanel, {
position: stretchPanelZPosition,
rotation: rotationX,
@ -1390,10 +1412,10 @@ SelectionDisplay = (function() {
}
// UPDATE CLONER (CURRENTLY HIDDEN FOR NOW)
var handleClonerOffset = {
x:CLONER_OFFSET.x * dimensions.x,
y:CLONER_OFFSET.y * dimensions.y,
z:CLONER_OFFSET.z * dimensions.z
var handleClonerOffset = {
x: CLONER_OFFSET.x * dimensions.x,
y: CLONER_OFFSET.y * dimensions.y,
z: CLONER_OFFSET.z * dimensions.z
};
var handleClonerPos = Vec3.sum(position, Vec3.multiplyQbyV(rotation, handleClonerOffset));
Overlays.editOverlay(handleCloner, {
@ -1431,9 +1453,9 @@ SelectionDisplay = (function() {
!isActiveTool(handleRotateYawRing) &&
!isActiveTool(handleRotateRollRing)));
//keep cloner always hidden for now since you can hold Alt to clone while
//translating an entity - we may bring cloner back for HMD only later
//that.setHandleClonerVisible(!activeTool || isActiveTool(handleCloner));
// keep cloner always hidden for now since you can hold Alt to clone while
// translating an entity - we may bring cloner back for HMD only later
// that.setHandleClonerVisible(!activeTool || isActiveTool(handleCloner));
if (wantDebug) {
print("====== Update Handles <=======");
@ -1451,8 +1473,8 @@ SelectionDisplay = (function() {
} else if (isActiveTool(handleRotateRollRing)) {
activeRotateRing = handleRotateRollRing;
}
if (activeRotateRing != null) {
var tickMarksAngle = ctrlPressed ? ROTATE_CTRL_SNAP_ANGLE : ROTATE_DEFAULT_TICK_MARKS_ANGLE;
if (activeRotateRing !== null) {
var tickMarksAngle = ctrlPressed ? ROTATE_CTRL_SNAP_ANGLE : ROTATE_DEFAULT_TICK_MARKS_ANGLE;
Overlays.editOverlay(activeRotateRing, { majorTickMarksAngle: tickMarksAngle });
}
};
@ -1633,7 +1655,7 @@ SelectionDisplay = (function() {
},
onMove: function(event) {
var wantDebug = false;
pickRay = generalComputePickRay(event.x, event.y);
var pickRay = generalComputePickRay(event.x, event.y);
var pick = rayPlaneIntersection2(pickRay, translateXZTool.pickPlanePosition, {
x: 0,
@ -1699,7 +1721,8 @@ SelectionDisplay = (function() {
}
constrainMajorOnly = event.isControl;
var cornerPosition = Vec3.sum(startPosition, Vec3.multiply(-0.5, SelectionManager.worldDimensions));
var negateAndHalve = -0.5;
var cornerPosition = Vec3.sum(startPosition, Vec3.multiply(negateAndHalve, SelectionManager.worldDimensions));
vector = Vec3.subtract(
grid.snapToGrid(Vec3.sum(cornerPosition, vector), constrainMajorOnly),
cornerPosition);
@ -1745,15 +1768,16 @@ SelectionDisplay = (function() {
var pickNormal = null;
var lastPick = null;
var projectionVector = null;
var previousPickRay = null;
addHandleTool(overlay, {
mode: mode,
onBegin: function(event, pickRay, pickResult) {
if (direction === TRANSLATE_DIRECTION.X) {
pickNormal = { x:0, y:1, z:1 };
pickNormal = { x: 0, y: 1, z: 1 };
} else if (direction === TRANSLATE_DIRECTION.Y) {
pickNormal = { x:1, y:0, z:1 };
pickNormal = { x: 1, y: 0, z: 1 };
} else if (direction === TRANSLATE_DIRECTION.Z) {
pickNormal = { x:1, y:1, z:0 };
pickNormal = { x: 1, y: 1, z: 0 };
}
var rotation = spaceMode === SPACE_LOCAL ? SelectionManager.localRotation : SelectionManager.worldRotation;
@ -1780,22 +1804,29 @@ SelectionDisplay = (function() {
} else {
duplicatedEntityIDs = null;
}
previousPickRay = pickRay;
},
onEnd: function(event, reason) {
pushCommandForSelections(duplicatedEntityIDs);
},
onMove: function(event) {
pickRay = generalComputePickRay(event.x, event.y);
var pickRay = generalComputePickRay(event.x, event.y);
// Use previousPickRay if new pickRay will cause resulting rayPlaneIntersection values to wrap around
if (usePreviousPickRay(pickRay.direction, previousPickRay.direction, pickNormal)) {
pickRay = previousPickRay;
}
var newIntersection = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, pickNormal);
var vector = Vec3.subtract(newIntersection, lastPick);
if (direction === TRANSLATE_DIRECTION.X) {
projectionVector = { x:1, y:0, z:0 };
projectionVector = { x: 1, y: 0, z: 0 };
} else if (direction === TRANSLATE_DIRECTION.Y) {
projectionVector = { x:0, y:1, z:0 };
projectionVector = { x: 0, y: 1, z: 0 };
} else if (direction === TRANSLATE_DIRECTION.Z) {
projectionVector = { x:0, y:0, z:1 };
projectionVector = { x: 0, y: 0, z: 1 };
}
var rotation = spaceMode === SPACE_LOCAL ? SelectionManager.localRotation : SelectionManager.worldRotation;
@ -1829,6 +1860,8 @@ SelectionDisplay = (function() {
var newPosition = Vec3.sum(properties.position, vector);
Entities.editEntity(id, { position: newPosition });
}
previousPickRay = pickRay;
SelectionManager._update();
}
@ -1869,7 +1902,6 @@ SelectionDisplay = (function() {
var lastPick3D = null;
var initialPosition = null;
var initialDimensions = null;
var initialIntersection = null;
var initialProperties = null;
var registrationPoint = null;
var deltaPivot = null;
@ -1877,6 +1909,7 @@ SelectionDisplay = (function() {
var pickRayPosition = null;
var pickRayPosition3D = null;
var rotation = null;
var previousPickRay = null;
var onBegin = function(event, pickRay, pickResult) {
var properties = Entities.getEntityProperties(SelectionManager.selections[0]);
@ -1909,7 +1942,7 @@ SelectionDisplay = (function() {
var scaledOffset = Vec3.multiply(0.5, offset);
// Offset from the registration point
offsetRP = Vec3.subtract(scaledOffset, centeredRP);
var offsetRP = Vec3.subtract(scaledOffset, centeredRP);
// Scaled offset in world coordinates
var scaledOffsetWorld = vec3Mult(initialDimensions, offsetRP);
@ -1919,57 +1952,10 @@ SelectionDisplay = (function() {
if (directionFor3DStretch) {
// pivot, offset and pickPlanePosition for 3D manipulation
var scaledPivot3D = Vec3.multiply(0.5, Vec3.multiply(1.0, directionFor3DStretch));
deltaPivot3D = Vec3.subtract(centeredRP, scaledPivot3D);
var scaledOffsetWorld3D = vec3Mult(initialDimensions,
Vec3.subtract(Vec3.multiply(0.5, Vec3.multiply(-1.0, directionFor3DStretch)), centeredRP));
deltaPivot3D = Vec3.subtract(centeredRP, scaledPivot3D);
pickRayPosition3D = Vec3.sum(initialPosition, Vec3.multiplyQbyV(rotation, scaledOffsetWorld));
}
var start = null;
var end = null;
if ((numDimensions === 1) && mask.x) {
start = Vec3.multiplyQbyV(rotation, {
x: -10000,
y: 0,
z: 0
});
start = Vec3.sum(start, properties.position);
end = Vec3.multiplyQbyV(rotation, {
x: 10000,
y: 0,
z: 0
});
end = Vec3.sum(end, properties.position);
}
if ((numDimensions === 1) && mask.y) {
start = Vec3.multiplyQbyV(rotation, {
x: 0,
y: -10000,
z: 0
});
start = Vec3.sum(start, properties.position);
end = Vec3.multiplyQbyV(rotation, {
x: 0,
y: 10000,
z: 0
});
end = Vec3.sum(end, properties.position);
}
if ((numDimensions === 1) && mask.z) {
start = Vec3.multiplyQbyV(rotation, {
x: 0,
y: 0,
z: -10000
});
start = Vec3.sum(start, properties.position);
end = Vec3.multiplyQbyV(rotation, {
x: 0,
y: 0,
z: 10000
});
end = Vec3.sum(end, properties.position);
}
if (numDimensions === 1) {
if (mask.x === 1) {
planeNormal = {
@ -2040,10 +2026,10 @@ SelectionDisplay = (function() {
SelectionManager.saveProperties();
that.resetPreviousHandleColor();
if (stretchPanel != null) {
if (stretchPanel !== null) {
Overlays.editOverlay(stretchPanel, { visible: true });
}
if (scaleHandle != null) {
if (scaleHandle !== null) {
Overlays.editOverlay(scaleHandle, { color: COLOR_SCALE_CUBE_SELECTED });
}
@ -2053,13 +2039,15 @@ SelectionDisplay = (function() {
Entities.editEntity(SelectionManager.selections[0], {collidesWith: newCollidesWith});
that.replaceCollisionsAfterStretch = true;
}
previousPickRay = pickRay;
};
var onEnd = function(event, reason) {
if (stretchPanel != null) {
if (stretchPanel !== null) {
Overlays.editOverlay(stretchPanel, { visible: false });
}
if (scaleHandle != null) {
if (scaleHandle !== null) {
Overlays.editOverlay(scaleHandle, { color: COLOR_SCALE_CUBE });
}
@ -2075,14 +2063,12 @@ SelectionDisplay = (function() {
var onMove = function(event) {
var proportional = (spaceMode === SPACE_WORLD) || directionEnum === STRETCH_DIRECTION.ALL;
var position, dimensions, rotation;
var position, rotation;
if (spaceMode === SPACE_LOCAL) {
position = SelectionManager.localPosition;
dimensions = SelectionManager.localDimensions;
rotation = SelectionManager.localRotation;
} else {
position = SelectionManager.worldPosition;
dimensions = SelectionManager.worldDimensions;
rotation = SelectionManager.worldRotation;
}
@ -2090,9 +2076,15 @@ SelectionDisplay = (function() {
var localSigns = signs;
var pickRay = generalComputePickRay(event.x, event.y);
// Use previousPickRay if new pickRay will cause resulting rayPlaneIntersection values to wrap around
if (usePreviousPickRay(pickRay.direction, previousPickRay.direction, planeNormal)) {
pickRay = previousPickRay;
}
// Are we using handControllers or Mouse - only relevant for 3D tools
var controllerPose = getControllerWorldLocation(activeHand, true);
var vector = null;
var newPick = null;
if (HMD.isHMDAvailable() && HMD.isHandControllerAvailable() &&
controllerPose.valid && that.triggered && directionFor3DStretch) {
localDeltaPivot = deltaPivot3D;
@ -2143,14 +2135,23 @@ SelectionDisplay = (function() {
}
var minimumDimension = directionEnum === STRETCH_DIRECTION.ALL ? STRETCH_ALL_MINIMUM_DIMENSION :
STRETCH_MINIMUM_DIMENSION;
newDimensions.x = Math.max(newDimensions.x, minimumDimension);
newDimensions.y = Math.max(newDimensions.y, minimumDimension);
newDimensions.z = Math.max(newDimensions.z, minimumDimension);
STRETCH_MINIMUM_DIMENSION;
if (newDimensions.x < minimumDimension) {
newDimensions.x = minimumDimension;
changeInDimensions.x = minimumDimension - initialDimensions.x;
}
if (newDimensions.y < minimumDimension) {
newDimensions.y = minimumDimension;
changeInDimensions.y = minimumDimension - initialDimensions.y;
}
if (newDimensions.z < minimumDimension) {
newDimensions.z = minimumDimension;
changeInDimensions.z = minimumDimension - initialDimensions.z;
}
var changeInPosition = Vec3.multiplyQbyV(rotation, vec3Mult(localDeltaPivot, changeInDimensions));
if (directionEnum === STRETCH_DIRECTION.ALL) {
changeInPosition = { x:0, y:0, z:0 };
changeInPosition = { x: 0, y: 0, z: 0 };
}
var newPosition = Vec3.sum(initialPosition, changeInPosition);
@ -2168,6 +2169,8 @@ SelectionDisplay = (function() {
Vec3.print(" changeInPosition:", changeInPosition);
Vec3.print(" newPosition:", newPosition);
}
previousPickRay = pickRay;
SelectionManager._update();
};// End of onMove def
@ -2184,13 +2187,13 @@ SelectionDisplay = (function() {
var directionVector, offset, stretchPanel;
if (directionEnum === STRETCH_DIRECTION.X) {
stretchPanel = handleStretchXPanel;
directionVector = { x:-1, y:0, z:0 };
directionVector = { x: -1, y: 0, z: 0 };
} else if (directionEnum === STRETCH_DIRECTION.Y) {
stretchPanel = handleStretchYPanel;
directionVector = { x:0, y:-1, z:0 };
directionVector = { x: 0, y: -1, z: 0 };
} else if (directionEnum === STRETCH_DIRECTION.Z) {
stretchPanel = handleStretchZPanel
directionVector = { x:0, y:0, z:-1 };
stretchPanel = handleStretchZPanel;
directionVector = { x: 0, y: 0, z: -1 };
}
offset = Vec3.multiply(directionVector, NEGATE_VECTOR);
var tool = makeStretchTool(mode, directionEnum, directionVector, directionVector, offset, stretchPanel, null);
@ -2201,28 +2204,28 @@ SelectionDisplay = (function() {
function addHandleScaleTool(overlay, mode, directionEnum) {
var directionVector, offset, selectedHandle;
if (directionEnum === SCALE_DIRECTION.LBN) {
directionVector = { x:1, y:1, z:1 };
directionVector = { x: 1, y: 1, z: 1 };
selectedHandle = handleScaleLBNCube;
} else if (directionEnum === SCALE_DIRECTION.RBN) {
directionVector = { x:-1, y:1, z:1 };
directionVector = { x: -1, y: 1, z: 1 };
selectedHandle = handleScaleRBNCube;
} else if (directionEnum === SCALE_DIRECTION.LBF) {
directionVector = { x:1, y:1, z:-1 };
directionVector = { x: 1, y: 1, z: -1 };
selectedHandle = handleScaleLBFCube;
} else if (directionEnum === SCALE_DIRECTION.RBF) {
directionVector = { x:-1, y:1, z:-1 };
directionVector = { x: -1, y: 1, z: -1 };
selectedHandle = handleScaleRBFCube;
} else if (directionEnum === SCALE_DIRECTION.LTN) {
directionVector = { x:1, y:-1, z:1 };
directionVector = { x: 1, y: -1, z: 1 };
selectedHandle = handleScaleLTNCube;
} else if (directionEnum === SCALE_DIRECTION.RTN) {
directionVector = { x:-1, y:-1, z:1 };
directionVector = { x: -1, y: -1, z: 1 };
selectedHandle = handleScaleRTNCube;
} else if (directionEnum === SCALE_DIRECTION.LTF) {
directionVector = { x:1, y:-1, z:-1 };
directionVector = { x: 1, y: -1, z: -1 };
selectedHandle = handleScaleLTFCube;
} else if (directionEnum === SCALE_DIRECTION.RTF) {
directionVector = { x:-1, y:-1, z:-1 };
directionVector = { x: -1, y: -1, z: -1 };
selectedHandle = handleScaleRTFCube;
}
offset = Vec3.multiply(directionVector, NEGATE_VECTOR);
@ -2233,7 +2236,6 @@ SelectionDisplay = (function() {
// FUNCTION: UPDATE ROTATION DEGREES OVERLAY
function updateRotationDegreesOverlay(angleFromZero, position) {
var angle = angleFromZero * (Math.PI / 180);
var toCameraDistance = getDistanceToCamera(position);
var overlayProps = {
position: position,
@ -2424,9 +2426,10 @@ SelectionDisplay = (function() {
var startAtCurrent = 0;
var endAtCurrent = angleFromZero;
var maxDegrees = 360;
if (angleFromZero < 0) {
startAtCurrent = 360 + angleFromZero;
endAtCurrent = 360;
startAtCurrent = maxDegrees + angleFromZero;
endAtCurrent = maxDegrees;
}
Overlays.editOverlay(handleRotateCurrentRing, {
startAt: startAtCurrent,
@ -2438,8 +2441,9 @@ SelectionDisplay = (function() {
if (spaceMode === SPACE_LOCAL) {
Overlays.editOverlay(handleRotateCurrentRing, { rotation: worldRotationZ });
} else {
var rotationDegrees = 90;
Overlays.editOverlay(handleRotateCurrentRing, {
rotation: Quat.fromPitchYawRollDegrees(-90, 0, 0)
rotation: Quat.fromPitchYawRollDegrees(-rotationDegrees, 0, 0)
});
}
}

View file

@ -15,13 +15,8 @@
// Computes SSIM - see https://en.wikipedia.org/wiki/Structural_similarity
// The value is computed for the luminance component and the average value is returned
double ImageComparer::compareImages(QImage resultImage, QImage expectedImage) const {
// Make sure the image is 8 bits per colour
QImage::Format format = expectedImage.format();
if (format != QImage::Format::Format_ARGB32) {
throw -1;
}
const int L = 255; // (2^number of bits per pixel) - 1
const double K1 { 0.01 };
const double K2 { 0.03 };
const double c1 = pow((K1 * L), 2);

View file

@ -25,6 +25,11 @@ extern AutoTester* autoTester;
Test::Test() {
mismatchWindow.setModal(true);
if (autoTester) {
autoTester->setUserText("highfidelity");
autoTester->setBranchText("master");
}
}
bool Test::createTestResultsFolderPath(const QString& directory) {
@ -63,7 +68,6 @@ bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar)
// Loop over both lists and compare each pair of images
// Quit loop if user has aborted due to a failed test.
const double THRESHOLD { 0.9995 };
bool success{ true };
bool keepOn{ true };
for (int i = 0; keepOn && i < expectedImagesFullFilenames.length(); ++i) {
@ -71,17 +75,14 @@ bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar)
QImage resultImage(resultImagesFullFilenames[i]);
QImage expectedImage(expectedImagesFullFilenames[i]);
double similarityIndex; // in [-1.0 .. 1.0], where 1.0 means images are identical
// similarityIndex is set to -100.0 to indicate images are not the same size
if (isInteractiveMode && (resultImage.width() != expectedImage.width() || resultImage.height() != expectedImage.height())) {
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Images are not the same size");
exit(-1);
}
double similarityIndex; // in [-1.0 .. 1.0], where 1.0 means images are identical
try {
similarityIndex = -100.0;
} else {
similarityIndex = imageComparer.compareImages(resultImage, expectedImage);
} catch (...) {
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Image not in expected format");
exit(-1);
}
if (similarityIndex < THRESHOLD) {
@ -176,20 +177,25 @@ void Test::appendTestResultsToFile(const QString& testResultsFolderPath, TestFai
comparisonImage.save(failureFolderPath + "/" + "Difference Image.png");
}
void Test::startTestsEvaluation(const QString& testFolder) {
// Get list of JPEG images in folder, sorted by name
QString previousSelection = snapshotDirectory;
QString parent = previousSelection.left(previousSelection.lastIndexOf('/'));
if (!parent.isNull() && parent.right(1) != "/") {
parent += "/";
}
snapshotDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", parent,
QFileDialog::ShowDirsOnly);
void Test::startTestsEvaluation(const QString& testFolder, const QString& branchFromCommandLine, const QString& userFromCommandLine) {
if (testFolder.isNull()) {
// Get list of JPEG images in folder, sorted by name
QString previousSelection = snapshotDirectory;
QString parent = previousSelection.left(previousSelection.lastIndexOf('/'));
if (!parent.isNull() && parent.right(1) != "/") {
parent += "/";
}
snapshotDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", parent,
QFileDialog::ShowDirsOnly);
// If user cancelled then restore previous selection and return
if (snapshotDirectory == "") {
snapshotDirectory = previousSelection;
return;
// If user cancelled then restore previous selection and return
if (snapshotDirectory == "") {
snapshotDirectory = previousSelection;
return;
}
} else {
snapshotDirectory = testFolder;
exitWhenComplete = true;
}
// Quit if test results folder could not be created
@ -197,17 +203,6 @@ void Test::startTestsEvaluation(const QString& testFolder) {
return;
}
// Before any processing - all images are converted to PNGs, as this is the format stored on GitHub
QStringList sortedSnapshotFilenames = createListOfAll_imagesInDirectory("jpg", snapshotDirectory);
foreach(QString filename, sortedSnapshotFilenames) {
QString filenameWithoutExtension = filename.left(filename.length() - 4);
copyJPGtoPNG(snapshotDirectory + "/" + filenameWithoutExtension + ".jpg",
snapshotDirectory + "/" + filenameWithoutExtension + ".png"
);
QFile::remove(snapshotDirectory + "/" + filenameWithoutExtension + ".jpg");
}
// Create two lists. The first is the test results, the second is the expected images
// The expected images are represented as a URL to enable download from GitHub
// Images that are in the wrong format are ignored.
@ -219,6 +214,9 @@ void Test::startTestsEvaluation(const QString& testFolder) {
expectedImagesFilenames.clear();
expectedImagesFullFilenames.clear();
QString branch = (branchFromCommandLine.isNull()) ? autoTester->getSelectedBranch() : branchFromCommandLine;
QString user = (userFromCommandLine.isNull()) ? autoTester->getSelectedUser() : userFromCommandLine;
foreach(QString currentFilename, sortedTestResultsFilenames) {
QString fullCurrentFilename = snapshotDirectory + "/" + currentFilename;
if (isInSnapshotFilenameFormat("png", currentFilename)) {
@ -231,7 +229,7 @@ void Test::startTestsEvaluation(const QString& testFolder) {
QString expectedImageFilenameTail = currentFilename.left(currentFilename.length() - 4).right(NUM_DIGITS);
QString expectedImageStoredFilename = EXPECTED_IMAGE_PREFIX + expectedImageFilenameTail + ".png";
QString imageURLString("https://raw.githubusercontent.com/" + GIT_HUB_USER + "/hifi_tests/" + GIT_HUB_BRANCH + "/" +
QString imageURLString("https://raw.githubusercontent.com/" + user + "/" + GIT_HUB_REPOSITORY + "/" + branch + "/" +
expectedImagePartialSourceDirectory + "/" + expectedImageStoredFilename);
expectedImagesURLs << imageURLString;
@ -259,6 +257,10 @@ void Test::finishTestsEvaluation(bool isRunningFromCommandline, bool interactive
}
zipAndDeleteTestResultsFolder();
if (exitWhenComplete) {
exit(0);
}
}
bool Test::isAValidDirectory(const QString& pathname) {
@ -299,10 +301,9 @@ QString Test::extractPathFromTestsDown(const QString& fullPath) {
void Test::includeTest(QTextStream& textStream, const QString& testPathname) {
QString partialPath = extractPathFromTestsDown(testPathname);
textStream << "Script.include(\""
<< "https://github.com/" << GIT_HUB_USER << "/hifi_tests/blob/" << GIT_HUB_BRANCH
<< partialPath + "?raw=true\");"
<< endl;
QString partialPathWithoutTests = partialPath.right(partialPath.length() - 7);
textStream << "Script.include(testsRootPath + \"" << partialPathWithoutTests + "\");" << endl;
}
// Creates a single script in a user-selected folder.
@ -397,12 +398,20 @@ void Test::createRecursiveScript(const QString& topLevelDirectory, bool interact
const QString DATE_TIME_FORMAT("MMM d yyyy, h:mm");
textStream << "// This is an automatically generated file, created by auto-tester on " << QDateTime::currentDateTime().toString(DATE_TIME_FORMAT) << endl << endl;
textStream << "user = \"" + GIT_HUB_USER + "/\";" << endl;
textStream << "repository = \"" + GIT_HUB_REPOSITORY + "/\";" << endl;
textStream << "branch = \"" + GIT_HUB_BRANCH + "/\";" << endl << endl;
// Include 'autoTest.js'
QString branch = autoTester->getSelectedBranch();
QString user = autoTester->getSelectedUser();
textStream << "var autoTester = Script.require(\"https://github.com/" + GIT_HUB_USER + "/hifi_tests/blob/"
+ GIT_HUB_BRANCH + "/tests/utils/autoTester.js?raw=true\");" << endl << endl;
textStream << "PATH_TO_THE_REPO_PATH_UTILS_FILE = \"https://raw.githubusercontent.com/" + user + "/hifi_tests/" + branch + "/tests/utils/branchUtils.js\";" << endl;
textStream << "Script.include(PATH_TO_THE_REPO_PATH_UTILS_FILE);" << endl;
textStream << "var autoTester = createAutoTester(Script.resolvePath(\".\"));" << endl << endl;
textStream << "var testsRootPath = autoTester.getTestsRootPath();" << endl << endl;
// Wait 10 seconds before starting
textStream << "if (typeof Test !== 'undefined') {" << endl;
textStream << " Test.wait(10000);" << endl;
textStream << "};" << endl << endl;
textStream << "autoTester.enableRecursive();" << endl;
textStream << "autoTester.enableAuto();" << endl << endl;
@ -498,13 +507,13 @@ void Test::createTests() {
return;
}
QStringList sortedImageFilenames = createListOfAll_imagesInDirectory("jpg", snapshotDirectory);
QStringList sortedImageFilenames = createListOfAll_imagesInDirectory("png", snapshotDirectory);
int i = 1;
const int maxImages = pow(10, NUM_DIGITS);
foreach (QString currentFilename, sortedImageFilenames) {
QString fullCurrentFilename = snapshotDirectory + "/" + currentFilename;
if (isInSnapshotFilenameFormat("jpg", currentFilename)) {
if (isInSnapshotFilenameFormat("png", currentFilename)) {
if (i >= maxImages) {
QMessageBox::critical(0, "Error", "More than " + QString::number(maxImages) + " images not supported");
exit(-1);
@ -528,9 +537,12 @@ void Test::createTests() {
fullNewFileName += "/" + newFilename;
try {
copyJPGtoPNG(fullCurrentFilename, fullNewFileName);
if (QFile::exists(fullNewFileName)) {
QFile::remove(fullNewFileName);
}
QFile::copy(fullCurrentFilename, fullNewFileName);
} catch (...) {
QMessageBox::critical(0, "Error", "Could not delete existing file: " + currentFilename + "\nTest creation aborted");
QMessageBox::critical(0, "Error", "Could not copy file: " + fullCurrentFilename + " to " + fullNewFileName + "\n");
exit(-1);
}
++i;
@ -560,9 +572,7 @@ ExtractedText Test::getTestScriptLines(QString testFileName) {
const QString ws("\\h*"); //white-space character
const QString functionPerformName(ws + "autoTester" + ws + "\\." + ws + "perform");
const QString quotedString("\\\".+\\\"");
const QString ownPath("Script" + ws + "\\." + ws + "resolvePath" + ws + "\\(" + ws + "\\\"\\.\\\"" + ws + "\\)");
const QString functionParameter("function" + ws + "\\(testType" + ws + "\\)");
QString regexTestTitle(ws + functionPerformName + "\\(" + quotedString + "\\," + ws + ownPath + "\\," + ws + functionParameter + ws + "{" + ".*");
QString regexTestTitle(ws + functionPerformName + "\\(" + quotedString);
QRegularExpression lineContainingTitle = QRegularExpression(regexTestTitle);
@ -811,19 +821,6 @@ void Test::createTestsOutline() {
QMessageBox::information(0, "Success", "Test outline file " + testsOutlineFilename + " has been created");
}
void Test::copyJPGtoPNG(const QString& sourceJPGFullFilename, const QString& destinationPNGFullFilename) {
QFile::remove(destinationPNGFullFilename);
QImageReader reader;
reader.setFileName(sourceJPGFullFilename);
QImage image = reader.read();
QImageWriter writer;
writer.setFileName(destinationPNGFullFilename);
writer.write(image);
}
QStringList Test::createListOfAll_imagesInDirectory(const QString& imageFormat, const QString& pathToImageDirectory) {
imageDirectory = QDir(pathToImageDirectory);
QStringList nameFilters;

View file

@ -37,7 +37,7 @@ class Test {
public:
Test();
void startTestsEvaluation(const QString& testFolder = QString());
void startTestsEvaluation(const QString& testFolder = QString(), const QString& branchFromCommandLine = QString(), const QString& userFromCommandLine = QString());
void finishTestsEvaluation(bool isRunningFromCommandline, bool interactiveMode, QProgressBar* progressBar);
void createRecursiveScript();
@ -69,13 +69,13 @@ public:
QString getExpectedImageDestinationDirectory(const QString& filename);
QString getExpectedImagePartialSourceDirectory(const QString& filename);
void copyJPGtoPNG(const QString& sourceJPGFullFilename, const QString& destinationPNGFullFilename);
private:
const QString TEST_FILENAME { "test.js" };
const QString TEST_RESULTS_FOLDER { "TestResults" };
const QString TEST_RESULTS_FILENAME { "TestResults.txt" };
const double THRESHOLD{ 0.96 };
QDir imageDirectory;
MismatchWindow mismatchWindow;
@ -102,9 +102,7 @@ private:
QStringList resultImagesFullFilenames;
// Used for accessing GitHub
const QString GIT_HUB_USER{ "highfidelity" };
const QString GIT_HUB_REPOSITORY{ "hifi_tests" };
const QString GIT_HUB_BRANCH{ "master" };
const QString DATETIME_FORMAT{ "yyyy-MM-dd_hh-mm-ss" };
@ -115,6 +113,8 @@ private:
// var pathSeparator = ".";
const QString ADVANCE_KEY{ "n" };
const QString PATH_SEPARATOR{ "." };
bool exitWhenComplete{ false };
};
#endif // hifi_test_h

View file

@ -10,23 +10,63 @@
#include <QtWidgets/QApplication>
#include "ui/AutoTester.h"
#include <iostream>
AutoTester* autoTester;
int main(int argc, char *argv[]) {
// Only parameter is "--testFolder"
// If no parameters then run in interactive mode
// Parameter --testFolder <folder containing the test images>
// Parameter --branch <branch on GitHub>
// default is "master"
// Parameter --user <GitHub user>
// default is "highfidelity"
// Parameter --repository <repository on GitHub>
// default is "highfidelity"
QString testFolder;
if (argc == 3) {
if (QString(argv[1]) == "--testFolder") {
testFolder = QString(argv[2]);
QString branch{ "master" };
QString user{ "highfidelity" };
for (int i = 1; i < argc - 1; ++i) {
if (QString(argv[i]) == "--testFolder") {
++i;
if (i < argc) {
testFolder = QString(argv[i]);
} else {
std::cout << "Missing parameter after --testFolder" << std::endl;
exit(-1);
}
} else if (QString(argv[i]) == "--branch") {
++i;
if (i < argc) {
branch = QString(argv[i]);
} else {
std::cout << "Missing parameter after --branch" << std::endl;
exit(-1);
}
} else if (QString(argv[i]) == "--user") {
++i;
if (i < argc) {
user = QString(argv[i]);
} else {
std::cout << "Missing parameter after --user" << std::endl;
exit(-1);
}
} else {
std::cout << "Unknown parameter" << std::endl;
exit(-1);
}
}
QApplication application(argc, argv);
autoTester = new AutoTester();
autoTester->setup();
if (!testFolder.isNull()) {
autoTester->runFromCommandLine(testFolder);
autoTester->runFromCommandLine(testFolder, branch, user);
} else {
autoTester->show();
}

View file

@ -29,13 +29,15 @@ AutoTester::AutoTester(QWidget *parent) : QMainWindow(parent) {
ui.hideTaskbarButton->setVisible(false);
ui.showTaskbarButton->setVisible(false);
#endif
}
void AutoTester::setup() {
test = new Test();
}
void AutoTester::runFromCommandLine(const QString& testFolder) {
void AutoTester::runFromCommandLine(const QString& testFolder, const QString& branch, const QString& user) {
isRunningFromCommandline = true;
test->startTestsEvaluation(testFolder);
test->startTestsEvaluation(testFolder, branch, user);
}
void AutoTester::on_evaluateTestsButton_clicked() {
@ -116,6 +118,7 @@ void AutoTester::downloadImages(const QStringList& URLs, const QString& director
ui.progressBar->setValue(0);
ui.progressBar->setVisible(true);
downloaders.clear();
for (int i = 0; i < _numberOfImagesToDownload; ++i) {
QUrl imageURL(URLs[i]);
downloadImage(imageURL);
@ -125,14 +128,12 @@ void AutoTester::downloadImages(const QStringList& URLs, const QString& director
}
void AutoTester::saveImage(int index) {
QPixmap pixmap;
pixmap.loadFromData(downloaders[index]->downloadedData());
QImage image = pixmap.toImage();
image = image.convertToFormat(QImage::Format_ARGB32);
QString fullPathname = _directoryName + "/" + _filenames[index];
if (!image.save(fullPathname, 0, 100)) {
try {
QFile file(_directoryName + "/" + _filenames[index]);
file.open(QIODevice::WriteOnly);
file.write(downloaders[index]->downloadedData());
file.close();
} catch (...) {
QMessageBox::information(0, "Test Aborted", "Failed to save image: " + _filenames[index]);
ui.progressBar->setVisible(false);
return;
@ -141,6 +142,7 @@ void AutoTester::saveImage(int index) {
++_numberOfImagesDownloaded;
if (_numberOfImagesDownloaded == _numberOfImagesToDownload) {
disconnect(signalMapper, SIGNAL (mapped(int)), this, SLOT (saveImage(int)));
test->finishTestsEvaluation(isRunningFromCommandline, ui.checkBoxInteractiveMode->isChecked(), ui.progressBar);
} else {
ui.progressBar->setValue(_numberOfImagesDownloaded);
@ -150,3 +152,20 @@ void AutoTester::saveImage(int index) {
void AutoTester::about() {
QMessageBox::information(0, "About", QString("Built ") + __DATE__ + " : " + __TIME__);
}
void AutoTester::setUserText(const QString& user) {
ui.userTextEdit->setText(user);
}
QString AutoTester::getSelectedUser()
{
return ui.userTextEdit->toPlainText();
}
void AutoTester::setBranchText(const QString& branch) {
ui.branchTextEdit->setText(branch);
}
QString AutoTester::getSelectedBranch() {
return ui.branchTextEdit->toPlainText();
}

View file

@ -12,6 +12,7 @@
#include <QtWidgets/QMainWindow>
#include <QSignalMapper>
#include <QTextEdit>
#include "ui_AutoTester.h"
#include "../Downloader.h"
@ -23,11 +24,19 @@ class AutoTester : public QMainWindow {
public:
AutoTester(QWidget *parent = Q_NULLPTR);
void runFromCommandLine(const QString& testFolder);
void setup();
void runFromCommandLine(const QString& testFolder, const QString& branch, const QString& user);
void downloadImage(const QUrl& url);
void downloadImages(const QStringList& URLs, const QString& directoryName, const QStringList& filenames);
void setUserText(const QString& user);
QString getSelectedUser();
void setBranchText(const QString& branch);
QString getSelectedBranch();
private slots:
void on_evaluateTestsButton_clicked();
void on_createRecursiveScriptButton_clicked();

View file

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>607</width>
<height>514</height>
<width>612</width>
<height>537</height>
</rect>
</property>
<property name="windowTitle">
@ -17,9 +17,9 @@
<widget class="QPushButton" name="closeButton">
<property name="geometry">
<rect>
<x>360</x>
<y>400</y>
<width>220</width>
<x>380</x>
<y>430</y>
<width>101</width>
<height>40</height>
</rect>
</property>
@ -43,9 +43,9 @@
<widget class="QPushButton" name="evaluateTestsButton">
<property name="geometry">
<rect>
<x>20</x>
<y>285</y>
<width>220</width>
<x>430</x>
<y>270</y>
<width>101</width>
<height>40</height>
</rect>
</property>
@ -56,8 +56,8 @@
<widget class="QPushButton" name="createRecursiveScriptButton">
<property name="geometry">
<rect>
<x>360</x>
<y>35</y>
<x>330</x>
<y>110</y>
<width>220</width>
<height>40</height>
</rect>
@ -69,8 +69,8 @@
<widget class="QCheckBox" name="checkBoxInteractiveMode">
<property name="geometry">
<rect>
<x>23</x>
<y>250</y>
<x>320</x>
<y>280</y>
<width>131</width>
<height>20</height>
</rect>
@ -85,8 +85,8 @@
<widget class="QProgressBar" name="progressBar">
<property name="geometry">
<rect>
<x>20</x>
<y>340</y>
<x>320</x>
<y>330</y>
<width>255</width>
<height>23</height>
</rect>
@ -98,8 +98,8 @@
<widget class="QPushButton" name="createAllRecursiveScriptsButton">
<property name="geometry">
<rect>
<x>360</x>
<y>100</y>
<x>330</x>
<y>170</y>
<width>220</width>
<height>40</height>
</rect>
@ -112,7 +112,7 @@
<property name="geometry">
<rect>
<x>20</x>
<y>80</y>
<y>110</y>
<width>220</width>
<height>40</height>
</rect>
@ -125,7 +125,7 @@
<property name="geometry">
<rect>
<x>20</x>
<y>130</y>
<y>160</y>
<width>220</width>
<height>40</height>
</rect>
@ -138,7 +138,7 @@
<property name="geometry">
<rect>
<x>20</x>
<y>180</y>
<y>250</y>
<width>220</width>
<height>40</height>
</rect>
@ -150,27 +150,83 @@
<widget class="QPushButton" name="showTaskbarButton">
<property name="geometry">
<rect>
<x>490</x>
<y>280</y>
<width>91</width>
<x>10</x>
<y>440</y>
<width>211</width>
<height>40</height>
</rect>
</property>
<property name="text">
<string>Show Taskbar</string>
<string>Show Windows Taskbar</string>
</property>
</widget>
<widget class="QPushButton" name="hideTaskbarButton">
<property name="geometry">
<rect>
<x>490</x>
<y>230</y>
<width>91</width>
<x>10</x>
<y>390</y>
<width>211</width>
<height>40</height>
</rect>
</property>
<property name="text">
<string>Hide Taskbar</string>
<string>Hide Windows Taskbar</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>330</x>
<y>55</y>
<width>81</width>
<height>16</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>GitHub Branch</string>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>330</x>
<y>15</y>
<width>81</width>
<height>16</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>GitHub User</string>
</property>
</widget>
<widget class="QTextEdit" name="userTextEdit">
<property name="geometry">
<rect>
<x>420</x>
<y>12</y>
<width>140</width>
<height>24</height>
</rect>
</property>
</widget>
<widget class="QTextEdit" name="branchTextEdit">
<property name="geometry">
<rect>
<x>420</x>
<y>50</y>
<width>140</width>
<height>24</height>
</rect>
</property>
</widget>
</widget>
@ -179,7 +235,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>607</width>
<width>612</width>
<height>21</height>
</rect>
</property>