mirror of
https://github.com/overte-org/overte.git
synced 2025-08-06 18:50:00 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into orange
This commit is contained in:
commit
9404846ca4
33 changed files with 487 additions and 178 deletions
BIN
examples/tests/cube_texture.png
Normal file
BIN
examples/tests/cube_texture.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.8 MiB |
41
examples/tests/textureStress.fs
Normal file
41
examples/tests/textureStress.fs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
float aspect(vec2 v) {
|
||||||
|
return v.x / v.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 aspectCorrectedTexture() {
|
||||||
|
vec2 uv = _position.xy;
|
||||||
|
uv += 0.5;
|
||||||
|
uv.y = 1.0 - uv.y;
|
||||||
|
|
||||||
|
float targetAspect = iWorldScale.x / iWorldScale.y;
|
||||||
|
float sourceAspect = aspect(iChannelResolution[0].xy);
|
||||||
|
float aspectCorrection = sourceAspect / targetAspect;
|
||||||
|
if (aspectCorrection > 1.0) {
|
||||||
|
float offset = aspectCorrection - 1.0;
|
||||||
|
float halfOffset = offset / 2.0;
|
||||||
|
uv.y -= halfOffset;
|
||||||
|
uv.y *= aspectCorrection;
|
||||||
|
} else {
|
||||||
|
float offset = 1.0 - aspectCorrection;
|
||||||
|
float halfOffset = offset / 2.0;
|
||||||
|
uv.x -= halfOffset;
|
||||||
|
uv.x /= aspectCorrection;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (any(lessThan(uv, vec2(0.0)))) {
|
||||||
|
return vec3(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (any(greaterThan(uv, vec2(1.0)))) {
|
||||||
|
return vec3(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 color = texture(iChannel0, uv);
|
||||||
|
return color.rgb * max(0.5, sourceAspect) * max(0.9, fract(iWorldPosition.x));
|
||||||
|
}
|
||||||
|
|
||||||
|
float getProceduralColors(inout vec3 diffuse, inout vec3 specular, inout float shininess) {
|
||||||
|
specular = aspectCorrectedTexture();
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
67
examples/tests/textureStress.js
Normal file
67
examples/tests/textureStress.js
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
Script.include("https://s3.amazonaws.com/DreamingContent/scripts/Austin.js");
|
||||||
|
|
||||||
|
var ENTITY_SPAWN_LIMIT = 500;
|
||||||
|
var ENTITY_LIFETIME = 600;
|
||||||
|
var RADIUS = 1.0; // Spawn within this radius (square)
|
||||||
|
var TEST_ENTITY_NAME = "EntitySpawnTest";
|
||||||
|
|
||||||
|
var entities = [];
|
||||||
|
var textureIndex = 0;
|
||||||
|
var texture = Script.resolvePath('cube_texture.png');
|
||||||
|
var shader = Script.resolvePath('textureStress.fs');
|
||||||
|
var qml = Script.resolvePath('textureStress.qml');
|
||||||
|
qmlWindow = new OverlayWindow({
|
||||||
|
title: 'Test Qml',
|
||||||
|
source: qml,
|
||||||
|
height: 240,
|
||||||
|
width: 320,
|
||||||
|
toolWindow: false,
|
||||||
|
visible: true
|
||||||
|
});
|
||||||
|
|
||||||
|
function deleteItems(count) {
|
||||||
|
if (!count) {
|
||||||
|
var ids = Entities.findEntities(MyAvatar.position, 50);
|
||||||
|
ids.forEach(function(id) {
|
||||||
|
var properties = Entities.getEntityProperties(id, ["name"]);
|
||||||
|
if (properties.name === TEST_ENTITY_NAME) {
|
||||||
|
Entities.deleteEntity(id);
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
entities = [];
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// FIXME... implement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createItems(count) {
|
||||||
|
for (var i = 0; i < count; ++i) {
|
||||||
|
var newEntity = Entities.addEntity({
|
||||||
|
type: "Box",
|
||||||
|
name: TEST_ENTITY_NAME,
|
||||||
|
position: AUSTIN.avatarRelativePosition(AUSTIN.randomPositionXZ({ x: 0, y: 0, z: -2 }, RADIUS)),
|
||||||
|
color: { r: 255, g: 255, b: 255 },
|
||||||
|
dimensions: AUSTIN.randomDimensions(),
|
||||||
|
lifetime: ENTITY_LIFETIME,
|
||||||
|
userData: JSON.stringify({
|
||||||
|
ProceduralEntity: {
|
||||||
|
version: 2,
|
||||||
|
shaderUrl: shader,
|
||||||
|
channels: [ texture + "?" + textureIndex++ ]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
entities.push(newEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qmlWindow.fromQml.connect(function(message){
|
||||||
|
print(message);
|
||||||
|
if (message[0] === "create") {
|
||||||
|
var count = message[1] || 1;
|
||||||
|
createItems(message[1] || 1);
|
||||||
|
} else if (message[0] === "delete") {
|
||||||
|
deleteItems(message[1]);
|
||||||
|
}
|
||||||
|
});
|
69
examples/tests/textureStress.qml
Normal file
69
examples/tests/textureStress.qml
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
width: parent ? parent.width : 100
|
||||||
|
height: parent ? parent.height : 100
|
||||||
|
|
||||||
|
signal sendToScript(var message);
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: label
|
||||||
|
text: "GPU Texture Usage: "
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
id: usage
|
||||||
|
anchors.left: label.right
|
||||||
|
anchors.leftMargin: 8
|
||||||
|
text: "N/A"
|
||||||
|
Timer {
|
||||||
|
repeat: true
|
||||||
|
running: true
|
||||||
|
interval: 500
|
||||||
|
onTriggered: {
|
||||||
|
usage.text = Render.getConfig("Stats")["textureGPUMemoryUsage"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
|
||||||
|
|
||||||
|
anchors { left: parent.left; right: parent.right; top: label.bottom; topMargin: 8; bottom: parent.bottom }
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: "Add 1"
|
||||||
|
onClicked: root.sendToScript(["create", 1]);
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "Add 10"
|
||||||
|
onClicked: root.sendToScript(["create", 10]);
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "Add 100"
|
||||||
|
onClicked: root.sendToScript(["create", 100]);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Button {
|
||||||
|
text: "Delete 1"
|
||||||
|
onClicked: root.sendToScript(["delete", 1]);
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "Delete 10"
|
||||||
|
onClicked: root.sendToScript(["delete", 10]);
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "Delete 100"
|
||||||
|
onClicked: root.sendToScript(["delete", 100]);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
Button {
|
||||||
|
text: "Delete All"
|
||||||
|
onClicked: root.sendToScript(["delete", 0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,3 +97,11 @@ bool HMDScriptingInterface::isMounted() const{
|
||||||
auto displayPlugin = qApp->getActiveDisplayPlugin();
|
auto displayPlugin = qApp->getActiveDisplayPlugin();
|
||||||
return (displayPlugin->isHmd() && displayPlugin->isDisplayVisible());
|
return (displayPlugin->isHmd() && displayPlugin->isDisplayVisible());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString HMDScriptingInterface::preferredAudioInput() const {
|
||||||
|
return qApp->getActiveDisplayPlugin()->getPreferredAudioInDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString HMDScriptingInterface::preferredAudioOutput() const {
|
||||||
|
return qApp->getActiveDisplayPlugin()->getPreferredAudioOutDevice();
|
||||||
|
}
|
||||||
|
|
|
@ -34,6 +34,8 @@ public:
|
||||||
|
|
||||||
Q_INVOKABLE glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const;
|
Q_INVOKABLE glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const;
|
||||||
Q_INVOKABLE glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const;
|
Q_INVOKABLE glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const;
|
||||||
|
Q_INVOKABLE QString preferredAudioInput() const;
|
||||||
|
Q_INVOKABLE QString preferredAudioOutput() const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HMDScriptingInterface();
|
HMDScriptingInterface();
|
||||||
|
|
|
@ -36,7 +36,7 @@ AnimationPointer AnimationCache::getAnimation(const QUrl& url) {
|
||||||
|
|
||||||
QSharedPointer<Resource> AnimationCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
|
QSharedPointer<Resource> AnimationCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
|
||||||
bool delayLoad, const void* extra) {
|
bool delayLoad, const void* extra) {
|
||||||
return QSharedPointer<Resource>(new Animation(url), &Resource::allReferencesCleared);
|
return QSharedPointer<Resource>(new Animation(url), &Resource::deleter);
|
||||||
}
|
}
|
||||||
|
|
||||||
Animation::Animation(const QUrl& url) : Resource(url) {}
|
Animation::Animation(const QUrl& url) : Resource(url) {}
|
||||||
|
|
|
@ -175,6 +175,50 @@ int numDestinationSamplesRequired(const QAudioFormat& sourceFormat, const QAudio
|
||||||
return (numSourceSamples * ratio) + 0.5f;
|
return (numSourceSamples * ratio) + 0.5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
QString friendlyNameForAudioDevice(IMMDevice* pEndpoint) {
|
||||||
|
QString deviceName;
|
||||||
|
IPropertyStore* pPropertyStore;
|
||||||
|
pEndpoint->OpenPropertyStore(STGM_READ, &pPropertyStore);
|
||||||
|
pEndpoint->Release();
|
||||||
|
pEndpoint = NULL;
|
||||||
|
PROPVARIANT pv;
|
||||||
|
PropVariantInit(&pv);
|
||||||
|
HRESULT hr = pPropertyStore->GetValue(PKEY_Device_FriendlyName, &pv);
|
||||||
|
pPropertyStore->Release();
|
||||||
|
pPropertyStore = NULL;
|
||||||
|
deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal);
|
||||||
|
if (!IsWindows8OrGreater()) {
|
||||||
|
// Windows 7 provides only the 31 first characters of the device name.
|
||||||
|
const DWORD QT_WIN7_MAX_AUDIO_DEVICENAME_LEN = 31;
|
||||||
|
deviceName = deviceName.left(QT_WIN7_MAX_AUDIO_DEVICENAME_LEN);
|
||||||
|
}
|
||||||
|
PropVariantClear(&pv);
|
||||||
|
return deviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AudioClient::friendlyNameForAudioDevice(wchar_t* guid) {
|
||||||
|
QString deviceName;
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
CoInitialize(NULL);
|
||||||
|
IMMDeviceEnumerator* pMMDeviceEnumerator = NULL;
|
||||||
|
CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator);
|
||||||
|
IMMDevice* pEndpoint;
|
||||||
|
hr = pMMDeviceEnumerator->GetDevice(guid, &pEndpoint);
|
||||||
|
if (hr == E_NOTFOUND) {
|
||||||
|
printf("Audio Error: device not found\n");
|
||||||
|
deviceName = QString("NONE");
|
||||||
|
} else {
|
||||||
|
deviceName = ::friendlyNameForAudioDevice(pEndpoint);
|
||||||
|
}
|
||||||
|
pMMDeviceEnumerator->Release();
|
||||||
|
pMMDeviceEnumerator = NULL;
|
||||||
|
CoUninitialize();
|
||||||
|
return deviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
|
QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
if (QAudioDeviceInfo::availableDevices(mode).size() > 1) {
|
if (QAudioDeviceInfo::availableDevices(mode).size() > 1) {
|
||||||
|
@ -248,23 +292,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
|
||||||
printf("Audio Error: device not found\n");
|
printf("Audio Error: device not found\n");
|
||||||
deviceName = QString("NONE");
|
deviceName = QString("NONE");
|
||||||
} else {
|
} else {
|
||||||
IPropertyStore* pPropertyStore;
|
deviceName = friendlyNameForAudioDevice(pEndpoint);
|
||||||
pEndpoint->OpenPropertyStore(STGM_READ, &pPropertyStore);
|
|
||||||
pEndpoint->Release();
|
|
||||||
pEndpoint = NULL;
|
|
||||||
PROPVARIANT pv;
|
|
||||||
PropVariantInit(&pv);
|
|
||||||
hr = pPropertyStore->GetValue(PKEY_Device_FriendlyName, &pv);
|
|
||||||
pPropertyStore->Release();
|
|
||||||
pPropertyStore = NULL;
|
|
||||||
deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal);
|
|
||||||
if (!IsWindows8OrGreater()) {
|
|
||||||
// Windows 7 provides only the 31 first characters of the device name.
|
|
||||||
const DWORD QT_WIN7_MAX_AUDIO_DEVICENAME_LEN = 31;
|
|
||||||
deviceName = deviceName.left(QT_WIN7_MAX_AUDIO_DEVICENAME_LEN);
|
|
||||||
}
|
|
||||||
qCDebug(audioclient) << (mode == QAudio::AudioOutput ? "output" : "input") << " device:" << deviceName;
|
|
||||||
PropVariantClear(&pv);
|
|
||||||
}
|
}
|
||||||
pMMDeviceEnumerator->Release();
|
pMMDeviceEnumerator->Release();
|
||||||
pMMDeviceEnumerator = NULL;
|
pMMDeviceEnumerator = NULL;
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <QtCore/qsystemdetection.h>
|
||||||
#include <QtCore/QByteArray>
|
#include <QtCore/QByteArray>
|
||||||
#include <QtCore/QElapsedTimer>
|
#include <QtCore/QElapsedTimer>
|
||||||
#include <QtCore/QObject>
|
#include <QtCore/QObject>
|
||||||
|
@ -126,6 +127,10 @@ public:
|
||||||
|
|
||||||
static const float CALLBACK_ACCELERATOR_RATIO;
|
static const float CALLBACK_ACCELERATOR_RATIO;
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
static QString friendlyNameForAudioDevice(wchar_t* guid);
|
||||||
|
#endif
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
|
|
|
@ -36,5 +36,5 @@ SharedSoundPointer SoundCache::getSound(const QUrl& url) {
|
||||||
QSharedPointer<Resource> SoundCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
|
QSharedPointer<Resource> SoundCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
|
||||||
bool delayLoad, const void* extra) {
|
bool delayLoad, const void* extra) {
|
||||||
qCDebug(audio) << "Requesting sound at" << url.toString();
|
qCDebug(audio) << "Requesting sound at" << url.toString();
|
||||||
return QSharedPointer<Resource>(new Sound(url), &Resource::allReferencesCleared);
|
return QSharedPointer<Resource>(new Sound(url), &Resource::deleter);
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,11 +116,18 @@ QVariantMap RenderableModelEntityItem::parseTexturesToMap(QString textures) {
|
||||||
|
|
||||||
QJsonParseError error;
|
QJsonParseError error;
|
||||||
QJsonDocument texturesJson = QJsonDocument::fromJson(textures.toUtf8(), &error);
|
QJsonDocument texturesJson = QJsonDocument::fromJson(textures.toUtf8(), &error);
|
||||||
|
// If textures are invalid, revert to original textures
|
||||||
if (error.error != QJsonParseError::NoError) {
|
if (error.error != QJsonParseError::NoError) {
|
||||||
qCWarning(entitiesrenderer) << "Could not evaluate textures property value:" << textures;
|
qCWarning(entitiesrenderer) << "Could not evaluate textures property value:" << textures;
|
||||||
return _originalTextures;
|
return _originalTextures;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariantMap texturesMap = texturesJson.toVariant().toMap();
|
||||||
|
// If textures are unset, revert to original textures
|
||||||
|
if (texturesMap.isEmpty()) {
|
||||||
|
return _originalTextures;
|
||||||
|
}
|
||||||
|
|
||||||
return texturesJson.toVariant().toMap();
|
return texturesJson.toVariant().toMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,10 +140,8 @@ void RenderableModelEntityItem::remapTextures() {
|
||||||
return; // nothing to do if the model has not yet loaded
|
return; // nothing to do if the model has not yet loaded
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& geometry = _model->getGeometry()->getGeometry();
|
|
||||||
|
|
||||||
if (!_originalTexturesRead) {
|
if (!_originalTexturesRead) {
|
||||||
_originalTextures = geometry->getTextures();
|
_originalTextures = _model->getTextures();
|
||||||
_originalTexturesRead = true;
|
_originalTexturesRead = true;
|
||||||
|
|
||||||
// Default to _originalTextures to avoid remapping immediately and lagging on load
|
// Default to _originalTextures to avoid remapping immediately and lagging on load
|
||||||
|
@ -152,7 +157,7 @@ void RenderableModelEntityItem::remapTextures() {
|
||||||
auto newTextures = parseTexturesToMap(textures);
|
auto newTextures = parseTexturesToMap(textures);
|
||||||
|
|
||||||
if (newTextures != _currentTextures) {
|
if (newTextures != _currentTextures) {
|
||||||
geometry->setTextures(newTextures);
|
_model->setTextures(newTextures);
|
||||||
_currentTextures = newTextures;
|
_currentTextures = newTextures;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -366,41 +371,16 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
||||||
assert(getType() == EntityTypes::Model);
|
assert(getType() == EntityTypes::Model);
|
||||||
|
|
||||||
if (hasModel()) {
|
if (hasModel()) {
|
||||||
if (_model) {
|
// Prepare the current frame
|
||||||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
|
||||||
|
|
||||||
// check to see if when we added our models to the scene they were ready, if they were not ready, then
|
|
||||||
// fix them up in the scene
|
|
||||||
bool shouldShowCollisionHull = (args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS) > 0;
|
|
||||||
if (_model->needsFixupInScene() || _showCollisionHull != shouldShowCollisionHull) {
|
|
||||||
_showCollisionHull = shouldShowCollisionHull;
|
|
||||||
render::PendingChanges pendingChanges;
|
|
||||||
|
|
||||||
_model->removeFromScene(scene, pendingChanges);
|
|
||||||
|
|
||||||
render::Item::Status::Getters statusGetters;
|
|
||||||
makeEntityItemStatusGetters(getThisPointer(), statusGetters);
|
|
||||||
_model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull);
|
|
||||||
|
|
||||||
scene->enqueuePendingChanges(pendingChanges);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: this seems like it could be optimized if we tracked our last known visible state in
|
|
||||||
// the renderable item. As it stands now the model checks it's visible/invisible state
|
|
||||||
// so most of the time we don't do anything in this function.
|
|
||||||
_model->setVisibleInScene(getVisible(), scene);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
remapTextures();
|
|
||||||
{
|
{
|
||||||
// float alpha = getLocalRenderAlpha();
|
|
||||||
|
|
||||||
if (!_model || _needsModelReload) {
|
if (!_model || _needsModelReload) {
|
||||||
// TODO: this getModel() appears to be about 3% of model render time. We should optimize
|
// TODO: this getModel() appears to be about 3% of model render time. We should optimize
|
||||||
PerformanceTimer perfTimer("getModel");
|
PerformanceTimer perfTimer("getModel");
|
||||||
EntityTreeRenderer* renderer = static_cast<EntityTreeRenderer*>(args->_renderer);
|
EntityTreeRenderer* renderer = static_cast<EntityTreeRenderer*>(args->_renderer);
|
||||||
getModel(renderer);
|
getModel(renderer);
|
||||||
|
|
||||||
|
// Remap textures immediately after loading to avoid flicker
|
||||||
|
remapTextures();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_model) {
|
if (_model) {
|
||||||
|
@ -431,15 +411,40 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
updateModelBounds();
|
updateModelBounds();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the URL has changed
|
// Enqueue updates for the next frame
|
||||||
// Do this last as the getModel is queued for the next frame,
|
if (_model) {
|
||||||
// and we need to keep state directing the model to reinitialize
|
|
||||||
auto& currentURL = getParsedModelURL();
|
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||||
if (currentURL != _model->getURL()) {
|
|
||||||
// Defer setting the url to the render thread
|
// FIXME: this seems like it could be optimized if we tracked our last known visible state in
|
||||||
getModel(_myRenderer);
|
// the renderable item. As it stands now the model checks it's visible/invisible state
|
||||||
}
|
// so most of the time we don't do anything in this function.
|
||||||
|
_model->setVisibleInScene(getVisible(), scene);
|
||||||
|
|
||||||
|
// Remap textures for the next frame to avoid flicker
|
||||||
|
remapTextures();
|
||||||
|
|
||||||
|
// check to see if when we added our models to the scene they were ready, if they were not ready, then
|
||||||
|
// fix them up in the scene
|
||||||
|
bool shouldShowCollisionHull = (args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS) > 0;
|
||||||
|
if (_model->needsFixupInScene() || _showCollisionHull != shouldShowCollisionHull) {
|
||||||
|
_showCollisionHull = shouldShowCollisionHull;
|
||||||
|
render::PendingChanges pendingChanges;
|
||||||
|
|
||||||
|
render::Item::Status::Getters statusGetters;
|
||||||
|
makeEntityItemStatusGetters(getThisPointer(), statusGetters);
|
||||||
|
_model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull);
|
||||||
|
|
||||||
|
scene->enqueuePendingChanges(pendingChanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& currentURL = getParsedModelURL();
|
||||||
|
if (currentURL != _model->getURL()) {
|
||||||
|
// Defer setting the url to the render thread
|
||||||
|
getModel(_myRenderer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -460,6 +460,14 @@ uint32 Texture::getStoredMipSize(uint16 level) const {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gpu::Resource::Size Texture::getStoredSize() const {
|
||||||
|
auto size = 0;
|
||||||
|
for (int level = 0; level < evalNumMips(); ++level) {
|
||||||
|
size += getStoredMipSize(level);
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
uint16 Texture::evalNumSamplesUsed(uint16 numSamplesTried) {
|
uint16 Texture::evalNumSamplesUsed(uint16 numSamplesTried) {
|
||||||
uint16 sample = numSamplesTried;
|
uint16 sample = numSamplesTried;
|
||||||
if (numSamplesTried <= 1)
|
if (numSamplesTried <= 1)
|
||||||
|
|
|
@ -289,9 +289,12 @@ public:
|
||||||
Stamp getStamp() const { return _stamp; }
|
Stamp getStamp() const { return _stamp; }
|
||||||
Stamp getDataStamp() const { return _storage->getStamp(); }
|
Stamp getDataStamp() const { return _storage->getStamp(); }
|
||||||
|
|
||||||
// The size in bytes of data stored in the texture
|
// The theoretical size in bytes of data stored in the texture
|
||||||
Size getSize() const { return _size; }
|
Size getSize() const { return _size; }
|
||||||
|
|
||||||
|
// The actual size in bytes of data stored in the texture
|
||||||
|
Size getStoredSize() const;
|
||||||
|
|
||||||
// Resize, unless auto mips mode would destroy all the sub mips
|
// Resize, unless auto mips mode would destroy all the sub mips
|
||||||
Size resize1D(uint16 width, uint16 numSamples);
|
Size resize1D(uint16 width, uint16 numSamples);
|
||||||
Size resize2D(uint16 width, uint16 height, uint16 numSamples);
|
Size resize2D(uint16 width, uint16 height, uint16 numSamples);
|
||||||
|
|
|
@ -66,16 +66,15 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) {
|
||||||
GeometryExtra extra{ mapping, textureBaseUrl };
|
GeometryExtra extra{ mapping, textureBaseUrl };
|
||||||
|
|
||||||
// Get the raw GeometryResource, not the wrapped NetworkGeometry
|
// Get the raw GeometryResource, not the wrapped NetworkGeometry
|
||||||
_geometryResource = modelCache->getResource(url, QUrl(), true, &extra).staticCast<GeometryResource>();
|
_geometryResource = modelCache->getResource(url, QUrl(), false, &extra).staticCast<GeometryResource>();
|
||||||
|
// Avoid caching nested resources - their references will be held by the parent
|
||||||
|
_geometryResource->_isCacheable = false;
|
||||||
|
|
||||||
if (_geometryResource->isLoaded()) {
|
if (_geometryResource->isLoaded()) {
|
||||||
onGeometryMappingLoaded(!_geometryResource->getURL().isEmpty());
|
onGeometryMappingLoaded(!_geometryResource->getURL().isEmpty());
|
||||||
} else {
|
} else {
|
||||||
connect(_geometryResource.data(), &Resource::finished, this, &GeometryMappingResource::onGeometryMappingLoaded);
|
connect(_geometryResource.data(), &Resource::finished, this, &GeometryMappingResource::onGeometryMappingLoaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid caching nested resources - their references will be held by the parent
|
|
||||||
_geometryResource->_isCacheable = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,6 +85,10 @@ void GeometryMappingResource::onGeometryMappingLoaded(bool success) {
|
||||||
_meshes = _geometryResource->_meshes;
|
_meshes = _geometryResource->_meshes;
|
||||||
_materials = _geometryResource->_materials;
|
_materials = _geometryResource->_materials;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Avoid holding onto extra references
|
||||||
|
_geometryResource.reset();
|
||||||
|
|
||||||
finishedLoading(success);
|
finishedLoading(success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +160,7 @@ class GeometryDefinitionResource : public GeometryResource {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
GeometryDefinitionResource(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) :
|
GeometryDefinitionResource(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) :
|
||||||
GeometryResource(url), _mapping(mapping), _textureBaseUrl(textureBaseUrl.isValid() ? textureBaseUrl : url) {}
|
GeometryResource(url, textureBaseUrl.isValid() ? textureBaseUrl : url), _mapping(mapping) {}
|
||||||
|
|
||||||
virtual void downloadFinished(const QByteArray& data) override;
|
virtual void downloadFinished(const QByteArray& data) override;
|
||||||
|
|
||||||
|
@ -166,7 +169,6 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVariantHash _mapping;
|
QVariantHash _mapping;
|
||||||
QUrl _textureBaseUrl;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void GeometryDefinitionResource::downloadFinished(const QByteArray& data) {
|
void GeometryDefinitionResource::downloadFinished(const QByteArray& data) {
|
||||||
|
@ -220,13 +222,20 @@ QSharedPointer<Resource> ModelCache::createResource(const QUrl& url, const QShar
|
||||||
resource = new GeometryDefinitionResource(url, geometryExtra->mapping, geometryExtra->textureBaseUrl);
|
resource = new GeometryDefinitionResource(url, geometryExtra->mapping, geometryExtra->textureBaseUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
return QSharedPointer<Resource>(resource, &Resource::allReferencesCleared);
|
return QSharedPointer<Resource>(resource, &Resource::deleter);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<NetworkGeometry> ModelCache::getGeometry(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) {
|
std::shared_ptr<NetworkGeometry> ModelCache::getGeometry(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) {
|
||||||
GeometryExtra geometryExtra = { mapping, textureBaseUrl };
|
GeometryExtra geometryExtra = { mapping, textureBaseUrl };
|
||||||
GeometryResource::Pointer resource = getResource(url, QUrl(), true, &geometryExtra).staticCast<GeometryResource>();
|
GeometryResource::Pointer resource = getResource(url, QUrl(), true, &geometryExtra).staticCast<GeometryResource>();
|
||||||
return std::make_shared<NetworkGeometry>(resource);
|
if (resource) {
|
||||||
|
if (resource->isLoaded() && !resource->hasTextures()) {
|
||||||
|
resource->setTextures();
|
||||||
|
}
|
||||||
|
return std::make_shared<NetworkGeometry>(resource);
|
||||||
|
} else {
|
||||||
|
return NetworkGeometry::Pointer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const QVariantMap Geometry::getTextures() const {
|
const QVariantMap Geometry::getTextures() const {
|
||||||
|
@ -270,6 +279,9 @@ void Geometry::setTextures(const QVariantMap& textureMap) {
|
||||||
|
|
||||||
material->setTextures(textureMap);
|
material->setTextures(textureMap);
|
||||||
_areTexturesLoaded = false;
|
_areTexturesLoaded = false;
|
||||||
|
|
||||||
|
// If we only use cached textures, they should all be loaded
|
||||||
|
areTexturesLoaded();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -279,8 +291,6 @@ void Geometry::setTextures(const QVariantMap& textureMap) {
|
||||||
|
|
||||||
bool Geometry::areTexturesLoaded() const {
|
bool Geometry::areTexturesLoaded() const {
|
||||||
if (!_areTexturesLoaded) {
|
if (!_areTexturesLoaded) {
|
||||||
_hasTransparentTextures = false;
|
|
||||||
|
|
||||||
for (auto& material : _materials) {
|
for (auto& material : _materials) {
|
||||||
// Check if material textures are loaded
|
// Check if material textures are loaded
|
||||||
if (std::any_of(material->_textures.cbegin(), material->_textures.cend(),
|
if (std::any_of(material->_textures.cbegin(), material->_textures.cend(),
|
||||||
|
@ -293,8 +303,6 @@ bool Geometry::areTexturesLoaded() const {
|
||||||
const auto albedoTexture = material->_textures[NetworkMaterial::MapChannel::ALBEDO_MAP];
|
const auto albedoTexture = material->_textures[NetworkMaterial::MapChannel::ALBEDO_MAP];
|
||||||
if (albedoTexture.texture && albedoTexture.texture->getGPUTexture()) {
|
if (albedoTexture.texture && albedoTexture.texture->getGPUTexture()) {
|
||||||
material->resetOpacityMap();
|
material->resetOpacityMap();
|
||||||
|
|
||||||
_hasTransparentTextures |= material->getKey().isTranslucent();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,6 +321,21 @@ const std::shared_ptr<const NetworkMaterial> Geometry::getShapeMaterial(int shap
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GeometryResource::deleter() {
|
||||||
|
resetTextures();
|
||||||
|
Resource::deleter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeometryResource::setTextures() {
|
||||||
|
for (const FBXMaterial& material : _geometry->materials) {
|
||||||
|
_materials.push_back(std::make_shared<NetworkMaterial>(material, _textureBaseUrl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeometryResource::resetTextures() {
|
||||||
|
_materials.clear();
|
||||||
|
}
|
||||||
|
|
||||||
NetworkGeometry::NetworkGeometry(const GeometryResource::Pointer& networkGeometry) : _resource(networkGeometry) {
|
NetworkGeometry::NetworkGeometry(const GeometryResource::Pointer& networkGeometry) : _resource(networkGeometry) {
|
||||||
connect(_resource.data(), &Resource::finished, this, &NetworkGeometry::resourceFinished);
|
connect(_resource.data(), &Resource::finished, this, &NetworkGeometry::resourceFinished);
|
||||||
connect(_resource.data(), &Resource::onRefresh, this, &NetworkGeometry::resourceRefreshed);
|
connect(_resource.data(), &Resource::onRefresh, this, &NetworkGeometry::resourceRefreshed);
|
||||||
|
|
|
@ -74,9 +74,6 @@ public:
|
||||||
void setTextures(const QVariantMap& textureMap);
|
void setTextures(const QVariantMap& textureMap);
|
||||||
|
|
||||||
virtual bool areTexturesLoaded() const;
|
virtual bool areTexturesLoaded() const;
|
||||||
// Returns true if any albedo texture has a non-masking alpha channel.
|
|
||||||
// This can only be known after areTexturesLoaded().
|
|
||||||
bool hasTransparentTextures() const { return _hasTransparentTextures; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class GeometryMappingResource;
|
friend class GeometryMappingResource;
|
||||||
|
@ -91,7 +88,6 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable bool _areTexturesLoaded { false };
|
mutable bool _areTexturesLoaded { false };
|
||||||
mutable bool _hasTransparentTextures { false };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A geometry loaded from the network.
|
/// A geometry loaded from the network.
|
||||||
|
@ -99,15 +95,24 @@ class GeometryResource : public Resource, public Geometry {
|
||||||
public:
|
public:
|
||||||
using Pointer = QSharedPointer<GeometryResource>;
|
using Pointer = QSharedPointer<GeometryResource>;
|
||||||
|
|
||||||
GeometryResource(const QUrl& url) : Resource(url) {}
|
GeometryResource(const QUrl& url, const QUrl& textureBaseUrl = QUrl()) : Resource(url) {}
|
||||||
|
|
||||||
virtual bool areTexturesLoaded() const { return isLoaded() && Geometry::areTexturesLoaded(); }
|
virtual bool areTexturesLoaded() const { return isLoaded() && Geometry::areTexturesLoaded(); }
|
||||||
|
|
||||||
|
virtual void deleter() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
friend class ModelCache;
|
||||||
friend class GeometryMappingResource;
|
friend class GeometryMappingResource;
|
||||||
|
|
||||||
virtual bool isCacheable() const override { return _loaded && _isCacheable; }
|
// Geometries may not hold onto textures while cached - that is for the texture cache
|
||||||
|
bool hasTextures() const { return !_materials.empty(); }
|
||||||
|
void setTextures();
|
||||||
|
void resetTextures();
|
||||||
|
|
||||||
|
QUrl _textureBaseUrl;
|
||||||
|
|
||||||
|
virtual bool isCacheable() const override { return _loaded && _isCacheable; }
|
||||||
bool _isCacheable { true };
|
bool _isCacheable { true };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,6 @@ NetworkShaderPointer ShaderCache::getShader(const QUrl& url) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<Resource> ShaderCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
|
QSharedPointer<Resource> ShaderCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
|
||||||
return QSharedPointer<Resource>(new NetworkShader(url, delayLoad), &Resource::allReferencesCleared);
|
return QSharedPointer<Resource>(new NetworkShader(url, delayLoad), &Resource::deleter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -166,12 +166,11 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path) {
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QSharedPointer<Resource> TextureCache::createResource(const QUrl& url,
|
QSharedPointer<Resource> TextureCache::createResource(const QUrl& url,
|
||||||
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
|
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
|
||||||
const TextureExtra* textureExtra = static_cast<const TextureExtra*>(extra);
|
const TextureExtra* textureExtra = static_cast<const TextureExtra*>(extra);
|
||||||
return QSharedPointer<Resource>(new NetworkTexture(url, textureExtra->type, textureExtra->content),
|
return QSharedPointer<Resource>(new NetworkTexture(url, textureExtra->type, textureExtra->content),
|
||||||
&Resource::allReferencesCleared);
|
&Resource::deleter);
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content) :
|
NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content) :
|
||||||
|
@ -339,10 +338,13 @@ void NetworkTexture::setImage(void* voidTexture, int originalWidth,
|
||||||
if (gpuTexture) {
|
if (gpuTexture) {
|
||||||
_width = gpuTexture->getWidth();
|
_width = gpuTexture->getWidth();
|
||||||
_height = gpuTexture->getHeight();
|
_height = gpuTexture->getHeight();
|
||||||
|
setBytes(gpuTexture->getStoredSize());
|
||||||
} else {
|
} else {
|
||||||
|
// FIXME: If !gpuTexture, we failed to load!
|
||||||
_width = _height = 0;
|
_width = _height = 0;
|
||||||
|
qWarning() << "Texture did not load";
|
||||||
}
|
}
|
||||||
|
|
||||||
finishedLoading(true);
|
finishedLoading(true);
|
||||||
|
|
||||||
emit networkTextureCreated(qWeakPointerCast<NetworkTexture, Resource> (_self));
|
emit networkTextureCreated(qWeakPointerCast<NetworkTexture, Resource> (_self));
|
||||||
|
|
|
@ -117,22 +117,22 @@ void ResourceCache::setUnusedResourceCacheSize(qint64 unusedResourcesMaxSize) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourceCache::addUnusedResource(const QSharedPointer<Resource>& resource) {
|
void ResourceCache::addUnusedResource(const QSharedPointer<Resource>& resource) {
|
||||||
if (resource->getBytesTotal() > _unusedResourcesMaxSize) {
|
// If it doesn't fit or its size is unknown, leave the cache alone.
|
||||||
// If it doesn't fit anyway, let's leave whatever is already in the cache.
|
if (resource->getBytes() == 0 || resource->getBytes() > _unusedResourcesMaxSize) {
|
||||||
resource->setCache(nullptr);
|
resource->setCache(nullptr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
reserveUnusedResource(resource->getBytesTotal());
|
reserveUnusedResource(resource->getBytes());
|
||||||
|
|
||||||
resource->setLRUKey(++_lastLRUKey);
|
resource->setLRUKey(++_lastLRUKey);
|
||||||
_unusedResources.insert(resource->getLRUKey(), resource);
|
_unusedResources.insert(resource->getLRUKey(), resource);
|
||||||
_unusedResourcesSize += resource->getBytesTotal();
|
_unusedResourcesSize += resource->getBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourceCache::removeUnusedResource(const QSharedPointer<Resource>& resource) {
|
void ResourceCache::removeUnusedResource(const QSharedPointer<Resource>& resource) {
|
||||||
if (_unusedResources.contains(resource->getLRUKey())) {
|
if (_unusedResources.contains(resource->getLRUKey())) {
|
||||||
_unusedResources.remove(resource->getLRUKey());
|
_unusedResources.remove(resource->getLRUKey());
|
||||||
_unusedResourcesSize -= resource->getBytesTotal();
|
_unusedResourcesSize -= resource->getBytes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ void ResourceCache::reserveUnusedResource(qint64 resourceSize) {
|
||||||
// unload the oldest resource
|
// unload the oldest resource
|
||||||
QMap<int, QSharedPointer<Resource> >::iterator it = _unusedResources.begin();
|
QMap<int, QSharedPointer<Resource> >::iterator it = _unusedResources.begin();
|
||||||
|
|
||||||
_unusedResourcesSize -= it.value()->getBytesTotal();
|
_unusedResourcesSize -= it.value()->getBytes();
|
||||||
it.value()->setCache(nullptr);
|
it.value()->setCache(nullptr);
|
||||||
_unusedResources.erase(it);
|
_unusedResources.erase(it);
|
||||||
}
|
}
|
||||||
|
@ -399,7 +399,7 @@ void Resource::makeRequest() {
|
||||||
connect(_request, &ResourceRequest::progress, this, &Resource::handleDownloadProgress);
|
connect(_request, &ResourceRequest::progress, this, &Resource::handleDownloadProgress);
|
||||||
connect(_request, &ResourceRequest::finished, this, &Resource::handleReplyFinished);
|
connect(_request, &ResourceRequest::finished, this, &Resource::handleReplyFinished);
|
||||||
|
|
||||||
_bytesReceived = _bytesTotal = 0;
|
_bytesReceived = _bytesTotal = _bytes = 0;
|
||||||
|
|
||||||
_request->send();
|
_request->send();
|
||||||
}
|
}
|
||||||
|
@ -412,6 +412,8 @@ void Resource::handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTota
|
||||||
void Resource::handleReplyFinished() {
|
void Resource::handleReplyFinished() {
|
||||||
Q_ASSERT_X(_request, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished");
|
Q_ASSERT_X(_request, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished");
|
||||||
|
|
||||||
|
_bytes = _bytesTotal;
|
||||||
|
|
||||||
if (!_request || _request != sender()) {
|
if (!_request || _request != sender()) {
|
||||||
// This can happen in the edge case that a request is timed out, but a `finished` signal is emitted before it is deleted.
|
// This can happen in the edge case that a request is timed out, but a `finished` signal is emitted before it is deleted.
|
||||||
qWarning(networking) << "Received signal Resource::handleReplyFinished from ResourceRequest that is not the current"
|
qWarning(networking) << "Received signal Resource::handleReplyFinished from ResourceRequest that is not the current"
|
||||||
|
|
|
@ -181,6 +181,9 @@ public:
|
||||||
/// For loading resources, returns the number of total bytes (<= zero if unknown).
|
/// For loading resources, returns the number of total bytes (<= zero if unknown).
|
||||||
qint64 getBytesTotal() const { return _bytesTotal; }
|
qint64 getBytesTotal() const { return _bytesTotal; }
|
||||||
|
|
||||||
|
/// For loaded resources, returns the number of actual bytes (defaults to total bytes if not explicitly set).
|
||||||
|
qint64 getBytes() const { return _bytes; }
|
||||||
|
|
||||||
/// For loading resources, returns the load progress.
|
/// For loading resources, returns the load progress.
|
||||||
float getProgress() const { return (_bytesTotal <= 0) ? 0.0f : (float)_bytesReceived / _bytesTotal; }
|
float getProgress() const { return (_bytesTotal <= 0) ? 0.0f : (float)_bytesReceived / _bytesTotal; }
|
||||||
|
|
||||||
|
@ -191,7 +194,7 @@ public:
|
||||||
|
|
||||||
void setCache(ResourceCache* cache) { _cache = cache; }
|
void setCache(ResourceCache* cache) { _cache = cache; }
|
||||||
|
|
||||||
Q_INVOKABLE void allReferencesCleared();
|
virtual void deleter() { allReferencesCleared(); }
|
||||||
|
|
||||||
const QUrl& getURL() const { return _url; }
|
const QUrl& getURL() const { return _url; }
|
||||||
|
|
||||||
|
@ -222,10 +225,15 @@ protected:
|
||||||
/// This should be overridden by subclasses that need to process the data once it is downloaded.
|
/// This should be overridden by subclasses that need to process the data once it is downloaded.
|
||||||
virtual void downloadFinished(const QByteArray& data) { finishedLoading(true); }
|
virtual void downloadFinished(const QByteArray& data) { finishedLoading(true); }
|
||||||
|
|
||||||
|
/// Called when the download is finished and processed, sets the number of actual bytes.
|
||||||
|
void setBytes(qint64 bytes) { _bytes = bytes; }
|
||||||
|
|
||||||
/// Called when the download is finished and processed.
|
/// Called when the download is finished and processed.
|
||||||
/// This should be called by subclasses that override downloadFinished to mark the end of processing.
|
/// This should be called by subclasses that override downloadFinished to mark the end of processing.
|
||||||
Q_INVOKABLE void finishedLoading(bool success);
|
Q_INVOKABLE void finishedLoading(bool success);
|
||||||
|
|
||||||
|
Q_INVOKABLE void allReferencesCleared();
|
||||||
|
|
||||||
QUrl _url;
|
QUrl _url;
|
||||||
QUrl _activeUrl;
|
QUrl _activeUrl;
|
||||||
bool _startedLoading = false;
|
bool _startedLoading = false;
|
||||||
|
@ -253,6 +261,7 @@ private:
|
||||||
QTimer* _replyTimer = nullptr;
|
QTimer* _replyTimer = nullptr;
|
||||||
qint64 _bytesReceived = 0;
|
qint64 _bytesReceived = 0;
|
||||||
qint64 _bytesTotal = 0;
|
qint64 _bytesTotal = 0;
|
||||||
|
qint64 _bytes = 0;
|
||||||
int _attempts = 0;
|
int _attempts = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,9 @@ public:
|
||||||
/// whether the HMD is being worn
|
/// whether the HMD is being worn
|
||||||
virtual bool isDisplayVisible() const { return false; }
|
virtual bool isDisplayVisible() const { return false; }
|
||||||
|
|
||||||
|
virtual QString getPreferredAudioInDevice() const { return QString(); }
|
||||||
|
virtual QString getPreferredAudioOutDevice() const { return QString(); }
|
||||||
|
|
||||||
// Rendering support
|
// Rendering support
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -36,6 +36,6 @@ NetworkClipLoaderPointer ClipCache::getClipLoader(const QUrl& url) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<Resource> ClipCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
|
QSharedPointer<Resource> ClipCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
|
||||||
return QSharedPointer<Resource>(new NetworkClipLoader(url, delayLoad), &Resource::allReferencesCleared);
|
return QSharedPointer<Resource>(new NetworkClipLoader(url, delayLoad), &Resource::deleter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,14 +76,9 @@ AbstractViewStateInterface* Model::_viewState = NULL;
|
||||||
|
|
||||||
bool Model::needsFixupInScene() const {
|
bool Model::needsFixupInScene() const {
|
||||||
if (readyToAddToScene()) {
|
if (readyToAddToScene()) {
|
||||||
// Once textures are loaded, fixup if they are now transparent
|
if (_needsUpdateTextures && _geometry->getGeometry()->areTexturesLoaded()) {
|
||||||
if (_needsUpdateTransparentTextures && _geometry->getGeometry()->areTexturesLoaded()) {
|
_needsUpdateTextures = false;
|
||||||
_needsUpdateTransparentTextures = false;
|
return true;
|
||||||
bool hasTransparentTextures = _geometry->getGeometry()->hasTransparentTextures();
|
|
||||||
if (_hasTransparentTextures != hasTransparentTextures) {
|
|
||||||
_hasTransparentTextures = hasTransparentTextures;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!_readyWhenAdded) {
|
if (!_readyWhenAdded) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -546,43 +541,6 @@ void Model::setVisibleInScene(bool newValue, std::shared_ptr<render::Scene> scen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges, bool showCollisionHull) {
|
|
||||||
|
|
||||||
if ((!_meshGroupsKnown || showCollisionHull != _showCollisionHull) && isLoaded()) {
|
|
||||||
_showCollisionHull = showCollisionHull;
|
|
||||||
segregateMeshGroups();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool somethingAdded = false;
|
|
||||||
|
|
||||||
foreach (auto renderItem, _modelMeshRenderItemsSet) {
|
|
||||||
auto item = scene->allocateID();
|
|
||||||
auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem);
|
|
||||||
pendingChanges.resetItem(item, renderPayload);
|
|
||||||
pendingChanges.updateItem<ModelMeshPartPayload>(item, [](ModelMeshPartPayload& data) {
|
|
||||||
data.notifyLocationChanged();
|
|
||||||
});
|
|
||||||
_modelMeshRenderItems.insert(item, renderPayload);
|
|
||||||
somethingAdded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (auto renderItem, _collisionRenderItemsSet) {
|
|
||||||
auto item = scene->allocateID();
|
|
||||||
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
|
|
||||||
pendingChanges.resetItem(item, renderPayload);
|
|
||||||
pendingChanges.updateItem<MeshPartPayload>(item, [](MeshPartPayload& data) {
|
|
||||||
data.notifyLocationChanged();
|
|
||||||
});
|
|
||||||
_collisionRenderItems.insert(item, renderPayload);
|
|
||||||
somethingAdded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_readyWhenAdded = readyToAddToScene();
|
|
||||||
|
|
||||||
return somethingAdded;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Model::addToScene(std::shared_ptr<render::Scene> scene,
|
bool Model::addToScene(std::shared_ptr<render::Scene> scene,
|
||||||
render::PendingChanges& pendingChanges,
|
render::PendingChanges& pendingChanges,
|
||||||
render::Item::Status::Getters& statusGetters,
|
render::Item::Status::Getters& statusGetters,
|
||||||
|
@ -594,28 +552,48 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene,
|
||||||
|
|
||||||
bool somethingAdded = false;
|
bool somethingAdded = false;
|
||||||
|
|
||||||
foreach (auto renderItem, _modelMeshRenderItemsSet) {
|
if (_modelMeshRenderItems.size()) {
|
||||||
auto item = scene->allocateID();
|
for (auto item : _modelMeshRenderItems.keys()) {
|
||||||
auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem);
|
pendingChanges.updateItem<ModelMeshPartPayload>(item, [](ModelMeshPartPayload& data) {
|
||||||
renderPayload->addStatusGetters(statusGetters);
|
data.notifyLocationChanged();
|
||||||
pendingChanges.resetItem(item, renderPayload);
|
});
|
||||||
pendingChanges.updateItem<ModelMeshPartPayload>(item, [](ModelMeshPartPayload& data) {
|
}
|
||||||
data.notifyLocationChanged();
|
} else {
|
||||||
});
|
for (auto renderItem : _modelMeshRenderItemsSet) {
|
||||||
_modelMeshRenderItems.insert(item, renderPayload);
|
auto item = scene->allocateID();
|
||||||
somethingAdded = true;
|
auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem);
|
||||||
|
if (statusGetters.size()) {
|
||||||
|
renderPayload->addStatusGetters(statusGetters);
|
||||||
|
}
|
||||||
|
pendingChanges.resetItem(item, renderPayload);
|
||||||
|
pendingChanges.updateItem<ModelMeshPartPayload>(item, [](ModelMeshPartPayload& data) {
|
||||||
|
data.notifyLocationChanged();
|
||||||
|
});
|
||||||
|
_modelMeshRenderItems.insert(item, renderPayload);
|
||||||
|
somethingAdded = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (auto renderItem, _collisionRenderItemsSet) {
|
if (_collisionRenderItems.size()) {
|
||||||
auto item = scene->allocateID();
|
for (auto item : _collisionRenderItems.keys()) {
|
||||||
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
|
pendingChanges.updateItem<MeshPartPayload>(item, [](MeshPartPayload& data) {
|
||||||
renderPayload->addStatusGetters(statusGetters);
|
data.notifyLocationChanged();
|
||||||
pendingChanges.resetItem(item, renderPayload);
|
});
|
||||||
pendingChanges.updateItem<MeshPartPayload>(item, [](MeshPartPayload& data) {
|
}
|
||||||
data.notifyLocationChanged();
|
} else {
|
||||||
});
|
for (auto renderItem : _collisionRenderItemsSet) {
|
||||||
_collisionRenderItems.insert(item, renderPayload);
|
auto item = scene->allocateID();
|
||||||
somethingAdded = true;
|
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
|
||||||
|
if (statusGetters.size()) {
|
||||||
|
renderPayload->addStatusGetters(statusGetters);
|
||||||
|
}
|
||||||
|
pendingChanges.resetItem(item, renderPayload);
|
||||||
|
pendingChanges.updateItem<MeshPartPayload>(item, [](MeshPartPayload& data) {
|
||||||
|
data.notifyLocationChanged();
|
||||||
|
});
|
||||||
|
_collisionRenderItems.insert(item, renderPayload);
|
||||||
|
somethingAdded = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_readyWhenAdded = readyToAddToScene();
|
_readyWhenAdded = readyToAddToScene();
|
||||||
|
@ -791,6 +769,13 @@ int Model::getLastFreeJointIndex(int jointIndex) const {
|
||||||
return (isActive() && jointIndex != -1) ? getFBXGeometry().joints.at(jointIndex).freeLineage.last() : -1;
|
return (isActive() && jointIndex != -1) ? getFBXGeometry().joints.at(jointIndex).freeLineage.last() : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Model::setTextures(const QVariantMap& textures) {
|
||||||
|
if (isLoaded()) {
|
||||||
|
_needsUpdateTextures = true;
|
||||||
|
_geometry->getGeometry()->setTextures(textures);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Model::setURL(const QUrl& url) {
|
void Model::setURL(const QUrl& url) {
|
||||||
// don't recreate the geometry if it's the same URL
|
// don't recreate the geometry if it's the same URL
|
||||||
if (_url == url && _geometry && _geometry->getURL() == url) {
|
if (_url == url && _geometry && _geometry->getURL() == url) {
|
||||||
|
@ -807,8 +792,7 @@ void Model::setURL(const QUrl& url) {
|
||||||
}
|
}
|
||||||
|
|
||||||
_needsReload = true;
|
_needsReload = true;
|
||||||
_needsUpdateTransparentTextures = true;
|
_needsUpdateTextures = true;
|
||||||
_hasTransparentTextures = false;
|
|
||||||
_meshGroupsKnown = false;
|
_meshGroupsKnown = false;
|
||||||
invalidCalculatedMeshBoxes();
|
invalidCalculatedMeshBoxes();
|
||||||
deleteGeometry();
|
deleteGeometry();
|
||||||
|
|
|
@ -87,7 +87,10 @@ public:
|
||||||
bool initWhenReady(render::ScenePointer scene);
|
bool initWhenReady(render::ScenePointer scene);
|
||||||
bool addToScene(std::shared_ptr<render::Scene> scene,
|
bool addToScene(std::shared_ptr<render::Scene> scene,
|
||||||
render::PendingChanges& pendingChanges,
|
render::PendingChanges& pendingChanges,
|
||||||
bool showCollisionHull = false);
|
bool showCollisionHull = false) {
|
||||||
|
auto getters = render::Item::Status::Getters(0);
|
||||||
|
return addToScene(scene, pendingChanges, getters, showCollisionHull);
|
||||||
|
}
|
||||||
bool addToScene(std::shared_ptr<render::Scene> scene,
|
bool addToScene(std::shared_ptr<render::Scene> scene,
|
||||||
render::PendingChanges& pendingChanges,
|
render::PendingChanges& pendingChanges,
|
||||||
render::Item::Status::Getters& statusGetters,
|
render::Item::Status::Getters& statusGetters,
|
||||||
|
@ -129,6 +132,9 @@ public:
|
||||||
/// Returns a reference to the shared collision geometry.
|
/// Returns a reference to the shared collision geometry.
|
||||||
const NetworkGeometry::Pointer& getCollisionGeometry() const { return _collisionGeometry; }
|
const NetworkGeometry::Pointer& getCollisionGeometry() const { return _collisionGeometry; }
|
||||||
|
|
||||||
|
const QVariantMap getTextures() const { assert(isLoaded()); return _geometry->getGeometry()->getTextures(); }
|
||||||
|
void setTextures(const QVariantMap& textures);
|
||||||
|
|
||||||
/// Provided as a convenience, will crash if !isLoaded()
|
/// Provided as a convenience, will crash if !isLoaded()
|
||||||
// And so that getGeometry() isn't chained everywhere
|
// And so that getGeometry() isn't chained everywhere
|
||||||
const FBXGeometry& getFBXGeometry() const { assert(isLoaded()); return getGeometry()->getGeometry()->getGeometry(); }
|
const FBXGeometry& getFBXGeometry() const { assert(isLoaded()); return getGeometry()->getGeometry()->getGeometry(); }
|
||||||
|
@ -385,9 +391,8 @@ protected:
|
||||||
bool _readyWhenAdded { false };
|
bool _readyWhenAdded { false };
|
||||||
bool _needsReload { true };
|
bool _needsReload { true };
|
||||||
bool _needsUpdateClusterMatrices { true };
|
bool _needsUpdateClusterMatrices { true };
|
||||||
mutable bool _needsUpdateTransparentTextures { true };
|
|
||||||
mutable bool _hasTransparentTextures { false };
|
|
||||||
bool _showCollisionHull { false };
|
bool _showCollisionHull { false };
|
||||||
|
mutable bool _needsUpdateTextures { true };
|
||||||
|
|
||||||
friend class ModelMeshPartPayload;
|
friend class ModelMeshPartPayload;
|
||||||
RigPointer _rig;
|
RigPointer _rig;
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <NetworkAccessManager.h>
|
#include <NetworkAccessManager.h>
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
#include "ResourceManager.h"
|
#include "ResourceManager.h"
|
||||||
|
#include "ScriptEngines.h"
|
||||||
|
|
||||||
BatchLoader::BatchLoader(const QList<QUrl>& urls)
|
BatchLoader::BatchLoader(const QList<QUrl>& urls)
|
||||||
: QObject(),
|
: QObject(),
|
||||||
|
@ -34,8 +35,9 @@ void BatchLoader::start() {
|
||||||
}
|
}
|
||||||
|
|
||||||
_started = true;
|
_started = true;
|
||||||
|
|
||||||
for (const auto& url : _urls) {
|
for (const auto& rawURL : _urls) {
|
||||||
|
QUrl url = expandScriptUrl(normalizeScriptURL(rawURL));
|
||||||
auto request = ResourceManager::createResourceRequest(this, url);
|
auto request = ResourceManager::createResourceRequest(this, url);
|
||||||
if (!request) {
|
if (!request) {
|
||||||
_data.insert(url, QString());
|
_data.insert(url, QString());
|
||||||
|
|
|
@ -220,11 +220,10 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_fileNameString = scriptURL.toString();
|
QUrl url = expandScriptUrl(normalizeScriptURL(scriptURL));
|
||||||
|
_fileNameString = url.toString();
|
||||||
_isReloading = reload;
|
_isReloading = reload;
|
||||||
|
|
||||||
QUrl url(scriptURL);
|
|
||||||
|
|
||||||
bool isPending;
|
bool isPending;
|
||||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||||
scriptCache->getScript(url, this, isPending, reload);
|
scriptCache->getScript(url, this, isPending, reload);
|
||||||
|
@ -848,7 +847,7 @@ QUrl ScriptEngine::resolvePath(const QString& include) const {
|
||||||
QUrl url(include);
|
QUrl url(include);
|
||||||
// first lets check to see if it's already a full URL
|
// first lets check to see if it's already a full URL
|
||||||
if (!url.scheme().isEmpty()) {
|
if (!url.scheme().isEmpty()) {
|
||||||
return url;
|
return expandScriptUrl(normalizeScriptURL(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
// we apparently weren't a fully qualified url, so, let's assume we're relative
|
// we apparently weren't a fully qualified url, so, let's assume we're relative
|
||||||
|
@ -865,7 +864,7 @@ QUrl ScriptEngine::resolvePath(const QString& include) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
// at this point we should have a legitimate fully qualified URL for our parent
|
// at this point we should have a legitimate fully qualified URL for our parent
|
||||||
url = parentURL.resolved(url);
|
url = expandScriptUrl(normalizeScriptURL(parentURL.resolved(url)));
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -429,7 +429,7 @@ ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserL
|
||||||
connect(scriptEngine, &ScriptEngine::errorLoadingScript, this, &ScriptEngines::onScriptEngineError);
|
connect(scriptEngine, &ScriptEngine::errorLoadingScript, this, &ScriptEngines::onScriptEngineError);
|
||||||
|
|
||||||
// get the script engine object to load the script at the designated script URL
|
// get the script engine object to load the script at the designated script URL
|
||||||
scriptEngine->loadURL(QUrl(expandScriptUrl(scriptUrl.toString())), reload);
|
scriptEngine->loadURL(scriptUrl, reload);
|
||||||
}
|
}
|
||||||
|
|
||||||
return scriptEngine;
|
return scriptEngine;
|
||||||
|
|
|
@ -309,6 +309,9 @@ void ScriptsModel::rebuildTree() {
|
||||||
QString hash;
|
QString hash;
|
||||||
QStringList pathList = script->getLocalPath().split(tr("/"));
|
QStringList pathList = script->getLocalPath().split(tr("/"));
|
||||||
pathList.removeLast();
|
pathList.removeLast();
|
||||||
|
if (pathList.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
QStringList::const_iterator pathIterator;
|
QStringList::const_iterator pathIterator;
|
||||||
for (pathIterator = pathList.constBegin(); pathIterator != pathList.constEnd(); ++pathIterator) {
|
for (pathIterator = pathList.constBegin(); pathIterator != pathList.constEnd(); ++pathIterator) {
|
||||||
hash.append(*pathIterator + "/");
|
hash.append(*pathIterator + "/");
|
||||||
|
|
|
@ -122,13 +122,18 @@ void QmlWindowClass::initQml(QVariantMap properties) {
|
||||||
object->setProperty(SOURCE_PROPERTY, _source);
|
object->setProperty(SOURCE_PROPERTY, _source);
|
||||||
|
|
||||||
// Forward messages received from QML on to the script
|
// Forward messages received from QML on to the script
|
||||||
connect(_qmlWindow, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(const QVariant&)), Qt::QueuedConnection);
|
connect(_qmlWindow, SIGNAL(sendToScript(QVariant)), this, SLOT(qmlToScript(const QVariant&)), Qt::QueuedConnection);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Q_ASSERT(_qmlWindow);
|
Q_ASSERT(_qmlWindow);
|
||||||
Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow.data()));
|
Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow.data()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QmlWindowClass::qmlToScript(const QVariant& message) {
|
||||||
|
QJSValue js = qvariant_cast<QJSValue>(message);
|
||||||
|
emit fromQml(js.toVariant());
|
||||||
|
}
|
||||||
|
|
||||||
void QmlWindowClass::sendToQml(const QVariant& message) {
|
void QmlWindowClass::sendToQml(const QVariant& message) {
|
||||||
// Forward messages received from the script on to QML
|
// Forward messages received from the script on to QML
|
||||||
QMetaObject::invokeMethod(asQuickItem(), "fromScript", Qt::QueuedConnection, Q_ARG(QVariant, message));
|
QMetaObject::invokeMethod(asQuickItem(), "fromScript", Qt::QueuedConnection, Q_ARG(QVariant, message));
|
||||||
|
|
|
@ -63,6 +63,7 @@ signals:
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void hasClosed();
|
void hasClosed();
|
||||||
|
void qmlToScript(const QVariant& message);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static QVariantMap parseArguments(QScriptContext* context);
|
static QVariantMap parseArguments(QScriptContext* context);
|
||||||
|
|
|
@ -12,8 +12,8 @@ if (WIN32)
|
||||||
add_definitions(-DGLEW_STATIC)
|
add_definitions(-DGLEW_STATIC)
|
||||||
|
|
||||||
set(TARGET_NAME oculus)
|
set(TARGET_NAME oculus)
|
||||||
setup_hifi_plugin()
|
setup_hifi_plugin(Multimedia)
|
||||||
link_hifi_libraries(shared gl gpu controllers ui plugins display-plugins input-plugins)
|
link_hifi_libraries(shared gl gpu controllers ui plugins display-plugins input-plugins audio-client networking)
|
||||||
|
|
||||||
include_hifi_library_headers(octree)
|
include_hifi_library_headers(octree)
|
||||||
|
|
||||||
|
@ -21,5 +21,6 @@ if (WIN32)
|
||||||
find_package(LibOVR REQUIRED)
|
find_package(LibOVR REQUIRED)
|
||||||
target_include_directories(${TARGET_NAME} PRIVATE ${LIBOVR_INCLUDE_DIRS})
|
target_include_directories(${TARGET_NAME} PRIVATE ${LIBOVR_INCLUDE_DIRS})
|
||||||
target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES})
|
target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES})
|
||||||
|
target_link_libraries(${TARGET_NAME} Winmm.lib)
|
||||||
|
|
||||||
endif()
|
endif()
|
|
@ -21,6 +21,7 @@ public:
|
||||||
// Stereo specific methods
|
// Stereo specific methods
|
||||||
virtual void resetSensors() override final;
|
virtual void resetSensors() override final;
|
||||||
virtual void beginFrameRender(uint32_t frameIndex) override;
|
virtual void beginFrameRender(uint32_t frameIndex) override;
|
||||||
|
float getTargetFrameRate() override { return _hmdDesc.DisplayRefreshRate; }
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -6,7 +6,14 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
#include "OculusDisplayPlugin.h"
|
#include "OculusDisplayPlugin.h"
|
||||||
|
|
||||||
|
// Odd ordering of header is required to avoid 'macro redinition warnings'
|
||||||
|
#include <AudioClient.h>
|
||||||
|
|
||||||
|
#include <OVR_CAPI_Audio.h>
|
||||||
|
|
||||||
#include <shared/NsightHelpers.h>
|
#include <shared/NsightHelpers.h>
|
||||||
|
|
||||||
#include "OculusHelpers.h"
|
#include "OculusHelpers.h"
|
||||||
|
|
||||||
const QString OculusDisplayPlugin::NAME("Oculus Rift");
|
const QString OculusDisplayPlugin::NAME("Oculus Rift");
|
||||||
|
@ -86,3 +93,26 @@ void OculusDisplayPlugin::hmdPresent() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OculusDisplayPlugin::isHmdMounted() const {
|
||||||
|
ovrSessionStatus status;
|
||||||
|
return (OVR_SUCCESS(ovr_GetSessionStatus(_session, &status)) &&
|
||||||
|
(ovrFalse != status.HmdMounted));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString OculusDisplayPlugin::getPreferredAudioInDevice() const {
|
||||||
|
WCHAR buffer[OVR_AUDIO_MAX_DEVICE_STR_SIZE];
|
||||||
|
if (!OVR_SUCCESS(ovr_GetAudioDeviceInGuidStr(buffer))) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
return AudioClient::friendlyNameForAudioDevice(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString OculusDisplayPlugin::getPreferredAudioOutDevice() const {
|
||||||
|
WCHAR buffer[OVR_AUDIO_MAX_DEVICE_STR_SIZE];
|
||||||
|
if (!OVR_SUCCESS(ovr_GetAudioDeviceOutGuidStr(buffer))) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
return AudioClient::friendlyNameForAudioDevice(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,20 +12,18 @@
|
||||||
struct SwapFramebufferWrapper;
|
struct SwapFramebufferWrapper;
|
||||||
using SwapFboPtr = QSharedPointer<SwapFramebufferWrapper>;
|
using SwapFboPtr = QSharedPointer<SwapFramebufferWrapper>;
|
||||||
|
|
||||||
const float TARGET_RATE_Oculus = 75.0f;
|
|
||||||
|
|
||||||
class OculusDisplayPlugin : public OculusBaseDisplayPlugin {
|
class OculusDisplayPlugin : public OculusBaseDisplayPlugin {
|
||||||
using Parent = OculusBaseDisplayPlugin;
|
using Parent = OculusBaseDisplayPlugin;
|
||||||
public:
|
public:
|
||||||
const QString& getName() const override { return NAME; }
|
const QString& getName() const override { return NAME; }
|
||||||
|
|
||||||
float getTargetFrameRate() override { return TARGET_RATE_Oculus; }
|
QString getPreferredAudioInDevice() const override;
|
||||||
|
QString getPreferredAudioOutDevice() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool internalActivate() override;
|
bool internalActivate() override;
|
||||||
void hmdPresent() override;
|
void hmdPresent() override;
|
||||||
// FIXME update with Oculus API call once it's available in the SDK
|
bool isHmdMounted() const override;
|
||||||
bool isHmdMounted() const override { return true; }
|
|
||||||
void customizeContext() override;
|
void customizeContext() override;
|
||||||
void uncustomizeContext() override;
|
void uncustomizeContext() override;
|
||||||
void cycleDebugOutput() override;
|
void cycleDebugOutput() override;
|
||||||
|
|
Loading…
Reference in a new issue