Merge remote-tracking branch 'sam/android-gles' into windows_gles

This commit is contained in:
Brad Davis 2018-01-13 11:36:31 -08:00
commit 24b83aa0f1
63 changed files with 1107 additions and 571 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

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

@ -1132,7 +1132,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
updateHeartbeat();
// Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success.
DependencyManager::get<GeometryCache>()->initializeShapePipelines();
// DependencyManager::get<GeometryCache>()->initializeShapePipelines();
// sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value.
// The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
@ -2256,6 +2256,9 @@ void Application::initializeGL() {
_renderEngine->load();
_renderEngine->registerScene(_main3DScene);
// Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success.
DependencyManager::get<GeometryCache>()->initializeShapePipelines();
_offscreenContext = new OffscreenGLCanvas();
_offscreenContext->setObjectName("MainThreadContext");
_offscreenContext->create(_glWidget->qglContext());
@ -6869,6 +6872,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

@ -312,6 +312,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

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

@ -26,7 +26,7 @@ static std::weak_ptr<gpu::Pipeline> _texturedPipeline;
// FIXME: This is interfering with the uniform buffers in DeferredLightingEffect.cpp, so use 11 to avoid collisions
static int32_t PARTICLE_UNIFORM_SLOT { 11 };
static ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const ShapeKey& key) {
static ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const ShapeKey& key, gpu::Batch& batch) {
auto texturedPipeline = _texturedPipeline.lock();
if (!texturedPipeline) {
auto state = std::make_shared<gpu::State>();
@ -40,10 +40,13 @@ static ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, co
auto fragShader = gpu::Shader::createPixel(std::string(textured_particle_frag));
auto program = gpu::Shader::createProgram(vertShader, fragShader);
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding(std::string("particleBuffer"), PARTICLE_UNIFORM_SLOT));
gpu::Shader::makeProgram(*program, slotBindings);
_texturedPipeline = texturedPipeline = gpu::Pipeline::create(program, state);
batch.runLambda([program] {
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding(std::string("particleBuffer"), PARTICLE_UNIFORM_SLOT));
gpu::Shader::makeProgram(*program, slotBindings);
});
}
return std::make_shared<render::ShapePipeline>(texturedPipeline, nullptr, nullptr, nullptr);

View file

@ -46,7 +46,7 @@ struct PolyLineUniforms {
glm::vec3 color;
};
static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key) {
static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key, gpu::Batch& batch) {
if (!polylinePipeline) {
auto VS = gpu::Shader::createVertex(std::string(paintStroke_vert));
auto PS = gpu::Shader::createPixel(std::string(paintStroke_frag));
@ -56,15 +56,21 @@ static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlum
auto fadePS = gpu::Shader::createPixel(std::string(paintStroke_fade_frag));
gpu::ShaderPointer fadeProgram = gpu::Shader::createProgram(fadeVS, fadePS);
#endif
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding(std::string("originalTexture"), PAINTSTROKE_TEXTURE_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("polyLineBuffer"), PAINTSTROKE_UNIFORM_SLOT));
gpu::Shader::makeProgram(*program, slotBindings);
batch.runLambda([program
#ifdef POLYLINE_ENTITY_USE_FADE_EFFECT
slotBindings.insert(gpu::Shader::Binding(std::string("fadeMaskMap"), PAINTSTROKE_TEXTURE_SLOT + 1));
slotBindings.insert(gpu::Shader::Binding(std::string("fadeParametersBuffer"), PAINTSTROKE_UNIFORM_SLOT + 1));
gpu::Shader::makeProgram(*fadeProgram, slotBindings);
, fadeProgram
#endif
] {
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding(std::string("originalTexture"), PAINTSTROKE_TEXTURE_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("polyLineBuffer"), PAINTSTROKE_UNIFORM_SLOT));
gpu::Shader::makeProgram(*program, slotBindings);
#ifdef POLYLINE_ENTITY_USE_FADE_EFFECT
slotBindings.insert(gpu::Shader::Binding(std::string("fadeMaskMap"), PAINTSTROKE_TEXTURE_SLOT + 1));
slotBindings.insert(gpu::Shader::Binding(std::string("fadeParametersBuffer"), PAINTSTROKE_UNIFORM_SLOT + 1));
gpu::Shader::makeProgram(*fadeProgram, slotBindings);
#endif
});
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(true, true, gpu::LESS_EQUAL);
PrepareStencil::testMask(*state);

