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

This commit is contained in:
Sam Gateau 2019-08-05 09:49:14 -07:00
commit d55f143300
59 changed files with 1150 additions and 393 deletions

View file

@ -1659,7 +1659,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
static const QString TESTER = "HIFI_TESTER";
auto gpuIdent = GPUIdent::getInstance();
auto glContextData = getGLContextData();
auto glContextData = gl::ContextInfo::get();
QJsonObject properties = {
{ "version", applicationVersion() },
{ "tester", QProcessEnvironment::systemEnvironment().contains(TESTER) || isTester },
@ -1676,11 +1676,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
{ "gpu_name", gpuIdent->getName() },
{ "gpu_driver", gpuIdent->getDriver() },
{ "gpu_memory", static_cast<qint64>(gpuIdent->getMemory()) },
{ "gl_version_int", glVersionToInteger(glContextData.value("version").toString()) },
{ "gl_version", glContextData["version"] },
{ "gl_vender", glContextData["vendor"] },
{ "gl_sl_version", glContextData["sl_version"] },
{ "gl_renderer", glContextData["renderer"] },
{ "gl_version_int", glVersionToInteger(glContextData.version.c_str()) },
{ "gl_version", glContextData.version.c_str() },
{ "gl_vender", glContextData.vendor.c_str() },
{ "gl_sl_version", glContextData.shadingLanguageVersion.c_str() },
{ "gl_renderer", glContextData.renderer.c_str() },
{ "ideal_thread_count", QThread::idealThreadCount() }
};
auto macVersion = QSysInfo::macVersion();
@ -2282,8 +2282,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
properties["active_display_plugin"] = getActiveDisplayPlugin()->getName();
properties["using_hmd"] = isHMDMode();
auto glInfo = getGLContextData();
properties["gl_info"] = glInfo;
auto contextInfo = gl::ContextInfo::get();
properties["gl_info"] = QJsonObject{
{ "version", contextInfo.version.c_str() },
{ "sl_version", contextInfo.shadingLanguageVersion.c_str() },
{ "vendor", contextInfo.vendor.c_str() },
{ "renderer", contextInfo.renderer.c_str() },
};
properties["gpu_used_memory"] = (int)BYTES_TO_MB(gpu::Context::getUsedGPUMemSize());
properties["gpu_free_memory"] = (int)BYTES_TO_MB(gpu::Context::getFreeGPUMemSize());
properties["gpu_frame_time"] = (float)(qApp->getGPUContext()->getFrameTimerGPUAverage());
@ -2996,6 +3001,9 @@ void Application::initializeGL() {
qCWarning(interfaceapp, "Unable to make window context current");
}
// Populate the global OpenGL context based on the information for the primary window GL context
gl::ContextInfo::get(true);
#if !defined(DISABLE_QML)
QStringList chromiumFlags;
// HACK: re-expose mic and camera to prevent crash on domain-change in chromium's media::FakeAudioInputStream::ReadAudioFromSource()

View file

@ -213,6 +213,11 @@ void Audio::setPTTHMD(bool enabled) {
}
void Audio::saveData() {
_avatarGainSetting.set(getAvatarGain());
_injectorGainSetting.set(getInjectorGain());
_localInjectorGainSetting.set(getLocalInjectorGain());
_systemInjectorGainSetting.set(getSystemInjectorGain());
_mutedDesktopSetting.set(getMutedDesktop());
_mutedHMDSetting.set(getMutedHMD());
_pttDesktopSetting.set(getPTTDesktop());
@ -220,6 +225,11 @@ void Audio::saveData() {
}
void Audio::loadData() {
setAvatarGain(_avatarGainSetting.get());
setInjectorGain(_injectorGainSetting.get());
setLocalInjectorGain(_localInjectorGainSetting.get());
setSystemInjectorGain(_systemInjectorGainSetting.get());
setMutedDesktop(_mutedDesktopSetting.get());
setMutedHMD(_mutedHMDSetting.get());
setPTTDesktop(_pttDesktopSetting.get());

View file

@ -521,6 +521,10 @@ private:
bool _settingsLoaded { false };
float _inputVolume { 1.0f };
float _inputLevel { 0.0f };
Setting::Handle<float> _avatarGainSetting { QStringList { Audio::AUDIO, "AvatarGain" }, 0.0f };
Setting::Handle<float> _injectorGainSetting { QStringList { Audio::AUDIO, "InjectorGain" }, 0.0f };
Setting::Handle<float> _localInjectorGainSetting { QStringList { Audio::AUDIO, "LocalInjectorGain" }, 0.0f };
Setting::Handle<float> _systemInjectorGainSetting { QStringList { Audio::AUDIO, "SystemInjectorGain" }, 0.0f };
float _localInjectorGain { 0.0f }; // in dB
float _systemInjectorGain { 0.0f }; // in dB
float _pttOutputGainDesktop { 0.0f }; // in dB

View file

@ -9,18 +9,10 @@
#include "QmlOverlay.h"
#include <QQuickItem>
#include <QThread>
#include <DependencyManager.h>
#include <GeometryCache.h>
#include <GLMHelpers.h>
#include <OffscreenUi.h>
#include <RegisteredMetaTypes.h>
#include <SharedUtil.h>
#include <TextureCache.h>
#include <ViewFrustum.h>
#include "Application.h"
#include "text/FontFamilies.h"
QmlOverlay::QmlOverlay(const QUrl& url) {
buildQmlElement(url);

View file

@ -9,10 +9,6 @@
#ifndef hifi_QmlOverlay_h
#define hifi_QmlOverlay_h
#include <QString>
#include <memory>
#include <SharedUtil.h>
#include "Overlay2D.h"
class QQuickItem;
@ -33,7 +29,7 @@ private:
Q_INVOKABLE void buildQmlElement(const QUrl& url);
protected:
QQuickItem* _qmlElement{ nullptr };
QQuickItem* _qmlElement { nullptr };
};
#endif // hifi_QmlOverlay_h

View file

@ -11,18 +11,9 @@
#include "TextOverlay.h"
#include <QQuickItem>
#include <QFontMetrics>
#include <DependencyManager.h>
#include <GeometryCache.h>
#include <GLMHelpers.h>
#include <OffscreenUi.h>
#include <RegisteredMetaTypes.h>
#include <SharedUtil.h>
#include <TextureCache.h>
#include <ViewFrustum.h>
#include "Application.h"
#include "text/FontFamilies.h"
#include "FontFamilies.h"
QString const TextOverlay::TYPE = "text";
QUrl const TextOverlay::URL(QString("hifi/overlays/TextOverlay.qml"));
@ -44,7 +35,7 @@ QSizeF TextOverlay::textSize(const QString& text) const {
++lines;
}
}
QFont font(SANS_FONT_FAMILY);
QFont font(ROBOTO_FONT_FAMILY);
font.setPixelSize(18);
QFontMetrics fm(font);
QSizeF result = QSizeF(fm.width(text), 18 * lines);

View file

@ -20,6 +20,8 @@ set(src_files
src/DownloadInterface.m
src/DownloadDomainContent.h
src/DownloadDomainContent.m
src/DownloadLauncher.h
src/DownloadLauncher.m
src/DownloadScripts.h
src/DownloadScripts.m
src/CredentialsRequest.h
@ -34,6 +36,8 @@ set(src_files
src/Interface.m
src/ErrorViewController.h
src/ErrorViewController.m
src/LauncherCommandlineArgs.h
src/LauncherCommandlineArgs.m
src/Settings.h
src/Settings.m
src/LaunchInterface.h
@ -47,6 +51,9 @@ set(src_files
nib/ProcessScreen.xib
nib/DisplayNameScreen.xib)
set(updater_src_files
src/updater/main.m)
set(APP_NAME "HQ Launcher")
set(CMAKE_C_FLAGS "-x objective-c")
@ -72,6 +79,7 @@ endfunction()
set_packaging_parameters()
add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${src_files})
add_executable("updater" ${updater_src_files})
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${APP_NAME}
MACOSX_BUNDLE_BUNDLE_NAME ${APP_NAME})
set_from_env(LAUNCHER_HMAC_SECRET LAUNCHER_HMAC_SECRET "")
@ -102,6 +110,10 @@ add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/images "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${APP_NAME}.app/Contents/Resources/")
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND updater
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/updater" "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${APP_NAME}.app/Contents/Resources/")
install(
TARGETS HQLauncher
BUNDLE DESTINATION "."

View file

@ -0,0 +1,8 @@
#import <Foundation/Foundation.h>
@interface DownloadLauncher : NSObject<NSURLSessionDataDelegate, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLDownloadDelegate> {
}
- (void) downloadLauncher:(NSString*) launcherUrl;
@end

View file

@ -0,0 +1,68 @@
#import "DownloadLauncher.h"
#import "Launcher.h"
@implementation DownloadLauncher
- (void) downloadLauncher:(NSString*)launcherUrl {
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:launcherUrl]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
NSURLSessionDownloadTask *downloadTask = [defaultSession downloadTaskWithRequest:request];
[downloadTask resume];
}
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
CGFloat prog = (float)totalBytesWritten/totalBytesExpectedToWrite;
NSLog(@"Launcher downloaded %f", (100.0*prog));
}
-(void)URLSession:(NSURLSession*)session downloadTask:(NSURLSessionDownloadTask*)downloadTask didFinishDownloadingToURL:(NSURL*)location {
NSLog(@"Did finish downloading to url");
NSError* error = nil;
NSFileManager* fileManager = [NSFileManager defaultManager];
NSString* destinationFileName = downloadTask.originalRequest.URL.lastPathComponent;
NSString* finalFilePath = [[[Launcher sharedLauncher] getDownloadPathForContentAndScripts] stringByAppendingPathComponent:destinationFileName];
NSURL *destinationURL = [NSURL URLWithString: [finalFilePath stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]] relativeToURL: [NSURL URLWithString:@"file://"]];
NSLog(@"desintation %@", destinationURL);
if([fileManager fileExistsAtPath:[destinationURL path]]) {
[fileManager removeItemAtURL:destinationURL error:nil];
}
NSLog(@"location: %@", location.path);
NSLog(@"destination: %@", destinationURL);
BOOL success = [fileManager moveItemAtURL:location toURL:destinationURL error:&error];
NSLog(success ? @"TRUE" : @"FALSE");
Launcher* sharedLauncher = [Launcher sharedLauncher];
if (error) {
NSLog(@"Download Launcher: failed to move file to destintation -> error: %@", error);
[sharedLauncher displayErrorPage];
return;
}
NSLog(@"extracting Launcher file");
BOOL extractionSuccessful = [sharedLauncher extractZipFileAtDestination:[sharedLauncher getDownloadPathForContentAndScripts] :[[sharedLauncher getDownloadPathForContentAndScripts] stringByAppendingString:@"HQ Launcher.zip"]];
if (!extractionSuccessful) {
[sharedLauncher displayErrorPage];
return;
}
NSLog(@"finished extracting Launcher file");
[[Launcher sharedLauncher] runAutoupdater];
}
- (void)URLSession:(NSURLSession*)session task:(NSURLSessionTask*)task didCompleteWithError:(NSError*)error {
NSLog(@"completed; error: %@", error);
if (error) {
[[Launcher sharedLauncher] displayErrorPage];
}
}
@end

View file

