mirror of
https://github.com/overte-org/overte.git
synced 2025-07-26 01:36:55 +02:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
c3d9f0c1e7
90 changed files with 1479 additions and 663 deletions
|
@ -8,14 +8,14 @@ like to get paid for your work, make sure you report the bug via a job on
|
||||||
[Worklist.net](https://worklist.net).
|
[Worklist.net](https://worklist.net).
|
||||||
|
|
||||||
We're hiring! We're looking for skilled developers;
|
We're hiring! We're looking for skilled developers;
|
||||||
send your resume to hiring@highfidelity.io
|
send your resume to hiring@highfidelity.com
|
||||||
|
|
||||||
##### Chat with us
|
##### Chat with us
|
||||||
Come chat with us in [our Gitter](http://gitter.im/highfidelity/hifi) if you have any questions or just want to say hi!
|
Come chat with us in [our Gitter](http://gitter.im/highfidelity/hifi) if you have any questions or just want to say hi!
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
=========
|
=========
|
||||||
Documentation is available at [docs.highfidelity.io](http://docs.highfidelity.io), if something is missing, please suggest it via a new job on Worklist (add to the hifi-docs project).
|
Documentation is available at [docs.highfidelity.com](http://docs.highfidelity.com), if something is missing, please suggest it via a new job on Worklist (add to the hifi-docs project).
|
||||||
|
|
||||||
Build Instructions
|
Build Instructions
|
||||||
=========
|
=========
|
||||||
|
|
|
@ -369,14 +369,6 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) {
|
||||||
reverbTime = _zoneReverbSettings[i].reverbTime;
|
reverbTime = _zoneReverbSettings[i].reverbTime;
|
||||||
wetLevel = _zoneReverbSettings[i].wetLevel;
|
wetLevel = _zoneReverbSettings[i].wetLevel;
|
||||||
|
|
||||||
// Modulate wet level with distance to wall
|
|
||||||
float MIN_ATTENUATION_DISTANCE = 2.0f;
|
|
||||||
float MAX_ATTENUATION = -12; // dB
|
|
||||||
glm::vec3 distanceToWalls = (box.getDimensions() / 2.0f) - glm::abs(streamPosition - box.calcCenter());
|
|
||||||
float distanceToClosestWall = glm::min(distanceToWalls.x, distanceToWalls.z);
|
|
||||||
if (distanceToClosestWall < MIN_ATTENUATION_DISTANCE) {
|
|
||||||
wetLevel += MAX_ATTENUATION * (1.0f - distanceToClosestWall / MIN_ATTENUATION_DISTANCE);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
4
cmake/externals/openvr/CMakeLists.txt
vendored
4
cmake/externals/openvr/CMakeLists.txt
vendored
|
@ -7,8 +7,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||||
|
|
||||||
ExternalProject_Add(
|
ExternalProject_Add(
|
||||||
${EXTERNAL_NAME}
|
${EXTERNAL_NAME}
|
||||||
URL https://github.com/ValveSoftware/openvr/archive/v0.9.15.zip
|
URL https://github.com/ValveSoftware/openvr/archive/v0.9.19.zip
|
||||||
URL_MD5 0ff8560b49b6da1150fcc47360e8ceca
|
URL_MD5 843f9dde488584d8af1f3ecf2252b4e0
|
||||||
CONFIGURE_COMMAND ""
|
CONFIGURE_COMMAND ""
|
||||||
BUILD_COMMAND ""
|
BUILD_COMMAND ""
|
||||||
INSTALL_COMMAND ""
|
INSTALL_COMMAND ""
|
||||||
|
|
|
@ -308,7 +308,7 @@
|
||||||
"name": "reverb",
|
"name": "reverb",
|
||||||
"type": "table",
|
"type": "table",
|
||||||
"label": "Reverb Settings",
|
"label": "Reverb Settings",
|
||||||
"help": "In this table you can set reverb levels for audio zones. For a medium-sized (e.g., 100 square meter) meeting room, try a decay time of around 1.5 seconds and a wet level of -10 db. For an airplane hangar or cathedral, try a decay time of 4 seconds and a wet level of -5 db.",
|
"help": "In this table you can set reverb levels for audio zones. For a medium-sized (e.g., 100 square meter) meeting room, try a decay time of around 1.5 seconds and a wet/dry mix of 25%. For an airplane hangar or cathedral, try a decay time of 4 seconds and a wet/dry mix of 50%.",
|
||||||
"numbered": true,
|
"numbered": true,
|
||||||
"columns": [
|
"columns": [
|
||||||
{
|
{
|
||||||
|
@ -325,9 +325,9 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "wet_level",
|
"name": "wet_level",
|
||||||
"label": "Wet Level",
|
"label": "Wet/Dry Mix",
|
||||||
"can_set": true,
|
"can_set": true,
|
||||||
"placeholder": "(in db)"
|
"placeholder": "(in percent)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
301
examples/airship/airship.js
Normal file
301
examples/airship/airship.js
Normal file
|
@ -0,0 +1,301 @@
|
||||||
|
//
|
||||||
|
// airship.js
|
||||||
|
//
|
||||||
|
// Animates a pirate airship that chases people and shoots cannonballs at them
|
||||||
|
//
|
||||||
|
// Created by Philip Rosedale on March 7, 2016
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
var entityID,
|
||||||
|
minimumDelay = 100, // milliseconds
|
||||||
|
distanceScale = 2.0, // distance at which 100% chance of hopping
|
||||||
|
timeScale = 300.0, // crash
|
||||||
|
hopStrength = 0.4, // meters / second
|
||||||
|
spotlight = null,
|
||||||
|
wantDebug = false,
|
||||||
|
timeoutID = undefined,
|
||||||
|
bullet = null,
|
||||||
|
particles = null,
|
||||||
|
nearbyAvatars = 0,
|
||||||
|
nearbyAvatarsInRange = 0,
|
||||||
|
averageAvatarLocation = { x: 0, y: 0, z: 0 },
|
||||||
|
properties,
|
||||||
|
lightTimer = 0,
|
||||||
|
lightTimeoutID = undefined,
|
||||||
|
audioInjector = null;
|
||||||
|
|
||||||
|
var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||||
|
var cannonSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "philip/cannonShot.wav");
|
||||||
|
var explosionSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "philip/explosion.wav");
|
||||||
|
|
||||||
|
var NO_SHOOT_COLOR = { red: 100, green: 100, blue: 100 };
|
||||||
|
|
||||||
|
var MAX_TARGET_RANGE = 200;
|
||||||
|
|
||||||
|
function printDebug(message) {
|
||||||
|
if (wantDebug) {
|
||||||
|
print(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var LIGHT_UPDATE_INTERVAL = 50;
|
||||||
|
var LIGHT_LIFETIME = 700;
|
||||||
|
|
||||||
|
function randomVector(size) {
|
||||||
|
return { x: (Math.random() - 0.5) * size,
|
||||||
|
y: (Math.random() - 0.5) * size,
|
||||||
|
z: (Math.random() - 0.5) * size };
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeLight(parent, position, colorDivisor) {
|
||||||
|
// Create a flickering light somewhere for a while
|
||||||
|
if (spotlight !== null) {
|
||||||
|
// light still exists, do nothing
|
||||||
|
printDebug("light still there");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printDebug("making light");
|
||||||
|
|
||||||
|
var colorIndex = 180 + Math.random() * 50;
|
||||||
|
|
||||||
|
spotlight = Entities.addEntity({
|
||||||
|
type: "Light",
|
||||||
|
name: "Test Light",
|
||||||
|
intensity: 50.0,
|
||||||
|
falloffRadius: 20.0,
|
||||||
|
dimensions: {
|
||||||
|
x: 150,
|
||||||
|
y: 150,
|
||||||
|
z: 150
|
||||||
|
},
|
||||||
|
position: position,
|
||||||
|
parentID: parent,
|
||||||
|
color: {
|
||||||
|
red: colorIndex,
|
||||||
|
green: colorIndex / colorDivisor,
|
||||||
|
blue: 0
|
||||||
|
},
|
||||||
|
lifetime: LIGHT_LIFETIME * 2
|
||||||
|
});
|
||||||
|
|
||||||
|
lightTimer = 0;
|
||||||
|
lightTimeoutID = Script.setTimeout(updateLight, LIGHT_UPDATE_INTERVAL);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
function updateLight() {
|
||||||
|
lightTimer += LIGHT_UPDATE_INTERVAL;
|
||||||
|
if ((spotlight !== null) && (lightTimer > LIGHT_LIFETIME)) {
|
||||||
|
printDebug("deleting light!");
|
||||||
|
Entities.deleteEntity(spotlight);
|
||||||
|
spotlight = null;
|
||||||
|
} else {
|
||||||
|
Entities.editEntity(spotlight, {
|
||||||
|
intensity: 5 + Math.random() * 50,
|
||||||
|
falloffRadius: 5 + Math.random() * 10
|
||||||
|
});
|
||||||
|
lightTimeoutID = Script.setTimeout(updateLight, LIGHT_UPDATE_INTERVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function move() {
|
||||||
|
|
||||||
|
var HOVER_DISTANCE = 30.0;
|
||||||
|
var RUN_TOWARD_STRENGTH = 0.002;
|
||||||
|
var VELOCITY_FOLLOW_RATE = 0.01;
|
||||||
|
|
||||||
|
var range = Vec3.distance(properties.position, averageAvatarLocation);
|
||||||
|
var impulse = { x: 0, y: 0, z: 0 };
|
||||||
|
|
||||||
|
// move in the XZ plane
|
||||||
|
var away = Vec3.subtract(properties.position, averageAvatarLocation);
|
||||||
|
away.y = 0.0;
|
||||||
|
|
||||||
|
if (range > HOVER_DISTANCE) {
|
||||||
|
impulse = Vec3.multiply(-RUN_TOWARD_STRENGTH, Vec3.normalize(away));
|
||||||
|
}
|
||||||
|
|
||||||
|
var rotation = Quat.rotationBetween(Vec3.UNIT_NEG_Z, properties.velocity);
|
||||||
|
Entities.editEntity(entityID, {velocity: Vec3.sum(properties.velocity, impulse), rotation: Quat.mix(properties.rotation, rotation, VELOCITY_FOLLOW_RATE)});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function countAvatars() {
|
||||||
|
var workQueue = AvatarList.getAvatarIdentifiers();
|
||||||
|
var averageLocation = {x: 0, y: 0, z: 0};
|
||||||
|
var summed = 0;
|
||||||
|
var inRange = 0;
|
||||||
|
for (var i = 0; i < workQueue.length; i++) {
|
||||||
|
var avatar = AvatarList.getAvatar(workQueue[i]), avatarPosition = avatar && avatar.position;
|
||||||
|
if (avatarPosition) {
|
||||||
|
averageLocation = Vec3.sum(averageLocation, avatarPosition);
|
||||||
|
summed++;
|
||||||
|
if (Vec3.distance(avatarPosition, properties.position) < MAX_TARGET_RANGE) {
|
||||||
|
inRange++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (summed > 0) {
|
||||||
|
averageLocation = Vec3.multiply(1 / summed, averageLocation);
|
||||||
|
}
|
||||||
|
nearbyAvatars = summed;
|
||||||
|
nearbyAvatarsInRange = inRange;
|
||||||
|
averageAvatarLocation = averageLocation;
|
||||||
|
|
||||||
|
//printDebug(" Avatars: " + summed + "in range: " + nearbyAvatarsInRange);
|
||||||
|
//Vec3.print(" location: ", averageAvatarLocation);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function shoot() {
|
||||||
|
if (bullet !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Vec3.distance(MyAvatar.position, properties.position) > MAX_TARGET_RANGE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var SPEED = 7.5;
|
||||||
|
var GRAVITY = -2.0;
|
||||||
|
var DIAMETER = 0.5;
|
||||||
|
var direction = Vec3.subtract(MyAvatar.position, properties.position);
|
||||||
|
var range = Vec3.distance(MyAvatar.position, properties.position);
|
||||||
|
var timeOfFlight = range / SPEED;
|
||||||
|
|
||||||
|
var fall = 0.5 * -GRAVITY * (timeOfFlight * timeOfFlight);
|
||||||
|
|
||||||
|
var velocity = Vec3.multiply(SPEED, Vec3.normalize(direction));
|
||||||
|
velocity.y += 0.5 * fall / timeOfFlight * 2.0;
|
||||||
|
|
||||||
|
var DISTANCE_TO_DECK = 3;
|
||||||
|
var bulletStart = properties.position;
|
||||||
|
bulletStart.y -= DISTANCE_TO_DECK;
|
||||||
|
|
||||||
|
makeLight(entityID, Vec3.sum(properties.position, randomVector(10.0)), 2);
|
||||||
|
|
||||||
|
bullet = Entities.addEntity({
|
||||||
|
type: "Sphere",
|
||||||
|
name: "cannonball",
|
||||||
|
position: properties.position,
|
||||||
|
dimensions: { x: DIAMETER, y: DIAMETER, z: DIAMETER },
|
||||||
|
color: { red: 10, green: 10, blue: 10 },
|
||||||
|
velocity: velocity,
|
||||||
|
damping: 0.01,
|
||||||
|
dynamic: true,
|
||||||
|
ignoreForCollisions: true,
|
||||||
|
gravity: { x: 0, y: GRAVITY, z: 0 },
|
||||||
|
lifetime: timeOfFlight * 2
|
||||||
|
});
|
||||||
|
|
||||||
|
Audio.playSound(cannonSound, {
|
||||||
|
position: Vec3.sum(MyAvatar.position, velocity),
|
||||||
|
volume: 1.0
|
||||||
|
});
|
||||||
|
makeParticles(properties.position, bullet, timeOfFlight * 2);
|
||||||
|
Script.setTimeout(explode, timeOfFlight * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function explode() {
|
||||||
|
var properties = Entities.getEntityProperties(bullet);
|
||||||
|
var direction = Vec3.normalize(Vec3.subtract(MyAvatar.position, properties.position));
|
||||||
|
makeLight(null, properties.position, 10);
|
||||||
|
Audio.playSound(explosionSound, { position: Vec3.sum(MyAvatar.position, direction), volume: 1.0 });
|
||||||
|
bullet = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function maybe() { // every user checks their distance and tries to claim if close enough.
|
||||||
|
var PROBABILITY_OF_SHOOT = 0.015;
|
||||||
|
properties = Entities.getEntityProperties(entityID);
|
||||||
|
countAvatars();
|
||||||
|
|
||||||
|
if (nearbyAvatars && (Math.random() < 1 / nearbyAvatars)) {
|
||||||
|
move();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nearbyAvatarsInRange && (Math.random() < PROBABILITY_OF_SHOOT / nearbyAvatarsInRange)) {
|
||||||
|
shoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
var TIME_TO_NEXT_CHECK = 33;
|
||||||
|
timeoutID = Script.setTimeout(maybe, TIME_TO_NEXT_CHECK);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.preload = function (givenEntityID) {
|
||||||
|
printDebug("preload airship v1...");
|
||||||
|
|
||||||
|
entityID = givenEntityID;
|
||||||
|
properties = Entities.getEntityProperties(entityID);
|
||||||
|
timeoutID = Script.setTimeout(maybe, minimumDelay);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.unload = function () {
|
||||||
|
printDebug("unload airship...");
|
||||||
|
if (timeoutID !== undefined) {
|
||||||
|
Script.clearTimeout(timeoutID);
|
||||||
|
}
|
||||||
|
if (lightTimeoutID !== undefined) {
|
||||||
|
Script.clearTimeout(lightTimeoutID);
|
||||||
|
}
|
||||||
|
if (spotlight !== null) {
|
||||||
|
Entities.deleteEntity(spotlight);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function makeParticles(position, parent, lifespan) {
|
||||||
|
particles = Entities.addEntity({
|
||||||
|
type: 'ParticleEffect',
|
||||||
|
position: position,
|
||||||
|
parentID: parent,
|
||||||
|
color: {
|
||||||
|
red: 70,
|
||||||
|
green: 70,
|
||||||
|
blue: 70
|
||||||
|
},
|
||||||
|
isEmitting: 1,
|
||||||
|
maxParticles: 1000,
|
||||||
|
lifetime: lifespan,
|
||||||
|
lifespan: lifespan / 3,
|
||||||
|
emitRate: 80,
|
||||||
|
emitSpeed: 0,
|
||||||
|
speedSpread: 1.0,
|
||||||
|
emitRadiusStart: 1,
|
||||||
|
polarStart: -Math.PI/8,
|
||||||
|
polarFinish: Math.PI/8,
|
||||||
|
azimuthStart: -Math.PI/4,
|
||||||
|
azimuthFinish: Math.PI/4,
|
||||||
|
emitAcceleration: { x: 0, y: 0, z: 0 },
|
||||||
|
particleRadius: 0.25,
|
||||||
|
radiusSpread: 0.1,
|
||||||
|
radiusStart: 0.3,
|
||||||
|
radiusFinish: 0.15,
|
||||||
|
colorSpread: {
|
||||||
|
red: 100,
|
||||||
|
green: 100,
|
||||||
|
blue: 0
|
||||||
|
},
|
||||||
|
colorStart: {
|
||||||
|
red: 125,
|
||||||
|
green: 125,
|
||||||
|
blue: 125
|
||||||
|
},
|
||||||
|
colorFinish: {
|
||||||
|
red: 10,
|
||||||
|
green: 10,
|
||||||
|
blue: 10
|
||||||
|
},
|
||||||
|
alpha: 0.5,
|
||||||
|
alphaSpread: 0,
|
||||||
|
alphaStart: 1,
|
||||||
|
alphaFinish: 0,
|
||||||
|
emitterShouldTrail: true,
|
||||||
|
textures: 'https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
59
examples/airship/makeAirship.js
Normal file
59
examples/airship/makeAirship.js
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
// makeAirship.js
|
||||||
|
//
|
||||||
|
// // Created by Philip Rosedale on March 7, 2016
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
var SIZE = 0.2;
|
||||||
|
var TYPE = "Model"; // Right now this can be "Box" or "Model" or "Sphere"
|
||||||
|
|
||||||
|
var MODEL_URL = "https://s3.amazonaws.com/hifi-public/philip/airship_compact.fbx"
|
||||||
|
|
||||||
|
var MODEL_DIMENSION = { x: 19.257, y: 24.094, z: 40.3122 };
|
||||||
|
var ENTITY_URL = "https://s3.amazonaws.com/hifi-public/scripts/airship/airship.js";
|
||||||
|
|
||||||
|
|
||||||
|
var LIFETIME = 3600 * 48;
|
||||||
|
|
||||||
|
var GRAVITY = { x: 0, y: 0, z: 0 };
|
||||||
|
var DAMPING = 0.05;
|
||||||
|
var ANGULAR_DAMPING = 0.01;
|
||||||
|
|
||||||
|
var collidable = true;
|
||||||
|
var gravity = true;
|
||||||
|
|
||||||
|
var HOW_FAR_IN_FRONT_OF_ME = 30;
|
||||||
|
var HOW_FAR_ABOVE_ME = 15;
|
||||||
|
|
||||||
|
var leaveBehind = true;
|
||||||
|
|
||||||
|
var shipLocation = Vec3.sum(MyAvatar.position, Vec3.multiply(HOW_FAR_IN_FRONT_OF_ME, Quat.getFront(Camera.orientation)));
|
||||||
|
shipLocation.y += HOW_FAR_ABOVE_ME;
|
||||||
|
|
||||||
|
|
||||||
|
var airship = Entities.addEntity({
|
||||||
|
type: TYPE,
|
||||||
|
modelURL: MODEL_URL,
|
||||||
|
name: "airship",
|
||||||
|
position: shipLocation,
|
||||||
|
dimensions: (TYPE == "Model") ? MODEL_DIMENSION : { x: SIZE, y: SIZE, z: SIZE },
|
||||||
|
damping: DAMPING,
|
||||||
|
angularDamping: ANGULAR_DAMPING,
|
||||||
|
gravity: (gravity ? GRAVITY : { x: 0, y: 0, z: 0}),
|
||||||
|
dynamic: collidable,
|
||||||
|
lifetime: LIFETIME,
|
||||||
|
animation: {url: MODEL_URL, running: true, currentFrame: 0, loop: true},
|
||||||
|
script: ENTITY_URL
|
||||||
|
});
|
||||||
|
|
||||||
|
function scriptEnding() {
|
||||||
|
if (!leaveBehind) {
|
||||||
|
Entities.deleteEntity(airship);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(scriptEnding);
|
|
@ -451,6 +451,37 @@
|
||||||
|
|
||||||
var elPreviewCameraButton = document.getElementById("preview-camera-button");
|
var elPreviewCameraButton = document.getElementById("preview-camera-button");
|
||||||
|
|
||||||
|
var urlUpdaters = document.getElementsByClassName("update-url-version");
|
||||||
|
var PARAM_REGEXP = /(?:\?)(\S+)/; // Check if this has any parameters.
|
||||||
|
var TIMESTAMP_REGEXP = /(&?HFTime=\d+)/;
|
||||||
|
|
||||||
|
var refreshEvent = function(event){
|
||||||
|
var urlElement = event.target.parentElement.getElementsByClassName("url")[0];
|
||||||
|
var content = urlElement.value;
|
||||||
|
var date = new Date();
|
||||||
|
var timeStamp = date.getTime();
|
||||||
|
|
||||||
|
if(content.length > 0){
|
||||||
|
if(PARAM_REGEXP.test(content)){
|
||||||
|
// Has params, so lets remove existing definition and append again.
|
||||||
|
content = content.replace(TIMESTAMP_REGEXP,"") + "&";
|
||||||
|
}else{
|
||||||
|
content += "?";
|
||||||
|
}
|
||||||
|
content = content.replace("?&","?");
|
||||||
|
urlElement.value = content + "HFTime=" + timeStamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
var evt = document.createEvent("HTMLEvents");
|
||||||
|
evt.initEvent("change", true, true );
|
||||||
|
urlElement.dispatchEvent(evt);
|
||||||
|
};
|
||||||
|
|
||||||
|
for(var index = 0; index < urlUpdaters.length; index++){
|
||||||
|
var urlUpdater = urlUpdaters[index];
|
||||||
|
urlUpdater.addEventListener("click", refreshEvent);
|
||||||
|
}
|
||||||
|
|
||||||
if (window.EventBridge !== undefined) {
|
if (window.EventBridge !== undefined) {
|
||||||
var properties;
|
var properties;
|
||||||
EventBridge.scriptEventReceived.connect(function(data) {
|
EventBridge.scriptEventReceived.connect(function(data) {
|
||||||
|
@ -1185,6 +1216,7 @@
|
||||||
<div class="label">Ambient URL</div>
|
<div class="label">Ambient URL</div>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
<input type="text" id="property-zone-key-ambient-url" class="url">
|
<input type="text" id="property-zone-key-ambient-url" class="url">
|
||||||
|
<div class="update-url-version"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1262,6 +1294,7 @@
|
||||||
<div class="label">Skybox URL</div>
|
<div class="label">Skybox URL</div>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
<input type="text" id="property-zone-skybox-url" class="url">
|
<input type="text" id="property-zone-skybox-url" class="url">
|
||||||
|
<div class="update-url-version"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1273,6 +1306,7 @@
|
||||||
<div class="label">Source URL</div>
|
<div class="label">Source URL</div>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
<input type="text" id="property-web-source-url" class="url">
|
<input type="text" id="property-web-source-url" class="url">
|
||||||
|
<div class="update-url-version"></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1286,12 +1320,14 @@
|
||||||
<div class="label">Href - Hifi://address</div>
|
<div class="label">Href - Hifi://address</div>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
<input id="property-hyperlink-href" class="url">
|
<input id="property-hyperlink-href" class="url">
|
||||||
|
<div class="update-url-version"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="hyperlink-section property">
|
<div class="hyperlink-section property">
|
||||||
<div class="label">Description</div>
|
<div class="label">Description</div>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
<input id="property-hyperlink-description" class="url">
|
<input id="property-hyperlink-description" class="url">
|
||||||
|
<div class="update-url-version"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1375,16 +1411,19 @@
|
||||||
<div class="label">X-axis Texture URL</div>
|
<div class="label">X-axis Texture URL</div>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
<input type="text" id="property-x-texture-url" class="url">
|
<input type="text" id="property-x-texture-url" class="url">
|
||||||
|
<div class="update-url-version"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="label">Y-axis Texture URL</div>
|
<div class="label">Y-axis Texture URL</div>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
<input type="text" id="property-y-texture-url" class="url">
|
<input type="text" id="property-y-texture-url" class="url">
|
||||||
|
<div class="update-url-version"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="label">Z-axis Texture URL</div>
|
<div class="label">Z-axis Texture URL</div>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
<input type="text" id="property-z-texture-url" class="url">
|
<input type="text" id="property-z-texture-url" class="url">
|
||||||
|
<div class="update-url-version"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1566,6 +1605,7 @@
|
||||||
<div class="label">Collision Sound URL</div>
|
<div class="label">Collision Sound URL</div>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
<input id="property-collision-sound-url" class="url">
|
<input id="property-collision-sound-url" class="url">
|
||||||
|
<div class="update-url-version"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1583,6 +1623,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
<input id="property-script-url" class="url">
|
<input id="property-script-url" class="url">
|
||||||
|
<div class="update-url-version"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1595,6 +1636,7 @@
|
||||||
<div class="label">Model URL</div>
|
<div class="label">Model URL</div>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
<input type="text" id="property-model-url" class="url">
|
<input type="text" id="property-model-url" class="url">
|
||||||
|
<div class="update-url-version"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1613,12 +1655,14 @@
|
||||||
<div class="label">Compound Shape URL</div>
|
<div class="label">Compound Shape URL</div>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
<input type="text" id="property-compound-shape-url" class="url">
|
<input type="text" id="property-compound-shape-url" class="url">
|
||||||
|
<div class="update-url-version"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="model-section property">
|
<div class="model-section property">
|
||||||
<div class="label">Animation URL</div>
|
<div class="label">Animation URL</div>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
<input type="text" id="property-model-animation-url" class="url">
|
<input type="text" id="property-model-animation-url" class="url">
|
||||||
|
<div class="update-url-version"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="model-section property">
|
<div class="model-section property">
|
||||||
|
|
|
@ -10,50 +10,11 @@
|
||||||
|
|
||||||
var EventBridge;
|
var EventBridge;
|
||||||
|
|
||||||
EventBridgeConnectionProxy = function(parent) {
|
openEventBridge = function(callback) {
|
||||||
this.parent = parent;
|
new QWebChannel(qt.webChannelTransport, function(channel) {
|
||||||
this.realSignal = this.parent.realBridge.scriptEventReceived
|
console.log("uid " + EventBridgeUid);
|
||||||
this.webWindowId = this.parent.webWindow.windowId;
|
EventBridge = channel.objects[EventBridgeUid];
|
||||||
}
|
callback(EventBridge);
|
||||||
|
|
||||||
EventBridgeConnectionProxy.prototype.connect = function(callback) {
|
|
||||||
var that = this;
|
|
||||||
this.realSignal.connect(function(id, message) {
|
|
||||||
if (id === that.webWindowId) { callback(message); }
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
EventBridgeProxy = function(webWindow) {
|
|
||||||
this.webWindow = webWindow;
|
|
||||||
this.realBridge = this.webWindow.eventBridge;
|
|
||||||
this.scriptEventReceived = new EventBridgeConnectionProxy(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
EventBridgeProxy.prototype.emitWebEvent = function(data) {
|
|
||||||
this.realBridge.emitWebEvent(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
openEventBridge = function(callback) {
|
|
||||||
EVENT_BRIDGE_URI = "ws://localhost:51016";
|
|
||||||
socket = new WebSocket(this.EVENT_BRIDGE_URI);
|
|
||||||
|
|
||||||
socket.onclose = function() {
|
|
||||||
console.error("web channel closed");
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.onerror = function(error) {
|
|
||||||
console.error("web channel error: " + error);
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.onopen = function() {
|
|
||||||
channel = new QWebChannel(socket, function(channel) {
|
|
||||||
console.log("Document url is " + document.URL);
|
|
||||||
var webWindow = channel.objects[document.URL.toLowerCase()];
|
|
||||||
console.log("WebWindow is " + webWindow)
|
|
||||||
eventBridgeProxy = new EventBridgeProxy(webWindow);
|
|
||||||
EventBridge = eventBridgeProxy;
|
|
||||||
if (callback) { callback(eventBridgeProxy); }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -4,21 +4,17 @@
|
||||||
<script type="text/javascript" src="jquery-2.1.4.min.js"></script>
|
<script type="text/javascript" src="jquery-2.1.4.min.js"></script>
|
||||||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||||
<script type="text/javascript" src="eventBridgeLoader.js"></script>
|
<script type="text/javascript" src="eventBridgeLoader.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var myBridge;
|
|
||||||
|
|
||||||
window.onload = function() {
|
window.onload = function() {
|
||||||
openEventBridge(function(eventBridge) {
|
openEventBridge(function() {
|
||||||
myBridge = eventBridge;
|
EventBridge.scriptEventReceived.connect(function(message) {
|
||||||
myBridge.scriptEventReceived.connect(function(message) {
|
|
||||||
console.log("HTML side received message: " + message);
|
console.log("HTML side received message: " + message);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
testClick = function() {
|
testClick = function() {
|
||||||
myBridge.emitWebEvent("HTML side sending message - button click");
|
EventBridge.emitWebEvent(["Foo", "Bar", { "baz": 1} ]);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -134,8 +134,18 @@ textarea {
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.update-url-version{
|
||||||
|
width:17px;
|
||||||
|
height:17px;
|
||||||
|
float:right;
|
||||||
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAQAAACRZI9xAAABAUlEQVQoz33RvyvEARjH8SdlUX6UbhFJfgwmE3aTxWEiitxqN/gH7IxCKTcymLiJFIOJsihiUKcrCYnuXoZv+F45n2d4lvfz6fPpCSFs6BO1JllvLmXk5F37dC0vJ5NGmlBxoFp7Rn6RDpRRtG5KtynrijhLoBBGQcllKkFWAXsyCbKEFxeGqmJmFZFLkE639vX+12hCSVft0nUR8RbLcRN/aSHGIqL2tVYPconL32qLtaiL88Rl0qtXc+pTDv1OPGMlidtgS8Wp2RR05FEFM98/GlRQ9mTHvB7jVt2rKGPAT9xh2z6qfnToHV1SjZpN23Tl051di1oco9W/pdttaBRfEhFXOZV7vEsAAAAASUVORK5CYII=);
|
||||||
|
padding:0 !important;
|
||||||
|
margin:0 2px 0 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
input.url {
|
input.url {
|
||||||
width: 100%;
|
width:85%;
|
||||||
|
padding-right: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input.coord {
|
input.coord {
|
||||||
|
|
|
@ -198,6 +198,16 @@ function createColorPicker(key) {
|
||||||
settings[key] = colorArray;
|
settings[key] = colorArray;
|
||||||
var controller = gui.addColor(settings, key);
|
var controller = gui.addColor(settings, key);
|
||||||
controller.onChange(function(value) {
|
controller.onChange(function(value) {
|
||||||
|
// Handle hex colors
|
||||||
|
if(_.isString(value) && value[0] === '#') {
|
||||||
|
const BASE_HEX = 16;
|
||||||
|
var colorRegExResult = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(value);
|
||||||
|
value = [
|
||||||
|
parseInt(colorRegExResult[1], BASE_HEX),
|
||||||
|
parseInt(colorRegExResult[2], BASE_HEX),
|
||||||
|
parseInt(colorRegExResult[3], BASE_HEX)
|
||||||
|
];
|
||||||
|
}
|
||||||
var obj = {};
|
var obj = {};
|
||||||
obj[key] = convertColorArrayToObject(value);
|
obj[key] = convertColorArrayToObject(value);
|
||||||
writeVec3ToInterface(obj);
|
writeVec3ToInterface(obj);
|
||||||
|
|
|
@ -8,26 +8,14 @@ webWindow.eventBridge.webEventReceived.connect(function(data) {
|
||||||
print("JS Side event received: " + data);
|
print("JS Side event received: " + data);
|
||||||
});
|
});
|
||||||
|
|
||||||
var titles = ["A", "B", "C"];
|
|
||||||
var titleIndex = 0;
|
|
||||||
|
|
||||||
Script.setInterval(function() {
|
Script.setInterval(function() {
|
||||||
webWindow.eventBridge.emitScriptEvent("JS Event sent");
|
var message = [ Math.random(), Math.random() ];
|
||||||
var size = webWindow.size;
|
print("JS Side sending: " + message);
|
||||||
var position = webWindow.position;
|
webWindow.emitScriptEvent(message);
|
||||||
print("Window url: " + webWindow.url)
|
}, 5 * 1000);
|
||||||
print("Window visible: " + webWindow.visible)
|
|
||||||
print("Window size: " + size.x + "x" + size.y)
|
|
||||||
print("Window pos: " + position.x + "x" + position.y)
|
|
||||||
webWindow.setVisible(!webWindow.visible);
|
|
||||||
webWindow.setTitle(titles[titleIndex]);
|
|
||||||
webWindow.setSize(320 + Math.random() * 100, 240 + Math.random() * 100);
|
|
||||||
titleIndex += 1;
|
|
||||||
titleIndex %= titles.length;
|
|
||||||
}, 2 * 1000);
|
|
||||||
|
|
||||||
Script.setTimeout(function() {
|
Script.scriptEnding.connect(function(){
|
||||||
print("Closing script");
|
|
||||||
webWindow.close();
|
webWindow.close();
|
||||||
Script.stop();
|
webWindow.deleteLater();
|
||||||
}, 15 * 1000)
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 1.2
|
import QtQuick.Controls 1.2
|
||||||
import QtWebEngine 1.1
|
import QtWebEngine 1.1
|
||||||
|
import QtWebChannel 1.0
|
||||||
|
|
||||||
import "windows" as Windows
|
import "windows" as Windows
|
||||||
import "controls" as Controls
|
import "controls" as Controls
|
||||||
|
@ -15,11 +16,22 @@ Windows.Window {
|
||||||
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
|
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
|
||||||
destroyOnCloseButton: false
|
destroyOnCloseButton: false
|
||||||
property alias source: webview.url
|
property alias source: webview.url
|
||||||
|
property alias webChannel: webview.webChannel
|
||||||
|
// A unique identifier to let the HTML JS find the event bridge
|
||||||
|
// object (our C++ wrapper)
|
||||||
|
property string uid;
|
||||||
|
|
||||||
|
// This is for JS/QML communication, which is unused in a WebWindow,
|
||||||
|
// but not having this here results in spurious warnings about a
|
||||||
|
// missing signal
|
||||||
|
signal sendToScript(var message);
|
||||||
|
|
||||||
Controls.WebView {
|
Controls.WebView {
|
||||||
id: webview
|
id: webview
|
||||||
url: "about:blank"
|
url: "about:blank"
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
focus: true
|
focus: true
|
||||||
|
onUrlChanged: webview.runJavaScript("EventBridgeUid = \"" + uid + "\";");
|
||||||
|
Component.onCompleted: webview.runJavaScript("EventBridgeUid = \"" + uid + "\";");
|
||||||
}
|
}
|
||||||
} // dialog
|
} // dialog
|
||||||
|
|
|
@ -37,14 +37,33 @@ Windows.Window {
|
||||||
Repeater {
|
Repeater {
|
||||||
model: 4
|
model: 4
|
||||||
Tab {
|
Tab {
|
||||||
|
// Force loading of the content even if the tab is not visible
|
||||||
|
// (required for letting the C++ code access the webview)
|
||||||
active: true
|
active: true
|
||||||
enabled: false;
|
enabled: false
|
||||||
// we need to store the original url here for future identification
|
|
||||||
property string originalUrl: "";
|
property string originalUrl: "";
|
||||||
onEnabledChanged: toolWindow.updateVisiblity();
|
|
||||||
Controls.WebView {
|
Controls.WebView {
|
||||||
id: webView;
|
id: webView;
|
||||||
|
// we need to store the original url here for future identification
|
||||||
|
// A unique identifier to let the HTML JS find the event bridge
|
||||||
|
// object (our C++ wrapper)
|
||||||
|
property string uid;
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
// This is for JS/QML communication, which is unused in a WebWindow,
|
||||||
|
// but not having this here results in spurious warnings about a
|
||||||
|
// missing signal
|
||||||
|
signal sendToScript(var message);
|
||||||
|
|
||||||
|
onUrlChanged: webView.runJavaScript("EventBridgeUid = \"" + uid + "\";");
|
||||||
|
onEnabledChanged: toolWindow.updateVisiblity();
|
||||||
|
onLoadingChanged: {
|
||||||
|
if (loadRequest.status == WebEngineView.LoadSucceededStatus) {
|
||||||
|
webView.runJavaScript("EventBridgeUid = \"" + uid + "\";");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,20 +132,23 @@ Windows.Window {
|
||||||
|
|
||||||
var tab = tabView.getTab(index);
|
var tab = tabView.getTab(index);
|
||||||
tab.title = "";
|
tab.title = "";
|
||||||
tab.originalUrl = "";
|
|
||||||
tab.enabled = false;
|
tab.enabled = false;
|
||||||
|
tab.originalUrl = "";
|
||||||
|
tab.item.url = "about:blank";
|
||||||
|
tab.item.enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addWebTab(properties) {
|
function addWebTab(properties) {
|
||||||
if (!properties.source) {
|
if (!properties.source) {
|
||||||
console.warn("Attempted to open Web Tool Pane without URL")
|
console.warn("Attempted to open Web Tool Pane without URL");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var existingTabIndex = findIndexForUrl(properties.source);
|
var existingTabIndex = findIndexForUrl(properties.source);
|
||||||
if (existingTabIndex >= 0) {
|
if (existingTabIndex >= 0) {
|
||||||
console.log("Existing tab " + existingTabIndex + " found with URL " + properties.source)
|
console.log("Existing tab " + existingTabIndex + " found with URL " + properties.source);
|
||||||
return tabView.getTab(existingTabIndex);
|
var tab = tabView.getTab(existingTabIndex);
|
||||||
|
return tab.item;
|
||||||
}
|
}
|
||||||
|
|
||||||
var freeTabIndex = findFreeTab();
|
var freeTabIndex = findFreeTab();
|
||||||
|
@ -135,25 +157,22 @@ Windows.Window {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var newTab = tabView.getTab(freeTabIndex);
|
|
||||||
newTab.title = properties.title || "Unknown";
|
|
||||||
newTab.originalUrl = properties.source;
|
|
||||||
newTab.item.url = properties.source;
|
|
||||||
newTab.active = true;
|
|
||||||
|
|
||||||
if (properties.width) {
|
if (properties.width) {
|
||||||
tabView.width = Math.min(Math.max(tabView.width, properties.width),
|
tabView.width = Math.min(Math.max(tabView.width, properties.width), toolWindow.maxSize.x);
|
||||||
toolWindow.maxSize.x);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties.height) {
|
if (properties.height) {
|
||||||
tabView.height = Math.min(Math.max(tabView.height, properties.height),
|
tabView.height = Math.min(Math.max(tabView.height, properties.height), toolWindow.maxSize.y);
|
||||||
toolWindow.maxSize.y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Updating visibility based on child tab added");
|
var tab = tabView.getTab(freeTabIndex);
|
||||||
newTab.enabledChanged.connect(updateVisiblity)
|
tab.title = properties.title || "Unknown";
|
||||||
updateVisiblity();
|
tab.enabled = true;
|
||||||
return newTab
|
tab.originalUrl = properties.source;
|
||||||
|
|
||||||
|
var result = tab.item;
|
||||||
|
result.enabled = true;
|
||||||
|
result.url = properties.source;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
24
interface/resources/qml/controls-uit/ComboBox.qml
Normal file → Executable file
24
interface/resources/qml/controls-uit/ComboBox.qml
Normal file → Executable file
|
@ -162,6 +162,30 @@ FocusScope {
|
||||||
height: 480
|
height: 480
|
||||||
width: root.width + 4
|
width: root.width + 4
|
||||||
|
|
||||||
|
style: ScrollViewStyle {
|
||||||
|
decrementControl: Item {
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
incrementControl: Item {
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
scrollBarBackground: Rectangle{
|
||||||
|
implicitWidth: 14
|
||||||
|
color: hifi.colors.baseGray
|
||||||
|
}
|
||||||
|
|
||||||
|
handle:
|
||||||
|
Rectangle {
|
||||||
|
implicitWidth: 8
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 3
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
radius: 3
|
||||||
|
color: hifi.colors.lightGrayText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: listView
|
id: listView
|
||||||
height: textField.height * count * 1.4
|
height: textField.height * count * 1.4
|
||||||
|
|
24
interface/resources/qml/controls-uit/SpinBox.qml
Normal file → Executable file
24
interface/resources/qml/controls-uit/SpinBox.qml
Normal file → Executable file
|
@ -35,7 +35,6 @@ SpinBox {
|
||||||
style: SpinBoxStyle {
|
style: SpinBoxStyle {
|
||||||
id: spinStyle
|
id: spinStyle
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
id: backgrondRec
|
|
||||||
color: isLightColorScheme
|
color: isLightColorScheme
|
||||||
? (spinBox.focus ? hifi.colors.white : hifi.colors.lightGray)
|
? (spinBox.focus ? hifi.colors.white : hifi.colors.lightGray)
|
||||||
: (spinBox.focus ? hifi.colors.black : hifi.colors.baseGrayShadow)
|
: (spinBox.focus ? hifi.colors.black : hifi.colors.baseGrayShadow)
|
||||||
|
@ -91,4 +90,27 @@ SpinBox {
|
||||||
color: spinBox.colorLabelInside
|
color: spinBox.colorLabelInside
|
||||||
visible: spinBox.labelInside != ""
|
visible: spinBox.labelInside != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
propagateComposedEvents: true
|
||||||
|
onWheel: {
|
||||||
|
if(spinBox.focus)
|
||||||
|
wheel.accepted = false
|
||||||
|
else
|
||||||
|
wheel.accepted = true
|
||||||
|
}
|
||||||
|
onPressed: {
|
||||||
|
mouse.accepted = false
|
||||||
|
}
|
||||||
|
onReleased: {
|
||||||
|
mouse.accepted = false
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
mouse.accepted = false
|
||||||
|
}
|
||||||
|
onDoubleClicked: {
|
||||||
|
mouse.accepted = false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,7 @@ WebEngineView {
|
||||||
request.openIn(newWindow.webView)
|
request.openIn(newWindow.webView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This breaks the webchannel used for passing messages. Fixed in Qt 5.6
|
||||||
profile: desktop.browserProfile
|
// See https://bugreports.qt.io/browse/QTBUG-49521
|
||||||
|
//profile: desktop.browserProfile
|
||||||
}
|
}
|
||||||
|
|
61
interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml
Normal file → Executable file
61
interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml
Normal file → Executable file
|
@ -4,18 +4,14 @@ import QtQuick.Dialogs 1.2 as OriginalDialogs
|
||||||
import Qt.labs.settings 1.0
|
import Qt.labs.settings 1.0
|
||||||
import QtQuick.Controls.Styles 1.4
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
|
||||||
//import "../../windows"
|
|
||||||
|
|
||||||
|
|
||||||
import "../../styles-uit"
|
import "../../styles-uit"
|
||||||
import "../../controls-uit" as HifiControls
|
import "../../controls-uit" as HifiControls
|
||||||
import "../../windows-uit"
|
import "../../windows-uit"
|
||||||
|
|
||||||
import "attachments"
|
import "attachments"
|
||||||
|
|
||||||
Window {
|
Window {
|
||||||
id: root
|
id: root
|
||||||
title: "Attachments Dialog"
|
title: "Attachments"
|
||||||
objectName: "AttachmentsDialog"
|
objectName: "AttachmentsDialog"
|
||||||
width: 600
|
width: 600
|
||||||
height: 600
|
height: 600
|
||||||
|
@ -43,6 +39,7 @@ Window {
|
||||||
listView.model.append({});
|
listView.model.append({});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
width: pane.contentWidth
|
width: pane.contentWidth
|
||||||
|
|
||||||
|
@ -55,14 +52,22 @@ Window {
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: attachmentsBackground
|
id: attachmentsBackground
|
||||||
anchors { left: parent.left; right: parent.right; top: parent.top; bottom: newAttachmentButton.top; margins: 8 }
|
anchors { left: parent.left; right: parent.right; top: parent.top; bottom: newAttachmentButton.top; margins: 8 }
|
||||||
color: hifi.colors.lightGrayText
|
color: hifi.colors.baseGrayShadow
|
||||||
radius: 4
|
radius: 4
|
||||||
|
|
||||||
ScrollView{
|
ScrollView {
|
||||||
id: scrollView
|
id: scrollView
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 4
|
anchors.margins: 4
|
||||||
|
|
||||||
style: ScrollViewStyle {
|
style: ScrollViewStyle {
|
||||||
|
|
||||||
|
padding {
|
||||||
|
top: 0
|
||||||
|
right: 0
|
||||||
|
bottom: 0
|
||||||
|
}
|
||||||
|
|
||||||
decrementControl: Item {
|
decrementControl: Item {
|
||||||
visible: false
|
visible: false
|
||||||
}
|
}
|
||||||
|
@ -72,16 +77,29 @@ Window {
|
||||||
scrollBarBackground: Rectangle{
|
scrollBarBackground: Rectangle{
|
||||||
implicitWidth: 14
|
implicitWidth: 14
|
||||||
color: hifi.colors.baseGray
|
color: hifi.colors.baseGray
|
||||||
}
|
radius: 4
|
||||||
|
Rectangle {
|
||||||
|
// Make top left corner of scrollbar appear square
|
||||||
|
width: 8
|
||||||
|
height: 4
|
||||||
|
color: hifi.colors.baseGray
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.horizontalCenter: parent.left
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
handle:
|
handle:
|
||||||
Rectangle {
|
Rectangle {
|
||||||
implicitWidth: 8
|
implicitWidth: 8
|
||||||
anchors.left: parent.left
|
anchors {
|
||||||
anchors.leftMargin: 3
|
left: parent.left
|
||||||
anchors.top: parent.top
|
leftMargin: 3
|
||||||
anchors.bottom: parent.bottom
|
top: parent.top
|
||||||
radius: 3
|
topMargin: 3
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: 4
|
||||||
|
}
|
||||||
|
radius: 4
|
||||||
color: hifi.colors.lightGrayText
|
color: hifi.colors.lightGrayText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,8 +108,9 @@ Window {
|
||||||
id: listView
|
id: listView
|
||||||
model: ListModel {}
|
model: ListModel {}
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
|
id: attachmentDelegate
|
||||||
implicitHeight: attachmentView.height + 8;
|
implicitHeight: attachmentView.height + 8;
|
||||||
implicitWidth: attachmentView.width;
|
implicitWidth: attachmentView.width
|
||||||
Attachment {
|
Attachment {
|
||||||
id: attachmentView
|
id: attachmentView
|
||||||
width: scrollView.width
|
width: scrollView.width
|
||||||
|
@ -113,7 +132,7 @@ Window {
|
||||||
anchors { left: parent.left; right: parent.right; bottom: buttonRow.top; margins: 8 }
|
anchors { left: parent.left; right: parent.right; bottom: buttonRow.top; margins: 8 }
|
||||||
text: "New Attachment"
|
text: "New Attachment"
|
||||||
color: hifi.buttons.black
|
color: hifi.buttons.black
|
||||||
colorScheme: hifi.colorSchemes.dark
|
colorScheme: hifi.colorSchemes.dark
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var template = {
|
var template = {
|
||||||
modelUrl: "",
|
modelUrl: "",
|
||||||
|
@ -133,15 +152,15 @@ Window {
|
||||||
id: buttonRow
|
id: buttonRow
|
||||||
spacing: 8
|
spacing: 8
|
||||||
anchors { right: parent.right; bottom: parent.bottom; margins: 8 }
|
anchors { right: parent.right; bottom: parent.bottom; margins: 8 }
|
||||||
HifiControls.Button {
|
|
||||||
action: cancelAction
|
|
||||||
color: hifi.buttons.black
|
|
||||||
colorScheme: hifi.colorSchemes.dark
|
|
||||||
}
|
|
||||||
HifiControls.Button {
|
HifiControls.Button {
|
||||||
action: okAction
|
action: okAction
|
||||||
color: hifi.buttons.black
|
color: hifi.buttons.black
|
||||||
colorScheme: hifi.colorSchemes.dark
|
colorScheme: hifi.colorSchemes.dark
|
||||||
|
}
|
||||||
|
HifiControls.Button {
|
||||||
|
action: cancelAction
|
||||||
|
color: hifi.buttons.black
|
||||||
|
colorScheme: hifi.colorSchemes.dark
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
109
interface/resources/qml/hifi/dialogs/attachments/Attachment.qml
Normal file → Executable file
109
interface/resources/qml/hifi/dialogs/attachments/Attachment.qml
Normal file → Executable file
|
@ -4,11 +4,8 @@ import QtQuick.Controls.Styles 1.4
|
||||||
import QtQuick.Dialogs 1.2 as OriginalDialogs
|
import QtQuick.Dialogs 1.2 as OriginalDialogs
|
||||||
import Qt.labs.settings 1.0
|
import Qt.labs.settings 1.0
|
||||||
|
|
||||||
//import "../../../windows"
|
|
||||||
//import "../../../controls" as VrControls
|
|
||||||
import "."
|
import "."
|
||||||
import ".."
|
import ".."
|
||||||
|
|
||||||
import "../../../styles-uit"
|
import "../../../styles-uit"
|
||||||
import "../../../controls-uit" as HifiControls
|
import "../../../controls-uit" as HifiControls
|
||||||
import "../../../windows-uit"
|
import "../../../windows-uit"
|
||||||
|
@ -33,13 +30,13 @@ Item {
|
||||||
Column {
|
Column {
|
||||||
y: 8
|
y: 8
|
||||||
id: column
|
id: column
|
||||||
anchors { left: parent.left; right: parent.right; margins: 8 }
|
anchors { left: parent.left; right: parent.right; margins: 20 }
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
height: modelChooserButton.height + urlLabel.height
|
height: modelChooserButton.height + urlLabel.height + 4
|
||||||
anchors { left: parent.left; right: parent.right;}
|
anchors { left: parent.left; right: parent.right;}
|
||||||
Text { id: urlLabel; color: hifi.colors.lightGrayText; text: "Model URL:"; width: 80; anchors.top: parent.top;}
|
HifiControls.Label { id: urlLabel; color: hifi.colors.lightGrayText; text: "Model URL"; anchors.top: parent.top;}
|
||||||
HifiControls.TextField {
|
HifiControls.TextField {
|
||||||
id: modelUrl;
|
id: modelUrl;
|
||||||
height: jointChooser.height;
|
height: jointChooser.height;
|
||||||
|
@ -60,12 +57,12 @@ Item {
|
||||||
colorScheme: hifi.colorSchemes.dark
|
colorScheme: hifi.colorSchemes.dark
|
||||||
anchors { right: parent.right; verticalCenter: modelUrl.verticalCenter }
|
anchors { right: parent.right; verticalCenter: modelUrl.verticalCenter }
|
||||||
Component {
|
Component {
|
||||||
id: modelBrowserBuiler;
|
id: modelBrowserBuilder;
|
||||||
ModelBrowserDialog {}
|
ModelBrowserDialog {}
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var browser = modelBrowserBuiler.createObject(desktop);
|
var browser = modelBrowserBuilder.createObject(desktop);
|
||||||
browser.selected.connect(function(newModelUrl){
|
browser.selected.connect(function(newModelUrl){
|
||||||
modelUrl.text = newModelUrl;
|
modelUrl.text = newModelUrl;
|
||||||
})
|
})
|
||||||
|
@ -74,12 +71,11 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
height: jointChooser.height + jointLabel.height
|
height: jointChooser.height + jointLabel.height + 4
|
||||||
anchors { left: parent.left; right: parent.right; }
|
anchors { left: parent.left; right: parent.right; }
|
||||||
Text {
|
HifiControls.Label {
|
||||||
id: jointLabel;
|
id: jointLabel;
|
||||||
width: 80;
|
text: "Joint";
|
||||||
text: "Joint:";
|
|
||||||
color: hifi.colors.lightGrayText;
|
color: hifi.colors.lightGrayText;
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
}
|
}
|
||||||
|
@ -99,9 +95,9 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
height: translation.height + translationLabel.height
|
height: translation.height + translationLabel.height + 4
|
||||||
anchors { left: parent.left; right: parent.right; }
|
anchors { left: parent.left; right: parent.right; }
|
||||||
Text { id: translationLabel; width: 80; color: hifi.colors.lightGrayText; text: "Translation:"; anchors.top: parent.top; }
|
HifiControls.Label { id: translationLabel; color: hifi.colors.lightGrayText; text: "Translation"; anchors.top: parent.top; }
|
||||||
Translation {
|
Translation {
|
||||||
id: translation;
|
id: translation;
|
||||||
anchors { left: parent.left; right: parent.right; bottom: parent.bottom}
|
anchors { left: parent.left; right: parent.right; bottom: parent.bottom}
|
||||||
|
@ -116,9 +112,9 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
height: rotation.height + rotationLabel.height
|
height: rotation.height + rotationLabel.height + 4
|
||||||
anchors { left: parent.left; right: parent.right; }
|
anchors { left: parent.left; right: parent.right; }
|
||||||
Text { id: rotationLabel; width: 80; color: hifi.colors.lightGrayText; text: "Rotation:"; anchors.top: parent.top; }
|
HifiControls.Label { id: rotationLabel; color: hifi.colors.lightGrayText; text: "Rotation"; anchors.top: parent.top; }
|
||||||
Rotation {
|
Rotation {
|
||||||
id: rotation;
|
id: rotation;
|
||||||
anchors { left: parent.left; right: parent.right; bottom: parent.bottom; }
|
anchors { left: parent.left; right: parent.right; bottom: parent.bottom; }
|
||||||
|
@ -133,45 +129,58 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
height: scaleSpinner.height + scaleLabel.height
|
height: scaleItem.height
|
||||||
anchors { left: parent.left; right: parent.right; }
|
anchors { left: parent.left; right: parent.right; }
|
||||||
Text { id: scaleLabel; width: 80; color: hifi.colors.lightGrayText; text: "Scale:"; anchors.top: parent.top; }
|
|
||||||
HifiControls.SpinBox {
|
Item {
|
||||||
id: scaleSpinner;
|
id: scaleItem
|
||||||
anchors { left: parent.left; right: parent.right; bottom: parent.bottom; }
|
height: scaleSpinner.height + scaleLabel.height + 4
|
||||||
decimals: 1;
|
width: parent.width / 3 - 8
|
||||||
minimumValue: 0.1
|
anchors { right: parent.right; }
|
||||||
maximumValue: 10
|
HifiControls.Label { id: scaleLabel; color: hifi.colors.lightGrayText; text: "Scale"; anchors.top: parent.top; }
|
||||||
stepSize: 0.1;
|
HifiControls.SpinBox {
|
||||||
value: attachment ? attachment.scale : 1.0
|
id: scaleSpinner;
|
||||||
colorScheme: hifi.colorSchemes.dark
|
anchors { left: parent.left; right: parent.right; bottom: parent.bottom; }
|
||||||
onValueChanged: {
|
decimals: 1;
|
||||||
if (completed && attachment && attachment.scale !== value) {
|
minimumValue: 0.1
|
||||||
attachment.scale = value;
|
maximumValue: 10
|
||||||
updateAttachment();
|
stepSize: 0.1;
|
||||||
|
value: attachment ? attachment.scale : 1.0
|
||||||
|
colorScheme: hifi.colorSchemes.dark
|
||||||
|
onValueChanged: {
|
||||||
|
if (completed && attachment && attachment.scale !== value) {
|
||||||
|
attachment.scale = value;
|
||||||
|
updateAttachment();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: isSoftItem
|
||||||
|
height: scaleSpinner.height
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
HifiControls.CheckBox {
|
||||||
|
id: soft
|
||||||
|
text: "Is soft"
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
checked: attachment ? attachment.soft : false
|
||||||
|
colorScheme: hifi.colorSchemes.dark
|
||||||
|
onCheckedChanged: {
|
||||||
|
if (completed && attachment && attachment.soft !== checked) {
|
||||||
|
attachment.soft = checked;
|
||||||
|
updateAttachment();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
|
||||||
height: soft.height
|
|
||||||
anchors { left: parent.left; right: parent.right; }
|
|
||||||
Text { id: softLabel; width: 80; color: hifi.colors.lightGrayText; text: "Is soft"; anchors.left: soft.right; anchors.leftMargin: 8; }
|
|
||||||
HifiControls.CheckBox {
|
|
||||||
id: soft;
|
|
||||||
anchors { left: parent.left; bottom: parent.bottom;}
|
|
||||||
checked: attachment ? attachment.soft : false
|
|
||||||
colorScheme: hifi.colorSchemes.dark
|
|
||||||
onCheckedChanged: {
|
|
||||||
if (completed && attachment && attachment.soft !== checked) {
|
|
||||||
attachment.soft = checked;
|
|
||||||
updateAttachment();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HifiControls.Button {
|
HifiControls.Button {
|
||||||
color: hifi.buttons.black
|
color: hifi.buttons.black
|
||||||
colorScheme: hifi.colorSchemes.dark
|
colorScheme: hifi.colorSchemes.dark
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import "."
|
import "."
|
||||||
|
|
||||||
Vector3 {
|
Vector3 {
|
||||||
decimals: 2;
|
decimals: 3;
|
||||||
stepSize: 0.01;
|
stepSize: 0.01;
|
||||||
maximumValue: 10
|
maximumValue: 10
|
||||||
minimumValue: -10
|
minimumValue: -10
|
||||||
|
|
|
@ -195,6 +195,7 @@ static const QString FBX_EXTENSION = ".fbx";
|
||||||
static const QString OBJ_EXTENSION = ".obj";
|
static const QString OBJ_EXTENSION = ".obj";
|
||||||
static const QString AVA_JSON_EXTENSION = ".ava.json";
|
static const QString AVA_JSON_EXTENSION = ".ava.json";
|
||||||
|
|
||||||
|
static const int MSECS_PER_SEC = 1000;
|
||||||
static const int MIRROR_VIEW_TOP_PADDING = 5;
|
static const int MIRROR_VIEW_TOP_PADDING = 5;
|
||||||
static const int MIRROR_VIEW_LEFT_PADDING = 10;
|
static const int MIRROR_VIEW_LEFT_PADDING = 10;
|
||||||
static const int MIRROR_VIEW_WIDTH = 265;
|
static const int MIRROR_VIEW_WIDTH = 265;
|
||||||
|
@ -239,7 +240,7 @@ class DeadlockWatchdogThread : public QThread {
|
||||||
public:
|
public:
|
||||||
static const unsigned long HEARTBEAT_CHECK_INTERVAL_SECS = 1;
|
static const unsigned long HEARTBEAT_CHECK_INTERVAL_SECS = 1;
|
||||||
static const unsigned long HEARTBEAT_UPDATE_INTERVAL_SECS = 1;
|
static const unsigned long HEARTBEAT_UPDATE_INTERVAL_SECS = 1;
|
||||||
static const unsigned long MAX_HEARTBEAT_AGE_USECS = 10 * USECS_PER_SECOND;
|
static const unsigned long MAX_HEARTBEAT_AGE_USECS = 15 * USECS_PER_SECOND;
|
||||||
|
|
||||||
// Set the heartbeat on launch
|
// Set the heartbeat on launch
|
||||||
DeadlockWatchdogThread() {
|
DeadlockWatchdogThread() {
|
||||||
|
@ -365,7 +366,7 @@ bool setupEssentials(int& argc, char** argv) {
|
||||||
|
|
||||||
Setting::preInit();
|
Setting::preInit();
|
||||||
|
|
||||||
CrashHandler::checkForAndHandleCrash();
|
bool previousSessionCrashed = CrashHandler::checkForAndHandleCrash();
|
||||||
CrashHandler::writeRunningMarkerFiler();
|
CrashHandler::writeRunningMarkerFiler();
|
||||||
qAddPostRoutine(CrashHandler::deleteRunningMarkerFile);
|
qAddPostRoutine(CrashHandler::deleteRunningMarkerFile);
|
||||||
|
|
||||||
|
@ -427,7 +428,7 @@ bool setupEssentials(int& argc, char** argv) {
|
||||||
DependencyManager::set<InterfaceParentFinder>();
|
DependencyManager::set<InterfaceParentFinder>();
|
||||||
DependencyManager::set<EntityTreeRenderer>(true, qApp, qApp);
|
DependencyManager::set<EntityTreeRenderer>(true, qApp, qApp);
|
||||||
DependencyManager::set<CompositorHelper>();
|
DependencyManager::set<CompositorHelper>();
|
||||||
return true;
|
return previousSessionCrashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME move to header, or better yet, design some kind of UI manager
|
// FIXME move to header, or better yet, design some kind of UI manager
|
||||||
|
@ -448,10 +449,13 @@ PluginContainer* _pluginContainer;
|
||||||
OffscreenGLCanvas* _chromiumShareContext { nullptr };
|
OffscreenGLCanvas* _chromiumShareContext { nullptr };
|
||||||
Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context);
|
Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context);
|
||||||
|
|
||||||
|
Setting::Handle<int> sessionRunTime{ "sessionRunTime", 0 };
|
||||||
|
|
||||||
Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
QApplication(argc, argv),
|
QApplication(argc, argv),
|
||||||
_window(new MainWindow(desktop())),
|
_window(new MainWindow(desktop())),
|
||||||
_dependencyManagerIsSetup(setupEssentials(argc, argv)),
|
_sessionRunTimer(startupTimer),
|
||||||
|
_previousSessionCrashed(setupEssentials(argc, argv)),
|
||||||
_undoStackScriptingInterface(&_undoStack),
|
_undoStackScriptingInterface(&_undoStack),
|
||||||
_frameCount(0),
|
_frameCount(0),
|
||||||
_fps(60.0f),
|
_fps(60.0f),
|
||||||
|
@ -601,7 +605,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));
|
connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));
|
||||||
|
|
||||||
// update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one
|
// update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one
|
||||||
const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000;
|
const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * MSECS_PER_SEC;
|
||||||
|
|
||||||
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
|
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
|
||||||
connect(&locationUpdateTimer, &QTimer::timeout, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);
|
connect(&locationUpdateTimer, &QTimer::timeout, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);
|
||||||
|
@ -623,7 +627,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
// connect to appropriate slots on AccountManager
|
// connect to appropriate slots on AccountManager
|
||||||
AccountManager& accountManager = AccountManager::getInstance();
|
AccountManager& accountManager = AccountManager::getInstance();
|
||||||
|
|
||||||
const qint64 BALANCE_UPDATE_INTERVAL_MSECS = 5 * 1000;
|
const qint64 BALANCE_UPDATE_INTERVAL_MSECS = 5 * MSECS_PER_SEC;
|
||||||
|
|
||||||
connect(&balanceUpdateTimer, &QTimer::timeout, &accountManager, &AccountManager::updateBalance);
|
connect(&balanceUpdateTimer, &QTimer::timeout, &accountManager, &AccountManager::updateBalance);
|
||||||
balanceUpdateTimer.start(BALANCE_UPDATE_INTERVAL_MSECS);
|
balanceUpdateTimer.start(BALANCE_UPDATE_INTERVAL_MSECS);
|
||||||
|
@ -638,7 +642,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
accountManager.setIsAgent(true);
|
accountManager.setIsAgent(true);
|
||||||
accountManager.setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL);
|
accountManager.setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL);
|
||||||
|
|
||||||
UserActivityLogger::getInstance().launch(applicationVersion());
|
// sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value.
|
||||||
|
// The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
|
||||||
|
UserActivityLogger::getInstance().launch(applicationVersion(), _previousSessionCrashed, sessionRunTime.get());
|
||||||
|
|
||||||
// once the event loop has started, check and signal for an access token
|
// once the event loop has started, check and signal for an access token
|
||||||
QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection);
|
||||||
|
@ -1409,7 +1415,7 @@ void Application::paintGL() {
|
||||||
_lastFramesPerSecondUpdate = now;
|
_lastFramesPerSecondUpdate = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
PROFILE_RANGE(__FUNCTION__);
|
PROFILE_RANGE_EX(__FUNCTION__, 0xff0000ff, (uint64_t)_frameCount);
|
||||||
PerformanceTimer perfTimer("paintGL");
|
PerformanceTimer perfTimer("paintGL");
|
||||||
|
|
||||||
if (nullptr == _displayPlugin) {
|
if (nullptr == _displayPlugin) {
|
||||||
|
@ -2548,11 +2554,12 @@ void Application::idle(uint64_t now) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PROFILE_RANGE(__FUNCTION__);
|
||||||
|
|
||||||
// We're going to execute idle processing, so restart the last idle timer
|
// We're going to execute idle processing, so restart the last idle timer
|
||||||
_lastTimeUpdated.start();
|
_lastTimeUpdated.start();
|
||||||
|
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(__FUNCTION__);
|
|
||||||
static uint64_t lastIdleStart{ now };
|
static uint64_t lastIdleStart{ now };
|
||||||
uint64_t idleStartToStartDuration = now - lastIdleStart;
|
uint64_t idleStartToStartDuration = now - lastIdleStart;
|
||||||
if (idleStartToStartDuration != 0) {
|
if (idleStartToStartDuration != 0) {
|
||||||
|
@ -2804,6 +2811,7 @@ bool Application::exportEntities(const QString& filename, float x, float y, floa
|
||||||
|
|
||||||
void Application::loadSettings() {
|
void Application::loadSettings() {
|
||||||
|
|
||||||
|
sessionRunTime.set(0); // Just clean living. We're about to saveSettings, which will update value.
|
||||||
DependencyManager::get<AudioClient>()->loadSettings();
|
DependencyManager::get<AudioClient>()->loadSettings();
|
||||||
DependencyManager::get<LODManager>()->loadSettings();
|
DependencyManager::get<LODManager>()->loadSettings();
|
||||||
|
|
||||||
|
@ -2817,6 +2825,7 @@ void Application::loadSettings() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::saveSettings() {
|
void Application::saveSettings() {
|
||||||
|
sessionRunTime.set(_sessionRunTimer.elapsed() / MSECS_PER_SEC);
|
||||||
DependencyManager::get<AudioClient>()->saveSettings();
|
DependencyManager::get<AudioClient>()->saveSettings();
|
||||||
DependencyManager::get<LODManager>()->saveSettings();
|
DependencyManager::get<LODManager>()->saveSettings();
|
||||||
|
|
||||||
|
@ -3138,6 +3147,9 @@ void Application::updateDialogs(float deltaTime) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::update(float deltaTime) {
|
void Application::update(float deltaTime) {
|
||||||
|
|
||||||
|
PROFILE_RANGE_EX(__FUNCTION__, 0xffff0000, (uint64_t)_frameCount + 1);
|
||||||
|
|
||||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||||
PerformanceWarning warn(showWarnings, "Application::update()");
|
PerformanceWarning warn(showWarnings, "Application::update()");
|
||||||
|
|
||||||
|
@ -3228,7 +3240,9 @@ void Application::update(float deltaTime) {
|
||||||
controller::Pose leftHandPose = userInputMapper->getPoseState(controller::Action::LEFT_HAND);
|
controller::Pose leftHandPose = userInputMapper->getPoseState(controller::Action::LEFT_HAND);
|
||||||
controller::Pose rightHandPose = userInputMapper->getPoseState(controller::Action::RIGHT_HAND);
|
controller::Pose rightHandPose = userInputMapper->getPoseState(controller::Action::RIGHT_HAND);
|
||||||
auto myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition());
|
auto myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition());
|
||||||
myAvatar->setHandControllerPosesInWorldFrame(leftHandPose.transform(myAvatarMatrix), rightHandPose.transform(myAvatarMatrix));
|
auto worldToSensorMatrix = glm::inverse(myAvatar->getSensorToWorldMatrix());
|
||||||
|
auto avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix;
|
||||||
|
myAvatar->setHandControllerPosesInSensorFrame(leftHandPose.transform(avatarToSensorMatrix), rightHandPose.transform(avatarToSensorMatrix));
|
||||||
|
|
||||||
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
|
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
|
||||||
updateDialogs(deltaTime); // update various stats dialogs if present
|
updateDialogs(deltaTime); // update various stats dialogs if present
|
||||||
|
@ -3236,9 +3250,13 @@ void Application::update(float deltaTime) {
|
||||||
QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>();
|
QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>();
|
||||||
|
|
||||||
if (_physicsEnabled) {
|
if (_physicsEnabled) {
|
||||||
|
PROFILE_RANGE_EX("Physics", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||||
|
|
||||||
PerformanceTimer perfTimer("physics");
|
PerformanceTimer perfTimer("physics");
|
||||||
|
|
||||||
{
|
{
|
||||||
|
PROFILE_RANGE_EX("UpdateStats", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||||
|
|
||||||
PerformanceTimer perfTimer("updateStates)");
|
PerformanceTimer perfTimer("updateStates)");
|
||||||
static VectorOfMotionStates motionStates;
|
static VectorOfMotionStates motionStates;
|
||||||
_entitySimulation.getObjectsToRemoveFromPhysics(motionStates);
|
_entitySimulation.getObjectsToRemoveFromPhysics(motionStates);
|
||||||
|
@ -3271,12 +3289,14 @@ void Application::update(float deltaTime) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
PROFILE_RANGE_EX("StepSimulation", 0xffff8000, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||||
PerformanceTimer perfTimer("stepSimulation");
|
PerformanceTimer perfTimer("stepSimulation");
|
||||||
getEntities()->getTree()->withWriteLock([&] {
|
getEntities()->getTree()->withWriteLock([&] {
|
||||||
_physicsEngine->stepSimulation();
|
_physicsEngine->stepSimulation();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
PROFILE_RANGE_EX("HarvestChanges", 0xffffff00, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||||
PerformanceTimer perfTimer("havestChanges");
|
PerformanceTimer perfTimer("havestChanges");
|
||||||
if (_physicsEngine->hasOutgoingChanges()) {
|
if (_physicsEngine->hasOutgoingChanges()) {
|
||||||
getEntities()->getTree()->withWriteLock([&] {
|
getEntities()->getTree()->withWriteLock([&] {
|
||||||
|
@ -3311,14 +3331,24 @@ void Application::update(float deltaTime) {
|
||||||
|
|
||||||
qApp->setAvatarSimrateSample(1.0f / deltaTime);
|
qApp->setAvatarSimrateSample(1.0f / deltaTime);
|
||||||
|
|
||||||
avatarManager->updateOtherAvatars(deltaTime);
|
{
|
||||||
|
PROFILE_RANGE_EX("OtherAvatars", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||||
|
avatarManager->updateOtherAvatars(deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
qApp->updateMyAvatarLookAtPosition();
|
qApp->updateMyAvatarLookAtPosition();
|
||||||
|
|
||||||
avatarManager->updateMyAvatar(deltaTime);
|
// update sensorToWorldMatrix for camera and hand controllers
|
||||||
|
myAvatar->updateSensorToWorldMatrix();
|
||||||
|
|
||||||
|
{
|
||||||
|
PROFILE_RANGE_EX("MyAvatar", 0xffff00ff, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||||
|
avatarManager->updateMyAvatar(deltaTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
PROFILE_RANGE_EX("Overlays", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||||
PerformanceTimer perfTimer("overlays");
|
PerformanceTimer perfTimer("overlays");
|
||||||
_overlays.update(deltaTime);
|
_overlays.update(deltaTime);
|
||||||
}
|
}
|
||||||
|
@ -3338,6 +3368,7 @@ void Application::update(float deltaTime) {
|
||||||
|
|
||||||
// Update my voxel servers with my current voxel query...
|
// Update my voxel servers with my current voxel query...
|
||||||
{
|
{
|
||||||
|
PROFILE_RANGE_EX("QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||||
PerformanceTimer perfTimer("queryOctree");
|
PerformanceTimer perfTimer("queryOctree");
|
||||||
quint64 sinceLastQuery = now - _lastQueriedTime;
|
quint64 sinceLastQuery = now - _lastQueriedTime;
|
||||||
const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;
|
const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;
|
||||||
|
@ -3374,9 +3405,6 @@ void Application::update(float deltaTime) {
|
||||||
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "sendDownstreamAudioStatsPacket", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "sendDownstreamAudioStatsPacket", Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// update sensorToWorldMatrix for rendering camera.
|
|
||||||
myAvatar->updateSensorToWorldMatrix();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -4667,13 +4695,18 @@ qreal Application::getDevicePixelRatio() {
|
||||||
}
|
}
|
||||||
|
|
||||||
DisplayPlugin* Application::getActiveDisplayPlugin() {
|
DisplayPlugin* Application::getActiveDisplayPlugin() {
|
||||||
std::unique_lock<std::recursive_mutex> lock(_displayPluginLock);
|
DisplayPlugin* result = nullptr;
|
||||||
if (nullptr == _displayPlugin && QThread::currentThread() == thread()) {
|
if (QThread::currentThread() == thread()) {
|
||||||
updateDisplayMode();
|
if (nullptr == _displayPlugin) {
|
||||||
Q_ASSERT(_displayPlugin);
|
updateDisplayMode();
|
||||||
|
Q_ASSERT(_displayPlugin);
|
||||||
|
}
|
||||||
|
result = _displayPlugin.get();
|
||||||
|
} else {
|
||||||
|
std::unique_lock<std::mutex> lock(_displayPluginLock);
|
||||||
|
result = _displayPlugin.get();
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
return _displayPlugin.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const DisplayPlugin* Application::getActiveDisplayPlugin() const {
|
const DisplayPlugin* Application::getActiveDisplayPlugin() const {
|
||||||
|
@ -4791,20 +4824,26 @@ void Application::updateDisplayMode() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_displayPlugin) {
|
|
||||||
_displayPlugin->deactivate();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||||
|
|
||||||
// FIXME probably excessive and useless context switching
|
// Make the switch atomic from the perspective of other threads
|
||||||
_offscreenContext->makeCurrent();
|
{
|
||||||
newDisplayPlugin->activate();
|
std::unique_lock<std::mutex> lock(_displayPluginLock);
|
||||||
_offscreenContext->makeCurrent();
|
|
||||||
offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize()));
|
if (_displayPlugin) {
|
||||||
_offscreenContext->makeCurrent();
|
_displayPlugin->deactivate();
|
||||||
getApplicationCompositor().setDisplayPlugin(newDisplayPlugin);
|
}
|
||||||
_displayPlugin = newDisplayPlugin;
|
|
||||||
|
// FIXME probably excessive and useless context switching
|
||||||
|
_offscreenContext->makeCurrent();
|
||||||
|
newDisplayPlugin->activate();
|
||||||
|
_offscreenContext->makeCurrent();
|
||||||
|
offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize()));
|
||||||
|
_offscreenContext->makeCurrent();
|
||||||
|
getApplicationCompositor().setDisplayPlugin(newDisplayPlugin);
|
||||||
|
_displayPlugin = newDisplayPlugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
emit activeDisplayPluginChanged();
|
emit activeDisplayPluginChanged();
|
||||||
|
|
||||||
|
|
|
@ -377,12 +377,13 @@ private:
|
||||||
void maybeToggleMenuVisible(QMouseEvent* event);
|
void maybeToggleMenuVisible(QMouseEvent* event);
|
||||||
|
|
||||||
MainWindow* _window;
|
MainWindow* _window;
|
||||||
|
QElapsedTimer& _sessionRunTimer;
|
||||||
|
|
||||||
bool _dependencyManagerIsSetup;
|
bool _previousSessionCrashed;
|
||||||
|
|
||||||
OffscreenGLCanvas* _offscreenContext { nullptr };
|
OffscreenGLCanvas* _offscreenContext { nullptr };
|
||||||
DisplayPluginPointer _displayPlugin;
|
DisplayPluginPointer _displayPlugin;
|
||||||
std::recursive_mutex _displayPluginLock;
|
std::mutex _displayPluginLock;
|
||||||
InputPluginList _activeInputPlugins;
|
InputPluginList _activeInputPlugins;
|
||||||
|
|
||||||
bool _activatingDisplayPlugin { false };
|
bool _activatingDisplayPlugin { false };
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
|
|
||||||
static const QString RUNNING_MARKER_FILENAME = "Interface.running";
|
static const QString RUNNING_MARKER_FILENAME = "Interface.running";
|
||||||
|
|
||||||
void CrashHandler::checkForAndHandleCrash() {
|
bool CrashHandler::checkForAndHandleCrash() {
|
||||||
QFile runningMarkerFile(runningMarkerFilePath());
|
QFile runningMarkerFile(runningMarkerFilePath());
|
||||||
if (runningMarkerFile.exists()) {
|
if (runningMarkerFile.exists()) {
|
||||||
QSettings::setDefaultFormat(QSettings::IniFormat);
|
QSettings::setDefaultFormat(QSettings::IniFormat);
|
||||||
|
@ -42,7 +42,9 @@ void CrashHandler::checkForAndHandleCrash() {
|
||||||
handleCrash(action);
|
handleCrash(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CrashHandler::Action CrashHandler::promptUserForAction() {
|
CrashHandler::Action CrashHandler::promptUserForAction() {
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
class CrashHandler {
|
class CrashHandler {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void checkForAndHandleCrash();
|
static bool checkForAndHandleCrash();
|
||||||
|
|
||||||
static void writeRunningMarkerFiler();
|
static void writeRunningMarkerFiler();
|
||||||
static void deleteRunningMarkerFile();
|
static void deleteRunningMarkerFile();
|
||||||
|
|
|
@ -107,7 +107,7 @@ Menu::Menu() {
|
||||||
|
|
||||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||||
// Edit > Stop All Scripts... [advanced]
|
// Edit > Stop All Scripts... [advanced]
|
||||||
addActionToQMenuAndActionHash(editMenu, MenuOption::StopAllScripts, 0,
|
addActionToQMenuAndActionHash(editMenu, MenuOption::StopAllScripts, 0,
|
||||||
scriptEngines.data(), SLOT(stopAllScripts()),
|
scriptEngines.data(), SLOT(stopAllScripts()),
|
||||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ Menu::Menu() {
|
||||||
// Edit > Reload All Content [advanced]
|
// Edit > Reload All Content [advanced]
|
||||||
addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()),
|
addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()),
|
||||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||||
|
|
||||||
|
|
||||||
// Edit > Package Model... [advanced]
|
// Edit > Package Model... [advanced]
|
||||||
addActionToQMenuAndActionHash(editMenu, MenuOption::PackageModel, 0,
|
addActionToQMenuAndActionHash(editMenu, MenuOption::PackageModel, 0,
|
||||||
|
@ -153,7 +153,7 @@ Menu::Menu() {
|
||||||
auto audioIO = DependencyManager::get<AudioClient>();
|
auto audioIO = DependencyManager::get<AudioClient>();
|
||||||
|
|
||||||
// Audio > Mute
|
// Audio > Mute
|
||||||
addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::MuteAudio, Qt::CTRL | Qt::Key_M, false,
|
addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::MuteAudio, Qt::CTRL | Qt::Key_M, false,
|
||||||
audioIO.data(), SLOT(toggleMute()));
|
audioIO.data(), SLOT(toggleMute()));
|
||||||
|
|
||||||
// Audio > Show Level Meter
|
// Audio > Show Level Meter
|
||||||
|
@ -458,7 +458,7 @@ Menu::Menu() {
|
||||||
avatar, SLOT(setEnableMeshVisible(bool)));
|
avatar, SLOT(setEnableMeshVisible(bool)));
|
||||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
|
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
|
||||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false);
|
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false);
|
||||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::UseAnimPreAndPostRotations, 0, false,
|
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::UseAnimPreAndPostRotations, 0, true,
|
||||||
avatar, SLOT(setUseAnimPreAndPostRotations(bool)));
|
avatar, SLOT(setUseAnimPreAndPostRotations(bool)));
|
||||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableInverseKinematics, 0, true,
|
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableInverseKinematics, 0, true,
|
||||||
avatar, SLOT(setEnableInverseKinematics(bool)));
|
avatar, SLOT(setEnableInverseKinematics(bool)));
|
||||||
|
@ -534,7 +534,7 @@ Menu::Menu() {
|
||||||
|
|
||||||
// Developer > Audio >>>
|
// Developer > Audio >>>
|
||||||
MenuWrapper* audioDebugMenu = developerMenu->addMenu("Audio");
|
MenuWrapper* audioDebugMenu = developerMenu->addMenu("Audio");
|
||||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction, 0, true,
|
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction, 0, true,
|
||||||
audioIO.data(), SLOT(toggleAudioNoiseReduction()));
|
audioIO.data(), SLOT(toggleAudioNoiseReduction()));
|
||||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio, 0, false,
|
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio, 0, false,
|
||||||
audioIO.data(), SLOT(toggleServerEcho()));
|
audioIO.data(), SLOT(toggleServerEcho()));
|
||||||
|
@ -617,7 +617,7 @@ Menu::Menu() {
|
||||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||||
|
|
||||||
|
|
||||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true,
|
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true,
|
||||||
NULL, NULL, UNSPECIFIED_POSITION, "Advanced");
|
NULL, NULL, UNSPECIFIED_POSITION, "Advanced");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -651,7 +651,7 @@ void Menu::addMenuItem(const MenuItemProperties& properties) {
|
||||||
} else if (properties.isCheckable) {
|
} else if (properties.isCheckable) {
|
||||||
menuItemAction = addCheckableActionToQMenuAndActionHash(menuObj, properties.menuItemName,
|
menuItemAction = addCheckableActionToQMenuAndActionHash(menuObj, properties.menuItemName,
|
||||||
properties.shortcutKeySequence, properties.isChecked,
|
properties.shortcutKeySequence, properties.isChecked,
|
||||||
MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()),
|
MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()),
|
||||||
requestedPosition, properties.grouping);
|
requestedPosition, properties.grouping);
|
||||||
} else {
|
} else {
|
||||||
menuItemAction = addActionToQMenuAndActionHash(menuObj, properties.menuItemName, properties.shortcutKeySequence,
|
menuItemAction = addActionToQMenuAndActionHash(menuObj, properties.menuItemName, properties.shortcutKeySequence,
|
||||||
|
|
|
@ -187,7 +187,7 @@ void Avatar::simulate(float deltaTime) {
|
||||||
|
|
||||||
// simple frustum check
|
// simple frustum check
|
||||||
float boundingRadius = getBoundingRadius();
|
float boundingRadius = getBoundingRadius();
|
||||||
bool inView = qApp->getViewFrustum()->sphereIntersectsFrustum(getPosition(), boundingRadius);
|
bool inView = qApp->getDisplayViewFrustum()->sphereIntersectsFrustum(getPosition(), boundingRadius);
|
||||||
|
|
||||||
if (_shouldAnimate && !_shouldSkipRender && inView) {
|
if (_shouldAnimate && !_shouldSkipRender && inView) {
|
||||||
{
|
{
|
||||||
|
|
|
@ -418,7 +418,7 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) {
|
||||||
_hmdSensorFacing = getFacingDir2D(_hmdSensorOrientation);
|
_hmdSensorFacing = getFacingDir2D(_hmdSensorOrientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
// best called at end of main loop, just before rendering.
|
// best called at end of main loop, after physics.
|
||||||
// update sensor to world matrix from current body position and hmd sensor.
|
// update sensor to world matrix from current body position and hmd sensor.
|
||||||
// This is so the correct camera can be used for rendering.
|
// This is so the correct camera can be used for rendering.
|
||||||
void MyAvatar::updateSensorToWorldMatrix() {
|
void MyAvatar::updateSensorToWorldMatrix() {
|
||||||
|
@ -1087,24 +1087,32 @@ static controller::Pose applyLowVelocityFilter(const controller::Pose& oldPose,
|
||||||
return finalPose;
|
return finalPose;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::setHandControllerPosesInWorldFrame(const controller::Pose& left, const controller::Pose& right) {
|
void MyAvatar::setHandControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) {
|
||||||
if (controller::InputDevice::getLowVelocityFilter()) {
|
if (controller::InputDevice::getLowVelocityFilter()) {
|
||||||
auto oldLeftPose = getLeftHandControllerPoseInWorldFrame();
|
auto oldLeftPose = getLeftHandControllerPoseInSensorFrame();
|
||||||
auto oldRightPose = getRightHandControllerPoseInWorldFrame();
|
auto oldRightPose = getRightHandControllerPoseInSensorFrame();
|
||||||
_leftHandControllerPoseInWorldFrameCache.set(applyLowVelocityFilter(oldLeftPose, left));
|
_leftHandControllerPoseInSensorFrameCache.set(applyLowVelocityFilter(oldLeftPose, left));
|
||||||
_rightHandControllerPoseInWorldFrameCache.set(applyLowVelocityFilter(oldRightPose, right));
|
_rightHandControllerPoseInSensorFrameCache.set(applyLowVelocityFilter(oldRightPose, right));
|
||||||
} else {
|
} else {
|
||||||
_leftHandControllerPoseInWorldFrameCache.set(left);
|
_leftHandControllerPoseInSensorFrameCache.set(left);
|
||||||
_rightHandControllerPoseInWorldFrameCache.set(right);
|
_rightHandControllerPoseInSensorFrameCache.set(right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
controller::Pose MyAvatar::getLeftHandControllerPoseInSensorFrame() const {
|
||||||
|
return _leftHandControllerPoseInSensorFrameCache.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
controller::Pose MyAvatar::getRightHandControllerPoseInSensorFrame() const {
|
||||||
|
return _rightHandControllerPoseInSensorFrameCache.get();
|
||||||
|
}
|
||||||
|
|
||||||
controller::Pose MyAvatar::getLeftHandControllerPoseInWorldFrame() const {
|
controller::Pose MyAvatar::getLeftHandControllerPoseInWorldFrame() const {
|
||||||
return _leftHandControllerPoseInWorldFrameCache.get();
|
return _leftHandControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix());
|
||||||
}
|
}
|
||||||
|
|
||||||
controller::Pose MyAvatar::getRightHandControllerPoseInWorldFrame() const {
|
controller::Pose MyAvatar::getRightHandControllerPoseInWorldFrame() const {
|
||||||
return _rightHandControllerPoseInWorldFrameCache.get();
|
return _rightHandControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix());
|
||||||
}
|
}
|
||||||
|
|
||||||
controller::Pose MyAvatar::getLeftHandControllerPoseInAvatarFrame() const {
|
controller::Pose MyAvatar::getLeftHandControllerPoseInAvatarFrame() const {
|
||||||
|
|
|
@ -247,7 +247,9 @@ public:
|
||||||
|
|
||||||
virtual void rebuildCollisionShape() override;
|
virtual void rebuildCollisionShape() override;
|
||||||
|
|
||||||
void setHandControllerPosesInWorldFrame(const controller::Pose& left, const controller::Pose& right);
|
void setHandControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right);
|
||||||
|
controller::Pose getLeftHandControllerPoseInSensorFrame() const;
|
||||||
|
controller::Pose getRightHandControllerPoseInSensorFrame() const;
|
||||||
controller::Pose getLeftHandControllerPoseInWorldFrame() const;
|
controller::Pose getLeftHandControllerPoseInWorldFrame() const;
|
||||||
controller::Pose getRightHandControllerPoseInWorldFrame() const;
|
controller::Pose getRightHandControllerPoseInWorldFrame() const;
|
||||||
controller::Pose getLeftHandControllerPoseInAvatarFrame() const;
|
controller::Pose getLeftHandControllerPoseInAvatarFrame() const;
|
||||||
|
@ -451,9 +453,9 @@ private:
|
||||||
bool _hoverReferenceCameraFacingIsCaptured { false };
|
bool _hoverReferenceCameraFacingIsCaptured { false };
|
||||||
glm::vec3 _hoverReferenceCameraFacing { 0.0f, 0.0f, -1.0f }; // hmd sensor space
|
glm::vec3 _hoverReferenceCameraFacing { 0.0f, 0.0f, -1.0f }; // hmd sensor space
|
||||||
|
|
||||||
// These are stored in WORLD frame
|
// These are stored in SENSOR frame
|
||||||
ThreadSafeValueCache<controller::Pose> _leftHandControllerPoseInWorldFrameCache { controller::Pose() };
|
ThreadSafeValueCache<controller::Pose> _leftHandControllerPoseInSensorFrameCache { controller::Pose() };
|
||||||
ThreadSafeValueCache<controller::Pose> _rightHandControllerPoseInWorldFrameCache { controller::Pose() };
|
ThreadSafeValueCache<controller::Pose> _rightHandControllerPoseInSensorFrameCache { controller::Pose() };
|
||||||
|
|
||||||
float AVATAR_MOVEMENT_ENERGY_CONSTANT { 0.001f };
|
float AVATAR_MOVEMENT_ENERGY_CONSTANT { 0.001f };
|
||||||
float AUDIO_ENERGY_CONSTANT { 0.000001f };
|
float AUDIO_ENERGY_CONSTANT { 0.000001f };
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include "AnimationLogging.h"
|
#include "AnimationLogging.h"
|
||||||
#include "AnimUtil.h"
|
#include "AnimUtil.h"
|
||||||
|
|
||||||
bool AnimClip::usePreAndPostPoseFromAnim = false;
|
bool AnimClip::usePreAndPostPoseFromAnim = true;
|
||||||
|
|
||||||
AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag) :
|
AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag) :
|
||||||
AnimNode(AnimNode::Type::Clip, id),
|
AnimNode(AnimNode::Type::Clip, id),
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <GeometryUtil.h>
|
#include <GeometryUtil.h>
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
#include <DebugDraw.h>
|
#include <DebugDraw.h>
|
||||||
|
#include <shared/NsightHelpers.h>
|
||||||
|
|
||||||
#include "AnimationLogging.h"
|
#include "AnimationLogging.h"
|
||||||
#include "AnimClip.h"
|
#include "AnimClip.h"
|
||||||
|
@ -852,6 +853,8 @@ void Rig::updateAnimationStateHandlers() { // called on avatar update thread (wh
|
||||||
|
|
||||||
void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
|
void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
|
||||||
|
|
||||||
|
PROFILE_RANGE_EX(__FUNCTION__, 0xffff00ff, 0);
|
||||||
|
|
||||||
setModelOffset(rootTransform);
|
setModelOffset(rootTransform);
|
||||||
|
|
||||||
if (_animNode) {
|
if (_animNode) {
|
||||||
|
|
|
@ -565,10 +565,10 @@ void AudioClient::updateReverbOptions() {
|
||||||
_zoneReverbOptions.setReverbTime(_receivedAudioStream.getRevebTime());
|
_zoneReverbOptions.setReverbTime(_receivedAudioStream.getRevebTime());
|
||||||
reverbChanged = true;
|
reverbChanged = true;
|
||||||
}
|
}
|
||||||
//if (_zoneReverbOptions.getWetLevel() != _receivedAudioStream.getWetLevel()) {
|
if (_zoneReverbOptions.getWetDryMix() != _receivedAudioStream.getWetLevel()) {
|
||||||
// _zoneReverbOptions.setWetLevel(_receivedAudioStream.getWetLevel());
|
_zoneReverbOptions.setWetDryMix(_receivedAudioStream.getWetLevel());
|
||||||
// reverbChanged = true;
|
reverbChanged = true;
|
||||||
//}
|
}
|
||||||
|
|
||||||
if (_reverbOptions != &_zoneReverbOptions) {
|
if (_reverbOptions != &_zoneReverbOptions) {
|
||||||
_reverbOptions = &_zoneReverbOptions;
|
_reverbOptions = &_zoneReverbOptions;
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
#include <gl/GLWidget.h>
|
#include <gl/GLWidget.h>
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
|
#include <shared/NsightHelpers.h>
|
||||||
#include <plugins/PluginContainer.h>
|
#include <plugins/PluginContainer.h>
|
||||||
#include <gl/Config.h>
|
#include <gl/Config.h>
|
||||||
#include <gl/GLEscrow.h>
|
#include <gl/GLEscrow.h>
|
||||||
|
@ -404,7 +404,11 @@ void OpenGLDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overla
|
||||||
|
|
||||||
void OpenGLDisplayPlugin::updateTextures() {
|
void OpenGLDisplayPlugin::updateTextures() {
|
||||||
// FIXME intrduce a GPU wait instead of a CPU/GPU sync point?
|
// FIXME intrduce a GPU wait instead of a CPU/GPU sync point?
|
||||||
|
#if THREADED_PRESENT
|
||||||
if (_sceneTextureEscrow.fetchSignaledAndRelease(_currentSceneTexture)) {
|
if (_sceneTextureEscrow.fetchSignaledAndRelease(_currentSceneTexture)) {
|
||||||
|
#else
|
||||||
|
if (_sceneTextureEscrow.fetchAndReleaseWithGpuWait(_currentSceneTexture)) {
|
||||||
|
#endif
|
||||||
updateFrameData();
|
updateFrameData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -527,6 +531,9 @@ void OpenGLDisplayPlugin::internalPresent() {
|
||||||
|
|
||||||
void OpenGLDisplayPlugin::present() {
|
void OpenGLDisplayPlugin::present() {
|
||||||
incrementPresentCount();
|
incrementPresentCount();
|
||||||
|
|
||||||
|
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount())
|
||||||
|
|
||||||
updateTextures();
|
updateTextures();
|
||||||
if (_currentSceneTexture) {
|
if (_currentSceneTexture) {
|
||||||
// Write all layers to a local framebuffer
|
// Write all layers to a local framebuffer
|
||||||
|
|
|
@ -17,9 +17,9 @@
|
||||||
#include <GLMHelpers.h>
|
#include <GLMHelpers.h>
|
||||||
#include <SimpleMovingAverage.h>
|
#include <SimpleMovingAverage.h>
|
||||||
#include <gl/OglplusHelpers.h>
|
#include <gl/OglplusHelpers.h>
|
||||||
#include <gl/GLEscrow.h>
|
|
||||||
|
|
||||||
#define THREADED_PRESENT 1
|
#define THREADED_PRESENT 1
|
||||||
|
#include <gl/GLEscrow.h>
|
||||||
|
|
||||||
class OpenGLDisplayPlugin : public DisplayPlugin {
|
class OpenGLDisplayPlugin : public DisplayPlugin {
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <gpu/GLBackend.h>
|
#include <gpu/GLBackend.h>
|
||||||
#include <CursorManager.h>
|
#include <CursorManager.h>
|
||||||
#include <gl/GLWidget.h>
|
#include <gl/GLWidget.h>
|
||||||
|
#include <shared/NsightHelpers.h>
|
||||||
|
|
||||||
#include "../Logging.h"
|
#include "../Logging.h"
|
||||||
#include "../CompositorHelper.h"
|
#include "../CompositorHelper.h"
|
||||||
|
@ -106,6 +107,9 @@ void HmdDisplayPlugin::compositePointer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HmdDisplayPlugin::internalPresent() {
|
void HmdDisplayPlugin::internalPresent() {
|
||||||
|
|
||||||
|
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount())
|
||||||
|
|
||||||
// Composite together the scene, overlay and mouse cursor
|
// Composite together the scene, overlay and mouse cursor
|
||||||
hmdPresent();
|
hmdPresent();
|
||||||
|
|
||||||
|
@ -149,6 +153,8 @@ void HmdDisplayPlugin::internalPresent() {
|
||||||
});
|
});
|
||||||
swapBuffers();
|
swapBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
postPreview();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HmdDisplayPlugin::setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) {
|
void HmdDisplayPlugin::setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) {
|
||||||
|
|
|
@ -31,6 +31,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
virtual void hmdPresent() = 0;
|
virtual void hmdPresent() = 0;
|
||||||
virtual bool isHmdMounted() const = 0;
|
virtual bool isHmdMounted() const = 0;
|
||||||
|
virtual void postPreview() {};
|
||||||
|
|
||||||
void internalActivate() override;
|
void internalActivate() override;
|
||||||
void compositeOverlay() override;
|
void compositeOverlay() override;
|
||||||
|
|
|
@ -113,14 +113,18 @@ QVariantMap RenderableModelEntityItem::parseTexturesToMap(QString textures) {
|
||||||
return _originalTexturesMap;
|
return _originalTexturesMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString jsonTextures = "{\"" + textures.replace(":\"", "\":\"").replace(",\n", ",\"") + "}";
|
// Legacy: a ,\n-delimited list of filename:"texturepath"
|
||||||
|
if (*textures.cbegin() != '{') {
|
||||||
|
textures = "{\"" + textures.replace(":\"", "\":\"").replace(",\n", ",\"") + "}";
|
||||||
|
}
|
||||||
|
|
||||||
QJsonParseError error;
|
QJsonParseError error;
|
||||||
QJsonDocument texturesAsJson = QJsonDocument::fromJson(jsonTextures.toUtf8(), &error);
|
QJsonDocument texturesJson = QJsonDocument::fromJson(textures.toUtf8(), &error);
|
||||||
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 _originalTexturesMap;
|
||||||
}
|
}
|
||||||
QJsonObject texturesAsJsonObject = texturesAsJson.object();
|
return texturesJson.object().toVariantMap();
|
||||||
return texturesAsJsonObject.toVariantMap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderableModelEntityItem::remapTextures() {
|
void RenderableModelEntityItem::remapTextures() {
|
||||||
|
|
|
@ -38,12 +38,6 @@ public:
|
||||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||||
bool& somethingChanged) override;
|
bool& somethingChanged) override;
|
||||||
|
|
||||||
virtual void somethingChangedNotification() override {
|
|
||||||
// FIX ME: this is overly aggressive. We only really need to simulate() if something about
|
|
||||||
// the world space transform has changed and/or if some animation is occurring.
|
|
||||||
_needsInitialSimulation = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool readyToAddToScene(RenderArgs* renderArgs = nullptr);
|
virtual bool readyToAddToScene(RenderArgs* renderArgs = nullptr);
|
||||||
virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) override;
|
virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) override;
|
||||||
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) override;
|
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) override;
|
||||||
|
|
|
@ -516,7 +516,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
||||||
EntityTreePointer tree = getTree();
|
EntityTreePointer tree = getTree();
|
||||||
if (tree && tree->isDeletedEntity(_id)) {
|
if (tree && tree->isDeletedEntity(_id)) {
|
||||||
#ifdef WANT_DEBUG
|
#ifdef WANT_DEBUG
|
||||||
qDebug() << "Recieved packet for previously deleted entity [" << _id << "] ignoring. "
|
qDebug() << "Received packet for previously deleted entity [" << _id << "] ignoring. "
|
||||||
"(inside " << __FUNCTION__ << ")";
|
"(inside " << __FUNCTION__ << ")";
|
||||||
#endif
|
#endif
|
||||||
ignoreServerPacket = true;
|
ignoreServerPacket = true;
|
||||||
|
|
|
@ -956,8 +956,10 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
|
||||||
entityItem->recordCreationTime();
|
entityItem->recordCreationTime();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "Recieved packet for previously deleted entity [" <<
|
#ifdef WANT_DEBUG
|
||||||
|
qDebug() << "Received packet for previously deleted entity [" <<
|
||||||
entityItem->getID() << "] ignoring. (inside " << __FUNCTION__ << ")";
|
entityItem->getID() << "] ignoring. (inside " << __FUNCTION__ << ")";
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1102,8 +1102,9 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
||||||
diffuseTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
diffuseTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
||||||
} else if (type.contains("tex_color_map")) {
|
} else if (type.contains("tex_color_map")) {
|
||||||
diffuseTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
diffuseTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
||||||
} else if (type.contains("transparentcolor")) { // it should be TransparentColor...
|
} else if (type.contains("transparentcolor")) { // Maya way of passing TransparentMap
|
||||||
// THis is how Maya assign a texture that affect diffuse color AND transparency ?
|
transparentTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
||||||
|
} else if (type.contains("transparencyfactor")) { // Blender way of passing TransparentMap
|
||||||
transparentTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
transparentTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
||||||
} else if (type.contains("bump")) {
|
} else if (type.contains("bump")) {
|
||||||
bumpTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
bumpTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
||||||
|
|
|
@ -102,16 +102,20 @@ void FBXReader::consolidateFBXMaterials() {
|
||||||
detectDifferentUVs = (diffuseTexture.texcoordSet != 0) || (!diffuseTexture.transform.isIdentity());
|
detectDifferentUVs = (diffuseTexture.texcoordSet != 0) || (!diffuseTexture.transform.isIdentity());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FBXTexture transparentTexture;
|
FBXTexture transparentTexture;
|
||||||
QString transparentTextureID = transparentTextures.value(material.materialID);
|
QString transparentTextureID = transparentTextures.value(material.materialID);
|
||||||
|
// If PBS Material, systematically bind the albedo texture as transparency texture and check for the alpha channel
|
||||||
|
if (material.isPBSMaterial) {
|
||||||
|
transparentTextureID = diffuseTextureID;
|
||||||
|
}
|
||||||
if (!transparentTextureID.isNull()) {
|
if (!transparentTextureID.isNull()) {
|
||||||
transparentTexture = getTexture(transparentTextureID);
|
transparentTexture = getTexture(transparentTextureID);
|
||||||
|
|
||||||
material.opacityTexture = transparentTexture;
|
material.opacityTexture = transparentTexture;
|
||||||
detectDifferentUVs |= (transparentTexture.texcoordSet != 0) || (!transparentTexture.transform.isIdentity());
|
detectDifferentUVs |= (transparentTexture.texcoordSet != 0) || (!transparentTexture.transform.isIdentity());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
FBXTexture normalTexture;
|
FBXTexture normalTexture;
|
||||||
QString bumpTextureID = bumpTextures.value(material.materialID);
|
QString bumpTextureID = bumpTextures.value(material.materialID);
|
||||||
QString normalTextureID = normalTextures.value(material.materialID);
|
QString normalTextureID = normalTextures.value(material.materialID);
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
|
#include <Finally.h>
|
||||||
|
|
||||||
#include "OffscreenGLCanvas.h"
|
#include "OffscreenGLCanvas.h"
|
||||||
#include "GLEscrow.h"
|
#include "GLEscrow.h"
|
||||||
|
@ -84,6 +85,7 @@ protected:
|
||||||
Queue _queue;
|
Queue _queue;
|
||||||
QMutex _mutex;
|
QMutex _mutex;
|
||||||
QWaitCondition _waitCondition;
|
QWaitCondition _waitCondition;
|
||||||
|
std::atomic<bool> _rendering { false };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Event-driven methods
|
// Event-driven methods
|
||||||
|
@ -271,15 +273,25 @@ void OffscreenQmlRenderThread::resize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OffscreenQmlRenderThread::render() {
|
void OffscreenQmlRenderThread::render() {
|
||||||
if (_surface->_paused) {
|
// Ensure we always release the main thread
|
||||||
|
Finally releaseMainThread([this] {
|
||||||
_waitCondition.wakeOne();
|
_waitCondition.wakeOne();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (_surface->_paused) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QMutexLocker locker(&_mutex);
|
_rendering = true;
|
||||||
_renderControl->sync();
|
Finally unmarkRenderingFlag([this] {
|
||||||
_waitCondition.wakeOne();
|
_rendering = false;
|
||||||
locker.unlock();
|
});
|
||||||
|
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&_mutex);
|
||||||
|
_renderControl->sync();
|
||||||
|
releaseMainThread.trigger();
|
||||||
|
}
|
||||||
|
|
||||||
using namespace oglplus;
|
using namespace oglplus;
|
||||||
|
|
||||||
|
@ -292,6 +304,7 @@ void OffscreenQmlRenderThread::render() {
|
||||||
_fbo->AttachTexture(Framebuffer::Target::Draw, FramebufferAttachment::Color, *texture, 0);
|
_fbo->AttachTexture(Framebuffer::Target::Draw, FramebufferAttachment::Color, *texture, 0);
|
||||||
_fbo->Complete(Framebuffer::Target::Draw);
|
_fbo->Complete(Framebuffer::Target::Draw);
|
||||||
{
|
{
|
||||||
|
PROFILE_RANGE("qml_render->rendercontrol")
|
||||||
_renderControl->render();
|
_renderControl->render();
|
||||||
// FIXME The web browsers seem to be leaving GL in an error state.
|
// FIXME The web browsers seem to be leaving GL in an error state.
|
||||||
// Need a debug context with sync logging to figure out why.
|
// Need a debug context with sync logging to figure out why.
|
||||||
|
@ -380,8 +393,6 @@ void OffscreenQmlSurface::resize(const QSize& newSize_) {
|
||||||
std::max(static_cast<int>(scale * newSize.height()), 10));
|
std::max(static_cast<int>(scale * newSize.height()), 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
QSize currentSize = _renderer->_quickWindow->geometry().size();
|
QSize currentSize = _renderer->_quickWindow->geometry().size();
|
||||||
if (newSize == currentSize) {
|
if (newSize == currentSize) {
|
||||||
return;
|
return;
|
||||||
|
@ -492,7 +503,12 @@ QObject* OffscreenQmlSurface::finishQmlLoad(std::function<void(QQmlContext*, QOb
|
||||||
}
|
}
|
||||||
|
|
||||||
void OffscreenQmlSurface::updateQuick() {
|
void OffscreenQmlSurface::updateQuick() {
|
||||||
if (!_renderer || !_renderer->allowNewFrame(_maxFps)) {
|
// If we're
|
||||||
|
// a) not set up
|
||||||
|
// b) already rendering a frame
|
||||||
|
// c) rendering too fast
|
||||||
|
// then skip this
|
||||||
|
if (!_renderer || _renderer->_rendering || !_renderer->allowNewFrame(_maxFps)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,11 +518,11 @@ void OffscreenQmlSurface::updateQuick() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_render) {
|
if (_render) {
|
||||||
|
PROFILE_RANGE(__FUNCTION__);
|
||||||
// Lock the GUI size while syncing
|
// Lock the GUI size while syncing
|
||||||
QMutexLocker locker(&(_renderer->_mutex));
|
QMutexLocker locker(&(_renderer->_mutex));
|
||||||
_renderer->_queue.add(RENDER);
|
_renderer->_queue.add(RENDER);
|
||||||
_renderer->_waitCondition.wait(&(_renderer->_mutex));
|
_renderer->_waitCondition.wait(&(_renderer->_mutex));
|
||||||
|
|
||||||
_render = false;
|
_render = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -139,7 +139,7 @@ bool NetworkGeometry::isLoadedWithTextures() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_isLoadedWithTextures) {
|
if (!_isLoadedWithTextures) {
|
||||||
_hasTransparentTextures = true;
|
_hasTransparentTextures = false;
|
||||||
|
|
||||||
for (auto&& material : _materials) {
|
for (auto&& material : _materials) {
|
||||||
if ((material->albedoTexture && !material->albedoTexture->isLoaded()) ||
|
if ((material->albedoTexture && !material->albedoTexture->isLoaded()) ||
|
||||||
|
@ -152,12 +152,11 @@ bool NetworkGeometry::isLoadedWithTextures() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (material->albedoTexture && material->albedoTexture->getGPUTexture()) {
|
if (material->albedoTexture && material->albedoTexture->getGPUTexture()) {
|
||||||
// Reset the materialKey transparentTexture key only, as it is albedoTexture-dependent
|
// Reassign the texture to make sure that itsalbedo alpha channel material key is detected correctly
|
||||||
|
material->_material->setTextureMap(model::MaterialKey::ALBEDO_MAP, material->_material->getTextureMap(model::MaterialKey::ALBEDO_MAP));
|
||||||
const auto& usage = material->albedoTexture->getGPUTexture()->getUsage();
|
const auto& usage = material->albedoTexture->getGPUTexture()->getUsage();
|
||||||
bool isTransparentTexture = usage.isAlpha() && !usage.isAlphaMask();
|
bool isTransparentTexture = usage.isAlpha() && !usage.isAlphaMask();
|
||||||
material->_material->setTransparentTexture(isTransparentTexture);
|
_hasTransparentTextures |= isTransparentTexture;
|
||||||
// FIXME: Materials with *some* transparent textures seem to give all *other* textures alphas of 0.
|
|
||||||
_hasTransparentTextures = isTransparentTexture && _hasTransparentTextures;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,9 +176,9 @@ void NetworkGeometry::setTextureWithNameToURL(const QString& name, const QUrl& u
|
||||||
|
|
||||||
auto albedoMap = model::TextureMapPointer(new model::TextureMap());
|
auto albedoMap = model::TextureMapPointer(new model::TextureMap());
|
||||||
albedoMap->setTextureSource(material->albedoTexture->_textureSource);
|
albedoMap->setTextureSource(material->albedoTexture->_textureSource);
|
||||||
albedoMap->setTextureTransform(
|
albedoMap->setTextureTransform(oldTextureMaps[model::MaterialKey::ALBEDO_MAP]->getTextureTransform());
|
||||||
oldTextureMaps[model::MaterialKey::ALBEDO_MAP]->getTextureTransform());
|
// when reassigning the albedo texture we also check for the alpha channel used as opacity
|
||||||
|
albedoMap->setUseAlphaChannel(true);
|
||||||
networkMaterial->setTextureMap(model::MaterialKey::ALBEDO_MAP, albedoMap);
|
networkMaterial->setTextureMap(model::MaterialKey::ALBEDO_MAP, albedoMap);
|
||||||
} else if (material->normalTextureName == name) {
|
} else if (material->normalTextureName == name) {
|
||||||
material->normalTexture = textureCache->getTexture(url);
|
material->normalTexture = textureCache->getTexture(url);
|
||||||
|
@ -212,10 +211,10 @@ void NetworkGeometry::setTextureWithNameToURL(const QString& name, const QUrl& u
|
||||||
|
|
||||||
networkMaterial->setTextureMap(model::MaterialKey::EMISSIVE_MAP, emissiveMap);
|
networkMaterial->setTextureMap(model::MaterialKey::EMISSIVE_MAP, emissiveMap);
|
||||||
} else if (material->lightmapTextureName == name) {
|
} else if (material->lightmapTextureName == name) {
|
||||||
material->emissiveTexture = textureCache->getTexture(url, LIGHTMAP_TEXTURE);
|
material->lightmapTexture = textureCache->getTexture(url, LIGHTMAP_TEXTURE);
|
||||||
|
|
||||||
auto lightmapMap = model::TextureMapPointer(new model::TextureMap());
|
auto lightmapMap = model::TextureMapPointer(new model::TextureMap());
|
||||||
lightmapMap->setTextureSource(material->emissiveTexture->_textureSource);
|
lightmapMap->setTextureSource(material->lightmapTexture->_textureSource);
|
||||||
lightmapMap->setTextureTransform(
|
lightmapMap->setTextureTransform(
|
||||||
oldTextureMaps[model::MaterialKey::LIGHTMAP_MAP]->getTextureTransform());
|
oldTextureMaps[model::MaterialKey::LIGHTMAP_MAP]->getTextureTransform());
|
||||||
glm::vec2 oldOffsetScale =
|
glm::vec2 oldOffsetScale =
|
||||||
|
@ -380,9 +379,20 @@ static NetworkMaterial* buildNetworkMaterial(NetworkGeometry* geometry, const FB
|
||||||
auto albedoMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.albedoTexture, DEFAULT_TEXTURE,
|
auto albedoMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.albedoTexture, DEFAULT_TEXTURE,
|
||||||
networkMaterial->albedoTexture, networkMaterial->albedoTextureName);
|
networkMaterial->albedoTexture, networkMaterial->albedoTextureName);
|
||||||
albedoMap->setTextureTransform(material.albedoTexture.transform);
|
albedoMap->setTextureTransform(material.albedoTexture.transform);
|
||||||
|
|
||||||
|
if (!material.opacityTexture.filename.isEmpty()) {
|
||||||
|
if (material.albedoTexture.filename == material.opacityTexture.filename) {
|
||||||
|
// Best case scenario, just indicating that the albedo map contains transparency
|
||||||
|
albedoMap->setUseAlphaChannel(true);
|
||||||
|
} else {
|
||||||
|
// Opacity Map is different from the Abledo map, not supported
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
material._material->setTextureMap(model::MaterialKey::ALBEDO_MAP, albedoMap);
|
material._material->setTextureMap(model::MaterialKey::ALBEDO_MAP, albedoMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!material.normalTexture.filename.isEmpty()) {
|
if (!material.normalTexture.filename.isEmpty()) {
|
||||||
auto normalMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.normalTexture,
|
auto normalMap = setupNetworkTextureMap(geometry, textureBaseUrl, material.normalTexture,
|
||||||
(material.normalTexture.isBumpmap ? BUMP_TEXTURE : NORMAL_TEXTURE),
|
(material.normalTexture.isBumpmap ? BUMP_TEXTURE : NORMAL_TEXTURE),
|
||||||
|
|
|
@ -199,7 +199,6 @@ public:
|
||||||
QSharedPointer<NetworkTexture> occlusionTexture;
|
QSharedPointer<NetworkTexture> occlusionTexture;
|
||||||
QString lightmapTextureName;
|
QString lightmapTextureName;
|
||||||
QSharedPointer<NetworkTexture> lightmapTexture;
|
QSharedPointer<NetworkTexture> lightmapTexture;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -320,13 +320,12 @@ void ImageReader::run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
QMetaObject::invokeMethod(texture.data(), "setImage",
|
QMetaObject::invokeMethod(texture.data(), "setImage",
|
||||||
Q_ARG(const QImage&, image),
|
|
||||||
Q_ARG(void*, theTexture),
|
Q_ARG(void*, theTexture),
|
||||||
Q_ARG(int, originalWidth), Q_ARG(int, originalHeight));
|
Q_ARG(int, originalWidth), Q_ARG(int, originalHeight));
|
||||||
QThread::currentThread()->setPriority(originalPriority);
|
QThread::currentThread()->setPriority(originalPriority);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkTexture::setImage(const QImage& image, void* voidTexture, int originalWidth,
|
void NetworkTexture::setImage(void* voidTexture, int originalWidth,
|
||||||
int originalHeight) {
|
int originalHeight) {
|
||||||
_originalWidth = originalWidth;
|
_originalWidth = originalWidth;
|
||||||
_originalHeight = originalHeight;
|
_originalHeight = originalHeight;
|
||||||
|
|
|
@ -136,7 +136,7 @@ protected:
|
||||||
|
|
||||||
Q_INVOKABLE void loadContent(const QByteArray& content);
|
Q_INVOKABLE void loadContent(const QByteArray& content);
|
||||||
// FIXME: This void* should be a gpu::Texture* but i cannot get it to work for now, moving on...
|
// FIXME: This void* should be a gpu::Texture* but i cannot get it to work for now, moving on...
|
||||||
Q_INVOKABLE void setImage(const QImage& image, void* texture, int originalWidth, int originalHeight);
|
Q_INVOKABLE void setImage(void* texture, int originalWidth, int originalHeight);
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -51,7 +51,7 @@ void Material::setEmissive(const Color& emissive, bool isSRGB) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Material::setOpacity(float opacity) {
|
void Material::setOpacity(float opacity) {
|
||||||
_key.setTransparent((opacity < 1.0f));
|
_key.setTranslucentFactor((opacity < 1.0f));
|
||||||
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
|
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
|
||||||
_schemaBuffer.edit<Schema>()._opacity = opacity;
|
_schemaBuffer.edit<Schema>()._opacity = opacity;
|
||||||
}
|
}
|
||||||
|
@ -80,19 +80,52 @@ void Material::setMetallic(float metallic) {
|
||||||
_schemaBuffer.edit<Schema>()._metallic = metallic;
|
_schemaBuffer.edit<Schema>()._metallic = metallic;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Material::setTransparentTexture(bool isTransparent) {
|
|
||||||
_key.setTransparentTexture(isTransparent);
|
|
||||||
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Material::setTextureMap(MapChannel channel, const TextureMapPointer& textureMap) {
|
void Material::setTextureMap(MapChannel channel, const TextureMapPointer& textureMap) {
|
||||||
if (textureMap) {
|
if (textureMap) {
|
||||||
_key.setMapChannel(channel, (true));
|
_key.setMapChannel(channel, (true));
|
||||||
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
|
|
||||||
|
if (channel == MaterialKey::ALBEDO_MAP) {
|
||||||
|
// clear the previous flags whatever they were:
|
||||||
|
_key.setOpacityMaskMap(false);
|
||||||
|
_key.setTranslucentMap(false);
|
||||||
|
|
||||||
|
if (textureMap->useAlphaChannel() && textureMap->isDefined() && textureMap->getTextureView().isValid()) {
|
||||||
|
auto usage = textureMap->getTextureView()._texture->getUsage();
|
||||||
|
if (usage.isAlpha()) {
|
||||||
|
// Texture has alpha, is not just a mask or a true transparent channel
|
||||||
|
if (usage.isAlphaMask()) {
|
||||||
|
_key.setOpacityMaskMap(true);
|
||||||
|
_key.setTranslucentMap(false);
|
||||||
|
} else {
|
||||||
|
_key.setOpacityMaskMap(false);
|
||||||
|
_key.setTranslucentMap(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_textureMaps[channel] = textureMap;
|
_textureMaps[channel] = textureMap;
|
||||||
} else {
|
} else {
|
||||||
_key.setMapChannel(channel, (false));
|
_key.setMapChannel(channel, (false));
|
||||||
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
|
|
||||||
|
if (channel == MaterialKey::ALBEDO_MAP) {
|
||||||
|
_key.setOpacityMaskMap(false);
|
||||||
|
_key.setTranslucentMap(false);
|
||||||
|
}
|
||||||
|
|
||||||
_textureMaps.erase(channel);
|
_textureMaps.erase(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const TextureMapPointer Material::getTextureMap(MapChannel channel) const {
|
||||||
|
auto result = _textureMaps.find(channel);
|
||||||
|
if (result != _textureMaps.end()) {
|
||||||
|
return (result->second);
|
||||||
|
} else {
|
||||||
|
return TextureMapPointer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,14 +31,15 @@ public:
|
||||||
ALBEDO_VAL_BIT,
|
ALBEDO_VAL_BIT,
|
||||||
METALLIC_VAL_BIT,
|
METALLIC_VAL_BIT,
|
||||||
GLOSSY_VAL_BIT,
|
GLOSSY_VAL_BIT,
|
||||||
TRANSPARENT_VAL_BIT,
|
OPACITY_VAL_BIT,
|
||||||
TRANSPARENT_TEX_VAL_BIT,
|
OPACITY_MASK_MAP_BIT, // OPacity Map and Opacity MASK map are mutually exclusive
|
||||||
|
OPACITY_TRANSLUCENT_MAP_BIT,
|
||||||
|
|
||||||
|
// THe map bits must be in the smae sequence as the enum names for the map channels
|
||||||
EMISSIVE_MAP_BIT,
|
EMISSIVE_MAP_BIT,
|
||||||
ALBEDO_MAP_BIT,
|
ALBEDO_MAP_BIT,
|
||||||
METALLIC_MAP_BIT,
|
METALLIC_MAP_BIT,
|
||||||
ROUGHNESS_MAP_BIT,
|
ROUGHNESS_MAP_BIT,
|
||||||
TRANSPARENT_MAP_BIT,
|
|
||||||
NORMAL_MAP_BIT,
|
NORMAL_MAP_BIT,
|
||||||
OCCLUSION_MAP_BIT,
|
OCCLUSION_MAP_BIT,
|
||||||
LIGHTMAP_MAP_BIT,
|
LIGHTMAP_MAP_BIT,
|
||||||
|
@ -52,7 +53,6 @@ public:
|
||||||
ALBEDO_MAP,
|
ALBEDO_MAP,
|
||||||
METALLIC_MAP,
|
METALLIC_MAP,
|
||||||
ROUGHNESS_MAP,
|
ROUGHNESS_MAP,
|
||||||
TRANSPARENT_MAP,
|
|
||||||
NORMAL_MAP,
|
NORMAL_MAP,
|
||||||
OCCLUSION_MAP,
|
OCCLUSION_MAP,
|
||||||
LIGHTMAP_MAP,
|
LIGHTMAP_MAP,
|
||||||
|
@ -77,13 +77,15 @@ public:
|
||||||
Builder& withAlbedo() { _flags.set(ALBEDO_VAL_BIT); return (*this); }
|
Builder& withAlbedo() { _flags.set(ALBEDO_VAL_BIT); return (*this); }
|
||||||
Builder& withMetallic() { _flags.set(METALLIC_VAL_BIT); return (*this); }
|
Builder& withMetallic() { _flags.set(METALLIC_VAL_BIT); return (*this); }
|
||||||
Builder& withGlossy() { _flags.set(GLOSSY_VAL_BIT); return (*this); }
|
Builder& withGlossy() { _flags.set(GLOSSY_VAL_BIT); return (*this); }
|
||||||
Builder& withTransparent() { _flags.set(TRANSPARENT_VAL_BIT); return (*this); }
|
Builder& withTranslucentFactor() { _flags.set(OPACITY_VAL_BIT); return (*this); }
|
||||||
|
|
||||||
Builder& withEmissiveMap() { _flags.set(EMISSIVE_MAP_BIT); return (*this); }
|
Builder& withEmissiveMap() { _flags.set(EMISSIVE_MAP_BIT); return (*this); }
|
||||||
Builder& withAlbedoMap() { _flags.set(ALBEDO_MAP_BIT); return (*this); }
|
Builder& withAlbedoMap() { _flags.set(ALBEDO_MAP_BIT); return (*this); }
|
||||||
Builder& withMetallicMap() { _flags.set(METALLIC_MAP_BIT); return (*this); }
|
Builder& withMetallicMap() { _flags.set(METALLIC_MAP_BIT); return (*this); }
|
||||||
Builder& withRoughnessMap() { _flags.set(ROUGHNESS_MAP_BIT); return (*this); }
|
Builder& withRoughnessMap() { _flags.set(ROUGHNESS_MAP_BIT); return (*this); }
|
||||||
Builder& withTransparentMap() { _flags.set(TRANSPARENT_MAP_BIT); return (*this); }
|
|
||||||
|
Builder& withTranslucentMap() { _flags.set(OPACITY_TRANSLUCENT_MAP_BIT); return (*this); }
|
||||||
|
Builder& withMaskMap() { _flags.set(OPACITY_MASK_MAP_BIT); return (*this); }
|
||||||
|
|
||||||
Builder& withNormalMap() { _flags.set(NORMAL_MAP_BIT); return (*this); }
|
Builder& withNormalMap() { _flags.set(NORMAL_MAP_BIT); return (*this); }
|
||||||
Builder& withOcclusionMap() { _flags.set(OCCLUSION_MAP_BIT); return (*this); }
|
Builder& withOcclusionMap() { _flags.set(OCCLUSION_MAP_BIT); return (*this); }
|
||||||
|
@ -102,9 +104,6 @@ public:
|
||||||
void setAlbedo(bool value) { _flags.set(ALBEDO_VAL_BIT, value); }
|
void setAlbedo(bool value) { _flags.set(ALBEDO_VAL_BIT, value); }
|
||||||
bool isAlbedo() const { return _flags[ALBEDO_VAL_BIT]; }
|
bool isAlbedo() const { return _flags[ALBEDO_VAL_BIT]; }
|
||||||
|
|
||||||
void setTransparentTexture(bool value) { _flags.set(TRANSPARENT_TEX_VAL_BIT, value); }
|
|
||||||
bool isTransparentTexture() const { return _flags[TRANSPARENT_TEX_VAL_BIT]; }
|
|
||||||
|
|
||||||
void setAlbedoMap(bool value) { _flags.set(ALBEDO_MAP_BIT, value); }
|
void setAlbedoMap(bool value) { _flags.set(ALBEDO_MAP_BIT, value); }
|
||||||
bool isAlbedoMap() const { return _flags[ALBEDO_MAP_BIT]; }
|
bool isAlbedoMap() const { return _flags[ALBEDO_MAP_BIT]; }
|
||||||
|
|
||||||
|
@ -121,13 +120,15 @@ public:
|
||||||
void setRoughnessMap(bool value) { _flags.set(ROUGHNESS_MAP_BIT, value); }
|
void setRoughnessMap(bool value) { _flags.set(ROUGHNESS_MAP_BIT, value); }
|
||||||
bool isRoughnessMap() const { return _flags[ROUGHNESS_MAP_BIT]; }
|
bool isRoughnessMap() const { return _flags[ROUGHNESS_MAP_BIT]; }
|
||||||
|
|
||||||
void setTransparent(bool value) { _flags.set(TRANSPARENT_VAL_BIT, value); }
|
void setTranslucentFactor(bool value) { _flags.set(OPACITY_VAL_BIT, value); }
|
||||||
bool isTransparent() const { return _flags[TRANSPARENT_VAL_BIT]; }
|
bool isTranslucentFactor() const { return _flags[OPACITY_VAL_BIT]; }
|
||||||
bool isOpaque() const { return !_flags[TRANSPARENT_VAL_BIT]; }
|
|
||||||
|
|
||||||
void setTransparentMap(bool value) { _flags.set(TRANSPARENT_MAP_BIT, value); }
|
void setTranslucentMap(bool value) { _flags.set(OPACITY_TRANSLUCENT_MAP_BIT, value); }
|
||||||
bool isTransparentMap() const { return _flags[TRANSPARENT_MAP_BIT]; }
|
bool isTranslucentMap() const { return _flags[OPACITY_TRANSLUCENT_MAP_BIT]; }
|
||||||
|
|
||||||
|
void setOpacityMaskMap(bool value) { _flags.set(OPACITY_MASK_MAP_BIT, value); }
|
||||||
|
bool isOpacityMaskMap() const { return _flags[OPACITY_MASK_MAP_BIT]; }
|
||||||
|
|
||||||
void setNormalMap(bool value) { _flags.set(NORMAL_MAP_BIT, value); }
|
void setNormalMap(bool value) { _flags.set(NORMAL_MAP_BIT, value); }
|
||||||
bool isNormalMap() const { return _flags[NORMAL_MAP_BIT]; }
|
bool isNormalMap() const { return _flags[NORMAL_MAP_BIT]; }
|
||||||
|
|
||||||
|
@ -140,6 +141,12 @@ public:
|
||||||
void setMapChannel(MapChannel channel, bool value) { _flags.set(EMISSIVE_MAP_BIT + channel, value); }
|
void setMapChannel(MapChannel channel, bool value) { _flags.set(EMISSIVE_MAP_BIT + channel, value); }
|
||||||
bool isMapChannel(MapChannel channel) const { return _flags[EMISSIVE_MAP_BIT + channel]; }
|
bool isMapChannel(MapChannel channel) const { return _flags[EMISSIVE_MAP_BIT + channel]; }
|
||||||
|
|
||||||
|
|
||||||
|
// Translucency and Opacity Heuristics are combining several flags:
|
||||||
|
bool isTranslucent() const { return isTranslucentFactor() || isTranslucentMap(); }
|
||||||
|
bool isOpaque() const { return !isTranslucent(); }
|
||||||
|
bool isSurfaceOpaque() const { return isOpaque() && !isOpacityMaskMap(); }
|
||||||
|
bool isTexelOpaque() const { return isOpaque() && isOpacityMaskMap(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -168,9 +175,6 @@ public:
|
||||||
Builder& withoutAlbedo() { _value.reset(MaterialKey::ALBEDO_VAL_BIT); _mask.set(MaterialKey::ALBEDO_VAL_BIT); return (*this); }
|
Builder& withoutAlbedo() { _value.reset(MaterialKey::ALBEDO_VAL_BIT); _mask.set(MaterialKey::ALBEDO_VAL_BIT); return (*this); }
|
||||||
Builder& withAlbedo() { _value.set(MaterialKey::ALBEDO_VAL_BIT); _mask.set(MaterialKey::ALBEDO_VAL_BIT); return (*this); }
|
Builder& withAlbedo() { _value.set(MaterialKey::ALBEDO_VAL_BIT); _mask.set(MaterialKey::ALBEDO_VAL_BIT); return (*this); }
|
||||||
|
|
||||||
Builder& withoutTransparentTexture() { _value.reset(MaterialKey::TRANSPARENT_TEX_VAL_BIT); _mask.set(MaterialKey::TRANSPARENT_TEX_VAL_BIT); return (*this); }
|
|
||||||
Builder& withTransparentTexture() { _value.set(MaterialKey::TRANSPARENT_TEX_VAL_BIT); _mask.set(MaterialKey::TRANSPARENT_TEX_VAL_BIT); return (*this); }
|
|
||||||
|
|
||||||
Builder& withoutAlbedoMap() { _value.reset(MaterialKey::ALBEDO_MAP_BIT); _mask.set(MaterialKey::ALBEDO_MAP_BIT); return (*this); }
|
Builder& withoutAlbedoMap() { _value.reset(MaterialKey::ALBEDO_MAP_BIT); _mask.set(MaterialKey::ALBEDO_MAP_BIT); return (*this); }
|
||||||
Builder& withAlbedoMap() { _value.set(MaterialKey::ALBEDO_MAP_BIT); _mask.set(MaterialKey::ALBEDO_MAP_BIT); return (*this); }
|
Builder& withAlbedoMap() { _value.set(MaterialKey::ALBEDO_MAP_BIT); _mask.set(MaterialKey::ALBEDO_MAP_BIT); return (*this); }
|
||||||
|
|
||||||
|
@ -186,11 +190,14 @@ public:
|
||||||
Builder& withoutRoughnessMap() { _value.reset(MaterialKey::ROUGHNESS_MAP_BIT); _mask.set(MaterialKey::ROUGHNESS_MAP_BIT); return (*this); }
|
Builder& withoutRoughnessMap() { _value.reset(MaterialKey::ROUGHNESS_MAP_BIT); _mask.set(MaterialKey::ROUGHNESS_MAP_BIT); return (*this); }
|
||||||
Builder& withRoughnessMap() { _value.set(MaterialKey::ROUGHNESS_MAP_BIT); _mask.set(MaterialKey::ROUGHNESS_MAP_BIT); return (*this); }
|
Builder& withRoughnessMap() { _value.set(MaterialKey::ROUGHNESS_MAP_BIT); _mask.set(MaterialKey::ROUGHNESS_MAP_BIT); return (*this); }
|
||||||
|
|
||||||
Builder& withoutTransparent() { _value.reset(MaterialKey::TRANSPARENT_VAL_BIT); _mask.set(MaterialKey::TRANSPARENT_VAL_BIT); return (*this); }
|
Builder& withoutTranslucentFactor() { _value.reset(MaterialKey::OPACITY_VAL_BIT); _mask.set(MaterialKey::OPACITY_VAL_BIT); return (*this); }
|
||||||
Builder& withTransparent() { _value.set(MaterialKey::TRANSPARENT_VAL_BIT); _mask.set(MaterialKey::TRANSPARENT_VAL_BIT); return (*this); }
|
Builder& withTranslucentFactor() { _value.set(MaterialKey::OPACITY_VAL_BIT); _mask.set(MaterialKey::OPACITY_VAL_BIT); return (*this); }
|
||||||
|
|
||||||
Builder& withoutTransparentMap() { _value.reset(MaterialKey::TRANSPARENT_MAP_BIT); _mask.set(MaterialKey::TRANSPARENT_MAP_BIT); return (*this); }
|
Builder& withoutTranslucentMap() { _value.reset(MaterialKey::OPACITY_TRANSLUCENT_MAP_BIT); _mask.set(MaterialKey::OPACITY_TRANSLUCENT_MAP_BIT); return (*this); }
|
||||||
Builder& withTransparentMap() { _value.set(MaterialKey::TRANSPARENT_MAP_BIT); _mask.set(MaterialKey::TRANSPARENT_MAP_BIT); return (*this); }
|
Builder& withTranslucentMap() { _value.set(MaterialKey::OPACITY_TRANSLUCENT_MAP_BIT); _mask.set(MaterialKey::OPACITY_TRANSLUCENT_MAP_BIT); return (*this); }
|
||||||
|
|
||||||
|
Builder& withoutMaskMap() { _value.reset(MaterialKey::OPACITY_MASK_MAP_BIT); _mask.set(MaterialKey::OPACITY_MASK_MAP_BIT); return (*this); }
|
||||||
|
Builder& withMaskMap() { _value.set(MaterialKey::OPACITY_MASK_MAP_BIT); _mask.set(MaterialKey::OPACITY_MASK_MAP_BIT); return (*this); }
|
||||||
|
|
||||||
Builder& withoutNormalMap() { _value.reset(MaterialKey::NORMAL_MAP_BIT); _mask.set(MaterialKey::NORMAL_MAP_BIT); return (*this); }
|
Builder& withoutNormalMap() { _value.reset(MaterialKey::NORMAL_MAP_BIT); _mask.set(MaterialKey::NORMAL_MAP_BIT); return (*this); }
|
||||||
Builder& withNormalMap() { _value.set(MaterialKey::NORMAL_MAP_BIT); _mask.set(MaterialKey::NORMAL_MAP_BIT); return (*this); }
|
Builder& withNormalMap() { _value.set(MaterialKey::NORMAL_MAP_BIT); _mask.set(MaterialKey::NORMAL_MAP_BIT); return (*this); }
|
||||||
|
@ -202,7 +209,7 @@ public:
|
||||||
Builder& withLightmapMap() { _value.set(MaterialKey::LIGHTMAP_MAP_BIT); _mask.set(MaterialKey::LIGHTMAP_MAP_BIT); return (*this); }
|
Builder& withLightmapMap() { _value.set(MaterialKey::LIGHTMAP_MAP_BIT); _mask.set(MaterialKey::LIGHTMAP_MAP_BIT); return (*this); }
|
||||||
|
|
||||||
// Convenient standard keys that we will keep on using all over the place
|
// Convenient standard keys that we will keep on using all over the place
|
||||||
static MaterialFilter opaqueAlbedo() { return Builder().withAlbedo().withoutTransparent().build(); }
|
static MaterialFilter opaqueAlbedo() { return Builder().withAlbedo().withoutTranslucentFactor().build(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Item Filter operator testing if a key pass the filter
|
// Item Filter operator testing if a key pass the filter
|
||||||
|
@ -255,7 +262,6 @@ public:
|
||||||
void setRoughness(float roughness);
|
void setRoughness(float roughness);
|
||||||
float getRoughness() const { return _schemaBuffer.get<Schema>()._roughness; }
|
float getRoughness() const { return _schemaBuffer.get<Schema>()._roughness; }
|
||||||
|
|
||||||
void setTransparentTexture(bool isTransparent);
|
|
||||||
|
|
||||||
// Schema to access the attribute values of the material
|
// Schema to access the attribute values of the material
|
||||||
class Schema {
|
class Schema {
|
||||||
|
@ -283,6 +289,7 @@ public:
|
||||||
// The texture map to channel association
|
// The texture map to channel association
|
||||||
void setTextureMap(MapChannel channel, const TextureMapPointer& textureMap);
|
void setTextureMap(MapChannel channel, const TextureMapPointer& textureMap);
|
||||||
const TextureMaps& getTextureMaps() const { return _textureMaps; }
|
const TextureMaps& getTextureMaps() const { return _textureMaps; }
|
||||||
|
const TextureMapPointer getTextureMap(MapChannel channel) const;
|
||||||
|
|
||||||
// conversion from legacy material properties to PBR equivalent
|
// conversion from legacy material properties to PBR equivalent
|
||||||
static float shininessToRoughness(float shininess) { return 1.0f - shininess / 100.0f; }
|
static float shininessToRoughness(float shininess) { return 1.0f - shininess / 100.0f; }
|
||||||
|
|
|
@ -39,21 +39,21 @@ float getMaterialShininess(Material m) { return 1.0 - getMaterialRoughness(m); }
|
||||||
|
|
||||||
int getMaterialKey(Material m) { return floatBitsToInt(m._spareKey.w); }
|
int getMaterialKey(Material m) { return floatBitsToInt(m._spareKey.w); }
|
||||||
|
|
||||||
const int EMISSIVE_VAL_BIT = 0x00000001;
|
const int EMISSIVE_VAL_BIT = 0x00000001;
|
||||||
const int ALBEDO_VAL_BIT = 0x00000002;
|
const int ALBEDO_VAL_BIT = 0x00000002;
|
||||||
const int METALLIC_VAL_BIT = 0x00000004;
|
const int METALLIC_VAL_BIT = 0x00000004;
|
||||||
const int GLOSSY_VAL_BIT = 0x00000008;
|
const int GLOSSY_VAL_BIT = 0x00000008;
|
||||||
const int TRANSPARENT_VAL_BIT = 0x00000010;
|
const int OPACITY_VAL_BIT = 0x00000010;
|
||||||
const int TRANSPARENT_TEX_VAL_BIT = 0x00000020;
|
const int OPACITY_MASK_MAP_BIT = 0x00000020;
|
||||||
|
const int OPACITY_TRANSLUCENT_MAP_BIT = 0x00000040;
|
||||||
|
|
||||||
const int EMISSIVE_MAP_BIT = 0x00000040;
|
const int EMISSIVE_MAP_BIT = 0x00000080;
|
||||||
const int ALBEDO_MAP_BIT = 0x00000080;
|
const int ALBEDO_MAP_BIT = 0x00000100;
|
||||||
const int METALLIC_MAP_BIT = 0x00000100;
|
const int METALLIC_MAP_BIT = 0x00000200;
|
||||||
const int ROUGHNESS_MAP_BIT = 0x00000200;
|
const int ROUGHNESS_MAP_BIT = 0x00000400;
|
||||||
const int TRANSPARENT_MAP_BIT = 0x00000400;
|
const int NORMAL_MAP_BIT = 0x00000800;
|
||||||
const int NORMAL_MAP_BIT = 0x00000800;
|
const int OCCLUSION_MAP_BIT = 0x00001000;
|
||||||
const int OCCLUSION_MAP_BIT = 0x00001000;
|
const int LIGHTMAP_MAP_BIT = 0x00002000;
|
||||||
|
|
||||||
const int LIGHTMAP_MAP_BIT = 0x00002000;
|
|
||||||
|
|
||||||
<@endif@>
|
<@endif@>
|
||||||
|
|
|
@ -56,6 +56,9 @@ public:
|
||||||
void setTextureTransform(const Transform& texcoordTransform);
|
void setTextureTransform(const Transform& texcoordTransform);
|
||||||
const Transform& getTextureTransform() const { return _texcoordTransform; }
|
const Transform& getTextureTransform() const { return _texcoordTransform; }
|
||||||
|
|
||||||
|
void setUseAlphaChannel(bool useAlpha) { _useAlphaChannel = useAlpha; }
|
||||||
|
bool useAlphaChannel() const { return _useAlphaChannel; }
|
||||||
|
|
||||||
void setLightmapOffsetScale(float offset, float scale);
|
void setLightmapOffsetScale(float offset, float scale);
|
||||||
const glm::vec2& getLightmapOffsetScale() const { return _lightmapOffsetScale; }
|
const glm::vec2& getLightmapOffsetScale() const { return _lightmapOffsetScale; }
|
||||||
|
|
||||||
|
@ -64,6 +67,8 @@ protected:
|
||||||
|
|
||||||
Transform _texcoordTransform;
|
Transform _texcoordTransform;
|
||||||
glm::vec2 _lightmapOffsetScale{ 0.0f, 1.0f };
|
glm::vec2 _lightmapOffsetScale{ 0.0f, 1.0f };
|
||||||
|
|
||||||
|
bool _useAlphaChannel{ false };
|
||||||
};
|
};
|
||||||
typedef std::shared_ptr< TextureMap > TextureMapPointer;
|
typedef std::shared_ptr< TextureMap > TextureMapPointer;
|
||||||
|
|
||||||
|
|
|
@ -216,17 +216,13 @@ Resource* ResourceCacheSharedItems::getHighestPendingRequest() {
|
||||||
bool ResourceCache::attemptRequest(Resource* resource) {
|
bool ResourceCache::attemptRequest(Resource* resource) {
|
||||||
auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>();
|
auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>();
|
||||||
|
|
||||||
// Disable request limiting for ATP
|
if (_requestsActive >= _requestLimit) {
|
||||||
if (resource->getURL().scheme() != URL_SCHEME_ATP) {
|
// wait until a slot becomes available
|
||||||
if (_requestsActive >= _requestLimit) {
|
sharedItems->appendPendingRequest(resource);
|
||||||
// wait until a slot becomes available
|
return false;
|
||||||
sharedItems->appendPendingRequest(resource);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
++_requestsActive;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++_requestsActive;
|
||||||
sharedItems->appendActiveRequest(resource);
|
sharedItems->appendActiveRequest(resource);
|
||||||
resource->makeRequest();
|
resource->makeRequest();
|
||||||
return true;
|
return true;
|
||||||
|
@ -235,9 +231,7 @@ bool ResourceCache::attemptRequest(Resource* resource) {
|
||||||
void ResourceCache::requestCompleted(Resource* resource) {
|
void ResourceCache::requestCompleted(Resource* resource) {
|
||||||
auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>();
|
auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>();
|
||||||
sharedItems->removeRequest(resource);
|
sharedItems->removeRequest(resource);
|
||||||
if (resource->getURL().scheme() != URL_SCHEME_ATP) {
|
--_requestsActive;
|
||||||
--_requestsActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
attemptHighestPriorityRequest();
|
attemptHighestPriorityRequest();
|
||||||
}
|
}
|
||||||
|
@ -380,6 +374,7 @@ void Resource::finishedLoading(bool success) {
|
||||||
_failedToLoad = true;
|
_failedToLoad = true;
|
||||||
}
|
}
|
||||||
_loadPriorities.clear();
|
_loadPriorities.clear();
|
||||||
|
emit finished(success);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Resource::reinsert() {
|
void Resource::reinsert() {
|
||||||
|
|
|
@ -201,6 +201,9 @@ signals:
|
||||||
/// This can be used instead of downloadFinished to access data before it is processed.
|
/// This can be used instead of downloadFinished to access data before it is processed.
|
||||||
void loaded(const QByteArray& request);
|
void loaded(const QByteArray& request);
|
||||||
|
|
||||||
|
/// Fired when the resource has finished loading.
|
||||||
|
void finished(bool success);
|
||||||
|
|
||||||
/// Fired when the resource failed to load.
|
/// Fired when the resource failed to load.
|
||||||
void failed(QNetworkReply::NetworkError error);
|
void failed(QNetworkReply::NetworkError error);
|
||||||
|
|
||||||
|
@ -224,9 +227,6 @@ protected:
|
||||||
/// 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);
|
||||||
|
|
||||||
/// Reinserts this resource into the cache.
|
|
||||||
virtual void reinsert();
|
|
||||||
|
|
||||||
QUrl _url;
|
QUrl _url;
|
||||||
QUrl _activeUrl;
|
QUrl _activeUrl;
|
||||||
bool _startedLoading = false;
|
bool _startedLoading = false;
|
||||||
|
@ -246,6 +246,7 @@ private:
|
||||||
|
|
||||||
void makeRequest();
|
void makeRequest();
|
||||||
void retry();
|
void retry();
|
||||||
|
void reinsert();
|
||||||
|
|
||||||
friend class ResourceCache;
|
friend class ResourceCache;
|
||||||
|
|
||||||
|
|
|
@ -78,11 +78,15 @@ void UserActivityLogger::requestError(QNetworkReply& errorReply) {
|
||||||
qCDebug(networking) << errorReply.error() << "-" << errorReply.errorString();
|
qCDebug(networking) << errorReply.error() << "-" << errorReply.errorString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserActivityLogger::launch(QString applicationVersion) {
|
void UserActivityLogger::launch(QString applicationVersion, bool previousSessionCrashed, int previousSessionRuntime) {
|
||||||
const QString ACTION_NAME = "launch";
|
const QString ACTION_NAME = "launch";
|
||||||
QJsonObject actionDetails;
|
QJsonObject actionDetails;
|
||||||
QString VERSION_KEY = "version";
|
QString VERSION_KEY = "version";
|
||||||
|
QString CRASH_KEY = "previousSessionCrashed";
|
||||||
|
QString RUNTIME_KEY = "previousSessionRuntime";
|
||||||
actionDetails.insert(VERSION_KEY, applicationVersion);
|
actionDetails.insert(VERSION_KEY, applicationVersion);
|
||||||
|
actionDetails.insert(CRASH_KEY, previousSessionCrashed);
|
||||||
|
actionDetails.insert(RUNTIME_KEY, previousSessionRuntime);
|
||||||
|
|
||||||
logAction(ACTION_NAME, actionDetails);
|
logAction(ACTION_NAME, actionDetails);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ public slots:
|
||||||
void disable(bool disable);
|
void disable(bool disable);
|
||||||
void logAction(QString action, QJsonObject details = QJsonObject(), JSONCallbackParameters params = JSONCallbackParameters());
|
void logAction(QString action, QJsonObject details = QJsonObject(), JSONCallbackParameters params = JSONCallbackParameters());
|
||||||
|
|
||||||
void launch(QString applicationVersion);
|
void launch(QString applicationVersion, bool previousSessionCrashed, int previousSessionRuntime);
|
||||||
|
|
||||||
void changedDisplayName(QString displayName);
|
void changedDisplayName(QString displayName);
|
||||||
void changedModel(QString typeOfModel, QString modelURL);
|
void changedModel(QString typeOfModel, QString modelURL);
|
||||||
|
|
|
@ -201,7 +201,7 @@ void DefaultCC::onTimeout() {
|
||||||
|
|
||||||
void DefaultCC::stopSlowStart() {
|
void DefaultCC::stopSlowStart() {
|
||||||
_slowStart = false;
|
_slowStart = false;
|
||||||
|
|
||||||
if (_receiveRate > 0) {
|
if (_receiveRate > 0) {
|
||||||
// Set the sending rate to the receiving rate.
|
// Set the sending rate to the receiving rate.
|
||||||
setPacketSendPeriod(USECS_PER_SECOND / _receiveRate);
|
setPacketSendPeriod(USECS_PER_SECOND / _receiveRate);
|
||||||
|
|
|
@ -103,6 +103,7 @@ SendQueue& Connection::getSendQueue() {
|
||||||
QObject::connect(_sendQueue.get(), &SendQueue::packetRetransmitted, this, &Connection::recordRetransmission);
|
QObject::connect(_sendQueue.get(), &SendQueue::packetRetransmitted, this, &Connection::recordRetransmission);
|
||||||
QObject::connect(_sendQueue.get(), &SendQueue::queueInactive, this, &Connection::queueInactive);
|
QObject::connect(_sendQueue.get(), &SendQueue::queueInactive, this, &Connection::queueInactive);
|
||||||
QObject::connect(_sendQueue.get(), &SendQueue::timeout, this, &Connection::queueTimeout);
|
QObject::connect(_sendQueue.get(), &SendQueue::timeout, this, &Connection::queueTimeout);
|
||||||
|
QObject::connect(_sendQueue.get(), &SendQueue::shortCircuitLoss, this, &Connection::queueShortCircuitLoss);
|
||||||
|
|
||||||
// set defaults on the send queue from our congestion control object and estimatedTimeout()
|
// set defaults on the send queue from our congestion control object and estimatedTimeout()
|
||||||
_sendQueue->setPacketSendPeriod(_congestionControl->_packetSendPeriod);
|
_sendQueue->setPacketSendPeriod(_congestionControl->_packetSendPeriod);
|
||||||
|
@ -140,6 +141,12 @@ void Connection::queueTimeout() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Connection::queueShortCircuitLoss(quint32 sequenceNumber) {
|
||||||
|
updateCongestionControlAndSendQueue([this, sequenceNumber]{
|
||||||
|
_congestionControl->onLoss(SequenceNumber { sequenceNumber }, SequenceNumber { sequenceNumber });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void Connection::sendReliablePacket(std::unique_ptr<Packet> packet) {
|
void Connection::sendReliablePacket(std::unique_ptr<Packet> packet) {
|
||||||
Q_ASSERT_X(packet->isReliable(), "Connection::send", "Trying to send an unreliable packet reliably.");
|
Q_ASSERT_X(packet->isReliable(), "Connection::send", "Trying to send an unreliable packet reliably.");
|
||||||
getSendQueue().queuePacket(std::move(packet));
|
getSendQueue().queuePacket(std::move(packet));
|
||||||
|
|
|
@ -87,6 +87,7 @@ private slots:
|
||||||
void recordRetransmission();
|
void recordRetransmission();
|
||||||
void queueInactive();
|
void queueInactive();
|
||||||
void queueTimeout();
|
void queueTimeout();
|
||||||
|
void queueShortCircuitLoss(quint32 sequenceNumber);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void sendACK(bool wasCausedBySyncTimeout = true);
|
void sendACK(bool wasCausedBySyncTimeout = true);
|
||||||
|
|
|
@ -128,13 +128,13 @@ void SendQueue::stop() {
|
||||||
_emptyCondition.notify_one();
|
_emptyCondition.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendQueue::sendPacket(const Packet& packet) {
|
int SendQueue::sendPacket(const Packet& packet) {
|
||||||
_socket->writeDatagram(packet.getData(), packet.getDataSize(), _destination);
|
return _socket->writeDatagram(packet.getData(), packet.getDataSize(), _destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendQueue::ack(SequenceNumber ack) {
|
void SendQueue::ack(SequenceNumber ack) {
|
||||||
// this is a response from the client, re-set our timeout expiry and our last response time
|
// this is a response from the client, re-set our timeout expiry and our last response time
|
||||||
_lastReceiverResponse = uint64_t(QDateTime::currentMSecsSinceEpoch());
|
_lastReceiverResponse = QDateTime::currentMSecsSinceEpoch();
|
||||||
|
|
||||||
if (_lastACKSequenceNumber == (uint32_t) ack) {
|
if (_lastACKSequenceNumber == (uint32_t) ack) {
|
||||||
return;
|
return;
|
||||||
|
@ -164,7 +164,7 @@ void SendQueue::ack(SequenceNumber ack) {
|
||||||
|
|
||||||
void SendQueue::nak(SequenceNumber start, SequenceNumber end) {
|
void SendQueue::nak(SequenceNumber start, SequenceNumber end) {
|
||||||
// this is a response from the client, re-set our timeout expiry
|
// this is a response from the client, re-set our timeout expiry
|
||||||
_lastReceiverResponse = uint64_t(QDateTime::currentMSecsSinceEpoch());
|
_lastReceiverResponse = QDateTime::currentMSecsSinceEpoch();
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> nakLocker(_naksLock);
|
std::lock_guard<std::mutex> nakLocker(_naksLock);
|
||||||
|
@ -177,8 +177,8 @@ void SendQueue::nak(SequenceNumber start, SequenceNumber end) {
|
||||||
|
|
||||||
void SendQueue::overrideNAKListFromPacket(ControlPacket& packet) {
|
void SendQueue::overrideNAKListFromPacket(ControlPacket& packet) {
|
||||||
// this is a response from the client, re-set our timeout expiry
|
// this is a response from the client, re-set our timeout expiry
|
||||||
_lastReceiverResponse = uint64_t(QDateTime::currentMSecsSinceEpoch());
|
_lastReceiverResponse = QDateTime::currentMSecsSinceEpoch();
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> nakLocker(_naksLock);
|
std::lock_guard<std::mutex> nakLocker(_naksLock);
|
||||||
_naks.clear();
|
_naks.clear();
|
||||||
|
@ -232,15 +232,16 @@ SequenceNumber SendQueue::getNextSequenceNumber() {
|
||||||
return _currentSequenceNumber;
|
return _currentSequenceNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket, SequenceNumber sequenceNumber) {
|
bool SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket, SequenceNumber sequenceNumber) {
|
||||||
// write the sequence number and send the packet
|
// write the sequence number and send the packet
|
||||||
newPacket->writeSequenceNumber(sequenceNumber);
|
newPacket->writeSequenceNumber(sequenceNumber);
|
||||||
sendPacket(*newPacket);
|
|
||||||
|
|
||||||
// Save packet/payload size before we move it
|
// Save packet/payload size before we move it
|
||||||
auto packetSize = newPacket->getDataSize();
|
auto packetSize = newPacket->getDataSize();
|
||||||
auto payloadSize = newPacket->getPayloadSize();
|
auto payloadSize = newPacket->getPayloadSize();
|
||||||
|
|
||||||
|
auto bytesWritten = sendPacket(*newPacket);
|
||||||
|
|
||||||
{
|
{
|
||||||
// Insert the packet we have just sent in the sent list
|
// Insert the packet we have just sent in the sent list
|
||||||
QWriteLocker locker(&_sentLock);
|
QWriteLocker locker(&_sentLock);
|
||||||
|
@ -249,8 +250,24 @@ void SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket,
|
||||||
entry.second.swap(newPacket);
|
entry.second.swap(newPacket);
|
||||||
}
|
}
|
||||||
Q_ASSERT_X(!newPacket, "SendQueue::sendNewPacketAndAddToSentList()", "Overriden packet in sent list");
|
Q_ASSERT_X(!newPacket, "SendQueue::sendNewPacketAndAddToSentList()", "Overriden packet in sent list");
|
||||||
|
|
||||||
emit packetSent(packetSize, payloadSize);
|
emit packetSent(packetSize, payloadSize);
|
||||||
|
|
||||||
|
if (bytesWritten < 0) {
|
||||||
|
// this is a short-circuit loss - we failed to put this packet on the wire
|
||||||
|
// so immediately add it to the loss list
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> nakLocker(_naksLock);
|
||||||
|
_naks.append(sequenceNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit shortCircuitLoss(quint32(sequenceNumber));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendQueue::run() {
|
void SendQueue::run() {
|
||||||
|
@ -285,12 +302,14 @@ void SendQueue::run() {
|
||||||
auto nextPacketTimestamp = p_high_resolution_clock::now();
|
auto nextPacketTimestamp = p_high_resolution_clock::now();
|
||||||
|
|
||||||
while (_state == State::Running) {
|
while (_state == State::Running) {
|
||||||
bool sentAPacket = maybeResendPacket();
|
bool attemptedToSendPacket = maybeResendPacket();
|
||||||
|
|
||||||
// if we didn't find a packet to re-send AND we think we can fit a new packet on the wire
|
// if we didn't find a packet to re-send AND we think we can fit a new packet on the wire
|
||||||
// (this is according to the current flow window size) then we send out a new packet
|
// (this is according to the current flow window size) then we send out a new packet
|
||||||
if (!sentAPacket) {
|
auto newPacketCount = 0;
|
||||||
sentAPacket = maybeSendNewPacket();
|
if (!attemptedToSendPacket) {
|
||||||
|
newPacketCount = maybeSendNewPacket();
|
||||||
|
attemptedToSendPacket = (newPacketCount > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// since we're a while loop, give the thread a chance to process events
|
// since we're a while loop, give the thread a chance to process events
|
||||||
|
@ -300,12 +319,13 @@ void SendQueue::run() {
|
||||||
// If the send queue has been innactive, skip the sleep for
|
// If the send queue has been innactive, skip the sleep for
|
||||||
// Either _isRunning will have been set to false and we'll break
|
// Either _isRunning will have been set to false and we'll break
|
||||||
// Or something happened and we'll keep going
|
// Or something happened and we'll keep going
|
||||||
if (_state != State::Running || isInactive(sentAPacket)) {
|
if (_state != State::Running || isInactive(attemptedToSendPacket)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// push the next packet timestamp forwards by the current packet send period
|
// push the next packet timestamp forwards by the current packet send period
|
||||||
nextPacketTimestamp += std::chrono::microseconds(_packetSendPeriod);
|
auto nextPacketDelta = (newPacketCount == 2 ? 2 : 1) * _packetSendPeriod;
|
||||||
|
nextPacketTimestamp += std::chrono::microseconds(nextPacketDelta);
|
||||||
|
|
||||||
// sleep as long as we need until next packet send, if we can
|
// sleep as long as we need until next packet send, if we can
|
||||||
const auto timeToSleep = duration_cast<microseconds>(nextPacketTimestamp - p_high_resolution_clock::now());
|
const auto timeToSleep = duration_cast<microseconds>(nextPacketTimestamp - p_high_resolution_clock::now());
|
||||||
|
@ -314,7 +334,7 @@ void SendQueue::run() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SendQueue::maybeSendNewPacket() {
|
int SendQueue::maybeSendNewPacket() {
|
||||||
if (!isFlowWindowFull()) {
|
if (!isFlowWindowFull()) {
|
||||||
// we didn't re-send a packet, so time to send a new one
|
// we didn't re-send a packet, so time to send a new one
|
||||||
|
|
||||||
|
@ -324,38 +344,43 @@ bool SendQueue::maybeSendNewPacket() {
|
||||||
// grab the first packet we will send
|
// grab the first packet we will send
|
||||||
std::unique_ptr<Packet> firstPacket = _packets.takePacket();
|
std::unique_ptr<Packet> firstPacket = _packets.takePacket();
|
||||||
Q_ASSERT(firstPacket);
|
Q_ASSERT(firstPacket);
|
||||||
|
|
||||||
std::unique_ptr<Packet> secondPacket;
|
|
||||||
bool shouldSendPairTail = false;
|
// attempt to send the first packet
|
||||||
|
if (sendNewPacketAndAddToSentList(move(firstPacket), nextNumber)) {
|
||||||
if (((uint32_t) nextNumber & 0xF) == 0) {
|
std::unique_ptr<Packet> secondPacket;
|
||||||
// the first packet is the first in a probe pair - every 16 (rightmost 16 bits = 0) packets
|
bool shouldSendPairTail = false;
|
||||||
// pull off a second packet if we can before we unlock
|
|
||||||
shouldSendPairTail = true;
|
if (((uint32_t) nextNumber & 0xF) == 0) {
|
||||||
|
// the first packet is the first in a probe pair - every 16 (rightmost 16 bits = 0) packets
|
||||||
secondPacket = _packets.takePacket();
|
// pull off a second packet if we can before we unlock
|
||||||
|
shouldSendPairTail = true;
|
||||||
|
|
||||||
|
secondPacket = _packets.takePacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
// do we have a second in a pair to send as well?
|
||||||
|
if (secondPacket) {
|
||||||
|
sendNewPacketAndAddToSentList(move(secondPacket), getNextSequenceNumber());
|
||||||
|
} else if (shouldSendPairTail) {
|
||||||
|
// we didn't get a second packet to send in the probe pair
|
||||||
|
// send a control packet of type ProbePairTail so the receiver can still do
|
||||||
|
// proper bandwidth estimation
|
||||||
|
static auto pairTailPacket = ControlPacket::create(ControlPacket::ProbeTail);
|
||||||
|
_socket->writeBasePacket(*pairTailPacket, _destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we attempted to send two packets, return 2
|
||||||
|
return 2;
|
||||||
|
} else {
|
||||||
|
// we attempted to send a single packet, return 1
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// definitely send the first packet
|
|
||||||
sendNewPacketAndAddToSentList(move(firstPacket), nextNumber);
|
|
||||||
|
|
||||||
// do we have a second in a pair to send as well?
|
|
||||||
if (secondPacket) {
|
|
||||||
sendNewPacketAndAddToSentList(move(secondPacket), getNextSequenceNumber());
|
|
||||||
} else if (shouldSendPairTail) {
|
|
||||||
// we didn't get a second packet to send in the probe pair
|
|
||||||
// send a control packet of type ProbePairTail so the receiver can still do
|
|
||||||
// proper bandwidth estimation
|
|
||||||
static auto pairTailPacket = ControlPacket::create(ControlPacket::ProbeTail);
|
|
||||||
_socket->writeBasePacket(*pairTailPacket, _destination);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We sent our packet(s), return here
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// No packets were sent
|
// No packets were sent
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SendQueue::maybeResendPacket() {
|
bool SendQueue::maybeResendPacket() {
|
||||||
|
@ -375,8 +400,9 @@ bool SendQueue::maybeResendPacket() {
|
||||||
|
|
||||||
// see if we can find the packet to re-send
|
// see if we can find the packet to re-send
|
||||||
auto it = _sentPackets.find(resendNumber);
|
auto it = _sentPackets.find(resendNumber);
|
||||||
|
|
||||||
if (it != _sentPackets.end()) {
|
if (it != _sentPackets.end()) {
|
||||||
|
|
||||||
auto& entry = it->second;
|
auto& entry = it->second;
|
||||||
// we found the packet - grab it
|
// we found the packet - grab it
|
||||||
auto& resendPacket = *(entry.second);
|
auto& resendPacket = *(entry.second);
|
||||||
|
@ -437,7 +463,7 @@ bool SendQueue::maybeResendPacket() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SendQueue::isInactive(bool sentAPacket) {
|
bool SendQueue::isInactive(bool attemptedToSendPacket) {
|
||||||
// check for connection timeout first
|
// check for connection timeout first
|
||||||
|
|
||||||
// that will be the case if we have had 16 timeouts since hearing back from the client, and it has been
|
// that will be the case if we have had 16 timeouts since hearing back from the client, and it has been
|
||||||
|
@ -447,7 +473,8 @@ bool SendQueue::isInactive(bool sentAPacket) {
|
||||||
|
|
||||||
auto sinceLastResponse = (QDateTime::currentMSecsSinceEpoch() - _lastReceiverResponse);
|
auto sinceLastResponse = (QDateTime::currentMSecsSinceEpoch() - _lastReceiverResponse);
|
||||||
|
|
||||||
if (sinceLastResponse >= quint64(NUM_TIMEOUTS_BEFORE_INACTIVE * (_estimatedTimeout / USECS_PER_MSEC)) &&
|
if (sinceLastResponse > 0 &&
|
||||||
|
sinceLastResponse >= int64_t(NUM_TIMEOUTS_BEFORE_INACTIVE * (_estimatedTimeout / USECS_PER_MSEC)) &&
|
||||||
_lastReceiverResponse > 0 &&
|
_lastReceiverResponse > 0 &&
|
||||||
sinceLastResponse > MIN_MS_BEFORE_INACTIVE) {
|
sinceLastResponse > MIN_MS_BEFORE_INACTIVE) {
|
||||||
// If the flow window has been full for over CONSIDER_INACTIVE_AFTER,
|
// If the flow window has been full for over CONSIDER_INACTIVE_AFTER,
|
||||||
|
@ -462,7 +489,7 @@ bool SendQueue::isInactive(bool sentAPacket) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sentAPacket) {
|
if (!attemptedToSendPacket) {
|
||||||
// During our processing above we didn't send any packets
|
// During our processing above we didn't send any packets
|
||||||
|
|
||||||
// If that is still the case we should use a condition_variable_any to sleep until we have data to handle.
|
// If that is still the case we should use a condition_variable_any to sleep until we have data to handle.
|
||||||
|
|
|
@ -79,6 +79,7 @@ signals:
|
||||||
|
|
||||||
void queueInactive();
|
void queueInactive();
|
||||||
|
|
||||||
|
void shortCircuitLoss(quint32 sequenceNumber);
|
||||||
void timeout();
|
void timeout();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
@ -91,13 +92,13 @@ private:
|
||||||
|
|
||||||
void sendHandshake();
|
void sendHandshake();
|
||||||
|
|
||||||
void sendPacket(const Packet& packet);
|
int sendPacket(const Packet& packet);
|
||||||
void sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket, SequenceNumber sequenceNumber);
|
bool sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket, SequenceNumber sequenceNumber);
|
||||||
|
|
||||||
bool maybeSendNewPacket(); // Figures out what packet to send next
|
int maybeSendNewPacket(); // Figures out what packet to send next
|
||||||
bool maybeResendPacket(); // Determines whether to resend a packet and which one
|
bool maybeResendPacket(); // Determines whether to resend a packet and which one
|
||||||
|
|
||||||
bool isInactive(bool sentAPacket);
|
bool isInactive(bool attemptedToSendPacket);
|
||||||
void deactivate(); // makes the queue inactive and cleans it up
|
void deactivate(); // makes the queue inactive and cleans it up
|
||||||
|
|
||||||
bool isFlowWindowFull() const;
|
bool isFlowWindowFull() const;
|
||||||
|
@ -122,7 +123,7 @@ private:
|
||||||
|
|
||||||
std::atomic<int> _estimatedTimeout { 0 }; // Estimated timeout, set from CC
|
std::atomic<int> _estimatedTimeout { 0 }; // Estimated timeout, set from CC
|
||||||
std::atomic<int> _syncInterval { udt::DEFAULT_SYN_INTERVAL_USECS }; // Sync interval, set from CC
|
std::atomic<int> _syncInterval { udt::DEFAULT_SYN_INTERVAL_USECS }; // Sync interval, set from CC
|
||||||
std::atomic<uint64_t> _lastReceiverResponse { 0 }; // Timestamp for the last time we got new data from the receiver (ACK/NAK)
|
std::atomic<int64_t> _lastReceiverResponse { 0 }; // Timestamp for the last time we got new data from the receiver (ACK/NAK)
|
||||||
|
|
||||||
std::atomic<int> _flowWindowSize { 0 }; // Flow control window size (number of packets that can be on wire) - set from CC
|
std::atomic<int> _flowWindowSize { 0 }; // Flow control window size (number of packets that can be on wire) - set from CC
|
||||||
|
|
||||||
|
|
|
@ -120,3 +120,21 @@ btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) {
|
||||||
}
|
}
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShapeFactory::deleteShape(btCollisionShape* shape) {
|
||||||
|
assert(shape);
|
||||||
|
if (shape->getShapeType() == (int)COMPOUND_SHAPE_PROXYTYPE) {
|
||||||
|
btCompoundShape* compoundShape = static_cast<btCompoundShape*>(shape);
|
||||||
|
const int numChildShapes = compoundShape->getNumChildShapes();
|
||||||
|
for (int i = 0; i < numChildShapes; i ++) {
|
||||||
|
btCollisionShape* childShape = compoundShape->getChildShape(i);
|
||||||
|
if (childShape->getShapeType() == (int)COMPOUND_SHAPE_PROXYTYPE) {
|
||||||
|
// recurse
|
||||||
|
ShapeFactory::deleteShape(childShape);
|
||||||
|
} else {
|
||||||
|
delete childShape;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete shape;
|
||||||
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
namespace ShapeFactory {
|
namespace ShapeFactory {
|
||||||
btConvexHullShape* createConvexHull(const QVector<glm::vec3>& points);
|
btConvexHullShape* createConvexHull(const QVector<glm::vec3>& points);
|
||||||
btCollisionShape* createShapeFromInfo(const ShapeInfo& info);
|
btCollisionShape* createShapeFromInfo(const ShapeInfo& info);
|
||||||
|
void deleteShape(btCollisionShape* shape);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_ShapeFactory_h
|
#endif // hifi_ShapeFactory_h
|
||||||
|
|
|
@ -23,7 +23,7 @@ ShapeManager::~ShapeManager() {
|
||||||
int numShapes = _shapeMap.size();
|
int numShapes = _shapeMap.size();
|
||||||
for (int i = 0; i < numShapes; ++i) {
|
for (int i = 0; i < numShapes; ++i) {
|
||||||
ShapeReference* shapeRef = _shapeMap.getAtIndex(i);
|
ShapeReference* shapeRef = _shapeMap.getAtIndex(i);
|
||||||
delete shapeRef->shape;
|
ShapeFactory::deleteShape(shapeRef->shape);
|
||||||
}
|
}
|
||||||
_shapeMap.clear();
|
_shapeMap.clear();
|
||||||
}
|
}
|
||||||
|
@ -32,13 +32,14 @@ btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
||||||
if (info.getType() == SHAPE_TYPE_NONE) {
|
if (info.getType() == SHAPE_TYPE_NONE) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
// Very small or large objects are not supported.
|
if (info.getType() != SHAPE_TYPE_COMPOUND) {
|
||||||
float diagonal = 4.0f * glm::length2(info.getHalfExtents());
|
// Very small or large non-compound objects are not supported.
|
||||||
const float MIN_SHAPE_DIAGONAL_SQUARED = 3.0e-4f; // 1 cm cube
|
float diagonal = 4.0f * glm::length2(info.getHalfExtents());
|
||||||
//const float MAX_SHAPE_DIAGONAL_SQUARED = 3.0e6f; // 1000 m cube
|
const float MIN_SHAPE_DIAGONAL_SQUARED = 3.0e-4f; // 1 cm cube
|
||||||
if (diagonal < MIN_SHAPE_DIAGONAL_SQUARED /* || diagonal > MAX_SHAPE_DIAGONAL_SQUARED*/ ) {
|
if (diagonal < MIN_SHAPE_DIAGONAL_SQUARED) {
|
||||||
// qCDebug(physics) << "ShapeManager::getShape -- not making shape due to size" << diagonal;
|
// qCDebug(physics) << "ShapeManager::getShape -- not making shape due to size" << diagonal;
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DoubleHashKey key = info.getHash();
|
DoubleHashKey key = info.getHash();
|
||||||
ShapeReference* shapeRef = _shapeMap.find(key);
|
ShapeReference* shapeRef = _shapeMap.find(key);
|
||||||
|
@ -58,14 +59,14 @@ btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// private helper method
|
// private helper method
|
||||||
bool ShapeManager::releaseShape(const DoubleHashKey& key) {
|
bool ShapeManager::releaseShapeByKey(const DoubleHashKey& key) {
|
||||||
ShapeReference* shapeRef = _shapeMap.find(key);
|
ShapeReference* shapeRef = _shapeMap.find(key);
|
||||||
if (shapeRef) {
|
if (shapeRef) {
|
||||||
if (shapeRef->refCount > 0) {
|
if (shapeRef->refCount > 0) {
|
||||||
shapeRef->refCount--;
|
shapeRef->refCount--;
|
||||||
if (shapeRef->refCount == 0) {
|
if (shapeRef->refCount == 0) {
|
||||||
_pendingGarbage.push_back(key);
|
_pendingGarbage.push_back(key);
|
||||||
const int MAX_GARBAGE_CAPACITY = 127;
|
const int MAX_GARBAGE_CAPACITY = 255;
|
||||||
if (_pendingGarbage.size() > MAX_GARBAGE_CAPACITY) {
|
if (_pendingGarbage.size() > MAX_GARBAGE_CAPACITY) {
|
||||||
collectGarbage();
|
collectGarbage();
|
||||||
}
|
}
|
||||||
|
@ -82,16 +83,12 @@ bool ShapeManager::releaseShape(const DoubleHashKey& key) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShapeManager::releaseShape(const ShapeInfo& info) {
|
|
||||||
return releaseShape(info.getHash());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ShapeManager::releaseShape(const btCollisionShape* shape) {
|
bool ShapeManager::releaseShape(const btCollisionShape* shape) {
|
||||||
int numShapes = _shapeMap.size();
|
int numShapes = _shapeMap.size();
|
||||||
for (int i = 0; i < numShapes; ++i) {
|
for (int i = 0; i < numShapes; ++i) {
|
||||||
ShapeReference* shapeRef = _shapeMap.getAtIndex(i);
|
ShapeReference* shapeRef = _shapeMap.getAtIndex(i);
|
||||||
if (shape == shapeRef->shape) {
|
if (shape == shapeRef->shape) {
|
||||||
return releaseShape(shapeRef->key);
|
return releaseShapeByKey(shapeRef->key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -103,17 +100,7 @@ void ShapeManager::collectGarbage() {
|
||||||
DoubleHashKey& key = _pendingGarbage[i];
|
DoubleHashKey& key = _pendingGarbage[i];
|
||||||
ShapeReference* shapeRef = _shapeMap.find(key);
|
ShapeReference* shapeRef = _shapeMap.find(key);
|
||||||
if (shapeRef && shapeRef->refCount == 0) {
|
if (shapeRef && shapeRef->refCount == 0) {
|
||||||
// if the shape we're about to delete is compound, delete the children first.
|
ShapeFactory::deleteShape(shapeRef->shape);
|
||||||
if (shapeRef->shape->getShapeType() == COMPOUND_SHAPE_PROXYTYPE) {
|
|
||||||
const btCompoundShape* compoundShape = static_cast<const btCompoundShape*>(shapeRef->shape);
|
|
||||||
const int numChildShapes = compoundShape->getNumChildShapes();
|
|
||||||
for (int i = 0; i < numChildShapes; i ++) {
|
|
||||||
const btCollisionShape* childShape = compoundShape->getChildShape(i);
|
|
||||||
delete childShape;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete shapeRef->shape;
|
|
||||||
_shapeMap.remove(key);
|
_shapeMap.remove(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,6 @@ public:
|
||||||
btCollisionShape* getShape(const ShapeInfo& info);
|
btCollisionShape* getShape(const ShapeInfo& info);
|
||||||
|
|
||||||
/// \return true if shape was found and released
|
/// \return true if shape was found and released
|
||||||
bool releaseShape(const ShapeInfo& info);
|
|
||||||
bool releaseShape(const btCollisionShape* shape);
|
bool releaseShape(const btCollisionShape* shape);
|
||||||
|
|
||||||
/// delete shapes that have zero references
|
/// delete shapes that have zero references
|
||||||
|
@ -39,10 +38,10 @@ public:
|
||||||
int getNumShapes() const { return _shapeMap.size(); }
|
int getNumShapes() const { return _shapeMap.size(); }
|
||||||
int getNumReferences(const ShapeInfo& info) const;
|
int getNumReferences(const ShapeInfo& info) const;
|
||||||
int getNumReferences(const btCollisionShape* shape) const;
|
int getNumReferences(const btCollisionShape* shape) const;
|
||||||
bool hasShape(const btCollisionShape* shape) const;
|
bool hasShape(const btCollisionShape* shape) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool releaseShape(const DoubleHashKey& key);
|
bool releaseShapeByKey(const DoubleHashKey& key);
|
||||||
|
|
||||||
struct ShapeReference {
|
struct ShapeReference {
|
||||||
int refCount;
|
int refCount;
|
||||||
|
|
|
@ -59,7 +59,7 @@ float fetchOcclusionMap(vec2 uv) {
|
||||||
|
|
||||||
<@func fetchMaterialTextures(matKey, texcoord0, albedo, roughness, normal, metallic, emissive, occlusion)@>
|
<@func fetchMaterialTextures(matKey, texcoord0, albedo, roughness, normal, metallic, emissive, occlusion)@>
|
||||||
<@if albedo@>
|
<@if albedo@>
|
||||||
vec4 <$albedo$> = (((<$matKey$> & ALBEDO_MAP_BIT) != 0) ? fetchAlbedoMap(<$texcoord0$>) : vec4(1.0));
|
vec4 <$albedo$> = (((<$matKey$> & (ALBEDO_MAP_BIT | OPACITY_MASK_MAP_BIT | OPACITY_TRANSLUCENT_MAP_BIT)) != 0) ? fetchAlbedoMap(<$texcoord0$>) : vec4(1.0));
|
||||||
<@endif@>
|
<@endif@>
|
||||||
<@if roughness@>
|
<@if roughness@>
|
||||||
float <$roughness$> = (((<$matKey$> & ROUGHNESS_MAP_BIT) != 0) ? fetchRoughnessMap(<$texcoord0$>) : 1.0);
|
float <$roughness$> = (((<$matKey$> & ROUGHNESS_MAP_BIT) != 0) ? fetchRoughnessMap(<$texcoord0$>) : 1.0);
|
||||||
|
@ -112,6 +112,23 @@ vec3 fetchLightmapMap(vec2 uv) {
|
||||||
}
|
}
|
||||||
<@endfunc@>
|
<@endfunc@>
|
||||||
|
|
||||||
|
<@func evalMaterialOpacity(fetchedOpacity, materialOpacity, matKey, opacity)@>
|
||||||
|
{
|
||||||
|
const float OPACITY_MASK_THRESHOLD = 0.5;
|
||||||
|
<$opacity$> = (((<$matKey$> & (OPACITY_TRANSLUCENT_MAP_BIT | OPACITY_MASK_MAP_BIT)) != 0) ?
|
||||||
|
(((<$matKey$> & OPACITY_MASK_MAP_BIT) != 0) ? step(OPACITY_MASK_THRESHOLD, <$fetchedOpacity$>) : <$fetchedOpacity$>) :
|
||||||
|
1.0) * <$materialOpacity$>;
|
||||||
|
}
|
||||||
|
<@endfunc@>
|
||||||
|
|
||||||
|
<@func $discardTransparent(opacity)@>
|
||||||
|
{
|
||||||
|
if (<$opacity$> < 1.0) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<@endfunc@>
|
||||||
|
|
||||||
<@func evalMaterialRoughness(fetchedRoughness, materialRoughness, matKey, roughness)@>
|
<@func evalMaterialRoughness(fetchedRoughness, materialRoughness, matKey, roughness)@>
|
||||||
{
|
{
|
||||||
<$roughness$> = (((<$matKey$> & ROUGHNESS_MAP_BIT) != 0) ? <$fetchedRoughness$> : <$materialRoughness$>);
|
<$roughness$> = (((<$matKey$> & ROUGHNESS_MAP_BIT) != 0) ? <$fetchedRoughness$> : <$materialRoughness$>);
|
||||||
|
|
|
@ -81,7 +81,7 @@ ItemKey MeshPartPayload::getKey() const {
|
||||||
|
|
||||||
if (_drawMaterial) {
|
if (_drawMaterial) {
|
||||||
auto matKey = _drawMaterial->getKey();
|
auto matKey = _drawMaterial->getKey();
|
||||||
if (matKey.isTransparent() || matKey.isTransparentTexture() || matKey.isTransparentMap()) {
|
if (matKey.isTranslucent()) {
|
||||||
builder.withTransparent();
|
builder.withTransparent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ ShapeKey MeshPartPayload::getShapeKey() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
ShapeKey::Builder builder;
|
ShapeKey::Builder builder;
|
||||||
if (drawMaterialKey.isTransparent() || drawMaterialKey.isTransparentTexture() || drawMaterialKey.isTransparentMap()) {
|
if (drawMaterialKey.isTranslucent()) {
|
||||||
builder.withTranslucent();
|
builder.withTranslucent();
|
||||||
}
|
}
|
||||||
if (drawMaterialKey.isNormalMap()) {
|
if (drawMaterialKey.isNormalMap()) {
|
||||||
|
@ -365,7 +365,7 @@ ItemKey ModelMeshPartPayload::getKey() const {
|
||||||
|
|
||||||
if (_drawMaterial) {
|
if (_drawMaterial) {
|
||||||
auto matKey = _drawMaterial->getKey();
|
auto matKey = _drawMaterial->getKey();
|
||||||
if (matKey.isTransparent() || matKey.isTransparentTexture() || matKey.isTransparentMap()) {
|
if (matKey.isTranslucent()) {
|
||||||
builder.withTransparent();
|
builder.withTransparent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -412,8 +412,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const {
|
||||||
drawMaterialKey = _drawMaterial->getKey();
|
drawMaterialKey = _drawMaterial->getKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isTranslucent =
|
bool isTranslucent = drawMaterialKey.isTranslucent();
|
||||||
drawMaterialKey.isTransparent() || drawMaterialKey.isTransparentTexture() || drawMaterialKey.isTransparentMap();
|
|
||||||
bool hasTangents = drawMaterialKey.isNormalMap() && !mesh.tangents.isEmpty();
|
bool hasTangents = drawMaterialKey.isNormalMap() && !mesh.tangents.isEmpty();
|
||||||
bool hasSpecular = drawMaterialKey.isMetallicMap();
|
bool hasSpecular = drawMaterialKey.isMetallicMap();
|
||||||
bool hasLightmap = drawMaterialKey.isLightmapMap();
|
bool hasLightmap = drawMaterialKey.isLightmapMap();
|
||||||
|
|
|
@ -29,6 +29,10 @@ void main(void) {
|
||||||
int matKey = getMaterialKey(mat);
|
int matKey = getMaterialKey(mat);
|
||||||
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, _SCRIBE_NULL, emissiveTex, occlusionTex)$>
|
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, _SCRIBE_NULL, emissiveTex, occlusionTex)$>
|
||||||
|
|
||||||
|
float opacity = 1.0;
|
||||||
|
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)&>;
|
||||||
|
<$discardTransparent(opacity)$>;
|
||||||
|
|
||||||
vec3 albedo = getMaterialAlbedo(mat);
|
vec3 albedo = getMaterialAlbedo(mat);
|
||||||
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
||||||
albedo *= _color;
|
albedo *= _color;
|
||||||
|
@ -41,7 +45,7 @@ void main(void) {
|
||||||
|
|
||||||
packDeferredFragment(
|
packDeferredFragment(
|
||||||
normalize(_normal.xyz),
|
normalize(_normal.xyz),
|
||||||
evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedoTex.a),
|
opacity,
|
||||||
albedo,
|
albedo,
|
||||||
roughness,
|
roughness,
|
||||||
getMaterialMetallic(mat),
|
getMaterialMetallic(mat),
|
||||||
|
|
|
@ -30,6 +30,10 @@ void main(void) {
|
||||||
int matKey = getMaterialKey(mat);
|
int matKey = getMaterialKey(mat);
|
||||||
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, _SCRIBE_NULL, emissiveTex, occlusionTex)$>
|
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, _SCRIBE_NULL, emissiveTex, occlusionTex)$>
|
||||||
|
|
||||||
|
float opacity = 1.0;
|
||||||
|
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)&>;
|
||||||
|
<$discardTransparent(opacity)$>;
|
||||||
|
|
||||||
vec3 albedo = getMaterialAlbedo(mat);
|
vec3 albedo = getMaterialAlbedo(mat);
|
||||||
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
||||||
albedo *= _color;
|
albedo *= _color;
|
||||||
|
@ -45,7 +49,7 @@ void main(void) {
|
||||||
|
|
||||||
packDeferredFragment(
|
packDeferredFragment(
|
||||||
viewNormal,
|
viewNormal,
|
||||||
evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedoTex.a),
|
opacity,
|
||||||
albedo,
|
albedo,
|
||||||
roughness,
|
roughness,
|
||||||
getMaterialMetallic(mat),
|
getMaterialMetallic(mat),
|
||||||
|
|
|
@ -30,6 +30,10 @@ void main(void) {
|
||||||
int matKey = getMaterialKey(mat);
|
int matKey = getMaterialKey(mat);
|
||||||
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, metallicTex, emissiveTex, occlusionTex)$>
|
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, metallicTex, emissiveTex, occlusionTex)$>
|
||||||
|
|
||||||
|
float opacity = 1.0;
|
||||||
|
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)&>;
|
||||||
|
<$discardTransparent(opacity)$>;
|
||||||
|
|
||||||
vec3 albedo = getMaterialAlbedo(mat);
|
vec3 albedo = getMaterialAlbedo(mat);
|
||||||
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
||||||
albedo *= _color;
|
albedo *= _color;
|
||||||
|
@ -49,7 +53,7 @@ void main(void) {
|
||||||
|
|
||||||
packDeferredFragment(
|
packDeferredFragment(
|
||||||
normalize(viewNormal.xyz),
|
normalize(viewNormal.xyz),
|
||||||
evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedoTex.a),
|
opacity,
|
||||||
albedo,
|
albedo,
|
||||||
roughness,
|
roughness,
|
||||||
metallic,
|
metallic,
|
||||||
|
|
|
@ -30,6 +30,10 @@ void main(void) {
|
||||||
int matKey = getMaterialKey(mat);
|
int matKey = getMaterialKey(mat);
|
||||||
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, metallicTex, emissiveTex, occlusionTex)$>
|
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, metallicTex, emissiveTex, occlusionTex)$>
|
||||||
|
|
||||||
|
float opacity = 1.0;
|
||||||
|
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)&>;
|
||||||
|
<$discardTransparent(opacity)$>;
|
||||||
|
|
||||||
vec3 albedo = getMaterialAlbedo(mat);
|
vec3 albedo = getMaterialAlbedo(mat);
|
||||||
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
||||||
albedo *= _color;
|
albedo *= _color;
|
||||||
|
@ -45,7 +49,7 @@ void main(void) {
|
||||||
|
|
||||||
packDeferredFragment(
|
packDeferredFragment(
|
||||||
normalize(_normal),
|
normalize(_normal),
|
||||||
evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedoTex.a),
|
opacity,
|
||||||
albedo,
|
albedo,
|
||||||
roughness,
|
roughness,
|
||||||
metallic,
|
metallic,
|
||||||
|
|
|
@ -36,6 +36,10 @@ void main(void) {
|
||||||
int matKey = getMaterialKey(mat);
|
int matKey = getMaterialKey(mat);
|
||||||
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, _SCRIBE_NULL, emissiveTex, occlusionTex)$>
|
<$fetchMaterialTextures(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, _SCRIBE_NULL, emissiveTex, occlusionTex)$>
|
||||||
|
|
||||||
|
float opacity = getMaterialOpacity(mat) * _alpha;
|
||||||
|
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)&>;
|
||||||
|
<$discardTransparent(opacity)$>;
|
||||||
|
|
||||||
vec3 albedo = getMaterialAlbedo(mat);
|
vec3 albedo = getMaterialAlbedo(mat);
|
||||||
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
<$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>;
|
||||||
albedo *= _color;
|
albedo *= _color;
|
||||||
|
@ -52,8 +56,6 @@ void main(void) {
|
||||||
vec3 fragPosition = _position.xyz;
|
vec3 fragPosition = _position.xyz;
|
||||||
vec3 fragNormal = normalize(_normal);
|
vec3 fragNormal = normalize(_normal);
|
||||||
|
|
||||||
float fragOpacity = getMaterialOpacity(mat) * albedoTex.a * _alpha;
|
|
||||||
|
|
||||||
TransformCamera cam = getTransformCamera();
|
TransformCamera cam = getTransformCamera();
|
||||||
|
|
||||||
_fragColor = vec4(evalAmbientSphereGlobalColor(
|
_fragColor = vec4(evalAmbientSphereGlobalColor(
|
||||||
|
@ -66,5 +68,5 @@ void main(void) {
|
||||||
metallic,
|
metallic,
|
||||||
emissive,
|
emissive,
|
||||||
roughness),
|
roughness),
|
||||||
fragOpacity);
|
opacity);
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,6 +114,7 @@ void FetchSpatialTree::run(const SceneContextPointer& sceneContext, const Render
|
||||||
void CullSpatialSelection::configure(const Config& config) {
|
void CullSpatialSelection::configure(const Config& config) {
|
||||||
_justFrozeFrustum = _justFrozeFrustum || (config.freezeFrustum && !_freezeFrustum);
|
_justFrozeFrustum = _justFrozeFrustum || (config.freezeFrustum && !_freezeFrustum);
|
||||||
_freezeFrustum = config.freezeFrustum;
|
_freezeFrustum = config.freezeFrustum;
|
||||||
|
_skipCulling = config.skipCulling;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext,
|
void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext,
|
||||||
|
@ -191,60 +192,112 @@ void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const Re
|
||||||
// visibility cull if partially selected ( octree cell contianing it was partial)
|
// visibility cull if partially selected ( octree cell contianing it was partial)
|
||||||
// distance cull if was a subcell item ( octree cell is way bigger than the item bound itself, so now need to test per item)
|
// distance cull if was a subcell item ( octree cell is way bigger than the item bound itself, so now need to test per item)
|
||||||
|
|
||||||
// inside & fit items: easy, just filter
|
if (_skipCulling) {
|
||||||
{
|
// inside & fit items: filter only, culling is disabled
|
||||||
PerformanceTimer perfTimer("insideFitItems");
|
{
|
||||||
for (auto id : inSelection.insideItems) {
|
PerformanceTimer perfTimer("insideFitItems");
|
||||||
auto& item = scene->getItem(id);
|
for (auto id : inSelection.insideItems) {
|
||||||
if (_filter.test(item.getKey())) {
|
auto& item = scene->getItem(id);
|
||||||
ItemBound itemBound(id, item.getBound());
|
if (_filter.test(item.getKey())) {
|
||||||
outItems.emplace_back(itemBound);
|
ItemBound itemBound(id, item.getBound());
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// inside & subcell items: filter & distance cull
|
|
||||||
{
|
|
||||||
PerformanceTimer perfTimer("insideSmallItems");
|
|
||||||
for (auto id : inSelection.insideSubcellItems) {
|
|
||||||
auto& item = scene->getItem(id);
|
|
||||||
if (_filter.test(item.getKey())) {
|
|
||||||
ItemBound itemBound(id, item.getBound());
|
|
||||||
if (test.solidAngleTest(itemBound.bound)) {
|
|
||||||
outItems.emplace_back(itemBound);
|
outItems.emplace_back(itemBound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// partial & fit items: filter & frustum cull
|
// inside & subcell items: filter only, culling is disabled
|
||||||
{
|
{
|
||||||
PerformanceTimer perfTimer("partialFitItems");
|
PerformanceTimer perfTimer("insideSmallItems");
|
||||||
for (auto id : inSelection.partialItems) {
|
for (auto id : inSelection.insideSubcellItems) {
|
||||||
auto& item = scene->getItem(id);
|
auto& item = scene->getItem(id);
|
||||||
if (_filter.test(item.getKey())) {
|
if (_filter.test(item.getKey())) {
|
||||||
ItemBound itemBound(id, item.getBound());
|
ItemBound itemBound(id, item.getBound());
|
||||||
if (test.frustumTest(itemBound.bound)) {
|
|
||||||
outItems.emplace_back(itemBound);
|
outItems.emplace_back(itemBound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// partial & subcell items:: filter & frutum cull & solidangle cull
|
// partial & fit items: filter only, culling is disabled
|
||||||
{
|
{
|
||||||
PerformanceTimer perfTimer("partialSmallItems");
|
PerformanceTimer perfTimer("partialFitItems");
|
||||||
for (auto id : inSelection.partialSubcellItems) {
|
for (auto id : inSelection.partialItems) {
|
||||||
auto& item = scene->getItem(id);
|
auto& item = scene->getItem(id);
|
||||||
if (_filter.test(item.getKey())) {
|
if (_filter.test(item.getKey())) {
|
||||||
ItemBound itemBound(id, item.getBound());
|
ItemBound itemBound(id, item.getBound());
|
||||||
if (test.frustumTest(itemBound.bound)) {
|
outItems.emplace_back(itemBound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// partial & subcell items: filter only, culling is disabled
|
||||||
|
{
|
||||||
|
PerformanceTimer perfTimer("partialSmallItems");
|
||||||
|
for (auto id : inSelection.partialSubcellItems) {
|
||||||
|
auto& item = scene->getItem(id);
|
||||||
|
if (_filter.test(item.getKey())) {
|
||||||
|
ItemBound itemBound(id, item.getBound());
|
||||||
|
outItems.emplace_back(itemBound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// inside & fit items: easy, just filter
|
||||||
|
{
|
||||||
|
PerformanceTimer perfTimer("insideFitItems");
|
||||||
|
for (auto id : inSelection.insideItems) {
|
||||||
|
auto& item = scene->getItem(id);
|
||||||
|
if (_filter.test(item.getKey())) {
|
||||||
|
ItemBound itemBound(id, item.getBound());
|
||||||
|
outItems.emplace_back(itemBound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// inside & subcell items: filter & distance cull
|
||||||
|
{
|
||||||
|
PerformanceTimer perfTimer("insideSmallItems");
|
||||||
|
for (auto id : inSelection.insideSubcellItems) {
|
||||||
|
auto& item = scene->getItem(id);
|
||||||
|
if (_filter.test(item.getKey())) {
|
||||||
|
ItemBound itemBound(id, item.getBound());
|
||||||
if (test.solidAngleTest(itemBound.bound)) {
|
if (test.solidAngleTest(itemBound.bound)) {
|
||||||
outItems.emplace_back(itemBound);
|
outItems.emplace_back(itemBound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// partial & fit items: filter & frustum cull
|
||||||
|
{
|
||||||
|
PerformanceTimer perfTimer("partialFitItems");
|
||||||
|
for (auto id : inSelection.partialItems) {
|
||||||
|
auto& item = scene->getItem(id);
|
||||||
|
if (_filter.test(item.getKey())) {
|
||||||
|
ItemBound itemBound(id, item.getBound());
|
||||||
|
if (test.frustumTest(itemBound.bound)) {
|
||||||
|
outItems.emplace_back(itemBound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// partial & subcell items:: filter & frutum cull & solidangle cull
|
||||||
|
{
|
||||||
|
PerformanceTimer perfTimer("partialSmallItems");
|
||||||
|
for (auto id : inSelection.partialSubcellItems) {
|
||||||
|
auto& item = scene->getItem(id);
|
||||||
|
if (_filter.test(item.getKey())) {
|
||||||
|
ItemBound itemBound(id, item.getBound());
|
||||||
|
if (test.frustumTest(itemBound.bound)) {
|
||||||
|
if (test.solidAngleTest(itemBound.bound)) {
|
||||||
|
outItems.emplace_back(itemBound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
details._rendered += (int)outItems.size();
|
details._rendered += (int)outItems.size();
|
||||||
|
|
|
@ -70,14 +70,16 @@ namespace render {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(int numItems READ getNumItems)
|
Q_PROPERTY(int numItems READ getNumItems)
|
||||||
Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum)
|
Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum)
|
||||||
|
Q_PROPERTY(bool skipCulling MEMBER skipCulling WRITE setSkipCulling)
|
||||||
public:
|
public:
|
||||||
int numItems{ 0 };
|
int numItems{ 0 };
|
||||||
int getNumItems() { return numItems; }
|
int getNumItems() { return numItems; }
|
||||||
|
|
||||||
bool freezeFrustum{ false };
|
bool freezeFrustum{ false };
|
||||||
|
bool skipCulling{ false };
|
||||||
public slots:
|
public slots:
|
||||||
void setFreezeFrustum(bool enabled) { freezeFrustum = enabled; emit dirty(); }
|
void setFreezeFrustum(bool enabled) { freezeFrustum = enabled; emit dirty(); }
|
||||||
|
void setSkipCulling(bool enabled) { skipCulling = enabled; emit dirty(); }
|
||||||
signals:
|
signals:
|
||||||
void dirty();
|
void dirty();
|
||||||
};
|
};
|
||||||
|
@ -85,6 +87,7 @@ namespace render {
|
||||||
class CullSpatialSelection {
|
class CullSpatialSelection {
|
||||||
bool _freezeFrustum{ false }; // initialized by Config
|
bool _freezeFrustum{ false }; // initialized by Config
|
||||||
bool _justFrozeFrustum{ false };
|
bool _justFrozeFrustum{ false };
|
||||||
|
bool _skipCulling{ false };
|
||||||
ViewFrustum _frozenFrutstum;
|
ViewFrustum _frozenFrutstum;
|
||||||
public:
|
public:
|
||||||
using Config = CullSpatialSelectionConfig;
|
using Config = CullSpatialSelectionConfig;
|
||||||
|
|
|
@ -20,6 +20,10 @@ public:
|
||||||
template <typename F>
|
template <typename F>
|
||||||
Finally(F f) : _f(f) {}
|
Finally(F f) : _f(f) {}
|
||||||
~Finally() { _f(); }
|
~Finally() { _f(); }
|
||||||
|
void trigger() {
|
||||||
|
_f();
|
||||||
|
_f = [] {};
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
std::function<void()> _f;
|
std::function<void()> _f;
|
||||||
};
|
};
|
||||||
|
|
|
@ -63,7 +63,7 @@ public:
|
||||||
void appendToPoints (const QVector<glm::vec3>& newPoints) { _points << newPoints; }
|
void appendToPoints (const QVector<glm::vec3>& newPoints) { _points << newPoints; }
|
||||||
|
|
||||||
float computeVolume() const;
|
float computeVolume() const;
|
||||||
|
|
||||||
/// Returns whether point is inside the shape
|
/// Returns whether point is inside the shape
|
||||||
/// For compound shapes it will only return whether it is inside the bounding box
|
/// For compound shapes it will only return whether it is inside the bounding box
|
||||||
bool contains(const glm::vec3& point) const;
|
bool contains(const glm::vec3& point) const;
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "NsightHelpers.h"
|
#include "NsightHelpers.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
#if defined(NSIGHT_FOUND)
|
#if defined(NSIGHT_FOUND)
|
||||||
#include "nvToolsExt.h"
|
#include "nvToolsExt.h"
|
||||||
|
|
||||||
|
@ -15,8 +16,28 @@ ProfileRange::ProfileRange(const char *name) {
|
||||||
nvtxRangePush(name);
|
nvtxRangePush(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProfileRange::ProfileRange(const char *name, uint32_t argbColor, uint64_t payload) {
|
||||||
|
|
||||||
|
nvtxEventAttributes_t eventAttrib = {0};
|
||||||
|
eventAttrib.version = NVTX_VERSION;
|
||||||
|
eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE;
|
||||||
|
eventAttrib.colorType = NVTX_COLOR_ARGB;
|
||||||
|
eventAttrib.color = argbColor;
|
||||||
|
eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII;
|
||||||
|
eventAttrib.message.ascii = name;
|
||||||
|
eventAttrib.payload.llValue = payload;
|
||||||
|
eventAttrib.payloadType = NVTX_PAYLOAD_TYPE_UNSIGNED_INT64;
|
||||||
|
|
||||||
|
nvtxRangePushEx(&eventAttrib);
|
||||||
|
}
|
||||||
|
|
||||||
ProfileRange::~ProfileRange() {
|
ProfileRange::~ProfileRange() {
|
||||||
nvtxRangePop();
|
nvtxRangePop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
ProfileRange::ProfileRange(const char *name) {}
|
||||||
|
ProfileRange::ProfileRange(const char *name, uint32_t argbColor, uint64_t payload) {}
|
||||||
|
ProfileRange::~ProfileRange() {}
|
||||||
#endif
|
#endif
|
||||||
|
#endif // _WIN32
|
||||||
|
|
|
@ -9,16 +9,21 @@
|
||||||
#ifndef hifi_gl_NsightHelpers_h
|
#ifndef hifi_gl_NsightHelpers_h
|
||||||
#define hifi_gl_NsightHelpers_h
|
#define hifi_gl_NsightHelpers_h
|
||||||
|
|
||||||
#if defined(NSIGHT_FOUND)
|
#ifdef _WIN32
|
||||||
class ProfileRange {
|
#include <stdint.h>
|
||||||
public:
|
|
||||||
ProfileRange(const char *name);
|
class ProfileRange {
|
||||||
~ProfileRange();
|
public:
|
||||||
};
|
ProfileRange(const char *name);
|
||||||
|
ProfileRange(const char *name, uint32_t argbColor, uint64_t payload);
|
||||||
|
~ProfileRange();
|
||||||
|
};
|
||||||
|
|
||||||
#define PROFILE_RANGE(name) ProfileRange profileRangeThis(name);
|
#define PROFILE_RANGE(name) ProfileRange profileRangeThis(name);
|
||||||
|
#define PROFILE_RANGE_EX(name, argbColor, payload) ProfileRange profileRangeThis(name, argbColor, payload);
|
||||||
#else
|
#else
|
||||||
#define PROFILE_RANGE(name)
|
#define PROFILE_RANGE(name)
|
||||||
|
#define PROFILE_RANGE_EX(name, argbColor, payload)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
#endif
|
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
#include <QtQml/QQmlContext>
|
#include <QtQml/QQmlContext>
|
||||||
|
|
||||||
|
#include <QtWebChannel/QWebChannel>
|
||||||
|
|
||||||
#include <QtScript/QScriptContext>
|
#include <QtScript/QScriptContext>
|
||||||
#include <QtScript/QScriptEngine>
|
#include <QtScript/QScriptEngine>
|
||||||
|
|
||||||
|
@ -31,19 +33,39 @@ static const char* const URL_PROPERTY = "source";
|
||||||
// Method called by Qt scripts to create a new web window in the overlay
|
// Method called by Qt scripts to create a new web window in the overlay
|
||||||
QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
|
QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
|
||||||
return QmlWindowClass::internalConstructor("QmlWebWindow.qml", context, engine,
|
return QmlWindowClass::internalConstructor("QmlWebWindow.qml", context, engine,
|
||||||
[&](QObject* object) { return new QmlWebWindowClass(object); });
|
[&](QObject* object) { return new QmlWebWindowClass(object); });
|
||||||
}
|
}
|
||||||
|
|
||||||
QmlWebWindowClass::QmlWebWindowClass(QObject* qmlWindow) : QmlWindowClass(qmlWindow) {
|
QmlWebWindowClass::QmlWebWindowClass(QObject* qmlWindow) : QmlWindowClass(qmlWindow) {
|
||||||
|
_uid = QUuid::createUuid().toString();
|
||||||
|
asQuickItem()->setProperty("uid", _uid);
|
||||||
|
auto webchannelVar = qmlWindow->property("webChannel");
|
||||||
|
_webchannel = qvariant_cast<QWebChannel*>(webchannelVar);
|
||||||
|
Q_ASSERT(_webchannel);
|
||||||
|
_webchannel->registerObject(_uid, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QmlWebWindowClass::emitScriptEvent(const QVariant& scriptMessage) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "emitScriptEvent", Qt::QueuedConnection, Q_ARG(QVariant, scriptMessage));
|
||||||
|
} else {
|
||||||
|
emit scriptEventReceived(scriptMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME remove.
|
void QmlWebWindowClass::emitWebEvent(const QVariant& webMessage) {
|
||||||
void QmlWebWindowClass::handleNavigation(const QString& url) {
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "emitWebEvent", Qt::QueuedConnection, Q_ARG(QVariant, webMessage));
|
||||||
|
} else {
|
||||||
|
emit webEventReceived(webMessage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString QmlWebWindowClass::getURL() const {
|
QString QmlWebWindowClass::getURL() const {
|
||||||
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
|
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
|
||||||
|
if (_qmlWindow.isNull()) {
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
return _qmlWindow->property(URL_PROPERTY);
|
return _qmlWindow->property(URL_PROPERTY);
|
||||||
});
|
});
|
||||||
return result.toString();
|
return result.toString();
|
||||||
|
@ -54,6 +76,8 @@ extern QString fixupHifiUrl(const QString& urlString);
|
||||||
|
|
||||||
void QmlWebWindowClass::setURL(const QString& urlString) {
|
void QmlWebWindowClass::setURL(const QString& urlString) {
|
||||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
||||||
_qmlWindow->setProperty(URL_PROPERTY, fixupHifiUrl(urlString));
|
if (!_qmlWindow.isNull()) {
|
||||||
|
_qmlWindow->setProperty(URL_PROPERTY, fixupHifiUrl(urlString));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,13 @@
|
||||||
|
|
||||||
#include "QmlWindowClass.h"
|
#include "QmlWindowClass.h"
|
||||||
|
|
||||||
|
class QWebChannel;
|
||||||
|
|
||||||
// FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping
|
// FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping
|
||||||
class QmlWebWindowClass : public QmlWindowClass {
|
class QmlWebWindowClass : public QmlWindowClass {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(QString url READ getURL CONSTANT)
|
Q_PROPERTY(QString url READ getURL CONSTANT)
|
||||||
|
Q_PROPERTY(QString uid READ getUid CONSTANT)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
|
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
|
||||||
|
@ -23,12 +26,18 @@ public:
|
||||||
public slots:
|
public slots:
|
||||||
QString getURL() const;
|
QString getURL() const;
|
||||||
void setURL(const QString& url);
|
void setURL(const QString& url);
|
||||||
|
const QString& getUid() const { return _uid; }
|
||||||
|
void emitScriptEvent(const QVariant& scriptMessage);
|
||||||
|
void emitWebEvent(const QVariant& webMessage);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void urlChanged();
|
void urlChanged();
|
||||||
|
void scriptEventReceived(const QVariant& message);
|
||||||
|
void webEventReceived(const QVariant& message);
|
||||||
|
|
||||||
private slots:
|
private:
|
||||||
void handleNavigation(const QString& url);
|
QString _uid;
|
||||||
|
QWebChannel* _webchannel { nullptr };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -26,10 +26,6 @@
|
||||||
|
|
||||||
#include "OffscreenUi.h"
|
#include "OffscreenUi.h"
|
||||||
|
|
||||||
QWebSocketServer* QmlWindowClass::_webChannelServer { nullptr };
|
|
||||||
static QWebChannel webChannel;
|
|
||||||
static const uint16_t WEB_CHANNEL_PORT = 51016;
|
|
||||||
static std::atomic<int> nextWindowId;
|
|
||||||
static const char* const SOURCE_PROPERTY = "source";
|
static const char* const SOURCE_PROPERTY = "source";
|
||||||
static const char* const TITLE_PROPERTY = "title";
|
static const char* const TITLE_PROPERTY = "title";
|
||||||
static const char* const WIDTH_PROPERTY = "width";
|
static const char* const WIDTH_PROPERTY = "width";
|
||||||
|
@ -37,54 +33,6 @@ static const char* const HEIGHT_PROPERTY = "height";
|
||||||
static const char* const VISIBILE_PROPERTY = "visible";
|
static const char* const VISIBILE_PROPERTY = "visible";
|
||||||
static const char* const TOOLWINDOW_PROPERTY = "toolWindow";
|
static const char* const TOOLWINDOW_PROPERTY = "toolWindow";
|
||||||
|
|
||||||
void QmlScriptEventBridge::emitWebEvent(const QString& data) {
|
|
||||||
QMetaObject::invokeMethod(this, "webEventReceived", Qt::QueuedConnection, Q_ARG(QString, data));
|
|
||||||
}
|
|
||||||
|
|
||||||
void QmlScriptEventBridge::emitScriptEvent(const QString& data) {
|
|
||||||
QMetaObject::invokeMethod(this, "scriptEventReceived", Qt::QueuedConnection,
|
|
||||||
Q_ARG(int, _webWindow->getWindowId()), Q_ARG(QString, data));
|
|
||||||
}
|
|
||||||
|
|
||||||
class QmlWebTransport : public QWebChannelAbstractTransport {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
QmlWebTransport(QWebSocket* webSocket) : _webSocket(webSocket) {
|
|
||||||
// Translate from the websocket layer to the webchannel layer
|
|
||||||
connect(webSocket, &QWebSocket::textMessageReceived, [this](const QString& message) {
|
|
||||||
QJsonParseError error;
|
|
||||||
QJsonDocument document = QJsonDocument::fromJson(message.toUtf8(), &error);
|
|
||||||
if (error.error || !document.isObject()) {
|
|
||||||
qWarning() << "Unable to parse incoming JSON message" << message;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
emit messageReceived(document.object(), this);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void sendMessage(const QJsonObject &message) override {
|
|
||||||
// Translate from the webchannel layer to the websocket layer
|
|
||||||
_webSocket->sendTextMessage(QJsonDocument(message).toJson(QJsonDocument::Compact));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
QWebSocket* const _webSocket;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void QmlWindowClass::setupServer() {
|
|
||||||
if (!_webChannelServer) {
|
|
||||||
_webChannelServer = new QWebSocketServer("EventBridge Server", QWebSocketServer::NonSecureMode);
|
|
||||||
if (!_webChannelServer->listen(QHostAddress::LocalHost, WEB_CHANNEL_PORT)) {
|
|
||||||
qFatal("Failed to open web socket server.");
|
|
||||||
}
|
|
||||||
|
|
||||||
QObject::connect(_webChannelServer, &QWebSocketServer::newConnection, [] {
|
|
||||||
webChannel.connectTo(new QmlWebTransport(_webChannelServer->nextPendingConnection()));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
|
QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
|
||||||
QScriptContext* context, QScriptEngine* engine,
|
QScriptContext* context, QScriptEngine* engine,
|
||||||
std::function<QmlWindowClass*(QObject*)> builder)
|
std::function<QmlWindowClass*(QObject*)> builder)
|
||||||
|
@ -168,10 +116,8 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
|
||||||
}
|
}
|
||||||
|
|
||||||
offscreenUi->returnFromUiThread([&] {
|
offscreenUi->returnFromUiThread([&] {
|
||||||
setupServer();
|
|
||||||
retVal = builder(newTab);
|
retVal = builder(newTab);
|
||||||
retVal->_toolWindow = true;
|
retVal->_toolWindow = true;
|
||||||
registerObject(url.toLower(), retVal);
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -179,10 +125,8 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource,
|
||||||
QMetaObject::invokeMethod(offscreenUi.data(), "load", Qt::BlockingQueuedConnection,
|
QMetaObject::invokeMethod(offscreenUi.data(), "load", Qt::BlockingQueuedConnection,
|
||||||
Q_ARG(const QString&, qmlSource),
|
Q_ARG(const QString&, qmlSource),
|
||||||
Q_ARG(std::function<void(QQmlContext*, QObject*)>, [&](QQmlContext* context, QObject* object) {
|
Q_ARG(std::function<void(QQmlContext*, QObject*)>, [&](QQmlContext* context, QObject* object) {
|
||||||
setupServer();
|
|
||||||
retVal = builder(object);
|
retVal = builder(object);
|
||||||
context->engine()->setObjectOwnership(retVal->_qmlWindow, QQmlEngine::CppOwnership);
|
context->engine()->setObjectOwnership(retVal->_qmlWindow, QQmlEngine::CppOwnership);
|
||||||
registerObject(url.toLower(), retVal);
|
|
||||||
if (!title.isEmpty()) {
|
if (!title.isEmpty()) {
|
||||||
retVal->setTitle(title);
|
retVal->setTitle(title);
|
||||||
}
|
}
|
||||||
|
@ -209,12 +153,9 @@ QScriptValue QmlWindowClass::constructor(QScriptContext* context, QScriptEngine*
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
QmlWindowClass::QmlWindowClass(QObject* qmlWindow)
|
QmlWindowClass::QmlWindowClass(QObject* qmlWindow) : _qmlWindow(qmlWindow) {
|
||||||
: _windowId(++nextWindowId), _qmlWindow(qmlWindow)
|
|
||||||
{
|
|
||||||
qDebug() << "Created window with ID " << _windowId;
|
|
||||||
Q_ASSERT(_qmlWindow);
|
Q_ASSERT(_qmlWindow);
|
||||||
Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow));
|
Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow.data()));
|
||||||
// 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, SIGNAL(fromQml(const QVariant&)), Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
@ -228,19 +169,11 @@ QmlWindowClass::~QmlWindowClass() {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlWindowClass::registerObject(const QString& name, QObject* object) {
|
|
||||||
webChannel.registerObject(name, object);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QmlWindowClass::deregisterObject(QObject* object) {
|
|
||||||
webChannel.deregisterObject(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
QQuickItem* QmlWindowClass::asQuickItem() const {
|
QQuickItem* QmlWindowClass::asQuickItem() const {
|
||||||
if (_toolWindow) {
|
if (_toolWindow) {
|
||||||
return DependencyManager::get<OffscreenUi>()->getToolWindow();
|
return DependencyManager::get<OffscreenUi>()->getToolWindow();
|
||||||
}
|
}
|
||||||
return dynamic_cast<QQuickItem*>(_qmlWindow);
|
return _qmlWindow.isNull() ? nullptr : dynamic_cast<QQuickItem*>(_qmlWindow.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlWindowClass::setVisible(bool visible) {
|
void QmlWindowClass::setVisible(bool visible) {
|
||||||
|
@ -260,32 +193,34 @@ void QmlWindowClass::setVisible(bool visible) {
|
||||||
|
|
||||||
bool QmlWindowClass::isVisible() const {
|
bool QmlWindowClass::isVisible() const {
|
||||||
// The tool window itself has special logic based on whether any tabs are enabled
|
// The tool window itself has special logic based on whether any tabs are enabled
|
||||||
if (_toolWindow) {
|
return DependencyManager::get<OffscreenUi>()->returnFromUiThread([&] {
|
||||||
auto targetTab = dynamic_cast<QQuickItem*>(_qmlWindow);
|
if (_qmlWindow.isNull()) {
|
||||||
return DependencyManager::get<OffscreenUi>()->returnFromUiThread([&] {
|
return QVariant::fromValue(false);
|
||||||
return QVariant::fromValue(targetTab->isEnabled());
|
}
|
||||||
}).toBool();
|
if (_toolWindow) {
|
||||||
} else {
|
return QVariant::fromValue(dynamic_cast<QQuickItem*>(_qmlWindow.data())->isEnabled());
|
||||||
QQuickItem* targetWindow = asQuickItem();
|
} else {
|
||||||
return DependencyManager::get<OffscreenUi>()->returnFromUiThread([&] {
|
return QVariant::fromValue(asQuickItem()->isVisible());
|
||||||
return QVariant::fromValue(targetWindow->isVisible());
|
}
|
||||||
}).toBool();
|
}).toBool();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec2 QmlWindowClass::getPosition() const {
|
glm::vec2 QmlWindowClass::getPosition() const {
|
||||||
QQuickItem* targetWindow = asQuickItem();
|
|
||||||
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
|
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
|
||||||
return targetWindow->position();
|
if (_qmlWindow.isNull()) {
|
||||||
|
return QVariant(QPointF(0, 0));
|
||||||
|
}
|
||||||
|
return asQuickItem()->position();
|
||||||
});
|
});
|
||||||
return toGlm(result.toPointF());
|
return toGlm(result.toPointF());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void QmlWindowClass::setPosition(const glm::vec2& position) {
|
void QmlWindowClass::setPosition(const glm::vec2& position) {
|
||||||
QQuickItem* targetWindow = asQuickItem();
|
|
||||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
||||||
targetWindow->setPosition(QPointF(position.x, position.y));
|
if (!_qmlWindow.isNull()) {
|
||||||
|
asQuickItem()->setPosition(QPointF(position.x, position.y));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,17 +234,21 @@ glm::vec2 toGlm(const QSizeF& size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec2 QmlWindowClass::getSize() const {
|
glm::vec2 QmlWindowClass::getSize() const {
|
||||||
QQuickItem* targetWindow = asQuickItem();
|
|
||||||
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
|
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
|
||||||
|
if (_qmlWindow.isNull()) {
|
||||||
|
return QVariant(QSizeF(0, 0));
|
||||||
|
}
|
||||||
|
QQuickItem* targetWindow = asQuickItem();
|
||||||
return QSizeF(targetWindow->width(), targetWindow->height());
|
return QSizeF(targetWindow->width(), targetWindow->height());
|
||||||
});
|
});
|
||||||
return toGlm(result.toSizeF());
|
return toGlm(result.toSizeF());
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlWindowClass::setSize(const glm::vec2& size) {
|
void QmlWindowClass::setSize(const glm::vec2& size) {
|
||||||
QQuickItem* targetWindow = asQuickItem();
|
|
||||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
||||||
targetWindow->setSize(QSizeF(size.x, size.y));
|
if (!_qmlWindow.isNull()) {
|
||||||
|
asQuickItem()->setSize(QSizeF(size.x, size.y));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,9 +257,10 @@ void QmlWindowClass::setSize(int width, int height) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlWindowClass::setTitle(const QString& title) {
|
void QmlWindowClass::setTitle(const QString& title) {
|
||||||
QQuickItem* targetWindow = asQuickItem();
|
|
||||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
||||||
targetWindow->setProperty(TITLE_PROPERTY, title);
|
if (!_qmlWindow.isNull()) {
|
||||||
|
asQuickItem()->setProperty(TITLE_PROPERTY, title);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,7 +285,12 @@ void QmlWindowClass::hasClosed() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlWindowClass::raise() {
|
void QmlWindowClass::raise() {
|
||||||
QMetaObject::invokeMethod(asQuickItem(), "raise", Qt::QueuedConnection);
|
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||||
|
offscreenUi->executeOnUiThread([=] {
|
||||||
|
if (!_qmlWindow.isNull()) {
|
||||||
|
QMetaObject::invokeMethod(asQuickItem(), "raise", Qt::DirectConnection);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "QmlWindowClass.moc"
|
#include "QmlWindowClass.moc"
|
||||||
|
|
|
@ -10,42 +10,21 @@
|
||||||
#define hifi_ui_QmlWindowClass_h
|
#define hifi_ui_QmlWindowClass_h
|
||||||
|
|
||||||
#include <QtCore/QObject>
|
#include <QtCore/QObject>
|
||||||
#include <GLMHelpers.h>
|
#include <QtCore/QPointer>
|
||||||
#include <QtScript/QScriptValue>
|
#include <QtScript/QScriptValue>
|
||||||
#include <QtQuick/QQuickItem>
|
#include <QtQuick/QQuickItem>
|
||||||
#include <QtWebChannel/QWebChannelAbstractTransport>
|
|
||||||
|
#include <GLMHelpers.h>
|
||||||
|
|
||||||
class QScriptEngine;
|
class QScriptEngine;
|
||||||
class QScriptContext;
|
class QScriptContext;
|
||||||
class QmlWindowClass;
|
|
||||||
class QWebSocketServer;
|
|
||||||
class QWebSocket;
|
|
||||||
|
|
||||||
class QmlScriptEventBridge : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
QmlScriptEventBridge(const QmlWindowClass* webWindow) : _webWindow(webWindow) {}
|
|
||||||
|
|
||||||
public slots :
|
|
||||||
void emitWebEvent(const QString& data);
|
|
||||||
void emitScriptEvent(const QString& data);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void webEventReceived(const QString& data);
|
|
||||||
void scriptEventReceived(int windowId, const QString& data);
|
|
||||||
|
|
||||||
private:
|
|
||||||
const QmlWindowClass* _webWindow { nullptr };
|
|
||||||
QWebSocket *_socket { nullptr };
|
|
||||||
};
|
|
||||||
|
|
||||||
// FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping
|
// FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping
|
||||||
class QmlWindowClass : public QObject {
|
class QmlWindowClass : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(QObject* eventBridge READ getEventBridge CONSTANT)
|
Q_PROPERTY(QObject* eventBridge READ getEventBridge CONSTANT)
|
||||||
Q_PROPERTY(int windowId READ getWindowId CONSTANT)
|
Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition NOTIFY positionChanged)
|
||||||
Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition)
|
Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize NOTIFY sizeChanged)
|
||||||
Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize)
|
|
||||||
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibilityChanged)
|
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibilityChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -64,21 +43,19 @@ public slots:
|
||||||
glm::vec2 getSize() const;
|
glm::vec2 getSize() const;
|
||||||
void setSize(const glm::vec2& size);
|
void setSize(const glm::vec2& size);
|
||||||
void setSize(int width, int height);
|
void setSize(int width, int height);
|
||||||
|
|
||||||
void setTitle(const QString& title);
|
void setTitle(const QString& title);
|
||||||
|
|
||||||
|
|
||||||
// Ugh.... do not want to do
|
|
||||||
Q_INVOKABLE void raise();
|
Q_INVOKABLE void raise();
|
||||||
Q_INVOKABLE void close();
|
Q_INVOKABLE void close();
|
||||||
Q_INVOKABLE int getWindowId() const { return _windowId; };
|
Q_INVOKABLE QObject* getEventBridge() { return this; };
|
||||||
Q_INVOKABLE QmlScriptEventBridge* getEventBridge() const { return _eventBridge; };
|
|
||||||
|
|
||||||
// Scripts can use this to send a message to the QML object
|
// Scripts can use this to send a message to the QML object
|
||||||
void sendToQml(const QVariant& message);
|
void sendToQml(const QVariant& message);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void visibilityChanged(bool visible); // Tool window
|
void visibilityChanged(bool visible); // Tool window
|
||||||
|
void positionChanged();
|
||||||
|
void sizeChanged();
|
||||||
void moved(glm::vec2 position);
|
void moved(glm::vec2 position);
|
||||||
void resized(QSizeF size);
|
void resized(QSizeF size);
|
||||||
void closed();
|
void closed();
|
||||||
|
@ -92,19 +69,13 @@ protected:
|
||||||
static QScriptValue internalConstructor(const QString& qmlSource,
|
static QScriptValue internalConstructor(const QString& qmlSource,
|
||||||
QScriptContext* context, QScriptEngine* engine,
|
QScriptContext* context, QScriptEngine* engine,
|
||||||
std::function<QmlWindowClass*(QObject*)> function);
|
std::function<QmlWindowClass*(QObject*)> function);
|
||||||
static void setupServer();
|
|
||||||
static void registerObject(const QString& name, QObject* object);
|
|
||||||
static void deregisterObject(QObject* object);
|
|
||||||
static QWebSocketServer* _webChannelServer;
|
|
||||||
|
|
||||||
QQuickItem* asQuickItem() const;
|
QQuickItem* asQuickItem() const;
|
||||||
QmlScriptEventBridge* const _eventBridge { new QmlScriptEventBridge(this) };
|
|
||||||
|
|
||||||
// FIXME needs to be initialized in the ctor once we have support
|
// FIXME needs to be initialized in the ctor once we have support
|
||||||
// for tool window panes in QML
|
// for tool window panes in QML
|
||||||
bool _toolWindow { false };
|
bool _toolWindow { false };
|
||||||
const int _windowId;
|
QPointer<QObject> _qmlWindow;
|
||||||
QObject* _qmlWindow;
|
|
||||||
QString _source;
|
QString _source;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
// 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"
|
||||||
|
#include <shared/NsightHelpers.h>
|
||||||
#include "OculusHelpers.h"
|
#include "OculusHelpers.h"
|
||||||
|
|
||||||
const QString OculusDisplayPlugin::NAME("Oculus Rift");
|
const QString OculusDisplayPlugin::NAME("Oculus Rift");
|
||||||
|
@ -54,6 +55,9 @@ void OculusDisplayPlugin::updateFrameData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OculusDisplayPlugin::hmdPresent() {
|
void OculusDisplayPlugin::hmdPresent() {
|
||||||
|
|
||||||
|
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentRenderFrameIndex)
|
||||||
|
|
||||||
if (!_currentSceneTexture) {
|
if (!_currentSceneTexture) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
#include <plugins/PluginContainer.h>
|
#include <plugins/PluginContainer.h>
|
||||||
#include <ViewFrustum.h>
|
#include <ViewFrustum.h>
|
||||||
|
#include <shared/NsightHelpers.h>
|
||||||
#include "OpenVrHelpers.h"
|
#include "OpenVrHelpers.h"
|
||||||
|
|
||||||
Q_DECLARE_LOGGING_CATEGORY(displayplugins)
|
Q_DECLARE_LOGGING_CATEGORY(displayplugins)
|
||||||
|
@ -69,6 +69,9 @@ void OpenVrDisplayPlugin::internalActivate() {
|
||||||
_compositor = vr::VRCompositor();
|
_compositor = vr::VRCompositor();
|
||||||
Q_ASSERT(_compositor);
|
Q_ASSERT(_compositor);
|
||||||
|
|
||||||
|
// enable async time warp
|
||||||
|
// _compositor->ForceInterleavedReprojectionOn(true);
|
||||||
|
|
||||||
// set up default sensor space such that the UI overlay will align with the front of the room.
|
// set up default sensor space such that the UI overlay will align with the front of the room.
|
||||||
auto chaperone = vr::VRChaperone();
|
auto chaperone = vr::VRChaperone();
|
||||||
if (chaperone) {
|
if (chaperone) {
|
||||||
|
@ -119,14 +122,11 @@ void OpenVrDisplayPlugin::updateHeadPose(uint32_t frameIndex) {
|
||||||
float vsyncToPhotons = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float);
|
float vsyncToPhotons = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float);
|
||||||
|
|
||||||
#if THREADED_PRESENT
|
#if THREADED_PRESENT
|
||||||
// TODO: this seems awfuly long, 44ms total, but it produced the best results.
|
// 3 frames of prediction + vsyncToPhotons = 44ms total
|
||||||
const float NUM_PREDICTION_FRAMES = 3.0f;
|
const float NUM_PREDICTION_FRAMES = 3.0f;
|
||||||
float predictedSecondsFromNow = NUM_PREDICTION_FRAMES * frameDuration + vsyncToPhotons;
|
float predictedSecondsFromNow = NUM_PREDICTION_FRAMES * frameDuration + vsyncToPhotons;
|
||||||
#else
|
#else
|
||||||
uint64_t frameCounter;
|
float predictedSecondsFromNow = frameDuration + vsyncToPhotons;
|
||||||
float timeSinceLastVsync;
|
|
||||||
_system->GetTimeSinceLastVsync(&timeSinceLastVsync, &frameCounter);
|
|
||||||
float predictedSecondsFromNow = 3.0f * frameDuration - timeSinceLastVsync + vsyncToPhotons;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
vr::TrackedDevicePose_t predictedTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
|
vr::TrackedDevicePose_t predictedTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
|
||||||
|
@ -144,6 +144,9 @@ void OpenVrDisplayPlugin::updateHeadPose(uint32_t frameIndex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenVrDisplayPlugin::hmdPresent() {
|
void OpenVrDisplayPlugin::hmdPresent() {
|
||||||
|
|
||||||
|
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentRenderFrameIndex)
|
||||||
|
|
||||||
// Flip y-axis since GL UV coords are backwards.
|
// Flip y-axis since GL UV coords are backwards.
|
||||||
static vr::VRTextureBounds_t leftBounds{ 0, 0, 0.5f, 1 };
|
static vr::VRTextureBounds_t leftBounds{ 0, 0, 0.5f, 1 };
|
||||||
static vr::VRTextureBounds_t rightBounds{ 0.5f, 0, 1, 1 };
|
static vr::VRTextureBounds_t rightBounds{ 0.5f, 0, 1, 1 };
|
||||||
|
@ -152,6 +155,10 @@ void OpenVrDisplayPlugin::hmdPresent() {
|
||||||
|
|
||||||
_compositor->Submit(vr::Eye_Left, &texture, &leftBounds);
|
_compositor->Submit(vr::Eye_Left, &texture, &leftBounds);
|
||||||
_compositor->Submit(vr::Eye_Right, &texture, &rightBounds);
|
_compositor->Submit(vr::Eye_Right, &texture, &rightBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenVrDisplayPlugin::postPreview() {
|
||||||
|
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentRenderFrameIndex)
|
||||||
|
|
||||||
vr::TrackedDevicePose_t currentTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
|
vr::TrackedDevicePose_t currentTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
|
||||||
_compositor->WaitGetPoses(currentTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, nullptr, 0);
|
_compositor->WaitGetPoses(currentTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, nullptr, 0);
|
||||||
|
|
|
@ -35,6 +35,7 @@ protected:
|
||||||
|
|
||||||
void hmdPresent() override;
|
void hmdPresent() override;
|
||||||
bool isHmdMounted() const override;
|
bool isHmdMounted() const override;
|
||||||
|
void postPreview() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
vr::IVRSystem* _system { nullptr };
|
vr::IVRSystem* _system { nullptr };
|
||||||
|
|
|
@ -51,7 +51,7 @@ vr::IVRSystem* acquireOpenVrSystem() {
|
||||||
if (!activeHmd) {
|
if (!activeHmd) {
|
||||||
qCDebug(displayplugins) << "openvr: No vr::IVRSystem instance active, building";
|
qCDebug(displayplugins) << "openvr: No vr::IVRSystem instance active, building";
|
||||||
vr::EVRInitError eError = vr::VRInitError_None;
|
vr::EVRInitError eError = vr::VRInitError_None;
|
||||||
activeHmd = vr::VR_Init(&eError);
|
activeHmd = vr::VR_Init(&eError, vr::VRApplication_Scene);
|
||||||
qCDebug(displayplugins) << "openvr display: HMD is " << activeHmd << " error is " << eError;
|
qCDebug(displayplugins) << "openvr display: HMD is " << activeHmd << " error is " << eError;
|
||||||
}
|
}
|
||||||
if (activeHmd) {
|
if (activeHmd) {
|
||||||
|
|
|
@ -44,6 +44,14 @@ ready = function() {
|
||||||
var domainServer = remote.getGlobal('domainServer');
|
var domainServer = remote.getGlobal('domainServer');
|
||||||
var acMonitor = remote.getGlobal('acMonitor');
|
var acMonitor = remote.getGlobal('acMonitor');
|
||||||
|
|
||||||
|
var pendingLines = {
|
||||||
|
'ds': new Array(),
|
||||||
|
'ac': new Array()
|
||||||
|
};
|
||||||
|
|
||||||
|
var UPDATE_INTERVAL = 16; // Update log at ~60 fps
|
||||||
|
var interval = setInterval(flushPendingLines, UPDATE_INTERVAL);
|
||||||
|
|
||||||
var logWatchers = {
|
var logWatchers = {
|
||||||
'ds': {
|
'ds': {
|
||||||
},
|
},
|
||||||
|
@ -83,7 +91,7 @@ ready = function() {
|
||||||
var logTail = new Tail(cleanFilePath, '\n', { start: start, interval: 500 });
|
var logTail = new Tail(cleanFilePath, '\n', { start: start, interval: 500 });
|
||||||
|
|
||||||
logTail.on('line', function(msg) {
|
logTail.on('line', function(msg) {
|
||||||
appendLogMessage(msg, stream);
|
pendingLines[stream].push(msg);
|
||||||
});
|
});
|
||||||
|
|
||||||
logTail.on('error', function(error) {
|
logTail.on('error', function(error) {
|
||||||
|
@ -107,6 +115,7 @@ ready = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
window.onbeforeunload = function(e) {
|
window.onbeforeunload = function(e) {
|
||||||
|
clearInterval(interval);
|
||||||
domainServer.removeListener('logs-updated', updateLogFiles);
|
domainServer.removeListener('logs-updated', updateLogFiles);
|
||||||
acMonitor.removeListener('logs-updated', updateLogFiles);
|
acMonitor.removeListener('logs-updated', updateLogFiles);
|
||||||
};
|
};
|
||||||
|
@ -164,14 +173,23 @@ ready = function() {
|
||||||
return !filter || message.toLowerCase().indexOf(filter) >= 0;
|
return !filter || message.toLowerCase().indexOf(filter) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function appendLogMessage(msg, name) {
|
function appendLogMessages(name) {
|
||||||
|
var array = pendingLines[name];
|
||||||
|
if (array.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (array.length > maxLogLines) {
|
||||||
|
array = array.slice(-maxLogLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(name, array.length);
|
||||||
|
|
||||||
var id = name == "ds" ? "domain-server" : "assignment-client";
|
var id = name == "ds" ? "domain-server" : "assignment-client";
|
||||||
var $pidLog = $('#' + id);
|
var $pidLog = $('#' + id);
|
||||||
|
|
||||||
var size = ++tabStates[id].size;
|
var size = tabStates[id].size + array.length;
|
||||||
if (size > maxLogLines) {
|
if (size > maxLogLines) {
|
||||||
$pidLog.find('div.log-line:first').remove();
|
$pidLog.find('div.log-line:lt(' + (size - maxLogLines) + ')').remove();
|
||||||
removed = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var wasAtBottom = false;
|
var wasAtBottom = false;
|
||||||
|
@ -179,17 +197,25 @@ ready = function() {
|
||||||
wasAtBottom = $pidLog[0].scrollTop >= ($pidLog[0].scrollHeight - $pidLog.height());
|
wasAtBottom = $pidLog[0].scrollTop >= ($pidLog[0].scrollHeight - $pidLog.height());
|
||||||
}
|
}
|
||||||
|
|
||||||
var $logLine = $('<div class="log-line">').text(msg);
|
for (line in array) {
|
||||||
if (!shouldDisplayLogMessage(msg)) {
|
var $logLine = $('<div class="log-line">').text(array[line]);
|
||||||
$logLine.hide();
|
if (!shouldDisplayLogMessage(array[line])) {
|
||||||
|
$logLine.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
$pidLog.append($logLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
$pidLog.append($logLine);
|
delete pendingLines[name];
|
||||||
|
pendingLines[name] = new Array();
|
||||||
|
|
||||||
if (wasAtBottom) {
|
if (wasAtBottom) {
|
||||||
$pidLog.scrollTop($pidLog[0].scrollHeight);
|
$pidLog.scrollTop($pidLog[0].scrollHeight);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
function flushPendingLines() {
|
||||||
|
appendLogMessages('ds');
|
||||||
|
appendLogMessages('ac');
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle filtering of table rows on input change
|
// handle filtering of table rows on input change
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
#include <BulletUtil.h>
|
#include <BulletUtil.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
// Implements functionality in QTestExtensions.h for glm types
|
// Implements functionality in QTestExtensions.h for glm types
|
||||||
// There are 3 functions in here (which need to be defined for all types that use them):
|
// There are 3 functions in here (which need to be defined for all types that use them):
|
||||||
//
|
//
|
||||||
|
|
|
@ -33,7 +33,7 @@ void MeshMassPropertiesTests::testParallelAxisTheorem() {
|
||||||
// verity we can compute the inertia tensor of a box in two different ways:
|
// verity we can compute the inertia tensor of a box in two different ways:
|
||||||
// (a) as one box
|
// (a) as one box
|
||||||
// (b) as a combination of two partial boxes.
|
// (b) as a combination of two partial boxes.
|
||||||
|
|
||||||
btScalar bigBoxX = 7.0f;
|
btScalar bigBoxX = 7.0f;
|
||||||
btScalar bigBoxY = 9.0f;
|
btScalar bigBoxY = 9.0f;
|
||||||
btScalar bigBoxZ = 11.0f;
|
btScalar bigBoxZ = 11.0f;
|
||||||
|
@ -62,9 +62,9 @@ void MeshMassPropertiesTests::testParallelAxisTheorem() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshMassPropertiesTests::testTetrahedron(){
|
void MeshMassPropertiesTests::testTetrahedron(){
|
||||||
// given the four vertices of a tetrahedron verify the analytic formula for inertia
|
// given the four vertices of a tetrahedron verify the analytic formula for inertia
|
||||||
// agrees with expected results
|
// agrees with expected results
|
||||||
|
|
||||||
// these numbers from the Tonon paper:
|
// these numbers from the Tonon paper:
|
||||||
btVector3 points[4];
|
btVector3 points[4];
|
||||||
points[0] = btVector3(8.33220f, -11.86875f, 0.93355f);
|
points[0] = btVector3(8.33220f, -11.86875f, 0.93355f);
|
||||||
|
@ -102,14 +102,14 @@ void MeshMassPropertiesTests::testTetrahedron(){
|
||||||
}
|
}
|
||||||
btMatrix3x3 inertia;
|
btMatrix3x3 inertia;
|
||||||
computeTetrahedronInertia(volume, points, inertia);
|
computeTetrahedronInertia(volume, points, inertia);
|
||||||
|
|
||||||
QCOMPARE_WITH_ABS_ERROR(volume, expectedVolume, acceptableRelativeError * volume);
|
QCOMPARE_WITH_ABS_ERROR(volume, expectedVolume, acceptableRelativeError * volume);
|
||||||
|
|
||||||
QCOMPARE_WITH_RELATIVE_ERROR(inertia, expectedInertia, acceptableRelativeError);
|
QCOMPARE_WITH_RELATIVE_ERROR(inertia, expectedInertia, acceptableRelativeError);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshMassPropertiesTests::testOpenTetrahedonMesh() {
|
void MeshMassPropertiesTests::testOpenTetrahedonMesh() {
|
||||||
// given the simplest possible mesh (open, with one triangle)
|
// given the simplest possible mesh (open, with one triangle)
|
||||||
// verify MeshMassProperties computes the right nubers
|
// verify MeshMassProperties computes the right nubers
|
||||||
|
|
||||||
// these numbers from the Tonon paper:
|
// these numbers from the Tonon paper:
|
||||||
|
@ -155,7 +155,7 @@ void MeshMassPropertiesTests::testOpenTetrahedonMesh() {
|
||||||
void MeshMassPropertiesTests::testClosedTetrahedronMesh() {
|
void MeshMassPropertiesTests::testClosedTetrahedronMesh() {
|
||||||
// given a tetrahedron as a closed mesh of four tiangles
|
// given a tetrahedron as a closed mesh of four tiangles
|
||||||
// verify MeshMassProperties computes the right nubers
|
// verify MeshMassProperties computes the right nubers
|
||||||
|
|
||||||
// these numbers from the Tonon paper:
|
// these numbers from the Tonon paper:
|
||||||
VectorOfPoints points;
|
VectorOfPoints points;
|
||||||
points.push_back(btVector3(8.33220f, -11.86875f, 0.93355f));
|
points.push_back(btVector3(8.33220f, -11.86875f, 0.93355f));
|
||||||
|
@ -186,7 +186,7 @@ void MeshMassPropertiesTests::testClosedTetrahedronMesh() {
|
||||||
|
|
||||||
// compute mass properties
|
// compute mass properties
|
||||||
MeshMassProperties mesh(points, triangles);
|
MeshMassProperties mesh(points, triangles);
|
||||||
|
|
||||||
// verify
|
// verify
|
||||||
QCOMPARE_WITH_ABS_ERROR(mesh._volume, expectedVolume, acceptableRelativeError * expectedVolume);
|
QCOMPARE_WITH_ABS_ERROR(mesh._volume, expectedVolume, acceptableRelativeError * expectedVolume);
|
||||||
QCOMPARE_WITH_ABS_ERROR(mesh._centerOfMass, expectedCenterOfMass, acceptableAbsoluteError);
|
QCOMPARE_WITH_ABS_ERROR(mesh._centerOfMass, expectedCenterOfMass, acceptableAbsoluteError);
|
||||||
|
@ -210,7 +210,7 @@ void MeshMassPropertiesTests::testClosedTetrahedronMesh() {
|
||||||
|
|
||||||
void MeshMassPropertiesTests::testBoxAsMesh() {
|
void MeshMassPropertiesTests::testBoxAsMesh() {
|
||||||
// verify that a mesh box produces the same mass properties as the analytic box.
|
// verify that a mesh box produces the same mass properties as the analytic box.
|
||||||
|
|
||||||
// build a box:
|
// build a box:
|
||||||
// /
|
// /
|
||||||
// y
|
// y
|
||||||
|
@ -265,7 +265,7 @@ void MeshMassPropertiesTests::testBoxAsMesh() {
|
||||||
MeshMassProperties mesh(points, triangles);
|
MeshMassProperties mesh(points, triangles);
|
||||||
|
|
||||||
// verify
|
// verify
|
||||||
|
|
||||||
QCOMPARE_WITH_ABS_ERROR(mesh._volume, expectedVolume, acceptableRelativeError * expectedVolume);
|
QCOMPARE_WITH_ABS_ERROR(mesh._volume, expectedVolume, acceptableRelativeError * expectedVolume);
|
||||||
QCOMPARE_WITH_ABS_ERROR(mesh._centerOfMass, expectedCenterOfMass, acceptableAbsoluteError);
|
QCOMPARE_WITH_ABS_ERROR(mesh._centerOfMass, expectedCenterOfMass, acceptableAbsoluteError);
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ void ShapeManagerTests::testShapeAccounting() {
|
||||||
ShapeManager shapeManager;
|
ShapeManager shapeManager;
|
||||||
ShapeInfo info;
|
ShapeInfo info;
|
||||||
info.setBox(glm::vec3(1.0f, 1.0f, 1.0f));
|
info.setBox(glm::vec3(1.0f, 1.0f, 1.0f));
|
||||||
|
|
||||||
int numReferences = shapeManager.getNumReferences(info);
|
int numReferences = shapeManager.getNumReferences(info);
|
||||||
QCOMPARE(numReferences, 0);
|
QCOMPARE(numReferences, 0);
|
||||||
|
|
||||||
|
@ -42,10 +42,10 @@ void ShapeManagerTests::testShapeAccounting() {
|
||||||
QCOMPARE(numReferences, expectedNumReferences);
|
QCOMPARE(numReferences, expectedNumReferences);
|
||||||
|
|
||||||
// release all references
|
// release all references
|
||||||
bool released = shapeManager.releaseShape(info);
|
bool released = shapeManager.releaseShape(shape);
|
||||||
numReferences--;
|
numReferences--;
|
||||||
while (numReferences > 0) {
|
while (numReferences > 0) {
|
||||||
released = shapeManager.releaseShape(info) && released;
|
released = shapeManager.releaseShape(shape) && released;
|
||||||
numReferences--;
|
numReferences--;
|
||||||
}
|
}
|
||||||
QCOMPARE(released, true);
|
QCOMPARE(released, true);
|
||||||
|
@ -69,7 +69,7 @@ void ShapeManagerTests::testShapeAccounting() {
|
||||||
QCOMPARE(numReferences, 1);
|
QCOMPARE(numReferences, 1);
|
||||||
|
|
||||||
// release reference and verify that it is collected as garbage
|
// release reference and verify that it is collected as garbage
|
||||||
released = shapeManager.releaseShape(info);
|
released = shapeManager.releaseShape(shape);
|
||||||
shapeManager.collectGarbage();
|
shapeManager.collectGarbage();
|
||||||
QCOMPARE(shapeManager.getNumShapes(), 0);
|
QCOMPARE(shapeManager.getNumShapes(), 0);
|
||||||
QCOMPARE(shapeManager.hasShape(shape), false);
|
QCOMPARE(shapeManager.hasShape(shape), false);
|
||||||
|
@ -183,3 +183,58 @@ void ShapeManagerTests::addCapsuleShape() {
|
||||||
QCOMPARE(shape, otherShape);
|
QCOMPARE(shape, otherShape);
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShapeManagerTests::addCompoundShape() {
|
||||||
|
// initialize some points for generating tetrahedral convex hulls
|
||||||
|
QVector<glm::vec3> tetrahedron;
|
||||||
|
tetrahedron.push_back(glm::vec3(1.0f, 1.0f, 1.0f));
|
||||||
|
tetrahedron.push_back(glm::vec3(1.0f, -1.0f, -1.0f));
|
||||||
|
tetrahedron.push_back(glm::vec3(-1.0f, 1.0f, -1.0f));
|
||||||
|
tetrahedron.push_back(glm::vec3(-1.0f, -1.0f, 1.0f));
|
||||||
|
int numHullPoints = tetrahedron.size();
|
||||||
|
|
||||||
|
// compute the points of the hulls
|
||||||
|
QVector< QVector<glm::vec3> > hulls;
|
||||||
|
int numHulls = 5;
|
||||||
|
glm::vec3 offsetNormal(1.0f, 0.0f, 0.0f);
|
||||||
|
for (int i = 0; i < numHulls; ++i) {
|
||||||
|
glm::vec3 offset = (float)(i - numHulls/2) * offsetNormal;
|
||||||
|
QVector<glm::vec3> hull;
|
||||||
|
float radius = (float)(i + 1);
|
||||||
|
for (int j = 0; j < numHullPoints; ++j) {
|
||||||
|
glm::vec3 point = radius * tetrahedron[j] + offset;
|
||||||
|
hull.push_back(point);
|
||||||
|
}
|
||||||
|
hulls.push_back(hull);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the ShapeInfo
|
||||||
|
ShapeInfo info;
|
||||||
|
info.setConvexHulls(hulls);
|
||||||
|
|
||||||
|
// create the shape
|
||||||
|
ShapeManager shapeManager;
|
||||||
|
btCollisionShape* shape = shapeManager.getShape(info);
|
||||||
|
QVERIFY(shape != nullptr);
|
||||||
|
|
||||||
|
// verify the shape is correct type
|
||||||
|
QCOMPARE(shape->getShapeType(), (int)COMPOUND_SHAPE_PROXYTYPE);
|
||||||
|
|
||||||
|
// verify the shape has correct number of children
|
||||||
|
btCompoundShape* compoundShape = static_cast<btCompoundShape*>(shape);
|
||||||
|
QCOMPARE(compoundShape->getNumChildShapes(), numHulls);
|
||||||
|
|
||||||
|
// verify manager has only one shape
|
||||||
|
QCOMPARE(shapeManager.getNumShapes(), 1);
|
||||||
|
QCOMPARE(shapeManager.getNumReferences(info), 1);
|
||||||
|
|
||||||
|
// release the shape
|
||||||
|
shapeManager.releaseShape(shape);
|
||||||
|
QCOMPARE(shapeManager.getNumShapes(), 1);
|
||||||
|
QCOMPARE(shapeManager.getNumReferences(info), 0);
|
||||||
|
|
||||||
|
// collect garbage
|
||||||
|
shapeManager.collectGarbage();
|
||||||
|
QCOMPARE(shapeManager.getNumShapes(), 0);
|
||||||
|
QCOMPARE(shapeManager.getNumReferences(info), 0);
|
||||||
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
class ShapeManagerTests : public QObject {
|
class ShapeManagerTests : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void testShapeAccounting();
|
void testShapeAccounting();
|
||||||
void addManyShapes();
|
void addManyShapes();
|
||||||
|
@ -24,6 +24,7 @@ private slots:
|
||||||
void addSphereShape();
|
void addSphereShape();
|
||||||
void addCylinderShape();
|
void addCylinderShape();
|
||||||
void addCapsuleShape();
|
void addCapsuleShape();
|
||||||
|
void addCompoundShape();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_ShapeManagerTests_h
|
#endif // hifi_ShapeManagerTests_h
|
||||||
|
|
|
@ -77,7 +77,7 @@ UDTTest::UDTTest(int& argc, char** argv) :
|
||||||
|
|
||||||
// randomize the seed for packet size randomization
|
// randomize the seed for packet size randomization
|
||||||
srand(time(NULL));
|
srand(time(NULL));
|
||||||
|
|
||||||
_socket.bind(QHostAddress::AnyIPv4, _argumentParser.value(PORT_OPTION).toUInt());
|
_socket.bind(QHostAddress::AnyIPv4, _argumentParser.value(PORT_OPTION).toUInt());
|
||||||
qDebug() << "Test socket is listening on" << _socket.localPort();
|
qDebug() << "Test socket is listening on" << _socket.localPort();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue