Merge branch 'master' of http://github.com/highfidelity/hifi into taa

This commit is contained in:
Olivier Prat 2018-02-27 09:11:43 +01:00
commit 2d71d7da9f
114 changed files with 1605 additions and 849 deletions

View file

@ -9,22 +9,13 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <LogHandler.h>
#include <BuildInfo.h>
#include <SharedUtil.h>
#include "AssignmentClientApp.h"
#include <BuildInfo.h>
int main(int argc, char* argv[]) {
disableQtBearerPoll(); // Fixes wifi ping spikes
QCoreApplication::setApplicationName(BuildInfo::ASSIGNMENT_CLIENT_NAME);
QCoreApplication::setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION);
QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN);
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
qInstallMessageHandler(LogHandler::verboseMessageHandler);
qInfo() << "Starting.";
setupHifiApplication(BuildInfo::ASSIGNMENT_CLIENT_NAME);
AssignmentClientApp app(argc, argv);

View file

@ -22,22 +22,10 @@
#include "DomainServer.h"
int main(int argc, char* argv[]) {
disableQtBearerPoll(); // Fixes wifi ping spikes
QCoreApplication::setApplicationName(BuildInfo::DOMAIN_SERVER_NAME);
QCoreApplication::setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION);
QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN);
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
setupHifiApplication(BuildInfo::DOMAIN_SERVER_NAME);
Setting::init();
#ifndef WIN32
setvbuf(stdout, NULL, _IOLBF, 0);
#endif
qInstallMessageHandler(LogHandler::verboseMessageHandler);
qInfo() << "Starting.";
int currentExitCode = 0;
// use a do-while to handle domain-server restart

View file

@ -11,18 +11,13 @@
#include <QtCore/QCoreApplication>
#include <LogHandler.h>
#include <SharedUtil.h>
#include "IceServer.h"
int main(int argc, char* argv[]) {
#ifndef WIN32
setvbuf(stdout, NULL, _IOLBF, 0);
#endif
qInstallMessageHandler(LogHandler::verboseMessageHandler);
qInfo() << "Starting.";
setupHifiApplication("Ice Server");
IceServer iceServer(argc, argv);
return iceServer.exec();
}
}

View file