View file

@ -1457,7 +1457,7 @@ static gpu::PipelinePointer _pipelines[2];
static gpu::PipelinePointer _wireframePipelines[2];
static gpu::Stream::FormatPointer _vertexFormat;
ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const ShapeKey& key) {
ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const ShapeKey& key, gpu::Batch& batch) {
if (!_pipelines[0]) {
gpu::ShaderPointer vertexShaders[2] = { gpu::Shader::createVertex(std::string(polyvox_vert)), gpu::Shader::createVertex(std::string(polyvox_fade_vert)) };
gpu::ShaderPointer pixelShaders[2] = { gpu::Shader::createPixel(std::string(polyvox_frag)), gpu::Shader::createPixel(std::string(polyvox_fade_frag)) };
@ -1485,7 +1485,10 @@ ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const Sha
// Two sets of pipelines: normal and fading
for (auto i = 0; i < 2; i++) {
gpu::ShaderPointer program = gpu::Shader::createProgram(vertexShaders[i], pixelShaders[i]);
gpu::Shader::makeProgram(*program, slotBindings);
batch.runLambda([program, slotBindings] {
gpu::Shader::makeProgram(*program, slotBindings);
});
_pipelines[i] = gpu::Pipeline::create(program, state);
_wireframePipelines[i] = gpu::Pipeline::create(program, wireframeState);

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

@ -68,11 +68,10 @@ void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) {
if (masks & Framebuffer::BUFFER_STENCIL) {
glClearStencil(stencil);
glmask |= GL_STENCIL_BUFFER_BIT;
cacheStencilMask = _pipeline._stateCache.stencilActivation.getWriteMaskFront();
if (cacheStencilMask != 0xFF) {
restoreStencilMask = true;
glStencilMask( 0xFF);
glStencilMask(0xFF);
}
}

View file

@ -269,6 +269,10 @@ public:
void _glColor4f(float red, float green, float blue, float alpha);
// Maybe useful but shoudln't be public. Please convince me otherwise
// Well porting to gles i need it...
void runLambda(std::function<void()> f);
enum Command {
COMMAND_draw = 0,
COMMAND_drawIndexed,
@ -497,8 +501,7 @@ protected:
void startNamedCall(const std::string& name);
void stopNamedCall();
// Maybe useful but shoudln't be public. Please convince me otherwise
void runLambda(std::function<void()> f);
void captureDrawCallInfoImpl();
};

View file

@ -89,12 +89,14 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
auto skyFS = gpu::Shader::createPixel(std::string(skybox_frag));
auto skyShader = gpu::Shader::createProgram(skyVS, skyFS);
gpu::Shader::BindingSet bindings;
bindings.insert(gpu::Shader::Binding(std::string("cubeMap"), SKYBOX_SKYMAP_SLOT));
bindings.insert(gpu::Shader::Binding(std::string("skyboxBuffer"), SKYBOX_CONSTANTS_SLOT));
if (!gpu::Shader::makeProgram(*skyShader, bindings)) {
batch.runLambda([skyShader] {
gpu::Shader::BindingSet bindings;
bindings.insert(gpu::Shader::Binding(std::string("cubeMap"), SKYBOX_SKYMAP_SLOT));
bindings.insert(gpu::Shader::Binding(std::string("skyboxBuffer"), SKYBOX_CONSTANTS_SLOT));
if (!gpu::Shader::makeProgram(*skyShader, bindings)) {
}
}
});
auto skyState = std::make_shared<gpu::State>();
// Must match PrepareStencil::STENCIL_BACKGROUND

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

@ -639,7 +639,7 @@ render::ShapePipelinePointer GeometryCache::_simpleWirePipeline;
uint8_t GeometryCache::CUSTOM_PIPELINE_NUMBER = 0;
render::ShapePipelinePointer GeometryCache::shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key) {
render::ShapePipelinePointer GeometryCache::shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key, gpu::Batch& batch) {
initializeShapePipelines();
if (key.isWireframe()) {
@ -2001,11 +2001,11 @@ void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const
}
void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) {
if (!_standardDrawPipeline) {
static std::once_flag once;
std::call_once(once, [&]() {
auto vs = gpu::Shader::createVertex(std::string(standardTransformPNTC_vert));
auto ps = gpu::Shader::createPixel(std::string(standardDrawTexture_frag));
auto program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::makeProgram((*program));
auto state = std::make_shared<gpu::State>();
@ -2021,9 +2021,15 @@ void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) {
auto noBlendPS = gpu::StandardShaderLib::getDrawTextureOpaquePS();
auto programNoBlend = gpu::Shader::createProgram(vs, noBlendPS);
gpu::Shader::makeProgram((*programNoBlend));
_standardDrawPipelineNoBlend = gpu::Pipeline::create(programNoBlend, stateNoBlend);
}
batch.runLambda([program, programNoBlend] {
gpu::Shader::makeProgram((*program));
gpu::Shader::makeProgram((*programNoBlend));
});
});
if (noBlend) {
batch.setPipeline(_standardDrawPipelineNoBlend);
} else {

View file

@ -158,7 +158,7 @@ public:
static void computeSimpleHullPointListForShape(int entityShape, const glm::vec3 &entityExtents, QVector<glm::vec3> &outPointList);
static uint8_t CUSTOM_PIPELINE_NUMBER;
static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key);
static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key, gpu::Batch& batch);
static void registerShapePipeline() {
if (!CUSTOM_PIPELINE_NUMBER) {
CUSTOM_PIPELINE_NUMBER = render::ShapePipeline::registerCustomShapePipelineFactory(shapePipelineFactory);

View file

@ -96,7 +96,7 @@ protected:
float enableObscurance{ 1.0f };
float enableMaterialTexturing { 1.0f };
float enableMaterialTexturing { 0.0f };
float enableWireframe { 0.0f }; // false by default
Parameters() {}
@ -148,7 +148,7 @@ public:
bool enableSpecular{ true };
bool enableAlbedo{ true };
bool enableMaterialTexturing { true };
bool enableMaterialTexturing { false };
bool enableAmbientLight{ true };
bool enableDirectionalLight{ true };

View file

@ -17,7 +17,9 @@
const int MAX_TEXCOORDS = 2;
struct TexMapArray {
mat4 _texcoordTransforms[MAX_TEXCOORDS];
// mat4 _texcoordTransforms[MAX_TEXCOORDS];
mat4 _texcoordTransforms0;
mat4 _texcoordTransforms1;
vec4 _lightmapParams;
};
@ -31,13 +33,13 @@ TexMapArray getTexMapArray() {
<@func evalTexMapArrayTexcoord0(texMapArray, inTexcoord0, outTexcoord0)@>
{
<$outTexcoord0$> = (<$texMapArray$>._texcoordTransforms[0] * vec4(<$inTexcoord0$>.st, 0.0, 1.0)).st;
<$outTexcoord0$> = (<$texMapArray$>._texcoordTransforms0 * vec4(<$inTexcoord0$>.st, 0.0, 1.0)).st;
}
<@endfunc@>
<@func evalTexMapArrayTexcoord1(texMapArray, inTexcoord1, outTexcoord1)@>
{
<$outTexcoord1$> = (<$texMapArray$>._texcoordTransforms[1] * vec4(<$inTexcoord1$>.st, 0.0, 1.0)).st;
<$outTexcoord1$> = (<$texMapArray$>._texcoordTransforms1 * vec4(<$inTexcoord1$>.st, 0.0, 1.0)).st;
}
<@endfunc@>

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

@ -26,6 +26,7 @@
#include "FramebufferCache.h"
#include "TextureCache.h"
#include "RenderCommonTask.h"
#include "LightStage.h"
#include "nop_frag.h"
@ -63,17 +64,21 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend
// GPU jobs: Start preparing the main framebuffer
const auto framebuffer = task.addJob<PrepareFramebuffer>("PrepareFramebuffer");
task.addJob<PrepareForward>("PrepareForward", lightingModel);
// draw a stencil mask in hidden regions of the framebuffer.
task.addJob<PrepareStencil>("PrepareStencil", framebuffer);
// Draw opaques forward
task.addJob<Draw>("DrawOpaques", opaques, shapePlumber);
const auto opaqueInputs = DrawForward::Inputs(opaques, lightingModel).asVarying();
task.addJob<DrawForward>("DrawOpaques", opaqueInputs, shapePlumber);
// Similar to light stage, background stage has been filled by several potential render items and resolved for the frame in this job
task.addJob<DrawBackgroundStage>("DrawBackgroundDeferred", lightingModel);
// Draw transparent objects forward
task.addJob<Draw>("DrawTransparents", transparents, shapePlumber);
const auto transparentInputs = DrawForward::Inputs(transparents, lightingModel).asVarying();
task.addJob<DrawForward>("DrawTransparents", transparentInputs, shapePlumber);
{ // Debug the bounds of the rendered items, still look at the zbuffer
@ -125,18 +130,85 @@ void PrepareFramebuffer::run(const RenderContextPointer& renderContext, gpu::Fra
batch.setFramebuffer(_framebuffer);
batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH |
gpu::Framebuffer::BUFFER_STENCIL,
vec4(vec3(0), 1), 1.0, 0, true);
vec4(vec3(0, 1.0, 0.0), 0), 1.0, 0, true);
});
framebuffer = _framebuffer;
}
void Draw::run(const RenderContextPointer& renderContext, const Inputs& items) {
enum ForwardShader_MapSlot {
DEFERRED_BUFFER_COLOR_UNIT = 0,
DEFERRED_BUFFER_NORMAL_UNIT = 1,
DEFERRED_BUFFER_EMISSIVE_UNIT = 2,
DEFERRED_BUFFER_DEPTH_UNIT = 3,
DEFERRED_BUFFER_OBSCURANCE_UNIT = 4,
SHADOW_MAP_UNIT = 5,
SKYBOX_MAP_UNIT = SHADOW_MAP_UNIT + 4,
DEFERRED_BUFFER_LINEAR_DEPTH_UNIT,
DEFERRED_BUFFER_CURVATURE_UNIT,
DEFERRED_BUFFER_DIFFUSED_CURVATURE_UNIT,
SCATTERING_LUT_UNIT,
SCATTERING_SPECULAR_UNIT,
};
enum ForwardShader_BufferSlot {
DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT = 0,
CAMERA_CORRECTION_BUFFER_SLOT,
SCATTERING_PARAMETERS_BUFFER_SLOT,
LIGHTING_MODEL_BUFFER_SLOT = render::ShapePipeline::Slot::LIGHTING_MODEL,
LIGHT_GPU_SLOT = render::ShapePipeline::Slot::LIGHT,
LIGHT_AMBIENT_SLOT = render::ShapePipeline::Slot::LIGHT_AMBIENT_BUFFER,
HAZE_MODEL_BUFFER_SLOT = render::ShapePipeline::Slot::HAZE_MODEL,
LIGHT_INDEX_GPU_SLOT,
LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT,
LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT,
LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT,
};
void PrepareForward::run(const RenderContextPointer& renderContext, const Inputs& inputs) {
RenderArgs* args = renderContext->args;
const auto& lightingModel = inputs;
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
args->_batch = &batch;
model::LightPointer keySunLight;
auto lightStage = args->_scene->getStage<LightStage>();
if (lightStage) {
keySunLight = lightStage->getCurrentKeyLight();
}
model::LightPointer keyAmbiLight;
if (lightStage) {
keyAmbiLight = lightStage->getCurrentAmbientLight();
}
if (keySunLight) {
if (LIGHT_GPU_SLOT >= 0) {
batch.setUniformBuffer(LIGHT_GPU_SLOT, keySunLight->getLightSchemaBuffer());
}
}
if (keyAmbiLight) {
if (LIGHT_AMBIENT_SLOT >= 0) {
batch.setUniformBuffer(LIGHT_AMBIENT_SLOT, keyAmbiLight->getAmbientSchemaBuffer());
}
if (keyAmbiLight->getAmbientMap() && (SKYBOX_MAP_UNIT >= 0)) {
batch.setResourceTexture(SKYBOX_MAP_UNIT, keyAmbiLight->getAmbientMap());
}
}
});
}
void DrawForward::run(const RenderContextPointer& renderContext, const Inputs& inputs) {
RenderArgs* args = renderContext->args;
const auto& inItems = inputs.get0();
const auto& lightingModel = inputs.get1();
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
args->_batch = &batch;
// Setup projection
glm::mat4 projMat;
Transform viewMat;
@ -146,63 +218,23 @@ void Draw::run(const RenderContextPointer& renderContext, const Inputs& items) {
batch.setViewTransform(viewMat);
batch.setModelTransform(Transform());
// Setup lighting model for all items;
batch.setUniformBuffer(render::ShapePipeline::Slot::LIGHTING_MODEL, lightingModel->getParametersBuffer());
// From the lighting model define a global shapeKey ORED with individiual keys
ShapeKey::Builder keyBuilder;
if (lightingModel->isWireframeEnabled()) {
keyBuilder.withWireframe();
}
ShapeKey globalKey = keyBuilder.build();
args->_globalShapeKey = globalKey._flags.to_ulong();
// Render items
renderStateSortShapes(renderContext, _shapePlumber, items, -1);
renderStateSortShapes(renderContext, _shapePlumber, inItems, -1, globalKey);
args->_batch = nullptr;
args->_globalShapeKey = 0;
});
args->_batch = nullptr;
}
const gpu::PipelinePointer Stencil::getPipeline() {
if (!_stencilPipeline) {
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
auto ps = gpu::Shader::createPixel(std::string(nop_frag));
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::makeProgram(*program);
auto state = std::make_shared<gpu::State>();
state->setDepthTest(true, false, gpu::LESS_EQUAL);
PrepareStencil::drawBackground(*state);
_stencilPipeline = gpu::Pipeline::create(program, state);
}
return _stencilPipeline;
}
void Stencil::run(const RenderContextPointer& renderContext) {
RenderArgs* args = renderContext->args;
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
args->_batch = &batch;
batch.enableStereo(false);
batch.setViewportTransform(args->_viewport);
batch.setStateScissorRect(args->_viewport);
batch.setPipeline(getPipeline());
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
args->_batch = nullptr;
}
void DrawBackground::run(const RenderContextPointer& renderContext, const Inputs& background) {
RenderArgs* args = renderContext->args;
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
args->_batch = &batch;
batch.enableSkybox(true);
batch.setViewportTransform(args->_viewport);
batch.setStateScissorRect(args->_viewport);
// Setup projection
glm::mat4 projMat;
Transform viewMat;
args->getViewFrustum().evalProjectionMatrix(projMat);
args->getViewFrustum().evalViewTransform(viewMat);
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
renderItems(renderContext, background);
});
args->_batch = nullptr;
}

