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

This commit is contained in:
RebeccaStankus 2019-08-14 12:04:20 -07:00
commit 9a5812d72c
52 changed files with 1483 additions and 362 deletions

View file

@ -5,7 +5,7 @@ If you are upgrading from previous versions, do a clean uninstall of those versi
Note: The prerequisites will require about 10 GB of space on your drive. You will also need a system with at least 8GB of main memory.
### Step 1. Visual Studio & Python
### Step 1. Visual Studio & Python 3.x
If you dont have Community or Professional edition of Visual Studio, download [Visual Studio Community 2019](https://visualstudio.microsoft.com/vs/). If you have Visual Studio 2017, you are not required to download Visual Studio 2019.
@ -21,7 +21,7 @@ When selecting components, check "Desktop development with C++". On the right on
* MSVC v141 - VS 2017 C++ x64/x86 build tools
* MSVC v140 - VS 2015 C++ build tools (v14.00)
If you do not already have a Python development environment installed, also check "Python Development" in this screen.
If you do not already have a Python 3.x development environment installed, also check "Python Development" in this screen.
If you already have Visual Studio installed and need to add Python, open the "Add or remove programs" control panel and find the "Microsoft Visual Studio Installer". Select it and click "Modify". In the installer, select "Modify" again, then check "Python Development" and allow the installer to apply the changes.
@ -31,9 +31,10 @@ If you do not wish to use the Python installation bundled with Visual Studio, yo
### Step 2. Installing CMake
Download and install the latest version of CMake 3.14.
Download and install the latest version of CMake 3.15.
* Note that earlier versions of CMake will work, but there is a specific bug related to the interaction of Visual Studio 2019 and CMake versions prior to 3.15 that will cause Visual Studio to rebuild far more than it needs to on every build
Download the file named win64-x64 Installer from the [CMake Website](https://cmake.org/download/). You can access the installer on this [3.14 Version page](https://cmake.org/files/v3.14/). During installation, make sure to check "Add CMake to system PATH for all users" when prompted.
Download the file named win64-x64 Installer from the [CMake Website](https://cmake.org/download/). You can access the installer on this [3.15 Version page](https://cmake.org/files/v3.15/). During installation, make sure to check "Add CMake to system PATH for all users" when prompted.
### Step 3. Create VCPKG environment variable
In the next step, you will use CMake to build High Fidelity. By default, the CMake process builds dependency files in Windows' `%TEMP%` directory, which is periodically cleared by the operating system. To prevent you from having to re-build the dependencies in the event that Windows clears that directory, we recommend that you create a `HIFI_VCPKG_BASE` environment variable linked to a directory somewhere on your machine. That directory will contain all dependency files until you manually remove them.
@ -42,9 +43,18 @@ To create this variable:
* Naviagte to 'Edit the System Environment Variables' Through the start menu.
* Click on 'Environment Variables'
* Select 'New'
* Set "Variable name" to HIFI_VCPKG_BASE
* Set "Variable name" to `HIFI_VCPKG_BASE`
* Set "Variable value" to any directory that you have control over.
Additionally, if you have Visual Studio 2019 installed and _only_ Visual Studio 2019 (i.e. you do not have Visual Studio 2017 installed) you must add an additional environment variable `HIFI_VCPKG_BOOTSTRAP` that will fix a bug in our `vcpkg` pre-build step.
To create this variable:
* Naviagte to 'Edit the System Environment Variables' Through the start menu.
* Click on 'Environment Variables'
* Select 'New'
* Set "Variable name" to `HIFI_VCPKG_BOOTSTRAP`
* Set "Variable value" to `1`
### Step 4. Running CMake to Generate Build Files
Run Command Prompt from Start and run the following commands:

View file

@ -75,7 +75,7 @@ public:
void each(std::function<void(AvatarMixerSlave& slave)> functor);
#ifdef DEBUG_EVENT_QUEUE
void AvatarMixerSlavePool::queueStats(QJsonObject& stats);
void queueStats(QJsonObject& stats);
#endif
void setNumThreads(int numThreads);

View file

@ -71,16 +71,19 @@ endif()
if 'Windows' == system:
self.exe = os.path.join(self.path, 'vcpkg.exe')
self.bootstrapCmd = 'bootstrap-vcpkg.bat'
self.vcpkgUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/vcpkg-win32.tar.gz?versionId=YZYkDejDRk7L_hrK_WVFthWvisAhbDzZ'
self.vcpkgHash = '3e0ff829a74956491d57666109b3e6b5ce4ed0735c24093884317102387b2cb1b2cd1ff38af9ed9173501f6e32ffa05cc6fe6d470b77a71ca1ffc3e0aa46ab9e'
self.hostTriplet = 'x64-windows'
elif 'Darwin' == system:
self.exe = os.path.join(self.path, 'vcpkg')
self.bootstrapCmd = 'bootstrap-vcpkg.sh'
self.vcpkgUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/vcpkg-osx.tar.gz?versionId=_fhqSxjfrtDJBvEsQ8L_ODcdUjlpX9cc'
self.vcpkgHash = '519d666d02ef22b87c793f016ca412e70f92e1d55953c8f9bd4ee40f6d9f78c1df01a6ee293907718f3bbf24075cc35492fb216326dfc50712a95858e9cbcb4d'
self.hostTriplet = 'x64-osx'
else:
self.exe = os.path.join(self.path, 'vcpkg')
self.bootstrapCmd = 'bootstrap-vcpkg.sh'
self.vcpkgUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/vcpkg-linux.tar.gz?versionId=97Nazh24etEVKWz33XwgLY0bvxEfZgMU'
self.vcpkgHash = '6a1ce47ef6621e699a4627e8821ad32528c82fce62a6939d35b205da2d299aaa405b5f392df4a9e5343dd6a296516e341105fbb2dd8b48864781d129d7fba10d'
self.hostTriplet = 'x64-linux'
@ -141,8 +144,14 @@ endif()
downloadVcpkg = True
if downloadVcpkg:
print("Fetching vcpkg from {} to {}".format(self.vcpkgUrl, self.path))
hifi_utils.downloadAndExtract(self.vcpkgUrl, self.path, self.vcpkgHash)
if "HIFI_VCPKG_BOOTSTRAP" in os.environ:
print("Cloning vcpkg from github to {}".format(self.path))
hifi_utils.executeSubprocess(['git', 'clone', 'git@github.com:microsoft/vcpkg.git', self.path])
print("Bootstrapping vcpkg")
hifi_utils.executeSubprocess([self.bootstrapCmd], folder=self.path)
else:
print("Fetching vcpkg from {} to {}".format(self.vcpkgUrl, self.path))
hifi_utils.downloadAndExtract(self.vcpkgUrl, self.path, self.vcpkgHash)
print("Replacing port files")
portsPath = os.path.join(self.path, 'ports')

File diff suppressed because it is too large Load diff

View file

@ -27,11 +27,15 @@ Rectangle {
HifiConstants { id: hifi }
readonly property real treeScale: 32768; // ~20 miles.. This is the number of meters of the 0.0 to 1.0 voxel universe
readonly property real halfTreeScale: treeScale / 2;
// This controls the LOD. Larger number will make smaller voxels visible at greater distance.
readonly property real defaultOctreeSizeScale: treeScale * 400.0
// This controls the LOD. Larger number will make smaller objects visible at greater distance.
readonly property real defaultMaxVisibilityDistance: 400.0
readonly property real unitElementMaxExtent: Math.sqrt(3.0) * 0.5
function visibilityDistanceToLODAngleDeg(visibilityDistance) {
var lodHalfAngle = Math.atan(unitElementMaxExtent / visibilityDistance);
var lodAngle = lodHalfAngle * 2.0;
return lodAngle * 180.0 / Math.PI;
}
Column {
anchors.margins: 10
@ -71,7 +75,7 @@ Rectangle {
id: adjustCheckbox
boxSize: 20
anchors.verticalCenter: parent.verticalCenter
onCheckedChanged: LODManager.setAutomaticLODAdjust(!checked);
onCheckedChanged: LODManager.setAutomaticLODAdjust(!adjustCheckbox.checked);
}
}
@ -89,10 +93,10 @@ Rectangle {
anchors.right: parent.right
minimumValue: 5
maximumValue: 2000
value: LODManager.getOctreeSizeScale() / treeScale
value: defaultMaxVisibilityDistance
tickmarksEnabled: false
onValueChanged: {
LODManager.setOctreeSizeScale(value * treeScale);
LODManager.lodAngleDeg = visibilityDistanceToLODAngleDeg(slider.value);
whatYouCanSeeLabel.text = LODManager.getLODFeedbackText()
}
}
@ -106,7 +110,7 @@ Rectangle {
colorScheme: root.colorScheme
height: 30
onClicked: {
slider.value = defaultOctreeSizeScale/treeScale
slider.value = defaultMaxVisibilityDistance
adjustCheckbox.checked = false
LODManager.setAutomaticLODAdjust(adjustCheckbox.checked);
}

View file

@ -292,7 +292,7 @@ static const uint32_t MAX_CONCURRENT_RESOURCE_DOWNLOADS = 4;
// For processing on QThreadPool, we target a number of threads after reserving some
// based on how many are being consumed by the application and the display plugin. However,
// we will never drop below the 'min' value
static const int MIN_PROCESSING_THREAD_POOL_SIZE = 1;
static const int MIN_PROCESSING_THREAD_POOL_SIZE = 2;
static const QString SNAPSHOT_EXTENSION = ".jpg";
static const QString JPG_EXTENSION = ".jpg";
@ -4418,8 +4418,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
} else if (isMeta) {
auto dialogsManager = DependencyManager::get<DialogsManager>();
dialogsManager->toggleAddressBar();
} else if (isShifted) {
Menu::getInstance()->triggerOption(MenuOption::LodTools);
}
break;
@ -6817,8 +6815,8 @@ void Application::updateRenderArgs(float deltaTime) {
_viewFrustum.setProjection(adjustedProjection);
_viewFrustum.calculate();
}
appRenderArgs._renderArgs = RenderArgs(_graphicsEngine.getGPUContext(), lodManager->getOctreeSizeScale(),
lodManager->getBoundaryLevelAdjust(), lodManager->getLODAngleHalfTan(), RenderArgs::DEFAULT_RENDER_MODE,
appRenderArgs._renderArgs = RenderArgs(_graphicsEngine.getGPUContext(), lodManager->getVisibilityDistance(),
lodManager->getBoundaryLevelAdjust(), lodManager->getLODHalfAngleTan(), RenderArgs::DEFAULT_RENDER_MODE,
RenderArgs::MONO, RenderArgs::DEFERRED, RenderArgs::RENDER_DEBUG_NONE);
appRenderArgs._renderArgs._scene = getMain3DScene();

View file

@ -12,7 +12,6 @@
#include "LODManager.h"
#include <SettingHandle.h>
#include <OctreeUtils.h>
#include <Util.h>
#include <shared/GlobalAppProperties.h>
@ -93,8 +92,7 @@ void LODManager::autoAdjustLOD(float realTimeDelta) {
return;
}
// Previous values for output
float oldOctreeSizeScale = getOctreeSizeScale();
// Previous value for output
float oldLODAngle = getLODAngleDeg();
// Target fps is slightly overshooted by 5hz
@ -165,7 +163,7 @@ void LODManager::autoAdjustLOD(float realTimeDelta) {
// And now add the output of the controller to the LODAngle where we will guarantee it is in the proper range
setLODAngleDeg(oldLODAngle + output);
if (oldOctreeSizeScale != _octreeSizeScale) {
if (oldLODAngle != getLODAngleDeg()) {
auto lodToolsDialog = DependencyManager::get<DialogsManager>()->getLodToolsDialog();
if (lodToolsDialog) {
lodToolsDialog->reloadSliders();
@ -173,21 +171,32 @@ void LODManager::autoAdjustLOD(float realTimeDelta) {
}
}
float LODManager::getLODAngleHalfTan() const {
return getPerspectiveAccuracyAngleTan(_octreeSizeScale, _boundaryLevelAdjust);
float LODManager::getLODHalfAngleTan() const {
return tan(_lodHalfAngle);
}
float LODManager::getLODAngle() const {
return 2.0f * atanf(getLODAngleHalfTan());
return 2.0f * _lodHalfAngle;
}
float LODManager::getLODAngleDeg() const {
return glm::degrees(getLODAngle());
}
float LODManager::getVisibilityDistance() const {
float systemDistance = getVisibilityDistanceFromHalfAngle(_lodHalfAngle);
// Maintain behavior with deprecated _boundaryLevelAdjust property
return systemDistance * powf(2.0f, _boundaryLevelAdjust);
}
void LODManager::setVisibilityDistance(float distance) {
// Maintain behavior with deprecated _boundaryLevelAdjust property
float userDistance = distance / powf(2.0f, _boundaryLevelAdjust);
_lodHalfAngle = getHalfAngleFromVisibilityDistance(userDistance);
}
void LODManager::setLODAngleDeg(float lodAngle) {
auto newSolidAngle = std::max(0.5f, std::min(lodAngle, 90.f));
auto halTan = glm::tan(glm::radians(newSolidAngle * 0.5f));
auto octreeSizeScale = TREE_SCALE * OCTREE_TO_MESH_RATIO / halTan;
setOctreeSizeScale(octreeSizeScale);
auto newLODAngleDeg = std::max(0.001f, std::min(lodAngle, 90.f));
auto newLODHalfAngle = glm::radians(newLODAngleDeg * 0.5f);
_lodHalfAngle = newLODHalfAngle;
}
void LODManager::setSmoothScale(float t) {
@ -267,7 +276,11 @@ bool LODManager::shouldRender(const RenderArgs* args, const AABox& bounds) {
};
void LODManager::setOctreeSizeScale(float sizeScale) {
_octreeSizeScale = sizeScale;
setVisibilityDistance(sizeScale / TREE_SCALE);
}
float LODManager::getOctreeSizeScale() const {
return getVisibilityDistance() * TREE_SCALE;
}
void LODManager::setBoundaryLevelAdjust(int boundaryLevelAdjust) {
@ -293,12 +306,14 @@ QString LODManager::getLODFeedbackText() {
} break;
}
// distance feedback
float octreeSizeScale = getOctreeSizeScale();
float relativeToDefault = octreeSizeScale / DEFAULT_OCTREE_SIZE_SCALE;
float visibilityDistance = getVisibilityDistance();
float relativeToDefault = visibilityDistance / DEFAULT_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT;
int relativeToTwentyTwenty = 20 / relativeToDefault;
QString result;
if (relativeToDefault > 1.01f) {
if (relativeToTwentyTwenty < 1) {
result = QString("%2 times further than average vision%3").arg(relativeToDefault, 0, 'f', 3).arg(granularityFeedback);
} else if (relativeToDefault > 1.01f) {
result = QString("20:%1 or %2 times further than average vision%3").arg(relativeToTwentyTwenty).arg(relativeToDefault, 0, 'f', 2).arg(granularityFeedback);
} else if (relativeToDefault > 0.99f) {
result = QString("20:20 or the default distance for average vision%1").arg(granularityFeedback);

View file

@ -17,6 +17,7 @@
#include <DependencyManager.h>
#include <NumericalConstants.h>
#include <OctreeConstants.h>
#include <OctreeUtils.h>
#include <PIDController.h>
#include <SimpleMovingAverage.h>
#include <render/Args.h>
@ -138,24 +139,28 @@ public:
/**jsdoc
* @function LODManager.setOctreeSizeScale
* @param {number} sizeScale
* @deprecated This function is deprecated and will be removed. Use the {@link LODManager.lodAngleDeg} property instead.
*/
Q_INVOKABLE void setOctreeSizeScale(float sizeScale);
/**jsdoc
* @function LODManager.getOctreeSizeScale
* @returns {number}
* @deprecated This function is deprecated and will be removed. Use the {@link LODManager.lodAngleDeg} property instead.
*/
Q_INVOKABLE float getOctreeSizeScale() const { return _octreeSizeScale; }
Q_INVOKABLE float getOctreeSizeScale() const;
/**jsdoc
* @function LODManager.setBoundaryLevelAdjust
* @param {number} boundaryLevelAdjust
* @deprecated This function is deprecated and will be removed.
*/
Q_INVOKABLE void setBoundaryLevelAdjust(int boundaryLevelAdjust);
/**jsdoc
* @function LODManager.getBoundaryLevelAdjust
* @returns {number}
* @deprecated This function is deprecated and will be removed.
*/
Q_INVOKABLE int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
@ -196,8 +201,10 @@ public:
float getLODAngleDeg() const;
void setLODAngleDeg(float lodAngle);
float getLODAngleHalfTan() const;
float getLODHalfAngleTan() const;
float getLODAngle() const;
float getVisibilityDistance() const;
void setVisibilityDistance(float distance);
float getPidKp() const;
float getPidKi() const;
@ -254,7 +261,7 @@ private:
float _desktopTargetFPS { LOD_OFFSET_FPS + LOD_DEFAULT_QUALITY_LEVEL * LOD_MAX_LIKELY_DESKTOP_FPS };
float _hmdTargetFPS { LOD_OFFSET_FPS + LOD_DEFAULT_QUALITY_LEVEL * LOD_MAX_LIKELY_HMD_FPS };
float _octreeSizeScale = DEFAULT_OCTREE_SIZE_SCALE;
float _lodHalfAngle = getHalfAngleFromVisibilityDistance(DEFAULT_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT);
int _boundaryLevelAdjust = 0;
glm::vec4 _pidCoefs{ 1.0f, 0.0f, 0.0f, 1.0f }; // Kp, Ki, Kd, Kv

View file

@ -258,6 +258,7 @@ void GraphicsEngine::render_performFrame() {
batch.setFramebuffer(finalFramebuffer);
batch.enableSkybox(true);
batch.enableStereo(isStereo);
batch.clearDepthStencilFramebuffer(1.0, 0);
batch.setViewportTransform({ 0, 0, finalFramebuffer->getSize() });
_splashScreen->render(batch, viewFrustum, renderArgs._renderMethod == RenderArgs::RenderMethod::FORWARD);
});

View file

@ -64,7 +64,7 @@ LodToolsDialog::LodToolsDialog(QWidget* parent) :
_lodSize->setTickPosition(QSlider::TicksBelow);
_lodSize->setFixedWidth(SLIDER_WIDTH);
_lodSize->setPageStep(PAGE_STEP_LOD_SIZE);
int sliderValue = lodManager->getOctreeSizeScale() / TREE_SCALE;
int sliderValue = lodManager->getVisibilityDistance();
_lodSize->setValue(sliderValue);
form->addRow("Level of Detail:", _lodSize);
connect(_lodSize,SIGNAL(valueChanged(int)),this,SLOT(sizeScaleValueChanged(int)));
@ -81,7 +81,7 @@ LodToolsDialog::LodToolsDialog(QWidget* parent) :
void LodToolsDialog::reloadSliders() {
auto lodManager = DependencyManager::get<LODManager>();
_lodSize->setValue(lodManager->getOctreeSizeScale() / TREE_SCALE);
_lodSize->setValue(lodManager->getVisibilityDistance());
_feedback->setText(lodManager->getLODFeedbackText());
}
@ -93,15 +93,14 @@ void LodToolsDialog::updateAutomaticLODAdjust() {
void LodToolsDialog::sizeScaleValueChanged(int value) {
auto lodManager = DependencyManager::get<LODManager>();
float realValue = value * TREE_SCALE;
lodManager->setOctreeSizeScale(realValue);
lodManager->setVisibilityDistance(value);
_feedback->setText(lodManager->getLODFeedbackText());
}
void LodToolsDialog::resetClicked(bool checked) {
int sliderValue = DEFAULT_OCTREE_SIZE_SCALE / TREE_SCALE;
int sliderValue = DEFAULT_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT;
_lodSize->setValue(sliderValue);
_manualLODAdjust->setChecked(false);
@ -124,8 +123,8 @@ void LodToolsDialog::closeEvent(QCloseEvent* event) {
lodManager->setAutomaticLODAdjust(true);
// if the user adjusted the LOD above "normal" then always revert back to default
if (lodManager->getOctreeSizeScale() > DEFAULT_OCTREE_SIZE_SCALE) {
lodManager->setOctreeSizeScale(DEFAULT_OCTREE_SIZE_SCALE);
if (lodManager->getVisibilityDistance() > DEFAULT_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT) {
lodManager->setVisibilityDistance(DEFAULT_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT);
}
#endif
}

View file

@ -43,6 +43,8 @@ set(src_files
src/LaunchInterface.h
src/CustomUI.h
src/CustomUI.m
src/NSTask+NSTaskExecveAdditions.h
src/NSTask+NSTaskExecveAdditions.m
src/main.mm
nib/Window.xib
nib/SplashScreen.xib

View file

@ -7,6 +7,7 @@
#import "ProcessScreen.h"
#import "ErrorViewController.h"
#import "Settings.h"
#import "NSTask+NSTaskExecveAdditions.h"
@interface Launcher ()
@ -456,8 +457,6 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
NSURL *url = [NSURL fileURLWithPath:[workspace fullPathForApplication:[[self getAppPath] stringByAppendingString:@"interface.app/Contents/MacOS/interface"]]];
NSError *error = nil;
NSString* contentPath = [[self getDownloadPathForContentAndScripts] stringByAppendingString:@"content"];
NSString* displayName = [ self displayName];
NSString* scriptsPath = [[self getAppPath] stringByAppendingString:@"interface.app/Contents/Resources/scripts/simplifiedUIBootstrapper.js"];
@ -484,13 +483,11 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
@"--no-updater",
@"--no-launcher", nil];
}
[workspace launchApplicationAtURL:url options:NSWorkspaceLaunchNewInstance configuration:[NSDictionary dictionaryWithObject:arguments forKey:NSWorkspaceLaunchConfigurationArguments] error:&error];
[NSTimer scheduledTimerWithTimeInterval: 3.0
target: self
selector: @selector(exitLauncher:)
userInfo:nil
repeats: NO];
NSTask *task = [[NSTask alloc] init];
task.launchPath = [url path];
task.arguments = arguments;
[task replaceThisProcess];
}
- (ProcessState) currentProccessState
@ -500,15 +497,20 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
- (void) callLaunchInterface:(NSTimer*) timer
{
NSWindow* mainWindow = [[[NSApplication sharedApplication] windows] objectAtIndex:0];
ProcessScreen* processScreen = [[ProcessScreen alloc] initWithNibName:@"ProcessScreen" bundle:nil];
[[[[NSApplication sharedApplication] windows] objectAtIndex:0] setContentViewController: processScreen];
[self launchInterface];
}
- (void) exitLauncher:(NSTimer*) timer
{
[NSApp terminate:self];
[mainWindow setContentViewController: processScreen];
@try
{
[self launchInterface];
}
@catch (NSException *exception)
{
NSLog(@"Caught exception: Name: %@, Reason: %@", exception.name, exception.reason);
ErrorViewController* errorViewController = [[ErrorViewController alloc] initWithNibName:@"ErrorScreen" bundle:nil];
[mainWindow setContentViewController: errorViewController];
}
}
@end

View file

@ -0,0 +1,9 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSTask (NSTaskExecveAdditions)
- (void) replaceThisProcess;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,73 @@
#import "NSTask+NSTaskExecveAdditions.h"
#import <libgen.h>
char **
toCArray(NSArray<NSString *> *array)
{
// Add one to count to accommodate the NULL that terminates the array.
char **cArray = (char **) calloc([array count] + 1, sizeof(char *));
if (cArray == NULL) {
NSException *exception = [NSException
exceptionWithName:@"MemoryException"
reason:@"malloc failed"
userInfo:nil];
@throw exception;
}
char *str;
for (int i = 0; i < [array count]; i++) {
str = (char *) [array[i] UTF8String];
if (str == NULL) {
NSException *exception = [NSException
exceptionWithName:@"NULLStringException"
reason:@"UTF8String was NULL"
userInfo:nil];
@throw exception;
}
if (asprintf(&cArray[i], "%s", str) == -1) {
for (int j = 0; j < i; j++) {
free(cArray[j]);
}
free(cArray);
NSException *exception = [NSException
exceptionWithName:@"MemoryException"
reason:@"malloc failed"
userInfo:nil];
@throw exception;
}
}
return cArray;
}
@implementation NSTask (NSTaskExecveAdditions)
- (void) replaceThisProcess {
char **args = toCArray([@[[self launchPath]] arrayByAddingObjectsFromArray:[self arguments]]);
NSMutableArray *env = [[NSMutableArray alloc] init];
NSDictionary* environvment = [[NSProcessInfo processInfo] environment];
for (NSString* key in environvment) {
NSString* environmentVariable = [[key stringByAppendingString:@"="] stringByAppendingString:environvment[key]];
[env addObject:environmentVariable];
}
char** envp = toCArray(env);
// `execve` replaces the current process with `path`.
// It will only return if it fails to replace the current process.
chdir(dirname(args[0]));
execve(args[0], (char * const *)args, envp);
// If we're here `execve` failed. :(
for (int i = 0; i < [[self arguments] count]; i++) {
free((void *) args[i]);
}
free((void *) args);
NSException *exception = [NSException
exceptionWithName:@"ExecveException"
reason:[NSString stringWithFormat:@"couldn't execve: %s", strerror(errno)]
userInfo:nil];
@throw exception;
}
@end

View file

@ -213,6 +213,12 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::
}
}
float AnimInverseKinematics::getInterpolationAlpha(float timer) {
float alpha = (JOINT_CHAIN_INTERP_TIME - timer) / JOINT_CHAIN_INTERP_TIME;
alpha = 1.0f - powf(2.0f, -10.0f * alpha);
return alpha;
}
void AnimInverseKinematics::solve(const AnimContext& context, const std::vector<IKTarget>& targets, float dt, JointChainInfoVec& jointChainInfoVec) {
// compute absolute poses that correspond to relative target poses
AnimPoseVec absolutePoses;
@ -227,6 +233,8 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector<
accumulator.clearAndClean();
}
std::map<int, int> targetToChainMap;
float maxError = 0.0f;
int numLoops = 0;
const int MAX_IK_LOOPS = 16;
@ -248,17 +256,13 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector<
break;
}
}
// on last iteration, interpolate jointChains, if necessary
if (numLoops == MAX_IK_LOOPS) {
for (size_t i = 0; i < _prevJointChainInfoVec.size(); i++) {
targetToChainMap.insert(std::pair<int, int>(_prevJointChainInfoVec[i].target.getIndex(), (int)i));
if (_prevJointChainInfoVec[i].timer > 0.0f) {
float alpha = (JOINT_CHAIN_INTERP_TIME - _prevJointChainInfoVec[i].timer) / JOINT_CHAIN_INTERP_TIME;
// ease in expo
alpha = 1.0f - powf(2.0f, -10.0f * alpha);
float alpha = getInterpolationAlpha(_prevJointChainInfoVec[i].timer);
size_t chainSize = std::min(_prevJointChainInfoVec[i].jointInfoVec.size(), jointChainInfoVec[i].jointInfoVec.size());
if (jointChainInfoVec[i].target.getType() != IKTarget::Type::Unknown) {
@ -336,22 +340,47 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector<
for (auto& target: targets) {
int tipIndex = target.getIndex();
int parentIndex = (tipIndex >= 0) ? _skeleton->getParentIndex(tipIndex) : -1;
int chainIndex = targetToChainMap[tipIndex];
bool needsInterpolation = _prevJointChainInfoVec[chainIndex].timer > 0.0f;
float alpha = needsInterpolation ? getInterpolationAlpha(_prevJointChainInfoVec[chainIndex].timer) : 0.0f;
// update rotationOnly targets that don't lie on the ik chain of other ik targets.
if (parentIndex != -1 && !_rotationAccumulators[tipIndex].isDirty() && target.getType() == IKTarget::Type::RotationOnly) {
const glm::quat& targetRotation = target.getRotation();
// compute tip's new parent-relative rotation
// Q = Qp * q --> q' = Qp^ * Q
glm::quat newRelativeRotation = glm::inverse(absolutePoses[parentIndex].rot()) * targetRotation;
RotationConstraint* constraint = getConstraint(tipIndex);
if (constraint) {
constraint->apply(newRelativeRotation);
// TODO: ATM the final rotation target just fails but we need to provide
// feedback to the IK system so that it can adjust the bones up the skeleton
// to help this rotation target get met.
if (parentIndex != -1 && !_rotationAccumulators[tipIndex].isDirty() &&
(target.getType() == IKTarget::Type::RotationOnly || target.getType() == IKTarget::Type::Unknown)) {
if (target.getType() == IKTarget::Type::RotationOnly) {
const glm::quat& targetRotation = target.getRotation();
// compute tip's new parent-relative rotation
// Q = Qp * q --> q' = Qp^ * Q
glm::quat newRelativeRotation = glm::inverse(absolutePoses[parentIndex].rot()) * targetRotation;
RotationConstraint* constraint = getConstraint(tipIndex);
if (constraint) {
constraint->apply(newRelativeRotation);
// TODO: ATM the final rotation target just fails but we need to provide
// feedback to the IK system so that it can adjust the bones up the skeleton
// to help this rotation target get met.
}
if (needsInterpolation) {
_relativePoses[tipIndex].rot() = safeMix(_relativePoses[tipIndex].rot(), newRelativeRotation, alpha);
} else {
_relativePoses[tipIndex].rot() = newRelativeRotation;
}
// Add last known rotations to interpolate from
if (_rotationOnlyIKRotations.find(tipIndex) == _rotationOnlyIKRotations.end()) {
_rotationOnlyIKRotations.insert(std::pair<int, glm::quat>(tipIndex, _relativePoses[tipIndex].rot()));
} else {
_rotationOnlyIKRotations[tipIndex] = _relativePoses[tipIndex].rot();
}
absolutePoses[tipIndex].rot() = targetRotation;
} else {
bool rotationSnapshotExist = _rotationOnlyIKRotations.find(tipIndex) != _rotationOnlyIKRotations.end();
if (needsInterpolation) {
if (rotationSnapshotExist) {
glm::quat lastKnownRotation = _rotationOnlyIKRotations[tipIndex];
_relativePoses[tipIndex].rot() = safeMix(_relativePoses[tipIndex].rot(), lastKnownRotation, (1 - alpha));
}
} else if (rotationSnapshotExist) {
_rotationOnlyIKRotations.erase(tipIndex);
}
}
_relativePoses[tipIndex].rot() = newRelativeRotation;
absolutePoses[tipIndex].rot() = targetRotation;
}
}
@ -928,6 +957,10 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
(jointChainInfoVec[i].target.getType() != _prevJointChainInfoVec[i].target.getType() ||
jointChainInfoVec[i].target.getPoleVectorEnabled() != _prevJointChainInfoVec[i].target.getPoleVectorEnabled())) {
_prevJointChainInfoVec[i].timer = JOINT_CHAIN_INTERP_TIME;
// Clear the rotations when the target is known
if (jointChainInfoVec[i].target.getType() != IKTarget::Type::Unknown) {
_rotationOnlyIKRotations.erase(jointChainInfoVec[i].target.getIndex());
}
}
}
}

View file

@ -148,6 +148,7 @@ protected:
void clearConstraints();
void initConstraints();
void initLimitCenterPoses();
float getInterpolationAlpha(float timer);
// no copies
AnimInverseKinematics(const AnimInverseKinematics&) = delete;
@ -181,6 +182,7 @@ protected:
AnimPoseVec _defaultRelativePoses; // poses of the relaxed state
AnimPoseVec _relativePoses; // current relative poses
AnimPoseVec _limitCenterPoses; // relative
std::map<int, glm::quat> _rotationOnlyIKRotations;
std::map<int, AnimPose> _secondaryTargetsInRigFrame;

View file

@ -57,14 +57,12 @@ const AnimPoseVec& AnimRandomSwitch::evaluate(const AnimVariantMap& animVars, co
lowerBound = upperBound;
}
if (abs(_randomSwitchEvaluationCount - context.getEvaluationCount()) > 1) {
_duringInterp = false;
switchRandomState(animVars, context, desiredState, _duringInterp);
switchRandomState(animVars, context, desiredState, false);
} else {
// firing a random switch, be sure that we aren't completing a previously triggered transition
if (currentStateHasPriority) {
if (desiredState->getID() != _currentState->getID()) {
_duringInterp = true;
switchRandomState(animVars, context, desiredState, _duringInterp);
switchRandomState(animVars, context, desiredState, true);
} else {
_duringInterp = false;
}
@ -79,8 +77,7 @@ const AnimPoseVec& AnimRandomSwitch::evaluate(const AnimVariantMap& animVars, co
// evaluate currentState transitions
auto transitionState = evaluateTransitions(animVars);
if (transitionState != _currentState) {
_duringInterp = true;
switchRandomState(animVars, context, transitionState, _duringInterp);
switchRandomState(animVars, context, transitionState, true);
_triggerTime = randFloatInRange(_triggerTimeMin, _triggerTimeMax);
_randomSwitchTime = randFloatInRange(_randomSwitchTimeMin, _randomSwitchTimeMax);
}
@ -172,6 +169,9 @@ void AnimRandomSwitch::switchRandomState(const AnimVariantMap& animVars, const A
_lastPlayedState = nextStateNode->getID();
if (shouldInterp) {
bool interpActive = _duringInterp;
_duringInterp = true;
const float FRAMES_PER_SECOND = 30.0f;
auto prevStateNode = _children[_currentState->getChildIndex()];
@ -195,13 +195,21 @@ void AnimRandomSwitch::switchRandomState(const AnimVariantMap& animVars, const A
}
_nextPoses = nextStateNode->evaluate(animVars, context, dt, triggers);
} else if (_interpType == InterpType::SnapshotPrev) {
// snapshot previoius pose
// snapshot previous pose
_prevPoses = _poses;
// no need to evaluate _nextPoses we will do it dynamically during the interp,
// however we need to set the current frame.
if (!desiredState->getResume()) {
nextStateNode->setCurrentFrame(desiredState->_interpTarget - duration);
}
} else if (_interpType == InterpType::EvaluateBoth) {
// need to set current frame in destination branch.
nextStateNode->setCurrentFrame(desiredState->_interpTarget - duration);
if (interpActive) {
// snapshot previous pose
_prevPoses = _poses;
_interpType = InterpType::SnapshotPrev;
}
} else {
assert(false);
}

View file

@ -128,6 +128,7 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, const AnimCon
auto prevStateNode = _children[_currentState->getChildIndex()];
auto nextStateNode = _children[desiredState->getChildIndex()];
bool interpActive = _duringInterp;
_duringInterp = true;
_alpha = 0.0f;
float duration = std::max(0.001f, animVars.lookup(desiredState->_interpDurationVar, desiredState->_interpDuration));
@ -146,11 +147,19 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, const AnimCon
nextStateNode->setCurrentFrame(desiredState->_interpTarget);
_nextPoses = nextStateNode->evaluate(animVars, context, dt, triggers);
} else if (_interpType == InterpType::SnapshotPrev) {
// snapshot previoius pose
// snapshot previous pose
_prevPoses = _poses;
// no need to evaluate _nextPoses we will do it dynamically during the interp,
// however we need to set the current frame.
nextStateNode->setCurrentFrame(desiredState->_interpTarget - duration);
} else if (_interpType == InterpType::EvaluateBoth) {
// need to set current frame in destination branch.
nextStateNode->setCurrentFrame(desiredState->_interpTarget - duration);
if (interpActive) {
// snapshot previous pose
_prevPoses = _poses;
_interpType = InterpType::SnapshotPrev;
}
} else {
assert(false);
}

View file

@ -2112,8 +2112,10 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
}
float easeOutInValue = _talkIdleInterpTime < 0.5f ? 4.0f * powf(_talkIdleInterpTime, 3.0f) : 4.0f * powf((_talkIdleInterpTime - 1.0f), 3.0f) + 1.0f;
_animVars.set("talkOverlayAlpha", easeOutInValue);
_animVars.set("idleOverlayAlpha", easeOutInValue); // backward compatibility for older anim graphs.
} else {
_animVars.set("talkOverlayAlpha", 1.0f);
_animVars.set("idleOverlayAlpha", 1.0f); // backward compatibility for older anim graphs.
}
} else {
if (_talkIdleInterpTime < 1.0f) {
@ -2124,8 +2126,10 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
float easeOutInValue = _talkIdleInterpTime < 0.5f ? 4.0f * powf(_talkIdleInterpTime, 3.0f) : 4.0f * powf((_talkIdleInterpTime - 1.0f), 3.0f) + 1.0f;
float talkAlpha = 1.0f - easeOutInValue;
_animVars.set("talkOverlayAlpha", talkAlpha);
_animVars.set("idleOverlayAlpha", talkAlpha); // backward compatibility for older anim graphs.
} else {
_animVars.set("talkOverlayAlpha", 0.0f);
_animVars.set("idleOverlayAlpha", 0.0f); // backward compatibility for older anim graphs.
}
}

View file

@ -78,7 +78,8 @@ public:
void setVsyncEnabled(bool vsyncEnabled) { _vsyncEnabled = vsyncEnabled; }
bool isVsyncEnabled() const { return _vsyncEnabled; }
// Three threads, one for rendering, one for texture transfers, one reserved for the GL driver
int getRequiredThreadCount() const override { return 3; }
// Drop to one reserved for better other-task performance in desktop
int getRequiredThreadCount() const override { return 1; }
virtual std::function<void(gpu::Batch&, const gpu::TexturePointer&)> getHUDOperator() override;
void copyTextureToQuickFramebuffer(NetworkTexturePointer source,

View file

@ -53,6 +53,8 @@ public:
void updateVisionSqueezeParameters(float visionSqueezeX, float visionSqueezeY, float visionSqueezeTransition,
int visionSqueezePerEye, float visionSqueezeGroundPlaneY,
float visionSqueezeSpotlightSize);
// Attempt to reserve two threads.
int getRequiredThreadCount() const override { return 2; }
signals:
void hmdMountedChanged();

View file

@ -33,8 +33,8 @@ using namespace gpu::gl;
#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F
#endif
bool GLTexelFormat::isCompressed() const {
switch (internalFormat) {
bool GLTexelFormat::isCompressed(GLenum format) {
switch (format) {
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
@ -92,6 +92,11 @@ bool GLTexelFormat::isCompressed() const {
}
}
bool GLTexelFormat::isCompressed() const {
return isCompressed(internalFormat);
}
GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
GLenum result = GL_RGBA8;
switch (dstFormat.getDimension()) {

View file

@ -14,13 +14,15 @@ namespace gpu { namespace gl {
class GLTexelFormat {
public:
GLenum internalFormat;
GLenum format;
GLenum type;
GLenum internalFormat{ GL_RGBA8 };
GLenum format{ GL_RGBA };
GLenum type{ GL_UNSIGNED_BYTE };
GLTexelFormat(GLenum glinternalFormat, GLenum glformat, GLenum gltype) : internalFormat(glinternalFormat), format(glformat), type(gltype) {}
GLTexelFormat(GLenum glinternalFormat) : internalFormat(glinternalFormat) {}
static bool isCompressed(GLenum glinternalFormat);
bool isCompressed() const;
static GLTexelFormat evalGLTexelFormat(const Element& dstFormat) {

View file

@ -33,6 +33,7 @@ class GL45Backend : public GLBackend {
friend class Context;
public:
static const GLint RESOURCE_TRANSFER_TEX_UNIT { 32 };
static GLint MAX_COMBINED_SHADER_STORAGE_BLOCKS;
static GLint MAX_UNIFORM_LOCATIONS;
#if GPU_BINDLESS_TEXTURES

View file

@ -226,81 +226,31 @@ void GL45Texture::generateMips() const {
Size GL45Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const {
Size amountCopied = sourceSize;
bool compressed = GLTexelFormat::isCompressed(internalFormat);
if (GL_TEXTURE_2D == _target) {
switch (internalFormat) {
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
case GL_COMPRESSED_RED_RGTC1:
case GL_COMPRESSED_RG_RGTC2:
case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
case GL_COMPRESSED_RGB8_ETC2:
case GL_COMPRESSED_SRGB8_ETC2:
case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
case GL_COMPRESSED_RGBA8_ETC2_EAC:
case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
case GL_COMPRESSED_R11_EAC:
case GL_COMPRESSED_SIGNED_R11_EAC:
case GL_COMPRESSED_RG11_EAC:
case GL_COMPRESSED_SIGNED_RG11_EAC:
glCompressedTextureSubImage2D(_id, mip, 0, yOffset, size.x, size.y, internalFormat,
static_cast<GLsizei>(sourceSize), sourcePointer);
break;
default:
glTextureSubImage2D(_id, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
break;
if (compressed) {
glCompressedTextureSubImage2D(_id, mip, 0, yOffset, size.x, size.y, internalFormat,
static_cast<GLsizei>(sourceSize), sourcePointer);
} else {
glTextureSubImage2D(_id, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
}
} else if (GL_TEXTURE_CUBE_MAP == _target) {
switch (internalFormat) {
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
case GL_COMPRESSED_RED_RGTC1:
case GL_COMPRESSED_RG_RGTC2:
case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
case GL_COMPRESSED_RGB8_ETC2:
case GL_COMPRESSED_SRGB8_ETC2:
case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
case GL_COMPRESSED_RGBA8_ETC2_EAC:
case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
case GL_COMPRESSED_R11_EAC:
case GL_COMPRESSED_SIGNED_R11_EAC:
case GL_COMPRESSED_RG11_EAC:
case GL_COMPRESSED_SIGNED_RG11_EAC:
#if AMD_CUBE_MAP_EXT_WORKAROUND
if (glCompressedTextureSubImage2DEXT) {
auto target = GLTexture::CUBE_FACE_LAYOUT[face];
glCompressedTextureSubImage2DEXT(_id, target, mip, 0, yOffset, size.x, size.y, internalFormat,
static_cast<GLsizei>(sourceSize), sourcePointer);
} else
#endif
{
glCompressedTextureSubImage3D(_id, mip, 0, yOffset, face, size.x, size.y, 1, internalFormat,
static_cast<GLsizei>(sourceSize), sourcePointer);
}
break;
default:
#if AMD_CUBE_MAP_EXT_WORKAROUND
if (glTextureSubImage2DEXT) {
auto target = GLTexture::CUBE_FACE_LAYOUT[face];
glTextureSubImage2DEXT(_id, target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
} else
#endif
{
glTextureSubImage3D(_id, mip, 0, yOffset, face, size.x, size.y, 1, format, type, sourcePointer);
}
break;
// DSA and cubemap functions are notoriously buggy. use the 4.1 compatible pathway
glActiveTexture(GL_TEXTURE0 + GL45Backend::RESOURCE_TRANSFER_TEX_UNIT);
glBindTexture(_target, _texture);
auto target = GLTexture::CUBE_FACE_LAYOUT[face];
if (compressed) {
glCompressedTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, internalFormat,
static_cast<GLsizei>(sourceSize), sourcePointer);
} else {
glTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer);
}
glBindTexture(_target, 0);
} else {
assert(false);
amountCopied = 0;
}
(void)CHECK_GL_ERROR();
return amountCopied;
}

View file

@ -334,12 +334,17 @@ void Socket::checkForReadyReadBackup() {
qCDebug(networking) << "Socket::checkForReadyReadyBackup() last sequence number"
<< (uint32_t) _lastReceivedSequenceNumber << "from" << _lastPacketSockAddr << "-"
<< _lastPacketSizeRead << "bytes";
#ifdef DEBUG_EVENT_QUEUE
qCDebug(networking) << "NodeList event queue size:" << ::hifi::qt::getEventQueueSize(thread());
#endif
// drop all of the pending datagrams on the floor
int droppedCount = 0;
while (_udpSocket.hasPendingDatagrams()) {
_udpSocket.readDatagram(nullptr, 0);
++droppedCount;
}
qCDebug(networking) << "Flushed" << droppedCount << "Packets";
}
}

View file

@ -21,8 +21,9 @@ const int TREE_SCALE = 32768; // ~20 miles.. This is the number of meters of the
const int HALF_TREE_SCALE = TREE_SCALE / 2;
// This controls the LOD. Larger number will make smaller voxels visible at greater distance.
const float MAX_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT = 400.0f; // max distance where a 1x1x1 cube is visible for 20:20 vision
const float DEFAULT_OCTREE_SIZE_SCALE = TREE_SCALE * MAX_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT;
const float DEFAULT_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT = 400.0f; // max distance where a 1x1x1 cube is visible for 20:20 vision
const float UNIT_ELEMENT_MAX_EXTENT = sqrtf(3.0f) / 2.0f; // A unit cube tilted on its edge will have its edge jutting out sqrt(3)/2 units from the center
const float DEFAULT_OCTREE_SIZE_SCALE = TREE_SCALE * DEFAULT_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT;
// Since entities like models live inside of octree cells, and they themselves can have very small mesh parts,
// we want to have some constant that controls have big a mesh part must be to render even if the octree cell itself

View file

@ -18,64 +18,31 @@
#include <AABox.h>
#include <AACube.h>
float calculateRenderAccuracy(const glm::vec3& position,
const AABox& bounds,
float octreeSizeScale,
int boundaryLevelAdjust) {
float largestDimension = bounds.getLargestDimension();
const float maxScale = (float)TREE_SCALE;
float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / OCTREE_TO_MESH_RATIO;
static std::once_flag once;
static QMap<float, float> shouldRenderTable;
std::call_once(once, [&] {
float SMALLEST_SCALE_IN_TABLE = 0.001f; // 1mm is plenty small
float scale = maxScale;
float factor = 1.0f;
while (scale > SMALLEST_SCALE_IN_TABLE) {
scale /= 2.0f;
factor /= 2.0f;
shouldRenderTable[scale] = factor;
}
});
float closestScale = maxScale;
float visibleDistanceAtClosestScale = visibleDistanceAtMaxScale;
QMap<float, float>::const_iterator lowerBound = shouldRenderTable.lowerBound(largestDimension);
if (lowerBound != shouldRenderTable.constEnd()) {
closestScale = lowerBound.key();
visibleDistanceAtClosestScale = visibleDistanceAtMaxScale * lowerBound.value();
}
if (closestScale < largestDimension) {
visibleDistanceAtClosestScale *= 2.0f;
}
// FIXME - for now, it's either visible or not visible. We want to adjust this to eventually return
// a floating point for objects that have small angular size to indicate that they may be rendered
// with lower preciscion
float distanceToCamera = glm::length(bounds.calcCenter() - position);
return (distanceToCamera <= visibleDistanceAtClosestScale) ? 1.0f : 0.0f;
float boundaryDistanceForRenderLevel(unsigned int renderLevel, float visibilityDistance) {
return visibilityDistance / powf(2.0f, renderLevel);
}
float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale) {
return voxelSizeScale / powf(2.0f, renderLevel);
float getPerspectiveAccuracyHalfAngleTan(float visibilityDistance, int boundaryLevelAdjust) {
float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, visibilityDistance);
return UNIT_ELEMENT_MAX_EXTENT / visibleDistanceAtMaxScale;
}
float getPerspectiveAccuracyAngleTan(float octreeSizeScale, int boundaryLevelAdjust) {
const float maxScale = (float)TREE_SCALE;
float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / OCTREE_TO_MESH_RATIO;
return (maxScale / visibleDistanceAtMaxScale);
float getPerspectiveAccuracyHalfAngle(float visibilityDistance, int boundaryLevelAdjust) {
return atan(getPerspectiveAccuracyHalfAngleTan(visibilityDistance, boundaryLevelAdjust));
}
float getPerspectiveAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust) {
return atan(getPerspectiveAccuracyAngleTan(octreeSizeScale, boundaryLevelAdjust));
float getVisibilityDistanceFromHalfAngle(float halfAngle) {
float halfAngleTan = tan(halfAngle);
return UNIT_ELEMENT_MAX_EXTENT / halfAngleTan;
}
float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust) {
float getHalfAngleFromVisibilityDistance(float visibilityDistance) {
float halfAngleTan = UNIT_ELEMENT_MAX_EXTENT / visibilityDistance;
return atan(halfAngleTan);
}
float getOrthographicAccuracySize(float visibilityDistance, int boundaryLevelAdjust) {
// Smallest visible element is 1cm
const float smallestSize = 0.01f;
return (smallestSize * MAX_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT) / boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale);
return (smallestSize * DEFAULT_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT) / boundaryDistanceForRenderLevel(boundaryLevelAdjust, visibilityDistance);
}

View file

@ -20,18 +20,13 @@ class AABox;
class AACube;
class QJsonDocument;
/// renderAccuracy represents a floating point "visibility" of an object based on it's view from the camera. At a simple
/// level it returns 0.0f for things that are so small for the current settings that they could not be visible.
float calculateRenderAccuracy(const glm::vec3& position,
const AABox& bounds,
float octreeSizeScale = DEFAULT_OCTREE_SIZE_SCALE,
int boundaryLevelAdjust = 0);
float boundaryDistanceForRenderLevel(unsigned int renderLevel, float visibilityDistance);
float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale);
float getPerspectiveAccuracyAngleTan(float octreeSizeScale, int boundaryLevelAdjust);
float getPerspectiveAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust);
float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust);
float getPerspectiveAccuracyHalfAngleTan(float visibilityDistance, int boundaryLevelAdjust);
float getPerspectiveAccuracyHalfAngle(float visibilityDistance, int boundaryLevelAdjust);
float getVisibilityDistanceFromHalfAngle(float halfAngle);
float getHalfAngleFromVisibilityDistance(float visibilityDistance);
float getOrthographicAccuracySize(float visibilityDistance, int boundaryLevelAdjust);
// MIN_ELEMENT_ANGULAR_DIAMETER = angular diameter of 1x1x1m cube at 400m = sqrt(3) / 400 = 0.0043301 radians ~= 0.25 degrees
const float MIN_ELEMENT_ANGULAR_DIAMETER = 0.0043301f; // radians

View file

@ -120,7 +120,7 @@ void DrawSceneOctree::run(const RenderContextPointer& renderContext, const ItemS
// Draw the LOD Reticle
{
float angle = glm::degrees(getPerspectiveAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust));
float angle = glm::degrees(getPerspectiveAccuracyHalfAngle(args->_sizeScale, args->_boundaryLevelAdjust));
Transform crosshairModel;
crosshairModel.setTranslation(glm::vec3(0.0, 0.0, -1000.0));
crosshairModel.setScale(1000.0f * tanf(glm::radians(angle))); // Scaling at the actual tan of the lod angle => Multiplied by TWO

View file

@ -30,34 +30,18 @@ Q_DECLARE_METATYPE(QByteArray*)
XMLHttpRequestClass::XMLHttpRequestClass(QScriptEngine* engine) :
_engine(engine),
_async(true),
_url(),
_method(""),
_responseType(""),
_request(),
_reply(NULL),
_sendData(NULL),
_rawResponseData(),
_responseData(""),
_onTimeout(QScriptValue::NullValue),
_onReadyStateChange(QScriptValue::NullValue),
_readyState(XMLHttpRequestClass::UNSENT),
_errorCode(QNetworkReply::NoError),
_timeout(0),
_timer(this),
_numRedirects(0) {
_timer(this) {
_request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
_timer.setSingleShot(true);
}
XMLHttpRequestClass::~XMLHttpRequestClass() {
if (_reply) { delete _reply; }
if (_sendData) { delete _sendData; }
if (_reply) { _reply->deleteLater(); }
}
QScriptValue XMLHttpRequestClass::constructor(QScriptContext* context, QScriptEngine* engine) {
return engine->newQObject(new XMLHttpRequestClass(engine));
return engine->newQObject(new XMLHttpRequestClass(engine), QScriptEngine::ScriptOwnership);
}
QScriptValue XMLHttpRequestClass::getStatus() const {
@ -169,13 +153,12 @@ void XMLHttpRequestClass::send() {
void XMLHttpRequestClass::send(const QScriptValue& data) {
if (_readyState == OPENED && !_reply) {
if (!data.isNull()) {
_sendData = new QBuffer(this);
if (data.isObject()) {
QByteArray ba = qscriptvalue_cast<QByteArray>(data);
_sendData->setData(ba);
_sendData = qscriptvalue_cast<QByteArray>(data);
} else {
_sendData->setData(data.toString().toUtf8());
_sendData = data.toString().toUtf8();
}
}
@ -237,6 +220,10 @@ void XMLHttpRequestClass::requestFinished() {
setReadyState(DONE);
emit requestComplete();
disconnectFromReply(_reply);
_reply->deleteLater();
_reply = nullptr;
}
void XMLHttpRequestClass::abortRequest() {
@ -246,7 +233,7 @@ void XMLHttpRequestClass::abortRequest() {
disconnectFromReply(_reply);
_reply->abort();
_reply->deleteLater();
_reply = NULL;
_reply = nullptr;
}
}

View file

@ -98,23 +98,23 @@ private:
void disconnectFromReply(QNetworkReply* reply);
void abortRequest();
QScriptEngine* _engine;
bool _async;
QScriptEngine* _engine { nullptr };
bool _async { true };
QUrl _url;
QString _method;
QString _responseType;
QNetworkRequest _request;
QNetworkReply* _reply;
QBuffer* _sendData;
QNetworkReply* _reply { nullptr };
QByteArray _sendData;
QByteArray _rawResponseData;
QScriptValue _responseData;
QScriptValue _onTimeout;
QScriptValue _onReadyStateChange;
ReadyState _readyState;
QNetworkReply::NetworkError _errorCode;
int _timeout;
QScriptValue _onTimeout { QScriptValue::NullValue };
QScriptValue _onReadyStateChange { QScriptValue::NullValue };
ReadyState _readyState { XMLHttpRequestClass::UNSENT };
QNetworkReply::NetworkError _errorCode { QNetworkReply::NoError };
int _timeout { 0 };
QTimer _timer;
int _numRedirects;
int _numRedirects { 0 };
private slots:
void requestFinished();

View file

@ -39,6 +39,10 @@ Q_LOGGING_CATEGORY(trace_baker, "trace.baker")
#endif
static bool tracingEnabled() {
if (!DependencyManager::isSet<tracing::Tracer>()) {
return false;
}
// Cheers, love! The cavalry's here!
auto tracer = DependencyManager::get<tracing::Tracer>();
return (tracer && tracer->isEnabled());

View file

@ -12,10 +12,10 @@
#include <QtCore/QObject>
#ifdef WIN32
#if defined(Q_OS_WIN) || defined(Q_OS_LINUX)
// Enable event queue debugging
#define DEBUG_EVENT_QUEUE
#endif // WIN32
#endif
namespace hifi { namespace qt {
void addBlockingForbiddenThread(const QString& name, QThread* thread = nullptr);

View file

@ -80,7 +80,7 @@ Item {
valueVar: LODManager["lodAngleDeg"]
valueVarSetter: (function (v) { LODManager["lodAngleDeg"] = v })
max: 90.0
min: 0.5
min: 0.01
integral: false
anchors.left: parent.left
@ -239,6 +239,7 @@ Item {
object: LODManager
valueScale: 1.0
valueUnit: "deg"
valueNumDigits: 2
plots: [
{
prop: "lodAngleDeg",

View file

@ -39,6 +39,10 @@ module.exports = {
if (error) {
response = { statusCode: httpRequest.status };
}
// Break circular reference to httpRequest so the engine can garbage collect it.
httpRequest.onreadystatechange = null;
callback(error, response, optionalCallbackParameter);
}
};

View file

@ -476,7 +476,7 @@ function maybeUpdateOutputDeviceMutedOverlay() {
var oldAutomaticLODAdjust;
var oldLODAngleDeg;
var SIMPLIFIED_UI_AUTO_LOD_ADJUST = false;
var SIMPLIFIED_UI_LOD_ANGLE_DEG = 0.5;
var SIMPLIFIED_UI_LOD_ANGLE_DEG = 0.248;
function modifyLODSettings() {
oldAutomaticLODAdjust = LODManager.automaticLODAdjust;
oldLODAngleDeg = LODManager.lodAngleDeg;

View file

@ -355,7 +355,7 @@ CameraManager = function() {
return;
}
if (event.isRightButton || (event.isLeftButton && event.isControl && !event.isShifted)) {
if (event.isRightButton || (event.isLeftButton && event.isAlt && !event.isShifted)) {
that.mode = MODE_ORBIT;
} else if (event.isMiddleButton || (event.isLeftButton && event.isControl && event.isShifted)) {
that.mode = MODE_PAN;

View file

@ -30,6 +30,8 @@ source_group("UI Files" FILES ${QT_UI_FILES})
# have qt5 wrap them and generate the appropriate header files
qt5_wrap_ui(QT_UI_HEADERS "${QT_UI_FILES}")
setup_memory_debugger()
# add them to the nitpick source files
set(NITPICK_SRCS ${NITPICK_SRCS} "${QT_UI_HEADERS}" "${QT_RESOURCES}")

View file

@ -24,12 +24,23 @@ static const QString CLI_OUTPUT_PARAMETER = "o";
static const QString CLI_TYPE_PARAMETER = "t";
static const QString CLI_DISABLE_TEXTURE_COMPRESSION_PARAMETER = "disable-texture-compression";
QUrl OvenCLIApplication::_inputUrlParameter;
QUrl OvenCLIApplication::_outputUrlParameter;
QString OvenCLIApplication::_typeParameter;
OvenCLIApplication::OvenCLIApplication(int argc, char* argv[]) :
QCoreApplication(argc, argv)
{
BakerCLI* cli = new BakerCLI(this);
QMetaObject::invokeMethod(cli, "bakeFile", Qt::QueuedConnection, Q_ARG(QUrl, _inputUrlParameter),
Q_ARG(QString, _outputUrlParameter.toString()), Q_ARG(QString, _typeParameter));
}
void OvenCLIApplication::parseCommandLine(int argc, char* argv[]) {
// parse the command line parameters
QCommandLineParser parser;
parser.setApplicationDescription("High Fidelity Oven");
parser.addOptions({
{ CLI_INPUT_PARAMETER, "Path to file that you would like to bake.", "input" },
{ CLI_OUTPUT_PARAMETER, "Path to folder that will be used as output.", "output" },
@ -37,25 +48,45 @@ OvenCLIApplication::OvenCLIApplication(int argc, char* argv[]) :
{ CLI_DISABLE_TEXTURE_COMPRESSION_PARAMETER, "Disable texture compression." }
});
parser.addHelpOption();
parser.process(*this);
auto versionOption = parser.addVersionOption();
auto helpOption = parser.addHelpOption();
if (parser.isSet(CLI_INPUT_PARAMETER) && parser.isSet(CLI_OUTPUT_PARAMETER)) {
BakerCLI* cli = new BakerCLI(this);
QUrl inputUrl(QDir::fromNativeSeparators(parser.value(CLI_INPUT_PARAMETER)));
QUrl outputUrl(QDir::fromNativeSeparators(parser.value(CLI_OUTPUT_PARAMETER)));
QString type = parser.isSet(CLI_TYPE_PARAMETER) ? parser.value(CLI_TYPE_PARAMETER) : QString::null;
if (parser.isSet(CLI_DISABLE_TEXTURE_COMPRESSION_PARAMETER)) {
qDebug() << "Disabling texture compression";
TextureBaker::setCompressionEnabled(false);
}
QMetaObject::invokeMethod(cli, "bakeFile", Qt::QueuedConnection, Q_ARG(QUrl, inputUrl),
Q_ARG(QString, outputUrl.toString()), Q_ARG(QString, type));
} else {
parser.showHelp();
QCoreApplication::quit();
QStringList arguments;
for (int i = 0; i < argc; ++i) {
arguments << argv[i];
}
if (!parser.parse(arguments)) {
std::cout << parser.errorText().toStdString() << std::endl; // Avoid Qt log spam
QCoreApplication mockApp(argc, argv); // required for call to showHelp()
parser.showHelp();
Q_UNREACHABLE();
}
if (parser.isSet(versionOption)) {
parser.showVersion();
Q_UNREACHABLE();
}
if (parser.isSet(helpOption)) {
QCoreApplication mockApp(argc, argv); // required for call to showHelp()
parser.showHelp();
Q_UNREACHABLE();
}
if (!parser.isSet(CLI_INPUT_PARAMETER) || !parser.isSet(CLI_OUTPUT_PARAMETER)) {
std::cout << "Error: Input and Output not set" << std::endl; // Avoid Qt log spam
QCoreApplication mockApp(argc, argv); // required for call to showHelp()
parser.showHelp();
Q_UNREACHABLE();
}
_inputUrlParameter = QDir::fromNativeSeparators(parser.value(CLI_INPUT_PARAMETER));
_outputUrlParameter = QDir::fromNativeSeparators(parser.value(CLI_OUTPUT_PARAMETER));
_typeParameter = parser.isSet(CLI_TYPE_PARAMETER) ? parser.value(CLI_TYPE_PARAMETER) : QString::null;
if (parser.isSet(CLI_DISABLE_TEXTURE_COMPRESSION_PARAMETER)) {
qDebug() << "Disabling texture compression";
TextureBaker::setCompressionEnabled(false);
}
}

View file

@ -21,7 +21,14 @@ class OvenCLIApplication : public QCoreApplication, public Oven {
public:
OvenCLIApplication(int argc, char* argv[]);
static void parseCommandLine(int argc, char* argv[]);
static OvenCLIApplication* instance() { return dynamic_cast<OvenCLIApplication*>(QCoreApplication::instance()); }
private:
static QUrl _inputUrlParameter;
static QUrl _outputUrlParameter;
static QString _typeParameter;
};
#endif // hifi_OvenCLIApplication_h

View file

@ -18,14 +18,19 @@
int main (int argc, char** argv) {
setupHifiApplication("Oven");
// init the settings interface so we can save and load settings
Setting::init();
// figure out if we're launching our GUI application or just the simple command line interface
if (argc > 1) {
OvenCLIApplication::parseCommandLine(argc, argv);
// init the settings interface so we can save and load settings
Setting::init();
OvenCLIApplication app { argc, argv };
return app.exec();
} else {
// init the settings interface so we can save and load settings
Setting::init();
OvenGUIApplication app { argc, argv };
return app.exec();
}