Merge branch 'master' of https://github.com/highfidelity/hifi into audio/throttle-filter

This commit is contained in:
Zach Pomerantz 2017-01-31 19:35:27 +00:00
commit 1f56814714
152 changed files with 5713 additions and 1912 deletions

View file

@ -266,6 +266,9 @@ void Agent::handleSelectedAudioFormat(QSharedPointer<ReceivedMessage> message) {
}
void Agent::selectAudioFormat(const QString& selectedCodecName) {
if (_selectedCodecName == selectedCodecName) {
return;
}
_selectedCodecName = selectedCodecName;
qDebug() << "Selected Codec:" << _selectedCodecName;
@ -351,9 +354,15 @@ void Agent::executeScript() {
Transform audioTransform;
audioTransform.setTranslation(scriptedAvatar->getPosition());
audioTransform.setRotation(scriptedAvatar->getOrientation());
AbstractAudioInterface::emitAudioPacket(audio.data(), audio.size(), audioSequenceNumber,
QByteArray encodedBuffer;
if (_encoder) {
_encoder->encode(audio, encodedBuffer);
} else {
encodedBuffer = audio;
}
AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber,
audioTransform, scriptedAvatar->getPosition(), glm::vec3(0),
PacketType::MicrophoneAudioNoEcho);
PacketType::MicrophoneAudioNoEcho, _selectedCodecName);
});
auto avatarHashMap = DependencyManager::set<AvatarHashMap>();

View file

@ -294,7 +294,9 @@ void EntityServer::readAdditionalConfiguration(const QJsonObject& settingsSectio
}
if (readOptionString("entityEditFilter", settingsSectionObject, _entityEditFilter) && !_entityEditFilter.isEmpty()) {
// Fetch script from file synchronously. We don't want the server processing edits while a restarting entity server is fetching from a DOS'd source.
// Tell the tree that we have a filter, so that it doesn't accept edits until we have a filter function set up.
std::static_pointer_cast<EntityTree>(_tree)->setHasEntityFilter(true);
// Now fetch script from file asynchronously.
QUrl scriptURL(_entityEditFilter);
// The following should be abstracted out for use in Agent.cpp (and maybe later AvatarMixer.cpp)
@ -314,7 +316,7 @@ void EntityServer::readAdditionalConfiguration(const QJsonObject& settingsSectio
// FIXME: handle atp rquests setup here. See Agent::requestScript()
qInfo() << "Requesting script at URL" << qPrintable(scriptRequest->getUrl().toString());
scriptRequest->send();
_scriptRequestLoop.exec(); // Block here, but allow the request to be processed and its signals to be handled.
qDebug() << "script request sent";
}
}
@ -351,6 +353,7 @@ static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName
return false;
}
void EntityServer::scriptRequestFinished() {
qDebug() << "script request completed";
auto scriptRequest = qobject_cast<ResourceRequest*>(sender());
const QString urlString = scriptRequest->getUrl().toString();
if (scriptRequest && scriptRequest->getResult() == ResourceRequest::Success) {
@ -364,9 +367,7 @@ void EntityServer::scriptRequestFinished() {
return hadUncaughtExceptions(_entityEditFilterEngine, _entityEditFilter);
});
scriptRequest->deleteLater();
if (_scriptRequestLoop.isRunning()) {
_scriptRequestLoop.quit();
}
qDebug() << "script request filter processed";
return;
}
}
@ -379,10 +380,8 @@ void EntityServer::scriptRequestFinished() {
}
// Hard stop of the assignment client on failure. We don't want anyone to think they have a filter in place when they don't.
// Alas, only indications will be the above logging with assignment client restarting repeatedly, and clients will not see any entities.
qDebug() << "script request failure causing stop";
stop();
if (_scriptRequestLoop.isRunning()) {
_scriptRequestLoop.quit();
}
}
void EntityServer::nodeAdded(SharedNodePointer node) {

View file

@ -80,7 +80,6 @@ private:
QString _entityEditFilter{};
QScriptEngine _entityEditFilterEngine{};
QEventLoop _scriptRequestLoop{};
};
#endif // hifi_EntityServer_h

View file

@ -6,8 +6,8 @@ if (WIN32)
include(ExternalProject)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi6.zip
URL_MD5 fcac808c1ba0b0f5b44ea06e2612ebab
URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi7.zip
URL_MD5 bc2861e50852dd590cdc773a14a041a7
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""

View file