View file

@ -38,37 +38,28 @@ private:
gpu::FramebufferPointer _framebuffer;
};
class Draw {
class PrepareForward {
public:
using Inputs = render::ItemBounds;
using JobModel = render::Job::ModelI<Draw, Inputs>;
using Inputs = LightingModelPointer;
using JobModel = render::Job::ModelI<PrepareForward, Inputs>;
Draw(const render::ShapePlumberPointer& shapePlumber) : _shapePlumber(shapePlumber) {}
void run(const render::RenderContextPointer& renderContext,
const Inputs& items);
const Inputs& inputs);
private:
};
class DrawForward{
public:
using Inputs = render::VaryingSet2<render::ItemBounds, LightingModelPointer>;
using JobModel = render::Job::ModelI<DrawForward, Inputs>;
DrawForward(const render::ShapePlumberPointer& shapePlumber) : _shapePlumber(shapePlumber) {}
void run(const render::RenderContextPointer& renderContext,
const Inputs& inputs);
private:
render::ShapePlumberPointer _shapePlumber;
};
class Stencil {
public:
using JobModel = render::Job::Model<Stencil>;
void run(const render::RenderContextPointer& renderContext);
private:
const gpu::PipelinePointer getPipeline();
gpu::PipelinePointer _stencilPipeline;
};
class DrawBackground {
public:
using Inputs = render::ItemBounds;
using JobModel = render::Job::ModelI<DrawBackground, Inputs>;
void run(const render::RenderContextPointer& renderContext,
const Inputs& background);
};
#endif // hifi_RenderForwardTask_h

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.0 - twoRealXSq - twoRealZSq,
twoRealYZ + twoRealXW,
0.0);
vec4 col2 = vec4(twoRealXZ + twoRealYW,
twoRealYZ - twoRealXW,
1.0 - 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.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.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.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

@ -68,7 +68,7 @@ void main(void) {
vec3 fragNormal;
<$transformEyeToWorldDir(cam, _normal, fragNormal)$>
/* vec4 color = vec4(evalSkyboxGlobalColor(
vec4 color = vec4(evalSkyboxGlobalColor(
cam._viewInverse,
1.0,
1.0,
@ -81,8 +81,6 @@ void main(void) {
opacity);
color.rgb += emissive * isEmissiveEnabled();
*/
_fragColor = vec4(albedo, opacity);
// _fragColor = vec4(albedo, opacity);
_fragColor = color;
}

View file

@ -12,10 +12,16 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include ForwardBufferWrite.slh@>
<! <@include ForwardBufferWrite.slh@> !>
<@include ForwardGlobalLight.slh@>
<$declareEvalSkyboxGlobalColor()$>
<@include model/Material.slh@>
<@include gpu/Transform.slh@>
<$declareStandardCameraTransform()$>
<@include MaterialTextures.slh@>
<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, _SCRIBE_NULL, EMISSIVE, OCCLUSION, SCATTERING)$>
@ -26,6 +32,8 @@ in vec3 _normal;
in vec3 _tangent;
in vec3 _color;
out vec4 _fragColor;
void main(void) {
Material mat = getMaterial();
int matKey = getMaterialKey(mat);
@ -40,6 +48,15 @@ void main(void) {
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
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;
}
float roughness = getMaterialRoughness(mat);
<$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>;
@ -52,13 +69,24 @@ void main(void) {
float scattering = getMaterialScattering(mat);
<$evalMaterialScattering(scatteringTex, scattering, matKey, scattering)$>;
packForwardFragment(
viewNormal,
opacity,
vec3 fragPosition = _position.xyz;
TransformCamera cam = getTransformCamera();
vec4 color = vec4(evalSkyboxGlobalColor(
cam._viewInverse,
1.0,
1.0,
fragPosition,
viewNormal,
albedo,
roughness,
getMaterialMetallic(mat),
emissive,
occlusionTex,
scattering);
fresnel,
metallic,
roughness),
opacity);
color.rgb += emissive * isEmissiveEnabled();
// _fragColor = vec4(albedo, opacity);
_fragColor = color;
}

View file

@ -12,10 +12,17 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include ForwardBufferWrite.slh@>
<!<@include ForwardBufferWrite.slh@> !>
<@include ForwardGlobalLight.slh@>
<$declareEvalSkyboxGlobalColor()$>
<@include model/Material.slh@>
<@include gpu/Transform.slh@>
<$declareStandardCameraTransform()$>
<@include MaterialTextures.slh@>
<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, METALLIC, EMISSIVE, OCCLUSION)$>
@ -26,6 +33,8 @@ in vec3 _normal;
in vec3 _tangent;
in vec3 _color;
out vec4 _fragColor;
void main(void) {
Material mat = getMaterial();
int matKey = getMaterialKey(mat);
@ -51,16 +60,32 @@ void main(void) {
float metallic = getMaterialMetallic(mat);
<$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>;
vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value
if (metallic <= 0.5) {
metallic = 0.0;
}
else {
fresnel = albedo;
metallic = 1.0;
}
vec3 fragPosition = _position.xyz;
float scattering = getMaterialScattering(mat);
TransformCamera cam = getTransformCamera();
packForwardFragment(
normalize(viewNormal.xyz),
opacity,
vec4 color = vec4(evalSkyboxGlobalColor(
cam._viewInverse,
1.0,
1.0,
fragPosition,
viewNormal,
albedo,
roughness,
fresnel,
metallic,
emissive,
occlusionTex,
scattering);
roughness),
opacity);
color.rgb += emissive * isEmissiveEnabled();
// _fragColor = vec4(albedo, opacity);
_fragColor = color;
}

View file

@ -12,10 +12,17 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include ForwardBufferWrite.slh@>
<!<@include ForwardBufferWrite.slh@> !>
<@include ForwardGlobalLight.slh@>
<$declareEvalSkyboxGlobalColor()$>
<@include model/Material.slh@>
<@include gpu/Transform.slh@>
<$declareStandardCameraTransform()$>
<@include MaterialTextures.slh@>
<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC, EMISSIVE, OCCLUSION)$>
@ -25,12 +32,13 @@ in vec2 _texCoord1;
in vec3 _normal;
in vec3 _color;
out vec4 _fragColor;
void main(void) {
Material mat = getMaterial();
int matKey = getMaterialKey(mat);
<$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, metallicTex, emissiveTex)$>
<$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$>
<! <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> !>
float opacity = 1.0;
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
@ -48,7 +56,14 @@ void main(void) {
float metallic = getMaterialMetallic(mat);
<$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>;
vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value
if (metallic <= 0.5) {
metallic = 0.0;
}
else {
fresnel = albedo;
metallic = 1.0;
}/*
float scattering = getMaterialScattering(mat);
packForwardFragment(
@ -60,4 +75,26 @@ void main(void) {
emissive,
occlusionTex,
scattering);
*/
vec3 fragPosition = _position.xyz;
TransformCamera cam = getTransformCamera();
vec3 fragNormal;
<$transformEyeToWorldDir(cam, _normal, fragNormal)$>
vec4 color = vec4(evalSkyboxGlobalColor(
cam._viewInverse,
1.0,
1.0,
fragPosition,
fragNormal,
albedo,
fresnel,
metallic,
roughness),
opacity);
color.rgb += emissive * isEmissiveEnabled();
// _fragColor = vec4(albedo, opacity);
_fragColor = color;
}

View file

@ -131,7 +131,7 @@ const ShapePipelinePointer ShapePlumber::pickPipeline(RenderArgs* args, const Ke
auto factoryIt = ShapePipeline::_globalCustomFactoryMap.find(key.getCustom());
if ((factoryIt != ShapePipeline::_globalCustomFactoryMap.end()) && (factoryIt)->second) {
// found a factory for the custom key, can now generate a shape pipeline for this case:
addPipelineHelper(Filter(key), key, 0, (factoryIt)->second(*this, key));
addPipelineHelper(Filter(key), key, 0, (factoryIt)->second(*this, key, *(args->_batch)));
return pickPipeline(args, key);
} else {

View file

@ -301,7 +301,7 @@ protected:
ItemSetter _itemSetter;
public:
using CustomKey = uint8_t;
using CustomFactory = std::function<std::shared_ptr<ShapePipeline> (const ShapePlumber& plumber, const ShapeKey& key)>;
using CustomFactory = std::function<std::shared_ptr<ShapePipeline> (const ShapePlumber& plumber, const ShapeKey& key, gpu::Batch& batch)>;
using CustomFactoryMap = std::map<CustomKey, CustomFactory>;
static CustomFactoryMap _globalCustomFactoryMap;

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