@ -22,6 +22,10 @@ TableView {
readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light
property bool expandSelectedRow: false
property bool centerHeaderText: false
readonly property real headerSpacing: 3 //spacing between sort indicator and table header title
property var titlePaintedPos: [] // storing extra data position behind painted
// title text and sort indicatorin table's header
signal titlePaintedPosSignal(int column) //signal that extradata position gets changed
model: ListModel { }
@ -69,36 +73,39 @@ TableView {
height: hifi.dimensions.tableHeaderHeight
color: isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark
RalewayRegular {
id: titleText
x: centerHeaderText ? (parent.width - paintedWidth -
((sortIndicatorVisible &&
sortIndicatorColumn === styleData.column) ?
(titleSort.paintedWidth / 5 + tableView.headerSpacing) : 0)) / 2 :
hifi.dimensions.tablePadding
text: styleData.value
size: hifi.fontSizes.tableHeading
font.capitalization: Font.AllUppercase
color: hifi.colors.baseGrayHighlight
horizontalAlignment: (centerHeaderText ? Text.AlignHCenter : Text.AlignLeft)
anchors {
left: parent.left
leftMargin: hifi.dimensions.tablePadding
right: parent.right
rightMargin: hifi.dimensions.tablePadding
verticalCenter: parent.verticalCenter
}
anchors.verticalCenter: parent.verticalCenter
}
//actual image of sort indicator in glyph font only 20% of real font size
//i.e. if the charachter size set to 60 pixels, actual image is 12 pixels
HiFiGlyphs {
id: titleSort
text: sortIndicatorOrder == Qt.AscendingOrder ? hifi.glyphs.caratUp : hifi.glyphs.caratDn
color: hifi.colors.darkGray
opacity: 0.6;
size: hifi.fontSizes.tableHeadingIcon
anchors {
left: titleText.right
leftMargin: -hifi.fontSizes.tableHeadingIcon / 3 - (centerHeaderText ? 15 : 10)
right: parent.right
rightMargin: hifi.dimensions.tablePadding
verticalCenter: titleText.verticalCenter
}
anchors.verticalCenter: titleText.verticalCenter
anchors.left: titleText.right
anchors.leftMargin: -(hifi.fontSizes.tableHeadingIcon / 2.5) + tableView.headerSpacing
visible: sortIndicatorVisible && sortIndicatorColumn === styleData.column
onXChanged: {
titlePaintedPos[styleData.column] = titleText.x + titleText.paintedWidth +
paintedWidth / 5 + tableView.headerSpacing*2
titlePaintedPosSignal(styleData.column)
}
}
Rectangle {
@ -152,7 +159,7 @@ TableView {
color: styleData.selected
? hifi.colors.primaryHighlight
: tableView.isLightColorScheme
? (styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd)
: (styleData.alternate ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd)
? (styleData.alternate ? hifi.colors.tableRowLightEven : hifi.colors.tableRowLightOdd)
: (styleData.alternate ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd)
}
}

View file

@ -206,7 +206,7 @@ Windows.ScrollingWindow {
SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box";
SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere";
var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH;
var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_SIMPLE_COMPOUND;
var DYNAMIC_DEFAULT = false;
var prompt = desktop.customInputDialog({
textInput: {

View file

@ -30,7 +30,7 @@ Rectangle {
property int myCardWidth: width - upperRightInfoContainer.width;
property int myCardHeight: 100;
property int rowHeight: 60;
property int actionButtonWidth: 55;
property int actionButtonWidth: 65;
property int locationColumnWidth: 170;
property int nearbyNameCardWidth: nearbyTable.width - (iAmAdmin ? (actionButtonWidth * 4) : (actionButtonWidth * 2)) - 4 - hifi.dimensions.scrollbarBackgroundWidth;
property int connectionsNameCardWidth: connectionsTable.width - locationColumnWidth - actionButtonWidth - 4 - hifi.dimensions.scrollbarBackgroundWidth;
@ -415,6 +415,7 @@ Rectangle {
movable: false;
resizable: false;
}
TableViewColumn {
role: "ignore";
title: "IGNORE";
@ -599,13 +600,23 @@ Rectangle {
}
// This Rectangle refers to the [?] popup button next to "NAMES"
Rectangle {
id: questionRect
color: hifi.colors.tableBackgroundLight;
width: 20;
height: hifi.dimensions.tableHeaderHeight - 2;
anchors.left: nearbyTable.left;
anchors.top: nearbyTable.top;
anchors.topMargin: 1;
anchors.leftMargin: actionButtonWidth + nearbyNameCardWidth/2 + displayNameHeaderMetrics.width/2 + 6;
Connections {
target: nearbyTable
onTitlePaintedPosSignal: {
if (column === 1) { // name column
questionRect.anchors.leftMargin = actionButtonWidth + nearbyTable.titlePaintedPos[column]
}
}
}
RalewayRegular {
id: helpText;
text: "[?]";

View file

@ -48,6 +48,7 @@ Rectangle {
property bool debugCheckoutSuccess: false;
property bool canRezCertifiedItems: Entities.canRezCertified() || Entities.canRezTmpCertified();
property string referrer;
property bool isInstalled;
// Style
color: hifi.colors.white;
Connections {
@ -122,6 +123,12 @@ Rectangle {
root.refreshBuyUI();
}
}
onAppInstalled: {
if (appHref === root.itemHref) {
root.isInstalled = true;
}
}
}
onItemIdChanged: {
@ -146,7 +153,8 @@ Rectangle {
}
onItemTypeChanged: {
if (root.itemType === "entity" || root.itemType === "wearable" || root.itemType === "contentSet" || root.itemType === "avatar") {
if (root.itemType === "entity" || root.itemType === "wearable" ||
root.itemType === "contentSet" || root.itemType === "avatar" || root.itemType === "app") {
root.isCertified = true;
} else {
root.isCertified = false;
@ -311,7 +319,7 @@ Rectangle {
z: 997;
visible: !root.ownershipStatusReceived || !root.balanceReceived;
anchors.fill: parent;
color: Qt.rgba(0.0, 0.0, 0.0, 0.7);
color: hifi.colors.white;
// This object is always used in a popup.
// This MouseArea is used to prevent a user from being
@ -323,8 +331,9 @@ Rectangle {
}
AnimatedImage {
source: "../common/images/loader.gif"
width: 96;
id: loadingImage;
source: "../common/images/loader-blue.gif"
width: 74;
height: width;
anchors.verticalCenter: parent.verticalCenter;
anchors.horizontalCenter: parent.horizontalCenter;
@ -679,7 +688,7 @@ Rectangle {
id: rezNowButton;
enabled: (root.itemType === "entity" && root.canRezCertifiedItems) ||
(root.itemType === "contentSet" && Entities.canReplaceContent()) ||
root.itemType === "wearable" || root.itemType === "avatar";
root.itemType === "wearable" || root.itemType === "avatar" || root.itemType === "app";
buttonGlyph: (root.buttonGlyph)[itemTypesArray.indexOf(root.itemType)];
color: hifi.buttons.red;
colorScheme: hifi.colorSchemes.light;
@ -688,7 +697,7 @@ Rectangle {
height: 50;
anchors.left: parent.left;
anchors.right: parent.right;
text: (root.buttonTextNormal)[itemTypesArray.indexOf(root.itemType)];
text: root.itemType === "app" && root.isInstalled ? "OPEN APP" : (root.buttonTextNormal)[itemTypesArray.indexOf(root.itemType)];
onClicked: {
if (root.itemType === "contentSet") {
lightboxPopup.titleText = "Replace Content";
@ -712,6 +721,12 @@ Rectangle {
lightboxPopup.button2text = "CONFIRM";
lightboxPopup.button2method = "MyAvatar.useFullAvatarURL('" + root.itemHref + "'); root.visible = false;";
lightboxPopup.visible = true;
} else if (root.itemType === "app") {
if (root.isInstalled) {
Commerce.openApp(root.itemHref);
} else {
Commerce.installApp(root.itemHref);
}
} else {
sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, itemType: root.itemType});
rezzedNotifContainer.visible = true;

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

View file

@ -47,6 +47,7 @@ Item {
property bool showConfirmation: false;
property bool hasPermissionToRezThis;
property bool permissionExplanationCardVisible;
property bool isInstalled;
property string originalStatusText;
property string originalStatusColor;
@ -62,6 +63,18 @@ Item {
showConfirmation = true;
}
}
onAppInstalled: {
if (appHref === root.itemHref) {
root.isInstalled = true;
}
}
onAppUninstalled: {
if (appHref === root.itemHref) {
root.isInstalled = false;
}
}
}
Connections {
@ -472,6 +485,43 @@ Item {
}
}
Rectangle {
id: appButtonContainer;
color: hifi.colors.white;
z: 994;
visible: root.isInstalled;
anchors.fill: buttonContainer;
HifiControlsUit.Button {
id: openAppButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.light;
anchors.top: parent.top;
anchors.right: parent.right;
anchors.left: parent.left;
width: 92;
height: 44;
text: "OPEN"
onClicked: {
Commerce.openApp(root.itemHref);
}
}
HifiControlsUit.Button {
id: uninstallAppButton;
color: hifi.buttons.noneBorderless;
colorScheme: hifi.colorSchemes.light;
anchors.bottom: parent.bottom;
anchors.right: parent.right;
anchors.left: parent.left;
height: 44;
text: "UNINSTALL"
onClicked: {
Commerce.uninstallApp(root.itemHref);
}
}
}
Button {
id: buttonContainer;
property int color: hifi.buttons.blue;
@ -506,6 +556,9 @@ Item {
sendToPurchases({method: 'showReplaceContentLightbox', itemHref: root.itemHref});
} else if (root.itemType === "avatar") {
sendToPurchases({method: 'showChangeAvatarLightbox', itemName: root.itemName, itemHref: root.itemHref});
} else if (root.itemType === "app") {
// "Run" and "Uninstall" buttons are separate.
Commerce.installApp(root.itemHref);
} else {
sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, itemType: root.itemType});
root.showConfirmation = true;

View file

@ -36,6 +36,7 @@ Rectangle {
property bool isShowingMyItems: false;
property bool isDebuggingFirstUseTutorial: false;
property int pendingItemCount: 0;
property string installedApps;
// Style
color: hifi.colors.white;
Connections {
@ -61,6 +62,7 @@ Rectangle {
root.activeView = "firstUseTutorial";
} else if (!Settings.getValue("isFirstUseOfPurchases", true) && root.activeView === "initialize") {
root.activeView = "purchasesMain";
root.installedApps = Commerce.getInstalledApps();
Commerce.inventory();
}
} else {
@ -269,6 +271,7 @@ Rectangle {
case 'tutorial_finished':
Settings.setValue("isFirstUseOfPurchases", false);
root.activeView = "purchasesMain";
root.installedApps = Commerce.getInstalledApps();
Commerce.inventory();
break;
}
@ -394,6 +397,7 @@ Rectangle {
limitedRun: model.limited_run;
displayedItemCount: model.displayedItemCount;
permissionExplanationCardVisible: model.permissionExplanationCardVisible;
isInstalled: model.isInstalled;
itemType: {
if (model.root_file_url.indexOf(".fst") > -1) {
"avatar";
@ -680,9 +684,13 @@ Rectangle {
if (sameItemCount !== tempPurchasesModel.count || filterBar.text !== filterBar.previousText) {
filteredPurchasesModel.clear();
var currentId;
for (var i = 0; i < tempPurchasesModel.count; i++) {
currentId = tempPurchasesModel.get(i).id;
filteredPurchasesModel.append(tempPurchasesModel.get(i));
filteredPurchasesModel.setProperty(i, 'permissionExplanationCardVisible', false);
filteredPurchasesModel.setProperty(i, 'isInstalled', ((root.installedApps).indexOf(currentId) > -1));
}
populateDisplayedItemCounts();

View file

@ -441,7 +441,7 @@ Item {
}
Item {
id: choosePassphraseContainer;
visible: root.activeView === "step_3";
visible: root.hasShownSecurityImageTip && root.activeView === "step_3";
// Anchors
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 30;
@ -451,7 +451,10 @@ Item {
onVisibleChanged: {
if (visible) {
sendSignalToWallet({method: 'disableHmdPreview'});
Commerce.getWalletAuthenticatedStatus();
} else {
sendSignalToWallet({method: 'maybeEnableHmdPreview'});
}
}

View file

@ -1,19 +0,0 @@
import QtQuick 2.5
import Qt.labs.settings 1.0
import "../../dialogs"
PreferencesDialog {
id: root
objectName: "GraphicsPreferencesDialog"
title: "Graphics Settings"
showCategories: ["Graphics"]
property var settings: Settings {
category: root.objectName
property alias x: root.x
property alias y: root.y
property alias width: root.width
property alias height: root.height
}
}

View file

@ -207,7 +207,7 @@ Rectangle {
SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box";
SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere";
var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH;
var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_SIMPLE_COMPOUND;
var DYNAMIC_DEFAULT = false;
var prompt = tabletRoot.customInputDialog({
textInput: {

View file

@ -1,37 +0,0 @@
//
// TabletGraphicsPreferences.qml
//
// Created by Vlad Stelmahovsky on 12 Mar 2017.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import "tabletWindows"
import "../../dialogs"
StackView {
id: profileRoot
initialItem: root
objectName: "stack"
property string title: "Graphics Settings"
signal sendToScript(var message);
function pushSource(path) {
profileRoot.push(Qt.resolvedUrl(path));
}
function popSource() {
profileRoot.pop();
}
TabletPreferencesDialog {
id: root
objectName: "TabletGraphicsPreferences"
showCategories: ["Graphics"]
}
}

View file

@ -68,7 +68,6 @@
#include <Midi.h>
#include <AudioInjectorManager.h>
#include <AvatarBookmarks.h>
#include <AvatarEntitiesBookmarks.h>
#include <CursorManager.h>
#include <DebugDraw.h>
#include <DeferredLightingEffect.h>
@ -392,7 +391,7 @@ const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensi
class DeadlockWatchdogThread : public QThread {
public:
static const unsigned long HEARTBEAT_UPDATE_INTERVAL_SECS = 1;
static const unsigned long MAX_HEARTBEAT_AGE_USECS = 30 * USECS_PER_SECOND;
static const unsigned long MAX_HEARTBEAT_AGE_USECS = 120 * USECS_PER_SECOND; // 2 mins with no checkin probably a deadlock
static const int WARNING_ELAPSED_HEARTBEAT = 500 * USECS_PER_MSEC; // warn if elapsed heartbeat average is large
static const int HEARTBEAT_SAMPLES = 100000; // ~5 seconds worth of samples
@ -575,10 +574,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt
QString logMessage = LogHandler::getInstance().printMessage((LogMsgType) type, context, message);
if (!logMessage.isEmpty()) {
#ifdef Q_OS_WIN
OutputDebugStringA(logMessage.toLocal8Bit().constData());
OutputDebugStringA("\n");
#elif defined Q_OS_ANDROID
#ifdef Q_OS_ANDROID
const char * local=logMessage.toStdString().c_str();
switch (type) {
case QtDebugMsg:
@ -599,7 +595,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt
abort();
}
#endif
qApp->getLogger()->addMessage(qPrintable(logMessage + "\n"));
qApp->getLogger()->addMessage(qPrintable(logMessage));
}
}
@ -785,7 +781,6 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<GooglePolyScriptingInterface>();
DependencyManager::set<OctreeStatsProvider>(nullptr, qApp->getOcteeSceneStats());
DependencyManager::set<AvatarBookmarks>();
DependencyManager::set<AvatarEntitiesBookmarks>();
DependencyManager::set<LocationBookmarks>();
DependencyManager::set<Snapshot>();
DependencyManager::set<CloseEventSender>();
@ -1515,6 +1510,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
settingsTimer->setSingleShot(false);
settingsTimer->setInterval(SAVE_SETTINGS_INTERVAL); // 10s, Qt::CoarseTimer acceptable
QObject::connect(settingsTimer, &QTimer::timeout, this, &Application::saveSettings);
settingsTimer->start();
}, QThread::LowestPriority);
if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) {
@ -1719,6 +1715,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
properties["has_async_reprojection"] = displayPlugin->hasAsyncReprojection();
properties["hardware_stats"] = displayPlugin->getHardwareStats();
// deadlock watchdog related stats
properties["deadlock_watchdog_maxElapsed"] = (int)DeadlockWatchdogThread::_maxElapsed;
properties["deadlock_watchdog_maxElapsedAverage"] = (int)DeadlockWatchdogThread::_maxElapsedAverage;
auto bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
properties["packet_rate_in"] = bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond();
properties["packet_rate_out"] = bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond();
@ -2606,7 +2606,6 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
surfaceContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
surfaceContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
surfaceContext->setContextProperty("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
surfaceContext->setContextProperty("AvatarEntitiesBookmarks", DependencyManager::get<AvatarEntitiesBookmarks>().data());
surfaceContext->setContextProperty("LocationBookmarks", DependencyManager::get<LocationBookmarks>().data());
// Caches
@ -5988,7 +5987,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
scriptEngine->registerGlobalObject("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());
scriptEngine->registerGlobalObject("AudioScope", DependencyManager::get<AudioScope>().data());
scriptEngine->registerGlobalObject("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
scriptEngine->registerGlobalObject("AvatarEntitiesBookmarks", DependencyManager::get<AvatarEntitiesBookmarks>().data());
scriptEngine->registerGlobalObject("LocationBookmarks", DependencyManager::get<LocationBookmarks>().data());
scriptEngine->registerGlobalObject("RayPick", DependencyManager::get<RayPickScriptingInterface>().data());
@ -7543,6 +7541,18 @@ void Application::deadlockApplication() {
}
}
// cause main thread to be unresponsive for 35 seconds
void Application::unresponsiveApplication() {
// to avoid compiler warnings about a loop that will never exit
uint64_t start = usecTimestampNow();
uint64_t UNRESPONSIVE_FOR_SECONDS = 35;
uint64_t UNRESPONSIVE_FOR_USECS = UNRESPONSIVE_FOR_SECONDS * USECS_PER_SECOND;
qCDebug(interfaceapp) << "Intentionally cause Interface to be unresponsive for " << UNRESPONSIVE_FOR_SECONDS << " seconds";
while (usecTimestampNow() - start < UNRESPONSIVE_FOR_USECS) {
QThread::sleep(1);
}
}
void Application::setActiveDisplayPlugin(const QString& pluginName) {
auto menu = Menu::getInstance();
foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) {

View file

@ -266,9 +266,8 @@ public:
float getGameLoopRate() const { return _gameLoopCounter.rate(); }
// Note that takeSnapshot has a default value, as this method is used internally.
void takeSnapshot(bool notify, bool includeAnimated = false, float aspectRatio = 0.0f, const QString& filename = QString());
void takeSecondaryCameraSnapshot(const QString& filename);
void takeSecondaryCameraSnapshot(const QString& filename = QString());
void shareSnapshot(const QString& filename, const QUrl& href = QUrl(""));
@ -369,6 +368,7 @@ public slots:
void updateHeartbeat() const;
static void deadlockApplication();
static void unresponsiveApplication(); // cause main thread to be unresponsive for 35 seconds
void rotationModeChanged() const;

View file

@ -19,6 +19,11 @@
#include <Application.h>
#include <OffscreenUi.h>
#include <avatar/AvatarManager.h>
#include <EntityItemID.h>
#include <EntityTree.h>
#include <PhysicalEntitySimulation.h>
#include <EntityEditPacketSender.h>
#include <VariantMapToScriptValue.h>
#include "MainWindow.h"
#include "Menu.h"
@ -29,6 +34,62 @@
#include <QtQuick/QQuickWindow>
void addAvatarEntities(const QVariantList& avatarEntities) {
auto nodeList = DependencyManager::get<NodeList>();
const QUuid myNodeID = nodeList->getSessionUUID();
EntityTreePointer entityTree = DependencyManager::get<EntityTreeRenderer>()->getTree();
if (!entityTree) {
return;
}
EntitySimulationPointer entitySimulation = entityTree->getSimulation();
PhysicalEntitySimulationPointer physicalEntitySimulation = std::static_pointer_cast<PhysicalEntitySimulation>(entitySimulation);
EntityEditPacketSender* entityPacketSender = physicalEntitySimulation->getPacketSender();
QScriptEngine scriptEngine;
for (int index = 0; index < avatarEntities.count(); index++) {
const QVariantMap& avatarEntityProperties = avatarEntities.at(index).toMap();
QVariant variantProperties = avatarEntityProperties["properties"];
QVariantMap asMap = variantProperties.toMap();
QScriptValue scriptProperties = variantMapToScriptValue(asMap, scriptEngine);
EntityItemProperties entityProperties;
EntityItemPropertiesFromScriptValueHonorReadOnly(scriptProperties, entityProperties);
entityProperties.setParentID(myNodeID);
entityProperties.setClientOnly(true);
entityProperties.setOwningAvatarID(myNodeID);
entityProperties.setSimulationOwner(myNodeID, AVATAR_ENTITY_SIMULATION_PRIORITY);
entityProperties.markAllChanged();
EntityItemID id = EntityItemID(QUuid::createUuid());
bool success = true;
entityTree->withWriteLock([&] {
EntityItemPointer entity = entityTree->addEntity(id, entityProperties);
if (entity) {
if (entityProperties.queryAACubeRelatedPropertyChanged()) {
// due to parenting, the server may not know where something is in world-space, so include the bounding cube.
bool success;
AACube queryAACube = entity->getQueryAACube(success);
if (success) {
entityProperties.setQueryAACube(queryAACube);
}
}
entity->setLastBroadcast(usecTimestampNow());
// since we're creating this object we will immediately volunteer to own its simulation
entity->flagForOwnershipBid(VOLUNTEER_SIMULATION_PRIORITY);
entityProperties.setLastEdited(entity->getLastEdited());
} else {
qCDebug(entities) << "AvatarEntitiesBookmark failed to add new Entity to local Octree";
success = false;
}
});
if (success) {
entityPacketSender->queueEditEntityMessage(PacketType::EntityAdd, entityTree, id, entityProperties);
}
}
}
AvatarBookmarks::AvatarBookmarks() {
_bookmarksFilename = PathUtils::getAppDataPath() + "/" + AVATARBOOKMARKS_FILENAME;
readFromFile();
@ -38,7 +99,7 @@ void AvatarBookmarks::readFromFile() {
// migrate old avatarbookmarks.json, used to be in 'local' folder on windows
QString oldConfigPath = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + AVATARBOOKMARKS_FILENAME;
QFile oldConfig(oldConfigPath);
// I imagine that in a year from now, this code for migrating (as well as the two lines above)
// may be removed since all bookmarks should have been migrated by then
// - Robbie Uvanni (6.8.2017)
@ -48,9 +109,9 @@ void AvatarBookmarks::readFromFile() {
} else {
qCDebug(interfaceapp) << "Failed to migrate" << AVATARBOOKMARKS_FILENAME;
}
}
Bookmarks::readFromFile();
}
Bookmarks::readFromFile();
}
void AvatarBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
@ -81,23 +142,27 @@ void AvatarBookmarks::changeToBookmarkedAvatar() {
myAvatar->useFullAvatarURL(action->data().toString());
qCDebug(interfaceapp) << " Using Legacy V1 Avatar Bookmark ";
} else {
const QMap<QString, QVariant> bookmark = action->data().toMap();
// Not magic value. This is the current made version, and if it changes this interpreter should be updated to
// Not magic value. This is the current made version, and if it changes this interpreter should be updated to
// handle the new one separately.
// This is where the avatar bookmark entry is parsed. If adding new Value, make sure to have backward compatability with previous
if (bookmark.value(ENTRY_VERSION) == 3) {
const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString();
myAvatar->useFullAvatarURL(avatarUrl);
qCDebug(interfaceapp) << "Avatar On " << avatarUrl;
const QList<QVariant>& attachments = bookmark.value(ENTRY_AVATAR_ATTACHMENTS, QList<QVariant>()).toList();
myAvatar->removeAvatarEntities();
const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString();
myAvatar->useFullAvatarURL(avatarUrl);
qCDebug(interfaceapp) << "Avatar On " << avatarUrl;
const QList<QVariant>& attachments = bookmark.value(ENTRY_AVATAR_ATTACHMENTS, QList<QVariant>()).toList();
qCDebug(interfaceapp) << "Attach " << attachments;
myAvatar->setAttachmentsVariant(attachments);
qCDebug(interfaceapp) << "Attach " << attachments;
myAvatar->setAttachmentsVariant(attachments);
const float& qScale = bookmark.value(ENTRY_AVATAR_SCALE, 1.0f).toFloat();
myAvatar->setAvatarScale(qScale);
const QVariantList& avatarEntities = bookmark.value(ENTRY_AVATAR_ENTITIES, QVariantList()).toList();
addAvatarEntities(avatarEntities);
const float& qScale = bookmark.value(ENTRY_AVATAR_SCALE, 1.0f).toFloat();
myAvatar->setAvatarScale(qScale);
} else {
qCDebug(interfaceapp) << " Bookmark entry does not match client version, make sure client has a handler for the new AvatarBookmark";
}
@ -126,6 +191,7 @@ void AvatarBookmarks::addBookmark() {
bookmark.insert(ENTRY_AVATAR_URL, avatarUrl);
bookmark.insert(ENTRY_AVATAR_SCALE, avatarScale);
bookmark.insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant());
bookmark.insert(ENTRY_AVATAR_ENTITIES, myAvatar->getAvatarEntitiesVariant());
Bookmarks::addBookmarkToFile(bookmarkName, bookmark);
});

View file

@ -34,6 +34,7 @@ private:
const QString AVATARBOOKMARKS_FILENAME = "avatarbookmarks.json";
const QString ENTRY_AVATAR_URL = "avatarUrl";
const QString ENTRY_AVATAR_ATTACHMENTS = "attachments";
const QString ENTRY_AVATAR_ENTITIES = "avatarEntites";
const QString ENTRY_AVATAR_SCALE = "avatarScale";
const QString ENTRY_VERSION = "version";

View file

@ -1,168 +0,0 @@
//
// AvatarEntitiesBookmarks.cpp
// interface/src
//
// Created by Dante Ruiz on 15/01/18.
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QAction>
#include <QInputDialog>
#include <QMessageBox>
#include <QStandardPaths>
#include <QQmlContext>
#include <QList>
#include <Application.h>
#include <OffscreenUi.h>
#include <EntityItemProperties.h>
#include <GLMHelpers.h>
#include <avatar/AvatarManager.h>
#include <EntityItemID.h>
#include <EntityTree.h>
#include <PhysicalEntitySimulation.h>
#include <EntityEditPacketSender.h>
#include <VariantMapToScriptValue.h>
#include "MainWindow.h"
#include "Menu.h"
#include "AvatarEntitiesBookmarks.h"
#include "InterfaceLogging.h"
#include "QVariantGLM.h"
#include <QtQuick/QQuickWindow>
void addAvatarEntities(const QVariantList& avatarEntities) {
auto nodeList = DependencyManager::get<NodeList>();
const QUuid myNodeID = nodeList->getSessionUUID();
EntityTreePointer entityTree = DependencyManager::get<EntityTreeRenderer>()->getTree();
if (!entityTree) {
return;
}
EntitySimulationPointer entitySimulation = entityTree->getSimulation();
PhysicalEntitySimulationPointer physicalEntitySimulation = std::static_pointer_cast<PhysicalEntitySimulation>(entitySimulation);
EntityEditPacketSender* entityPacketSender = physicalEntitySimulation->getPacketSender();
QScriptEngine scriptEngine;
for (int index = 0; index < avatarEntities.count(); index++) {
const QVariantMap& avatarEntityProperties = avatarEntities.at(index).toMap();
QVariant variantProperties = avatarEntityProperties["properties"];
QVariantMap asMap = variantProperties.toMap();
QScriptValue scriptProperties = variantMapToScriptValue(asMap, scriptEngine);
EntityItemProperties entityProperties;
EntityItemPropertiesFromScriptValueHonorReadOnly(scriptProperties, entityProperties);
entityProperties.setParentID(myNodeID);
entityProperties.setClientOnly(true);
entityProperties.setOwningAvatarID(myNodeID);
entityProperties.setSimulationOwner(myNodeID, AVATAR_ENTITY_SIMULATION_PRIORITY);
entityProperties.markAllChanged();
EntityItemID id = EntityItemID(QUuid::createUuid());
bool success = true;
entityTree->withWriteLock([&] {
EntityItemPointer entity = entityTree->addEntity(id, entityProperties);
if (entity) {
if (entityProperties.queryAACubeRelatedPropertyChanged()) {
// due to parenting, the server may not know where something is in world-space, so include the bounding cube.
bool success;
AACube queryAACube = entity->getQueryAACube(success);
if (success) {
entityProperties.setQueryAACube(queryAACube);
}
}
entity->setLastBroadcast(usecTimestampNow());
// since we're creating this object we will immediately volunteer to own its simulation
entity->flagForOwnershipBid(VOLUNTEER_SIMULATION_PRIORITY);
entityProperties.setLastEdited(entity->getLastEdited());
} else {
qCDebug(entities) << "AvatarEntitiesBookmark failed to add new Entity to local Octree";
success = false;
}
});
if (success) {
entityPacketSender->queueEditEntityMessage(PacketType::EntityAdd, entityTree, id, entityProperties);
}
}
}
AvatarEntitiesBookmarks::AvatarEntitiesBookmarks() {
_bookmarksFilename = PathUtils::getAppDataPath() + "/" + AVATAR_ENTITIES_BOOKMARKS_FILENAME;
Bookmarks::readFromFile();
}
void AvatarEntitiesBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
auto bookmarkAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::BookmarkAvatarEntities);
QObject::connect(bookmarkAction, SIGNAL(triggered()), this, SLOT(addBookmark()), Qt::QueuedConnection);
_bookmarksMenu = menu->addMenu(MenuOption::AvatarEntitiesBookmarks);
_deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteAvatarEntitiesBookmark);
QObject::connect(_deleteBookmarksAction, SIGNAL(triggered()), this, SLOT(deleteBookmark()), Qt::QueuedConnection);
for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it) {
addBookmarkToMenu(menubar, it.key(), it.value());
}
Bookmarks::sortActions(menubar, _bookmarksMenu);
}
void AvatarEntitiesBookmarks::applyBookmarkedAvatarEntities() {
QAction* action = qobject_cast<QAction*>(sender());
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
const QMap<QString, QVariant> bookmark = action->data().toMap();
if (bookmark.value(ENTRY_VERSION) == AVATAR_ENTITIES_BOOKMARK_VERSION) {
myAvatar->removeAvatarEntities();
const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString();
myAvatar->useFullAvatarURL(avatarUrl);
const QVariantList& avatarEntities = bookmark.value(ENTRY_AVATAR_ENTITIES, QVariantList()).toList();
addAvatarEntities(avatarEntities);
const float& avatarScale = bookmark.value(ENTRY_AVATAR_SCALE, 1.0f).toFloat();
myAvatar->setAvatarScale(avatarScale);
} else {
qCDebug(interfaceapp) << " Bookmark entry does not match client version, make sure client has a handler for the new AvatarEntitiesBookmark";
}
}
void AvatarEntitiesBookmarks::addBookmark() {
ModalDialogListener* dlg = OffscreenUi::getTextAsync(OffscreenUi::ICON_PLACEMARK, "Bookmark Avatar Entities", "Name", QString());
connect(dlg, &ModalDialogListener::response, this, [=] (QVariant response) {
disconnect(dlg, &ModalDialogListener::response, this, nullptr);
auto bookmarkName = response.toString();
bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " ");
if (bookmarkName.length() == 0) {
return;
}
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
const QString& avatarUrl = myAvatar->getSkeletonModelURL().toString();
const QVariant& avatarScale = myAvatar->getAvatarScale();
QVariantMap bookmark;
bookmark.insert(ENTRY_VERSION, AVATAR_ENTITIES_BOOKMARK_VERSION);
bookmark.insert(ENTRY_AVATAR_URL, avatarUrl);
bookmark.insert(ENTRY_AVATAR_SCALE, avatarScale);
bookmark.insert(ENTRY_AVATAR_ENTITIES, myAvatar->getAvatarEntitiesVariant());
Bookmarks::addBookmarkToFile(bookmarkName, bookmark);
});
}
void AvatarEntitiesBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) {
QAction* changeAction = _bookmarksMenu->newAction();
changeAction->setData(bookmark);
connect(changeAction, SIGNAL(triggered()), this, SLOT(applyBookmarkedAvatarEntities()));
if (!_isMenuSorted) {
menubar->addActionToQMenuAndActionHash(_bookmarksMenu, changeAction, name, 0, QAction::NoRole);
} else {
// TODO: this is aggressive but other alternatives have proved less fruitful so far.
menubar->addActionToQMenuAndActionHash(_bookmarksMenu, changeAction, name, 0, QAction::NoRole);
Bookmarks::sortActions(menubar, _bookmarksMenu);
}
}

View file

@ -1,45 +0,0 @@
//
// AvatarEntitiesBookmarks.h
// interface/src
//
// Created by Dante Ruiz on 15/01/18.
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_AvatarEntitiesBookmarks_h
#define hifi_AvatarEntitiesBookmarks_h
#include <DependencyManager.h>
#include "Bookmarks.h"
class AvatarEntitiesBookmarks: public Bookmarks, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
AvatarEntitiesBookmarks();
void setupMenus(Menu* menubar, MenuWrapper* menu) override;
public slots:
void addBookmark();
protected:
void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) override;
private:
const QString AVATAR_ENTITIES_BOOKMARKS_FILENAME = "AvatarEntitiesBookmarks.json";
const QString ENTRY_AVATAR_URL = "AvatarUrl";
const QString ENTRY_AVATAR_SCALE = "AvatarScale";
const QString ENTRY_AVATAR_ENTITIES = "AvatarEntities";
const QString ENTRY_VERSION = "version";
const int AVATAR_ENTITIES_BOOKMARK_VERSION = 1;
private slots:
void applyBookmarkedAvatarEntities();
};
#endif

View file

@ -34,7 +34,6 @@
#include "audio/AudioScope.h"
#include "avatar/AvatarManager.h"
#include "AvatarBookmarks.h"
#include "AvatarEntitiesBookmarks.h"
#include "devices/DdeFaceTracker.h"
#include "MainWindow.h"
#include "render/DrawStatus.h"
@ -44,6 +43,7 @@
#include "ui/StandAloneJSConsole.h"
#include "InterfaceLogging.h"
#include "LocationBookmarks.h"
#include "DeferredLightingEffect.h"
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
#include "SpeechRecognizer.h"
@ -207,10 +207,6 @@ Menu::Menu() {
auto avatarBookmarks = DependencyManager::get<AvatarBookmarks>();
avatarBookmarks->setupMenus(this, avatarMenu);
auto avatarEntitiesBookmarks = DependencyManager::get<AvatarEntitiesBookmarks>();
avatarEntitiesBookmarks->setupMenus(this, avatarMenu);
// Display menu ----------------------------------
// FIXME - this is not yet matching Alan's spec because it doesn't have
// menus for "2D"/"3D" - we need to add support for detecting the appropriate
@ -366,10 +362,15 @@ Menu::Menu() {
MenuWrapper* developerMenu = addMenu("Developer", "Developer");
// Developer > Graphics...
action = addActionToQMenuAndActionHash(developerMenu, "Graphics...");
connect(action, &QAction::triggered, [] {
qApp->showDialog(QString("hifi/dialogs/GraphicsPreferencesDialog.qml"),
QString("hifi/tablet/TabletGraphicsPreferences.qml"), "GraphicsPreferencesDialog");
MenuWrapper* graphicsOptionsMenu = developerMenu->addMenu("Render");
action = addCheckableActionToQMenuAndActionHash(graphicsOptionsMenu, MenuOption::Shadows, 0, true);
connect(action, &QAction::triggered, [action] {
DependencyManager::get<DeferredLightingEffect>()->setShadowMapEnabled(action->isChecked());
});
action = addCheckableActionToQMenuAndActionHash(graphicsOptionsMenu, MenuOption::AmbientOcclusion, 0, false);
connect(action, &QAction::triggered, [action] {
DependencyManager::get<DeferredLightingEffect>()->setAmbientOcclusionEnabled(action->isChecked());
});
// Developer > UI >>>
@ -713,6 +714,7 @@ Menu::Menu() {
MenuWrapper* crashMenu = developerMenu->addMenu("Crash");
addActionToQMenuAndActionHash(crashMenu, MenuOption::DeadlockInterface, 0, qApp, SLOT(deadlockApplication()));
addActionToQMenuAndActionHash(crashMenu, MenuOption::UnresponsiveInterface, 0, qApp, SLOT(unresponsiveApplication()));
action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashPureVirtualFunction);
connect(action, &QAction::triggered, qApp, []() { crash::pureVirtualCall(); });

View file

@ -46,7 +46,6 @@ namespace MenuOption {
const QString AutoMuteAudio = "Auto Mute Microphone";
const QString AvatarReceiveStats = "Show Receive Stats";
const QString AvatarBookmarks = "Avatar Bookmarks";
const QString AvatarEntitiesBookmarks = "Avatar Entities Bookmarks";
const QString Back = "Back";
const QString BinaryEyelidControl = "Binary Eyelid Control";
const QString BookmarkAvatar = "Bookmark Avatar";
@ -77,6 +76,7 @@ namespace MenuOption {
const QString CrashNewFault = "New Fault";
const QString CrashNewFaultThreaded = "New Fault (threaded)";
const QString DeadlockInterface = "Deadlock Interface";
const QString UnresponsiveInterface = "Unresponsive Interface";
const QString DecreaseAvatarSize = "Decrease Avatar Size";
const QString DefaultSkybox = "Default Skybox";
const QString DeleteAvatarBookmark = "Delete Avatar Bookmark...";
@ -204,6 +204,8 @@ namespace MenuOption {
const QString WorldAxes = "World Axes";
const QString DesktopTabletToToolbar = "Desktop Tablet Becomes Toolbar";
const QString HMDTabletToToolbar = "HMD Tablet Becomes Toolbar";
const QString Shadows = "Shadows";
const QString AmbientOcclusion = "AmbientOcclusion";
}
#endif // hifi_Menu_h

View file

@ -79,6 +79,8 @@ float DEFAULT_SCRIPTED_MOTOR_TIMESCALE = 1.0e6f;
const int SCRIPTED_MOTOR_CAMERA_FRAME = 0;
const int SCRIPTED_MOTOR_AVATAR_FRAME = 1;
const int SCRIPTED_MOTOR_WORLD_FRAME = 2;
const int SCRIPTED_MOTOR_SIMPLE_MODE = 0;
const int SCRIPTED_MOTOR_DYNAMIC_MODE = 1;
const QString& DEFAULT_AVATAR_COLLISION_SOUND_URL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/Body_Hits_Impact.wav";
const float MyAvatar::ZOOM_MIN = 0.5f;
@ -92,6 +94,7 @@ MyAvatar::MyAvatar(QThread* thread) :
_pitchSpeed(PITCH_SPEED_DEFAULT),
_scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE),
_scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME),
_scriptedMotorMode(SCRIPTED_MOTOR_SIMPLE_MODE),
_motionBehaviors(AVATAR_MOTION_DEFAULTS),
_characterController(this),
_eyeContactTarget(LEFT_EYE),
@ -1112,6 +1115,7 @@ void MyAvatar::setEnableDebugDrawIKChains(bool isEnabled) {
void MyAvatar::setEnableMeshVisible(bool isEnabled) {
_skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true);
_skeletonModel->setCanCastShadow(isEnabled, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true);
}
void MyAvatar::setEnableInverseKinematics(bool isEnabled) {
@ -1464,6 +1468,7 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
int skeletonModelChangeCount = _skeletonModelChangeCount;
Avatar::setSkeletonModelURL(skeletonModelURL);
_skeletonModel->setVisibleInScene(true, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true);
_skeletonModel->setCanCastShadow(true, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true);
_headBoneSet.clear();
_cauterizationNeedsUpdate = true;
@ -1623,20 +1628,27 @@ controller::Pose MyAvatar::getControllerPoseInAvatarFrame(controller::Action act
void MyAvatar::updateMotors() {
_characterController.clearMotors();
glm::quat motorRotation;
const float FLYING_MOTOR_TIMESCALE = 0.05f;
const float WALKING_MOTOR_TIMESCALE = 0.2f;
const float INVALID_MOTOR_TIMESCALE = 1.0e6f;
float horizontalMotorTimescale;
float verticalMotorTimescale;
if (_characterController.getState() == CharacterController::State::Hover ||
_characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) {
horizontalMotorTimescale = FLYING_MOTOR_TIMESCALE;
verticalMotorTimescale = FLYING_MOTOR_TIMESCALE;
} else {
horizontalMotorTimescale = WALKING_MOTOR_TIMESCALE;
verticalMotorTimescale = INVALID_MOTOR_TIMESCALE;
}
if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {
const float FLYING_MOTOR_TIMESCALE = 0.05f;
const float WALKING_MOTOR_TIMESCALE = 0.2f;
const float INVALID_MOTOR_TIMESCALE = 1.0e6f;
float horizontalMotorTimescale;
float verticalMotorTimescale;
if (_characterController.getState() == CharacterController::State::Hover ||
_characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) {
motorRotation = getMyHead()->getHeadOrientation();
horizontalMotorTimescale = FLYING_MOTOR_TIMESCALE;
verticalMotorTimescale = FLYING_MOTOR_TIMESCALE;
} else {
// non-hovering = walking: follow camera twist about vertical but not lift
// we decompose camera's rotation and store the twist part in motorRotation
@ -1647,8 +1659,6 @@ void MyAvatar::updateMotors() {
glm::quat liftRotation;
swingTwistDecomposition(headOrientation, Vectors::UNIT_Y, liftRotation, motorRotation);
motorRotation = orientation * motorRotation;
horizontalMotorTimescale = WALKING_MOTOR_TIMESCALE;
verticalMotorTimescale = INVALID_MOTOR_TIMESCALE;
}
if (_isPushing || _isBraking || !_isBeingPushed) {
@ -1668,7 +1678,12 @@ void MyAvatar::updateMotors() {
// world-frame
motorRotation = glm::quat();
}
_characterController.addMotor(_scriptedMotorVelocity, motorRotation, _scriptedMotorTimescale);
if (_scriptedMotorMode == SCRIPTED_MOTOR_SIMPLE_MODE) {
_characterController.addMotor(_scriptedMotorVelocity, motorRotation, _scriptedMotorTimescale);
} else {
// dynamic mode
_characterController.addMotor(_scriptedMotorVelocity, motorRotation, horizontalMotorTimescale, verticalMotorTimescale);
}
}
// legacy support for 'MyAvatar::applyThrust()', which has always been implemented as a
@ -1752,6 +1767,14 @@ QString MyAvatar::getScriptedMotorFrame() const {
return frame;
}
QString MyAvatar::getScriptedMotorMode() const {
QString mode = "simple";
if (_scriptedMotorMode == SCRIPTED_MOTOR_DYNAMIC_MODE) {
mode = "dynamic";
}
return mode;
}
void MyAvatar::setScriptedMotorVelocity(const glm::vec3& velocity) {
float MAX_SCRIPTED_MOTOR_SPEED = 500.0f;
_scriptedMotorVelocity = velocity;
@ -1778,6 +1801,14 @@ void MyAvatar::setScriptedMotorFrame(QString frame) {
}
}
void MyAvatar::setScriptedMotorMode(QString mode) {
if (mode.toLower() == "simple") {
_scriptedMotorMode = SCRIPTED_MOTOR_SIMPLE_MODE;
} else if (mode.toLower() == "dynamic") {
_scriptedMotorMode = SCRIPTED_MOTOR_DYNAMIC_MODE;
}
}
void MyAvatar::clearScriptableSettings() {
_scriptedMotorVelocity = Vectors::ZERO;
_scriptedMotorTimescale = DEFAULT_SCRIPTED_MOTOR_TIMESCALE;
@ -1819,12 +1850,6 @@ void MyAvatar::attach(const QString& modelURL, const QString& jointName,
Avatar::attach(modelURL, jointName, translation, rotation, scale, isSoft, allowDuplicates, useSaved);
}
void MyAvatar::setVisibleInSceneIfReady(Model* model, const render::ScenePointer& scene, bool visible) {
if (model->isActive() && model->isRenderable()) {
model->setVisibleInScene(visible, scene, render::ItemKey::TAG_BITS_NONE, true);
}
}
void MyAvatar::initHeadBones() {
int neckJointIndex = -1;
if (_skeletonModel->isLoaded()) {
@ -2014,8 +2039,11 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
_attachmentData[i].jointName.compare("RightEye", Qt::CaseInsensitive) == 0 ||
_attachmentData[i].jointName.compare("HeadTop_End", Qt::CaseInsensitive) == 0 ||
_attachmentData[i].jointName.compare("Face", Qt::CaseInsensitive) == 0) {
_attachmentModels[i]->setVisibleInScene(shouldDrawHead, qApp->getMain3DScene(),
render::ItemKey::TAG_BITS_NONE, true);
_attachmentModels[i]->setCanCastShadow(shouldDrawHead, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true);
}
}
}

View file

@ -69,6 +69,7 @@ class MyAvatar : public Avatar {
* @property motorTimescale {float} Specifies how quickly the avatar should accelerate to meet the motorVelocity,
* smaller values will result in higher acceleration.
* @property motorReferenceFrame {string} Reference frame of the motorVelocity, must be one of the following: "avatar", "camera", "world"
* @property motorMode {string} Type of scripted motor behavior, "simple" = use motorTimescale property (default mode) and "dynamic" = use action motor's timescales
* @property collisionSoundURL {string} Specifies the sound to play when the avatar experiences a collision.
* You can provide a mono or stereo 16-bit WAV file running at either 24 Khz or 48 Khz.
* The latter is downsampled by the audio mixer, so all audio effectively plays back at a 24 Khz sample rate.
@ -124,6 +125,7 @@ class MyAvatar : public Avatar {
Q_PROPERTY(glm::vec3 motorVelocity READ getScriptedMotorVelocity WRITE setScriptedMotorVelocity)
Q_PROPERTY(float motorTimescale READ getScriptedMotorTimescale WRITE setScriptedMotorTimescale)
Q_PROPERTY(QString motorReferenceFrame READ getScriptedMotorFrame WRITE setScriptedMotorFrame)
Q_PROPERTY(QString motorMode READ getScriptedMotorMode WRITE setScriptedMotorMode)
Q_PROPERTY(QString collisionSoundURL READ getCollisionSoundURL WRITE setCollisionSoundURL)
Q_PROPERTY(AudioListenerMode audioListenerMode READ getAudioListenerMode WRITE setAudioListenerMode)
Q_PROPERTY(glm::vec3 customListenPosition READ getCustomListenPosition WRITE setCustomListenPosition)
@ -662,9 +664,11 @@ private:
glm::vec3 getScriptedMotorVelocity() const { return _scriptedMotorVelocity; }
float getScriptedMotorTimescale() const { return _scriptedMotorTimescale; }
QString getScriptedMotorFrame() const;
QString getScriptedMotorMode() const;
void setScriptedMotorVelocity(const glm::vec3& velocity);
void setScriptedMotorTimescale(float timescale);
void setScriptedMotorFrame(QString frame);
void setScriptedMotorMode(QString mode);
virtual void attach(const QString& modelURL, const QString& jointName = QString(),
const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(),
float scale = 1.0f, bool isSoft = false,
@ -677,8 +681,6 @@ private:
// These are made private for MyAvatar so that you will use the "use" methods instead
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
void setVisibleInSceneIfReady(Model* model, const render::ScenePointer& scene, bool visiblity);
virtual void updatePalms() override {}
void lateUpdatePalms();
@ -706,6 +708,7 @@ private:
glm::vec3 _scriptedMotorVelocity; // target local-frame velocity of avatar (analog script)
float _scriptedMotorTimescale; // timescale for avatar to achieve its target velocity
int _scriptedMotorFrame;
int _scriptedMotorMode;
quint32 _motionBehaviors;
QString _collisionSoundURL;

View file

@ -10,6 +10,7 @@
//
#include "QmlCommerce.h"
#include "CommerceLogging.h"
#include "Application.h"
#include "DependencyManager.h"
#include "Ledger.h"
@ -17,6 +18,9 @@
#include <AccountManager.h>
#include <Application.h>
#include <UserActivityLogger.h>
#include <ScriptEngines.h>
#include <ui/TabletScriptingInterface.h>
#include "scripting/HMDScriptingInterface.h"
QmlCommerce::QmlCommerce() {
auto ledger = DependencyManager::get<Ledger>();
@ -40,6 +44,8 @@ QmlCommerce::QmlCommerce() {
connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() {
setPassphrase("");
});
_appsPath = PathUtils::getAppDataPath() + "Apps/";
}
void QmlCommerce::getWalletStatus() {
@ -183,3 +189,148 @@ void QmlCommerce::alreadyOwned(const QString& marketplaceId) {
auto ledger = DependencyManager::get<Ledger>();
ledger->alreadyOwned(marketplaceId);
}
QString QmlCommerce::getInstalledApps() {
QString installedAppsFromMarketplace;
QStringList runningScripts = DependencyManager::get<ScriptEngines>()->getRunningScripts();
QDir directory(_appsPath);
QStringList apps = directory.entryList(QStringList("*.app.json"));
foreach(QString appFileName, apps) {
installedAppsFromMarketplace += appFileName;
installedAppsFromMarketplace += ",";
QFile appFile(_appsPath + appFileName);
if (appFile.open(QIODevice::ReadOnly)) {
QJsonDocument appFileJsonDocument = QJsonDocument::fromJson(appFile.readAll());
appFile.close();
QJsonObject appFileJsonObject = appFileJsonDocument.object();
QString scriptURL = appFileJsonObject["scriptURL"].toString();
// If the script .app.json is on the user's local disk but the associated script isn't running
// for some reason, start that script again.
if (!runningScripts.contains(scriptURL)) {
if ((DependencyManager::get<ScriptEngines>()->loadScript(scriptURL.trimmed())).isNull()) {
qCDebug(commerce) << "Couldn't start script while checking installed apps.";
}
}
} else {
qCDebug(commerce) << "Couldn't open local .app.json file for reading.";
}
}
return installedAppsFromMarketplace;
}
bool QmlCommerce::installApp(const QString& itemHref) {
if (!QDir(_appsPath).exists()) {
if (!QDir().mkdir(_appsPath)) {
qCDebug(commerce) << "Couldn't make _appsPath directory.";
return false;
}
}
QUrl appHref(itemHref);
auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(this, appHref);
if (!request) {
qCDebug(commerce) << "Couldn't create resource request for app.";
return false;
}
connect(request, &ResourceRequest::finished, this, [=]() {
if (request->getResult() != ResourceRequest::Success) {
qCDebug(commerce) << "Failed to get .app.json file from remote.";
return false;
}
// Copy the .app.json to the apps directory inside %AppData%/High Fidelity/Interface
auto requestData = request->getData();
QFile appFile(_appsPath + "/" + appHref.fileName());
if (!appFile.open(QIODevice::WriteOnly)) {
qCDebug(commerce) << "Couldn't open local .app.json file for creation.";
return false;
}
if (appFile.write(requestData) == -1) {
qCDebug(commerce) << "Couldn't write to local .app.json file.";
return false;
}
// Close the file
appFile.close();
// Read from the returned datastream to know what .js to add to Running Scripts
QJsonDocument appFileJsonDocument = QJsonDocument::fromJson(requestData);
QJsonObject appFileJsonObject = appFileJsonDocument.object();
QString scriptUrl = appFileJsonObject["scriptURL"].toString();
if ((DependencyManager::get<ScriptEngines>()->loadScript(scriptUrl.trimmed())).isNull()) {
qCDebug(commerce) << "Couldn't load script.";
return false;
}
emit appInstalled(itemHref);
return true;
});
request->send();
return true;
}
bool QmlCommerce::uninstallApp(const QString& itemHref) {
QUrl appHref(itemHref);
// Read from the file to know what .js script to stop
QFile appFile(_appsPath + "/" + appHref.fileName());
if (!appFile.open(QIODevice::ReadOnly)) {
qCDebug(commerce) << "Couldn't open local .app.json file for deletion.";
return false;
}
QJsonDocument appFileJsonDocument = QJsonDocument::fromJson(appFile.readAll());
QJsonObject appFileJsonObject = appFileJsonDocument.object();
QString scriptUrl = appFileJsonObject["scriptURL"].toString();
if (!DependencyManager::get<ScriptEngines>()->stopScript(scriptUrl.trimmed(), false)) {
qCDebug(commerce) << "Couldn't stop script.";
return false;
}
// Delete the .app.json from the filesystem
// remove() closes the file first.
if (!appFile.remove()) {
qCDebug(commerce) << "Couldn't delete local .app.json file.";
return false;
}
emit appUninstalled(itemHref);
return true;
}
bool QmlCommerce::openApp(const QString& itemHref) {
QUrl appHref(itemHref);
// Read from the file to know what .html or .qml document to open
QFile appFile(_appsPath + "/" + appHref.fileName());
if (!appFile.open(QIODevice::ReadOnly)) {
qCDebug(commerce) << "Couldn't open local .app.json file.";
return false;
}
QJsonDocument appFileJsonDocument = QJsonDocument::fromJson(appFile.readAll());
QJsonObject appFileJsonObject = appFileJsonDocument.object();
QString homeUrl = appFileJsonObject["homeURL"].toString();
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
if (homeUrl.contains(".qml", Qt::CaseInsensitive)) {
tablet->loadQMLSource(homeUrl);
} else if (homeUrl.contains(".html", Qt::CaseInsensitive)) {
tablet->gotoWebScreen(homeUrl);
} else {
qCDebug(commerce) << "Attempted to open unknown type of homeURL!";
return false;
}
DependencyManager::get<HMDScriptingInterface>()->openTablet();
return true;
}

View file

@ -51,6 +51,9 @@ signals:
void contentSetChanged(const QString& contentSetHref);
void appInstalled(const QString& appHref);
void appUninstalled(const QString& appHref);
protected:
Q_INVOKABLE void getWalletStatus();
@ -76,8 +79,15 @@ protected:
Q_INVOKABLE void transferHfcToNode(const QString& nodeID, const int& amount, const QString& optionalMessage);
Q_INVOKABLE void transferHfcToUsername(const QString& username, const int& amount, const QString& optionalMessage);
Q_INVOKABLE void replaceContentSet(const QString& itemHref);
Q_INVOKABLE QString getInstalledApps();
Q_INVOKABLE bool installApp(const QString& appHref);
Q_INVOKABLE bool uninstallApp(const QString& appHref);
Q_INVOKABLE bool openApp(const QString& appHref);
private:
QString _appsPath;
};
#endif // hifi_QmlCommerce_h

View file

@ -38,6 +38,7 @@ extern "C" {
#endif
int main(int argc, const char* argv[]) {
setupHifiApplication(BuildInfo::INTERFACE_NAME);
#ifdef Q_OS_LINUX
QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
@ -51,17 +52,9 @@ int main(int argc, const char* argv[]) {
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
#endif
disableQtBearerPoll(); // Fixes wifi ping spikes
QElapsedTimer startupTime;
startupTime.start();
// Set application infos
QCoreApplication::setApplicationName(BuildInfo::INTERFACE_NAME);
QCoreApplication::setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION);
QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN);
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
Setting::init();
// Instance UserActivityLogger now that the settings are loaded

View file

@ -17,6 +17,8 @@
#include "SceneScriptingInterface.h"
OctreePacketProcessor::OctreePacketProcessor() {
setObjectName("Octree Packet Processor");
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerDirectListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },

View file

@ -305,6 +305,7 @@ public slots:
* {@link Window.stillSnapshotTaken|stillSnapshotTaken} is emitted; when a still image plus moving images are captured,
* {@link Window.processingGifStarted|processingGifStarted} and {@link Window.processingGifCompleted|processingGifCompleted}
* are emitted. The path to store the snapshots and the length of the animated GIF to capture are specified in Settings >
* NOTE: to provide a non-default value - all previous parameters must be provided.
* General > Snapshots.
* @function Window.takeSnapshot
* @param {boolean} notify=true - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken}
@ -314,8 +315,10 @@ public slots:
* @param {number} aspectRatio=0 - The width/height ratio of the snapshot required. If the value is <code>0</code> the
* full resolution is used (window dimensions in desktop mode; HMD display dimensions in HMD mode), otherwise one of the
* dimensions is adjusted in order to match the aspect ratio.
* @param {string} filename=QString() - If this value is not null then the image will be saved to this filename, with an appended ",jpg".
* otherwise, the image will be saved as 'hifi-snap-by-<user name>-YYYY-MM-DD_HH-MM-SS'
* @param {string} filename="" - If this parameter is not given, the image will be saved as 'hifi-snap-by-<user name>-YYYY-MM-DD_HH-MM-SS'.
* If this parameter is <code>""</code> then the image will be saved as ".jpg".
* Otherwise, the image will be saved to this filename, with an appended ".jpg".
*
* @example <caption>Using the snapshot function and signals.</caption>
* function onStillSnapshotTaken(path, notify) {
* print("Still snapshot taken: " + path);
@ -337,15 +340,18 @@ public slots:
* var notify = true;
* var animated = true;
* var aspect = 1920 / 1080;
* var filename = QString();
* var filename = "";
* Window.takeSnapshot(notify, animated, aspect, filename);
*/
void takeSnapshot(bool notify = true, bool includeAnimated = false, float aspectRatio = 0.0f, const QString& filename = QString());
/**jsdoc
* Takes a still snapshot of the current view from the secondary camera that can be set up through the {@link Render} API.
* NOTE: to provide a non-default value - all previous parameters must be provided.
* @function Window.takeSecondaryCameraSnapshot
* @param {string} filename=QString() - If this value is not null then the image will be saved to this filename, with an appended ".jpg"
* @param {string} filename="" - If this parameter is not given, the image will be saved as 'hifi-snap-by-<user name>-YYYY-MM-DD_HH-MM-SS'.
* If this parameter is <code>""</code> then the image will be saved as ".jpg".
* Otherwise, the image will be saved to this filename, with an appended ".jpg".
*
* var filename = QString();
*/

View file

@ -31,6 +31,8 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
_defaultEyeModelPosition(glm::vec3(0.0f, 0.0f, 0.0f)),
_headClipDistance(DEFAULT_NEAR_CLIP)
{
// SkeletonModels, and by extention Avatars, use Dual Quaternion skinning.
_useDualQuaternionSkinning = true;
assert(_owningAvatar);
}

View file

@ -164,7 +164,12 @@ ItemKey EntityRenderer::getKey() {
return ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1);
}
return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1);
// This allows shapes to cast shadows
if (_canCastShadow) {
return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1).withShadowCaster();
} else {
return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1);
}
}
uint32_t EntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) {
@ -377,6 +382,7 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa
_moving = entity->isMovingRelativeToParent();
_visible = entity->getVisible();
_canCastShadow = entity->getCanCastShadow();
_cauterized = entity->getCauterized();
_needsRenderUpdate = false;
});

View file

@ -126,6 +126,7 @@ protected:
bool _isFading{ _entitiesShouldFadeFunction() };
bool _prevIsTransparent { false };
bool _visible { false };
bool _canCastShadow { false };
bool _cauterized { false };
bool _moving { false };
bool _needsRenderUpdate { false };

View file

@ -133,6 +133,9 @@ void RenderableModelEntityItem::doInitialModelSimulation() {
model->setRotation(getWorldOrientation());
model->setTranslation(getWorldPosition());
glm::vec3 scale = model->getScale();
model->setUseDualQuaternionSkinning(!isNonUniformScale(scale));
if (_needsInitialSimulation) {
model->simulate(0.0f);
_needsInitialSimulation = false;
@ -243,6 +246,8 @@ void RenderableModelEntityItem::updateModelBounds() {
}
if (updateRenderItems) {
glm::vec3 scale = model->getScale();
model->setUseDualQuaternionSkinning(!isNonUniformScale(scale));
model->updateRenderItems();
}
}
@ -1356,6 +1361,10 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
}
// TODO? early exit here when not visible?
if (model->canCastShadow() != _canCastShadow) {
model->setCanCastShadow(_canCastShadow, scene, viewTaskBits, false);
}
if (_needsCollisionGeometryUpdate) {
setCollisionMeshKey(entity->getCollisionMeshKey());
_needsCollisionGeometryUpdate = false;
@ -1500,4 +1509,4 @@ void ModelEntityRenderer::processMaterials() {
material.pop();
}
}
}
}

View file

@ -330,6 +330,7 @@ void ZoneEntityRenderer::updateKeySunFromEntity(const TypedEntityPointer& entity
sunLight->setColor(ColorUtils::toVec3(_keyLightProperties.getColor()));
sunLight->setIntensity(_keyLightProperties.getIntensity());
sunLight->setDirection(entity->getTransform().getRotation() * _keyLightProperties.getDirection());
sunLight->setCastShadows(_keyLightProperties.getCastShadows());
}
void ZoneEntityRenderer::updateAmbientLightFromEntity(const TypedEntityPointer& entity) {

View file

@ -91,6 +91,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
requestedProperties += PROP_REGISTRATION_POINT;
requestedProperties += PROP_ANGULAR_DAMPING;
requestedProperties += PROP_VISIBLE;
requestedProperties += PROP_CAN_CAST_SHADOW;
requestedProperties += PROP_COLLISIONLESS;
requestedProperties += PROP_COLLISION_MASK;
requestedProperties += PROP_DYNAMIC;
@ -249,6 +250,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, getRegistrationPoint());
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, getAngularDamping());
APPEND_ENTITY_PROPERTY(PROP_VISIBLE, getVisible());
APPEND_ENTITY_PROPERTY(PROP_CAN_CAST_SHADOW, getCanCastShadow());
APPEND_ENTITY_PROPERTY(PROP_COLLISIONLESS, getCollisionless());
APPEND_ENTITY_PROPERTY(PROP_COLLISION_MASK, getCollisionMask());
APPEND_ENTITY_PROPERTY(PROP_DYNAMIC, getDynamic());
@ -799,6 +801,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, setAngularDamping);
READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, setVisible);
READ_ENTITY_PROPERTY(PROP_CAN_CAST_SHADOW, bool, setCanCastShadow);
READ_ENTITY_PROPERTY(PROP_COLLISIONLESS, bool, setCollisionless);
READ_ENTITY_PROPERTY(PROP_COLLISION_MASK, uint8_t, setCollisionMask);
READ_ENTITY_PROPERTY(PROP_DYNAMIC, bool, setDynamic);
@ -1234,6 +1237,7 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper
COPY_ENTITY_PROPERTY_TO_PROPERTIES(angularDamping, getAngularDamping);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(localRenderAlpha, getLocalRenderAlpha);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(visible, getVisible);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(canCastShadow, getCanCastShadow);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionless, getCollisionless);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionMask, getCollisionMask);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(dynamic, getDynamic);
@ -1346,6 +1350,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionSoundURL, setCollisionSoundURL);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(localRenderAlpha, setLocalRenderAlpha);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(visible, setVisible);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(canCastShadow, setCanCastShadow);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData);
// Certifiable Properties
@ -2723,6 +2728,28 @@ void EntityItem::setVisible(bool value) {
}
}
bool EntityItem::getCanCastShadow() const {
bool result;
withReadLock([&] {
result = _canCastShadow;
});
return result;
}
void EntityItem::setCanCastShadow(bool value) {
bool changed = false;
withWriteLock([&] {
if (_canCastShadow != value) {
changed = true;
_canCastShadow = value;
}
});
if (changed) {
emit requestRenderUpdate();
}
}
bool EntityItem::isChildOfMyAvatar() const {
QUuid ancestorID = findAncestorOfType(NestableType::Avatar);
return !ancestorID.isNull() && (ancestorID == Physics::getSessionUUID() || ancestorID == AVATAR_SELF_ID);

View file

@ -274,6 +274,10 @@ public:
bool getVisible() const;
void setVisible(bool value);
bool getCanCastShadow() const;
void setCanCastShadow(bool value);
inline bool isVisible() const { return getVisible(); }
inline bool isInvisible() const { return !getVisible(); }
@ -551,6 +555,7 @@ protected:
glm::vec3 _registrationPoint { ENTITY_ITEM_DEFAULT_REGISTRATION_POINT };
float _angularDamping { ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING };
bool _visible { ENTITY_ITEM_DEFAULT_VISIBLE };
bool _canCastShadow{ ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW };
bool _collisionless { ENTITY_ITEM_DEFAULT_COLLISIONLESS };
uint8_t _collisionMask { ENTITY_COLLISION_MASK_DEFAULT };
bool _dynamic { ENTITY_ITEM_DEFAULT_DYNAMIC };

View file

@ -316,6 +316,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL);
CHECK_PROPERTY_CHANGE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL);
CHECK_PROPERTY_CHANGE(PROP_VISIBLE, visible);
CHECK_PROPERTY_CHANGE(PROP_CAN_CAST_SHADOW, canCastShadow);
CHECK_PROPERTY_CHANGE(PROP_REGISTRATION_POINT, registrationPoint);
CHECK_PROPERTY_CHANGE(PROP_ANGULAR_VELOCITY, angularVelocity);
CHECK_PROPERTY_CHANGE(PROP_ANGULAR_DAMPING, angularDamping);
@ -490,6 +491,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ANGULAR_VELOCITY, angularVelocity);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ANGULAR_DAMPING, angularDamping);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VISIBLE, visible);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CAN_CAST_SHADOW, canCastShadow);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISIONLESS, collisionless);
COPY_PROXY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_COLLISIONLESS, collisionless, ignoreForCollisions, getCollisionless()); // legacy support
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISION_MASK, collisionMask);
@ -751,6 +753,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
COPY_PROPERTY_FROM_QSCRIPTVALUE(angularVelocity, glmVec3, setAngularVelocity);
COPY_PROPERTY_FROM_QSCRIPTVALUE(angularDamping, float, setAngularDamping);
COPY_PROPERTY_FROM_QSCRIPTVALUE(visible, bool, setVisible);
COPY_PROPERTY_FROM_QSCRIPTVALUE(canCastShadow, bool, setCanCastShadow);
COPY_PROPERTY_FROM_QSCRIPTVALUE(color, xColor, setColor);
COPY_PROPERTY_FROM_QSCRIPTVALUE(colorSpread, xColor, setColorSpread);
COPY_PROPERTY_FROM_QSCRIPTVALUE(colorStart, xColor, setColorStart);
@ -922,6 +925,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
COPY_PROPERTY_IF_CHANGED(angularVelocity);
COPY_PROPERTY_IF_CHANGED(angularDamping);
COPY_PROPERTY_IF_CHANGED(visible);
COPY_PROPERTY_IF_CHANGED(canCastShadow);
COPY_PROPERTY_IF_CHANGED(color);
COPY_PROPERTY_IF_CHANGED(colorSpread);
COPY_PROPERTY_IF_CHANGED(colorStart);
@ -1094,6 +1098,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
std::call_once(initMap, [](){
ADD_PROPERTY_TO_MAP(PROP_VISIBLE, Visible, visible, bool);
ADD_PROPERTY_TO_MAP(PROP_CAN_CAST_SHADOW, CanCastShadow, canCastShadow, bool);
ADD_PROPERTY_TO_MAP(PROP_POSITION, Position, position, glm::vec3);
ADD_PROPERTY_TO_MAP(PROP_DIMENSIONS, Dimensions, dimensions, glm::vec3);
ADD_PROPERTY_TO_MAP(PROP_ROTATION, Rotation, rotation, glm::quat);
@ -1187,6 +1192,8 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_COLOR, KeyLightColor, keyLightColor, xColor);
ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_INTENSITY, KeyLightIntensity, keyLightIntensity, float);
ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_DIRECTION, KeyLightDirection, keyLightDirection, glm::vec3);
ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_CAST_SHADOW, KeyLightCastShadows, keyLightCastShadows, bool);
ADD_PROPERTY_TO_MAP(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3);
ADD_PROPERTY_TO_MAP(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray);
ADD_PROPERTY_TO_MAP(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t);
@ -1412,6 +1419,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, properties.getAngularVelocity());
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, properties.getAngularDamping());
APPEND_ENTITY_PROPERTY(PROP_VISIBLE, properties.getVisible());
APPEND_ENTITY_PROPERTY(PROP_CAN_CAST_SHADOW, properties.getCanCastShadow());
APPEND_ENTITY_PROPERTY(PROP_COLLISIONLESS, properties.getCollisionless());
APPEND_ENTITY_PROPERTY(PROP_COLLISION_MASK, properties.getCollisionMask());
APPEND_ENTITY_PROPERTY(PROP_DYNAMIC, properties.getDynamic());
@ -1784,6 +1792,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_VELOCITY, glm::vec3, setAngularVelocity);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_DAMPING, float, setAngularDamping);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VISIBLE, bool, setVisible);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CAN_CAST_SHADOW, bool, setCanCastShadow);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISIONLESS, bool, setCollisionless);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_MASK, uint8_t, setCollisionMask);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DYNAMIC, bool, setDynamic);
@ -2053,6 +2062,7 @@ void EntityItemProperties::markAllChanged() {
_angularDampingChanged = true;
_nameChanged = true;
_visibleChanged = true;
_canCastShadowChanged = true;
_colorChanged = true;
_alphaChanged = true;
_modelURLChanged = true;
@ -2250,6 +2260,9 @@ QList<QString> EntityItemProperties::listChangedProperties() {
if (visibleChanged()) {
out += "visible";
}
if (canCastShadowChanged()) {
out += "canCastShadow";
}
if (rotationChanged()) {
out += "rotation";
}

View file

@ -117,6 +117,7 @@ public:
// bool _fooChanged { false };
DEFINE_PROPERTY(PROP_VISIBLE, Visible, visible, bool, ENTITY_ITEM_DEFAULT_VISIBLE);
DEFINE_PROPERTY(PROP_CAN_CAST_SHADOW, CanCastShadow, canCastShadow, bool, ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW);
DEFINE_PROPERTY_REF_WITH_SETTER(PROP_POSITION, Position, position, glm::vec3, ENTITY_ITEM_ZERO_VEC3);
DEFINE_PROPERTY_REF(PROP_DIMENSIONS, Dimensions, dimensions, glm::vec3, ENTITY_ITEM_DEFAULT_DIMENSIONS);
DEFINE_PROPERTY_REF(PROP_ROTATION, Rotation, rotation, glm::quat, ENTITY_ITEM_DEFAULT_ROTATION);
@ -426,6 +427,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Velocity, velocity, "in meters");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Name, name, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Visible, visible, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CanCastShadow, canCastShadow, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Rotation, rotation, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Density, density, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Gravity, gravity, "");

View file

@ -46,6 +46,7 @@ const quint32 ENTITY_ITEM_DEFAULT_STATIC_CERTIFICATE_VERSION = 0;
const float ENTITY_ITEM_DEFAULT_ALPHA = 1.0f;
const float ENTITY_ITEM_DEFAULT_LOCAL_RENDER_ALPHA = 1.0f;
const bool ENTITY_ITEM_DEFAULT_VISIBLE = true;
const bool ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW { true };
const QString ENTITY_ITEM_DEFAULT_SCRIPT = QString("");
const quint64 ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP = 0;

View file

@ -20,6 +20,7 @@ enum EntityPropertyList {
// these properties are supported by the EntityItem base class
PROP_VISIBLE,
PROP_CAN_CAST_SHADOW,
PROP_POSITION,
PROP_DIMENSIONS,
PROP_ROTATION,
@ -205,6 +206,11 @@ enum EntityPropertyList {
PROP_HAZE_MODE,
PROP_KEYLIGHT_COLOR,
PROP_KEYLIGHT_INTENSITY,
PROP_KEYLIGHT_DIRECTION,
PROP_KEYLIGHT_CAST_SHADOW,
PROP_HAZE_RANGE,
PROP_HAZE_COLOR,
PROP_HAZE_GLARE_COLOR,
@ -254,10 +260,6 @@ enum EntityPropertyList {
// Aliases/Piggyback properties for Zones. These properties intentionally reuse the enum values for
// other properties which will never overlap with each other. We do this so that we don't have to expand
// the size of the properties bitflags mask
PROP_KEYLIGHT_COLOR = PROP_COLOR,
PROP_KEYLIGHT_INTENSITY = PROP_INTENSITY,
PROP_KEYLIGHT_DIRECTION = PROP_EXPONENT,
PROP_SKYBOX_COLOR = PROP_ANIMATION_URL,
PROP_SKYBOX_URL = PROP_ANIMATION_FPS,

View file

@ -97,6 +97,7 @@ EntityItemPointer EntityTypes::constructEntityItem(EntityType entityType, const
auto mutableProperties = properties;
mutableProperties.markAllChanged();
newEntityItem = factory(entityID, mutableProperties);
newEntityItem->moveToThread(qApp->thread());
}
return newEntityItem;
}

View file

@ -21,6 +21,7 @@ const xColor KeyLightPropertyGroup::DEFAULT_KEYLIGHT_COLOR = { 255, 255, 255 };
const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_INTENSITY = 1.0f;
const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY = 0.5f;
const glm::vec3 KeyLightPropertyGroup::DEFAULT_KEYLIGHT_DIRECTION = { 0.0f, -1.0f, 0.0f };
const bool KeyLightPropertyGroup::DEFAULT_KEYLIGHT_CAST_SHADOWS { false };
void KeyLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties,
QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const {
@ -28,23 +29,27 @@ void KeyLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desired
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_COLOR, KeyLight, keyLight, Color, color);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_INTENSITY, KeyLight, keyLight, Intensity, intensity);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_DIRECTION, KeyLight, keyLight, Direction, direction);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_CAST_SHADOW, KeyLight, keyLight, CastShadows, castShadows);
}
void KeyLightPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) {
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, color, xColor, setColor);
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, intensity, float, setIntensity);
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, direction, glmVec3, setDirection);
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, castShadows, bool, setCastShadows);
// legacy property support
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightColor, xColor, setColor, getColor);
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightIntensity, float, setIntensity, getIntensity);
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightDirection, glmVec3, setDirection, getDirection);
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightCastShadows, bool, setCastShadows, getCastShadows);
}
void KeyLightPropertyGroup::merge(const KeyLightPropertyGroup& other) {
COPY_PROPERTY_IF_CHANGED(color);
COPY_PROPERTY_IF_CHANGED(intensity);
COPY_PROPERTY_IF_CHANGED(direction);
COPY_PROPERTY_IF_CHANGED(castShadows);
}
void KeyLightPropertyGroup::debugDump() const {
@ -52,6 +57,7 @@ void KeyLightPropertyGroup::debugDump() const {
qCDebug(entities) << " color:" << getColor(); // << "," << getColor()[1] << "," << getColor()[2];
qCDebug(entities) << " intensity:" << getIntensity();
qCDebug(entities) << " direction:" << getDirection();
qCDebug(entities) << " castShadows:" << getCastShadows();
}
void KeyLightPropertyGroup::listChangedProperties(QList<QString>& out) {
@ -64,6 +70,9 @@ void KeyLightPropertyGroup::listChangedProperties(QList<QString>& out) {
if (directionChanged()) {
out << "keyLight-direction";
}
if (castShadowsChanged()) {
out << "keyLight-castShadows";
}
}
bool KeyLightPropertyGroup::appendToEditPacket(OctreePacketData* packetData,
@ -71,19 +80,21 @@ bool KeyLightPropertyGroup::appendToEditPacket(OctreePacketData* packetData,
EntityPropertyFlags& propertyFlags,
EntityPropertyFlags& propertiesDidntFit,
int& propertyCount,
OctreeElement::AppendState& appendState) const {
OctreeElement::AppendState& appendState) const
{
bool successPropertyFits = true;
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, getColor());
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, getIntensity());
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, getDirection());
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_CAST_SHADOW, getCastShadows());
return true;
}
bool KeyLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt,
int& processedBytes) {
int& processedBytes) {
int bytesRead = 0;
bool overwriteLocalData = true;
@ -92,11 +103,13 @@ bool KeyLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFl
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, xColor, setColor);
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, float, setIntensity);
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, glm::vec3, setDirection);
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_CAST_SHADOW, bool, setCastShadows);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_COLOR, Color);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_INTENSITY, Intensity);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_DIRECTION, Direction);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_CAST_SHADOW, CastShadows);
processedBytes += bytesRead;
Q_UNUSED(somethingChanged);
@ -108,6 +121,7 @@ void KeyLightPropertyGroup::markAllChanged() {
_colorChanged = true;
_intensityChanged = true;
_directionChanged = true;
_castShadowsChanged = true;
}
EntityPropertyFlags KeyLightPropertyGroup::getChangedProperties() const {
@ -116,7 +130,8 @@ EntityPropertyFlags KeyLightPropertyGroup::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_COLOR, color);
CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_INTENSITY, intensity);
CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_DIRECTION, direction);
CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_CAST_SHADOW, castShadows);
return changedProperties;
}
@ -124,6 +139,7 @@ void KeyLightPropertyGroup::getProperties(EntityItemProperties& properties) cons
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, Color, getColor);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, Intensity, getIntensity);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, Direction, getDirection);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, CastShadows, getCastShadows);
}
bool KeyLightPropertyGroup::setProperties(const EntityItemProperties& properties) {
@ -132,6 +148,7 @@ bool KeyLightPropertyGroup::setProperties(const EntityItemProperties& properties
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, Color, color, setColor);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, Intensity, intensity, setIntensity);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, Direction, direction, setDirection);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, CastShadows, castShadows, setCastShadows);
return somethingChanged;
}
@ -142,6 +159,7 @@ EntityPropertyFlags KeyLightPropertyGroup::getEntityProperties(EncodeBitstreamPa
requestedProperties += PROP_KEYLIGHT_COLOR;
requestedProperties += PROP_KEYLIGHT_INTENSITY;
requestedProperties += PROP_KEYLIGHT_DIRECTION;
requestedProperties += PROP_KEYLIGHT_CAST_SHADOW;
return requestedProperties;
}
@ -159,6 +177,7 @@ void KeyLightPropertyGroup::appendSubclassData(OctreePacketData* packetData, Enc
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, getColor());
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, getIntensity());
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, getDirection());
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_CAST_SHADOW, getCastShadows());
}
int KeyLightPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
@ -172,6 +191,7 @@ int KeyLightPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char*
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, xColor, setColor);
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, float, setIntensity);
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, glm::vec3, setDirection);
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_CAST_SHADOW, bool, setCastShadows);
return bytesRead;
}

