Merge branch 'master' into 21319

This commit is contained in:
David Rowe 2017-05-09 08:04:27 +12:00
commit c8328e9b9f
71 changed files with 733 additions and 373 deletions

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 576 576" style="enable-background:new 0 0 576 576;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M359.2,249.6c16.7,43.9,33.3,87.9,50,131.8c1.3,3.4,2.5,6.7-0.2,9.9c-2.8,3.3-6.3,3-10.1,2.2
c-42.4-8.8-84.8-17.5-127.2-26.4c-5.6-1.2-10.7-1-16,1.4c-5,2.2-10.3,3.9-15.4,5.9c-8.2,3.2-9.5,7.6-4.1,14.4
c22.2,28.7,44.4,57.3,66.6,86c1.2,1.6,2.5,3.1,3.5,4.8c2.3,4.1,1.3,7.8-2.8,10.2c-1.6,0.9-3.3,1.6-5,2.2
c-15.6,5.9-31.2,11.8-46.7,17.7c-8.7,3.3-10.2,2.9-15.7-4.2c-24-31-48-62.1-72-93.2c-4.8-6.3-6.5-6.7-14-3.9c-3,1.1-5.9,2.3-8.9,3.3
c-10.9,3.5-20.5-1.1-24.6-11.7c-13-34.1-25.9-68.2-38.8-102.4c-4.5-11.7-0.2-21.5,11.6-26.2c9.8-3.9,19.6-7.5,29.4-11.2
c28-10.6,56-21.4,84.2-31.8c5.4-2,9.4-5.1,12.8-9.7c25.7-34.6,51.6-69.1,77.3-103.7c2.3-3.1,4.7-5.8,8.9-5.3c4.4,0.5,5.7,4,7,7.5
C325.7,161.4,342.4,205.5,359.2,249.6z"/>
<path class="st0" d="M348.2,141.2c-2.2,0-4.5-0.6-6.5-1.8c-5.9-3.6-7.8-11.3-4.2-17.2l39.3-64.3c3.6-5.9,11.3-7.8,17.2-4.2
c5.9,3.6,7.8,11.3,4.2,17.2l-39.3,64.3C356.5,139.1,352.4,141.2,348.2,141.2z"/>
<path class="st0" d="M504.6,363c-0.9,0-1.8-0.1-2.7-0.3L424,345.6c-6.7-1.5-11-8.1-9.5-14.9c1.5-6.7,8.1-11,14.9-9.5l77.8,17.1
c6.7,1.5,11,8.1,9.5,14.9C515.5,359,510.3,363,504.6,363z"/>
<path class="st0" d="M393.2,237.7c-4.7,0-9.2-2.7-11.3-7.2c-2.9-6.3-0.2-13.7,6.1-16.6l78.4-36.4c6.3-2.9,13.7-0.2,16.6,6.1
c2.9,6.3,0.2,13.7-6.1,16.6l-78.4,36.4C396.7,237.3,394.9,237.7,393.2,237.7z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -167,11 +167,9 @@ Item {
Rectangle { Rectangle {
id: lozenge; id: lozenge;
visible: isAnnouncement; visible: isAnnouncement;
color: hifi.colors.redHighlight; color: lozengeHot.containsMouse ? hifi.colors.redAccent : hifi.colors.redHighlight;
anchors.fill: infoRow; anchors.fill: infoRow;
radius: lozenge.height / 2.0; radius: lozenge.height / 2.0;
border.width: lozengeHot.containsMouse ? 4 : 0;
border.color: "white";
} }
Row { Row {
id: infoRow; id: infoRow;

View file

@ -23,7 +23,8 @@ Item {
property var callbackFunction; property var callbackFunction;
property int dialogWidth; property int dialogWidth;
property int dialogHeight; property int dialogHeight;
property int comboOptionTextSize: 18; property int comboOptionTextSize: 16;
property int comboBodyTextSize: 16;
FontLoader { id: ralewayRegular; source: "../../fonts/Raleway-Regular.ttf"; } FontLoader { id: ralewayRegular; source: "../../fonts/Raleway-Regular.ttf"; }
FontLoader { id: ralewaySemiBold; source: "../../fonts/Raleway-SemiBold.ttf"; } FontLoader { id: ralewaySemiBold; source: "../../fonts/Raleway-SemiBold.ttf"; }
visible: false; visible: false;
@ -63,7 +64,7 @@ Item {
anchors.left: parent.left; anchors.left: parent.left;
anchors.leftMargin: 20; anchors.leftMargin: 20;
size: 24; size: 24;
color: 'black'; color: hifi.colors.darkGray;
horizontalAlignment: Text.AlignLeft; horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignTop; verticalAlignment: Text.AlignTop;
} }
@ -141,6 +142,7 @@ Item {
height: 30; height: 30;
size: comboOptionTextSize; size: comboOptionTextSize;
wrapMode: Text.WordWrap; wrapMode: Text.WordWrap;
color: hifi.colors.darkGray;
} }
RalewayRegular { RalewayRegular {
@ -148,11 +150,12 @@ Item {
text: bodyText; text: bodyText;
anchors.top: optionTitle.bottom; anchors.top: optionTitle.bottom;
anchors.left: comboOptionSelected.right; anchors.left: comboOptionSelected.right;
anchors.leftMargin: 25; anchors.leftMargin: 10;
anchors.right: parent.right; anchors.right: parent.right;
anchors.rightMargin: 10; anchors.rightMargin: 10;
size: comboOptionTextSize; size: comboBodyTextSize;
wrapMode: Text.WordWrap; wrapMode: Text.WordWrap;
color: hifi.colors.darkGray;
} }
MouseArea { MouseArea {

View file

@ -1013,10 +1013,10 @@ Rectangle {
onClicked: { onClicked: {
popupComboDialog("Set your availability:", popupComboDialog("Set your availability:",
availabilityComboBox.availabilityStrings, availabilityComboBox.availabilityStrings,
["Your username will be visible in everyone's 'Nearby' list.\nAnyone will be able to jump to your location from within the 'Nearby' list.", ["Your username will be visible in everyone's 'Nearby' list. Anyone will be able to jump to your location from within the 'Nearby' list.",
"Your location will be visible in the 'Connections' list only for those with whom you are connected or friends.\nThey will be able to jump to your location if the domain allows.", "Your location will be visible in the 'Connections' list only for those with whom you are connected or friends. They'll be able to jump to your location if the domain allows.",
"Your location will be visible in the 'Connections' list only for those with whom you are friends.\nThey will be able to jump to your location if the domain allows.", "Your location will be visible in the 'Connections' list only for those with whom you are friends. They'll be able to jump to your location if the domain allows. You will only receive 'Happening Now' notifications in 'Go To' from friends.",
"Your location will not be visible in the 'Connections' list of any other users. Only domain admins will be able to see your username in the 'Nearby' list."], "You will appear offline in the 'Connections' list, and you will not receive 'Happening Now' notifications in 'Go To'."],
["all", "connections", "friends", "none"]); ["all", "connections", "friends", "none"]);
} }
onEntered: availabilityComboBox.color = hifi.colors.lightGrayText; onEntered: availabilityComboBox.color = hifi.colors.lightGrayText;

View file

@ -34,9 +34,6 @@ ScrollingWindow {
property var runningScriptsModel: ListModel { } property var runningScriptsModel: ListModel { }
property bool isHMD: false property bool isHMD: false
onVisibleChanged: console.log("Running scripts visible changed to " + visible)
onShownChanged: console.log("Running scripts visible changed to " + visible)
Settings { Settings {
category: "Overlay.RunningScripts" category: "Overlay.RunningScripts"
property alias x: root.x property alias x: root.x

View file

@ -5461,7 +5461,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("Test", TestScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("Test", TestScriptingInterface::getInstance());
} }
scriptEngine->registerGlobalObject("Overlays", &_overlays);
scriptEngine->registerGlobalObject("Rates", new RatesScriptingInterface(this)); scriptEngine->registerGlobalObject("Rates", new RatesScriptingInterface(this));
// hook our avatar and avatar hash map object into this script engine // hook our avatar and avatar hash map object into this script engine
@ -5560,6 +5559,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
auto entityScriptServerLog = DependencyManager::get<EntityScriptServerLogClient>(); auto entityScriptServerLog = DependencyManager::get<EntityScriptServerLogClient>();
scriptEngine->registerGlobalObject("EntityScriptServerLog", entityScriptServerLog.data()); scriptEngine->registerGlobalObject("EntityScriptServerLog", entityScriptServerLog.data());
scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance());
qScriptRegisterMetaType(scriptEngine, OverlayIDtoScriptValue, OverlayIDfromScriptValue); qScriptRegisterMetaType(scriptEngine, OverlayIDtoScriptValue, OverlayIDfromScriptValue);

View file

@ -140,9 +140,6 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
auto orientation = myAvatar->getLocalOrientation(); auto orientation = myAvatar->getLocalOrientation();
_rig->computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState); _rig->computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState);
// evaluate AnimGraph animation and update jointStates.
Model::updateRig(deltaTime, parentTransform);
Rig::EyeParameters eyeParams; Rig::EyeParameters eyeParams;
eyeParams.eyeLookAt = lookAt; eyeParams.eyeLookAt = lookAt;
eyeParams.eyeSaccade = head->getSaccade(); eyeParams.eyeSaccade = head->getSaccade();
@ -153,6 +150,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
_rig->updateFromEyeParameters(eyeParams); _rig->updateFromEyeParameters(eyeParams);
// evaluate AnimGraph animation and update jointStates.
Parent::updateRig(deltaTime, parentTransform); Parent::updateRig(deltaTime, parentTransform);
} }

View file

@ -34,7 +34,7 @@ class AvatarInputs : public QQuickItem {
public: public:
static AvatarInputs* getInstance(); static AvatarInputs* getInstance();
float loudnessToAudioLevel(float loudness); Q_INVOKABLE float loudnessToAudioLevel(float loudness);
AvatarInputs(QQuickItem* parent = nullptr); AvatarInputs(QQuickItem* parent = nullptr);
void update(); void update();
bool showAudioTools() const { return _showAudioTools; } bool showAudioTools() const { return _showAudioTools; }

View file

@ -28,11 +28,15 @@ const int MAX_HISTORY_SIZE = 64;
const QString COMMAND_STYLE = "color: #266a9b;"; const QString COMMAND_STYLE = "color: #266a9b;";
const QString RESULT_SUCCESS_STYLE = "color: #677373;"; const QString RESULT_SUCCESS_STYLE = "color: #677373;";
const QString RESULT_INFO_STYLE = "color: #223bd1;";
const QString RESULT_WARNING_STYLE = "color: #d13b22;";
const QString RESULT_ERROR_STYLE = "color: #d13b22;"; const QString RESULT_ERROR_STYLE = "color: #d13b22;";
const QString GUTTER_PREVIOUS_COMMAND = "<span style=\"color: #57b8bb;\">&lt;</span>"; const QString GUTTER_PREVIOUS_COMMAND = "<span style=\"color: #57b8bb;\">&lt;</span>";
const QString GUTTER_ERROR = "<span style=\"color: #d13b22;\">X</span>"; const QString GUTTER_ERROR = "<span style=\"color: #d13b22;\">X</span>";
const QString JSConsole::_consoleFileName { "about:console" };
JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) : JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) :
QWidget(parent), QWidget(parent),
_ui(new Ui::Console), _ui(new Ui::Console),
@ -77,6 +81,8 @@ void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) {
} }
if (_scriptEngine != NULL) { if (_scriptEngine != NULL) {
disconnect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); disconnect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint);
disconnect(_scriptEngine, &ScriptEngine::infoMessage, this, &JSConsole::handleInfo);
disconnect(_scriptEngine, &ScriptEngine::warningMessage, this, &JSConsole::handleWarning);
disconnect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError); disconnect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError);
if (_ownScriptEngine) { if (_ownScriptEngine) {
_scriptEngine->deleteLater(); _scriptEngine->deleteLater();
@ -84,10 +90,12 @@ void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) {
} }
// if scriptEngine is NULL then create one and keep track of it using _ownScriptEngine // if scriptEngine is NULL then create one and keep track of it using _ownScriptEngine
_ownScriptEngine = scriptEngine == NULL; _ownScriptEngine = (scriptEngine == NULL);
_scriptEngine = _ownScriptEngine ? DependencyManager::get<ScriptEngines>()->loadScript(QString(), false) : scriptEngine; _scriptEngine = _ownScriptEngine ? DependencyManager::get<ScriptEngines>()->loadScript(_consoleFileName, false) : scriptEngine;
connect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); connect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint);
connect(_scriptEngine, &ScriptEngine::infoMessage, this, &JSConsole::handleInfo);
connect(_scriptEngine, &ScriptEngine::warningMessage, this, &JSConsole::handleWarning);
connect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError); connect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError);
} }
@ -107,11 +115,10 @@ void JSConsole::executeCommand(const QString& command) {
QScriptValue JSConsole::executeCommandInWatcher(const QString& command) { QScriptValue JSConsole::executeCommandInWatcher(const QString& command) {
QScriptValue result; QScriptValue result;
static const QString filename = "JSConcole";
QMetaObject::invokeMethod(_scriptEngine, "evaluate", Qt::ConnectionType::BlockingQueuedConnection, QMetaObject::invokeMethod(_scriptEngine, "evaluate", Qt::ConnectionType::BlockingQueuedConnection,
Q_RETURN_ARG(QScriptValue, result), Q_RETURN_ARG(QScriptValue, result),
Q_ARG(const QString&, command), Q_ARG(const QString&, command),
Q_ARG(const QString&, filename)); Q_ARG(const QString&, _consoleFileName));
return result; return result;
} }
@ -134,16 +141,26 @@ void JSConsole::commandFinished() {
resetCurrentCommandHistory(); resetCurrentCommandHistory();
} }
void JSConsole::handleError(const QString& scriptName, const QString& message) { void JSConsole::handleError(const QString& message, const QString& scriptName) {
Q_UNUSED(scriptName); Q_UNUSED(scriptName);
appendMessage(GUTTER_ERROR, "<span style='" + RESULT_ERROR_STYLE + "'>" + message.toHtmlEscaped() + "</span>"); appendMessage(GUTTER_ERROR, "<span style='" + RESULT_ERROR_STYLE + "'>" + message.toHtmlEscaped() + "</span>");
} }
void JSConsole::handlePrint(const QString& scriptName, const QString& message) { void JSConsole::handlePrint(const QString& message, const QString& scriptName) {
Q_UNUSED(scriptName); Q_UNUSED(scriptName);
appendMessage("", message); appendMessage("", message);
} }
void JSConsole::handleInfo(const QString& message, const QString& scriptName) {
Q_UNUSED(scriptName);
appendMessage("", "<span style='" + RESULT_INFO_STYLE + "'>" + message.toHtmlEscaped() + "</span>");
}
void JSConsole::handleWarning(const QString& message, const QString& scriptName) {
Q_UNUSED(scriptName);
appendMessage("", "<span style='" + RESULT_WARNING_STYLE + "'>" + message.toHtmlEscaped() + "</span>");
}
void JSConsole::mouseReleaseEvent(QMouseEvent* event) { void JSConsole::mouseReleaseEvent(QMouseEvent* event) {
_ui->promptTextEdit->setFocus(); _ui->promptTextEdit->setFocus();
} }

View file

@ -47,8 +47,10 @@ protected:
protected slots: protected slots:
void scrollToBottom(); void scrollToBottom();
void resizeTextInput(); void resizeTextInput();
void handlePrint(const QString& scriptName, const QString& message); void handlePrint(const QString& message, const QString& scriptName);
void handleError(const QString& scriptName, const QString& message); void handleInfo(const QString& message, const QString& scriptName);
void handleWarning(const QString& message, const QString& scriptName);
void handleError(const QString& message, const QString& scriptName);
void commandFinished(); void commandFinished();
private: private:
@ -66,6 +68,7 @@ private:
bool _ownScriptEngine; bool _ownScriptEngine;
QString _rootCommand; QString _rootCommand;
ScriptEngine* _scriptEngine; ScriptEngine* _scriptEngine;
static const QString _consoleFileName;
}; };

View file

@ -330,6 +330,30 @@ void setupPreferences() {
preferences->addPreference(preference); preferences->addPreference(preference);
} }
} }
{
auto getter = []()->bool { return image::isColorTexturesCompressionEnabled(); };
auto setter = [](bool value) { return image::setColorTexturesCompressionEnabled(value); };
auto preference = new CheckPreference(RENDER, "Compress Color Textures", getter, setter);
preferences->addPreference(preference);
}
{
auto getter = []()->bool { return image::isNormalTexturesCompressionEnabled(); };
auto setter = [](bool value) { return image::setNormalTexturesCompressionEnabled(value); };
auto preference = new CheckPreference(RENDER, "Compress Normal Textures", getter, setter);
preferences->addPreference(preference);
}
{
auto getter = []()->bool { return image::isGrayscaleTexturesCompressionEnabled(); };
auto setter = [](bool value) { return image::setGrayscaleTexturesCompressionEnabled(value); };
auto preference = new CheckPreference(RENDER, "Compress Grayscale Textures", getter, setter);
preferences->addPreference(preference);
}
{
auto getter = []()->bool { return image::isCubeTexturesCompressionEnabled(); };
auto setter = [](bool value) { return image::setCubeTexturesCompressionEnabled(value); };
auto preference = new CheckPreference(RENDER, "Compress Cube Textures", getter, setter);
preferences->addPreference(preference);
}
} }
{ {
static const QString RENDER("Networking"); static const QString RENDER("Networking");

View file

@ -81,6 +81,10 @@ QVariantMap convertOverlayLocationFromScriptSemantics(const QVariantMap& propert
void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
QVariantMap properties = originalProperties; QVariantMap properties = originalProperties;
if (properties["name"].isValid()) {
setName(properties["name"].toString());
}
// carry over some legacy keys // carry over some legacy keys
if (!properties["position"].isValid() && !properties["localPosition"].isValid()) { if (!properties["position"].isValid() && !properties["localPosition"].isValid()) {
if (properties["p1"].isValid()) { if (properties["p1"].isValid()) {
@ -207,6 +211,9 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
} }
QVariant Base3DOverlay::getProperty(const QString& property) { QVariant Base3DOverlay::getProperty(const QString& property) {
if (property == "name") {
return _name;
}
if (property == "position" || property == "start" || property == "p1" || property == "point") { if (property == "position" || property == "start" || property == "p1" || property == "point") {
return vec3toVariant(getPosition()); return vec3toVariant(getPosition());
} }

View file

@ -26,6 +26,9 @@ public:
virtual OverlayID getOverlayID() const override { return OverlayID(getID().toString()); } virtual OverlayID getOverlayID() const override { return OverlayID(getID().toString()); }
void setOverlayID(OverlayID overlayID) override { setID(overlayID); } void setOverlayID(OverlayID overlayID) override { setID(overlayID); }
virtual QString getName() const override { return QString("Overlay:") + _name; }
void setName(QString name) { _name = name; }
// getters // getters
virtual bool is3D() const override { return true; } virtual bool is3D() const override { return true; }
@ -74,6 +77,8 @@ protected:
bool _drawInFront; bool _drawInFront;
bool _isAA; bool _isAA;
bool _isGrabbable { false }; bool _isGrabbable { false };
QString _name;
}; };
#endif // hifi_Base3DOverlay_h #endif // hifi_Base3DOverlay_h

View file

@ -288,3 +288,10 @@ void ModelOverlay::locationChanged(bool tellPhysics) {
_model->setTranslation(getPosition()); _model->setTranslation(getPosition());
} }
} }
QString ModelOverlay::getName() const {
if (_name != "") {
return QString("Overlay:") + getType() + ":" + _name;
}
return QString("Overlay:") + getType() + ":" + _url.toString();
}

View file

@ -22,6 +22,8 @@ public:
static QString const TYPE; static QString const TYPE;
virtual QString getType() const override { return TYPE; } virtual QString getType() const override { return TYPE; }
virtual QString getName() const override;
ModelOverlay(); ModelOverlay();
ModelOverlay(const ModelOverlay* modelOverlay); ModelOverlay(const ModelOverlay* modelOverlay);

View file

@ -421,6 +421,13 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) {
return; return;
} }
//do not send secondary button events to tablet
if (event.getButton() == PointerEvent::SecondaryButton ||
//do not block composed events
event.getButtons() == PointerEvent::SecondaryButton) {
return;
}
QTouchEvent::TouchPoint point; QTouchEvent::TouchPoint point;
point.setId(event.getID()); point.setId(event.getID());
point.setState(touchPointState); point.setState(touchPointState);

View file

