diff --git a/assignment-client/src/avatars/MixerAvatar.cpp b/assignment-client/src/avatars/MixerAvatar.cpp index 29f7e9ebd2..9be93dad40 100644 --- a/assignment-client/src/avatars/MixerAvatar.cpp +++ b/assignment-client/src/avatars/MixerAvatar.cpp @@ -33,7 +33,7 @@ MixerAvatar::MixerAvatar() { _challengeTimer.setSingleShot(true); _challengeTimer.setInterval(CHALLENGE_TIMEOUT_MS); - _challengeTimer.callOnTimeout([this]() { + _challengeTimer.callOnTimeout(this, [this]() { if (_verifyState == challengeClient) { _pendingEvent = false; _verifyState = verificationFailed; diff --git a/cmake/macros/TargetWebRTC.cmake b/cmake/macros/TargetWebRTC.cmake index d2821528df..d215b0698e 100644 --- a/cmake/macros/TargetWebRTC.cmake +++ b/cmake/macros/TargetWebRTC.cmake @@ -15,9 +15,11 @@ macro(TARGET_WEBRTC) # select_library_configurations(WEBRTC) else() set(WEBRTC_INCLUDE_DIRS "${VCPKG_INSTALL_ROOT}/include/webrtc") - find_library(WEBRTC_LIBRARY NAMES webrtc PATHS ${VCPKG_INSTALL_ROOT}/lib/ NO_DEFAULT_PATH) target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${WEBRTC_INCLUDE_DIRS}) - target_link_libraries(${TARGET_NAME} ${WEBRTC_LIBRARY}) + find_library(WEBRTC_LIBRARY_RELEASE webrtc PATHS ${VCPKG_INSTALL_ROOT}/lib NO_DEFAULT_PATH) + find_library(WEBRTC_LIBRARY_DEBUG webrtc PATHS ${VCPKG_INSTALL_ROOT}/debug/lib NO_DEFAULT_PATH) + select_library_configurations(WEBRTC) + target_link_libraries(${TARGET_NAME} ${WEBRTC_LIBRARIES}) endif() diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index c08cf47030..4bcd7aaf0f 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -117,6 +117,7 @@ if (APPLE) # configure CMake to use a custom Info.plist set_target_properties(${this_target} PROPERTIES MACOSX_BUNDLE_INFO_PLIST MacOSXBundleInfo.plist.in) + set(MACOSX_BUNDLE_BUNDLE_NAME "High Fidelity") if (PRODUCTION_BUILD) set(MACOSX_BUNDLE_GUI_IDENTIFIER com.highfidelity.interface) else () diff --git a/interface/resources/qml/hifi/simplifiedUI/settingsApp/general/General.qml b/interface/resources/qml/hifi/simplifiedUI/settingsApp/general/General.qml index 9e58a0aa98..24fff2dae4 100644 --- a/interface/resources/qml/hifi/simplifiedUI/settingsApp/general/General.qml +++ b/interface/resources/qml/hifi/simplifiedUI/settingsApp/general/General.qml @@ -136,17 +136,17 @@ Flickable { Layout.preferredHeight: 18 Layout.preferredWidth: parent.width labelTextOn: "Show Emote UI" - checked: Settings.getValue("simplifiedUI/emoteIndicatorVisible", true) + checked: Settings.getValue("simplifiedUI/allowEmoteDrawerExpansion", true) onClicked: { - var currentSetting = Settings.getValue("simplifiedUI/emoteIndicatorVisible", true); - Settings.setValue("simplifiedUI/emoteIndicatorVisible", !currentSetting); + var currentSetting = Settings.getValue("simplifiedUI/allowEmoteDrawerExpansion", true); + Settings.setValue("simplifiedUI/allowEmoteDrawerExpansion", !currentSetting); } Connections { target: Settings onValueChanged: { - if (setting === "simplifiedUI/emoteIndicatorVisible") { + if (setting === "simplifiedUI/allowEmoteDrawerExpansion") { emoteSwitch.checked = value; } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index bb6f036533..0108fb5eda 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -154,6 +154,7 @@ class MyAvatar : public Avatar { * * @property {Vec3} qmlPosition - A synonym for position for use by QML. * + * @property {Vec3} feetPosition - The position of the avatar's feet. * @property {boolean} shouldRenderLocally=true - If true then your avatar is rendered for you in Interface, * otherwise it is not rendered for you (but it is still rendered for other users). * @property {Vec3} motorVelocity=Vec3.ZERO - The target velocity of your avatar to be achieved by a scripted motor. @@ -340,6 +341,7 @@ class MyAvatar : public Avatar { Q_PROPERTY(QVector3D qmlPosition READ getQmlPosition) QVector3D getQmlPosition() { auto p = getWorldPosition(); return QVector3D(p.x, p.y, p.z); } + Q_PROPERTY(glm::vec3 feetPosition READ getWorldFeetPosition WRITE goToFeetLocation) Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally) Q_PROPERTY(glm::vec3 motorVelocity READ getScriptedMotorVelocity WRITE setScriptedMotorVelocity) Q_PROPERTY(float motorTimescale READ getScriptedMotorTimescale WRITE setScriptedMotorTimescale) @@ -1938,9 +1940,8 @@ public slots: * @param {boolean} [shouldFaceLocation=false] - Set to true to position the avatar a short distance away from * the new position and orientate the avatar to face the position. */ - void goToFeetLocation(const glm::vec3& newPosition, - bool hasOrientation, const glm::quat& newOrientation, - bool shouldFaceLocation); + void goToFeetLocation(const glm::vec3& newPosition, bool hasOrientation = false, + const glm::quat& newOrientation = glm::quat(), bool shouldFaceLocation = false); /**jsdoc * Moves the avatar to a new position and/or orientation in the domain. diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 6080911dd9..9d5693da33 100755 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -337,10 +337,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { eyeParams.modelTranslation = getTranslation(); eyeParams.leftEyeJointIndex = _rig.indexOfJoint("LeftEye"); eyeParams.rightEyeJointIndex = _rig.indexOfJoint("RightEye"); - - if (_owningAvatar->getHasProceduralEyeFaceMovement()) { - _rig.updateFromEyeParameters(eyeParams); - } + _rig.updateFromEyeParameters(eyeParams); updateFingers(); } diff --git a/launchers/darwin/src/DownloadLauncher.m b/launchers/darwin/src/DownloadLauncher.m index 7c32cf7d90..c54227ba83 100644 --- a/launchers/darwin/src/DownloadLauncher.m +++ b/launchers/darwin/src/DownloadLauncher.m @@ -1,6 +1,10 @@ #import "DownloadLauncher.h" #import "Launcher.h" +#include + +static const __int32_t kMinLauncherSize = 250000; // 308kb is our smallest launcher +static const NSString *kIOError = @"IOError"; @implementation DownloadLauncher @@ -22,8 +26,38 @@ } +-(void)validateHQLauncherZipAt:(NSURL *)location { + // Does the file look like a valid HQLauncher ZIP? + struct stat lStat; + const char *cStrLocation = location.fileSystemRepresentation; + if (stat(cStrLocation, &lStat) != 0) { + NSLog(@"couldn't stat download file: %s", cStrLocation); + @throw [NSException exceptionWithName:(NSString *)kIOError + reason:@"couldn't stat download file" + userInfo:nil]; + } + if (lStat.st_size <= kMinLauncherSize) { + NSLog(@"download is too small: %s is %lld bytes, should be at least %d bytes", + cStrLocation, lStat.st_size, kMinLauncherSize); + @throw [NSException exceptionWithName:(NSString *)kIOError + reason:@"download is too small" + userInfo:nil]; + } +} + -(void)URLSession:(NSURLSession*)session downloadTask:(NSURLSessionDownloadTask*)downloadTask didFinishDownloadingToURL:(NSURL*)location { NSLog(@"Did finish downloading to url"); + @try { + [self validateHQLauncherZipAt:location]; + } + @catch (NSException *exc) { + if ([exc.name isEqualToString:(NSString *)kIOError]) { + [[Launcher sharedLauncher] displayErrorPage]; + return; + } + @throw; + } + NSError* error = nil; NSFileManager* fileManager = [NSFileManager defaultManager]; NSString* destinationFileName = downloadTask.originalRequest.URL.lastPathComponent; diff --git a/launchers/darwin/src/updater/main.m b/launchers/darwin/src/updater/main.m index 7c7ace6f70..f8fe5a598c 100644 --- a/launchers/darwin/src/updater/main.m +++ b/launchers/darwin/src/updater/main.m @@ -21,15 +21,39 @@ int main(int argc, char const* argv[]) { for (int index = 0; index < argc; index++) { NSLog(@"argv at index %d = %s", index, argv[index]); } + + NSError *error = nil; + NSString* oldLauncher = [NSString stringWithUTF8String:argv[1]]; NSString* newLauncher = [NSString stringWithUTF8String:argv[2]]; NSURL* destinationUrl = [UpdaterHelper NSStringToNSURL:newLauncher]; NSFileManager* fileManager = [NSFileManager defaultManager]; - [fileManager replaceItemAtURL:[UpdaterHelper NSStringToNSURL:oldLauncher] withItemAtURL:[UpdaterHelper NSStringToNSURL:newLauncher] backupItemName:nil options:NSFileManagerItemReplacementUsingNewMetadataOnly resultingItemURL:&destinationUrl error:nil]; + [fileManager replaceItemAtURL:[UpdaterHelper NSStringToNSURL:oldLauncher] + withItemAtURL:[UpdaterHelper NSStringToNSURL:newLauncher] + backupItemName:nil + options:NSFileManagerItemReplacementUsingNewMetadataOnly + resultingItemURL:&destinationUrl + error:&error]; + if (error != nil) { + NSLog(@"couldn't update launcher: %@", error); + return 1; + } + NSWorkspace* workspace = [NSWorkspace sharedWorkspace]; NSURL* applicationURL = [UpdaterHelper NSStringToNSURL: [oldLauncher stringByAppendingString: @"/Contents/MacOS/HQ Launcher"]]; NSArray* arguments =@[]; NSLog(@"Launcher agruments: %@", arguments); - [workspace launchApplicationAtURL:applicationURL options:NSWorkspaceLaunchNewInstance configuration:[NSDictionary dictionaryWithObject:arguments forKey:NSWorkspaceLaunchConfigurationArguments] error:nil]; + + NSDictionary *configuration = [NSDictionary dictionaryWithObject:arguments + forKey:NSWorkspaceLaunchConfigurationArguments]; + [workspace launchApplicationAtURL:applicationURL + options:NSWorkspaceLaunchNewInstance + configuration:configuration + error:&error]; + if (error != nil) { + NSLog(@"couldn't start launcher: %@", error); + return 1; + } + return 0; } diff --git a/launchers/win32/LauncherDlg.cpp b/launchers/win32/LauncherDlg.cpp index 9cf21e9c8c..386d2747d6 100644 --- a/launchers/win32/LauncherDlg.cpp +++ b/launchers/win32/LauncherDlg.cpp @@ -13,6 +13,8 @@ #include "LauncherApp.h" #include "LauncherDlg.h" +#include +#include #include #pragma comment(lib, "d2d1") @@ -84,7 +86,7 @@ END_MESSAGE_MAP() BOOL CLauncherDlg::OnInitDialog() { CDialog::OnInitDialog(); - + MarkWindowAsUnpinnable(); SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon @@ -129,6 +131,19 @@ BOOL CLauncherDlg::OnInitDialog() { return TRUE; } +void CLauncherDlg::MarkWindowAsUnpinnable() { + HWND hwnd = AfxGetMainWnd()->m_hWnd; + IPropertyStore* pps; + HRESULT hr = SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(&pps)); + if (SUCCEEDED(hr)) { + PROPVARIANT var; + var.vt = VT_BOOL; + var.boolVal = VARIANT_TRUE; + hr = pps->SetValue(PKEY_AppUserModel_PreventPinning, var); + pps->Release(); + } +} + POINT CLauncherDlg::getMouseCoords(MSG* pMsg) { POINT pos; pos.x = (int)(short)LOWORD(pMsg->lParam); diff --git a/launchers/win32/LauncherDlg.h b/launchers/win32/LauncherDlg.h index d25618c554..b0ccbfa04f 100644 --- a/launchers/win32/LauncherDlg.h +++ b/launchers/win32/LauncherDlg.h @@ -59,6 +59,7 @@ protected: BOOL getTextFormat(int ResID, TextFormat& formatOut); void showWindows(std::vector windows, bool show); POINT getMouseCoords(MSG* pMsg); + void MarkWindowAsUnpinnable(); bool _isConsoleRunning { false }; diff --git a/launchers/win32/LauncherUtils.cpp b/launchers/win32/LauncherUtils.cpp index ef23073ca5..4f8e7f9a74 100644 --- a/launchers/win32/LauncherUtils.cpp +++ b/launchers/win32/LauncherUtils.cpp @@ -150,7 +150,7 @@ BOOL LauncherUtils::launchApplication(LPCWSTR lpApplicationName, LPTSTR cmdArgs) ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); - + // start the program up BOOL success = CreateProcess( lpApplicationName, // the path diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index 17ca88123f..c066dc92eb 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -14,9 +14,10 @@ #include "AnimUtil.h" #include "AnimClip.h" -AnimBlendLinear::AnimBlendLinear(const QString& id, float alpha) : +AnimBlendLinear::AnimBlendLinear(const QString& id, float alpha, AnimBlendType blendType) : AnimNode(AnimNode::Type::BlendLinear, id), - _alpha(alpha) { + _alpha(alpha), + _blendType(blendType) { } @@ -36,6 +37,19 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, con } else if (_children.size() == 1) { _poses = _children[0]->evaluate(animVars, context, dt, triggersOut); context.setDebugAlpha(_children[0]->getID(), parentDebugAlpha, _children[0]->getType()); + } else if (_children.size() == 2 && _blendType != AnimBlendType_Normal) { + // special case for additive blending + float alpha = glm::clamp(_alpha, 0.0f, 1.0f); + const size_t prevPoseIndex = 0; + const size_t nextPoseIndex = 1; + evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, dt); + + // for animation stack debugging + float weight2 = alpha; + float weight1 = 1.0f - weight2; + context.setDebugAlpha(_children[prevPoseIndex]->getID(), weight1 * parentDebugAlpha, _children[prevPoseIndex]->getType()); + context.setDebugAlpha(_children[nextPoseIndex]->getID(), weight2 * parentDebugAlpha, _children[nextPoseIndex]->getType()); + } else { float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1)); size_t prevPoseIndex = glm::floor(clampedAlpha); @@ -79,7 +93,35 @@ void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, c if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) { _poses.resize(prevPoses.size()); - ::blend(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]); + if (_blendType == AnimBlendType_Normal) { + ::blend(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]); + } else if (_blendType == AnimBlendType_AddRelative) { + ::blendAdd(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]); + } else if (_blendType == AnimBlendType_AddAbsolute) { + // convert prev from relative to absolute + AnimPoseVec absPrev = prevPoses; + _skeleton->convertRelativePosesToAbsolute(absPrev); + + // rotate the offset rotations from next into the parent relative frame of each joint. + AnimPoseVec relOffsetPoses; + relOffsetPoses.reserve(nextPoses.size()); + for (size_t i = 0; i < nextPoses.size(); ++i) { + + // copy translation and scale from nextPoses + AnimPose pose = nextPoses[i]; + + int parentIndex = _skeleton->getParentIndex((int)i); + if (parentIndex >= 0) { + // but transform nextPoses rot into absPrev parent frame. + pose.rot() = glm::inverse(absPrev[parentIndex].rot()) * pose.rot() * absPrev[parentIndex].rot(); + } + + relOffsetPoses.push_back(pose); + } + + // then blend + ::blendAdd(_poses.size(), &prevPoses[0], &relOffsetPoses[0], alpha, &_poses[0]); + } } } } diff --git a/libraries/animation/src/AnimBlendLinear.h b/libraries/animation/src/AnimBlendLinear.h index d0fe2a8503..40a20adb2c 100644 --- a/libraries/animation/src/AnimBlendLinear.h +++ b/libraries/animation/src/AnimBlendLinear.h @@ -27,7 +27,7 @@ class AnimBlendLinear : public AnimNode { public: friend class AnimTests; - AnimBlendLinear(const QString& id, float alpha); + AnimBlendLinear(const QString& id, float alpha, AnimBlendType blendType); virtual ~AnimBlendLinear() override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override; @@ -44,6 +44,7 @@ protected: AnimPoseVec _poses; float _alpha; + AnimBlendType _blendType; QString _alphaVar; diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index e5edb46f69..96bc87d738 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -16,100 +16,6 @@ #include "AnimationLogging.h" #include "AnimUtil.h" - -AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag) : - AnimNode(AnimNode::Type::Clip, id), - _startFrame(startFrame), - _endFrame(endFrame), - _timeScale(timeScale), - _loopFlag(loopFlag), - _mirrorFlag(mirrorFlag), - _frame(startFrame) -{ - loadURL(url); -} - -AnimClip::~AnimClip() { - -} - -const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) { - - // lookup parameters from animVars, using current instance variables as defaults. - _startFrame = animVars.lookup(_startFrameVar, _startFrame); - _endFrame = animVars.lookup(_endFrameVar, _endFrame); - _timeScale = animVars.lookup(_timeScaleVar, _timeScale); - _loopFlag = animVars.lookup(_loopFlagVar, _loopFlag); - _mirrorFlag = animVars.lookup(_mirrorFlagVar, _mirrorFlag); - float frame = animVars.lookup(_frameVar, _frame); - - _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame, dt, _loopFlag, _id, triggersOut); - - // poll network anim to see if it's finished loading yet. - if (_networkAnim && _networkAnim->isLoaded() && _skeleton) { - // loading is complete, copy animation frames from network animation, then throw it away. - copyFromNetworkAnim(); - _networkAnim.reset(); - } - - if (_anim.size()) { - - // lazy creation of mirrored animation frames. - if (_mirrorFlag && _anim.size() != _mirrorAnim.size()) { - buildMirrorAnim(); - } - - int prevIndex = (int)glm::floor(_frame); - int nextIndex; - if (_loopFlag && _frame >= _endFrame) { - nextIndex = (int)glm::ceil(_startFrame); - } else { - nextIndex = (int)glm::ceil(_frame); - } - - // It can be quite possible for the user to set _startFrame and _endFrame to - // values before or past valid ranges. We clamp the frames here. - int frameCount = (int)_anim.size(); - prevIndex = std::min(std::max(0, prevIndex), frameCount - 1); - nextIndex = std::min(std::max(0, nextIndex), frameCount - 1); - - const AnimPoseVec& prevFrame = _mirrorFlag ? _mirrorAnim[prevIndex] : _anim[prevIndex]; - const AnimPoseVec& nextFrame = _mirrorFlag ? _mirrorAnim[nextIndex] : _anim[nextIndex]; - float alpha = glm::fract(_frame); - - ::blend(_poses.size(), &prevFrame[0], &nextFrame[0], alpha, &_poses[0]); - } - - processOutputJoints(triggersOut); - - return _poses; -} - -void AnimClip::loadURL(const QString& url) { - auto animCache = DependencyManager::get(); - _networkAnim = animCache->getAnimation(url); - _url = url; -} - -void AnimClip::setCurrentFrameInternal(float frame) { - // because dt is 0, we should not encounter any triggers - const float dt = 0.0f; - AnimVariantMap triggers; - _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame + _startFrame, dt, _loopFlag, _id, triggers); -} - -static std::vector buildJointIndexMap(const AnimSkeleton& dstSkeleton, const AnimSkeleton& srcSkeleton) { - std::vector jointIndexMap; - int srcJointCount = srcSkeleton.getNumJoints(); - jointIndexMap.reserve(srcJointCount); - for (int srcJointIndex = 0; srcJointIndex < srcJointCount; srcJointIndex++) { - QString srcJointName = srcSkeleton.getJointName(srcJointIndex); - int dstJointIndex = dstSkeleton.nameToJointIndex(srcJointName); - jointIndexMap.push_back(dstJointIndex); - } - return jointIndexMap; -} - #ifdef USE_CUSTOM_ASSERT #undef ASSERT #define ASSERT(x) \ @@ -123,12 +29,73 @@ static std::vector buildJointIndexMap(const AnimSkeleton& dstSkeleton, cons #define ASSERT assert #endif -void AnimClip::copyFromNetworkAnim() { - assert(_networkAnim && _networkAnim->isLoaded() && _skeleton); - _anim.clear(); +static std::vector buildJointIndexMap(const AnimSkeleton& dstSkeleton, const AnimSkeleton& srcSkeleton) { + std::vector jointIndexMap; + int srcJointCount = srcSkeleton.getNumJoints(); + jointIndexMap.reserve(srcJointCount); + for (int srcJointIndex = 0; srcJointIndex < srcJointCount; srcJointIndex++) { + QString srcJointName = srcSkeleton.getJointName(srcJointIndex); + int dstJointIndex = dstSkeleton.nameToJointIndex(srcJointName); + jointIndexMap.push_back(dstJointIndex); + } + return jointIndexMap; +} - auto avatarSkeleton = getSkeleton(); - const HFMModel& animModel = _networkAnim->getHFMModel(); +static void bakeRelativeDeltaAnim(std::vector& anim, const AnimPoseVec& basePoses) { + + // invert all the basePoses + AnimPoseVec invBasePoses = basePoses; + for (auto&& invBasePose : invBasePoses) { + invBasePose = invBasePose.inverse(); + } + + // for each frame of the animation + for (auto&& animPoses : anim) { + ASSERT(animPoses.size() == basePoses.size()); + + // for each joint in animPoses + for (size_t i = 0; i < animPoses.size(); ++i) { + // convert this relative AnimPose into a delta animation. + animPoses[i] = animPoses[i] * invBasePoses[i]; + } + } +} + +void bakeAbsoluteDeltaAnim(std::vector& anim, const AnimPoseVec& basePoses, AnimSkeleton::ConstPointer skeleton) { + + // invert all the basePoses + AnimPoseVec invBasePoses = basePoses; + for (auto&& invBasePose : invBasePoses) { + invBasePose = invBasePose.inverse(); + } + + AnimPoseVec absBasePoses = basePoses; + skeleton->convertRelativePosesToAbsolute(absBasePoses); + + // for each frame of the animation + for (auto&& animPoses : anim) { + ASSERT(animPoses.size() == basePoses.size()); + + // for each joint in animPoses + for (size_t i = 0; i < animPoses.size(); ++i) { + + // scale and translation are relative frame + animPoses[i] = animPoses[i] * invBasePoses[i]; + + // but transform the rotation delta into the absolute frame. + int parentIndex = skeleton->getParentIndex((int)i); + if (parentIndex >= 0) { + animPoses[i].rot() = absBasePoses[parentIndex].rot() * animPoses[i].rot() * glm::inverse(absBasePoses[parentIndex].rot()); + } + } + } +} + +static std::vector copyAndRetargetFromNetworkAnim(AnimationPointer networkAnim, AnimSkeleton::ConstPointer avatarSkeleton) { + ASSERT(networkAnim && networkAnim->isLoaded() && avatarSkeleton); + std::vector anim; + + const HFMModel& animModel = networkAnim->getHFMModel(); AnimSkeleton animSkeleton(animModel); const int animJointCount = animSkeleton.getNumJoints(); const int avatarJointCount = avatarSkeleton->getNumJoints(); @@ -137,7 +104,7 @@ void AnimClip::copyFromNetworkAnim() { std::vector avatarToAnimJointIndexMap = buildJointIndexMap(animSkeleton, *avatarSkeleton); const int animFrameCount = animModel.animationFrames.size(); - _anim.resize(animFrameCount); + anim.resize(animFrameCount); // find the size scale factor for translation in the animation. float boneLengthScale = 1.0f; @@ -223,8 +190,8 @@ void AnimClip::copyFromNetworkAnim() { // convert avatar rotations into relative frame avatarSkeleton->convertAbsoluteRotationsToRelative(avatarRotations); - ASSERT(frame >= 0 && frame < (int)_anim.size()); - _anim[frame].reserve(avatarJointCount); + ASSERT(frame >= 0 && frame < (int)anim.size()); + anim[frame].reserve(avatarJointCount); for (int avatarJointIndex = 0; avatarJointIndex < avatarJointCount; avatarJointIndex++) { const AnimPose& avatarDefaultPose = avatarSkeleton->getRelativeDefaultPose(avatarJointIndex); @@ -251,14 +218,129 @@ void AnimClip::copyFromNetworkAnim() { // build the final pose ASSERT(avatarJointIndex >= 0 && avatarJointIndex < (int)avatarRotations.size()); - _anim[frame].push_back(AnimPose(relativeScale, avatarRotations[avatarJointIndex], relativeTranslation)); + anim[frame].push_back(AnimPose(relativeScale, avatarRotations[avatarJointIndex], relativeTranslation)); } } - // mirrorAnim will be re-built on demand, if needed. - _mirrorAnim.clear(); + return anim; +} - _poses.resize(avatarJointCount); +AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag, + AnimBlendType blendType, const QString& baseURL, float baseFrame) : + AnimNode(AnimNode::Type::Clip, id), + _startFrame(startFrame), + _endFrame(endFrame), + _timeScale(timeScale), + _loopFlag(loopFlag), + _mirrorFlag(mirrorFlag), + _frame(startFrame), + _blendType(blendType), + _baseFrame(baseFrame) +{ + loadURL(url); + + if (blendType != AnimBlendType_Normal) { + auto animCache = DependencyManager::get(); + _baseNetworkAnim = animCache->getAnimation(baseURL); + _baseURL = baseURL; + } +} + +AnimClip::~AnimClip() { + +} + +const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) { + + // lookup parameters from animVars, using current instance variables as defaults. + _startFrame = animVars.lookup(_startFrameVar, _startFrame); + _endFrame = animVars.lookup(_endFrameVar, _endFrame); + _timeScale = animVars.lookup(_timeScaleVar, _timeScale); + _loopFlag = animVars.lookup(_loopFlagVar, _loopFlag); + _mirrorFlag = animVars.lookup(_mirrorFlagVar, _mirrorFlag); + float frame = animVars.lookup(_frameVar, _frame); + + _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame, dt, _loopFlag, _id, triggersOut); + + // poll network anim to see if it's finished loading yet. + if (_blendType == AnimBlendType_Normal) { + if (_networkAnim && _networkAnim->isLoaded() && _skeleton) { + // loading is complete, copy & retarget animation. + _anim = copyAndRetargetFromNetworkAnim(_networkAnim, _skeleton); + + // we no longer need the actual animation resource anymore. + _networkAnim.reset(); + + // mirrorAnim will be re-built on demand, if needed. + _mirrorAnim.clear(); + + _poses.resize(_skeleton->getNumJoints()); + } + } else { + // an additive blend type + if (_networkAnim && _networkAnim->isLoaded() && _baseNetworkAnim && _baseNetworkAnim->isLoaded() && _skeleton) { + // loading is complete, copy & retarget animation. + _anim = copyAndRetargetFromNetworkAnim(_networkAnim, _skeleton); + + // we no longer need the actual animation resource anymore. + _networkAnim.reset(); + + // mirrorAnim will be re-built on demand, if needed. + // TODO: handle mirrored relative animations. + _mirrorAnim.clear(); + + _poses.resize(_skeleton->getNumJoints()); + + // copy & retarget baseAnim! + auto baseAnim = copyAndRetargetFromNetworkAnim(_baseNetworkAnim, _skeleton); + + if (_blendType == AnimBlendType_AddAbsolute) { + bakeAbsoluteDeltaAnim(_anim, baseAnim[(int)_baseFrame], _skeleton); + } else { + // AnimBlendType_AddRelative + bakeRelativeDeltaAnim(_anim, baseAnim[(int)_baseFrame]); + } + } + } + + if (_anim.size()) { + + // lazy creation of mirrored animation frames. + if (_mirrorFlag && _anim.size() != _mirrorAnim.size()) { + buildMirrorAnim(); + } + + int prevIndex = (int)glm::floor(_frame); + int nextIndex; + if (_loopFlag && _frame >= _endFrame) { + nextIndex = (int)glm::ceil(_startFrame); + } else { + nextIndex = (int)glm::ceil(_frame); + } + + // It can be quite possible for the user to set _startFrame and _endFrame to + // values before or past valid ranges. We clamp the frames here. + int frameCount = (int)_anim.size(); + prevIndex = std::min(std::max(0, prevIndex), frameCount - 1); + nextIndex = std::min(std::max(0, nextIndex), frameCount - 1); + + const AnimPoseVec& prevFrame = _mirrorFlag ? _mirrorAnim[prevIndex] : _anim[prevIndex]; + const AnimPoseVec& nextFrame = _mirrorFlag ? _mirrorAnim[nextIndex] : _anim[nextIndex]; + float alpha = glm::fract(_frame); + + ::blend(_poses.size(), &prevFrame[0], &nextFrame[0], alpha, &_poses[0]); + } + + processOutputJoints(triggersOut); + + return _poses; +} + +void AnimClip::setCurrentFrameInternal(float frame) { + // because dt is 0, we should not encounter any triggers + const float dt = 0.0f; + AnimVariantMap triggers; + _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame + _startFrame, dt, _loopFlag, _id, triggers); } void AnimClip::buildMirrorAnim() { @@ -275,3 +357,9 @@ void AnimClip::buildMirrorAnim() { const AnimPoseVec& AnimClip::getPosesInternal() const { return _poses; } + +void AnimClip::loadURL(const QString& url) { + auto animCache = DependencyManager::get(); + _networkAnim = animCache->getAnimation(url); + _url = url; +} diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 92f4c01dcc..24acdcf20a 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -25,7 +25,8 @@ class AnimClip : public AnimNode { public: friend class AnimTests; - AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag); + AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag, + AnimBlendType blendType, const QString& baseURL, float baseFrame); virtual ~AnimClip() override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override; @@ -52,19 +53,20 @@ public: void setMirrorFlag(bool mirrorFlag) { _mirrorFlag = mirrorFlag; } float getFrame() const { return _frame; } - void loadURL(const QString& url); + protected: virtual void setCurrentFrameInternal(float frame) override; - void copyFromNetworkAnim(); void buildMirrorAnim(); // for AnimDebugDraw rendering virtual const AnimPoseVec& getPosesInternal() const override; AnimationPointer _networkAnim; + AnimationPointer _baseNetworkAnim; + AnimPoseVec _poses; // _anim[frame][joint] @@ -78,6 +80,9 @@ protected: bool _loopFlag; bool _mirrorFlag; float _frame; + AnimBlendType _blendType; + QString _baseURL; + float _baseFrame; QString _startFrameVar; QString _endFrameVar; diff --git a/libraries/animation/src/AnimContext.h b/libraries/animation/src/AnimContext.h index 5f353fcae4..2ee8a30980 100644 --- a/libraries/animation/src/AnimContext.h +++ b/libraries/animation/src/AnimContext.h @@ -34,6 +34,13 @@ enum class AnimNodeType { NumTypes }; +enum AnimBlendType { + AnimBlendType_Normal, + AnimBlendType_AddRelative, + AnimBlendType_AddAbsolute, + AnimBlendType_NumTypes +}; + class AnimContext { public: AnimContext() {} diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index d43351fff9..100269c55c 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -161,6 +161,19 @@ static EasingType stringToEasingType(const QString& str) { } } +static AnimBlendType stringToAnimBlendType(const QString& str) { + if (str == "normal") { + return AnimBlendType_Normal; + } else if (str == "addRelative") { + return AnimBlendType_AddRelative; + } else if (str == "addAbsolute") { + return AnimBlendType_AddAbsolute; + } else { + return AnimBlendType_NumTypes; + } +} + + static const char* animManipulatorJointVarTypeToString(AnimManipulator::JointVar::Type type) { switch (type) { case AnimManipulator::JointVar::Type::Absolute: return "absolute"; @@ -374,6 +387,9 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& READ_FLOAT(timeScale, jsonObj, id, jsonUrl, nullptr); READ_BOOL(loopFlag, jsonObj, id, jsonUrl, nullptr); READ_OPTIONAL_BOOL(mirrorFlag, jsonObj, false); + READ_OPTIONAL_STRING(blendType, jsonObj); + READ_OPTIONAL_STRING(baseURL, jsonObj); + READ_OPTIONAL_FLOAT(baseFrame, jsonObj, 0.0f); READ_OPTIONAL_STRING(startFrameVar, jsonObj); READ_OPTIONAL_STRING(endFrameVar, jsonObj); @@ -381,11 +397,22 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& READ_OPTIONAL_STRING(loopFlagVar, jsonObj); READ_OPTIONAL_STRING(mirrorFlagVar, jsonObj); + // animation urls can be relative to the containing url document. auto tempUrl = QUrl(url); tempUrl = jsonUrl.resolved(tempUrl); - auto node = std::make_shared(id, tempUrl.toString(), startFrame, endFrame, timeScale, loopFlag, mirrorFlag); + // AJT: + AnimBlendType blendTypeEnum = AnimBlendType_Normal; // default value + if (!blendType.isEmpty()) { + blendTypeEnum = stringToAnimBlendType(blendType); + if (blendTypeEnum == AnimBlendType_NumTypes) { + qCCritical(animation) << "AnimNodeLoader, bad blendType on clip, id = " << id; + return nullptr; + } + } + + auto node = std::make_shared(id, tempUrl.toString(), startFrame, endFrame, timeScale, loopFlag, mirrorFlag, blendTypeEnum, baseURL, baseFrame); if (!startFrameVar.isEmpty()) { node->setStartFrameVar(startFrameVar); @@ -409,10 +436,19 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr); - + READ_OPTIONAL_STRING(blendType, jsonObj); READ_OPTIONAL_STRING(alphaVar, jsonObj); - auto node = std::make_shared(id, alpha); + AnimBlendType blendTypeEnum = AnimBlendType_Normal; // default value + if (!blendType.isEmpty()) { + blendTypeEnum = stringToAnimBlendType(blendType); + if (blendTypeEnum == AnimBlendType_NumTypes) { + qCCritical(animation) << "AnimNodeLoader, bad blendType on blendLinear, id = " << id; + return nullptr; + } + } + + auto node = std::make_shared(id, alpha, blendTypeEnum); if (!alphaVar.isEmpty()) { node->setAlphaVar(alphaVar); diff --git a/libraries/animation/src/AnimUtil.cpp b/libraries/animation/src/AnimUtil.cpp index f4f922d704..e5f7cf4182 100644 --- a/libraries/animation/src/AnimUtil.cpp +++ b/libraries/animation/src/AnimUtil.cpp @@ -15,7 +15,6 @@ // TODO: use restrict keyword // TODO: excellent candidate for simd vectorization. - void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result) { for (size_t i = 0; i < numPoses; i++) { const AnimPose& aPose = a[i]; @@ -27,6 +26,29 @@ void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, A } } +// additive blend +void blendAdd(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result) { + + const glm::quat identity = glm::quat(); + for (size_t i = 0; i < numPoses; i++) { + const AnimPose& aPose = a[i]; + const AnimPose& bPose = b[i]; + + result[i].scale() = lerp(aPose.scale(), bPose.scale(), alpha); + + // ensure that delta has the same "polarity" as the identity quat. + // we don't need to do a full dot product, just sign of w is sufficient. + glm::quat delta = bPose.rot(); + if (delta.w < 0.0f) { + delta = -delta; + } + delta = glm::lerp(identity, delta, alpha); + result[i].rot() = glm::normalize(delta * aPose.rot()); + + result[i].trans() = aPose.trans() + (alpha * bPose.trans()); + } +} + glm::quat averageQuats(size_t numQuats, const glm::quat* quats) { if (numQuats == 0) { return glm::quat(); diff --git a/libraries/animation/src/AnimUtil.h b/libraries/animation/src/AnimUtil.h index 26dc19da06..5d67b27abb 100644 --- a/libraries/animation/src/AnimUtil.h +++ b/libraries/animation/src/AnimUtil.h @@ -16,6 +16,9 @@ // this is where the magic happens void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result); +// additive blending +void blendAdd(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result); + glm::quat averageQuats(size_t numQuats, const glm::quat* quats); float accumulateTime(float startFrame, float endFrame, float timeScale, float currentFrame, float dt, bool loopFlag, diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 211c54def8..ceb18041f1 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -564,7 +564,7 @@ void Rig::overrideRoleAnimation(const QString& role, const QString& url, float f _origRoleAnimations[role] = node; const float REFERENCE_FRAMES_PER_SECOND = 30.0f; float timeScale = fps / REFERENCE_FRAMES_PER_SECOND; - auto clipNode = std::make_shared(role, url, firstFrame, lastFrame, timeScale, loop, false); + auto clipNode = std::make_shared(role, url, firstFrame, lastFrame, timeScale, loop, false, AnimBlendType_Normal, "", 0.0f); _roleAnimStates[role] = { role, url, fps, loop, firstFrame, lastFrame }; AnimNode::Pointer parent = node->getParent(); parent->replaceChild(node, clipNode); @@ -1973,7 +1973,7 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm // TODO: does not properly handle avatar scale. - if (isIndexValid(index)) { + if (isIndexValid(index) && !_internalPoseSet._overrideFlags[index]) { const glm::mat4 rigToWorld = createMatFromQuatAndPos(modelRotation, modelTranslation); const glm::mat4 worldToRig = glm::inverse(rigToWorld); const glm::vec3 lookAtVector = glm::normalize(transformPoint(worldToRig, lookAtSpot) - _internalPoseSet._absolutePoses[index].trans()); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 3574544bba..853e36b45b 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -72,7 +72,6 @@ #include "EntityTreeRenderer.h" #include "RenderablePolyVoxEntityItem.h" -#include "EntityEditPacketSender.h" #include "PhysicalEntitySimulation.h" const float MARCHING_CUBE_COLLISION_HULL_OFFSET = 0.5; @@ -83,30 +82,60 @@ const float MARCHING_CUBE_COLLISION_HULL_OFFSET = 0.5; _voxelData -- compressed QByteArray representation of which voxels have which values _volData -- datastructure from the PolyVox library which holds which voxels have which values _mesh -- renderable representation of the voxels - _shape -- used for bullet collisions + _shape -- used for bullet (physics) collisions Each one depends on the one before it, except that _voxelData is set from _volData if a script edits the voxels. There are booleans to indicate that something has been updated and the dependents now need to be updated. + _meshReady -- do we have something to give scripts that ask for the mesh? + _voxelDataDirty -- do we need to uncompress data and expand it into _volData? + _volDataDirty -- does recomputeMesh need to be called? + _shapeReady -- are we ready to tell bullet our shape? - _voxelDataDirty - _volDataDirty - _meshDirty - In RenderablePolyVoxEntityItem::render, these flags are checked and changes are propagated along the chain. - decompressVolumeData() is called to decompress _voxelData into _volData. recomputeMesh() is called to invoke the - polyVox surface extractor to create _mesh (as well as set Simulation _flags). Because Simulation::DIRTY_SHAPE - is set, isReadyToComputeShape() gets called and _shape is created either from _volData or _shape, depending on - the surface style. + Here is a simplified diagram of the state machine implemented in RenderablePolyVoxEntityItem::update - When a script changes _volData, compressVolumeDataAndSendEditPacket is called to update _voxelData and to - send a packet to the entity-server. + +-------------------+ + | | + | | + | volDataDirty | voxelDataDirty + | +--v--+ + | +------+Ready+--------+ + | | +-----+ | + | | | + | +-----v----+ +------v------+ + | |BakingMesh| |Uncompressing| + | +-----+----+ +------+------+ + | | | + | | | + | +---v-------+ +-------v------------+ + | |Compressing| |BakingMeshNoCompress| + | +---------+-+ ++-------------------+ + | | | + | | | + | +-v--------v+ + | |BakingShape| + | +-----+-----+ + | | + +-------------------+ - decompressVolumeData, recomputeMesh, computeShapeInfoWorker, and compressVolumeDataAndSendEditPacket are too expensive - to run on a thread that has other things to do. These use QtConcurrent::run to spawn a thread. As each thread - finishes, it adjusts the dirty flags so that the next call to render() will kick off the next step. - polyvoxes are designed to seemlessly fit up against neighbors. If voxels go right up to the edge of polyvox, + The work for each step is done on temporary worker threads. The PolyVox entity will update _updateNeeded and + enable or disable update calls on the entity, depending on if there is more work to do. + + From the 'Ready' state, if we receive an update from the network, _voxelDataDirty will be set true. We + uncompress the received data, bake the mesh (for the render-engine's benefit), and then compute the shape + (for the physics-engine's benefit). This is the right-hand side of the diagram. + + From the 'Ready' state, if a script changes a voxel, _volDataDirty will be set true. We bake the mesh, + compress the voxels into a new _voxelData, and transmit the new _voxelData to the entity-server. We then + bake the shape. This is the left-hand side of the diagram. + + The actual state machine is more complicated than the diagram, because it's possible for _volDataDirty or + _voxelDataDirty to be set true while worker threads are attempting to bake meshes or shapes. If this happens, + we jump back to a higher point in the diagram to avoid wasting effort. + + PolyVoxes are designed to seemlessly fit up against neighbors. If voxels go right up to the edge of polyvox, the resulting mesh wont be closed -- the library assumes you'll have another polyvox next to it to continue the mesh. @@ -114,8 +143,8 @@ const float MARCHING_CUBE_COLLISION_HULL_OFFSET = 0.5; previously mentioned gaps along the edges. Non-edged polyvox entities can be told about their neighbors in all 6 cardinal directions. On the positive - edges of the polyvox, the values are set from the (negative edge of) relevant neighbor so that their meshes - knit together. This is handled by bonkNeighbors and copyUpperEdgesFromNeighbors. In these functions, variable + edges of the polyvox, the values are set from the (negative edge of the) relevant neighbor so that their meshes + knit together. This is handled by tellNeighborsToRecopyEdges and copyUpperEdgesFromNeighbors. In these functions, variable names have XP for x-positive, XN x-negative, etc. */ @@ -140,7 +169,8 @@ EntityItemPointer RenderablePolyVoxEntityItem::factory(const EntityItemID& entit return entity; } -RenderablePolyVoxEntityItem::RenderablePolyVoxEntityItem(const EntityItemID& entityItemID) : PolyVoxEntityItem(entityItemID) { } +RenderablePolyVoxEntityItem::RenderablePolyVoxEntityItem(const EntityItemID& entityItemID) : PolyVoxEntityItem(entityItemID) { +} RenderablePolyVoxEntityItem::~RenderablePolyVoxEntityItem() { withWriteLock([&] { @@ -165,11 +195,12 @@ bool isEdged(PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle) { } void RenderablePolyVoxEntityItem::setVoxelData(const QByteArray& voxelData) { - // compressed voxel information from the entity-server + // accept compressed voxel information from the entity-server withWriteLock([&] { if (_voxelData != voxelData) { _voxelData = voxelData; _voxelDataDirty = true; + startUpdates(); } }); } @@ -189,21 +220,22 @@ void RenderablePolyVoxEntityItem::setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxel bool willBeEdged = isEdged(voxelSurfaceStyle); if (wasEdged != willBeEdged) { - _volDataDirty = true; _volData.reset(); - _voxelSurfaceStyle = voxelSurfaceStyle; _voxelDataDirty = true; volSizeChanged = true; - } else { - _volDataDirty = true; - _voxelSurfaceStyle = voxelSurfaceStyle; } + _voxelSurfaceStyle = voxelSurfaceStyle; + startUpdates(); }); if (volSizeChanged) { // setVoxelVolumeSize will re-alloc _volData with the right size setVoxelVolumeSize(_voxelVolumeSize); } + + _updateFromNeighborXEdge = _updateFromNeighborYEdge = _updateFromNeighborZEdge = true; + tellNeighborsToRecopyEdges(true); + startUpdates(); } glm::vec3 RenderablePolyVoxEntityItem::getSurfacePositionAdjustment() const { @@ -262,9 +294,6 @@ bool RenderablePolyVoxEntityItem::setVoxel(const ivec3& v, uint8_t toValue) { withWriteLock([&] { result = setVoxelInternal(v, toValue); }); - if (result) { - compressVolumeDataAndSendEditPacket(); - } return result; } @@ -311,9 +340,6 @@ bool RenderablePolyVoxEntityItem::setAll(uint8_t toValue) { result |= setVoxelInternal(v, toValue); }); }); - if (result) { - compressVolumeDataAndSendEditPacket(); - } return result; } @@ -336,9 +362,6 @@ bool RenderablePolyVoxEntityItem::setCuboid(const glm::vec3& lowPosition, const result |= setVoxelInternal(v, toValue); }); }); - if (result) { - compressVolumeDataAndSendEditPacket(); - } return result; } @@ -374,9 +397,6 @@ bool RenderablePolyVoxEntityItem::setSphereInVolume(const vec3& center, float ra }); }); - if (result) { - compressVolumeDataAndSendEditPacket(); - } return result; } @@ -434,9 +454,6 @@ bool RenderablePolyVoxEntityItem::setSphere(const vec3& centerWorldCoords, float }); }); - if (result) { - compressVolumeDataAndSendEditPacket(); - } return result; } @@ -482,9 +499,6 @@ bool RenderablePolyVoxEntityItem::setCapsule(const vec3& startWorldCoords, const }); }); - if (result) { - compressVolumeDataAndSendEditPacket(); - } return result; } @@ -710,8 +724,9 @@ ShapeType RenderablePolyVoxEntityItem::getShapeType() const { void RenderablePolyVoxEntityItem::setRegistrationPoint(const glm::vec3& value) { if (value != _registrationPoint) { - _meshDirty = true; + _shapeReady = false; EntityItem::setRegistrationPoint(value); + startUpdates(); } } @@ -721,15 +736,11 @@ bool RenderablePolyVoxEntityItem::isReadyToComputeShape() const { return true; } - // we determine if we are ready to compute the physics shape by actually doing so. - // if _voxelDataDirty or _volDataDirty is set, don't do this yet -- wait for their - // threads to finish before creating the collision shape. - if (_meshDirty && !_voxelDataDirty && !_volDataDirty) { - const_cast(this)->_meshDirty = false; - const_cast(this)->computeShapeInfoWorker(); - return false; - } - return true; + bool result; + withReadLock([&] { + result = _shapeReady; + }); + return result; } void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) { @@ -739,33 +750,155 @@ void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) { return; } - // the shape was actually computed in isReadyToComputeShape. Just hand it off, here. - withWriteLock([&] { + withReadLock([&] { info = _shapeInfo; }); } -bool RenderablePolyVoxEntityItem::updateDependents() { - bool voxelDataDirty; - bool volDataDirty; +void RenderablePolyVoxEntityItem::changeUpdates(bool value) { + if (_updateNeeded != value) { + EntityTreePointer entityTree = getTree(); + if (entityTree) { + EntitySimulationPointer simulation = entityTree->getSimulation(); + if (simulation) { + _updateNeeded = value; + _flags |= Simulation::DIRTY_UPDATEABLE; + simulation->changeEntity(getThisPointer()); + } + } + } +} + +void RenderablePolyVoxEntityItem::startUpdates() { + changeUpdates(true); +} + +void RenderablePolyVoxEntityItem::stopUpdates() { + changeUpdates(false); +} + +void RenderablePolyVoxEntityItem::update(const quint64& now) { + bool doRecomputeMesh { false }; + bool doUncompress { false }; + bool doCompress { false }; + bool doRecomputeShape { false }; + withWriteLock([&] { - voxelDataDirty = _voxelDataDirty; - volDataDirty = _volDataDirty; - if (_voxelDataDirty) { - _voxelDataDirty = false; - } else if (_volDataDirty) { - _volDataDirty = false; - } else { - _meshReady = true; + tellNeighborsToRecopyEdges(false); + + switch (_state) { + + case PolyVoxState::Ready: { + if (_volDataDirty) { + _volDataDirty = _voxelDataDirty = false; + _state = PolyVoxState::BakingMesh; + doRecomputeMesh = true; + } else if (_voxelDataDirty) { + _voxelDataDirty = false; + _state = PolyVoxState::Uncompressing; + doUncompress = true; + } else { + copyUpperEdgesFromNeighbors(); + if (!_volDataDirty && !_voxelDataDirty) { + // nothing to do + stopUpdates(); + } + } + break; + } + + case PolyVoxState::Uncompressing: { + break; // wait + } + case PolyVoxState::UncompressingFinished: { + if (_volDataDirty) { + _volDataDirty = _voxelDataDirty = false; + _state = PolyVoxState::BakingMeshNoCompress; + doRecomputeMesh = true; + } else if (_voxelDataDirty) { + _voxelDataDirty = false; + // _voxelData changed while we were uncompressing the previous version, uncompress again + _state = PolyVoxState::Uncompressing; + doUncompress = true; + } else { + _state = PolyVoxState::Ready; + } + break; + } + + case PolyVoxState::BakingMesh: { + break; // wait + } + case PolyVoxState::BakingMeshFinished: { + if (_volDataDirty) { + _volDataDirty = _voxelDataDirty = false; + _state = PolyVoxState::BakingMesh; + // a local edit happened while we were baking the mesh. rebake mesh... + doRecomputeMesh = true; + } else if (_voxelDataDirty) { + _voxelDataDirty = false; + // we received a change from the wire while baking the mesh. + _state = PolyVoxState::Uncompressing; + doUncompress = true; + } else { + _state = PolyVoxState::Compressing; + doCompress = true; + } + break; + } + + case PolyVoxState::BakingMeshNoCompress: { + break; // wait + } + case PolyVoxState::BakingMeshNoCompressFinished: { + if (_volDataDirty) { + _volDataDirty = _voxelDataDirty = false; + _state = PolyVoxState::BakingMesh; + // a local edit happened while we were baking the mesh. rebake mesh... + doRecomputeMesh = true; + } else if (_voxelDataDirty) { + _voxelDataDirty = false; + // we received a change from the wire while baking the mesh. + _state = PolyVoxState::Uncompressing; + doUncompress = true; + } else { + _state = PolyVoxState::BakingShape; + doRecomputeShape = true; + } + break; + } + + case PolyVoxState::Compressing: { + break; // wait + } + case PolyVoxState::CompressingFinished: { + _state = PolyVoxState::BakingShape; + doRecomputeShape = true; + break; + } + + case PolyVoxState::BakingShape: { + break; // wait + } + case PolyVoxState::BakingShapeFinished: { + _state = PolyVoxState::Ready; + break; + } } }); - if (voxelDataDirty) { - decompressVolumeData(); - } else if (volDataDirty) { + + if (doRecomputeMesh) { recomputeMesh(); } - - return !volDataDirty; + if (doUncompress) { + uncompressVolumeData(); + } + if (doCompress) { + compressVolumeDataAndSendEditPacket(); + } + if (doRecomputeShape) { + computeShapeInfoWorker(); + } } void RenderablePolyVoxEntityItem::setVoxelVolumeSize(const glm::vec3& voxelVolumeSize) { @@ -782,6 +915,8 @@ void RenderablePolyVoxEntityItem::setVoxelVolumeSize(const glm::vec3& voxelVolum _voxelVolumeSize = voxelVolumeSize; _volData.reset(); _onCount = 0; + _updateFromNeighborXEdge = _updateFromNeighborYEdge = _updateFromNeighborZEdge = true; + startUpdates(); static const PolyVox::Vector3DInt32 lowCorner(0, 0, 0); PolyVox::Vector3DInt32 highCorner; @@ -806,6 +941,8 @@ void RenderablePolyVoxEntityItem::setVoxelVolumeSize(const glm::vec3& voxelVolum // having the "outside of voxel-space" value be 255 has helped me notice some problems. _volData->setBorderValue(255); }); + + tellNeighborsToRecopyEdges(true); } bool inUserBounds(const std::shared_ptr> vol, @@ -853,32 +990,38 @@ uint8_t RenderablePolyVoxEntityItem::getVoxelInternal(const ivec3& v) const { } +void RenderablePolyVoxEntityItem::setVoxelMarkNeighbors(int x, int y, int z, uint8_t toValue) { + _volData->setVoxelAt(x, y, z, toValue); + if (x == 0) { + _neighborXNeedsUpdate = true; + startUpdates(); + } + if (y == 0) { + _neighborYNeedsUpdate = true; + startUpdates(); + } + if (z == 0) { + _neighborZNeedsUpdate = true; + startUpdates(); + } +} + bool RenderablePolyVoxEntityItem::setVoxelInternal(const ivec3& v, uint8_t toValue) { - // set a voxel without recompressing the voxel data. This assumes that the caller has - // write-locked the entity. - bool result = false; - if (!inUserBounds(_volData, _voxelSurfaceStyle, v)) { - return result; + // set a voxel without recompressing the voxel data. This assumes that the caller has write-locked the entity. + bool result = updateOnCount(v, toValue); + if (result) { + if (isEdged()) { + setVoxelMarkNeighbors(v.x + 1, v.y + 1, v.z + 1, toValue); + } else { + setVoxelMarkNeighbors(v.x, v.y, v.z, toValue); + } + _volDataDirty = true; + startUpdates(); } - result = updateOnCount(v, toValue); - - if (isEdged()) { - _volData->setVoxelAt(v.x + 1, v.y + 1, v.z + 1, toValue); - } else { - _volData->setVoxelAt(v.x, v.y, v.z, toValue); - } - - if (glm::any(glm::equal(ivec3(0), v))) { - _neighborsNeedUpdate = true; - } - - _volDataDirty |= result; - return result; } - bool RenderablePolyVoxEntityItem::updateOnCount(const ivec3& v, uint8_t toValue) { // keep _onCount up to date if (!inUserBounds(_volData, _voxelSurfaceStyle, v)) { @@ -902,7 +1045,7 @@ bool RenderablePolyVoxEntityItem::updateOnCount(const ivec3& v, uint8_t toValue) return false; } -void RenderablePolyVoxEntityItem::decompressVolumeData() { +void RenderablePolyVoxEntityItem::uncompressVolumeData() { // take compressed data and expand it into _volData. QByteArray voxelData; auto entity = std::static_pointer_cast(getThisPointer()); @@ -921,9 +1064,9 @@ void RenderablePolyVoxEntityItem::decompressVolumeData() { if (voxelXSize == 0 || voxelXSize > PolyVoxEntityItem::MAX_VOXEL_DIMENSION || voxelYSize == 0 || voxelYSize > PolyVoxEntityItem::MAX_VOXEL_DIMENSION || voxelZSize == 0 || voxelZSize > PolyVoxEntityItem::MAX_VOXEL_DIMENSION) { - qCDebug(entitiesrenderer) << "voxelSize is not reasonable, skipping decompressions." - << voxelXSize << voxelYSize << voxelZSize << getName() << getID(); - entity->setVoxelDataDirty(false); + qCDebug(entitiesrenderer) << "voxelSize is not reasonable, skipping uncompressions." + << voxelXSize << voxelYSize << voxelZSize << getName() << getID(); + entity->setVoxelsFromData(QByteArray(1, 0), 1, 1, 1); return; } int rawSize = voxelXSize * voxelYSize * voxelZSize; @@ -934,10 +1077,10 @@ void RenderablePolyVoxEntityItem::decompressVolumeData() { QByteArray uncompressedData = qUncompress(compressedData); if (uncompressedData.size() != rawSize) { - qCDebug(entitiesrenderer) << "PolyVox decompress -- size is (" << voxelXSize << voxelYSize << voxelZSize << ")" + qCDebug(entitiesrenderer) << "PolyVox uncompress -- size is (" << voxelXSize << voxelYSize << voxelZSize << ")" << "so expected uncompressed length of" << rawSize << "but length is" << uncompressedData.size() - << getName() << getID(); - entity->setVoxelDataDirty(false); + << getName() << getID(); + entity->setVoxelsFromData(QByteArray(1, 0), 1, 1, 1); return; } @@ -947,13 +1090,14 @@ void RenderablePolyVoxEntityItem::decompressVolumeData() { void RenderablePolyVoxEntityItem::setVoxelsFromData(QByteArray uncompressedData, quint16 voxelXSize, quint16 voxelYSize, quint16 voxelZSize) { - // this accepts the payload from decompressVolumeData + // this accepts the payload from uncompressVolumeData withWriteLock([&] { loop3(ivec3(0), ivec3(voxelXSize, voxelYSize, voxelZSize), [&](const ivec3& v) { int uncompressedIndex = (v.z * voxelYSize * voxelXSize) + (v.y * voxelZSize) + v.x; setVoxelInternal(v, uncompressedData[uncompressedIndex]); }); - _volDataDirty = true; + + _state = PolyVoxState::UncompressingFinished; }); } @@ -972,10 +1116,7 @@ void RenderablePolyVoxEntityItem::compressVolumeDataAndSendEditPacket() { voxelZSize = _voxelVolumeSize.z; }); - EntityTreeElementPointer element = getElement(); - EntityTreePointer tree = element ? element->getTree() : nullptr; - - QtConcurrent::run([voxelXSize, voxelYSize, voxelZSize, entity, tree] { + QtConcurrent::run([voxelXSize, voxelYSize, voxelZSize, entity] { auto polyVoxEntity = std::static_pointer_cast(entity); QByteArray uncompressedData = polyVoxEntity->volDataToArray(voxelXSize, voxelYSize, voxelZSize); @@ -992,17 +1133,37 @@ void RenderablePolyVoxEntityItem::compressVolumeDataAndSendEditPacket() { // HACK -- until we have a way to allow for properties larger than MTU, don't update. // revert the active voxel-space to the last version that fit. qCDebug(entitiesrenderer) << "compressed voxel data is too large" << entity->getName() << entity->getID(); + + const auto polyVoxEntity = std::static_pointer_cast(entity); + polyVoxEntity->compressVolumeDataFinished(QByteArray()); return; } - auto now = usecTimestampNow(); - entity->setLastEdited(now); - entity->setLastBroadcast(now); + std::static_pointer_cast(entity)->compressVolumeDataFinished(newVoxelData); + }); +} - std::static_pointer_cast(entity)->setVoxelData(newVoxelData); +void RenderablePolyVoxEntityItem::compressVolumeDataFinished(const QByteArray& voxelData) { + // compressed voxel information from the entity-server + withWriteLock([&] { + if (voxelData.size() > 0 && _voxelData != voxelData) { + _voxelData = voxelData; + } + _state = PolyVoxState::CompressingFinished; + }); + auto now = usecTimestampNow(); + setLastEdited(now); + setLastBroadcast(now); + + EntityTreeElementPointer element = getElement(); + EntityTreePointer tree = element ? element->getTree() : nullptr; + + if (tree) { tree->withReadLock([&] { - EntityItemProperties properties = entity->getProperties(); + EntityPropertyFlags desiredProperties; + desiredProperties.setHasProperty(PROP_VOXEL_DATA); + EntityItemProperties properties = getProperties(desiredProperties, false); properties.setVoxelDataDirty(); properties.setLastEdited(now); @@ -1010,12 +1171,13 @@ void RenderablePolyVoxEntityItem::compressVolumeDataAndSendEditPacket() { PhysicalEntitySimulationPointer peSimulation = std::static_pointer_cast(simulation); EntityEditPacketSender* packetSender = peSimulation ? peSimulation->getPacketSender() : nullptr; if (packetSender) { - packetSender->queueEditEntityMessage(PacketType::EntityEdit, tree, entity->getID(), properties); + packetSender->queueEditEntityMessage(PacketType::EntityEdit, tree, getID(), properties); } }); - }); + } } + EntityItemPointer lookUpNeighbor(EntityTreePointer tree, EntityItemID neighborID, EntityItemWeakPointer& currentWP) { EntityItemPointer current = currentWP.lock(); @@ -1064,56 +1226,103 @@ void RenderablePolyVoxEntityItem::copyUpperEdgesFromNeighbors() { return; } - auto currentXPNeighbor = getXPNeighbor(); - auto currentYPNeighbor = getYPNeighbor(); - auto currentZPNeighbor = getZPNeighbor(); - - if (currentXPNeighbor && currentXPNeighbor->getVoxelVolumeSize() == _voxelVolumeSize) { - withWriteLock([&] { - for (int y = 0; y < _volData->getHeight(); y++) { - for (int z = 0; z < _volData->getDepth(); z++) { - uint8_t neighborValue = currentXPNeighbor->getVoxel({ 0, y, z }); - if ((y == 0 || z == 0) && _volData->getVoxelAt(_volData->getWidth() - 1, y, z) != neighborValue) { - bonkNeighbors(); - } - _volData->setVoxelAt(_volData->getWidth() - 1, y, z, neighborValue); - } - } - }); + if (!_updateFromNeighborXEdge && !_updateFromNeighborYEdge && !_updateFromNeighborZEdge) { + return; } + cacheNeighbors(); - if (currentYPNeighbor && currentYPNeighbor->getVoxelVolumeSize() == _voxelVolumeSize) { - withWriteLock([&] { - for (int x = 0; x < _volData->getWidth(); x++) { - for (int z = 0; z < _volData->getDepth(); z++) { - uint8_t neighborValue = currentYPNeighbor->getVoxel({ x, 0, z }); - if ((x == 0 || z == 0) && _volData->getVoxelAt(x, _volData->getHeight() - 1, z) != neighborValue) { - bonkNeighbors(); - } - _volData->setVoxelAt(x, _volData->getHeight() - 1, z, neighborValue); - } - } - }); - } - - - if (currentZPNeighbor && currentZPNeighbor->getVoxelVolumeSize() == _voxelVolumeSize) { - withWriteLock([&] { - for (int x = 0; x < _volData->getWidth(); x++) { + if (_updateFromNeighborXEdge) { + _updateFromNeighborXEdge = false; + auto currentXPNeighbor = getXPNeighbor(); + if (currentXPNeighbor && currentXPNeighbor->getVoxelVolumeSize() == _voxelVolumeSize) { + withWriteLock([&] { + int x = _volData->getWidth() - 1; for (int y = 0; y < _volData->getHeight(); y++) { - uint8_t neighborValue = currentZPNeighbor->getVoxel({ x, y, 0 }); - _volData->setVoxelAt(x, y, _volData->getDepth() - 1, neighborValue); - if ((x == 0 || y == 0) && _volData->getVoxelAt(x, y, _volData->getDepth() - 1) != neighborValue) { - bonkNeighbors(); + for (int z = 0; z < _volData->getDepth(); z++) { + uint8_t neighborValue = currentXPNeighbor->getVoxel({ 0, y, z }); + uint8_t prevValue = _volData->getVoxelAt(x, y, z); + if (prevValue != neighborValue) { + _volData->setVoxelAt(x, y, z, neighborValue); + _volDataDirty = true; + } } - _volData->setVoxelAt(x, y, _volData->getDepth() - 1, neighborValue); } - } - }); + }); + } + } + + if (_updateFromNeighborYEdge) { + _updateFromNeighborYEdge = false; + auto currentYPNeighbor = getYPNeighbor(); + if (currentYPNeighbor && currentYPNeighbor->getVoxelVolumeSize() == _voxelVolumeSize) { + withWriteLock([&] { + int y = _volData->getHeight() - 1; + for (int x = 0; x < _volData->getWidth(); x++) { + for (int z = 0; z < _volData->getDepth(); z++) { + uint8_t neighborValue = currentYPNeighbor->getVoxel({ x, 0, z }); + uint8_t prevValue = _volData->getVoxelAt(x, y, z); + if (prevValue != neighborValue) { + _volData->setVoxelAt(x, y, z, neighborValue); + _volDataDirty = true; + } + } + } + }); + } + } + + if (_updateFromNeighborZEdge) { + _updateFromNeighborZEdge = false; + auto currentZPNeighbor = getZPNeighbor(); + if (currentZPNeighbor && currentZPNeighbor->getVoxelVolumeSize() == _voxelVolumeSize) { + withWriteLock([&] { + int z = _volData->getDepth() - 1; + for (int x = 0; x < _volData->getWidth(); x++) { + for (int y = 0; y < _volData->getHeight(); y++) { + uint8_t neighborValue = currentZPNeighbor->getVoxel({ x, y, 0 }); + uint8_t prevValue = _volData->getVoxelAt(x, y, z); + if (prevValue != neighborValue) { + _volData->setVoxelAt(x, y, z, neighborValue); + _volDataDirty = true; + } + } + } + }); + } } } +void RenderablePolyVoxEntityItem::tellNeighborsToRecopyEdges(bool force) { + // if this polyvox has changed any of its voxels with a zero coord (in x, y, or z) notify neighbors, if there are any + if (force || _neighborXNeedsUpdate || _neighborYNeedsUpdate || _neighborZNeedsUpdate) { + cacheNeighbors(); + + if (force || _neighborXNeedsUpdate) { + _neighborXNeedsUpdate = false; + auto currentXNNeighbor = getXNNeighbor(); + if (currentXNNeighbor) { + currentXNNeighbor->neighborXEdgeChanged(); + } + } + if (force || _neighborYNeedsUpdate) { + _neighborYNeedsUpdate = false; + auto currentYNNeighbor = getYNNeighbor(); + if (currentYNNeighbor) { + currentYNNeighbor->neighborYEdgeChanged(); + } + } + if (force || _neighborZNeedsUpdate) { + _neighborZNeedsUpdate = false; + auto currentZNNeighbor = getZNNeighbor(); + if (currentZNNeighbor) { + currentZNNeighbor->neighborZEdgeChanged(); + } + } + } +} + + void RenderablePolyVoxEntityItem::recomputeMesh() { // use _volData to make a renderable mesh PolyVoxSurfaceStyle voxelSurfaceStyle; @@ -1121,9 +1330,6 @@ void RenderablePolyVoxEntityItem::recomputeMesh() { voxelSurfaceStyle = _voxelSurfaceStyle; }); - cacheNeighbors(); - copyUpperEdgesFromNeighbors(); - auto entity = std::static_pointer_cast(getThisPointer()); QtConcurrent::run([entity, voxelSurfaceStyle] { @@ -1135,24 +1341,14 @@ void RenderablePolyVoxEntityItem::recomputeMesh() { entity->withReadLock([&] { PolyVox::SimpleVolume* volData = entity->getVolData(); switch (voxelSurfaceStyle) { - case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: { - PolyVox::MarchingCubesSurfaceExtractor> surfaceExtractor - (volData, volData->getEnclosingRegion(), &polyVoxMesh); - surfaceExtractor.execute(); - break; - } + case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: { PolyVox::MarchingCubesSurfaceExtractor> surfaceExtractor (volData, volData->getEnclosingRegion(), &polyVoxMesh); surfaceExtractor.execute(); break; } - case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: { - PolyVox::CubicSurfaceExtractorWithNormals> surfaceExtractor - (volData, volData->getEnclosingRegion(), &polyVoxMesh); - surfaceExtractor.execute(); - break; - } + case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: case PolyVoxEntityItem::SURFACE_CUBIC: { PolyVox::CubicSurfaceExtractorWithNormals> surfaceExtractor (volData, volData->getEnclosingRegion(), &polyVoxMesh); @@ -1180,7 +1376,6 @@ void RenderablePolyVoxEntityItem::recomputeMesh() { gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); mesh->setVertexBuffer(vertexBufferView); - // TODO -- use 3-byte normals rather than 3-float normals mesh->addAttribute(gpu::Stream::NORMAL, gpu::BufferView(vertexBufferPtr, @@ -1194,28 +1389,24 @@ void RenderablePolyVoxEntityItem::recomputeMesh() { (graphics::Index)vecIndices.size(), // numIndices (graphics::Index)0, // baseVertex graphics::Mesh::TRIANGLES)); // topology - mesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(graphics::Mesh::Part), - (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); + mesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(graphics::Mesh::Part), (gpu::Byte*) parts.data()), + gpu::Element::PART_DRAWCALL)); entity->setMesh(mesh); }); } void RenderablePolyVoxEntityItem::setMesh(graphics::MeshPointer mesh) { // this catches the payload from recomputeMesh - bool neighborsNeedUpdate; withWriteLock([&] { if (!_collisionless) { _flags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; } + _shapeReady = false; _mesh = mesh; - _meshDirty = true; + _state = PolyVoxState::BakingMeshFinished; _meshReady = true; - neighborsNeedUpdate = _neighborsNeedUpdate; - _neighborsNeedUpdate = false; + startUpdates(); }); - if (neighborsNeedUpdate) { - bonkNeighbors(); - } somethingChangedNotification(); } @@ -1223,9 +1414,6 @@ void RenderablePolyVoxEntityItem::setMesh(graphics::MeshPointer mesh) { void RenderablePolyVoxEntityItem::computeShapeInfoWorker() { // this creates a collision-shape for the physics engine. The shape comes from // _volData for cubic extractors and from _mesh for marching-cube extractors - if (!_meshReady) { - return; - } EntityItemPointer entity = getThisPointer(); @@ -1295,8 +1483,8 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorker() { const auto& x = v.x; const auto& y = v.y; const auto& z = v.z; - if (glm::all(glm::greaterThan(v, ivec3(0))) && - glm::all(glm::lessThan(v, ivec3(voxelVolumeSize) - 1)) && + if (glm::all(glm::greaterThan(v, ivec3(0))) && + glm::all(glm::lessThan(v, ivec3(voxelVolumeSize) - 1)) && (polyVoxEntity->getVoxelInternal({ x - 1, y, z }) > 0) && (polyVoxEntity->getVoxelInternal({ x, y - 1, z }) > 0) && (polyVoxEntity->getVoxelInternal({ x, y, z - 1 }) > 0) && @@ -1360,6 +1548,10 @@ void RenderablePolyVoxEntityItem::setCollisionPoints(ShapeInfo::PointCollection // this catches the payload from computeShapeInfoWorker if (pointCollection.isEmpty()) { EntityItem::computeShapeInfo(_shapeInfo); + withWriteLock([&] { + _shapeReady = true; + _state = PolyVoxState::BakingShapeFinished; + }); return; } @@ -1373,7 +1565,8 @@ void RenderablePolyVoxEntityItem::setCollisionPoints(ShapeInfo::PointCollection QString::number(_registrationPoint.z); _shapeInfo.setParams(SHAPE_TYPE_COMPOUND, collisionModelDimensions, shapeKey); _shapeInfo.setPointCollection(pointCollection); - _meshDirty = false; + _shapeReady = true; + _state = PolyVoxState::BakingShapeFinished; }); } @@ -1384,7 +1577,8 @@ void RenderablePolyVoxEntityItem::setXNNeighborID(const EntityItemID& xNNeighbor if (xNNeighborID != _xNNeighborID) { PolyVoxEntityItem::setXNNeighborID(xNNeighborID); - cacheNeighbors(); + _neighborXNeedsUpdate = true; + startUpdates(); } } @@ -1395,7 +1589,8 @@ void RenderablePolyVoxEntityItem::setYNNeighborID(const EntityItemID& yNNeighbor if (yNNeighborID != _yNNeighborID) { PolyVoxEntityItem::setYNNeighborID(yNNeighborID); - cacheNeighbors(); + _neighborYNeedsUpdate = true; + startUpdates(); } } @@ -1406,7 +1601,8 @@ void RenderablePolyVoxEntityItem::setZNNeighborID(const EntityItemID& zNNeighbor if (zNNeighborID != _zNNeighborID) { PolyVoxEntityItem::setZNNeighborID(zNNeighborID); - cacheNeighbors(); + _neighborZNeedsUpdate = true; + startUpdates(); } } @@ -1416,7 +1612,8 @@ void RenderablePolyVoxEntityItem::setXPNeighborID(const EntityItemID& xPNeighbor } if (xPNeighborID != _xPNeighborID) { PolyVoxEntityItem::setXPNeighborID(xPNeighborID); - _volDataDirty = true; + _updateFromNeighborXEdge = true; + startUpdates(); } } @@ -1426,7 +1623,8 @@ void RenderablePolyVoxEntityItem::setYPNeighborID(const EntityItemID& yPNeighbor } if (yPNeighborID != _yPNeighborID) { PolyVoxEntityItem::setYPNeighborID(yPNeighborID); - _volDataDirty = true; + _updateFromNeighborYEdge = true; + startUpdates(); } } @@ -1436,7 +1634,8 @@ void RenderablePolyVoxEntityItem::setZPNeighborID(const EntityItemID& zPNeighbor } if (zPNeighborID != _zPNeighborID) { PolyVoxEntityItem::setZPNeighborID(zPNeighborID); - _volDataDirty = true; + _updateFromNeighborZEdge = true; + startUpdates(); } } @@ -1464,31 +1663,8 @@ std::shared_ptr RenderablePolyVoxEntityItem::getZPN return std::dynamic_pointer_cast(_zPNeighbor.lock()); } -void RenderablePolyVoxEntityItem::bonkNeighbors() { - // flag neighbors to the negative of this entity as needing to rebake their meshes. - cacheNeighbors(); - - auto currentXNNeighbor = getXNNeighbor(); - auto currentYNNeighbor = getYNNeighbor(); - auto currentZNNeighbor = getZNNeighbor(); - - if (currentXNNeighbor) { - currentXNNeighbor->setVolDataDirty(); - } - if (currentYNNeighbor) { - currentYNNeighbor->setVolDataDirty(); - } - if (currentZNNeighbor) { - currentZNNeighbor->setVolDataDirty(); - } -} - // deprecated bool RenderablePolyVoxEntityItem::getMeshes(MeshProxyList& result) { - if (!updateDependents()) { - return false; - } - bool success = false; if (_mesh) { MeshProxy* meshProxy = nullptr; @@ -1517,7 +1693,7 @@ bool RenderablePolyVoxEntityItem::getMeshes(MeshProxyList& result) { } scriptable::ScriptableModelBase RenderablePolyVoxEntityItem::getScriptableModel() { - if (!updateDependents() || !_mesh) { + if (!_mesh) { return scriptable::ScriptableModelBase(); } @@ -1628,9 +1804,6 @@ bool PolyVoxEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPo } void PolyVoxEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { - if (entity->_voxelDataDirty || entity->_volDataDirty) { - entity->updateDependents(); - } #ifdef POLYVOX_ENTITY_USE_FADE_EFFECT if (!_hasTransitioned) { @@ -1662,7 +1835,6 @@ void PolyVoxEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoi graphics::MeshPointer newMesh; entity->withReadLock([&] { newMesh = entity->_mesh; - }); if (newMesh && newMesh->getIndexBuffer()._buffer) { @@ -1705,3 +1877,41 @@ void PolyVoxEntityRenderer::doRender(RenderArgs* args) { batch.drawIndexed(gpu::TRIANGLES, (gpu::uint32)_mesh->getNumIndices(), 0); } +QDebug operator<<(QDebug debug, PolyVoxState state) { + switch (state) { + case PolyVoxState::Ready: + debug << "Ready"; + break; + case PolyVoxState::Uncompressing: + debug << "Uncompressing"; + break; + case PolyVoxState::UncompressingFinished: + debug << "UncompressingFinished"; + break; + case PolyVoxState::BakingMesh: + debug << "BakingMesh"; + break; + case PolyVoxState::BakingMeshFinished: + debug << "BakingMeshFinished"; + break; + case PolyVoxState::BakingMeshNoCompress: + debug << "BakingMeshNoCompress"; + break; + case PolyVoxState::BakingMeshNoCompressFinished: + debug << "BakingMeshNoCompressFinished"; + break; + case PolyVoxState::Compressing: + debug << "Compressing"; + break; + case PolyVoxState::CompressingFinished: + debug << "CompressingFinished"; + break; + case PolyVoxState::BakingShape: + debug << "BakingShape"; + break; + case PolyVoxState::BakingShapeFinished: + debug << "BakingShapeFinished"; + break; + } + return debug; +} diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index cca34767a4..c322959f86 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -30,7 +30,25 @@ namespace render { namespace entities { class PolyVoxEntityRenderer; -} } +} } + + +enum class PolyVoxState { + Ready, + Uncompressing, + UncompressingFinished, + BakingMesh, + BakingMeshFinished, + BakingMeshNoCompress, + BakingMeshNoCompressFinished, + Compressing, + CompressingFinished, + BakingShape, + BakingShapeFinished +}; + +QDebug operator<<(QDebug debug, PolyVoxState state); + class RenderablePolyVoxEntityItem : public PolyVoxEntityItem, public scriptable::ModelProvider { friend class render::entities::PolyVoxEntityRenderer; @@ -113,41 +131,61 @@ public: uint8_t getVoxelInternal(const ivec3& v) const; bool setVoxelInternal(const ivec3& v, uint8_t toValue); + void setVoxelMarkNeighbors(int x, int y, int z, uint8_t toValue); - void setVolDataDirty() { withWriteLock([&] { _volDataDirty = true; _meshReady = false; }); } + void compressVolumeDataFinished(const QByteArray& voxelData); + void neighborXEdgeChanged() { withWriteLock([&] { _updateFromNeighborXEdge = true; }); startUpdates(); } + void neighborYEdgeChanged() { withWriteLock([&] { _updateFromNeighborYEdge = true; }); startUpdates(); } + void neighborZEdgeChanged() { withWriteLock([&] { _updateFromNeighborZEdge = true; }); startUpdates(); } bool getMeshes(MeshProxyList& result) override; // deprecated virtual scriptable::ScriptableModelBase getScriptableModel() override; + virtual void update(const quint64& now) override; + bool needsToCallUpdate() const override { return _updateNeeded; } + private: bool updateOnCount(const ivec3& v, uint8_t toValue); PolyVox::RaycastResult doRayCast(glm::vec4 originInVoxel, glm::vec4 farInVoxel, glm::vec4& result) const; + void changeUpdates(bool value); + void startUpdates(); + void stopUpdates(); + void recomputeMesh(); void cacheNeighbors(); void copyUpperEdgesFromNeighbors(); - void bonkNeighbors(); + void tellNeighborsToRecopyEdges(bool force); bool updateDependents(); // these are run off the main thread - void decompressVolumeData(); + void uncompressVolumeData(); void compressVolumeDataAndSendEditPacket(); void computeShapeInfoWorker(); // The PolyVoxEntityItem class has _voxelData which contains dimensions and compressed voxel data. The dimensions // may not match _voxelVolumeSize. - bool _meshDirty { true }; // does collision-shape need to be recomputed? - bool _meshReady{ false }; + bool _meshReady { false }; // do we have something to give scripts that ask for the mesh? + bool _voxelDataDirty { false }; // do we need to uncompress data and expand it into _volData? + bool _volDataDirty { false }; // does recomputeMesh need to be called? + bool _shapeReady { false }; // are we ready to tell bullet our shape? + PolyVoxState _state { PolyVoxState::Ready }; + bool _updateNeeded { true }; + graphics::MeshPointer _mesh; ShapeInfo _shapeInfo; std::shared_ptr> _volData; - bool _voxelDataDirty{ true }; - bool _volDataDirty { false }; // does recomputeMesh need to be called? int _onCount; // how many non-zero voxels are in _volData - bool _neighborsNeedUpdate { false }; + bool _neighborXNeedsUpdate { false }; + bool _neighborYNeedsUpdate { false }; + bool _neighborZNeedsUpdate { false }; + + bool _updateFromNeighborXEdge { false }; + bool _updateFromNeighborYEdge { false }; + bool _updateFromNeighborZEdge { false }; // these are cached lookups of _xNNeighborID, _yNNeighborID, _zNNeighborID, _xPNeighborID, _yPNeighborID, _zPNeighborID EntityItemWeakPointer _xNNeighbor; // neighbor found by going along negative X axis @@ -156,7 +194,6 @@ private: EntityItemWeakPointer _xPNeighbor; // neighbor found by going along positive X axis EntityItemWeakPointer _yPNeighbor; EntityItemWeakPointer _zPNeighbor; - }; namespace render { namespace entities { @@ -170,7 +207,7 @@ public: virtual scriptable::ScriptableModelBase getScriptableModel() override { return asTypedEntity()->getScriptableModel(); } - + protected: virtual ItemKey getKey() override { return ItemKey::Builder::opaqueShape().withTagBits(getTagMask()).withLayer(getHifiRenderLayer()); } virtual ShapeKey getShapeKey() override; @@ -192,7 +229,6 @@ private: glm::mat4 _lastVoxelToWorldMatrix; PolyVoxEntityItem::PolyVoxSurfaceStyle _lastSurfaceStyle { PolyVoxEntityItem::SURFACE_MARCHING_CUBES }; std::array _xyzTextureUrls; - bool _neighborsNeedUpdate{ false }; }; } } diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index c16839dc1a..5437ceaab8 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -965,7 +965,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * // Value overrides entity's "color" property. * albedo: [1.0, 1.0, 0] // Yellow * } - * }), + * }) * }); */ diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index ca9108852f..52470e56c4 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -1294,7 +1294,7 @@ public slots: Q_INVOKABLE int getJointParent(const QUuid& entityID, int index); /**jsdoc - * Gets the translation of a joint in a {@link Entities.EntityProperties-Model|Model} entity relative to the entity's + * Gets the rotation of a joint in a {@link Entities.EntityProperties-Model|Model} entity relative to the entity's * position and orientation. * @function Entities.getAbsoluteJointRotationInObjectFrame * @param {Uuid} entityID - The ID of the entity. diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index 180e914d60..c9da8373db 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -139,6 +139,11 @@ void Antialiasing::run(const render::RenderContextPointer& renderContext, const } #else +void AntialiasingConfig::setAAMode(int mode) { + _mode = std::min((int)AntialiasingConfig::MODE_COUNT, std::max(0, mode)); + emit dirty(); +} + Antialiasing::Antialiasing(bool isSharpenEnabled) : _isSharpenEnabled{ isSharpenEnabled } { } @@ -189,6 +194,8 @@ const gpu::PipelinePointer& Antialiasing::getDebugBlendPipeline() { } void Antialiasing::configure(const Config& config) { + _mode = (AntialiasingConfig::Mode) config.getAAMode(); + _sharpen = config.sharpen * 0.25f; if (!_isSharpenEnabled) { _sharpen = 0.0f; @@ -298,29 +305,33 @@ void Antialiasing::run(const render::RenderContextPointer& renderContext, const }); } - void JitterSampleConfig::setIndex(int current) { _index = (current) % JitterSample::SEQUENCE_LENGTH; emit dirty(); } -int JitterSampleConfig::cycleStopPauseRun() { - _state = (_state + 1) % 3; +void JitterSampleConfig::setState(int state) { + _state = (state) % 3; switch (_state) { - case 0: { - return none(); - break; - } - case 1: { - return pause(); - break; - } - case 2: - default: { - return play(); - break; - } + case 0: { + none(); + break; } + case 1: { + pause(); + break; + } + case 2: + default: { + play(); + break; + } + } + emit dirty(); +} + +int JitterSampleConfig::cycleStopPauseRun() { + setState((_state + 1) % 3); return _state; } diff --git a/libraries/render-utils/src/AntialiasingEffect.h b/libraries/render-utils/src/AntialiasingEffect.h index 7d8bbb44d9..fc25343751 100644 --- a/libraries/render-utils/src/AntialiasingEffect.h +++ b/libraries/render-utils/src/AntialiasingEffect.h @@ -25,6 +25,7 @@ class JitterSampleConfig : public render::Job::Config { Q_PROPERTY(bool freeze MEMBER freeze NOTIFY dirty) Q_PROPERTY(bool stop MEMBER stop NOTIFY dirty) Q_PROPERTY(int index READ getIndex NOTIFY dirty) + Q_PROPERTY(int state READ getState WRITE setState NOTIFY dirty) public: JitterSampleConfig() : render::Job::Config(true) {} @@ -33,6 +34,7 @@ public: bool freeze{ false }; void setIndex(int current); + void setState(int state); public slots: int cycleStopPauseRun(); @@ -86,6 +88,7 @@ private: class AntialiasingConfig : public render::Job::Config { Q_OBJECT + Q_PROPERTY(int mode READ getAAMode WRITE setAAMode NOTIFY dirty) Q_PROPERTY(float blend MEMBER blend NOTIFY dirty) Q_PROPERTY(float sharpen MEMBER sharpen NOTIFY dirty) Q_PROPERTY(float covarianceGamma MEMBER covarianceGamma NOTIFY dirty) @@ -106,9 +109,21 @@ class AntialiasingConfig : public render::Job::Config { public: AntialiasingConfig() : render::Job::Config(true) {} + enum Mode { + OFF = 0, + TAA, + FXAA, + MODE_COUNT + }; + + void setAAMode(int mode); + int getAAMode() const { return _mode; } + void setDebugFXAA(bool debug) { debugFXAAX = (debug ? 0.0f : 1.0f); emit dirty();} bool debugFXAA() const { return (debugFXAAX == 0.0f ? true : false); } + int _mode{ TAA }; + float blend{ 0.25f }; float sharpen{ 0.05f }; @@ -195,6 +210,7 @@ private: gpu::PipelinePointer _debugBlendPipeline; TAAParamsBuffer _params; + AntialiasingConfig::Mode _mode{ AntialiasingConfig::TAA }; float _sharpen{ 0.15f }; bool _isSharpenEnabled{ true }; }; diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index e58d07ac33..0c499a9d34 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -35,12 +35,13 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons const auto frameTransform = inputs.get0(); const auto inputFrameBuffer = inputs.get1(); const auto bloomFrame = inputs.get2(); + const auto lightingModel = inputs.get3(); const auto& bloomStage = renderContext->_scene->getStage(); graphics::BloomPointer bloom; if (bloomStage && bloomFrame->_blooms.size()) { bloom = bloomStage->getBloom(bloomFrame->_blooms.front()); } - if (!bloom) { + if (!bloom || (lightingModel && !lightingModel->isBloomEnabled())) { renderContext->taskFlow.abortTask(); return; } @@ -187,12 +188,17 @@ void BloomDraw::run(const render::RenderContextPointer& renderContext, const Inp } } +void DebugBloomConfig::setMode(int mode) { + _mode = std::min((int)DebugBloomConfig::MODE_COUNT, std::max(0, mode)); + emit dirty(); +} + DebugBloom::DebugBloom() { _params = std::make_shared(sizeof(glm::vec4), nullptr); } void DebugBloom::configure(const Config& config) { - _mode = static_cast(config.mode); + _mode = (DebugBloomConfig::Mode) config.getMode(); assert(_mode < DebugBloomConfig::MODE_COUNT); } @@ -201,6 +207,10 @@ void DebugBloom::run(const render::RenderContextPointer& renderContext, const In assert(renderContext->args->hasViewFrustum()); RenderArgs* args = renderContext->args; + if (_mode == DebugBloomConfig::OFF) { + return; + } + const auto frameBuffer = inputs.get0(); const auto combinedBlurBuffer = inputs.get4(); const auto framebufferSize = frameBuffer->getSize(); diff --git a/libraries/render-utils/src/BloomEffect.h b/libraries/render-utils/src/BloomEffect.h index 07ae2887c9..47558affd9 100644 --- a/libraries/render-utils/src/BloomEffect.h +++ b/libraries/render-utils/src/BloomEffect.h @@ -17,6 +17,7 @@ #include "BloomStage.h" #include "DeferredFrameTransform.h" +#include "LightingModel.h" class BloomConfig : public render::Task::Config { Q_OBJECT @@ -28,7 +29,7 @@ class BloomThresholdConfig : public render::Job::Config { class BloomThreshold { public: - using Inputs = render::VaryingSet3; + using Inputs = render::VaryingSet4; using Outputs = render::VaryingSet3; using Config = BloomThresholdConfig; using JobModel = render::Job::ModelIO; @@ -87,12 +88,13 @@ private: class DebugBloomConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(int mode MEMBER mode NOTIFY dirty) + Q_PROPERTY(int mode READ getMode WRITE setMode NOTIFY dirty) public: enum Mode { - MODE_LEVEL0 = 0, + OFF = 0, + MODE_LEVEL0, MODE_LEVEL1, MODE_LEVEL2, MODE_ALL_LEVELS, @@ -102,7 +104,10 @@ public: DebugBloomConfig() : render::Job::Config(false) {} - int mode{ MODE_ALL_LEVELS }; + void setMode(int mode); + int getMode() const { return _mode; } + + int _mode{ MODE_ALL_LEVELS }; signals: void dirty(); @@ -127,14 +132,14 @@ private: class BloomEffect { public: - using Inputs = render::VaryingSet3; + using Inputs = render::VaryingSet4; using Config = BloomConfig; - using JobModel = render::Task::ModelI; + using JobModel = render::Task::ModelI; - BloomEffect(); + BloomEffect(); - void configure(const Config& config); - void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); + void configure(const Config& config); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); }; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 4561cf903d..c506f22bc7 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -150,8 +150,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren // Prepare deferred, generate the shared Deferred Frame Transform. Only valid with the scaled frame buffer const auto deferredFrameTransform = task.addJob("DeferredFrameTransform", jitter); - const auto opaqueRangeTimer = task.addJob("BeginOpaqueRangeTimer", "DrawOpaques"); - const auto prepareDeferredInputs = PrepareDeferred::Inputs(scaledPrimaryFramebuffer, lightingModel).asVarying(); const auto prepareDeferredOutputs = task.addJob("PrepareDeferred", prepareDeferredInputs); const auto deferredFramebuffer = prepareDeferredOutputs.getN(0); @@ -164,8 +162,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto opaqueInputs = DrawStateSortDeferred::Inputs(opaques, lightingModel, jitter).asVarying(); task.addJob("DrawOpaqueDeferred", opaqueInputs, shapePlumber); - task.addJob("OpaqueRangeTimer", opaqueRangeTimer); - // Opaque all rendered // Linear Depth Pass @@ -216,13 +212,10 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto transparentsInputs = RenderTransparentDeferred::Inputs(transparents, hazeFrame, lightFrame, lightingModel, lightClusters, shadowFrame, jitter).asVarying(); task.addJob("DrawTransparentDeferred", transparentsInputs, shapePlumber); - const auto outlineRangeTimer = task.addJob("BeginHighlightRangeTimer", "Highlight"); - + // Highlight const auto outlineInputs = DrawHighlightTask::Inputs(items, deferredFramebuffer, lightingFramebuffer, deferredFrameTransform, jitter).asVarying(); task.addJob("DrawHighlight", outlineInputs); - task.addJob("HighlightRangeTimer", outlineRangeTimer); - // Layered Over (in front) const auto inFrontOpaquesInputs = DrawLayered3D::Inputs(inFrontOpaque, lightingModel, hazeFrame, jitter).asVarying(); const auto inFrontTransparentsInputs = DrawLayered3D::Inputs(inFrontTransparent, lightingModel, hazeFrame, jitter).asVarying(); @@ -234,7 +227,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("Antialiasing", antialiasingInputs); // Add bloom - const auto bloomInputs = BloomEffect::Inputs(deferredFrameTransform, lightingFramebuffer, bloomFrame).asVarying(); + const auto bloomInputs = BloomEffect::Inputs(deferredFrameTransform, lightingFramebuffer, bloomFrame, lightingModel).asVarying(); task.addJob("Bloom", bloomInputs); const auto destFramebuffer = static_cast(nullptr); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index e679e9f6c5..57e9b06f9d 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -2210,7 +2210,7 @@ void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& /**jsdoc * Triggered when the script starts for a user. See also, {@link Script.entityScriptPreloadFinished}. *

Note: Can only be connected to via this.preload = function (...) { ... } in the entity script.

- *
Available in:Client Entity ScriptsServer Entity Scripts
+ *

Supported Script Types: Client Entity Scripts • Server Entity Scripts

* @function Entities.preload * @param {Uuid} entityID - The ID of the entity that the script is running in. * @returns {Signal} @@ -2416,7 +2416,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co /**jsdoc * Triggered when the script terminates for a user. *

Note: Can only be connected to via this.unoad = function () { ... } in the entity script.

- *
Available in:Client Entity ScriptsServer Entity Scripts
+ *

Supported Script Types: Client Entity Scripts • Server Entity Scripts

* @function Entities.unload * @param {Uuid} entityID - The ID of the entity that the script is running in. * @returns {Signal} diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 7300f52f9b..3ffef16844 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -416,8 +416,10 @@ public: * Provides access to methods or objects provided in an external JavaScript or JSON file. * See {@link https://docs.highfidelity.com/script/js-tips.html} for further details. * @function Script.require - * @param {string} module - The module to use. May be a JavaScript file or the name of a system module such as - * "sppUi". + * @param {string} module - The module to use. May be a JavaScript file, a JSON file, or the name of a system module such + * as "appUi" (i.e., the "appUi.js" system module JavaScript file). + * @returns {object|array} The value assigned to module.exports in the JavaScript file, or the value defined + * in the JSON file. */ Q_INVOKABLE QScriptValue require(const QString& moduleId); @@ -842,7 +844,7 @@ signals: /**jsdoc * Triggered when the script starts for the user. See also, {@link Entities.preload}. - *
Available in:Client Entity ScriptsServer Entity Scripts
+ *

Supported Script Types: Client Entity Scripts • Server Entity Scripts

* @function Script.entityScriptPreloadFinished * @param {Uuid} entityID - The ID of the entity that the script is running in. * @returns {Signal} diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index c21a9ae4df..8aa7311de4 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -1319,13 +1319,13 @@ void ViveControllerManager::InputDevice::setConfigFromString(const QString& valu * RXnumbernumberRight stick x-axis scale. * RYnumbernumberRight stick y-axis scale. * LSnumbernumberLeft touch pad pressed. - * LS_CENTERnumbernumberLeft touch pad center pressed. - * LS_XnumbernumberLeft touch pad pressed x-coordinate. - * LS_YnumbernumberLeft touch pad pressed y-coordinate. + * LSCenternumbernumberLeft touch pad center pressed. + * LSXnumbernumberLeft touch pad pressed x-coordinate. + * LSYnumbernumberLeft touch pad pressed y-coordinate. * RSnumbernumberRight touch pad pressed. - * RS_CENTERnumbernumberRight touch pad center pressed. - * RS_XnumbernumberRight touch pad pressed x-coordinate. - * RS_YnumbernumberRight touch pad pressed y-coordinate. + * RSCenternumbernumberRight touch pad center pressed. + * RSXnumbernumberRight touch pad pressed x-coordinate. + * RSYnumbernumberRight touch pad pressed y-coordinate. * LSTouchnumbernumberLeft touch pad is touched. * RSTouchnumbernumberRight touch pad is touched. * Triggers diff --git a/scripts/developer/utilities/render/bloom.qml b/scripts/developer/utilities/render/bloom.qml deleted file mode 100644 index 705a9826d6..0000000000 --- a/scripts/developer/utilities/render/bloom.qml +++ /dev/null @@ -1,83 +0,0 @@ -// -// bloom.qml -// developer/utilities/render -// -// Olivier Prat, created on 09/25/2017. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html -// -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import "configSlider" - -Item { - id: root - property var configDebug: Render.getConfig("RenderMainView.DebugBloom") - - Column { - spacing: 8 - - GroupBox { - title: "Debug" - Row { - ExclusiveGroup { id: debugGroup } - RadioButton { - text : "Off" - checked : !root.configDebug["enabled"] - onCheckedChanged: { - if (checked) { - root.configDebug["enabled"] = false - } - } - exclusiveGroup : debugGroup - } - RadioButton { - text : "Lvl 0" - checked :root.configDebug["enabled"] && root.configDebug["mode"]==0 - onCheckedChanged: { - if (checked) { - root.configDebug["enabled"] = true - root.configDebug["mode"] = 0 - } - } - exclusiveGroup : debugGroup - } - RadioButton { - text : "Lvl 1" - checked : root.configDebug["enabled"] && root.configDebug["mode"]==1 - onCheckedChanged: { - if (checked) { - root.configDebug["enabled"] = true - root.configDebug["mode"] = 1 - } - } - exclusiveGroup : debugGroup - } - RadioButton { - text : "Lvl 2" - checked : root.configDebug["enabled"] && root.configDebug["mode"]==2 - onCheckedChanged: { - if (checked) { - root.configDebug["enabled"] = true - root.configDebug["mode"] = 2 - } - } - exclusiveGroup : debugGroup - } - RadioButton { - text : "All" - checked : root.configDebug["enabled"] && root.configDebug["mode"]==3 - onCheckedChanged: { - if (checked) { - root.configDebug["enabled"] = true - root.configDebug["mode"] = 3 - } - } - exclusiveGroup : debugGroup - } - } - } - } -} diff --git a/scripts/developer/utilities/render/debugBloom.js b/scripts/developer/utilities/render/debugBloom.js index 39629ab0ce..3c5e722a18 100644 --- a/scripts/developer/utilities/render/debugBloom.js +++ b/scripts/developer/utilities/render/debugBloom.js @@ -10,11 +10,8 @@ // // Set up the qml ui -var qml = Script.resolvePath('bloom.qml'); -var window = new OverlayWindow({ - title: 'Bloom', - source: qml, - width: 285, - height: 40, -}); -window.closed.connect(function() { Script.stop(); }); \ No newline at end of file +var window = Desktop.createWindow(Script.resolvePath('./luci/Bloom.qml'), { + title: "Bloom", + presentationMode: Desktop.PresentationMode.NATIVE, + size: {x: 285, y: 40} +}); \ No newline at end of file diff --git a/scripts/developer/utilities/render/deferredLighting.qml b/scripts/developer/utilities/render/deferredLighting.qml deleted file mode 100644 index f2891ddc55..0000000000 --- a/scripts/developer/utilities/render/deferredLighting.qml +++ /dev/null @@ -1,103 +0,0 @@ -// -// deferredLighting.qml -// -// Created by Sam Gateau on 6/6/2016 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html -// -import QtQuick 2.7 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.3 - -import stylesUit 1.0 -import controlsUit 1.0 as HifiControls -import "configSlider" -import "luci" - -Rectangle { - HifiConstants { id: hifi;} - id: render; - anchors.margins: hifi.dimensions.contentMargin.x - - color: hifi.colors.baseGray; - property var mainViewTask: Render.getConfig("RenderMainView") - - Column { - spacing: 5 - anchors.left: parent.left - anchors.right: parent.right - anchors.margins: hifi.dimensions.contentMargin.x - - - HifiControls.Label { - text: "Shading" - } - ShadingModel {} - - Separator {} - ToneMapping {} - - Separator {} - Column { - anchors.left: parent.left - anchors.right: parent.right - spacing: 5 - Repeater { - model: [ "MSAA:PreparePrimaryBufferForward:numSamples:4:1" - ] - ConfigSlider { - label: qsTr(modelData.split(":")[0]) - integral: true - config: render.mainViewTask.getConfig(modelData.split(":")[1]) - property: modelData.split(":")[2] - max: modelData.split(":")[3] - min: modelData.split(":")[4] - - anchors.left: parent.left - anchors.right: parent.right - } - } - } - Separator {} - Framebuffer {} - - Separator {} - BoundingBoxes { - - } - Separator {} - Row { - HifiControls.Button { - text: "Engine" - // activeFocusOnPress: false - onClicked: { - sendToScript({method: "openEngineView"}); - } - } - HifiControls.Button { - text: "LOD" - // activeFocusOnPress: false - onClicked: { - sendToScript({method: "openEngineLODView"}); - } - } - HifiControls.Button { - text: "Cull" - // activeFocusOnPress: false - onClicked: { - sendToScript({method: "openCullInspectorView"}); - } - } - } - Row { - HifiControls.Button { - text: "Material" - onClicked: { - sendToScript({method: "openMaterialInspectorView"}); - } - } - } - } -} diff --git a/scripts/developer/utilities/render/luci.js b/scripts/developer/utilities/render/luci.js index fd84f55e65..e2e5523ccd 100644 --- a/scripts/developer/utilities/render/luci.js +++ b/scripts/developer/utilities/render/luci.js @@ -1,80 +1,84 @@ -"use strict"; - -// -// Luci.js -// tablet-engine app -// -// 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 -// +var MaterialInspector = Script.require('./materialInspector.js'); +var Page = Script.require('./luci/Page.js'); -(function() { - var AppUi = Script.require('appUi'); - - var MaterialInspector = Script.require('./materialInspector.js'); - var Page = Script.require('./luci/Page.js'); - var moveDebugCursor = false; - var onMousePressEvent = function (e) { - if (e.isMiddleButton) { - moveDebugCursor = true; - setDebugCursor(e.x, e.y); - } - }; - Controller.mousePressEvent.connect(onMousePressEvent); - - var onMouseReleaseEvent = function () { - moveDebugCursor = false; - }; - Controller.mouseReleaseEvent.connect(onMouseReleaseEvent); - - var onMouseMoveEvent = function (e) { - if (moveDebugCursor) { - setDebugCursor(e.x, e.y); - } - }; - Controller.mouseMoveEvent.connect(onMouseMoveEvent); - - function setDebugCursor(x, y) { - var nx = 2.0 * (x / Window.innerWidth) - 1.0; - var ny = 1.0 - 2.0 * ((y) / (Window.innerHeight)); - - Render.getConfig("RenderMainView").getConfig("DebugDeferredBuffer").size = { x: nx, y: ny, z: 1.0, w: 1.0 }; - } +function openView() { + //window.closed.connect(function() { Script.stop(); }); var pages = new Pages(); - - pages.addPage('openEngineLODView', 'Render LOD', '../lod.qml', 300, 400); - pages.addPage('openCullInspectorView', 'Cull Inspector', '../luci/Culling.qml', 300, 400); - pages.addPage('openMaterialInspectorView', 'Material Inspector', '../materialInspector.qml', 300, 400, MaterialInspector.setWindow); - function fromQml(message) { if (pages.open(message.method)) { return; } } - var ui; - function startup() { - ui = new AppUi({ - buttonName: "LUCI", - home: Script.resolvePath("deferredLighting.qml"), - additionalAppScreens : Script.resolvePath("engineInspector.qml"), - onMessage: fromQml, - normalButton: Script.resolvePath("../../../system/assets/images/luci-i.svg"), - activeButton: Script.resolvePath("../../../system/assets/images/luci-a.svg") - }); + var luciWindow + function openLuciWindow(window) { + if (luciWindow !== undefined) { + activeWindow.fromQml.disconnect(fromQml); + } + if (window !== undefined) { + window.fromQml.connect(fromQml); + } + luciWindow = window; + + + var moveDebugCursor = false; + var onMousePressEvent = function (e) { + if (e.isMiddleButton) { + moveDebugCursor = true; + setDebugCursor(e.x, e.y); + } + }; + Controller.mousePressEvent.connect(onMousePressEvent); + + var onMouseReleaseEvent = function () { + moveDebugCursor = false; + }; + Controller.mouseReleaseEvent.connect(onMouseReleaseEvent); + + var onMouseMoveEvent = function (e) { + if (moveDebugCursor) { + setDebugCursor(e.x, e.y); + } + }; + Controller.mouseMoveEvent.connect(onMouseMoveEvent); + + function setDebugCursor(x, y) { + var nx = 2.0 * (x / Window.innerWidth) - 1.0; + var ny = 1.0 - 2.0 * ((y) / (Window.innerHeight)); + + Render.getConfig("RenderMainView").getConfig("DebugDeferredBuffer").size = { x: nx, y: ny, z: 1.0, w: 1.0 }; + } + } - startup(); - Script.scriptEnding.connect(function () { + + function closeLuciWindow() { + if (luciWindow !== undefined) { + activeWindow.fromQml.disconnect(fromQml); + } + luciWindow = {}; + Controller.mousePressEvent.disconnect(onMousePressEvent); Controller.mouseReleaseEvent.disconnect(onMouseReleaseEvent); Controller.mouseMoveEvent.disconnect(onMouseMoveEvent); pages.clear(); - }); -}()); + } + + pages.addPage('Luci', 'Luci', '../luci.qml', 300, 420, openLuciWindow, closeLuciWindow); + pages.addPage('openEngineInspectorView', 'Render Engine Inspector', '../engineInspector.qml', 300, 400); + pages.addPage('openEngineLODView', 'Render LOD', '../lod.qml', 300, 400); + pages.addPage('openMaterialInspectorView', 'Material Inspector', '../materialInspector.qml', 300, 400, MaterialInspector.setWindow, MaterialInspector.setWindow); + + pages.open('Luci'); + + + return pages; +} + + +openView(); + diff --git a/scripts/developer/utilities/render/luci.qml b/scripts/developer/utilities/render/luci.qml index d768acb160..71962992b8 100644 --- a/scripts/developer/utilities/render/luci.qml +++ b/scripts/developer/utilities/render/luci.qml @@ -72,6 +72,12 @@ Rectangle { Antialiasing {} } } + Prop.PropFolderPanel { + label: "Bloom" + panelFrameData: Component { + Bloom {} + } + } Prop.PropFolderPanel { label: "Culling" panelFrameData: Component { diff --git a/scripts/developer/utilities/render/luci/Antialiasing.qml b/scripts/developer/utilities/render/luci/Antialiasing.qml index 6d1209157f..2a52dfed46 100644 --- a/scripts/developer/utilities/render/luci/Antialiasing.qml +++ b/scripts/developer/utilities/render/luci/Antialiasing.qml @@ -22,15 +22,12 @@ import "../../lib/prop" as Prop Column{ - HifiConstants { id: hifi; } + id: antialiasing - id: antialiasing - padding: 10 anchors.left: parent.left anchors.right: parent.right - spacing: 10 - Prop.PropScalar { + Prop.PropScalar { label: "MSAA" object: Render.getConfig("RenderMainView.PreparePrimaryBufferForward") property: "numSamples" @@ -38,49 +35,44 @@ Column{ max: 32 integral: true } - Row { - spacing: 10 - id: fxaaOnOff - property bool debugFXAA: false - HifiControls.Button { - function getTheText() { - if (Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff) { - return "FXAA" - } else { - return "TAA" - } - } - text: getTheText() - onClicked: { - var onOff = !Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff; - if (onOff) { - Render.getConfig("RenderMainView.JitterCam").none(); - Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff = true; - } else { - Render.getConfig("RenderMainView.JitterCam").play(); - Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff = false; - } - - } - } + + Prop.PropEnum { + label: "Deferred AA Method" + object: Render.getConfig("RenderMainView.Antialiasing") + property: "mode" + enums: [ + "Off", + "TAA", + "FXAA", + ] + } + Prop.PropEnum { + id: jitter + label: "Jitter" + object: Render.getConfig("RenderMainView.JitterCam") + property: "state" + enums: [ + "Off", + "On", + "Paused", + ] } Separator {} + + Prop.PropScalar { + visible: (Render.getConfig("RenderMainView.JitterCam").state == 2) + label: "Sample Index" + object: Render.getConfig("RenderMainView.JitterCam") + property: "index" + // min: -1 + // max: 32 + readOnly: true + integral: true + } Row { + visible: (Render.getConfig("RenderMainView.JitterCam").state == 2) spacing: 10 - - HifiControls.Button { - text: { - var state = 2 - (Render.getConfig("RenderMainView.JitterCam").freeze * 1 - Render.getConfig("RenderMainView.JitterCam").stop * 2); - if (state === 2) { - return "Jitter" - } else if (state === 1) { - return "Paused at " + Render.getConfig("RenderMainView.JitterCam").index + "" - } else { - return "No Jitter" - } - } - onClicked: { Render.getConfig("RenderMainView.JitterCam").cycleStopPauseRun(); } - } + HifiControls.Button { text: "<" onClicked: { Render.getConfig("RenderMainView.JitterCam").prev(); } @@ -90,96 +82,75 @@ Column{ onClicked: { Render.getConfig("RenderMainView.JitterCam").next(); } } } - Separator {} - HifiControls.CheckBox { - boxSize: 20 - text: "Constrain color" - checked: Render.getConfig("RenderMainView.Antialiasing")["constrainColor"] - onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["constrainColor"] = checked } - } - ConfigSlider { - label: qsTr("Covariance gamma") - integral: false - config: Render.getConfig("RenderMainView.Antialiasing") + Separator {} + Prop.PropBool { + label: "Constrain color" + object: Render.getConfig("RenderMainView.Antialiasing") + property: "constrainColor" + } + Prop.PropScalar { + label: "Covariance gamma" + object: Render.getConfig("RenderMainView.Antialiasing") property: "covarianceGamma" max: 1.5 min: 0.5 - height: 38 - } - Separator {} - HifiControls.CheckBox { - boxSize: 20 - text: "Feedback history color" - checked: Render.getConfig("RenderMainView.Antialiasing")["feedbackColor"] - onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["feedbackColor"] = checked } - } - - ConfigSlider { - label: qsTr("Source blend") - integral: false - config: Render.getConfig("RenderMainView.Antialiasing") + } + Separator {} + Prop.PropBool { + label: "Feedback history color" + object: Render.getConfig("RenderMainView.Antialiasing") + property: "feedbackColor" + } + Prop.PropScalar { + label: "Source blend" + object: Render.getConfig("RenderMainView.Antialiasing") property: "blend" max: 1.0 min: 0.0 - height: 38 } - - ConfigSlider { - label: qsTr("Post sharpen") - integral: false - config: Render.getConfig("RenderMainView.Antialiasing") + Prop.PropScalar { + label: "Post sharpen" + object: Render.getConfig("RenderMainView.Antialiasing") property: "sharpen" max: 1.0 min: 0.0 } - Separator {} - Row { - - spacing: 10 - HifiControls.CheckBox { - boxSize: 20 - text: "Debug" - checked: Render.getConfig("RenderMainView.Antialiasing")["debug"] - onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["debug"] = checked } - } - HifiControls.CheckBox { - boxSize: 20 - text: "Show Debug Cursor" - checked: Render.getConfig("RenderMainView.Antialiasing")["showCursorPixel"] - onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["showCursorPixel"] = checked } - } - } - ConfigSlider { - label: qsTr("Debug Region <") - integral: false - config: Render.getConfig("RenderMainView.Antialiasing") + Separator {} + Prop.PropBool { + label: "Debug" + object: Render.getConfig("RenderMainView.Antialiasing") + property: "debug" + } + Prop.PropBool { + label: "Show Debug Cursor" + object: Render.getConfig("RenderMainView.Antialiasing") + property: "showCursorPixel" + } + Prop.PropScalar { + label: "Debug Region <" + object: Render.getConfig("RenderMainView.Antialiasing") property: "debugX" max: 1.0 min: 0.0 } - HifiControls.CheckBox { - boxSize: 20 - text: "Closest Fragment" - checked: Render.getConfig("RenderMainView.Antialiasing")["showClosestFragment"] - onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["showClosestFragment"] = checked } - } - ConfigSlider { - label: qsTr("Debug Velocity Threshold [pix]") - integral: false - config: Render.getConfig("RenderMainView.Antialiasing") + Prop.PropBool { + label: "Closest Fragment" + object: Render.getConfig("RenderMainView.Antialiasing") + property: "showClosestFragment" + } + Prop.PropScalar { + label: "Debug Velocity Threshold [pix]" + object: Render.getConfig("RenderMainView.Antialiasing") property: "debugShowVelocityThreshold" max: 50 min: 0.0 - height: 38 } - ConfigSlider { - label: qsTr("Debug Orb Zoom") - integral: false - config: Render.getConfig("RenderMainView.Antialiasing") + Prop.PropScalar { + label: "Debug Orb Zoom" + object: Render.getConfig("RenderMainView.Antialiasing") property: "debugOrbZoom" max: 32.0 min: 1.0 - height: 38 - } + } } diff --git a/scripts/developer/utilities/render/luci/Bloom.qml b/scripts/developer/utilities/render/luci/Bloom.qml new file mode 100644 index 0000000000..7db75d94ef --- /dev/null +++ b/scripts/developer/utilities/render/luci/Bloom.qml @@ -0,0 +1,48 @@ +// +// bloom.qml +// +// Olivier Prat, created on 09/25/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// +import QtQuick 2.7 +import "../../lib/prop" as Prop + +Column { + anchors.left: parent.left + anchors.right: parent.right + + id: bloom + + property var config: Render.getConfig("RenderMainView.DebugBloom") + + Prop.PropBool { + label: "Apply Bloom" + object: Render.getConfig("RenderMainView.LightingModel") + property: "enableBloom" + } + + function setDebugMode(mode) { + console.log("Bloom mode is " + mode) + bloom.config.enabled = (mode != 0); + bloom.config.mode = mode; + } + + Prop.PropEnum { + label: "Debug Bloom Buffer" + // object: config + // property: "mode" + enums: [ + "Off", + "Lvl 0", + "Lvl 1", + "Lvl 2", + "All", + ] + + valueVarSetter: function (mode) { bloom.setDebugMode(mode) } + } +} + diff --git a/scripts/developer/utilities/render/luci/BoundingBoxes.qml b/scripts/developer/utilities/render/luci/BoundingBoxes.qml index 636267729c..6b34e41b4c 100644 --- a/scripts/developer/utilities/render/luci/BoundingBoxes.qml +++ b/scripts/developer/utilities/render/luci/BoundingBoxes.qml @@ -54,7 +54,7 @@ Column { Prop.PropCheckBox { text: "Zones" checked: root.mainViewTask.getConfig("DrawZones")["enabled"] - onCheckedChanged: { root.mainViewTask.getConfig("ZoneRenderer")["enabled"] = checked; root.mainViewTask.getConfig("DrawZones")["enabled"] = checked; } + onCheckedChanged: { root.mainViewTask.getConfig("DrawZones")["enabled"] = checked; } } } Column { diff --git a/scripts/developer/utilities/render/luci/ShadingModel.qml b/scripts/developer/utilities/render/luci/ShadingModel.qml index 78ca7f1740..afeca5a204 100644 --- a/scripts/developer/utilities/render/luci/ShadingModel.qml +++ b/scripts/developer/utilities/render/luci/ShadingModel.qml @@ -35,7 +35,8 @@ Column { "Emissive:LightingModel:enableEmissive", "Lightmap:LightingModel:enableLightmap", "Background:LightingModel:enableBackground", - "Haze:LightingModel:enableHaze", + "Haze:LightingModel:enableHaze", + "Bloom:LightingModel:enableBloom", "AO:LightingModel:enableAmbientOcclusion", "Textures:LightingModel:enableMaterialTexturing" ] diff --git a/scripts/developer/utilities/render/luci/qmldir b/scripts/developer/utilities/render/luci/qmldir index 3ebd9fcd8d..a4059ffcab 100644 --- a/scripts/developer/utilities/render/luci/qmldir +++ b/scripts/developer/utilities/render/luci/qmldir @@ -5,6 +5,7 @@ BoundingBoxes 1.0 BoundingBoxes.qml Framebuffer 1.0 Framebuffer.qml Antialiasing 1.0 Antialiasing.qml Culling 1.0 Culling.qml +Bloom 1.0 Bloom.qml Platform 1.0 Platform.qml RenderSettings 1.0 RenderSettings.qml diff --git a/scripts/developer/utilities/render/luci2.js b/scripts/developer/utilities/render/luci2.js deleted file mode 100644 index e2e5523ccd..0000000000 --- a/scripts/developer/utilities/render/luci2.js +++ /dev/null @@ -1,84 +0,0 @@ - - -var MaterialInspector = Script.require('./materialInspector.js'); -var Page = Script.require('./luci/Page.js'); - - -function openView() { - //window.closed.connect(function() { Script.stop(); }); - - - var pages = new Pages(); - function fromQml(message) { - if (pages.open(message.method)) { - return; - } - } - - var luciWindow - function openLuciWindow(window) { - if (luciWindow !== undefined) { - activeWindow.fromQml.disconnect(fromQml); - } - if (window !== undefined) { - window.fromQml.connect(fromQml); - } - luciWindow = window; - - - var moveDebugCursor = false; - var onMousePressEvent = function (e) { - if (e.isMiddleButton) { - moveDebugCursor = true; - setDebugCursor(e.x, e.y); - } - }; - Controller.mousePressEvent.connect(onMousePressEvent); - - var onMouseReleaseEvent = function () { - moveDebugCursor = false; - }; - Controller.mouseReleaseEvent.connect(onMouseReleaseEvent); - - var onMouseMoveEvent = function (e) { - if (moveDebugCursor) { - setDebugCursor(e.x, e.y); - } - }; - Controller.mouseMoveEvent.connect(onMouseMoveEvent); - - function setDebugCursor(x, y) { - var nx = 2.0 * (x / Window.innerWidth) - 1.0; - var ny = 1.0 - 2.0 * ((y) / (Window.innerHeight)); - - Render.getConfig("RenderMainView").getConfig("DebugDeferredBuffer").size = { x: nx, y: ny, z: 1.0, w: 1.0 }; - } - - } - - function closeLuciWindow() { - if (luciWindow !== undefined) { - activeWindow.fromQml.disconnect(fromQml); - } - luciWindow = {}; - - Controller.mousePressEvent.disconnect(onMousePressEvent); - Controller.mouseReleaseEvent.disconnect(onMouseReleaseEvent); - Controller.mouseMoveEvent.disconnect(onMouseMoveEvent); - pages.clear(); - } - - pages.addPage('Luci', 'Luci', '../luci.qml', 300, 420, openLuciWindow, closeLuciWindow); - pages.addPage('openEngineInspectorView', 'Render Engine Inspector', '../engineInspector.qml', 300, 400); - pages.addPage('openEngineLODView', 'Render LOD', '../lod.qml', 300, 400); - pages.addPage('openMaterialInspectorView', 'Material Inspector', '../materialInspector.qml', 300, 400, MaterialInspector.setWindow, MaterialInspector.setWindow); - - pages.open('Luci'); - - - return pages; -} - - -openView(); - diff --git a/scripts/simplifiedUI/simplifiedEmote/emojiApp/simplifiedEmoji.js b/scripts/simplifiedUI/simplifiedEmote/emojiApp/simplifiedEmoji.js index d3ec9da32d..a0fe6cf811 100644 --- a/scripts/simplifiedUI/simplifiedEmote/emojiApp/simplifiedEmoji.js +++ b/scripts/simplifiedUI/simplifiedEmote/emojiApp/simplifiedEmoji.js @@ -388,7 +388,8 @@ function playPopAnimation() { var emojiCodeMap; var customEmojiCodeMap; var signalsConnected = false; -function init() { +var _this; +function startup() { // make a map of just the utf codes to help with accesing emojiCodeMap = emojiList.reduce(function (codeMap, currentEmojiInList, index) { if ( @@ -414,55 +415,30 @@ function init() { pruneOldAvimojis(); + Script.scriptEnding.connect(unload); Window.domainChanged.connect(onDomainChanged); MyAvatar.scaleChanged.connect(onScaleChanged); - Script.scriptEnding.connect(scriptEnding); signalsConnected = true; -} - -// #endregion -// ************************************* -// END main -// ************************************* - -// ************************************* -// START cleanup -// ************************************* -// #region cleanup - - -function scriptEnding() { - resetEmojis(); - if (signalsConnected) { - Script.scriptEnding.disconnect(scriptEnding); - Window.domainChanged.disconnect(onDomainChanged); - MyAvatar.scaleChanged.disconnect(onScaleChanged); - signalsConnected = false; + function AviMoji() { + _this = this; + this._avimojiQMLWindow = null; } + + AviMoji.prototype = { + addEmoji: addEmojiFromQML, + registerAvimojiQMLWindow: registerAvimojiQMLWindow + }; + + return new AviMoji(); } -// #endregion -// ************************************* -// END cleanup -// ************************************* - -// ************************************* -// START API -// ************************************* -// #region API - -var _this; -function AviMoji() { - _this = this; - this._avimojiQMLWindow; -} - function registerAvimojiQMLWindow(avimojiQMLWindow) { this._avimojiQMLWindow = avimojiQMLWindow; } + function addEmojiFromQML(code) { var emojiObject = emojiList[emojiCodeMap[code]]; var emojiFilename; @@ -475,25 +451,17 @@ function addEmojiFromQML(code) { handleSelectedEmoji(emojiFilename); } + function unload() { - scriptEnding(); + resetEmojis(); + if (signalsConnected) { + Window.domainChanged.disconnect(onDomainChanged); + MyAvatar.scaleChanged.disconnect(onScaleChanged); + signalsConnected = false; + } } -function startup() { - init(); -} -AviMoji.prototype = { - startup: startup, - addEmoji: addEmojiFromQML, - unload: unload, - registerAvimojiQMLWindow: registerAvimojiQMLWindow -}; +var aviMoji = startup(); - -module.exports = AviMoji; - -// #endregion -// ************************************* -// END API -// ************************************* \ No newline at end of file +module.exports = aviMoji; \ No newline at end of file diff --git a/scripts/simplifiedUI/simplifiedEmote/simplifiedEmote.js b/scripts/simplifiedUI/simplifiedEmote/simplifiedEmote.js index cd6c80dd64..abf0dd73e8 100644 --- a/scripts/simplifiedUI/simplifiedEmote/simplifiedEmote.js +++ b/scripts/simplifiedUI/simplifiedEmote/simplifiedEmote.js @@ -38,7 +38,6 @@ var customEmojiList = Script.require("./emojiApp/resources/modules/customEmojiLi // #region EMOTE_UTILITY - function updateEmoteAppBarPosition() { if (!emoteAppBarWindow) { return; @@ -425,7 +424,7 @@ function onGeometryChanged(rect) { function onWindowMinimizedChanged(isMinimized) { if (isMinimized) { handleEmoteIndicatorVisibleChanged(false); - } else if (!HMD.active && Settings.getValue("simplifiedUI/emoteIndicatorVisible", true)) { + } else if (!HMD.active) { handleEmoteIndicatorVisibleChanged(true); } } @@ -520,8 +519,8 @@ function showEmoteAppBar() { } -function handleEmoteIndicatorVisibleChanged(newValue) { - if (newValue && !emoteAppBarWindow) { +function handleEmoteIndicatorVisibleChanged(shouldBeVisible) { + if (shouldBeVisible && !emoteAppBarWindow) { showEmoteAppBar(); } else if (emoteAppBarWindow) { emoteAppBarWindow.fromQml.disconnect(onMessageFromEmoteAppBar); @@ -531,13 +530,6 @@ function handleEmoteIndicatorVisibleChanged(newValue) { } -function onSettingsValueChanged(settingName, newValue) { - if (settingName === "simplifiedUI/emoteIndicatorVisible") { - handleEmoteIndicatorVisibleChanged(newValue); - } -} - - function onDisplayModeChanged(isHMDMode) { reactionsBegun.forEach(function(react) { endReactionWrapper(react); @@ -545,18 +537,17 @@ function onDisplayModeChanged(isHMDMode) { if (isHMDMode) { handleEmoteIndicatorVisibleChanged(false); - } else if (Settings.getValue("simplifiedUI/emoteIndicatorVisible", true)) { + } else { handleEmoteIndicatorVisibleChanged(true); } } -var EmojiAPI = Script.require("./emojiApp/simplifiedEmoji.js"); -var emojiAPI = new EmojiAPI(); +var emojiAPI = Script.require("./emojiApp/simplifiedEmoji.js"); var keyPressSignalsConnected = false; var emojiCodeMap; var customEmojiCodeMap; -function init() { +function setup() { deleteOldReticles(); // make a map of just the utf codes to help with accesing @@ -584,22 +575,19 @@ function init() { Window.minimizedChanged.connect(onWindowMinimizedChanged); Window.geometryChanged.connect(onGeometryChanged); - Settings.valueChanged.connect(onSettingsValueChanged); HMD.displayModeChanged.connect(onDisplayModeChanged); - emojiAPI.startup(); getSounds(); - handleEmoteIndicatorVisibleChanged(Settings.getValue("simplifiedUI/emoteIndicatorVisible", true)); + handleEmoteIndicatorVisibleChanged(true); Controller.keyPressEvent.connect(keyPressHandler); Controller.keyReleaseEvent.connect(keyReleaseHandler); keyPressSignalsConnected = true; - - Script.scriptEnding.connect(shutdown); + Script.scriptEnding.connect(unload); } -function shutdown() { +function unload() { if (emoteAppBarWindow) { emoteAppBarWindow.fromQml.disconnect(onMessageFromEmoteAppBar); emoteAppBarWindow.close(); @@ -614,14 +602,12 @@ function shutdown() { endReactionWrapper(react); }); - emojiAPI.unload(); maybeClearClapSoundInterval(); maybeClearReticleUpdateLimiterTimeout(); maybeDeleteRemoteIndicatorTimeout(); Window.minimizedChanged.disconnect(onWindowMinimizedChanged); Window.geometryChanged.disconnect(onGeometryChanged); - Settings.valueChanged.disconnect(onSettingsValueChanged); HMD.displayModeChanged.disconnect(onDisplayModeChanged); if (keyPressSignalsConnected) { @@ -768,37 +754,4 @@ function toggleEmojiApp() { // END EMOJI // ************************************* -// ************************************* -// START API -// ************************************* -// #region API - - -function startup() { - init(); -} - - -function unload() { - shutdown(); -} - -var _this; -function EmoteBar() { - _this = this; -} - - -EmoteBar.prototype = { - startup: startup, - unload: unload -}; - -module.exports = EmoteBar; - - -// #endregion -// ************************************* -// END API -// ************************************* - +setup(); \ No newline at end of file diff --git a/scripts/simplifiedUI/simplifiedEmote/ui/qml/SimplifiedEmoteIndicator.qml b/scripts/simplifiedUI/simplifiedEmote/ui/qml/SimplifiedEmoteIndicator.qml index a401feec12..787ccadd62 100644 --- a/scripts/simplifiedUI/simplifiedEmote/ui/qml/SimplifiedEmoteIndicator.qml +++ b/scripts/simplifiedUI/simplifiedEmote/ui/qml/SimplifiedEmoteIndicator.qml @@ -21,20 +21,24 @@ Rectangle { id: root color: simplifiedUI.colors.white anchors.fill: parent - property int originalWidth: 48 property int expandedWidth: mainEmojiContainer.width + drawerContainer.width // For the below to work, the Repeater's Item's second child must be the individual button's `MouseArea` - property int requestedWidth: (drawerContainer.keepDrawerExpanded || - emoteIndicatorMouseArea.containsMouse || - emoteButtonsRepeater.itemAt(0).hovered || - emoteButtonsRepeater.itemAt(1).hovered || - emoteButtonsRepeater.itemAt(2).hovered || - emoteButtonsRepeater.itemAt(3).hovered || - emoteButtonsRepeater.itemAt(4).hovered || - emoteButtonsRepeater.itemAt(5).hovered) ? expandedWidth : originalWidth; + property int requestedWidth: ( + root.allowEmoteDrawerExpansion && ( + drawerContainer.keepDrawerExpanded || + emoteIndicatorMouseArea.containsMouse || + emoteButtonsRepeater.itemAt(0).hovered || + emoteButtonsRepeater.itemAt(1).hovered || + emoteButtonsRepeater.itemAt(2).hovered || + emoteButtonsRepeater.itemAt(3).hovered || + emoteButtonsRepeater.itemAt(4).hovered || + emoteButtonsRepeater.itemAt(5).hovered) + ) ? expandedWidth : originalWidth; readonly property int totalEmojiDurationMS: 7000 // Must match `TOTAL_EMOJI_DURATION_MS` in `simplifiedEmoji.js` readonly property string emoteIconSource: "images/emote_Icon.svg" + property bool allowEmoteDrawerExpansion: Settings.getValue("simplifiedUI/allowEmoteDrawerExpansion", true) + onRequestedWidthChanged: { root.requestNewWidth(root.requestedWidth); @@ -45,6 +49,16 @@ Rectangle { SmoothedAnimation { duration: 220 } } + Connections { + target: Settings + + onValueChanged: { + if (setting === "simplifiedUI/allowEmoteDrawerExpansion") { + root.allowEmoteDrawerExpansion = value; + } + } + } + SimplifiedConstants.SimplifiedConstants { id: simplifiedUI } @@ -158,7 +172,7 @@ Rectangle { anchors.fill: lockIcon source: lockIcon color: "#ffffff" - visible: drawerContainer.keepDrawerExpanded + visible: root.allowEmoteDrawerExpansion && drawerContainer.keepDrawerExpanded } MouseArea { diff --git a/scripts/simplifiedUI/ui/simplifiedNametag/simplifiedNametag.js b/scripts/simplifiedUI/ui/simplifiedNametag/simplifiedNametag.js index beea979170..15ce8a83f6 100644 --- a/scripts/simplifiedUI/ui/simplifiedNametag/simplifiedNametag.js +++ b/scripts/simplifiedUI/ui/simplifiedNametag/simplifiedNametag.js @@ -47,15 +47,24 @@ function onAvatarAdded(uuid) { } -// Called on init +// Create a new nametag list manager, connect signals, and return back a new Nametag object. var avatarNametagMode; function startup() { nameTagListManager.create(); handleAvatarNametagMode(Settings.getValue("simplifiedNametag/avatarNametagMode", "on")); + Script.scriptEnding.connect(unload); Window.domainChanged.connect(onDomainChange); AvatarManager.avatarRemovedEvent.connect(onAvatarRemoved); AvatarManager.avatarAddedEvent.connect(onAvatarAdded); + + function NameTag() {} + + NameTag.prototype = { + handleAvatarNametagMode: handleAvatarNametagMode + }; + + return new NameTag(); } @@ -77,23 +86,7 @@ function handleAvatarNametagMode(newAvatarNameTagMode) { } -// ************************************* -// START api -// ************************************* -// #region api +var nameTag = startup(); -function NameTag() {} +module.exports = nameTag; -NameTag.prototype = { - startup: startup, - unload: unload, - handleAvatarNametagMode: handleAvatarNametagMode -}; - -module.exports = NameTag; - - -// #endregion -// ************************************* -// END api -// ************************************* \ No newline at end of file diff --git a/scripts/simplifiedUI/ui/simplifiedStatusIndicator/simplifiedStatusIndicator.js b/scripts/simplifiedUI/ui/simplifiedStatusIndicator/simplifiedStatusIndicator.js index 2b401c5bc5..4d06ecc99b 100644 --- a/scripts/simplifiedUI/ui/simplifiedStatusIndicator/simplifiedStatusIndicator.js +++ b/scripts/simplifiedUI/ui/simplifiedStatusIndicator/simplifiedStatusIndicator.js @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -function simplifiedStatusIndicator(properties) { +function SimplifiedStatusIndicator() { var that = this; var DEBUG = false; @@ -86,6 +86,7 @@ function simplifiedStatusIndicator(properties) { }); } + // Get status from database function getStatus(callback) { var queryParamString = "type=getStatus"; @@ -125,6 +126,17 @@ function simplifiedStatusIndicator(properties) { // #region SIGNALS + function updateProperties(properties) { + // Overwrite with the given properties + var overwriteableKeys = ["statusChanged"]; + Object.keys(properties).forEach(function (key) { + if (overwriteableKeys.indexOf(key) > -1) { + that[key] = properties[key]; + } + }); + } + + var currentStatus = "available"; // Default is available function toggleStatus() { if (currentStatus === "busy") { @@ -207,6 +219,8 @@ function simplifiedStatusIndicator(properties) { Window.domainChanged.connect(onDomainChanged); getStatus(setStatus); + + Script.scriptEnding.connect(unload); } @@ -224,20 +238,15 @@ function simplifiedStatusIndicator(properties) { // #endregion APP LIFETIME - that.startup = startup; - that.unload = unload; that.toggleStatus = toggleStatus; that.setStatus = setStatus; that.getLocalStatus = getLocalStatus; that.statusChanged = statusChanged; - - // Overwrite with the given properties - var overwriteableKeys = ["statusChanged"]; - Object.keys(properties).forEach(function (key) { - if (overwriteableKeys.indexOf(key) > -1) { - that[key] = properties[key]; - } - }); + that.updateProperties = updateProperties; + + startup(); } +var simplifiedStatusIndicator = new SimplifiedStatusIndicator(); + module.exports = simplifiedStatusIndicator; \ No newline at end of file diff --git a/scripts/simplifiedUI/ui/simplifiedUI.js b/scripts/simplifiedUI/ui/simplifiedUI.js index 803c6627c0..c388c594c9 100644 --- a/scripts/simplifiedUI/ui/simplifiedUI.js +++ b/scripts/simplifiedUI/ui/simplifiedUI.js @@ -583,12 +583,9 @@ function restoreLODSettings() { } -var SimplifiedNametag = Script.require("./simplifiedNametag/simplifiedNametag.js?" + Date.now()); -var SimplifiedStatusIndicator = Script.require("./simplifiedStatusIndicator/simplifiedStatusIndicator.js?" + Date.now()); -var SimplifiedEmote = Script.require("../simplifiedEmote/simplifiedEmote.js?" + Date.now()); -var si; -var nametag; -var emote; +var nametag = Script.require("./simplifiedNametag/simplifiedNametag.js?" + Date.now()); +var si = Script.require("./simplifiedStatusIndicator/simplifiedStatusIndicator.js?" + Date.now()) +var emote = Script.require("../simplifiedEmote/simplifiedEmote.js?" + Date.now()); var oldShowAudioTools; var oldShowBubbleTools; var keepExistingUIAndScriptsSetting = Settings.getValue("simplifiedUI/keepExistingUIAndScripts", false); @@ -607,16 +604,8 @@ function startup() { loadSimplifiedTopBar(); - si = new SimplifiedStatusIndicator({ - statusChanged: onStatusChanged - }); - si.startup(); - nametag = new SimplifiedNametag(); - nametag.startup(); - - emote = new SimplifiedEmote(); - emote.startup(); + si.updateProperties({ statusChanged: onStatusChanged }); updateInputDeviceMutedOverlay(Audio.muted); updateOutputDeviceMutedOverlay(isOutputMuted()); @@ -665,10 +654,6 @@ function shutdown() { maybeDeleteInputDeviceMutedOverlay(); maybeDeleteOutputDeviceMutedOverlay(); - nametag.unload(); - si.unload(); - emote.unload(); - Audio.mutedDesktopChanged.disconnect(onDesktopInputDeviceMutedChanged); Audio.mutedHMDChanged.disconnect(onHMDInputDeviceMutedChanged); Window.geometryChanged.disconnect(onGeometryChanged);