@ -372,6 +372,13 @@
"help": "Password used for basic HTTP authentication. Leave this blank if you do not want to change it.",
"value-hidden": true
},
{
"name": "verify_http_password",
"label": "Verify HTTP Password",
"type": "password",
"help": "Must match the password entered above for change to be saved.",
"value-hidden": true
},
{
"name": "maximum_user_capacity",
"label": "Maximum User Capacity",

View file

@ -904,10 +904,18 @@ function saveSettings() {
var formJSON = form2js('settings-form', ".", false, cleanupFormValues, true);
// check if we've set the basic http password - if so convert it to base64
var canPost = true;
if (formJSON["security"]) {
var password = formJSON["security"]["http_password"];
var verify_password = formJSON["security"]["verify_http_password"];
if (password && password.length > 0) {
formJSON["security"]["http_password"] = sha256_digest(password);
if (password != verify_password) {
bootbox.alert({"message": "Passwords must match!", "title":"Password Error"});
canPost = false;
} else {
formJSON["security"]["http_password"] = sha256_digest(password);
delete formJSON["security"]["verify_http_password"];
}
}
}
@ -923,7 +931,9 @@ function saveSettings() {
$(this).blur();
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
postSettings(formJSON);
if (canPost) {
postSettings(formJSON);
}
}
$('body').on('click', '.save-button', function(e){

View file

@ -1,50 +1,12 @@
{
"name": "Standard to Action",
"when": "Application.NavigationFocused",
"name": "Standard to UI Navigation Action",
"channels": [
{ "from": "Standard.DU", "to": "Actions.UiNavVertical" },
{ "from": "Standard.DD", "to": "Actions.UiNavVertical", "filters": "invert" },
{ "from": "Standard.DL", "to": "Actions.UiNavLateral", "filters": "invert" },
{ "from": "Standard.DR", "to": "Actions.UiNavLateral" },
{ "from": "Standard.LB", "to": "Actions.UiNavGroup","filters": "invert" },
{ "from": "Standard.RB", "to": "Actions.UiNavGroup" },
{ "from": [ "Standard.A", "Standard.X" ], "to": "Actions.UiNavSelect" },
{ "from": [ "Standard.B", "Standard.Y" ], "to": "Actions.UiNavBack" },
{ "from": [ "Standard.RTClick", "Standard.LTClick" ], "to": "Actions.UiNavSelect" },
{
"from": "Standard.LX", "to": "Actions.UiNavLateral",
"filters": [
{ "type": "deadZone", "min": 0.95 },
"constrainToInteger",
{ "type": "pulse", "interval": 0.4 }
]
},
{
"from": "Standard.LY", "to": "Actions.UiNavVertical",
"filters": [
"invert",
{ "type": "deadZone", "min": 0.95 },
"constrainToInteger",
{ "type": "pulse", "interval": 0.4 }
]
},
{
"from": "Standard.RX", "to": "Actions.UiNavLateral",
"filters": [
{ "type": "deadZone", "min": 0.95 },
"constrainToInteger",
{ "type": "pulse", "interval": 0.4 }
]
},
{
"from": "Standard.RY", "to": "Actions.UiNavVertical",
"filters": [
"invert",
{ "type": "deadZone", "min": 0.95 },
"constrainToInteger",
{ "type": "pulse", "interval": 0.4 }
]
}
{ "from": "Standard.RB", "to": "Actions.UiNavGroup" }
]
}

View file

@ -56,6 +56,9 @@
{ "from": "GamePad.A", "to": "Standard.A" },
{ "from": "GamePad.B", "to": "Standard.B" },
{ "from": "GamePad.X", "to": "Standard.X" },
{ "from": "GamePad.Y", "to": "Standard.Y" }
{ "from": "GamePad.Y", "to": "Standard.Y" },
{ "from": [ "Standard.A", "Standard.X" ], "to": "Actions.UiNavSelect" },
{ "from": [ "Standard.B", "Standard.Y" ], "to": "Actions.UiNavBack" }
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 124 KiB

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="70mm"
height="70mm"
viewBox="0 0 248.0315 248.03149"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="circle.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.979899"
inkscape:cx="79.162615"
inkscape:cy="188.36943"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="2560"
inkscape:window-height="1377"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-804.33071)">
<circle
style="fill:#4b504f;fill-opacity:1;stroke:#feffff;stroke-width:0.22076035;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4140"
cx="124.46446"
cy="928.42853"
r="121.41819" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 50 49.999998"
xml:space="preserve"
id="svg4239"
inkscape:version="0.91 r13725"
sodipodi:docname="edit-icon.svg"
width="50"
height="50"><metadata
id="metadata4336"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs4334" /><sodipodi:namedview
pagecolor="#615f71"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1663"
inkscape:window-height="1023"
id="namedview4332"
showgrid="false"
inkscape:zoom="4.717641"
inkscape:cx="27.234437"
inkscape:cy="40.361616"
inkscape:window-x="412"
inkscape:window-y="188"
inkscape:window-maximized="0"
inkscape:current-layer="svg4239" /><style
type="text/css"
id="style4241">
.st0{fill:#414042;}
.st1{fill:#FFFFFF;}
.st2{fill:#1E1E1E;}
.st3{fill:#333333;}
</style><g
id="Layer_3"
transform="matrix(2.0055009,0,0,2.0055009,-26.481547,-324.23978)" /><path
style="fill:#ffffff;fill-opacity:1"
inkscape:connector-curvature="0"
id="path4268"
d="m 17.615466,29.206401 c -2.807702,2.807702 -5.615403,5.615403 -8.4231037,8.423104 -0.601651,-0.60165 -1.403851,-1.40385 -2.005501,-2.005501 2.807701,-2.807701 5.6154017,-5.615402 8.4231037,-8.423104 l -2.406601,-2.206051 c -3.2088017,3.208802 -6.4176027,6.417603 -9.8269547,9.626405 -0.60165,0.60165 -1.00275,1.40385 -1.2033,2.406601 -0.401101,2.607151 -0.802201,5.214302 -1.20330104,8.022004 0.20055004,0 0.40110004,0 0.60165004,0 2.607151,-0.601651 5.013753,-1.002751 7.620904,-1.604401 0.4011,-0.20055 1.0027497,-0.4011 1.4038497,-0.802201 3.409352,-3.409351 6.818703,-6.818703 10.228055,-10.228054 l -3.208801,-3.208802 z"
class="st3" /><path
style="fill:#ffffff;fill-opacity:1"
inkscape:connector-curvature="0"
id="path4270"
d="m 30.851772,7.3464388 c 1.00275,-1.00275 2.206051,-2.206051 3.409351,-3.409351 0.4011,-0.4011 1.002751,-0.4011 1.403851,0 2.005501,2.005501 4.011002,4.011002 6.016502,6.016503 0.401101,0.4011002 0.401101,1.0027502 0,1.6044002 -1.2033,1.203303 -2.406601,2.406603 -3.409351,3.409354 -2.406601,-2.406603 -4.813202,-5.0137542 -7.420353,-7.6209062 z"
class="st3" /><path
style="fill:#ffffff;fill-opacity:1"
inkscape:connector-curvature="0"
id="path4272"
d="m 33.659473,25.9976 c -0.60165,0.20055 -0.802201,0 -1.203301,-0.4011 -0.8022,-0.802201 -1.6044,-1.604401 -2.406601,-2.406601 2.005501,-2.005501 3.810452,-3.810452 5.815953,-5.815953 0.20055,-0.20055 0.4011,-0.4011 0.4011,-0.4011 -2.607151,-2.607151 -5.013752,-5.013754 -7.620903,-7.8214562 -0.200551,0.20055 -0.200551,0.4011 -0.401101,0.601651 -2.005501,2.0055002 -3.810451,3.8104532 -5.815952,5.8159542 -3.609902,-3.609903 -7.019253,-7.0192552 -10.629155,-10.6291572 -0.20055,-0.20055 -0.60165,-0.60165 -0.8022,-0.8022 -2.0055007,-1.403851 -4.4121017,-1.203301 -6.0165027,0.60165 -1.604401,1.804951 -1.403851,4.412102 0.20055,6.0165032 5.2143017,5.214304 10.6291547,10.629157 15.8434567,15.843459 1.804951,1.804951 3.409352,3.409352 5.214302,5.214303 0.200551,0.20055 0.401101,0.4011 0.200551,0.8022 -0.200551,0.8022 -0.401101,1.604401 -0.401101,2.406601 -0.4011,5.013752 2.807702,9.425854 7.821454,10.829705 2.005501,0.60165 4.011002,0.4011 6.217053,-0.4011 -0.20055,-0.20055 -0.4011,-0.4011 -0.601651,-0.4011 -1.40385,-1.403851 -3.008251,-3.008252 -4.412102,-4.412102 -1.00275,-1.002751 -1.2033,-2.807702 -0.20055,-3.810452 0.802201,-0.802201 1.604401,-1.604401 2.406601,-2.406601 1.203301,-1.002751 2.607152,-1.002751 3.609902,0 0.20055,0.20055 0.4011,0.4011 0.60165,0.60165 1.403851,1.403851 3.008252,3.008251 4.412102,4.412102 0,0 0.20055,0 0.20055,0 0.200551,-0.8022 0.401101,-1.604401 0.401101,-2.607151 C 47.697979,29.607502 40.678726,23.992099 33.659473,25.9976 Z M 8.1896113,9.1513898 c -0.8022,0 -1.40385,-0.60165 -1.40385,-1.40385 0,-0.802201 0.60165,-1.604401 1.40385,-1.604401 0.802201,0 1.604401,0.8022 1.604401,1.403851 0,0.8022 -0.8022,1.6044 -1.604401,1.6044 z M 29.046821,13.764044 c 0.60165,0.601651 1.2033,1.403851 2.005501,2.005501 -1.403851,1.403851 -2.807702,2.807702 -4.211552,4.211552 -0.601651,-0.60165 -1.403851,-1.40385 -2.005501,-2.005501 1.40385,-1.40385 2.807701,-2.807701 4.211552,-4.211552 z"
class="st3" /></svg>

After

Width:  |  Height:  |  Size: 5 KiB

View file

@ -0,0 +1,46 @@
<?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" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="Layer_2">
</g>
<g id="Layer_1">
<g>
<path class="st0" d="M23.2,24.1c-0.8,0.9-1.5,1.8-2.2,2.6c-0.1,0.2-0.1,0.5-0.1,0.7c0.1,1.7,0.2,3.4,0.2,5.1
c0,0.8-0.4,1.2-1.1,1.3c-0.7,0.1-1.3-0.4-1.4-1.1c-0.2-2.2-0.3-4.3-0.5-6.5c0-0.3,0.1-0.7,0.4-1c1.1-1.5,2.3-3,3.4-4.5
c0.6-0.7,1.6-1.6,2.6-1.6c0.3,0,1.1,0,1.4,0c0.8-0.1,1.3,0.1,1.9,0.9c1,1.2,1.5,2.3,2.4,3.6c0.7,1.1,1.4,1.6,2.9,1.9
c1.1,0.2,2.2,0.5,3.3,0.8c0.3,0.1,0.6,0.2,0.8,0.3c0.5,0.3,0.7,0.8,0.6,1.3c-0.1,0.5-0.5,0.7-1,0.8c-0.4,0-0.9,0-1.3-0.1
c-1.4-0.3-2.7-0.6-4.1-0.9c-0.8-0.2-1.5-0.6-2.1-1.1c-0.3-0.3-0.6-0.5-0.9-0.8c0,0.3,0,0.5,0,0.7c0,1.2,0,2.4,0,3.6
c0,0.4-0.3,12.6-0.1,16.8c0,0.5-0.1,1-0.2,1.5c-0.2,0.7-0.6,1-1.4,1.1c-0.8,0-1.4-0.3-1.7-1c-0.2-0.5-0.3-1.1-0.4-1.6
c-0.4-4.6-0.9-12.9-1.1-13.8c-0.1-0.8-0.2-1.1-0.3-2.1c-0.1-0.5-0.1-0.9-0.1-1.3C23.3,27.9,23.2,26.1,23.2,24.1z"/>
<path class="st0" d="M28.2,14.6c0,1.4-1.1,2.6-2.6,2.6l0,0c-1.4,0-2.6-1.1-2.6-2.6v-1.6c0-1.4,1.1-2.6,2.6-2.6l0,0
c1.4,0,2.6,1.1,2.6,2.6V14.6z"/>
</g>
<path class="st0" d="M8.4,38.9c2.8,3.2,6.4,5.5,10.5,6.7c0.6,0.2,1.3,0.1,1.7-0.3c0.4-0.3,0.6-0.6,0.7-1c0.2-0.5,0.1-1.1-0.2-1.5
c-0.3-0.5-0.7-0.8-1.2-1c-1.6-0.5-3.2-1.2-4.6-2.1c-1.5-0.9-2.8-2.1-4-3.4c-0.4-0.4-0.9-0.7-1.5-0.7c-0.5,0-1,0.2-1.3,0.5
c-0.4,0.4-0.6,0.8-0.7,1.4C7.8,38,8,38.5,8.4,38.9z"/>
<path class="st0" d="M43.7,36.8c0.3-0.4,0.4-1,0.3-1.5c-0.1-0.5-0.4-1-0.8-1.3c-0.3-0.2-0.7-0.3-1.1-0.3c-0.7,0-1.3,0.3-1.7,0.9
c-1.1,1.6-2.4,3-4,4.2c-1.2,0.9-2.5,1.7-3.9,2.3c-0.5,0.2-0.9,0.6-1.1,1.1c-0.2,0.5-0.2,1,0,1.5c0.4,1,1.6,1.5,2.6,1
c1.7-0.7,3.3-1.7,4.8-2.8C40.7,40.4,42.4,38.7,43.7,36.8z"/>
<path class="st0" d="M5.1,33.2c0.5,0.4,1.2,0.4,1.8,0.2c0.5-0.2,0.9-0.6,1.1-1.1c0.2-0.5,0.2-1,0-1.5c-0.1-0.4-0.4-0.7-0.7-0.9
c-0.3-0.2-0.7-0.3-1.1-0.3c-0.2,0-0.5,0-0.7,0.1c-1,0.4-1.5,1.6-1.1,2.6C4.5,32.7,4.7,33,5.1,33.2z"/>
<path class="st0" d="M45.4,27.3c-0.2,0-0.3-0.1-0.5-0.1c-0.9,0-1.7,0.6-1.9,1.5c-0.1,0.5-0.1,1.1,0.2,1.5c0.3,0.5,0.7,0.8,1.2,0.9
c0.2,0,0.3,0.1,0.5,0.1c0.9,0,1.7-0.6,1.9-1.5c0.1-0.5,0.1-1.1-0.2-1.5C46.4,27.8,45.9,27.5,45.4,27.3z"/>
<path class="st0" d="M8.6,12c-0.3-0.2-0.7-0.3-1-0.3c-0.3,0-0.7,0.1-1,0.3c-0.3,0.2-0.6,0.4-0.7,0.7c-2,3.5-3.1,7.4-3.1,11.4
c0,0.2,0,0.4,0,0.6c0,0.5,0.2,1,0.6,1.4c0.4,0.4,0.9,0.6,1.4,0.6v0.4l0.1-0.4c0.5,0,1-0.2,1.4-0.6c0.4-0.4,0.6-0.9,0.5-1.4
c0-0.2,0-0.4,0-0.5c0-3.3,0.9-6.6,2.6-9.4C9.9,13.8,9.6,12.6,8.6,12z"/>
<path class="st0" d="M39.3,11.4c-0.1,0.5,0.1,1.1,0.4,1.5c1.1,1.4,2,3,2.6,4.6c0.6,1.6,1,3.2,1.2,4.9c0,0.5,0.3,1,0.6,1.3
c0.4,0.4,1,0.6,1.5,0.5c0.5,0,1-0.3,1.4-0.7c0.3-0.4,0.5-0.9,0.5-1.5c-0.4-4.2-2-8.2-4.6-11.6c-0.4-0.5-1-0.8-1.6-0.8
c-0.4,0-0.9,0.1-1.2,0.4C39.7,10.4,39.4,10.8,39.3,11.4z"/>
<path class="st0" d="M12.2,6.3c-0.5,0-0.9,0.2-1.3,0.5c-0.4,0.3-0.7,0.8-0.7,1.4c-0.1,0.5,0.1,1.1,0.4,1.5c0.7,0.8,2,1,2.8,0.3
c0.4-0.3,0.7-0.8,0.7-1.3c0.1-0.5-0.1-1.1-0.4-1.5C13.4,6.6,12.8,6.3,12.2,6.3z"/>
<path class="st0" d="M37.2,5.2c-0.3-0.2-0.7-0.3-1.1-0.3c-0.7,0-1.3,0.3-1.7,0.9c-0.3,0.4-0.4,1-0.3,1.5c0.1,0.5,0.4,1,0.9,1.3
C36,9.2,37.3,8.9,37.9,8C38.4,7.1,38.2,5.8,37.2,5.2z"/>
<path class="st0" d="M16.5,4c-0.2,0.5-0.3,1-0.1,1.5c0.4,1,1.5,1.6,2.6,1.2c3.3-1.2,6.8-1.4,10.2-0.6c0.6,0.1,1.2,0,1.7-0.4
c0.4-0.3,0.6-0.7,0.7-1.1c0.1-0.5,0-1.1-0.3-1.5c-0.3-0.5-0.7-0.8-1.3-0.9c-1.6-0.4-3.3-0.5-4.9-0.5c-2.6,0-5.1,0.4-7.5,1.3
C17.1,3.2,16.7,3.6,16.5,4z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -0,0 +1,25 @@
<?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 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="Layer_2">
</g>
<g>
<path class="st0" d="M20.7,29.7c-2.2,2.2-4.4,4.4-6.7,6.7c-0.5-0.5-1.1-1.1-1.6-1.6c2.2-2.2,4.4-4.4,6.7-6.7l-1.8-1.8
c-2.6,2.5-5.1,5.1-7.7,7.6c-0.5,0.5-0.9,1.1-1,1.8C8.3,37.8,8,39.8,7.7,42c0.2,0,0.4,0,0.5,0c2-0.4,4-0.8,5.9-1.2
c0.4-0.1,0.8-0.3,1.1-0.6c2.7-2.6,5.3-5.3,8-8L20.7,29.7z"/>
<path class="st0" d="M31.1,11c0.8-0.8,1.8-1.8,2.7-2.7C34.2,8,34.6,8,34.9,8.4c1.6,1.6,3.1,3.1,4.7,4.7c0.4,0.4,0.4,0.8,0,1.2
c-0.9,0.9-1.8,1.8-2.7,2.7C35,15,33.1,13,31.1,11z"/>
<path class="st0" d="M33,25.9c-0.4,0.1-0.6,0-0.9-0.2c-0.6-0.6-1.3-1.3-1.9-1.9c1.5-1.5,3.1-3.1,4.6-4.6c0.1-0.1,0.3-0.3,0.3-0.3
c-2-2-3.9-4-5.9-6.1c-0.1,0.2-0.2,0.3-0.4,0.5c-1.5,1.5-3,3-4.6,4.6c-2.8-2.8-5.6-5.6-8.4-8.4c-0.2-0.2-0.4-0.4-0.6-0.6
c-1.5-1.2-3.5-1-4.8,0.4c-1.2,1.4-1.1,3.5,0.2,4.8c4.2,4.2,8.3,8.3,12.5,12.5c1.4,1.4,2.7,2.7,4.1,4.1c0.2,0.2,0.2,0.4,0.2,0.7
c-0.2,0.6-0.3,1.2-0.3,1.9c-0.3,4,2.3,7.5,6.1,8.5c1.6,0.4,3.2,0.3,4.8-0.3c-0.1-0.2-0.3-0.2-0.4-0.4c-1.2-1.2-2.3-2.3-3.5-3.5
c-0.8-0.9-0.9-2.1-0.1-3c0.6-0.7,1.3-1.3,2-2c0.9-0.8,2-0.8,2.9,0c0.2,0.2,0.3,0.3,0.5,0.5c1.2,1.2,2.3,2.3,3.5,3.5
c0.1,0,0.1,0,0.2,0c0.1-0.7,0.3-1.3,0.3-2C43.9,28.7,38.5,24.3,33,25.9z M12.9,12.6c-0.6,0-1.2-0.5-1.2-1.2s0.5-1.2,1.2-1.2
c0.6,0,1.2,0.6,1.2,1.2C14.1,12,13.6,12.6,12.9,12.6z M29.3,16.3c0.5,0.5,1,1.1,1.6,1.6c-1.1,1.1-2.2,2.2-3.3,3.3
c-0.5-0.5-1.1-1.1-1.6-1.6C27.1,18.5,28.2,17.4,29.3,16.3z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,16 @@
<?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" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="Layer_2">
</g>
<g id="Layer_1">
<path class="st0" d="M47.2,41.3l-9.1-9.1c-0.8-0.8-1.9-1.1-3-1l-2.4-2.4c1.8-2.6,2.8-5.7,2.8-9c0-8.9-7.2-16.1-16.1-16.1
S3.3,11,3.3,19.8c0,8.9,7.2,16.1,16.1,16.1c4.1,0,7.8-1.5,10.6-4l2.2,2.2c-0.2,1.1,0.1,2.2,1,3l9.1,9.1c1.4,1.4,3.6,1.4,4.9,0
C48.5,44.9,48.5,42.7,47.2,41.3z M19.4,32.2c-6.8,0-12.3-5.5-12.3-12.3c0-6.8,5.5-12.3,12.3-12.3s12.3,5.5,12.3,12.3
C31.8,26.6,26.2,32.2,19.4,32.2z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 832 B

View file

@ -0,0 +1,27 @@
<?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" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{fill:#FFFFFF;stroke:#FFFFFF;stroke-width:0.25;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
</style>
<g id="Layer_2">
</g>
<g id="Layer_1">
<path class="st0" d="M11.3,36.7c-0.2-0.3-0.4-0.6-0.6-0.8C6.3,29,5.8,21.8,10,14.8c4.3-7.2,11-10.4,19.3-9.3
c7.9,1.1,13.4,5.8,15.8,13.4c2.4,7.8,0.4,14.6-5.5,20.1c-3.8,3.6-8.4,5.1-13.5,5.1c-7.3,0-14.6-0.1-21.9-0.1c-0.2,0-0.5,0-1,0
C6,41.5,8.6,39.1,11.3,36.7z M10.9,41c0.3,0,0.6,0.1,0.8,0.1c4.9,0,9.9,0,14.8,0C35.6,41,42.8,34,43,25.1
c0.2-9.3-7.3-16.9-16.6-16.7c-6.9,0.1-12,3.5-14.9,9.7c-2.9,6.4-1.8,12.5,2.8,17.8c0.3,0.4,0.7,0.7,1,1.1
C13.9,38.3,12.5,39.6,10.9,41z"/>
<path class="st1" d="M22.5,20.9c0-0.7,0.1-1.4,0.3-2c0.2-0.6,0.6-1.1,1-1.6c0.4-0.4,0.9-0.8,1.6-1c0.6-0.2,1.3-0.4,2-0.4
c0.6,0,1.2,0.1,1.7,0.3c0.5,0.2,1,0.4,1.4,0.8c0.4,0.3,0.7,0.8,1,1.3c0.2,0.5,0.3,1.1,0.3,1.8c0,0.5-0.1,0.9-0.2,1.2
c-0.1,0.3-0.2,0.6-0.4,0.9c-0.2,0.3-0.4,0.5-0.6,0.7c-0.2,0.2-0.4,0.4-0.7,0.6c-0.3,0.2-0.5,0.4-0.7,0.6c-0.2,0.2-0.4,0.4-0.6,0.7
c-0.2,0.3-0.3,0.5-0.4,0.9c-0.1,0.3-0.1,0.8-0.1,1.2H26c0-0.6,0-1.1,0.1-1.5c0.1-0.4,0.2-0.8,0.3-1.1c0.1-0.3,0.3-0.6,0.5-0.8
c0.2-0.2,0.4-0.5,0.7-0.7c0.2-0.2,0.4-0.4,0.6-0.5c0.2-0.2,0.4-0.3,0.5-0.5c0.2-0.2,0.3-0.4,0.4-0.7c0.1-0.2,0.1-0.5,0.1-0.9
c0-0.4-0.1-0.8-0.2-1.1c-0.1-0.3-0.3-0.5-0.5-0.7c-0.2-0.2-0.4-0.3-0.7-0.4c-0.2-0.1-0.4-0.1-0.6-0.1c-0.8,0-1.5,0.3-1.9,0.8
c-0.4,0.6-0.6,1.3-0.6,2.2H22.5z"/>
<path class="st0" d="M27.6,29.8c-0.1,0-0.3-0.1-0.4-0.1c-0.8,0-1.4,0.5-1.6,1.2c-0.1,0.4,0,0.9,0.2,1.3c0.2,0.4,0.6,0.7,1,0.8
c0.1,0,0.3,0.1,0.4,0.1c0.8,0,1.4-0.5,1.6-1.2c0.1-0.4,0.1-0.9-0.2-1.3C28.4,30.1,28,29.9,27.6,29.8z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1,28 @@
<?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" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="Layer_2">
</g>
<g id="Layer_1">
<path class="st0" d="M27.1,42.3c-1.4,0.3-2.7,0.5-4.1,0.5c-9.3,0-16.8-7.5-16.8-16.8S13.8,9.2,23,9.2S39.8,16.7,39.8,26
c0,0.2,0,0.3,0,0.5c0.7-0.7,1.4-1,2.4-1.5C41.7,14.9,33.3,6.8,23,6.8C12.4,6.8,3.8,15.4,3.8,26S12.4,45.2,23,45.2
c1.7,0,3.4-0.3,5.1-0.7C27.8,43.6,27.5,43,27.1,42.3z"/>
<path class="st0" d="M32.6,20.7L32.6,20.7c-0.4-0.4-0.7-0.4-1.2-0.3c0,0-5.9,0.7-8.4,0.7h-0.1c-2.5,0-8.6-0.9-8.6-0.9
c-0.4-0.1-0.9,0-1.2,0.4L13,21c-0.1,0.3-0.1,0.6-0.1,1c0.1,0.3,0.3,0.6,0.6,0.7c1,0.4,4.9,1.8,5.9,2.1c0.1,0,0.6,0.1,0.6,0.9
c0,0.9-0.3,4.6-0.7,6.4c-0.4,1.8-1.2,4-1.2,4c-0.1,0.6,0.1,1.3,0.7,1.5l0.7,0.3c0.3,0.1,0.6,0.1,0.9,0c0.3-0.1,0.4-0.4,0.6-0.7
l2.1-6.4l1.9,6.5c0.1,0.3,0.3,0.6,0.6,0.7c0.1,0.1,0.3,0.1,0.4,0.1c0.1,0,0.3,0,0.4-0.1l0.7-0.3c0.6-0.1,0.9-0.7,0.7-1.3
c0,0-0.6-2.4-1-4.3c-0.3-1.2-0.4-3-0.4-4.3c0-0.9-0.1-1.6-0.1-2.2c0-0.3,0.1-0.6,0.6-0.7c0.1,0,5.5-1.9,5.5-1.9
c0.4-0.1,0.6-0.4,0.7-0.9C32.9,21.5,32.9,21,32.6,20.7z"/>
<circle class="st0" cx="23" cy="17" r="2.5"/>
<g>
<path class="st0" d="M43.1,43L43.1,43L43.1,43z"/>
<path class="st0" d="M41,35.6l4.6-4.6c0.7-0.7,0.7-1.9,0-2.6c-0.7-0.7-1.9-0.7-2.6,0L38.5,33l-4.6-4.6c-0.7-0.7-1.9-0.7-2.6,0
s-0.7,1.9,0,2.6l4.6,4.6l-4.6,4.6c-0.7,0.7-0.7,1.9,0,2.6s1.9,0.7,2.6,0l4.6-4.6l4.6,4.6c0.7,0.7,1.9,0.7,2.6,0
c0.7-0.7,0.7-1.9,0-2.6L41,35.6z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,177 @@
<?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 50 200.1" style="enable-background:new 0 0 50 200.1;" xml:space="preserve">
<style type="text/css">
.st0{fill:#414042;}
.st1{fill:#FFFFFF;}
.st2{fill:#1E1E1E;}
.st3{fill:#333333;}
</style>
<g id="Layer_2">
<g>
<g>
<path class="st0" d="M50.1,146.1c0,2.2-1.8,4-4,4h-42c-2.2,0-4-1.8-4-4v-42c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V146.1z"/>
</g>
</g>
<g>
<g>
<path class="st0" d="M50,196.1c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4v-42c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V196.1z"/>
</g>
</g>
<g>
<g>
<path class="st1" d="M50,46c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4V4c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V46z"/>
</g>
</g>
<g>
<path class="st2" d="M50,96.1c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4v-42c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V96.1z"/>
</g>
</g>
<path class="st1" d="M24.7,81.2c-6.2,0-11.2-5-11.2-11.2c0-6.2,5-11.2,11.2-11.2c6.2,0,11.2,5,11.2,11.2
C35.9,76.2,30.9,81.2,24.7,81.2z M24.7,60.3c-5.4,0-9.8,4.4-9.8,9.8c0,5.4,4.4,9.8,9.8,9.8c5.4,0,9.8-4.4,9.8-9.8
C34.5,64.6,30.1,60.3,24.7,60.3z"/>
<g>
<path class="st3" d="M7.7,42.5v-6.4h1.2v6.4H7.7z"/>
<path class="st3" d="M14.7,41.8c-0.5,0.5-1.1,0.8-1.7,0.8c-0.4,0-0.8-0.1-1.2-0.3c-0.4-0.2-0.7-0.4-0.9-0.7c-0.3-0.3-0.5-0.7-0.6-1
s-0.2-0.8-0.2-1.2c0-0.4,0.1-0.9,0.2-1.2c0.2-0.4,0.4-0.7,0.6-1c0.3-0.3,0.6-0.5,1-0.7c0.4-0.2,0.8-0.3,1.2-0.3
c0.6,0,1.1,0.1,1.5,0.4s0.7,0.6,0.9,1l-0.9,0.7c-0.2-0.3-0.4-0.6-0.6-0.7s-0.6-0.3-0.9-0.3c-0.3,0-0.5,0.1-0.7,0.2
c-0.2,0.1-0.4,0.3-0.5,0.5c-0.2,0.2-0.3,0.4-0.4,0.7c-0.1,0.3-0.1,0.5-0.1,0.8c0,0.3,0,0.6,0.1,0.8c0.1,0.3,0.2,0.5,0.4,0.7
c0.2,0.2,0.4,0.3,0.6,0.5c0.2,0.1,0.5,0.2,0.7,0.2c0.6,0,1.1-0.3,1.6-0.9v-0.5h-1.3v-0.9h2.3v3.3h-1V41.8z"/>
<path class="st3" d="M18.3,38.4v4.1h-1.2v-6.4h1l3.3,4.2v-4.2h1.2v6.4h-1L18.3,38.4z"/>
<path class="st3" d="M26.8,42.5c-0.5,0-0.9-0.1-1.2-0.3s-0.7-0.4-1-0.7c-0.3-0.3-0.5-0.6-0.6-1c-0.1-0.4-0.2-0.8-0.2-1.2
c0-0.4,0.1-0.8,0.2-1.2s0.4-0.7,0.6-1c0.3-0.3,0.6-0.5,1-0.7s0.8-0.3,1.2-0.3c0.5,0,0.9,0.1,1.2,0.3s0.7,0.4,1,0.7
c0.3,0.3,0.5,0.7,0.6,1c0.1,0.4,0.2,0.8,0.2,1.2c0,0.4-0.1,0.8-0.2,1.2c-0.2,0.4-0.4,0.7-0.6,1c-0.3,0.3-0.6,0.5-1,0.7
C27.7,42.4,27.2,42.5,26.8,42.5z M25,39.3c0,0.3,0,0.5,0.1,0.8c0.1,0.3,0.2,0.5,0.4,0.7c0.2,0.2,0.3,0.4,0.6,0.5s0.5,0.2,0.8,0.2
c0.3,0,0.5-0.1,0.8-0.2s0.4-0.3,0.6-0.5c0.1-0.2,0.3-0.4,0.3-0.7s0.1-0.5,0.1-0.8c0-0.3,0-0.5-0.1-0.8c-0.1-0.3-0.2-0.5-0.4-0.7
c-0.2-0.2-0.3-0.4-0.6-0.5c-0.2-0.1-0.5-0.2-0.7-0.2c-0.3,0-0.5,0.1-0.8,0.2s-0.4,0.3-0.6,0.5c-0.2,0.2-0.3,0.4-0.3,0.7
C25.1,38.8,25,39,25,39.3z"/>
<path class="st3" d="M31,42.5v-6.4h2.8c0.3,0,0.6,0.1,0.8,0.2s0.5,0.3,0.6,0.5c0.2,0.2,0.3,0.4,0.4,0.7c0.1,0.3,0.2,0.5,0.2,0.8
c0,0.4-0.1,0.8-0.3,1.1c-0.2,0.3-0.5,0.6-0.8,0.7l1.5,2.4h-1.4l-1.3-2.1h-1.2v2.1H31z M32.3,39.3h1.6c0.1,0,0.2,0,0.3-0.1
c0.1-0.1,0.2-0.1,0.3-0.2c0.1-0.1,0.1-0.2,0.2-0.3s0.1-0.3,0.1-0.4c0-0.1,0-0.3-0.1-0.4s-0.1-0.2-0.2-0.3c-0.1-0.1-0.2-0.2-0.3-0.2
c-0.1-0.1-0.2-0.1-0.3-0.1h-1.5V39.3z"/>
<path class="st3" d="M41.8,41.4v1.1h-4.4v-6.4h4.4v1.1h-3.1v1.5h2.7v1h-2.7v1.7H41.8z"/>
</g>
<path class="st1" d="M27.1,79.5c-0.8,0.2-1.6,0.3-2.4,0.3c-5.4,0-9.8-4.4-9.8-9.8c0-5.4,4.4-9.8,9.8-9.8c5.4,0,9.8,4.4,9.8,9.8
c0,0.1,0,0.2,0,0.3c0.4-0.4,0.8-0.6,1.4-0.9c-0.3-5.9-5.2-10.6-11.2-10.6c-6.2,0-11.2,5-11.2,11.2c0,6.2,5,11.2,11.2,11.2
c1,0,2-0.2,3-0.4C27.5,80.4,27.3,80,27.1,79.5z"/>
<path class="st1" d="M31.2,66.5L31.2,66.5c-0.3-0.3-0.5-0.3-0.8-0.2c0,0-4,0.5-5.7,0.5c0,0-0.1,0-0.1,0c-1.7,0-5.8-0.6-5.8-0.6
c-0.3-0.1-0.6,0-0.8,0.3l-0.1,0.2c-0.1,0.2-0.1,0.4-0.1,0.7c0.1,0.2,0.2,0.4,0.4,0.5c0.7,0.3,3.3,1.2,4,1.4c0.1,0,0.4,0.1,0.4,0.6
c0,0.6-0.2,3.1-0.5,4.3c-0.3,1.2-0.8,2.7-0.8,2.7c-0.1,0.4,0.1,0.9,0.5,1l0.5,0.2c0.2,0.1,0.4,0.1,0.6,0c0.2-0.1,0.3-0.3,0.4-0.5
l1.4-4.3l1.3,4.4c0.1,0.2,0.2,0.4,0.4,0.5c0.1,0.1,0.2,0.1,0.3,0.1c0.1,0,0.2,0,0.3-0.1l0.5-0.2c0.4-0.1,0.6-0.5,0.5-0.9
c0,0-0.4-1.6-0.7-2.9c-0.2-0.8-0.3-2-0.3-2.9c0-0.6-0.1-1.1-0.1-1.5c0-0.2,0.1-0.4,0.4-0.5c0.1,0,3.7-1.3,3.7-1.3
c0.3-0.1,0.4-0.3,0.5-0.6C31.4,66.9,31.4,66.7,31.2,66.5z"/>
<circle class="st1" cx="24.7" cy="64" r="1.7"/>
<g>
<path class="st1" d="M7.7,92.3V86h1.2v6.4H7.7z"/>
<path class="st1" d="M14.7,91.6c-0.5,0.5-1.1,0.8-1.7,0.8c-0.4,0-0.8-0.1-1.2-0.3c-0.4-0.2-0.7-0.4-0.9-0.7c-0.3-0.3-0.5-0.7-0.6-1
s-0.2-0.8-0.2-1.2c0-0.4,0.1-0.9,0.2-1.2c0.2-0.4,0.4-0.7,0.6-1c0.3-0.3,0.6-0.5,1-0.7c0.4-0.2,0.8-0.3,1.2-0.3
c0.6,0,1.1,0.1,1.5,0.4s0.7,0.6,0.9,1L14.6,88c-0.2-0.3-0.4-0.6-0.6-0.7S13.4,87,13.1,87c-0.3,0-0.5,0.1-0.7,0.2
c-0.2,0.1-0.4,0.3-0.5,0.5c-0.2,0.2-0.3,0.4-0.4,0.7c-0.1,0.3-0.1,0.5-0.1,0.8c0,0.3,0,0.6,0.1,0.8c0.1,0.3,0.2,0.5,0.4,0.7
c0.2,0.2,0.4,0.3,0.6,0.5c0.2,0.1,0.5,0.2,0.7,0.2c0.6,0,1.1-0.3,1.6-0.9V90h-1.3v-0.9h2.3v3.3h-1V91.6z"/>
<path class="st1" d="M18.3,88.2v4.1h-1.2V86h1l3.3,4.2V86h1.2v6.4h-1L18.3,88.2z"/>
<path class="st1" d="M26.8,92.4c-0.5,0-0.9-0.1-1.2-0.3s-0.7-0.4-1-0.7c-0.3-0.3-0.5-0.6-0.6-1c-0.1-0.4-0.2-0.8-0.2-1.2
c0-0.4,0.1-0.8,0.2-1.2s0.4-0.7,0.6-1c0.3-0.3,0.6-0.5,1-0.7s0.8-0.3,1.2-0.3c0.5,0,0.9,0.1,1.2,0.3s0.7,0.4,1,0.7
c0.3,0.3,0.5,0.7,0.6,1c0.1,0.4,0.2,0.8,0.2,1.2c0,0.4-0.1,0.8-0.2,1.2c-0.2,0.4-0.4,0.7-0.6,1c-0.3,0.3-0.6,0.5-1,0.7
C27.7,92.3,27.2,92.4,26.8,92.4z M25,89.1c0,0.3,0,0.5,0.1,0.8c0.1,0.3,0.2,0.5,0.4,0.7c0.2,0.2,0.3,0.4,0.6,0.5s0.5,0.2,0.8,0.2
c0.3,0,0.5-0.1,0.8-0.2s0.4-0.3,0.6-0.5c0.1-0.2,0.3-0.4,0.3-0.7s0.1-0.5,0.1-0.8c0-0.3,0-0.5-0.1-0.8c-0.1-0.3-0.2-0.5-0.4-0.7
c-0.2-0.2-0.3-0.4-0.6-0.5c-0.2-0.1-0.5-0.2-0.7-0.2c-0.3,0-0.5,0.1-0.8,0.2s-0.4,0.3-0.6,0.5c-0.2,0.2-0.3,0.4-0.3,0.7
C25.1,88.6,25,88.9,25,89.1z"/>
<path class="st1" d="M31,92.3V86h2.8c0.3,0,0.6,0.1,0.8,0.2s0.5,0.3,0.6,0.5c0.2,0.2,0.3,0.4,0.4,0.7c0.1,0.3,0.2,0.5,0.2,0.8
c0,0.4-0.1,0.8-0.3,1.1c-0.2,0.3-0.5,0.6-0.8,0.7l1.5,2.4h-1.4l-1.3-2.1h-1.2v2.1H31z M32.3,89.1h1.6c0.1,0,0.2,0,0.3-0.1
c0.1-0.1,0.2-0.1,0.3-0.2c0.1-0.1,0.1-0.2,0.2-0.3s0.1-0.3,0.1-0.4c0-0.1,0-0.3-0.1-0.4s-0.1-0.2-0.2-0.3c-0.1-0.1-0.2-0.2-0.3-0.2
C34,87.1,33.9,87,33.8,87h-1.5V89.1z"/>
<path class="st1" d="M41.8,91.3v1.1h-4.4V86h4.4V87h-3.1v1.5h2.7v1h-2.7v1.7H41.8z"/>
</g>
<path class="st3" d="M27.1,29.7c-0.8,0.2-1.6,0.3-2.4,0.3c-5.4,0-9.8-4.4-9.8-9.8c0-5.4,4.4-9.8,9.8-9.8c5.4,0,9.8,4.4,9.8,9.8
c0,0.1,0,0.2,0,0.3c0.4-0.4,0.8-0.6,1.4-0.9C35.6,13.7,30.7,9,24.7,9c-6.2,0-11.2,5-11.2,11.2c0,6.2,5,11.2,11.2,11.2
c1,0,2-0.2,3-0.4C27.5,30.5,27.3,30.1,27.1,29.7z"/>
<path class="st3" d="M31.2,16.6L31.2,16.6c-0.3-0.3-0.5-0.3-0.8-0.2c0,0-4,0.5-5.7,0.5c0,0-0.1,0-0.1,0c-1.7,0-5.8-0.6-5.8-0.6
c-0.3-0.1-0.6,0-0.8,0.3l-0.1,0.2c-0.1,0.2-0.1,0.4-0.1,0.7c0.1,0.2,0.2,0.4,0.4,0.5c0.7,0.3,3.3,1.2,4,1.4c0.1,0,0.4,0.1,0.4,0.6
c0,0.6-0.2,3.1-0.5,4.3c-0.3,1.2-0.8,2.7-0.8,2.7c-0.1,0.4,0.1,0.9,0.5,1l0.5,0.2c0.2,0.1,0.4,0.1,0.6,0c0.2-0.1,0.3-0.3,0.4-0.5
l1.4-4.3l1.3,4.4c0.1,0.2,0.2,0.4,0.4,0.5c0.1,0.1,0.2,0.1,0.3,0.1c0.1,0,0.2,0,0.3-0.1l0.5-0.2c0.4-0.1,0.6-0.5,0.5-0.9
c0,0-0.4-1.6-0.7-2.9c-0.2-0.8-0.3-2-0.3-2.9c0-0.6-0.1-1.1-0.1-1.5c0-0.2,0.1-0.4,0.4-0.5c0.1,0,3.7-1.3,3.7-1.3
c0.3-0.1,0.4-0.3,0.5-0.6C31.4,17.1,31.4,16.8,31.2,16.6z"/>
<circle class="st3" cx="24.7" cy="14.1" r="1.7"/>
<g>
<polygon class="st3" points="36.4,30.1 36.4,30.1 36.4,30.1 "/>
<path class="st3" d="M35.2,25.8l2.7-2.7c0.4-0.4,0.4-1.1,0-1.5c-0.4-0.4-1.1-0.4-1.5,0l-2.7,2.7l-2.7-2.7c-0.4-0.4-1.1-0.4-1.5,0
c-0.4,0.4-0.4,1.1,0,1.5l2.7,2.7l-2.7,2.7c-0.4,0.4-0.4,1.1,0,1.5c0.4,0.4,1.1,0.4,1.5,0l2.7-2.7l2.7,2.7c0.4,0.4,1.1,0.4,1.5,0
c0.4-0.4,0.4-1.1,0-1.5L35.2,25.8z"/>
</g>
<path class="st1" d="M27.3,129.6c-0.8,0.2-1.6,0.3-2.4,0.3c-5.4,0-9.8-4.4-9.8-9.8c0-5.4,4.4-9.8,9.8-9.8c5.4,0,9.8,4.4,9.8,9.8
c0,0.1,0,0.2,0,0.3c0.4-0.4,0.8-0.6,1.4-0.9c-0.3-5.9-5.2-10.6-11.2-10.6c-6.2,0-11.2,5-11.2,11.2c0,6.2,5,11.2,11.2,11.2
c1,0,2-0.2,3-0.4C27.6,130.5,27.4,130.1,27.3,129.6z"/>
<path class="st1" d="M31.3,116.6L31.3,116.6c-0.3-0.3-0.5-0.3-0.8-0.2c0,0-4,0.5-5.7,0.5c0,0-0.1,0-0.1,0c-1.7,0-5.8-0.6-5.8-0.6
c-0.3-0.1-0.6,0-0.8,0.3l-0.1,0.2c-0.1,0.2-0.1,0.4-0.1,0.7c0.1,0.2,0.2,0.4,0.4,0.5c0.7,0.3,3.3,1.2,4,1.4c0.1,0,0.4,0.1,0.4,0.6
c0,0.6-0.2,3.1-0.5,4.3c-0.3,1.2-0.8,2.7-0.8,2.7c-0.1,0.4,0.1,0.9,0.5,1l0.5,0.2c0.2,0.1,0.4,0.1,0.6,0c0.2-0.1,0.3-0.3,0.4-0.5
l1.4-4.3l1.3,4.4c0.1,0.2,0.2,0.4,0.4,0.5c0.1,0.1,0.2,0.1,0.3,0.1c0.1,0,0.2,0,0.3-0.1l0.5-0.2c0.4-0.1,0.6-0.5,0.5-0.9
c0,0-0.4-1.6-0.7-2.9c-0.2-0.8-0.3-2-0.3-2.9c0-0.6-0.1-1.1-0.1-1.5c0-0.2,0.1-0.4,0.4-0.5c0.1,0,3.7-1.3,3.7-1.3
c0.3-0.1,0.4-0.3,0.5-0.6C31.6,117,31.5,116.8,31.3,116.6z"/>
<circle class="st1" cx="24.9" cy="114.1" r="1.7"/>
<g>
<path class="st1" d="M7.8,142.5v-6.4H9v6.4H7.8z"/>
<path class="st1" d="M14.9,141.7c-0.5,0.5-1.1,0.8-1.7,0.8c-0.4,0-0.8-0.1-1.2-0.3c-0.4-0.2-0.7-0.4-0.9-0.7
c-0.3-0.3-0.5-0.7-0.6-1s-0.2-0.8-0.2-1.2c0-0.4,0.1-0.9,0.2-1.2c0.2-0.4,0.4-0.7,0.6-1c0.3-0.3,0.6-0.5,1-0.7
c0.4-0.2,0.8-0.3,1.2-0.3c0.6,0,1.1,0.1,1.5,0.4s0.7,0.6,0.9,1l-0.9,0.7c-0.2-0.3-0.4-0.6-0.6-0.7s-0.6-0.3-0.9-0.3
c-0.3,0-0.5,0.1-0.7,0.2c-0.2,0.1-0.4,0.3-0.5,0.5c-0.2,0.2-0.3,0.4-0.4,0.7c-0.1,0.3-0.1,0.5-0.1,0.8c0,0.3,0,0.6,0.1,0.8
c0.1,0.3,0.2,0.5,0.4,0.7c0.2,0.2,0.4,0.3,0.6,0.5c0.2,0.1,0.5,0.2,0.7,0.2c0.6,0,1.1-0.3,1.6-0.9v-0.5h-1.3v-0.9h2.3v3.3h-1V141.7
z"/>
<path class="st1" d="M18.4,138.4v4.1h-1.2v-6.4h1l3.3,4.2v-4.2h1.2v6.4h-1L18.4,138.4z"/>
<path class="st1" d="M26.9,142.5c-0.5,0-0.9-0.1-1.2-0.3s-0.7-0.4-1-0.7c-0.3-0.3-0.5-0.6-0.6-1c-0.1-0.4-0.2-0.8-0.2-1.2
c0-0.4,0.1-0.8,0.2-1.2s0.4-0.7,0.6-1c0.3-0.3,0.6-0.5,1-0.7s0.8-0.3,1.2-0.3c0.5,0,0.9,0.1,1.2,0.3s0.7,0.4,1,0.7
c0.3,0.3,0.5,0.7,0.6,1c0.1,0.4,0.2,0.8,0.2,1.2c0,0.4-0.1,0.8-0.2,1.2c-0.2,0.4-0.4,0.7-0.6,1c-0.3,0.3-0.6,0.5-1,0.7
C27.8,142.4,27.4,142.5,26.9,142.5z M25.2,139.3c0,0.3,0,0.5,0.1,0.8c0.1,0.3,0.2,0.5,0.4,0.7c0.2,0.2,0.3,0.4,0.6,0.5
s0.5,0.2,0.8,0.2c0.3,0,0.5-0.1,0.8-0.2s0.4-0.3,0.6-0.5c0.1-0.2,0.3-0.4,0.3-0.7s0.1-0.5,0.1-0.8c0-0.3,0-0.5-0.1-0.8
c-0.1-0.3-0.2-0.5-0.4-0.7c-0.2-0.2-0.3-0.4-0.6-0.5c-0.2-0.1-0.5-0.2-0.7-0.2c-0.3,0-0.5,0.1-0.8,0.2s-0.4,0.3-0.6,0.5
c-0.2,0.2-0.3,0.4-0.3,0.7C25.2,138.7,25.2,139,25.2,139.3z"/>
<path class="st1" d="M31.2,142.5v-6.4H34c0.3,0,0.6,0.1,0.8,0.2s0.5,0.3,0.6,0.5c0.2,0.2,0.3,0.4,0.4,0.7c0.1,0.3,0.2,0.5,0.2,0.8
c0,0.4-0.1,0.8-0.3,1.1c-0.2,0.3-0.5,0.6-0.8,0.7l1.5,2.4H35l-1.3-2.1h-1.2v2.1H31.2z M32.4,139.2H34c0.1,0,0.2,0,0.3-0.1
c0.1-0.1,0.2-0.1,0.3-0.2c0.1-0.1,0.1-0.2,0.2-0.3s0.1-0.3,0.1-0.4c0-0.1,0-0.3-0.1-0.4s-0.1-0.2-0.2-0.3c-0.1-0.1-0.2-0.2-0.3-0.2
c-0.1-0.1-0.2-0.1-0.3-0.1h-1.5V139.2z"/>
<path class="st1" d="M41.9,141.4v1.1h-4.4v-6.4h4.4v1.1h-3.1v1.5h2.7v1h-2.7v1.7H41.9z"/>
</g>
<g>
<polygon class="st1" points="36.5,130.1 36.5,130.1 36.5,130.1 "/>
<path class="st1" d="M35.3,125.8l2.7-2.7c0.4-0.4,0.4-1.1,0-1.5c-0.4-0.4-1.1-0.4-1.5,0l-2.7,2.7l-2.7-2.7c-0.4-0.4-1.1-0.4-1.5,0
c-0.4,0.4-0.4,1.1,0,1.5l2.7,2.7l-2.7,2.7c-0.4,0.4-0.4,1.1,0,1.5c0.4,0.4,1.1,0.4,1.5,0l2.7-2.7l2.7,2.7c0.4,0.4,1.1,0.4,1.5,0
c0.4-0.4,0.4-1.1,0-1.5L35.3,125.8z"/>
</g>
<g>
<path class="st1" d="M7.7,192.7v-6.4h1.2v6.4H7.7z"/>
<path class="st1" d="M14.7,192c-0.5,0.5-1.1,0.8-1.7,0.8c-0.4,0-0.8-0.1-1.2-0.3c-0.4-0.2-0.7-0.4-0.9-0.7c-0.3-0.3-0.5-0.7-0.6-1
s-0.2-0.8-0.2-1.2c0-0.4,0.1-0.9,0.2-1.2c0.2-0.4,0.4-0.7,0.6-1c0.3-0.3,0.6-0.5,1-0.7c0.4-0.2,0.8-0.3,1.2-0.3
c0.6,0,1.1,0.1,1.5,0.4s0.7,0.6,0.9,1l-0.9,0.7c-0.2-0.3-0.4-0.6-0.6-0.7s-0.6-0.3-0.9-0.3c-0.3,0-0.5,0.1-0.7,0.2
c-0.2,0.1-0.4,0.3-0.5,0.5c-0.2,0.2-0.3,0.4-0.4,0.7c-0.1,0.3-0.1,0.5-0.1,0.8c0,0.3,0,0.6,0.1,0.8c0.1,0.3,0.2,0.5,0.4,0.7
c0.2,0.2,0.4,0.3,0.6,0.5c0.2,0.1,0.5,0.2,0.7,0.2c0.6,0,1.1-0.3,1.6-0.9v-0.5h-1.3v-0.9h2.3v3.3h-1V192z"/>
<path class="st1" d="M18.3,188.6v4.1h-1.2v-6.4h1l3.3,4.2v-4.2h1.2v6.4h-1L18.3,188.6z"/>
<path class="st1" d="M26.8,192.7c-0.5,0-0.9-0.1-1.2-0.3s-0.7-0.4-1-0.7c-0.3-0.3-0.5-0.6-0.6-1c-0.1-0.4-0.2-0.8-0.2-1.2
c0-0.4,0.1-0.8,0.2-1.2s0.4-0.7,0.6-1c0.3-0.3,0.6-0.5,1-0.7s0.8-0.3,1.2-0.3c0.5,0,0.9,0.1,1.2,0.3s0.7,0.4,1,0.7
c0.3,0.3,0.5,0.7,0.6,1c0.1,0.4,0.2,0.8,0.2,1.2c0,0.4-0.1,0.8-0.2,1.2c-0.2,0.4-0.4,0.7-0.6,1c-0.3,0.3-0.6,0.5-1,0.7
C27.7,192.6,27.2,192.7,26.8,192.7z M25,189.5c0,0.3,0,0.5,0.1,0.8c0.1,0.3,0.2,0.5,0.4,0.7c0.2,0.2,0.3,0.4,0.6,0.5
s0.5,0.2,0.8,0.2c0.3,0,0.5-0.1,0.8-0.2s0.4-0.3,0.6-0.5c0.1-0.2,0.3-0.4,0.3-0.7s0.1-0.5,0.1-0.8c0-0.3,0-0.5-0.1-0.8
c-0.1-0.3-0.2-0.5-0.4-0.7c-0.2-0.2-0.3-0.4-0.6-0.5c-0.2-0.1-0.5-0.2-0.7-0.2c-0.3,0-0.5,0.1-0.8,0.2s-0.4,0.3-0.6,0.5
c-0.2,0.2-0.3,0.4-0.3,0.7C25.1,189,25,189.2,25,189.5z"/>
<path class="st1" d="M31,192.7v-6.4h2.8c0.3,0,0.6,0.1,0.8,0.2s0.5,0.3,0.6,0.5c0.2,0.2,0.3,0.4,0.4,0.7c0.1,0.3,0.2,0.5,0.2,0.8
c0,0.4-0.1,0.8-0.3,1.1c-0.2,0.3-0.5,0.6-0.8,0.7l1.5,2.4h-1.4l-1.3-2.1h-1.2v2.1H31z M32.3,189.5h1.6c0.1,0,0.2,0,0.3-0.1
c0.1-0.1,0.2-0.1,0.3-0.2c0.1-0.1,0.1-0.2,0.2-0.3s0.1-0.3,0.1-0.4c0-0.1,0-0.3-0.1-0.4s-0.1-0.2-0.2-0.3c-0.1-0.1-0.2-0.2-0.3-0.2
c-0.1-0.1-0.2-0.1-0.3-0.1h-1.5V189.5z"/>
<path class="st1" d="M41.8,191.6v1.1h-4.4v-6.4h4.4v1.1h-3.1v1.5h2.7v1h-2.7v1.7H41.8z"/>
</g>
<path class="st1" d="M27.1,179.9c-0.8,0.2-1.6,0.3-2.4,0.3c-5.4,0-9.8-4.4-9.8-9.8c0-5.4,4.4-9.8,9.8-9.8c5.4,0,9.8,4.4,9.8,9.8
c0,0.1,0,0.2,0,0.3c0.4-0.4,0.8-0.6,1.4-0.9c-0.3-5.9-5.2-10.6-11.2-10.6c-6.2,0-11.2,5-11.2,11.2c0,6.2,5,11.2,11.2,11.2
c1,0,2-0.2,3-0.4C27.5,180.7,27.3,180.3,27.1,179.9z"/>
<path class="st1" d="M31.2,166.8L31.2,166.8c-0.3-0.3-0.5-0.3-0.8-0.2c0,0-4,0.5-5.7,0.5c0,0-0.1,0-0.1,0c-1.7,0-5.8-0.6-5.8-0.6
c-0.3-0.1-0.6,0-0.8,0.3l-0.1,0.2c-0.1,0.2-0.1,0.4-0.1,0.7c0.1,0.2,0.2,0.4,0.4,0.5c0.7,0.3,3.3,1.2,4,1.4c0.1,0,0.4,0.1,0.4,0.6
c0,0.6-0.2,3.1-0.5,4.3c-0.3,1.2-0.8,2.7-0.8,2.7c-0.1,0.4,0.1,0.9,0.5,1l0.5,0.2c0.2,0.1,0.4,0.1,0.6,0c0.2-0.1,0.3-0.3,0.4-0.5
l1.4-4.3l1.3,4.4c0.1,0.2,0.2,0.4,0.4,0.5c0.1,0.1,0.2,0.1,0.3,0.1c0.1,0,0.2,0,0.3-0.1l0.5-0.2c0.4-0.1,0.6-0.5,0.5-0.9
c0,0-0.4-1.6-0.7-2.9c-0.2-0.8-0.3-2-0.3-2.9c0-0.6-0.1-1.1-0.1-1.5c0-0.2,0.1-0.4,0.4-0.5c0.1,0,3.7-1.3,3.7-1.3
c0.3-0.1,0.4-0.3,0.5-0.6C31.4,167.3,31.4,167,31.2,166.8z"/>
<circle class="st1" cx="24.7" cy="164.3" r="1.7"/>
<g>
<polygon class="st1" points="36.4,180.3 36.4,180.3 36.4,180.3 "/>
<path class="st1" d="M35.2,176l2.7-2.7c0.4-0.4,0.4-1.1,0-1.5c-0.4-0.4-1.1-0.4-1.5,0l-2.7,2.7l-2.7-2.7c-0.4-0.4-1.1-0.4-1.5,0
c-0.4,0.4-0.4,1.1,0,1.5l2.7,2.7l-2.7,2.7c-0.4,0.4-0.4,1.1,0,1.5c0.4,0.4,1.1,0.4,1.5,0l2.7-2.7l2.7,2.7c0.4,0.4,1.1,0.4,1.5,0
c0.4-0.4,0.4-1.1,0-1.5L35.2,176z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -0,0 +1,140 @@
<?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 50 200.1" style="enable-background:new 0 0 50 200.1;" xml:space="preserve">
<style type="text/css">
.st0{fill:#414042;}
.st1{fill:#FFFFFF;}
.st2{fill:#1E1E1E;}
.st3{fill:#333333;}
</style>
<g id="Layer_2">
<g>
<g>
<path class="st0" d="M50.1,146.1c0,2.2-1.8,4-4,4h-42c-2.2,0-4-1.8-4-4v-42c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V146.1z"/>
</g>
</g>
<g>
<g>
<path class="st0" d="M50,196.1c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4v-42c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V196.1z"/>
</g>
</g>
<g>
<g>
<path class="st1" d="M50,46c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4V4c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V46z"/>
</g>
</g>
<g>
<path class="st2" d="M50,96.1c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4v-42c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V96.1z"/>
</g>
</g>
<path class="st1" d="M24.7,81.2c-6.2,0-11.2-5-11.2-11.2c0-6.2,5-11.2,11.2-11.2c6.2,0,11.2,5,11.2,11.2
C35.9,76.2,30.9,81.2,24.7,81.2z M24.7,60.3c-5.4,0-9.8,4.4-9.8,9.8c0,5.4,4.4,9.8,9.8,9.8c5.4,0,9.8-4.4,9.8-9.8
C34.5,64.6,30.1,60.3,24.7,60.3z"/>
<path class="st1" d="M27.1,79.5c-0.8,0.2-1.6,0.3-2.4,0.3c-5.4,0-9.8-4.4-9.8-9.8c0-5.4,4.4-9.8,9.8-9.8c5.4,0,9.8,4.4,9.8,9.8
c0,0.1,0,0.2,0,0.3c0.4-0.4,0.8-0.6,1.4-0.9c-0.3-5.9-5.2-10.6-11.2-10.6c-6.2,0-11.2,5-11.2,11.2c0,6.2,5,11.2,11.2,11.2
c1,0,2-0.2,3-0.4C27.5,80.4,27.3,80,27.1,79.5z"/>
<path class="st1" d="M31.2,66.5L31.2,66.5c-0.3-0.3-0.5-0.3-0.8-0.2c0,0-4,0.5-5.7,0.5c0,0-0.1,0-0.1,0c-1.7,0-5.8-0.6-5.8-0.6
c-0.3-0.1-0.6,0-0.8,0.3l-0.1,0.2c-0.1,0.2-0.1,0.4-0.1,0.7c0.1,0.2,0.2,0.4,0.4,0.5c0.7,0.3,3.3,1.2,4,1.4c0.1,0,0.4,0.1,0.4,0.6
c0,0.6-0.2,3.1-0.5,4.3c-0.3,1.2-0.8,2.7-0.8,2.7c-0.1,0.4,0.1,0.9,0.5,1l0.5,0.2c0.2,0.1,0.4,0.1,0.6,0c0.2-0.1,0.3-0.3,0.4-0.5
l1.4-4.3l1.3,4.4c0.1,0.2,0.2,0.4,0.4,0.5c0.1,0.1,0.2,0.1,0.3,0.1c0.1,0,0.2,0,0.3-0.1l0.5-0.2c0.4-0.1,0.6-0.5,0.5-0.9
c0,0-0.4-1.6-0.7-2.9c-0.2-0.8-0.3-2-0.3-2.9c0-0.6-0.1-1.1-0.1-1.5c0-0.2,0.1-0.4,0.4-0.5c0.1,0,3.7-1.3,3.7-1.3
c0.3-0.1,0.4-0.3,0.5-0.6C31.4,66.9,31.4,66.7,31.2,66.5z"/>
<circle class="st1" cx="24.7" cy="64" r="1.7"/>
<path class="st3" d="M27.1,29.7c-0.8,0.2-1.6,0.3-2.4,0.3c-5.4,0-9.8-4.4-9.8-9.8c0-5.4,4.4-9.8,9.8-9.8c5.4,0,9.8,4.4,9.8,9.8
c0,0.1,0,0.2,0,0.3c0.4-0.4,0.8-0.6,1.4-0.9C35.6,13.7,30.7,9,24.7,9c-6.2,0-11.2,5-11.2,11.2c0,6.2,5,11.2,11.2,11.2
c1,0,2-0.2,3-0.4C27.5,30.5,27.3,30.1,27.1,29.7z"/>
<path class="st3" d="M31.2,16.6L31.2,16.6c-0.3-0.3-0.5-0.3-0.8-0.2c0,0-4,0.5-5.7,0.5c0,0-0.1,0-0.1,0c-1.7,0-5.8-0.6-5.8-0.6
c-0.3-0.1-0.6,0-0.8,0.3l-0.1,0.2c-0.1,0.2-0.1,0.4-0.1,0.7c0.1,0.2,0.2,0.4,0.4,0.5c0.7,0.3,3.3,1.2,4,1.4c0.1,0,0.4,0.1,0.4,0.6
c0,0.6-0.2,3.1-0.5,4.3c-0.3,1.2-0.8,2.7-0.8,2.7c-0.1,0.4,0.1,0.9,0.5,1l0.5,0.2c0.2,0.1,0.4,0.1,0.6,0c0.2-0.1,0.3-0.3,0.4-0.5
l1.4-4.3l1.3,4.4c0.1,0.2,0.2,0.4,0.4,0.5c0.1,0.1,0.2,0.1,0.3,0.1c0.1,0,0.2,0,0.3-0.1l0.5-0.2c0.4-0.1,0.6-0.5,0.5-0.9
c0,0-0.4-1.6-0.7-2.9c-0.2-0.8-0.3-2-0.3-2.9c0-0.6-0.1-1.1-0.1-1.5c0-0.2,0.1-0.4,0.4-0.5c0.1,0,3.7-1.3,3.7-1.3
c0.3-0.1,0.4-0.3,0.5-0.6C31.4,17.1,31.4,16.8,31.2,16.6z"/>
<circle class="st3" cx="24.7" cy="14.1" r="1.7"/>
<g>
<polygon class="st3" points="36.4,30.1 36.4,30.1 36.4,30.1 "/>
<path class="st3" d="M35.2,25.8l2.7-2.7c0.4-0.4,0.4-1.1,0-1.5c-0.4-0.4-1.1-0.4-1.5,0l-2.7,2.7l-2.7-2.7c-0.4-0.4-1.1-0.4-1.5,0
c-0.4,0.4-0.4,1.1,0,1.5l2.7,2.7l-2.7,2.7c-0.4,0.4-0.4,1.1,0,1.5c0.4,0.4,1.1,0.4,1.5,0l2.7-2.7l2.7,2.7c0.4,0.4,1.1,0.4,1.5,0
c0.4-0.4,0.4-1.1,0-1.5L35.2,25.8z"/>
</g>
<path class="st1" d="M27.3,129.6c-0.8,0.2-1.6,0.3-2.4,0.3c-5.4,0-9.8-4.4-9.8-9.8c0-5.4,4.4-9.8,9.8-9.8c5.4,0,9.8,4.4,9.8,9.8
c0,0.1,0,0.2,0,0.3c0.4-0.4,0.8-0.6,1.4-0.9c-0.3-5.9-5.2-10.6-11.2-10.6c-6.2,0-11.2,5-11.2,11.2c0,6.2,5,11.2,11.2,11.2
c1,0,2-0.2,3-0.4C27.6,130.5,27.4,130.1,27.3,129.6z"/>
<path class="st1" d="M31.3,116.6L31.3,116.6c-0.3-0.3-0.5-0.3-0.8-0.2c0,0-4,0.5-5.7,0.5c0,0-0.1,0-0.1,0c-1.7,0-5.8-0.6-5.8-0.6
c-0.3-0.1-0.6,0-0.8,0.3l-0.1,0.2c-0.1,0.2-0.1,0.4-0.1,0.7c0.1,0.2,0.2,0.4,0.4,0.5c0.7,0.3,3.3,1.2,4,1.4c0.1,0,0.4,0.1,0.4,0.6
c0,0.6-0.2,3.1-0.5,4.3c-0.3,1.2-0.8,2.7-0.8,2.7c-0.1,0.4,0.1,0.9,0.5,1l0.5,0.2c0.2,0.1,0.4,0.1,0.6,0c0.2-0.1,0.3-0.3,0.4-0.5
l1.4-4.3l1.3,4.4c0.1,0.2,0.2,0.4,0.4,0.5c0.1,0.1,0.2,0.1,0.3,0.1c0.1,0,0.2,0,0.3-0.1l0.5-0.2c0.4-0.1,0.6-0.5,0.5-0.9
c0,0-0.4-1.6-0.7-2.9c-0.2-0.8-0.3-2-0.3-2.9c0-0.6-0.1-1.1-0.1-1.5c0-0.2,0.1-0.4,0.4-0.5c0.1,0,3.7-1.3,3.7-1.3
c0.3-0.1,0.4-0.3,0.5-0.6C31.6,117,31.5,116.8,31.3,116.6z"/>
<circle class="st1" cx="24.9" cy="114.1" r="1.7"/>
<g>
<polygon class="st1" points="36.5,130.1 36.5,130.1 36.5,130.1 "/>
<path class="st1" d="M35.3,125.8l2.7-2.7c0.4-0.4,0.4-1.1,0-1.5c-0.4-0.4-1.1-0.4-1.5,0l-2.7,2.7l-2.7-2.7c-0.4-0.4-1.1-0.4-1.5,0
c-0.4,0.4-0.4,1.1,0,1.5l2.7,2.7l-2.7,2.7c-0.4,0.4-0.4,1.1,0,1.5c0.4,0.4,1.1,0.4,1.5,0l2.7-2.7l2.7,2.7c0.4,0.4,1.1,0.4,1.5,0
c0.4-0.4,0.4-1.1,0-1.5L35.3,125.8z"/>
</g>
<path class="st1" d="M27.1,179.9c-0.8,0.2-1.6,0.3-2.4,0.3c-5.4,0-9.8-4.4-9.8-9.8c0-5.4,4.4-9.8,9.8-9.8c5.4,0,9.8,4.4,9.8,9.8
c0,0.1,0,0.2,0,0.3c0.4-0.4,0.8-0.6,1.4-0.9c-0.3-5.9-5.2-10.6-11.2-10.6c-6.2,0-11.2,5-11.2,11.2c0,6.2,5,11.2,11.2,11.2
c1,0,2-0.2,3-0.4C27.5,180.7,27.3,180.3,27.1,179.9z"/>
<path class="st1" d="M31.2,166.8L31.2,166.8c-0.3-0.3-0.5-0.3-0.8-0.2c0,0-4,0.5-5.7,0.5c0,0-0.1,0-0.1,0c-1.7,0-5.8-0.6-5.8-0.6
c-0.3-0.1-0.6,0-0.8,0.3l-0.1,0.2c-0.1,0.2-0.1,0.4-0.1,0.7c0.1,0.2,0.2,0.4,0.4,0.5c0.7,0.3,3.3,1.2,4,1.4c0.1,0,0.4,0.1,0.4,0.6
c0,0.6-0.2,3.1-0.5,4.3c-0.3,1.2-0.8,2.7-0.8,2.7c-0.1,0.4,0.1,0.9,0.5,1l0.5,0.2c0.2,0.1,0.4,0.1,0.6,0c0.2-0.1,0.3-0.3,0.4-0.5
l1.4-4.3l1.3,4.4c0.1,0.2,0.2,0.4,0.4,0.5c0.1,0.1,0.2,0.1,0.3,0.1c0.1,0,0.2,0,0.3-0.1l0.5-0.2c0.4-0.1,0.6-0.5,0.5-0.9
c0,0-0.4-1.6-0.7-2.9c-0.2-0.8-0.3-2-0.3-2.9c0-0.6-0.1-1.1-0.1-1.5c0-0.2,0.1-0.4,0.4-0.5c0.1,0,3.7-1.3,3.7-1.3
c0.3-0.1,0.4-0.3,0.5-0.6C31.4,167.3,31.4,167,31.2,166.8z"/>
<circle class="st1" cx="24.7" cy="164.3" r="1.7"/>
<g>
<polygon class="st1" points="36.4,180.3 36.4,180.3 36.4,180.3 "/>
<path class="st1" d="M35.2,176l2.7-2.7c0.4-0.4,0.4-1.1,0-1.5c-0.4-0.4-1.1-0.4-1.5,0l-2.7,2.7l-2.7-2.7c-0.4-0.4-1.1-0.4-1.5,0
c-0.4,0.4-0.4,1.1,0,1.5l2.7,2.7l-2.7,2.7c-0.4,0.4-0.4,1.1,0,1.5c0.4,0.4,1.1,0.4,1.5,0l2.7-2.7l2.7,2.7c0.4,0.4,1.1,0.4,1.5,0
c0.4-0.4,0.4-1.1,0-1.5L35.2,176z"/>
</g>
<g>
<path class="st1" d="M14.7,92.3v-6.4h1.2v3.1l2.8-3.1H20l-2.4,2.8l2.6,3.6h-1.3l-2-2.9l-0.9,0.9v1.9H14.7z"/>
<path class="st1" d="M20.9,92.3v-6.4h1.2v6.4H20.9z"/>
<path class="st1" d="M23.3,89.1c0-0.4,0.1-0.8,0.2-1.2c0.1-0.4,0.3-0.7,0.6-1c0.3-0.3,0.6-0.5,1-0.7s0.8-0.3,1.3-0.3
c0.6,0,1.1,0.1,1.5,0.4c0.4,0.3,0.7,0.6,0.9,1l-1,0.7c-0.1-0.2-0.2-0.3-0.3-0.5c-0.1-0.1-0.2-0.2-0.4-0.3c-0.1-0.1-0.3-0.1-0.4-0.2
c-0.1,0-0.3,0-0.4,0c-0.3,0-0.6,0.1-0.8,0.2s-0.4,0.3-0.6,0.5c-0.1,0.2-0.3,0.4-0.3,0.7s-0.1,0.5-0.1,0.8c0,0.3,0,0.6,0.1,0.8
c0.1,0.3,0.2,0.5,0.4,0.7c0.2,0.2,0.3,0.4,0.6,0.5c0.2,0.1,0.5,0.2,0.7,0.2c0.1,0,0.3,0,0.4-0.1c0.1,0,0.3-0.1,0.4-0.2
c0.1-0.1,0.3-0.2,0.4-0.3c0.1-0.1,0.2-0.3,0.3-0.4l1,0.6c-0.1,0.2-0.2,0.5-0.4,0.6c-0.2,0.2-0.4,0.3-0.6,0.5
c-0.2,0.1-0.5,0.2-0.7,0.3c-0.3,0.1-0.5,0.1-0.8,0.1c-0.4,0-0.9-0.1-1.2-0.3c-0.4-0.2-0.7-0.4-1-0.8c-0.3-0.3-0.5-0.7-0.6-1.1
C23.4,89.9,23.3,89.5,23.3,89.1z"/>
<path class="st1" d="M30,92.3v-6.4h1.2v3.1l2.8-3.1h1.3l-2.4,2.8l2.6,3.6h-1.3l-2-2.9l-0.9,0.9v1.9H30z"/>
</g>
<g>
<path class="st3" d="M14.7,42.6v-6.4h1.2v3.1l2.8-3.1H20l-2.4,2.8l2.6,3.6h-1.3l-2-2.9l-0.9,0.9v1.9H14.7z"/>
<path class="st3" d="M20.9,42.6v-6.4h1.2v6.4H20.9z"/>
<path class="st3" d="M23.3,39.4c0-0.4,0.1-0.8,0.2-1.2c0.1-0.4,0.3-0.7,0.6-1c0.3-0.3,0.6-0.5,1-0.7s0.8-0.3,1.3-0.3
c0.6,0,1.1,0.1,1.5,0.4c0.4,0.3,0.7,0.6,0.9,1l-1,0.7c-0.1-0.2-0.2-0.3-0.3-0.5c-0.1-0.1-0.2-0.2-0.4-0.3c-0.1-0.1-0.3-0.1-0.4-0.2
c-0.1,0-0.3,0-0.4,0c-0.3,0-0.6,0.1-0.8,0.2S25.2,37.8,25,38c-0.1,0.2-0.3,0.4-0.3,0.7s-0.1,0.5-0.1,0.8c0,0.3,0,0.6,0.1,0.8
c0.1,0.3,0.2,0.5,0.4,0.7c0.2,0.2,0.3,0.4,0.6,0.5c0.2,0.1,0.5,0.2,0.7,0.2c0.1,0,0.3,0,0.4-0.1c0.1,0,0.3-0.1,0.4-0.2
c0.1-0.1,0.3-0.2,0.4-0.3c0.1-0.1,0.2-0.3,0.3-0.4l1,0.6c-0.1,0.2-0.2,0.5-0.4,0.6c-0.2,0.2-0.4,0.3-0.6,0.5
c-0.2,0.1-0.5,0.2-0.7,0.3c-0.3,0.1-0.5,0.1-0.8,0.1c-0.4,0-0.9-0.1-1.2-0.3c-0.4-0.2-0.7-0.4-1-0.8c-0.3-0.3-0.5-0.7-0.6-1.1
C23.4,40.2,23.3,39.8,23.3,39.4z"/>
<path class="st3" d="M30,42.6v-6.4h1.2v3.1l2.8-3.1h1.3l-2.4,2.8l2.6,3.6h-1.3l-2-2.9l-0.9,0.9v1.9H30z"/>
</g>
<g>
<path class="st1" d="M14.8,142.4v-6.4H16v3.1l2.8-3.1h1.3l-2.4,2.8l2.6,3.6h-1.3l-2-2.9l-0.9,0.9v1.9H14.8z"/>
<path class="st1" d="M21,142.4v-6.4h1.2v6.4H21z"/>
<path class="st1" d="M23.4,139.2c0-0.4,0.1-0.8,0.2-1.2c0.1-0.4,0.3-0.7,0.6-1c0.3-0.3,0.6-0.5,1-0.7c0.4-0.2,0.8-0.3,1.3-0.3
c0.6,0,1.1,0.1,1.5,0.4c0.4,0.3,0.7,0.6,0.9,1l-1,0.7c-0.1-0.2-0.2-0.3-0.3-0.5c-0.1-0.1-0.2-0.2-0.4-0.3c-0.1-0.1-0.3-0.1-0.4-0.2
c-0.1,0-0.3,0-0.4,0c-0.3,0-0.6,0.1-0.8,0.2s-0.4,0.3-0.6,0.5c-0.1,0.2-0.3,0.4-0.3,0.7s-0.1,0.5-0.1,0.8c0,0.3,0,0.6,0.1,0.8
c0.1,0.3,0.2,0.5,0.4,0.7c0.2,0.2,0.3,0.4,0.6,0.5c0.2,0.1,0.5,0.2,0.7,0.2c0.1,0,0.3,0,0.4-0.1c0.1,0,0.3-0.1,0.4-0.2
s0.3-0.2,0.4-0.3c0.1-0.1,0.2-0.3,0.3-0.4l1,0.6c-0.1,0.2-0.2,0.5-0.4,0.6c-0.2,0.2-0.4,0.3-0.6,0.5c-0.2,0.1-0.5,0.2-0.7,0.3
c-0.3,0.1-0.5,0.1-0.8,0.1c-0.4,0-0.9-0.1-1.2-0.3c-0.4-0.2-0.7-0.4-1-0.8c-0.3-0.3-0.5-0.7-0.6-1.1
C23.5,140,23.4,139.6,23.4,139.2z"/>
<path class="st1" d="M30.1,142.4v-6.4h1.2v3.1l2.8-3.1h1.3l-2.4,2.8l2.6,3.6h-1.3l-2-2.9l-0.9,0.9v1.9H30.1z"/>
</g>
<g>
<path class="st1" d="M14.7,192.7v-6.4h1.2v3.1l2.8-3.1H20l-2.4,2.8l2.6,3.6h-1.3l-2-2.9l-0.9,0.9v1.9H14.7z"/>
<path class="st1" d="M20.9,192.7v-6.4h1.2v6.4H20.9z"/>
<path class="st1" d="M23.3,189.4c0-0.4,0.1-0.8,0.2-1.2c0.1-0.4,0.3-0.7,0.6-1c0.3-0.3,0.6-0.5,1-0.7c0.4-0.2,0.8-0.3,1.3-0.3
c0.6,0,1.1,0.1,1.5,0.4s0.7,0.6,0.9,1l-1,0.7c-0.1-0.2-0.2-0.3-0.3-0.5c-0.1-0.1-0.2-0.2-0.4-0.3s-0.3-0.1-0.4-0.2
c-0.1,0-0.3,0-0.4,0c-0.3,0-0.6,0.1-0.8,0.2s-0.4,0.3-0.6,0.5c-0.1,0.2-0.3,0.4-0.3,0.7s-0.1,0.5-0.1,0.8c0,0.3,0,0.6,0.1,0.8
c0.1,0.3,0.2,0.5,0.4,0.7c0.2,0.2,0.3,0.4,0.6,0.5c0.2,0.1,0.5,0.2,0.7,0.2c0.1,0,0.3,0,0.4-0.1c0.1,0,0.3-0.1,0.4-0.2
c0.1-0.1,0.3-0.2,0.4-0.3c0.1-0.1,0.2-0.3,0.3-0.4l1,0.6c-0.1,0.2-0.2,0.5-0.4,0.6c-0.2,0.2-0.4,0.3-0.6,0.5
c-0.2,0.1-0.5,0.2-0.7,0.3c-0.3,0.1-0.5,0.1-0.8,0.1c-0.4,0-0.9-0.1-1.2-0.3c-0.4-0.2-0.7-0.4-1-0.8c-0.3-0.3-0.5-0.7-0.6-1.1
C23.4,190.2,23.3,189.8,23.3,189.4z"/>
<path class="st1" d="M29.9,192.7v-6.4h1.2v3.1l2.8-3.1h1.3l-2.4,2.8l2.6,3.6h-1.3l-2-2.9l-0.9,0.9v1.9H29.9z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

View file

@ -0,0 +1,23 @@
<?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" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="Layer_2">
</g>
<g id="Layer_1">
<g>
<path class="st0" d="M45.4,13.7c-1.6-0.1-3.2,0-4.8,0c-7.5,0-18,0-25.7,0c-0.6-1.8-1-3.5-1.7-5.2C13,7.9,12.2,7.1,11.5,7
C9.4,6.7,7.3,6.7,5.2,6.7c-1.1,0-1.9,0.5-1.9,1.7c0,1.2,0.8,1.7,1.9,1.7c1.3,0,2.7,0,4,0.1c0.5,0.1,1.2,0.6,1.4,1.1
c0.9,2.6,1.7,5.2,2.5,7.8c1.3,4.3,1.8,5.5,3.1,10.2c0.6,2.3,1.2,2.8,2.2,3.3c1.1,0.4,2.1,0.4,2.1,0.4h1.8c4.6,0,12.2,0,16.8,0
c1.1,0,2.1-0.1,2.6-1.4c1.9-5.1,3.8-10.2,5.7-15.3C47.8,14.7,47.1,13.8,45.4,13.7z M38.9,28.7c-0.1,0.3-0.8,0.7-1.2,0.7
c-4.6,0-12.2,0-16.8,0c-0.4,0-1.1-0.3-1.2-0.7c-1.3-3.8-2.4-7.6-3.7-11.5h27.1C41.8,21.2,40.4,24.9,38.9,28.7z"/>
<path class="st0" d="M17.2,37.3L17.2,37.3c-1,0-1.7,0.2-2.2,0.7c-0.6,0.6-0.9,1.3-0.9,2.3c0,1,0.3,1.8,0.8,2.4
c0.6,0.6,1.4,0.9,2.2,0.9c1.8,0,3.2-1.4,3.2-3.2c0-0.9-0.3-1.7-0.9-2.3C18.8,37.6,18,37.3,17.2,37.3z"/>
<path class="st0" d="M35.1,37.3L35.1,37.3c-1,0-1.7,0.2-2.2,0.7c-0.6,0.6-0.9,1.4-0.9,2.4c0,1,0.3,1.8,0.8,2.4
c0.6,0.6,1.3,0.9,2.2,0.9c1.8,0,3.2-1.5,3.2-3.3c0-0.9-0.3-1.6-0.9-2.2C36.8,37.6,36,37.3,35.1,37.3z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1,17 @@
<?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" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="Layer_2">
</g>
<g id="Layer_1">
<path class="st0" d="M37,18.5H14.1c-0.9,0-1.7-0.7-1.7-1.7c0-0.9,0.8-1.7,1.7-1.7H37c0.9,0,1.7,0.7,1.7,1.7
C38.6,17.8,37.9,18.5,37,18.5z"/>
<path class="st0" d="M37,28H14.1c-0.9,0-1.7-0.8-1.7-1.7c0-0.9,0.8-1.7,1.7-1.7H37c0.9,0,1.7,0.8,1.7,1.7C38.6,27.3,37.9,28,37,28z
"/>
<path class="st0" d="M37,37.4H14.1c-0.9,0-1.7-0.8-1.7-1.7s0.8-1.7,1.7-1.7H37c0.9,0,1.7,0.8,1.7,1.7S37.9,37.4,37,37.4z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 835 B

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" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="Layer_2">
</g>
<g id="Layer_1">
<path class="st0" d="M28.9,17.1v-0.5c0-2-1.7-3.6-3.7-3.6h0c-2,0-3.7,1.6-3.7,3.6v6.9L28.9,17.1z"/>
<path class="st0" d="M21.5,29.2v0.2c0,2,1.6,3.6,3.7,3.6h0c2,0,3.7-1.6,3.7-3.6v-6.6L21.5,29.2z"/>
<path class="st0" d="M39.1,16.8L13.6,39.1c-0.7,0.6-1.8,0.5-2.4-0.2l-0.2-0.2c-0.6-0.7-0.5-1.8,0.2-2.4l25.4-22.4
c0.7-0.6,1.8-0.5,2.4,0.2l0.2,0.2C39.8,15.1,39.7,16.1,39.1,16.8z"/>
<path class="st0" d="M23.4,40.2l0,3.4h-4.3c-1,0-1.8,0.8-1.8,1.8c0,1,0.8,1.8,1.8,1.8h12.3c1,0,1.8-0.8,1.8-1.8
c0-1-0.8-1.8-1.8-1.8h-4.4l0-3.4c5.2-0.8,9.2-5,9.2-10.1c0-0.1,0-5.1,0-5.3c0-1-0.9-1.7-1.8-1.7c-1,0-1.7,0.8-1.7,1.8
c0,0.3,0,4.8,0,5.2c0,3.7-3.4,6.7-7.5,6.7c-3.6,0-6.7-2.3-7.3-5.4L15,34C16.4,37.2,19.6,39.7,23.4,40.2z"/>
<path class="st0" d="M17.7,24.9c0-1-0.7-1.8-1.6-1.8c-1-0.1-1.8,0.7-1.9,1.6c0,0.2,0,4.2,0,5.3l3.5-3.1
C17.7,25.9,17.7,25,17.7,24.9z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,22 @@
<?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" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="Layer_2">
</g>
<g id="Layer_1">
<path class="st0" d="M31.4,14.1l2.2-2.2c-2.1-2.5-5.3-4.1-8.8-4.1c-3.4,0-6.4,1.5-8.5,3.8c0.7,0.7,1.5,1.5,2.2,2.2
c1.6-1.7,3.8-2.9,6.3-2.9C27.5,10.9,29.9,12.1,31.4,14.1z"/>
<path class="st0" d="M36.5,9l2.2-2.2C35.3,3,30.3,0.6,24.8,0.6c-5.3,0-10.2,2.3-13.6,5.9c0.7,0.7,1.5,1.5,2.2,2.2
c2.9-3,6.9-5,11.4-5C29.5,3.7,33.6,5.8,36.5,9z"/>
<path class="st0" d="M28.5,22.7v-4.4c0-2-1.6-3.6-3.7-3.6h0c-2,0-3.7,1.6-3.7,3.6v4.4H28.5z"/>
<path class="st0" d="M21.1,26.5v4.3c0,2,1.7,3.6,3.7,3.6h0c2,0,3.7-1.6,3.7-3.6v-4.3H21.1z"/>
<path class="st0" d="M36,31.5c0-0.1,0-5.1,0-5.3c0-1-0.9-1.7-1.8-1.7c-1,0-1.7,0.8-1.7,1.8c0,0.3,0,4.8,0,5.2
c0,3.7-3.4,6.7-7.5,6.7c-4.1,0-7.5-3-7.5-6.7c0-0.4,0-4.9,0-5.3c0-1-0.7-1.8-1.6-1.8C14.9,24.3,14,25,14,26c0,0.3,0,5.4,0,5.5
c0,5.1,4,9.3,9.2,10.1l0,3.4h-4.3c-1,0-1.8,0.8-1.8,1.8c0,1,0.8,1.8,1.8,1.8h12.3c1,0,1.8-0.8,1.8-1.8c0-1-0.8-1.8-1.8-1.8h-4.4
l0-3.4C32,40.7,36,36.6,36,31.5z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,22 @@
<?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" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="Layer_2">
</g>
<g id="Layer_1">
<circle class="st0" cx="25.6" cy="14.8" r="8.1"/>
<path class="st0" d="M31.4,27h-11c-4.6,0-8.2,3.9-8.2,8.5v4.3c3.8,2.8,8.1,4.5,13.1,4.5c5.6,0,10.6-2.1,14.4-5.6v-3.2
C39.6,30.9,35.9,27,31.4,27z"/>
<circle class="st0" cx="41.6" cy="17.1" r="3.5"/>
<path class="st0" d="M43.9,23.9h-4.1c-0.9,0-1.7,0.4-2.3,1c1.2,0.6,2.3,1.6,3.1,2.5c1,1.2,1.5,2.7,1.7,4.3c0.3,0.9,0.4,1.8,0.2,2.8
v0.6c1.6-2.2,3.3-6,4-8.1v-0.3C46.5,25.7,45.3,23.9,43.9,23.9z"/>
<circle class="st0" cx="9.4" cy="18" r="3.5"/>
<path class="st0" d="M8.5,35.7c-0.1-0.7-0.1-1.4,0-2.1l0.1-0.2c0-0.2,0-0.4,0-0.6c0-0.2,0-0.3,0.1-0.5c0-0.2,0-0.3,0-0.5
c0.2-2.1,1.6-4.4,3.2-5.7c0.4-0.4,0.9-0.6,1.4-0.9c-0.5-0.5-1.3-0.7-2-0.7H7c-1.4,0-2.6,1.8-2.4,2.7v0.3
C5.1,29.8,6.8,33.5,8.5,35.7z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

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" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="Layer_2">
</g>
<g id="Layer_1">
<g>
<path class="st0" d="M29.1,45H12c-3.5,0-6.4-1.8-8.4-5.1c-1.4-2.4-1.9-4.7-1.9-4.8l-0.3-1.6h10.4V4.8h35.8v27
c0.1,0.8,0.5,6.6-2.4,10c-1.4,1.6-3.3,2.4-5.6,2.4c-4.8,0-7.2-2.6-8.3-4.7c-0.6-1.2-1-2.3-1.1-3.2H5c0.3,0.7,0.6,1.5,1.1,2.3
c1.5,2.4,3.5,3.6,5.9,3.6h17.1c0.8,0,1.4,0.6,1.4,1.4C30.5,44.3,29.9,45,29.1,45z M14.6,33.5h18l0.1,1.3c0,0,0.1,1.7,1,3.4
c1.2,2.1,3.1,3.2,5.8,3.2c1.5,0,2.7-0.5,3.5-1.4c1.2-1.3,1.6-3.3,1.7-4.8c0.2-1.7,0-3.1,0-3.1l0-0.1l0-0.1V7.6H14.6V33.5z"/>
<path class="st0" d="M39.6,14.3H19.7c-0.8,0-1.5-0.7-1.5-1.5s0.7-1.5,1.5-1.5h19.9c0.8,0,1.5,0.7,1.5,1.5S40.4,14.3,39.6,14.3z"/>
<path class="st0" d="M39.6,21.3H19.7c-0.8,0-1.5-0.7-1.5-1.6s0.7-1.6,1.5-1.6h19.9c0.8,0,1.5,0.7,1.5,1.6S40.4,21.3,39.6,21.3z"/>
<path class="st0" d="M39.6,28.1H19.7c-0.8,0-1.5-0.7-1.5-1.6s0.7-1.6,1.5-1.6h19.9c0.8,0,1.5,0.7,1.5,1.6S40.4,28.1,39.6,28.1z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,18 @@
<?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" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="Layer_2">
</g>
<g id="Layer_1">
<g>
<path class="st0" d="M38.5,16.3h-3.7v-1.6c0-2.4-1.6-3-4-3H18.7c-2.4,0-3.6,0.5-3.6,3v1.6h-3.6c-2.4,0-4.5,1-4.5,3.5V36
c0,2.4,1.9,4.6,4.5,4.6h27.1c2.4,0,4.2-2.7,4.2-5.2V19.7C42.8,17.3,41,16.3,38.5,16.3z M25.2,37c-5.5,0-9.9-4.5-9.9-9.9
c0-5.5,4.5-9.9,9.9-9.9c5.5,0,9.9,4.5,9.9,9.9S30.8,37,25.2,37z"/>
<circle class="st0" cx="25.2" cy="27" r="5.9"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 804 B

View file

@ -0,0 +1,16 @@
<?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" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="Layer_2">
</g>
<g id="Layer_1">
<path class="st0" d="M42.2,6.2H8.1c-3.5,0-6.4,3.1-6.4,6.8v18c0,3.8,2.9,6.8,6.4,6.8h15.3v3.5h-8.9c-0.8,0-1.5,0.9-1.5,1.8
c0,0.9,0.7,1.8,1.5,1.8h21.9c0.8,0,1.5-0.9,1.5-1.8c0-0.9-0.7-1.8-1.5-1.8h-9.6v-3.4c-0.3,0-0.6,0-0.9,0h16.3
c3.5,0,6.4-3.1,6.4-6.8V13C48.6,9.2,45.7,6.2,42.2,6.2z M45.2,31c0,1.7-1.3,3.1-2.9,3.1H8.1c-1.6,0-2.9-1.4-2.9-3.1V13
c0-1.7,1.3-3.1,2.9-3.1h34.2c1.6,0,2.9,1.4,2.9,3.1V31z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 842 B

View file

@ -0,0 +1,15 @@
<?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" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="Layer_2">
</g>
<g id="Layer_1">
<path class="st0" d="M25.4,28.7c1.7,0,2.8,1.8,3.8,3.6c0.5,0.8,1.6,2.6,2,2.6h7.5c4.2,0,7.6-3.8,7.6-8.4v-5.4
c0-4.6-3.4-8.4-7.6-8.4H11.9c-4.2,0-7.6,3.8-7.6,8.4v5.4c0,4.6,3.4,8.4,7.6,8.4h7.3c0.6,0,1.7-1.6,2.4-2.7
C22.6,30.5,23.7,28.7,25.4,28.7C25.3,28.7,25.4,28.7,25.4,28.7z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 717 B

View file

@ -0,0 +1,30 @@
<?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" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="Layer_2">
</g>
<g id="Layer_1">
<g>
<path class="st0" d="M22.6,14.8c-0.1-0.9-0.1-1.8,0.1-2.6h-8.6c-0.9,0-1.7,0.8-1.7,1.8s0.8,1.8,1.7,1.8h8.7
C22.7,15.4,22.7,15.1,22.6,14.8z"/>
<path class="st0" d="M26.5,22.3c-0.6-0.5-1.1-1-1.6-1.6H14.1c-0.9,0-1.7,0.8-1.7,1.8c0,1,0.8,1.8,1.7,1.8h8.2
c0.3-0.2,0.6-0.3,0.9-0.4c1.2-0.8,2.5-1.2,3.9-1.3C26.9,22.6,26.7,22.5,26.5,22.3z"/>
<path class="st0" d="M17.9,30.3c0.1-0.3,0.1-0.5,0.2-0.7h-4c-0.9,0-1.7,0.8-1.7,1.8c0,1,0.8,1.8,1.7,1.8h3.2
C17.3,32.2,17.5,31.2,17.9,30.3z"/>
<path class="st0" d="M5.9,15.7H4.5c-0.9,0-1.7-0.8-1.7-1.8s0.8-1.8,1.7-1.8h1.4c0.9,0,1.7,0.8,1.7,1.8S6.8,15.7,5.9,15.7z"/>
<path class="st0" d="M5.9,24.4H4.5c-0.9,0-1.7-0.8-1.7-1.8c0-1,0.8-1.8,1.7-1.8h1.4c0.9,0,1.7,0.8,1.7,1.8
C7.6,23.6,6.8,24.4,5.9,24.4z"/>
<path class="st0" d="M5.9,33.2H4.5c-0.9,0-1.7-0.8-1.7-1.8s0.8-1.8,1.7-1.8h1.4c0.9,0,1.7,0.8,1.7,1.8S6.8,33.2,5.9,33.2z"/>
<path class="st0" d="M17.3,38.3h-3.2c-0.9,0-1.7,0.8-1.7,1.8s0.8,1.8,1.7,1.8h3.2V38.3z"/>
<path class="st0" d="M5.9,42H4.5c-0.9,0-1.7-0.8-1.7-1.8c0-1,0.8-1.8,1.7-1.8h1.4c0.9,0,1.7,0.8,1.7,1.8C7.6,41.1,6.8,42,5.9,42z"
/>
<circle class="st0" cx="34.5" cy="14.3" r="7.8"/>
<path class="st0" d="M23.9,44.1c10.4,0,19.5-4.4,23.3-13.4c-1.3-2.7-3.9-4.7-7.1-4.7H29.5c-4.4,0-8.2,3.8-8.2,8.3v9.7
C22.2,44,23,44.1,23.9,44.1z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,18 @@
<?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 40 40" style="enable-background:new 0 0 40 40;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M24.6,12.5l1.6-1.6C24.7,9.2,22.4,8,19.9,8c-2.4,0-4.6,1-6.1,2.7c0.5,0.5,1,1,1.6,1.6c1.1-1.2,2.7-2,4.5-2
C21.8,10.2,23.5,11.1,24.6,12.5z"/>
<path class="st0" d="M28.3,8.9l1.6-1.6c-2.4-2.7-6-4.4-9.9-4.4c-3.8,0-7.3,1.6-9.7,4.2c0.5,0.5,1,1,1.6,1.6c2-2.2,4.9-3.5,8.1-3.5
C23.2,5.1,26.2,6.6,28.3,8.9z"/>
<path class="st0" d="M22.5,18.7v-3.1c0-1.5-1.2-2.6-2.6-2.6h0c-1.5,0-2.6,1.1-2.6,2.6v3.1H22.5z"/>
<path class="st0" d="M17.3,21.4v3c0,1.5,1.2,2.6,2.7,2.6h0c1.5,0,2.6-1.2,2.6-2.6v-3H17.3z"/>
<path class="st0" d="M27.9,24.9c0,0,0-3.6,0-3.8c0-0.7-0.6-1.2-1.3-1.2c-0.7,0-1.2,0.6-1.2,1.3c0,0.2,0,3.4,0,3.7
c0,2.6-2.4,4.8-5.3,4.8c-2.9,0-5.3-2.1-5.3-4.8c0-0.3,0-3.5,0-3.8c0-0.7-0.5-1.3-1.2-1.3c-0.7,0-1.3,0.5-1.3,1.2c0,0.2,0,3.9,0,3.9
c0,3.6,2.9,6.6,6.6,7.2l0,2.4h-3.1c-0.7,0-1.3,0.6-1.3,1.3c0,0.7,0.6,1.3,1.3,1.3h8.8c0.7,0,1.3-0.6,1.3-1.3c0-0.7-0.6-1.3-1.3-1.3
h-3.2l0-2.4C25,31.6,27.9,28.6,27.9,24.9z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,19 @@
<?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 40 40" style="enable-background:new 0 0 40 40;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M17.3,18.4v6c0,1.5,1.2,2.6,2.7,2.6h0c1.5,0,2.6-1.2,2.6-2.6v-6H17.3z"/>
<path class="st0" d="M27.9,24.9c0,0,0-3.6,0-3.8c0-0.7-0.6-1.2-1.3-1.2c-0.7,0-1.2,0.6-1.2,1.3c0,0.2,0,3.4,0,3.7
c0,2.6-2.4,4.8-5.3,4.8c-2.9,0-5.3-2.1-5.3-4.8c0-0.3,0-3.5,0-3.8c0-0.7-0.5-1.3-1.2-1.3c-0.7,0-1.3,0.5-1.3,1.2c0,0.2,0,3.9,0,3.9
c0,3.6,2.9,6.6,6.6,7.2l0,2.4h-3.1c-0.7,0-1.3,0.6-1.3,1.3c0,0.7,0.6,1.3,1.3,1.3h8.8c0.7,0,1.3-0.6,1.3-1.3c0-0.7-0.6-1.3-1.3-1.3
h-3.2l0-2.4C25,31.6,27.9,28.6,27.9,24.9z"/>
<g>
<polygon class="st0" points="23.4,15.8 23.4,15.8 23.4,15.8 "/>
<path class="st0" d="M21.9,10.5l3.4-3.4c0.5-0.5,0.5-1.4,0-1.9c-0.5-0.5-1.4-0.5-1.9,0L20,8.6l-3.4-3.4c-0.5-0.5-1.4-0.5-1.9,0
s-0.5,1.4,0,1.9l3.4,3.4l-3.4,3.4c-0.5,0.5-0.5,1.4,0,1.9c0.5,0.5,1.4,0.5,1.9,0l3.4-3.4l3.4,3.4c0.5,0.5,1.4,0.5,1.9,0
c0.5-0.5,0.5-1.4,0-1.9L21.9,10.5z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -187,9 +187,6 @@ Window {
ToolbarButton {
id: homeButton
imageURL: "../images/home.svg"
buttonState: 1
defaultState: 1
hoverState: 2
onClicked: {
addressBarDialog.loadHome();
root.shown = false;
@ -204,9 +201,6 @@ Window {
ToolbarButton {
id: backArrow;
imageURL: "../images/backward.svg";
hoverState: addressBarDialog.backEnabled ? 2 : 0;
defaultState: addressBarDialog.backEnabled ? 1 : 0;
buttonState: addressBarDialog.backEnabled ? 1 : 0;
onClicked: addressBarDialog.loadBack();
anchors {
left: homeButton.right
@ -216,9 +210,6 @@ Window {
ToolbarButton {
id: forwardArrow;
imageURL: "../images/forward.svg";
hoverState: addressBarDialog.forwardEnabled ? 2 : 0;
defaultState: addressBarDialog.forwardEnabled ? 1 : 0;
buttonState: addressBarDialog.forwardEnabled ? 1 : 0;
onClicked: addressBarDialog.loadForward();
anchors {
left: backArrow.right

View file

@ -12,7 +12,6 @@ import QtQuick 2.5
import QtQuick.Controls 1.4
import "../dialogs"
import "../menus"
import "../js/Utils.js" as Utils
// This is our primary 'desktop' object to which all VR dialogs and windows are childed.
@ -465,32 +464,7 @@ FocusScope {
Component { id: fileDialogBuilder; FileDialog { } }
function fileDialog(properties) {
return fileDialogBuilder.createObject(desktop, properties);
}
MenuMouseHandler { id: menuPopperUpper }
function popupMenu(point) {
menuPopperUpper.popup(desktop, rootMenu.items, point);
}
function toggleMenu(point) {
menuPopperUpper.toggle(desktop, rootMenu.items, point);
}
Keys.onEscapePressed: {
if (menuPopperUpper.closeLastMenu()) {
event.accepted = true;
return;
}
event.accepted = false;
}
Keys.onLeftPressed: {
if (menuPopperUpper.closeLastMenu()) {
event.accepted = true;
return;
}
event.accepted = false;
}
}
function unfocusWindows() {
// First find the active focus item, and unfocus it, all the way

View file

@ -48,14 +48,7 @@ OriginalDesktop.Desktop {
// This used to create sysToolbar dynamically with a call to getToolbar() within onCompleted.
// Beginning with QT 5.6, this stopped working, as anything added to toolbars too early got
// wiped during startup.
Toolbar {
id: sysToolbar;
objectName: "com.highfidelity.interface.toolbar.system";
anchors.horizontalCenter: settings.constrainToolbarToCenterX ? desktop.horizontalCenter : undefined;
// Literal 50 is overwritten by settings from previous session, and sysToolbar.x comes from settings when not constrained.
x: sysToolbar.x
y: 50
}
Settings {
id: settings;
category: "toolbar";
@ -65,7 +58,6 @@ OriginalDesktop.Desktop {
settings.constrainToolbarToCenterX = constrain;
}
property var toolbars: (function (map) { // answer dictionary preloaded with sysToolbar
map[sysToolbar.objectName] = sysToolbar;
return map; })({});
@ -74,27 +66,6 @@ OriginalDesktop.Desktop {
WebEngine.settings.javascriptCanAccessClipboard = false;
WebEngine.settings.spatialNavigationEnabled = false;
WebEngine.settings.localContentCanAccessRemoteUrls = true;
[ // Allocate the standard buttons in the correct order. They will get images, etc., via scripts.
"hmdToggle", "mute", "pal", "bubble", "help",
"hudToggle",
"com.highfidelity.interface.system.editButton", "marketplace", "snapshot", "goto"
].forEach(function (name) {
sysToolbar.addButton({objectName: name});
});
var toggleHudButton = sysToolbar.findButton("hudToggle");
toggleHudButton.imageURL = "../../../icons/hud.svg";
toggleHudButton.pinned = true;
sysToolbar.updatePinned(); // automatic when adding buttons only IFF button is pinned at creation.
toggleHudButton.buttonState = Qt.binding(function(){
return desktop.pinned ? 1 : 0
});
toggleHudButton.clicked.connect(function(){
console.log("Clicked on hud button")
var overlayMenuItem = "Overlays"
MenuInterface.setIsOptionChecked(overlayMenuItem, !MenuInterface.isOptionChecked(overlayMenuItem));
});
}
// Accept a download through the webview

View file

@ -158,8 +158,9 @@ Rectangle {
onSortIndicatorOrderChanged: sortModel()
TableViewColumn {
id: displayNameHeader
role: "displayName"
title: "NAMES"
title: table.rowCount + (table.rowCount === 1 ? " NAME" : " NAMES")
width: nameCardWidth
movable: false
resizable: false
@ -351,6 +352,11 @@ Rectangle {
visible: iAmAdmin
color: hifi.colors.lightGrayText
}
TextMetrics {
id: displayNameHeaderMetrics
text: displayNameHeader.title
font: displayNameHeader.font
}
// This Rectangle refers to the [?] popup button next to "NAMES"
Rectangle {
color: hifi.colors.tableBackgroundLight
@ -359,7 +365,7 @@ Rectangle {
anchors.left: table.left
anchors.top: table.top
anchors.topMargin: 1
anchors.leftMargin: nameCardWidth/2 + 24
anchors.leftMargin: nameCardWidth/2 + displayNameHeaderMetrics.width/2 + 6
RalewayRegular {
id: helpText
text: "[?]"

View file

@ -0,0 +1,278 @@
import QtQuick 2.0
import QtGraphicalEffects 1.0
import "../../styles-uit"
Item {
id: tablet
objectName: "tablet"
property double micLevel: 0.8
property int rowIndex: 0
property int columnIndex: 0
property int count: (flowMain.children.length - 1)
// called by C++ code to keep audio bar updated
function setMicLevel(newMicLevel) {
tablet.micLevel = newMicLevel;
}
// used to look up a button by its uuid
function findButtonIndex(uuid) {
if (!uuid) {
return -1;
}
for (var i in flowMain.children) {
var child = flowMain.children[i];
if (child.uuid === uuid) {
return i;
}
}
return -1;
}
// called by C++ code when a button should be added to the tablet
function addButtonProxy(properties) {
var component = Qt.createComponent("TabletButton.qml");
var button = component.createObject(flowMain);
// copy all properites to button
var keys = Object.keys(properties).forEach(function (key) {
button[key] = properties[key];
});
// pass a reference to the tabletRoot object to the button.
button.tabletRoot = parent.parent;
return button;
}
// called by C++ code when a button should be removed from the tablet
function removeButtonProxy(properties) {
var index = findButtonIndex(properties.uuid);
if (index < 0) {
console.log("Warning: Tablet.qml could not find button with uuid = " + properties.uuid);
} else {
flowMain.children[index].destroy();
}
}
Rectangle {
id: bgTopBar
height: 90
gradient: Gradient {
GradientStop {
position: 0
color: "#2b2b2b"
}
GradientStop {
position: 1
color: "#1e1e1e"
}
}
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
anchors.topMargin: 0
anchors.top: parent.top
Image {
id: muteIcon
width: 40
height: 40
source: "../../../icons/tablet-mute-icon.svg"
anchors.verticalCenter: parent.verticalCenter
}
Item {
id: item1
width: 170
height: 10
anchors.left: parent.left
anchors.leftMargin: 50
anchors.verticalCenter: parent.verticalCenter
Rectangle {
id: audioBarBase
color: "#333333"
radius: 5
anchors.fill: parent
}
Rectangle {
id: audioBarMask
width: parent.width * tablet.micLevel
color: "#333333"
radius: 5
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
anchors.top: parent.top
anchors.topMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
}
LinearGradient {
anchors.fill: audioBarMask
source: audioBarMask
start: Qt.point(0, 0)
end: Qt.point(170, 0)
gradient: Gradient {
GradientStop {
position: 0
color: "#2c8e72"
}
GradientStop {
position: 0.8
color: "#1fc6a6"
}
GradientStop {
position: 0.81
color: "#ea4c5f"
}
GradientStop {
position: 1
color: "#ea4c5f"
}
}
}
}
RalewaySemiBold {
id: usernameText
text: tablet.parent.parent.username
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 20
horizontalAlignment: Text.AlignRight
font.pixelSize: 20
color: "#afafaf"
}
}
Rectangle {
id: bgMain
gradient: Gradient {
GradientStop {
position: 0
color: "#2b2b2b"
}
GradientStop {
position: 1
color: "#0f212e"
}
}
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
anchors.top: bgTopBar.bottom
anchors.topMargin: 0
Flickable {
id: flickable
width: parent.width
height: parent.height
contentWidth: parent.width
contentHeight: flowMain.childrenRect.height + flowMain.anchors.topMargin + flowMain.anchors.bottomMargin + flowMain.spacing
clip: true
Flow {
id: flowMain
spacing: 16
anchors.right: parent.right
anchors.rightMargin: 30
anchors.left: parent.left
anchors.leftMargin: 30
anchors.bottom: parent.bottom
anchors.bottomMargin: 30
anchors.top: parent.top
anchors.topMargin: 30
}
}
}
states: [
State {
name: "muted state"
PropertyChanges {
target: muteText
text: "UNMUTE"
}
PropertyChanges {
target: muteIcon
source: "../../../icons/tablet-unmute-icon.svg"
}
PropertyChanges {
target: tablet
micLevel: 0
}
}
]
function setCurrentItemState(state) {
var index = rowIndex + columnIndex;
if (index >= 0 && index <= count ) {
flowMain.children[index].state = state;
}
}
function nextItem() {
setCurrentItemState("base state");
var nextColumnIndex = (columnIndex + 3 + 1) % 3;
var nextIndex = rowIndex + nextColumnIndex;
if(nextIndex <= count) {
columnIndex = nextColumnIndex;
};
setCurrentItemState("hover state");
}
function previousItem() {
setCurrentItemState("base state");
var prevIndex = (columnIndex + 3 - 1) % 3;
if((rowIndex + prevIndex) <= count){
columnIndex = prevIndex;
}
setCurrentItemState("hover state");
}
function upItem() {
setCurrentItemState("base state");
rowIndex = rowIndex - 3;
if (rowIndex < 0 ) {
rowIndex = (count - (count % 3));
var index = rowIndex + columnIndex;
if(index > count) {
rowIndex = rowIndex - 3;
}
}
setCurrentItemState("hover state");
}
function downItem() {
setCurrentItemState("base state");
rowIndex = rowIndex + 3;
var index = rowIndex + columnIndex;
if (index > count ) {
rowIndex = 0;
}
setCurrentItemState("hover state");
}
function selectItem() {
flowMain.children[rowIndex + columnIndex].clicked();
if (tabletRoot) {
tabletRoot.playButtonClickSound();
}
}
Keys.onRightPressed: nextItem();
Keys.onLeftPressed: previousItem();
Keys.onDownPressed: downItem();
Keys.onUpPressed: upItem();
Keys.onReturnPressed: selectItem();
}

View file

@ -0,0 +1,225 @@
import QtQuick 2.0
import QtGraphicalEffects 1.0
Item {
id: tabletButton
property var uuid;
property string text: "EDIT"
property string icon: "icons/edit-icon.svg"
property string activeText: tabletButton.text
property string activeIcon: tabletButton.icon
property bool isActive: false
property bool inDebugMode: false
property bool isEntered: false
property var tabletRoot;
width: 129
height: 129
signal clicked()
function changeProperty(key, value) {
tabletButton[key] = value;
}
onIsActiveChanged: {
if (tabletButton.isEntered) {
tabletButton.state = (tabletButton.isActive) ? "hover active state" : "hover sate";
} else {
tabletButton.state = (tabletButton.isActive) ? "active state" : "base sate";
}
}
Rectangle {
id: buttonBg
color: "#000000"
opacity: 0.1
radius: 8
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
anchors.top: parent.top
anchors.topMargin: 0
}
Rectangle {
id: buttonOutline
color: "#00000000"
opacity: 0.2
radius: 8
z: 1
border.width: 2
border.color: "#ffffff"
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
anchors.top: parent.top
anchors.topMargin: 0
}
DropShadow {
id: glow
visible: false
anchors.fill: parent
horizontalOffset: 0
verticalOffset: 0
color: "#ffffff"
radius: 20
z: -1
samples: 41
source: buttonOutline
}
Image {
id: icon
width: 50
height: 50
visible: false
anchors.bottom: text.top
anchors.bottomMargin: 5
anchors.horizontalCenter: parent.horizontalCenter
fillMode: Image.Stretch
source: "../../../" + tabletButton.icon
}
ColorOverlay {
id: iconColorOverlay
anchors.fill: icon
source: icon
color: "#ffffff"
}
Text {
id: text
color: "#ffffff"
text: tabletButton.text
font.bold: true
font.pixelSize: 18
anchors.bottom: parent.bottom
anchors.bottomMargin: 20
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
enabled: true
onClicked: {
console.log("Tablet Button Clicked!");
if (tabletButton.inDebugMode) {
if (tabletButton.isActive) {
tabletButton.isActive = false;
} else {
tabletButton.isActive = true;
}
}
tabletButton.clicked();
if (tabletRoot) {
tabletRoot.playButtonClickSound();
}
}
onEntered: {
tabletButton.isEntered = true;
if (tabletButton.isActive) {
tabletButton.state = "hover active state";
} else {
tabletButton.state = "hover state";
}
}
onExited: {
tabletButton.isEntered = false;
if (tabletButton.isActive) {
tabletButton.state = "active state";
} else {
tabletButton.state = "base state";
}
}
}
states: [
State {
name: "hover state"
PropertyChanges {
target: buttonOutline
border.color: "#1fc6a6"
opacity: 1
}
PropertyChanges {
target: glow
visible: true
}
},
State {
name: "active state"
PropertyChanges {
target: buttonOutline
border.color: "#1fc6a6"
opacity: 1
}
PropertyChanges {
target: buttonBg
color: "#1fc6a6"
opacity: 1
}
PropertyChanges {
target: text
color: "#333333"
text: tabletButton.activeText
}
PropertyChanges {
target: iconColorOverlay
color: "#333333"
}
PropertyChanges {
target: icon
source: "../../../" + tabletButton.activeIcon
}
},
State {
name: "hover active state"
PropertyChanges {
target: glow
visible: true
}
PropertyChanges {
target: buttonOutline
border.color: "#ffffff"
opacity: 1
}
PropertyChanges {
target: buttonBg
color: "#1fc6a6"
opacity: 1
}
PropertyChanges {
target: text
color: "#333333"
}
PropertyChanges {
target: iconColorOverlay
color: "#333333"
}
}
]
}

View file

@ -0,0 +1,101 @@
import QtQuick 2.5
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4
import QtQml 2.2
import "."
import "../../styles-uit"
FocusScope {
id: tabletMenu
objectName: "tabletMenu"
width: 480
height: 720
property var rootMenu: Menu { objectName:"rootMenu" }
property var point: Qt.point(50, 50)
TabletMouseHandler { id: menuPopperUpper }
Rectangle {
id: bgNavBar
height: 90
z: 1
gradient: Gradient {
GradientStop {
position: 0
color: "#2b2b2b"
}
GradientStop {
position: 1
color: "#1e1e1e"
}
}
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
anchors.topMargin: 0
anchors.top: parent.top
Image {
id: menuRootIcon
width: 40
height: 40
source: "../../../icons/tablet-icons/menu-i.svg"
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 15
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: iconColorOverlay.color = "#1fc6a6";
onExited: iconColorOverlay.color = "#ffffff";
// navigate back to root level menu
onClicked: buildMenu();
}
}
ColorOverlay {
id: iconColorOverlay
anchors.fill: menuRootIcon
source: menuRootIcon
color: "#34a2c7"
}
RalewayBold {
id: breadcrumbText
text: "Menu"
size: 26
color: "#34a2c7"
anchors.verticalCenter: parent.verticalCenter
anchors.left: menuRootIcon.right
anchors.leftMargin: 15
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: breadcrumbText.color = "#1fc6a6";
onExited: breadcrumbText.color = "#34a2c7";
// navigate back to parent level menu if there is one
onClicked:
if (breadcrumbText.text !== "Menu") {
menuPopperUpper.closeLastMenu();
}
}
}
}
function pop() {
menuPopperUpper.closeLastMenu();
}
function setRootMenu(menu) {
tabletMenu.rootMenu = menu
buildMenu()
}
function buildMenu() {
menuPopperUpper.popup(tabletMenu, rootMenu.items)
}
}

View file

@ -12,8 +12,8 @@ import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "../controls-uit"
import "../styles-uit"
import "../../controls-uit"
import "../../styles-uit"
Item {
id: root
@ -31,7 +31,7 @@ Item {
// FIXME: Should use radio buttons if source.exclusiveGroup.
anchors {
left: parent.left
leftMargin: hifi.dimensions.menuPadding.x
leftMargin: hifi.dimensions.menuPadding.x + 15
top: label.top
topMargin: 0
}
@ -50,7 +50,7 @@ Item {
RalewaySemiBold {
id: label
size: hifi.fontSizes.rootMenu
size: 20
font.capitalization: isSubMenu ? Font.MixedCase : Font.AllUppercase
anchors.left: check.right
anchors.verticalCenter: parent.verticalCenter
@ -103,7 +103,7 @@ Item {
HiFiGlyphs {
text: hifi.glyphs.disclosureExpand
color: source.enabled ? hifi.colors.baseGrayShadow : hifi.colors.baseGrayShadow25
size: 2 * hifi.fontSizes.rootMenuDisclosure
size: 70
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
horizontalAlignment: Text.AlignRight

View file

@ -12,13 +12,13 @@ import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "../styles-uit"
import "../../styles-uit"
import "."
FocusScope {
id: root
implicitHeight: background.height
implicitWidth: background.width
objectName: "root"
property alias currentItem: listView.currentItem
property alias model: listView.model
property bool isSubMenu: false
@ -32,15 +32,23 @@ FocusScope {
radius: hifi.dimensions.borderRadius
border.width: hifi.dimensions.borderWidth
border.color: hifi.colors.lightGrayText80
color: isSubMenu ? hifi.colors.faintGray : hifi.colors.faintGray80
color: hifi.colors.faintGray
//color: isSubMenu ? hifi.colors.faintGray : hifi.colors.faintGray80
}
ListView {
id: listView
x: 8; y: 8
width: 128
height: count * 32
x: 0
y: 0
width: 480
height: 720
contentWidth: 480
contentHeight: 720
objectName: "menuList"
topMargin: hifi.dimensions.menuPadding.y
bottomMargin: hifi.dimensions.menuPadding.y
onEnabledChanged: recalcSize();
onVisibleChanged: recalcSize();
onCountChanged: recalcSize();
@ -57,7 +65,7 @@ FocusScope {
color: hifi.colors.white
}
delegate: VrMenuItem {
delegate: TabletMenuItem {
text: name
source: item
onImplicitHeightChanged: listView.recalcSize()
@ -91,26 +99,30 @@ FocusScope {
newHeight += currentItem.implicitHeight
}
}
newHeight += 2 * hifi.dimensions.menuPadding.y; // White space at top and bottom.
newHeight += hifi.dimensions.menuPadding.y * 2; // White space at top and bottom.
if (maxWidth > width) {
width = maxWidth;
}
if (newHeight > height) {
height = newHeight
if (newHeight > contentHeight) {
contentHeight = newHeight;
}
currentIndex = originalIndex;
}
function previousItem() { currentIndex = (currentIndex + count - 1) % count; }
function nextItem() { currentIndex = (currentIndex + count + 1) % count; }
function selectCurrentItem() { if (currentIndex != -1) root.selected(currentItem.source); }
Keys.onUpPressed: previousItem();
Keys.onDownPressed: nextItem();
Keys.onSpacePressed: selectCurrentItem();
Keys.onRightPressed: selectCurrentItem();
Keys.onReturnPressed: selectCurrentItem();
Keys.onLeftPressed: previousPage();
}
function previousItem() { listView.currentIndex = (listView.currentIndex + listView.count - 1) % listView.count; }
function nextItem() { listView.currentIndex = (listView.currentIndex + listView.count + 1) % listView.count; }
function selectCurrentItem() { if (listView.currentIndex != -1) root.selected(currentItem.source); }
function previousPage() { root.parent.pop(); }
}

View file

@ -16,11 +16,11 @@ import "."
Item {
id: root
anchors.fill: parent
objectName: "MouseMenuHandlerItem"
objectName: "tabletMenuHandlerItem"
MouseArea {
id: menuRoot;
objectName: "MouseMenuHandlerMouseArea"
objectName: "tabletMenuHandlerMouseArea"
anchors.fill: parent
enabled: d.topMenu !== null
onClicked: {
@ -34,7 +34,7 @@ Item {
property var topMenu: null;
property var modelMaker: Component { ListModel { } }
property var menuViewMaker: Component {
VrMenuView {
TabletMenuView {
id: subMenu
onSelected: d.handleSelection(subMenu, currentItem, item)
}
@ -54,7 +54,7 @@ Item {
}
function toModel(items) {
var result = modelMaker.createObject(desktop);
var result = modelMaker.createObject(tabletMenu);
for (var i = 0; i < items.length; ++i) {
var item = items[i];
if (!item.visible) continue;
@ -63,7 +63,9 @@ Item {
result.append({"name": item.title, "item": item})
break;
case MenuItemType.Item:
result.append({"name": item.text, "item": item})
if (item.text !== "Users Online") {
result.append({"name": item.text, "item": item})
}
break;
case MenuItemType.Separator:
result.append({"name": "", "item": item})
@ -80,10 +82,16 @@ Item {
if (menuStack.length) {
topMenu = menuStack[menuStack.length - 1];
topMenu.focus = true;
topMenu.forceActiveFocus();
// show current menu level on nav bar
if (topMenu.objectName === "") {
breadcrumbText.text = "Menu";
} else {
breadcrumbText.text = topMenu.objectName;
}
} else {
breadcrumbText.text = "Menu";
topMenu = null;
offscreenFlags.navigationFocused = false;
menuRoot.enabled = false;
}
}
@ -91,7 +99,7 @@ Item {
menuStack.push(newMenu);
topMenu = newMenu;
topMenu.focus = true;
offscreenFlags.navigationFocused = true;
topMenu.forceActiveFocus();
}
function clearMenus() {
@ -118,12 +126,7 @@ Item {
function buildMenu(items, targetPosition) {
var model = toModel(items);
// Menus must be childed to desktop for Z-ordering
var newMenu = menuViewMaker.createObject(desktop, { model: model, z: topMenu ? topMenu.z + 1 : desktop.zLevels.menu, isSubMenu: topMenu !== null });
if (targetPosition) {
newMenu.x = targetPosition.x
newMenu.y = targetPosition.y - newMenu.height / 3 * 1
}
clampMenuPosition(newMenu);
var newMenu = menuViewMaker.createObject(tabletMenu, { model: model, isSubMenu: topMenu !== null });
pushMenu(newMenu);
return newMenu;
}
@ -137,39 +140,36 @@ Item {
case MenuItemType.Menu:
var target = Qt.vector2d(topMenu.x, topMenu.y).plus(Qt.vector2d(selectedItem.x + 96, selectedItem.y));
buildMenu(item.items, target).objectName = item.title;
// show current menu level on nav bar
breadcrumbText.text = item.title;
break;
case MenuItemType.Item:
console.log("Triggering " + item.text)
// Don't block waiting for modal dialogs and such that the menu might open.
delay.trigger(item);
clearMenus();
break;
}
}
}
function popup(parent, items, point) {
function popup(parent, items) {
d.clearMenus();
menuRoot.enabled = true;
d.buildMenu(items, point);
}
function toggle(parent, items, point) {
if (d.topMenu) {
d.clearMenus();
return;
}
popup(parent, items, point);
}
function closeLastMenu() {
if (d.menuStack.length) {
if (d.menuStack.length > 1) {
d.popMenu();
return true;
}
return false;
}
function previousItem() { d.topMenu.previousItem(); }
function nextItem() { d.topMenu.nextItem(); }
function selectCurrentItem() { d.topMenu.selectCurrentItem(); }
function previousPage() { d.topMenu.previousPage(); }
}

View file

@ -0,0 +1,63 @@
import QtQuick 2.0
import Hifi 1.0
Item {
id: tabletRoot
objectName: "tabletRoot"
property string username: "Unknown user"
property var eventBridge;
signal showDesktop();
function loadSource(url) {
loader.source = url;
}
function loadWebUrl(url, injectedJavaScriptUrl) {
loader.item.url = url;
loader.item.scriptURL = injectedJavaScriptUrl;
}
SoundEffect {
id: buttonClickSound
source: "../../../sounds/Gamemaster-Audio-button-click.wav"
}
function playButtonClickSound() {
// Because of the asynchronous nature of initalization, it is possible for this function to be
// called before the C++ has set the globalPosition context variable.
if (typeof globalPosition !== 'undefined') {
buttonClickSound.play(globalPosition);
}
}
function setUsername(newUsername) {
username = newUsername;
}
Loader {
id: loader
objectName: "loader"
asynchronous: false
width: parent.width
height: parent.height
onLoaded: {
if (loader.item.hasOwnProperty("eventBridge")) {
loader.item.eventBridge = eventBridge;
// Hook up callback for clara.io download from the marketplace.
eventBridge.webEventReceived.connect(function (event) {
if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {
ApplicationInterface.addAssetToWorldFromURL(event.slice(18));
}
});
}
loader.item.forceActiveFocus();
}
}
width: 480
height: 720
}

View file

@ -0,0 +1,10 @@
import QtQuick 2.0
import QtWebEngine 1.2
import "../../controls" as Controls
Controls.WebView {
}

View file

@ -6,7 +6,7 @@ Item {
property alias alpha: image.opacity
property var subImage;
property int yOffset: 0
property int buttonState: 0
property int buttonState: 1
property real size: 50
width: size; height: size
property bool pinned: false

View file

@ -3,11 +3,34 @@ import QtQuick.Controls 1.4
StateImage {
id: button
property int hoverState: -1
property int defaultState: -1
property bool isActive: false
property bool isEntered: false
property int imageOffOut: 1
property int imageOffIn: 3
property int imageOnOut: 0
property int imageOnIn: 2
signal clicked()
function changeProperty(key, value) {
button[key] = value;
}
function updateState() {
if (!button.isEntered && !button.isActive) {
buttonState = imageOffOut;
} else if (!button.isEntered && button.isActive) {
buttonState = imageOnOut;
} else if (button.isEntered && !button.isActive) {
buttonState = imageOffIn;
} else {
buttonState = imageOnIn;
}
}
onIsActiveChanged: updateState();
Timer {
id: asyncClickSender
interval: 10
@ -22,14 +45,12 @@ StateImage {
anchors.fill: parent
onClicked: asyncClickSender.start();
onEntered: {
if (hoverState >= 0) {
buttonState = hoverState;
}
button.isEntered = true;
updateState();
}
onExited: {
if (defaultState >= 0) {
buttonState = defaultState;
}
button.isEntered = false;
updateState();
}
}
}

View file

@ -156,7 +156,7 @@ Item {
readonly property real modalDialogTitleHeight: 40
readonly property real controlLineHeight: 28 // Height of spinbox control on 1920 x 1080 monitor
readonly property real controlInterlineHeight: 21 // 75% of controlLineHeight
readonly property vector2d menuPadding: Qt.vector2d(14, 12)
readonly property vector2d menuPadding: Qt.vector2d(14, 102)
readonly property real scrollbarBackgroundWidth: 18
readonly property real scrollbarHandleWidth: scrollbarBackgroundWidth - 2
}

View file

@ -111,6 +111,7 @@
#include <ScriptEngines.h>
#include <ScriptCache.h>
#include <SoundCache.h>
#include <TabletScriptingInterface.h>
#include <Tooltip.h>
#include <udt/PacketHeaders.h>
#include <UserActivityLogger.h>
@ -492,6 +493,7 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set<WindowScriptingInterface>();
DependencyManager::set<HMDScriptingInterface>();
DependencyManager::set<ResourceScriptingInterface>();
DependencyManager::set<TabletScriptingInterface>();
DependencyManager::set<ToolbarScriptingInterface>();
DependencyManager::set<UserActivityLoggerScriptingInterface>();
@ -977,7 +979,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) {
using namespace controller;
auto offscreenUi = DependencyManager::get<OffscreenUi>();
if (offscreenUi->navigationFocused()) {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
{
auto actionEnum = static_cast<Action>(action);
int key = Qt::Key_unknown;
static int lastKey = Qt::Key_unknown;
@ -1021,25 +1024,28 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
break;
}
if (navAxis) {
auto window = tabletScriptingInterface->getTabletWindow();
if (navAxis && window) {
if (lastKey != Qt::Key_unknown) {
QKeyEvent event(QEvent::KeyRelease, lastKey, Qt::NoModifier);
sendEvent(offscreenUi->getWindow(), &event);
sendEvent(window, &event);
lastKey = Qt::Key_unknown;
}
if (key != Qt::Key_unknown) {
QKeyEvent event(QEvent::KeyPress, key, Qt::NoModifier);
sendEvent(offscreenUi->getWindow(), &event);
sendEvent(window, &event);
tabletScriptingInterface->processEvent(&event);
lastKey = key;
}
} else if (key != Qt::Key_unknown) {
} else if (key != Qt::Key_unknown && window) {
if (state) {
QKeyEvent event(QEvent::KeyPress, key, Qt::NoModifier);
sendEvent(offscreenUi->getWindow(), &event);
sendEvent(window, &event);
tabletScriptingInterface->processEvent(&event);
} else {
QKeyEvent event(QEvent::KeyRelease, key, Qt::NoModifier);
sendEvent(offscreenUi->getWindow(), &event);
sendEvent(window, &event);
}
return;
}
@ -1065,12 +1071,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
DependencyManager::get<AudioClient>()->toggleMute();
} else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) {
cycleCamera();
} else if (action == controller::toInt(controller::Action::UI_NAV_SELECT)) {
if (!offscreenUi->navigationFocused()) {
toggleMenuUnderReticle();
}
} else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) {
toggleMenuUnderReticle();
toggleTabletUI();
} else if (action == controller::toInt(controller::Action::RETICLE_X)) {
auto oldPos = getApplicationCompositor().getReticlePosition();
getApplicationCompositor().setReticlePosition({ oldPos.x + state, oldPos.y });
@ -1589,15 +1591,17 @@ QString Application::getUserAgent() {
return userAgent;
}
void Application::toggleMenuUnderReticle() const {
// In HMD, if the menu is near the mouse but not under it, the reticle can be at a significantly
// different depth. When you focus on the menu, the cursor can appear to your crossed eyes as both
// on the menu and off.
// Even in 2D, it is arguable whether the user would want the menu to be to the side.
const float X_LEFT_SHIFT = 50.0;
auto offscreenUi = DependencyManager::get<OffscreenUi>();
auto reticlePosition = getApplicationCompositor().getReticlePosition();
offscreenUi->toggleMenu(QPoint(reticlePosition.x - X_LEFT_SHIFT, reticlePosition.y));
uint64_t lastTabletUIToggle { 0 };
const uint64_t toggleTabletUILockout { 500000 };
void Application::toggleTabletUI() const {
uint64_t now = usecTimestampNow();
if (now - lastTabletUIToggle < toggleTabletUILockout) {
return;
}
lastTabletUIToggle = now;
auto HMD = DependencyManager::get<HMDScriptingInterface>();
HMD->toggleShouldShowTablet();
}
void Application::checkChangeCursor() {
@ -2933,10 +2937,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
void Application::keyReleaseEvent(QKeyEvent* event) {
if (event->key() == Qt::Key_Alt && _altPressed && hasFocus()) {
toggleMenuUnderReticle();
}
_keysPressed.remove(event->key());
_controllerScriptingInterface->emitKeyReleaseEvent(event); // send events to any registered scripts
@ -4161,9 +4161,11 @@ void Application::setKeyboardFocusOverlay(unsigned int overlayID) {
}
_lastAcceptedKeyPress = usecTimestampNow();
auto size = overlay->getSize() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR;
const float OVERLAY_DEPTH = 0.0105f;
setKeyboardFocusHighlight(overlay->getPosition(), overlay->getRotation(), glm::vec3(size.x, size.y, OVERLAY_DEPTH));
if (overlay->getProperty("showKeyboardFocusHighlight").toBool()) {
auto size = overlay->getSize() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR;
const float OVERLAY_DEPTH = 0.0105f;
setKeyboardFocusHighlight(overlay->getPosition(), overlay->getRotation(), glm::vec3(size.x, size.y, OVERLAY_DEPTH));
}
}
}
}
@ -4377,6 +4379,10 @@ void Application::update(float deltaTime) {
PROFILE_RANGE_EX(simulation_physics, "HarvestChanges", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
PerformanceTimer perfTimer("harvestChanges");
if (_physicsEngine->hasOutgoingChanges()) {
// grab the collision events BEFORE handleOutgoingChanges() because at this point
// we have a better idea of which objects we own or should own.
auto& collisionEvents = _physicsEngine->getCollisionEvents();
getEntities()->getTree()->withWriteLock([&] {
PerformanceTimer perfTimer("handleOutgoingChanges");
const VectorOfMotionStates& outgoingChanges = _physicsEngine->getOutgoingChanges();
@ -4384,11 +4390,10 @@ void Application::update(float deltaTime) {
avatarManager->handleOutgoingChanges(outgoingChanges);
});
auto collisionEvents = _physicsEngine->getCollisionEvents();
avatarManager->handleCollisionEvents(collisionEvents);
if (!_aboutToQuit) {
// handleCollisionEvents() AFTER handleOutgoinChanges()
PerformanceTimer perfTimer("entities");
avatarManager->handleCollisionEvents(collisionEvents);
// Collision events (and their scripts) must not be handled when we're locked, above. (That would risk
// deadlock.)
_entitySimulation->handleCollisionEvents(collisionEvents);

View file

@ -484,7 +484,7 @@ private:
static void dragEnterEvent(QDragEnterEvent* event);
void maybeToggleMenuVisible(QMouseEvent* event) const;
void toggleMenuUnderReticle() const;
void toggleTabletUI() const;
MainWindow* _window;
QElapsedTimer& _sessionRunTimer;

View file

@ -160,7 +160,7 @@ Menu::Menu() {
audioIO.data(), SLOT(toggleMute()));
// Audio > Show Level Meter
addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::AudioTools, 0, true);
addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::AudioTools, 0, false);
// Avatar menu ----------------------------------

View file

@ -892,6 +892,16 @@ glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const {
Transform controllerRightHandTransform = Transform(getControllerRightHandMatrix());
return controllerRightHandTransform.getRotation();
}
case CAMERA_MATRIX_INDEX: {
glm::quat rotation;
if (_skeletonModel && _skeletonModel->isActive()) {
int headJointIndex = _skeletonModel->getFBXGeometry().headJointIndex;
if (headJointIndex >= 0) {
_skeletonModel->getAbsoluteJointRotationInRigFrame(headJointIndex, rotation);
}
}
return rotation;
}
default: {
glm::quat rotation;
_skeletonModel->getAbsoluteJointRotationInRigFrame(index, rotation);
@ -918,6 +928,16 @@ glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
Transform controllerRightHandTransform = Transform(getControllerRightHandMatrix());
return controllerRightHandTransform.getTranslation();
}
case CAMERA_MATRIX_INDEX: {
glm::vec3 translation;
if (_skeletonModel && _skeletonModel->isActive()) {
int headJointIndex = _skeletonModel->getFBXGeometry().headJointIndex;
if (headJointIndex >= 0) {
_skeletonModel->getAbsoluteJointTranslationInRigFrame(headJointIndex, translation);
}
}
return translation;
}
default: {
glm::vec3 translation;
_skeletonModel->getAbsoluteJointTranslationInRigFrame(index, translation);

View file

@ -128,32 +128,44 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm::
glm::quat palmRotation;
if (holdingAvatar->isMyAvatar()) {
std::shared_ptr<MyAvatar> myAvatar = avatarManager->getMyAvatar();
// fetch the hand controller pose
controller::Pose pose;
if (isRightHand) {
pose = avatarManager->getMyAvatar()->getRightHandControllerPoseInWorldFrame();
pose = myAvatar->getRightHandControllerPoseInWorldFrame();
} else {
pose = avatarManager->getMyAvatar()->getLeftHandControllerPoseInWorldFrame();
pose = myAvatar->getLeftHandControllerPoseInWorldFrame();
}
if (pose.isValid()) {
linearVelocity = pose.getVelocity();
angularVelocity = pose.getAngularVelocity();
if (isRightHand) {
pose = avatarManager->getMyAvatar()->getRightHandControllerPoseInAvatarFrame();
} else {
pose = avatarManager->getMyAvatar()->getLeftHandControllerPoseInAvatarFrame();
}
}
if (_ignoreIK && pose.isValid()) {
// this position/rotation should be the same as the one in scripts/system/libraries/controllers.js
// otherwise things will do a little hop when you grab them.
// if (isRightHand) {
// pose = myAvatar->getRightHandControllerPoseInAvatarFrame();
// } else {
// pose = myAvatar->getLeftHandControllerPoseInAvatarFrame();
// }
// glm::vec3 camRelPos = pose.getTranslation();
// glm::quat camRelRot = pose.getRotation();
int camRelIndex = isRightHand ?
CAMERA_RELATIVE_CONTROLLER_RIGHTHAND_INDEX :
CAMERA_RELATIVE_CONTROLLER_LEFTHAND_INDEX;
glm::vec3 camRelPos = myAvatar->getAbsoluteJointTranslationInObjectFrame(camRelIndex);
glm::quat camRelRot = myAvatar->getAbsoluteJointRotationInObjectFrame(camRelIndex);
Transform avatarTransform;
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
avatarTransform = myAvatar->getTransform();
palmPosition = avatarTransform.transform(pose.getTranslation() / myAvatar->getDomainLimitedScale());
palmRotation = avatarTransform.getRotation() * pose.getRotation();
palmPosition = avatarTransform.transform(camRelPos / myAvatar->getDomainLimitedScale());
palmRotation = avatarTransform.getRotation() * camRelRot;
} else {
glm::vec3 avatarRigidBodyPosition;
glm::quat avatarRigidBodyRotation;
@ -223,6 +235,11 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) {
qDebug() << "AvatarActionHold::doKinematicUpdate -- no owning entity";
return;
}
if (ownerEntity->getParentID() != QUuid()) {
// if the held entity has been given a parent, stop acting on it.
return;
}
void* physicsInfo = ownerEntity->getPhysicsInfo();
if (!physicsInfo) {
qDebug() << "AvatarActionHold::doKinematicUpdate -- no owning physics info";

View file

@ -797,8 +797,14 @@ void MyAvatar::saveData() {
settings.beginWriteArray("avatarEntityData");
int avatarEntityIndex = 0;
auto hmdInterface = DependencyManager::get<HMDScriptingInterface>();
_avatarEntitiesLock.withReadLock([&] {
for (auto entityID : _avatarEntityData.keys()) {
if (hmdInterface->getCurrentTableUIID() == entityID) {
// don't persist the tablet between domains / sessions
continue;
}
settings.setArrayIndex(avatarEntityIndex);
settings.setValue("id", entityID);
settings.setValue("properties", _avatarEntityData.value(entityID));
@ -2388,6 +2394,13 @@ glm::quat MyAvatar::getAbsoluteJointRotationInObjectFrame(int index) const {
glm::mat4 result = computeCameraRelativeHandControllerMatrix(controllerSensorMatrix);
return glmExtractRotation(result);
}
case CAMERA_MATRIX_INDEX: {
bool success;
Transform avatarTransform;
Transform::mult(avatarTransform, getParentTransform(success), getLocalTransform());
glm::mat4 invAvatarMat = avatarTransform.getInverseMatrix();
return glmExtractRotation(invAvatarMat * qApp->getCamera()->getTransform());
}
default: {
return Avatar::getAbsoluteJointRotationInObjectFrame(index);
}
@ -2414,6 +2427,13 @@ glm::vec3 MyAvatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
glm::mat4 result = computeCameraRelativeHandControllerMatrix(controllerSensorMatrix);
return extractTranslation(result);
}
case CAMERA_MATRIX_INDEX: {
bool success;
Transform avatarTransform;
Transform::mult(avatarTransform, getParentTransform(success), getLocalTransform());
glm::mat4 invAvatarMat = avatarTransform.getInverseMatrix();
return extractTranslation(invAvatarMat * qApp->getCamera()->getTransform());
}
default: {
return Avatar::getAbsoluteJointTranslationInObjectFrame(index);
}

View file

@ -18,6 +18,7 @@
#include <display-plugins/CompositorHelper.h>
#include <OffscreenUi.h>
#include <plugins/PluginUtils.h>
#include <QUuid>
#include "Application.h"
@ -76,6 +77,10 @@ bool HMDScriptingInterface::shouldShowHandControllers() const {
return _showHandControllersCount > 0;
}
void HMDScriptingInterface::closeTablet() {
_showTablet = false;
}
QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) {
glm::vec3 hudIntersection;
auto instance = DependencyManager::get<HMDScriptingInterface>();

View file

@ -28,6 +28,10 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen
Q_PROPERTY(glm::vec3 position READ getPosition)
Q_PROPERTY(glm::quat orientation READ getOrientation)
Q_PROPERTY(bool mounted READ isMounted)
Q_PROPERTY(bool showTablet READ getShouldShowTablet)
Q_PROPERTY(QUuid tabletID READ getCurrentTableUIID WRITE setCurrentTabletUIID)
Q_PROPERTY(unsigned int homeButtonID READ getCurrentHomeButtonUUID WRITE setCurrentHomeButtonUUID)
public:
Q_INVOKABLE glm::vec3 calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction) const;
@ -53,11 +57,11 @@ public:
Q_INVOKABLE void disableExtraLaser() const;
/// Suppress the activation of any on-screen keyboard so that a script operation will
/// Suppress the activation of any on-screen keyboard so that a script operation will
/// not be interrupted by a keyboard popup
/// Returns false if there is already an active keyboard displayed.
/// Clients should re-enable the keyboard when the operation is complete and ensure
/// that they balance any call to suppressKeyboard() that returns true with a corresponding
/// that they balance any call to suppressKeyboard() that returns true with a corresponding
/// call to unsuppressKeyboard() within a reasonable amount of time
Q_INVOKABLE bool suppressKeyboard();
@ -70,6 +74,8 @@ public:
// rotate the overlay UI sphere so that it is centered about the the current HMD position and orientation
Q_INVOKABLE void centerUI();
Q_INVOKABLE void closeTablet();
signals:
bool shouldShowHandControllersChanged();
@ -80,10 +86,25 @@ public:
bool isMounted() const;
void toggleShouldShowTablet() { _showTablet = !_showTablet; }
void setShouldShowTablet(bool value) { _showTablet = value; }
bool getShouldShowTablet() const { return _showTablet; }
void setCurrentTabletUIID(QUuid tabletID) { _tabletUIID = tabletID; }
QUuid getCurrentTableUIID() const { return _tabletUIID; }
void setCurrentHomeButtonUUID(unsigned int homeButtonID) { _homeButtonID = homeButtonID; }
unsigned int getCurrentHomeButtonUUID() const { return _homeButtonID; }
private:
bool _showTablet { false };
QUuid _tabletUIID; // this is the entityID of the WebEntity which is part of (a child of) the tablet-ui.
unsigned int _homeButtonID;
QUuid _tabletEntityID;
// Get the position of the HMD
glm::vec3 getPosition() const;
// Get the orientation of the HMD
glm::quat getOrientation() const;

View file

@ -147,3 +147,14 @@ void MenuScriptingInterface::triggerOption(const QString& menuOption) {
QMetaObject::invokeMethod(Menu::getInstance(), "triggerOption", Q_ARG(const QString&, menuOption));
}
void MenuScriptingInterface::closeInfoView(const QString& path) {
QMetaObject::invokeMethod(Menu::getInstance(), "closeInfoView", Q_ARG(const QString&, path));
}
bool MenuScriptingInterface::isInfoViewVisible(const QString& path) {
bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "isInfoViewVisible", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, result), Q_ARG(const QString&, path));
return result;
}

View file

@ -182,6 +182,9 @@ public slots:
*/
void setMenuEnabled(const QString& menuName, bool isEnabled);
void closeInfoView(const QString& path);
bool isInfoViewVisible(const QString& path);
signals:
/**jsdoc
* This is a signal that is emitted when a menu item is clicked.

View file

@ -0,0 +1,63 @@
//
// Created by Anthony J. Thibault on 2016-12-12
// Copyright 2013-2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_QmlWrapper_h
#define hifi_QmlWrapper_h
#include <QtCore/QObject>
#include <OffscreenUi.h>
#include <DependencyManager.h>
class QmlWrapper : public QObject {
Q_OBJECT
public:
QmlWrapper(QObject* qmlObject, QObject* parent = nullptr)
: QObject(parent), _qmlObject(qmlObject) {
}
Q_INVOKABLE void writeProperty(QString propertyName, QVariant propertyValue) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->executeOnUiThread([=] {
_qmlObject->setProperty(propertyName.toStdString().c_str(), propertyValue);
});
}
Q_INVOKABLE void writeProperties(QVariant propertyMap) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->executeOnUiThread([=] {
QVariantMap map = propertyMap.toMap();
for (const QString& key : map.keys()) {
_qmlObject->setProperty(key.toStdString().c_str(), map[key]);
}
});
}
Q_INVOKABLE QVariant readProperty(const QString& propertyName) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
return offscreenUi->returnFromUiThread([&]()->QVariant {
return _qmlObject->property(propertyName.toStdString().c_str());
});
}
Q_INVOKABLE QVariant readProperties(const QVariant& propertyList) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
return offscreenUi->returnFromUiThread([&]()->QVariant {
QVariantMap result;
for (const QVariant& property : propertyList.toList()) {
QString propertyString = property.toString();
result.insert(propertyString, _qmlObject->property(propertyString.toStdString().c_str()));
}
return result;
});
}
protected:
QObject* _qmlObject{ nullptr };
};
#endif

View file

@ -8,69 +8,44 @@
#include "ToolbarScriptingInterface.h"
#include <QtCore/QThread>
#include <OffscreenUi.h>
class QmlWrapper : public QObject {
Q_OBJECT
public:
QmlWrapper(QObject* qmlObject, QObject* parent = nullptr)
: QObject(parent), _qmlObject(qmlObject) {
}
Q_INVOKABLE void writeProperty(QString propertyName, QVariant propertyValue) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->executeOnUiThread([=] {
_qmlObject->setProperty(propertyName.toStdString().c_str(), propertyValue);
});
}
Q_INVOKABLE void writeProperties(QVariant propertyMap) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->executeOnUiThread([=] {
QVariantMap map = propertyMap.toMap();
for (const QString& key : map.keys()) {
_qmlObject->setProperty(key.toStdString().c_str(), map[key]);
}
});
}
Q_INVOKABLE QVariant readProperty(const QString& propertyName) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
return offscreenUi->returnFromUiThread([&]()->QVariant {
return _qmlObject->property(propertyName.toStdString().c_str());
});
}
Q_INVOKABLE QVariant readProperties(const QVariant& propertyList) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
return offscreenUi->returnFromUiThread([&]()->QVariant {
QVariantMap result;
for (const QVariant& property : propertyList.toList()) {
QString propertyString = property.toString();
result.insert(propertyString, _qmlObject->property(propertyString.toStdString().c_str()));
}
return result;
});
}
protected:
QObject* _qmlObject{ nullptr };
};
#include "QmlWrapper.h"
class ToolbarButtonProxy : public QmlWrapper {
Q_OBJECT
public:
ToolbarButtonProxy(QObject* qmlObject, QObject* parent = nullptr) : QmlWrapper(qmlObject, parent) {
std::lock_guard<std::mutex> guard(_mutex);
_qmlButton = qobject_cast<QQuickItem*>(qmlObject);
connect(qmlObject, SIGNAL(clicked()), this, SIGNAL(clicked()));
}
Q_INVOKABLE void editProperties(QVariantMap properties) {
std::lock_guard<std::mutex> guard(_mutex);
QVariantMap::const_iterator iter = properties.constBegin();
while (iter != properties.constEnd()) {
_properties[iter.key()] = iter.value();
if (_qmlButton) {
// [01/25 14:26:20] [WARNING] [default] QMetaObject::invokeMethod: No such method ToolbarButton_QMLTYPE_195::changeProperty(QVariant,QVariant)
QMetaObject::invokeMethod(_qmlButton, "changeProperty", Qt::AutoConnection,
Q_ARG(QVariant, QVariant(iter.key())), Q_ARG(QVariant, iter.value()));
}
++iter;
}
}
signals:
void clicked();
protected:
mutable std::mutex _mutex;
QQuickItem* _qmlButton { nullptr };
QVariantMap _properties;
};
class ToolbarProxy : public QmlWrapper {

View file

@ -58,6 +58,8 @@ WindowScriptingInterface::WindowScriptingInterface() {
OffscreenUi::warning("Import SVO Error", "You need to be running edit.js to import entities.");
}
});
connect(qApp->getWindow(), &MainWindow::windowGeometryChanged, this, &WindowScriptingInterface::geometryChanged);
}
WindowScriptingInterface::~WindowScriptingInterface() {

View file

@ -76,6 +76,9 @@ signals:
void messageBoxClosed(int id, int button);
// triggered when window size or position changes
void geometryChanged(QRect geometry);
private:
QString getPreviousBrowseLocation() const;
void setPreviousBrowseLocation(const QString& location);

View file

@ -26,7 +26,8 @@ Base3DOverlay::Base3DOverlay() :
_isSolid(DEFAULT_IS_SOLID),
_isDashedLine(DEFAULT_IS_DASHED_LINE),
_ignoreRayIntersection(false),
_drawInFront(false)
_drawInFront(false),
_isAA(true)
{
}
@ -37,7 +38,8 @@ Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
_isSolid(base3DOverlay->_isSolid),
_isDashedLine(base3DOverlay->_isDashedLine),
_ignoreRayIntersection(base3DOverlay->_ignoreRayIntersection),
_drawInFront(base3DOverlay->_drawInFront)
_drawInFront(base3DOverlay->_drawInFront),
_isAA(base3DOverlay->_isAA)
{
setTransform(base3DOverlay->getTransform());
}
@ -175,6 +177,13 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
needRenderItemUpdate = true;
}
auto isAA = properties["isAA"];
if (isAA.isValid()) {
bool value = isAA.toBool();
setIsAA(value);
needRenderItemUpdate = true;
}
// Communicate changes to the renderItem if needed
if (needRenderItemUpdate) {
auto itemID = getRenderItemID();
@ -224,6 +233,9 @@ QVariant Base3DOverlay::getProperty(const QString& property) {
if (property == "parentJointIndex") {
return getParentJointIndex();
}
if (property == "isAA") {
return _isAA;
}
return Overlay::getProperty(property);
}

View file

@ -36,11 +36,14 @@ public:
bool getIgnoreRayIntersection() const { return _ignoreRayIntersection; }
bool getDrawInFront() const { return _drawInFront; }
virtual bool isAA() const { return _isAA; }
void setLineWidth(float lineWidth) { _lineWidth = lineWidth; }
void setIsSolid(bool isSolid) { _isSolid = isSolid; }
void setIsDashedLine(bool isDashedLine) { _isDashedLine = isDashedLine; }
void setIgnoreRayIntersection(bool value) { _ignoreRayIntersection = value; }
void setDrawInFront(bool value) { _drawInFront = value; }
void setIsAA(bool value) { _isAA = value; }
virtual AABox getBounds() const override = 0;
@ -64,6 +67,7 @@ protected:
bool _isDashedLine;
bool _ignoreRayIntersection;
bool _drawInFront;
bool _isAA;
};
#endif // hifi_Base3DOverlay_h

View file

@ -38,6 +38,9 @@ namespace render {
if (std::static_pointer_cast<Base3DOverlay>(overlay)->getDrawInFront()) {
builder.withLayered();
}
if (!std::static_pointer_cast<Base3DOverlay>(overlay)->isAA()) {
builder.withLayered();
}
if (overlay->getAlphaPulse() != 0.0f || overlay->getAlpha() != 1.0f) {
builder.withTransparent();
}
@ -51,11 +54,17 @@ namespace render {
}
template <> int payloadGetLayer(const Overlay::Pointer& overlay) {
// MAgic number while we are defining the layering mechanism:
const int LAYER_2D = 2;
const int LAYER_NO_AA = 3;
const int LAYER_2D = 2;
const int LAYER_3D_FRONT = 1;
const int LAYER_3D = 0;
if (overlay->is3D()) {
return (std::dynamic_pointer_cast<Base3DOverlay>(overlay)->getDrawInFront() ? LAYER_3D_FRONT : LAYER_3D);
auto overlay3D = std::dynamic_pointer_cast<Base3DOverlay>(overlay);
if (overlay3D->isAA())
return (overlay3D->getDrawInFront() ? LAYER_3D_FRONT : LAYER_3D);
else
return LAYER_NO_AA;
} else {
return LAYER_2D;
}

View file

@ -18,15 +18,17 @@
#include <QtQuick/QQuickItem>
#include <QtQml/QQmlContext>
#include <AbstractViewStateInterface.h>
#include <gpu/Batch.h>
#include <DependencyManager.h>
#include <GeometryCache.h>
#include <GeometryUtil.h>
#include <TextureCache.h>
#include <gl/OffscreenQmlSurface.h>
#include <PathUtils.h>
#include <gpu/Batch.h>
#include <RegisteredMetaTypes.h>
#include <TabletScriptingInterface.h>
#include <TextureCache.h>
#include <AbstractViewStateInterface.h>
#include <gl/OffscreenQmlSurface.h>
#include <gl/OffscreenQmlSurfaceCache.h>
@ -37,7 +39,7 @@ static const float OPAQUE_ALPHA_THRESHOLD = 0.99f;
const QString Web3DOverlay::TYPE = "web3d";
const QString Web3DOverlay::QML = "Web3DOverlay.qml";
Web3DOverlay::Web3DOverlay() : _dpi(DPI) {
Web3DOverlay::Web3DOverlay() : _dpi(DPI) {
_touchDevice.setCapabilities(QTouchDevice::Position);
_touchDevice.setType(QTouchDevice::TouchScreen);
_touchDevice.setName("RenderableWebEntityItemTouchDevice");
@ -51,13 +53,31 @@ Web3DOverlay::Web3DOverlay(const Web3DOverlay* Web3DOverlay) :
_url(Web3DOverlay->_url),
_scriptURL(Web3DOverlay->_scriptURL),
_dpi(Web3DOverlay->_dpi),
_resolution(Web3DOverlay->_resolution)
_resolution(Web3DOverlay->_resolution),
_showKeyboardFocusHighlight(Web3DOverlay->_showKeyboardFocusHighlight)
{
_geometryId = DependencyManager::get<GeometryCache>()->allocateID();
}
Web3DOverlay::~Web3DOverlay() {
if (_webSurface) {
QQuickItem* rootItem = _webSurface->getRootItem();
if (rootItem && rootItem->objectName() == "tabletRoot") {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", nullptr, nullptr);
}
// Fix for crash in QtWebEngineCore when rapidly switching domains
// Call stop on the QWebEngineView before destroying OffscreenQMLSurface.
if (rootItem) {
QObject* obj = rootItem->findChild<QObject*>("webEngineView");
if (obj) {
// stop loading
QMetaObject::invokeMethod(obj, "stop");
}
}
_webSurface->pause();
_webSurface->disconnect(_connection);
@ -92,15 +112,54 @@ Web3DOverlay::~Web3DOverlay() {
}
void Web3DOverlay::update(float deltatime) {
// FIXME: applyTransformTo causes tablet overlay to detach from tablet entity.
// Perhaps rather than deleting the following code it should be run only if isFacingAvatar() is true?
/*
if (usecTimestampNow() > _transformExpiry) {
Transform transform = getTransform();
applyTransformTo(transform);
setTransform(transform);
if (_webSurface) {
// update globalPosition
_webSurface->getRootContext()->setContextProperty("globalPosition", vec3toVariant(getPosition()));
}
*/
}
QString Web3DOverlay::pickURL() {
QUrl sourceUrl(_url);
if (sourceUrl.scheme() == "http" || sourceUrl.scheme() == "https" ||
_url.toLower().endsWith(".htm") || _url.toLower().endsWith(".html")) {
_webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
return "Web3DOverlay.qml";
} else {
return QUrl::fromLocalFile(PathUtils::resourcesPath()).toString() + "/" + _url;
}
}
void Web3DOverlay::loadSourceURL() {
QUrl sourceUrl(_url);
if (sourceUrl.scheme() == "http" || sourceUrl.scheme() == "https" ||
_url.toLower().endsWith(".htm") || _url.toLower().endsWith(".html")) {
_webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
_webSurface->load("Web3DOverlay.qml");
_webSurface->resume();
_webSurface->getRootItem()->setProperty("url", _url);
_webSurface->getRootItem()->setProperty("scriptURL", _scriptURL);
_webSurface->getRootContext()->setContextProperty("ApplicationInterface", qApp);
} else {
_webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath()));
_webSurface->load(_url, [&](QQmlContext* context, QObject* obj) {});
_webSurface->resume();
if (_webSurface->getRootItem() && _webSurface->getRootItem()->objectName() == "tabletRoot") {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto flags = tabletScriptingInterface->getFlags();
_webSurface->getRootContext()->setContextProperty("offscreenFlags", flags);
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface->getRootItem(), _webSurface.data());
// Override min fps for tablet UI, for silky smooth scrolling
_webSurface->setMaxFps(90);
}
}
_webSurface->getRootContext()->setContextProperty("globalPosition", vec3toVariant(getPosition()));
}
void Web3DOverlay::render(RenderArgs* args) {
@ -111,10 +170,11 @@ void Web3DOverlay::render(RenderArgs* args) {
QOpenGLContext * currentContext = QOpenGLContext::currentContext();
QSurface * currentSurface = currentContext->surface();
if (!_webSurface) {
_webSurface = DependencyManager::get<OffscreenQmlSurfaceCache>()->acquire(QML);
_webSurface = DependencyManager::get<OffscreenQmlSurfaceCache>()->acquire(pickURL());
_webSurface->setMaxFps(10);
// FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces
// and the current rendering load)
loadSourceURL();
_webSurface->resume();
_webSurface->resize(QSize(_resolution.x, _resolution.y));
_webSurface->getRootItem()->setProperty("url", _url);
@ -143,7 +203,7 @@ void Web3DOverlay::render(RenderArgs* args) {
point.setPos(windowPoint);
QList<QTouchEvent::TouchPoint> touchPoints;
touchPoints.push_back(point);
QTouchEvent* touchEvent = new QTouchEvent(QEvent::TouchEnd, nullptr, Qt::NoModifier, Qt::TouchPointReleased,
QTouchEvent* touchEvent = new QTouchEvent(QEvent::TouchEnd, nullptr, Qt::NoModifier, Qt::TouchPointReleased,
touchPoints);
touchEvent->setWindow(_webSurface->getWindow());
touchEvent->setDevice(&_touchDevice);
@ -160,7 +220,7 @@ void Web3DOverlay::render(RenderArgs* args) {
vec4 color(toGlm(getColor()), getAlpha());
Transform transform = getTransform();
// FIXME: applyTransformTo causes tablet overlay to detach from tablet entity.
// Perhaps rather than deleting the following code it should be run only if isFacingAvatar() is true?
/*
@ -189,9 +249,9 @@ void Web3DOverlay::render(RenderArgs* args) {
batch.setModelTransform(transform);
auto geometryCache = DependencyManager::get<GeometryCache>();
if (color.a < OPAQUE_ALPHA_THRESHOLD) {
geometryCache->bindTransparentWebBrowserProgram(batch);
geometryCache->bindTransparentWebBrowserProgram(batch, _isAA);
} else {
geometryCache->bindOpaqueWebBrowserProgram(batch);
geometryCache->bindOpaqueWebBrowserProgram(batch, _isAA);
}
geometryCache->renderQuad(batch, halfSize * -1.0f, halfSize, vec2(0), vec2(1), color, _geometryId);
batch.setResourceTexture(0, args->_whiteTexture); // restore default white color after me
@ -230,7 +290,7 @@ void Web3DOverlay::handlePointerEvent(const PointerEvent& event) {
if (event.getType() == PointerEvent::Move) {
// Forward a mouse move event to the Web surface.
QMouseEvent* mouseEvent = new QMouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, Qt::NoButton,
QMouseEvent* mouseEvent = new QMouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, Qt::NoButton,
Qt::NoButton, Qt::NoModifier);
QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent);
}
@ -309,6 +369,11 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) {
if (dpi.isValid()) {
_dpi = dpi.toFloat();
}
auto showKeyboardFocusHighlight = properties["showKeyboardFocusHighlight"];
if (showKeyboardFocusHighlight.isValid()) {
_showKeyboardFocusHighlight = showKeyboardFocusHighlight.toBool();
}
}
QVariant Web3DOverlay::getProperty(const QString& property) {
@ -324,6 +389,9 @@ QVariant Web3DOverlay::getProperty(const QString& property) {
if (property == "dpi") {
return _dpi;
}
if (property == "showKeyboardFocusHighlight") {
return _showKeyboardFocusHighlight;
}
return Billboard3DOverlay::getProperty(property);
}
@ -331,7 +399,7 @@ void Web3DOverlay::setURL(const QString& url) {
_url = url;
if (_webSurface) {
AbstractViewStateInterface::instance()->postLambdaEvent([this, url] {
_webSurface->getRootItem()->setProperty("url", url);
loadSourceURL();
});
}
}

View file

@ -29,6 +29,8 @@ public:
Web3DOverlay(const Web3DOverlay* Web3DOverlay);
virtual ~Web3DOverlay();
QString pickURL();
void loadSourceURL();
virtual void render(RenderArgs* args) override;
virtual const render::ShapeKey getShapeKey() override;
@ -47,7 +49,7 @@ public:
glm::vec2 getSize();
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, glm::vec3& surfaceNormal) override;
virtual Web3DOverlay* createClone() const override;
@ -68,6 +70,7 @@ private:
float _dpi;
vec2 _resolution{ 640, 480 };
int _geometryId { 0 };
bool _showKeyboardFocusHighlight{ true };
bool _pressed{ false };
QTouchDevice _touchDevice;

View file

@ -112,6 +112,42 @@ private:
bool _quit { false };
};
void AudioInjectorsThread::prepare() {
_audio->prepareLocalAudioInjectors();
}
static void channelUpmix(int16_t* source, int16_t* dest, int numSamples, int numExtraChannels) {
for (int i = 0; i < numSamples/2; i++) {
// read 2 samples
int16_t left = *source++;
int16_t right = *source++;
// write 2 + N samples
*dest++ = left;
*dest++ = right;
for (int n = 0; n < numExtraChannels; n++) {
*dest++ = 0;
}
}
}
static void channelDownmix(int16_t* source, int16_t* dest, int numSamples) {
for (int i = 0; i < numSamples/2; i++) {
// read 2 samples
int16_t left = *source++;
int16_t right = *source++;
// write 1 sample
*dest++ = (int16_t)((left + right) / 2);
}
}
static inline float convertToFloat(int16_t sample) {
return (float)sample * (1 / 32768.0f);
}
AudioClient::AudioClient() :
AbstractAudioInterface(),
_gate(this),
@ -127,6 +163,7 @@ AudioClient::AudioClient() :
_loopbackAudioOutput(NULL),
_loopbackOutputDevice(NULL),
_inputRingBuffer(0),
_localInjectorsStream(0),
_receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES),
_isStereoInput(false),
_outputStarveDetectionStartTimeMsec(0),
@ -144,13 +181,18 @@ AudioClient::AudioClient() :
_reverbOptions(&_scriptReverbOptions),
_inputToNetworkResampler(NULL),
_networkToOutputResampler(NULL),
_localToOutputResampler(NULL),
_localAudioThread(this),
_audioLimiter(AudioConstants::SAMPLE_RATE, OUTPUT_CHANNEL_COUNT),
_outgoingAvatarAudioSequenceNumber(0),
_audioOutputIODevice(_receivedAudioStream, this),
_audioOutputIODevice(_localInjectorsStream, _receivedAudioStream, this),
_stats(&_receivedAudioStream),
_inputGate(),
_positionGetter(DEFAULT_POSITION_GETTER),
_orientationGetter(DEFAULT_ORIENTATION_GETTER) {
// avoid putting a lock in the device callback
assert(_localSamplesAvailable.is_lock_free());
// deprecate legacy settings
{
Setting::Handle<int>::Deprecated("maxFramesOverDesired", InboundAudioStream::MAX_FRAMES_OVER_DESIRED);
@ -176,6 +218,10 @@ AudioClient::AudioClient() :
_checkDevicesThread->setPriority(QThread::LowPriority);
_checkDevicesThread->start();
// start a thread to process local injectors
_localAudioThread.setObjectName("LocalAudio Thread");
_localAudioThread.start();
configureReverb();
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
@ -213,6 +259,7 @@ void AudioClient::reset() {
_stats.reset();
_sourceReverb.reset();
_listenerReverb.reset();
_localReverb.reset();
}
void AudioClient::audioMixerKilled() {
@ -365,7 +412,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
CoUninitialize();
}
qCDebug(audioclient) << "DEBUG [" << deviceName << "] [" << getNamedAudioDeviceForMode(mode, deviceName).deviceName() << "]";
qCDebug(audioclient) << "[" << deviceName << "] [" << getNamedAudioDeviceForMode(mode, deviceName).deviceName() << "]";
return getNamedAudioDeviceForMode(mode, deviceName);
#endif
@ -387,12 +434,12 @@ bool nativeFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
audioFormat.setByteOrder(QAudioFormat::LittleEndian);
if (!audioDevice.isFormatSupported(audioFormat)) {
qCDebug(audioclient) << "WARNING: The native format is" << audioFormat << "but isFormatSupported() failed.";
qCWarning(audioclient) << "The native format is" << audioFormat << "but isFormatSupported() failed.";
return false;
}
// converting to/from this rate must produce an integral number of samples
if (audioFormat.sampleRate() * AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL % AudioConstants::SAMPLE_RATE != 0) {
qCDebug(audioclient) << "WARNING: The native sample rate [" << audioFormat.sampleRate() << "] is not supported.";
qCWarning(audioclient) << "The native sample rate [" << audioFormat.sampleRate() << "] is not supported.";
return false;
}
return true;
@ -726,12 +773,12 @@ QVector<QString> AudioClient::getDeviceNames(QAudio::Mode mode) {
}
bool AudioClient::switchInputToAudioDevice(const QString& inputDeviceName) {
qCDebug(audioclient) << "DEBUG [" << inputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName).deviceName() << "]";
qCDebug(audioclient) << "[" << inputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName).deviceName() << "]";
return switchInputToAudioDevice(getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName));
}
bool AudioClient::switchOutputToAudioDevice(const QString& outputDeviceName) {
qCDebug(audioclient) << "DEBUG [" << outputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName).deviceName() << "]";
qCDebug(audioclient) << "[" << outputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName).deviceName() << "]";
return switchOutputToAudioDevice(getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName));
}
@ -762,6 +809,7 @@ void AudioClient::configureReverb() {
p.wetDryMix = _reverbOptions->getWetDryMix();
_listenerReverb.setParameters(&p);
_localReverb.setParameters(&p);
// used only for adding self-reverb to loopback audio
p.sampleRate = _outputFormat.sampleRate();
@ -808,6 +856,7 @@ void AudioClient::setReverb(bool reverb) {
if (!_reverb) {
_sourceReverb.reset();
_listenerReverb.reset();
_localReverb.reset();
}
}
@ -841,36 +890,6 @@ void AudioClient::setReverbOptions(const AudioEffectOptions* options) {
}
}
static void channelUpmix(int16_t* source, int16_t* dest, int numSamples, int numExtraChannels) {
for (int i = 0; i < numSamples/2; i++) {
// read 2 samples
int16_t left = *source++;
int16_t right = *source++;
// write 2 + N samples
*dest++ = left;
*dest++ = right;
for (int n = 0; n < numExtraChannels; n++) {
*dest++ = 0;
}
}
}
static void channelDownmix(int16_t* source, int16_t* dest, int numSamples) {
for (int i = 0; i < numSamples/2; i++) {
// read 2 samples
int16_t left = *source++;
int16_t right = *source++;
// write 1 sample
*dest++ = (int16_t)((left + right) / 2);
}
}
void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
// If there is server echo, reverb will be applied to the recieved audio stream so no need to have it here.
bool hasReverb = _reverb || _receivedAudioStream.hasReverb();
@ -1082,14 +1101,78 @@ void AudioClient::handleRecordedAudioInput(const QByteArray& audio) {
PacketType::MicrophoneAudioWithEcho, _selectedCodecName);
}
void AudioClient::mixLocalAudioInjectors(float* mixBuffer) {
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();
while (samplesNeeded > 0) {
// lock for every write to avoid locking out the device callback
// this lock is intentional - the buffer is only lock-free in its use in the device callback
Lock lock(_localAudioMutex);
samplesNeeded = bufferCapacity - _localSamplesAvailable.load(std::memory_order_relaxed);
if (samplesNeeded <= 0) {
break;
}
// get a network frame of local injectors' audio
if (!mixLocalAudioInjectors(_localMixBuffer)) {
break;
}
// reverb
if (_reverb) {
_localReverb.render(_localMixBuffer, _localMixBuffer, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
}
int samples;
if (_localToOutputResampler) {
// resample to output sample rate
int frames = _localToOutputResampler->render(_localMixBuffer, _localOutputMixBuffer,
AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
// write to local injectors' ring buffer
samples = frames * AudioConstants::STEREO;
_localInjectorsStream.writeSamples(_localOutputMixBuffer, samples);
} else {
// write to local injectors' ring buffer
samples = AudioConstants::NETWORK_FRAME_SAMPLES_STEREO;
_localInjectorsStream.writeSamples(_localMixBuffer,
AudioConstants::NETWORK_FRAME_SAMPLES_STEREO);
}
_localSamplesAvailable.fetch_add(samples, std::memory_order_release);
samplesNeeded -= samples;
}
}
bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) {
QVector<AudioInjector*> injectorsToRemove;
// lock the injector vector
Lock lock(_injectorsMutex);
for (AudioInjector* injector : getActiveLocalAudioInjectors()) {
if (_activeLocalAudioInjectors.size() == 0) {
return false;
}
memset(mixBuffer, 0, AudioConstants::NETWORK_FRAME_SAMPLES_STEREO * sizeof(float));
for (AudioInjector* injector : _activeLocalAudioInjectors) {
if (injector->getLocalBuffer()) {
static const int HRTF_DATASET_INDEX = 1;
@ -1098,8 +1181,8 @@ void AudioClient::mixLocalAudioInjectors(float* mixBuffer) {
qint64 bytesToRead = numChannels * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL;
// get one frame from the injector
memset(_scratchBuffer, 0, bytesToRead);
if (0 < injector->getLocalBuffer()->readData((char*)_scratchBuffer, bytesToRead)) {
memset(_localScratchBuffer, 0, bytesToRead);
if (0 < injector->getLocalBuffer()->readData((char*)_localScratchBuffer, bytesToRead)) {
if (injector->isAmbisonic()) {
@ -1119,7 +1202,7 @@ void AudioClient::mixLocalAudioInjectors(float* mixBuffer) {
float qz = relativeOrientation.y;
// Ambisonic gets spatialized into mixBuffer
injector->getLocalFOA().render(_scratchBuffer, mixBuffer, HRTF_DATASET_INDEX,
injector->getLocalFOA().render(_localScratchBuffer, mixBuffer, HRTF_DATASET_INDEX,
qw, qx, qy, qz, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
} else if (injector->isStereo()) {
@ -1127,7 +1210,7 @@ void AudioClient::mixLocalAudioInjectors(float* mixBuffer) {
// stereo gets directly mixed into mixBuffer
float gain = injector->getVolume();
for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; i++) {
mixBuffer[i] += (float)_scratchBuffer[i] * (1/32768.0f) * gain;
mixBuffer[i] += convertToFloat(_localScratchBuffer[i]) * gain;
}
} else {
@ -1136,10 +1219,10 @@ void AudioClient::mixLocalAudioInjectors(float* mixBuffer) {
glm::vec3 relativePosition = injector->getPosition() - _positionGetter();
float distance = glm::max(glm::length(relativePosition), EPSILON);
float gain = gainForSource(distance, injector->getVolume());
float azimuth = azimuthForSource(relativePosition);
float azimuth = azimuthForSource(relativePosition);
// mono gets spatialized into mixBuffer
injector->getLocalHRTF().render(_scratchBuffer, mixBuffer, HRTF_DATASET_INDEX,
injector->getLocalHRTF().render(_localScratchBuffer, mixBuffer, HRTF_DATASET_INDEX,
azimuth, distance, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
}
@ -1160,8 +1243,10 @@ void AudioClient::mixLocalAudioInjectors(float* mixBuffer) {
for (AudioInjector* injector : injectorsToRemove) {
qCDebug(audioclient) << "removing injector";
getActiveLocalAudioInjectors().removeOne(injector);
_activeLocalAudioInjectors.removeOne(injector);
}
return true;
}
void AudioClient::processReceivedSamples(const QByteArray& decodedBuffer, QByteArray& outputBuffer) {
@ -1172,33 +1257,24 @@ void AudioClient::processReceivedSamples(const QByteArray& decodedBuffer, QByteA
outputBuffer.resize(_outputFrameSize * AudioConstants::SAMPLE_SIZE);
int16_t* outputSamples = reinterpret_cast<int16_t*>(outputBuffer.data());
// convert network audio to float
for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; i++) {
_mixBuffer[i] = (float)decodedSamples[i] * (1/32768.0f);
}
// mix in active injectors
if (getActiveLocalAudioInjectors().size() > 0) {
mixLocalAudioInjectors(_mixBuffer);
}
bool hasReverb = _reverb || _receivedAudioStream.hasReverb();
// apply stereo reverb
bool hasReverb = _reverb || _receivedAudioStream.hasReverb();
if (hasReverb) {
updateReverbOptions();
_listenerReverb.render(_mixBuffer, _mixBuffer, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
int16_t* reverbSamples = _networkToOutputResampler ? _networkScratchBuffer : outputSamples;
_listenerReverb.render(decodedSamples, reverbSamples, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
}
// resample to output sample rate
if (_networkToOutputResampler) {
const int16_t* inputSamples = hasReverb ? _networkScratchBuffer : decodedSamples;
_networkToOutputResampler->render(inputSamples, outputSamples, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
}
// resample to output sample rate
_audioLimiter.render(_mixBuffer, _scratchBuffer, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
_networkToOutputResampler->render(_scratchBuffer, outputSamples, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
} else {
// no resampling needed
_audioLimiter.render(_mixBuffer, outputSamples, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
// if no transformations were applied, we still need to copy the buffer
if (!hasReverb && !_networkToOutputResampler) {
memcpy(outputSamples, decodedSamples, decodedBuffer.size());
}
}
@ -1381,6 +1457,9 @@ void AudioClient::outputNotify() {
bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo) {
bool supportedFormat = false;
Lock lock(_localAudioMutex);
_localSamplesAvailable.exchange(0, std::memory_order_release);
// cleanup any previously initialized device
if (_audioOutput) {
_audioOutput->stop();
@ -1391,12 +1470,24 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
_loopbackOutputDevice = NULL;
delete _loopbackAudioOutput;
_loopbackAudioOutput = NULL;
delete[] _outputMixBuffer;
_outputMixBuffer = NULL;
delete[] _outputScratchBuffer;
_outputScratchBuffer = NULL;
delete[] _localOutputMixBuffer;
_localOutputMixBuffer = NULL;
}
if (_networkToOutputResampler) {
// if we were using an input to network resampler, delete it here
delete _networkToOutputResampler;
_networkToOutputResampler = NULL;
delete _localToOutputResampler;
_localToOutputResampler = NULL;
}
if (!outputDeviceInfo.isNull()) {
@ -1416,6 +1507,7 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
assert(_outputFormat.sampleSize() == 16);
_networkToOutputResampler = new AudioSRC(_desiredOutputFormat.sampleRate(), _outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT);
_localToOutputResampler = new AudioSRC(_desiredOutputFormat.sampleRate(), _outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT);
} else {
qCDebug(audioclient) << "No resampling required for network output to match actual output format.";
@ -1441,6 +1533,14 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
_audioOutput->start(&_audioOutputIODevice);
lock.unlock();
int periodSampleSize = _audioOutput->periodSize() / AudioConstants::SAMPLE_SIZE;
// device callback is not restricted to periodSampleSize, so double the mix/scratch buffer sizes
_outputPeriod = periodSampleSize * 2;
_outputMixBuffer = new float[_outputPeriod];
_outputScratchBuffer = new int16_t[_outputPeriod];
_localOutputMixBuffer = new float[_outputPeriod];
_localInjectorsStream.resizeForFrameSize(_outputPeriod * 2);
qCDebug(audioclient) << "Output Buffer capacity in frames: " << _audioOutput->bufferSize() / AudioConstants::SAMPLE_SIZE / (float)deviceFrameSize <<
"requested bytes:" << requestedSize << "actual bytes:" << _audioOutput->bufferSize() <<
"os default:" << osDefaultBufferSize << "period size:" << _audioOutput->periodSize();
@ -1550,26 +1650,61 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
// samples requested from OUTPUT_CHANNEL_COUNT
int deviceChannelCount = _audio->_outputFormat.channelCount();
int samplesRequested = (int)(maxSize / AudioConstants::SAMPLE_SIZE) * OUTPUT_CHANNEL_COUNT / deviceChannelCount;
// restrict samplesRequested to the size of our mix/scratch buffers
samplesRequested = std::min(samplesRequested, _audio->_outputPeriod);
int samplesPopped;
int bytesWritten;
int16_t* scratchBuffer = _audio->_outputScratchBuffer;
float* mixBuffer = _audio->_outputMixBuffer;
if ((samplesPopped = _receivedAudioStream.popSamples(samplesRequested, false)) > 0) {
qCDebug(audiostream, "Read %d samples from buffer (%d available)", samplesPopped, _receivedAudioStream.getSamplesAvailable());
int networkSamplesPopped;
if ((networkSamplesPopped = _receivedAudioStream.popSamples(samplesRequested, false)) > 0) {
qCDebug(audiostream, "Read %d samples from buffer (%d available, %d requested)", networkSamplesPopped, _receivedAudioStream.getSamplesAvailable(), samplesRequested);
AudioRingBuffer::ConstIterator lastPopOutput = _receivedAudioStream.getLastPopOutput();
lastPopOutput.readSamples(scratchBuffer, networkSamplesPopped);
// if required, upmix or downmix to deviceChannelCount
if (deviceChannelCount == OUTPUT_CHANNEL_COUNT) {
lastPopOutput.readSamples((int16_t*)data, samplesPopped);
} else if (deviceChannelCount > OUTPUT_CHANNEL_COUNT) {
lastPopOutput.readSamplesWithUpmix((int16_t*)data, samplesPopped, deviceChannelCount - OUTPUT_CHANNEL_COUNT);
} else {
lastPopOutput.readSamplesWithDownmix((int16_t*)data, samplesPopped);
for (int i = 0; i < networkSamplesPopped; i++) {
mixBuffer[i] = convertToFloat(scratchBuffer[i]);
}
bytesWritten = (samplesPopped * AudioConstants::SAMPLE_SIZE) * deviceChannelCount / OUTPUT_CHANNEL_COUNT;
samplesRequested = networkSamplesPopped;
}
int injectorSamplesPopped = 0;
{
Lock lock(_audio->_localAudioMutex);
bool append = networkSamplesPopped > 0;
samplesRequested = std::min(samplesRequested, _audio->_localSamplesAvailable.load(std::memory_order_acquire));
if ((injectorSamplesPopped = _localInjectorsStream.appendSamples(mixBuffer, samplesRequested, append)) > 0) {
_audio->_localSamplesAvailable.fetch_sub(injectorSamplesPopped, std::memory_order_release);
qCDebug(audiostream, "Read %d samples from injectors (%d available, %d requested)", injectorSamplesPopped, _localInjectorsStream.samplesAvailable(), samplesRequested);
}
}
// prepare injectors for the next callback
QMetaObject::invokeMethod(&_audio->_localAudioThread, "prepare", Qt::QueuedConnection);
int samplesPopped = std::max(networkSamplesPopped, injectorSamplesPopped);
int framesPopped = samplesPopped / AudioConstants::STEREO;
int bytesWritten;
if (samplesPopped > 0) {
if (deviceChannelCount == OUTPUT_CHANNEL_COUNT) {
// limit the audio
_audio->_audioLimiter.render(mixBuffer, (int16_t*)data, framesPopped);
} else {
_audio->_audioLimiter.render(mixBuffer, scratchBuffer, framesPopped);
// upmix or downmix to deviceChannelCount
if (deviceChannelCount > OUTPUT_CHANNEL_COUNT) {
int extraChannels = deviceChannelCount - OUTPUT_CHANNEL_COUNT;
channelUpmix(scratchBuffer, (int16_t*)data, samplesPopped, extraChannels);
} else {
channelDownmix(scratchBuffer, (int16_t*)data, samplesPopped);
}
}
bytesWritten = framesPopped * AudioConstants::SAMPLE_SIZE * deviceChannelCount;
} else {
// nothing on network, don't grab anything from injectors, and just return 0s
// this will flood the log: qCDebug(audioclient, "empty/partial network buffer");
memset(data, 0, maxSize);
bytesWritten = maxSize;
}

View file

@ -69,9 +69,24 @@ class QIODevice;
class Transform;
class NLPacket;
class AudioInjectorsThread : public QThread {
Q_OBJECT
public:
AudioInjectorsThread(AudioClient* audio) : _audio(audio) {}
public slots :
void prepare();
private:
AudioClient* _audio;
};
class AudioClient : public AbstractAudioInterface, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
using LocalInjectorsStream = AudioMixRingBuffer;
public:
static const int MIN_BUFFER_FRAMES;
static const int MAX_BUFFER_FRAMES;
@ -84,8 +99,10 @@ public:
class AudioOutputIODevice : public QIODevice {
public:
AudioOutputIODevice(MixedProcessedAudioStream& receivedAudioStream, AudioClient* audio) :
_receivedAudioStream(receivedAudioStream), _audio(audio), _unfulfilledReads(0) {};
AudioOutputIODevice(LocalInjectorsStream& localInjectorsStream, MixedProcessedAudioStream& receivedAudioStream,
AudioClient* audio) :
_localInjectorsStream(localInjectorsStream), _receivedAudioStream(receivedAudioStream),
_audio(audio), _unfulfilledReads(0) {}
void start() { open(QIODevice::ReadOnly | QIODevice::Unbuffered); }
void stop() { close(); }
@ -93,6 +110,7 @@ public:
qint64 writeData(const char * data, qint64 maxSize) override { return 0; }
int getRecentUnfulfilledReads() { int unfulfilledReads = _unfulfilledReads; _unfulfilledReads = 0; return unfulfilledReads; }
private:
LocalInjectorsStream& _localInjectorsStream;
MixedProcessedAudioStream& _receivedAudioStream;
AudioClient* _audio;
int _unfulfilledReads;
@ -129,8 +147,6 @@ public:
Q_INVOKABLE void setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec3 scale);
QVector<AudioInjector*>& getActiveLocalAudioInjectors() { return _activeLocalAudioInjectors; }
void checkDevices();
static const float CALLBACK_ACCELERATOR_RATIO;
@ -171,6 +187,7 @@ public slots:
int setOutputBufferSize(int numFrames, bool persist = true);
void prepareLocalAudioInjectors();
bool outputLocalInjector(AudioInjector* injector) override;
bool shouldLoopbackInjectors() override { return _shouldEchoToServer; }
@ -218,7 +235,7 @@ protected:
private:
void outputFormatChanged();
void mixLocalAudioInjectors(float* mixBuffer);
bool mixLocalAudioInjectors(float* mixBuffer);
float azimuthForSource(const glm::vec3& relativePosition);
float gainForSource(float distance, float volume);
@ -262,6 +279,10 @@ private:
QAudioOutput* _loopbackAudioOutput;
QIODevice* _loopbackOutputDevice;
AudioRingBuffer _inputRingBuffer;
LocalInjectorsStream _localInjectorsStream;
// In order to use _localInjectorsStream as a lock-free pipe,
// use it with a single producer/consumer, and track available samples
std::atomic<int> _localSamplesAvailable { 0 };
MixedProcessedAudioStream _receivedAudioStream;
bool _isStereoInput;
@ -292,14 +313,28 @@ private:
AudioEffectOptions* _reverbOptions;
AudioReverb _sourceReverb { AudioConstants::SAMPLE_RATE };
AudioReverb _listenerReverb { AudioConstants::SAMPLE_RATE };
AudioReverb _localReverb { AudioConstants::SAMPLE_RATE };
// possible streams needed for resample
AudioSRC* _inputToNetworkResampler;
AudioSRC* _networkToOutputResampler;
AudioSRC* _localToOutputResampler;
// for network audio (used by network audio thread)
int16_t _networkScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC];
// for local audio (used by audio injectors thread)
float _localMixBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO];
int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC];
float* _localOutputMixBuffer { NULL };
AudioInjectorsThread _localAudioThread;
Mutex _localAudioMutex;
// for output audio (used by this thread)
int _outputPeriod { 0 };
float* _outputMixBuffer { NULL };
int16_t* _outputScratchBuffer { NULL };
// for local hrtf-ing
float _mixBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO];
int16_t _scratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC];
AudioLimiter _audioLimiter;
// Adds Reverb

View file

@ -26,46 +26,51 @@
static const QString RING_BUFFER_OVERFLOW_DEBUG { "AudioRingBuffer::writeData has overflown the buffer. Overwriting old data." };
static const QString DROPPED_SILENT_DEBUG { "AudioRingBuffer::addSilentSamples dropping silent samples to prevent overflow." };
AudioRingBuffer::AudioRingBuffer(int numFrameSamples, int numFramesCapacity) :
template <class T>
AudioRingBufferTemplate<T>::AudioRingBufferTemplate(int numFrameSamples, int numFramesCapacity) :
_numFrameSamples(numFrameSamples),
_frameCapacity(numFramesCapacity),
_sampleCapacity(numFrameSamples * numFramesCapacity),
_bufferLength(numFrameSamples * (numFramesCapacity + 1))
{
if (numFrameSamples) {
_buffer = new int16_t[_bufferLength];
memset(_buffer, 0, _bufferLength * sizeof(int16_t));
_buffer = new Sample[_bufferLength];
memset(_buffer, 0, _bufferLength * SampleSize);
_nextOutput = _buffer;
_endOfLastWrite = _buffer;
}
static QString repeatedOverflowMessage = LogHandler::getInstance().addRepeatedMessageRegex(RING_BUFFER_OVERFLOW_DEBUG);
static QString repeatedDroppedMessage = LogHandler::getInstance().addRepeatedMessageRegex(DROPPED_SILENT_DEBUG);
};
}
AudioRingBuffer::~AudioRingBuffer() {
template <class T>
AudioRingBufferTemplate<T>::~AudioRingBufferTemplate() {
delete[] _buffer;
}
void AudioRingBuffer::clear() {
template <class T>
void AudioRingBufferTemplate<T>::clear() {
_endOfLastWrite = _buffer;
_nextOutput = _buffer;
}
void AudioRingBuffer::reset() {
template <class T>
void AudioRingBufferTemplate<T>::reset() {
clear();
_overflowCount = 0;
}
void AudioRingBuffer::resizeForFrameSize(int numFrameSamples) {
template <class T>
void AudioRingBufferTemplate<T>::resizeForFrameSize(int numFrameSamples) {
delete[] _buffer;
_numFrameSamples = numFrameSamples;
_sampleCapacity = numFrameSamples * _frameCapacity;
_bufferLength = numFrameSamples * (_frameCapacity + 1);
if (numFrameSamples) {
_buffer = new int16_t[_bufferLength];
memset(_buffer, 0, _bufferLength * sizeof(int16_t));
_buffer = new Sample[_bufferLength];
memset(_buffer, 0, _bufferLength * SampleSize);
} else {
_buffer = nullptr;
}
@ -73,17 +78,29 @@ void AudioRingBuffer::resizeForFrameSize(int numFrameSamples) {
reset();
}
int AudioRingBuffer::readSamples(int16_t* destination, int maxSamples) {
return readData((char*)destination, maxSamples * sizeof(int16_t)) / sizeof(int16_t);
template <class T>
int AudioRingBufferTemplate<T>::readSamples(Sample* destination, int maxSamples) {
return readData((char*)destination, maxSamples * SampleSize) / SampleSize;
}
int AudioRingBuffer::writeSamples(const int16_t* source, int maxSamples) {
return writeData((char*)source, maxSamples * sizeof(int16_t)) / sizeof(int16_t);
template <class T>
int AudioRingBufferTemplate<T>::appendSamples(Sample* destination, int maxSamples, bool append) {
if (append) {
return appendData((char*)destination, maxSamples * SampleSize) / SampleSize;
} else {
return readData((char*)destination, maxSamples * SampleSize) / SampleSize;
}
}
int AudioRingBuffer::readData(char *data, int maxSize) {
template <class T>
int AudioRingBufferTemplate<T>::writeSamples(const Sample* source, int maxSamples) {
return writeData((char*)source, maxSamples * SampleSize) / SampleSize;
}
template <class T>
int AudioRingBufferTemplate<T>::readData(char *data, int maxSize) {
// only copy up to the number of samples we have available
int maxSamples = maxSize / sizeof(int16_t);
int maxSamples = maxSize / SampleSize;
int numReadSamples = std::min(maxSamples, samplesAvailable());
if (_nextOutput + numReadSamples > _buffer + _bufferLength) {
@ -91,22 +108,56 @@ int AudioRingBuffer::readData(char *data, int maxSize) {
int numSamplesToEnd = (_buffer + _bufferLength) - _nextOutput;
// read to the end of the buffer
memcpy(data, _nextOutput, numSamplesToEnd * sizeof(int16_t));
memcpy(data, _nextOutput, numSamplesToEnd * SampleSize);
// read the rest from the beginning of the buffer
memcpy(data + (numSamplesToEnd * sizeof(int16_t)), _buffer, (numReadSamples - numSamplesToEnd) * sizeof(int16_t));
memcpy(data + (numSamplesToEnd * SampleSize), _buffer, (numReadSamples - numSamplesToEnd) * SampleSize);
} else {
memcpy(data, _nextOutput, numReadSamples * sizeof(int16_t));
memcpy(data, _nextOutput, numReadSamples * SampleSize);
}
shiftReadPosition(numReadSamples);
return numReadSamples * sizeof(int16_t);
return numReadSamples * SampleSize;
}
int AudioRingBuffer::writeData(const char* data, int maxSize) {
template <class T>
int AudioRingBufferTemplate<T>::appendData(char *data, int maxSize) {
// only copy up to the number of samples we have available
int maxSamples = maxSize / SampleSize;
int numReadSamples = std::min(maxSamples, samplesAvailable());
Sample* dest = reinterpret_cast<Sample*>(data);
Sample* output = _nextOutput;
if (_nextOutput + numReadSamples > _buffer + _bufferLength) {
// we're going to need to do two reads to get this data, it wraps around the edge
int numSamplesToEnd = (_buffer + _bufferLength) - _nextOutput;
// read to the end of the buffer
for (int i = 0; i < numSamplesToEnd; i++) {
*dest++ += *output++;
}
// read the rest from the beginning of the buffer
output = _buffer;
for (int i = 0; i < (numReadSamples - numSamplesToEnd); i++) {
*dest++ += *output++;
}
} else {
for (int i = 0; i < numReadSamples; i++) {
*dest++ += *output++;
}
}
shiftReadPosition(numReadSamples);
return numReadSamples * SampleSize;
}
template <class T>
int AudioRingBufferTemplate<T>::writeData(const char* data, int maxSize) {
// only copy up to the number of samples we have capacity for
int maxSamples = maxSize / sizeof(int16_t);
int maxSamples = maxSize / SampleSize;
int numWriteSamples = std::min(maxSamples, _sampleCapacity);
int samplesRoomFor = _sampleCapacity - samplesAvailable();
@ -124,20 +175,21 @@ int AudioRingBuffer::writeData(const char* data, int maxSize) {
int numSamplesToEnd = (_buffer + _bufferLength) - _endOfLastWrite;
// write to the end of the buffer
memcpy(_endOfLastWrite, data, numSamplesToEnd * sizeof(int16_t));
memcpy(_endOfLastWrite, data, numSamplesToEnd * SampleSize);
// write the rest to the beginning of the buffer
memcpy(_buffer, data + (numSamplesToEnd * sizeof(int16_t)), (numWriteSamples - numSamplesToEnd) * sizeof(int16_t));
memcpy(_buffer, data + (numSamplesToEnd * SampleSize), (numWriteSamples - numSamplesToEnd) * SampleSize);
} else {
memcpy(_endOfLastWrite, data, numWriteSamples * sizeof(int16_t));
memcpy(_endOfLastWrite, data, numWriteSamples * SampleSize);
}
_endOfLastWrite = shiftedPositionAccomodatingWrap(_endOfLastWrite, numWriteSamples);
return numWriteSamples * sizeof(int16_t);
return numWriteSamples * SampleSize;
}
int AudioRingBuffer::samplesAvailable() const {
template <class T>
int AudioRingBufferTemplate<T>::samplesAvailable() const {
if (!_endOfLastWrite) {
return 0;
}
@ -149,31 +201,8 @@ int AudioRingBuffer::samplesAvailable() const {
return sampleDifference;
}
int AudioRingBuffer::addSilentSamples(int silentSamples) {
// NOTE: This implementation is nearly identical to writeData save for s/memcpy/memset, refer to comments there
int numWriteSamples = std::min(silentSamples, _sampleCapacity);
int samplesRoomFor = _sampleCapacity - samplesAvailable();
if (numWriteSamples > samplesRoomFor) {
numWriteSamples = samplesRoomFor;
qCDebug(audio) << qPrintable(DROPPED_SILENT_DEBUG);
}
if (_endOfLastWrite + numWriteSamples > _buffer + _bufferLength) {
int numSamplesToEnd = (_buffer + _bufferLength) - _endOfLastWrite;
memset(_endOfLastWrite, 0, numSamplesToEnd * sizeof(int16_t));
memset(_buffer, 0, (numWriteSamples - numSamplesToEnd) * sizeof(int16_t));
} else {
memset(_endOfLastWrite, 0, numWriteSamples * sizeof(int16_t));
}
_endOfLastWrite = shiftedPositionAccomodatingWrap(_endOfLastWrite, numWriteSamples);
return numWriteSamples;
}
int16_t* AudioRingBuffer::shiftedPositionAccomodatingWrap(int16_t* position, int numSamplesShift) const {
template <class T>
typename AudioRingBufferTemplate<T>::Sample* AudioRingBufferTemplate<T>::shiftedPositionAccomodatingWrap(Sample* position, int numSamplesShift) const {
// NOTE: It is possible to shift out-of-bounds if (|numSamplesShift| > 2 * _bufferLength), but this should not occur
if (numSamplesShift > 0 && position + numSamplesShift >= _buffer + _bufferLength) {
// this shift will wrap the position around to the beginning of the ring
@ -186,11 +215,37 @@ int16_t* AudioRingBuffer::shiftedPositionAccomodatingWrap(int16_t* position, int
}
}
float AudioRingBuffer::getFrameLoudness(const int16_t* frameStart) const {
template <class T>
int AudioRingBufferTemplate<T>::addSilentSamples(int silentSamples) {
// NOTE: This implementation is nearly identical to writeData save for s/memcpy/memset, refer to comments there
int numWriteSamples = std::min(silentSamples, _sampleCapacity);
int samplesRoomFor = _sampleCapacity - samplesAvailable();
if (numWriteSamples > samplesRoomFor) {
numWriteSamples = samplesRoomFor;
qCDebug(audio) << qPrintable(DROPPED_SILENT_DEBUG);
}
if (_endOfLastWrite + numWriteSamples > _buffer + _bufferLength) {
int numSamplesToEnd = (_buffer + _bufferLength) - _endOfLastWrite;
memset(_endOfLastWrite, 0, numSamplesToEnd * SampleSize);
memset(_buffer, 0, (numWriteSamples - numSamplesToEnd) * SampleSize);
} else {
memset(_endOfLastWrite, 0, numWriteSamples * SampleSize);
}
_endOfLastWrite = shiftedPositionAccomodatingWrap(_endOfLastWrite, numWriteSamples);
return numWriteSamples;
}
template <class T>
float AudioRingBufferTemplate<T>::getFrameLoudness(const Sample* frameStart) const {
// FIXME: This is a bad measure of loudness - normal estimation uses sqrt(sum(x*x))
float loudness = 0.0f;
const int16_t* sampleAt = frameStart;
const int16_t* bufferLastAt = _buffer + _bufferLength - 1;
const Sample* sampleAt = frameStart;
const Sample* bufferLastAt = _buffer + _bufferLength - 1;
for (int i = 0; i < _numFrameSamples; ++i) {
loudness += (float) std::abs(*sampleAt);
@ -203,14 +258,16 @@ float AudioRingBuffer::getFrameLoudness(const int16_t* frameStart) const {
return loudness;
}
float AudioRingBuffer::getFrameLoudness(ConstIterator frameStart) const {
template <class T>
float AudioRingBufferTemplate<T>::getFrameLoudness(ConstIterator frameStart) const {
if (frameStart.isNull()) {
return 0.0f;
}
return getFrameLoudness(&(*frameStart));
}
int AudioRingBuffer::writeSamples(ConstIterator source, int maxSamples) {
template <class T>
int AudioRingBufferTemplate<T>::writeSamples(ConstIterator source, int maxSamples) {
int samplesToCopy = std::min(maxSamples, _sampleCapacity);
int samplesRoomFor = _sampleCapacity - samplesAvailable();
if (samplesToCopy > samplesRoomFor) {
@ -221,7 +278,7 @@ int AudioRingBuffer::writeSamples(ConstIterator source, int maxSamples) {
qCDebug(audio) << qPrintable(RING_BUFFER_OVERFLOW_DEBUG);
}
int16_t* bufferLast = _buffer + _bufferLength - 1;
Sample* bufferLast = _buffer + _bufferLength - 1;
for (int i = 0; i < samplesToCopy; i++) {
*_endOfLastWrite = *source;
_endOfLastWrite = (_endOfLastWrite == bufferLast) ? _buffer : _endOfLastWrite + 1;
@ -231,7 +288,8 @@ int AudioRingBuffer::writeSamples(ConstIterator source, int maxSamples) {
return samplesToCopy;
}
int AudioRingBuffer::writeSamplesWithFade(ConstIterator source, int maxSamples, float fade) {
template <class T>
int AudioRingBufferTemplate<T>::writeSamplesWithFade(ConstIterator source, int maxSamples, float fade) {
int samplesToCopy = std::min(maxSamples, _sampleCapacity);
int samplesRoomFor = _sampleCapacity - samplesAvailable();
if (samplesToCopy > samplesRoomFor) {
@ -242,12 +300,16 @@ int AudioRingBuffer::writeSamplesWithFade(ConstIterator source, int maxSamples,
qCDebug(audio) << qPrintable(RING_BUFFER_OVERFLOW_DEBUG);
}
int16_t* bufferLast = _buffer + _bufferLength - 1;
Sample* bufferLast = _buffer + _bufferLength - 1;
for (int i = 0; i < samplesToCopy; i++) {
*_endOfLastWrite = (int16_t)((float)(*source) * fade);
*_endOfLastWrite = (Sample)((float)(*source) * fade);
_endOfLastWrite = (_endOfLastWrite == bufferLast) ? _buffer : _endOfLastWrite + 1;
++source;
}
return samplesToCopy;
}
// explicit instantiations for scratch/mix buffers
template class AudioRingBufferTemplate<int16_t>;
template class AudioRingBufferTemplate<float>;

View file

@ -21,15 +21,19 @@
const int DEFAULT_RING_BUFFER_FRAME_CAPACITY = 10;
class AudioRingBuffer {
template <class T>
class AudioRingBufferTemplate {
using Sample = T;
static const int SampleSize = sizeof(Sample);
public:
AudioRingBuffer(int numFrameSamples, int numFramesCapacity = DEFAULT_RING_BUFFER_FRAME_CAPACITY);
~AudioRingBuffer();
AudioRingBufferTemplate(int numFrameSamples, int numFramesCapacity = DEFAULT_RING_BUFFER_FRAME_CAPACITY);
~AudioRingBufferTemplate();
// disallow copying
AudioRingBuffer(const AudioRingBuffer&) = delete;
AudioRingBuffer(AudioRingBuffer&&) = delete;
AudioRingBuffer& operator=(const AudioRingBuffer&) = delete;
AudioRingBufferTemplate(const AudioRingBufferTemplate&) = delete;
AudioRingBufferTemplate(AudioRingBufferTemplate&&) = delete;
AudioRingBufferTemplate& operator=(const AudioRingBufferTemplate&) = delete;
/// Invalidate any data in the buffer
void clear();
@ -41,13 +45,27 @@ public:
// FIXME: discards any data in the buffer
void resizeForFrameSize(int numFrameSamples);
// Reading and writing to the buffer uses minimal shared data, such that
// in cases that avoid overwriting the buffer, a single producer/consumer
// may use this as a lock-free pipe (see audio-client/src/AudioClient.cpp).
// IMPORTANT: Avoid changes to the implementation that touch shared data unless you can
// maintain this behavior.
/// Read up to maxSamples into destination (will only read up to samplesAvailable())
/// Returns number of read samples
int readSamples(int16_t* destination, int maxSamples);
int readSamples(Sample* destination, int maxSamples);
/// Append up to maxSamples into destination (will only read up to samplesAvailable())
/// If append == false, behaves as readSamples
/// Returns number of appended samples
int appendSamples(Sample* destination, int maxSamples, bool append = true);
/// Skip up to maxSamples (will only skip up to samplesAvailable())
void skipSamples(int maxSamples) { shiftReadPosition(std::min(maxSamples, samplesAvailable())); }
/// Write up to maxSamples from source (will only write up to sample capacity)
/// Returns number of written samples
int writeSamples(const int16_t* source, int maxSamples);
int writeSamples(const Sample* source, int maxSamples);
/// Write up to maxSamples silent samples (will only write until other data exists in the buffer)
/// This method will not overwrite existing data in the buffer, instead dropping silent samples that would overflow
@ -58,13 +76,17 @@ public:
/// Returns number of read bytes
int readData(char* destination, int maxSize);
/// Append up to maxSize into destination
/// Returns number of read bytes
int appendData(char* destination, int maxSize);
/// Write up to maxSize from source
/// Returns number of written bytes
int writeData(const char* source, int maxSize);
/// Returns a reference to the index-th sample offset from the current read sample
int16_t& operator[](const int index) { return *shiftedPositionAccomodatingWrap(_nextOutput, index); }
const int16_t& operator[] (const int index) const { return *shiftedPositionAccomodatingWrap(_nextOutput, index); }
Sample& operator[](const int index) { return *shiftedPositionAccomodatingWrap(_nextOutput, index); }
const Sample& operator[] (const int index) const { return *shiftedPositionAccomodatingWrap(_nextOutput, index); }
/// Essentially discards the next numSamples from the ring buffer
/// NOTE: This is not checked - it is possible to shift past written data
@ -84,41 +106,104 @@ public:
class ConstIterator {
public:
ConstIterator();
ConstIterator(int16_t* bufferFirst, int capacity, int16_t* at);
ConstIterator() :
_bufferLength(0),
_bufferFirst(NULL),
_bufferLast(NULL),
_at(NULL) {}
ConstIterator(Sample* bufferFirst, int capacity, Sample* at) :
_bufferLength(capacity),
_bufferFirst(bufferFirst),
_bufferLast(bufferFirst + capacity - 1),
_at(at) {}
ConstIterator(const ConstIterator& rhs) = default;
bool isNull() const { return _at == NULL; }
bool operator==(const ConstIterator& rhs) { return _at == rhs._at; }
bool operator!=(const ConstIterator& rhs) { return _at != rhs._at; }
const int16_t& operator*() { return *_at; }
const Sample& operator*() { return *_at; }
ConstIterator& operator=(const ConstIterator& rhs);
ConstIterator& operator++();
ConstIterator operator++(int);
ConstIterator& operator--();
ConstIterator operator--(int);
const int16_t& operator[] (int i);
ConstIterator operator+(int i);
ConstIterator operator-(int i);
ConstIterator& operator=(const ConstIterator& rhs) {
_bufferLength = rhs._bufferLength;
_bufferFirst = rhs._bufferFirst;
_bufferLast = rhs._bufferLast;
_at = rhs._at;
return *this;
}
ConstIterator& operator++() {
_at = (_at == _bufferLast) ? _bufferFirst : _at + 1;
return *this;
}
ConstIterator operator++(int) {
ConstIterator tmp(*this);
++(*this);
return tmp;
}
ConstIterator& operator--() {
_at = (_at == _bufferFirst) ? _bufferLast : _at - 1;
return *this;
}
ConstIterator operator--(int) {
ConstIterator tmp(*this);
--(*this);
return tmp;
}
const Sample& operator[] (int i) {
return *atShiftedBy(i);
}
ConstIterator operator+(int i) {
return ConstIterator(_bufferFirst, _bufferLength, atShiftedBy(i));
}
ConstIterator operator-(int i) {
return ConstIterator(_bufferFirst, _bufferLength, atShiftedBy(-i));
}
void readSamples(Sample* dest, int numSamples) {
auto samplesToEnd = _bufferLast - _at + 1;
if (samplesToEnd >= numSamples) {
memcpy(dest, _at, numSamples * SampleSize);
_at += numSamples;
} else {
auto samplesFromStart = numSamples - samplesToEnd;
memcpy(dest, _at, samplesToEnd * SampleSize);
memcpy(dest + samplesToEnd, _bufferFirst, samplesFromStart * SampleSize);
_at = _bufferFirst + samplesFromStart;
}
}
void readSamplesWithFade(Sample* dest, int numSamples, float fade) {
Sample* at = _at;
for (int i = 0; i < numSamples; i++) {
*dest = (float)*at * fade;
++dest;
at = (at == _bufferLast) ? _bufferFirst : at + 1;
}
}
void readSamples(int16_t* dest, int numSamples);
void readSamplesWithFade(int16_t* dest, int numSamples, float fade);
void readSamplesWithUpmix(int16_t* dest, int numSamples, int numExtraChannels);
void readSamplesWithDownmix(int16_t* dest, int numSamples);
private:
int16_t* atShiftedBy(int i);
Sample* atShiftedBy(int i) {
i = (_at - _bufferFirst + i) % _bufferLength;
if (i < 0) {
i += _bufferLength;
}
return _bufferFirst + i;
}
int _bufferLength;
int16_t* _bufferFirst;
int16_t* _bufferLast;
int16_t* _at;
Sample* _bufferFirst;
Sample* _bufferLast;
Sample* _at;
};
ConstIterator nextOutput() const;
ConstIterator lastFrameWritten() const;
ConstIterator nextOutput() const {
return ConstIterator(_buffer, _bufferLength, _nextOutput);
}
ConstIterator lastFrameWritten() const {
return ConstIterator(_buffer, _bufferLength, _endOfLastWrite) - _numFrameSamples;
}
int writeSamples(ConstIterator source, int maxSamples);
int writeSamplesWithFade(ConstIterator source, int maxSamples, float fade);
@ -126,8 +211,8 @@ public:
float getFrameLoudness(ConstIterator frameStart) const;
protected:
int16_t* shiftedPositionAccomodatingWrap(int16_t* position, int numSamplesShift) const;
float getFrameLoudness(const int16_t* frameStart) const;
Sample* shiftedPositionAccomodatingWrap(Sample* position, int numSamplesShift) const;
float getFrameLoudness(const Sample* frameStart) const;
int _numFrameSamples;
int _frameCapacity;
@ -135,138 +220,13 @@ protected:
int _bufferLength; // actual _buffer length (_sampleCapacity + 1)
int _overflowCount{ 0 }; // times the ring buffer has overwritten data
int16_t* _nextOutput{ nullptr };
int16_t* _endOfLastWrite{ nullptr };
int16_t* _buffer{ nullptr };
Sample* _nextOutput{ nullptr };
Sample* _endOfLastWrite{ nullptr };
Sample* _buffer{ nullptr };
};
// inline the iterator:
inline AudioRingBuffer::ConstIterator::ConstIterator() :
_bufferLength(0),
_bufferFirst(NULL),
_bufferLast(NULL),
_at(NULL) {}
inline AudioRingBuffer::ConstIterator::ConstIterator(int16_t* bufferFirst, int capacity, int16_t* at) :
_bufferLength(capacity),
_bufferFirst(bufferFirst),
_bufferLast(bufferFirst + capacity - 1),
_at(at) {}
inline AudioRingBuffer::ConstIterator& AudioRingBuffer::ConstIterator::operator=(const ConstIterator& rhs) {
_bufferLength = rhs._bufferLength;
_bufferFirst = rhs._bufferFirst;
_bufferLast = rhs._bufferLast;
_at = rhs._at;
return *this;
}
inline AudioRingBuffer::ConstIterator& AudioRingBuffer::ConstIterator::operator++() {
_at = (_at == _bufferLast) ? _bufferFirst : _at + 1;
return *this;
}
inline AudioRingBuffer::ConstIterator AudioRingBuffer::ConstIterator::operator++(int) {
ConstIterator tmp(*this);
++(*this);
return tmp;
}
inline AudioRingBuffer::ConstIterator& AudioRingBuffer::ConstIterator::operator--() {
_at = (_at == _bufferFirst) ? _bufferLast : _at - 1;
return *this;
}
inline AudioRingBuffer::ConstIterator AudioRingBuffer::ConstIterator::operator--(int) {
ConstIterator tmp(*this);
--(*this);
return tmp;
}
inline const int16_t& AudioRingBuffer::ConstIterator::operator[] (int i) {
return *atShiftedBy(i);
}
inline AudioRingBuffer::ConstIterator AudioRingBuffer::ConstIterator::operator+(int i) {
return ConstIterator(_bufferFirst, _bufferLength, atShiftedBy(i));
}
inline AudioRingBuffer::ConstIterator AudioRingBuffer::ConstIterator::operator-(int i) {
return ConstIterator(_bufferFirst, _bufferLength, atShiftedBy(-i));
}
inline int16_t* AudioRingBuffer::ConstIterator::atShiftedBy(int i) {
i = (_at - _bufferFirst + i) % _bufferLength;
if (i < 0) {
i += _bufferLength;
}
return _bufferFirst + i;
}
inline void AudioRingBuffer::ConstIterator::readSamples(int16_t* dest, int numSamples) {
auto samplesToEnd = _bufferLast - _at + 1;
if (samplesToEnd >= numSamples) {
memcpy(dest, _at, numSamples * sizeof(int16_t));
_at += numSamples;
} else {
auto samplesFromStart = numSamples - samplesToEnd;
memcpy(dest, _at, samplesToEnd * sizeof(int16_t));
memcpy(dest + samplesToEnd, _bufferFirst, samplesFromStart * sizeof(int16_t));
_at = _bufferFirst + samplesFromStart;
}
}
inline void AudioRingBuffer::ConstIterator::readSamplesWithFade(int16_t* dest, int numSamples, float fade) {
int16_t* at = _at;
for (int i = 0; i < numSamples; i++) {
*dest = (float)*at * fade;
++dest;
at = (at == _bufferLast) ? _bufferFirst : at + 1;
}
}
inline void AudioRingBuffer::ConstIterator::readSamplesWithUpmix(int16_t* dest, int numSamples, int numExtraChannels) {
int16_t* at = _at;
for (int i = 0; i < numSamples/2; i++) {
// read 2 samples
int16_t left = *at;
at = (at == _bufferLast) ? _bufferFirst : at + 1;
int16_t right = *at;
at = (at == _bufferLast) ? _bufferFirst : at + 1;
// write 2 + N samples
*dest++ = left;
*dest++ = right;
for (int n = 0; n < numExtraChannels; n++) {
*dest++ = 0;
}
}
}
inline void AudioRingBuffer::ConstIterator::readSamplesWithDownmix(int16_t* dest, int numSamples) {
int16_t* at = _at;
for (int i = 0; i < numSamples/2; i++) {
// read 2 samples
int16_t left = *at;
at = (at == _bufferLast) ? _bufferFirst : at + 1;
int16_t right = *at;
at = (at == _bufferLast) ? _bufferFirst : at + 1;
// write 1 sample
*dest++ = (int16_t)((left + right) / 2);
}
}
inline AudioRingBuffer::ConstIterator AudioRingBuffer::nextOutput() const {
return ConstIterator(_buffer, _bufferLength, _nextOutput);
}
inline AudioRingBuffer::ConstIterator AudioRingBuffer::lastFrameWritten() const {
return ConstIterator(_buffer, _bufferLength, _endOfLastWrite) - _numFrameSamples;
}
// expose explicit instantiations for scratch/mix buffers
using AudioRingBuffer = AudioRingBufferTemplate<int16_t>;
using AudioMixRingBuffer = AudioRingBufferTemplate<float>;
#endif // hifi_AudioRingBuffer_h

View file

@ -1304,6 +1304,9 @@ int AvatarData::getFauxJointIndex(const QString& name) const {
if (name == "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND") {
return CAMERA_RELATIVE_CONTROLLER_RIGHTHAND_INDEX;
}
if (name == "_CAMERA_MATRIX") {
return CAMERA_MATRIX_INDEX;
}
return -1;
}
@ -1739,7 +1742,7 @@ static const QString JSON_AVATAR_HEAD_MODEL = QStringLiteral("headModel");
static const QString JSON_AVATAR_BODY_MODEL = QStringLiteral("bodyModel");
static const QString JSON_AVATAR_DISPLAY_NAME = QStringLiteral("displayName");
// It isn't meaningful to persist sessionDisplayName.
static const QString JSON_AVATAR_ATTACHEMENTS = QStringLiteral("attachments");
static const QString JSON_AVATAR_ATTACHMENTS = QStringLiteral("attachments");
static const QString JSON_AVATAR_ENTITIES = QStringLiteral("attachedEntities");
static const QString JSON_AVATAR_SCALE = QStringLiteral("scale");
static const QString JSON_AVATAR_VERSION = QStringLiteral("version");
@ -1782,7 +1785,7 @@ QJsonObject AvatarData::toJson() const {
for (auto attachment : getAttachmentData()) {
attachmentsJson.push_back(attachment.toJson());
}
root[JSON_AVATAR_ATTACHEMENTS] = attachmentsJson;
root[JSON_AVATAR_ATTACHMENTS] = attachmentsJson;
}
_avatarEntitiesLock.withReadLock([&] {
@ -1895,8 +1898,8 @@ void AvatarData::fromJson(const QJsonObject& json) {
setTargetScale((float)json[JSON_AVATAR_SCALE].toDouble());
}
if (json.contains(JSON_AVATAR_ATTACHEMENTS) && json[JSON_AVATAR_ATTACHEMENTS].isArray()) {
QJsonArray attachmentsJson = json[JSON_AVATAR_ATTACHEMENTS].toArray();
if (json.contains(JSON_AVATAR_ATTACHMENTS) && json[JSON_AVATAR_ATTACHMENTS].isArray()) {
QJsonArray attachmentsJson = json[JSON_AVATAR_ATTACHMENTS].toArray();
QVector<AttachmentData> attachments;
for (auto attachmentJson : attachmentsJson) {
AttachmentData attachment;
@ -1907,7 +1910,7 @@ void AvatarData::fromJson(const QJsonObject& json) {
}
// if (json.contains(JSON_AVATAR_ENTITIES) && json[JSON_AVATAR_ENTITIES].isArray()) {
// QJsonArray attachmentsJson = json[JSON_AVATAR_ATTACHEMENTS].toArray();
// QJsonArray attachmentsJson = json[JSON_AVATAR_ATTACHMENTS].toArray();
// for (auto attachmentJson : attachmentsJson) {
// // TODO -- something
// }

View file

@ -765,5 +765,6 @@ const int CONTROLLER_RIGHTHAND_INDEX = 65533; // -3
const int CONTROLLER_LEFTHAND_INDEX = 65532; // -4
const int CAMERA_RELATIVE_CONTROLLER_RIGHTHAND_INDEX = 65531; // -5
const int CAMERA_RELATIVE_CONTROLLER_LEFTHAND_INDEX = 65530; // -6
const int CAMERA_MATRIX_INDEX = 65529; // -7
#endif // hifi_AvatarData_h

View file

@ -956,68 +956,29 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, const
}
}
bool EntityTreeRenderer::isCollisionOwner(const QUuid& myNodeID, EntityTreePointer entityTree,
const EntityItemID& id, const Collision& collision) {
EntityItemPointer entity = entityTree->findEntityByEntityItemID(id);
if (!entity) {
return false;
}
QUuid simulatorID = entity->getSimulatorID();
if (simulatorID.isNull()) {
// Can be null if it has never moved since being created or coming out of persistence.
// However, for there to be a collission, one of the two objects must be moving.
const EntityItemID& otherID = (id == collision.idA) ? collision.idB : collision.idA;
EntityItemPointer otherEntity = entityTree->findEntityByEntityItemID(otherID);
if (!otherEntity) {
return false;
}
simulatorID = otherEntity->getSimulatorID();
}
if (simulatorID.isNull() || (simulatorID != myNodeID)) {
return false;
}
return true;
}
void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityTreePointer entityTree,
const EntityItemID& id, const Collision& collision) {
if (!isCollisionOwner(myNodeID, entityTree, id, collision)) {
return;
}
SharedSoundPointer collisionSound;
float mass = 1.0; // value doesn't get used, but set it so compiler is quiet
AACube minAACube;
bool success = false;
_tree->withReadLock([&] {
EntityItemPointer entity = entityTree->findEntityByEntityItemID(id);
if (entity) {
collisionSound = entity->getCollisionSound();
mass = entity->computeMass();
minAACube = entity->getMinimumAACube(success);
}
});
if (!success) {
return;
}
void EntityTreeRenderer::playEntityCollisionSound(EntityItemPointer entity, const Collision& collision) {
assert((bool)entity);
SharedSoundPointer collisionSound = entity->getCollisionSound();
if (!collisionSound) {
return;
}
bool success = false;
AACube minAACube = entity->getMinimumAACube(success);
if (!success) {
return;
}
float mass = entity->computeMass();
const float COLLISION_PENETRATION_TO_VELOCITY = 50; // as a subsitute for RELATIVE entity->getVelocity()
const float COLLISION_PENETRATION_TO_VELOCITY = 50.0f; // as a subsitute for RELATIVE entity->getVelocity()
// The collision.penetration is a pretty good indicator of changed velocity AFTER the initial contact,
// but that first contact depends on exactly where we hit in the physics step.
// We can get a more consistent initial-contact energy reading by using the changed velocity.
// Note that velocityChange is not a good indicator for continuing collisions, because it does not distinguish
// between bounce and sliding along a surface.
const float linearVelocity = (collision.type == CONTACT_EVENT_TYPE_START) ?
glm::length(collision.velocityChange) :
glm::length(collision.penetration) * COLLISION_PENETRATION_TO_VELOCITY;
const float energy = mass * linearVelocity * linearVelocity / 2.0f;
const glm::vec3 position = collision.contactPoint;
const float speedSquared = (collision.type == CONTACT_EVENT_TYPE_START) ?
glm::length2(collision.velocityChange) :
glm::length2(collision.penetration) * COLLISION_PENETRATION_TO_VELOCITY;
const float energy = mass * speedSquared / 2.0f;
const float COLLISION_ENERGY_AT_FULL_VOLUME = (collision.type == CONTACT_EVENT_TYPE_START) ? 150.0f : 5.0f;
const float COLLISION_MINIMUM_VOLUME = 0.005f;
const float energyFactorOfFull = fmin(1.0f, energy / COLLISION_ENERGY_AT_FULL_VOLUME);
@ -1031,7 +992,7 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT
// Shift the pitch down by ln(1 + (size / COLLISION_SIZE_FOR_STANDARD_PITCH)) / ln(2)
const float COLLISION_SIZE_FOR_STANDARD_PITCH = 0.2f;
const float stretchFactor = log(1.0f + (minAACube.getLargestDimension() / COLLISION_SIZE_FOR_STANDARD_PITCH)) / log(2);
AudioInjector::playSound(collisionSound, volume, stretchFactor, position);
AudioInjector::playSound(collisionSound, volume, stretchFactor, collision.contactPoint);
}
void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB,
@ -1041,30 +1002,28 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons
if (!_tree || _shuttingDown) {
return;
}
// Don't respond to small continuous contacts.
const float COLLISION_MINUMUM_PENETRATION = 0.002f;
if ((collision.type == CONTACT_EVENT_TYPE_CONTINUE) && (glm::length(collision.penetration) < COLLISION_MINUMUM_PENETRATION)) {
return;
}
// See if we should play sounds
EntityTreePointer entityTree = std::static_pointer_cast<EntityTree>(_tree);
const QUuid& myNodeID = DependencyManager::get<NodeList>()->getSessionUUID();
playEntityCollisionSound(myNodeID, entityTree, idA, collision);
playEntityCollisionSound(myNodeID, entityTree, idB, collision);
// And now the entity scripts
if (isCollisionOwner(myNodeID, entityTree, idA, collision)) {
// trigger scripted collision sounds and events for locally owned objects
EntityItemPointer entityA = entityTree->findEntityByEntityItemID(idA);
if ((bool)entityA && myNodeID == entityA->getSimulatorID()) {
playEntityCollisionSound(entityA, collision);
emit collisionWithEntity(idA, idB, collision);
if (_entitiesScriptEngine) {
_entitiesScriptEngine->callEntityScriptMethod(idA, "collisionWithEntity", idB, collision);
}
}
if (isCollisionOwner(myNodeID, entityTree, idB, collision)) {
emit collisionWithEntity(idB, idA, collision);
EntityItemPointer entityB = entityTree->findEntityByEntityItemID(idB);
if ((bool)entityB && myNodeID == entityB->getSimulatorID()) {
playEntityCollisionSound(entityB, collision);
// since we're swapping A and B we need to send the inverted collision
Collision invertedCollision(collision);
invertedCollision.invert();
emit collisionWithEntity(idB, idA, invertedCollision);
if (_entitiesScriptEngine) {
_entitiesScriptEngine->callEntityScriptMethod(idB, "collisionWithEntity", idA, collision);
_entitiesScriptEngine->callEntityScriptMethod(idB, "collisionWithEntity", idA, invertedCollision);
}
}
}

View file

@ -170,11 +170,7 @@ private:
bool _wantScripts;
QSharedPointer<ScriptEngine> _entitiesScriptEngine;
bool isCollisionOwner(const QUuid& myNodeID, EntityTreePointer entityTree,
const EntityItemID& id, const Collision& collision);
void playEntityCollisionSound(const QUuid& myNodeID, EntityTreePointer entityTree,
const EntityItemID& id, const Collision& collision);
static void playEntityCollisionSound(EntityItemPointer entity, const Collision& collision);
bool _lastPointerEventValid;
PointerEvent _lastPointerEvent;

View file

@ -320,12 +320,9 @@ bool RenderableModelEntityItem::getAnimationFrame() {
glm::mat4 finalMat = (translationMat * fbxJoints[index].preTransform *
rotationMat * fbxJoints[index].postTransform);
_localJointTranslations[j] = extractTranslation(finalMat);
_localJointTranslationsSet[j] = true;
_localJointTranslationsDirty[j] = true;
_localJointRotations[j] = glmExtractRotation(finalMat);
_localJointRotationsSet[j] = true;
_localJointRotationsDirty[j] = true;
}
}
@ -508,6 +505,7 @@ ModelPointer RenderableModelEntityItem::getModel(QSharedPointer<EntityTreeRender
// If we don't have a model, allocate one *immediately*
if (!_model) {
_model = _myRenderer->allocateModel(getModelURL(), renderer->getEntityLoadingPriority(*this));
_model->setSpatiallyNestableOverride(shared_from_this());
_needsInitialSimulation = true;
// If we need to change URLs, update it *after rendering* (to avoid access violations)
} else if (QUrl(getModelURL()) != _model->getURL()) {
@ -1192,8 +1190,7 @@ void RenderableModelEntityItem::locationChanged(bool tellPhysics) {
PerformanceTimer pertTimer("locationChanged");
EntityItem::locationChanged(tellPhysics);
if (_model && _model->isActive()) {
_model->setRotation(getRotation());
_model->setTranslation(getPosition());
_model->updateRenderItems();
void* key = (void*)this;
std::weak_ptr<RenderableModelEntityItem> weakSelf =

View file

@ -18,12 +18,12 @@
#include <GeometryCache.h>
#include <PerfStat.h>
#include <gl/OffscreenQmlSurface.h>
#include <AbstractViewStateInterface.h>
#include <GLMHelpers.h>
#include <PathUtils.h>
#include <TextureCache.h>
#include <gpu/Context.h>
#include <TabletScriptingInterface.h>
#include "EntityTreeRenderer.h"
#include "EntitiesRendererLogging.h"
@ -46,6 +46,7 @@ EntityItemPointer RenderableWebEntityItem::factory(const EntityItemID& entityID,
RenderableWebEntityItem::RenderableWebEntityItem(const EntityItemID& entityItemID) :
WebEntityItem(entityItemID) {
qCDebug(entities) << "Created web entity " << getID();
_touchDevice.setCapabilities(QTouchDevice::Position);
@ -57,7 +58,9 @@ RenderableWebEntityItem::RenderableWebEntityItem(const EntityItemID& entityItemI
RenderableWebEntityItem::~RenderableWebEntityItem() {
destroyWebSurface();
qCDebug(entities) << "Destroyed web entity " << getID();
auto geometryCache = DependencyManager::get<GeometryCache>();
if (geometryCache) {
geometryCache->releaseID(_geometryId);
@ -78,18 +81,19 @@ bool RenderableWebEntityItem::buildWebSurface(QSharedPointer<EntityTreeRenderer>
QString createGlobalEventBridgeStr = QTextStream(&createGlobalEventBridgeFile).readAll();
// concatenate these js files
javaScriptToInject = webChannelStr + createGlobalEventBridgeStr;
_javaScriptToInject = webChannelStr + createGlobalEventBridgeStr;
} else {
qCWarning(entitiesrenderer) << "unable to find qwebchannel.js or createGlobalEventBridge.js";
}
// Save the original GL context, because creating a QML surface will create a new context
QOpenGLContext * currentContext = QOpenGLContext::currentContext();
QOpenGLContext* currentContext = QOpenGLContext::currentContext();
if (!currentContext) {
return false;
}
++_currentWebCount;
qCDebug(entities) << "Building web surface: " << getID() << ", #" << _currentWebCount << ", url = " << _sourceUrl;
QSurface * currentSurface = currentContext->surface();
@ -113,13 +117,12 @@ bool RenderableWebEntityItem::buildWebSurface(QSharedPointer<EntityTreeRenderer>
// The lifetime of the QML surface MUST be managed by the main thread
// Additionally, we MUST use local variables copied by value, rather than
// member variables, since they would implicitly refer to a this that
// member variables, since they would implicitly refer to a this that
// is no longer valid
_webSurface->create(currentContext);
_webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/controls/"));
_webSurface->load("WebView.qml", [&](QQmlContext* context, QObject* obj) {
context->setContextProperty("eventBridgeJavaScriptToInject", QVariant(javaScriptToInject));
});
loadSourceURL();
_webSurface->resume();
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
_webSurface->getRootContext()->setContextProperty("desktop", QVariant());
@ -156,7 +159,8 @@ bool RenderableWebEntityItem::buildWebSurface(QSharedPointer<EntityTreeRenderer>
point.setPos(windowPoint);
QList<QTouchEvent::TouchPoint> touchPoints;
touchPoints.push_back(point);
QTouchEvent* touchEvent = new QTouchEvent(QEvent::TouchEnd, nullptr, Qt::NoModifier, Qt::TouchPointReleased, touchPoints);
QTouchEvent* touchEvent = new QTouchEvent(QEvent::TouchEnd, nullptr,
Qt::NoModifier, Qt::TouchPointReleased, touchPoints);
touchEvent->setWindow(_webSurface->getWindow());
touchEvent->setDevice(&_touchDevice);
touchEvent->setTarget(_webSurface->getRootItem());
@ -237,14 +241,42 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio);
const bool IS_AA = true;
if (fadeRatio < OPAQUE_ALPHA_THRESHOLD) {
DependencyManager::get<GeometryCache>()->bindTransparentWebBrowserProgram(batch);
DependencyManager::get<GeometryCache>()->bindTransparentWebBrowserProgram(batch, IS_AA);
} else {
DependencyManager::get<GeometryCache>()->bindOpaqueWebBrowserProgram(batch);
DependencyManager::get<GeometryCache>()->bindOpaqueWebBrowserProgram(batch, IS_AA);
}
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, fadeRatio), _geometryId);
}
void RenderableWebEntityItem::loadSourceURL() {
QUrl sourceUrl(_sourceUrl);
if (sourceUrl.scheme() == "http" || sourceUrl.scheme() == "https" ||
_sourceUrl.toLower().endsWith(".htm") || _sourceUrl.toLower().endsWith(".html")) {
_contentType = htmlContent;
_webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "qml/controls/"));
_webSurface->load("WebView.qml", [&](QQmlContext* context, QObject* obj) {
context->setContextProperty("eventBridgeJavaScriptToInject", QVariant(_javaScriptToInject));
});
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
_webSurface->getRootContext()->setContextProperty("desktop", QVariant());
} else {
_contentType = qmlContent;
_webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath()));
_webSurface->load(_sourceUrl, [&](QQmlContext* context, QObject* obj) {});
if (_webSurface->getRootItem() && _webSurface->getRootItem()->objectName() == "tabletRoot") {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system",
_webSurface->getRootItem(), _webSurface.data());
}
}
_webSurface->getRootContext()->setContextProperty("globalPosition", vec3toVariant(getPosition()));
}
void RenderableWebEntityItem::setSourceUrl(const QString& value) {
auto valueBeforeSuperclassSet = _sourceUrl;
@ -254,7 +286,10 @@ void RenderableWebEntityItem::setSourceUrl(const QString& value) {
qCDebug(entities) << "Changing web entity source URL to " << _sourceUrl;
AbstractViewStateInterface::instance()->postLambdaEvent([this] {
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
loadSourceURL();
if (_contentType == htmlContent) {
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
}
});
}
}
@ -337,6 +372,14 @@ void RenderableWebEntityItem::destroyWebSurface() {
--_currentWebCount;
QQuickItem* rootItem = _webSurface->getRootItem();
if (rootItem && rootItem->objectName() == "tabletRoot") {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", nullptr, nullptr);
}
// Fix for crash in QtWebEngineCore when rapidly switching domains
// Call stop on the QWebEngineView before destroying OffscreenQMLSurface.
if (rootItem) {
QObject* obj = rootItem->findChild<QObject*>("webEngineView");
if (obj) {
@ -363,6 +406,12 @@ void RenderableWebEntityItem::destroyWebSurface() {
}
void RenderableWebEntityItem::update(const quint64& now) {
if (_webSurface) {
// update globalPosition
_webSurface->getRootContext()->setContextProperty("globalPosition", vec3toVariant(getPosition()));
}
auto interval = now - _lastRenderTime;
if (interval > MAX_NO_RENDER_INTERVAL) {
destroyWebSurface();
@ -374,6 +423,13 @@ bool RenderableWebEntityItem::isTransparent() {
return fadeRatio < OPAQUE_ALPHA_THRESHOLD;
}
QObject* RenderableWebEntityItem::getRootItem() {
if (_webSurface) {
return dynamic_cast<QObject*>(_webSurface->getRootItem());
}
return nullptr;
}
void RenderableWebEntityItem::emitScriptEvent(const QVariant& message) {
if (_webSurface) {
_webSurface->emitScriptEvent(message);

View file

@ -16,6 +16,7 @@
#include <gl/OffscreenQmlSurface.h>
#include <WebEntityItem.h>
#include <gl/OffscreenQmlSurface.h>
#include "RenderableEntityItem.h"
@ -33,6 +34,7 @@ public:
~RenderableWebEntityItem();
virtual void render(RenderArgs* args) override;
void loadSourceURL();
virtual void setSourceUrl(const QString& value) override;
virtual bool wantsHandControllerPointerEvents() const override { return true; }
@ -51,6 +53,10 @@ public:
virtual bool isTransparent() override;
public:
virtual QObject* getRootItem() override;
private:
bool buildWebSurface(QSharedPointer<EntityTreeRenderer> renderer);
void destroyWebSurface();
@ -67,6 +73,14 @@ private:
QMetaObject::Connection _mouseReleaseConnection;
QMetaObject::Connection _mouseMoveConnection;
QMetaObject::Connection _hoverLeaveConnection;
QString _javaScriptToInject;
enum contentType {
htmlContent,
qmlContent
};
contentType _contentType;
int _geometryId { 0 };
};

View file

@ -1327,7 +1327,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(href, setHref);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(description, setDescription);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(actionData, setActionData);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentID, setParentID);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentID, updateParentID);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentJointIndex, setParentJointIndex);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(queryAACube, setQueryAACube);
@ -1584,6 +1584,14 @@ void EntityItem::updatePosition(const glm::vec3& value) {
}
}
void EntityItem::updateParentID(const QUuid& value) {
if (_parentID != value) {
setParentID(value);
_dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; // children are forced to be kinematic
_dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
}
}
void EntityItem::updatePositionFromNetwork(const glm::vec3& value) {
if (shouldSuppressLocationEdits()) {
return;
@ -1792,7 +1800,6 @@ void EntityItem::updateCreated(uint64_t value) {
}
void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask) const {
// TODO: detect attachment status and adopt group of wearer
if (_collisionless) {
group = BULLET_COLLISION_GROUP_COLLISIONLESS;
mask = 0;
@ -1806,10 +1813,33 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask
}
uint8_t userMask = getCollisionMask();
if (userMask & USER_COLLISION_GROUP_MY_AVATAR) {
// if this entity is a descendant of MyAvatar, don't collide with MyAvatar. This avoids the
// "bootstrapping" problem where you can shoot yourself across the room by grabbing something
// and holding it against your own avatar.
QUuid ancestorID = findAncestorOfType(NestableType::Avatar);
if (!ancestorID.isNull() && ancestorID == Physics::getSessionUUID()) {
userMask &= ~USER_COLLISION_GROUP_MY_AVATAR;
}
}
if (userMask & USER_COLLISION_GROUP_MY_AVATAR) {
// also, don't bootstrap our own avatar with a hold action
QList<EntityActionPointer> holdActions = getActionsOfType(ACTION_TYPE_HOLD);
QList<EntityActionPointer>::const_iterator i = holdActions.begin();
while (i != holdActions.end()) {
EntityActionPointer action = *i;
if (action->isMine()) {
userMask &= ~USER_COLLISION_GROUP_MY_AVATAR;
break;
}
i++;
}
}
if ((bool)(userMask & USER_COLLISION_GROUP_MY_AVATAR) !=
(bool)(userMask & USER_COLLISION_GROUP_OTHER_AVATAR)) {
// asymmetric avatar collision mask bits
if (!getSimulatorID().isNull() && (!getSimulatorID().isNull()) && getSimulatorID() != Physics::getSessionUUID()) {
if (!getSimulatorID().isNull() && getSimulatorID() != Physics::getSessionUUID()) {
// someone else owns the simulation, so we toggle the avatar bits (swap interpretation)
userMask ^= USER_COLLISION_MASK_AVATARS | ~userMask;
}
@ -1908,6 +1938,7 @@ bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityAct
if (success) {
_allActionsDataCache = newDataCache;
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
_dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
} else {
qCDebug(entities) << "EntityItem::addActionInternal -- serializeActions failed";
}
@ -1930,6 +1961,7 @@ bool EntityItem::updateAction(EntitySimulationPointer simulation, const QUuid& a
action->setIsMine(true);
serializeActions(success, _allActionsDataCache);
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
_dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
} else {
qCDebug(entities) << "EntityItem::updateAction failed";
}
@ -1967,6 +1999,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
bool success = true;
serializeActions(success, _allActionsDataCache);
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
_dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
setActionDataNeedsTransmit(true);
return success;
}
@ -1987,6 +2020,7 @@ bool EntityItem::clearActions(EntitySimulationPointer simulation) {
_actionsToRemove.clear();
_allActionsDataCache.clear();
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
_dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
});
return true;
}
@ -2193,7 +2227,7 @@ bool EntityItem::shouldSuppressLocationEdits() const {
return false;
}
QList<EntityActionPointer> EntityItem::getActionsOfType(EntityActionType typeToGet) {
QList<EntityActionPointer> EntityItem::getActionsOfType(EntityActionType typeToGet) const {
QList<EntityActionPointer> result;
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();

View file

@ -343,6 +343,7 @@ public:
// updateFoo() methods to be used when changes need to be accumulated in the _dirtyFlags
virtual void updateRegistrationPoint(const glm::vec3& value);
void updatePosition(const glm::vec3& value);
void updateParentID(const QUuid& value);
void updatePositionFromNetwork(const glm::vec3& value);
void updateDimensions(const glm::vec3& value);
void updateRotation(const glm::quat& rotation);
@ -419,7 +420,7 @@ public:
const QUuid& getSourceUUID() const { return _sourceUUID; }
bool matchesSourceUUID(const QUuid& sourceUUID) const { return _sourceUUID == sourceUUID; }
QList<EntityActionPointer> getActionsOfType(EntityActionType typeToGet);
QList<EntityActionPointer> getActionsOfType(EntityActionType typeToGet) const;
// these are in the frame of this object
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override { return glm::quat(); }

View file

@ -25,6 +25,7 @@
#include "QVariantGLM.h"
#include "SimulationOwner.h"
#include "ZoneEntityItem.h"
#include "WebEntityItem.h"
#include <EntityScriptClient.h>
@ -1366,14 +1367,16 @@ bool EntityScriptingInterface::isChildOfParent(QUuid childID, QUuid parentID) {
_entityTree->withReadLock([&] {
EntityItemPointer parent = _entityTree->findEntityByEntityItemID(parentID);
parent->forEachDescendant([&](SpatiallyNestablePointer descendant) {
if(descendant->getID() == childID) {
isChild = true;
return;
}
});
if (parent) {
parent->forEachDescendant([&](SpatiallyNestablePointer descendant) {
if (descendant->getID() == childID) {
isChild = true;
return;
}
});
}
});
return isChild;
}
@ -1397,7 +1400,8 @@ QVector<QUuid> EntityScriptingInterface::getChildrenIDsOfJoint(const QUuid& pare
return;
}
parent->forEachChild([&](SpatiallyNestablePointer child) {
if (child->getParentJointIndex() == jointIndex) {
if (child->getParentJointIndex() == jointIndex &&
child->getNestableType() != NestableType::Overlay) {
result.push_back(child->getID());
}
});
@ -1492,3 +1496,12 @@ void EntityScriptingInterface::setCostMultiplier(float value) {
costMultiplier = value;
}
QObject* EntityScriptingInterface::getWebViewRoot(const QUuid& entityID) {
if (auto entity = checkForTreeEntityAndTypeMatch(entityID, EntityTypes::Web)) {
auto webEntity = std::dynamic_pointer_cast<WebEntityItem>(entity);
QObject* root = webEntity->getRootItem();
return root;
} else {
return nullptr;
}
}

View file

@ -285,6 +285,8 @@ public slots:
Q_INVOKABLE void emitScriptEvent(const EntityItemID& entityID, const QVariant& message);
Q_INVOKABLE QObject* getWebViewRoot(const QUuid& entityID);
signals:
void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);

View file

@ -927,13 +927,21 @@ void EntityTree::initEntityEditFilterEngine(QScriptEngine* engine, std::function
_entityEditFilterHadUncaughtExceptions = entityEditFilterHadUncaughtExceptions;
auto global = _entityEditFilterEngine->globalObject();
_entityEditFilterFunction = global.property("filter");
_hasEntityEditFilter = _entityEditFilterFunction.isFunction();
if (!_entityEditFilterFunction.isFunction()) {
qCDebug(entities) << "Filter function specified but not found. Will reject all edits.";
_entityEditFilterEngine = nullptr; // So that we don't try to call it. See filterProperties.
}
_hasEntityEditFilter = true;
}
bool EntityTree::filterProperties(EntityItemProperties& propertiesIn, EntityItemProperties& propertiesOut, bool& wasChanged) {
if (!_hasEntityEditFilter || !_entityEditFilterEngine) {
if (!_entityEditFilterEngine) {
propertiesOut = propertiesIn;
wasChanged = false; // not changed
if (_hasEntityEditFilter) {
qCDebug(entities) << "Rejecting properties because filter has not been set.";
return false;
}
return true; // allowed
}
auto oldProperties = propertiesIn.getDesiredProperties();
@ -1552,6 +1560,8 @@ bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extra
return args->map->value(oldID);
}
EntityItemID newID = QUuid::createUuid();
args->map->insert(oldID, newID);
EntityItemProperties properties = item->getProperties();
EntityItemID oldParentID = properties.getParentID();
if (oldParentID.isInvalidID()) { // no parent
@ -1567,6 +1577,43 @@ bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extra
}
}
if (!properties.getXNNeighborID().isInvalidID()) {
auto neighborEntity = args->ourTree->findEntityByEntityItemID(properties.getXNNeighborID());
if (neighborEntity) {
properties.setXNNeighborID(getMapped(neighborEntity));
}
}
if (!properties.getXPNeighborID().isInvalidID()) {
auto neighborEntity = args->ourTree->findEntityByEntityItemID(properties.getXPNeighborID());
if (neighborEntity) {
properties.setXPNeighborID(getMapped(neighborEntity));
}
}
if (!properties.getYNNeighborID().isInvalidID()) {
auto neighborEntity = args->ourTree->findEntityByEntityItemID(properties.getYNNeighborID());
if (neighborEntity) {
properties.setYNNeighborID(getMapped(neighborEntity));
}
}
if (!properties.getYPNeighborID().isInvalidID()) {
auto neighborEntity = args->ourTree->findEntityByEntityItemID(properties.getYPNeighborID());
if (neighborEntity) {
properties.setYPNeighborID(getMapped(neighborEntity));
}
}
if (!properties.getZNNeighborID().isInvalidID()) {
auto neighborEntity = args->ourTree->findEntityByEntityItemID(properties.getZNNeighborID());
if (neighborEntity) {
properties.setZNNeighborID(getMapped(neighborEntity));
}
}
if (!properties.getZPNeighborID().isInvalidID()) {
auto neighborEntity = args->ourTree->findEntityByEntityItemID(properties.getZPNeighborID());
if (neighborEntity) {
properties.setZPNeighborID(getMapped(neighborEntity));
}
}
// set creation time to "now" for imported entities
properties.setCreated(usecTimestampNow());
@ -1584,7 +1631,6 @@ bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extra
args->otherTree->addEntity(newID, properties);
});
}
args->map->insert(oldID, newID);
return newID;
};

View file

@ -267,6 +267,7 @@ public:
void notifyNewCollisionSoundURL(const QString& newCollisionSoundURL, const EntityItemID& entityID);
void initEntityEditFilterEngine(QScriptEngine* engine, std::function<bool()> entityEditFilterHadUncaughtExceptions);
void setHasEntityFilter(bool hasFilter) { _hasEntityEditFilter = hasFilter; }
static const float DEFAULT_MAX_TMP_ENTITY_LIFETIME;

View file

@ -59,6 +59,8 @@ public:
void setDPI(uint16_t value);
uint16_t getDPI() const;
virtual QObject* getRootItem() { return nullptr; }
protected:
QString _sourceUrl;
uint16_t _dpi;

View file

@ -749,8 +749,12 @@ void OffscreenQmlSurface::resume() {
_paused = false;
_render = true;
getRootItem()->setProperty("eventBridge", QVariant::fromValue(this));
getRootContext()->setContextProperty("webEntity", this);
if (getRootItem()) {
getRootItem()->setProperty("eventBridge", QVariant::fromValue(this));
}
if (getRootContext()) {
getRootContext()->setContextProperty("webEntity", this);
}
}
bool OffscreenQmlSurface::isPaused() const {

View file

@ -92,6 +92,7 @@ void SparseInfo::maybeMakeSparse() {
if (!texture._gpuObject.isAutogenerateMips()) {
return;
}
return;
const uvec3 dimensions = texture._gpuObject.getDimensions();
auto allowedPageDimensions = getPageDimensionsForFormat(texture._target, texture._internalFormat);

View file

@ -1084,17 +1084,17 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
params.stopReason = EncodeBitstreamParams::WAS_IN_VIEW;
return bytesAtThisLevel;
}
}
// If we're not in delta sending mode, and we weren't asked to do a force send, and the voxel hasn't changed,
// then we can also bail early and save bits
if (!params.forceSendScene && !params.deltaView &&
!element->hasChangedSince(params.lastQuerySent - CHANGE_FUDGE)) {
if (params.stats) {
params.stats->skippedNoChange(element);
}
params.stopReason = EncodeBitstreamParams::NO_CHANGE;
return bytesAtThisLevel;
// If we're not in delta sending mode, and we weren't asked to do a force send, and the octree element hasn't changed,
// then we can also bail early and save bits
if (!params.forceSendScene && !params.deltaView &&
!element->hasChangedSince(params.lastQuerySent - CHANGE_FUDGE)) {
if (params.stats) {
params.stats->skippedNoChange(element);
}
params.stopReason = EncodeBitstreamParams::NO_CHANGE;
return bytesAtThisLevel;
}
bool keepDiggingDeeper = true; // Assuming we're in view we have a great work ethic, we're always ready for more!

View file

@ -13,15 +13,25 @@
void ContactInfo::update(uint32_t currentStep, const btManifoldPoint& p) {
_lastStep = currentStep;
++_numSteps;
positionWorldOnB = p.m_positionWorldOnB;
normalWorldOnB = p.m_normalWorldOnB;
distance = p.m_distance1;
}
}
const uint32_t STEPS_BETWEEN_CONTINUE_EVENTS = 9;
ContactEventType ContactInfo::computeType(uint32_t thisStep) {
if (_lastStep != thisStep) {
return CONTACT_EVENT_TYPE_END;
if (_continueExpiry == 0) {
_continueExpiry = thisStep + STEPS_BETWEEN_CONTINUE_EVENTS;
return CONTACT_EVENT_TYPE_START;
}
return (_numSteps == 1) ? CONTACT_EVENT_TYPE_START : CONTACT_EVENT_TYPE_CONTINUE;
return (_lastStep == thisStep) ? CONTACT_EVENT_TYPE_CONTINUE : CONTACT_EVENT_TYPE_END;
}
bool ContactInfo::readyForContinue(uint32_t thisStep) {
if (thisStep > _continueExpiry) {
_continueExpiry = thisStep + STEPS_BETWEEN_CONTINUE_EVENTS;
return true;
}
return false;
}

View file

@ -19,20 +19,22 @@
class ContactInfo {
public:
public:
void update(uint32_t currentStep, const btManifoldPoint& p);
ContactEventType computeType(uint32_t thisStep);
const btVector3& getPositionWorldOnB() const { return positionWorldOnB; }
btVector3 getPositionWorldOnA() const { return positionWorldOnB + normalWorldOnB * distance; }
bool readyForContinue(uint32_t thisStep);
btVector3 positionWorldOnB;
btVector3 normalWorldOnB;
btScalar distance;
private:
uint32_t _lastStep = 0;
uint32_t _numSteps = 0;
};
uint32_t _lastStep { 0 };
uint32_t _continueExpiry { 0 };
};
#endif // hifi_ContactEvent_h

View file

@ -762,6 +762,11 @@ void EntityMotionState::computeCollisionGroupAndMask(int16_t& group, int16_t& ma
_entity->computeCollisionGroupAndFinalMask(group, mask);
}
bool EntityMotionState::shouldBeLocallyOwned() const {
return (_outgoingPriority > VOLUNTEER_SIMULATION_PRIORITY && _outgoingPriority > _entity->getSimulationPriority()) ||
_entity->getSimulatorID() == Physics::getSessionUUID();
}
void EntityMotionState::upgradeOutgoingPriority(uint8_t priority) {
_outgoingPriority = glm::max<uint8_t>(_outgoingPriority, priority);
}

View file

@ -78,6 +78,8 @@ public:
virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const override;
bool shouldBeLocallyOwned() const override;
friend class PhysicalEntitySimulation;
protected:

View file

@ -133,6 +133,7 @@ QVariantMap ObjectAction::getArguments() {
arguments["::no-motion-state"] = true;
}
}
arguments["isMine"] = isMine();
});
return arguments;
}

View file

@ -146,6 +146,8 @@ public:
void dirtyInternalKinematicChanges() { _hasInternalKinematicChanges = true; }
void clearInternalKinematicChanges() { _hasInternalKinematicChanges = false; }
virtual bool shouldBeLocallyOwned() const { return false; }
friend class PhysicsEngine;
protected:

View file

@ -270,7 +270,7 @@ void PhysicsEngine::stepSimulation() {
}
auto onSubStep = [this]() {
updateContactMap();
this->updateContactMap();
};
int numSubsteps = _dynamicsWorld->stepSimulationWithSubstepCallback(timeStep, PHYSICS_ENGINE_MAX_NUM_SUBSTEPS,
@ -393,7 +393,6 @@ void PhysicsEngine::updateContactMap() {
}
const CollisionEvents& PhysicsEngine::getCollisionEvents() {
const uint32_t CONTINUE_EVENT_FILTER_FREQUENCY = 10;
_collisionEvents.clear();
// scan known contacts and trigger events
@ -402,28 +401,42 @@ const CollisionEvents& PhysicsEngine::getCollisionEvents() {
while (contactItr != _contactMap.end()) {
ContactInfo& contact = contactItr->second;
ContactEventType type = contact.computeType(_numContactFrames);
if(type != CONTACT_EVENT_TYPE_CONTINUE || _numSubsteps % CONTINUE_EVENT_FILTER_FREQUENCY == 0) {
const btScalar SIGNIFICANT_DEPTH = -0.002f; // penetrations have negative distance
if (type != CONTACT_EVENT_TYPE_CONTINUE ||
(contact.distance < SIGNIFICANT_DEPTH &&
contact.readyForContinue(_numContactFrames))) {
ObjectMotionState* motionStateA = static_cast<ObjectMotionState*>(contactItr->first._a);
ObjectMotionState* motionStateB = static_cast<ObjectMotionState*>(contactItr->first._b);
glm::vec3 velocityChange = (motionStateA ? motionStateA->getObjectLinearVelocityChange() : glm::vec3(0.0f)) +
(motionStateB ? motionStateB->getObjectLinearVelocityChange() : glm::vec3(0.0f));
if (motionStateA) {
// NOTE: the MyAvatar RigidBody is the only object in the simulation that does NOT have a MotionState
// which means should we ever want to report ALL collision events against the avatar we can
// modify the logic below.
//
// We only create events when at least one of the objects is (or should be) owned in the local simulation.
if (motionStateA && (motionStateA->shouldBeLocallyOwned())) {
QUuid idA = motionStateA->getObjectID();
QUuid idB;
if (motionStateB) {
idB = motionStateB->getObjectID();
}
glm::vec3 position = bulletToGLM(contact.getPositionWorldOnB()) + _originOffset;
glm::vec3 velocityChange = motionStateA->getObjectLinearVelocityChange() +
(motionStateB ? motionStateB->getObjectLinearVelocityChange() : glm::vec3(0.0f));
glm::vec3 penetration = bulletToGLM(contact.distance * contact.normalWorldOnB);
_collisionEvents.push_back(Collision(type, idA, idB, position, penetration, velocityChange));
} else if (motionStateB) {
} else if (motionStateB && (motionStateB->shouldBeLocallyOwned())) {
QUuid idB = motionStateB->getObjectID();
QUuid idA;
if (motionStateA) {
idA = motionStateA->getObjectID();
}
glm::vec3 position = bulletToGLM(contact.getPositionWorldOnA()) + _originOffset;
glm::vec3 velocityChange = motionStateB->getObjectLinearVelocityChange() +
(motionStateA ? motionStateA->getObjectLinearVelocityChange() : glm::vec3(0.0f));
// NOTE: we're flipping the order of A and B (so that the first objectID is never NULL)
// hence we must negate the penetration.
// hence we negate the penetration (because penetration always points from B to A).
glm::vec3 penetration = - bulletToGLM(contact.distance * contact.normalWorldOnB);
_collisionEvents.push_back(Collision(type, idB, QUuid(), position, penetration, velocityChange));
_collisionEvents.push_back(Collision(type, idB, idA, position, penetration, velocityChange));
}
}

View file

@ -36,7 +36,9 @@
#include "simple_textured_frag.h"
#include "simple_textured_unlit_frag.h"
#include "simple_opaque_web_browser_frag.h"
#include "simple_opaque_web_browser_overlay_frag.h"
#include "simple_transparent_web_browser_frag.h"
#include "simple_transparent_web_browser_overlay_frag.h"
#include "glowLine_vert.h"
#include "glowLine_frag.h"
@ -1760,66 +1762,61 @@ inline bool operator==(const SimpleProgramKey& a, const SimpleProgramKey& b) {
return a.getRaw() == b.getRaw();
}
void GeometryCache::bindOpaqueWebBrowserProgram(gpu::Batch& batch) {
batch.setPipeline(getOpaqueWebBrowserProgram());
static void buildWebShader(const std::string& vertShaderText, const std::string& fragShaderText, bool blendEnable,
gpu::ShaderPointer& shaderPointerOut, gpu::PipelinePointer& pipelinePointerOut) {
auto VS = gpu::Shader::createVertex(vertShaderText);
auto PS = gpu::Shader::createPixel(fragShaderText);
shaderPointerOut = gpu::Shader::createProgram(VS, PS);
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), render::ShapePipeline::Slot::MAP::NORMAL_FITTING));
gpu::Shader::makeProgram(*shaderPointerOut, slotBindings);
auto state = std::make_shared<gpu::State>();
state->setCullMode(gpu::State::CULL_NONE);
state->setDepthTest(true, true, gpu::LESS_EQUAL);
state->setBlendFunction(blendEnable,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
pipelinePointerOut = gpu::Pipeline::create(shaderPointerOut, state);
}
void GeometryCache::bindOpaqueWebBrowserProgram(gpu::Batch& batch, bool isAA) {
batch.setPipeline(getOpaqueWebBrowserProgram(isAA));
// Set a default normal map
batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING,
DependencyManager::get<TextureCache>()->getNormalFittingTexture());
}
gpu::PipelinePointer GeometryCache::getOpaqueWebBrowserProgram() {
gpu::PipelinePointer GeometryCache::getOpaqueWebBrowserProgram(bool isAA) {
static std::once_flag once;
std::call_once(once, [&]() {
auto VS = gpu::Shader::createVertex(std::string(simple_vert));
auto PS = gpu::Shader::createPixel(std::string(simple_opaque_web_browser_frag));
_simpleOpaqueWebBrowserShader = gpu::Shader::createProgram(VS, PS);
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), render::ShapePipeline::Slot::MAP::NORMAL_FITTING));
gpu::Shader::makeProgram(*_simpleOpaqueWebBrowserShader, slotBindings);
auto state = std::make_shared<gpu::State>();
state->setCullMode(gpu::State::CULL_NONE);
state->setDepthTest(true, true, gpu::LESS_EQUAL);
state->setBlendFunction(false,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
_simpleOpaqueWebBrowserPipeline = gpu::Pipeline::create(_simpleOpaqueWebBrowserShader, state);
const bool BLEND_ENABLE = false;
buildWebShader(simple_vert, simple_opaque_web_browser_frag, BLEND_ENABLE, _simpleOpaqueWebBrowserShader, _simpleOpaqueWebBrowserPipeline);
buildWebShader(simple_vert, simple_opaque_web_browser_overlay_frag, BLEND_ENABLE, _simpleOpaqueWebBrowserOverlayShader, _simpleOpaqueWebBrowserOverlayPipeline);
});
return _simpleOpaqueWebBrowserPipeline;
return isAA ? _simpleOpaqueWebBrowserPipeline : _simpleOpaqueWebBrowserOverlayPipeline;
}
void GeometryCache::bindTransparentWebBrowserProgram(gpu::Batch& batch) {
batch.setPipeline(getTransparentWebBrowserProgram());
void GeometryCache::bindTransparentWebBrowserProgram(gpu::Batch& batch, bool isAA) {
batch.setPipeline(getTransparentWebBrowserProgram(isAA));
// Set a default normal map
batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING,
DependencyManager::get<TextureCache>()->getNormalFittingTexture());
}
gpu::PipelinePointer GeometryCache::getTransparentWebBrowserProgram() {
gpu::PipelinePointer GeometryCache::getTransparentWebBrowserProgram(bool isAA) {
static std::once_flag once;
std::call_once(once, [&]() {
auto VS = gpu::Shader::createVertex(std::string(simple_vert));
auto PS = gpu::Shader::createPixel(std::string(simple_transparent_web_browser_frag));
_simpleTransparentWebBrowserShader = gpu::Shader::createProgram(VS, PS);
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), render::ShapePipeline::Slot::MAP::NORMAL_FITTING));
gpu::Shader::makeProgram(*_simpleTransparentWebBrowserShader, slotBindings);
auto state = std::make_shared<gpu::State>();
state->setCullMode(gpu::State::CULL_NONE);
state->setDepthTest(true, true, gpu::LESS_EQUAL);
state->setBlendFunction(true,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
_simpleTransparentWebBrowserPipeline = gpu::Pipeline::create(_simpleTransparentWebBrowserShader, state);
const bool BLEND_ENABLE = true;
buildWebShader(simple_vert, simple_transparent_web_browser_frag, BLEND_ENABLE, _simpleTransparentWebBrowserShader, _simpleTransparentWebBrowserPipeline);
buildWebShader(simple_vert, simple_transparent_web_browser_overlay_frag, BLEND_ENABLE, _simpleTransparentWebBrowserOverlayShader, _simpleTransparentWebBrowserOverlayPipeline);
});
return _simpleTransparentWebBrowserPipeline;
return isAA ? _simpleTransparentWebBrowserPipeline : _simpleTransparentWebBrowserOverlayPipeline;
}
void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool transparent, bool culled, bool unlit, bool depthBiased) {

View file

@ -158,11 +158,11 @@ public:
gpu::PipelinePointer getSimplePipeline(bool textured = false, bool transparent = false, bool culled = true,
bool unlit = false, bool depthBias = false);
void bindOpaqueWebBrowserProgram(gpu::Batch& batch);
gpu::PipelinePointer getOpaqueWebBrowserProgram();
void bindOpaqueWebBrowserProgram(gpu::Batch& batch, bool isAA);
gpu::PipelinePointer getOpaqueWebBrowserProgram(bool isAA);
void bindTransparentWebBrowserProgram(gpu::Batch& batch);
gpu::PipelinePointer getTransparentWebBrowserProgram();
void bindTransparentWebBrowserProgram(gpu::Batch& batch, bool isAA);
gpu::PipelinePointer getTransparentWebBrowserProgram(bool isAA);
render::ShapePipelinePointer getOpaqueShapePipeline() { return GeometryCache::_simpleOpaquePipeline; }
render::ShapePipelinePointer getTransparentShapePipeline() { return GeometryCache::_simpleTransparentPipeline; }
@ -420,15 +420,21 @@ private:
gpu::ShaderPointer _unlitShader;
static render::ShapePipelinePointer _simpleOpaquePipeline;
static render::ShapePipelinePointer _simpleTransparentPipeline;
static render::ShapePipelinePointer _simpleOpaqueOverlayPipeline;
static render::ShapePipelinePointer _simpleTransparentOverlayPipeline;
static render::ShapePipelinePointer _simpleWirePipeline;
gpu::PipelinePointer _glowLinePipeline;
QHash<SimpleProgramKey, gpu::PipelinePointer> _simplePrograms;
gpu::ShaderPointer _simpleOpaqueWebBrowserShader;
gpu::PipelinePointer _simpleOpaqueWebBrowserPipeline;
gpu::ShaderPointer _simpleTransparentWebBrowserShader;
gpu::PipelinePointer _simpleTransparentWebBrowserPipeline;
gpu::ShaderPointer _simpleOpaqueWebBrowserOverlayShader;
gpu::PipelinePointer _simpleOpaqueWebBrowserOverlayPipeline;
gpu::ShaderPointer _simpleTransparentWebBrowserOverlayShader;
gpu::PipelinePointer _simpleTransparentWebBrowserOverlayPipeline;
};
#endif // hifi_GeometryCache_h

View file

@ -92,6 +92,15 @@ bool LightingModel::isAlbedoEnabled() const {
return (bool)_parametersBuffer.get<Parameters>().enableAlbedo;
}
void LightingModel::setMaterialTexturing(bool enable) {
if (enable != isMaterialTexturingEnabled()) {
_parametersBuffer.edit<Parameters>().enableMaterialTexturing = (float)enable;
}
}
bool LightingModel::isMaterialTexturingEnabled() const {
return (bool)_parametersBuffer.get<Parameters>().enableMaterialTexturing;
}
void LightingModel::setAmbientLight(bool enable) {
if (enable != isAmbientLightEnabled()) {
_parametersBuffer.edit<Parameters>().enableAmbientLight = (float)enable;
@ -150,6 +159,8 @@ void MakeLightingModel::configure(const Config& config) {
_lightingModel->setSpecular(config.enableSpecular);
_lightingModel->setAlbedo(config.enableAlbedo);
_lightingModel->setMaterialTexturing(config.enableMaterialTexturing);
_lightingModel->setAmbientLight(config.enableAmbientLight);
_lightingModel->setDirectionalLight(config.enableDirectionalLight);
_lightingModel->setPointLight(config.enablePointLight);
@ -160,5 +171,8 @@ void MakeLightingModel::configure(const Config& config) {
void MakeLightingModel::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, LightingModelPointer& lightingModel) {
lightingModel = _lightingModel;
lightingModel = _lightingModel;
// make sure the enableTexturing flag of the render ARgs is in sync
renderContext->args->_enableTexturing = _lightingModel->isMaterialTexturingEnabled();
}

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