View file

@ -78,10 +78,12 @@ public:
static const float DEFAULT_KEYLIGHT_INTENSITY;
static const float DEFAULT_KEYLIGHT_AMBIENT_INTENSITY;
static const glm::vec3 DEFAULT_KEYLIGHT_DIRECTION;
static const bool DEFAULT_KEYLIGHT_CAST_SHADOWS;
DEFINE_PROPERTY_REF(PROP_KEYLIGHT_COLOR, Color, color, xColor, DEFAULT_KEYLIGHT_COLOR);
DEFINE_PROPERTY(PROP_KEYLIGHT_INTENSITY, Intensity, intensity, float, DEFAULT_KEYLIGHT_INTENSITY);
DEFINE_PROPERTY_REF(PROP_KEYLIGHT_DIRECTION, Direction, direction, glm::vec3, DEFAULT_KEYLIGHT_DIRECTION);
DEFINE_PROPERTY(PROP_KEYLIGHT_CAST_SHADOW, CastShadows, castShadows, bool, DEFAULT_KEYLIGHT_CAST_SHADOWS);
};
#endif // hifi_KeyLightPropertyGroup_h

View file

@ -53,6 +53,7 @@ void ModelEntityItem::setTextures(const QString& textures) {
EntityItemProperties ModelEntityItem::getProperties(EntityPropertyFlags desiredProperties) const {
EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(modelURL, getModelURL);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL);
@ -64,6 +65,7 @@ EntityItemProperties ModelEntityItem::getProperties(EntityPropertyFlags desiredP
COPY_ENTITY_PROPERTY_TO_PROPERTIES(jointTranslations, getJointTranslations);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(relayParentJoints, getRelayParentJoints);
_animationProperties.getProperties(properties);
return properties;
}
@ -191,8 +193,6 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, getJointTranslations());
}
// added update function back for property fix
void ModelEntityItem::update(const quint64& now) {
@ -571,15 +571,16 @@ QVector<bool> ModelEntityItem::getJointTranslationsSet() const {
return result;
}
xColor ModelEntityItem::getXColor() const {
xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color;
}
bool ModelEntityItem::hasModel() const {
return resultWithReadLock<bool>([&] {
return !_modelURL.isEmpty();
});
}
bool ModelEntityItem::hasCompoundShapeURL() const {
return !_compoundShapeURL.get().isEmpty();
}
@ -659,7 +660,6 @@ bool ModelEntityItem::getAnimationLoop() const {
});
}
void ModelEntityItem::setAnimationHold(bool hold) {
withWriteLock([&] {
_animationProperties.setHold(hold);

View file

@ -92,6 +92,7 @@ EntityItemProperties ShapeEntityItem::getProperties(EntityPropertyFlags desiredP
EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class
properties.setColor(getXColor());
properties.setShape(entity::stringFromShape(getShape()));
return properties;
}
@ -166,6 +167,7 @@ EntityPropertyFlags ShapeEntityItem::getEntityProperties(EncodeBitstreamParams&
requestedProperties += PROP_SHAPE;
requestedProperties += PROP_COLOR;
requestedProperties += PROP_ALPHA;
return requestedProperties;
}
@ -368,4 +370,3 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) {
ShapeType ShapeEntityItem::getShapeType() const {
return _collisionShapeType;
}

View file

@ -114,6 +114,8 @@ protected:
//! ellipsoids.
ShapeType _collisionShapeType{ ShapeType::SHAPE_TYPE_ELLIPSOID };
bool _canCastShadow { ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW };
std::shared_ptr<graphics::Material> _material;
};

View file

@ -65,6 +65,14 @@ const Vec3& Light::getDirection() const {
return _lightSchemaBuffer->volume.direction;
}
void Light::setCastShadows(const bool castShadows) {
_castShadows = castShadows;
}
bool Light::getCastShadows() const {
return _castShadows;
}
void Light::setColor(const Color& color) {
_lightSchemaBuffer.edit().irradiance.color = color;
updateLightRadius();
@ -132,7 +140,6 @@ void Light::setSpotExponent(float exponent) {
_lightSchemaBuffer.edit().irradiance.falloffSpot = exponent;
}
void Light::setAmbientIntensity(float intensity) {
_ambientSchemaBuffer.edit().intensity = intensity;
}

View file

@ -103,6 +103,9 @@ public:
void setDirection(const Vec3& direction);
const Vec3& getDirection() const;
void setCastShadows(const bool castShadows);
bool getCastShadows() const;
void setOrientation(const Quat& orientation);
const glm::quat& getOrientation() const { return _transform.getRotation(); }
@ -191,6 +194,8 @@ protected:
void updateLightRadius();
bool _castShadows{ false };
};
typedef std::shared_ptr< Light > LightPointer;

View file

@ -30,7 +30,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::EntityEdit:
case PacketType::EntityData:
case PacketType::EntityPhysics:
return static_cast<PacketVersion>(EntityVersion::MaterialEntities);
return static_cast<PacketVersion>(EntityVersion::ShadowControl);
case PacketType::EntityQuery:
return static_cast<PacketVersion>(EntityQueryPacketVersion::RemovedJurisdictions);
case PacketType::AvatarIdentity:

View file

@ -218,7 +218,8 @@ enum class EntityVersion : PacketVersion {
ZoneLightInheritModes = 82,
ZoneStageRemoved,
SoftEntities,
MaterialEntities
MaterialEntities,
ShadowControl
};
enum class EntityScriptCallMethodVersion : PacketVersion {

View file

@ -328,14 +328,14 @@ void Socket::checkForReadyReadBackup() {
void Socket::readPendingDatagrams() {
int packetSizeWithHeader = -1;
while ((packetSizeWithHeader = _udpSocket.pendingDatagramSize()) != -1) {
while (_udpSocket.hasPendingDatagrams() && (packetSizeWithHeader = _udpSocket.pendingDatagramSize()) != -1) {
// we're reading a packet so re-start the readyRead backup timer
_readyReadBackupTimer->start();
// grab a time point we can mark as the receive time of this packet
auto receiveTime = p_high_resolution_clock::now();
// setup a HifiSockAddr to read into
HifiSockAddr senderSockAddr;

View file

@ -20,16 +20,32 @@ using namespace render;
CauterizedMeshPartPayload::CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform)
: ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {}
void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector<TransformType>& clusterTransforms, const std::vector<TransformType>& cauterizedClusterTransforms) {
ModelMeshPartPayload::updateClusterBuffer(clusterTransforms);
void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices,
const std::vector<glm::mat4>& cauterizedClusterMatrices) {
ModelMeshPartPayload::updateClusterBuffer(clusterMatrices);
if (cauterizedClusterTransforms.size() > 1) {
if (cauterizedClusterMatrices.size() > 1) {
if (!_cauterizedClusterBuffer) {
_cauterizedClusterBuffer = std::make_shared<gpu::Buffer>(cauterizedClusterTransforms.size() * sizeof(TransformType),
(const gpu::Byte*) cauterizedClusterTransforms.data());
_cauterizedClusterBuffer = std::make_shared<gpu::Buffer>(cauterizedClusterMatrices.size() * sizeof(glm::mat4),
(const gpu::Byte*) cauterizedClusterMatrices.data());
} else {
_cauterizedClusterBuffer->setSubData(0, cauterizedClusterTransforms.size() * sizeof(TransformType),
(const gpu::Byte*) cauterizedClusterTransforms.data());
_cauterizedClusterBuffer->setSubData(0, cauterizedClusterMatrices.size() * sizeof(glm::mat4),
(const gpu::Byte*) cauterizedClusterMatrices.data());
}
}
}
void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector<Model::TransformDualQuaternion>& clusterDualQuaternions,
const std::vector<Model::TransformDualQuaternion>& cauterizedClusterDualQuaternions) {
ModelMeshPartPayload::updateClusterBuffer(clusterDualQuaternions);
if (cauterizedClusterDualQuaternions.size() > 1) {
if (!_cauterizedClusterBuffer) {
_cauterizedClusterBuffer = std::make_shared<gpu::Buffer>(cauterizedClusterDualQuaternions.size() * sizeof(Model::TransformDualQuaternion),
(const gpu::Byte*) cauterizedClusterDualQuaternions.data());
} else {
_cauterizedClusterBuffer->setSubData(0, cauterizedClusterDualQuaternions.size() * sizeof(Model::TransformDualQuaternion),
(const gpu::Byte*) cauterizedClusterDualQuaternions.data());
}
}
}

View file

@ -15,13 +15,13 @@ class CauterizedMeshPartPayload : public ModelMeshPartPayload {
public:
CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform);
#if defined(SKIN_DQ)
using TransformType = Model::TransformDualQuaternion;
#else
using TransformType = glm::mat4;
#endif
// matrix palette skinning
void updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices,
const std::vector<glm::mat4>& cauterizedClusterMatrices);
void updateClusterBuffer(const std::vector<TransformType>& clusterTransforms, const std::vector<TransformType>& cauterizedClusterTransforms);
// dual quaternion skinning
void updateClusterBuffer(const std::vector<Model::TransformDualQuaternion>& clusterDualQuaternions,
const std::vector<Model::TransformDualQuaternion>& cauterizedClusterQuaternions);
void updateTransformForCauterizedMesh(const Transform& renderTransform);

View file

@ -35,8 +35,13 @@ bool CauterizedModel::updateGeometry() {
const FBXGeometry& fbxGeometry = getFBXGeometry();
foreach (const FBXMesh& mesh, fbxGeometry.meshes) {
Model::MeshState state;
state.clusterTransforms.resize(mesh.clusters.size());
_cauterizeMeshStates.append(state);
if (_useDualQuaternionSkinning) {
state.clusterDualQuaternions.resize(mesh.clusters.size());
_cauterizeMeshStates.append(state);
} else {
state.clusterMatrices.resize(mesh.clusters.size());
_cauterizeMeshStates.append(state);
}
}
}
return needsFullUpdate;
@ -109,33 +114,33 @@ void CauterizedModel::updateClusterMatrices() {
const FBXMesh& mesh = geometry.meshes.at(i);
for (int j = 0; j < mesh.clusters.size(); j++) {
const FBXCluster& cluster = mesh.clusters.at(j);
#if defined(SKIN_DQ)
auto jointPose = _rig.getJointPose(cluster.jointIndex);
Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans());
Transform clusterTransform;
Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform);
state.clusterTransforms[j] = Model::TransformDualQuaternion(clusterTransform);
state.clusterTransforms[j].setCauterizationParameters(0.0f, jointPose.trans());
#else
auto jointMatrix = _rig.getJointTransform(cluster.jointIndex);
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]);
#endif
if (_useDualQuaternionSkinning) {
auto jointPose = _rig.getJointPose(cluster.jointIndex);
Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans());
Transform clusterTransform;
Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform);
state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform);
state.clusterDualQuaternions[j].setCauterizationParameters(0.0f, jointPose.trans());
} else {
auto jointMatrix = _rig.getJointTransform(cluster.jointIndex);
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]);
}
}
}
// as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty.
if (!_cauterizeBoneSet.empty()) {
#if defined(SKIN_DQ)
AnimPose cauterizePose = _rig.getJointPose(geometry.neckJointIndex);
cauterizePose.scale() = glm::vec3(0.0001f, 0.0001f, 0.0001f);
#else
static const glm::mat4 zeroScale(
glm::vec4(0.0001f, 0.0f, 0.0f, 0.0f),
glm::vec4(0.0f, 0.0001f, 0.0f, 0.0f),
glm::vec4(0.0f, 0.0f, 0.0001f, 0.0f),
glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
auto cauterizeMatrix = _rig.getJointTransform(geometry.neckJointIndex) * zeroScale;
#endif
for (int i = 0; i < _cauterizeMeshStates.size(); i++) {
Model::MeshState& state = _cauterizeMeshStates[i];
const FBXMesh& mesh = geometry.meshes.at(i);
@ -143,19 +148,24 @@ void CauterizedModel::updateClusterMatrices() {
for (int j = 0; j < mesh.clusters.size(); j++) {
const FBXCluster& cluster = mesh.clusters.at(j);
if (_cauterizeBoneSet.find(cluster.jointIndex) == _cauterizeBoneSet.end()) {
// not cauterized so just copy the value from the non-cauterized version.
state.clusterTransforms[j] = _meshStates[i].clusterTransforms[j];
if (_useDualQuaternionSkinning) {
if (_cauterizeBoneSet.find(cluster.jointIndex) == _cauterizeBoneSet.end()) {
// not cauterized so just copy the value from the non-cauterized version.
state.clusterDualQuaternions[j] = _meshStates[i].clusterDualQuaternions[j];
} else {
Transform jointTransform(cauterizePose.rot(), cauterizePose.scale(), cauterizePose.trans());
Transform clusterTransform;
Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform);
state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform);
state.clusterDualQuaternions[j].setCauterizationParameters(1.0f, cauterizePose.trans());
}
} else {
#if defined(SKIN_DQ)
Transform jointTransform(cauterizePose.rot(), cauterizePose.scale(), cauterizePose.trans());
Transform clusterTransform;
Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform);
state.clusterTransforms[j] = Model::TransformDualQuaternion(clusterTransform);
state.clusterTransforms[j].setCauterizationParameters(1.0f, cauterizePose.trans());
#else
glm_mat4u_mul(cauterizeMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]);
#endif
if (_cauterizeBoneSet.find(cluster.jointIndex) == _cauterizeBoneSet.end()) {
// not cauterized so just copy the value from the non-cauterized version.
state.clusterMatrices[j] = _meshStates[i].clusterMatrices[j];
} else {
glm_mat4u_mul(cauterizeMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]);
}
}
}
}
@ -204,6 +214,7 @@ void CauterizedModel::updateRenderItems() {
bool isWireframe = self->isWireframe();
bool isVisible = self->isVisible();
bool canCastShadow = self->canCastShadow();
bool isLayeredInFront = self->isLayeredInFront();
bool isLayeredInHUD = self->isLayeredInHUD();
bool enableCauterization = self->getEnableCauterization();
@ -213,45 +224,59 @@ void CauterizedModel::updateRenderItems() {
auto itemID = self->_modelMeshRenderItemIDs[i];
auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex;
auto clusterTransforms(self->getMeshState(meshIndex).clusterTransforms);
auto clusterTransformsCauterized(self->getCauterizeMeshState(meshIndex).clusterTransforms);
const auto& meshState = self->getMeshState(meshIndex);
const auto& cauterizedMeshState = self->getCauterizeMeshState(meshIndex);
bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex);
bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning();
transaction.updateItem<CauterizedMeshPartPayload>(itemID, [modelTransform, clusterTransforms, clusterTransformsCauterized, invalidatePayloadShapeKey,
isWireframe, isVisible, isLayeredInFront, isLayeredInHUD, enableCauterization](CauterizedMeshPartPayload& data) {
data.updateClusterBuffer(clusterTransforms, clusterTransformsCauterized);
transaction.updateItem<CauterizedMeshPartPayload>(itemID, [modelTransform, meshState, useDualQuaternionSkinning, cauterizedMeshState, invalidatePayloadShapeKey,
isWireframe, isVisible, isLayeredInFront, isLayeredInHUD, canCastShadow, enableCauterization](CauterizedMeshPartPayload& data) {
if (useDualQuaternionSkinning) {
data.updateClusterBuffer(meshState.clusterDualQuaternions,
cauterizedMeshState.clusterDualQuaternions);
} else {
data.updateClusterBuffer(meshState.clusterMatrices,
cauterizedMeshState.clusterMatrices);
}
Transform renderTransform = modelTransform;
if (clusterTransforms.size() == 1) {
#if defined(SKIN_DQ)
Transform transform(clusterTransforms[0].getRotation(),
clusterTransforms[0].getScale(),
clusterTransforms[0].getTranslation());
renderTransform = modelTransform.worldTransform(transform);
#else
renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0]));
#endif
if (useDualQuaternionSkinning) {
if (meshState.clusterDualQuaternions.size() == 1) {
const auto& dq = meshState.clusterDualQuaternions[0];
Transform transform(dq.getRotation(),
dq.getScale(),
dq.getTranslation());
renderTransform = modelTransform.worldTransform(transform);
}
} else {
if (meshState.clusterMatrices.size() == 1) {
renderTransform = modelTransform.worldTransform(Transform(meshState.clusterMatrices[0]));
}
}
data.updateTransformForSkinnedMesh(renderTransform, modelTransform);
renderTransform = modelTransform;
if (clusterTransformsCauterized.size() == 1) {
#if defined(SKIN_DQ)
Transform transform(clusterTransformsCauterized[0].getRotation(),
clusterTransformsCauterized[0].getScale(),
clusterTransformsCauterized[0].getTranslation());
renderTransform = modelTransform.worldTransform(Transform(transform));
#else
renderTransform = modelTransform.worldTransform(Transform(clusterTransformsCauterized[0]));
#endif
if (useDualQuaternionSkinning) {
if (cauterizedMeshState.clusterDualQuaternions.size() == 1) {
const auto& dq = cauterizedMeshState.clusterDualQuaternions[0];
Transform transform(dq.getRotation(),
dq.getScale(),
dq.getTranslation());
renderTransform = modelTransform.worldTransform(Transform(transform));
}
} else {
if (cauterizedMeshState.clusterMatrices.size() == 1) {
renderTransform = modelTransform.worldTransform(Transform(cauterizedMeshState.clusterMatrices[0]));
}
}
data.updateTransformForCauterizedMesh(renderTransform);
data.setEnableCauterization(enableCauterization);
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, render::ItemKey::TAG_BITS_ALL);
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, render::ItemKey::TAG_BITS_ALL);
data.setLayer(isLayeredInFront, isLayeredInHUD);
data.setShapeKey(invalidatePayloadShapeKey, isWireframe);
data.setShapeKey(invalidatePayloadShapeKey, isWireframe, useDualQuaternionSkinning);
});
}

