Merge branch 'master' of git://github.com/highfidelity/hifi into scribe

This commit is contained in:
Olivier Prat 2018-01-16 16:43:50 +01:00
commit cdb94d0832
65 changed files with 1111 additions and 663 deletions

View file

@ -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);
});

View file

@ -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

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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;
}
}
}

View file

@ -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 { }
}
}
}

View file

@ -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;

View file

@ -22,7 +22,6 @@ Item {
anchors.fill: parent
id: d
objectName: "stack"
initialItem: topMenu
property var menuStack: []
property var topMenu: null;

View file

@ -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;
}
}

View file

@ -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

View file

@ -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;

View file

@ -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>();

View file

@ -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);

View file

@ -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();

View file

@ -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);

View file

@ -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) {

View file

@ -49,8 +49,6 @@ public:
bool walletIsAuthenticatedWithPassphrase();
bool changePassphrase(const QString& newPassphrase);
void reset();
void getWalletStatus();
enum WalletStatus {
WALLET_STATUS_NOT_LOGGED_IN = 0,

View file

@ -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}

View file

@ -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);

View file

@ -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));
}

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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(""));

View file

@ -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);

View file

@ -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;
}

View file

@ -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;

View file

@ -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);
}
}
}

View file

@ -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)));
}

View file

@ -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;

View file

@ -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
};

View file

@ -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());
}
}
}

View file

@ -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);

View file

@ -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);

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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@>

View file

@ -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@>

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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
}
}

View file

@ -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); }

View file

@ -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

View file

@ -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
}
}

View file

@ -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)@>

View file

@ -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();

View file

@ -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)$>;

View file

@ -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)$>;

View file

@ -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();

View file

@ -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)$>;

View file

@ -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)$>;

View file

@ -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;

View file

@ -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;
}

View file

@ -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)

View 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);
}

View 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

View file

@ -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);

View 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);
}

View 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