@ -9,8 +9,8 @@
NSInteger currentVersion;
@try {
NSString* interfaceAppPath = [[Launcher.sharedLauncher getAppPath] stringByAppendingString:@"interface.app"];
NSError * error = nil;
Interface * interface = [[Interface alloc] initWith:interfaceAppPath];
NSError* error = nil;
Interface* interface = [[Interface alloc] initWith:interfaceAppPath];
currentVersion = [interface getVersion:&error];
if (currentVersion == 0 && error != nil) {
NSLog(@"can't get version from interface, falling back to settings: %@", error);
@ -24,17 +24,17 @@
}
- (void) requestLatestBuildInfo {
NSMutableURLRequest *request = [NSMutableURLRequest new];
NSMutableURLRequest* request = [NSMutableURLRequest new];
[request setURL:[NSURL URLWithString:@"https://thunder.highfidelity.com/builds/api/tags/latest?format=json"]];
[request setHTTPMethod:@"GET"];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
// We're using an ephermeral session here to ensure the tags api response is never cached.
NSURLSession * session = [NSURLSession sessionWithConfiguration:NSURLSessionConfiguration.ephemeralSessionConfiguration];
NSURLSession* session = [NSURLSession sessionWithConfiguration:NSURLSessionConfiguration.ephemeralSessionConfiguration];
NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(@"Latest Build Request error: %@", error);
NSLog(@"Latest Build Request Data: %@", data);
NSHTTPURLResponse *ne = (NSHTTPURLResponse *)response;
NSHTTPURLResponse* ne = (NSHTTPURLResponse *)response;
NSLog(@"Latest Build Request Response: %ld", [ne statusCode]);
Launcher* sharedLauncher = [Launcher sharedLauncher];
@ -47,9 +47,9 @@
NSMutableData* webData = [NSMutableData data];
[webData appendData:data];
NSString* jsonString = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[data length] encoding:NSUTF8StringEncoding];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSLog(@"Latest Build Request -> json string: %@", jsonString);
NSError *jsonError = nil;
NSError* jsonError = nil;
id json = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&jsonError];
if (jsonError) {
@ -57,10 +57,12 @@
}
NSFileManager* fileManager = [NSFileManager defaultManager];
NSArray *values = [json valueForKey:@"results"];
NSDictionary *value = [values objectAtIndex:0];
NSArray* values = [json valueForKey:@"results"];
NSDictionary* launcherValues = [json valueForKey:@"launcher"];
NSDictionary* value = [values objectAtIndex:0];
NSString* launcherVersion = [launcherValues valueForKey:@"version"];
NSString* launcherUrl = [[launcherValues valueForKey:@"mac"] valueForKey:@"url"];
NSString* buildNumber = [value valueForKey:@"latest_version"];
NSDictionary* installers = [value objectForKey:@"installers"];
NSDictionary* macInstallerObject = [installers objectForKey:@"mac"];
@ -71,16 +73,22 @@
dispatch_async(dispatch_get_main_queue(), ^{
NSInteger currentVersion = [self getCurrentVersion];
NSInteger currentLauncherVersion = atoi(LAUNCHER_BUILD_VERSION);
NSLog(@"Latest Build Request -> current launcher version %ld", currentLauncherVersion);
NSLog(@"Latest Build Request -> latest launcher version %ld", launcherVersion.integerValue);
NSLog(@"Latest Build Request -> launcher url %@", launcherUrl);
NSLog(@"Latest Build Request -> does build directory exist: %@", appDirectoryExist ? @"TRUE" : @"FALSE");
NSLog(@"Latest Build Request -> current version: %ld", currentVersion);
NSLog(@"Latest Build Request -> latest version: %ld", buildNumber.integerValue);
NSLog(@"Latest Build Request -> mac url: %@", macInstallerUrl);
BOOL latestVersionAvailable = (currentVersion != buildNumber.integerValue);
BOOL latestLauncherVersionAvailable = (currentLauncherVersion != launcherVersion.integerValue);
[[Settings sharedSettings] buildVersion:buildNumber.integerValue];
BOOL shouldDownloadInterface = (latestVersionAvailable || !appDirectoryExist);
NSLog(@"Latest Build Request -> SHOULD DOWNLOAD: %@", shouldDownloadInterface ? @"TRUE" : @"FALSE");
[sharedLauncher shouldDownloadLatestBuild:shouldDownloadInterface :macInstallerUrl];
[sharedLauncher shouldDownloadLatestBuild:shouldDownloadInterface :macInstallerUrl
:latestLauncherVersionAvailable :launcherUrl];
});
}];

View file

@ -2,6 +2,7 @@
#import "DownloadInterface.h"
#import "CredentialsRequest.h"
#import "DownloadDomainContent.h"
#import "DownloadLauncher.h"
#import "LatestBuildRequest.h"
#import "OrganizationRequest.h"
#import "DownloadScripts.h"
@ -44,6 +45,7 @@ struct LatestBuildInfo {
@property (nonatomic, retain) DownloadInterface* downloadInterface;
@property (nonatomic, retain) CredentialsRequest* credentialsRequest;
@property (nonatomic, retain) DownloadDomainContent* downloadDomainContent;
@property (nonatomic, retain) DownloadLauncher* downloadLauncher;
@property (nonatomic, retain) DownloadScripts* downloadScripts;
@property (nonatomic, retain) LatestBuildRequest* latestBuildRequest;
@property (nonatomic, retain) OrganizationRequest* organizationRequest;
@ -74,11 +76,12 @@ struct LatestBuildInfo {
- (void) showLoginScreen;
- (void) restart;
- (NSString*) getLauncherPath;
- (void) runAutoupdater;
- (ProcessState) currentProccessState;
- (void) setCurrentProcessState:(ProcessState) aProcessState;
- (void) setLoginErrorState:(LoginError) aLoginError;
- (LoginError) getLoginErrorState;
- (void) shouldDownloadLatestBuild:(BOOL) shouldDownload :(NSString*) downloadUrl;
- (void) shouldDownloadLatestBuild:(BOOL) shouldDownload :(NSString*) downloadUrl :(BOOL) newLauncherAvailable :(NSString*) launcherUrl;
- (void) interfaceFinishedDownloading;
- (NSString*) getDownloadPathForContentAndScripts;
- (void) launchInterface;

View file

@ -3,6 +3,7 @@
#import "SplashScreen.h"
#import "LoginScreen.h"
#import "DisplayNameScreen.h"
#import "LauncherCommandlineArgs.h"
#import "ProcessScreen.h"
#import "ErrorViewController.h"
#import "Settings.h"
@ -32,6 +33,7 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
self.username = [[NSString alloc] initWithString:@"Default Property Value"];
self.downloadInterface = [DownloadInterface alloc];
self.downloadDomainContent = [DownloadDomainContent alloc];
self.downloadLauncher = [DownloadLauncher alloc];
self.credentialsRequest = [CredentialsRequest alloc];
self.latestBuildRequest = [LatestBuildRequest alloc];
self.organizationRequest = [OrganizationRequest alloc];
@ -362,28 +364,44 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
[[[[NSApplication sharedApplication] windows] objectAtIndex:0] setContentViewController: loginScreen];
}
- (void) shouldDownloadLatestBuild:(BOOL) shouldDownload :(NSString*) downloadUrl
- (void) shouldDownloadLatestBuild:(BOOL) shouldDownload :(NSString*) downloadUrl :(BOOL) newLauncherAvailable :(NSString*) launcherUrl
{
self.shouldDownloadInterface = shouldDownload;
self.interfaceDownloadUrl = downloadUrl;
self.latestBuildRequestFinished = TRUE;
if ([self isLoadedIn]) {
Launcher* sharedLauncher = [Launcher sharedLauncher];
[sharedLauncher setCurrentProcessState:CHECKING_UPDATE];
if (shouldDownload) {
ProcessScreen* processScreen = [[ProcessScreen alloc] initWithNibName:@"ProcessScreen" bundle:nil];
[[[[NSApplication sharedApplication] windows] objectAtIndex:0] setContentViewController: processScreen];
[self startUpdateProgressIndicatorTimer];
[self.downloadInterface downloadInterface: downloadUrl];
return;
}
[self interfaceFinishedDownloading];
NSDictionary* launcherArguments = [LauncherCommandlineArgs arguments];
if (newLauncherAvailable && ![launcherArguments valueForKey: @"--noUpdate"]) {
[self.downloadLauncher downloadLauncher: launcherUrl];
} else {
[[NSApplication sharedApplication] activateIgnoringOtherApps:TRUE];
[self showLoginScreen];
self.shouldDownloadInterface = shouldDownload;
self.interfaceDownloadUrl = downloadUrl;
self.latestBuildRequestFinished = TRUE;
if ([self isLoadedIn]) {
Launcher* sharedLauncher = [Launcher sharedLauncher];
[sharedLauncher setCurrentProcessState:CHECKING_UPDATE];
if (shouldDownload) {
ProcessScreen* processScreen = [[ProcessScreen alloc] initWithNibName:@"ProcessScreen" bundle:nil];
[[[[NSApplication sharedApplication] windows] objectAtIndex:0] setContentViewController: processScreen];
[self startUpdateProgressIndicatorTimer];
[self.downloadInterface downloadInterface: downloadUrl];
return;
}
[self interfaceFinishedDownloading];
} else {
[[NSApplication sharedApplication] activateIgnoringOtherApps:TRUE];
[self showLoginScreen];
}
}
}
-(void)runAutoupdater
{
NSTask* task = [[NSTask alloc] init];
NSString* newLauncher = [[[Launcher sharedLauncher] getDownloadPathForContentAndScripts] stringByAppendingPathComponent: @"HQ Launcher.app"];
task.launchPath = [newLauncher stringByAppendingString:@"/Contents/Resources/updater"];
task.arguments = @[[[NSBundle mainBundle] bundlePath], newLauncher];
[task launch];
[NSApp terminate:self];
}
-(void)onSplashScreenTimerFinished:(NSTimer *)timer
{
[self.latestBuildRequest requestLatestBuildInfo];

View file

@ -0,0 +1,8 @@
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
@interface LauncherCommandlineArgs : NSObject {
}
+(NSDictionary*) arguments;
@end

View file

@ -0,0 +1,29 @@
#import "LauncherCommandlineArgs.h"
@implementation LauncherCommandlineArgs
+(NSDictionary*) arguments {
NSArray* arguments = [[NSProcessInfo processInfo] arguments];
if (arguments.count < 2)
{
return nil;
}
NSMutableDictionary* argsDict = [[NSMutableDictionary alloc] init];
NSMutableArray* args = [arguments mutableCopy];
for (NSString* arg in args)
{
if ([arg rangeOfString:@"="].location != NSNotFound && [arg rangeOfString:@"--"].location != NSNotFound) {
NSArray* components = [arg componentsSeparatedByString:@"="];
NSString* key = [components objectAtIndex:0];
NSString* value = [components objectAtIndex:1];
[argsDict setObject:value forKey:key];
} else if ([arg rangeOfString:@"--"].location != NSNotFound) {
[argsDict setObject:@TRUE forKey:arg];
}
}
NSLog(@"AGS: %@", argsDict);
return argsDict;
}
@end

View file

@ -1,5 +1,6 @@
#import "Launcher.h"
#import "Settings.h"
#import "LauncherCommandlineArgs.h"
void redirectLogToDocuments()
{

View file

@ -0,0 +1,35 @@
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
@interface UpdaterHelper : NSObject
+(NSURL*) NSStringToNSURL: (NSString*) path;
@end
@implementation UpdaterHelper
+(NSURL*) NSStringToNSURL: (NSString*) path
{
return [NSURL URLWithString: [path stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]] relativeToURL: [NSURL URLWithString:@"file://"]];
}
@end
int main(int argc, char const* argv[]) {
if (argc < 3) {
NSLog(@"Error: wrong number of arguments");
return 0;
}
for (int index = 0; index < argc; index++) {
NSLog(@"argv at index %d = %s", index, argv[index]);
}
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];
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];
return 0;
}

View file

@ -598,26 +598,6 @@ void Avatar::measureMotionDerivatives(float deltaTime) {
}
}
enum TextRendererType {
CHAT,
DISPLAYNAME
};
static TextRenderer3D* textRenderer(TextRendererType type) {
static TextRenderer3D* chatRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, -1,
false, SHADOW_EFFECT);
static TextRenderer3D* displayNameRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY);
switch(type) {
case CHAT:
return chatRenderer;
case DISPLAYNAME:
return displayNameRenderer;
}
return displayNameRenderer;
}
void Avatar::metaBlendshapeOperator(render::ItemID renderItemID, int blendshapeNumber, const QVector<BlendshapeOffset>& blendshapeOffsets,
const QVector<int>& blendedMeshSizes, const render::ItemIDs& subItemIDs) {
render::Transaction transaction;
@ -1050,7 +1030,6 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const
|| (glm::dot(view.getDirection(), getDisplayNamePosition() - view.getPosition()) <= CLIP_DISTANCE)) {
return;
}
auto renderer = textRenderer(DISPLAYNAME);
// optionally render timing stats for this avatar with the display name
QString renderedDisplayName = _displayName;
@ -1065,7 +1044,8 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const
}
// Compute display name extent/position offset
const glm::vec2 extent = renderer->computeExtent(renderedDisplayName);
static TextRenderer3D* displayNameRenderer = TextRenderer3D::getInstance(ROBOTO_FONT_FAMILY);
const glm::vec2 extent = displayNameRenderer->computeExtent(renderedDisplayName);
if (!glm::any(glm::isCompNull(extent, EPSILON))) {
const QRect nameDynamicRect = QRect(0, 0, (int)extent.x, (int)extent.y);
const int text_x = -nameDynamicRect.width() / 2;
@ -1104,11 +1084,11 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const
QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit();
// Render text slightly in front to avoid z-fighting
textTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_IN_FRONT * renderer->getFontSize()));
textTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_IN_FRONT * displayNameRenderer->getFontSize()));
batch.setModelTransform(textTransform);
{
PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderText");
renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor, glm::vec2(-1.0f), true, forward);
displayNameRenderer->draw(batch, text_x, -text_y, glm::vec2(-1.0f), nameUTF8.data(), textColor, true, forward);
}
}
}

View file

@ -483,3 +483,26 @@ glm::vec4 EntityRenderer::calculatePulseColor(const glm::vec4& color, const Puls
return result;
}
glm::vec3 EntityRenderer::calculatePulseColor(const glm::vec3& color, const PulsePropertyGroup& pulseProperties, quint64 start) {
if (pulseProperties.getPeriod() == 0.0f || (pulseProperties.getColorMode() == PulseMode::NONE && pulseProperties.getAlphaMode() == PulseMode::NONE)) {
return color;
}
float t = ((float)(usecTimestampNow() - start)) / ((float)USECS_PER_SECOND);
float pulse = 0.5f * (cosf(t * (2.0f * (float)M_PI) / pulseProperties.getPeriod()) + 1.0f) * (pulseProperties.getMax() - pulseProperties.getMin()) + pulseProperties.getMin();
float outPulse = (1.0f - pulse);
glm::vec3 result = color;
if (pulseProperties.getColorMode() == PulseMode::IN_PHASE) {
result.r *= pulse;
result.g *= pulse;
result.b *= pulse;
} else if (pulseProperties.getColorMode() == PulseMode::OUT_PHASE) {
result.r *= outPulse;
result.g *= outPulse;
result.b *= outPulse;
}
return result;
}

View file