View file

@ -492,7 +492,7 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext,
batch.setResourceTexture(DEFERRED_BUFFER_DEPTH_UNIT, deferredFramebuffer->getPrimaryDepthTexture());
// FIXME: Different render modes should have different tasks
if (args->_renderMode == RenderArgs::DEFAULT_RENDER_MODE && deferredLightingEffect->isAmbientOcclusionEnabled()) {
if (args->_renderMode == RenderArgs::DEFAULT_RENDER_MODE && deferredLightingEffect->isAmbientOcclusionEnabled() && ambientOcclusionFramebuffer) {
batch.setResourceTexture(DEFERRED_BUFFER_OBSCURANCE_UNIT, ambientOcclusionFramebuffer->getOcclusionTexture());
} else {
// need to assign the white texture if ao is off
@ -537,15 +537,25 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext,
auto keyLight = lightAndShadow.first;
graphics::LightPointer keyAmbientLight;
graphics::LightPointer ambientLight;
if (lightStage && lightStage->_currentFrame._ambientLights.size()) {
keyAmbientLight = lightStage->getLight(lightStage->_currentFrame._ambientLights.front());
ambientLight = lightStage->getLight(lightStage->_currentFrame._ambientLights.front());
}
bool hasAmbientMap = (keyAmbientLight != nullptr);
bool hasAmbientMap = (ambientLight != nullptr);
// Setup the global directional pass pipeline
{
if (deferredLightingEffect->_shadowMapEnabled) {
// Check if keylight casts shadows
bool keyLightCastShadows { false };
if (lightStage && lightStage->_currentFrame._sunLights.size()) {
graphics::LightPointer keyLight = lightStage->getLight(lightStage->_currentFrame._sunLights.front());
if (keyLight) {
keyLightCastShadows = keyLight->getCastShadows();
}
}
if (deferredLightingEffect->_shadowMapEnabled && keyLightCastShadows) {
// If the keylight has an ambient Map then use the Skybox version of the pass
// otherwise use the ambient sphere version

View file

@ -61,7 +61,7 @@ public:
private:
DeferredLightingEffect() = default;
bool _shadowMapEnabled{ false };
bool _shadowMapEnabled{ true }; // note that this value is overwritten in the ::configure method
bool _ambientOcclusionEnabled{ false };
graphics::MeshPointer _pointLightMesh;

View file

@ -77,7 +77,7 @@ void MeshPartPayload::removeMaterial(graphics::MaterialPointer material) {
_drawMaterials.remove(material);
}
void MeshPartPayload::updateKey(bool isVisible, bool isLayered, uint8_t tagBits, bool isGroupCulled) {
void MeshPartPayload::updateKey(bool isVisible, bool isLayered, bool canCastShadow, uint8_t tagBits, bool isGroupCulled) {
ItemKey::Builder builder;
builder.withTypeShape();
@ -91,6 +91,10 @@ void MeshPartPayload::updateKey(bool isVisible, bool isLayered, uint8_t tagBits,
builder.withLayered();
}
if (canCastShadow) {
builder.withShadowCaster();
}
if (isGroupCulled) {
builder.withSubMetaCulled();
}
@ -222,25 +226,35 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in
_shapeID(shapeIndex) {
assert(model && model->isLoaded());
bool useDualQuaternionSkinning = model->getUseDualQuaternionSkinning();
_blendedVertexBuffer = model->_blendedVertexBuffers[_meshIndex];
auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex);
const Model::MeshState& state = model->getMeshState(_meshIndex);
updateMeshPart(modelMesh, partIndex);
computeAdjustedLocalBound(state.clusterTransforms);
if (useDualQuaternionSkinning) {
computeAdjustedLocalBound(state.clusterDualQuaternions);
} else {
computeAdjustedLocalBound(state.clusterMatrices);
}
updateTransform(transform, offsetTransform);
Transform renderTransform = transform;
if (state.clusterTransforms.size() == 1) {
#if defined(SKIN_DQ)
Transform transform(state.clusterTransforms[0].getRotation(),
state.clusterTransforms[0].getScale(),
state.clusterTransforms[0].getTranslation());
renderTransform = transform.worldTransform(Transform(transform));
#else
renderTransform = transform.worldTransform(Transform(state.clusterTransforms[0]));
#endif
if (useDualQuaternionSkinning) {
if (state.clusterDualQuaternions.size() == 1) {
const auto& dq = state.clusterDualQuaternions[0];
Transform transform(dq.getRotation(),
dq.getScale(),
dq.getTranslation());
renderTransform = transform.worldTransform(Transform(transform));
}
} else {
if (state.clusterMatrices.size() == 1) {
renderTransform = transform.worldTransform(Transform(state.clusterMatrices[0]));
}
}
updateTransformForSkinnedMesh(renderTransform, transform);
@ -270,16 +284,44 @@ void ModelMeshPartPayload::notifyLocationChanged() {
}
void ModelMeshPartPayload::updateClusterBuffer(const std::vector<TransformType>& clusterTransforms) {
void ModelMeshPartPayload::updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices) {
// reset cluster buffer if we change the cluster buffer type
if (_clusterBufferType != ClusterBufferType::Matrices) {
_clusterBuffer.reset();
}
_clusterBufferType = ClusterBufferType::Matrices;
// Once computed the cluster matrices, update the buffer(s)
if (clusterTransforms.size() > 1) {
if (clusterMatrices.size() > 1) {
if (!_clusterBuffer) {
_clusterBuffer = std::make_shared<gpu::Buffer>(clusterTransforms.size() * sizeof(TransformType),
(const gpu::Byte*) clusterTransforms.data());
_clusterBuffer = std::make_shared<gpu::Buffer>(clusterMatrices.size() * sizeof(glm::mat4),
(const gpu::Byte*) clusterMatrices.data());
}
else {
_clusterBuffer->setSubData(0, clusterTransforms.size() * sizeof(TransformType),
(const gpu::Byte*) clusterTransforms.data());
_clusterBuffer->setSubData(0, clusterMatrices.size() * sizeof(glm::mat4),
(const gpu::Byte*) clusterMatrices.data());
}
}
}
void ModelMeshPartPayload::updateClusterBuffer(const std::vector<Model::TransformDualQuaternion>& clusterDualQuaternions) {
// reset cluster buffer if we change the cluster buffer type
if (_clusterBufferType != ClusterBufferType::DualQuaternions) {
_clusterBuffer.reset();
}
_clusterBufferType = ClusterBufferType::DualQuaternions;
// Once computed the cluster matrices, update the buffer(s)
if (clusterDualQuaternions.size() > 1) {
if (!_clusterBuffer) {
_clusterBuffer = std::make_shared<gpu::Buffer>(clusterDualQuaternions.size() * sizeof(Model::TransformDualQuaternion),
(const gpu::Byte*) clusterDualQuaternions.data());
}
else {
_clusterBuffer->setSubData(0, clusterDualQuaternions.size() * sizeof(Model::TransformDualQuaternion),
(const gpu::Byte*) clusterDualQuaternions.data());
}
}
}
@ -290,7 +332,8 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& render
_worldBound.transform(boundTransform);
}
void ModelMeshPartPayload::updateKey(bool isVisible, bool isLayered, uint8_t tagBits, bool isGroupCulled) {
// Note that this method is called for models but not for shapes
void ModelMeshPartPayload::updateKey(bool isVisible, bool isLayered, bool canCastShadow, uint8_t tagBits, bool isGroupCulled) {
ItemKey::Builder builder;
builder.withTypeShape();
@ -304,6 +347,10 @@ void ModelMeshPartPayload::updateKey(bool isVisible, bool isLayered, uint8_t tag
builder.withLayered();
}
if (canCastShadow) {
builder.withShadowCaster();
}
if (isGroupCulled) {
builder.withSubMetaCulled();
}
@ -336,7 +383,7 @@ int ModelMeshPartPayload::getLayer() const {
return _layer;
}
void ModelMeshPartPayload::setShapeKey(bool invalidateShapeKey, bool isWireframe) {
void ModelMeshPartPayload::setShapeKey(bool invalidateShapeKey, bool isWireframe, bool useDualQuaternionSkinning) {
if (invalidateShapeKey) {
_shapeKey = ShapeKey::Builder::invalid();
return;
@ -383,6 +430,10 @@ void ModelMeshPartPayload::setShapeKey(bool invalidateShapeKey, bool isWireframe
if (isWireframe) {
builder.withWireframe();
}
if (isSkinned && useDualQuaternionSkinning) {
builder.withDualQuatSkinned();
}
_shapeKey = builder.build();
}
@ -438,29 +489,33 @@ void ModelMeshPartPayload::render(RenderArgs* args) {
args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE;
}
void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector<TransformType>& clusterTransforms) {
void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector<glm::mat4>& clusterMatrices) {
_adjustedLocalBound = _localBound;
if (clusterTransforms.size() > 0) {
#if defined(SKIN_DQ)
Transform rootTransform(clusterTransforms[0].getRotation(),
clusterTransforms[0].getScale(),
clusterTransforms[0].getTranslation());
_adjustedLocalBound.transform(rootTransform);
#else
_adjustedLocalBound.transform(clusterTransforms[0]);
#endif
if (clusterMatrices.size() > 0) {
_adjustedLocalBound.transform(clusterMatrices[0]);
for (int i = 1; i < (int)clusterTransforms.size(); ++i) {
for (int i = 1; i < (int)clusterMatrices.size(); ++i) {
AABox clusterBound = _localBound;
#if defined(SKIN_DQ)
Transform transform(clusterTransforms[i].getRotation(),
clusterTransforms[i].getScale(),
clusterTransforms[i].getTranslation());
clusterBound.transform(transform);
#else
clusterBound.transform(clusterTransforms[i]);
#endif
clusterBound.transform(clusterMatrices[i]);
_adjustedLocalBound += clusterBound;
}
}
}
void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector<Model::TransformDualQuaternion>& clusterDualQuaternions) {
_adjustedLocalBound = _localBound;
if (clusterDualQuaternions.size() > 0) {
Transform rootTransform(clusterDualQuaternions[0].getRotation(),
clusterDualQuaternions[0].getScale(),
clusterDualQuaternions[0].getTranslation());
_adjustedLocalBound.transform(rootTransform);
for (int i = 1; i < (int)clusterDualQuaternions.size(); ++i) {
AABox clusterBound = _localBound;
Transform transform(clusterDualQuaternions[i].getRotation(),
clusterDualQuaternions[i].getScale(),
clusterDualQuaternions[i].getTranslation());
clusterBound.transform(transform);
_adjustedLocalBound += clusterBound;
}
}

View file

@ -32,7 +32,7 @@ public:
typedef render::Payload<MeshPartPayload> Payload;
typedef Payload::DataPointer Pointer;
virtual void updateKey(bool isVisible, bool isLayered, uint8_t tagBits, bool isGroupCulled = false);
virtual void updateKey(bool isVisible, bool isLayered, bool canCastShadow, uint8_t tagBits, bool isGroupCulled = false);
virtual void updateMeshPart(const std::shared_ptr<const graphics::Mesh>& drawMesh, int partIndex);
@ -92,14 +92,14 @@ public:
void notifyLocationChanged() override;
#if defined(SKIN_DQ)
using TransformType = Model::TransformDualQuaternion;
#else
using TransformType = glm::mat4;
#endif
void updateKey(bool isVisible, bool isLayered, bool canCastShadow, uint8_t tagBits, bool isGroupCulled = false) override;
// matrix palette skinning
void updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices);
// dual quaternion skinning
void updateClusterBuffer(const std::vector<Model::TransformDualQuaternion>& clusterDualQuaternions);
void updateKey(bool isVisible, bool isLayered, uint8_t tagBits, bool isGroupCulled = false) override;
void updateClusterBuffer(const std::vector<TransformType>& clusterTransforms);
void updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform);
// Render Item interface
@ -108,16 +108,23 @@ public:
void render(RenderArgs* args) override;
void setLayer(bool isLayeredInFront, bool isLayeredInHUD);
void setShapeKey(bool invalidateShapeKey, bool isWireframe);
void setShapeKey(bool invalidateShapeKey, bool isWireframe, bool useDualQuaternionSkinning);
// ModelMeshPartPayload functions to perform render
void bindMesh(gpu::Batch& batch) override;
void bindTransform(gpu::Batch& batch, RenderArgs::RenderMode renderMode) const override;
void computeAdjustedLocalBound(const std::vector<TransformType>& clusterTransforms);
// matrix palette skinning
void computeAdjustedLocalBound(const std::vector<glm::mat4>& clusterMatrices);
// dual quaternion skinning
void computeAdjustedLocalBound(const std::vector<Model::TransformDualQuaternion>& clusterDualQuaternions);
gpu::BufferPointer _clusterBuffer;
enum class ClusterBufferType { Matrices, DualQuaternions };
ClusterBufferType _clusterBufferType { ClusterBufferType::Matrices };
int _meshIndex;
int _shapeID;

View file

@ -102,6 +102,7 @@ Model::Model(QObject* parent, SpatiallyNestable* spatiallyNestableOverride) :
_snappedToRegistrationPoint(false),
_url(HTTP_INVALID_COM),
_isVisible(true),
_canCastShadow(false),
_blendNumber(0),
_appliedBlendNumber(0),
_isWireframe(false)
@ -268,6 +269,7 @@ void Model::updateRenderItems() {
bool isWireframe = self->isWireframe();
bool isVisible = self->isVisible();
bool canCastShadow = self->canCastShadow();
uint8_t viewTagBits = self->getViewTagBits();
bool isLayeredInFront = self->isLayeredInFront();
bool isLayeredInHUD = self->isLayeredInHUD();
@ -278,32 +280,42 @@ void Model::updateRenderItems() {
auto itemID = self->_modelMeshRenderItemIDs[i];
auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex;
auto clusterTransforms(self->getMeshState(meshIndex).clusterTransforms);
const auto& meshState = self->getMeshState(meshIndex);
bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex);
bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning();
transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, clusterTransforms,
transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, meshState, useDualQuaternionSkinning,
invalidatePayloadShapeKey, isWireframe, isVisible,
viewTagBits, isLayeredInFront,
canCastShadow, viewTagBits, isLayeredInFront,
isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
data.updateClusterBuffer(clusterTransforms);
if (useDualQuaternionSkinning) {
data.updateClusterBuffer(meshState.clusterDualQuaternions);
} else {
data.updateClusterBuffer(meshState.clusterMatrices);
}
Transform renderTransform = modelTransform;
if (clusterTransforms.size() == 1) {
#if defined(SKIN_DQ)
Transform transform(clusterTransforms[0].getRotation(),
clusterTransforms[0].getScale(),
clusterTransforms[0].getTranslation());
renderTransform = modelTransform.worldTransform(Transform(transform));
#else
renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0]));
#endif
if (useDualQuaternionSkinning) {
if (meshState.clusterDualQuaternions.size() == 1) {
const auto& dq = meshState.clusterDualQuaternions[0];
Transform transform(dq.getRotation(),
dq.getScale(),
dq.getTranslation());
renderTransform = modelTransform.worldTransform(Transform(transform));
}
} else {
if (meshState.clusterMatrices.size() == 1) {
renderTransform = modelTransform.worldTransform(Transform(meshState.clusterMatrices[0]));
}
}
data.updateTransformForSkinnedMesh(renderTransform, modelTransform);
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits, isGroupCulled);
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled);
data.setLayer(isLayeredInFront, isLayeredInHUD);
data.setShapeKey(invalidatePayloadShapeKey, isWireframe);
data.setShapeKey(invalidatePayloadShapeKey, isWireframe, useDualQuaternionSkinning);
});
}
@ -378,7 +390,8 @@ bool Model::updateGeometry() {
const FBXGeometry& fbxGeometry = getFBXGeometry();
foreach (const FBXMesh& mesh, fbxGeometry.meshes) {
MeshState state;
state.clusterTransforms.resize(mesh.clusters.size());
state.clusterDualQuaternions.resize(mesh.clusters.size());
state.clusterMatrices.resize(mesh.clusters.size());
_meshStates.push_back(state);
// Note: we add empty buffers for meshes that lack blendshapes so we can access the buffers by index
@ -693,46 +706,66 @@ void Model::setVisibleInScene(bool isVisible, const render::ScenePointer& scene,
bool isLayeredInFront = _isLayeredInFront;
bool isLayeredInHUD = _isLayeredInHUD;
bool canCastShadow = _canCastShadow;
render::Transaction transaction;
foreach (auto item, _modelMeshRenderItemsMap.keys()) {
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront,
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow,
isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits, isGroupCulled);
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled);
});
}
foreach(auto item, _collisionRenderItemsMap.keys()) {
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront,
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow,
isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits, isGroupCulled);
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled);
});
}
scene->enqueueTransaction(transaction);
}
}
void Model::setCanCastShadow(bool canCastShadow, const render::ScenePointer& scene, uint8_t viewTagBits, bool isGroupCulled) {
if (_canCastShadow != canCastShadow) {
_canCastShadow = canCastShadow;
bool isVisible = _isVisible;
bool isLayeredInFront = _isLayeredInFront;
bool isLayeredInHUD = _isLayeredInHUD;
render::Transaction transaction;
foreach (auto item, _modelMeshRenderItemsMap.keys()) {
transaction.updateItem<ModelMeshPartPayload>(item,
[isVisible, viewTagBits, canCastShadow, isLayeredInFront, isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
data.updateKey(isVisible, viewTagBits, canCastShadow, isLayeredInFront || isLayeredInHUD, isGroupCulled);
});
}
scene->enqueueTransaction(transaction);
}
}
void Model::setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& scene) {
if (_isLayeredInFront != isLayeredInFront) {
_isLayeredInFront = isLayeredInFront;
bool isVisible = _isVisible;
bool canCastShadow = _canCastShadow;
uint8_t viewTagBits = _viewTagBits;
bool isLayeredInHUD = _isLayeredInHUD;
bool isGroupCulled = _isGroupCulled;
render::Transaction transaction;
foreach(auto item, _modelMeshRenderItemsMap.keys()) {
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront,
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow,
isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits, isGroupCulled);
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled);
data.setLayer(isLayeredInFront, isLayeredInHUD);
});
}
foreach(auto item, _collisionRenderItemsMap.keys()) {
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront,
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow,
isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits, isGroupCulled);
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled);
data.setLayer(isLayeredInFront, isLayeredInHUD);
});
}
@ -745,22 +778,23 @@ void Model::setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& sce
_isLayeredInHUD = isLayeredInHUD;
bool isVisible = _isVisible;
bool canCastShadow = _canCastShadow;
uint8_t viewTagBits = _viewTagBits;
bool isLayeredInFront = _isLayeredInFront;
bool isGroupCulled = _isGroupCulled;
render::Transaction transaction;
foreach(auto item, _modelMeshRenderItemsMap.keys()) {
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront,
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow,
isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits, isGroupCulled);
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled);
data.setLayer(isLayeredInFront, isLayeredInHUD);
});
}
foreach(auto item, _collisionRenderItemsMap.keys()) {
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront,
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow,
isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, viewTagBits, isGroupCulled);
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled);
data.setLayer(isLayeredInFront, isLayeredInHUD);
});
}
@ -1234,6 +1268,10 @@ void Model::snapToRegistrationPoint() {
_snappedToRegistrationPoint = true;
}
void Model::setUseDualQuaternionSkinning(bool value) {
_useDualQuaternionSkinning = value;
}
void Model::simulate(float deltaTime, bool fullUpdate) {
DETAILED_PROFILE_RANGE(simulation_detail, __FUNCTION__);
fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit)
@ -1267,7 +1305,11 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) {
void Model::computeMeshPartLocalBounds() {
for (auto& part : _modelMeshRenderItems) {
const Model::MeshState& state = _meshStates.at(part->_meshIndex);
part->computeAdjustedLocalBound(state.clusterTransforms);
if (_useDualQuaternionSkinning) {
part->computeAdjustedLocalBound(state.clusterDualQuaternions);
} else {
part->computeAdjustedLocalBound(state.clusterMatrices);
}
}
}
@ -1286,16 +1328,16 @@ void Model::updateClusterMatrices() {
const FBXMesh& mesh = geometry.meshes.at(i);
for (int j = 0; j < mesh.clusters.size(); j++) {
const FBXCluster& cluster = mesh.clusters.at(j);
#if defined(SKIN_DQ)
auto jointPose = _rig.getJointPose(cluster.jointIndex);
Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans());
Transform clusterTransform;
Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform);
state.clusterTransforms[j] = Model::TransformDualQuaternion(clusterTransform);
#else
auto jointMatrix = _rig.getJointTransform(cluster.jointIndex);
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]);
#endif
if (_useDualQuaternionSkinning) {
auto jointPose = _rig.getJointPose(cluster.jointIndex);
Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans());
Transform clusterTransform;
Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform);
state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform);
} else {
auto jointMatrix = _rig.getJointTransform(cluster.jointIndex);
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]);
}
}
}
@ -1562,15 +1604,17 @@ void Model::addMaterial(graphics::MaterialLayer material, const std::string& par
uint8_t viewTagBits = getViewTagBits();
bool layeredInFront = isLayeredInFront();
bool layeredInHUD = isLayeredInHUD();
bool canCastShadow = _canCastShadow;
bool wireframe = isWireframe();
auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex;
bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex);
transaction.updateItem<ModelMeshPartPayload>(itemID, [material, visible, layeredInFront, layeredInHUD, viewTagBits,
invalidatePayloadShapeKey, wireframe](ModelMeshPartPayload& data) {
bool useDualQuaternionSkinning = _useDualQuaternionSkinning;
transaction.updateItem<ModelMeshPartPayload>(itemID, [material, visible, layeredInFront, layeredInHUD, viewTagBits, canCastShadow,
invalidatePayloadShapeKey, wireframe, useDualQuaternionSkinning](ModelMeshPartPayload& data) {
data.addMaterial(material);
// if the material changed, we might need to update our item key or shape key
data.updateKey(visible, layeredInFront || layeredInHUD, viewTagBits);
data.setShapeKey(invalidatePayloadShapeKey, wireframe);
data.updateKey(visible, layeredInFront || layeredInHUD, canCastShadow, viewTagBits);
data.setShapeKey(invalidatePayloadShapeKey, wireframe, useDualQuaternionSkinning);
});
}
}
@ -1587,15 +1631,17 @@ void Model::removeMaterial(graphics::MaterialPointer material, const std::string
uint8_t viewTagBits = getViewTagBits();
bool layeredInFront = isLayeredInFront();
bool layeredInHUD = isLayeredInHUD();
bool canCastShadow = _canCastShadow;
bool wireframe = isWireframe();
auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex;
bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex);
transaction.updateItem<ModelMeshPartPayload>(itemID, [material, visible, layeredInFront, layeredInHUD, viewTagBits,
invalidatePayloadShapeKey, wireframe](ModelMeshPartPayload& data) {
bool useDualQuaternionSkinning = _useDualQuaternionSkinning;
transaction.updateItem<ModelMeshPartPayload>(itemID, [material, visible, layeredInFront, layeredInHUD, viewTagBits, canCastShadow,
invalidatePayloadShapeKey, wireframe, useDualQuaternionSkinning](ModelMeshPartPayload& data) {
data.removeMaterial(material);
// if the material changed, we might need to update our item key or shape key
data.updateKey(visible, layeredInFront || layeredInHUD, viewTagBits);
data.setShapeKey(invalidatePayloadShapeKey, wireframe);
data.updateKey(visible, layeredInFront || layeredInHUD, canCastShadow, viewTagBits);
data.setShapeKey(invalidatePayloadShapeKey, wireframe, useDualQuaternionSkinning);
});
}
}

