mirror of
https://github.com/overte-org/overte.git
synced 2025-04-08 14:12:50 +02:00
Merge branch 'master' of git://github.com/highfidelity/hifi into scribe
This commit is contained in:
commit
cdb94d0832
65 changed files with 1111 additions and 663 deletions
|
@ -439,7 +439,7 @@ void Agent::executeScript() {
|
|||
encodedBuffer = audio;
|
||||
}
|
||||
|
||||
AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber,
|
||||
AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber, false,
|
||||
audioTransform, scriptedAvatar->getWorldPosition(), glm::vec3(0),
|
||||
packetType, _selectedCodecName);
|
||||
});
|
||||
|
|
|
@ -275,17 +275,28 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) {
|
|||
if (micStreamIt == _audioStreams.end()) {
|
||||
// we don't have a mic stream yet, so add it
|
||||
|
||||
// read the channel flag to see if our stream is stereo or not
|
||||
// hop past the sequence number that leads the packet
|
||||
message.seek(sizeof(quint16));
|
||||
|
||||
quint8 channelFlag;
|
||||
message.readPrimitive(&channelFlag);
|
||||
// pull the codec string from the packet
|
||||
auto codecString = message.readString();
|
||||
|
||||
bool isStereo = channelFlag == 1;
|
||||
// determine if the stream is stereo or not
|
||||
bool isStereo;
|
||||
if (packetType == PacketType::SilentAudioFrame
|
||||
|| packetType == PacketType::ReplicatedSilentAudioFrame) {
|
||||
quint16 numSilentSamples;
|
||||
message.readPrimitive(&numSilentSamples);
|
||||
isStereo = numSilentSamples == AudioConstants::NETWORK_FRAME_SAMPLES_STEREO;
|
||||
} else {
|
||||
quint8 channelFlag;
|
||||
message.readPrimitive(&channelFlag);
|
||||
isStereo = channelFlag == 1;
|
||||
}
|
||||
|
||||
auto avatarAudioStream = new AvatarAudioStream(isStereo, AudioMixer::getStaticJitterFrames());
|
||||
avatarAudioStream->setupCodec(_codec, _selectedCodecName, AudioConstants::MONO);
|
||||
qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName;
|
||||
avatarAudioStream->setupCodec(_codec, _selectedCodecName, isStereo ? AudioConstants::STEREO : AudioConstants::MONO);
|
||||
qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName << "isStereo:" << isStereo;
|
||||
|
||||
connect(avatarAudioStream, &InboundAudioStream::mismatchedAudioCodec,
|
||||
this, &AudioMixerClientData::handleMismatchAudioFormat);
|
||||
|
@ -324,7 +335,7 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) {
|
|||
|
||||
#if INJECTORS_SUPPORT_CODECS
|
||||
injectorStream->setupCodec(_codec, _selectedCodecName, isStereo ? AudioConstants::STEREO : AudioConstants::MONO);
|
||||
qCDebug(audio) << "creating new injectorStream... codec:" << _selectedCodecName;
|
||||
qCDebug(audio) << "creating new injectorStream... codec:" << _selectedCodecName << "isStereo:" << isStereo;
|
||||
#endif
|
||||
|
||||
auto emplaced = _audioStreams.emplace(
|
||||
|
@ -567,7 +578,8 @@ void AudioMixerClientData::setupCodec(CodecPluginPointer codec, const QString& c
|
|||
|
||||
auto avatarAudioStream = getAvatarAudioStream();
|
||||
if (avatarAudioStream) {
|
||||
avatarAudioStream->setupCodec(codec, codecName, AudioConstants::MONO);
|
||||
avatarAudioStream->setupCodec(codec, codecName, avatarAudioStream->isStereo() ? AudioConstants::STEREO : AudioConstants::MONO);
|
||||
qCDebug(audio) << "setting AvatarAudioStream... codec:" << _selectedCodecName << "isStereo:" << avatarAudioStream->isStereo();
|
||||
}
|
||||
|
||||
#if INJECTORS_SUPPORT_CODECS
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <udt/PacketHeaders.h>
|
||||
|
||||
#include "AudioLogging.h"
|
||||
#include "AvatarAudioStream.h"
|
||||
|
||||
AvatarAudioStream::AvatarAudioStream(bool isStereo, int numStaticJitterFrames) :
|
||||
|
@ -41,6 +42,15 @@ int AvatarAudioStream::parseStreamProperties(PacketType type, const QByteArray&
|
|||
_ringBuffer.resizeForFrameSize(isStereo
|
||||
? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO
|
||||
: AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
|
||||
// restart the codec
|
||||
if (_codec) {
|
||||
if (_decoder) {
|
||||
_codec->releaseDecoder(_decoder);
|
||||
}
|
||||
_decoder = _codec->createDecoder(AudioConstants::SAMPLE_RATE, isStereo ? AudioConstants::STEREO : AudioConstants::MONO);
|
||||
}
|
||||
qCDebug(audio) << "resetting AvatarAudioStream... codec:" << _selectedCodecName << "isStereo:" << isStereo;
|
||||
|
||||
_isStereo = isStereo;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,9 +41,9 @@ Item {
|
|||
onNewViewRequestedCallback: {
|
||||
// desktop is not defined for web-entities or tablet
|
||||
if (typeof desktop !== "undefined") {
|
||||
desktop.openBrowserWindow(request, profile);
|
||||
desktop.openBrowserWindow(request, webViewCoreProfile);
|
||||
} else {
|
||||
tabletRoot.openBrowserWindow(request, profile);
|
||||
tabletRoot.openBrowserWindow(request, webViewCoreProfile);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
//
|
||||
// AvatarBrowser.qml
|
||||
//
|
||||
// Created by Bradley Austin Davis on 30 Aug 2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import QtWebChannel 1.0
|
||||
import QtWebEngine 1.2
|
||||
|
||||
import "../../windows"
|
||||
import "../../controls-uit"
|
||||
import "../../styles-uit"
|
||||
|
||||
Window {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
width: 900; height: 700
|
||||
resizable: true
|
||||
modality: Qt.ApplicationModal
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: true
|
||||
property bool punctuationMode: false
|
||||
|
||||
BaseWebView {
|
||||
id: webview
|
||||
url: Account.metaverseServerURL + "/marketplace?category=avatars"
|
||||
focus: true
|
||||
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: keyboard.top
|
||||
}
|
||||
|
||||
// Create a global EventBridge object for raiseAndLowerKeyboard.
|
||||
WebEngineScript {
|
||||
id: createGlobalEventBridge
|
||||
sourceCode: eventBridgeJavaScriptToInject
|
||||
injectionPoint: WebEngineScript.DocumentCreation
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
// Detect when may want to raise and lower keyboard.
|
||||
WebEngineScript {
|
||||
id: raiseAndLowerKeyboard
|
||||
injectionPoint: WebEngineScript.Deferred
|
||||
sourceUrl: resourceDirectoryUrl + "html/raiseAndLowerKeyboard.js"
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
Keyboard {
|
||||
id: keyboard
|
||||
raised: parent.keyboardEnabled && parent.keyboardRaised
|
||||
numeric: parent.punctuationMode
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
keyboardEnabled = HMD.active;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -99,25 +99,9 @@ Preference {
|
|||
leftMargin: dataTextField.acceptableInput ? hifi.dimensions.contentSpacing.x : 0
|
||||
}
|
||||
onClicked: {
|
||||
if (typeof desktop !== "undefined") {
|
||||
// Load dialog via OffscreenUi so that JavaScript EventBridge is available.
|
||||
root.browser = OffscreenUi.load("dialogs/preferences/AvatarBrowser.qml");
|
||||
root.browser.windowDestroyed.connect(function(){
|
||||
root.browser = null;
|
||||
});
|
||||
} else {
|
||||
root.browser = tabletAvatarBrowserBuilder.createObject(tabletRoot);
|
||||
|
||||
// Make dialog modal.
|
||||
tabletRoot.openModal = root.browser;
|
||||
}
|
||||
ApplicationInterface.loadAvatarBrowser();
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: tabletAvatarBrowserBuilder;
|
||||
TabletAvatarBrowser { }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ Item {
|
|||
|
||||
id: root;
|
||||
property string keyFilePath;
|
||||
property bool showDebugButtons: true;
|
||||
|
||||
Connections {
|
||||
target: Commerce;
|
||||
|
@ -55,37 +54,6 @@ Item {
|
|||
// Style
|
||||
color: hifi.colors.blueHighlight;
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
id: clearCachedPassphraseButton;
|
||||
visible: root.showDebugButtons;
|
||||
color: hifi.buttons.black;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: parent.top;
|
||||
anchors.left: helpTitleText.right;
|
||||
anchors.leftMargin: 20;
|
||||
height: 40;
|
||||
width: 150;
|
||||
text: "DBG: Clear Pass";
|
||||
onClicked: {
|
||||
Commerce.setPassphrase("");
|
||||
sendSignalToWallet({method: 'passphraseReset'});
|
||||
}
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
id: resetButton;
|
||||
visible: root.showDebugButtons;
|
||||
color: hifi.buttons.red;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.top: clearCachedPassphraseButton.top;
|
||||
anchors.left: clearCachedPassphraseButton.right;
|
||||
height: 40;
|
||||
width: 150;
|
||||
text: "DBG: RST Wallet";
|
||||
onClicked: {
|
||||
Commerce.reset();
|
||||
sendSignalToWallet({method: 'walletReset'});
|
||||
}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: helpModel;
|
||||
|
|
|
@ -22,7 +22,6 @@ Item {
|
|||
anchors.fill: parent
|
||||
id: d
|
||||
objectName: "stack"
|
||||
initialItem: topMenu
|
||||
|
||||
property var menuStack: []
|
||||
property var topMenu: null;
|
||||
|
|
|
@ -1,111 +0,0 @@
|
|||
//
|
||||
// TabletAvatarBrowser.qml
|
||||
//
|
||||
// Created by David Rowe on 14 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 QtWebChannel 1.0
|
||||
import QtWebEngine 1.2
|
||||
|
||||
import "../../../../windows"
|
||||
import "../../../../controls-uit"
|
||||
import "../../../../styles-uit"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
objectName: "ModelBrowserDialog"
|
||||
|
||||
property string title: "Attachment Model"
|
||||
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
BaseWebView {
|
||||
id: webview
|
||||
url: (Account.metaverseServerURL + "/marketplace?category=avatars")
|
||||
focus: true
|
||||
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: footer.top
|
||||
}
|
||||
|
||||
// Create a global EventBridge object for raiseAndLowerKeyboard.
|
||||
WebEngineScript {
|
||||
id: createGlobalEventBridge
|
||||
sourceCode: eventBridgeJavaScriptToInject
|
||||
injectionPoint: WebEngineScript.DocumentCreation
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
// Detect when may want to raise and lower keyboard.
|
||||
WebEngineScript {
|
||||
id: raiseAndLowerKeyboard
|
||||
injectionPoint: WebEngineScript.Deferred
|
||||
sourceUrl: resourceDirectoryUrl + "html/raiseAndLowerKeyboard.js"
|
||||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
|
||||
|
||||
Component.onCompleted: {
|
||||
webChannel.registerObject("eventBridge", eventBridge);
|
||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: footer
|
||||
height: 40
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: keyboard.top
|
||||
}
|
||||
|
||||
color: hifi.colors.baseGray
|
||||
|
||||
Row {
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
right: parent.right
|
||||
rightMargin: hifi.dimensions.contentMargin.x
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Cancel"
|
||||
color: hifi.buttons.white
|
||||
onClicked: root.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keyboard {
|
||||
id: keyboard
|
||||
|
||||
raised: parent.keyboardEnabled && parent.keyboardRaised
|
||||
numeric: parent.punctuationMode
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
keyboardEnabled = HMD.active;
|
||||
}
|
||||
}
|
|
@ -6178,7 +6178,7 @@ void Application::showAssetServerWidget(QString filePath) {
|
|||
if (!hmd->getShouldShowTablet() && !isHMDMode()) {
|
||||
DependencyManager::get<OffscreenUi>()->show(url, "AssetServer", startUpload);
|
||||
} else {
|
||||
static const QUrl url("hifi/dialogs/TabletAssetServer.qml");
|
||||
static const QUrl url("../dialogs/TabletAssetServer.qml");
|
||||
tablet->pushOntoStack(url);
|
||||
}
|
||||
}
|
||||
|
@ -6807,6 +6807,15 @@ void Application::loadAddAvatarBookmarkDialog() const {
|
|||
avatarBookmarks->addBookmark();
|
||||
}
|
||||
|
||||
void Application::loadAvatarBrowser() const {
|
||||
auto tablet = dynamic_cast<TabletProxy*>(DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system"));
|
||||
// construct the url to the marketplace item
|
||||
QString url = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace?category=avatars";
|
||||
QString MARKETPLACES_INJECT_SCRIPT_PATH = "file:///" + qApp->applicationDirPath() + "/scripts/system/html/js/marketplacesInject.js";
|
||||
tablet->gotoWebScreen(url, MARKETPLACES_INJECT_SCRIPT_PATH);
|
||||
DependencyManager::get<HMDScriptingInterface>()->openTablet();
|
||||
}
|
||||
|
||||
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) {
|
||||
postLambdaEvent([notify, includeAnimated, aspectRatio, this] {
|
||||
// Get a screenshot and save it
|
||||
|
|
|
@ -309,6 +309,7 @@ public slots:
|
|||
void toggleEntityScriptServerLogDialog();
|
||||
Q_INVOKABLE void showAssetServerWidget(QString filePath = "");
|
||||
Q_INVOKABLE void loadAddAvatarBookmarkDialog() const;
|
||||
Q_INVOKABLE void loadAvatarBrowser() const;
|
||||
Q_INVOKABLE SharedSoundPointer getSampleSound() const;
|
||||
|
||||
void showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const QString& name) const;
|
||||
|
|
|
@ -189,13 +189,6 @@ void Ledger::history(const QStringList& keys, const int& pageNumber) {
|
|||
keysQuery("history", "historySuccess", "historyFailure", params);
|
||||
}
|
||||
|
||||
// The api/failResponse is called just for the side effect of logging.
|
||||
void Ledger::resetSuccess(QNetworkReply& reply) { apiResponse("reset", reply); }
|
||||
void Ledger::resetFailure(QNetworkReply& reply) { failResponse("reset", reply); }
|
||||
void Ledger::reset() {
|
||||
send("reset_user_hfc_account", "resetSuccess", "resetFailure", QNetworkAccessManager::PutOperation, AccountManagerAuth::Required, QJsonObject());
|
||||
}
|
||||
|
||||
void Ledger::accountSuccess(QNetworkReply& reply) {
|
||||
// lets set the appropriate stuff in the wallet now
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
|
|
|
@ -31,7 +31,6 @@ public:
|
|||
void inventory(const QStringList& keys);
|
||||
void history(const QStringList& keys, const int& pageNumber);
|
||||
void account();
|
||||
void reset();
|
||||
void updateLocation(const QString& asset_id, const QString location, const bool controlledFailure = false);
|
||||
void certificateInfo(const QString& certificateId);
|
||||
|
||||
|
@ -66,8 +65,6 @@ public slots:
|
|||
void inventoryFailure(QNetworkReply& reply);
|
||||
void historySuccess(QNetworkReply& reply);
|
||||
void historyFailure(QNetworkReply& reply);
|
||||
void resetSuccess(QNetworkReply& reply);
|
||||
void resetFailure(QNetworkReply& reply);
|
||||
void accountSuccess(QNetworkReply& reply);
|
||||
void accountFailure(QNetworkReply& reply);
|
||||
void updateLocationSuccess(QNetworkReply& reply);
|
||||
|
|
|
@ -128,18 +128,6 @@ void QmlCommerce::generateKeyPair() {
|
|||
getWalletAuthenticatedStatus();
|
||||
}
|
||||
|
||||
void QmlCommerce::reset() {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
ledger->reset();
|
||||
wallet->reset();
|
||||
}
|
||||
|
||||
void QmlCommerce::resetLocalWalletOnly() {
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
wallet->reset();
|
||||
}
|
||||
|
||||
void QmlCommerce::account() {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
ledger->account();
|
||||
|
|
|
@ -62,8 +62,6 @@ protected:
|
|||
Q_INVOKABLE void inventory();
|
||||
Q_INVOKABLE void history(const int& pageNumber);
|
||||
Q_INVOKABLE void generateKeyPair();
|
||||
Q_INVOKABLE void reset();
|
||||
Q_INVOKABLE void resetLocalWalletOnly();
|
||||
Q_INVOKABLE void account();
|
||||
|
||||
Q_INVOKABLE void certificateInfo(const QString& certificateId);
|
||||
|
|
|
@ -144,15 +144,13 @@ bool writeKeys(const char* filename, EC_KEY* keys) {
|
|||
if ((fp = fopen(filename, "wt"))) {
|
||||
if (!PEM_write_EC_PUBKEY(fp, keys)) {
|
||||
fclose(fp);
|
||||
qCDebug(commerce) << "failed to write public key";
|
||||
QFile(QString(filename)).remove();
|
||||
qCCritical(commerce) << "failed to write public key";
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (!PEM_write_ECPrivateKey(fp, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) {
|
||||
fclose(fp);
|
||||
qCDebug(commerce) << "failed to write private key";
|
||||
QFile(QString(filename)).remove();
|
||||
qCCritical(commerce) << "failed to write private key";
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
@ -168,7 +166,8 @@ bool writeKeys(const char* filename, EC_KEY* keys) {
|
|||
QPair<QByteArray*, QByteArray*> generateECKeypair() {
|
||||
|
||||
EC_KEY* keyPair = EC_KEY_new_by_curve_name(NID_secp256k1);
|
||||
QPair<QByteArray*, QByteArray*> retval;
|
||||
QPair<QByteArray*, QByteArray*> retval{};
|
||||
|
||||
EC_KEY_set_asn1_flag(keyPair, OPENSSL_EC_NAMED_CURVE);
|
||||
if (!EC_KEY_generate_key(keyPair)) {
|
||||
qCDebug(commerce) << "Error generating EC Keypair -" << ERR_get_error();
|
||||
|
@ -517,6 +516,9 @@ bool Wallet::generateKeyPair() {
|
|||
|
||||
qCInfo(commerce) << "Generating keypair.";
|
||||
auto keyPair = generateECKeypair();
|
||||
if (!keyPair.first) {
|
||||
return false;
|
||||
}
|
||||
|
||||
writeBackupInstructions();
|
||||
|
||||
|
@ -653,20 +655,6 @@ QString Wallet::getKeyFilePath() {
|
|||
}
|
||||
}
|
||||
|
||||
void Wallet::reset() {
|
||||
_publicKeys.clear();
|
||||
|
||||
delete _securityImage;
|
||||
_securityImage = nullptr;
|
||||
|
||||
// tell the provider we got nothing
|
||||
updateImageProvider();
|
||||
_passphrase->clear();
|
||||
|
||||
|
||||
QFile keyFile(keyFilePath());
|
||||
keyFile.remove();
|
||||
}
|
||||
bool Wallet::writeWallet(const QString& newPassphrase) {
|
||||
EC_KEY* keys = readKeys(keyFilePath().toStdString().c_str());
|
||||
if (keys) {
|
||||
|
|
|
@ -49,8 +49,6 @@ public:
|
|||
bool walletIsAuthenticatedWithPassphrase();
|
||||
bool changePassphrase(const QString& newPassphrase);
|
||||
|
||||
void reset();
|
||||
|
||||
void getWalletStatus();
|
||||
enum WalletStatus {
|
||||
WALLET_STATUS_NOT_LOGGED_IN = 0,
|
||||
|
|
|
@ -42,7 +42,7 @@ void CustomPromptResultFromScriptValue(const QScriptValue& object, CustomPromptR
|
|||
* @property {number} innerWidth - The width of the drawable area of the Interface window (i.e., without borders or other
|
||||
* chrome), in pixels. <em>Read-only.</em>
|
||||
* @property {number} innerHeight - The height of the drawable area of the Interface window (i.e., without borders or other
|
||||
* chrome) plus the height of the menu bar, in pixels. <em>Read-only.</em>
|
||||
* chrome), in pixels. <em>Read-only.</em>
|
||||
* @property {object} location - Provides facilities for working with your current metaverse location. See {@link location}.
|
||||
* @property {number} x - The x coordinate of the top left corner of the Interface window on the display. <em>Read-only.</em>
|
||||
* @property {number} y - The y coordinate of the top left corner of the Interface window on the display. <em>Read-only.</em>
|
||||
|
@ -301,7 +301,7 @@ public slots:
|
|||
/**jsdoc
|
||||
* Get Interface's build number.
|
||||
* @function Window.checkVersion
|
||||
* @returns {string} - Interface's build number.
|
||||
* @returns {string} Interface's build number.
|
||||
*/
|
||||
QString checkVersion();
|
||||
|
||||
|
@ -327,7 +327,7 @@ public slots:
|
|||
* 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.
|
||||
* @example <caption>Using the snapshot function and signals.</caption>
|
||||
* function onStillSnapshottaken(path, notify) {
|
||||
* function onStillSnapshotTaken(path, notify) {
|
||||
* print("Still snapshot taken: " + path);
|
||||
* print("Notify: " + notify);
|
||||
* }
|
||||
|
@ -340,7 +340,7 @@ public slots:
|
|||
* print("Animated snapshot taken: " + animatedPath);
|
||||
* }
|
||||
*
|
||||
* Window.stillSnapshotTaken.connect(onStillSnapshottaken);
|
||||
* Window.stillSnapshotTaken.connect(onStillSnapshotTaken);
|
||||
* Window.processingGifStarted.connect(onProcessingGifStarted);
|
||||
* Window.processingGifCompleted.connect(onProcessingGifCompleted);
|
||||
*
|
||||
|
@ -555,7 +555,7 @@ signals:
|
|||
|
||||
/**jsdoc
|
||||
* Triggered when a still snapshot has been taken by calling {@link Window.takeSnapshot|takeSnapshot} with
|
||||
* <code>includeAnimated = false</code>.
|
||||
* <code>includeAnimated = false</code> or {@link Window.takeSecondaryCameraSnapshot|takeSecondaryCameraSnapshot}.
|
||||
* @function Window.stillSnapshotTaken
|
||||
* @param {string} pathStillSnapshot - The path and name of the snapshot image file.
|
||||
* @param {boolean} notify - The value of the <code>notify</code> parameter that {@link Window.takeSnapshot|takeSnapshot}
|
||||
|
|
|
@ -591,19 +591,31 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
|
|||
glm::vec3 d = basePose.trans() - topPose.trans();
|
||||
float dLen = glm::length(d);
|
||||
if (dLen > EPSILON) {
|
||||
|
||||
glm::vec3 dUnit = d / dLen;
|
||||
glm::vec3 e = midPose.xformVector(target.getPoleReferenceVector());
|
||||
|
||||
// if mid joint is straight use the reference vector to compute eProj, otherwise use reference vector.
|
||||
// however if mid joint angle is in between the two blend between both solutions.
|
||||
vec3 u = normalize(basePose.trans() - midPose.trans());
|
||||
vec3 v = normalize(topPose.trans() - midPose.trans());
|
||||
|
||||
const float LERP_THRESHOLD = 3.05433f; // 175 deg
|
||||
const float BENT_THRESHOLD = 2.96706f; // 170 deg
|
||||
|
||||
float jointAngle = acos(dot(u, v));
|
||||
if (jointAngle < BENT_THRESHOLD) {
|
||||
glm::vec3 midPoint = topPose.trans() + d * 0.5f;
|
||||
e = normalize(midPose.trans() - midPoint);
|
||||
} else if (jointAngle < LERP_THRESHOLD) {
|
||||
glm::vec3 midPoint = topPose.trans() + d * 0.5f;
|
||||
float alpha = (jointAngle - LERP_THRESHOLD) / (BENT_THRESHOLD - LERP_THRESHOLD);
|
||||
e = lerp(e, normalize(midPose.trans() - midPoint), alpha);
|
||||
}
|
||||
|
||||
glm::vec3 eProj = e - glm::dot(e, dUnit) * dUnit;
|
||||
float eProjLen = glm::length(eProj);
|
||||
|
||||
const float MIN_EPROJ_LEN = 0.5f;
|
||||
if (eProjLen < MIN_EPROJ_LEN) {
|
||||
glm::vec3 midPoint = topPose.trans() + d * 0.5f;
|
||||
e = midPose.trans() - midPoint;
|
||||
eProj = e - glm::dot(e, dUnit) * dUnit;
|
||||
eProjLen = glm::length(eProj);
|
||||
}
|
||||
|
||||
glm::vec3 p = target.getPoleVector();
|
||||
glm::vec3 pProj = p - glm::dot(p, dUnit) * dUnit;
|
||||
float pProjLen = glm::length(pProj);
|
||||
|
@ -634,16 +646,27 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
|
|||
|
||||
AnimPose geomToWorldPose = AnimPose(context.getRigToWorldMatrix() * context.getGeometryToRigMatrix());
|
||||
|
||||
glm::vec3 dUnit = d / dLen;
|
||||
glm::vec3 e = midPose.xformVector(target.getPoleReferenceVector());
|
||||
glm::vec3 eProj = e - glm::dot(e, dUnit) * dUnit;
|
||||
float eProjLen = glm::length(eProj);
|
||||
const float MIN_EPROJ_LEN = 0.5f;
|
||||
if (eProjLen < MIN_EPROJ_LEN) {
|
||||
|
||||
// if mid joint is straight use the reference vector to compute eProj, otherwise use reference vector.
|
||||
// however if mid joint angle is in between the two blend between both solutions.
|
||||
vec3 u = normalize(basePose.trans() - midPose.trans());
|
||||
vec3 v = normalize(topPose.trans() - midPose.trans());
|
||||
|
||||
const float LERP_THRESHOLD = 3.05433f; // 175 deg
|
||||
const float BENT_THRESHOLD = 2.96706f; // 170 deg
|
||||
|
||||
float jointAngle = acos(dot(u, v));
|
||||
glm::vec4 eColor = RED;
|
||||
if (jointAngle < BENT_THRESHOLD) {
|
||||
glm::vec3 midPoint = topPose.trans() + d * 0.5f;
|
||||
e = midPose.trans() - midPoint;
|
||||
eProj = e - glm::dot(e, dUnit) * dUnit;
|
||||
eProjLen = glm::length(eProj);
|
||||
e = normalize(midPose.trans() - midPoint);
|
||||
eColor = GREEN;
|
||||
} else if (jointAngle < LERP_THRESHOLD) {
|
||||
glm::vec3 midPoint = topPose.trans() + d * 0.5f;
|
||||
float alpha = (jointAngle - LERP_THRESHOLD) / (BENT_THRESHOLD - LERP_THRESHOLD);
|
||||
e = lerp(e, normalize(midPose.trans() - midPoint), alpha);
|
||||
eColor = YELLOW;
|
||||
}
|
||||
|
||||
glm::vec3 p = target.getPoleVector();
|
||||
|
@ -655,7 +678,7 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
|
|||
YELLOW);
|
||||
DebugDraw::getInstance().drawRay(geomToWorldPose.xformPoint(midPoint),
|
||||
geomToWorldPose.xformPoint(midPoint + PROJ_VECTOR_LEN * glm::normalize(e)),
|
||||
RED);
|
||||
eColor);
|
||||
DebugDraw::getInstance().drawRay(geomToWorldPose.xformPoint(midPoint),
|
||||
geomToWorldPose.xformPoint(midPoint + POLE_VECTOR_LEN * glm::normalize(p)),
|
||||
BLUE);
|
||||
|
|
|
@ -76,3 +76,5 @@ AnimPose::operator glm::mat4() const {
|
|||
return glm::mat4(glm::vec4(xAxis, 0.0f), glm::vec4(yAxis, 0.0f),
|
||||
glm::vec4(zAxis, 0.0f), glm::vec4(_trans, 1.0f));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1732,6 +1732,14 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const {
|
|||
}
|
||||
}
|
||||
|
||||
AnimPose Rig::getJointPose(int jointIndex) const {
|
||||
if (isIndexValid(jointIndex)) {
|
||||
return _internalPoseSet._absolutePoses[jointIndex];
|
||||
} else {
|
||||
return AnimPose::identity;
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::copyJointsIntoJointData(QVector<JointData>& jointDataVec) const {
|
||||
|
||||
const AnimPose geometryToRigPose(_geometryToRigTransform);
|
||||
|
|
|
@ -164,6 +164,7 @@ public:
|
|||
|
||||
// rig space
|
||||
glm::mat4 getJointTransform(int jointIndex) const;
|
||||
AnimPose getJointPose(int jointIndex) const;
|
||||
|
||||
// Start or stop animations as needed.
|
||||
void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, CharacterControllerState ccState);
|
||||
|
|
|
@ -782,7 +782,7 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) {
|
|||
|
||||
_selectedCodecName = selectedCodecName;
|
||||
|
||||
qCDebug(audioclient) << "Selected Codec:" << _selectedCodecName;
|
||||
qCDebug(audioclient) << "Selected Codec:" << _selectedCodecName << "isStereoInput:" << _isStereoInput;
|
||||
|
||||
// release any old codec encoder/decoder first...
|
||||
if (_codec && _encoder) {
|
||||
|
@ -797,7 +797,7 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) {
|
|||
if (_selectedCodecName == plugin->getName()) {
|
||||
_codec = plugin;
|
||||
_receivedAudioStream.setupCodec(plugin, _selectedCodecName, AudioConstants::STEREO);
|
||||
_encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, AudioConstants::MONO);
|
||||
_encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, _isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO);
|
||||
qCDebug(audioclient) << "Selected Codec Plugin:" << _codec.get();
|
||||
break;
|
||||
}
|
||||
|
@ -1079,7 +1079,7 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) {
|
|||
encodedBuffer = audioBuffer;
|
||||
}
|
||||
|
||||
emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber,
|
||||
emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, _isStereoInput,
|
||||
audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale,
|
||||
packetType, _selectedCodecName);
|
||||
_stats.sentPacket();
|
||||
|
@ -1382,7 +1382,16 @@ void AudioClient::setIsStereoInput(bool isStereoInput) {
|
|||
_desiredInputFormat.setChannelCount(1);
|
||||
}
|
||||
|
||||
// change in channel count for desired input format, restart the input device
|
||||
// restart the codec
|
||||
if (_codec) {
|
||||
if (_encoder) {
|
||||
_codec->releaseEncoder(_encoder);
|
||||
}
|
||||
_encoder = _codec->createEncoder(AudioConstants::SAMPLE_RATE, _isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO);
|
||||
}
|
||||
qCDebug(audioclient) << "Reset Codec:" << _selectedCodecName << "isStereoInput:" << _isStereoInput;
|
||||
|
||||
// restart the input device
|
||||
switchInputToAudioDevice(_inputDeviceInfo);
|
||||
}
|
||||
}
|
||||
|
@ -1418,7 +1427,7 @@ void AudioClient::outputFormatChanged() {
|
|||
_receivedAudioStream.outputFormatChanged(_outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT);
|
||||
}
|
||||
|
||||
bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo, bool isShutdownRequest) {
|
||||
bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest) {
|
||||
qCDebug(audioclient) << __FUNCTION__ << "inputDeviceInfo: [" << inputDeviceInfo.deviceName() << "]";
|
||||
bool supportedFormat = false;
|
||||
|
||||
|
@ -1601,7 +1610,7 @@ void AudioClient::outputNotify() {
|
|||
}
|
||||
}
|
||||
|
||||
bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo, bool isShutdownRequest) {
|
||||
bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest) {
|
||||
qCDebug(audioclient) << "AudioClient::switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]";
|
||||
bool supportedFormat = false;
|
||||
|
||||
|
|
|
@ -378,8 +378,8 @@ private:
|
|||
|
||||
void handleLocalEchoAndReverb(QByteArray& inputByteArray);
|
||||
|
||||
bool switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo, bool isShutdownRequest = false);
|
||||
bool switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo, bool isShutdownRequest = false);
|
||||
bool switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest = false);
|
||||
bool switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest = false);
|
||||
|
||||
// Callback acceleration dependent calculations
|
||||
int calculateNumberOfInputCallbackBytes(const QAudioFormat& format) const;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#include "AudioConstants.h"
|
||||
|
||||
void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber,
|
||||
void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, bool isStereo,
|
||||
const Transform& transform, glm::vec3 avatarBoundingBoxCorner, glm::vec3 avatarBoundingBoxScale,
|
||||
PacketType packetType, QString codecName) {
|
||||
static std::mutex _mutex;
|
||||
|
@ -30,9 +30,6 @@ void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes
|
|||
Locker lock(_mutex);
|
||||
auto audioPacket = NLPacket::create(packetType);
|
||||
|
||||
// FIXME - this is not a good way to determine stereoness with codecs....
|
||||
quint8 isStereo = bytes == AudioConstants::NETWORK_FRAME_BYTES_STEREO ? 1 : 0;
|
||||
|
||||
// write sequence number
|
||||
auto sequence = sequenceNumber++;
|
||||
audioPacket->writePrimitive(sequence);
|
||||
|
@ -48,7 +45,8 @@ void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes
|
|||
audioPacket->writePrimitive(numSilentSamples);
|
||||
} else {
|
||||
// set the mono/stereo byte
|
||||
audioPacket->writePrimitive(isStereo);
|
||||
quint8 channelFlag = isStereo ? 1 : 0;
|
||||
audioPacket->writePrimitive(channelFlag);
|
||||
}
|
||||
|
||||
// pack the three float positions
|
||||
|
|
|
@ -29,7 +29,7 @@ class AbstractAudioInterface : public QObject {
|
|||
public:
|
||||
AbstractAudioInterface(QObject* parent = 0) : QObject(parent) {};
|
||||
|
||||
static void emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber,
|
||||
static void emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, bool isStereo,
|
||||
const Transform& transform, glm::vec3 avatarBoundingBoxCorner, glm::vec3 avatarBoundingBoxScale,
|
||||
PacketType packetType, QString codecName = QString(""));
|
||||
|
||||
|
|
|
@ -185,11 +185,9 @@ public:
|
|||
DEFINE_PROPERTY_REF(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t, PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE);
|
||||
DEFINE_PROPERTY_REF(PROP_NAME, Name, name, QString, ENTITY_ITEM_DEFAULT_NAME);
|
||||
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t, (uint32_t)COMPONENT_MODE_ENABLED);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t, (uint32_t)COMPONENT_MODE_ENABLED);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t, (uint32_t)COMPONENT_MODE_ENABLED);
|
||||
|
||||
// This is the default mode for zone creation
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
|
||||
|
||||
DEFINE_PROPERTY_GROUP(Skybox, skybox, SkyboxPropertyGroup);
|
||||
|
|
|
@ -39,11 +39,9 @@
|
|||
#include "EntityEditFilters.h"
|
||||
#include "EntityDynamicFactoryInterface.h"
|
||||
|
||||
|
||||
static const quint64 DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER = USECS_PER_MSEC * 50;
|
||||
const float EntityTree::DEFAULT_MAX_TMP_ENTITY_LIFETIME = 60 * 60; // 1 hour
|
||||
|
||||
|
||||
// combines the ray cast arguments into a single object
|
||||
class RayArgs {
|
||||
public:
|
||||
|
@ -2237,10 +2235,14 @@ bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElementPointer
|
|||
}
|
||||
|
||||
bool EntityTree::readFromMap(QVariantMap& map) {
|
||||
// These are needed to deal with older content (before adding inheritance modes)
|
||||
int contentVersion = map["Version"].toInt();
|
||||
bool needsConversion = (contentVersion < (int)EntityVersion::ZoneLightInheritModes);
|
||||
|
||||
// map will have a top-level list keyed as "Entities". This will be extracted
|
||||
// and iterated over. Each member of this list is converted to a QVariantMap, then
|
||||
// to a QScriptValue, and then to EntityItemProperties. These properties are used
|
||||
// to add the new entity to the EnitytTree.
|
||||
// to add the new entity to the EntityTree.
|
||||
QVariantList entitiesQList = map["Entities"].toList();
|
||||
QScriptEngine scriptEngine;
|
||||
|
||||
|
@ -2281,29 +2283,30 @@ bool EntityTree::readFromMap(QVariantMap& map) {
|
|||
properties.setOwningAvatarID(myNodeID);
|
||||
}
|
||||
|
||||
// TEMPORARY fix for older content not containing these fields in the zones
|
||||
if (properties.getType() == EntityTypes::EntityType::Zone) {
|
||||
if (!entityMap.contains("keyLightMode")) {
|
||||
properties.setKeyLightMode(COMPONENT_MODE_ENABLED);
|
||||
// Fix for older content not containing these fields in the zones
|
||||
if (needsConversion && (properties.getType() == EntityTypes::EntityType::Zone)) {
|
||||
// The ambient URL has been moved from "keyLight" to "ambientLight"
|
||||
if (entityMap.contains("keyLight")) {
|
||||
QVariantMap keyLightObject = entityMap["keyLight"].toMap();
|
||||
properties.getAmbientLight().setAmbientURL(keyLightObject["ambientURL"].toString());
|
||||
}
|
||||
|
||||
if (!entityMap.contains("skyboxMode")) {
|
||||
if (entityMap.contains("backgroundMode") && (entityMap["backgroundMode"].toString() == "nothing")) {
|
||||
properties.setSkyboxMode(COMPONENT_MODE_INHERIT);
|
||||
} else {
|
||||
// Either the background mode field is missing (shouldn't happen) or the background mode is "skybox"
|
||||
properties.setSkyboxMode(COMPONENT_MODE_ENABLED);
|
||||
|
||||
// Copy the skybox URL if the ambient URL is empty, as this is the legacy behaviour
|
||||
if (properties.getAmbientLight().getAmbientURL() == "") {
|
||||
properties.getAmbientLight().setAmbientURL(properties.getSkybox().getURL());
|
||||
}
|
||||
// The background should be enabled if the mode is skybox
|
||||
// Note that if the values are default then they are not stored in the JSON file
|
||||
if (entityMap.contains("backgroundMode") && (entityMap["backgroundMode"].toString() == "skybox")) {
|
||||
properties.setSkyboxMode(COMPONENT_MODE_ENABLED);
|
||||
|
||||
// Copy the skybox URL if the ambient URL is empty, as this is the legacy behaviour
|
||||
if (properties.getAmbientLight().getAmbientURL() == "") {
|
||||
properties.getAmbientLight().setAmbientURL(properties.getSkybox().getURL());
|
||||
}
|
||||
} else {
|
||||
properties.setSkyboxMode(COMPONENT_MODE_INHERIT);
|
||||
}
|
||||
|
||||
if (!entityMap.contains("ambientLightMode")) {
|
||||
properties.setAmbientLightMode(COMPONENT_MODE_ENABLED);
|
||||
}
|
||||
// The legacy version had no keylight/ambient modes - these are always on
|
||||
properties.setKeyLightMode(COMPONENT_MODE_ENABLED);
|
||||
properties.setAmbientLightMode(COMPONENT_MODE_ENABLED);
|
||||
}
|
||||
|
||||
EntityItemPointer entity = addEntity(entityItemID, properties);
|
||||
|
@ -2312,6 +2315,7 @@ bool EntityTree::readFromMap(QVariantMap& map) {
|
|||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
|
|
@ -116,6 +116,7 @@ public:
|
|||
|
||||
int jointIndex;
|
||||
glm::mat4 inverseBindMatrix;
|
||||
Transform inverseBindTransform;
|
||||
};
|
||||
|
||||
const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048;
|
||||
|
@ -225,7 +226,7 @@ public:
|
|||
QVector<glm::vec2> texCoords;
|
||||
QVector<glm::vec2> texCoords1;
|
||||
QVector<uint16_t> clusterIndices;
|
||||
QVector<uint8_t> clusterWeights;
|
||||
QVector<uint16_t> clusterWeights;
|
||||
QVector<int32_t> originalIndices;
|
||||
|
||||
QVector<FBXCluster> clusters;
|
||||
|
|
|
@ -1675,6 +1675,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
|||
fbxCluster.jointIndex = 0;
|
||||
}
|
||||
fbxCluster.inverseBindMatrix = glm::inverse(cluster.transformLink) * modelTransform;
|
||||
fbxCluster.inverseBindTransform = Transform(fbxCluster.inverseBindMatrix);
|
||||
extracted.mesh.clusters.append(fbxCluster);
|
||||
|
||||
// override the bind rotation with the transform link
|
||||
|
@ -1789,9 +1790,9 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
|||
}
|
||||
if (totalWeight > 0.0f) {
|
||||
const float ALMOST_HALF = 0.499f;
|
||||
float weightScalingFactor = (float)(UINT8_MAX) / totalWeight;
|
||||
float weightScalingFactor = (float)(UINT16_MAX) / totalWeight;
|
||||
for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) {
|
||||
extracted.mesh.clusterWeights[k] = (uint8_t)(weightScalingFactor * weightAccumulators[k] + ALMOST_HALF);
|
||||
extracted.mesh.clusterWeights[k] = (uint16_t)(weightScalingFactor * weightAccumulators[k] + ALMOST_HALF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -624,7 +624,8 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) {
|
|||
// we need 16 bits instead of just 8 for clusterIndices
|
||||
clusterIndicesSize *= 2;
|
||||
}
|
||||
const int clusterWeightsSize = fbxMesh.clusterWeights.size() * sizeof(uint8_t);
|
||||
|
||||
const int clusterWeightsSize = fbxMesh.clusterWeights.size() * sizeof(uint16_t);
|
||||
|
||||
// Normals and tangents are interleaved
|
||||
const int normalsOffset = 0;
|
||||
|
@ -759,7 +760,7 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) {
|
|||
if (clusterWeightsSize) {
|
||||
mesh->addAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT,
|
||||
model::BufferView(attribBuffer, clusterWeightsOffset, clusterWeightsSize,
|
||||
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::XYZW)));
|
||||
gpu::Element(gpu::VEC4, gpu::NUINT16, gpu::XYZW)));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -63,12 +63,17 @@ namespace gl {
|
|||
}
|
||||
*/
|
||||
|
||||
qCWarning(glLogging) << "GLShader::compileShader - failed to compile the gl shader object:";
|
||||
qCCritical(glLogging) << "GLShader::compileShader - failed to compile the gl shader object:";
|
||||
int lineNumber = 0;
|
||||
for (auto s : srcstr) {
|
||||
qCWarning(glLogging) << s;
|
||||
QString str(s);
|
||||
QStringList lines = str.split("\n");
|
||||
for (auto& line : lines) {
|
||||
qCCritical(glLogging).noquote() << QString("%1: %2").arg(lineNumber++, 5, 10, QChar('0')).arg(line);
|
||||
}
|
||||
}
|
||||
qCWarning(glLogging) << "GLShader::compileShader - errors:";
|
||||
qCWarning(glLogging) << temp;
|
||||
qCCritical(glLogging) << "GLShader::compileShader - errors:";
|
||||
qCCritical(glLogging) << temp;
|
||||
|
||||
error = std::string(temp);
|
||||
delete[] temp;
|
||||
|
|
|
@ -196,13 +196,15 @@ void sendWrongProtocolVersionsSignature(bool sendWrongVersion); /// for debuggin
|
|||
uint qHash(const PacketType& key, uint seed);
|
||||
QDebug operator<<(QDebug debug, const PacketType& type);
|
||||
|
||||
// Due to the different legacy behaviour, we need special processing for domains that were created before
|
||||
// the zone inheritance modes were added. These have version numbers up to 80
|
||||
enum class EntityVersion : PacketVersion {
|
||||
StrokeColorProperty = 0,
|
||||
HasDynamicOwnershipTests,
|
||||
HazeEffect,
|
||||
StaticCertJsonVersionOne,
|
||||
OwnershipChallengeFix,
|
||||
ZoneLightInheritModes,
|
||||
ZoneLightInheritModes = 82,
|
||||
ZoneStageRemoved
|
||||
};
|
||||
|
||||
|
|
|
@ -20,16 +20,16 @@ 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<glm::mat4>& clusterMatrices, const std::vector<glm::mat4>& cauterizedClusterMatrices) {
|
||||
ModelMeshPartPayload::updateClusterBuffer(clusterMatrices);
|
||||
void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector<TransformType>& clusterTransforms, const std::vector<TransformType>& cauterizedClusterTransforms) {
|
||||
ModelMeshPartPayload::updateClusterBuffer(clusterTransforms);
|
||||
|
||||
if (cauterizedClusterMatrices.size() > 1) {
|
||||
if (cauterizedClusterTransforms.size() > 1) {
|
||||
if (!_cauterizedClusterBuffer) {
|
||||
_cauterizedClusterBuffer = std::make_shared<gpu::Buffer>(cauterizedClusterMatrices.size() * sizeof(glm::mat4),
|
||||
(const gpu::Byte*) cauterizedClusterMatrices.data());
|
||||
_cauterizedClusterBuffer = std::make_shared<gpu::Buffer>(cauterizedClusterTransforms.size() * sizeof(TransformType),
|
||||
(const gpu::Byte*) cauterizedClusterTransforms.data());
|
||||
} else {
|
||||
_cauterizedClusterBuffer->setSubData(0, cauterizedClusterMatrices.size() * sizeof(glm::mat4),
|
||||
(const gpu::Byte*) cauterizedClusterMatrices.data());
|
||||
_cauterizedClusterBuffer->setSubData(0, cauterizedClusterTransforms.size() * sizeof(TransformType),
|
||||
(const gpu::Byte*) cauterizedClusterTransforms.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,13 @@ class CauterizedMeshPartPayload : public ModelMeshPartPayload {
|
|||
public:
|
||||
CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform);
|
||||
|
||||
void updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices, const std::vector<glm::mat4>& cauterizedClusterMatrices);
|
||||
#if defined(SKIN_DQ)
|
||||
using TransformType = Model::TransformDualQuaternion;
|
||||
#else
|
||||
using TransformType = glm::mat4;
|
||||
#endif
|
||||
|
||||
void updateClusterBuffer(const std::vector<TransformType>& clusterTransforms, const std::vector<TransformType>& cauterizedClusterTransforms);
|
||||
|
||||
void updateTransformForCauterizedMesh(const Transform& renderTransform);
|
||||
|
||||
|
|
|
@ -9,13 +9,13 @@
|
|||
#include "CauterizedModel.h"
|
||||
|
||||
#include <PerfStat.h>
|
||||
#include <DualQuaternion.h>
|
||||
|
||||
#include "AbstractViewStateInterface.h"
|
||||
#include "MeshPartPayload.h"
|
||||
#include "CauterizedMeshPartPayload.h"
|
||||
#include "RenderUtilsLogging.h"
|
||||
|
||||
|
||||
CauterizedModel::CauterizedModel(QObject* parent) :
|
||||
Model(parent) {
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ bool CauterizedModel::updateGeometry() {
|
|||
const FBXGeometry& fbxGeometry = getFBXGeometry();
|
||||
foreach (const FBXMesh& mesh, fbxGeometry.meshes) {
|
||||
Model::MeshState state;
|
||||
state.clusterMatrices.resize(mesh.clusters.size());
|
||||
state.clusterTransforms.resize(mesh.clusters.size());
|
||||
_cauterizeMeshStates.append(state);
|
||||
}
|
||||
}
|
||||
|
@ -109,30 +109,52 @@ 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);
|
||||
#else
|
||||
auto jointMatrix = _rig.getJointTransform(cluster.jointIndex);
|
||||
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]);
|
||||
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// 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.0f, 0.0f, 0.0f, 0.0f),
|
||||
glm::vec4(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
glm::vec4(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
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);
|
||||
|
||||
for (int j = 0; j < mesh.clusters.size(); j++) {
|
||||
const FBXCluster& cluster = mesh.clusters.at(j);
|
||||
auto jointMatrix = _rig.getJointTransform(cluster.jointIndex);
|
||||
if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) {
|
||||
jointMatrix = cauterizeMatrix;
|
||||
|
||||
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];
|
||||
} 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);
|
||||
#else
|
||||
glm_mat4u_mul(cauterizeMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]);
|
||||
#endif
|
||||
}
|
||||
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -189,24 +211,38 @@ void CauterizedModel::updateRenderItems() {
|
|||
|
||||
auto itemID = self->_modelMeshRenderItemIDs[i];
|
||||
auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex;
|
||||
auto clusterMatrices(self->getMeshState(meshIndex).clusterMatrices);
|
||||
auto clusterMatricesCauterized(self->getCauterizeMeshState(meshIndex).clusterMatrices);
|
||||
auto clusterTransforms(self->getMeshState(meshIndex).clusterTransforms);
|
||||
auto clusterTransformsCauterized(self->getCauterizeMeshState(meshIndex).clusterTransforms);
|
||||
|
||||
bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex);
|
||||
|
||||
transaction.updateItem<CauterizedMeshPartPayload>(itemID, [modelTransform, clusterMatrices, clusterMatricesCauterized, invalidatePayloadShapeKey,
|
||||
transaction.updateItem<CauterizedMeshPartPayload>(itemID, [modelTransform, clusterTransforms, clusterTransformsCauterized, invalidatePayloadShapeKey,
|
||||
isWireframe, isVisible, isLayeredInFront, isLayeredInHUD, enableCauterization](CauterizedMeshPartPayload& data) {
|
||||
data.updateClusterBuffer(clusterMatrices, clusterMatricesCauterized);
|
||||
data.updateClusterBuffer(clusterTransforms, clusterTransformsCauterized);
|
||||
|
||||
Transform renderTransform = modelTransform;
|
||||
if (clusterMatrices.size() == 1) {
|
||||
renderTransform = modelTransform.worldTransform(Transform(clusterMatrices[0]));
|
||||
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
|
||||
}
|
||||
data.updateTransformForSkinnedMesh(renderTransform, modelTransform);
|
||||
|
||||
renderTransform = modelTransform;
|
||||
if (clusterMatricesCauterized.size() == 1) {
|
||||
renderTransform = modelTransform.worldTransform(Transform(clusterMatricesCauterized[0]));
|
||||
if (clusterTransformsCauterized.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(clusterTransformsCauterized[0]));
|
||||
#endif
|
||||
}
|
||||
data.updateTransformForCauterizedMesh(renderTransform);
|
||||
|
||||
|
|
|
@ -46,6 +46,15 @@ struct DeferredFragment {
|
|||
float depthVal;
|
||||
};
|
||||
|
||||
<@if not GETFRESNEL0@>
|
||||
<@def GETFRESNEL0@>
|
||||
vec3 getFresnelF0(float metallic, vec3 metalF0) {
|
||||
// Enable continuous metallness value by lerping between dielectric
|
||||
// and metal fresnel F0 value based on the "metallic" parameter
|
||||
return mix(vec3(0.03), metalF0, metallic);
|
||||
}
|
||||
<@endif@>
|
||||
|
||||
DeferredFragment unpackDeferredFragmentNoPosition(vec2 texcoord) {
|
||||
vec4 normalVal;
|
||||
vec4 diffuseVal;
|
||||
|
@ -73,13 +82,7 @@ DeferredFragment unpackDeferredFragmentNoPosition(vec2 texcoord) {
|
|||
frag.scattering = specularVal.x;
|
||||
}
|
||||
|
||||
if (frag.metallic <= 0.5) {
|
||||
frag.metallic = 0.0;
|
||||
frag.fresnel = vec3(0.03); // Default Di-electric fresnel value
|
||||
} else {
|
||||
frag.fresnel = vec3(diffuseVal.xyz);
|
||||
frag.metallic = 1.0;
|
||||
}
|
||||
frag.fresnel = getFresnelF0(frag.metallic, diffuseVal.xyz);
|
||||
|
||||
return frag;
|
||||
}
|
||||
|
@ -106,14 +109,7 @@ DeferredFragment unpackDeferredFragmentNoPositionNoAmbient(vec2 texcoord) {
|
|||
//frag.emissive = specularVal.xyz;
|
||||
frag.obscurance = 1.0;
|
||||
|
||||
|
||||
if (frag.metallic <= 0.5) {
|
||||
frag.metallic = 0.0;
|
||||
frag.fresnel = vec3(0.03); // Default Di-electric fresnel value
|
||||
} else {
|
||||
frag.fresnel = vec3(diffuseVal.xyz);
|
||||
frag.metallic = 1.0;
|
||||
}
|
||||
frag.fresnel = getFresnelF0(frag.metallic, diffuseVal.xyz);
|
||||
|
||||
return frag;
|
||||
}
|
||||
|
|
|
@ -65,10 +65,12 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness
|
|||
|
||||
<$prepareGlobalLight($supportScattering$)$>
|
||||
|
||||
SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir);
|
||||
|
||||
// Ambient
|
||||
vec3 ambientDiffuse;
|
||||
vec3 ambientSpecular;
|
||||
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance
|
||||
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance
|
||||
<@if supportScattering@>
|
||||
,scattering, midNormalCurvature, lowNormalCurvature
|
||||
<@endif@> );
|
||||
|
@ -79,7 +81,7 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness
|
|||
// Directional
|
||||
vec3 directionalDiffuse;
|
||||
vec3 directionalSpecular;
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation
|
||||
<@if supportScattering@>
|
||||
,scattering, midNormalCurvature, lowNormalCurvature
|
||||
<@endif@> );
|
||||
|
@ -110,10 +112,12 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu
|
|||
) {
|
||||
<$prepareGlobalLight($supportScattering$)$>
|
||||
|
||||
SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir);
|
||||
|
||||
// Ambient
|
||||
vec3 ambientDiffuse;
|
||||
vec3 ambientSpecular;
|
||||
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance
|
||||
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance
|
||||
<@if supportScattering@>
|
||||
,scattering, midNormalCurvature, lowNormalCurvature
|
||||
<@endif@>
|
||||
|
@ -123,7 +127,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu
|
|||
|
||||
vec3 directionalDiffuse;
|
||||
vec3 directionalSpecular;
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation
|
||||
<@if supportScattering@>
|
||||
,scattering, midNormalCurvature, lowNormalCurvature
|
||||
<@endif@>
|
||||
|
@ -174,19 +178,21 @@ vec3 evalLightmappedColor(mat4 invViewMat, float shadowAttenuation, float obscur
|
|||
vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float metallic, vec3 emissive, float roughness, float opacity) {
|
||||
<$prepareGlobalLight()$>
|
||||
|
||||
SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir);
|
||||
|
||||
color += emissive * isEmissiveEnabled();
|
||||
|
||||
// Ambient
|
||||
vec3 ambientDiffuse;
|
||||
vec3 ambientSpecular;
|
||||
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance);
|
||||
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance);
|
||||
color += ambientDiffuse;
|
||||
color += ambientSpecular / opacity;
|
||||
|
||||
// Directional
|
||||
vec3 directionalDiffuse;
|
||||
vec3 directionalSpecular;
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation);
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation);
|
||||
color += directionalDiffuse;
|
||||
color += directionalSpecular / opacity;
|
||||
|
||||
|
@ -199,19 +205,21 @@ vec3 evalGlobalLightingAlphaBlendedWithHaze(
|
|||
{
|
||||
<$prepareGlobalLight()$>
|
||||
|
||||
SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir);
|
||||
|
||||
color += emissive * isEmissiveEnabled();
|
||||
|
||||
// Ambient
|
||||
vec3 ambientDiffuse;
|
||||
vec3 ambientSpecular;
|
||||
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance);
|
||||
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance);
|
||||
color += ambientDiffuse;
|
||||
color += ambientSpecular / opacity;
|
||||
|
||||
// Directional
|
||||
vec3 directionalDiffuse;
|
||||
vec3 directionalSpecular;
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation);
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation);
|
||||
color += directionalDiffuse;
|
||||
color += directionalSpecular / opacity;
|
||||
|
||||
|
|
|
@ -65,10 +65,12 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness
|
|||
|
||||
<$prepareGlobalLight($supportScattering$)$>
|
||||
|
||||
SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir);
|
||||
|
||||
// Ambient
|
||||
vec3 ambientDiffuse;
|
||||
vec3 ambientSpecular;
|
||||
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance
|
||||
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance
|
||||
<@if supportScattering@>
|
||||
,scattering, midNormalCurvature, lowNormalCurvature
|
||||
<@endif@> );
|
||||
|
@ -79,7 +81,7 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness
|
|||
// Directional
|
||||
vec3 directionalDiffuse;
|
||||
vec3 directionalSpecular;
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation
|
||||
<@if supportScattering@>
|
||||
,scattering, midNormalCurvature, lowNormalCurvature
|
||||
<@endif@> );
|
||||
|
@ -109,10 +111,12 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu
|
|||
) {
|
||||
<$prepareGlobalLight($supportScattering$)$>
|
||||
|
||||
SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir);
|
||||
|
||||
// Ambient
|
||||
vec3 ambientDiffuse;
|
||||
vec3 ambientSpecular;
|
||||
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance
|
||||
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance
|
||||
<@if supportScattering@>
|
||||
,scattering, midNormalCurvature, lowNormalCurvature
|
||||
<@endif@>
|
||||
|
@ -124,7 +128,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu
|
|||
// Directional
|
||||
vec3 directionalDiffuse;
|
||||
vec3 directionalSpecular;
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation
|
||||
<@if supportScattering@>
|
||||
,scattering, midNormalCurvature, lowNormalCurvature
|
||||
<@endif@>
|
||||
|
@ -173,19 +177,21 @@ vec3 evalLightmappedColor(mat4 invViewMat, float shadowAttenuation, float obscur
|
|||
vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float metallic, vec3 emissive, float roughness, float opacity) {
|
||||
<$prepareGlobalLight()$>
|
||||
|
||||
SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir);
|
||||
|
||||
color += emissive * isEmissiveEnabled();
|
||||
|
||||
// Ambient
|
||||
vec3 ambientDiffuse;
|
||||
vec3 ambientSpecular;
|
||||
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance);
|
||||
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance);
|
||||
color += ambientDiffuse;
|
||||
color += ambientSpecular / opacity;
|
||||
|
||||
// Directional
|
||||
vec3 directionalDiffuse;
|
||||
vec3 directionalSpecular;
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation);
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation);
|
||||
color += directionalDiffuse;
|
||||
color += directionalSpecular / opacity;
|
||||
|
||||
|
|
|
@ -16,22 +16,27 @@ uniform samplerCube skyboxMap;
|
|||
vec4 evalSkyboxLight(vec3 direction, float lod) {
|
||||
// textureQueryLevels is not available until #430, so we require explicit lod
|
||||
// float mipmapLevel = lod * textureQueryLevels(skyboxMap);
|
||||
float filterLod = textureQueryLod(skyboxMap, direction).x;
|
||||
// Keep texture filtering LOD as limit to prevent aliasing on specular reflection
|
||||
lod = max(lod, filterLod);
|
||||
return textureLod(skyboxMap, direction, lod);
|
||||
}
|
||||
<@endfunc@>
|
||||
|
||||
<@func declareEvalAmbientSpecularIrradiance(supportAmbientSphere, supportAmbientMap, supportIfAmbientMapElseAmbientSphere)@>
|
||||
|
||||
vec3 fresnelSchlickAmbient(vec3 fresnelColor, vec3 lightDir, vec3 halfDir, float gloss) {
|
||||
return fresnelColor + (max(vec3(gloss), fresnelColor) - fresnelColor) * pow(1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0), 5.0);
|
||||
vec3 fresnelSchlickAmbient(vec3 fresnelColor, float ndotd, float gloss) {
|
||||
float f = pow(1.0 - ndotd, 5.0);
|
||||
return fresnelColor + (max(vec3(gloss), fresnelColor) - fresnelColor) * f;
|
||||
// return fresnelColor + (vec3(1.0) - fresnelColor) * f;
|
||||
}
|
||||
|
||||
<@if supportAmbientMap@>
|
||||
<$declareSkyboxMap()$>
|
||||
<@endif@>
|
||||
|
||||
vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 fragNormal, float roughness) {
|
||||
vec3 direction = -reflect(fragEyeDir, fragNormal);
|
||||
vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, SurfaceData surface) {
|
||||
vec3 lightDir = -reflect(surface.eyeDir, surface.normal);
|
||||
vec3 specularLight;
|
||||
<@if supportIfAmbientMapElseAmbientSphere@>
|
||||
if (getLightHasAmbientMap(ambient))
|
||||
|
@ -39,8 +44,10 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 f
|
|||
<@if supportAmbientMap@>
|
||||
{
|
||||
float levels = getLightAmbientMapNumMips(ambient);
|
||||
float lod = min(((roughness)* levels), levels);
|
||||
specularLight = evalSkyboxLight(direction, lod).xyz;
|
||||
float m = 12.0 / (1.0+11.0*surface.roughness);
|
||||
float lod = levels - m;
|
||||
lod = max(lod, 0);
|
||||
specularLight = evalSkyboxLight(lightDir, lod).xyz;
|
||||
}
|
||||
<@endif@>
|
||||
<@if supportIfAmbientMapElseAmbientSphere@>
|
||||
|
@ -48,11 +55,11 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 f
|
|||
<@endif@>
|
||||
<@if supportAmbientSphere@>
|
||||
{
|
||||
specularLight = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), direction).xyz;
|
||||
specularLight = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), lightDir).xyz;
|
||||
}
|
||||
<@endif@>
|
||||
|
||||
return specularLight;
|
||||
return specularLight;
|
||||
}
|
||||
<@endfunc@>
|
||||
|
||||
|
@ -66,21 +73,21 @@ float curvatureAO(in float k) {
|
|||
}
|
||||
<@endif@>
|
||||
|
||||
void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambient, vec3 eyeDir, vec3 normal,
|
||||
float roughness, float metallic, vec3 fresnel, vec3 albedo, float obscurance
|
||||
void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambient, SurfaceData surface,
|
||||
float metallic, vec3 fresnelF0, vec3 albedo, float obscurance
|
||||
<@if supportScattering@>
|
||||
, float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature
|
||||
<@endif@>
|
||||
) {
|
||||
|
||||
// Fresnel
|
||||
vec3 ambientFresnel = fresnelSchlickAmbient(fresnel, eyeDir, normal, 1.0 - roughness);
|
||||
vec3 ambientFresnel = fresnelSchlickAmbient(fresnelF0, surface.ndotv, 1.0-surface.roughness);
|
||||
|
||||
// Diffuse from ambient
|
||||
diffuse = (1.0 - metallic) * (vec3(1.0) - ambientFresnel) * sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), normal).xyz;
|
||||
diffuse = (1.0 - metallic) * (vec3(1.0) - ambientFresnel) * sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), surface.normal).xyz;
|
||||
|
||||
// Specular highlight from ambient
|
||||
specular = evalAmbientSpecularIrradiance(ambient, eyeDir, normal, roughness) * ambientFresnel;
|
||||
specular = evalAmbientSpecularIrradiance(ambient, surface) * ambientFresnel;
|
||||
|
||||
<@if supportScattering@>
|
||||
if (scattering * isScatteringEnabled() > 0.0) {
|
||||
|
@ -92,7 +99,7 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie
|
|||
|
||||
// Diffuse from ambient
|
||||
diffuse = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), lowNormalCurvature.xyz).xyz;
|
||||
|
||||
diffuse /= 3.1415926;
|
||||
specular = vec3(0.0);
|
||||
}
|
||||
<@endif@>
|
||||
|
@ -107,8 +114,9 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie
|
|||
diffuse *= albedo;
|
||||
}
|
||||
|
||||
diffuse *= lightEnergy * isDiffuseEnabled() * isAmbientEnabled();
|
||||
specular *= lightEnergy * isSpecularEnabled() * isAmbientEnabled();
|
||||
lightEnergy *= isAmbientEnabled();
|
||||
diffuse *= lightEnergy * isDiffuseEnabled();
|
||||
specular *= lightEnergy * isSpecularEnabled();
|
||||
}
|
||||
|
||||
<@endfunc@>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<@func declareLightingDirectional(supportScattering)@>
|
||||
|
||||
void evalLightingDirectional(out vec3 diffuse, out vec3 specular, vec3 lightDir, vec3 lightIrradiance,
|
||||
vec3 eyeDir, vec3 normal, float roughness,
|
||||
SurfaceData surface,
|
||||
float metallic, vec3 fresnel, vec3 albedo, float shadow
|
||||
<@if supportScattering@>
|
||||
, float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature
|
||||
|
@ -22,14 +22,17 @@ void evalLightingDirectional(out vec3 diffuse, out vec3 specular, vec3 lightDir,
|
|||
// Attenuation
|
||||
vec3 lightEnergy = shadow * lightIrradiance;
|
||||
|
||||
evalFragShading(diffuse, specular, normal, -lightDir, eyeDir, metallic, fresnel, roughness, albedo
|
||||
updateSurfaceDataWithLight(surface, -lightDir);
|
||||
|
||||
evalFragShading(diffuse, specular, metallic, fresnel, surface, albedo
|
||||
<@if supportScattering@>
|
||||
,scattering, midNormalCurvature, lowNormalCurvature
|
||||
<@endif@>
|
||||
);
|
||||
|
||||
diffuse *= lightEnergy * isDiffuseEnabled() * isDirectionalEnabled();
|
||||
specular *= lightEnergy * isSpecularEnabled() * isDirectionalEnabled();
|
||||
lightEnergy *= isDirectionalEnabled();
|
||||
diffuse *= lightEnergy * isDiffuseEnabled();
|
||||
specular *= lightEnergy * isSpecularEnabled();
|
||||
}
|
||||
|
||||
<@endfunc@>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<@func declareLightingPoint(supportScattering)@>
|
||||
|
||||
void evalLightingPoint(out vec3 diffuse, out vec3 specular, Light light,
|
||||
vec4 fragLightDirLen, vec3 fragEyeDir, vec3 normal, float roughness,
|
||||
vec4 fragLightDirLen, SurfaceData surface,
|
||||
float metallic, vec3 fresnel, vec3 albedo, float shadow
|
||||
<@if supportScattering@>
|
||||
, float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature
|
||||
|
@ -23,19 +23,22 @@ void evalLightingPoint(out vec3 diffuse, out vec3 specular, Light light,
|
|||
float fragLightDistance = fragLightDirLen.w;
|
||||
vec3 fragLightDir = fragLightDirLen.xyz;
|
||||
|
||||
updateSurfaceDataWithLight(surface, fragLightDir);
|
||||
|
||||
// Eval attenuation
|
||||
float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance);
|
||||
vec3 lightEnergy = radialAttenuation * shadow * getLightIrradiance(light);
|
||||
|
||||
// Eval shading
|
||||
evalFragShading(diffuse, specular, normal, fragLightDir, fragEyeDir, metallic, fresnel, roughness, albedo
|
||||
evalFragShading(diffuse, specular, metallic, fresnel, surface, albedo
|
||||
<@if supportScattering@>
|
||||
,scattering, midNormalCurvature, lowNormalCurvature
|
||||
<@endif@>
|
||||
);
|
||||
|
||||
diffuse *= lightEnergy * isDiffuseEnabled() * isPointEnabled();
|
||||
specular *= lightEnergy * isSpecularEnabled() * isPointEnabled();
|
||||
lightEnergy *= isPointEnabled();
|
||||
diffuse *= lightEnergy * isDiffuseEnabled();
|
||||
specular *= lightEnergy * isSpecularEnabled();
|
||||
|
||||
if (isShowLightContour() > 0.0) {
|
||||
// Show edge
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<@func declareLightingSpot(supportScattering)@>
|
||||
|
||||
void evalLightingSpot(out vec3 diffuse, out vec3 specular, Light light,
|
||||
vec4 fragLightDirLen, float cosSpotAngle, vec3 fragEyeDir, vec3 normal, float roughness,
|
||||
vec4 fragLightDirLen, float cosSpotAngle, SurfaceData surface,
|
||||
float metallic, vec3 fresnel, vec3 albedo, float shadow
|
||||
<@if supportScattering@>
|
||||
, float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature
|
||||
|
@ -23,6 +23,7 @@ void evalLightingSpot(out vec3 diffuse, out vec3 specular, Light light,
|
|||
float fragLightDistance = fragLightDirLen.w;
|
||||
vec3 fragLightDir = fragLightDirLen.xyz;
|
||||
|
||||
updateSurfaceDataWithLight(surface, fragLightDir);
|
||||
|
||||
// Eval attenuation
|
||||
float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance);
|
||||
|
@ -30,14 +31,15 @@ void evalLightingSpot(out vec3 diffuse, out vec3 specular, Light light,
|
|||
vec3 lightEnergy = angularAttenuation * radialAttenuation * shadow *getLightIrradiance(light);
|
||||
|
||||
// Eval shading
|
||||
evalFragShading(diffuse, specular, normal, fragLightDir, fragEyeDir, metallic, fresnel, roughness, albedo
|
||||
evalFragShading(diffuse, specular, metallic, fresnel, surface, albedo
|
||||
<@if supportScattering@>
|
||||
,scattering, midNormalCurvature, lowNormalCurvature
|
||||
<@endif@>
|
||||
);
|
||||
|
||||
diffuse *= lightEnergy * isDiffuseEnabled() * isSpotEnabled();
|
||||
specular *= lightEnergy * isSpecularEnabled() * isSpotEnabled();
|
||||
lightEnergy *= isSpotEnabled();
|
||||
diffuse *= lightEnergy * isDiffuseEnabled();
|
||||
specular *= lightEnergy * isSpecularEnabled();
|
||||
|
||||
if (isShowLightContour() > 0.0) {
|
||||
// Show edges
|
||||
|
|
|
@ -77,6 +77,30 @@ float isWireframeEnabled() {
|
|||
<@endfunc@>
|
||||
<$declareLightingModel()$>
|
||||
|
||||
struct SurfaceData {
|
||||
vec3 normal;
|
||||
vec3 eyeDir;
|
||||
vec3 lightDir;
|
||||
vec3 halfDir;
|
||||
float roughness;
|
||||
float roughness2;
|
||||
float roughness4;
|
||||
float ndotv;
|
||||
float ndotl;
|
||||
float ndoth;
|
||||
float ldoth;
|
||||
float smithInvG1NdotV;
|
||||
};
|
||||
|
||||
<@if not GETFRESNEL0@>
|
||||
<@def GETFRESNEL0@>
|
||||
vec3 getFresnelF0(float metallic, vec3 metalF0) {
|
||||
// Enable continuous metallness value by lerping between dielectric
|
||||
// and metal fresnel F0 value based on the "metallic" parameter
|
||||
return mix(vec3(0.03), metalF0, metallic);
|
||||
}
|
||||
<@endif@>
|
||||
|
||||
<@func declareBeckmannSpecular()@>
|
||||
|
||||
uniform sampler2D scatteringSpecularBeckmann;
|
||||
|
@ -85,17 +109,13 @@ float fetchSpecularBeckmann(float ndoth, float roughness) {
|
|||
return pow(2.0 * texture(scatteringSpecularBeckmann, vec2(ndoth, roughness)).r, 10.0);
|
||||
}
|
||||
|
||||
vec2 skinSpecular(vec3 N, vec3 L, vec3 V, float roughness, float intensity) {
|
||||
vec2 skinSpecular(SurfaceData surface, float intensity) {
|
||||
vec2 result = vec2(0.0, 1.0);
|
||||
float ndotl = dot(N, L);
|
||||
if (ndotl > 0.0) {
|
||||
vec3 h = L + V;
|
||||
vec3 H = normalize(h);
|
||||
float ndoth = dot(N, H);
|
||||
float PH = fetchSpecularBeckmann(ndoth, roughness);
|
||||
float F = fresnelSchlickScalar(0.028, H, V);
|
||||
float frSpec = max(PH * F / dot(h, h), 0.0);
|
||||
result.x = ndotl * intensity * frSpec;
|
||||
if (surface.ndotl > 0.0) {
|
||||
float PH = fetchSpecularBeckmann(surface.ndoth, surface.roughness);
|
||||
float F = fresnelSchlickScalar(0.028, surface);
|
||||
float frSpec = max(PH * F / dot(surface.halfDir, surface.halfDir), 0.0);
|
||||
result.x = surface.ndotl * intensity * frSpec;
|
||||
result.y -= F;
|
||||
}
|
||||
|
||||
|
@ -105,117 +125,136 @@ vec2 skinSpecular(vec3 N, vec3 L, vec3 V, float roughness, float intensity) {
|
|||
|
||||
<@func declareEvalPBRShading()@>
|
||||
|
||||
vec3 fresnelSchlickColor(vec3 fresnelColor, vec3 lightDir, vec3 halfDir) {
|
||||
float base = 1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0);
|
||||
float evalSmithInvG1(float roughness4, float ndotd) {
|
||||
return ndotd + sqrt(roughness4+ndotd*ndotd*(1.0-roughness4));
|
||||
}
|
||||
|
||||
SurfaceData initSurfaceData(float roughness, vec3 normal, vec3 eyeDir) {
|
||||
SurfaceData surface;
|
||||
surface.eyeDir = eyeDir;
|
||||
surface.normal = normal;
|
||||
surface.roughness = mix(0.001, 1.0, roughness);
|
||||
surface.roughness2 = surface.roughness * surface.roughness;
|
||||
surface.roughness4 = surface.roughness2 * surface.roughness2;
|
||||
surface.ndotv = clamp(dot(normal, eyeDir), 0.0, 1.0);
|
||||
surface.smithInvG1NdotV = evalSmithInvG1(surface.roughness4, surface.ndotv);
|
||||
|
||||
// These values will be set when we know the light direction, in updateSurfaceDataWithLight
|
||||
surface.ndoth = 0.0;
|
||||
surface.ndotl = 0.0;
|
||||
surface.ldoth = 0.0;
|
||||
surface.lightDir = vec3(0,0,1);
|
||||
surface.halfDir = vec3(0,0,1);
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
void updateSurfaceDataWithLight(inout SurfaceData surface, vec3 lightDir) {
|
||||
surface.lightDir = lightDir;
|
||||
surface.halfDir = normalize(surface.eyeDir + lightDir);
|
||||
vec3 dots;
|
||||
dots.x = dot(surface.normal, surface.halfDir);
|
||||
dots.y = dot(surface.normal, surface.lightDir);
|
||||
dots.z = dot(surface.halfDir, surface.lightDir);
|
||||
dots = clamp(dots, vec3(0), vec3(1));
|
||||
surface.ndoth = dots.x;
|
||||
surface.ndotl = dots.y;
|
||||
surface.ldoth = dots.z;
|
||||
}
|
||||
|
||||
vec3 fresnelSchlickColor(vec3 fresnelColor, SurfaceData surface) {
|
||||
float base = 1.0 - surface.ldoth;
|
||||
//float exponential = pow(base, 5.0);
|
||||
float base2 = base * base;
|
||||
float exponential = base * base2 * base2;
|
||||
return vec3(exponential) + fresnelColor * (1.0 - exponential);
|
||||
}
|
||||
|
||||
float fresnelSchlickScalar(float fresnelScalar, vec3 lightDir, vec3 halfDir) {
|
||||
float base = 1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0);
|
||||
float fresnelSchlickScalar(float fresnelScalar, SurfaceData surface) {
|
||||
float base = 1.0 - surface.ldoth;
|
||||
//float exponential = pow(base, 5.0);
|
||||
float base2 = base * base;
|
||||
float exponential = base * base2 * base2;
|
||||
return (exponential) + fresnelScalar * (1.0 - exponential);
|
||||
}
|
||||
|
||||
float specularDistribution(float roughness, vec3 normal, vec3 halfDir) {
|
||||
float ndoth = clamp(dot(halfDir, normal), 0.0, 1.0);
|
||||
// float gloss2 = pow(0.001 + roughness, 4);
|
||||
float gloss2 = (0.001 + roughness);
|
||||
gloss2 *= gloss2; // pow 2
|
||||
gloss2 *= gloss2; // pow 4
|
||||
float denom = (ndoth * ndoth*(gloss2 - 1.0) + 1.0);
|
||||
float power = gloss2 / (3.14159 * denom * denom);
|
||||
float specularDistribution(SurfaceData surface) {
|
||||
// See https://www.khronos.org/assets/uploads/developers/library/2017-web3d/glTF-2.0-Launch_Jun17.pdf
|
||||
// for details of equations, especially page 20
|
||||
float denom = (surface.ndoth*surface.ndoth * (surface.roughness2 - 1.0) + 1.0);
|
||||
denom *= denom;
|
||||
// Add geometric factors G1(n,l) and G1(n,v)
|
||||
float smithInvG1NdotL = evalSmithInvG1(surface.roughness4, surface.ndotl);
|
||||
denom *= surface.smithInvG1NdotV * smithInvG1NdotL;
|
||||
// Don't divide by PI as it will be done later
|
||||
float power = surface.roughness4 / denom;
|
||||
return power;
|
||||
}
|
||||
float specularDistributionGloss(float gloss2, vec3 normal, vec3 halfDir) {
|
||||
float ndoth = clamp(dot(halfDir, normal), 0.0, 1.0);
|
||||
// float gloss2 = pow(0.001 + roughness, 4);
|
||||
float denom = (ndoth * ndoth*(gloss2 - 1.0) + 1.0);
|
||||
float power = gloss2 / (3.14159 * denom * denom);
|
||||
return power;
|
||||
}
|
||||
<! //NOTE: ANother implementation for specularDistribution
|
||||
float specularDistribution(float roughness, vec3 normal, vec3 halfDir) {
|
||||
float gloss = exp2(10 * (1.0 - roughness) + 1);
|
||||
float power = pow(clamp(dot(halfDir, normal), 0.0, 1.0), gloss);
|
||||
power *= (gloss * 0.125 + 0.25);
|
||||
return power;
|
||||
}
|
||||
!>
|
||||
// Frag Shading returns the diffuse amount as W and the specular rgb as xyz
|
||||
vec4 evalPBRShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float metallic, vec3 fresnel, float roughness) {
|
||||
// Diffuse Lighting
|
||||
float diffuse = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0);
|
||||
|
||||
// Specular Lighting
|
||||
vec3 halfDir = normalize(fragEyeDir + fragLightDir);
|
||||
vec3 fresnelColor = fresnelSchlickColor(fresnel, fragLightDir, halfDir);
|
||||
float power = specularDistribution(roughness, fragNormal, halfDir);
|
||||
vec3 specular = fresnelColor * power * diffuse;
|
||||
|
||||
return vec4(specular, (1.0 - metallic) * diffuse * (1.0 - fresnelColor.x));
|
||||
}
|
||||
|
||||
// Frag Shading returns the diffuse amount as W and the specular rgb as xyz
|
||||
vec4 evalPBRShadingDielectric(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float roughness, float fresnel) {
|
||||
// Diffuse Lighting
|
||||
float diffuse = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0);
|
||||
vec4 evalPBRShading(float metallic, vec3 fresnel, SurfaceData surface) {
|
||||
// Incident angle attenuation
|
||||
float angleAttenuation = surface.ndotl;
|
||||
|
||||
// Specular Lighting
|
||||
vec3 halfDir = normalize(fragEyeDir + fragLightDir);
|
||||
float fresnelScalar = fresnelSchlickScalar(fresnel, fragLightDir, halfDir);
|
||||
float power = specularDistribution(roughness, fragNormal, halfDir);
|
||||
vec3 specular = vec3(fresnelScalar) * power * diffuse;
|
||||
vec3 fresnelColor = fresnelSchlickColor(fresnel, surface);
|
||||
float power = specularDistribution(surface);
|
||||
vec3 specular = fresnelColor * power * angleAttenuation;
|
||||
float diffuse = (1.0 - metallic) * angleAttenuation * (1.0 - fresnelColor.x);
|
||||
|
||||
return vec4(specular, diffuse * (1.0 - fresnelScalar));
|
||||
diffuse /= 3.1415926;
|
||||
// Diffuse is divided by PI but specular isn't because an infinitesimal volume light source
|
||||
// has a multiplier of PI, says Naty Hoffman.
|
||||
// (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf
|
||||
// page 23 paragraph "Punctual light sources")
|
||||
|
||||
return vec4(specular, diffuse);
|
||||
}
|
||||
|
||||
vec4 evalPBRShadingMetallic(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float roughness, vec3 fresnel) {
|
||||
// Diffuse Lighting
|
||||
float diffuse = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0);
|
||||
// Frag Shading returns the diffuse amount as W and the specular rgb as xyz
|
||||
vec4 evalPBRShadingDielectric(SurfaceData surface, float fresnel) {
|
||||
// Incident angle attenuation
|
||||
float angleAttenuation = surface.ndotl;
|
||||
|
||||
// Specular Lighting
|
||||
vec3 halfDir = normalize(fragEyeDir + fragLightDir);
|
||||
vec3 fresnelColor = fresnelSchlickColor(fresnel, fragLightDir, halfDir);
|
||||
float power = specularDistribution(roughness, fragNormal, halfDir);
|
||||
vec3 specular = fresnelColor * power * diffuse;
|
||||
float fresnelScalar = fresnelSchlickScalar(fresnel, surface);
|
||||
float power = specularDistribution(surface);
|
||||
vec3 specular = vec3(fresnelScalar) * power * angleAttenuation;
|
||||
float diffuse = angleAttenuation * (1.0 - fresnelScalar);
|
||||
|
||||
diffuse /= 3.1415926;
|
||||
// Diffuse is divided by PI but specular isn't because an infinitesimal volume light source
|
||||
// has a multiplier of PI, says Naty Hoffman.
|
||||
// (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf
|
||||
// page 23 paragraph "Punctual light sources")
|
||||
return vec4(specular, diffuse);
|
||||
}
|
||||
|
||||
vec4 evalPBRShadingMetallic(SurfaceData surface, vec3 fresnel) {
|
||||
// Incident angle attenuation
|
||||
float angleAttenuation = surface.ndotl;
|
||||
|
||||
// Specular Lighting
|
||||
vec3 fresnelColor = fresnelSchlickColor(fresnel, surface);
|
||||
float power = specularDistribution(surface);
|
||||
vec3 specular = fresnelColor * power * angleAttenuation;
|
||||
|
||||
// Specular isn't divided by PI because an infinitesimal volume light source
|
||||
// has a multiplier of PI, says Naty Hoffman.
|
||||
// (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf
|
||||
// page 23 paragraph "Punctual light sources")
|
||||
return vec4(specular, 0.f);
|
||||
}
|
||||
|
||||
// Frag Shading returns the diffuse amount as W and the specular rgb as xyz
|
||||
vec4 evalPBRShadingGloss(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float metallic, vec3 fresnel, float gloss2) {
|
||||
// Diffuse Lighting
|
||||
float diffuse = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0);
|
||||
|
||||
// Specular Lighting
|
||||
vec3 halfDir = normalize(fragEyeDir + fragLightDir);
|
||||
vec3 fresnelColor = fresnelSchlickColor(fresnel, fragLightDir, halfDir);
|
||||
float power = specularDistributionGloss(gloss2, fragNormal, halfDir);
|
||||
vec3 specular = fresnelColor * power * diffuse;
|
||||
|
||||
return vec4(specular, (1.0 - metallic) * diffuse * (1.0 - fresnelColor.x));
|
||||
}
|
||||
|
||||
<@endfunc@>
|
||||
|
||||
|
||||
|
||||
<$declareEvalPBRShading()$>
|
||||
|
||||
// Return xyz the specular/reflection component and w the diffuse component
|
||||
//vec4 evalFragShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float metallic, vec3 fresnel, float roughness) {
|
||||
// return evalPBRShading(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, roughness);
|
||||
//}
|
||||
|
||||
void evalFragShading(out vec3 diffuse, out vec3 specular,
|
||||
vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir,
|
||||
float metallic, vec3 fresnel, float roughness, vec3 albedo) {
|
||||
vec4 shading = evalPBRShading(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, roughness);
|
||||
float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo) {
|
||||
vec4 shading = evalPBRShading(metallic, fresnel, surface);
|
||||
diffuse = vec3(shading.w);
|
||||
if (isAlbedoEnabled() > 0.0) {
|
||||
diffuse *= albedo;
|
||||
|
@ -229,22 +268,19 @@ void evalFragShading(out vec3 diffuse, out vec3 specular,
|
|||
|
||||
|
||||
void evalFragShading(out vec3 diffuse, out vec3 specular,
|
||||
vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir,
|
||||
float metallic, vec3 fresnel, float roughness, vec3 albedo,
|
||||
float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo,
|
||||
float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature) {
|
||||
if (scattering * isScatteringEnabled() > 0.0) {
|
||||
vec3 brdf = evalSkinBRDF(fragLightDir, fragNormal, midNormalCurvature.xyz, lowNormalCurvature.xyz, lowNormalCurvature.w);
|
||||
float NdotL = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0);
|
||||
diffuse = mix(vec3(NdotL), brdf, scattering);
|
||||
vec3 brdf = evalSkinBRDF(surface.lightDir, surface.normal, midNormalCurvature.xyz, lowNormalCurvature.xyz, lowNormalCurvature.w);
|
||||
diffuse = mix(vec3(surface.ndotl), brdf, scattering);
|
||||
|
||||
// Specular Lighting
|
||||
vec3 halfDir = normalize(fragEyeDir + fragLightDir);
|
||||
vec2 specularBrdf = skinSpecular(fragNormal, fragLightDir, fragEyeDir, roughness, 1.0);
|
||||
vec2 specularBrdf = skinSpecular(surface, 1.0);
|
||||
|
||||
diffuse *= specularBrdf.y;
|
||||
specular = vec3(specularBrdf.x);
|
||||
} else {
|
||||
vec4 shading = evalPBRShadingGloss(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, roughness);
|
||||
vec4 shading = evalPBRShading(metallic, fresnel, surface);
|
||||
diffuse = vec3(shading.w);
|
||||
specular = shading.xyz;
|
||||
}
|
||||
|
@ -253,17 +289,15 @@ void evalFragShading(out vec3 diffuse, out vec3 specular,
|
|||
|
||||
|
||||
void evalFragShadingScattering(out vec3 diffuse, out vec3 specular,
|
||||
vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir,
|
||||
float metallic, vec3 fresnel, float roughness, vec3 albedo
|
||||
float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo
|
||||
,float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature
|
||||
) {
|
||||
vec3 brdf = evalSkinBRDF(fragLightDir, fragNormal, midNormalCurvature.xyz, lowNormalCurvature.xyz, lowNormalCurvature.w);
|
||||
float NdotL = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0);
|
||||
vec3 brdf = evalSkinBRDF(surface.lightDir, surface.normal, midNormalCurvature.xyz, lowNormalCurvature.xyz, lowNormalCurvature.w);
|
||||
float NdotL = surface.ndotl;
|
||||
diffuse = mix(vec3(NdotL), brdf, scattering);
|
||||
|
||||
// Specular Lighting
|
||||
vec3 halfDir = normalize(fragEyeDir + fragLightDir);
|
||||
vec2 specularBrdf = skinSpecular(fragNormal, fragLightDir, fragEyeDir, roughness, 1.0);
|
||||
vec2 specularBrdf = skinSpecular(surface, 1.0);
|
||||
|
||||
diffuse *= specularBrdf.y;
|
||||
specular = vec3(specularBrdf.x);
|
||||
|
@ -271,10 +305,9 @@ void evalFragShadingScattering(out vec3 diffuse, out vec3 specular,
|
|||
}
|
||||
|
||||
void evalFragShadingGloss(out vec3 diffuse, out vec3 specular,
|
||||
vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir,
|
||||
float metallic, vec3 fresnel, float gloss, vec3 albedo
|
||||
float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo
|
||||
) {
|
||||
vec4 shading = evalPBRShadingGloss(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, gloss);
|
||||
vec4 shading = evalPBRShading(metallic, fresnel, surface);
|
||||
diffuse = vec3(shading.w);
|
||||
diffuse *= mix(vec3(1.0), albedo, isAlbedoEnabled());
|
||||
specular = shading.xyz;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "MeshPartPayload.h"
|
||||
|
||||
#include <PerfStat.h>
|
||||
#include <DualQuaternion.h>
|
||||
|
||||
#include "DeferredLightingEffect.h"
|
||||
|
||||
|
@ -325,12 +326,20 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in
|
|||
const Model::MeshState& state = model->getMeshState(_meshIndex);
|
||||
|
||||
updateMeshPart(modelMesh, partIndex);
|
||||
computeAdjustedLocalBound(state.clusterMatrices);
|
||||
computeAdjustedLocalBound(state.clusterTransforms);
|
||||
|
||||
updateTransform(transform, offsetTransform);
|
||||
Transform renderTransform = transform;
|
||||
if (state.clusterMatrices.size() == 1) {
|
||||
renderTransform = transform.worldTransform(Transform(state.clusterMatrices[0]));
|
||||
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
|
||||
|
||||
}
|
||||
updateTransformForSkinnedMesh(renderTransform, transform);
|
||||
|
||||
|
@ -360,17 +369,16 @@ void ModelMeshPartPayload::notifyLocationChanged() {
|
|||
|
||||
}
|
||||
|
||||
|
||||
void ModelMeshPartPayload::updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices) {
|
||||
void ModelMeshPartPayload::updateClusterBuffer(const std::vector<TransformType>& clusterTransforms) {
|
||||
// Once computed the cluster matrices, update the buffer(s)
|
||||
if (clusterMatrices.size() > 1) {
|
||||
if (clusterTransforms.size() > 1) {
|
||||
if (!_clusterBuffer) {
|
||||
_clusterBuffer = std::make_shared<gpu::Buffer>(clusterMatrices.size() * sizeof(glm::mat4),
|
||||
(const gpu::Byte*) clusterMatrices.data());
|
||||
_clusterBuffer = std::make_shared<gpu::Buffer>(clusterTransforms.size() * sizeof(TransformType),
|
||||
(const gpu::Byte*) clusterTransforms.data());
|
||||
}
|
||||
else {
|
||||
_clusterBuffer->setSubData(0, clusterMatrices.size() * sizeof(glm::mat4),
|
||||
(const gpu::Byte*) clusterMatrices.data());
|
||||
_clusterBuffer->setSubData(0, clusterTransforms.size() * sizeof(TransformType),
|
||||
(const gpu::Byte*) clusterTransforms.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -530,13 +538,29 @@ void ModelMeshPartPayload::render(RenderArgs* args) {
|
|||
args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE;
|
||||
}
|
||||
|
||||
void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector<glm::mat4>& clusterMatrices) {
|
||||
|
||||
void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector<TransformType>& clusterTransforms) {
|
||||
_adjustedLocalBound = _localBound;
|
||||
if (clusterMatrices.size() > 0) {
|
||||
_adjustedLocalBound.transform(clusterMatrices[0]);
|
||||
for (int i = 1; i < (int)clusterMatrices.size(); ++i) {
|
||||
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
|
||||
|
||||
for (int i = 1; i < (int)clusterTransforms.size(); ++i) {
|
||||
AABox clusterBound = _localBound;
|
||||
clusterBound.transform(clusterMatrices[i]);
|
||||
#if defined(SKIN_DQ)
|
||||
Transform transform(clusterTransforms[i].getRotation(),
|
||||
clusterTransforms[i].getScale(),
|
||||
clusterTransforms[i].getTranslation());
|
||||
clusterBound.transform(transform);
|
||||
#else
|
||||
clusterBound.transform(clusterTransforms[i]);
|
||||
#endif
|
||||
_adjustedLocalBound += clusterBound;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,7 +87,14 @@ public:
|
|||
typedef Payload::DataPointer Pointer;
|
||||
|
||||
void notifyLocationChanged() override;
|
||||
void updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices);
|
||||
|
||||
#if defined(SKIN_DQ)
|
||||
using TransformType = Model::TransformDualQuaternion;
|
||||
#else
|
||||
using TransformType = glm::mat4;
|
||||
#endif
|
||||
|
||||
void updateClusterBuffer(const std::vector<TransformType>& clusterTransforms);
|
||||
void updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform);
|
||||
|
||||
// Render Item interface
|
||||
|
@ -104,7 +111,7 @@ public:
|
|||
void bindMesh(gpu::Batch& batch) override;
|
||||
void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override;
|
||||
|
||||
void computeAdjustedLocalBound(const std::vector<glm::mat4>& clusterMatrices);
|
||||
void computeAdjustedLocalBound(const std::vector<TransformType>& clusterTransforms);
|
||||
|
||||
gpu::BufferPointer _clusterBuffer;
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <TBBHelpers.h>
|
||||
|
||||
#include <model-networking/SimpleMeshProxy.h>
|
||||
#include <DualQuaternion.h>
|
||||
|
||||
#include <glm/gtc/packing.hpp>
|
||||
|
||||
|
@ -269,16 +270,24 @@ void Model::updateRenderItems() {
|
|||
|
||||
auto itemID = self->_modelMeshRenderItemIDs[i];
|
||||
auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex;
|
||||
auto clusterMatrices(self->getMeshState(meshIndex).clusterMatrices);
|
||||
auto clusterTransforms(self->getMeshState(meshIndex).clusterTransforms);
|
||||
|
||||
bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex);
|
||||
|
||||
transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, clusterMatrices, invalidatePayloadShapeKey,
|
||||
transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, clusterTransforms, invalidatePayloadShapeKey,
|
||||
isWireframe, isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) {
|
||||
data.updateClusterBuffer(clusterMatrices);
|
||||
data.updateClusterBuffer(clusterTransforms);
|
||||
|
||||
Transform renderTransform = modelTransform;
|
||||
if (clusterMatrices.size() == 1) {
|
||||
renderTransform = modelTransform.worldTransform(Transform(clusterMatrices[0]));
|
||||
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
|
||||
}
|
||||
data.updateTransformForSkinnedMesh(renderTransform, modelTransform);
|
||||
|
||||
|
@ -359,7 +368,7 @@ bool Model::updateGeometry() {
|
|||
const FBXGeometry& fbxGeometry = getFBXGeometry();
|
||||
foreach (const FBXMesh& mesh, fbxGeometry.meshes) {
|
||||
MeshState state;
|
||||
state.clusterMatrices.resize(mesh.clusters.size());
|
||||
state.clusterTransforms.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
|
||||
|
@ -1211,7 +1220,7 @@ 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.clusterMatrices);
|
||||
part->computeAdjustedLocalBound(state.clusterTransforms);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1222,6 +1231,7 @@ void Model::updateClusterMatrices() {
|
|||
if (!_needsUpdateClusterMatrices || !isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_needsUpdateClusterMatrices = false;
|
||||
const FBXGeometry& geometry = getFBXGeometry();
|
||||
for (int i = 0; i < (int) _meshStates.size(); i++) {
|
||||
|
@ -1229,8 +1239,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.clusterMatrices[j]);
|
||||
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,11 +30,15 @@
|
|||
#include <Transform.h>
|
||||
#include <SpatiallyNestable.h>
|
||||
#include <TriangleSet.h>
|
||||
#include <DualQuaternion.h>
|
||||
|
||||
#include "GeometryCache.h"
|
||||
#include "TextureCache.h"
|
||||
#include "Rig.h"
|
||||
|
||||
// Use dual quaternion skinning!
|
||||
// Must match define in Skinning.slh
|
||||
#define SKIN_DQ
|
||||
|
||||
class AbstractViewStateInterface;
|
||||
class QScriptEngine;
|
||||
|
@ -246,9 +250,46 @@ public:
|
|||
int getRenderInfoDrawCalls() const { return _renderInfoDrawCalls; }
|
||||
bool getRenderInfoHasTransparent() const { return _renderInfoHasTransparent; }
|
||||
|
||||
|
||||
#if defined(SKIN_DQ)
|
||||
class TransformDualQuaternion {
|
||||
public:
|
||||
TransformDualQuaternion() {}
|
||||
TransformDualQuaternion(const glm::mat4& m) {
|
||||
AnimPose p(m);
|
||||
_scale.x = p.scale().x;
|
||||
_scale.y = p.scale().y;
|
||||
_scale.z = p.scale().z;
|
||||
_dq = DualQuaternion(p.rot(), p.trans());
|
||||
}
|
||||
TransformDualQuaternion(const glm::vec3& scale, const glm::quat& rot, const glm::vec3& trans) {
|
||||
_scale.x = scale.x;
|
||||
_scale.y = scale.y;
|
||||
_scale.z = scale.z;
|
||||
_dq = DualQuaternion(rot, trans);
|
||||
}
|
||||
TransformDualQuaternion(const Transform& transform) {
|
||||
_scale = glm::vec4(transform.getScale(), 0.0f);
|
||||
_dq = DualQuaternion(transform.getRotation(), transform.getTranslation());
|
||||
}
|
||||
glm::vec3 getScale() const { return glm::vec3(_scale); }
|
||||
glm::quat getRotation() const { return _dq.getRotation(); }
|
||||
glm::vec3 getTranslation() const { return _dq.getTranslation(); }
|
||||
glm::mat4 getMatrix() const { return createMatFromScaleQuatAndPos(getScale(), getRotation(), getTranslation()); };
|
||||
protected:
|
||||
glm::vec4 _scale { 1.0f, 1.0f, 1.0f, 0.0f };
|
||||
DualQuaternion _dq;
|
||||
glm::vec4 _padding;
|
||||
};
|
||||
#endif
|
||||
|
||||
class MeshState {
|
||||
public:
|
||||
std::vector<glm::mat4> clusterMatrices;
|
||||
#if defined(SKIN_DQ)
|
||||
std::vector<TransformDualQuaternion> clusterTransforms;
|
||||
#else
|
||||
std::vector<glm::mat4> clusterTransforms;
|
||||
#endif
|
||||
};
|
||||
|
||||
const MeshState& getMeshState(int index) { return _meshStates.at(index); }
|
||||
|
|
|
@ -11,6 +11,10 @@
|
|||
<@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;
|
||||
|
||||
|
@ -18,6 +22,156 @@ layout(std140) uniform skinClusterBuffer {
|
|||
mat4 clusterMatrices[MAX_CLUSTERS];
|
||||
};
|
||||
|
||||
<@if SKIN_DQ@>
|
||||
|
||||
mat4 dualQuatToMat4(vec4 real, vec4 dual) {
|
||||
float twoRealXSq = 2.0 * real.x * real.x;
|
||||
float twoRealYSq = 2.0 * real.y * real.y;
|
||||
float twoRealZSq = 2.0 * real.z * real.z;
|
||||
float twoRealXY = 2.0 * real.x * real.y;
|
||||
float twoRealXZ = 2.0 * real.x * real.z;
|
||||
float twoRealXW = 2.0 * real.x * real.w;
|
||||
float twoRealZW = 2.0 * real.z * real.w;
|
||||
float twoRealYZ = 2.0 * real.y * real.z;
|
||||
float twoRealYW = 2.0 * real.y * real.w;
|
||||
vec4 col0 = vec4(1.0 - twoRealYSq - twoRealZSq,
|
||||
twoRealXY + twoRealZW,
|
||||
twoRealXZ - twoRealYW,
|
||||
0.0);
|
||||
vec4 col1 = vec4(twoRealXY - twoRealZW,
|
||||
1 - twoRealXSq - twoRealZSq,
|
||||
twoRealYZ + twoRealXW,
|
||||
0.0);
|
||||
vec4 col2 = vec4(twoRealXZ + twoRealYW,
|
||||
twoRealYZ - twoRealXW,
|
||||
1 - twoRealXSq - twoRealYSq,
|
||||
0.0);
|
||||
vec4 col3 = vec4(2.0 * (-dual.w * real.x + dual.x * real.w - dual.y * real.z + dual.z * real.y),
|
||||
2.0 * (-dual.w * real.y + dual.x * real.z + dual.y * real.w - dual.z * real.x),
|
||||
2.0 * (-dual.w * real.z - dual.x * real.y + dual.y * real.x + dual.z * real.w),
|
||||
1.0);
|
||||
|
||||
return mat4(col0, col1, col2, col3);
|
||||
}
|
||||
|
||||
// dual quaternion linear blending
|
||||
void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) {
|
||||
|
||||
// linearly blend scale and dual quaternion components
|
||||
vec3 sAccum = vec3(0.0, 0.0, 0.0);
|
||||
vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 dAccum = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1];
|
||||
for (int i = 0; i < INDICES_PER_VERTEX; i++) {
|
||||
mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])];
|
||||
float clusterWeight = skinClusterWeight[i];
|
||||
|
||||
vec3 scale = vec3(clusterMatrix[0]);
|
||||
vec4 real = clusterMatrix[1];
|
||||
vec4 dual = clusterMatrix[2];
|
||||
|
||||
// to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity.
|
||||
float dqClusterWeight = clusterWeight;
|
||||
if (dot(real, polarityReference) < 0) {
|
||||
dqClusterWeight = -clusterWeight;
|
||||
}
|
||||
|
||||
sAccum += scale * clusterWeight;
|
||||
rAccum += real * dqClusterWeight;
|
||||
dAccum += dual * dqClusterWeight;
|
||||
}
|
||||
|
||||
// normalize dual quaternion
|
||||
float norm = length(rAccum);
|
||||
rAccum /= norm;
|
||||
dAccum /= norm;
|
||||
|
||||
// conversion from dual quaternion to 4x4 matrix.
|
||||
mat4 m = dualQuatToMat4(rAccum, dAccum);
|
||||
skinnedPosition = m * (vec4(sAccum, 1) * inPosition);
|
||||
}
|
||||
|
||||
void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal,
|
||||
out vec4 skinnedPosition, out vec3 skinnedNormal) {
|
||||
|
||||
// linearly blend scale and dual quaternion components
|
||||
vec3 sAccum = vec3(0.0, 0.0, 0.0);
|
||||
vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 dAccum = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1];
|
||||
|
||||
for (int i = 0; i < INDICES_PER_VERTEX; i++) {
|
||||
mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])];
|
||||
float clusterWeight = skinClusterWeight[i];
|
||||
|
||||
vec3 scale = vec3(clusterMatrix[0]);
|
||||
vec4 real = clusterMatrix[1];
|
||||
vec4 dual = clusterMatrix[2];
|
||||
|
||||
// to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity.
|
||||
float dqClusterWeight = clusterWeight;
|
||||
if (dot(real, polarityReference) < 0) {
|
||||
dqClusterWeight = -clusterWeight;
|
||||
}
|
||||
|
||||
sAccum += scale * clusterWeight;
|
||||
rAccum += real * dqClusterWeight;
|
||||
dAccum += dual * dqClusterWeight;
|
||||
}
|
||||
|
||||
// normalize dual quaternion
|
||||
float norm = length(rAccum);
|
||||
rAccum /= norm;
|
||||
dAccum /= norm;
|
||||
|
||||
// conversion from dual quaternion to 4x4 matrix.
|
||||
mat4 m = dualQuatToMat4(rAccum, dAccum);
|
||||
skinnedPosition = m * (vec4(sAccum, 1) * inPosition);
|
||||
skinnedNormal = vec3(m * vec4(inNormal, 0));
|
||||
}
|
||||
|
||||
void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, vec3 inTangent,
|
||||
out vec4 skinnedPosition, out vec3 skinnedNormal, out vec3 skinnedTangent) {
|
||||
|
||||
// linearly blend scale and dual quaternion components
|
||||
vec3 sAccum = vec3(0.0, 0.0, 0.0);
|
||||
vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 dAccum = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1];
|
||||
|
||||
for (int i = 0; i < INDICES_PER_VERTEX; i++) {
|
||||
mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])];
|
||||
float clusterWeight = skinClusterWeight[i];
|
||||
|
||||
vec3 scale = vec3(clusterMatrix[0]);
|
||||
vec4 real = clusterMatrix[1];
|
||||
vec4 dual = clusterMatrix[2];
|
||||
|
||||
// to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity.
|
||||
float dqClusterWeight = clusterWeight;
|
||||
if (dot(real, polarityReference) < 0) {
|
||||
dqClusterWeight = -clusterWeight;
|
||||
}
|
||||
|
||||
sAccum += scale * clusterWeight;
|
||||
rAccum += real * dqClusterWeight;
|
||||
dAccum += dual * dqClusterWeight;
|
||||
}
|
||||
|
||||
// normalize dual quaternion
|
||||
float norm = length(rAccum);
|
||||
rAccum /= norm;
|
||||
dAccum /= norm;
|
||||
|
||||
// conversion from dual quaternion to 4x4 matrix.
|
||||
mat4 m = dualQuatToMat4(rAccum, dAccum);
|
||||
skinnedPosition = m * (vec4(sAccum, 1) * inPosition);
|
||||
skinnedNormal = vec3(m * vec4(inNormal, 0));
|
||||
skinnedTangent = vec3(m * vec4(inTangent, 0));
|
||||
}
|
||||
|
||||
<@else@> // SKIN_DQ
|
||||
|
||||
void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) {
|
||||
vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
|
@ -65,5 +219,6 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v
|
|||
skinnedTangent = newTangent.xyz;
|
||||
}
|
||||
|
||||
<@endif@> // if SKIN_DQ
|
||||
|
||||
<@endif@>
|
||||
<@endif@> // if not SKINNING_SLH
|
||||
|
|
|
@ -52,13 +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);
|
||||
}
|
||||
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]);
|
||||
|
||||
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);
|
||||
} else {
|
||||
jointMatrix = _rig.getJointTransform(cluster.jointIndex);
|
||||
}
|
||||
|
||||
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,38 +63,6 @@ vec3 scatter(float r) {
|
|||
<@endfunc@>
|
||||
|
||||
|
||||
<@func declareSkinSpecularLighting()@>
|
||||
|
||||
uniform sampler2D scatteringSpecularBeckmann;
|
||||
|
||||
float fetchSpecularBeckmann(float ndoth, float roughness) {
|
||||
return pow( 2.0 * texture(scatteringSpecularBeckmann, vec2(ndoth, roughness)).r, 10.0);
|
||||
}
|
||||
|
||||
float fresnelReflectance(vec3 H, vec3 V, float Fo) {
|
||||
float base = 1.0 - dot(V, H);
|
||||
float exponential = pow(base, 5.0);
|
||||
return exponential + Fo * (1.0 - exponential);
|
||||
}
|
||||
|
||||
float skinSpecular(vec3 N, vec3 L, vec3 V, float roughness, float intensity) {
|
||||
float result = 0.0;
|
||||
float ndotl = dot(N, L);
|
||||
if (ndotl > 0.0) {
|
||||
vec3 h = L + V;
|
||||
vec3 H = normalize(h);
|
||||
float ndoth = dot(N, H);
|
||||
float PH = fetchSpecularBeckmann(ndoth, roughness);
|
||||
float F = fresnelReflectance(H, V, 0.028);
|
||||
float frSpec = max(PH * F / dot(h, h), 0.0);
|
||||
result = ndotl * intensity * frSpec;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
<@endfunc@>
|
||||
|
||||
<@func declareSubsurfaceScatteringIntegrate(NumIntegrationSteps)@>
|
||||
|
||||
|
||||
|
|
|
@ -84,9 +84,8 @@ void main(void) {
|
|||
// Frag to eye vec
|
||||
vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0);
|
||||
vec3 fragEyeDir = normalize(fragEyeVector.xyz);
|
||||
|
||||
// Compute the rougness into gloss2 once:
|
||||
float fragGloss2 = pow(frag.roughness + 0.001, 4.0);
|
||||
|
||||
SurfaceData surface = initSurfaceData(frag.roughness, frag.normal, fragEyeDir);
|
||||
bool withScattering = (frag.scattering * isScatteringEnabled() > 0.0);
|
||||
|
||||
int numLightTouching = 0;
|
||||
|
@ -119,16 +118,18 @@ void main(void) {
|
|||
float fragLightDistance = fragLightDirLen.w;
|
||||
vec3 fragLightDir = fragLightDirLen.xyz;
|
||||
|
||||
updateSurfaceDataWithLight(surface, fragLightDir);
|
||||
|
||||
// Eval attenuation
|
||||
float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance);
|
||||
vec3 lightEnergy = radialAttenuation * getLightIrradiance(light);
|
||||
|
||||
// Eval shading
|
||||
if (withScattering) {
|
||||
evalFragShadingScattering(diffuse, specular, frag.normal, fragLightDir, fragEyeDir, frag.metallic, frag.fresnel, frag.roughness, frag.albedo
|
||||
evalFragShadingScattering(diffuse, specular, frag.metallic, frag.fresnel, surface, frag.albedo
|
||||
,frag.scattering, midNormalCurvature, lowNormalCurvature );
|
||||
} else {
|
||||
evalFragShadingGloss(diffuse, specular, frag.normal, fragLightDir, fragEyeDir, frag.metallic, frag.fresnel, fragGloss2, frag.albedo);
|
||||
evalFragShadingGloss(diffuse, specular, frag.metallic, frag.fresnel, surface, frag.albedo);
|
||||
}
|
||||
|
||||
diffuse *= lightEnergy * isDiffuseEnabled();
|
||||
|
@ -173,6 +174,8 @@ void main(void) {
|
|||
float fragLightDistance = fragLightDirLen.w;
|
||||
vec3 fragLightDir = fragLightDirLen.xyz;
|
||||
|
||||
updateSurfaceDataWithLight(surface, fragLightDir);
|
||||
|
||||
// Eval attenuation
|
||||
float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance);
|
||||
float angularAttenuation = lightIrradiance_evalLightSpotAttenuation(light.irradiance, cosSpotAngle);
|
||||
|
@ -180,10 +183,10 @@ void main(void) {
|
|||
|
||||
// Eval shading
|
||||
if (withScattering) {
|
||||
evalFragShadingScattering(diffuse, specular, frag.normal, fragLightDir, fragEyeDir, frag.metallic, frag.fresnel, frag.roughness, frag.albedo
|
||||
evalFragShadingScattering(diffuse, specular, frag.metallic, frag.fresnel, surface, frag.albedo
|
||||
,frag.scattering, midNormalCurvature, lowNormalCurvature );
|
||||
} else {
|
||||
evalFragShadingGloss(diffuse, specular, frag.normal, fragLightDir, fragEyeDir, frag.metallic, frag.fresnel, fragGloss2, frag.albedo);
|
||||
evalFragShadingGloss(diffuse, specular, frag.metallic, frag.fresnel, surface, frag.albedo);
|
||||
}
|
||||
|
||||
diffuse *= lightEnergy * isDiffuseEnabled();
|
||||
|
|
|
@ -50,13 +50,7 @@ void main(void) {
|
|||
<$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>;
|
||||
|
||||
float metallic = getMaterialMetallic(mat);
|
||||
vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value
|
||||
if (metallic <= 0.5) {
|
||||
metallic = 0.0;
|
||||
} else {
|
||||
fresnel = albedo;
|
||||
metallic = 1.0;
|
||||
}
|
||||
vec3 fresnel = getFresnelF0(metallic, albedo);
|
||||
|
||||
vec3 emissive = getMaterialEmissive(mat);
|
||||
<$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>;
|
||||
|
|
|
@ -60,13 +60,7 @@ void main(void) {
|
|||
<$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>;
|
||||
|
||||
float metallic = getMaterialMetallic(mat);
|
||||
vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value
|
||||
if (metallic <= 0.5) {
|
||||
metallic = 0.0;
|
||||
} else {
|
||||
fresnel = albedo;
|
||||
metallic = 1.0;
|
||||
}
|
||||
vec3 fresnel = getFresnelF0(metallic, albedo);
|
||||
|
||||
vec3 emissive = getMaterialEmissive(mat);
|
||||
<$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>;
|
||||
|
|
|
@ -40,12 +40,14 @@ vec4 evalGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 a
|
|||
vec3 fragEyeDir;
|
||||
<$transformEyeToWorldDir(cam, fragEyeVectorView, fragEyeDir)$>
|
||||
|
||||
SurfaceData surface = initSurfaceData(roughness, normal, fragEyeDir);
|
||||
|
||||
vec3 color = opacity * albedo * getLightColor(light) * getLightAmbientIntensity(ambient);
|
||||
|
||||
// Directional
|
||||
vec3 directionalDiffuse;
|
||||
vec3 directionalSpecular;
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation);
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation);
|
||||
color += directionalDiffuse * isDiffuseEnabled() * isDirectionalEnabled();
|
||||
color += directionalSpecular * isSpecularEnabled() * isDirectionalEnabled();
|
||||
|
||||
|
|
|
@ -46,13 +46,7 @@ void main(void) {
|
|||
albedo *= _color;
|
||||
|
||||
float metallic = getMaterialMetallic(mat);
|
||||
vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value
|
||||
if (metallic <= 0.5) {
|
||||
metallic = 0.0;
|
||||
} else {
|
||||
fresnel = albedo;
|
||||
metallic = 1.0;
|
||||
}
|
||||
vec3 fresnel = getFresnelF0(metallic, albedo);
|
||||
|
||||
float roughness = getMaterialRoughness(mat);
|
||||
<$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>;
|
||||
|
|
|
@ -44,13 +44,7 @@ void main(void) {
|
|||
albedo *= _color;
|
||||
|
||||
float metallic = getMaterialMetallic(mat);
|
||||
vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value
|
||||
if (metallic <= 0.5) {
|
||||
metallic = 0.0;
|
||||
} else {
|
||||
fresnel = albedo;
|
||||
metallic = 1.0;
|
||||
}
|
||||
vec3 fresnel = getFresnelF0(metallic, albedo);
|
||||
|
||||
float roughness = getMaterialRoughness(mat);
|
||||
<$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>;
|
||||
|
|
|
@ -40,12 +40,14 @@ vec4 evalGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 a
|
|||
vec3 fragEyeDir;
|
||||
<$transformEyeToWorldDir(cam, fragEyeVectorView, fragEyeDir)$>
|
||||
|
||||
SurfaceData surface = initSurfaceData(roughness, normal, fragEyeDir);
|
||||
|
||||
vec3 color = opacity * albedo * getLightColor(light) * getLightAmbientIntensity(ambient);
|
||||
|
||||
// Directional
|
||||
vec3 directionalDiffuse;
|
||||
vec3 directionalSpecular;
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation);
|
||||
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation);
|
||||
color += directionalDiffuse;
|
||||
color += directionalSpecular / opacity;
|
||||
|
||||
|
|
|
@ -16,9 +16,11 @@
|
|||
#include <AssetRequest.h>
|
||||
#include <AssetUpload.h>
|
||||
#include <MappingRequest.h>
|
||||
#include <NetworkLogging.h>
|
||||
#include <NodeList.h>
|
||||
|
||||
#include "ScriptEngineLogging.h"
|
||||
|
||||
|
||||
AssetScriptingInterface::AssetScriptingInterface(QScriptEngine* engine) :
|
||||
_engine(engine)
|
||||
{
|
||||
|
@ -53,10 +55,32 @@ void AssetScriptingInterface::setMapping(QString path, QString hash, QScriptValu
|
|||
setMappingRequest->start();
|
||||
}
|
||||
|
||||
void AssetScriptingInterface::getMapping(QString path, QScriptValue callback) {
|
||||
auto request = DependencyManager::get<AssetClient>()->createGetMappingRequest(path);
|
||||
QObject::connect(request, &GetMappingRequest::finished, this, [=](GetMappingRequest* request) mutable {
|
||||
auto result = request->getError();
|
||||
if (callback.isFunction()) {
|
||||
if (result == GetMappingRequest::NotFound) {
|
||||
QScriptValueList args { "", true };
|
||||
callback.call(_engine->currentContext()->thisObject(), args);
|
||||
} else if (result == GetMappingRequest::NoError) {
|
||||
QScriptValueList args { request->getHash(), true };
|
||||
callback.call(_engine->currentContext()->thisObject(), args);
|
||||
} else {
|
||||
qCDebug(scriptengine) << "error -- " << request->getError() << " -- " << request->getErrorString();
|
||||
QScriptValueList args { "", false };
|
||||
callback.call(_engine->currentContext()->thisObject(), args);
|
||||
}
|
||||
request->deleteLater();
|
||||
}
|
||||
});
|
||||
request->start();
|
||||
}
|
||||
|
||||
void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callback) {
|
||||
|
||||
if (!urlString.startsWith(ATP_SCHEME)) {
|
||||
qCDebug(scriptengine) << "AssetScriptingInterface::downloadData url must be of form atp:<hash-value>";
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -75,7 +75,24 @@ public:
|
|||
* @param {string} error
|
||||
*/
|
||||
Q_INVOKABLE void setMapping(QString path, QString hash, QScriptValue callback);
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* Look up a path to hash mapping within the connected domain's asset server
|
||||
* @function Assets.getMapping
|
||||
* @static
|
||||
* @param path {string}
|
||||
* @param callback {Assets~getMappingCallback}
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Called when getMapping is complete.
|
||||
* @callback Assets~getMappingCallback
|
||||
* @param assetID {string} hash value if found, else an empty string
|
||||
* @param success {boolean} false for errors other than "not found", else true
|
||||
*/
|
||||
Q_INVOKABLE void getMapping(QString path, QScriptValue callback);
|
||||
|
||||
|
||||
Q_INVOKABLE void setBakingEnabled(QString path, bool enabled, QScriptValue callback);
|
||||
|
||||
#if (PR_BUILD || DEV_BUILD)
|
||||
|
|
92
libraries/shared/src/DualQuaternion.cpp
Normal file
92
libraries/shared/src/DualQuaternion.cpp
Normal file
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// DualQuaternion.cpp
|
||||
//
|
||||
// Created by Anthony J. Thibault on Dec 13th 2017.
|
||||
// Copyright (c) 2017 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "DualQuaternion.h"
|
||||
#include "GLMHelpers.h"
|
||||
|
||||
// delegating constructor
|
||||
DualQuaternion::DualQuaternion() : _real(1.0f, 0.0f, 0.0f, 0.0), _dual(0.0f, 0.0f, 0.0f, 0.0f) {
|
||||
}
|
||||
|
||||
DualQuaternion::DualQuaternion(const glm::mat4& m) : DualQuaternion(glmExtractRotation(m), extractTranslation(m)) {
|
||||
}
|
||||
|
||||
DualQuaternion::DualQuaternion(const glm::quat& real, const glm::quat& dual) : _real(real), _dual(dual) {
|
||||
}
|
||||
|
||||
DualQuaternion::DualQuaternion(const glm::vec4& real, const glm::vec4& dual) :
|
||||
_real(real.w, real.x, real.y, real.z),
|
||||
_dual(dual.w, dual.x, dual.y, dual.z) {
|
||||
}
|
||||
|
||||
DualQuaternion::DualQuaternion(const glm::quat& rotation, const glm::vec3& translation) {
|
||||
_real = rotation;
|
||||
_dual = glm::quat(0.0f, 0.5f * translation.x, 0.5f * translation.y, 0.5f * translation.z) * rotation;
|
||||
}
|
||||
|
||||
DualQuaternion DualQuaternion::operator*(const DualQuaternion& rhs) const {
|
||||
return DualQuaternion(_real * rhs._real, _real * rhs._dual + _dual * rhs._real);
|
||||
}
|
||||
|
||||
DualQuaternion DualQuaternion::operator*(float scalar) const {
|
||||
return DualQuaternion(_real * scalar, _dual * scalar);
|
||||
}
|
||||
|
||||
DualQuaternion DualQuaternion::operator+(const DualQuaternion& rhs) const {
|
||||
return DualQuaternion(_real + rhs._real, _dual + rhs._dual);
|
||||
}
|
||||
|
||||
glm::vec3 DualQuaternion::xformPoint(const glm::vec3& rhs) const {
|
||||
DualQuaternion v(glm::quat(), glm::quat(0.0f, rhs.x, rhs.y, rhs.z));
|
||||
DualQuaternion dualConj(glm::conjugate(_real), -glm::conjugate(_dual));
|
||||
DualQuaternion result = *this * v * dualConj;
|
||||
return vec3(result._dual.x, result._dual.y, result._dual.z);
|
||||
}
|
||||
|
||||
glm::quat DualQuaternion::getRotation() const {
|
||||
return _real;
|
||||
}
|
||||
|
||||
glm::vec3 DualQuaternion::getTranslation() const {
|
||||
glm::quat result = 2.0f * (_dual * glm::inverse(_real));
|
||||
return glm::vec3(result.x, result.y, result.z);
|
||||
}
|
||||
|
||||
glm::vec3 DualQuaternion::xformVector(const glm::vec3& rhs) const {
|
||||
return _real * rhs;
|
||||
}
|
||||
|
||||
DualQuaternion DualQuaternion::inverse() const {
|
||||
glm::quat invReal = glm::inverse(_real);
|
||||
return DualQuaternion(invReal, - invReal * _dual * invReal);
|
||||
}
|
||||
|
||||
DualQuaternion DualQuaternion::conjugate() const {
|
||||
return DualQuaternion(glm::conjugate(_real), glm::conjugate(_dual));
|
||||
}
|
||||
|
||||
float DualQuaternion::length() const {
|
||||
float dot = this->dot(*this);
|
||||
return sqrtf(dot);
|
||||
}
|
||||
|
||||
DualQuaternion DualQuaternion::normalize() const {
|
||||
float invLen = 1.0f / length();
|
||||
return *this * invLen;
|
||||
}
|
||||
|
||||
float DualQuaternion::dot(const DualQuaternion& rhs) const {
|
||||
DualQuaternion result = *this * conjugate();
|
||||
return result._real.w;
|
||||
}
|
||||
|
||||
DualQuaternion DualQuaternion::operator-() const {
|
||||
return DualQuaternion(-_real, -_dual);
|
||||
}
|
62
libraries/shared/src/DualQuaternion.h
Normal file
62
libraries/shared/src/DualQuaternion.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
//
|
||||
// DualQuaternion.h
|
||||
//
|
||||
// Created by Anthony J. Thibault on Dec 13th 2017.
|
||||
// Copyright (c) 2017 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
#ifndef hifi_DualQuaternion
|
||||
#define hifi_DualQuaternion
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QDebug>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
class DualQuaternion {
|
||||
public:
|
||||
DualQuaternion();
|
||||
explicit DualQuaternion(const glm::mat4& m);
|
||||
DualQuaternion(const glm::quat& real, const glm::quat& imag);
|
||||
DualQuaternion(const glm::quat& rotation, const glm::vec3& translation);
|
||||
DualQuaternion(const glm::vec4& real, const glm::vec4& imag);
|
||||
DualQuaternion operator*(const DualQuaternion& rhs) const;
|
||||
DualQuaternion operator*(float scalar) const;
|
||||
DualQuaternion operator+(const DualQuaternion& rhs) const;
|
||||
|
||||
const glm::quat& real() const { return _real; }
|
||||
glm::quat& real() { return _real; }
|
||||
|
||||
const glm::quat& dual() const { return _dual; }
|
||||
glm::quat& dual() { return _dual; }
|
||||
|
||||
glm::quat getRotation() const;
|
||||
glm::vec3 getTranslation() const;
|
||||
|
||||
glm::vec3 xformPoint(const glm::vec3& rhs) const;
|
||||
glm::vec3 xformVector(const glm::vec3& rhs) const;
|
||||
|
||||
DualQuaternion inverse() const;
|
||||
DualQuaternion conjugate() const;
|
||||
float length() const;
|
||||
DualQuaternion normalize() const;
|
||||
float dot(const DualQuaternion& rhs) const;
|
||||
DualQuaternion operator-() const;
|
||||
|
||||
protected:
|
||||
friend QDebug operator<<(QDebug debug, const DualQuaternion& pose);
|
||||
glm::quat _real;
|
||||
glm::quat _dual;
|
||||
};
|
||||
|
||||
|
||||
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 << ")";
|
||||
return debug;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -58,7 +58,7 @@ public:
|
|||
_rotation(rotation),
|
||||
_scale(scale),
|
||||
_translation(translation),
|
||||
_flags(FLAG_CACHE_INVALID_BITSET) // invalid cache
|
||||
_flags(0xf) // FLAG_TRANSLATION | FLAG_ROTATION | FLAG_SCALING | FLAG_NON_UNIFORM
|
||||
{
|
||||
if (!isValidScale(_scale)) {
|
||||
_scale = Vec3(1.0f);
|
||||
|
|
115
tests/shared/src/DualQuaternionTests.cpp
Normal file
115
tests/shared/src/DualQuaternionTests.cpp
Normal file
|
@ -0,0 +1,115 @@
|
|||
//
|
||||
// DualQuaternionTests.cpp
|
||||
// tests/shared/src
|
||||
//
|
||||
// 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 <iostream>
|
||||
|
||||
#include "DualQuaternionTests.h"
|
||||
|
||||
#include <DualQuaternion.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <StreamUtils.h>
|
||||
|
||||
#include <../GLMTestUtils.h>
|
||||
#include <../QTestExtensions.h>
|
||||
|
||||
QTEST_MAIN(DualQuaternionTests)
|
||||
|
||||
static void quatComp(const glm::quat& q1, const glm::quat& q2) {
|
||||
QCOMPARE_WITH_ABS_ERROR(q1.x, q2.x, EPSILON);
|
||||
QCOMPARE_WITH_ABS_ERROR(q1.y, q2.y, EPSILON);
|
||||
QCOMPARE_WITH_ABS_ERROR(q1.z, q2.z, EPSILON);
|
||||
QCOMPARE_WITH_ABS_ERROR(q1.w, q2.w, EPSILON);
|
||||
}
|
||||
|
||||
void DualQuaternionTests::ctor() {
|
||||
glm::quat real = angleAxis(PI / 2.0f, Vectors::UNIT_Y);
|
||||
glm::quat dual(0.0f, 1.0f, 2.0f, 3.0f);
|
||||
|
||||
DualQuaternion dq(real, dual);
|
||||
quatComp(real, dq.real());
|
||||
quatComp(dual, dq.dual());
|
||||
|
||||
glm::quat rotation = angleAxis(PI / 3.0f, Vectors::UNIT_X);
|
||||
glm::vec3 translation(1.0, 2.0f, 3.0f);
|
||||
dq = DualQuaternion(rotation, translation);
|
||||
quatComp(rotation, dq.getRotation());
|
||||
QCOMPARE_WITH_ABS_ERROR(translation, dq.getTranslation(), EPSILON);
|
||||
|
||||
rotation = angleAxis(-2.0f * PI / 7.0f, Vectors::UNIT_Z);
|
||||
translation = glm::vec3(-1.0, 12.0f, 2.0f);
|
||||
glm::mat4 m = createMatFromQuatAndPos(rotation, translation);
|
||||
dq = DualQuaternion(m);
|
||||
quatComp(rotation, dq.getRotation());
|
||||
QCOMPARE_WITH_ABS_ERROR(translation, dq.getTranslation(), EPSILON);
|
||||
}
|
||||
|
||||
void DualQuaternionTests::mult() {
|
||||
|
||||
glm::quat rotation = angleAxis(PI / 3.0f, Vectors::UNIT_X);
|
||||
glm::vec3 translation(1.0, 2.0f, 3.0f);
|
||||
glm::mat4 m1 = createMatFromQuatAndPos(rotation, translation);
|
||||
DualQuaternion dq1(m1);
|
||||
|
||||
rotation = angleAxis(-2.0f * PI / 7.0f, Vectors::UNIT_Z);
|
||||
translation = glm::vec3(-1.0, 12.0f, 2.0f);
|
||||
glm::mat4 m2 = createMatFromQuatAndPos(rotation, translation);
|
||||
DualQuaternion dq2(m2);
|
||||
|
||||
DualQuaternion dq3 = dq1 * dq2;
|
||||
glm::mat4 m3 = m1 * m2;
|
||||
|
||||
rotation = glmExtractRotation(m3);
|
||||
translation = extractTranslation(m3);
|
||||
|
||||
quatComp(rotation, dq3.getRotation());
|
||||
QCOMPARE_WITH_ABS_ERROR(translation, dq3.getTranslation(), EPSILON);
|
||||
}
|
||||
|
||||
void DualQuaternionTests::xform() {
|
||||
|
||||
glm::quat rotation = angleAxis(PI / 3.0f, Vectors::UNIT_X);
|
||||
glm::vec3 translation(1.0, 2.0f, 3.0f);
|
||||
glm::mat4 m1 = createMatFromQuatAndPos(rotation, translation);
|
||||
DualQuaternion dq1(m1);
|
||||
|
||||
rotation = angleAxis(-2.0f * PI / 7.0f, Vectors::UNIT_Z);
|
||||
translation = glm::vec3(-1.0, 12.0f, 2.0f);
|
||||
glm::mat4 m2 = createMatFromQuatAndPos(rotation, translation);
|
||||
DualQuaternion dq2(m2);
|
||||
|
||||
DualQuaternion dq3 = dq1 * dq2;
|
||||
glm::mat4 m3 = m1 * m2;
|
||||
|
||||
glm::vec3 p(1.0f, 2.0f, 3.0f);
|
||||
|
||||
glm::vec3 p1 = transformPoint(m3, p);
|
||||
glm::vec3 p2 = dq3.xformPoint(p);
|
||||
|
||||
QCOMPARE_WITH_ABS_ERROR(p1, p2, 0.001f);
|
||||
|
||||
p1 = transformVectorFast(m3, p);
|
||||
p2 = dq3.xformVector(p);
|
||||
|
||||
QCOMPARE_WITH_ABS_ERROR(p1, p2, 0.001f);
|
||||
}
|
||||
|
||||
void DualQuaternionTests::trans() {
|
||||
glm::vec3 t1 = glm::vec3();
|
||||
DualQuaternion dq1(Quaternions::IDENTITY, t1);
|
||||
glm::vec3 t2 = glm::vec3(1.0f, 2.0f, 3.0f);
|
||||
DualQuaternion dq2(angleAxis(PI / 3.0f, Vectors::UNIT_X), t2);
|
||||
glm::vec3 t3 = glm::vec3(3.0f, 2.0f, 1.0f);
|
||||
DualQuaternion dq3(angleAxis(PI / 5.0f, Vectors::UNIT_Y), t3);
|
||||
|
||||
QCOMPARE_WITH_ABS_ERROR(t1, dq1.getTranslation(), 0.001f);
|
||||
QCOMPARE_WITH_ABS_ERROR(t2, dq2.getTranslation(), 0.001f);
|
||||
QCOMPARE_WITH_ABS_ERROR(t3, dq3.getTranslation(), 0.001f);
|
||||
}
|
25
tests/shared/src/DualQuaternionTests.h
Normal file
25
tests/shared/src/DualQuaternionTests.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// DualQuaternionTests.h
|
||||
// tests/shared/src
|
||||
//
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_DualQuaternionTests_h
|
||||
#define hifi_DualQuaternionTests_h
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
class DualQuaternionTests : public QObject {
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void ctor();
|
||||
void mult();
|
||||
void xform();
|
||||
void trans();
|
||||
};
|
||||
|
||||
#endif // hifi_DualQuaternionTests_h
|
Loading…
Reference in a new issue