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

This commit is contained in:
samcake 2016-03-21 13:30:28 -07:00
commit ec7165983d
30 changed files with 290 additions and 153 deletions

View file

@ -151,7 +151,7 @@
data.properties[group][property] = {
x: elX.value,
y: elY.value,
z: elZ.value,
z: elZ ? elZ.value : 0,
};
EventBridge.emitWebEvent(JSON.stringify(data));
}
@ -683,7 +683,6 @@
elZoneKeyLightAmbientIntensity.value = properties.keyLight.ambientIntensity.toFixed(2);
elZoneKeyLightDirectionX.value = properties.keyLight.direction.x.toFixed(2);
elZoneKeyLightDirectionY.value = properties.keyLight.direction.y.toFixed(2);
elZoneKeyLightDirectionZ.value = properties.keyLight.direction.z.toFixed(2);
elZoneKeyLightAmbientURL.value = properties.keyLight.ambientURL;
@ -937,12 +936,11 @@
elZoneKeyLightColorBlue.addEventListener('change', zoneKeyLightColorChangeFunction);
elZoneKeyLightIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','intensity'));
elZoneKeyLightAmbientIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','ambientIntensity'));
var zoneKeyLightDirectionChangeFunction = createEmitGroupVec3PropertyUpdateFunction('keyLight','direction', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY, elZoneKeyLightDirectionZ);
elZoneKeyLightAmbientURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('keyLight','ambientURL'));
var zoneKeyLightDirectionChangeFunction = createEmitGroupVec3PropertyUpdateFunction('keyLight','direction', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY);
elZoneKeyLightDirectionX.addEventListener('change', zoneKeyLightDirectionChangeFunction);
elZoneKeyLightDirectionY.addEventListener('change', zoneKeyLightDirectionChangeFunction);
elZoneKeyLightDirectionZ.addEventListener('change', zoneKeyLightDirectionChangeFunction);
elZoneKeyLightAmbientURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('keyLight','ambientURL'));
elZoneStageLatitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','latitude'));
elZoneStageLongitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','longitude'));
elZoneStageAltitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','altitude'));
@ -1170,9 +1168,8 @@
<div class="zone-section keyLight-section property">
<div class="label">Light Direction</div>
<div class="value">
<div class="input-area">Pitch <input class="coord" type="number" id="property-zone-key-light-direction-x"></div>
<div class="input-area">Yaw <input class="coord" type="number" id="property-zone-key-light-direction-y"></div>
<div class="input-area">Roll <input class="coord" type="number" id="property-zone-key-light-direction-z"></div>
<div class="input-area">Altitude <input class="coord" type="number" id="property-zone-key-light-direction-x"></div>
<div class="input-area">Azimuth <input class="coord" type="number" id="property-zone-key-light-direction-y"></div>
</div>
</div>

View file