@ -1097,28 +1097,27 @@ void AudioClient::handleRecordedAudioInput(const QByteArray& audio) {
} }
void AudioClient::prepareLocalAudioInjectors() { void AudioClient::prepareLocalAudioInjectors() {
if (_outputPeriod == 0) {
return;
}
int bufferCapacity = _localInjectorsStream.getSampleCapacity();
if (_localToOutputResampler) {
// avoid overwriting the buffer,
// instead of failing on writes because the buffer is used as a lock-free pipe
bufferCapacity -=
_localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) *
AudioConstants::STEREO;
bufferCapacity += 1;
}
int samplesNeeded = std::numeric_limits<int>::max(); int samplesNeeded = std::numeric_limits<int>::max();
while (samplesNeeded > 0) { while (samplesNeeded > 0) {
// lock for every write to avoid locking out the device callback // unlock between every write to allow device switching
// this lock is intentional - the buffer is only lock-free in its use in the device callback Lock lock(_localAudioMutex);
RecursiveLock lock(_localAudioMutex);
// in case of a device switch, consider bufferCapacity volatile across iterations
if (_outputPeriod == 0) {
return;
}
int bufferCapacity = _localInjectorsStream.getSampleCapacity();
int maxOutputSamples = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * AudioConstants::STEREO;
if (_localToOutputResampler) {
maxOutputSamples =
_localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) *
AudioConstants::STEREO;
}
samplesNeeded = bufferCapacity - _localSamplesAvailable.load(std::memory_order_relaxed); samplesNeeded = bufferCapacity - _localSamplesAvailable.load(std::memory_order_relaxed);
if (samplesNeeded <= 0) { if (samplesNeeded < maxOutputSamples) {
// avoid overwriting the buffer to prevent losing frames
break; break;
} }
@ -1168,16 +1167,18 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) {
memset(mixBuffer, 0, AudioConstants::NETWORK_FRAME_SAMPLES_STEREO * sizeof(float)); memset(mixBuffer, 0, AudioConstants::NETWORK_FRAME_SAMPLES_STEREO * sizeof(float));
for (AudioInjector* injector : _activeLocalAudioInjectors) { for (AudioInjector* injector : _activeLocalAudioInjectors) {
if (injector->getLocalBuffer()) { // the lock guarantees that injectorBuffer, if found, is invariant
AudioInjectorLocalBuffer* injectorBuffer = injector->getLocalBuffer();
if (injectorBuffer) {
static const int HRTF_DATASET_INDEX = 1; static const int HRTF_DATASET_INDEX = 1;
int numChannels = injector->isAmbisonic() ? AudioConstants::AMBISONIC : (injector->isStereo() ? AudioConstants::STEREO : AudioConstants::MONO); int numChannels = injector->isAmbisonic() ? AudioConstants::AMBISONIC : (injector->isStereo() ? AudioConstants::STEREO : AudioConstants::MONO);
qint64 bytesToRead = numChannels * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; size_t bytesToRead = numChannels * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL;
// get one frame from the injector // get one frame from the injector
memset(_localScratchBuffer, 0, bytesToRead); memset(_localScratchBuffer, 0, bytesToRead);
if (0 < injector->getLocalBuffer()->readData((char*)_localScratchBuffer, bytesToRead)) { if (0 < injectorBuffer->readData((char*)_localScratchBuffer, bytesToRead)) {
if (injector->isAmbisonic()) { if (injector->isAmbisonic()) {
@ -1317,15 +1318,17 @@ void AudioClient::setIsStereoInput(bool isStereoInput) {
} }
bool AudioClient::outputLocalInjector(AudioInjector* injector) { bool AudioClient::outputLocalInjector(AudioInjector* injector) {
Lock lock(_injectorsMutex); AudioInjectorLocalBuffer* injectorBuffer = injector->getLocalBuffer();
if (injector->getLocalBuffer() && _audioInput ) { if (injectorBuffer) {
// just add it to the vector of active local injectors, if // local injectors are on the AudioInjectorsThread, so we must guard access
// not already there. Lock lock(_injectorsMutex);
// Since this is invoked with invokeMethod, there _should_ be
// no reason to lock access to the vector of injectors.
if (!_activeLocalAudioInjectors.contains(injector)) { if (!_activeLocalAudioInjectors.contains(injector)) {
qCDebug(audioclient) << "adding new injector"; qCDebug(audioclient) << "adding new injector";
_activeLocalAudioInjectors.append(injector); _activeLocalAudioInjectors.append(injector);
// move local buffer to the LocalAudioThread to avoid dataraces with AudioInjector (like stop())
injectorBuffer->setParent(nullptr);
injectorBuffer->moveToThread(&_localAudioThread);
} else { } else {
qCDebug(audioclient) << "injector exists in active list already"; qCDebug(audioclient) << "injector exists in active list already";
} }
@ -1333,7 +1336,7 @@ bool AudioClient::outputLocalInjector(AudioInjector* injector) {
return true; return true;
} else { } else {
// no local buffer or audio // no local buffer
return false; return false;
} }
} }
@ -1452,7 +1455,7 @@ void AudioClient::outputNotify() {
bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo) { bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo) {
bool supportedFormat = false; bool supportedFormat = false;
RecursiveLock lock(_localAudioMutex); Lock lock(_localAudioMutex);
_localSamplesAvailable.exchange(0, std::memory_order_release); _localSamplesAvailable.exchange(0, std::memory_order_release);
// cleanup any previously initialized device // cleanup any previously initialized device
@ -1681,8 +1684,12 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
int injectorSamplesPopped = 0; int injectorSamplesPopped = 0;
{ {
RecursiveLock lock(_audio->_localAudioMutex);
bool append = networkSamplesPopped > 0; bool append = networkSamplesPopped > 0;
// this does not require a lock as of the only two functions adding to _localSamplesAvailable (samples count):
// - prepareLocalAudioInjectors will only increase samples count
// - switchOutputToAudioDevice will zero samples count
// stop the device, so that readData will exhaust the existing buffer or see a zeroed samples count
// and start the device, which can only see a zeroed samples count
samplesRequested = std::min(samplesRequested, _audio->_localSamplesAvailable.load(std::memory_order_acquire)); samplesRequested = std::min(samplesRequested, _audio->_localSamplesAvailable.load(std::memory_order_acquire));
if ((injectorSamplesPopped = _localInjectorsStream.appendSamples(mixBuffer, samplesRequested, append)) > 0) { if ((injectorSamplesPopped = _localInjectorsStream.appendSamples(mixBuffer, samplesRequested, append)) > 0) {
_audio->_localSamplesAvailable.fetch_sub(injectorSamplesPopped, std::memory_order_release); _audio->_localSamplesAvailable.fetch_sub(injectorSamplesPopped, std::memory_order_release);

View file

@ -96,8 +96,6 @@ public:
using AudioPositionGetter = std::function<glm::vec3()>; using AudioPositionGetter = std::function<glm::vec3()>;
using AudioOrientationGetter = std::function<glm::quat()>; using AudioOrientationGetter = std::function<glm::quat()>;
using RecursiveMutex = std::recursive_mutex;
using RecursiveLock = std::unique_lock<RecursiveMutex>;
using Mutex = std::mutex; using Mutex = std::mutex;
using Lock = std::unique_lock<Mutex>; using Lock = std::unique_lock<Mutex>;
@ -345,7 +343,7 @@ private:
int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC]; int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC];
float* _localOutputMixBuffer { NULL }; float* _localOutputMixBuffer { NULL };
AudioInjectorsThread _localAudioThread; AudioInjectorsThread _localAudioThread;
RecursiveMutex _localAudioMutex; Mutex _localAudioMutex;
// for output audio (used by this thread) // for output audio (used by this thread)
int _outputPeriod { 0 }; int _outputPeriod { 0 };

View file

@ -33,7 +33,11 @@ public:
PacketType packetType, QString codecName = QString("")); PacketType packetType, QString codecName = QString(""));
public slots: public slots:
// threadsafe
// moves injector->getLocalBuffer() to another thread (so removes its parent)
// take care to delete it when ~AudioInjector, as parenting Qt semantics will not work
virtual bool outputLocalInjector(AudioInjector* injector) = 0; virtual bool outputLocalInjector(AudioInjector* injector) = 0;
virtual bool shouldLoopbackInjectors() { return false; } virtual bool shouldLoopbackInjectors() { return false; }
virtual void setIsStereoInput(bool stereo) = 0; virtual void setIsStereoInput(bool stereo) = 0;

View file

@ -51,6 +51,10 @@ AudioInjector::AudioInjector(const QByteArray& audioData, const AudioInjectorOpt
{ {
} }
AudioInjector::~AudioInjector() {
deleteLocalBuffer();
}
bool AudioInjector::stateHas(AudioInjectorState state) const { bool AudioInjector::stateHas(AudioInjectorState state) const {
return (_state & state) == state; return (_state & state) == state;
} }
@ -87,11 +91,7 @@ void AudioInjector::finish() {
emit finished(); emit finished();
if (_localBuffer) { deleteLocalBuffer();
_localBuffer->stop();
_localBuffer->deleteLater();
_localBuffer = NULL;
}
if (stateHas(AudioInjectorState::PendingDelete)) { if (stateHas(AudioInjectorState::PendingDelete)) {
// we've been asked to delete after finishing, trigger a deleteLater here // we've been asked to delete after finishing, trigger a deleteLater here
@ -163,7 +163,7 @@ bool AudioInjector::injectLocally() {
if (_localAudioInterface) { if (_localAudioInterface) {
if (_audioData.size() > 0) { if (_audioData.size() > 0) {
_localBuffer = new AudioInjectorLocalBuffer(_audioData, this); _localBuffer = new AudioInjectorLocalBuffer(_audioData);
_localBuffer->open(QIODevice::ReadOnly); _localBuffer->open(QIODevice::ReadOnly);
_localBuffer->setShouldLoop(_options.loop); _localBuffer->setShouldLoop(_options.loop);
@ -172,7 +172,8 @@ bool AudioInjector::injectLocally() {
_localBuffer->setCurrentOffset(_currentSendOffset); _localBuffer->setCurrentOffset(_currentSendOffset);
// call this function on the AudioClient's thread // call this function on the AudioClient's thread
success = QMetaObject::invokeMethod(_localAudioInterface, "outputLocalInjector", Q_ARG(AudioInjector*, this)); // this will move the local buffer's thread to the LocalInjectorThread
success = _localAudioInterface->outputLocalInjector(this);
if (!success) { if (!success) {
qCDebug(audio) << "AudioInjector::injectLocally could not output locally via _localAudioInterface"; qCDebug(audio) << "AudioInjector::injectLocally could not output locally via _localAudioInterface";
@ -185,6 +186,14 @@ bool AudioInjector::injectLocally() {
return success; return success;
} }
void AudioInjector::deleteLocalBuffer() {
if (_localBuffer) {
_localBuffer->stop();
_localBuffer->deleteLater();
_localBuffer = nullptr;
}
}
const uchar MAX_INJECTOR_VOLUME = packFloatGainToByte(1.0f); const uchar MAX_INJECTOR_VOLUME = packFloatGainToByte(1.0f);
static const int64_t NEXT_FRAME_DELTA_ERROR_OR_FINISHED = -1; static const int64_t NEXT_FRAME_DELTA_ERROR_OR_FINISHED = -1;
static const int64_t NEXT_FRAME_DELTA_IMMEDIATELY = 0; static const int64_t NEXT_FRAME_DELTA_IMMEDIATELY = 0;

View file

@ -52,6 +52,7 @@ class AudioInjector : public QObject {
public: public:
AudioInjector(const Sound& sound, const AudioInjectorOptions& injectorOptions); AudioInjector(const Sound& sound, const AudioInjectorOptions& injectorOptions);
AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions); AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions);
~AudioInjector();
bool isFinished() const { return (stateHas(AudioInjectorState::Finished)); } bool isFinished() const { return (stateHas(AudioInjectorState::Finished)); }
@ -99,6 +100,7 @@ private:
int64_t injectNextFrame(); int64_t injectNextFrame();
bool inject(bool(AudioInjectorManager::*injection)(AudioInjector*)); bool inject(bool(AudioInjectorManager::*injection)(AudioInjector*));
bool injectLocally(); bool injectLocally();
void deleteLocalBuffer();
static AbstractAudioInterface* _localAudioInterface; static AbstractAudioInterface* _localAudioInterface;

View file

@ -11,8 +11,7 @@
#include "AudioInjectorLocalBuffer.h" #include "AudioInjectorLocalBuffer.h"
AudioInjectorLocalBuffer::AudioInjectorLocalBuffer(const QByteArray& rawAudioArray, QObject* parent) : AudioInjectorLocalBuffer::AudioInjectorLocalBuffer(const QByteArray& rawAudioArray) :
QIODevice(parent),
_rawAudioArray(rawAudioArray), _rawAudioArray(rawAudioArray),
_shouldLoop(false), _shouldLoop(false),
_isStopped(false), _isStopped(false),

View file

@ -19,7 +19,7 @@
class AudioInjectorLocalBuffer : public QIODevice { class AudioInjectorLocalBuffer : public QIODevice {
Q_OBJECT Q_OBJECT
public: public:
AudioInjectorLocalBuffer(const QByteArray& rawAudioArray, QObject* parent); AudioInjectorLocalBuffer(const QByteArray& rawAudioArray);
void stop(); void stop();

View file

@ -120,7 +120,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
} }
// evaluate AnimGraph animation and update jointStates. // evaluate AnimGraph animation and update jointStates.
Model::updateRig(deltaTime, parentTransform); Parent::updateRig(deltaTime, parentTransform);
} }
void SkeletonModel::updateAttitude() { void SkeletonModel::updateAttitude() {
@ -136,7 +136,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
if (fullUpdate) { if (fullUpdate) {
setBlendshapeCoefficients(_owningAvatar->getHead()->getSummedBlendshapeCoefficients()); setBlendshapeCoefficients(_owningAvatar->getHead()->getSummedBlendshapeCoefficients());
Model::simulate(deltaTime, fullUpdate); Parent::simulate(deltaTime, fullUpdate);
// let rig compute the model offset // let rig compute the model offset
glm::vec3 registrationPoint; glm::vec3 registrationPoint;
@ -144,7 +144,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
setOffset(registrationPoint); setOffset(registrationPoint);
} }
} else { } else {
Model::simulate(deltaTime, fullUpdate); Parent::simulate(deltaTime, fullUpdate);
} }
if (!isActive() || !_owningAvatar->isMyAvatar()) { if (!isActive() || !_owningAvatar->isMyAvatar()) {

View file

@ -23,6 +23,7 @@ using SkeletonModelWeakPointer = std::weak_ptr<SkeletonModel>;
/// A skeleton loaded from a model. /// A skeleton loaded from a model.
class SkeletonModel : public CauterizedModel { class SkeletonModel : public CauterizedModel {
using Parent = CauterizedModel;
Q_OBJECT Q_OBJECT
public: public:

View file

@ -357,6 +357,8 @@ class AvatarData : public QObject, public SpatiallyNestable {
public: public:
virtual QString getName() const override { return QString("Avatar:") + _displayName; }
static const QString FRAME_NAME; static const QString FRAME_NAME;
static void fromFrame(const QByteArray& frameData, AvatarData& avatar, bool useFrameSkeleton = true); static void fromFrame(const QByteArray& frameData, AvatarData& avatar, bool useFrameSkeleton = true);

View file

@ -281,7 +281,7 @@ public:
float getAngularDamping() const; float getAngularDamping() const;
void setAngularDamping(float value); void setAngularDamping(float value);
QString getName() const; virtual QString getName() const override;
void setName(const QString& value); void setName(const QString& value);
QString getDebugName(); QString getDebugName();

View file

@ -407,9 +407,11 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
// return QUuid(); // return QUuid();
// } // }
bool entityFound { false };
_entityTree->withReadLock([&] { _entityTree->withReadLock([&] {
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
if (entity) { if (entity) {
entityFound = true;
// make sure the properties has a type, so that the encode can know which properties to include // make sure the properties has a type, so that the encode can know which properties to include
properties.setType(entity->getType()); properties.setType(entity->getType());
bool hasTerseUpdateChanges = properties.hasTerseUpdateChanges(); bool hasTerseUpdateChanges = properties.hasTerseUpdateChanges();
@ -464,6 +466,27 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
}); });
} }
}); });
if (!entityFound) {
// we've made an edit to an entity we don't know about, or to a non-entity. If it's a known non-entity,
// print a warning and don't send an edit packet to the entity-server.
QSharedPointer<SpatialParentFinder> parentFinder = DependencyManager::get<SpatialParentFinder>();
if (parentFinder) {
bool success;
auto nestableWP = parentFinder->find(id, success, static_cast<SpatialParentTree*>(_entityTree.get()));
if (success) {
auto nestable = nestableWP.lock();
if (nestable) {
NestableType nestableType = nestable->getNestableType();
if (nestableType == NestableType::Overlay || nestableType == NestableType::Avatar) {
qCWarning(entities) << "attempted edit on non-entity: " << id << nestable->getName();
return QUuid(); // null UUID to indicate failure
}
}
}
}
}
// we queue edit packets even if we don't know about the entity. This is to allow AC agents
// to edit entities they know only by ID.
queueEntityMessage(PacketType::EntityEdit, entityID, properties); queueEntityMessage(PacketType::EntityEdit, entityID, properties);
return id; return id;
} }
@ -1515,6 +1538,24 @@ bool EntityScriptingInterface::isChildOfParent(QUuid childID, QUuid parentID) {
return isChild; return isChild;
} }
QString EntityScriptingInterface::getNestableType(QUuid id) {
QSharedPointer<SpatialParentFinder> parentFinder = DependencyManager::get<SpatialParentFinder>();
if (!parentFinder) {
return "unknown";
}
bool success;
SpatiallyNestableWeakPointer objectWP = parentFinder->find(id, success);
if (!success) {
return "unknown";
}
SpatiallyNestablePointer object = objectWP.lock();
if (!object) {
return "unknown";
}
NestableType nestableType = object->getNestableType();
return SpatiallyNestable::nestableTypeToString(nestableType);
}
QVector<QUuid> EntityScriptingInterface::getChildrenIDsOfJoint(const QUuid& parentID, int jointIndex) { QVector<QUuid> EntityScriptingInterface::getChildrenIDsOfJoint(const QUuid& parentID, int jointIndex) {
QVector<QUuid> result; QVector<QUuid> result;
if (!_entityTree) { if (!_entityTree) {

View file

@ -304,6 +304,8 @@ public slots:
Q_INVOKABLE QVector<QUuid> getChildrenIDsOfJoint(const QUuid& parentID, int jointIndex); Q_INVOKABLE QVector<QUuid> getChildrenIDsOfJoint(const QUuid& parentID, int jointIndex);
Q_INVOKABLE bool isChildOfParent(QUuid childID, QUuid parentID); Q_INVOKABLE bool isChildOfParent(QUuid childID, QUuid parentID);
Q_INVOKABLE QString getNestableType(QUuid id);
Q_INVOKABLE QUuid getKeyboardFocusEntity() const; Q_INVOKABLE QUuid getKeyboardFocusEntity() const;
Q_INVOKABLE void setKeyboardFocusEntity(QUuid id); Q_INVOKABLE void setKeyboardFocusEntity(QUuid id);

View file

@ -990,6 +990,17 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
entityItemID, properties); entityItemID, properties);
endDecode = usecTimestampNow(); endDecode = usecTimestampNow();
EntityItemPointer existingEntity;
if (!isAdd) {
// search for the entity by EntityItemID
startLookup = usecTimestampNow();
existingEntity = findEntityByEntityItemID(entityItemID);
endLookup = usecTimestampNow();
if (!existingEntity) {
// this is not an add-entity operation, and we don't know about the identified entity.
validEditPacket = false;
}
}
if (validEditPacket && !_entityScriptSourceWhitelist.isEmpty() && !properties.getScript().isEmpty()) { if (validEditPacket && !_entityScriptSourceWhitelist.isEmpty() && !properties.getScript().isEmpty()) {
bool passedWhiteList = false; bool passedWhiteList = false;
@ -1036,12 +1047,6 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
// If we got a valid edit packet, then it could be a new entity or it could be an update to // If we got a valid edit packet, then it could be a new entity or it could be an update to
// an existing entity... handle appropriately // an existing entity... handle appropriately
if (validEditPacket) { if (validEditPacket) {
// search for the entity by EntityItemID
startLookup = usecTimestampNow();
EntityItemPointer existingEntity = findEntityByEntityItemID(entityItemID);
endLookup = usecTimestampNow();
startFilter = usecTimestampNow(); startFilter = usecTimestampNow();
bool wasChanged = false; bool wasChanged = false;
// Having (un)lock rights bypasses the filter, unless it's a physics result. // Having (un)lock rights bypasses the filter, unless it's a physics result.

View file

@ -149,6 +149,10 @@ void GLBackend::resetUniformStage() {
void GLBackend::do_setUniformBuffer(const Batch& batch, size_t paramOffset) { void GLBackend::do_setUniformBuffer(const Batch& batch, size_t paramOffset) {
GLuint slot = batch._params[paramOffset + 3]._uint; GLuint slot = batch._params[paramOffset + 3]._uint;
if (slot >(GLuint)MAX_NUM_UNIFORM_BUFFERS) {
qCDebug(gpugllogging) << "GLBackend::do_setUniformBuffer: Trying to set a uniform Buffer at slot #" << slot << " which doesn't exist. MaxNumUniformBuffers = " << getMaxNumUniformBuffers();
return;
}
BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint);
GLintptr rangeStart = batch._params[paramOffset + 1]._uint; GLintptr rangeStart = batch._params[paramOffset + 1]._uint;
GLsizeiptr rangeSize = batch._params[paramOffset + 0]._uint; GLsizeiptr rangeSize = batch._params[paramOffset + 0]._uint;
@ -203,7 +207,7 @@ void GLBackend::resetResourceStage() {
void GLBackend::do_setResourceBuffer(const Batch& batch, size_t paramOffset) { void GLBackend::do_setResourceBuffer(const Batch& batch, size_t paramOffset) {
GLuint slot = batch._params[paramOffset + 1]._uint; GLuint slot = batch._params[paramOffset + 1]._uint;
if (slot >= (GLuint)MAX_NUM_RESOURCE_BUFFERS) { if (slot >= (GLuint)MAX_NUM_RESOURCE_BUFFERS) {
// "GLBackend::do_setResourceBuffer: Trying to set a resource Buffer at slot #" + slot + " which doesn't exist. MaxNumResourceBuffers = " + getMaxNumResourceBuffers()); qCDebug(gpugllogging) << "GLBackend::do_setResourceBuffer: Trying to set a resource Buffer at slot #" << slot << " which doesn't exist. MaxNumResourceBuffers = " << getMaxNumResourceBuffers();
return; return;
} }
@ -233,7 +237,7 @@ void GLBackend::do_setResourceBuffer(const Batch& batch, size_t paramOffset) {
void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) {
GLuint slot = batch._params[paramOffset + 1]._uint; GLuint slot = batch._params[paramOffset + 1]._uint;
if (slot >= (GLuint) MAX_NUM_RESOURCE_TEXTURES) { if (slot >= (GLuint) MAX_NUM_RESOURCE_TEXTURES) {
// "GLBackend::do_setResourceTexture: Trying to set a resource Texture at slot #" + slot + " which doesn't exist. MaxNumResourceTextures = " + getMaxNumResourceTextures()); qCDebug(gpugllogging) << "GLBackend::do_setResourceTexture: Trying to set a resource Texture at slot #" << slot << " which doesn't exist. MaxNumResourceTextures = " << getMaxNumResourceTextures();
return; return;
} }

View file

@ -140,6 +140,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
switch (dstFormat.getSemantic()) { switch (dstFormat.getSemantic()) {
case gpu::RGB: case gpu::RGB:
case gpu::RGBA: case gpu::RGBA:
case gpu::XY:
result = GL_RG8; result = GL_RG8;
break; break;
default: default:
@ -289,6 +290,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
switch (dstFormat.getSemantic()) { switch (dstFormat.getSemantic()) {
case gpu::RGB: case gpu::RGB:
case gpu::RGBA: case gpu::RGBA:
case gpu::XY:
texel.internalFormat = GL_RG8; texel.internalFormat = GL_RG8;
break; break;
default: default:
@ -516,6 +518,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
switch (dstFormat.getSemantic()) { switch (dstFormat.getSemantic()) {
case gpu::RGB: case gpu::RGB:
case gpu::RGBA: case gpu::RGBA:
case gpu::XY:
texel.internalFormat = GL_RG8; texel.internalFormat = GL_RG8;
break; break;
default: default:

View file

@ -25,6 +25,8 @@ const Element Element::COLOR_COMPRESSED_SRGBA_MASK{ VEC4, NUINT8, COMPRESSED_BC1
const Element Element::COLOR_COMPRESSED_SRGBA{ VEC4, NUINT8, COMPRESSED_BC3_SRGBA }; const Element Element::COLOR_COMPRESSED_SRGBA{ VEC4, NUINT8, COMPRESSED_BC3_SRGBA };
const Element Element::COLOR_COMPRESSED_XY{ VEC4, NUINT8, COMPRESSED_BC5_XY }; const Element Element::COLOR_COMPRESSED_XY{ VEC4, NUINT8, COMPRESSED_BC5_XY };
const Element Element::VEC2NU8_XY{ VEC2, NUINT8, XY };
const Element Element::COLOR_R11G11B10{ SCALAR, FLOAT, R11G11B10 }; const Element Element::COLOR_R11G11B10{ SCALAR, FLOAT, R11G11B10 };
const Element Element::VEC4F_COLOR_RGBA{ VEC4, FLOAT, RGBA }; const Element Element::VEC4F_COLOR_RGBA{ VEC4, FLOAT, RGBA };
const Element Element::VEC2F_UV{ VEC2, FLOAT, UV }; const Element Element::VEC2F_UV{ VEC2, FLOAT, UV };

View file

@ -234,6 +234,7 @@ public:
static const Element COLOR_COMPRESSED_SRGBA_MASK; static const Element COLOR_COMPRESSED_SRGBA_MASK;
static const Element COLOR_COMPRESSED_SRGBA; static const Element COLOR_COMPRESSED_SRGBA;
static const Element COLOR_COMPRESSED_XY; static const Element COLOR_COMPRESSED_XY;
static const Element VEC2NU8_XY;
static const Element VEC4F_COLOR_RGBA; static const Element VEC4F_COLOR_RGBA;
static const Element VEC2F_UV; static const Element VEC2F_UV;
static const Element VEC2F_XY; static const Element VEC2F_XY;

View file

@ -495,6 +495,8 @@ bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat
header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA);
} else if (texelFormat == Format::COLOR_R_8 && mipFormat == Format::COLOR_R_8) { } else if (texelFormat == Format::COLOR_R_8 && mipFormat == Format::COLOR_R_8) {
header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RED, ktx::GLInternalFormat_Uncompressed::R8, ktx::GLBaseInternalFormat::RED); header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RED, ktx::GLInternalFormat_Uncompressed::R8, ktx::GLBaseInternalFormat::RED);
} else if (texelFormat == Format::VEC2NU8_XY && mipFormat == Format::VEC2NU8_XY) {
header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RG, ktx::GLInternalFormat_Uncompressed::RG8, ktx::GLBaseInternalFormat::RG);
} else if (texelFormat == Format::COLOR_COMPRESSED_SRGB && mipFormat == Format::COLOR_COMPRESSED_SRGB) { } else if (texelFormat == Format::COLOR_COMPRESSED_SRGB && mipFormat == Format::COLOR_COMPRESSED_SRGB) {
header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_S3TC_DXT1_EXT, ktx::GLBaseInternalFormat::RGB); header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_S3TC_DXT1_EXT, ktx::GLBaseInternalFormat::RGB);
} else if (texelFormat == Format::COLOR_COMPRESSED_SRGBA_MASK && mipFormat == Format::COLOR_COMPRESSED_SRGBA_MASK) { } else if (texelFormat == Format::COLOR_COMPRESSED_SRGBA_MASK && mipFormat == Format::COLOR_COMPRESSED_SRGBA_MASK) {

View file

@ -22,16 +22,19 @@
#include <Profile.h> #include <Profile.h>
#include <StatTracker.h> #include <StatTracker.h>
#include <GLMHelpers.h> #include <GLMHelpers.h>
#include <SettingHandle.h>
#include "ImageLogging.h" #include "ImageLogging.h"
using namespace gpu; using namespace gpu;
#define CPU_MIPMAPS 1 #define CPU_MIPMAPS 1
#define COMPRESS_COLOR_TEXTURES 0
#define COMPRESS_NORMALMAP_TEXTURES 0 // Disable Normalmap compression for now static std::mutex settingsMutex;
#define COMPRESS_GRAYSCALE_TEXTURES 0 static Setting::Handle<bool> compressColorTextures("hifi.graphics.compressColorTextures", false);
#define COMPRESS_CUBEMAP_TEXTURES 0 // Disable Cubemap compression for now static Setting::Handle<bool> compressNormalTextures("hifi.graphics.compressNormalTextures", false);
static Setting::Handle<bool> compressGrayscaleTextures("hifi.graphics.compressGrayscaleTextures", false);
static Setting::Handle<bool> compressCubeTextures("hifi.graphics.compressCubeTextures", false);
static const glm::uvec2 SPARSE_PAGE_SIZE(128); static const glm::uvec2 SPARSE_PAGE_SIZE(128);
static const glm::uvec2 MAX_TEXTURE_SIZE(4096); static const glm::uvec2 MAX_TEXTURE_SIZE(4096);
@ -144,6 +147,64 @@ gpu::TexturePointer TextureUsage::createCubeTextureFromImageWithoutIrradiance(co
return processCubeTextureColorFromImage(srcImage, srcImageName, false); return processCubeTextureColorFromImage(srcImage, srcImageName, false);
} }
bool isColorTexturesCompressionEnabled() {
#if CPU_MIPMAPS
std::lock_guard<std::mutex> guard(settingsMutex);
return compressColorTextures.get();
#else
return false;
#endif
}
bool isNormalTexturesCompressionEnabled() {
#if CPU_MIPMAPS
std::lock_guard<std::mutex> guard(settingsMutex);
return compressNormalTextures.get();
#else
return false;
#endif
}
bool isGrayscaleTexturesCompressionEnabled() {
#if CPU_MIPMAPS
std::lock_guard<std::mutex> guard(settingsMutex);
return compressGrayscaleTextures.get();
#else
return false;
#endif
}
bool isCubeTexturesCompressionEnabled() {
#if CPU_MIPMAPS
std::lock_guard<std::mutex> guard(settingsMutex);
return compressCubeTextures.get();
#else
return false;
#endif
}
void setColorTexturesCompressionEnabled(bool enabled) {
std::lock_guard<std::mutex> guard(settingsMutex);
compressColorTextures.set(enabled);
}
void setNormalTexturesCompressionEnabled(bool enabled) {
std::lock_guard<std::mutex> guard(settingsMutex);
compressNormalTextures.set(enabled);
}
void setGrayscaleTexturesCompressionEnabled(bool enabled) {
std::lock_guard<std::mutex> guard(settingsMutex);
compressGrayscaleTextures.set(enabled);
}
void setCubeTexturesCompressionEnabled(bool enabled) {
std::lock_guard<std::mutex> guard(settingsMutex);
compressCubeTextures.set(enabled);
}
gpu::TexturePointer processImage(const QByteArray& content, const std::string& filename, int maxNumPixels, TextureUsage::Type textureType) { gpu::TexturePointer processImage(const QByteArray& content, const std::string& filename, int maxNumPixels, TextureUsage::Type textureType) {
// Help the QImage loader by extracting the image file format from the url filename ext. // Help the QImage loader by extracting the image file format from the url filename ext.
// Some tga are not created properly without it. // Some tga are not created properly without it.
@ -290,6 +351,19 @@ void generateMips(gpu::Texture* texture, QImage& image, int face = -1) {
float inputGamma = 2.2f; float inputGamma = 2.2f;
float outputGamma = 2.2f; float outputGamma = 2.2f;
nvtt::InputOptions inputOptions;
inputOptions.setTextureLayout(textureType, width, height);
inputOptions.setMipmapData(data, width, height);
inputOptions.setFormat(inputFormat);
inputOptions.setGamma(inputGamma, outputGamma);
inputOptions.setAlphaMode(alphaMode);
inputOptions.setWrapMode(wrapMode);
inputOptions.setRoundMode(roundMode);
inputOptions.setMipmapGeneration(true);
inputOptions.setMipmapFilter(nvtt::MipmapFilter_Box);
nvtt::CompressionOptions compressionOptions; nvtt::CompressionOptions compressionOptions;
compressionOptions.setQuality(nvtt::Quality_Production); compressionOptions.setQuality(nvtt::Quality_Production);
@ -346,26 +420,17 @@ void generateMips(gpu::Texture* texture, QImage& image, int face = -1) {
compressionOptions.setFormat(nvtt::Format_RGB); compressionOptions.setFormat(nvtt::Format_RGB);
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
compressionOptions.setPixelFormat(8, 0, 0, 0); compressionOptions.setPixelFormat(8, 0, 0, 0);
} else if (mipFormat == gpu::Element::VEC2NU8_XY) {
inputOptions.setNormalMap(true);
compressionOptions.setFormat(nvtt::Format_RGBA);
compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm);
compressionOptions.setPixelFormat(8, 8, 0, 0);
} else { } else {
qCWarning(imagelogging) << "Unknown mip format"; qCWarning(imagelogging) << "Unknown mip format";
Q_UNREACHABLE(); Q_UNREACHABLE();
return; return;
} }
nvtt::InputOptions inputOptions;
inputOptions.setTextureLayout(textureType, width, height);
inputOptions.setMipmapData(data, width, height);
inputOptions.setFormat(inputFormat);
inputOptions.setGamma(inputGamma, outputGamma);
inputOptions.setAlphaMode(alphaMode);
inputOptions.setWrapMode(wrapMode);
inputOptions.setRoundMode(roundMode);
inputOptions.setMipmapGeneration(true);
inputOptions.setMipmapFilter(nvtt::MipmapFilter_Box);
nvtt::OutputOptions outputOptions; nvtt::OutputOptions outputOptions;
outputOptions.setOutputHeader(false); outputOptions.setOutputHeader(false);
MyOutputHandler outputHandler(texture, face); MyOutputHandler outputHandler(texture, face);
@ -424,18 +489,19 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(const QImage& s
gpu::TexturePointer theTexture = nullptr; gpu::TexturePointer theTexture = nullptr;
if ((image.width() > 0) && (image.height() > 0)) { if ((image.width() > 0) && (image.height() > 0)) {
#if CPU_MIPMAPS && COMPRESS_COLOR_TEXTURES gpu::Element formatMip;
gpu::Element formatGPU; gpu::Element formatGPU;
if (validAlpha) { if (isColorTexturesCompressionEnabled()) {
formatGPU = alphaAsMask ? gpu::Element::COLOR_COMPRESSED_SRGBA_MASK : gpu::Element::COLOR_COMPRESSED_SRGBA; if (validAlpha) {
formatGPU = alphaAsMask ? gpu::Element::COLOR_COMPRESSED_SRGBA_MASK : gpu::Element::COLOR_COMPRESSED_SRGBA;
} else {
formatGPU = gpu::Element::COLOR_COMPRESSED_SRGB;
}
formatMip = formatGPU;
} else { } else {
formatGPU = gpu::Element::COLOR_COMPRESSED_SRGB; formatMip = gpu::Element::COLOR_SBGRA_32;
formatGPU = gpu::Element::COLOR_SRGBA_32;
} }
gpu::Element formatMip = formatGPU;
#else
gpu::Element formatMip = gpu::Element::COLOR_SBGRA_32;
gpu::Element formatGPU = gpu::Element::COLOR_SRGBA_32;
#endif
if (isStrict) { if (isStrict) {
theTexture = gpu::Texture::createStrict(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); theTexture = gpu::Texture::createStrict(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
@ -543,14 +609,12 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(const QImag
gpu::TexturePointer theTexture = nullptr; gpu::TexturePointer theTexture = nullptr;
if ((image.width() > 0) && (image.height() > 0)) { if ((image.width() > 0) && (image.height() > 0)) {
gpu::Element formatMip = gpu::Element::VEC2NU8_XY;
#if CPU_MIPMAPS && COMPRESS_NORMALMAP_TEXTURES gpu::Element formatGPU = gpu::Element::VEC2NU8_XY;
gpu::Element formatMip = gpu::Element::COLOR_COMPRESSED_XY; if (isNormalTexturesCompressionEnabled()) {
gpu::Element formatGPU = gpu::Element::COLOR_COMPRESSED_XY; formatMip = gpu::Element::COLOR_COMPRESSED_XY;
#else formatGPU = gpu::Element::COLOR_COMPRESSED_XY;
gpu::Element formatMip = gpu::Element::COLOR_RGBA_32; }
gpu::Element formatGPU = gpu::Element::COLOR_RGBA_32;
#endif
theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
theTexture->setSource(srcImageName); theTexture->setSource(srcImageName);
@ -576,14 +640,15 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(const QImag
gpu::TexturePointer theTexture = nullptr; gpu::TexturePointer theTexture = nullptr;
if ((image.width() > 0) && (image.height() > 0)) { if ((image.width() > 0) && (image.height() > 0)) {
gpu::Element formatMip;
#if CPU_MIPMAPS && COMPRESS_GRAYSCALE_TEXTURES gpu::Element formatGPU;
gpu::Element formatMip = gpu::Element::COLOR_COMPRESSED_RED; if (isGrayscaleTexturesCompressionEnabled()) {
gpu::Element formatGPU = gpu::Element::COLOR_COMPRESSED_RED; formatMip = gpu::Element::COLOR_COMPRESSED_RED;
#else formatGPU = gpu::Element::COLOR_COMPRESSED_RED;
gpu::Element formatMip = gpu::Element::COLOR_R_8; } else {
gpu::Element formatGPU = gpu::Element::COLOR_R_8; formatMip = gpu::Element::COLOR_R_8;
#endif formatGPU = gpu::Element::COLOR_R_8;
}
theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR));
theTexture->setSource(srcImageName); theTexture->setSource(srcImageName);
@ -860,13 +925,15 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(const QImage&
image = image.convertToFormat(QImage::Format_ARGB32); image = image.convertToFormat(QImage::Format_ARGB32);
} }
#if CPU_MIPMAPS && COMPRESS_CUBEMAP_TEXTURES gpu::Element formatMip;
gpu::Element formatMip = gpu::Element::COLOR_COMPRESSED_SRGBA; gpu::Element formatGPU;
gpu::Element formatGPU = gpu::Element::COLOR_COMPRESSED_SRGBA; if (isCubeTexturesCompressionEnabled()) {
#else formatMip = gpu::Element::COLOR_COMPRESSED_SRGBA;
gpu::Element formatMip = gpu::Element::COLOR_SRGBA_32; formatGPU = gpu::Element::COLOR_COMPRESSED_SRGBA;
gpu::Element formatGPU = gpu::Element::COLOR_SRGBA_32; } else {
#endif formatMip = gpu::Element::COLOR_SRGBA_32;
formatGPU = gpu::Element::COLOR_SRGBA_32;
}
// Find the layout of the cubemap in the 2D image // Find the layout of the cubemap in the 2D image
// Use the original image size since processSourceImage may have altered the size / aspect ratio // Use the original image size since processSourceImage may have altered the size / aspect ratio

View file

@ -63,6 +63,16 @@ gpu::TexturePointer processCubeTextureColorFromImage(const QImage& srcImage, con
} // namespace TextureUsage } // namespace TextureUsage
bool isColorTexturesCompressionEnabled();
bool isNormalTexturesCompressionEnabled();
bool isGrayscaleTexturesCompressionEnabled();
bool isCubeTexturesCompressionEnabled();
void setColorTexturesCompressionEnabled(bool enabled);
void setNormalTexturesCompressionEnabled(bool enabled);
void setGrayscaleTexturesCompressionEnabled(bool enabled);
void setCubeTexturesCompressionEnabled(bool enabled);
gpu::TexturePointer processImage(const QByteArray& content, const std::string& url, int maxNumPixels, TextureUsage::Type textureType); gpu::TexturePointer processImage(const QByteArray& content, const std::string& url, int maxNumPixels, TextureUsage::Type textureType);
} // namespace image } // namespace image

View file

@ -174,7 +174,7 @@ namespace ktx {
} }
std::unique_ptr<KTX> KTX::create(const StoragePointer& src) { std::unique_ptr<KTX> KTX::create(const StoragePointer& src) {
if (!src) { if (!src || !(*src)) {
return nullptr; return nullptr;
} }

View file

@ -421,7 +421,7 @@ void NetworkTexture::startRequestForNextMipLevel() {
_ktxResourceState = PENDING_MIP_REQUEST; _ktxResourceState = PENDING_MIP_REQUEST;
init(); init(false);
float priority = -(float)_originalKtxDescriptor->header.numberOfMipmapLevels + (float)_lowestKnownPopulatedMip; float priority = -(float)_originalKtxDescriptor->header.numberOfMipmapLevels + (float)_lowestKnownPopulatedMip;
setLoadPriority(this, priority); setLoadPriority(this, priority);
_url.setFragment(QString::number(_lowestKnownPopulatedMip - 1)); _url.setFragment(QString::number(_lowestKnownPopulatedMip - 1));
@ -472,6 +472,10 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) {
void NetworkTexture::ktxHeaderRequestFinished() { void NetworkTexture::ktxHeaderRequestFinished() {
Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA);
if (!_ktxHeaderRequest) {
return;
}
_ktxHeaderRequestFinished = true; _ktxHeaderRequestFinished = true;
maybeHandleFinishedInitialLoad(); maybeHandleFinishedInitialLoad();
} }
@ -479,6 +483,10 @@ void NetworkTexture::ktxHeaderRequestFinished() {
void NetworkTexture::ktxMipRequestFinished() { void NetworkTexture::ktxMipRequestFinished() {
Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA || _ktxResourceState == REQUESTING_MIP); Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA || _ktxResourceState == REQUESTING_MIP);
if (!_ktxMipRequest) {
return;
}
if (_ktxResourceState == LOADING_INITIAL_DATA) { if (_ktxResourceState == LOADING_INITIAL_DATA) {
_ktxHighMipRequestFinished = true; _ktxHighMipRequestFinished = true;
maybeHandleFinishedInitialLoad(); maybeHandleFinishedInitialLoad();
@ -682,6 +690,27 @@ void NetworkTexture::loadContent(const QByteArray& content) {
QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, _maxNumPixels)); QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, _maxNumPixels));
} }
void NetworkTexture::refresh() {
if ((_ktxHeaderRequest || _ktxMipRequest) && !_loaded && !_failedToLoad) {
return;
}
if (_ktxHeaderRequest || _ktxMipRequest) {
if (_ktxHeaderRequest) {
_ktxHeaderRequest->disconnect(this);
_ktxHeaderRequest->deleteLater();
_ktxHeaderRequest = nullptr;
}
if (_ktxMipRequest) {
_ktxMipRequest->disconnect(this);
_ktxMipRequest->deleteLater();
_ktxMipRequest = nullptr;
}
TextureCache::requestCompleted(_self);
}
Resource::refresh();
}
ImageReader::ImageReader(const QWeakPointer<Resource>& resource, const QUrl& url, const QByteArray& data, int maxNumPixels) : ImageReader::ImageReader(const QWeakPointer<Resource>& resource, const QUrl& url, const QByteArray& data, int maxNumPixels) :
_resource(resource), _resource(resource),
_url(url), _url(url),

View file

@ -58,6 +58,8 @@ public:
gpu::TexturePointer getFallbackTexture() const; gpu::TexturePointer getFallbackTexture() const;
void refresh() override;
signals: signals:
void networkTextureCreated(const QWeakPointer<NetworkTexture>& self); void networkTextureCreated(const QWeakPointer<NetworkTexture>& self);

View file

@ -19,8 +19,8 @@
#include <DependencyManager.h> #include <DependencyManager.h>
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include <comdef.h> #include <Windows.h>
#include <Wbemidl.h> #include <winreg.h>
#endif //Q_OS_WIN #endif //Q_OS_WIN
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
@ -30,6 +30,9 @@
#endif //Q_OS_MAC #endif //Q_OS_MAC
static const QString FALLBACK_FINGERPRINT_KEY = "fallbackFingerprint"; static const QString FALLBACK_FINGERPRINT_KEY = "fallbackFingerprint";
QUuid FingerprintUtils::_machineFingerprint { QUuid() };
QString FingerprintUtils::getMachineFingerprintString() { QString FingerprintUtils::getMachineFingerprintString() {
QString uuidString; QString uuidString;
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
@ -47,122 +50,32 @@ QString FingerprintUtils::getMachineFingerprintString() {
#endif //Q_OS_MAC #endif //Q_OS_MAC
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
HRESULT hres; HKEY cryptoKey;
IWbemLocator *pLoc = NULL;
// initialize com. Interface already does, but other
// users of this lib don't necessarily do so.
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hres)) {
qCDebug(networking) << "Failed to initialize COM library!";
return uuidString;
}
// initialize WbemLocator // try and open the key that contains the machine GUID
hres = CoCreateInstance( if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Cryptography", 0, KEY_READ, &cryptoKey) == ERROR_SUCCESS) {
CLSID_WbemLocator, DWORD type;
0, DWORD guidSize;
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *) &pLoc);
if (FAILED(hres)) { const char* MACHINE_GUID_KEY = "MachineGuid";
qCDebug(networking) << "Failed to initialize WbemLocator";
return uuidString;
}
// Connect to WMI through the IWbemLocator::ConnectServer method
IWbemServices *pSvc = NULL;
// Connect to the root\cimv2 namespace with // try and retrieve the size of the GUID value
// the current user and obtain pointer pSvc if (RegQueryValueEx(cryptoKey, MACHINE_GUID_KEY, NULL, &type, NULL, &guidSize) == ERROR_SUCCESS) {
// to make IWbemServices calls. // make sure that the value is a string
hres = pLoc->ConnectServer( if (type == REG_SZ) {
_bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace // retrieve the machine GUID and return that as our UUID string
NULL, // User name. NULL = current user std::string machineGUID(guidSize / sizeof(char), '\0');
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (for example, Kerberos)
0, // Context object
&pSvc // pointer to IWbemServices proxy
);
if (FAILED(hres)) { if (RegQueryValueEx(cryptoKey, MACHINE_GUID_KEY, NULL, NULL,
pLoc->Release(); reinterpret_cast<LPBYTE>(&machineGUID[0]), &guidSize) == ERROR_SUCCESS) {
qCDebug(networking) << "Failed to connect to WMI"; uuidString = QString::fromStdString(machineGUID);
return uuidString; }
}
// Set security levels on the proxy
hres = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if (FAILED(hres)) {
pSvc->Release();
pLoc->Release();
qCDebug(networking) << "Failed to set security on proxy blanket";
return uuidString;
}
// Use the IWbemServices pointer to grab the Win32_BIOS stuff
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t("WQL"),
bstr_t("SELECT * FROM Win32_ComputerSystemProduct"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (FAILED(hres)) {
pSvc->Release();
pLoc->Release();
qCDebug(networking) << "query to get Win32_ComputerSystemProduct info";
return uuidString;
}
// Get the SerialNumber from the Win32_BIOS data
IWbemClassObject *pclsObj;
ULONG uReturn = 0;
SHORT sRetStatus = -100;
while (pEnumerator) {
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
if(0 == uReturn){
break;
}
VARIANT vtProp;
// Get the value of the Name property
hr = pclsObj->Get(L"UUID", 0, &vtProp, 0, 0);
if (!FAILED(hres)) {
switch (vtProp.vt) {
case VT_BSTR:
uuidString = QString::fromWCharArray(vtProp.bstrVal);
break;
} }
} }
VariantClear(&vtProp);
pclsObj->Release(); RegCloseKey(cryptoKey);
} }
pEnumerator->Release();
// Cleanup
pSvc->Release();
pLoc->Release();
qCDebug(networking) << "Windows BIOS UUID: " << uuidString;
#endif //Q_OS_WIN #endif //Q_OS_WIN
return uuidString; return uuidString;
@ -171,29 +84,36 @@ QString FingerprintUtils::getMachineFingerprintString() {
QUuid FingerprintUtils::getMachineFingerprint() { QUuid FingerprintUtils::getMachineFingerprint() {
QString uuidString = getMachineFingerprintString(); if (_machineFingerprint.isNull()) {
QString uuidString = getMachineFingerprintString();
// now, turn into uuid. A malformed string will
// return QUuid() ("{00000...}"), which handles
// any errors in getting the string
QUuid uuid(uuidString);
// now, turn into uuid. A malformed string will
// return QUuid() ("{00000...}"), which handles
// any errors in getting the string
QUuid uuid(uuidString);
if (uuid == QUuid()) {
// if you cannot read a fallback key cuz we aren't saving them, just generate one for
// this session and move on
if (DependencyManager::get<Setting::Manager>().isNull()) {
return QUuid::createUuid();
}
// read fallback key (if any)
Settings settings;
uuid = QUuid(settings.value(FALLBACK_FINGERPRINT_KEY).toString());
qCDebug(networking) << "read fallback maching fingerprint: " << uuid.toString();
if (uuid == QUuid()) { if (uuid == QUuid()) {
// no fallback yet, set one // if you cannot read a fallback key cuz we aren't saving them, just generate one for
uuid = QUuid::createUuid(); // this session and move on
settings.setValue(FALLBACK_FINGERPRINT_KEY, uuid.toString()); if (DependencyManager::get<Setting::Manager>().isNull()) {
qCDebug(networking) << "no fallback machine fingerprint, setting it to: " << uuid.toString(); return QUuid::createUuid();
}
// read fallback key (if any)
Settings settings;
uuid = QUuid(settings.value(FALLBACK_FINGERPRINT_KEY).toString());
qCDebug(networking) << "read fallback maching fingerprint: " << uuid.toString();
if (uuid == QUuid()) {
// no fallback yet, set one
uuid = QUuid::createUuid();
settings.setValue(FALLBACK_FINGERPRINT_KEY, uuid.toString());
qCDebug(networking) << "no fallback machine fingerprint, setting it to: " << uuid.toString();
}
} }
_machineFingerprint = uuid;
} }
return uuid;
return _machineFingerprint;
} }

View file

@ -21,6 +21,7 @@ public:
private: private:
static QString getMachineFingerprintString(); static QString getMachineFingerprintString();
static QUuid _machineFingerprint;
}; };
#endif // hifi_FingerprintUtils_h #endif // hifi_FingerprintUtils_h

View file

@ -533,13 +533,13 @@ void Resource::ensureLoading() {
} }
void Resource::setLoadPriority(const QPointer<QObject>& owner, float priority) { void Resource::setLoadPriority(const QPointer<QObject>& owner, float priority) {
if (!(_failedToLoad || _loaded)) { if (!(_failedToLoad)) {
_loadPriorities.insert(owner, priority); _loadPriorities.insert(owner, priority);
} }
} }
void Resource::setLoadPriorities(const QHash<QPointer<QObject>, float>& priorities) { void Resource::setLoadPriorities(const QHash<QPointer<QObject>, float>& priorities) {
if (_failedToLoad || _loaded) { if (_failedToLoad) {
return; return;
} }
for (QHash<QPointer<QObject>, float>::const_iterator it = priorities.constBegin(); for (QHash<QPointer<QObject>, float>::const_iterator it = priorities.constBegin();
@ -549,7 +549,7 @@ void Resource::setLoadPriorities(const QHash<QPointer<QObject>, float>& prioriti
} }
void Resource::clearLoadPriority(const QPointer<QObject>& owner) { void Resource::clearLoadPriority(const QPointer<QObject>& owner) {
if (!(_failedToLoad || _loaded)) { if (!(_failedToLoad)) {
_loadPriorities.remove(owner); _loadPriorities.remove(owner);
} }
} }
@ -612,10 +612,12 @@ void Resource::allReferencesCleared() {
} }
} }
void Resource::init() { void Resource::init(bool resetLoaded) {
_startedLoading = false; _startedLoading = false;
_failedToLoad = false; _failedToLoad = false;
_loaded = false; if (resetLoaded) {
_loaded = false;
}
_attempts = 0; _attempts = 0;
_activeUrl = _url; _activeUrl = _url;

View file

@ -385,7 +385,7 @@ public:
float getProgress() const { return (_bytesTotal <= 0) ? 0.0f : (float)_bytesReceived / _bytesTotal; } float getProgress() const { return (_bytesTotal <= 0) ? 0.0f : (float)_bytesReceived / _bytesTotal; }
/// Refreshes the resource. /// Refreshes the resource.
void refresh(); virtual void refresh();
void setSelf(const QWeakPointer<Resource>& self) { _self = self; } void setSelf(const QWeakPointer<Resource>& self) { _self = self; }
@ -425,7 +425,7 @@ protected slots:
void attemptRequest(); void attemptRequest();
protected: protected:
virtual void init(); virtual void init(bool resetLoaded = true);
/// Called by ResourceCache to begin loading this Resource. /// Called by ResourceCache to begin loading this Resource.
/// This method can be overriden to provide custom request functionality. If this is done, /// This method can be overriden to provide custom request functionality. If this is done,
@ -454,9 +454,14 @@ protected:
QUrl _url; QUrl _url;
QUrl _activeUrl; QUrl _activeUrl;
ByteRange _requestByteRange; ByteRange _requestByteRange;
// _loaded == true means we are in a loaded and usable state. It is possible that there may still be
// active requests/loading while in this state. Example: Progressive KTX downloads, where higher resolution
// mips are being download.
bool _startedLoading = false; bool _startedLoading = false;
bool _failedToLoad = false; bool _failedToLoad = false;
bool _loaded = false; bool _loaded = false;
QHash<QPointer<QObject>, float> _loadPriorities; QHash<QPointer<QObject>, float> _loadPriorities;
QWeakPointer<Resource> _self; QWeakPointer<Resource> _self;
QPointer<ResourceCache> _cache; QPointer<ResourceCache> _cache;

View file

@ -28,10 +28,10 @@ public:
const std::unordered_set<int>& getCauterizeBoneSet() const { return _cauterizeBoneSet; } const std::unordered_set<int>& getCauterizeBoneSet() const { return _cauterizeBoneSet; }
void setCauterizeBoneSet(const std::unordered_set<int>& boneSet) { _cauterizeBoneSet = boneSet; } void setCauterizeBoneSet(const std::unordered_set<int>& boneSet) { _cauterizeBoneSet = boneSet; }
void deleteGeometry() override; void deleteGeometry() override;
bool updateGeometry() override; bool updateGeometry() override;
void createVisibleRenderItemSet() override; void createVisibleRenderItemSet() override;
void createCollisionRenderItemSet() override; void createCollisionRenderItemSet() override;
virtual void updateClusterMatrices() override; virtual void updateClusterMatrices() override;
@ -41,7 +41,7 @@ public:
protected: protected:
std::unordered_set<int> _cauterizeBoneSet; std::unordered_set<int> _cauterizeBoneSet;
QVector<Model::MeshState> _cauterizeMeshStates; QVector<Model::MeshState> _cauterizeMeshStates;
bool _isCauterized { false }; bool _isCauterized { false };
bool _enableCauterization { false }; bool _enableCauterization { false };
}; };

View file

@ -64,7 +64,9 @@ float fetchRoughnessMap(vec2 uv) {
uniform sampler2D normalMap; uniform sampler2D normalMap;
vec3 fetchNormalMap(vec2 uv) { vec3 fetchNormalMap(vec2 uv) {
// unpack normal, swizzle to get into hifi tangent space with Y axis pointing out // unpack normal, swizzle to get into hifi tangent space with Y axis pointing out
return normalize(texture(normalMap, uv).rbg -vec3(0.5, 0.5, 0.5)); vec2 t = 2.0 * (texture(normalMap, uv).rg - vec2(0.5, 0.5));
vec2 t2 = t*t;
return vec3(t.x, sqrt(1 - t2.x - t2.y), t.y);
} }
<@endif@> <@endif@>

View file

@ -573,7 +573,7 @@ bool Model::addToScene(const render::ScenePointer& scene,
bool somethingAdded = false; bool somethingAdded = false;
if (_collisionGeometry) { if (_collisionGeometry) {
if (_collisionRenderItems.empty()) { if (_collisionRenderItemsMap.empty()) {
foreach (auto renderItem, _collisionRenderItems) { foreach (auto renderItem, _collisionRenderItems) {
auto item = scene->allocateID(); auto item = scene->allocateID();
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem); auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
@ -583,7 +583,7 @@ bool Model::addToScene(const render::ScenePointer& scene,
transaction.resetItem(item, renderPayload); transaction.resetItem(item, renderPayload);
_collisionRenderItemsMap.insert(item, renderPayload); _collisionRenderItemsMap.insert(item, renderPayload);
} }
somethingAdded = !_collisionRenderItems.empty(); somethingAdded = !_collisionRenderItemsMap.empty();
} }
} else { } else {
if (_modelMeshRenderItemsMap.empty()) { if (_modelMeshRenderItemsMap.empty()) {
@ -632,7 +632,7 @@ void Model::removeFromScene(const render::ScenePointer& scene, render::Transacti
transaction.removeItem(item); transaction.removeItem(item);
} }
_collisionRenderItems.clear(); _collisionRenderItems.clear();
_collisionRenderItems.clear(); _collisionRenderItemsMap.clear();
_addedToScene = false; _addedToScene = false;
_renderInfoVertexCount = 0; _renderInfoVertexCount = 0;

View file

@ -11,7 +11,9 @@
#include <QDebug> #include <QDebug>
#include <GLMHelpers.h> #include <GLMHelpers.h>
#include <glm/gtx/string_cast.hpp>
#include "ScriptEngineLogging.h" #include "ScriptEngineLogging.h"
#include "ScriptEngine.h"
#include "Mat4.h" #include "Mat4.h"
glm::mat4 Mat4::multiply(const glm::mat4& m1, const glm::mat4& m2) const { glm::mat4 Mat4::multiply(const glm::mat4& m1, const glm::mat4& m2) const {
@ -66,10 +68,12 @@ glm::vec3 Mat4::getUp(const glm::mat4& m) const {
return glm::vec3(m[0][1], m[1][1], m[2][1]); return glm::vec3(m[0][1], m[1][1], m[2][1]);
} }
void Mat4::print(const QString& label, const glm::mat4& m) const { void Mat4::print(const QString& label, const glm::mat4& m, bool transpose) const {
qCDebug(scriptengine) << qPrintable(label) << glm::dmat4 out = transpose ? glm::transpose(m) : m;
"row0 =" << m[0][0] << "," << m[1][0] << "," << m[2][0] << "," << m[3][0] << QString message = QString("%1 %2").arg(qPrintable(label));
"row1 =" << m[0][1] << "," << m[1][1] << "," << m[2][1] << "," << m[3][1] << message = message.arg(glm::to_string(out).c_str());
"row2 =" << m[0][2] << "," << m[1][2] << "," << m[2][2] << "," << m[3][2] << qCDebug(scriptengine) << message;
"row3 =" << m[0][3] << "," << m[1][3] << "," << m[2][3] << "," << m[3][3]; if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine())) {
scriptEngine->print(message);
}
} }

View file

@ -16,9 +16,10 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QtScript/QScriptable>
/// Scriptable Mat4 object. Used exclusively in the JavaScript API /// Scriptable Mat4 object. Used exclusively in the JavaScript API
class Mat4 : public QObject { class Mat4 : public QObject, protected QScriptable {
Q_OBJECT Q_OBJECT
public slots: public slots:
@ -43,7 +44,7 @@ public slots:
glm::vec3 getRight(const glm::mat4& m) const; glm::vec3 getRight(const glm::mat4& m) const;
glm::vec3 getUp(const glm::mat4& m) const; glm::vec3 getUp(const glm::mat4& m) const;
void print(const QString& label, const glm::mat4& m) const; void print(const QString& label, const glm::mat4& m, bool transpose = false) const;
}; };
#endif // hifi_Mat4_h #endif // hifi_Mat4_h

View file

@ -15,7 +15,9 @@
#include <OctreeConstants.h> #include <OctreeConstants.h>
#include <GLMHelpers.h> #include <GLMHelpers.h>
#include <glm/gtx/string_cast.hpp>
#include "ScriptEngineLogging.h" #include "ScriptEngineLogging.h"
#include "ScriptEngine.h"
#include "Quat.h" #include "Quat.h"
quat Quat::normalize(const glm::quat& q) { quat Quat::normalize(const glm::quat& q) {
@ -114,8 +116,17 @@ float Quat::dot(const glm::quat& q1, const glm::quat& q2) {
return glm::dot(q1, q2); return glm::dot(q1, q2);
} }
void Quat::print(const QString& label, const glm::quat& q) { void Quat::print(const QString& label, const glm::quat& q, bool asDegrees) {
qCDebug(scriptengine) << qPrintable(label) << q.x << "," << q.y << "," << q.z << "," << q.w; QString message = QString("%1 %2").arg(qPrintable(label));
if (asDegrees) {
message = message.arg(glm::to_string(glm::dvec3(safeEulerAngles(q))).c_str());
} else {
message = message.arg(glm::to_string(glm::dquat(q)).c_str());
}
qCDebug(scriptengine) << message;
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine())) {
scriptEngine->print(message);
}
} }
bool Quat::equal(const glm::quat& q1, const glm::quat& q2) { bool Quat::equal(const glm::quat& q1, const glm::quat& q2) {

View file

@ -18,6 +18,7 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QtScript/QScriptable>
/**jsdoc /**jsdoc
* A Quaternion * A Quaternion
@ -30,7 +31,7 @@
*/ */
/// Scriptable interface a Quaternion helper class object. Used exclusively in the JavaScript API /// Scriptable interface a Quaternion helper class object. Used exclusively in the JavaScript API
class Quat : public QObject { class Quat : public QObject, protected QScriptable {
Q_OBJECT Q_OBJECT
public slots: public slots:
@ -58,7 +59,7 @@ public slots:
glm::quat slerp(const glm::quat& q1, const glm::quat& q2, float alpha); glm::quat slerp(const glm::quat& q1, const glm::quat& q2, float alpha);
glm::quat squad(const glm::quat& q1, const glm::quat& q2, const glm::quat& s1, const glm::quat& s2, float h); glm::quat squad(const glm::quat& q1, const glm::quat& q2, const glm::quat& s1, const glm::quat& s2, float h);
float dot(const glm::quat& q1, const glm::quat& q2); float dot(const glm::quat& q1, const glm::quat& q2);
void print(const QString& label, const glm::quat& q); void print(const QString& label, const glm::quat& q, bool asDegrees = false);
bool equal(const glm::quat& q1, const glm::quat& q2); bool equal(const glm::quat& q1, const glm::quat& q2);
glm::quat cancelOutRollAndPitch(const glm::quat& q); glm::quat cancelOutRollAndPitch(const glm::quat& q);
glm::quat cancelOutRoll(const glm::quat& q); glm::quat cancelOutRoll(const glm::quat& q);

View file

@ -105,11 +105,11 @@ static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) {
} }
message += context->argument(i).toString(); message += context->argument(i).toString();
} }
qCDebug(scriptengineScript).noquote() << "script:print()<<" << message; // noquote() so that \n is treated as newline qCDebug(scriptengineScript).noquote() << message; // noquote() so that \n is treated as newline
// FIXME - this approach neeeds revisiting. print() comes here, which ends up calling Script.print? if (ScriptEngine *scriptEngine = qobject_cast<ScriptEngine*>(engine)) {
engine->globalObject().property("Script").property("print") scriptEngine->print(message);
.call(engine->nullValue(), QScriptValueList({ message })); }
return QScriptValue(); return QScriptValue();
} }
@ -472,6 +472,11 @@ void ScriptEngine::scriptInfoMessage(const QString& message) {
emit infoMessage(message, getFilename()); emit infoMessage(message, getFilename());
} }
void ScriptEngine::scriptPrintedMessage(const QString& message) {
qCDebug(scriptengine) << message;
emit printedMessage(message, getFilename());
}
// Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of // Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of
// callAnimationStateHandler requires that the type be registered. // callAnimationStateHandler requires that the type be registered.
// These two are meaningful, if we ever do want to use them... // These two are meaningful, if we ever do want to use them...

View file

@ -221,6 +221,7 @@ public:
void scriptErrorMessage(const QString& message); void scriptErrorMessage(const QString& message);
void scriptWarningMessage(const QString& message); void scriptWarningMessage(const QString& message);
void scriptInfoMessage(const QString& message); void scriptInfoMessage(const QString& message);
void scriptPrintedMessage(const QString& message);
int getNumRunningEntityScripts() const; int getNumRunningEntityScripts() const;
bool getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const; bool getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const;

View file

@ -453,7 +453,8 @@ ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserL
(scriptFilename.scheme() != "http" && (scriptFilename.scheme() != "http" &&
scriptFilename.scheme() != "https" && scriptFilename.scheme() != "https" &&
scriptFilename.scheme() != "atp" && scriptFilename.scheme() != "atp" &&
scriptFilename.scheme() != "file")) { scriptFilename.scheme() != "file" &&
scriptFilename.scheme() != "about")) {
// deal with a "url" like c:/something // deal with a "url" like c:/something
scriptUrl = normalizeScriptURL(QUrl::fromLocalFile(scriptFilename.toString())); scriptUrl = normalizeScriptURL(QUrl::fromLocalFile(scriptFilename.toString()));
} else { } else {
@ -472,7 +473,7 @@ ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserL
}, Qt::QueuedConnection); }, Qt::QueuedConnection);
if (scriptFilename.isEmpty()) { if (scriptFilename.isEmpty() || !scriptUrl.isValid()) {
launchScriptEngine(scriptEngine); launchScriptEngine(scriptEngine);
} else { } else {
// connect to the appropriate signals of this script engine // connect to the appropriate signals of this script engine

View file

@ -14,6 +14,7 @@
#include <QDebug> #include <QDebug>
#include "ScriptEngineLogging.h" #include "ScriptEngineLogging.h"
#include "ScriptEngine.h"
#include "ScriptUUID.h" #include "ScriptUUID.h"
QUuid ScriptUUID::fromString(const QString& s) { QUuid ScriptUUID::fromString(const QString& s) {
@ -36,6 +37,11 @@ bool ScriptUUID::isNull(const QUuid& id) {
return id.isNull(); return id.isNull();
} }
void ScriptUUID::print(const QString& lable, const QUuid& id) { void ScriptUUID::print(const QString& label, const QUuid& id) {
qCDebug(scriptengine) << qPrintable(lable) << id.toString(); QString message = QString("%1 %2").arg(qPrintable(label));
message = message.arg(id.toString());
qCDebug(scriptengine) << message;
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine())) {
scriptEngine->print(message);
}
} }

View file

@ -15,9 +15,10 @@
#define hifi_ScriptUUID_h #define hifi_ScriptUUID_h
#include <QUuid> #include <QUuid>
#include <QtScript/QScriptable>
/// Scriptable interface for a UUID helper class object. Used exclusively in the JavaScript API /// Scriptable interface for a UUID helper class object. Used exclusively in the JavaScript API
class ScriptUUID : public QObject { class ScriptUUID : public QObject, protected QScriptable {
Q_OBJECT Q_OBJECT
public slots: public slots:
@ -26,7 +27,7 @@ public slots:
QUuid generate(); QUuid generate();
bool isEqual(const QUuid& idA, const QUuid& idB); bool isEqual(const QUuid& idA, const QUuid& idB);
bool isNull(const QUuid& id); bool isNull(const QUuid& id);
void print(const QString& lable, const QUuid& id); void print(const QString& label, const QUuid& id);
}; };
#endif // hifi_ScriptUUID_h #endif // hifi_ScriptUUID_h

View file

@ -14,20 +14,26 @@
#include <QDebug> #include <QDebug>
#include <GLMHelpers.h> #include <GLMHelpers.h>
#include <glm/gtx/string_cast.hpp>
#include "ScriptEngineLogging.h" #include "ScriptEngineLogging.h"
#include "NumericalConstants.h" #include "NumericalConstants.h"
#include "Vec3.h" #include "Vec3.h"
#include "ScriptEngine.h"
float Vec3::orientedAngle(const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& v3) { float Vec3::orientedAngle(const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& v3) {
float radians = glm::orientedAngle(glm::normalize(v1), glm::normalize(v2), glm::normalize(v3)); float radians = glm::orientedAngle(glm::normalize(v1), glm::normalize(v2), glm::normalize(v3));
return glm::degrees(radians); return glm::degrees(radians);
} }
void Vec3::print(const QString& label, const glm::vec3& v) {
void Vec3::print(const QString& lable, const glm::vec3& v) { QString message = QString("%1 %2").arg(qPrintable(label));
qCDebug(scriptengine) << qPrintable(lable) << v.x << "," << v.y << "," << v.z; message = message.arg(glm::to_string(glm::dvec3(v)).c_str());
qCDebug(scriptengine) << message;
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine())) {
scriptEngine->print(message);
}
} }
bool Vec3::withinEpsilon(const glm::vec3& v1, const glm::vec3& v2, float epsilon) { bool Vec3::withinEpsilon(const glm::vec3& v1, const glm::vec3& v2, float epsilon) {

View file

@ -17,6 +17,7 @@
#include <QtCore/QObject> #include <QtCore/QObject>
#include <QtCore/QString> #include <QtCore/QString>
#include <QtScript/QScriptable>
#include "GLMHelpers.h" #include "GLMHelpers.h"
@ -48,7 +49,7 @@
*/ */
/// Scriptable interface a Vec3ernion helper class object. Used exclusively in the JavaScript API /// Scriptable interface a Vec3ernion helper class object. Used exclusively in the JavaScript API
class Vec3 : public QObject { class Vec3 : public QObject, protected QScriptable {
Q_OBJECT Q_OBJECT
Q_PROPERTY(glm::vec3 UNIT_X READ UNIT_X CONSTANT) Q_PROPERTY(glm::vec3 UNIT_X READ UNIT_X CONSTANT)
Q_PROPERTY(glm::vec3 UNIT_Y READ UNIT_Y CONSTANT) Q_PROPERTY(glm::vec3 UNIT_Y READ UNIT_Y CONSTANT)

View file

@ -1138,3 +1138,17 @@ SpatiallyNestablePointer SpatiallyNestable::findByID(QUuid id, bool& success) {
} }
return parentWP.lock(); return parentWP.lock();
} }
QString SpatiallyNestable::nestableTypeToString(NestableType nestableType) {
switch(nestableType) {
case NestableType::Entity:
return "entity";
case NestableType::Avatar:
return "avatar";
case NestableType::Overlay:
return "overlay";
default:
return "unknown";
}
}

View file

@ -42,6 +42,8 @@ public:
virtual const QUuid getID() const; virtual const QUuid getID() const;
virtual void setID(const QUuid& id); virtual void setID(const QUuid& id);
virtual QString getName() const { return "SpatiallyNestable"; }
virtual const QUuid getParentID() const; virtual const QUuid getParentID() const;
virtual void setParentID(const QUuid& parentID); virtual void setParentID(const QUuid& parentID);
@ -62,6 +64,8 @@ public:
static glm::vec3 localToWorldAngularVelocity(const glm::vec3& angularVelocity, static glm::vec3 localToWorldAngularVelocity(const glm::vec3& angularVelocity,
const QUuid& parentID, int parentJointIndex, bool& success); const QUuid& parentID, int parentJointIndex, bool& success);
static QString nestableTypeToString(NestableType nestableType);
// world frame // world frame
virtual const Transform getTransform(bool& success, int depth = 0) const; virtual const Transform getTransform(bool& success, int depth = 0) const;
virtual const Transform getTransform() const; virtual const Transform getTransform() const;

View file

@ -20,9 +20,10 @@
print('<span style="color:red">Tests completed with ' + print('<span style="color:red">Tests completed with ' +
errorCount + ' ' + ERROR + '.<span>'); errorCount + ' ' + ERROR + '.<span>');
} }
if (pending.length) if (pending.length) {
print ('<span style="color:darkorange">disabled: <br />&nbsp;&nbsp;&nbsp;'+ print ('<span style="color:darkorange">disabled: <br />&nbsp;&nbsp;&nbsp;'+
pending.join('<br />&nbsp;&nbsp;&nbsp;')+'</span>'); pending.join('<br />&nbsp;&nbsp;&nbsp;')+'</span>');
}
print('Tests completed in ' + (endTime - startTime) + 'ms.'); print('Tests completed in ' + (endTime - startTime) + 'ms.');
}; };
this.suiteStarted = function(obj) { this.suiteStarted = function(obj) {

View file

@ -0,0 +1,39 @@
/* eslint-env jasmine */
// this test generates sample print, Script.print, etc. output
main();
function main() {
// to match with historical behavior, Script.print(message) output only triggers
// the printedMessage signal (and therefore doesn't show up in the application log)
Script.print('[Script.print] hello world');
// the rest of these should show up in both the application log and signaled print handlers
print('[print]', 'hello', 'world');
// note: these trigger the equivalent of an emit
Script.printedMessage('[Script.printedMessage] hello world', '{filename}');
Script.infoMessage('[Script.infoMessage] hello world', '{filename}');
Script.warningMessage('[Script.warningMessage] hello world', '{filename}');
Script.errorMessage('[Script.errorMessage] hello world', '{filename}');
{
Vec3.print('[Vec3.print]', Vec3.HALF);
var q = Quat.fromPitchYawRollDegrees(45, 45, 45);
Quat.print('[Quat.print]', q);
Quat.print('[Quat.print (euler)]', q, true);
function vec4(x,y,z,w) {
return { x: x, y: y, z: z, w: w };
}
var m = Mat4.createFromColumns(
vec4(1,2,3,4), vec4(5,6,7,8), vec4(9,10,11,12), vec4(13,14,15,16)
);
Mat4.print('[Mat4.print (col major)]', m);
Mat4.print('[Mat4.print (row major)]', m, true);
Uuid.print('[Uuid.print]', Uuid.fromString(Uuid.toString(0)));
}
}

View file

@ -1,5 +1,7 @@
/* eslint-env jasmine */
// Art3mis // Art3mis
// eslint-disable-next-line max-len
var DEFAULT_AVATAR_URL = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/e76946cc-c272-4adf-9bb6-02cde0a4b57d/8fd984ea6fe1495147a3303f87fa6e23.fst?1460131758"; var DEFAULT_AVATAR_URL = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/e76946cc-c272-4adf-9bb6-02cde0a4b57d/8fd984ea6fe1495147a3303f87fa6e23.fst?1460131758";
var ORIGIN = {x: 0, y: 0, z: 0}; var ORIGIN = {x: 0, y: 0, z: 0};
@ -8,6 +10,15 @@ var ROT_IDENT = {x: 0, y: 0, z: 0, w: 1};
describe("MyAvatar", function () { describe("MyAvatar", function () {
// backup/restore current skeletonModelURL
beforeAll(function() {
this.oldURL = MyAvatar.skeletonModelURL;
});
afterAll(function() {
MyAvatar.skeletonModelURL = this.oldURL;
});
// reload the avatar from scratch before each test. // reload the avatar from scratch before each test.
beforeEach(function (done) { beforeEach(function (done) {
MyAvatar.skeletonModelURL = DEFAULT_AVATAR_URL; MyAvatar.skeletonModelURL = DEFAULT_AVATAR_URL;
@ -20,12 +31,12 @@ describe("MyAvatar", function () {
MyAvatar.position = ORIGIN; MyAvatar.position = ORIGIN;
MyAvatar.orientation = ROT_IDENT; MyAvatar.orientation = ROT_IDENT;
// give the avatar 1/2 a second to settle on the ground in the idle pose. // give the avatar 1/2 a second to settle on the ground in the idle pose.
Script.setTimeout(function () { Script.setTimeout(function () {
done(); done();
}, 500); }, 500);
} }
}, 500); }, 500);
}); }, 10000 /* timeout -- allow time to download avatar*/);
// makes the assumption that there is solid ground somewhat underneath the avatar. // makes the assumption that there is solid ground somewhat underneath the avatar.
it("position and orientation getters", function () { it("position and orientation getters", function () {

View file

@ -1,3 +1,5 @@
/* eslint-env jasmine */
Script.include('../../../system/libraries/utils.js'); Script.include('../../../system/libraries/utils.js');
describe('Bind', function() { describe('Bind', function() {

View file

@ -1,3 +1,5 @@
/* eslint-env jasmine */
describe('Entity', function() { describe('Entity', function() {
var center = Vec3.sum( var center = Vec3.sum(
MyAvatar.position, MyAvatar.position,
@ -19,6 +21,14 @@ describe('Entity', function() {
}, },
}; };
it('serversExist', function() {
expect(Entities.serversExist()).toBe(true);
});
it('canRezTmp', function() {
expect(Entities.canRezTmp()).toBe(true);
});
beforeEach(function() { beforeEach(function() {
boxEntity = Entities.addEntity(boxProps); boxEntity = Entities.addEntity(boxProps);
}); });
@ -62,4 +72,4 @@ describe('Entity', function() {
props = Entities.getEntityProperties(boxEntity); props = Entities.getEntityProperties(boxEntity);
expect(props.lastEdited).toBeGreaterThan(prevLastEdited); expect(props.lastEdited).toBeGreaterThan(prevLastEdited);
}); });
}); });

View file

@ -1,13 +1,30 @@
/* eslint-env jasmine */
// Include testing library // Include testing library
Script.include('../../libraries/jasmine/jasmine.js'); Script.include('../../libraries/jasmine/jasmine.js');
Script.include('../../libraries/jasmine/hifi-boot.js') Script.include('../../libraries/jasmine/hifi-boot.js');
// Include unit tests // Include unit tests
// FIXME: Figure out why jasmine done() is not working. Script.include('avatarUnitTests.js');
// Script.include('avatarUnitTests.js');
Script.include('bindUnitTest.js'); Script.include('bindUnitTest.js');
Script.include('entityUnitTests.js'); Script.include('entityUnitTests.js');
describe("jasmine internal tests", function() {
it('should support async .done()', function(done) {
var start = new Date;
Script.setTimeout(function() {
expect((new Date - start)/1000).toBeCloseTo(0.5, 1);
done();
}, 500);
});
// jasmine pending test
xit('disabled test', function() {
expect(false).toBe(true);
});
});
// invoke Script.stop (after any async tests complete)
jasmine.getEnv().addReporter({ jasmineDone: Script.stop });
// Run the tests // Run the tests
jasmine.getEnv().execute(); jasmine.getEnv().execute();
Script.stop();

View file

@ -3881,6 +3881,7 @@ function MyController(hand) {
// we appear to be holding something and this script isn't in a state that would be holding something. // we appear to be holding something and this script isn't in a state that would be holding something.
// unhook it. if we previously took note of this entity's parent, put it back where it was. This // unhook it. if we previously took note of this entity's parent, put it back where it was. This
// works around some problems that happen when more than one hand or avatar is passing something around. // works around some problems that happen when more than one hand or avatar is passing something around.
var childType = Entities.getNestableType(childID);
if (_this.previousParentID[childID]) { if (_this.previousParentID[childID]) {
var previousParentID = _this.previousParentID[childID]; var previousParentID = _this.previousParentID[childID];
var previousParentJointIndex = _this.previousParentJointIndex[childID]; var previousParentJointIndex = _this.previousParentJointIndex[childID];
@ -3898,7 +3899,7 @@ function MyController(hand) {
} }
_this.previouslyUnhooked[childID] = now; _this.previouslyUnhooked[childID] = now;
if (Overlays.getProperty(childID, "grabbable")) { if (childType == "overlay" && Overlays.getProperty(childID, "grabbable")) {
// only auto-unhook overlays that were flagged as grabbable. this avoids unhooking overlays // only auto-unhook overlays that were flagged as grabbable. this avoids unhooking overlays
// used in tutorial. // used in tutorial.
Overlays.editOverlay(childID, { Overlays.editOverlay(childID, {
@ -3906,12 +3907,20 @@ function MyController(hand) {
parentJointIndex: previousParentJointIndex parentJointIndex: previousParentJointIndex
}); });
} }
Entities.editEntity(childID, { parentID: previousParentID, parentJointIndex: previousParentJointIndex }); if (childType == "entity") {
Entities.editEntity(childID, {
parentID: previousParentID,
parentJointIndex: previousParentJointIndex
});
}
} else { } else {
Entities.editEntity(childID, { parentID: NULL_UUID }); if (childType == "entity") {
if (Overlays.getProperty(childID, "grabbable")) { Entities.editEntity(childID, { parentID: NULL_UUID });
Overlays.editOverlay(childID, { parentID: NULL_UUID }); } else if (childType == "overlay") {
if (Overlays.getProperty(childID, "grabbable")) {
Overlays.editOverlay(childID, { parentID: NULL_UUID });
}
} }
} }
} }

View file

@ -275,7 +275,8 @@ WebTablet.prototype.getLocation = function() {
}; };
WebTablet.prototype.setHomeButtonTexture = function() { WebTablet.prototype.setHomeButtonTexture = function() {
Entities.editEntity(this.tabletEntityID, {textures: JSON.stringify({"tex.close": HOME_BUTTON_TEXTURE})}); // TODO - is this still needed?
// Entities.editEntity(this.tabletEntityID, {textures: JSON.stringify({"tex.close": HOME_BUTTON_TEXTURE})});
}; };
WebTablet.prototype.setURL = function (url) { WebTablet.prototype.setURL = function (url) {
@ -338,7 +339,8 @@ WebTablet.prototype.geometryChanged = function (geometry) {
// compute position, rotation & parentJointIndex of the tablet // compute position, rotation & parentJointIndex of the tablet
this.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties); this.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties);
Entities.editEntity(this.tabletEntityID, tabletProperties); // TODO -- is this still needed?
// Entities.editEntity(this.tabletEntityID, tabletProperties);
} }
}; };
@ -439,7 +441,8 @@ WebTablet.prototype.onHmdChanged = function () {
var tabletProperties = {}; var tabletProperties = {};
// compute position, rotation & parentJointIndex of the tablet // compute position, rotation & parentJointIndex of the tablet
this.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties); this.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties);
Entities.editEntity(this.tabletEntityID, tabletProperties); // TODO -- is this still needed?
// Entities.editEntity(this.tabletEntityID, tabletProperties);
// Full scene FXAA should be disabled on the overlay when the tablet in desktop mode. // Full scene FXAA should be disabled on the overlay when the tablet in desktop mode.
// This should make the text more readable. // This should make the text more readable.
@ -530,7 +533,8 @@ WebTablet.prototype.cameraModeChanged = function (newMode) {
var tabletProperties = {}; var tabletProperties = {};
// compute position, rotation & parentJointIndex of the tablet // compute position, rotation & parentJointIndex of the tablet
self.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties); self.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties);
Entities.editEntity(self.tabletEntityID, tabletProperties); // TODO -- is this still needed?
// Entities.editEntity(self.tabletEntityID, tabletProperties);
} }
}; };

View file

@ -13,7 +13,7 @@
// //
/* global Script, HMD, WebTablet, UIWebTablet, UserActivityLogger, Settings, Entities, Messages, Tablet, Overlays, /* global Script, HMD, WebTablet, UIWebTablet, UserActivityLogger, Settings, Entities, Messages, Tablet, Overlays,
MyAvatar, Menu, Vec3 */ MyAvatar, Menu, AvatarInputs, Vec3 */
(function() { // BEGIN LOCAL_SCOPE (function() { // BEGIN LOCAL_SCOPE
var tabletRezzed = false; var tabletRezzed = false;
@ -25,9 +25,18 @@
var debugTablet = false; var debugTablet = false;
var tabletScalePercentage = 100.0; var tabletScalePercentage = 100.0;
UIWebTablet = null; UIWebTablet = null;
var MSECS_PER_SEC = 1000.0;
var MUTE_MICROPHONE_MENU_ITEM = "Mute Microphone";
var gTablet = null;
Script.include("../libraries/WebTablet.js"); Script.include("../libraries/WebTablet.js");
function checkTablet() {
if (gTablet === null) {
gTablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
}
}
function tabletIsValid() { function tabletIsValid() {
if (!UIWebTablet) { if (!UIWebTablet) {
return false; return false;
@ -49,7 +58,8 @@
} }
function getTabletScalePercentageFromSettings() { function getTabletScalePercentageFromSettings() {
var toolbarMode = Tablet.getTablet("com.highfidelity.interface.tablet.system").toolbarMode; checkTablet()
var toolbarMode = gTablet.toolbarMode;
var tabletScalePercentage = DEFAULT_TABLET_SCALE; var tabletScalePercentage = DEFAULT_TABLET_SCALE;
if (!toolbarMode) { if (!toolbarMode) {
if (HMD.active) { if (HMD.active) {
@ -77,6 +87,7 @@
if (debugTablet) { if (debugTablet) {
print("TABLET rezzing"); print("TABLET rezzing");
} }
checkTablet()
tabletScalePercentage = getTabletScalePercentageFromSettings(); tabletScalePercentage = getTabletScalePercentageFromSettings();
UIWebTablet = new WebTablet("qml/hifi/tablet/TabletRoot.qml", UIWebTablet = new WebTablet("qml/hifi/tablet/TabletRoot.qml",
@ -92,7 +103,8 @@
} }
function showTabletUI() { function showTabletUI() {
Tablet.getTablet("com.highfidelity.interface.tablet.system").tabletShown = true; checkTablet()
gTablet.tabletShown = true;
if (!tabletRezzed || !tabletIsValid()) { if (!tabletRezzed || !tabletIsValid()) {
closeTabletUI(); closeTabletUI();
@ -114,7 +126,8 @@
} }
function hideTabletUI() { function hideTabletUI() {
Tablet.getTablet("com.highfidelity.interface.tablet.system").tabletShown = false; checkTablet()
gTablet.tabletShown = false;
if (!UIWebTablet) { if (!UIWebTablet) {
return; return;
} }
@ -130,7 +143,8 @@
} }
function closeTabletUI() { function closeTabletUI() {
Tablet.getTablet("com.highfidelity.interface.tablet.system").tabletShown = false; checkTablet()
gTablet.tabletShown = false;
if (UIWebTablet) { if (UIWebTablet) {
if (UIWebTablet.onClose) { if (UIWebTablet.onClose) {
UIWebTablet.onClose(); UIWebTablet.onClose();
@ -149,17 +163,19 @@
print("TABLET closeTabletUI, UIWebTablet is null"); print("TABLET closeTabletUI, UIWebTablet is null");
} }
tabletRezzed = false; tabletRezzed = false;
gTablet = null
} }
function updateShowTablet() { function updateShowTablet() {
var MSECS_PER_SEC = 1000.0;
var now = Date.now(); var now = Date.now();
checkTablet()
// close the WebTablet if it we go into toolbar mode. // close the WebTablet if it we go into toolbar mode.
var tabletShown = Tablet.getTablet("com.highfidelity.interface.tablet.system").tabletShown; var tabletShown = gTablet.tabletShown;
var toolbarMode = Tablet.getTablet("com.highfidelity.interface.tablet.system").toolbarMode; var toolbarMode = gTablet.toolbarMode;
var landscape = Tablet.getTablet("com.highfidelity.interface.tablet.system").landscape; var landscape = gTablet.landscape;
if (tabletShown && toolbarMode) { if (tabletShown && toolbarMode) {
closeTabletUI(); closeTabletUI();
@ -167,18 +183,20 @@
return; return;
} }
//TODO: move to tablet qml?
if (tabletShown) { if (tabletShown) {
var MUTE_MICROPHONE_MENU_ITEM = "Mute Microphone";
var currentMicEnabled = !Menu.isOptionChecked(MUTE_MICROPHONE_MENU_ITEM); var currentMicEnabled = !Menu.isOptionChecked(MUTE_MICROPHONE_MENU_ITEM);
var currentMicLevel = getMicLevel(); var currentMicLevel = getMicLevel();
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); gTablet.updateMicEnabled(currentMicEnabled);
tablet.updateMicEnabled(currentMicEnabled); gTablet.updateAudioBar(currentMicLevel);
tablet.updateAudioBar(currentMicLevel);
} }
updateTabletWidthFromSettings(); if (validCheckTime - now > MSECS_PER_SEC/4) {
if (UIWebTablet) { //each 250ms should be just fine
UIWebTablet.setLandscape(landscape); updateTabletWidthFromSettings();
if (UIWebTablet) {
UIWebTablet.setLandscape(landscape);
}
} }
if (validCheckTime - now > MSECS_PER_SEC) { if (validCheckTime - now > MSECS_PER_SEC) {
@ -217,21 +235,20 @@
// also cause the stylus model to be loaded // also cause the stylus model to be loaded
var tmpStylusID = Overlays.addOverlay("model", { var tmpStylusID = Overlays.addOverlay("model", {
name: "stylus", name: "stylus",
url: Script.resourcesPath() + "meshes/tablet-stylus-fat.fbx", url: Script.resourcesPath() + "meshes/tablet-stylus-fat.fbx",
loadPriority: 10.0, loadPriority: 10.0,
position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.1, z: -2})), position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.1, z: -2})),
dimensions: { x: 0.01, y: 0.01, z: 0.2 }, dimensions: { x: 0.01, y: 0.01, z: 0.2 },
solid: true, solid: true,
visible: true, visible: true,
ignoreRayIntersection: true, ignoreRayIntersection: true,
drawInFront: false, drawInFront: false,
lifetime: 3 lifetime: 3
}); });
Script.setTimeout(function() { Script.setTimeout(function() {
Overlays.deleteOverlay(tmpStylusID); Overlays.deleteOverlay(tmpStylusID);
}, 300); }, 300);
} else if (!tabletShown) { } else if (!tabletShown) {
hideTabletUI(); hideTabletUI();
} }
@ -246,7 +263,8 @@
} }
if (channel === "home") { if (channel === "home") {
if (UIWebTablet) { if (UIWebTablet) {
Tablet.getTablet("com.highfidelity.interface.tablet.system").landscape = false; checkTablet()
gTablet.landscape = false;
} }
} }
} }
@ -257,30 +275,10 @@
Script.setInterval(updateShowTablet, 100); Script.setInterval(updateShowTablet, 100);
// Initialise variables used to calculate audio level
var accumulatedLevel = 0.0;
// Note: Might have to tweak the following two based on the rate we're getting the data
var AVERAGING_RATIO = 0.05;
// Calculate microphone level with the same scaling equation (log scale, exponentially averaged) in AvatarInputs and pal.js // Calculate microphone level with the same scaling equation (log scale, exponentially averaged) in AvatarInputs and pal.js
function getMicLevel() { function getMicLevel() {
var LOUDNESS_FLOOR = 11.0; //reuse already existing C++ code
var LOUDNESS_SCALE = 2.8 / 5.0; return AvatarInputs.loudnessToAudioLevel(MyAvatar.audioLoudness)
var LOG2 = Math.log(2.0);
var micLevel = 0.0;
accumulatedLevel = AVERAGING_RATIO * accumulatedLevel + (1 - AVERAGING_RATIO) * (MyAvatar.audioLoudness);
// Convert to log base 2
var logLevel = Math.log(accumulatedLevel + 1) / LOG2;
if (logLevel <= LOUDNESS_FLOOR) {
micLevel = logLevel / LOUDNESS_FLOOR * LOUDNESS_SCALE;
} else {
micLevel = (logLevel - (LOUDNESS_FLOOR - 1.0)) * LOUDNESS_SCALE;
}
if (micLevel > 1.0) {
micLevel = 1.0;
}
return micLevel;
} }
Script.scriptEnding.connect(function () { Script.scriptEnding.connect(function () {

View file

@ -42,7 +42,7 @@ const appIcon = path.join(__dirname, '../resources/console.png');
const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Days const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Days
const LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/; const LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/;
const HOME_CONTENT_URL = "http://cdn.highfidelity.com/content-sets/home-tutorial-28.tar.gz"; const HOME_CONTENT_URL = "http://cdn.highfidelity.com/content-sets/home-tutorial-RC39.tar.gz";
function getBuildInfo() { function getBuildInfo() {
var buildInfoPath = null; var buildInfoPath = null;

3
tutorial/Changelog.md Normal file
View file

@ -0,0 +1,3 @@
* home-tutorial-34
* Update tutorial to only start if `HMD.active`
* Update builder's grid to use "Good - Sub-meshes" for collision shape type