mirror of
https://github.com/Armored-Dragon/overte.git
synced 2025-03-11 16:13:16 +01:00
Merge branch 'master' into move_to_qqc2
This commit is contained in:
commit
29a17d366e
139 changed files with 4215 additions and 1055 deletions
|
@ -477,7 +477,7 @@ void EntityServer::startDynamicDomainVerification() {
|
|||
QNetworkRequest networkRequest;
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL;
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
|
||||
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location");
|
||||
QJsonObject request;
|
||||
request["certificate_id"] = i.key();
|
||||
|
|
|
@ -94,7 +94,7 @@ bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection,
|
|||
root.insert(requestSubobjectKey, subobject);
|
||||
QJsonDocument doc { root };
|
||||
|
||||
QUrl url { NetworkingConstants::METAVERSE_SERVER_URL.toString() + metaversePath };
|
||||
QUrl url { NetworkingConstants::METAVERSE_SERVER_URL().toString() + metaversePath };
|
||||
|
||||
QNetworkRequest req(url);
|
||||
req.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
|
@ -420,7 +420,7 @@ bool DomainServer::optionallySetupOAuth() {
|
|||
|
||||
// if we don't have an oauth provider URL then we default to the default node auth url
|
||||
if (_oauthProviderURL.isEmpty()) {
|
||||
_oauthProviderURL = NetworkingConstants::METAVERSE_SERVER_URL;
|
||||
_oauthProviderURL = NetworkingConstants::METAVERSE_SERVER_URL();
|
||||
}
|
||||
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
|
@ -2159,7 +2159,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
QJsonDocument doc(root);
|
||||
|
||||
|
||||
QUrl url { NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/api/v1/places/" + place_id };
|
||||
QUrl url { NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/api/v1/places/" + place_id };
|
||||
|
||||
url.setQuery("access_token=" + accessTokenVariant->toString());
|
||||
|
||||
|
|
|
@ -208,7 +208,7 @@ void IceServer::requestDomainPublicKey(const QUuid& domainID) {
|
|||
// send a request to the metaverse API for the public key for this domain
|
||||
auto& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
|
||||
QUrl publicKeyURL { NetworkingConstants::METAVERSE_SERVER_URL };
|
||||
QUrl publicKeyURL { NetworkingConstants::METAVERSE_SERVER_URL() };
|
||||
QString publicKeyPath = QString("/api/v1/domains/%1/public_key").arg(uuidStringWithoutCurlyBraces(domainID));
|
||||
publicKeyURL.setPath(publicKeyPath);
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ var EventBridge;
|
|||
var tempEventBridge = EventBridge;
|
||||
EventBridge = channel.objects.eventBridge;
|
||||
EventBridge.audioOutputDeviceChanged.connect(function(deviceName) {
|
||||
navigator.mediaDevices.getUserMedia({ audio: true, video: true }).then(function(mediaStream) {
|
||||
navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(function(mediaStream) {
|
||||
navigator.mediaDevices.enumerateDevices().then(function(devices) {
|
||||
devices.forEach(function(device) {
|
||||
if (device.kind == "audiooutput") {
|
||||
|
|
|
@ -52,7 +52,11 @@ Item {
|
|||
targetHeight += hifi.dimensions.contentSpacing.y + additionalInformation.height
|
||||
}
|
||||
|
||||
parent.width = root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth));
|
||||
var newWidth = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth));
|
||||
if(!isNaN(newWidth)) {
|
||||
parent.width = root.width = newWidth;
|
||||
}
|
||||
|
||||
parent.height = root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
|
||||
+ (keyboardEnabled && keyboardRaised ? (200 + 2 * hifi.dimensions.contentSpacing.y) : hifi.dimensions.contentSpacing.y);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ import "../"
|
|||
import "../toolbars"
|
||||
import "../../styles-uit" as HifiStyles
|
||||
import "../../controls-uit" as HifiControls
|
||||
import QtQuick.Controls 2.2 as QQC2
|
||||
import QtQuick.Templates 2.2 as T
|
||||
|
||||
// references HMD, AddressManager, AddressBarDialog from root context
|
||||
|
||||
|
@ -223,7 +225,7 @@ StackView {
|
|||
visible: addressLine.text.length === 0
|
||||
}
|
||||
|
||||
TextField {
|
||||
QQC2.TextField {
|
||||
id: addressLine
|
||||
width: addressLineContainer.width - addressLineContainer.anchors.leftMargin - addressLineContainer.anchors.rightMargin;
|
||||
anchors {
|
||||
|
@ -238,16 +240,36 @@ StackView {
|
|||
addressBarDialog.keyboardEnabled = false;
|
||||
toggleOrGo();
|
||||
}
|
||||
placeholderText: "Type domain address here"
|
||||
|
||||
// unfortunately TextField from Quick Controls 2 disallow customization of placeHolderText color without creation of new style
|
||||
property string placeholderText2: "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
|
||||
|
||||
font {
|
||||
family: hifi.fonts.fontFamily
|
||||
pixelSize: hifi.fonts.pixelSize * 0.75
|
||||
}
|
||||
|
||||
color: hifi.colors.text
|
||||
background: Item {}
|
||||
|
||||
QQC2.Label {
|
||||
T.TextField {
|
||||
id: control
|
||||
|
||||
padding: 6 // numbers taken from Qt\5.9.2\Src\qtquickcontrols2\src\imports\controls\TextField.qml
|
||||
leftPadding: padding + 4
|
||||
}
|
||||
background: Item {}
|
||||
|
||||
font: parent.font
|
||||
|
||||
x: control.leftPadding
|
||||
y: control.topPadding
|
||||
|
||||
text: parent.placeholderText2
|
||||
verticalAlignment: "AlignVCenter"
|
||||
color: 'gray'
|
||||
visible: parent.text === ''
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -971,7 +971,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
|
||||
// set the account manager's root URL and trigger a login request if we don't have the access token
|
||||
accountManager->setIsAgent(true);
|
||||
accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL);
|
||||
accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL());
|
||||
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
|
||||
|
@ -3222,8 +3222,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Application::keyReleaseEvent(QKeyEvent* event) {
|
||||
_keysPressed.remove(event->key());
|
||||
|
||||
|
@ -4848,8 +4846,7 @@ void Application::update(float deltaTime) {
|
|||
if (_physicsEnabled) {
|
||||
{
|
||||
PROFILE_RANGE(simulation_physics, "PreStep");
|
||||
|
||||
PerformanceTimer perfTimer("updateStates)");
|
||||
PerformanceTimer perfTimer("preStep)");
|
||||
static VectorOfMotionStates motionStates;
|
||||
_entitySimulation->getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
|
@ -4882,22 +4879,22 @@ void Application::update(float deltaTime) {
|
|||
}
|
||||
{
|
||||
PROFILE_RANGE(simulation_physics, "Step");
|
||||
PerformanceTimer perfTimer("stepSimulation");
|
||||
PerformanceTimer perfTimer("step");
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
_physicsEngine->stepSimulation();
|
||||
});
|
||||
}
|
||||
{
|
||||
PROFILE_RANGE(simulation_physics, "PostStep");
|
||||
PerformanceTimer perfTimer("harvestChanges");
|
||||
PerformanceTimer perfTimer("postStep");
|
||||
if (_physicsEngine->hasOutgoingChanges()) {
|
||||
// grab the collision events BEFORE handleOutgoingChanges() because at this point
|
||||
// we have a better idea of which objects we own or should own.
|
||||
auto& collisionEvents = _physicsEngine->getCollisionEvents();
|
||||
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
PROFILE_RANGE(simulation_physics, "Harvest");
|
||||
PerformanceTimer perfTimer("handleOutgoingChanges");
|
||||
PROFILE_RANGE(simulation_physics, "HandleChanges");
|
||||
PerformanceTimer perfTimer("handleChanges");
|
||||
|
||||
const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates();
|
||||
_entitySimulation->handleChangedMotionStates(outgoingChanges);
|
||||
|
@ -4908,17 +4905,15 @@ void Application::update(float deltaTime) {
|
|||
});
|
||||
|
||||
if (!_aboutToQuit) {
|
||||
// handleCollisionEvents() AFTER handleOutgoinChanges()
|
||||
// handleCollisionEvents() AFTER handleOutgoingChanges()
|
||||
{
|
||||
PROFILE_RANGE(simulation_physics, "CollisionEvents");
|
||||
PerformanceTimer perfTimer("entities");
|
||||
avatarManager->handleCollisionEvents(collisionEvents);
|
||||
// Collision events (and their scripts) must not be handled when we're locked, above. (That would risk
|
||||
// deadlock.)
|
||||
_entitySimulation->handleCollisionEvents(collisionEvents);
|
||||
}
|
||||
|
||||
PROFILE_RANGE(simulation_physics, "UpdateEntities");
|
||||
// NOTE: the getEntities()->update() call below will wait for lock
|
||||
// and will simulate entity motion (the EntityTree has been given an EntitySimulation).
|
||||
getEntities()->update(true); // update the models...
|
||||
|
@ -4929,7 +4924,8 @@ void Application::update(float deltaTime) {
|
|||
myAvatar->harvestResultsFromPhysicsSimulation(deltaTime);
|
||||
}
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) &&
|
||||
if (PerformanceTimer::isActive() &&
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) &&
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming)) {
|
||||
_physicsEngine->harvestPerformanceStats();
|
||||
}
|
||||
|
@ -7506,4 +7502,9 @@ void Application::setAvatarOverrideUrl(const QUrl& url, bool save) {
|
|||
_avatarOverrideUrl = url;
|
||||
_saveAvatarOverrideUrl = save;
|
||||
}
|
||||
|
||||
void Application::saveNextPhysicsStats(QString filename) {
|
||||
_physicsEngine->saveNextPhysicsStats(filename);
|
||||
}
|
||||
|
||||
#include "Application.moc"
|
||||
|
|
|
@ -280,6 +280,7 @@ public:
|
|||
void clearAvatarOverrideUrl() { _avatarOverrideUrl = QUrl(); _saveAvatarOverrideUrl = false; }
|
||||
QUrl getAvatarOverrideUrl() { return _avatarOverrideUrl; }
|
||||
bool getSaveAvatarOverrideUrl() { return _saveAvatarOverrideUrl; }
|
||||
void saveNextPhysicsStats(QString filename);
|
||||
|
||||
signals:
|
||||
void svoImportRequested(const QString& url);
|
||||
|
@ -432,6 +433,7 @@ private slots:
|
|||
|
||||
void handleSandboxStatus(QNetworkReply* reply);
|
||||
void switchDisplayMode();
|
||||
|
||||
private:
|
||||
static void initDisplay();
|
||||
void init();
|
||||
|
|
|
@ -19,10 +19,14 @@ class FancyCamera : public Camera {
|
|||
Q_OBJECT
|
||||
|
||||
/**jsdoc
|
||||
* @namespace Camera
|
||||
* @property cameraEntity {EntityID} The position and rotation properties of
|
||||
* the entity specified by this ID are then used as the camera's position and
|
||||
* orientation. Only works when <code>mode</code> is "entity".
|
||||
* @namespace
|
||||
* @augments Camera
|
||||
*/
|
||||
|
||||
// FIXME: JSDoc 3.5.5 doesn't augment @property definitions. The following definition is repeated in Camera.h.
|
||||
/**jsdoc
|
||||
* @property cameraEntity {Uuid} The ID of the entity that the camera position and orientation follow when the camera is in
|
||||
* entity mode.
|
||||
*/
|
||||
Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity)
|
||||
|
||||
|
@ -34,7 +38,25 @@ public:
|
|||
|
||||
|
||||
public slots:
|
||||
/**jsdoc
|
||||
* Get the ID of the entity that the camera is set to use the position and orientation from when it's in entity mode. You can
|
||||
* also get the entity ID using the <code>Camera.cameraEntity</code> property.
|
||||
* @function Camera.getCameraEntity
|
||||
* @returns {Uuid} The ID of the entity that the camera is set to follow when in entity mode; <code>null</code> if no camera
|
||||
* entity has been set.
|
||||
*/
|
||||
QUuid getCameraEntity() const;
|
||||
|
||||
/**jsdoc
|
||||
* Set the entity that the camera should use the position and orientation from when it's in entity mode. You can also set the
|
||||
* entity using the <code>Camera.cameraEntity</code> property.
|
||||
* @function Camera.setCameraEntity
|
||||
* @param {Uuid} entityID The entity that the camera should follow when it's in entity mode.
|
||||
* @example <caption>Move your camera to the position and orientation of the closest entity.</caption>
|
||||
* Camera.setModeString("entity");
|
||||
* var entity = Entities.findClosestEntity(MyAvatar.position, 100.0);
|
||||
* Camera.setCameraEntity(entity);
|
||||
*/
|
||||
void setCameraEntity(QUuid entityID);
|
||||
|
||||
private:
|
||||
|
|
|
@ -645,7 +645,8 @@ Menu::Menu() {
|
|||
// Developer > Timing >>>
|
||||
MenuWrapper* timingMenu = developerMenu->addMenu("Timing");
|
||||
MenuWrapper* perfTimerMenu = timingMenu->addMenu("Performance Timer");
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false,
|
||||
qApp, SLOT(enablePerfStats(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::OnlyDisplayTopTen, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarTiming, 0, false);
|
||||
|
|
|
@ -81,6 +81,7 @@ const QString& DEFAULT_AVATAR_COLLISION_SOUND_URL = "https://hifi-public.s3.amaz
|
|||
const float MyAvatar::ZOOM_MIN = 0.5f;
|
||||
const float MyAvatar::ZOOM_MAX = 25.0f;
|
||||
const float MyAvatar::ZOOM_DEFAULT = 1.5f;
|
||||
const float MIN_SCALE_CHANGED_DELTA = 0.001f;
|
||||
|
||||
MyAvatar::MyAvatar(QThread* thread) :
|
||||
Avatar(thread),
|
||||
|
@ -670,6 +671,11 @@ void MyAvatar::updateSensorToWorldMatrix() {
|
|||
glm::mat4 desiredMat = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), getWorldOrientation(), getWorldPosition());
|
||||
_sensorToWorldMatrix = desiredMat * glm::inverse(_bodySensorMatrix);
|
||||
|
||||
bool hasSensorToWorldScaleChanged = false;
|
||||
if (fabsf(getSensorToWorldScale() - sensorToWorldScale) > MIN_SCALE_CHANGED_DELTA) {
|
||||
hasSensorToWorldScaleChanged = true;
|
||||
}
|
||||
|
||||
lateUpdatePalms();
|
||||
|
||||
if (_enableDebugDrawSensorToWorldMatrix) {
|
||||
|
@ -678,9 +684,13 @@ void MyAvatar::updateSensorToWorldMatrix() {
|
|||
}
|
||||
|
||||
_sensorToWorldMatrixCache.set(_sensorToWorldMatrix);
|
||||
|
||||
updateJointFromController(controller::Action::LEFT_HAND, _controllerLeftHandMatrixCache);
|
||||
updateJointFromController(controller::Action::RIGHT_HAND, _controllerRightHandMatrixCache);
|
||||
|
||||
if (hasSensorToWorldScaleChanged) {
|
||||
emit sensorToWorldScaleChanged(sensorToWorldScale);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Update avatar head rotation with sensor data
|
||||
|
@ -1405,6 +1415,7 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
_skeletonModel->setVisibleInScene(true, qApp->getMain3DScene());
|
||||
_headBoneSet.clear();
|
||||
emit skeletonChanged();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -1440,6 +1451,7 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN
|
|||
UserActivityLogger::getInstance().changedModel("skeleton", urlString);
|
||||
}
|
||||
markIdentityDataChanged();
|
||||
|
||||
}
|
||||
|
||||
void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
||||
|
|
|
@ -9,9 +9,12 @@
|
|||
#include "MySkeletonModel.h"
|
||||
|
||||
#include <avatars-renderer/Avatar.h>
|
||||
#include <DebugDraw.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "InterfaceLogging.h"
|
||||
#include "AnimUtil.h"
|
||||
|
||||
|
||||
MySkeletonModel::MySkeletonModel(Avatar* owningAvatar, QObject* parent) : SkeletonModel(owningAvatar, parent) {
|
||||
}
|
||||
|
@ -30,6 +33,39 @@ Rig::CharacterControllerState convertCharacterControllerState(CharacterControlle
|
|||
};
|
||||
}
|
||||
|
||||
static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
|
||||
glm::mat4 hipsMat = myAvatar->deriveBodyFromHMDSensor();
|
||||
glm::vec3 hipsPos = extractTranslation(hipsMat);
|
||||
glm::quat hipsRot = glmExtractRotation(hipsMat);
|
||||
|
||||
glm::mat4 avatarToWorldMat = myAvatar->getTransform().getMatrix();
|
||||
glm::mat4 worldToSensorMat = glm::inverse(myAvatar->getSensorToWorldMatrix());
|
||||
glm::mat4 avatarToSensorMat = worldToSensorMat * avatarToWorldMat;
|
||||
|
||||
// dampen hips rotation, by mixing it with the avatar orientation in sensor space
|
||||
const float MIX_RATIO = 0.5f;
|
||||
hipsRot = safeLerp(glmExtractRotation(avatarToSensorMat), hipsRot, MIX_RATIO);
|
||||
|
||||
if (isFlying) {
|
||||
// rotate the hips back to match the flying animation.
|
||||
|
||||
const float TILT_ANGLE = 0.523f;
|
||||
const glm::quat tiltRot = glm::angleAxis(TILT_ANGLE, transformVectorFast(avatarToSensorMat, -Vectors::UNIT_X));
|
||||
|
||||
glm::vec3 headPos;
|
||||
int headIndex = myAvatar->getJointIndex("Head");
|
||||
if (headIndex != -1) {
|
||||
headPos = transformPoint(avatarToSensorMat, myAvatar->getAbsoluteJointTranslationInObjectFrame(headIndex));
|
||||
} else {
|
||||
headPos = transformPoint(myAvatar->getSensorToWorldMatrix(), myAvatar->getHMDSensorPosition());
|
||||
}
|
||||
hipsRot = tiltRot * hipsRot;
|
||||
hipsPos = headPos + tiltRot * (hipsPos - headPos);
|
||||
}
|
||||
|
||||
return AnimPose(hipsRot * Quaternions::Y_180, hipsPos);
|
||||
}
|
||||
|
||||
// Called within Model::simulate call, below.
|
||||
void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||
const FBXGeometry& geometry = getFBXGeometry();
|
||||
|
@ -124,6 +160,39 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
}
|
||||
}
|
||||
|
||||
// if hips are not under direct control, estimate the hips position.
|
||||
if (avatarHeadPose.isValid() && !params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Hips]) {
|
||||
bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS);
|
||||
|
||||
if (!_prevHipsValid) {
|
||||
AnimPose hips = computeHipsInSensorFrame(myAvatar, isFlying);
|
||||
_prevHips = hips;
|
||||
}
|
||||
|
||||
AnimPose hips = computeHipsInSensorFrame(myAvatar, isFlying);
|
||||
|
||||
// smootly lerp hips, in sensorframe, with different coeff for horiz and vertical translation.
|
||||
const float ROT_ALPHA = 0.9f;
|
||||
const float TRANS_HORIZ_ALPHA = 0.9f;
|
||||
const float TRANS_VERT_ALPHA = 0.1f;
|
||||
float hipsY = hips.trans().y;
|
||||
hips.trans() = lerp(hips.trans(), _prevHips.trans(), TRANS_HORIZ_ALPHA);
|
||||
hips.trans().y = lerp(hipsY, _prevHips.trans().y, TRANS_VERT_ALPHA);
|
||||
hips.rot() = safeLerp(hips.rot(), _prevHips.rot(), ROT_ALPHA);
|
||||
|
||||
_prevHips = hips;
|
||||
_prevHipsValid = true;
|
||||
|
||||
glm::mat4 invRigMat = glm::inverse(myAvatar->getTransform().getMatrix() * Matrices::Y_180);
|
||||
AnimPose sensorToRigPose(invRigMat * myAvatar->getSensorToWorldMatrix());
|
||||
|
||||
params.primaryControllerPoses[Rig::PrimaryControllerType_Hips] = sensorToRigPose * hips;
|
||||
params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Hips] = true;
|
||||
|
||||
} else {
|
||||
_prevHipsValid = false;
|
||||
}
|
||||
|
||||
params.isTalking = head->getTimeWithoutTalking() <= 1.5f;
|
||||
|
||||
// pass detailed torso k-dops to rig.
|
||||
|
|
|
@ -25,6 +25,9 @@ public:
|
|||
|
||||
private:
|
||||
void updateFingers();
|
||||
|
||||
AnimPose _prevHips; // sensor frame
|
||||
bool _prevHipsValid { false };
|
||||
};
|
||||
|
||||
#endif // hifi_MySkeletonModel_h
|
||||
|
|
|
@ -130,7 +130,7 @@ QString amountString(const QString& label, const QString&color, const QJsonValue
|
|||
return result + QString("</font>");
|
||||
}
|
||||
|
||||
static const QString MARKETPLACE_ITEMS_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/marketplace/items/";
|
||||
static const QString MARKETPLACE_ITEMS_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace/items/";
|
||||
void Ledger::historySuccess(QNetworkReply& reply) {
|
||||
// here we send a historyResult with some extra stuff in it
|
||||
// Namely, the styled text we'd like to show. The issue is the
|
||||
|
|
|
@ -315,7 +315,6 @@ Wallet::Wallet() {
|
|||
}
|
||||
|
||||
walletScriptingInterface->setWalletStatus(status);
|
||||
emit walletStatusResult(status);
|
||||
});
|
||||
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
|
@ -491,6 +490,7 @@ bool Wallet::walletIsAuthenticatedWithPassphrase() {
|
|||
}
|
||||
if (_publicKeys.count() > 0) {
|
||||
// we _must_ be authenticated if the publicKeys are there
|
||||
DependencyManager::get<WalletScriptingInterface>()->setWalletStatus((uint)WalletStatus::WALLET_STATUS_READY);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -503,6 +503,7 @@ bool Wallet::walletIsAuthenticatedWithPassphrase() {
|
|||
|
||||
// be sure to add the public key so we don't do this over and over
|
||||
_publicKeys.push_back(publicKey.toBase64());
|
||||
DependencyManager::get<WalletScriptingInterface>()->setWalletStatus((uint)WalletStatus::WALLET_STATUS_READY);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -801,15 +802,12 @@ void Wallet::account() {
|
|||
|
||||
void Wallet::getWalletStatus() {
|
||||
auto walletScriptingInterface = DependencyManager::get<WalletScriptingInterface>();
|
||||
uint status;
|
||||
|
||||
if (DependencyManager::get<AccountManager>()->isLoggedIn()) {
|
||||
// This will set account info for the wallet, allowing us to decrypt and display the security image.
|
||||
account();
|
||||
} else {
|
||||
status = (uint)WalletStatus::WALLET_STATUS_NOT_LOGGED_IN;
|
||||
emit walletStatusResult(status);
|
||||
walletScriptingInterface->setWalletStatus(status);
|
||||
walletScriptingInterface->setWalletStatus((uint)WalletStatus::WALLET_STATUS_NOT_LOGGED_IN);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ QNetworkRequest createNetworkRequest() {
|
|||
|
||||
QNetworkRequest request;
|
||||
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL;
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
|
||||
requestURL.setPath(USER_ACTIVITY_URL);
|
||||
|
||||
request.setUrl(requestURL);
|
||||
|
|
|
@ -23,10 +23,10 @@ static const float WEB_STYLUS_LENGTH = 0.2f;
|
|||
static const float TABLET_MIN_HOVER_DISTANCE = -0.1f;
|
||||
static const float TABLET_MAX_HOVER_DISTANCE = 0.1f;
|
||||
static const float TABLET_MIN_TOUCH_DISTANCE = -0.1f;
|
||||
static const float TABLET_MAX_TOUCH_DISTANCE = 0.01f;
|
||||
static const float TABLET_MAX_TOUCH_DISTANCE = 0.005f;
|
||||
|
||||
static const float HOVER_HYSTERESIS = 0.01f;
|
||||
static const float TOUCH_HYSTERESIS = 0.02f;
|
||||
static const float TOUCH_HYSTERESIS = 0.001f;
|
||||
|
||||
static const float STYLUS_MOVE_DELAY = 0.33f * USECS_PER_SECOND;
|
||||
static const float TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.0481f;
|
||||
|
|
|
@ -34,7 +34,7 @@ bool ClipboardScriptingInterface::exportEntities(const QString& filename, const
|
|||
return retVal;
|
||||
}
|
||||
|
||||
bool ClipboardScriptingInterface::exportEntities(const QString& filename, float x, float y, float z, float s) {
|
||||
bool ClipboardScriptingInterface::exportEntities(const QString& filename, float x, float y, float z, float scale) {
|
||||
bool retVal;
|
||||
BLOCKING_INVOKE_METHOD(qApp, "exportEntities",
|
||||
Q_RETURN_ARG(bool, retVal),
|
||||
|
@ -42,7 +42,7 @@ bool ClipboardScriptingInterface::exportEntities(const QString& filename, float
|
|||
Q_ARG(float, x),
|
||||
Q_ARG(float, y),
|
||||
Q_ARG(float, z),
|
||||
Q_ARG(float, s));
|
||||
Q_ARG(float, scale));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include <EntityItemID.h>
|
||||
|
||||
/**jsdoc
|
||||
* The Clipboard API enables you to export and import entities to and from JSON files.
|
||||
*
|
||||
* @namespace Clipboard
|
||||
*/
|
||||
class ClipboardScriptingInterface : public QObject {
|
||||
|
@ -25,48 +27,56 @@ class ClipboardScriptingInterface : public QObject {
|
|||
public:
|
||||
ClipboardScriptingInterface();
|
||||
|
||||
signals:
|
||||
void readyToImport();
|
||||
|
||||
public:
|
||||
/**jsdoc
|
||||
* Compute the extents of the contents held in the clipboard.
|
||||
* @function Clipboard.getContentsDimensions
|
||||
* @return {Vec3} The extents of the contents held in the clipboard.
|
||||
* @returns {Vec3} The extents of the contents held in the clipboard.
|
||||
*/
|
||||
Q_INVOKABLE glm::vec3 getContentsDimensions();
|
||||
|
||||
/**jsdoc
|
||||
* Compute largest dimension of the extents of the contents held in the clipboard
|
||||
* Compute the largest dimension of the extents of the contents held in the clipboard.
|
||||
* @function Clipboard.getClipboardContentsLargestDimension
|
||||
* @return {float} The largest dimension computed.
|
||||
* @returns {number} The largest dimension computed.
|
||||
*/
|
||||
Q_INVOKABLE float getClipboardContentsLargestDimension();
|
||||
|
||||
/**jsdoc
|
||||
* Import entities from a .json file containing entity data into the clipboard.
|
||||
* You can generate * a .json file using {Clipboard.exportEntities}.
|
||||
* Import entities from a JSON file containing entity data into the clipboard.
|
||||
* You can generate a JSON file using {@link Clipboard.exportEntities}.
|
||||
* @function Clipboard.importEntities
|
||||
* @param {string} filename Filename of file to import.
|
||||
* @return {bool} True if the import was succesful, otherwise false.
|
||||
* @param {string} filename Path and name of file to import.
|
||||
* @returns {boolean} <code>true</code> if the import was successful, otherwise <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE bool importEntities(const QString& filename);
|
||||
|
||||
/**jsdoc
|
||||
* Export the entities listed in `entityIDs` to the file `filename`
|
||||
* Export the entities specified to a JSON file.
|
||||
* @function Clipboard.exportEntities
|
||||
* @param {string} filename Path to the file to export entities to.
|
||||
* @param {EntityID[]} entityIDs IDs of entities to export.
|
||||
* @return {bool} True if the export was succesful, otherwise false.
|
||||
* @param {string} filename Path and name of the file to export the entities to. Should have the extension ".json".
|
||||
* @param {Uuid[]} entityIDs Array of IDs of the entities to export.
|
||||
* @returns {boolean} <code>true</code> if the export was successful, otherwise <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE bool exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs);
|
||||
Q_INVOKABLE bool exportEntities(const QString& filename, float x, float y, float z, float s);
|
||||
|
||||
/**jsdoc
|
||||
* Export the entities with centers within a cube to a JSON file.
|
||||
* @function Clipboard.exportEntities
|
||||
* @param {string} filename Path and name of the file to export the entities to. Should have the extension ".json".
|
||||
* @param {number} x X-coordinate of the cube center.
|
||||
* @param {number} y Y-coordinate of the cube center.
|
||||
* @param {number} z Z-coordinate of the cube center.
|
||||
* @param {number} scale Half dimension of the cube.
|
||||
* @returns {boolean} <code>true</code> if the export was successful, otherwise <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE bool exportEntities(const QString& filename, float x, float y, float z, float scale);
|
||||
|
||||
/**jsdoc
|
||||
* Paste the contents of the clipboard into the world.
|
||||
* @function Clipboard.pasteEntities
|
||||
* @param {Vec3} position Position to paste clipboard at.
|
||||
* @return {EntityID[]} Array of entity IDs for the new entities that were
|
||||
* created as a result of the paste operation.
|
||||
* @param {Vec3} position Position to paste the clipboard contents at.
|
||||
* @returns {Uuid[]} Array of entity IDs for the new entities that were created as a result of the paste operation.
|
||||
*/
|
||||
Q_INVOKABLE QVector<EntityItemID> pasteEntities(glm::vec3 position);
|
||||
};
|
||||
|
|
|
@ -18,15 +18,18 @@
|
|||
class MenuItemProperties;
|
||||
|
||||
/**jsdoc
|
||||
* The `Menu` provides access to the menu that is shown at the top of the window
|
||||
* shown on a user's desktop and the right click menu that is accessible
|
||||
* in both Desktop and HMD mode.
|
||||
* The Menu API provides access to the menu that is displayed at the top of the window
|
||||
* on a user's desktop and in the tablet when the "MENU" button is pressed.
|
||||
*
|
||||
* <p />
|
||||
*
|
||||
* <h3>Groupings</h3>
|
||||
* A `grouping` is a way to group a set of menus and/or menu items together
|
||||
* so that they can all be set visible or invisible as a group. There are
|
||||
* 2 available groups: "Advanced" and "Developer"
|
||||
*
|
||||
* A "grouping" provides a way to group a set of menus or menu items together so
|
||||
* that they can all be set visible or invisible as a group.
|
||||
* There are two available groups: <code>"Advanced"</code> and <code>"Developer"</code>.
|
||||
* These groupings can be toggled in the "Settings" menu.
|
||||
* If a menu item doesn't belong to a group it is always displayed.
|
||||
*
|
||||
* @namespace Menu
|
||||
*/
|
||||
|
@ -55,86 +58,113 @@ public slots:
|
|||
/**jsdoc
|
||||
* Add a new top-level menu.
|
||||
* @function Menu.addMenu
|
||||
* @param {string} menuName Name that will be shown in the menu.
|
||||
* @param {string} grouping Name of the grouping to add this menu to.
|
||||
* @param {string} menuName - Name that will be displayed for the menu. Nested menus can be described using the ">" symbol.
|
||||
* @param {string} [grouping] - Name of the grouping, if any, to add this menu to.
|
||||
*
|
||||
* @example <caption>Add a menu and a nested submenu.</caption>
|
||||
* Menu.addMenu("Test Menu");
|
||||
* Menu.addMenu("Test Menu > Test Sub Menu");
|
||||
*
|
||||
* @example <caption>Add a menu to the Settings menu that is only visible if Settings > Advanced is enabled.</caption>
|
||||
* Menu.addMenu("Settings > Test Grouping Menu", "Advanced");
|
||||
*/
|
||||
void addMenu(const QString& menuName, const QString& grouping = QString());
|
||||
|
||||
/**jsdoc
|
||||
* Remove a top-level menu.
|
||||
* @function Menu.removeMenu
|
||||
* @param {string} menuName Name of the menu to remove.
|
||||
* @param {string} menuName - Name of the menu to remove.
|
||||
* @example <caption>Remove a menu and nested submenu.</caption>
|
||||
* Menu.removeMenu("Test Menu > Test Sub Menu");
|
||||
* Menu.removeMenu("Test Menu");
|
||||
*/
|
||||
void removeMenu(const QString& menuName);
|
||||
|
||||
/**jsdoc
|
||||
* Check whether a top-level menu exists.
|
||||
* @function Menu.menuExists
|
||||
* @param {string} menuName Name of the menu to check for existence.
|
||||
* @return {bool} `true` if the menu exists, otherwise `false`.
|
||||
* @param {string} menuName - Name of the menu to check for existence.
|
||||
* @returns {boolean} <code>true</code> if the menu exists, otherwise <code>false</code>.
|
||||
* @example <caption>Check if the "Developer" menu exists.</caption>
|
||||
* if (Menu.menuExists("Developer")) {
|
||||
* print("Developer menu exists.");
|
||||
* }
|
||||
*/
|
||||
bool menuExists(const QString& menuName);
|
||||
|
||||
/**jsdoc
|
||||
* Add a separator with an unclickable label below it.
|
||||
* The line will be placed at the bottom of the menu.
|
||||
* Add a separator with an unclickable label below it. The separator will be placed at the bottom of the menu.
|
||||
* If you want to add a separator at a specific point in the menu, use {@link Menu.addMenuItem} with
|
||||
* {@link Menu.MenuItemProperties} instead.
|
||||
* @function Menu.addSeparator
|
||||
* @param {string} menuName Name of the menu to add a separator to.
|
||||
* @param {string} separatorName Name of the separator that will be shown (but unclickable) below the separator line.
|
||||
* @param {string} menuName - Name of the menu to add a separator to.
|
||||
* @param {string} separatorName - Name of the separator that will be displayed as the label below the separator line.
|
||||
* @example <caption>Add a separator.</caption>
|
||||
* Menu.addSeparator("Developer","Test Separator");
|
||||
*/
|
||||
void addSeparator(const QString& menuName, const QString& separatorName);
|
||||
|
||||
/**jsdoc
|
||||
* Remove a separator and its label from a menu.
|
||||
* Remove a separator from a menu.
|
||||
* @function Menu.removeSeparator
|
||||
* @param {string} menuName Name of the menu to remove a separator from.
|
||||
* @param {string} separatorName Name of the separator to remove.
|
||||
* @param {string} menuName - Name of the menu to remove the separator from.
|
||||
* @param {string} separatorName - Name of the separator to remove.
|
||||
* @example <caption>Remove a separator.</caption>
|
||||
* Menu.removeSeparator("Developer","Test Separator");
|
||||
*/
|
||||
void removeSeparator(const QString& menuName, const QString& separatorName);
|
||||
|
||||
/**jsdoc
|
||||
* Add a new menu item to a menu.
|
||||
* @function Menu.addMenuItem
|
||||
* @param {Menu.MenuItemProperties} properties
|
||||
* @param {Menu.MenuItemProperties} properties - Properties of the menu item to create.
|
||||
* @example <caption>Add a menu item using {@link Menu.MenuItemProperties}.</caption>
|
||||
* Menu.addMenuItem({
|
||||
* menuName: "Developer",
|
||||
* menuItemName: "Test",
|
||||
* afterItem: "Log",
|
||||
* shortcutKey: "Ctrl+Shift+T",
|
||||
* grouping: "Advanced"
|
||||
* });
|
||||
*/
|
||||
void addMenuItem(const MenuItemProperties& properties);
|
||||
|
||||
/**jsdoc
|
||||
* Add a new menu item to a menu.
|
||||
* Add a new menu item to a menu. The new item is added at the end of the menu.
|
||||
* @function Menu.addMenuItem
|
||||
* @param {string} menuName Name of the menu to add a menu item to.
|
||||
* @param {string} menuItem Name of the menu item. This is what will be displayed in the menu.
|
||||
* @param {string} shortcutKey A shortcut key that can be used to trigger the menu item.
|
||||
* @param {string} menuName - Name of the menu to add a menu item to.
|
||||
* @param {string} menuItem - Name of the menu item. This is what will be displayed in the menu.
|
||||
* @param {string} [shortcutKey] A shortcut key that can be used to trigger the menu item.
|
||||
* @example <caption>Add a menu item to the end of the "Developer" menu.</caption>
|
||||
* Menu.addMenuItem("Developer", "Test", "Ctrl+Shift+T");
|
||||
*/
|
||||
void addMenuItem(const QString& menuName, const QString& menuitem, const QString& shortcutKey);
|
||||
|
||||
/**jsdoc
|
||||
* Add a new menu item to a menu.
|
||||
* @function Menu.addMenuItem
|
||||
* @param {string} menuName Name of the menu to add a menu item to.
|
||||
* @param {string} menuItem Name of the menu item. This is what will be displayed in the menu.
|
||||
*/
|
||||
void addMenuItem(const QString& menuName, const QString& menuitem);
|
||||
|
||||
/**jsdoc
|
||||
* Remove a menu item from a menu.
|
||||
* @function Menu.removeMenuItem
|
||||
* @param {string} menuName Name of the menu to remove a menu item from.
|
||||
* @param {string} menuItem Name of the menu item to remove.
|
||||
* @param {string} menuName - Name of the menu to remove a menu item from.
|
||||
* @param {string} menuItem - Name of the menu item to remove.
|
||||
* Menu.removeMenuItem("Developer", "Test");
|
||||
*/
|
||||
void removeMenuItem(const QString& menuName, const QString& menuitem);
|
||||
|
||||
/**jsdoc
|
||||
* Check if a menu item exists.
|
||||
* @function Menu.menuItemExists
|
||||
* @param {string} menuName Name of the menu that the menu item is in.
|
||||
* @param {string} menuItem Name of the menu item to check for existence of.
|
||||
* @return {bool} `true` if the menu item exists, otherwise `false`.
|
||||
* @param {string} menuName - Name of the menu that the menu item is in.
|
||||
* @param {string} menuItem - Name of the menu item to check for existence of.
|
||||
* @returns {boolean} <code>true</code> if the menu item exists, otherwise <code>false</code>.
|
||||
* @example <caption>Determine if the Developer > Stats menu exists.</caption>
|
||||
* if (Menu.menuItemExists("Developer", "Stats")) {
|
||||
* print("Developer > Stats menu item exists.");
|
||||
* }
|
||||
*/
|
||||
bool menuItemExists(const QString& menuName, const QString& menuitem);
|
||||
|
||||
/**
|
||||
* Not working, will not document until fixed
|
||||
* TODO: Not working; don't document until fixed.
|
||||
*/
|
||||
void addActionGroup(const QString& groupName, const QStringList& actionList,
|
||||
const QString& selected = QString());
|
||||
|
@ -143,53 +173,73 @@ public slots:
|
|||
/**jsdoc
|
||||
* Check whether a checkable menu item is checked.
|
||||
* @function Menu.isOptionChecked
|
||||
* @param {string} menuOption The name of the menu item.
|
||||
* @return `true` if the option is checked, otherwise false.
|
||||
* @param {string} menuOption - The name of the menu item.
|
||||
* @returns {boolean} <code>true</code> if the option is checked, otherwise <code>false</code>.
|
||||
* @example <caption>Report whether the Settings > Advanced menu item is turned on.</caption>
|
||||
* print(Menu.isOptionChecked("Advanced Menus")); // true or false
|
||||
*/
|
||||
bool isOptionChecked(const QString& menuOption);
|
||||
|
||||
/**jsdoc
|
||||
* Set a checkable menu item as checked or unchecked.
|
||||
* @function Menu.setIsOptionChecked
|
||||
* @param {string} menuOption The name of the menu item to modify.
|
||||
* @param {bool} isChecked If `true`, the menu item will be checked, otherwise it will not be checked.
|
||||
* @param {string} menuOption - The name of the menu item to modify.
|
||||
* @param {boolean} isChecked - If <code>true</code>, the menu item will be checked, otherwise it will not be checked.
|
||||
* @example <caption>Turn on Settings > Advanced Menus.</caption>
|
||||
* Menu.setIsOptionChecked("Advanced Menus", true);
|
||||
* print(Menu.isOptionChecked("Advanced Menus")); // true
|
||||
*/
|
||||
void setIsOptionChecked(const QString& menuOption, bool isChecked);
|
||||
|
||||
/**jsdoc
|
||||
* Toggle the status of a checkable menu item. If it is checked, it will be unchecked.
|
||||
* If it is unchecked, it will be checked.
|
||||
* @function Menu.setIsOptionChecked
|
||||
* @param {string} menuOption The name of the menu item to toggle.
|
||||
* Trigger the menu item as if the user clicked on it.
|
||||
* @function Menu.triggerOption
|
||||
* @param {string} menuOption - The name of the menu item to trigger.
|
||||
* @example <caption>Open the help window.</caption>
|
||||
* Menu.triggerOption('Help...');
|
||||
*/
|
||||
void triggerOption(const QString& menuOption);
|
||||
|
||||
/**jsdoc
|
||||
* Check whether a menu is enabled. If a menu is disabled it will be greyed out
|
||||
* and unselectable.
|
||||
* Check whether a menu or menu item is enabled. If disabled, the item is grayed out and unusable.
|
||||
* Menus are enabled by default.
|
||||
* @function Menu.isMenuEnabled
|
||||
* @param {string} menuName The name of the menu to check.
|
||||
* @return {bool} `true` if the menu is enabled, otherwise false.
|
||||
* @param {string} menuName The name of the menu or menu item to check.
|
||||
* @returns {boolean} <code>true</code> if the menu is enabled, otherwise <code>false</code>.
|
||||
* @example <caption>Report with the Settings > Advanced Menus menu item is enabled.</caption>
|
||||
* print(Menu.isMenuEnabled("Settings > Advanced Menus")); // true or false
|
||||
*/
|
||||
bool isMenuEnabled(const QString& menuName);
|
||||
|
||||
/**jsdoc
|
||||
* Set a menu to be enabled or disabled.
|
||||
* Set a menu or menu item to be enabled or disabled. If disabled, the item is grayed out and unusable.
|
||||
* @function Menu.setMenuEnabled
|
||||
* @param {string} menuName The name of the menu to modify.
|
||||
* @param {bool} isEnabled Whether the menu will be enabled or not.
|
||||
* @param {string} menuName - The name of the menu or menu item to modify.
|
||||
* @param {boolean} isEnabled - If <code>true</code>, the menu will be enabled, otherwise it will be disabled.
|
||||
* @example <caption>Disable the Settings > Advanced Menus menu item.</caption>
|
||||
* Menu.setMenuEnabled("Settings > Advanced Menus", false);
|
||||
* print(Menu.isMenuEnabled("Settings > Advanced Menus")); // false
|
||||
*/
|
||||
void setMenuEnabled(const QString& menuName, bool isEnabled);
|
||||
|
||||
/**
|
||||
* TODO: Not used or useful; will not document until used.
|
||||
*/
|
||||
void closeInfoView(const QString& path);
|
||||
bool isInfoViewVisible(const QString& path);
|
||||
|
||||
signals:
|
||||
/**jsdoc
|
||||
* This is a signal that is emitted when a menu item is clicked.
|
||||
* Triggered when a menu item is clicked (or triggered by {@link Menu.triggerOption}).
|
||||
* @function Menu.menuItemEvent
|
||||
* @param {string} menuItem Name of the menu item that was triggered.
|
||||
* @param {string} menuItem - Name of the menu item that was clicked.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Detect menu item events.</caption>
|
||||
* function onMenuItemEvent(menuItem) {
|
||||
* print("You clicked on " + menuItem);
|
||||
* }
|
||||
*
|
||||
* Menu.menuItemEvent.connect(onMenuItemEvent);
|
||||
*/
|
||||
void menuItemEvent(const QString& menuItem);
|
||||
};
|
||||
|
|
|
@ -11,11 +11,12 @@
|
|||
#include <QtCore/QLoggingCategory>
|
||||
#include <QtCore/QThread>
|
||||
|
||||
#include <shared/FileUtils.h>
|
||||
#include <shared/QtHelpers.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <Trace.h>
|
||||
#include <StatTracker.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <StatTracker.h>
|
||||
#include <Trace.h>
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
|
@ -141,6 +142,15 @@ void TestScriptingInterface::endTraceEvent(QString name) {
|
|||
tracing::traceEvent(trace_test(), name, tracing::DurationEnd);
|
||||
}
|
||||
|
||||
void TestScriptingInterface::savePhysicsSimulationStats(QString originalPath) {
|
||||
QString path = FileUtils::replaceDateTimeTokens(originalPath);
|
||||
path = FileUtils::computeDocumentPath(path);
|
||||
if (!FileUtils::canCreateFile(path)) {
|
||||
return;
|
||||
}
|
||||
qApp->saveNextPhysicsStats(path);
|
||||
}
|
||||
|
||||
void TestScriptingInterface::profileRange(const QString& name, QScriptValue fn) {
|
||||
PROFILE_RANGE(script, name);
|
||||
fn.call();
|
||||
|
|
|
@ -71,6 +71,11 @@ public slots:
|
|||
|
||||
void endTraceEvent(QString name);
|
||||
|
||||
/**jsdoc
|
||||
* Write detailed timing stats of next physics stepSimulation() to filename
|
||||
*/
|
||||
void savePhysicsSimulationStats(QString filename);
|
||||
|
||||
Q_INVOKABLE void profileRange(const QString& name, QScriptValue function);
|
||||
|
||||
private:
|
||||
|
|
|
@ -22,3 +22,8 @@ void WalletScriptingInterface::refreshWalletStatus() {
|
|||
auto wallet = DependencyManager::get<Wallet>();
|
||||
wallet->getWalletStatus();
|
||||
}
|
||||
|
||||
void WalletScriptingInterface::setWalletStatus(const uint& status) {
|
||||
_walletStatus = status;
|
||||
emit DependencyManager::get<Wallet>()->walletStatusResult(status);
|
||||
}
|
|
@ -39,7 +39,9 @@ public:
|
|||
|
||||
Q_INVOKABLE void refreshWalletStatus();
|
||||
Q_INVOKABLE uint getWalletStatus() { return _walletStatus; }
|
||||
void setWalletStatus(const uint& status) { _walletStatus = status; }
|
||||
// setWalletStatus() should never be made Q_INVOKABLE. If it were,
|
||||
// scripts could cause the Wallet to incorrectly report its status.
|
||||
void setWalletStatus(const uint& status);
|
||||
|
||||
signals:
|
||||
void walletStatusChanged();
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
bool forwardEnabled() { return _forwardEnabled; }
|
||||
bool useFeed() { return _useFeed; }
|
||||
void setUseFeed(bool useFeed) { if (_useFeed != useFeed) { _useFeed = useFeed; emit useFeedChanged(); } }
|
||||
QString metaverseServerUrl() { return NetworkingConstants::METAVERSE_SERVER_URL.toString(); }
|
||||
QString metaverseServerUrl() { return NetworkingConstants::METAVERSE_SERVER_URL().toString(); }
|
||||
|
||||
signals:
|
||||
void backEnabledChanged();
|
||||
|
|
|
@ -78,6 +78,8 @@ bool Stats::includeTimingRecord(const QString& name) {
|
|||
return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming);
|
||||
} else if (name.startsWith("/paintGL/")) {
|
||||
return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming);
|
||||
} else if (name.startsWith("step/")) {
|
||||
return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -200,6 +200,31 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
|||
}
|
||||
}
|
||||
|
||||
// JSDoc for copying to @typedefs of overlay types that inherit Base3DOverlay.
|
||||
/**jsdoc
|
||||
* @property {string} name="" - A friendly name for the overlay.
|
||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*/
|
||||
QVariant Base3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "name") {
|
||||
return _name;
|
||||
|
|
|
@ -61,8 +61,8 @@ public:
|
|||
|
||||
void notifyRenderVariableChange() const;
|
||||
|
||||
void setProperties(const QVariantMap& properties) override;
|
||||
QVariant getProperty(const QString& property) override;
|
||||
virtual void setProperties(const QVariantMap& properties) override;
|
||||
virtual QVariant getProperty(const QString& property) override;
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal);
|
||||
|
|
|
@ -23,6 +23,11 @@ void Billboardable::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
}
|
||||
|
||||
// JSDoc for copying to @typedefs of overlay types that inherit Billboardable.
|
||||
/**jsdoc
|
||||
* @property {boolean} isFacingAvatar - If <code>true</code>, the overlay is rotated to face the user's camera about an axis
|
||||
* parallel to the user's avatar's "up" direction.
|
||||
*/
|
||||
QVariant Billboardable::getProperty(const QString &property) {
|
||||
if (property == "isFacingAvatar") {
|
||||
return isFacingAvatar();
|
||||
|
|
|
@ -16,10 +16,7 @@
|
|||
|
||||
QString const Circle3DOverlay::TYPE = "circle3d";
|
||||
|
||||
Circle3DOverlay::Circle3DOverlay() {
|
||||
memset(&_minorTickMarksColor, 0, sizeof(_minorTickMarksColor));
|
||||
memset(&_majorTickMarksColor, 0, sizeof(_majorTickMarksColor));
|
||||
}
|
||||
Circle3DOverlay::Circle3DOverlay() {}
|
||||
|
||||
Circle3DOverlay::Circle3DOverlay(const Circle3DOverlay* circle3DOverlay) :
|
||||
Planar3DOverlay(circle3DOverlay),
|
||||
|
@ -27,17 +24,21 @@ Circle3DOverlay::Circle3DOverlay(const Circle3DOverlay* circle3DOverlay) :
|
|||
_endAt(circle3DOverlay->_endAt),
|
||||
_outerRadius(circle3DOverlay->_outerRadius),
|
||||
_innerRadius(circle3DOverlay->_innerRadius),
|
||||
_innerStartColor(circle3DOverlay->_innerStartColor),
|
||||
_innerEndColor(circle3DOverlay->_innerEndColor),
|
||||
_outerStartColor(circle3DOverlay->_outerStartColor),
|
||||
_outerEndColor(circle3DOverlay->_outerEndColor),
|
||||
_innerStartAlpha(circle3DOverlay->_innerStartAlpha),
|
||||
_innerEndAlpha(circle3DOverlay->_innerEndAlpha),
|
||||
_outerStartAlpha(circle3DOverlay->_outerStartAlpha),
|
||||
_outerEndAlpha(circle3DOverlay->_outerEndAlpha),
|
||||
_hasTickMarks(circle3DOverlay->_hasTickMarks),
|
||||
_majorTickMarksAngle(circle3DOverlay->_majorTickMarksAngle),
|
||||
_minorTickMarksAngle(circle3DOverlay->_minorTickMarksAngle),
|
||||
_majorTickMarksLength(circle3DOverlay->_majorTickMarksLength),
|
||||
_minorTickMarksLength(circle3DOverlay->_minorTickMarksLength),
|
||||
_majorTickMarksColor(circle3DOverlay->_majorTickMarksColor),
|
||||
_minorTickMarksColor(circle3DOverlay->_minorTickMarksColor),
|
||||
_quadVerticesID(GeometryCache::UNKNOWN_ID),
|
||||
_lineVerticesID(GeometryCache::UNKNOWN_ID),
|
||||
_majorTicksVerticesID(GeometryCache::UNKNOWN_ID),
|
||||
_minorTicksVerticesID(GeometryCache::UNKNOWN_ID)
|
||||
_minorTickMarksColor(circle3DOverlay->_minorTickMarksColor)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -80,9 +81,8 @@ void Circle3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
Q_ASSERT(args->_batch);
|
||||
auto& batch = *args->_batch;
|
||||
if (args->_shapePipeline) {
|
||||
batch.setPipeline(args->_shapePipeline->pipeline);
|
||||
}
|
||||
|
||||
DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, isTransparent(), false, !getIsSolid(), true);
|
||||
|
||||
batch.setModelTransform(getRenderTransform());
|
||||
|
||||
|
@ -185,11 +185,10 @@ void Circle3DOverlay::render(RenderArgs* args) {
|
|||
// for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise
|
||||
// we just draw a line...
|
||||
if (getHasTickMarks()) {
|
||||
|
||||
if (_majorTicksVerticesID == GeometryCache::UNKNOWN_ID) {
|
||||
if (!_majorTicksVerticesID) {
|
||||
_majorTicksVerticesID = geometryCache->allocateID();
|
||||
}
|
||||
if (_minorTicksVerticesID == GeometryCache::UNKNOWN_ID) {
|
||||
if (!_minorTicksVerticesID) {
|
||||
_minorTicksVerticesID = geometryCache->allocateID();
|
||||
}
|
||||
|
||||
|
@ -368,6 +367,98 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
}
|
||||
|
||||
// Overlay's color and alpha properties are overridden. And the dimensions property is not used.
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>circle3d</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.Circle3DProperties
|
||||
*
|
||||
* @property {string} type=circle3d - Has the value <code>"circle3d"</code>. <em>Read-only.</em>
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
||||
* rotating as you move your avatar.
|
||||
*
|
||||
* @property {string} name="" - A friendly name for the overlay.
|
||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
* <em>Not used.</em>
|
||||
*
|
||||
* @property {number} startAt=0 - The counter-clockwise angle from the overlay's x-axis that drawing starts at, in degrees.
|
||||
* @property {number} endAt=360 - The counter-clockwise angle from the overlay's x-axis that drawing ends at, in degrees.
|
||||
* @property {number} outerRadius=1 - The outer radius of the overlay, in meters. Synonym: <code>radius</code>.
|
||||
* @property {number} innerRadius=0 - The inner radius of the overlay, in meters.
|
||||
* @property {Color} color=255,255,255 - The color of the overlay. Setting this value also sets the values of
|
||||
* <code>innerStartColor</code>, <code>innerEndColor</code>, <code>outerStartColor</code>, and <code>outerEndColor</code>.
|
||||
* @property {Color} startColor - Sets the values of <code>innerStartColor</code> and <code>outerStartColor</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {Color} endColor - Sets the values of <code>innerEndColor</code> and <code>outerEndColor</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {Color} innerColor - Sets the values of <code>innerStartColor</code> and <code>innerEndColor</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {Color} outerColor - Sets the values of <code>outerStartColor</code> and <code>outerEndColor</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {Color} innerStartcolor - The color at the inner start point of the overlay. <em>Write-only.</em>
|
||||
* @property {Color} innerEndColor - The color at the inner end point of the overlay. <em>Write-only.</em>
|
||||
* @property {Color} outerStartColor - The color at the outer start point of the overlay. <em>Write-only.</em>
|
||||
* @property {Color} outerEndColor - The color at the outer end point of the overlay. <em>Write-only.</em>
|
||||
* @property {number} alpha=0.5 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>. Setting this value also sets
|
||||
* the values of <code>innerStartAlpha</code>, <code>innerEndAlpha</code>, <code>outerStartAlpha</code>, and
|
||||
* <code>outerEndAlpha</code>. Synonym: <code>Alpha</code>; <em>write-only</em>.
|
||||
* @property {number} startAlpha - Sets the values of <code>innerStartAlpha</code> and <code>outerStartAlpha</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {number} endAlpha - Sets the values of <code>innerEndAlpha</code> and <code>outerEndAlpha</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {number} innerAlpha - Sets the values of <code>innerStartAlpha</code> and <code>innerEndAlpha</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {number} outerAlpha - Sets the values of <code>outerStartAlpha</code> and <code>outerEndAlpha</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {number} innerStartAlpha=0 - The alpha at the inner start point of the overlay. <em>Write-only.</em>
|
||||
* @property {number} innerEndAlpha=0 - The alpha at the inner end point of the overlay. <em>Write-only.</em>
|
||||
* @property {number} outerStartAlpha=0 - The alpha at the outer start point of the overlay. <em>Write-only.</em>
|
||||
* @property {number} outerEndAlpha=0 - The alpha at the outer end point of the overlay. <em>Write-only.</em>
|
||||
|
||||
* @property {boolean} hasTickMarks=false - If <code>true</code>, tick marks are drawn.
|
||||
* @property {number} majorTickMarksAngle=0 - The angle between major tick marks, in degrees.
|
||||
* @property {number} minorTickMarksAngle=0 - The angle between minor tick marks, in degrees.
|
||||
* @property {number} majorTickMarksLength=0 - The length of the major tick marks, in meters. A positive value draws tick marks
|
||||
* outwards from the inner radius; a negative value draws tick marks inwards from the outer radius.
|
||||
* @property {number} minorTickMarksLength=0 - The length of the minor tick marks, in meters. A positive value draws tick marks
|
||||
* outwards from the inner radius; a negative value draws tick marks inwards from the outer radius.
|
||||
* @property {Color} majorTickMarksColor=0,0,0 - The color of the major tick marks.
|
||||
* @property {Color} minorTickMarksColor=0,0,0 - The color of the minor tick marks.
|
||||
*/
|
||||
QVariant Circle3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "startAt") {
|
||||
return _startAt;
|
||||
|
@ -384,6 +475,30 @@ QVariant Circle3DOverlay::getProperty(const QString& property) {
|
|||
if (property == "innerRadius") {
|
||||
return _innerRadius;
|
||||
}
|
||||
if (property == "innerStartColor") {
|
||||
return xColorToVariant(_innerStartColor);
|
||||
}
|
||||
if (property == "innerEndColor") {
|
||||
return xColorToVariant(_innerEndColor);
|
||||
}
|
||||
if (property == "outerStartColor") {
|
||||
return xColorToVariant(_outerStartColor);
|
||||
}
|
||||
if (property == "outerEndColor") {
|
||||
return xColorToVariant(_outerEndColor);
|
||||
}
|
||||
if (property == "innerStartAlpha") {
|
||||
return _innerStartAlpha;
|
||||
}
|
||||
if (property == "innerEndAlpha") {
|
||||
return _innerEndAlpha;
|
||||
}
|
||||
if (property == "outerStartAlpha") {
|
||||
return _outerStartAlpha;
|
||||
}
|
||||
if (property == "outerEndAlpha") {
|
||||
return _outerEndAlpha;
|
||||
}
|
||||
if (property == "hasTickMarks") {
|
||||
return _hasTickMarks;
|
||||
}
|
||||
|
@ -409,7 +524,6 @@ QVariant Circle3DOverlay::getProperty(const QString& property) {
|
|||
return Planar3DOverlay::getProperty(property);
|
||||
}
|
||||
|
||||
|
||||
bool Circle3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal) {
|
||||
|
||||
|
|
|
@ -65,22 +65,22 @@ protected:
|
|||
float _outerRadius { 1 };
|
||||
float _innerRadius { 0 };
|
||||
|
||||
xColor _innerStartColor;
|
||||
xColor _innerEndColor;
|
||||
xColor _outerStartColor;
|
||||
xColor _outerEndColor;
|
||||
float _innerStartAlpha;
|
||||
float _innerEndAlpha;
|
||||
float _outerStartAlpha;
|
||||
float _outerEndAlpha;
|
||||
xColor _innerStartColor { DEFAULT_OVERLAY_COLOR };
|
||||
xColor _innerEndColor { DEFAULT_OVERLAY_COLOR };
|
||||
xColor _outerStartColor { DEFAULT_OVERLAY_COLOR };
|
||||
xColor _outerEndColor { DEFAULT_OVERLAY_COLOR };
|
||||
float _innerStartAlpha { DEFAULT_ALPHA };
|
||||
float _innerEndAlpha { DEFAULT_ALPHA };
|
||||
float _outerStartAlpha { DEFAULT_ALPHA };
|
||||
float _outerEndAlpha { DEFAULT_ALPHA };
|
||||
|
||||
bool _hasTickMarks { false };
|
||||
float _majorTickMarksAngle { 0 };
|
||||
float _minorTickMarksAngle { 0 };
|
||||
float _majorTickMarksLength { 0 };
|
||||
float _minorTickMarksLength { 0 };
|
||||
xColor _majorTickMarksColor;
|
||||
xColor _minorTickMarksColor;
|
||||
xColor _majorTickMarksColor { DEFAULT_OVERLAY_COLOR };
|
||||
xColor _minorTickMarksColor { DEFAULT_OVERLAY_COLOR };
|
||||
gpu::Primitive _solidPrimitive { gpu::TRIANGLE_FAN };
|
||||
int _quadVerticesID { 0 };
|
||||
int _lineVerticesID { 0 };
|
||||
|
|
|
@ -289,7 +289,7 @@ void ContextOverlayInterface::openInspectionCertificate() {
|
|||
QNetworkRequest networkRequest;
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL;
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
|
||||
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer");
|
||||
QJsonObject request;
|
||||
request["certificate_id"] = entityProperties.getCertificateID();
|
||||
|
@ -359,7 +359,7 @@ void ContextOverlayInterface::openInspectionCertificate() {
|
|||
}
|
||||
}
|
||||
|
||||
static const QString MARKETPLACE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/marketplace/items/";
|
||||
static const QString MARKETPLACE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace/items/";
|
||||
|
||||
void ContextOverlayInterface::openMarketplace() {
|
||||
// lets open the tablet and go to the current item in
|
||||
|
|
|
@ -134,6 +134,56 @@ void Cube3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>cube</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.CubeProperties
|
||||
*
|
||||
* @property {string} type=cube - Has the value <code>"cube"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
||||
* rotating as you move your avatar.
|
||||
*
|
||||
* @property {string} name="" - A friendly name for the overlay.
|
||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
*
|
||||
* @property {number} borderSize - Not used.
|
||||
*/
|
||||
QVariant Cube3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "borderSize") {
|
||||
return _borderSize;
|
||||
|
|
|
@ -112,6 +112,61 @@ void Grid3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
updateGrid();
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>grid</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.GridProperties
|
||||
*
|
||||
* @property {string} type=grid - Has the value <code>"grid"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
||||
* rotating as you move your avatar.
|
||||
*
|
||||
* @property {string} name="" - A friendly name for the overlay.
|
||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
*
|
||||
* @property {boolean} followCamera=true - If <code>true</code>, the grid is always visible even as the camera moves to another
|
||||
* position.
|
||||
* @property {number} majorGridEvery=5 - Integer number of <code>minorGridEvery</code> intervals at which to draw a thick grid
|
||||
* line. Minimum value = <code>1</code>.
|
||||
* @property {number} minorGridEvery=1 - Real number of meters at which to draw thin grid lines. Minimum value =
|
||||
* <code>0.001</code>.
|
||||
*/
|
||||
QVariant Grid3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "followCamera") {
|
||||
return _followCamera;
|
||||
|
|
|
@ -188,6 +188,62 @@ void Image3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* These are the properties of an <code>image3d</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.Image3DProperties
|
||||
*
|
||||
* @property {string} type=image3d - Has the value <code>"image3d"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
||||
* rotating as you move your avatar.
|
||||
*
|
||||
* @property {string} name="" - A friendly name for the overlay.
|
||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
*
|
||||
* @property {boolean} isFacingAvatar - If <code>true</code>, the overlay is rotated to face the user's camera about an axis
|
||||
* parallel to the user's avatar's "up" direction.
|
||||
*
|
||||
* @property {string} url - The URL of the PNG or JPG image to display.
|
||||
* @property {Rect} subImage - The portion of the image to display. Defaults to the full image.
|
||||
* @property {boolean} emissive - If <code>true</code>, the overlay is displayed at full brightness, otherwise it is rendered
|
||||
* with scene lighting.
|
||||
*/
|
||||
QVariant Image3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "url") {
|
||||
return _url;
|
||||
|
|
|
@ -19,6 +19,29 @@
|
|||
QString const ImageOverlay::TYPE = "image";
|
||||
QUrl const ImageOverlay::URL(QString("hifi/overlays/ImageOverlay.qml"));
|
||||
|
||||
// ImageOverlay's properties are defined in the QML file specified above.
|
||||
/**jsdoc
|
||||
* These are the properties of an <code>image</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.ImageProperties
|
||||
*
|
||||
* @property {Rect} bounds - The position and size of the image display area, in pixels. <em>Write-only.</em>
|
||||
* @property {number} x - Integer left, x-coordinate value of the image display area = <code>bounds.x</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {number} y - Integer top, y-coordinate value of the image display area = <code>bounds.y</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {number} width - Integer width of the image display area = <code>bounds.width</code>. <em>Write-only.</em>
|
||||
* @property {number} height - Integer height of the image display area = <code>bounds.height</code>. <em>Write-only.</em>
|
||||
* @property {string} imageURL - The URL of the image file to display. The image is scaled to fit to the <code>bounds</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {Vec2} subImage=0,0 - Integer coordinates of the top left pixel to start using image content from.
|
||||
* <em>Write-only.</em>
|
||||
* @property {Color} color=0,0,0 - The color to apply over the top of the image to colorize it. <em>Write-only.</em>
|
||||
* @property {number} alpha=0.0 - The opacity of the color applied over the top of the image, <code>0.0</code> -
|
||||
* <code>1.0</code>. <em>Write-only.</em>
|
||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||
* <em>Write-only.</em>
|
||||
*/
|
||||
|
||||
ImageOverlay::ImageOverlay()
|
||||
: QmlOverlay(URL) { }
|
||||
|
||||
|
|
|
@ -255,6 +255,67 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
|||
}
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>line3d</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.Line3DProperties
|
||||
*
|
||||
* @property {string} type=line3d - Has the value <code>"line3d"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
||||
* rotating as you move your avatar.
|
||||
*
|
||||
* @property {string} name="" - A friendly name for the overlay.
|
||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Uuid} endParentID=null - The avatar, entity, or overlay that the end point of the line is parented to.
|
||||
* @property {number} endParentJointIndex=65535 - Integer value specifying the skeleton joint that the end point of the line is
|
||||
* attached to if <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
* @property {Vec3} start - The start point of the line. Synonyms: <code>startPoint</code>, <code>p1</code>, and
|
||||
* <code>position</code>.
|
||||
* @property {Vec3} end - The end point of the line. Synonyms: <code>endPoint</code> and <code>p2</code>.
|
||||
* @property {Vec3} localStart - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>start</code>. Synonym: <code>localPosition</code>.
|
||||
* @property {Vec3} localEnd - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>endParentID</code> set, otherwise the same value as <code>end</code>.
|
||||
* @property {number} length - The length of the line, in meters. This can be set after creating a line with start and end
|
||||
* points.
|
||||
* @property {number} glow=0 - If <code>glow > 0</code>, the line is rendered with a glow.
|
||||
* @property {number} lineWidth=0.02 - If <code>glow > 0</code>, this is the width of the glow, in meters.
|
||||
*/
|
||||
QVariant Line3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "start" || property == "startPoint" || property == "p1") {
|
||||
return vec3toVariant(getStart());
|
||||
|
|
|
@ -152,7 +152,7 @@ void ModelOverlay::setProperties(const QVariantMap& properties) {
|
|||
_scaleToFit = true;
|
||||
setDimensions(vec3FromVariant(dimensions));
|
||||
} else if (scale.isValid()) {
|
||||
// if "scale" property is set but "dimentions" is not.
|
||||
// if "scale" property is set but "dimensions" is not.
|
||||
// do NOT scale to fit.
|
||||
_scaleToFit = false;
|
||||
}
|
||||
|
@ -181,6 +181,10 @@ void ModelOverlay::setProperties(const QVariantMap& properties) {
|
|||
Q_ARG(const QVariantMap&, textureMap));
|
||||
}
|
||||
|
||||
// jointNames is read-only.
|
||||
// jointPositions is read-only.
|
||||
// jointOrientations is read-only.
|
||||
|
||||
// relative
|
||||
auto jointTranslationsValue = properties["jointTranslations"];
|
||||
if (jointTranslationsValue.canConvert(QVariant::List)) {
|
||||
|
@ -276,6 +280,75 @@ vectorType ModelOverlay::mapJoints(mapFunction<itemType> function) const {
|
|||
return result;
|
||||
}
|
||||
|
||||
// Note: ModelOverlay overrides Volume3DOverlay's "dimensions" and "scale" properties.
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>model</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.ModelProperties
|
||||
*
|
||||
* @property {string} type=sphere - Has the value <code>"model"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
||||
* rotating as you move your avatar.
|
||||
*
|
||||
* @property {string} name="" - A friendly name for the overlay.
|
||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {string} url - The URL of the FBX or OBJ model used for the overlay.
|
||||
* @property {Vec3} dimensions - The dimensions of the overlay. Synonym: <code>size</code>.
|
||||
* @property {Vec3} scale - The scale factor applied to the model's dimensions.
|
||||
* @property {object.<name, url>} textures - Maps the named textures in the model to the JPG or PNG images in the urls.
|
||||
* @property {Array.<string>} jointNames - The names of the joints - if any - in the model. <em>Read-only</em>
|
||||
* @property {Array.<Quat>} jointRotations - The relative rotations of the model's joints.
|
||||
* @property {Array.<Vec3>} jointTranslations - The relative translations of the model's joints.
|
||||
* @property {Array.<Quat>} jointOrientations - The absolute orientations of the model's joints, in world coordinates.
|
||||
* <em>Read-only</em>
|
||||
* @property {Array.<Vec3>} jointPositions - The absolute positions of the model's joints, in world coordinates.
|
||||
* <em>Read-only</em>
|
||||
* @property {string} animationSettings.url="" - The URL of an FBX file containing an animation to play.
|
||||
* @property {number} animationSettings.fps=0 - The frame rate (frames/sec) to play the animation at.
|
||||
* @property {number} animationSettings.firstFrame=0 - The frame to start playing at.
|
||||
* @property {number} animationSettings.lastFrame=0 - The frame to finish playing at.
|
||||
* @property {boolean} animationSettings.running=false - Whether or not the animation is playing.
|
||||
* @property {boolean} animationSettings.loop=false - Whether or not the animation should repeat in a loop.
|
||||
* @property {boolean} animationSettings.hold=false - Whether or not when the animation finishes, the rotations and
|
||||
* translations of the last frame played should be maintained.
|
||||
* @property {boolean} animationSettings.allowTranslation=false - Whether or not translations contained in the animation should
|
||||
* be played.
|
||||
*/
|
||||
QVariant ModelOverlay::getProperty(const QString& property) {
|
||||
if (property == "url") {
|
||||
return _url.toString();
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
|
||||
#include "Application.h"
|
||||
|
||||
static const xColor DEFAULT_OVERLAY_COLOR = { 255, 255, 255 };
|
||||
static const float DEFAULT_ALPHA = 0.7f;
|
||||
const xColor Overlay::DEFAULT_OVERLAY_COLOR = { 255, 255, 255 };
|
||||
const float Overlay::DEFAULT_ALPHA = 0.7f;
|
||||
|
||||
Overlay::Overlay() :
|
||||
_renderItemID(render::Item::INVALID_ITEM_ID),
|
||||
|
@ -101,6 +101,27 @@ void Overlay::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
}
|
||||
|
||||
// JSDoc for copying to @typedefs of overlay types that inherit Overlay.
|
||||
/**jsdoc
|
||||
* @property {string} type=TODO - Has the value <code>"TODO"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
||||
* rotating as you move your avatar.
|
||||
*/
|
||||
QVariant Overlay::getProperty(const QString& property) {
|
||||
if (property == "type") {
|
||||
return QVariant(getType());
|
||||
|
|
|
@ -122,6 +122,9 @@ protected:
|
|||
|
||||
unsigned int _stackOrder { 0 };
|
||||
|
||||
static const xColor DEFAULT_OVERLAY_COLOR;
|
||||
static const float DEFAULT_ALPHA;
|
||||
|
||||
private:
|
||||
OverlayID _overlayID; // only used for non-3d overlays
|
||||
};
|
||||
|
|
|
@ -23,6 +23,15 @@ AABox Overlay2D::getBounds() const {
|
|||
glm::vec3(_bounds.width(), _bounds.height(), 0.01f));
|
||||
}
|
||||
|
||||
// JSDoc for copying to @typedefs of overlay types that inherit Overlay2D.
|
||||
// QmlOverlay-derived classes don't support getProperty().
|
||||
/**jsdoc
|
||||
* @property {Rect} bounds - The position and size of the rectangle. <em>Write-only.</em>
|
||||
* @property {number} x - Integer left, x-coordinate value = <code>bounds.x</code>. <em>Write-only.</em>
|
||||
* @property {number} y - Integer top, y-coordinate value = <code>bounds.y</code>. <em>Write-only.</em>
|
||||
* @property {number} width - Integer width of the rectangle = <code>bounds.width</code>. <em>Write-only.</em>
|
||||
* @property {number} height - Integer height of the rectangle = <code>bounds.height</code>. <em>Write-only.</em>
|
||||
*/
|
||||
void Overlay2D::setProperties(const QVariantMap& properties) {
|
||||
Overlay::setProperties(properties);
|
||||
|
||||
|
|
|
@ -172,6 +172,63 @@ OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties)
|
|||
|
||||
Overlay::Pointer thisOverlay = nullptr;
|
||||
|
||||
/**jsdoc
|
||||
* <p>An overlay may be one of the following types:</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>2D/3D</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>circle3d</code></td><td>3D</td><td>A circle.</td></tr>
|
||||
* <tr><td><code>cube</code></td><td>3D</td><td>A cube. Can also use a <code>shape</code> overlay to create a
|
||||
* cube.</td></tr>
|
||||
* <tr><td><code>grid</code></td><td>3D</td><td>A grid of lines in a plane.</td></tr>
|
||||
* <tr><td><code>image</code></td><td>2D</td><td>An image. Synonym: <code>billboard</code>.</td></tr>
|
||||
* <tr><td><code>image3d</code></td><td>3D</td><td>An image.</td></tr>
|
||||
* <tr><td><code>line3d</code></td><td>3D</td><td>A line.</td></tr>
|
||||
* <tr><td><code>model</code></td><td>3D</td><td>A model.</td></tr>
|
||||
* <tr><td><code>rectangle</code></td><td>2D</td><td>A rectangle.</td></tr>
|
||||
* <tr><td><code>rectangle3d</code></td><td>3D</td><td>A rectangle.</td></tr>
|
||||
* <tr><td><code>shape</code></td><td>3D</td><td>A geometric shape, such as a cube, sphere, or cylinder.</td></tr>
|
||||
* <tr><td><code>sphere</code></td><td>3D</td><td>A sphere. Can also use a <code>shape</code> overlay to create a
|
||||
* sphere.</td></tr>
|
||||
* <tr><td><code>text</code></td><td>2D</td><td>Text.</td></tr>
|
||||
* <tr><td><code>text3d</code></td><td>3D</td><td>Text.</td></tr>
|
||||
* <tr><td><code>web3d</code></td><td>3D</td><td>Web content.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* <p>2D overlays are rendered on the display surface in desktop mode and on the HUD surface in HMD mode. 3D overlays are
|
||||
* rendered at a position and orientation in-world.<p>
|
||||
* <p>Each overlay type has different {@link Overlays.OverlayProperties|OverlayProperties}.</p>
|
||||
* @typedef {string} Overlays.OverlayType
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* <p>Different overlay types have different properties:</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>{@link Overlays.OverlayType|OverlayType}</th><th>Overlay Properties</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>circle3d</code></td><td>{@link Overlays.Circle3DProperties|Circle3DProperties}</td></tr>
|
||||
* <tr><td><code>cube</code></td><td>{@link Overlays.CubeProperties|CubeProperties}</td></tr>
|
||||
* <tr><td><code>grid</code></td><td>{@link Overlays.GridProperties|GridProperties}</td></tr>
|
||||
* <tr><td><code>image</code></td><td>{@link Overlays.ImageProperties|ImageProperties}</td></tr>
|
||||
* <tr><td><code>image3d</code></td><td>{@link Overlays.Image3DProperties|Image3DProperties}</td></tr>
|
||||
* <tr><td><code>line3d</code></td><td>{@link Overlays.Line3DProperties|Line3DProperties}</td></tr>
|
||||
* <tr><td><code>model</code></td><td>{@link Overlays.ModelProperties|ModelProperties}</td></tr>
|
||||
* <tr><td><code>rectangle</code></td><td>{@link Overlays.RectangleProperties|RectangleProperties}</td></tr>
|
||||
* <tr><td><code>rectangle3d</code></td><td>{@link Overlays.Rectangle3DProperties|Rectangle3DProperties}</td></tr>
|
||||
* <tr><td><code>shape</code></td><td>{@link Overlays.ShapeProperties|ShapeProperties}</td></tr>
|
||||
* <tr><td><code>sphere</code></td><td>{@link Overlays.SphereProperties|SphereProperties}</td></tr>
|
||||
* <tr><td><code>text</code></td><td>{@link Overlays.TextProperties|TextProperties}</td></tr>
|
||||
* <tr><td><code>text3d</code></td><td>{@link Overlays.Text3DProperties|Text3DProperties}</td></tr>
|
||||
* <tr><td><code>web3d</code></td><td>{@link Overlays.Web3DProperties|Web3DProperties}</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {object} Overlays.OverlayProperties
|
||||
*/
|
||||
|
||||
if (type == ImageOverlay::TYPE) {
|
||||
thisOverlay = Overlay::Pointer(new ImageOverlay(), [](Overlay* ptr) { ptr->deleteLater(); });
|
||||
} else if (type == Image3DOverlay::TYPE || type == "billboard") { // "billboard" for backwards compatibility
|
||||
|
|
|
@ -45,12 +45,13 @@ void OverlayPropertyResultFromScriptValue(const QScriptValue& object, OverlayPro
|
|||
const OverlayID UNKNOWN_OVERLAY_ID = OverlayID();
|
||||
|
||||
/**jsdoc
|
||||
* @typedef Overlays.RayToOverlayIntersectionResult
|
||||
* @property {bool} intersects True if the PickRay intersected with a 3D overlay.
|
||||
* @property {Overlays.OverlayID} overlayID The ID of the overlay that was intersected with.
|
||||
* @property {float} distance The distance from the PickRay origin to the intersection point.
|
||||
* @property {Vec3} surfaceNormal The normal of the surface that was intersected with.
|
||||
* @property {Vec3} intersection The point at which the PickRay intersected with the overlay.
|
||||
* @typedef {object} Overlays.RayToOverlayIntersectionResult
|
||||
* @property {boolean} intersects - <code>true</code> if the {@link PickRay} intersected with a 3D overlay, otherwise
|
||||
* <code>false</code>.
|
||||
* @property {Uuid} overlayID - The UUID of the overlay that was intersected.
|
||||
* @property {number} distance - The distance from the {@link PickRay} origin to the intersection point.
|
||||
* @property {Vec3} surfaceNormal - The normal of the overlay surface at the intersection point.
|
||||
* @property {Vec3} intersection - The position of the intersection point.
|
||||
*/
|
||||
class RayToOverlayIntersectionResult {
|
||||
public:
|
||||
|
@ -70,13 +71,11 @@ QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine,
|
|||
void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, RayToOverlayIntersectionResult& value);
|
||||
|
||||
/**jsdoc
|
||||
* @typedef {int} Overlays.OverlayID
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
*
|
||||
* Overlays namespace...
|
||||
* The Overlays API provides facilities to create and interact with overlays. Overlays are 2D and 3D objects visible only to
|
||||
* yourself and that aren't persisted to the domain. They are used for UI.
|
||||
* @namespace Overlays
|
||||
* @property {Uuid} keyboardFocusOverlay - Get or set the {@link Overlays.OverlayType|web3d} overlay that has keyboard focus.
|
||||
* If no overlay is set, get returns <code>null</code>; set to <code>null</code> to clear keyboard focus.
|
||||
*/
|
||||
|
||||
class Overlays : public QObject {
|
||||
|
@ -111,100 +110,246 @@ public:
|
|||
|
||||
public slots:
|
||||
/**jsdoc
|
||||
* Add an overlays to the scene. The properties specified will depend
|
||||
* on the type of overlay that is being created.
|
||||
*
|
||||
* Add an overlay to the scene.
|
||||
* @function Overlays.addOverlay
|
||||
* @param {string} type The type of the overlay to add.
|
||||
* @param {Overlays.OverlayProperties} The properties of the overlay that you want to add.
|
||||
* @return {Overlays.OverlayID} The ID of the newly created overlay.
|
||||
* @param {Overlays.OverlayType} type - The type of the overlay to add.
|
||||
* @param {Overlays.OverlayProperties} properties - The properties of the overlay to add.
|
||||
* @returns {Uuid} The ID of the newly created overlay.
|
||||
* @example <caption>Add a cube overlay in front of your avatar.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
*/
|
||||
OverlayID addOverlay(const QString& type, const QVariant& properties);
|
||||
|
||||
/**jsdoc
|
||||
* Create a clone of an existing overlay.
|
||||
*
|
||||
* @function Overlays.cloneOverlay
|
||||
* @param {Overlays.OverlayID} overlayID The ID of the overlay to clone.
|
||||
* @return {Overlays.OverlayID} The ID of the new overlay.
|
||||
* @param {Uuid} overlayID - The ID of the overlay to clone.
|
||||
* @returns {Uuid} The ID of the new overlay.
|
||||
* @example <caption>Add an overlay in front of your avatar, clone it, and move the clone to be above the
|
||||
* original.</caption>
|
||||
* var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 }));
|
||||
* var original = Overlays.addOverlay("cube", {
|
||||
* position: position,
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
*
|
||||
* var clone = Overlays.cloneOverlay(original);
|
||||
* Overlays.editOverlay(clone, {
|
||||
* position: Vec3.sum({ x: 0, y: 0.5, z: 0}, position)
|
||||
* });
|
||||
*/
|
||||
OverlayID cloneOverlay(OverlayID id);
|
||||
|
||||
/**jsdoc
|
||||
* Edit an overlay's properties.
|
||||
*
|
||||
* @function Overlays.editOverlay
|
||||
* @param {Overlays.OverlayID} overlayID The ID of the overlay to edit.
|
||||
* @return {bool} `true` if the overlay was found and edited, otherwise false.
|
||||
* @param {Uuid} overlayID - The ID of the overlay to edit.
|
||||
* @param {Overlays.OverlayProperties} properties - The properties changes to make.
|
||||
* @returns {boolean} <code>true</code> if the overlay was found and edited, otherwise <code>false</code>.
|
||||
* @example <caption>Add an overlay in front of your avatar then change its color.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
*
|
||||
* var success = Overlays.editOverlay(overlay, {
|
||||
* color: { red: 255, green: 0, blue: 0 }
|
||||
* });
|
||||
* print("Success: " + success);
|
||||
*/
|
||||
bool editOverlay(OverlayID id, const QVariant& properties);
|
||||
|
||||
/// edits an overlay updating only the included properties, will return the identified OverlayID in case of
|
||||
/// successful edit, if the input id is for an unknown overlay this function will have no effect
|
||||
/**jsdoc
|
||||
* Edit multiple overlays' properties.
|
||||
* @function Overlays.editOverlays
|
||||
* @param propertiesById {object.<Uuid, Overlays.OverlayProperties>} - An object with overlay IDs as keys and
|
||||
* {@link Overlays.OverlayProperties|OverlayProperties} edits to make as values.
|
||||
* @returns {boolean} <code>true</code> if all overlays were found and edited, otherwise <code>false</code> (some may have
|
||||
* been found and edited).
|
||||
* @example <caption>Create two overlays in front of your avatar then change their colors.</caption>
|
||||
* var overlayA = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: -0.3, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* var overlayB = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0.3, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
*
|
||||
* var overlayEdits = {};
|
||||
* overlayEdits[overlayA] = { color: { red: 255, green: 0, blue: 0 } };
|
||||
* overlayEdits[overlayB] = { color: { red: 0, green: 255, blue: 0 } };
|
||||
* var success = Overlays.editOverlays(overlayEdits);
|
||||
* print("Success: " + success);
|
||||
*/
|
||||
bool editOverlays(const QVariant& propertiesById);
|
||||
|
||||
/**jsdoc
|
||||
* Delete an overlay.
|
||||
*
|
||||
* @function Overlays.deleteOverlay
|
||||
* @param {Overlays.OverlayID} overlayID The ID of the overlay to delete.
|
||||
* @param {Uuid} overlayID - The ID of the overlay to delete.
|
||||
* @example <caption>Create an overlay in front of your avatar then delete it.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* print("Overlay: " + overlay);
|
||||
* Overlays.deleteOverlay(overlay);
|
||||
*/
|
||||
void deleteOverlay(OverlayID id);
|
||||
|
||||
/**jsdoc
|
||||
* Get the type of an overlay.
|
||||
*
|
||||
* @function Overlays.getOverlayType
|
||||
* @param {Overlays.OverlayID} overlayID The ID of the overlay to get the type of.
|
||||
* @return {string} The type of the overlay if found, otherwise the empty string.
|
||||
* @param {Uuid} overlayID - The ID of the overlay to get the type of.
|
||||
* @returns {Overlays.OverlayType} The type of the overlay if found, otherwise an empty string.
|
||||
* @example <caption>Create an overlay in front of your avatar then get and report its type.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* var type = Overlays.getOverlayType(overlay);
|
||||
* print("Type: " + type);
|
||||
*/
|
||||
QString getOverlayType(OverlayID overlayId);
|
||||
|
||||
/**jsdoc
|
||||
* Get the overlay Script object.
|
||||
*
|
||||
* @function Overlays.getOverlayObject
|
||||
* @param {Overlays.OverlayID} overlayID The ID of the overlay to get the script object of.
|
||||
* @return {Object} The script object for the overlay if found.
|
||||
*/
|
||||
* Get the overlay script object.
|
||||
* @function Overlays.getOverlayObject
|
||||
* @param {Uuid} overlayID - The ID of the overlay to get the script object of.
|
||||
* @returns {object} The script object for the overlay if found.
|
||||
*/
|
||||
QObject* getOverlayObject(OverlayID id);
|
||||
|
||||
/**jsdoc
|
||||
* Get the ID of the overlay at a particular point on the HUD/screen.
|
||||
*
|
||||
* Get the ID of the 2D overlay at a particular point on the screen or HUD.
|
||||
* @function Overlays.getOverlayAtPoint
|
||||
* @param {Vec2} point The point to check for an overlay.
|
||||
* @return {Overlays.OverlayID} The ID of the overlay at the point specified.
|
||||
* If no overlay is found, `0` will be returned.
|
||||
* @param {Vec2} point - The point to check for an overlay.
|
||||
* @returns {Uuid} The ID of the 2D overlay at the specified point if found, otherwise <code>null</code>.
|
||||
* @example <caption>Create a 2D overlay and add an event function that reports the overlay clicked on, if any.</caption>
|
||||
* var overlay = Overlays.addOverlay("rectangle", {
|
||||
* bounds: { x: 100, y: 100, width: 200, height: 100 },
|
||||
* color: { red: 255, green: 255, blue: 255 }
|
||||
* });
|
||||
* print("Created: " + overlay);
|
||||
*
|
||||
* Controller.mousePressEvent.connect(function (event) {
|
||||
* var overlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
|
||||
* print("Clicked: " + overlay);
|
||||
* });
|
||||
*/
|
||||
OverlayID getOverlayAtPoint(const glm::vec2& point);
|
||||
|
||||
/**jsdoc
|
||||
* Get the value of a an overlay's property.
|
||||
*
|
||||
* Get the value of a 3D overlay's property.
|
||||
* @function Overlays.getProperty
|
||||
* @param {Overlays.OverlayID} The ID of the overlay to get the property of.
|
||||
* @param {string} The name of the property to get the value of.
|
||||
* @return {Object} The value of the property. If the overlay or the property could
|
||||
* not be found, `undefined` will be returned.
|
||||
* @param {Uuid} overlayID - The ID of the overlay. <em>Must be for a 3D {@link Overlays.OverlayType|OverlayType}.</em>
|
||||
* @param {string} property - The name of the property value to get.
|
||||
* @returns {object} The value of the property if the 3D overlay and property can be found, otherwise
|
||||
* <code>undefined</code>.
|
||||
* @example <caption>Create an overlay in front of your avatar then report its alpha property value.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* var alpha = Overlays.getProperty(overlay, "alpha");
|
||||
* print("Overlay alpha: " + alpha);
|
||||
*/
|
||||
OverlayPropertyResult getProperty(OverlayID id, const QString& property);
|
||||
|
||||
/**jsdoc
|
||||
* Get the values of an overlay's properties.
|
||||
* @function Overlays.getProperties
|
||||
* @param {Uuid} overlayID - The ID of the overlay.
|
||||
* @param {Array.<string>} properties - An array of names of properties to get the values of.
|
||||
* @returns {Overlays.OverlayProperties} The values of valid properties if the overlay can be found, otherwise
|
||||
* <code>undefined</code>.
|
||||
* @example <caption>Create an overlay in front of your avatar then report some of its properties.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* var properties = Overlays.getProperties(overlay, ["color", "alpha", "grabbable"]);
|
||||
* print("Overlay properties: " + JSON.stringify(properties));
|
||||
*/
|
||||
OverlayPropertyResult getProperties(const OverlayID& id, const QStringList& properties);
|
||||
|
||||
/**jsdoc
|
||||
* Get the values of multiple overlays' properties.
|
||||
* @function Overlays.getOverlaysProperties
|
||||
* @param propertiesById {object.<Uuid, Array.<string>>} - An object with overlay IDs as keys and arrays of the names of
|
||||
* properties to get for each as values.
|
||||
* @returns {object.<Uuid, Overlays.OverlayProperties>} An object with overlay IDs as keys and
|
||||
* {@link Overlays.OverlayProperties|OverlayProperties} as values.
|
||||
* @example <caption>Create two cube overlays in front of your avatar then get some of their properties.</caption>
|
||||
* var overlayA = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: -0.3, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* var overlayB = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0.3, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* var propertiesToGet = {};
|
||||
* propertiesToGet[overlayA] = ["color", "alpha"];
|
||||
* propertiesToGet[overlayB] = ["dimensions"];
|
||||
* var properties = Overlays.getOverlaysProperties(propertiesToGet);
|
||||
* print("Overlays properties: " + JSON.stringify(properties));
|
||||
*/
|
||||
OverlayPropertyResult getOverlaysProperties(const QVariant& overlaysProperties);
|
||||
|
||||
/*jsdoc
|
||||
* Find the closest 3D overlay hit by a pick ray.
|
||||
*
|
||||
/**jsdoc
|
||||
* Find the closest 3D overlay intersected by a {@link PickRay}.
|
||||
* @function Overlays.findRayIntersection
|
||||
* @param {PickRay} The PickRay to use for finding overlays.
|
||||
* @param {bool} Unused; Exists to match Entity interface
|
||||
* @param {List of Overlays.OverlayID} Whitelist for intersection test.
|
||||
* @param {List of Overlays.OverlayID} Blacklist for intersection test.
|
||||
* @param {bool} Unused; Exists to match Entity interface
|
||||
* @param {bool} Unused; Exists to match Entity interface
|
||||
* @return {Overlays.RayToOverlayIntersectionResult} The result of the ray cast.
|
||||
* @param {PickRay} pickRay - The PickRay to use for finding overlays.
|
||||
* @param {boolean} [precisionPicking=false] - <em>Unused</em>; exists to match Entity API.
|
||||
* @param {Array.<Uuid>} [overlayIDsToInclude=[]] - Whitelist for intersection test. If empty then the result isn't limited
|
||||
* to overlays in the list.
|
||||
* @param {Array.<Uuid>} [overlayIDsToExclude=[]] - Blacklist for intersection test. If empty then the result doesn't
|
||||
* exclude overlays in the list.
|
||||
* @param {boolean} [visibleOnly=false] - <em>Unused</em>; exists to match Entity API.
|
||||
* @param {boolean} [collidableOnly=false] - <em>Unused</em>; exists to match Entity API.
|
||||
* @returns {Overlays.RayToOverlayIntersectionResult} The closest 3D overlay intersected by <code>pickRay</code>, taking
|
||||
* into account <code>overlayIDsToInclude</code> and <code>overlayIDsToExclude</code> if they're not empty.
|
||||
* @example <caption>Create a cube overlay in front of your avatar. Report 3D overlay intersection details for mouse
|
||||
* clicks.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
*
|
||||
* Controller.mousePressEvent.connect(function (event) {
|
||||
* var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
* var intersection = Overlays.findRayIntersection(pickRay);
|
||||
* print("Intersection: " + JSON.stringify(intersection));
|
||||
* });
|
||||
*/
|
||||
RayToOverlayIntersectionResult findRayIntersection(const PickRay& ray,
|
||||
bool precisionPicking = false,
|
||||
|
@ -213,61 +358,113 @@ public slots:
|
|||
bool visibleOnly = false,
|
||||
bool collidableOnly = false);
|
||||
|
||||
// Same as above but with QVectors
|
||||
// TODO: Apart from the name, this function signature on JavaScript is identical to that of findRayIntersection() and should
|
||||
// probably be removed from the JavaScript API so as not to cause confusion.
|
||||
/**jsdoc
|
||||
* Find the closest 3D overlay intersected by a {@link PickRay}.
|
||||
* @function Overlays.findRayIntersectionVector
|
||||
* @deprecated Use {@link Overlays.findRayIntersection} instead; it has identical parameters and results.
|
||||
* @param {PickRay} pickRay - The PickRay to use for finding overlays.
|
||||
* @param {boolean} [precisionPicking=false] - <em>Unused</em>; exists to match Entity API.
|
||||
* @param {Array.<Uuid>} [overlayIDsToInclude=[]] - Whitelist for intersection test. If empty then the result isn't limited
|
||||
* to overlays in the list.
|
||||
* @param {Array.<Uuid>} [overlayIDsToExclude=[]] - Blacklist for intersection test. If empty then the result doesn't
|
||||
* exclude overlays in the list.
|
||||
* @param {boolean} [visibleOnly=false] - <em>Unused</em>; exists to match Entity API.
|
||||
* @param {boolean} [collidableOnly=false] - <em>Unused</em>; exists to match Entity API.
|
||||
* @returns {Overlays.RayToOverlayIntersectionResult} The closest 3D overlay intersected by <code>pickRay</code>, taking
|
||||
* into account <code>overlayIDsToInclude</code> and <code>overlayIDsToExclude</code> if they're not empty.
|
||||
*/
|
||||
RayToOverlayIntersectionResult findRayIntersectionVector(const PickRay& ray, bool precisionPicking,
|
||||
const QVector<OverlayID>& overlaysToInclude,
|
||||
const QVector<OverlayID>& overlaysToDiscard,
|
||||
bool visibleOnly = false, bool collidableOnly = false);
|
||||
|
||||
/**jsdoc
|
||||
* Return a list of 3d overlays with bounding boxes that touch the given sphere
|
||||
*
|
||||
* Return a list of 3D overlays with bounding boxes that touch a search sphere.
|
||||
* @function Overlays.findOverlays
|
||||
* @param {Vec3} center the point to search from.
|
||||
* @param {float} radius search radius
|
||||
* @return {Overlays.OverlayID[]} list of overlays withing the radius
|
||||
* @param {Vec3} center - The center of the search sphere.
|
||||
* @param {number} radius - The radius of the search sphere.
|
||||
* @returns {Uuid[]} An array of overlay IDs with bounding boxes that touch a search sphere.
|
||||
* @example <caption>Create two cube overlays in front of your avatar then search for overlays near your avatar.</caption>
|
||||
* var overlayA = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: -0.3, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* print("Overlay A: " + overlayA);
|
||||
* var overlayB = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0.3, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* print("Overlay B: " + overlayB);
|
||||
*
|
||||
* var overlaysFound = Overlays.findOverlays(MyAvatar.position, 5.0);
|
||||
* print("Overlays found: " + JSON.stringify(overlaysFound));
|
||||
*/
|
||||
QVector<QUuid> findOverlays(const glm::vec3& center, float radius);
|
||||
|
||||
/**jsdoc
|
||||
* Check whether an overlay's assets have been loaded. For example, if the
|
||||
* overlay is an "image" overlay, this will indicate whether the its image
|
||||
* has loaded.
|
||||
* Check whether an overlay's assets have been loaded. For example, for an <code>image</code> overlay the result indicates
|
||||
* whether its image has been loaded.
|
||||
* @function Overlays.isLoaded
|
||||
* @param {Overlays.OverlayID} The ID of the overlay to check.
|
||||
* @return {bool} `true` if the overlay's assets have been loaded, otherwise `false`.
|
||||
* @param {Uuid} overlayID - The ID of the overlay to check.
|
||||
* @returns {boolean} <code>true</code> if the overlay's assets have been loaded, otherwise <code>false</code>.
|
||||
* @example <caption>Create an image overlay and report whether its image is loaded after 1s.</caption>
|
||||
* var overlay = Overlays.addOverlay("image", {
|
||||
* bounds: { x: 100, y: 100, width: 200, height: 200 },
|
||||
* imageURL: "https://content.highfidelity.com/DomainContent/production/Particles/wispy-smoke.png"
|
||||
* });
|
||||
* Script.setTimeout(function () {
|
||||
* var isLoaded = Overlays.isLoaded(overlay);
|
||||
* print("Image loaded: " + isLoaded);
|
||||
* }, 1000);
|
||||
*/
|
||||
bool isLoaded(OverlayID id);
|
||||
|
||||
/**jsdoc
|
||||
* Calculates the size of the given text in the specified overlay if it is a text overlay.
|
||||
* If it is a 2D text overlay, the size will be in pixels.
|
||||
* If it is a 3D text overlay, the size will be in meters.
|
||||
*
|
||||
* @function Overlays.textSize
|
||||
* @param {Overlays.OverlayID} The ID of the overlay to measure.
|
||||
* @param {string} The string to measure.
|
||||
* @return {Vec2} The size of the text.
|
||||
* @param {Uuid} overlayID - The ID of the overlay to use for calculation.
|
||||
* @param {string} text - The string to calculate the size of.
|
||||
* @returns {Size} The size of the <code>text</code> if the overlay is a text overlay, otherwise
|
||||
* <code>{ height: 0, width : 0 }</code>. If the overlay is a 2D overlay, the size is in pixels; if the overlay is a 3D
|
||||
* overlay, the size is in meters.
|
||||
* @example <caption>Calculate the size of "hello" in a 3D text overlay.</caption>
|
||||
* var overlay = Overlays.addOverlay("text3d", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -2 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* text: "hello",
|
||||
* lineHeight: 0.2
|
||||
* });
|
||||
* var textSize = Overlays.textSize(overlay, "hello");
|
||||
* print("Size of \"hello\": " + JSON.stringify(textSize));
|
||||
*/
|
||||
QSizeF textSize(OverlayID id, const QString& text);
|
||||
|
||||
/**jsdoc
|
||||
* Get the width of the virtual 2D HUD.
|
||||
*
|
||||
* Get the width of the window or HUD.
|
||||
* @function Overlays.width
|
||||
* @return {float} The width of the 2D HUD.
|
||||
* @returns {number} The width, in pixels, of the Interface window if in desktop mode or the HUD if in HMD mode.
|
||||
*/
|
||||
float width();
|
||||
|
||||
/**jsdoc
|
||||
* Get the height of the virtual 2D HUD.
|
||||
*
|
||||
* Get the height of the window or HUD.
|
||||
* @function Overlays.height
|
||||
* @return {float} The height of the 2D HUD.
|
||||
* @returns {number} The height, in pixels, of the Interface window if in desktop mode or the HUD if in HMD mode.
|
||||
*/
|
||||
float height();
|
||||
|
||||
/// return true if there is an overlay with that id else false
|
||||
/**jsdoc
|
||||
* Check if there is an overlay of a given ID.
|
||||
* @function Overlays.isAddedOverly
|
||||
* @param {Uuid} overlayID - The ID to check.
|
||||
* @returns {boolean} <code>true</code> if an overlay with the given ID exists, <code>false</code> otherwise.
|
||||
*/
|
||||
bool isAddedOverlay(OverlayID id);
|
||||
|
||||
#if OVERLAY_PANELS
|
||||
|
@ -294,36 +491,237 @@ public slots:
|
|||
|
||||
#endif
|
||||
|
||||
/**jsdoc
|
||||
* Generate a mouse press event on an overlay.
|
||||
* @function Overlays.sendMousePressOnOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay to generate a mouse press event on.
|
||||
* @param {PointerEvent} event - The mouse press event details.
|
||||
* @example <caption>Create a 2D rectangle overlay plus a 3D cube overlay and generate mousePressOnOverlay events for the 2D
|
||||
* overlay.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* print("3D overlay: " + overlay);
|
||||
*
|
||||
* var overlay = Overlays.addOverlay("rectangle", {
|
||||
* bounds: { x: 100, y: 100, width: 200, height: 100 },
|
||||
* color: { red: 255, green: 255, blue: 255 }
|
||||
* });
|
||||
* print("2D overlay: " + overlay);
|
||||
*
|
||||
* // Overlays.mousePressOnOverlay by default applies only to 3D overlays.
|
||||
* Overlays.mousePressOnOverlay.connect(function(overlayID, event) {
|
||||
* print("Clicked: " + overlayID);
|
||||
* });
|
||||
*
|
||||
* Controller.mousePressEvent.connect(function (event) {
|
||||
* // Overlays.getOverlayAtPoint applies only to 2D overlays.
|
||||
* var overlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
|
||||
* if (overlay) {
|
||||
* Overlays.sendMousePressOnOverlay(overlay, {
|
||||
* type: "press",
|
||||
* id: 0,
|
||||
* pos2D: event
|
||||
* });
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
void sendMousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Generate a mouse release event on an overlay.
|
||||
* @function Overlays.sendMouseReleaseOnOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay to generate a mouse release event on.
|
||||
* @param {PointerEvent} event - The mouse release event details.
|
||||
*/
|
||||
void sendMouseReleaseOnOverlay(const OverlayID& overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Generate a mouse move event on an overlay.
|
||||
* @function Overlays.sendMouseMoveOnOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay to generate a mouse move event on.
|
||||
* @param {PointerEvent} event - The mouse move event details.
|
||||
*/
|
||||
void sendMouseMoveOnOverlay(const OverlayID& overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Generate a hover enter event on an overlay.
|
||||
* @function Overlays.sendHoverEnterOverlay
|
||||
* @param {Uuid} id - The ID of the overlay to generate a hover enter event on.
|
||||
* @param {PointerEvent} event - The hover enter event details.
|
||||
*/
|
||||
void sendHoverEnterOverlay(const OverlayID& overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Generate a hover over event on an overlay.
|
||||
* @function Overlays.sendHoverOverOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay to generate a hover over event on.
|
||||
* @param {PointerEvent} event - The hover over event details.
|
||||
*/
|
||||
void sendHoverOverOverlay(const OverlayID& overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Generate a hover leave event on an overlay.
|
||||
* @function Overlays.sendHoverLeaveOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay to generate a hover leave event on.
|
||||
* @param {PointerEvent} event - The hover leave event details.
|
||||
*/
|
||||
void sendHoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Get the ID of the Web3D overlay that has keyboard focus.
|
||||
* @function Overlays.getKeyboardFocusOverlay
|
||||
* @returns {Uuid} The ID of the {@link Overlays.OverlayType|web3d} overlay that has focus, if any, otherwise
|
||||
* <code>null</code>.
|
||||
*/
|
||||
OverlayID getKeyboardFocusOverlay();
|
||||
|
||||
/**jsdoc
|
||||
* Set the Web3D overlay that has keyboard focus.
|
||||
* @function Overlays.setKeyboardFocusOverlay
|
||||
* @param {Uuid} overlayID - The ID of the {@link Overlays.OverlayType|web3d} overlay to set keyboard focus to. Use
|
||||
* {@link Uuid|Uuid.NULL} or <code>null</code> to unset keyboard focus from an overlay.
|
||||
*/
|
||||
void setKeyboardFocusOverlay(const OverlayID& id);
|
||||
|
||||
signals:
|
||||
/**jsdoc
|
||||
* Emitted when an overlay is deleted
|
||||
*
|
||||
* Triggered when an overlay is deleted.
|
||||
* @function Overlays.overlayDeleted
|
||||
* @param {OverlayID} The ID of the overlay that was deleted.
|
||||
* @param {Uuid} overlayID - The ID of the overlay that was deleted.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Create an overlay then delete it after 1s.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* print("Overlay: " + overlay);
|
||||
*
|
||||
* Overlays.overlayDeleted.connect(function(overlayID) {
|
||||
* print("Deleted: " + overlayID);
|
||||
* });
|
||||
* Script.setTimeout(function () {
|
||||
* Overlays.deleteOverlay(overlay);
|
||||
* }, 1000);
|
||||
*/
|
||||
void overlayDeleted(OverlayID id);
|
||||
void panelDeleted(OverlayID id);
|
||||
|
||||
#if OVERLAY_PANELS
|
||||
void panelDeleted(OverlayID id);
|
||||
#endif
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a mouse press event occurs on an overlay. Only occurs for 3D overlays (unless you use
|
||||
* {@link Overlays.sendMousePressOnOverlay|sendMousePressOnOverlay} for a 2D overlay).
|
||||
* @function Overlays.mousePressOnOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay the mouse press event occurred on.
|
||||
* @param {PointerEvent} event - The mouse press event details.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Create a cube overlay in front of your avatar and report mouse clicks on it.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* print("My overlay: " + overlay);
|
||||
*
|
||||
* Overlays.mousePressOnOverlay.connect(function(overlayID, event) {
|
||||
* if (overlayID === overlay) {
|
||||
* print("Clicked on my overlay");
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
void mousePressOnOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a mouse double press event occurs on an overlay. Only occurs for 3D overlays.
|
||||
* @function Overlays.mouseDoublePressOnOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay the mouse double press event occurred on.
|
||||
* @param {PointerEvent} event - The mouse double press event details.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void mouseDoublePressOnOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a mouse release event occurs on an overlay. Only occurs for 3D overlays (unless you use
|
||||
* {@link Overlays.sendMouseReleaseOnOverlay|sendMouseReleaseOnOverlay} for a 2D overlay).
|
||||
* @function Overlays.mouseReleaseOnOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay the mouse release event occurred on.
|
||||
* @param {PointerEvent} event - The mouse release event details.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void mouseReleaseOnOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a mouse move event occurs on an overlay. Only occurs for 3D overlays (unless you use
|
||||
* {@link Overlays.sendMouseMoveOnOverlay|sendMouseMoveOnOverlay} for a 2D overlay).
|
||||
* @function Overlays.mouseMoveOnOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay the mouse moved event occurred on.
|
||||
* @param {PointerEvent} event - The mouse move event details.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void mouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a mouse press event occurs on something other than a 3D overlay.
|
||||
* @function Overlays.mousePressOffOverlay
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void mousePressOffOverlay();
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a mouse double press event occurs on something other than a 3D overlay.
|
||||
* @function Overlays.mouseDoublePressOffOverlay
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void mouseDoublePressOffOverlay();
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a mouse cursor starts hovering over an overlay. Only occurs for 3D overlays (unless you use
|
||||
* {@link Overlays.sendHoverEnterOverlay|sendHoverEnterOverlay} for a 2D overlay).
|
||||
* @function Overlays.hoverEnterOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay the mouse moved event occurred on.
|
||||
* @param {PointerEvent} event - The mouse move event details.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Create a cube overlay in front of your avatar and report when you start hovering your mouse over
|
||||
* it.</caption>
|
||||
* var overlay = Overlays.addOverlay("cube", {
|
||||
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
|
||||
* rotation: MyAvatar.orientation,
|
||||
* dimensions: { x: 0.3, y: 0.3, z: 0.3 },
|
||||
* solid: true
|
||||
* });
|
||||
* print("Overlay: " + overlay);
|
||||
* Overlays.hoverEnterOverlay.connect(function(overlayID, event) {
|
||||
* print("Hover enter: " + overlayID);
|
||||
* });
|
||||
*/
|
||||
void hoverEnterOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a mouse cursor continues hovering over an overlay. Only occurs for 3D overlays (unless you use
|
||||
* {@link Overlays.sendHoverOverOverlay|sendHoverOverOverlay} for a 2D overlay).
|
||||
* @function Overlays.hoverOverOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay the hover over event occurred on.
|
||||
* @param {PointerEvent} event - The hover over event details.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void hoverOverOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a mouse cursor finishes hovering over an overlay. Only occurs for 3D overlays (unless you use
|
||||
* {@link Overlays.sendHoverLeaveOverlay|sendHoverLeaveOverlay} for a 2D overlay).
|
||||
* @function Overlays.hoverLeaveOverlay
|
||||
* @param {Uuid} overlayID - The ID of the overlay the hover leave event occurred on.
|
||||
* @param {PointerEvent} event - The hover leave event details.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void hoverLeaveOverlay(OverlayID overlayID, const PointerEvent& event);
|
||||
|
||||
private:
|
||||
|
|
|
@ -27,6 +27,8 @@ bool PanelAttachable::getParentVisible() const {
|
|||
#endif
|
||||
}
|
||||
|
||||
// JSDoc for copying to @typedefs of overlay types that inherit PanelAttachable.
|
||||
// No JSDoc because these properties are not actually used.
|
||||
QVariant PanelAttachable::getProperty(const QString& property) {
|
||||
if (property == "offsetPosition") {
|
||||
return vec3toVariant(getOffsetPosition());
|
||||
|
|
|
@ -58,6 +58,10 @@ void Planar3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
}
|
||||
|
||||
// JSDoc for copying to @typedefs of overlay types that inherit Planar3DOverlay.
|
||||
/**jsdoc
|
||||
* @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
*/
|
||||
QVariant Planar3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "dimensions" || property == "scale" || property == "size") {
|
||||
return vec2toVariant(getDimensions());
|
||||
|
|
|
@ -27,8 +27,8 @@ public:
|
|||
void setDimensions(float value) { setDimensions(glm::vec2(value)); }
|
||||
void setDimensions(const glm::vec2& value);
|
||||
|
||||
void setProperties(const QVariantMap& properties) override;
|
||||
QVariant getProperty(const QString& property) override;
|
||||
virtual void setProperties(const QVariantMap& properties) override;
|
||||
virtual QVariant getProperty(const QString& property) override;
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal) override;
|
||||
|
|
|
@ -53,6 +53,7 @@ QmlOverlay::~QmlOverlay() {
|
|||
_qmlElement.reset();
|
||||
}
|
||||
|
||||
// QmlOverlay replaces Overlay's properties with those defined in the QML file used but keeps Overlay2D's properties.
|
||||
void QmlOverlay::setProperties(const QVariantMap& properties) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setProperties", Q_ARG(QVariantMap, properties));
|
||||
|
|
|
@ -107,6 +107,54 @@ const render::ShapeKey Rectangle3DOverlay::getShapeKey() {
|
|||
return builder.build();
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>rectangle3d</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.Rectangle3DProperties
|
||||
*
|
||||
* @property {string} type=rectangle3d - Has the value <code>"rectangle3d"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
||||
* rotating as you move your avatar.
|
||||
*
|
||||
* @property {string} name="" - A friendly name for the overlay.
|
||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
*/
|
||||
void Rectangle3DOverlay::setProperties(const QVariantMap& properties) {
|
||||
Planar3DOverlay::setProperties(properties);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,29 @@
|
|||
QString const RectangleOverlay::TYPE = "rectangle";
|
||||
QUrl const RectangleOverlay::URL(QString("hifi/overlays/RectangleOverlay.qml"));
|
||||
|
||||
// RectangleOverlay's properties are defined in the QML file specified above.
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>rectangle</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.RectangleProperties
|
||||
*
|
||||
* @property {Rect} bounds - The position and size of the rectangle, in pixels. <em>Write-only.</em>
|
||||
* @property {number} x - Integer left, x-coordinate value = <code>bounds.x</code>. <em>Write-only.</em>
|
||||
* @property {number} y - Integer top, y-coordinate value = <code>bounds.y</code>. <em>Write-only.</em>
|
||||
* @property {number} width - Integer width of the rectangle = <code>bounds.width</code>. <em>Write-only.</em>
|
||||
* @property {number} height - Integer height of the rectangle = <code>bounds.height</code>. <em>Write-only.</em>
|
||||
*
|
||||
* @property {Color} color=0,0,0 - The color of the overlay. <em>Write-only.</em>
|
||||
* @property {number} alpha=1.0 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>. <em>Write-only.</em>
|
||||
* @property {number} borderWidth=1 - Integer width of the border, in pixels. The border is drawn within the rectangle's bounds.
|
||||
* It is not drawn unless either <code>borderColor</code> or <code>borderAlpha</code> are specified. <em>Write-only.</em>
|
||||
* @property {number} radius=0 - Integer corner radius, in pixels. <em>Write-only.</em>
|
||||
* @property {Color} borderColor=0,0,0 - The color of the border. <em>Write-only.</em>
|
||||
* @property {number} borderAlpha=1.0 - The opacity of the border, <code>0.0</code> - <code>1.0</code>.
|
||||
* <em>Write-only.</em>
|
||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||
* <em>Write-only.</em>
|
||||
*/
|
||||
|
||||
RectangleOverlay::RectangleOverlay() : QmlOverlay(URL) {}
|
||||
|
||||
RectangleOverlay::RectangleOverlay(const RectangleOverlay* rectangleOverlay)
|
||||
|
|
|
@ -108,6 +108,57 @@ void Shape3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>shape</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.ShapeProperties
|
||||
*
|
||||
* @property {string} type=shape - Has the value <code>"shape"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
||||
* rotating as you move your avatar.
|
||||
*
|
||||
* @property {string} name="" - A friendly name for the overlay.
|
||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
*
|
||||
* @property {Shape} shape=Hexagon - The geometrical shape of the overlay.
|
||||
* @property {number} borderSize - Not used.
|
||||
*/
|
||||
QVariant Shape3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "borderSize") {
|
||||
return _borderSize;
|
||||
|
|
|
@ -26,6 +26,56 @@ Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) :
|
|||
{
|
||||
}
|
||||
|
||||
// If Sphere3DOverlay had a getProperty() method then it would go here; do JSDoc here.
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>sphere</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.SphereProperties
|
||||
*
|
||||
* @property {string} type=sphere - Has the value <code>"sphere"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
||||
* rotating as you move your avatar.
|
||||
*
|
||||
* @property {string} name="" - A friendly name for the overlay.
|
||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
*/
|
||||
|
||||
void Sphere3DOverlay::render(RenderArgs* args) {
|
||||
if (!_renderVisible) {
|
||||
return; // do nothing if we're not visible
|
||||
|
|
|
@ -204,6 +204,68 @@ void Text3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>text3d</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.Text3DProperties
|
||||
*
|
||||
* @property {string} type=text3d - Has the value <code>"text3d"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
||||
* rotating as you move your avatar.
|
||||
*
|
||||
* @property {string} name="" - A friendly name for the overlay.
|
||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {Vec2} dimensions=1,1 - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
*
|
||||
* @property {boolean} isFacingAvatar - If <code>true</code>, the overlay is rotated to face the user's camera about an axis
|
||||
* parallel to the user's avatar's "up" direction.
|
||||
*
|
||||
* @property {string} text="" - The text to display. Text does not automatically wrap; use <code>\n</code> for a line break.
|
||||
* @property {number} textAlpha=1 - The text alpha value.
|
||||
* @property {Color} backgroundColor=0,0,0 - The background color.
|
||||
* @property {number} backgroundAlpha=0.7 - The background alpha value.
|
||||
* @property {number} lineHeight=1 - The height of a line of text in meters.
|
||||
* @property {number} leftMargin=0.1 - The left margin, in meters.
|
||||
* @property {number} topMargin=0.1 - The top margin, in meters.
|
||||
* @property {number} rightMargin=0.1 - The right margin, in meters.
|
||||
* @property {number} bottomMargin=0.1 - The bottom margin, in meters.
|
||||
*/
|
||||
|
||||
QVariant Text3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "text") {
|
||||
return getText();
|
||||
|
|
|
@ -27,6 +27,33 @@
|
|||
QString const TextOverlay::TYPE = "text";
|
||||
QUrl const TextOverlay::URL(QString("hifi/overlays/TextOverlay.qml"));
|
||||
|
||||
// TextOverlay's properties are defined in the QML file specified above.
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>text</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.TextProperties
|
||||
*
|
||||
* @property {Rect} bounds - The position and size of the rectangle, in pixels. <em>Write-only.</em>
|
||||
* @property {number} x - Integer left, x-coordinate value = <code>bounds.x</code>. <em>Write-only.</em>
|
||||
* @property {number} y - Integer top, y-coordinate value = <code>bounds.y</code>. <em>Write-only.</em>
|
||||
* @property {number} width - Integer width of the rectangle = <code>bounds.width</code>. <em>Write-only.</em>
|
||||
* @property {number} height - Integer height of the rectangle = <code>bounds.height</code>. <em>Write-only.</em>
|
||||
*
|
||||
* @property {number} margin=0 - Sets the <code>leftMargin</code> and <code>topMargin</code> values, in pixels.
|
||||
* <em>Write-only.</em>
|
||||
* @property {number} leftMargin=0 - The left margin's size, in pixels. <em>Write-only.</em>
|
||||
* @property {number} topMargin=0 - The top margin's size, in pixels. <em>Write-only.</em>
|
||||
* @property {string} text="" - The text to display. Text does not automatically wrap; use <code>\n</code> for a line break. Text
|
||||
* is clipped to the <code>bounds</code>. <em>Write-only.</em>
|
||||
* @property {number} font.size=18 - The size of the text, in pixels. <em>Write-only.</em>
|
||||
* @property {number} lineHeight=18 - The height of a line of text, in pixels. <em>Write-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the text. Synonym: <code>textColor</code>. <em>Write-only.</em>
|
||||
* @property {number} alpha=1.0 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>. <em>Write-only.</em>
|
||||
* @property {Color} backgroundColor=0,0,0 - The color of the background rectangle. <em>Write-only.</em>
|
||||
* @property {number} backgroundAlpha=0.7 - The opacity of the background rectangle. <em>Write-only.</em>
|
||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||
* <em>Write-only.</em>
|
||||
*/
|
||||
|
||||
TextOverlay::TextOverlay() : QmlOverlay(URL) { }
|
||||
|
||||
TextOverlay::TextOverlay(const TextOverlay* textOverlay)
|
||||
|
|
|
@ -62,6 +62,11 @@ void Volume3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
}
|
||||
|
||||
// JSDoc for copying to @typedefs of overlay types that inherit Volume3DOverlay.
|
||||
/**jsdoc
|
||||
* @typedef
|
||||
* @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: <code>scale</code>, <code>size</code>.
|
||||
*/
|
||||
QVariant Volume3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "dimensions" || property == "scale" || property == "size") {
|
||||
return vec3toVariant(getDimensions());
|
||||
|
|
|
@ -466,6 +466,67 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
}
|
||||
}
|
||||
|
||||
// Web3DOverlay overrides the meaning of Planar3DOverlay's dimensions property.
|
||||
/**jsdoc
|
||||
* These are the properties of a <code>web3d</code> {@link Overlays.OverlayType|OverlayType}.
|
||||
* @typedef {object} Overlays.Web3DProperties
|
||||
*
|
||||
* @property {string} type=web3d - Has the value <code>"web3d"</code>. <em>Read-only.</em>
|
||||
* @property {Color} color=255,255,255 - The color of the overlay.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay, <code>0.0</code> - <code>1.0</code>.
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
|
||||
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
|
||||
* rotating as you move your avatar.
|
||||
*
|
||||
* @property {string} name="" - A friendly name for the overlay.
|
||||
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
|
||||
* <code>start</code>.
|
||||
* @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
|
||||
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
|
||||
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
* {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay.
|
||||
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of other overlays that don't
|
||||
* have <code>drawInFront</code> set to <code>true</code>, and in front of entities.
|
||||
* @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if
|
||||
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
*
|
||||
* @property {boolean} isFacingAvatar - If <code>true</code>, the overlay is rotated to face the user's camera about an axis
|
||||
* parallel to the user's avatar's "up" direction.
|
||||
*
|
||||
* @property {string} url - The URL of the Web page to display.
|
||||
* @property {string} scriptURL="" - The URL of a JavaScript file to inject into the Web page.
|
||||
* @property {Vec2} resolution - <strong>Deprecated:</strong> This property has been removed.
|
||||
* @property {number} dpi=30 - The dots per inch to display the Web page at, on the overlay.
|
||||
* @property {Vec2} dimensions=1,1 - The size of the overlay to display the Web page on, in meters. Synonyms:
|
||||
* <code>scale</code>, <code>size</code>.
|
||||
* @property {number} maxFPS=10 - The maximum update rate for the Web overlay content, in frames/second.
|
||||
* @property {boolean} showKeyboardFocusHighlight=true - If <code>true</code>, the Web overlay is highlighted when it has
|
||||
* keyboard focus.
|
||||
* @property {string} inputMode=Touch - The user input mode to use - either <code>"Touch"</code> or <code>"Mouse"</code>.
|
||||
*/
|
||||
QVariant Web3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "url") {
|
||||
return _url;
|
||||
|
|
|
@ -1641,9 +1641,17 @@ void Rig::initAnimGraph(const QUrl& url) {
|
|||
// load the anim graph
|
||||
_animLoader.reset(new AnimNodeLoader(url));
|
||||
_animLoading = true;
|
||||
connect(_animLoader.get(), &AnimNodeLoader::success, [this](AnimNode::Pointer nodeIn) {
|
||||
std::weak_ptr<AnimSkeleton> weakSkeletonPtr = _animSkeleton;
|
||||
connect(_animLoader.get(), &AnimNodeLoader::success, [this, weakSkeletonPtr](AnimNode::Pointer nodeIn) {
|
||||
_animNode = nodeIn;
|
||||
_animNode->setSkeleton(_animSkeleton);
|
||||
|
||||
// abort load if the previous skeleton was deleted.
|
||||
auto sharedSkeletonPtr = weakSkeletonPtr.lock();
|
||||
if (!sharedSkeletonPtr) {
|
||||
return;
|
||||
}
|
||||
|
||||
_animNode->setSkeleton(sharedSkeletonPtr);
|
||||
|
||||
if (_userAnimState.clipNodeEnum != UserAnimState::None) {
|
||||
// restore the user animation we had before reset.
|
||||
|
@ -1651,6 +1659,7 @@ void Rig::initAnimGraph(const QUrl& url) {
|
|||
_userAnimState = { UserAnimState::None, "", 30.0f, false, 0.0f, 0.0f };
|
||||
overrideAnimation(origState.url, origState.fps, origState.loop, origState.firstFrame, origState.lastFrame);
|
||||
}
|
||||
|
||||
// restore the role animations we had before reset.
|
||||
for (auto& roleAnimState : _roleAnimStates) {
|
||||
auto roleState = roleAnimState.second;
|
||||
|
|
|
@ -31,7 +31,7 @@ class AnimInverseKinematics;
|
|||
// Rig instances are reentrant.
|
||||
// However only specific methods thread-safe. Noted below.
|
||||
|
||||
class Rig : public QObject, public std::enable_shared_from_this<Rig> {
|
||||
class Rig : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
struct StateHandler {
|
||||
|
|
|
@ -96,9 +96,13 @@ void SoundProcessor::run() {
|
|||
QByteArray outputAudioByteArray;
|
||||
|
||||
int sampleRate = interpretAsWav(rawAudioByteArray, outputAudioByteArray);
|
||||
if (sampleRate != 0) {
|
||||
downSample(outputAudioByteArray, sampleRate);
|
||||
if (sampleRate == 0) {
|
||||
qCDebug(audio) << "Unsupported WAV file type";
|
||||
emit onError(300, "Failed to load sound file, reason: unsupported WAV file type");
|
||||
return;
|
||||
}
|
||||
|
||||
downSample(outputAudioByteArray, sampleRate);
|
||||
} else if (fileName.endsWith(RAW_EXTENSION)) {
|
||||
// check if this was a stereo raw file
|
||||
// since it's raw the only way for us to know that is if the file was called .stereo.raw
|
||||
|
|
|
@ -251,10 +251,10 @@ void EntityTreeRenderer::shutdown() {
|
|||
}
|
||||
|
||||
void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||
PROFILE_RANGE_EX(simulation_physics, "Add", 0xffff00ff, (uint64_t)_entitiesToAdd.size());
|
||||
PROFILE_RANGE_EX(simulation_physics, "AddToScene", 0xffff00ff, (uint64_t)_entitiesToAdd.size());
|
||||
PerformanceTimer pt("add");
|
||||
// Clear any expired entities
|
||||
// FIXME should be able to use std::remove_if, but it fails due to some
|
||||
// Clear any expired entities
|
||||
// FIXME should be able to use std::remove_if, but it fails due to some
|
||||
// weird compilation error related to EntityItemID assignment operators
|
||||
for (auto itr = _entitiesToAdd.begin(); _entitiesToAdd.end() != itr; ) {
|
||||
if (itr->second.expired()) {
|
||||
|
@ -272,7 +272,7 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r
|
|||
continue;
|
||||
}
|
||||
|
||||
// Path to the parent transforms is not valid,
|
||||
// Path to the parent transforms is not valid,
|
||||
// don't add to the scene graph yet
|
||||
if (!entity->isParentPathComplete()) {
|
||||
continue;
|
||||
|
@ -296,7 +296,7 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r
|
|||
}
|
||||
|
||||
void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene, const ViewFrustum& view, render::Transaction& transaction) {
|
||||
PROFILE_RANGE_EX(simulation_physics, "Change", 0xffff00ff, (uint64_t)_changedEntities.size());
|
||||
PROFILE_RANGE_EX(simulation_physics, "ChangeInScene", 0xffff00ff, (uint64_t)_changedEntities.size());
|
||||
PerformanceTimer pt("change");
|
||||
std::unordered_set<EntityItemID> changedEntities;
|
||||
_changedEntitiesGuard.withWriteLock([&] {
|
||||
|
@ -402,6 +402,8 @@ void EntityTreeRenderer::update(bool simulate) {
|
|||
PerformanceTimer perfTimer("ETRupdate");
|
||||
if (_tree && !_shuttingDown) {
|
||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||
|
||||
// here we update _currentFrame and _lastAnimated and sync with the server properties.
|
||||
tree->update(simulate);
|
||||
|
||||
// Update the rendereable entities as needed
|
||||
|
@ -736,7 +738,7 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) {
|
|||
PickRay ray = _viewState->computePickRay(event->x(), event->y());
|
||||
RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID);
|
||||
if (rayPickResult.intersects && rayPickResult.entity) {
|
||||
//qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID;
|
||||
// qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID;
|
||||
|
||||
glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult);
|
||||
PointerEvent pointerEvent(PointerEvent::Release, PointerManager::MOUSE_POINTER_ID,
|
||||
|
|
|
@ -63,13 +63,17 @@ bool ModelEntityWrapper::isModelLoaded() const {
|
|||
EntityItemPointer RenderableModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
EntityItemPointer entity(new RenderableModelEntityItem(entityID, properties.getDimensionsInitialized()),
|
||||
[](EntityItem* ptr) { ptr->deleteLater(); });
|
||||
|
||||
entity->setProperties(properties);
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
RenderableModelEntityItem::RenderableModelEntityItem(const EntityItemID& entityItemID, bool dimensionsInitialized) :
|
||||
ModelEntityWrapper(entityItemID),
|
||||
_dimensionsInitialized(dimensionsInitialized) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
RenderableModelEntityItem::~RenderableModelEntityItem() { }
|
||||
|
@ -464,7 +468,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) {
|
|||
shapeInfo.setParams(type, dimensions, getCompoundShapeURL());
|
||||
} else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) {
|
||||
// TODO: assert we never fall in here when model not fully loaded
|
||||
//assert(_model && _model->isLoaded());
|
||||
// assert(_model && _model->isLoaded());
|
||||
|
||||
updateModelBounds();
|
||||
model->updateGeometry();
|
||||
|
@ -974,9 +978,6 @@ void ModelEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entit
|
|||
entity->setModel({});
|
||||
}
|
||||
|
||||
bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
void ModelEntityRenderer::animate(const TypedEntityPointer& entity) {
|
||||
if (!_animation || !_animation->isLoaded()) {
|
||||
|
@ -991,19 +992,12 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!_lastAnimated) {
|
||||
_lastAnimated = usecTimestampNow();
|
||||
return;
|
||||
}
|
||||
|
||||
auto now = usecTimestampNow();
|
||||
auto interval = now - _lastAnimated;
|
||||
_lastAnimated = now;
|
||||
float deltaTime = (float)interval / (float)USECS_PER_SECOND;
|
||||
_currentFrame += (deltaTime * _renderAnimationProperties.getFPS());
|
||||
|
||||
{
|
||||
int animationCurrentFrame = (int)(glm::floor(_currentFrame)) % frameCount;
|
||||
// the current frame is set on the server in update() in ModelEntityItem.cpp
|
||||
int animationCurrentFrame = (int)(glm::floor(entity->getAnimationCurrentFrame()));
|
||||
|
||||
// in the case where the last frame is greater than the framecount then clamp
|
||||
// it to the end of the animation until it loops around.
|
||||
if (animationCurrentFrame < 0 || animationCurrentFrame > frameCount) {
|
||||
animationCurrentFrame = 0;
|
||||
}
|
||||
|
@ -1039,10 +1033,10 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) {
|
|||
glm::mat4 translationMat;
|
||||
|
||||
if (allowTranslation) {
|
||||
if(index < translations.size()){
|
||||
if (index < translations.size()) {
|
||||
translationMat = glm::translate(translations[index]);
|
||||
}
|
||||
} else if (index < animationJointNames.size()){
|
||||
} else if (index < animationJointNames.size()) {
|
||||
QString jointName = fbxJoints[index].name; // Pushing this here so its not done on every entity, with the exceptions of those allowing for translation
|
||||
|
||||
if (originalFbxIndices.contains(jointName)) {
|
||||
|
@ -1317,25 +1311,17 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
if (model->getRenderItemsNeedUpdate()) {
|
||||
model->updateRenderItems();
|
||||
}
|
||||
|
||||
{
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "CheckAnimation");
|
||||
// make a copy of the animation properites
|
||||
auto newAnimationProperties = entity->getAnimationProperties();
|
||||
if (newAnimationProperties != _renderAnimationProperties) {
|
||||
withWriteLock([&] {
|
||||
_renderAnimationProperties = newAnimationProperties;
|
||||
_currentFrame = _renderAnimationProperties.getCurrentFrame();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The code to deal with the change of properties is now in ModelEntityItem.cpp
|
||||
// That is where _currentFrame and _lastAnimated were updated.
|
||||
if (_animating) {
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "Animate");
|
||||
if (!jointsMapped()) {
|
||||
mapJoints(entity, model->getJointNames());
|
||||
}
|
||||
animate(entity);
|
||||
if (!(entity->getAnimationFirstFrame() < 0) && !(entity->getAnimationFirstFrame() > entity->getAnimationLastFrame())) {
|
||||
animate(entity);
|
||||
}
|
||||
emit requestRenderUpdate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace render { namespace entities {
|
|||
class ModelEntityRenderer;
|
||||
} }
|
||||
|
||||
//#define MODEL_ENTITY_USE_FADE_EFFECT
|
||||
// #define MODEL_ENTITY_USE_FADE_EFFECT
|
||||
class ModelEntityWrapper : public ModelEntityItem {
|
||||
using Parent = ModelEntityItem;
|
||||
friend class render::entities::ModelEntityRenderer;
|
||||
|
@ -133,7 +133,7 @@ class ModelEntityRenderer : public TypedEntityRenderer<RenderableModelEntityItem
|
|||
friend class EntityRenderer;
|
||||
|
||||
public:
|
||||
ModelEntityRenderer(const EntityItemPointer& entity) : Parent(entity) { }
|
||||
ModelEntityRenderer(const EntityItemPointer& entity) : Parent(entity) {}
|
||||
|
||||
protected:
|
||||
virtual void removeFromScene(const ScenePointer& scene, Transaction& transaction) override;
|
||||
|
@ -184,8 +184,6 @@ private:
|
|||
bool _shouldHighlight { false };
|
||||
bool _animating { false };
|
||||
uint64_t _lastAnimated { 0 };
|
||||
float _currentFrame { 0 };
|
||||
|
||||
};
|
||||
|
||||
} } // namespace
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
// Sphere entities should fit inside a cube entity of the same size, so a sphere that has dimensions 1x1x1
|
||||
// is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down.
|
||||
static const float SPHERE_ENTITY_SCALE = 0.5f;
|
||||
static const unsigned int SUN_SHADOW_CASCADE_COUNT{ 4 };
|
||||
static const float SUN_SHADOW_MAX_DISTANCE{ 40.0f };
|
||||
|
||||
using namespace render;
|
||||
using namespace render::entities;
|
||||
|
@ -116,7 +118,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
|
|||
// Do we need to allocate the light in the stage ?
|
||||
if (LightStage::isIndexInvalid(_sunIndex)) {
|
||||
_sunIndex = _stage->addLight(_sunLight);
|
||||
_shadowIndex = _stage->addShadow(_sunIndex);
|
||||
_shadowIndex = _stage->addShadow(_sunIndex, SUN_SHADOW_MAX_DISTANCE, SUN_SHADOW_CASCADE_COUNT);
|
||||
} else {
|
||||
_stage->updateLightArrayBuffer(_sunIndex);
|
||||
}
|
||||
|
|
|
@ -22,15 +22,28 @@ const float AnimationPropertyGroup::MAXIMUM_POSSIBLE_FRAME = 100000.0f;
|
|||
|
||||
bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b) {
|
||||
return
|
||||
(a._url == b._url) &&
|
||||
|
||||
(a._currentFrame == b._currentFrame) &&
|
||||
(a._running == b._running) &&
|
||||
(a._loop == b._loop) &&
|
||||
(a._hold == b._hold) &&
|
||||
(a._firstFrame == b._firstFrame) &&
|
||||
(a._lastFrame == b._lastFrame) &&
|
||||
(a._hold == b._hold);
|
||||
(a._url == b._url);
|
||||
}
|
||||
|
||||
bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b) {
|
||||
return
|
||||
(a._currentFrame != b._currentFrame) ||
|
||||
(a._running != b._running) ||
|
||||
(a._loop != b._loop) ||
|
||||
(a._hold != b._hold) ||
|
||||
(a._firstFrame != b._firstFrame) ||
|
||||
(a._lastFrame != b._lastFrame) ||
|
||||
(a._url != b._url);
|
||||
}
|
||||
|
||||
|
||||
void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const {
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_URL, Animation, animation, URL, url);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation);
|
||||
|
@ -130,6 +143,7 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) {
|
|||
allowTranslation = settingsMap["allowTranslation"].toBool();
|
||||
}
|
||||
|
||||
|
||||
setAllowTranslation(allowTranslation);
|
||||
setFPS(fps);
|
||||
setCurrentFrame(currentFrame);
|
||||
|
|
|
@ -89,6 +89,7 @@ public:
|
|||
|
||||
protected:
|
||||
friend bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b);
|
||||
friend bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b);
|
||||
void setFromOldAnimationSettings(const QString& value);
|
||||
};
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <GLMHelpers.h>
|
||||
#include <Octree.h>
|
||||
#include <PhysicsHelpers.h>
|
||||
#include <Profile.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <SharedUtil.h> // usecTimestampNow()
|
||||
#include <LogHandler.h>
|
||||
|
@ -984,6 +985,7 @@ void EntityItem::setCollisionSoundURL(const QString& value) {
|
|||
}
|
||||
|
||||
void EntityItem::simulate(const quint64& now) {
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "Simulate");
|
||||
if (getLastSimulated() == 0) {
|
||||
setLastSimulated(now);
|
||||
}
|
||||
|
@ -1039,6 +1041,7 @@ void EntityItem::simulate(const quint64& now) {
|
|||
}
|
||||
|
||||
bool EntityItem::stepKinematicMotion(float timeElapsed) {
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "StepKinematicMotion");
|
||||
// get all the data
|
||||
Transform transform;
|
||||
glm::vec3 linearVelocity;
|
||||
|
@ -2840,7 +2843,7 @@ void EntityItem::retrieveMarketplacePublicKey() {
|
|||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest;
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL;
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
|
||||
requestURL.setPath("/api/v1/commerce/marketplace_key");
|
||||
QJsonObject request;
|
||||
networkRequest.setUrl(requestURL);
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <openssl/pem.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/ecdsa.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
|
|
|
@ -29,7 +29,6 @@ void EntitySimulation::setEntityTree(EntityTreePointer tree) {
|
|||
}
|
||||
|
||||
void EntitySimulation::updateEntities() {
|
||||
PROFILE_RANGE(simulation_physics, "ES::updateEntities");
|
||||
QMutexLocker lock(&_mutex);
|
||||
quint64 now = usecTimestampNow();
|
||||
|
||||
|
@ -38,12 +37,7 @@ void EntitySimulation::updateEntities() {
|
|||
callUpdateOnEntitiesThatNeedIt(now);
|
||||
moveSimpleKinematics(now);
|
||||
updateEntitiesInternal(now);
|
||||
|
||||
{
|
||||
PROFILE_RANGE(simulation_physics, "Sort");
|
||||
PerformanceTimer perfTimer("sortingEntities");
|
||||
sortEntitiesThatMoved();
|
||||
}
|
||||
sortEntitiesThatMoved();
|
||||
}
|
||||
|
||||
void EntitySimulation::takeEntitiesToDelete(VectorOfEntities& entitiesToDelete) {
|
||||
|
@ -101,6 +95,7 @@ void EntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
|||
// protected
|
||||
void EntitySimulation::expireMortalEntities(const quint64& now) {
|
||||
if (now > _nextExpiry) {
|
||||
PROFILE_RANGE_EX(simulation_physics, "ExpireMortals", 0xffff00ff, (uint64_t)_mortalEntities.size());
|
||||
// only search for expired entities if we expect to find one
|
||||
_nextExpiry = quint64(-1);
|
||||
QMutexLocker lock(&_mutex);
|
||||
|
@ -146,6 +141,7 @@ void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) {
|
|||
|
||||
// protected
|
||||
void EntitySimulation::sortEntitiesThatMoved() {
|
||||
PROFILE_RANGE_EX(simulation_physics, "SortTree", 0xffff00ff, (uint64_t)_entitiesToSort.size());
|
||||
// NOTE: this is only for entities that have been moved by THIS EntitySimulation.
|
||||
// External changes to entity position/shape are expected to be sorted outside of the EntitySimulation.
|
||||
MovingEntitiesOperator moveOperator;
|
||||
|
@ -265,7 +261,7 @@ void EntitySimulation::clearEntities() {
|
|||
}
|
||||
|
||||
void EntitySimulation::moveSimpleKinematics(const quint64& now) {
|
||||
PROFILE_RANGE_EX(simulation_physics, "Kinematics", 0xffff00ff, (uint64_t)_simpleKinematicEntities.size());
|
||||
PROFILE_RANGE_EX(simulation_physics, "MoveSimples", 0xffff00ff, (uint64_t)_simpleKinematicEntities.size());
|
||||
SetOfEntities::iterator itemItr = _simpleKinematicEntities.begin();
|
||||
while (itemItr != _simpleKinematicEntities.end()) {
|
||||
EntityItemPointer entity = *itemItr;
|
||||
|
|
|
@ -1308,7 +1308,7 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt
|
|||
QNetworkRequest networkRequest;
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL;
|
||||
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
|
||||
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer");
|
||||
QJsonObject request;
|
||||
request["certificate_id"] = certID;
|
||||
|
@ -1770,24 +1770,26 @@ void EntityTree::addToNeedsParentFixupList(EntityItemPointer entity) {
|
|||
}
|
||||
|
||||
void EntityTree::update(bool simulate) {
|
||||
PROFILE_RANGE(simulation_physics, "ET::update");
|
||||
PROFILE_RANGE(simulation_physics, "UpdateTree");
|
||||
fixupNeedsParentFixups();
|
||||
if (simulate && _simulation) {
|
||||
withWriteLock([&] {
|
||||
_simulation->updateEntities();
|
||||
VectorOfEntities pendingDeletes;
|
||||
_simulation->takeEntitiesToDelete(pendingDeletes);
|
||||
{
|
||||
PROFILE_RANGE(simulation_physics, "Deletes");
|
||||
VectorOfEntities pendingDeletes;
|
||||
_simulation->takeEntitiesToDelete(pendingDeletes);
|
||||
if (pendingDeletes.size() > 0) {
|
||||
// translate into list of ID's
|
||||
QSet<EntityItemID> idsToDelete;
|
||||
|
||||
if (pendingDeletes.size() > 0) {
|
||||
// translate into list of ID's
|
||||
QSet<EntityItemID> idsToDelete;
|
||||
for (auto entity : pendingDeletes) {
|
||||
idsToDelete.insert(entity->getEntityItemID());
|
||||
}
|
||||
|
||||
for (auto entity : pendingDeletes) {
|
||||
idsToDelete.insert(entity->getEntityItemID());
|
||||
// delete these things the roundabout way
|
||||
deleteEntities(idsToDelete, true);
|
||||
}
|
||||
|
||||
// delete these things the roundabout way
|
||||
deleteEntities(idsToDelete, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -33,6 +33,8 @@ EntityItemPointer ModelEntityItem::factory(const EntityItemID& entityID, const E
|
|||
|
||||
ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID)
|
||||
{
|
||||
_lastAnimated = usecTimestampNow();
|
||||
// set the last animated when interface (re)starts
|
||||
_type = EntityTypes::Model;
|
||||
_lastKnownCurrentFrame = -1;
|
||||
_color[0] = _color[1] = _color[2] = 0;
|
||||
|
@ -186,6 +188,105 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
|
|||
}
|
||||
|
||||
|
||||
|
||||
// added update function back for property fix
|
||||
void ModelEntityItem::update(const quint64& now) {
|
||||
|
||||
{
|
||||
auto currentAnimationProperties = this->getAnimationProperties();
|
||||
|
||||
if (_previousAnimationProperties != currentAnimationProperties) {
|
||||
withWriteLock([&] {
|
||||
// if we hit start animation or change the first or last frame then restart the animation
|
||||
if ((currentAnimationProperties.getFirstFrame() != _previousAnimationProperties.getFirstFrame()) ||
|
||||
(currentAnimationProperties.getLastFrame() != _previousAnimationProperties.getLastFrame()) ||
|
||||
(currentAnimationProperties.getRunning() && !_previousAnimationProperties.getRunning())) {
|
||||
|
||||
// when we start interface and the property is are set then the current frame is initialized to -1
|
||||
if (_currentFrame < 0) {
|
||||
// don't reset _lastAnimated here because we need the timestamp from the ModelEntityItem constructor for when the properties were set
|
||||
_currentFrame = currentAnimationProperties.getCurrentFrame();
|
||||
setAnimationCurrentFrame(_currentFrame);
|
||||
} else {
|
||||
_lastAnimated = usecTimestampNow();
|
||||
_currentFrame = currentAnimationProperties.getFirstFrame();
|
||||
setAnimationCurrentFrame(currentAnimationProperties.getFirstFrame());
|
||||
}
|
||||
} else if (!currentAnimationProperties.getRunning() && _previousAnimationProperties.getRunning()) {
|
||||
_currentFrame = currentAnimationProperties.getFirstFrame();
|
||||
setAnimationCurrentFrame(_currentFrame);
|
||||
} else if (currentAnimationProperties.getCurrentFrame() != _previousAnimationProperties.getCurrentFrame()) {
|
||||
// don't reset _lastAnimated here because the currentFrame was set with the previous setting of _lastAnimated
|
||||
_currentFrame = currentAnimationProperties.getCurrentFrame();
|
||||
}
|
||||
|
||||
});
|
||||
_previousAnimationProperties = this->getAnimationProperties();
|
||||
|
||||
}
|
||||
|
||||
if (isAnimatingSomething()) {
|
||||
if (!(getAnimationFirstFrame() < 0) && !(getAnimationFirstFrame() > getAnimationLastFrame())) {
|
||||
updateFrameCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EntityItem::update(now);
|
||||
}
|
||||
|
||||
bool ModelEntityItem::needsToCallUpdate() const {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ModelEntityItem::updateFrameCount() {
|
||||
|
||||
if (_currentFrame < 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_lastAnimated) {
|
||||
_lastAnimated = usecTimestampNow();
|
||||
return;
|
||||
}
|
||||
|
||||
auto now = usecTimestampNow();
|
||||
|
||||
// update the interval since the last animation.
|
||||
auto interval = now - _lastAnimated;
|
||||
_lastAnimated = now;
|
||||
|
||||
// if fps is negative then increment timestamp and return.
|
||||
if (getAnimationFPS() < 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
int updatedFrameCount = getAnimationLastFrame() - getAnimationFirstFrame() + 1;
|
||||
|
||||
if (!getAnimationHold() && getAnimationIsPlaying()) {
|
||||
float deltaTime = (float)interval / (float)USECS_PER_SECOND;
|
||||
_currentFrame += (deltaTime * getAnimationFPS());
|
||||
if (_currentFrame > getAnimationLastFrame()) {
|
||||
if (getAnimationLoop()) {
|
||||
_currentFrame = getAnimationFirstFrame() + (int)(glm::floor(_currentFrame - getAnimationFirstFrame())) % (updatedFrameCount - 1);
|
||||
} else {
|
||||
_currentFrame = getAnimationLastFrame();
|
||||
}
|
||||
} else if (_currentFrame < getAnimationFirstFrame()) {
|
||||
if (getAnimationFirstFrame() < 0) {
|
||||
_currentFrame = 0;
|
||||
} else {
|
||||
_currentFrame = getAnimationFirstFrame();
|
||||
}
|
||||
}
|
||||
// qCDebug(entities) << "in update frame " << _currentFrame;
|
||||
setAnimationCurrentFrame(_currentFrame);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void ModelEntityItem::debugDump() const {
|
||||
qCDebug(entities) << "ModelEntityItem id:" << getEntityItemID();
|
||||
qCDebug(entities) << " edited ago:" << getEditedAgo();
|
||||
|
@ -538,6 +639,13 @@ void ModelEntityItem::setAnimationLoop(bool loop) {
|
|||
});
|
||||
}
|
||||
|
||||
bool ModelEntityItem::getAnimationLoop() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _animationProperties.getLoop();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void ModelEntityItem::setAnimationHold(bool hold) {
|
||||
withWriteLock([&] {
|
||||
_animationProperties.setHold(hold);
|
||||
|
@ -573,8 +681,9 @@ float ModelEntityItem::getAnimationLastFrame() const {
|
|||
return _animationProperties.getLastFrame();
|
||||
});
|
||||
}
|
||||
|
||||
bool ModelEntityItem::getAnimationIsPlaying() const {
|
||||
return resultWithReadLock<float>([&] {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _animationProperties.getRunning();
|
||||
});
|
||||
}
|
||||
|
@ -585,8 +694,15 @@ float ModelEntityItem::getAnimationCurrentFrame() const {
|
|||
});
|
||||
}
|
||||
|
||||
bool ModelEntityItem::isAnimatingSomething() const {
|
||||
float ModelEntityItem::getAnimationFPS() const {
|
||||
return resultWithReadLock<float>([&] {
|
||||
return _animationProperties.getFPS();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
bool ModelEntityItem::isAnimatingSomething() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return !_animationProperties.getURL().isEmpty() &&
|
||||
_animationProperties.getRunning() &&
|
||||
(_animationProperties.getFPS() != 0.0f);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <ThreadSafeValueCache.h>
|
||||
#include "AnimationPropertyGroup.h"
|
||||
|
||||
|
||||
class ModelEntityItem : public EntityItem {
|
||||
public:
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
@ -46,8 +47,11 @@ public:
|
|||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||
bool& somethingChanged) override;
|
||||
|
||||
//virtual void update(const quint64& now) override;
|
||||
//virtual bool needsToCallUpdate() const override;
|
||||
// update() and needstocallupdate() added back for the entity property fix
|
||||
virtual void update(const quint64& now) override;
|
||||
virtual bool needsToCallUpdate() const override;
|
||||
void updateFrameCount();
|
||||
|
||||
virtual void debugDump() const override;
|
||||
|
||||
void setShapeType(ShapeType type) override;
|
||||
|
@ -90,6 +94,7 @@ public:
|
|||
bool getAnimationAllowTranslation() const { return _animationProperties.getAllowTranslation(); };
|
||||
|
||||
void setAnimationLoop(bool loop);
|
||||
bool getAnimationLoop() const;
|
||||
|
||||
void setAnimationHold(bool hold);
|
||||
bool getAnimationHold() const;
|
||||
|
@ -102,6 +107,7 @@ public:
|
|||
|
||||
bool getAnimationIsPlaying() const;
|
||||
float getAnimationCurrentFrame() const;
|
||||
float getAnimationFPS() const;
|
||||
bool isAnimatingSomething() const;
|
||||
|
||||
static const QString DEFAULT_TEXTURES;
|
||||
|
@ -147,7 +153,7 @@ protected:
|
|||
};
|
||||
|
||||
QVector<ModelJointData> _localJointData;
|
||||
int _lastKnownCurrentFrame;
|
||||
int _lastKnownCurrentFrame{-1};
|
||||
|
||||
rgbColor _color;
|
||||
QString _modelURL;
|
||||
|
@ -160,6 +166,11 @@ protected:
|
|||
QString _textures;
|
||||
|
||||
ShapeType _shapeType = SHAPE_TYPE_NONE;
|
||||
|
||||
private:
|
||||
uint64_t _lastAnimated{ 0 };
|
||||
AnimationPropertyGroup _previousAnimationProperties;
|
||||
float _currentFrame{ -1.0f };
|
||||
};
|
||||
|
||||
#endif // hifi_ModelEntityItem_h
|
||||
|
|
|
@ -56,7 +56,7 @@ float OBJTokenizer::getFloat() {
|
|||
return std::stof((nextToken() != OBJTokenizer::DATUM_TOKEN) ? nullptr : getDatum().data());
|
||||
}
|
||||
|
||||
int OBJTokenizer::nextToken() {
|
||||
int OBJTokenizer::nextToken(bool allowSpaceChar /*= false*/) {
|
||||
if (_pushedBackToken != NO_PUSHBACKED_TOKEN) {
|
||||
int token = _pushedBackToken;
|
||||
_pushedBackToken = NO_PUSHBACKED_TOKEN;
|
||||
|
@ -93,7 +93,7 @@ int OBJTokenizer::nextToken() {
|
|||
_datum = "";
|
||||
_datum.append(ch);
|
||||
while (_device->getChar(&ch)) {
|
||||
if (QChar(ch).isSpace() || ch == '\"') {
|
||||
if ((QChar(ch).isSpace() || ch == '\"') && (!allowSpaceChar || ch != ' ')) {
|
||||
ungetChar(ch); // read until we encounter a special character, then replace it
|
||||
break;
|
||||
}
|
||||
|
@ -399,7 +399,7 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi
|
|||
currentMaterialName = QString("part-") + QString::number(_partCounter++);
|
||||
}
|
||||
} else if (token == "mtllib" && !_url.isEmpty()) {
|
||||
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
|
||||
if (tokenizer.nextToken(true) != OBJTokenizer::DATUM_TOKEN) {
|
||||
break;
|
||||
}
|
||||
QByteArray libraryName = tokenizer.getDatum();
|
||||
|
|
|
@ -11,7 +11,7 @@ public:
|
|||
DATUM_TOKEN = 0x100,
|
||||
COMMENT_TOKEN = 0x101
|
||||
};
|
||||
int nextToken();
|
||||
int nextToken(bool allowSpaceChar = false);
|
||||
const QByteArray& getDatum() const { return _datum; }
|
||||
bool isNextTokenFloat();
|
||||
const QByteArray getLineAsDatum(); // some "filenames" have spaces in them
|
||||
|
|
|
@ -40,10 +40,6 @@ static bool disableOpenGL45 = QProcessEnvironment::systemEnvironment().contains(
|
|||
static GLBackend* INSTANCE{ nullptr };
|
||||
|
||||
BackendPointer GLBackend::createBackend() {
|
||||
// The ATI memory info extension only exposes 'free memory' so we want to force it to
|
||||
// cache the value as early as possible
|
||||
getDedicatedMemory();
|
||||
|
||||
// FIXME provide a mechanism to override the backend for testing
|
||||
// Where the gpuContext is initialized and where the TRUE Backend is created and assigned
|
||||
auto version = QOpenGLContextWrapper::currentContextVersion();
|
||||
|
|
|
@ -318,7 +318,10 @@ int GLBackend::makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slot
|
|||
if (requestedBinding != slotBindings.end()) {
|
||||
if (binding != (*requestedBinding)._location) {
|
||||
binding = (*requestedBinding)._location;
|
||||
glProgramUniform1i(glprogram, location, binding);
|
||||
for (auto i = 0; i < size; i++) {
|
||||
// If we are working with an array of textures, reserve for each elemet
|
||||
glProgramUniform1i(glprogram, location+i, binding+i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -88,53 +88,6 @@ gpu::Size getFreeDedicatedMemory() {
|
|||
return result;
|
||||
}
|
||||
|
||||
gpu::Size getDedicatedMemory() {
|
||||
static Size dedicatedMemory { 0 };
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&] {
|
||||
#ifdef Q_OS_WIN
|
||||
if (!dedicatedMemory && wglGetGPUIDsAMD && wglGetGPUInfoAMD) {
|
||||
UINT maxCount = wglGetGPUIDsAMD(0, 0);
|
||||
std::vector<UINT> ids;
|
||||
ids.resize(maxCount);
|
||||
wglGetGPUIDsAMD(maxCount, &ids[0]);
|
||||
GLuint memTotal;
|
||||
wglGetGPUInfoAMD(ids[0], WGL_GPU_RAM_AMD, GL_UNSIGNED_INT, sizeof(GLuint), &memTotal);
|
||||
dedicatedMemory = MB_TO_BYTES(memTotal);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!dedicatedMemory) {
|
||||
GLint atiGpuMemory[4];
|
||||
// not really total memory, but close enough if called early enough in the application lifecycle
|
||||
glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, atiGpuMemory);
|
||||
if (GL_NO_ERROR == glGetError()) {
|
||||
dedicatedMemory = KB_TO_BYTES(atiGpuMemory[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!dedicatedMemory) {
|
||||
GLint nvGpuMemory { 0 };
|
||||
glGetIntegerv(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &nvGpuMemory);
|
||||
if (GL_NO_ERROR == glGetError()) {
|
||||
dedicatedMemory = KB_TO_BYTES(nvGpuMemory);
|
||||
}
|
||||
}
|
||||
|
||||
if (!dedicatedMemory) {
|
||||
auto gpuIdent = GPUIdent::getInstance();
|
||||
if (gpuIdent && gpuIdent->isValid()) {
|
||||
dedicatedMemory = MB_TO_BYTES(gpuIdent->getMemory());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return dedicatedMemory;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ComparisonFunction comparisonFuncFromGL(GLenum func) {
|
||||
if (func == GL_NEVER) {
|
||||
return NEVER;
|
||||
|
|
|
@ -28,7 +28,6 @@ void serverWait();
|
|||
// Create a fence and synchronously wait on the fence
|
||||
void clientWait();
|
||||
|
||||
gpu::Size getDedicatedMemory();
|
||||
gpu::Size getFreeDedicatedMemory();
|
||||
ComparisonFunction comparisonFuncFromGL(GLenum func);
|
||||
State::StencilOp stencilOpFromGL(GLenum stencilOp);
|
||||
|
|
|
@ -97,7 +97,7 @@ public:
|
|||
void setTemporaryDomain(const QUuid& domainID, const QString& key);
|
||||
const QString& getTemporaryDomainKey(const QUuid& domainID) { return _accountInfo.getTemporaryDomainKey(domainID); }
|
||||
|
||||
QUrl getMetaverseServerURL() { return NetworkingConstants::METAVERSE_SERVER_URL; }
|
||||
QUrl getMetaverseServerURL() { return NetworkingConstants::METAVERSE_SERVER_URL(); }
|
||||
|
||||
public slots:
|
||||
void requestAccessToken(const QString& login, const QString& password);
|
||||
|
|
|
@ -12,15 +12,31 @@
|
|||
#ifndef hifi_NetworkingConstants_h
|
||||
#define hifi_NetworkingConstants_h
|
||||
|
||||
#include <QtCore/QProcessEnvironment>
|
||||
#include <QtCore/QUrl>
|
||||
|
||||
namespace NetworkingConstants {
|
||||
// If you want to use STAGING instead of STABLE,
|
||||
// don't forget to ALSO change the Domain Server Metaverse Server URL inside of:
|
||||
// links from the Domain Server web interface (like the connect account token generation)
|
||||
// will still point at stable unless you ALSO change the Domain Server Metaverse Server URL inside of:
|
||||
// <hifi repo>\domain-server\resources\web\js\shared.js
|
||||
|
||||
// You can avoid changing that and still effectively use a connected domain on staging
|
||||
// if you manually generate a personal access token for the domains scope
|
||||
// at https://staging.highfidelity.com/user/tokens/new?for_domain_server=true
|
||||
|
||||
const QUrl METAVERSE_SERVER_URL_STABLE("https://metaverse.highfidelity.com");
|
||||
const QUrl METAVERSE_SERVER_URL_STAGING("https://staging.highfidelity.com");
|
||||
const QUrl METAVERSE_SERVER_URL = METAVERSE_SERVER_URL_STABLE;
|
||||
|
||||
// You can change the return of this function if you want to use a custom metaverse URL at compile time
|
||||
// or you can pass a custom URL via the env variable
|
||||
static const QUrl METAVERSE_SERVER_URL() {
|
||||
static const QString HIFI_METAVERSE_URL_ENV = "HIFI_METAVERSE_URL";
|
||||
static const QUrl serverURL = QProcessEnvironment::systemEnvironment().contains(HIFI_METAVERSE_URL_ENV)
|
||||
? QUrl(QProcessEnvironment::systemEnvironment().value(HIFI_METAVERSE_URL_ENV))
|
||||
: METAVERSE_SERVER_URL_STABLE;
|
||||
return serverURL;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // hifi_NetworkingConstants_h
|
||||
|
|
|
@ -35,7 +35,7 @@ QNetworkReply* OAuthNetworkAccessManager::createRequest(QNetworkAccessManager::O
|
|||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
|
||||
if (accountManager->hasValidAccessToken()
|
||||
&& req.url().host() == NetworkingConstants::METAVERSE_SERVER_URL.host()) {
|
||||
&& req.url().host() == NetworkingConstants::METAVERSE_SERVER_URL().host()) {
|
||||
QNetworkRequest authenticatedRequest(req);
|
||||
authenticatedRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
authenticatedRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
|
|
|
@ -14,8 +14,9 @@
|
|||
#include <EntityItem.h>
|
||||
#include <EntityItemProperties.h>
|
||||
#include <EntityEditPacketSender.h>
|
||||
#include <PhysicsCollisionGroups.h>
|
||||
#include <LogHandler.h>
|
||||
#include <PhysicsCollisionGroups.h>
|
||||
#include <Profile.h>
|
||||
|
||||
#include "BulletUtil.h"
|
||||
#include "EntityMotionState.h"
|
||||
|
@ -325,6 +326,7 @@ bool EntityMotionState::isCandidateForOwnership() const {
|
|||
}
|
||||
|
||||
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "CheckOutOfSync");
|
||||
// NOTE: we only get here if we think we own the simulation
|
||||
assert(_body);
|
||||
|
||||
|
@ -476,6 +478,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
|||
}
|
||||
|
||||
bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "ShouldSend");
|
||||
// NOTE: we expect _entity and _body to be valid in this context, since shouldSendUpdate() is only called
|
||||
// after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL.
|
||||
assert(_entity);
|
||||
|
@ -516,6 +519,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
|
|||
}
|
||||
|
||||
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) {
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "Send");
|
||||
assert(_entity);
|
||||
assert(entityTreeIsLocked());
|
||||
|
||||
|
@ -731,6 +735,7 @@ void EntityMotionState::resetMeasuredBodyAcceleration() {
|
|||
}
|
||||
|
||||
void EntityMotionState::measureBodyAcceleration() {
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "MeasureAccel");
|
||||
// try to manually measure the true acceleration of the object
|
||||
uint32_t thisStep = ObjectMotionState::getWorldSimulationStep();
|
||||
uint32_t numSubsteps = thisStep - _lastMeasureStep;
|
||||
|
|
|
@ -10,12 +10,14 @@
|
|||
//
|
||||
|
||||
|
||||
#include "PhysicalEntitySimulation.h"
|
||||
|
||||
#include <Profile.h>
|
||||
|
||||
#include "PhysicsHelpers.h"
|
||||
#include "PhysicsLogging.h"
|
||||
#include "ShapeManager.h"
|
||||
|
||||
#include "PhysicalEntitySimulation.h"
|
||||
|
||||
PhysicalEntitySimulation::PhysicalEntitySimulation() {
|
||||
}
|
||||
|
@ -274,20 +276,24 @@ void PhysicalEntitySimulation::handleDeactivatedMotionStates(const VectorOfMotio
|
|||
}
|
||||
|
||||
void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionStates& motionStates) {
|
||||
PROFILE_RANGE_EX(simulation_physics, "ChangedEntities", 0x00000000, (uint64_t)motionStates.size());
|
||||
QMutexLocker lock(&_mutex);
|
||||
|
||||
// walk the motionStates looking for those that correspond to entities
|
||||
for (auto stateItr : motionStates) {
|
||||
ObjectMotionState* state = &(*stateItr);
|
||||
assert(state);
|
||||
if (state->getType() == MOTIONSTATE_TYPE_ENTITY) {
|
||||
EntityMotionState* entityState = static_cast<EntityMotionState*>(state);
|
||||
EntityItemPointer entity = entityState->getEntity();
|
||||
assert(entity.get());
|
||||
if (entityState->isCandidateForOwnership()) {
|
||||
_outgoingChanges.insert(entityState);
|
||||
{
|
||||
PROFILE_RANGE_EX(simulation_physics, "Filter", 0x00000000, (uint64_t)motionStates.size());
|
||||
for (auto stateItr : motionStates) {
|
||||
ObjectMotionState* state = &(*stateItr);
|
||||
assert(state);
|
||||
if (state->getType() == MOTIONSTATE_TYPE_ENTITY) {
|
||||
EntityMotionState* entityState = static_cast<EntityMotionState*>(state);
|
||||
EntityItemPointer entity = entityState->getEntity();
|
||||
assert(entity.get());
|
||||
if (entityState->isCandidateForOwnership()) {
|
||||
_outgoingChanges.insert(entityState);
|
||||
}
|
||||
_entitiesToSort.insert(entity);
|
||||
}
|
||||
_entitiesToSort.insert(entity);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,6 +308,7 @@ void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionSta
|
|||
}
|
||||
|
||||
// look for entities to prune or update
|
||||
PROFILE_RANGE_EX(simulation_physics, "Prune/Send", 0x00000000, (uint64_t)_outgoingChanges.size());
|
||||
QSet<EntityMotionState*>::iterator stateItr = _outgoingChanges.begin();
|
||||
while (stateItr != _outgoingChanges.end()) {
|
||||
EntityMotionState* state = *stateItr;
|
||||
|
|
|
@ -11,7 +11,12 @@
|
|||
|
||||
#include <PhysicsCollisionGroups.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <QFile>
|
||||
|
||||
#include <PerfStat.h>
|
||||
#include <Profile.h>
|
||||
|
||||
#include "CharacterController.h"
|
||||
#include "ObjectMotionState.h"
|
||||
|
@ -290,6 +295,7 @@ void PhysicsEngine::stepSimulation() {
|
|||
float timeStep = btMin(dt, MAX_TIMESTEP);
|
||||
|
||||
if (_myAvatarController) {
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "avatarController");
|
||||
BT_PROFILE("avatarController");
|
||||
// TODO: move this stuff outside and in front of stepSimulation, because
|
||||
// the updateShapeIfNecessary() call needs info from MyAvatar and should
|
||||
|
@ -328,45 +334,107 @@ void PhysicsEngine::stepSimulation() {
|
|||
}
|
||||
}
|
||||
|
||||
class CProfileOperator {
|
||||
public:
|
||||
CProfileOperator() {}
|
||||
void recurse(CProfileIterator* itr, QString context) {
|
||||
// The context string will get too long if we accumulate it properly
|
||||
//QString newContext = context + QString("/") + itr->Get_Current_Parent_Name();
|
||||
// so we use this four-character indentation
|
||||
QString newContext = context + QString(".../");
|
||||
process(itr, newContext);
|
||||
|
||||
// count the children
|
||||
int32_t numChildren = 0;
|
||||
itr->First();
|
||||
while (!itr->Is_Done()) {
|
||||
itr->Next();
|
||||
++numChildren;
|
||||
}
|
||||
|
||||
// recurse the children
|
||||
if (numChildren > 0) {
|
||||
// recurse the children
|
||||
for (int32_t i = 0; i < numChildren; ++i) {
|
||||
itr->Enter_Child(i);
|
||||
recurse(itr, newContext);
|
||||
}
|
||||
}
|
||||
// retreat back to parent
|
||||
itr->Enter_Parent();
|
||||
}
|
||||
virtual void process(CProfileIterator*, QString context) = 0;
|
||||
};
|
||||
|
||||
class StatsHarvester : public CProfileOperator {
|
||||
public:
|
||||
StatsHarvester() {}
|
||||
void process(CProfileIterator* itr, QString context) override {
|
||||
QString name = context + itr->Get_Current_Parent_Name();
|
||||
uint64_t time = (uint64_t)((btScalar)MSECS_PER_SECOND * itr->Get_Current_Parent_Total_Time());
|
||||
PerformanceTimer::addTimerRecord(name, time);
|
||||
};
|
||||
};
|
||||
|
||||
class StatsWriter : public CProfileOperator {
|
||||
public:
|
||||
StatsWriter(QString filename) : _file(filename) {
|
||||
_file.open(QFile::WriteOnly);
|
||||
if (_file.error() != QFileDevice::NoError) {
|
||||
qCDebug(physics) << "unable to open file " << _file.fileName() << " to save stepSimulation() stats";
|
||||
}
|
||||
}
|
||||
~StatsWriter() {
|
||||
_file.close();
|
||||
}
|
||||
void process(CProfileIterator* itr, QString context) override {
|
||||
QString name = context + itr->Get_Current_Parent_Name();
|
||||
float time = (btScalar)MSECS_PER_SECOND * itr->Get_Current_Parent_Total_Time();
|
||||
if (_file.error() == QFileDevice::NoError) {
|
||||
QTextStream s(&_file);
|
||||
s << name << " = " << time << "\n";
|
||||
}
|
||||
}
|
||||
protected:
|
||||
QFile _file;
|
||||
};
|
||||
|
||||
void PhysicsEngine::harvestPerformanceStats() {
|
||||
// unfortunately the full context names get too long for our stats presentation format
|
||||
//QString contextName = PerformanceTimer::getContextName(); // TODO: how to show full context name?
|
||||
QString contextName("...");
|
||||
|
||||
CProfileIterator* profileIterator = CProfileManager::Get_Iterator();
|
||||
if (profileIterator) {
|
||||
CProfileIterator* itr = CProfileManager::Get_Iterator();
|
||||
if (itr) {
|
||||
// hunt for stepSimulation context
|
||||
profileIterator->First();
|
||||
for (int32_t childIndex = 0; !profileIterator->Is_Done(); ++childIndex) {
|
||||
if (QString(profileIterator->Get_Current_Name()) == "stepSimulation") {
|
||||
profileIterator->Enter_Child(childIndex);
|
||||
recursivelyHarvestPerformanceStats(profileIterator, contextName);
|
||||
itr->First();
|
||||
for (int32_t childIndex = 0; !itr->Is_Done(); ++childIndex) {
|
||||
if (QString(itr->Get_Current_Name()) == "stepSimulation") {
|
||||
itr->Enter_Child(childIndex);
|
||||
StatsHarvester harvester;
|
||||
harvester.recurse(itr, "step/");
|
||||
break;
|
||||
}
|
||||
profileIterator->Next();
|
||||
itr->Next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsEngine::recursivelyHarvestPerformanceStats(CProfileIterator* profileIterator, QString contextName) {
|
||||
QString parentContextName = contextName + QString("/") + QString(profileIterator->Get_Current_Parent_Name());
|
||||
// get the stats for the children
|
||||
int32_t numChildren = 0;
|
||||
profileIterator->First();
|
||||
while (!profileIterator->Is_Done()) {
|
||||
QString childContextName = parentContextName + QString("/") + QString(profileIterator->Get_Current_Name());
|
||||
uint64_t time = (uint64_t)((btScalar)MSECS_PER_SECOND * profileIterator->Get_Current_Total_Time());
|
||||
PerformanceTimer::addTimerRecord(childContextName, time);
|
||||
profileIterator->Next();
|
||||
++numChildren;
|
||||
void PhysicsEngine::printPerformanceStatsToFile(const QString& filename) {
|
||||
CProfileIterator* itr = CProfileManager::Get_Iterator();
|
||||
if (itr) {
|
||||
// hunt for stepSimulation context
|
||||
itr->First();
|
||||
for (int32_t childIndex = 0; !itr->Is_Done(); ++childIndex) {
|
||||
if (QString(itr->Get_Current_Name()) == "stepSimulation") {
|
||||
itr->Enter_Child(childIndex);
|
||||
StatsWriter writer(filename);
|
||||
writer.recurse(itr, "");
|
||||
break;
|
||||
}
|
||||
itr->Next();
|
||||
}
|
||||
}
|
||||
// recurse the children
|
||||
for (int32_t i = 0; i < numChildren; ++i) {
|
||||
profileIterator->Enter_Child(i);
|
||||
recursivelyHarvestPerformanceStats(profileIterator, contextName);
|
||||
}
|
||||
// retreat back to parent
|
||||
profileIterator->Enter_Parent();
|
||||
}
|
||||
|
||||
void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB) {
|
||||
|
@ -399,6 +467,7 @@ void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const
|
|||
}
|
||||
|
||||
void PhysicsEngine::updateContactMap() {
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "updateContactMap");
|
||||
BT_PROFILE("updateContactMap");
|
||||
++_numContactFrames;
|
||||
|
||||
|
@ -515,10 +584,21 @@ const VectorOfMotionStates& PhysicsEngine::getChangedMotionStates() {
|
|||
void PhysicsEngine::dumpStatsIfNecessary() {
|
||||
if (_dumpNextStats) {
|
||||
_dumpNextStats = false;
|
||||
CProfileManager::Increment_Frame_Counter();
|
||||
if (_saveNextStats) {
|
||||
_saveNextStats = false;
|
||||
printPerformanceStatsToFile(_statsFilename);
|
||||
}
|
||||
CProfileManager::dumpAll();
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsEngine::saveNextPhysicsStats(QString filename) {
|
||||
_saveNextStats = true;
|
||||
_dumpNextStats = true;
|
||||
_statsFilename = filename;
|
||||
}
|
||||
|
||||
// Bullet collision flags are as follows:
|
||||
// CF_STATIC_OBJECT= 1,
|
||||
// CF_KINEMATIC_OBJECT= 2,
|
||||
|
|
|
@ -62,6 +62,7 @@ public:
|
|||
|
||||
void stepSimulation();
|
||||
void harvestPerformanceStats();
|
||||
void printPerformanceStatsToFile(const QString& filename);
|
||||
void updateContactMap();
|
||||
|
||||
bool hasOutgoingChanges() const { return _hasOutgoingChanges; }
|
||||
|
@ -76,6 +77,9 @@ public:
|
|||
/// \brief prints timings for last frame if stats have been requested.
|
||||
void dumpStatsIfNecessary();
|
||||
|
||||
/// \brief saves timings for last frame in filename
|
||||
void saveNextPhysicsStats(QString filename);
|
||||
|
||||
/// \param offset position of simulation origin in domain-frame
|
||||
void setOriginOffset(const glm::vec3& offset) { _originOffset = offset; }
|
||||
|
||||
|
@ -94,7 +98,6 @@ public:
|
|||
private:
|
||||
QList<EntityDynamicPointer> removeDynamicsForBody(btRigidBody* body);
|
||||
void addObjectToDynamicsWorld(ObjectMotionState* motionState);
|
||||
void recursivelyHarvestPerformanceStats(CProfileIterator* profileIterator, QString contextName);
|
||||
|
||||
/// \brief bump any objects that touch this one, then remove contact info
|
||||
void bumpAndPruneContacts(ObjectMotionState* motionState);
|
||||
|
@ -116,6 +119,7 @@ private:
|
|||
QHash<QUuid, EntityDynamicPointer> _objectDynamics;
|
||||
QHash<btRigidBody*, QSet<QUuid>> _objectDynamicsByBody;
|
||||
std::set<btRigidBody*> _activeStaticBodies;
|
||||
QString _statsFilename;
|
||||
|
||||
glm::vec3 _originOffset;
|
||||
|
||||
|
@ -124,8 +128,9 @@ private:
|
|||
uint32_t _numContactFrames = 0;
|
||||
uint32_t _numSubsteps;
|
||||
|
||||
bool _dumpNextStats = false;
|
||||
bool _hasOutgoingChanges = false;
|
||||
bool _dumpNextStats { false };
|
||||
bool _saveNextStats { false };
|
||||
bool _hasOutgoingChanges { false };
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <LinearMath/btQuickprof.h>
|
||||
|
||||
#include "ThreadSafeDynamicsWorld.h"
|
||||
#include "Profile.h"
|
||||
|
||||
ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld(
|
||||
btDispatcher* dispatcher,
|
||||
|
@ -29,6 +30,7 @@ ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld(
|
|||
|
||||
int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep, int maxSubSteps,
|
||||
btScalar fixedTimeStep, SubStepCallback onSubStep) {
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "stepWithCB");
|
||||
BT_PROFILE("stepSimulationWithSubstepCallback");
|
||||
int subSteps = 0;
|
||||
if (maxSubSteps) {
|
||||
|
@ -68,11 +70,13 @@ int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep
|
|||
saveKinematicState(fixedTimeStep*clampedSimulationSteps);
|
||||
|
||||
{
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "applyGravity");
|
||||
BT_PROFILE("applyGravity");
|
||||
applyGravity();
|
||||
}
|
||||
|
||||
for (int i=0;i<clampedSimulationSteps;i++) {
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "substep");
|
||||
internalSingleStepSimulation(fixedTimeStep);
|
||||
onSubStep();
|
||||
}
|
||||
|
@ -118,7 +122,8 @@ void ThreadSafeDynamicsWorld::synchronizeMotionState(btRigidBody* body) {
|
|||
}
|
||||
|
||||
void ThreadSafeDynamicsWorld::synchronizeMotionStates() {
|
||||
BT_PROFILE("synchronizeMotionStates");
|
||||
PROFILE_RANGE(simulation_physics, "SyncMotionStates");
|
||||
BT_PROFILE("syncMotionStates");
|
||||
_changedMotionStates.clear();
|
||||
|
||||
// NOTE: m_synchronizeAllMotionStates is 'false' by default for optimization.
|
||||
|
@ -161,6 +166,7 @@ void ThreadSafeDynamicsWorld::saveKinematicState(btScalar timeStep) {
|
|||
///would like to iterate over m_nonStaticRigidBodies, but unfortunately old API allows
|
||||
///to switch status _after_ adding kinematic objects to the world
|
||||
///fix it for Bullet 3.x release
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "saveKinematicState");
|
||||
BT_PROFILE("saveKinematicState");
|
||||
for (int i=0;i<m_collisionObjects.size();i++)
|
||||
{
|
||||
|
|
|
@ -39,7 +39,7 @@ void DebugDeferredBufferConfig::setMode(int newMode) {
|
|||
emit dirty();
|
||||
}
|
||||
|
||||
enum Slot {
|
||||
enum TextureSlot {
|
||||
Albedo = 0,
|
||||
Normal,
|
||||
Specular,
|
||||
|
@ -56,7 +56,11 @@ enum Slot {
|
|||
AmbientOcclusionBlurred
|
||||
};
|
||||
|
||||
|
||||
enum ParamSlot {
|
||||
CameraCorrection = 0,
|
||||
DeferredFrameTransform,
|
||||
ShadowTransform
|
||||
};
|
||||
|
||||
static const std::string DEFAULT_ALBEDO_SHADER {
|
||||
"vec4 getFragmentColor() {"
|
||||
|
@ -127,12 +131,14 @@ static const std::string DEFAULT_DEPTH_SHADER {
|
|||
" return vec4(vec3(texture(depthMap, uv).x), 1.0);"
|
||||
" }"
|
||||
};
|
||||
|
||||
static const std::string DEFAULT_LIGHTING_SHADER {
|
||||
"vec4 getFragmentColor() {"
|
||||
" return vec4(pow(texture(lightingMap, uv).xyz, vec3(1.0 / 2.2)), 1.0);"
|
||||
" }"
|
||||
};
|
||||
static const std::string DEFAULT_SHADOW_SHADER {
|
||||
|
||||
static const std::string DEFAULT_SHADOW_SHADER{
|
||||
"uniform sampler2DShadow shadowMap;"
|
||||
"vec4 getFragmentColor() {"
|
||||
" for (int i = 255; i >= 0; --i) {"
|
||||
|
@ -145,10 +151,31 @@ static const std::string DEFAULT_SHADOW_SHADER {
|
|||
" }"
|
||||
};
|
||||
|
||||
static const std::string DEFAULT_SHADOW_CASCADE_SHADER{
|
||||
"vec3 cascadeColors[4] = vec3[4]( vec3(0,1,0), vec3(0,0,1), vec3(1,0,0), vec3(1) );"
|
||||
"vec4 getFragmentColor() {"
|
||||
" DeferredFrameTransform deferredTransform = getDeferredFrameTransform();"
|
||||
" DeferredFragment frag = unpackDeferredFragment(deferredTransform, uv);"
|
||||
" vec4 viewPosition = vec4(frag.position.xyz, 1.0);"
|
||||
" float viewDepth = -viewPosition.z;"
|
||||
" vec4 worldPosition = getViewInverse() * viewPosition;"
|
||||
" vec4 cascadeShadowCoords[2];"
|
||||
" ivec2 cascadeIndices;"
|
||||
" float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices);"
|
||||
" vec3 firstCascadeColor = cascadeColors[cascadeIndices.x];"
|
||||
" vec3 secondCascadeColor = cascadeColors[cascadeIndices.x];"
|
||||
" if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) {"
|
||||
" secondCascadeColor = cascadeColors[cascadeIndices.y];"
|
||||
" }"
|
||||
" vec3 color = mix(firstCascadeColor, secondCascadeColor, cascadeMix);"
|
||||
" return vec4(mix(vec3(0.0), color, evalShadowFalloff(viewDepth)), 1.0);"
|
||||
"}"
|
||||
};
|
||||
|
||||
static const std::string DEFAULT_LINEAR_DEPTH_SHADER {
|
||||
"vec4 getFragmentColor() {"
|
||||
" return vec4(vec3(1.0 - texture(linearDepthMap, uv).x * 0.01), 1.0);"
|
||||
" }"
|
||||
"}"
|
||||
};
|
||||
|
||||
static const std::string DEFAULT_HALF_LINEAR_DEPTH_SHADER{
|
||||
|
@ -285,8 +312,13 @@ std::string DebugDeferredBuffer::getShaderSourceCode(Mode mode, std::string cust
|
|||
return DEFAULT_SCATTERING_SHADER;
|
||||
case LightingMode:
|
||||
return DEFAULT_LIGHTING_SHADER;
|
||||
case ShadowMode:
|
||||
case ShadowCascade0Mode:
|
||||
case ShadowCascade1Mode:
|
||||
case ShadowCascade2Mode:
|
||||
case ShadowCascade3Mode:
|
||||
return DEFAULT_SHADOW_SHADER;
|
||||
case ShadowCascadeIndicesMode:
|
||||
return DEFAULT_SHADOW_CASCADE_SHADER;
|
||||
case LinearDepthMode:
|
||||
return DEFAULT_LINEAR_DEPTH_SHADER;
|
||||
case HalfLinearDepthMode:
|
||||
|
@ -353,6 +385,10 @@ const gpu::PipelinePointer& DebugDeferredBuffer::getPipeline(Mode mode, std::str
|
|||
const auto program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding("cameraCorrectionBuffer", CameraCorrection));
|
||||
slotBindings.insert(gpu::Shader::Binding("deferredFrameTransformBuffer", DeferredFrameTransform));
|
||||
slotBindings.insert(gpu::Shader::Binding("shadowTransformBuffer", ShadowTransform));
|
||||
|
||||
slotBindings.insert(gpu::Shader::Binding("albedoMap", Albedo));
|
||||
slotBindings.insert(gpu::Shader::Binding("normalMap", Normal));
|
||||
slotBindings.insert(gpu::Shader::Binding("specularMap", Specular));
|
||||
|
@ -404,6 +440,7 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I
|
|||
auto& linearDepthTarget = inputs.get1();
|
||||
auto& surfaceGeometryFramebuffer = inputs.get2();
|
||||
auto& ambientOcclusionFramebuffer = inputs.get3();
|
||||
auto& frameTransform = inputs.get4();
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
|
@ -422,8 +459,8 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I
|
|||
|
||||
// TODO REMOVE: Temporary until UI
|
||||
auto first = _customPipelines.begin()->first;
|
||||
|
||||
batch.setPipeline(getPipeline(_mode, first));
|
||||
auto pipeline = getPipeline(_mode, first);
|
||||
batch.setPipeline(pipeline);
|
||||
|
||||
if (deferredFramebuffer) {
|
||||
batch.setResourceTexture(Albedo, deferredFramebuffer->getDeferredColorTexture());
|
||||
|
@ -439,7 +476,10 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I
|
|||
auto lightAndShadow = lightStage->getCurrentKeyLightAndShadow();
|
||||
const auto& globalShadow = lightAndShadow.second;
|
||||
if (globalShadow) {
|
||||
batch.setResourceTexture(Shadow, globalShadow->map);
|
||||
const auto cascadeIndex = glm::clamp(_mode - Mode::ShadowCascade0Mode, 0, (int)globalShadow->getCascadeCount() - 1);
|
||||
batch.setResourceTexture(Shadow, globalShadow->getCascade(cascadeIndex).map);
|
||||
batch.setUniformBuffer(ShadowTransform, globalShadow->getBuffer());
|
||||
batch.setUniformBuffer(DeferredFrameTransform, frameTransform->getFrameTransformBuffer());
|
||||
}
|
||||
|
||||
if (linearDepthTarget) {
|
||||
|
@ -460,7 +500,6 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I
|
|||
const glm::vec2 topRight(_size.z, _size.w);
|
||||
geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryId);
|
||||
|
||||
|
||||
batch.setResourceTexture(Albedo, nullptr);
|
||||
batch.setResourceTexture(Normal, nullptr);
|
||||
batch.setResourceTexture(Specular, nullptr);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <QFileInfo>
|
||||
|
||||
#include <render/DrawTask.h>
|
||||
#include "DeferredFrameTransform.h"
|
||||
#include "DeferredFramebuffer.h"
|
||||
#include "SurfaceGeometryPass.h"
|
||||
#include "AmbientOcclusionEffect.h"
|
||||
|
@ -37,7 +38,7 @@ signals:
|
|||
|
||||
class DebugDeferredBuffer {
|
||||
public:
|
||||
using Inputs = render::VaryingSet4<DeferredFramebufferPointer, LinearDepthFramebufferPointer, SurfaceGeometryFramebufferPointer, AmbientOcclusionFramebufferPointer>;
|
||||
using Inputs = render::VaryingSet5<DeferredFramebufferPointer, LinearDepthFramebufferPointer, SurfaceGeometryFramebufferPointer, AmbientOcclusionFramebufferPointer, DeferredFrameTransformPointer>;
|
||||
using Config = DebugDeferredBufferConfig;
|
||||
using JobModel = render::Job::ModelI<DebugDeferredBuffer, Inputs, Config>;
|
||||
|
||||
|
@ -64,7 +65,11 @@ protected:
|
|||
LightmapMode,
|
||||
ScatteringMode,
|
||||
LightingMode,
|
||||
ShadowMode,
|
||||
ShadowCascade0Mode,
|
||||
ShadowCascade1Mode,
|
||||
ShadowCascade2Mode,
|
||||
ShadowCascade3Mode,
|
||||
ShadowCascadeIndicesMode,
|
||||
LinearDepthMode,
|
||||
HalfLinearDepthMode,
|
||||
HalfNormalMode,
|
||||
|
|
|
@ -58,7 +58,7 @@ enum DeferredShader_MapSlot {
|
|||
DEFERRED_BUFFER_DEPTH_UNIT = 3,
|
||||
DEFERRED_BUFFER_OBSCURANCE_UNIT = 4,
|
||||
SHADOW_MAP_UNIT = 5,
|
||||
SKYBOX_MAP_UNIT = 6,
|
||||
SKYBOX_MAP_UNIT = SHADOW_MAP_UNIT + SHADOW_CASCADE_MAX_COUNT,
|
||||
DEFERRED_BUFFER_LINEAR_DEPTH_UNIT,
|
||||
DEFERRED_BUFFER_CURVATURE_UNIT,
|
||||
DEFERRED_BUFFER_DIFFUSED_CURVATURE_UNIT,
|
||||
|
@ -156,7 +156,7 @@ static gpu::ShaderPointer makeLightProgram(const char* vertSource, const char* f
|
|||
slotBindings.insert(gpu::Shader::Binding(std::string("specularMap"), DEFERRED_BUFFER_EMISSIVE_UNIT));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("depthMap"), DEFERRED_BUFFER_DEPTH_UNIT));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("obscuranceMap"), DEFERRED_BUFFER_OBSCURANCE_UNIT));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("shadowMap"), SHADOW_MAP_UNIT));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("shadowMaps"), SHADOW_MAP_UNIT));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), SKYBOX_MAP_UNIT));
|
||||
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("linearZeyeMap"), DEFERRED_BUFFER_LINEAR_DEPTH_UNIT));
|
||||
|
@ -501,9 +501,11 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext,
|
|||
auto lightAndShadow = lightStage->getCurrentKeyLightAndShadow();
|
||||
const auto& globalShadow = lightAndShadow.second;
|
||||
|
||||
// Bind the shadow buffer
|
||||
// Bind the shadow buffers
|
||||
if (globalShadow) {
|
||||
batch.setResourceTexture(SHADOW_MAP_UNIT, globalShadow->map);
|
||||
for (unsigned int i = 0; i < globalShadow->getCascadeCount(); i++) {
|
||||
batch.setResourceTexture(SHADOW_MAP_UNIT+i, globalShadow->getCascade(i).map);
|
||||
}
|
||||
}
|
||||
|
||||
auto& program = deferredLightingEffect->_directionalSkyboxLight;
|
||||
|
@ -567,8 +569,9 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext,
|
|||
|
||||
deferredLightingEffect->unsetKeyLightBatch(batch, locations->lightBufferUnit, locations->ambientBufferUnit, SKYBOX_MAP_UNIT);
|
||||
|
||||
|
||||
batch.setResourceTexture(SHADOW_MAP_UNIT, nullptr);
|
||||
for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) {
|
||||
batch.setResourceTexture(SHADOW_MAP_UNIT+i, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -169,7 +169,12 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu
|
|||
auto hazeStage = args->_scene->getStage<HazeStage>();
|
||||
if (hazeStage && hazeStage->_currentFrame._hazes.size() > 0) {
|
||||
model::HazePointer hazePointer = hazeStage->getHaze(hazeStage->_currentFrame._hazes.front());
|
||||
batch.setUniformBuffer(HazeEffect_ParamsSlot, hazePointer->getHazeParametersBuffer());
|
||||
if (hazePointer) {
|
||||
batch.setUniformBuffer(HazeEffect_ParamsSlot, hazePointer->getHazeParametersBuffer());
|
||||
} else {
|
||||
// Something is wrong, so just quit Haze
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
batch.setUniformBuffer(HazeEffect_TransformBufferSlot, transformBuffer->getFrameTransformBuffer());
|
||||
|
@ -178,7 +183,7 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu
|
|||
if (lightStage) {
|
||||
model::LightPointer keyLight;
|
||||
keyLight = lightStage->getCurrentKeyLight();
|
||||
if (keyLight != nullptr) {
|
||||
if (keyLight) {
|
||||
batch.setUniformBuffer(HazeEffect_LightingMapSlot, keyLight->getLightSchemaBuffer());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,6 +74,32 @@ static std::array<GeometryCache::Shape, (GeometryCache::NUM_SHAPES - 1)> MAPPING
|
|||
GeometryCache::Cylinder,
|
||||
} };
|
||||
|
||||
/**jsdoc
|
||||
* <p>{@link Entities} and {@link Overlays} may have the following geometrical shapes:</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>Line</code></td><td>A 1D line oriented in 3 dimensions.</td></tr>
|
||||
* <tr><td><code>Triangle</code></td><td>A triangular prism.</td></tr>
|
||||
* <tr><td><code>Quad</code></td><td>A 2D square oriented in 3 dimensions.</tr>
|
||||
* <tr><td><code>Hexagon</code></td><td>A hexagonal prism.</td></tr>
|
||||
* <tr><td><code>Octagon</code></td><td>An octagonal prism.</td></tr>
|
||||
* <tr><td><code>Circle</code></td><td>A 2D circle oriented in 3 dimensions.</td></td></tr>
|
||||
* <tr><td><code>Cube</code></td><td>A cube.</td></tr>
|
||||
* <tr><td><code>Sphere</code></td><td>A sphere.</td></tr>
|
||||
* <tr><td><code>Tetrahedron</code></td><td>A tetrahedron.</td></tr>
|
||||
* <tr><td><code>Octahedron</code></td><td>An octahedron.</td></tr>
|
||||
* <tr><td><code>Dodecahedron</code></td><td>A dodecahedron.</td></tr>
|
||||
* <tr><td><code>Icosahedron</code></td><td>An icosahedron.</td></tr>
|
||||
* <tr><td><code>Torus</code></td><td>A torus. <em>Not implemented.</em></td></tr>
|
||||
* <tr><td><code>Cone</code></td><td>A cone.</td></tr>
|
||||
* <tr><td><code>Cylinder</code></td><td>A cylinder.</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {string} Shape
|
||||
*/
|
||||
static const std::array<const char * const, GeometryCache::NUM_SHAPES> GEOCACHE_SHAPE_STRINGS{ {
|
||||
"Line",
|
||||
"Triangle",
|
||||
|
|
|
@ -13,65 +13,202 @@
|
|||
|
||||
#include "LightStage.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
std::string LightStage::_stageName { "LIGHT_STAGE"};
|
||||
const glm::mat4 LightStage::Shadow::_biasMatrix{
|
||||
0.5, 0.0, 0.0, 0.0,
|
||||
0.0, 0.5, 0.0, 0.0,
|
||||
0.0, 0.0, 0.5, 0.0,
|
||||
0.5, 0.5, 0.5, 1.0 };
|
||||
const int LightStage::Shadow::MAP_SIZE = 1024;
|
||||
|
||||
static const auto MAX_BIAS = 0.006f;
|
||||
|
||||
const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
|
||||
|
||||
LightStage::LightStage() {
|
||||
}
|
||||
|
||||
LightStage::Shadow::Schema::Schema() :
|
||||
bias{ 0.005f },
|
||||
scale{ 1.0f / MAP_SIZE } {
|
||||
|
||||
LightStage::Shadow::Schema::Schema() {
|
||||
ShadowTransform defaultTransform;
|
||||
defaultTransform.bias = MAX_BIAS;
|
||||
std::fill(cascades, cascades + SHADOW_CASCADE_MAX_COUNT, defaultTransform);
|
||||
invMapSize = 1.0f / MAP_SIZE;
|
||||
cascadeCount = 1;
|
||||
invCascadeBlendWidth = 1.0f / 0.2f;
|
||||
invFalloffDistance = 1.0f / 2.0f;
|
||||
maxDistance = 20.0f;
|
||||
}
|
||||
|
||||
gpu::FramebufferPointer LightStage::Shadow::framebuffer;
|
||||
gpu::TexturePointer LightStage::Shadow::map;
|
||||
LightStage::Shadow::Cascade::Cascade() :
|
||||
_frustum{ std::make_shared<ViewFrustum>() },
|
||||
_minDistance{ 0.0f },
|
||||
_maxDistance{ 20.0f } {
|
||||
framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::createShadowmap(MAP_SIZE));
|
||||
map = framebuffer->getDepthStencilBuffer();
|
||||
}
|
||||
|
||||
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);
|
||||
const glm::mat4& LightStage::Shadow::Cascade::getView() const {
|
||||
return _frustum->getView();
|
||||
}
|
||||
|
||||
if (!framebuffer) {
|
||||
framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::createShadowmap(MAP_SIZE));
|
||||
map = framebuffer->getDepthStencilBuffer();
|
||||
const glm::mat4& LightStage::Shadow::Cascade::getProjection() const {
|
||||
return _frustum->getProjection();
|
||||
}
|
||||
|
||||
float LightStage::Shadow::Cascade::computeFarDistance(const ViewFrustum& viewFrustum, const Transform& shadowViewInverse,
|
||||
float left, float right, float bottom, float top, float viewMaxShadowDistance) const {
|
||||
// Far distance should be extended to the intersection of the infinitely extruded shadow frustum
|
||||
// with the view frustum side planes. To do so, we generate 10 triangles in shadow space which are the result of
|
||||
// tesselating the side and far faces of the view frustum and clip them with the 4 side planes of the
|
||||
// shadow frustum. The resulting clipped triangle vertices with the farthest Z gives the desired
|
||||
// shadow frustum far distance.
|
||||
std::array<Triangle, 10> viewFrustumTriangles;
|
||||
Plane shadowClipPlanes[4] = {
|
||||
Plane(glm::vec3(0.0f, -1.0f, 0.0f), glm::vec3(0.0f, top, 0.0f)),
|
||||
Plane(glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, bottom, 0.0f)),
|
||||
Plane(glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(left, 0.0f, 0.0f)),
|
||||
Plane(glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(right, 0.0f, 0.0f))
|
||||
};
|
||||
|
||||
viewFrustum.tesselateSidesAndFar(shadowViewInverse, viewFrustumTriangles.data(), viewMaxShadowDistance);
|
||||
|
||||
static const int MAX_TRIANGLE_COUNT = 16;
|
||||
auto far = 0.0f;
|
||||
|
||||
for (auto& triangle : viewFrustumTriangles) {
|
||||
Triangle clippedTriangles[MAX_TRIANGLE_COUNT];
|
||||
auto clippedTriangleCount = clipTriangleWithPlanes(triangle, shadowClipPlanes, 4, clippedTriangles, MAX_TRIANGLE_COUNT);
|
||||
|
||||
for (auto i = 0; i < clippedTriangleCount; i++) {
|
||||
const auto& clippedTriangle = clippedTriangles[i];
|
||||
far = glm::max(far, -clippedTriangle.v0.z);
|
||||
far = glm::max(far, -clippedTriangle.v1.z);
|
||||
far = glm::max(far, -clippedTriangle.v2.z);
|
||||
}
|
||||
}
|
||||
|
||||
return far;
|
||||
}
|
||||
|
||||
void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum,
|
||||
float viewMinShadowDistance, float viewMaxShadowDistance,
|
||||
float nearDepth, float farDepth) {
|
||||
assert(viewMinShadowDistance < viewMaxShadowDistance);
|
||||
assert(nearDepth < farDepth);
|
||||
LightStage::Shadow::Shadow(model::LightPointer light, float maxDistance, unsigned int cascadeCount) :
|
||||
_light{ light } {
|
||||
cascadeCount = std::min(cascadeCount, (unsigned int)SHADOW_CASCADE_MAX_COUNT);
|
||||
Schema schema;
|
||||
schema.cascadeCount = cascadeCount;
|
||||
_schemaBuffer = std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema);
|
||||
_cascades.resize(cascadeCount);
|
||||
|
||||
setMaxDistance(maxDistance);
|
||||
}
|
||||
|
||||
void LightStage::Shadow::setMaxDistance(float value) {
|
||||
// This overlaping factor isn't really used directly for blending of shadow cascades. It
|
||||
// just there to be sure the cascades do overlap. The blending width used is relative
|
||||
// to the UV space and is set in the Schema with invCascadeBlendWidth.
|
||||
static const auto OVERLAP_FACTOR = 1.0f / 5.0f;
|
||||
|
||||
_maxDistance = std::max(0.0f, value);
|
||||
|
||||
if (_cascades.size() == 1) {
|
||||
_cascades.front().setMinDistance(0.0f);
|
||||
_cascades.front().setMaxDistance(_maxDistance);
|
||||
} else {
|
||||
// Distribute the cascades along that distance
|
||||
// TODO : these parameters should be exposed to the user as part of the light entity parameters, no?
|
||||
static const auto LOW_MAX_DISTANCE = 2.0f;
|
||||
static const auto MAX_RESOLUTION_LOSS = 0.6f; // Between 0 and 1, 0 giving tighter distributions
|
||||
|
||||
// The max cascade distance is computed by multiplying the previous cascade's max distance by a certain
|
||||
// factor. There is a "user" factor that is computed from a desired max resolution loss in the shadow
|
||||
// and an optimal one based on the global min and max shadow distance, all cascades considered. The final
|
||||
// distance is a gradual blend between the two
|
||||
const auto userDistanceScale = 1.0f / (1.0f - MAX_RESOLUTION_LOSS);
|
||||
const auto optimalDistanceScale = powf(_maxDistance / LOW_MAX_DISTANCE, 1.0f / (_cascades.size() - 1));
|
||||
|
||||
float maxCascadeUserDistance = LOW_MAX_DISTANCE;
|
||||
float maxCascadeOptimalDistance = LOW_MAX_DISTANCE;
|
||||
float minCascadeDistance = 0.0f;
|
||||
|
||||
for (size_t cascadeIndex = 0; cascadeIndex < _cascades.size(); ++cascadeIndex) {
|
||||
float blendFactor = cascadeIndex / float(_cascades.size() - 1);
|
||||
float maxCascadeDistance;
|
||||
|
||||
if (cascadeIndex == size_t(_cascades.size() - 1)) {
|
||||
maxCascadeDistance = _maxDistance;
|
||||
} else {
|
||||
maxCascadeDistance = maxCascadeUserDistance + (maxCascadeOptimalDistance - maxCascadeUserDistance)*blendFactor*blendFactor;
|
||||
}
|
||||
|
||||
float shadowOverlapDistance = maxCascadeDistance * OVERLAP_FACTOR;
|
||||
|
||||
_cascades[cascadeIndex].setMinDistance(minCascadeDistance);
|
||||
_cascades[cascadeIndex].setMaxDistance(maxCascadeDistance + shadowOverlapDistance);
|
||||
|
||||
// Compute distances for next cascade
|
||||
minCascadeDistance = maxCascadeDistance;
|
||||
maxCascadeUserDistance = maxCascadeUserDistance * userDistanceScale;
|
||||
maxCascadeOptimalDistance = maxCascadeOptimalDistance * optimalDistanceScale;
|
||||
maxCascadeUserDistance = std::min(maxCascadeUserDistance, _maxDistance);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the buffer
|
||||
const auto& lastCascade = _cascades.back();
|
||||
auto& schema = _schemaBuffer.edit<Schema>();
|
||||
schema.maxDistance = _maxDistance;
|
||||
schema.invFalloffDistance = 1.0f / (OVERLAP_FACTOR*lastCascade.getMaxDistance());
|
||||
}
|
||||
|
||||
void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum,
|
||||
float nearDepth, float farDepth) {
|
||||
assert(nearDepth < farDepth);
|
||||
// Orient the keylight frustum
|
||||
const auto& direction = glm::normalize(_light->getDirection());
|
||||
auto lightDirection = glm::normalize(_light->getDirection());
|
||||
glm::quat orientation;
|
||||
if (direction == IDENTITY_UP) {
|
||||
if (lightDirection == IDENTITY_UP) {
|
||||
orientation = glm::quat(glm::mat3(-IDENTITY_RIGHT, IDENTITY_FORWARD, -IDENTITY_UP));
|
||||
} else if (direction == -IDENTITY_UP) {
|
||||
} else if (lightDirection == -IDENTITY_UP) {
|
||||
orientation = glm::quat(glm::mat3(IDENTITY_RIGHT, IDENTITY_FORWARD, IDENTITY_UP));
|
||||
} else {
|
||||
auto side = glm::normalize(glm::cross(direction, IDENTITY_UP));
|
||||
auto up = glm::normalize(glm::cross(side, direction));
|
||||
orientation = glm::quat_cast(glm::mat3(side, up, -direction));
|
||||
auto side = glm::normalize(glm::cross(lightDirection, IDENTITY_UP));
|
||||
auto up = glm::normalize(glm::cross(side, lightDirection));
|
||||
orientation = glm::quat_cast(glm::mat3(side, up, -lightDirection));
|
||||
}
|
||||
_frustum->setOrientation(orientation);
|
||||
|
||||
// Position the keylight frustum
|
||||
_frustum->setPosition(viewFrustum.getPosition() - (nearDepth + farDepth)*direction);
|
||||
auto position = viewFrustum.getPosition() - (nearDepth + farDepth)*lightDirection;
|
||||
for (auto& cascade : _cascades) {
|
||||
cascade._frustum->setOrientation(orientation);
|
||||
cascade._frustum->setPosition(position);
|
||||
}
|
||||
// Update the buffer
|
||||
auto& schema = _schemaBuffer.edit<Schema>();
|
||||
schema.lightDirInViewSpace = glm::inverse(viewFrustum.getView()) * glm::vec4(lightDirection, 0.f);
|
||||
}
|
||||
|
||||
const Transform view{ _frustum->getView()};
|
||||
const Transform viewInverse{ view.getInverseMatrix() };
|
||||
void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum,
|
||||
float nearDepth, float farDepth) {
|
||||
assert(nearDepth < farDepth);
|
||||
assert(cascadeIndex < _cascades.size());
|
||||
|
||||
auto nearCorners = viewFrustum.getCorners(viewMinShadowDistance);
|
||||
auto farCorners = viewFrustum.getCorners(viewMaxShadowDistance);
|
||||
auto& cascade = _cascades[cascadeIndex];
|
||||
const auto viewMinCascadeShadowDistance = std::max(viewFrustum.getNearClip(), cascade.getMinDistance());
|
||||
const auto viewMaxCascadeShadowDistance = std::min(viewFrustum.getFarClip(), cascade.getMaxDistance());
|
||||
const auto viewMaxShadowDistance = _cascades.back().getMaxDistance();
|
||||
|
||||
vec3 min{ viewInverse.transform(nearCorners.bottomLeft) };
|
||||
const Transform shadowView{ cascade._frustum->getView()};
|
||||
const Transform shadowViewInverse{ shadowView.getInverseMatrix() };
|
||||
|
||||
auto nearCorners = viewFrustum.getCorners(viewMinCascadeShadowDistance);
|
||||
auto farCorners = viewFrustum.getCorners(viewMaxCascadeShadowDistance);
|
||||
|
||||
vec3 min{ shadowViewInverse.transform(nearCorners.bottomLeft) };
|
||||
vec3 max{ min };
|
||||
// Expand keylight frustum to fit view frustum
|
||||
auto fitFrustum = [&min, &max, &viewInverse](const vec3& viewCorner) {
|
||||
const auto corner = viewInverse.transform(viewCorner);
|
||||
auto fitFrustum = [&min, &max, &shadowViewInverse](const vec3& viewCorner) {
|
||||
const auto corner = shadowViewInverse.transform(viewCorner);
|
||||
|
||||
min.x = glm::min(min.x, corner.x);
|
||||
min.y = glm::min(min.y, corner.y);
|
||||
|
@ -89,36 +226,35 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum,
|
|||
fitFrustum(farCorners.topLeft);
|
||||
fitFrustum(farCorners.topRight);
|
||||
|
||||
// Re-adjust near shadow distance
|
||||
auto near = glm::max(max.z, -nearDepth);
|
||||
auto far = -min.z;
|
||||
// Re-adjust near and far shadow distance
|
||||
auto near = glm::min(-max.z, nearDepth);
|
||||
auto far = cascade.computeFarDistance(viewFrustum, shadowViewInverse, min.x, max.x, min.y, max.y, viewMaxShadowDistance);
|
||||
|
||||
glm::mat4 ortho = glm::ortho<float>(min.x, max.x, min.y, max.y, near, far);
|
||||
_frustum->setProjection(ortho);
|
||||
cascade._frustum->setProjection(ortho);
|
||||
|
||||
// Calculate the frustum's internal state
|
||||
_frustum->calculate();
|
||||
cascade._frustum->calculate();
|
||||
|
||||
// Update the buffer
|
||||
_schemaBuffer.edit<Schema>().projection = ortho;
|
||||
_schemaBuffer.edit<Schema>().viewInverse = viewInverse.getMatrix();
|
||||
auto& schema = _schemaBuffer.edit<Schema>();
|
||||
schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix();
|
||||
// Adapt shadow bias to shadow resolution with a totally empirical formula
|
||||
const auto maxShadowFrustumDim = std::max(fabsf(min.x - max.x), fabsf(min.y - max.y));
|
||||
const auto REFERENCE_TEXEL_DENSITY = 7.5f;
|
||||
const auto cascadeTexelDensity = MAP_SIZE / maxShadowFrustumDim;
|
||||
schema.cascades[cascadeIndex].bias = MAX_BIAS * std::min(1.0f, REFERENCE_TEXEL_DENSITY / cascadeTexelDensity);
|
||||
}
|
||||
|
||||
void LightStage::Shadow::setFrustum(const ViewFrustum& shadowFrustum) {
|
||||
void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) {
|
||||
assert(cascadeIndex < _cascades.size());
|
||||
const Transform view{ shadowFrustum.getView() };
|
||||
const Transform viewInverse{ view.getInverseMatrix() };
|
||||
auto& cascade = _cascades[cascadeIndex];
|
||||
|
||||
*_frustum = shadowFrustum;
|
||||
*cascade._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();
|
||||
}
|
||||
|
||||
const glm::mat4& LightStage::Shadow::getProjection() const {
|
||||
return _frustum->getProjection();
|
||||
_schemaBuffer.edit<Schema>().cascades[cascadeIndex].reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix();
|
||||
}
|
||||
|
||||
LightStage::Index LightStage::findLight(const LightPointer& light) const {
|
||||
|
@ -142,7 +278,7 @@ LightStage::Index LightStage::addLight(const LightPointer& light) {
|
|||
_descs.emplace_back(Desc());
|
||||
} else {
|
||||
assert(_descs[lightId].shadowId == INVALID_INDEX);
|
||||
_descs.emplace(_descs.begin() + lightId, Desc());
|
||||
_descs[lightId] = Desc();
|
||||
}
|
||||
|
||||
// INsert the light and its index in the reverese map
|
||||
|
@ -156,12 +292,12 @@ LightStage::Index LightStage::addLight(const LightPointer& light) {
|
|||
}
|
||||
}
|
||||
|
||||
LightStage::Index LightStage::addShadow(Index lightIndex) {
|
||||
LightStage::Index LightStage::addShadow(Index lightIndex, float maxDistance, unsigned int cascadeCount) {
|
||||
auto light = getLight(lightIndex);
|
||||
Index shadowId = INVALID_INDEX;
|
||||
if (light) {
|
||||
assert(_descs[lightIndex].shadowId == INVALID_INDEX);
|
||||
shadowId = _shadows.newElement(std::make_shared<Shadow>(light));
|
||||
shadowId = _shadows.newElement(std::make_shared<Shadow>(light, maxDistance, cascadeCount));
|
||||
_descs[lightIndex].shadowId = shadowId;
|
||||
}
|
||||
return shadowId;
|
||||
|
|
|
@ -44,45 +44,74 @@ public:
|
|||
class Shadow {
|
||||
public:
|
||||
using UniformBufferView = gpu::BufferView;
|
||||
static const int MAP_SIZE = 1024;
|
||||
static const int MAP_SIZE;
|
||||
|
||||
Shadow(model::LightPointer light);
|
||||
class Cascade {
|
||||
friend Shadow;
|
||||
public:
|
||||
|
||||
void setKeylightFrustum(const ViewFrustum& viewFrustum, float viewMinShadowDistance, float viewMaxShadowDistance, float nearDepth = 1.0f, float farDepth = 1000.0f);
|
||||
Cascade();
|
||||
|
||||
void setFrustum(const ViewFrustum& shadowFrustum);
|
||||
const std::shared_ptr<ViewFrustum> getFrustum() const { return _frustum; }
|
||||
gpu::FramebufferPointer framebuffer;
|
||||
gpu::TexturePointer map;
|
||||
|
||||
const glm::mat4& getView() const;
|
||||
const glm::mat4& getProjection() const;
|
||||
const std::shared_ptr<ViewFrustum>& getFrustum() const { return _frustum; }
|
||||
|
||||
const glm::mat4& getView() const;
|
||||
const glm::mat4& getProjection() const;
|
||||
|
||||
void setMinDistance(float value) { _minDistance = value; }
|
||||
void setMaxDistance(float value) { _maxDistance = value; }
|
||||
float getMinDistance() const { return _minDistance; }
|
||||
float getMaxDistance() const { return _maxDistance; }
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<ViewFrustum> _frustum;
|
||||
float _minDistance;
|
||||
float _maxDistance;
|
||||
|
||||
float computeFarDistance(const ViewFrustum& viewFrustum, const Transform& shadowViewInverse,
|
||||
float left, float right, float bottom, float top, float viewMaxShadowDistance) const;
|
||||
};
|
||||
|
||||
Shadow(model::LightPointer light, float maxDistance, unsigned int cascadeCount = 1);
|
||||
|
||||
void setKeylightFrustum(const ViewFrustum& viewFrustum,
|
||||
float nearDepth = 1.0f, float farDepth = 1000.0f);
|
||||
void setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum,
|
||||
float nearDepth = 1.0f, float farDepth = 1000.0f);
|
||||
void setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum);
|
||||
|
||||
const UniformBufferView& getBuffer() const { return _schemaBuffer; }
|
||||
|
||||
// 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;
|
||||
unsigned int getCascadeCount() const { return (unsigned int)_cascades.size(); }
|
||||
const Cascade& getCascade(unsigned int index) const { return _cascades[index]; }
|
||||
|
||||
float getMaxDistance() const { return _maxDistance; }
|
||||
void setMaxDistance(float value);
|
||||
|
||||
const model::LightPointer& getLight() const { return _light; }
|
||||
|
||||
protected:
|
||||
|
||||
model::LightPointer _light;
|
||||
std::shared_ptr<ViewFrustum> _frustum;
|
||||
#include "Shadows_shared.slh"
|
||||
|
||||
class Schema {
|
||||
using Cascades = std::vector<Cascade>;
|
||||
|
||||
static const glm::mat4 _biasMatrix;
|
||||
|
||||
model::LightPointer _light;
|
||||
float _maxDistance;
|
||||
Cascades _cascades;
|
||||
|
||||
class Schema : public ShadowParameters {
|
||||
public:
|
||||
|
||||
Schema();
|
||||
|
||||
glm::mat4 projection;
|
||||
glm::mat4 viewInverse;
|
||||
|
||||
glm::float32 bias;
|
||||
glm::float32 scale;
|
||||
};
|
||||
UniformBufferView _schemaBuffer = nullptr;
|
||||
|
||||
};
|
||||
|
||||
using ShadowPointer = std::shared_ptr<Shadow>;
|
||||
|
@ -91,7 +120,7 @@ public:
|
|||
Index findLight(const LightPointer& light) const;
|
||||
Index addLight(const LightPointer& light);
|
||||
|
||||
Index addShadow(Index lightIndex);
|
||||
Index addShadow(Index lightIndex, float maxDistance = 20.0f, unsigned int cascadeCount = 1U);
|
||||
|
||||
LightPointer removeLight(Index index);
|
||||
|
||||
|
|
|
@ -205,34 +205,23 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
|||
task.addJob<DrawBounds>("DrawZones", zones);
|
||||
const auto frustums = task.addJob<ExtractFrustums>("ExtractFrustums");
|
||||
const auto viewFrustum = frustums.getN<ExtractFrustums::Output>(ExtractFrustums::VIEW_FRUSTUM);
|
||||
const auto shadowFrustum = frustums.getN<ExtractFrustums::Output>(ExtractFrustums::SHADOW_FRUSTUM);
|
||||
task.addJob<DrawFrustum>("DrawViewFrustum", viewFrustum, glm::vec3(1.0f, 1.0f, 0.0f));
|
||||
task.addJob<DrawFrustum>("DrawShadowFrustum", shadowFrustum, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
for (auto i = 0; i < ExtractFrustums::SHADOW_CASCADE_FRUSTUM_COUNT; i++) {
|
||||
const auto shadowFrustum = frustums.getN<ExtractFrustums::Output>(ExtractFrustums::SHADOW_CASCADE0_FRUSTUM+i);
|
||||
float tint = 1.0f - i / float(ExtractFrustums::SHADOW_CASCADE_FRUSTUM_COUNT - 1);
|
||||
char jobName[64];
|
||||
sprintf(jobName, "DrawShadowFrustum%d", i);
|
||||
task.addJob<DrawFrustum>(jobName, shadowFrustum, glm::vec3(0.0f, tint, 1.0f));
|
||||
}
|
||||
|
||||
// Render.getConfig("RenderMainView.DrawSelectionBounds").enabled = true
|
||||
task.addJob<DrawBounds>("DrawSelectionBounds", selectedItems);
|
||||
}
|
||||
|
||||
// Layered Overlays
|
||||
const auto filteredOverlaysOpaque = task.addJob<FilterLayeredItems>("FilterOverlaysLayeredOpaque", overlayOpaques, Item::LAYER_3D_FRONT);
|
||||
const auto filteredOverlaysTransparent = task.addJob<FilterLayeredItems>("FilterOverlaysLayeredTransparent", overlayTransparents, Item::LAYER_3D_FRONT);
|
||||
const auto overlaysInFrontOpaque = filteredOverlaysOpaque.getN<FilterLayeredItems::Outputs>(0);
|
||||
const auto overlaysInFrontTransparent = filteredOverlaysTransparent.getN<FilterLayeredItems::Outputs>(0);
|
||||
|
||||
const auto overlayInFrontOpaquesInputs = DrawOverlay3D::Inputs(overlaysInFrontOpaque, lightingModel).asVarying();
|
||||
const auto overlayInFrontTransparentsInputs = DrawOverlay3D::Inputs(overlaysInFrontTransparent, lightingModel).asVarying();
|
||||
task.addJob<DrawOverlay3D>("DrawOverlayInFrontOpaque", overlayInFrontOpaquesInputs, true);
|
||||
task.addJob<DrawOverlay3D>("DrawOverlayInFrontTransparent", overlayInFrontTransparentsInputs, false);
|
||||
|
||||
{ // Debug the bounds of the rendered Overlay items that are marked drawInFront, still look at the zbuffer
|
||||
task.addJob<DrawBounds>("DrawOverlayInFrontOpaqueBounds", overlaysInFrontOpaque);
|
||||
task.addJob<DrawBounds>("DrawOverlayInFrontTransparentBounds", overlaysInFrontTransparent);
|
||||
}
|
||||
|
||||
// Debugging stages
|
||||
{
|
||||
// Debugging Deferred buffer job
|
||||
const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer));
|
||||
const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, deferredFrameTransform));
|
||||
task.addJob<DebugDeferredBuffer>("DebugDeferredBuffer", debugFramebuffers);
|
||||
|
||||
const auto debugSubsurfaceScatteringInputs = DebugSubsurfaceScattering::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel,
|
||||
|
@ -259,6 +248,22 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
|||
task.addJob<DebugZoneLighting>("DrawZoneStack", deferredFrameTransform);
|
||||
}
|
||||
|
||||
// Layered Overlays
|
||||
const auto filteredOverlaysOpaque = task.addJob<FilterLayeredItems>("FilterOverlaysLayeredOpaque", overlayOpaques, Item::LAYER_3D_FRONT);
|
||||
const auto filteredOverlaysTransparent = task.addJob<FilterLayeredItems>("FilterOverlaysLayeredTransparent", overlayTransparents, Item::LAYER_3D_FRONT);
|
||||
const auto overlaysInFrontOpaque = filteredOverlaysOpaque.getN<FilterLayeredItems::Outputs>(0);
|
||||
const auto overlaysInFrontTransparent = filteredOverlaysTransparent.getN<FilterLayeredItems::Outputs>(0);
|
||||
|
||||
const auto overlayInFrontOpaquesInputs = DrawOverlay3D::Inputs(overlaysInFrontOpaque, lightingModel).asVarying();
|
||||
const auto overlayInFrontTransparentsInputs = DrawOverlay3D::Inputs(overlaysInFrontTransparent, lightingModel).asVarying();
|
||||
task.addJob<DrawOverlay3D>("DrawOverlayInFrontOpaque", overlayInFrontOpaquesInputs, true);
|
||||
task.addJob<DrawOverlay3D>("DrawOverlayInFrontTransparent", overlayInFrontTransparentsInputs, false);
|
||||
|
||||
{ // Debug the bounds of the rendered Overlay items that are marked drawInFront, still look at the zbuffer
|
||||
task.addJob<DrawBounds>("DrawOverlayInFrontOpaqueBounds", overlaysInFrontOpaque);
|
||||
task.addJob<DrawBounds>("DrawOverlayInFrontTransparentBounds", overlaysInFrontTransparent);
|
||||
}
|
||||
|
||||
// AA job to be revisited
|
||||
task.addJob<Antialiasing>("Antialiasing", primaryFramebuffer);
|
||||
|
||||
|
@ -555,17 +560,20 @@ void ExtractFrustums::run(const render::RenderContextPointer& renderContext, Out
|
|||
}
|
||||
|
||||
// Return shadow frustum
|
||||
auto& shadowFrustum = output[SHADOW_FRUSTUM].edit<ViewFrustumPointer>();
|
||||
auto lightStage = args->_scene->getStage<LightStage>(LightStage::getName());
|
||||
if (lightStage) {
|
||||
auto globalShadow = lightStage->getCurrentKeyShadow();
|
||||
for (auto i = 0; i < SHADOW_CASCADE_FRUSTUM_COUNT; i++) {
|
||||
auto& shadowFrustum = output[SHADOW_CASCADE0_FRUSTUM+i].edit<ViewFrustumPointer>();
|
||||
if (lightStage) {
|
||||
auto globalShadow = lightStage->getCurrentKeyShadow();
|
||||
|
||||
if (globalShadow) {
|
||||
shadowFrustum = globalShadow->getFrustum();
|
||||
if (globalShadow && i<(int)globalShadow->getCascadeCount()) {
|
||||
auto& cascade = globalShadow->getCascade(i);
|
||||
shadowFrustum = cascade.getFrustum();
|
||||
} else {
|
||||
shadowFrustum.reset();
|
||||
}
|
||||
} else {
|
||||
shadowFrustum.reset();
|
||||
}
|
||||
} else {
|
||||
shadowFrustum.reset();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -174,8 +174,14 @@ class ExtractFrustums {
|
|||
public:
|
||||
|
||||
enum Frustum {
|
||||
VIEW_FRUSTUM,
|
||||
SHADOW_FRUSTUM,
|
||||
SHADOW_CASCADE0_FRUSTUM = 0,
|
||||
SHADOW_CASCADE1_FRUSTUM,
|
||||
SHADOW_CASCADE2_FRUSTUM,
|
||||
SHADOW_CASCADE3_FRUSTUM,
|
||||
|
||||
SHADOW_CASCADE_FRUSTUM_COUNT,
|
||||
|
||||
VIEW_FRUSTUM = SHADOW_CASCADE_FRUSTUM_COUNT,
|
||||
|
||||
FRUSTUM_COUNT
|
||||
};
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include "DeferredLightingEffect.h"
|
||||
#include "FramebufferCache.h"
|
||||
|
||||
#include "RenderUtilsLogging.h"
|
||||
|
||||
// These values are used for culling the objects rendered in the shadow map
|
||||
// but are readjusted afterwards
|
||||
#define SHADOW_FRUSTUM_NEAR 1.0f
|
||||
|
@ -89,31 +91,13 @@ static void adjustNearFar(const AABox& inShapeBounds, ViewFrustum& shadowFrustum
|
|||
for (i = 0; i < 8; i++) {
|
||||
sceneBoundVertices[i] = shadowViewInverse.transform(inShapeBounds.getVertex(static_cast<BoxVertex>(i)));
|
||||
}
|
||||
// This indirection array is just a protection in case the ViewFrustum::PlaneIndex enum
|
||||
// changes order especially as we don't need to test the NEAR and FAR planes.
|
||||
static const ViewFrustum::PlaneIndex planeIndices[4] = {
|
||||
ViewFrustum::TOP_PLANE,
|
||||
ViewFrustum::BOTTOM_PLANE,
|
||||
ViewFrustum::LEFT_PLANE,
|
||||
ViewFrustum::RIGHT_PLANE
|
||||
};
|
||||
// Same goes for the shadow frustum planes.
|
||||
for (i = 0; i < 4; i++) {
|
||||
const auto& worldPlane = shadowFrustum.getPlanes()[planeIndices[i]];
|
||||
// We assume the transform doesn't have a non uniform scale component to apply the
|
||||
// transform to the normal without using the correct transpose of inverse, which should be the
|
||||
// case for a view matrix.
|
||||
auto planeNormal = shadowViewInverse.transformDirection(worldPlane.getNormal());
|
||||
auto planePoint = shadowViewInverse.transform(worldPlane.getPoint());
|
||||
shadowClipPlanes[i].setNormalAndPoint(planeNormal, planePoint);
|
||||
}
|
||||
shadowFrustum.getUniformlyTransformedSidePlanes(shadowViewInverse, shadowClipPlanes);
|
||||
|
||||
float near = std::numeric_limits<float>::max();
|
||||
float far = 0.0f;
|
||||
|
||||
computeNearFar(sceneBoundVertices, shadowClipPlanes, near, far);
|
||||
// Limit the far range to the one used originally. There's no point in rendering objects
|
||||
// that are not in the view frustum.
|
||||
// Limit the far range to the one used originally.
|
||||
far = glm::min(far, shadowFrustum.getFarClip());
|
||||
|
||||
const auto depthEpsilon = 0.1f;
|
||||
|
@ -137,9 +121,12 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con
|
|||
assert(lightStage);
|
||||
|
||||
auto shadow = lightStage->getCurrentKeyShadow();
|
||||
if (!shadow) return;
|
||||
if (!shadow || _cascadeIndex >= shadow->getCascadeCount()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& fbo = shadow->framebuffer;
|
||||
auto& cascade = shadow->getCascade(_cascadeIndex);
|
||||
auto& fbo = cascade.framebuffer;
|
||||
|
||||
RenderArgs* args = renderContext->args;
|
||||
ShapeKey::Builder defaultKeyBuilder;
|
||||
|
@ -149,7 +136,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con
|
|||
// the minimal Z range.
|
||||
adjustNearFar(inShapeBounds, adjustedShadowFrustum);
|
||||
// Reapply the frustum as it has been adjusted
|
||||
shadow->setFrustum(adjustedShadowFrustum);
|
||||
shadow->setCascadeFrustum(_cascadeIndex, adjustedShadowFrustum);
|
||||
args->popViewFrustum();
|
||||
args->pushViewFrustum(adjustedShadowFrustum);
|
||||
|
||||
|
@ -178,6 +165,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con
|
|||
auto shadowSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned());
|
||||
|
||||
std::vector<ShapeKey> skinnedShapeKeys{};
|
||||
std::vector<ShapeKey> ownPipelineShapeKeys{};
|
||||
|
||||
// Iterate through all inShapes and render the unskinned
|
||||
args->_shapePipeline = shadowPipeline;
|
||||
|
@ -185,8 +173,10 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con
|
|||
for (auto items : inShapes) {
|
||||
if (items.first.isSkinned()) {
|
||||
skinnedShapeKeys.push_back(items.first);
|
||||
} else {
|
||||
} else if (!items.first.hasOwnPipeline()) {
|
||||
renderItems(renderContext, items.second);
|
||||
} else {
|
||||
ownPipelineShapeKeys.push_back(items.first);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,7 +187,15 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con
|
|||
renderItems(renderContext, inShapes.at(key));
|
||||
}
|
||||
|
||||
// Finally render the items with their own pipeline last to prevent them from breaking the
|
||||
// render state. This is probably a temporary code as there is probably something better
|
||||
// to do in the render call of objects that have their own pipeline.
|
||||
args->_shapePipeline = nullptr;
|
||||
for (const auto& key : ownPipelineShapeKeys) {
|
||||
args->_itemShapeKey = key._flags.to_ulong();
|
||||
renderItems(renderContext, inShapes.at(key));
|
||||
}
|
||||
|
||||
args->_batch = nullptr;
|
||||
});
|
||||
}
|
||||
|
@ -215,22 +213,26 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
|
|||
initZPassPipelines(*shapePlumber, state);
|
||||
}
|
||||
|
||||
const auto cachedMode = task.addJob<RenderShadowSetup>("ShadowSetup");
|
||||
task.addJob<RenderShadowSetup>("ShadowSetup");
|
||||
|
||||
// CPU jobs:
|
||||
// Fetch and cull the items from the scene
|
||||
auto shadowFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered();
|
||||
const auto shadowSelection = task.addJob<FetchSpatialTree>("FetchShadowSelection", shadowFilter);
|
||||
const auto culledShadowSelection = task.addJob<CullSpatialSelection>("CullShadowSelection", shadowSelection, cullFunctor, RenderDetails::SHADOW, shadowFilter);
|
||||
for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) {
|
||||
const auto setupOutput = task.addJob<RenderShadowCascadeSetup>("ShadowCascadeSetup", i);
|
||||
const auto shadowFilter = setupOutput.getN<RenderShadowCascadeSetup::Outputs>(1);
|
||||
|
||||
// Sort
|
||||
const auto sortedPipelines = task.addJob<PipelineSortShapes>("PipelineSortShadowSort", culledShadowSelection);
|
||||
const auto sortedShapesAndBounds = task.addJob<DepthSortShapesAndComputeBounds>("DepthSortShadowMap", sortedPipelines, true);
|
||||
// CPU jobs:
|
||||
// Fetch and cull the items from the scene
|
||||
const auto shadowSelection = task.addJob<FetchSpatialTree>("FetchShadowSelection", shadowFilter);
|
||||
const auto cullInputs = CullSpatialSelection::Inputs(shadowSelection, shadowFilter).asVarying();
|
||||
const auto culledShadowSelection = task.addJob<CullSpatialSelection>("CullShadowSelection", cullInputs, cullFunctor, RenderDetails::SHADOW);
|
||||
|
||||
// GPU jobs: Render to shadow map
|
||||
task.addJob<RenderShadowMap>("RenderShadowMap", sortedShapesAndBounds, shapePlumber);
|
||||
// Sort
|
||||
const auto sortedPipelines = task.addJob<PipelineSortShapes>("PipelineSortShadowSort", culledShadowSelection);
|
||||
const auto sortedShapesAndBounds = task.addJob<DepthSortShapesAndComputeBounds>("DepthSortShadowMap", sortedPipelines, true);
|
||||
|
||||
task.addJob<RenderShadowTeardown>("ShadowTeardown", cachedMode);
|
||||
// GPU jobs: Render to shadow map
|
||||
task.addJob<RenderShadowMap>("RenderShadowMap", sortedShapesAndBounds, shapePlumber, i);
|
||||
task.addJob<RenderShadowCascadeTeardown>("ShadowCascadeTeardown", setupOutput);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderShadowTask::configure(const Config& configuration) {
|
||||
|
@ -239,31 +241,57 @@ void RenderShadowTask::configure(const Config& configuration) {
|
|||
// Task::configure(configuration);
|
||||
}
|
||||
|
||||
void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, Output& output) {
|
||||
void RenderShadowSetup::run(const render::RenderContextPointer& renderContext) {
|
||||
auto lightStage = renderContext->_scene->getStage<LightStage>();
|
||||
assert(lightStage);
|
||||
// Cache old render args
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
const auto globalShadow = lightStage->getCurrentKeyShadow();
|
||||
if (globalShadow) {
|
||||
// Cache old render args
|
||||
RenderArgs* args = renderContext->args;
|
||||
output = args->_renderMode;
|
||||
|
||||
auto nearClip = args->getViewFrustum().getNearClip();
|
||||
float nearDepth = -args->_boomOffset.z;
|
||||
const float SHADOW_MAX_DISTANCE = 20.0f;
|
||||
globalShadow->setKeylightFrustum(args->getViewFrustum(), nearDepth, nearClip + SHADOW_MAX_DISTANCE, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR);
|
||||
|
||||
// Set the keylight render args
|
||||
args->pushViewFrustum(*(globalShadow->getFrustum()));
|
||||
args->_renderMode = RenderArgs::SHADOW_RENDER_MODE;
|
||||
globalShadow->setKeylightFrustum(args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderShadowTeardown::run(const render::RenderContextPointer& renderContext, const Input& input) {
|
||||
void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) {
|
||||
auto lightStage = renderContext->_scene->getStage<LightStage>();
|
||||
assert(lightStage);
|
||||
// Cache old render args
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
output.edit0() = args->_renderMode;
|
||||
output.edit2() = args->_sizeScale;
|
||||
|
||||
const auto globalShadow = lightStage->getCurrentKeyShadow();
|
||||
if (globalShadow && _cascadeIndex<globalShadow->getCascadeCount()) {
|
||||
output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered();
|
||||
|
||||
globalShadow->setKeylightCascadeFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR);
|
||||
|
||||
// Set the keylight render args
|
||||
args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum()));
|
||||
args->_renderMode = RenderArgs::SHADOW_RENDER_MODE;
|
||||
if (lightStage->getCurrentKeyLight()->getType() == model::Light::SUN) {
|
||||
const float shadowSizeScale = 1e16f;
|
||||
// Set the size scale to a ridiculously high value to prevent small object culling which assumes
|
||||
// the view frustum is a perspective projection. But this isn't the case for the sun which
|
||||
// is an orthographic projection.
|
||||
args->_sizeScale = shadowSizeScale;
|
||||
}
|
||||
|
||||
} else {
|
||||
output.edit1() = ItemFilter::Builder::nothing();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderShadowCascadeTeardown::run(const render::RenderContextPointer& renderContext, const Input& input) {
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
if (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE && !input.get1().selectsNothing()) {
|
||||
args->popViewFrustum();
|
||||
}
|
||||
assert(args->hasViewFrustum());
|
||||
// Reset the render args
|
||||
args->popViewFrustum();
|
||||
args->_renderMode = input;
|
||||
args->_renderMode = input.get0();
|
||||
args->_sizeScale = input.get2();
|
||||
};
|
||||
|
|
|
@ -24,11 +24,12 @@ public:
|
|||
using Inputs = render::VaryingSet2<render::ShapeBounds, AABox>;
|
||||
using JobModel = render::Job::ModelI<RenderShadowMap, Inputs>;
|
||||
|
||||
RenderShadowMap(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {}
|
||||
RenderShadowMap(render::ShapePlumberPointer shapePlumber, unsigned int cascadeIndex) : _shapePlumber{ shapePlumber }, _cascadeIndex{ cascadeIndex } {}
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
|
||||
|
||||
protected:
|
||||
render::ShapePlumberPointer _shapePlumber;
|
||||
unsigned int _cascadeIndex;
|
||||
};
|
||||
|
||||
class RenderShadowTaskConfig : public render::Task::Config::Persistent {
|
||||
|
@ -54,15 +55,30 @@ public:
|
|||
|
||||
class RenderShadowSetup {
|
||||
public:
|
||||
using Output = RenderArgs::RenderMode;
|
||||
using JobModel = render::Job::ModelO<RenderShadowSetup, Output>;
|
||||
void run(const render::RenderContextPointer& renderContext, Output& output);
|
||||
using JobModel = render::Job::Model<RenderShadowSetup>;
|
||||
|
||||
RenderShadowSetup() {}
|
||||
void run(const render::RenderContextPointer& renderContext);
|
||||
|
||||
};
|
||||
|
||||
class RenderShadowTeardown {
|
||||
class RenderShadowCascadeSetup {
|
||||
public:
|
||||
using Input = RenderArgs::RenderMode;
|
||||
using JobModel = render::Job::ModelI<RenderShadowTeardown, Input>;
|
||||
using Outputs = render::VaryingSet3<RenderArgs::RenderMode, render::ItemFilter, float>;
|
||||
using JobModel = render::Job::ModelO<RenderShadowCascadeSetup, Outputs>;
|
||||
|
||||
RenderShadowCascadeSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {}
|
||||
void run(const render::RenderContextPointer& renderContext, Outputs& output);
|
||||
|
||||
private:
|
||||
|
||||
unsigned int _cascadeIndex;
|
||||
};
|
||||
|
||||
class RenderShadowCascadeTeardown {
|
||||
public:
|
||||
using Input = RenderShadowCascadeSetup::Outputs;
|
||||
using JobModel = render::Job::ModelI<RenderShadowCascadeTeardown, Input>;
|
||||
void run(const render::RenderContextPointer& renderContext, const Input& input);
|
||||
};
|
||||
|
||||
|
|
|
@ -14,15 +14,21 @@
|
|||
#include "RenderDeferredTask.h"
|
||||
#include "RenderForwardTask.h"
|
||||
|
||||
|
||||
|
||||
void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, bool isDeferred) {
|
||||
// auto items = input.get<Input>();
|
||||
|
||||
// Shadows use an orthographic projection because they are linked to sunlights
|
||||
// but the cullFunctor passed is probably tailored for perspective projection and culls too much.
|
||||
// TODO : create a special cull functor for this.
|
||||
task.addJob<RenderShadowTask>("RenderShadowTask", nullptr);
|
||||
task.addJob<RenderShadowTask>("RenderShadowTask", [](const RenderArgs* args, const AABox& bounds) {
|
||||
// Cull only objects that are too small relatively to shadow frustum
|
||||
auto& frustum = args->getViewFrustum();
|
||||
auto frustumSize = std::max(frustum.getHeight(), frustum.getWidth());
|
||||
const auto boundsRadius = bounds.getDimensions().length();
|
||||
const auto relativeBoundRadius = boundsRadius / frustumSize;
|
||||
const auto threshold = 1e-3f;
|
||||
return relativeBoundRadius > threshold;
|
||||
return true;
|
||||
});
|
||||
|
||||
const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor);
|
||||
assert(items.canCast<RenderFetchCullSortTask::Output>());
|
||||
|
|
|
@ -11,53 +11,17 @@
|
|||
<@if not SHADOW_SLH@>
|
||||
<@def SHADOW_SLH@>
|
||||
|
||||
<@include ShadowCore.slh@>
|
||||
|
||||
#define SHADOW_NOISE_ENABLED 0
|
||||
#define SHADOW_SCREEN_SPACE_DITHER 1
|
||||
|
||||
// the shadow texture
|
||||
uniform sampler2DShadow shadowMap;
|
||||
|
||||
struct ShadowTransform {
|
||||
mat4 projection;
|
||||
mat4 viewInverse;
|
||||
|
||||
float bias;
|
||||
float scale;
|
||||
};
|
||||
|
||||
uniform shadowTransformBuffer {
|
||||
ShadowTransform _shadowTransform;
|
||||
};
|
||||
|
||||
mat4 getShadowViewInverse() {
|
||||
return _shadowTransform.viewInverse;
|
||||
}
|
||||
|
||||
mat4 getShadowProjection() {
|
||||
return _shadowTransform.projection;
|
||||
}
|
||||
|
||||
float getShadowScale() {
|
||||
return _shadowTransform.scale;
|
||||
}
|
||||
|
||||
float getShadowBias() {
|
||||
return _shadowTransform.bias;
|
||||
}
|
||||
|
||||
// Compute the texture coordinates from world coordinates
|
||||
vec4 evalShadowTexcoord(vec4 position) {
|
||||
mat4 biasMatrix = mat4(
|
||||
0.5, 0.0, 0.0, 0.0,
|
||||
0.0, 0.5, 0.0, 0.0,
|
||||
0.0, 0.0, 0.5, 0.0,
|
||||
0.5, 0.5, 0.5, 1.0);
|
||||
float bias = -getShadowBias();
|
||||
|
||||
vec4 shadowCoord = biasMatrix * getShadowProjection() * getShadowViewInverse() * position;
|
||||
return vec4(shadowCoord.xy, shadowCoord.z + bias, 1.0);
|
||||
}
|
||||
uniform sampler2DShadow shadowMaps[SHADOW_CASCADE_MAX_COUNT];
|
||||
|
||||
// Sample the shadowMap with PCF (built-in)
|
||||
float fetchShadow(vec3 shadowTexcoord) {
|
||||
return texture(shadowMap, shadowTexcoord);
|
||||
float fetchShadow(int cascadeIndex, vec3 shadowTexcoord) {
|
||||
return texture(shadowMaps[cascadeIndex], shadowTexcoord);
|
||||
}
|
||||
|
||||
vec2 PCFkernel[4] = vec2[4](
|
||||
|
@ -67,38 +31,83 @@ vec2 PCFkernel[4] = vec2[4](
|
|||
vec2(0.5, -1.5)
|
||||
);
|
||||
|
||||
float evalShadowAttenuationPCF(vec4 position, vec4 shadowTexcoord) {
|
||||
// PCF is buggy so disable it for the time being
|
||||
#if 0
|
||||
float pcfRadius = 3.0;
|
||||
float evalShadowNoise(vec4 seed) {
|
||||
float dot_product = dot(seed, vec4(12.9898,78.233,45.164,94.673));
|
||||
return fract(sin(dot_product) * 43758.5453);
|
||||
}
|
||||
|
||||
struct ShadowSampleOffsets {
|
||||
vec3 points[4];
|
||||
};
|
||||
|
||||
ShadowSampleOffsets evalShadowFilterOffsets(vec4 position) {
|
||||
float shadowScale = getShadowScale();
|
||||
ShadowSampleOffsets offsets;
|
||||
|
||||
#if SHADOW_SCREEN_SPACE_DITHER
|
||||
// Pattern dithering in screen space
|
||||
ivec2 coords = ivec2(gl_FragCoord.xy);
|
||||
#else
|
||||
// Pattern dithering in world space (mm resolution)
|
||||
ivec2 coords = ivec2(position.x, position.y+position.z);
|
||||
#endif
|
||||
|
||||
#if SHADOW_NOISE_ENABLED
|
||||
// Add some noise to break dithering
|
||||
int index = int(4.0*evalShadowNoise(gl_FragCoord.xyyx))%4;
|
||||
coords.x += index & 1;
|
||||
coords.y += (index & 2) >> 1;
|
||||
#endif
|
||||
|
||||
// Offset for efficient PCF, see http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html
|
||||
vec2 offset = pcfRadius * step(fract(position.xy), vec2(0.5, 0.5));
|
||||
ivec2 offset = coords & ivec2(1,1);
|
||||
offset.y = (offset.x+offset.y) & 1;
|
||||
|
||||
float shadowAttenuation = (0.25 * (
|
||||
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[0], 0.0)) +
|
||||
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[1], 0.0)) +
|
||||
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[2], 0.0)) +
|
||||
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[3], 0.0))
|
||||
));
|
||||
#else
|
||||
float shadowAttenuation = fetchShadow(shadowTexcoord.xyz);
|
||||
#endif
|
||||
offsets.points[0] = shadowScale * vec3(offset + PCFkernel[0], 0.0);
|
||||
offsets.points[1] = shadowScale * vec3(offset + PCFkernel[1], 0.0);
|
||||
offsets.points[2] = shadowScale * vec3(offset + PCFkernel[2], 0.0);
|
||||
offsets.points[3] = shadowScale * vec3(offset + PCFkernel[3], 0.0);
|
||||
|
||||
return offsets;
|
||||
}
|
||||
|
||||
float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float bias) {
|
||||
shadowTexcoord.z -= bias;
|
||||
float shadowAttenuation = 0.25 * (
|
||||
fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[0]) +
|
||||
fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[1]) +
|
||||
fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[2]) +
|
||||
fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[3])
|
||||
);
|
||||
|
||||
return shadowAttenuation;
|
||||
}
|
||||
|
||||
float evalShadowAttenuation(vec4 position) {
|
||||
vec4 shadowTexcoord = evalShadowTexcoord(position);
|
||||
if (shadowTexcoord.x < 0.0 || shadowTexcoord.x > 1.0 ||
|
||||
shadowTexcoord.y < 0.0 || shadowTexcoord.y > 1.0 ||
|
||||
shadowTexcoord.z < 0.0 || shadowTexcoord.z > 1.0) {
|
||||
float evalShadowCascadeAttenuation(int cascadeIndex, vec3 viewNormal, ShadowSampleOffsets offsets, vec4 shadowTexcoord) {
|
||||
if (!isShadowCascadeProjectedOnPixel(shadowTexcoord)) {
|
||||
// If a point is not in the map, do not attenuate
|
||||
return 1.0;
|
||||
}
|
||||
// Multiply bias if we are at a grazing angle with light
|
||||
float tangentFactor = abs(dot(getShadowDirInViewSpace(), viewNormal));
|
||||
float bias = getShadowBias(cascadeIndex) * (5.0-4.0*tangentFactor);
|
||||
return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord, bias);
|
||||
}
|
||||
|
||||
return evalShadowAttenuationPCF(position, shadowTexcoord);
|
||||
float evalShadowAttenuation(vec4 worldPosition, float viewDepth, vec3 viewNormal) {
|
||||
ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition);
|
||||
vec4 cascadeShadowCoords[2];
|
||||
ivec2 cascadeIndices;
|
||||
float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices);
|
||||
|
||||
vec2 cascadeAttenuations = vec2(1.0, 1.0);
|
||||
cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, viewNormal, offsets, cascadeShadowCoords[0]);
|
||||
if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) {
|
||||
cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, viewNormal, offsets, cascadeShadowCoords[1]);
|
||||
}
|
||||
float attenuation = mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix);
|
||||
// Falloff to max distance
|
||||
return mix(1.0, attenuation, evalShadowFalloff(viewDepth));
|
||||
}
|
||||
|
||||
<@endif@>
|
||||
|
|
96
libraries/render-utils/src/ShadowCore.slh
Normal file
96
libraries/render-utils/src/ShadowCore.slh
Normal file
|
@ -0,0 +1,96 @@
|
|||
<!
|
||||
// ShadowCore.slh
|
||||
// libraries/render-utils/src
|
||||
//
|
||||
// Created by Olivier Prat on 11/13/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
|
||||
!>
|
||||
<@if not SHADOW_CORE_SLH@>
|
||||
<@def SHADOW_CORE_SLH@>
|
||||
|
||||
<@include Shadows_shared.slh@>
|
||||
|
||||
layout(std140) uniform shadowTransformBuffer {
|
||||
ShadowParameters shadow;
|
||||
};
|
||||
|
||||
int getShadowCascadeCount() {
|
||||
return shadow.cascadeCount;
|
||||
}
|
||||
|
||||
float getShadowCascadeInvBlendWidth() {
|
||||
return shadow.invCascadeBlendWidth;
|
||||
}
|
||||
|
||||
float evalShadowFalloff(float depth) {
|
||||
return clamp((shadow.maxDistance-depth) * shadow.invFalloffDistance, 0.0, 1.0);
|
||||
}
|
||||
|
||||
mat4 getShadowReprojection(int cascadeIndex) {
|
||||
return shadow.cascades[cascadeIndex].reprojection;
|
||||
}
|
||||
|
||||
float getShadowScale() {
|
||||
return shadow.invMapSize;
|
||||
}
|
||||
|
||||
float getShadowBias(int cascadeIndex) {
|
||||
return shadow.cascades[cascadeIndex].bias;
|
||||
}
|
||||
|
||||
vec3 getShadowDirInViewSpace() {
|
||||
return shadow.lightDirInViewSpace;
|
||||
}
|
||||
|
||||
// Compute the texture coordinates from world coordinates
|
||||
vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) {
|
||||
vec4 shadowCoord = getShadowReprojection(cascadeIndex) * position;
|
||||
return vec4(shadowCoord.xyz, 1.0);
|
||||
}
|
||||
|
||||
bool isShadowCascadeProjectedOnPixel(vec4 cascadeTexCoords) {
|
||||
bvec2 greaterThanZero = greaterThanEqual(cascadeTexCoords.xy, vec2(0));
|
||||
bvec2 lessThanOne = lessThanEqual(cascadeTexCoords.xy, vec2(1));
|
||||
return all(greaterThanZero) && all(lessThanOne);
|
||||
}
|
||||
|
||||
int getFirstShadowCascadeOnPixel(int startCascadeIndex, vec4 worldPosition, out vec4 cascadeShadowCoords) {
|
||||
int cascadeIndex;
|
||||
startCascadeIndex = min(startCascadeIndex, getShadowCascadeCount()-1);
|
||||
for (cascadeIndex=startCascadeIndex ; cascadeIndex<getShadowCascadeCount() ; cascadeIndex++) {
|
||||
cascadeShadowCoords = evalShadowTexcoord(cascadeIndex, worldPosition);
|
||||
if (isShadowCascadeProjectedOnPixel(cascadeShadowCoords)) {
|
||||
return cascadeIndex;
|
||||
}
|
||||
}
|
||||
return cascadeIndex;
|
||||
}
|
||||
|
||||
float evalShadowCascadeWeight(vec4 cascadeTexCoords) {
|
||||
// Inspired by DirectX CascadeShadowMaps11 example
|
||||
vec2 distanceToOne = vec2(1.0) - cascadeTexCoords.xy;
|
||||
float blend1 = min( cascadeTexCoords.x, cascadeTexCoords.y );
|
||||
float blend2 = min( distanceToOne.x, distanceToOne.y );
|
||||
float blend = min( blend1, blend2 );
|
||||
return clamp(blend * getShadowCascadeInvBlendWidth(), 0.0, 1.0);
|
||||
}
|
||||
|
||||
float determineShadowCascadesOnPixel(vec4 worldPosition, float viewDepth, out vec4 cascadeShadowCoords[2], out ivec2 cascadeIndices) {
|
||||
cascadeIndices.x = getFirstShadowCascadeOnPixel(0, worldPosition, cascadeShadowCoords[0]);
|
||||
cascadeIndices.y = cascadeIndices.x+1;
|
||||
if (cascadeIndices.x < (getShadowCascadeCount()-1)) {
|
||||
cascadeIndices.y = getFirstShadowCascadeOnPixel(cascadeIndices.y, worldPosition, cascadeShadowCoords[1]);
|
||||
|
||||
float firstCascadeWeight = evalShadowCascadeWeight(cascadeShadowCoords[0]);
|
||||
float secondCascadeWeight = evalShadowCascadeWeight(cascadeShadowCoords[1]);
|
||||
// Returns the mix amount between first and second cascade.
|
||||
return ((1.0-firstCascadeWeight) * secondCascadeWeight) / (firstCascadeWeight + secondCascadeWeight);
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
<@endif@>
|
34
libraries/render-utils/src/Shadows_shared.slh
Normal file
34
libraries/render-utils/src/Shadows_shared.slh
Normal file
|
@ -0,0 +1,34 @@
|
|||
// glsl / C++ compatible source as interface for Shadows
|
||||
#ifdef __cplusplus
|
||||
# define MAT4 glm::mat4
|
||||
# define VEC3 glm::vec3
|
||||
#else
|
||||
# define MAT4 mat4
|
||||
# define VEC3 vec3
|
||||
#endif
|
||||
|
||||
#define SHADOW_CASCADE_MAX_COUNT 4
|
||||
|
||||
struct ShadowTransform {
|
||||
MAT4 reprojection;
|
||||
|
||||
float bias;
|
||||
float _padding1;
|
||||
float _padding2;
|
||||
float _padding3;
|
||||
};
|
||||
|
||||
struct ShadowParameters {
|
||||
ShadowTransform cascades[SHADOW_CASCADE_MAX_COUNT];
|
||||
VEC3 lightDirInViewSpace;
|
||||
int cascadeCount;
|
||||
float invMapSize;
|
||||
float invCascadeBlendWidth;
|
||||
float maxDistance;
|
||||
float invFalloffDistance;
|
||||
};
|
||||
|
||||
// <@if 1@>
|
||||
// Trigger Scribe include
|
||||
// <@endif@> <!def that !>
|
||||
//
|
|
@ -16,7 +16,6 @@
|
|||
<@include gpu/Color.slh@>
|
||||
<$declareColorWheel()$>
|
||||
|
||||
|
||||
uniform sampler2D linearDepthMap;
|
||||
uniform sampler2D halfLinearDepthMap;
|
||||
uniform sampler2D halfNormalMap;
|
||||
|
@ -24,6 +23,8 @@ uniform sampler2D occlusionMap;
|
|||
uniform sampler2D occlusionBlurredMap;
|
||||
uniform sampler2D scatteringMap;
|
||||
|
||||
<@include ShadowCore.slh@>
|
||||
|
||||
<$declareDeferredCurvature()$>
|
||||
|
||||
float curvatureAO(float k) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue