mirror of
https://github.com/overte-org/overte.git
synced 2025-04-11 16:03:24 +02:00
merge with master
This commit is contained in:
commit
42d047c572
195 changed files with 5008 additions and 2247 deletions
|
@ -68,7 +68,7 @@ module.exports = {
|
|||
"eqeqeq": ["error", "always"],
|
||||
"indent": ["error", 4, { "SwitchCase": 1 }],
|
||||
"keyword-spacing": ["error", { "before": true, "after": true }],
|
||||
"max-len": ["error", 192, 4],
|
||||
"max-len": ["error", 128, 4],
|
||||
"new-cap": ["error"],
|
||||
"no-floating-decimal": ["error"],
|
||||
//"no-magic-numbers": ["error", { "ignore": [0, 1], "ignoreArrayIndexes": true }],
|
||||
|
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -64,6 +64,10 @@ gvr-interface/libs/*
|
|||
# ignore files for various dev environments
|
||||
TAGS
|
||||
*.sw[po]
|
||||
*.qmlc
|
||||
|
||||
# ignore QML compilation output
|
||||
*.qmlc
|
||||
|
||||
# ignore node files for the console
|
||||
node_modules
|
||||
|
|
|
@ -28,6 +28,10 @@
|
|||
const QString ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME = "assignment-client-monitor";
|
||||
const int WAIT_FOR_CHILD_MSECS = 1000;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
HANDLE PROCESS_GROUP = createProcessGroup();
|
||||
#endif
|
||||
|
||||
AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmentClientForks,
|
||||
const unsigned int minAssignmentClientForks,
|
||||
const unsigned int maxAssignmentClientForks,
|
||||
|
@ -202,6 +206,10 @@ void AssignmentClientMonitor::spawnChildClient() {
|
|||
assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
assignmentClient->start(QCoreApplication::applicationFilePath(), _childArguments);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
addProcessToGroup(PROCESS_GROUP, assignmentClient->processId());
|
||||
#endif
|
||||
|
||||
QString stdoutPath, stderrPath;
|
||||
|
||||
if (_wantsChildFileLogging) {
|
||||
|
|
|
@ -82,8 +82,12 @@ bool OctreeSendThread::process() {
|
|||
if (auto node = _node.lock()) {
|
||||
OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(node->getLinkedData());
|
||||
|
||||
// Sometimes the node data has not yet been linked, in which case we can't really do anything
|
||||
if (nodeData && !nodeData->isShuttingDown()) {
|
||||
// If we don't have the OctreeQueryNode at all
|
||||
// or it's uninitialized because we haven't received a query yet from the client
|
||||
// or we don't know where we should send packets for this node
|
||||
// or we're shutting down
|
||||
// then we can't send an entity data packet
|
||||
if (nodeData && nodeData->hasReceivedFirstQuery() && node->getActiveSocket() && !nodeData->isShuttingDown()) {
|
||||
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
|
||||
packetDistributor(node, nodeData, viewFrustumChanged);
|
||||
}
|
||||
|
|
76
cmake/externals/hifiAudioCodec/CMakeLists.txt
vendored
76
cmake/externals/hifiAudioCodec/CMakeLists.txt
vendored
|
@ -5,43 +5,41 @@ set(EXTERNAL_NAME hifiAudioCodec)
|
|||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
if (NOT ANDROID)
|
||||
|
||||
if (WIN32 OR APPLE)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-1.zip
|
||||
URL_MD5 23ec3fe51eaa155ea159a4971856fc13
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1
|
||||
)
|
||||
else ()
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-linux.zip
|
||||
URL_MD5 7d37914a18aa4de971d2f45dd3043bde
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1
|
||||
)
|
||||
endif()
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE INTERNAL)
|
||||
|
||||
if (WIN32)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/audio.lib CACHE TYPE INTERNAL)
|
||||
elseif(APPLE)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL)
|
||||
elseif(NOT ANDROID)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-win-2.0.zip)
|
||||
set(DOWNLOAD_MD5 9199d4dbd6b16bed736b235efe980e67)
|
||||
elseif (APPLE)
|
||||
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-mac-2.0.zip)
|
||||
set(DOWNLOAD_MD5 21649881e7d5dc94f922179be96f76ba)
|
||||
elseif (ANDROID)
|
||||
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-android-2.0.zip)
|
||||
set(DOWNLOAD_MD5 aef2a852600d498d58aa586668191683)
|
||||
elseif (UNIX)
|
||||
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/codecSDK-linux-2.0.zip)
|
||||
set(DOWNLOAD_MD5 67fb7755f9bcafb98a9fceea53bc7481)
|
||||
else()
|
||||
return()
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL ${DOWNLOAD_URL}
|
||||
URL_MD5 ${DOWNLOAD_MD5}
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1
|
||||
)
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE INTERNAL)
|
||||
|
||||
if (WIN32)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/audio.lib CACHE TYPE INTERNAL)
|
||||
else()
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/Release/libaudio.a CACHE TYPE INTERNAL)
|
||||
endif()
|
||||
|
|
|
@ -19,12 +19,13 @@
|
|||
Upload an entities file (e.g.: models.json.gz) to replace the content of this domain.<br>
|
||||
Note: <strong>Your domain's content will be replaced by the content you upload</strong>, but the backup files of your domain's content will not immediately be changed.
|
||||
</p>
|
||||
<p>
|
||||
If your domain has any content that you would like to re-use at a later date, save a manual backup of your models.json.gz file, which is usually stored at the following paths:<br>
|
||||
<pre>C:/Users/[username]/AppData/Roaming/High Fidelity/assignment-client/entities/models.json.gz</pre>
|
||||
<pre>/Users/[username]/Library/Application Support/High Fidelity/assignment-client/entities/models.json.gz</pre>
|
||||
<pre>/home/[username]/.local/share/High Fidelity/assignment-client/entities/models.json.gz</pre>
|
||||
</p>
|
||||
<p>If your domain has any content that you would like to re-use at a later date, save a manual backup of your models.json.gz file, which is usually stored at the following paths:</p>
|
||||
<label class="control-label">Windows</label>
|
||||
<pre>C:/Users/[username]/AppData/Roaming/High Fidelity/assignment-client/entities/models.json.gz</pre>
|
||||
<label class="control-label">OSX</label>
|
||||
<pre>/Users/[username]/Library/Application Support/High Fidelity/assignment-client/entities/models.json.gz</pre>
|
||||
<label class="control-label">Linux</label>
|
||||
<pre>/home/[username]/.local/share/High Fidelity/assignment-client/entities/models.json.gz</pre>
|
||||
<br>
|
||||
<input type="file" name="entities-file" class="form-control-file" accept=".json, .gz">
|
||||
<br>
|
||||
|
|
|
@ -307,7 +307,6 @@ table .headers + .headers td {
|
|||
margin-right: 20px;
|
||||
}
|
||||
|
||||
#visit-domain-link,
|
||||
.blue-link {
|
||||
font-size: 14px;
|
||||
text-decoration-line: underline;
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
<li><a href="/settings/">Settings</a></li>
|
||||
</ul>
|
||||
<ul class="nav navbar-right navbar-nav">
|
||||
<li><a id="visit-domain-link" target="_blank" style="display: none;">Visit domain in VR</a></li>
|
||||
<li><a id="visit-domain-link" class="blue-link" target="_blank" style="display: none;">Visit domain in VR</a></li>
|
||||
<li><a href="#" id="restart-server"><span class="glyphicon glyphicon-refresh"></span> Restart</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -31,6 +31,12 @@ label {
|
|||
color: #373A3C;
|
||||
}
|
||||
|
||||
.wizard-link {
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
color: #2F80ED;
|
||||
}
|
||||
|
||||
#admin-row {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
|
@ -84,6 +90,14 @@ label {
|
|||
height: 169px;
|
||||
}
|
||||
|
||||
#visit-domain-row {
|
||||
margin-bottom: 68px;
|
||||
#congratulation-text {
|
||||
margin-bottom: 59px;
|
||||
}
|
||||
|
||||
#visit-domain-checkbox {
|
||||
margin-bottom: 23px;
|
||||
}
|
||||
|
||||
#visit-domain-checkbox label {
|
||||
margin: 0 0;
|
||||
}
|
||||
|
|
|
@ -60,8 +60,7 @@
|
|||
</div>
|
||||
<div id="admin-row" class="row">
|
||||
<p class="col-md-6">
|
||||
<span id="admin-description" class="step-info"><b>Add your High Fidelity username</b> and any other usernames to grant administrator privileges.</span>
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle="tooltip" title="Users who will have all the permissions for this domain."></span>
|
||||
<span class="step-info"><span id="admin-description"><b>Add your High Fidelity username</b> and any other usernames</span> to grant <span class='wizard-link' data-toggle="tooltip" title="Users who will have all the permissions for this domain.">administrator privileges</span></span>
|
||||
</p>
|
||||
<div class="col-md-6">
|
||||
<input id="admin-usernames" type="text" class="form-control">
|
||||
|
@ -78,7 +77,7 @@
|
|||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p id="connect-question" class="step-info">
|
||||
Who can connect to your domain?
|
||||
Who can <a href='#' class='wizard-link perms-link'>connect</a> to your domain?
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='You can set this to allow a user to connect to this domain.'></span>
|
||||
</p>
|
||||
</div>
|
||||
|
@ -87,25 +86,21 @@
|
|||
<p class="col-md-2">
|
||||
<label>
|
||||
<input id="connect-none" name="connect-radio" type="radio" value="none" checked> None
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='Only the admins of this domain'></span>
|
||||
</label>
|
||||
</p>
|
||||
<p class="col-md-3">
|
||||
<label>
|
||||
<input id="connect-friends" name="connect-radio" type="radio" value="friends"> Friends
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='Users who are your Friends in High Fidelity'></span>
|
||||
</label>
|
||||
</p>
|
||||
<p class="col-md-5">
|
||||
<label>
|
||||
<input id="connect-logged-in" name="connect-radio" type="radio" value="logged-in"> Users logged in to High Fidelity
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='Users who are currently logged into High Fidelity'></span>
|
||||
<input id="connect-logged-in" name="connect-radio" type="radio" value="logged-in"> Users logged into High Fidelity
|
||||
</label>
|
||||
</p>
|
||||
<p class="col-md-2">
|
||||
<label>
|
||||
<input id="connect-everyone" name="connect-radio" type="radio" value="everyone"> Everyone
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title="Users who aren't logged into High Fidelity"></span>
|
||||
</label>
|
||||
</p>
|
||||
</div>
|
||||
|
@ -113,7 +108,7 @@
|
|||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p class="step-info">
|
||||
Who can rez items in your domain?
|
||||
Who can <a href='#' class='wizard-link perms-link'>rez items</a> in your domain?
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='You can set this to allow a user to create entities in this domain.'></span>
|
||||
</p>
|
||||
</div>
|
||||
|
@ -122,33 +117,32 @@
|
|||
<p class="col-md-2">
|
||||
<label>
|
||||
<input id="rez-none" name="rez-radio" type="radio" value="none" checked> None
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='Only the admins of this domain'></span>
|
||||
</label>
|
||||
</p>
|
||||
<p class="col-md-3">
|
||||
<label>
|
||||
<input id="rez-friends" name="rez-radio" type="radio" value="friends"> Friends
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='Users who are your Friends in High Fidelity'></span>
|
||||
<input id="rez-friends" name="rez-radio" type="radio" value="friends" disabled> Friends
|
||||
</label>
|
||||
</p>
|
||||
<p class="col-md-5">
|
||||
<label>
|
||||
<input id="rez-logged-in" name="rez-radio" type="radio" value="logged-in"> Users logged in to High Fidelity
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title='Users who are currently logged into High Fidelity'></span>
|
||||
<input id="rez-logged-in" name="rez-radio" type="radio" value="logged-in" disabled> Users logged into High Fidelity
|
||||
</label>
|
||||
</p>
|
||||
<p class="col-md-2">
|
||||
<label>
|
||||
<input id="rez-everyone" name="rez-radio" type="radio" value="everyone"> Everyone
|
||||
<span class='glyphicon glyphicon-info-sign' data-toggle='tooltip' title="Users who aren't logged into High Fidelity"></span>
|
||||
<input id="rez-everyone" name="rez-radio" type="radio" value="everyone" disabled> Everyone
|
||||
</label>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-md-offset-9">
|
||||
<dd class="col-md-3">
|
||||
<button type="button" class="btn btn-md btn-block btn-default back-button">Back</button>
|
||||
</dd>
|
||||
<dd class="col-md-3 col-md-offset-6">
|
||||
<button id="save-permissions" type="button" class="btn btn-md btn-block btn-primary">Next</button>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -188,35 +182,37 @@
|
|||
</dl>
|
||||
|
||||
<dl class="row">
|
||||
<dd class="col-md-3 col-md-offset-9">
|
||||
<dd class="col-md-3">
|
||||
<button type="button" class="btn btn-md btn-block btn-default back-button">Back</button>
|
||||
</dd>
|
||||
<dd class="col-md-3 col-md-offset-6">
|
||||
<button id="save-username-password" type="button" class="btn btn-md btn-block btn-primary">Finish</button>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="wizard-step cloud-only col-md-7 col-centered" style="display: none;">
|
||||
<div class="wizard-step cloud-only col-xs-12 col-sm-12 col-md-9 col-lg-7 col-centered" style="display: none;">
|
||||
<div class="row">
|
||||
<div class="col-xs-4 col-centered">
|
||||
<div class="col-xs-12" align="center">
|
||||
<img id="checkmark-image" src="../images/checkmark.svg">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div id="congratulation-text" class="row">
|
||||
<div class="col-xs-12">
|
||||
<p class="step-info">Congratulations! You have successfully setup and configured your cloud hosted domain.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="visit-domain-row" class="row">
|
||||
<div class="col-md-12">
|
||||
<label><input id="go-to-domain" class="form-check-input" type="checkbox"> Visit domain in VR now</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<dl class="row">
|
||||
<dd class="col-md-5 col-md-offset-7">
|
||||
<button id="explore-settings" type="button" class="btn btn-md btn-block btn-primary">Explore all domain server settings</button>
|
||||
</dd>
|
||||
<div class="col-xs-12">
|
||||
<div class="pull-right">
|
||||
<div id="visit-domain-checkbox">
|
||||
<label><input id="go-to-domain" class="form-check-input" type="checkbox"> Visit domain in VR now</label>
|
||||
</div>
|
||||
<button id="explore-settings" type="button" class="btn btn-md btn-primary">Explore all domain server settings</button>
|
||||
</div>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@ var Metaverse = {
|
|||
accessToken: null
|
||||
}
|
||||
|
||||
var currentStepNumber;
|
||||
|
||||
$(document).ready(function(){
|
||||
Strings.ADD_PLACE_NOT_CONNECTED_MESSAGE = "You must have an access token to query your High Fidelity places.<br><br>" +
|
||||
"Please go back and connect your account.";
|
||||
|
@ -9,6 +11,22 @@ $(document).ready(function(){
|
|||
$('#connect-account-btn').attr('href', URLs.METAVERSE_URL + "/user/tokens/new?for_domain_server=true");
|
||||
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
|
||||
$('.perms-link').on('click', function() {
|
||||
var modal_body = '<div>';
|
||||
modal_body += '<b>None</b> - No one will have permissions. Only you and the users your have given administrator privileges to will have permissions.</br></br>';
|
||||
modal_body += '<b>Friends</b> - Users who are your Friends in High Fidelity.</br></br>';
|
||||
modal_body += '<b>Users logged into High Fidelity</b> - Users who are currently logged into High Fidelity.</br></br>';
|
||||
modal_body += '<b>Everyone</b> - Anyone who uses High Fidelity.';
|
||||
modal_body += '</div>';
|
||||
|
||||
dialog = bootbox.dialog({
|
||||
title: "User definition",
|
||||
message: modal_body,
|
||||
closeButton: true
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('body').on('click', '.next-button', function() {
|
||||
goToNextStep();
|
||||
|
@ -56,6 +74,36 @@ $(document).ready(function(){
|
|||
exploreSettings();
|
||||
});
|
||||
|
||||
$('input[type=radio][name=connect-radio]').change(function() {
|
||||
var inputs = $('input[type=radio][name=rez-radio]');
|
||||
var disabled = [];
|
||||
|
||||
switch (this.value) {
|
||||
case 'none':
|
||||
disabled = inputs.splice(1);
|
||||
break;
|
||||
case 'friends':
|
||||
disabled = inputs.splice(2);
|
||||
break;
|
||||
case 'logged-in':
|
||||
disabled = inputs.splice(3);
|
||||
break;
|
||||
case 'everyone':
|
||||
disabled = inputs.splice(4);
|
||||
break;
|
||||
}
|
||||
|
||||
$.each(inputs, function() {
|
||||
$(this).prop('disabled', false);
|
||||
});
|
||||
$.each(disabled, function() {
|
||||
if ($(this).prop('checked')) {
|
||||
$(inputs.last()).prop('checked', true);
|
||||
}
|
||||
$(this).prop('disabled', true);
|
||||
});
|
||||
});
|
||||
|
||||
reloadSettings(function(success) {
|
||||
if (success) {
|
||||
getDomainFromAPI();
|
||||
|
@ -73,12 +121,12 @@ $(document).ready(function(){
|
|||
});
|
||||
|
||||
function setupWizardSteps() {
|
||||
var stepsCompleted = Settings.data.values.wizard.steps_completed;
|
||||
currentStepNumber = Settings.data.values.wizard.steps_completed;
|
||||
var steps = null;
|
||||
|
||||
if (Settings.data.values.wizard.cloud_domain) {
|
||||
$('.desktop-only').remove();
|
||||
$('.wizard-step').find('.back-button').hide();
|
||||
$('.wizard-step:first').find('.back-button').hide();
|
||||
|
||||
steps = $('.wizard-step');
|
||||
$(steps).each(function(i) {
|
||||
|
@ -86,7 +134,7 @@ function setupWizardSteps() {
|
|||
});
|
||||
|
||||
$('#permissions-description').html('You <span id="username-display"></span>have been assigned administrator privileges to this domain.');
|
||||
$('#admin-description').html('Add more High Fidelity usernames to grant administrator privileges.');
|
||||
$('#admin-description').html('Add more High Fidelity usernames');
|
||||
} else {
|
||||
$('.cloud-only').remove();
|
||||
$('#save-permissions').text("Finish");
|
||||
|
@ -96,12 +144,12 @@ function setupWizardSteps() {
|
|||
$(this).children(".step-title").text("Step " + (i + 1) + " of " + steps.length);
|
||||
});
|
||||
|
||||
if (stepsCompleted == 0) {
|
||||
if (currentStepNumber == 0) {
|
||||
$('#skip-wizard-button').show();
|
||||
}
|
||||
}
|
||||
|
||||
var currentStep = steps[stepsCompleted];
|
||||
var currentStep = steps[currentStepNumber];
|
||||
$(currentStep).show();
|
||||
}
|
||||
|
||||
|
@ -204,7 +252,7 @@ function goToNextStep() {
|
|||
currentStep.hide();
|
||||
nextStep.show();
|
||||
|
||||
var currentStepNumber = parseInt(Settings.data.values.wizard.steps_completed) + 1;
|
||||
currentStepNumber += 1;
|
||||
|
||||
postSettings({
|
||||
"wizard": {
|
||||
|
@ -233,7 +281,7 @@ function goToPreviousStep() {
|
|||
currentStep.hide();
|
||||
previousStep.show();
|
||||
|
||||
var currentStepNumber = parseInt(Settings.data.values.wizard.steps_completed) - 1;
|
||||
currentStepNumber -= 1;
|
||||
|
||||
postSettings({
|
||||
"wizard": {
|
||||
|
@ -439,7 +487,7 @@ function saveUsernamePassword() {
|
|||
return;
|
||||
}
|
||||
|
||||
var currentStepNumber = parseInt(Settings.data.values.wizard.steps_completed) + 1;
|
||||
currentStepNumber += 1;
|
||||
|
||||
var formJSON = {
|
||||
"security": {
|
||||
|
|
Binary file not shown.
BIN
interface/resources/images/lowerKeyboard.png
Normal file
BIN
interface/resources/images/lowerKeyboard.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1 KiB |
|
@ -1,532 +0,0 @@
|
|||
//
|
||||
// AddressBarDialog.qml
|
||||
//
|
||||
// Created by Austin Davis on 2015/04/14
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import Hifi 1.0
|
||||
import QtQuick 2.4
|
||||
import "controls"
|
||||
import "styles"
|
||||
import "windows"
|
||||
import "hifi"
|
||||
import "hifi/toolbars"
|
||||
import "styles-uit" as HifiStyles
|
||||
import "controls-uit" as HifiControls
|
||||
|
||||
Window {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
HifiStyles.HifiConstants { id: hifiStyleConstants }
|
||||
|
||||
objectName: "AddressBarDialog"
|
||||
title: "Go To:"
|
||||
|
||||
shown: false
|
||||
destroyOnHidden: false
|
||||
resizable: false
|
||||
pinnable: false;
|
||||
|
||||
width: addressBarDialog.implicitWidth
|
||||
height: addressBarDialog.implicitHeight
|
||||
property int gap: 14
|
||||
|
||||
onShownChanged: {
|
||||
addressBarDialog.keyboardEnabled = HMD.active;
|
||||
addressBarDialog.observeShownChanged(shown);
|
||||
}
|
||||
Component.onCompleted: {
|
||||
root.parentChanged.connect(center);
|
||||
center();
|
||||
}
|
||||
Component.onDestruction: {
|
||||
root.parentChanged.disconnect(center);
|
||||
}
|
||||
|
||||
function center() {
|
||||
// Explicitly center in order to avoid warnings at shutdown
|
||||
anchors.centerIn = parent;
|
||||
}
|
||||
|
||||
function resetAfterTeleport() {
|
||||
storyCardFrame.shown = root.shown = false;
|
||||
}
|
||||
function goCard(targetString) {
|
||||
if (0 !== targetString.indexOf('hifi://')) {
|
||||
storyCardHTML.url = addressBarDialog.metaverseServerUrl + targetString;
|
||||
storyCardFrame.shown = true;
|
||||
return;
|
||||
}
|
||||
addressLine.text = targetString;
|
||||
toggleOrGo(true);
|
||||
clearAddressLineTimer.start();
|
||||
}
|
||||
property var allStories: [];
|
||||
property int cardWidth: 212;
|
||||
property int cardHeight: 152;
|
||||
property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/";
|
||||
property bool isCursorVisible: false // Override default cursor visibility.
|
||||
|
||||
AddressBarDialog {
|
||||
id: addressBarDialog
|
||||
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
|
||||
implicitWidth: backgroundImage.width
|
||||
implicitHeight: scroll.height + gap + backgroundImage.height + (keyboardEnabled ? keyboard.height : 0);
|
||||
|
||||
// The buttons have their button state changed on hover, so we have to manually fix them up here
|
||||
onBackEnabledChanged: backArrow.buttonState = addressBarDialog.backEnabled ? 1 : 0;
|
||||
onForwardEnabledChanged: forwardArrow.buttonState = addressBarDialog.forwardEnabled ? 1 : 0;
|
||||
onReceivedHifiSchemeURL: resetAfterTeleport();
|
||||
|
||||
// Update location after using back and forward buttons.
|
||||
onHostChanged: updateLocationTextTimer.start();
|
||||
|
||||
ListModel { id: suggestions }
|
||||
|
||||
ListView {
|
||||
id: scroll
|
||||
height: cardHeight + scroll.stackedCardShadowHeight
|
||||
property int stackedCardShadowHeight: 10;
|
||||
spacing: gap;
|
||||
clip: true;
|
||||
anchors {
|
||||
left: backgroundImage.left
|
||||
right: swipe.left
|
||||
bottom: backgroundImage.top
|
||||
}
|
||||
model: suggestions;
|
||||
orientation: ListView.Horizontal;
|
||||
delegate: Card {
|
||||
width: cardWidth;
|
||||
height: cardHeight;
|
||||
goFunction: goCard;
|
||||
userName: model.username;
|
||||
placeName: model.place_name;
|
||||
hifiUrl: model.place_name + model.path;
|
||||
thumbnail: model.thumbnail_url;
|
||||
imageUrl: model.image_url;
|
||||
action: model.action;
|
||||
timestamp: model.created_at;
|
||||
onlineUsers: model.online_users;
|
||||
storyId: model.metaverseId;
|
||||
drillDownToPlace: model.drillDownToPlace;
|
||||
shadowHeight: scroll.stackedCardShadowHeight;
|
||||
hoverThunk: function () { ListView.view.currentIndex = index; }
|
||||
unhoverThunk: function () { ListView.view.currentIndex = -1; }
|
||||
}
|
||||
highlightMoveDuration: -1;
|
||||
highlightMoveVelocity: -1;
|
||||
highlight: Rectangle { color: "transparent"; border.width: 4; border.color: hifiStyleConstants.colors.blueHighlight; z: 1; }
|
||||
}
|
||||
Image { // Just a visual indicator that the user can swipe the cards over to see more.
|
||||
id: swipe;
|
||||
source: "../images/swipe-chevron.svg";
|
||||
width: 72;
|
||||
visible: suggestions.count > 3;
|
||||
anchors {
|
||||
right: backgroundImage.right;
|
||||
top: scroll.top;
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: scroll.currentIndex = (scroll.currentIndex < 0) ? 3 : (scroll.currentIndex + 3)
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 2 * hifi.layout.spacing;
|
||||
anchors {
|
||||
top: parent.top;
|
||||
left: parent.left;
|
||||
leftMargin: 150;
|
||||
topMargin: -30;
|
||||
}
|
||||
property var selected: allTab;
|
||||
TextButton {
|
||||
id: allTab;
|
||||
text: "ALL";
|
||||
property string includeActions: 'snapshot,concurrency';
|
||||
selected: allTab === selectedTab;
|
||||
action: tabSelect;
|
||||
}
|
||||
TextButton {
|
||||
id: placeTab;
|
||||
text: "PLACES";
|
||||
property string includeActions: 'concurrency';
|
||||
selected: placeTab === selectedTab;
|
||||
action: tabSelect;
|
||||
}
|
||||
TextButton {
|
||||
id: snapsTab;
|
||||
text: "SNAPS";
|
||||
property string includeActions: 'snapshot';
|
||||
selected: snapsTab === selectedTab;
|
||||
action: tabSelect;
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: backgroundImage
|
||||
source: "../images/address-bar-856.svg"
|
||||
width: 856
|
||||
height: 100
|
||||
anchors {
|
||||
bottom: parent.keyboardEnabled ? keyboard.top : parent.bottom;
|
||||
}
|
||||
property int inputAreaHeight: 70
|
||||
property int inputAreaStep: (height - inputAreaHeight) / 2
|
||||
|
||||
ToolbarButton {
|
||||
id: homeButton
|
||||
imageURL: "../images/home.svg"
|
||||
onClicked: {
|
||||
addressBarDialog.loadHome();
|
||||
root.shown = false;
|
||||
}
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: homeButton.width / 2
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
ToolbarButton {
|
||||
id: backArrow;
|
||||
imageURL: "../images/backward.svg";
|
||||
onClicked: addressBarDialog.loadBack();
|
||||
anchors {
|
||||
left: homeButton.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
ToolbarButton {
|
||||
id: forwardArrow;
|
||||
imageURL: "../images/forward.svg";
|
||||
onClicked: addressBarDialog.loadForward();
|
||||
anchors {
|
||||
left: backArrow.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
HifiStyles.RalewayLight {
|
||||
id: notice;
|
||||
font.pixelSize: hifi.fonts.pixelSize * 0.50;
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: parent.inputAreaStep + 12
|
||||
left: addressLine.left
|
||||
right: addressLine.right
|
||||
}
|
||||
}
|
||||
HifiStyles.FiraSansRegular {
|
||||
id: location;
|
||||
font.pixelSize: addressLine.font.pixelSize;
|
||||
color: "gray";
|
||||
clip: true;
|
||||
anchors.fill: addressLine;
|
||||
visible: addressLine.text.length === 0
|
||||
}
|
||||
TextInput {
|
||||
id: addressLine
|
||||
focus: true
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
left: forwardArrow.right
|
||||
right: parent.right
|
||||
leftMargin: forwardArrow.width
|
||||
rightMargin: forwardArrow.width / 2
|
||||
topMargin: parent.inputAreaStep + (2 * hifi.layout.spacing)
|
||||
bottomMargin: parent.inputAreaStep
|
||||
}
|
||||
font.pixelSize: hifi.fonts.pixelSize * 0.75
|
||||
cursorVisible: false
|
||||
onTextChanged: {
|
||||
filterChoicesByText();
|
||||
updateLocationText(text.length > 0);
|
||||
if (!isCursorVisible && text.length > 0) {
|
||||
isCursorVisible = true;
|
||||
cursorVisible = true;
|
||||
}
|
||||
}
|
||||
onActiveFocusChanged: {
|
||||
cursorVisible = isCursorVisible && focus;
|
||||
}
|
||||
MouseArea {
|
||||
// If user clicks in address bar show cursor to indicate ability to enter address.
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
isCursorVisible = true;
|
||||
parent.cursorVisible = true;
|
||||
parent.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
// Delay updating location text a bit to avoid flicker of content and so that connection status is valid.
|
||||
id: updateLocationTextTimer
|
||||
running: false
|
||||
interval: 500 // ms
|
||||
repeat: false
|
||||
onTriggered: updateLocationText(false);
|
||||
}
|
||||
|
||||
Timer {
|
||||
// Delay clearing address line so as to avoid flicker of "not connected" being displayed after entering an address.
|
||||
id: clearAddressLineTimer
|
||||
running: false
|
||||
interval: 100 // ms
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
addressLine.text = "";
|
||||
isCursorVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
Window {
|
||||
width: 938
|
||||
height: 625
|
||||
HifiControls.WebView {
|
||||
anchors.fill: parent;
|
||||
id: storyCardHTML;
|
||||
}
|
||||
id: storyCardFrame;
|
||||
|
||||
shown: false;
|
||||
destroyOnCloseButton: false;
|
||||
pinnable: false;
|
||||
|
||||
anchors {
|
||||
verticalCenter: backgroundImage.verticalCenter;
|
||||
horizontalCenter: scroll.horizontalCenter;
|
||||
}
|
||||
z: 100
|
||||
}
|
||||
|
||||
HifiControls.Keyboard {
|
||||
id: keyboard
|
||||
raised: parent.keyboardEnabled // Ignore keyboardRaised; keep keyboard raised if enabled (i.e., in HMD).
|
||||
numeric: parent.punctuationMode
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getRequest(url, cb) { // cb(error, responseOfCorrectContentType) of url. General for 'get' text/html/json, but without redirects.
|
||||
// TODO: make available to other .qml.
|
||||
var request = new XMLHttpRequest();
|
||||
// QT bug: apparently doesn't handle onload. Workaround using readyState.
|
||||
request.onreadystatechange = function () {
|
||||
var READY_STATE_DONE = 4;
|
||||
var HTTP_OK = 200;
|
||||
if (request.readyState >= READY_STATE_DONE) {
|
||||
var error = (request.status !== HTTP_OK) && request.status.toString() + ':' + request.statusText,
|
||||
response = !error && request.responseText,
|
||||
contentType = !error && request.getResponseHeader('content-type');
|
||||
if (!error && contentType.indexOf('application/json') === 0) {
|
||||
try {
|
||||
response = JSON.parse(response);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
}
|
||||
cb(error, response);
|
||||
}
|
||||
};
|
||||
request.open("GET", url, true);
|
||||
request.send();
|
||||
}
|
||||
|
||||
function identity(x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
function handleError(url, error, data, cb) { // cb(error) and answer truthy if needed, else falsey
|
||||
if (!error && (data.status === 'success')) {
|
||||
return;
|
||||
}
|
||||
if (!error) { // Create a message from the data
|
||||
error = data.status + ': ' + data.error;
|
||||
}
|
||||
if (typeof(error) === 'string') { // Make a proper Error object
|
||||
error = new Error(error);
|
||||
}
|
||||
error.message += ' in ' + url; // Include the url.
|
||||
cb(error);
|
||||
return true;
|
||||
}
|
||||
function resolveUrl(url) {
|
||||
return (url.indexOf('/') === 0) ? (addressBarDialog.metaverseServerUrl + url) : url;
|
||||
}
|
||||
|
||||
function makeModelData(data) { // create a new obj from data
|
||||
// ListModel elements will only ever have those properties that are defined by the first obj that is added.
|
||||
// So here we make sure that we have all the properties we need, regardless of whether it is a place data or user story.
|
||||
var name = data.place_name,
|
||||
tags = data.tags || [data.action, data.username],
|
||||
description = data.description || "",
|
||||
thumbnail_url = data.thumbnail_url || "";
|
||||
return {
|
||||
place_name: name,
|
||||
username: data.username || "",
|
||||
path: data.path || "",
|
||||
created_at: data.created_at || "",
|
||||
action: data.action || "",
|
||||
thumbnail_url: resolveUrl(thumbnail_url),
|
||||
image_url: resolveUrl(data.details.image_url),
|
||||
|
||||
metaverseId: (data.id || "").toString(), // Some are strings from server while others are numbers. Model objects require uniformity.
|
||||
|
||||
tags: tags,
|
||||
description: description,
|
||||
online_users: data.details.concurrency || 0,
|
||||
drillDownToPlace: false,
|
||||
|
||||
searchText: [name].concat(tags, description || []).join(' ').toUpperCase()
|
||||
}
|
||||
}
|
||||
function suggestable(place) {
|
||||
if (place.action === 'snapshot') {
|
||||
return true;
|
||||
}
|
||||
return (place.place_name !== AddressManager.placename); // Not our entry, but do show other entry points to current domain.
|
||||
}
|
||||
property var selectedTab: allTab;
|
||||
function tabSelect(textButton) {
|
||||
selectedTab = textButton;
|
||||
fillDestinations();
|
||||
}
|
||||
property var placeMap: ({});
|
||||
function addToSuggestions(place) {
|
||||
var collapse = allTab.selected && (place.action !== 'concurrency');
|
||||
if (collapse) {
|
||||
var existing = placeMap[place.place_name];
|
||||
if (existing) {
|
||||
existing.drillDownToPlace = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
suggestions.append(place);
|
||||
if (collapse) {
|
||||
placeMap[place.place_name] = suggestions.get(suggestions.count - 1);
|
||||
} else if (place.action === 'concurrency') {
|
||||
suggestions.get(suggestions.count - 1).drillDownToPlace = true; // Don't change raw place object (in allStories).
|
||||
}
|
||||
}
|
||||
property int requestId: 0;
|
||||
function getUserStoryPage(pageNumber, cb) { // cb(error) after all pages of domain data have been added to model
|
||||
var options = [
|
||||
'now=' + new Date().toISOString(),
|
||||
'include_actions=' + selectedTab.includeActions,
|
||||
'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'),
|
||||
'require_online=true',
|
||||
'protocol=' + encodeURIComponent(AddressManager.protocolVersion()),
|
||||
'page=' + pageNumber
|
||||
];
|
||||
var url = metaverseBase + 'user_stories?' + options.join('&');
|
||||
var thisRequestId = ++requestId;
|
||||
getRequest(url, function (error, data) {
|
||||
if ((thisRequestId !== requestId) || handleError(url, error, data, cb)) {
|
||||
return;
|
||||
}
|
||||
var stories = data.user_stories.map(function (story) { // explicit single-argument function
|
||||
return makeModelData(story, url);
|
||||
});
|
||||
allStories = allStories.concat(stories);
|
||||
stories.forEach(makeFilteredPlaceProcessor());
|
||||
if ((data.current_page < data.total_pages) && (data.current_page <= 10)) { // just 10 pages = 100 stories for now
|
||||
return getUserStoryPage(pageNumber + 1, cb);
|
||||
}
|
||||
cb();
|
||||
});
|
||||
}
|
||||
function makeFilteredPlaceProcessor() { // answer a function(placeData) that adds it to suggestions if it matches
|
||||
var words = addressLine.text.toUpperCase().split(/\s+/).filter(identity),
|
||||
data = allStories;
|
||||
function matches(place) {
|
||||
if (!words.length) {
|
||||
return suggestable(place);
|
||||
}
|
||||
return words.every(function (word) {
|
||||
return place.searchText.indexOf(word) >= 0;
|
||||
});
|
||||
}
|
||||
return function (place) {
|
||||
if (matches(place)) {
|
||||
addToSuggestions(place);
|
||||
}
|
||||
};
|
||||
}
|
||||
function filterChoicesByText() {
|
||||
suggestions.clear();
|
||||
placeMap = {};
|
||||
allStories.forEach(makeFilteredPlaceProcessor());
|
||||
}
|
||||
|
||||
function fillDestinations() {
|
||||
allStories = [];
|
||||
suggestions.clear();
|
||||
placeMap = {};
|
||||
getUserStoryPage(1, function (error) {
|
||||
console.log('user stories query', error || 'ok', allStories.length);
|
||||
});
|
||||
}
|
||||
|
||||
function updateLocationText(enteringAddress) {
|
||||
if (enteringAddress) {
|
||||
notice.text = "Go to a place, @user, path or network address";
|
||||
notice.color = hifiStyleConstants.colors.baseGrayHighlight;
|
||||
} else {
|
||||
notice.text = AddressManager.isConnected ? "Your location:" : "Not Connected";
|
||||
notice.color = AddressManager.isConnected ? hifiStyleConstants.colors.baseGrayHighlight : hifiStyleConstants.colors.redHighlight;
|
||||
// Display hostname, which includes ip address, localhost, and other non-placenames.
|
||||
location.text = (AddressManager.placename || AddressManager.hostname || '') + (AddressManager.pathname ? AddressManager.pathname.match(/\/[^\/]+/)[0] : '');
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
updateLocationText(false);
|
||||
if (visible) {
|
||||
addressLine.forceActiveFocus();
|
||||
fillDestinations();
|
||||
}
|
||||
}
|
||||
|
||||
function toggleOrGo(fromSuggestions) {
|
||||
if (addressLine.text !== "") {
|
||||
addressBarDialog.loadAddress(addressLine.text, fromSuggestions)
|
||||
}
|
||||
root.shown = false;
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Escape:
|
||||
case Qt.Key_Back:
|
||||
root.shown = false
|
||||
clearAddressLineTimer.start();
|
||||
event.accepted = true
|
||||
break
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
toggleOrGo()
|
||||
clearAddressLineTimer.start();
|
||||
event.accepted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,10 +5,16 @@ Item {
|
|||
id: keyItem
|
||||
width: 45
|
||||
height: 50
|
||||
|
||||
property int contentPadding: 4
|
||||
property string glyph: "a"
|
||||
property bool toggle: false // does this button have the toggle behaivor?
|
||||
property bool toggled: false // is this button currently toggled?
|
||||
property alias mouseArea: mouseArea1
|
||||
property alias fontFamily: letter.font.family;
|
||||
property alias fontPixelSize: letter.font.pixelSize
|
||||
property alias verticalAlignment: letter.verticalAlignment
|
||||
property alias letterAnchors: letter.anchors
|
||||
|
||||
function resetToggledMode(mode) {
|
||||
toggled = mode;
|
||||
|
@ -105,14 +111,8 @@ Item {
|
|||
color: "#121212"
|
||||
radius: 2
|
||||
border.color: "#00000000"
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 4
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 4
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 4
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 4
|
||||
anchors.fill: parent
|
||||
anchors.margins: contentPadding
|
||||
}
|
||||
|
||||
Text {
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.0
|
||||
import QtQuick 2.7
|
||||
import QtGraphicalEffects 1.0
|
||||
import "."
|
||||
|
||||
Rectangle {
|
||||
|
@ -55,6 +56,8 @@ Rectangle {
|
|||
return ">";
|
||||
} else if (str === "/") {
|
||||
return "?";
|
||||
} else if (str === "-") {
|
||||
return "_";
|
||||
} else {
|
||||
return str.toUpperCase(str);
|
||||
}
|
||||
|
@ -67,6 +70,8 @@ Rectangle {
|
|||
return ".";
|
||||
} else if (str === "?") {
|
||||
return "/";
|
||||
} else if (str === "_") {
|
||||
return "-";
|
||||
} else {
|
||||
return str.toLowerCase(str);
|
||||
}
|
||||
|
@ -85,7 +90,7 @@ Rectangle {
|
|||
|
||||
onShiftModeChanged: {
|
||||
forEachKey(function (key) {
|
||||
if (/[a-z]/i.test(key.glyph)) {
|
||||
if (/[a-z-_]/i.test(key.glyph)) {
|
||||
if (shiftMode) {
|
||||
key.glyph = keyboardBase.toUpper(key.glyph);
|
||||
} else {
|
||||
|
@ -112,8 +117,6 @@ Rectangle {
|
|||
}
|
||||
|
||||
Rectangle {
|
||||
y: 0
|
||||
x: 0
|
||||
height: showMirrorText ? mirrorTextHeight : 0
|
||||
width: keyboardWidth
|
||||
color: "#252525"
|
||||
|
@ -122,13 +125,18 @@ Rectangle {
|
|||
TextInput {
|
||||
id: mirrorText
|
||||
visible: showMirrorText
|
||||
FontLoader { id: ralewaySemiBold; source: "../../fonts/Raleway-SemiBold.ttf"; }
|
||||
font.family: ralewaySemiBold.name
|
||||
font.pointSize: 13.5
|
||||
FontLoader { id: font; source: "../../fonts/FiraSans-Regular.ttf"; }
|
||||
font.family: font.name
|
||||
font.pixelSize: 20
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
color: "#FFFFFF";
|
||||
anchors.fill: parent
|
||||
color: "#00B4EF";
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 10
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
wrapMode: Text.WordWrap
|
||||
readOnly: false // we need this to allow control to accept QKeyEvent
|
||||
selectByMouse: false
|
||||
|
@ -140,16 +148,15 @@ Rectangle {
|
|||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea { // ... and we need this mouse area to prevent mirrorText from getting mouse events to ensure it will never get focus
|
||||
anchors.fill: parent
|
||||
MouseArea { // ... and we need this mouse area to prevent mirrorText from getting mouse events to ensure it will never get focus
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: keyboardRect
|
||||
x: 0
|
||||
y: showMirrorText ? mirrorTextHeight : 0
|
||||
width: keyboardWidth
|
||||
height: raisedHeight
|
||||
|
@ -158,6 +165,8 @@ Rectangle {
|
|||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 0
|
||||
|
||||
FontLoader { id: hiFiGlyphs; source: pathToFonts + "fonts/hifi-glyphs.ttf"; }
|
||||
|
||||
Column {
|
||||
id: columnAlpha
|
||||
width: keyboardWidth
|
||||
|
@ -221,7 +230,7 @@ Rectangle {
|
|||
Key { width: 43; glyph: "b"; }
|
||||
Key { width: 43; glyph: "n"; }
|
||||
Key { width: 43; glyph: "m"; }
|
||||
Key { width: 43; glyph: "_"; }
|
||||
Key { width: 43; glyph: "-"; }
|
||||
Key { width: 43; glyph: "/"; }
|
||||
Key { width: 43; glyph: "?"; }
|
||||
}
|
||||
|
@ -240,8 +249,13 @@ Rectangle {
|
|||
Key { width: 231; glyph: " "; }
|
||||
Key { width: 43; glyph: ","; }
|
||||
Key { width: 43; glyph: "."; }
|
||||
Key { width: 43; glyph: "\u276C"; }
|
||||
Key { width: 43; glyph: "\u276D"; }
|
||||
Key {
|
||||
fontFamily: hiFiGlyphs.name;
|
||||
fontPixelSize: 48;
|
||||
letterAnchors.topMargin: -4;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
width: 86; glyph: "\ue02b";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -328,8 +342,13 @@ Rectangle {
|
|||
Key { width: 231; glyph: " "; }
|
||||
Key { width: 43; glyph: ","; }
|
||||
Key { width: 43; glyph: "."; }
|
||||
Key { width: 43; glyph: "\u276C"; }
|
||||
Key { width: 43; glyph: "\u276D"; }
|
||||
Key {
|
||||
fontFamily: hiFiGlyphs.name;
|
||||
fontPixelSize: 48;
|
||||
letterAnchors.topMargin: -4;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
width: 86; glyph: "\ue02b";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,12 @@ Item {
|
|||
id: hifi
|
||||
}
|
||||
|
||||
function unfocus() {
|
||||
webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) {
|
||||
console.log('unfocus completed: ', result);
|
||||
});
|
||||
}
|
||||
|
||||
function onLoadingChanged(loadRequest) {
|
||||
if (WebEngineView.LoadStartedStatus === loadRequest.status) {
|
||||
|
||||
|
|
|
@ -10,6 +10,11 @@ Item {
|
|||
property alias urlTag: webroot.urlTag
|
||||
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
|
||||
property bool keyboardRaised: false
|
||||
onKeyboardRaisedChanged: {
|
||||
if(!keyboardRaised) {
|
||||
webroot.unfocus();
|
||||
}
|
||||
}
|
||||
property bool punctuationMode: false
|
||||
|
||||
// FIXME - Keyboard HMD only: Make Interface either set keyboardRaised property directly in OffscreenQmlSurface
|
||||
|
|
|
@ -15,6 +15,11 @@ Item {
|
|||
property string scriptURL
|
||||
property bool keyboardEnabled: false
|
||||
property bool keyboardRaised: false
|
||||
onKeyboardRaisedChanged: {
|
||||
if(!keyboardRaised) {
|
||||
webroot.unfocus();
|
||||
}
|
||||
}
|
||||
property bool punctuationMode: false
|
||||
property bool passwordField: false
|
||||
property bool isDesktop: false
|
||||
|
|
|
@ -12,6 +12,11 @@ Item {
|
|||
property alias urlTag: webroot.urlTag
|
||||
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
|
||||
property bool keyboardRaised: false
|
||||
onKeyboardRaisedChanged: {
|
||||
if(!keyboardRaised) {
|
||||
webroot.unfocus();
|
||||
}
|
||||
}
|
||||
property bool punctuationMode: false
|
||||
property bool passwordField: false
|
||||
property alias flickable: webroot.interactive
|
||||
|
|
|
@ -596,9 +596,7 @@ Rectangle {
|
|||
anchors.right: parent.right;
|
||||
text: root.isWearable ? "Wear It" : "Rez It"
|
||||
onClicked: {
|
||||
if (urlHandler.canHandleUrl(root.itemHref)) {
|
||||
urlHandler.handleUrl(root.itemHref);
|
||||
}
|
||||
sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable});
|
||||
rezzedNotifContainer.visible = true;
|
||||
rezzedNotifContainerTimer.start();
|
||||
}
|
||||
|
|
|
@ -113,21 +113,6 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible) {
|
||||
titleBarText.text = "Certificate";
|
||||
popText.text = "PROOF OF PURCHASE";
|
||||
root.certificateId = "";
|
||||
root.itemName = "--";
|
||||
root.itemOwner = "--";
|
||||
root.itemEdition = "--";
|
||||
root.dateOfPurchase = "--";
|
||||
root.marketplaceUrl = "";
|
||||
root.isMyCert = false;
|
||||
errorText.text = "";
|
||||
}
|
||||
}
|
||||
|
||||
// This object is always used in a popup.
|
||||
// This MouseArea is used to prevent a user from being
|
||||
// able to click on a button/mouseArea underneath the popup.
|
||||
|
@ -420,6 +405,18 @@ Rectangle {
|
|||
case 'inspectionCertificate_setCertificateId':
|
||||
root.certificateId = message.certificateId;
|
||||
break;
|
||||
case 'inspectionCertificate_resetCert':
|
||||
titleBarText.text = "Certificate";
|
||||
popText.text = "PROOF OF PURCHASE";
|
||||
root.certificateId = "";
|
||||
root.itemName = "--";
|
||||
root.itemOwner = "--";
|
||||
root.itemEdition = "--";
|
||||
root.dateOfPurchase = "--";
|
||||
root.marketplaceUrl = "";
|
||||
root.isMyCert = false;
|
||||
errorText.text = "";
|
||||
break;
|
||||
default:
|
||||
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
|
||||
}
|
||||
|
|
|
@ -346,9 +346,7 @@ Item {
|
|||
enabled: (root.canRezCertifiedItems || root.isWearable) && root.purchaseStatus !== "invalidated";
|
||||
|
||||
onClicked: {
|
||||
if (urlHandler.canHandleUrl(root.itemHref)) {
|
||||
urlHandler.handleUrl(root.itemHref);
|
||||
}
|
||||
sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable});
|
||||
rezzedNotifContainer.visible = true;
|
||||
rezzedNotifContainerTimer.start();
|
||||
}
|
||||
|
|
|
@ -442,6 +442,8 @@ Rectangle {
|
|||
onSendToPurchases: {
|
||||
if (msg.method === 'purchases_itemInfoClicked') {
|
||||
sendToScript({method: 'purchases_itemInfoClicked', itemId: itemId});
|
||||
} else if (msg.method === "purchases_rezClicked") {
|
||||
sendToScript({method: 'purchases_rezClicked', itemHref: itemHref, isWearable: isWearable});
|
||||
} else if (msg.method === 'purchases_itemCertificateClicked') {
|
||||
inspectionCertificate.visible = true;
|
||||
inspectionCertificate.isLightbox = true;
|
||||
|
|
|
@ -196,7 +196,38 @@ Item {
|
|||
anchors.bottom: parent.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 24;
|
||||
|
||||
Item {
|
||||
visible: transactionHistoryModel.count === 0 && root.historyReceived;
|
||||
anchors.centerIn: parent;
|
||||
width: parent.width - 12;
|
||||
height: parent.height;
|
||||
|
||||
HifiControlsUit.Separator {
|
||||
colorScheme: 1;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.top: parent.top;
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
id: noActivityText;
|
||||
text: "<b>The Wallet app is in closed Beta.</b><br><br>To request entry and <b>receive free HFC</b>, please contact " +
|
||||
"<b>info@highfidelity.com</b> with your High Fidelity account username and the email address registered to that account.";
|
||||
// Text size
|
||||
size: 24;
|
||||
// Style
|
||||
color: hifi.colors.blueAccent;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 12;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 12;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
height: paintedHeight;
|
||||
wrapMode: Text.WordWrap;
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: transactionHistory;
|
||||
|
@ -294,17 +325,6 @@ Item {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This should never be visible (since you immediately get 100 HFC)
|
||||
FiraSansRegular {
|
||||
id: emptyTransationHistory;
|
||||
size: 24;
|
||||
visible: !transactionHistory.visible && root.historyReceived;
|
||||
text: "Recent Activity Unavailable";
|
||||
anchors.fill: parent;
|
||||
horizontalAlignment: Text.AlignHCenter;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -350,7 +370,9 @@ Item {
|
|||
}
|
||||
|
||||
root.pendingCount = pendingCount;
|
||||
transactionHistoryModel.insert(0, {"transaction_type": "pendingCount"});
|
||||
if (pendingCount > 0) {
|
||||
transactionHistoryModel.insert(0, {"transaction_type": "pendingCount"});
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -53,8 +53,6 @@ Item {
|
|||
onWalletAuthenticatedStatusResult: {
|
||||
if (isAuthenticated) {
|
||||
root.activeView = "step_4";
|
||||
} else {
|
||||
root.activeView = "step_3";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ Rectangle {
|
|||
|
||||
color: hifi.colors.baseGray
|
||||
|
||||
property bool keyboardEnabled: HMD.active
|
||||
property bool keyboardRaised: false
|
||||
|
||||
LetterboxMessage {
|
||||
id: letterBoxMessage
|
||||
|
@ -380,7 +382,7 @@ Rectangle {
|
|||
Component.onCompleted: scriptsModel.filterRegExp = new RegExp("^.*$", "i")
|
||||
onActiveFocusChanged: {
|
||||
// raise the keyboard
|
||||
keyboard.raised = activeFocus;
|
||||
root.keyboardRaised = activeFocus;
|
||||
|
||||
// scroll to the bottom of the content area.
|
||||
if (activeFocus) {
|
||||
|
@ -481,7 +483,7 @@ Rectangle {
|
|||
|
||||
HifiControls.Keyboard {
|
||||
id: keyboard
|
||||
raised: false
|
||||
raised: parent.keyboardEnabled && parent.keyboardRaised
|
||||
numeric: false
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
|
|
|
@ -11,8 +11,11 @@
|
|||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Dialogs 1.2 as OriginalDialogs
|
||||
|
||||
import "../../styles-uit"
|
||||
import "../../controls-uit"
|
||||
import "../dialogs"
|
||||
|
||||
Rectangle {
|
||||
id: newModelDialog
|
||||
|
@ -25,6 +28,15 @@ Rectangle {
|
|||
property bool punctuationMode: false
|
||||
property bool keyboardRasied: false
|
||||
|
||||
function errorMessageBox(message) {
|
||||
return desktop.messageBox({
|
||||
icon: hifi.icons.warning,
|
||||
defaultButton: OriginalDialogs.StandardButton.Ok,
|
||||
title: "Error",
|
||||
text: message
|
||||
});
|
||||
}
|
||||
|
||||
Item {
|
||||
id: column1
|
||||
anchors.rightMargin: 10
|
||||
|
@ -98,7 +110,6 @@ Rectangle {
|
|||
CheckBox {
|
||||
id: dynamic
|
||||
text: qsTr("Dynamic")
|
||||
|
||||
}
|
||||
|
||||
Row {
|
||||
|
@ -117,6 +128,7 @@ Rectangle {
|
|||
Text {
|
||||
id: text2
|
||||
width: 160
|
||||
x: dynamic.width / 2
|
||||
color: "#ffffff"
|
||||
text: qsTr("Models with automatic collisions set to 'Exact' cannot be dynamic, and should not be used as floors")
|
||||
wrapMode: Text.WordWrap
|
||||
|
@ -139,15 +151,40 @@ Rectangle {
|
|||
|
||||
ComboBox {
|
||||
id: collisionType
|
||||
|
||||
property int priorIndex: 0
|
||||
property string staticMeshCollisionText: "Exact - All polygons"
|
||||
property var collisionArray: ["No Collision",
|
||||
"Basic - Whole model",
|
||||
"Good - Sub-meshes",
|
||||
staticMeshCollisionText,
|
||||
"Box",
|
||||
"Sphere"]
|
||||
|
||||
width: 200
|
||||
z: 100
|
||||
transformOrigin: Item.Center
|
||||
model: ["No Collision",
|
||||
"Basic - Whole model",
|
||||
"Good - Sub-meshes",
|
||||
"Exact - All polygons",
|
||||
"Box",
|
||||
"Sphere"]
|
||||
model: collisionArray
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
if (collisionArray[currentIndex] === staticMeshCollisionText) {
|
||||
|
||||
if (dynamic.checked) {
|
||||
currentIndex = priorIndex;
|
||||
|
||||
errorMessageBox("Models with Automatic Collisions set to \""
|
||||
+ staticMeshCollisionText + "\" cannot be dynamic.");
|
||||
//--EARLY EXIT--( Can't have a static mesh model that's dynamic )
|
||||
return;
|
||||
}
|
||||
|
||||
dynamic.enabled = false;
|
||||
} else {
|
||||
dynamic.enabled = true;
|
||||
}
|
||||
|
||||
priorIndex = currentIndex;
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
|
@ -155,10 +192,10 @@ Rectangle {
|
|||
width: 200
|
||||
height: 400
|
||||
spacing: 5
|
||||
|
||||
anchors {
|
||||
rightMargin: 15
|
||||
}
|
||||
|
||||
anchors.horizontalCenter: column3.horizontalCenter
|
||||
anchors.horizontalCenterOffset: -20
|
||||
|
||||
Button {
|
||||
id: button1
|
||||
text: qsTr("Add")
|
||||
|
|
|
@ -8,8 +8,8 @@ import "../audio" as HifiAudio
|
|||
Item {
|
||||
id: tablet
|
||||
objectName: "tablet"
|
||||
property int rowIndex: 0
|
||||
property int columnIndex: 0
|
||||
property int rowIndex: 6 // by default
|
||||
property int columnIndex: 1 // point to 'go to location'
|
||||
property int count: (flowMain.children.length - 1)
|
||||
|
||||
// used to look up a button by its uuid
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
import Hifi 1.0
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import QtGraphicalEffects 1.0
|
||||
import "../../controls"
|
||||
import "../../styles"
|
||||
|
@ -83,7 +84,6 @@ StackView {
|
|||
anchors.centerIn = parent;
|
||||
}
|
||||
|
||||
|
||||
function resetAfterTeleport() {
|
||||
//storyCardFrame.shown = root.shown = false;
|
||||
}
|
||||
|
@ -134,7 +134,8 @@ StackView {
|
|||
bottom: parent.bottom
|
||||
}
|
||||
|
||||
onHostChanged: updateLocationTextTimer.start();
|
||||
onHostChanged: updateLocationTextTimer.restart();
|
||||
|
||||
Rectangle {
|
||||
id: navBar
|
||||
width: parent.width
|
||||
|
@ -205,16 +206,16 @@ StackView {
|
|||
anchors {
|
||||
top: parent.top;
|
||||
left: addressLineContainer.left;
|
||||
right: addressLineContainer.right;
|
||||
}
|
||||
}
|
||||
|
||||
HifiStyles.FiraSansRegular {
|
||||
id: location;
|
||||
anchors {
|
||||
left: addressLineContainer.left;
|
||||
leftMargin: 8;
|
||||
verticalCenter: addressLineContainer.verticalCenter;
|
||||
left: notice.right
|
||||
leftMargin: 8
|
||||
right: addressLineContainer.right
|
||||
verticalCenter: notice.verticalCenter
|
||||
}
|
||||
font.pixelSize: addressLine.font.pixelSize;
|
||||
color: "gray";
|
||||
|
@ -222,7 +223,7 @@ StackView {
|
|||
visible: addressLine.text.length === 0
|
||||
}
|
||||
|
||||
TextInput {
|
||||
TextField {
|
||||
id: addressLine
|
||||
width: addressLineContainer.width - addressLineContainer.anchors.leftMargin - addressLineContainer.anchors.rightMargin;
|
||||
anchors {
|
||||
|
@ -230,7 +231,6 @@ StackView {
|
|||
leftMargin: 8;
|
||||
verticalCenter: addressLineContainer.verticalCenter;
|
||||
}
|
||||
font.pixelSize: hifi.fonts.pixelSize * 0.75
|
||||
onTextChanged: {
|
||||
updateLocationText(text.length > 0);
|
||||
}
|
||||
|
@ -238,6 +238,17 @@ StackView {
|
|||
addressBarDialog.keyboardEnabled = false;
|
||||
toggleOrGo();
|
||||
}
|
||||
placeholderText: "Type domain address here"
|
||||
verticalAlignment: TextInput.AlignBottom
|
||||
style: TextFieldStyle {
|
||||
textColor: hifi.colors.text
|
||||
placeholderTextColor: "gray"
|
||||
font {
|
||||
family: hifi.fonts.fontFamily
|
||||
pixelSize: hifi.fonts.pixelSize * 0.75
|
||||
}
|
||||
background: Item {}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
@ -347,7 +358,7 @@ StackView {
|
|||
// Delay updating location text a bit to avoid flicker of content and so that connection status is valid.
|
||||
id: updateLocationTextTimer
|
||||
running: false
|
||||
interval: 500 // ms
|
||||
interval: 1000 // ms
|
||||
repeat: false
|
||||
onTriggered: updateLocationText(false);
|
||||
}
|
||||
|
@ -366,7 +377,7 @@ StackView {
|
|||
|
||||
HifiControls.Keyboard {
|
||||
id: keyboard
|
||||
raised: parent.keyboardEnabled
|
||||
raised: parent.keyboardEnabled && parent.keyboardRaised
|
||||
numeric: parent.punctuationMode
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
|
|
|
@ -1700,8 +1700,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
lastLeftHandPose = leftHandPose;
|
||||
lastRightHandPose = rightHandPose;
|
||||
|
||||
properties["local_socket_changes"] = DependencyManager::get<StatTracker>()->getStat(LOCAL_SOCKET_CHANGE_STAT).toInt();
|
||||
|
||||
UserActivityLogger::getInstance().logAction("stats", properties);
|
||||
});
|
||||
sendStatsTimer->start();
|
||||
|
@ -1825,6 +1823,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
// Preload Tablet sounds
|
||||
DependencyManager::get<TabletScriptingInterface>()->preloadSounds();
|
||||
|
||||
_pendingIdleEvent = false;
|
||||
_pendingRenderEvent = false;
|
||||
|
||||
qCDebug(interfaceapp) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID());
|
||||
}
|
||||
|
||||
|
@ -2693,7 +2694,7 @@ bool Application::importFromZIP(const QString& filePath) {
|
|||
qDebug() << "A zip file has been dropped in: " << filePath;
|
||||
QUrl empty;
|
||||
// handle Blocks download from Marketplace
|
||||
if (filePath.contains("vr.google.com/downloads")) {
|
||||
if (filePath.contains("poly.google.com/downloads")) {
|
||||
addAssetToWorldFromURL(filePath);
|
||||
} else {
|
||||
qApp->getFileDownloadInterface()->runUnzip(filePath, empty, true, true, false);
|
||||
|
@ -4441,7 +4442,7 @@ void Application::cameraModeChanged() {
|
|||
void Application::cameraMenuChanged() {
|
||||
auto menu = Menu::getInstance();
|
||||
if (menu->isOptionChecked(MenuOption::FullscreenMirror)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
|
||||
if (!isHMDMode() && _myCamera.getMode() != CAMERA_MODE_MIRROR) {
|
||||
_myCamera.setMode(CAMERA_MODE_MIRROR);
|
||||
getMyAvatar()->reset(false, false, false); // to reset any active MyAvatar::FollowHelpers
|
||||
}
|
||||
|
@ -6235,7 +6236,7 @@ void Application::addAssetToWorldFromURL(QString url) {
|
|||
if (url.contains("filename")) {
|
||||
filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.
|
||||
}
|
||||
if (url.contains("vr.google.com/downloads")) {
|
||||
if (url.contains("poly.google.com/downloads")) {
|
||||
filename = url.section('/', -1);
|
||||
if (url.contains("noDownload")) {
|
||||
filename.remove(".zip?noDownload=false");
|
||||
|
@ -6270,7 +6271,7 @@ void Application::addAssetToWorldFromURLRequestFinished() {
|
|||
if (url.contains("filename")) {
|
||||
filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.
|
||||
}
|
||||
if (url.contains("vr.google.com/downloads")) {
|
||||
if (url.contains("poly.google.com/downloads")) {
|
||||
filename = url.section('/', -1);
|
||||
if (url.contains("noDownload")) {
|
||||
filename.remove(".zip?noDownload=false");
|
||||
|
@ -7271,6 +7272,10 @@ void Application::updateDisplayMode() {
|
|||
menu->setIsOptionChecked(MenuOption::FirstPerson, true);
|
||||
cameraMenuChanged();
|
||||
}
|
||||
|
||||
// Remove the mirror camera option from menu if in HMD mode
|
||||
auto mirrorAction = menu->getActionForOption(MenuOption::FullscreenMirror);
|
||||
mirrorAction->setVisible(!isHmd);
|
||||
|
||||
Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin");
|
||||
}
|
||||
|
|
|
@ -708,7 +708,7 @@ private:
|
|||
|
||||
friend class RenderEventHandler;
|
||||
|
||||
std::atomic<bool> _pendingIdleEvent { false };
|
||||
std::atomic<bool> _pendingRenderEvent { false };
|
||||
std::atomic<bool> _pendingIdleEvent { true };
|
||||
std::atomic<bool> _pendingRenderEvent { true };
|
||||
};
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -54,7 +54,9 @@ void LODManager::autoAdjustLOD(float batchTime, float engineRunTime, float delta
|
|||
float renderTime = batchTime + OVERLAY_AND_SWAP_TIME_BUDGET;
|
||||
float maxTime = glm::max(renderTime, engineRunTime);
|
||||
const float BLEND_TIMESCALE = 0.3f; // sec
|
||||
float blend = BLEND_TIMESCALE / deltaTimeSec;
|
||||
const float MIN_DELTA_TIME = 0.001f;
|
||||
const float safeDeltaTime = glm::max(deltaTimeSec, MIN_DELTA_TIME);
|
||||
float blend = BLEND_TIMESCALE / safeDeltaTime;
|
||||
if (blend > 1.0f) {
|
||||
blend = 1.0f;
|
||||
}
|
||||
|
|
|
@ -135,7 +135,7 @@ MyAvatar::MyAvatar(QThread* thread) :
|
|||
connect(&domainHandler, &DomainHandler::settingsReceived, this, &MyAvatar::restrictScaleFromDomainSettings);
|
||||
|
||||
// when we leave a domain we lift whatever restrictions that domain may have placed on our scale
|
||||
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &MyAvatar::clearScaleRestriction);
|
||||
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &MyAvatar::leaveDomain);
|
||||
|
||||
_bodySensorMatrix = deriveBodyFromHMDSensor();
|
||||
|
||||
|
@ -2279,6 +2279,18 @@ void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettings
|
|||
settings.endGroup();
|
||||
}
|
||||
|
||||
void MyAvatar::leaveDomain() {
|
||||
clearScaleRestriction();
|
||||
saveAvatarScale();
|
||||
}
|
||||
|
||||
void MyAvatar::saveAvatarScale() {
|
||||
Settings settings;
|
||||
settings.beginGroup("Avatar");
|
||||
settings.setValue("scale", _targetScale);
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
void MyAvatar::clearScaleRestriction() {
|
||||
_domainMinimumScale = MIN_AVATAR_SCALE;
|
||||
_domainMaximumScale = MAX_AVATAR_SCALE;
|
||||
|
|
|
@ -622,6 +622,9 @@ signals:
|
|||
void attachmentsChanged();
|
||||
void scaleChanged();
|
||||
|
||||
private slots:
|
||||
void leaveDomain();
|
||||
|
||||
private:
|
||||
|
||||
bool requiresSafeLanding(const glm::vec3& positionIn, glm::vec3& positionOut);
|
||||
|
@ -638,6 +641,8 @@ private:
|
|||
virtual int parseDataFromBuffer(const QByteArray& buffer) override;
|
||||
virtual glm::vec3 getSkeletonPosition() const override;
|
||||
|
||||
void saveAvatarScale();
|
||||
|
||||
glm::vec3 getScriptedMotorVelocity() const { return _scriptedMotorVelocity; }
|
||||
float getScriptedMotorTimescale() const { return _scriptedMotorTimescale; }
|
||||
QString getScriptedMotorFrame() const;
|
||||
|
|
|
@ -90,7 +90,7 @@ void Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, cons
|
|||
signedSend("transaction", transactionString, hfc_key, "buy", "buySuccess", "buyFailure", controlled_failure);
|
||||
}
|
||||
|
||||
bool Ledger::receiveAt(const QString& hfc_key, const QString& old_key, const QString& machine_fingerprint) {
|
||||
bool Ledger::receiveAt(const QString& hfc_key, const QString& old_key) {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
if (!accountManager->isLoggedIn()) {
|
||||
qCWarning(commerce) << "Cannot set receiveAt when not logged in.";
|
||||
|
@ -99,13 +99,7 @@ bool Ledger::receiveAt(const QString& hfc_key, const QString& old_key, const QSt
|
|||
return false; // We know right away that we will fail, so tell the caller.
|
||||
}
|
||||
|
||||
QJsonObject transaction;
|
||||
transaction["hfc_key"] = hfc_key;
|
||||
transaction["machine_fingerprint"] = machine_fingerprint;
|
||||
QJsonDocument transactionDoc{ transaction };
|
||||
auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
|
||||
|
||||
signedSend("transaction", transactionString, old_key, "receive_at", "receiveAtSuccess", "receiveAtFailure");
|
||||
signedSend("public_key", hfc_key.toUtf8(), old_key, "receive_at", "receiveAtSuccess", "receiveAtFailure");
|
||||
return true; // Note that there may still be an asynchronous signal of failure that callers might be interested in.
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ class Ledger : public QObject, public Dependency {
|
|||
|
||||
public:
|
||||
void buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const bool controlled_failure = false);
|
||||
bool receiveAt(const QString& hfc_key, const QString& old_key, const QString& machine_fingerprint);
|
||||
bool receiveAt(const QString& hfc_key, const QString& old_key);
|
||||
void balance(const QStringList& keys);
|
||||
void inventory(const QStringList& keys);
|
||||
void history(const QStringList& keys);
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include "ui/ImageProvider.h"
|
||||
#include "scripting/HMDScriptingInterface.h"
|
||||
|
||||
#include <FingerprintUtils.h>
|
||||
#include <PathUtils.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <AccountManager.h>
|
||||
|
@ -542,8 +541,7 @@ bool Wallet::generateKeyPair() {
|
|||
// 2. It is maximally private, and we can step back from that later if desired.
|
||||
// 3. It maximally exercises all the machinery, so we are most likely to surface issues now.
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
QString machineFingerprint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint());
|
||||
return ledger->receiveAt(key, oldKey, machineFingerprint);
|
||||
return ledger->receiveAt(key, oldKey);
|
||||
}
|
||||
|
||||
QStringList Wallet::listPublicKeys() {
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include "RayPickScriptingInterface.h"
|
||||
|
||||
LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates,
|
||||
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled) :
|
||||
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool scaleWithAvatar, const bool enabled) :
|
||||
_renderingEnabled(enabled),
|
||||
_renderStates(renderStates),
|
||||
_defaultRenderStates(defaultRenderStates),
|
||||
|
@ -23,6 +23,7 @@ LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& rende
|
|||
_centerEndY(centerEndY),
|
||||
_lockEnd(lockEnd),
|
||||
_distanceScaleEnd(distanceScaleEnd),
|
||||
_scaleWithAvatar(scaleWithAvatar),
|
||||
_rayPickUID(DependencyManager::get<RayPickScriptingInterface>()->createRayPick(rayProps))
|
||||
{
|
||||
|
||||
|
@ -94,6 +95,10 @@ void LaserPointer::editRenderState(const std::string& state, const QVariant& sta
|
|||
if (endDim.isValid()) {
|
||||
_renderStates[state].setEndDim(vec3FromVariant(endDim));
|
||||
}
|
||||
QVariant lineWidth = pathProps.toMap()["lineWidth"];
|
||||
if (lineWidth.isValid()) {
|
||||
_renderStates[state].setLineWidth(lineWidth.toFloat());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -152,6 +157,7 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVariant end = vec3toVariant(endVec);
|
||||
if (!renderState.getPathID().isNull()) {
|
||||
QVariantMap pathProps;
|
||||
|
@ -159,6 +165,9 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter
|
|||
pathProps.insert("end", end);
|
||||
pathProps.insert("visible", true);
|
||||
pathProps.insert("ignoreRayIntersection", renderState.doesPathIgnoreRays());
|
||||
if (_scaleWithAvatar) {
|
||||
pathProps.insert("lineWidth", renderState.getLineWidth() * DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale());
|
||||
}
|
||||
qApp->getOverlays().editOverlay(renderState.getPathID(), pathProps);
|
||||
}
|
||||
if (!renderState.getEndID().isNull()) {
|
||||
|
@ -166,7 +175,7 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter
|
|||
glm::quat faceAvatarRotation = DependencyManager::get<AvatarManager>()->getMyAvatar()->getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, 180.0f, 0.0f)));
|
||||
glm::vec3 dim = vec3FromVariant(qApp->getOverlays().getProperty(renderState.getEndID(), "dimensions").value);
|
||||
if (_distanceScaleEnd) {
|
||||
dim = renderState.getEndDim() * glm::distance(pickRay.origin, endVec) * DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale();
|
||||
dim = renderState.getEndDim() * glm::distance(pickRay.origin, endVec);
|
||||
endProps.insert("dimensions", vec3toVariant(dim));
|
||||
}
|
||||
if (_centerEndY) {
|
||||
|
@ -258,6 +267,7 @@ RenderState::RenderState(const OverlayID& startID, const OverlayID& pathID, cons
|
|||
}
|
||||
if (!_pathID.isNull()) {
|
||||
_pathIgnoreRays = qApp->getOverlays().getProperty(_pathID, "ignoreRayIntersection").value.toBool();
|
||||
_lineWidth = qApp->getOverlays().getProperty(_pathID, "lineWidth").value.toFloat();
|
||||
}
|
||||
if (!_endID.isNull()) {
|
||||
_endDim = vec3FromVariant(qApp->getOverlays().getProperty(_endID, "dimensions").value);
|
||||
|
|
|
@ -43,6 +43,9 @@ public:
|
|||
void setEndDim(const glm::vec3& endDim) { _endDim = endDim; }
|
||||
const glm::vec3& getEndDim() const { return _endDim; }
|
||||
|
||||
void setLineWidth(const float& lineWidth) { _lineWidth = lineWidth; }
|
||||
const float& getLineWidth() const { return _lineWidth; }
|
||||
|
||||
void deleteOverlays();
|
||||
|
||||
private:
|
||||
|
@ -54,6 +57,7 @@ private:
|
|||
bool _endIgnoreRays;
|
||||
|
||||
glm::vec3 _endDim;
|
||||
float _lineWidth;
|
||||
};
|
||||
|
||||
|
||||
|
@ -66,7 +70,7 @@ public:
|
|||
typedef std::unordered_map<std::string, std::pair<float, RenderState>> DefaultRenderStateMap;
|
||||
|
||||
LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates,
|
||||
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled);
|
||||
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool scaleWithAvatar, const bool enabled);
|
||||
~LaserPointer();
|
||||
|
||||
QUuid getRayUID() { return _rayPickUID; }
|
||||
|
@ -97,6 +101,7 @@ private:
|
|||
bool _centerEndY;
|
||||
bool _lockEnd;
|
||||
bool _distanceScaleEnd;
|
||||
bool _scaleWithAvatar;
|
||||
LockEndObject _lockEndObject;
|
||||
|
||||
const QUuid _rayPickUID;
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
#include "LaserPointerManager.h"
|
||||
|
||||
QUuid LaserPointerManager::createLaserPointer(const QVariant& rayProps, const LaserPointer::RenderStateMap& renderStates, const LaserPointer::DefaultRenderStateMap& defaultRenderStates,
|
||||
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled) {
|
||||
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool scaleWithAvatar, const bool enabled) {
|
||||
QUuid result;
|
||||
std::shared_ptr<LaserPointer> laserPointer = std::make_shared<LaserPointer>(rayProps, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, enabled);
|
||||
std::shared_ptr<LaserPointer> laserPointer = std::make_shared<LaserPointer>(rayProps, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, scaleWithAvatar, enabled);
|
||||
if (!laserPointer->getRayUID().isNull()) {
|
||||
result = QUuid::createUuid();
|
||||
withWriteLock([&] { _laserPointers[result] = laserPointer; });
|
||||
|
|
|
@ -25,7 +25,7 @@ class LaserPointerManager : protected ReadWriteLockable {
|
|||
|
||||
public:
|
||||
QUuid createLaserPointer(const QVariant& rayProps, const LaserPointer::RenderStateMap& renderStates, const LaserPointer::DefaultRenderStateMap& defaultRenderStates,
|
||||
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled);
|
||||
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool scaleWithAvatar, const bool enabled);
|
||||
|
||||
void removeLaserPointer(const QUuid& uid);
|
||||
void enableLaserPointer(const QUuid& uid) const;
|
||||
|
|
|
@ -51,6 +51,11 @@ QUuid LaserPointerScriptingInterface::createLaserPointer(const QVariant& propert
|
|||
enabled = propertyMap["enabled"].toBool();
|
||||
}
|
||||
|
||||
bool scaleWithAvatar = false;
|
||||
if (propertyMap["scaleWithAvatar"].isValid()) {
|
||||
scaleWithAvatar = propertyMap["scaleWithAvatar"].toBool();
|
||||
}
|
||||
|
||||
LaserPointer::RenderStateMap renderStates;
|
||||
if (propertyMap["renderStates"].isValid()) {
|
||||
QList<QVariant> renderStateVariants = propertyMap["renderStates"].toList();
|
||||
|
@ -80,7 +85,7 @@ QUuid LaserPointerScriptingInterface::createLaserPointer(const QVariant& propert
|
|||
}
|
||||
}
|
||||
|
||||
return qApp->getLaserPointerManager().createLaserPointer(properties, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, enabled);
|
||||
return qApp->getLaserPointerManager().createLaserPointer(properties, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, scaleWithAvatar, enabled);
|
||||
}
|
||||
|
||||
void LaserPointerScriptingInterface::editRenderState(const QUuid& uid, const QString& renderState, const QVariant& properties) const {
|
||||
|
|
|
@ -71,6 +71,12 @@ bool SelectionScriptingInterface::removeFromSelectedItemsList(const QString& lis
|
|||
return false;
|
||||
}
|
||||
|
||||
bool SelectionScriptingInterface::clearSelectedItemsList(const QString& listName) {
|
||||
_selectedItemsListMap.insert(listName, GameplayObjects());
|
||||
emit selectedItemsListChanged(listName);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T> bool SelectionScriptingInterface::addToGameplayObjects(const QString& listName, T idToAdd) {
|
||||
GameplayObjects currentList = _selectedItemsListMap.value(listName);
|
||||
currentList.addToGameplayObjects(idToAdd);
|
||||
|
|
|
@ -56,11 +56,45 @@ public:
|
|||
|
||||
GameplayObjects getList(const QString& listName);
|
||||
|
||||
/**jsdoc
|
||||
* Prints out the list of avatars, entities and overlays stored in a particular selection.
|
||||
* @function Selection.printList
|
||||
* @param listName {string} name of the selection
|
||||
*/
|
||||
Q_INVOKABLE void printList(const QString& listName);
|
||||
/**jsdoc
|
||||
* Removes a named selection from the list of selections.
|
||||
* @function Selection.removeListFromMap
|
||||
* @param listName {string} name of the selection
|
||||
* @returns {bool} true if the selection existed and was successfully removed.
|
||||
*/
|
||||
Q_INVOKABLE bool removeListFromMap(const QString& listName);
|
||||
|
||||
/**jsdoc
|
||||
* Add an item in a selection.
|
||||
* @function Selection.addToSelectedItemsList
|
||||
* @param listName {string} name of the selection
|
||||
* @param itemType {string} the type of the item (one of "avatar", "entity" or "overlay")
|
||||
* @param id {EntityID} the Id of the item to add to the selection
|
||||
* @returns {bool} true if the item was successfully added.
|
||||
*/
|
||||
Q_INVOKABLE bool addToSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id);
|
||||
/**jsdoc
|
||||
* Remove an item from a selection.
|
||||
* @function Selection.removeFromSelectedItemsList
|
||||
* @param listName {string} name of the selection
|
||||
* @param itemType {string} the type of the item (one of "avatar", "entity" or "overlay")
|
||||
* @param id {EntityID} the Id of the item to remove
|
||||
* @returns {bool} true if the item was successfully removed.
|
||||
*/
|
||||
Q_INVOKABLE bool removeFromSelectedItemsList(const QString& listName, const QString& itemType, const QUuid& id);
|
||||
/**jsdoc
|
||||
* Remove all items from a selection.
|
||||
* @function Selection.clearSelectedItemsList
|
||||
* @param listName {string} name of the selection
|
||||
* @returns {bool} true if the item was successfully cleared.
|
||||
*/
|
||||
Q_INVOKABLE bool clearSelectedItemsList(const QString& listName);
|
||||
|
||||
signals:
|
||||
void selectedItemsListChanged(const QString& listName);
|
||||
|
|
|
@ -40,6 +40,10 @@ AddressBarDialog::AddressBarDialog(QQuickItem* parent) : OffscreenQmlDialog(pare
|
|||
_backEnabled = !(DependencyManager::get<AddressManager>()->getBackStack().isEmpty());
|
||||
_forwardEnabled = !(DependencyManager::get<AddressManager>()->getForwardStack().isEmpty());
|
||||
connect(addressManager.data(), &AddressManager::hostChanged, this, &AddressBarDialog::hostChanged);
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
const DomainHandler& domainHandler = nodeList->getDomainHandler();
|
||||
connect(&domainHandler, &DomainHandler::connectedToDomain, this, &AddressBarDialog::hostChanged);
|
||||
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &AddressBarDialog::hostChanged);
|
||||
connect(DependencyManager::get<DialogsManager>().data(), &DialogsManager::setUseFeed, this, &AddressBarDialog::setUseFeed);
|
||||
connect(qApp, &Application::receivedHifiSchemeURL, this, &AddressBarDialog::receivedHifiSchemeURL);
|
||||
}
|
||||
|
|
|
@ -16,13 +16,11 @@
|
|||
#include "Application.h"
|
||||
|
||||
|
||||
const float DEFAULT_LINE_WIDTH = 1.0f;
|
||||
const bool DEFAULT_IS_SOLID = false;
|
||||
const bool DEFAULT_IS_DASHED_LINE = false;
|
||||
|
||||
Base3DOverlay::Base3DOverlay() :
|
||||
SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()),
|
||||
_lineWidth(DEFAULT_LINE_WIDTH),
|
||||
_isSolid(DEFAULT_IS_SOLID),
|
||||
_isDashedLine(DEFAULT_IS_DASHED_LINE),
|
||||
_ignoreRayIntersection(false),
|
||||
|
@ -34,7 +32,6 @@ Base3DOverlay::Base3DOverlay() :
|
|||
Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
|
||||
Overlay(base3DOverlay),
|
||||
SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()),
|
||||
_lineWidth(base3DOverlay->_lineWidth),
|
||||
_isSolid(base3DOverlay->_isSolid),
|
||||
_isDashedLine(base3DOverlay->_isDashedLine),
|
||||
_ignoreRayIntersection(base3DOverlay->_ignoreRayIntersection),
|
||||
|
@ -153,12 +150,6 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
|||
setLocalOrientation(quatFromVariant(properties["orientation"]));
|
||||
needRenderItemUpdate = true;
|
||||
}
|
||||
|
||||
if (properties["lineWidth"].isValid()) {
|
||||
setLineWidth(properties["lineWidth"].toFloat());
|
||||
needRenderItemUpdate = true;
|
||||
}
|
||||
|
||||
if (properties["isSolid"].isValid()) {
|
||||
setIsSolid(properties["isSolid"].toBool());
|
||||
}
|
||||
|
@ -225,9 +216,6 @@ QVariant Base3DOverlay::getProperty(const QString& property) {
|
|||
if (property == "localRotation" || property == "localOrientation") {
|
||||
return quatToVariant(getLocalOrientation());
|
||||
}
|
||||
if (property == "lineWidth") {
|
||||
return _lineWidth;
|
||||
}
|
||||
if (property == "isSolid" || property == "isFilled" || property == "solid" || property == "filed") {
|
||||
return _isSolid;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@ public:
|
|||
// TODO: consider implementing registration points in this class
|
||||
glm::vec3 getCenter() const { return getPosition(); }
|
||||
|
||||
float getLineWidth() const { return _lineWidth; }
|
||||
bool getIsSolid() const { return _isSolid; }
|
||||
bool getIsDashedLine() const { return _isDashedLine; }
|
||||
bool getIsSolidLine() const { return !_isDashedLine; }
|
||||
|
@ -47,7 +46,6 @@ public:
|
|||
bool getDrawHUDLayer() const { return _drawHUDLayer; }
|
||||
bool getIsGrabbable() const { return _isGrabbable; }
|
||||
|
||||
void setLineWidth(float lineWidth) { _lineWidth = lineWidth; }
|
||||
void setIsSolid(bool isSolid) { _isSolid = isSolid; }
|
||||
void setIsDashedLine(bool isDashedLine) { _isDashedLine = isDashedLine; }
|
||||
void setIgnoreRayIntersection(bool value) { _ignoreRayIntersection = value; }
|
||||
|
@ -85,7 +83,6 @@ protected:
|
|||
void setRenderVisible(bool visible);
|
||||
const Transform& getRenderTransform() const { return _renderTransform; }
|
||||
|
||||
float _lineWidth;
|
||||
bool _isSolid;
|
||||
bool _isDashedLine;
|
||||
bool _ignoreRayIntersection;
|
||||
|
|
|
@ -38,8 +38,6 @@ ContextOverlayInterface::ContextOverlayInterface() {
|
|||
_tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
_selectionScriptingInterface = DependencyManager::get<SelectionScriptingInterface>();
|
||||
|
||||
_selectionToSceneHandler.initialize("contextOverlayHighlightList");
|
||||
|
||||
_entityPropertyFlags += PROP_POSITION;
|
||||
_entityPropertyFlags += PROP_ROTATION;
|
||||
_entityPropertyFlags += PROP_MARKETPLACE_ID;
|
||||
|
@ -66,12 +64,20 @@ ContextOverlayInterface::ContextOverlayInterface() {
|
|||
}
|
||||
});
|
||||
connect(entityScriptingInterface, &EntityScriptingInterface::deletingEntity, this, &ContextOverlayInterface::deletingEntity);
|
||||
|
||||
connect(&qApp->getOverlays(), &Overlays::mousePressOnOverlay, this, &ContextOverlayInterface::contextOverlays_mousePressOnOverlay);
|
||||
connect(&qApp->getOverlays(), &Overlays::hoverEnterOverlay, this, &ContextOverlayInterface::contextOverlays_hoverEnterOverlay);
|
||||
connect(&qApp->getOverlays(), &Overlays::hoverLeaveOverlay, this, &ContextOverlayInterface::contextOverlays_hoverLeaveOverlay);
|
||||
|
||||
connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &_selectionToSceneHandler, &SelectionToSceneHandler::selectedItemsListChanged);
|
||||
{
|
||||
render::Transaction transaction;
|
||||
initializeSelectionToSceneHandler(_selectionToSceneHandlers[0], "contextOverlayHighlightList", transaction);
|
||||
for (auto i = 1; i < MAX_SELECTION_COUNT; i++) {
|
||||
auto selectionName = QString("highlightList") + QString::number(i);
|
||||
initializeSelectionToSceneHandler(_selectionToSceneHandlers[i], selectionName, transaction);
|
||||
}
|
||||
const render::ScenePointer& scene = qApp->getMain3DScene();
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
|
@ -79,6 +85,12 @@ ContextOverlayInterface::ContextOverlayInterface() {
|
|||
_challengeOwnershipTimeoutTimer.setSingleShot(true);
|
||||
}
|
||||
|
||||
void ContextOverlayInterface::initializeSelectionToSceneHandler(SelectionToSceneHandler& handler, const QString& selectionName, render::Transaction& transaction) {
|
||||
handler.initialize(selectionName);
|
||||
connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &handler, &SelectionToSceneHandler::selectedItemsListChanged);
|
||||
transaction.resetSelectionHighlight(selectionName.toStdString());
|
||||
}
|
||||
|
||||
static const uint32_t MOUSE_HW_ID = 0;
|
||||
static const uint32_t LEFT_HAND_HW_ID = 1;
|
||||
static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 };
|
||||
|
|
|
@ -47,6 +47,7 @@ class ContextOverlayInterface : public QObject, public Dependency {
|
|||
OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID };
|
||||
std::shared_ptr<Image3DOverlay> _contextOverlay { nullptr };
|
||||
public:
|
||||
|
||||
ContextOverlayInterface();
|
||||
|
||||
Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; }
|
||||
|
@ -75,6 +76,11 @@ private slots:
|
|||
void handleChallengeOwnershipReplyPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||
|
||||
private:
|
||||
|
||||
enum {
|
||||
MAX_SELECTION_COUNT = 16
|
||||
};
|
||||
|
||||
bool _verboseLogging { true };
|
||||
bool _enabled { true };
|
||||
EntityItemID _currentEntityWithContextOverlay{};
|
||||
|
@ -90,8 +96,9 @@ private:
|
|||
void disableEntityHighlight(const EntityItemID& entityItemID);
|
||||
|
||||
void deletingEntity(const EntityItemID& entityItemID);
|
||||
void initializeSelectionToSceneHandler(SelectionToSceneHandler& handler, const QString& selectionName, render::Transaction& transaction);
|
||||
|
||||
SelectionToSceneHandler _selectionToSceneHandler;
|
||||
SelectionToSceneHandler _selectionToSceneHandlers[MAX_SELECTION_COUNT];
|
||||
|
||||
Q_INVOKABLE void startChallengeOwnershipTimer();
|
||||
QTimer _challengeOwnershipTimeoutTimer;
|
||||
|
|
|
@ -33,8 +33,8 @@ Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) :
|
|||
_length = line3DOverlay->getLength();
|
||||
_endParentID = line3DOverlay->getEndParentID();
|
||||
_endParentJointIndex = line3DOverlay->getEndJointIndex();
|
||||
_lineWidth = line3DOverlay->getLineWidth();
|
||||
_glow = line3DOverlay->getGlow();
|
||||
_glowWidth = line3DOverlay->getGlowWidth();
|
||||
}
|
||||
|
||||
Line3DOverlay::~Line3DOverlay() {
|
||||
|
@ -145,7 +145,7 @@ void Line3DOverlay::render(RenderArgs* args) {
|
|||
geometryCache->renderDashedLine(*batch, start, end, colorv4, _geometryCacheID);
|
||||
} else {
|
||||
// renderGlowLine handles both glow = 0 and glow > 0 cases
|
||||
geometryCache->renderGlowLine(*batch, start, end, colorv4, _glow, _glowWidth, _geometryCacheID);
|
||||
geometryCache->renderGlowLine(*batch, start, end, colorv4, _glow, _lineWidth, _geometryCacheID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -239,11 +239,10 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
|||
}
|
||||
}
|
||||
|
||||
auto glowWidth = properties["glowWidth"];
|
||||
if (glowWidth.isValid()) {
|
||||
setGlowWidth(glowWidth.toFloat());
|
||||
auto lineWidth = properties["lineWidth"];
|
||||
if (lineWidth.isValid()) {
|
||||
setLineWidth(lineWidth.toFloat());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QVariant Line3DOverlay::getProperty(const QString& property) {
|
||||
|
@ -262,6 +261,9 @@ QVariant Line3DOverlay::getProperty(const QString& property) {
|
|||
if (property == "length") {
|
||||
return QVariant(getLength());
|
||||
}
|
||||
if (property == "lineWidth") {
|
||||
return _lineWidth;
|
||||
}
|
||||
|
||||
return Base3DOverlay::getProperty(property);
|
||||
}
|
||||
|
|
|
@ -31,8 +31,8 @@ public:
|
|||
// getters
|
||||
glm::vec3 getStart() const;
|
||||
glm::vec3 getEnd() const;
|
||||
const float& getLineWidth() const { return _lineWidth; }
|
||||
const float& getGlow() const { return _glow; }
|
||||
const float& getGlowWidth() const { return _glowWidth; }
|
||||
|
||||
// setters
|
||||
void setStart(const glm::vec3& start);
|
||||
|
@ -41,8 +41,8 @@ public:
|
|||
void setLocalStart(const glm::vec3& localStart) { setLocalPosition(localStart); }
|
||||
void setLocalEnd(const glm::vec3& localEnd);
|
||||
|
||||
void setLineWidth(const float& lineWidth) { _lineWidth = lineWidth; }
|
||||
void setGlow(const float& glow) { _glow = glow; }
|
||||
void setGlowWidth(const float& glowWidth) { _glowWidth = glowWidth; }
|
||||
|
||||
void setProperties(const QVariantMap& properties) override;
|
||||
QVariant getProperty(const QString& property) override;
|
||||
|
@ -70,8 +70,9 @@ private:
|
|||
glm::vec3 _direction; // in parent frame
|
||||
float _length { 1.0 }; // in parent frame
|
||||
|
||||
const float DEFAULT_LINE_WIDTH = 0.02f;
|
||||
float _lineWidth { DEFAULT_LINE_WIDTH };
|
||||
float _glow { 0.0 };
|
||||
float _glowWidth { 0.0 };
|
||||
int _geometryCacheID;
|
||||
};
|
||||
|
||||
|
|
|
@ -39,7 +39,12 @@ static std::mutex rigRegistryMutex;
|
|||
|
||||
static bool isEqual(const glm::vec3& u, const glm::vec3& v) {
|
||||
const float EPSILON = 0.0001f;
|
||||
return glm::length(u - v) / glm::length(u) <= EPSILON;
|
||||
float uLen = glm::length(u);
|
||||
if (uLen == 0.0f) {
|
||||
return glm::length(v) <= EPSILON;
|
||||
} else {
|
||||
return (glm::length(u - v) / uLen) <= EPSILON;
|
||||
}
|
||||
}
|
||||
|
||||
static bool isEqual(const glm::quat& p, const glm::quat& q) {
|
||||
|
@ -174,6 +179,11 @@ void Rig::restoreRoleAnimation(const QString& role) {
|
|||
} else {
|
||||
qCWarning(animation) << "Rig::restoreRoleAnimation could not find role " << role;
|
||||
}
|
||||
|
||||
auto statesIter = _roleAnimStates.find(role);
|
||||
if (statesIter != _roleAnimStates.end()) {
|
||||
_roleAnimStates.erase(statesIter);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qCWarning(animation) << "Rig::overrideRoleAnimation avatar not ready yet";
|
||||
|
@ -303,7 +313,9 @@ void Rig::setModelOffset(const glm::mat4& modelOffsetMat) {
|
|||
_rigToGeometryTransform = glm::inverse(_geometryToRigTransform);
|
||||
|
||||
// rebuild cached default poses
|
||||
buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses);
|
||||
if (_animSkeleton) {
|
||||
buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -453,7 +453,9 @@ void Avatar::applyPositionDelta(const glm::vec3& delta) {
|
|||
void Avatar::measureMotionDerivatives(float deltaTime) {
|
||||
PerformanceTimer perfTimer("derivatives");
|
||||
// linear
|
||||
float invDeltaTime = 1.0f / deltaTime;
|
||||
const float MIN_DELTA_TIME = 0.001f;
|
||||
const float safeDeltaTime = glm::max(deltaTime, MIN_DELTA_TIME);
|
||||
float invDeltaTime = 1.0f / safeDeltaTime;
|
||||
// Floating point error prevents us from computing velocity in a naive way
|
||||
// (e.g. vel = (pos - oldPos) / dt) so instead we use _positionOffsetAccumulator.
|
||||
glm::vec3 velocity = _positionDeltaAccumulator * invDeltaTime;
|
||||
|
@ -1577,7 +1579,7 @@ float Avatar::getEyeHeight() const {
|
|||
|
||||
if (QThread::currentThread() != thread()) {
|
||||
float result = DEFAULT_AVATAR_EYE_HEIGHT;
|
||||
BLOCKING_INVOKE_METHOD(const_cast<Avatar*>(this), "getHeight", Q_RETURN_ARG(float, result));
|
||||
BLOCKING_INVOKE_METHOD(const_cast<Avatar*>(this), "getEyeHeight", Q_RETURN_ARG(float, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,21 +15,7 @@
|
|||
#include <string>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
/* VS2010 defines stdint.h, but not inttypes.h */
|
||||
#if defined(_MSC_VER)
|
||||
typedef signed char int8_t;
|
||||
typedef signed short int16_t;
|
||||
typedef signed int int32_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef signed long long int64_t;
|
||||
typedef unsigned long long quint64;
|
||||
#define PRId64 "I64d"
|
||||
#else
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
#include <vector>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
|
|
@ -40,22 +40,26 @@ void ZoneEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity
|
|||
if (_stage) {
|
||||
if (!LightStage::isIndexInvalid(_sunIndex)) {
|
||||
_stage->removeLight(_sunIndex);
|
||||
_sunIndex = INVALID_INDEX;
|
||||
_shadowIndex = INVALID_INDEX;
|
||||
}
|
||||
if (!LightStage::isIndexInvalid(_ambientIndex)) {
|
||||
_stage->removeLight(_ambientIndex);
|
||||
|
||||
_ambientIndex = INVALID_INDEX;
|
||||
}
|
||||
}
|
||||
|
||||
if (_backgroundStage) {
|
||||
if (!BackgroundStage::isIndexInvalid(_backgroundIndex)) {
|
||||
_backgroundStage->removeBackground(_backgroundIndex);
|
||||
_backgroundIndex = INVALID_INDEX;
|
||||
}
|
||||
}
|
||||
|
||||
if (_hazeStage) {
|
||||
if (!HazeStage::isIndexInvalid(_hazeIndex)) {
|
||||
_hazeStage->removeHaze(_hazeIndex);
|
||||
_hazeIndex = INVALID_INDEX;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,7 +97,8 @@ bool operator==(const Properties& a, const Properties& b) {
|
|||
(a.maxParticles == b.maxParticles) &&
|
||||
(a.emission == b.emission) &&
|
||||
(a.polar == b.polar) &&
|
||||
(a.azimuth == b.azimuth);
|
||||
(a.azimuth == b.azimuth) &&
|
||||
(a.textures == b.textures);
|
||||
}
|
||||
|
||||
bool operator!=(const Properties& a, const Properties& b) {
|
||||
|
|
|
@ -297,7 +297,7 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f;
|
||||
if (diameter > MIN_DIAMETER
|
||||
&& fabsf(diameter - entityDimensions.z) / diameter < MIN_RELATIVE_SPHERICAL_ERROR) {
|
||||
_collisionShapeType = SHAPE_TYPE_SPHERE;
|
||||
_collisionShapeType = SHAPE_TYPE_CYLINDER_Y;
|
||||
} else if (hullShapeCalculator) {
|
||||
hullShapeCalculator(this, info);
|
||||
_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL;
|
||||
|
|
|
@ -110,6 +110,7 @@ static const GLenum ELEMENT_TYPE_TO_GL[gpu::NUM_TYPES] = {
|
|||
GL_SHORT,
|
||||
GL_UNSIGNED_SHORT,
|
||||
GL_BYTE,
|
||||
GL_UNSIGNED_BYTE,
|
||||
GL_UNSIGNED_BYTE
|
||||
};
|
||||
|
||||
|
|
|
@ -212,6 +212,9 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
|
|||
case gpu::NUINT8:
|
||||
result = GL_RGBA8;
|
||||
break;
|
||||
case gpu::NUINT2:
|
||||
result = GL_RGBA2;
|
||||
break;
|
||||
case gpu::NINT8:
|
||||
result = GL_RGBA8_SNORM;
|
||||
break;
|
||||
|
@ -498,6 +501,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
|
|||
break;
|
||||
}
|
||||
case gpu::COMPRESSED:
|
||||
case gpu::NUINT2:
|
||||
case gpu::NUM_TYPES: { // quiet compiler
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
@ -548,6 +552,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
|
|||
break;
|
||||
}
|
||||
case gpu::COMPRESSED:
|
||||
case gpu::NUINT2:
|
||||
case gpu::NUM_TYPES: { // quiet compiler
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
@ -660,6 +665,10 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
|
|||
texel.format = GL_RGBA;
|
||||
texel.internalFormat = GL_RGBA8_SNORM;
|
||||
break;
|
||||
case gpu::NUINT2:
|
||||
texel.format = GL_RGBA;
|
||||
texel.internalFormat = GL_RGBA2;
|
||||
break;
|
||||
case gpu::NUINT32:
|
||||
case gpu::NINT32:
|
||||
case gpu::COMPRESSED:
|
||||
|
|
19
libraries/gpu/src/gpu/DrawColor.slf
Normal file
19
libraries/gpu/src/gpu/DrawColor.slf
Normal file
|
@ -0,0 +1,19 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// Draw with color uniform
|
||||
//
|
||||
// Created by Olivier Prat on 25/10/2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
uniform vec4 color;
|
||||
|
||||
out vec4 outFragColor;
|
||||
|
||||
void main(void) {
|
||||
outFragColor = color;
|
||||
}
|
|
@ -19,6 +19,8 @@ const Element Element::COLOR_SRGBA_32{ VEC4, NUINT8, SRGBA };
|
|||
const Element Element::COLOR_BGRA_32{ VEC4, NUINT8, BGRA };
|
||||
const Element Element::COLOR_SBGRA_32{ VEC4, NUINT8, SBGRA };
|
||||
|
||||
const Element Element::COLOR_RGBA_2{ VEC4, NUINT2, RGBA };
|
||||
|
||||
const Element Element::COLOR_COMPRESSED_RED{ TILE4x4, COMPRESSED, COMPRESSED_BC4_RED };
|
||||
const Element Element::COLOR_COMPRESSED_SRGB { TILE4x4, COMPRESSED, COMPRESSED_BC1_SRGB };
|
||||
const Element Element::COLOR_COMPRESSED_SRGBA_MASK { TILE4x4, COMPRESSED, COMPRESSED_BC1_SRGBA };
|
||||
|
|
|
@ -38,6 +38,7 @@ enum Type : uint8_t {
|
|||
NUINT16,
|
||||
NINT8,
|
||||
NUINT8,
|
||||
NUINT2,
|
||||
|
||||
COMPRESSED,
|
||||
|
||||
|
@ -309,6 +310,7 @@ public:
|
|||
static const Element COLOR_SRGBA_32;
|
||||
static const Element COLOR_BGRA_32;
|
||||
static const Element COLOR_SBGRA_32;
|
||||
static const Element COLOR_RGBA_2;
|
||||
static const Element COLOR_R11G11B10;
|
||||
static const Element COLOR_RGB9E5;
|
||||
static const Element COLOR_COMPRESSED_RED;
|
||||
|
|
|
@ -220,7 +220,7 @@ uint32 Framebuffer::getRenderBufferSubresource(uint32 slot) const {
|
|||
}
|
||||
}
|
||||
|
||||
bool Framebuffer::setDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource) {
|
||||
bool Framebuffer::assignDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource) {
|
||||
if (isSwapchain()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -244,20 +244,59 @@ bool Framebuffer::setDepthStencilBuffer(const TexturePointer& texture, const For
|
|||
// assign the new one
|
||||
_depthStencilBuffer = TextureView(texture, subresource, format);
|
||||
|
||||
_bufferMask = ( _bufferMask & ~BUFFER_DEPTHSTENCIL);
|
||||
if (texture) {
|
||||
if (format.getSemantic() == gpu::DEPTH) {
|
||||
_bufferMask |= BUFFER_DEPTH;
|
||||
} else if (format.getSemantic() == gpu::STENCIL) {
|
||||
_bufferMask |= BUFFER_STENCIL;
|
||||
} else if (format.getSemantic() == gpu::DEPTH_STENCIL) {
|
||||
_bufferMask |= BUFFER_DEPTHSTENCIL;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Framebuffer::setDepthBuffer(const TexturePointer& texture, const Format& format, uint32 subresource) {
|
||||
if (assignDepthStencilBuffer(texture, format, subresource)) {
|
||||
_bufferMask = (_bufferMask & ~BUFFER_DEPTHSTENCIL);
|
||||
if (texture) {
|
||||
if (format.getSemantic() == gpu::DEPTH || format.getSemantic() == gpu::DEPTH_STENCIL) {
|
||||
_bufferMask |= BUFFER_DEPTH;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Framebuffer::setStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource) {
|
||||
if (assignDepthStencilBuffer(texture, format, subresource)) {
|
||||
_bufferMask = (_bufferMask & ~BUFFER_DEPTHSTENCIL);
|
||||
if (texture) {
|
||||
if (format.getSemantic() == gpu::STENCIL || format.getSemantic() == gpu::DEPTH_STENCIL) {
|
||||
_bufferMask |= BUFFER_STENCIL;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Framebuffer::setDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource) {
|
||||
if (assignDepthStencilBuffer(texture, format, subresource)) {
|
||||
_bufferMask = (_bufferMask & ~BUFFER_DEPTHSTENCIL);
|
||||
if (texture) {
|
||||
if (format.getSemantic() == gpu::DEPTH) {
|
||||
_bufferMask |= BUFFER_DEPTH;
|
||||
} else if (format.getSemantic() == gpu::STENCIL) {
|
||||
_bufferMask |= BUFFER_STENCIL;
|
||||
} else if (format.getSemantic() == gpu::DEPTH_STENCIL) {
|
||||
_bufferMask |= BUFFER_DEPTHSTENCIL;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TexturePointer Framebuffer::getDepthStencilBuffer() const {
|
||||
if (isSwapchain()) {
|
||||
return TexturePointer();
|
||||
|
|
|
@ -107,6 +107,8 @@ public:
|
|||
TexturePointer getRenderBuffer(uint32 slot) const;
|
||||
uint32 getRenderBufferSubresource(uint32 slot) const;
|
||||
|
||||
bool setDepthBuffer(const TexturePointer& texture, const Format& format, uint32 subresource = 0);
|
||||
bool setStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource = 0);
|
||||
bool setDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource = 0);
|
||||
TexturePointer getDepthStencilBuffer() const;
|
||||
uint32 getDepthStencilBufferSubresource() const;
|
||||
|
@ -168,6 +170,7 @@ protected:
|
|||
uint16 _numSamples = 0;
|
||||
|
||||
void updateSize(const TexturePointer& texture);
|
||||
bool assignDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource);
|
||||
|
||||
// Non exposed
|
||||
Framebuffer(const Framebuffer& framebuffer) = delete;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
const char DrawNada_frag[] = "void main(void) {}"; // DrawNada is really simple...
|
||||
|
||||
#include "DrawWhite_frag.h"
|
||||
#include "DrawColor_frag.h"
|
||||
#include "DrawTexture_frag.h"
|
||||
#include "DrawTextureMirroredX_frag.h"
|
||||
#include "DrawTextureOpaque_frag.h"
|
||||
|
@ -37,6 +38,7 @@ ShaderPointer StandardShaderLib::_drawVertexPositionVS;
|
|||
ShaderPointer StandardShaderLib::_drawTransformVertexPositionVS;
|
||||
ShaderPointer StandardShaderLib::_drawNadaPS;
|
||||
ShaderPointer StandardShaderLib::_drawWhitePS;
|
||||
ShaderPointer StandardShaderLib::_drawColorPS;
|
||||
ShaderPointer StandardShaderLib::_drawTexturePS;
|
||||
ShaderPointer StandardShaderLib::_drawTextureMirroredXPS;
|
||||
ShaderPointer StandardShaderLib::_drawTextureOpaquePS;
|
||||
|
@ -125,6 +127,13 @@ ShaderPointer StandardShaderLib::getDrawWhitePS() {
|
|||
return _drawWhitePS;
|
||||
}
|
||||
|
||||
ShaderPointer StandardShaderLib::getDrawColorPS() {
|
||||
if (!_drawColorPS) {
|
||||
_drawColorPS = gpu::Shader::createPixel(std::string(DrawColor_frag));
|
||||
}
|
||||
return _drawColorPS;
|
||||
}
|
||||
|
||||
ShaderPointer StandardShaderLib::getDrawTexturePS() {
|
||||
if (!_drawTexturePS) {
|
||||
_drawTexturePS = gpu::Shader::createPixel(std::string(DrawTexture_frag));
|
||||
|
|
|
@ -46,6 +46,7 @@ public:
|
|||
static ShaderPointer getDrawNadaPS();
|
||||
|
||||
static ShaderPointer getDrawWhitePS();
|
||||
static ShaderPointer getDrawColorPS();
|
||||
static ShaderPointer getDrawTexturePS();
|
||||
static ShaderPointer getDrawTextureMirroredXPS();
|
||||
static ShaderPointer getDrawTextureOpaquePS();
|
||||
|
@ -67,6 +68,7 @@ protected:
|
|||
|
||||
static ShaderPointer _drawNadaPS;
|
||||
static ShaderPointer _drawWhitePS;
|
||||
static ShaderPointer _drawColorPS;
|
||||
static ShaderPointer _drawTexturePS;
|
||||
static ShaderPointer _drawTextureMirroredXPS;
|
||||
static ShaderPointer _drawTextureOpaquePS;
|
||||
|
|
|
@ -193,13 +193,17 @@ TransformObject getTransformObject() {
|
|||
}
|
||||
<@endfunc@>
|
||||
|
||||
<@func transformModelToClipPos(cameraTransform, objectTransform, modelPos, clipPos)@>
|
||||
{ // transformModelToClipPos
|
||||
<@func transformModelToMonoClipPos(cameraTransform, objectTransform, modelPos, clipPos)@>
|
||||
{ // transformModelToMonoClipPos
|
||||
vec4 eyeWAPos;
|
||||
<$transformModelToEyeWorldAlignedPos($cameraTransform$, $objectTransform$, $modelPos$, eyeWAPos)$>
|
||||
|
||||
<$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * eyeWAPos;
|
||||
|
||||
}
|
||||
<@endfunc@>
|
||||
|
||||
<@func transformModelToClipPos(cameraTransform, objectTransform, modelPos, clipPos)@>
|
||||
{ // transformModelToClipPos
|
||||
<$transformModelToMonoClipPos($cameraTransform$, $objectTransform$, $modelPos$, $clipPos$)$>
|
||||
<$transformStereoClipsSpace($cameraTransform$, $clipPos$)$>
|
||||
}
|
||||
<@endfunc@>
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include <memory>
|
||||
|
||||
#include <memory>
|
||||
#include "Haze.h"
|
||||
|
||||
using namespace model;
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include <NumericalConstants.h>
|
||||
#include <SettingHandle.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <StatTracker.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "AccountManager.h"
|
||||
|
@ -1110,7 +1109,6 @@ void LimitedNodeList::setLocalSocket(const HifiSockAddr& sockAddr) {
|
|||
qCInfo(networking) << "Local socket is" << sockAddr;
|
||||
} else {
|
||||
qCInfo(networking) << "Local socket has changed from" << _localSockAddr << "to" << sockAddr;
|
||||
DependencyManager::get<StatTracker>()->incrementStat(LOCAL_SOCKET_CHANGE_STAT);
|
||||
}
|
||||
|
||||
_localSockAddr = sockAddr;
|
||||
|
|
|
@ -66,8 +66,6 @@ const QHostAddress DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME = QHostAddress::Lo
|
|||
|
||||
const QString USERNAME_UUID_REPLACEMENT_STATS_KEY = "$username";
|
||||
|
||||
const QString LOCAL_SOCKET_CHANGE_STAT = "LocalSocketChanges";
|
||||
|
||||
typedef std::pair<QUuid, SharedNodePointer> UUIDNodePair;
|
||||
typedef tbb::concurrent_unordered_map<QUuid, SharedNodePointer, UUIDHasher> NodeHash;
|
||||
|
||||
|
|
|
@ -12,21 +12,7 @@
|
|||
#ifndef hifi_OctreeQuery_h
|
||||
#define hifi_OctreeQuery_h
|
||||
|
||||
/* VS2010 defines stdint.h, but not inttypes.h */
|
||||
#if defined(_MSC_VER)
|
||||
typedef signed char int8_t;
|
||||
typedef signed short int16_t;
|
||||
typedef signed int int32_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef signed long long int64_t;
|
||||
typedef unsigned long long quint64;
|
||||
#define PRId64 "I64d"
|
||||
#else
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
@ -45,7 +31,7 @@ public:
|
|||
virtual ~OctreeQuery() {}
|
||||
|
||||
int getBroadcastData(unsigned char* destinationBuffer);
|
||||
int parseData(ReceivedMessage& message) override;
|
||||
virtual int parseData(ReceivedMessage& message) override;
|
||||
|
||||
// getters for camera details
|
||||
const glm::vec3& getCameraPosition() const { return _cameraPosition; }
|
||||
|
|
|
@ -18,6 +18,12 @@
|
|||
#include <SharedUtil.h>
|
||||
#include <UUID.h>
|
||||
|
||||
int OctreeQueryNode::parseData(ReceivedMessage& message) {
|
||||
// set our flag to indicate that we've parsed for this query at least once
|
||||
_hasReceivedFirstQuery = true;
|
||||
|
||||
return OctreeQuery::parseData(message);
|
||||
}
|
||||
|
||||
void OctreeQueryNode::nodeKilled() {
|
||||
_isShuttingDown = true;
|
||||
|
|
|
@ -35,6 +35,8 @@ public:
|
|||
void init(); // called after creation to set up some virtual items
|
||||
virtual PacketType getMyPacketType() const = 0;
|
||||
|
||||
virtual int parseData(ReceivedMessage& message) override;
|
||||
|
||||
void resetOctreePacket(); // resets octree packet to after "V" header
|
||||
|
||||
void writeToPacket(const unsigned char* buffer, unsigned int bytes); // writes to end of packet
|
||||
|
@ -106,6 +108,8 @@ public:
|
|||
bool shouldForceFullScene() const { return _shouldForceFullScene; }
|
||||
void setShouldForceFullScene(bool shouldForceFullScene) { _shouldForceFullScene = shouldForceFullScene; }
|
||||
|
||||
bool hasReceivedFirstQuery() const { return _hasReceivedFirstQuery; }
|
||||
|
||||
private:
|
||||
OctreeQueryNode(const OctreeQueryNode &);
|
||||
OctreeQueryNode& operator= (const OctreeQueryNode&);
|
||||
|
@ -153,6 +157,8 @@ private:
|
|||
QJsonObject _lastCheckJSONParameters;
|
||||
|
||||
bool _shouldForceFullScene { false };
|
||||
|
||||
bool _hasReceivedFirstQuery { false };
|
||||
};
|
||||
|
||||
#endif // hifi_OctreeQueryNode_h
|
||||
|
|
|
@ -16,6 +16,40 @@
|
|||
#include "ShapeFactory.h"
|
||||
#include "BulletUtil.h"
|
||||
|
||||
|
||||
class StaticMeshShape : public btBvhTriangleMeshShape {
|
||||
public:
|
||||
StaticMeshShape() = delete;
|
||||
|
||||
StaticMeshShape(btTriangleIndexVertexArray* dataArray)
|
||||
: btBvhTriangleMeshShape(dataArray, true), _dataArray(dataArray) {
|
||||
assert(_dataArray);
|
||||
}
|
||||
|
||||
~StaticMeshShape() {
|
||||
assert(_dataArray);
|
||||
IndexedMeshArray& meshes = _dataArray->getIndexedMeshArray();
|
||||
for (int32_t i = 0; i < meshes.size(); ++i) {
|
||||
btIndexedMesh mesh = meshes[i];
|
||||
mesh.m_numTriangles = 0;
|
||||
delete [] mesh.m_triangleIndexBase;
|
||||
mesh.m_triangleIndexBase = nullptr;
|
||||
mesh.m_numVertices = 0;
|
||||
delete [] mesh.m_vertexBase;
|
||||
mesh.m_vertexBase = nullptr;
|
||||
}
|
||||
meshes.clear();
|
||||
delete _dataArray;
|
||||
_dataArray = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
// the StaticMeshShape owns its vertex/index data
|
||||
btTriangleIndexVertexArray* _dataArray;
|
||||
};
|
||||
|
||||
// the dataArray must be created before we create the StaticMeshShape
|
||||
|
||||
// These are the same normalized directions used by the btShapeHull class.
|
||||
// 12 points for the face centers of a dodecahedron plus another 30 points
|
||||
// for the midpoints the edges, for a total of 42.
|
||||
|
@ -230,23 +264,6 @@ btTriangleIndexVertexArray* createStaticMeshArray(const ShapeInfo& info) {
|
|||
return dataArray;
|
||||
}
|
||||
|
||||
// util method
|
||||
void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray) {
|
||||
assert(dataArray);
|
||||
IndexedMeshArray& meshes = dataArray->getIndexedMeshArray();
|
||||
for (int32_t i = 0; i < meshes.size(); ++i) {
|
||||
btIndexedMesh mesh = meshes[i];
|
||||
mesh.m_numTriangles = 0;
|
||||
delete [] mesh.m_triangleIndexBase;
|
||||
mesh.m_triangleIndexBase = nullptr;
|
||||
mesh.m_numVertices = 0;
|
||||
delete [] mesh.m_vertexBase;
|
||||
mesh.m_vertexBase = nullptr;
|
||||
}
|
||||
meshes.clear();
|
||||
delete dataArray;
|
||||
}
|
||||
|
||||
const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) {
|
||||
btCollisionShape* shape = NULL;
|
||||
int type = info.getType();
|
||||
|
@ -431,7 +448,6 @@ void ShapeFactory::deleteShape(const btCollisionShape* shape) {
|
|||
assert(shape);
|
||||
// ShapeFactory is responsible for deleting all shapes, even the const ones that are stored
|
||||
// in the ShapeManager, so we must cast to non-const here when deleting.
|
||||
// so we cast to non-const here when deleting memory.
|
||||
btCollisionShape* nonConstShape = const_cast<btCollisionShape*>(shape);
|
||||
if (nonConstShape->getShapeType() == (int)COMPOUND_SHAPE_PROXYTYPE) {
|
||||
btCompoundShape* compoundShape = static_cast<btCompoundShape*>(nonConstShape);
|
||||
|
@ -448,14 +464,3 @@ void ShapeFactory::deleteShape(const btCollisionShape* shape) {
|
|||
}
|
||||
delete nonConstShape;
|
||||
}
|
||||
|
||||
// the dataArray must be created before we create the StaticMeshShape
|
||||
ShapeFactory::StaticMeshShape::StaticMeshShape(btTriangleIndexVertexArray* dataArray)
|
||||
: btBvhTriangleMeshShape(dataArray, true), _dataArray(dataArray) {
|
||||
assert(dataArray);
|
||||
}
|
||||
|
||||
ShapeFactory::StaticMeshShape::~StaticMeshShape() {
|
||||
deleteStaticMeshArray(_dataArray);
|
||||
_dataArray = nullptr;
|
||||
}
|
||||
|
|
|
@ -17,25 +17,11 @@
|
|||
|
||||
#include <ShapeInfo.h>
|
||||
|
||||
// translates between ShapeInfo and btShape
|
||||
// The ShapeFactory assembles and correctly disassembles btCollisionShapes.
|
||||
|
||||
namespace ShapeFactory {
|
||||
const btCollisionShape* createShapeFromInfo(const ShapeInfo& info);
|
||||
void deleteShape(const btCollisionShape* shape);
|
||||
|
||||
//btTriangleIndexVertexArray* createStaticMeshArray(const ShapeInfo& info);
|
||||
//void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray);
|
||||
|
||||
class StaticMeshShape : public btBvhTriangleMeshShape {
|
||||
public:
|
||||
StaticMeshShape() = delete;
|
||||
StaticMeshShape(btTriangleIndexVertexArray* dataArray);
|
||||
~StaticMeshShape();
|
||||
|
||||
private:
|
||||
// the StaticMeshShape owns its vertex/index data
|
||||
btTriangleIndexVertexArray* _dataArray;
|
||||
};
|
||||
};
|
||||
|
||||
#endif // hifi_ShapeFactory_h
|
||||
|
|
|
@ -32,7 +32,7 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
|||
if (info.getType() == SHAPE_TYPE_NONE) {
|
||||
return nullptr;
|
||||
}
|
||||
DoubleHashKey key = info.getHash();
|
||||
HashKey key = info.getHash();
|
||||
ShapeReference* shapeRef = _shapeMap.find(key);
|
||||
if (shapeRef) {
|
||||
shapeRef->refCount++;
|
||||
|
@ -50,7 +50,7 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
|||
}
|
||||
|
||||
// private helper method
|
||||
bool ShapeManager::releaseShapeByKey(const DoubleHashKey& key) {
|
||||
bool ShapeManager::releaseShapeByKey(const HashKey& key) {
|
||||
ShapeReference* shapeRef = _shapeMap.find(key);
|
||||
if (shapeRef) {
|
||||
if (shapeRef->refCount > 0) {
|
||||
|
@ -88,7 +88,7 @@ bool ShapeManager::releaseShape(const btCollisionShape* shape) {
|
|||
void ShapeManager::collectGarbage() {
|
||||
int numShapes = _pendingGarbage.size();
|
||||
for (int i = 0; i < numShapes; ++i) {
|
||||
DoubleHashKey& key = _pendingGarbage[i];
|
||||
HashKey& key = _pendingGarbage[i];
|
||||
ShapeReference* shapeRef = _shapeMap.find(key);
|
||||
if (shapeRef && shapeRef->refCount == 0) {
|
||||
ShapeFactory::deleteShape(shapeRef->shape);
|
||||
|
@ -99,7 +99,7 @@ void ShapeManager::collectGarbage() {
|
|||
}
|
||||
|
||||
int ShapeManager::getNumReferences(const ShapeInfo& info) const {
|
||||
DoubleHashKey key = info.getHash();
|
||||
HashKey key = info.getHash();
|
||||
const ShapeReference* shapeRef = _shapeMap.find(key);
|
||||
if (shapeRef) {
|
||||
return shapeRef->refCount;
|
||||
|
|
|
@ -17,7 +17,29 @@
|
|||
|
||||
#include <ShapeInfo.h>
|
||||
|
||||
#include "DoubleHashKey.h"
|
||||
#include "HashKey.h"
|
||||
|
||||
// The ShapeManager handles the ref-counting on shared shapes:
|
||||
//
|
||||
// Each object added to the physics simulation gets a corresponding btRigidBody.
|
||||
// The body has a btCollisionShape that represents the contours of its collision
|
||||
// surface. Multiple bodies may have the same shape. Rather than create a unique
|
||||
// btCollisionShape instance for every body with a particular shape we can instead
|
||||
// use a single shape instance for all of the bodies. This is called "shape
|
||||
// sharing".
|
||||
//
|
||||
// When body needs a new shape a description of ths shape (ShapeInfo) is assembled
|
||||
// and a request is sent to the ShapeManager for a corresponding btCollisionShape
|
||||
// pointer. The ShapeManager will compute a hash of the ShapeInfo's data and use
|
||||
// that to find the shape in its map. If it finds one it increments the ref-count
|
||||
// and returns the pointer. If not it asks the ShapeFactory to create it, adds an
|
||||
// entry in the map with a ref-count of 1, and returns the pointer.
|
||||
//
|
||||
// When a body stops using a shape the ShapeManager must be informed so it can
|
||||
// decrement its ref-count. When a ref-count drops to zero the ShapeManager
|
||||
// doesn't delete it right away. Instead it puts the shape's key on a list delete
|
||||
// later. When that list grows big enough the ShapeManager will remove any matching
|
||||
// entries that still have zero ref-count.
|
||||
|
||||
class ShapeManager {
|
||||
public:
|
||||
|
@ -41,18 +63,19 @@ public:
|
|||
bool hasShape(const btCollisionShape* shape) const;
|
||||
|
||||
private:
|
||||
bool releaseShapeByKey(const DoubleHashKey& key);
|
||||
bool releaseShapeByKey(const HashKey& key);
|
||||
|
||||
class ShapeReference {
|
||||
public:
|
||||
int refCount;
|
||||
const btCollisionShape* shape;
|
||||
DoubleHashKey key;
|
||||
HashKey key;
|
||||
ShapeReference() : refCount(0), shape(nullptr) {}
|
||||
};
|
||||
|
||||
btHashMap<DoubleHashKey, ShapeReference> _shapeMap;
|
||||
btAlignedObjectArray<DoubleHashKey> _pendingGarbage;
|
||||
// btHashMap is required because it supports memory alignment of the btCollisionShapes
|
||||
btHashMap<HashKey, ShapeReference> _shapeMap;
|
||||
btAlignedObjectArray<HashKey> _pendingGarbage;
|
||||
};
|
||||
|
||||
#endif // hifi_ShapeManager_h
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <gpu/Context.h>
|
||||
|
||||
std::string BackgroundStage::_stageName { "BACKGROUND_STAGE"};
|
||||
const BackgroundStage::Index BackgroundStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
|
||||
|
||||
BackgroundStage::Index BackgroundStage::findBackground(const BackgroundPointer& background) const {
|
||||
auto found = _backgroundMap.find(background);
|
||||
|
|
|
@ -27,7 +27,7 @@ public:
|
|||
static const std::string& getName() { return _stageName; }
|
||||
|
||||
using Index = render::indexed_container::Index;
|
||||
static const Index INVALID_INDEX { render::indexed_container::INVALID_INDEX };
|
||||
static const Index INVALID_INDEX;
|
||||
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
|
||||
|
||||
using BackgroundPointer = model::SunSkyStagePointer;
|
||||
|
|
27
libraries/render-utils/src/BloomApply.slf
Normal file
27
libraries/render-utils/src/BloomApply.slf
Normal file
|
@ -0,0 +1,27 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// BloomApply.slf
|
||||
// Mix the three gaussian blur textures.
|
||||
//
|
||||
// Created by Olivier Prat on 10/09/2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
uniform sampler2D blurMap0;
|
||||
uniform sampler2D blurMap1;
|
||||
uniform sampler2D blurMap2;
|
||||
uniform float intensity;
|
||||
|
||||
in vec2 varTexCoord0;
|
||||
out vec4 outFragColor;
|
||||
|
||||
void main(void) {
|
||||
vec4 blur0 = texture(blurMap0, varTexCoord0);
|
||||
vec4 blur1 = texture(blurMap1, varTexCoord0);
|
||||
vec4 blur2 = texture(blurMap2, varTexCoord0);
|
||||
|
||||
outFragColor = vec4((blur0.rgb+blur1.rgb+blur2.rgb)*intensity, 1.0f);
|
||||
}
|
359
libraries/render-utils/src/BloomEffect.cpp
Normal file
359
libraries/render-utils/src/BloomEffect.cpp
Normal file
|
@ -0,0 +1,359 @@
|
|||
//
|
||||
// BloomEffect.cpp
|
||||
// render-utils/src/
|
||||
//
|
||||
// Created by Olivier Prat on 09/25/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "BloomEffect.h"
|
||||
|
||||
#include "gpu/Context.h"
|
||||
#include "gpu/StandardShaderLib.h"
|
||||
|
||||
#include <render/BlurTask.h>
|
||||
#include <render/ResampleTask.h>
|
||||
|
||||
#include "BloomThreshold_frag.h"
|
||||
#include "BloomApply_frag.h"
|
||||
|
||||
#define BLOOM_BLUR_LEVEL_COUNT 3
|
||||
|
||||
BloomThreshold::BloomThreshold(unsigned int downsamplingFactor) :
|
||||
_downsamplingFactor(downsamplingFactor) {
|
||||
assert(downsamplingFactor > 0);
|
||||
}
|
||||
|
||||
void BloomThreshold::configure(const Config& config) {
|
||||
_threshold = config.threshold;
|
||||
}
|
||||
|
||||
void BloomThreshold::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
const auto frameTransform = inputs.get0();
|
||||
const auto inputFrameBuffer = inputs.get1();
|
||||
|
||||
assert(inputFrameBuffer->hasColor());
|
||||
|
||||
auto inputBuffer = inputFrameBuffer->getRenderBuffer(0);
|
||||
auto bufferSize = gpu::Vec2u(inputBuffer->getDimensions());
|
||||
|
||||
// Downsample resolution
|
||||
bufferSize.x /= _downsamplingFactor;
|
||||
bufferSize.y /= _downsamplingFactor;
|
||||
|
||||
if (!_outputBuffer || _outputBuffer->getSize() != bufferSize) {
|
||||
auto colorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(inputBuffer->getTexelFormat(), bufferSize.x, bufferSize.y,
|
||||
gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
||||
|
||||
_outputBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("BloomThreshold"));
|
||||
_outputBuffer->setRenderBuffer(0, colorTexture);
|
||||
}
|
||||
|
||||
static const int COLOR_MAP_SLOT = 0;
|
||||
static const int THRESHOLD_SLOT = 1;
|
||||
|
||||
if (!_pipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
|
||||
auto ps = gpu::Shader::createPixel(std::string(BloomThreshold_frag));
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding("colorMap", COLOR_MAP_SLOT));
|
||||
slotBindings.insert(gpu::Shader::Binding("threshold", THRESHOLD_SLOT));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
glm::ivec4 viewport{ 0, 0, bufferSize.x, bufferSize.y };
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
|
||||
batch.setViewportTransform(viewport);
|
||||
batch.setProjectionTransform(glm::mat4());
|
||||
batch.resetViewTransform();
|
||||
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(bufferSize, viewport));
|
||||
batch.setPipeline(_pipeline);
|
||||
|
||||
batch.setFramebuffer(_outputBuffer);
|
||||
batch.setResourceTexture(COLOR_MAP_SLOT, inputBuffer);
|
||||
batch._glUniform1f(THRESHOLD_SLOT, _threshold);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
|
||||
outputs = _outputBuffer;
|
||||
}
|
||||
|
||||
BloomApply::BloomApply() {
|
||||
|
||||
}
|
||||
|
||||
void BloomApply::configure(const Config& config) {
|
||||
_intensity = config.intensity;
|
||||
}
|
||||
|
||||
void BloomApply::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
static auto BLUR0_SLOT = 0;
|
||||
static auto BLUR1_SLOT = 1;
|
||||
static auto BLUR2_SLOT = 2;
|
||||
static auto INTENSITY_SLOT = 3;
|
||||
|
||||
if (!_pipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
|
||||
auto ps = gpu::Shader::createPixel(std::string(BloomApply_frag));
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding("blurMap0", BLUR0_SLOT));
|
||||
slotBindings.insert(gpu::Shader::Binding("blurMap1", BLUR1_SLOT));
|
||||
slotBindings.insert(gpu::Shader::Binding("blurMap2", BLUR2_SLOT));
|
||||
slotBindings.insert(gpu::Shader::Binding("intensity", INTENSITY_SLOT));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(gpu::State::DepthTest(false, false));
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
const auto frameBuffer = inputs.get0();
|
||||
const auto framebufferSize = frameBuffer->getSize();
|
||||
const auto blur0FB = inputs.get1();
|
||||
const auto blur1FB = inputs.get2();
|
||||
const auto blur2FB = inputs.get3();
|
||||
const glm::ivec4 viewport{ 0, 0, framebufferSize.x, framebufferSize.y };
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
|
||||
batch.setFramebuffer(frameBuffer);
|
||||
|
||||
batch.setViewportTransform(viewport);
|
||||
batch.setProjectionTransform(glm::mat4());
|
||||
batch.resetViewTransform();
|
||||
batch.setPipeline(_pipeline);
|
||||
|
||||
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, viewport));
|
||||
batch.setResourceTexture(BLUR0_SLOT, blur0FB->getRenderBuffer(0));
|
||||
batch.setResourceTexture(BLUR1_SLOT, blur1FB->getRenderBuffer(0));
|
||||
batch.setResourceTexture(BLUR2_SLOT, blur2FB->getRenderBuffer(0));
|
||||
batch._glUniform1f(INTENSITY_SLOT, _intensity / 3.0f);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
}
|
||||
|
||||
void BloomDraw::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
const auto frameBuffer = inputs.get0();
|
||||
const auto bloomFrameBuffer = inputs.get1();
|
||||
|
||||
if (frameBuffer && bloomFrameBuffer) {
|
||||
const auto framebufferSize = frameBuffer->getSize();
|
||||
|
||||
if (!_pipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
|
||||
auto ps = gpu::StandardShaderLib::getDrawTextureOpaquePS();
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(gpu::State::DepthTest(false, false));
|
||||
state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE,
|
||||
gpu::State::ZERO, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
|
||||
batch.setFramebuffer(frameBuffer);
|
||||
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setProjectionTransform(glm::mat4());
|
||||
batch.resetViewTransform();
|
||||
batch.setPipeline(_pipeline);
|
||||
|
||||
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport));
|
||||
batch.setResourceTexture(0, bloomFrameBuffer->getRenderBuffer(0));
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
DebugBloom::DebugBloom() {
|
||||
}
|
||||
|
||||
void DebugBloom::configure(const Config& config) {
|
||||
_mode = static_cast<DebugBloomConfig::Mode>(config.mode);
|
||||
assert(_mode < DebugBloomConfig::MODE_COUNT);
|
||||
}
|
||||
|
||||
void DebugBloom::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
const auto frameBuffer = inputs.get0();
|
||||
const auto combinedBlurBuffer = inputs.get4();
|
||||
const auto framebufferSize = frameBuffer->getSize();
|
||||
const auto level0FB = inputs.get1();
|
||||
const auto level1FB = inputs.get2();
|
||||
const auto level2FB = inputs.get3();
|
||||
const gpu::TexturePointer levelTextures[BLOOM_BLUR_LEVEL_COUNT] = {
|
||||
level0FB->getRenderBuffer(0),
|
||||
level1FB->getRenderBuffer(0),
|
||||
level2FB->getRenderBuffer(0)
|
||||
};
|
||||
|
||||
static auto TEXCOORD_RECT_SLOT = 1;
|
||||
|
||||
if (!_pipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawTexcoordRectTransformUnitQuadVS();
|
||||
auto ps = gpu::StandardShaderLib::getDrawTextureOpaquePS();
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("texcoordRect"), TEXCOORD_RECT_SLOT));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(gpu::State::DepthTest(false));
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
|
||||
batch.setFramebuffer(frameBuffer);
|
||||
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setProjectionTransform(glm::mat4());
|
||||
batch.resetViewTransform();
|
||||
batch.setPipeline(_pipeline);
|
||||
|
||||
Transform modelTransform;
|
||||
if (_mode == DebugBloomConfig::MODE_ALL_LEVELS) {
|
||||
batch._glUniform4f(TEXCOORD_RECT_SLOT, 0.0f, 0.0f, 1.f, 1.f);
|
||||
|
||||
modelTransform = gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport / 2);
|
||||
modelTransform.postTranslate(glm::vec3(-1.0f, 1.0f, 0.0f));
|
||||
batch.setModelTransform(modelTransform);
|
||||
batch.setResourceTexture(0, levelTextures[0]);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
|
||||
modelTransform.postTranslate(glm::vec3(2.0f, 0.0f, 0.0f));
|
||||
batch.setModelTransform(modelTransform);
|
||||
batch.setResourceTexture(0, levelTextures[1]);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
|
||||
modelTransform.postTranslate(glm::vec3(-2.0f, -2.0f, 0.0f));
|
||||
batch.setModelTransform(modelTransform);
|
||||
batch.setResourceTexture(0, levelTextures[2]);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
|
||||
modelTransform.postTranslate(glm::vec3(2.0f, 0.0f, 0.0f));
|
||||
batch.setModelTransform(modelTransform);
|
||||
batch.setResourceTexture(0, combinedBlurBuffer->getRenderBuffer(0));
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
} else {
|
||||
auto viewport = args->_viewport;
|
||||
auto blurLevel = _mode - DebugBloomConfig::MODE_LEVEL0;
|
||||
|
||||
viewport.z /= 2;
|
||||
|
||||
batch._glUniform4f(TEXCOORD_RECT_SLOT, 0.5f, 0.0f, 0.5f, 1.f);
|
||||
|
||||
modelTransform = gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, viewport);
|
||||
modelTransform.postTranslate(glm::vec3(-1.0f, 0.0f, 0.0f));
|
||||
batch.setModelTransform(modelTransform);
|
||||
batch.setResourceTexture(0, levelTextures[blurLevel]);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void BloomConfig::setIntensity(float value) {
|
||||
auto task = static_cast<render::Task::TaskConcept*>(_task);
|
||||
auto blurJobIt = task->editJob("BloomApply");
|
||||
assert(blurJobIt != task->_jobs.end());
|
||||
blurJobIt->getConfiguration()->setProperty("intensity", value);
|
||||
}
|
||||
|
||||
float BloomConfig::getIntensity() const {
|
||||
auto task = static_cast<render::Task::TaskConcept*>(_task);
|
||||
auto blurJobIt = task->getJob("BloomApply");
|
||||
assert(blurJobIt != task->_jobs.end());
|
||||
return blurJobIt->getConfiguration()->property("intensity").toFloat();
|
||||
}
|
||||
|
||||
void BloomConfig::setSize(float value) {
|
||||
std::string blurName{ "BloomBlurN" };
|
||||
auto sigma = 0.5f+value*3.5f;
|
||||
|
||||
for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) {
|
||||
blurName.back() = '0' + i;
|
||||
auto task = static_cast<render::Task::TaskConcept*>(_task);
|
||||
auto blurJobIt = task->editJob(blurName);
|
||||
assert(blurJobIt != task->_jobs.end());
|
||||
auto& gaussianBlur = blurJobIt->edit<render::BlurGaussian>();
|
||||
auto gaussianBlurParams = gaussianBlur.getParameters();
|
||||
gaussianBlurParams->setFilterGaussianTaps(5, sigma);
|
||||
// Gaussian blur increases at each level to have a slower rolloff on the edge
|
||||
// of the response
|
||||
sigma *= 1.5f;
|
||||
}
|
||||
}
|
||||
|
||||
Bloom::Bloom() {
|
||||
|
||||
}
|
||||
|
||||
void Bloom::configure(const Config& config) {
|
||||
std::string blurName{ "BloomBlurN" };
|
||||
|
||||
for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) {
|
||||
blurName.back() = '0' + i;
|
||||
auto blurConfig = config.getConfig<render::BlurGaussian>(blurName);
|
||||
blurConfig->setProperty("filterScale", 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void Bloom::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) {
|
||||
// Start by computing threshold of color buffer input at quarter resolution
|
||||
const auto bloomInputBuffer = task.addJob<BloomThreshold>("BloomThreshold", inputs, 4U);
|
||||
|
||||
// Multi-scale blur, each new blur is half resolution of the previous pass
|
||||
const auto blurFB0 = task.addJob<render::BlurGaussian>("BloomBlur0", bloomInputBuffer, true);
|
||||
const auto blurFB1 = task.addJob<render::BlurGaussian>("BloomBlur1", blurFB0, true, 2U);
|
||||
const auto blurFB2 = task.addJob<render::BlurGaussian>("BloomBlur2", blurFB1, true, 2U);
|
||||
|
||||
const auto& input = inputs.get<Inputs>();
|
||||
const auto& frameBuffer = input[1];
|
||||
|
||||
// Mix all blur levels at quarter resolution
|
||||
const auto applyInput = BloomApply::Inputs(bloomInputBuffer, blurFB0, blurFB1, blurFB2).asVarying();
|
||||
task.addJob<BloomApply>("BloomApply", applyInput);
|
||||
// And them blend result in additive manner on top of final color buffer
|
||||
const auto drawInput = BloomDraw::Inputs(frameBuffer, bloomInputBuffer).asVarying();
|
||||
task.addJob<BloomDraw>("BloomDraw", drawInput);
|
||||
|
||||
const auto debugInput = DebugBloom::Inputs(frameBuffer, blurFB0, blurFB1, blurFB2, bloomInputBuffer).asVarying();
|
||||
task.addJob<DebugBloom>("DebugBloom", debugInput);
|
||||
}
|
166
libraries/render-utils/src/BloomEffect.h
Normal file
166
libraries/render-utils/src/BloomEffect.h
Normal file
|
@ -0,0 +1,166 @@
|
|||
//
|
||||
// BloomEffect.h
|
||||
// render-utils/src/
|
||||
//
|
||||
// Created by Olivier Prat on 09/25/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_render_utils_BloomEffect_h
|
||||
#define hifi_render_utils_BloomEffect_h
|
||||
|
||||
#include <render/Engine.h>
|
||||
|
||||
#include "DeferredFrameTransform.h"
|
||||
|
||||
class BloomConfig : public render::Task::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float intensity READ getIntensity WRITE setIntensity NOTIFY dirty)
|
||||
Q_PROPERTY(float size MEMBER size WRITE setSize NOTIFY dirty)
|
||||
|
||||
public:
|
||||
|
||||
BloomConfig() : render::Task::Config(false) {}
|
||||
|
||||
float size{ 0.8f };
|
||||
|
||||
void setIntensity(float value);
|
||||
float getIntensity() const;
|
||||
void setSize(float value);
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class BloomThresholdConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float threshold MEMBER threshold NOTIFY dirty)
|
||||
|
||||
public:
|
||||
|
||||
float threshold{ 1.25f };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class BloomThreshold {
|
||||
public:
|
||||
using Inputs = render::VaryingSet2<DeferredFrameTransformPointer, gpu::FramebufferPointer>;
|
||||
using Outputs = gpu::FramebufferPointer;
|
||||
using Config = BloomThresholdConfig;
|
||||
using JobModel = render::Job::ModelIO<BloomThreshold, Inputs, Outputs, Config>;
|
||||
|
||||
BloomThreshold(unsigned int downsamplingFactor);
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs);
|
||||
|
||||
private:
|
||||
|
||||
gpu::FramebufferPointer _outputBuffer;
|
||||
gpu::PipelinePointer _pipeline;
|
||||
float _threshold;
|
||||
unsigned int _downsamplingFactor;
|
||||
};
|
||||
|
||||
|
||||
class BloomApplyConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float intensity MEMBER intensity NOTIFY dirty)
|
||||
|
||||
public:
|
||||
|
||||
float intensity{ 0.8f };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class BloomApply {
|
||||
public:
|
||||
using Inputs = render::VaryingSet4<gpu::FramebufferPointer, gpu::FramebufferPointer, gpu::FramebufferPointer, gpu::FramebufferPointer>;
|
||||
using Config = BloomApplyConfig;
|
||||
using JobModel = render::Job::ModelI<BloomApply, Inputs, Config>;
|
||||
|
||||
BloomApply();
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
|
||||
|
||||
private:
|
||||
|
||||
gpu::PipelinePointer _pipeline;
|
||||
float _intensity{ 1.0f };
|
||||
};
|
||||
|
||||
class BloomDraw {
|
||||
public:
|
||||
using Inputs = render::VaryingSet2<gpu::FramebufferPointer, gpu::FramebufferPointer>;
|
||||
using JobModel = render::Job::ModelI<BloomDraw, Inputs>;
|
||||
|
||||
BloomDraw() {}
|
||||
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
|
||||
|
||||
private:
|
||||
|
||||
gpu::PipelinePointer _pipeline;
|
||||
};
|
||||
|
||||
class DebugBloomConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int mode MEMBER mode NOTIFY dirty)
|
||||
|
||||
public:
|
||||
|
||||
enum Mode {
|
||||
MODE_LEVEL0 = 0,
|
||||
MODE_LEVEL1,
|
||||
MODE_LEVEL2,
|
||||
MODE_ALL_LEVELS,
|
||||
|
||||
MODE_COUNT
|
||||
};
|
||||
|
||||
DebugBloomConfig() : render::Job::Config(false) {}
|
||||
|
||||
int mode{ MODE_ALL_LEVELS };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class DebugBloom {
|
||||
public:
|
||||
using Inputs = render::VaryingSet5<gpu::FramebufferPointer, gpu::FramebufferPointer, gpu::FramebufferPointer, gpu::FramebufferPointer, gpu::FramebufferPointer>;
|
||||
using Config = DebugBloomConfig;
|
||||
using JobModel = render::Job::ModelI<DebugBloom, Inputs, Config>;
|
||||
|
||||
DebugBloom();
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
|
||||
|
||||
private:
|
||||
gpu::PipelinePointer _pipeline;
|
||||
DebugBloomConfig::Mode _mode;
|
||||
};
|
||||
|
||||
class Bloom {
|
||||
public:
|
||||
using Inputs = render::VaryingSet2<DeferredFrameTransformPointer, gpu::FramebufferPointer>;
|
||||
using Config = BloomConfig;
|
||||
using JobModel = render::Task::ModelI<Bloom, Inputs, Config>;
|
||||
|
||||
Bloom();
|
||||
|
||||
void configure(const Config& config);
|
||||
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs);
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_render_utils_BloomEffect_h
|
45
libraries/render-utils/src/BloomThreshold.slf
Normal file
45
libraries/render-utils/src/BloomThreshold.slf
Normal file
|
@ -0,0 +1,45 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// BloomThreshold.slf
|
||||
// Perform a soft threshold on an input texture and downsample to half size in one go.
|
||||
//
|
||||
// Created by Olivier Prat on 09/26/2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
uniform sampler2D colorMap;
|
||||
uniform float threshold;
|
||||
|
||||
in vec2 varTexCoord0;
|
||||
out vec4 outFragColor;
|
||||
|
||||
#define DOWNSAMPLING_FACTOR 4
|
||||
#define SAMPLE_COUNT (DOWNSAMPLING_FACTOR/2)
|
||||
|
||||
void main(void) {
|
||||
vec2 deltaX = dFdx(varTexCoord0) / SAMPLE_COUNT;
|
||||
vec2 deltaY = dFdy(varTexCoord0) / SAMPLE_COUNT;
|
||||
vec2 startUv = varTexCoord0;
|
||||
vec4 maskedColor = vec4(0,0,0,0);
|
||||
|
||||
for (int y=0 ; y<SAMPLE_COUNT ; y++) {
|
||||
vec2 uv = startUv;
|
||||
|
||||
for (int x=0 ; x<SAMPLE_COUNT ; x++) {
|
||||
vec4 color = texture(colorMap, uv);
|
||||
float luminance = (color.r+color.g+color.b) / 3.0;
|
||||
float mask = clamp((luminance-threshold)*0.25, 0, 1);
|
||||
|
||||
color *= mask;
|
||||
maskedColor += color;
|
||||
uv += deltaX;
|
||||
}
|
||||
|
||||
startUv += deltaY;
|
||||
}
|
||||
maskedColor /= SAMPLE_COUNT*SAMPLE_COUNT;
|
||||
outFragColor = vec4(maskedColor.rgb, 1.0);
|
||||
}
|
|
@ -73,9 +73,9 @@ void DeferredFramebuffer::allocate() {
|
|||
_deferredFramebufferDepthColor->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
|
||||
|
||||
|
||||
auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR);
|
||||
auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT);
|
||||
|
||||
_lightingTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, gpu::Texture::SINGLE_MIP, defaultSampler);
|
||||
_lightingTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, gpu::Texture::SINGLE_MIP, smoothSampler);
|
||||
_lightingFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("lighting"));
|
||||
_lightingFramebuffer->setRenderBuffer(0, _lightingTexture);
|
||||
_lightingFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
|
||||
|
|
|
@ -105,13 +105,13 @@ void DeferredLightingEffect::setupKeyLightBatch(const RenderArgs* args, gpu::Bat
|
|||
PerformanceTimer perfTimer("DLE->setupBatch()");
|
||||
model::LightPointer keySunLight;
|
||||
auto lightStage = args->_scene->getStage<LightStage>();
|
||||
if (lightStage && lightStage->_currentFrame._sunLights.size()) {
|
||||
keySunLight = lightStage->getLight(lightStage->_currentFrame._sunLights.front());
|
||||
if (lightStage) {
|
||||
keySunLight = lightStage->getCurrentKeyLight();
|
||||
}
|
||||
|
||||
model::LightPointer keyAmbiLight;
|
||||
if (lightStage && lightStage->_currentFrame._ambientLights.size()) {
|
||||
keyAmbiLight = lightStage->getLight(lightStage->_currentFrame._ambientLights.front());
|
||||
if (lightStage) {
|
||||
keyAmbiLight = lightStage->getCurrentAmbientLight();
|
||||
}
|
||||
|
||||
if (keySunLight) {
|
||||
|
@ -620,7 +620,7 @@ void RenderDeferredLocals::run(const render::RenderContextPointer& renderContext
|
|||
auto& lightIndices = lightClusters->_visibleLightIndices;
|
||||
if (!lightIndices.empty() && lightIndices[0] > 0) {
|
||||
// Bind the global list of lights and the visible lights this frame
|
||||
batch.setUniformBuffer(deferredLightingEffect->_localLightLocations->lightBufferUnit, lightClusters->_lightStage->_lightArrayBuffer);
|
||||
batch.setUniformBuffer(deferredLightingEffect->_localLightLocations->lightBufferUnit, lightClusters->_lightStage->getLightArrayBuffer());
|
||||
|
||||
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT, lightClusters->_frustumGridBuffer);
|
||||
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, lightClusters->_clusterGridBuffer);
|
||||
|
|
|
@ -175,9 +175,9 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu
|
|||
batch.setUniformBuffer(HazeEffect_TransformBufferSlot, transformBuffer->getFrameTransformBuffer());
|
||||
|
||||
auto lightStage = args->_scene->getStage<LightStage>();
|
||||
if (lightStage && lightStage->_currentFrame._sunLights.size() > 0) {
|
||||
model::LightPointer keyLight;
|
||||
keyLight = lightStage->getLight(lightStage->_currentFrame._sunLights.front());
|
||||
if (lightStage) {
|
||||
model::LightPointer keyLight;
|
||||
keyLight = lightStage->getCurrentKeyLight();
|
||||
if (keyLight != nullptr) {
|
||||
batch.setUniformBuffer(HazeEffect_LightingMapSlot, keyLight->getLightSchemaBuffer());
|
||||
}
|
||||
|
|
|
@ -1931,9 +1931,10 @@ void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const
|
|||
vec4 p1;
|
||||
vec4 p2;
|
||||
vec4 color;
|
||||
float width;
|
||||
};
|
||||
|
||||
LineData lineData { vec4(p1, 1.0f), vec4(p2, 1.0f), color };
|
||||
LineData lineData { vec4(p1, 1.0f), vec4(p2, 1.0f), color, glowWidth };
|
||||
details.uniformBuffer->resize(sizeof(LineData));
|
||||
details.uniformBuffer->setSubData(0, lineData);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <gpu/Context.h>
|
||||
|
||||
std::string HazeStage::_stageName { "HAZE_STAGE"};
|
||||
const HazeStage::Index HazeStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
|
||||
|
||||
FetchHazeStage::FetchHazeStage() {
|
||||
_haze = std::make_shared<model::Haze>();
|
||||
|
|
|
@ -28,7 +28,7 @@ public:
|
|||
static const std::string& getName() { return _stageName; }
|
||||
|
||||
using Index = render::indexed_container::Index;
|
||||
static const Index INVALID_INDEX { render::indexed_container::INVALID_INDEX };
|
||||
static const Index INVALID_INDEX;
|
||||
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
|
||||
|
||||
using HazePointer = model::HazePointer;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Outline.slf
|
||||
// Add outline effect based on two zbuffers : one containing the total scene z and another
|
||||
// Highlight.slf
|
||||
// Add highlight effect based on two zbuffers : one containing the total scene z and another
|
||||
// with the z of only the objects to be outlined.
|
||||
// This is the version without the fill effect inside the silhouette.
|
||||
//
|
||||
|
@ -9,5 +9,5 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
<@include Outline.slh@>
|
||||
<@include Highlight.slh@>
|
||||
<$main(0)$>
|
|
@ -1,7 +1,7 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
<!
|
||||
// Outline.slh
|
||||
// Highlight.slh
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Olivier Prat on 9/7/17.
|
||||
|
@ -13,14 +13,14 @@
|
|||
<@include DeferredTransform.slh@>
|
||||
<$declareDeferredFrameTransform()$>
|
||||
|
||||
<@include Outline_shared.slh@>
|
||||
<@include Highlight_shared.slh@>
|
||||
|
||||
uniform outlineParamsBuffer {
|
||||
OutlineParameters params;
|
||||
uniform highlightParamsBuffer {
|
||||
HighlightParameters params;
|
||||
};
|
||||
|
||||
uniform sampler2D sceneDepthMap;
|
||||
uniform sampler2D outlinedDepthMap;
|
||||
uniform sampler2D highlightedDepthMap;
|
||||
|
||||
in vec2 varTexCoord0;
|
||||
out vec4 outFragColor;
|
||||
|
@ -35,30 +35,26 @@ void main(void) {
|
|||
// We offset by half a texel to be centered on the depth sample. If we don't do this
|
||||
// the blur will have a different width between the left / right sides and top / bottom
|
||||
// sides of the silhouette
|
||||
vec2 halfTexel = getInvWidthHeight() / 2;
|
||||
vec2 texCoord0 = varTexCoord0+halfTexel;
|
||||
float outlinedDepth = texture(outlinedDepthMap, texCoord0).x;
|
||||
float highlightedDepth = texture(highlightedDepthMap, varTexCoord0).x;
|
||||
float intensity = 0.0;
|
||||
|
||||
if (outlinedDepth < FAR_Z) {
|
||||
// We're not on the far plane so we are on the outlined object, thus no outline to do!
|
||||
if (highlightedDepth < FAR_Z) {
|
||||
// We're not on the far plane so we are on the highlighted object, thus no outline to do!
|
||||
<@if IS_FILLED@>
|
||||
// But we need to fill the interior
|
||||
float sceneDepth = texture(sceneDepthMap, texCoord0).x;
|
||||
float sceneDepth = texture(sceneDepthMap, varTexCoord0).x;
|
||||
// Transform to linear depth for better precision
|
||||
outlinedDepth = -evalZeyeFromZdb(outlinedDepth);
|
||||
highlightedDepth = -evalZeyeFromZdb(highlightedDepth);
|
||||
sceneDepth = -evalZeyeFromZdb(sceneDepth);
|
||||
|
||||
// Are we occluded?
|
||||
if (sceneDepth < (outlinedDepth-LINEAR_DEPTH_BIAS)) {
|
||||
intensity = params._fillOpacityOccluded;
|
||||
} else {
|
||||
intensity = params._fillOpacityUnoccluded;
|
||||
}
|
||||
intensity = sceneDepth < (highlightedDepth-LINEAR_DEPTH_BIAS) ? params._occludedFillOpacity : params._unoccludedFillOpacity;
|
||||
<@else@>
|
||||
discard;
|
||||
<@endif@>
|
||||
} else {
|
||||
vec2 halfTexel = getInvWidthHeight() / 2;
|
||||
vec2 texCoord0 = varTexCoord0+halfTexel;
|
||||
float weight = 0.0;
|
||||
vec2 deltaUv = params._size / params._blurKernelSize;
|
||||
vec2 lineStartUv = texCoord0 - params._size / 2.0;
|
||||
|
@ -74,9 +70,9 @@ void main(void) {
|
|||
for (x=0 ; x<params._blurKernelSize ; x++) {
|
||||
if (uv.x>=0.0 && uv.x<=1.0)
|
||||
{
|
||||
outlinedDepth = texture(outlinedDepthMap, uv).x;
|
||||
intensity += (outlinedDepth < FAR_Z) ? 1.0 : 0.0;
|
||||
weight += 1.f;
|
||||
highlightedDepth = texture(highlightedDepthMap, uv).x;
|
||||
intensity += (highlightedDepth < FAR_Z) ? 1.0 : 0.0;
|
||||
weight += 1.0;
|
||||
}
|
||||
uv.x += deltaUv.x;
|
||||
}
|
562
libraries/render-utils/src/HighlightEffect.cpp
Normal file
562
libraries/render-utils/src/HighlightEffect.cpp
Normal file
|
@ -0,0 +1,562 @@
|
|||
//
|
||||
// HighlightEffect.cpp
|
||||
// render-utils/src/
|
||||
//
|
||||
// Created by Olivier Prat on 08/08/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "HighlightEffect.h"
|
||||
|
||||
#include "GeometryCache.h"
|
||||
|
||||
#include "CubeProjectedPolygon.h"
|
||||
|
||||
#include <render/FilterTask.h>
|
||||
#include <render/SortTask.h>
|
||||
|
||||
#include "gpu/Context.h"
|
||||
#include "gpu/StandardShaderLib.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "surfaceGeometry_copyDepth_frag.h"
|
||||
#include "debug_deferred_buffer_vert.h"
|
||||
#include "debug_deferred_buffer_frag.h"
|
||||
#include "Highlight_frag.h"
|
||||
#include "Highlight_filled_frag.h"
|
||||
#include "Highlight_aabox_vert.h"
|
||||
#include "nop_frag.h"
|
||||
|
||||
using namespace render;
|
||||
|
||||
#define OUTLINE_STENCIL_MASK 1
|
||||
|
||||
HighlightRessources::HighlightRessources() {
|
||||
}
|
||||
|
||||
void HighlightRessources::update(const gpu::FramebufferPointer& primaryFrameBuffer) {
|
||||
auto newFrameSize = glm::ivec2(primaryFrameBuffer->getSize());
|
||||
|
||||
// If the buffer size changed, we need to delete our FBOs and recreate them at the
|
||||
// new correct dimensions.
|
||||
if (_frameSize != newFrameSize) {
|
||||
_frameSize = newFrameSize;
|
||||
allocateDepthBuffer(primaryFrameBuffer);
|
||||
allocateColorBuffer(primaryFrameBuffer);
|
||||
} else {
|
||||
if (!_depthFrameBuffer) {
|
||||
allocateDepthBuffer(primaryFrameBuffer);
|
||||
}
|
||||
if (!_colorFrameBuffer) {
|
||||
allocateColorBuffer(primaryFrameBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HighlightRessources::allocateColorBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) {
|
||||
_colorFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("primaryWithStencil"));
|
||||
_colorFrameBuffer->setRenderBuffer(0, primaryFrameBuffer->getRenderBuffer(0));
|
||||
_colorFrameBuffer->setStencilBuffer(_depthStencilTexture, _depthStencilTexture->getTexelFormat());
|
||||
}
|
||||
|
||||
void HighlightRessources::allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) {
|
||||
auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL);
|
||||
_depthStencilTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, _frameSize.x, _frameSize.y));
|
||||
_depthFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("highlightDepth"));
|
||||
_depthFrameBuffer->setDepthStencilBuffer(_depthStencilTexture, depthFormat);
|
||||
}
|
||||
|
||||
gpu::FramebufferPointer HighlightRessources::getDepthFramebuffer() {
|
||||
assert(_depthFrameBuffer);
|
||||
return _depthFrameBuffer;
|
||||
}
|
||||
|
||||
gpu::TexturePointer HighlightRessources::getDepthTexture() {
|
||||
return _depthStencilTexture;
|
||||
}
|
||||
|
||||
gpu::FramebufferPointer HighlightRessources::getColorFramebuffer() {
|
||||
assert(_colorFrameBuffer);
|
||||
return _colorFrameBuffer;
|
||||
}
|
||||
|
||||
HighlightSharedParameters::HighlightSharedParameters() {
|
||||
_highlightIds.fill(render::HighlightStage::INVALID_INDEX);
|
||||
}
|
||||
|
||||
float HighlightSharedParameters::getBlurPixelWidth(const render::HighlightStyle& style, int frameBufferHeight) {
|
||||
return ceilf(style.outlineWidth * frameBufferHeight / 400.0f);
|
||||
}
|
||||
|
||||
PrepareDrawHighlight::PrepareDrawHighlight() {
|
||||
_ressources = std::make_shared<HighlightRessources>();
|
||||
}
|
||||
|
||||
void PrepareDrawHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
|
||||
auto destinationFrameBuffer = inputs;
|
||||
|
||||
_ressources->update(destinationFrameBuffer);
|
||||
outputs = _ressources;
|
||||
}
|
||||
|
||||
gpu::PipelinePointer DrawHighlightMask::_stencilMaskPipeline;
|
||||
gpu::PipelinePointer DrawHighlightMask::_stencilMaskFillPipeline;
|
||||
|
||||
DrawHighlightMask::DrawHighlightMask(unsigned int highlightIndex,
|
||||
render::ShapePlumberPointer shapePlumber, HighlightSharedParametersPointer parameters) :
|
||||
_highlightPassIndex{ highlightIndex },
|
||||
_shapePlumber { shapePlumber },
|
||||
_sharedParameters{ parameters } {
|
||||
}
|
||||
|
||||
void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
auto& inShapes = inputs.get0();
|
||||
|
||||
if (!_stencilMaskPipeline || !_stencilMaskFillPipeline) {
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(true, false, gpu::LESS_EQUAL);
|
||||
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_ZERO, gpu::State::STENCIL_OP_REPLACE));
|
||||
state->setColorWriteMask(false, false, false, false);
|
||||
state->setCullMode(gpu::State::CULL_FRONT);
|
||||
|
||||
gpu::StatePointer fillState = gpu::StatePointer(new gpu::State());
|
||||
fillState->setDepthTest(false, false, gpu::LESS_EQUAL);
|
||||
fillState->setStencilTest(true, 0xFF, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_REPLACE));
|
||||
fillState->setColorWriteMask(false, false, false, false);
|
||||
fillState->setCullMode(gpu::State::CULL_FRONT);
|
||||
|
||||
auto vs = gpu::Shader::createVertex(std::string(Highlight_aabox_vert));
|
||||
auto ps = gpu::Shader::createPixel(std::string(nop_frag));
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
_stencilMaskPipeline = gpu::Pipeline::create(program, state);
|
||||
_stencilMaskFillPipeline = gpu::Pipeline::create(program, fillState);
|
||||
}
|
||||
|
||||
if (!_boundsBuffer) {
|
||||
_boundsBuffer = std::make_shared<gpu::Buffer>(sizeof(render::ItemBound));
|
||||
}
|
||||
|
||||
auto highlightStage = renderContext->_scene->getStage<render::HighlightStage>(render::HighlightStage::getName());
|
||||
auto highlightId = _sharedParameters->_highlightIds[_highlightPassIndex];
|
||||
|
||||
if (!inShapes.empty() && !render::HighlightStage::isIndexInvalid(highlightId)) {
|
||||
auto ressources = inputs.get1();
|
||||
auto& highlight = highlightStage->getHighlight(highlightId);
|
||||
|
||||
RenderArgs* args = renderContext->args;
|
||||
ShapeKey::Builder defaultKeyBuilder;
|
||||
|
||||
// Render full screen
|
||||
outputs = args->_viewport;
|
||||
|
||||
// Clear the framebuffer without stereo
|
||||
// Needs to be distinct from the other batch because using the clear call
|
||||
// while stereo is enabled triggers a warning
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
batch.setFramebuffer(ressources->getDepthFramebuffer());
|
||||
batch.clearDepthStencilFramebuffer(1.0f, 0);
|
||||
});
|
||||
|
||||
glm::mat4 projMat;
|
||||
Transform viewMat;
|
||||
args->getViewFrustum().evalProjectionMatrix(projMat);
|
||||
args->getViewFrustum().evalViewTransform(viewMat);
|
||||
|
||||
render::ItemBounds itemBounds;
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
args->_batch = &batch;
|
||||
|
||||
auto maskPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder);
|
||||
auto maskSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned());
|
||||
|
||||
// Setup camera, projection and viewport for all items
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setViewTransform(viewMat);
|
||||
|
||||
std::vector<ShapeKey> skinnedShapeKeys{};
|
||||
|
||||
// Iterate through all inShapes and render the unskinned
|
||||
args->_shapePipeline = maskPipeline;
|
||||
batch.setPipeline(maskPipeline->pipeline);
|
||||
for (const auto& items : inShapes) {
|
||||
itemBounds.insert(itemBounds.end(), items.second.begin(), items.second.end());
|
||||
if (items.first.isSkinned()) {
|
||||
skinnedShapeKeys.push_back(items.first);
|
||||
} else {
|
||||
renderItems(renderContext, items.second);
|
||||
}
|
||||
}
|
||||
|
||||
// Reiterate to render the skinned
|
||||
args->_shapePipeline = maskSkinnedPipeline;
|
||||
batch.setPipeline(maskSkinnedPipeline->pipeline);
|
||||
for (const auto& key : skinnedShapeKeys) {
|
||||
renderItems(renderContext, inShapes.at(key));
|
||||
}
|
||||
|
||||
args->_shapePipeline = nullptr;
|
||||
args->_batch = nullptr;
|
||||
});
|
||||
|
||||
_boundsBuffer->setData(itemBounds.size() * sizeof(render::ItemBound), (const gpu::Byte*) itemBounds.data());
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
// Setup camera, projection and viewport for all items
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setViewTransform(viewMat);
|
||||
|
||||
// Draw stencil mask with object bounding boxes
|
||||
const auto highlightWidthLoc = _stencilMaskPipeline->getProgram()->getUniforms().findLocation("outlineWidth");
|
||||
const auto securityMargin = 2.0f;
|
||||
const float blurPixelWidth = 2.0f * securityMargin * HighlightSharedParameters::getBlurPixelWidth(highlight._style, args->_viewport.w);
|
||||
const auto framebufferSize = ressources->getSourceFrameSize();
|
||||
|
||||
auto stencilPipeline = highlight._style.isFilled() ? _stencilMaskFillPipeline : _stencilMaskPipeline;
|
||||
batch.setPipeline(stencilPipeline);
|
||||
batch.setResourceBuffer(0, _boundsBuffer);
|
||||
batch._glUniform2f(highlightWidthLoc, blurPixelWidth / framebufferSize.x, blurPixelWidth / framebufferSize.y);
|
||||
static const int NUM_VERTICES_PER_CUBE = 36;
|
||||
batch.draw(gpu::TRIANGLES, NUM_VERTICES_PER_CUBE * (gpu::uint32) itemBounds.size(), 0);
|
||||
});
|
||||
} else {
|
||||
// Highlight rect should be null as there are no highlighted shapes
|
||||
outputs = glm::ivec4(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
gpu::PipelinePointer DrawHighlight::_pipeline;
|
||||
gpu::PipelinePointer DrawHighlight::_pipelineFilled;
|
||||
|
||||
DrawHighlight::DrawHighlight(unsigned int highlightIndex, HighlightSharedParametersPointer parameters) :
|
||||
_highlightPassIndex{ highlightIndex },
|
||||
_sharedParameters{ parameters } {
|
||||
}
|
||||
|
||||
void DrawHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
|
||||
auto highlightFrameBuffer = inputs.get1();
|
||||
auto highlightRect = inputs.get3();
|
||||
|
||||
if (highlightFrameBuffer && highlightRect.z>0 && highlightRect.w>0) {
|
||||
auto sceneDepthBuffer = inputs.get2();
|
||||
const auto frameTransform = inputs.get0();
|
||||
auto highlightedDepthTexture = highlightFrameBuffer->getDepthTexture();
|
||||
auto destinationFrameBuffer = highlightFrameBuffer->getColorFramebuffer();
|
||||
auto framebufferSize = glm::ivec2(highlightedDepthTexture->getDimensions());
|
||||
|
||||
if (sceneDepthBuffer) {
|
||||
auto args = renderContext->args;
|
||||
|
||||
auto highlightStage = renderContext->_scene->getStage<render::HighlightStage>(render::HighlightStage::getName());
|
||||
auto highlightId = _sharedParameters->_highlightIds[_highlightPassIndex];
|
||||
if (!render::HighlightStage::isIndexInvalid(highlightId)) {
|
||||
auto& highlight = highlightStage->getHighlight(highlightId);
|
||||
auto pipeline = getPipeline(highlight._style);
|
||||
{
|
||||
auto& shaderParameters = _configuration.edit();
|
||||
|
||||
shaderParameters._color = highlight._style.color;
|
||||
shaderParameters._intensity = highlight._style.outlineIntensity * (highlight._style.isOutlineSmooth ? 2.0f : 1.0f);
|
||||
shaderParameters._unoccludedFillOpacity = highlight._style.unoccludedFillOpacity;
|
||||
shaderParameters._occludedFillOpacity = highlight._style.occludedFillOpacity;
|
||||
shaderParameters._threshold = highlight._style.isOutlineSmooth ? 1.0f : 1e-3f;
|
||||
shaderParameters._blurKernelSize = std::min(7, std::max(2, (int)floorf(highlight._style.outlineWidth * 3 + 0.5f)));
|
||||
// Size is in normalized screen height. We decide that for highlight width = 1, this is equal to 1/400.
|
||||
auto size = highlight._style.outlineWidth / 400.0f;
|
||||
shaderParameters._size.x = (size * framebufferSize.y) / framebufferSize.x;
|
||||
shaderParameters._size.y = size;
|
||||
}
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
batch.setFramebuffer(destinationFrameBuffer);
|
||||
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setProjectionTransform(glm::mat4());
|
||||
batch.resetViewTransform();
|
||||
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport));
|
||||
batch.setPipeline(pipeline);
|
||||
|
||||
batch.setUniformBuffer(HIGHLIGHT_PARAMS_SLOT, _configuration);
|
||||
batch.setUniformBuffer(FRAME_TRANSFORM_SLOT, frameTransform->getFrameTransformBuffer());
|
||||
batch.setResourceTexture(SCENE_DEPTH_MAP_SLOT, sceneDepthBuffer->getPrimaryDepthTexture());
|
||||
batch.setResourceTexture(HIGHLIGHTED_DEPTH_MAP_SLOT, highlightedDepthTexture);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const gpu::PipelinePointer& DrawHighlight::getPipeline(const render::HighlightStyle& style) {
|
||||
if (!_pipeline) {
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(gpu::State::DepthTest(false, false));
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||
state->setStencilTest(true, 0, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::EQUAL));
|
||||
|
||||
auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS();
|
||||
auto ps = gpu::Shader::createPixel(std::string(Highlight_frag));
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding("highlightParamsBuffer", HIGHLIGHT_PARAMS_SLOT));
|
||||
slotBindings.insert(gpu::Shader::Binding("deferredFrameTransformBuffer", FRAME_TRANSFORM_SLOT));
|
||||
slotBindings.insert(gpu::Shader::Binding("sceneDepthMap", SCENE_DEPTH_MAP_SLOT));
|
||||
slotBindings.insert(gpu::Shader::Binding("highlightedDepthMap", HIGHLIGHTED_DEPTH_MAP_SLOT));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
|
||||
ps = gpu::Shader::createPixel(std::string(Highlight_filled_frag));
|
||||
program = gpu::Shader::createProgram(vs, ps);
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
_pipelineFilled = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
return style.isFilled() ? _pipelineFilled : _pipeline;
|
||||
}
|
||||
|
||||
DebugHighlight::DebugHighlight() {
|
||||
_geometryDepthId = DependencyManager::get<GeometryCache>()->allocateID();
|
||||
}
|
||||
|
||||
DebugHighlight::~DebugHighlight() {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
if (geometryCache) {
|
||||
geometryCache->releaseID(_geometryDepthId);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugHighlight::configure(const Config& config) {
|
||||
_isDisplayEnabled = config.viewMask;
|
||||
}
|
||||
|
||||
void DebugHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& input) {
|
||||
const auto highlightRessources = input.get0();
|
||||
const auto highlightRect = input.get1();
|
||||
|
||||
if (_isDisplayEnabled && highlightRessources && highlightRect.z>0 && highlightRect.w>0) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setFramebuffer(highlightRessources->getColorFramebuffer());
|
||||
|
||||
const auto geometryBuffer = DependencyManager::get<GeometryCache>();
|
||||
|
||||
glm::mat4 projMat;
|
||||
Transform viewMat;
|
||||
args->getViewFrustum().evalProjectionMatrix(projMat);
|
||||
args->getViewFrustum().evalViewTransform(viewMat);
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setViewTransform(viewMat, true);
|
||||
batch.setModelTransform(Transform());
|
||||
|
||||
const glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
batch.setPipeline(getDepthPipeline());
|
||||
batch.setResourceTexture(0, highlightRessources->getDepthTexture());
|
||||
const glm::vec2 bottomLeft(-1.0f, -1.0f);
|
||||
const glm::vec2 topRight(1.0f, 1.0f);
|
||||
geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryDepthId);
|
||||
|
||||
batch.setResourceTexture(0, nullptr);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void DebugHighlight::initializePipelines() {
|
||||
static const std::string VERTEX_SHADER{ debug_deferred_buffer_vert };
|
||||
static const std::string FRAGMENT_SHADER{ debug_deferred_buffer_frag };
|
||||
static const std::string SOURCE_PLACEHOLDER{ "//SOURCE_PLACEHOLDER" };
|
||||
static const auto SOURCE_PLACEHOLDER_INDEX = FRAGMENT_SHADER.find(SOURCE_PLACEHOLDER);
|
||||
Q_ASSERT_X(SOURCE_PLACEHOLDER_INDEX != std::string::npos, Q_FUNC_INFO,
|
||||
"Could not find source placeholder");
|
||||
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setDepthTest(gpu::State::DepthTest(false, false));
|
||||
state->setStencilTest(true, 0, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::EQUAL));
|
||||
|
||||
const auto vs = gpu::Shader::createVertex(VERTEX_SHADER);
|
||||
|
||||
// Depth shader
|
||||
{
|
||||
static const std::string DEPTH_SHADER{
|
||||
"vec4 getFragmentColor() {"
|
||||
" float Zdb = texelFetch(depthMap, ivec2(gl_FragCoord.xy), 0).x;"
|
||||
" Zdb = 1.0-(1.0-Zdb)*100;"
|
||||
" return vec4(Zdb, Zdb, Zdb, 1.0); "
|
||||
"}"
|
||||
};
|
||||
|
||||
auto fragmentShader = FRAGMENT_SHADER;
|
||||
fragmentShader.replace(SOURCE_PLACEHOLDER_INDEX, SOURCE_PLACEHOLDER.size(), DEPTH_SHADER);
|
||||
|
||||
const auto ps = gpu::Shader::createPixel(fragmentShader);
|
||||
const auto program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding("depthMap", 0));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
_depthPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
}
|
||||
|
||||
const gpu::PipelinePointer& DebugHighlight::getDepthPipeline() {
|
||||
if (!_depthPipeline) {
|
||||
initializePipelines();
|
||||
}
|
||||
|
||||
return _depthPipeline;
|
||||
}
|
||||
|
||||
void SelectionToHighlight::run(const render::RenderContextPointer& renderContext, Outputs& outputs) {
|
||||
auto scene = renderContext->_scene;
|
||||
auto highlightStage = scene->getStage<render::HighlightStage>(render::HighlightStage::getName());
|
||||
|
||||
outputs.clear();
|
||||
_sharedParameters->_highlightIds.fill(render::HighlightStage::INVALID_INDEX);
|
||||
|
||||
for (auto i = 0; i < HighlightSharedParameters::MAX_PASS_COUNT; i++) {
|
||||
std::ostringstream stream;
|
||||
if (i > 0) {
|
||||
stream << "highlightList" << i;
|
||||
} else {
|
||||
stream << "contextOverlayHighlightList";
|
||||
}
|
||||
auto selectionName = stream.str();
|
||||
if (!scene->isSelectionEmpty(selectionName)) {
|
||||
auto highlightId = highlightStage->getHighlightIdBySelection(selectionName);
|
||||
if (!render::HighlightStage::isIndexInvalid(highlightId)) {
|
||||
_sharedParameters->_highlightIds[outputs.size()] = highlightId;
|
||||
outputs.emplace_back(selectionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExtractSelectionName::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
|
||||
if (_highlightPassIndex < inputs.size()) {
|
||||
outputs = inputs[_highlightPassIndex];
|
||||
} else {
|
||||
outputs.clear();
|
||||
}
|
||||
}
|
||||
|
||||
DrawHighlightTask::DrawHighlightTask() {
|
||||
|
||||
}
|
||||
|
||||
void DrawHighlightTask::configure(const Config& config) {
|
||||
|
||||
}
|
||||
|
||||
void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) {
|
||||
const auto items = inputs.getN<Inputs>(0).get<RenderFetchCullSortTask::BucketList>();
|
||||
const auto sceneFrameBuffer = inputs.getN<Inputs>(1);
|
||||
const auto primaryFramebuffer = inputs.getN<Inputs>(2);
|
||||
const auto deferredFrameTransform = inputs.getN<Inputs>(3);
|
||||
|
||||
// Prepare the ShapePipeline
|
||||
auto shapePlumber = std::make_shared<ShapePlumber>();
|
||||
{
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
state->setColorWriteMask(false, false, false, false);
|
||||
|
||||
initMaskPipelines(*shapePlumber, state);
|
||||
}
|
||||
auto sharedParameters = std::make_shared<HighlightSharedParameters>();
|
||||
|
||||
const auto highlightSelectionNames = task.addJob<SelectionToHighlight>("SelectionToHighlight", sharedParameters);
|
||||
|
||||
// Prepare for highlight group rendering.
|
||||
const auto highlightRessources = task.addJob<PrepareDrawHighlight>("PrepareHighlight", primaryFramebuffer);
|
||||
render::Varying highlight0Rect;
|
||||
|
||||
for (auto i = 0; i < HighlightSharedParameters::MAX_PASS_COUNT; i++) {
|
||||
const auto selectionName = task.addJob<ExtractSelectionName>("ExtractSelectionName", highlightSelectionNames, i);
|
||||
const auto groupItems = addSelectItemJobs(task, selectionName, items);
|
||||
const auto highlightedItemIDs = task.addJob<render::MetaToSubItems>("HighlightMetaToSubItemIDs", groupItems);
|
||||
const auto highlightedItems = task.addJob<render::IDsToBounds>("HighlightMetaToSubItems", highlightedItemIDs);
|
||||
|
||||
// Sort
|
||||
const auto sortedPipelines = task.addJob<render::PipelineSortShapes>("HighlightPipelineSort", highlightedItems);
|
||||
const auto sortedBounds = task.addJob<render::DepthSortShapes>("HighlightDepthSort", sortedPipelines);
|
||||
|
||||
// Draw depth of highlighted objects in separate buffer
|
||||
std::string name;
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "HighlightMask" << i;
|
||||
name = stream.str();
|
||||
}
|
||||
const auto drawMaskInputs = DrawHighlightMask::Inputs(sortedBounds, highlightRessources).asVarying();
|
||||
const auto highlightedRect = task.addJob<DrawHighlightMask>(name, drawMaskInputs, i, shapePlumber, sharedParameters);
|
||||
if (i == 0) {
|
||||
highlight0Rect = highlightedRect;
|
||||
}
|
||||
|
||||
// Draw highlight
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "HighlightEffect" << i;
|
||||
name = stream.str();
|
||||
}
|
||||
const auto drawHighlightInputs = DrawHighlight::Inputs(deferredFrameTransform, highlightRessources, sceneFrameBuffer, highlightedRect).asVarying();
|
||||
task.addJob<DrawHighlight>(name, drawHighlightInputs, i, sharedParameters);
|
||||
}
|
||||
|
||||
// Debug highlight
|
||||
const auto debugInputs = DebugHighlight::Inputs(highlightRessources, const_cast<const render::Varying&>(highlight0Rect)).asVarying();
|
||||
task.addJob<DebugHighlight>("HighlightDebug", debugInputs);
|
||||
}
|
||||
|
||||
const render::Varying DrawHighlightTask::addSelectItemJobs(JobModel& task, const render::Varying& selectionName,
|
||||
const RenderFetchCullSortTask::BucketList& items) {
|
||||
const auto& opaques = items[RenderFetchCullSortTask::OPAQUE_SHAPE];
|
||||
const auto& transparents = items[RenderFetchCullSortTask::TRANSPARENT_SHAPE];
|
||||
const auto& metas = items[RenderFetchCullSortTask::META];
|
||||
|
||||
const auto selectMetaInput = SelectItems::Inputs(metas, Varying(), selectionName).asVarying();
|
||||
const auto selectedMetas = task.addJob<SelectItems>("MetaSelection", selectMetaInput);
|
||||
const auto selectMetaAndOpaqueInput = SelectItems::Inputs(opaques, selectedMetas, selectionName).asVarying();
|
||||
const auto selectedMetasAndOpaques = task.addJob<SelectItems>("OpaqueSelection", selectMetaAndOpaqueInput);
|
||||
const auto selectItemInput = SelectItems::Inputs(transparents, selectedMetasAndOpaques, selectionName).asVarying();
|
||||
return task.addJob<SelectItems>("TransparentSelection", selectItemInput);
|
||||
}
|
||||
|
||||
#include "model_shadow_vert.h"
|
||||
#include "skin_model_shadow_vert.h"
|
||||
|
||||
#include "model_shadow_frag.h"
|
||||
|
||||
void DrawHighlightTask::initMaskPipelines(render::ShapePlumber& shapePlumber, gpu::StatePointer state) {
|
||||
auto modelVertex = gpu::Shader::createVertex(std::string(model_shadow_vert));
|
||||
auto modelPixel = gpu::Shader::createPixel(std::string(model_shadow_frag));
|
||||
gpu::ShaderPointer modelProgram = gpu::Shader::createProgram(modelVertex, modelPixel);
|
||||
shapePlumber.addPipeline(
|
||||
ShapeKey::Filter::Builder().withoutSkinned(),
|
||||
modelProgram, state);
|
||||
|
||||
auto skinVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_vert));
|
||||
gpu::ShaderPointer skinProgram = gpu::Shader::createProgram(skinVertex, modelPixel);
|
||||
shapePlumber.addPipeline(
|
||||
ShapeKey::Filter::Builder().withSkinned(),
|
||||
skinProgram, state);
|
||||
}
|
226
libraries/render-utils/src/HighlightEffect.h
Normal file
226
libraries/render-utils/src/HighlightEffect.h
Normal file
|
@ -0,0 +1,226 @@
|
|||
//
|
||||
// HighlightEffect.h
|
||||
// render-utils/src/
|
||||
//
|
||||
// Created by Olivier Prat on 08/08/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_render_utils_HighlightEffect_h
|
||||
#define hifi_render_utils_HighlightEffect_h
|
||||
|
||||
#include <render/Engine.h>
|
||||
#include <render/HighlightStage.h>
|
||||
#include <render/RenderFetchCullSortTask.h>
|
||||
|
||||
#include "DeferredFramebuffer.h"
|
||||
#include "DeferredFrameTransform.h"
|
||||
|
||||
class HighlightRessources {
|
||||
public:
|
||||
HighlightRessources();
|
||||
|
||||
gpu::FramebufferPointer getDepthFramebuffer();
|
||||
gpu::TexturePointer getDepthTexture();
|
||||
|
||||
gpu::FramebufferPointer getColorFramebuffer();
|
||||
|
||||
// Update the source framebuffer size which will drive the allocation of all the other resources.
|
||||
void update(const gpu::FramebufferPointer& primaryFrameBuffer);
|
||||
const glm::ivec2& getSourceFrameSize() const { return _frameSize; }
|
||||
|
||||
protected:
|
||||
|
||||
gpu::FramebufferPointer _depthFrameBuffer;
|
||||
gpu::FramebufferPointer _colorFrameBuffer;
|
||||
gpu::TexturePointer _depthStencilTexture;
|
||||
|
||||
glm::ivec2 _frameSize;
|
||||
|
||||
void allocateColorBuffer(const gpu::FramebufferPointer& primaryFrameBuffer);
|
||||
void allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer);
|
||||
};
|
||||
|
||||
using HighlightRessourcesPointer = std::shared_ptr<HighlightRessources>;
|
||||
|
||||
class HighlightSharedParameters {
|
||||
public:
|
||||
|
||||
enum {
|
||||
MAX_PASS_COUNT = 8
|
||||
};
|
||||
|
||||
HighlightSharedParameters();
|
||||
|
||||
std::array<render::HighlightStage::Index, MAX_PASS_COUNT> _highlightIds;
|
||||
|
||||
static float getBlurPixelWidth(const render::HighlightStyle& style, int frameBufferHeight);
|
||||
};
|
||||
|
||||
using HighlightSharedParametersPointer = std::shared_ptr<HighlightSharedParameters>;
|
||||
|
||||
class PrepareDrawHighlight {
|
||||
public:
|
||||
using Inputs = gpu::FramebufferPointer;
|
||||
using Outputs = HighlightRessourcesPointer;
|
||||
using JobModel = render::Job::ModelIO<PrepareDrawHighlight, Inputs, Outputs>;
|
||||
|
||||
PrepareDrawHighlight();
|
||||
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs);
|
||||
|
||||
private:
|
||||
|
||||
HighlightRessourcesPointer _ressources;
|
||||
|
||||
};
|
||||
|
||||
class SelectionToHighlight {
|
||||
public:
|
||||
|
||||
using Outputs = std::vector<std::string>;
|
||||
using JobModel = render::Job::ModelO<SelectionToHighlight, Outputs>;
|
||||
|
||||
SelectionToHighlight(HighlightSharedParametersPointer parameters) : _sharedParameters{ parameters } {}
|
||||
|
||||
void run(const render::RenderContextPointer& renderContext, Outputs& outputs);
|
||||
|
||||
private:
|
||||
|
||||
HighlightSharedParametersPointer _sharedParameters;
|
||||
};
|
||||
|
||||
class ExtractSelectionName {
|
||||
public:
|
||||
|
||||
using Inputs = SelectionToHighlight::Outputs;
|
||||
using Outputs = std::string;
|
||||
using JobModel = render::Job::ModelIO<ExtractSelectionName, Inputs, Outputs>;
|
||||
|
||||
ExtractSelectionName(unsigned int highlightIndex) : _highlightPassIndex{ highlightIndex } {}
|
||||
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs);
|
||||
|
||||
private:
|
||||
|
||||
unsigned int _highlightPassIndex;
|
||||
|
||||
};
|
||||
|
||||
class DrawHighlightMask {
|
||||
public:
|
||||
|
||||
using Inputs = render::VaryingSet2<render::ShapeBounds, HighlightRessourcesPointer>;
|
||||
using Outputs = glm::ivec4;
|
||||
using JobModel = render::Job::ModelIO<DrawHighlightMask, Inputs, Outputs>;
|
||||
|
||||
DrawHighlightMask(unsigned int highlightIndex, render::ShapePlumberPointer shapePlumber, HighlightSharedParametersPointer parameters);
|
||||
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs);
|
||||
|
||||
protected:
|
||||
|
||||
unsigned int _highlightPassIndex;
|
||||
render::ShapePlumberPointer _shapePlumber;
|
||||
HighlightSharedParametersPointer _sharedParameters;
|
||||
gpu::BufferPointer _boundsBuffer;
|
||||
|
||||
static gpu::PipelinePointer _stencilMaskPipeline;
|
||||
static gpu::PipelinePointer _stencilMaskFillPipeline;
|
||||
};
|
||||
|
||||
class DrawHighlight {
|
||||
public:
|
||||
|
||||
using Inputs = render::VaryingSet4<DeferredFrameTransformPointer, HighlightRessourcesPointer, DeferredFramebufferPointer, glm::ivec4>;
|
||||
using Config = render::Job::Config;
|
||||
using JobModel = render::Job::ModelI<DrawHighlight, Inputs, Config>;
|
||||
|
||||
DrawHighlight(unsigned int highlightIndex, HighlightSharedParametersPointer parameters);
|
||||
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
|
||||
|
||||
private:
|
||||
|
||||
#include "Highlight_shared.slh"
|
||||
|
||||
enum {
|
||||
SCENE_DEPTH_MAP_SLOT = 0,
|
||||
HIGHLIGHTED_DEPTH_MAP_SLOT,
|
||||
|
||||
HIGHLIGHT_PARAMS_SLOT = 0,
|
||||
FRAME_TRANSFORM_SLOT,
|
||||
};
|
||||
|
||||
using HighlightConfigurationBuffer = gpu::StructBuffer<HighlightParameters>;
|
||||
|
||||
static const gpu::PipelinePointer& getPipeline(const render::HighlightStyle& style);
|
||||
|
||||
static gpu::PipelinePointer _pipeline;
|
||||
static gpu::PipelinePointer _pipelineFilled;
|
||||
|
||||
unsigned int _highlightPassIndex;
|
||||
HighlightParameters _parameters;
|
||||
HighlightSharedParametersPointer _sharedParameters;
|
||||
HighlightConfigurationBuffer _configuration;
|
||||
};
|
||||
|
||||
class DebugHighlightConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool viewMask MEMBER viewMask NOTIFY dirty)
|
||||
|
||||
public:
|
||||
|
||||
bool viewMask{ false };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class DebugHighlight {
|
||||
public:
|
||||
using Inputs = render::VaryingSet2<HighlightRessourcesPointer, glm::ivec4>;
|
||||
using Config = DebugHighlightConfig;
|
||||
using JobModel = render::Job::ModelI<DebugHighlight, Inputs, Config>;
|
||||
|
||||
DebugHighlight();
|
||||
~DebugHighlight();
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
|
||||
|
||||
private:
|
||||
|
||||
gpu::PipelinePointer _depthPipeline;
|
||||
int _geometryDepthId{ 0 };
|
||||
bool _isDisplayEnabled{ false };
|
||||
|
||||
const gpu::PipelinePointer& getDepthPipeline();
|
||||
void initializePipelines();
|
||||
};
|
||||
|
||||
class DrawHighlightTask {
|
||||
public:
|
||||
|
||||
using Inputs = render::VaryingSet4<RenderFetchCullSortTask::BucketList, DeferredFramebufferPointer, gpu::FramebufferPointer, DeferredFrameTransformPointer>;
|
||||
using Config = render::Task::Config;
|
||||
using JobModel = render::Task::ModelI<DrawHighlightTask, Inputs, Config>;
|
||||
|
||||
DrawHighlightTask();
|
||||
|
||||
void configure(const Config& config);
|
||||
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs);
|
||||
|
||||
private:
|
||||
|
||||
static void initMaskPipelines(render::ShapePlumber& plumber, gpu::StatePointer state);
|
||||
static const render::Varying addSelectItemJobs(JobModel& task, const render::Varying& selectionName, const RenderFetchCullSortTask::BucketList& items);
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_render_utils_HighlightEffect_h
|
||||
|
||||
|
104
libraries/render-utils/src/Highlight_aabox.slv
Normal file
104
libraries/render-utils/src/Highlight_aabox.slv
Normal file
|
@ -0,0 +1,104 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// Draw and transform the fed vertex position with the standard MVP stack
|
||||
// and offset the vertices by a certain amount in the vertex direction
|
||||
//
|
||||
// Created by Olivier Prat on 11/02/2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
<@include gpu/Transform.slh@>
|
||||
|
||||
<$declareStandardTransform()$>
|
||||
|
||||
struct ItemBound {
|
||||
vec4 id_boundPos;
|
||||
vec4 boundDim_s;
|
||||
};
|
||||
|
||||
#if defined(GPU_GL410)
|
||||
uniform samplerBuffer ssbo0Buffer;
|
||||
ItemBound getItemBound(int i) {
|
||||
int offset = 2 * i;
|
||||
ItemBound bound;
|
||||
bound.id_boundPos = texelFetch(ssbo0Buffer, offset);
|
||||
bound.boundDim_s = texelFetch(ssbo0Buffer, offset + 1);
|
||||
return bound;
|
||||
}
|
||||
#else
|
||||
layout(std140) buffer ssbo0Buffer {
|
||||
ItemBound bounds[];
|
||||
};
|
||||
ItemBound getItemBound(int i) {
|
||||
ItemBound bound = bounds[i];
|
||||
return bound;
|
||||
}
|
||||
#endif
|
||||
|
||||
uniform vec2 outlineWidth;
|
||||
|
||||
void main(void) {
|
||||
const vec3 UNIT_BOX_VERTICES[8] = vec3[8](
|
||||
vec3(0.0, 1.0, 0.0),
|
||||
vec3(1.0, 1.0, 0.0),
|
||||
vec3(1.0, 0.0, 0.0),
|
||||
vec3(0.0, 0.0, 0.0),
|
||||
vec3(0.0, 1.0, 1.0),
|
||||
vec3(1.0, 1.0, 1.0),
|
||||
vec3(1.0, 0.0, 1.0),
|
||||
vec3(0.0, 0.0, 1.0)
|
||||
);
|
||||
const vec3 UNIT_BOX_NORMALS[8] = vec3[8](
|
||||
vec3(-1.0, 1.0, -1.0),
|
||||
vec3(1.0, 1.0, -1.0),
|
||||
vec3(1.0, -1.0, -1.0),
|
||||
vec3(-1.0, -1.0, -1.0),
|
||||
vec3(-1.0, 1.0, 1.0),
|
||||
vec3(1.0, 1.0, 1.0),
|
||||
vec3(1.0, -1.0, 1.0),
|
||||
vec3(-1.0, -1.0, 1.0)
|
||||
);
|
||||
const int NUM_VERTICES_PER_CUBE = 36;
|
||||
const int UNIT_BOX_TRIANGLE_INDICES[NUM_VERTICES_PER_CUBE] = int[NUM_VERTICES_PER_CUBE](
|
||||
0, 1, 2,
|
||||
0, 2, 3,
|
||||
3, 2, 6,
|
||||
3, 6, 7,
|
||||
7, 6, 5,
|
||||
7, 5, 4,
|
||||
4, 5, 1,
|
||||
4, 1, 0,
|
||||
1, 5, 6,
|
||||
1, 6, 2,
|
||||
4, 0, 3,
|
||||
4, 3, 7
|
||||
);
|
||||
|
||||
int boundID = gl_VertexID / NUM_VERTICES_PER_CUBE;
|
||||
int vertexID = gl_VertexID - boundID * NUM_VERTICES_PER_CUBE;
|
||||
int triangleIndex = UNIT_BOX_TRIANGLE_INDICES[vertexID];
|
||||
vec3 cubeVec = UNIT_BOX_VERTICES[triangleIndex];
|
||||
|
||||
ItemBound bound = getItemBound(boundID);
|
||||
vec3 boundPos = bound.id_boundPos.yzw;
|
||||
vec3 boundDim = bound.boundDim_s.xyz;
|
||||
|
||||
vec4 pos = vec4(boundPos + boundDim * cubeVec.xyz, 1.0);
|
||||
|
||||
// standard transform
|
||||
TransformCamera cam = getTransformCamera();
|
||||
TransformObject obj = getTransformObject();
|
||||
<$transformModelToMonoClipPos(cam, obj, pos, gl_Position)$>
|
||||
|
||||
// Offset the vertex to take into account the outline width
|
||||
pos.xyz += UNIT_BOX_NORMALS[triangleIndex];
|
||||
vec4 offsetPosition;
|
||||
<$transformModelToMonoClipPos(cam, obj, pos, offsetPosition)$>
|
||||
gl_Position.xy += normalize(offsetPosition.xy-gl_Position.xy) * outlineWidth * gl_Position.w;
|
||||
<$transformStereoClipsSpace(cam, gl_Position)$>
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
// Outline_filled.slf
|
||||
// Add outline effect based on two zbuffers : one containing the total scene z and another
|
||||
// Highlight_filled.slf
|
||||
// Add highlight effect based on two zbuffers : one containing the total scene z and another
|
||||
// with the z of only the objects to be outlined.
|
||||
// This is the version with the fill effect inside the silhouette.
|
||||
//
|
||||
|
@ -9,5 +9,5 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
<@include Outline.slh@>
|
||||
<@include Highlight.slh@>
|
||||
<$main(1)$>
|
30
libraries/render-utils/src/Highlight_shared.slh
Normal file
30
libraries/render-utils/src/Highlight_shared.slh
Normal file
|
@ -0,0 +1,30 @@
|
|||
// glsl / C++ compatible source as interface for highlight
|
||||
#ifdef __cplusplus
|
||||
# define TVEC2 glm::vec2
|
||||
# define TVEC3 glm::vec3
|
||||
# define TVEC4 glm::vec4
|
||||
#else
|
||||
# define TVEC2 vec2
|
||||
# define TVEC3 vec3
|
||||
# define TVEC4 vec4
|
||||
#endif
|
||||
|
||||
struct HighlightParameters
|
||||
{
|
||||
TVEC3 _color;
|
||||
float _intensity;
|
||||
|
||||
TVEC2 _size;
|
||||
float _unoccludedFillOpacity;
|
||||
float _occludedFillOpacity;
|
||||
|
||||
float _threshold;
|
||||
int _blurKernelSize;
|
||||
float padding2;
|
||||
float padding3;
|
||||
};
|
||||
|
||||
// <@if 1@>
|
||||
// Trigger Scribe include
|
||||
// <@endif@> <!def that !>
|
||||
//
|
|
@ -727,7 +727,7 @@ void DebugLightClusters::run(const render::RenderContextPointer& renderContext,
|
|||
batch.setModelTransform(Transform());
|
||||
|
||||
// Bind the Light CLuster data strucutre
|
||||
batch.setUniformBuffer(LIGHT_GPU_SLOT, lightClusters->_lightStage->_lightArrayBuffer);
|
||||
batch.setUniformBuffer(LIGHT_GPU_SLOT, lightClusters->_lightStage->getLightArrayBuffer());
|
||||
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT, lightClusters->_frustumGridBuffer);
|
||||
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, lightClusters->_clusterGridBuffer);
|
||||
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, lightClusters->_clusterContentBuffer);
|
||||
|
|
|
@ -14,18 +14,34 @@
|
|||
#include "LightStage.h"
|
||||
|
||||
std::string LightStage::_stageName { "LIGHT_STAGE"};
|
||||
const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
|
||||
|
||||
LightStage::LightStage() {
|
||||
}
|
||||
|
||||
LightStage::Shadow::Shadow(model::LightPointer light) : _light{ light}, _frustum{ std::make_shared<ViewFrustum>() } {
|
||||
framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::createShadowmap(MAP_SIZE));
|
||||
map = framebuffer->getDepthStencilBuffer();
|
||||
Schema schema;
|
||||
_schemaBuffer = std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema);
|
||||
LightStage::Shadow::Schema::Schema() :
|
||||
bias{ 0.005f },
|
||||
scale{ 1.0f / MAP_SIZE } {
|
||||
|
||||
}
|
||||
|
||||
void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, float nearDepth, float farDepth) {
|
||||
gpu::FramebufferPointer LightStage::Shadow::framebuffer;
|
||||
gpu::TexturePointer LightStage::Shadow::map;
|
||||
|
||||
LightStage::Shadow::Shadow(model::LightPointer light) : _light{ light}, _frustum{ std::make_shared<ViewFrustum>() } {
|
||||
Schema schema;
|
||||
_schemaBuffer = std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema);
|
||||
|
||||
if (!framebuffer) {
|
||||
framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::createShadowmap(MAP_SIZE));
|
||||
map = framebuffer->getDepthStencilBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum,
|
||||
float viewMinShadowDistance, float viewMaxShadowDistance,
|
||||
float nearDepth, float farDepth) {
|
||||
assert(viewMinShadowDistance < viewMaxShadowDistance);
|
||||
assert(nearDepth < farDepth);
|
||||
|
||||
// Orient the keylight frustum
|
||||
|
@ -48,8 +64,8 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, floa
|
|||
const Transform view{ _frustum->getView()};
|
||||
const Transform viewInverse{ view.getInverseMatrix() };
|
||||
|
||||
auto nearCorners = viewFrustum.getCorners(nearDepth);
|
||||
auto farCorners = viewFrustum.getCorners(farDepth);
|
||||
auto nearCorners = viewFrustum.getCorners(viewMinShadowDistance);
|
||||
auto farCorners = viewFrustum.getCorners(viewMaxShadowDistance);
|
||||
|
||||
vec3 min{ viewInverse.transform(nearCorners.bottomLeft) };
|
||||
vec3 max{ min };
|
||||
|
@ -73,7 +89,10 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, floa
|
|||
fitFrustum(farCorners.topLeft);
|
||||
fitFrustum(farCorners.topRight);
|
||||
|
||||
glm::mat4 ortho = glm::ortho<float>(min.x, max.x, min.y, max.y, -max.z, -min.z);
|
||||
// Re-adjust near shadow distance
|
||||
auto near = glm::max(max.z, -nearDepth);
|
||||
auto far = -min.z;
|
||||
glm::mat4 ortho = glm::ortho<float>(min.x, max.x, min.y, max.y, near, far);
|
||||
_frustum->setProjection(ortho);
|
||||
|
||||
// Calculate the frustum's internal state
|
||||
|
@ -84,6 +103,16 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, floa
|
|||
_schemaBuffer.edit<Schema>().viewInverse = viewInverse.getMatrix();
|
||||
}
|
||||
|
||||
void LightStage::Shadow::setFrustum(const ViewFrustum& shadowFrustum) {
|
||||
const Transform view{ shadowFrustum.getView() };
|
||||
const Transform viewInverse{ view.getInverseMatrix() };
|
||||
|
||||
*_frustum = shadowFrustum;
|
||||
// Update the buffer
|
||||
_schemaBuffer.edit<Schema>().projection = shadowFrustum.getProjection();
|
||||
_schemaBuffer.edit<Schema>().viewInverse = viewInverse.getMatrix();
|
||||
}
|
||||
|
||||
const glm::mat4& LightStage::Shadow::getView() const {
|
||||
return _frustum->getView();
|
||||
}
|
||||
|
@ -99,11 +128,9 @@ LightStage::Index LightStage::findLight(const LightPointer& light) const {
|
|||
} else {
|
||||
return (*found).second;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LightStage::Index LightStage::addLight(const LightPointer& light) {
|
||||
|
||||
auto found = _lightMap.find(light);
|
||||
if (found == _lightMap.end()) {
|
||||
auto lightId = _lights.newElement(light);
|
||||
|
@ -114,6 +141,7 @@ LightStage::Index LightStage::addLight(const LightPointer& light) {
|
|||
if (lightId >= (Index) _descs.size()) {
|
||||
_descs.emplace_back(Desc());
|
||||
} else {
|
||||
assert(_descs[lightId].shadowId == INVALID_INDEX);
|
||||
_descs.emplace(_descs.begin() + lightId, Desc());
|
||||
}
|
||||
|
||||
|
@ -132,6 +160,7 @@ LightStage::Index LightStage::addShadow(Index lightIndex) {
|
|||
auto light = getLight(lightIndex);
|
||||
Index shadowId = INVALID_INDEX;
|
||||
if (light) {
|
||||
assert(_descs[lightIndex].shadowId == INVALID_INDEX);
|
||||
shadowId = _shadows.newElement(std::make_shared<Shadow>(light));
|
||||
_descs[lightIndex].shadowId = shadowId;
|
||||
}
|
||||
|
@ -139,18 +168,65 @@ LightStage::Index LightStage::addShadow(Index lightIndex) {
|
|||
}
|
||||
|
||||
LightStage::LightPointer LightStage::removeLight(Index index) {
|
||||
LightPointer removed = _lights.freeElement(index);
|
||||
|
||||
if (removed) {
|
||||
LightPointer removedLight = _lights.freeElement(index);
|
||||
if (removedLight) {
|
||||
auto shadowId = _descs[index].shadowId;
|
||||
// Remove shadow if one exists for this light
|
||||
if (shadowId != INVALID_INDEX) {
|
||||
_shadows.freeElement(shadowId);
|
||||
auto removedShadow = _shadows.freeElement(shadowId);
|
||||
assert(removedShadow);
|
||||
assert(removedShadow->getLight() == removedLight);
|
||||
}
|
||||
_lightMap.erase(removed);
|
||||
_lightMap.erase(removedLight);
|
||||
_descs[index] = Desc();
|
||||
}
|
||||
return removed;
|
||||
assert(_descs.size() <= index || _descs[index].shadowId == INVALID_INDEX);
|
||||
return removedLight;
|
||||
}
|
||||
|
||||
LightStage::LightPointer LightStage::getCurrentKeyLight() const {
|
||||
Index keyLightId{ 0 };
|
||||
if (!_currentFrame._sunLights.empty()) {
|
||||
keyLightId = _currentFrame._sunLights.front();
|
||||
}
|
||||
return _lights.get(keyLightId);
|
||||
}
|
||||
|
||||
LightStage::LightPointer LightStage::getCurrentAmbientLight() const {
|
||||
Index keyLightId{ 0 };
|
||||
if (!_currentFrame._ambientLights.empty()) {
|
||||
keyLightId = _currentFrame._ambientLights.front();
|
||||
}
|
||||
return _lights.get(keyLightId);
|
||||
}
|
||||
|
||||
LightStage::ShadowPointer LightStage::getCurrentKeyShadow() const {
|
||||
Index keyLightId{ 0 };
|
||||
if (!_currentFrame._sunLights.empty()) {
|
||||
keyLightId = _currentFrame._sunLights.front();
|
||||
}
|
||||
auto shadow = getShadow(keyLightId);
|
||||
assert(shadow == nullptr || shadow->getLight() == getLight(keyLightId));
|
||||
return shadow;
|
||||
}
|
||||
|
||||
LightStage::LightAndShadow LightStage::getCurrentKeyLightAndShadow() const {
|
||||
Index keyLightId{ 0 };
|
||||
if (!_currentFrame._sunLights.empty()) {
|
||||
keyLightId = _currentFrame._sunLights.front();
|
||||
}
|
||||
auto shadow = getShadow(keyLightId);
|
||||
auto light = getLight(keyLightId);
|
||||
assert(shadow == nullptr || shadow->getLight() == light);
|
||||
return LightAndShadow(light, shadow);
|
||||
}
|
||||
|
||||
LightStage::Index LightStage::getShadowId(Index lightId) const {
|
||||
if (checkLightId(lightId)) {
|
||||
return _descs[lightId].shadowId;
|
||||
} else {
|
||||
return INVALID_INDEX;
|
||||
}
|
||||
}
|
||||
|
||||
void LightStage::updateLightArrayBuffer(Index lightId) {
|
||||
|
|
|
@ -32,7 +32,7 @@ public:
|
|||
static const std::string& getName() { return _stageName; }
|
||||
|
||||
using Index = render::indexed_container::Index;
|
||||
static const Index INVALID_INDEX { render::indexed_container::INVALID_INDEX };
|
||||
static const Index INVALID_INDEX;
|
||||
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
|
||||
|
||||
using LightPointer = model::LightPointer;
|
||||
|
@ -48,8 +48,9 @@ public:
|
|||
|
||||
Shadow(model::LightPointer light);
|
||||
|
||||
void setKeylightFrustum(const ViewFrustum& viewFrustum, float nearDepth, float farDepth);
|
||||
void setKeylightFrustum(const ViewFrustum& viewFrustum, float viewMinShadowDistance, float viewMaxShadowDistance, float nearDepth = 1.0f, float farDepth = 1000.0f);
|
||||
|
||||
void setFrustum(const ViewFrustum& shadowFrustum);
|
||||
const std::shared_ptr<ViewFrustum> getFrustum() const { return _frustum; }
|
||||
|
||||
const glm::mat4& getView() const;
|
||||
|
@ -57,32 +58,36 @@ public:
|
|||
|
||||
const UniformBufferView& getBuffer() const { return _schemaBuffer; }
|
||||
|
||||
gpu::FramebufferPointer framebuffer;
|
||||
gpu::TexturePointer map;
|
||||
// Shadow maps are shared among all lights for the moment as only one key light
|
||||
// is used.
|
||||
static gpu::FramebufferPointer framebuffer;
|
||||
static gpu::TexturePointer map;
|
||||
|
||||
const model::LightPointer& getLight() const { return _light; }
|
||||
|
||||
protected:
|
||||
|
||||
model::LightPointer _light;
|
||||
std::shared_ptr<ViewFrustum> _frustum;
|
||||
|
||||
class Schema {
|
||||
public:
|
||||
|
||||
Schema();
|
||||
|
||||
glm::mat4 projection;
|
||||
glm::mat4 viewInverse;
|
||||
|
||||
glm::float32 bias = 0.005f;
|
||||
glm::float32 scale = 1 / MAP_SIZE;
|
||||
glm::float32 bias;
|
||||
glm::float32 scale;
|
||||
};
|
||||
UniformBufferView _schemaBuffer = nullptr;
|
||||
|
||||
friend class Light;
|
||||
};
|
||||
|
||||
using ShadowPointer = std::shared_ptr<Shadow>;
|
||||
using Shadows = render::indexed_container::IndexedPointerVector<Shadow>;
|
||||
|
||||
struct Desc {
|
||||
Index shadowId { INVALID_INDEX };
|
||||
};
|
||||
using Descs = std::vector<Desc>;
|
||||
|
||||
Index findLight(const LightPointer& light) const;
|
||||
Index addLight(const LightPointer& light);
|
||||
|
||||
|
@ -100,50 +105,29 @@ public:
|
|||
return _lights.get(lightId);
|
||||
}
|
||||
|
||||
Index getShadowId(Index lightId) const {
|
||||
if (checkLightId(lightId)) {
|
||||
return _descs[lightId].shadowId;
|
||||
} else {
|
||||
return INVALID_INDEX;
|
||||
}
|
||||
}
|
||||
Index getShadowId(Index lightId) const;
|
||||
|
||||
ShadowPointer getShadow(Index lightId) const {
|
||||
return _shadows.get(getShadowId(lightId));
|
||||
}
|
||||
|
||||
using LightAndShadow = std::pair<LightPointer, ShadowPointer>;
|
||||
LightAndShadow getLightAndShadow(Index lightId) const {
|
||||
return LightAndShadow(getLight(lightId), getShadow(lightId));
|
||||
auto light = getLight(lightId);
|
||||
auto shadow = getShadow(lightId);
|
||||
assert(shadow == nullptr || shadow->getLight() == light);
|
||||
return LightAndShadow(light, shadow);
|
||||
}
|
||||
|
||||
LightPointer getCurrentKeyLight() const {
|
||||
Index keyLightId{ 0 };
|
||||
if (!_currentFrame._sunLights.empty()) {
|
||||
keyLightId = _currentFrame._sunLights.front();
|
||||
}
|
||||
return _lights.get(keyLightId);
|
||||
}
|
||||
|
||||
ShadowPointer getCurrentKeyShadow() const {
|
||||
Index keyLightId{ 0 };
|
||||
if (!_currentFrame._sunLights.empty()) {
|
||||
keyLightId = _currentFrame._sunLights.front();
|
||||
}
|
||||
return getShadow(keyLightId);
|
||||
}
|
||||
|
||||
LightAndShadow getCurrentKeyLightAndShadow() const {
|
||||
Index keyLightId{ 0 };
|
||||
if (!_currentFrame._sunLights.empty()) {
|
||||
keyLightId = _currentFrame._sunLights.front();
|
||||
}
|
||||
return LightAndShadow(getLight(keyLightId), getShadow(keyLightId));
|
||||
}
|
||||
LightPointer getCurrentKeyLight() const;
|
||||
LightPointer getCurrentAmbientLight() const;
|
||||
ShadowPointer getCurrentKeyShadow() const;
|
||||
LightAndShadow getCurrentKeyLightAndShadow() const;
|
||||
|
||||
LightStage();
|
||||
Lights _lights;
|
||||
LightMap _lightMap;
|
||||
Descs _descs;
|
||||
|
||||
gpu::BufferPointer getLightArrayBuffer() const { return _lightArrayBuffer; }
|
||||
void updateLightArrayBuffer(Index lightId);
|
||||
|
||||
class Frame {
|
||||
public:
|
||||
|
@ -172,15 +156,24 @@ public:
|
|||
|
||||
Frame _currentFrame;
|
||||
|
||||
gpu::BufferPointer _lightArrayBuffer;
|
||||
void updateLightArrayBuffer(Index lightId);
|
||||
protected:
|
||||
|
||||
struct Desc {
|
||||
Index shadowId{ INVALID_INDEX };
|
||||
};
|
||||
using Descs = std::vector<Desc>;
|
||||
|
||||
gpu::BufferPointer _lightArrayBuffer;
|
||||
|
||||
Lights _lights;
|
||||
Shadows _shadows;
|
||||
Descs _descs;
|
||||
LightMap _lightMap;
|
||||
|
||||
};
|
||||
using LightStagePointer = std::shared_ptr<LightStage>;
|
||||
|
||||
|
||||
|
||||
class LightStageSetup {
|
||||
public:
|
||||
using JobModel = render::Job::Model<LightStageSetup>;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue