Merge remote-tracking branch 'refs/remotes/JulianGro/master'

Conflicts:
	.github/workflows/pr_build.yml
This commit is contained in:
Julian Groß 2022-08-10 01:38:25 +02:00
commit f96c82fc68
105 changed files with 4053 additions and 16406 deletions

View file

@ -32,7 +32,7 @@ file(REMOVE_RECURSE "${CMAKE_CURRENT_BINARY_DIR}/_env")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/_env")
# Base URL for externally downloaded files
set(EXTERNAL_BUILD_ASSETS "https://athena-public.s3.amazonaws.com")
set(EXTERNAL_BUILD_ASSETS "https://build-deps.overte.org")
if( DEFINED ENV{EXTERNAL_BUILD_ASSETS} )
set(EXTERNAL_BUILD_ASSETS "$ENV{EXTERNAL_BUILD_ASSETS}")

View file

@ -1,4 +1,5 @@
<p align="center"><a href="https://overte.org/"><img src="interface/resources/images/brand-banner-black.svg" alt="Overte" width="350"/></a></p>
<p align="center"><a href="https://overte.org/"><picture><source srcset="interface/resources/images/brand-banner.svg" alt="Overte" width="350" media="(prefers-color-scheme: dark)"><img src="interface/resources/images/brand-banner-black.svg" alt="Overte" width="350"></picture></a></p>
<h3 align="center"><a href="https://overte.org/">Website</a> | <a href="https://matrix.to/#/#overte:matrix.org">Matrix</a> | <a href="https://overte.org/#download">Download</a></h3>
<p align="center">
<a href="https://docs.overte.org/en/latest/contribute.html"><img alt="GitHub contributors" src="https://img.shields.io/github/contributors/overte-org/overte"></a>

View file

@ -107,6 +107,9 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
const QCommandLineOption parentPIDOption(PARENT_PID_OPTION, "PID of the parent process", "parent-pid");
parser.addOption(parentPIDOption);
const QCommandLineOption logOption("logOptions", "Logging options, comma separated: color,nocolor,process_id,thread_id,milliseconds,keep_repeats,journald,nojournald", "options");
parser.addOption(logOption);
if (!parser.parse(QCoreApplication::arguments())) {
std::cout << parser.errorText().toStdString() << std::endl; // Avoid Qt log spam
parser.showHelp();
@ -123,6 +126,16 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
Q_UNREACHABLE();
}
// We want to configure the logging system as early as possible
auto &logHandler = LogHandler::getInstance();
if (parser.isSet(logOption)) {
if (!logHandler.parseOptions(parser.value(logOption).toUtf8(), logOption.names().first())) {
QCoreApplication mockApp(argc, const_cast<char**>(argv)); // required for call to showHelp()
parser.showHelp();
Q_UNREACHABLE();
}
}
const QVariantMap argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments());
unsigned int numForks = 0;

View file

@ -11,7 +11,7 @@ if (POLICY CMP0042)
endif ()
if (POLICY CMP0074)
cmake_policy(SET CMP0074 OLD)
cmake_policy(SET CMP0074 NEW)
endif ()
set_property(GLOBAL PROPERTY USE_FOLDERS ON)

View file

@ -0,0 +1,45 @@
# - Try to find Journald library.
# Once done this will define
#
# JOURNALD_FOUND - system has Journald
# JOURNALD_INCLUDE_DIR - the Journald include directory
# JOURNALD_LIBRARIES - Link these to use Journald
# JOURNALD_DEFINITIONS - Compiler switches required for using Journald
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
#
# Copyright (c) 2015 David Edmundson
#
# use pkg-config to get the directories and then use these values
# in the FIND_PATH() and FIND_LIBRARY() calls
find_package(PkgConfig)
pkg_check_modules(PC_JOURNALD QUIET systemd)
set(JOURNALD_FOUND ${PC_JOURNALD_FOUND})
set(JOURNALD_DEFINITIONS ${PC_JOURNALD_CFLAGS_OTHER})
find_path(JOURNALD_INCLUDE_DIR NAMES systemd/sd-journal.h
PATHS
${PC_JOURNALD_INCLUDEDIR}
${PC_JOURNALD_INCLUDE_DIRS}
)
find_library(JOURNALD_LIBRARY NAMES systemd
PATHS
${PC_JOURNALD_LIBDIR}
${PC_JOURNALD_LIBRARY_DIRS}
)
set(JOURNALD_LIBRARIES ${JOURNALD_LIBRARY})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Journald DEFAULT_MSG JOURNALD_LIBRARY JOURNALD_INCLUDE_DIR)
include(FeatureSummary)
set_package_properties(Journald PROPERTIES URL https://github.com/systemd
DESCRIPTION "Systemd logging daemon")
# show the JOURNALD_INCLUDE_DIR and JOURNALD_LIBRARY variables only in the advanced view
mark_as_advanced(JOURNALD_INCLUDE_DIR JOURNALD_LIBRARY)

View file