@ -61,6 +61,7 @@ public:
virtual scriptable::ScriptableModelBase getScriptableModel() override { return scriptable::ScriptableModelBase(); }
static glm::vec4 calculatePulseColor(const glm::vec4& color, const PulsePropertyGroup& pulseProperties, quint64 start);
static glm::vec3 calculatePulseColor(const glm::vec3& color, const PulsePropertyGroup& pulseProperties, quint64 start);
virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const override;
virtual Item::Bound getBound() override;

View file

@ -30,7 +30,7 @@ const float LINE_SCALE_RATIO = 1.2f;
TextEntityRenderer::TextEntityRenderer(const EntityItemPointer& entity) :
Parent(entity),
_textRenderer(TextRenderer3D::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE / 2.0f)) {
_textRenderer(TextRenderer3D::getInstance(ROBOTO_FONT_FAMILY)) {
auto geometryCache = DependencyManager::get<GeometryCache>();
if (geometryCache) {
_geometryID = geometryCache->allocateID();
@ -96,7 +96,6 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
if (_text != entity->getText()) {
return true;
}
if (_lineHeight != entity->getLineHeight()) {
return true;
}
@ -104,15 +103,12 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
if (_textColor != toGlm(entity->getTextColor())) {
return true;
}
if (_textAlpha != entity->getTextAlpha()) {
return true;
}
if (_backgroundColor != toGlm(entity->getBackgroundColor())) {
return true;
}
if (_backgroundAlpha != entity->getBackgroundAlpha()) {
return true;
}
@ -128,15 +124,12 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
if (_leftMargin != entity->getLeftMargin()) {
return true;
}
if (_rightMargin != entity->getRightMargin()) {
return true;
}
if (_topMargin != entity->getTopMargin()) {
return true;
}
if (_bottomMargin != entity->getBottomMargin()) {
return true;
}
@ -145,6 +138,20 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
return true;
}
if (_font != entity->getFont()) {
return true;
}
if (_effect != entity->getTextEffect()) {
return true;
}
if (_effectColor != toGlm(entity->getTextEffectColor())) {
return true;
}
if (_effectThickness != entity->getTextEffectThickness()) {
return true;
}
if (_pulseProperties != entity->getPulseProperties()) {
return true;
}
@ -179,6 +186,10 @@ void TextEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointe
_topMargin = entity->getTopMargin();
_bottomMargin = entity->getBottomMargin();
_unlit = entity->getUnlit();
_font = entity->getFont();
_effect = entity->getTextEffect();
_effectColor = toGlm(entity->getTextEffectColor());
_effectThickness = entity->getTextEffectThickness();
updateTextRenderItem();
});
}
@ -282,7 +293,22 @@ ItemKey entities::TextPayload::getKey() const {
auto renderable = entityTreeRenderer->renderableForEntityId(_entityID);
if (renderable) {
auto textRenderable = std::static_pointer_cast<TextEntityRenderer>(renderable);
return ItemKey::Builder(textRenderable->getKey()).withoutMetaCullGroup().withSubMetaCulled();
// Similar to RenderableEntityItem::getKey()
ItemKey::Builder builder = ItemKey::Builder().withTypeShape().withTypeMeta().withTagBits(textRenderable->getTagMask()).withLayer(textRenderable->getHifiRenderLayer());
builder.withSubMetaCulled();
if (textRenderable->isTextTransparent()) {
builder.withTransparent();
} else if (textRenderable->_canCastShadow) {
builder.withShadowCaster();
}
if (!textRenderable->_visible) {
builder.withInvisible();
}
return builder;
}
}
return ItemKey::Builder::opaqueShape();
@ -342,27 +368,40 @@ void entities::TextPayload::render(RenderArgs* args) {
}
auto textRenderable = std::static_pointer_cast<TextEntityRenderer>(renderable);
glm::vec4 textColor;
Transform modelTransform;
BillboardMode billboardMode;
float lineHeight, leftMargin, rightMargin, topMargin, bottomMargin;
QString text;
glm::vec3 dimensions;
BillboardMode billboardMode;
QString text;
glm::vec4 textColor;
QString font;
TextEffect effect;
glm::vec3 effectColor;
float effectThickness;
float lineHeight, leftMargin, rightMargin, topMargin, bottomMargin;
bool forward;
textRenderable->withReadLock([&] {
modelTransform = textRenderable->_renderTransform;
dimensions = textRenderable->_dimensions;
billboardMode = textRenderable->_billboardMode;
text = textRenderable->_text;
font = textRenderable->_font;
effect = textRenderable->_effect;
effectThickness = textRenderable->_effectThickness;
lineHeight = textRenderable->_lineHeight;
leftMargin = textRenderable->_leftMargin;
rightMargin = textRenderable->_rightMargin;
topMargin = textRenderable->_topMargin;
bottomMargin = textRenderable->_bottomMargin;
text = textRenderable->_text;
dimensions = textRenderable->_dimensions;
float fadeRatio = textRenderable->_isFading ? Interpolate::calculateFadeRatio(textRenderable->_fadeStartTime) : 1.0f;
textColor = glm::vec4(textRenderable->_textColor, fadeRatio * textRenderable->_textAlpha);
textColor = EntityRenderer::calculatePulseColor(textColor, textRenderable->_pulseProperties, textRenderable->_created);
effectColor = EntityRenderer::calculatePulseColor(textRenderable->_effectColor, textRenderable->_pulseProperties, textRenderable->_created);
forward = textRenderable->_renderLayer != RenderLayer::WORLD || args->_renderMethod == render::Args::FORWARD;
});
@ -378,7 +417,9 @@ void entities::TextPayload::render(RenderArgs* args) {
batch.setModelTransform(modelTransform);
glm::vec2 bounds = glm::vec2(dimensions.x - (leftMargin + rightMargin), dimensions.y - (topMargin + bottomMargin));
textRenderer->draw(batch, leftMargin / scale, -topMargin / scale, text, textColor, bounds / scale, textRenderable->_unlit, forward);
textRenderer->draw(batch, leftMargin / scale, -topMargin / scale, bounds / scale, scale,
text, font, textColor, effectColor, effectThickness, effect,
textRenderable->_unlit, forward);
}
namespace render {

View file

@ -67,6 +67,11 @@ private:
BillboardMode _billboardMode;
glm::vec3 _dimensions;
QString _font { "" };
TextEffect _effect { TextEffect::NO_EFFECT };
glm::vec3 _effectColor { 0 };
float _effectThickness { 0.0f };
int _geometryID { 0 };
std::shared_ptr<TextPayload> _textPayload;

View file

@ -299,6 +299,24 @@ void EntityItemProperties::setAvatarPriorityFromString(const QString& mode) {
}
}
inline void addTextEffect(QHash<QString, TextEffect>& lookup, TextEffect effect) { lookup[TextEffectHelpers::getNameForTextEffect(effect)] = effect; }
const QHash<QString, TextEffect> stringToTextEffectLookup = [] {
QHash<QString, TextEffect> toReturn;
addTextEffect(toReturn, TextEffect::NO_EFFECT);
addTextEffect(toReturn, TextEffect::OUTLINE_EFFECT);
addTextEffect(toReturn, TextEffect::OUTLINE_WITH_FILL_EFFECT);
addTextEffect(toReturn, TextEffect::SHADOW_EFFECT);
return toReturn;
}();
QString EntityItemProperties::getTextEffectAsString() const { return TextEffectHelpers::getNameForTextEffect(_textEffect); }
void EntityItemProperties::setTextEffectFromString(const QString& effect) {
auto textEffectItr = stringToTextEffectLookup.find(effect.toLower());
if (textEffectItr != stringToTextEffectLookup.end()) {
_textEffect = textEffectItr.value();
_textEffectChanged = true;
}
}
QString getCollisionGroupAsString(uint16_t group) {
switch (group) {
case USER_COLLISION_GROUP_DYNAMIC:
@ -528,6 +546,10 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_TOP_MARGIN, topMargin);
CHECK_PROPERTY_CHANGE(PROP_BOTTOM_MARGIN, bottomMargin);
CHECK_PROPERTY_CHANGE(PROP_UNLIT, unlit);
CHECK_PROPERTY_CHANGE(PROP_FONT, font);
CHECK_PROPERTY_CHANGE(PROP_TEXT_EFFECT, textEffect);
CHECK_PROPERTY_CHANGE(PROP_TEXT_EFFECT_COLOR, textEffectColor);
CHECK_PROPERTY_CHANGE(PROP_TEXT_EFFECT_THICKNESS, textEffectThickness);
// Zone
changedProperties += _keyLight.getChangedProperties();
@ -1287,6 +1309,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* @property {number} bottomMargin=0.0 - The bottom margin, in meters.
* @property {boolean} unlit=false - <code>true</code> if the entity should be unaffected by lighting. Otherwise, the text
* is lit by the keylight and local lights.
* @property {string} font="" - The text is rendered with this font. Can be one of the following: <code>Courier</code,
* <code>Inconsolata</code>, <code>Roboto</code>, <code>Timeless</code>, or a path to a .sdff file.
* @property {TextEffect} textEffect="none" - The effect that is applied to the text.
* @property {Color} textEffectColor=255,255,255 - The color of the effect.
* @property {number} textEffectThickness=0.2 - The magnitude of the text effect, range <code>0.0</code> &ndash; <code>0.5</code>.
* @property {BillboardMode} billboardMode="none" - Whether the entity is billboarded to face the camera.
* @property {boolean} faceCamera - <code>true</code> if <code>billboardMode</code> is <code>"yaw"</code>, <code>false</code>
* if it isn't. Setting this property to <code>false</code> sets the <code>billboardMode</code> to <code>"none"</code>.
@ -1727,6 +1754,10 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TOP_MARGIN, topMargin);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_BOTTOM_MARGIN, bottomMargin);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_UNLIT, unlit);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FONT, font);
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_TEXT_EFFECT, textEffect, getTextEffectAsString());
COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_TEXT_EFFECT_COLOR, textEffectColor, u8vec3Color);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXT_EFFECT_THICKNESS, textEffectThickness);
}
// Zones only
@ -2103,6 +2134,10 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
COPY_PROPERTY_FROM_QSCRIPTVALUE(topMargin, float, setTopMargin);
COPY_PROPERTY_FROM_QSCRIPTVALUE(bottomMargin, float, setBottomMargin);
COPY_PROPERTY_FROM_QSCRIPTVALUE(unlit, bool, setUnlit);
COPY_PROPERTY_FROM_QSCRIPTVALUE(font, QString, setFont);
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(textEffect, TextEffect);
COPY_PROPERTY_FROM_QSCRIPTVALUE(textEffectColor, u8vec3Color, setTextEffectColor);
COPY_PROPERTY_FROM_QSCRIPTVALUE(textEffectThickness, float, setTextEffectThickness);
// Zone
_keyLight.copyFromScriptValue(object, _defaultSettings);
@ -2387,6 +2422,10 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
COPY_PROPERTY_IF_CHANGED(topMargin);
COPY_PROPERTY_IF_CHANGED(bottomMargin);
COPY_PROPERTY_IF_CHANGED(unlit);
COPY_PROPERTY_IF_CHANGED(font);
COPY_PROPERTY_IF_CHANGED(textEffect);
COPY_PROPERTY_IF_CHANGED(textEffectColor);
COPY_PROPERTY_IF_CHANGED(textEffectThickness);
// Zone
_keyLight.merge(other._keyLight);
@ -2746,6 +2785,10 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr
ADD_PROPERTY_TO_MAP(PROP_TOP_MARGIN, TopMargin, topMargin, float);
ADD_PROPERTY_TO_MAP(PROP_BOTTOM_MARGIN, BottomMargin, bottomMargin, float);
ADD_PROPERTY_TO_MAP(PROP_UNLIT, Unlit, unlit, bool);
ADD_PROPERTY_TO_MAP(PROP_FONT, Font, font, QString);
ADD_PROPERTY_TO_MAP(PROP_TEXT_EFFECT, TextEffect, textEffect, TextEffect);
ADD_PROPERTY_TO_MAP(PROP_TEXT_EFFECT_COLOR, TextEffectColor, textEffectColor, u8vec3Color);
ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_TEXT_EFFECT_THICKNESS, TextEffectThickness, textEffectThickness, float, 0.0, 0.5);
// Zone
{ // Keylight
@ -3178,6 +3221,10 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
APPEND_ENTITY_PROPERTY(PROP_TOP_MARGIN, properties.getTopMargin());
APPEND_ENTITY_PROPERTY(PROP_BOTTOM_MARGIN, properties.getBottomMargin());
APPEND_ENTITY_PROPERTY(PROP_UNLIT, properties.getUnlit());
APPEND_ENTITY_PROPERTY(PROP_FONT, properties.getFont());
APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT, (uint32_t)properties.getTextEffect());
APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT_COLOR, properties.getTextEffectColor());
APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT_THICKNESS, properties.getTextEffectThickness());
}
if (properties.getType() == EntityTypes::Zone) {
@ -3657,6 +3704,10 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TOP_MARGIN, float, setTopMargin);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BOTTOM_MARGIN, float, setBottomMargin);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_UNLIT, bool, setUnlit);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FONT, QString, setFont);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_EFFECT, TextEffect, setTextEffect);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_EFFECT_COLOR, u8vec3Color, setTextEffectColor);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_EFFECT_THICKNESS, float, setTextEffectThickness);
}
if (properties.getType() == EntityTypes::Zone) {
@ -4050,6 +4101,10 @@ void EntityItemProperties::markAllChanged() {
_topMarginChanged = true;
_bottomMarginChanged = true;
_unlitChanged = true;
_fontChanged = true;
_textEffectChanged = true;
_textEffectColorChanged = true;
_textEffectThicknessChanged = true;
// Zone
_keyLight.markAllChanged();
@ -4642,6 +4697,18 @@ QList<QString> EntityItemProperties::listChangedProperties() {
if (unlitChanged()) {
out += "unlit";
}
if (fontChanged()) {
out += "font";
}
if (textEffectChanged()) {
out += "textEffect";
}
if (textEffectColorChanged()) {
out += "textEffectColor";
}
if (textEffectThicknessChanged()) {
out += "textEffectThickness";
}
// Zone
getKeyLight().listChangedProperties(out);

View file

@ -32,6 +32,7 @@
#include <OctreeConstants.h>
#include <ShapeInfo.h>
#include <ColorUtils.h>
#include "FontFamilies.h"
#include "EntityItemID.h"
#include "EntityItemPropertiesDefaults.h"
@ -64,6 +65,7 @@
#include "PrimitiveMode.h"
#include "WebInputMode.h"
#include "GizmoType.h"
#include "TextEffect.h"
const quint64 UNKNOWN_CREATED_TIME = 0;
@ -315,6 +317,10 @@ public:
DEFINE_PROPERTY_REF(PROP_TOP_MARGIN, TopMargin, topMargin, float, TextEntityItem::DEFAULT_MARGIN);
DEFINE_PROPERTY_REF(PROP_BOTTOM_MARGIN, BottomMargin, bottomMargin, float, TextEntityItem::DEFAULT_MARGIN);
DEFINE_PROPERTY_REF(PROP_UNLIT, Unlit, unlit, bool, false);
DEFINE_PROPERTY_REF(PROP_FONT, Font, font, QString, ROBOTO_FONT_FAMILY);
DEFINE_PROPERTY_REF_ENUM(PROP_TEXT_EFFECT, TextEffect, textEffect, TextEffect, TextEffect::NO_EFFECT);
DEFINE_PROPERTY_REF(PROP_TEXT_EFFECT_COLOR, TextEffectColor, textEffectColor, u8vec3Color, TextEntityItem::DEFAULT_TEXT_COLOR);
DEFINE_PROPERTY(PROP_TEXT_EFFECT_THICKNESS, TextEffectThickness, textEffectThickness, float, TextEntityItem::DEFAULT_TEXT_EFFECT_THICKNESS);
// Zone
DEFINE_PROPERTY_GROUP(KeyLight, keyLight, KeyLightPropertyGroup);

View file

@ -244,6 +244,10 @@ enum EntityPropertyList {
PROP_TOP_MARGIN = PROP_DERIVED_8,
PROP_BOTTOM_MARGIN = PROP_DERIVED_9,
PROP_UNLIT = PROP_DERIVED_10,
PROP_FONT = PROP_DERIVED_11,
PROP_TEXT_EFFECT = PROP_DERIVED_12,
PROP_TEXT_EFFECT_COLOR = PROP_DERIVED_13,
PROP_TEXT_EFFECT_THICKNESS = PROP_DERIVED_14,
// Zone
// Keylight

View file

@ -78,7 +78,7 @@ public:
bool accurate { true };
QUuid entityID;
float distance { 0.0f };
BoxFace face;
BoxFace face { UNKNOWN_FACE };
glm::vec3 intersection;
glm::vec3 surfaceNormal;
QVariantMap extraInfo;
@ -94,7 +94,7 @@ public:
QUuid entityID;
float distance { 0.0f };
float parabolicDistance { 0.0f };
BoxFace face;
BoxFace face { UNKNOWN_FACE };
glm::vec3 intersection;
glm::vec3 surfaceNormal;
QVariantMap extraInfo;

View file

@ -29,6 +29,7 @@ const glm::u8vec3 TextEntityItem::DEFAULT_TEXT_COLOR = { 255, 255, 255 };
const float TextEntityItem::DEFAULT_TEXT_ALPHA = 1.0f;
const glm::u8vec3 TextEntityItem::DEFAULT_BACKGROUND_COLOR = { 0, 0, 0};
const float TextEntityItem::DEFAULT_MARGIN = 0.0f;
const float TextEntityItem::DEFAULT_TEXT_EFFECT_THICKNESS = 0.2f;
EntityItemPointer TextEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
EntityItemPointer entity(new TextEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); });
@ -65,6 +66,10 @@ EntityItemProperties TextEntityItem::getProperties(const EntityPropertyFlags& de
COPY_ENTITY_PROPERTY_TO_PROPERTIES(topMargin, getTopMargin);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(bottomMargin, getBottomMargin);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(unlit, getUnlit);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(font, getFont);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textEffect, getTextEffect);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textEffectColor, getTextEffectColor);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textEffectThickness, getTextEffectThickness);
return properties;
}
@ -89,6 +94,10 @@ bool TextEntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(topMargin, setTopMargin);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(bottomMargin, setBottomMargin);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(unlit, setUnlit);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(font, setFont);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textEffect, setTextEffect);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textEffectColor, setTextEffectColor);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textEffectThickness, setTextEffectThickness);
if (somethingChanged) {
bool wantDebug = false;
@ -132,6 +141,10 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
READ_ENTITY_PROPERTY(PROP_TOP_MARGIN, float, setTopMargin);
READ_ENTITY_PROPERTY(PROP_BOTTOM_MARGIN, float, setBottomMargin);
READ_ENTITY_PROPERTY(PROP_UNLIT, bool, setUnlit);
READ_ENTITY_PROPERTY(PROP_FONT, QString, setFont);
READ_ENTITY_PROPERTY(PROP_TEXT_EFFECT, TextEffect, setTextEffect);
READ_ENTITY_PROPERTY(PROP_TEXT_EFFECT_COLOR, glm::u8vec3, setTextEffectColor);
READ_ENTITY_PROPERTY(PROP_TEXT_EFFECT_THICKNESS, float, setTextEffectThickness);
return bytesRead;
}
@ -153,6 +166,10 @@ EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& p
requestedProperties += PROP_TOP_MARGIN;
requestedProperties += PROP_BOTTOM_MARGIN;
requestedProperties += PROP_UNLIT;
requestedProperties += PROP_FONT;
requestedProperties += PROP_TEXT_EFFECT;
requestedProperties += PROP_TEXT_EFFECT_COLOR;
requestedProperties += PROP_TEXT_EFFECT_THICKNESS;
return requestedProperties;
}
@ -184,6 +201,10 @@ void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
APPEND_ENTITY_PROPERTY(PROP_TOP_MARGIN, getTopMargin());
APPEND_ENTITY_PROPERTY(PROP_BOTTOM_MARGIN, getBottomMargin());
APPEND_ENTITY_PROPERTY(PROP_UNLIT, getUnlit());
APPEND_ENTITY_PROPERTY(PROP_FONT, getFont());
APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT, (uint32_t)getTextEffect());
APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT_COLOR, getTextEffectColor());
APPEND_ENTITY_PROPERTY(PROP_TEXT_EFFECT_THICKNESS, getTextEffectThickness());
}
glm::vec3 TextEntityItem::getRaycastDimensions() const {
@ -255,12 +276,10 @@ void TextEntityItem::setText(const QString& value) {
});
}
QString TextEntityItem::getText() const {
QString result;
withReadLock([&] {
result = _text;
QString TextEntityItem::getText() const {
return resultWithReadLock<QString>([&] {
return _text;
});
return result;
}
void TextEntityItem::setLineHeight(float value) {
@ -399,6 +418,54 @@ bool TextEntityItem::getUnlit() const {
});
}
void TextEntityItem::setFont(const QString& value) {
withWriteLock([&] {
_font = value;
});
}
QString TextEntityItem::getFont() const {
return resultWithReadLock<QString>([&] {
return _font;
});
}
void TextEntityItem::setTextEffect(TextEffect value) {
withWriteLock([&] {
_effect = value;
});
}
TextEffect TextEntityItem::getTextEffect() const {
return resultWithReadLock<TextEffect>([&] {
return _effect;
});
}
void TextEntityItem::setTextEffectColor(const glm::u8vec3& value) {
withWriteLock([&] {
_effectColor = value;
});
}
glm::u8vec3 TextEntityItem::getTextEffectColor() const {
return resultWithReadLock<glm::u8vec3>([&] {
return _effectColor;
});
}
void TextEntityItem::setTextEffectThickness(float value) {
withWriteLock([&] {
_effectThickness = value;
});
}
float TextEntityItem::getTextEffectThickness() const {
return resultWithReadLock<float>([&] {
return _effectThickness;
});
}
PulsePropertyGroup TextEntityItem::getPulseProperties() const {
return resultWithReadLock<PulsePropertyGroup>([&] {
return _pulseProperties;

View file

@ -15,6 +15,7 @@
#include "EntityItem.h"
#include "PulsePropertyGroup.h"
#include "TextEffect.h"
class TextEntityItem : public EntityItem {
public:
@ -100,6 +101,19 @@ public:
bool getUnlit() const;
void setUnlit(bool value);
void setFont(const QString& value);
QString getFont() const;
TextEffect getTextEffect() const;
void setTextEffect(TextEffect value);
glm::u8vec3 getTextEffectColor() const;
void setTextEffectColor(const glm::u8vec3& value);
static const float DEFAULT_TEXT_EFFECT_THICKNESS;
float getTextEffectThickness() const;
void setTextEffectThickness(float value);
PulsePropertyGroup getPulseProperties() const;
private:
@ -117,6 +131,11 @@ private:
float _topMargin;
float _bottomMargin;
bool _unlit;
QString _font;
TextEffect _effect;
glm::u8vec3 _effectColor;
float _effectThickness;
};
#endif // hifi_TextEntityItem_h

View file

@ -148,6 +148,33 @@ static bool setupPixelFormatSimple(HDC hdc) {
#endif
const gl::ContextInfo& gl::ContextInfo::get(bool init) {
static gl::ContextInfo INSTANCE;
if (init) {
static std::once_flag once;
std::call_once(once, [&] {
INSTANCE.init();
});
}
return INSTANCE;
}
gl::ContextInfo& gl::ContextInfo::init() {
if (glGetString) {
version = (const char*)glGetString(GL_VERSION);
shadingLanguageVersion = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION);
vendor = (const char*)glGetString(GL_VENDOR);
renderer = (const char*)glGetString(GL_RENDERER);
GLint n = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &n);
for (GLint i = 0; i < n; ++i) {
extensions.emplace_back((const char*)glGetStringi(GL_EXTENSIONS, i));
}
}
return *this;
}
uint16_t gl::getAvailableVersion() {
static uint8_t major = 0, minor = 0;
static std::once_flag once;
@ -277,25 +304,6 @@ int glVersionToInteger(QString glVersion) {
return (majorNumber << 16) | minorNumber;
}
QJsonObject getGLContextData() {
static QJsonObject result;
static std::once_flag once;
std::call_once(once, [] {
QString glVersion = QString((const char*)glGetString(GL_VERSION));
QString glslVersion = QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION));
QString glVendor = QString((const char*) glGetString(GL_VENDOR));
QString glRenderer = QString((const char*)glGetString(GL_RENDERER));
result = QJsonObject {
{ "version", glVersion },
{ "sl_version", glslVersion },
{ "vendor", glVendor },
{ "renderer", glRenderer },
};
});
return result;
}
QThread* RENDER_THREAD = nullptr;
bool isRenderThread() {

View file

@ -29,7 +29,6 @@ class QGLFormat;
size_t evalGLFormatSwapchainPixelSize(const QSurfaceFormat& format);
const QSurfaceFormat& getDefaultOpenGLSurfaceFormat();
QJsonObject getGLContextData();
int glVersionToInteger(QString glVersion);
bool isRenderThread();
@ -39,6 +38,26 @@ bool isRenderThread();
#define GL_GET_MAJOR_VERSION(glversion) ((glversion & 0xFF00) >> 8)
namespace gl {
struct ContextInfo {
std::string version;
std::string shadingLanguageVersion;
std::string vendor;
std::string renderer;
std::vector<std::string> extensions;
ContextInfo& init();
operator bool() const { return !version.empty(); }
static const ContextInfo& get(bool init = false);
};
#define LOG_GL_CONTEXT_INFO(category, contextInfo) \
qCDebug(category) << "GL Version: " << contextInfo.version.c_str(); \
qCDebug(category) << "GL Shader Language Version: " << contextInfo.shadingLanguageVersion.c_str(); \
qCDebug(category) << "GL Vendor: " << contextInfo.vendor.c_str(); \
qCDebug(category) << "GL Renderer: " << contextInfo.renderer.c_str();
void globalLock();
void globalRelease(bool finish = true);

View file

@ -33,14 +33,12 @@ GLWindow::~GLWindow() {
bool GLWindow::makeCurrent() {
bool makeCurrentResult = _context->makeCurrent();
Q_ASSERT(makeCurrentResult);
std::call_once(_reportOnce, []{
qCDebug(glLogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION));
qCDebug(glLogging) << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION));
qCDebug(glLogging) << "GL Vendor: " << QString((const char*) glGetString(GL_VENDOR));
qCDebug(glLogging) << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER));
});
if (makeCurrentResult) {
std::call_once(_reportOnce, []{
LOG_GL_CONTEXT_INFO(glLogging, gl::ContextInfo().init());
});
}
return makeCurrentResult;
}

View file

@ -73,13 +73,9 @@ bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) {
bool OffscreenGLCanvas::makeCurrent() {
bool result = _context->makeCurrent(_offscreenSurface);
if (glGetString) {
if (result) {
std::call_once(_reportOnce, [] {
qCDebug(glLogging) << "GL Version: " << QString((const char*)glGetString(GL_VERSION));
qCDebug(glLogging) << "GL Shader Language Version: "
<< QString((const char*)glGetString(GL_SHADING_LANGUAGE_VERSION));
qCDebug(glLogging) << "GL Vendor: " << QString((const char*)glGetString(GL_VENDOR));
qCDebug(glLogging) << "GL Renderer: " << QString((const char*)glGetString(GL_RENDERER));
LOG_GL_CONTEXT_INFO(glLogging, gl::ContextInfo().init());
});
}

View file

@ -117,9 +117,10 @@ void GLBackend::init() {
static std::once_flag once;
std::call_once(once, [] {
QString vendor{ (const char*)glGetString(GL_VENDOR) };
QString renderer{ (const char*)glGetString(GL_RENDERER) };
::gl::ContextInfo contextInfo;
contextInfo.init();
QString vendor { contextInfo.vendor.c_str() };
QString renderer { contextInfo.renderer.c_str() };
// Textures
GL_GET_INTEGER(MAX_TEXTURE_IMAGE_UNITS);
@ -131,10 +132,7 @@ void GLBackend::init() {
GL_GET_INTEGER(MAX_UNIFORM_BLOCK_SIZE);
GL_GET_INTEGER(UNIFORM_BUFFER_OFFSET_ALIGNMENT);
qCDebug(gpugllogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION));
qCDebug(gpugllogging) << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION));
qCDebug(gpugllogging) << "GL Vendor: " << vendor;
qCDebug(gpugllogging) << "GL Renderer: " << renderer;
LOG_GL_CONTEXT_INFO(gpugllogging, contextInfo);
GPUIdent* gpu = GPUIdent::getInstance(vendor, renderer);
// From here on, GPUIdent::getInstance()->getMumble() should efficiently give the same answers.
qCDebug(gpugllogging) << "GPU:";

View file

@ -1143,6 +1143,10 @@ void NodeList::maybeSendIgnoreSetToNode(SharedNodePointer newNode) {
}
void NodeList::setAvatarGain(const QUuid& nodeID, float gain) {
if (nodeID.isNull()) {
_avatarGain = gain;
}
// cannot set gain of yourself
if (getSessionUUID() != nodeID) {
auto audioMixer = soloNodeOfType(NodeType::AudioMixer);
@ -1160,7 +1164,6 @@ void NodeList::setAvatarGain(const QUuid& nodeID, float gain) {
qCDebug(networking) << "Sending Set MASTER Avatar Gain packet with Gain:" << gain;
sendPacket(std::move(setAvatarGainPacket), *audioMixer);
_avatarGain = gain;
} else {
qCDebug(networking) << "Sending Set Avatar Gain packet with UUID:" << uuidStringWithoutCurlyBraces(nodeID) << "Gain:" << gain;
@ -1192,6 +1195,8 @@ float NodeList::getAvatarGain(const QUuid& nodeID) {
}
void NodeList::setInjectorGain(float gain) {
_injectorGain = gain;
auto audioMixer = soloNodeOfType(NodeType::AudioMixer);
if (audioMixer) {
// setup the packet
@ -1203,7 +1208,6 @@ void NodeList::setInjectorGain(float gain) {
qCDebug(networking) << "Sending Set Injector Gain packet with Gain:" << gain;
sendPacket(std::move(setInjectorGainPacket), *audioMixer);
_injectorGain = gain;
} else {
qWarning() << "Couldn't find audio mixer to send set gain request";

View file

@ -273,6 +273,7 @@ enum class EntityVersion : PacketVersion {
PrivateUserData,
TextUnlit,
ShadowBiasAndDistance,
TextEntityFonts,
// Add new versions above here
NUM_PACKET_TYPE,

View file

@ -40,6 +40,7 @@
#include "WebInputMode.h"
#include "PulseMode.h"
#include "GizmoType.h"
#include "TextEffect.h"
#include "OctreeConstants.h"
#include "OctreeElement.h"
@ -273,6 +274,7 @@ public:
static int unpackDataFromBytes(const unsigned char* dataBytes, WebInputMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
static int unpackDataFromBytes(const unsigned char* dataBytes, PulseMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
static int unpackDataFromBytes(const unsigned char* dataBytes, GizmoType& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
static int unpackDataFromBytes(const unsigned char* dataBytes, TextEffect& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec2& result);
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec3& result);
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::u8vec3& result);

View file

@ -1,7 +1,7 @@
set(TARGET_NAME platform)
setup_hifi_library()
link_hifi_libraries(shared)
link_hifi_libraries(shared gl)
GroupSources("src")
target_json()
@ -15,4 +15,4 @@ if (APPLE)
elseif(WIN32)
target_link_libraries(${TARGET_NAME} Iphlpapi.lib)
endif ()
endif()

View file

@ -34,6 +34,43 @@ namespace platform { namespace keys{
extern const char* displays;
extern const char* isMaster;
}
namespace graphicsAPI {
extern const char* name;
extern const char* version;
extern const char* apiOpenGL;
extern const char* apiVulkan;
extern const char* apiDirect3D11;
extern const char* apiDirect3D12;
extern const char* apiMetal;
namespace gl {
extern const char* shadingLanguageVersion;
extern const char* vendor;
extern const char* renderer;
extern const char* extensions;
}
namespace vk {
extern const char* devices;
namespace device {
extern const char* apiVersion;
extern const char* driverVersion;
extern const char* deviceType;
extern const char* vendor;
extern const char* name;
extern const char* formats;
extern const char* extensions;
extern const char* queues;
extern const char* heaps;
namespace heap {
extern const char* flags;
extern const char* size;
}
namespace queue {
extern const char* flags;
extern const char* count;
}
}
}
}
namespace nic {
extern const char* mac;
extern const char* name;
@ -78,6 +115,7 @@ namespace platform { namespace keys{
// Keys for categories used in json returned by getAll()
extern const char* CPUS;
extern const char* GPUS;
extern const char* GRAPHICS_APIS;
extern const char* DISPLAYS;
extern const char* NICS;
extern const char* MEMORY;

View file

@ -322,3 +322,8 @@ void MACOSInstance::enumerateComputer(){
}
void MACOSInstance::enumerateGraphicsApis() {
Instance::enumerateGraphicsApis();
// TODO imeplement Metal query
}

View file

@ -19,6 +19,7 @@ namespace platform {
void enumerateGpusAndDisplays() override;
void enumerateMemory() override;
void enumerateComputer() override;
void enumerateGraphicsApis() override;
};
} // namespace platform

View file

@ -35,6 +35,45 @@ namespace platform { namespace keys {
const char* displays = "displays";
const char* isMaster = "isMaster";
}
namespace graphicsAPI {
const char* name = "name";
const char* version = "version";
const char* apiOpenGL = "OpenGL";
const char* apiVulkan = "Vulkan";
const char* apiDirect3D11 = "D3D11";
const char* apiDirect3D12 = "D3D12";
const char* apiMetal = "Metal";
namespace gl {
const char* shadingLanguageVersion = "shadingLanguageVersion";
const char* vendor = "vendor";
const char* renderer = "renderer";
const char* extensions = "extensions";
}
namespace vk {
const char* devices = "devices";
namespace device {
const char* apiVersion = "apiVersion";
const char* driverVersion = "driverVersion";
const char* deviceType = "deviceType";
const char* vendor = "vendor";
const char* name = "name";
const char* formats = "formats";
const char* extensions = "extensions";
const char* heaps = "heaps";
namespace heap {
const char* flags = "flags";
const char* size = "size";
}
const char* queues = "queues";
namespace queue {
const char* flags = "flags";
const char* count = "count";
}
}
}
}
namespace nic {
const char* mac = "mac";
const char* name = "name";
@ -78,6 +117,7 @@ namespace platform { namespace keys {
const char* CPUS = "cpus";
const char* GPUS = "gpus";
const char* GRAPHICS_APIS = "graphicsAPIs";
const char* DISPLAYS = "displays";
const char* NICS = "nics";
const char* MEMORY = "memory";

View file

@ -10,9 +10,18 @@
#include "PlatformInstance.h"
#include <QNetworkInterface>
#include <gl/GLHelpers.h>
#include "../PlatformKeys.h"
#include "../Profiler.h"
// For testing the vulkan dump
//#define HAVE_VULKAN 1
//#pragma comment(lib, "C:\\VulkanSDK\\1.1.101.0\\Lib\\vulkan-1.lib")
#ifdef HAVE_VULKAN
#include <vulkan/vulkan.hpp>
#endif
using namespace platform;
bool Instance::enumeratePlatform() {
@ -30,6 +39,7 @@ bool Instance::enumeratePlatform() {
enumerateCpus();
enumerateGpusAndDisplays();
enumerateNics();
enumerateGraphicsApis();
// eval the master index for each platform scopes
updateMasterIndices();
@ -105,6 +115,74 @@ void Instance::enumerateNics() {
}
}
#if defined(HAVE_VULKAN)
static std::string vkVersionToString(uint32_t version) {
return QString("%1.%2.%3").arg(VK_VERSION_MAJOR(version)).arg(VK_VERSION_MINOR(version)).arg(VK_VERSION_PATCH(version)).toStdString();
}
#endif
void Instance::enumerateGraphicsApis() {
// OpenGL rendering API is supported on all platforms
{
auto& glContextInfo = gl::ContextInfo::get();
json gl;
gl[keys::graphicsAPI::name] = keys::graphicsAPI::apiOpenGL;
gl[keys::graphicsAPI::version] = glContextInfo.version;
gl[keys::graphicsAPI::gl::vendor] = glContextInfo.vendor;
gl[keys::graphicsAPI::gl::renderer] = glContextInfo.renderer;
gl[keys::graphicsAPI::gl::shadingLanguageVersion] = glContextInfo.shadingLanguageVersion;
gl[keys::graphicsAPI::gl::extensions] = glContextInfo.extensions;
_graphicsApis.push_back(gl);
}
#if defined(HAVE_VULKAN)
// Vulkan rendering API is supported on all platforms (sort of)
{
try {
vk::ApplicationInfo appInfo{ "Interface", 1, "Luci", 1, VK_API_VERSION_1_1 };
auto instancePtr = vk::createInstanceUnique({ {}, &appInfo });
if (instancePtr) {
json vkinfo;
const auto& vkinstance = *instancePtr;
vkinfo[keys::graphicsAPI::name] = keys::graphicsAPI::apiVulkan;
vkinfo[keys::graphicsAPI::version] = vkVersionToString(VK_API_VERSION_1_1);
for (const auto& physicalDevice : vkinstance.enumeratePhysicalDevices()) {
json vkdevice;
auto properties = physicalDevice.getProperties();
vkdevice[keys::graphicsAPI::vk::device::driverVersion] = vkVersionToString(properties.driverVersion);
vkdevice[keys::graphicsAPI::vk::device::apiVersion] = vkVersionToString(properties.apiVersion);
vkdevice[keys::graphicsAPI::vk::device::deviceType] = vk::to_string(properties.deviceType);
vkdevice[keys::graphicsAPI::vk::device::vendor] = properties.vendorID;
vkdevice[keys::graphicsAPI::vk::device::name] = properties.deviceName;
for (const auto& extensionProperties : physicalDevice.enumerateDeviceExtensionProperties()) {
vkdevice[keys::graphicsAPI::vk::device::extensions].push_back(extensionProperties.extensionName);
}
for (const auto& queueFamilyProperties : physicalDevice.getQueueFamilyProperties()) {
json vkqueuefamily;
vkqueuefamily[keys::graphicsAPI::vk::device::queue::flags] = vk::to_string(queueFamilyProperties.queueFlags);
vkqueuefamily[keys::graphicsAPI::vk::device::queue::count] = queueFamilyProperties.queueCount;
vkdevice[keys::graphicsAPI::vk::device::queues].push_back(vkqueuefamily);
}
auto memoryProperties = physicalDevice.getMemoryProperties();
for (uint32_t heapIndex = 0; heapIndex < memoryProperties.memoryHeapCount; ++heapIndex) {
json vkmemoryheap;
const auto& heap = memoryProperties.memoryHeaps[heapIndex];
vkmemoryheap[keys::graphicsAPI::vk::device::heap::flags] = vk::to_string(heap.flags);
vkmemoryheap[keys::graphicsAPI::vk::device::heap::size] = heap.size;
vkdevice[keys::graphicsAPI::vk::device::heaps].push_back(vkmemoryheap);
}
vkinfo[keys::graphicsAPI::vk::devices].push_back(vkdevice);
}
_graphicsApis.push_back(vkinfo);
}
} catch (const std::runtime_error&) {
}
}
#endif
}
json Instance::getCPU(int index) {
assert(index < (int)_cpus.size());
@ -166,6 +244,14 @@ json Instance::listAllKeys() {
keys::gpu::driver,
keys::gpu::displays,
keys::graphicsAPI::version,
keys::graphicsAPI::name,
keys::graphicsAPI::gl::shadingLanguageVersion,
keys::graphicsAPI::gl::vendor,
keys::graphicsAPI::gl::renderer,
keys::graphicsAPI::gl::extensions,
keys::display::boundsLeft,
keys::display::boundsRight,
keys::display::boundsTop,
@ -187,6 +273,7 @@ json Instance::listAllKeys() {
keys::CPUS,
keys::GPUS,
keys::GRAPHICS_APIS,
keys::DISPLAYS,
keys::MEMORY,
keys::COMPUTER,
@ -218,6 +305,7 @@ json Instance::getAll() {
all[keys::MEMORY] = _memory;
all[keys::CPUS] = _cpus;
all[keys::GPUS] = _gpus;
all[keys::GRAPHICS_APIS] = _graphicsApis;
all[keys::DISPLAYS] = _displays;
all[keys::NICS] = _nics;

View file

@ -44,6 +44,7 @@ public:
void virtual enumerateNics();
void virtual enumerateMemory() = 0;
void virtual enumerateComputer()=0;
virtual void enumerateGraphicsApis();
virtual ~Instance();
@ -57,6 +58,7 @@ protected:
std::vector<json> _gpus;
std::vector<json> _displays;
std::vector<json> _nics;
json _graphicsApis;
json _memory;
json _computer;

View file

@ -27,7 +27,6 @@
#include <dxgi1_3.h>
#pragma comment(lib, "dxgi.lib")
#include <shellscalingapi.h>
#pragma comment(lib, "Shcore.lib")
#endif
@ -118,10 +117,19 @@ void WINInstance::enumerateGpusAndDisplays() {
// Grab the dpi info for the monitor
UINT dpiX{ 0 };
UINT dpiY{ 0 };
GetDpiForMonitor(outputDesc.Monitor, MDT_RAW_DPI, &dpiX, &dpiY);
UINT dpiXScaled{ 0 };
UINT dpiYScaled{ 0 };
GetDpiForMonitor(outputDesc.Monitor, MDT_EFFECTIVE_DPI, &dpiXScaled, &dpiYScaled);
// SHCore.dll is not available prior to Windows 8.1
HMODULE SHCoreDLL = LoadLibraryW(L"SHCore.dll");
if (SHCoreDLL) {
auto _GetDpiForMonitor = reinterpret_cast<decltype(GetDpiForMonitor)*>(GetProcAddress(SHCoreDLL, "GetDpiForMonitor"));
if (_GetDpiForMonitor) {
_GetDpiForMonitor(outputDesc.Monitor, MDT_RAW_DPI, &dpiX, &dpiY);
_GetDpiForMonitor(outputDesc.Monitor, MDT_EFFECTIVE_DPI, &dpiXScaled, &dpiYScaled);
}
FreeLibrary(SHCoreDLL);
}
// Current display mode
DEVMODEW devMode;
@ -251,3 +259,9 @@ void WINInstance::enumerateNics() {
}
#endif
}
void WINInstance::enumerateGraphicsApis() {
Instance::enumerateGraphicsApis();
// TODO imeplement D3D 11/12 queries
}

View file

@ -20,6 +20,7 @@ namespace platform {
void enumerateMemory() override;
void enumerateComputer () override;
void enumerateNics() override;
void enumerateGraphicsApis() override;
};
} // namespace platform

View file

@ -10,45 +10,19 @@
//
#include "TextRenderer3D.h"
#include <StreamHelpers.h>
#include <gpu/Shader.h>
#include <QBuffer>
#include <QImage>
#include <QStringList>
#include <QFile>
#include <shaders/Shaders.h>
#include "text/Font.h"
#include "GLMHelpers.h"
#include "MatrixStack.h"
#include "RenderUtilsLogging.h"
#include "GeometryCache.h"
const float TextRenderer3D::DEFAULT_POINT_SIZE = 12;
TextRenderer3D* TextRenderer3D::getInstance(const char* family, float pointSize,
bool bold, bool italic, EffectType effect, int effectThickness) {
return new TextRenderer3D(family, pointSize, false, italic, effect, effectThickness);
TextRenderer3D* TextRenderer3D::getInstance(const char* family) {
return new TextRenderer3D(family);
}
TextRenderer3D::TextRenderer3D(const char* family, float pointSize, int weight, bool italic,
EffectType effect, int effectThickness) :
_effectType(effect),
_effectThickness(effectThickness),
TextRenderer3D::TextRenderer3D(const char* family) :
_family(family),
_font(Font::load(family)) {
if (!_font) {
qWarning() << "Unable to load font with family " << family;
_font = Font::load("Courier");
}
if (1 != _effectThickness) {
qWarning() << "Effect thickness not currently supported";
}
if (NO_EFFECT != _effectType && OUTLINE_EFFECT != _effectType) {
qWarning() << "Effect type not currently supported";
_font = Font::load(ROBOTO_FONT_FAMILY);
}
}
@ -66,12 +40,21 @@ float TextRenderer3D::getFontSize() const {
return 0.0f;
}
void TextRenderer3D::draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color,
const glm::vec2& bounds, bool unlit, bool forward) {
// The font does all the OpenGL work
void TextRenderer3D::draw(gpu::Batch& batch, float x, float y, const glm::vec2& bounds,
const QString& str, const glm::vec4& color, bool unlit, bool forward) {
if (_font) {
_color = color;
_font->drawString(batch, _drawInfo, str, _color, _effectType, { x, y }, bounds, unlit, forward);
_font->drawString(batch, _drawInfo, str, color, glm::vec3(0.0f), 0, TextEffect::NO_EFFECT, { x, y }, bounds, 1.0f, unlit, forward);
}
}
void TextRenderer3D::draw(gpu::Batch& batch, float x, float y, const glm::vec2& bounds, float scale,
const QString& str, const QString& font, const glm::vec4& color, const glm::vec3& effectColor,
float effectThickness, TextEffect effect, bool unlit, bool forward) {
if (font != _family) {
_family = font;
_font = Font::load(_family);
}
if (_font) {
_font->drawString(batch, _drawInfo, str, color, effectColor, effectThickness, effect, { x, y }, bounds, scale, unlit, forward);
}
}

View file

@ -14,48 +14,32 @@
#include <memory>
#include <glm/glm.hpp>
#include <QColor>
#include <gpu/Forward.h>
namespace gpu {
class Batch;
}
class Font;
#include "text/Font.h"
#include "text/EffectType.h"
#include "text/FontFamilies.h"
#include "TextEffect.h"
#include "FontFamilies.h"
// TextRenderer3D is actually a fairly thin wrapper around a Font class
// defined in the cpp file.
class TextRenderer3D {
public:
static const float DEFAULT_POINT_SIZE;
static TextRenderer3D* getInstance(const char* family, float pointSize = DEFAULT_POINT_SIZE,
bool bold = false, bool italic = false, EffectType effect = NO_EFFECT, int effectThickness = 1);
static TextRenderer3D* getInstance(const char* family);
glm::vec2 computeExtent(const QString& str) const;
float getFontSize() const; // Pixel size
void draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color,
const glm::vec2& bounds, bool unlit, bool forward);
void draw(gpu::Batch& batch, float x, float y, const glm::vec2& bounds,
const QString& str, const glm::vec4& color, bool unlit, bool forward);
void draw(gpu::Batch& batch, float x, float y, const glm::vec2& bounds, float scale,
const QString& str, const QString& font, const glm::vec4& color, const glm::vec3& effectColor,
float effectThickness, TextEffect effect, bool unlit, bool forward);
private:
TextRenderer3D(const char* family, float pointSize, int weight = -1, bool italic = false,
EffectType effect = NO_EFFECT, int effectThickness = 1);
TextRenderer3D(const char* family);
// the type of effect to apply
const EffectType _effectType;
QString _family;
// the thickness of the effect
const int _effectThickness;
// text color
glm::vec4 _color;
Font::DrawInfo _drawInfo;
std::shared_ptr<Font> _font;
Font::DrawInfo _drawInfo;
};
#endif // hifi_TextRenderer3D_h

View file

@ -36,29 +36,29 @@
layout(location=RENDER_UTILS_ATTR_POSITION_ES) in vec4 _positionES;
<@endif@>
layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS;
layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color;
layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01;
#define _texCoord0 _texCoord01.xy
#define _texCoord1 _texCoord01.zw
layout(location=RENDER_UTILS_ATTR_FADE1) flat in vec4 _glyphBounds; // we're reusing the fade texcoord locations here
void main() {
float alpha = evalSDFSuperSampled(_texCoord0);
vec4 color = evalSDFSuperSampled(_texCoord0, _glyphBounds);
<@if HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@>
alpha *= _color.a;
if (alpha <= 0.0) {
color.a *= params.color.a;
if (color.a <= 0.0) {
discard;
}
<@endif@>
<@if HIFI_USE_UNLIT@>
<@if HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@>
_fragColor0 = vec4(_color.rgb * isUnlitEnabled(), alpha);
_fragColor0 = vec4(color.rgb * isUnlitEnabled(), color.a);
<@else@>
packDeferredFragmentUnlit(
normalize(_normalWS),
alpha,
_color.rgb);
color.a,
color.rgb);
<@endif@>
<@else@>
<@if HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@>
@ -72,12 +72,12 @@ void main() {
DEFAULT_OCCLUSION,
fragPosition,
normalize(_normalWS),
_color.rgb,
color.rgb,
DEFAULT_FRESNEL,
DEFAULT_METALLIC,
DEFAULT_EMISSIVE,
DEFAULT_ROUGHNESS, alpha),
alpha);
DEFAULT_ROUGHNESS, color.a),
color.a);
<@else@>
_fragColor0 = vec4(evalSkyboxGlobalColor(
cam._viewInverse,
@ -85,17 +85,17 @@ void main() {
DEFAULT_OCCLUSION,
fragPosition,
normalize(_normalWS),
_color.rgb,
color.rgb,
DEFAULT_FRESNEL,
DEFAULT_METALLIC,
DEFAULT_ROUGHNESS),
alpha);
color.a);
<@endif@>
<@else@>
packDeferredFragment(
normalize(_normalWS),
alpha,
_color.rgb,
color.a,
color.rgb,
DEFAULT_ROUGHNESS,
DEFAULT_METALLIC,
DEFAULT_EMISSIVE,

View file

@ -14,11 +14,16 @@
<@if not SDF_TEXT3D_SLH@>
<@def SDF_TEXT3D_SLH@>
LAYOUT(binding=0) uniform sampler2D Font;
LAYOUT(binding=0) uniform sampler2D fontTexture;
struct TextParams {
vec4 color;
vec4 outline;
vec3 effectColor;
float effectThickness;
int effect;
vec3 spare;
};
LAYOUT(binding=0) uniform textParamsBuffer {
@ -29,32 +34,57 @@ LAYOUT(binding=0) uniform textParamsBuffer {
#define TAA_TEXTURE_LOD_BIAS -3.0
const float interiorCutoff = 0.8;
const float outlineExpansion = 0.2;
const float interiorCutoff = 0.5;
const float taaBias = pow(2.0, TAA_TEXTURE_LOD_BIAS);
float evalSDF(vec2 texCoord) {
// retrieve signed distance
float sdf = textureLod(Font, texCoord, TAA_TEXTURE_LOD_BIAS).g;
sdf = mix(sdf, mix(sdf + outlineExpansion, 1.0 - sdf, float(sdf > interiorCutoff)), float(params.outline.x > 0.0));
vec4 evalSDF(vec2 texCoord, vec4 glyphBounds) {
vec3 color = params.color.rgb;
float sdf = textureLod(fontTexture, texCoord, TAA_TEXTURE_LOD_BIAS).g;
// Rely on TAA for anti-aliasing
return step(0.5, sdf);
// Outline
if (params.effect == 1 || params.effect == 2) {
float outline = float(sdf < interiorCutoff);
color = mix(color, params.effectColor, outline);
// with or without fill
sdf = mix(sdf, 0.0, float(params.effect == 1) * (1.0 - outline));
const float EPSILON = 0.00001;
sdf += mix(0.0, params.effectThickness - EPSILON, outline);
} else if (params.effect == 3) { // Shadow
// don't sample from outside of our glyph bounds
sdf *= mix(1.0, 0.0, float(clamp(texCoord, glyphBounds.xy, glyphBounds.xy + glyphBounds.zw) != texCoord));
if (sdf < interiorCutoff) {
color = params.effectColor;
const float DOUBLE_MAX_OFFSET_PIXELS = 20.0; // must match value in Font.cpp
// FIXME: TAA_TEXTURE_LOD_BIAS doesn't have any effect because we're only generating one mip, so here we need to use 0, but it should
// really match the LOD that we use in the textureLod call below
vec2 textureOffset = vec2(params.effectThickness * DOUBLE_MAX_OFFSET_PIXELS) / vec2(textureSize(fontTexture, 0/*int(TAA_TEXTURE_LOD_BIAS)*/));
vec2 shadowTexCoords = texCoord - textureOffset;
sdf = textureLod(fontTexture, shadowTexCoords, TAA_TEXTURE_LOD_BIAS).g;
// don't sample from outside of our glyph bounds
sdf *= mix(1.0, 0.0, float(clamp(shadowTexCoords, glyphBounds.xy, glyphBounds.xy + glyphBounds.zw) != shadowTexCoords));
}
}
return vec4(color, sdf);
}
float evalSDFSuperSampled(vec2 texCoord) {
vec4 evalSDFSuperSampled(vec2 texCoord, vec4 glyphBounds) {
vec2 dxTexCoord = dFdx(texCoord) * 0.5 * taaBias;
vec2 dyTexCoord = dFdy(texCoord) * 0.5 * taaBias;
// Perform 4x supersampling for anisotropic filtering
float a;
a = evalSDF(texCoord);
a += evalSDF(texCoord + dxTexCoord);
a += evalSDF(texCoord + dyTexCoord);
a += evalSDF(texCoord + dxTexCoord + dyTexCoord);
a *= 0.25;
vec4 color;
color = evalSDF(texCoord, glyphBounds);
color += evalSDF(texCoord + dxTexCoord, glyphBounds);
color += evalSDF(texCoord + dyTexCoord, glyphBounds);
color += evalSDF(texCoord + dxTexCoord + dyTexCoord, glyphBounds);
color *= 0.25;
return a;
return vec4(color.rgb, step(interiorCutoff, color.a));
}
<@endfunc@>

View file

@ -23,19 +23,28 @@
layout(location=RENDER_UTILS_ATTR_POSITION_ES) out vec4 _positionES;
<@endif@>
layout(location=RENDER_UTILS_ATTR_NORMAL_WS) out vec3 _normalWS;
layout(location=RENDER_UTILS_ATTR_COLOR) out vec4 _color;
layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01;
layout(location=RENDER_UTILS_ATTR_FADE1) flat out vec4 _glyphBounds; // we're reusing the fade texcoord locations here
void main() {
_texCoord01 = vec4(inTexCoord0.st, 0.0, 0.0);
_color = color_sRGBAToLinear(params.color);
_glyphBounds = inTexCoord1;
vec4 position = inPosition;
// if we're in shadow mode, we need to move each subsequent quad slightly forward so it doesn't z-fight
// with the shadows of the letters before it
if (params.effect == 3) { // Shadow
const int VERTICES_PER_QUAD = 4; // must match value in Font.cpp
const float EPSILON = 0.001;
position.z += float(gl_VertexID / VERTICES_PER_QUAD) * EPSILON;
}
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<@if HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@>
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _positionES, gl_Position)$>
<$transformModelToEyeAndClipPos(cam, obj, position, _positionES, gl_Position)$>
<@else@>
<$transformModelToClipPos(cam, obj, inPosition, gl_Position)$>
<$transformModelToClipPos(cam, obj, position, gl_Position)$>
<@endif@>
const vec3 normal = vec3(0, 0, 1);

View file

@ -1,15 +0,0 @@
//
// Created by Bradley Austin Davis on 2015/07/16
// Copyright 2013 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
//
#pragma once
#ifndef hifi_EffectType_h
#define hifi_EffectType_h
enum EffectType { NO_EFFECT, SHADOW_EFFECT, OUTLINE_EFFECT };
#endif

View file

@ -2,6 +2,7 @@
#include <QFile>
#include <QImage>
#include <QNetworkReply>
#include <ColorUtils.h>
@ -13,6 +14,8 @@
#include "FontFamilies.h"
#include "../StencilMaskPass.h"
#include "NetworkAccessManager.h"
static std::mutex fontMutex;
std::map<std::tuple<bool, bool, bool>, gpu::PipelinePointer> Font::_pipelines;
@ -21,67 +24,90 @@ gpu::Stream::FormatPointer Font::_format;
struct TextureVertex {
glm::vec2 pos;
glm::vec2 tex;
glm::vec4 bounds;
TextureVertex() {}
TextureVertex(const glm::vec2& pos, const glm::vec2& tex) : pos(pos), tex(tex) {}
TextureVertex(const glm::vec2& pos, const glm::vec2& tex, const glm::vec4& bounds) : pos(pos), tex(tex), bounds(bounds) {}
};
static const int NUMBER_OF_INDICES_PER_QUAD = 6; // 1 quad = 2 triangles
static const int VERTICES_PER_QUAD = 4; // 1 quad = 4 vertices
static const int NUMBER_OF_INDICES_PER_QUAD = 6; // 1 quad = 2 triangles
static const int VERTICES_PER_QUAD = 4; // 1 quad = 4 vertices (must match value in sdf_text3D.slv)
const float DOUBLE_MAX_OFFSET_PIXELS = 20.0f; // must match value in sdf_text3D.slh
struct QuadBuilder {
TextureVertex vertices[VERTICES_PER_QUAD];
QuadBuilder(const glm::vec2& min, const glm::vec2& size,
const glm::vec2& texMin, const glm::vec2& texSize) {
QuadBuilder(const Glyph& glyph, const glm::vec2& offset, float scale, bool enlargeForShadows) {
glm::vec2 min = offset + glm::vec2(glyph.offset.x, glyph.offset.y - glyph.size.y);
glm::vec2 size = glyph.size;
glm::vec2 texMin = glyph.texOffset;
glm::vec2 texSize = glyph.texSize;
// We need the pre-adjustment bounds for clamping
glm::vec4 bounds = glm::vec4(texMin, texSize);
if (enlargeForShadows) {
glm::vec2 imageSize = glyph.size / glyph.texSize;
glm::vec2 sizeDelta = 0.5f * DOUBLE_MAX_OFFSET_PIXELS * scale * imageSize;
glm::vec2 oldSize = size;
size += sizeDelta;
min.y -= sizeDelta.y;
texSize = texSize * (size / oldSize);
}
// min = bottomLeft
vertices[0] = TextureVertex(min,
texMin + glm::vec2(0.0f, texSize.y));
texMin + glm::vec2(0.0f, texSize.y), bounds);
vertices[1] = TextureVertex(min + glm::vec2(size.x, 0.0f),
texMin + texSize);
texMin + texSize, bounds);
vertices[2] = TextureVertex(min + glm::vec2(0.0f, size.y),
texMin);
texMin, bounds);
vertices[3] = TextureVertex(min + size,
texMin + glm::vec2(texSize.x, 0.0f));
texMin + glm::vec2(texSize.x, 0.0f), bounds);
}
QuadBuilder(const Glyph& glyph, const glm::vec2& offset) :
QuadBuilder(offset + glm::vec2(glyph.offset.x, glyph.offset.y - glyph.size.y), glyph.size,
glyph.texOffset, glyph.texSize) {}
};
static QHash<QString, Font::Pointer> LOADED_FONTS;
Font::Pointer Font::load(QIODevice& fontFile) {
Pointer font = std::make_shared<Font>();
font->read(fontFile);
return font;
}
static QHash<QString, Font::Pointer> LOADED_FONTS;
Font::Pointer Font::load(const QString& family) {
std::lock_guard<std::mutex> lock(fontMutex);
if (!LOADED_FONTS.contains(family)) {
static const QString SDFF_COURIER_PRIME_FILENAME{ ":/CourierPrime.sdff" };
static const QString SDFF_INCONSOLATA_MEDIUM_FILENAME{ ":/InconsolataMedium.sdff" };
static const QString SDFF_ROBOTO_FILENAME{ ":/Roboto.sdff" };
static const QString SDFF_TIMELESS_FILENAME{ ":/Timeless.sdff" };
QString loadFilename;
if (family == MONO_FONT_FAMILY) {
loadFilename = SDFF_COURIER_PRIME_FILENAME;
if (family == ROBOTO_FONT_FAMILY) {
loadFilename = ":/Roboto.sdff";
} else if (family == INCONSOLATA_FONT_FAMILY) {
loadFilename = SDFF_INCONSOLATA_MEDIUM_FILENAME;
} else if (family == SANS_FONT_FAMILY) {
loadFilename = SDFF_ROBOTO_FILENAME;
loadFilename = ":/InconsolataMedium.sdff";
} else if (family == COURIER_FONT_FAMILY) {
loadFilename = ":/CourierPrime.sdff";
} else if (family == TIMELESS_FONT_FAMILY) {
loadFilename = ":/Timeless.sdff";
} else if (family.startsWith("http")) {
auto loadingFont = std::make_shared<Font>();
loadingFont->setLoaded(false);
LOADED_FONTS[family] = loadingFont;
auto& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest;
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
networkRequest.setUrl(family);
auto networkReply = networkAccessManager.get(networkRequest);
connect(networkReply, &QNetworkReply::finished, loadingFont.get(), &Font::handleFontNetworkReply);
} else if (!LOADED_FONTS.contains(ROBOTO_FONT_FAMILY)) {
// Unrecognized font and we haven't loaded Roboto yet
loadFilename = ":/Roboto.sdff";
} else {
if (!LOADED_FONTS.contains(SERIF_FONT_FAMILY)) {
loadFilename = SDFF_TIMELESS_FILENAME;
} else {
LOADED_FONTS[family] = LOADED_FONTS[SERIF_FONT_FAMILY];
}
// Unrecognized font but we've already loaded Roboto
LOADED_FONTS[family] = LOADED_FONTS[ROBOTO_FONT_FAMILY];
}
if (!loadFilename.isEmpty()) {
@ -96,14 +122,24 @@ Font::Pointer Font::load(const QString& family) {
return LOADED_FONTS[family];
}
Font::Font() {
static bool fontResourceInitComplete = false;
if (!fontResourceInitComplete) {
Q_INIT_RESOURCE(fonts);
fontResourceInitComplete = true;
void Font::handleFontNetworkReply() {
auto requestReply = qobject_cast<QNetworkReply*>(sender());
if (requestReply->error() == QNetworkReply::NoError) {
setLoaded(true);
read(*requestReply);
} else {
qDebug() << "Error downloading " << requestReply->url() << " - " << requestReply->errorString();
}
}
Font::Font() {
static std::once_flag once;
std::call_once(once, []{
Q_INIT_RESOURCE(fonts);
});
}
// NERD RAGE: why doesn't QHash have a 'const T & operator[] const' member
const Glyph& Font::getGlyph(const QChar& c) const {
if (!_glyphs.contains(c)) {
@ -139,7 +175,7 @@ glm::vec2 Font::computeTokenExtent(const QString& token) const {
glm::vec2 Font::computeExtent(const QString& str) const {
glm::vec2 extent = glm::vec2(0.0f, 0.0f);
QStringList lines{ splitLines(str) };
QStringList lines = splitLines(str);
if (!lines.empty()) {
for(const auto& line : lines) {
glm::vec2 tokenExtent = computeTokenExtent(line);
@ -154,7 +190,9 @@ void Font::read(QIODevice& in) {
uint8_t header[4];
readStream(in, header);
if (memcmp(header, "SDFF", 4)) {
qFatal("Bad SDFF file");
qDebug() << "Bad SDFF file";
_loaded = false;
return;
}
uint16_t version;
@ -191,7 +229,9 @@ void Font::read(QIODevice& in) {
// read image data
QImage image;
if (!image.loadFromData(in.readAll(), "PNG")) {
qFatal("Failed to read SDFF image");
qDebug() << "Failed to read SDFF image";
_loaded = false;
return;
}
_glyphs.clear();
@ -212,6 +252,9 @@ void Font::read(QIODevice& in) {
formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::BGRA);
}
// FIXME: We're forcing this to use only one mip, and then manually doing anisotropic filtering in the shader,
// and also calling textureLod. Shouldn't this just use anisotropic filtering and auto-generate mips?
// We should also use smoothstep for anti-aliasing, as explained here: https://github.com/libgdx/libgdx/wiki/Distance-field-fonts
_texture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::SINGLE_MIP,
gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR));
_texture->setStoredMipFormat(formatMip);
@ -244,20 +287,21 @@ void Font::setupGPU() {
}
// Sanity checks
static const int OFFSET = offsetof(TextureVertex, tex);
assert(OFFSET == sizeof(glm::vec2));
assert(sizeof(glm::vec2) == 2 * sizeof(float));
assert(sizeof(TextureVertex) == 2 * sizeof(glm::vec2));
static const int TEX_COORD_OFFSET = offsetof(TextureVertex, tex);
static const int TEX_BOUNDS_OFFSET = offsetof(TextureVertex, bounds);
assert(TEX_COORD_OFFSET == sizeof(glm::vec2));
assert(sizeof(TextureVertex) == 2 * sizeof(glm::vec2) + sizeof(glm::vec4));
assert(sizeof(QuadBuilder) == 4 * sizeof(TextureVertex));
// Setup rendering structures
_format = std::make_shared<gpu::Stream::Format>();
_format->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ), 0);
_format->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), OFFSET);
_format->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), TEX_COORD_OFFSET);
_format->setAttribute(gpu::Stream::TEXCOORD1, 0, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::XYZW), TEX_BOUNDS_OFFSET);
}
}
void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm::vec2& origin, const glm::vec2& bounds) {
void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm::vec2& origin, const glm::vec2& bounds, float scale, bool enlargeForShadows) {
drawInfo.verticesBuffer = std::make_shared<gpu::Buffer>();
drawInfo.indicesBuffer = std::make_shared<gpu::Buffer>();
drawInfo.indexCount = 0;
@ -267,6 +311,8 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
drawInfo.bounds = bounds;
drawInfo.origin = origin;
float enlargedBoundsX = bounds.x - 0.5f * DOUBLE_MAX_OFFSET_PIXELS * float(enlargeForShadows);
// Top left of text
glm::vec2 advance = origin;
foreach(const QString& token, tokenizeForWrapping(str)) {
@ -274,7 +320,7 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
bool forceNewLine = false;
// Handle wrapping
if (!isNewLine && (bounds.x != -1) && (advance.x + computeExtent(token).x > origin.x + bounds.x)) {
if (!isNewLine && (bounds.x != -1) && (advance.x + computeExtent(token).x > origin.x + enlargedBoundsX)) {
// We are out of the x bound, force new line
forceNewLine = true;
}
@ -285,7 +331,7 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
if (isNewLine) {
// No need to draw anything, go directly to next token
continue;
} else if (computeExtent(token).x > bounds.x) {
} else if (computeExtent(token).x > enlargedBoundsX) {
// token will never fit, stop drawing
break;
}
@ -301,10 +347,10 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
auto glyph = _glyphs[c];
quint16 verticesOffset = numVertices;
QuadBuilder qd(glyph, advance - glm::vec2(0.0f, _ascent));
QuadBuilder qd(glyph, advance - glm::vec2(0.0f, _ascent), scale, enlargeForShadows);
drawInfo.verticesBuffer->append(qd);
numVertices += 4;
numVertices += VERTICES_PER_QUAD;
// Sam's recommended triangle slices
// Triangle tri1 = { v0, v1, v3 };
// Triangle tri2 = { v1, v2, v3 };
@ -331,7 +377,6 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
indices[5] = verticesOffset + 3;
drawInfo.indicesBuffer->append(sizeof(indices), (const gpu::Byte*)indices);
drawInfo.indexCount += NUMBER_OF_INDICES_PER_QUAD;
// Advance by glyph size
advance.x += glyph.d;
@ -344,38 +389,49 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
}
void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const QString& str, const glm::vec4& color,
EffectType effectType, const glm::vec2& origin, const glm::vec2& bounds, bool unlit, bool forward) {
if (str == "") {
const glm::vec3& effectColor, float effectThickness, TextEffect effect,
const glm::vec2& origin, const glm::vec2& bounds, float scale, bool unlit, bool forward) {
if (!_loaded || str == "") {
return;
}
if (str != drawInfo.string || bounds != drawInfo.bounds || origin != drawInfo.origin) {
buildVertices(drawInfo, str, origin, bounds);
int textEffect = (int)effect;
const int SHADOW_EFFECT = (int)TextEffect::SHADOW_EFFECT;
// If we're switching to or from shadow effect mode, we need to rebuild the vertices
if (str != drawInfo.string || bounds != drawInfo.bounds || origin != drawInfo.origin ||
(drawInfo.params.effect != textEffect && (textEffect == SHADOW_EFFECT || drawInfo.params.effect == SHADOW_EFFECT)) ||
(textEffect == SHADOW_EFFECT && scale != _scale)) {
_scale = scale;
buildVertices(drawInfo, str, origin, bounds, scale, textEffect == SHADOW_EFFECT);
}
setupGPU();
struct GpuDrawParams {
glm::vec4 color;
glm::vec4 outline;
};
if (!drawInfo.paramsBuffer || drawInfo.params.color != color || drawInfo.params.effect != effectType) {
if (!drawInfo.paramsBuffer || drawInfo.params.color != color || drawInfo.params.effectColor != effectColor ||
drawInfo.params.effectThickness != effectThickness || drawInfo.params.effect != textEffect) {
drawInfo.params.color = color;
drawInfo.params.effect = effectType;
GpuDrawParams gpuDrawParams;
drawInfo.params.effectColor = effectColor;
drawInfo.params.effectThickness = effectThickness;
drawInfo.params.effect = textEffect;
// need the gamma corrected color here
DrawParams gpuDrawParams;
gpuDrawParams.color = ColorUtils::sRGBToLinearVec4(drawInfo.params.color);
gpuDrawParams.outline.x = (drawInfo.params.effect == OUTLINE_EFFECT) ? 1 : 0;
drawInfo.paramsBuffer = std::make_shared<gpu::Buffer>(sizeof(GpuDrawParams), nullptr);
drawInfo.paramsBuffer->setSubData(0, sizeof(GpuDrawParams), (const gpu::Byte*)&gpuDrawParams);
gpuDrawParams.effectColor = ColorUtils::sRGBToLinearVec3(drawInfo.params.effectColor);
gpuDrawParams.effectThickness = drawInfo.params.effectThickness;
gpuDrawParams.effect = drawInfo.params.effect;
if (!drawInfo.paramsBuffer) {
drawInfo.paramsBuffer = std::make_shared<gpu::Buffer>(sizeof(DrawParams), nullptr);
}
drawInfo.paramsBuffer->setSubData(0, sizeof(DrawParams), (const gpu::Byte*)&gpuDrawParams);
}
// need the gamma corrected color here
batch.setPipeline(_pipelines[std::make_tuple(color.a < 1.0f, unlit, forward)]);
batch.setInputFormat(_format);
batch.setInputBuffer(0, drawInfo.verticesBuffer, 0, _format->getChannels().at(0)._stride);
batch.setResourceTexture(render_utils::slot::texture::TextFont, _texture);
batch.setUniformBuffer(0, drawInfo.paramsBuffer, 0, sizeof(GpuDrawParams));
batch.setUniformBuffer(0, drawInfo.paramsBuffer, 0, sizeof(DrawParams));
batch.setIndexBuffer(gpu::UINT16, drawInfo.indicesBuffer, 0);
batch.drawIndexed(gpu::TRIANGLES, drawInfo.indexCount, 0);
}

View file

@ -10,12 +10,16 @@
#ifndef hifi_Font_h
#define hifi_Font_h
#include <QObject>
#include "Glyph.h"
#include "EffectType.h"
#include "TextEffect.h"
#include <gpu/Batch.h>
#include <gpu/Pipeline.h>
class Font {
class Font : public QObject {
Q_OBJECT
public:
using Pointer = std::shared_ptr<Font>;
@ -24,14 +28,23 @@ public:
void read(QIODevice& path);
struct DrawParams {
vec4 color{ -1 };
EffectType effect;
vec4 color { 0 };
vec3 effectColor { 0 };
float effectThickness { 0 };
int effect { 0 };
#if defined(__clang__)
__attribute__((unused))
#endif
vec3 _spare;
};
struct DrawInfo {
gpu::BufferPointer verticesBuffer;
gpu::BufferPointer indicesBuffer;
gpu::BufferPointer paramsBuffer;
gpu::BufferPointer verticesBuffer { nullptr };
gpu::BufferPointer indicesBuffer { nullptr };
gpu::BufferPointer paramsBuffer { nullptr };
uint32_t indexCount;
QString string;
@ -44,12 +57,18 @@ public:
float getFontSize() const { return _fontSize; }
// Render string to batch
void drawString(gpu::Batch& batch, DrawInfo& drawInfo, const QString& str,
const glm::vec4& color, EffectType effectType,
const glm::vec2& origin, const glm::vec2& bound, bool unlit, bool forward);
void drawString(gpu::Batch& batch, DrawInfo& drawInfo, const QString& str, const glm::vec4& color,
const glm::vec3& effectColor, float effectThickness, TextEffect effect,
const glm::vec2& origin, const glm::vec2& bound, float scale, bool unlit, bool forward);
static Pointer load(const QString& family);
bool isLoaded() const { return _loaded; }
void setLoaded(bool loaded) { _loaded = loaded; }
public slots:
void handleFontNetworkReply();
private:
static Pointer load(QIODevice& fontFile);
QStringList tokenizeForWrapping(const QString& str) const;
@ -57,7 +76,7 @@ private:
glm::vec2 computeTokenExtent(const QString& str) const;
const Glyph& getGlyph(const QChar& c) const;
void buildVertices(DrawInfo& drawInfo, const QString& str, const glm::vec2& origin, const glm::vec2& bounds);
void buildVertices(DrawInfo& drawInfo, const QString& str, const glm::vec2& origin, const glm::vec2& bounds, float scale, bool enlargeForShadows);
void setupGPU();
@ -70,11 +89,15 @@ private:
// Font characteristics
QString _family;
float _fontSize = 0.0f;
float _leading = 0.0f;
float _ascent = 0.0f;
float _descent = 0.0f;
float _spaceWidth = 0.0f;
float _fontSize { 0.0f };
float _leading { 0.0f };
float _ascent { 0.0f };
float _descent { 0.0f };
float _spaceWidth { 0.0f };
float _scale { 0.0f };
bool _loaded { true };
gpu::TexturePointer _texture;
gpu::BufferStreamPointer _stream;

View file

@ -1,31 +0,0 @@
//
// Created by Bradley Austin Davis on 2015/07/16
// Copyright 2013 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
//
#pragma once
#ifndef hifi_FontFamilies_h
#define hifi_FontFamilies_h
// the standard sans serif font family
#define SANS_FONT_FAMILY "Helvetica"
// the standard sans serif font family
#define SERIF_FONT_FAMILY "Timeless"
// the standard mono font family
#define MONO_FONT_FAMILY "Courier"
// the Inconsolata font family
#ifdef Q_OS_WIN
#define INCONSOLATA_FONT_FAMILY "Fixedsys"
#define INCONSOLATA_FONT_WEIGHT QFont::Normal
#else
#define INCONSOLATA_FONT_FAMILY "Inconsolata"
#define INCONSOLATA_FONT_WEIGHT QFont::Bold
#endif
#endif

View file

@ -18,5 +18,6 @@ void Glyph::read(QIODevice& in) {
readStream(in, size);
readStream(in, offset);
readStream(in, d);
// texSize is divided by the image size later
texSize = size;
}

View file

@ -401,13 +401,14 @@ void ScriptEngines::stopAllScripts(bool restart) {
continue;
}
bool isOverrideScript = it.key().toString().compare(this->_defaultScriptsOverride.toString());
// queue user scripts if restarting
if (restart && scriptEngine->isUserLoaded()) {
if (restart && (scriptEngine->isUserLoaded() || isOverrideScript)) {
_isReloading = true;
ScriptEngine::Type type = scriptEngine->getType();
connect(scriptEngine.data(), &ScriptEngine::finished, this, [this, type] (QString scriptName) {
reloadScript(scriptName, true)->setType(type);
connect(scriptEngine.data(), &ScriptEngine::finished, this, [this, type, isOverrideScript] (QString scriptName) {
reloadScript(scriptName, !isOverrideScript)->setType(type);
});
}

View file

@ -0,0 +1,17 @@
//
// Created by Bradley Austin Davis on 2015/07/16
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_FontFamilies_h
#define hifi_FontFamilies_h
#define ROBOTO_FONT_FAMILY "Roboto"
#define COURIER_FONT_FAMILY "Courier"
#define INCONSOLATA_FONT_FAMILY "Inconsolata"
#define TIMELESS_FONT_FAMILY "Timeless"
#endif // hifi_FontFamilies_h

View file

@ -0,0 +1,26 @@
//
// Created by Sam Gondelman on 7/21/2019
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "TextEffect.h"
const char* textEffectNames[] = {
"none",
"outline",
"outline fill",
"shadow"
};
static const size_t TEXT_EFFECT_NAMES = (sizeof(textEffectNames) / sizeof(textEffectNames[0]));
QString TextEffectHelpers::getNameForTextEffect(TextEffect effect) {
if (((int)effect <= 0) || ((int)effect >= (int)TEXT_EFFECT_NAMES)) {
effect = (TextEffect)0;
}
return textEffectNames[(int)effect];
}

View file

@ -0,0 +1,42 @@
//
// Created by Bradley Austin Davis on 2015/07/16
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_TextEffect_h
#define hifi_TextEffect_h
#include "QString"
/**jsdoc
* <p>A {@link Entities.EntityProperties-Text|Text} entity may use one of the following effects:</p>
* <table>
* <thead>
* <tr><th>Value</th><th>Description</th></tr>
* </thead>
* <tbody>
* <tr><td><code>"none"</code></td><td>No effect.</td></tr>
* <tr><td><code>"outline"</code></td><td>An outline effect.</td></tr>
* <tr><td><code>"outlineFill"</code></td><td>An outline effect, with fill.</td></tr>
* <tr><td><code>"shadow"</code></td><td>A shadow effect.</td></tr>
* </tbody>
* </table>
* @typedef {string} Entities.TextEffect
*/
enum class TextEffect {
NO_EFFECT = 0,
OUTLINE_EFFECT,
OUTLINE_WITH_FILL_EFFECT,
SHADOW_EFFECT
};
class TextEffectHelpers {
public:
static QString getNameForTextEffect(TextEffect effect);
};
#endif // hifi_TextEffect_h

View file

@ -264,7 +264,19 @@ void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) {
}
auto rootContext = engine->rootContext();
rootContext->setContextProperty("GL", ::getGLContextData());
static QJsonObject QML_GL_INFO;
static std::once_flag once_gl_info;
std::call_once(once_gl_info, [] {
const auto& contextInfo = gl::ContextInfo::get();
QML_GL_INFO = QJsonObject {
{ "version", contextInfo.version.c_str() },
{ "sl_version", contextInfo.shadingLanguageVersion.c_str() },
{ "vendor", contextInfo.vendor.c_str() },
{ "renderer", contextInfo.renderer.c_str() },
};
});
rootContext->setContextProperty("GL", QML_GL_INFO);
rootContext->setContextProperty("urlHandler", new UrlHandler(rootContext));
rootContext->setContextProperty("resourceDirectoryUrl", QUrl::fromLocalFile(PathUtils::resourcesPath()));
rootContext->setContextProperty("ApplicationInterface", qApp);