Merge branch 'master' of github.com:highfidelity/hifi into application-dpi-scale

Conflicts:
	interface/src/ui/LoginDialog.cpp
This commit is contained in:
Ryan Huffman 2015-02-20 18:09:55 -08:00
commit e6d9a1b251
98 changed files with 1707 additions and 1335 deletions

View file

@ -2,7 +2,6 @@
* [cmake](http://www.cmake.org/cmake/resources/software.html) ~> 2.8.12.2
* [Qt](http://qt-project.org/downloads) ~> 5.3.2
* [glm](http://glm.g-truc.net/0.9.5/index.html) ~> 0.9.5.4
* [OpenSSL](https://www.openssl.org/related/binaries.html) ~> 1.0.1g
* IMPORTANT: OpenSSL 1.0.1g is critical to avoid a security vulnerability.
* [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/) ~> 4.3
@ -10,6 +9,13 @@
* [Bullet Physics Engine](https://code.google.com/p/bullet/downloads/list) ~> 2.82
* [Gverb](https://github.com/highfidelity/gverb/archive/master.zip) (direct download to latest version)
#### CMake External Project Dependencies
The following dependencies will be downloaded, built, linked and included automatically by CMake where we require them. The CMakeLists files that handle grabbing each of the following external dependencies can be found in the [cmake/externals folder](cmake/externals). The resulting downloads, source files and binaries will be placed in the `build` directory in each of the subfolders for each external project. These are not placed in your normal build tree when doing an out of source build so that they do not need to be re-downloaded and re-compiled every time the CMake build folder is cleared.
* [glm](http://glm.g-truc.net/0.9.5/index.html) ~> 0.9.5.4
* [gverb](https://github.com/highfidelity/gverb)
### OS Specific Build Guides
* [BUILD_OSX.md](BUILD_OSX.md) - additional instructions for OS X.
* [BUILD_LINUX.md](BUILD_LINUX.md) - additional instructions for Linux.

View file

@ -127,11 +127,6 @@ To put the Gear VR Service into developer mode you need an application with an O
Once the application is on your device, go to `Settings->Application Manager->Gear VR Service->Manage Storage`. Tap on `VR Service Version` six times. It will scan your device to verify that you have an osig file in an application on your device, and then it will let you enable Developer mode.
####GLM
GLM is a header only library and technically the same GLM used for desktop builds of hifi could be used for the Android build. However, to avoid conflicts with system installations of Android dependencies, CMake will only look for Android headers and libraries in `ANDROID_LIB_DIR` or in your android-ndk install.
Download the [glm headers](http://sourceforge.net/projects/ogl-math/files/) from their sourceforge page. The version you download should match the requirement shown in the [general build guide](BUILD.md). Extract the archive into your `ANDROID_LIB_DIR` and rename the extracted folder to `glm`.
###CMake

View file

@ -4,7 +4,7 @@ Please read the [general build guide](BUILD.md) for information on dependencies
[Homebrew](http://brew.sh/) is an excellent package manager for OS X. It makes install of all hifi dependencies very simple.
brew tap highfidelity/homebrew-formulas
brew install cmake glm openssl tbb libsoxr
brew install cmake openssl tbb libsoxr
brew install highfidelity/formulas/qt5
brew link qt5 --force

View file

@ -56,9 +56,6 @@ The recommended route for CMake to find the external dependencies is to place al
-> bin
-> include
-> lib
-> glm
-> glm
-> glm.hpp
-> openssl
-> bin
-> include
@ -129,26 +126,6 @@ Download the binary package: `glew-1.10.0-win32.zip`. Extract to %HIFI_LIB_DIR%\
Add to the PATH: `%HIFI_LIB_DIR%\glew\bin\Release\Win32`
###GLM
This package contains only headers, so there's nothing to add to the PATH.
Be careful with glm. For the folder other libraries would normally call 'include', the folder containing the headers, glm opts to use 'glm'. You will have a glm folder nested inside the top-level glm folder.
###Gverb
1. Go to https://github.com/highfidelity/gverb
Or download the sources directly via this link:
https://github.com/highfidelity/gverb/archive/master.zip
2. Extract the archive
3. Place the directories “include” and “src” in interface/external/gverb
(Normally next to this readme)
4. Clear your build directory, run cmake, build and you should be all set.
###Bullet
Bullet 2.82 source can be [downloaded here](https://code.google.com/p/bullet/downloads/detail?name=bullet-2.82-r2704.zip). Bullet does not come with prebuilt libraries, you need to make those yourself.
@ -186,6 +163,8 @@ We recommend you install it to %HIFI_LIB_DIR%\soxr. This will help our FindSoxr
Extract the soxr archive wherever you like. Then, inside the extracted folder, create a directory called `build`. From that build directory, the following commands will build and then install soxr to `%HIFI_LIB_DIR%`.
(Make sure to run the following inside Visual Studio)
```
cmake .. -G "NMake Makefiles" -DCMAKE_INSTALL_PREFIX=%HIFI_LIB_DIR%/soxr
nmake

View file

@ -40,7 +40,7 @@ if (WIN32)
elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic")
#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unknown-pragmas")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fno-strict-aliasing")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fno-strict-aliasing -ggdb")
endif(WIN32)
if (NOT MSVC12)
@ -104,6 +104,7 @@ set(HIFI_LIBRARY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/")
set(MACRO_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macros")
set(EXTERNAL_PROJECT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cmake/externals")
file(GLOB HIFI_CUSTOM_MACROS "cmake/macros/*.cmake")
foreach(CUSTOM_MACRO ${HIFI_CUSTOM_MACROS})

View file

@ -2,7 +2,9 @@ set(TARGET_NAME assignment-client)
setup_hifi_project(Core Gui Network Script Widgets)
include_glm()
add_dependency_external_project(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PRIVATE ${GLM_INCLUDE_DIRS})
# link in the shared libraries
link_hifi_libraries(

View file

@ -47,6 +47,7 @@ Agent::Agent(const QByteArray& packet) :
_scriptEngine.getEntityScriptingInterface()->setPacketSender(&_entityEditSender);
DependencyManager::set<ResouceCacheSharedItems>();
DependencyManager::set<SoundCache>();
}
void Agent::readPendingDatagrams() {

View file

@ -136,7 +136,6 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) :
// Create Singleton objects on main thread
NetworkAccessManager::getInstance();
auto soundCache = DependencyManager::get<SoundCache>();
}
void AssignmentClient::sendAssignmentRequest() {

View file

@ -479,10 +479,20 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) {
for (int i = 0; i < _zoneReverbSettings.size(); ++i) {
AudioMixerClientData* data = static_cast<AudioMixerClientData*>(node->getLinkedData());
glm::vec3 streamPosition = data->getAvatarAudioStream()->getPosition();
if (_audioZones[_zoneReverbSettings[i].zone].contains(streamPosition)) {
AABox box = _audioZones[_zoneReverbSettings[i].zone];
if (box.contains(streamPosition)) {
hasReverb = true;
reverbTime = _zoneReverbSettings[i].reverbTime;
wetLevel = _zoneReverbSettings[i].wetLevel;
// Modulate wet level with distance to wall
float MIN_ATTENUATION_DISTANCE = 2.0f;
float MAX_ATTENUATION = -12; // dB
glm::vec3 distanceToWalls = (box.getDimensions() / 2.0f) - glm::abs(streamPosition - box.calcCenter());
float distanceToClosestWall = glm::min(distanceToWalls.x, distanceToWalls.z);
if (distanceToClosestWall < MIN_ATTENUATION_DISTANCE) {
wetLevel += MAX_ATTENUATION * (1.0f - distanceToClosestWall / MIN_ATTENUATION_DISTANCE);
}
break;
}
}

View file

@ -147,3 +147,14 @@ void EntityServer::pruneDeletedEntities() {
}
}
void EntityServer::readAdditionalConfiguration(const QJsonObject& settingsSectionObject) {
bool wantEditLogging = false;
readOptionBool(QString("wantEditLogging"), settingsSectionObject, wantEditLogging);
qDebug("wantEditLogging=%s", debug::valueOf(wantEditLogging));
EntityTree* tree = static_cast<EntityTree*>(_tree);
tree->setWantEditLogging(wantEditLogging);
}

View file

@ -41,6 +41,7 @@ public:
virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent);
virtual void entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode);
virtual void readAdditionalConfiguration(const QJsonObject& settingsSectionObject);
public slots:
void pruneDeletedEntities();

15
cmake/externals/glm/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,15 @@
set(EXTERNAL_NAME glm)
include(ExternalProject)
ExternalProject_Add(
${EXTERNAL_NAME}
PREFIX ${EXTERNAL_NAME}
URL http://pkgs.fedoraproject.org/repo/pkgs/glm/glm-0.9.5.4.zip/fab76fc982b256b46208e5c750ed456a/glm-0.9.5.4.zip
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
LOG_DOWNLOAD ON
)
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE TYPE STRING)

25
cmake/externals/gverb/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,25 @@
set(EXTERNAL_NAME gverb)
if (ANDROID)
set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19")
endif ()
include(ExternalProject)
ExternalProject_Add(
${EXTERNAL_NAME}
PREFIX ${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/gverb-master.zip
CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
LOG_DOWNLOAD ON
)
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE TYPE STRING)
if (WIN32)
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${INSTALL_DIR}/lib/gverb.lib CACHE TYPE STRING)
else ()
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${INSTALL_DIR}/lib/libgverb.a CACHE TYPE STRING)
endif ()

View file

@ -0,0 +1,25 @@
#
# SetupExternalProject.cmake
# cmake/macros
#
# Copyright 2015 High Fidelity, Inc.
# Created by Stephen Birarda on February 13, 2014
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(ADD_DEPENDENCY_EXTERNAL_PROJECT _PROJ_NAME)
string(TOUPPER ${_PROJ_NAME} _PROJ_NAME_UPPER)
if (NOT DEFINED GET_${_PROJ_NAME_UPPER} OR GET_${_PROJ_NAME_UPPER})
if (NOT TARGET ${_PROJ_NAME})
add_subdirectory(${EXTERNAL_PROJECT_DIR}/${_PROJ_NAME} ${CMAKE_BINARY_DIR}/externals/${_PROJ_NAME})
endif ()
add_dependencies(${TARGET_NAME} ${_PROJ_NAME})
endif ()
endmacro()

View file

@ -10,22 +10,21 @@
macro(HIFI_LIBRARY_SEARCH_HINTS LIBRARY_FOLDER)
string(TOUPPER ${LIBRARY_FOLDER} LIBRARY_PREFIX)
set(${LIBRARY_PREFIX}_SEARCH_DIRS "")
if (${LIBRARY_PREFIX}_ROOT_DIR)
set(${LIBRARY_PREFIX}_SEARCH_DIRS "${${LIBRARY_PREFIX}_ROOT_DIR}")
list(APPEND ${LIBRARY_PREFIX}_SEARCH_DIRS "${${LIBRARY_PREFIX}_ROOT_DIR}")
endif ()
if (ANDROID)
set(${LIBRARY_PREFIX}_SEARCH_DIRS "${${LIBRARY_PREFIX}_SEARCH_DIRS}" "/${LIBRARY_FOLDER}")
list(APPEND ${LIBRARY_PREFIX}_SEARCH_DIRS "/${LIBRARY_FOLDER}")
endif ()
if (DEFINED ENV{${LIBRARY_PREFIX}_ROOT_DIR})
set(${LIBRARY_PREFIX}_SEARCH_DIRS "${${LIBRARY_PREFIX}_SEARCH_DIRS}" "$ENV{${LIBRARY_PREFIX}_ROOT_DIR}")
list(APPEND ${LIBRARY_PREFIX}_SEARCH_DIRS "$ENV{${LIBRARY_PREFIX}_ROOT_DIR}")
endif ()
if (DEFINED ENV{HIFI_LIB_DIR})
set(${LIBRARY_PREFIX}_SEARCH_DIRS "${${LIBRARY_PREFIX}_SEARCH_DIRS}" "$ENV{HIFI_LIB_DIR}/${LIBRARY_FOLDER}")
list(APPEND ${LIBRARY_PREFIX}_SEARCH_DIRS "$ENV{HIFI_LIB_DIR}/${LIBRARY_FOLDER}")
endif ()
endmacro(HIFI_LIBRARY_SEARCH_HINTS _library_folder)

View file

@ -1,13 +0,0 @@
#
# IncludeGLM.cmake
#
# Copyright 2013 High Fidelity, Inc.
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(INCLUDE_GLM)
find_package(GLM REQUIRED)
include_directories(SYSTEM "${GLM_INCLUDE_DIRS}")
endmacro(INCLUDE_GLM)

View file

@ -18,9 +18,7 @@ include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("glm")
# locate header
find_path(GLM_INCLUDE_DIR "glm/glm.hpp" HINTS ${GLM_SEARCH_DIRS})
set(GLM_INCLUDE_DIRS "${GLM_INCLUDE_DIR}")
find_path(GLM_INCLUDE_DIRS "glm/glm.hpp" HINTS ${GLM_SEARCH_DIRS})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GLM DEFAULT_MSG GLM_INCLUDE_DIRS)

View file

@ -15,25 +15,11 @@
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
if (GVERB_INCLUDE_DIRS)
# in cache already
set(GVERB_FOUND TRUE)
else (GVERB_INCLUDE_DIRS)
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("gverb")
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("gverb")
find_path(GVERB_INCLUDE_DIRS gverb.h PATH_SUFFIXES include HINTS ${GVERB_SEARCH_DIRS})
find_library(GVERB_LIBRARIES gverb PATH_SUFFIXES lib HINTS ${GVERB_SEARCH_DIRS})
find_path(GVERB_INCLUDE_DIRS gverb.h PATH_SUFFIXES include HINTS ${GVERB_SEARCH_DIRS} NO_CMAKE_FIND_ROOT_PATH)
find_path(GVERB_SRC_DIRS gverb.c PATH_SUFFIXES src HINTS ${GVERB_SEARCH_DIRS} NO_CMAKE_FIND_ROOT_PATH)
if (GVERB_INCLUDE_DIRS)
set(GVERB_FOUND TRUE)
endif (GVERB_INCLUDE_DIRS)
if (GVERB_FOUND)
message(STATUS "Found Gverb: ${GVERB_INCLUDE_DIRS}")
else (GVERB_FOUND)
message(FATAL_ERROR "Could NOT find Gverb. Read ./libraries/audio-client/externals/gverb/readme.txt")
endif (GVERB_FOUND)
endif(GVERB_INCLUDE_DIRS)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GVERB DEFAULT_MSG GVERB_INCLUDE_DIRS GVERB_LIBRARIES)

View file

@ -409,6 +409,13 @@
"default": "",
"advanced": true
},
{
"name": "wantEditLogging",
"type": "checkbox",
"help": "Logging of all edits to entities",
"default": true,
"advanced": true
},
{
"name": "verboseDebug",
"type": "checkbox",

View file

@ -0,0 +1,256 @@
//
// goTo.js
// examples
//
// Created by Thijs Wenker on 12/28/14.
// Copyright 2014 High Fidelity, Inc.
//
// Control a virtual keyboard using your favorite HMD.
// Usage: Enable VR-mode and go to First person mode,
// look at the key that you would like to press, and press the spacebar on your "REAL" keyboard.
//
// Enter a location URL using your HMD. Press Enter to pop-up the virtual keyboard and location input.
// Press Space on the keyboard or the X button on your gamepad to press a key that you have selected.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../libraries/globals.js");
Script.include("../../libraries/virtualKeyboard.js");
Script.include("../../libraries/soundArray.js");
const MAX_SHOW_INSTRUCTION_TIMES = 2;
const INSTRUCTIONS_SETTING = "GoToInstructionsShowCounter"
var windowDimensions = Controller.getViewportDimensions();
function Instructions(imageURL, originalWidth, originalHeight) {
var tthis = this;
this.originalSize = {w: originalWidth, h: originalHeight};
this.visible = false;
this.overlay = Overlays.addOverlay("image", {
imageURL: imageURL,
x: 0,
y: 0,
width: originalWidth,
height: originalHeight,
alpha: 1,
visible: this.visible
});
this.show = function() {
var timesShown = Settings.getValue(INSTRUCTIONS_SETTING);
timesShown = timesShown === "" ? 0 : parseInt(timesShown);
print(timesShown);
if (timesShown < MAX_SHOW_INSTRUCTION_TIMES) {
Settings.setValue(INSTRUCTIONS_SETTING, timesShown + 1);
tthis.visible = true;
tthis.rescale();
Overlays.editOverlay(tthis.overlay, {visible: tthis.visible});
return;
}
tthis.remove();
}
this.remove = function() {
Overlays.deleteOverlay(tthis.overlay);
tthis.visible = false;
};
this.rescale = function() {
var scale = Math.min(windowDimensions.x / tthis.originalSize.w, windowDimensions.y / tthis.originalSize.h);
var newWidth = tthis.originalSize.w * scale;
var newHeight = tthis.originalSize.h * scale;
Overlays.editOverlay(tthis.overlay, {
x: (windowDimensions.x / 2) - (newWidth / 2),
y: (windowDimensions.y / 2) - (newHeight / 2),
width: newWidth,
height: newHeight
});
};
this.rescale();
};
var theInstruction = new Instructions(HIFI_PUBLIC_BUCKET + "images/tutorial-goTo.svg", 457, 284.1);
var firstControllerPlugged = false;
var cursor = new Cursor({visible: false});
var keyboard = new Keyboard({visible: false});
var textFontSize = 9;
var text = null;
var locationURL = "";
var randomSounds = new SoundArray({}, true);
var numberOfSounds = 7;
for (var i = 1; i <= numberOfSounds; i++) {
randomSounds.addSound(HIFI_PUBLIC_BUCKET + "sounds/UI/virtualKeyboard-press" + i + ".raw");
}
function appendChar(char) {
locationURL += char;
updateTextOverlay();
Overlays.editOverlay(text, {text: locationURL});
}
function deleteChar() {
if (locationURL.length > 0) {
locationURL = locationURL.substring(0, locationURL.length - 1);
updateTextOverlay();
}
}
function updateTextOverlay() {
var maxLineWidth = Overlays.textSize(text, locationURL).width;
var suggestedFontSize = (windowDimensions.x / maxLineWidth) * textFontSize * 0.90;
var maxFontSize = 140;
textFontSize = (suggestedFontSize > maxFontSize) ? maxFontSize : suggestedFontSize;
var topMargin = (250 - textFontSize) / 4;
Overlays.editOverlay(text, {text: locationURL, font: {size: textFontSize}, topMargin: topMargin, visible: keyboard.visible});
maxLineWidth = Overlays.textSize(text, locationURL).width;
Overlays.editOverlay(text, {leftMargin: (windowDimensions.x - maxLineWidth) / 2});
}
keyboard.onKeyPress = function(event) {
randomSounds.playRandom();
if (event.event == 'keypress') {
appendChar(event.char);
}
};
keyboard.onKeyRelease = function(event) {
// you can cancel a key by releasing its focusing before releasing it
if (event.focus) {
if (event.event == 'delete') {
deleteChar();
} else if (event.event == 'submit' || event.event == 'enter') {
print("going to hifi://" + locationURL);
location = "hifi://" + locationURL;
locationURL = "";
keyboard.hide();
cursor.hide();
updateTextOverlay();
}
}
};
keyboard.onFullyLoaded = function() {
print("Virtual-keyboard fully loaded.");
var dimensions = Controller.getViewportDimensions();
text = Overlays.addOverlay("text", {
x: 0,
y: dimensions.y - keyboard.height() - 260,
width: dimensions.x,
height: 250,
backgroundColor: { red: 255, green: 255, blue: 255},
color: { red: 0, green: 0, blue: 0},
topMargin: 5,
leftMargin: 0,
font: {size: textFontSize},
text: "",
alpha: 0.8,
visible: keyboard.visible
});
updateTextOverlay();
// the cursor is being loaded after the keyboard, else it will be on the background of the keyboard
cursor.initialize();
cursor.updateVisibility(keyboard.visible);
cursor.onUpdate = function(position) {
keyboard.setFocusPosition(position.x, position.y);
};
};
function keyPressEvent(event) {
if (theInstruction.visible) {
return;
}
if (event.key === SPACEBAR_CHARCODE) {
keyboard.pressFocussedKey();
} else if (event.key === ENTER_CHARCODE || event.key === RETURN_CHARCODE) {
keyboard.toggle();
if (cursor !== undefined) {
cursor.updateVisibility(keyboard.visible);
}
Overlays.editOverlay(text, {visible: keyboard.visible});
}
}
function keyReleaseEvent(event) {
if (theInstruction.visible) {
return;
}
if (event.key === SPACEBAR_CHARCODE) {
keyboard.releaseKeys();
}
}
function scriptEnding() {
keyboard.remove();
cursor.remove();
Overlays.deleteOverlay(text);
Overlays.deleteOverlay(textSizeMeasureOverlay);
Controller.releaseKeyEvents({key: SPACEBAR_CHARCODE});
Controller.releaseKeyEvents({key: RETURN_CHARCODE});
Controller.releaseKeyEvents({key: ENTER_CHARCODE});
theInstruction.remove();
}
function reportButtonValue(button, newValue, oldValue) {
if (theInstruction.visible) {
if (button == Joysticks.BUTTON_FACE_BOTTOM && newValue) {
theInstruction.remove();
}
} else if (button == Joysticks.BUTTON_FACE_BOTTOM) {
if (newValue) {
keyboard.pressFocussedKey();
} else {
keyboard.releaseKeys();
}
} else if (button == Joysticks.BUTTON_FACE_RIGHT && newValue) {
deleteChar();
} else if (button == Joysticks.BUTTON_FACE_LEFT && newValue) {
keyboard.hide();
if (cursor !== undefined) {
cursor.hide();
}
Overlays.editOverlay(text, {visible: false});
} else if (button == Joysticks.BUTTON_RIGHT_SHOULDER && newValue) {
if (keyboard.visible) {
print("going to hifi://" + locationURL);
location = "hifi://" + locationURL;
locationURL = "";
keyboard.hide();
cursor.hide();
updateTextOverlay();
return;
}
keyboard.show();
if (cursor !== undefined) {
cursor.show();
}
Overlays.editOverlay(text, {visible: true});
}
}
function addJoystick(gamepad) {
gamepad.buttonStateChanged.connect(reportButtonValue);
if (!firstControllerPlugged) {
firstControllerPlugged = true;
theInstruction.show();
}
}
var allJoysticks = Joysticks.getAllJoysticks();
for (var i = 0; i < allJoysticks.length; i++) {
addJoystick(allJoysticks[i]);
}
Joysticks.joystickAdded.connect(addJoystick);
Controller.captureKeyEvents({key: RETURN_CHARCODE});
Controller.captureKeyEvents({key: ENTER_CHARCODE});
Controller.captureKeyEvents({key: SPACEBAR_CHARCODE});
Controller.keyPressEvent.connect(keyPressEvent);
Controller.keyReleaseEvent.connect(keyReleaseEvent);
Script.scriptEnding.connect(scriptEnding);

View file

@ -0,0 +1,191 @@
//
// virtualKeyboardTextEntityExample.js
// examples
//
// Created by Thijs Wenker on 12/28/14.
// Copyright 2014 High Fidelity, Inc.
//
// Control a virtual keyboard using your favorite HMD.
// Usage: Enable VR-mode and go to First person mode,
// look at the key that you would like to press, and press the spacebar on your "REAL" keyboard.
//
// leased some code from newEditEntities.js for Text Entity example
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../libraries/globals.js");
Script.include("../../libraries/virtualKeyboard.js");
Script.include("../../libraries/soundArray.js");
const SPAWN_DISTANCE = 1;
const DEFAULT_TEXT_DIMENSION_Z = 0.02;
const TEXT_MARGIN_TOP = 0.15;
const TEXT_MARGIN_LEFT = 0.15;
const TEXT_MARGIN_RIGHT = 0.17;
const TEXT_MARGIN_BOTTOM = 0.17;
var windowDimensions = Controller.getViewportDimensions();
var cursor = new Cursor();
var keyboard = new Keyboard();
var textFontSize = 9;
var text = null;
var textText = "";
var textSizeMeasureOverlay = Overlays.addOverlay("text3d", {visible: false});
var randomSounds = new SoundArray({}, true);
var numberOfSounds = 7;
for (var i = 1; i <= numberOfSounds; i++) {
randomSounds.addSound(HIFI_PUBLIC_BUCKET + "sounds/UI/virtualKeyboard-press" + i + ".raw");
}
function appendChar(char) {
textText += char;
updateTextOverlay();
Overlays.editOverlay(text, {text: textText});
}
function deleteChar() {
if (textText.length > 0) {
textText = textText.substring(0, textText.length - 1);
updateTextOverlay();
}
}
function updateTextOverlay() {
var textLines = textText.split("\n");
var suggestedFontSize = (windowDimensions.x / Overlays.textSize(text, textText).width) * textFontSize * 0.90;
var maxFontSize = 170 / textLines.length;
textFontSize = (suggestedFontSize > maxFontSize) ? maxFontSize : suggestedFontSize;
var topMargin = (250 - (textFontSize * textLines.length)) / 4;
Overlays.editOverlay(text, {text: textText, font: {size: textFontSize}, topMargin: topMargin});
Overlays.editOverlay(text, {leftMargin: (windowDimensions.x - Overlays.textSize(text, textLines).width) / 2});
}
keyboard.onKeyPress = function(event) {
randomSounds.playRandom();
if (event.event == 'keypress') {
appendChar(event.char);
} else if (event.event == 'enter') {
appendChar("\n");
}
};
keyboard.onKeyRelease = function(event) {
print("Key release event test");
// you can cancel a key by releasing its focusing before releasing it
if (event.focus) {
if (event.event == 'delete') {
deleteChar();
} else if (event.event == 'submit') {
print(textText);
var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE));
var textLines = textText.split("\n");
var maxLineWidth = Overlays.textSize(textSizeMeasureOverlay, textText).width;
var usernameLine = "--" + GlobalServices.myUsername;
var usernameWidth = Overlays.textSize(textSizeMeasureOverlay, usernameLine).width;
if (maxLineWidth < usernameWidth) {
maxLineWidth = usernameWidth;
} else {
var spaceableWidth = maxLineWidth - usernameWidth;
//TODO: WORKAROUND WARNING BELOW Fix this when spaces are not trimmed out of the textsize calculation anymore
var spaceWidth = Overlays.textSize(textSizeMeasureOverlay, "| |").width
- Overlays.textSize(textSizeMeasureOverlay, "||").width;
var numberOfSpaces = Math.floor(spaceableWidth / spaceWidth);
for (var i = 0; i < numberOfSpaces; i++) {
usernameLine = " " + usernameLine;
}
}
var dimension_x = maxLineWidth + TEXT_MARGIN_RIGHT + TEXT_MARGIN_LEFT;
if (position.x > 0 && position.y > 0 && position.z > 0) {
Entities.addEntity({
type: "Text",
rotation: MyAvatar.orientation,
position: position,
dimensions: { x: dimension_x, y: (textLines.length + 1) * 0.14 + TEXT_MARGIN_TOP + TEXT_MARGIN_BOTTOM, z: DEFAULT_TEXT_DIMENSION_Z },
backgroundColor: { red: 0, green: 0, blue: 0 },
textColor: { red: 255, green: 255, blue: 255 },
text: textText + "\n" + usernameLine,
lineHeight: 0.1
});
}
textText = "";
updateTextOverlay();
}
}
};
keyboard.onFullyLoaded = function() {
print("Virtual-keyboard fully loaded.");
var dimensions = Controller.getViewportDimensions();
text = Overlays.addOverlay("text", {
x: 0,
y: dimensions.y - keyboard.height() - 260,
width: dimensions.x,
height: 250,
backgroundColor: { red: 255, green: 255, blue: 255},
color: { red: 0, green: 0, blue: 0},
topMargin: 5,
leftMargin: 0,
font: {size: textFontSize},
text: "",
alpha: 0.8
});
updateTextOverlay();
// the cursor is being loaded after the keyboard, else it will be on the background of the keyboard
cursor.initialize();
cursor.onUpdate = function(position) {
keyboard.setFocusPosition(position.x, position.y);
};
};
function keyPressEvent(event) {
if (event.key === SPACEBAR_CHARCODE) {
keyboard.pressFocussedKey();
}
}
function keyReleaseEvent(event) {
if (event.key === SPACEBAR_CHARCODE) {
keyboard.releaseKeys();
}
}
function scriptEnding() {
keyboard.remove();
cursor.remove();
Overlays.deleteOverlay(text);
Overlays.deleteOverlay(textSizeMeasureOverlay);
Controller.releaseKeyEvents({key: SPACEBAR_CHARCODE});
}
function reportButtonValue(button, newValue, oldValue) {
if (button == Joysticks.BUTTON_FACE_BOTTOM) {
if (newValue) {
keyboard.pressFocussedKey();
} else {
keyboard.releaseKeys();
}
} else if (button == Joysticks.BUTTON_FACE_RIGHT && newValue) {
deleteChar();
}
}
function addJoystick(gamepad) {
gamepad.buttonStateChanged.connect(reportButtonValue);
}
var allJoysticks = Joysticks.getAllJoysticks();
for (var i = 0; i < allJoysticks.length; i++) {
addJoystick(allJoysticks[i]);
}
Joysticks.joystickAdded.connect(addJoystick);
Controller.captureKeyEvents({key: SPACEBAR_CHARCODE});
Controller.keyPressEvent.connect(keyPressEvent);
Controller.keyReleaseEvent.connect(keyReleaseEvent);
Script.scriptEnding.connect(scriptEnding);

View file

@ -127,7 +127,7 @@ var toolBar = (function () {
height: toolHeight,
alpha: 0.9,
visible: true
}, true, false);
});
browseModelsButton = toolBar.addTool({
imageURL: toolIconUrl + "list-icon.svg",
@ -262,6 +262,8 @@ var toolBar = (function () {
toolBar.move(toolsX, toolsY);
};
var newModelButtonDown = false;
var browseModelsButtonDown = false;
that.mousePressEvent = function (event) {
var clickedOverlay,
url,
@ -274,19 +276,14 @@ var toolBar = (function () {
return true;
}
// Handle these two buttons in the mouseRelease event handler so that we don't suppress a mouseRelease event from
// occurring when showing a modal dialog.
if (newModelButton === toolBar.clicked(clickedOverlay)) {
url = Window.prompt("Model URL", modelURLs[Math.floor(Math.random() * modelURLs.length)]);
if (url !== null && url !== "") {
addModel(url);
}
newModelButtonDown = true;
return true;
}
if (browseModelsButton === toolBar.clicked(clickedOverlay)) {
url = Window.s3Browse(".*(fbx|FBX)");
if (url !== null && url !== "") {
addModel(url);
}
browseModelsButtonDown = true;
return true;
}
@ -348,6 +345,7 @@ var toolBar = (function () {
return true;
}
if (newTextButton === toolBar.clicked(clickedOverlay)) {
var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE));
@ -367,10 +365,37 @@ var toolBar = (function () {
return true;
}
return false;
};
that.mouseReleaseEvent = function(event) {
var handled = false;
if (newModelButtonDown) {
var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
if (newModelButton === toolBar.clicked(clickedOverlay)) {
url = Window.prompt("Model URL", modelURLs[Math.floor(Math.random() * modelURLs.length)]);
if (url !== null && url !== "") {
addModel(url);
}
handled = true;
}
} else if (browseModelsButtonDown) {
var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
if (browseModelsButton === toolBar.clicked(clickedOverlay)) {
url = Window.s3Browse(".*(fbx|FBX)");
if (url !== null && url !== "") {
addModel(url);
}
handled = true;
}
}
newModelButtonDown = false;
browseModelsButtonDown = false;
return handled;
}
that.cleanup = function () {
toolBar.cleanup();
};
@ -533,6 +558,9 @@ function highlightEntityUnderCursor(position, accurateRay) {
function mouseReleaseEvent(event) {
if (toolBar.mouseReleaseEvent(event)) {
return true;
}
if (placingEntityID) {
if (isActive) {
selectionManager.setSelections([placingEntityID]);
@ -959,9 +987,18 @@ PropertiesTool = function(opts) {
selectionManager.saveProperties();
for (var i = 0; i < selectionManager.selections.length; i++) {
var properties = selectionManager.savedProperties[selectionManager.selections[i].id];
Entities.editEntity(selectionManager.selections[i], {
dimensions: properties.naturalDimensions,
});
var naturalDimensions = properties.naturalDimensions;
// If any of the natural dimensions are not 0, resize
if (properties.type == "Model" && naturalDimensions.x == 0
&& naturalDimensions.y == 0 && naturalDimensions.z == 0) {
Window.alert("Cannot reset entity to its natural dimensions: Model URL"
+ " is invalid or the model has not yet been loaded.");
} else {
Entities.editEntity(selectionManager.selections[i], {
dimensions: properties.naturalDimensions,
});
}
}
pushCommandForSelections();
selectionManager._update();

View file

@ -1,6 +1,7 @@
(function(){
var teleport;
var portalDestination;
var animationURL;
function playSound() {
Audio.playSound(teleport, { volume: 0.40, localOnly: true });
@ -11,6 +12,7 @@
var properties = Entities.getEntityProperties(entityID);
portalDestination = properties.userData;
animationURL = properties.modelURL;
print("The portal destination is " + portalDestination);
}
@ -25,7 +27,7 @@
this.leaveEntity = function(entityID) {
Entities.editEntity(entityID, {
animationURL: "http://hifi-public.s3.amazonaws.com/models/content/phonebooth.fbx",
animationURL: animationURL,
animationSettings: '{ "frameIndex": 1, "running": false }'
});
@ -34,7 +36,7 @@
this.hoverEnterEntity = function(entityID) {
Entities.editEntity(entityID, {
animationURL: "http://hifi-public.s3.amazonaws.com/models/content/phonebooth.fbx",
animationURL: animationURL,
animationSettings: '{ "fps": 24, "firstFrame": 1, "lastFrame": 25, "frameIndex": 1, "running": true, "hold": true }'
});
};

View file

@ -105,37 +105,27 @@ function makeTable(pos) {
}
function makeBalls(pos) {
var colors = [{ red: 255, green: 255, blue: 0}, // Yellow
{ red: 0, green: 0, blue: 255}, // Blue
{ red: 255, green: 0, blue: 0}, // Red
{ red: 128, green: 0, blue: 128}, // Purple
{ red: 255, green: 165, blue: 0}, // Orange
{ red: 0, green: 255, blue: 0}, // Green
{ red: 128, green: 0, blue: 0}, // Maroon
{ red: 0, green: 0, blue: 0}, // Black
{ red: 255, green: 255, blue: 224}, // Light Yellow
{ red: 173, green: 216, blue: 230}, // Light Blue
{ red: 205, green: 92, blue: 92}, // Indian Red
{ red: 218, green: 112, blue: 214}, // Orchid
{ red: 218, green: 165, blue: 32}, // GoldenRod
{ red: 255, green: 99, blue: 71}, // Tomato
{ red: 128, green: 128, blue: 128}]; // Gray
// Object balls
var whichBall = [ 1, 14, 15, 4, 8, 7, 12, 9, 3, 13, 10, 5, 6, 11, 2 ];
var ballNumber = 0;
var ballPosition = { x: pos.x + (LENGTH / 4.0) * SCALE, y: pos.y + HEIGHT / 2.0 + DROP_HEIGHT, z: pos.z };
for (var row = 1; row <= 5; row++) {
ballPosition.z = pos.z - ((row - 1.0) / 2.0 * (BALL_SIZE + BALL_GAP) * SCALE);
for (var spot = 0; spot < row; spot++) {
balls.push(Entities.addEntity(
{ type: "Sphere",
{ type: "Model",
modelURL: "https://s3.amazonaws.com/hifi-public/models/props/Pool/ball_" + whichBall[ballNumber].toString() + ".fbx",
position: ballPosition,
dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE },
color: colors[balls.length],
rotation: Quat.fromPitchYawRollDegrees((Math.random() - 0.5) * 20, (Math.random() - 0.5) * 20, (Math.random() - 0.5) * 20),
color: { red: 255, green: 255, blue: 255 },
gravity: { x: 0, y: GRAVITY, z: 0 },
ignoreCollisions: false,
damping: 0.50,
shapeType: 2,
collisionsWillMove: true }));
ballPosition.z += (BALL_SIZE + BALL_GAP) * SCALE;
ballNumber++;
}
ballPosition.x += (BALL_GAP + Math.sqrt(3.0) / 2.0 * BALL_SIZE) * SCALE;
}
@ -143,7 +133,8 @@ function makeBalls(pos) {
// Cue Ball
cuePosition = { x: pos.x - (LENGTH / 4.0) * SCALE, y: pos.y + HEIGHT / 2.0 + DROP_HEIGHT, z: pos.z };
cueBall = Entities.addEntity(
{ type: "Sphere",
{ type: "Model",
modelURL: "https://s3.amazonaws.com/hifi-public/models/props/Pool/cue_ball.fbx",
position: cuePosition,
dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE },
color: { red: 255, green: 255, blue: 255 },
@ -152,6 +143,7 @@ function makeBalls(pos) {
velocity: {x: 0, y: 0, z: 0 },
ignoreCollisions: false,
damping: 0.50,
shapeType: 2,
collisionsWillMove: true });
}

View file

@ -477,7 +477,7 @@ CameraManager = function() {
// Last mode that was first or third person
var lastAvatarCameraMode = "first person";
Camera.modeUpdated.connect(function(newMode) {
if (newMode == "first person" || newMode == "third person") {
if (newMode != "independent") {
lastAvatarCameraMode = newMode;
that.disable(true);
} else {

View file

@ -0,0 +1,42 @@
/**
* An array for sounds, allows you to randomly play a sound
* taken from the removed editVoxels.js
*/
SoundArray = function(audioOptions, autoUpdateAudioPosition) {
this.audioOptions = audioOptions !== undefined ? audioOptions : {};
this.autoUpdateAudioPosition = autoUpdateAudioPosition !== undefined ? autoUpdateAudioPosition : false;
if (this.audioOptions.position === undefined) {
this.audioOptions.position = Vec3.sum(MyAvatar.position, { x: 0, y: 1, z: 0}),
}
if (this.audioOptions.volume === undefined) {
this.audioOptions.volume = 1.0;
}
this.sounds = new Array();
this.addSound = function (soundURL) {
this.sounds[this.sounds.length] = SoundCache.getSound(soundURL);
};
this.play = function (index) {
if (0 <= index && index < this.sounds.length) {
if (this.autoUpdateAudioPosition) {
this.updateAudioPosition();
}
if (this.sounds[index].downloaded) {
Audio.playSound(this.sounds[index], this.audioOptions);
}
} else {
print("[ERROR] libraries/soundArray.js:play() : Index " + index + " out of range.");
}
};
this.playRandom = function () {
if (this.sounds.length > 0) {
this.play(Math.floor(Math.random() * this.sounds.length));
} else {
print("[ERROR] libraries/soundArray.js:playRandom() : Array is empty.");
}
};
this.updateAudioPosition = function() {
var position = MyAvatar.position;
var forwardVector = Quat.getFront(MyAvatar.orientation);
this.audioOptions.position = Vec3.sum(position, forwardVector);
};
};

View file

@ -9,179 +9,50 @@
// Usage: Enable VR-mode and go to First person mode,
// look at the key that you would like to press, and press the spacebar on your "REAL" keyboard.
//
// leased some code from newEditEntities.js for Text Entity example
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
// experimental 3dmode
THREE_D_MODE = false;
const KBD_UPPERCASE_DEFAULT = 0;
const KBD_LOWERCASE_DEFAULT = 1;
const KBD_UPPERCASE_HOVER = 2;
const KBD_LOWERCASE_HOVER = 3;
const KBD_BACKGROUND = 4;
KBD_UPPERCASE_DEFAULT = 0;
KBD_LOWERCASE_DEFAULT = 1;
KBD_UPPERCASE_HOVER = 2;
KBD_LOWERCASE_HOVER = 3;
KBD_BACKGROUND = 4;
const KEYBOARD_URL = HIFI_PUBLIC_BUCKET + "images/keyboard.svg";
const CURSOR_URL = HIFI_PUBLIC_BUCKET + "images/cursor.svg";
KEYBOARD_URL = HIFI_PUBLIC_BUCKET + "images/keyboard.svg";
CURSOR_URL = HIFI_PUBLIC_BUCKET + "images/cursor.svg";
const SPACEBAR_CHARCODE = 32;
RETURN_CHARCODE = 0x01000004;
ENTER_CHARCODE = 0x01000005;
SPACEBAR_CHARCODE = 32;
const KEYBOARD_WIDTH = 1174.7;
const KEYBOARD_HEIGHT = 434.1;
KEYBOARD_WIDTH = 1174.7;
KEYBOARD_HEIGHT = 434.1;
const CURSOR_WIDTH = 33.9;
const CURSOR_HEIGHT = 33.9;
CURSOR_WIDTH = 33.9;
CURSOR_HEIGHT = 33.9;
KEYBOARD_SCALE_MULTIPLIER = 0.50;
// VIEW_ANGLE can be adjusted to your likings, the smaller the faster movement.
// Try setting it to 60 if it goes too fast for you.
const VIEW_ANGLE = 40.0;
const VIEW_ANGLE_BY_TWO = VIEW_ANGLE / 2;
const SPAWN_DISTANCE = 1;
const DEFAULT_TEXT_DIMENSION_Z = 0.02;
const BOUND_X = 0;
const BOUND_Y = 1;
const BOUND_W = 2;
const BOUND_H = 3;
const KEY_STATE_LOWER = 0;
const KEY_STATE_UPPER = 1;
const TEXT_MARGIN_TOP = 0.15;
const TEXT_MARGIN_LEFT = 0.15;
const TEXT_MARGIN_RIGHT = 0.17;
const TEXT_MARGIN_BOTTOM = 0.17;
KEY_STATE_LOWER = 0;
KEY_STATE_UPPER = 1;
var windowDimensions = Controller.getViewportDimensions();
var cursor = null;
var keyboard = new Keyboard();
var textFontSize = 9;
var text = null;
var textText = "";
var textSizeMeasureOverlay = Overlays.addOverlay("text3d", {visible: false});
function appendChar(char) {
textText += char;
updateTextOverlay();
Overlays.editOverlay(text, {text: textText});
}
function deleteChar() {
if (textText.length > 0) {
textText = textText.substring(0, textText.length - 1);
updateTextOverlay();
}
}
function updateTextOverlay() {
var textLines = textText.split("\n");
var maxLineWidth = 0;
for (textLine in textLines) {
var lineWidth = Overlays.textSize(text, textLines[textLine]).width;
if (lineWidth > maxLineWidth) {
maxLineWidth = lineWidth;
}
}
var suggestedFontSize = (windowDimensions.x / maxLineWidth) * textFontSize * 0.90;
var maxFontSize = 190 / textLines.length;
textFontSize = (suggestedFontSize > maxFontSize) ? maxFontSize : suggestedFontSize;
var topMargin = (250 - (textFontSize * textLines.length)) / 4;
Overlays.editOverlay(text, {text: textText, font: {size: textFontSize}, topMargin: topMargin});
var maxLineWidth = 0;
for (textLine in textLines) {
var lineWidth = Overlays.textSize(text, textLines[textLine]).width;
if (lineWidth > maxLineWidth) {
maxLineWidth = lineWidth;
}
}
Overlays.editOverlay(text, {leftMargin: (windowDimensions.x - maxLineWidth) / 2});
}
keyboard.onKeyPress = function(event) {
if (event.event == 'keypress') {
appendChar(event.char);
} else if (event.event == 'enter') {
appendChar("\n");
}
};
keyboard.onKeyRelease = function(event) {
print("Key release event test");
// you can cancel a key by releasing its focusing before releasing it
if (event.focus) {
if (event.event == 'delete') {
deleteChar();
} else if (event.event == 'submit') {
print(textText);
var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE));
var textLines = textText.split("\n");
var maxLineWidth = 0;
for (textLine in textLines) {
var lineWidth = Overlays.textSize(textSizeMeasureOverlay, textLines[textLine]).width;
if (lineWidth > maxLineWidth) {
maxLineWidth = lineWidth;
}
}
var usernameLine = "--" + GlobalServices.myUsername;
var usernameWidth = Overlays.textSize(textSizeMeasureOverlay, usernameLine).width;
if (maxLineWidth < usernameWidth) {
maxLineWidth = usernameWidth;
} else {
var spaceableWidth = maxLineWidth - usernameWidth;
var spaceWidth = Overlays.textSize(textSizeMeasureOverlay, " ").width;
var numberOfSpaces = Math.floor(spaceableWidth / spaceWidth);
for (var i = 0; i < numberOfSpaces; i++) {
usernameLine = " " + usernameLine;
}
}
var dimension_x = maxLineWidth + TEXT_MARGIN_RIGHT + TEXT_MARGIN_LEFT;
if (position.x > 0 && position.y > 0 && position.z > 0) {
Entities.addEntity({
type: "Text",
rotation: MyAvatar.orientation,
position: position,
dimensions: { x: dimension_x, y: (textLines.length + 1) * 0.14 + TEXT_MARGIN_TOP + TEXT_MARGIN_BOTTOM, z: DEFAULT_TEXT_DIMENSION_Z },
backgroundColor: { red: 0, green: 0, blue: 0 },
textColor: { red: 255, green: 255, blue: 255 },
text: textText + "\n" + usernameLine
});
}
textText = "";
updateTextOverlay();
}
}
};
keyboard.onFullyLoaded = function() {
print("Virtual-keyboard fully loaded.");
var dimensions = Controller.getViewportDimensions();
text = Overlays.addOverlay("text", {
x: 0,
y: dimensions.y - keyboard.height() - 260,
width: dimensions.x,
height: 250,
backgroundColor: { red: 255, green: 255, blue: 255},
color: { red: 0, green: 0, blue: 0},
topMargin: 5,
leftMargin: 0,
font: {size: textFontSize},
text: "",
alpha: 0.8
});
updateTextOverlay();
// the cursor is being loaded after the keyboard, else it will be on the background of the keyboard
cursor = new Cursor();
cursor.onUpdate = function(position) {
keyboard.setFocusPosition(position.x, position.y);
};
};
function KeyboardKey(keyboard, keyProperties) {
KeyboardKey = (function(keyboard, keyProperties) {
var tthis = this;
this._focus = false;
this._beingPressed = false;
@ -247,6 +118,11 @@ function KeyboardKey(keyboard, keyProperties) {
});
}
};
this.updateVisibility = function() {
for (var i = 0; i < tthis.bounds.length; i++) {
Overlays.editOverlay(tthis.overlays[i], {visible: tthis.keyboard.visible});
}
};
this.rescale = function() {
for (var i = 0; i < tthis.bounds.length; i++) {
Overlays.editOverlay(tthis.overlays[i], {
@ -271,24 +147,48 @@ function KeyboardKey(keyboard, keyProperties) {
return true;
};
for (var i = 0; i < this.bounds.length; i++) {
var newOverlay = Overlays.cloneOverlay(this.keyboard.background);
Overlays.editOverlay(newOverlay, {
x: this.keyboard.getX() + this.bounds[i][BOUND_X] * keyboard.scale,
y: this.keyboard.getY() + this.bounds[i][BOUND_Y] * keyboard.scale,
width: this.bounds[i][BOUND_W] * keyboard.scale,
height: this.bounds[i][BOUND_H] * keyboard.scale,
subImage: {width: this.bounds[i][BOUND_W], height: this.bounds[i][BOUND_H], x: this.bounds[i][BOUND_X], y: (KEYBOARD_HEIGHT * this.keyState) + this.bounds[i][BOUND_Y]},
alpha: 1
});
this.overlays.push(newOverlay);
if (THREE_D_MODE) {
this.overlays.push(Overlays.addOverlay("billboard", {
scale: 1,
rotation: MyAvatar.rotation,
isFacingAvatar: false,
url: KEYBOARD_URL,
alpha: 1,
position: {
x: MyAvatar.position.x,// + this.bounds[i][BOUND_X] * 0.01,// /*+ this.keyboard.getX()*/ + this.bounds[i][BOUND_X] * keyboard.scale,
y: MyAvatar.position.y,// - this.bounds[i][BOUND_Y] * 0.01,// /*+ this.keyboard.getY()*/ + this.bounds[i][BOUND_Y] * keyboard.scale,
z: MyAvatar.position.z
},
width: this.bounds[i][BOUND_W] * keyboard.scale,
height: this.bounds[i][BOUND_H] * keyboard.scale,
subImage: {width: this.bounds[i][BOUND_W], height: this.bounds[i][BOUND_H], x: this.bounds[i][BOUND_X], y: (KEYBOARD_HEIGHT * this.keyState) + this.bounds[i][BOUND_Y]},
alpha: 1,
visible: tthis.keyboard.visible
}));
} else {
this.overlays.push(Overlays.addOverlay("image", {
imageURL: KEYBOARD_URL,
x: this.keyboard.getX() + this.bounds[i][BOUND_X] * keyboard.scale,
y: this.keyboard.getY() + this.bounds[i][BOUND_Y] * keyboard.scale,
width: this.bounds[i][BOUND_W] * keyboard.scale,
height: this.bounds[i][BOUND_H] * keyboard.scale,
subImage: {width: this.bounds[i][BOUND_W], height: this.bounds[i][BOUND_H], x: this.bounds[i][BOUND_X], y: (KEYBOARD_HEIGHT * this.keyState) + this.bounds[i][BOUND_Y]},
alpha: 1,
visible: tthis.keyboard.visible
}));
}
}
}
});
function Keyboard() {
Keyboard = (function(params) {
if (params === undefined) {
params = {};
}
var tthis = this;
this.focussed_key = -1;
this.scale = windowDimensions.x / KEYBOARD_WIDTH;
this.scale = (windowDimensions.x / KEYBOARD_WIDTH) * KEYBOARD_SCALE_MULTIPLIER;
this.shift = false;
this.visible = params.visible != undefined ? params.visible : true;
this.width = function() {
return KEYBOARD_WIDTH * tthis.scale;
};
@ -301,17 +201,33 @@ function Keyboard() {
this.getY = function() {
return windowDimensions.y - this.height();
};
this.background = Overlays.addOverlay("image", {
x: this.getX(),
y: this.getY(),
width: this.width(),
height: this.height(),
subImage: {width: KEYBOARD_WIDTH, height: KEYBOARD_HEIGHT, y: KEYBOARD_HEIGHT * KBD_BACKGROUND},
imageURL: KEYBOARD_URL,
alpha: 1
});
if (THREE_D_MODE) {
this.background = Overlays.addOverlay("billboard", {
scale: 1,
position: MyAvatar.position,
rotation: MyAvatar.rotation,
width: this.width(),
height: this.height(),
subImage: {width: KEYBOARD_WIDTH, height: KEYBOARD_HEIGHT, y: KEYBOARD_HEIGHT * KBD_BACKGROUND},
isFacingAvatar: false,
url: KEYBOARD_URL,
alpha: 1,
visible: this.visible
});
} else {
this.background = Overlays.addOverlay("image", {
x: this.getX(),
y: this.getY(),
width: this.width(),
height: this.height(),
subImage: {width: KEYBOARD_WIDTH, height: KEYBOARD_HEIGHT, y: KEYBOARD_HEIGHT * KBD_BACKGROUND},
imageURL: KEYBOARD_URL,
alpha: 1,
visible: this.visible
});
}
this.rescale = function() {
this.scale = windowDimensions.x / KEYBOARD_WIDTH;
this.scale = (windowDimensions.x / KEYBOARD_WIDTH) * KEYBOARD_SCALE_MULTIPLIER;
Overlays.editOverlay(tthis.background, {
x: this.getX(),
y: this.getY(),
@ -349,6 +265,9 @@ function Keyboard() {
};
this.pressFocussedKey = function() {
if (!tthis.visible) {
return tthis;
}
if (tthis.focussed_key != -1) {
if (tthis.keys[tthis.focussed_key].event == 'shift') {
tthis.toggleShift();
@ -402,6 +321,32 @@ function Keyboard() {
for (var i = 0; i < this.keys.length; i++) {
this.keys[i].remove();
}
// resets the cursor and magnifier
this.updateVisibility(false);
};
this.show = function() {
tthis.updateVisibility(true);
};
this.hide = function() {
tthis.updateVisibility(false);
};
this.toggle = function() {
tthis.updateVisibility(!tthis.visible);
};
this.updateVisibility = function(visible) {
tthis.visible = visible;
if (HMD.magnifier == visible) {
HMD.toggleMagnifier();
}
Window.cursorVisible = !visible;
Overlays.editOverlay(tthis.background, { visible: tthis.visible });
for (var i = 0; i < this.keys.length; i++) {
this.keys[i].updateVisibility();
}
};
this.onKeyPress = null;
@ -502,97 +447,94 @@ function Keyboard() {
{bounds: [[899, 355, 263, 67]], event: 'submit'}
];
for (var i = 0; i < keyProperties.length; i++) {
this.keys.push(new KeyboardKey(this, keyProperties[i]));
}
this.keyboardTextureLoaded = function() {
if (Overlays.isLoaded(tthis.background)) {
Script.clearInterval(tthis.keyboardTextureLoaded_timer);
for (var i = 0; i < keyProperties.length; i++) {
tthis.keys.push(new KeyboardKey(tthis, keyProperties[i]));
}
if (keyboard.onFullyLoaded != null) {
tthis.onFullyLoaded();
}
}
};
this.keyboardTextureLoaded_timer = Script.setInterval(this.keyboardTextureLoaded, 250);
}
});
function Cursor() {
Cursor = (function(params) {
if (params === undefined) {
params = {};
}
var tthis = this;
this.x = windowDimensions.x / 2;
this.y = windowDimensions.y / 2;
this.overlay = Overlays.addOverlay("image", {
x: this.x,
y: this.y,
width: CURSOR_WIDTH,
height: CURSOR_HEIGHT,
imageURL: CURSOR_URL,
alpha: 1
});
this.remove = function() {
Overlays.deleteOverlay(this.overlay);
this.initialize = function() {
this.visible = params.visible != undefined ? params.visible : true;
this.x = windowDimensions.x / 2;
this.y = windowDimensions.y / 2;
this.overlay = Overlays.addOverlay("image", {
x: this.x,
y: this.y,
width: CURSOR_WIDTH,
height: CURSOR_HEIGHT,
imageURL: CURSOR_URL,
alpha: 1,
visible: this.visible
});
this.remove = function() {
Overlays.deleteOverlay(this.overlay);
};
this.getPosition = function() {
return {x: tthis.getX(), y: tthis.getY()};
};
this.getX = function() {
return tthis.x;
};
this.getY = function() {
return tthis.y;
};
this.show = function() {
tthis.updateVisibility(true);
};
this.hide = function() {
tthis.updateVisibility(false);
};
this.toggle = function() {
tthis.updateVisibility(!tthis.visible);
};
this.updateVisibility = function(visible) {
tthis.visible = visible;
Overlays.editOverlay(this.overlay, { visible: tthis.visible });
};
this.onUpdate = null;
this.update = function() {
var newWindowDimensions = Controller.getViewportDimensions();
if (newWindowDimensions.x != windowDimensions.x || newWindowDimensions.y != windowDimensions.y) {
windowDimensions = newWindowDimensions;
keyboard.rescale();
Overlays.editOverlay(text, {
y: windowDimensions.y - keyboard.height() - 260,
width: windowDimensions.x
});
}
var editobject = {};
var hudLookatPosition = HMD.getHUDLookAtPosition2D();
if (hudLookatPosition === null) {
return;
}
if (tthis.x !== hudLookatPosition.x) {
tthis.x = hudLookatPosition.x;
editobject.x = tthis.x - (CURSOR_WIDTH / 2);
}
if (tthis.y !== hudLookatPosition.y) {
tthis.y = hudLookatPosition.y;
editobject.y = tthis.y - (CURSOR_HEIGHT / 2);
}
if (Object.keys(editobject).length > 0) {
Overlays.editOverlay(tthis.overlay, editobject);
if (tthis.onUpdate != null) {
tthis.onUpdate(tthis.getPosition());
}
}
};
Script.update.connect(this.update);
};
this.getPosition = function() {
return {x: tthis.getX(), y: tthis.getY()};
};
this.getX = function() {
return tthis.x;
};
this.getY = function() {
return tthis.y;
};
this.onUpdate = null;
this.update = function() {
var newWindowDimensions = Controller.getViewportDimensions();
if (newWindowDimensions.x != windowDimensions.x || newWindowDimensions.y != windowDimensions.y) {
windowDimensions = newWindowDimensions;
keyboard.rescale();
Overlays.editOverlay(text, {
y: windowDimensions.y - keyboard.height() - 260,
width: windowDimensions.x
});
}
var editobject = {};
if (MyAvatar.getHeadFinalYaw() <= VIEW_ANGLE_BY_TWO && MyAvatar.getHeadFinalYaw() >= -1 * VIEW_ANGLE_BY_TWO) {
angle = ((-1 * MyAvatar.getHeadFinalYaw()) + VIEW_ANGLE_BY_TWO) / VIEW_ANGLE;
tthis.x = angle * windowDimensions.x;
editobject.x = tthis.x - (CURSOR_WIDTH / 2);
}
if (MyAvatar.getHeadFinalPitch() <= VIEW_ANGLE_BY_TWO && MyAvatar.getHeadFinalPitch() >= -1 * VIEW_ANGLE_BY_TWO) {
angle = ((-1 * MyAvatar.getHeadFinalPitch()) + VIEW_ANGLE_BY_TWO) / VIEW_ANGLE;
tthis.y = angle * windowDimensions.y;
editobject.y = tthis.y - (CURSOR_HEIGHT / 2);
}
if (Object.keys(editobject).length > 0) {
Overlays.editOverlay(tthis.overlay, editobject);
if (tthis.onUpdate != null) {
tthis.onUpdate(tthis.getPosition());
}
}
};
Script.update.connect(this.update);
}
function keyPressEvent(event) {
if (event.key === SPACEBAR_CHARCODE) {
keyboard.pressFocussedKey();
}
}
function keyReleaseEvent(event) {
if (event.key === SPACEBAR_CHARCODE) {
keyboard.releaseKeys();
}
}
function scriptEnding() {
keyboard.remove();
cursor.remove();
Overlays.deleteOverlay(text);
Overlays.deleteOverlay(textSizeMeasureOverlay);
Controller.releaseKeyEvents({key: SPACEBAR_CHARCODE});
}
Controller.captureKeyEvents({key: SPACEBAR_CHARCODE});
Controller.keyPressEvent.connect(keyPressEvent);
Controller.keyReleaseEvent.connect(keyReleaseEvent);
Script.scriptEnding.connect(scriptEnding);
});

View file

@ -86,4 +86,3 @@ MyAvatar.bodyRoll = 0;
// would be nice to change to update
Script.update.connect(update);
Script.scriptEnding.connect(scriptEnding);

View file

@ -1,13 +1,13 @@
//
// notifications.js
// Version 0.801
// Created by Adrian
//
// notifications.js
// Version 0.801
// Created by Adrian
//
// Adrian McCarlie 8-10-14
// This script demonstrates on-screen overlay type notifications.
// Copyright 2014 High Fidelity, Inc.
//
//
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -20,29 +20,29 @@
// CTRL/m for mic mute and unmute.
// System generated notifications:
// Displays users online at startup.
// Displays users online at startup.
// If Screen is resized.
// Triggers notification if @MyUserName is mentioned in chat.
// Announces existing user logging out.
// Announces new user logging in.
// If mic is muted for any reason.
//
//
// To add a new System notification type:
//
// 1. Set the Event Connector at the bottom of the script.
// example:
// 1. Set the Event Connector at the bottom of the script.
// example:
// GlobalServices.incomingMessage.connect(onIncomingMessage);
//
// 2. Create a new function to produce a text string, do not include new line returns.
// 2. Create a new function to produce a text string, do not include new line returns.
// example:
// function onIncomingMessage(user, message) {
// //do stuff here;
// //do stuff here;
// var text = "This is a notification";
// wordWrap(text);
// }
//
// This new function must call wordWrap(text) if the length of message is longer than 42 chars or unknown.
// wordWrap() will format the text to fit the notifications overlay and send it to createNotification(text).
// wordWrap() will format the text to fit the notifications overlay and send it to createNotification(text).
// If the message is 42 chars or less you should bypass wordWrap() and call createNotification() directly.
@ -57,6 +57,8 @@
// var welcome = "There are " + GlobalServices.onlineUsers.length + " users online now.";
// createNotification(welcome);
// }
Script.include("./libraries/globals.js");
Script.include("./libraries/soundArray.js");
var width = 340.0; //width of notification overlay
var windowDimensions = Controller.getViewportDimensions(); // get the size of the interface window
@ -66,7 +68,7 @@ var locationY = 20.0; // position down from top of interface window
var topMargin = 13.0;
var leftMargin = 10.0;
var textColor = { red: 228, green: 228, blue: 228}; // text color
var backColor = { red: 2, green: 2, blue: 2}; // background color was 38,38,38
var backColor = { red: 2, green: 2, blue: 2}; // background color was 38,38,38
var backgroundAlpha = 0;
var fontSize = 12.0;
var PERSIST_TIME_2D = 10.0; // Time in seconds before notification fades
@ -81,6 +83,22 @@ var last_users = GlobalServices.onlineUsers;
var users = [];
var ctrlIsPressed = false;
var ready = true;
var randomSounds = new SoundArray({ localOnly: true }, true);
var numberOfSounds = 2;
for (var i = 1; i <= numberOfSounds; i++) {
randomSounds.addSound(HIFI_PUBLIC_BUCKET + "sounds/UI/notification-general" + i + ".raw");
}
// When our script shuts down, we should clean up all of our overlays
function scriptEnding() {
for (i = 0; i < notifications.length; i++) {
Overlays.deleteOverlay(notifications[i]);
Overlays.deleteOverlay(buttons[i]);
}
}
Script.scriptEnding.connect(scriptEnding);
var notifications = [];
var buttons = [];
var times = [];
@ -193,6 +211,8 @@ function notify(notice, button, height) {
positions,
last;
randomSounds.playRandom();
if (isOnHMD) {
// Calculate 3D values from 2D overlay properties.
@ -454,7 +474,6 @@ function onOnlineUsersChanged(users) {
if (last_users.indexOf(users[i]) === -1.0) {
createNotification(users[i] + " has joined");
}
}
for (i = 0; i < last_users.length; i += 1) {

View file

@ -13,9 +13,13 @@
(function () {
var progress = 100, // %
var rawProgress = 100, // % raw value.
displayProgress = 100, // % smoothed value to display.
DISPLAY_PROGRESS_MINOR_MAXIMUM = 8, // % displayed progress bar goes up to while 0% raw progress.
DISPLAY_PROGRESS_MINOR_INCREMENT = 0.1, // % amount to increment display value each update when 0% raw progress.
DISPLAY_PROGRESS_MAJOR_INCREMENT = 5, // % maximum amount to increment display value when >0% raw progress.
alpha = 0.0,
alphaDelta = 0.0, // > 0 if fading in; < 0 if fading out/
alphaDelta = 0.0, // > 0 if fading in; < 0 if fading out.
ALPHA_DELTA_IN = 0.15,
ALPHA_DELTA_OUT = -0.02,
fadeTimer = null,
@ -23,18 +27,18 @@
fadeWaitTimer = null,
FADE_OUT_WAIT = 1000, // Wait before starting to fade out after progress 100%.
visible = false,
BAR_WIDTH = 320, // Nominal dimension of SVG in pixels of visible portion (half) of the bar.
BAR_HEIGHT = 20,
BAR_WIDTH = 480, // Dimension of SVG in pixels of visible portion (half) of the bar.
BAR_HEIGHT = 30,
BAR_URL = "http://hifi-public.s3.amazonaws.com/images/progress-bar.svg",
BACKGROUND_WIDTH = 360,
BACKGROUND_HEIGHT = 60,
BACKGROUND_WIDTH = 540,
BACKGROUND_HEIGHT = 90,
BACKGROUND_URL = "http://hifi-public.s3.amazonaws.com/images/progress-bar-background.svg",
isOnHMD = false,
windowWidth = 0,
windowHeight = 0,
background2D = {},
bar2D = {},
SCALE_2D = 0.55, // Scale the SVGs for 2D display.
SCALE_2D = 0.35, // Scale the SVGs for 2D display.
background3D = {},
bar3D = {},
ENABLE_VR_MODE_MENU_ITEM = "Enable VR Mode",
@ -43,7 +47,7 @@
PROGRESS_3D_ELEVATION = -0.8, // Height of top middle of top notification relative to avatar eyes.
PROGRESS_3D_YAW = 0.0, // Degrees relative to notifications direction.
PROGRESS_3D_PITCH = -60.0, // Degrees from vertical.
SCALE_3D = 0.0017, // Scale the bar SVG for 3D display.
SCALE_3D = 0.0011, // Scale the bar SVG for 3D display.
BACKGROUND_3D_SIZE = { x: 0.76, y: 0.08 }, // Match up with the 3D background with those of notifications.js notices.
BACKGROUND_3D_COLOR = { red: 2, green: 2, blue: 2 },
BACKGROUND_3D_ALPHA = 0.7;
@ -89,56 +93,15 @@
function onDownloadInfoChanged(info) {
var i;
// Calculate progress
// Update raw progress value
if (info.downloading.length + info.pending === 0) {
progress = 100;
rawProgress = 100;
} else {
progress = 0;
rawProgress = 0;
for (i = 0; i < info.downloading.length; i += 1) {
progress += info.downloading[i];
rawProgress += info.downloading[i];
}
progress = progress / (info.downloading.length + info.pending);
}
// Update state
if (!visible) { // Not visible because no recent downloads
if (progress < 100) { // Have started downloading so fade in
visible = true;
alphaDelta = ALPHA_DELTA_IN;
fadeTimer = Script.setInterval(fade, FADE_INTERVAL);
}
} else if (alphaDelta !== 0.0) { // Fading in or out
if (alphaDelta > 0) {
if (progress === 100) { // Was donloading but now have finished so fade out
alphaDelta = ALPHA_DELTA_OUT;
}
} else {
if (progress < 100) { // Was finished downloading but have resumed so fade in
alphaDelta = ALPHA_DELTA_IN;
}
}
} else { // Fully visible because downloading or recently so
if (fadeWaitTimer === null) {
if (progress === 100) { // Was downloading but have finished so fade out soon
fadeWaitTimer = Script.setTimeout(function () {
alphaDelta = ALPHA_DELTA_OUT;
fadeTimer = Script.setInterval(fade, FADE_INTERVAL);
fadeWaitTimer = null;
}, FADE_OUT_WAIT);
}
} else {
if (progress < 100) { // Was finished and waiting to fade out but have resumed downloading so don't fade out
Script.clearInterval(fadeWaitTimer);
fadeWaitTimer = null;
}
}
}
// Update progress bar
if (visible) {
Overlays.editOverlay(isOnHMD ? bar3D.overlay : bar2D.overlay, {
subImage: { x: BAR_WIDTH * (1 - progress / 100), y: 0, width: BAR_WIDTH, height: BAR_HEIGHT }
});
rawProgress = rawProgress / (info.downloading.length + info.pending);
}
}
@ -200,7 +163,58 @@
createOverlays();
}
// Calculate progress value to display
if (rawProgress === 0 && displayProgress <= DISPLAY_PROGRESS_MINOR_MAXIMUM) {
displayProgress = Math.min(displayProgress + DISPLAY_PROGRESS_MINOR_INCREMENT, DISPLAY_PROGRESS_MINOR_MAXIMUM);
} else if (rawProgress < displayProgress) {
displayProgress = rawProgress;
} else if (rawProgress > displayProgress) {
displayProgress = Math.min(rawProgress, displayProgress + DISPLAY_PROGRESS_MAJOR_INCREMENT);
} // else (rawProgress === displayProgress); do nothing.
// Update state
if (!visible) { // Not visible because no recent downloads
if (displayProgress < 100) { // Have started downloading so fade in
visible = true;
alphaDelta = ALPHA_DELTA_IN;
fadeTimer = Script.setInterval(fade, FADE_INTERVAL);
}
} else if (alphaDelta !== 0.0) { // Fading in or out
if (alphaDelta > 0) {
if (displayProgress === 100) { // Was downloading but now have finished so fade out
alphaDelta = ALPHA_DELTA_OUT;
}
} else {
if (displayProgress < 100) { // Was finished downloading but have resumed so fade in
alphaDelta = ALPHA_DELTA_IN;
}
}
} else { // Fully visible because downloading or recently so
if (fadeWaitTimer === null) {
if (displayProgress === 100) { // Was downloading but have finished so fade out soon
fadeWaitTimer = Script.setTimeout(function () {
alphaDelta = ALPHA_DELTA_OUT;
fadeTimer = Script.setInterval(fade, FADE_INTERVAL);
fadeWaitTimer = null;
}, FADE_OUT_WAIT);
}
} else {
if (displayProgress < 100) { // Was finished and waiting to fade out but have resumed so don't fade out
Script.clearInterval(fadeWaitTimer);
fadeWaitTimer = null;
}
}
}
if (visible) {
// Update progress bar
Overlays.editOverlay(isOnHMD ? bar3D.overlay : bar2D.overlay, {
visible: visible,
subImage: { x: BAR_WIDTH * (1 - displayProgress / 100), y: 0, width: BAR_WIDTH, height: BAR_HEIGHT }
});
// Update position
if (isOnHMD) {
// Update 3D overlays to maintain positions relative to avatar
eyePosition = MyAvatar.getDefaultEyePosition();

View file

@ -24,7 +24,9 @@ endif ()
include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
include_glm()
add_dependency_external_project(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PRIVATE ${GLM_INCLUDE_DIRS})
link_hifi_libraries(shared networking audio-client avatars)
include_dependency_includes()

View file

@ -32,8 +32,6 @@ elseif (WIN32)
set(GL_HEADERS "#include <windowshacks.h>\n#include <GL/glew.h>\n#include <GL/wglew.h>")
endif ()
# set up the external glm library
include_glm()
include_bullet()
# create the InterfaceConfig.h file based on GL_HEADERS above
@ -109,6 +107,11 @@ endif()
# create the executable, make it a bundle on OS X
add_executable(${TARGET_NAME} MACOSX_BUNDLE ${INTERFACE_SRCS} ${QM})
# set up the external glm library
add_dependency_external_project(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PRIVATE ${GLM_INCLUDE_DIRS})
# link required hifi libraries
link_hifi_libraries(shared octree environment gpu model fbx metavoxels networking entities avatars
audio audio-client animation script-engine physics

View file

@ -13,9 +13,14 @@ You may optionally choose to place this folder in a location outside the reposit
If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder faceshift that contains the lib and include folders.
1. Build a Faceshift static library from the fsbinarystream.cpp file. If you build a release version call it libfaceshift.a. The debug version should be called libfaceshiftd.a. Place this in the lib folder in your Faceshift folder.
1. Build a Faceshift static library from the fsbinarystream.cpp file.
Windows: Win32 console application; no precompiled header or SDL checks; no ATL or MFC headers; Project Properties, Configuration Type = Static Library (.lib).
2. Copy the fsbinarystream.h header file from the Faceshift SDK into the include folder in your Faceshift folder.
2. Copy the library files to the lib folder in your Faceshift folder.
OSX: If you build a release version call it libfaceshift.a. The debug version should be called libfaceshiftd.a.
Windows: The release and debug versions should be called faceshift.lib and faceshiftd.lib, respectively. Copy them into a Win32 folder in your lib folder.
3. Clear your build directory, run cmake and build, and you should be all set.
3. Copy the fsbinarystream.h header file from the Faceshift SDK into the include folder in your Faceshift folder.
4. Clear your build directory, run cmake and build, and you should be all set.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -1,628 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div style="width: 839px; margin: 0 auto;">
<input type="hidden" id="version" value="1" />
<svg version="1.1" id="Layer_1" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="-32 22 839 765"
enable-background="new -32 22 839 765" xml:space="preserve" width="839px" height="765px">
<title>Welcome to Interface</title>
<description>Created with Sketch (http://www.bohemiancoding.com/sketch)</description>
<g>
<g enable-background="new ">
<path fill="#0E7077" d="M115.6,66l3.4,14.8l0.7,4.1l0.7-4l2.9-14.9h5.7l3.1,14.8L133,85l0.8-4l3.5-15h5.5l-7.3,25.9h-5.2
l-3.1-15.2l-0.9-5l-0.9,5l-3.1,15.2h-5L109.8,66H115.6z"/>
<path fill="#0E7077" d="M159.4,72.9c1,0.4,1.8,1,2.4,1.9c0.5,0.7,0.8,1.5,1,2.2c0.1,0.8,0.2,2,0.2,3.7v11.3h-5.1V80.2
c0-1-0.2-1.9-0.5-2.5c-0.5-0.9-1.3-1.3-2.6-1.3c-1.3,0-2.3,0.4-3,1.3s-1,2.1-1,3.8v10.4h-5V66.1h5v9.1c0.7-1.1,1.6-1.9,2.5-2.3
c1-0.4,2-0.7,3-0.7C157.4,72.2,158.4,72.4,159.4,72.9z"/>
<path fill="#0E7077" d="M175.2,80.3c0.9-0.1,1.6-0.3,2-0.4c0.7-0.3,1.1-0.8,1.1-1.4c0-0.8-0.3-1.3-0.8-1.6s-1.4-0.4-2.4-0.4
c-1.2,0-2,0.3-2.5,0.9c-0.4,0.4-0.6,1-0.7,1.8h-4.8c0.1-1.7,0.6-3.1,1.4-4.1c1.3-1.7,3.6-2.5,6.9-2.5c2.1,0,4,0.4,5.6,1.2
c1.6,0.8,2.5,2.4,2.5,4.7V87c0,0.6,0,1.3,0,2.2c0,0.7,0.1,1.1,0.3,1.3s0.4,0.4,0.7,0.6v0.7h-5.4c-0.2-0.4-0.3-0.8-0.3-1.1
s-0.1-0.7-0.1-1.2c-0.7,0.8-1.5,1.4-2.4,1.9c-1.1,0.6-2.3,0.9-3.7,0.9c-1.7,0-3.2-0.5-4.3-1.5s-1.7-2.4-1.7-4.2
c0-2.4,0.9-4.1,2.7-5.1c1-0.6,2.5-1,4.4-1.2L175.2,80.3z M178.3,82.6c-0.3,0.2-0.6,0.4-1,0.5s-0.8,0.2-1.3,0.3l-1.1,0.2
c-1.1,0.2-1.8,0.4-2.3,0.7c-0.8,0.5-1.2,1.2-1.2,2.1c0,0.9,0.2,1.5,0.7,1.9s1.1,0.6,1.8,0.6c1.1,0,2.1-0.3,3-0.9s1.4-1.8,1.4-3.5
V82.6z"/>
<path fill="#0E7077" d="M185.7,76.5v-3.6h2.7v-5.3h5v5.3h3.1v3.6h-3.1v10.1c0,0.8,0.1,1.3,0.3,1.5s0.8,0.3,1.8,0.3
c0.2,0,0.3,0,0.5,0s0.3,0,0.5,0v3.7l-2.4,0.1c-2.4,0.1-4-0.3-4.9-1.2c-0.6-0.6-0.8-1.5-0.8-2.7V76.5H185.7z"/>
<path fill="#0E7077" d="M210.4,95.6l0.6,0c0.5,0,1,0,1.4-0.1s0.8-0.2,1.1-0.4c0.3-0.2,0.6-0.6,0.8-1.2s0.4-1,0.3-1.2l-7-20h5.6
l4.2,14.1l4-14.1h5.3l-6.6,18.8c-1.3,3.6-2.3,5.9-3,6.8c-0.7,0.9-2.2,1.3-4.4,1.3c-0.4,0-0.8,0-1.1,0c-0.3,0-0.7,0-1.2-0.1V95.6z"
/>
<path fill="#0E7077" d="M245.6,89.6c-1.6,2-4.1,3-7.4,3s-5.7-1-7.4-3s-2.4-4.4-2.4-7.2c0-2.8,0.8-5.2,2.4-7.2c1.6-2,4.1-3,7.4-3
s5.7,1,7.4,3c1.6,2,2.4,4.4,2.4,7.2C248.1,85.2,247.3,87.6,245.6,89.6z M241.6,86.8c0.8-1,1.2-2.5,1.2-4.4s-0.4-3.4-1.2-4.4
c-0.8-1-1.9-1.6-3.4-1.6s-2.6,0.5-3.4,1.6c-0.8,1-1.2,2.5-1.2,4.4s0.4,3.4,1.2,4.4c0.8,1,1.9,1.6,3.4,1.6S240.8,87.8,241.6,86.8z"
/>
<path fill="#0E7077" d="M256.6,72.7v11.5c0,1.1,0.1,1.9,0.4,2.5c0.5,1,1.3,1.5,2.7,1.5c1.7,0,2.9-0.7,3.5-2.1
c0.3-0.8,0.5-1.7,0.5-3V72.7h5.1v19.2h-4.9v-2.7c0,0.1-0.2,0.2-0.4,0.5s-0.4,0.6-0.7,0.8c-0.8,0.7-1.5,1.2-2.3,1.4
s-1.6,0.4-2.6,0.4c-2.8,0-4.7-1-5.7-3.1c-0.5-1.1-0.8-2.8-0.8-5V72.7H256.6z"/>
<path fill="#0E7077" d="M295.1,79.6c-0.1-0.7-0.3-1.4-0.7-1.9c-0.6-0.8-1.4-1.2-2.6-1.2c-1.7,0-2.8,0.8-3.5,2.5
c-0.3,0.9-0.5,2.1-0.5,3.5c0,1.4,0.2,2.5,0.5,3.4c0.6,1.6,1.7,2.4,3.4,2.4c1.2,0,2-0.3,2.5-0.9c0.5-0.6,0.8-1.5,0.9-2.5h5.1
c-0.1,1.5-0.7,3-1.7,4.3c-1.6,2.2-3.9,3.3-7,3.3c-3.1,0-5.4-0.9-6.8-2.7c-1.5-1.8-2.2-4.2-2.2-7.1c0-3.3,0.8-5.8,2.4-7.7
c1.6-1.8,3.8-2.7,6.7-2.7c2.4,0,4.4,0.5,5.9,1.6s2.4,3,2.7,5.7H295.1z"/>
<path fill="#0E7077" d="M311.2,80.3c0.9-0.1,1.6-0.3,2-0.4c0.7-0.3,1.1-0.8,1.1-1.4c0-0.8-0.3-1.3-0.8-1.6s-1.4-0.4-2.4-0.4
c-1.2,0-2,0.3-2.5,0.9c-0.4,0.4-0.6,1-0.7,1.8H303c0.1-1.7,0.6-3.1,1.4-4.1c1.3-1.7,3.6-2.5,6.9-2.5c2.1,0,4,0.4,5.6,1.2
c1.6,0.8,2.5,2.4,2.5,4.7V87c0,0.6,0,1.3,0,2.2c0,0.7,0.1,1.1,0.3,1.3s0.4,0.4,0.7,0.6v0.7h-5.4c-0.2-0.4-0.3-0.8-0.3-1.1
s-0.1-0.7-0.1-1.2c-0.7,0.8-1.5,1.4-2.4,1.9c-1.1,0.6-2.3,0.9-3.7,0.9c-1.7,0-3.2-0.5-4.3-1.5s-1.7-2.4-1.7-4.2
c0-2.4,0.9-4.1,2.7-5.1c1-0.6,2.5-1,4.4-1.2L311.2,80.3z M314.3,82.6c-0.3,0.2-0.6,0.4-1,0.5s-0.8,0.2-1.3,0.3l-1.1,0.2
c-1.1,0.2-1.8,0.4-2.3,0.7c-0.8,0.5-1.2,1.2-1.2,2.1c0,0.9,0.2,1.5,0.7,1.9s1.1,0.6,1.8,0.6c1.1,0,2.1-0.3,3-0.9s1.4-1.8,1.4-3.5
V82.6z"/>
<path fill="#0E7077" d="M339.1,73.8c1.3,1,1.9,2.8,1.9,5.2v12.9h-5.1V80.3c0-1-0.1-1.8-0.4-2.3c-0.5-1-1.4-1.5-2.8-1.5
c-1.7,0-2.8,0.7-3.5,2.2c-0.3,0.8-0.5,1.7-0.5,2.9v10.4h-5V72.8h4.9v2.8c0.6-1,1.2-1.7,1.8-2.1c1-0.8,2.3-1.2,3.9-1.2
C336.3,72.3,337.9,72.8,339.1,73.8z"/>
<path fill="#0E7077" d="M365.8,73.1c0.9,0.5,1.6,1.2,2.2,2.1V66h5.1v25.9h-4.9v-2.7c-0.7,1.1-1.5,2-2.4,2.5s-2.1,0.8-3.4,0.8
c-2.2,0-4.1-0.9-5.7-2.7s-2.3-4.1-2.3-7c0-3.3,0.8-5.8,2.3-7.7s3.5-2.8,6-2.8C363.9,72.3,364.9,72.5,365.8,73.1z M367.1,86.7
c0.7-1.1,1.1-2.4,1.1-4.1c0-2.3-0.6-4-1.8-5c-0.7-0.6-1.6-0.9-2.5-0.9c-1.5,0-2.5,0.6-3.2,1.7s-1,2.5-1,4.1c0,1.8,0.3,3.2,1,4.2
s1.8,1.6,3.2,1.6S366.3,87.8,367.1,86.7z"/>
<path fill="#0E7077" d="M393.6,89.6c-1.6,2-4.1,3-7.4,3s-5.7-1-7.4-3s-2.4-4.4-2.4-7.2c0-2.8,0.8-5.2,2.4-7.2c1.6-2,4.1-3,7.4-3
s5.7,1,7.4,3c1.6,2,2.4,4.4,2.4,7.2C396.1,85.2,395.3,87.6,393.6,89.6z M389.6,86.8c0.8-1,1.2-2.5,1.2-4.4s-0.4-3.4-1.2-4.4
c-0.8-1-1.9-1.6-3.4-1.6s-2.6,0.5-3.4,1.6c-0.8,1-1.2,2.5-1.2,4.4s0.4,3.4,1.2,4.4c0.8,1,1.9,1.6,3.4,1.6S388.8,87.8,389.6,86.8z"
/>
<path fill="#0E7077" d="M418.7,72.7h5.1l2.9,13.8l3-13.8h5.2l-5.6,19.2h-5.2l-3-14l-3,14h-5.2l-5.4-19.2h5.4l3,13.7L418.7,72.7z"
/>
<path fill="#0E7077" d="M442.8,70.5h-5.1v-4.6h5.1V70.5z M437.7,72.7h5.1v19.2h-5.1V72.7z"/>
<path fill="#0E7077" d="M445.7,76.5v-3.6h2.7v-5.3h5v5.3h3.1v3.6h-3.1v10.1c0,0.8,0.1,1.3,0.3,1.5s0.8,0.3,1.8,0.3
c0.2,0,0.3,0,0.5,0s0.3,0,0.5,0v3.7l-2.4,0.1c-2.4,0.1-4-0.3-4.9-1.2c-0.6-0.6-0.8-1.5-0.8-2.7V76.5H445.7z"/>
<path fill="#0E7077" d="M473.4,72.9c1,0.4,1.8,1,2.4,1.9c0.5,0.7,0.8,1.5,1,2.2c0.1,0.8,0.2,2,0.2,3.7v11.3h-5.1V80.2
c0-1-0.2-1.9-0.5-2.5c-0.5-0.9-1.3-1.3-2.6-1.3c-1.3,0-2.3,0.4-3,1.3s-1,2.1-1,3.8v10.4h-5V66.1h5v9.1c0.7-1.1,1.6-1.9,2.5-2.3
s2-0.7,3-0.7C471.4,72.2,472.5,72.4,473.4,72.9z"/>
<path fill="#0E7077" d="M492,91.9V66h5.4v9.9h10.1V66h5.4v25.9h-5.4V80.3h-10.1v11.6H492z"/>
<path fill="#0E7077" d="M522.8,70.5h-5.1v-4.6h5.1V70.5z M517.7,72.7h5.1v19.2h-5.1V72.7z"/>
<path fill="#0E7077" d="M535.6,65.8c0.3,0,0.6,0,1.1,0.1v4.1c-0.3,0-0.8-0.1-1.4-0.1s-1.1,0.1-1.4,0.4s-0.4,0.7-0.4,1s0,0.9,0,1.6
h3.3v3.5h-3.3v15.5h-5V76.5h-2.8v-3.5h2.7v-1.2c0-2.1,0.3-3.5,1-4.2c0.7-1.1,2.5-1.7,5.3-1.7C535,65.7,535.3,65.7,535.6,65.8z"/>
<path fill="#0E7077" d="M544.8,70.5h-5.1v-4.6h5.1V70.5z M539.7,72.7h5.1v19.2h-5.1V72.7z"/>
<path fill="#0E7077" d="M563.5,85.8c0.1,0.9,0.3,1.5,0.7,1.9c0.6,0.7,1.8,1,3.5,1c1,0,1.8-0.1,2.4-0.4s0.9-0.7,0.9-1.3
c0-0.6-0.2-1-0.7-1.3s-2.2-0.8-5.2-1.5c-2.2-0.5-3.7-1.2-4.6-2c-0.9-0.8-1.3-1.9-1.3-3.4c0-1.8,0.7-3.3,2.1-4.6s3.3-1.9,5.9-1.9
c2.4,0,4.3,0.5,5.9,1.4c1.5,1,2.4,2.6,2.6,4.9h-5c-0.1-0.6-0.3-1.2-0.5-1.5c-0.6-0.7-1.5-1-2.8-1c-1.1,0-1.9,0.2-2.3,0.5
c-0.5,0.3-0.7,0.7-0.7,1.2c0,0.6,0.2,1,0.7,1.2c0.5,0.3,2.2,0.7,5.2,1.4c2,0.5,3.5,1.2,4.5,2.1c1,1,1.5,2.2,1.5,3.6
c0,1.9-0.7,3.4-2.1,4.6s-3.6,1.8-6.6,1.8c-3,0-5.2-0.6-6.7-1.9s-2.2-2.9-2.2-4.9H563.5z"/>
<path fill="#0E7077" d="M595.6,89.6c-1.6,2-4.1,3-7.4,3s-5.7-1-7.4-3s-2.4-4.4-2.4-7.2c0-2.8,0.8-5.2,2.4-7.2c1.6-2,4.1-3,7.4-3
s5.7,1,7.4,3c1.6,2,2.4,4.4,2.4,7.2C598.1,85.2,597.2,87.6,595.6,89.6z M591.6,86.8c0.8-1,1.2-2.5,1.2-4.4s-0.4-3.4-1.2-4.4
c-0.8-1-1.9-1.6-3.4-1.6s-2.6,0.5-3.4,1.6c-0.8,1-1.2,2.5-1.2,4.4s0.4,3.4,1.2,4.4c0.8,1,1.9,1.6,3.4,1.6S590.8,87.8,591.6,86.8z"
/>
<path fill="#0E7077" d="M619.6,65.8c0.3,0,0.6,0,1.1,0.1v4.1c-0.3,0-0.8-0.1-1.4-0.1s-1.1,0.1-1.4,0.4s-0.4,0.7-0.4,1s0,0.9,0,1.6
h3.3v3.5h-3.3v15.5h-5V76.5h-2.8v-3.5h2.7v-1.2c0-2.1,0.3-3.5,1-4.2c0.7-1.1,2.5-1.7,5.3-1.7C619,65.7,619.3,65.7,619.6,65.8z"/>
<path fill="#0E7077" d="M631.2,80.3c0.9-0.1,1.6-0.3,2-0.4c0.7-0.3,1.1-0.8,1.1-1.4c0-0.8-0.3-1.3-0.8-1.6
c-0.6-0.3-1.4-0.4-2.4-0.4c-1.2,0-2,0.3-2.5,0.9c-0.4,0.4-0.6,1-0.7,1.8h-4.8c0.1-1.7,0.6-3.1,1.4-4.1c1.3-1.7,3.6-2.5,6.9-2.5
c2.1,0,4,0.4,5.6,1.2c1.6,0.8,2.5,2.4,2.5,4.7V87c0,0.6,0,1.3,0,2.2c0,0.7,0.1,1.1,0.3,1.3s0.4,0.4,0.7,0.6v0.7h-5.4
c-0.2-0.4-0.3-0.8-0.3-1.1s-0.1-0.7-0.1-1.2c-0.7,0.8-1.5,1.4-2.4,1.9c-1.1,0.6-2.3,0.9-3.7,0.9c-1.7,0-3.2-0.5-4.3-1.5
s-1.7-2.4-1.7-4.2c0-2.4,0.9-4.1,2.7-5.1c1-0.6,2.5-1,4.4-1.2L631.2,80.3z M634.3,82.6c-0.3,0.2-0.6,0.4-1,0.5
c-0.3,0.1-0.8,0.2-1.3,0.3l-1.1,0.2c-1.1,0.2-1.8,0.4-2.3,0.7c-0.8,0.5-1.2,1.2-1.2,2.1c0,0.9,0.2,1.5,0.7,1.9
c0.5,0.4,1.1,0.6,1.8,0.6c1.1,0,2.1-0.3,3-0.9c0.9-0.6,1.4-1.8,1.4-3.5V82.6z"/>
<path fill="#0E7077" d="M654.3,72.3c0.1,0,0.2,0,0.4,0v5.1c-0.3,0-0.6-0.1-0.8-0.1s-0.4,0-0.6,0c-2,0-3.4,0.7-4.1,2
c-0.4,0.7-0.6,1.9-0.6,3.4v9.2h-5V72.7h4.8v3.3c0.8-1.3,1.4-2.2,2-2.6c0.9-0.8,2.2-1.2,3.7-1.2C654.2,72.3,654.2,72.3,654.3,72.3z
"/>
<path fill="#0E7077" d="M659.4,73.5h5.3v5.3h-5.3V73.5z M659.4,86.7h5.3v5.3h-5.3V86.7z"/>
</g>
</g>
<g enable-background="new ">
<path fill="#0E7077" d="M549.2,149.4h3.9v15.3h9.3v3.4h-13.3V149.4z"/>
<path fill="#0E7077" d="M568.5,152.6h-3.7v-3.3h3.7V152.6z M564.8,154.3h3.7v13.8h-3.7V154.3z"/>
<path fill="#0E7077" d="M574.8,163.7c0.1,0.6,0.2,1.1,0.5,1.4c0.5,0.5,1.3,0.7,2.5,0.7c0.7,0,1.3-0.1,1.7-0.3
c0.4-0.2,0.6-0.5,0.6-1c0-0.4-0.2-0.7-0.5-0.9s-1.6-0.6-3.8-1.1c-1.6-0.4-2.7-0.9-3.3-1.5c-0.6-0.6-1-1.4-1-2.5
c0-1.3,0.5-2.4,1.5-3.3c1-0.9,2.4-1.4,4.2-1.4c1.7,0,3.1,0.3,4.2,1c1.1,0.7,1.7,1.9,1.9,3.6h-3.6c-0.1-0.5-0.2-0.8-0.4-1.1
c-0.4-0.5-1.1-0.7-2-0.7c-0.8,0-1.4,0.1-1.7,0.4c-0.3,0.2-0.5,0.5-0.5,0.9c0,0.4,0.2,0.7,0.5,0.9c0.4,0.2,1.6,0.5,3.8,1
c1.4,0.3,2.5,0.9,3.2,1.5c0.7,0.7,1.1,1.6,1.1,2.6c0,1.4-0.5,2.5-1.5,3.4c-1,0.9-2.6,1.3-4.7,1.3c-2.2,0-3.8-0.5-4.8-1.4
c-1-0.9-1.6-2.1-1.6-3.5H574.8z"/>
<path fill="#0E7077" d="M585,157v-2.6h1.9v-3.9h3.6v3.9h2.2v2.6h-2.2v7.3c0,0.6,0.1,0.9,0.2,1.1c0.1,0.1,0.6,0.2,1.3,0.2
c0.1,0,0.2,0,0.3,0c0.1,0,0.2,0,0.4,0v2.7l-1.7,0.1c-1.7,0.1-2.9-0.2-3.5-0.9c-0.4-0.4-0.6-1.1-0.6-1.9V157H585z"/>
<path fill="#0E7077" d="M604.1,154.5c1,0.4,1.8,1.1,2.4,2c0.6,0.8,0.9,1.8,1.1,2.9c0.1,0.6,0.1,1.6,0.1,2.7h-10.1
c0.1,1.4,0.5,2.4,1.4,2.9c0.6,0.3,1.2,0.5,2,0.5c0.8,0,1.5-0.2,2-0.6c0.3-0.2,0.5-0.5,0.7-1h3.7c-0.1,0.8-0.5,1.7-1.3,2.5
c-1.2,1.3-3,2-5.2,2c-1.8,0-3.5-0.6-4.9-1.7c-1.4-1.1-2.1-3-2.1-5.5c0-2.4,0.6-4.2,1.9-5.5c1.3-1.3,2.9-1.9,4.9-1.9
C602.1,153.9,603.2,154.1,604.1,154.5z M598.7,157.7c-0.5,0.5-0.8,1.2-1,2.1h6.2c-0.1-1-0.4-1.7-1-2.2s-1.3-0.7-2.1-0.7
C599.9,156.9,599.2,157.1,598.7,157.7z"/>
<path fill="#0E7077" d="M620.7,155.1c0.9,0.8,1.4,2,1.4,3.7v9.3h-3.7v-8.4c0-0.7-0.1-1.3-0.3-1.7c-0.4-0.7-1-1.1-2-1.1
c-1.2,0-2.1,0.5-2.5,1.6c-0.2,0.5-0.4,1.3-0.4,2.1v7.5h-3.6v-13.8h3.5v2c0.5-0.7,0.9-1.2,1.3-1.5c0.7-0.6,1.7-0.8,2.8-0.8
C618.6,153.9,619.8,154.3,620.7,155.1z"/>
<path fill="#0E7077" d="M633.2,160.3c0.6-0.8,1.5-1.6,2.7-2.4l0.4-0.2c-0.5-0.6-1-1.2-1.3-1.9s-0.4-1.3-0.4-2
c0-1.4,0.5-2.4,1.4-3.2c0.9-0.8,2.1-1.2,3.6-1.2c1.3,0,2.5,0.4,3.3,1.2c0.9,0.8,1.3,1.8,1.3,3c0,1.1-0.3,2-0.8,2.7
c-0.5,0.7-1.3,1.3-2.3,1.9l2.7,3.3c0.3-0.4,0.5-0.9,0.7-1.4c0.2-0.5,0.3-1.1,0.3-1.6h3.2c-0.1,1.1-0.3,2.3-0.8,3.5
c-0.3,0.7-0.7,1.4-1.3,2.2l3.4,4.1h-4.4l-1.3-1.6c-0.6,0.6-1.2,1-1.8,1.3c-1,0.5-2.1,0.8-3.4,0.8c-1.9,0-3.4-0.6-4.5-1.7
c-1.1-1.1-1.6-2.4-1.6-3.7C632.4,162,632.7,161.1,633.2,160.3z M636.8,164.8c0.5,0.5,1.2,0.8,2,0.8c0.6,0,1.2-0.1,1.7-0.4
c0.5-0.3,1-0.6,1.3-0.9l-3.5-4.3c-0.9,0.6-1.5,1.1-1.8,1.6c-0.3,0.5-0.4,1-0.4,1.5C636,163.7,636.2,164.2,636.8,164.8z
M638.4,154.7c0.2,0.2,0.5,0.7,1,1.3c0.5-0.4,0.9-0.7,1.1-0.9c0.4-0.5,0.7-1,0.7-1.5c0-0.4-0.1-0.7-0.4-1s-0.7-0.5-1.2-0.5
c-0.3,0-0.6,0.1-0.9,0.3c-0.4,0.3-0.7,0.7-0.7,1.2C638,154,638.1,154.4,638.4,154.7z"/>
<path fill="#0E7077" d="M657.2,157v-2.6h1.9v-3.9h3.6v3.9h2.2v2.6h-2.2v7.3c0,0.6,0.1,0.9,0.2,1.1c0.1,0.1,0.6,0.2,1.3,0.2
c0.1,0,0.2,0,0.3,0c0.1,0,0.2,0,0.4,0v2.7l-1.7,0.1c-1.7,0.1-2.9-0.2-3.5-0.9c-0.4-0.4-0.6-1.1-0.6-1.9V157H657.2z"/>
<path fill="#0E7077" d="M672.8,159.7c0.7-0.1,1.2-0.2,1.4-0.3c0.5-0.2,0.8-0.6,0.8-1c0-0.6-0.2-1-0.6-1.2c-0.4-0.2-1-0.3-1.8-0.3
c-0.9,0-1.5,0.2-1.8,0.6c-0.3,0.3-0.4,0.7-0.5,1.3h-3.5c0.1-1.2,0.4-2.2,1-3c1-1.2,2.6-1.8,4.9-1.8c1.5,0,2.9,0.3,4.1,0.9
s1.8,1.7,1.8,3.4v6.3c0,0.4,0,1,0,1.6c0,0.5,0.1,0.8,0.2,1s0.3,0.3,0.5,0.4v0.5h-3.9c-0.1-0.3-0.2-0.5-0.2-0.8s-0.1-0.5-0.1-0.8
c-0.5,0.5-1.1,1-1.7,1.4c-0.8,0.4-1.7,0.7-2.7,0.7c-1.3,0-2.3-0.4-3.1-1.1c-0.8-0.7-1.2-1.7-1.2-3c0-1.7,0.7-2.9,2-3.7
c0.7-0.4,1.8-0.7,3.2-0.9L672.8,159.7z M675,161.4c-0.2,0.1-0.5,0.3-0.7,0.3c-0.2,0.1-0.6,0.2-1,0.2l-0.8,0.2
c-0.8,0.1-1.3,0.3-1.7,0.5c-0.6,0.3-0.9,0.8-0.9,1.5c0,0.6,0.2,1.1,0.5,1.3c0.3,0.3,0.8,0.4,1.3,0.4c0.8,0,1.5-0.2,2.2-0.7
s1-1.3,1-2.5V161.4z"/>
<path fill="#0E7077" d="M685.5,168.1h-3.6v-18.7h3.6V168.1z"/>
<path fill="#0E7077" d="M689,149.5h3.6v10.1l4.6-5.2h4.5l-5,5.2l5.2,8.6h-4.4l-3.4-5.9l-1.5,1.6v4.4H689V149.5z"/>
</g>
<g enable-background="new ">
<path fill="#0E7077" d="M31.6,466.1c0.6,0.8,0.9,1.7,0.9,2.9c0,1.1-0.3,2.1-0.9,2.8c-0.3,0.4-0.8,0.7-1.4,1.1
c1,0.3,1.7,0.9,2.2,1.7s0.7,1.7,0.7,2.7c0,1.1-0.3,2.1-0.8,3c-0.4,0.6-0.8,1.1-1.3,1.5c-0.6,0.5-1.3,0.8-2.1,0.9
c-0.8,0.2-1.7,0.3-2.6,0.3h-8.4v-18.7h9C29.1,464.2,30.7,464.8,31.6,466.1z M21.5,467.4v4.1H26c0.8,0,1.5-0.2,2-0.5
c0.5-0.3,0.8-0.9,0.8-1.6c0-0.9-0.3-1.4-1-1.7c-0.6-0.2-1.3-0.3-2.2-0.3H21.5z M21.5,474.6v5H26c0.8,0,1.4-0.1,1.9-0.3
c0.8-0.4,1.2-1.2,1.2-2.3c0-1-0.4-1.6-1.2-2c-0.4-0.2-1.1-0.3-1.9-0.3H21.5z"/>
<path fill="#0E7077" d="M39.8,469v8.3c0,0.8,0.1,1.4,0.3,1.8c0.3,0.7,1,1.1,1.9,1.1c1.2,0,2.1-0.5,2.5-1.5c0.2-0.5,0.4-1.3,0.4-2.1
V469h3.7v13.8H45v-2c0,0-0.1,0.2-0.3,0.4c-0.1,0.2-0.3,0.4-0.5,0.6c-0.6,0.5-1.1,0.9-1.6,1c-0.5,0.2-1.1,0.3-1.9,0.3
c-2,0-3.4-0.7-4.1-2.2c-0.4-0.8-0.6-2-0.6-3.6V469H39.8z"/>
<path fill="#0E7077" d="M55.8,467.3h-3.7V464h3.7V467.3z M52.1,469h3.7v13.8h-3.7V469z"/>
<path fill="#0E7077" d="M63,482.8h-3.6v-18.7H63V482.8z"/>
<path fill="#0E7077" d="M73.8,469.2c0.7,0.4,1.2,0.9,1.6,1.5v-6.6h3.7v18.7h-3.5v-1.9c-0.5,0.8-1.1,1.4-1.8,1.8s-1.5,0.6-2.5,0.6
c-1.6,0-3-0.7-4.1-2c-1.1-1.3-1.7-3-1.7-5c0-2.4,0.5-4.2,1.6-5.6s2.5-2,4.4-2C72.4,468.7,73.2,468.8,73.8,469.2z M74.7,479.1
c0.5-0.8,0.8-1.7,0.8-3c0-1.7-0.4-2.9-1.3-3.6c-0.5-0.4-1.1-0.7-1.8-0.7c-1.1,0-1.8,0.4-2.3,1.2c-0.5,0.8-0.7,1.8-0.7,3
c0,1.3,0.3,2.3,0.8,3.1c0.5,0.8,1.3,1.1,2.3,1.1S74.2,479.8,74.7,479.1z"/>
<path fill="#0E7077" d="M92.4,478.4c0.1,0.6,0.2,1.1,0.5,1.4c0.5,0.5,1.3,0.7,2.5,0.7c0.7,0,1.3-0.1,1.7-0.3s0.6-0.5,0.6-1
c0-0.4-0.2-0.7-0.5-0.9c-0.3-0.2-1.6-0.6-3.8-1.1c-1.6-0.4-2.7-0.9-3.3-1.5c-0.6-0.6-1-1.4-1-2.5c0-1.3,0.5-2.4,1.5-3.3
c1-0.9,2.4-1.4,4.2-1.4c1.7,0,3.1,0.3,4.2,1c1.1,0.7,1.7,1.9,1.9,3.6h-3.6c-0.1-0.5-0.2-0.8-0.4-1.1c-0.4-0.5-1.1-0.7-2-0.7
c-0.8,0-1.4,0.1-1.7,0.4c-0.3,0.2-0.5,0.5-0.5,0.9c0,0.4,0.2,0.7,0.5,0.9c0.4,0.2,1.6,0.5,3.8,1c1.4,0.3,2.5,0.9,3.2,1.5
c0.7,0.7,1.1,1.6,1.1,2.6c0,1.4-0.5,2.5-1.5,3.4c-1,0.9-2.6,1.3-4.7,1.3c-2.2,0-3.8-0.5-4.8-1.4c-1-0.9-1.6-2.1-1.6-3.5H92.4z"/>
<path fill="#0E7077" d="M115.6,481.1c-1.2,1.4-2.9,2.2-5.3,2.2s-4.2-0.7-5.3-2.2c-1.2-1.4-1.8-3.2-1.8-5.2c0-2,0.6-3.7,1.8-5.2
s2.9-2.2,5.3-2.2s4.2,0.7,5.3,2.2s1.8,3.2,1.8,5.2C117.3,478,116.8,479.7,115.6,481.1z M112.7,479.1c0.6-0.8,0.9-1.8,0.9-3.2
c0-1.4-0.3-2.5-0.9-3.2s-1.4-1.1-2.4-1.1s-1.9,0.4-2.4,1.1s-0.9,1.8-0.9,3.2c0,1.4,0.3,2.5,0.9,3.2s1.4,1.1,2.4,1.1
S112.1,479.9,112.7,479.1z"/>
<path fill="#0E7077" d="M137.2,469c0.6,0.2,1.1,0.7,1.6,1.2c0.4,0.5,0.7,1.1,0.8,1.8c0.1,0.5,0.1,1.1,0.1,2l0,8.7H136V474
c0-0.5-0.1-1-0.3-1.3c-0.3-0.6-0.9-1-1.8-1c-1,0-1.7,0.4-2.1,1.2c-0.2,0.4-0.3,1-0.3,1.6v8.2H128v-8.2c0-0.8-0.1-1.4-0.3-1.8
c-0.3-0.7-0.9-1-1.8-1c-1,0-1.7,0.3-2.1,1c-0.2,0.4-0.3,0.9-0.3,1.7v8.3h-3.7V469h3.5v2c0.4-0.7,0.9-1.2,1.3-1.5
c0.7-0.5,1.6-0.8,2.7-0.8c1.1,0,1.9,0.2,2.6,0.7c0.5,0.4,0.9,1,1.2,1.7c0.5-0.8,1.1-1.4,1.8-1.8c0.7-0.4,1.6-0.6,2.5-0.6
C136,468.7,136.6,468.8,137.2,469z"/>
<path fill="#0E7077" d="M152.1,469.3c1,0.4,1.8,1.1,2.4,2c0.6,0.8,0.9,1.8,1.1,2.9c0.1,0.6,0.1,1.6,0.1,2.7h-10.1
c0.1,1.4,0.5,2.4,1.4,2.9c0.6,0.3,1.2,0.5,2,0.5c0.8,0,1.5-0.2,2-0.6c0.3-0.2,0.5-0.5,0.7-1h3.7c-0.1,0.8-0.5,1.7-1.3,2.5
c-1.2,1.3-3,2-5.2,2c-1.8,0-3.5-0.6-4.9-1.7c-1.4-1.1-2.1-3-2.1-5.5c0-2.4,0.6-4.2,1.9-5.5c1.3-1.3,2.9-1.9,4.9-1.9
C150,468.6,151.1,468.8,152.1,469.3z M146.7,472.4c-0.5,0.5-0.8,1.2-1,2.1h6.2c-0.1-1-0.4-1.7-1-2.2s-1.3-0.7-2.1-0.7
C147.9,471.6,147.2,471.9,146.7,472.4z"/>
<path fill="#0E7077" d="M156.1,471.7v-2.6h1.9v-3.9h3.6v3.9h2.2v2.6h-2.2v7.3c0,0.6,0.1,0.9,0.2,1.1c0.1,0.1,0.6,0.2,1.3,0.2
c0.1,0,0.2,0,0.3,0c0.1,0,0.2,0,0.4,0v2.7l-1.7,0.1c-1.7,0.1-2.9-0.2-3.5-0.9c-0.4-0.4-0.6-1.1-0.6-1.9v-8.5H156.1z"/>
<path fill="#0E7077" d="M176.1,469.1c0.7,0.3,1.3,0.8,1.7,1.4c0.4,0.5,0.6,1,0.7,1.6c0.1,0.5,0.1,1.4,0.1,2.7v8.1H175v-8.4
c0-0.7-0.1-1.3-0.4-1.8c-0.3-0.6-0.9-1-1.9-1c-0.9,0-1.7,0.3-2.2,1c-0.5,0.6-0.7,1.6-0.7,2.7v7.5h-3.6v-18.6h3.6v6.6
c0.5-0.8,1.1-1.4,1.8-1.7c0.7-0.3,1.4-0.5,2.2-0.5C174.7,468.6,175.4,468.8,176.1,469.1z"/>
<path fill="#0E7077" d="M185.8,467.3h-3.7V464h3.7V467.3z M182.1,469h3.7v13.8h-3.7V469z"/>
<path fill="#0E7077" d="M200.4,469.8c0.9,0.8,1.4,2,1.4,3.7v9.3h-3.7v-8.4c0-0.7-0.1-1.3-0.3-1.7c-0.4-0.7-1-1.1-2-1.1
c-1.2,0-2.1,0.5-2.5,1.6c-0.2,0.5-0.4,1.3-0.4,2.1v7.5h-3.6V469h3.5v2c0.5-0.7,0.9-1.2,1.3-1.5c0.7-0.6,1.7-0.8,2.8-0.8
C198.4,468.7,199.5,469,200.4,469.8z"/>
<path fill="#0E7077" d="M212,469c0.9,0.4,1.6,1,2.1,2v-2h3.5v13.1c0,1.8-0.3,3.1-0.9,4c-1,1.6-3,2.3-5.9,2.3c-1.8,0-3.2-0.3-4.3-1
s-1.7-1.7-1.9-3.1h3.9c0.1,0.4,0.3,0.7,0.5,0.9c0.4,0.3,1,0.5,2,0.5c1.3,0,2.2-0.4,2.6-1.3c0.3-0.6,0.4-1.5,0.4-2.8v-0.9
c-0.3,0.6-0.7,1-1.1,1.3c-0.7,0.5-1.7,0.8-2.8,0.8c-1.8,0-3.2-0.6-4.3-1.9c-1.1-1.2-1.6-2.9-1.6-5.1c0-2.1,0.5-3.8,1.5-5.2
c1-1.4,2.5-2.1,4.3-2.1C210.9,468.7,211.5,468.8,212,469z M213.2,478.9c0.6-0.6,0.9-1.7,0.9-3.1c0-1.3-0.3-2.3-0.8-3
c-0.6-0.7-1.3-1-2.2-1c-1.3,0-2.1,0.6-2.6,1.8c-0.3,0.6-0.4,1.4-0.4,2.3c0,0.8,0.1,1.5,0.4,2.1c0.5,1.2,1.4,1.8,2.6,1.8
C211.9,479.9,212.7,479.5,213.2,478.9z"/>
</g>
<g enable-background="new ">
<path fill="#0E7077" d="M551,466.1c1.5-1.5,3.4-2.3,5.8-2.3c3.1,0,5.4,1,6.8,3.1c0.8,1.2,1.2,2.3,1.3,3.5h-3.9
c-0.2-0.9-0.6-1.6-1-2c-0.7-0.8-1.7-1.2-3.1-1.2c-1.4,0-2.5,0.6-3.3,1.7c-0.8,1.1-1.2,2.8-1.2,4.9s0.4,3.7,1.3,4.7
c0.8,1,1.9,1.6,3.2,1.6c1.3,0,2.4-0.4,3.1-1.3c0.4-0.5,0.7-1.2,1-2.2h3.9c-0.3,2-1.2,3.7-2.6,5c-1.4,1.3-3.1,1.9-5.3,1.9
c-2.7,0-4.8-0.9-6.3-2.6c-1.5-1.7-2.3-4.1-2.3-7.1C548.4,470.4,549.2,467.8,551,466.1z"/>
<path fill="#0E7077" d="M579.2,481.1c-1.2,1.4-2.9,2.2-5.3,2.2c-2.4,0-4.2-0.7-5.3-2.2c-1.2-1.4-1.8-3.2-1.8-5.2
c0-2,0.6-3.7,1.8-5.2s2.9-2.2,5.3-2.2c2.4,0,4.2,0.7,5.3,2.2s1.8,3.2,1.8,5.2C580.9,478,580.4,479.7,579.2,481.1z M576.3,479.1
c0.6-0.8,0.9-1.8,0.9-3.2c0-1.4-0.3-2.5-0.9-3.2c-0.6-0.7-1.4-1.1-2.4-1.1c-1.1,0-1.9,0.4-2.4,1.1s-0.9,1.8-0.9,3.2
c0,1.4,0.3,2.5,0.9,3.2s1.4,1.1,2.4,1.1C574.9,480.3,575.7,479.9,576.3,479.1z"/>
<path fill="#0E7077" d="M594.7,469.8c0.9,0.8,1.4,2,1.4,3.7v9.3h-3.7v-8.4c0-0.7-0.1-1.3-0.3-1.7c-0.4-0.7-1-1.1-2-1.1
c-1.2,0-2.1,0.5-2.5,1.6c-0.2,0.5-0.4,1.3-0.4,2.1v7.5h-3.6V469h3.5v2c0.5-0.7,0.9-1.2,1.3-1.5c0.7-0.6,1.7-0.8,2.8-0.8
C592.6,468.7,593.8,469,594.7,469.8z"/>
<path fill="#0E7077" d="M610.6,469.8c0.9,0.8,1.4,2,1.4,3.7v9.3h-3.7v-8.4c0-0.7-0.1-1.3-0.3-1.7c-0.4-0.7-1-1.1-2-1.1
c-1.2,0-2.1,0.5-2.5,1.6c-0.2,0.5-0.4,1.3-0.4,2.1v7.5h-3.6V469h3.5v2c0.5-0.7,0.9-1.2,1.3-1.5c0.7-0.6,1.7-0.8,2.8-0.8
C608.5,468.7,609.7,469,610.6,469.8z"/>
<path fill="#0E7077" d="M624.3,469.3c1,0.4,1.8,1.1,2.4,2c0.6,0.8,0.9,1.8,1.1,2.9c0.1,0.6,0.1,1.6,0.1,2.7h-10.1
c0.1,1.4,0.5,2.4,1.4,2.9c0.6,0.3,1.2,0.5,2,0.5c0.8,0,1.5-0.2,2-0.6c0.3-0.2,0.5-0.5,0.7-1h3.7c-0.1,0.8-0.5,1.7-1.3,2.5
c-1.2,1.3-3,2-5.2,2c-1.8,0-3.5-0.6-4.9-1.7c-1.4-1.1-2.1-3-2.1-5.5c0-2.4,0.6-4.2,1.9-5.5c1.3-1.3,2.9-1.9,4.9-1.9
C622.3,468.6,623.4,468.8,624.3,469.3z M618.9,472.4c-0.5,0.5-0.8,1.2-1,2.1h6.2c-0.1-1-0.4-1.7-1-2.2c-0.6-0.5-1.3-0.7-2.1-0.7
C620.1,471.6,619.4,471.9,618.9,472.4z"/>
<path fill="#0E7077" d="M638,474c-0.1-0.5-0.2-1-0.5-1.4c-0.4-0.6-1-0.8-1.9-0.8c-1.2,0-2.1,0.6-2.5,1.8c-0.2,0.6-0.4,1.5-0.4,2.6
c0,1,0.1,1.8,0.4,2.4c0.4,1.2,1.2,1.7,2.4,1.7c0.8,0,1.5-0.2,1.8-0.7s0.6-1,0.6-1.8h3.7c-0.1,1.1-0.5,2.1-1.2,3.1
c-1.1,1.6-2.8,2.4-5,2.4c-2.2,0-3.9-0.7-4.9-2c-1-1.3-1.6-3-1.6-5.1c0-2.4,0.6-4.2,1.7-5.5c1.2-1.3,2.8-2,4.8-2
c1.7,0,3.2,0.4,4.3,1.2s1.8,2.2,2,4.1H638z"/>
<path fill="#0E7077" d="M642.8,471.7v-2.6h1.9v-3.9h3.6v3.9h2.2v2.6h-2.2v7.3c0,0.6,0.1,0.9,0.2,1.1c0.1,0.1,0.6,0.2,1.3,0.2
c0.1,0,0.2,0,0.3,0c0.1,0,0.2,0,0.4,0v2.7l-1.7,0.1c-1.7,0.1-2.9-0.2-3.5-0.9c-0.4-0.4-0.6-1.1-0.6-1.9v-8.5H642.8z"/>
<path fill="#0E7077" d="M667.4,469.2c0.7,0.4,1.2,0.9,1.6,1.5v-6.6h3.7v18.7h-3.5v-1.9c-0.5,0.8-1.1,1.4-1.8,1.8s-1.5,0.6-2.5,0.6
c-1.6,0-3-0.7-4.1-2c-1.1-1.3-1.7-3-1.7-5c0-2.4,0.5-4.2,1.6-5.6c1.1-1.4,2.5-2,4.4-2C666,468.7,666.8,468.8,667.4,469.2z
M668.3,479.1c0.5-0.8,0.8-1.7,0.8-3c0-1.7-0.4-2.9-1.3-3.6c-0.5-0.4-1.1-0.7-1.8-0.7c-1.1,0-1.8,0.4-2.3,1.2
c-0.5,0.8-0.7,1.8-0.7,3c0,1.3,0.3,2.3,0.8,3.1c0.5,0.8,1.3,1.1,2.3,1.1C667,480.2,667.8,479.8,668.3,479.1z"/>
<path fill="#0E7077" d="M685,469.3c1,0.4,1.8,1.1,2.4,2c0.6,0.8,0.9,1.8,1.1,2.9c0.1,0.6,0.1,1.6,0.1,2.7h-10.1
c0.1,1.4,0.5,2.4,1.4,2.9c0.6,0.3,1.2,0.5,2,0.5c0.8,0,1.5-0.2,2-0.6c0.3-0.2,0.5-0.5,0.7-1h3.7c-0.1,0.8-0.5,1.7-1.3,2.5
c-1.2,1.3-3,2-5.2,2c-1.8,0-3.5-0.6-4.9-1.7c-1.4-1.1-2.1-3-2.1-5.5c0-2.4,0.6-4.2,1.9-5.5c1.3-1.3,2.9-1.9,4.9-1.9
C683,468.6,684,468.8,685,469.3z M679.6,472.4c-0.5,0.5-0.8,1.2-1,2.1h6.2c-0.1-1-0.4-1.7-1-2.2c-0.6-0.5-1.3-0.7-2.1-0.7
C680.8,471.6,680.1,471.9,679.6,472.4z"/>
<path fill="#0E7077" d="M699,469h3.9l-5,13.8H694l-5-13.8h4.1l2.9,10.2L699,469z"/>
<path fill="#0E7077" d="M708.6,467.3H705V464h3.7V467.3z M705,469h3.7v13.8H705V469z"/>
<path fill="#0E7077" d="M720.4,474c-0.1-0.5-0.2-1-0.5-1.4c-0.4-0.6-1-0.8-1.9-0.8c-1.2,0-2.1,0.6-2.5,1.8
c-0.2,0.6-0.4,1.5-0.4,2.6c0,1,0.1,1.8,0.4,2.4c0.4,1.2,1.2,1.7,2.4,1.7c0.8,0,1.5-0.2,1.8-0.7s0.6-1,0.6-1.8h3.7
c-0.1,1.1-0.5,2.1-1.2,3.1c-1.1,1.6-2.8,2.4-5,2.4c-2.2,0-3.9-0.7-4.9-2c-1-1.3-1.6-3-1.6-5.1c0-2.4,0.6-4.2,1.7-5.5
c1.2-1.3,2.8-2,4.8-2c1.7,0,3.2,0.4,4.3,1.2s1.8,2.2,2,4.1H720.4z"/>
<path fill="#0E7077" d="M735.6,469.3c1,0.4,1.8,1.1,2.4,2c0.6,0.8,0.9,1.8,1.1,2.9c0.1,0.6,0.1,1.6,0.1,2.7h-10.1
c0.1,1.4,0.5,2.4,1.4,2.9c0.6,0.3,1.2,0.5,2,0.5c0.8,0,1.5-0.2,2-0.6c0.3-0.2,0.5-0.5,0.7-1h3.7c-0.1,0.8-0.5,1.7-1.3,2.5
c-1.2,1.3-3,2-5.2,2c-1.8,0-3.5-0.6-4.9-1.7c-1.4-1.1-2.1-3-2.1-5.5c0-2.4,0.6-4.2,1.9-5.5c1.3-1.3,2.9-1.9,4.9-1.9
C733.6,468.6,734.6,468.8,735.6,469.3z M730.2,472.4c-0.5,0.5-0.8,1.2-1,2.1h6.2c-0.1-1-0.4-1.7-1-2.2c-0.6-0.5-1.3-0.7-2.1-0.7
C731.4,471.6,730.7,471.9,730.2,472.4z"/>
<path fill="#0E7077" d="M743.9,478.4c0.1,0.6,0.2,1.1,0.5,1.4c0.5,0.5,1.3,0.7,2.5,0.7c0.7,0,1.3-0.1,1.7-0.3s0.6-0.5,0.6-1
c0-0.4-0.2-0.7-0.5-0.9s-1.6-0.6-3.8-1.1c-1.6-0.4-2.7-0.9-3.3-1.5c-0.6-0.6-1-1.4-1-2.5c0-1.3,0.5-2.4,1.5-3.3
c1-0.9,2.4-1.4,4.2-1.4c1.7,0,3.1,0.3,4.2,1c1.1,0.7,1.7,1.9,1.9,3.6h-3.6c-0.1-0.5-0.2-0.8-0.4-1.1c-0.4-0.5-1.1-0.7-2-0.7
c-0.8,0-1.4,0.1-1.7,0.4c-0.3,0.2-0.5,0.5-0.5,0.9c0,0.4,0.2,0.7,0.5,0.9c0.4,0.2,1.6,0.5,3.8,1c1.4,0.3,2.5,0.9,3.2,1.5
c0.7,0.7,1.1,1.6,1.1,2.6c0,1.4-0.5,2.5-1.5,3.4c-1,0.9-2.6,1.3-4.7,1.3c-2.2,0-3.8-0.5-4.8-1.4c-1-0.9-1.6-2.1-1.6-3.5H743.9z"/>
</g>
<g enable-background="new ">
<path fill="#0E7077" d="M285.6,152.4h3.9v15.3h9.3v3.4h-13.3V152.4z"/>
<path fill="#0E7077" d="M312.7,169.4c-1.2,1.4-2.9,2.2-5.3,2.2s-4.2-0.7-5.3-2.2c-1.2-1.4-1.8-3.2-1.8-5.2c0-2,0.6-3.7,1.8-5.2
s2.9-2.2,5.3-2.2s4.2,0.7,5.3,2.2s1.8,3.2,1.8,5.2C314.5,166.2,313.9,168,312.7,169.4z M309.8,167.4c0.6-0.8,0.9-1.8,0.9-3.2
c0-1.4-0.3-2.5-0.9-3.2s-1.4-1.1-2.4-1.1s-1.9,0.4-2.4,1.1s-0.9,1.8-0.9,3.2c0,1.4,0.3,2.5,0.9,3.2s1.4,1.1,2.4,1.1
S309.3,168.2,309.8,167.4z"/>
<path fill="#0E7077" d="M328.6,169.4c-1.2,1.4-2.9,2.2-5.3,2.2s-4.2-0.7-5.3-2.2c-1.2-1.4-1.8-3.2-1.8-5.2c0-2,0.6-3.7,1.8-5.2
s2.9-2.2,5.3-2.2s4.2,0.7,5.3,2.2s1.8,3.2,1.8,5.2C330.4,166.2,329.8,168,328.6,169.4z M325.7,167.4c0.6-0.8,0.9-1.8,0.9-3.2
c0-1.4-0.3-2.5-0.9-3.2s-1.4-1.1-2.4-1.1s-1.9,0.4-2.4,1.1s-0.9,1.8-0.9,3.2c0,1.4,0.3,2.5,0.9,3.2s1.4,1.1,2.4,1.1
S325.1,168.2,325.7,167.4z"/>
<path fill="#0E7077" d="M332.9,152.4h3.6v10.1l4.6-5.2h4.5l-5,5.2l5.2,8.6h-4.4l-3.4-5.9l-1.5,1.6v4.4h-3.6V152.4z"/>
<path fill="#0E7077" d="M360.1,162.7c0.7-0.1,1.2-0.2,1.4-0.3c0.5-0.2,0.8-0.6,0.8-1c0-0.6-0.2-1-0.6-1.2c-0.4-0.2-1-0.3-1.8-0.3
c-0.9,0-1.5,0.2-1.8,0.6c-0.3,0.3-0.4,0.7-0.5,1.3h-3.5c0.1-1.2,0.4-2.2,1-3c1-1.2,2.6-1.8,4.9-1.8c1.5,0,2.9,0.3,4.1,0.9
c1.2,0.6,1.8,1.7,1.8,3.4v6.3c0,0.4,0,1,0,1.6c0,0.5,0.1,0.8,0.2,1s0.3,0.3,0.5,0.4v0.5h-3.9c-0.1-0.3-0.2-0.5-0.2-0.8
s-0.1-0.5-0.1-0.8c-0.5,0.5-1.1,1-1.7,1.4c-0.8,0.4-1.7,0.7-2.7,0.7c-1.3,0-2.3-0.4-3.1-1.1c-0.8-0.7-1.2-1.7-1.2-3
c0-1.7,0.7-2.9,2-3.7c0.7-0.4,1.8-0.7,3.2-0.9L360.1,162.7z M362.3,164.4c-0.2,0.1-0.5,0.3-0.7,0.3c-0.2,0.1-0.6,0.2-1,0.2
l-0.8,0.2c-0.8,0.1-1.3,0.3-1.7,0.5c-0.6,0.3-0.9,0.8-0.9,1.5c0,0.6,0.2,1.1,0.5,1.3c0.3,0.3,0.8,0.4,1.3,0.4
c0.8,0,1.5-0.2,2.2-0.7c0.7-0.5,1-1.3,1-2.5V164.4z"/>
<path fill="#0E7077" d="M376.8,156.9c0,0,0.2,0,0.3,0v3.7c-0.2,0-0.4,0-0.6-0.1s-0.3,0-0.4,0c-1.5,0-2.4,0.5-2.9,1.4
c-0.3,0.5-0.4,1.4-0.4,2.5v6.6h-3.6v-13.8h3.5v2.4c0.6-0.9,1-1.6,1.5-1.9c0.7-0.6,1.6-0.9,2.6-0.9
C376.7,156.9,376.7,156.9,376.8,156.9z"/>
<path fill="#0E7077" d="M390.8,169.4c-1.2,1.4-2.9,2.2-5.3,2.2s-4.2-0.7-5.3-2.2c-1.2-1.4-1.8-3.2-1.8-5.2c0-2,0.6-3.7,1.8-5.2
s2.9-2.2,5.3-2.2s4.2,0.7,5.3,2.2s1.8,3.2,1.8,5.2C392.5,166.2,391.9,168,390.8,169.4z M387.9,167.4c0.6-0.8,0.9-1.8,0.9-3.2
c0-1.4-0.3-2.5-0.9-3.2s-1.4-1.1-2.4-1.1s-1.9,0.4-2.4,1.1s-0.9,1.8-0.9,3.2c0,1.4,0.3,2.5,0.9,3.2s1.4,1.1,2.4,1.1
S387.3,168.2,387.9,167.4z"/>
<path fill="#0E7077" d="M398.7,157.3v8.3c0,0.8,0.1,1.4,0.3,1.8c0.3,0.7,1,1.1,1.9,1.1c1.2,0,2.1-0.5,2.5-1.5
c0.2-0.5,0.4-1.3,0.4-2.1v-7.5h3.7v13.8h-3.5v-2c0,0-0.1,0.2-0.3,0.4s-0.3,0.4-0.5,0.6c-0.6,0.5-1.1,0.9-1.6,1
c-0.5,0.2-1.1,0.3-1.9,0.3c-2,0-3.4-0.7-4.1-2.2c-0.4-0.8-0.6-2-0.6-3.6v-8.3H398.7z"/>
<path fill="#0E7077" d="M422.1,158c0.9,0.8,1.4,2,1.4,3.7v9.3h-3.7v-8.4c0-0.7-0.1-1.3-0.3-1.7c-0.4-0.7-1-1.1-2-1.1
c-1.2,0-2.1,0.5-2.5,1.6c-0.2,0.5-0.4,1.3-0.4,2.1v7.5H411v-13.8h3.5v2c0.5-0.7,0.9-1.2,1.3-1.5c0.7-0.6,1.7-0.8,2.8-0.8
C420.1,156.9,421.2,157.3,422.1,158z"/>
<path fill="#0E7077" d="M434.2,157.5c0.7,0.4,1.2,0.9,1.6,1.5v-6.6h3.7v18.7h-3.5v-1.9c-0.5,0.8-1.1,1.4-1.8,1.8s-1.5,0.6-2.5,0.6
c-1.6,0-3-0.7-4.1-2c-1.1-1.3-1.7-3-1.7-5c0-2.4,0.5-4.2,1.6-5.6c1.1-1.4,2.5-2,4.4-2C432.8,156.9,433.5,157.1,434.2,157.5z
M435.1,167.3c0.5-0.8,0.8-1.7,0.8-3c0-1.7-0.4-2.9-1.3-3.6c-0.5-0.4-1.1-0.7-1.8-0.7c-1.1,0-1.8,0.4-2.3,1.2
c-0.5,0.8-0.7,1.8-0.7,3c0,1.3,0.3,2.3,0.8,3.1c0.5,0.8,1.3,1.1,2.3,1.1C433.8,168.5,434.6,168.1,435.1,167.3z"/>
</g>
<path id="Line-copy" sketch:type="MSShapeGroup" fill="none" stroke="#739898" stroke-width="2" stroke-miterlimit="10" d="
M17.3,118h742"/>
<g id="Build-something" transform="translate(283.000000, 447.000000)" sketch:type="MSLayerGroup">
<path id="Fill-1_1_" sketch:type="MSShapeGroup" fill="#739898" d="M-187.9,89.3l13.3,7.7l13.3-7.7l-13.3-7.7L-187.9,89.3"/>
<path id="Fill-2_1_" sketch:type="MSShapeGroup" fill="#0E7077" d="M-188.1,89.5v15.3l13.3,7.7V97.2L-188.1,89.5"/>
<path id="Fill-3_1_" sketch:type="MSShapeGroup" fill="#739898" d="M-161.6,89.3v15.3l-13.3,7.7V97L-161.6,89.3L-161.6,89.3"/>
<path id="Stroke-4" sketch:type="MSShapeGroup" fill="none" stroke="#333333" stroke-linecap="round" stroke-linejoin="bevel" d="
M-148.4,81.7V66.4 M-148.4,97v15.3 M-161.6,89.3v15.3 M-161.6,135.2v-15.3 M-161.6,89.3V74 M-161.6,135.2v15.3 M-161.6,104.6
L-161.6,104.6 M-174.9,66.4v15.3 M-174.9,127.6v-15.3 M-174.9,158.2L-174.9,158.2 M-174.9,112.3V97 M-174.9,158.2v-15.3
M-188.1,58.7V74 M-188.1,119.9v15.3 M-188.1,89.3v15.3 M-188.1,150.5v-15.3 M-188.1,119.9L-188.1,119.9 M-201.4,142.9v15.3
M-201.4,112.3v15.3 M-201.4,81.7V97 M-214.7,119.9L-214.7,119.9 M-214.7,150.5v-15.3 M-214.7,104.6v15.3 M-227.9,127.6v15.3
M-227.9,142.9L-227.9,142.9 M-135.1,104.6V89.3 M-135.1,135.2v-30.6 M-188.1,150.5l-13.3,7.7 M-174.9,142.9l13.3-7.7
M-148.4,112.3l13.3-7.7 M-161.6,119.9l13.3-7.7 M-201.4,142.9l13.3-7.7"/>
<path id="Stroke-5" sketch:type="MSShapeGroup" fill="none" stroke="#333333" stroke-linecap="round" stroke-linejoin="bevel" d="
M-188.1,135.2l13.3-7.7 M-227.9,142.9L-227.9,142.9 M-214.7,135.2l13.3-7.7 M-201.4,127.6L-201.4,127.6 M-161.6,104.6L-161.6,104.6
M-188.1,119.9l13.3-7.7 M-174.9,112.3l13.3-7.7 M-148.4,97l13.3-7.7 M-214.7,119.9l-13.3,7.7 M-201.4,112.3l13.3-7.7 M-148.4,81.7
l-13.3,7.7 M-174.9,97l13.3-7.7 M-214.7,104.6l13.3-7.7"/>
<path id="Fill-6_1_" sketch:type="MSShapeGroup" fill="#72184B" d="M-174.9,81.7l-13.3,7.7"/>
<path id="Stroke-7" sketch:type="MSShapeGroup" fill="none" stroke="#333333" stroke-linecap="round" stroke-linejoin="bevel" d="
M-174.9,81.7l-13.3,7.7"/>
<path id="Stroke-8" sketch:type="MSShapeGroup" fill="none" stroke="#333333" stroke-linecap="round" stroke-linejoin="bevel" d="
M-148.4,66.4l-13.3,7.7 M-188.1,74l-13.3,7.7 M-161.6,58.7l-13.3,7.7 M-188.1,58.7l13.3-7.7 M-174.9,158.2l13.3-7.7 M-148.4,142.9
l13.3-7.7 M-148.4,142.9l-13.3,7.7 M-227.9,142.9l13.3,7.7 M-227.9,142.9L-227.9,142.9 M-201.4,158.2l-13.3-7.7 M-161.6,74
l-13.3-7.7 M-135.1,89.3l-13.3-7.7 M-174.9,66.4l-13.3-7.7 M-174.9,81.7l-13.3-7.7 M-148.4,97l-13.3-7.7 M-161.6,89.3l-13.3-7.7
M-174.9,97l-13.3-7.7 M-148.4,112.3l-13.3-7.7 M-188.1,89.3l-13.3-7.7 M-201.4,97l13.3,7.7 M-174.9,112.3l-13.3-7.7 M-161.6,119.9
l-13.3-7.7 M-188.1,119.9L-188.1,119.9 M-214.7,104.6l13.3,7.7 M-188.1,119.9l-13.3-7.7 M-161.6,135.2l-13.3-7.7 M-201.4,127.6
L-201.4,127.6 M-201.4,127.6l-13.3-7.7 M-188.1,135.2l13.3,7.7 M-188.1,135.2l-13.3-7.7 M-214.7,119.9L-214.7,119.9 M-201.4,142.9
l-13.3-7.7"/>
<path id="Stroke-9" sketch:type="MSShapeGroup" fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="bevel" d="
M-214.7,135.2l-13.3-7.7 M-174.9,158.2L-174.9,158.2 M-188.1,150.5l13.3,7.7 M-161.6,58.7l13.3,7.7 M-161.6,58.7l-13.3-7.7"/>
</g>
<g id="Look-around" transform="translate(424.000000, 181.000000)" sketch:type="MSLayerGroup">
<g>
<path id="Fill-1_3_" sketch:type="MSShapeGroup" fill="#221F1F" d="M-103.4,69.8h2.5l-3.2-4h6.6v-2h-6.6l3.2-4h-2.5l-4,4.9
L-103.4,69.8"/>
<path id="Fill-2_3_" sketch:type="MSShapeGroup" fill="#221F1F" d="M-68.8,39.1v2.5l4-3.2v6.6h2v-6.6l4,3.2v-2.5l-4.9-4
L-68.8,39.1"/>
<path id="Fill-3_3_" sketch:type="MSShapeGroup" fill="#221F1F" d="M-24.3,59.9h-2.5l3.2,4h-6.6v2h6.6l-3.2,4h2.5l4-4.9
L-24.3,59.9"/>
<path id="Fill-4_2_" sketch:type="MSShapeGroup" fill="#221F1F" d="M-58.9,90.5V88l-4,3.2v-6.6h-2v6.6l-4-3.2v2.5l4.9,4
L-58.9,90.5"/>
<path id="Fill-5_2_" sketch:type="MSShapeGroup" fill="#221F1F" d="M-77.7,69.7c-5.9,0-5.1-7.2-5.1-7.2s1-2.3,5.1-2.3
c4.1,0,5.1,2.3,5.1,2.3S-71.8,69.7-77.7,69.7 M-77.7,57.9c-6.2,0-8,5.6-8,18.8v0.6c2.3,1.5,5.1,2.4,8,2.4c3,0,5.7-0.9,8-2.4v-0.6
C-69.7,63.5-71.5,57.9-77.7,57.9"/>
<path id="Fill-6_3_" sketch:type="MSShapeGroup" fill="#221F1F" d="M-50,50c-6.3,0-11.7,4-13.8,9.5C-66,54-71.4,50-77.7,50
c-8.2,0-14.8,6.6-14.8,14.8c0,0.5,0.4,1,1,1c0.5,0,1-0.4,1-1c0-7.1,5.8-12.8,12.8-12.8c7.1,0,12.8,5.8,12.8,12.8v0
c0,0.5,0.4,1,1,1c0.5,0,1-0.4,1-1c0-7.1,5.8-12.8,12.8-12.8c7.1,0,12.8,5.8,12.8,12.8c0,0.5,0.4,1,1,1c0.5,0,1-0.4,1-1
C-35.2,56.6-41.9,50-50,50"/>
<path id="Fill-7_2_" sketch:type="MSShapeGroup" fill="#221F1F" d="M-50,69.7c-5.9,0-5.1-7.2-5.1-7.2s1-2.3,5.1-2.3
c4.1,0,5.1,2.3,5.1,2.3S-44.1,69.7-50,69.7 M-50,57.9c-6.2,0-8,5.6-8,18.8v0.6c2.3,1.5,5.1,2.4,8,2.4c3,0,5.7-0.9,8-2.4v-0.6
C-42,63.5-43.8,57.9-50,57.9"/>
</g>
<path id="Stroke-8_1_" sketch:type="MSShapeGroup" fill="none" stroke="#676767" d="M12.7,113.1c0,5.4-4.4,9.8-9.8,9.8h-133.4
c-5.4,0-9.8-4.4-9.8-9.8V16.5c0-5.4,4.4-9.8,9.8-9.8H2.8c5.4,0,9.8,4.4,9.8,9.8V113.1L12.7,113.1L12.7,113.1L12.7,113.1z"/>
</g>
<g enable-background="new ">
<path fill="#0E7077" d="M30.1,152.4h5.6v18.7h-3.6v-12.7c0-0.4,0-0.9,0-1.5c0-0.7,0-1.2,0-1.5l-3.5,15.7h-3.8l-3.5-15.7
c0,0.4,0,0.9,0,1.5c0,0.7,0,1.2,0,1.5v12.7h-3.6v-18.7h5.7l3.4,14.7L30.1,152.4z"/>
<path fill="#0E7077" d="M50.6,169.4c-1.2,1.4-2.9,2.2-5.3,2.2s-4.2-0.7-5.3-2.2c-1.2-1.4-1.8-3.2-1.8-5.2c0-2,0.6-3.7,1.8-5.2
s2.9-2.2,5.3-2.2s4.2,0.7,5.3,2.2s1.8,3.2,1.8,5.2C52.3,166.2,51.8,168,50.6,169.4z M47.7,167.4c0.6-0.8,0.9-1.8,0.9-3.2
c0-1.4-0.3-2.5-0.9-3.2s-1.4-1.1-2.4-1.1s-1.9,0.4-2.4,1.1s-0.9,1.8-0.9,3.2c0,1.4,0.3,2.5,0.9,3.2s1.4,1.1,2.4,1.1
S47.1,168.2,47.7,167.4z"/>
<path fill="#0E7077" d="M63.5,157.3h3.9l-5,13.8h-3.8l-5-13.8h4.1l2.9,10.2L63.5,157.3z"/>
<path fill="#0E7077" d="M78.4,157.5c1,0.4,1.8,1.1,2.4,2c0.6,0.8,0.9,1.8,1.1,2.9c0.1,0.6,0.1,1.6,0.1,2.7H71.9
c0.1,1.4,0.5,2.4,1.4,2.9c0.6,0.3,1.2,0.5,2,0.5c0.8,0,1.5-0.2,2-0.6c0.3-0.2,0.5-0.5,0.7-1h3.7c-0.1,0.8-0.5,1.7-1.3,2.5
c-1.2,1.3-3,2-5.2,2c-1.8,0-3.5-0.6-4.9-1.7c-1.4-1.1-2.1-3-2.1-5.5c0-2.4,0.6-4.2,1.9-5.5c1.3-1.3,2.9-1.9,4.9-1.9
C76.4,156.9,77.4,157.1,78.4,157.5z M73,160.7c-0.5,0.5-0.8,1.2-1,2.1h6.2c-0.1-1-0.4-1.7-1-2.2s-1.3-0.7-2.1-0.7
C74.2,159.9,73.5,160.1,73,160.7z"/>
<path fill="#0E7077" d="M96.5,162.7c0.7-0.1,1.2-0.2,1.4-0.3c0.5-0.2,0.8-0.6,0.8-1c0-0.6-0.2-1-0.6-1.2c-0.4-0.2-1-0.3-1.8-0.3
c-0.9,0-1.5,0.2-1.8,0.6c-0.3,0.3-0.4,0.7-0.5,1.3h-3.5c0.1-1.2,0.4-2.2,1-3c1-1.2,2.6-1.8,4.9-1.8c1.5,0,2.9,0.3,4.1,0.9
c1.2,0.6,1.8,1.7,1.8,3.4v6.3c0,0.4,0,1,0,1.6c0,0.5,0.1,0.8,0.2,1s0.3,0.3,0.5,0.4v0.5h-3.9c-0.1-0.3-0.2-0.5-0.2-0.8
s-0.1-0.5-0.1-0.8c-0.5,0.5-1.1,1-1.7,1.4c-0.8,0.4-1.7,0.7-2.7,0.7c-1.3,0-2.3-0.4-3.1-1.1c-0.8-0.7-1.2-1.7-1.2-3
c0-1.7,0.7-2.9,2-3.7c0.7-0.4,1.8-0.7,3.2-0.9L96.5,162.7z M98.8,164.4c-0.2,0.1-0.5,0.3-0.7,0.3c-0.2,0.1-0.6,0.2-1,0.2l-0.8,0.2
c-0.8,0.1-1.3,0.3-1.7,0.5c-0.6,0.3-0.9,0.8-0.9,1.5c0,0.6,0.2,1.1,0.5,1.3c0.3,0.3,0.8,0.4,1.3,0.4c0.8,0,1.5-0.2,2.2-0.7
c0.7-0.5,1-1.3,1-2.5V164.4z"/>
<path fill="#0E7077" d="M113.2,156.9c0,0,0.2,0,0.3,0v3.7c-0.2,0-0.4,0-0.6-0.1s-0.3,0-0.4,0c-1.5,0-2.4,0.5-2.9,1.4
c-0.3,0.5-0.4,1.4-0.4,2.5v6.6h-3.6v-13.8h3.5v2.4c0.6-0.9,1-1.6,1.5-1.9c0.7-0.6,1.6-0.9,2.6-0.9
C113.1,156.9,113.2,156.9,113.2,156.9z"/>
<path fill="#0E7077" d="M127.2,169.4c-1.2,1.4-2.9,2.2-5.3,2.2s-4.2-0.7-5.3-2.2c-1.2-1.4-1.8-3.2-1.8-5.2c0-2,0.6-3.7,1.8-5.2
s2.9-2.2,5.3-2.2s4.2,0.7,5.3,2.2s1.8,3.2,1.8,5.2C128.9,166.2,128.4,168,127.2,169.4z M124.3,167.4c0.6-0.8,0.9-1.8,0.9-3.2
c0-1.4-0.3-2.5-0.9-3.2s-1.4-1.1-2.4-1.1s-1.9,0.4-2.4,1.1s-0.9,1.8-0.9,3.2c0,1.4,0.3,2.5,0.9,3.2s1.4,1.1,2.4,1.1
S123.7,168.2,124.3,167.4z"/>
<path fill="#0E7077" d="M135.1,157.3v8.3c0,0.8,0.1,1.4,0.3,1.8c0.3,0.7,1,1.1,1.9,1.1c1.2,0,2.1-0.5,2.5-1.5
c0.2-0.5,0.4-1.3,0.4-2.1v-7.5h3.7v13.8h-3.5v-2c0,0-0.1,0.2-0.3,0.4c-0.1,0.2-0.3,0.4-0.5,0.6c-0.6,0.5-1.1,0.9-1.6,1
c-0.5,0.2-1.1,0.3-1.9,0.3c-2,0-3.4-0.7-4.1-2.2c-0.4-0.8-0.6-2-0.6-3.6v-8.3H135.1z"/>
<path fill="#0E7077" d="M158.6,158c0.9,0.8,1.4,2,1.4,3.7v9.3h-3.7v-8.4c0-0.7-0.1-1.3-0.3-1.7c-0.4-0.7-1-1.1-2-1.1
c-1.2,0-2.1,0.5-2.5,1.6c-0.2,0.5-0.4,1.3-0.4,2.1v7.5h-3.6v-13.8h3.5v2c0.5-0.7,0.9-1.2,1.3-1.5c0.7-0.6,1.7-0.8,2.8-0.8
C156.5,156.9,157.7,157.3,158.6,158z"/>
<path fill="#0E7077" d="M170.6,157.5c0.7,0.4,1.2,0.9,1.6,1.5v-6.6h3.7v18.7h-3.5v-1.9c-0.5,0.8-1.1,1.4-1.8,1.8s-1.5,0.6-2.5,0.6
c-1.6,0-3-0.7-4.1-2c-1.1-1.3-1.7-3-1.7-5c0-2.4,0.5-4.2,1.6-5.6s2.5-2,4.4-2C169.2,156.9,170,157.1,170.6,157.5z M171.5,167.3
c0.5-0.8,0.8-1.7,0.8-3c0-1.7-0.4-2.9-1.3-3.6c-0.5-0.4-1.1-0.7-1.8-0.7c-1.1,0-1.8,0.4-2.3,1.2c-0.5,0.8-0.7,1.8-0.7,3
c0,1.3,0.3,2.3,0.8,3.1c0.5,0.8,1.3,1.1,2.3,1.1S171,168.1,171.5,167.3z"/>
</g>
<rect x="548.2" y="322.7" fill="none" width="191" height="78"/>
<text transform="matrix(1 0 0 1 548.1891 332.7482)"><tspan x="0" y="0" fill="#666666" font-family="'Helvetica'" font-size="14">Use your best headphones </tspan><tspan x="0" y="19" fill="#666666" font-family="'Helvetica'" font-size="14">and microphone for high </tspan><tspan x="0" y="38" fill="#666666" font-family="'Helvetica'" font-size="14">fidelity audio. Chat via text by </tspan><tspan x="0" y="57" fill="#666666" font-family="'Helvetica'" font-size="14">pressing the Enter key.</tspan></text>
<rect x="283.6" y="322.7" fill="none" width="213" height="91.4"/>
<text transform="matrix(1 0 0 1 283.6183 332.7482)"><tspan x="0" y="0" fill="#666666" font-family="'Helvetica'" font-size="14">Use two fingers to look around. </tspan><tspan x="0" y="19" fill="#666666" font-family="'Helvetica'" font-size="14">Turn this off by opening Running </tspan><tspan x="0" y="38" fill="#666666" font-family="'Helvetica'" font-size="14">Scripts (Cmnd/Cntrl+J) and </tspan><tspan x="0" y="57" fill="#666666" font-family="'Helvetica'" font-size="14">clicking the X next to </tspan><tspan x="0" y="76" fill="#666666" font-family="'Helvetica'" font-size="14">lookWithTouch.js </tspan></text>
<rect x="17.5" y="322.7" fill="none" width="191" height="99.4"/>
<text transform="matrix(1 0 0 1 17.5192 332.7482)"><tspan x="0" y="0" fill="#666666" font-family="'Helvetica'" font-size="14">Move around with WASD &amp; fly </tspan><tspan x="0" y="19" fill="#666666" font-family="'Helvetica'" font-size="14">up or down with E &amp; C.</tspan><tspan x="0" y="38" fill="#666666" font-family="'Helvetica'" font-size="14">Cmnd/Cntrl+G will send you </tspan><tspan x="0" y="57" fill="#666666" font-family="'Helvetica'" font-size="14">home. @ (Shift+2) will let you </tspan><tspan x="0" y="76" fill="#666666" font-family="'Helvetica'" font-size="14">teleport to a user or location.</tspan></text>
<rect x="548.2" y="631.8" fill="none" width="191" height="90.2"/>
<text transform="matrix(1 0 0 1 548.1891 641.8737)"><tspan x="0" y="0" fill="#666666" font-family="'Helvetica'" font-size="14">Have an Oculus Rift, a Razer </tspan><tspan x="0" y="19" fill="#666666" font-family="'Helvetica'" font-size="14">Hydra, or a PrimeSense 3D </tspan><tspan x="0" y="38" fill="#666666" font-family="'Helvetica'" font-size="14">camera? We support them all.</tspan></text>
<rect x="17.5" y="631.8" fill="none" width="191" height="105.1"/>
<text transform="matrix(1 0 0 1 17.5191 641.8737)"><tspan x="0" y="0" fill="#666666" font-family="'Helvetica'" font-size="14">Use the </tspan><tspan x="52.1" y="0" fill="#666666" font-family="'Helvetica-Bold'" font-size="14">editVoxels.js</tspan><tspan x="137.7" y="0" fill="#666666" font-family="'Helvetica'" font-size="14"> script to </tspan><tspan x="0" y="19" fill="#666666" font-family="'Helvetica'" font-size="14">build with your mouse use </tspan><tspan x="0" y="38" fill="#666666" font-family="'Helvetica'" font-size="14">the tab key to toggle the tools </tspan><tspan x="0" y="57" fill="#666666" font-family="'Helvetica'" font-size="14">on/off.</tspan></text>
<g enable-background="new ">
<path fill="#0E7077" d="M288.4,477.1c0.1,0.9,0.4,1.5,0.7,1.9c0.6,0.8,1.8,1.2,3.3,1.2c0.9,0,1.7-0.1,2.3-0.3
c1.1-0.4,1.7-1.1,1.7-2.2c0-0.6-0.3-1.1-0.8-1.4c-0.5-0.3-1.4-0.6-2.6-0.9l-2-0.4c-2-0.4-3.4-0.9-4.1-1.4c-1.3-0.9-1.9-2.2-1.9-4
c0-1.7,0.6-3.1,1.9-4.2s3-1.7,5.4-1.7c2,0,3.7,0.5,5.1,1.6c1.4,1,2.1,2.6,2.2,4.6h-3.8c-0.1-1.1-0.6-1.9-1.5-2.4
c-0.6-0.3-1.4-0.5-2.3-0.5c-1,0-1.9,0.2-2.5,0.6s-0.9,1-0.9,1.7c0,0.7,0.3,1.2,0.9,1.5c0.4,0.2,1.2,0.5,2.5,0.8l3.3,0.8
c1.4,0.3,2.5,0.8,3.2,1.4c1.1,0.9,1.7,2.2,1.7,3.8c0,1.7-0.7,3.1-2,4.3c-1.3,1.1-3.2,1.7-5.6,1.7c-2.5,0-4.4-0.6-5.8-1.7
c-1.4-1.1-2.1-2.6-2.1-4.6H288.4z"/>
<path fill="#0E7077" d="M310.9,474c-0.1-0.5-0.2-1-0.5-1.4c-0.4-0.6-1-0.8-1.9-0.8c-1.2,0-2.1,0.6-2.5,1.8
c-0.2,0.6-0.4,1.5-0.4,2.6c0,1,0.1,1.8,0.4,2.4c0.4,1.2,1.2,1.7,2.4,1.7c0.8,0,1.5-0.2,1.8-0.7c0.4-0.5,0.6-1,0.6-1.8h3.7
c-0.1,1.1-0.5,2.1-1.2,3.1c-1.1,1.6-2.8,2.4-5,2.4s-3.9-0.7-4.9-2c-1.1-1.3-1.6-3-1.6-5.1c0-2.4,0.6-4.2,1.7-5.5s2.8-2,4.8-2
c1.7,0,3.2,0.4,4.3,1.2c1.1,0.8,1.8,2.2,2,4.1H310.9z"/>
<path fill="#0E7077" d="M324.8,468.7c0,0,0.2,0,0.3,0v3.7c-0.2,0-0.4,0-0.6-0.1s-0.3,0-0.4,0c-1.5,0-2.4,0.5-2.9,1.4
c-0.3,0.5-0.4,1.4-0.4,2.5v6.6h-3.6V469h3.5v2.4c0.6-0.9,1-1.6,1.5-1.9c0.7-0.6,1.6-0.9,2.6-0.9
C324.7,468.7,324.7,468.7,324.8,468.7z"/>
<path fill="#0E7077" d="M330.9,467.3h-3.7V464h3.7V467.3z M327.3,469h3.7v13.8h-3.7V469z"/>
<path fill="#0E7077" d="M346.1,470.5c1.1,1.2,1.7,2.9,1.7,5.3c0,2.4-0.6,4.3-1.7,5.6s-2.5,1.9-4.3,1.9c-1.1,0-2-0.3-2.8-0.8
c-0.4-0.3-0.8-0.7-1.2-1.3v7.2h-3.6V469h3.5v2c0.4-0.6,0.8-1.1,1.3-1.4c0.8-0.6,1.8-0.9,2.9-0.9C343.6,468.7,345,469.3,346.1,470.5
z M343.3,473.1c-0.5-0.8-1.3-1.2-2.4-1.2c-1.3,0-2.2,0.6-2.7,1.9c-0.3,0.7-0.4,1.5-0.4,2.5c0,1.6,0.4,2.7,1.3,3.4
c0.5,0.4,1.1,0.6,1.8,0.6c1,0,1.8-0.4,2.3-1.2c0.5-0.8,0.8-1.8,0.8-3.1C344,474.8,343.8,473.9,343.3,473.1z"/>
<path fill="#0E7077" d="M348.9,471.7v-2.6h1.9v-3.9h3.6v3.9h2.2v2.6h-2.2v7.3c0,0.6,0.1,0.9,0.2,1.1c0.1,0.1,0.6,0.2,1.3,0.2
c0.1,0,0.2,0,0.3,0c0.1,0,0.2,0,0.4,0v2.7L355,483c-1.7,0.1-2.9-0.2-3.5-0.9c-0.4-0.4-0.6-1.1-0.6-1.9v-8.5H348.9z"/>
<path fill="#0E7077" d="M369,478.4c0.1,0.6,0.2,1.1,0.5,1.4c0.5,0.5,1.3,0.7,2.5,0.7c0.7,0,1.3-0.1,1.7-0.3s0.6-0.5,0.6-1
c0-0.4-0.2-0.7-0.5-0.9c-0.3-0.2-1.6-0.6-3.8-1.1c-1.6-0.4-2.7-0.9-3.3-1.5c-0.6-0.6-1-1.4-1-2.5c0-1.3,0.5-2.4,1.5-3.3
c1-0.9,2.4-1.4,4.2-1.4c1.7,0,3.1,0.3,4.2,1c1.1,0.7,1.7,1.9,1.9,3.6H374c-0.1-0.5-0.2-0.8-0.4-1.1c-0.4-0.5-1.1-0.7-2-0.7
c-0.8,0-1.4,0.1-1.7,0.4c-0.3,0.2-0.5,0.5-0.5,0.9c0,0.4,0.2,0.7,0.5,0.9c0.4,0.2,1.6,0.5,3.8,1c1.4,0.3,2.5,0.9,3.2,1.5
c0.7,0.7,1.1,1.6,1.1,2.6c0,1.4-0.5,2.5-1.5,3.4c-1,0.9-2.6,1.3-4.7,1.3c-2.2,0-3.8-0.5-4.8-1.4c-1-0.9-1.6-2.1-1.6-3.5H369z"/>
<path fill="#0E7077" d="M392.2,481.1c-1.2,1.4-2.9,2.2-5.3,2.2s-4.2-0.7-5.3-2.2c-1.2-1.4-1.8-3.2-1.8-5.2c0-2,0.6-3.7,1.8-5.2
s2.9-2.2,5.3-2.2s4.2,0.7,5.3,2.2s1.8,3.2,1.8,5.2C394,478,393.4,479.7,392.2,481.1z M389.3,479.1c0.6-0.8,0.9-1.8,0.9-3.2
c0-1.4-0.3-2.5-0.9-3.2s-1.4-1.1-2.4-1.1s-1.9,0.4-2.4,1.1s-0.9,1.8-0.9,3.2c0,1.4,0.3,2.5,0.9,3.2s1.4,1.1,2.4,1.1
S388.8,479.9,389.3,479.1z"/>
<path fill="#0E7077" d="M413.8,469c0.6,0.2,1.1,0.7,1.6,1.2c0.4,0.5,0.7,1.1,0.8,1.8c0.1,0.5,0.1,1.1,0.1,2l0,8.7h-3.7V474
c0-0.5-0.1-1-0.3-1.3c-0.3-0.6-0.9-1-1.8-1c-1,0-1.7,0.4-2.1,1.2c-0.2,0.4-0.3,1-0.3,1.6v8.2h-3.6v-8.2c0-0.8-0.1-1.4-0.3-1.8
c-0.3-0.7-0.9-1-1.8-1c-1,0-1.7,0.3-2.1,1c-0.2,0.4-0.3,0.9-0.3,1.7v8.3h-3.7V469h3.5v2c0.4-0.7,0.9-1.2,1.3-1.5
c0.7-0.5,1.6-0.8,2.7-0.8c1.1,0,1.9,0.2,2.6,0.7c0.5,0.4,0.9,1,1.2,1.7c0.5-0.8,1.1-1.4,1.8-1.8c0.7-0.4,1.6-0.6,2.5-0.6
C412.6,468.7,413.2,468.8,413.8,469z"/>
<path fill="#0E7077" d="M428.7,469.3c1,0.4,1.8,1.1,2.4,2c0.6,0.8,0.9,1.8,1.1,2.9c0.1,0.6,0.1,1.6,0.1,2.7h-10.1
c0.1,1.4,0.5,2.4,1.4,2.9c0.6,0.3,1.2,0.5,2,0.5c0.8,0,1.5-0.2,2-0.6c0.3-0.2,0.5-0.5,0.7-1h3.7c-0.1,0.8-0.5,1.7-1.3,2.5
c-1.2,1.3-3,2-5.2,2c-1.8,0-3.5-0.6-4.9-1.7c-1.4-1.1-2.1-3-2.1-5.5c0-2.4,0.6-4.2,1.9-5.5s2.9-1.9,4.9-1.9
C426.6,468.6,427.7,468.8,428.7,469.3z M423.3,472.4c-0.5,0.5-0.8,1.2-1,2.1h6.2c-0.1-1-0.4-1.7-1-2.2c-0.6-0.5-1.3-0.7-2.1-0.7
C424.5,471.6,423.8,471.9,423.3,472.4z"/>
<path fill="#0E7077" d="M432.7,471.7v-2.6h1.9v-3.9h3.6v3.9h2.2v2.6h-2.2v7.3c0,0.6,0.1,0.9,0.2,1.1c0.1,0.1,0.6,0.2,1.3,0.2
c0.1,0,0.2,0,0.3,0c0.1,0,0.2,0,0.4,0v2.7l-1.7,0.1c-1.7,0.1-2.9-0.2-3.5-0.9c-0.4-0.4-0.6-1.1-0.6-1.9v-8.5H432.7z"/>
<path fill="#0E7077" d="M452.7,469.1c0.7,0.3,1.3,0.8,1.7,1.4c0.4,0.5,0.6,1,0.7,1.6c0.1,0.5,0.1,1.4,0.1,2.7v8.1h-3.7v-8.4
c0-0.7-0.1-1.3-0.4-1.8c-0.3-0.6-0.9-1-1.9-1c-0.9,0-1.7,0.3-2.2,1s-0.7,1.6-0.7,2.7v7.5h-3.6v-18.6h3.6v6.6
c0.5-0.8,1.1-1.4,1.8-1.7c0.7-0.3,1.4-0.5,2.2-0.5C451.3,468.6,452,468.8,452.7,469.1z"/>
<path fill="#0E7077" d="M462.4,467.3h-3.7V464h3.7V467.3z M458.7,469h3.7v13.8h-3.7V469z"/>
<path fill="#0E7077" d="M477.1,469.8c0.9,0.8,1.4,2,1.4,3.7v9.3h-3.7v-8.4c0-0.7-0.1-1.3-0.3-1.7c-0.4-0.7-1-1.1-2-1.1
c-1.2,0-2.1,0.5-2.5,1.6c-0.2,0.5-0.4,1.3-0.4,2.1v7.5h-3.6V469h3.5v2c0.5-0.7,0.9-1.2,1.3-1.5c0.7-0.6,1.7-0.8,2.8-0.8
C475,468.7,476.2,469,477.1,469.8z"/>
<path fill="#0E7077" d="M488.6,469c0.9,0.4,1.6,1,2.1,2v-2h3.5v13.1c0,1.8-0.3,3.1-0.9,4c-1,1.6-3,2.3-5.9,2.3
c-1.8,0-3.2-0.3-4.3-1s-1.7-1.7-1.9-3.1h3.9c0.1,0.4,0.3,0.7,0.5,0.9c0.4,0.3,1,0.5,2,0.5c1.3,0,2.2-0.4,2.6-1.3
c0.3-0.6,0.4-1.5,0.4-2.8v-0.9c-0.3,0.6-0.7,1-1.1,1.3c-0.7,0.5-1.7,0.8-2.8,0.8c-1.8,0-3.2-0.6-4.3-1.9c-1.1-1.2-1.6-2.9-1.6-5.1
c0-2.1,0.5-3.8,1.5-5.2c1-1.4,2.5-2.1,4.3-2.1C487.5,468.7,488.1,468.8,488.6,469z M489.9,478.9c0.6-0.6,0.9-1.7,0.9-3.1
c0-1.3-0.3-2.3-0.8-3s-1.3-1-2.2-1c-1.3,0-2.1,0.6-2.6,1.8c-0.3,0.6-0.4,1.4-0.4,2.3c0,0.8,0.1,1.5,0.4,2.1
c0.5,1.2,1.4,1.8,2.6,1.8C488.6,479.9,489.3,479.5,489.9,478.9z"/>
</g>
<rect x="283.6" y="631.8" fill="none" width="213" height="116.2"/>
<text transform="matrix(1 0 0 1 283.6183 641.8741)"><tspan x="0" y="0" fill="#666666" font-family="'Helvetica'" font-size="14">Write a script; were always </tspan><tspan x="0" y="19" fill="#666666" font-family="'Helvetica'" font-size="14">adding new features. </tspan><tspan x="0" y="38" fill="#666666" font-family="'Helvetica'" font-size="14">Cmnd/Cntrl+J will launch a </tspan><tspan x="0" y="57" fill="#666666" font-family="'Helvetica'" font-size="14">Running Scripts dialog to help </tspan><tspan x="0" y="76" fill="#666666" font-family="'Helvetica'" font-size="14">manage your scripts.</tspan></text>
<g>
<g id="Your_Icon">
<g>
<path fill="#333333" d="M646.2,223.8c-4.3-4.7-10.5-7.5-16.9-7.5c-6.4,0-12.6,2.7-16.9,7.5l0.9,1.1c-3.5,3.8-5.9,8.8-5.9,14.4
V259h3.7v-19.7c0-4.7,1.9-8.9,4.8-12.1l1.2,1.1c3.1-3.4,7.5-5.3,12.1-5.3c4.6,0,9,1.9,12.1,5.3l0.8-1.1c2.9,3.2,4.4,7.5,4.4,12.2
V259h3.7v-19.7c0-5.5-2-10.6-5.4-14.4L646.2,223.8z"/>
<path fill="#333333" d="M651.4,237.2v21.7c4.3,0,6.7-3,6.7-6.8V244C658.2,240.2,655.7,237.2,651.4,237.2z"/>
<path fill="#333333" d="M626.8,266.4H622c-2,0-3.7,1.7-3.7,3.7c0,0.4,0.1,0,0.2,1.2h-8.4c-3.3,0-6.4-3.1-6.4-6.4v-6.3
c0.6,0.2,1.2,0.4,2.4,0.4v-21.7c-4.3,0-7.3,3-7.3,6.8v8.1c0,2,1.2,3.7,2.4,5v7.8c0,4.6,4.2,8.9,8.9,8.9H622h0.1h4.8
c2,0,3.7-1.7,3.7-3.7C630.5,268,628.8,266.4,626.8,266.4z"/>
</g>
</g>
<g>
<path fill="none" stroke="#739898" stroke-miterlimit="10" d="M709,303.5H558.2c-5.5,0-9.7-5-9.7-10.5V187"/>
</g>
</g>
<g>
<g>
<path fill="none" stroke="#739898" stroke-miterlimit="10" d="M178,303.5H27.5c-5.5,0-10-5-10-10.5V187"/>
</g>
</g>
<g>
<path fill="none" stroke="#739898" stroke-miterlimit="10" d="M709,614.5H558.2c-5.5,0-9.7-5-9.7-10.5V498"/>
</g>
<g>
<path fill="none" stroke="#739898" stroke-miterlimit="10" d="M178,614.5H27.5c-5.5,0-10-5-10-10.5V498"/>
</g>
<g>
<path id="Fill-1" sketch:type="MSShapeGroup" fill="#333333" d="M678.6,526.4h-26.9v1c0.3,0,0.6,0,0.5,0c0.3,0,0.7,0.2,0.9,0.3
c0.3,0.2,5.8,2.5,6.3,2.7c0.4,0.3,0.2,0.7,0,1.1s-0.6,1.6-0.7,1.9c-0.1,0.3-0.2,0.4-0.7,0.4h-7.9h-0.4h-7.9c-0.4,0-0.5-0.1-0.7-0.4
c-0.1-0.3-0.5-1.5-0.7-1.9s-0.4-0.9,0-1.1c0.4-0.3,5.9-2.6,6.3-2.7c0.3-0.1,0.6-0.3,0.9-0.3c-0.1,0,0.3,0,0.7,0v-1h-27
c-1.1,0-2-0.9-2-2v-4.2c0-1.1,0.9-2,2-2h57.3c1.1,0,2,0.9,2,2v4.2C680.6,525.5,679.7,526.4,678.6,526.4L678.6,526.4L678.6,526.4
L678.6,526.4z M635.2,520.2c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2s2-0.9,2-2C637.2,521,636.3,520.2,635.2,520.2L635.2,520.2
L635.2,520.2L635.2,520.2z M641.1,521.7c-0.2,0-0.4,0.2-0.4,0.4c0,0.2,0.2,0.4,0.4,0.4c0.2,0,0.4-0.2,0.4-0.4
C641.5,521.9,641.3,521.7,641.1,521.7L641.1,521.7L641.1,521.7L641.1,521.7z M646.8,520.2c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2
c1.1,0,2-0.9,2-2C648.8,521,647.9,520.2,646.8,520.2L646.8,520.2L646.8,520.2L646.8,520.2z M652.8,520.2c-1.1,0-2,0.9-2,2
c0,1.1,0.9,2,2,2c1.1,0,2-0.9,2-2C654.8,521,653.9,520.2,652.8,520.2L652.8,520.2z"/>
</g>
<g>
<path id="Fill-5" sketch:type="MSShapeGroup" fill="#333333" d="M644.1,589c0,3.1-2.5,5.6-5.6,5.6h-62.8c-3.1,0-5.6-2.5-5.6-5.6
v-42.1c0-3.1,2.5-5.6,5.6-5.6h62.8c3.1,0,5.6,2.5,5.6,5.6V589"/>
<path id="Fill-6" sketch:type="MSShapeGroup" fill="#4D4D4D" d="M597.2,585.5c-1.5,0-2.8-1.2-2.8-2.8c0-1.5,1.2-2.8,2.8-2.8
c1.5,0,2.8,1.2,2.8,2.8C600,584.3,598.8,585.5,597.2,585.5 M597.3,578.7c-0.4,0-0.8,0-1.2,0.1c-0.8,0.1-1.6,0.4-2.3,0.8
c-1.1,0.6-2.2,1.4-3.1,2.3c-0.2,0.2-0.3,0.3-0.5,0.5c-0.2,0.2-0.2,0.5,0,0.8c0.5,0.5,1,1,1.6,1.5c1.2,1,2.6,1.7,4.1,2.1
c0.5,0.1,0.9,0.1,1.4,0.1c0.4,0,0.8,0,1.2-0.1c0.8-0.1,1.6-0.4,2.3-0.8c1.1-0.6,2.2-1.4,3.1-2.3c0.2-0.2,0.3-0.3,0.5-0.5
c0.1-0.1,0.1-0.2,0.1-0.3l0,0l0,0l0,0l0,0v0l0,0v0l0,0l0,0l0,0v0c0-0.1-0.1-0.2-0.1-0.3c-0.5-0.5-1-1-1.6-1.5
c-1.2-1-2.6-1.8-4.1-2.1C598.2,578.7,597.7,578.7,597.3,578.7"/>
<path id="Fill-7" sketch:type="MSShapeGroup" fill="#4D4D4D" d="M597.2,581.8c-0.5,0-0.9,0.4-0.9,0.9c0,0.5,0.4,0.9,0.9,0.9
c0.5,0,0.9-0.4,0.9-0.9C598.2,582.2,597.8,581.8,597.2,581.8"/>
<path id="Fill-8" sketch:type="MSShapeGroup" fill="#FFFFFF" d="M644.1,588.4v0.6c0,0.3,0,0.6-0.1,0.9l0.5-0.1
C644.4,589.4,644.3,588.9,644.1,588.4 M644,545.6c0.1,0.2,0.1,0.5,0.1,0.7c0.2-0.1,0.2-0.2,0.3-0.2L644,545.6"/>
<path id="Fill-9" sketch:type="MSShapeGroup" fill="#4D4D4D" d="M643.9,545.5c0,0-0.1,0.1-0.3,0.2c-1.3,0.8-7.4,4.2-20,8.8
l-0.6,0.2l0.4,0.5c11.1,13,18.1,26.9,20.2,35l0.3-0.1c0.1-0.3,0.1-0.6,0.1-0.9v-0.6c-2.5-8.3-9.3-21.1-19.6-33.4
c12.4-4.5,18.3-7.9,19.6-8.6c0-0.2-0.1-0.5-0.1-0.7L643.9,545.5"/>
<path id="Fill-10_1_" sketch:type="MSShapeGroup" fill="#FFFFFF" d="M570,588.4c-0.2,0.5-0.3,1-0.4,1.4l0.5,0.1
c-0.1-0.3-0.1-0.6-0.1-0.9V588.4 M570.1,545.6l-0.4,0.6c0,0,0.1,0.1,0.3,0.2C570.1,546.1,570.1,545.8,570.1,545.6"/>
<path id="Fill-11" sketch:type="MSShapeGroup" fill="#4D4D4D" d="M570.2,545.5l-0.1,0.1c-0.1,0.2-0.1,0.5-0.1,0.7
c1.2,0.8,7.2,4.2,19.6,8.6c-10.3,12.3-17.1,25.1-19.6,33.4v0.6c0,0.3,0,0.6,0.1,0.9l0.3,0.1c2.1-8.1,9.1-21.9,20.2-35l0.4-0.5
l-0.6-0.2c-7.2-2.6-12.3-4.8-15.5-6.4c-1.6-0.8-2.8-1.4-3.6-1.9c-0.4-0.2-0.7-0.4-0.9-0.5C570.3,545.5,570.2,545.5,570.2,545.5"/>
</g>
<g>
<g>
<g>
<path fill="#FFFFFF" d="M679.8,599.1c-0.8,0.2-1.3,0.3-1.9,0.6c-0.7-0.1-1.4-0.3-1.4-0.3c-5.1-10.7-6.7-15.8-7-16.9
c-0.4-1.1-2.4-9.3-3.3-12.8c-0.9-3.5-2.3-7.3-2.3-7.3c-0.3-1.1-0.7-1.9-0.8-2.3c-0.7-3.2-0.1-6.9,0.2-8.4
c1.3-1.4,1.9-1.6,2.2-1.8c0,0,0.9-0.4,2.5-0.8"/>
<path fill="#333333" d="M678,600.2l-0.1,0c-0.7-0.1-1.5-0.3-1.5-0.3l-0.3-0.1l-0.1-0.3c-2.2-4.7-5.3-11.5-7-16.8l0-0.1
c-0.2-0.7-1-3.8-1.9-7.2c-0.5-2.1-1.1-4.3-1.5-5.7c-0.9-3.4-2.3-7.2-2.3-7.2l0-0.1c-0.1-0.6-0.4-1.1-0.6-1.6
c-0.1-0.3-0.2-0.6-0.3-0.8c-0.8-3.2-0.1-7.1,0.2-8.6l0-0.2l0.1-0.1c1.3-1.4,1.9-1.7,2.3-1.9l0.1,0c0.1,0,1-0.4,2.6-0.8l0.3,1.2
c-1.5,0.4-2.3,0.7-2.3,0.7l0,0c-0.3,0.2-0.8,0.4-1.9,1.5c-0.3,1.5-0.8,5-0.2,7.9c0,0.1,0.1,0.3,0.2,0.6c0.2,0.4,0.4,1,0.6,1.7
c0.1,0.4,1.4,4,2.3,7.3c0.4,1.4,0.9,3.6,1.5,5.7c0.8,3.2,1.6,6.5,1.8,7.1l0,0.1c1.7,5.2,4.6,11.7,6.8,16.4
c0.2,0.1,0.6,0.1,0.9,0.2c0.5-0.2,1-0.3,1.6-0.5l0.2-0.1l0.3,1.2l-0.2,0.1c-0.7,0.2-1.1,0.3-1.6,0.5L678,600.2z"/>
</g>
<g>
<path fill="#FFFFFF" d="M679.1,599.2c0.8-0.2,1.3-0.3,2-0.4c0.6-0.4,1.2-0.9,1.2-0.9c-0.2-11.8-1.1-17.1-1.3-18.2
c-0.2-1.1-2-9.4-2.8-13c-0.7-3.5-1.2-7.6-1.2-7.6c-0.3-1.1-0.2-2-0.3-2.5c-0.7-3.2-3-6.2-3.9-7.4c-1.8-0.6-2.4-0.6-2.8-0.6
c0,0-1,0-2.5,0.4"/>
<path fill="#333333" d="M679.2,599.8l-0.3-1.2l0.2-0.1c0.7-0.2,1.1-0.3,1.7-0.3c0.3-0.2,0.6-0.4,0.8-0.6
c-0.1-5.2-0.4-12.3-1.2-17.7l0-0.1c-0.1-0.7-0.8-4-1.5-7.2c-0.5-2.1-0.9-4.3-1.2-5.7c-0.7-3.4-1.2-7.2-1.2-7.6
c-0.2-0.7-0.2-1.3-0.2-1.8c0-0.3,0-0.5-0.1-0.6c-0.7-2.9-2.8-7-3.7-8.2c-1.5-0.5-2-1.7-2.3-1.7H670c0,0-0.9,1.2-2.4,1.6l-0.3-1.2
c1.6-0.4,2.6-0.4,2.7-0.4h0.1c0.4,0,1.1,0.6,2.9,1.2l0.2,0.4l0.1,0.3c0.9,1.2,3.2,4.4,4,7.7c0.1,0.2,0.1,0.6,0.1,0.9
c0,0.5,0.1,1,0.2,1.7l0,0.1c0,0,0.5,4,1.2,7.5c0.3,1.4,0.8,3.6,1.2,5.7c0.7,3.4,1.4,6.6,1.5,7.3l0,0.1c0.8,5.6,1.1,12.9,1.2,18.2
l0,0.3l-0.2,0.2c0,0-0.6,0.5-1.3,0.9l-0.1,0.1l-0.1,0c-0.6,0-1,0.1-1.6,0.3L679.2,599.8z"/>
</g>
</g>
<g>
<path fill="#666666" d="M668.3,555.6c-1.5,0-2.9-1-3.2-2.3c-0.4-1.7,0.8-3.4,2.6-3.8c0.3-0.1,0.6-0.1,0.9-0.1c1.5,0,2.9,1,3.2,2.3
c0.2,0.8,0,1.7-0.5,2.4c-0.5,0.7-1.2,1.2-2.1,1.4C668.9,555.6,668.6,555.6,668.3,555.6z M668.6,550.2c-0.2,0-0.5,0-0.7,0.1
c-1.4,0.3-2.3,1.6-2,2.8c0.2,1,1.2,1.7,2.4,1.7c0.2,0,0.5,0,0.7-0.1c0.7-0.2,1.3-0.6,1.7-1.1c0.4-0.5,0.5-1.2,0.4-1.7
C670.8,550.9,669.8,550.2,668.6,550.2z"/>
</g>
<g>
<path fill="#FFFFFF" d="M654.1,616.5c0,0,4.5-11.5,4.5-22.4c0-8.8-4-24-3.8-25.1c0.2-1.1,2-15.3,2.3-16c0.3-0.7,0.5-1.4,1.7-1.9
c1.1-0.5,6.4-3.8,7-4c0.6-0.1,5.8-1.4,7.9-0.2c1.4,0.8-0.3,2.4-0.3,2.4s-1.5-1.7-4.4-1c-3,0.7-5.4,1.9-6,3.1
c-0.6,1.2-0.2,5.3-0.2,7c0,1.7,0.7,5.7,2.7,9.8c1.2,2.4,0.5,1.3,2,2.4s4,3.5,10.2-3.9c5.6-6.6,6.8-7.5,8.8-6.1
c2,1.4,0.7,4.7,0.4,6.8s-1.1,1.7-2,3.2c-0.9,1.5-2.9,4.1-2.8,10.3c0.1,8.4-0.8,10-5.3,19.1c-2.4,6.6-6.7,26.2-6.7,26.2
L654.1,616.5z"/>
</g>
<g>
<path fill="#333333" d="M654.5,616.6l-0.8-0.3c0-0.1,2.4-6.7,3.7-13.6c1.1-5.8,0.8-8.6,0.6-10.7c0-0.4-0.1-0.8-0.1-1.2
c-0.2-2.4-1.9-14.3-2.4-16.5c-0.7-2.8-1.1-4.7-1-5.3c0-0.2,0.1-0.7,0.2-1.4c0.3-1.5,0.7-4.1,1.1-7.3c0.2-1.7,0.3-3,0.4-3.9
c0.2-1.7,0.3-2.3,0.3-2.6c0,0,0-0.1,0-0.2c0-0.6,0.1-1.9,1.9-2.7c1.1-0.5,3.1-1.6,4.7-2.6c1.2-0.7,2.1-1.2,2.6-1.4
c1.2-0.5,3.4-0.7,5-0.8c1.4,0,2.9,0,3.7,0.8c0.3,0.3,0.5,0.8,0.5,1.3l0,0.1l-0.1,0.1c-0.3,0.6-0.6,1-1,0.8
c-0.2-0.1-0.3-0.3-0.2-0.6l0.8,0.2c0-0.1,0-0.2-0.2-0.3c-0.2-0.1-0.3,0-0.4,0c0,0,0.1-0.1,0.3-0.4c0-0.2-0.1-0.4-0.2-0.6
c-0.5-0.6-2-0.6-3.1-0.5c-2.2,0.1-3.9,0.3-4.7,0.7c-0.4,0.2-1.4,0.7-2.5,1.4c-1.6,0.9-3.6,2.1-4.7,2.6c-1.3,0.6-1.4,1.5-1.4,2
c0,0.1,0,0.3,0,0.4c-0.1,0.3-0.1,0.9-0.3,2.5c-0.1,0.9-0.2,2.2-0.5,3.9c-0.4,3.3-0.8,5.8-1.1,7.4c-0.1,0.6-0.2,1.1-0.2,1.3
c-0.1,0.6,0.5,3.1,1,5.1c0.5,2.2,2.3,14.2,2.5,16.7c0,0.4,0.1,0.8,0.1,1.2c0.2,2.2,0.5,5-0.6,11
C656.9,609.8,654.5,616.5,654.5,616.6z"/>
</g>
<g>
<path fill="#333333" d="M670.5,626.2l-0.8-0.1c0.1-1.5,5.3-23.8,8-28.9c0.3-0.5,0.5-1,0.7-1.4c2.1-4,3.1-5.9,3.2-11.5
c-0.1-7.8,0.9-9.8,1.4-10.8c0.2-0.3,0.2-0.5,0.3-0.7c0.1-0.7,0.9-1.7,1.7-2.6c0.7-0.8,1.6-1.8,1.5-2.3c-0.1-1.1,0.4-2.9,0.5-3.6
c0-0.1,0-0.2,0.1-0.2c0-0.1,0.1-0.2,0.1-0.3c0.2-0.5,0.5-1.2-0.5-2.3c-1.2-1.3-2.2-1.1-3.7-0.2c-0.4,0.4-3.3,3.6-5.3,5.8
c-2.2,2.4-4.9,5.5-8.7,5.3l0-0.8c3.4,0.2,6-2.7,8.1-5c2.2-2.4,5.3-5.9,5.3-5.9l0.1-0.1c1.8-1.1,3.2-1.4,4.8,0.3
c1.4,1.5,0.9,2.6,0.7,3.2c0,0.1-0.1,0.1-0.1,0.2c0,0.1,0,0.1-0.1,0.2c-0.2,0.6-0.6,2.3-0.5,3.3c0.1,0.8-0.8,1.8-1.7,2.8
c-0.7,0.8-1.5,1.7-1.5,2.2c0,0.4-0.2,0.7-0.4,1c-0.5,1-1.4,2.8-1.3,10.4c-0.2,5.8-1.2,7.7-3.3,11.9c-0.2,0.4-0.5,0.9-0.7,1.4
C675.9,602.5,670.6,624.7,670.5,626.2z"/>
</g>
<g>
<path fill="#333333" d="M669.6,571.9c-0.9-0.1-6.1-3.2-6.2-3.3l0.4-0.7c1.6,1,5.3,3.1,5.8,3.1L669.6,571.9z"/>
</g>
<g>
<path fill="#333333" d="M666.1,569.9c-0.4-1.7-2.7-6.2-2.7-6.2l0.4-0.2c0,0,2.3,4.5,2.7,6.3L666.1,569.9z"/>
</g>
<g>
<path fill="#333333" d="M684.2,563.6c-1-0.9-2.1-0.7-3.1-0.4l-0.1-0.4c1-0.2,2.3-0.5,3.4,0.5L684.2,563.6z"/>
</g>
<g>
<path fill="#333333" d="M686.5,564.4L686.5,564.4c-0.8,0-1.2-0.6-1.3-0.6c0,0-0.8-0.7-1.4-1.6c-0.4-0.6-0.4-1-0.3-1.3
c0.1-0.4,0.4-0.6,0.6-0.7c0.4-0.2,0.7-0.2,1.1-0.2c0.7,0,1.2,0.4,1.9,0.9l0.1,0.1c1.1,0.9,1,2.1,0.5,2.9l-0.1,0.1
C687.3,564.2,686.9,564.4,686.5,564.4z M685.2,560.4c-0.3,0-0.6,0.1-0.9,0.2c-0.1,0.1-0.3,0.2-0.4,0.4c-0.1,0.2-0.1,0.5,0.2,1
c0.6,0.9,1.4,1.5,1.4,1.5c0,0,0.4,0.5,1,0.5c0.3,0,0.6-0.1,0.9-0.3c0.2-0.4,0.7-1.5-0.4-2.3l-0.1-0.1
C686.2,560.7,685.8,560.4,685.2,560.4z"/>
</g>
</g>
<g>
<path id="Fill-1_4_" sketch:type="MSShapeGroup" fill="#333333" d="M31.7,247.5c0,4.5,3.6,8.1,8.1,8.1H54c4.5,0,8.1-3.6,8.1-8.1
v-14.2c0-4.5-3.6-8.1-8.1-8.1H39.8c-4.5,0-8.1,3.6-8.1,8.1V247.5"/>
<path id="Fill-2_4_" sketch:type="MSShapeGroup" fill="#FFFFFF" d="M39.8,240.4l8.5-5.7v11.4L39.8,240.4"/>
<path id="Fill-3_4_" sketch:type="MSShapeGroup" fill="#333333" d="M88,211.6c0,4.5-3.6,8.1-8.1,8.1H65.6c-4.5,0-8.1-3.6-8.1-8.1
v-14.2c0-4.5,3.6-8.1,8.1-8.1h14.2c4.5,0,8.1,3.6,8.1,8.1V211.6"/>
<path id="Fill-4_3_" sketch:type="MSShapeGroup" fill="#676767" d="M123.6,211.6c0,4.5-3.6,8.1-8.1,8.1h-14.2
c-4.5,0-8.1-3.6-8.1-8.1v-14.2c0-4.5,3.6-8.1,8.1-8.1h14.2c4.5,0,8.1,3.6,8.1,8.1V211.6"/>
<path id="Fill-5_3_" sketch:type="MSShapeGroup" fill="#333333" d="M97.8,247.5c0,4.5-3.6,8.1-8.1,8.1H75.4c-4.5,0-8.1-3.6-8.1-8.1
v-14.2c0-4.5,3.6-8.1,8.1-8.1h14.2c4.5,0,8.1,3.6,8.1,8.1V247.5"/>
<path id="Fill-6_4_" sketch:type="MSShapeGroup" fill="#333333" d="M133.4,247.5c0,4.5-3.6,8.1-8.1,8.1H111c-4.5,0-8.1-3.6-8.1-8.1
v-14.2c0-4.5,3.6-8.1,8.1-8.1h14.2c4.5,0,8.1,3.6,8.1,8.1V247.5"/>
<path id="Fill-7_3_" sketch:type="MSShapeGroup" fill="#676767" d="M150,283.2c0,4.5-3.6,8.1-8.1,8.1h-14.2c-4.5,0-8.1-3.6-8.1-8.1
V269c0-4.5,3.6-8.1,8.1-8.1h14.2c4.5,0,8.1,3.6,8.1,8.1V283.2"/>
<text transform="matrix(1 0 0 1 36.5368 235.1614)" fill="#FFFFFF" font-family="'Futura-Medium'" font-size="7.1837">A</text>
<text transform="matrix(1 0 0 1 123.0293 270.1382)" fill="#FFFFFF" font-family="'Futura-Medium'" font-size="7.1837">C</text>
<text transform="matrix(1 0 0 1 107.8522 235.1614)" fill="#FFFFFF" font-family="'Futura-Medium'" font-size="7.1837">D</text>
<text transform="matrix(1 0 0 1 72.7347 235.1614)" fill="#FFFFFF" font-family="'Futura-Medium'" font-size="7.1837">S</text>
<text transform="matrix(1 0 0 1 62.5311 198.6201)" fill="#FFFFFF" font-family="'Futura-Medium'" font-size="7.1837" letter-spacing="3">W</text>
<path id="Fill-8_2_" sketch:type="MSShapeGroup" fill="#FFFFFF" d="M109.1,199.9c-4.8,3.9-7.9,6.3-7.9,6.3l3.4,1.1L109.1,199.9"/>
<path id="Fill-9_2_" sketch:type="MSShapeGroup" fill="#FFFFFF" d="M115.5,203.8c-0.2-0.3-0.6-0.4-1-0.2l-1.8,1.2l0.5-2.3
c0.1-0.4-0.2-0.7-0.5-0.8c-0.1,0-0.1,0-0.2,0l-1.8-1l0.5-1c0.1,0.5,0.3,0.9,0.7,1.2c0.8,0.5,1.9,0.3,2.4-0.5s0.3-1.9-0.5-2.4
c-0.5-0.3-1.1-0.4-1.7-0.2l2.1-3.8c0.2-0.3,0.1-0.8-0.3-0.9c-0.3-0.2-0.8-0.1-0.9,0.3l-4,7.1c0,0,0,0,0,0l-3.3,5.3l0,0
c0,0-0.1,0.1-0.1,0.1l-4.6,8.2c-0.3,0.5-0.1,1.1,0.4,1.4c0.1,0.1,0.2,0.1,0.3,0.1c0.4,0.1,0.9-0.1,1.1-0.5l4.5-8.1l0.7,0.5l-2,2.5
c-0.4,0.4-0.3,1.1,0.1,1.5c0.1,0.1,0.3,0.2,0.5,0.2c0.4,0.1,0.7-0.1,1-0.4l3-3.6c0.4-0.4,0.3-1.1-0.1-1.5c-0.1-0.1-0.1-0.1-0.2-0.1
l1-1.5l-0.3,1.5c-0.1,0.3,0.1,0.6,0.3,0.7c0.1,0.1,0.2,0.1,0.3,0.1c0.2,0,0.3,0,0.5-0.1l3.2-2.1
C115.6,204.5,115.7,204.1,115.5,203.8"/>
<path id="Fill-10_2_" sketch:type="MSShapeGroup" fill="#FFFFFF" d="M137.1,279c0.5-4.6,0.9-7.5,0.9-7.5l-2.4,1.2L137.1,279"/>
<path id="Fill-11_1_" sketch:type="MSShapeGroup" fill="#FFFFFF" d="M131.7,280.4c0.3,0.1,0.6-0.1,0.6-0.4l0.3-1.6l1,1.5
c0.2,0.2,0.5,0.3,0.7,0.1c0,0,0.1-0.1,0.1-0.1l1.5-0.4l0.2,0.8c-0.3-0.2-0.7-0.3-1-0.2c-0.7,0.1-1.2,0.8-1,1.5
c0.1,0.7,0.8,1.2,1.5,1c0.5-0.1,0.8-0.4,1-0.8l0.9,3.1c0.1,0.3,0.4,0.4,0.6,0.4s0.4-0.4,0.4-0.6l-1.6-5.9c0,0,0,0,0,0l-1.1-4.6h0
c0,0,0-0.1,0-0.1l-1.9-6.8c-0.1-0.4-0.5-0.6-1-0.5c-0.1,0-0.2,0.1-0.2,0.1c-0.3,0.2-0.4,0.5-0.3,0.8l1.9,6.7l-0.6,0.1l-0.2-2.4
c0-0.4-0.4-0.7-0.8-0.7c-0.1,0-0.3,0.1-0.4,0.1c-0.2,0.2-0.3,0.4-0.3,0.7l0.3,3.5c0,0.4,0.4,0.7,0.8,0.7c0.1,0,0.1,0,0.2,0l0.2,1.3
l-0.6-0.9c-0.1-0.2-0.3-0.3-0.5-0.2c-0.1,0-0.1,0-0.2,0.1c-0.1,0.1-0.2,0.2-0.2,0.3l-0.6,2.8C131.2,280,131.4,280.3,131.7,280.4"/>
<path id="Fill-12" sketch:type="MSShapeGroup" fill="#FFFFFF" d="M72.7,200.3l5.7,8.5H67.1L72.7,200.3"/>
<path id="Fill-13" sketch:type="MSShapeGroup" fill="#FFFFFF" d="M116.8,246.1v-11.4l8.5,5.7L116.8,246.1"/>
<path id="Fill-14" sketch:type="MSShapeGroup" fill="#FFFFFF" d="M82.5,246.1l-5.7-8.5h11.4L82.5,246.1"/>
<text transform="matrix(1 0 0 1 98.7506 198.62)" fill="#FFFFFF" font-family="'Futura-Medium'" font-size="7.1837" letter-spacing="3">E</text>
</g>
<g>
<g id="Your_Icon_1_">
<circle fill="#333333" cx="290.6" cy="504.6" r="1.9"/>
<circle fill="#333333" cx="296.9" cy="504.6" r="1.9"/>
<circle fill="#333333" cx="303.2" cy="504.6" r="1.9"/>
<path fill="#333333" d="M400.1,498.9H286c-1.3,0-2.4,1.1-2.4,2.4v110.3c0,1.3,1.1,2.4,2.4,2.4h114c1.3,0,2.4-1.1,2.4-2.4V501.4
C402.5,500,401.4,498.9,400.1,498.9z M286,612.8c-0.6,0-1.2-0.5-1.2-1.2v-33H362v34.2H286z M401.3,611.6c0,0.6-0.5,1.2-1.2,1.2
h-36.8V510.3h37.9V611.6z M401.3,509.1H284.9v-7.7c0-0.6,0.5-1.2,1.2-1.2h114c0.6,0,1.2,0.5,1.2,1.2V509.1z"/>
</g>
<g>
<path fill="#FFFFFF" d="M302.9,521.6v1.5h-2.5v9.2c0,1.2,0,2.4-0.6,3.6c-0.8,1.4-2.2,2.2-3.7,2.2c-1.4,0-2.8-0.6-3.8-1.8l1.2-1.5
c0.2,0.1,0,0.3,0.2,0.5c0.1,0.1,1.1,1.2,2.4,1.2c0.8,0,1.5-0.4,1.9-1c0.6-0.9,0.6-2.1,0.6-3.1v-9.3h-3.1v-1.5H302.9z"/>
<path fill="#FFFFFF" d="M314.2,525.1c-0.1,0-0.1-0.1-0.1-0.2c0-0.1,0-0.1-0.1-0.2c-0.6-0.9-1.8-1.6-3.3-1.6
c-1.9,0-2.9,1.1-2.9,2.4c0,0.7,0.3,1.6,1.7,2.4c1.5,0.9,3.8,1.3,5.2,2.7c1,1,1.3,2.1,1.3,2.9c0,2-1.5,4.7-5.4,4.7
c-2,0-3.8-0.7-5.1-2.1l1.1-1.9c0.1,0.1,0.1,0.2,0.1,0.2c0,0.1,0,0.2,0.1,0.3c0.7,0.9,2.1,1.8,3.9,1.8c2.4,0,3.5-1.5,3.5-2.9
c0-0.7-0.3-1.6-1.4-2.2c-1.2-0.8-3.6-1.3-5.1-2.4c-1.4-1-1.7-2.2-1.7-3.2c0-2.2,2-4.2,4.9-4.2c1.8,0,3.4,0.7,4.5,2L314.2,525.1z"
/>
</g>
</g>
</svg>
<div>

File diff suppressed because one or more lines are too long

View file

@ -536,22 +536,5 @@
height="44.57473"
x="84.498352"
y="1050.0748" />
<g
id="g3322"
transform="translate(-46.607143,-3.5714285)">
<use
x="0"
y="0"
width="744.09448"
height="1052.3622"
transform="translate(0.5864354,0.4607839)"
xlink:href="#rect899"
id="use1503" />
<path
d="m 75.506508,1023.3478 1.372845,5.4631 -14.094975,-1.152 2.35e-4,7.2772 14.094975,-1.152 -1.372845,5.1249 13.761293,-7.6113 -13.761293,-7.9499 z"
id="rect899"
inkscape:connector-curvature="0"
style="fill:#d3d3d3;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:0.77974033;stroke-linecap:round;stroke-linejoin:round" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -65,6 +65,7 @@
#include <HFBackEvent.h>
#include <LogHandler.h>
#include <MainWindow.h>
#include <ModelEntityItem.h>
#include <NetworkAccessManager.h>
#include <OctalCode.h>
#include <OctreeSceneStats.h>
@ -110,6 +111,7 @@
#include "scripting/AccountScriptingInterface.h"
#include "scripting/AudioDeviceScriptingInterface.h"
#include "scripting/ClipboardScriptingInterface.h"
#include "scripting/HMDScriptingInterface.h"
#include "scripting/JoystickScriptingInterface.h"
#include "scripting/GlobalServicesScriptingInterface.h"
#include "scripting/LocationScriptingInterface.h"
@ -138,6 +140,12 @@ static unsigned STARFIELD_SEED = 1;
const qint64 MAXIMUM_CACHE_SIZE = 10737418240; // 10GB
static QTimer* locationUpdateTimer = NULL;
static QTimer* balanceUpdateTimer = NULL;
static QTimer* silentNodeTimer = NULL;
static QTimer* identityPacketTimer = NULL;
static QTimer* billboardPacketTimer = NULL;
static QTimer* checkFPStimer = NULL;
static QTimer* idleTimer = NULL;
const QString CHECK_VERSION_URL = "https://highfidelity.io/latestVersion.xml";
@ -360,7 +368,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
// update our location every 5 seconds in the data-server, assuming that we are authenticated with one
const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000;
QTimer* locationUpdateTimer = new QTimer(this);
locationUpdateTimer = new QTimer(this);
connect(locationUpdateTimer, &QTimer::timeout, this, &Application::updateLocationInServer);
locationUpdateTimer->start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS);
@ -376,7 +384,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
const qint64 BALANCE_UPDATE_INTERVAL_MSECS = 5 * 1000;
QTimer* balanceUpdateTimer = new QTimer(this);
balanceUpdateTimer = new QTimer(this);
connect(balanceUpdateTimer, &QTimer::timeout, &accountManager, &AccountManager::updateBalance);
balanceUpdateTimer->start(BALANCE_UPDATE_INTERVAL_MSECS);
@ -415,18 +423,18 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent);
// move the silentNodeTimer to the _nodeThread
QTimer* silentNodeTimer = new QTimer();
silentNodeTimer = new QTimer();
connect(silentNodeTimer, SIGNAL(timeout()), nodeList.data(), SLOT(removeSilentNodes()));
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_MSECS);
silentNodeTimer->moveToThread(_nodeThread);
// send the identity packet for our avatar each second to our avatar mixer
QTimer* identityPacketTimer = new QTimer();
identityPacketTimer = new QTimer();
connect(identityPacketTimer, &QTimer::timeout, _myAvatar, &MyAvatar::sendIdentityPacket);
identityPacketTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS);
// send the billboard packet for our avatar every few seconds
QTimer* billboardPacketTimer = new QTimer();
billboardPacketTimer = new QTimer();
connect(billboardPacketTimer, &QTimer::timeout, _myAvatar, &MyAvatar::sendBillboardPacket);
billboardPacketTimer->start(AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS);
@ -525,13 +533,38 @@ void Application::aboutToQuit() {
}
void Application::cleanupBeforeQuit() {
// first stop all timers directly or by invokeMethod
// depending on what thread they run in
locationUpdateTimer->stop();
balanceUpdateTimer->stop();
QMetaObject::invokeMethod(silentNodeTimer, "stop", Qt::BlockingQueuedConnection);
identityPacketTimer->stop();
billboardPacketTimer->stop();
checkFPStimer->stop();
idleTimer->stop();
QMetaObject::invokeMethod(&_settingsTimer, "stop", Qt::BlockingQueuedConnection);
// and then delete those that got created by "new"
delete locationUpdateTimer;
delete balanceUpdateTimer;
delete silentNodeTimer;
delete identityPacketTimer;
delete billboardPacketTimer;
delete checkFPStimer;
delete idleTimer;
// no need to delete _settingsTimer here as it is no pointer
// save state
_settingsThread.quit();
saveSettings();
_window->saveGeometry();
// TODO: now that this is in cleanupBeforeQuit do we really need it to stop and force
// an event loop to send the packet?
UserActivityLogger::getInstance().close();
// let the avatar mixer know we're out
MyAvatar::sendKillAvatar();
// stop the AudioClient
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(),
@ -542,16 +575,12 @@ void Application::cleanupBeforeQuit() {
}
Application::~Application() {
EntityTree* tree = _entities.getTree();
tree->lockForWrite();
_entities.getTree()->setSimulation(NULL);
tree->unlock();
qInstallMessageHandler(NULL);
_window->saveGeometry();
// make sure we don't call the idle timer any more
delete idleTimer;
// let the avatar mixer know we're out
MyAvatar::sendKillAvatar();
// ask the datagram processing thread to quit and wait until it is done
_nodeThread->quit();
@ -563,6 +592,8 @@ Application::~Application() {
Menu::getInstance()->deleteLater();
_myAvatar = NULL;
ModelEntityItem::cleanupLoadedAnimations() ;
DependencyManager::destroy<GLCanvas>();
@ -622,9 +653,9 @@ void Application::initializeGL() {
_entityEditSender.initialize(_enableProcessOctreeThread);
// call our timer function every second
QTimer* timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), SLOT(timer()));
timer->start(1000);
checkFPStimer = new QTimer(this);
connect(checkFPStimer, SIGNAL(timeout()), SLOT(checkFPS()));
checkFPStimer->start(1000);
// call our idle function whenever we can
idleTimer = new QTimer(this);
@ -1420,7 +1451,7 @@ void Application::sendPingPackets() {
}
// Every second, check the frame rates and other stuff
void Application::timer() {
void Application::checkFPS() {
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
sendPingPackets();
}
@ -3485,6 +3516,10 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("UndoStack", &_undoStackScriptingInterface);
QScriptValue hmdInterface = scriptEngine->registerGlobalObject("HMD", &HMDScriptingInterface::getInstance());
scriptEngine->registerFunction(hmdInterface, "getHUDLookAtPosition2D", HMDScriptingInterface::getHUDLookAtPosition2D, 0);
scriptEngine->registerFunction(hmdInterface, "getHUDLookAtPosition3D", HMDScriptingInterface::getHUDLookAtPosition3D, 0);
#ifdef HAVE_RTMIDI
scriptEngine->registerGlobalObject("MIDI", &MIDIManager::getInstance());
#endif

View file

@ -111,7 +111,7 @@ static const float MIRROR_FIELD_OF_VIEW = 30.0f;
static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND;
static const QString INFO_HELP_PATH = "html/interface-welcome-allsvg.html";
static const QString INFO_HELP_PATH = "html/interface-welcome.html";
static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-entities-commands.html";
#ifdef Q_OS_WIN
@ -368,7 +368,7 @@ public slots:
private slots:
void clearDomainOctreeDetails();
void timer();
void checkFPS();
void idle();
void aboutToQuit();

View file

@ -129,12 +129,12 @@ void DatagramProcessor::processDatagrams() {
if (incomingType == PacketTypeMuteEnvironment) {
glm::vec3 position;
float radius, distance;
float radius;
int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment);
memcpy(&position, incomingPacket.constData() + headerSize, sizeof(glm::vec3));
memcpy(&radius, incomingPacket.constData() + headerSize + sizeof(glm::vec3), sizeof(float));
distance = glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(),
float distance = glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(),
position);
mute = mute && (distance < radius);

View file

@ -230,7 +230,7 @@ int AudioScope::addSilenceToScope(QByteArray* byteArray, int frameOffset, int si
int samplesToBufferEnd = _samplesPerScope - frameOffset;
if (silentSamples > samplesToBufferEnd) {
memset(destination + frameOffset, 0, samplesToBufferEnd * sizeof(int16_t));
memset(destination, 0, silentSamples - samplesToBufferEnd * sizeof(int16_t));
memset(destination, 0, (silentSamples - samplesToBufferEnd) * sizeof(int16_t));
} else {
memset(destination + frameOffset, 0, silentSamples * sizeof(int16_t));
}

View file

@ -688,7 +688,7 @@ void OculusManager::getEulerAngles(float& yaw, float& pitch, float& roll) {
roll = 0.0f;
#endif
}
glm::vec3 OculusManager::getRelativePosition() {
#if (defined(__APPLE__) || defined(_WIN32)) && HAVE_LIBOVR
ovrTrackingState trackingState = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds());

View file

@ -0,0 +1,56 @@
//
// HMDScriptingInterface.cpp
// interface/src/scripting
//
// Created by Thijs Wenker on 1/12/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "HMDScriptingInterface.h"
#include <avatar/AvatarManager.h>
HMDScriptingInterface& HMDScriptingInterface::getInstance() {
static HMDScriptingInterface sharedInstance;
return sharedInstance;
}
bool HMDScriptingInterface::getHUDLookAtPosition3D(glm::vec3& result) const {
Camera* camera = Application::getInstance()->getCamera();
glm::vec3 position = camera->getPosition();
glm::quat orientation = camera->getOrientation();
glm::vec3 direction = orientation * glm::vec3(0.0f, 0.0f, -1.0f);
ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay();
return applicationOverlay.calculateRayUICollisionPoint(position, direction, result);
}
QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) {
glm::vec3 hudIntersection;
if ((&HMDScriptingInterface::getInstance())->getHUDLookAtPosition3D(hudIntersection)) {
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
glm::vec3 sphereCenter = myAvatar->getDefaultEyePosition();
glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * (hudIntersection - sphereCenter);
glm::quat rotation = ::rotationBetween(glm::vec3(0.0f, 0.0f, -1.0f), direction);
glm::vec3 eulers = ::safeEulerAngles(rotation);
return qScriptValueFromValue<glm::vec2>(engine, Application::getInstance()->getApplicationOverlay()
.sphericalToOverlay(glm::vec2(eulers.y, -eulers.x)));
}
return QScriptValue::NullValue;
}
QScriptValue HMDScriptingInterface::getHUDLookAtPosition3D(QScriptContext* context, QScriptEngine* engine) {
glm::vec3 result;
HMDScriptingInterface* hmdInterface = &HMDScriptingInterface::getInstance();
if ((&HMDScriptingInterface::getInstance())->getHUDLookAtPosition3D(result)) {
return qScriptValueFromValue<glm::vec3>(engine, result);
}
return QScriptValue::NullValue;
}

View file

@ -0,0 +1,42 @@
//
// HMDScriptingInterface.h
// interface/src/scripting
//
// Created by Thijs Wenker on 1/12/15.
// Copyright 2015 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_HMDScriptingInterface_h
#define hifi_HMDScriptingInterface_h
#include <GLMHelpers.h>
#include "Application.h"
#include "devices/OculusManager.h"
class HMDScriptingInterface : public QObject {
Q_OBJECT
Q_PROPERTY(bool magnifier READ getMagnifier)
Q_PROPERTY(bool active READ isHMDMode)
public:
static HMDScriptingInterface& getInstance();
static QScriptValue getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine);
static QScriptValue getHUDLookAtPosition3D(QScriptContext* context, QScriptEngine* engine);
public slots:
void toggleMagnifier() { Application::getInstance()->getApplicationOverlay().toggleMagnifier(); };
private:
HMDScriptingInterface() {};
bool getMagnifier() const { return Application::getInstance()->getApplicationOverlay().hasMagnifier(); };
bool isHMDMode() const { return Application::getInstance()->isHMDMode(); }
bool getHUDLookAtPosition3D(glm::vec3& result) const;
};
#endif // hifi_HMDScriptingInterface_h

View file

@ -139,6 +139,7 @@ ApplicationOverlay::ApplicationOverlay() :
_alpha(1.0f),
_oculusUIRadius(1.0f),
_crosshairTexture(0),
_magnifier(true),
_previousBorderWidth(-1),
_previousBorderHeight(-1),
_previousMagnifierBottomLeft(),
@ -559,7 +560,7 @@ void ApplicationOverlay::renderPointers() {
_reticlePosition[MOUSE] = position;
_reticleActive[MOUSE] = true;
_magActive[MOUSE] = true;
_magActive[MOUSE] = _magnifier;
_reticleActive[LEFT_CONTROLLER] = false;
_reticleActive[RIGHT_CONTROLLER] = false;
} else if (qApp->getLastMouseMoveWasSimulated() && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) {
@ -718,6 +719,9 @@ void ApplicationOverlay::renderPointersOculus(const glm::vec3& eyePos) {
//Renders a small magnification of the currently bound texture at the coordinates
void ApplicationOverlay::renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder) {
if (!_magnifier) {
return;
}
auto glCanvas = DependencyManager::get<GLCanvas>();
const int widgetWidth = glCanvas->width();
@ -789,7 +793,7 @@ void ApplicationOverlay::renderAudioMeter() {
// Audio VU Meter and Mute Icon
const int MUTE_ICON_SIZE = 24;
const int MUTE_ICON_PADDING = 10;
const int AUDIO_METER_WIDTH = MIRROR_VIEW_WIDTH - MUTE_ICON_SIZE - MUTE_ICON_PADDING;
const int AUDIO_METER_WIDTH = MIRROR_VIEW_WIDTH - MUTE_ICON_SIZE - MUTE_ICON_PADDING;
const int AUDIO_METER_SCALE_WIDTH = AUDIO_METER_WIDTH - 2 ;
const int AUDIO_METER_HEIGHT = 8;
const int AUDIO_METER_GAP = 5;
@ -845,8 +849,8 @@ void ApplicationOverlay::renderAudioMeter() {
audioMeterY += AUDIO_METER_HEIGHT;
// Draw audio meter background Quad
DependencyManager::get<GeometryCache>()->renderQuad(AUDIO_METER_X, audioMeterY, AUDIO_METER_WIDTH, AUDIO_METER_HEIGHT,
glm::vec4(0.298f, 0.757f, 0.722f, 1));
DependencyManager::get<GeometryCache>()->renderQuad(AUDIO_METER_X, audioMeterY, AUDIO_METER_WIDTH, AUDIO_METER_HEIGHT,
glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
if (audioLevel > AUDIO_RED_START) {
glm::vec4 quadColor;

View file

@ -37,6 +37,9 @@ public:
QPoint getPalmClickLocation(const PalmData *palm) const;
bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const;
bool hasMagnifier() const { return _magnifier; }
void toggleMagnifier() { _magnifier = !_magnifier; }
float getOculusUIAngularSize() const { return _oculusUIAngularSize; }
void setOculusUIAngularSize(float oculusUIAngularSize) { _oculusUIAngularSize = oculusUIAngularSize; }
@ -109,7 +112,8 @@ private:
bool _magActive[NUMBER_OF_RETICLES];
float _magSizeMult[NUMBER_OF_RETICLES];
quint64 _lastMouseMove;
bool _magnifier;
float _alpha;
float _oculusUIRadius;
float _trailingAudioLoudness;

View file

@ -11,6 +11,7 @@
#include <QApplication>
#include <QDesktopWidget>
#include <QDesktopServices>
#include <QFileInfo>
#include <QtWebKitWidgets/QWebFrame>
#include <QtWebKit/QWebElement>
@ -32,6 +33,9 @@ InfoView::InfoView(bool forced, QString path) :
QString absPath = QFileInfo(PathUtils::resourcesPath() + path).absoluteFilePath();
QUrl url = QUrl::fromLocalFile(absPath);
page()->setLinkDelegationPolicy(QWebPage::DelegateExternalLinks);
connect(this, SIGNAL(linkClicked(QUrl)), this, SLOT(linkClickedInfoView(QUrl)));
load(url);
connect(this, SIGNAL(loadFinished(bool)), this, SLOT(loaded(bool)));
}
@ -83,3 +87,8 @@ void InfoView::loaded(bool ok) {
setAttribute(Qt::WA_DeleteOnClose);
show();
}
void InfoView::linkClickedInfoView(QUrl url) {
close();
QDesktopServices::openUrl(url);
}

View file

@ -27,6 +27,7 @@ private:
private slots:
void loaded(bool ok);
void linkClickedInfoView(QUrl url);
};
#endif // hifi_InfoView_h

View file

@ -44,6 +44,9 @@ LoginDialog::LoginDialog(QWidget* parent) :
this, &LoginDialog::close);
UIUtil::scaleWidgetFontSizes(this);
// Initialize toggle connection
toggleQAction();
};
LoginDialog::~LoginDialog() {

View file

@ -17,7 +17,9 @@
#include "Line3DOverlay.h"
Line3DOverlay::Line3DOverlay() {
Line3DOverlay::Line3DOverlay() :
_geometryCacheID(DependencyManager::get<GeometryCache>()->allocateID())
{
}
Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) :

View file

@ -19,7 +19,9 @@
#include "Rectangle3DOverlay.h"
Rectangle3DOverlay::Rectangle3DOverlay() {
Rectangle3DOverlay::Rectangle3DOverlay() :
_geometryCacheID(DependencyManager::get<GeometryCache>()->allocateID())
{
}
Rectangle3DOverlay::Rectangle3DOverlay(const Rectangle3DOverlay* rectangle3DOverlay) :

View file

@ -8,17 +8,13 @@ link_hifi_libraries(audio)
# append audio includes to our list of includes to bubble
list(APPEND ${TARGET_NAME}_DEPENDENCY_INCLUDES "${HIFI_LIBRARY_DIR}/audio/src")
set(GVERB_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/gverb")
# have CMake grab Gverb from git and then set up linking and directory include
add_dependency_external_project(gverb)
# As Gverb is currently the only reverb library, it's required.
find_package(Gverb REQUIRED)
file(GLOB GVERB_SRCS ${GVERB_SRC_DIRS}/*.c)
add_library(gverb STATIC ${GVERB_SRCS})
target_link_libraries(${TARGET_NAME} gverb)
# append gverb includes to our list of includes to bubble
list(APPEND ${TARGET_NAME}_DEPENDENCY_INCLUDES "${GVERB_INCLUDE_DIRS}")
target_link_libraries(${TARGET_NAME} ${GVERB_LIBRARIES})
target_include_directories(${TARGET_NAME} PRIVATE ${GVERB_INCLUDE_DIRS})
# we use libsoxr for resampling
find_package(Soxr REQUIRED)

View file

@ -1,14 +0,0 @@
Instructions for adding the Gverb library to audio-client
(This is a required library)
Clément Brisset, October 22nd, 2014
1. Go to https://github.com/highfidelity/gverb
Or download the sources directly via this link:
https://github.com/highfidelity/gverb/archive/master.zip
2. Extract the archive
3. Place the directories “include” and “src” in libraries/audio-client/external/gverb
(Normally next to this readme)
4. Clear your build directory, run cmake, build and you should be all set.

View file

@ -33,6 +33,11 @@
#include <QtMultimedia/QAudioInput>
#include <QtMultimedia/QAudioOutput>
extern "C" {
#include <gverb/gverb.h>
#include <gverb/gverbdsp.h>
}
#include <soxr.h>
#include <NodeList.h>
@ -104,7 +109,6 @@ AudioClient::AudioClient() :
_audioSourceInjectEnabled(false),
_reverb(false),
_reverbOptions(&_scriptReverbOptions),
_gverbLocal(NULL),
_gverb(NULL),
_inputToNetworkResampler(NULL),
_networkToOutputResampler(NULL),
@ -121,26 +125,23 @@ AudioClient::AudioClient() :
connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples,
this, &AudioClient::processReceivedSamples, Qt::DirectConnection);
// Initialize GVerb
initGverb();
const qint64 DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000;
_inputDevices = getDeviceNames(QAudio::AudioInput);
_outputDevices = getDeviceNames(QAudio::AudioOutput);
const qint64 DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000;
QTimer* updateTimer = new QTimer(this);
connect(updateTimer, &QTimer::timeout, this, &AudioClient::checkDevices);
updateTimer->start(DEVICE_CHECK_INTERVAL_MSECS);
// create GVerb filter
_gverb = createGverbFilter();
configureGverbFilter(_gverb);
}
AudioClient::~AudioClient() {
stop();
if (_gverbLocal) {
gverb_free(_gverbLocal);
}
if (_gverb) {
gverb_free(_gverb);
}
@ -153,6 +154,8 @@ void AudioClient::reset() {
_toneSource.reset();
_sourceGain.reset();
_inputGain.reset();
gverb_flush(_gverb);
}
void AudioClient::audioMixerKilled() {
@ -486,8 +489,8 @@ void AudioClient::start() {
_sourceGain.initialize();
_noiseSource.initialize();
_toneSource.initialize();
_sourceGain.setParameters(0.25f,0.0f);
_inputGain.setParameters(1.0f,0.0f);
_sourceGain.setParameters(0.25f, 0.0f);
_inputGain.setParameters(1.0f, 0.0f);
}
void AudioClient::stop() {
@ -530,38 +533,24 @@ bool AudioClient::switchOutputToAudioDevice(const QString& outputDeviceName) {
return switchOutputToAudioDevice(getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName));
}
void AudioClient::initGverb() {
ty_gverb* AudioClient::createGverbFilter() {
// Initialize a new gverb instance
if (_gverbLocal) {
gverb_free(_gverbLocal);
}
_gverbLocal = gverb_new(_outputFormat.sampleRate(), _reverbOptions->getMaxRoomSize(), _reverbOptions->getRoomSize(),
_reverbOptions->getReverbTime(), _reverbOptions->getDamping(), _reverbOptions->getSpread(),
_reverbOptions->getInputBandwidth(), _reverbOptions->getEarlyLevel(),
_reverbOptions->getTailLevel());
if (_gverb) {
gverb_free(_gverb);
}
_gverb = gverb_new(_outputFormat.sampleRate(), _reverbOptions->getMaxRoomSize(), _reverbOptions->getRoomSize(),
_reverbOptions->getReverbTime(), _reverbOptions->getDamping(), _reverbOptions->getSpread(),
_reverbOptions->getInputBandwidth(), _reverbOptions->getEarlyLevel(),
_reverbOptions->getTailLevel());
ty_gverb* filter = gverb_new(_outputFormat.sampleRate(), _reverbOptions->getMaxRoomSize(), _reverbOptions->getRoomSize(),
_reverbOptions->getReverbTime(), _reverbOptions->getDamping(), _reverbOptions->getSpread(),
_reverbOptions->getInputBandwidth(), _reverbOptions->getEarlyLevel(),
_reverbOptions->getTailLevel());
return filter;
}
void AudioClient::configureGverbFilter(ty_gverb* filter) {
// Configure the instance (these functions are not super well named - they actually set several internal variables)
gverb_set_roomsize(_gverbLocal, _reverbOptions->getRoomSize());
gverb_set_revtime(_gverbLocal, _reverbOptions->getReverbTime());
gverb_set_damping(_gverbLocal, _reverbOptions->getDamping());
gverb_set_inputbandwidth(_gverbLocal, _reverbOptions->getInputBandwidth());
gverb_set_earlylevel(_gverbLocal, DB_CO(_reverbOptions->getEarlyLevel()));
gverb_set_taillevel(_gverbLocal, DB_CO(_reverbOptions->getTailLevel()));
gverb_set_roomsize(_gverb, _reverbOptions->getRoomSize());
gverb_set_revtime(_gverb, _reverbOptions->getReverbTime());
gverb_set_damping(_gverb, _reverbOptions->getDamping());
gverb_set_inputbandwidth(_gverb, _reverbOptions->getInputBandwidth());
gverb_set_earlylevel(_gverb, DB_CO(_reverbOptions->getEarlyLevel()));
gverb_set_taillevel(_gverb, DB_CO(_reverbOptions->getTailLevel()));
gverb_set_roomsize(filter, _reverbOptions->getRoomSize());
gverb_set_revtime(filter, _reverbOptions->getReverbTime());
gverb_set_damping(filter, _reverbOptions->getDamping());
gverb_set_inputbandwidth(filter, _reverbOptions->getInputBandwidth());
gverb_set_earlylevel(filter, DB_CO(_reverbOptions->getEarlyLevel()));
gverb_set_taillevel(filter, DB_CO(_reverbOptions->getTailLevel()));
}
void AudioClient::updateGverbOptions() {
@ -574,7 +563,7 @@ void AudioClient::updateGverbOptions() {
}
if (_zoneReverbOptions.getWetLevel() != _receivedAudioStream.getWetLevel()) {
_zoneReverbOptions.setWetLevel(_receivedAudioStream.getWetLevel());
reverbChanged = true;
// Not part of actual filter config, no need to set reverbChanged to true
}
if (_reverbOptions != &_zoneReverbOptions) {
@ -587,7 +576,17 @@ void AudioClient::updateGverbOptions() {
}
if (reverbChanged) {
initGverb();
gverb_free(_gverb);
_gverb = createGverbFilter();
configureGverbFilter(_gverb);
}
}
void AudioClient::setReverb(bool reverb) {
_reverb = reverb;
if (!_reverb) {
gverb_flush(_gverb);
}
}
@ -606,14 +605,17 @@ void AudioClient::setReverbOptions(const AudioEffectOptions* options) {
_scriptReverbOptions.setWetLevel(options->getWetLevel());
if (_reverbOptions == &_scriptReverbOptions) {
// Apply them to the reverb instance(s)
initGverb();
// Apply them to the reverb instances
gverb_free(_gverb);
_gverb = createGverbFilter();
configureGverbFilter(_gverb);
}
}
void AudioClient::addReverb(ty_gverb* gverb, int16_t* samplesData, int numSamples, QAudioFormat& audioFormat, bool noEcho) {
void AudioClient::addReverb(ty_gverb* gverb, int16_t* samplesData, int16_t* reverbAlone, int numSamples,
QAudioFormat& audioFormat, bool noEcho) {
float wetFraction = DB_CO(_reverbOptions->getWetLevel());
float dryFraction = (noEcho) ? 0.0f : (1.0f - wetFraction);
float dryFraction = 1.0f - wetFraction;
float lValue,rValue;
for (int sample = 0; sample < numSamples; sample += audioFormat.channelCount()) {
@ -628,11 +630,19 @@ void AudioClient::addReverb(ty_gverb* gverb, int16_t* samplesData, int numSample
int lResult = glm::clamp((int)(samplesData[j] * dryFraction + lValue * wetFraction),
AudioConstants::MIN_SAMPLE_VALUE, AudioConstants::MAX_SAMPLE_VALUE);
samplesData[j] = (int16_t)lResult;
if (noEcho) {
reverbAlone[j] = (int16_t)lValue * wetFraction;
}
} else if (j == (sample + 1)) {
// right channel
int rResult = glm::clamp((int)(samplesData[j] * dryFraction + rValue * wetFraction),
AudioConstants::MIN_SAMPLE_VALUE, AudioConstants::MAX_SAMPLE_VALUE);
samplesData[j] = (int16_t)rResult;
if (noEcho) {
reverbAlone[j] = (int16_t)rValue * wetFraction;
}
} else {
// ignore channels above 2
}
@ -642,9 +652,8 @@ void AudioClient::addReverb(ty_gverb* gverb, int16_t* samplesData, int numSample
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 hasLocalReverb = (_reverb || _receivedAudioStream.hasReverb()) &&
!_shouldEchoToServer;
if (_muted || !_audioOutput || (!_shouldEchoLocally && !hasLocalReverb)) {
bool hasReverb = _reverb || _receivedAudioStream.hasReverb();
if (_muted || !_audioOutput || (!_shouldEchoLocally && !hasReverb)) {
return;
}
@ -654,6 +663,10 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
if (!_loopbackOutputDevice && _loopbackAudioOutput) {
// we didn't have the loopback output device going so set that up now
_loopbackOutputDevice = _loopbackAudioOutput->start();
if (!_loopbackOutputDevice) {
return;
}
}
// do we need to setup a resampler?
@ -666,26 +679,31 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
}
}
static QByteArray reverbAlone; // Intermediary for local reverb with no echo
static QByteArray loopBackByteArray;
loopBackByteArray.resize(numDestinationSamplesRequired(_inputFormat, _outputFormat,
inputByteArray.size() / sizeof(int16_t)) * sizeof(int16_t));
int numInputSamples = inputByteArray.size() / sizeof(int16_t);
int numLoopbackSamples = numDestinationSamplesRequired(_inputFormat, _outputFormat, numInputSamples);
reverbAlone.resize(numInputSamples * sizeof(int16_t));
loopBackByteArray.resize(numLoopbackSamples * sizeof(int16_t));
int16_t* inputSamples = reinterpret_cast<int16_t*>(inputByteArray.data());
int16_t* reverbAloneSamples = reinterpret_cast<int16_t*>(reverbAlone.data());
int16_t* loopbackSamples = reinterpret_cast<int16_t*>(loopBackByteArray.data());
if (hasReverb) {
updateGverbOptions();
addReverb(_gverb, inputSamples, reverbAloneSamples, numInputSamples,
_inputFormat, !_shouldEchoLocally);
}
possibleResampling(_loopbackResampler,
reinterpret_cast<int16_t*>(inputByteArray.data()),
reinterpret_cast<int16_t*>(loopBackByteArray.data()),
inputByteArray.size() / sizeof(int16_t), loopBackByteArray.size() / sizeof(int16_t),
(_shouldEchoLocally) ? inputSamples : reverbAloneSamples, loopbackSamples,
numInputSamples, numLoopbackSamples,
_inputFormat, _outputFormat);
if (hasLocalReverb) {
int16_t* loopbackSamples = reinterpret_cast<int16_t*>(loopBackByteArray.data());
int numLoopbackSamples = loopBackByteArray.size() / sizeof(int16_t);
updateGverbOptions();
addReverb(_gverbLocal, loopbackSamples, numLoopbackSamples, _outputFormat, !_shouldEchoLocally);
}
if (_loopbackOutputDevice) {
_loopbackOutputDevice->write(loopBackByteArray);
}
_loopbackOutputDevice->write(loopBackByteArray);
}
void AudioClient::handleAudioInput() {
@ -879,22 +897,18 @@ void AudioClient::processReceivedSamples(const QByteArray& inputBuffer, QByteArr
reinterpret_cast<int16_t*>(outputBuffer.data()),
numNetworkOutputSamples, numDeviceOutputSamples,
_desiredOutputFormat, _outputFormat);
if(_reverb || _receivedAudioStream.hasReverb()) {
updateGverbOptions();
addReverb(_gverb, (int16_t*)outputBuffer.data(), numDeviceOutputSamples, _outputFormat);
}
}
void AudioClient::sendMuteEnvironmentPacket() {
QByteArray mutePacket = byteArrayWithPopulatedHeader(PacketTypeMuteEnvironment);
QDataStream mutePacketStream(&mutePacket, QIODevice::Append);
int headerSize = mutePacket.size();
const float MUTE_RADIUS = 50;
glm::vec3 currentSourcePosition = _positionGetter();
mutePacketStream.writeBytes(reinterpret_cast<const char *>(&currentSourcePosition), sizeof(glm::vec3));
mutePacketStream.writeBytes(reinterpret_cast<const char *>(&MUTE_RADIUS), sizeof(float));
mutePacket.resize(mutePacket.size() + sizeof(glm::vec3) + sizeof(float));
memcpy(mutePacket.data() + headerSize, &currentSourcePosition, sizeof(glm::vec3));
memcpy(mutePacket.data() + headerSize + sizeof(glm::vec3), &MUTE_RADIUS, sizeof(float));
// grab our audio mixer from the NodeList, if it exists
auto nodelist = DependencyManager::get<NodeList>();

View file

@ -47,10 +47,7 @@
#pragma warning( disable : 4273 )
#pragma warning( disable : 4305 )
#endif
extern "C" {
#include <gverb.h>
#include <gverbdsp.h>
}
#ifdef _WIN32
#pragma warning( pop )
#endif
@ -68,6 +65,7 @@ class QAudioInput;
class QAudioOutput;
class QIODevice;
struct soxr;
typedef struct ty_gverb ty_gverb;
typedef glm::vec3 (*AudioPositionGetter)();
typedef glm::quat (*AudioOrientationGetter)();
@ -168,7 +166,7 @@ public slots:
float getInputVolume() const { return (_audioInput) ? _audioInput->volume() : 0.0f; }
void setInputVolume(float volume) { if (_audioInput) _audioInput->setVolume(volume); }
void setReverb(bool reverb) { _reverb = reverb; }
void setReverb(bool reverb);
void setReverbOptions(const AudioEffectOptions* options);
void outputNotify();
@ -243,7 +241,6 @@ private:
AudioEffectOptions _scriptReverbOptions;
AudioEffectOptions _zoneReverbOptions;
AudioEffectOptions* _reverbOptions;
ty_gverb* _gverbLocal;
ty_gverb* _gverb;
// possible soxr streams needed for resample
@ -252,9 +249,10 @@ private:
soxr* _loopbackResampler;
// Adds Reverb
void initGverb();
ty_gverb* createGverbFilter();
void configureGverbFilter(ty_gverb* filter);
void updateGverbOptions();
void addReverb(ty_gverb* gverb, int16_t* samples, int numSamples, QAudioFormat& format, bool noEcho = false);
void addReverb(ty_gverb* gverb, int16_t* samples, int16_t* reverbAlone, int numSamples, QAudioFormat& format, bool noEcho = false);
void handleLocalEchoAndReverb(QByteArray& inputByteArray);

View file

@ -3,7 +3,9 @@ set(TARGET_NAME audio)
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
setup_hifi_library(Network)
include_glm()
add_dependency_external_project(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
link_hifi_libraries(networking shared)

View file

@ -23,8 +23,8 @@ public:
bool getZeroCrossing(uint32_t start, bool direction, float32_t epsilon, uint32_t& zero);
void linearFade(uint32_t start, uint32_t stop, bool slope);
void exponentialFade(uint32_t start, uint32_t stop, bool slope);
void linearFade(uint32_t start, uint32_t stop, bool increasing);
void exponentialFade(uint32_t start, uint32_t stop, bool increasing);
};
template< typename T >
@ -74,7 +74,7 @@ inline bool AudioEditBuffer<T>::getZeroCrossing(uint32_t start, bool direction,
}
template< typename T >
inline void AudioEditBuffer<T>::linearFade(uint32_t start, uint32_t stop, bool slope) {
inline void AudioEditBuffer<T>::linearFade(uint32_t start, uint32_t stop, bool increasing) {
if (start >= stop || start > this->_frameCount || stop > this->_frameCount ) {
return;
@ -84,7 +84,7 @@ inline void AudioEditBuffer<T>::linearFade(uint32_t start, uint32_t stop, bool s
float32_t delta;
float32_t gain;
if (slope) { // 0.0 to 1.0f in delta increments
if (increasing) { // 0.0 to 1.0f in delta increments
delta = 1.0f / (float32_t)count;
gain = 0.0f;
} else { // 1.0f to 0.0f in delta increments
@ -95,13 +95,13 @@ inline void AudioEditBuffer<T>::linearFade(uint32_t start, uint32_t stop, bool s
for (uint32_t i = start; i < stop; ++i) {
for (uint32_t j = 0; j < this->_channelCount; ++j) {
this->_frameBuffer[j][i] *= gain;
gain += delta;
}
gain += delta;
}
}
template< typename T >
inline void AudioEditBuffer<T>::exponentialFade(uint32_t start, uint32_t stop, bool slope) {
inline void AudioEditBuffer<T>::exponentialFade(uint32_t start, uint32_t stop, bool increasing) {
// TBD
}

View file

@ -3,7 +3,9 @@ set(TARGET_NAME avatars)
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
setup_hifi_library(Network Script)
include_glm()
add_dependency_external_project(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
link_hifi_libraries(audio shared networking)

View file

@ -3,7 +3,9 @@ set(TARGET_NAME entities-renderer)
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
setup_hifi_library(Widgets OpenGL Network Script)
include_glm()
add_dependency_external_project(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
link_hifi_libraries(shared gpu script-engine render-utils)

View file

@ -3,7 +3,10 @@ set(TARGET_NAME entities)
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
setup_hifi_library(Network Script)
include_glm()
add_dependency_external_project(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
include_bullet()
link_hifi_libraries(avatars shared octree gpu model fbx networking animation)

View file

@ -194,6 +194,7 @@ public:
bool containsBoundsProperties() const { return (_positionChanged || _dimensionsChanged); }
bool containsPositionChange() const { return _positionChanged; }
bool containsDimensionsChange() const { return _dimensionsChanged; }
bool containsAnimationSettingsChange() const { return _animationSettingsChanged; }
float getGlowLevel() const { return _glowLevel; }
float getLocalRenderAlpha() const { return _localRenderAlpha; }
@ -256,12 +257,57 @@ inline void EntityItemProperties::setPosition(const glm::vec3& value)
inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
debug << "EntityItemProperties[" << "\n"
<< " position:" << properties.getPosition() << "in meters" << "\n"
<< " velocity:" << properties.getVelocity() << "in meters" << "\n"
<< " last edited:" << properties.getLastEdited() << "\n"
<< " edited ago:" << properties.getEditedAgo() << "\n"
<< "]";
debug << "EntityItemProperties[" << "\n";
// TODO: figure out why position and animationSettings don't seem to like the macro approach
if (properties.containsPositionChange()) {
debug << " position:" << properties.getPosition() << "in meters" << "\n";
}
if (properties.containsAnimationSettingsChange()) {
debug << " animationSettings:" << properties.getAnimationSettings() << "\n";
}
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Dimensions, dimensions, "in meters");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Velocity, velocity, "in meters");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Visible, visible, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Rotation, rotation, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Density, density, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Gravity, gravity, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Damping, damping, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Lifetime, lifetime, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Script, script, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Color, color, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ModelURL, modelURL, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationURL, animationURL, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationFPS, animationFPS, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationFrameIndex, animationFrameIndex, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationIsPlaying, animationIsPlaying, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, RegistrationPoint, registrationPoint, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AngularVelocity, angularVelocity, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AngularDamping, angularDamping, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, IgnoreForCollisions, ignoreForCollisions, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CollisionsWillMove, collisionsWillMove, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, IsSpotlight, isSpotlight, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, DiffuseColor, diffuseColor, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AmbientColor, ambientColor, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, SpecularColor, specularColor, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ConstantAttenuation, constantAttenuation, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, LinearAttenuation, linearAttenuation, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, QuadraticAttenuation, quadraticAttenuation, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Exponent, exponent, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Cutoff, cutoff, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Locked, locked, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Textures, textures, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, UserData, userData, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Text, text, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, LineHeight, lineHeight, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, TextColor, textColor, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, BackgroundColor, backgroundColor, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ShapeType, shapeType, "");
debug << " last edited:" << properties.getLastEdited() << "\n";
debug << " edited ago:" << properties.getEditedAgo() << "\n";
debug << "]";
return debug;
}

View file

@ -321,6 +321,10 @@
T _##n; \
bool _##n##Changed;
#define DEBUG_PROPERTY_IF_CHANGED(D, P, N, n, x) \
if (P.n##Changed()) { \
D << " " << #n << ":" << P.get##N() << x << "\n"; \
}
#endif // hifi_EntityItemPropertiesMacros_h

View file

@ -592,6 +592,10 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char
// if the EntityItem exists, then update it
if (existingEntity) {
if (wantEditLogging()) {
qDebug() << "User [" << senderNode->getUUID() << "] editing entity. ID:" << entityItemID;
qDebug() << " properties:" << properties;
}
updateEntity(entityItemID, properties, senderNode->getCanAdjustLocks());
existingEntity->markAsChangedOnServer();
} else {

View file

@ -151,6 +151,9 @@ public:
void emitEntityScriptChanging(const EntityItemID& entityItemID);
void setSimulation(EntitySimulation* simulation);
bool wantEditLogging() const { return _wantEditLogging; }
void setWantEditLogging(bool value) { _wantEditLogging = value; }
signals:
void deletingEntity(const EntityItemID& entityID);
@ -180,6 +183,8 @@ private:
QHash<EntityItemID, EntityTreeElement*> _entityToElementMap;
EntitySimulation* _simulation;
bool _wantEditLogging = false;
};
#endif // hifi_EntityTree_h

View file

@ -163,16 +163,6 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
QMap<QString, AnimationPointer> ModelEntityItem::_loadedAnimations; // TODO: improve cleanup by leveraging the AnimationPointer(s)
// This class/instance will cleanup the animations once unloaded.
class EntityAnimationsBookkeeper {
public:
~EntityAnimationsBookkeeper() {
ModelEntityItem::cleanupLoadedAnimations();
}
};
EntityAnimationsBookkeeper modelAnimationsBookkeeperInstance;
void ModelEntityItem::cleanupLoadedAnimations() {
foreach(AnimationPointer animation, _loadedAnimations) {
animation.clear();

View file

@ -3,7 +3,9 @@ set(TARGET_NAME environment)
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
setup_hifi_library()
include_glm()
add_dependency_external_project(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
link_hifi_libraries(shared networking)

View file

@ -3,7 +3,9 @@ set(TARGET_NAME fbx)
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
setup_hifi_library()
include_glm()
add_dependency_external_project(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
link_hifi_libraries(shared gpu model networking octree)

View file

@ -3,8 +3,6 @@ set(TARGET_NAME gpu)
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
setup_hifi_library()
include_glm()
link_hifi_libraries(shared)
if (APPLE)

View file

@ -8,7 +8,9 @@ setup_hifi_library(Network Script Widgets)
# link in the networking library
link_hifi_libraries(shared networking)
include_glm()
add_dependency_external_project(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
# call macro to include our dependency includes and bubble them up via a property on our target
include_dependency_includes()

View file

@ -3,7 +3,9 @@ set(TARGET_NAME model)
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
setup_hifi_library()
include_glm()
add_dependency_external_project(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
link_hifi_libraries(shared gpu)

View file

@ -39,6 +39,7 @@ const char SOLO_NODE_TYPES[2] = {
const QUrl DEFAULT_NODE_AUTH_URL = QUrl("https://metaverse.highfidelity.io");
LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short dtlsListenPort) :
linkedDataCreateCallback(NULL),
_sessionUUID(),
_nodeHash(),
_nodeMutex(QReadWriteLock::Recursive),
@ -309,20 +310,23 @@ int LimitedNodeList::updateNodeWithDataFromPacket(const SharedNodePointer& match
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
if (!matchingNode->getLinkedData() && linkedDataCreateCallback) {
NodeData* linkedData = matchingNode->getLinkedData();
if (!linkedData && linkedDataCreateCallback) {
linkedDataCreateCallback(matchingNode.data());
}
QMutexLocker linkedDataLocker(&matchingNode->getLinkedData()->getMutex());
return matchingNode->getLinkedData()->parseData(packet);
if (linkedData) {
QMutexLocker linkedDataLocker(&linkedData->getMutex());
return linkedData->parseData(packet);
}
return 0;
}
int LimitedNodeList::findNodeAndUpdateWithDataFromPacket(const QByteArray& packet) {
SharedNodePointer matchingNode = sendingNodeForPacket(packet);
if (matchingNode) {
updateNodeWithDataFromPacket(matchingNode, packet);
return updateNodeWithDataFromPacket(matchingNode, packet);
}
// we weren't able to match the sender address to the address we have for this node, unlock and don't parse

View file

@ -347,7 +347,7 @@ void NodeList::handleICEConnectionToDomainServer() {
qDebug() << "Sending ping packets to establish connectivity with domain-server with ID"
<< uuidStringWithoutCurlyBraces(_domainHandler.getICEDomainID());
// send the ping packet to the local and public sockets for this nodfe
// send the ping packet to the local and public sockets for this node
QByteArray localPingPacket = constructPingPacket(PingType::Local, false, _domainHandler.getICEClientID());
writeUnverifiedDatagram(localPingPacket, _domainHandler.getICEPeer().getLocalSocket());

View file

@ -3,7 +3,9 @@ set(TARGET_NAME octree)
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
setup_hifi_library()
include_glm()
add_dependency_external_project(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
link_hifi_libraries(shared networking)

View file

@ -49,14 +49,32 @@ void OctreePersistThread::parseSettings(const QJsonObject& settings) {
qDebug() << "BACKUP RULES:";
foreach (const QJsonValue& value, backupRules) {
QJsonObject obj = value.toObject();
int interval = 0;
int count = 0;
QJsonValue intervalVal = obj["backupInterval"];
if (intervalVal.isString()) {
interval = intervalVal.toString().toInt();
} else {
interval = intervalVal.toInt();
}
QJsonValue countVal = obj["maxBackupVersions"];
if (countVal.isString()) {
count = countVal.toString().toInt();
} else {
count = countVal.toInt();
}
qDebug() << " Name:" << obj["Name"].toString();
qDebug() << " format:" << obj["format"].toString();
qDebug() << " interval:" << obj["backupInterval"].toInt();
qDebug() << " count:" << obj["maxBackupVersions"].toInt();
qDebug() << " interval:" << interval;
qDebug() << " count:" << count;
BackupRule newRule = { obj["Name"].toString(), obj["backupInterval"].toInt(),
obj["format"].toString(), obj["maxBackupVersions"].toInt(), 0};
BackupRule newRule = { obj["Name"].toString(), interval, obj["format"].toString(), count, 0};
newRule.lastBackup = getMostRecentBackupTimeInUsecs(obj["format"].toString());
@ -213,7 +231,9 @@ void OctreePersistThread::persist() {
}
_tree->unlock();
qDebug() << "persist operation calling backup...";
backup(); // handle backup if requested
qDebug() << "persist operation DONE with backup...";
// create our "lock" file to indicate we're saving.
@ -319,34 +339,41 @@ bool OctreePersistThread::getMostRecentBackup(const QString& format,
void OctreePersistThread::rollOldBackupVersions(const BackupRule& rule) {
if (rule.extensionFormat.contains("%N")) {
qDebug() << "Rolling old backup versions for rule" << rule.name << "...";
for(int n = rule.maxBackupVersions - 1; n > 0; n--) {
QString backupExtensionN = rule.extensionFormat;
QString backupExtensionNplusOne = rule.extensionFormat;
backupExtensionN.replace(QString("%N"), QString::number(n));
backupExtensionNplusOne.replace(QString("%N"), QString::number(n+1));
if (rule.maxBackupVersions > 0) {
qDebug() << "Rolling old backup versions for rule" << rule.name << "...";
for(int n = rule.maxBackupVersions - 1; n > 0; n--) {
QString backupExtensionN = rule.extensionFormat;
QString backupExtensionNplusOne = rule.extensionFormat;
backupExtensionN.replace(QString("%N"), QString::number(n));
backupExtensionNplusOne.replace(QString("%N"), QString::number(n+1));
QString backupFilenameN = _filename + backupExtensionN;
QString backupFilenameNplusOne = _filename + backupExtensionNplusOne;
QString backupFilenameN = _filename + backupExtensionN;
QString backupFilenameNplusOne = _filename + backupExtensionNplusOne;
QFile backupFileN(backupFilenameN);
QFile backupFileN(backupFilenameN);
if (backupFileN.exists()) {
qDebug() << "rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "...";
int result = rename(qPrintable(backupFilenameN), qPrintable(backupFilenameNplusOne));
if (result == 0) {
qDebug() << "DONE rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "...";
} else {
qDebug() << "ERROR in rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "...";
if (backupFileN.exists()) {
qDebug() << "rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "...";
int result = rename(qPrintable(backupFilenameN), qPrintable(backupFilenameNplusOne));
if (result == 0) {
qDebug() << "DONE rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "...";
} else {
qDebug() << "ERROR in rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "...";
}
}
}
qDebug() << "Done rolling old backup versions...";
} else {
qDebug() << "Rolling backups for rule" << rule.name << "."
<< " Max Rolled Backup Versions less than 1 [" << rule.maxBackupVersions << "]."
<< " No need to roll backups...";
}
qDebug() << "Done rolling old backup versions...";
}
}
void OctreePersistThread::backup() {
qDebug() << "backup operation wantBackup:" << _wantBackup;
if (_wantBackup) {
quint64 now = usecTimestampNow();
@ -354,10 +381,12 @@ void OctreePersistThread::backup() {
BackupRule& rule = _backupRules[i];
quint64 sinceLastBackup = now - rule.lastBackup;
quint64 SECS_TO_USECS = 1000 * 1000;
quint64 intervalToBackup = rule.interval * SECS_TO_USECS;
qDebug() << "Checking [" << rule.name << "] - Time since last backup [" << sinceLastBackup << "] " <<
"compared to backup interval [" << intervalToBackup << "]...";
if (sinceLastBackup > intervalToBackup) {
qDebug() << "Time since last backup [" << sinceLastBackup << "] for rule [" << rule.name
<< "] exceeds backup interval [" << intervalToBackup << "] doing backup now...";
@ -379,15 +408,28 @@ void OctreePersistThread::backup() {
}
qDebug() << "backing up persist file " << _filename << "to" << backupFileName << "...";
bool result = QFile::copy(_filename, backupFileName);
if (result) {
qDebug() << "DONE backing up persist file...";
if (rule.maxBackupVersions > 0) {
QFile persistFile(_filename);
if (persistFile.exists()) {
qDebug() << "backing up persist file " << _filename << "to" << backupFileName << "...";
bool result = QFile::copy(_filename, backupFileName);
if (result) {
qDebug() << "DONE backing up persist file...";
rule.lastBackup = now; // only record successful backup in this case.
} else {
qDebug() << "ERROR in backing up persist file...";
}
} else {
qDebug() << "persist file " << _filename << " does not exist. " <<
"nothing to backup for this rule ["<< rule.name << "]...";
}
} else {
qDebug() << "ERROR in backing up persist file...";
qDebug() << "This backup rule" << rule.name
<< " has Max Rolled Backup Versions less than 1 [" << rule.maxBackupVersions << "]."
<< " There are no backups to be done...";
}
rule.lastBackup = now;
} else {
qDebug() << "Backup not needed for this rule ["<< rule.name << "]...";
}
}
}

View file

@ -95,7 +95,7 @@ protected:
glm::vec3 _cameraPosition = glm::vec3(0.0f);
glm::quat _cameraOrientation = glm::quat();
float _cameraFov = 0.0f;
float _cameraAspectRatio = 0.0f;
float _cameraAspectRatio = 1.0f;
float _cameraNearClip = 0.0f;
float _cameraFarClip = 0.0f;
glm::vec3 _cameraEyeOffsetPosition = glm::vec3(0.0f);

View file

@ -3,7 +3,10 @@ set(TARGET_NAME physics)
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
setup_hifi_library()
include_glm()
add_dependency_external_project(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
include_bullet()
if (BULLET_FOUND)
target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES})

View file

@ -8,7 +8,9 @@ qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
setup_hifi_library(Widgets OpenGL Network Script)
include_glm()
add_dependency_external_project(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
link_hifi_libraries(animation fbx shared gpu)

View file

@ -511,6 +511,10 @@ TextRenderer::TextRenderer(const char* family, float pointSize, int weight,
bool italic, EffectType effect, int effectThickness,
const QColor& color) :
_effectType(effect), _effectThickness(effectThickness), _pointSize(pointSize), _color(color), _font(loadFont(family)) {
if (!_font) {
qWarning() << "Unable to load font with family " << family;
_font = loadFont("Courier");
}
if (1 != _effectThickness) {
qWarning() << "Effect thickness not current supported";
}
@ -524,7 +528,10 @@ TextRenderer::~TextRenderer() {
glm::vec2 TextRenderer::computeExtent(const QString & str) const {
float scale = (_pointSize / DEFAULT_POINT_SIZE) * 0.25f;
return _font->computeExtent(str) * scale;
if (_font) {
return _font->computeExtent(str) * scale;
}
return glm::vec2(0.1f,0.1f);
}
float TextRenderer::draw(float x, float y, const QString & str,
@ -544,7 +551,9 @@ float TextRenderer::draw(float x, float y, const QString & str,
// scale at all.
mv.translate(glm::vec2(x, y)).scale(glm::vec3(scale, -scale, scale));
// The font does all the OpenGL work
result = _font->drawString(x, y, str, actualColor, _effectType, bounds / scale);
if (_font) {
result = _font->drawString(x, y, str, actualColor, _effectType, bounds / scale);
}
});
return result.x;
}

View file

@ -3,7 +3,9 @@ set(TARGET_NAME script-engine)
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
setup_hifi_library(Gui Network Script Widgets)
include_glm()
add_dependency_external_project(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
link_hifi_libraries(shared octree gpu model fbx entities animation audio physics metavoxels)

View file

@ -31,44 +31,41 @@ AudioScriptingInterface::AudioScriptingInterface() :
}
ScriptAudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions& injectorOptions) {
AudioInjector* injector = NULL;
QMetaObject::invokeMethod(this, "invokedPlaySound", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(AudioInjector*, injector),
Q_ARG(Sound*, sound), Q_ARG(const AudioInjectorOptions&, injectorOptions));
if (injector) {
return new ScriptAudioInjector(injector);
} else {
return NULL;
if (QThread::currentThread() != thread()) {
ScriptAudioInjector* injector = NULL;
QMetaObject::invokeMethod(this, "playSound", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(ScriptAudioInjector*, injector),
Q_ARG(Sound*, sound), Q_ARG(const AudioInjectorOptions&, injectorOptions));
return injector;
}
}
AudioInjector* AudioScriptingInterface::invokedPlaySound(Sound* sound, const AudioInjectorOptions& injectorOptions) {
if (sound) {
// stereo option isn't set from script, this comes from sound metadata or filename
AudioInjectorOptions optionsCopy = injectorOptions;
optionsCopy.stereo = sound->isStereo();
QThread* injectorThread = new QThread();
injectorThread->setObjectName("Audio Injector Thread");
AudioInjector* injector = new AudioInjector(sound, optionsCopy);
injector->setLocalAudioInterface(_localAudioInterface);
injector->moveToThread(injectorThread);
// start injecting when the injector thread starts
connect(injectorThread, &QThread::started, injector, &AudioInjector::injectAudio);
// connect the right slots and signals for AudioInjector and thread cleanup
connect(injector, &AudioInjector::destroyed, injectorThread, &QThread::quit);
connect(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater);
injectorThread->start();
return injector;
return new ScriptAudioInjector(injector);
} else {
qDebug() << "AudioScriptingInterface::playSound called with null Sound object.";
return NULL;
}
}
}

View file

@ -32,9 +32,6 @@ protected:
signals:
void mutedByMixer();
void environmentMuted();
private slots:
AudioInjector* invokedPlaySound(Sound* sound, const AudioInjectorOptions& injectorOptions);
private:
AudioScriptingInterface();

View file

@ -13,7 +13,9 @@
QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in) {
// when the script goes down we want to cleanup the injector
QObject::connect(engine, &QScriptEngine::destroyed, in, &ScriptAudioInjector::stopInjectorImmediately);
QObject::connect(engine, &QScriptEngine::destroyed, in, &ScriptAudioInjector::stopInjectorImmediately,
Qt::DirectConnection);
return engine->newQObject(in, QScriptEngine::ScriptOwnership);
}
@ -36,5 +38,6 @@ ScriptAudioInjector::~ScriptAudioInjector() {
}
void ScriptAudioInjector::stopInjectorImmediately() {
qDebug() << "ScriptAudioInjector::stopInjectorImmediately called to stop audio injector immediately.";
_injector->stopAndDeleteLater();
}

View file

@ -272,8 +272,12 @@ QScriptValue ScriptEngine::registerGlobalObject(const QString& name, QObject* ob
}
void ScriptEngine::registerFunction(const QString& name, QScriptEngine::FunctionSignature fun, int numArguments) {
registerFunction(globalObject(), name, fun, numArguments);
}
void ScriptEngine::registerFunction(QScriptValue parent, const QString& name, QScriptEngine::FunctionSignature fun, int numArguments) {
QScriptValue scriptFun = newFunction(fun, numArguments);
globalObject().setProperty(name, scriptFun);
parent.setProperty(name, scriptFun);
}
void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter,

View file

@ -58,6 +58,8 @@ public:
void registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter,
QScriptEngine::FunctionSignature setter, QScriptValue object = QScriptValue::NullValue);
void registerFunction(const QString& name, QScriptEngine::FunctionSignature fun, int numArguments = -1);
void registerFunction(QScriptValue parent, const QString& name, QScriptEngine::FunctionSignature fun,
int numArguments = -1);
Q_INVOKABLE void setIsAvatar(bool isAvatar);
bool isAvatar() const { return _isAvatar; }

View file

@ -4,5 +4,9 @@ set(TARGET_NAME shared)
# TODO: there isn't really a good reason to have Script linked here - let's get what is requiring it out (RegisteredMetaTypes.cpp)
setup_hifi_library(Gui Network Script Widgets)
add_dependency_external_project(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
# call macro to include our dependency includes and bubble them up via a property on our target
include_dependency_includes()

View file

@ -2,8 +2,6 @@ set(TARGET_NAME audio-tests)
setup_hifi_project()
include_glm()
# link in the shared libraries
link_hifi_libraries(shared audio networking)

View file

@ -2,8 +2,6 @@ set(TARGET_NAME metavoxel-tests)
auto_mtc()
include_glm()
setup_hifi_project(Network Script Widgets)
# link in the shared libraries

View file

@ -2,8 +2,6 @@ set(TARGET_NAME octree-tests)
setup_hifi_project(Script Network)
include_glm()
# link in the shared libraries
link_hifi_libraries(shared octree gpu model fbx metavoxels networking entities avatars audio animation script-engine physics)

View file

@ -2,7 +2,10 @@ set(TARGET_NAME physics-tests)
setup_hifi_project()
include_glm()
add_dependency_external_project(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
include_bullet()
link_hifi_libraries(shared physics)

View file

@ -2,8 +2,6 @@ set(TARGET_NAME render-utils-tests)
setup_hifi_project(Quick Gui OpenGL)
include_glm()
#include_oglplus()
# link in the shared libraries

View file

@ -2,8 +2,6 @@ set(TARGET_NAME shared-tests)
setup_hifi_project()
include_glm()
# link in the shared libraries
link_hifi_libraries(shared)

View file

@ -1,8 +1,6 @@
set(TARGET_NAME bitstream2json)
setup_hifi_project(Widgets Script)
include_glm()
link_hifi_libraries(metavoxels)
include_dependency_includes()

View file

@ -1,8 +1,6 @@
set(TARGET_NAME json2bitstream)
setup_hifi_project(Widgets Script)
include_glm()
link_hifi_libraries(metavoxels)
include_dependency_includes()