@ -1,11 +1,11 @@
include(vcpkg_common_functions)
set(VERSION 1.2.11)
set(VERSION 1.2.12)
vcpkg_download_distfile(ARCHIVE_FILE
URLS "http://www.zlib.net/zlib-${VERSION}.tar.gz" "https://downloads.sourceforge.net/project/libpng/zlib/${VERSION}/zlib-${VERSION}.tar.gz"
FILENAME "zlib1211.tar.gz"
SHA512 73fd3fff4adeccd4894084c15ddac89890cd10ef105dd5e1835e1e9bbb6a49ff229713bd197d203edfa17c2727700fce65a2a235f07568212d820dca88b528ae
URLS "http://www.zlib.net/zlib-${VERSION}.tar.xz" "https://downloads.sourceforge.net/project/libpng/zlib/${VERSION}/zlib-${VERSION}.tar.xz" "https://build-deps.overte.org/dependencies/zlib-${VERSION}.tar.xz"
FILENAME "zlib1212.tar.xz"
SHA512 12940e81e988f7661da52fa20bdc333314ae86a621fdb748804a20840b065a1d6d984430f2d41f3a057de0effc6ff9bcf42f9ee9510b88219085f59cbbd082bd
)
vcpkg_extract_source_archive_ex(

View file

@ -22,7 +22,7 @@
"name": "automatic_networking",
"label": "Automatic Networking",
"help": "This defines how other nodes in the Metaverse will be able to reach your domain-server.<br/>If you don't want to deal with any network settings, use full automatic networking.",
"default": "disabled",
"default": "full",
"type": "select",
"options": [
{

View file

@ -414,6 +414,9 @@ void DomainServer::parseCommandLine(int argc, char* argv[]) {
const QCommandLineOption parentPIDOption(PARENT_PID_OPTION, "PID of the parent process", "parent-pid");
parser.addOption(parentPIDOption);
const QCommandLineOption logOption("logOptions", "Logging options, comma separated: color,nocolor,process_id,thread_id,milliseconds,keep_repeats,journald,nojournald", "options");
parser.addOption(logOption);
QStringList arguments;
for (int i = 0; i < argc; ++i) {
@ -436,6 +439,16 @@ void DomainServer::parseCommandLine(int argc, char* argv[]) {
Q_UNREACHABLE();
}
// We want to configure the logging system as early as possible
auto &logHandler = LogHandler::getInstance();
if (parser.isSet(logOption)) {
if (!logHandler.parseOptions(parser.value(logOption).toUtf8(), logOption.names().first())) {
QCoreApplication mockApp(argc, const_cast<char**>(argv)); // required for call to showHelp()
parser.showHelp();
Q_UNREACHABLE();
}
}
if (parser.isSet(iceServerAddressOption)) {
// parse the IP and port combination for this target
QString hostnamePortString = parser.value(iceServerAddressOption);

View file

@ -134,7 +134,7 @@ endif()
return
if 'Windows' == system:
self.qtUrl = self.assets_url + '/dependencies/vcpkg/qt5-install-5.15.2-windows.tar.gz'
self.qtUrl = self.assets_url + '/dependencies/qt5/qt5-install-5.15.5-qtwebengine-5.15.11-2022.08.01-kde_9480de754ec97f4b63c2b5176672903f80e3f22ff59d3acf0ddd8c68f58c1ed8-windows-x86_64.tar.xz'
elif 'Darwin' == system:
self.qtUrl = self.assets_url + '/dependencies/vcpkg/qt5-install-5.15.2-macos.tar.gz'
elif 'Linux' == system:
@ -146,7 +146,7 @@ endif()
u_minor = int( distro.minor_version() )
if distro.id() == 'ubuntu' or distro.id() == 'linuxmint':
if (distro.id() == 'ubuntu' and u_major == 18) or distro.id() == 'linuxmint' and u_major == 19:
self.qtUrl = self.assets_url + '/dependencies/vcpkg/qt5-install-5.15.2-ubuntu-18.04-amd64.tar.xz'
self.qtUrl = self.assets_url + '/dependencies/qt5/qt5-install-5.15.5-2022.07.17-kde_ea4efc067b47c11b1aac61668afd8578a6834f5b-ubuntu-18.04-amd64.tar.xz'
elif (distro.id() == 'ubuntu' and u_major > 18) or (distro.id() == 'linuxmint' and u_major > 19):
self.__no_qt_package_error()
else:
@ -205,7 +205,7 @@ endif()
def installQt(self):
if not os.path.isdir(self.fullPath):
print ('Downloading Qt from AWS')
print ('Downloading Qt package')
print('Extracting ' + self.qtUrl + ' to ' + self.path)
hifi_utils.downloadAndExtract(self.qtUrl, self.path)
else:

View file

@ -46,7 +46,7 @@ class Singleton:
self.fh = None
# print is horked here so write directly to stdout.
with open(1, mode="w", closefd=False) as _stdout:
_stdout.write("Couldn't aquire lock, retrying in 10 seconds\n")
_stdout.write(f"Couldn't aquire lock {self.path}, retrying in 10 seconds\n")
_stdout.flush()
time.sleep(10)
return self

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

View file

@ -27,7 +27,7 @@ Item {
width: parent.width
property string title: "Controls"
property var openVRDevices: ["HTC Vive", "Valve Index", "Valve HMD", "Valve"]
property var openVRDevices: ["HTC Vive", "Valve Index", "Valve HMD", "Valve", "WindowsMR"]
HifiConstants { id: hifi }

View file

@ -1045,8 +1045,8 @@ Flickable {
var hmdDesktopPosition = settings["hmdDesktopTracking"];
var eyeTrackingEnabled = settings["eyeTrackingEnabled"];
armCircumference.realValue = settings.armCircumference;
shoulderWidth.realValue = settings.shoulderWidth;
armCircumference.realValue = settings["armCircumference"];
shoulderWidth.realValue = settings["shoulderWidth"];
if (HmdHead) {
headBox.checked = true;

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

View file

@ -306,7 +306,7 @@ static const uint32_t MAX_CONCURRENT_RESOURCE_DOWNLOADS = 4;
// we will never drop below the 'min' value
static const int MIN_PROCESSING_THREAD_POOL_SIZE = 2;
static const QString SNAPSHOT_EXTENSION = ".jpg";
static const QString SNAPSHOT_EXTENSION = ".png";
static const QString JPG_EXTENSION = ".jpg";
static const QString PNG_EXTENSION = ".png";
static const QString SVO_EXTENSION = ".svo";
@ -3212,7 +3212,7 @@ void Application::showLoginScreen() {
#endif
}
static const QUrl AUTHORIZED_EXTERNAL_QML_SOURCE { "https://cdn.vircadia.com/community-apps/applications" };
static const QUrl AUTHORIZED_EXTERNAL_QML_SOURCE { "https://more.overte.org/applications" };
void Application::initializeUi() {

View file

@ -2089,7 +2089,7 @@ void MyAvatar::loadData() {
setDriveGear3(firstRunVal.get() ? DEFAULT_GEAR_3 : _driveGear3Setting.get());
setDriveGear4(firstRunVal.get() ? DEFAULT_GEAR_4 : _driveGear4Setting.get());
setDriveGear5(firstRunVal.get() ? DEFAULT_GEAR_5 : _driveGear5Setting.get());
setControlSchemeIndex(firstRunVal.get() ? LocomotionControlsMode::CONTROLS_DEFAULT : _controlSchemeIndexSetting.get());
setControlSchemeIndex(firstRunVal.get() ? LocomotionControlsMode::CONTROLS_ANALOG : _controlSchemeIndexSetting.get());
setAnalogWalkSpeed(firstRunVal.get() ? ANALOG_AVATAR_MAX_WALKING_SPEED : _analogWalkSpeedSetting.get());
setAnalogPlusWalkSpeed(firstRunVal.get() ? ANALOG_PLUS_AVATAR_MAX_WALKING_SPEED : _analogPlusWalkSpeedSetting.get());
setFlyingEnabled(getFlyingEnabled());

View file

@ -2763,7 +2763,7 @@ private:
float _driveGear3 { DEFAULT_GEAR_3 };
float _driveGear4 { DEFAULT_GEAR_4 };
float _driveGear5 { DEFAULT_GEAR_5 };
int _controlSchemeIndex { CONTROLS_DEFAULT };
int _controlSchemeIndex { CONTROLS_ANALOG };
int _movementReference{ 0 };
glm::vec3 _thrust { 0.0f }; // impulse accumulator for outside sources

View file

@ -30,8 +30,8 @@
#include "InterfaceLogging.h"
#include "UserActivityLogger.h"
#include "MainWindow.h"
#include "Profile.h"
#include "LogHandler.h"
#ifdef Q_OS_WIN
#include <Windows.h>
@ -45,7 +45,7 @@ int main(int argc, const char* argv[]) {
auto format = getDefaultOpenGLSurfaceFormat();
// Deal with some weirdness in the chromium context sharing on Mac.
// The primary share context needs to be 3.2, so that the Chromium will
// succeed in it's creation of it's command stub contexts.
// succeed in it's creation of it's command stub contexts.
format.setVersion(3, 2);
// This appears to resolve the issues with corrupted fonts on OSX. No
// idea why.
@ -54,8 +54,8 @@ int main(int argc, const char* argv[]) {
QSurfaceFormat::setDefaultFormat(format);
#endif
#if defined(Q_OS_WIN)
// Check the minimum version of
#if defined(Q_OS_WIN)
// Check the minimum version of
if (gl::getAvailableVersion() < gl::getRequiredVersion()) {
MessageBoxA(nullptr, "Interface requires OpenGL 4.1 or higher", "Unsupported", MB_OK);
return -1;
@ -64,6 +64,9 @@ int main(int argc, const char* argv[]) {
setupHifiApplication(BuildInfo::INTERFACE_NAME);
// Journald by default in user applications is probably a bit too modern still.
LogHandler::getInstance().setShouldUseJournald(false);
QCommandLineParser parser;
parser.setApplicationDescription("Overte -- A free/libre and open-source metaverse client");
QCommandLineOption helpOption = parser.addHelpOption();
@ -161,7 +164,7 @@ int main(int argc, const char* argv[]) {
"url"
);
QCommandLineOption replaceAvatarURLOption(
"replace-avatar-url",
"replaceAvatarURL",
"Replaces the avatar U.R.L. When used with --avatarURL, this takes precedence.",
"url"
);
@ -237,6 +240,11 @@ int main(int argc, const char* argv[]) {
"fast-heartbeat",
"Change stats polling interval from 10000ms to 1000ms."
);
QCommandLineOption logOption(
"logOptions",
"Logging options, comma separated: color,nocolor,process_id,thread_id,milliseconds,keep_repeats,journald,nojournald",
"options"
);
// "--qmljsdebugger", which appears in output from "--help-all".
// Those below don't seem to be optional.
// --ignore-gpu-blacklist
@ -277,6 +285,7 @@ int main(int argc, const char* argv[]) {
parser.addOption(testResultsLocationOption);
parser.addOption(quitWhenFinishedOption);
parser.addOption(fastHeartbeatOption);
parser.addOption(logOption);
QString applicationPath;
// A temporary application instance is needed to get the location of the running executable
@ -299,6 +308,16 @@ int main(int argc, const char* argv[]) {
#endif
}
// We want to configure the logging system as early as possible
auto &logHandler = LogHandler::getInstance();
if (parser.isSet(logOption)) {
if (!logHandler.parseOptions(parser.value(logOption).toUtf8(), logOption.names().first())) {
QCoreApplication mockApp(argc, const_cast<char**>(argv)); // required for call to showHelp()
parser.showHelp();
Q_UNREACHABLE();
}
}
// Act on arguments for early termination.
if (parser.isSet(versionOption)) {
parser.showVersion();
@ -356,7 +375,7 @@ int main(int argc, const char* argv[]) {
}
}
// Early check for --traceFile argument
// Early check for --traceFile argument
auto tracer = DependencyManager::set<tracing::Tracer>();
const char * traceFile = nullptr;
float traceDuration = 0.0f;
@ -370,7 +389,7 @@ int main(int argc, const char* argv[]) {
return 1;
}
}
PROFILE_SYNC_BEGIN(startup, "main startup", "");
#ifdef Q_OS_LINUX
@ -378,8 +397,8 @@ int main(int argc, const char* argv[]) {
#endif
#if defined(USE_GLES) && defined(Q_OS_WIN)
// When using GLES on Windows, we can't create normal GL context in Qt, so
// we force Qt to use angle. This will cause the QML to be unable to be used
// When using GLES on Windows, we can't create normal GL context in Qt, so
// we force Qt to use angle. This will cause the QML to be unable to be used
// in the output window, so QML should be disabled.
qputenv("QT_ANGLE_PLATFORM", "d3d11");
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
@ -550,7 +569,7 @@ int main(int argc, const char* argv[]) {
// Extend argv to enable WebGL rendering
std::vector<const char*> argvExtended(&argv[0], &argv[argc]);
argvExtended.push_back("--ignore-gpu-blacklist");
argvExtended.push_back("--ignore-gpu-blocklist");
#ifdef Q_OS_ANDROID
argvExtended.push_back("--suppress-settings-reset");
#endif

View file

@ -42,9 +42,9 @@
#include "Snapshot.h"
#include "SnapshotUploader.h"
// filename format: overte-snap-by-%username%-on-%date%_%time%_@-%location%.jpg
// filename format: overte-snap-by-%username%-on-%date%_%time%_@-%location%.png
// %1 <= username, %2 <= date and time, %3 <= current location
const QString FILENAME_PATH_FORMAT = "overte-snap-by-%1-on-%2.jpg";
const QString FILENAME_PATH_FORMAT = "overte-snap-by-%1-on-%2.png";
const QString DATETIME_FORMAT = "yyyy-MM-dd_hh-mm-ss";
const QString SNAPSHOTS_DIRECTORY = "Snapshots";
const QString URL = "overte_url";
@ -385,11 +385,19 @@ QFile* Snapshot::savedFileForSnapshot(QImage& shot,
imageQuality = 50;
}
} else {
filename = userSelectedFilename + ".jpg";
filename = userSelectedFilename + ".png";
imageQuality = 50;
}
} else {
filename = FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT));
QFileInfo snapshotFileInfo(filename);
QString filenameSuffix = snapshotFileInfo.suffix();
filenameSuffix = filenameSuffix.toLower();
if (filenameSuffix == "png") {
imageQuality = 50;
}
}
if (!isTemporary) {
// If user has requested specific path then use it, else use the application value

View file

@ -45,6 +45,7 @@ void SnapshotAnimated::saveSnapshotAnimated(QString pathStill, float aspectRatio
SnapshotAnimated::snapshotStillPath = pathStill;
SnapshotAnimated::snapshotAnimatedPath = pathStill;
SnapshotAnimated::snapshotAnimatedPath.replace("jpg", "gif");
SnapshotAnimated::snapshotAnimatedPath.replace("png", "gif");
// Ensure the snapshot timer is Precise (attempted millisecond precision)
SnapshotAnimated::snapshotAnimatedTimer->setTimerType(Qt::PreciseTimer);
@ -148,7 +149,7 @@ void SnapshotAnimated::processFrames() {
GifEnd(&(SnapshotAnimated::snapshotAnimatedGifWriter));
SnapshotAnimated::clearTempVariables();
// Update the "Share" dialog with the processed GIF.
emit SnapshotAnimated::snapshotAnimatedDM->processingGifCompleted(SnapshotAnimated::snapshotAnimatedPath);
}

View file

@ -4,6 +4,7 @@
//
// Created by Stephen Birarda on 1/16/14.
// Copyright 2014 High Fidelity, Inc.
// Copyright 2022 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -196,7 +197,7 @@ QList<FormData> HTTPConnection::parseFormData() const {
qWarning() << "Missing end boundary." << _address;
return data;
}
datum.second = QByteArray::fromRawData(_requestContent->content().data() + buffer.pos(),
datum.second = QByteArray(_requestContent->content().data() + buffer.pos(),
idx - buffer.pos());
data.append(datum);
buffer.seek(idx + end.length());

View file

@ -4,6 +4,7 @@
//
// Created by Seth Alves on 5/19/15.
// Copyright 2015 High Fidelity, Inc.
// Copyright 2022 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -197,13 +198,17 @@ bool isEdged(PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle) {
void RenderablePolyVoxEntityItem::setVoxelData(const QByteArray& voxelData) {
// accept compressed voxel information from the entity-server
bool changed = false;
withWriteLock([&] {
if (_voxelData != voxelData) {
_voxelData = voxelData;
_voxelDataDirty = true;
startUpdates();
changed = true;
}
});
if (changed) {
startUpdates();
}
}
void RenderablePolyVoxEntityItem::setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) {
@ -248,6 +253,9 @@ bool RenderablePolyVoxEntityItem::setVoxel(const ivec3& v, uint8_t toValue) {
withWriteLock([&] {
result = setVoxelInternal(v, toValue);
});
if (result) {
startUpdates();
}
return result;
}
@ -272,11 +280,10 @@ QByteArray RenderablePolyVoxEntityItem::volDataToArray(quint16 voxelXSize, quint
withReadLock([&] {
if (isEdged()) {
low += 1;
voxelSize += 2;
}
loop3(low, voxelSize, [&](const ivec3& v){
result[index++] = _volData->getVoxelAt(v.x, v.y, v.z);
loop3(ivec3(0), voxelSize, [&](const ivec3& v){
result[index++] = _volData->getVoxelAt(v.x + low.x, v.y + low.y, v.z + low.z);
});
});
@ -294,10 +301,12 @@ bool RenderablePolyVoxEntityItem::setAll(uint8_t toValue) {
result |= setVoxelInternal(v, toValue);
});
});
if (result) {
startUpdates();
}
return result;
}
bool RenderablePolyVoxEntityItem::setCuboid(const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int toValue) {
bool result = false;
if (_locked) {
@ -316,11 +325,12 @@ bool RenderablePolyVoxEntityItem::setCuboid(const glm::vec3& lowPosition, const
result |= setVoxelInternal(v, toValue);
});
});
if (result) {
startUpdates();
}
return result;
}
bool RenderablePolyVoxEntityItem::setVoxelInVolume(const vec3& position, uint8_t toValue) {
if (_locked) {
return false;
@ -350,6 +360,9 @@ bool RenderablePolyVoxEntityItem::setSphereInVolume(const vec3& center, float ra
}
});
});
if (result) {
startUpdates();
}
return result;
}
@ -407,6 +420,9 @@ bool RenderablePolyVoxEntityItem::setSphere(const vec3& centerWorldCoords, float
// }
});
});
if (result) {
startUpdates();
}
return result;
}
@ -452,6 +468,9 @@ bool RenderablePolyVoxEntityItem::setCapsule(const vec3& startWorldCoords, const
}
});
});
if (result) {
startUpdates();
}
return result;
}
@ -718,7 +737,7 @@ void RenderablePolyVoxEntityItem::changeUpdates(bool value) {
EntitySimulationPointer simulation = entityTree->getSimulation();
if (simulation) {
_updateNeeded = value;
_flags |= Simulation::DIRTY_UPDATEABLE;
markDirtyFlags(Simulation::DIRTY_UPDATEABLE);
simulation->changeEntity(getThisPointer());
}
}
@ -972,9 +991,7 @@ bool RenderablePolyVoxEntityItem::setVoxelInternal(const ivec3& v, uint8_t toVal
setVoxelMarkNeighbors(v.x, v.y, v.z, toValue);
}
_volDataDirty = true;
startUpdates();
}
return result;
}
@ -1025,6 +1042,7 @@ void RenderablePolyVoxEntityItem::uncompressVolumeData() {
entity->setVoxelsFromData(QByteArray(1, 0), 1, 1, 1);
return;
}
int rawSize = voxelXSize * voxelYSize * voxelZSize;
QByteArray compressedData;
@ -1047,14 +1065,23 @@ void RenderablePolyVoxEntityItem::uncompressVolumeData() {
void RenderablePolyVoxEntityItem::setVoxelsFromData(QByteArray uncompressedData,
quint16 voxelXSize, quint16 voxelYSize, quint16 voxelZSize) {
// this accepts the payload from uncompressVolumeData
ivec3 low{ 0 };
bool result = false;
withWriteLock([&] {
if (isEdged()) {
low += 1;
}
loop3(ivec3(0), ivec3(voxelXSize, voxelYSize, voxelZSize), [&](const ivec3& v) {
int uncompressedIndex = (v.z * voxelYSize * voxelXSize) + (v.y * voxelZSize) + v.x;
setVoxelInternal(v, uncompressedData[uncompressedIndex]);
int uncompressedIndex = (v.z * (voxelYSize) * (voxelXSize)) + (v.y * (voxelZSize)) + v.x;
result |= setVoxelInternal(v, uncompressedData[uncompressedIndex]);
});
_state = PolyVoxState::UncompressingFinished;
});
if (result) {
startUpdates();
}
}
void RenderablePolyVoxEntityItem::compressVolumeDataAndSendEditPacket() {

View file

@ -5,6 +5,7 @@
// Created by Brad Hefta-Gaub on 12/4/13.
// Copyright 2013 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
// Copyright 2022 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -1292,6 +1293,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* type: "PolyVox",
* position: position,
* dimensions: { x: 2, y: 2, z: 2 },
* voxelVolumeSize: { x: 16, y: 16, z: 16 },
* voxelSurfaceStyle: 2,
* xTextureURL: texture,
* yTextureURL: texture,
* zTextureURL: texture,

View file

@ -353,7 +353,7 @@ std::pair<gpu::TexturePointer, glm::ivec2> processImage(std::shared_ptr<QIODevic
// Validate that the image loaded
if (imageWidth == 0 || imageHeight == 0 || image.getFormat() == Image::Format_Invalid) {
QString reason(image.getFormat() == Image::Format_Invalid ? "(Invalid Format)" : "(Size is invalid)");
qCWarning(imagelogging) << "Failed to load:" << qPrintable(reason);
qCWarning(imagelogging) << "Failed to load" << QString::fromStdString(filename) << ":" << qPrintable(reason);
return { nullptr, { imageWidth, imageHeight } };
}

View file

@ -38,7 +38,7 @@ void KTXCache::initialize() {
std::unique_ptr<File> KTXCache::createFile(Metadata&& metadata, const std::string& filepath) {
qCInfo(file_cache) << "Wrote KTX" << metadata.key.c_str();
qCDebug(file_cache) << "Wrote KTX" << metadata.key.c_str();
return FileCache::createFile(std::move(metadata), filepath);
}

View file

@ -21,6 +21,16 @@ if (APPLE)
target_link_libraries(${TARGET_NAME} ${FRAMEWORK_IOKIT} ${CORE_FOUNDATION} ${OpenGL})
endif()
if (UNIX AND NOT APPLE)
find_package(Journald)
if (${JOURNALD_FOUND})
target_link_libraries(${TARGET_NAME} ${JOURNALD_LIBRARIES})
target_include_directories(${TARGET_NAME} PRIVATE ${JOURNALD_INCLUDE_DIR})
target_compile_definitions(${TARGET_NAME} PUBLIC HAS_JOURNALD)
endif()
endif()
target_zlib()
target_nsight()
target_json()

View file

@ -0,0 +1,28 @@
//
// Breakpoint.h
//
//
// Created by Dale Glass on 5/6/2022
// Copyright 2022 Dale Glass
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// Software defined breakpoints, for aiding in debugging
#pragma once
#if defined(__GNUC__)
#include <csignal>
#define BREAKPOINT raise(SIGINT);
#elif defined(__clang__)
#define BREAKPOINT __builtin_trap();
#elif _MSC_VER && !__INTEL_COMPILER
#include <intrin.h>
#define BREAKPOINT __debugbreak();
#else
#include "CrashHelpers.h"
#define BREAKPOINT crash::nullDeref();
#endif

View file

@ -11,6 +11,7 @@
//
#include "LogHandler.h"
#include "Breakpoint.h"
#include <mutex>
@ -29,6 +30,12 @@
#include <QtCore/QThread>
#include <QtCore/QTimer>
#include <QRecursiveMutex>
#include <vector>
#ifdef HAS_JOURNALD
#include <systemd/sd-journal.h>
#include <sys/uio.h>
#endif
QRecursiveMutex LogHandler::_mutex;
@ -50,27 +57,11 @@ LogHandler::LogHandler() {
}
#endif
auto optionList = logOptions.split(",");
#ifdef HAS_JOURNALD
_useJournald = true;
#endif
for (auto option : optionList) {
option = option.trimmed();
if (option == "color") {
_useColor = true;
} else if (option == "nocolor") {
_useColor = false;
} else if (option == "process_id") {
_shouldOutputProcessID = true;
} else if (option == "thread_id") {
_shouldOutputThreadID = true;
} else if (option == "milliseconds") {
_shouldDisplayMilliseconds = true;
} else if (option == "keep_repeats") {
_keepRepeats = true;
} else if (option != "") {
fprintf(stdout, "Unrecognized option in VIRCADIA_LOG_OPTIONS: '%s'\n", option.toUtf8().constData());
}
}
parseOptions(logOptions, "VIRCADIA_LOG_OPTIONS");
}
const char* stringForLogType(LogMsgType msgType) {
@ -115,12 +106,53 @@ const char* colorReset() {
return "\u001b[0m";
}
#ifdef HAS_JOURNALD
void addString(std::vector<struct iovec>&list, const QByteArray &str) {
auto data = str.constData();
struct iovec iov{(void*)data, strlen(data)};
list.emplace_back(iov);
}
#endif
// the following will produce 11/18 13:55:36
const QString DATE_STRING_FORMAT = "MM/dd hh:mm:ss";
// the following will produce 11/18 13:55:36.999
const QString DATE_STRING_FORMAT_WITH_MILLISECONDS = "MM/dd hh:mm:ss.zzz";
bool LogHandler::parseOptions(const QString& logOptions, const QString& paramName) {
QMutexLocker lock(&_mutex);
auto optionList = logOptions.split(",");
for (auto option : optionList) {
option = option.trimmed();
if (option == "color") {
_useColor = true;
} else if (option == "nocolor") {
_useColor = false;
} else if (option == "process_id") {
_shouldOutputProcessID = true;
} else if (option == "thread_id") {
_shouldOutputThreadID = true;
} else if (option == "milliseconds") {
_shouldDisplayMilliseconds = true;
} else if (option == "keep_repeats") {
_keepRepeats = true;
} else if (option == "journald") {
_useJournald = true;
} else if (option == "nojournald") {
_useJournald = false;
} else if (option != "") {
fprintf(stderr, "Unrecognized option in %s: '%s'\n", paramName.toUtf8().constData(), option.toUtf8().constData());
return false;
}
}
return true;
}
void LogHandler::setTargetName(const QString& targetName) {
QMutexLocker lock(&_mutex);
_targetName = targetName;
@ -141,6 +173,24 @@ void LogHandler::setShouldDisplayMilliseconds(bool shouldDisplayMilliseconds) {
_shouldDisplayMilliseconds = shouldDisplayMilliseconds;
}
void LogHandler::setShouldUseJournald(bool shouldUseJournald) {
QMutexLocker lock(&_mutex);
#ifdef HAS_JOURNALD
_useJournald = shouldUseJournald;
#else
if (shouldUseJournald) {
fprintf(stderr, "Journald is not supported on this system or was not compiled in.\n");
}
#endif
}
bool LogHandler::isJournaldAvailable() const {
#ifdef HAS_JOURNALD
return true;
#else
return false;
#endif
}
void LogHandler::flushRepeatedMessages() {
QMutexLocker lock(&_mutex);
@ -162,6 +212,7 @@ QString LogHandler::printMessage(LogMsgType type, const QMessageLogContext& cont
if (message.isEmpty()) {
return QString();
}
QMutexLocker lock(&_mutex);
// log prefix is in the following format
@ -195,32 +246,100 @@ QString LogHandler::printMessage(LogMsgType type, const QMessageLogContext& cont
}
}
// This is returned from this function and wanted by the LogEntityServer,
// so we have to have it even when using journald.
QString logMessage = QString("%1 %2\n").arg(prefixString, message.split('\n').join('\n' + prefixString + " "));
const char* color = "";
const char* resetColor = "";
if (_useColor) {
color = colorForLogType(type);
resetColor = colorReset();
}
if (_keepRepeats || _previousMessage != message) {
if (_repeatCount > 0) {
fprintf(stdout, "[Previous message was repeated %i times]\n", _repeatCount);
if ( _useJournald ) {
#ifdef HAS_JOURNALD
int priority = LOG_NOTICE;
switch(type) {
case LogMsgType::LogFatal: priority = LOG_EMERG; break;
case LogMsgType::LogCritical: priority = LOG_CRIT; break;
case LogMsgType::LogWarning: priority = LOG_WARNING; break;
case LogMsgType::LogInfo: priority = LOG_INFO; break;
case LogMsgType::LogDebug: priority = LOG_DEBUG; break;
case LogMsgType::LogSuppressed: priority = LOG_DEBUG; break;
default:
fprintf(stderr, "Unrecognized log type: %i\n", (int)type);
}
fprintf(stdout, "%s%s%s", color, qPrintable(logMessage), resetColor);
_repeatCount = 0;
QByteArray sd_file = QString("CODE_FILE=%1").arg(context.file).toUtf8();
QByteArray sd_line = QString("CODE_LINE=%1").arg(context.line).toUtf8();
QByteArray sd_message = QString("MESSAGE=%1").arg(message).toUtf8();
QByteArray sd_priority = QString("PRIORITY=%1").arg(priority).toUtf8();
QByteArray sd_category = QString("CATEGORY=%1").arg(context.category).toUtf8();
QByteArray sd_tid = QString("TID=%1").arg((qlonglong)QThread::currentThreadId()).toUtf8();
QByteArray sd_target = QString("COMPONENT=%1").arg(_targetName).toUtf8();
std::vector<struct iovec> fields;
addString(fields, sd_message);
addString(fields, sd_priority);
addString(fields, sd_category);
addString(fields, sd_tid);
if (!_targetName.isEmpty()) {
addString(fields, sd_target);
}
int retval = sd_journal_sendv_with_location(sd_file.constData(),
sd_line.constData(),
context.function == NULL ? "(unknown)" : context.function,
fields.data(),
fields.size());
if ( retval != 0 ) {
fprintf(stderr, "Failed to log message, error %i: ", retval);
fprintf(stderr, "file=%s, line=%i, func=%s, prio=%i, msg=%s\n",
context.file,
context.line,
context.function,
priority,
message.toUtf8().constData()
);
}
#endif
} else {
_repeatCount++;
const char* color = "";
const char* resetColor = "";
if (_useColor) {
color = colorForLogType(type);
resetColor = colorReset();
}
if (_keepRepeats || _previousMessage != message) {
if (_repeatCount > 0) {
fprintf(stdout, "[Previous message was repeated %i times]\n", _repeatCount);
}
fprintf(stdout, "%s%s%s", color, qPrintable(logMessage), resetColor);
_repeatCount = 0;
} else {
_repeatCount++;
}
_previousMessage = message;
#ifdef Q_OS_WIN
// On windows, this will output log lines into the Visual Studio "output" tab
OutputDebugStringA(qPrintable(logMessage));
#endif
}
if ( !_breakMessages.empty() ) {
for(const auto &str : _breakMessages) {
if (logMessage.contains(str)) {
BREAKPOINT
}
}
}
_previousMessage = message;
#ifdef Q_OS_WIN
// On windows, this will output log lines into the Visual Studio "output" tab
OutputDebugStringA(qPrintable(logMessage));
#endif
return logMessage;
}
@ -262,3 +381,9 @@ void LogHandler::printRepeatedMessage(int messageID, LogMsgType type, const QMes
++_repeatedMessageRecords[messageID].repeatCount;
}
void LogHandler::breakOnMessage(const char *message) {
QMutexLocker lock(&_mutex);
LogHandler::getInstance()._breakMessages.append(QString::fromUtf8(message));
}

View file

@ -31,24 +31,105 @@ enum LogMsgType {
LogSuppressed = 100
};
/// Handles custom message handling and sending of stats/logs to Logstash instance
///
/**
* @brief Handles custom message handling and sending of stats/logs to Logstash instance
*
*/
class LogHandler : public QObject {
Q_OBJECT
public:
/**
* @brief Returns the one instance of the LogHandler object
*
* @return LogHandler&
*/
static LogHandler& getInstance();
/// sets the target name to output via the verboseMessageHandler, called once before logging begins
/// \param targetName the desired target name to output in logs
/**
* @brief Parse logging options
*
* This parses the logging settings in the environment variable, or from the commandline
*
* @param options Option list
* @param paramName Name of the log option, for error reporting.
* @return true Option list was parsed successfully
* @return false There was an error
*/
bool parseOptions(const QString& options, const QString &paramName);
/**
* @brief Set the name of the component that's producing log output
*
* For instance, "assignment-client", "audio-mixer", etc.
* Called once before logging begins
*
* @param targetName the desired target name to output in logs
*/
void setTargetName(const QString& targetName);
/**
* @brief Set whether to output the process ID
*
* @note This has no effect when logging with journald, the PID is always logged
*
* @param shouldOutputProcessID Whether to output the PID
*/
void setShouldOutputProcessID(bool shouldOutputProcessID);
/**
* @brief Set whether to output the thread ID
*
* @param shouldOutputThreadID
*/
void setShouldOutputThreadID(bool shouldOutputThreadID);
/**
* @brief Set whether to display timestamps with milliseconds
*
* @param shouldDisplayMilliseconds
*/
void setShouldDisplayMilliseconds(bool shouldDisplayMilliseconds);
/**
* @brief Set whether to use Journald, if it's available
*
* @param shouldUseJournald Whether to use journald
*/
void setShouldUseJournald(bool shouldUseJournald);
/**
* @brief Whether Journald is available on this version/system.
*
* Support is available depending on compile options and only on Linux.
*
* @return true Journald is available
* @return false Journald is not available
*/
bool isJournaldAvailable() const;
/**
* @brief Process a log message
*
* This writes it to a file, logs it to the console, or sends it to journald.
*
* @param type Log message type
* @param context Context of the log message (source file, line, function)
* @param message Log message
* @return QString The log message's text with added severity and timestamp
*/
QString printMessage(LogMsgType type, const QMessageLogContext& context, const QString &message);
/// a qtMessageHandler that can be hooked up to a target that links to Qt
/// prints various process, message type, and time information
/**
* @brief A qtMessageHandler that can be hooked up to a target that links to Qt
*
* Prints various process, message type, and time information
*
* @param type Log message type
* @param context Context of the log message (source file, line, function)
* @param message Log message
*/
static void verboseMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message);
int newRepeatedMessageID();
@ -56,6 +137,27 @@ public:
void setupRepeatedMessageFlusher();
/**
* @brief Break when a message that contains the specified string is logged
*
* This is a function intended to be invoked from inside a debugger. It should be of help when it's hard to put a breakpoint
* on the generating line because it comes from inlined code, or the interesting text is generated at runtime.
*
* Example usage:
*
* @code {.cpp}
* LogHandler::breakOnMessage("No instance available for");
* @endcode
*
* Then the debugger should be triggered as soon as a message containing that string is logged. Backtracking
* through the call stack should lead back to the source.
*
* @note Support for creating a breakpoint in software is compiler and OS specific. If there's no support for
* creating a breakpoint on the current compiler/OS, then an abort will be triggered instead.
*
* @param str Text to match
*/
static void breakOnMessage(const char *str);
private:
LogHandler();
~LogHandler() = default;
@ -68,6 +170,7 @@ private:
bool _shouldDisplayMilliseconds { false };
bool _useColor { false };
bool _keepRepeats { false };
bool _useJournald { false };
QString _previousMessage;
int _repeatCount { 0 };
@ -79,6 +182,8 @@ private:
QString repeatString;
};
std::vector<RepeatedMessageRecord> _repeatedMessageRecords;
QStringList _breakMessages;
static QRecursiveMutex _mutex;
};

View file

@ -4,6 +4,7 @@
// Persist toolbar by HRS 6/11/15.
// Copyright 2014 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
// Copyright 2022 Overte e.V.
//
// This script allows you to edit entities with a new UI/UX for mouse and trackpad based editing
//
@ -11,7 +12,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
/* global Script, SelectionDisplay, LightOverlayManager, CameraManager, Grid, GridTool, EntityListTool, Vec3, SelectionManager,
/* global Script, SelectionDisplay, LightOverlayManager, CameraManager, Grid, GridTool, EditTools, EditVoxels, EntityListTool, Vec3, SelectionManager,
Overlays, OverlayWebWindow, UserActivityLogger, Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera,
progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, OverlaySystemWindow,
keyUpEventFromUIWindow:true */
@ -36,7 +37,9 @@ Script.include([
"entityList/entityList.js",
"entitySelectionTool/entitySelectionTool.js",
"audioFeedback/audioFeedback.js",
"modules/brokenURLReport.js"
"modules/brokenURLReport.js",
"editModes/editModes.js",
"editModes/editVoxels.js"
]);
var CreateWindow = Script.require('./modules/createWindow.js');
@ -126,6 +129,16 @@ var gridTool = new GridTool({
});
gridTool.setVisible(false);
var editTools = new EditTools({
createToolsWindow: createToolsWindow,
});
var editVoxels = new EditVoxels();
editVoxels.editTools = editTools;
editTools.addListener(editVoxels.updateEditSettings);
editTools.addListener(selectionManager.updateEditSettings);
var entityShapeVisualizerSessionName = "SHAPE_VISUALIZER_" + Uuid.generate();
var EntityShapeVisualizer = Script.require('./modules/entityShapeVisualizer.js');
@ -750,6 +763,92 @@ var toolBar = (function () {
}
}
}
function handleNewPolyVoxDialogResult(result) {
if (result) {
var initialShape = result.initialShapeIndex;
var volumeSizeX = parseInt(result.volumeSizeX);
var volumeSizeY = parseInt(result.volumeSizeY);
var volumeSizeZ = parseInt(result.volumeSizeZ);
var voxelSurfaceStyle = parseInt(result.surfaceStyleIndex);
var voxelPosition = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: volumeSizeZ * -1.6 }));
var polyVoxID = createNewEntity({
type: "PolyVox",
name: "terrain",
dimensions: {
x: volumeSizeX,
y: volumeSizeY,
z: volumeSizeZ
},
voxelVolumeSize: {
x: volumeSizeX,
y: volumeSizeY,
z: volumeSizeZ
},
xTextureURL: result.xTextureURL,
yTextureURL: result.yTextureURL,
zTextureURL: result.zTextureURL,
voxelSurfaceStyle: voxelSurfaceStyle,
collisionless: !(result.collisions),
grab: {
grabbable: result.grabbable
},
});
Entities.editEntity(polyVoxID, {
position: voxelPosition
});
if (polyVoxID){
switch (initialShape) {
case 0:
Entities.setVoxelsInCuboid(polyVoxID, {
x: Math.round(volumeSizeX / 4),
y: Math.round(volumeSizeY / 4),
z: Math.round(volumeSizeZ / 4)
}, {
x: Math.round(volumeSizeX / 2.0),
y: Math.round(volumeSizeY / 2.0),
z: Math.round(volumeSizeZ / 2.0)
}, 255);
break;
// Plane 1/4
case 1:
Entities.setVoxelsInCuboid(polyVoxID, {
x: 0,
y: 0,
z: 0
}, {
x: volumeSizeX,
y: Math.round(volumeSizeY / 4),
z: volumeSizeZ
}, 255);
break;
// Plane 3/4
case 2:
Entities.setVoxelsInCuboid(polyVoxID, {
x: 0,
y: 0,
z: 0
}, {
x: volumeSizeX,
y: Math.round(3 * volumeSizeY / 4),
z: volumeSizeZ
}, 255);
break;
// Single voxel at center
case 3:
Entities.setVoxel(polyVoxID, {
x: Math.round(volumeSizeX / 2),
y: Math.round(volumeSizeY / 2),
z: Math.round(volumeSizeZ / 2)
}, 255);
break;
}
}
}
}
function handleNewMaterialDialogResult(result) {
if (result) {
@ -806,6 +905,13 @@ var toolBar = (function () {
case "newMaterialDialogCancel":
closeExistingDialogWindow();
break;
case "newPolyVoxDialogAdd":
handleNewPolyVoxDialogResult(message.params);
closeExistingDialogWindow();
break;
case "newPolyVoxDialogCancel":
closeExistingDialogWindow();
break;
}
}
@ -912,6 +1018,10 @@ var toolBar = (function () {
closeExistingDialogWindow();
var qmlPath = Script.resolvePath("qml/New" + entityType + "Window.qml");
var DIALOG_WINDOW_SIZE = { x: 500, y: 300 };
if( entityType === "PolyVox" ){
DIALOG_WINDOW_SIZE.x = 600;
DIALOG_WINDOW_SIZE.y = 500;
}
dialogWindow = Desktop.createWindow(qmlPath, {
title: "New " + entityType + " Entity",
additionalFlags: Desktop.ALWAYS_ON_TOP | Desktop.CLOSE_BUTTON_HIDES,
@ -971,6 +1081,8 @@ var toolBar = (function () {
addButton("newMaterialButton", createNewEntityDialogButtonCallback("Material"));
addButton("newPolyVoxButton", createNewEntityDialogButtonCallback("PolyVox"));
var deactivateCreateIfDesktopWindowsHidden = function() {
if (!shouldUseEditTabletApp() && !entityListTool.isVisible() && !createToolsWindow.isVisible()) {
that.setActive(false);
@ -1016,6 +1128,8 @@ var toolBar = (function () {
isActive = active;
activeButton.editProperties({isActive: isActive});
undoHistory.setEnabled(isActive);
editVoxels.setActive(active);
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
@ -1369,7 +1483,7 @@ function mouseClickEvent(event) {
var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) &&
(allowSmallModels || angularSize > MIN_ANGULAR_SIZE);
if (0 < x && sizeOK) {
if (0 < x && sizeOK && selectionManager.editEnabled) {
selectedEntityID = foundEntity;
orientation = MyAvatar.orientation;
intersection = rayPlaneIntersection(pickRay, P, Quat.getForward(orientation));

View file

@ -0,0 +1,181 @@
//
// editModes.js
//
// Created by Karol Suprynowicz on 2022.05.15.
// Copyright 2022 Overte e.V.
//
// Partially based on gridTool.js
// Created by Ryan Huffman on 6 Nov 2014
// Copyright 2014 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
// Copyright 2022 Overte e.V.
//
// This script implements a class for managing different edit modes
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
EditTools = function(options) {
var that = {};
var createAppMode = "object";
var voxelEditMode = "single";
var voxelSphereSize = 0.3;
var voxelEditDynamics = "click";
var voxelRemove = false;
var voxelPointerMode = "laser";
var voxelBrushLength = 0.5;
var listeners = [];
var createToolsWindow = options.createToolsWindow;
that.emitUpdate = function() {
var dataString = JSON.stringify({
createAppMode : createAppMode,
voxelEditMode : voxelEditMode,
voxelSphereSize : voxelSphereSize,
voxelEditDynamics : voxelEditDynamics,
voxelRemove : voxelRemove,
voxelPointerMode : voxelPointerMode,
voxelBrushLength : voxelBrushLength,
});
webView.emitScriptEvent(dataString);
createToolsWindow.emitScriptEvent(dataString);
};
that.getCreateAppMode = function() {
return createAppMode;
}
that.setCreateAppMode = function(value) {
createAppMode = value;
that.emitUpdate();
}
that.getVoxelEditMode = function() {
return voxelEditMode;
}
that.setVoxelEditMode = function(value) {
voxelEditMode = value;
that.emitUpdate();
}
that.getVoxelSphereSize = function() {
return voxelSphereSize;
}
that.setVoxelSphereSize = function(value) {
voxelSphereSize = value;
that.emitUpdate();
}
that.getVoxelEditDynamics = function() {
return voxelEditDynamics;
}
that.setVoxelEditDynamics = function(value) {
voxelEditDynamics = value;
that.emitUpdate();
}
that.getVoxelRemove = function() {
return voxelRemove;
}
that.setVoxelRemove = function(value) {
voxelRemove = value;
that.emitUpdate();
}
that.getVoxelPointerMode = function() {
return voxelPointerMode;
}
that.setVoxelPointerMode = function(value) {
voxelPointerMode = value;
that.emitUpdate();
}
that.getVoxelBrushLength = function() {
return voxelBrushLength;
}
that.setVoxelBrushLength = function(value) {
voxelBrushLength = value;
that.emitUpdate();
}
that.update = function(data) {
if (data.type !== "update-edit-tools") {
return;
}
print(JSON.stringify(data));
var needsUpdate = false;
if (data.createAppMode) {
createAppMode = data.createAppMode;
needsUpdate = true;
}
if (data.voxelEditMode) {
voxelEditMode = data.voxelEditMode;
needsUpdate = true;
}
if (data.voxelSphereSize) {
voxelSphereSize = data.voxelSphereSize;
needsUpdate = true;
}
if (data.voxelEditDynamics) {
voxelEditDynamics = data.voxelEditDynamics;
needsUpdate = true;
}
if (data.voxelRemove) {
voxelRemove = data.voxelRemove;
needsUpdate = true;
}
if (data.voxelPointerMode) {
voxelPointerMode = data.voxelPointerMode;
needsUpdate = true;
}
if (data.voxelBrushLength) {
voxelBrushLength = data.voxelBrushLength;
needsUpdate = true;
}
}
var webEventReceived = function(data) {
try {
data = JSON.parse(data);
} catch (e) {
console.log("editModes.js, EditTools.webEventReceived: Cannot parse received JSON data");
return;
}
if (data.type === "init") {
that.emitUpdate();
} else if (data.type === "update-edit-tools") {
that.update(data);
for (var i = 0; i < listeners.length; i++) {
listeners[i] && listeners[i](data);
}
}
};
var webView = null;
webView = Tablet.getTablet("com.highfidelity.interface.tablet.system");
webView.webEventReceived.connect(webEventReceived);
createToolsWindow.webEventReceived.addListener(webEventReceived);
that.addListener = function(callback) {
listeners.push(callback);
};
function cleanup() {
}
return that;
}

View file

@ -0,0 +1,687 @@
//
// editModes.js
//
// Created by dr Karol Suprynowicz on 2022.05.17.
// Copyright 2022 Overte e.V.
//
// Based on voxels.js
// Created by Seth Alves on 2015-08-25
// Copyright 2015 High Fidelity, Inc.
//
// Based on entitySelectionTool.js
// Created by Brad hefta-Gaub on 10/1/14.
// Modified by Daniela Fontes * @DanielaFifo and Tiago Andrade @TagoWill on 4/7/2017
// Modified by David Back on 1/9/2018
// Copyright 2014 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors
//
// This script implements voxel edit mode
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include([
"./libraries/utils.js",
"entitySelectionTool/entitySelectionTool.js"
]);
var selectionManager = SelectionManager;
EditVoxels = function() {
var self = this;
var that = {};
const NO_HAND = -1;
var controlHeld = false;
var shiftHeld = false;
var isLeftGripPressed = false;
var isRightGripPressed = false;
var editEnabled = false;
var editSingleVoxels = false;
var editSpheres = false;
var editCubes = false;
var editAdd = true; // Remove voxels if false
var inverseOperation = false; // True when middle mouse button or grip is pressed
var brushPointer = false;
var isActive = true;
var editSphereRadius = 0.15;
var brushLength = 0.5;
// Vector calculated from editSphereRadius for adding/remiving cubes
var cubeSize = null;
// Local plane for continuous voxel editing
// 0 - plane parallel to YZ plane
// 1 - plane parallel to XZ plane
// 2 - plane parallel to YZ plane
var editPlane = 0;
// Is true when mouse button is pressed
var isEditing = false;
var editedVoxelEntity = null;
// Position of last edit in voxel space
var oldEditPosition = null;
// True when original operation added voxels, false otherwise
var lastEditValue = 255;
var isOnUpdateConnected = false;
var isSphereResizingStarted = true;
var sphereResizingInitialHandDistance = 0.1;
var sphereInitialRadius = editSphereRadius;
var sphereEntityID = null;
that.triggerClickMapping = Controller.newMapping(Script.resolvePath('') + '-click-voxels');
that.triggerPressMapping = Controller.newMapping(Script.resolvePath('') + '-press-voxels');
that.gripPressMapping = Controller.newMapping(Script.resolvePath('') + '-grip-voxels');
that.triggeredHand = NO_HAND;
that.pressedHand = NO_HAND;
var soundAdd = SoundCache.getSound(Script.resourcesPath() + "sounds/Button05.wav");
var soundDelete = SoundCache.getSound(Script.resourcesPath() + "sounds/Tab03.wav");
// Continuous start timer prevents activating continuous mode on short button presses
// and adding multiple voxels when only one was intended
var continuousStartTimerMax = 0.200;
var continuousStartTimer = 0.0;
that.setActive = function(active) {
isActive = (active === true);
}
that.updateEditSettings = function(data) {
if (data.createAppMode) {
if (data.createAppMode === "voxel"){
editEnabled = true;
} else {
editEnabled = false;
}
}
if (data.voxelEditMode) {
editAdd = true;
if (data.voxelRemove) {
editAdd = false;
}
if (data.voxelEditMode === "single") {
editSpheres = false;
editSingleVoxels = true;
editCubes = false;
} else if (data.voxelEditMode === "sphere") {
editSpheres = true;
editSingleVoxels = false;
editCubes = false;
} else if (data.voxelEditMode === "cube") {
editSpheres = false;
editSingleVoxels = false;
editCubes = true;
}
}
if (data.voxelSphereSize) {
editSphereRadius = parseFloat(data.voxelSphereSize) / 2.0;
}
if (data.voxelPointerMode) {
if (data.voxelPointerMode === "brush") {
brushPointer = true;
} else {
brushPointer = false;
}
}
if (data.voxelBrushLength) {
voxelBrushLength = parseFloat(data.voxelBrushLength);
}
}
function floorVector(v) {
return {
x: Math.floor(v.x),
y: Math.floor(v.y),
z: Math.floor(v.z)
};
}
function ceilVector(v) {
return {
x: Math.ceil(v.x),
y: Math.ceil(v.y),
z: Math.ceil(v.z)
};
}
function attemptVoxelChangeForEntity(entityID, pickRayDir, intersectionLocation) {
var wantDebug = false;
if (wantDebug) {
print("=============== eV::attemptVoxelChangeForEntity BEG =======================");
}
var properties = Entities.getEntityProperties(entityID);
if (properties.type != "PolyVox") {
return false;
}
if (!editEnabled || !isActive) {
return false;
}
if (editSingleVoxels === false && editSpheres === false && editCubes === false) {
return false;
}
var voxelOrigin = Entities.worldCoordsToVoxelCoords(entityID, Vec3.subtract(intersectionLocation, pickRayDir));
var voxelPosition = Entities.worldCoordsToVoxelCoords(entityID, intersectionLocation);
var pickRayDirInVoxelSpace = Vec3.subtract(voxelPosition, voxelOrigin);
pickRayDirInVoxelSpace = Vec3.normalize(pickRayDirInVoxelSpace);
var absX = Math.abs(pickRayDirInVoxelSpace.x);
var absY = Math.abs(pickRayDirInVoxelSpace.y);
var absZ = Math.abs(pickRayDirInVoxelSpace.z);
if(absX >= absY && absX >= absZ){
editPlane = 0;
}else if(absY >= absX && absY >= absZ){
editPlane = 1;
}else if(absZ >= absX && absZ >= absY){
editPlane = 2;
}
if (wantDebug) {
print("voxelOrigin: " + JSON.stringify(voxelOrigin));
print("voxelPosition: " + JSON.stringify(voxelPosition));
print("pickRayDirInVoxelSpace: " + JSON.stringify(pickRayDirInVoxelSpace));
}
lastEditValue = 0;
if((editAdd && !inverseOperation) || (!editAdd && inverseOperation)){
lastEditValue = 255;
}
var toDrawPosition = null;
if(lastEditValue === 255){
toDrawPosition = Vec3.subtract(voxelPosition, Vec3.multiply(pickRayDirInVoxelSpace, 0.1));
}else{
toDrawPosition = Vec3.subtract(voxelPosition, Vec3.multiply(pickRayDirInVoxelSpace, -0.1));
}
if (editSingleVoxels) {
if (wantDebug) {
print("Calling setVoxel");
print("entityID: " + JSON.stringify(entityID));
print("floorVector(toDrawPosition): " + JSON.stringify(floorVector(toDrawPosition)));
}
oldEditPosition = floorVector(toDrawPosition);
if (Entities.setVoxel(entityID, oldEditPosition, lastEditValue)){
Audio.playSystemSound((lastEditValue === 255) ? soundAdd : soundDelete);
return true;
}else{
return false;
}
}
if (editSpheres) {
if (wantDebug) {
print("Calling setVoxelSphere");
print("entityID: " + JSON.stringify(entityID));
print("editSphereRadius: " + JSON.stringify(editSphereRadius));
print("floorVector(toDrawPosition): " + JSON.stringify(floorVector(toDrawPosition)));
}
oldEditPosition = floorVector(toDrawPosition);
var toDrawPositionWorld = Entities.voxelCoordsToWorldCoords(entityID, oldEditPosition);
if (Entities.setVoxelSphere(entityID, toDrawPositionWorld, editSphereRadius, lastEditValue)){
Audio.playSystemSound((lastEditValue === 255) ? soundAdd : soundDelete);
return true;
}else{
return false;
}
}
if (editCubes) {
if (wantDebug) {
print("Calling setVoxelsInCuboid");
print("entityID: " + JSON.stringify(entityID));
print("editSphereRadius: " + JSON.stringify(editSphereRadius));
print("floorVector(toDrawPosition): " + JSON.stringify(floorVector(toDrawPosition)));
}
oldEditPosition = floorVector(toDrawPosition);
var cubeSizeWorld = {x : editSphereRadius * 2, y : editSphereRadius * 2, z : editSphereRadius * 2};
var zeroVecWorld = {x : 0, y: 0, z: 0};
var zeroVecLocal = Entities.worldCoordsToVoxelCoords(entityID, zeroVecWorld);
var cubeSizeVecLocal = Entities.worldCoordsToVoxelCoords(entityID, cubeSizeWorld);
cubeSize = ceilVector(Vec3.subtract(cubeSizeVecLocal, zeroVecLocal));
var lowPosition = Vec3.subtract(oldEditPosition, Vec3.multiply(cubeSize, 0.5));
if (Entities.setVoxelsInCuboid(entityID, lowPosition, cubeSize, lastEditValue)){
Audio.playSystemSound((lastEditValue === 255) ? soundAdd : soundDelete);
return true;
}else{
return false;
}
}
}
function attemptVoxelChange(pickRayDir, intersection) {
var wantDebug = false;
if (wantDebug) {
print("=============== eV::attemptVoxelChange BEG =======================");
}
var ids;
ids = Entities.findEntities(intersection.intersection, editSphereRadius + 1.0);
if (ids.indexOf(intersection.entityID) < 0) {
ids.push(intersection.entityID);
}
if (wantDebug) {
print("Entities: " + JSON.stringify(ids));
}
var success = false;
for (var i = 0; i < ids.length; i++) {
var entityID = ids[i];
success |= attemptVoxelChangeForEntity(entityID, pickRayDir, intersection.intersection)
}
return success;
}
function controllerComputePickRay() {
var hand = triggered() ? that.triggeredHand : that.pressedHand;
var controllerPose = getControllerWorldLocation(hand, true);
if (controllerPose.valid) {
var controllerPosition = controllerPose.translation;
// This gets point direction right, but if you want general quaternion it would be more complicated:
var controllerDirection = Quat.getUp(controllerPose.rotation);
return {origin: controllerPosition, direction: controllerDirection};
}
}
function generalComputePickRay(x, y) {
return controllerComputePickRay() || Camera.computePickRay(x, y);
}
function mousePressEvent(event) {
var wantDebug = false;
var attemptChangeOnEmpty = false;
if (!editEnabled || !isActive) {
return false;
}
if (wantDebug) {
print("=============== eV::mousePressEvent BEG =======================");
}
if (!(event.isLeftButton || event.isMiddleButton) && !triggered()) {
return;
}
if (event.isLeftButton || event.isMiddleButton){
if (event.isMiddleButton){
inverseOperation = true;
}else{
inverseOperation = false;
}
}else{
inverseOperation = false;
if(that.triggeredHand === Controller.Standard.RightHand && Controller.getValue(Controller.Standard.RightGrip) > 0.5){
inverseOperation = true;
}
if(that.triggeredHand === Controller.Standard.LeftHand && Controller.getValue(Controller.Standard.LeftGrip) > 0.5){
inverseOperation = true;
}
}
continuousStartTimer = 0;
var pickRay = generalComputePickRay(event.x, event.y);
var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking
if (wantDebug) {
print("Pick ray: " + JSON.stringify(pickRay));
print("Intersection: " + JSON.stringify(intersection));
}
if (intersection.intersects) {
if (attemptVoxelChangeForEntity(intersection.entityID, pickRay.direction, intersection.intersection)) {
Script.update.connect(onUpdateHandler);
isOnUpdateConnected = true;
isEditing = true;
editedVoxelEntity = intersection.entityID;
if (wantDebug) {
print("onUpdateHandler connected");
}
return;
}
}
// if the PolyVox entity is empty, we can't pick against its "on" voxels. try picking against its
// bounding box, instead.
if (attemptChangeOnEmpty) {
intersection = Entities.findRayIntersection(pickRay, false); // bounding box picking
if (intersection.intersects) {
if(attemptVoxelChange(pickRay.direction, intersection)){
Script.update.connect(onUpdateHandler);
isOnUpdateConnected = true;
if (wantDebug) {
print("onUpdateHandler connected");
}
}
}
}
}
function mouseReleaseEvent(event) {
var wantDebug = false;
if (wantDebug) {
print("=============== eV::mouseReleaseEvent BEG =======================");
}
if(isOnUpdateConnected){
Script.update.disconnect(onUpdateHandler);
isOnUpdateConnected = false;
isEditing = false;
editedVoxelEntity = null;
}
return;
}
function keyPressEvent(event) {
if (event.text == "CONTROL") {
controlHeld = true;
}
if (event.text == "SHIFT") {
shiftHeld = true;
}
}
function keyReleaseEvent(event) {
if (event.text == "CONTROL") {
controlHeld = false;
}
if (event.text == "SHIFT") {
shiftHeld = false;
}
}
function triggered() {
return that.triggeredHand !== NO_HAND;
};
function pointingAtDesktopWindowOrTablet(hand) {
var pointingAtDesktopWindow = (hand === Controller.Standard.RightHand &&
SelectionManager.pointingAtDesktopWindowRight) ||
(hand === Controller.Standard.LeftHand &&
SelectionManager.pointingAtDesktopWindowLeft);
var pointingAtTablet = (hand === Controller.Standard.RightHand && SelectionManager.pointingAtTabletRight) ||
(hand === Controller.Standard.LeftHand && SelectionManager.pointingAtTabletLeft);
return pointingAtDesktopWindow || pointingAtTablet;
}
function makeClickHandler(hand) {
return function (clicked) {
if (!editEnabled) {
return;
}
// Don't allow both hands to trigger at the same time
if (triggered() && hand !== that.triggeredHand) {
return;
}
if (!triggered() && clicked && !pointingAtDesktopWindowOrTablet(hand)) {
that.triggeredHand = hand;
mousePressEvent({});
} else if (triggered() && !clicked) {
that.triggeredHand = NO_HAND;
mouseReleaseEvent({});
}
};
}
function makePressHandler(hand) {
return function (value) {
if (!editEnabled) {
return;
}
if (value >= TRIGGER_ON_VALUE && !triggered() && !pointingAtDesktopWindowOrTablet(hand)) {
that.pressedHand = hand;
} else {
that.pressedHand = NO_HAND;
if(isOnUpdateConnected){
Script.update.disconnect(onUpdateHandler);
isOnUpdateConnected = false;
}
}
}
}
function getDistanceBetweenControllers(){
var poseLeft = getControllerWorldLocation(Controller.Standard.LeftHand, true);
var poseRight = getControllerWorldLocation(Controller.Standard.RightHand, true);
return Vec3.distance(poseLeft.translation, poseRight.translation);
}
function getEditSpherePosition( radius ){
var poseLeft = getControllerWorldLocation(Controller.Standard.LeftHand, true);
var poseRight = getControllerWorldLocation(Controller.Standard.RightHand, true);
var handsPosition = Vec3.multiply(Vec3.sum(poseLeft.translation, poseRight.translation), 0.5);
return Vec3.sum(handsPosition, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: radius * -2.0 }));
}
function updateSphereResizing(delta) {
var wantDebug = false;
var newDistance = getDistanceBetweenControllers();
var newRadius = (sphereInitialRadius / sphereResizingInitialHandDistance) * newDistance;
var newPosition = getEditSpherePosition(newRadius);
var newDimensions = Vec3.multiply({ x: 1.0, y: 1.0, z: 1.0 }, newRadius * 2.0);
if (wantDebug) {
print("newDistance: " + JSON.stringify(newDistance));
print("newRadius: " + JSON.stringify(newRadius));
print("newPosition: " + JSON.stringify(newPosition));
print("newDimensions: " + JSON.stringify(newDimensions));
}
Entities.editEntity(sphereEntityID, {
position: newPosition,
dimensions: newDimensions
});
if( that.editTools ) {
editTools.setVoxelSphereSize(newRadius * 2);
}
editSphereRadius = newRadius;
}
function startSphereResizing() {
var wantDebug = false;
if (wantDebug) {
print("=============== eV::startSphereResizing BEG =======================");
}
Script.update.connect(updateSphereResizing);
sphereResizingInitialHandDistance = getDistanceBetweenControllers();
sphereInitialRadius = editSphereRadius;
var spherePosition = getEditSpherePosition(sphereInitialRadius);
var sphereDimensions = Vec3.multiply({ x: 1.0, y: 1.0, z: 1.0 }, sphereInitialRadius * 2.0);
sphereEntityID = Entities.addEntity({
type: "Shape",
shape: "Sphere",
name: "voxelEditSphere",
position: spherePosition,
color: { r: 60, g: 100, b: 60 },
alpha: 0.5,
dimensions: sphereDimensions,
collisionless: true,
},"world");
}
function stopSphereResizing() {
var wantDebug = false;
if (wantDebug) {
print("=============== eV::stopSphereResizing BEG =======================");
}
Script.update.disconnect(updateSphereResizing);
if (sphereEntityID !== null) {
Entities.deleteEntity(sphereEntityID);
}
sphereEntityID = null;
}
function makeGripPressHandler(hand) {
return function (value) {
if (!editEnabled) {
return;
}
if (value > 0.5) {
if (hand === Controller.Standard.LeftHand) {
isLeftGripPressed = true;
} else if (hand === Controller.Standard.RightHand) {
isRightGripPressed = true;
}
} else if (value < 0.4){
if (hand === Controller.Standard.LeftHand) {
isLeftGripPressed = false;
} else if (hand === Controller.Standard.RightHand) {
isRightGripPressed = false;
}
}
if ( isLeftGripPressed && isRightGripPressed) {
if( !isSphereResizingStarted ) {
isSphereResizingStarted = true;
startSphereResizing();
}
} else {
if( isSphereResizingStarted ) {
isSphereResizingStarted = false;
stopSphereResizing();
}
}
}
}
function onUpdateHandler(delta){
var wantDebug = false;
if (isEditing === false || editedVoxelEntity === null){
return;
}
continuousStartTimer += delta;
if (continuousStartTimer < continuousStartTimerMax) {
return;
}
// Get pick ray origin and direction
var pickRay = null;
var hand = triggered() ? that.triggeredHand : that.pressedHand;
if (hand === NO_HAND) {
pickRay = Camera.computePickRay(Controller.getValue(Controller.Hardware.Keyboard.MouseX), Controller.getValue(Controller.Hardware.Keyboard.MouseY));
}else{
pickRay = controllerComputePickRay();
}
if (pickRay === null) {
return;
}
// Compute intersection of pick ray with given plane in local coordinates
var globalOriginInVoxelSpace = Entities.worldCoordsToVoxelCoords(editedVoxelEntity, { x: 0, y: 0, z: 0 });
var pickRayDirInVoxelSpace = Vec3.subtract(Entities.worldCoordsToVoxelCoords(editedVoxelEntity, pickRay.direction), globalOriginInVoxelSpace);
var voxelPickRayOrigin = Entities.worldCoordsToVoxelCoords(editedVoxelEntity, pickRay.origin);
pickRayDirInVoxelSpace = Vec3.normalize(pickRayDirInVoxelSpace);
var directionMultiplier = 1.0;
var offsetVector = { x: 0, y: 0, z: 0 };
switch (editPlane) {
// 0 - plane parallel to YZ plane
case 0:
offsetVector.x = 0.5;
offsetVector.y = (offsetVector.x / pickRayDirInVoxelSpace.x) * pickRayDirInVoxelSpace.y;
offsetVector.z = (offsetVector.x / pickRayDirInVoxelSpace.x) * pickRayDirInVoxelSpace.z;
directionMultiplier = (oldEditPosition.x - voxelPickRayOrigin.x) / pickRayDirInVoxelSpace.x;
break;
// 1 - plane parallel to XZ plane
case 1:
offsetVector.y = 0.5;
offsetVector.x = (offsetVector.y / pickRayDirInVoxelSpace.y) * pickRayDirInVoxelSpace.x;
offsetVector.z = (offsetVector.y / pickRayDirInVoxelSpace.y) * pickRayDirInVoxelSpace.z;
directionMultiplier = (oldEditPosition.y - voxelPickRayOrigin.y) / pickRayDirInVoxelSpace.y;
break;
// 2 - plane parallel to XY plane
case 2:
offsetVector.z = 0.5;
offsetVector.x = (offsetVector.z / pickRayDirInVoxelSpace.z) * pickRayDirInVoxelSpace.x;
offsetVector.y = (offsetVector.z / pickRayDirInVoxelSpace.z) * pickRayDirInVoxelSpace.y;
directionMultiplier = (oldEditPosition.z - voxelPickRayOrigin.z) / pickRayDirInVoxelSpace.z;
break;
default:
return;
}
intersectionPoint = Vec3.sum(Vec3.multiply(pickRayDirInVoxelSpace, directionMultiplier), voxelPickRayOrigin);
newEditPosition = floorVector(Vec3.sum(intersectionPoint, offsetVector));
if (newEditPosition === oldEditPosition) {
return;
}
if (wantDebug) {
print("Old edit position: " + JSON.stringify(oldEditPosition));
print("New edit position: " + JSON.stringify(newEditPosition));
print("directionMultiplier: " + JSON.stringify(directionMultiplier) + " pickRay.direction: " + JSON.stringify(pickRay.direction) + " pickRayDirInVoxelSpace: " + JSON.stringify(pickRayDirInVoxelSpace) + " voxelPickRayOrigin: " + JSON.stringify(voxelPickRayOrigin) + " editPlane: " + JSON.stringify(editPlane));
}
if (editSingleVoxels) {
if (Entities.setVoxel(editedVoxelEntity, newEditPosition, lastEditValue)){
oldEditPosition = newEditPosition;
Audio.playSystemSound((lastEditValue === 255) ? soundAdd : soundDelete);
}
} else if (editSpheres) {
var toDrawPositionWorld = Entities.voxelCoordsToWorldCoords(editedVoxelEntity, newEditPosition);
if (Entities.setVoxelSphere(editedVoxelEntity, toDrawPositionWorld, editSphereRadius, lastEditValue)){
oldEditPosition = newEditPosition;
Audio.playSystemSound((lastEditValue === 255) ? soundAdd : soundDelete);
}
} else if (editCubes) {
var lowPosition = Vec3.subtract(newEditPosition, Vec3.multiply(cubeSize, 0.5));
if (Entities.setVoxelsInCuboid(editedVoxelEntity, lowPosition, cubeSize, lastEditValue)){
oldEditPosition = newEditPosition;
Audio.playSystemSound((lastEditValue === 255) ? soundAdd : soundDelete);
}
}
}
function cleanup() {
Controller.mousePressEvent.disconnect(self.mousePressEvent);
Controller.mouseReleaseEvent.disconnect(self.mouseReleaseEvent);
Controller.keyPressEvent.disconnect(self.keyPressEvent);
Controller.keyReleaseEvent.disconnect(self.keyReleaseEvent);
}
Controller.mousePressEvent.connect(mousePressEvent);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
Controller.keyPressEvent.connect(keyPressEvent);
Controller.keyReleaseEvent.connect(keyReleaseEvent);
that.triggerClickMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand));
that.triggerClickMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand));
that.triggerPressMapping.from(Controller.Standard.RT).peek().to(makePressHandler(Controller.Standard.RightHand));
that.triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand));
that.gripPressMapping.from(Controller.Standard.LeftGrip).peek().to(makeGripPressHandler(Controller.Standard.LeftHand));
that.gripPressMapping.from(Controller.Standard.RightGrip).peek().to(makeGripPressHandler(Controller.Standard.RightHand));
that.enableTriggerMapping = function() {
that.triggerClickMapping.enable();
that.triggerPressMapping.enable();
that.gripPressMapping.enable();
};
that.disableTriggerMapping = function() {
that.triggerClickMapping.disable();
that.triggerPressMapping.disable();
that.gripPressMapping.disable();
};
that.enableTriggerMapping();
Script.scriptEnding.connect(cleanup);
Script.scriptEnding.connect(that.disableTriggerMapping);
return that;
}

View file

@ -5,6 +5,7 @@
// Created by Ryan Huffman on 13 Nov 2014
// Copyright 2014 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
// Copyright 2022 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -17,7 +18,8 @@
<link rel="stylesheet" type="text/css" href="../../../html/css/edit-style.css">
<link rel="stylesheet" type="text/css" href="../../../html/css/colpick.css">
<link rel="stylesheet" type="text/css" href="../../../html/css/jsoneditor.css">
<link rel="stylesheet" type="text/css" href="../../../html/css/tabs.css">
<link rel="stylesheet" type="text/css" href="../../../html/css/tabs.css">
<link rel="stylesheet" type="text/css" href="../../../html/css/materialAssistant.css">
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
<script src="../../../html/js/jquery-2.1.4.min.js"></script>
<script type="text/javascript" src="../../../html/js/colpick.js"></script>
@ -29,7 +31,8 @@
<script type="text/javascript" src="js/underscore-min.js"></script>
<script type="text/javascript" src="js/createAppTooltip.js"></script>
<script type="text/javascript" src="js/draggableNumber.js"></script>
<script type="text/javascript" src="js/entityProperties.js"></script>
<script type="text/javascript" src="js/entityProperties.js"></script>
<script type="text/javascript" src="js/materialAssistant.js"></script>
</head>
<body onload='loaded();'>
<div id="properties-list">
@ -59,5 +62,137 @@
</tr>
</table>
</div>
<div id="uiMaterialAssistant" style="display: none;">
<div id="uiMaterialAssistant-sidewalk">
<div id="uiMaterialAssistant-formContainer">
<div id="uiMaterialAssistant-headerContainer">
<div style="width: 85%; text-align: left;"><font class="uiMaterialAssistant-title">MATERIAL DATA</font></div>
<div style="width: 15%; text-align: right;"><span id="uiMaterialAssistant-closeButton">&#10006;</span></div>
</div>
<div class="uiMaterialAssistant-group">
<font class="uiMaterialAssistant-label">Name:&nbsp;</font>
<input name = "ma-name" id = "ma-name" type = "text" class="uiMaterialAssistant-input" style= "width: 90%;">
</div>
<div class="uiMaterialAssistant-group">
<button class="uiMaterialAssistant-active" id="ma-albedo-isActive"></button>
<font class="uiMaterialAssistant-label">Albedo (Color): </font>
<div id="ma-albedo-colorPicker" class="uiMaterialAssistant-color-picker"></div>
<br><br>
<button class="uiMaterialAssistant-active" id="ma-albedoMap-isActive"></button>
<font class="uiMaterialAssistant-label">Albedo Map (RGB) URL: </font>
<input name = "ma-albedoMap" id = "ma-albedoMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;"><br>
<font class="uiMaterialAssistant-Explain">The Albedo (Color) can be used to tint the texture of the Albedo Map.</font>
</div>
<div class="uiMaterialAssistant-group">
<button class="uiMaterialAssistant-active" id="ma-metallic-isActive"></button>
<font class="uiMaterialAssistant-label">Metallic: </font>
<input class="uiMaterialAssistant-input" name = "ma-metallic" id = "ma-metallic" readonly type = "text" size = "5"><br>
<input type="range" style="width:100%;" min="1" max="1000" value="1" class="uiMaterialAssistant-slider" name = "ma-metallic-slider" id = "ma-metallic-slider"><br>
<div style="width: 100%; display: flex;">
<div style="width: 15%; text-align: left;"><font class = "uiMaterialAssistant-Explain">|&lt; Nonmetal</font></div>
<div style="width: 15%; text-align: left;"><font class = "uiMaterialAssistant-Explain">| Hair</font></div>
<div style="width: 60%; text-align: left;"><font class = "uiMaterialAssistant-Explain">| Chitin</font></div>
<div style="width: 10%; text-align: right;"><font class = "uiMaterialAssistant-Explain">Metal &gt;|</font></div>
</div><br>
<font class="uiMaterialAssistant-label">Metallic Map (Grayscale) URL: </font>
<input name = "ma-metallicMap" id = "ma-metallicMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;">
</div>
<div class="uiMaterialAssistant-group">
<button class="uiMaterialAssistant-active" id="ma-roughness-isActive"></button>
<font class="uiMaterialAssistant-label">Roughness:</font>
<input name = "ma-roughness" id = "ma-roughness" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
<input type="range" style="width:100%;" min="0" max="1000" value="0" class="uiMaterialAssistant-slider" name = "ma-roughness-slider" id = "ma-roughness-slider"><br>
<div style="width: 100%; display: flex;">
<div style="width: 50%; text-align: left;"><font class = "uiMaterialAssistant-Explain">|&lt; Glossy, polished, lustrous</font></div>
<div style="width: 50%; text-align: right;"><font class = "uiMaterialAssistant-Explain">Unpolished, mat, rough &gt;|</font></div>
</div><br>
<font class="uiMaterialAssistant-label">Roughness Map (Grayscale) URL: </font>
<input name = "ma-roughnessMap" id = "ma-roughnessMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;">
</div>
<div class="uiMaterialAssistant-group">
<button class="uiMaterialAssistant-active" id="ma-normalMap-isActive"></button>
<font class="uiMaterialAssistant-label">Normal Map URL: </font>
<input name = "ma-normalMap" id = "ma-normalMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;">
</div>
<div class="uiMaterialAssistant-group">
<button class="uiMaterialAssistant-active" id="ma-opacity-isActive"></button>
<font class="uiMaterialAssistant-label">Opacity: </font>
<input name = "ma-opacity" id = "ma-opacity" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
<input type="range" style="width:100%;" min="0" max="1000" value="0" class="uiMaterialAssistant-slider" name = "ma-opacity-slider" id = "ma-opacity-slider"><br>
<div style="width: 100%; display: flex;">
<div style="width: 50%; text-align: left;"><font class = "uiMaterialAssistant-Explain">|&lt; Transparent</font></div>
<div style="width: 50%; text-align: right;"><font class = "uiMaterialAssistant-Explain">Opaque &gt;|</font></div>
</div>
<br>
<font class="uiMaterialAssistant-label">Opacity Map Mode:</font><br><br>
<input type="radio" class="uiMaterialAssistant-radio" checked name = "ma-opacityMapMode" id = "ma-opacityMapMode-dont" value = "OPACITY_MAP_OPAQUE"> Do not used&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<input type="radio" class="uiMaterialAssistant-radio" name = "ma-opacityMapMode" id = "ma-opacityMapMode-mask" value = "OPACITY_MAP_MASK"> Cut off mask&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<input type="radio" class="uiMaterialAssistant-radio" name = "ma-opacityMapMode" id = "ma-opacityMapMode-blend" value = "OPACITY_MAP_BLEND"> Blend<br>
<font class="uiMaterialAssistant-Explain"><br>Note: For an opacity map, the alpha layer of the Albedo Map will be used.<br>
'Blend' mode will used the alpha value to determine the opacity of a pixel.<br>
'Cut off mask' mode will use the 'Cut off threshold' to determine if a pixel will be opaque or transparent, based on the alpha value from the map.</font><br><br>
<font class="uiMaterialAssistant-label">Cut Off Threshold: </font>
<input name = "ma-opacityCutoff" id = "ma-opacityCutoff" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
<input type="range" style="width:100%;" min="0" max="1000" value="0" class="uiMaterialAssistant-slider" name = "ma-opacityCutoff-slider" id = "ma-opacityCutoff-slider"><br>
<div style="width: 100%; display: flex;">
<div style="width: 50%; text-align: left;"><font class = "uiMaterialAssistant-Explain">|&lt; Transparent</font></div>
<div style="width: 50%; text-align: right;"><font class = "uiMaterialAssistant-Explain">Opaque &gt;|</font></div>
</div>
</div>
<div class="uiMaterialAssistant-group">
<div style="width: 100%; display: flex;">
<div style="width: 70%; text-align: left;">
<button class="uiMaterialAssistant-active" id="ma-emissive-isActive"></button>
<font class="uiMaterialAssistant-label">Emissive: </font>
<div id="ma-emissive-colorPicker" class="uiMaterialAssistant-color-picker"></div>
</div>
<div style="width: 30%; text-align: left;">
<input type="checkbox" class="uiMaterialAssistant-checkbox" name = "ma-unlit" id = "ma-unlit">
<font class="uiMaterialAssistant-label">Unlit</font>
</div>
</div>
<font class="uiMaterialAssistant-label">Bloom Factor:</font>
<input name = "ma-bloom" id = "ma-bloom" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
<input type="range" style="width:100%;" min="100" max="900" value="100" class="uiMaterialAssistant-slider" name = "ma-bloom-slider" id = "ma-bloom-slider"><br>
<font class="uiMaterialAssistant-label">Emissive Map (RGB) URL:</font>
<input name = "ma-emissiveMap" id = "ma-emissiveMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;">
</div>
<div class="uiMaterialAssistant-group">
<button class="uiMaterialAssistant-active" id="ma-scattering-isActive"></button>
<font class="uiMaterialAssistant-label">Scattering: </font>
<input name = "ma-scattering" id = "ma-scattering" readonly class="uiMaterialAssistant-input" type = "text" size = "5"><br>
<input type="range" style="width:100%;" min="0" max="1000" value="0" class="uiMaterialAssistant-slider" name = "ma-scattering-slider" id = "ma-scattering-slider"><br>
<font class="uiMaterialAssistant-label">Scattering Map (Grayscale) URL: </font>
<input name = "ma-scatteringMap" id = "ma-scatteringMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;">
</div>
<div class="uiMaterialAssistant-group">
<button class="uiMaterialAssistant-active" id="ma-occlusionMap-isActive"></button>
<font class="uiMaterialAssistant-label">Occlusion Map (Grayscale) URL: </font>
<input name = "ma-occlusionMap" id = "ma-occlusionMap" class="uiMaterialAssistant-input" type = "text" style= "width:100%;">
</div>
<div class="uiMaterialAssistant-group">
<font class="uiMaterialAssistant-label">Material displayed on surface: </font><br><br>
<input type="radio" class="uiMaterialAssistant-radio" checked name = "ma-cullFaceMode" id = "ma-cullFaceMode-back" value = "CULL_BACK"> Outside&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<input type="radio" class="uiMaterialAssistant-radio" name = "ma-cullFaceMode" id = "ma-cullFaceMode-front" value = "CULL_FRONT"> Inside&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<input type="radio" class="uiMaterialAssistant-radio" name = "ma-cullFaceMode" id = "ma-cullFaceMode-none" value = "CULL_NONE"> Both<br>
</div>
<br><br><br><br><br>
</div>
</div>
</div>
<script>
initiateMaUi();
</script>
</body>
</html>

View file

@ -3,6 +3,7 @@
// Created by Ryan Huffman on 13 Nov 2014
// Copyright 2014 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
// Copyright 2022 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -877,9 +878,10 @@ const GROUPS = [
{
label: "Material Data",
type: "textarea",
buttons: [ { id: "clear", label: "Clear Material Data", className: "red", onClick: clearMaterialData },
{ id: "edit", label: "Edit as JSON", className: "blue", onClick: newJSONMaterialEditor },
{ id: "save", label: "Save Material Data", className: "black", onClick: saveMaterialData } ],
buttons: [ { id: "materialAssistant", label: "Assistant...", className: "secondary_blue blue", onClick: openMaterialAssistant },
{ id: "clear", label: "Clear Material", className: "secondary_red red", onClick: clearMaterialData },
{ id: "edit", label: "Edit as JSON", className: "secondary", onClick: newJSONMaterialEditor },
{ id: "save", label: "Save Material", className: "secondary", onClick: saveMaterialData }],
propertyID: "materialData",
},
{
@ -1326,6 +1328,57 @@ const GROUPS = [
}
]
},
{
id: "polyvox",
label: "POLYVOX",
properties: [
{
label: "Volume Size",
type: "vec3",
vec3Type: "xyz",
step: 1.0,
decimals: 0,
subLabels: [ "x", "y", "z" ],
unit: "",
propertyID: "voxelVolumeSize",
},
{
label: "Texture preset",
type: "dropdown",
options: { 0 : "None", 1 : "Grass + ground", 2 : "Bricks", 3 : "Stone",
4: "Concrete", 5 : "Rock"},
propertyID: "polyVoxPreset",
onDropdownChange: createPolyVoxPresetChangedFunction,
skipPropertyUpdate: true,
},
{
label: "Surface Style",
type: "dropdown",
options: { 0: "Marching cubes", 1: "Cubic",
2: "Edged cubic", 3: "Edged marching cubes" },
propertyID: "voxelSurfaceStyle",
propertyName: "voxelSurfaceStyle",
},
{
label: "X Texture URL",
type: "string",
propertyID: "xTextureURL",
propertyName: "xTextureURL",
},
{
label: "Y Texture URL",
type: "string",
propertyID: "yTextureURL",
propertyName: "yTextureURL",
},
{
label: "Z Texture URL",
type: "string",
propertyID: "zTextureURL",
propertyName: "zTextureURL",
},
]
},
{
id: "spatial",
label: "SPATIAL",
@ -1705,7 +1758,7 @@ const GROUPS_PER_TYPE = {
ParticleEffect: [ 'base', 'particles', 'particles_emit', 'particles_size', 'particles_color',
'particles_behavior', 'particles_constraints', 'spatial', 'behavior', 'scripts', 'physics' ],
PolyLine: [ 'base', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ],
PolyVox: [ 'base', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ],
PolyVox: [ 'base', 'polyvox', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ],
Grid: [ 'base', 'grid', 'spatial', 'behavior', 'scripts', 'physics' ],
Multiple: [ 'base', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ],
};
@ -3197,6 +3250,55 @@ function parentIDChanged() {
}
}
function createPolyVoxPresetChangedFunction(property) {
return function() {
property.elInput.classList.remove('multi-diff');
var xTextureURL = "";
var yTextureURL = "";
var zTextureURL = "";
switch (parseInt(this.value)) {
// Clear texture entries
case 0:
xTextureURL = "";
yTextureURL = "";
zTextureURL = "";
break;
// Grass + ground
case 1:
xTextureURL = "qrc:///serverless/Textures/ground_5-2K/2K-ground_5-diffuse.jpg";
yTextureURL = "qrc:///serverless/Textures/ground_grass_gen_05.png";
zTextureURL = "qrc:///serverless/Textures/ground_5-2K/2K-ground_5-diffuse.jpg";
break;
// Bricks
case 2:
xTextureURL = "qrc:///serverless/Textures/2K-wall_stone_2-diffuse_l.jpg";
yTextureURL = "qrc:///serverless/Textures/2K-stone_floor_3-diffuse_l.jpg";
zTextureURL = "qrc:///serverless/Textures/2K-wall_stone_2-diffuse_l.jpg";
break;
// Stone
case 3:
xTextureURL = "qrc:///serverless/Textures/wall_l.png";
yTextureURL = "qrc:///serverless/Textures/floor_l.png";
zTextureURL = "qrc:///serverless/Textures/wall_l.png";
break;
// Concrete
case 4:
xTextureURL = "qrc:///serverless/Textures/concrete_12-2K/2K-concrete_12-diffuse.jpg";
yTextureURL = "qrc:///serverless/Textures/concrete_12-2K/2K-concrete_12-diffuse.jpg";
zTextureURL = "qrc:///serverless/Textures/concrete_12-2K/2K-concrete_12-diffuse.jpg";
break;
// Rock
case 5:
xTextureURL = "qrc:///serverless/Textures/Rock026_2K-JPG/Rock026_2K_Color.jpg";
yTextureURL = "qrc:///serverless/Textures/Rock026_2K-JPG/Rock026_2K_Color.jpg";
zTextureURL = "qrc:///serverless/Textures/Rock026_2K-JPG/Rock026_2K_Color.jpg";
break;
}
updateProperty("xTextureURL", xTextureURL, false);
updateProperty("yTextureURL", yTextureURL, false);
updateProperty("zTextureURL", zTextureURL, false);
};
}
/**
* BUTTON CALLBACKS
@ -3478,6 +3580,21 @@ function saveMaterialData() {
saveJSONMaterialData(true);
}
function openMaterialAssistant() {
if (materialEditor === null) {
loadDataInMaUi({});
} else {
loadDataInMaUi(materialEditor.getText());
}
$('#uiMaterialAssistant').show();
$('#properties-list').hide();
}
function closeMaterialAssistant() {
$('#uiMaterialAssistant').hide();
$('#properties-list').show();
}
/**
* @param {boolean} noUpdate - don't update the UI, but do send a property update.
* @param {Set.<string>} [entityIDsToUpdate] - Entity IDs to update materialData for.
@ -3509,6 +3626,8 @@ function setMaterialDataFromEditor(noUpdate, entityIDsToUpdate) {
} else {
updateProperty('materialData', text, false);
}
maGetMaterialDataAssistantAvailability(text);
}
let materialEditor = null;
@ -3567,6 +3686,14 @@ function hideMaterialDataSaved() {
$('#property-materialData-saved').hide();
}
function showMaterialAssistantButton() {
$('#property-materialData-button-materialAssistant').show();
}
function hideMaterialAssistantButton() {
$('#property-materialData-button-materialAssistant').hide();
}
function setMaterialEditorJSON(json) {
materialEditor.set(json);
if (materialEditor.hasOwnProperty('expandAll')) {
@ -4043,6 +4170,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) {
const multipleSelections = currentSelections.length > 1;
const hasSelectedEntityChanged = !areSetsEqual(selectedEntityIDs, previouslySelectedEntityIDs);
closeMaterialAssistant();
requestZoneList();
if (selections.length === 0) {
@ -4344,20 +4472,20 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) {
let materialDataMultiValue = getMultiplePropertyValue("materialData");
let materialDataTextArea = getPropertyInputElement("materialData");
let materialJson = null;
let materialJSON = null;
if (!materialDataMultiValue.isMultiDiffValue) {
try {
materialJson = JSON.parse(materialDataMultiValue.value);
materialJSON = JSON.parse(materialDataMultiValue.value);
} catch (e) {
}
}
if (materialJson !== null && !lockedMultiValue.isMultiDiffValue && !lockedMultiValue.value) {
if (materialJSON !== null && !lockedMultiValue.isMultiDiffValue && !lockedMultiValue.value) {
if (materialEditor === null) {
createJSONMaterialEditor();
}
materialDataTextArea.classList.remove('multi-diff');
setMaterialEditorJSON(materialJson);
setMaterialEditorJSON(materialJSON);
showSaveMaterialDataButton();
hideMaterialDataTextArea();
hideNewJSONMaterialEditorButton();
@ -4373,6 +4501,8 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) {
hideMaterialDataSaved();
}
maGetMaterialDataAssistantAvailability(materialJSON);
if (hasSelectedEntityChanged && selections.length === 1 && entityTypes[0] === "Material") {
requestMaterialTarget();
}
@ -4756,7 +4886,16 @@ function loaded() {
let propertyID = elDropdown.getAttribute("propertyID");
let property = properties[propertyID];
property.elInput = dt;
dt.addEventListener('change', createEmitTextPropertyUpdateFunction(property));
if (property.data.
skipPropertyUpdate !== true) {
dt.addEventListener('change', createEmitTextPropertyUpdateFunction(property));
}
if (property.data.
onDropdownChange !== undefined) {
dt.addEventListener('change', property.data.onDropdownChange(property));
}
}
document.addEventListener('click', function(ev) { closeAllDropdowns() }, true);

View file

@ -0,0 +1,924 @@
// materialAssistant.js
//
// Created by Alezia Kurdis on May 19th, 2022.
// Copyright 2022 Vircadia contributors.
// Copyright 2022 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
let maMaterialData = {};
let maClose,
maName,
maAlbedoIsActive,
maAlbedoColorPicker,
maAlbedoColorPickerSel,
maAlbedoMapIsActive,
maAlbedoMap,
maMetallicIsActive,
maMetallic,
maMetallicSlider,
maMetallicMap,
maRoughnessIsActive,
maRoughness,
maRoughnessSlider,
maRoughnessMap,
maNormalMapIsActive,
maNormalMap,
maOpacityIsActive,
maOpacity,
maOpacitySlider,
maOpacityMapModeDont,
maOpacityMapModeMask,
maOpacityMapModeBlend,
maOpacityCutoff,
maOpacityCutoffSlider,
maEmissiveIsActive,
maEmissiveColorPicker,
maEmissiveColorPickerSel,
maBloom,
maBloomSlider,
maUnlit,
maEmissiveMap,
maScatteringIsActive,
maScattering,
maScatteringSlider,
maScatteringMap,
maOcclusionMapIsActive,
maOcclusionMap,
maCullFaceModeBack,
maCullFaceModeFront,
maCullFaceModeNone;
let DEFAULT_ALBEDO = [1,1,1];
let DEFAULT_EMISSIVE = [0,0,0];
let DEFAULT_BLOOM_FACTOR = 1;
let DEFAULT_ROUGHNESS = 0.9;
let DEFAULT_METALLIC_FOR_MA_UI = 0.001;
let DEFAULT_METALLIC = 0;
let DEFAULT_OPACITY = 1;
let DEFAULT_OPACITY_CUTOFF = 0.5;
let DEFAULT_SCATTERING = 0;
function initiateMaUi() {
maClose = document.getElementById("uiMaterialAssistant-closeButton");
maName = document.getElementById("ma-name");
maAlbedoIsActive = document.getElementById("ma-albedo-isActive");
maAlbedoColorPicker = document.getElementById("ma-albedo-colorPicker");
maAlbedoMapIsActive = document.getElementById("ma-albedoMap-isActive");
maAlbedoMap = document.getElementById("ma-albedoMap");
maMetallicIsActive = document.getElementById("ma-metallic-isActive");
maMetallic = document.getElementById("ma-metallic");
maMetallicSlider = document.getElementById("ma-metallic-slider");
maMetallicMap = document.getElementById("ma-metallicMap");
maRoughnessIsActive = document.getElementById("ma-roughness-isActive");
maRoughness = document.getElementById("ma-roughness");
maRoughnessSlider = document.getElementById("ma-roughness-slider");
maRoughnessMap = document.getElementById("ma-roughnessMap");
maNormalMapIsActive = document.getElementById("ma-normalMap-isActive");
maNormalMap = document.getElementById("ma-normalMap");
maOpacityIsActive = document.getElementById("ma-opacity-isActive");
maOpacity = document.getElementById("ma-opacity");
maOpacitySlider = document.getElementById("ma-opacity-slider");
maOpacityMapModeDont = document.getElementById("ma-opacityMapMode-dont");
maOpacityMapModeMask = document.getElementById("ma-opacityMapMode-mask");
maOpacityMapModeBlend = document.getElementById("ma-opacityMapMode-blend");
maOpacityCutoff = document.getElementById("ma-opacityCutoff");
maOpacityCutoffSlider = document.getElementById("ma-opacityCutoff-slider");
maEmissiveIsActive = document.getElementById("ma-emissive-isActive");
maEmissiveColorPicker = document.getElementById("ma-emissive-colorPicker");
maBloom = document.getElementById("ma-bloom");
maBloomSlider = document.getElementById("ma-bloom-slider");
maUnlit = document.getElementById("ma-unlit");
maEmissiveMap = document.getElementById("ma-emissiveMap");
maScatteringIsActive = document.getElementById("ma-scattering-isActive");
maScattering = document.getElementById("ma-scattering");
maScatteringSlider = document.getElementById("ma-scattering-slider");
maScatteringMap = document.getElementById("ma-scatteringMap");
maOcclusionMapIsActive = document.getElementById("ma-occlusionMap-isActive");
maOcclusionMap = document.getElementById("ma-occlusionMap");
maCullFaceModeBack = document.getElementById("ma-cullFaceMode-back");
maCullFaceModeFront = document.getElementById("ma-cullFaceMode-front");
maCullFaceModeNone = document.getElementById("ma-cullFaceMode-none");
maClose.onclick = function() {
closeMaterialAssistant();
};
maAlbedoIsActive.onclick = function() {
if (maMaterialData.albedoIsActive) {
maMaterialData.albedoIsActive = false;
maAlbedoIsActive.className = "uiMaterialAssistant-inactive";
maAlbedoColorPicker.style.pointerEvents = 'none';
maAlbedoColorPicker.style.backgroundColor = "#555555";
} else {
maMaterialData.albedoIsActive = true;
maAlbedoIsActive.className = "uiMaterialAssistant-active";
maAlbedoColorPicker.style.pointerEvents = 'auto';
maAlbedoColorPicker.style.backgroundColor = maGetRGB(maMaterialData.albedo);
}
maGenerateJsonAndSave();
};
maAlbedoMapIsActive.onclick = function() {
if (maMaterialData.albedoMapIsActive) {
maMaterialData.albedoMapIsActive = false;
maAlbedoMapIsActive.className = "uiMaterialAssistant-inactive";
maAlbedoMap.disabled = true;
} else {
maMaterialData.albedoMapIsActive = true;
maAlbedoMapIsActive.className = "uiMaterialAssistant-active";
maAlbedoMap.disabled = false;
}
maGenerateJsonAndSave();
};
maMetallicIsActive.onclick = function() {
if (maMaterialData.metallicIsActive) {
maMaterialData.metallicIsActive = false;
maMetallicIsActive.className = "uiMaterialAssistant-inactive";
maMetallic.disabled = true;
maMetallicSlider.disabled = true;
maMetallicMap.disabled = true;
} else {
maMaterialData.metallicIsActive = true;
maMetallicIsActive.className = "uiMaterialAssistant-active";
maMetallic.disabled = false;
maMetallicSlider.disabled = false;
maMetallicMap.disabled = false;
}
maGenerateJsonAndSave();
};
maRoughnessIsActive.onclick = function() {
if (maMaterialData.roughnessIsActive) {
maMaterialData.roughnessIsActive = false;
maRoughnessIsActive.className = "uiMaterialAssistant-inactive";
maRoughness.disabled = true;
maRoughnessSlider.disabled = true;
maRoughnessMap.disabled = true;
} else {
maMaterialData.roughnessIsActive = true;
maRoughnessIsActive.className = "uiMaterialAssistant-active";
maRoughness.disabled = false;
maRoughnessSlider.disabled = false;
maRoughnessMap.disabled = false;
}
maGenerateJsonAndSave();
};
maNormalMapIsActive.onclick = function() {
if (maMaterialData.normalMapIsActive) {
maMaterialData.normalMapIsActive = false;
maNormalMapIsActive.className = "uiMaterialAssistant-inactive";
maNormalMap.disabled = true;
} else {
maMaterialData.normalMapIsActive = true;
maNormalMapIsActive.className = "uiMaterialAssistant-active";
maNormalMap.disabled = false;
}
maGenerateJsonAndSave();
};
maOpacityIsActive.onclick = function() {
if (maMaterialData.opacityIsActive) {
maMaterialData.opacityIsActive = false;
maOpacityIsActive.className = "uiMaterialAssistant-inactive";
maOpacity.disabled = true;
maOpacitySlider.disabled = true;
maOpacityMapModeDont.disabled = true;
maOpacityMapModeMask.disabled = true;
maOpacityMapModeBlend.disabled = true;
maOpacityCutoff.disabled = true;
maOpacityCutoffSlider.disabled = true;
} else {
maMaterialData.opacityIsActive = true;
maOpacityIsActive.className = "uiMaterialAssistant-active";
maOpacity.disabled = false;
maOpacitySlider.disabled = false;
maOpacityMapModeDont.disabled = false;
maOpacityMapModeMask.disabled = false;
maOpacityMapModeBlend.disabled = false;
maOpacityCutoff.disabled = false;
maOpacityCutoffSlider.disabled = false;
}
maGenerateJsonAndSave();
};
maEmissiveIsActive.onclick = function() {
if (maMaterialData.emissiveIsActive) {
maMaterialData.emissiveIsActive = false;
maEmissiveIsActive.className = "uiMaterialAssistant-inactive";
maBloom.disabled = true;
maBloomSlider.disabled = true;
maEmissiveMap.disabled = true;
maEmissiveColorPicker.style.pointerEvents = 'none';
maEmissiveColorPicker.style.backgroundColor = "#555555";
} else {
maMaterialData.emissiveIsActive = true;
maEmissiveIsActive.className = "uiMaterialAssistant-active";
maBloom.disabled = false;
maBloomSlider.disabled = false;
maEmissiveMap.disabled = false;
maEmissiveColorPicker.style.pointerEvents = 'auto';
maEmissiveColorPicker.style.backgroundColor = maGetRGB(maMaterialData.emissive);
}
maGenerateJsonAndSave();
};
maScatteringIsActive.onclick = function() {
if (maMaterialData.scatteringIsActive) {
maMaterialData.scatteringIsActive = false;
maScatteringIsActive.className = "uiMaterialAssistant-inactive";
maScattering.disabled = true;
maScatteringSlider.disabled = true;
maScatteringMap.disabled = true;
} else {
maMaterialData.scatteringIsActive = true;
maScatteringIsActive.className = "uiMaterialAssistant-active";
maScattering.disabled = false;
maScatteringSlider.disabled = false;
maScatteringMap.disabled = false;
}
maGenerateJsonAndSave();
};
maOcclusionMapIsActive.onclick = function() {
if (maMaterialData.occlusionMapIsActive) {
maMaterialData.occlusionMapIsActive = false;
maOcclusionMapIsActive.className = "uiMaterialAssistant-inactive";
maOcclusionMap.disabled = true;
} else {
maMaterialData.occlusionMapIsActive = true;
maOcclusionMapIsActive.className = "uiMaterialAssistant-active";
maOcclusionMap.disabled = false;
}
maGenerateJsonAndSave();
};
maName.oninput = function() {
maMaterialData.name = maName.value;
maGenerateJsonAndSave();
};
maAlbedoMap.oninput = function() {
maMaterialData.albedoMap = maAlbedoMap.value;
maGenerateJsonAndSave();
};
maMetallicSlider.oninput = function() {
maMetallic.value = maMetallicSlider.value/1000;
maMaterialData.metallic = parseFloat(maMetallic.value);
maGenerateJsonAndSave();
};
maMetallicMap.oninput = function() {
maMaterialData.metallicMap = maMetallicMap.value;
maGenerateJsonAndSave();
};
maRoughnessSlider.oninput = function() {
maRoughness.value = maRoughnessSlider.value/1000;
maMaterialData.roughness = parseFloat(maRoughness.value);
maGenerateJsonAndSave();
};
maRoughnessMap.oninput = function() {
maMaterialData.roughnessMap = maRoughnessMap.value;
maGenerateJsonAndSave();
};
maNormalMap.oninput = function() {
maMaterialData.normalMap = maNormalMap.value;
maGenerateJsonAndSave();
};
maOpacitySlider.oninput = function() {
maOpacity.value = maOpacitySlider.value/1000;
maMaterialData.opacity = parseFloat(maOpacity.value);
maGenerateJsonAndSave();
};
maOpacityMapModeDont.oninput = function() {
maMaterialData.opacityMapMode = maOpacityMapModeDont.value;
maGenerateJsonAndSave();
};
maOpacityMapModeMask.oninput = function() {
maMaterialData.opacityMapMode = maOpacityMapModeMask.value;
maGenerateJsonAndSave();
};
maOpacityMapModeBlend.oninput = function() {
maMaterialData.opacityMapMode = maOpacityMapModeBlend.value;
maGenerateJsonAndSave();
};
maOpacityCutoffSlider.oninput = function() {
maOpacityCutoff.value = maOpacityCutoffSlider.value/1000;
maMaterialData.opacityCutoff = parseFloat(maOpacityCutoff.value);
maGenerateJsonAndSave();
};
maBloomSlider.oninput = function() {
maBloom.value = maBloomSlider.value/100;
maMaterialData.bloom = parseFloat(maBloom.value);
maGenerateJsonAndSave();
};
maUnlit.oninput = function() {
maMaterialData.unlit = maUnlit.checked;
maGenerateJsonAndSave();
};
maEmissiveMap.oninput = function() {
maMaterialData.emissiveMap = maEmissiveMap.value;
maGenerateJsonAndSave();
};
maScatteringSlider.oninput = function() {
maScattering.value = maScatteringSlider.value/1000;
maMaterialData.scattering = parseFloat(maScattering.value);
maGenerateJsonAndSave();
};
maScatteringMap.oninput = function() {
maMaterialData.scatteringMap = maScatteringMap.value;
maGenerateJsonAndSave();
};
maOcclusionMap.oninput = function() {
maMaterialData.occlusionMap = maOcclusionMap.value;
maGenerateJsonAndSave();
};
maCullFaceModeBack.oninput = function() {
maMaterialData.cullFaceMode = maCullFaceModeBack.value;
maGenerateJsonAndSave();
};
maCullFaceModeFront.oninput = function() {
maMaterialData.cullFaceMode = maCullFaceModeFront.value;
maGenerateJsonAndSave();
};
maCullFaceModeNone.oninput = function() {
maMaterialData.cullFaceMode = maCullFaceModeNone.value;
maGenerateJsonAndSave();
};
var maAlbedoColorPickerID = "#ma-albedo-colorPicker";
maAlbedoColorPickerSel = $(maAlbedoColorPickerID).colpick({
colorScheme: 'dark',
layout: 'rgbhex',
color: '000000',
submit: false,
onShow: function(colpick) {
$(maAlbedoColorPickerID).colpickSetColor({
"r": maMaterialData.albedo[0] * 255,
"g": maMaterialData.albedo[1] * 255,
"b": maMaterialData.albedo[2] * 255,
});
$(maAlbedoColorPickerID).attr('active', 'true');
},
onHide: function(colpick) {
$(maAlbedoColorPickerID).attr('active', 'false');
},
onChange: function(hsb, hex, rgb, el) {
$(el).css('background-color', '#' + hex);
if ($(maAlbedoColorPickerID).attr('active') === 'true') {
maMaterialData.albedo = [(rgb.r/255), (rgb.g/255), (rgb.b/255)];
maGenerateJsonAndSave();
}
}
});
var maEmissiveColorPickerID = "#ma-emissive-colorPicker";
maEmissiveColorPickerSel = $(maEmissiveColorPickerID).colpick({
colorScheme: 'dark',
layout: 'rgbhex',
color: '000000',
submit: false,
onShow: function(colpick) {
$(maEmissiveColorPickerID).colpickSetColor({
"r": maMaterialData.emissive[0] * 255,
"g": maMaterialData.emissive[1] * 255,
"b": maMaterialData.emissive[2] * 255,
});
$(maEmissiveColorPickerID).attr('active', 'true');
},
onHide: function(colpick) {
$(maEmissiveColorPickerID).attr('active', 'false');
},
onChange: function(hsb, hex, rgb, el) {
$(el).css('background-color', '#' + hex);
if ($(maEmissiveColorPickerID).attr('active') === 'true') {
maMaterialData.emissive = [(rgb.r/255), (rgb.g/255), (rgb.b/255)];
maGenerateJsonAndSave();
}
}
});
}
function loadDataInMaUi(materialDataPropertyValue) {
var entityMaterialData, materialDefinition;
try {
entityMaterialData = JSON.parse(materialDataPropertyValue);
}
catch(e) {
entityMaterialData = {
"materials":{}
};
}
if (entityMaterialData.materials.length === undefined) {
materialDefinition = entityMaterialData.materials;
} else {
materialDefinition = entityMaterialData.materials[0];
}
//MODEL (value other than "hifi_pbr" are not supposed to get the button, so we can assume.)
maMaterialData.model === "hifi_pbr";
//NAME
if (materialDefinition.name !== undefined) {
maMaterialData.name = materialDefinition.name;
} else {
maMaterialData.name = "";
}
maName.value = maMaterialData.name;
//ALBEDO
if (materialDefinition.defaultFallthrough === true && materialDefinition.albedo === undefined) {
maMaterialData.albedoIsActive = false;
maAlbedoIsActive.className = "uiMaterialAssistant-inactive";
maAlbedoColorPicker.style.pointerEvents = 'none';
maAlbedoColorPicker.style.backgroundColor = "#555555";
} else {
maMaterialData.albedoIsActive = true;
maAlbedoIsActive.className = "uiMaterialAssistant-active";
maAlbedoColorPicker.style.pointerEvents = 'auto';
maAlbedoColorPicker.style.backgroundColor = maGetRGB(maMaterialData.albedo);
}
if (materialDefinition.albedo !== undefined) {
maMaterialData.albedo = materialDefinition.albedo;
} else {
maMaterialData.albedo = DEFAULT_ALBEDO;
}
maAlbedoColorPicker.style.backgroundColor = maGetRGB(maMaterialData.albedo);
//ALBEDO MAP
if (materialDefinition.defaultFallthrough === true && materialDefinition.albedoMap === undefined) {
maMaterialData.albedoMapIsActive = false;
maAlbedoMapIsActive.className = "uiMaterialAssistant-inactive";
maAlbedoMap.disabled = true;
} else {
maMaterialData.albedoMapIsActive = true;
maAlbedoMapIsActive.className = "uiMaterialAssistant-active";
maAlbedoMap.disabled = false;
}
if (materialDefinition.albedoMap !== undefined) {
maMaterialData.albedoMap = materialDefinition.albedoMap;
} else {
maMaterialData.albedoMap = "";
}
maAlbedoMap.value = maMaterialData.albedoMap;
//METALLIC
if (materialDefinition.defaultFallthrough === true
&& materialDefinition.metallic === undefined
&& materialDefinition.metallicMap === undefined) {
maMaterialData.metallicIsActive = false;
maMetallicIsActive.className = "uiMaterialAssistant-inactive";
maMetallic.disabled = true;
maMetallicSlider.disabled = true;
maMetallicMap.disabled = true;
} else {
maMaterialData.metallicIsActive = true;
maMetallicIsActive.className = "uiMaterialAssistant-active";
maMetallic.disabled = false;
maMetallicSlider.disabled = false;
maMetallicMap.disabled = false;
}
if (materialDefinition.metallic !== undefined) {
maMaterialData.metallic = materialDefinition.metallic;
} else {
maMaterialData.metallic = DEFAULT_METALLIC_FOR_MA_UI;
}
maMetallic.value = maMaterialData.metallic;
maMetallicSlider.value = Math.floor(maMaterialData.metallic * 1000);
if (materialDefinition.metallicMap !== undefined) {
maMaterialData.metallicMap = materialDefinition.metallicMap;
} else {
maMaterialData.metallicMap = "";
}
maMetallicMap.value = maMaterialData.metallicMap;
//ROUGHNESS
if (materialDefinition.defaultFallthrough === true
&& materialDefinition.roughness === undefined
&& materialDefinition.roughnessMap === undefined) {
maMaterialData.roughnessIsActive = false;
maRoughnessIsActive.className = "uiMaterialAssistant-inactive";
maRoughness.disabled = true;
maRoughnessSlider.disabled = true;
maRoughnessMap.disabled = true;
} else {
maMaterialData.roughnessIsActive = true;
maRoughnessIsActive.className = "uiMaterialAssistant-active";
maRoughness.disabled = false;
maRoughnessSlider.disabled = false;
maRoughnessMap.disabled = false;
}
if (materialDefinition.roughness !== undefined) {
maMaterialData.roughness = materialDefinition.roughness;
} else {
maMaterialData.roughness = DEFAULT_ROUGHNESS;
}
maRoughness.value = maMaterialData.roughness;
maRoughnessSlider.value = Math.floor(maMaterialData.roughness * 1000);
if (materialDefinition.roughnessMap !== undefined) {
maMaterialData.roughnessMap = materialDefinition.roughnessMap;
} else {
maMaterialData.roughnessMap = "";
}
maRoughnessMap.value = maMaterialData.roughnessMap;
//NORMAL MAP
if (materialDefinition.defaultFallthrough === true && materialDefinition.normalMap === undefined) {
maMaterialData.normalMapIsActive = false;
maNormalMapIsActive.className = "uiMaterialAssistant-inactive";
maNormalMap.disabled = true;
} else {
maMaterialData.normalMapIsActive = true;
maNormalMapIsActive.className = "uiMaterialAssistant-active";
maNormalMap.disabled = false;
}
if (materialDefinition.normalMap !== undefined) {
maMaterialData.normalMap = materialDefinition.normalMap;
} else {
maMaterialData.normalMap = "";
}
maNormalMap.value = maMaterialData.normalMap;
//OPACITY
if (materialDefinition.defaultFallthrough === true
&& materialDefinition.opacity === undefined
&& materialDefinition.opacityMapMode === undefined) {
maMaterialData.opacityIsActive = false;
maOpacityIsActive.className = "uiMaterialAssistant-inactive";
maOpacity.disabled = true;
maOpacitySlider.disabled = true;
maOpacityMapModeDont.disabled = true;
maOpacityMapModeMask.disabled = true;
maOpacityMapModeBlend.disabled = true;
maOpacityCutoff.disabled = true;
maOpacityCutoffSlider.disabled = true;
} else {
maMaterialData.opacityIsActive = true;
maOpacityIsActive.className = "uiMaterialAssistant-active";
maOpacity.disabled = false;
maOpacitySlider.disabled = false;
maOpacityMapModeDont.disabled = false;
maOpacityMapModeMask.disabled = false;
maOpacityMapModeBlend.disabled = false;
maOpacityCutoff.disabled = false;
maOpacityCutoffSlider.disabled = false;
}
if (materialDefinition.opacity !== undefined) {
maMaterialData.opacity = materialDefinition.opacity;
} else {
maMaterialData.opacity = DEFAULT_OPACITY;
}
maOpacity.value = maMaterialData.opacity;
maOpacitySlider.value = Math.floor(maMaterialData.opacity * 1000);
if (materialDefinition.opacityMapMode !== undefined) {
maMaterialData.opacityMapMode = materialDefinition.opacityMapMode;
} else {
maMaterialData.opacityMapMode = "OPACITY_MAP_OPAQUE";
}
switch (maMaterialData.opacityMapMode) {
case "OPACITY_MAP_OPAQUE":
maOpacityMapModeDont.checked = true;
break;
case "OPACITY_MAP_MASK":
maOpacityMapModeMask.checked = true;
break;
case "OPACITY_MAP_BLEND":
maOpacityMapModeBlend.checked = true;
break;
default:
alert("ERROR: opacityMapMode = '" + maMaterialData.opacityMapMode + "'. Something has been broken in the code.");
}
if (materialDefinition.opacityCutoff !== undefined) {
maMaterialData.opacityCutoff = materialDefinition.opacityCutoff;
} else {
maMaterialData.opacityCutoff = DEFAULT_OPACITY_CUTOFF;
}
maOpacityCutoff.value = maMaterialData.opacityCutoff;
maOpacityCutoffSlider.value = Math.floor(maMaterialData.opacityCutoff * 1000);
//EMISSIVE
if (materialDefinition.defaultFallthrough === true
&& materialDefinition.emissive === undefined
&& materialDefinition.emissiveMap === undefined) {
maMaterialData.emissiveIsActive = false;
maEmissiveIsActive.className = "uiMaterialAssistant-inactive";
maBloom.disabled = true;
maBloomSlider.disabled = true;
maEmissiveMap.disabled = true;
maEmissiveColorPicker.style.pointerEvents = 'none';
maEmissiveColorPicker.style.backgroundColor = "#555555";
} else {
maMaterialData.emissiveIsActive = true;
maEmissiveIsActive.className = "uiMaterialAssistant-active";
maBloom.disabled = false;
maBloomSlider.disabled = false;
maEmissiveMap.disabled = false;
maEmissiveColorPicker.style.pointerEvents = 'auto';
maEmissiveColorPicker.style.backgroundColor = maGetRGB(maMaterialData.emissive);
}
if (materialDefinition.emissive !== undefined) {
maMaterialData.emissive = maGetColorValueFromEmissive(materialDefinition.emissive);
maMaterialData.bloom = maGetBloomFactorFromEmissive(materialDefinition.emissive);
} else {
maMaterialData.emissive = DEFAULT_EMISSIVE;
maMaterialData.bloom = DEFAULT_BLOOM_FACTOR;
}
maEmissiveColorPicker.style.backgroundColor = maGetRGB(maMaterialData.emissive);
maBloom.value = maMaterialData.bloom;
maBloomSlider.value = Math.floor(maMaterialData.bloom * 100);
if (materialDefinition.emissiveMap !== undefined) {
maMaterialData.emissiveMap = materialDefinition.emissiveMap;
} else {
maMaterialData.emissiveMap = "";
}
maEmissiveMap.value = maMaterialData.emissiveMap;
//UNLIT
if (materialDefinition.unlit !== undefined) {
maMaterialData.unlit = materialDefinition.unlit;
} else {
maMaterialData.unlit = false;
}
maUnlit.checked = maMaterialData.unlit;
//SCATTERING
if (materialDefinition.defaultFallthrough === true
&& materialDefinition.scattering === undefined
&& materialDefinition.scatteringMap === undefined) {
maMaterialData.scatteringIsActive = false;
maScatteringIsActive.className = "uiMaterialAssistant-inactive";
maScattering.disabled = true;
maScatteringSlider.disabled = true;
maScatteringMap.disabled = true;
} else {
maMaterialData.scatteringIsActive = true;
maScatteringIsActive.className = "uiMaterialAssistant-active";
maScattering.disabled = false;
maScatteringSlider.disabled = false;
maScatteringMap.disabled = false;
}
if (materialDefinition.scattering !== undefined) {
maMaterialData.scattering = materialDefinition.scattering;
} else {
maMaterialData.scattering = DEFAULT_SCATTERING;
}
maScattering.value = maMaterialData.scattering;
maScatteringSlider.value = Math.floor(maMaterialData.scattering * 1000);
if (materialDefinition.scatteringMap !== undefined) {
maMaterialData.scatteringMap = materialDefinition.scatteringMap;
} else {
maMaterialData.scatteringMap = "";
}
maScatteringMap.value = maMaterialData.scatteringMap;
//OCCLUSION MAP
if (materialDefinition.defaultFallthrough === true && materialDefinition.occlusionMap === undefined) {
maMaterialData.occlusionMapIsActive = false;
maOcclusionMapIsActive.className = "uiMaterialAssistant-inactive";
maOcclusionMap.disabled = true;
} else {
maMaterialData.occlusionMapIsActive = true;
maOcclusionMapIsActive.className = "uiMaterialAssistant-active";
maOcclusionMap.disabled = false;
}
if (materialDefinition.occlusionMap !== undefined) {
maMaterialData.occlusionMap = materialDefinition.occlusionMap;
} else {
maMaterialData.occlusionMap = "";
}
maOcclusionMap.value = maMaterialData.occlusionMap;
//CULL FACE MODE
if (materialDefinition.cullFaceMode !== undefined) {
maMaterialData.cullFaceMode = materialDefinition.cullFaceMode;
} else {
maMaterialData.cullFaceMode = "CULL_BACK";
}
switch (maMaterialData.cullFaceMode) {
case "CULL_BACK":
maCullFaceModeBack.checked = true;
break;
case "CULL_FRONT":
maCullFaceModeFront.checked = true;
break;
case "CULL_NONE":
maCullFaceModeNone.checked = true;
break;
default:
alert("ERROR: cullFaceMode = '" + maMaterialData.cullFaceMode + "'. Something has been broken in the code.");
}
}
function maGenerateJsonAndSave() {
var newMaterial = {};
var defaultFallthrough = false;
//NAME
if (maMaterialData.name != "") {
newMaterial.name = maMaterialData.name;
}
//ALBEDO & ALBEDOMAP
if (maMaterialData.albedoIsActive) {
newMaterial.albedo = maMaterialData.albedo;
if (maMaterialData.albedoMap !== "") {
newMaterial.albedoMap = maMaterialData.albedoMap;
}
} else {
defaultFallthrough = true;
}
//METALLIC & METALLICMAP
if (maMaterialData.metallicIsActive) {
if (maMaterialData.metallicMap === "") {
newMaterial.metallic = maMaterialData.metallic;
} else {
newMaterial.metallicMap = maMaterialData.metallicMap;
}
} else {
defaultFallthrough = true;
}
//ROUGHNESS & ROUGHNESSMAP
if (maMaterialData.roughnessIsActive) {
if (maMaterialData.roughnessMap === "") {
newMaterial.roughness = maMaterialData.roughness;
} else {
newMaterial.roughnessMap = maMaterialData.roughnessMap;
}
} else {
defaultFallthrough = true;
}
//NORMAL MAP
if (maMaterialData.normalMapIsActive) {
if (maMaterialData.normalMap !== "") {
newMaterial.normalMap = maMaterialData.normalMap;
}
} else {
defaultFallthrough = true;
}
//OPACITY
if (maMaterialData.opacityIsActive) {
switch (maMaterialData.opacityMapMode) {
case "OPACITY_MAP_OPAQUE":
newMaterial.opacity = maMaterialData.opacity;
break;
case "OPACITY_MAP_MASK":
newMaterial.opacityMapMode = maMaterialData.opacityMapMode;
newMaterial.opacityMap = maMaterialData.albedoMap;
newMaterial.opacityCutoff = maMaterialData.opacityCutoff;
break;
case "OPACITY_MAP_BLEND":
newMaterial.opacityMapMode = maMaterialData.opacityMapMode;
newMaterial.opacityMap = maMaterialData.albedoMap;
break;
default:
alert("ERROR: opacityMapMode = '" + maMaterialData.opacityMapMode + "'. Something has been broken in the code.");
}
} else {
defaultFallthrough = true;
}
//EMISSIVE
if (maMaterialData.emissiveIsActive) {
if (maMaterialData.emissiveMap === "") {
newMaterial.emissive = scaleEmissiveByBloomFactor(maMaterialData.emissive, maMaterialData.bloom);
} else {
newMaterial.emissiveMap = maMaterialData.emissiveMap;
}
} else {
defaultFallthrough = true;
}
//UNLIT
if (maMaterialData.unlit) {
newMaterial.unlit = maMaterialData.unlit;
}
//SCATTERING
if (maMaterialData.scatteringIsActive) {
if (maMaterialData.scatteringMap === "") {
newMaterial.scattering = maMaterialData.scattering;
} else {
newMaterial.scatteringMap = maMaterialData.scatteringMap;
}
} else {
defaultFallthrough = true;
}
//OCCLUSION MAP
if (maMaterialData.occlusionMapIsActive) {
if (maMaterialData.occlusionMap !== "") {
newMaterial.occlusionMap = maMaterialData.occlusionMap;
}
} else {
defaultFallthrough = true;
}
//CULL FACE MODE
newMaterial.cullFaceMode = maMaterialData.cullFaceMode;
//MODEL
newMaterial.model = maMaterialData.model;
//defaultFallthrough
if (defaultFallthrough) {
newMaterial.defaultFallthrough = true;
}
//insert newMaterial to materialData
var materialDataForUpdate = {
"materialVersion": 1,
"materials": []
};
materialDataForUpdate.materials.push(newMaterial);
//save to property
EventBridge.emitWebEvent(
JSON.stringify({
ids: [...selectedEntityIDs],
type: "saveMaterialData",
properties: {
materialData: JSON.stringify(materialDataForUpdate)
}
})
);
materialEditor.set(materialDataForUpdate);
}
function maGetColorValueFromEmissive(colorArray) {
if (Array.isArray(colorArray)) {
var max = maGetHighestValue(colorArray);
if (max > 1) {
var normalizer = 1/max;
return [colorArray[0] * normalizer, colorArray[1] * normalizer, colorArray[2] * normalizer];
} else {
return colorArray;
}
} else {
return [0,0,0];
}
}
function maGetBloomFactorFromEmissive(colorArray) {
if (Array.isArray(colorArray)) {
return maGetHighestValue(colorArray);
} else {
return 1;
}
}
function maGetHighestValue(colorArray) {
var highest = colorArray[0];
for (var i = 0; i < colorArray.length; i++) {
if (colorArray[i] > highest) {
highest = colorArray[i];
}
}
return highest;
}
function scaleEmissiveByBloomFactor(colorArray, bloomFactor) {
return [colorArray[0] * bloomFactor, colorArray[1] * bloomFactor, colorArray[2] * bloomFactor];
}
function maGetRGB(colorArray){
if (colorArray === undefined) {
return "#000000";
} else {
return "rgb(" + Math.floor(colorArray[0] * 255) + ", " + Math.floor(colorArray[1] * 255) + ", " + Math.floor(colorArray[2] * 255) + ")";
}
}
/**
* @param {string or object} materialData - json of the materialData as a string or as an object.
*/
function maGetMaterialDataAssistantAvailability(materialData) {
var materialDataJSON, materialDataString;
if (typeof materialData === "string") {
materialDataJSON = JSON.parse(materialData);
materialDataString = materialData;
} else {
materialDataJSON = materialData;
materialDataString = JSON.stringify(materialData);
}
if (getPropertyInputElement("materialURL").value === "materialData" && materialDataString.indexOf("hifi_shader_simple") === -1 &&
materialDataString.indexOf("glossMap") === -1 && materialDataString.indexOf("specularMap") === -1 &&
materialDataString.indexOf("bumpMap") === -1 && materialDataString.indexOf("lightMap") === -1 &&
materialDataString.indexOf("texCoordTransform0") === -1 && materialDataString.indexOf("texCoordTransform1") === -1 &&
(materialDataJSON.materials === undefined || materialDataJSON.materials.length <= 1 || typeof materialDataJSON.materials === "object")) {
showMaterialAssistantButton();
} else {
hideMaterialAssistantButton();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -6,6 +6,7 @@
// Modified by David Back on 1/9/2018
// Copyright 2014 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors
// Copyright 2022 Overte e.V.
//
// This script implements a class useful for building tools for editing entities.
//
@ -99,6 +100,9 @@ SelectionManager = (function() {
}
if (messageParsed.method === "selectEntity") {
if (!that.editEnabled) {
return;
}
if (!SelectionDisplay.triggered() || SelectionDisplay.triggeredHand === messageParsed.hand) {
if (wantDebug) {
print("setting selection to " + messageParsed.entityID);
@ -187,6 +191,21 @@ SelectionManager = (function() {
that.pointingAtDesktopWindowRight = false;
that.pointingAtTabletLeft = false;
that.pointingAtTabletRight = false;
that.editEnabled = true;
that.updateEditSettings = function(data) {
if (data.createAppMode) {
if (data.createAppMode === "object"){
that.editEnabled = true;
} else {
that.editEnabled = false;
if(that.hasSelection()){
that.clearSelections();
}
}
}
}
that.saveProperties = function() {
that.savedProperties = {};
@ -1303,6 +1322,9 @@ SelectionDisplay = (function() {
}
function makeClickHandler(hand) {
return function (clicked) {
if (!SelectionManager.editEnabled) {
return;
}
// Don't allow both hands to trigger at the same time
if (that.triggered() && hand !== that.triggeredHand) {
return;
@ -1318,6 +1340,9 @@ SelectionDisplay = (function() {
}
function makePressHandler(hand) {
return function (value) {
if (!SelectionManager.editEnabled) {
return;
}
if (value >= TRIGGER_ON_VALUE && !that.triggered() && !pointingAtDesktopWindowOrTablet(hand)) {
that.pressedHand = hand;
that.updateHighlight({});
@ -1413,6 +1438,9 @@ SelectionDisplay = (function() {
if (wantDebug) {
print("=============== eST::MousePressEvent BEG =======================");
}
if (!SelectionManager.editEnabled) {
return;
}
if (!event.isLeftButton && !that.triggered()) {
// EARLY EXIT-(if another mouse button than left is pressed ignore it)
return false;

View file

@ -178,6 +178,18 @@ TabBar {
editTabView.currentIndex = 2
}
}
NewEntityButton {
icon: "icons/voxels.svg"
text: "VOXELS"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked",
params: { buttonName: "newPolyVoxButton" }
});
editTabView.currentIndex = 2
}
}
}
HifiControls.Button {
@ -274,7 +286,7 @@ TabBar {
}
EditTabButton {
title: "GRID"
title: "TOOLS"
active: true
enabled: true
property string originalUrl: ""

View file

@ -184,6 +184,18 @@ TabBar {
editTabView.currentIndex = tabIndex.properties
}
}
NewEntityButton {
icon: "icons/voxels.svg"
text: "VOXELS"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked",
params: { buttonName: "newPolyVoxButton" }
});
editTabView.currentIndex = tabIndex.properties
}
}
}
HifiControls.Button {
@ -264,7 +276,7 @@ TabBar {
}
EditTabButton {
title: "GRID"
title: "TOOLS"
active: true
enabled: true
property string originalUrl: ""

View file

@ -0,0 +1,560 @@
//
// NewPolyVoxDialog.qml
// Created by dr Karol Suprynowicz on 2022.05.17.
// based on NewModelDialog.qml
// qml/hifi
//
// Copyright 2017 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors
// Copyright 2022 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Dialogs 1.2 as OriginalDialogs
import stylesUit 1.0
import controlsUit 1.0
import hifi.dialogs 1.0
Rectangle {
id: newPolyVoxDialog
// width: parent.width
// height: parent.height
HifiConstants { id: hifi }
color: hifi.colors.baseGray;
signal sendToScript(var message);
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
property bool keyboardRasied: false
function errorMessageBox(message) {
try {
return desktop.messageBox({
icon: hifi.icons.warning,
defaultButton: OriginalDialogs.StandardButton.Ok,
title: "Error",
text: message
});
} catch(e) {
Window.alert(message);
}
}
Item {
id: column1
anchors.rightMargin: 10
anchors.leftMargin: 10
anchors.bottomMargin: 10
anchors.topMargin: 0
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: keyboard.top
ComboBox {
id: texturePreset
currentIndex: 0
property var texturePresetArray: ["Material presets",
"Grass + ground",
"Bricks",
"Stone",
"Concrete",
"Rock"]
width: 200
z: 100
transformOrigin: Item.Center
model: texturePresetArray
onCurrentIndexChanged: {
switch (currentIndex) {
// Clear texture entries
case 0:
xTextureURL.text = "";
yTextureURL.text = "";
zTextureURL.text = "";
break;
// Grass + ground
case 1:
xTextureURL.text = "qrc:///serverless/Textures/ground_5-2K/2K-ground_5-diffuse.jpg";
yTextureURL.text = "qrc:///serverless/Textures/ground_grass_gen_05.png";
zTextureURL.text = "qrc:///serverless/Textures/ground_5-2K/2K-ground_5-diffuse.jpg";
break;
// Bricks
case 2:
xTextureURL.text = "qrc:///serverless/Textures/2K-wall_stone_2-diffuse_l.jpg";
yTextureURL.text = "qrc:///serverless/Textures/2K-stone_floor_3-diffuse_l.jpg";
zTextureURL.text = "qrc:///serverless/Textures/2K-wall_stone_2-diffuse_l.jpg";
break;
// Stone
case 3:
xTextureURL.text = "qrc:///serverless/Textures/wall_l.png";
yTextureURL.text = "qrc:///serverless/Textures/floor_l.png";
zTextureURL.text = "qrc:///serverless/Textures/wall_l.png";
break;
// Concrete
case 4:
xTextureURL.text = "qrc:///serverless/Textures/concrete_12-2K/2K-concrete_12-diffuse.jpg";
yTextureURL.text = "qrc:///serverless/Textures/concrete_12-2K/2K-concrete_12-diffuse.jpg";
zTextureURL.text = "qrc:///serverless/Textures/concrete_12-2K/2K-concrete_12-diffuse.jpg";
break;
// Rock
case 5:
xTextureURL.text = "qrc:///serverless/Textures/Rock026_2K-JPG/Rock026_2K_Color.jpg";
yTextureURL.text = "qrc:///serverless/Textures/Rock026_2K-JPG/Rock026_2K_Color.jpg";
zTextureURL.text = "qrc:///serverless/Textures/Rock026_2K-JPG/Rock026_2K_Color.jpg";
break;
}
}
}
Text {
id: text1
anchors.top: texturePreset.bottom
anchors.topMargin: 5
text: qsTr("X Texture URL")
color: "#ffffff"
font.pixelSize: 12
}
TextInput {
id: xTextureURL
height: 20
text: qsTr("")
color: "white"
anchors.top: text1.bottom
anchors.topMargin: 5
anchors.left: parent.left
anchors.leftMargin: 0
anchors.right: parent.right
anchors.rightMargin: 0
font.pixelSize: 12
onAccepted: {
newPolyVoxDialog.keyboardEnabled = false;
}
onTextChanged : {
if (xTextureURL.text.length === 0){
button1.enabled = false;
} else {
button1.enabled = true;
}
}
MouseArea {
anchors.fill: parent
onClicked: {
newPolyVoxDialog.keyboardEnabled = HMD.active
parent.focus = true;
parent.forceActiveFocus();
xTextureURL.cursorPosition = xTextureURL.positionAt(mouseX, mouseY, TextInput.CursorBetweenCharaters);
}
}
}
Rectangle {
id: textInputBox1
color: "white"
anchors.fill: xTextureURL
opacity: 0.1
}
Text {
id: text2
text: qsTr("Y Texture URL")
color: "#ffffff"
font.pixelSize: 12
anchors.top: textInputBox1.bottom
anchors.topMargin: 5
}
TextInput {
id: yTextureURL
height: 20
text: qsTr("")
color: "white"
anchors.top: text2.bottom
anchors.topMargin: 5
anchors.left: parent.left
anchors.leftMargin: 0
anchors.right: parent.right
anchors.rightMargin: 0
font.pixelSize: 12
onAccepted: {
newPolyVoxDialog.keyboardEnabled = false;
}
onTextChanged : {
if (yTextureURL.text.length === 0){
button1.enabled = false;
} else {
button1.enabled = true;
}
}
MouseArea {
anchors.fill: parent
onClicked: {
newPolyVoxDialog.keyboardEnabled = HMD.active
parent.focus = true;
parent.forceActiveFocus();
yTextureURL.cursorPosition = yTextureURL.positionAt(mouseX, mouseY, TextInput.CursorBetweenCharaters);
}
}
}
Rectangle {
id: textInputBox2
color: "white"
anchors.fill: yTextureURL
opacity: 0.1
}
Text {
id: text3
text: qsTr("Z Texture URL")
color: "#ffffff"
font.pixelSize: 12
anchors.top: textInputBox2.bottom
anchors.topMargin: 5
}
TextInput {
id: zTextureURL
height: 20
text: qsTr("")
color: "white"
anchors.top: text3.bottom
anchors.topMargin: 5
anchors.left: parent.left
anchors.leftMargin: 0
anchors.right: parent.right
anchors.rightMargin: 0
font.pixelSize: 12
onAccepted: {
newPolyVoxDialog.keyboardEnabled = false;
}
onTextChanged : {
if (zTextureURL.text.length === 0){
button1.enabled = false;
} else {
button1.enabled = true;
}
}
MouseArea {
anchors.fill: parent
onClicked: {
newPolyVoxDialog.keyboardEnabled = HMD.active
parent.focus = true;
parent.forceActiveFocus();
zTextureURL.cursorPosition = zTextureURL.positionAt(mouseX, mouseY, TextInput.CursorBetweenCharaters);
}
}
}
Rectangle {
id: textInputBox3
color: "white"
anchors.fill: zTextureURL
opacity: 0.1
}
Text {
id: textVolumeSize
text: qsTr("Volume Size (number of voxels along the edge)")
color: "#ffffff"
font.pixelSize: 12
anchors.top: zTextureURL.bottom
anchors.topMargin: 5
}
Row {
id: rowVolumeSize
height: 50
spacing: 30
anchors.top: textVolumeSize.bottom
anchors.topMargin: 5
anchors.left: parent.left
anchors.leftMargin: 0
anchors.right: parent.right
anchors.rightMargin: 0
Text {
id: textVolumeSizeX
text: qsTr("X")
color: "#ffffff"
font.pixelSize: 12
}
TextInput {
id: volumeSizeX
height: 20
width: 50
anchors.left: textVolumeSizeX.right
anchors.leftMargin: 3
text: qsTr("16")
color: "white"
font.pixelSize: 12
validator: IntValidator{bottom: 8; top: 64;}
onAccepted: {
newPolyVoxDialog.keyboardEnabled = false;
}
MouseArea {
anchors.fill: parent
onClicked: {
newPolyVoxDialog.keyboardEnabled = HMD.active
parent.focus = true;
parent.forceActiveFocus();
volumeSizeX.cursorPosition = volumeSizeX.positionAt(mouseX, mouseY, TextInput.CursorBetweenCharaters);
}
}
}
Rectangle {
id: textInputBoxVolumeSizeX
color: "white"
anchors.fill: volumeSizeX
opacity: 0.1
}
Text {
id: textVolumeSizeY
text: qsTr("Y")
color: "#ffffff"
font.pixelSize: 12
anchors.left: volumeSizeX.right
anchors.leftMargin: 5
}
TextInput {
id: volumeSizeY
height: 20
width: 50
anchors.left: textVolumeSizeY.right
anchors.leftMargin: 3
text: qsTr("16")
color: "white"
font.pixelSize: 12
validator: IntValidator{bottom: 8; top: 64;}
onAccepted: {
newPolyVoxDialog.keyboardEnabled = false;
}
MouseArea {
anchors.fill: parent
onClicked: {
newPolyVoxDialog.keyboardEnabled = HMD.active
parent.focus = true;
parent.forceActiveFocus();
volumeSizeY.cursorPosition = volumeSizeY.positionAt(mouseX, mouseY, TextInput.CursorBetweenCharaters);
}
}
}
Rectangle {
id: textInputBoxVolumeSizeY
color: "white"
anchors.fill: volumeSizeY
opacity: 0.1
}
Text {
id: textVolumeSizeZ
text: qsTr("X")
color: "#ffffff"
font.pixelSize: 12
anchors.left: volumeSizeY.right
anchors.leftMargin: 5
}
TextInput {
id: volumeSizeZ
height: 20
width: 50
anchors.left: textVolumeSizeZ.right
anchors.leftMargin: 3
text: qsTr("16")
color: "white"
font.pixelSize: 12
validator: IntValidator{bottom: 8; top: 64;}
onAccepted: {
newPolyVoxDialog.keyboardEnabled = false;
}
MouseArea {
anchors.fill: parent
onClicked: {
newPolyVoxDialog.keyboardEnabled = HMD.active
parent.focus = true;
parent.forceActiveFocus();
volumeSizeZ.cursorPosition = volumeSizeZ.positionAt(mouseX, mouseY, TextInput.CursorBetweenCharaters);
}
}
}
Rectangle {
id: textInputBoxVolumeSizeZ
color: "white"
anchors.fill: volumeSizeZ
opacity: 0.1
}
}
Row {
id: row1
height: 400
spacing: 30
anchors.top: rowVolumeSize.bottom
anchors.topMargin: 5
anchors.left: parent.left
anchors.leftMargin: 0
anchors.right: parent.right
anchors.rightMargin: 0
Column {
id: column2
width: 200
height: 600
spacing: 10
CheckBox {
id: grabbable
text: qsTr("Grabbable")
}
CheckBox {
id: collisions
text: qsTr("Collisions")
}
Row {
id: row2
width: 200
height: 400
spacing: 20
}
}
Column {
id: column3
height: 400
spacing: 10
Text {
id: text4
text: qsTr("Voxel type")
color: "#ffffff"
font.pixelSize: 12
}
ComboBox {
id: surfaceStyle
currentIndex: 3
property var surfaceStyleArray: ["Marching Cubes",
"Cubic",
"Edged Cubic",
"Edged Marching Cubes"]
width: 200
z: 100
transformOrigin: Item.Center
model: surfaceStyleArray
}
Text {
id: textInitialShape
text: qsTr("Initial shape")
color: "#ffffff"
font.pixelSize: 12
}
ComboBox {
id: initialShape
currentIndex: 0
property var initialShapeArray: ["Box",
"Plane, 1/4 full",
"Plane, 3/4 full",
"Single voxel",
]
width: 200
z: 100
transformOrigin: Item.Center
model: initialShapeArray
}
Row {
id: row3
width: 200
height: 400
spacing: 5
anchors.horizontalCenter: column3.horizontalCenter
anchors.horizontalCenterOffset: -20
Button {
id: button1
text: qsTr("Create")
z: -1
enabled: false
onClicked: {
newPolyVoxDialog.sendToScript({
method: "newPolyVoxDialogAdd",
params: {
xTextureURL: xTextureURL.text,
yTextureURL: yTextureURL.text,
zTextureURL: zTextureURL.text,
volumeSizeX: volumeSizeX.text,
volumeSizeY: volumeSizeY.text,
volumeSizeZ: volumeSizeZ.text,
surfaceStyleIndex: surfaceStyle.currentIndex,
initialShapeIndex: initialShape.currentIndex,
grabbable: grabbable.checked,
collisions: collisions.checked,
}
});
}
}
Button {
id: button2
z: -1
text: qsTr("Cancel")
onClicked: {
newPolyVoxDialog.sendToScript({method: "newPolyVoxDialogCancel"})
}
}
}
}
}
}
Keyboard {
id: keyboard
raised: parent.keyboardEnabled && parent.keyboardRaised
numeric: parent.punctuationMode
anchors {
bottom: parent.bottom
bottomMargin: 40
left: parent.left
right: parent.right
}
}
}

View file

@ -0,0 +1,20 @@
import QtQuick 2.7
import QtQuick.Controls 2.2
StackView {
id: stackView
anchors.fill: parent
anchors.leftMargin: 10
anchors.rightMargin: 10
anchors.topMargin: 40
signal sendToScript(var message);
NewPolyVoxDialog {
id: dialog
anchors.fill: parent
Component.onCompleted:{
dialog.sendToScript.connect(stackView.sendToScript);
}
}
}

View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 50 50"
style="enable-background:new 0 0 50 50;"
xml:space="preserve"
sodipodi:docname="voxels.svg"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs27" /><sodipodi:namedview
id="namedview25"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="17.42"
inkscape:cx="25.028703"
inkscape:cy="20.407577"
inkscape:window-width="1918"
inkscape:window-height="1042"
inkscape:window-x="1920"
inkscape:window-y="18"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<style
type="text/css"
id="style2">
.st0{opacity:0.9;}
.st1{fill:#1E1E1E;}
.st2{fill:#EAEAEA;}
</style>
<path
class="st2"
d="M 42.986179,11.33074 34.367376,6.3639716 c -0.438244,-0.2921628 -1.02257,-0.2921628 -1.460814,0 L 25.164247,10.892495 17.129769,6.2178902 c -0.438244,-0.2921628 -1.02257,-0.2921628 -1.460814,0 L 7.196233,11.184658 C 6.7579887,11.476821 6.4658259,11.915065 6.4658259,12.35331 v 9.933535 c 0,0.438244 0.2921628,1.02257 0.7304071,1.168651 l 7.888397,4.528524 v 9.057048 c 0,0.438244 0.292162,1.02257 0.730407,1.168651 l 8.618803,4.966768 c 0.292163,0.146081 0.438244,0.146081 0.730407,0.146081 0.292163,0 0.438244,0 0.730407,-0.146081 l 8.618803,-4.966768 c 0.438245,-0.292163 0.730407,-0.730407 0.730407,-1.168651 v -8.910966 l 7.888397,-4.528524 c 0.438244,-0.292163 0.730407,-0.730407 0.730407,-1.168652 V 12.499391 C 43.716586,12.061147 43.424423,11.622902 42.986179,11.33074 Z m -2.04514,8.910965 -5.989337,-3.505954 V 9.8699255 l 5.989337,3.5059535 z m -7.011907,5.551094 -7.304071,-4.236361 v -8.180559 l 5.989338,-3.5059535 v 6.8658255 l -2.921628,1.752977 c -0.584326,0.292163 -0.730407,1.02257 -0.438244,1.606896 0.292162,0.438244 0.584325,0.584326 1.022569,0.584326 0.146082,0 0.438245,0 0.584326,-0.146082 l 2.921628,-1.752977 5.989338,3.505954 z m -7.596234,5.697175 v -6.719745 l 5.989338,3.505954 v 6.719745 z M 23.703433,20.095624 17.714095,16.58967 V 9.7238441 l 5.989338,3.5059539 z M 9.2413727,13.229798 15.230711,9.7238441 V 16.58967 l -3.213791,1.899058 c -0.584326,0.292163 -0.730407,1.02257 -0.438244,1.606896 0.292163,0.438244 0.584325,0.584326 1.02257,0.584326 0.146081,0 0.438244,0 0.584325,-0.146082 l 3.213791,-1.899058 5.989338,3.505954 -5.989338,3.505954 -7.1579893,-4.09028 z m 15.9228743,27.317224 -7.157989,-4.09028 v -8.180559 l 5.989338,-3.505954 v 6.865826 l -2.921629,1.606896 c -0.584325,0.292163 -0.730407,1.02257 -0.438244,1.606895 0.292163,0.438245 0.584326,0.584326 1.02257,0.584326 0.146082,0 0.438244,0 0.584326,-0.146081 l 2.921628,-1.606896 5.989338,3.505954 z"
id="path10"
style="stroke-width:1.46081;fill:#000000" />
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -4,6 +4,7 @@
// Created by Ryan Huffman on 13 Nov 2014
// Copyright 2014 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
// Copyright 2022 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -495,6 +496,22 @@ input[type=button].secondary_red, button.hifi-edit-button.secondary_red {
background: linear-gradient(#d42043 20%, #94132e 100%);
cursor: pointer;
}
input[type=button].secondary_blue, button.hifi-edit-button.secondary_blue {
font-family: Raleway-Bold;
font-size: 10px;
text-transform: uppercase;
vertical-align: top;
height: 18px;
min-width: 60px;
padding: 0 14px;
margin-right: 6px;
border-radius: 4px;
border: none;
color: #fff;
background-color: #1080b8;
background: linear-gradient(#00b4ef 20%, #1080b8 100%);
cursor: pointer;
}
input[type=button]:enabled:hover, button.hifi-edit-button:enabled:hover {
background: linear-gradient(#000, #000);
border: none;
@ -2062,6 +2079,42 @@ div.entity-list-menu {
cursor: pointer;
}
div.tools-select-menu {
position: relative;
display: none;
width: 370px;
height: 0px;
top: 0px;
left: 8px;
right: 0;
bottom: 0;
border-style: solid;
border-color: #505050;
border-width: 1px;
background-color: #c0c0c0;
z-index: 2;
cursor: pointer;
}
div.tools-help-popup {
font-family: FiraSans-SemiBold;
font-size: 15px;
position: relative;
display: none;
width: 690px;
height: auto;
top: 0px;
left: 8px;
right: 0;
bottom: 0;
border-style: solid;
border-color: #505050;
border-width: 1px;
background-color: #404040;
z-index: 2;
cursor: pointer;
}
div.menu-separator{
width: 100%;
height: 2px;

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

View file

@ -0,0 +1,223 @@
/*
// materialAssistant.css
//
// Created by Alezia Kurdis on May 17th, 2022.
// Copyright 2022 Vircadia contributors.
// Copyright 2022 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
*/
#uiMaterialAssistant {
position: fixed;
display: none;
width: 100%;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
border: 0px;
padding: 0px;
background-color: #404040;
z-index: 2;
border-collapse: collapse;
overflow-y: scroll;
}
#uiMaterialAssistant-headerContainer {
width: 98%;
text-align: right;
padding: 6px;
display: flex;
}
#uiMaterialAssistant-closeButton {
font-family: Raleway-Bold;
font-size: 18px;
text-align: center;
border: 0px;
padding: 6px;
color: #FFFFFF;
}
#uiMaterialAssistant-sidewalk {
width: 100%;
background-image: url("img/sidewalk.png");
background-repeat: repeat;
}
#uiMaterialAssistant-formContainer {
background-color: #404040;
width: 90%;
padding: 2px;
}
div.uiMaterialAssistant-group{
background-color: #2E2E2E;
border: 2px solid #aaaaaa;
padding: 5px;
margin: 0px 0px 6px 0px;
font-family: Raleway-SemiBold;
text-decoration: none;
font-size: 13px;
color: #ffffff;
}
font.uiMaterialAssistant-Explain{
background-color: #2E2E2E;
font-family: Raleway-Regular;
text-decoration: Italic;
font-size: 10px;
color: #8ad5ff;
}
font.uiMaterialAssistant-label{
background-color: #2E2E2E;
font-family: Raleway-SemiBold;
text-decoration: none;
font-size: 12px;
color: #D2D2D2;
}
font.uiMaterialAssistant-title{
background-color: #404040;
font-family: Raleway-Bold;
font-size: 18px;
text-decoration: none;
color: #F2F2F2;
}
input[type=range].uiMaterialAssistant-slider {
margin: 10px 0;
width: 100%;
background-color: #2E2E2E;
}
input[type=range].uiMaterialAssistant-slider:focus {
outline: none;
}
input[type=range].uiMaterialAssistant-slider::-webkit-slider-runnable-track {
width: 100%;
height: 6px;
cursor: pointer;
animate: 0.2s;
box-shadow: 0px 0px 0px #000000;
background: #ffffff;
border-radius: 0px;
border: 0px solid #000000;
}
input[type=range].uiMaterialAssistant-slider:disabled::-webkit-slider-runnable-track {
background: #555555;
border: 0px solid #333333;
}
input[type=range].uiMaterialAssistant-slider::-webkit-slider-thumb {
box-shadow: 1px 1px 1px #000000;
border: 1px solid #000000;
height: 22px;
width: 12px;
border-radius: 10px;
background: #FFFFFF;
cursor: pointer;
-webkit-appearance: none;
margin-top: -8.5px;
}
input[type=range].uiMaterialAssistant-slider:disabled::-webkit-slider-thumb {
box-shadow: 1px 1px 1px #000000;
border: 1px solid #333333;
height: 22px;
width: 12px;
border-radius: 10px;
background: #555555;
}
input[type=range].uiMaterialAssistant-slider:focus::-webkit-slider-runnable-track {
background: #ffffff;
}
input[type=text].uiMaterialAssistant-input {
background-color: #ffffff;
border: 1px solid #000000;
font-family: FiraSans-SemiBold;
font-size: 14px;
width: auto;
height: auto;
padding: 3px;
margin: 3px 0px 3px 0px;
color: #000000;
}
input[type=text].uiMaterialAssistant-input:disabled {
background-color: #555555;
color: #888888;
}
input[type=radio].uiMaterialAssistant-radio {
width: 16px;
height: 16px;
margin: 2px;
padding: 3px;
}
input[type=radio].uiMaterialAssistant-radio:focus {
outline: none;
}
input[type=checkbox].uiMaterialAssistant-checkbox {
width: 16px;
height: 16px;
margin: 2px;
padding: 3px;
display: block;
text-align: left;
}
input[type=checkbox].uiMaterialAssistant-checkbox:focus {
outline: none;
}
button.uiMaterialAssistant-active {
width: 12px;
height: 12px;
margin: 0px;
padding: 0px;
border: 1px solid #000000;
background-color: #00FF00;
}
button.uiMaterialAssistant-active:hover {
background-color: #33FF33;
border: 1px solid #ffffff;
}
button.uiMaterialAssistant-active:focus {
outline: none;
}
button.uiMaterialAssistant-inactive {
width: 12px;
height: 12px;
margin: 0px;
padding: 0px;
border: 1px solid #000000;
background-color: #555555;
}
button.uiMaterialAssistant-inactive:hover {
background-color: #777777;
border: 1px solid #ffffff;
}
button.uiMaterialAssistant-inactive:focus {
outline: none;
}
div.uiMaterialAssistant-color-picker {
background-color: #000000;
border: 1px solid #AAAAAA;
width: 120px;
height: 30px;
}

View file

@ -3,6 +3,7 @@
//
// Created by Ryan Huffman on 6 Nov 2014
// Copyright 2014 High Fidelity, Inc.
// Copyright 2022 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -20,7 +21,80 @@
<script type="text/javascript" src="js/gridControls.js"></script>
</head>
<body onload='loaded();'>
<div id="mode-section" class="section">
<div class="property container">
<label for="create-app-mode">Create app mode </label>
<div class="property container">
<input name="create-app-mode" type="button" class="entity-list-menutitle" id="create-app-mode" value="Create app mode&#9662;" />
</div>
</div>
<div class="tools-select-menu" id="edit-mode-menu" >
<button class="menu-button" id="edit-mode-object" >
<div class = "menu-item">
<div class = "menu-item-caption">Object mode</div>
<div class = "menu-item-shortcut"></div>
</div>
</button>
<button class="menu-button" id="edit-mode-voxel" >
<div class = "menu-item">
<div class = "menu-item-caption">Voxel edit mode</div>
<div class = "menu-item-shortcut"></div>
</div>
</button>
</div>
</div>
<div id="voxels-section" class="section">
<h2>Voxel edit settings</h2>
<div class="property container">
<label for="voxel-edit-mode">Voxel edit mode </label>
<div class="property container">
<input name="voxel-edit-mode" type="button" class="entity-list-menutitle" id="voxel-edit-mode" value="Voxel edit mode&#9662;" />
</div>
<label for="voxel-sphere-size">&nbsp;&nbsp;Sphere/cube size <span class="unit">m</span></label>
<div class="number">
<input type="number" id="voxel-sphere-size" min="0.01" step="0.2" />
</div>
</div>
<div class="tools-select-menu" id="voxel-edit-mode-menu" >
<button class="menu-button" id="voxel-edit-mode-single" >
<div class = "menu-item">
<div class = "menu-item-caption">Single voxels</div>
<div class = "menu-item-shortcut"></div>
</div>
</button>
<button class="menu-button" id="voxel-edit-mode-sphere" >
<div class = "menu-item">
<div class = "menu-item-caption">Spheres</div>
<div class = "menu-item-shortcut"></div>
</div>
</button>
<button class="menu-button" id="voxel-edit-mode-cube" >
<div class = "menu-item">
<div class = "menu-item-caption">Cubes</div>
<div class = "menu-item-shortcut"></div>
</div>
</button>
</div>
<div class="property container">
<label for="voxel-remove">&nbsp;&nbsp;Remove voxels</label>
<div style="width: 100%">
<input type='checkbox' id="voxel-remove" style="width: 100%">
<label for="voxel-remove">&nbsp;</label>
</div>
<div class="property container">
<input name="voxel-help-button" type="button" class="entity-list-menutitle" id="voxel-help-button" value="Voxel edit help&#9662;" />
</div>
</div>
<div class="tools-help-popup" id="voxel-help-popup" >
<p>To edit voxels, Voxel Edit Mode needs to be selected.</p>
<p>Desktop mode:</p>
<p>Click the left mouse button to add a voxel. Click the middle mouse button to remove a voxel. Hold the mouse button and move the mouse to add or remove voxels in a single plane. The plane is determined by the direction in which you are looking when first voxel is added or removed (for example, look downwards to draw in horizontal plane).</p>
<p>VR mode:</p>
<p>Press the trigger to add a voxel. Press the trigger while holding the grip to remove a voxel. Hold the trigger and move the controller to add or remove voxels in a single plane. The plane is determined by the direction in which the controller's ray points when the first voxel is added or removed (for example point downwards to draw in the horizontal plane). Hold both grips and move your hands together or apart to change the size of the edit sphere.</p>
</div>
</div>
<div id="grid-section" class="section">
<h2>Grid settings</h2>
<div class="property container">
<label for="horiz-grid-visible">Visible</label>
<div style="width: 100%">
@ -67,5 +141,6 @@
</div>
</div>
</div>
<div id="menuBackgroundOverlay" ></div>
</body>
</html>

View file

@ -2,12 +2,30 @@
//
// Created by Ryan Huffman on 6 Nov 2014
// Copyright 2014 High Fidelity, Inc.
// Copyright 2022 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
var createAppModeValue = "";
var voxelEditModeValue = "";
function loaded() {
openEventBridge(function() {
elCreateAppModeMenu = document.getElementById("create-app-mode");
elEditModeObject = document.getElementById("edit-mode-object");
elEditModeVoxel = document.getElementById("edit-mode-voxel");
elVoxelEditModeMenu = document.getElementById("voxel-edit-mode");
elVoxelEditModeSingle = document.getElementById("voxel-edit-mode-single");
elVoxelEditModeSphere = document.getElementById("voxel-edit-mode-sphere");
elVoxelEditModeCube = document.getElementById("voxel-edit-mode-cube");
elMenuBackgroundOverlay = document.getElementById("menuBackgroundOverlay");
elVoxelHelpButton = document.getElementById("voxel-help-button");
elVoxelHelpPopup = document.getElementById("voxel-help-popup");
elVoxelSphereSize = document.getElementById("voxel-sphere-size");
elVoxelRemove = document.getElementById("voxel-remove");
elPosY = document.getElementById("horiz-y");
elMinorSpacing = document.getElementById("minor-spacing");
elMajorSpacing = document.getElementById("major-spacing");
@ -16,10 +34,102 @@ function loaded() {
elMoveToSelection = document.getElementById("move-to-selection");
elMoveToAvatar = document.getElementById("move-to-avatar");
elCreateAppModeMenu.onclick = function() {
document.getElementById("menuBackgroundOverlay").style.display = "block";
document.getElementById("edit-mode-menu").style.display = "block";
};
elVoxelEditModeMenu.onclick = function() {
document.getElementById("menuBackgroundOverlay").style.display = "block";
document.getElementById("voxel-edit-mode-menu").style.display = "block";
};
elVoxelHelpButton.onclick = function() {
document.getElementById("menuBackgroundOverlay").style.display = "block";
document.getElementById("voxel-help-popup").style.display = "block";
};
elMenuBackgroundOverlay.onclick = function() {
closeAllEntityListMenu();
};
elEditModeObject.onclick = function() {
createAppModeValue = "object";
elCreateAppModeMenu.value = "Object mode\u25BE";
emitUpdateEditTools();
closeAllEntityListMenu();
};
elEditModeVoxel.onclick = function() {
createAppModeValue = "voxel";
elCreateAppModeMenu.value = "Voxel edit mode\u25BE";
emitUpdateEditTools();
closeAllEntityListMenu();
};
elVoxelEditModeSingle.onclick = function() {
voxelEditModeValue = "single";
elVoxelEditModeMenu.value = "Single voxels\u25BE";
emitUpdateEditTools();
closeAllEntityListMenu();
};
elVoxelEditModeSphere.onclick = function() {
voxelEditModeValue = "sphere";
elVoxelEditModeMenu.value = "Spheres\u25BE";
emitUpdateEditTools();
closeAllEntityListMenu();
};
elVoxelEditModeCube.onclick = function() {
voxelEditModeValue = "cube";
elVoxelEditModeMenu.value = "Cubes\u25BE";
emitUpdateEditTools();
closeAllEntityListMenu();
};
elVoxelHelpPopup.onclick = function() {
closeAllEntityListMenu();
};
if (window.EventBridge !== undefined) {
EventBridge.scriptEventReceived.connect(function(data) {
data = JSON.parse(data);
if (data.createAppMode !== undefined) {
if (data.createAppMode === "object") {
createAppModeValue = data.createAppMode;
elCreateAppModeMenu.value = "Object mode\u25BE";
}
if (data.createAppMode === "voxel") {
createAppModeValue = data.createAppMode;
elCreateAppModeMenu.value = "Voxel edit mode\u25BE";
}
}
if (data.voxelEditMode !== undefined) {
if (data.voxelEditMode === "single") {
voxelEditModeValue = data.voxelEditMode;
elVoxelEditModeMenu.value = "Single voxels\u25BE";
}
if (data.voxelEditMode === "sphere") {
voxelEditModeValue = data.voxelEditMode;
elVoxelEditModeMenu.value = "Spheres\u25BE";
}
if (data.voxelEditMode === "cube") {
voxelEditModeValue = data.voxelEditMode;
elVoxelEditModeMenu.value = "Cubes\u25BE";
}
}
if (data.voxelSphereSize !== undefined) {
elVoxelSphereSize.value = data.voxelSphereSize;
}
if (data.voxelRemove !== undefined) {
elVoxelRemove.checked = data.voxelRemove == true;
}
if (data.origin) {
var origin = data.origin;
elPosY.value = origin.y;
@ -60,8 +170,20 @@ function loaded() {
}));
}
function emitUpdateEditTools() {
EventBridge.emitWebEvent(JSON.stringify({
type: "update-edit-tools",
createAppMode: createAppModeValue,
voxelEditMode: voxelEditModeValue,
voxelSphereSize: elVoxelSphereSize.value,
voxelRemove: elVoxelRemove.checked,
}));
}
}
elVoxelSphereSize.addEventListener("change", emitUpdateEditTools);
elVoxelRemove.addEventListener("change", emitUpdateEditTools);
elPosY.addEventListener("change", emitUpdate);
elMinorSpacing.addEventListener("change", emitUpdate);
elMajorSpacing.addEventListener("change", emitUpdate);
@ -154,8 +276,16 @@ function loaded() {
}));
}, false);
function closeAllEntityListMenu() {
document.getElementById("menuBackgroundOverlay").style.display = "none";
document.getElementById("edit-mode-menu").style.display = "none";
document.getElementById("voxel-edit-mode-menu").style.display = "none";
document.getElementById("voxel-help-popup").style.display = "none";
}
// Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
document.addEventListener("contextmenu", function (event) {
event.preventDefault();
}, false);
}

View file

@ -508,4 +508,4 @@
});
</script>
</body>
</html>
</html>

View file

@ -1,20 +0,0 @@
.DS_Store
node_modules
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View file

@ -1,24 +0,0 @@
# inventory
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

View file

@ -1,5 +0,0 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

View file

@ -1 +0,0 @@
.draggable-card{background-color:#272727;margin:5px 0}.draggable-card .handle{width:40px!important}.top-level-folder{background-color:#272727}.top-level-folder .v-list-group__header__prepend-icon{background-color:rgba(0,0,0,.3);width:50px;height:50px;margin:5px 5px 7px 0!important;padding:5px 18px 5px 8px}.top-level-folder .handle{width:40px!important}.top-level-folder .folder-icon{margin-right:10px}.top-level-folder .folder-button{font-size:.795rem!important}.v-list-group .column-item{max-width:100%!important;margin-top:5px;margin-bottom:5px}.v-list-group .draggable-card{background-color:rgba(0,0,0,.3);padding-right:16px;padding-left:0!important}.v-list-group .draggable-card .handle{margin-right:16px}.app-version{text-align:center;color:hsla(0,0%,100%,.6);font-weight:lighter}.handle{background-color:rgba(0,0,0,.3)}.inventoryApp::-webkit-scrollbar{width:0!important}

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View file

@ -1 +0,0 @@
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=favicon.ico><title>Inventory</title><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900"><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css><link href=css/app.a93e8b1f.css rel=preload as=style><link href=css/chunk-vendors.8540aa41.css rel=preload as=style><link href=js/app.a3555a80.js rel=preload as=script><link href=js/chunk-vendors.a0f21a27.js rel=preload as=script><link href=css/chunk-vendors.8540aa41.css rel=stylesheet><link href=css/app.a93e8b1f.css rel=stylesheet></head><body><noscript><strong>We're sorry but Inventory doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=js/chunk-vendors.a0f21a27.js></script><script src=js/app.a3555a80.js></script></body></html>

View file

@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path fill="#0c0" d="M17,14H19V17H22V19H19V22H17V19H14V17H17V14M10,2H14A2,2 0 0,1 16,4V6H20A2,2 0 0,1 22,8V13.53C20.94,12.58 19.54,12 18,12A6,6 0 0,0 12,18C12,19.09 12.29,20.12 12.8,21H4C2.89,21 2,20.1 2,19V8C2,6.89 2.89,6 4,6H8V4C8,2.89 8.89,2 10,2M14,6V4H10V6H14Z" /></svg>

Before

Width:  |  Height:  |  Size: 552 B

View file

@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M16.5,12C19,12 21,14 21,16.5C21,17.38 20.75,18.21 20.31,18.9L23.39,22L22,23.39L18.88,20.32C18.19,20.75 17.37,21 16.5,21C14,21 12,19 12,16.5C12,14 14,12 16.5,12M16.5,14A2.5,2.5 0 0,0 14,16.5A2.5,2.5 0 0,0 16.5,19A2.5,2.5 0 0,0 19,16.5A2.5,2.5 0 0,0 16.5,14M10,2H14A2,2 0 0,1 16,4V6H20A2,2 0 0,1 22,8V13.03C20.85,11.21 18.82,10 16.5,10A6.5,6.5 0 0,0 10,16.5C10,18.25 10.69,19.83 11.81,21H4C2.89,21 2,20.1 2,19V8C2,6.89 2.89,6 4,6H8V4C8,2.89 8.89,2 10,2M14,6V4H10V6H14Z" /></svg>

Before

Width:  |  Height:  |  Size: 761 B

View file

@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path fill="#0f0" d="M17,14H19V17H22V19H19V22H17V19H14V17H17V14M10,2H14A2,2 0 0,1 16,4V6H20A2,2 0 0,1 22,8V13.53C20.94,12.58 19.54,12 18,12A6,6 0 0,0 12,18C12,19.09 12.29,20.12 12.8,21H4C2.89,21 2,20.1 2,19V8C2,6.89 2.89,6 4,6H8V4C8,2.89 8.89,2 10,2M14,6V4H10V6H14Z" /></svg>

Before

Width:  |  Height:  |  Size: 552 B

View file

@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path fill="white" d="M16.5,12C19,12 21,14 21,16.5C21,17.38 20.75,18.21 20.31,18.9L23.39,22L22,23.39L18.88,20.32C18.19,20.75 17.37,21 16.5,21C14,21 12,19 12,16.5C12,14 14,12 16.5,12M16.5,14A2.5,2.5 0 0,0 14,16.5A2.5,2.5 0 0,0 16.5,19A2.5,2.5 0 0,0 19,16.5A2.5,2.5 0 0,0 16.5,14M10,2H14A2,2 0 0,1 16,4V6H20A2,2 0 0,1 22,8V13.03C20.85,11.21 18.82,10 16.5,10A6.5,6.5 0 0,0 10,16.5C10,18.25 10.69,19.83 11.81,21H4C2.89,21 2,20.1 2,19V8C2,6.89 2.89,6 4,6H8V4C8,2.89 8.89,2 10,2M14,6V4H10V6H14Z" /></svg>

Before

Width:  |  Height:  |  Size: 775 B

View file

@ -1,299 +0,0 @@
//
// inventory.js
//
// Created by kasenvr@gmail.com on 2 Apr 2020
// Copyright 2020 Vircadia and contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
/* global AvatarList Clipboard console Controller Entities location Messages MyAvatar Script ScriptDiscoveryService Settings
Tablet Vec3 Window */
(function () { // BEGIN LOCAL_SCOPE
"use strict";
var AppUi = Script.require('appUi');
var ui;
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
// VARIABLES
var inventoryDataSettingString = "inventoryApp.data";
var inventoryData;
var inventorySettingsString = "inventoryApp.settings";
var inventorySettings;
var RECEIVING_ITEM_QUEUE_LIMIT = 5;
var receivingItemQueue = [];
var NEARBY_USERS_SEARCH_RADIUS = 25;
// APP EVENT AND MESSAGING ROUTING
function onWebAppEventReceived(event) {
var eventJSON = JSON.parse(event);
if (eventJSON.app === "inventory") { // This is our web app!
// print("inventory.js received a web event: " + event);
if (eventJSON.command === "ready") {
initializeInventoryApp();
}
if (eventJSON.command === "web-to-script-inventory") {
receiveInventory(eventJSON.data);
}
if (eventJSON.command === "web-to-script-settings") {
receiveSettings(eventJSON.data);
}
if (eventJSON.command === "use-item") {
useItem(eventJSON.data);
}
if (eventJSON.command === "share-item") {
shareItem(eventJSON.data);
}
if (eventJSON.command === "web-to-script-request-nearby-users") {
sendNearbyUsers();
}
if (eventJSON.command === "web-to-script-request-receiving-item-queue") {
sendReceivingItemQueue();
}
if (eventJSON.command === "web-to-script-update-receiving-item-queue") {
updateReceivingItemQueue(eventJSON.data);
}
}
}
tablet.webEventReceived.connect(onWebAppEventReceived);
function sendToWeb(command, data) {
var dataToSend = {
"app": "inventory",
"command": command,
"data": data
};
tablet.emitScriptEvent(JSON.stringify(dataToSend));
}
var inventoryMessagesChannel = "com.vircadia.inventory";
function onMessageReceived(channel, message, sender, localOnly) {
if (channel === inventoryMessagesChannel) {
var messageJSON = JSON.parse(message);
// Window.alert("Passed 0 " + messageJSON.recipient + " vs " + MyAvatar.sessionUUID);
if (messageJSON.command === "share-item"
&& messageJSON.recipient === MyAvatar.sessionUUID) { // We are receiving an item.
// Window.alert("Passed 1 " + messageJSON.recipient + " vs " + MyAvatar.sessionUUID);
pushReceivedItemToQueue(sender, messageJSON.type, messageJSON.name, messageJSON.url);
}
}
// print("Message received:");
// print("- channel: " + channel);
// print("- message: " + message);
// print("- sender: " + sender);
// print("- localOnly: " + localOnly);
}
function sendMessage(dataToSend) {
Messages.sendMessage(inventoryMessagesChannel, JSON.stringify(dataToSend));
}
// END APP EVENT AND MESSAGING ROUTING
// SEND AND RECEIVE INVENTORY STATE
function receiveInventory(receivedInventoryData) {
inventoryData = receivedInventoryData;
saveInventory();
}
function sendInventory() {
sendToWeb("script-to-web-inventory", inventoryData);
}
// END SEND AND RECEIVE INVENTORY STATE
// SEND AND RECEIVE SETTINGS STATE
function receiveSettings(receivedSettingsData) {
inventorySettings = receivedSettingsData;
saveSettings();
}
function sendSettings() {
sendToWeb("script-to-web-settings", inventorySettings);
}
// END SEND AND RECEIVE SETTINGS STATE
function saveInventory() {
Settings.setValue(inventoryDataSettingString, inventoryData);
}
function loadInventory() {
inventoryData = Settings.getValue(inventoryDataSettingString);
}
function saveSettings() {
Settings.setValue(inventorySettingsString, inventorySettings);
}
function loadSettings() {
inventorySettings = Settings.getValue(inventorySettingsString);
}
function pushReceivedItemToQueue(senderUUID, type, name, url) {
console.info("Receiving an item:", name, "from:", senderUUID);
var getAvatarData = AvatarList.getAvatar(senderUUID);
var senderName = getAvatarData.sessionDisplayName;
var senderDistance = Vec3.distance(MyAvatar.position, getAvatarData.position);
var packageRequest = {
"senderUUID": senderUUID,
"senderName": senderName,
"senderDistance": senderDistance,
"data": {
"type": type,
"name": name,
"url": url
}
};
if (receivingItemQueue.length === RECEIVING_ITEM_QUEUE_LIMIT) {
receivingItemQueue = receivingItemQueue.slice(1, RECEIVING_ITEM_QUEUE_LIMIT);
}
receivingItemQueue.push(packageRequest);
ui.messagesWaiting(receivingItemQueue.length > 0);
}
function sendReceivingItemQueue() {
sendToWeb("script-to-web-receiving-item-queue", receivingItemQueue);
}
function updateReceivingItemQueue(data) {
receivingItemQueue = data;
ui.messagesWaiting(receivingItemQueue.length > 0);
}
function sendNearbyUsers() {
var nearbyUsers = AvatarList.getAvatarsInRange(MyAvatar.position, NEARBY_USERS_SEARCH_RADIUS);
var nearbyUsersToSend = [];
nearbyUsers.forEach(function(user) {
var objectToWrite;
var aviDetails = AvatarList.getAvatar(user);
var aviName = aviDetails.displayName;
var aviDistance = Vec3.distance(MyAvatar.position, aviDetails.position);
// Window.alert("aviName" + aviName + "user" + user + "MyAvatar.sessionUUID" + MyAvatar.sessionUUID);
if (user !== MyAvatar.sessionUUID
|| Controller.getValue(Controller.Hardware.Keyboard.Shift)) { // Don't add ourselves to the list!
objectToWrite = { "name": aviName, "distance": aviDistance, "uuid": user };
nearbyUsersToSend.push(objectToWrite);
}
});
sendToWeb("script-to-web-nearby-users", nearbyUsersToSend);
}
function useItem(item) {
//TODO: Add animation support for avatars...?
// Convert the item.type before checking it...
item.type = item.type.toUpperCase();
// Depending on the type, we decide how to load this item.
if (item.type === "SCRIPT") {
ScriptDiscoveryService.loadScript(item.url, true, false, false, true, false);
}
if (item.type === "MODEL") {
Entities.addEntity({
type: "Model",
position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1.5 })),
rotation: MyAvatar.orientation,
modelURL: item.url,
collisionless: true
});
}
if (item.type === "AVATAR") {
MyAvatar.useFullAvatarURL(item.url);
}
if (item.type === "PLACE") {
location.handleLookupString(item.url, true);
}
if (item.type === "JSON") {
var jsonToLoad = item.url;
if (jsonToLoad) {
if (Clipboard.importEntities(jsonToLoad)) {
Clipboard.pasteEntities(
Vec3.sum(
MyAvatar.position,
Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1.5 })
)
);
}
}
}
if (item.type === "UNKNOWN") {
// We don't know how to handle this yet.
Window.alert("Unknown item type, unable to use.");
}
}
function shareItem(data) {
data.command = "share-item";
sendMessage(data);
}
function initializeInventoryApp() {
sendSettings();
sendInventory();
sendReceivingItemQueue();
}
function onOpened() {
}
function onClosed() {
}
function startup() {
loadInventory();
loadSettings();
Messages.messageReceived.connect(onMessageReceived);
Messages.subscribe(inventoryMessagesChannel);
ui = new AppUi({
buttonName: "INVENTORY",
home: Script.resolvePath("index.html"),
graphicsDirectory: Script.resolvePath("./"), // Where your button icons are located
onOpened: onOpened,
onClosed: onClosed
});
}
startup();
Script.scriptEnding.connect(function () {
Messages.messageReceived.disconnect(onMessageReceived);
Messages.unsubscribe(inventoryMessagesChannel);
});
}()); // END LOCAL_SCOPE

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -1,52 +0,0 @@
{
"name": "Inventory",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@mdi/font": "^4.9.95",
"core-js": "^3.6.5",
"vue": "^2.6.11",
"vuedraggable": "^2.24.0",
"vuetify": "^2.3.4",
"vuex": "^3.5.1"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.3.1",
"@vue/cli-plugin-eslint": "^4.3.1",
"@vue/cli-service": "^4.3.1",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"sass": "^1.26.10",
"sass-loader": "^8.0.0",
"vue-cli-plugin-vuetify": "^2.0.7",
"vue-template-compiler": "^2.6.11",
"vuetify-loader": "^1.6.0"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead",
"ChromeAndroid > 55",
"Chrome > 55"
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View file

@ -1,30 +0,0 @@
<!--
//
// index.html
//
// Created by kasenvr@gmail.com on 7 Apr 2020
// Copyright 2020 Vircadia and contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View file

@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path fill="#0c0" d="M17,14H19V17H22V19H19V22H17V19H14V17H17V14M10,2H14A2,2 0 0,1 16,4V6H20A2,2 0 0,1 22,8V13.53C20.94,12.58 19.54,12 18,12A6,6 0 0,0 12,18C12,19.09 12.29,20.12 12.8,21H4C2.89,21 2,20.1 2,19V8C2,6.89 2.89,6 4,6H8V4C8,2.89 8.89,2 10,2M14,6V4H10V6H14Z" /></svg>

Before

Width:  |  Height:  |  Size: 552 B

View file

@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M16.5,12C19,12 21,14 21,16.5C21,17.38 20.75,18.21 20.31,18.9L23.39,22L22,23.39L18.88,20.32C18.19,20.75 17.37,21 16.5,21C14,21 12,19 12,16.5C12,14 14,12 16.5,12M16.5,14A2.5,2.5 0 0,0 14,16.5A2.5,2.5 0 0,0 16.5,19A2.5,2.5 0 0,0 19,16.5A2.5,2.5 0 0,0 16.5,14M10,2H14A2,2 0 0,1 16,4V6H20A2,2 0 0,1 22,8V13.03C20.85,11.21 18.82,10 16.5,10A6.5,6.5 0 0,0 10,16.5C10,18.25 10.69,19.83 11.81,21H4C2.89,21 2,20.1 2,19V8C2,6.89 2.89,6 4,6H8V4C8,2.89 8.89,2 10,2M14,6V4H10V6H14Z" /></svg>

Before

Width:  |  Height:  |  Size: 761 B

View file

@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path fill="#0f0" d="M17,14H19V17H22V19H19V22H17V19H14V17H17V14M10,2H14A2,2 0 0,1 16,4V6H20A2,2 0 0,1 22,8V13.53C20.94,12.58 19.54,12 18,12A6,6 0 0,0 12,18C12,19.09 12.29,20.12 12.8,21H4C2.89,21 2,20.1 2,19V8C2,6.89 2.89,6 4,6H8V4C8,2.89 8.89,2 10,2M14,6V4H10V6H14Z" /></svg>

Before

Width:  |  Height:  |  Size: 552 B

View file

@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path fill="white" d="M16.5,12C19,12 21,14 21,16.5C21,17.38 20.75,18.21 20.31,18.9L23.39,22L22,23.39L18.88,20.32C18.19,20.75 17.37,21 16.5,21C14,21 12,19 12,16.5C12,14 14,12 16.5,12M16.5,14A2.5,2.5 0 0,0 14,16.5A2.5,2.5 0 0,0 16.5,19A2.5,2.5 0 0,0 19,16.5A2.5,2.5 0 0,0 16.5,14M10,2H14A2,2 0 0,1 16,4V6H20A2,2 0 0,1 22,8V13.03C20.85,11.21 18.82,10 16.5,10A6.5,6.5 0 0,0 10,16.5C10,18.25 10.69,19.83 11.81,21H4C2.89,21 2,20.1 2,19V8C2,6.89 2.89,6 4,6H8V4C8,2.89 8.89,2 10,2M14,6V4H10V6H14Z" /></svg>

Before

Width:  |  Height:  |  Size: 775 B

View file

@ -1,299 +0,0 @@
//
// inventory.js
//
// Created by kasenvr@gmail.com on 2 Apr 2020
// Copyright 2020 Vircadia and contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
/* global AvatarList Clipboard console Controller Entities location Messages MyAvatar Script ScriptDiscoveryService Settings
Tablet Vec3 Window */
(function () { // BEGIN LOCAL_SCOPE
"use strict";
var AppUi = Script.require('appUi');
var ui;
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
// VARIABLES
var inventoryDataSettingString = "inventoryApp.data";
var inventoryData;
var inventorySettingsString = "inventoryApp.settings";
var inventorySettings;
var RECEIVING_ITEM_QUEUE_LIMIT = 5;
var receivingItemQueue = [];
var NEARBY_USERS_SEARCH_RADIUS = 25;
// APP EVENT AND MESSAGING ROUTING
function onWebAppEventReceived(event) {
var eventJSON = JSON.parse(event);
if (eventJSON.app === "inventory") { // This is our web app!
// print("inventory.js received a web event: " + event);
if (eventJSON.command === "ready") {
initializeInventoryApp();
}
if (eventJSON.command === "web-to-script-inventory") {
receiveInventory(eventJSON.data);
}
if (eventJSON.command === "web-to-script-settings") {
receiveSettings(eventJSON.data);
}
if (eventJSON.command === "use-item") {
useItem(eventJSON.data);
}
if (eventJSON.command === "share-item") {
shareItem(eventJSON.data);
}
if (eventJSON.command === "web-to-script-request-nearby-users") {
sendNearbyUsers();
}
if (eventJSON.command === "web-to-script-request-receiving-item-queue") {
sendReceivingItemQueue();
}
if (eventJSON.command === "web-to-script-update-receiving-item-queue") {
updateReceivingItemQueue(eventJSON.data);
}
}
}
tablet.webEventReceived.connect(onWebAppEventReceived);
function sendToWeb(command, data) {
var dataToSend = {
"app": "inventory",
"command": command,
"data": data
};
tablet.emitScriptEvent(JSON.stringify(dataToSend));
}
var inventoryMessagesChannel = "com.vircadia.inventory";
function onMessageReceived(channel, message, sender, localOnly) {
if (channel === inventoryMessagesChannel) {
var messageJSON = JSON.parse(message);
// Window.alert("Passed 0 " + messageJSON.recipient + " vs " + MyAvatar.sessionUUID);
if (messageJSON.command === "share-item"
&& messageJSON.recipient === MyAvatar.sessionUUID) { // We are receiving an item.
// Window.alert("Passed 1 " + messageJSON.recipient + " vs " + MyAvatar.sessionUUID);
pushReceivedItemToQueue(sender, messageJSON.type, messageJSON.name, messageJSON.url);
}
}
// print("Message received:");
// print("- channel: " + channel);
// print("- message: " + message);
// print("- sender: " + sender);
// print("- localOnly: " + localOnly);
}
function sendMessage(dataToSend) {
Messages.sendMessage(inventoryMessagesChannel, JSON.stringify(dataToSend));
}
// END APP EVENT AND MESSAGING ROUTING
// SEND AND RECEIVE INVENTORY STATE
function receiveInventory(receivedInventoryData) {
inventoryData = receivedInventoryData;
saveInventory();
}
function sendInventory() {
sendToWeb("script-to-web-inventory", inventoryData);
}
// END SEND AND RECEIVE INVENTORY STATE
// SEND AND RECEIVE SETTINGS STATE
function receiveSettings(receivedSettingsData) {
inventorySettings = receivedSettingsData;
saveSettings();
}
function sendSettings() {
sendToWeb("script-to-web-settings", inventorySettings);
}
// END SEND AND RECEIVE SETTINGS STATE
function saveInventory() {
Settings.setValue(inventoryDataSettingString, inventoryData);
}
function loadInventory() {
inventoryData = Settings.getValue(inventoryDataSettingString);
}
function saveSettings() {
Settings.setValue(inventorySettingsString, inventorySettings);
}
function loadSettings() {
inventorySettings = Settings.getValue(inventorySettingsString);
}
function pushReceivedItemToQueue(senderUUID, type, name, url) {
console.info("Receiving an item:", name, "from:", senderUUID);
var getAvatarData = AvatarList.getAvatar(senderUUID);
var senderName = getAvatarData.sessionDisplayName;
var senderDistance = Vec3.distance(MyAvatar.position, getAvatarData.position);
var packageRequest = {
"senderUUID": senderUUID,
"senderName": senderName,
"senderDistance": senderDistance,
"data": {
"type": type,
"name": name,
"url": url
}
};
if (receivingItemQueue.length === RECEIVING_ITEM_QUEUE_LIMIT) {
receivingItemQueue = receivingItemQueue.slice(1, RECEIVING_ITEM_QUEUE_LIMIT);
}
receivingItemQueue.push(packageRequest);
ui.messagesWaiting(receivingItemQueue.length > 0);
}
function sendReceivingItemQueue() {
sendToWeb("script-to-web-receiving-item-queue", receivingItemQueue);
}
function updateReceivingItemQueue(data) {
receivingItemQueue = data;
ui.messagesWaiting(receivingItemQueue.length > 0);
}
function sendNearbyUsers() {
var nearbyUsers = AvatarList.getAvatarsInRange(MyAvatar.position, NEARBY_USERS_SEARCH_RADIUS);
var nearbyUsersToSend = [];
nearbyUsers.forEach(function(user) {
var objectToWrite;
var aviDetails = AvatarList.getAvatar(user);
var aviName = aviDetails.displayName;
var aviDistance = Vec3.distance(MyAvatar.position, aviDetails.position);
// Window.alert("aviName" + aviName + "user" + user + "MyAvatar.sessionUUID" + MyAvatar.sessionUUID);
if (user !== MyAvatar.sessionUUID
|| Controller.getValue(Controller.Hardware.Keyboard.Shift)) { // Don't add ourselves to the list!
objectToWrite = { "name": aviName, "distance": aviDistance, "uuid": user };
nearbyUsersToSend.push(objectToWrite);
}
});
sendToWeb("script-to-web-nearby-users", nearbyUsersToSend);
}
function useItem(item) {
//TODO: Add animation support for avatars...?
// Convert the item.type before checking it...
item.type = item.type.toUpperCase();
// Depending on the type, we decide how to load this item.
if (item.type === "SCRIPT") {
ScriptDiscoveryService.loadScript(item.url, true, false, false, true, false);
}
if (item.type === "MODEL") {
Entities.addEntity({
type: "Model",
position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1.5 })),
rotation: MyAvatar.orientation,
modelURL: item.url,
collisionless: true
});
}
if (item.type === "AVATAR") {
MyAvatar.useFullAvatarURL(item.url);
}
if (item.type === "PLACE") {
location.handleLookupString(item.url, true);
}
if (item.type === "JSON") {
var jsonToLoad = item.url;
if (jsonToLoad) {
if (Clipboard.importEntities(jsonToLoad)) {
Clipboard.pasteEntities(
Vec3.sum(
MyAvatar.position,
Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1.5 })
)
);
}
}
}
if (item.type === "UNKNOWN") {
// We don't know how to handle this yet.
Window.alert("Unknown item type, unable to use.");
}
}
function shareItem(data) {
data.command = "share-item";
sendMessage(data);
}
function initializeInventoryApp() {
sendSettings();
sendInventory();
sendReceivingItemQueue();
}
function onOpened() {
}
function onClosed() {
}
function startup() {
loadInventory();
loadSettings();
Messages.messageReceived.connect(onMessageReceived);
Messages.subscribe(inventoryMessagesChannel);
ui = new AppUi({
buttonName: "INVENTORY",
home: Script.resolvePath("index.html"),
graphicsDirectory: Script.resolvePath("./"), // Where your button icons are located
onOpened: onOpened,
onClosed: onClosed
});
}
startup();
Script.scriptEnding.connect(function () {
Messages.messageReceived.disconnect(onMessageReceived);
Messages.unsubscribe(inventoryMessagesChannel);
});
}()); // END LOCAL_SCOPE

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View file

@ -1 +0,0 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 87.5 100"><defs><style>.cls-1{fill:#1697f6;}.cls-2{fill:#7bc6ff;}.cls-3{fill:#1867c0;}.cls-4{fill:#aeddff;}</style></defs><title>Artboard 46</title><polyline class="cls-1" points="43.75 0 23.31 0 43.75 48.32"/><polygon class="cls-2" points="43.75 62.5 43.75 100 0 14.58 22.92 14.58 43.75 62.5"/><polyline class="cls-3" points="43.75 0 64.19 0 43.75 48.32"/><polygon class="cls-4" points="64.58 14.58 87.5 14.58 43.75 100 43.75 62.5 64.58 14.58"/></svg>

Before

Width:  |  Height:  |  Size: 539 B

View file

@ -1,78 +0,0 @@
/*
styles.css
Created by Kalila L. on 7 Apr 2020
Copyright 2020 Vircadia and contributors.
Distributed under the Apache License, Version 2.0.
See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
*/
/* Top Level */
.draggable-card {
background-color: rgba(39, 39, 39, 1.0);
margin: 5px 0px;
}
.draggable-card .handle {
width: 40px !important;
}
.top-level-folder {
background-color: rgba(39, 39, 39, 1.0);
}
.top-level-folder .v-list-group__header__prepend-icon {
background-color: rgba(0, 0, 0, 0.3);
width: 50px;
height: 50px;
margin: 5px 5px 7px 0px !important;
padding: 5px 18px 5px 8px;
}
.top-level-folder .handle {
width: 40px !important;
}
.top-level-folder .folder-icon {
margin-right: 10px;
}
.top-level-folder .folder-button {
font-size: 0.795rem !important;
}
/* Second Level */
.v-list-group .column-item {
max-width: 100% !important;
margin-top: 5px;
margin-bottom: 5px;
}
.v-list-group .draggable-card {
background-color: rgba(0, 0, 0, 0.3);
padding-right: 16px;
padding-left: 0px !important;
}
.v-list-group .draggable-card .handle {
margin-right: 16px;
}
/* Menu Slideout */
.app-version {
text-align: center;
color: rgba(255, 255, 255, 0.6);
font-weight: lighter;
}
/* Universal */
.handle {
background-color: rgba(0, 0, 0, 0.3);
}
.inventoryApp::-webkit-scrollbar { width: 0 !important }

View file

@ -1,391 +0,0 @@
<!--
// ItemIterator.vue
//
// Created by Kalila L. on 13 April 2020.
// Copyright 2020 Vircadia and contributors..
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-->
<template>
<draggable :group="options" :list="itemsForIterator" handle=".handle">
<v-item-group
v-for="item in itemsForIterator"
v-bind:key="item.uuid"
>
<v-list-item
one-line
v-if="!item.items"
class="mx-auto draggable-card"
max-width="344"
outlined
>
<div class="handle pa-2">
<!-- <v-icon color="orange darken-2">mdi-blur-linear</v-icon> -->
<!-- <v-icon color="orange darken-2">mdi-package-variant</v-icon> -->
<v-icon color="orange darken-2">mdi-square-medium-outline</v-icon>
</div>
<v-list-item-content
class="pb-1 pt-2 pl-4"
>
<div v-show="settings.displayDensity.size > 0" class="overline" style="font-size: 0.825rem !important;">{{item.type}}</div>
<v-list-item-title class="subtitle-1 mb-1">{{item.name}}</v-list-item-title>
<v-list-item-subtitle v-show="settings.displayDensity.size == 2">{{item.url}}</v-list-item-subtitle>
</v-list-item-content>
<v-menu bottom left>
<template v-slot:activator="{ on }">
<!-- settings.displayDensity.size >= 1 -->
<v-btn
:style="{backgroundColor: (getIconColor(item.type)) }"
v-show="settings.displayDensity.size >= 1"
medium
fab
dark
v-on="on"
>
<v-icon>{{getIcon(item.type)}}</v-icon>
</v-btn>
<!-- settings.displayDensity.size < 1 -->
<v-btn
:style="{backgroundColor: (getIconColor(item.type)) }"
v-show="settings.displayDensity.size < 1"
small
fab
dark
v-on="on"
>
<v-icon>{{getIcon(item.type)}}</v-icon>
</v-btn>
</template>
<v-list color="grey darken-3">
<v-list-item
@click="sendEvent('use-item', { 'type': item.type, 'url': item.url })"
>
<v-list-item-title>Use</v-list-item-title>
<v-list-item-action>
<v-icon>mdi-play</v-icon>
</v-list-item-action>
</v-list-item>
<v-list-item
@click="
editDialogStore.show = true;
editDialogStore.uuid = item.uuid;
editDialogStore.data.type = item.type.toUpperCase();
editDialogStore.data.folder = null;
editDialogStore.data.name = item.name;
editDialogStore.data.url = item.url;
"
>
<v-list-item-title>Edit</v-list-item-title>
<v-list-item-action>
<v-icon>mdi-pencil</v-icon>
</v-list-item-action>
</v-list-item>
<v-list-item
@click="
shareDialogStore.show = true;
shareDialogStore.data.url = item.url;
shareDialogStore.data.uuid = item.uuid;
"
>
<v-list-item-title>Share</v-list-item-title>
<v-list-item-action>
<v-icon>mdi-share</v-icon>
</v-list-item-action>
</v-list-item>
<v-list-item
@click="
removeDialogStore.show = true;
removeDialogStore.uuid = item.uuid;
"
color="red darken-1"
>
<v-list-item-title>Remove</v-list-item-title>
<v-list-item-action>
<v-icon>mdi-minus</v-icon>
</v-list-item-action>
</v-list-item>
</v-list>
</v-menu>
</v-list-item>
<!-- The Folder List Item -->
<v-list-group
v-else
class="top-level-folder"
>
<!-- prepend-icon="mdi-blur-linear" put this in the list group, no idea how to make it a handle yet though... -->
<template v-slot:activator>
<v-list-item
one-line
class="mx-auto"
max-width="344"
outlined
>
<v-icon class="folder-icon" color="teal">mdi-folder-settings</v-icon>
{{item.name}}
</v-list-item>
</template>
<div class="text-center my-2">
<v-btn medium tile color="purple" class="mx-1 folder-button"
@click="
editFolderDialogStore.data.uuid = item.uuid;
editFolderDialogStore.data.name = item.name;
editFolderDialogStore.show = true;
"
>
<v-icon>mdi-pencil</v-icon>
</v-btn>
<v-btn medium tile color="red" class="mx-1 folder-button"
@click="
removeFolderDialogStore.show = true;
removeFolderDialogStore.uuid = item.uuid;
"
>
<v-icon>mdi-minus</v-icon>
</v-btn>
<v-menu bottom left>
<template v-slot:activator="{ on }">
<v-btn
medium tile
color="blue"
class="mx-1 folder-button"
v-on="on"
>
<v-icon>mdi-sort</v-icon>
</v-btn>
</template>
<v-list color="grey darken-3">
<v-list-item
@click="sortFolder(item.uuid, 'az');"
>
<v-list-item-title>A-Z</v-list-item-title>
<v-list-item-action>
<v-icon large>mdi-sort-alphabetical-ascending</v-icon>
</v-list-item-action>
</v-list-item>
<v-list-item
@click="sortFolder(item.uuid, 'za');"
>
<v-list-item-title>Z-A</v-list-item-title>
<v-list-item-action>
<v-icon large>mdi-sort-alphabetical-descending</v-icon>
</v-list-item-action>
</v-list-item>
</v-list>
</v-menu>
</div>
<v-container fluid>
<itemiterator v-bind:key="item.uuid" :itemsForIterator="item.items"></itemiterator>
</v-container>
</v-list-group>
</v-item-group>
</draggable>
</template>
<script>
import { EventBus } from '../plugins/event-bus.js';
import draggable from 'vuedraggable';
export default {
name: 'itemiterator',
components: {
draggable
},
props: ['itemsForIterator'],
data: () => ({
settings: {}
}),
created: function () {
this.settings = this.$store.state.settings;
},
computed: {
options : function (){
return {
name: 'column-item',
pull: true,
put: true
}
},
settingsChanged() {
return this.$store.state.settings;
},
addDialogStore: {
get() {
return this.$store.state.addDialog;
},
set(value) {
this.$store.commit('mutate', {
property: 'addDialog',
with: value
});
}
},
editDialogStore: {
get() {
return this.$store.state.editDialog;
},
set(value) {
this.$store.commit('mutate', {
property: 'editDialog',
with: value
});
}
},
editFolderDialogStore: {
get() {
return this.$store.state.editFolderDialog;
},
set(value) {
this.$store.commit('mutate', {
property: 'editFolderDialog',
with: value
});
}
},
shareDialogStore: {
get() {
return this.$store.state.shareDialog;
},
set(value) {
this.$store.commit('mutate', {
property: 'shareDialog',
with: value
});
}
},
removeFolderDialogStore: {
get() {
return this.$store.state.removeFolderDialog;
},
set(value) {
this.$store.commit('mutate', {
property: 'removeFolderDialog',
with: value
});
}
},
removeDialogStore: {
get() {
return this.$store.state.removeDialog;
},
set(value) {
this.$store.commit('mutate', {
property: 'removeDialog',
with: value
});
}
}
},
watch: {
settingsChanged (newVal, oldVal) {
console.info ("Settings previous value:", oldVal);
if (newVal) {
this.settings = newVal;
}
}
},
methods: {
sendEvent: function(command, data) {
EventBus.$emit(command, data);
},
getIcon: function(itemType) {
itemType = itemType.toUpperCase();
var returnedItemIcon;
if (this.$store.state.iconType[itemType]) {
returnedItemIcon = this.$store.state.iconType[itemType].icon;
} else {
returnedItemIcon = this.$store.state.iconType.UNKNOWN.icon;
}
return returnedItemIcon;
},
getIconColor: function(itemType) {
itemType = itemType.toUpperCase();
var returnedItemIconColor;
if (this.$store.state.iconType[itemType]) {
returnedItemIconColor = this.$store.state.iconType[itemType].color;
} else {
returnedItemIconColor = this.$store.state.iconType.UNKNOWN.color;
}
return returnedItemIconColor;
},
sortFolder: function(uuid, sort) {
var findFolder = this.searchForItem(uuid);
if (findFolder) {
if (sort === "az") {
findFolder.returnedItem.items.sort(function(a, b) {
var nameA = a.name.toUpperCase(); // ignore upper and lowercase
var nameB = b.name.toUpperCase(); // ignore upper and lowercase
if (nameA < nameB) {
return -1;
}
if (nameA > nameB) {
return 1;
}
// names must be equal
return 0;
});
}
if (sort === "za") {
findFolder.returnedItem.items.sort(function(a, b) {
var nameA = a.name.toUpperCase(); // ignore upper and lowercase
var nameB = b.name.toUpperCase(); // ignore upper and lowercase
if (nameA > nameB) {
return -1;
}
if (nameA < nameB) {
return 1;
}
// names must be equal
return 0;
});
}
}
},
searchForItem: function(uuid) {
var foundItem = this.recursiveSingularSearch(uuid, this.itemsForIterator);
if (foundItem) {
return {
"returnedItem": foundItem.returnedItem,
"iteration": foundItem.iteration,
"parentArray": foundItem.parentArray,
"itemUUID": uuid,
}
}
},
recursiveSingularSearch: function(uuid, indexToSearch) {
for (var i = 0; i < indexToSearch.length; i++) {
if (indexToSearch[i].uuid == uuid) {
var foundItem = {
"returnedItem": indexToSearch[i],
"iteration": i,
"parentArray": indexToSearch,
}
return foundItem;
} else if (Object.prototype.hasOwnProperty.call(indexToSearch[i], "items") && indexToSearch[i].items.length > 0) {
return this.recursiveSingularSearch(uuid, indexToSearch[i].items);
}
}
},
}
};
</script>

View file

@ -1,272 +0,0 @@
<!--
NotUsing.vue
Created by Kalila L. on 7 Apr 2020
Copyright 2020 Vircadia and contributors.
Distributed under the Apache License, Version 2.0.
See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-->
<template v-if="!disabledProp">
<v-data-iterator
:items="items"
hide-default-footer
>
<template>
<v-col
cols="12"
sm="6"
md="4"
lg="3"
class="py-1 column-item"
>
<draggable :group="options" :list="items" handle=".handle">
<v-item-group
v-for="item in items"
v-bind:key="item.uuid"
>
<v-list-item
one-line
v-if="!item.hasChildren"
class="mx-auto draggable-card"
max-width="344"
outlined
>
<div class="handle pa-2">
<v-icon color="orange darken-2">mdi-blur-linear</v-icon>
</div>
<v-list-item-content
class="pb-1 pt-2 pl-4"
>
<div v-show="settings.displayDensity.size > 0" class="overline" style="font-size: 0.825rem !important;">{{item.type}}</div>
<v-list-item-title class="subtitle-1 mb-1">{{item.name}}</v-list-item-title>
<v-list-item-subtitle v-show="settings.displayDensity.size == 2">{{item.url}}</v-list-item-subtitle>
</v-list-item-content>
<v-menu bottom left>
<template v-slot:activator="{ on }">
<!-- settings.displayDensity.size >= 1 -->
<v-btn
:style="{backgroundColor: (getIconColor(item.type)) }"
v-show="settings.displayDensity.size >= 1"
medium
fab
dark
v-on="on"
>
<v-icon>{{getIcon(item.type)}}</v-icon>
</v-btn>
<!-- settings.displayDensity.size < 1 -->
<v-btn
:style="{backgroundColor: (getIconColor(item.type)) }"
v-show="settings.displayDensity.size < 1"
small
fab
dark
v-on="on"
>
<v-icon>{{getIcon(item.type)}}</v-icon>
</v-btn>
</template>
<v-list color="grey darken-3">
<v-list-item
@click="useItem(item.type, item.url)"
>
<v-list-item-title>Use</v-list-item-title>
<v-list-item-action>
<v-icon>mdi-play</v-icon>
</v-list-item-action>
</v-list-item>
<v-list-item
@click="
editDialog.show = true;
editDialog.uuid = item.uuid;
editDialog.data.type = item.type.toUpperCase();
editDialog.data.folder = null;
editDialog.data.name = item.name;
editDialog.data.url = item.url;
getFolderList('edit');
"
>
<v-list-item-title>Edit</v-list-item-title>
<v-list-item-action>
<v-icon>mdi-pencil</v-icon>
</v-list-item-action>
</v-list-item>
<v-list-item
@click="shareDialog.show = true; shareDialog.data.url = item.url; shareDialog.data.uuid = item.uuid; sendAppMessage('web-to-script-request-nearby-users', '')"
>
<v-list-item-title>Share</v-list-item-title>
<v-list-item-action>
<v-icon>mdi-share</v-icon>
</v-list-item-action>
</v-list-item>
<v-list-item
@click="removeDialog.show = true; removeDialog.uuid = item.uuid;"
color="red darken-1"
>
<v-list-item-title>Remove</v-list-item-title>
<v-list-item-action>
<v-icon>mdi-minus</v-icon>
</v-list-item-action>
</v-list-item>
</v-list>
</v-menu>
</v-list-item>
<!-- The Folder List Item -->
<v-list-group
v-if="item.hasChildren"
class="top-level-folder"
>
<!-- prepend-icon="mdi-blur-linear" put this in the list group, no idea how to make it a handle yet though... -->
<template v-slot:activator>
<v-list-item
one-line
class="mx-auto"
max-width="344"
outlined
>
<v-icon class="folder-icon" color="teal">mdi-folder-settings</v-icon>
{{item.name}}
</v-list-item>
</template>
<div class="text-center my-2">
<v-btn medium tile color="purple" class="mx-1 folder-button"
@click="
editFolderDialog.show = true;
editFolderDialog.uuid = item.uuid;
editFolderDialog.data.name = item.name;
"
>
<v-icon>mdi-pencil</v-icon>
</v-btn>
<v-btn medium tile color="red" class="mx-1 folder-button"
@click="removeFolderDialog.show = true; removeFolderDialog.uuid = item.uuid;"
>
<v-icon>mdi-minus</v-icon>
</v-btn>
<v-btn medium tile color="blue" class="mx-1 folder-button"
@click="sortFolder(item.uuid);"
>
<v-icon>mdi-ab-testing</v-icon>
</v-btn>
</div>
<v-col
cols="12"
sm="6"
md="4"
lg="3"
class="py-1 column-item"
>
<draggable
:list="item.items"
:group="options"
>
<v-item-group
v-for="item in item.items"
v-bind:key="item.uuid"
>
<v-list-item
one-line
class="mx-auto draggable-card"
outlined
>
<div class="handle pa-2">
<v-icon color="orange darken-2">mdi-blur-linear</v-icon>
</div>
<v-list-item-content class="pb-1 pt-2">
<div v-show="settings.displayDensity.size > 0" class="overline" style="font-size: 0.825rem !important;">{{item.type}}</div>
<v-list-item-title class="subtitle-1 mb-1">{{item.name}}</v-list-item-title>
<v-list-item-subtitle v-show="settings.displayDensity.size == 2">{{item.url}}</v-list-item-subtitle>
</v-list-item-content>
<v-menu bottom left>
<template v-slot:activator="{ on }">
<!-- settings.displayDensity.size >= 1 -->
<v-btn
:style="{backgroundColor: (getIconColor(item.type)) }"
v-show="settings.displayDensity.size >= 1"
medium
fab
dark
v-on="on"
>
<v-icon>{{getIcon(item.type)}}</v-icon>
</v-btn>
<!-- settings.displayDensity.size < 1 -->
<v-btn
:style="{backgroundColor: (getIconColor(item.type)) }"
v-show="settings.displayDensity.size < 1"
small
fab
dark
v-on="on"
>
<v-icon>{{getIcon(item.type)}}</v-icon>
</v-btn>
</template>
<v-list color="grey darken-3">
<v-list-item
@click="useItem(item.type, item.url)"
>
<v-list-item-title>Use</v-list-item-title>
<v-list-item-action>
<v-icon>mdi-play</v-icon>
</v-list-item-action>
</v-list-item>
<v-list-item
@click="
editDialog.show = true;
editDialog.uuid = item.uuid;
editDialog.data.type = item.type.toUpperCase();
editDialog.data.folder = null;
editDialog.data.name = item.name;
editDialog.data.url = item.url;
getFolderList('edit');
"
>
<v-list-item-title>Edit</v-list-item-title>
<v-list-item-action>
<v-icon>mdi-pencil</v-icon>
</v-list-item-action>
</v-list-item>
<v-list-item
@click="shareDialog.show = true; shareDialog.data.url = item.url; shareDialog.data.uuid = item.uuid; sendAppMessage('web-to-script-request-nearby-users', '')"
>
<v-list-item-title>Share</v-list-item-title>
<v-list-item-action>
<v-icon>mdi-share</v-icon>
</v-list-item-action>
</v-list-item>
<v-list-item
@click="removeDialog.show = true; removeDialog.uuid = item.uuid;"
color="red darken-1"
>
<v-list-item-title>Remove</v-list-item-title>
<v-list-item-action>
<v-icon>mdi-minus</v-icon>
</v-list-item-action>
</v-list-item>
</v-list>
</v-menu>
</v-list-item>
</v-item-group>
</draggable>
</v-col>
</v-list-group>
</v-item-group>
</draggable>
</v-col>
</template>
</v-data-iterator>
</template>

View file

@ -1,22 +0,0 @@
/*
main.js
Created by Kalila L. on 7 Apr 2020
Copyright 2020 Vircadia and contributors.
Distributed under the Apache License, Version 2.0.
See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
*/
import Vue from 'vue'
import App from './App.vue'
import vuetify from './plugins/vuetify';
import { store } from './plugins/store';
Vue.config.productionTip = false;
window.vm = new Vue({
vuetify,
store,
render: h => h(App)
}).$mount('#app');

View file

@ -1,12 +0,0 @@
/*
event-bus.js
Created by Kalila L. on 21 May 2020.
Copyright 2020 Vircadia and contributors.
Distributed under the Apache License, Version 2.0.
See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
*/
import Vue from 'vue';
export const EventBus = new Vue();

View file

@ -1,321 +0,0 @@
/*
store.js
Created by Kalila L. on 16 Apr 2020.
Copyright 2020 Vircadia and contributors.
Distributed under the Apache License, Version 2.0.
See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
*/
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const store = new Vuex.Store({
devtools: true,
state: {
items: [
// This is test data and is primarily used for in browser development.
{
"type": "script",
"name": "VRGrabScale",
"url": "https://gooawefaweawfgle.com/vr.js",
"folder": "No Folder",
"uuid": "54254354353"
},
{
"name": "Test Folder",
"folder": "No Folder",
"items": [
{
"name": "inception1",
"folder": "Test Folder",
"items": [
{
"name": "inception2",
"folder": "Test Folder",
"items": [
{
"type": "script",
"name": "itemincepted",
"url": "https://googfdafsgaergale.com/vr.js",
"folder": "FolderWithinAFolder",
"uuid": "hkjkjhkjk",
},
],
"uuid": "adsfa32"
},
],
"uuid": "s4g4sg"
},
],
"uuid": "sdfsdf",
},
{
"type": "script",
"name": "VRGrabScale",
"url": "https://googfdafsgaergale.com/vr.js",
"folder": "No Folder",
"uuid": "54hgfhgf254354353",
},
{
"type": "script",
"name": "TEST",
"url": "https://gooadfdagle.com/vr.js",
"folder": "No Folder",
"uuid": "542rfwat4t5fsddf4354353",
},
{
"type": "json",
"name": "TESTJSON",
"url": "https://gooadfdagle.com/vr.json",
"folder": "No Folder",
"uuid": "542rfwat4t54354353",
},
{
"type": "script",
"name": "TESTLONGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG",
"url": "https://googfdaffle.com/vrLONGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG.js",
"folder": "No Folder",
"uuid": "5425ggsrg45354353",
},
{
"type": "whatttype",
"name": "BrokenIcon",
"url": "https://googfdaffle.com/vrLONGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG.js",
"folder": "No Folder",
"uuid": "5425ggsrg4fdaffdff535asdasd4353",
},
{
"type": "avatar",
"name": "AVI",
"url": "https://googlfadfe.com/vr.fst",
"folder": "No Folder",
"uuid": "542gregg45s3g4354353",
},
{
"type": "avatar",
"name": "AVI",
"url": "https://googlefdaf.com/vr.fst",
"folder": "No Folder",
"uuid": "5420798-087-54354353",
},
{
"type": "model",
"name": "3D MODEL",
"url": "https://googlee.com/vr.fbx",
"folder": "No Folder",
"uuid": "54254354980-7667jt353",
},
{
"type": "place",
"name": "PLACE DOMAIN",
"url": "https://googleee.com/vr.fbx",
"folder": "No Folder",
"uuid": "542543sg45s4gg54353",
},
],
settings: {
"displayDensity": {
"size": 1,
"labels": [
"List",
"Compact",
"Large",
],
},
},
iconType: {
"SCRIPT": {
"icon": "mdi-code-tags",
"color": "red",
},
"MODEL": {
"icon": "mdi-video-3d",
"color": "green",
},
"AVATAR": {
"icon": "mdi-account-convert",
"color": "purple",
},
"PLACE": {
"icon": "mdi-earth",
"color": "#0097A7", // cyan darken-2
},
"JSON": {
"icon": "mdi-inbox-multiple",
"color": "#37474F", // blue-grey darken-3
},
"UNKNOWN": {
"icon": "mdi-help",
"color": "grey",
}
},
supportedItemTypes: [
"SCRIPT",
"MODEL",
"AVATAR",
"PLACE",
"JSON",
"UNKNOWN",
],
removeDialog: {
show: false,
uuid: null,
},
removeFolderDialog: {
show: false,
uuid: null,
},
createFolderDialog: {
show: false,
valid: false,
data: {
"name": null,
},
},
addDialog: {
show: false,
valid: false,
data: {
"name": null,
"folder": null,
"url": null,
},
},
editDialog: {
show: false,
valid: false,
uuid: null, //
data: {
"type": null,
"name": null,
"url": null,
"folder": null,
},
},
editFolderDialog: {
show: false,
valid: false,
uuid: null, //
data: {
"name": null,
"folder": null,
},
},
receiveDialog: {
show: false,
valid: false,
data: {
"userUUID": null,
"userDisplayName": null,
"name": null,
"folder": null,
"type": null,
"url": null,
},
},
shareDialog: {
show: false,
valid: false,
data: {
"uuid": null, // UUID of the item you want to share. THIS IS THE KEY.
"url": null, // The item you want to share.
"recipient": null,
}
},
},
mutations: {
mutate (state, payload) {
state[payload.property] = payload.with;
// console.info("Payload:", payload.property, "with:", payload.with, "state is now:", this.state);
},
sortTopInventory (state, payload) {
let { items } = state;
if (payload.sort === "az") {
state.items.sort(function(a, b) {
var nameA = a.name.toUpperCase(); // ignore upper and lowercase
var nameB = b.name.toUpperCase(); // ignore upper and lowercase
if (nameA < nameB) {
return -1;
}
if (nameA > nameB) {
return 1;
}
// names must be equal
return 0;
});
}
if (payload.sort === "za") {
state.items.sort(function(a, b) {
var nameA = a.name.toUpperCase(); // ignore upper and lowercase
var nameB = b.name.toUpperCase(); // ignore upper and lowercase
if (nameA > nameB) {
return -1;
}
if (nameA < nameB) {
return 1;
}
// names must be equal
return 0;
});
}
Vue.set(state,'items', items);
},
pushToItems (state, payload) {
let { items } = state;
items.push(payload);
Vue.set(state,'items', items);
},
moveFolder (state, payload) {
let { items } = state;
if (payload.parentFolderUUID === "top") {
payload.findFolder.returnedItem.folder = "No Folder";
// console.info("Going to push...", payload.findFolder.returnedItem);
// console.info("Containing these items...", payload.findFolder.returnedItem.items);
items.push(payload.findFolder.returnedItem);
Vue.set(state, 'items', items);
} else if (payload.findParentFolder) {
// console.info("Going to push...", payload.findFolder.returnedItem);
// console.info("Containing these items...", payload.findFolder.returnedItem.items);
// console.info("Into...", payload.findParentFolder.returnedItem);
payload.findFolder.returnedItem.folder = payload.findParentFolder.name;
payload.findParentFolder.returnedItem.items.push(payload.findFolder.returnedItem);
Vue.set(state,'items', items);
}
},
moveItem (state, payload) {
let { items } = state;
if (payload.parentFolderUUID === "top") {
payload.findItem.returnedItem.folder = "No Folder";
// console.info("Going to push...", payload.findFolder.returnedItem);
// console.info("Containing these items...", payload.findFolder.returnedItem.items);
items.push(payload.findItem.returnedItem);
Vue.set(state,'items', items);
} else if (payload.findParentFolder) {
// console.info("Going to push...", payload.findFolder.returnedItem);
// console.info("Containing these items...", payload.findFolder.returnedItem.items);
// console.info("Into...", payload.findParentFolder.returnedItem);
payload.findItem.returnedItem.folder = payload.findParentFolder.name;
payload.findParentFolder.returnedItem.items.push(payload.findItem.returnedItem);
Vue.set(state,'items', items);
}
}
}
})

View file

@ -1,17 +0,0 @@
/*
vuetify.js
Created by Kalila L. on 7 Apr 2020
Copyright 2020 Vircadia and contributors.
Distributed under the Apache License, Version 2.0.
See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
*/
import Vue from 'vue';
import Vuetify from 'vuetify/lib';
Vue.use(Vuetify);
export default new Vuetify({
});

View file

@ -1,17 +0,0 @@
/*
vue.config.js
Created by Kalila L. on 7 Apr 2020
Copyright 2020 Vircadia and contributors.
Distributed under the Apache License, Version 2.0.
See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
*/
module.exports = {
publicPath: "./",
assetsDir: "./",
"transpileDependencies": [
"vuetify"
]
}

View file

@ -3,6 +3,7 @@
// Created by Ryan Huffman on 6 Nov 2014
// Copyright 2014 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
// Copyright 2022 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -196,8 +197,11 @@ Grid = function() {
};
that.update = function(data) {
var gridNeedsUpdate = false;
if (data.snapToGrid !== undefined) {
snapToGrid = data.snapToGrid;
var gridNeedsUpdate = true;
}
if (data.origin) {
@ -206,29 +210,37 @@ Grid = function() {
pos.y = pos.y === undefined ? origin.y : parseFloat(pos.y);
pos.z = pos.z === undefined ? origin.z : parseFloat(pos.z);
that.setPosition(pos, true);
var gridNeedsUpdate = true;
}
if (data.minorGridEvery) {
minorGridEvery = data.minorGridEvery;
var gridNeedsUpdate = true;
}
if (data.majorGridEvery) {
majorGridEvery = data.majorGridEvery;
var gridNeedsUpdate = true;
}
if (data.gridColor) {
gridColor = data.gridColor;
var gridNeedsUpdate = true;
}
if (data.gridSize) {
halfSize = data.gridSize;
var gridNeedsUpdate = true;
}
if (data.visible !== undefined) {
that.setVisible(data.visible, true);
var gridNeedsUpdate = true;
}
updateGrid(true);
if (gridNeedsUpdate) {
updateGrid(true);
}
};
function updateGrid(noUpdate) {

View file

@ -1,4 +1,4 @@
[
{"node": "https://metaverse.vircadia.com/live"},
{"node": "https://mv.overte.org/server"}
]
{"node": "https://mv.overte.org/server"}
]

View file

@ -487,6 +487,41 @@ div.placeEntry {
text-align: center;
}
div.addMsEntry {
width: 85%;
height: 38px;
background: #292929;
border: 0px;
border-radius: 6px;
box-shadow: 3px 3px 5px rgba(0,0,0,0.8);
margin: 6px 0px 9px 6px;
text-align: left;
}
#addMS {
margin: 8px;
width: 85%;
}
#addMsBtn {
background: #bababa;
background-image: linear-gradient(to bottom, #bababa, #424242);
border-radius: 4px;
color: #ffffff;
font-size: 18px;
padding: 1px 5px 1px 5px;
border: 0px;
text-decoration: none;
font-weight: 700;
}
#addMsBtn:hover {
background: #e0e0e0;
background-image: linear-gradient(to bottom, #e0e0e0, #737373);
text-decoration: none;
font-weight: 700;
}
div.placeEntryHighlighter {
width: 85%;
height: 80px;
@ -847,7 +882,7 @@ td.warningTitle {
font-size: 10px;
font-style: none;
color: #ffffff;
text-align: left;
text-align: left;
}
.region-LOCAL {
@ -913,3 +948,10 @@ button.federationFilterOn:hover {
button.externalFilterOn:hover {
border: solid #00e2ff 1px;
}
#reloading {
width: 80%;
color: #ffffff;
font-size: 18px;
text-align: center;
}

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