View file

@ -87,6 +87,10 @@ public:
// new Scene/Engine rendering support
void setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t viewTagBits, bool isGroupCulled);
bool canCastShadow() const { return _canCastShadow; }
void setCanCastShadow(bool canCastShadow, const render::ScenePointer& scene, uint8_t viewTagBits, bool isGroupCulled);
void setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& scene);
void setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& scene);
bool needsFixupInScene() const;
@ -256,8 +260,6 @@ public:
int getRenderInfoDrawCalls() const { return _renderInfoDrawCalls; }
bool getRenderInfoHasTransparent() const { return _renderInfoHasTransparent; }
#if defined(SKIN_DQ)
class TransformDualQuaternion {
public:
TransformDualQuaternion() {}
@ -295,15 +297,11 @@ public:
DualQuaternion _dq;
glm::vec4 _cauterizedPosition { 0.0f, 0.0f, 0.0f, 1.0f };
};
#endif
class MeshState {
public:
#if defined(SKIN_DQ)
std::vector<TransformDualQuaternion> clusterTransforms;
#else
std::vector<glm::mat4> clusterTransforms;
#endif
std::vector<TransformDualQuaternion> clusterDualQuaternions;
std::vector<glm::mat4> clusterMatrices;
};
const MeshState& getMeshState(int index) { return _meshStates.at(index); }
@ -319,6 +317,8 @@ public:
Q_INVOKABLE MeshProxyList getMeshes() const;
void scaleToFit();
bool getUseDualQuaternionSkinning() const { return _useDualQuaternionSkinning; }
void setUseDualQuaternionSkinning(bool value);
void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName);
void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName);
@ -404,6 +404,8 @@ protected:
bool _isVisible;
uint8_t _viewTagBits{ render::ItemKey::TAG_BITS_ALL };
bool _canCastShadow;
gpu::Buffers _blendedVertexBuffers;
QVector<QVector<QSharedPointer<Texture> > > _dilatedTextures;
@ -425,6 +427,7 @@ protected:
virtual void createCollisionRenderItemSet();
bool _isWireframe;
bool _useDualQuaternionSkinning { false };
// debug rendering support
int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID;

View file

@ -26,6 +26,8 @@
#include "model_lightmap_normal_map_vert.h"
#include "skin_model_vert.h"
#include "skin_model_normal_map_vert.h"
#include "skin_model_dq_vert.h"
#include "skin_model_normal_map_dq_vert.h"
#include "model_lightmap_fade_vert.h"
#include "model_lightmap_normal_map_fade_vert.h"
@ -33,6 +35,8 @@
#include "model_translucent_normal_map_vert.h"
#include "skin_model_fade_vert.h"
#include "skin_model_normal_map_fade_vert.h"
#include "skin_model_fade_dq_vert.h"
#include "skin_model_normal_map_fade_dq_vert.h"
#include "simple_vert.h"
#include "simple_textured_frag.h"
@ -95,6 +99,7 @@
#include "model_shadow_vert.h"
#include "skin_model_shadow_vert.h"
#include "skin_model_shadow_dq_vert.h"
#include "model_shadow_frag.h"
#include "skin_model_shadow_frag.h"
@ -195,16 +200,28 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip
auto modelTranslucentVertex = model_translucent_vert::getShader();
auto modelTranslucentNormalMapVertex = model_translucent_normal_map_vert::getShader();
auto modelShadowVertex = model_shadow_vert::getShader();
auto modelLightmapFadeVertex = model_lightmap_fade_vert::getShader();
auto modelLightmapNormalMapFadeVertex = model_lightmap_normal_map_fade_vert::getShader();
// matrix palette skinned
auto skinModelVertex = skin_model_vert::getShader();
auto skinModelNormalMapVertex = skin_model_normal_map_vert::getShader();
auto skinModelShadowVertex = skin_model_shadow_vert::getShader();
auto modelLightmapFadeVertex = model_lightmap_fade_vert::getShader();
auto modelLightmapNormalMapFadeVertex = model_lightmap_normal_map_fade_vert::getShader();
auto skinModelFadeVertex = skin_model_fade_vert::getShader();
auto skinModelNormalMapFadeVertex = skin_model_normal_map_fade_vert::getShader();
auto skinModelTranslucentVertex = skinModelFadeVertex; // We use the same because it ouputs world position per vertex
auto skinModelNormalMapTranslucentVertex = skinModelNormalMapFadeVertex; // We use the same because it ouputs world position per vertex
// dual quaternion skinned
auto skinModelDualQuatVertex = skin_model_dq_vert::getShader();
auto skinModelNormalMapDualQuatVertex = skin_model_normal_map_dq_vert::getShader();
auto skinModelShadowDualQuatVertex = skin_model_shadow_dq_vert::getShader();
auto skinModelFadeDualQuatVertex = skin_model_fade_dq_vert::getShader();
auto skinModelNormalMapFadeDualQuatVertex = skin_model_normal_map_fade_dq_vert::getShader();
auto skinModelTranslucentDualQuatVertex = skinModelFadeDualQuatVertex; // We use the same because it ouputs world position per vertex
auto skinModelNormalMapTranslucentDualQuatVertex = skinModelNormalMapFadeDualQuatVertex; // We use the same because it ouputs world position per vertex
auto modelFadeVertex = model_fade_vert::getShader();
auto modelNormalMapFadeVertex = model_normal_map_fade_vert::getShader();
auto simpleFadeVertex = simple_fade_vert::getShader();
@ -376,7 +393,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip
Key::Builder().withMaterial().withLightmap().withTangents().withSpecular().withFade(),
modelLightmapNormalMapFadeVertex, modelLightmapNormalSpecularMapFadePixel, batchSetter, itemSetter);
// Skinned
// matrix palette skinned
addPipeline(
Key::Builder().withMaterial().withSkinned(),
skinModelVertex, modelPixel, nullptr, nullptr);
@ -403,7 +420,7 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip
Key::Builder().withMaterial().withSkinned().withTangents().withSpecular().withFade(),
skinModelNormalMapFadeVertex, modelNormalSpecularMapFadePixel, batchSetter, itemSetter);
// Skinned and Translucent
// matrix palette skinned and translucent
addPipeline(
Key::Builder().withMaterial().withSkinned().withTranslucent(),
skinModelTranslucentVertex, modelTranslucentPixel, nullptr, nullptr);
@ -430,6 +447,60 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip
Key::Builder().withMaterial().withSkinned().withTranslucent().withTangents().withSpecular().withFade(),
skinModelNormalMapFadeVertex, modelTranslucentNormalMapFadePixel, batchSetter, itemSetter);
// dual quaternion skinned
addPipeline(
Key::Builder().withMaterial().withSkinned().withDualQuatSkinned(),
skinModelDualQuatVertex, modelPixel, nullptr, nullptr);
addPipeline(
Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTangents(),
skinModelNormalMapDualQuatVertex, modelNormalMapPixel, nullptr, nullptr);
addPipeline(
Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withSpecular(),
skinModelDualQuatVertex, modelSpecularMapPixel, nullptr, nullptr);
addPipeline(
Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTangents().withSpecular(),
skinModelNormalMapDualQuatVertex, modelNormalSpecularMapPixel, nullptr, nullptr);
// Same thing but with Fade on
addPipeline(
Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withFade(),
skinModelFadeDualQuatVertex, modelFadePixel, batchSetter, itemSetter);
addPipeline(
Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTangents().withFade(),
skinModelNormalMapFadeDualQuatVertex, modelNormalMapFadePixel, batchSetter, itemSetter);
addPipeline(
Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withSpecular().withFade(),
skinModelFadeDualQuatVertex, modelSpecularMapFadePixel, batchSetter, itemSetter);
addPipeline(
Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTangents().withSpecular().withFade(),
skinModelNormalMapFadeDualQuatVertex, modelNormalSpecularMapFadePixel, batchSetter, itemSetter);
// dual quaternion skinned and translucent
addPipeline(
Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTranslucent(),
skinModelTranslucentDualQuatVertex, modelTranslucentPixel, nullptr, nullptr);
addPipeline(
Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTranslucent().withTangents(),
skinModelNormalMapTranslucentDualQuatVertex, modelTranslucentNormalMapPixel, nullptr, nullptr);
addPipeline(
Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTranslucent().withSpecular(),
skinModelTranslucentDualQuatVertex, modelTranslucentPixel, nullptr, nullptr);
addPipeline(
Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTranslucent().withTangents().withSpecular(),
skinModelNormalMapTranslucentDualQuatVertex, modelTranslucentNormalMapPixel, nullptr, nullptr);
// Same thing but with Fade on
addPipeline(
Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTranslucent().withFade(),
skinModelFadeVertex, modelTranslucentFadePixel, batchSetter, itemSetter);
addPipeline(
Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTranslucent().withTangents().withFade(),
skinModelNormalMapFadeDualQuatVertex, modelTranslucentNormalMapFadePixel, batchSetter, itemSetter);
addPipeline(
Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTranslucent().withSpecular().withFade(),
skinModelFadeDualQuatVertex, modelTranslucentFadePixel, batchSetter, itemSetter);
addPipeline(
Key::Builder().withMaterial().withSkinned().withDualQuatSkinned().withTranslucent().withTangents().withSpecular().withFade(),
skinModelNormalMapFadeDualQuatVertex, modelTranslucentNormalMapFadePixel, batchSetter, itemSetter);
// Depth-only
addPipeline(
Key::Builder().withDepthOnly(),

View file

@ -218,7 +218,10 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
const auto setupOutput = task.addJob<RenderShadowSetup>("ShadowSetup");
const auto queryResolution = setupOutput.getN<RenderShadowSetup::Outputs>(2);
// Fetch and cull the items from the scene
static const auto shadowCasterFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(tagBits, tagMask);
// Enable models to not cast shadows (otherwise, models will always cast shadows)
static const auto shadowCasterFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(tagBits, tagMask).withShadowCaster();
const auto fetchInput = FetchSpatialTree::Inputs(shadowCasterFilter, queryResolution).asVarying();
const auto shadowSelection = task.addJob<FetchSpatialTree>("FetchShadowTree", fetchInput);
const auto selectionInputs = FetchSpatialSelection::Inputs(shadowSelection, shadowCasterFilter).asVarying();
@ -297,8 +300,14 @@ void RenderShadowSetup::setSlopeBias(int cascadeIndex, float value) {
}
void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) {
// Abort all jobs if not casting shadows
auto lightStage = renderContext->_scene->getStage<LightStage>();
assert(lightStage);
if (!lightStage->getCurrentKeyLight() || !lightStage->getCurrentKeyLight()->getCastShadows()) {
renderContext->taskFlow.abortTask();
return;
}
// Cache old render args
RenderArgs* args = renderContext->args;
@ -378,12 +387,13 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O
void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) {
auto lightStage = renderContext->_scene->getStage<LightStage>();
assert(lightStage);
// Cache old render args
RenderArgs* args = renderContext->args;
const auto globalShadow = lightStage->getCurrentKeyShadow();
if (globalShadow && _cascadeIndex<globalShadow->getCascadeCount()) {
output.edit0() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask);
output.edit0() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask).withShadowCaster();
// Set the keylight render args
auto& cascade = globalShadow->getCascade(_cascadeIndex);

