153
BUILD_WIN.md
|
@ -1,81 +1,104 @@
|
|||
This is a stand-alone guide for creating your first High Fidelity build for Windows 64-bit.
|
||||
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Windows specific instructions are found in this file.
|
||||
|
||||
###Step 1. Installing Visual Studio 2013
|
||||
Interface can be built as 32 or 64 bit.
|
||||
|
||||
If you don't already have the Community or Professional edition of Visual Studio 2013, download and install [Visual Studio Community 2013](https://www.visualstudio.com/en-us/news/releasenotes/vs2013-community-vs). You do not need to install any of the optional components when going through the installer.
|
||||
###Visual Studio 2013
|
||||
|
||||
Note: Newer versions of Visual Studio are not yet compatible.
|
||||
You can use the Community or Professional editions of Visual Studio 2013.
|
||||
|
||||
###Step 2. Installing CMake
|
||||
You can start a Visual Studio 2013 command prompt using the shortcut provided in the Visual Studio Tools folder installed as part of Visual Studio 2013.
|
||||
|
||||
Download and install the CMake 3.8.0-rc2 "win64-x64 Installer" from the [CMake Website](https://cmake.org/download/). Make sure "Add CMake to system PATH for all users" is checked when going through the installer.
|
||||
Or you can start a regular command prompt and then run:
|
||||
|
||||
###Step 3. Installing Qt
|
||||
"%VS120COMNTOOLS%\vsvars32.bat"
|
||||
|
||||
Download and install the [Qt 5.6.1 Installer](https://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-windows-x86-msvc2013_64-5.6.1-1.exe). Please note that the download file is large (850MB) and may take some time.
|
||||
####Windows SDK 8.1
|
||||
|
||||
Make sure to select all components when going through the installer.
|
||||
If using Visual Studio 2013 and building as a Visual Studio 2013 project you need the Windows 8 SDK which you should already have as part of installing Visual Studio 2013. You should be able to see it at `C:\Program Files (x86)\Windows Kits\8.1\Lib\winv6.3\um\x86`.
|
||||
|
||||
###Step 4. Setting Qt Environment Variable
|
||||
####nmake
|
||||
|
||||
Go to "Control Panel > System > Advanced System Settings > Environment Variables > New..." (or search “Environment Variables” in Start Search).
|
||||
* Set "Variable name": QT_CMAKE_PREFIX_PATH
|
||||
* Set "Variable value": `C:\Qt\Qt5.6.1\5.6\msvc2013_64\lib\cmake`
|
||||
Some of the external projects may require nmake to compile and install. If it is not installed at the location listed below, please ensure that it is in your PATH so CMake can find it when required.
|
||||
|
||||
###Step 5. Installing OpenSSL
|
||||
We expect nmake.exe to be located at the following path.
|
||||
|
||||
Download and install the "Win64 OpenSSL v1.0.2k" Installer from [this website](https://slproweb.com/products/Win32OpenSSL.html).
|
||||
|
||||
###Step 6. Running CMake to Generate Build Files
|
||||
|
||||
Run Command Prompt from Start and run the following commands:
|
||||
cd "%HIFI_DIR%"
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -G "Visual Studio 12 Win64"
|
||||
|
||||
Where %HIFI_DIR% is the directory for the highfidelity repository.
|
||||
|
||||
###Step 7. Making a Build
|
||||
|
||||
Open '%HIFI_DIR%\build\hifi.sln' using Visual Studio.
|
||||
|
||||
Change the Solution Configuration (next to the green play button) from "Debug" to "Release" for best performance.
|
||||
|
||||
Run Build > Build Solution.
|
||||
|
||||
###Step 8. Testing Interface
|
||||
|
||||
Create another environment variable (see Step #4)
|
||||
* Set "Variable name": _NO_DEBUG_HEAP
|
||||
* Set "Variable value": 1
|
||||
|
||||
In Visual Studio, right+click "interface" under the Apps folder in Solution Explorer and select "Set as Startup Project". Run Debug > Start Debugging.
|
||||
|
||||
Now, you should have a full build of High Fidelity and be able to run the Interface using Visual Studio. Please check our [Docs](https://wiki.highfidelity.com/wiki/Main_Page) for more information regarding the programming workflow.
|
||||
|
||||
Note: You can also run Interface by launching it from command line or File Explorer from %HIFI_DIR%\build\interface\Release\interface.exe
|
||||
|
||||
###Troubleshooting
|
||||
|
||||
For any problems after Step #6, first try this:
|
||||
* Delete your locally cloned copy of the highfidelity repository
|
||||
* Restart your computer
|
||||
* Redownload the [repository](https://github.com/highfidelity/hifi)
|
||||
* Restart directions from Step #6
|
||||
|
||||
####CMake gives you the same error message repeatedly after the build fails
|
||||
|
||||
Remove `CMakeCache.txt` found in the '%HIFI_DIR%\build' directory
|
||||
|
||||
####nmake cannot be found
|
||||
|
||||
Make sure nmake.exe is located at the following path:
|
||||
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin
|
||||
|
||||
If not, add the directory where nmake is located to the PATH environment variable.
|
||||
|
||||
####Qt is throwing an error
|
||||
###Qt
|
||||
You can use the online installer or the offline installer. If you use the offline installer, be sure to select the "OpenGL" version.
|
||||
|
||||
Make sure you have the correct version (5.6.1-1) installed and 'QT_CMAKE_PREFIX_PATH' environment variable is set correctly.
|
||||
* [Download the online installer](http://www.qt.io/download-open-source/#section-2)
|
||||
* When it asks you to select components, ONLY select one of the following, 32- or 64-bit to match your build preference:
|
||||
* Qt > Qt 5.6.1 > **msvc2013 32-bit**
|
||||
* Qt > Qt 5.6.1 > **msvc2013 64-bit**
|
||||
|
||||
* Download the offline installer, 32- or 64-bit to match your build preference:
|
||||
* [32-bit](https://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-windows-x86-msvc2013-5.6.1-1.exe)
|
||||
* [64-bit](https://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-windows-x86-msvc2013_64-5.6.1-1.exe)
|
||||
|
||||
Once Qt is installed, you need to manually configure the following:
|
||||
* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.6.1\msvc2013\lib\cmake` or `Qt\5.6.1\msvc2013_64\lib\cmake` directory.
|
||||
* You can set an environment variable from Control Panel > System > Advanced System Settings > Environment Variables > New
|
||||
|
||||
###External Libraries
|
||||
|
||||
All libraries should be 32- or 64-bit to match your build preference.
|
||||
|
||||
CMake will need to know where the headers and libraries for required external dependencies are.
|
||||
|
||||
We use CMake's `fixup_bundle` to find the DLLs all of our executable targets require, and then copy them beside the executable in a post-build step. If `fixup_bundle` is having problems finding a DLL, you can fix it manually on your end by adding the folder containing that DLL to your path. Let us know which DLL CMake had trouble finding, as it is possible a tweak to our CMake files is required.
|
||||
|
||||
The recommended route for CMake to find the external dependencies is to place all of the dependencies in one folder and set one ENV variable - HIFI_LIB_DIR. That ENV variable should point to a directory with the following structure:
|
||||
|
||||
root_lib_dir
|
||||
-> openssl
|
||||
-> bin
|
||||
-> include
|
||||
-> lib
|
||||
|
||||
For many of the external libraries where precompiled binaries are readily available you should be able to simply copy the extracted folder that you get from the download links provided at the top of the guide. Otherwise you may need to build from source and install the built product to this directory. The `root_lib_dir` in the above example can be wherever you choose on your system - as long as the environment variable HIFI_LIB_DIR is set to it. From here on, whenever you see %HIFI_LIB_DIR% you should substitute the directory that you chose.
|
||||
|
||||
####OpenSSL
|
||||
|
||||
Qt will use OpenSSL if it's available, but it doesn't install it, so you must install it separately.
|
||||
|
||||
Your system may already have several versions of the OpenSSL DLL's (ssleay32.dll, libeay32.dll) lying around, but they may be the wrong version. If these DLL's are in the PATH then QT will try to use them, and if they're the wrong version then you will see the following errors in the console:
|
||||
|
||||
QSslSocket: cannot resolve TLSv1_1_client_method
|
||||
QSslSocket: cannot resolve TLSv1_2_client_method
|
||||
QSslSocket: cannot resolve TLSv1_1_server_method
|
||||
QSslSocket: cannot resolve TLSv1_2_server_method
|
||||
QSslSocket: cannot resolve SSL_select_next_proto
|
||||
QSslSocket: cannot resolve SSL_CTX_set_next_proto_select_cb
|
||||
QSslSocket: cannot resolve SSL_get0_next_proto_negotiated
|
||||
|
||||
To prevent these problems, install OpenSSL yourself. Download one of the following binary packages [from this website](https://slproweb.com/products/Win32OpenSSL.html):
|
||||
* Win32 OpenSSL v1.0.1q
|
||||
* Win64 OpenSSL v1.0.1q
|
||||
|
||||
Install OpenSSL into the Windows system directory, to make sure that Qt uses the version that you've just installed, and not some other version.
|
||||
|
||||
###Build High Fidelity using Visual Studio
|
||||
Follow the same build steps from the CMake section of [BUILD.md](BUILD.md), but pass a different generator to CMake.
|
||||
|
||||
For 32-bit builds:
|
||||
|
||||
cmake .. -G "Visual Studio 12"
|
||||
|
||||
For 64-bit builds:
|
||||
|
||||
cmake .. -G "Visual Studio 12 Win64"
|
||||
|
||||
Open %HIFI_DIR%\build\hifi.sln and compile.
|
||||
|
||||
###Running Interface
|
||||
If you need to debug Interface, you can run interface from within Visual Studio (see the section below). You can also run Interface by launching it from command line or File Explorer from %HIFI_DIR%\build\interface\Debug\interface.exe
|
||||
|
||||
###Debugging Interface
|
||||
* In the Solution Explorer, right click interface and click Set as StartUp Project
|
||||
* Set the "Working Directory" for the Interface debugging sessions to the Debug output directory so that your application can load resources. Do this: right click interface and click Properties, choose Debugging from Configuration Properties, set Working Directory to .\Debug
|
||||
* Now you can run and debug interface through Visual Studio
|
||||
|
||||
For better performance when running debug builds, set the environment variable ```_NO_DEBUG_HEAP``` to ```1```
|
||||
|
||||
http://preshing.com/20110717/the-windows-heap-is-slow-when-launched-from-the-debugger/
|
||||
|
|
|
@ -62,7 +62,6 @@ Agent::Agent(ReceivedMessage& message) :
|
|||
|
||||
DependencyManager::set<ResourceCacheSharedItems>();
|
||||
DependencyManager::set<SoundCache>();
|
||||
DependencyManager::set<AudioScriptingInterface>();
|
||||
DependencyManager::set<AudioInjectorManager>();
|
||||
DependencyManager::set<recording::Deck>();
|
||||
DependencyManager::set<recording::Recorder>();
|
||||
|
@ -372,39 +371,25 @@ void Agent::executeScript() {
|
|||
using namespace recording;
|
||||
static const FrameType AUDIO_FRAME_TYPE = Frame::registerFrameType(AudioConstants::getAudioFrameName());
|
||||
Frame::registerFrameHandler(AUDIO_FRAME_TYPE, [this, &scriptedAvatar](Frame::ConstPointer frame) {
|
||||
const QByteArray& audio = frame->data;
|
||||
static quint16 audioSequenceNumber{ 0 };
|
||||
|
||||
QByteArray audio(frame->data);
|
||||
|
||||
if (_isNoiseGateEnabled) {
|
||||
static int numSamples = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL;
|
||||
_noiseGate.gateSamples(reinterpret_cast<int16_t*>(audio.data()), numSamples);
|
||||
}
|
||||
|
||||
computeLoudness(&audio, scriptedAvatar);
|
||||
|
||||
// the codec needs a flush frame before sending silent packets, so
|
||||
// do not send one if the gate closed in this block (eventually this can be crossfaded).
|
||||
auto packetType = PacketType::MicrophoneAudioNoEcho;
|
||||
if (scriptedAvatar->getAudioLoudness() == 0.0f && !_noiseGate.closedInLastBlock()) {
|
||||
packetType = PacketType::SilentAudioFrame;
|
||||
}
|
||||
|
||||
Transform audioTransform;
|
||||
|
||||
auto headOrientation = scriptedAvatar->getHeadOrientation();
|
||||
audioTransform.setTranslation(scriptedAvatar->getPosition());
|
||||
audioTransform.setRotation(headOrientation);
|
||||
|
||||
computeLoudness(&audio, scriptedAvatar);
|
||||
|
||||
QByteArray encodedBuffer;
|
||||
if (_encoder) {
|
||||
_encoder->encode(audio, encodedBuffer);
|
||||
} else {
|
||||
encodedBuffer = audio;
|
||||
}
|
||||
|
||||
AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber,
|
||||
audioTransform, scriptedAvatar->getPosition(), glm::vec3(0),
|
||||
packetType, _selectedCodecName);
|
||||
PacketType::MicrophoneAudioNoEcho, _selectedCodecName);
|
||||
});
|
||||
|
||||
auto avatarHashMap = DependencyManager::set<AvatarHashMap>();
|
||||
|
@ -498,14 +483,6 @@ void Agent::setIsListeningToAudioStream(bool isListeningToAudioStream) {
|
|||
_isListeningToAudioStream = isListeningToAudioStream;
|
||||
}
|
||||
|
||||
void Agent::setIsNoiseGateEnabled(bool isNoiseGateEnabled) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setIsNoiseGateEnabled", Q_ARG(bool, isNoiseGateEnabled));
|
||||
return;
|
||||
}
|
||||
_isNoiseGateEnabled = isNoiseGateEnabled;
|
||||
}
|
||||
|
||||
void Agent::setIsAvatar(bool isAvatar) {
|
||||
// this must happen on Agent's main thread
|
||||
if (QThread::currentThread() != thread()) {
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
|
||||
#include <plugins/CodecPlugin.h>
|
||||
|
||||
#include "AudioNoiseGate.h"
|
||||
#include "MixedAudioStream.h"
|
||||
#include "avatars/ScriptableAvatar.h"
|
||||
|
||||
|
@ -39,7 +38,6 @@ class Agent : public ThreadedAssignment {
|
|||
Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar)
|
||||
Q_PROPERTY(bool isPlayingAvatarSound READ isPlayingAvatarSound)
|
||||
Q_PROPERTY(bool isListeningToAudioStream READ isListeningToAudioStream WRITE setIsListeningToAudioStream)
|
||||
Q_PROPERTY(bool isNoiseGateEnabled READ isNoiseGateEnabled WRITE setIsNoiseGateEnabled)
|
||||
Q_PROPERTY(float lastReceivedAudioLoudness READ getLastReceivedAudioLoudness)
|
||||
Q_PROPERTY(QUuid sessionUUID READ getSessionUUID)
|
||||
|
||||
|
@ -54,9 +52,6 @@ public:
|
|||
bool isListeningToAudioStream() const { return _isListeningToAudioStream; }
|
||||
void setIsListeningToAudioStream(bool isListeningToAudioStream);
|
||||
|
||||
bool isNoiseGateEnabled() const { return _isNoiseGateEnabled; }
|
||||
void setIsNoiseGateEnabled(bool isNoiseGateEnabled);
|
||||
|
||||
float getLastReceivedAudioLoudness() const { return _lastReceivedAudioLoudness; }
|
||||
QUuid getSessionUUID() const;
|
||||
|
||||
|
@ -111,9 +106,6 @@ private:
|
|||
QTimer* _avatarIdentityTimer = nullptr;
|
||||
QHash<QUuid, quint16> _outgoingScriptAudioSequenceNumbers;
|
||||
|
||||
AudioNoiseGate _noiseGate;
|
||||
bool _isNoiseGateEnabled { false };
|
||||
|
||||
CodecPluginPointer _codec;
|
||||
QString _selectedCodecName;
|
||||
Encoder* _encoder { nullptr };
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include <QtCore/QString>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
#include <PathUtils.h>
|
||||
#include <ServerPathUtils.h>
|
||||
|
||||
#include "NetworkLogging.h"
|
||||
#include "NodeType.h"
|
||||
|
@ -162,7 +162,7 @@ void AssetServer::completeSetup() {
|
|||
if (assetsPath.isRelative()) {
|
||||
// if the domain settings passed us a relative path, make an absolute path that is relative to the
|
||||
// default data directory
|
||||
absoluteFilePath = PathUtils::getAppDataFilePath("assets/" + assetsPathString);
|
||||
absoluteFilePath = ServerPathUtils::getDataFilePath("assets/" + assetsPathString);
|
||||
}
|
||||
|
||||
_resourcesDirectory = QDir(absoluteFilePath);
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
#include "OctreeQueryNode.h"
|
||||
#include "OctreeServerConsts.h"
|
||||
#include <QtCore/QStandardPaths>
|
||||
#include <PathUtils.h>
|
||||
#include <ServerPathUtils.h>
|
||||
#include <QtCore/QDir>
|
||||
|
||||
int OctreeServer::_clientCount = 0;
|
||||
|
@ -279,7 +279,8 @@ OctreeServer::~OctreeServer() {
|
|||
|
||||
void OctreeServer::initHTTPManager(int port) {
|
||||
// setup the embedded web server
|
||||
QString documentRoot = QString("%1/web").arg(PathUtils::getAppDataPath());
|
||||
|
||||
QString documentRoot = QString("%1/web").arg(ServerPathUtils::getDataDirectory());
|
||||
|
||||
// setup an httpManager with us as the request handler and the parent
|
||||
_httpManager = new HTTPManager(QHostAddress::AnyIPv4, port, documentRoot, this, this);
|
||||
|
@ -1178,7 +1179,7 @@ void OctreeServer::domainSettingsRequestComplete() {
|
|||
if (persistPath.isRelative()) {
|
||||
// if the domain settings passed us a relative path, make an absolute path that is relative to the
|
||||
// default data directory
|
||||
persistAbsoluteFilePath = QDir(PathUtils::getAppDataFilePath("entities/")).absoluteFilePath(_persistFilePath);
|
||||
persistAbsoluteFilePath = QDir(ServerPathUtils::getDataFilePath("entities/")).absoluteFilePath(_persistFilePath);
|
||||
}
|
||||
|
||||
static const QString ENTITY_PERSIST_EXTENSION = ".json.gz";
|
||||
|
@ -1244,7 +1245,7 @@ void OctreeServer::domainSettingsRequestComplete() {
|
|||
QDir backupDirectory { _backupDirectoryPath };
|
||||
QString absoluteBackupDirectory;
|
||||
if (backupDirectory.isRelative()) {
|
||||
absoluteBackupDirectory = QDir(PathUtils::getAppDataFilePath("entities/")).absoluteFilePath(_backupDirectoryPath);
|
||||
absoluteBackupDirectory = QDir(ServerPathUtils::getDataFilePath("entities/")).absoluteFilePath(_backupDirectoryPath);
|
||||
absoluteBackupDirectory = QDir(absoluteBackupDirectory).absolutePath();
|
||||
} else {
|
||||
absoluteBackupDirectory = backupDirectory.absolutePath();
|
||||
|
|
|
@ -58,8 +58,6 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig
|
|||
|
||||
DependencyManager::registerInheritance<SpatialParentFinder, AssignmentParentFinder>();
|
||||
|
||||
DependencyManager::set<AudioScriptingInterface>();
|
||||
|
||||
DependencyManager::set<ResourceCacheSharedItems>();
|
||||
DependencyManager::set<SoundCache>();
|
||||
DependencyManager::set<AudioInjectorManager>();
|
||||
|
@ -326,26 +324,7 @@ void EntityScriptServer::nodeActivated(SharedNodePointer activatedNode) {
|
|||
void EntityScriptServer::nodeKilled(SharedNodePointer killedNode) {
|
||||
switch (killedNode->getType()) {
|
||||
case NodeType::EntityServer: {
|
||||
// Before we clear, make sure this was our only entity server.
|
||||
// Otherwise we're assuming that we have "trading" entity servers
|
||||
// (an old one going away and a new one coming onboard)
|
||||
// and that we shouldn't clear here because we're still doing work.
|
||||
bool hasAnotherEntityServer = false;
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
nodeList->eachNodeBreakable([&hasAnotherEntityServer, &killedNode](const SharedNodePointer& node){
|
||||
if (node->getType() == NodeType::EntityServer && node->getUUID() != killedNode->getUUID()) {
|
||||
// we're talking to > 1 entity servers, we know we won't clear
|
||||
hasAnotherEntityServer = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!hasAnotherEntityServer) {
|
||||
clear();
|
||||
}
|
||||
clear();
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -416,8 +395,7 @@ void EntityScriptServer::selectAudioFormat(const QString& selectedCodecName) {
|
|||
|
||||
void EntityScriptServer::resetEntitiesScriptEngine() {
|
||||
auto engineName = QString("about:Entities %1").arg(++_entitiesScriptEngineCount);
|
||||
auto newEngine = QSharedPointer<ScriptEngine>(new ScriptEngine(ScriptEngine::ENTITY_SERVER_SCRIPT, NO_SCRIPT, engineName),
|
||||
&ScriptEngine::deleteLater);
|
||||
auto newEngine = QSharedPointer<ScriptEngine>(new ScriptEngine(ScriptEngine::ENTITY_SERVER_SCRIPT, NO_SCRIPT, engineName));
|
||||
|
||||
auto webSocketServerConstructorValue = newEngine->newFunction(WebSocketServerClass::constructor);
|
||||
newEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue);
|
||||
|
@ -477,13 +455,13 @@ void EntityScriptServer::addingEntity(const EntityItemID& entityID) {
|
|||
|
||||
void EntityScriptServer::deletingEntity(const EntityItemID& entityID) {
|
||||
if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->unloadEntityScript(entityID, true);
|
||||
_entitiesScriptEngine->unloadEntityScript(entityID);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityScriptServer::entityServerScriptChanging(const EntityItemID& entityID, const bool reload) {
|
||||
if (_entityViewer.getTree() && !_shuttingDown) {
|
||||
_entitiesScriptEngine->unloadEntityScript(entityID, true);
|
||||
_entitiesScriptEngine->unloadEntityScript(entityID);
|
||||
checkAndCallPreload(entityID, reload);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,9 +24,9 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT)
|
|||
TARGET ${TARGET_NAME}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DBUNDLE_EXECUTABLE="$<TARGET_FILE:${TARGET_NAME}>"
|
||||
-DBUNDLE_PLUGIN_DIR="$<TARGET_FILE_DIR:${TARGET_NAME}>/${PLUGIN_PATH}"
|
||||
-P "${CMAKE_CURRENT_BINARY_DIR}/FixupBundlePostBuild.cmake"
|
||||
-DBUNDLE_EXECUTABLE=$<TARGET_FILE:${TARGET_NAME}>
|
||||
-DBUNDLE_PLUGIN_DIR=$<TARGET_FILE_DIR:${TARGET_NAME}>/${PLUGIN_PATH}
|
||||
-P ${CMAKE_CURRENT_BINARY_DIR}/FixupBundlePostBuild.cmake
|
||||
)
|
||||
|
||||
find_program(WINDEPLOYQT_COMMAND windeployqt PATHS ${QT_DIR}/bin NO_DEFAULT_PATH)
|
||||
|
@ -39,27 +39,27 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT)
|
|||
add_custom_command(
|
||||
TARGET ${TARGET_NAME}
|
||||
POST_BUILD
|
||||
COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} ${EXTRA_DEPLOY_OPTIONS} $<$<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>,$<CONFIG:RelWithDebInfo>>:--release> \"$<TARGET_FILE:${TARGET_NAME}>\""
|
||||
COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} ${EXTRA_DEPLOY_OPTIONS} $<$<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>,$<CONFIG:RelWithDebInfo>>:--release> $<TARGET_FILE:${TARGET_NAME}>"
|
||||
)
|
||||
|
||||
set(QTAUDIO_PATH "$<TARGET_FILE_DIR:${TARGET_NAME}>/audio")
|
||||
set(QTAUDIO_WIN7_PATH "$<TARGET_FILE_DIR:${TARGET_NAME}>/audioWin7/audio")
|
||||
set(QTAUDIO_WIN8_PATH "$<TARGET_FILE_DIR:${TARGET_NAME}>/audioWin8/audio")
|
||||
set(QTAUDIO_PATH $<TARGET_FILE_DIR:${TARGET_NAME}>/audio)
|
||||
set(QTAUDIO_WIN7_PATH $<TARGET_FILE_DIR:${TARGET_NAME}>/audioWin7/audio)
|
||||
set(QTAUDIO_WIN8_PATH $<TARGET_FILE_DIR:${TARGET_NAME}>/audioWin8/audio)
|
||||
|
||||
# copy qtaudio_wasapi.dll and qtaudio_windows.dll in the correct directories for runtime selection
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${QTAUDIO_WIN7_PATH}"
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${QTAUDIO_WIN8_PATH}"
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${QTAUDIO_WIN7_PATH}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${QTAUDIO_WIN8_PATH}
|
||||
# copy release DLLs
|
||||
COMMAND if exist "${QTAUDIO_PATH}/qtaudio_windows.dll" ( ${CMAKE_COMMAND} -E copy "${QTAUDIO_PATH}/qtaudio_windows.dll" "${QTAUDIO_WIN7_PATH}" )
|
||||
COMMAND if exist "${QTAUDIO_PATH}/qtaudio_windows.dll" ( ${CMAKE_COMMAND} -E copy "${WASAPI_DLL_PATH}/qtaudio_wasapi.dll" "${QTAUDIO_WIN8_PATH}" )
|
||||
COMMAND if exist ${QTAUDIO_PATH}/qtaudio_windows.dll ( ${CMAKE_COMMAND} -E copy ${QTAUDIO_PATH}/qtaudio_windows.dll ${QTAUDIO_WIN7_PATH} )
|
||||
COMMAND if exist ${QTAUDIO_PATH}/qtaudio_windows.dll ( ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapi.dll ${QTAUDIO_WIN8_PATH} )
|
||||
# copy debug DLLs
|
||||
COMMAND if exist "${QTAUDIO_PATH}/qtaudio_windowsd.dll" ( ${CMAKE_COMMAND} -E copy "${QTAUDIO_PATH}/qtaudio_windowsd.dll" "${QTAUDIO_WIN7_PATH}" )
|
||||
COMMAND if exist "${QTAUDIO_PATH}/qtaudio_windowsd.dll" ( ${CMAKE_COMMAND} -E copy "${WASAPI_DLL_PATH}/qtaudio_wasapid.dll" "${QTAUDIO_WIN8_PATH}" )
|
||||
COMMAND if exist ${QTAUDIO_PATH}/qtaudio_windowsd.dll ( ${CMAKE_COMMAND} -E copy ${QTAUDIO_PATH}/qtaudio_windowsd.dll ${QTAUDIO_WIN7_PATH} )
|
||||
COMMAND if exist ${QTAUDIO_PATH}/qtaudio_windowsd.dll ( ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapid.dll ${QTAUDIO_WIN8_PATH} )
|
||||
# remove directory
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory "${QTAUDIO_PATH}"
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${QTAUDIO_PATH}
|
||||
)
|
||||
|
||||
endif ()
|
||||
|
|
|
@ -14,7 +14,7 @@ macro(SYMLINK_OR_COPY_DIRECTORY_BESIDE_TARGET _SHOULD_SYMLINK _DIRECTORY _DESTIN
|
|||
# remove the current directory
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E remove_directory "$<TARGET_FILE_DIR:${TARGET_NAME}>/${_DESTINATION}"
|
||||
COMMAND "${CMAKE_COMMAND}" -E remove_directory $<TARGET_FILE_DIR:${TARGET_NAME}>/${_DESTINATION}
|
||||
)
|
||||
|
||||
if (${_SHOULD_SYMLINK})
|
||||
|
@ -48,8 +48,8 @@ macro(SYMLINK_OR_COPY_DIRECTORY_BESIDE_TARGET _SHOULD_SYMLINK _DIRECTORY _DESTIN
|
|||
# copy the directory
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${_DIRECTORY}"
|
||||
"$<TARGET_FILE_DIR:${TARGET_NAME}>/${_DESTINATION}"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${_DIRECTORY}
|
||||
$<TARGET_FILE_DIR:${TARGET_NAME}>/${_DESTINATION}
|
||||
)
|
||||
endif ()
|
||||
# glob everything in this directory - add a custom command to copy any files
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
#include <ShutdownEventListener.h>
|
||||
#include <UUID.h>
|
||||
#include <LogHandler.h>
|
||||
#include <PathUtils.h>
|
||||
#include <ServerPathUtils.h>
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
#include "DomainServerNodeData.h"
|
||||
|
@ -1618,7 +1618,7 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) {
|
|||
QDir pathForAssignmentScriptsDirectory() {
|
||||
static const QString SCRIPTS_DIRECTORY_NAME = "/scripts/";
|
||||
|
||||
QDir directory(PathUtils::getAppDataPath() + SCRIPTS_DIRECTORY_NAME);
|
||||
QDir directory(ServerPathUtils::getDataDirectory() + SCRIPTS_DIRECTORY_NAME);
|
||||
if (!directory.exists()) {
|
||||
directory.mkpath(".");
|
||||
qInfo() << "Created path to " << directory.path();
|
||||
|
|
|
@ -246,13 +246,10 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
|
|||
_agentPermissions[editorKey]->set(NodePermissions::Permission::canAdjustLocks);
|
||||
}
|
||||
|
||||
std::list<std::unordered_map<NodePermissionsKey, NodePermissionsPointer>> permissionsSets{
|
||||
_standardAgentPermissions.get(),
|
||||
_agentPermissions.get()
|
||||
};
|
||||
QList<QHash<NodePermissionsKey, NodePermissionsPointer>> permissionsSets;
|
||||
permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get();
|
||||
foreach (auto permissionsSet, permissionsSets) {
|
||||
for (auto entry : permissionsSet) {
|
||||
const auto& userKey = entry.first;
|
||||
foreach (NodePermissionsKey userKey, permissionsSet.keys()) {
|
||||
if (onlyEditorsAreRezzers) {
|
||||
if (permissionsSet[userKey]->can(NodePermissions::Permission::canAdjustLocks)) {
|
||||
permissionsSet[userKey]->set(NodePermissions::Permission::canRezPermanentEntities);
|
||||
|
@ -303,6 +300,7 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
|
|||
}
|
||||
|
||||
QVariantMap& DomainServerSettingsManager::getDescriptorsMap() {
|
||||
|
||||
static const QString DESCRIPTORS{ "descriptors" };
|
||||
|
||||
auto& settingsMap = getSettingsMap();
|
||||
|
@ -1357,12 +1355,18 @@ QStringList DomainServerSettingsManager::getAllKnownGroupNames() {
|
|||
// extract all the group names from the group-permissions and group-forbiddens settings
|
||||
QSet<QString> result;
|
||||
|
||||
for (const auto& entry : _groupPermissions.get()) {
|
||||
result += entry.first.first;
|
||||
QHashIterator<NodePermissionsKey, NodePermissionsPointer> i(_groupPermissions.get());
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
NodePermissionsKey key = i.key();
|
||||
result += key.first;
|
||||
}
|
||||
|
||||
for (const auto& entry : _groupForbiddens.get()) {
|
||||
result += entry.first.first;
|
||||
QHashIterator<NodePermissionsKey, NodePermissionsPointer> j(_groupForbiddens.get());
|
||||
while (j.hasNext()) {
|
||||
j.next();
|
||||
NodePermissionsKey key = j.key();
|
||||
result += key.first;
|
||||
}
|
||||
|
||||
return result.toList();
|
||||
|
@ -1373,17 +1377,20 @@ bool DomainServerSettingsManager::setGroupID(const QString& groupName, const QUu
|
|||
_groupIDs[groupName.toLower()] = groupID;
|
||||
_groupNames[groupID] = groupName;
|
||||
|
||||
|
||||
for (const auto& entry : _groupPermissions.get()) {
|
||||
auto& perms = entry.second;
|
||||
QHashIterator<NodePermissionsKey, NodePermissionsPointer> i(_groupPermissions.get());
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
NodePermissionsPointer perms = i.value();
|
||||
if (perms->getID().toLower() == groupName.toLower() && !perms->isGroup()) {
|
||||
changed = true;
|
||||
perms->setGroupID(groupID);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& entry : _groupForbiddens.get()) {
|
||||
auto& perms = entry.second;
|
||||
QHashIterator<NodePermissionsKey, NodePermissionsPointer> j(_groupForbiddens.get());
|
||||
while (j.hasNext()) {
|
||||
j.next();
|
||||
NodePermissionsPointer perms = j.value();
|
||||
if (perms->getID().toLower() == groupName.toLower() && !perms->isGroup()) {
|
||||
changed = true;
|
||||
perms->setGroupID(groupID);
|
||||
|
|
|
@ -189,7 +189,7 @@ endif()
|
|||
|
||||
# link required hifi libraries
|
||||
link_hifi_libraries(
|
||||
shared octree ktx gpu gl gpu-gl procedural model render
|
||||
shared octree gpu gl gpu-gl procedural model render
|
||||
recording fbx networking model-networking entities avatars
|
||||
audio audio-client animation script-engine physics
|
||||
render-utils entities-renderer ui auto-updater
|
||||
|
@ -288,7 +288,7 @@ if (APPLE)
|
|||
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||
"${CMAKE_SOURCE_DIR}/scripts"
|
||||
"$<TARGET_FILE_DIR:${TARGET_NAME}>/../Resources/scripts"
|
||||
$<TARGET_FILE_DIR:${TARGET_NAME}>/../Resources/scripts
|
||||
)
|
||||
|
||||
# call the fixup_interface macro to add required bundling commands for installation
|
||||
|
@ -299,10 +299,10 @@ else (APPLE)
|
|||
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||
"${PROJECT_SOURCE_DIR}/resources"
|
||||
"$<TARGET_FILE_DIR:${TARGET_NAME}>/resources"
|
||||
$<TARGET_FILE_DIR:${TARGET_NAME}>/resources
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||
"${CMAKE_SOURCE_DIR}/scripts"
|
||||
"$<TARGET_FILE_DIR:${TARGET_NAME}>/scripts"
|
||||
$<TARGET_FILE_DIR:${TARGET_NAME}>/scripts
|
||||
)
|
||||
|
||||
# link target to external libraries
|
||||
|
@ -337,7 +337,7 @@ endif()
|
|||
add_bugsplat()
|
||||
|
||||
if (WIN32)
|
||||
set(EXTRA_DEPLOY_OPTIONS "--qmldir \"${PROJECT_SOURCE_DIR}/resources/qml\"")
|
||||
set(EXTRA_DEPLOY_OPTIONS "--qmldir ${PROJECT_SOURCE_DIR}/resources/qml")
|
||||
|
||||
set(TARGET_INSTALL_DIR ${INTERFACE_INSTALL_DIR})
|
||||
set(TARGET_INSTALL_COMPONENT ${CLIENT_COMPONENT})
|
||||
|
|
|
@ -2,27 +2,7 @@
|
|||
"name": "Standard to Action",
|
||||
"channels": [
|
||||
{ "from": "Standard.LY", "to": "Actions.TranslateZ" },
|
||||
|
||||
{ "from": "Standard.LX",
|
||||
"when": [
|
||||
"Application.InHMD", "!Application.AdvancedMovement",
|
||||
"Application.SnapTurn", "!Standard.RX"
|
||||
],
|
||||
"to": "Actions.StepYaw",
|
||||
"filters":
|
||||
[
|
||||
{ "type": "deadZone", "min": 0.15 },
|
||||
"constrainToInteger",
|
||||
{ "type": "pulse", "interval": 0.25 },
|
||||
{ "type": "scale", "scale": 22.5 }
|
||||
]
|
||||
},
|
||||
{ "from": "Standard.LX", "to": "Actions.TranslateX",
|
||||
"when": [ "Application.AdvancedMovement" ]
|
||||
},
|
||||
{ "from": "Standard.LX", "to": "Actions.Yaw",
|
||||
"when": [ "!Application.AdvancedMovement", "!Application.SnapTurn" ]
|
||||
},
|
||||
{ "from": "Standard.LX", "to": "Actions.TranslateX" },
|
||||
|
||||
{ "from": "Standard.RX",
|
||||
"when": [ "Application.InHMD", "Application.SnapTurn" ],
|
||||
|
@ -35,29 +15,29 @@
|
|||
{ "type": "scale", "scale": 22.5 }
|
||||
]
|
||||
},
|
||||
{ "from": "Standard.RX", "to": "Actions.Yaw",
|
||||
"when": [ "!Application.SnapTurn" ]
|
||||
},
|
||||
|
||||
{ "from": "Standard.RY",
|
||||
"when": "Application.Grounded",
|
||||
"to": "Actions.Up",
|
||||
"filters":
|
||||
{ "from": "Standard.RX", "to": "Actions.Yaw" },
|
||||
{ "from": "Standard.RY",
|
||||
"when": "Application.Grounded",
|
||||
"to": "Actions.Up",
|
||||
"filters":
|
||||
[
|
||||
{ "type": "deadZone", "min": 0.6 },
|
||||
"invert"
|
||||
]
|
||||
},
|
||||
},
|
||||
|
||||
{ "from": "Standard.RY", "to": "Actions.Up", "filters": "invert"},
|
||||
{ "from": "Standard.RY", "to": "Actions.Up", "filters": "invert"},
|
||||
|
||||
{ "from": "Standard.Back", "to": "Actions.CycleCamera" },
|
||||
{ "from": "Standard.Start", "to": "Actions.ContextMenu" },
|
||||
|
||||
{ "from": "Standard.LT", "to": "Actions.LeftHandClick" },
|
||||
{ "from": "Standard.LT", "to": "Actions.LeftHandClick" },
|
||||
{ "from": "Standard.RT", "to": "Actions.RightHandClick" },
|
||||
|
||||
{ "from": "Standard.LeftHand", "to": "Actions.LeftHand" },
|
||||
{ "from": "Standard.LeftHand", "to": "Actions.LeftHand" },
|
||||
{ "from": "Standard.RightHand", "to": "Actions.RightHand" }
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
|
BIN
interface/resources/html/img/devices.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
interface/resources/html/img/models.png
Normal file
After Width: | Height: | Size: 8.5 KiB |
BIN
interface/resources/html/img/move.png
Normal file
After Width: | Height: | Size: 6 KiB |
BIN
interface/resources/html/img/run-script.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
interface/resources/html/img/talk.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
interface/resources/html/img/write-script.png
Normal file
After Width: | Height: | Size: 2 KiB |
187
interface/resources/html/interface-welcome.html
Normal file
125
interface/resources/icons/load-script.svg
Normal file
|
@ -0,0 +1,125 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="45"
|
||||
height="45"
|
||||
id="svg3827"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
sodipodi:docname="load-script.svg">
|
||||
<defs
|
||||
id="defs3829" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="3.959798"
|
||||
inkscape:cx="171.17264"
|
||||
inkscape:cy="-8.0710166"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1028"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata3832">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>T.Hofmeister</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1007.3622)">
|
||||
<g
|
||||
id="layer1-7"
|
||||
transform="matrix(-0.25019951,0,0,0.28877175,123.44112,917.40972)"
|
||||
style="fill:#696969">
|
||||
<g
|
||||
id="g3257"
|
||||
transform="matrix(1.9175,0,0,1.9175,-607.19,-179.09)"
|
||||
style="fill:#696969">
|
||||
<path
|
||||
id="path3224"
|
||||
d="m 563.06,260.72 -1.25,0.16 -40.15,4.87 -1.66,0.22 0.38,1.62 c 3.78,16.93 -0.59,34.07 -2.88,51.57 l -0.22,1.84 1.81,-0.16 42,-3 1.25,-0.06 0.16,-1.25 c 2.13,-17.86 6.92,-36.12 0.94,-54.62 l -0.38,-1.19 z m -2.18,3.28 c 4.96,16.72 -1.56,33.17 -3.72,50.47 l -35.63,2.78 c 2.25,-16.14 7.03,-29.55 3.78,-46.19 L 560.88,264 z"
|
||||
style="fill:#696969;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3237"
|
||||
d="m 531.39,275.61 23.34,-4.6"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3243"
|
||||
d="m 532.1,280.91 22.27,-2.83"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3247"
|
||||
d="m 531.92,285.86 22.89,-1.95"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3249"
|
||||
d="m 530.15,292.84 24.22,-2.12"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3251"
|
||||
d="m 529.45,297.53 22.98,-0.8"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3253"
|
||||
d="m 528.65,304.24 22.45,-2.56"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3255"
|
||||
d="m 527.5,309.37 22.81,-1.77"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
<path
|
||||
display="block"
|
||||
d="m 22.50945,1041.7031 c 10.249979,14.5758 18.326744,5.8628 15.179173,-14.1826 l 3.401516,-0.2354 -8.04206,-17.0393 -2.800459,17.789 3.507825,-0.2428 c 2.535261,14.6877 0.402108,18.0407 -11.324416,13.7916 z"
|
||||
style="color:#000000;fill:#a9a9a9;stroke:#000000;stroke-width:0.61923206;display:block"
|
||||
id="path1432"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
<rect
|
||||
style="color:#000000;fill:#00ffff;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:0.42527184;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="rect3032"
|
||||
width="44.57473"
|
||||
height="44.57473"
|
||||
x="0.18852967"
|
||||
y="1007.5989" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.5 KiB |
129
interface/resources/icons/new-script.svg
Normal file
|
@ -0,0 +1,129 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="45"
|
||||
height="45"
|
||||
id="svg3827"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
sodipodi:docname="new-script - Kopie (2).svg">
|
||||
<defs
|
||||
id="defs3829" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.6"
|
||||
inkscape:cx="39.376692"
|
||||
inkscape:cy="9.0006701"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1028"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata3832">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>T.Hofmeister</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1007.3622)">
|
||||
<rect
|
||||
style="color:#000000;fill:#00ffff;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:0.42527184;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="rect3032"
|
||||
width="44.57473"
|
||||
height="44.57473"
|
||||
x="0.18852967"
|
||||
y="1007.5989" />
|
||||
<g
|
||||
id="layer1-7-7"
|
||||
transform="matrix(-0.23943965,0,0,0.3096188,117.90945,912.00498)"
|
||||
style="fill:#696969">
|
||||
<g
|
||||
id="g3257-4"
|
||||
transform="matrix(1.9175,0,0,1.9175,-607.19,-179.09)"
|
||||
style="fill:#696969">
|
||||
<path
|
||||
id="path3224-0"
|
||||
d="m 563.06,260.72 -1.25,0.16 -40.15,4.87 -1.66,0.22 0.38,1.62 c 3.78,16.93 -0.59,34.07 -2.88,51.57 l -0.22,1.84 1.81,-0.16 42,-3 1.25,-0.06 0.16,-1.25 c 2.13,-17.86 6.92,-36.12 0.94,-54.62 l -0.38,-1.19 z m -2.18,3.28 c 4.96,16.72 -1.56,33.17 -3.72,50.47 l -35.63,2.78 c 2.25,-16.14 7.03,-29.55 3.78,-46.19 L 560.88,264 z"
|
||||
style="fill:#696969;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3237-9"
|
||||
d="m 531.39,275.61 23.34,-4.6"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3243-4"
|
||||
d="m 532.1,280.91 22.27,-2.83"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3247-8"
|
||||
d="m 531.92,285.86 22.89,-1.95"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3249-8"
|
||||
d="m 530.15,292.84 24.22,-2.12"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3251-2"
|
||||
d="m 529.45,297.53 22.98,-0.8"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3253-4"
|
||||
d="m 528.65,304.24 22.45,-2.56"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3255-5"
|
||||
d="m 527.5,309.37 22.81,-1.77"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="layer1-1"
|
||||
transform="matrix(0.85515704,0.72492349,-0.91920854,2.1402565,983.21735,-1213.0824)"
|
||||
style="fill:#a9a9a9;stroke:#000000">
|
||||
<path
|
||||
style="fill:#a9a9a9;fill-opacity:1;stroke:#000000;stroke-width:0.25;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
id="path2996"
|
||||
transform="translate(0,1036.3622)"
|
||||
d="m 3.4723994,8.5185577 3.0304576,-7.0710678 6.944799,0 -5.0191957,5.08233 4.1353117,0 -8.1127873,9.0913731 2.0834396,-7.1342026 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.7 KiB |
674
interface/resources/icons/save-script.svg
Normal file
|
@ -0,0 +1,674 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="45"
|
||||
height="45"
|
||||
id="svg3827"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
sodipodi:docname="load-script - Kopie.svg">
|
||||
<defs
|
||||
id="defs3829">
|
||||
<linearGradient
|
||||
x1="28.061"
|
||||
x2="28.061"
|
||||
y1="31.431"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="36.437"
|
||||
id="linearGradient6971">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#ddd"
|
||||
id="stop6967" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#fdfdfd"
|
||||
id="stop6969" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
x1="12.25"
|
||||
x2="7"
|
||||
y1="18.25"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="21.118"
|
||||
id="linearGradient6931">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#204a87"
|
||||
id="stop6927" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-opacity="0"
|
||||
stop-color="#204a87"
|
||||
id="stop6929" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
x1="14.752"
|
||||
x2="8.8953"
|
||||
y1="15.868"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="16.743"
|
||||
id="linearGradient6907">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#3465a4"
|
||||
id="stop6903" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-opacity="0"
|
||||
stop-color="#3465a4"
|
||||
id="stop6905" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
x1="33.431"
|
||||
x2="21.748"
|
||||
y1="31.965"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="11.781"
|
||||
id="linearGradient2553">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#fff"
|
||||
id="stop2557" />
|
||||
<stop
|
||||
offset=".5"
|
||||
stop-color="#e6e6e6"
|
||||
id="stop2561" />
|
||||
<stop
|
||||
offset=".75"
|
||||
stop-color="#fff"
|
||||
id="stop2563" />
|
||||
<stop
|
||||
offset=".84167"
|
||||
stop-color="#e1e1e1"
|
||||
id="stop2565" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#fff"
|
||||
id="stop2559" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="26.357"
|
||||
y1="11.319"
|
||||
gradientTransform="translate(0,5.125)"
|
||||
x2="23.688"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="23.688"
|
||||
id="linearGradient4272">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-opacity=".2549"
|
||||
stop-color="#fff"
|
||||
id="stop4276" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#fff"
|
||||
id="stop4278" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="47.621"
|
||||
y1="4.4331"
|
||||
gradientTransform="translate(0,5.125)"
|
||||
x2="44.096"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="12.378"
|
||||
id="linearGradient4260">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#fff"
|
||||
id="stop4256" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-opacity="0"
|
||||
stop-color="#fff"
|
||||
id="stop4258" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="20.936"
|
||||
gradientTransform="matrix(1.2862,0.7817,-0.71078,1.1696,-2.3543,0.24814)"
|
||||
cx="15.571"
|
||||
cy="2.9585"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient4250">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#e4e4e4"
|
||||
id="stop4246" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#d3d3d3"
|
||||
id="stop4248" />
|
||||
</radialGradient>
|
||||
<linearGradient
|
||||
y2="33.759"
|
||||
y1="37.206"
|
||||
gradientTransform="translate(0,5.125)"
|
||||
x2="12.222"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="12.277"
|
||||
id="linearGradient4242">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#eee"
|
||||
id="stop4238" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-opacity="0"
|
||||
stop-color="#eee"
|
||||
id="stop4240" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="40.944"
|
||||
y1="28.481"
|
||||
gradientTransform="translate(0,5.125)"
|
||||
x2="36.183"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="7.6046"
|
||||
id="linearGradient4234">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#bbb"
|
||||
id="stop4230" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#9f9f9f"
|
||||
id="stop4232" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="35.281"
|
||||
y1="35.281"
|
||||
gradientTransform="translate(0.79549,3.7992)"
|
||||
x2="24.688"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="7.0625"
|
||||
id="linearGradient4209">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#838383"
|
||||
id="stop4186" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-opacity="0"
|
||||
stop-color="#bbb"
|
||||
id="stop4188" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="15.645"
|
||||
gradientTransform="matrix(1,0,0,0.53672,0,16.873)"
|
||||
cx="24.837"
|
||||
cy="36.421"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient8668">
|
||||
<stop
|
||||
offset="0"
|
||||
id="stop8664" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-opacity="0"
|
||||
id="stop8666" />
|
||||
</radialGradient>
|
||||
<linearGradient
|
||||
y2="12.584"
|
||||
x2="12.624"
|
||||
gradientTransform="matrix(0.91411,0,0,0.91411,-3.8687,-2.7069)"
|
||||
y1="27.394"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="33.06"
|
||||
id="linearGradient1764">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#fff"
|
||||
id="stop2189" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-opacity="0"
|
||||
stop-color="#fff"
|
||||
id="stop2191" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="19.062"
|
||||
gradientTransform="matrix(-1.3145,-0.010063,-0.01023,1.3362,46.221,-4.9099)"
|
||||
cx="23.447"
|
||||
cy="6.4577"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient4997">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#fff"
|
||||
id="stop4993" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-opacity="0"
|
||||
stop-color="#fff"
|
||||
id="stop4995" />
|
||||
</radialGradient>
|
||||
<linearGradient
|
||||
y2="609.51"
|
||||
x2="302.86"
|
||||
gradientTransform="matrix(2.7744,0,0,1.9697,-1892.2,-872.89)"
|
||||
y1="366.65"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="302.86"
|
||||
id="linearGradient5027">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-opacity="0"
|
||||
id="stop5050" />
|
||||
<stop
|
||||
offset=".5"
|
||||
id="stop5056" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-opacity="0"
|
||||
id="stop5052" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="117.14"
|
||||
gradientTransform="matrix(2.7744,0,0,1.9697,-1891.6,-872.89)"
|
||||
cx="605.71"
|
||||
cy="486.65"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
xlink:href="#linearGradient5060"
|
||||
id="radialGradient5029" />
|
||||
<linearGradient
|
||||
id="linearGradient5060">
|
||||
<stop
|
||||
offset="0"
|
||||
id="stop5062" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-opacity="0"
|
||||
id="stop5064" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="117.14"
|
||||
gradientTransform="matrix(-2.7744,0,0,1.9697,112.76,-872.89)"
|
||||
cx="605.71"
|
||||
cy="486.65"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
xlink:href="#linearGradient5060"
|
||||
id="radialGradient5031" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6971"
|
||||
id="linearGradient3239"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="28.061"
|
||||
y1="31.431"
|
||||
x2="28.061"
|
||||
y2="36.437"
|
||||
gradientTransform="translate(51.972416,1005.3761)" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#radialGradient8668"
|
||||
id="radialGradient3253"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.1302,0,0,-0.40769251,48.062716,1046.2254)"
|
||||
cx="24.837"
|
||||
cy="36.421"
|
||||
r="15.645" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5027"
|
||||
id="linearGradient3361"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.7744,0,0,1.9697,-1892.2,-872.89)"
|
||||
x1="302.86"
|
||||
y1="366.65"
|
||||
x2="302.86"
|
||||
y2="609.51" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5060"
|
||||
id="radialGradient3363"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.7744,0,0,1.9697,-1891.6,-872.89)"
|
||||
cx="605.71"
|
||||
cy="486.65"
|
||||
r="117.14" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5060"
|
||||
id="radialGradient3365"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(-2.7744,0,0,1.9697,112.76,-872.89)"
|
||||
cx="605.71"
|
||||
cy="486.65"
|
||||
r="117.14" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4234"
|
||||
id="linearGradient3367"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(0,5.125)"
|
||||
x1="7.6046"
|
||||
y1="28.481"
|
||||
x2="36.183"
|
||||
y2="40.944" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#radialGradient4250"
|
||||
id="radialGradient3369"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.2862,0.7817,-0.71078,1.1696,-2.3543,0.24814)"
|
||||
cx="15.571"
|
||||
cy="2.9585"
|
||||
r="20.936" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4209"
|
||||
id="linearGradient3371"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(0.79549,3.7992)"
|
||||
x1="7.0625"
|
||||
y1="35.281"
|
||||
x2="24.688"
|
||||
y2="35.281" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4242"
|
||||
id="linearGradient3373"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(0,5.125)"
|
||||
x1="12.277"
|
||||
y1="37.206"
|
||||
x2="12.222"
|
||||
y2="33.759" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4272"
|
||||
id="linearGradient3375"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(0,5.125)"
|
||||
x1="23.688"
|
||||
y1="11.319"
|
||||
x2="23.688"
|
||||
y2="26.357" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4260"
|
||||
id="linearGradient3377"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(0,5.125)"
|
||||
x1="12.378"
|
||||
y1="4.4331"
|
||||
x2="44.096"
|
||||
y2="47.621" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2553"
|
||||
id="linearGradient3379"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="33.431"
|
||||
y1="31.965"
|
||||
x2="21.748"
|
||||
y2="11.781" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#radialGradient8668"
|
||||
id="radialGradient3381"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.1302,0,0,-0.40769251,48.062716,1046.2254)"
|
||||
cx="24.837"
|
||||
cy="36.421"
|
||||
r="15.645" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6971"
|
||||
id="linearGradient3383"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(51.972416,1005.3761)"
|
||||
x1="28.061"
|
||||
y1="31.431"
|
||||
x2="28.061"
|
||||
y2="36.437" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.6"
|
||||
inkscape:cx="113.76169"
|
||||
inkscape:cy="-12.107928"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1028"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata3832">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>T.Hofmeister</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1007.3622)">
|
||||
<g
|
||||
id="g3385">
|
||||
<g
|
||||
transform="matrix(0.87964671,0,0,1,-44.027424,-2)"
|
||||
id="g3255">
|
||||
<g
|
||||
id="layer2"
|
||||
transform="translate(51.972416,1005.3761)">
|
||||
<g
|
||||
id="g5022"
|
||||
transform="matrix(0.024114,0,0,0.019292,45.49,41.752)">
|
||||
<rect
|
||||
id="rect4173"
|
||||
style="opacity:0.40206;color:#000000;fill:url(#linearGradient3361)"
|
||||
height="478.35999"
|
||||
width="1339.6"
|
||||
y="-150.7"
|
||||
x="-1559.3" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path5058"
|
||||
style="opacity:0.40206;color:#000000;fill:url(#radialGradient3363)"
|
||||
d="m -219.62,-150.68 v 478.33 c 142.88,0.9 345.4,-107.17 345.4,-239.2 0,-132.02 -159.44,-239.13 -345.4,-239.13 z" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path5018"
|
||||
style="opacity:0.40206;color:#000000;fill:url(#radialGradient3365)"
|
||||
d="m -1559.3,-150.68 v 478.33 c -142.8,0.9 -345.4,-107.17 -345.4,-239.2 0,-132.02 159.5,-239.13 345.4,-239.13 z" />
|
||||
</g>
|
||||
<path
|
||||
style="fill:none;stroke:#535353;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4196"
|
||||
d="m 11.286,13.088 c -0.625,0 -1.032,0.29 -1.282,0.843 L 3.5357,31.035 c 0,0 -0.25,0.671 -0.25,1.781 v 9.65 c 0,1.083 0.6578,1.625 1.6562,1.625 h 38.562 c 0.985,0 1.594,-0.718 1.594,-1.844 v -9.65 c 0,0 0.106,-0.77 -0.094,-1.312 l -6.718,-17.197 c -0.185,-0.512 -0.637,-0.988 -1.125,-1 h -25.875 z" />
|
||||
<path
|
||||
style="fill:url(#linearGradient3367);fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4170"
|
||||
d="m 3.2736,32.122 0.7646,-0.692 37.61,0.062 3.462,0.317 v 10.439 c 0,1.125 -0.607,1.843 -1.592,1.843 H 4.9352 c -0.998,0 -1.6614,-0.542 -1.6614,-1.624 V 32.122 z" />
|
||||
<path
|
||||
style="fill:url(#radialGradient3369);fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3093"
|
||||
d="m 3.5491,31.039 c -0.7143,1.465 -6e-4,2.393 1.0357,2.393 h 39 c 1.119,-0.024 1.845,-1.012 1.428,-2.143 l -6.714,-17.21 c -0.184,-0.512 -0.655,-0.988 -1.143,-1 h -25.857 c -0.625,0 -1.036,0.303 -1.286,0.857 L 3.5489,31.039 z" />
|
||||
<rect
|
||||
id="rect4174"
|
||||
style="color:#000000;fill:url(#linearGradient3371);fill-rule:evenodd"
|
||||
height="5.5625"
|
||||
width="17.625"
|
||||
y="36.299"
|
||||
x="7.8579998" />
|
||||
<path
|
||||
style="opacity:0.81142997;fill:url(#linearGradient3373);fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4194"
|
||||
d="M 7.858,41.862 V 37.85 c 1.8355,3.179 8.296,4.012 12.937,4.012 H 7.858 z" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4201"
|
||||
d="m 44.796,30.754 c 0.064,1.25 -0.414,2.316 -1.322,2.343 H 5.355 c -1.2889,0 -1.8674,-0.325 -2.0837,-0.868 0.0917,0.945 0.8258,1.65 2.084,1.65 h 38.119 c 1.076,-0.033 1.753,-1.424 1.352,-2.995 l -0.03,-0.13 z" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4211"
|
||||
style="opacity:0.69142995;color:#000000;fill:url(#linearGradient3375);fill-rule:evenodd"
|
||||
d="m 10.969,15.281 c -0.046,0.201 -0.188,0.387 -0.188,0.594 0,0.949 0.591,1.789 1.344,2.594 0.24,-0.154 0.365,-0.355 0.625,-0.5 -0.94,-0.816 -1.553,-1.717 -1.781,-2.688 z m 26.656,0 c -0.229,0.97 -0.842,1.873 -1.781,2.688 0.274,0.153 0.404,0.368 0.656,0.531 0.757,-0.807 1.312,-1.673 1.312,-2.625 0,-0.207 -0.141,-0.393 -0.187,-0.594 z m 2.187,8.438 c -0.613,4.04 -7.298,7.25 -15.531,7.25 -8.212,0 -14.86,-3.193 -15.5,-7.219 -0.0321,0.197 -0.1248,0.392 -0.1248,0.594 10e-5,4.318 6.9888,7.844 15.625,7.844 8.636,0 15.656,-3.526 15.657,-7.844 0,-0.213 -0.09,-0.418 -0.126,-0.625 z" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4224"
|
||||
style="color:#000000;fill:#ffffff;fill-opacity:0.45762999;fill-rule:evenodd"
|
||||
d="m 8.5737,25.594 a 1.37005,1.0165371 0 1 1 -2.7401,0 1.37005,1.0165371 0 1 1 2.7401,0 z"
|
||||
transform="translate(0.088388,5.3018)" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4226"
|
||||
style="color:#000000;fill:#ffffff;fill-opacity:0.45762999;fill-rule:evenodd"
|
||||
d="m 8.5737,25.594 a 1.37005,1.0165371 0 1 1 -2.7401,0 1.37005,1.0165371 0 1 1 2.7401,0 z"
|
||||
transform="translate(33.967,5.2134)" />
|
||||
<path
|
||||
style="fill:none;stroke:url(#linearGradient3377);stroke-linecap:round;stroke-linejoin:round"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4252"
|
||||
d="m 11.643,13.541 c -0.602,0 -0.993,0.279 -1.234,0.812 L 3.994,30.944 c 0,0 -0.2406,0.646 -0.2406,1.715 v 9.29 c 0,1.354 0.444,1.627 1.5944,1.627 h 37.687 c 1.323,0 1.534,-0.317 1.534,-1.838 v -9.29 c 0,0 0.102,-0.742 -0.09,-1.264 l -6.593,-16.806 c -0.178,-0.492 -0.551,-0.826 -1.021,-0.837 h -25.222 z" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:square;stroke-opacity:0.42372999"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4282"
|
||||
d="m 40.5,36.554 v 5.021" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:square;stroke-opacity:0.42372999"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4284"
|
||||
d="m 38.5,36.614 v 5.021" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:square;stroke-opacity:0.42372999"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4286"
|
||||
d="m 36.5,36.614 v 5.021" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:square;stroke-opacity:0.42372999"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4288"
|
||||
d="m 34.5,36.614 v 5.021" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:square;stroke-opacity:0.42372999"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4290"
|
||||
d="m 32.5,36.614 v 5.021" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:square;stroke-opacity:0.42372999"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4292"
|
||||
d="m 30.5,36.614 v 5.021" />
|
||||
<path
|
||||
style="opacity:0.09714302;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:square"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4294"
|
||||
d="m 39.5,36.604 v 5.021" />
|
||||
<path
|
||||
style="opacity:0.09714302;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:square"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4296"
|
||||
d="m 37.5,36.664 v 5.021" />
|
||||
<path
|
||||
style="opacity:0.09714302;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:square"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4298"
|
||||
d="m 35.5,36.664 v 5.021" />
|
||||
<path
|
||||
style="opacity:0.09714302;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:square"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4300"
|
||||
d="m 33.5,36.664 v 5.021" />
|
||||
<path
|
||||
style="opacity:0.09714302;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:square"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4302"
|
||||
d="m 31.5,36.664 v 5.021" />
|
||||
<path
|
||||
style="opacity:0.43999999;fill:#ffffff;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4572"
|
||||
d="m 7.875,36.312 v 5.532 H 20.438 L 8.219,41.5 7.875,36.312 z" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path2545"
|
||||
style="opacity:0.20571002;color:#000000;fill:url(#linearGradient3379);fill-rule:evenodd"
|
||||
d="m 39.875,19.562 a 14.875,6.6875 0 1 1 -29.75,0 14.875,6.6875 0 1 1 29.75,0 z"
|
||||
transform="matrix(1.0378,0,0,1.0607,-1.6329,3.0304)" />
|
||||
</g>
|
||||
<path
|
||||
d="m 93.815472,1031.3767 a 17.681979,6.3782852 0 1 0 -35.363958,0 17.681979,6.3782852 0 1 0 35.363958,0 z"
|
||||
style="opacity:0.14118;color:#000000;fill:url(#radialGradient3381);fill-rule:evenodd"
|
||||
id="path8660"
|
||||
inkscape:connector-curvature="0" />
|
||||
<rect
|
||||
x="56.535915"
|
||||
y="1035.6741"
|
||||
width="39.248001"
|
||||
height="12.278"
|
||||
ry="1.625"
|
||||
rx="1.625"
|
||||
display="block"
|
||||
style="color:#000000;fill:url(#linearGradient3383);stroke:#7d7d7d;stroke-linecap:round;display:block"
|
||||
id="rect6951" />
|
||||
<rect
|
||||
x="58.972416"
|
||||
y="1038.3761"
|
||||
width="16"
|
||||
height="7"
|
||||
ry="0"
|
||||
display="block"
|
||||
style="opacity:0.59658999;color:#000000;fill:#7d7d7d;display:block"
|
||||
id="rect6953" />
|
||||
<rect
|
||||
display="block"
|
||||
height="9"
|
||||
x="75.97242"
|
||||
y="1037.3761"
|
||||
width="1"
|
||||
style="color:#000000;display:block"
|
||||
id="rect6957" />
|
||||
</g>
|
||||
<path
|
||||
sodipodi:nodetypes="cccccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path1432"
|
||||
style="color:#000000;fill:#a9a9a9;stroke:#000000;stroke-width:0.48586071;display:block"
|
||||
d="m 6.087091,1019.0168 c -2.4783484,-13.3382 7.140839,-12.9964 16.821938,-0.5354 l 2.083662,-1.7011 5.154697,13.4755 -11.796108,-8.0529 2.148807,-1.7542 c -6.962373,-9.2534 -10.105498,-9.9089 -14.389461,-1.3256 z"
|
||||
display="block" />
|
||||
<rect
|
||||
y="1007.8514"
|
||||
x="0.20883489"
|
||||
height="44.57473"
|
||||
width="44.57473"
|
||||
id="rect3032"
|
||||
style="color:#000000;fill:#00ffff;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:0.42527184;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 23 KiB |
550
interface/resources/icons/start-script.svg
Normal file
|
@ -0,0 +1,550 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="45"
|
||||
height="45"
|
||||
id="svg3827"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.3.1 r9886"
|
||||
sodipodi:docname="start-script.svg">
|
||||
<defs
|
||||
id="defs3829">
|
||||
<radialGradient
|
||||
r="15.645"
|
||||
cy="36.421"
|
||||
cx="24.837"
|
||||
gradientTransform="matrix(1.1302,0,0,-0.40769251,48.062716,1046.2254)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3253"
|
||||
xlink:href="#radialGradient8668"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
gradientTransform="translate(51.972416,1005.3761)"
|
||||
y2="36.437"
|
||||
x2="28.061"
|
||||
y1="31.431"
|
||||
x1="28.061"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3239"
|
||||
xlink:href="#linearGradient6971"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
id="radialGradient5031"
|
||||
xlink:href="#linearGradient5060"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cy="486.65"
|
||||
cx="605.71"
|
||||
gradientTransform="matrix(-2.7744,0,0,1.9697,112.76,-872.89)"
|
||||
r="117.14" />
|
||||
<linearGradient
|
||||
id="linearGradient5060">
|
||||
<stop
|
||||
id="stop5062"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop5064"
|
||||
stop-opacity="0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
id="radialGradient5029"
|
||||
xlink:href="#linearGradient5060"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cy="486.65"
|
||||
cx="605.71"
|
||||
gradientTransform="matrix(2.7744,0,0,1.9697,-1891.6,-872.89)"
|
||||
r="117.14" />
|
||||
<linearGradient
|
||||
id="linearGradient5027"
|
||||
x1="302.86"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y1="366.65"
|
||||
gradientTransform="matrix(2.7744,0,0,1.9697,-1892.2,-872.89)"
|
||||
x2="302.86"
|
||||
y2="609.51">
|
||||
<stop
|
||||
id="stop5050"
|
||||
stop-opacity="0"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop5056"
|
||||
offset=".5" />
|
||||
<stop
|
||||
id="stop5052"
|
||||
stop-opacity="0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
id="radialGradient4997"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cy="6.4577"
|
||||
cx="23.447"
|
||||
gradientTransform="matrix(-1.3145,-0.010063,-0.01023,1.3362,46.221,-4.9099)"
|
||||
r="19.062">
|
||||
<stop
|
||||
id="stop4993"
|
||||
stop-color="#fff"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4995"
|
||||
stop-color="#fff"
|
||||
stop-opacity="0"
|
||||
offset="1" />
|
||||
</radialGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1764"
|
||||
x1="33.06"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y1="27.394"
|
||||
gradientTransform="matrix(0.91411,0,0,0.91411,-3.8687,-2.7069)"
|
||||
x2="12.624"
|
||||
y2="12.584">
|
||||
<stop
|
||||
id="stop2189"
|
||||
stop-color="#fff"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop2191"
|
||||
stop-color="#fff"
|
||||
stop-opacity="0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
id="radialGradient8668"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cy="36.421"
|
||||
cx="24.837"
|
||||
gradientTransform="matrix(1,0,0,0.53672,0,16.873)"
|
||||
r="15.645">
|
||||
<stop
|
||||
id="stop8664"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop8666"
|
||||
stop-opacity="0"
|
||||
offset="1" />
|
||||
</radialGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4209"
|
||||
x1="7.0625"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="24.688"
|
||||
gradientTransform="translate(0.79549,3.7992)"
|
||||
y1="35.281"
|
||||
y2="35.281">
|
||||
<stop
|
||||
id="stop4186"
|
||||
stop-color="#838383"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4188"
|
||||
stop-color="#bbb"
|
||||
stop-opacity="0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4234"
|
||||
x1="7.6046"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="36.183"
|
||||
gradientTransform="translate(0,5.125)"
|
||||
y1="28.481"
|
||||
y2="40.944">
|
||||
<stop
|
||||
id="stop4230"
|
||||
stop-color="#bbb"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4232"
|
||||
stop-color="#9f9f9f"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4242"
|
||||
x1="12.277"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="12.222"
|
||||
gradientTransform="translate(0,5.125)"
|
||||
y1="37.206"
|
||||
y2="33.759">
|
||||
<stop
|
||||
id="stop4238"
|
||||
stop-color="#eee"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4240"
|
||||
stop-color="#eee"
|
||||
stop-opacity="0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
id="radialGradient4250"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cy="2.9585"
|
||||
cx="15.571"
|
||||
gradientTransform="matrix(1.2862,0.7817,-0.71078,1.1696,-2.3543,0.24814)"
|
||||
r="20.936">
|
||||
<stop
|
||||
id="stop4246"
|
||||
stop-color="#e4e4e4"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4248"
|
||||
stop-color="#d3d3d3"
|
||||
offset="1" />
|
||||
</radialGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4260"
|
||||
x1="12.378"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="44.096"
|
||||
gradientTransform="translate(0,5.125)"
|
||||
y1="4.4331"
|
||||
y2="47.621">
|
||||
<stop
|
||||
id="stop4256"
|
||||
stop-color="#fff"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4258"
|
||||
stop-color="#fff"
|
||||
stop-opacity="0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4272"
|
||||
x1="23.688"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="23.688"
|
||||
gradientTransform="translate(0,5.125)"
|
||||
y1="11.319"
|
||||
y2="26.357">
|
||||
<stop
|
||||
id="stop4276"
|
||||
stop-color="#fff"
|
||||
stop-opacity=".2549"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4278"
|
||||
stop-color="#fff"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2553"
|
||||
y2="11.781"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y1="31.965"
|
||||
x2="21.748"
|
||||
x1="33.431">
|
||||
<stop
|
||||
id="stop2557"
|
||||
stop-color="#fff"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop2561"
|
||||
stop-color="#e6e6e6"
|
||||
offset=".5" />
|
||||
<stop
|
||||
id="stop2563"
|
||||
stop-color="#fff"
|
||||
offset=".75" />
|
||||
<stop
|
||||
id="stop2565"
|
||||
stop-color="#e1e1e1"
|
||||
offset=".84167" />
|
||||
<stop
|
||||
id="stop2559"
|
||||
stop-color="#fff"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient6907"
|
||||
y2="16.743"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y1="15.868"
|
||||
x2="8.8953"
|
||||
x1="14.752">
|
||||
<stop
|
||||
id="stop6903"
|
||||
stop-color="#3465a4"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop6905"
|
||||
stop-color="#3465a4"
|
||||
stop-opacity="0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient6931"
|
||||
y2="21.118"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y1="18.25"
|
||||
x2="7"
|
||||
x1="12.25">
|
||||
<stop
|
||||
id="stop6927"
|
||||
stop-color="#204a87"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop6929"
|
||||
stop-color="#204a87"
|
||||
stop-opacity="0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient6971"
|
||||
y2="36.437"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y1="31.431"
|
||||
x2="28.061"
|
||||
x1="28.061">
|
||||
<stop
|
||||
id="stop6967"
|
||||
stop-color="#ddd"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop6969"
|
||||
stop-color="#fdfdfd"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5027"
|
||||
id="linearGradient3248"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.7744,0,0,1.9697,-1892.2,-872.89)"
|
||||
x1="302.86"
|
||||
y1="366.65"
|
||||
x2="302.86"
|
||||
y2="609.51" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5060"
|
||||
id="radialGradient3250"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.7744,0,0,1.9697,-1891.6,-872.89)"
|
||||
cx="605.71"
|
||||
cy="486.65"
|
||||
r="117.14" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5060"
|
||||
id="radialGradient3252"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(-2.7744,0,0,1.9697,112.76,-872.89)"
|
||||
cx="605.71"
|
||||
cy="486.65"
|
||||
r="117.14" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4234"
|
||||
id="linearGradient3254"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(0,5.125)"
|
||||
x1="7.6046"
|
||||
y1="28.481"
|
||||
x2="36.183"
|
||||
y2="40.944" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#radialGradient4250"
|
||||
id="radialGradient3256"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.2862,0.7817,-0.71078,1.1696,-2.3543,0.24814)"
|
||||
cx="15.571"
|
||||
cy="2.9585"
|
||||
r="20.936" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4209"
|
||||
id="linearGradient3258"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(0.79549,3.7992)"
|
||||
x1="7.0625"
|
||||
y1="35.281"
|
||||
x2="24.688"
|
||||
y2="35.281" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4242"
|
||||
id="linearGradient3260"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(0,5.125)"
|
||||
x1="12.277"
|
||||
y1="37.206"
|
||||
x2="12.222"
|
||||
y2="33.759" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4272"
|
||||
id="linearGradient3262"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(0,5.125)"
|
||||
x1="23.688"
|
||||
y1="11.319"
|
||||
x2="23.688"
|
||||
y2="26.357" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4260"
|
||||
id="linearGradient3264"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(0,5.125)"
|
||||
x1="12.378"
|
||||
y1="4.4331"
|
||||
x2="44.096"
|
||||
y2="47.621" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2553"
|
||||
id="linearGradient3266"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="33.431"
|
||||
y1="31.965"
|
||||
x2="21.748"
|
||||
y2="11.781" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#radialGradient8668"
|
||||
id="radialGradient3268"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.1302,0,0,-0.40769251,48.062716,1046.2254)"
|
||||
cx="24.837"
|
||||
cy="36.421"
|
||||
r="15.645" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6971"
|
||||
id="linearGradient3270"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(51.972416,1005.3761)"
|
||||
x1="28.061"
|
||||
y1="31.431"
|
||||
x2="28.061"
|
||||
y2="36.437" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="22.4"
|
||||
inkscape:cx="44.04179"
|
||||
inkscape:cy="22.346221"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1680"
|
||||
inkscape:window-height="997"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="21"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata3832">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Maximillian Merlin</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1007.3622)">
|
||||
<g
|
||||
id="layer1-7"
|
||||
transform="matrix(-0.25019951,0,0,0.28877175,123.44112,917.40972)"
|
||||
style="fill:#696969">
|
||||
<g
|
||||
id="g3257"
|
||||
transform="matrix(1.9175,0,0,1.9175,-607.19,-179.09)"
|
||||
style="fill:#696969">
|
||||
<path
|
||||
id="path3224"
|
||||
d="m 563.06,260.72 -1.25,0.16 -40.15,4.87 -1.66,0.22 0.38,1.62 c 3.78,16.93 -0.59,34.07 -2.88,51.57 l -0.22,1.84 1.81,-0.16 42,-3 1.25,-0.06 0.16,-1.25 c 2.13,-17.86 6.92,-36.12 0.94,-54.62 l -0.38,-1.19 z m -2.18,3.28 c 4.96,16.72 -1.56,33.17 -3.72,50.47 l -35.63,2.78 c 2.25,-16.14 7.03,-29.55 3.78,-46.19 L 560.88,264 z"
|
||||
style="fill:#696969;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3237"
|
||||
d="m 531.39,275.61 23.34,-4.6"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3243"
|
||||
d="m 532.1,280.91 22.27,-2.83"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3247"
|
||||
d="m 531.92,285.86 22.89,-1.95"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3249"
|
||||
d="m 530.15,292.84 24.22,-2.12"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3251"
|
||||
d="m 529.45,297.53 22.98,-0.8"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3253"
|
||||
d="m 528.65,304.24 22.45,-2.56"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3255"
|
||||
d="m 527.5,309.37 22.81,-1.77"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
<rect
|
||||
style="color:#000000;fill:#00ffff;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:0.42527184;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="rect3032"
|
||||
width="44.57473"
|
||||
height="44.57473"
|
||||
x="0.18852967"
|
||||
y="1007.5989" />
|
||||
<rect
|
||||
style="color:#000000;fill:#00ffff;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:0.42527184;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="rect3032-4"
|
||||
width="44.57473"
|
||||
height="44.57473"
|
||||
x="84.498352"
|
||||
y="1050.0748" />
|
||||
<g
|
||||
id="run-arrow"
|
||||
transform="translate(-46.607143,-3.5714285)"
|
||||
inkscape:label="#run-arrow">
|
||||
<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="arrow-rect"
|
||||
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>
|
After Width: | Height: | Size: 16 KiB |
163
interface/resources/icons/stop-script.svg
Normal file
|
@ -0,0 +1,163 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="45"
|
||||
height="45"
|
||||
id="svg3827"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
sodipodi:docname="stop-script.svg">
|
||||
<defs
|
||||
id="defs3829" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.6"
|
||||
inkscape:cx="15.598703"
|
||||
inkscape:cy="22.861751"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1028"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata3832">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Maximillian Merlin</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1007.3622)">
|
||||
<g
|
||||
id="layer1-7"
|
||||
transform="matrix(-0.25019951,0,0,0.28877175,123.44112,917.40972)"
|
||||
style="fill:#696969">
|
||||
<g
|
||||
id="g3257"
|
||||
transform="matrix(1.9175,0,0,1.9175,-607.19,-179.09)"
|
||||
style="fill:#696969">
|
||||
<path
|
||||
id="path3224"
|
||||
d="m 563.06,260.72 -1.25,0.16 -40.15,4.87 -1.66,0.22 0.38,1.62 c 3.78,16.93 -0.59,34.07 -2.88,51.57 l -0.22,1.84 1.81,-0.16 42,-3 1.25,-0.06 0.16,-1.25 c 2.13,-17.86 6.92,-36.12 0.94,-54.62 l -0.38,-1.19 z m -2.18,3.28 c 4.96,16.72 -1.56,33.17 -3.72,50.47 l -35.63,2.78 c 2.25,-16.14 7.03,-29.55 3.78,-46.19 L 560.88,264 z"
|
||||
style="fill:#696969;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3237"
|
||||
d="m 531.39,275.61 23.34,-4.6"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3243"
|
||||
d="m 532.1,280.91 22.27,-2.83"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3247"
|
||||
d="m 531.92,285.86 22.89,-1.95"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3249"
|
||||
d="m 530.15,292.84 24.22,-2.12"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3251"
|
||||
d="m 529.45,297.53 22.98,-0.8"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3253"
|
||||
d="m 528.65,304.24 22.45,-2.56"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3255"
|
||||
d="m 527.5,309.37 22.81,-1.77"
|
||||
style="fill:#696969;stroke:#000000;stroke-width:1px"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
<rect
|
||||
style="color:#000000;fill:#00ffff;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:0.42527184;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="rect3032"
|
||||
width="44.57473"
|
||||
height="44.57473"
|
||||
x="0.18852967"
|
||||
y="1007.5989" />
|
||||
<g
|
||||
id="stop"
|
||||
transform="matrix(0.08804464,0,0,0.0856179,29.060719,1033.5397)">
|
||||
<g
|
||||
id="g3004">
|
||||
<path
|
||||
d="M 0,-150 H 62.132 L 150,-62.132 V 62.128 L 62.132,150 H -62.128 L -149,62.132 V -62.128 L -61.132,-150 H 1 0 z"
|
||||
id="stop_octagon"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#e81108" />
|
||||
</g>
|
||||
<g
|
||||
id="g3007"
|
||||
transform="scale(0.95,0.95)">
|
||||
<path
|
||||
d="M 0,-142.5 H 59.025 L 142.5,-59.025 V 59.025 L 59.025,142.5 H -59.025 L -141.549,59.025 V -59.025 L -58.074,-142.5 H 0.951 0.001 z"
|
||||
id="stop_octagon_1_"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:5" />
|
||||
</g>
|
||||
<g
|
||||
id="g3010"
|
||||
transform="scale(1,1.45)">
|
||||
<g
|
||||
id="g3012"
|
||||
style="fill:#ffffff;enable-background:new">
|
||||
<path
|
||||
id="path3014"
|
||||
d="m -124.2,12.152 8.489,-0.742 c 0.401,3.402 1.337,6.192 2.806,8.373 1.469,2.18 3.75,3.943 6.842,5.288 3.092,1.345 6.571,2.018 10.437,2.018 3.433,0 6.463,-0.51 9.092,-1.53 2.628,-1.021 4.584,-2.42 5.868,-4.198 1.283,-1.778 1.925,-3.718 1.925,-5.821 0,-2.134 -0.619,-3.997 -1.855,-5.59 -1.237,-1.592 -3.278,-2.93 -6.123,-4.013 -1.825,-0.711 -5.861,-1.816 -12.107,-3.317 -6.247,-1.5 -10.623,-2.914 -13.127,-4.244 -3.247,-1.7 -5.667,-3.811 -7.26,-6.332 -1.593,-2.52 -2.389,-5.342 -2.389,-8.465 0,-3.433 0.974,-6.641 2.922,-9.625 1.948,-2.984 4.793,-5.249 8.535,-6.796 3.741,-1.546 7.901,-2.319 12.478,-2.319 5.04,0 9.486,0.812 13.336,2.435 3.85,1.623 6.811,4.012 8.883,7.167 2.071,3.154 3.185,6.726 3.34,10.715 l -8.628,0.65 c -0.464,-4.298 -2.034,-7.545 -4.708,-9.741 -2.675,-2.195 -6.626,-3.293 -11.852,-3.293 -5.443,0 -9.409,0.997 -11.898,2.992 -2.489,1.995 -3.734,4.4 -3.734,7.213 0,2.443 0.881,4.453 2.644,6.03 1.731,1.577 6.254,3.194 13.568,4.847 7.313,1.655 12.331,3.101 15.053,4.337 3.958,1.825 6.88,4.136 8.767,6.935 1.886,2.799 2.83,6.023 2.83,9.672 0,3.618 -1.037,7.028 -3.108,10.228 -2.072,3.201 -5.049,5.69 -8.93,7.468 -3.881,1.778 -8.25,2.667 -13.104,2.667 -6.154,0 -11.311,-0.897 -15.47,-2.691 -4.159,-1.793 -7.422,-4.492 -9.788,-8.094 -2.37,-3.604 -3.62,-7.678 -3.74,-12.224 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3016"
|
||||
d="m -40.473,34 v -59.978 h -22.405 v -8.025 h 53.901 v 8.025 H -31.475 V 34 h -8.998 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3018"
|
||||
d="m -4.198,0.88 c 0,-11.287 3.03,-20.124 9.092,-26.51 6.061,-6.385 13.885,-9.579 23.472,-9.579 6.277,0 11.937,1.5 16.978,4.5 5.04,3 8.883,7.182 11.527,12.547 2.644,5.366 3.966,11.45 3.966,18.253 0,6.896 -1.392,13.066 -4.175,18.508 -2.783,5.443 -6.726,9.564 -11.829,12.362 -5.103,2.799 -10.607,4.198 -16.514,4.198 -6.401,0 -12.123,-1.546 -17.163,-4.639 C 6.115,27.428 2.296,23.207 -0.302,17.856 -2.899,12.508 -4.198,6.848 -4.198,0.88 z m 9.277,0.139 c 0,8.195 2.203,14.651 6.61,19.366 4.407,4.716 9.934,7.074 16.583,7.074 6.772,0 12.346,-2.381 16.722,-7.143 4.376,-4.762 6.564,-11.519 6.564,-20.271 0,-5.535 -0.936,-10.367 -2.807,-14.496 -1.871,-4.129 -4.608,-7.329 -8.21,-9.602 -3.603,-2.273 -7.646,-3.41 -12.13,-3.41 -6.371,0 -11.852,2.188 -16.444,6.564 -4.592,4.377 -6.888,11.682 -6.888,21.918 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3020"
|
||||
d="m 72.433,34 v -68.003 h 25.652 c 4.515,0 7.962,0.217 10.344,0.65 3.34,0.557 6.138,1.616 8.396,3.178 2.257,1.562 4.074,3.75 5.45,6.564 1.376,2.815 2.064,5.907 2.064,9.277 0,5.783 -1.84,10.677 -5.52,14.681 -3.681,4.005 -10.329,6.007 -19.946,6.007 H 81.431 V 34 h -8.999 z m 8.999,-35.672 h 17.581 c 5.813,0 9.942,-1.082 12.385,-3.247 2.442,-2.164 3.665,-5.21 3.665,-9.138 0,-2.845 -0.719,-5.28 -2.157,-7.306 -1.438,-2.025 -3.333,-3.363 -5.682,-4.012 -1.516,-0.401 -4.314,-0.603 -8.396,-0.603 H 81.436 v 24.306 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 8 KiB |
|
@ -206,7 +206,7 @@ ScrollingWindow {
|
|||
print("Error: model cannot be both static mesh and dynamic. This should never happen.");
|
||||
} else if (url) {
|
||||
var name = assetProxyModel.data(treeView.selection.currentIndex);
|
||||
var addPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation)));
|
||||
var addPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(MyAvatar.orientation)));
|
||||
var gravity;
|
||||
if (dynamic) {
|
||||
// Create a vector <0, -10, 0>. { x: 0, y: -10, z: 0 } won't work because Qt is dumb and this is a
|
||||
|
|
|
@ -15,11 +15,12 @@ import Qt.labs.settings 1.0
|
|||
Hifi.AvatarInputs {
|
||||
id: root
|
||||
objectName: "AvatarInputs"
|
||||
width: rootWidth
|
||||
height: controls.height
|
||||
width: mirrorWidth
|
||||
height: controls.height + mirror.height
|
||||
x: 10; y: 5
|
||||
|
||||
readonly property int rootWidth: 265
|
||||
readonly property int mirrorHeight: 215
|
||||
readonly property int mirrorWidth: 265
|
||||
readonly property int iconSize: 24
|
||||
readonly property int iconPadding: 5
|
||||
|
||||
|
@ -38,15 +39,61 @@ Hifi.AvatarInputs {
|
|||
anchors.fill: parent
|
||||
}
|
||||
|
||||
Item {
|
||||
id: mirror
|
||||
width: root.mirrorWidth
|
||||
height: root.mirrorVisible ? root.mirrorHeight : 0
|
||||
visible: root.mirrorVisible
|
||||
anchors.left: parent.left
|
||||
clip: true
|
||||
|
||||
Image {
|
||||
id: closeMirror
|
||||
visible: hover.containsMouse
|
||||
width: root.iconSize
|
||||
height: root.iconSize
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: root.iconPadding
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: root.iconPadding
|
||||
source: "../images/close.svg"
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
root.closeMirror();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: zoomIn
|
||||
visible: hover.containsMouse
|
||||
width: root.iconSize
|
||||
height: root.iconSize
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: root.iconPadding
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: root.iconPadding
|
||||
source: root.mirrorZoomed ? "../images/minus.svg" : "../images/plus.svg"
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
root.toggleZoom();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: controls
|
||||
width: root.rootWidth
|
||||
width: root.mirrorWidth
|
||||
height: 44
|
||||
visible: root.showAudioTools
|
||||
anchors.top: mirror.bottom
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "#00000000"
|
||||
color: root.mirrorVisible ? (root.audioClipping ? "red" : "#696969") : "#00000000"
|
||||
|
||||
Item {
|
||||
id: audioMeter
|
||||
|
|
|
@ -198,7 +198,7 @@ Item {
|
|||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: "Audio Out Mic: " + root.audioOutboundPPS + " pps, " +
|
||||
text: "Audio Out Mic: " + root.audioMicOutboundPPS + " pps, " +
|
||||
"Silent: " + root.audioSilentOutboundPPS + " pps";
|
||||
}
|
||||
StatText {
|
||||
|
@ -266,7 +266,7 @@ Item {
|
|||
text: "GPU Textures: ";
|
||||
}
|
||||
StatText {
|
||||
text: " Pressure State: " + root.gpuTextureMemoryPressureState;
|
||||
text: " Sparse Enabled: " + (0 == root.gpuSparseTextureEnabled ? "false" : "true");
|
||||
}
|
||||
StatText {
|
||||
text: " Count: " + root.gpuTextures;
|
||||
|
@ -278,10 +278,14 @@ Item {
|
|||
text: " Decimated: " + root.decimatedTextureCount;
|
||||
}
|
||||
StatText {
|
||||
text: " Pending Transfer: " + root.texturePendingTransfers + " MB";
|
||||
text: " Sparse Count: " + root.gpuTexturesSparse;
|
||||
visible: 0 != root.gpuSparseTextureEnabled;
|
||||
}
|
||||
StatText {
|
||||
text: " Resource Memory: " + root.gpuTextureMemory + " MB";
|
||||
text: " Virtual Memory: " + root.gpuTextureVirtualMemory + " MB";
|
||||
}
|
||||
StatText {
|
||||
text: " Commited Memory: " + root.gpuTextureMemory + " MB";
|
||||
}
|
||||
StatText {
|
||||
text: " Framebuffer Memory: " + root.gpuTextureFramebufferMemory + " MB";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
QPlainTextEdit {
|
||||
font-family: Inconsolata, Consolas, Courier New, monospace;
|
||||
font-family: Inconsolata, Lucida Console, Andale Mono, Monaco;
|
||||
font-size: 16px;
|
||||
padding-left: 28px;
|
||||
padding-top: 7px;
|
||||
|
@ -11,7 +11,7 @@ QPlainTextEdit {
|
|||
}
|
||||
|
||||
QLineEdit {
|
||||
font-family: Inconsolata, Consolas, Courier New, monospace;
|
||||
font-family: Inconsolata, Lucida Console, Andale Mono, Monaco;
|
||||
padding-left: 7px;
|
||||
background-color: #CCCCCC;
|
||||
border-width: 0;
|
||||
|
|
|
@ -177,8 +177,6 @@
|
|||
#include "FrameTimingsScriptingInterface.h"
|
||||
#include <GPUIdent.h>
|
||||
#include <gl/GLHelpers.h>
|
||||
#include <EntityScriptClient.h>
|
||||
#include <ModelScriptingInterface.h>
|
||||
|
||||
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
||||
// FIXME seems to be broken.
|
||||
|
@ -215,10 +213,18 @@ static const QString FBX_EXTENSION = ".fbx";
|
|||
static const QString OBJ_EXTENSION = ".obj";
|
||||
static const QString AVA_JSON_EXTENSION = ".ava.json";
|
||||
|
||||
static const int MIRROR_VIEW_TOP_PADDING = 5;
|
||||
static const int MIRROR_VIEW_LEFT_PADDING = 10;
|
||||
static const int MIRROR_VIEW_WIDTH = 265;
|
||||
static const int MIRROR_VIEW_HEIGHT = 215;
|
||||
static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f;
|
||||
static const float MIRROR_REARVIEW_DISTANCE = 0.722f;
|
||||
static const float MIRROR_REARVIEW_BODY_DISTANCE = 2.56f;
|
||||
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_WELCOME_PATH = "html/interface-welcome.html";
|
||||
static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html";
|
||||
static const QString INFO_HELP_PATH = "html/help.html";
|
||||
|
||||
|
@ -417,7 +423,6 @@ static const QString STATE_CAMERA_THIRD_PERSON = "CameraThirdPerson";
|
|||
static const QString STATE_CAMERA_ENTITY = "CameraEntity";
|
||||
static const QString STATE_CAMERA_INDEPENDENT = "CameraIndependent";
|
||||
static const QString STATE_SNAP_TURN = "SnapTurn";
|
||||
static const QString STATE_ADVANCED_MOVEMENT_CONTROLS = "AdvancedMovement";
|
||||
static const QString STATE_GROUNDED = "Grounded";
|
||||
static const QString STATE_NAV_FOCUSED = "NavigationFocused";
|
||||
|
||||
|
@ -508,7 +513,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
DependencyManager::set<MessagesClient>();
|
||||
controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR,
|
||||
STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_THIRD_PERSON, STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT,
|
||||
STATE_SNAP_TURN, STATE_ADVANCED_MOVEMENT_CONTROLS, STATE_GROUNDED, STATE_NAV_FOCUSED } });
|
||||
STATE_SNAP_TURN, STATE_GROUNDED, STATE_NAV_FOCUSED } });
|
||||
DependencyManager::set<UserInputMapper>();
|
||||
DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>();
|
||||
DependencyManager::set<InterfaceParentFinder>();
|
||||
|
@ -560,6 +565,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
_entityClipboardRenderer(false, this, this),
|
||||
_entityClipboard(new EntityTree()),
|
||||
_lastQueriedTime(usecTimestampNow()),
|
||||
_mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
|
||||
_previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION),
|
||||
_fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES),
|
||||
_hmdTabletScale("hmdTabletScale", DEFAULT_HMD_TABLET_SCALE_PERCENT),
|
||||
|
@ -740,24 +746,23 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
}
|
||||
});
|
||||
|
||||
auto audioScriptingInterface = DependencyManager::set<AudioScriptingInterface>();
|
||||
auto& audioScriptingInterface = AudioScriptingInterface::getInstance();
|
||||
connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start);
|
||||
connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit);
|
||||
connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater);
|
||||
connect(audioIO.data(), &AudioClient::muteToggled, this, &Application::audioMuteToggled);
|
||||
connect(audioIO.data(), &AudioClient::mutedByMixer, audioScriptingInterface.data(), &AudioScriptingInterface::mutedByMixer);
|
||||
connect(audioIO.data(), &AudioClient::receivedFirstPacket, audioScriptingInterface.data(), &AudioScriptingInterface::receivedFirstPacket);
|
||||
connect(audioIO.data(), &AudioClient::disconnected, audioScriptingInterface.data(), &AudioScriptingInterface::disconnected);
|
||||
connect(audioIO.data(), &AudioClient::mutedByMixer, &audioScriptingInterface, &AudioScriptingInterface::mutedByMixer);
|
||||
connect(audioIO.data(), &AudioClient::receivedFirstPacket, &audioScriptingInterface, &AudioScriptingInterface::receivedFirstPacket);
|
||||
connect(audioIO.data(), &AudioClient::disconnected, &audioScriptingInterface, &AudioScriptingInterface::disconnected);
|
||||
connect(audioIO.data(), &AudioClient::muteEnvironmentRequested, [](glm::vec3 position, float radius) {
|
||||
auto audioClient = DependencyManager::get<AudioClient>();
|
||||
auto audioScriptingInterface = DependencyManager::get<AudioScriptingInterface>();
|
||||
auto myAvatarPosition = DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition();
|
||||
float distance = glm::distance(myAvatarPosition, position);
|
||||
bool shouldMute = !audioClient->isMuted() && (distance < radius);
|
||||
|
||||
if (shouldMute) {
|
||||
audioClient->toggleMute();
|
||||
audioScriptingInterface->environmentMuted();
|
||||
AudioScriptingInterface::getInstance().environmentMuted();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1124,10 +1129,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
_applicationStateDevice->setInputVariant(STATE_SNAP_TURN, []() -> float {
|
||||
return qApp->getMyAvatar()->getSnapTurn() ? 1 : 0;
|
||||
});
|
||||
_applicationStateDevice->setInputVariant(STATE_ADVANCED_MOVEMENT_CONTROLS, []() -> float {
|
||||
return qApp->getMyAvatar()->useAdvancedMovementControls() ? 1 : 0;
|
||||
});
|
||||
|
||||
_applicationStateDevice->setInputVariant(STATE_GROUNDED, []() -> float {
|
||||
return qApp->getMyAvatar()->getCharacterController()->onGround() ? 1 : 0;
|
||||
});
|
||||
|
@ -1182,10 +1183,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
|
||||
// set the local loopback interface for local sounds
|
||||
AudioInjector::setLocalAudioInterface(audioIO.data());
|
||||
audioScriptingInterface->setLocalAudioInterface(audioIO.data());
|
||||
connect(audioIO.data(), &AudioClient::noiseGateOpened, audioScriptingInterface.data(), &AudioScriptingInterface::noiseGateOpened);
|
||||
connect(audioIO.data(), &AudioClient::noiseGateClosed, audioScriptingInterface.data(), &AudioScriptingInterface::noiseGateClosed);
|
||||
connect(audioIO.data(), &AudioClient::inputReceived, audioScriptingInterface.data(), &AudioScriptingInterface::inputReceived);
|
||||
AudioScriptingInterface::getInstance().setLocalAudioInterface(audioIO.data());
|
||||
connect(audioIO.data(), &AudioClient::noiseGateOpened, &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::noiseGateOpened);
|
||||
connect(audioIO.data(), &AudioClient::noiseGateClosed, &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::noiseGateClosed);
|
||||
connect(audioIO.data(), &AudioClient::inputReceived, &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::inputReceived);
|
||||
|
||||
|
||||
this->installEventFilter(this);
|
||||
|
@ -1444,7 +1445,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
scriptEngines->loadScript(testScript, false);
|
||||
} else {
|
||||
// Get sandbox content set version, if available
|
||||
auto acDirPath = PathUtils::getAppDataPath() + "../../" + BuildInfo::MODIFIED_ORGANIZATION + "/assignment-client/";
|
||||
auto acDirPath = PathUtils::getRootDataDirectory() + BuildInfo::MODIFIED_ORGANIZATION + "/assignment-client/";
|
||||
auto contentVersionPath = acDirPath + "content-version.txt";
|
||||
qCDebug(interfaceapp) << "Checking " << contentVersionPath << " for content version";
|
||||
auto contentVersion = 0;
|
||||
|
@ -1950,7 +1951,7 @@ void Application::initializeUi() {
|
|||
// For some reason there is already an "Application" object in the QML context,
|
||||
// though I can't find it. Hence, "ApplicationInterface"
|
||||
rootContext->setContextProperty("ApplicationInterface", this);
|
||||
rootContext->setContextProperty("Audio", DependencyManager::get<AudioScriptingInterface>().data());
|
||||
rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance());
|
||||
rootContext->setContextProperty("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());
|
||||
rootContext->setContextProperty("AudioScope", DependencyManager::get<AudioScope>().data());
|
||||
|
||||
|
@ -2118,6 +2119,21 @@ void Application::paintGL() {
|
|||
batch.resetStages();
|
||||
});
|
||||
|
||||
auto inputs = AvatarInputs::getInstance();
|
||||
if (inputs->mirrorVisible()) {
|
||||
PerformanceTimer perfTimer("Mirror");
|
||||
|
||||
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
|
||||
renderArgs._blitFramebuffer = DependencyManager::get<FramebufferCache>()->getSelfieFramebuffer();
|
||||
|
||||
_mirrorViewRect.moveTo(inputs->x(), inputs->y());
|
||||
|
||||
renderRearViewMirror(&renderArgs, _mirrorViewRect, inputs->mirrorZoomed());
|
||||
|
||||
renderArgs._blitFramebuffer.reset();
|
||||
renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE;
|
||||
}
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("renderOverlay");
|
||||
// NOTE: There is no batch associated with this renderArgs
|
||||
|
@ -2132,7 +2148,7 @@ void Application::paintGL() {
|
|||
PerformanceTimer perfTimer("CameraUpdates");
|
||||
|
||||
auto myAvatar = getMyAvatar();
|
||||
boomOffset = myAvatar->getScale() * myAvatar->getBoomLength() * -IDENTITY_FORWARD;
|
||||
boomOffset = myAvatar->getScale() * myAvatar->getBoomLength() * -IDENTITY_FRONT;
|
||||
|
||||
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN);
|
||||
|
@ -2365,6 +2381,10 @@ void Application::setSettingConstrainToolbarPosition(bool setting) {
|
|||
DependencyManager::get<OffscreenUi>()->setConstrainToolbarToCenterX(setting);
|
||||
}
|
||||
|
||||
void Application::aboutApp() {
|
||||
InfoView::show(INFO_WELCOME_PATH);
|
||||
}
|
||||
|
||||
void Application::showHelp() {
|
||||
static const QString HAND_CONTROLLER_NAME_VIVE = "vive";
|
||||
static const QString HAND_CONTROLLER_NAME_OCULUS_TOUCH = "oculus";
|
||||
|
@ -2746,6 +2766,8 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
case Qt::Key_S:
|
||||
if (isShifted && isMeta && !isOption) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::SuppressShortTimings);
|
||||
} else if (isOption && !isShifted && !isMeta) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::ScriptEditor);
|
||||
} else if (!isOption && !isShifted && isMeta) {
|
||||
takeSnapshot(true);
|
||||
}
|
||||
|
@ -2864,49 +2886,51 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
break;
|
||||
#endif
|
||||
|
||||
case Qt::Key_H: {
|
||||
// whenever switching to/from full screen mirror from the keyboard, remember
|
||||
// the state you were in before full screen mirror, and return to that.
|
||||
auto previousMode = _myCamera.getMode();
|
||||
if (previousMode != CAMERA_MODE_MIRROR) {
|
||||
switch (previousMode) {
|
||||
case CAMERA_MODE_FIRST_PERSON:
|
||||
_returnFromFullScreenMirrorTo = MenuOption::FirstPerson;
|
||||
break;
|
||||
case CAMERA_MODE_THIRD_PERSON:
|
||||
_returnFromFullScreenMirrorTo = MenuOption::ThirdPerson;
|
||||
break;
|
||||
case Qt::Key_H:
|
||||
if (isShifted) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::MiniMirror);
|
||||
} else {
|
||||
// whenever switching to/from full screen mirror from the keyboard, remember
|
||||
// the state you were in before full screen mirror, and return to that.
|
||||
auto previousMode = _myCamera.getMode();
|
||||
if (previousMode != CAMERA_MODE_MIRROR) {
|
||||
switch (previousMode) {
|
||||
case CAMERA_MODE_FIRST_PERSON:
|
||||
_returnFromFullScreenMirrorTo = MenuOption::FirstPerson;
|
||||
break;
|
||||
case CAMERA_MODE_THIRD_PERSON:
|
||||
_returnFromFullScreenMirrorTo = MenuOption::ThirdPerson;
|
||||
break;
|
||||
|
||||
// FIXME - it's not clear that these modes make sense to return to...
|
||||
case CAMERA_MODE_INDEPENDENT:
|
||||
_returnFromFullScreenMirrorTo = MenuOption::IndependentMode;
|
||||
break;
|
||||
case CAMERA_MODE_ENTITY:
|
||||
_returnFromFullScreenMirrorTo = MenuOption::CameraEntityMode;
|
||||
break;
|
||||
// FIXME - it's not clear that these modes make sense to return to...
|
||||
case CAMERA_MODE_INDEPENDENT:
|
||||
_returnFromFullScreenMirrorTo = MenuOption::IndependentMode;
|
||||
break;
|
||||
case CAMERA_MODE_ENTITY:
|
||||
_returnFromFullScreenMirrorTo = MenuOption::CameraEntityMode;
|
||||
break;
|
||||
|
||||
default:
|
||||
_returnFromFullScreenMirrorTo = MenuOption::ThirdPerson;
|
||||
break;
|
||||
default:
|
||||
_returnFromFullScreenMirrorTo = MenuOption::ThirdPerson;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isMirrorChecked = Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, !isMirrorChecked);
|
||||
if (isMirrorChecked) {
|
||||
bool isMirrorChecked = Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, !isMirrorChecked);
|
||||
if (isMirrorChecked) {
|
||||
|
||||
// if we got here without coming in from a non-Full Screen mirror case, then our
|
||||
// _returnFromFullScreenMirrorTo is unknown. In that case we'll go to the old
|
||||
// behavior of returning to ThirdPerson
|
||||
if (_returnFromFullScreenMirrorTo.isEmpty()) {
|
||||
_returnFromFullScreenMirrorTo = MenuOption::ThirdPerson;
|
||||
// if we got here without coming in from a non-Full Screen mirror case, then our
|
||||
// _returnFromFullScreenMirrorTo is unknown. In that case we'll go to the old
|
||||
// behavior of returning to ThirdPerson
|
||||
if (_returnFromFullScreenMirrorTo.isEmpty()) {
|
||||
_returnFromFullScreenMirrorTo = MenuOption::ThirdPerson;
|
||||
}
|
||||
Menu::getInstance()->setIsOptionChecked(_returnFromFullScreenMirrorTo, true);
|
||||
}
|
||||
Menu::getInstance()->setIsOptionChecked(_returnFromFullScreenMirrorTo, true);
|
||||
cameraMenuChanged();
|
||||
}
|
||||
cameraMenuChanged();
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_P: {
|
||||
if (!(isShifted || isMeta || isOption)) {
|
||||
bool isFirstPersonChecked = Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson);
|
||||
|
@ -3821,6 +3845,8 @@ void Application::init() {
|
|||
DependencyManager::get<AvatarManager>()->init();
|
||||
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
|
||||
|
||||
_mirrorCamera.setMode(CAMERA_MODE_MIRROR);
|
||||
|
||||
_timerStart.start();
|
||||
_lastTimeUpdated.start();
|
||||
|
||||
|
@ -3955,7 +3981,7 @@ void Application::updateMyAvatarLookAtPosition() {
|
|||
auto lookingAtHead = static_pointer_cast<Avatar>(lookingAt)->getHead();
|
||||
|
||||
const float MAXIMUM_FACE_ANGLE = 65.0f * RADIANS_PER_DEGREE;
|
||||
glm::vec3 lookingAtFaceOrientation = lookingAtHead->getFinalOrientationInWorldFrame() * IDENTITY_FORWARD;
|
||||
glm::vec3 lookingAtFaceOrientation = lookingAtHead->getFinalOrientationInWorldFrame() * IDENTITY_FRONT;
|
||||
glm::vec3 fromLookingAtToMe = glm::normalize(myAvatar->getHead()->getEyePosition()
|
||||
- lookingAtHead->getEyePosition());
|
||||
float faceAngle = glm::angle(lookingAtFaceOrientation, fromLookingAtToMe);
|
||||
|
@ -4357,16 +4383,16 @@ void Application::update(float deltaTime) {
|
|||
myAvatar->clearDriveKeys();
|
||||
if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) {
|
||||
if (!_controllerScriptingInterface->areActionsCaptured()) {
|
||||
myAvatar->setDriveKey(MyAvatar::TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z));
|
||||
myAvatar->setDriveKey(MyAvatar::TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y));
|
||||
myAvatar->setDriveKey(MyAvatar::TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X));
|
||||
myAvatar->setDriveKeys(TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z));
|
||||
myAvatar->setDriveKeys(TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y));
|
||||
myAvatar->setDriveKeys(TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X));
|
||||
if (deltaTime > FLT_EPSILON) {
|
||||
myAvatar->setDriveKey(MyAvatar::PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH));
|
||||
myAvatar->setDriveKey(MyAvatar::YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW));
|
||||
myAvatar->setDriveKey(MyAvatar::STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW));
|
||||
myAvatar->setDriveKeys(PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH));
|
||||
myAvatar->setDriveKeys(YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW));
|
||||
myAvatar->setDriveKeys(STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW));
|
||||
}
|
||||
}
|
||||
myAvatar->setDriveKey(MyAvatar::ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z));
|
||||
myAvatar->setDriveKeys(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z));
|
||||
}
|
||||
|
||||
controller::Pose leftHandPose = userInputMapper->getPoseState(controller::Action::LEFT_HAND);
|
||||
|
@ -4437,12 +4463,9 @@ void Application::update(float deltaTime) {
|
|||
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
PerformanceTimer perfTimer("handleOutgoingChanges");
|
||||
const VectorOfMotionStates& deactivations = _physicsEngine->getDeactivatedMotionStates();
|
||||
_entitySimulation->handleDeactivatedMotionStates(deactivations);
|
||||
|
||||
const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates();
|
||||
_entitySimulation->handleChangedMotionStates(outgoingChanges);
|
||||
avatarManager->handleChangedMotionStates(outgoingChanges);
|
||||
const VectorOfMotionStates& outgoingChanges = _physicsEngine->getOutgoingChanges();
|
||||
_entitySimulation->handleOutgoingChanges(outgoingChanges);
|
||||
avatarManager->handleOutgoingChanges(outgoingChanges);
|
||||
});
|
||||
|
||||
if (!_aboutToQuit) {
|
||||
|
@ -5099,6 +5122,58 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
activeRenderingThread = nullptr;
|
||||
}
|
||||
|
||||
void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool isZoomed) {
|
||||
auto originalViewport = renderArgs->_viewport;
|
||||
// Grab current viewport to reset it at the end
|
||||
|
||||
float aspect = (float)region.width() / region.height();
|
||||
float fov = MIRROR_FIELD_OF_VIEW;
|
||||
|
||||
auto myAvatar = getMyAvatar();
|
||||
|
||||
// bool eyeRelativeCamera = false;
|
||||
if (!isZoomed) {
|
||||
_mirrorCamera.setPosition(myAvatar->getChestPosition() +
|
||||
myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * myAvatar->getScale());
|
||||
|
||||
} else { // HEAD zoom level
|
||||
// FIXME note that the positioning of the camera relative to the avatar can suffer limited
|
||||
// precision as the user's position moves further away from the origin. Thus at
|
||||
// /1e7,1e7,1e7 (well outside the buildable volume) the mirror camera veers and sways
|
||||
// wildly as you rotate your avatar because the floating point values are becoming
|
||||
// larger, squeezing out the available digits of precision you have available at the
|
||||
// human scale for camera positioning.
|
||||
|
||||
// Previously there was a hack to correct this using the mechanism of repositioning
|
||||
// the avatar at the origin of the world for the purposes of rendering the mirror,
|
||||
// but it resulted in failing to render the avatar's head model in the mirror view
|
||||
// when in first person mode. Presumably this was because of some missed culling logic
|
||||
// that was not accounted for in the hack.
|
||||
|
||||
// This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further
|
||||
// investigated in order to adapt the technique while fixing the head rendering issue,
|
||||
// but the complexity of the hack suggests that a better approach
|
||||
_mirrorCamera.setPosition(myAvatar->getDefaultEyePosition() +
|
||||
myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * myAvatar->getScale());
|
||||
}
|
||||
_mirrorCamera.setProjection(glm::perspective(glm::radians(fov), aspect, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP));
|
||||
_mirrorCamera.setOrientation(myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f)));
|
||||
|
||||
|
||||
// set the bounds of rear mirror view
|
||||
// the region is in device independent coordinates; must convert to device
|
||||
float ratio = (float)QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale();
|
||||
int width = region.width() * ratio;
|
||||
int height = region.height() * ratio;
|
||||
gpu::Vec4i viewport = gpu::Vec4i(0, 0, width, height);
|
||||
renderArgs->_viewport = viewport;
|
||||
|
||||
// render rear mirror view
|
||||
displaySide(renderArgs, _mirrorCamera, true);
|
||||
|
||||
renderArgs->_viewport = originalViewport;
|
||||
}
|
||||
|
||||
void Application::resetSensors(bool andReload) {
|
||||
DependencyManager::get<Faceshift>()->reset();
|
||||
DependencyManager::get<DdeFaceTracker>()->reset();
|
||||
|
@ -5428,7 +5503,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
scriptEngine->registerGlobalObject("Rates", new RatesScriptingInterface(this));
|
||||
|
||||
// hook our avatar and avatar hash map object into this script engine
|
||||
getMyAvatar()->registerMetaTypes(scriptEngine);
|
||||
scriptEngine->registerGlobalObject("MyAvatar", getMyAvatar().get());
|
||||
qScriptRegisterMetaType(scriptEngine, audioListenModeToScriptValue, audioListenModeFromScriptValue);
|
||||
|
||||
scriptEngine->registerGlobalObject("AvatarList", DependencyManager::get<AvatarManager>().data());
|
||||
|
||||
|
|
|
@ -72,8 +72,6 @@
|
|||
|
||||
#include <procedural/ProceduralSkybox.h>
|
||||
#include <model/Skybox.h>
|
||||
#include <ModelScriptingInterface.h>
|
||||
|
||||
|
||||
class OffscreenGLCanvas;
|
||||
class GLCanvas;
|
||||
|
@ -278,6 +276,8 @@ public:
|
|||
|
||||
virtual void pushPostUpdateLambda(void* key, std::function<void()> func) override;
|
||||
|
||||
const QRect& getMirrorViewRect() const { return _mirrorViewRect; }
|
||||
|
||||
void updateMyAvatarLookAtPosition();
|
||||
|
||||
float getAvatarSimrate() const { return _avatarSimCounter.rate(); }
|
||||
|
@ -368,6 +368,7 @@ public slots:
|
|||
void calibrateEyeTracker5Points();
|
||||
#endif
|
||||
|
||||
void aboutApp();
|
||||
static void showHelp();
|
||||
|
||||
void cycleCamera();
|
||||
|
@ -556,6 +557,8 @@ private:
|
|||
int _avatarSimsPerSecondReport {0};
|
||||
quint64 _lastAvatarSimsPerSecondUpdate {0};
|
||||
Camera _myCamera; // My view onto the world
|
||||
Camera _mirrorCamera; // Camera for mirror view
|
||||
QRect _mirrorViewRect;
|
||||
|
||||
Setting::Handle<QString> _previousScriptLocation;
|
||||
Setting::Handle<float> _fieldOfView;
|
||||
|
|
|
@ -74,6 +74,9 @@ Menu::Menu() {
|
|||
// File > Help
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::Help, 0, qApp, SLOT(showHelp()));
|
||||
|
||||
// File > About
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::AboutApp, 0, qApp, SLOT(aboutApp()), QAction::AboutRole);
|
||||
|
||||
// File > Quit
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::Quit, Qt::CTRL | Qt::Key_Q, qApp, SLOT(quit()), QAction::QuitRole);
|
||||
|
||||
|
@ -117,6 +120,11 @@ Menu::Menu() {
|
|||
scriptEngines.data(), SLOT(reloadAllScripts()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
// Edit > Scripts Editor... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::ScriptEditor, Qt::ALT | Qt::Key_S,
|
||||
dialogsManager.data(), SLOT(showScriptEditor()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
// Edit > Console... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::Console, Qt::CTRL | Qt::ALT | Qt::Key_J,
|
||||
DependencyManager::get<StandAloneJSConsole>().data(),
|
||||
|
@ -241,6 +249,9 @@ Menu::Menu() {
|
|||
|
||||
viewMenu->addSeparator();
|
||||
|
||||
// View > Mini Mirror
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::MiniMirror, 0, false);
|
||||
|
||||
// View > Center Player In View
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::CenterPlayerInView,
|
||||
0, true, qApp, SLOT(rotationModeChanged()),
|
||||
|
@ -406,9 +417,6 @@ Menu::Menu() {
|
|||
}
|
||||
|
||||
// Developer > Assets >>>
|
||||
// Menu item is not currently needed but code should be kept in case it proves useful again at some stage.
|
||||
//#define WANT_ASSET_MIGRATION
|
||||
#ifdef WANT_ASSET_MIGRATION
|
||||
MenuWrapper* assetDeveloperMenu = developerMenu->addMenu("Assets");
|
||||
auto& atpMigrator = ATPAssetMigrator::getInstance();
|
||||
atpMigrator.setDialogParent(this);
|
||||
|
@ -416,7 +424,6 @@ Menu::Menu() {
|
|||
addActionToQMenuAndActionHash(assetDeveloperMenu, MenuOption::AssetMigration,
|
||||
0, &atpMigrator,
|
||||
SLOT(loadEntityServerFile()));
|
||||
#endif
|
||||
|
||||
// Developer > Avatar >>>
|
||||
MenuWrapper* avatarDebugMenu = developerMenu->addMenu("Avatar");
|
||||
|
@ -547,14 +554,16 @@ Menu::Menu() {
|
|||
"NetworkingPreferencesDialog");
|
||||
});
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()));
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::ClearDiskCache, 0,
|
||||
DependencyManager::get<AssetClient>().data(), SLOT(clearCache()));
|
||||
addCheckableActionToQMenuAndActionHash(networkMenu,
|
||||
MenuOption::DisableActivityLogger,
|
||||
0,
|
||||
false,
|
||||
&UserActivityLogger::getInstance(),
|
||||
SLOT(disable(bool)));
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::CachesSize, 0,
|
||||
dialogsManager.data(), SLOT(cachesSizeDialog()));
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::DiskCacheEditor, 0,
|
||||
dialogsManager.data(), SLOT(toggleDiskCacheEditor()));
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::ShowDSConnectTable, 0,
|
||||
dialogsManager.data(), SLOT(showDomainConnectionDialog()));
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ public:
|
|||
};
|
||||
|
||||
namespace MenuOption {
|
||||
const QString AboutApp = "About Interface";
|
||||
const QString AddRemoveFriends = "Add/Remove Friends...";
|
||||
const QString AddressBar = "Show Address Bar";
|
||||
const QString Animations = "Animations...";
|
||||
|
@ -51,11 +52,11 @@ namespace MenuOption {
|
|||
const QString BinaryEyelidControl = "Binary Eyelid Control";
|
||||
const QString BookmarkLocation = "Bookmark Location";
|
||||
const QString Bookmarks = "Bookmarks";
|
||||
const QString CachesSize = "RAM Caches Size";
|
||||
const QString CalibrateCamera = "Calibrate Camera";
|
||||
const QString CameraEntityMode = "Entity Mode";
|
||||
const QString CenterPlayerInView = "Center Player In View";
|
||||
const QString Chat = "Chat...";
|
||||
const QString ClearDiskCache = "Clear Disk Cache";
|
||||
const QString Collisions = "Collisions";
|
||||
const QString Connexion = "Activate 3D Connexion Devices";
|
||||
const QString Console = "Console...";
|
||||
|
@ -82,6 +83,7 @@ namespace MenuOption {
|
|||
const QString DisableActivityLogger = "Disable Activity Logger";
|
||||
const QString DisableEyelidAdjustment = "Disable Eyelid Adjustment";
|
||||
const QString DisableLightEntities = "Disable Light Entities";
|
||||
const QString DiskCacheEditor = "Disk Cache Editor";
|
||||
const QString DisplayCrashOptions = "Display Crash Options";
|
||||
const QString DisplayHandTargets = "Show Hand Targets";
|
||||
const QString DisplayModelBounds = "Display Model Bounds";
|
||||
|
@ -122,6 +124,7 @@ namespace MenuOption {
|
|||
const QString LogExtraTimings = "Log Extra Timing Details";
|
||||
const QString LowVelocityFilter = "Low Velocity Filter";
|
||||
const QString MeshVisible = "Draw Mesh";
|
||||
const QString MiniMirror = "Mini Mirror";
|
||||
const QString MuteAudio = "Mute Microphone";
|
||||
const QString MuteEnvironment = "Mute Environment";
|
||||
const QString MuteFaceTracking = "Mute Face Tracking";
|
||||
|
@ -166,6 +169,7 @@ namespace MenuOption {
|
|||
const QString RunningScripts = "Running Scripts...";
|
||||
const QString RunClientScriptTests = "Run Client Script Tests";
|
||||
const QString RunTimingTests = "Run Timing Tests";
|
||||
const QString ScriptEditor = "Script Editor...";
|
||||
const QString ScriptedMotorControl = "Enable Scripted Motor Control";
|
||||
const QString SendWrongDSConnectVersion = "Send wrong DS connect version";
|
||||
const QString SendWrongProtocolVersion = "Send wrong protocol version";
|
||||
|
|
|
@ -236,6 +236,7 @@ protected:
|
|||
|
||||
glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
|
||||
glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; }
|
||||
glm::vec3 getBodyFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
|
||||
glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const;
|
||||
void measureMotionDerivatives(float deltaTime);
|
||||
|
||||
|
|
|
@ -424,7 +424,7 @@ void AvatarManager::getObjectsToChange(VectorOfMotionStates& result) {
|
|||
}
|
||||
}
|
||||
|
||||
void AvatarManager::handleChangedMotionStates(const VectorOfMotionStates& motionStates) {
|
||||
void AvatarManager::handleOutgoingChanges(const VectorOfMotionStates& motionStates) {
|
||||
// TODO: extract the MyAvatar results once we use a MotionState for it.
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ public:
|
|||
void getObjectsToRemoveFromPhysics(VectorOfMotionStates& motionStates);
|
||||
void getObjectsToAddToPhysics(VectorOfMotionStates& motionStates);
|
||||
void getObjectsToChange(VectorOfMotionStates& motionStates);
|
||||
void handleChangedMotionStates(const VectorOfMotionStates& motionStates);
|
||||
void handleOutgoingChanges(const VectorOfMotionStates& motionStates);
|
||||
void handleCollisionEvents(const CollisionEvents& collisionEvents);
|
||||
|
||||
Q_INVOKABLE float getAvatarDataRate(const QUuid& sessionID, const QString& rateName = QString("")) const;
|
||||
|
|
|
@ -20,28 +20,55 @@ using namespace render;
|
|||
CauterizedMeshPartPayload::CauterizedMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform)
|
||||
: ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {}
|
||||
|
||||
void CauterizedMeshPartPayload::updateTransformForCauterizedMesh(
|
||||
const Transform& renderTransform,
|
||||
const gpu::BufferPointer& buffer) {
|
||||
_cauterizedTransform = renderTransform;
|
||||
_cauterizedClusterBuffer = buffer;
|
||||
void CauterizedMeshPartPayload::updateTransformForSkinnedCauterizedMesh(const Transform& transform,
|
||||
const QVector<glm::mat4>& clusterMatrices,
|
||||
const QVector<glm::mat4>& cauterizedClusterMatrices) {
|
||||
_transform = transform;
|
||||
_cauterizedTransform = transform;
|
||||
|
||||
if (clusterMatrices.size() > 0) {
|
||||
_worldBound = AABox();
|
||||
for (auto& clusterMatrix : clusterMatrices) {
|
||||
AABox clusterBound = _localBound;
|
||||
clusterBound.transform(clusterMatrix);
|
||||
_worldBound += clusterBound;
|
||||
}
|
||||
|
||||
_worldBound.transform(transform);
|
||||
if (clusterMatrices.size() == 1) {
|
||||
_transform = _transform.worldTransform(Transform(clusterMatrices[0]));
|
||||
if (cauterizedClusterMatrices.size() != 0) {
|
||||
_cauterizedTransform = _cauterizedTransform.worldTransform(Transform(cauterizedClusterMatrices[0]));
|
||||
} else {
|
||||
_cauterizedTransform = _transform;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_worldBound = _localBound;
|
||||
_worldBound.transform(_drawTransform);
|
||||
}
|
||||
}
|
||||
|
||||
void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const {
|
||||
// Still relying on the raw data from the model
|
||||
const Model::MeshState& state = _model->getMeshState(_meshIndex);
|
||||
SkeletonModel* skeleton = static_cast<SkeletonModel*>(_model);
|
||||
bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE) && skeleton->getEnableCauterization();
|
||||
|
||||
if (useCauterizedMesh) {
|
||||
if (_cauterizedClusterBuffer) {
|
||||
batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, _cauterizedClusterBuffer);
|
||||
}
|
||||
batch.setModelTransform(_cauterizedTransform);
|
||||
} else {
|
||||
if (_clusterBuffer) {
|
||||
batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, _clusterBuffer);
|
||||
if (state.clusterBuffer) {
|
||||
if (useCauterizedMesh) {
|
||||
const Model::MeshState& cState = skeleton->getCauterizeMeshState(_meshIndex);
|
||||
batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, cState.clusterBuffer);
|
||||
} else {
|
||||
batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.clusterBuffer);
|
||||
}
|
||||
batch.setModelTransform(_transform);
|
||||
} else {
|
||||
if (useCauterizedMesh) {
|
||||
batch.setModelTransform(_cauterizedTransform);
|
||||
} else {
|
||||
batch.setModelTransform(_transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,13 +17,12 @@
|
|||
class CauterizedMeshPartPayload : public ModelMeshPartPayload {
|
||||
public:
|
||||
CauterizedMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform);
|
||||
|
||||
void updateTransformForCauterizedMesh(const Transform& renderTransform, const gpu::BufferPointer& buffer);
|
||||
void updateTransformForSkinnedCauterizedMesh(const Transform& transform,
|
||||
const QVector<glm::mat4>& clusterMatrices,
|
||||
const QVector<glm::mat4>& cauterizedClusterMatrices);
|
||||
|
||||
void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override;
|
||||
|
||||
private:
|
||||
gpu::BufferPointer _cauterizedClusterBuffer;
|
||||
Transform _cauterizedTransform;
|
||||
};
|
||||
|
||||
|
|
|
@ -26,8 +26,8 @@ CauterizedModel::~CauterizedModel() {
|
|||
}
|
||||
|
||||
void CauterizedModel::deleteGeometry() {
|
||||
Model::deleteGeometry();
|
||||
_cauterizeMeshStates.clear();
|
||||
Model::deleteGeometry();
|
||||
_cauterizeMeshStates.clear();
|
||||
}
|
||||
|
||||
bool CauterizedModel::updateGeometry() {
|
||||
|
@ -41,7 +41,7 @@ bool CauterizedModel::updateGeometry() {
|
|||
_cauterizeMeshStates.append(state);
|
||||
}
|
||||
}
|
||||
return needsFullUpdate;
|
||||
return needsFullUpdate;
|
||||
}
|
||||
|
||||
void CauterizedModel::createVisibleRenderItemSet() {
|
||||
|
@ -56,9 +56,9 @@ void CauterizedModel::createVisibleRenderItemSet() {
|
|||
}
|
||||
|
||||
// We should not have any existing renderItems if we enter this section of code
|
||||
Q_ASSERT(_modelMeshRenderItems.isEmpty());
|
||||
Q_ASSERT(_modelMeshRenderItemsSet.isEmpty());
|
||||
|
||||
_modelMeshRenderItems.clear();
|
||||
_modelMeshRenderItemsSet.clear();
|
||||
|
||||
Transform transform;
|
||||
transform.setTranslation(_translation);
|
||||
|
@ -81,18 +81,18 @@ void CauterizedModel::createVisibleRenderItemSet() {
|
|||
int numParts = (int)mesh->getNumParts();
|
||||
for (int partIndex = 0; partIndex < numParts; partIndex++) {
|
||||
auto ptr = std::make_shared<CauterizedMeshPartPayload>(this, i, partIndex, shapeID, transform, offset);
|
||||
_modelMeshRenderItems << std::static_pointer_cast<ModelMeshPartPayload>(ptr);
|
||||
_modelMeshRenderItemsSet << std::static_pointer_cast<ModelMeshPartPayload>(ptr);
|
||||
shapeID++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Model::createVisibleRenderItemSet();
|
||||
Model::createVisibleRenderItemSet();
|
||||
}
|
||||
}
|
||||
|
||||
void CauterizedModel::createCollisionRenderItemSet() {
|
||||
// Temporary HACK: use base class method for now
|
||||
Model::createCollisionRenderItemSet();
|
||||
Model::createCollisionRenderItemSet();
|
||||
}
|
||||
|
||||
void CauterizedModel::updateClusterMatrices() {
|
||||
|
@ -122,8 +122,8 @@ void CauterizedModel::updateClusterMatrices() {
|
|||
state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4),
|
||||
(const gpu::Byte*) state.clusterMatrices.constData());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty.
|
||||
if (!_cauterizeBoneSet.empty()) {
|
||||
|
@ -191,9 +191,6 @@ void CauterizedModel::updateRenderItems() {
|
|||
return;
|
||||
}
|
||||
|
||||
// lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box.
|
||||
self->updateClusterMatrices();
|
||||
|
||||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
|
||||
Transform modelTransform;
|
||||
|
@ -212,22 +209,15 @@ void CauterizedModel::updateRenderItems() {
|
|||
if (data._model && data._model->isLoaded()) {
|
||||
// Ensure the model geometry was not reset between frames
|
||||
if (deleteGeometryCounter == data._model->getGeometryCounter()) {
|
||||
// this stuff identical to what happens in regular Model
|
||||
const Model::MeshState& state = data._model->getMeshState(data._meshIndex);
|
||||
Transform renderTransform = modelTransform;
|
||||
if (state.clusterMatrices.size() == 1) {
|
||||
renderTransform = modelTransform.worldTransform(Transform(state.clusterMatrices[0]));
|
||||
}
|
||||
data.updateTransformForSkinnedMesh(renderTransform, modelTransform, state.clusterBuffer);
|
||||
// lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box.
|
||||
data._model->updateClusterMatrices();
|
||||
|
||||
// this stuff for cauterized mesh
|
||||
// update the model transform and bounding box for this render item.
|
||||
const Model::MeshState& state = data._model->getMeshState(data._meshIndex);
|
||||
CauterizedModel* cModel = static_cast<CauterizedModel*>(data._model);
|
||||
const Model::MeshState& cState = cModel->getCauterizeMeshState(data._meshIndex);
|
||||
renderTransform = modelTransform;
|
||||
if (cState.clusterMatrices.size() == 1) {
|
||||
renderTransform = modelTransform.worldTransform(Transform(cState.clusterMatrices[0]));
|
||||
}
|
||||
data.updateTransformForCauterizedMesh(renderTransform, cState.clusterBuffer);
|
||||
assert(data._meshIndex < cModel->_cauterizeMeshStates.size());
|
||||
const Model::MeshState& cState = cModel->_cauterizeMeshStates.at(data._meshIndex);
|
||||
data.updateTransformForSkinnedCauterizedMesh(modelTransform, state.clusterMatrices, cState.clusterMatrices);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -268,7 +268,7 @@ void Head::applyEyelidOffset(glm::quat headOrientation) {
|
|||
return;
|
||||
}
|
||||
|
||||
glm::quat eyeRotation = rotationBetween(headOrientation * IDENTITY_FORWARD, getLookAtPosition() - _eyePosition);
|
||||
glm::quat eyeRotation = rotationBetween(headOrientation * IDENTITY_FRONT, getLookAtPosition() - _eyePosition);
|
||||
eyeRotation = eyeRotation * glm::angleAxis(safeEulerAngles(headOrientation).y, IDENTITY_UP); // Rotation w.r.t. head
|
||||
float eyePitch = safeEulerAngles(eyeRotation).x;
|
||||
|
||||
|
@ -375,7 +375,7 @@ glm::quat Head::getCameraOrientation() const {
|
|||
glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const {
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 lookAtDelta = _lookAtPosition - eyePosition;
|
||||
return rotationBetween(orientation * IDENTITY_FORWARD, lookAtDelta + glm::length(lookAtDelta) * _saccade) * orientation;
|
||||
return rotationBetween(orientation * IDENTITY_FRONT, lookAtDelta + glm::length(lookAtDelta) * _saccade) * orientation;
|
||||
}
|
||||
|
||||
void Head::setFinalPitch(float finalPitch) {
|
||||
|
|
|
@ -58,14 +58,14 @@ public:
|
|||
const glm::vec3& getSaccade() const { return _saccade; }
|
||||
glm::vec3 getRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
|
||||
glm::vec3 getUpDirection() const { return getOrientation() * IDENTITY_UP; }
|
||||
glm::vec3 getForwardDirection() const { return getOrientation() * IDENTITY_FORWARD; }
|
||||
glm::vec3 getFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
|
||||
|
||||
glm::quat getEyeRotation(const glm::vec3& eyePosition) const;
|
||||
|
||||
const glm::vec3& getRightEyePosition() const { return _rightEyePosition; }
|
||||
const glm::vec3& getLeftEyePosition() const { return _leftEyePosition; }
|
||||
glm::vec3 getRightEarPosition() const { return _rightEyePosition + (getRightDirection() * EYE_EAR_GAP) + (getForwardDirection() * -EYE_EAR_GAP); }
|
||||
glm::vec3 getLeftEarPosition() const { return _leftEyePosition + (getRightDirection() * -EYE_EAR_GAP) + (getForwardDirection() * -EYE_EAR_GAP); }
|
||||
glm::vec3 getRightEarPosition() const { return _rightEyePosition + (getRightDirection() * EYE_EAR_GAP) + (getFrontDirection() * -EYE_EAR_GAP); }
|
||||
glm::vec3 getLeftEarPosition() const { return _leftEyePosition + (getRightDirection() * -EYE_EAR_GAP) + (getFrontDirection() * -EYE_EAR_GAP); }
|
||||
glm::vec3 getMouthPosition() const { return _eyePosition - getUpDirection() * glm::length(_rightEyePosition - _leftEyePosition); }
|
||||
|
||||
bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected)
|
||||
|
|
|
@ -104,7 +104,6 @@ MyAvatar::MyAvatar(RigPointer rig) :
|
|||
_eyeContactTarget(LEFT_EYE),
|
||||
_realWorldFieldOfView("realWorldFieldOfView",
|
||||
DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES),
|
||||
_useAdvancedMovementControls("advancedMovementForHandControllersIsChecked", false),
|
||||
_hmdSensorMatrix(),
|
||||
_hmdSensorOrientation(),
|
||||
_hmdSensorPosition(),
|
||||
|
@ -120,7 +119,9 @@ MyAvatar::MyAvatar(RigPointer rig) :
|
|||
using namespace recording;
|
||||
_skeletonModel->flagAsCauterized();
|
||||
|
||||
clearDriveKeys();
|
||||
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
|
||||
_driveKeys[i] = 0.0f;
|
||||
}
|
||||
|
||||
// Necessary to select the correct slot
|
||||
using SlotType = void(MyAvatar::*)(const glm::vec3&, bool, const glm::quat&, bool);
|
||||
|
@ -153,12 +154,9 @@ MyAvatar::MyAvatar(RigPointer rig) :
|
|||
if (recordingInterface->getPlayFromCurrentLocation()) {
|
||||
setRecordingBasis();
|
||||
}
|
||||
_wasCharacterControllerEnabled = _characterController.isEnabled();
|
||||
_characterController.setEnabled(false);
|
||||
} else {
|
||||
clearRecordingBasis();
|
||||
useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName);
|
||||
_characterController.setEnabled(_wasCharacterControllerEnabled);
|
||||
}
|
||||
|
||||
auto audioIO = DependencyManager::get<AudioClient>();
|
||||
|
@ -229,21 +227,6 @@ MyAvatar::~MyAvatar() {
|
|||
_lookAtTargetAvatar.reset();
|
||||
}
|
||||
|
||||
void MyAvatar::registerMetaTypes(QScriptEngine* engine) {
|
||||
QScriptValue value = engine->newQObject(this, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects);
|
||||
engine->globalObject().setProperty("MyAvatar", value);
|
||||
|
||||
QScriptValue driveKeys = engine->newObject();
|
||||
auto metaEnum = QMetaEnum::fromType<DriveKeys>();
|
||||
for (int i = 0; i < MAX_DRIVE_KEYS; ++i) {
|
||||
driveKeys.setProperty(metaEnum.key(i), metaEnum.value(i));
|
||||
}
|
||||
engine->globalObject().setProperty("DriveKeys", driveKeys);
|
||||
|
||||
qScriptRegisterMetaType(engine, audioListenModeToScriptValue, audioListenModeFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, driveKeysToScriptValue, driveKeysFromScriptValue);
|
||||
}
|
||||
|
||||
void MyAvatar::setOrientationVar(const QVariant& newOrientationVar) {
|
||||
Avatar::setOrientation(quatFromVariant(newOrientationVar));
|
||||
}
|
||||
|
@ -476,7 +459,7 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
// When there are no step values, we zero out the last step pulse.
|
||||
// This allows a user to do faster snapping by tapping a control
|
||||
for (int i = STEP_TRANSLATE_X; !stepAction && i <= STEP_YAW; ++i) {
|
||||
if (getDriveKey((DriveKeys)i) != 0.0f) {
|
||||
if (_driveKeys[i] != 0.0f) {
|
||||
stepAction = true;
|
||||
}
|
||||
}
|
||||
|
@ -1068,7 +1051,7 @@ void MyAvatar::updateLookAtTargetAvatar() {
|
|||
_lookAtTargetAvatar.reset();
|
||||
_targetAvatarPosition = glm::vec3(0.0f);
|
||||
|
||||
glm::vec3 lookForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FORWARD;
|
||||
glm::vec3 lookForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FRONT;
|
||||
glm::vec3 cameraPosition = qApp->getCamera()->getPosition();
|
||||
|
||||
float smallestAngleTo = glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES) / 2.0f;
|
||||
|
@ -1669,7 +1652,7 @@ bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const {
|
|||
void MyAvatar::updateOrientation(float deltaTime) {
|
||||
|
||||
// Smoothly rotate body with arrow keys
|
||||
float targetSpeed = getDriveKey(YAW) * _yawSpeed;
|
||||
float targetSpeed = _driveKeys[YAW] * _yawSpeed;
|
||||
if (targetSpeed != 0.0f) {
|
||||
const float ROTATION_RAMP_TIMESCALE = 0.1f;
|
||||
float blend = deltaTime / ROTATION_RAMP_TIMESCALE;
|
||||
|
@ -1698,8 +1681,8 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
// Comfort Mode: If you press any of the left/right rotation drive keys or input, you'll
|
||||
// get an instantaneous 15 degree turn. If you keep holding the key down you'll get another
|
||||
// snap turn every half second.
|
||||
if (getDriveKey(STEP_YAW) != 0.0f) {
|
||||
totalBodyYaw += getDriveKey(STEP_YAW);
|
||||
if (_driveKeys[STEP_YAW] != 0.0f) {
|
||||
totalBodyYaw += _driveKeys[STEP_YAW];
|
||||
}
|
||||
|
||||
// use head/HMD orientation to turn while flying
|
||||
|
@ -1736,7 +1719,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
// update body orientation by movement inputs
|
||||
setOrientation(getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f))));
|
||||
|
||||
getHead()->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime);
|
||||
getHead()->setBasePitch(getHead()->getBasePitch() + _driveKeys[PITCH] * _pitchSpeed * deltaTime);
|
||||
|
||||
if (qApp->isHMDMode()) {
|
||||
glm::quat orientation = glm::quat_cast(getSensorToWorldMatrix()) * getHMDSensorOrientation();
|
||||
|
@ -1770,14 +1753,14 @@ void MyAvatar::updateActionMotor(float deltaTime) {
|
|||
}
|
||||
|
||||
// compute action input
|
||||
glm::vec3 forward = (getDriveKey(TRANSLATE_Z)) * IDENTITY_FORWARD;
|
||||
glm::vec3 right = (getDriveKey(TRANSLATE_X)) * IDENTITY_RIGHT;
|
||||
glm::vec3 front = (_driveKeys[TRANSLATE_Z]) * IDENTITY_FRONT;
|
||||
glm::vec3 right = (_driveKeys[TRANSLATE_X]) * IDENTITY_RIGHT;
|
||||
|
||||
glm::vec3 direction = forward + right;
|
||||
glm::vec3 direction = front + right;
|
||||
CharacterController::State state = _characterController.getState();
|
||||
if (state == CharacterController::State::Hover) {
|
||||
// we're flying --> support vertical motion
|
||||
glm::vec3 up = (getDriveKey(TRANSLATE_Y)) * IDENTITY_UP;
|
||||
glm::vec3 up = (_driveKeys[TRANSLATE_Y]) * IDENTITY_UP;
|
||||
direction += up;
|
||||
}
|
||||
|
||||
|
@ -1816,7 +1799,7 @@ void MyAvatar::updateActionMotor(float deltaTime) {
|
|||
_actionMotorVelocity = MAX_WALKING_SPEED * direction;
|
||||
}
|
||||
|
||||
float boomChange = getDriveKey(ZOOM);
|
||||
float boomChange = _driveKeys[ZOOM];
|
||||
_boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange;
|
||||
_boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX);
|
||||
}
|
||||
|
@ -1847,11 +1830,11 @@ void MyAvatar::updatePosition(float deltaTime) {
|
|||
}
|
||||
|
||||
// capture the head rotation, in sensor space, when the user first indicates they would like to move/fly.
|
||||
if (!_hoverReferenceCameraFacingIsCaptured && (fabs(getDriveKey(TRANSLATE_Z)) > 0.1f || fabs(getDriveKey(TRANSLATE_X)) > 0.1f)) {
|
||||
if (!_hoverReferenceCameraFacingIsCaptured && (fabs(_driveKeys[TRANSLATE_Z]) > 0.1f || fabs(_driveKeys[TRANSLATE_X]) > 0.1f)) {
|
||||
_hoverReferenceCameraFacingIsCaptured = true;
|
||||
// transform the camera facing vector into sensor space.
|
||||
_hoverReferenceCameraFacing = transformVectorFast(glm::inverse(_sensorToWorldMatrix), getHead()->getCameraOrientation() * Vectors::UNIT_Z);
|
||||
} else if (_hoverReferenceCameraFacingIsCaptured && (fabs(getDriveKey(TRANSLATE_Z)) <= 0.1f && fabs(getDriveKey(TRANSLATE_X)) <= 0.1f)) {
|
||||
} else if (_hoverReferenceCameraFacingIsCaptured && (fabs(_driveKeys[TRANSLATE_Z]) <= 0.1f && fabs(_driveKeys[TRANSLATE_X]) <= 0.1f)) {
|
||||
_hoverReferenceCameraFacingIsCaptured = false;
|
||||
}
|
||||
}
|
||||
|
@ -2053,7 +2036,7 @@ void MyAvatar::goToLocation(const glm::vec3& newPosition,
|
|||
|
||||
// move the user a couple units away
|
||||
const float DISTANCE_TO_USER = 2.0f;
|
||||
_goToPosition = newPosition - quatOrientation * IDENTITY_FORWARD * DISTANCE_TO_USER;
|
||||
_goToPosition = newPosition - quatOrientation * IDENTITY_FRONT * DISTANCE_TO_USER;
|
||||
}
|
||||
|
||||
_goToOrientation = quatOrientation;
|
||||
|
@ -2107,61 +2090,17 @@ bool MyAvatar::getCharacterControllerEnabled() {
|
|||
}
|
||||
|
||||
void MyAvatar::clearDriveKeys() {
|
||||
_driveKeys.fill(0.0f);
|
||||
}
|
||||
|
||||
void MyAvatar::setDriveKey(DriveKeys key, float val) {
|
||||
try {
|
||||
_driveKeys.at(key) = val;
|
||||
} catch (const std::exception&) {
|
||||
qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds";
|
||||
}
|
||||
}
|
||||
|
||||
float MyAvatar::getDriveKey(DriveKeys key) const {
|
||||
return isDriveKeyDisabled(key) ? 0.0f : getRawDriveKey(key);
|
||||
}
|
||||
|
||||
float MyAvatar::getRawDriveKey(DriveKeys key) const {
|
||||
try {
|
||||
return _driveKeys.at(key);
|
||||
} catch (const std::exception&) {
|
||||
qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds";
|
||||
return 0.0f;
|
||||
for (int i = 0; i < MAX_DRIVE_KEYS; ++i) {
|
||||
_driveKeys[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::relayDriveKeysToCharacterController() {
|
||||
if (getDriveKey(TRANSLATE_Y) > 0.0f) {
|
||||
if (_driveKeys[TRANSLATE_Y] > 0.0f) {
|
||||
_characterController.jump();
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::disableDriveKey(DriveKeys key) {
|
||||
try {
|
||||
_disabledDriveKeys.set(key);
|
||||
} catch (const std::exception&) {
|
||||
qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds";
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::enableDriveKey(DriveKeys key) {
|
||||
try {
|
||||
_disabledDriveKeys.reset(key);
|
||||
} catch (const std::exception&) {
|
||||
qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds";
|
||||
}
|
||||
}
|
||||
|
||||
bool MyAvatar::isDriveKeyDisabled(DriveKeys key) const {
|
||||
try {
|
||||
return _disabledDriveKeys.test(key);
|
||||
} catch (const std::exception&) {
|
||||
qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::getWorldBodyPosition() const {
|
||||
return transformPoint(_sensorToWorldMatrix, extractTranslation(_bodySensorMatrix));
|
||||
}
|
||||
|
@ -2247,15 +2186,7 @@ QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioList
|
|||
}
|
||||
|
||||
void audioListenModeFromScriptValue(const QScriptValue& object, AudioListenerMode& audioListenerMode) {
|
||||
audioListenerMode = static_cast<AudioListenerMode>(object.toUInt16());
|
||||
}
|
||||
|
||||
QScriptValue driveKeysToScriptValue(QScriptEngine* engine, const MyAvatar::DriveKeys& driveKeys) {
|
||||
return driveKeys;
|
||||
}
|
||||
|
||||
void driveKeysFromScriptValue(const QScriptValue& object, MyAvatar::DriveKeys& driveKeys) {
|
||||
driveKeys = static_cast<MyAvatar::DriveKeys>(object.toUInt16());
|
||||
audioListenerMode = (AudioListenerMode)object.toUInt16();
|
||||
}
|
||||
|
||||
|
||||
|
@ -2448,7 +2379,7 @@ bool MyAvatar::didTeleport() {
|
|||
}
|
||||
|
||||
bool MyAvatar::hasDriveInput() const {
|
||||
return fabsf(getDriveKey(TRANSLATE_X)) > 0.0f || fabsf(getDriveKey(TRANSLATE_Y)) > 0.0f || fabsf(getDriveKey(TRANSLATE_Z)) > 0.0f;
|
||||
return fabsf(_driveKeys[TRANSLATE_X]) > 0.0f || fabsf(_driveKeys[TRANSLATE_Y]) > 0.0f || fabsf(_driveKeys[TRANSLATE_Z]) > 0.0f;
|
||||
}
|
||||
|
||||
void MyAvatar::setAway(bool value) {
|
||||
|
@ -2564,7 +2495,7 @@ bool MyAvatar::pinJoint(int index, const glm::vec3& position, const glm::quat& o
|
|||
return false;
|
||||
}
|
||||
|
||||
slamPosition(position);
|
||||
setPosition(position);
|
||||
setOrientation(orientation);
|
||||
|
||||
_rig->setMaxHipsOffsetLength(0.05f);
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
#ifndef hifi_MyAvatar_h
|
||||
#define hifi_MyAvatar_h
|
||||
|
||||
#include <bitset>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <SettingHandle.h>
|
||||
|
@ -31,6 +29,20 @@
|
|||
class AvatarActionHold;
|
||||
class ModelItemID;
|
||||
|
||||
enum DriveKeys {
|
||||
TRANSLATE_X = 0,
|
||||
TRANSLATE_Y,
|
||||
TRANSLATE_Z,
|
||||
YAW,
|
||||
STEP_TRANSLATE_X,
|
||||
STEP_TRANSLATE_Y,
|
||||
STEP_TRANSLATE_Z,
|
||||
STEP_YAW,
|
||||
PITCH,
|
||||
ZOOM,
|
||||
MAX_DRIVE_KEYS
|
||||
};
|
||||
|
||||
enum eyeContactTarget {
|
||||
LEFT_EYE,
|
||||
RIGHT_EYE,
|
||||
|
@ -74,29 +86,11 @@ class MyAvatar : public Avatar {
|
|||
|
||||
Q_PROPERTY(bool hmdLeanRecenterEnabled READ getHMDLeanRecenterEnabled WRITE setHMDLeanRecenterEnabled)
|
||||
Q_PROPERTY(bool characterControllerEnabled READ getCharacterControllerEnabled WRITE setCharacterControllerEnabled)
|
||||
Q_PROPERTY(bool useAdvancedMovementControls READ useAdvancedMovementControls WRITE setUseAdvancedMovementControls)
|
||||
|
||||
public:
|
||||
enum DriveKeys {
|
||||
TRANSLATE_X = 0,
|
||||
TRANSLATE_Y,
|
||||
TRANSLATE_Z,
|
||||
YAW,
|
||||
STEP_TRANSLATE_X,
|
||||
STEP_TRANSLATE_Y,
|
||||
STEP_TRANSLATE_Z,
|
||||
STEP_YAW,
|
||||
PITCH,
|
||||
ZOOM,
|
||||
MAX_DRIVE_KEYS
|
||||
};
|
||||
Q_ENUM(DriveKeys)
|
||||
|
||||
explicit MyAvatar(RigPointer rig);
|
||||
~MyAvatar();
|
||||
|
||||
void registerMetaTypes(QScriptEngine* engine);
|
||||
|
||||
virtual void simulateAttachments(float deltaTime) override;
|
||||
|
||||
AudioListenerMode getAudioListenerModeHead() const { return FROM_HEAD; }
|
||||
|
@ -177,10 +171,6 @@ public:
|
|||
Q_INVOKABLE void setHMDLeanRecenterEnabled(bool value) { _hmdLeanRecenterEnabled = value; }
|
||||
Q_INVOKABLE bool getHMDLeanRecenterEnabled() const { return _hmdLeanRecenterEnabled; }
|
||||
|
||||
bool useAdvancedMovementControls() const { return _useAdvancedMovementControls.get(); }
|
||||
void setUseAdvancedMovementControls(bool useAdvancedMovementControls)
|
||||
{ _useAdvancedMovementControls.set(useAdvancedMovementControls); }
|
||||
|
||||
// get/set avatar data
|
||||
void saveData();
|
||||
void loadData();
|
||||
|
@ -190,15 +180,9 @@ public:
|
|||
|
||||
// Set what driving keys are being pressed to control thrust levels
|
||||
void clearDriveKeys();
|
||||
void setDriveKey(DriveKeys key, float val);
|
||||
float getDriveKey(DriveKeys key) const;
|
||||
Q_INVOKABLE float getRawDriveKey(DriveKeys key) const;
|
||||
void setDriveKeys(int key, float val) { _driveKeys[key] = val; };
|
||||
void relayDriveKeysToCharacterController();
|
||||
|
||||
Q_INVOKABLE void disableDriveKey(DriveKeys key);
|
||||
Q_INVOKABLE void enableDriveKey(DriveKeys key);
|
||||
Q_INVOKABLE bool isDriveKeyDisabled(DriveKeys key) const;
|
||||
|
||||
eyeContactTarget getEyeContactTarget();
|
||||
|
||||
Q_INVOKABLE glm::vec3 getTrackedHeadPosition() const { return _trackedHeadPosition; }
|
||||
|
@ -368,6 +352,7 @@ private:
|
|||
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override;
|
||||
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; setEnableMeshVisible(shouldRender); }
|
||||
bool getShouldRenderLocally() const { return _shouldRender; }
|
||||
bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; };
|
||||
bool isMyAvatar() const override { return true; }
|
||||
virtual int parseDataFromBuffer(const QByteArray& buffer) override;
|
||||
virtual glm::vec3 getSkeletonPosition() const override;
|
||||
|
@ -403,9 +388,7 @@ private:
|
|||
void clampScaleChangeToDomainLimits(float desiredScale);
|
||||
glm::mat4 computeCameraRelativeHandControllerMatrix(const glm::mat4& controllerSensorMatrix) const;
|
||||
|
||||
std::array<float, MAX_DRIVE_KEYS> _driveKeys;
|
||||
std::bitset<MAX_DRIVE_KEYS> _disabledDriveKeys;
|
||||
|
||||
float _driveKeys[MAX_DRIVE_KEYS];
|
||||
bool _wasPushing;
|
||||
bool _isPushing;
|
||||
bool _isBeingPushed;
|
||||
|
@ -428,7 +411,6 @@ private:
|
|||
SharedSoundPointer _collisionSound;
|
||||
|
||||
MyCharacterController _characterController;
|
||||
bool _wasCharacterControllerEnabled { true };
|
||||
|
||||
AvatarWeakPointer _lookAtTargetAvatar;
|
||||
glm::vec3 _targetAvatarPosition;
|
||||
|
@ -441,7 +423,6 @@ private:
|
|||
glm::vec3 _trackedHeadPosition;
|
||||
|
||||
Setting::Handle<float> _realWorldFieldOfView;
|
||||
Setting::Handle<bool> _useAdvancedMovementControls;
|
||||
|
||||
// private methods
|
||||
void updateOrientation(float deltaTime);
|
||||
|
@ -559,7 +540,4 @@ private:
|
|||
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);
|
||||
void audioListenModeFromScriptValue(const QScriptValue& object, AudioListenerMode& audioListenerMode);
|
||||
|
||||
QScriptValue driveKeysToScriptValue(QScriptEngine* engine, const MyAvatar::DriveKeys& driveKeys);
|
||||
void driveKeysFromScriptValue(const QScriptValue& object, MyAvatar::DriveKeys& driveKeys);
|
||||
|
||||
#endif // hifi_MyAvatar_h
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <avatar/AvatarManager.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <FramebufferCache.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <CursorManager.h>
|
||||
|
@ -41,6 +42,7 @@ ApplicationOverlay::ApplicationOverlay()
|
|||
_domainStatusBorder = geometryCache->allocateID();
|
||||
_magnifierBorder = geometryCache->allocateID();
|
||||
_qmlGeometryId = geometryCache->allocateID();
|
||||
_rearViewGeometryId = geometryCache->allocateID();
|
||||
}
|
||||
|
||||
ApplicationOverlay::~ApplicationOverlay() {
|
||||
|
@ -49,6 +51,7 @@ ApplicationOverlay::~ApplicationOverlay() {
|
|||
geometryCache->releaseID(_domainStatusBorder);
|
||||
geometryCache->releaseID(_magnifierBorder);
|
||||
geometryCache->releaseID(_qmlGeometryId);
|
||||
geometryCache->releaseID(_rearViewGeometryId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,6 +86,7 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
|
|||
// Now render the overlay components together into a single texture
|
||||
renderDomainConnectionStatusBorder(renderArgs); // renders the connected domain line
|
||||
renderAudioScope(renderArgs); // audio scope in the very back - NOTE: this is the debug audio scope, not the VU meter
|
||||
renderRearView(renderArgs); // renders the mirror view selfie
|
||||
renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope
|
||||
renderQmlUi(renderArgs); // renders a unit quad with the QML UI texture, and the text overlays from scripts
|
||||
renderStatsAndLogs(renderArgs); // currently renders nothing
|
||||
|
@ -95,7 +99,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
|
|||
PROFILE_RANGE(app, __FUNCTION__);
|
||||
|
||||
if (!_uiTexture) {
|
||||
_uiTexture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda()));
|
||||
_uiTexture = gpu::TexturePointer(gpu::Texture::createExternal2D(OffscreenQmlSurface::getDiscardLambda()));
|
||||
_uiTexture->setSource(__FUNCTION__);
|
||||
}
|
||||
// Once we move UI rendering and screen rendering to different
|
||||
|
@ -159,6 +163,45 @@ void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) {
|
|||
qApp->getOverlays().renderHUD(renderArgs);
|
||||
}
|
||||
|
||||
void ApplicationOverlay::renderRearViewToFbo(RenderArgs* renderArgs) {
|
||||
}
|
||||
|
||||
void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) {
|
||||
if (!qApp->isHMDMode() && Menu::getInstance()->isOptionChecked(MenuOption::MiniMirror) &&
|
||||
!Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
|
||||
gpu::Batch& batch = *renderArgs->_batch;
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
auto framebuffer = DependencyManager::get<FramebufferCache>();
|
||||
auto selfieTexture = framebuffer->getSelfieFramebuffer()->getRenderBuffer(0);
|
||||
|
||||
int width = renderArgs->_viewport.z;
|
||||
int height = renderArgs->_viewport.w;
|
||||
mat4 legacyProjection = glm::ortho<float>(0, width, height, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP);
|
||||
batch.setProjectionTransform(legacyProjection);
|
||||
batch.setModelTransform(Transform());
|
||||
batch.resetViewTransform();
|
||||
|
||||
float screenRatio = ((float)qApp->getDevicePixelRatio());
|
||||
float renderRatio = ((float)qApp->getRenderResolutionScale());
|
||||
|
||||
auto viewport = qApp->getMirrorViewRect();
|
||||
glm::vec2 bottomLeft(viewport.left(), viewport.top() + viewport.height());
|
||||
glm::vec2 topRight(viewport.left() + viewport.width(), viewport.top());
|
||||
bottomLeft *= screenRatio;
|
||||
topRight *= screenRatio;
|
||||
glm::vec2 texCoordMinCorner(0.0f, 0.0f);
|
||||
glm::vec2 texCoordMaxCorner(viewport.width() * renderRatio / float(selfieTexture->getWidth()), viewport.height() * renderRatio / float(selfieTexture->getHeight()));
|
||||
|
||||
batch.setResourceTexture(0, selfieTexture);
|
||||
float alpha = DependencyManager::get<OffscreenUi>()->getDesktop()->property("unpinnedAlpha").toFloat();
|
||||
geometryCache->renderQuad(batch, bottomLeft, topRight, texCoordMinCorner, texCoordMaxCorner, glm::vec4(1.0f, 1.0f, 1.0f, alpha), _rearViewGeometryId);
|
||||
|
||||
batch.setResourceTexture(0, renderArgs->_whiteTexture);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationOverlay::renderStatsAndLogs(RenderArgs* renderArgs) {
|
||||
|
||||
// Display stats and log text onscreen
|
||||
|
@ -229,13 +272,13 @@ void ApplicationOverlay::buildFramebufferObject() {
|
|||
auto width = uiSize.x;
|
||||
auto height = uiSize.y;
|
||||
if (!_overlayFramebuffer->getDepthStencilBuffer()) {
|
||||
auto overlayDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(DEPTH_FORMAT, width, height, DEFAULT_SAMPLER));
|
||||
auto overlayDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(DEPTH_FORMAT, width, height, DEFAULT_SAMPLER));
|
||||
_overlayFramebuffer->setDepthStencilBuffer(overlayDepthTexture, DEPTH_FORMAT);
|
||||
}
|
||||
|
||||
if (!_overlayFramebuffer->getRenderBuffer(0)) {
|
||||
const gpu::Sampler OVERLAY_SAMPLER(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP);
|
||||
auto colorBuffer = gpu::TexturePointer(gpu::Texture::createRenderBuffer(COLOR_FORMAT, width, height, OVERLAY_SAMPLER));
|
||||
auto colorBuffer = gpu::TexturePointer(gpu::Texture::create2D(COLOR_FORMAT, width, height, OVERLAY_SAMPLER));
|
||||
_overlayFramebuffer->setRenderBuffer(0, colorBuffer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@ public:
|
|||
private:
|
||||
void renderStatsAndLogs(RenderArgs* renderArgs);
|
||||
void renderDomainConnectionStatusBorder(RenderArgs* renderArgs);
|
||||
void renderRearViewToFbo(RenderArgs* renderArgs);
|
||||
void renderRearView(RenderArgs* renderArgs);
|
||||
void renderQmlUi(RenderArgs* renderArgs);
|
||||
void renderAudioScope(RenderArgs* renderArgs);
|
||||
void renderOverlays(RenderArgs* renderArgs);
|
||||
|
@ -49,6 +51,7 @@ private:
|
|||
gpu::TexturePointer _overlayColorTexture;
|
||||
gpu::FramebufferPointer _overlayFramebuffer;
|
||||
int _qmlGeometryId { 0 };
|
||||
int _rearViewGeometryId { 0 };
|
||||
};
|
||||
|
||||
#endif // hifi_ApplicationOverlay_h
|
||||
|
|
|
@ -20,6 +20,10 @@ HIFI_QML_DEF(AvatarInputs)
|
|||
|
||||
|
||||
static AvatarInputs* INSTANCE{ nullptr };
|
||||
static const char SETTINGS_GROUP_NAME[] = "Rear View Tools";
|
||||
static const char ZOOM_LEVEL_SETTINGS[] = "ZoomLevel";
|
||||
|
||||
static Setting::Handle<int> rearViewZoomLevel(QStringList() << SETTINGS_GROUP_NAME << ZOOM_LEVEL_SETTINGS, 0);
|
||||
|
||||
AvatarInputs* AvatarInputs::getInstance() {
|
||||
if (!INSTANCE) {
|
||||
|
@ -32,6 +36,8 @@ AvatarInputs* AvatarInputs::getInstance() {
|
|||
|
||||
AvatarInputs::AvatarInputs(QQuickItem* parent) : QQuickItem(parent) {
|
||||
INSTANCE = this;
|
||||
int zoomSetting = rearViewZoomLevel.get();
|
||||
_mirrorZoomed = zoomSetting == 0;
|
||||
}
|
||||
|
||||
#define AI_UPDATE(name, src) \
|
||||
|
@ -56,6 +62,8 @@ void AvatarInputs::update() {
|
|||
if (!Menu::getInstance()) {
|
||||
return;
|
||||
}
|
||||
AI_UPDATE(mirrorVisible, Menu::getInstance()->isOptionChecked(MenuOption::MiniMirror) && !qApp->isHMDMode()
|
||||
&& !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror));
|
||||
AI_UPDATE(cameraEnabled, !Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking));
|
||||
AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking));
|
||||
AI_UPDATE(isHMD, qApp->isHMDMode());
|
||||
|
@ -114,3 +122,15 @@ void AvatarInputs::toggleAudioMute() {
|
|||
void AvatarInputs::resetSensors() {
|
||||
qApp->resetSensors();
|
||||
}
|
||||
|
||||
void AvatarInputs::toggleZoom() {
|
||||
_mirrorZoomed = !_mirrorZoomed;
|
||||
rearViewZoomLevel.set(_mirrorZoomed ? 0 : 1);
|
||||
emit mirrorZoomedChanged();
|
||||
}
|
||||
|
||||
void AvatarInputs::closeMirror() {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::MiniMirror)) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::MiniMirror);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@ class AvatarInputs : public QQuickItem {
|
|||
AI_PROPERTY(bool, audioMuted, false)
|
||||
AI_PROPERTY(bool, audioClipping, false)
|
||||
AI_PROPERTY(float, audioLevel, 0)
|
||||
AI_PROPERTY(bool, mirrorVisible, false)
|
||||
AI_PROPERTY(bool, mirrorZoomed, true)
|
||||
AI_PROPERTY(bool, isHMD, false)
|
||||
AI_PROPERTY(bool, showAudioTools, true)
|
||||
|
||||
|
@ -42,6 +44,8 @@ signals:
|
|||
void audioMutedChanged();
|
||||
void audioClippingChanged();
|
||||
void audioLevelChanged();
|
||||
void mirrorVisibleChanged();
|
||||
void mirrorZoomedChanged();
|
||||
void isHMDChanged();
|
||||
void showAudioToolsChanged();
|
||||
|
||||
|
@ -49,6 +53,8 @@ protected:
|
|||
Q_INVOKABLE void resetSensors();
|
||||
Q_INVOKABLE void toggleCameraMute();
|
||||
Q_INVOKABLE void toggleAudioMute();
|
||||
Q_INVOKABLE void toggleZoom();
|
||||
Q_INVOKABLE void closeMirror();
|
||||
|
||||
private:
|
||||
float _trailingAudioLoudness{ 0 };
|
||||
|
|
|
@ -28,23 +28,17 @@ const int SEARCH_BUTTON_LEFT = 25;
|
|||
const int SEARCH_BUTTON_WIDTH = 20;
|
||||
const int SEARCH_TOGGLE_BUTTON_WIDTH = 50;
|
||||
const int SEARCH_TEXT_WIDTH = 240;
|
||||
const int TIME_STAMP_LENGTH = 16;
|
||||
const int FONT_WEIGHT = 75;
|
||||
const QColor HIGHLIGHT_COLOR = QColor("#3366CC");
|
||||
const QColor BOLD_COLOR = QColor("#445c8c");
|
||||
const QString BOLD_PATTERN = "\\[\\d*\\/.*:\\d*:\\d*\\]";
|
||||
|
||||
class Highlighter : public QSyntaxHighlighter {
|
||||
class KeywordHighlighter : public QSyntaxHighlighter {
|
||||
public:
|
||||
Highlighter(QTextDocument* parent = nullptr);
|
||||
void setBold(int indexToBold);
|
||||
KeywordHighlighter(QTextDocument* parent = nullptr);
|
||||
QString keyword;
|
||||
|
||||
protected:
|
||||
void highlightBlock(const QString& text) override;
|
||||
|
||||
private:
|
||||
QTextCharFormat boldFormat;
|
||||
QTextCharFormat keywordFormat;
|
||||
|
||||
};
|
||||
|
@ -95,7 +89,7 @@ void BaseLogDialog::initControls() {
|
|||
_leftPad += SEARCH_TOGGLE_BUTTON_WIDTH + BUTTON_MARGIN;
|
||||
_searchPrevButton->show();
|
||||
connect(_searchPrevButton, SIGNAL(clicked()), SLOT(toggleSearchPrev()));
|
||||
|
||||
|
||||
_searchNextButton = new QPushButton(this);
|
||||
_searchNextButton->setObjectName("searchNextButton");
|
||||
_searchNextButton->setGeometry(_leftPad, ELEMENT_MARGIN, SEARCH_TOGGLE_BUTTON_WIDTH, ELEMENT_HEIGHT);
|
||||
|
@ -107,8 +101,9 @@ void BaseLogDialog::initControls() {
|
|||
_logTextBox = new QPlainTextEdit(this);
|
||||
_logTextBox->setReadOnly(true);
|
||||
_logTextBox->show();
|
||||
_highlighter = new Highlighter(_logTextBox->document());
|
||||
_highlighter = new KeywordHighlighter(_logTextBox->document());
|
||||
connect(_logTextBox, SIGNAL(selectionChanged()), SLOT(updateSelection()));
|
||||
|
||||
}
|
||||
|
||||
void BaseLogDialog::showEvent(QShowEvent* event) {
|
||||
|
@ -121,9 +116,7 @@ void BaseLogDialog::resizeEvent(QResizeEvent* event) {
|
|||
|
||||
void BaseLogDialog::appendLogLine(QString logLine) {
|
||||
if (logLine.contains(_searchTerm, Qt::CaseInsensitive)) {
|
||||
int indexToBold = _logTextBox->document()->characterCount();
|
||||
_logTextBox->appendPlainText(logLine.trimmed());
|
||||
_highlighter->setBold(indexToBold);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,7 +128,7 @@ void BaseLogDialog::handleSearchTextChanged(QString searchText) {
|
|||
if (searchText.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
QTextCursor cursor = _logTextBox->textCursor();
|
||||
if (cursor.hasSelection()) {
|
||||
QString selectedTerm = cursor.selectedText();
|
||||
|
@ -143,16 +136,16 @@ void BaseLogDialog::handleSearchTextChanged(QString searchText) {
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cursor.setPosition(0, QTextCursor::MoveAnchor);
|
||||
_logTextBox->setTextCursor(cursor);
|
||||
bool foundTerm = _logTextBox->find(searchText);
|
||||
|
||||
|
||||
if (!foundTerm) {
|
||||
cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
|
||||
_logTextBox->setTextCursor(cursor);
|
||||
}
|
||||
|
||||
|
||||
_searchTerm = searchText;
|
||||
_highlighter->keyword = searchText;
|
||||
_highlighter->rehighlight();
|
||||
|
@ -182,7 +175,6 @@ void BaseLogDialog::showLogData() {
|
|||
_logTextBox->clear();
|
||||
_logTextBox->appendPlainText(getCurrentLog());
|
||||
_logTextBox->ensureCursorVisible();
|
||||
_highlighter->rehighlight();
|
||||
}
|
||||
|
||||
void BaseLogDialog::updateSelection() {
|
||||
|
@ -195,28 +187,16 @@ void BaseLogDialog::updateSelection() {
|
|||
}
|
||||
}
|
||||
|
||||
Highlighter::Highlighter(QTextDocument* parent) : QSyntaxHighlighter(parent) {
|
||||
boldFormat.setFontWeight(FONT_WEIGHT);
|
||||
boldFormat.setForeground(BOLD_COLOR);
|
||||
KeywordHighlighter::KeywordHighlighter(QTextDocument* parent) : QSyntaxHighlighter(parent) {
|
||||
keywordFormat.setForeground(HIGHLIGHT_COLOR);
|
||||
}
|
||||
|
||||
void Highlighter::highlightBlock(const QString& text) {
|
||||
QRegExp expression(BOLD_PATTERN);
|
||||
|
||||
int index = text.indexOf(expression, 0);
|
||||
|
||||
while (index >= 0) {
|
||||
int length = expression.matchedLength();
|
||||
setFormat(index, length, boldFormat);
|
||||
index = text.indexOf(expression, index + length);
|
||||
}
|
||||
|
||||
void KeywordHighlighter::highlightBlock(const QString& text) {
|
||||
if (keyword.isNull() || keyword.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
index = text.indexOf(keyword, 0, Qt::CaseInsensitive);
|
||||
int index = text.indexOf(keyword, 0, Qt::CaseInsensitive);
|
||||
int length = keyword.length();
|
||||
|
||||
while (index >= 0) {
|
||||
|
@ -224,7 +204,3 @@ void Highlighter::highlightBlock(const QString& text) {
|
|||
index = text.indexOf(keyword, index + length, Qt::CaseInsensitive);
|
||||
}
|
||||
}
|
||||
|
||||
void Highlighter::setBold(int indexToBold) {
|
||||
setFormat(indexToBold, TIME_STAMP_LENGTH, boldFormat);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ const int BUTTON_MARGIN = 8;
|
|||
class QPushButton;
|
||||
class QLineEdit;
|
||||
class QPlainTextEdit;
|
||||
class Highlighter;
|
||||
class KeywordHighlighter;
|
||||
|
||||
class BaseLogDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
@ -56,7 +56,7 @@ private:
|
|||
QPushButton* _searchPrevButton { nullptr };
|
||||
QPushButton* _searchNextButton { nullptr };
|
||||
QString _searchTerm;
|
||||
Highlighter* _highlighter { nullptr };
|
||||
KeywordHighlighter* _highlighter { nullptr };
|
||||
|
||||
void initControls();
|
||||
void showLogData();
|
||||
|
|
84
interface/src/ui/CachesSizeDialog.cpp
Normal file
|
@ -0,0 +1,84 @@
|
|||
//
|
||||
// CachesSizeDialog.cpp
|
||||
//
|
||||
//
|
||||
// Created by Clement 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 <QDoubleSpinBox>
|
||||
#include <QFormLayout>
|
||||
#include <QPushButton>
|
||||
|
||||
#include <AnimationCache.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <SoundCache.h>
|
||||
#include <TextureCache.h>
|
||||
|
||||
#include "CachesSizeDialog.h"
|
||||
|
||||
|
||||
QDoubleSpinBox* createDoubleSpinBox(QWidget* parent) {
|
||||
QDoubleSpinBox* box = new QDoubleSpinBox(parent);
|
||||
box->setDecimals(0);
|
||||
box->setRange(MIN_UNUSED_MAX_SIZE / BYTES_PER_MEGABYTES, MAX_UNUSED_MAX_SIZE / BYTES_PER_MEGABYTES);
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
CachesSizeDialog::CachesSizeDialog(QWidget* parent) :
|
||||
QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint)
|
||||
{
|
||||
setWindowTitle("Caches Size");
|
||||
|
||||
// Create layouter
|
||||
QFormLayout* form = new QFormLayout(this);
|
||||
setLayout(form);
|
||||
|
||||
form->addRow("Animations cache size (MB):", _animations = createDoubleSpinBox(this));
|
||||
form->addRow("Geometries cache size (MB):", _geometries = createDoubleSpinBox(this));
|
||||
form->addRow("Sounds cache size (MB):", _sounds = createDoubleSpinBox(this));
|
||||
form->addRow("Textures cache size (MB):", _textures = createDoubleSpinBox(this));
|
||||
|
||||
resetClicked(true);
|
||||
|
||||
// Add a button to reset
|
||||
QPushButton* confirmButton = new QPushButton("Confirm", this);
|
||||
QPushButton* resetButton = new QPushButton("Reset", this);
|
||||
form->addRow(confirmButton, resetButton);
|
||||
connect(confirmButton, SIGNAL(clicked(bool)), this, SLOT(confirmClicked(bool)));
|
||||
connect(resetButton, SIGNAL(clicked(bool)), this, SLOT(resetClicked(bool)));
|
||||
}
|
||||
|
||||
void CachesSizeDialog::confirmClicked(bool checked) {
|
||||
DependencyManager::get<AnimationCache>()->setUnusedResourceCacheSize(_animations->value() * BYTES_PER_MEGABYTES);
|
||||
DependencyManager::get<ModelCache>()->setUnusedResourceCacheSize(_geometries->value() * BYTES_PER_MEGABYTES);
|
||||
DependencyManager::get<SoundCache>()->setUnusedResourceCacheSize(_sounds->value() * BYTES_PER_MEGABYTES);
|
||||
// Disabling the texture cache because it's a liability in cases where we're overcommiting GPU memory
|
||||
#if 0
|
||||
DependencyManager::get<TextureCache>()->setUnusedResourceCacheSize(_textures->value() * BYTES_PER_MEGABYTES);
|
||||
#endif
|
||||
|
||||
QDialog::close();
|
||||
}
|
||||
|
||||
void CachesSizeDialog::resetClicked(bool checked) {
|
||||
_animations->setValue(DependencyManager::get<AnimationCache>()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES);
|
||||
_geometries->setValue(DependencyManager::get<ModelCache>()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES);
|
||||
_sounds->setValue(DependencyManager::get<SoundCache>()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES);
|
||||
_textures->setValue(DependencyManager::get<TextureCache>()->getUnusedResourceCacheSize() / BYTES_PER_MEGABYTES);
|
||||
}
|
||||
|
||||
void CachesSizeDialog::reject() {
|
||||
// Just regularly close upon ESC
|
||||
QDialog::close();
|
||||
}
|
||||
|
||||
void CachesSizeDialog::closeEvent(QCloseEvent* event) {
|
||||
QDialog::closeEvent(event);
|
||||
emit closed();
|
||||
}
|
45
interface/src/ui/CachesSizeDialog.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// CachesSizeDialog.h
|
||||
//
|
||||
//
|
||||
// Created by Clement 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_CachesSizeDialog_h
|
||||
#define hifi_CachesSizeDialog_h
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class QDoubleSpinBox;
|
||||
|
||||
class CachesSizeDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
// Sets up the UI
|
||||
CachesSizeDialog(QWidget* parent);
|
||||
|
||||
signals:
|
||||
void closed();
|
||||
|
||||
public slots:
|
||||
void reject() override;
|
||||
void confirmClicked(bool checked);
|
||||
void resetClicked(bool checked);
|
||||
|
||||
protected:
|
||||
// Emits a 'closed' signal when this dialog is closed.
|
||||
void closeEvent(QCloseEvent* event) override;
|
||||
|
||||
private:
|
||||
QDoubleSpinBox* _animations = nullptr;
|
||||
QDoubleSpinBox* _geometries = nullptr;
|
||||
QDoubleSpinBox* _scripts = nullptr;
|
||||
QDoubleSpinBox* _sounds = nullptr;
|
||||
QDoubleSpinBox* _textures = nullptr;
|
||||
};
|
||||
|
||||
#endif // hifi_CachesSizeDialog_h
|
|
@ -19,13 +19,16 @@
|
|||
#include <PathUtils.h>
|
||||
|
||||
#include "AddressBarDialog.h"
|
||||
#include "CachesSizeDialog.h"
|
||||
#include "ConnectionFailureDialog.h"
|
||||
#include "DiskCacheEditor.h"
|
||||
#include "DomainConnectionDialog.h"
|
||||
#include "HMDToolsDialog.h"
|
||||
#include "LodToolsDialog.h"
|
||||
#include "LoginDialog.h"
|
||||
#include "OctreeStatsDialog.h"
|
||||
#include "PreferencesDialog.h"
|
||||
#include "ScriptEditorWindow.h"
|
||||
#include "UpdateDialog.h"
|
||||
|
||||
template<typename T>
|
||||
|
@ -64,6 +67,11 @@ void DialogsManager::setDomainConnectionFailureVisibility(bool visible) {
|
|||
}
|
||||
}
|
||||
|
||||
void DialogsManager::toggleDiskCacheEditor() {
|
||||
maybeCreateDialog(_diskCacheEditor);
|
||||
_diskCacheEditor->toggle();
|
||||
}
|
||||
|
||||
void DialogsManager::toggleLoginDialog() {
|
||||
LoginDialog::toggleAction();
|
||||
}
|
||||
|
@ -89,6 +97,16 @@ void DialogsManager::octreeStatsDetails() {
|
|||
_octreeStatsDialog->raise();
|
||||
}
|
||||
|
||||
void DialogsManager::cachesSizeDialog() {
|
||||
if (!_cachesSizeDialog) {
|
||||
maybeCreateDialog(_cachesSizeDialog);
|
||||
|
||||
connect(_cachesSizeDialog, SIGNAL(closed()), _cachesSizeDialog, SLOT(deleteLater()));
|
||||
_cachesSizeDialog->show();
|
||||
}
|
||||
_cachesSizeDialog->raise();
|
||||
}
|
||||
|
||||
void DialogsManager::lodTools() {
|
||||
if (!_lodToolsDialog) {
|
||||
maybeCreateDialog(_lodToolsDialog);
|
||||
|
@ -119,6 +137,12 @@ void DialogsManager::hmdToolsClosed() {
|
|||
}
|
||||
}
|
||||
|
||||
void DialogsManager::showScriptEditor() {
|
||||
maybeCreateDialog(_scriptEditor);
|
||||
_scriptEditor->show();
|
||||
_scriptEditor->raise();
|
||||
}
|
||||
|
||||
void DialogsManager::showTestingResults() {
|
||||
if (!_testingDialog) {
|
||||
_testingDialog = new TestingDialog(qApp->getWindow());
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
class AnimationsDialog;
|
||||
class AttachmentsDialog;
|
||||
class CachesSizeDialog;
|
||||
class DiskCacheEditor;
|
||||
class LodToolsDialog;
|
||||
class OctreeStatsDialog;
|
||||
class ScriptEditorWindow;
|
||||
|
@ -45,11 +46,14 @@ public slots:
|
|||
void showAddressBar();
|
||||
void showFeed();
|
||||
void setDomainConnectionFailureVisibility(bool visible);
|
||||
void toggleDiskCacheEditor();
|
||||
void toggleLoginDialog();
|
||||
void showLoginDialog();
|
||||
void octreeStatsDetails();
|
||||
void cachesSizeDialog();
|
||||
void lodTools();
|
||||
void hmdTools(bool showTools);
|
||||
void showScriptEditor();
|
||||
void showDomainConnectionDialog();
|
||||
void showTestingResults();
|
||||
|
||||
|
@ -73,10 +77,12 @@ private:
|
|||
QPointer<AnimationsDialog> _animationsDialog;
|
||||
QPointer<AttachmentsDialog> _attachmentsDialog;
|
||||
QPointer<CachesSizeDialog> _cachesSizeDialog;
|
||||
QPointer<DiskCacheEditor> _diskCacheEditor;
|
||||
QPointer<QMessageBox> _ircInfoBox;
|
||||
QPointer<HMDToolsDialog> _hmdToolsDialog;
|
||||
QPointer<LodToolsDialog> _lodToolsDialog;
|
||||
QPointer<OctreeStatsDialog> _octreeStatsDialog;
|
||||
QPointer<ScriptEditorWindow> _scriptEditor;
|
||||
QPointer<TestingDialog> _testingDialog;
|
||||
QPointer<DomainConnectionDialog> _domainConnectionDialog;
|
||||
};
|
||||
|
|
146
interface/src/ui/DiskCacheEditor.cpp
Normal file
|
@ -0,0 +1,146 @@
|
|||
//
|
||||
// DiskCacheEditor.cpp
|
||||
//
|
||||
//
|
||||
// Created by Clement on 3/4/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 "DiskCacheEditor.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDialog>
|
||||
#include <QGridLayout>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QTimer>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <AssetClient.h>
|
||||
|
||||
#include "OffscreenUi.h"
|
||||
|
||||
DiskCacheEditor::DiskCacheEditor(QWidget* parent) : QObject(parent) {
|
||||
}
|
||||
|
||||
QWindow* DiskCacheEditor::windowHandle() {
|
||||
return (_dialog) ? _dialog->windowHandle() : nullptr;
|
||||
}
|
||||
|
||||
void DiskCacheEditor::toggle() {
|
||||
if (!_dialog) {
|
||||
makeDialog();
|
||||
}
|
||||
|
||||
if (!_dialog->isActiveWindow()) {
|
||||
_dialog->show();
|
||||
_dialog->raise();
|
||||
_dialog->activateWindow();
|
||||
} else {
|
||||
_dialog->close();
|
||||
}
|
||||
}
|
||||
|
||||
void DiskCacheEditor::makeDialog() {
|
||||
_dialog = new QDialog(static_cast<QWidget*>(parent()));
|
||||
Q_CHECK_PTR(_dialog);
|
||||
_dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
_dialog->setWindowTitle("Disk Cache Editor");
|
||||
|
||||
QGridLayout* layout = new QGridLayout(_dialog);
|
||||
Q_CHECK_PTR(layout);
|
||||
_dialog->setLayout(layout);
|
||||
|
||||
|
||||
QLabel* path = new QLabel("Path : ", _dialog);
|
||||
Q_CHECK_PTR(path);
|
||||
path->setAlignment(Qt::AlignRight);
|
||||
layout->addWidget(path, 0, 0);
|
||||
|
||||
QLabel* size = new QLabel("Current Size : ", _dialog);
|
||||
Q_CHECK_PTR(size);
|
||||
size->setAlignment(Qt::AlignRight);
|
||||
layout->addWidget(size, 1, 0);
|
||||
|
||||
QLabel* maxSize = new QLabel("Max Size : ", _dialog);
|
||||
Q_CHECK_PTR(maxSize);
|
||||
maxSize->setAlignment(Qt::AlignRight);
|
||||
layout->addWidget(maxSize, 2, 0);
|
||||
|
||||
|
||||
_path = new QLabel(_dialog);
|
||||
Q_CHECK_PTR(_path);
|
||||
_path->setAlignment(Qt::AlignLeft);
|
||||
layout->addWidget(_path, 0, 1, 1, 3);
|
||||
|
||||
_size = new QLabel(_dialog);
|
||||
Q_CHECK_PTR(_size);
|
||||
_size->setAlignment(Qt::AlignLeft);
|
||||
layout->addWidget(_size, 1, 1, 1, 3);
|
||||
|
||||
_maxSize = new QLabel(_dialog);
|
||||
Q_CHECK_PTR(_maxSize);
|
||||
_maxSize->setAlignment(Qt::AlignLeft);
|
||||
layout->addWidget(_maxSize, 2, 1, 1, 3);
|
||||
|
||||
refresh();
|
||||
|
||||
|
||||
static const int REFRESH_INTERVAL = 100; // msec
|
||||
_refreshTimer = new QTimer(_dialog);
|
||||
_refreshTimer->setInterval(REFRESH_INTERVAL); // Qt::CoarseTimer acceptable, no need for real time accuracy
|
||||
_refreshTimer->setSingleShot(false);
|
||||
QObject::connect(_refreshTimer.data(), &QTimer::timeout, this, &DiskCacheEditor::refresh);
|
||||
_refreshTimer->start();
|
||||
|
||||
QPushButton* clearCacheButton = new QPushButton(_dialog);
|
||||
Q_CHECK_PTR(clearCacheButton);
|
||||
clearCacheButton->setText("Clear");
|
||||
clearCacheButton->setToolTip("Erases the entire content of the disk cache.");
|
||||
connect(clearCacheButton, SIGNAL(clicked()), SLOT(clear()));
|
||||
layout->addWidget(clearCacheButton, 3, 3);
|
||||
}
|
||||
|
||||
void DiskCacheEditor::refresh() {
|
||||
DependencyManager::get<AssetClient>()->cacheInfoRequest(this, "cacheInfoCallback");
|
||||
}
|
||||
|
||||
void DiskCacheEditor::cacheInfoCallback(QString cacheDirectory, qint64 cacheSize, qint64 maximumCacheSize) {
|
||||
static const auto stringify = [](qint64 number) {
|
||||
static const QStringList UNITS = QStringList() << "B" << "KB" << "MB" << "GB";
|
||||
static const qint64 CHUNK = 1024;
|
||||
QString unit;
|
||||
int i = 0;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
if (number / CHUNK > 0) {
|
||||
number /= CHUNK;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QString("%0 %1").arg(number).arg(UNITS[i]);
|
||||
};
|
||||
|
||||
if (_path) {
|
||||
_path->setText(cacheDirectory);
|
||||
}
|
||||
if (_size) {
|
||||
_size->setText(stringify(cacheSize));
|
||||
}
|
||||
if (_maxSize) {
|
||||
_maxSize->setText(stringify(maximumCacheSize));
|
||||
}
|
||||
}
|
||||
|
||||
void DiskCacheEditor::clear() {
|
||||
auto buttonClicked = OffscreenUi::question(_dialog, "Clearing disk cache",
|
||||
"You are about to erase all the content of the disk cache, "
|
||||
"are you sure you want to do that?",
|
||||
QMessageBox::Ok | QMessageBox::Cancel);
|
||||
if (buttonClicked == QMessageBox::Ok) {
|
||||
DependencyManager::get<AssetClient>()->clearCache();
|
||||
}
|
||||
}
|
49
interface/src/ui/DiskCacheEditor.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
//
|
||||
// DiskCacheEditor.h
|
||||
//
|
||||
//
|
||||
// Created by Clement on 3/4/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_DiskCacheEditor_h
|
||||
#define hifi_DiskCacheEditor_h
|
||||
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
|
||||
class QDialog;
|
||||
class QLabel;
|
||||
class QWindow;
|
||||
class QTimer;
|
||||
|
||||
class DiskCacheEditor : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DiskCacheEditor(QWidget* parent = nullptr);
|
||||
|
||||
QWindow* windowHandle();
|
||||
|
||||
public slots:
|
||||
void toggle();
|
||||
|
||||
private slots:
|
||||
void refresh();
|
||||
void cacheInfoCallback(QString cacheDirectory, qint64 cacheSize, qint64 maximumCacheSize);
|
||||
void clear();
|
||||
|
||||
private:
|
||||
void makeDialog();
|
||||
|
||||
QPointer<QDialog> _dialog;
|
||||
QPointer<QLabel> _path;
|
||||
QPointer<QLabel> _size;
|
||||
QPointer<QLabel> _maxSize;
|
||||
QPointer<QTimer> _refreshTimer;
|
||||
};
|
||||
|
||||
#endif // hifi_DiskCacheEditor_h
|
111
interface/src/ui/ScriptEditBox.cpp
Normal file
|
@ -0,0 +1,111 @@
|
|||
//
|
||||
// ScriptEditBox.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Thijs Wenker on 4/30/14.
|
||||
// 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
|
||||
//
|
||||
|
||||
#include "ScriptEditBox.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QTextBlock>
|
||||
|
||||
#include "ScriptLineNumberArea.h"
|
||||
|
||||
ScriptEditBox::ScriptEditBox(QWidget* parent) :
|
||||
QPlainTextEdit(parent)
|
||||
{
|
||||
_scriptLineNumberArea = new ScriptLineNumberArea(this);
|
||||
|
||||
connect(this, &ScriptEditBox::blockCountChanged, this, &ScriptEditBox::updateLineNumberAreaWidth);
|
||||
connect(this, &ScriptEditBox::updateRequest, this, &ScriptEditBox::updateLineNumberArea);
|
||||
connect(this, &ScriptEditBox::cursorPositionChanged, this, &ScriptEditBox::highlightCurrentLine);
|
||||
|
||||
updateLineNumberAreaWidth(0);
|
||||
highlightCurrentLine();
|
||||
}
|
||||
|
||||
int ScriptEditBox::lineNumberAreaWidth() {
|
||||
int digits = 1;
|
||||
const int SPACER_PIXELS = 3;
|
||||
const int BASE_TEN = 10;
|
||||
int max = qMax(1, blockCount());
|
||||
while (max >= BASE_TEN) {
|
||||
max /= BASE_TEN;
|
||||
digits++;
|
||||
}
|
||||
return SPACER_PIXELS + fontMetrics().width(QLatin1Char('H')) * digits;
|
||||
}
|
||||
|
||||
void ScriptEditBox::updateLineNumberAreaWidth(int blockCount) {
|
||||
setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
|
||||
}
|
||||
|
||||
void ScriptEditBox::updateLineNumberArea(const QRect& rect, int deltaY) {
|
||||
if (deltaY) {
|
||||
_scriptLineNumberArea->scroll(0, deltaY);
|
||||
} else {
|
||||
_scriptLineNumberArea->update(0, rect.y(), _scriptLineNumberArea->width(), rect.height());
|
||||
}
|
||||
|
||||
if (rect.contains(viewport()->rect())) {
|
||||
updateLineNumberAreaWidth(0);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEditBox::resizeEvent(QResizeEvent* event) {
|
||||
QPlainTextEdit::resizeEvent(event);
|
||||
|
||||
QRect localContentsRect = contentsRect();
|
||||
_scriptLineNumberArea->setGeometry(QRect(localContentsRect.left(), localContentsRect.top(), lineNumberAreaWidth(),
|
||||
localContentsRect.height()));
|
||||
}
|
||||
|
||||
void ScriptEditBox::highlightCurrentLine() {
|
||||
QList<QTextEdit::ExtraSelection> extraSelections;
|
||||
|
||||
if (!isReadOnly()) {
|
||||
QTextEdit::ExtraSelection selection;
|
||||
|
||||
QColor lineColor = QColor(Qt::gray).lighter();
|
||||
|
||||
selection.format.setBackground(lineColor);
|
||||
selection.format.setProperty(QTextFormat::FullWidthSelection, true);
|
||||
selection.cursor = textCursor();
|
||||
selection.cursor.clearSelection();
|
||||
extraSelections.append(selection);
|
||||
}
|
||||
|
||||
setExtraSelections(extraSelections);
|
||||
}
|
||||
|
||||
void ScriptEditBox::lineNumberAreaPaintEvent(QPaintEvent* event)
|
||||
{
|
||||
QPainter painter(_scriptLineNumberArea);
|
||||
painter.fillRect(event->rect(), Qt::lightGray);
|
||||
QTextBlock block = firstVisibleBlock();
|
||||
int blockNumber = block.blockNumber();
|
||||
int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
|
||||
int bottom = top + (int) blockBoundingRect(block).height();
|
||||
|
||||
while (block.isValid() && top <= event->rect().bottom()) {
|
||||
if (block.isVisible() && bottom >= event->rect().top()) {
|
||||
QFont font = painter.font();
|
||||
font.setBold(this->textCursor().blockNumber() == block.blockNumber());
|
||||
painter.setFont(font);
|
||||
QString number = QString::number(blockNumber + 1);
|
||||
painter.setPen(Qt::black);
|
||||
painter.drawText(0, top, _scriptLineNumberArea->width(), fontMetrics().height(),
|
||||
Qt::AlignRight, number);
|
||||
}
|
||||
|
||||
block = block.next();
|
||||
top = bottom;
|
||||
bottom = top + (int) blockBoundingRect(block).height();
|
||||
blockNumber++;
|
||||
}
|
||||
}
|
38
interface/src/ui/ScriptEditBox.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// ScriptEditBox.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Thijs Wenker on 4/30/14.
|
||||
// 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
|
||||
//
|
||||
|
||||
#ifndef hifi_ScriptEditBox_h
|
||||
#define hifi_ScriptEditBox_h
|
||||
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
class ScriptEditBox : public QPlainTextEdit {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ScriptEditBox(QWidget* parent = NULL);
|
||||
|
||||
void lineNumberAreaPaintEvent(QPaintEvent* event);
|
||||
int lineNumberAreaWidth();
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
|
||||
private slots:
|
||||
void updateLineNumberAreaWidth(int blockCount);
|
||||
void highlightCurrentLine();
|
||||
void updateLineNumberArea(const QRect& rect, int deltaY);
|
||||
|
||||
private:
|
||||
QWidget* _scriptLineNumberArea;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptEditBox_h
|
256
interface/src/ui/ScriptEditorWidget.cpp
Normal file
|
@ -0,0 +1,256 @@
|
|||
//
|
||||
// ScriptEditorWidget.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Thijs Wenker on 4/14/14.
|
||||
// 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
|
||||
//
|
||||
|
||||
#include "ui_scriptEditorWidget.h"
|
||||
#include "ScriptEditorWidget.h"
|
||||
#include "ScriptEditorWindow.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QFileDialog>
|
||||
#include <QFrame>
|
||||
#include <QLayoutItem>
|
||||
#include <QMainWindow>
|
||||
#include <QMessageBox>
|
||||
#include <QPalette>
|
||||
#include <QScrollBar>
|
||||
#include <QSizePolicy>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
#include <ScriptEngines.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <OffscreenUi.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "ScriptHighlighting.h"
|
||||
|
||||
ScriptEditorWidget::ScriptEditorWidget() :
|
||||
_scriptEditorWidgetUI(new Ui::ScriptEditorWidget),
|
||||
_scriptEngine(NULL),
|
||||
_isRestarting(false),
|
||||
_isReloading(false)
|
||||
{
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
_scriptEditorWidgetUI->setupUi(this);
|
||||
|
||||
connect(_scriptEditorWidgetUI->scriptEdit->document(), &QTextDocument::modificationChanged, this,
|
||||
&ScriptEditorWidget::scriptModified);
|
||||
connect(_scriptEditorWidgetUI->scriptEdit->document(), &QTextDocument::contentsChanged, this,
|
||||
&ScriptEditorWidget::onScriptModified);
|
||||
|
||||
// remove the title bar (see the Qt docs on setTitleBarWidget)
|
||||
setTitleBarWidget(new QWidget());
|
||||
QFontMetrics fm(_scriptEditorWidgetUI->scriptEdit->font());
|
||||
_scriptEditorWidgetUI->scriptEdit->setTabStopWidth(fm.width('0') * 4);
|
||||
// We create a new ScriptHighligting QObject and provide it with a parent so this is NOT a memory leak.
|
||||
new ScriptHighlighting(_scriptEditorWidgetUI->scriptEdit->document());
|
||||
QTimer::singleShot(0, _scriptEditorWidgetUI->scriptEdit, SLOT(setFocus()));
|
||||
|
||||
_console = new JSConsole(this);
|
||||
_console->setFixedHeight(CONSOLE_HEIGHT);
|
||||
_scriptEditorWidgetUI->verticalLayout->addWidget(_console);
|
||||
connect(_scriptEditorWidgetUI->clearButton, &QPushButton::clicked, _console, &JSConsole::clear);
|
||||
}
|
||||
|
||||
ScriptEditorWidget::~ScriptEditorWidget() {
|
||||
delete _scriptEditorWidgetUI;
|
||||
delete _console;
|
||||
}
|
||||
|
||||
void ScriptEditorWidget::onScriptModified() {
|
||||
if(_scriptEditorWidgetUI->onTheFlyCheckBox->isChecked() && isModified() && isRunning() && !_isReloading) {
|
||||
_isRestarting = true;
|
||||
setRunning(false);
|
||||
// Script is restarted once current script instance finishes.
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEditorWidget::onScriptFinished(const QString& scriptPath) {
|
||||
_scriptEngine = NULL;
|
||||
_console->setScriptEngine(NULL);
|
||||
if (_isRestarting) {
|
||||
_isRestarting = false;
|
||||
setRunning(true);
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptEditorWidget::isModified() {
|
||||
return _scriptEditorWidgetUI->scriptEdit->document()->isModified();
|
||||
}
|
||||
|
||||
bool ScriptEditorWidget::isRunning() {
|
||||
return (_scriptEngine != NULL) ? _scriptEngine->isRunning() : false;
|
||||
}
|
||||
|
||||
bool ScriptEditorWidget::setRunning(bool run) {
|
||||
if (run && isModified() && !save()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_scriptEngine != NULL) {
|
||||
disconnect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged);
|
||||
disconnect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified);
|
||||
disconnect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished);
|
||||
}
|
||||
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
if (run) {
|
||||
const QString& scriptURLString = QUrl(_currentScript).toString();
|
||||
// Reload script so that an out of date copy is not retrieved from the cache
|
||||
_scriptEngine = scriptEngines->loadScript(scriptURLString, true, true, false, true);
|
||||
connect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged);
|
||||
connect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified);
|
||||
connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished);
|
||||
} else {
|
||||
connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished);
|
||||
const QString& scriptURLString = QUrl(_currentScript).toString();
|
||||
scriptEngines->stopScript(scriptURLString);
|
||||
_scriptEngine = NULL;
|
||||
}
|
||||
_console->setScriptEngine(_scriptEngine);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScriptEditorWidget::saveFile(const QString &scriptPath) {
|
||||
QFile file(scriptPath);
|
||||
if (!file.open(QFile::WriteOnly | QFile::Text)) {
|
||||
OffscreenUi::warning(this, tr("Interface"), tr("Cannot write script %1:\n%2.").arg(scriptPath)
|
||||
.arg(file.errorString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
QTextStream out(&file);
|
||||
out << _scriptEditorWidgetUI->scriptEdit->toPlainText();
|
||||
file.close();
|
||||
|
||||
setScriptFile(scriptPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptEditorWidget::loadFile(const QString& scriptPath) {
|
||||
QUrl url(scriptPath);
|
||||
|
||||
// if the scheme length is one or lower, maybe they typed in a file, let's try
|
||||
const int WINDOWS_DRIVE_LETTER_SIZE = 1;
|
||||
if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) {
|
||||
QFile file(scriptPath);
|
||||
if (!file.open(QFile::ReadOnly | QFile::Text)) {
|
||||
OffscreenUi::warning(this, tr("Interface"), tr("Cannot read script %1:\n%2.").arg(scriptPath)
|
||||
.arg(file.errorString()));
|
||||
return;
|
||||
}
|
||||
QTextStream in(&file);
|
||||
_scriptEditorWidgetUI->scriptEdit->setPlainText(in.readAll());
|
||||
file.close();
|
||||
setScriptFile(scriptPath);
|
||||
|
||||
if (_scriptEngine != NULL) {
|
||||
disconnect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged);
|
||||
disconnect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified);
|
||||
disconnect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished);
|
||||
}
|
||||
} else {
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest = QNetworkRequest(url);
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
QNetworkReply* reply = networkAccessManager.get(networkRequest);
|
||||
qDebug() << "Downloading included script at" << scriptPath;
|
||||
QEventLoop loop;
|
||||
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||
loop.exec();
|
||||
_scriptEditorWidgetUI->scriptEdit->setPlainText(reply->readAll());
|
||||
delete reply;
|
||||
|
||||
if (!saveAs()) {
|
||||
static_cast<ScriptEditorWindow*>(this->parent()->parent()->parent())->terminateCurrentTab();
|
||||
}
|
||||
}
|
||||
const QString& scriptURLString = QUrl(_currentScript).toString();
|
||||
_scriptEngine = DependencyManager::get<ScriptEngines>()->getScriptEngine(scriptURLString);
|
||||
if (_scriptEngine != NULL) {
|
||||
connect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged);
|
||||
connect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified);
|
||||
connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished);
|
||||
}
|
||||
_console->setScriptEngine(_scriptEngine);
|
||||
}
|
||||
|
||||
bool ScriptEditorWidget::save() {
|
||||
return _currentScript.isEmpty() ? saveAs() : saveFile(_currentScript);
|
||||
}
|
||||
|
||||
bool ScriptEditorWidget::saveAs() {
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
QString fileName = QFileDialog::getSaveFileName(this, tr("Save script"),
|
||||
qApp->getPreviousScriptLocation(),
|
||||
tr("JavaScript Files (*.js)"));
|
||||
if (!fileName.isEmpty()) {
|
||||
qApp->setPreviousScriptLocation(fileName);
|
||||
return saveFile(fileName);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEditorWidget::setScriptFile(const QString& scriptPath) {
|
||||
_currentScript = scriptPath;
|
||||
_currentScriptModified = QFileInfo(_currentScript).lastModified();
|
||||
_scriptEditorWidgetUI->scriptEdit->document()->setModified(false);
|
||||
setWindowModified(false);
|
||||
|
||||
emit scriptnameChanged();
|
||||
}
|
||||
|
||||
bool ScriptEditorWidget::questionSave() {
|
||||
if (_scriptEditorWidgetUI->scriptEdit->document()->isModified()) {
|
||||
QMessageBox::StandardButton button = OffscreenUi::warning(this, tr("Interface"),
|
||||
tr("The script has been modified.\nDo you want to save your changes?"), QMessageBox::Save | QMessageBox::Discard |
|
||||
QMessageBox::Cancel, QMessageBox::Save);
|
||||
return button == QMessageBox::Save ? save() : (button == QMessageBox::Discard);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptEditorWidget::onWindowActivated() {
|
||||
if (!_isReloading) {
|
||||
_isReloading = true;
|
||||
|
||||
QDateTime fileStamp = QFileInfo(_currentScript).lastModified();
|
||||
if (fileStamp > _currentScriptModified) {
|
||||
bool doReload = false;
|
||||
auto window = static_cast<ScriptEditorWindow*>(this->parent()->parent()->parent());
|
||||
window->inModalDialog = true;
|
||||
if (window->autoReloadScripts()
|
||||
|| OffscreenUi::question(this, tr("Reload Script"),
|
||||
tr("The following file has been modified outside of the Interface editor:") + "\n" + _currentScript + "\n"
|
||||
+ (isModified()
|
||||
? tr("Do you want to reload it and lose the changes you've made in the Interface editor?")
|
||||
: tr("Do you want to reload it?")),
|
||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
doReload = true;
|
||||
}
|
||||
window->inModalDialog = false;
|
||||
if (doReload) {
|
||||
loadFile(_currentScript);
|
||||
if (_scriptEditorWidgetUI->onTheFlyCheckBox->isChecked() && isRunning()) {
|
||||
_isRestarting = true;
|
||||
setRunning(false);
|
||||
// Script is restarted once current script instance finishes.
|
||||
}
|
||||
} else {
|
||||
_currentScriptModified = fileStamp; // Asked and answered. Don't ask again until the external file is changed again.
|
||||
}
|
||||
}
|
||||
_isReloading = false;
|
||||
}
|
||||
}
|
64
interface/src/ui/ScriptEditorWidget.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
// ScriptEditorWidget.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Thijs Wenker on 4/14/14.
|
||||
// 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
|
||||
//
|
||||
|
||||
#ifndef hifi_ScriptEditorWidget_h
|
||||
#define hifi_ScriptEditorWidget_h
|
||||
|
||||
#include <QDockWidget>
|
||||
|
||||
#include "JSConsole.h"
|
||||
#include "ScriptEngine.h"
|
||||
|
||||
namespace Ui {
|
||||
class ScriptEditorWidget;
|
||||
}
|
||||
|
||||
class ScriptEditorWidget : public QDockWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ScriptEditorWidget();
|
||||
~ScriptEditorWidget();
|
||||
|
||||
bool isModified();
|
||||
bool isRunning();
|
||||
bool setRunning(bool run);
|
||||
bool saveFile(const QString& scriptPath);
|
||||
void loadFile(const QString& scriptPath);
|
||||
void setScriptFile(const QString& scriptPath);
|
||||
bool save();
|
||||
bool saveAs();
|
||||
bool questionSave();
|
||||
const QString getScriptName() const { return _currentScript; };
|
||||
|
||||
signals:
|
||||
void runningStateChanged();
|
||||
void scriptnameChanged();
|
||||
void scriptModified();
|
||||
|
||||
public slots:
|
||||
void onWindowActivated();
|
||||
|
||||
private slots:
|
||||
void onScriptModified();
|
||||
void onScriptFinished(const QString& scriptName);
|
||||
|
||||
private:
|
||||
JSConsole* _console;
|
||||
Ui::ScriptEditorWidget* _scriptEditorWidgetUI;
|
||||
ScriptEngine* _scriptEngine;
|
||||
QString _currentScript;
|
||||
QDateTime _currentScriptModified;
|
||||
bool _isRestarting;
|
||||
bool _isReloading;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptEditorWidget_h
|
259
interface/src/ui/ScriptEditorWindow.cpp
Normal file
|
@ -0,0 +1,259 @@
|
|||
//
|
||||
// ScriptEditorWindow.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Thijs Wenker on 4/14/14.
|
||||
// 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
|
||||
//
|
||||
|
||||
#include <QFileDialog>
|
||||
|
||||
#include "ui_scriptEditorWindow.h"
|
||||
#include "ScriptEditorWindow.h"
|
||||
#include "ScriptEditorWidget.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QtCore/QSignalMapper>
|
||||
#include <QFrame>
|
||||
#include <QLayoutItem>
|
||||
#include <QMainWindow>
|
||||
#include <QMessageBox>
|
||||
#include <QPalette>
|
||||
#include <QScrollBar>
|
||||
#include <QShortcut>
|
||||
#include <QSizePolicy>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
#include <ScriptEngines.h>
|
||||
#include "Application.h"
|
||||
#include "PathUtils.h"
|
||||
|
||||
ScriptEditorWindow::ScriptEditorWindow(QWidget* parent) :
|
||||
QWidget(parent),
|
||||
_ScriptEditorWindowUI(new Ui::ScriptEditorWindow),
|
||||
_loadMenu(new QMenu),
|
||||
_saveMenu(new QMenu)
|
||||
{
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
_ScriptEditorWindowUI->setupUi(this);
|
||||
|
||||
this->setWindowFlags(Qt::Tool);
|
||||
addScriptEditorWidget("New script");
|
||||
connect(_loadMenu, &QMenu::aboutToShow, this, &ScriptEditorWindow::loadMenuAboutToShow);
|
||||
_ScriptEditorWindowUI->loadButton->setMenu(_loadMenu);
|
||||
|
||||
_saveMenu->addAction("Save as..", this, SLOT(saveScriptAsClicked()), Qt::CTRL | Qt::SHIFT | Qt::Key_S);
|
||||
|
||||
_ScriptEditorWindowUI->saveButton->setMenu(_saveMenu);
|
||||
|
||||
connect(new QShortcut(QKeySequence("Ctrl+N"), this), &QShortcut::activated, this, &ScriptEditorWindow::newScriptClicked);
|
||||
connect(new QShortcut(QKeySequence("Ctrl+S"), this), &QShortcut::activated, this,&ScriptEditorWindow::saveScriptClicked);
|
||||
connect(new QShortcut(QKeySequence("Ctrl+O"), this), &QShortcut::activated, this, &ScriptEditorWindow::loadScriptClicked);
|
||||
connect(new QShortcut(QKeySequence("F5"), this), &QShortcut::activated, this, &ScriptEditorWindow::toggleRunScriptClicked);
|
||||
|
||||
_ScriptEditorWindowUI->loadButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/load-script.svg")));
|
||||
_ScriptEditorWindowUI->newButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/new-script.svg")));
|
||||
_ScriptEditorWindowUI->saveButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/save-script.svg")));
|
||||
_ScriptEditorWindowUI->toggleRunButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/start-script.svg")));
|
||||
}
|
||||
|
||||
ScriptEditorWindow::~ScriptEditorWindow() {
|
||||
delete _ScriptEditorWindowUI;
|
||||
}
|
||||
|
||||
void ScriptEditorWindow::setRunningState(bool run) {
|
||||
if (_ScriptEditorWindowUI->tabWidget->currentIndex() != -1) {
|
||||
static_cast<ScriptEditorWidget*>(_ScriptEditorWindowUI->tabWidget->currentWidget())->setRunning(run);
|
||||
}
|
||||
this->updateButtons();
|
||||
}
|
||||
|
||||
void ScriptEditorWindow::updateButtons() {
|
||||
bool isRunning = _ScriptEditorWindowUI->tabWidget->currentIndex() != -1 &&
|
||||
static_cast<ScriptEditorWidget*>(_ScriptEditorWindowUI->tabWidget->currentWidget())->isRunning();
|
||||
_ScriptEditorWindowUI->toggleRunButton->setEnabled(_ScriptEditorWindowUI->tabWidget->currentIndex() != -1);
|
||||
_ScriptEditorWindowUI->toggleRunButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + ((isRunning ?
|
||||
"icons/stop-script.svg" : "icons/start-script.svg")))));
|
||||
}
|
||||
|
||||
void ScriptEditorWindow::loadScriptMenu(const QString& scriptName) {
|
||||
addScriptEditorWidget("loading...")->loadFile(scriptName);
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
void ScriptEditorWindow::loadScriptClicked() {
|
||||
QString scriptName = QFileDialog::getOpenFileName(this, tr("Interface"),
|
||||
qApp->getPreviousScriptLocation(),
|
||||
tr("JavaScript Files (*.js)"));
|
||||
if (!scriptName.isEmpty()) {
|
||||
qApp->setPreviousScriptLocation(scriptName);
|
||||
addScriptEditorWidget("loading...")->loadFile(scriptName);
|
||||
updateButtons();
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEditorWindow::loadMenuAboutToShow() {
|
||||
_loadMenu->clear();
|
||||
QStringList runningScripts = DependencyManager::get<ScriptEngines>()->getRunningScripts();
|
||||
if (runningScripts.count() > 0) {
|
||||
QSignalMapper* signalMapper = new QSignalMapper(this);
|
||||
foreach (const QString& runningScript, runningScripts) {
|
||||
QAction* runningScriptAction = new QAction(runningScript, _loadMenu);
|
||||
connect(runningScriptAction, SIGNAL(triggered()), signalMapper, SLOT(map()));
|
||||
signalMapper->setMapping(runningScriptAction, runningScript);
|
||||
_loadMenu->addAction(runningScriptAction);
|
||||
}
|
||||
connect(signalMapper, SIGNAL(mapped(const QString &)), this, SLOT(loadScriptMenu(const QString&)));
|
||||
} else {
|
||||
QAction* naAction = new QAction("(no running scripts)", _loadMenu);
|
||||
naAction->setDisabled(true);
|
||||
_loadMenu->addAction(naAction);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEditorWindow::newScriptClicked() {
|
||||
addScriptEditorWidget(QString("New script"));
|
||||
}
|
||||
|
||||
void ScriptEditorWindow::toggleRunScriptClicked() {
|
||||
this->setRunningState(!(_ScriptEditorWindowUI->tabWidget->currentIndex() !=-1
|
||||
&& static_cast<ScriptEditorWidget*>(_ScriptEditorWindowUI->tabWidget->currentWidget())->isRunning()));
|
||||
}
|
||||
|
||||
void ScriptEditorWindow::saveScriptClicked() {
|
||||
if (_ScriptEditorWindowUI->tabWidget->currentIndex() != -1) {
|
||||
ScriptEditorWidget* currentScriptWidget = static_cast<ScriptEditorWidget*>(_ScriptEditorWindowUI->tabWidget
|
||||
->currentWidget());
|
||||
currentScriptWidget->save();
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEditorWindow::saveScriptAsClicked() {
|
||||
if (_ScriptEditorWindowUI->tabWidget->currentIndex() != -1) {
|
||||
ScriptEditorWidget* currentScriptWidget = static_cast<ScriptEditorWidget*>(_ScriptEditorWindowUI->tabWidget
|
||||
->currentWidget());
|
||||
currentScriptWidget->saveAs();
|
||||
}
|
||||
}
|
||||
|
||||
ScriptEditorWidget* ScriptEditorWindow::addScriptEditorWidget(QString title) {
|
||||
ScriptEditorWidget* newScriptEditorWidget = new ScriptEditorWidget();
|
||||
connect(newScriptEditorWidget, &ScriptEditorWidget::scriptnameChanged, this, &ScriptEditorWindow::updateScriptNameOrStatus);
|
||||
connect(newScriptEditorWidget, &ScriptEditorWidget::scriptModified, this, &ScriptEditorWindow::updateScriptNameOrStatus);
|
||||
connect(newScriptEditorWidget, &ScriptEditorWidget::runningStateChanged, this, &ScriptEditorWindow::updateButtons);
|
||||
connect(this, &ScriptEditorWindow::windowActivated, newScriptEditorWidget, &ScriptEditorWidget::onWindowActivated);
|
||||
_ScriptEditorWindowUI->tabWidget->addTab(newScriptEditorWidget, title);
|
||||
_ScriptEditorWindowUI->tabWidget->setCurrentWidget(newScriptEditorWidget);
|
||||
newScriptEditorWidget->setUpdatesEnabled(true);
|
||||
newScriptEditorWidget->adjustSize();
|
||||
return newScriptEditorWidget;
|
||||
}
|
||||
|
||||
void ScriptEditorWindow::tabSwitched(int tabIndex) {
|
||||
this->updateButtons();
|
||||
if (_ScriptEditorWindowUI->tabWidget->currentIndex() != -1) {
|
||||
ScriptEditorWidget* currentScriptWidget = static_cast<ScriptEditorWidget*>(_ScriptEditorWindowUI->tabWidget
|
||||
->currentWidget());
|
||||
QString modifiedStar = (currentScriptWidget->isModified() ? "*" : "");
|
||||
if (currentScriptWidget->getScriptName().length() > 0) {
|
||||
this->setWindowTitle("Script Editor [" + currentScriptWidget->getScriptName() + modifiedStar + "]");
|
||||
} else {
|
||||
this->setWindowTitle("Script Editor [New script" + modifiedStar + "]");
|
||||
}
|
||||
} else {
|
||||
this->setWindowTitle("Script Editor");
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEditorWindow::tabCloseRequested(int tabIndex) {
|
||||
if (ignoreCloseForModal(nullptr)) {
|
||||
return;
|
||||
}
|
||||
ScriptEditorWidget* closingScriptWidget = static_cast<ScriptEditorWidget*>(_ScriptEditorWindowUI->tabWidget
|
||||
->widget(tabIndex));
|
||||
if(closingScriptWidget->questionSave()) {
|
||||
_ScriptEditorWindowUI->tabWidget->removeTab(tabIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// If this operating system window causes a qml overlay modal dialog (which might not even be seen by the user), closing this window
|
||||
// will crash the code that was waiting on the dialog result. So that code whousl set inModalDialog to true while the question is up.
|
||||
// This code will not be necessary when switch out all operating system windows for qml overlays.
|
||||
bool ScriptEditorWindow::ignoreCloseForModal(QCloseEvent* event) {
|
||||
if (!inModalDialog) {
|
||||
return false;
|
||||
}
|
||||
// Deliberately not using OffscreenUi, so that the dialog is seen.
|
||||
QMessageBox::information(this, tr("Interface"), tr("There is a modal dialog that must be answered before closing."),
|
||||
QMessageBox::Discard, QMessageBox::Discard);
|
||||
if (event) {
|
||||
event->ignore(); // don't close
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptEditorWindow::closeEvent(QCloseEvent *event) {
|
||||
if (ignoreCloseForModal(event)) {
|
||||
return;
|
||||
}
|
||||
bool unsaved_docs_warning = false;
|
||||
for (int i = 0; i < _ScriptEditorWindowUI->tabWidget->count(); i++){
|
||||
if(static_cast<ScriptEditorWidget*>(_ScriptEditorWindowUI->tabWidget->widget(i))->isModified()){
|
||||
unsaved_docs_warning = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!unsaved_docs_warning || QMessageBox::warning(this, tr("Interface"),
|
||||
tr("There are some unsaved scripts, are you sure you want to close the editor? Changes will be lost!"),
|
||||
QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Cancel) == QMessageBox::Discard) {
|
||||
event->accept();
|
||||
} else {
|
||||
event->ignore();
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEditorWindow::updateScriptNameOrStatus() {
|
||||
ScriptEditorWidget* source = static_cast<ScriptEditorWidget*>(QObject::sender());
|
||||
QString modifiedStar = (source->isModified()? "*" : "");
|
||||
if (source->getScriptName().length() > 0) {
|
||||
for (int i = 0; i < _ScriptEditorWindowUI->tabWidget->count(); i++){
|
||||
if (_ScriptEditorWindowUI->tabWidget->widget(i) == source) {
|
||||
_ScriptEditorWindowUI->tabWidget->setTabText(i, modifiedStar + QFileInfo(source->getScriptName()).fileName());
|
||||
_ScriptEditorWindowUI->tabWidget->setTabToolTip(i, source->getScriptName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_ScriptEditorWindowUI->tabWidget->currentWidget() == source) {
|
||||
if (source->getScriptName().length() > 0) {
|
||||
this->setWindowTitle("Script Editor [" + source->getScriptName() + modifiedStar + "]");
|
||||
} else {
|
||||
this->setWindowTitle("Script Editor [New script" + modifiedStar + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEditorWindow::terminateCurrentTab() {
|
||||
if (_ScriptEditorWindowUI->tabWidget->currentIndex() != -1) {
|
||||
_ScriptEditorWindowUI->tabWidget->removeTab(_ScriptEditorWindowUI->tabWidget->currentIndex());
|
||||
this->raise();
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptEditorWindow::autoReloadScripts() {
|
||||
return _ScriptEditorWindowUI->autoReloadCheckBox->isChecked();
|
||||
}
|
||||
|
||||
bool ScriptEditorWindow::event(QEvent* event) {
|
||||
if (event->type() == QEvent::WindowActivate) {
|
||||
emit windowActivated();
|
||||
}
|
||||
return QWidget::event(event);
|
||||
}
|
||||
|
64
interface/src/ui/ScriptEditorWindow.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
// ScriptEditorWindow.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Thijs Wenker on 4/14/14.
|
||||
// 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
|
||||
//
|
||||
|
||||
#ifndef hifi_ScriptEditorWindow_h
|
||||
#define hifi_ScriptEditorWindow_h
|
||||
|
||||
#include "ScriptEditorWidget.h"
|
||||
|
||||
namespace Ui {
|
||||
class ScriptEditorWindow;
|
||||
}
|
||||
|
||||
class ScriptEditorWindow : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ScriptEditorWindow(QWidget* parent = nullptr);
|
||||
~ScriptEditorWindow();
|
||||
|
||||
void terminateCurrentTab();
|
||||
bool autoReloadScripts();
|
||||
|
||||
bool inModalDialog { false };
|
||||
bool ignoreCloseForModal(QCloseEvent* event);
|
||||
|
||||
signals:
|
||||
void windowActivated();
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent* event) override;
|
||||
virtual bool event(QEvent* event) override;
|
||||
|
||||
private:
|
||||
Ui::ScriptEditorWindow* _ScriptEditorWindowUI;
|
||||
QMenu* _loadMenu;
|
||||
QMenu* _saveMenu;
|
||||
|
||||
ScriptEditorWidget* addScriptEditorWidget(QString title);
|
||||
void setRunningState(bool run);
|
||||
void setScriptName(const QString& scriptName);
|
||||
|
||||
private slots:
|
||||
void loadScriptMenu(const QString& scriptName);
|
||||
void loadScriptClicked();
|
||||
void newScriptClicked();
|
||||
void toggleRunScriptClicked();
|
||||
void saveScriptClicked();
|
||||
void saveScriptAsClicked();
|
||||
void loadMenuAboutToShow();
|
||||
void tabSwitched(int tabIndex);
|
||||
void tabCloseRequested(int tabIndex);
|
||||
void updateScriptNameOrStatus();
|
||||
void updateButtons();
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptEditorWindow_h
|
28
interface/src/ui/ScriptLineNumberArea.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// ScriptLineNumberArea.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Thijs Wenker on 4/30/14.
|
||||
// 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
|
||||
//
|
||||
|
||||
#include "ScriptLineNumberArea.h"
|
||||
|
||||
#include "ScriptEditBox.h"
|
||||
|
||||
ScriptLineNumberArea::ScriptLineNumberArea(ScriptEditBox* scriptEditBox) :
|
||||
QWidget(scriptEditBox)
|
||||
{
|
||||
_scriptEditBox = scriptEditBox;
|
||||
}
|
||||
|
||||
QSize ScriptLineNumberArea::sizeHint() const {
|
||||
return QSize(_scriptEditBox->lineNumberAreaWidth(), 0);
|
||||
}
|
||||
|
||||
void ScriptLineNumberArea::paintEvent(QPaintEvent* event) {
|
||||
_scriptEditBox->lineNumberAreaPaintEvent(event);
|
||||
}
|
32
interface/src/ui/ScriptLineNumberArea.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// ScriptLineNumberArea.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Thijs Wenker on 4/30/14.
|
||||
// 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
|
||||
//
|
||||
|
||||
#ifndef hifi_ScriptLineNumberArea_h
|
||||
#define hifi_ScriptLineNumberArea_h
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class ScriptEditBox;
|
||||
|
||||
class ScriptLineNumberArea : public QWidget {
|
||||
|
||||
public:
|
||||
ScriptLineNumberArea(ScriptEditBox* scriptEditBox);
|
||||
QSize sizeHint() const override;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
|
||||
private:
|
||||
ScriptEditBox* _scriptEditBox;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptLineNumberArea_h
|
49
interface/src/ui/ScriptsTableWidget.cpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
//
|
||||
// ScriptsTableWidget.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Mohammed Nafees on 04/03/2014.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QDebug>
|
||||
#include <QHeaderView>
|
||||
#include <QKeyEvent>
|
||||
#include <QPainter>
|
||||
|
||||
#include "ScriptsTableWidget.h"
|
||||
|
||||
ScriptsTableWidget::ScriptsTableWidget(QWidget* parent) :
|
||||
QTableWidget(parent) {
|
||||
verticalHeader()->setVisible(false);
|
||||
horizontalHeader()->setVisible(false);
|
||||
setShowGrid(false);
|
||||
setSelectionMode(QAbstractItemView::NoSelection);
|
||||
setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
setStyleSheet("QTableWidget { border: none; background: transparent; color: #333333; } QToolTip { color: #000000; background: #f9f6e4; padding: 2px; }");
|
||||
setToolTipDuration(200);
|
||||
setWordWrap(true);
|
||||
setGeometry(0, 0, parent->width(), parent->height());
|
||||
}
|
||||
|
||||
void ScriptsTableWidget::paintEvent(QPaintEvent* event) {
|
||||
QPainter painter(viewport());
|
||||
painter.setPen(QColor::fromRgb(225, 225, 225)); // #e1e1e1
|
||||
|
||||
int y = 0;
|
||||
for (int i = 0; i < rowCount(); i++) {
|
||||
painter.drawLine(5, rowHeight(i) + y, width(), rowHeight(i) + y);
|
||||
y += rowHeight(i);
|
||||
}
|
||||
painter.end();
|
||||
|
||||
QTableWidget::paintEvent(event);
|
||||
}
|
||||
|
||||
void ScriptsTableWidget::keyPressEvent(QKeyEvent* event) {
|
||||
// Ignore keys so they will propagate correctly
|
||||
event->ignore();
|
||||
}
|
28
interface/src/ui/ScriptsTableWidget.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// ScriptsTableWidget.h
|
||||
// interface
|
||||
//
|
||||
// Created by Mohammed Nafees on 04/03/2014.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// 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__ScriptsTableWidget_h
|
||||
#define hifi__ScriptsTableWidget_h
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTableWidget>
|
||||
|
||||
class ScriptsTableWidget : public QTableWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ScriptsTableWidget(QWidget* parent);
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent* event) override;
|
||||
virtual void keyPressEvent(QKeyEvent* event) override;
|
||||
};
|
||||
|
||||
#endif // hifi__ScriptsTableWidget_h
|
|
@ -38,8 +38,6 @@ using namespace std;
|
|||
|
||||
static Stats* INSTANCE{ nullptr };
|
||||
|
||||
QString getTextureMemoryPressureModeString();
|
||||
|
||||
Stats* Stats::getInstance() {
|
||||
if (!INSTANCE) {
|
||||
Stats::registerType();
|
||||
|
@ -222,10 +220,10 @@ void Stats::updateStats(bool force) {
|
|||
STAT_UPDATE(audioMixerInPps, roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer)));
|
||||
STAT_UPDATE(audioMixerOutKbps, roundf(bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer)));
|
||||
STAT_UPDATE(audioMixerOutPps, roundf(bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer)));
|
||||
STAT_UPDATE(audioMicOutboundPPS, audioClient->getMicAudioOutboundPPS());
|
||||
STAT_UPDATE(audioSilentOutboundPPS, audioClient->getSilentOutboundPPS());
|
||||
STAT_UPDATE(audioAudioInboundPPS, audioClient->getAudioInboundPPS());
|
||||
STAT_UPDATE(audioSilentInboundPPS, audioClient->getSilentInboundPPS());
|
||||
STAT_UPDATE(audioOutboundPPS, audioClient->getAudioOutboundPPS());
|
||||
STAT_UPDATE(audioSilentOutboundPPS, audioClient->getSilentOutboundPPS());
|
||||
} else {
|
||||
STAT_UPDATE(audioMixerKbps, -1);
|
||||
STAT_UPDATE(audioMixerPps, -1);
|
||||
|
@ -233,7 +231,7 @@ void Stats::updateStats(bool force) {
|
|||
STAT_UPDATE(audioMixerInPps, -1);
|
||||
STAT_UPDATE(audioMixerOutKbps, -1);
|
||||
STAT_UPDATE(audioMixerOutPps, -1);
|
||||
STAT_UPDATE(audioOutboundPPS, -1);
|
||||
STAT_UPDATE(audioMicOutboundPPS, -1);
|
||||
STAT_UPDATE(audioSilentOutboundPPS, -1);
|
||||
STAT_UPDATE(audioAudioInboundPPS, -1);
|
||||
STAT_UPDATE(audioSilentInboundPPS, -1);
|
||||
|
@ -342,12 +340,10 @@ void Stats::updateStats(bool force) {
|
|||
STAT_UPDATE(glContextSwapchainMemory, (int)BYTES_TO_MB(gl::Context::getSwapchainMemoryUsage()));
|
||||
|
||||
STAT_UPDATE(qmlTextureMemory, (int)BYTES_TO_MB(OffscreenQmlSurface::getUsedTextureMemory()));
|
||||
STAT_UPDATE(texturePendingTransfers, (int)BYTES_TO_MB(gpu::Texture::getTextureTransferPendingSize()));
|
||||
STAT_UPDATE(gpuTextureMemory, (int)BYTES_TO_MB(gpu::Texture::getTextureGPUMemoryUsage()));
|
||||
STAT_UPDATE(gpuTextureVirtualMemory, (int)BYTES_TO_MB(gpu::Texture::getTextureGPUVirtualMemoryUsage()));
|
||||
STAT_UPDATE(gpuTextureFramebufferMemory, (int)BYTES_TO_MB(gpu::Texture::getTextureGPUFramebufferMemoryUsage()));
|
||||
STAT_UPDATE(gpuTextureSparseMemory, (int)BYTES_TO_MB(gpu::Texture::getTextureGPUSparseMemoryUsage()));
|
||||
STAT_UPDATE(gpuTextureMemoryPressureState, getTextureMemoryPressureModeString());
|
||||
STAT_UPDATE(gpuSparseTextureEnabled, gpuContext->getBackend()->isTextureManagementSparseEnabled() ? 1 : 0);
|
||||
STAT_UPDATE(gpuFreeMemory, (int)BYTES_TO_MB(gpu::Context::getFreeGPUMemory()));
|
||||
STAT_UPDATE(rectifiedTextureCount, (int)RECTIFIED_TEXTURE_COUNT.load());
|
||||
|
|
|
@ -77,7 +77,7 @@ class Stats : public QQuickItem {
|
|||
STATS_PROPERTY(int, audioMixerOutPps, 0)
|
||||
STATS_PROPERTY(int, audioMixerKbps, 0)
|
||||
STATS_PROPERTY(int, audioMixerPps, 0)
|
||||
STATS_PROPERTY(int, audioOutboundPPS, 0)
|
||||
STATS_PROPERTY(int, audioMicOutboundPPS, 0)
|
||||
STATS_PROPERTY(int, audioSilentOutboundPPS, 0)
|
||||
STATS_PROPERTY(int, audioAudioInboundPPS, 0)
|
||||
STATS_PROPERTY(int, audioSilentInboundPPS, 0)
|
||||
|
@ -117,13 +117,11 @@ class Stats : public QQuickItem {
|
|||
STATS_PROPERTY(int, gpuTexturesSparse, 0)
|
||||
STATS_PROPERTY(int, glContextSwapchainMemory, 0)
|
||||
STATS_PROPERTY(int, qmlTextureMemory, 0)
|
||||
STATS_PROPERTY(int, texturePendingTransfers, 0)
|
||||
STATS_PROPERTY(int, gpuTextureMemory, 0)
|
||||
STATS_PROPERTY(int, gpuTextureVirtualMemory, 0)
|
||||
STATS_PROPERTY(int, gpuTextureFramebufferMemory, 0)
|
||||
STATS_PROPERTY(int, gpuTextureSparseMemory, 0)
|
||||
STATS_PROPERTY(int, gpuSparseTextureEnabled, 0)
|
||||
STATS_PROPERTY(QString, gpuTextureMemoryPressureState, QString())
|
||||
STATS_PROPERTY(int, gpuFreeMemory, 0)
|
||||
STATS_PROPERTY(float, gpuFrameTime, 0)
|
||||
STATS_PROPERTY(float, batchFrameTime, 0)
|
||||
|
@ -200,7 +198,7 @@ signals:
|
|||
void audioMixerOutPpsChanged();
|
||||
void audioMixerKbpsChanged();
|
||||
void audioMixerPpsChanged();
|
||||
void audioOutboundPPSChanged();
|
||||
void audioMicOutboundPPSChanged();
|
||||
void audioSilentOutboundPPSChanged();
|
||||
void audioAudioInboundPPSChanged();
|
||||
void audioSilentInboundPPSChanged();
|
||||
|
@ -234,7 +232,6 @@ signals:
|
|||
void timingStatsChanged();
|
||||
void glContextSwapchainMemoryChanged();
|
||||
void qmlTextureMemoryChanged();
|
||||
void texturePendingTransfersChanged();
|
||||
void gpuBuffersChanged();
|
||||
void gpuBufferMemoryChanged();
|
||||
void gpuTexturesChanged();
|
||||
|
@ -243,7 +240,6 @@ signals:
|
|||
void gpuTextureVirtualMemoryChanged();
|
||||
void gpuTextureFramebufferMemoryChanged();
|
||||
void gpuTextureSparseMemoryChanged();
|
||||
void gpuTextureMemoryPressureStateChanged();
|
||||
void gpuSparseTextureEnabledChanged();
|
||||
void gpuFreeMemoryChanged();
|
||||
void gpuFrameTimeChanged();
|
||||
|
|
|
@ -431,9 +431,7 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionInternal(const PickR
|
|||
if (thisOverlay->findRayIntersectionExtraInfo(ray.origin, ray.direction, thisDistance,
|
||||
thisFace, thisSurfaceNormal, thisExtraInfo)) {
|
||||
bool isDrawInFront = thisOverlay->getDrawInFront();
|
||||
if ((bestIsFront && isDrawInFront && thisDistance < bestDistance)
|
||||
|| (!bestIsFront && (isDrawInFront || thisDistance < bestDistance))) {
|
||||
|
||||
if (thisDistance < bestDistance && (!bestIsFront || isDrawInFront)) {
|
||||
bestIsFront = isDrawInFront;
|
||||
bestDistance = thisDistance;
|
||||
result.intersects = true;
|
||||
|
|
|
@ -270,7 +270,7 @@ void Web3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
if (!_texture) {
|
||||
auto webSurface = _webSurface;
|
||||
_texture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda()));
|
||||
_texture = gpu::TexturePointer(gpu::Texture::createExternal2D(OffscreenQmlSurface::getDiscardLambda()));
|
||||
_texture->setSource(__FUNCTION__);
|
||||
}
|
||||
OffscreenQmlSurface::TextureAndFence newTextureAndFence;
|
||||
|
|
142
interface/ui/scriptEditorWidget.ui
Normal file
|
@ -0,0 +1,142 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ScriptEditorWidget</class>
|
||||
<widget class="QDockWidget" name="ScriptEditorWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>691</width>
|
||||
<height>549</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>690</width>
|
||||
<height>328</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-family: Helvetica, Arial, sans-serif;</string>
|
||||
</property>
|
||||
<property name="features">
|
||||
<set>QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable</set>
|
||||
</property>
|
||||
<property name="allowedAreas">
|
||||
<set>Qt::NoDockWidgetArea</set>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Edit Script</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="dockWidgetContents">
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="ScriptEditBox" name="scriptEdit">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Courier</family>
|
||||
<pointsize>-1</pointsize>
|
||||
<weight>50</weight>
|
||||
<italic>false</italic>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: 16px "Courier";</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: 13px "Helvetica","Arial","sans-serif";</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Debug Log:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="onTheFlyCheckBox">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Helvetica,Arial,sans-serif</family>
|
||||
<pointsize>-1</pointsize>
|
||||
<weight>50</weight>
|
||||
<italic>false</italic>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: 13px "Helvetica","Arial","sans-serif";</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Run on the fly (Careful: Any valid change made to the code will run immediately) </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="clearButton">
|
||||
<property name="text">
|
||||
<string>Clear</string>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ScriptEditBox</class>
|
||||
<extends>QTextEdit</extends>
|
||||
<header>ui/ScriptEditBox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
</ui>
|
324
interface/ui/scriptEditorWindow.ui
Normal file
|
@ -0,0 +1,324 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ScriptEditorWindow</class>
|
||||
<widget class="QWidget" name="ScriptEditorWindow">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::NonModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>780</width>
|
||||
<height>717</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>400</width>
|
||||
<height>250</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Script Editor</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-family: Helvetica, Arial, sans-serif;</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,0,0,0">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetNoConstraint</enum>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QToolButton" name="newButton">
|
||||
<property name="toolTip">
|
||||
<string>New Script (Ctrl+N)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>New</string>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="loadButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>25</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Load Script (Ctrl+O)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Load</string>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="popupMode">
|
||||
<enum>QToolButton::MenuButtonPopup</enum>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonIconOnly</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="saveButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::NoContextMenu</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Save Script (Ctrl+S)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save</string>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="autoRepeatDelay">
|
||||
<number>316</number>
|
||||
</property>
|
||||
<property name="popupMode">
|
||||
<enum>QToolButton::MenuButtonPopup</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="toggleRunButton">
|
||||
<property name="toolTip">
|
||||
<string>Toggle Run Script (F5)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Run/Stop</string>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="autoReloadCheckBox">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: 13px "Helvetica","Arial","sans-serif";</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Automatically reload externally changed files</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>250</width>
|
||||
<height>80</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="tabPosition">
|
||||
<enum>QTabWidget::West</enum>
|
||||
</property>
|
||||
<property name="tabShape">
|
||||
<enum>QTabWidget::Triangular</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<property name="elideMode">
|
||||
<enum>Qt::ElideNone</enum>
|
||||
</property>
|
||||
<property name="tabsClosable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="movable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>saveButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>ScriptEditorWindow</receiver>
|
||||
<slot>saveScriptClicked()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>236</x>
|
||||
<y>10</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>199</x>
|
||||
<y>264</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>toggleRunButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>ScriptEditorWindow</receiver>
|
||||
<slot>toggleRunScriptClicked()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>330</x>
|
||||
<y>10</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>199</x>
|
||||
<y>264</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>newButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>ScriptEditorWindow</receiver>
|
||||
<slot>newScriptClicked()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>58</x>
|
||||
<y>10</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>199</x>
|
||||
<y>264</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>loadButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>ScriptEditorWindow</receiver>
|
||||
<slot>loadScriptClicked()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>85</x>
|
||||
<y>10</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>199</x>
|
||||
<y>264</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>tabWidget</sender>
|
||||
<signal>currentChanged(int)</signal>
|
||||
<receiver>ScriptEditorWindow</receiver>
|
||||
<slot>tabSwitched(int)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>352</x>
|
||||
<y>360</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>352</x>
|
||||
<y>340</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>tabWidget</sender>
|
||||
<signal>tabCloseRequested(int)</signal>
|
||||
<receiver>ScriptEditorWindow</receiver>
|
||||
<slot>tabCloseRequested(int)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>352</x>
|
||||
<y>360</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>352</x>
|
||||
<y>340</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
|
@ -558,15 +558,15 @@ static const std::vector<float> LATERAL_SPEEDS = { 0.2f, 0.65f }; // m/s
|
|||
|
||||
void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, CharacterControllerState ccState) {
|
||||
|
||||
glm::vec3 forward = worldRotation * IDENTITY_FORWARD;
|
||||
glm::vec3 front = worldRotation * IDENTITY_FRONT;
|
||||
glm::vec3 workingVelocity = worldVelocity;
|
||||
|
||||
{
|
||||
glm::vec3 localVel = glm::inverse(worldRotation) * workingVelocity;
|
||||
|
||||
float forwardSpeed = glm::dot(localVel, IDENTITY_FORWARD);
|
||||
float forwardSpeed = glm::dot(localVel, IDENTITY_FRONT);
|
||||
float lateralSpeed = glm::dot(localVel, IDENTITY_RIGHT);
|
||||
float turningSpeed = glm::orientedAngle(forward, _lastForward, IDENTITY_UP) / deltaTime;
|
||||
float turningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime;
|
||||
|
||||
// filter speeds using a simple moving average.
|
||||
_averageForwardSpeed.updateAverage(forwardSpeed);
|
||||
|
@ -852,7 +852,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
|||
_lastEnableInverseKinematics = _enableInverseKinematics;
|
||||
}
|
||||
|
||||
_lastForward = forward;
|
||||
_lastFront = front;
|
||||
_lastPosition = worldPosition;
|
||||
_lastVelocity = workingVelocity;
|
||||
}
|
||||
|
|
|
@ -267,7 +267,7 @@ protected:
|
|||
int _rightElbowJointIndex { -1 };
|
||||
int _rightShoulderJointIndex { -1 };
|
||||
|
||||
glm::vec3 _lastForward;
|
||||
glm::vec3 _lastFront;
|
||||
glm::vec3 _lastPosition;
|
||||
glm::vec3 _lastVelocity;
|
||||
|
||||
|
|
|
@ -160,7 +160,7 @@ AudioClient::AudioClient() :
|
|||
_loopbackAudioOutput(NULL),
|
||||
_loopbackOutputDevice(NULL),
|
||||
_inputRingBuffer(0),
|
||||
_localInjectorsStream(0, 1),
|
||||
_localInjectorsStream(0),
|
||||
_receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES),
|
||||
_isStereoInput(false),
|
||||
_outputStarveDetectionStartTimeMsec(0),
|
||||
|
@ -184,6 +184,7 @@ AudioClient::AudioClient() :
|
|||
_outgoingAvatarAudioSequenceNumber(0),
|
||||
_audioOutputIODevice(_localInjectorsStream, _receivedAudioStream, this),
|
||||
_stats(&_receivedAudioStream),
|
||||
_inputGate(),
|
||||
_positionGetter(DEFAULT_POSITION_GETTER),
|
||||
_orientationGetter(DEFAULT_ORIENTATION_GETTER) {
|
||||
// avoid putting a lock in the device callback
|
||||
|
@ -970,87 +971,14 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
|
|||
}
|
||||
}
|
||||
|
||||
void AudioClient::handleAudioInput(QByteArray& audioBuffer) {
|
||||
if (_muted) {
|
||||
_lastInputLoudness = 0.0f;
|
||||
_timeSinceLastClip = 0.0f;
|
||||
} else {
|
||||
int16_t* samples = reinterpret_cast<int16_t*>(audioBuffer.data());
|
||||
int numSamples = audioBuffer.size() / sizeof(AudioConstants::SAMPLE_SIZE);
|
||||
bool didClip = false;
|
||||
|
||||
bool shouldRemoveDCOffset = !_isPlayingBackRecording && !_isStereoInput;
|
||||
if (shouldRemoveDCOffset) {
|
||||
_noiseGate.removeDCOffset(samples, numSamples);
|
||||
}
|
||||
|
||||
bool shouldNoiseGate = (_isPlayingBackRecording || !_isStereoInput) && _isNoiseGateEnabled;
|
||||
if (shouldNoiseGate) {
|
||||
_noiseGate.gateSamples(samples, numSamples);
|
||||
_lastInputLoudness = _noiseGate.getLastLoudness();
|
||||
didClip = _noiseGate.clippedInLastBlock();
|
||||
} else {
|
||||
float loudness = 0.0f;
|
||||
for (int i = 0; i < numSamples; ++i) {
|
||||
int16_t sample = std::abs(samples[i]);
|
||||
loudness += (float)sample;
|
||||
didClip = didClip ||
|
||||
(sample > (AudioConstants::MAX_SAMPLE_VALUE * AudioNoiseGate::CLIPPING_THRESHOLD));
|
||||
}
|
||||
_lastInputLoudness = fabs(loudness / numSamples);
|
||||
}
|
||||
|
||||
if (didClip) {
|
||||
_timeSinceLastClip = 0.0f;
|
||||
} else if (_timeSinceLastClip >= 0.0f) {
|
||||
_timeSinceLastClip += (float)numSamples / (float)AudioConstants::SAMPLE_RATE;
|
||||
}
|
||||
|
||||
emit inputReceived({ audioBuffer.data(), numSamples });
|
||||
|
||||
if (_noiseGate.openedInLastBlock()) {
|
||||
emit noiseGateOpened();
|
||||
} else if (_noiseGate.closedInLastBlock()) {
|
||||
emit noiseGateClosed();
|
||||
}
|
||||
}
|
||||
|
||||
// the codec needs a flush frame before sending silent packets, so
|
||||
// do not send one if the gate closed in this block (eventually this can be crossfaded).
|
||||
auto packetType = _shouldEchoToServer ?
|
||||
PacketType::MicrophoneAudioWithEcho : PacketType::MicrophoneAudioNoEcho;
|
||||
if (_lastInputLoudness == 0.0f && !_noiseGate.closedInLastBlock()) {
|
||||
packetType = PacketType::SilentAudioFrame;
|
||||
_silentOutbound.increment();
|
||||
} else {
|
||||
_audioOutbound.increment();
|
||||
}
|
||||
|
||||
Transform audioTransform;
|
||||
audioTransform.setTranslation(_positionGetter());
|
||||
audioTransform.setRotation(_orientationGetter());
|
||||
|
||||
QByteArray encodedBuffer;
|
||||
if (_encoder) {
|
||||
_encoder->encode(audioBuffer, encodedBuffer);
|
||||
} else {
|
||||
encodedBuffer = audioBuffer;
|
||||
}
|
||||
|
||||
emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber,
|
||||
audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale,
|
||||
packetType, _selectedCodecName);
|
||||
_stats.sentPacket();
|
||||
}
|
||||
|
||||
void AudioClient::handleMicAudioInput() {
|
||||
void AudioClient::handleAudioInput() {
|
||||
if (!_inputDevice || _isPlayingBackRecording) {
|
||||
return;
|
||||
}
|
||||
|
||||
// input samples required to produce exactly NETWORK_FRAME_SAMPLES of output
|
||||
const int inputSamplesRequired = (_inputToNetworkResampler ?
|
||||
_inputToNetworkResampler->getMinInput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) :
|
||||
const int inputSamplesRequired = (_inputToNetworkResampler ?
|
||||
_inputToNetworkResampler->getMinInput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) :
|
||||
AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) * _inputFormat.channelCount();
|
||||
|
||||
const auto inputAudioSamples = std::unique_ptr<int16_t[]>(new int16_t[inputSamplesRequired]);
|
||||
|
@ -1073,27 +1001,126 @@ void AudioClient::handleMicAudioInput() {
|
|||
static int16_t networkAudioSamples[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO];
|
||||
|
||||
while (_inputRingBuffer.samplesAvailable() >= inputSamplesRequired) {
|
||||
if (_muted) {
|
||||
_inputRingBuffer.shiftReadPosition(inputSamplesRequired);
|
||||
} else {
|
||||
|
||||
if (!_muted) {
|
||||
|
||||
|
||||
// Increment the time since the last clip
|
||||
if (_timeSinceLastClip >= 0.0f) {
|
||||
_timeSinceLastClip += (float)numNetworkSamples / (float)AudioConstants::SAMPLE_RATE;
|
||||
}
|
||||
|
||||
_inputRingBuffer.readSamples(inputAudioSamples.get(), inputSamplesRequired);
|
||||
possibleResampling(_inputToNetworkResampler,
|
||||
inputAudioSamples.get(), networkAudioSamples,
|
||||
inputSamplesRequired, numNetworkSamples,
|
||||
_inputFormat.channelCount(), _desiredInputFormat.channelCount());
|
||||
|
||||
// Remove DC offset
|
||||
if (!_isStereoInput) {
|
||||
_inputGate.removeDCOffset(networkAudioSamples, numNetworkSamples);
|
||||
}
|
||||
|
||||
// only impose the noise gate and perform tone injection if we are sending mono audio
|
||||
if (!_isStereoInput && _isNoiseGateEnabled) {
|
||||
_inputGate.gateSamples(networkAudioSamples, numNetworkSamples);
|
||||
|
||||
// if we performed the noise gate we can get values from it instead of enumerating the samples again
|
||||
_lastInputLoudness = _inputGate.getLastLoudness();
|
||||
|
||||
if (_inputGate.clippedInLastBlock()) {
|
||||
_timeSinceLastClip = 0.0f;
|
||||
}
|
||||
|
||||
} else {
|
||||
float loudness = 0.0f;
|
||||
|
||||
for (int i = 0; i < numNetworkSamples; i++) {
|
||||
int thisSample = std::abs(networkAudioSamples[i]);
|
||||
loudness += (float)thisSample;
|
||||
|
||||
if (thisSample > (AudioConstants::MAX_SAMPLE_VALUE * AudioNoiseGate::CLIPPING_THRESHOLD)) {
|
||||
_timeSinceLastClip = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
_lastInputLoudness = fabs(loudness / numNetworkSamples);
|
||||
}
|
||||
|
||||
emit inputReceived({ reinterpret_cast<char*>(networkAudioSamples), numNetworkBytes });
|
||||
|
||||
if (_inputGate.openedInLastBlock()) {
|
||||
emit noiseGateOpened();
|
||||
} else if (_inputGate.closedInLastBlock()) {
|
||||
emit noiseGateClosed();
|
||||
}
|
||||
|
||||
} else {
|
||||
// our input loudness is 0, since we're muted
|
||||
_lastInputLoudness = 0;
|
||||
_timeSinceLastClip = 0.0f;
|
||||
|
||||
_inputRingBuffer.shiftReadPosition(inputSamplesRequired);
|
||||
}
|
||||
|
||||
auto packetType = _shouldEchoToServer ?
|
||||
PacketType::MicrophoneAudioWithEcho : PacketType::MicrophoneAudioNoEcho;
|
||||
|
||||
// if the _inputGate closed in this last frame, then we don't actually want
|
||||
// to send a silent packet, instead, we want to go ahead and encode and send
|
||||
// the output from the input gate (eventually, this could be crossfaded)
|
||||
// and allow the codec to properly encode down to silent/zero. If we still
|
||||
// have _lastInputLoudness of 0 in our NEXT frame, we will send a silent packet
|
||||
if (_lastInputLoudness == 0 && !_inputGate.closedInLastBlock()) {
|
||||
packetType = PacketType::SilentAudioFrame;
|
||||
_silentOutbound.increment();
|
||||
} else {
|
||||
_micAudioOutbound.increment();
|
||||
}
|
||||
|
||||
Transform audioTransform;
|
||||
audioTransform.setTranslation(_positionGetter());
|
||||
audioTransform.setRotation(_orientationGetter());
|
||||
// FIXME find a way to properly handle both playback audio and user audio concurrently
|
||||
|
||||
QByteArray decodedBuffer(reinterpret_cast<char*>(networkAudioSamples), numNetworkBytes);
|
||||
QByteArray encodedBuffer;
|
||||
if (_encoder) {
|
||||
_encoder->encode(decodedBuffer, encodedBuffer);
|
||||
} else {
|
||||
encodedBuffer = decodedBuffer;
|
||||
}
|
||||
|
||||
emitAudioPacket(encodedBuffer.constData(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber,
|
||||
audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale,
|
||||
packetType, _selectedCodecName);
|
||||
_stats.sentPacket();
|
||||
|
||||
int bytesInInputRingBuffer = _inputRingBuffer.samplesAvailable() * AudioConstants::SAMPLE_SIZE;
|
||||
float msecsInInputRingBuffer = bytesInInputRingBuffer / (float)(_inputFormat.bytesForDuration(USECS_PER_MSEC));
|
||||
_stats.updateInputMsUnplayed(msecsInInputRingBuffer);
|
||||
|
||||
QByteArray audioBuffer(reinterpret_cast<char*>(networkAudioSamples), numNetworkBytes);
|
||||
handleAudioInput(audioBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME - should this go through the noise gate and honor mute and echo?
|
||||
void AudioClient::handleRecordedAudioInput(const QByteArray& audio) {
|
||||
QByteArray audioBuffer(audio);
|
||||
handleAudioInput(audioBuffer);
|
||||
Transform audioTransform;
|
||||
audioTransform.setTranslation(_positionGetter());
|
||||
audioTransform.setRotation(_orientationGetter());
|
||||
|
||||
QByteArray encodedBuffer;
|
||||
if (_encoder) {
|
||||
_encoder->encode(audio, encodedBuffer);
|
||||
} else {
|
||||
encodedBuffer = audio;
|
||||
}
|
||||
|
||||
_micAudioOutbound.increment();
|
||||
|
||||
// FIXME check a flag to see if we should echo audio?
|
||||
emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber,
|
||||
audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale,
|
||||
PacketType::MicrophoneAudioWithEcho, _selectedCodecName);
|
||||
}
|
||||
|
||||
void AudioClient::prepareLocalAudioInjectors() {
|
||||
|
@ -1407,7 +1434,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn
|
|||
lock.unlock();
|
||||
|
||||
if (_inputDevice) {
|
||||
connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleMicAudioInput()));
|
||||
connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleAudioInput()));
|
||||
supportedFormat = true;
|
||||
} else {
|
||||
qCDebug(audioclient) << "Error starting audio input -" << _audioInput->error();
|
||||
|
@ -1513,39 +1540,12 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
|
|||
// setup our general output device for audio-mixer audio
|
||||
_audioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this);
|
||||
|
||||
int osDefaultBufferSize = _audioOutput->bufferSize();
|
||||
int deviceChannelCount = _outputFormat.channelCount();
|
||||
int frameSize = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * deviceChannelCount * _outputFormat.sampleRate()) / _desiredOutputFormat.sampleRate();
|
||||
int requestedSize = _sessionOutputBufferSizeFrames * frameSize * AudioConstants::SAMPLE_SIZE;
|
||||
int deviceFrameSize = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * deviceChannelCount * _outputFormat.sampleRate()) / _desiredOutputFormat.sampleRate();
|
||||
int requestedSize = _sessionOutputBufferSizeFrames * deviceFrameSize * AudioConstants::SAMPLE_SIZE;
|
||||
_audioOutput->setBufferSize(requestedSize);
|
||||
|
||||
// initialize mix buffers on the _audioOutput thread to avoid races
|
||||
connect(_audioOutput, &QAudioOutput::stateChanged, [&, frameSize, requestedSize](QAudio::State state) {
|
||||
if (state == QAudio::ActiveState) {
|
||||
// restrict device callback to _outputPeriod samples
|
||||
_outputPeriod = (_audioOutput->periodSize() / AudioConstants::SAMPLE_SIZE) * 2;
|
||||
_outputMixBuffer = new float[_outputPeriod];
|
||||
_outputScratchBuffer = new int16_t[_outputPeriod];
|
||||
|
||||
// size local output mix buffer based on resampled network frame size
|
||||
_networkPeriod = _localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_STEREO);
|
||||
_localOutputMixBuffer = new float[_networkPeriod];
|
||||
int localPeriod = _outputPeriod * 2;
|
||||
_localInjectorsStream.resizeForFrameSize(localPeriod);
|
||||
|
||||
int bufferSize = _audioOutput->bufferSize();
|
||||
int bufferSamples = bufferSize / AudioConstants::SAMPLE_SIZE;
|
||||
int bufferFrames = bufferSamples / (float)frameSize;
|
||||
qCDebug(audioclient) << "frame (samples):" << frameSize;
|
||||
qCDebug(audioclient) << "buffer (frames):" << bufferFrames;
|
||||
qCDebug(audioclient) << "buffer (samples):" << bufferSamples;
|
||||
qCDebug(audioclient) << "buffer (bytes):" << bufferSize;
|
||||
qCDebug(audioclient) << "requested (bytes):" << requestedSize;
|
||||
qCDebug(audioclient) << "period (samples):" << _outputPeriod;
|
||||
qCDebug(audioclient) << "local buffer (samples):" << localPeriod;
|
||||
|
||||
disconnect(_audioOutput, &QAudioOutput::stateChanged, 0, 0);
|
||||
}
|
||||
});
|
||||
connect(_audioOutput, &QAudioOutput::notify, this, &AudioClient::outputNotify);
|
||||
|
||||
_audioOutputIODevice.start();
|
||||
|
@ -1555,6 +1555,18 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
|
|||
_audioOutput->start(&_audioOutputIODevice);
|
||||
lock.unlock();
|
||||
|
||||
int periodSampleSize = _audioOutput->periodSize() / AudioConstants::SAMPLE_SIZE;
|
||||
// device callback is not restricted to periodSampleSize, so double the mix/scratch buffer sizes
|
||||
_outputPeriod = periodSampleSize * 2;
|
||||
_outputMixBuffer = new float[_outputPeriod];
|
||||
_outputScratchBuffer = new int16_t[_outputPeriod];
|
||||
_localOutputMixBuffer = new float[_outputPeriod];
|
||||
_localInjectorsStream.resizeForFrameSize(_outputPeriod * 2);
|
||||
|
||||
qCDebug(audioclient) << "Output Buffer capacity in frames: " << _audioOutput->bufferSize() / AudioConstants::SAMPLE_SIZE / (float)deviceFrameSize <<
|
||||
"requested bytes:" << requestedSize << "actual bytes:" << _audioOutput->bufferSize() <<
|
||||
"os default:" << osDefaultBufferSize << "period size:" << _audioOutput->periodSize();
|
||||
|
||||
// setup a loopback audio output device
|
||||
_loopbackAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this);
|
||||
|
||||
|
|
|
@ -124,16 +124,16 @@ public:
|
|||
void selectAudioFormat(const QString& selectedCodecName);
|
||||
|
||||
Q_INVOKABLE QString getSelectedAudioFormat() const { return _selectedCodecName; }
|
||||
Q_INVOKABLE bool getNoiseGateOpen() const { return _noiseGate.isOpen(); }
|
||||
Q_INVOKABLE bool getNoiseGateOpen() const { return _inputGate.isOpen(); }
|
||||
Q_INVOKABLE float getSilentOutboundPPS() const { return _silentOutbound.rate(); }
|
||||
Q_INVOKABLE float getMicAudioOutboundPPS() const { return _micAudioOutbound.rate(); }
|
||||
Q_INVOKABLE float getSilentInboundPPS() const { return _silentInbound.rate(); }
|
||||
Q_INVOKABLE float getAudioInboundPPS() const { return _audioInbound.rate(); }
|
||||
Q_INVOKABLE float getSilentOutboundPPS() const { return _silentOutbound.rate(); }
|
||||
Q_INVOKABLE float getAudioOutboundPPS() const { return _audioOutbound.rate(); }
|
||||
|
||||
const MixedProcessedAudioStream& getReceivedAudioStream() const { return _receivedAudioStream; }
|
||||
MixedProcessedAudioStream& getReceivedAudioStream() { return _receivedAudioStream; }
|
||||
|
||||
float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _noiseGate.getMeasuredFloor(), 0.0f); }
|
||||
float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _inputGate.getMeasuredFloor(), 0.0f); }
|
||||
|
||||
float getTimeSinceLastClip() const { return _timeSinceLastClip; }
|
||||
float getAudioAverageInputLoudness() const { return _lastInputLoudness; }
|
||||
|
@ -180,7 +180,7 @@ public slots:
|
|||
void handleMismatchAudioFormat(SharedNodePointer node, const QString& currentCodec, const QString& recievedCodec);
|
||||
|
||||
void sendDownstreamAudioStatsPacket() { _stats.publish(); }
|
||||
void handleMicAudioInput();
|
||||
void handleAudioInput();
|
||||
void handleRecordedAudioInput(const QByteArray& audio);
|
||||
void reset();
|
||||
void audioMixerKilled();
|
||||
|
@ -250,7 +250,6 @@ protected:
|
|||
|
||||
private:
|
||||
void outputFormatChanged();
|
||||
void handleAudioInput(QByteArray& audioBuffer);
|
||||
bool mixLocalAudioInjectors(float* mixBuffer);
|
||||
float azimuthForSource(const glm::vec3& relativePosition);
|
||||
float gainForSource(float distance, float volume);
|
||||
|
@ -340,7 +339,6 @@ private:
|
|||
int16_t _networkScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC];
|
||||
|
||||
// for local audio (used by audio injectors thread)
|
||||
int _networkPeriod { 0 };
|
||||
float _localMixBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO];
|
||||
int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC];
|
||||
float* _localOutputMixBuffer { NULL };
|
||||
|
@ -373,7 +371,7 @@ private:
|
|||
|
||||
AudioIOStats _stats;
|
||||
|
||||
AudioNoiseGate _noiseGate;
|
||||
AudioNoiseGate _inputGate;
|
||||
|
||||
AudioPositionGetter _positionGetter;
|
||||
AudioOrientationGetter _orientationGetter;
|
||||
|
@ -397,7 +395,7 @@ private:
|
|||
QThread* _checkDevicesThread { nullptr };
|
||||
|
||||
RateCounter<> _silentOutbound;
|
||||
RateCounter<> _audioOutbound;
|
||||
RateCounter<> _micAudioOutbound;
|
||||
RateCounter<> _silentInbound;
|
||||
RateCounter<> _audioInbound;
|
||||
};
|
||||
|
|
|
@ -65,8 +65,8 @@ glm::quat HeadData::getOrientation() const {
|
|||
void HeadData::setOrientation(const glm::quat& orientation) {
|
||||
// rotate body about vertical axis
|
||||
glm::quat bodyOrientation = _owningAvatar->getOrientation();
|
||||
glm::vec3 newForward = glm::inverse(bodyOrientation) * (orientation * IDENTITY_FORWARD);
|
||||
bodyOrientation = bodyOrientation * glm::angleAxis(atan2f(-newForward.x, -newForward.z), glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
glm::vec3 newFront = glm::inverse(bodyOrientation) * (orientation * IDENTITY_FRONT);
|
||||
bodyOrientation = bodyOrientation * glm::angleAxis(atan2f(-newFront.x, -newFront.z), glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
_owningAvatar->setOrientation(bodyOrientation);
|
||||
|
||||
// the rest goes to the head
|
||||
|
|
|
@ -355,16 +355,14 @@ void OpenGLDisplayPlugin::customizeContext() {
|
|||
if ((image.width() > 0) && (image.height() > 0)) {
|
||||
|
||||
cursorData.texture.reset(
|
||||
gpu::Texture::createStrict(
|
||||
gpu::Texture::create2D(
|
||||
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA),
|
||||
image.width(), image.height(),
|
||||
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||
cursorData.texture->setSource("cursor texture");
|
||||
auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha();
|
||||
cursorData.texture->setUsage(usage.build());
|
||||
cursorData.texture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
||||
cursorData.texture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||
cursorData.texture->autoGenerateMips(-1);
|
||||
cursorData.texture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.byteCount(), image.constBits());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -296,32 +296,33 @@ void HmdDisplayPlugin::internalPresent() {
|
|||
image = image.convertToFormat(QImage::Format_RGBA8888);
|
||||
if (!_previewTexture) {
|
||||
_previewTexture.reset(
|
||||
gpu::Texture::createStrict(
|
||||
gpu::Texture::create2D(
|
||||
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA),
|
||||
image.width(), image.height(),
|
||||
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||
_previewTexture->setSource("HMD Preview Texture");
|
||||
_previewTexture->setUsage(gpu::Texture::Usage::Builder().withColor().build());
|
||||
_previewTexture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
||||
_previewTexture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||
_previewTexture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.byteCount(), image.constBits());
|
||||
_previewTexture->autoGenerateMips(-1);
|
||||
}
|
||||
|
||||
auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions()));
|
||||
if (getGLBackend()->isTextureReady(_previewTexture)) {
|
||||
auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions()));
|
||||
|
||||
render([&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
batch.resetViewTransform();
|
||||
batch.setFramebuffer(gpu::FramebufferPointer());
|
||||
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0));
|
||||
batch.setStateScissorRect(viewport);
|
||||
batch.setViewportTransform(viewport);
|
||||
batch.setResourceTexture(0, _previewTexture);
|
||||
batch.setPipeline(_presentPipeline);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
_clearPreviewFlag = false;
|
||||
swapBuffers();
|
||||
render([&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
batch.resetViewTransform();
|
||||
batch.setFramebuffer(gpu::FramebufferPointer());
|
||||
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0));
|
||||
batch.setStateScissorRect(viewport);
|
||||
batch.setViewportTransform(viewport);
|
||||
batch.setResourceTexture(0, _previewTexture);
|
||||
batch.setPipeline(_presentPipeline);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
_clearPreviewFlag = false;
|
||||
swapBuffers();
|
||||
}
|
||||
}
|
||||
postPreview();
|
||||
|
||||
|
|
|
@ -146,7 +146,6 @@ void EntityTreeRenderer::clear() {
|
|||
|
||||
void EntityTreeRenderer::reloadEntityScripts() {
|
||||
_entitiesScriptEngine->unloadAllEntityScripts();
|
||||
_entitiesScriptEngine->resetModuleCache();
|
||||
foreach(auto entity, _entitiesInScene) {
|
||||
if (!entity->getScript().isEmpty()) {
|
||||
_entitiesScriptEngine->loadEntityScript(entity->getEntityItemID(), entity->getScript(), true);
|
||||
|
@ -941,7 +940,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) {
|
|||
|
||||
void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
|
||||
if (_tree && !_shuttingDown && _entitiesScriptEngine) {
|
||||
_entitiesScriptEngine->unloadEntityScript(entityID, true);
|
||||
_entitiesScriptEngine->unloadEntityScript(entityID);
|
||||
}
|
||||
|
||||
forceRecheckEntities(); // reset our state to force checking our inside/outsideness of entities
|
||||
|
@ -996,7 +995,7 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, const
|
|||
}
|
||||
bool shouldLoad = entity->shouldPreloadScript() && _entitiesScriptEngine;
|
||||
QString scriptUrl = entity->getScript();
|
||||
if (shouldLoad && (unloadFirst || scriptUrl.isEmpty())) {
|
||||
if ((unloadFirst && shouldLoad) || scriptUrl.isEmpty()) {
|
||||
_entitiesScriptEngine->unloadEntityScript(entityID);
|
||||
entity->scriptHasUnloaded();
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include <QByteArray>
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
#include <glm/gtx/transform.hpp>
|
||||
#include "ModelScriptingInterface.h"
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
|
@ -54,8 +53,6 @@
|
|||
#include "PhysicalEntitySimulation.h"
|
||||
|
||||
gpu::PipelinePointer RenderablePolyVoxEntityItem::_pipeline = nullptr;
|
||||
gpu::PipelinePointer RenderablePolyVoxEntityItem::_wireframePipeline = nullptr;
|
||||
|
||||
const float MARCHING_CUBE_COLLISION_HULL_OFFSET = 0.5;
|
||||
|
||||
|
||||
|
@ -76,7 +73,7 @@ const float MARCHING_CUBE_COLLISION_HULL_OFFSET = 0.5;
|
|||
_meshDirty
|
||||
|
||||
In RenderablePolyVoxEntityItem::render, these flags are checked and changes are propagated along the chain.
|
||||
decompressVolumeData() is called to decompress _voxelData into _volData. recomputeMesh() is called to invoke the
|
||||
decompressVolumeData() is called to decompress _voxelData into _volData. getMesh() is called to invoke the
|
||||
polyVox surface extractor to create _mesh (as well as set Simulation _dirtyFlags). Because Simulation::DIRTY_SHAPE
|
||||
is set, isReadyToComputeShape() gets called and _shape is created either from _volData or _shape, depending on
|
||||
the surface style.
|
||||
|
@ -84,7 +81,7 @@ const float MARCHING_CUBE_COLLISION_HULL_OFFSET = 0.5;
|
|||
When a script changes _volData, compressVolumeDataAndSendEditPacket is called to update _voxelData and to
|
||||
send a packet to the entity-server.
|
||||
|
||||
decompressVolumeData, recomputeMesh, computeShapeInfoWorker, and compressVolumeDataAndSendEditPacket are too expensive
|
||||
decompressVolumeData, getMesh, computeShapeInfoWorker, and compressVolumeDataAndSendEditPacket are too expensive
|
||||
to run on a thread that has other things to do. These use QtConcurrent::run to spawn a thread. As each thread
|
||||
finishes, it adjusts the dirty flags so that the next call to render() will kick off the next step.
|
||||
|
||||
|
@ -666,8 +663,11 @@ void RenderablePolyVoxEntityItem::setZTextureURL(QString zTextureURL) {
|
|||
}
|
||||
}
|
||||
|
||||
void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RenderablePolyVoxEntityItem::render");
|
||||
assert(getType() == EntityTypes::PolyVox);
|
||||
Q_ASSERT(args->_batch);
|
||||
|
||||
bool RenderablePolyVoxEntityItem::updateDependents() {
|
||||
bool voxelDataDirty;
|
||||
bool volDataDirty;
|
||||
withWriteLock([&] {
|
||||
|
@ -682,20 +682,9 @@ bool RenderablePolyVoxEntityItem::updateDependents() {
|
|||
if (voxelDataDirty) {
|
||||
decompressVolumeData();
|
||||
} else if (volDataDirty) {
|
||||
recomputeMesh();
|
||||
getMesh();
|
||||
}
|
||||
|
||||
return !volDataDirty;
|
||||
}
|
||||
|
||||
|
||||
void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RenderablePolyVoxEntityItem::render");
|
||||
assert(getType() == EntityTypes::PolyVox);
|
||||
Q_ASSERT(args->_batch);
|
||||
|
||||
updateDependents();
|
||||
|
||||
model::MeshPointer mesh;
|
||||
glm::vec3 voxelVolumeSize;
|
||||
withReadLock([&] {
|
||||
|
@ -707,7 +696,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
|
|||
!mesh->getIndexBuffer()._buffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!_pipeline) {
|
||||
gpu::ShaderPointer vertexShader = gpu::Shader::createVertex(std::string(polyvox_vert));
|
||||
gpu::ShaderPointer pixelShader = gpu::Shader::createPixel(std::string(polyvox_frag));
|
||||
|
@ -726,13 +715,6 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
|
|||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
|
||||
auto wireframeState = std::make_shared<gpu::State>();
|
||||
wireframeState->setCullMode(gpu::State::CULL_BACK);
|
||||
wireframeState->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
wireframeState->setFillMode(gpu::State::FILL_LINE);
|
||||
|
||||
_wireframePipeline = gpu::Pipeline::create(program, wireframeState);
|
||||
}
|
||||
|
||||
if (!_vertexFormat) {
|
||||
|
@ -743,11 +725,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
|
|||
}
|
||||
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
|
||||
// Pick correct Pipeline
|
||||
bool wireframe = (render::ShapeKey(args->_globalShapeKey).isWireframe());
|
||||
auto pipeline = (wireframe ? _wireframePipeline : _pipeline);
|
||||
batch.setPipeline(pipeline);
|
||||
batch.setPipeline(_pipeline);
|
||||
|
||||
Transform transform(voxelToWorldMatrix());
|
||||
batch.setModelTransform(transform);
|
||||
|
@ -784,7 +762,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
|
|||
batch.setResourceTexture(2, DependencyManager::get<TextureCache>()->getWhiteTexture());
|
||||
}
|
||||
|
||||
int voxelVolumeSizeLocation = pipeline->getProgram()->getUniforms().findLocation("voxelVolumeSize");
|
||||
int voxelVolumeSizeLocation = _pipeline->getProgram()->getUniforms().findLocation("voxelVolumeSize");
|
||||
batch._glUniform3f(voxelVolumeSizeLocation, voxelVolumeSize.x, voxelVolumeSize.y, voxelVolumeSize.z);
|
||||
|
||||
batch.drawIndexed(gpu::TRIANGLES, (gpu::uint32)mesh->getNumIndices(), 0);
|
||||
|
@ -1221,7 +1199,7 @@ void RenderablePolyVoxEntityItem::copyUpperEdgesFromNeighbors() {
|
|||
}
|
||||
}
|
||||
|
||||
void RenderablePolyVoxEntityItem::recomputeMesh() {
|
||||
void RenderablePolyVoxEntityItem::getMesh() {
|
||||
// use _volData to make a renderable mesh
|
||||
PolyVoxSurfaceStyle voxelSurfaceStyle;
|
||||
withReadLock([&] {
|
||||
|
@ -1291,20 +1269,12 @@ void RenderablePolyVoxEntityItem::recomputeMesh() {
|
|||
vertexBufferPtr->getSize() ,
|
||||
sizeof(PolyVox::PositionMaterialNormal),
|
||||
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW)));
|
||||
|
||||
std::vector<model::Mesh::Part> parts;
|
||||
parts.emplace_back(model::Mesh::Part((model::Index)0, // startIndex
|
||||
(model::Index)vecIndices.size(), // numIndices
|
||||
(model::Index)0, // baseVertex
|
||||
model::Mesh::TRIANGLES)); // topology
|
||||
mesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part),
|
||||
(gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL));
|
||||
entity->setMesh(mesh);
|
||||
});
|
||||
}
|
||||
|
||||
void RenderablePolyVoxEntityItem::setMesh(model::MeshPointer mesh) {
|
||||
// this catches the payload from recomputeMesh
|
||||
// this catches the payload from getMesh
|
||||
bool neighborsNeedUpdate;
|
||||
withWriteLock([&] {
|
||||
if (!_collisionless) {
|
||||
|
@ -1561,6 +1531,7 @@ std::shared_ptr<RenderablePolyVoxEntityItem> RenderablePolyVoxEntityItem::getZPN
|
|||
return std::dynamic_pointer_cast<RenderablePolyVoxEntityItem>(_zPNeighbor.lock());
|
||||
}
|
||||
|
||||
|
||||
void RenderablePolyVoxEntityItem::bonkNeighbors() {
|
||||
// flag neighbors to the negative of this entity as needing to rebake their meshes.
|
||||
cacheNeighbors();
|
||||
|
@ -1580,6 +1551,7 @@ void RenderablePolyVoxEntityItem::bonkNeighbors() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void RenderablePolyVoxEntityItem::locationChanged(bool tellPhysics) {
|
||||
EntityItem::locationChanged(tellPhysics);
|
||||
if (!_pipeline || !render::Item::isValidID(_myItem)) {
|
||||
|
@ -1591,25 +1563,3 @@ void RenderablePolyVoxEntityItem::locationChanged(bool tellPhysics) {
|
|||
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
}
|
||||
|
||||
bool RenderablePolyVoxEntityItem::getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) {
|
||||
if (!updateDependents()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
MeshProxy* meshProxy = nullptr;
|
||||
glm::mat4 transform = voxelToLocalMatrix();
|
||||
withReadLock([&] {
|
||||
if (_meshInitialized) {
|
||||
success = true;
|
||||
// the mesh will be in voxel-space. transform it into object-space
|
||||
meshProxy = new MeshProxy(
|
||||
_mesh->map([=](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); },
|
||||
[=](glm::vec3 normal){ return glm::vec3(transform * glm::vec4(normal, 0.0f)); },
|
||||
[](uint32_t index){ return index; }));
|
||||
}
|
||||
});
|
||||
result = meshToScriptValue(engine, meshProxy);
|
||||
return success;
|
||||
}
|
||||
|
|
|
@ -61,8 +61,6 @@ public:
|
|||
virtual uint8_t getVoxel(int x, int y, int z) override;
|
||||
virtual bool setVoxel(int x, int y, int z, uint8_t toValue) override;
|
||||
|
||||
int getOnCount() const override { return _onCount; }
|
||||
|
||||
void render(RenderArgs* args) override;
|
||||
virtual bool supportsDetailedRayIntersection() const override { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
|
@ -135,7 +133,6 @@ public:
|
|||
QByteArray volDataToArray(quint16 voxelXSize, quint16 voxelYSize, quint16 voxelZSize) const;
|
||||
|
||||
void setMesh(model::MeshPointer mesh);
|
||||
bool getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) override;
|
||||
void setCollisionPoints(ShapeInfo::PointCollection points, AABox box);
|
||||
PolyVox::SimpleVolume<uint8_t>* getVolData() { return _volData; }
|
||||
|
||||
|
@ -166,12 +163,11 @@ private:
|
|||
const int MATERIAL_GPU_SLOT = 3;
|
||||
render::ItemID _myItem{ render::Item::INVALID_ITEM_ID };
|
||||
static gpu::PipelinePointer _pipeline;
|
||||
static gpu::PipelinePointer _wireframePipeline;
|
||||
|
||||
ShapeInfo _shapeInfo;
|
||||
|
||||
PolyVox::SimpleVolume<uint8_t>* _volData = nullptr;
|
||||
bool _volDataDirty = false; // does recomputeMesh need to be called?
|
||||
bool _volDataDirty = false; // does getMesh need to be called?
|
||||
int _onCount; // how many non-zero voxels are in _volData
|
||||
|
||||
bool _neighborsNeedUpdate { false };
|
||||
|
@ -182,7 +178,7 @@ private:
|
|||
// these are run off the main thread
|
||||
void decompressVolumeData();
|
||||
void compressVolumeDataAndSendEditPacket();
|
||||
virtual void recomputeMesh() override; // recompute mesh
|
||||
virtual void getMesh() override; // recompute mesh
|
||||
void computeShapeInfoWorker();
|
||||
|
||||
// these are cached lookups of _xNNeighborID, _yNNeighborID, _zNNeighborID, _xPNeighborID, _yPNeighborID, _zPNeighborID
|
||||
|
@ -195,7 +191,6 @@ private:
|
|||
void cacheNeighbors();
|
||||
void copyUpperEdgesFromNeighbors();
|
||||
void bonkNeighbors();
|
||||
bool updateDependents();
|
||||
};
|
||||
|
||||
bool inUserBounds(const PolyVox::SimpleVolume<uint8_t>* vol, PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle,
|
||||
|
|
|
@ -114,22 +114,13 @@ void RenderableShapeEntityItem::render(RenderArgs* args) {
|
|||
auto outColor = _procedural->getColor(color);
|
||||
outColor.a *= _procedural->isFading() ? Interpolate::calculateFadeRatio(_procedural->getFadeStartTime()) : 1.0f;
|
||||
batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a);
|
||||
if (render::ShapeKey(args->_globalShapeKey).isWireframe()) {
|
||||
DependencyManager::get<GeometryCache>()->renderWireShape(batch, MAPPING[_shape]);
|
||||
} else {
|
||||
DependencyManager::get<GeometryCache>()->renderShape(batch, MAPPING[_shape]);
|
||||
}
|
||||
DependencyManager::get<GeometryCache>()->renderShape(batch, MAPPING[_shape]);
|
||||
} else {
|
||||
// FIXME, support instanced multi-shape rendering using multidraw indirect
|
||||
color.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
auto pipeline = color.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline();
|
||||
|
||||
if (render::ShapeKey(args->_globalShapeKey).isWireframe()) {
|
||||
geometryCache->renderWireShapeInstance(batch, MAPPING[_shape], color, pipeline);
|
||||
} else {
|
||||
geometryCache->renderSolidShapeInstance(batch, MAPPING[_shape], color, pipeline);
|
||||
}
|
||||
geometryCache->renderSolidShapeInstance(batch, MAPPING[_shape], color, pipeline);
|
||||
}
|
||||
|
||||
static const auto triCount = DependencyManager::get<GeometryCache>()->getShapeTriangleCount(MAPPING[_shape]);
|
||||
|
|
|
@ -216,7 +216,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
|
|||
|
||||
if (!_texture) {
|
||||
auto webSurface = _webSurface;
|
||||
_texture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda()));
|
||||
_texture = gpu::TexturePointer(gpu::Texture::createExternal2D(OffscreenQmlSurface::getDiscardLambda()));
|
||||
_texture->setSource(__FUNCTION__);
|
||||
}
|
||||
OffscreenQmlSurface::TextureAndFence newTextureAndFence;
|
||||
|
|
|
@ -15,13 +15,11 @@
|
|||
#define hifi_EntitiesScriptEngineProvider_h
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QFuture>
|
||||
#include "EntityItemID.h"
|
||||
|
||||
class EntitiesScriptEngineProvider {
|
||||
public:
|
||||
virtual void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params = QStringList()) = 0;
|
||||
virtual QFuture<QVariant> getLocalEntityScriptDetails(const EntityItemID& entityID) = 0;
|
||||
};
|
||||
|
||||
#endif // hifi_EntitiesScriptEngineProvider_h
|
||||
#endif // hifi_EntitiesScriptEngineProvider_h
|
|
@ -655,11 +655,13 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
|
||||
|
||||
// pack SimulationOwner and terse update properties near each other
|
||||
|
||||
// NOTE: the server is authoritative for changes to simOwnerID so we always unpack ownership data
|
||||
// even when we would otherwise ignore the rest of the packet.
|
||||
|
||||
bool filterRejection = false;
|
||||
if (propertyFlags.getHasProperty(PROP_SIMULATION_OWNER)) {
|
||||
|
||||
QByteArray simOwnerData;
|
||||
int bytes = OctreePacketData::unpackDataFromBytes(dataAt, simOwnerData);
|
||||
SimulationOwner newSimOwner;
|
||||
|
@ -1877,7 +1879,6 @@ void EntityItem::setSimulationOwner(const SimulationOwner& owner) {
|
|||
}
|
||||
|
||||
void EntityItem::updateSimulationOwner(const SimulationOwner& owner) {
|
||||
// NOTE: this method only used by EntityServer. The Interface uses special code in readEntityDataFromBuffer().
|
||||
if (wantTerseEditLogging() && _simulationOwner != owner) {
|
||||
qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << owner;
|
||||
}
|
||||
|
@ -1893,9 +1894,8 @@ void EntityItem::clearSimulationOwnership() {
|
|||
}
|
||||
|
||||
_simulationOwner.clear();
|
||||
// don't bother setting the DIRTY_SIMULATOR_ID flag because:
|
||||
// (a) when entity-server calls clearSimulationOwnership() the dirty-flags are meaningless (only used by interface)
|
||||
// (b) the interface only calls clearSimulationOwnership() in a context that already knows best about dirty flags
|
||||
// don't bother setting the DIRTY_SIMULATOR_ID flag because clearSimulationOwnership()
|
||||
// is only ever called on the entity-server and the flags are only used client-side
|
||||
//_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID;
|
||||
|
||||
}
|
||||
|
|
|
@ -49,6 +49,13 @@ EntityItemProperties::EntityItemProperties(EntityPropertyFlags desiredProperties
|
|||
|
||||
}
|
||||
|
||||
void EntityItemProperties::setSittingPoints(const QVector<SittingPoint>& sittingPoints) {
|
||||
_sittingPoints.clear();
|
||||
foreach (SittingPoint sitPoint, sittingPoints) {
|
||||
_sittingPoints.append(sitPoint);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityItemProperties::calculateNaturalPosition(const glm::vec3& min, const glm::vec3& max) {
|
||||
glm::vec3 halfDimension = (max - min) / 2.0f;
|
||||
_naturalPosition = max - halfDimension;
|
||||
|
@ -539,6 +546,20 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures);
|
||||
}
|
||||
|
||||
// Sitting properties support
|
||||
if (!skipDefaults && !strictSemantics) {
|
||||
QScriptValue sittingPoints = engine->newObject();
|
||||
for (int i = 0; i < _sittingPoints.size(); ++i) {
|
||||
QScriptValue sittingPoint = engine->newObject();
|
||||
sittingPoint.setProperty("name", _sittingPoints.at(i).name);
|
||||
sittingPoint.setProperty("position", vec3toScriptValue(engine, _sittingPoints.at(i).position));
|
||||
sittingPoint.setProperty("rotation", quatToScriptValue(engine, _sittingPoints.at(i).rotation));
|
||||
sittingPoints.setProperty(i, sittingPoint);
|
||||
}
|
||||
sittingPoints.setProperty("length", _sittingPoints.size());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_ALWAYS(sittingPoints, sittingPoints); // gettable, but not settable
|
||||
}
|
||||
|
||||
if (!skipDefaults && !strictSemantics) {
|
||||
AABox aaBox = getAABox();
|
||||
QScriptValue boundingBox = engine->newObject();
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <QString>
|
||||
|
||||
#include <AACube.h>
|
||||
#include <FBXReader.h> // for SittingPoint
|
||||
#include <NumericalConstants.h>
|
||||
#include <PropertyFlags.h>
|
||||
#include <OctreeConstants.h>
|
||||
|
@ -254,6 +255,8 @@ public:
|
|||
void clearID() { _id = UNKNOWN_ENTITY_ID; _idSet = false; }
|
||||
void markAllChanged();
|
||||
|
||||
void setSittingPoints(const QVector<SittingPoint>& sittingPoints);
|
||||
|
||||
const glm::vec3& getNaturalDimensions() const { return _naturalDimensions; }
|
||||
void setNaturalDimensions(const glm::vec3& value) { _naturalDimensions = value; }
|
||||
|
||||
|
@ -322,6 +325,7 @@ private:
|
|||
|
||||
// NOTE: The following are pseudo client only properties. They are only used in clients which can access
|
||||
// properties of model geometry. But these properties are not serialized like other properties.
|
||||
QVector<SittingPoint> _sittingPoints;
|
||||
QVariantMap _textureNames;
|
||||
glm::vec3 _naturalDimensions;
|
||||
glm::vec3 _naturalPosition;
|
||||
|
|
|
@ -8,15 +8,8 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include "EntityScriptingInterface.h"
|
||||
|
||||
#include <QFutureWatcher>
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
|
||||
#include "EntityItemID.h"
|
||||
#include <VariantMapToScriptValue.h>
|
||||
#include <SharedUtil.h>
|
||||
|
@ -296,11 +289,13 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit
|
|||
|
||||
results = entity->getProperties(desiredProperties);
|
||||
|
||||
// TODO: improve naturalDimensions in the future,
|
||||
// for now we've added this hack for setting natural dimensions of models
|
||||
// TODO: improve sitting points and naturalDimensions in the future,
|
||||
// for now we've included the old sitting points model behavior for entity types that are models
|
||||
// we've also added this hack for setting natural dimensions of models
|
||||
if (entity->getType() == EntityTypes::Model) {
|
||||
const FBXGeometry* geometry = _entityTree->getGeometryForEntity(entity);
|
||||
if (geometry) {
|
||||
results.setSittingPoints(geometry->sittingPoints);
|
||||
Extents meshExtents = geometry->getUnscaledMeshExtents();
|
||||
results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum);
|
||||
results.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum);
|
||||
|
@ -685,118 +680,6 @@ bool EntityScriptingInterface::reloadServerScripts(QUuid entityID) {
|
|||
return client->reloadServerScript(entityID);
|
||||
}
|
||||
|
||||
bool EntityPropertyMetadataRequest::script(EntityItemID entityID, QScriptValue handler) {
|
||||
using LocalScriptStatusRequest = QFutureWatcher<QVariant>;
|
||||
|
||||
LocalScriptStatusRequest* request = new LocalScriptStatusRequest;
|
||||
QObject::connect(request, &LocalScriptStatusRequest::finished, _engine, [=]() mutable {
|
||||
auto details = request->result().toMap();
|
||||
QScriptValue err, result;
|
||||
if (details.contains("isError")) {
|
||||
if (!details.contains("message")) {
|
||||
details["message"] = details["errorInfo"];
|
||||
}
|
||||
err = _engine->makeError(_engine->toScriptValue(details));
|
||||
} else {
|
||||
details["success"] = true;
|
||||
result = _engine->toScriptValue(details);
|
||||
}
|
||||
callScopedHandlerObject(handler, err, result);
|
||||
request->deleteLater();
|
||||
});
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
entityScriptingInterface->withEntitiesScriptEngine([&](EntitiesScriptEngineProvider* entitiesScriptEngine) {
|
||||
if (entitiesScriptEngine) {
|
||||
request->setFuture(entitiesScriptEngine->getLocalEntityScriptDetails(entityID));
|
||||
}
|
||||
});
|
||||
if (!request->isStarted()) {
|
||||
request->deleteLater();
|
||||
callScopedHandlerObject(handler, _engine->makeError("Entities Scripting Provider unavailable", "InternalError"), QScriptValue());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EntityPropertyMetadataRequest::serverScripts(EntityItemID entityID, QScriptValue handler) {
|
||||
auto client = DependencyManager::get<EntityScriptClient>();
|
||||
auto request = client->createScriptStatusRequest(entityID);
|
||||
QPointer<BaseScriptEngine> engine = _engine;
|
||||
QObject::connect(request, &GetScriptStatusRequest::finished, _engine, [=](GetScriptStatusRequest* request) mutable {
|
||||
auto engine = _engine;
|
||||
if (!engine) {
|
||||
qCDebug(entities) << __FUNCTION__ << " -- engine destroyed while inflight" << entityID;
|
||||
return;
|
||||
}
|
||||
QVariantMap details;
|
||||
details["success"] = request->getResponseReceived();
|
||||
details["isRunning"] = request->getIsRunning();
|
||||
details["status"] = EntityScriptStatus_::valueToKey(request->getStatus()).toLower();
|
||||
details["errorInfo"] = request->getErrorInfo();
|
||||
|
||||
QScriptValue err, result;
|
||||
if (!details["success"].toBool()) {
|
||||
if (!details.contains("message") && details.contains("errorInfo")) {
|
||||
details["message"] = details["errorInfo"];
|
||||
}
|
||||
if (details["message"].toString().isEmpty()) {
|
||||
details["message"] = "entity server script details not found";
|
||||
}
|
||||
err = engine->makeError(engine->toScriptValue(details));
|
||||
} else {
|
||||
result = engine->toScriptValue(details);
|
||||
}
|
||||
callScopedHandlerObject(handler, err, result);
|
||||
request->deleteLater();
|
||||
});
|
||||
request->start();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EntityScriptingInterface::queryPropertyMetadata(QUuid entityID, QScriptValue property, QScriptValue scopeOrCallback, QScriptValue methodOrName) {
|
||||
auto name = property.toString();
|
||||
auto handler = makeScopedHandlerObject(scopeOrCallback, methodOrName);
|
||||
QPointer<BaseScriptEngine> engine = dynamic_cast<BaseScriptEngine*>(handler.engine());
|
||||
if (!engine) {
|
||||
qCDebug(entities) << "queryPropertyMetadata without detectable engine" << entityID << name;
|
||||
return false;
|
||||
}
|
||||
#ifdef DEBUG_ENGINE_STATE
|
||||
connect(engine, &QObject::destroyed, this, [=]() {
|
||||
qDebug() << "queryPropertyMetadata -- engine destroyed!" << (!engine ? "nullptr" : "engine");
|
||||
});
|
||||
#endif
|
||||
if (!handler.property("callback").isFunction()) {
|
||||
qDebug() << "!handler.callback.isFunction" << engine;
|
||||
engine->raiseException(engine->makeError("callback is not a function", "TypeError"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTE: this approach is a work-in-progress and for now just meant to work 100% correctly and provide
|
||||
// some initial structure for organizing metadata adapters around.
|
||||
|
||||
// The extra layer of indirection is *essential* because in real world conditions errors are often introduced
|
||||
// by accident and sometimes without exact memory of "what just changed."
|
||||
|
||||
// Here the scripter only needs to know an entityID and a property name -- which means all scripters can
|
||||
// level this method when stuck in dead-end scenarios or to learn more about "magic" Entity properties
|
||||
// like .script that work in terms of side-effects.
|
||||
|
||||
// This is an async callback pattern -- so if needed C++ can easily throttle or restrict queries later.
|
||||
|
||||
EntityPropertyMetadataRequest request(engine);
|
||||
|
||||
if (name == "script") {
|
||||
return request.script(entityID, handler);
|
||||
} else if (name == "serverScripts") {
|
||||
return request.serverScripts(entityID, handler);
|
||||
} else {
|
||||
engine->raiseException(engine->makeError("metadata for property " + name + " is not yet queryable"));
|
||||
engine->maybeEmitUncaughtException(__FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool EntityScriptingInterface::getServerScriptStatus(QUuid entityID, QScriptValue callback) {
|
||||
auto client = DependencyManager::get<EntityScriptClient>();
|
||||
auto request = client->createScriptStatusRequest(entityID);
|
||||
|
@ -932,7 +815,8 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra
|
|||
}
|
||||
}
|
||||
|
||||
bool EntityScriptingInterface::polyVoxWorker(QUuid entityID, std::function<bool(PolyVoxEntityItem&)> actor) {
|
||||
bool EntityScriptingInterface::setVoxels(QUuid entityID,
|
||||
std::function<bool(PolyVoxEntityItem&)> actor) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
if (!_entityTree) {
|
||||
|
@ -998,9 +882,11 @@ bool EntityScriptingInterface::setPoints(QUuid entityID, std::function<bool(Line
|
|||
return success;
|
||||
}
|
||||
|
||||
|
||||
bool EntityScriptingInterface::setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
return polyVoxWorker(entityID, [center, radius, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
|
||||
return setVoxels(entityID, [center, radius, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return polyVoxEntity.setSphere(center, radius, value);
|
||||
});
|
||||
}
|
||||
|
@ -1010,7 +896,7 @@ bool EntityScriptingInterface::setVoxelCapsule(QUuid entityID,
|
|||
float radius, int value) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
return polyVoxWorker(entityID, [start, end, radius, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return setVoxels(entityID, [start, end, radius, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return polyVoxEntity.setCapsule(start, end, radius, value);
|
||||
});
|
||||
}
|
||||
|
@ -1018,7 +904,7 @@ bool EntityScriptingInterface::setVoxelCapsule(QUuid entityID,
|
|||
bool EntityScriptingInterface::setVoxel(QUuid entityID, const glm::vec3& position, int value) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
return polyVoxWorker(entityID, [position, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return setVoxels(entityID, [position, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return polyVoxEntity.setVoxelInVolume(position, value);
|
||||
});
|
||||
}
|
||||
|
@ -1026,7 +912,7 @@ bool EntityScriptingInterface::setVoxel(QUuid entityID, const glm::vec3& positio
|
|||
bool EntityScriptingInterface::setAllVoxels(QUuid entityID, int value) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
return polyVoxWorker(entityID, [value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return setVoxels(entityID, [value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return polyVoxEntity.setAll(value);
|
||||
});
|
||||
}
|
||||
|
@ -1035,30 +921,11 @@ bool EntityScriptingInterface::setVoxelsInCuboid(QUuid entityID, const glm::vec3
|
|||
const glm::vec3& cuboidSize, int value) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
return polyVoxWorker(entityID, [lowPosition, cuboidSize, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return setVoxels(entityID, [lowPosition, cuboidSize, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return polyVoxEntity.setCuboid(lowPosition, cuboidSize, value);
|
||||
});
|
||||
}
|
||||
|
||||
void EntityScriptingInterface::voxelsToMesh(QUuid entityID, QScriptValue callback) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
bool success { false };
|
||||
QScriptValue mesh { false };
|
||||
|
||||
polyVoxWorker(entityID, [&](PolyVoxEntityItem& polyVoxEntity) mutable {
|
||||
if (polyVoxEntity.getOnCount() == 0) {
|
||||
success = true;
|
||||
} else {
|
||||
success = polyVoxEntity.getMeshAsScriptValue(callback.engine(), mesh);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
QScriptValueList args { mesh, success };
|
||||
callback.call(QScriptValue(), args);
|
||||
}
|
||||
|
||||
bool EntityScriptingInterface::setAllPoints(QUuid entityID, const QVector<glm::vec3>& points) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
|
@ -1674,20 +1541,3 @@ bool EntityScriptingInterface::AABoxIntersectsCapsule(const glm::vec3& low, cons
|
|||
AABox aaBox(low, dimensions);
|
||||
return aaBox.findCapsulePenetration(start, end, radius, penetration);
|
||||
}
|
||||
|
||||
glm::mat4 EntityScriptingInterface::getEntityTransform(const QUuid& entityID) {
|
||||
glm::mat4 result;
|
||||
if (_entityTree) {
|
||||
_entityTree->withReadLock([&] {
|
||||
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(entityID));
|
||||
if (entity) {
|
||||
glm::mat4 translation = glm::translate(entity->getPosition());
|
||||
glm::mat4 rotation = glm::mat4_cast(entity->getRotation());
|
||||
glm::mat4 registration = glm::translate(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT -
|
||||
entity->getRegistrationPoint());
|
||||
result = translation * rotation * registration;
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -34,23 +34,7 @@
|
|||
#include "EntitiesScriptEngineProvider.h"
|
||||
#include "EntityItemProperties.h"
|
||||
|
||||
#include "BaseScriptEngine.h"
|
||||
|
||||
class EntityTree;
|
||||
class MeshProxy;
|
||||
|
||||
// helper factory to compose standardized, async metadata queries for "magic" Entity properties
|
||||
// like .script and .serverScripts. This is used for automated testing of core scripting features
|
||||
// as well as to provide early adopters a self-discoverable, consistent way to diagnose common
|
||||
// problems with their own Entity scripts.
|
||||
class EntityPropertyMetadataRequest {
|
||||
public:
|
||||
EntityPropertyMetadataRequest(BaseScriptEngine* engine) : _engine(engine) {};
|
||||
bool script(EntityItemID entityID, QScriptValue handler);
|
||||
bool serverScripts(EntityItemID entityID, QScriptValue handler);
|
||||
private:
|
||||
QPointer<BaseScriptEngine> _engine;
|
||||
};
|
||||
|
||||
class RayToEntityIntersectionResult {
|
||||
public:
|
||||
|
@ -83,7 +67,6 @@ class EntityScriptingInterface : public OctreeScriptingInterface, public Depende
|
|||
Q_PROPERTY(float costMultiplier READ getCostMultiplier WRITE setCostMultiplier)
|
||||
Q_PROPERTY(QUuid keyboardFocusEntity READ getKeyboardFocusEntity WRITE setKeyboardFocusEntity)
|
||||
|
||||
friend EntityPropertyMetadataRequest;
|
||||
public:
|
||||
EntityScriptingInterface(bool bidOnSimulationOwnership);
|
||||
|
||||
|
@ -228,26 +211,6 @@ public slots:
|
|||
Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue());
|
||||
|
||||
Q_INVOKABLE bool reloadServerScripts(QUuid entityID);
|
||||
|
||||
/**jsdoc
|
||||
* Query additional metadata for "magic" Entity properties like `script` and `serverScripts`.
|
||||
*
|
||||
* @function Entities.queryPropertyMetadata
|
||||
* @param {EntityID} entityID The ID of the entity.
|
||||
* @param {string} property The name of the property extended metadata is wanted for.
|
||||
* @param {ResultCallback} callback Executes callback(err, result) with the query results.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Query additional metadata for "magic" Entity properties like `script` and `serverScripts`.
|
||||
*
|
||||
* @function Entities.queryPropertyMetadata
|
||||
* @param {EntityID} entityID The ID of the entity.
|
||||
* @param {string} property The name of the property extended metadata is wanted for.
|
||||
* @param {Object} thisObject The scoping "this" context that callback will be executed within.
|
||||
* @param {ResultCallback} callbackOrMethodName Executes thisObject[callbackOrMethodName](err, result) with the query results.
|
||||
*/
|
||||
Q_INVOKABLE bool queryPropertyMetadata(QUuid entityID, QScriptValue property, QScriptValue scopeOrCallback, QScriptValue methodOrName = QScriptValue());
|
||||
|
||||
Q_INVOKABLE bool getServerScriptStatus(QUuid entityID, QScriptValue callback);
|
||||
|
||||
Q_INVOKABLE void setLightsArePickable(bool value);
|
||||
|
@ -266,7 +229,6 @@ public slots:
|
|||
Q_INVOKABLE bool setAllVoxels(QUuid entityID, int value);
|
||||
Q_INVOKABLE bool setVoxelsInCuboid(QUuid entityID, const glm::vec3& lowPosition,
|
||||
const glm::vec3& cuboidSize, int value);
|
||||
Q_INVOKABLE void voxelsToMesh(QUuid entityID, QScriptValue callback);
|
||||
|
||||
Q_INVOKABLE bool setAllPoints(QUuid entityID, const QVector<glm::vec3>& points);
|
||||
Q_INVOKABLE bool appendPoint(QUuid entityID, const glm::vec3& point);
|
||||
|
@ -331,15 +293,6 @@ public slots:
|
|||
const glm::vec3& start, const glm::vec3& end, float radius);
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* Returns object to world transform, excluding scale
|
||||
*
|
||||
* @function Entities.getEntityTransform
|
||||
* @param {EntityID} entityID The ID of the entity whose transform is to be returned
|
||||
* @return {Mat4} Entity's object to world transform, excluding scale
|
||||
*/
|
||||
Q_INVOKABLE glm::mat4 getEntityTransform(const QUuid& entityID);
|
||||
|
||||
signals:
|
||||
void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
|
||||
|
||||
|
@ -370,14 +323,9 @@ signals:
|
|||
|
||||
void webEventReceived(const EntityItemID& entityItemID, const QVariant& message);
|
||||
|
||||
protected:
|
||||
void withEntitiesScriptEngine(std::function<void(EntitiesScriptEngineProvider*)> function) {
|
||||
std::lock_guard<std::recursive_mutex> lock(_entitiesScriptEngineLock);
|
||||
function(_entitiesScriptEngine);
|
||||
};
|
||||
private:
|
||||
bool actionWorker(const QUuid& entityID, std::function<bool(EntitySimulationPointer, EntityItemPointer)> actor);
|
||||
bool polyVoxWorker(QUuid entityID, std::function<bool(PolyVoxEntityItem&)> actor);
|
||||
bool setVoxels(QUuid entityID, std::function<bool(PolyVoxEntityItem&)> actor);
|
||||
bool setPoints(QUuid entityID, std::function<bool(LineEntityItem&)> actor);
|
||||
void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties);
|
||||
|
||||
|
|
|
@ -242,7 +242,3 @@ const QByteArray PolyVoxEntityItem::getVoxelData() const {
|
|||
});
|
||||
return voxelDataCopy;
|
||||
}
|
||||
|
||||
bool PolyVoxEntityItem::getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -57,8 +57,6 @@ class PolyVoxEntityItem : public EntityItem {
|
|||
virtual void setVoxelData(QByteArray voxelData);
|
||||
virtual const QByteArray getVoxelData() const;
|
||||
|
||||
virtual int getOnCount() const { return 0; }
|
||||
|
||||
enum PolyVoxSurfaceStyle {
|
||||
SURFACE_MARCHING_CUBES,
|
||||
SURFACE_CUBIC,
|
||||
|
@ -133,9 +131,7 @@ class PolyVoxEntityItem : public EntityItem {
|
|||
virtual void rebakeMesh() {};
|
||||
|
||||
void setVoxelDataDirty(bool value) { withWriteLock([&] { _voxelDataDirty = value; }); }
|
||||
virtual void recomputeMesh() {};
|
||||
|
||||
virtual bool getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result);
|
||||
virtual void getMesh() {}; // recompute mesh
|
||||
|
||||
protected:
|
||||
glm::vec3 _voxelVolumeSize; // this is always 3 bytes
|
||||
|
|
|
@ -14,11 +14,9 @@
|
|||
|
||||
#include <QtScript/QScriptEngine>
|
||||
|
||||
#include <OctreeElement.h>
|
||||
|
||||
//#include "EntityItemProperties.h"
|
||||
#include "EntityPropertyFlags.h"
|
||||
|
||||
|
||||
class EntityItemProperties;
|
||||
class EncodeBitstreamParams;
|
||||
class OctreePacketData;
|
||||
|
@ -26,6 +24,31 @@ class EntityTreeElementExtraEncodeData;
|
|||
class ReadBitstreamToTreeParams;
|
||||
using EntityTreeElementExtraEncodeDataPointer = std::shared_ptr<EntityTreeElementExtraEncodeData>;
|
||||
|
||||
#include <OctreeElement.h>
|
||||
|
||||
/*
|
||||
#include <stdint.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/extented_min_max.hpp>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QVector>
|
||||
#include <QString>
|
||||
|
||||
#include <AACube.h>
|
||||
#include <FBXReader.h> // for SittingPoint
|
||||
#include <PropertyFlags.h>
|
||||
#include <OctreeConstants.h>
|
||||
#include <ShapeInfo.h>
|
||||
|
||||
#include "EntityItemID.h"
|
||||
#include "PropertyGroupMacros.h"
|
||||
#include "EntityTypes.h"
|
||||
*/
|
||||
|
||||
//typedef PropertyFlags<EntityPropertyList> EntityPropertyFlags;
|
||||
|
||||
|
||||
class PropertyGroup {
|
||||
public:
|
||||
|
|
|
@ -1468,9 +1468,6 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
|||
// Create the Material Library
|
||||
consolidateFBXMaterials(mapping);
|
||||
|
||||
// We can't allow the scaling of a given image to different sizes, because the hash used for the KTX cache is based on the original image
|
||||
// Allowing scaling of the same image to different sizes would cause different KTX files to target the same cache key
|
||||
#if 0
|
||||
// HACK: until we get proper LOD management we're going to cap model textures
|
||||
// according to how many unique textures the model uses:
|
||||
// 1 - 8 textures --> 2048
|
||||
|
@ -1484,7 +1481,6 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
|||
int numTextures = uniqueTextures.size();
|
||||
const int MAX_NUM_TEXTURES_AT_MAX_RESOLUTION = 8;
|
||||
int maxWidth = sqrt(MAX_NUM_PIXELS_FOR_FBX_TEXTURE);
|
||||
|
||||
if (numTextures > MAX_NUM_TEXTURES_AT_MAX_RESOLUTION) {
|
||||
int numTextureThreshold = MAX_NUM_TEXTURES_AT_MAX_RESOLUTION;
|
||||
const int MIN_MIP_TEXTURE_WIDTH = 64;
|
||||
|
@ -1498,7 +1494,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
|||
material.setMaxNumPixelsPerTexture(maxWidth * maxWidth);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
geometry.materials = _fbxMaterials;
|
||||
|
||||
// see if any materials have texture children
|
||||
|
@ -1799,6 +1795,19 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
|||
}
|
||||
geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString());
|
||||
|
||||
// Add sitting points
|
||||
QVariantHash sittingPoints = mapping.value("sit").toHash();
|
||||
for (QVariantHash::const_iterator it = sittingPoints.constBegin(); it != sittingPoints.constEnd(); it++) {
|
||||
SittingPoint sittingPoint;
|
||||
sittingPoint.name = it.key();
|
||||
|
||||
QVariantList properties = it->toList();
|
||||
sittingPoint.position = parseVec3(properties.at(0).toString());
|
||||
sittingPoint.rotation = glm::quat(glm::radians(parseVec3(properties.at(1).toString())));
|
||||
|
||||
geometry.sittingPoints.append(sittingPoint);
|
||||
}
|
||||
|
||||
// attempt to map any meshes to a named model
|
||||
for (QHash<QString, int>::const_iterator m = meshIDsToMeshIndices.constBegin();
|
||||
m != meshIDsToMeshIndices.constEnd(); m++) {
|
||||
|
|
|
@ -265,6 +265,24 @@ public:
|
|||
Q_DECLARE_METATYPE(FBXAnimationFrame)
|
||||
Q_DECLARE_METATYPE(QVector<FBXAnimationFrame>)
|
||||
|
||||
/// A point where an avatar can sit
|
||||
class SittingPoint {
|
||||
public:
|
||||
QString name;
|
||||
glm::vec3 position; // relative postion
|
||||
glm::quat rotation; // relative orientation
|
||||
};
|
||||
|
||||
inline bool operator==(const SittingPoint& lhs, const SittingPoint& rhs)
|
||||
{
|
||||
return (lhs.name == rhs.name) && (lhs.position == rhs.position) && (lhs.rotation == rhs.rotation);
|
||||
}
|
||||
|
||||
inline bool operator!=(const SittingPoint& lhs, const SittingPoint& rhs)
|
||||
{
|
||||
return (lhs.name != rhs.name) || (lhs.position != rhs.position) || (lhs.rotation != rhs.rotation);
|
||||
}
|
||||
|
||||
/// A set of meshes extracted from an FBX document.
|
||||
class FBXGeometry {
|
||||
public:
|
||||
|
@ -302,6 +320,8 @@ public:
|
|||
|
||||
glm::vec3 palmDirection;
|
||||
|
||||
QVector<SittingPoint> sittingPoints;
|
||||
|
||||
glm::vec3 neckPivot;
|
||||
|
||||
Extents bindExtents;
|
||||
|
|
|
@ -54,8 +54,7 @@ template<class T> QVariant readBinaryArray(QDataStream& in, int& position) {
|
|||
in.readRawData(compressed.data() + sizeof(quint32), compressedLength);
|
||||
position += compressedLength;
|
||||
arrayData = qUncompress(compressed);
|
||||
if (arrayData.isEmpty() ||
|
||||
(unsigned int)arrayData.size() != (sizeof(T) * arrayLength)) { // answers empty byte array if corrupt
|
||||
if (arrayData.isEmpty() || arrayData.size() != (sizeof(T) * arrayLength)) { // answers empty byte array if corrupt
|
||||
throw QString("corrupt fbx file");
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -267,7 +267,7 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
|
|||
}
|
||||
if (token == "map_Kd") {
|
||||
currentMaterial.diffuseTextureFilename = filename;
|
||||
} else if( token == "map_Ks" ) {
|
||||
} else {
|
||||
currentMaterial.specularTextureFilename = filename;
|
||||
}
|
||||
}
|
||||
|
@ -546,7 +546,6 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping,
|
|||
QString queryPart = _url.query();
|
||||
bool suppressMaterialsHack = queryPart.contains("hifiusemat"); // If this appears in query string, don't fetch mtl even if used.
|
||||
OBJMaterial& preDefinedMaterial = materials[SMART_DEFAULT_MATERIAL_NAME];
|
||||
preDefinedMaterial.used = true;
|
||||
if (suppressMaterialsHack) {
|
||||
needsMaterialLibrary = preDefinedMaterial.userSpecifiesUV = false; // I said it was a hack...
|
||||
}
|
||||
|
@ -595,8 +594,8 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping,
|
|||
}
|
||||
|
||||
foreach (QString materialID, materials.keys()) {
|
||||
OBJMaterial& objMaterial = materials[materialID];
|
||||
if (!objMaterial.used) {
|
||||
OBJMaterial& objMaterial = materials[materialID];
|
||||
if (!objMaterial.used) {
|
||||
continue;
|
||||
}
|
||||
geometry.materials[materialID] = FBXMaterial(objMaterial.diffuseColor,
|
||||
|
@ -612,9 +611,6 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping,
|
|||
if (!objMaterial.diffuseTextureFilename.isEmpty()) {
|
||||
fbxMaterial.albedoTexture.filename = objMaterial.diffuseTextureFilename;
|
||||
}
|
||||
if (!objMaterial.specularTextureFilename.isEmpty()) {
|
||||
fbxMaterial.specularTexture.filename = objMaterial.specularTextureFilename;
|
||||
}
|
||||
|
||||
modelMaterial->setEmissive(fbxMaterial.emissiveColor);
|
||||
modelMaterial->setAlbedo(fbxMaterial.diffuseColor);
|
||||
|
|
|
@ -58,7 +58,7 @@ public:
|
|||
QByteArray specularTextureFilename;
|
||||
bool used { false };
|
||||
bool userSpecifiesUV { false };
|
||||
OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f) {}
|
||||
OBJMaterial() : shininess(96.0f), opacity(1.0f), diffuseColor(1.0f), specularColor(1.0f) {}
|
||||
};
|
||||
|
||||
class OBJReader: public QObject { // QObject so we can make network requests.
|
||||
|
|
|
@ -1,148 +0,0 @@
|
|||
//
|
||||
// OBJWriter.cpp
|
||||
// libraries/fbx/src/
|
||||
//
|
||||
// Created by Seth Alves on 2017-1-27.
|
||||
// Copyright 2017 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 <QFile>
|
||||
#include <QFileInfo>
|
||||
#include "model/Geometry.h"
|
||||
#include "OBJWriter.h"
|
||||
#include "ModelFormatLogging.h"
|
||||
|
||||
static QString formatFloat(double n) {
|
||||
// limit precision to 6, but don't output trailing zeros.
|
||||
QString s = QString::number(n, 'f', 6);
|
||||
while (s.endsWith("0")) {
|
||||
s.remove(s.size() - 1, 1);
|
||||
}
|
||||
if (s.endsWith(".")) {
|
||||
s.remove(s.size() - 1, 1);
|
||||
}
|
||||
|
||||
// check for non-numbers. if we get NaN or inf or scientific notation, just return 0
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
auto c = s.at(i).toLatin1();
|
||||
if (c != '-' &&
|
||||
c != '.' &&
|
||||
(c < '0' || c > '9')) {
|
||||
qCDebug(modelformat) << "OBJWriter zeroing bad vertex coordinate:" << s << "because of" << c;
|
||||
return QString("0");
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
bool writeOBJToTextStream(QTextStream& out, QList<MeshPointer> meshes) {
|
||||
// each mesh's vertices are numbered from zero. We're combining all their vertices into one list here,
|
||||
// so keep track of the start index for each mesh.
|
||||
QList<int> meshVertexStartOffset;
|
||||
int currentVertexStartOffset = 0;
|
||||
|
||||
// write out all vertices
|
||||
foreach (const MeshPointer& mesh, meshes) {
|
||||
meshVertexStartOffset.append(currentVertexStartOffset);
|
||||
const gpu::BufferView& vertexBuffer = mesh->getVertexBuffer();
|
||||
int vertexCount = 0;
|
||||
gpu::BufferView::Iterator<const glm::vec3> vertexItr = vertexBuffer.cbegin<const glm::vec3>();
|
||||
while (vertexItr != vertexBuffer.cend<const glm::vec3>()) {
|
||||
glm::vec3 v = *vertexItr;
|
||||
out << "v ";
|
||||
out << formatFloat(v[0]) << " ";
|
||||
out << formatFloat(v[1]) << " ";
|
||||
out << formatFloat(v[2]) << "\n";
|
||||
vertexItr++;
|
||||
vertexCount++;
|
||||
}
|
||||
currentVertexStartOffset += vertexCount;
|
||||
}
|
||||
out << "\n";
|
||||
|
||||
// write out faces
|
||||
int nth = 0;
|
||||
foreach (const MeshPointer& mesh, meshes) {
|
||||
currentVertexStartOffset = meshVertexStartOffset.takeFirst();
|
||||
|
||||
const gpu::BufferView& partBuffer = mesh->getPartBuffer();
|
||||
const gpu::BufferView& indexBuffer = mesh->getIndexBuffer();
|
||||
|
||||
model::Index partCount = (model::Index)mesh->getNumParts();
|
||||
for (int partIndex = 0; partIndex < partCount; partIndex++) {
|
||||
const model::Mesh::Part& part = partBuffer.get<model::Mesh::Part>(partIndex);
|
||||
|
||||
out << "g part-" << nth++ << "\n";
|
||||
|
||||
// model::Mesh::TRIANGLES
|
||||
// TODO -- handle other formats
|
||||
gpu::BufferView::Iterator<const uint32_t> indexItr = indexBuffer.cbegin<uint32_t>();
|
||||
indexItr += part._startIndex;
|
||||
|
||||
int indexCount = 0;
|
||||
while (indexItr != indexBuffer.cend<uint32_t>() && indexCount < part._numIndices) {
|
||||
uint32_t index0 = *indexItr;
|
||||
indexItr++;
|
||||
indexCount++;
|
||||
if (indexItr == indexBuffer.cend<uint32_t>() || indexCount >= part._numIndices) {
|
||||
qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3";
|
||||
break;
|
||||
}
|
||||
uint32_t index1 = *indexItr;
|
||||
indexItr++;
|
||||
indexCount++;
|
||||
if (indexItr == indexBuffer.cend<uint32_t>() || indexCount >= part._numIndices) {
|
||||
qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3";
|
||||
break;
|
||||
}
|
||||
uint32_t index2 = *indexItr;
|
||||
indexItr++;
|
||||
indexCount++;
|
||||
|
||||
out << "f ";
|
||||
out << currentVertexStartOffset + index0 + 1 << " ";
|
||||
out << currentVertexStartOffset + index1 + 1 << " ";
|
||||
out << currentVertexStartOffset + index2 + 1 << "\n";
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeOBJToFile(QString path, QList<MeshPointer> meshes) {
|
||||
if (QFileInfo(path).exists() && !QFile::remove(path)) {
|
||||
qCDebug(modelformat) << "OBJ writer failed, file exists:" << path;
|
||||
return false;
|
||||
}
|
||||
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
qCDebug(modelformat) << "OBJ writer failed to open output file:" << path;
|
||||
return false;
|
||||
}
|
||||
|
||||
QTextStream outStream(&file);
|
||||
|
||||
bool success;
|
||||
success = writeOBJToTextStream(outStream, meshes);
|
||||
|
||||
file.close();
|
||||
return success;
|
||||
}
|
||||
|
||||
QString writeOBJToString(QList<MeshPointer> meshes) {
|
||||
QString result;
|
||||
QTextStream outStream(&result, QIODevice::ReadWrite);
|
||||
bool success;
|
||||
success = writeOBJToTextStream(outStream, meshes);
|
||||
if (success) {
|
||||
return result;
|
||||
}
|
||||
return QString("");
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
//
|
||||
// OBJWriter.h
|
||||
// libraries/fbx/src/
|
||||
//
|
||||
// Created by Seth Alves on 2017-1-27.
|
||||
// Copyright 2017 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_objwriter_h
|
||||
#define hifi_objwriter_h
|
||||
|
||||
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <model/Geometry.h>
|
||||
|
||||
using MeshPointer = std::shared_ptr<model::Mesh>;
|
||||
|
||||
bool writeOBJToTextStream(QTextStream& out, QList<MeshPointer> meshes);
|
||||
bool writeOBJToFile(QString path, QList<MeshPointer> meshes);
|
||||
QString writeOBJToString(QList<MeshPointer> meshes);
|
||||
|
||||
#endif // hifi_objwriter_h
|
|
@ -62,6 +62,8 @@ BackendPointer GLBackend::createBackend() {
|
|||
INSTANCE = result.get();
|
||||
void* voidInstance = &(*result);
|
||||
qApp->setProperty(hifi::properties::gl::BACKEND, QVariant::fromValue(voidInstance));
|
||||
|
||||
gl::GLTexture::initTextureTransferHelper();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -207,7 +209,7 @@ void GLBackend::renderPassTransfer(const Batch& batch) {
|
|||
}
|
||||
}
|
||||
|
||||
{ // Sync all the transform states
|
||||
{ // Sync all the buffers
|
||||
PROFILE_RANGE(render_gpu_gl_detail, "syncCPUTransform");
|
||||
_transform._cameras.clear();
|
||||
_transform._cameraOffsets.clear();
|
||||
|
@ -275,7 +277,7 @@ void GLBackend::renderPassDraw(const Batch& batch) {
|
|||
updateInput();
|
||||
updateTransform(batch);
|
||||
updatePipeline();
|
||||
|
||||
|
||||
CommandCall call = _commandCalls[(*command)];
|
||||
(this->*(call))(batch, *offset);
|
||||
break;
|
||||
|
@ -621,7 +623,6 @@ void GLBackend::queueLambda(const std::function<void()> lambda) const {
|
|||
}
|
||||
|
||||
void GLBackend::recycle() const {
|
||||
PROFILE_RANGE(render_gpu_gl, __FUNCTION__)
|
||||
{
|
||||
std::list<std::function<void()>> lamdbasTrash;
|
||||
{
|
||||
|
@ -744,6 +745,10 @@ void GLBackend::recycle() const {
|
|||
glDeleteQueries((GLsizei)ids.size(), ids.data());
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef THREADED_TEXTURE_TRANSFER
|
||||
gl::GLTexture::_textureTransferHelper->process();
|
||||
#endif
|
||||
}
|
||||
|
||||
void GLBackend::setCameraCorrection(const Mat4& correction) {
|
||||
|
|
|
@ -187,15 +187,10 @@ public:
|
|||
virtual void do_setStateScissorRect(const Batch& batch, size_t paramOffset) final;
|
||||
|
||||
virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0;
|
||||
virtual GLuint getTextureID(const TexturePointer& texture) final;
|
||||
virtual GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) = 0;
|
||||
virtual GLuint getBufferID(const Buffer& buffer) = 0;
|
||||
virtual GLuint getQueryID(const QueryPointer& query) = 0;
|
||||
|
||||
virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0;
|
||||
virtual GLBuffer* syncGPUObject(const Buffer& buffer) = 0;
|
||||
virtual GLTexture* syncGPUObject(const TexturePointer& texture);
|
||||
virtual GLQuery* syncGPUObject(const Query& query) = 0;
|
||||
//virtual bool isTextureReady(const TexturePointer& texture);
|
||||
virtual bool isTextureReady(const TexturePointer& texture);
|
||||
|
||||
virtual void releaseBuffer(GLuint id, Size size) const;
|
||||
virtual void releaseExternalTexture(GLuint id, const Texture::ExternalRecycler& recycler) const;
|
||||
|
@ -211,6 +206,10 @@ public:
|
|||
protected:
|
||||
|
||||
void recycle() const override;
|
||||
virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0;
|
||||
virtual GLBuffer* syncGPUObject(const Buffer& buffer) = 0;
|
||||
virtual GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) = 0;
|
||||
virtual GLQuery* syncGPUObject(const Query& query) = 0;
|
||||
|
||||
static const size_t INVALID_OFFSET = (size_t)-1;
|
||||
bool _inRenderTransferPass { false };
|
||||
|
|