@ -16,8 +16,8 @@ var deletingVoxels = false;
var addingSpheres = false;
var deletingSpheres = false;
var offAlpha = 0.5;
var onAlpha = 0.9;
var offAlpha = 0.8;
var onAlpha = 1.0;
var editSphereRadius = 4;
function floorVector(v) {
@ -48,52 +48,47 @@ var toolBar = (function () {
height: toolHeight,
alpha: onAlpha,
visible: true,
});
}, false);
addVoxelButton = toolBar.addTool({
imageURL: toolIconUrl + "voxel-add.svg",
subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
width: toolWidth,
height: toolHeight,
alpha: offAlpha,
visible: false
});
}, false);
deleteVoxelButton = toolBar.addTool({
imageURL: toolIconUrl + "voxel-delete.svg",
subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
width: toolWidth,
height: toolHeight,
alpha: offAlpha,
visible: false
});
}, false);
addSphereButton = toolBar.addTool({
imageURL: toolIconUrl + "sphere-add.svg",
subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
width: toolWidth,
height: toolHeight,
alpha: offAlpha,
visible: false
});
}, false);
deleteSphereButton = toolBar.addTool({
imageURL: toolIconUrl + "sphere-delete.svg",
subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
width: toolWidth,
height: toolHeight,
alpha: offAlpha,
visible: false
});
}, false);
addTerrainButton = toolBar.addTool({
imageURL: toolIconUrl + "voxel-terrain.svg",
subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
width: toolWidth,
height: toolHeight,
alpha: onAlpha,
visible: false
});
}, false);
that.setActive(false);
}
@ -193,7 +188,6 @@ var toolBar = (function () {
that.cleanup = function () {
toolBar.cleanup();
// Overlays.deleteOverlay(activeButton);
};
@ -237,7 +231,6 @@ function grabLowestJointY() {
}
function addTerrainBlock() {
var baseLocation = getTerrainAlignedLocation(Vec3.sum(MyAvatar.position, {x:8, y:8, z:8}));
if (baseLocation.y > MyAvatar.position.y) {
@ -253,10 +246,26 @@ function addTerrainBlock() {
baseLocation = getTerrainAlignedLocation(facingPosition);
alreadyThere = lookupTerrainForLocation(baseLocation);
if (alreadyThere) {
return;
return null;
}
}
var polyVoxID = addTerrainBlockNearLocation(baseLocation);
if (polyVoxID) {
var AvatarPositionInVoxelCoords = Entities.worldCoordsToVoxelCoords(polyVoxID, MyAvatar.position);
// TODO -- how to find the avatar's feet?
var topY = Math.round(AvatarPositionInVoxelCoords.y) - 4;
Entities.setVoxelsInCuboid(polyVoxID, {x:0, y:0, z:0}, {x:16, y:topY, z:16}, 255);
}
}
function addTerrainBlockNearLocation(baseLocation) {
var alreadyThere = lookupTerrainForLocation(baseLocation);
if (alreadyThere) {
return null;
}
var polyVoxID = Entities.addEntity({
type: "PolyVox",
name: "terrain",
@ -269,12 +278,6 @@ function addTerrainBlock() {
zTextureURL: "http://headache.hungry.com/~seth/hifi/dirt.jpeg"
});
var AvatarPositionInVoxelCoords = Entities.worldCoordsToVoxelCoords(polyVoxID, MyAvatar.position);
// TODO -- how to find the avatar's feet?
var topY = Math.round(AvatarPositionInVoxelCoords.y) - 4;
Entities.setVoxelsInCuboid(polyVoxID, {x:0, y:0, z:0}, {x:16, y:topY, z:16}, 255);
//////////
// stitch together the terrain with x/y/z NeighorID properties
//////////
@ -330,7 +333,7 @@ function addTerrainBlock() {
properties.zPNeighborID = lookupTerrainForLocation(Vec3.sum(baseLocation, {x:0, y:0, z:16}));
Entities.editEntity(polyVoxID, properties);
return true;
return polyVoxID;
}
@ -456,9 +459,6 @@ function keyReleaseEvent(event) {
function cleanup() {
for (var i = 0; i < overlays.length; i++) {
Overlays.deleteOverlay(overlays[i]);
}
toolBar.cleanup();
}

View file

@ -169,6 +169,28 @@ Item {
text: "Downloads: " + root.downloads + "/" + root.downloadLimit +
", Pending: " + root.downloadsPending;
}
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
visible: root.expanded && root.downloadUrls.length > 0;
text: "Download URLs:"
}
ListView {
width: geoCol.width
height: root.downloadUrls.length * 15
visible: root.expanded && root.downloadUrls.length > 0;
model: root.downloadUrls
delegate: Text {
color: root.fontColor;
font.pixelSize: root.fontSize
visible: root.expanded;
text: modelData.length > 30
? modelData.substring(0, 5) + "..." + modelData.substring(modelData.length - 22)
: modelData
}
}
}
}
Rectangle {

View file

@ -272,11 +272,13 @@ public:
void run() override {
while (!_quit) {
QThread::sleep(HEARTBEAT_UPDATE_INTERVAL_SECS);
#ifdef NDEBUG
auto now = usecTimestampNow();
auto lastHeartbeatAge = now - _heartbeat;
if (lastHeartbeatAge > MAX_HEARTBEAT_AGE_USECS) {
deadlockDetectionCrash();
}
#endif
}
}
@ -1418,6 +1420,8 @@ void Application::paintGL() {
// FIXME not needed anymore?
_offscreenContext->makeCurrent();
displayPlugin->updateHeadPose(_frameCount);
// update the avatar with a fresh HMD pose
getMyAvatar()->updateFromHMDSensorMatrix(getHMDSensorPose());
@ -1598,12 +1602,7 @@ void Application::paintGL() {
auto baseProjection = renderArgs._viewFrustum->getProjection();
auto hmdInterface = DependencyManager::get<HMDScriptingInterface>();
float IPDScale = hmdInterface->getIPDScale();
// Tell the plugin what pose we're using to render. In this case we're just using the
// unmodified head pose because the only plugin that cares (the Oculus plugin) uses it
// for rotational timewarp. If we move to support positonal timewarp, we need to
// ensure this contains the full pose composed with the eye offsets.
mat4 headPose = displayPlugin->getHeadPose(_frameCount);
mat4 headPose = displayPlugin->getHeadPose();
// FIXME we probably don't need to set the projection matrix every frame,
// only when the display plugin changes (or in non-HMD modes when the user
@ -1620,6 +1619,10 @@ void Application::paintGL() {
mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * IPDScale);
eyeOffsets[eye] = eyeOffsetTransform;
// Tell the plugin what pose we're using to render. In this case we're just using the
// unmodified head pose because the only plugin that cares (the Oculus plugin) uses it
// for rotational timewarp. If we move to support positonal timewarp, we need to
// ensure this contains the full pose composed with the eye offsets.
displayPlugin->setEyeRenderPose(_frameCount, eye, headPose * glm::inverse(eyeOffsetTransform));
eyeProjections[eye] = displayPlugin->getEyeProjection(eye, baseProjection);
@ -2975,7 +2978,7 @@ void Application::updateMyAvatarLookAtPosition() {
lookAtPosition.x = -lookAtPosition.x;
}
if (isHMD) {
glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose(_frameCount);
glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose();
glm::quat hmdRotation = glm::quat_cast(headPose);
lookAtSpot = _myCamera.getPosition() + myAvatar->getOrientation() * (hmdRotation * lookAtPosition);
} else {
@ -4927,7 +4930,7 @@ mat4 Application::getEyeOffset(int eye) const {
mat4 Application::getHMDSensorPose() const {
if (isHMDMode()) {
return getActiveDisplayPlugin()->getHeadPose(_frameCount);
return getActiveDisplayPlugin()->getHeadPose();
}
return mat4();
}

View file

@ -30,7 +30,6 @@ void AvatarUpdate::synchronousProcess() {
// Keep our own updated value, so that our asynchronous code can consult it.
_isHMDMode = qApp->isHMDMode();
auto frameCount = qApp->getFrameCount();
QSharedPointer<AvatarManager> manager = DependencyManager::get<AvatarManager>();
MyAvatar* myAvatar = manager->getMyAvatar();
@ -38,7 +37,7 @@ void AvatarUpdate::synchronousProcess() {
// transform the head pose from the displayPlugin into avatar coordinates.
glm::mat4 invAvatarMat = glm::inverse(createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition()));
_headPose = invAvatarMat * (myAvatar->getSensorToWorldMatrix() * qApp->getActiveDisplayPlugin()->getHeadPose(frameCount));
_headPose = invAvatarMat * (myAvatar->getSensorToWorldMatrix() * qApp->getActiveDisplayPlugin()->getHeadPose());
if (!isThreaded()) {
process();
@ -55,6 +54,7 @@ bool AvatarUpdate::process() {
deltaMicroseconds = 10000; // 10 ms
}
float deltaSeconds = (float) deltaMicroseconds / (float) USECS_PER_SECOND;
assert(deltaSeconds > 0.0f);
_lastAvatarUpdate = start;
qApp->setAvatarSimrateSample(1.0f / deltaSeconds);

View file

@ -1258,7 +1258,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl
if (qApp->isHMDMode()) {
glm::vec3 cameraPosition = qApp->getCamera()->getPosition();
glm::mat4 headPose = qApp->getActiveDisplayPlugin()->getHeadPose(qApp->getFrameCount());
glm::mat4 headPose = qApp->getActiveDisplayPlugin()->getHeadPose();
glm::mat4 leftEyePose = qApp->getActiveDisplayPlugin()->getEyeToHeadTransform(Eye::Left);
leftEyePose = leftEyePose * headPose;
glm::vec3 leftEyePosition = extractTranslation(leftEyePose);

View file

@ -156,7 +156,7 @@ void Stats::updateStats(bool force) {
}
}
});
// update the entities ping with the average for all connected entity servers
STAT_UPDATE(entitiesPing, octreeServerCount ? totalPingOctree / octreeServerCount : -1);
@ -192,9 +192,29 @@ void Stats::updateStats(bool force) {
STAT_UPDATE(audioMixerPps, -1);
}
STAT_UPDATE(downloads, ResourceCache::getLoadingRequests().size());
QList<Resource*> loadingRequests = ResourceCache::getLoadingRequests();
STAT_UPDATE(downloads, loadingRequests.size());
STAT_UPDATE(downloadLimit, ResourceCache::getRequestLimit())
STAT_UPDATE(downloadsPending, ResourceCache::getPendingRequestCount());
// See if the active download urls have changed
bool shouldUpdateUrls = _downloads != _downloadUrls.size();
if (!shouldUpdateUrls) {
for (int i = 0; i < _downloads; i++) {
if (loadingRequests[i]->getURL().toString() != _downloadUrls[i]) {
shouldUpdateUrls = true;
break;
}
}
}
// If the urls have changed, update the list
if (shouldUpdateUrls) {
_downloadUrls.clear();
foreach (Resource* resource, loadingRequests) {
_downloadUrls << resource->getURL().toString();
}
emit downloadUrlsChanged();
}
// TODO fix to match original behavior
//stringstream downloads;
//downloads << "Downloads: ";
@ -306,7 +326,7 @@ void Stats::updateStats(bool force) {
// we will also include room for 1 line per timing record and a header of 4 lines
// Timing details...
// First iterate all the records, and for the ones that should be included, insert them into
// First iterate all the records, and for the ones that should be included, insert them into
// a new Map sorted by average time...
bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen);
QMap<float, QString> sortedRecords;
@ -366,7 +386,7 @@ void Stats::setRenderDetails(const RenderDetails& details) {
/*
// display expanded or contracted stats
void Stats::display(
int voxelPacketsToProcess)
int voxelPacketsToProcess)
{
// iterate all the current voxel stats, and list their sending modes, and total voxel counts

View file

@ -19,7 +19,7 @@
public: \
type name() { return _##name; }; \
private: \
type _##name{ initialValue };
type _##name{ initialValue };
class Stats : public QQuickItem {
@ -58,6 +58,7 @@ class Stats : public QQuickItem {
STATS_PROPERTY(int, downloads, 0)
STATS_PROPERTY(int, downloadLimit, 0)
STATS_PROPERTY(int, downloadsPending, 0)
Q_PROPERTY(QStringList downloadUrls READ downloadUrls NOTIFY downloadUrlsChanged)
STATS_PROPERTY(int, triangles, 0)
STATS_PROPERTY(int, quads, 0)
STATS_PROPERTY(int, materialSwitches, 0)
@ -105,6 +106,8 @@ public:
}
}
QStringList downloadUrls () { return _downloadUrls; }
public slots:
void forceUpdateStats() { updateStats(true); }
@ -138,6 +141,7 @@ signals:
void downloadsChanged();
void downloadLimitChanged();
void downloadsPendingChanged();
void downloadUrlsChanged();
void trianglesChanged();
void quadsChanged();
void materialSwitchesChanged();
@ -167,6 +171,7 @@ private:
bool _timingExpanded{ false };
QString _monospaceFont;
const AudioIOStats* _audioStats;
QStringList _downloadUrls = QStringList();
};
#endif // hifi_Stats_h

View file

@ -481,7 +481,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
// smooth transitions by relaxing _hipsOffset toward the new value
const float HIPS_OFFSET_SLAVE_TIMESCALE = 0.15f;
float tau = dt > HIPS_OFFSET_SLAVE_TIMESCALE ? 1.0f : dt / HIPS_OFFSET_SLAVE_TIMESCALE;
float tau = dt < HIPS_OFFSET_SLAVE_TIMESCALE ? dt / HIPS_OFFSET_SLAVE_TIMESCALE : 1.0f;
_hipsOffset += (newHipsOffset - _hipsOffset) * tau;
}
}

View file

@ -384,11 +384,20 @@ void SwingTwistConstraint::dynamicallyAdjustLimits(const glm::quat& rotation) {
swingTwistDecomposition(postRotation, Vectors::UNIT_Y, swingRotation, twistRotation);
// adjust swing limits
glm::vec3 swungY = swingRotation * Vectors::UNIT_Y;
glm::vec3 swingAxis = glm::cross(Vectors::UNIT_Y, swungY);
float theta = atan2f(-swingAxis.z, swingAxis.x);
_swingLimitFunction.dynamicallyAdjustMinDots(theta, swungY.y);
{ // adjust swing limits
glm::vec3 swungY = swingRotation * Vectors::UNIT_Y;
glm::vec3 swingAxis = glm::cross(Vectors::UNIT_Y, swungY);
float theta = atan2f(-swingAxis.z, swingAxis.x);
if (isnan(theta)) {
// atan2f() will only return NaN if either of its arguments is NaN, which can only
// happen if we've been given a bad rotation. Since a NaN value here could potentially
// cause a crash (we use the value of theta to compute indices into a std::vector)
// we specifically check for this case.
theta = 0.0f;
swungY.y = 1.0f;
}
_swingLimitFunction.dynamicallyAdjustMinDots(theta, swungY.y);
}
// restore twist limits
if (_twistAdjusted) {

View file

@ -342,7 +342,7 @@ void CompositorHelper::computeHmdPickRay(const glm::vec2& cursorPos, glm::vec3&
}
glm::mat4 CompositorHelper::getUiTransform() const {
return _currentCamera * glm::inverse(_currentDisplayPlugin->getHeadPose(_currentFrame));
return _currentCamera * glm::inverse(_currentDisplayPlugin->getHeadPose());
}
//Finds the collision point of a world space ray

View file

@ -160,4 +160,8 @@ void HmdDisplayPlugin::updateFrameData() {
Parent::updateFrameData();
Lock lock(_mutex);
_currentRenderEyePoses = _renderEyePoses[_currentRenderFrameIndex];
}
}
glm::mat4 HmdDisplayPlugin::getHeadPose() const {
return _headPoseCache.get();
}

View file

@ -7,6 +7,8 @@
//
#pragma once
#include <ThreadSafeValueCache.h>
#include <QtGlobal>
#include "../OpenGLDisplayPlugin.h"
@ -24,7 +26,7 @@ public:
void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) override final;
bool isDisplayVisible() const override { return isHmdMounted(); }
virtual glm::mat4 getHeadPose() const override;
protected:
virtual void hmdPresent() = 0;
@ -46,6 +48,7 @@ protected:
using EyePoses = std::array<glm::mat4, 2>;
QMap<uint32_t, EyePoses> _renderEyePoses;
EyePoses _currentRenderEyePoses;
ThreadSafeValueCache<glm::mat4> _headPoseCache { glm::mat4() };
private:
bool _enablePreview { false };

View file

@ -182,9 +182,10 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(const glm::vec3&
// if this entity is a zone, use this time to determine the bestZone
if (entity->getType() == EntityTypes::Zone) {
if (!entity->getVisible()) {
#ifdef WANT_DEBUG
qCDebug(entitiesrenderer) << "not visible";
}
else {
#endif
} else {
float entityVolumeEstimate = entity->getVolumeEstimate();
if (entityVolumeEstimate < _bestZoneVolume) {
_bestZoneVolume = entityVolumeEstimate;

View file

@ -179,7 +179,7 @@ void OBJFace::addFrom(const OBJFace* face, int index) { // add using data from f
}
static bool replyOK(QNetworkReply* netReply, QUrl url) { // This will be reworked when we make things asynchronous
return (netReply->isFinished() &&
return (netReply && netReply->isFinished() &&
(url.toString().startsWith("file", Qt::CaseInsensitive) ? // file urls don't have http status codes
netReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString().isEmpty() :
(netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200)));
@ -191,11 +191,10 @@ bool OBJReader::isValidTexture(const QByteArray &filename) {
}
QUrl candidateUrl = _url.resolved(QUrl(filename));
QNetworkReply *netReply = request(candidateUrl, true);
if (!netReply) {
return false;
}
bool isValid = replyOK(netReply, candidateUrl);
netReply->deleteLater();
if (netReply) {
netReply->deleteLater();
}
return isValid;
}
@ -264,20 +263,19 @@ QNetworkReply* OBJReader::request(QUrl& url, bool isTest) {
if (!qApp) {
return nullptr;
}
bool aboutToQuit{ false };
auto connection = QObject::connect(qApp, &QCoreApplication::aboutToQuit, [&] {
aboutToQuit = true;
});
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest netRequest(url);
QNetworkReply* netReply = isTest ? networkAccessManager.head(netRequest) : networkAccessManager.get(netRequest);
if (!qApp) {
return netReply;
if (!qApp || aboutToQuit) {
return nullptr;
}
QEventLoop loop; // Create an event loop that will quit when we get the finished signal
QObject::connect(netReply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec(); // Nothing is going to happen on this whole run thread until we get this
bool aboutToQuit { false };
auto connection = QObject::connect(qApp, &QCoreApplication::aboutToQuit, [&] {
aboutToQuit = true;
});
static const int WAIT_TIMEOUT_MS = 500;
while (qApp && !aboutToQuit && !netReply->isReadable()) {
netReply->waitForReadyRead(WAIT_TIMEOUT_MS); // so we might as well block this thread waiting for the response, rather than
@ -570,9 +568,11 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping,
parseMaterialLibrary(netReply);
} else {
qCDebug(modelformat) << "OBJ Reader WARNING:" << libraryName << "did not answer. Got"
<< netReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
<< (!netReply ? "aborted" : netReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString());
}
if (netReply) {
netReply->deleteLater();
}
netReply->deleteLater();
}
}

View file

@ -32,17 +32,25 @@ OffscreenGLCanvas::~OffscreenGLCanvas() {
_context->doneCurrent();
}
void OffscreenGLCanvas::create(QOpenGLContext* sharedContext) {
bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) {
if (nullptr != sharedContext) {
sharedContext->doneCurrent();
_context->setShareContext(sharedContext);
}
_context->setFormat(getDefaultOpenGLSurfaceFormat());
_context->create();
if (_context->create()) {
_offscreenSurface->setFormat(_context->format());
_offscreenSurface->create();
return true;
}
_offscreenSurface->setFormat(_context->format());
_offscreenSurface->create();
qWarning() << "GL Version: " << QString((const char*) glGetString(GL_VERSION));
qWarning() << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION));
qWarning() << "GL Vendor: " << QString((const char*) glGetString(GL_VENDOR));
qWarning() << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER));
qWarning() << "Failed to create OffscreenGLCanvas";
return false;
}
bool OffscreenGLCanvas::makeCurrent() {

View file

@ -23,7 +23,7 @@ class OffscreenGLCanvas : public QObject {
public:
OffscreenGLCanvas();
~OffscreenGLCanvas();
void create(QOpenGLContext* sharedContext = nullptr);
bool create(QOpenGLContext* sharedContext = nullptr);
bool makeCurrent();
void doneCurrent();
QOpenGLContext* getContext() {

View file

@ -65,7 +65,11 @@ class OffscreenQmlRenderer : public OffscreenGLCanvas {
public:
OffscreenQmlRenderer(OffscreenQmlSurface* surface, QOpenGLContext* shareContext) : _surface(surface) {
OffscreenGLCanvas::create(shareContext);
if (!OffscreenGLCanvas::create(shareContext)) {
static const char* error = "Failed to create OffscreenGLCanvas";
qWarning() << error;
throw error;
};
_renderControl = new QMyQuickRenderControl();
@ -153,7 +157,7 @@ private:
qWarning("Failed to make context current on render thread");
return;
}
_renderControl->initialize(_context);
_renderControl->initialize(getContext());
setupFbo();
_escrow.setRecycler([this](GLuint texture){
_textures.recycleTexture(texture);

View file

@ -72,11 +72,15 @@ void GeometryReader::run() {
const bool grabLightmaps = true;
const float lightmapLevel = 1.0f;
fbxgeo = readFBX(_data, _mapping, _url.path(), grabLightmaps, lightmapLevel);
if (fbxgeo->meshes.size() == 0 && fbxgeo->joints.size() == 0) {
// empty fbx geometry, indicates error
throw QString("empty geometry, possibly due to an unsupported FBX version");
}
} else if (_url.path().toLower().endsWith(".obj")) {
fbxgeo = OBJReader().readOBJ(_data, _mapping, _url);
} else {
QString errorStr("unsupported format");
emit onError(NetworkGeometry::ModelParseError, errorStr);
throw errorStr;
}
emit onSuccess(fbxgeo);
} else {

View file

@ -56,7 +56,7 @@ gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, con
bool validAlpha = false;
bool alphaAsMask = true;
const uint8 OPAQUE_ALPHA = 255;
const uint8 TRANSLUCENT_ALPHA = 0;
const uint8 TRANSPARENT_ALPHA = 0;
if (image.hasAlphaChannel()) {
std::map<uint8, uint32> alphaHistogram;
@ -70,10 +70,7 @@ gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, con
for (int x = 0; x < image.width(); ++x) {
auto alpha = qAlpha(data[x]);
alphaHistogram[alpha] ++;
if (alpha != OPAQUE_ALPHA) {
validAlpha = true;
break;
}
validAlpha = validAlpha || (alpha != OPAQUE_ALPHA);
}
}
@ -81,10 +78,10 @@ gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, con
if (validAlpha && (alphaHistogram.size() > 1)) {
auto totalNumPixels = image.height() * image.width();
auto numOpaques = alphaHistogram[OPAQUE_ALPHA];
auto numTranslucents = alphaHistogram[TRANSLUCENT_ALPHA];
auto numTransparents = totalNumPixels - numOpaques - numTranslucents;
auto numTransparents = alphaHistogram[TRANSPARENT_ALPHA];
auto numTranslucents = totalNumPixels - numOpaques - numTransparents;
alphaAsMask = ((numTransparents / (double)totalNumPixels) < 0.05);
alphaAsMask = ((numTranslucents / (double)totalNumPixels) < 0.05);
}
}

View file

@ -159,44 +159,45 @@ void ResourceCache::clearUnusedResource() {
}
}
bool ResourceCache::attemptRequest(Resource* resource) {
auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>();
// Disable request limiting for ATP
if (resource->getURL().scheme() != URL_SCHEME_ATP) {
if (_requestsActive >= _requestLimit) {
// wait until a slot becomes available
sharedItems->_pendingRequests.append(resource);
return false;
}
++_requestsActive;
}
sharedItems->_loadingRequests.append(resource);
resource->makeRequest();
return true;
void ResourceCacheSharedItems::appendActiveRequest(Resource* resource) {
Lock lock(_mutex);
_loadingRequests.append(resource);
}
void ResourceCache::requestCompleted(Resource* resource) {
auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>();
sharedItems->_loadingRequests.removeOne(resource);
if (resource->getURL().scheme() != URL_SCHEME_ATP) {
--_requestsActive;
}
attemptHighestPriorityRequest();
void ResourceCacheSharedItems::appendPendingRequest(Resource* resource) {
Lock lock(_mutex);
_pendingRequests.append(resource);
}
bool ResourceCache::attemptHighestPriorityRequest() {
auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>();
QList<QPointer<Resource>> ResourceCacheSharedItems::getPendingRequests() const {
Lock lock(_mutex);
return _pendingRequests;
}
uint32_t ResourceCacheSharedItems::getPendingRequestsCount() const {
Lock lock(_mutex);
return _pendingRequests.size();
}
QList<Resource*> ResourceCacheSharedItems::getLoadingRequests() const {
Lock lock(_mutex);
return _loadingRequests;
}
void ResourceCacheSharedItems::removeRequest(Resource* resource) {
Lock lock(_mutex);
_loadingRequests.removeOne(resource);
}
Resource* ResourceCacheSharedItems::getHighestPendingRequest() {
Lock lock(_mutex);
// look for the highest priority pending request
int highestIndex = -1;
float highestPriority = -FLT_MAX;
for (int i = 0; i < sharedItems->_pendingRequests.size(); ) {
Resource* resource = sharedItems->_pendingRequests.at(i).data();
for (int i = 0; i < _pendingRequests.size();) {
Resource* resource = _pendingRequests.at(i).data();
if (!resource) {
sharedItems->_pendingRequests.removeAt(i);
_pendingRequests.removeAt(i);
continue;
}
float priority = resource->getLoadPriority();
@ -206,7 +207,45 @@ bool ResourceCache::attemptHighestPriorityRequest() {
}
i++;
}
return (highestIndex >= 0) && attemptRequest(sharedItems->_pendingRequests.takeAt(highestIndex));
if (highestIndex >= 0) {
return _pendingRequests.takeAt(highestIndex);
}
return nullptr;
}
bool ResourceCache::attemptRequest(Resource* resource) {
auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>();
// Disable request limiting for ATP
if (resource->getURL().scheme() != URL_SCHEME_ATP) {
if (_requestsActive >= _requestLimit) {
// wait until a slot becomes available
sharedItems->appendPendingRequest(resource);
return false;
}
++_requestsActive;
}
sharedItems->appendActiveRequest(resource);
resource->makeRequest();
return true;
}
void ResourceCache::requestCompleted(Resource* resource) {
auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>();
sharedItems->removeRequest(resource);
if (resource->getURL().scheme() != URL_SCHEME_ATP) {
--_requestsActive;
}
attemptHighestPriorityRequest();
}
bool ResourceCache::attemptHighestPriorityRequest() {
auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>();
auto resource = sharedItems->getHighestPendingRequest();
return (resource && attemptRequest(resource));
}
const int DEFAULT_REQUEST_LIMIT = 10;
@ -228,9 +267,10 @@ Resource::Resource(const QUrl& url, bool delayLoad) :
Resource::~Resource() {
if (_request) {
ResourceCache::requestCompleted(this);
_request->disconnect(this);
_request->deleteLater();
_request = nullptr;
ResourceCache::requestCompleted(this);
}
}
@ -375,7 +415,14 @@ void Resource::handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTota
}
void Resource::handleReplyFinished() {
Q_ASSERT(_request);
Q_ASSERT_X(_request, "Resource::handleReplyFinished", "Request should not be null while in handleReplyFinished");
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.
qWarning(networking) << "Received signal Resource::handleReplyFinished from ResourceRequest that is not the current"
<< " request: " << sender() << ", " << _request;
return;
}
ResourceCache::requestCompleted(this);

View file

@ -12,6 +12,7 @@
#ifndef hifi_ResourceCache_h
#define hifi_ResourceCache_h
#include <mutex>
#include <QtCore/QHash>
#include <QtCore/QList>
#include <QtCore/QObject>
@ -53,12 +54,25 @@ static const qint64 MAX_UNUSED_MAX_SIZE = 10 * BYTES_PER_GIGABYTES;
// object instead
class ResourceCacheSharedItems : public Dependency {
SINGLETON_DEPENDENCY
using Mutex = std::mutex;
using Lock = std::unique_lock<Mutex>;
public:
QList<QPointer<Resource>> _pendingRequests;
QList<Resource*> _loadingRequests;
void appendPendingRequest(Resource* newRequest);
void appendActiveRequest(Resource* newRequest);
void removeRequest(Resource* doneRequest);
QList<QPointer<Resource>> getPendingRequests() const;
uint32_t getPendingRequestsCount() const;
QList<Resource*> getLoadingRequests() const;
Resource* getHighestPendingRequest();
private:
ResourceCacheSharedItems() { }
virtual ~ResourceCacheSharedItems() { }
mutable Mutex _mutex;
QList<QPointer<Resource>> _pendingRequests;
QList<Resource*> _loadingRequests;
};
@ -75,11 +89,11 @@ public:
void setUnusedResourceCacheSize(qint64 unusedResourcesMaxSize);
qint64 getUnusedResourceCacheSize() const { return _unusedResourcesMaxSize; }
static const QList<Resource*>& getLoadingRequests()
{ return DependencyManager::get<ResourceCacheSharedItems>()->_loadingRequests; }
static const QList<Resource*> getLoadingRequests()
{ return DependencyManager::get<ResourceCacheSharedItems>()->getLoadingRequests(); }
static int getPendingRequestCount()
{ return DependencyManager::get<ResourceCacheSharedItems>()->_pendingRequests.size(); }
{ return DependencyManager::get<ResourceCacheSharedItems>()->getPendingRequestsCount(); }
ResourceCache(QObject* parent = NULL);
virtual ~ResourceCache();

View file

@ -121,8 +121,12 @@ public:
static const glm::mat4 transform; return transform;
}
virtual glm::mat4 getHeadPose(uint32_t frameIndex) const {
static const glm::mat4 pose; return pose;
// will query the underlying hmd api to compute the most recent head pose
virtual void updateHeadPose(uint32_t frameIndex) {}
// returns a copy of the most recent head pose, computed via updateHeadPose
virtual glm::mat4 getHeadPose() const {
return glm::mat4();
}
// Needed for timewarp style features

View file

@ -571,6 +571,7 @@ void Model::removeFromScene(std::shared_ptr<render::Scene> scene, render::Pendin
}
_renderItems.clear();
_renderItemsSet.clear();
_meshGroupsKnown = false;
_readyWhenAdded = false;
}

View file

@ -15,14 +15,11 @@ void OculusBaseDisplayPlugin::resetSensors() {
ovr_RecenterPose(_session);
}
glm::mat4 OculusBaseDisplayPlugin::getHeadPose(uint32_t frameIndex) const {
static uint32_t lastFrameSeen = 0;
void OculusBaseDisplayPlugin::updateHeadPose(uint32_t frameIndex) {
auto displayTime = ovr_GetPredictedDisplayTime(_session, frameIndex);
auto trackingState = ovr_GetTrackingState(_session, displayTime, frameIndex > lastFrameSeen);
if (frameIndex > lastFrameSeen) {
lastFrameSeen = frameIndex;
}
return toGlm(trackingState.HeadPose.ThePose);
auto trackingState = ovr_GetTrackingState(_session, displayTime, true);
mat4 headPose = toGlm(trackingState.HeadPose.ThePose);
_headPoseCache.set(headPose);
}
bool OculusBaseDisplayPlugin::isSupported() const {

View file

@ -20,7 +20,7 @@ public:
// Stereo specific methods
virtual void resetSensors() override final;
virtual glm::mat4 getHeadPose(uint32_t frameIndex) const override;
virtual void updateHeadPose(uint32_t frameIndex) override;
protected:
void customizeContext() override;

View file

@ -35,14 +35,10 @@ void OculusLegacyDisplayPlugin::resetSensors() {
ovrHmd_RecenterPose(_hmd);
}
glm::mat4 OculusLegacyDisplayPlugin::getHeadPose(uint32_t frameIndex) const {
static uint32_t lastFrameSeen = 0;
if (frameIndex > lastFrameSeen) {
Lock lock(_mutex);
_trackingState = ovrHmd_GetTrackingState(_hmd, ovr_GetTimeInSeconds());
lastFrameSeen = frameIndex;
}
return toGlm(_trackingState.HeadPose.ThePose);
void OculusLegacyDisplayPlugin::updateHeadPose(uint32_t frameIndex) {
Lock lock(_mutex);
_trackingState = ovrHmd_GetTrackingState(_hmd, ovr_GetTimeInSeconds());
_headPoseCache.set(toGlm(_trackingState.HeadPose.ThePose));
}
bool OculusLegacyDisplayPlugin::isSupported() const {

View file

@ -26,7 +26,7 @@ public:
// Stereo specific methods
virtual void resetSensors() override;
virtual glm::mat4 getHeadPose(uint32_t frameIndex) const override;
virtual void updateHeadPose(uint32_t frameIndex) override;
virtual float getTargetFrameRate() override;

View file

@ -112,7 +112,7 @@ void OpenVrDisplayPlugin::resetSensors() {
_sensorResetMat = glm::inverse(cancelOutRollAndPitch(m));
}
glm::mat4 OpenVrDisplayPlugin::getHeadPose(uint32_t frameIndex) const {
void OpenVrDisplayPlugin::updateHeadPose(uint32_t frameIndex) {
float displayFrequency = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_DisplayFrequency_Float);
float frameDuration = 1.f / displayFrequency;
@ -139,14 +139,15 @@ glm::mat4 OpenVrDisplayPlugin::getHeadPose(uint32_t frameIndex) const {
_trackedDeviceLinearVelocities[i] = transformVectorFast(_sensorResetMat, toGlm(_trackedDevicePose[i].vVelocity));
_trackedDeviceAngularVelocities[i] = transformVectorFast(_sensorResetMat, toGlm(_trackedDevicePose[i].vAngularVelocity));
}
return _trackedDevicePoseMat4[0];
_headPoseCache.set(_trackedDevicePoseMat4[0]);
}
void OpenVrDisplayPlugin::hmdPresent() {
// Flip y-axis since GL UV coords are backwards.
static vr::VRTextureBounds_t leftBounds{ 0, 0, 0.5f, 1 };
static vr::VRTextureBounds_t rightBounds{ 0.5f, 0, 1, 1 };
vr::Texture_t texture { (void*)oglplus::GetName(_compositeFramebuffer->color), vr::API_OpenGL, vr::ColorSpace_Auto };
_compositor->Submit(vr::Eye_Left, &texture, &leftBounds);

View file

@ -27,7 +27,7 @@ public:
// Stereo specific methods
virtual void resetSensors() override;
virtual glm::mat4 getHeadPose(uint32_t frameIndex) const override;
virtual void updateHeadPose(uint32_t frameIndex) override;
protected:
void internalActivate() override;