View file

@ -38,7 +38,7 @@ class RenderShadowTaskConfig : public render::Task::Config::Persistent {
Q_OBJECT
Q_PROPERTY(bool enabled MEMBER enabled NOTIFY dirty)
public:
RenderShadowTaskConfig() : render::Task::Config::Persistent(QStringList() << "Render" << "Engine" << "Shadows", false) {}
RenderShadowTaskConfig() : render::Task::Config::Persistent(QStringList() << "Render" << "Engine" << "Shadows", true) {}
signals:
void dirty();

View file

@ -89,7 +89,9 @@ float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets
float evalShadowAttenuation(vec3 worldLightDir, vec4 worldPosition, float viewDepth, vec3 worldNormal) {
ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition);
vec4 cascadeShadowCoords[2] = { vec4(0), vec4(0) };
vec4 cascadeShadowCoords[2];
cascadeShadowCoords[0] = vec4(0);
cascadeShadowCoords[1] = vec4(0);
ivec2 cascadeIndices;
float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices);

View file

@ -11,18 +11,16 @@
<@if not SKINNING_SLH@>
<@def SKINNING_SLH@>
// Use dual quaternion skinning
// Must match #define SKIN_DQ in Model.h
<@def SKIN_DQ@>
const int MAX_CLUSTERS = 128;
const int INDICES_PER_VERTEX = 4;
<@func declareUseDualQuaternionSkinning(USE_DUAL_QUATERNION_SKINNING)@>
layout(std140) uniform skinClusterBuffer {
mat4 clusterMatrices[MAX_CLUSTERS];
};
<@if SKIN_DQ@>
<@if USE_DUAL_QUATERNION_SKINNING@>
mat4 dualQuatToMat4(vec4 real, vec4 dual) {
float twoRealXSq = 2.0 * real.x * real.x;
@ -211,7 +209,7 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v
skinnedTangent = vec3(m * vec4(inTangent, 0));
}
<@else@> // SKIN_DQ
<@else@> // USE_DUAL_QUATERNION_SKINNING
void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) {
vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0);
@ -260,6 +258,8 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v
skinnedTangent = newTangent.xyz;
}
<@endif@> // if SKIN_DQ
<@endif@> // if USE_DUAL_QUATERNION_SKINNING
<@endfunc@> // func declareUseDualQuaternionSkinning(USE_DUAL_QUATERNION_SKINNING)
<@endif@> // if not SKINNING_SLH

View file

@ -52,27 +52,27 @@ void SoftAttachmentModel::updateClusterMatrices() {
// TODO: cache these look-ups as an optimization
int jointIndexOverride = getJointIndexOverride(cluster.jointIndex);
#if defined(SKIN_DQ)
glm::mat4 jointMatrix;
if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) {
jointMatrix = _rigOverride.getJointTransform(jointIndexOverride);
} else {
jointMatrix = _rig.getJointTransform(cluster.jointIndex);
}
if (_useDualQuaternionSkinning) {
glm::mat4 jointMatrix;
if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) {
jointMatrix = _rigOverride.getJointTransform(jointIndexOverride);
} else {
jointMatrix = _rig.getJointTransform(cluster.jointIndex);
}
glm::mat4 m;
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m);
state.clusterTransforms[j] = Model::TransformDualQuaternion(m);
#else
glm::mat4 jointMatrix;
if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) {
jointMatrix = _rigOverride.getJointTransform(jointIndexOverride);
glm::mat4 m;
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m);
state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(m);
} else {
jointMatrix = _rig.getJointTransform(cluster.jointIndex);
}
glm::mat4 jointMatrix;
if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) {
jointMatrix = _rigOverride.getJointTransform(jointIndexOverride);
} else {
jointMatrix = _rig.getJointTransform(cluster.jointIndex);
}
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]);
#endif
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]);
}
}
}

View file

@ -18,6 +18,7 @@
<$declareStandardTransform()$>
<@include Skinning.slh@>
<$declareUseDualQuaternionSkinning()$>
<@include MaterialTextures.slh@>
<$declareMaterialTexMapArrayBuffer()$>

View file

@ -0,0 +1,52 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// skin_model.vert
// vertex shader
//
// Created by Andrzej Kapolka on 10/14/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include gpu/Inputs.slh@>
<@include gpu/Color.slh@>
<@include gpu/Transform.slh@>
<$declareStandardTransform()$>
<@include Skinning.slh@>
<$declareUseDualQuaternionSkinning(1)$>
<@include MaterialTextures.slh@>
<$declareMaterialTexMapArrayBuffer()$>
out vec4 _position;
out vec2 _texCoord0;
out vec2 _texCoord1;
out vec3 _normal;
out vec3 _color;
out float _alpha;
void main(void) {
vec4 position = vec4(0.0, 0.0, 0.0, 0.0);
vec3 interpolatedNormal = vec3(0.0, 0.0, 0.0);
skinPositionNormal(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, position, interpolatedNormal);
// pass along the color
_color = colorToLinearRGB(inColor.rgb);
_alpha = inColor.a;
TexMapArray texMapArray = getTexMapArray();
<$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _texCoord0)$>
<$evalTexMapArrayTexcoord1(texMapArray, inTexCoord0, _texCoord1)$>
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$>
<$transformModelToWorldDir(cam, obj, interpolatedNormal.xyz, _normal.xyz)$>
}

View file

@ -18,6 +18,7 @@
<$declareStandardTransform()$>
<@include Skinning.slh@>
<$declareUseDualQuaternionSkinning()$>
<@include MaterialTextures.slh@>
<$declareMaterialTexMapArrayBuffer()$>

View file

@ -0,0 +1,54 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// skin_model_fade.vert
// vertex shader
//
// Created by Olivier Prat on 06/045/17.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include gpu/Inputs.slh@>
<@include gpu/Color.slh@>
<@include gpu/Transform.slh@>
<$declareStandardTransform()$>
<@include Skinning.slh@>
<$declareUseDualQuaternionSkinning(1)$>
<@include MaterialTextures.slh@>
<$declareMaterialTexMapArrayBuffer()$>
out vec4 _position;
out vec2 _texCoord0;
out vec2 _texCoord1;
out vec3 _normal;
out vec3 _color;
out float _alpha;
out vec4 _worldPosition;
void main(void) {
vec4 position = vec4(0.0, 0.0, 0.0, 0.0);
vec3 interpolatedNormal = vec3(0.0, 0.0, 0.0);
skinPositionNormal(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, position, interpolatedNormal);
// pass along the color
_color = colorToLinearRGB(inColor.rgb);
_alpha = inColor.a;
TexMapArray texMapArray = getTexMapArray();
<$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _texCoord0)$>
<$evalTexMapArrayTexcoord1(texMapArray, inTexCoord0, _texCoord1)$>
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$>
<$transformModelToWorldPos(obj, position, _worldPosition)$>
<$transformModelToWorldDir(cam, obj, interpolatedNormal.xyz, _normal.xyz)$>
}

View file

@ -18,6 +18,7 @@
<$declareStandardTransform()$>
<@include Skinning.slh@>
<$declareUseDualQuaternionSkinning()$>
<@include MaterialTextures.slh@>
<$declareMaterialTexMapArrayBuffer()$>

View file

@ -0,0 +1,61 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// skin_model_normal_map.vert
// vertex shader
//
// Created by Andrzej Kapolka on 10/29/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include gpu/Inputs.slh@>
<@include gpu/Color.slh@>
<@include gpu/Transform.slh@>
<$declareStandardTransform()$>
<@include Skinning.slh@>
<$declareUseDualQuaternionSkinning(1)$>
<@include MaterialTextures.slh@>
<$declareMaterialTexMapArrayBuffer()$>
out vec4 _position;
out vec2 _texCoord0;
out vec2 _texCoord1;
out vec3 _normal;
out vec3 _tangent;
out vec3 _color;
out float _alpha;
void main(void) {
vec4 position = vec4(0.0, 0.0, 0.0, 0.0);
vec4 interpolatedNormal = vec4(0.0, 0.0, 0.0, 0.0);
vec4 interpolatedTangent = vec4(0.0, 0.0, 0.0, 0.0);
skinPositionNormalTangent(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, inTangent.xyz, position, interpolatedNormal.xyz, interpolatedTangent.xyz);
// pass along the color
_color = colorToLinearRGB(inColor.rgb);
_alpha = inColor.a;
TexMapArray texMapArray = getTexMapArray();
<$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _texCoord0)$>
<$evalTexMapArrayTexcoord1(texMapArray, inTexCoord0, _texCoord1)$>
interpolatedNormal = vec4(normalize(interpolatedNormal.xyz), 0.0);
interpolatedTangent = vec4(normalize(interpolatedTangent.xyz), 0.0);
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$>
<$transformModelToWorldDir(cam, obj, interpolatedNormal.xyz, interpolatedNormal.xyz)$>
<$transformModelToWorldDir(cam, obj, interpolatedTangent.xyz, interpolatedTangent.xyz)$>
_normal = interpolatedNormal.xyz;
_tangent = interpolatedTangent.xyz;
}

View file

@ -18,6 +18,7 @@
<$declareStandardTransform()$>
<@include Skinning.slh@>
<$declareUseDualQuaternionSkinning()$>
<@include MaterialTextures.slh@>
<$declareMaterialTexMapArrayBuffer()$>

View file

@ -0,0 +1,61 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// skin_model_normal_map.vert
// vertex shader
//
// Created by Andrzej Kapolka on 10/29/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include gpu/Inputs.slh@>
<@include gpu/Color.slh@>
<@include gpu/Transform.slh@>
<$declareStandardTransform()$>
<@include Skinning.slh@>
<$declareUseDualQuaternionSkinning(1)$>
<@include MaterialTextures.slh@>
<$declareMaterialTexMapArrayBuffer()$>
out vec4 _position;
out vec2 _texCoord0;
out vec2 _texCoord1;
out vec3 _normal;
out vec3 _tangent;
out vec3 _color;
out float _alpha;
void main(void) {
vec4 position = vec4(0.0, 0.0, 0.0, 0.0);
vec4 interpolatedNormal = vec4(0.0, 0.0, 0.0, 0.0);
vec4 interpolatedTangent = vec4(0.0, 0.0, 0.0, 0.0);
skinPositionNormalTangent(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, inTangent.xyz, position, interpolatedNormal.xyz, interpolatedTangent.xyz);
// pass along the color
_color = colorToLinearRGB(inColor.rgb);
_alpha = inColor.a;
TexMapArray texMapArray = getTexMapArray();
<$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _texCoord0)$>
<$evalTexMapArrayTexcoord1(texMapArray, inTexCoord0, _texCoord1)$>
interpolatedNormal = vec4(normalize(interpolatedNormal.xyz), 0.0);
interpolatedTangent = vec4(normalize(interpolatedTangent.xyz), 0.0);
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$>
<$transformModelToWorldDir(cam, obj, interpolatedNormal.xyz, interpolatedNormal.xyz)$>
<$transformModelToWorldDir(cam, obj, interpolatedTangent.xyz, interpolatedTangent.xyz)$>
_normal = interpolatedNormal.xyz;
_tangent = interpolatedTangent.xyz;
}

View file

@ -17,6 +17,7 @@
<$declareStandardTransform()$>
<@include Skinning.slh@>
<$declareUseDualQuaternionSkinning()$>
void main(void) {
vec4 position = vec4(0.0, 0.0, 0.0, 0.0);

View file

@ -0,0 +1,30 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// skin_model_shadow.vert
// vertex shader
//
// Created by Andrzej Kapolka on 3/24/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include gpu/Inputs.slh@>
<@include gpu/Transform.slh@>
<$declareStandardTransform()$>
<@include Skinning.slh@>
<$declareUseDualQuaternionSkinning(1)$>
void main(void) {
vec4 position = vec4(0.0, 0.0, 0.0, 0.0);
skinPosition(inSkinClusterIndex, inSkinClusterWeight, inPosition, position);
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToClipPos(cam, obj, position, gl_Position)$>
}

View file

@ -17,6 +17,7 @@
<$declareStandardTransform()$>
<@include Skinning.slh@>
<$declareUseDualQuaternionSkinning()$>
out vec4 _worldPosition;

View file

@ -0,0 +1,33 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// skin_model_shadow_fade.vert
// vertex shader
//
// Created by Olivier Prat on 06/045/17.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include gpu/Inputs.slh@>
<@include gpu/Transform.slh@>
<$declareStandardTransform()$>
<@include Skinning.slh@>
<$declareUseDualQuaternionSkinning(1)$>
out vec4 _worldPosition;
void main(void) {
vec4 position = vec4(0.0, 0.0, 0.0, 0.0);
skinPosition(inSkinClusterIndex, inSkinClusterWeight, inPosition, position);
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToClipPos(cam, obj, position, gl_Position)$>
<$transformModelToWorldPos(obj, position, _worldPosition)$>
}

View file

@ -226,7 +226,6 @@ public:
Builder& withVisible() { _value.reset(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); }
Builder& withInvisible() { _value.set(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); }
Builder& withNoShadowCaster() { _value.reset(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); }
Builder& withShadowCaster() { _value.set(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); }
Builder& withoutLayered() { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); }

View file

@ -32,6 +32,7 @@ public:
SPECULAR,
UNLIT,
SKINNED,
DUAL_QUAT_SKINNED,
DEPTH_ONLY,
DEPTH_BIAS,
WIREFRAME,
@ -80,6 +81,7 @@ public:
Builder& withSpecular() { _flags.set(SPECULAR); return (*this); }
Builder& withUnlit() { _flags.set(UNLIT); return (*this); }
Builder& withSkinned() { _flags.set(SKINNED); return (*this); }
Builder& withDualQuatSkinned() { _flags.set(DUAL_QUAT_SKINNED); return (*this); }
Builder& withDepthOnly() { _flags.set(DEPTH_ONLY); return (*this); }
Builder& withDepthBias() { _flags.set(DEPTH_BIAS); return (*this); }
Builder& withWireframe() { _flags.set(WIREFRAME); return (*this); }
@ -133,6 +135,9 @@ public:
Builder& withSkinned() { _flags.set(SKINNED); _mask.set(SKINNED); return (*this); }
Builder& withoutSkinned() { _flags.reset(SKINNED); _mask.set(SKINNED); return (*this); }
Builder& withDualQuatSkinned() { _flags.set(DUAL_QUAT_SKINNED); _mask.set(SKINNED); return (*this); }
Builder& withoutDualQuatSkinned() { _flags.reset(DUAL_QUAT_SKINNED); _mask.set(SKINNED); return (*this); }
Builder& withDepthOnly() { _flags.set(DEPTH_ONLY); _mask.set(DEPTH_ONLY); return (*this); }
Builder& withoutDepthOnly() { _flags.reset(DEPTH_ONLY); _mask.set(DEPTH_ONLY); return (*this); }

View file

@ -55,7 +55,7 @@ protected:
inline QDebug operator<<(QDebug debug, const DualQuaternion& dq) {
debug << "AnimPose, real = (" << dq._real.x << dq._real.y << dq._real.z << dq._real.w << "), dual = (" << dq._dual.x << dq._dual.y << dq._dual.z << dq._dual.w << ")";
debug << "DualQuaternion, real = (" << dq._real.x << dq._real.y << dq._real.z << dq._real.w << "), dual = (" << dq._dual.x << dq._dual.y << dq._dual.z << dq._dual.w << ")";
return debug;
}

View file

@ -601,3 +601,6 @@ glm::vec3 randVector() {
return glm::vec3(randFloat() - 0.5f, randFloat() - 0.5f, randFloat() - 0.5f) * 2.0f;
}
bool isNonUniformScale(const glm::vec3& scale) {
return fabsf(scale.x - scale.y) > EPSILON || fabsf(scale.y - scale.z) > EPSILON || fabsf(scale.z - scale.x) > EPSILON;
}

View file

@ -260,6 +260,8 @@ glm::mat4 orthoInverse(const glm::mat4& m);
// Return a random vector of average length 1
glm::vec3 randVector();
bool isNonUniformScale(const glm::vec3& scale);
//
// Safe replacement of glm_mat4_mul() for unaligned arguments instead of __m128
//

View file

@ -14,6 +14,10 @@
#include <mutex>
#ifdef Q_OS_WIN
#include <windows.h>
#endif
#include <QtCore/QCoreApplication>
#include <QtCore/QDateTime>
#include <QtCore/QDebug>
@ -184,8 +188,13 @@ QString LogHandler::printMessage(LogMsgType type, const QMessageLogContext& cont
}
}
QString logMessage = QString("%1 %2").arg(prefixString, message.split('\n').join('\n' + prefixString + " "));
fprintf(stdout, "%s\n", qPrintable(logMessage));
QString logMessage = QString("%1 %2\n").arg(prefixString, message.split('\n').join('\n' + prefixString + " "));
fprintf(stdout, "%s", qPrintable(logMessage));
#ifdef Q_OS_WIN
// On windows, this will output log lines into the Visual Studio "output" tab
OutputDebugStringA(qPrintable(logMessage));
#endif
return logMessage;
}

View file

