resolve conflicts on merge with upstream master
128
examples/findParticleExample.js
Normal file
|
@ -0,0 +1,128 @@
|
|||
//
|
||||
// editParticleExample.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 1/24/14.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// This is an example script that demonstrates "finding" particles
|
||||
//
|
||||
|
||||
var iteration = 0;
|
||||
|
||||
var particleA = Particles.addParticle(
|
||||
{
|
||||
position: { x: 2, y: 0, z: 2 },
|
||||
velocity: { x: 0, y: 0, z: 0 },
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
radius : 0.1,
|
||||
color: { red: 0, green: 255, blue: 0 }
|
||||
});
|
||||
|
||||
var particleB = Particles.addParticle(
|
||||
{
|
||||
position: { x: 5, y: 0, z: 5 },
|
||||
velocity: { x: 0, y: 0, z: 0 },
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
radius : 0.1,
|
||||
color: { red: 0, green: 255, blue: 255 }
|
||||
});
|
||||
|
||||
var searchAt = { x: 0, y: 0, z: 0};
|
||||
var moveSearch = { x: 0.1, y: 0, z: 0.1};
|
||||
var searchRadius = 1;
|
||||
var searchRadiusChange = 0;
|
||||
|
||||
print("particleA.creatorTokenID = " + particleA.creatorTokenID);
|
||||
print("particleB.creatorTokenID = " + particleB.creatorTokenID);
|
||||
|
||||
|
||||
function scriptEnding() {
|
||||
print("calling Particles.deleteParticle()");
|
||||
Particles.deleteParticle(particleA);
|
||||
Particles.deleteParticle(particleB);
|
||||
}
|
||||
|
||||
function printProperties(properties) {
|
||||
for (var property in properties) {
|
||||
if (properties.hasOwnProperty(property)) {
|
||||
if (property == "position" ||
|
||||
property == "gravity" ||
|
||||
property == "velocity") {
|
||||
print(property +": " + properties[property].x + ", " + properties[property].y + ", " + properties[property].z);
|
||||
} else if (property == "color") {
|
||||
print(property +": " + properties[property].red + ", "
|
||||
+ properties[property].green + ", " + properties[property].blue);
|
||||
} else {
|
||||
print(property +": " + properties[property])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function findParticles() {
|
||||
|
||||
// run for a while, then clean up
|
||||
// stop it...
|
||||
if (iteration >= 100) {
|
||||
print("calling Agent.stop()");
|
||||
Agent.stop();
|
||||
}
|
||||
|
||||
print("--------------------------");
|
||||
print("iteration =" + iteration);
|
||||
iteration++;
|
||||
|
||||
// Check to see if we've been notified of the actual identity of the particles we created
|
||||
if (!particleA.isKnownID) {
|
||||
var identifyA = Particles.identifyParticle(particleA);
|
||||
if (identifyA.isKnownID) {
|
||||
particleA = identifyA;
|
||||
print(">>>> identified particleA.id = " + particleA.id);
|
||||
}
|
||||
}
|
||||
if (!particleB.isKnownID) {
|
||||
var identifyB = Particles.identifyParticle(particleB);
|
||||
if (identifyB.isKnownID) {
|
||||
particleB = identifyB;
|
||||
print(">>>> identified particleB.id = " + particleB.id);
|
||||
}
|
||||
}
|
||||
|
||||
// also check to see if we can "find" particles...
|
||||
print("searching for particles at:" + searchAt.x + ", " + searchAt.y + ", " + searchAt.z + " radius:" + searchRadius);
|
||||
var foundParticles = Particles.findParticles(searchAt, searchRadius);
|
||||
print("found this many particles: "+ foundParticles.length);
|
||||
for (var i = 0; i < foundParticles.length; i++) {
|
||||
print(" particle[" + i + "].id:" + foundParticles[i].id);
|
||||
if (foundParticles[i].id == particleA.id) {
|
||||
print(">>>> found particleA!!");
|
||||
var propertiesA = Particles.getParticleProperties(particleA);
|
||||
printProperties(propertiesA);
|
||||
}
|
||||
if (foundParticles[i].id == particleB.id) {
|
||||
print(">>>> found particleB!!");
|
||||
}
|
||||
}
|
||||
// move search
|
||||
searchAt.x += moveSearch.x;
|
||||
searchAt.y += moveSearch.y;
|
||||
searchAt.z += moveSearch.z;
|
||||
searchRadius += searchRadiusChange;
|
||||
|
||||
// after we've searched for 80 iterations, change our search mechanism to be from the center with expanding radius
|
||||
if (iteration == 80) {
|
||||
searchAt = { x: 3.5, y: 0, z: 3.5};
|
||||
moveSearch = { x: 0, y: 0, z: 0};
|
||||
searchRadius = 0.5;
|
||||
searchRadiusChange = 0.5;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// register the call back so it fires before each data send
|
||||
Agent.willSendVisualDataCallback.connect(findParticles);
|
||||
|
||||
// register our scriptEnding callback
|
||||
Agent.scriptEnding.connect(scriptEnding);
|
174
examples/particleBird.js
Normal file
|
@ -0,0 +1,174 @@
|
|||
//
|
||||
// particleBird.js
|
||||
// hifi
|
||||
//
|
||||
// This sample script moves a voxel around like a bird and sometimes makes tweeting noises
|
||||
//
|
||||
function vLength(v) {
|
||||
return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
|
||||
}
|
||||
function printVector(v) {
|
||||
print(v.x + ", " + v.y + ", " + v.z + "\n");
|
||||
}
|
||||
// Create a random vector with individual lengths between a,b
|
||||
function randVector(a, b) {
|
||||
var rval = { x: a + Math.random() * (b - a), y: a + Math.random() * (b - a), z: a + Math.random() * (b - a) };
|
||||
return rval;
|
||||
}
|
||||
function vMinus(a, b) {
|
||||
var rval = { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z };
|
||||
return rval;
|
||||
}
|
||||
function vPlus(a, b) {
|
||||
var rval = { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z };
|
||||
return rval;
|
||||
}
|
||||
function vCopy(a, b) {
|
||||
a.x = b.x;
|
||||
a.y = b.y;
|
||||
a.z = b.z;
|
||||
return;
|
||||
}
|
||||
// Returns a vector which is fraction of the way between a and b
|
||||
function vInterpolate(a, b, fraction) {
|
||||
var rval = { x: a.x + (b.x - a.x) * fraction, y: a.y + (b.y - a.y) * fraction, z: a.z + (b.z - a.z) * fraction };
|
||||
return rval;
|
||||
}
|
||||
|
||||
// Decide what kind of bird we are
|
||||
var tweet;
|
||||
var color;
|
||||
var size;
|
||||
var which = Math.random();
|
||||
if (which < 0.2) {
|
||||
tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/bushtit_1.raw");
|
||||
color = { r: 100, g: 50, b: 120 };
|
||||
size = 0.08;
|
||||
} else if (which < 0.4) {
|
||||
tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/rosyfacedlovebird.raw");
|
||||
color = { r: 100, g: 150, b: 75 };
|
||||
size = 0.09;
|
||||
} else if (which < 0.6) {
|
||||
tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/saysphoebe.raw");
|
||||
color = { r: 84, g: 121, b: 36 };
|
||||
size = 0.05;
|
||||
} else if (which < 0.8) {
|
||||
tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/mexicanWhipoorwill.raw");
|
||||
color = { r: 23, g: 197, b: 230 };
|
||||
size = 0.12;
|
||||
} else {
|
||||
tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/westernscreechowl.raw");
|
||||
color = { r: 50, g: 67, b: 144 };
|
||||
size = 0.15;
|
||||
}
|
||||
|
||||
var startTimeInSeconds = new Date().getTime() / 1000;
|
||||
|
||||
var birdLifetime = 20; // lifetime of the bird in seconds!
|
||||
var position = { x: 0, y: 0, z: 0 };
|
||||
var targetPosition = { x: 0, y: 0, z: 0 };
|
||||
var range = 1.0; // Over what distance in meters do you want your bird to fly around
|
||||
var frame = 0;
|
||||
var moving = false;
|
||||
var tweeting = 0;
|
||||
var moved = false;
|
||||
var CHANCE_OF_MOVING = 0.05;
|
||||
var CHANCE_OF_TWEETING = 0.05;
|
||||
var START_HEIGHT_ABOVE_ME = 1.5;
|
||||
var myPosition = MyAvatar.position;
|
||||
var properties = {
|
||||
lifetime: birdLifetime,
|
||||
position: { x: myPosition.x, y: myPosition.y + START_HEIGHT_ABOVE_ME, z: myPosition.z },
|
||||
velocity: { x: 0, y: 0, z: 0 },
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
radius : 0.1,
|
||||
color: { red: 0,
|
||||
green: 255,
|
||||
blue: 0 }
|
||||
};
|
||||
var range = 1.0; // Distance around avatar where I can move
|
||||
// Create the actual bird
|
||||
var particleID = Particles.addParticle(properties);
|
||||
function moveBird() {
|
||||
|
||||
// check to see if we've been running long enough that our bird is dead
|
||||
var nowTimeInSeconds = new Date().getTime() / 1000;
|
||||
if ((nowTimeInSeconds - startTimeInSeconds) >= birdLifetime) {
|
||||
print("our bird is dying, stop our script");
|
||||
Agent.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
myPosition = MyAvatar.position;
|
||||
frame++;
|
||||
if (frame % 3 == 0) {
|
||||
// Tweeting behavior
|
||||
if (tweeting == 0) {
|
||||
if (Math.random() < CHANCE_OF_TWEETING) {
|
||||
//print("tweet!" + "\n");
|
||||
var options = new AudioInjectionOptions();
|
||||
options.position = position;
|
||||
options.volume = 0.75;
|
||||
Audio.playSound(tweet, options);
|
||||
tweeting = 10;
|
||||
}
|
||||
} else {
|
||||
tweeting -= 1;
|
||||
}
|
||||
// Moving behavior
|
||||
if (moving == false) {
|
||||
if (Math.random() < CHANCE_OF_MOVING) {
|
||||
targetPosition = randVector(- range, range);
|
||||
targetPosition = vPlus(targetPosition, myPosition);
|
||||
if (targetPosition.x < 0) {
|
||||
targetPosition.x = 0;
|
||||
}
|
||||
if (targetPosition.y < 0) {
|
||||
targetPosition.y = 0;
|
||||
}
|
||||
if (targetPosition.z < 0) {
|
||||
targetPosition.z = 0;
|
||||
}
|
||||
if (targetPosition.x > TREE_SCALE) {
|
||||
targetPosition.x = TREE_SCALE;
|
||||
}
|
||||
if (targetPosition.y > TREE_SCALE) {
|
||||
targetPosition.y = TREE_SCALE;
|
||||
}
|
||||
if (targetPosition.z > TREE_SCALE) {
|
||||
targetPosition.z = TREE_SCALE;
|
||||
}
|
||||
//printVector(position);
|
||||
moving = true;
|
||||
}
|
||||
}
|
||||
if (moving) {
|
||||
position = vInterpolate(position, targetPosition, 0.5);
|
||||
if (vLength(vMinus(position, targetPosition)) < (size / 5.0)) {
|
||||
moved = false;
|
||||
moving = false;
|
||||
} else {
|
||||
moved = true;
|
||||
}
|
||||
}
|
||||
if (moved || (tweeting > 0)) {
|
||||
if (tweeting > 0) {
|
||||
var newProperties = {
|
||||
position: position,
|
||||
radius : size * 1.5,
|
||||
color: { red: Math.random() * 255, green: 0, blue: 0 }
|
||||
};
|
||||
} else {
|
||||
var newProperties = {
|
||||
position: position,
|
||||
radius : size,
|
||||
color: { red: color.r, green: color.g, blue: color.b }
|
||||
};
|
||||
}
|
||||
Particles.editParticle(particleID, newProperties);
|
||||
moved = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// register the call back so it fires before each data send
|
||||
Agent.willSendVisualDataCallback.connect(moveBird);
|
19
interface/resources/config/config.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"importFormats" : [
|
||||
{
|
||||
"extension": "png",
|
||||
"description": "Square PNG",
|
||||
"icon": "raster.svg"
|
||||
},
|
||||
{
|
||||
"extension": "svo",
|
||||
"description": "Sparse Voxel Octree Files",
|
||||
"icon": "voxel.svg"
|
||||
},
|
||||
{
|
||||
"extension": "schematic",
|
||||
"description": "Schematic Files",
|
||||
"icon": "voxel.svg"
|
||||
}
|
||||
]
|
||||
}
|
12
interface/resources/icons/computer.svg
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve">
|
||||
<g>
|
||||
<path fill="#666666" d="M18.223,11.845c0,0.835-0.683,1.519-1.519,1.519h-5.163c0,0.807,0.607,1.49,0.607,1.822
|
||||
s-0.275,0.607-0.607,0.607H6.682c-0.332,0-0.607-0.275-0.607-0.607c0-0.351,0.607-0.997,0.607-1.822H1.519
|
||||
C0.683,13.363,0,12.68,0,11.845V1.519C0,0.684,0.683,0,1.519,0h15.186c0.835,0,1.519,0.684,1.519,1.519V11.845z M17.008,1.519
|
||||
c0-0.161-0.143-0.304-0.304-0.304H1.519c-0.161,0-0.304,0.143-0.304,0.304v7.896c0,0.161,0.143,0.304,0.304,0.304h15.186
|
||||
c0.161,0,0.304-0.143,0.304-0.304V1.519z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 884 B |
17
interface/resources/icons/desktop.svg
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<rect fill="#666666" width="17" height="1"/>
|
||||
</g>
|
||||
<g>
|
||||
<rect y="2" fill="#666666" width="17" height="11"/>
|
||||
</g>
|
||||
<rect x="1" y="4" fill="#989898" width="1" height="1"/>
|
||||
<rect x="1" y="6" fill="#989898" width="1" height="1"/>
|
||||
<rect x="4" y="4" fill="#989898" width="1" height="1"/>
|
||||
<rect x="4" y="6" fill="#989898" width="1" height="1"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 732 B |
15
interface/resources/icons/documents.svg
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#666666" d="M17.615,4.556v11.541c0,0.503-0.408,0.911-0.911,0.911H7.593c-0.503,0-0.911-0.408-0.911-0.911v-2.733
|
||||
H1.519c-0.503,0-0.911-0.408-0.911-0.911V6.074c0-0.503,0.295-1.206,0.646-1.557l3.872-3.872C5.477,0.294,6.179,0,6.682,0h3.948
|
||||
c0.503,0,0.911,0.408,0.911,0.911v3.113c0.37-0.219,0.845-0.38,1.215-0.38h3.948C17.207,3.645,17.615,4.053,17.615,4.556z
|
||||
M10.326,5.163V1.215H6.682v3.948c0,0.503-0.408,0.911-0.911,0.911H1.822v6.074h4.859v-2.43c0-0.503,0.295-1.206,0.646-1.557
|
||||
L10.326,5.163z M2.629,4.859h2.838V2.021L2.629,4.859z M16.4,4.859h-3.645v3.948c0,0.503-0.408,0.911-0.911,0.911H7.896v6.074
|
||||
H16.4V4.859z M8.703,8.504h2.838V5.666L8.703,8.504z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1 KiB |
14
interface/resources/icons/file.svg
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve">
|
||||
<g>
|
||||
<path fill="#666666" d="M12.148,13.667c0,0.503-0.408,0.911-0.911,0.911H0.911C0.408,14.578,0,14.17,0,13.667V0.911
|
||||
C0,0.408,0.408,0,0.911,0h6.074c0.503,0,1.206,0.294,1.557,0.646l2.961,2.961c0.351,0.351,0.646,1.054,0.646,1.557V13.667z
|
||||
M10.934,13.363V6.074H6.985c-0.503,0-0.911-0.408-0.911-0.911V1.215H1.215v12.148H10.934z M9.719,8.2
|
||||
c0,0.171-0.133,0.304-0.304,0.304H2.733C2.562,8.504,2.43,8.371,2.43,8.2V7.593c0-0.171,0.133-0.304,0.304-0.304h6.682
|
||||
c0.171,0,0.304,0.133,0.304,0.304V8.2z M9.719,10.63c0,0.171-0.133,0.304-0.304,0.304H2.733c-0.171,0-0.304-0.133-0.304-0.304
|
||||
v-0.607c0-0.171,0.133-0.304,0.304-0.304h6.682c0.171,0,0.304,0.133,0.304,0.304V10.63z M10.857,4.859
|
||||
c-0.057-0.161-0.142-0.323-0.208-0.389L7.678,1.5C7.612,1.433,7.45,1.348,7.289,1.291v3.568H10.857z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
11
interface/resources/icons/folder.svg
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve">
|
||||
<g>
|
||||
<path fill="#666666" d="M14.854,10.569c0,1.098-0.901,2-2,2H2c-1.098,0-2-0.901-2-2V2c0-1.098,0.901-2,2-2h2.856
|
||||
c1.098,0,2,0.901,2,2v0.286h5.999c1.098,0,2,0.901,2,2V10.569z M13.711,4.285c0-0.473-0.384-0.857-0.857-0.857H6.57
|
||||
c-0.473,0-0.857-0.384-0.857-0.857V2c0-0.473-0.384-0.857-0.857-0.857H2C1.526,1.143,1.143,1.526,1.143,2v8.569
|
||||
c0,0.473,0.384,0.857,0.857,0.857h10.854c0.473,0,0.857-0.384,0.857-0.857V4.285z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 800 B |
16
interface/resources/icons/home.svg
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#666666" d="M16.191,7.893c-0.054,0.063-0.139,0.106-0.223,0.116c-0.011,0-0.021,0-0.032,0
|
||||
c-0.085,0-0.159-0.021-0.223-0.074L8.375,1.816L1.037,7.935C0.963,7.988,0.878,8.02,0.782,8.009
|
||||
c-0.085-0.01-0.17-0.053-0.223-0.116l-0.657-0.785c-0.117-0.138-0.096-0.36,0.042-0.477l7.625-6.352
|
||||
c0.445-0.372,1.166-0.372,1.612,0l2.587,2.163V0.374c0-0.191,0.148-0.339,0.34-0.339h2.036c0.19,0,0.339,0.148,0.339,0.339v4.327
|
||||
l2.322,1.93c0.139,0.117,0.159,0.339,0.043,0.477L16.191,7.893z M14.483,12.93c0,0.371-0.308,0.679-0.679,0.679H9.732V9.536H7.018
|
||||
v4.072H2.945c-0.371,0-0.679-0.308-0.679-0.679V7.84c0-0.021,0.011-0.042,0.011-0.063l6.098-5.027l6.098,5.027
|
||||
c0.011,0.021,0.011,0.042,0.011,0.063V12.93z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
12
interface/resources/icons/raster.svg
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve">
|
||||
<g>
|
||||
<path fill="#666666" d="M15.049,10.75c0,0.688-0.562,1.25-1.25,1.25h-12.5c-0.688,0-1.25-0.562-1.25-1.25v-9.5
|
||||
C0.049,0.562,0.611,0,1.299,0h12.5c0.688,0,1.25,0.562,1.25,1.25V10.75z M1.299,1c-0.133,0-0.25,0.117-0.25,0.25v9.5
|
||||
c0,0.133,0.117,0.25,0.25,0.25h12.5c0.133,0,0.25-0.117,0.25-0.25v-9.5c0-0.133-0.117-0.25-0.25-0.25H1.299z M3.549,5
|
||||
c-0.828,0-1.5-0.672-1.5-1.5S2.72,2,3.549,2s1.5,0.672,1.5,1.5S4.377,5,3.549,5z M13.049,10h-11V8.5l2.5-2.5l1.25,1.25l4-4
|
||||
l3.25,3.25V10z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 862 B |
18
interface/resources/icons/voxel.svg
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#666666" d="M13.402,4.612c0-0.098-0.052-0.188-0.137-0.237c-0.085-0.049-0.189-0.049-0.274,0L7.704,7.428
|
||||
C7.423,7.59,7.25,7.89,7.25,8.215v6.105c0,0.098,0.052,0.188,0.137,0.237c0.085,0.049,0.189,0.049,0.274,0l5.085-2.936
|
||||
c0.407-0.235,0.657-0.668,0.657-1.138V4.612z"/>
|
||||
<path fill="#666666" d="M0.41,4.375c-0.085-0.049-0.189-0.049-0.274,0C0.052,4.424,0,4.514,0,4.612c0,0,0,0,0,0v5.872
|
||||
c0,0.469,0.25,0.903,0.657,1.138l5.085,2.936c0,0,0,0,0,0c0.085,0.049,0.189,0.049,0.274,0c0.085-0.049,0.136-0.139,0.136-0.237
|
||||
V8.215c0-0.325-0.173-0.625-0.455-0.787L0.41,4.375z"/>
|
||||
</g>
|
||||
<path fill="#666666" d="M12.443,3.586c0.085-0.049,0.137-0.139,0.137-0.237c0-0.098-0.052-0.188-0.137-0.237c0,0,0,0,0,0
|
||||
L7.358,0.176c-0.407-0.235-0.907-0.235-1.313,0L0.959,3.112c0,0,0,0,0,0C0.874,3.161,0.822,3.251,0.822,3.349
|
||||
c0,0.098,0.052,0.188,0.137,0.237l5.287,3.053c0.281,0.162,0.628,0.162,0.909,0L12.443,3.586z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
BIN
interface/resources/sounds/snap.wav
Normal file
88
interface/resources/styles/import_dialog.qss
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* import_dialog.qss
|
||||
* hifi
|
||||
*
|
||||
* Created by Stojce on 1/5/2014.
|
||||
* Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
* {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
QLabel {
|
||||
font-size: 16px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
QLabel#infoLabel {
|
||||
padding: 7px 0 0 7px;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
QPushButton {
|
||||
border-width: 0;
|
||||
border-radius: 9px;
|
||||
font-size: 18px;
|
||||
padding: 17px 0px 15px;
|
||||
width: 107px;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
QPushButton#importButton {
|
||||
color: #FFFFFF;
|
||||
font-weight: bold;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
QPushButton#importButton:enabled {
|
||||
background: #333333;
|
||||
}
|
||||
|
||||
QPushButton#importButton:!enabled {
|
||||
background: rgba(50, 50, 50, 0.5);
|
||||
}
|
||||
|
||||
QPushButton#cancelButton {
|
||||
color: #333333;
|
||||
background-color: #FFFFFF;
|
||||
width: 74px;
|
||||
margin-right: 11px;
|
||||
}
|
||||
|
||||
QSidebar, QTreeView {
|
||||
border: 1px solid #C5C5C5;
|
||||
font-size: 14px;
|
||||
margin: 0px;
|
||||
selection-background-color: #BDE4E3;
|
||||
selection-color: #333333;
|
||||
|
||||
}
|
||||
|
||||
QTreeView {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
QSplitter::handle, QDialog {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
QTreeView QHeaderView {
|
||||
background: white;
|
||||
}
|
||||
|
||||
QTreeView QHeaderView:section {
|
||||
border-left: none;
|
||||
border-top: none;
|
||||
border-bottom: 1px solid #C5C5C5;
|
||||
border-right: 1px solid #C5C5C5;
|
||||
background: white;
|
||||
color: #666666;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
QSidebar::item,
|
||||
QTreeView::item {
|
||||
padding: 5px 0 4px;
|
||||
}
|
|
@ -47,6 +47,7 @@
|
|||
#include <QDesktopServices>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamAttributes>
|
||||
#include <QMediaPlayer>
|
||||
|
||||
#include <AudioInjector.h>
|
||||
#include <Logging.h>
|
||||
|
@ -69,6 +70,7 @@
|
|||
#include "renderer/ProgramObject.h"
|
||||
#include "ui/TextRenderer.h"
|
||||
#include "InfoView.h"
|
||||
#include "ui/Snapshot.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -97,6 +99,8 @@ const float MIRROR_REARVIEW_BODY_DISTANCE = 2.3f;
|
|||
const QString CHECK_VERSION_URL = "http://highfidelity.io/latestVersion.xml";
|
||||
const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion";
|
||||
|
||||
const int STATS_PELS_PER_LINE = 20;
|
||||
|
||||
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message) {
|
||||
QString messageWithNewLine = message + "\n";
|
||||
fprintf(stdout, "%s", messageWithNewLine.toLocal8Bit().constData());
|
||||
|
@ -107,6 +111,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
QApplication(argc, argv),
|
||||
_window(new QMainWindow(desktop())),
|
||||
_glWidget(new GLCanvas()),
|
||||
_statsExpanded(false),
|
||||
_nodeThread(new QThread(this)),
|
||||
_datagramProcessor(),
|
||||
_frameCount(0),
|
||||
|
@ -249,6 +254,10 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
|
||||
restoreSizeAndPosition();
|
||||
loadScripts();
|
||||
|
||||
QFontDatabase fontDatabase;
|
||||
fontDatabase.addApplicationFont("resources/styles/Inconsolata.otf");
|
||||
|
||||
_window->setVisible(true);
|
||||
_glWidget->setFocusPolicy(Qt::StrongFocus);
|
||||
_glWidget->setFocus();
|
||||
|
@ -764,6 +773,8 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
_voxels.collectStatsForTreesAndVBOs();
|
||||
} else if (isShifted && isMeta) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::SuppressShortTimings);
|
||||
} else if (!isShifted && isMeta) {
|
||||
takeSnapshot();
|
||||
} else if (_nudgeStarted) {
|
||||
if (_lookingAlongX) {
|
||||
if (_lookingAwayFromOrigin) {
|
||||
|
@ -1258,6 +1269,9 @@ void Application::mouseReleaseEvent(QMouseEvent* event) {
|
|||
_mouseY = event->y();
|
||||
_mousePressed = false;
|
||||
checkBandwidthMeterClick();
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
|
||||
checkStatsClick();
|
||||
}
|
||||
|
||||
_pieMenu.mouseReleaseEvent(_mouseX, _mouseY);
|
||||
}
|
||||
|
@ -1718,7 +1732,7 @@ void Application::init() {
|
|||
_sharedVoxelSystem.changeTree(&_clipboard);
|
||||
delete tmpTree;
|
||||
|
||||
_voxelImporter.init();
|
||||
_voxelImporter.init(_settings);
|
||||
|
||||
_environment.init();
|
||||
|
||||
|
@ -2962,9 +2976,11 @@ void Application::displayOverlay() {
|
|||
}
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
|
||||
displayStatsBackground(0x33333399, 0, _glWidget->height() - 68, 296, 68);
|
||||
_audio.render(_glWidget->width(), _glWidget->height());
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Oscilloscope)) {
|
||||
_audioScope.render(45, _glWidget->height() - 200);
|
||||
int oscilloscopeTop = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) ? 130 : 25;
|
||||
_audioScope.render(25, oscilloscopeTop);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3019,17 +3035,9 @@ void Application::displayOverlay() {
|
|||
displayStats();
|
||||
// Bandwidth meter
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth)) {
|
||||
displayStatsBackground(0x33333399, _glWidget->width() - 296, _glWidget->height() - 68, 296, 68);
|
||||
_bandwidthMeter.render(_glWidget->width(), _glWidget->height());
|
||||
}
|
||||
// Stats at upper right of screen about who domain server is telling us about
|
||||
glPointSize(1.0f);
|
||||
char nodes[100];
|
||||
|
||||
int totalAvatars = _avatarManager.size();
|
||||
int totalServers = NodeList::getInstance()->size();
|
||||
|
||||
sprintf(nodes, "Servers: %d, Avatars: %d\n", totalServers, totalAvatars);
|
||||
drawtext(_glWidget->width() - 150, 20, 0.10f, 0, 1.0f, 0, nodes, 1, 0, 0);
|
||||
}
|
||||
|
||||
// testing rendering coverage map
|
||||
|
@ -3051,8 +3059,12 @@ void Application::displayOverlay() {
|
|||
char frameTimer[10];
|
||||
uint64_t mSecsNow = floor(usecTimestampNow() / 1000.0 + 0.5);
|
||||
sprintf(frameTimer, "%d\n", (int)(mSecsNow % 1000));
|
||||
drawtext(_glWidget->width() - 100, _glWidget->height() - 20, 0.30f, 0, 1.0f, 0, frameTimer, 0, 0, 0);
|
||||
drawtext(_glWidget->width() - 102, _glWidget->height() - 22, 0.30f, 0, 1.0f, 0, frameTimer, 1, 1, 1);
|
||||
int timerBottom =
|
||||
(Menu::getInstance()->isOptionChecked(MenuOption::Stats) &&
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth))
|
||||
? 80 : 20;
|
||||
drawtext(_glWidget->width() - 100, _glWidget->height() - timerBottom, 0.30f, 0, 1.0f, 0, frameTimer, 0, 0, 0);
|
||||
drawtext(_glWidget->width() - 102, _glWidget->height() - timerBottom - 2, 0.30f, 0, 1.0f, 0, frameTimer, 1, 1, 1);
|
||||
}
|
||||
|
||||
_palette.render(_glWidget->width(), _glWidget->height());
|
||||
|
@ -3110,15 +3122,73 @@ void Application::displayOverlay() {
|
|||
glPopMatrix();
|
||||
}
|
||||
|
||||
// translucent background box that makes stats more readable
|
||||
void Application::displayStatsBackground(unsigned int rgba, int x, int y, int width, int height) {
|
||||
glBegin(GL_QUADS);
|
||||
glColor4f(((rgba >> 24) & 0xff) / 255.0f,
|
||||
((rgba >> 16) & 0xff) / 255.0f,
|
||||
((rgba >> 8) & 0xff) / 255.0f,
|
||||
(rgba & 0xff) / 255.0f);
|
||||
glVertex3f(x, y, 0);
|
||||
glVertex3f(x + width, y, 0);
|
||||
glVertex3f(x + width, y + height, 0);
|
||||
glVertex3f(x , y + height, 0);
|
||||
glEnd();
|
||||
glColor4f(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
// display expanded or contracted stats
|
||||
void Application::displayStats() {
|
||||
int statsVerticalOffset = 8;
|
||||
const int PELS_PER_LINE = 15;
|
||||
char stats[200];
|
||||
statsVerticalOffset += PELS_PER_LINE;
|
||||
sprintf(stats, "%3.0f FPS, %d Pkts/sec, %3.2f Mbps ",
|
||||
_fps, _packetsPerSecond, (float)_bytesPerSecond * 8.f / 1000000.f);
|
||||
drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, stats);
|
||||
unsigned int backgroundColor = 0x33333399;
|
||||
int verticalOffset = 0, horizontalOffset = 0, lines = 0;
|
||||
bool mirrorEnabled = Menu::getInstance()->isOptionChecked(MenuOption::Mirror);
|
||||
|
||||
QLocale locale(QLocale::English);
|
||||
std::stringstream voxelStats;
|
||||
|
||||
glPointSize(1.0f);
|
||||
|
||||
int totalAvatars = 0, totalServers = 0;
|
||||
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
|
||||
node->getType() == NODE_TYPE_AGENT ? totalAvatars++ : totalServers++;
|
||||
}
|
||||
|
||||
if (mirrorEnabled) {
|
||||
horizontalOffset += MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2;
|
||||
}
|
||||
|
||||
lines = _statsExpanded ? 5 : 3;
|
||||
displayStatsBackground(backgroundColor, horizontalOffset, 0, 165, lines * STATS_PELS_PER_LINE + 10);
|
||||
horizontalOffset += 5;
|
||||
|
||||
char serverNodes[30];
|
||||
sprintf(serverNodes, "Servers: %d", totalServers);
|
||||
char avatarNodes[30];
|
||||
sprintf(avatarNodes, "Avatars: %d", totalAvatars);
|
||||
char framesPerSecond[30];
|
||||
sprintf(framesPerSecond, "Framerate: %3.0f FPS", _fps);
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0f, 2, serverNodes, .93f, .93f, .93f);
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0f, 2, avatarNodes, .93f, .93f, .93f);
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, framesPerSecond, .93f, .93f, .93f);
|
||||
|
||||
if (_statsExpanded) {
|
||||
char packetsPerSecond[30];
|
||||
sprintf(packetsPerSecond, "Pkts/sec: %d", _packetsPerSecond);
|
||||
char averageMegabitsPerSecond[30];
|
||||
sprintf(averageMegabitsPerSecond, "Avg Mbps: %3.2f", (float)_bytesPerSecond * 8.f / 1000000.f);
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, packetsPerSecond, .93f, .93f, .93f);
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, averageMegabitsPerSecond, .93f, .93f, .93f);
|
||||
}
|
||||
|
||||
verticalOffset = 0;
|
||||
horizontalOffset += 161;
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
|
||||
int pingAudio = 0, pingAvatar = 0, pingVoxel = 0, pingVoxelMax = 0;
|
||||
|
@ -3130,7 +3200,6 @@ void Application::displayStats() {
|
|||
pingAudio = audioMixerNode ? audioMixerNode->getPingMs() : 0;
|
||||
pingAvatar = avatarMixerNode ? avatarMixerNode->getPingMs() : 0;
|
||||
|
||||
|
||||
// Now handle voxel servers, since there could be more than one, we average their ping times
|
||||
unsigned long totalPingVoxel = 0;
|
||||
int voxelServerCount = 0;
|
||||
|
@ -3149,35 +3218,115 @@ void Application::displayStats() {
|
|||
pingVoxel = totalPingVoxel/voxelServerCount;
|
||||
}
|
||||
|
||||
char pingStats[200];
|
||||
statsVerticalOffset += PELS_PER_LINE;
|
||||
sprintf(pingStats, "Ping audio/avatar/voxel: %d / %d / %d avg %d max ", pingAudio, pingAvatar, pingVoxel, pingVoxelMax);
|
||||
drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, pingStats);
|
||||
lines = _statsExpanded ? 4 : 3;
|
||||
displayStatsBackground(backgroundColor, horizontalOffset, 0, 175, lines * STATS_PELS_PER_LINE + 10);
|
||||
horizontalOffset += 5;
|
||||
|
||||
char audioPing[30];
|
||||
sprintf(audioPing, "Audio ping: %d", pingAudio);
|
||||
char avatarPing[30];
|
||||
sprintf(avatarPing, "Avatar ping: %d", pingAvatar);
|
||||
char voxelAvgPing[30];
|
||||
sprintf(voxelAvgPing, "Voxel avg ping: %d", pingVoxel);
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, audioPing, .93f, .93f, .93f);
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, avatarPing, .93f, .93f, .93f);
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, voxelAvgPing, .93f, .93f, .93f);
|
||||
|
||||
if (_statsExpanded) {
|
||||
char voxelMaxPing[30];
|
||||
sprintf(voxelMaxPing, "Voxel max ping: %d", pingVoxelMax);
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, voxelMaxPing, .93f, .93f, .93f);
|
||||
}
|
||||
|
||||
verticalOffset = 0;
|
||||
horizontalOffset += 171;
|
||||
}
|
||||
|
||||
char avatarStats[200];
|
||||
statsVerticalOffset += PELS_PER_LINE;
|
||||
glm::vec3 avatarPos = _myAvatar.getPosition();
|
||||
sprintf(avatarStats, "Avatar: pos %.3f, %.3f, %.3f, vel %.1f, yaw = %.2f", avatarPos.x, avatarPos.y, avatarPos.z, glm::length(_myAvatar.getVelocity()), _myAvatar.getBodyYaw());
|
||||
drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, avatarStats);
|
||||
|
||||
SharedNodePointer avatarMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AVATAR_MIXER);
|
||||
char avatarMixerStats[200];
|
||||
if (avatarMixer) {
|
||||
sprintf(avatarMixerStats, "Avatar Mixer: %.f kbps, %.f pps",
|
||||
roundf(avatarMixer->getAverageKilobitsPerSecond()),
|
||||
roundf(avatarMixer->getAveragePacketsPerSecond()));
|
||||
lines = _statsExpanded ? 4 : 3;
|
||||
displayStatsBackground(backgroundColor, horizontalOffset, 0, _glWidget->width() - (mirrorEnabled ? 301 : 411) - horizontalOffset, lines * STATS_PELS_PER_LINE + 10);
|
||||
horizontalOffset += 5;
|
||||
|
||||
char avatarPosition[200];
|
||||
if (mirrorEnabled) {
|
||||
// shorthand formatting
|
||||
sprintf(avatarPosition, "Pos: %.0f,%.0f,%.0f", avatarPos.x, avatarPos.y, avatarPos.z);
|
||||
} else {
|
||||
sprintf(avatarMixerStats, "No Avatar Mixer");
|
||||
// longhand way
|
||||
sprintf(avatarPosition, "Position: %.3f, %.3f, %.3f", avatarPos.x, avatarPos.y, avatarPos.z);
|
||||
}
|
||||
char avatarVelocity[30];
|
||||
sprintf(avatarVelocity, "Velocity: %.1f", glm::length(_myAvatar.getVelocity()));
|
||||
char avatarBodyYaw[30];
|
||||
sprintf(avatarBodyYaw, "Yaw: %.2f", _myAvatar.getBodyYaw());
|
||||
char avatarMixerStats[200];
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, avatarPosition, .93f, .93f, .93f);
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, avatarVelocity, .93f, .93f, .93f);
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, avatarBodyYaw, .93f, .93f, .93f);
|
||||
|
||||
if (_statsExpanded) {
|
||||
SharedNodePointer avatarMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AVATAR_MIXER);
|
||||
if (avatarMixer) {
|
||||
sprintf(avatarMixerStats, "Avatar Mixer: %.f kbps, %.f pps",
|
||||
roundf(avatarMixer->getAverageKilobitsPerSecond()),
|
||||
roundf(avatarMixer->getAveragePacketsPerSecond()));
|
||||
} else {
|
||||
sprintf(avatarMixerStats, "No Avatar Mixer");
|
||||
}
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, avatarMixerStats, .93f, .93f, .93f);
|
||||
}
|
||||
statsVerticalOffset += PELS_PER_LINE;
|
||||
drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, avatarMixerStats);
|
||||
|
||||
verticalOffset = 0;
|
||||
horizontalOffset = _glWidget->width() - (mirrorEnabled ? 300 : 410);
|
||||
|
||||
// Used for formatting voxel stats details
|
||||
statsVerticalOffset += PELS_PER_LINE; // skip a line for voxels
|
||||
QLocale locale(QLocale::English);
|
||||
std::stringstream voxelStats;
|
||||
lines = _statsExpanded ? 11 : 3;
|
||||
displayStatsBackground(backgroundColor, horizontalOffset, 0, _glWidget->width() - horizontalOffset, lines * STATS_PELS_PER_LINE + 10);
|
||||
horizontalOffset += 5;
|
||||
|
||||
if (_statsExpanded) {
|
||||
// Local Voxel Memory Usage
|
||||
voxelStats.str("");
|
||||
voxelStats << "Voxels Memory Nodes: " << VoxelTreeElement::getTotalMemoryUsage() / 1000000.f << "MB";
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, (char*)voxelStats.str().c_str(), .93f, .93f, .93f);
|
||||
|
||||
voxelStats.str("");
|
||||
voxelStats <<
|
||||
"Geometry RAM: " << _voxels.getVoxelMemoryUsageRAM() / 1000000.f << "MB / " <<
|
||||
"VBO: " << _voxels.getVoxelMemoryUsageVBO() / 1000000.f << "MB";
|
||||
if (_voxels.hasVoxelMemoryUsageGPU()) {
|
||||
voxelStats << " / GPU: " << _voxels.getVoxelMemoryUsageGPU() / 1000000.f << "MB";
|
||||
}
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, (char*)voxelStats.str().c_str(), .93f, .93f, .93f);
|
||||
|
||||
// Voxel Rendering
|
||||
voxelStats.str("");
|
||||
voxelStats.precision(4);
|
||||
voxelStats << "Voxel Rendering Slots Max: " << _voxels.getMaxVoxels() / 1000.f << "K";
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, (char*)voxelStats.str().c_str(), .93f, .93f, .93f);
|
||||
}
|
||||
|
||||
voxelStats.str("");
|
||||
voxelStats.precision(4);
|
||||
voxelStats << "Drawn: " << _voxels.getVoxelsWritten() / 1000.f << "K " <<
|
||||
"Abandoned: " << _voxels.getAbandonedVoxels() / 1000.f << "K ";
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, (char*)voxelStats.str().c_str(), .93f, .93f, .93f);
|
||||
|
||||
// iterate all the current voxel stats, and list their sending modes, and total voxel counts
|
||||
std::stringstream sendingMode("");
|
||||
|
@ -3191,93 +3340,50 @@ void Application::displayStats() {
|
|||
//const QUuid& uuid = i->first;
|
||||
VoxelSceneStats& stats = i->second;
|
||||
serverCount++;
|
||||
if (serverCount > 1) {
|
||||
sendingMode << ",";
|
||||
}
|
||||
if (stats.isMoving()) {
|
||||
sendingMode << "M";
|
||||
movingServerCount++;
|
||||
} else {
|
||||
sendingMode << "S";
|
||||
if (_statsExpanded) {
|
||||
if (serverCount > 1) {
|
||||
sendingMode << ",";
|
||||
}
|
||||
if (stats.isMoving()) {
|
||||
sendingMode << "M";
|
||||
movingServerCount++;
|
||||
} else {
|
||||
sendingMode << "S";
|
||||
}
|
||||
}
|
||||
|
||||
// calculate server node totals
|
||||
totalNodes += stats.getTotalElements();
|
||||
totalInternal += stats.getTotalInternal();
|
||||
totalLeaves += stats.getTotalLeaves();
|
||||
if (_statsExpanded) {
|
||||
totalInternal += stats.getTotalInternal();
|
||||
totalLeaves += stats.getTotalLeaves();
|
||||
}
|
||||
}
|
||||
if (serverCount == 0) {
|
||||
sendingMode << "---";
|
||||
if (_statsExpanded) {
|
||||
if (serverCount == 0) {
|
||||
sendingMode << "---";
|
||||
}
|
||||
sendingMode << "] " << serverCount << " servers";
|
||||
if (movingServerCount > 0) {
|
||||
sendingMode << " <SCENE NOT STABLE>";
|
||||
} else {
|
||||
sendingMode << " <SCENE STABLE>";
|
||||
}
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, (char*)sendingMode.str().c_str(), .93f, .93f, .93f);
|
||||
}
|
||||
sendingMode << "] " << serverCount << " servers";
|
||||
if (movingServerCount > 0) {
|
||||
sendingMode << " <SCENE NOT STABLE>";
|
||||
} else {
|
||||
sendingMode << " <SCENE STABLE>";
|
||||
}
|
||||
|
||||
QString serversTotalString = locale.toString((uint)totalNodes); // consider adding: .rightJustified(10, ' ');
|
||||
QString serversInternalString = locale.toString((uint)totalInternal);
|
||||
QString serversLeavesString = locale.toString((uint)totalLeaves);
|
||||
|
||||
// Server Voxels
|
||||
voxelStats.str("");
|
||||
voxelStats <<
|
||||
"Server Voxels Total: " << serversTotalString.toLocal8Bit().constData() << " / " <<
|
||||
"Internal: " << serversInternalString.toLocal8Bit().constData() << " / " <<
|
||||
"Leaves: " << serversLeavesString.toLocal8Bit().constData() << "";
|
||||
statsVerticalOffset += PELS_PER_LINE;
|
||||
drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char*)voxelStats.str().c_str());
|
||||
|
||||
unsigned long localTotal = VoxelTreeElement::getNodeCount();
|
||||
unsigned long localInternal = VoxelTreeElement::getInternalNodeCount();
|
||||
unsigned long localLeaves = VoxelTreeElement::getLeafNodeCount();
|
||||
QString localTotalString = locale.toString((uint)localTotal); // consider adding: .rightJustified(10, ' ');
|
||||
QString localInternalString = locale.toString((uint)localInternal);
|
||||
QString localLeavesString = locale.toString((uint)localLeaves);
|
||||
|
||||
// Local Voxels
|
||||
voxelStats.str("");
|
||||
voxelStats <<
|
||||
"Local Voxels Total: " << localTotalString.toLocal8Bit().constData() << " / " <<
|
||||
"Internal: " << localInternalString.toLocal8Bit().constData() << " / " <<
|
||||
"Leaves: " << localLeavesString.toLocal8Bit().constData() << "";
|
||||
statsVerticalOffset += PELS_PER_LINE;
|
||||
drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char*)voxelStats.str().c_str());
|
||||
|
||||
// Local Voxel Memory Usage
|
||||
voxelStats.str("");
|
||||
voxelStats <<
|
||||
"Voxels Memory Nodes: " << VoxelTreeElement::getTotalMemoryUsage() / 1000000.f << "MB "
|
||||
"Geometry RAM: " << _voxels.getVoxelMemoryUsageRAM() / 1000000.f << "MB " <<
|
||||
"VBO: " << _voxels.getVoxelMemoryUsageVBO() / 1000000.f << "MB ";
|
||||
if (_voxels.hasVoxelMemoryUsageGPU()) {
|
||||
voxelStats << "GPU: " << _voxels.getVoxelMemoryUsageGPU() / 1000000.f << "MB ";
|
||||
}
|
||||
statsVerticalOffset += PELS_PER_LINE;
|
||||
drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char*)voxelStats.str().c_str());
|
||||
|
||||
// Voxel Rendering
|
||||
voxelStats.str("");
|
||||
voxelStats.precision(4);
|
||||
voxelStats << "Voxel Rendering Slots " <<
|
||||
"Max: " << _voxels.getMaxVoxels() / 1000.f << "K " <<
|
||||
"Drawn: " << _voxels.getVoxelsWritten() / 1000.f << "K " <<
|
||||
"Abandoned: " << _voxels.getAbandonedVoxels() / 1000.f << "K ";
|
||||
statsVerticalOffset += PELS_PER_LINE;
|
||||
drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char*)voxelStats.str().c_str());
|
||||
|
||||
// draw Sending mode AFTER server node stats
|
||||
statsVerticalOffset += PELS_PER_LINE;
|
||||
drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char*)sendingMode.str().c_str());
|
||||
|
||||
// Incoming packets
|
||||
voxelStats.str("");
|
||||
int voxelPacketsToProcess = _voxelProcessor.packetsToProcessCount();
|
||||
QString packetsString = locale.toString((int)voxelPacketsToProcess);
|
||||
QString maxString = locale.toString((int)_recentMaxPackets);
|
||||
voxelStats << "Voxel Packets to Process: " << packetsString.toLocal8Bit().constData()
|
||||
<< " [Recent Max: " << maxString.toLocal8Bit().constData() << "]";
|
||||
if (_statsExpanded) {
|
||||
voxelStats.str("");
|
||||
QString packetsString = locale.toString((int)voxelPacketsToProcess);
|
||||
QString maxString = locale.toString((int)_recentMaxPackets);
|
||||
voxelStats << "Voxel Packets to Process: " << packetsString.toLocal8Bit().constData()
|
||||
<< " [Recent Max: " << maxString.toLocal8Bit().constData() << "]";
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, (char*)voxelStats.str().c_str(), .93f, .93f, .93f);
|
||||
}
|
||||
|
||||
if (_resetRecentMaxPacketsSoon && voxelPacketsToProcess > 0) {
|
||||
_recentMaxPackets = 0;
|
||||
|
@ -3290,8 +3396,109 @@ void Application::displayStats() {
|
|||
_recentMaxPackets = voxelPacketsToProcess;
|
||||
}
|
||||
}
|
||||
statsVerticalOffset += PELS_PER_LINE;
|
||||
drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char*)voxelStats.str().c_str());
|
||||
|
||||
verticalOffset += (_statsExpanded ? STATS_PELS_PER_LINE : 0);
|
||||
|
||||
QString serversTotalString = locale.toString((uint)totalNodes); // consider adding: .rightJustified(10, ' ');
|
||||
|
||||
// Server Voxels
|
||||
voxelStats.str("");
|
||||
voxelStats << "Server voxels: " << serversTotalString.toLocal8Bit().constData();
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, (char*)voxelStats.str().c_str(), .93f, .93f, .93f);
|
||||
|
||||
if (_statsExpanded) {
|
||||
QString serversInternalString = locale.toString((uint)totalInternal);
|
||||
QString serversLeavesString = locale.toString((uint)totalLeaves);
|
||||
|
||||
voxelStats.str("");
|
||||
voxelStats <<
|
||||
"Internal: " << serversInternalString.toLocal8Bit().constData() << " " <<
|
||||
"Leaves: " << serversLeavesString.toLocal8Bit().constData() << "";
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, (char*)voxelStats.str().c_str(), .93f, .93f, .93f);
|
||||
}
|
||||
|
||||
unsigned long localTotal = VoxelTreeElement::getNodeCount();
|
||||
QString localTotalString = locale.toString((uint)localTotal); // consider adding: .rightJustified(10, ' ');
|
||||
|
||||
// Local Voxels
|
||||
voxelStats.str("");
|
||||
voxelStats << "Local voxels: " << localTotalString.toLocal8Bit().constData();
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, (char*)voxelStats.str().c_str(), .93f, .93f, .93f);
|
||||
|
||||
if (_statsExpanded) {
|
||||
unsigned long localInternal = VoxelTreeElement::getInternalNodeCount();
|
||||
unsigned long localLeaves = VoxelTreeElement::getLeafNodeCount();
|
||||
QString localInternalString = locale.toString((uint)localInternal);
|
||||
QString localLeavesString = locale.toString((uint)localLeaves);
|
||||
|
||||
voxelStats.str("");
|
||||
voxelStats <<
|
||||
"Internal: " << localInternalString.toLocal8Bit().constData() << " " <<
|
||||
"Leaves: " << localLeavesString.toLocal8Bit().constData() << "";
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawtext(horizontalOffset, verticalOffset, 0.10f, 0, 1.0, 2, (char*)voxelStats.str().c_str(), .93f, .93f, .93f);
|
||||
}
|
||||
}
|
||||
|
||||
// called on mouse click release
|
||||
// check for clicks over stats in order to expand or contract them
|
||||
void Application::checkStatsClick() {
|
||||
if (0 != glm::compMax(glm::abs(glm::ivec2(_mouseX - _mouseDragStartedX, _mouseY - _mouseDragStartedY)))) {
|
||||
// not worried about dragging on stats
|
||||
return;
|
||||
}
|
||||
|
||||
int statsHeight = 0, statsWidth = 0, statsX = 0, statsY = 0, lines = 0;
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
statsX += MIRROR_VIEW_WIDTH;
|
||||
}
|
||||
|
||||
// top-left stats click
|
||||
lines = _statsExpanded ? 5 : 3;
|
||||
statsHeight = lines * STATS_PELS_PER_LINE + 10;
|
||||
statsWidth = 165;
|
||||
if (_mouseX > statsX && _mouseX < statsX + statsWidth && _mouseY > statsY && _mouseY < statsY + statsHeight) {
|
||||
toggleStatsExpanded();
|
||||
return;
|
||||
}
|
||||
|
||||
// ping stats click
|
||||
lines = _statsExpanded ? 4 : 3;
|
||||
statsX += statsWidth;
|
||||
statsHeight = lines * STATS_PELS_PER_LINE + 10;
|
||||
statsWidth = 175;
|
||||
if (_mouseX > statsX && _mouseX < statsX + statsWidth && _mouseY > statsY && _mouseY < statsY + statsHeight) {
|
||||
toggleStatsExpanded();
|
||||
return;
|
||||
}
|
||||
|
||||
// top-center stats panel click
|
||||
lines = _statsExpanded ? 4 : 3;
|
||||
statsX += statsWidth;
|
||||
statsHeight = lines * STATS_PELS_PER_LINE + 10;
|
||||
statsWidth = _glWidget->width() - 411 - statsX;
|
||||
if (_mouseX > statsX && _mouseX < statsX + statsWidth && _mouseY > statsY && _mouseY < statsY + statsHeight) {
|
||||
toggleStatsExpanded();
|
||||
return;
|
||||
}
|
||||
|
||||
// top-right stats click
|
||||
lines = _statsExpanded ? 11 : 3;
|
||||
statsX = _glWidget->width() - 410;
|
||||
statsHeight = lines * STATS_PELS_PER_LINE + 10;
|
||||
statsWidth = _glWidget->width() - statsX;
|
||||
if (_mouseX > statsX && _mouseX < statsX + statsWidth && _mouseY > statsY && _mouseY < statsY + statsHeight) {
|
||||
toggleStatsExpanded();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Application::toggleStatsExpanded() {
|
||||
_statsExpanded = !_statsExpanded;
|
||||
}
|
||||
|
||||
void Application::renderThrustAtVoxel(const glm::vec3& thrust) {
|
||||
|
@ -4123,3 +4330,14 @@ void Application::skipVersion(QString latestVersion) {
|
|||
skipFile.seek(0);
|
||||
skipFile.write(latestVersion.toStdString().c_str());
|
||||
}
|
||||
|
||||
void Application::takeSnapshot() {
|
||||
switchToResourcesParentIfRequired();
|
||||
QMediaPlayer* player = new QMediaPlayer();
|
||||
QFileInfo inf = QFileInfo("resources/sounds/snap.wav");
|
||||
player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
|
||||
player->play();
|
||||
|
||||
Snapshot::saveSnapshot(_glWidget, _profile.getUsername(), _myAvatar.getPosition());
|
||||
}
|
||||
|
||||
|
|
|
@ -305,7 +305,11 @@ private:
|
|||
|
||||
void updateShadowMap();
|
||||
void displayOverlay();
|
||||
void displayStatsBackground(unsigned int rgba, int x, int y, int width, int height);
|
||||
void displayStats();
|
||||
void checkStatsClick();
|
||||
void toggleStatsExpanded();
|
||||
void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false);
|
||||
void renderViewFrustum(ViewFrustum& viewFrustum);
|
||||
|
||||
void checkBandwidthMeterClick();
|
||||
|
@ -326,6 +330,7 @@ private:
|
|||
QMainWindow* _window;
|
||||
QGLWidget* _glWidget;
|
||||
|
||||
bool _statsExpanded;
|
||||
BandwidthMeter _bandwidthMeter;
|
||||
|
||||
QThread* _nodeThread;
|
||||
|
@ -498,6 +503,7 @@ private:
|
|||
void checkVersion();
|
||||
void displayUpdateDialog();
|
||||
bool shouldSkipVersion(QString latestVersion);
|
||||
void takeSnapshot();
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__Application__) */
|
||||
|
|
|
@ -39,8 +39,9 @@ static const float AUDIO_CALLBACK_MSECS = (float) NETWORK_BUFFER_LENGTH_SAMPLES_
|
|||
|
||||
// Mute icon configration
|
||||
static const int ICON_SIZE = 24;
|
||||
static const int ICON_LEFT = 20;
|
||||
static const int BOTTOM_PADDING = 110;
|
||||
static const int ICON_LEFT = 0;
|
||||
static const int ICON_TOP = 115;
|
||||
static const int ICON_TOP_MIRROR = 220;
|
||||
|
||||
Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* parent) :
|
||||
AbstractAudioInterface(parent),
|
||||
|
@ -541,13 +542,13 @@ void Audio::render(int screenWidth, int screenHeight) {
|
|||
if (_audioInput) {
|
||||
glLineWidth(2.0);
|
||||
glBegin(GL_LINES);
|
||||
glColor3f(1,1,1);
|
||||
glColor3f(.93f, .93f, .93f);
|
||||
|
||||
int startX = 20.0;
|
||||
int currentX = startX;
|
||||
int topY = screenHeight - 40;
|
||||
int bottomY = screenHeight - 20;
|
||||
float frameWidth = 20.0;
|
||||
int topY = screenHeight - 45;
|
||||
int bottomY = screenHeight - 25;
|
||||
float frameWidth = 23.0;
|
||||
float halfY = topY + ((bottomY - topY) / 2.0);
|
||||
|
||||
// draw the lines for the base of the ring buffer
|
||||
|
@ -576,9 +577,9 @@ void Audio::render(int screenWidth, int screenHeight) {
|
|||
float msLeftForAudioOutput = (secondsLeftForAudioOutput + secondsLeftForRingBuffer) * 1000;
|
||||
|
||||
if (_numFramesDisplayStarve == 0) {
|
||||
glColor3f(0, 1, 0);
|
||||
glColor3f(0, .8f, .4f);
|
||||
} else {
|
||||
glColor3f(0.5 + (_numFramesDisplayStarve / 20.0f), 0, 0);
|
||||
glColor3f(0.5 + (_numFramesDisplayStarve / 20.0f), .2f, .4f);
|
||||
_numFramesDisplayStarve--;
|
||||
}
|
||||
|
||||
|
@ -589,44 +590,44 @@ void Audio::render(int screenWidth, int screenHeight) {
|
|||
}
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2f(startX, topY + 2);
|
||||
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth, topY + 2);
|
||||
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth, bottomY - 2);
|
||||
glVertex2f(startX, bottomY - 2);
|
||||
glVertex2f(startX + 1, topY + 2);
|
||||
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth + 1, topY + 2);
|
||||
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth + 1, bottomY - 2);
|
||||
glVertex2f(startX + 1, bottomY - 2);
|
||||
glEnd();
|
||||
|
||||
// Show a yellow bar with the averaged msecs latency you are hearing (from time of packet receipt)
|
||||
glColor3f(1,1,0);
|
||||
glColor3f(1, .8f, 0);
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth - 2, topY - 2);
|
||||
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth + 2, topY - 2);
|
||||
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth + 2, bottomY + 2);
|
||||
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth - 2, bottomY + 2);
|
||||
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth - 1, topY - 2);
|
||||
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth + 3, topY - 2);
|
||||
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth + 3, bottomY + 2);
|
||||
glVertex2f(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth - 1, bottomY + 2);
|
||||
glEnd();
|
||||
|
||||
char out[40];
|
||||
sprintf(out, "%3.0f\n", _averagedLatency);
|
||||
drawtext(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth - 10, topY - 9, 0.10f, 0, 1, 0, out, 1,1,0);
|
||||
drawtext(startX + _averagedLatency / AUDIO_CALLBACK_MSECS * frameWidth - 10, topY - 9, 0.10f, 0, 1, 2, out, 1, .8f, 0);
|
||||
|
||||
// Show a red bar with the 'start' point of one frame plus the jitter buffer
|
||||
|
||||
glColor3f(1, 0, 0);
|
||||
glColor3f(1, .2f, .4f);
|
||||
int jitterBufferPels = (1.f + (float)getJitterBufferSamples()
|
||||
/ (float) NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL) * frameWidth;
|
||||
sprintf(out, "%.0f\n", getJitterBufferSamples() / SAMPLE_RATE * 1000.f);
|
||||
drawtext(startX + jitterBufferPels - 5, topY - 9, 0.10f, 0, 1, 0, out, 1, 0, 0);
|
||||
drawtext(startX + jitterBufferPels - 5, topY - 9, 0.10f, 0, 1, 2, out, 1, .2f, .4f);
|
||||
sprintf(out, "j %.1f\n", _measuredJitter);
|
||||
if (Menu::getInstance()->getAudioJitterBufferSamples() == 0) {
|
||||
drawtext(startX + jitterBufferPels - 5, bottomY + 12, 0.10f, 0, 1, 0, out, 1, 0, 0);
|
||||
drawtext(startX + jitterBufferPels - 5, bottomY + 12, 0.10f, 0, 1, 2, out, 1, .2f, .4f);
|
||||
} else {
|
||||
drawtext(startX, bottomY + 12, 0.10f, 0, 1, 0, out, 1, 0, 0);
|
||||
drawtext(startX, bottomY + 12, 0.10f, 0, 1, 2, out, 1, .2f, .4f);
|
||||
}
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2f(startX + jitterBufferPels - 2, topY - 2);
|
||||
glVertex2f(startX + jitterBufferPels + 2, topY - 2);
|
||||
glVertex2f(startX + jitterBufferPels + 2, bottomY + 2);
|
||||
glVertex2f(startX + jitterBufferPels - 2, bottomY + 2);
|
||||
glVertex2f(startX + jitterBufferPels - 1, topY - 2);
|
||||
glVertex2f(startX + jitterBufferPels + 3, topY - 2);
|
||||
glVertex2f(startX + jitterBufferPels + 3, bottomY + 2);
|
||||
glVertex2f(startX + jitterBufferPels - 1, bottomY + 2);
|
||||
glEnd();
|
||||
|
||||
}
|
||||
|
@ -732,11 +733,13 @@ void Audio::handleAudioByteArray(const QByteArray& audioByteArray) {
|
|||
|
||||
void Audio::renderToolIcon(int screenHeight) {
|
||||
|
||||
_iconBounds = QRect(ICON_LEFT, screenHeight - BOTTOM_PADDING, ICON_SIZE, ICON_SIZE);
|
||||
int iconTop = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) ? ICON_TOP_MIRROR : ICON_TOP;
|
||||
|
||||
_iconBounds = QRect(ICON_LEFT, iconTop, ICON_SIZE, ICON_SIZE);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, _micTextureId);
|
||||
glColor3f(1, 1, 1);
|
||||
glColor3f(.93f, .93f, .93f);
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
glTexCoord2f(1, 1);
|
||||
|
|
|
@ -14,21 +14,21 @@
|
|||
namespace { // .cpp-local
|
||||
|
||||
int const AREA_WIDTH = -280; // Width of the area used. Aligned to the right when negative.
|
||||
int const AREA_HEIGHT = 40; // Height of the area used. Aligned to the bottom when negative.
|
||||
int const BORDER_DISTANCE_HORIZ = -20; // Distance to edge of screen (use negative value when width is negative).
|
||||
int const BORDER_DISTANCE_VERT = 40; // Distance to edge of screen (use negative value when height is negative).
|
||||
int const AREA_HEIGHT = -40; // Height of the area used. Aligned to the bottom when negative.
|
||||
int const BORDER_DISTANCE_HORIZ = -10; // Distance to edge of screen (use negative value when width is negative).
|
||||
int const BORDER_DISTANCE_VERT = -15; // Distance to edge of screen (use negative value when height is negative).
|
||||
|
||||
int SPACING_VERT_BARS = 2; // Vertical distance between input and output bar
|
||||
int SPACING_RIGHT_CAPTION_IN_OUT = 4; // IN/OUT <--> |######## : |
|
||||
int SPACING_LEFT_CAPTION_UNIT = 4; // |######## : | <--> UNIT
|
||||
int PADDING_HORIZ_VALUE = 2; // |<-->X.XX<:-># |
|
||||
|
||||
unsigned const COLOR_TEXT = 0xe0e0e0e0; // ^ ^ ^ ^ ^ ^
|
||||
unsigned const COLOR_TEXT = 0xedededff; // ^ ^ ^ ^ ^ ^
|
||||
unsigned const COLOR_FRAME = 0xe0e0e0b0; // | | |
|
||||
unsigned const COLOR_INDICATOR = 0xc0c0c0b0; // |
|
||||
|
||||
char const* CAPTION_IN = "IN";
|
||||
char const* CAPTION_OUT = "OUT";
|
||||
char const* CAPTION_IN = "In";
|
||||
char const* CAPTION_OUT = "Out";
|
||||
char const* CAPTION_UNIT = "Mbps";
|
||||
|
||||
double const UNIT_SCALE = 8000.0 / (1024.0 * 1024.0); // Bytes/ms -> Mbps
|
||||
|
@ -38,13 +38,13 @@ namespace { // .cpp-local
|
|||
}
|
||||
|
||||
BandwidthMeter::ChannelInfo BandwidthMeter::_CHANNELS[] = {
|
||||
{ "Audio" , "Kbps", 8000.0 / 1024.0, 0x40ff40d0 },
|
||||
{ "Audio" , "Kbps", 8000.0 / 1024.0, 0x33cc99ff },
|
||||
{ "Avatars" , "Kbps", 8000.0 / 1024.0, 0xffef40c0 },
|
||||
{ "Voxels" , "Kbps", 8000.0 / 1024.0, 0xd0d0d0a0 }
|
||||
};
|
||||
|
||||
BandwidthMeter::BandwidthMeter() :
|
||||
_textRenderer(SANS_FONT_FAMILY, -1, -1, false, TextRenderer::SHADOW_EFFECT),
|
||||
_textRenderer(INCONSOLATA_FONT_FAMILY, -1, QFont::Bold, false),
|
||||
_scaleMaxIndex(INITIAL_SCALE_MAXIMUM_INDEX) {
|
||||
|
||||
_channels = static_cast<ChannelInfo*>( malloc(sizeof(_CHANNELS)) );
|
||||
|
|
|
@ -14,17 +14,17 @@
|
|||
#include <QDir>
|
||||
#include <QDesktopServices>
|
||||
|
||||
const QString FILENAME_FORMAT = "hifi-log_%1_%2.txt";
|
||||
const QString DATETIME_FORMAT = "yyyy-MM-dd_hh.mm.ss";
|
||||
const QString LOGS_DIRECTORY = "Logs";
|
||||
|
||||
FileLogger::FileLogger() : _logData(NULL) {
|
||||
setExtraDebugging(false);
|
||||
_fileName = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
||||
QDir logDir(_fileName);
|
||||
if (!logDir.exists(_fileName)) {
|
||||
logDir.mkdir(_fileName);
|
||||
}
|
||||
|
||||
_fileName = FileUtils::standardPath(LOGS_DIRECTORY);
|
||||
QHostAddress clientAddress = QHostAddress(getHostOrderLocalAddress());
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
_fileName.append(QString("/hifi-log_%1_%2.txt").arg(clientAddress.toString(), now.toString("yyyy-MM-dd_hh.mm.ss")));
|
||||
_fileName.append(QString(FILENAME_FORMAT).arg(clientAddress.toString(), now.toString(DATETIME_FORMAT)));
|
||||
}
|
||||
|
||||
void FileLogger::addMessage(QString message) {
|
||||
|
@ -40,5 +40,5 @@ void FileLogger::addMessage(QString message) {
|
|||
}
|
||||
|
||||
void FileLogger::locateLog() {
|
||||
FileUtils::LocateFile(_fileName);
|
||||
FileUtils::locateFile(_fileName);
|
||||
}
|
||||
|
|
|
@ -6,212 +6,148 @@
|
|||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
#include "ImportDialog.h"
|
||||
#include "Application.h"
|
||||
|
||||
#include <QStandardPaths>
|
||||
#include <QGridLayout>
|
||||
#include <QMouseEvent>
|
||||
#include <QSplitter>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
const QString WINDOW_NAME = QObject::tr("Import Voxels");
|
||||
const QString IMPORT_BUTTON_NAME = QObject::tr("Import");
|
||||
const QString IMPORT_TO_CLIPBOARD_CHECKBOX_STRING = QObject::tr("Import into clipboard");
|
||||
const QString PREVIEW_CHECKBOX_STRING = QObject::tr("Load preview");
|
||||
const QString IMPORT_FILE_TYPES = QObject::tr("Sparse Voxel Octree Files, "
|
||||
"Square PNG, "
|
||||
"Schematic Files "
|
||||
"(*.svo *.png *.schematic)");
|
||||
const QString WINDOW_NAME = QObject::tr("Import Voxels");
|
||||
const QString IMPORT_BUTTON_NAME = QObject::tr("Import");
|
||||
const QString IMPORT_INFO = QObject::tr("<b>Import</b> %1 as voxels");
|
||||
const QString CANCEL_BUTTON_NAME = QObject::tr("Cancel");
|
||||
const QString INFO_LABEL_TEXT = QObject::tr("<div style='line-height:20px;'>"
|
||||
"This will load the selected file into Hifi and allow you<br/>"
|
||||
"to place it with %1-V; you must be in select or<br/>"
|
||||
"add mode (S or V keys will toggle mode) to place.</div>");
|
||||
|
||||
const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
const int SHORT_FILE_EXTENSION = 4;
|
||||
const int SECOND_INDEX_LETTER = 1;
|
||||
|
||||
QIcon HiFiIconProvider::icon(QFileIconProvider::IconType type) const {
|
||||
|
||||
const glm::vec3 UP_VECT = glm::vec3(0, 1, 0);
|
||||
const float ANGULAR_RATE = 0.02f;
|
||||
const float VERTICAL_ANGLE = (float)M_PI_4 / 2.0f;
|
||||
const float RETURN_RATE = 0.02f;
|
||||
const float NEAR_CLIP = 0.5f;
|
||||
const float FAR_CLIP = 10.0f;
|
||||
const float FIELD_OF_VIEW = 60.0f;
|
||||
switchToResourcesParentIfRequired();
|
||||
|
||||
class GLWidget : public QGLWidget {
|
||||
public:
|
||||
GLWidget(QWidget* parent = NULL);
|
||||
void setDraw(bool draw) {_draw = draw;}
|
||||
void setTargetCenter(glm::vec3 targetCenter) { _targetCenter = targetCenter; }
|
||||
// types
|
||||
// Computer, Desktop, Trashcan, Network, Drive, Folder, File
|
||||
QString typeString;
|
||||
|
||||
protected:
|
||||
virtual void initializeGL();
|
||||
virtual void resizeGL(int width, int height);
|
||||
virtual void paintGL();
|
||||
switch (type) {
|
||||
case QFileIconProvider::Computer:
|
||||
typeString = "computer";
|
||||
break;
|
||||
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseMoveEvent(QMouseEvent* event);
|
||||
void mouseReleaseEvent(QMouseEvent* event);
|
||||
case QFileIconProvider::Desktop:
|
||||
typeString = "desktop";
|
||||
break;
|
||||
|
||||
private:
|
||||
VoxelSystem* _voxelSystem;
|
||||
case QFileIconProvider::Trashcan:
|
||||
case QFileIconProvider::Network:
|
||||
case QFileIconProvider::Drive:
|
||||
case QFileIconProvider::Folder:
|
||||
typeString = "folder";
|
||||
break;
|
||||
|
||||
bool _draw;
|
||||
|
||||
double _a; // horizontal angle of the camera to the center of the object
|
||||
double _h; // vertical angle of the camera to the center of the object
|
||||
glm::vec3 _targetCenter;
|
||||
|
||||
bool _pressed;
|
||||
int _mouseX;
|
||||
int _mouseY;
|
||||
};
|
||||
|
||||
GLWidget::GLWidget(QWidget *parent)
|
||||
: QGLWidget(parent, Application::getInstance()->getGLWidget()),
|
||||
_draw(false),
|
||||
_a(0.0f),
|
||||
_h(VERTICAL_ANGLE),
|
||||
_targetCenter(0.5f, 0.5f, 0.5f),
|
||||
_pressed(false),
|
||||
_mouseX(0),
|
||||
_mouseY(0) {
|
||||
_voxelSystem = Application::getInstance()->getSharedVoxelSystem();
|
||||
}
|
||||
|
||||
void GLWidget::initializeGL() {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glShadeModel (GL_SMOOTH);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
void GLWidget::resizeGL(int width, int height) {
|
||||
glViewport(0, 0, width, height);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
gluPerspective(FIELD_OF_VIEW,
|
||||
(float) width / height,
|
||||
NEAR_CLIP,
|
||||
FAR_CLIP);
|
||||
}
|
||||
|
||||
void GLWidget::paintGL() {
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
if (!_pressed) {
|
||||
_a += ANGULAR_RATE;
|
||||
_h = (1.0f - RETURN_RATE) * _h + RETURN_RATE * VERTICAL_ANGLE;
|
||||
default:
|
||||
typeString = "file";
|
||||
break;
|
||||
}
|
||||
|
||||
gluLookAt(_targetCenter.x + (glm::length(_targetCenter) + NEAR_CLIP) * cos(_a),
|
||||
_targetCenter.y + (glm::length(_targetCenter) + NEAR_CLIP) * sin(_h),
|
||||
_targetCenter.z + (glm::length(_targetCenter) + NEAR_CLIP) * sin(_a),
|
||||
_targetCenter.x, _targetCenter.y, _targetCenter.z,
|
||||
UP_VECT.x, UP_VECT.y, UP_VECT.z);
|
||||
return QIcon("resources/icons/" + typeString + ".svg");
|
||||
}
|
||||
|
||||
QIcon HiFiIconProvider::icon(const QFileInfo &info) const {
|
||||
switchToResourcesParentIfRequired();
|
||||
const QString ext = info.suffix().toLower();
|
||||
|
||||
if (_draw) {
|
||||
glBegin(GL_LINES);
|
||||
glColor3d(1, 1 ,1);
|
||||
glVertex3d(0, 0, 0);
|
||||
glVertex3d(1, 0, 0);
|
||||
|
||||
glVertex3d(0, 0, 0);
|
||||
glVertex3d(0, 1, 0);
|
||||
|
||||
glVertex3d(0, 0, 0);
|
||||
glVertex3d(0, 0, 1);
|
||||
|
||||
|
||||
glColor3d(0.4f, 0.4f ,0.4f);
|
||||
glVertex3d(2 * _targetCenter.x, 2 * _targetCenter.y, 2 * _targetCenter.z);
|
||||
glVertex3d(0 , 2 * _targetCenter.y, 2 * _targetCenter.z);
|
||||
|
||||
glVertex3d(2 * _targetCenter.x, 2 * _targetCenter.y, 2 * _targetCenter.z);
|
||||
glVertex3d(2 * _targetCenter.x, 0 , 2 * _targetCenter.z);
|
||||
|
||||
glVertex3d(2 * _targetCenter.x, 2 * _targetCenter.y, 2 * _targetCenter.z);
|
||||
glVertex3d(2 * _targetCenter.x, 2 * _targetCenter.y, 0 );
|
||||
glEnd();
|
||||
|
||||
glScalef(1.0f / TREE_SCALE, 1.0f / TREE_SCALE, 1.0f / TREE_SCALE);
|
||||
_voxelSystem->render(false);
|
||||
if (info.isDir()) {
|
||||
if (info.absoluteFilePath() == QDir::homePath()) {
|
||||
return QIcon("resources/icons/home.svg");
|
||||
} else if (info.absoluteFilePath() == DESKTOP_LOCATION) {
|
||||
return QIcon("resources/icons/desktop.svg");
|
||||
} else if (info.absoluteFilePath() == QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)) {
|
||||
return QIcon("resources/icons/documents.svg");
|
||||
}
|
||||
return QIcon("resources/icons/folder.svg");
|
||||
}
|
||||
|
||||
QFileInfo iconFile("resources/icons/" + iconsMap[ext]);
|
||||
if (iconFile.exists() && iconFile.isFile()) {
|
||||
return QIcon(iconFile.filePath());
|
||||
}
|
||||
|
||||
return QIcon("resources/icons/file.svg");
|
||||
}
|
||||
|
||||
void GLWidget::mousePressEvent(QMouseEvent* event) {
|
||||
_pressed = true;
|
||||
_mouseX = event->globalX();
|
||||
_mouseY = event->globalY();
|
||||
QString HiFiIconProvider::type(const QFileInfo &info) const {
|
||||
if (info.isFile()) {
|
||||
if (info.suffix().size() > SHORT_FILE_EXTENSION) {
|
||||
// Capitalize extension
|
||||
return info.suffix().left(SECOND_INDEX_LETTER).toUpper() + info.suffix().mid(SECOND_INDEX_LETTER);
|
||||
}
|
||||
return info.suffix().toUpper();
|
||||
}
|
||||
|
||||
return QFileIconProvider::type(info);
|
||||
}
|
||||
|
||||
void GLWidget::mouseMoveEvent(QMouseEvent* event) {
|
||||
_a += (M_PI * (event->globalX() - _mouseX)) / height();
|
||||
_h += (M_PI * (event->globalY() - _mouseY)) / height();
|
||||
_h = glm::clamp(_h, -M_PI_4, M_PI_4);
|
||||
ImportDialog::ImportDialog(QWidget* parent) :
|
||||
QFileDialog(parent, WINDOW_NAME, DESKTOP_LOCATION, NULL),
|
||||
_importButton(IMPORT_BUTTON_NAME, this),
|
||||
_cancelButton(CANCEL_BUTTON_NAME, this),
|
||||
fileAccepted(false) {
|
||||
|
||||
_mouseX = event->globalX();
|
||||
_mouseY = event->globalY();
|
||||
}
|
||||
|
||||
void GLWidget::mouseReleaseEvent(QMouseEvent* event) {
|
||||
_pressed = false;
|
||||
}
|
||||
|
||||
ImportDialog::ImportDialog(QWidget *parent)
|
||||
: QFileDialog(parent, WINDOW_NAME, DESKTOP_LOCATION, IMPORT_FILE_TYPES),
|
||||
_importButton (IMPORT_BUTTON_NAME, this),
|
||||
_clipboardImportBox(IMPORT_TO_CLIPBOARD_CHECKBOX_STRING, this),
|
||||
_previewBox (PREVIEW_CHECKBOX_STRING, this),
|
||||
_previewBar (this),
|
||||
_glPreview (new GLWidget(this)) {
|
||||
setOption(QFileDialog::DontUseNativeDialog, true);
|
||||
setFileMode(QFileDialog::ExistingFile);
|
||||
setViewMode(QFileDialog::Detail);
|
||||
|
||||
QGridLayout* gridLayout = (QGridLayout*) layout();
|
||||
gridLayout->addWidget(&_importButton , 2, 2);
|
||||
gridLayout->addWidget(&_clipboardImportBox, 2, 3);
|
||||
gridLayout->addWidget(&_previewBox , 3, 3);
|
||||
gridLayout->addWidget(&_previewBar , 0, 3);
|
||||
gridLayout->addWidget(_glPreview , 1, 3);
|
||||
gridLayout->setColumnStretch(3, 1);
|
||||
#ifdef Q_OS_MAC
|
||||
QString cmdString = ("Command");
|
||||
#else
|
||||
QString cmdString = ("Control");
|
||||
#endif
|
||||
QLabel* infoLabel = new QLabel(QString(INFO_LABEL_TEXT).arg(cmdString));
|
||||
infoLabel->setObjectName("infoLabel");
|
||||
|
||||
_previewBar.setVisible(false);
|
||||
_previewBar.setRange(0, 100);
|
||||
_previewBar.setValue(0);
|
||||
QGridLayout* gridLayout = (QGridLayout*) layout();
|
||||
gridLayout->addWidget(infoLabel, 2, 0, 2, 1);
|
||||
gridLayout->addWidget(&_cancelButton, 2, 1, 2, 1);
|
||||
gridLayout->addWidget(&_importButton, 2, 2, 2, 1);
|
||||
|
||||
setImportTypes();
|
||||
setLayout();
|
||||
|
||||
connect(&_importButton, SIGNAL(pressed()), SLOT(import()));
|
||||
connect(&_previewBox, SIGNAL(toggled(bool)), SIGNAL(previewToggled(bool)));
|
||||
connect(&_previewBox, SIGNAL(toggled(bool)), SLOT(preview(bool)));
|
||||
|
||||
connect(this, SIGNAL(currentChanged(QString)), SLOT(saveCurrentFile(QString)));
|
||||
connect(&_glTimer, SIGNAL(timeout()), SLOT(timer()));
|
||||
|
||||
connect(&_cancelButton, SIGNAL(pressed()), SLOT(close()));
|
||||
connect(this, SIGNAL(currentChanged(QString)), SLOT(saveCurrentFile(QString)));
|
||||
}
|
||||
|
||||
ImportDialog::~ImportDialog() {
|
||||
delete _glPreview;
|
||||
}
|
||||
|
||||
void ImportDialog::init() {
|
||||
VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem();
|
||||
connect(voxelSystem, SIGNAL(importSize(float,float,float)), SLOT(setGLCamera(float,float,float)));
|
||||
connect(voxelSystem, SIGNAL(importProgress(int)), &_previewBar, SLOT(setValue(int)));
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void ImportDialog::import() {
|
||||
_importButton.setDisabled(true);
|
||||
_clipboardImportBox.setDisabled(true);
|
||||
_previewBox.setDisabled(true);
|
||||
|
||||
_previewBar.setValue(0);
|
||||
_previewBar.setVisible(true);
|
||||
|
||||
fileAccepted = true;
|
||||
emit accepted();
|
||||
}
|
||||
|
||||
void ImportDialog::accept() {
|
||||
QFileDialog::accept();
|
||||
// do nothing if import is not enable
|
||||
if (!_importButton.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fileAccepted) {
|
||||
fileAccepted = true;
|
||||
emit accepted();
|
||||
} else {
|
||||
QFileDialog::accept();
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDialog::reject() {
|
||||
|
@ -219,44 +155,141 @@ void ImportDialog::reject() {
|
|||
}
|
||||
|
||||
int ImportDialog::exec() {
|
||||
// deselect selected file
|
||||
selectFile(" ");
|
||||
return QFileDialog::exec();
|
||||
}
|
||||
|
||||
void ImportDialog::setGLCamera(float x, float y, float z) {
|
||||
_glPreview->setTargetCenter(glm::vec3(x, y, z) / 2.0f);
|
||||
}
|
||||
|
||||
void ImportDialog::reset() {
|
||||
_previewBox.setChecked(false);
|
||||
_previewBar.setVisible(false);
|
||||
_previewBar.setValue(0);
|
||||
_importButton.setEnabled(true);
|
||||
_clipboardImportBox.setEnabled(true);
|
||||
_previewBox.setEnabled(true);
|
||||
|
||||
_glTimer.stop();
|
||||
_glPreview->setDraw(false);
|
||||
_glPreview->updateGL();
|
||||
}
|
||||
|
||||
void ImportDialog::preview(bool wantPreview) {
|
||||
_previewBar.setValue(0);
|
||||
_previewBar.setVisible(wantPreview);
|
||||
_glPreview->setDraw(wantPreview);
|
||||
|
||||
if (wantPreview) {
|
||||
_glTimer.start();
|
||||
} else {
|
||||
_glTimer.stop();
|
||||
_glPreview->updateGL();
|
||||
}
|
||||
_importButton.setEnabled(false);
|
||||
}
|
||||
|
||||
void ImportDialog::saveCurrentFile(QString filename) {
|
||||
_currentFile = filename;
|
||||
if (!filename.isEmpty() && QFileInfo(filename).isFile()) {
|
||||
_currentFile = filename;
|
||||
_importButton.setEnabled(true);
|
||||
} else {
|
||||
_currentFile.clear();
|
||||
_importButton.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDialog::timer() {
|
||||
_glPreview->updateGL();
|
||||
_glTimer.start(16);
|
||||
void ImportDialog::setLayout() {
|
||||
|
||||
// set ObjectName used in qss for styling
|
||||
_importButton.setObjectName("importButton");
|
||||
_cancelButton.setObjectName("cancelButton");
|
||||
|
||||
// set fixed size
|
||||
_importButton.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
_cancelButton.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
|
||||
// hide unused embedded widgets in QFileDialog
|
||||
QWidget* widget = findChild<QWidget*>("lookInCombo");
|
||||
widget->hide();
|
||||
|
||||
widget = findChild<QWidget*>("backButton");
|
||||
widget->hide();
|
||||
|
||||
widget = findChild<QWidget*>("forwardButton");
|
||||
widget->hide();
|
||||
|
||||
widget = findChild<QWidget*>("toParentButton");
|
||||
widget->hide();
|
||||
|
||||
widget = findChild<QWidget*>("newFolderButton");
|
||||
widget->hide();
|
||||
|
||||
widget = findChild<QWidget*>("listModeButton");
|
||||
widget->hide();
|
||||
|
||||
widget = findChild<QWidget*>("detailModeButton");
|
||||
widget->hide();
|
||||
|
||||
widget = findChild<QWidget*>("fileNameEdit");
|
||||
widget->hide();
|
||||
|
||||
widget = findChild<QWidget*>("fileTypeCombo");
|
||||
widget->hide();
|
||||
|
||||
widget = findChild<QWidget*>("fileTypeLabel");
|
||||
widget->hide();
|
||||
|
||||
widget = findChild<QWidget*>("fileNameLabel");
|
||||
widget->hide();
|
||||
|
||||
widget = findChild<QWidget*>("buttonBox");
|
||||
widget->hide();
|
||||
|
||||
QSplitter* splitter = findChild<QSplitter*>("splitter");
|
||||
splitter->setHandleWidth(0);
|
||||
|
||||
// remove blue outline on Mac
|
||||
widget = findChild<QWidget*>("sidebar");
|
||||
widget->setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
|
||||
widget = findChild<QWidget*>("treeView");
|
||||
widget->setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
|
||||
// remove reference to treeView
|
||||
widget = NULL;
|
||||
widget->deleteLater();
|
||||
|
||||
switchToResourcesParentIfRequired();
|
||||
QFile styleSheet("resources/styles/import_dialog.qss");
|
||||
if (styleSheet.open(QIODevice::ReadOnly)) {
|
||||
setStyleSheet(styleSheet.readAll());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ImportDialog::setImportTypes() {
|
||||
|
||||
switchToResourcesParentIfRequired();
|
||||
QFile config("resources/config/config.json");
|
||||
config.open(QFile::ReadOnly | QFile::Text);
|
||||
QJsonDocument document = QJsonDocument::fromJson(config.readAll());
|
||||
if (!document.isNull() && !document.isEmpty()) {
|
||||
|
||||
QString importFormatsInfo;
|
||||
QString importFormatsFilterList;
|
||||
QHash<QString, QString> iconsMap;
|
||||
|
||||
QJsonObject configObject = document.object();
|
||||
if (!configObject.isEmpty()) {
|
||||
QJsonArray fileFormats = configObject["importFormats"].toArray();
|
||||
int formatsCounter = 0;
|
||||
foreach (const QJsonValue& fileFormat, fileFormats) {
|
||||
QJsonObject fileFormatObject = fileFormat.toObject();
|
||||
|
||||
QString ext(fileFormatObject["extension"].toString());
|
||||
QString icon(fileFormatObject.value("icon").toString());
|
||||
|
||||
if (formatsCounter > 0) {
|
||||
importFormatsInfo.append(",");
|
||||
}
|
||||
|
||||
// set ' or' on last import type text
|
||||
if (formatsCounter == fileFormats.count() - 1) {
|
||||
importFormatsInfo.append(" or");
|
||||
}
|
||||
|
||||
importFormatsFilterList.append(QString("*.%1 ").arg(ext));
|
||||
importFormatsInfo.append(" .").append(ext);
|
||||
iconsMap[ext] = icon;
|
||||
formatsCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
// set custom file icons
|
||||
setIconProvider(new HiFiIconProvider(iconsMap));
|
||||
setNameFilter(importFormatsFilterList);
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
QString cmdString = ("Command");
|
||||
#else
|
||||
QString cmdString = ("Control");
|
||||
#endif
|
||||
setLabelText(QFileDialog::LookIn, QString(IMPORT_INFO).arg(importFormatsInfo));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,54 +9,54 @@
|
|||
#ifndef __hifi__ImportDialog__
|
||||
#define __hifi__ImportDialog__
|
||||
|
||||
#include <VoxelSystem.h>
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QPushButton>
|
||||
#include <QCheckBox>
|
||||
#include <QProgressBar>
|
||||
#include <QGLWidget>
|
||||
#include <QTimer>
|
||||
#include <QLabel>
|
||||
#include <QFileIconProvider>
|
||||
#include <QHash>
|
||||
|
||||
class GLWidget;
|
||||
#include <SharedUtil.h>
|
||||
|
||||
class HiFiIconProvider : public QFileIconProvider {
|
||||
public:
|
||||
HiFiIconProvider(const QHash<QString, QString> map) { iconsMap = map; };
|
||||
virtual QIcon icon(IconType type) const;
|
||||
virtual QIcon icon(const QFileInfo &info) const;
|
||||
virtual QString type(const QFileInfo &info) const;
|
||||
QHash<QString, QString> iconsMap;
|
||||
};
|
||||
|
||||
class ImportDialog : public QFileDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ImportDialog(QWidget* parent = NULL);
|
||||
~ImportDialog();
|
||||
|
||||
void init();
|
||||
void reset();
|
||||
|
||||
bool getWantPreview() const { return _previewBox.isChecked(); }
|
||||
QString getCurrentFile() const { return _currentFile; }
|
||||
bool getImportIntoClipboard() const { return _clipboardImportBox.isChecked(); }
|
||||
|
||||
signals:
|
||||
void previewToggled(bool);
|
||||
void accepted();
|
||||
|
||||
public slots:
|
||||
int exec();
|
||||
void setGLCamera(float x, float y, float z);
|
||||
void import();
|
||||
void accept();
|
||||
void reject();
|
||||
|
||||
private slots:
|
||||
void preview(bool preview);
|
||||
void saveCurrentFile(QString);
|
||||
void timer();
|
||||
|
||||
private:
|
||||
QString _currentFile;
|
||||
QPushButton _importButton;
|
||||
QCheckBox _clipboardImportBox;
|
||||
QCheckBox _previewBox;
|
||||
QProgressBar _previewBar;
|
||||
GLWidget* _glPreview;
|
||||
QTimer _glTimer;
|
||||
QString _currentFile;
|
||||
QPushButton _importButton;
|
||||
QPushButton _cancelButton;
|
||||
|
||||
void setLayout();
|
||||
void setImportTypes();
|
||||
bool fileAccepted;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__ImportDialog__) */
|
||||
|
|
|
@ -373,9 +373,18 @@ const glm::vec3 randVector() {
|
|||
}
|
||||
|
||||
static TextRenderer* textRenderer(int mono) {
|
||||
static TextRenderer* monoRenderer = new TextRenderer(MONO_FONT_FAMILY);
|
||||
static TextRenderer* proportionalRenderer = new TextRenderer(SANS_FONT_FAMILY, -1, -1, false, TextRenderer::SHADOW_EFFECT);
|
||||
return mono ? monoRenderer : proportionalRenderer;
|
||||
static TextRenderer* monoRenderer = new TextRenderer(MONO_FONT_FAMILY);
|
||||
static TextRenderer* proportionalRenderer = new TextRenderer(SANS_FONT_FAMILY, -1, -1, false, TextRenderer::SHADOW_EFFECT);
|
||||
static TextRenderer* inconsolataRenderer = new TextRenderer(INCONSOLATA_FONT_FAMILY, -1, QFont::Bold, false);
|
||||
switch (mono) {
|
||||
case 1:
|
||||
return monoRenderer;
|
||||
case 2:
|
||||
return inconsolataRenderer;
|
||||
case 0:
|
||||
default:
|
||||
return proportionalRenderer;
|
||||
}
|
||||
}
|
||||
|
||||
int widthText(float scale, int mono, char const* string) {
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
// the standard mono font family
|
||||
#define MONO_FONT_FAMILY "Courier"
|
||||
|
||||
// the Inconsolata font family
|
||||
#define INCONSOLATA_FONT_FAMILY "Inconsolata"
|
||||
|
||||
void eulerToOrthonormals(glm::vec3 * angles, glm::vec3 * fwd, glm::vec3 * left, glm::vec3 * up);
|
||||
|
||||
float azimuth_to(glm::vec3 head_pos, glm::vec3 source_pos);
|
||||
|
|
|
@ -21,6 +21,9 @@ private:
|
|||
QString _filename;
|
||||
};
|
||||
|
||||
const QString SETTINGS_GROUP_NAME = "VoxelImport";
|
||||
const QString IMPORT_DIALOG_SETTINGS_KEY = "ImportDialogSettings";
|
||||
|
||||
VoxelImporter::VoxelImporter(QWidget* parent)
|
||||
: QObject(parent),
|
||||
_voxelTree(true),
|
||||
|
@ -28,13 +31,20 @@ VoxelImporter::VoxelImporter(QWidget* parent)
|
|||
_currentTask(NULL),
|
||||
_nextTask(NULL) {
|
||||
|
||||
connect(&_importDialog, SIGNAL(previewToggled(bool)), SLOT(preImport()));
|
||||
connect(&_importDialog, SIGNAL(currentChanged(QString)), SLOT(preImport()));
|
||||
connect(&_importDialog, SIGNAL(accepted()), SLOT(import()));
|
||||
}
|
||||
|
||||
void VoxelImporter::init() {
|
||||
_importDialog.init();
|
||||
void VoxelImporter::saveSettings(QSettings* settings) {
|
||||
settings->beginGroup(SETTINGS_GROUP_NAME);
|
||||
settings->setValue(IMPORT_DIALOG_SETTINGS_KEY, _importDialog.saveState());
|
||||
settings->endGroup();
|
||||
}
|
||||
|
||||
void VoxelImporter::init(QSettings* settings) {
|
||||
settings->beginGroup(SETTINGS_GROUP_NAME);
|
||||
_importDialog.restoreState(settings->value(IMPORT_DIALOG_SETTINGS_KEY).toByteArray());
|
||||
settings->endGroup();
|
||||
}
|
||||
|
||||
VoxelImporter::~VoxelImporter() {
|
||||
|
@ -74,15 +84,13 @@ int VoxelImporter::exec() {
|
|||
reset();
|
||||
} else {
|
||||
_importDialog.reset();
|
||||
|
||||
if (_importDialog.getImportIntoClipboard()) {
|
||||
VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem();
|
||||
|
||||
voxelSystem->copySubTreeIntoNewTree(voxelSystem->getTree()->getRoot(),
|
||||
Application::getInstance()->getClipboard(),
|
||||
true);
|
||||
voxelSystem->changeTree(Application::getInstance()->getClipboard());
|
||||
}
|
||||
|
||||
VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem();
|
||||
|
||||
voxelSystem->copySubTreeIntoNewTree(voxelSystem->getTree()->getRoot(),
|
||||
Application::getInstance()->getClipboard(),
|
||||
true);
|
||||
voxelSystem->changeTree(Application::getInstance()->getClipboard());
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -94,24 +102,22 @@ int VoxelImporter::preImport() {
|
|||
if (!QFileInfo(filename).isFile()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_importDialog.getWantPreview()) {
|
||||
_filename = filename;
|
||||
|
||||
if (_nextTask) {
|
||||
delete _nextTask;
|
||||
}
|
||||
|
||||
_nextTask = new ImportTask(_filename);
|
||||
connect(_nextTask, SIGNAL(destroyed()), SLOT(launchTask()));
|
||||
|
||||
if (_currentTask != NULL) {
|
||||
_voxelTree.cancelImport();
|
||||
} else {
|
||||
launchTask();
|
||||
}
|
||||
|
||||
_filename = filename;
|
||||
|
||||
if (_nextTask) {
|
||||
delete _nextTask;
|
||||
}
|
||||
|
||||
|
||||
_nextTask = new ImportTask(_filename);
|
||||
connect(_nextTask, SIGNAL(destroyed()), SLOT(launchTask()));
|
||||
|
||||
if (_currentTask != NULL) {
|
||||
_voxelTree.cancelImport();
|
||||
} else {
|
||||
launchTask();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,8 +23,9 @@ public:
|
|||
VoxelImporter(QWidget* parent = NULL);
|
||||
~VoxelImporter();
|
||||
|
||||
void init();
|
||||
void init(QSettings* settings);
|
||||
void reset();
|
||||
void saveSettings(QSettings* settings);
|
||||
|
||||
VoxelTree* getVoxelTree() { return &_voxelTree; }
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ void AvatarManager::updateAvatars(float deltaTime) {
|
|||
|
||||
// simulate avatars
|
||||
AvatarHash::iterator avatar = _avatarHash.begin();
|
||||
if (avatar != _avatarHash.end()) {
|
||||
while (avatar != _avatarHash.end()) {
|
||||
if (avatar->data()->getOwningAvatarMixer()) {
|
||||
// this avatar's mixer is still around, go ahead and simulate it
|
||||
avatar->data()->simulate(deltaTime, NULL);
|
||||
|
@ -72,6 +72,7 @@ void AvatarManager::updateAvatars(float deltaTime) {
|
|||
|
||||
avatar->data()->setMouseRay(applicationInstance->getMouseRayOrigin(),
|
||||
applicationInstance->getMouseRayDirection());
|
||||
avatar++;
|
||||
} else {
|
||||
// the mixer that owned this avatar is gone, give it to the vector of fades and kill it
|
||||
avatar = removeAvatarAtHashIterator(avatar);
|
||||
|
@ -225,4 +226,4 @@ void AvatarManager::clearHash() {
|
|||
while (removeAvatar != _avatarHash.end()) {
|
||||
removeAvatar = removeAvatarAtHashIterator(removeAvatar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <QNetworkReply>
|
||||
#include <QOpenGLBuffer>
|
||||
#include <QTimer>
|
||||
|
||||
#include "Application.h"
|
||||
#include "GeometryCache.h"
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <QGLWidget>
|
||||
#include <QNetworkReply>
|
||||
#include <QOpenGLFramebufferObject>
|
||||
#include <QTimer>
|
||||
|
||||
#include <glm/gtc/random.hpp>
|
||||
|
||||
|
|
46
interface/src/ui/Snapshot.cpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// Snapshot.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stojce Slavkovski on 1/26/14.
|
||||
//
|
||||
//
|
||||
|
||||
#include "Snapshot.h"
|
||||
|
||||
#include <FileUtils.h>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QFileInfo>
|
||||
#include <QDebug>
|
||||
|
||||
// filename format: hifi-snap-by-%username%-on-%date%_%time%_@-%location%.jpg
|
||||
// %1 <= username, %2 <= date and time, %3 <= current location
|
||||
const QString FILENAME_PATH_FORMAT = "hifi-snap-by-%1-on-%2@%3.jpg";
|
||||
|
||||
const QString DATETIME_FORMAT = "yyyy-MM-dd_hh-mm-ss";
|
||||
const QString SNAPSHOTS_DIRECTORY = "Snapshots";
|
||||
|
||||
void Snapshot::saveSnapshot(QGLWidget* widget, QString username, glm::vec3 location) {
|
||||
QImage shot = widget->grabFrameBuffer();
|
||||
|
||||
// add metadata
|
||||
shot.setText("location-x", QString::number(location.x));
|
||||
shot.setText("location-y", QString::number(location.y));
|
||||
shot.setText("location-z", QString::number(location.z));
|
||||
|
||||
QString formattedLocation = QString("%1_%2_%3").arg(location.x).arg(location.y).arg(location.z);
|
||||
// replace decimal . with '-'
|
||||
formattedLocation.replace('.', '-');
|
||||
|
||||
// normalize username, replace all non alphanumeric with '-'
|
||||
username.replace(QRegExp("[^A-Za-z0-9_]"), "-");
|
||||
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
|
||||
QString fileName = FileUtils::standardPath(SNAPSHOTS_DIRECTORY);
|
||||
fileName.append(QString(FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT), formattedLocation)));
|
||||
shot.save(fileName, 0, 100);
|
||||
}
|
||||
|
||||
|
27
interface/src/ui/Snapshot.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// Snapshot.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stojce Slavkovski on 1/26/14.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__Snapshot__
|
||||
#define __hifi__Snapshot__
|
||||
|
||||
#import <QString>
|
||||
#import <QImage>
|
||||
#import <QGLWidget>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
class Snapshot {
|
||||
|
||||
public:
|
||||
static void saveSnapshot(QGLWidget* widget, QString username, glm::vec3 location);
|
||||
|
||||
private:
|
||||
QString _username;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Snapshot__) */
|
|
@ -289,10 +289,7 @@ int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
return bytesRead;
|
||||
}
|
||||
|
||||
|
||||
Particle Particle::fromEditPacket(const unsigned char* data, int length, int& processedBytes, ParticleTree* tree) {
|
||||
|
||||
//qDebug() << "Particle::fromEditPacket() length=" << length;
|
||||
Particle Particle::fromEditPacket(const unsigned char* data, int length, int& processedBytes, ParticleTree* tree, bool& valid) {
|
||||
|
||||
Particle newParticle; // id and _lastUpdated will get set here...
|
||||
const unsigned char* dataAt = data;
|
||||
|
@ -302,9 +299,6 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
int octets = numberOfThreeBitSectionsInCode(data);
|
||||
int lengthOfOctcode = bytesRequiredForCodeLength(octets);
|
||||
|
||||
//qDebug() << "Particle::fromEditPacket() lengthOfOctcode=" << lengthOfOctcode;
|
||||
//printOctalCode(data);
|
||||
|
||||
// we don't actually do anything with this octcode...
|
||||
dataAt += lengthOfOctcode;
|
||||
processedBytes += lengthOfOctcode;
|
||||
|
@ -321,8 +315,6 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
|
||||
// special case for handling "new" particles
|
||||
if (isNewParticle) {
|
||||
//qDebug() << "editID == NEW_PARTICLE";
|
||||
|
||||
// If this is a NEW_PARTICLE, then we assume that there's an additional uint32_t creatorToken, that
|
||||
// we want to send back to the creator as an map to the actual id
|
||||
uint32_t creatorTokenID;
|
||||
|
@ -330,11 +322,8 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
dataAt += sizeof(creatorTokenID);
|
||||
processedBytes += sizeof(creatorTokenID);
|
||||
|
||||
//qDebug() << "creatorTokenID:" << creatorTokenID;
|
||||
|
||||
newParticle.setCreatorTokenID(creatorTokenID);
|
||||
newParticle._newlyCreated = true;
|
||||
|
||||
newParticle.setAge(0); // this guy is new!
|
||||
|
||||
} else {
|
||||
|
@ -344,15 +333,19 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
// copy existing properties before over-writing with new properties
|
||||
if (existingParticle) {
|
||||
newParticle = *existingParticle;
|
||||
//qDebug() << "newParticle = *existingParticle... calling debugDump()...";
|
||||
//existingParticle->debugDump();
|
||||
} else {
|
||||
// the user attempted to edit a particle that doesn't exist
|
||||
qDebug() << "user attempted to edit a particle that doesn't exist...";
|
||||
valid = false;
|
||||
return newParticle;
|
||||
}
|
||||
|
||||
newParticle._id = editID;
|
||||
newParticle._newlyCreated = false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
// if we got this far, then our result will be valid
|
||||
valid = true;
|
||||
|
||||
|
||||
// lastEdited
|
||||
memcpy(&newParticle._lastEdited, dataAt, sizeof(newParticle._lastEdited));
|
||||
|
@ -838,6 +831,8 @@ ParticleProperties::ParticleProperties() :
|
|||
_inHand(false),
|
||||
_shouldDie(false),
|
||||
|
||||
_id(UNKNOWN_PARTICLE_ID),
|
||||
_idSet(false),
|
||||
_lastEdited(usecTimestampNow()),
|
||||
_positionChanged(false),
|
||||
_colorChanged(false),
|
||||
|
@ -923,6 +918,11 @@ QScriptValue ParticleProperties::copyToScriptValue(QScriptEngine* engine) const
|
|||
properties.setProperty("script", _script);
|
||||
properties.setProperty("inHand", _inHand);
|
||||
properties.setProperty("shouldDie", _shouldDie);
|
||||
|
||||
if (_idSet) {
|
||||
properties.setProperty("id", _id);
|
||||
properties.setProperty("isKnownID", (_id == UNKNOWN_PARTICLE_ID));
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
@ -1116,6 +1116,9 @@ void ParticleProperties::copyFromParticle(const Particle& particle) {
|
|||
_inHand = particle.getInHand();
|
||||
_shouldDie = particle.getShouldDie();
|
||||
|
||||
_id = particle.getID();
|
||||
_idSet = true;
|
||||
|
||||
_positionChanged = false;
|
||||
_colorChanged = false;
|
||||
_radiusChanged = false;
|
||||
|
|
|
@ -94,6 +94,9 @@ public:
|
|||
void setShouldDie(bool shouldDie) { _shouldDie = shouldDie; _shouldDieChanged = true; }
|
||||
void setLifetime(float value) { _lifetime = value; _lifetimeChanged = true; }
|
||||
void setScript(QString updateScript) { _script = updateScript; _scriptChanged = true; }
|
||||
|
||||
/// used by ParticleScriptingInterface to return ParticleProperties for unknown particles
|
||||
void setIsUnknownID() { _id = UNKNOWN_PARTICLE_ID; _idSet = true; }
|
||||
|
||||
private:
|
||||
glm::vec3 _position;
|
||||
|
@ -107,6 +110,8 @@ private:
|
|||
bool _inHand;
|
||||
bool _shouldDie;
|
||||
|
||||
uint32_t _id;
|
||||
bool _idSet;
|
||||
uint64_t _lastEdited;
|
||||
bool _positionChanged;
|
||||
bool _colorChanged;
|
||||
|
@ -145,6 +150,7 @@ public:
|
|||
};
|
||||
|
||||
Q_DECLARE_METATYPE(ParticleID);
|
||||
Q_DECLARE_METATYPE(QVector<ParticleID>);
|
||||
QScriptValue ParticleIDtoScriptValue(QScriptEngine* engine, const ParticleID& properties);
|
||||
void ParticleIDfromScriptValue(const QScriptValue &object, ParticleID& properties);
|
||||
|
||||
|
@ -161,8 +167,9 @@ public:
|
|||
glm::vec3 gravity = DEFAULT_GRAVITY, float damping = DEFAULT_DAMPING, float lifetime = DEFAULT_LIFETIME,
|
||||
bool inHand = NOT_IN_HAND, QString updateScript = DEFAULT_SCRIPT, uint32_t id = NEW_PARTICLE);
|
||||
|
||||
|
||||
/// creates an NEW particle from an PacketType_PARTICLE_ADD_OR_EDIT edit data buffer
|
||||
static Particle fromEditPacket(const unsigned char* data, int length, int& processedBytes, ParticleTree* tree);
|
||||
static Particle fromEditPacket(const unsigned char* data, int length, int& processedBytes, ParticleTree* tree, bool& valid);
|
||||
|
||||
virtual ~Particle();
|
||||
virtual void init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity,
|
||||
|
|
|
@ -161,6 +161,42 @@ const Particle* ParticleTree::findClosestParticle(glm::vec3 position, float targ
|
|||
return args.closestParticle;
|
||||
}
|
||||
|
||||
class FindAllNearPointArgs {
|
||||
public:
|
||||
glm::vec3 position;
|
||||
float targetRadius;
|
||||
QVector<const Particle*> particles;
|
||||
};
|
||||
|
||||
|
||||
bool ParticleTree::findInSphereOperation(OctreeElement* element, void* extraData) {
|
||||
FindAllNearPointArgs* args = static_cast<FindAllNearPointArgs*>(extraData);
|
||||
ParticleTreeElement* particleTreeElement = static_cast<ParticleTreeElement*>(element);
|
||||
|
||||
glm::vec3 penetration;
|
||||
bool sphereIntersection = particleTreeElement->getAABox().findSpherePenetration(args->position,
|
||||
args->targetRadius, penetration);
|
||||
|
||||
// If this particleTreeElement contains the point, then search it...
|
||||
if (sphereIntersection) {
|
||||
QVector<const Particle*> moreParticles = particleTreeElement->getParticles(args->position, args->targetRadius);
|
||||
args->particles << moreParticles;
|
||||
return true; // keep searching in case children have closer particles
|
||||
}
|
||||
|
||||
// if this element doesn't contain the point, then none of it's children can contain the point, so stop searching
|
||||
return false;
|
||||
}
|
||||
|
||||
QVector<const Particle*> ParticleTree::findParticles(const glm::vec3& center, float radius) {
|
||||
QVector<Particle*> result;
|
||||
FindAllNearPointArgs args = { center, radius };
|
||||
lockForRead();
|
||||
recurseTreeWithOperation(findInSphereOperation, &args);
|
||||
unlock();
|
||||
return args.particles;
|
||||
}
|
||||
|
||||
class FindByIDArgs {
|
||||
public:
|
||||
uint32_t id;
|
||||
|
@ -215,23 +251,27 @@ int ParticleTree::processEditPacketData(PacketType packetType, const unsigned ch
|
|||
// we handle these types of "edit" packets
|
||||
switch (packetType) {
|
||||
case PacketTypeParticleAddOrEdit: {
|
||||
//qDebug() << " got PacketType_PARTICLE_ADD_OR_EDIT... ";
|
||||
Particle newParticle = Particle::fromEditPacket(editData, maxLength, processedBytes, this);
|
||||
storeParticle(newParticle, senderNode);
|
||||
if (newParticle.isNewlyCreated()) {
|
||||
notifyNewlyCreatedParticle(newParticle, senderNode);
|
||||
bool isValid;
|
||||
Particle newParticle = Particle::fromEditPacket(editData, maxLength, processedBytes, this, isValid);
|
||||
if (isValid) {
|
||||
storeParticle(newParticle, senderNode);
|
||||
if (newParticle.isNewlyCreated()) {
|
||||
notifyNewlyCreatedParticle(newParticle, senderNode);
|
||||
}
|
||||
}
|
||||
//qDebug() << " DONE... PacketType_PARTICLE_ADD_OR_EDIT... ";
|
||||
return processedBytes;
|
||||
}
|
||||
} break;
|
||||
|
||||
// TODO: wire in support here for server to get PacketType_PARTICLE_ERASE messages
|
||||
// instead of using PacketType_PARTICLE_ADD_OR_EDIT messages to delete particles
|
||||
// TODO: wire in support here for server to get PACKET_TYPE_PARTICLE_ERASE messages
|
||||
// instead of using PACKET_TYPE_PARTICLE_ADD_OR_EDIT messages to delete particles
|
||||
case PacketTypeParticleErase:
|
||||
return 0;
|
||||
processedBytes = 0;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
processedBytes = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return processedBytes;
|
||||
}
|
||||
|
||||
void ParticleTree::notifyNewlyCreatedParticle(const Particle& newParticle, Node* senderNode) {
|
||||
|
|
|
@ -42,6 +42,7 @@ public:
|
|||
void storeParticle(const Particle& particle, Node* senderNode = NULL);
|
||||
const Particle* findClosestParticle(glm::vec3 position, float targetRadius);
|
||||
const Particle* findParticleByID(uint32_t id, bool alreadyLocked = false);
|
||||
QVector<const Particle*> findParticles(const glm::vec3& center, float radius);
|
||||
|
||||
void addNewlyCreatedHook(NewlyCreatedParticleHook* hook);
|
||||
void removeNewlyCreatedHook(NewlyCreatedParticleHook* hook);
|
||||
|
@ -58,6 +59,7 @@ private:
|
|||
static bool updateOperation(OctreeElement* element, void* extraData);
|
||||
static bool findAndUpdateOperation(OctreeElement* element, void* extraData);
|
||||
static bool findNearPointOperation(OctreeElement* element, void* extraData);
|
||||
static bool findInSphereOperation(OctreeElement* element, void* extraData);
|
||||
static bool pruneOperation(OctreeElement* element, void* extraData);
|
||||
static bool findByIDOperation(OctreeElement* element, void* extraData);
|
||||
static bool findAndDeleteOperation(OctreeElement* element, void* extraData);
|
||||
|
|
|
@ -185,6 +185,23 @@ const Particle* ParticleTreeElement::getClosestParticle(glm::vec3 position) cons
|
|||
return closestParticle;
|
||||
}
|
||||
|
||||
QVector<const Particle*> ParticleTreeElement::getParticles(glm::vec3 searchPosition, float searchRadius) const {
|
||||
QVector<const Particle*> results;
|
||||
uint16_t numberOfParticles = _particles->size();
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
const Particle* particle = &(*_particles)[i];
|
||||
glm::vec3 particlePosition = particle->getPosition();
|
||||
float particleRadius = particle->getRadius();
|
||||
glm::vec3 penetration;
|
||||
|
||||
// check to see that the particle (penetrator) penetrates the search area
|
||||
if (findSphereSpherePenetration(particlePosition, particleRadius, searchPosition, searchRadius, penetration)) {
|
||||
results << particle;
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
const Particle* ParticleTreeElement::getParticleWithID(uint32_t id) const {
|
||||
// NOTE: this lookup is O(N) but maybe we don't care? (guaranteed that num particles per elemen is small?)
|
||||
const Particle* foundParticle = NULL;
|
||||
|
|
|
@ -89,6 +89,7 @@ public:
|
|||
bool containsParticle(const Particle& particle) const;
|
||||
bool updateParticle(const Particle& particle);
|
||||
const Particle* getClosestParticle(glm::vec3 position) const;
|
||||
QVector<const Particle*> getParticles(glm::vec3 position, float radius) const;
|
||||
const Particle* getParticleWithID(uint32_t id) const;
|
||||
|
||||
bool removeParticleWithID(uint32_t id);
|
||||
|
|
|
@ -34,15 +34,44 @@ ParticleID ParticlesScriptingInterface::addParticle(const ParticleProperties& pr
|
|||
return id;
|
||||
}
|
||||
|
||||
void ParticlesScriptingInterface::editParticle(ParticleID particleID, const ParticleProperties& properties) {
|
||||
ParticleID ParticlesScriptingInterface::identifyParticle(ParticleID particleID) {
|
||||
uint32_t actualID = particleID.id;
|
||||
if (!particleID.isKnownID) {
|
||||
actualID = Particle::getIDfromCreatorTokenID(particleID.creatorTokenID);
|
||||
// hmmm... we kind of want to bail if someone attempts to edit an unknown
|
||||
if (actualID == UNKNOWN_PARTICLE_ID) {
|
||||
//qDebug() << "ParticlesScriptingInterface::editParticle()... BAILING!!! particleID.creatorTokenID="
|
||||
// << particleID.creatorTokenID;
|
||||
return; // bailing early
|
||||
return particleID; // bailing early
|
||||
}
|
||||
|
||||
// found it!
|
||||
particleID.id = actualID;
|
||||
particleID.isKnownID = true;
|
||||
}
|
||||
return particleID;
|
||||
}
|
||||
|
||||
ParticleProperties ParticlesScriptingInterface::getParticleProperties(ParticleID particleID) {
|
||||
ParticleProperties results;
|
||||
ParticleID identity = identifyParticle(particleID);
|
||||
if (!identity.isKnownID) {
|
||||
results.setIsUnknownID();
|
||||
return results;
|
||||
}
|
||||
if (_particleTree) {
|
||||
const Particle* particle = _particleTree->findParticleByID(identity.id);
|
||||
results.copyFromParticle(*particle);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ParticleID ParticlesScriptingInterface::editParticle(ParticleID particleID, const ParticleProperties& properties) {
|
||||
uint32_t actualID = particleID.id;
|
||||
if (!particleID.isKnownID) {
|
||||
actualID = Particle::getIDfromCreatorTokenID(particleID.creatorTokenID);
|
||||
if (actualID == UNKNOWN_PARTICLE_ID) {
|
||||
return particleID; // bailing early
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,7 +99,9 @@ void ParticlesScriptingInterface::editParticle(ParticleID particleID, const Part
|
|||
qDebug() << "ParticlesScriptingInterface::editParticle()... properties.getInHand()=" << properties.getInHand();
|
||||
}
|
||||
}
|
||||
|
||||
queueParticleMessage(PacketTypeParticleAddOrEdit, particleID, properties);
|
||||
return particleID;
|
||||
}
|
||||
|
||||
|
||||
|
@ -116,3 +147,17 @@ ParticleID ParticlesScriptingInterface::findClosestParticle(const glm::vec3& cen
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
QVector<ParticleID> ParticlesScriptingInterface::findParticles(const glm::vec3& center, float radius) const {
|
||||
QVector<ParticleID> result;
|
||||
if (_particleTree) {
|
||||
QVector<const Particle*> particles = _particleTree->findParticles(center/(float)TREE_SCALE, radius/(float)TREE_SCALE);
|
||||
|
||||
foreach (const Particle* particle, particles) {
|
||||
ParticleID thisParticleID(particle->getID(), UNKNOWN_TOKEN, true);
|
||||
result << thisParticleID;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,11 +28,33 @@ public:
|
|||
ParticleTree* getParticleTree(ParticleTree*) { return _particleTree; }
|
||||
|
||||
public slots:
|
||||
/// adds a particle with the specific properties
|
||||
ParticleID addParticle(const ParticleProperties& properties);
|
||||
void editParticle(ParticleID particleID, const ParticleProperties& properties);
|
||||
|
||||
/// identify a recently created particle to determine its true ID
|
||||
ParticleID identifyParticle(ParticleID particleID);
|
||||
|
||||
/// gets the current particle properties for a specific particle
|
||||
/// this function will not find return results in script engine contexts which don't have access to particles
|
||||
ParticleProperties getParticleProperties(ParticleID particleID);
|
||||
|
||||
/// edits a particle updating only the included properties, will return the identified ParticleID in case of
|
||||
/// successful edit, if the input particleID is for an unknown particle this function will have no effect
|
||||
ParticleID editParticle(ParticleID particleID, const ParticleProperties& properties);
|
||||
|
||||
/// deletes a particle
|
||||
void deleteParticle(ParticleID particleID);
|
||||
|
||||
/// finds the closest particle to the center point, within the radius
|
||||
/// will return a ParticleID.isKnownID = false if no particles are in the radius
|
||||
/// this function will not find any particles in script engine contexts which don't have access to particles
|
||||
ParticleID findClosestParticle(const glm::vec3& center, float radius) const;
|
||||
|
||||
/// finds particles within the search sphere specified by the center point and radius
|
||||
/// this function will not find any particles in script engine contexts which don't have access to particles
|
||||
QVector<ParticleID> findParticles(const glm::vec3& center, float radius) const;
|
||||
|
||||
|
||||
private:
|
||||
void queueParticleMessage(PacketType packetType, ParticleID particleID, const ParticleProperties& properties);
|
||||
|
||||
|
|
|
@ -118,6 +118,7 @@ void ScriptEngine::init() {
|
|||
registerMetaTypes(&_engine);
|
||||
qScriptRegisterMetaType(&_engine, ParticlePropertiesToScriptValue, ParticlePropertiesFromScriptValue);
|
||||
qScriptRegisterMetaType(&_engine, ParticleIDtoScriptValue, ParticleIDfromScriptValue);
|
||||
qScriptRegisterSequenceMetaType<QVector<ParticleID> >(&_engine);
|
||||
|
||||
QScriptValue agentValue = _engine.newQObject(this);
|
||||
_engine.globalObject().setProperty("Agent", agentValue);
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
#include <QtCore>
|
||||
#include <QDesktopServices>
|
||||
|
||||
void FileUtils::LocateFile(QString filePath) {
|
||||
void FileUtils::locateFile(QString filePath) {
|
||||
|
||||
// adopted from
|
||||
// adapted from
|
||||
// http://stackoverflow.com/questions/3490336/how-to-reveal-in-finder-or-show-in-explorer-with-qt
|
||||
// and
|
||||
// http://lynxline.com/show-in-finder-show-in-explorer/
|
||||
|
@ -54,3 +54,26 @@ void FileUtils::LocateFile(QString filePath) {
|
|||
QDesktopServices::openUrl(QUrl::fromLocalFile(folder));
|
||||
}
|
||||
}
|
||||
|
||||
QString FileUtils::standardPath(QString subfolder) {
|
||||
// standard path
|
||||
// Mac: ~/Library/Application Support/Interface
|
||||
QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
|
||||
path.append("/Interface");
|
||||
|
||||
if (!subfolder.startsWith("/")) {
|
||||
subfolder.prepend("/");
|
||||
}
|
||||
|
||||
if (!subfolder.endsWith("/")) {
|
||||
subfolder.append("/");
|
||||
}
|
||||
|
||||
path.append(subfolder);
|
||||
QDir logDir(path);
|
||||
if (!logDir.exists(path)) {
|
||||
logDir.mkpath(path);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
class FileUtils {
|
||||
|
||||
public:
|
||||
static void LocateFile(QString);
|
||||
static void locateFile(QString fileName);
|
||||
static QString standardPath(QString subfolder);
|
||||
|
||||
};
|
||||
|
||||
|
|