@ -23,51 +23,69 @@
#include "SharedUtil.h"
namespace Setting {
static QSharedPointer<Manager> globalManager;
// This should only run as a post-routine in the QCoreApplication destructor
void cleanupSettingsSaveThread() {
auto globalManager = DependencyManager::get<Manager>();
Q_ASSERT(qApp && globalManager);
// cleans up the settings private instance. Should only be run once at closing down.
void cleanupPrivateInstance() {
// grab the thread before we nuke the instance
QThread* settingsManagerThread = DependencyManager::get<Manager>()->thread();
// Grab the settings thread to shut it down
QThread* settingsManagerThread = globalManager->thread();
// tell the private instance to clean itself up on its thread
DependencyManager::destroy<Manager>();
globalManager.reset();
// quit the settings manager thread and wait on it to make sure it's gone
// Quit the settings manager thread and wait for it so we
// don't get concurrent accesses when we save all settings below
settingsManagerThread->quit();
settingsManagerThread->wait();
// [IMPORTANT] Save all settings when the QApplication goes down
globalManager->saveAll();
qCDebug(shared) << "Settings thread stopped.";
}
void setupPrivateInstance() {
// Ensure Setting::init has already ran and qApp exists
if (qApp && globalManager) {
// Let's set up the settings Private instance on its own thread
QThread* thread = new QThread();
Q_CHECK_PTR(thread);
thread->setObjectName("Settings Thread");
// This should only run as a pre-routine in the QCoreApplication constructor
void setupSettingsSaveThread() {
auto globalManager = DependencyManager::get<Manager>();
Q_ASSERT(qApp && globalManager);
QObject::connect(thread, SIGNAL(started()), globalManager.data(), SLOT(startTimer()));
QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
QObject::connect(thread, SIGNAL(finished()), globalManager.data(), SLOT(deleteLater()));
globalManager->moveToThread(thread);
thread->start();
qCDebug(shared) << "Settings thread started.";
// Let's set up the settings private instance on its own thread
QThread* thread = new QThread(qApp);
Q_CHECK_PTR(thread);
thread->setObjectName("Settings Thread");
// Register cleanupPrivateInstance to run inside QCoreApplication's destructor.
qAddPostRoutine(cleanupPrivateInstance);
}
// Setup setting periodical save timer
QObject::connect(thread, &QThread::started, globalManager.data(), &Manager::startTimer);
QObject::connect(thread, &QThread::finished, globalManager.data(), &Manager::stopTimer);
// Setup manager threading affinity
// This makes the timer fire on the settings thread so we don't block the main
// thread with a lot of file I/O.
// We bring back the manager to the main thread when the QApplication goes down
globalManager->moveToThread(thread);
QObject::connect(thread, &QThread::finished, globalManager.data(), [] {
auto globalManager = DependencyManager::get<Manager>();
Q_ASSERT(qApp && globalManager);
// Move manager back to the main thread (has to be done on owning thread)
globalManager->moveToThread(qApp->thread());
});
// Start the settings save thread
thread->start();
qCDebug(shared) << "Settings thread started.";
// Register cleanupSettingsSaveThread to run inside QCoreApplication's destructor.
// This will cleanup the settings thread and save all settings before shut down.
qAddPostRoutine(cleanupSettingsSaveThread);
}
FIXED_Q_COREAPP_STARTUP_FUNCTION(setupPrivateInstance)
// Sets up the settings private instance. Should only be run once at startup. preInit() must be run beforehand,
// Sets up the settings private instance. Should only be run once at startup.
void init() {
// Set settings format
QSettings::setDefaultFormat(JSON_FORMAT);
QSettings settings;
qCDebug(shared) << "Settings file:" << settings.fileName();
// Backward compatibility for old settings file
if (settings.allKeys().isEmpty()) {
loadOldINIFile(settings);
}
@ -80,11 +98,13 @@ namespace Setting {
qCDebug(shared) << (deleted ? "Deleted" : "Failed to delete") << "settings lock file" << settingsLockFilename;
}
globalManager = DependencyManager::set<Manager>();
// Setup settings manager, the manager will live until the process shuts down
DependencyManager::set<Manager>();
setupPrivateInstance();
// Add pre-routine to setup threading
qAddPreRoutine(setupSettingsSaveThread);
}
void Interface::init() {
if (!DependencyManager::isSet<Manager>()) {
// WARNING: As long as we are using QSettings this should always be triggered for each Setting::Handle

View file

@ -23,11 +23,7 @@ namespace Setting {
// Cleanup timer
stopTimer();
delete _saveTimer;
// Save all settings before exit
saveAll();
// sync will be called in the QSettings destructor
_saveTimer = nullptr;
}
// Custom deleter does nothing, because we need to shutdown later than the dependency manager

View file

@ -50,8 +50,8 @@ namespace Setting {
QHash<QString, QVariant> _pendingChanges;
friend class Interface;
friend void cleanupPrivateInstance();
friend void setupPrivateInstance();
friend void cleanupSettingsSaveThread();
friend void setupSettingsSaveThread();
};
}

View file

@ -59,16 +59,32 @@ extern "C" FILE * __cdecl __iob_func(void) {
#include <QSysInfo>
#include <QThread>
#include <BuildInfo.h>
#include "LogHandler.h"
#include "NumericalConstants.h"
#include "OctalCode.h"
#include "SharedLogging.h"
// Global instances are stored inside the QApplication properties
// to provide a single instance across DLL boundaries.
// This is something we cannot do here since several DLLs
// and our main binaries statically link this "shared" library
// resulting in multiple static memory blocks in different constexts
// But we need to be able to use global instances before the QApplication
// is setup, so to accomplish that we stage the global instances in a local
// map and setup a pre routine (commitGlobalInstances) that will run in the
// QApplication constructor and commit all the staged instances to the
// QApplication properties.
// Note: One of the side effects of this, is that no DLL loaded before
// the QApplication is constructed, can expect to access the existing staged
// global instanced. For this reason, we advise all DLLs be loaded after
// the QApplication is instanced.
static std::mutex stagedGlobalInstancesMutex;
static std::unordered_map<std::string, QVariant> stagedGlobalInstances;
std::mutex& globalInstancesMutex() {
static std::mutex mutex;
return mutex;
return stagedGlobalInstancesMutex;
}
static void commitGlobalInstances() {
@ -78,7 +94,13 @@ static void commitGlobalInstances() {
}
stagedGlobalInstances.clear();
}
FIXED_Q_COREAPP_STARTUP_FUNCTION(commitGlobalInstances)
// This call is necessary for global instances to work across DLL boundaries
// Ideally, this founction would be called at the top of the main function.
// See description at the top of the file.
void setupGlobalInstances() {
qAddPreRoutine(commitGlobalInstances);
}
QVariant getGlobalInstance(const char* propertyName) {
if (qApp) {
@ -813,8 +835,8 @@ bool similarStrings(const QString& stringA, const QString& stringB) {
}
void disableQtBearerPoll() {
// to disable the Qt constant wireless scanning, set the env for polling interval
qDebug() << "Disabling Qt wireless polling by using a negative value for QTimer::setInterval";
// To disable the Qt constant wireless scanning, set the env for polling interval to -1
// The constant polling causes ping spikes on windows every 10 seconds or so that affect the audio
const QByteArray DISABLE_BEARER_POLL_TIMEOUT = QString::number(-1).toLocal8Bit();
qputenv("QT_BEARER_POLL_TIMEOUT", DISABLE_BEARER_POLL_TIMEOUT);
}
@ -1176,6 +1198,32 @@ void watchParentProcess(int parentPID) {
}
#endif
void setupHifiApplication(QString applicationName) {
disableQtBearerPoll(); // Fixes wifi ping spikes
// Those calls are necessary to format the log correctly
// and to direct the application to the correct location
// for read/writes into AppData and other platform equivalents.
QCoreApplication::setApplicationName(applicationName);
QCoreApplication::setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION);
QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN);
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
// This ensures the global instances mechanism is correctly setup.
// You can find more details as to why this is important in the SharedUtil.h/cpp files
setupGlobalInstances();
#ifndef WIN32
// Windows tends to hold onto log lines until it has a sizeable buffer
// This makes the log feel unresponsive and trap useful log data in the log buffer
// when a crash occurs.
//Force windows to flush the buffer on each new line character to avoid this.
setvbuf(stdout, NULL, _IOLBF, 0);
#endif
// Install the standard hifi message handler so we get consistant log formatting
qInstallMessageHandler(LogHandler::verboseMessageHandler);
}
#ifdef Q_OS_WIN
QString getLastErrorAsString() {

View file

@ -25,23 +25,6 @@
#include <QtCore/QCoreApplication>
#include <QUuid>
// Workaround for https://bugreports.qt.io/browse/QTBUG-54479
// Wrap target function inside another function that holds
// a unique string identifier and uses it to ensure it only runs once
// by storing a state within the qApp
// We cannot used std::call_once with a static once_flag because
// this is used in shared libraries that are linked by several DLLs
// (ie. plugins), meaning the static will be useless in that case
#define FIXED_Q_COREAPP_STARTUP_FUNCTION(AFUNC) \
static void AFUNC ## _fixed() { \
const auto propertyName = std::string(Q_FUNC_INFO) + __FILE__; \
if (!qApp->property(propertyName.c_str()).toBool()) { \
AFUNC(); \
qApp->setProperty(propertyName.c_str(), QVariant(true)); \
} \
} \
Q_COREAPP_STARTUP_FUNCTION(AFUNC ## _fixed)
// When writing out avatarEntities to a QByteArray, if the parentID is the ID of MyAvatar, use this ID instead. This allows
// the value to be reset when the sessionID changes.
const QUuid AVATAR_SELF_ID = QUuid("{00000000-0000-0000-0000-000000000001}");
@ -53,20 +36,11 @@ std::unique_ptr<T>& globalInstancePointer() {
return instancePtr;
}
template <typename T>
void setGlobalInstance(const char* propertyName, T* instance) {
globalInstancePointer<T>().reset(instance);
}
template <typename T>
bool destroyGlobalInstance() {
std::unique_ptr<T>& instancePtr = globalInstancePointer<T>();
if (instancePtr.get()) {
instancePtr.reset();
return true;
}
return false;
}
// Sets up the global instances for use
// This NEEDS to be called on startup
// for any binary planing on using global instances
// More details in cpp file
void setupGlobalInstances();
std::mutex& globalInstancesMutex();
QVariant getGlobalInstance(const char* propertyName);
@ -78,7 +52,6 @@ void setGlobalInstance(const char* propertyName, const QVariant& variant);
template <typename T, typename... Args>
T* globalInstance(const char* propertyName, Args&&... args) {
static T* resultInstance { nullptr };
static std::mutex mutex;
if (!resultInstance) {
std::unique_lock<std::mutex> lock(globalInstancesMutex());
if (!resultInstance) {
@ -260,6 +233,7 @@ void watchParentProcess(int parentPID);
bool processIsRunning(int64_t pid);
void setupHifiApplication(QString applicationName);
#ifdef Q_OS_WIN
void* createProcessGroup();

View file

@ -625,8 +625,7 @@ private:
auto windows = qApp->topLevelWindows();
QWindow* result = nullptr;
for (auto window : windows) {
QVariant isMainWindow = window->property("MainWindow");
if (!qobject_cast<QQuickWindow*>(window)) {
if (window->objectName().contains("MainWindow")) {
result = window;
break;
}

View file

@ -15,6 +15,7 @@
(function() { // BEGIN LOCAL_SCOPE
var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png";
var HELP_URL = Script.resourcesPath() + "html/tabletHelp.html";
var buttonName = "HELP";
var onHelpScreen = false;
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
@ -39,7 +40,7 @@
}
function onScreenChanged(type, url) {
onHelpScreen = type === "Web" && url.startsWith("../../../html/tabletHelp.html");
onHelpScreen = type === "Web" && url.startsWith(HELP_URL);
button.editProperties({ isActive: onHelpScreen });
}

View file

@ -42,25 +42,28 @@
</div>
</fieldset>
<fieldset id="general" class="major">
<div class="shape-group shape-section property dropdown" id="shape-list">
<label for="property-shape">Shape</label>
<select name="SelectShape" id="property-shape">
<option value="Cube">Box</option>
<option value="Sphere">Sphere</option>
<option value="Tetrahedron">Tetrahedron</option>
<option value="Octahedron">Octahedron</option>
<option value="Icosahedron">Icosahedron</option>
<option value="Dodecahedron">Dodecahedron</option>
<option value="Hexagon">Hexagon</option>
<option value="Triangle">Triangle</option>
<option value="Octagon">Octagon</option>
<option value="Cylinder">Cylinder</option>
<option value="Cone">Cone</option>
<option value="Circle">Circle</option>
<option value="Quad">Quad</option>
</select>
<option value="Cube">Box</option>
<option value="Sphere">Sphere</option>
<option value="Tetrahedron">Tetrahedron</option>
<option value="Octahedron">Octahedron</option>
<option value="Icosahedron">Icosahedron</option>
<option value="Dodecahedron">Dodecahedron</option>
<option value="Hexagon">Hexagon</option>
<option value="Triangle">Triangle</option>
<option value="Octagon">Octagon</option>
<option value="Cylinder">Cylinder</option>
<option value="Cone">Cone</option>
<option value="Circle">Circle</option>
<option value="Quad">Quad</option>
</select>
</div>
<div class="can-cast-shadow-section property checkbox">
<input type="checkbox" id="property-can-cast-shadow">
<label for="property-can-cast-shadow">Can cast shadow</label>
</div>
<div class="property text">
<label for="property-name">Name</label>
@ -293,7 +296,6 @@
</div>
</fieldset>
<fieldset id="behavior" class="major">
<legend class="section-header behavior-group">
Behavior<span class=".collapse-icon">M</span>
@ -365,8 +367,6 @@
</div>
</fieldset>
<fieldset id="light" class="major">
<legend class="section-header light-group light-section">
Light<span class=".collapse-icon">M</span>
@ -400,7 +400,6 @@
</fieldset>
</fieldset>
<fieldset id="model" class="major">
<legend class="section-header model-group model-section zone-section">
Model<span class=".collapse-icon">M</span>
@ -484,7 +483,6 @@
</fieldset>
</fieldset>
<fieldset id="zone" class="major">
<legend class="section-header zone-group zone-section">
Zone<span class=".collapse-icon">M</span>
@ -532,6 +530,10 @@
<div></div>
</div>
</div>
<div class="zone-group zone-section keylight-section property checkbox">
<input type="checkbox" id="property-zone-key-light-cast-shadows">
<label for="property-zone-key-light-cast-shadows">Cast Shadows</label>
</div>
<fieldset class="minor">
<legend class="sub-section-header zone-group zone-section background-section">
Skybox
@ -676,7 +678,7 @@
min="-1000" max="50000" step="10"></div>
</div>
</fieldset-->
</fieldset>
</fieldset>
</fieldset>
<fieldset id="text" class="major">
<legend class="section-header text-group text-section">

View file

@ -616,6 +616,8 @@ function loaded() {
var elShape = document.getElementById("property-shape");
var elCanCastShadow = document.getElementById("property-can-cast-shadow");
var elLightSpotLight = document.getElementById("property-light-spot-light");
var elLightColor = document.getElementById("property-light-color");
var elLightColorRed = document.getElementById("property-light-color-red");
@ -687,6 +689,8 @@ function loaded() {
var elZoneKeyLightDirectionX = document.getElementById("property-zone-key-light-direction-x");
var elZoneKeyLightDirectionY = document.getElementById("property-zone-key-light-direction-y");
var elZoneKeyLightCastShadows = document.getElementById("property-zone-key-light-cast-shadows");
// Skybox
var elZoneSkyboxModeInherit = document.getElementById("property-zone-skybox-mode-inherit");
var elZoneSkyboxModeDisabled = document.getElementById("property-zone-skybox-mode-disabled");
@ -844,7 +848,6 @@ function loaded() {
elLocked.checked = properties.locked;
elName.value = properties.name;
elVisible.checked = properties.visible;
@ -1011,6 +1014,12 @@ function loaded() {
properties.color.green + "," + properties.color.blue + ")";
}
if (properties.type === "Model" ||
properties.type === "Shape" || properties.type === "Box" || properties.type === "Sphere") {
elCanCastShadow.checked = properties.canCastShadow;
}
if (properties.type === "Model") {
elModelURL.value = properties.modelURL;
elShapeType.value = properties.shapeType;
@ -1060,7 +1069,6 @@ function loaded() {
elLightFalloffRadius.value = properties.falloffRadius.toFixed(1);
elLightExponent.value = properties.exponent.toFixed(2);
elLightCutoff.value = properties.cutoff.toFixed(2);
} else if (properties.type === "Zone") {
// Key light
elZoneKeyLightModeInherit.checked = (properties.keyLightMode === 'inherit');
@ -1076,6 +1084,8 @@ function loaded() {
elZoneKeyLightDirectionX.value = properties.keyLight.direction.x.toFixed(2);
elZoneKeyLightDirectionY.value = properties.keyLight.direction.y.toFixed(2);
elZoneKeyLightCastShadows.checked = properties.keyLight.castShadows;
// Skybox
elZoneSkyboxModeInherit.checked = (properties.skyboxMode === 'inherit');
elZoneSkyboxModeDisabled.checked = (properties.skyboxMode === 'disabled');
@ -1139,13 +1149,15 @@ function loaded() {
// Show/hide sections as required
showElements(document.getElementsByClassName('skybox-section'),
elZoneSkyboxModeEnabled.checked);
showElements(document.getElementsByClassName('keylight-section'),
elZoneKeyLightModeEnabled.checked);
showElements(document.getElementsByClassName('ambient-section'),
elZoneAmbientLightModeEnabled.checked);
showElements(document.getElementsByClassName('haze-section'),
elZoneHazeModeEnabled.checked);
} else if (properties.type === "PolyVox") {
elVoxelVolumeSizeX.value = properties.voxelVolumeSize.x.toFixed(2);
elVoxelVolumeSizeY.value = properties.voxelVolumeSize.y.toFixed(2);
@ -1176,6 +1188,15 @@ function loaded() {
elMaterialMappingRot.value = properties.materialMappingRot.toFixed(2);
}
// Only these types can cast a shadow
if (properties.type === "Model" ||
properties.type === "Shape" || properties.type === "Box" || properties.type === "Sphere") {
showElements(document.getElementsByClassName('can-cast-shadow-section'), true);
} else {
showElements(document.getElementsByClassName('can-cast-shadow-section'), false);
}
if (properties.locked) {
disableProperties();
elLocked.removeAttribute('disabled');
@ -1432,6 +1453,8 @@ function loaded() {
elShape.addEventListener('change', createEmitTextPropertyUpdateFunction('shape'));
elCanCastShadow.addEventListener('change', createEmitCheckedPropertyUpdateFunction('canCastShadow'));
elImageURL.addEventListener('change', createImageURLUpdateFunction('textures'));
elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl'));
@ -1590,6 +1613,9 @@ function loaded() {
elZoneKeyLightDirectionX.addEventListener('change', zoneKeyLightDirectionChangeFunction);
elZoneKeyLightDirectionY.addEventListener('change', zoneKeyLightDirectionChangeFunction);
elZoneKeyLightCastShadows.addEventListener('change',
createEmitGroupCheckedPropertyUpdateFunction('keyLight', 'castShadows'));
// Skybox
var skyboxModeChanged = createZoneComponentModeChangedFunction('skyboxMode',
elZoneSkyboxModeInherit, elZoneSkyboxModeDisabled, elZoneSkyboxModeEnabled);

View file

@ -551,11 +551,7 @@ var selectionDisplay = null; // for gridTool.js to ignore
break;
case 'checkout_rezClicked':
case 'purchases_rezClicked':
if (message.itemType === "app") {
console.log("How did you get here? You can't buy apps yet!");
} else {
rezEntity(message.itemHref, message.itemType);
}
rezEntity(message.itemHref, message.itemType);
break;
case 'header_marketplaceImageClicked':
case 'purchases_backClicked':

View file

@ -20,6 +20,7 @@
#include <EntityItemProperties.h>
#include <Octree.h>
#include <PathUtils.h>
#include <SharedUtil.h>
const QString& getTestResourceDir() {
static QString dir;
@ -136,6 +137,8 @@ void testPropertyFlags() {
}
int main(int argc, char** argv) {
setupHifiApplication("Entities Test");
QCoreApplication app(argc, argv);
{
auto start = usecTimestampNow();

View file

@ -45,6 +45,7 @@
#include <TextureCache.h>
#include <PerfStat.h>
#include <PathUtils.h>
#include <SharedUtil.h>
#include <ViewFrustum.h>
#include <gpu/Pipeline.h>
@ -192,7 +193,9 @@ void testSparseRectify() {
}
}
int main(int argc, char** argv) {
int main(int argc, char** argv) {
setupHifiApplication("GPU Test");
testSparseRectify();
// FIXME this test appears to be broken

View file

@ -149,7 +149,8 @@ static const QString TEST_FOLDER { "H:/ktx_cacheold" };
static const QString EXTENSIONS { "*.ktx" };
int mainTemp(int, char**) {
qInstallMessageHandler(messageHandler);
setupHifiApplication("KTX Tests");
auto fileInfoList = QDir { TEST_FOLDER }.entryInfoList(QStringList { EXTENSIONS });
for (auto fileInfo : fileInfoList) {
qDebug() << fileInfo.filePath();

View file

@ -179,18 +179,10 @@ void TestWindow::resizeEvent(QResizeEvent* ev) {
resizeWindow(ev->size());
}
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
if (!message.isEmpty()) {
#ifdef Q_OS_WIN
OutputDebugStringA(message.toLocal8Bit().constData());
OutputDebugStringA("\n");
#endif
}
}
int main(int argc, char** argv) {
setupHifiApplication("QML Test");
int main(int argc, char** argv) {
QGuiApplication app(argc, argv);
qInstallMessageHandler(messageHandler);
TestWindow window;
app.exec();
return 0;

View file

@ -63,14 +63,11 @@ void Qt59TestApp::finish(int exitCode) {
int main(int argc, char * argv[]) {
QCoreApplication::setApplicationName("Qt59Test");
QCoreApplication::setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION);
QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN);
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
setupHifiApplication("Qt59Test");
Qt59TestApp app(argc, argv);
return app.exec();
}
#include "main.moc"
#include "main.moc"

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