diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 60129aa444..f3efccf31a 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -217,6 +218,8 @@ void Agent::run() { _scriptEngine.registerGlobalObject("Agent", this); _scriptEngine.init(); // must be done before we set up the viewers + + _scriptEngine.registerGlobalObject("SoundCache", &SoundCache::getInstance()); _scriptEngine.registerGlobalObject("VoxelViewer", &_voxelViewer); // connect the VoxelViewer and the VoxelScriptingInterface to each other diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 4173cacfc7..6c8a4fd1c2 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -774,7 +774,8 @@ void AudioMixer::run() { nodeData->checkBuffersBeforeFrameSend(); // if the stream should be muted, send mute packet - if (shouldMute(nodeData->getAvatarAudioStream()->getQuietestFrameLoudness())) { + if (nodeData->getAvatarAudioStream() + && shouldMute(nodeData->getAvatarAudioStream()->getQuietestFrameLoudness())) { static const int TIME_BETWEEN_MUTES = 5; // in secs if (usecTimestampNow() - nodeData->getAvatarAudioStream()->getLastMuted() > TIME_BETWEEN_MUTES * USECS_PER_SECOND) { diff --git a/examples/airGuitar.js b/examples/airGuitar.js index 2c3d0409fa..5a62ee3e7b 100644 --- a/examples/airGuitar.js +++ b/examples/airGuitar.js @@ -34,22 +34,22 @@ var guitarModel = HIFI_PUBLIC_BUCKET + "models/attachments/guitar.fst"; var chords = new Array(); // Nylon string guitar -chords[1] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Nylon+A.raw"); -chords[2] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Nylon+B.raw"); -chords[3] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Nylon+E.raw"); -chords[4] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Nylon+G.raw"); +chords[1] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Nylon+A.raw"); +chords[2] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Nylon+B.raw"); +chords[3] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Nylon+E.raw"); +chords[4] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Nylon+G.raw"); // Electric guitar -chords[5] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Metal+A+short.raw"); -chords[6] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Metal+B+short.raw"); -chords[7] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Metal+E+short.raw"); -chords[8] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Metal+G+short.raw"); +chords[5] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Metal+A+short.raw"); +chords[6] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Metal+B+short.raw"); +chords[7] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Metal+E+short.raw"); +chords[8] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Metal+G+short.raw"); // Steel Guitar -chords[9] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Steel+A.raw"); -chords[10] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Steel+B.raw"); -chords[11] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Steel+E.raw"); -chords[12] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Steel+G.raw"); +chords[9] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Steel+A.raw"); +chords[10] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Steel+B.raw"); +chords[11] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Steel+E.raw"); +chords[12] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Steel+G.raw"); var NUM_CHORDS = 4; var NUM_GUITARS = 3; diff --git a/examples/audioBall.js b/examples/audioBall.js index ca666285a9..e0c0ce7976 100644 --- a/examples/audioBall.js +++ b/examples/audioBall.js @@ -15,7 +15,7 @@ Script.include("libraries/globals.js"); -var sound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Animals/mexicanWhipoorwill.raw"); +var sound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Animals/mexicanWhipoorwill.raw"); var CHANCE_OF_PLAYING_SOUND = 0.01; var FACTOR = 0.05; diff --git a/examples/avatarCollision.js b/examples/avatarCollision.js index 4bd0adf69a..ce13daa50d 100644 --- a/examples/avatarCollision.js +++ b/examples/avatarCollision.js @@ -24,28 +24,28 @@ var audioOptions = { } var hitSounds = new Array(); -hitSounds[0] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit1.raw"); -hitSounds[1] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit2.raw"); -hitSounds[2] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit3.raw"); -hitSounds[3] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit4.raw"); -hitSounds[4] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit5.raw"); -hitSounds[5] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit6.raw"); -hitSounds[6] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit7.raw"); -hitSounds[7] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit8.raw"); -hitSounds[8] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit9.raw"); -hitSounds[9] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit10.raw"); -hitSounds[10] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit11.raw"); -hitSounds[11] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit12.raw"); -hitSounds[12] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit13.raw"); -hitSounds[13] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit14.raw"); -hitSounds[14] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit15.raw"); -hitSounds[15] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit16.raw"); -hitSounds[16] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit17.raw"); -hitSounds[17] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit18.raw"); -hitSounds[18] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit19.raw"); -hitSounds[19] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit20.raw"); -hitSounds[20] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit21.raw"); -hitSounds[21] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit22.raw"); +hitSounds[0] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit1.raw"); +hitSounds[1] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit2.raw"); +hitSounds[2] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit3.raw"); +hitSounds[3] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit4.raw"); +hitSounds[4] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit5.raw"); +hitSounds[5] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit6.raw"); +hitSounds[6] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit7.raw"); +hitSounds[7] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit8.raw"); +hitSounds[8] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit9.raw"); +hitSounds[9] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit10.raw"); +hitSounds[10] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit11.raw"); +hitSounds[11] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit12.raw"); +hitSounds[12] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit13.raw"); +hitSounds[13] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit14.raw"); +hitSounds[14] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit15.raw"); +hitSounds[15] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit16.raw"); +hitSounds[16] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit17.raw"); +hitSounds[17] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit18.raw"); +hitSounds[18] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit19.raw"); +hitSounds[19] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit20.raw"); +hitSounds[20] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit21.raw"); +hitSounds[21] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit22.raw"); function playHitSound(mySessionID, theirSessionID, collision) { var now = new Date(); diff --git a/examples/birdSongs.js b/examples/birdSongs.js index 680cb025ad..267fa20b49 100644 --- a/examples/birdSongs.js +++ b/examples/birdSongs.js @@ -67,7 +67,7 @@ function maybePlaySound(deltaTime) { lifetime: 10 }); } - + playing.push({ audioId: Audio.playSound(birds[whichBird].sound, options), entityId: entityId, lightId: lightId, color: birds[whichBird].color }); } if (playing.length != numPlaying) { @@ -159,8 +159,9 @@ function loadBirds() { var SOUND_BASE_URL = "http://public.highfidelity.io/sounds/Animals/"; for (var i = 0; i < sound_filenames.length; i++) { - birds.push({ sound: new Sound(SOUND_BASE_URL + sound_filenames[i]), - color: colors[i] - } ); + birds.push({ + sound: SoundCache.getSound(SOUND_BASE_URL + sound_filenames[i]), + color: colors[i] + }); } } \ No newline at end of file diff --git a/examples/bot.js b/examples/bot.js index 09c9a51cdd..aadd038eb6 100644 --- a/examples/bot.js +++ b/examples/bot.js @@ -228,6 +228,6 @@ function loadSounds() { var SOUND_BASE_URL = HIFI_PUBLIC_BUCKET + "sounds/Cocktail+Party+Snippets/Raws/"; for (var i = 0; i < sound_filenames.length; i++) { - sounds.push(new Sound(SOUND_BASE_URL + sound_filenames[i])); + sounds.push(SoundCache.getSound(SOUND_BASE_URL + sound_filenames[i])); } } diff --git a/examples/botProceduralWayPoints.js b/examples/botProceduralWayPoints.js index 0f8b369470..e20714bd27 100644 --- a/examples/botProceduralWayPoints.js +++ b/examples/botProceduralWayPoints.js @@ -151,11 +151,11 @@ function loadSounds() { var FOOTSTEP_BASE_URL = HIFI_PUBLIC_BUCKET + "sounds/Footsteps/"; for (var i = 0; i < sound_filenames.length; i++) { - sounds.push(new Sound(SOUND_BASE_URL + sound_filenames[i])); + sounds.push(SoundCache.getSound(SOUND_BASE_URL + sound_filenames[i])); } for (var i = 0; i < footstep_filenames.length; i++) { - footstepSounds.push(new Sound(FOOTSTEP_BASE_URL + footstep_filenames[i])); + footstepSounds.push(SoundCache.getSound(FOOTSTEP_BASE_URL + footstep_filenames[i])); } } diff --git a/examples/bot_procedural.js b/examples/bot_procedural.js index 80f83fcdfa..eec3c8906d 100644 --- a/examples/bot_procedural.js +++ b/examples/bot_procedural.js @@ -113,11 +113,11 @@ function loadSounds() { var FOOTSTEP_BASE_URL = HIFI_PUBLIC_BUCKET + "sounds/Footsteps/"; for (var i = 0; i < sound_filenames.length; i++) { - sounds.push(new Sound(SOUND_BASE_URL + sound_filenames[i])); + sounds.push(SoundCache.getSound(SOUND_BASE_URL + sound_filenames[i])); } for (var i = 0; i < footstep_filenames.length; i++) { - footstepSounds.push(new Sound(FOOTSTEP_BASE_URL + footstep_filenames[i])); + footstepSounds.push(SoundCache.getSound(FOOTSTEP_BASE_URL + footstep_filenames[i])); } } diff --git a/examples/clap.js b/examples/clap.js index bf71f13cea..2b011404c0 100644 --- a/examples/clap.js +++ b/examples/clap.js @@ -28,16 +28,16 @@ var lastClapFrame = 0; var lastAnimFrame = 0; var claps = []; -claps.push(new Sound(HIFI_PUBLIC_BUCKET + "sounds/claps/BClap1Rvb.wav")); -claps.push(new Sound(HIFI_PUBLIC_BUCKET + "sounds/claps/BClap2Rvb.wav")); -claps.push(new Sound(HIFI_PUBLIC_BUCKET + "sounds/claps/BClap3Rvb.wav")); -claps.push(new Sound(HIFI_PUBLIC_BUCKET + "sounds/claps/BClap4Rvb.wav")); -claps.push(new Sound(HIFI_PUBLIC_BUCKET + "sounds/claps/BClap5Rvb.wav")); -claps.push(new Sound(HIFI_PUBLIC_BUCKET + "sounds/claps/BClap6Rvb.wav")); -claps.push(new Sound(HIFI_PUBLIC_BUCKET + "sounds/claps/BClap7Rvb.wav")); -claps.push(new Sound(HIFI_PUBLIC_BUCKET + "sounds/claps/BClap8Rvb.wav")); -claps.push(new Sound(HIFI_PUBLIC_BUCKET + "sounds/claps/BClap9Rvb.wav")); -claps.push(new Sound(HIFI_PUBLIC_BUCKET + "sounds/claps/BClap10Rvb.wav")); +claps.push(SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/claps/BClap1Rvb.wav")); +claps.push(SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/claps/BClap2Rvb.wav")); +claps.push(SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/claps/BClap3Rvb.wav")); +claps.push(SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/claps/BClap4Rvb.wav")); +claps.push(SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/claps/BClap5Rvb.wav")); +claps.push(SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/claps/BClap6Rvb.wav")); +claps.push(SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/claps/BClap7Rvb.wav")); +claps.push(SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/claps/BClap8Rvb.wav")); +claps.push(SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/claps/BClap9Rvb.wav")); +claps.push(SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/claps/BClap10Rvb.wav")); var numberOfSounds = claps.length; var clappingNow = false; diff --git a/examples/drumStick.js b/examples/drumStick.js index 1af9ffc3dd..463b653e5f 100644 --- a/examples/drumStick.js +++ b/examples/drumStick.js @@ -28,8 +28,8 @@ function vMinus(a, b) { // First, load two percussion sounds to be used on the sticks -var drum1 = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Drums/RackTomHi.raw"); -var drum2 = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Drums/RackTomLo.raw"); +var drum1 = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Drums/RackTomHi.raw"); +var drum2 = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Drums/RackTomLo.raw"); // State Machine: // 0 = not triggered diff --git a/examples/editModels.js b/examples/editModels.js index d26a6e14b0..7538a83fef 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -1125,12 +1125,12 @@ var toolBar = (function () { browseModelsButton, loadURLMenuItem, loadFileMenuItem, - menuItemWidth = 125, + menuItemWidth, menuItemOffset, menuItemHeight, menuItemMargin = 5, menuTextColor = { red: 255, green: 255, blue: 255 }, - menuBackgoundColor = { red: 18, green: 66, blue: 66 }; + menuBackgroundColor = { red: 18, green: 66, blue: 66 }; function initialize() { toolBar = new ToolBar(0, 0, ToolBar.VERTICAL); @@ -1167,9 +1167,8 @@ var toolBar = (function () { loadURLMenuItem = Overlays.addOverlay("text", { x: newModelButton.x - menuItemWidth, y: newModelButton.y + menuItemOffset, - width: menuItemWidth, height: menuItemHeight, - backgroundColor: menuBackgoundColor, + backgroundColor: menuBackgroundColor, topMargin: menuItemMargin, text: "Model URL", alpha: 0.9, @@ -1179,15 +1178,19 @@ var toolBar = (function () { loadFileMenuItem = Overlays.addOverlay("text", { x: newModelButton.x - menuItemWidth, y: newModelButton.y + menuItemOffset + menuItemHeight, - width: menuItemWidth, height: menuItemHeight, - backgroundColor: menuBackgoundColor, + backgroundColor: menuBackgroundColor, topMargin: menuItemMargin, text: "Model File", alpha: 0.9, visible: false }); + menuItemWidth = Math.max(Overlays.textWidth(loadURLMenuItem, "Model URL"), + Overlays.textWidth(loadFileMenuItem, "Model File")) + 20; + Overlays.editOverlay(loadURLMenuItem, { width: menuItemWidth }); + Overlays.editOverlay(loadFileMenuItem, { width: menuItemWidth }); + newCubeButton = toolBar.addTool({ imageURL: toolIconUrl + "add-cube.svg", subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 0747b9269f..1dd825d3cf 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -78,7 +78,7 @@ function SoundArray() { this.audioOptions = audioOptions this.sounds = new Array(); this.addSound = function (soundURL) { - this.sounds[this.sounds.length] = new Sound(soundURL); + this.sounds[this.sounds.length] = SoundCache.getSound(soundURL); } this.play = function (index) { if (0 <= index && index < this.sounds.length) { @@ -319,7 +319,7 @@ function ScaleSelector() { width: this.SECOND_PART, height: this.height, topMargin: 13, text: this.scale.toString(), - alpha: 0.0, + alpha: 0.9, visible: editToolsOn }); this.powerOverlay = Overlays.addOverlay("text", { diff --git a/examples/entityBirds.js b/examples/entityBirds.js index d18513ba49..9512597af9 100644 --- a/examples/entityBirds.js +++ b/examples/entityBirds.js @@ -70,23 +70,23 @@ function addBird() var size; var which = Math.random(); if (which < 0.2) { - tweet = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Animals/bushtit_1.raw"); + tweet = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Animals/bushtit_1.raw"); color = { red: 100, green: 50, blue: 120 }; size = 0.08; } else if (which < 0.4) { - tweet = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Animals/rosyfacedlovebird.raw"); + tweet = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Animals/rosyfacedlovebird.raw"); color = { red: 100, green: 150, blue: 75 }; size = 0.09; } else if (which < 0.6) { - tweet = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Animals/saysphoebe.raw"); + tweet = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Animals/saysphoebe.raw"); color = { red: 84, green: 121, blue: 36 }; size = 0.05; } else if (which < 0.8) { - tweet = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Animals/mexicanWhipoorwill.raw"); + tweet = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Animals/mexicanWhipoorwill.raw"); color = { red: 23, green: 197, blue: 230 }; size = 0.12; } else { - tweet = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Animals/westernscreechowl.raw"); + tweet = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Animals/westernscreechowl.raw"); color = { red: 50, green: 67, blue: 144 }; size = 0.15; } diff --git a/examples/entityScripts/playSoundOnClick.js b/examples/entityScripts/playSoundOnClick.js index 4ab83a1952..6c1279774b 100644 --- a/examples/entityScripts/playSoundOnClick.js +++ b/examples/entityScripts/playSoundOnClick.js @@ -16,7 +16,7 @@ this.preload = function(entityID) { print("preload("+entityID.id+")"); - bird = new Sound("http://s3.amazonaws.com/hifi-public/sounds/Animals/bushtit_1.raw"); + bird = SoundCache.getSound("http://s3.amazonaws.com/hifi-public/sounds/Animals/bushtit_1.raw"); }; this.clickDownOnEntity = function(entityID, mouseEvent) { diff --git a/examples/entityScripts/playSoundOnEnterOrLeave.js b/examples/entityScripts/playSoundOnEnterOrLeave.js index 07be090c31..f82c05c580 100644 --- a/examples/entityScripts/playSoundOnEnterOrLeave.js +++ b/examples/entityScripts/playSoundOnEnterOrLeave.js @@ -23,7 +23,7 @@ this.preload = function(entityID) { print("preload("+entityID.id+")"); - bird = new Sound("http://s3.amazonaws.com/hifi-public/sounds/Animals/bushtit_1.raw"); + bird = SoundCache.getSound("http://s3.amazonaws.com/hifi-public/sounds/Animals/bushtit_1.raw"); }; this.enterEntity = function(entityID) { diff --git a/examples/frisbee.js b/examples/frisbee.js index 7e266de34b..cf271f4f04 100644 --- a/examples/frisbee.js +++ b/examples/frisbee.js @@ -160,9 +160,9 @@ var rightMouseControl = new MouseControl("RIGHT"); var mouseControls = [leftMouseControl, middleMouseControl, rightMouseControl]; var currentMouseControl = false; -var newSound = new Sound("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/throw.raw"); -var catchSound = new Sound("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/catch.raw"); -var throwSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Switches%20and%20sliders/slider%20-%20whoosh1.raw"); +var newSound = SoundCache.getSound("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/throw.raw"); +var catchSound = SoundCache.getSound("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/catch.raw"); +var throwSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Switches%20and%20sliders/slider%20-%20whoosh1.raw"); var simulatedFrisbees = []; diff --git a/examples/grenadeLauncher.js b/examples/grenadeLauncher.js index e95d8dd79d..3907e41ea6 100644 --- a/examples/grenadeLauncher.js +++ b/examples/grenadeLauncher.js @@ -36,11 +36,11 @@ var RELOAD_INTERVAL = 5; var showScore = false; // Load some sound to use for loading and firing -var fireSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Guns/GUN-SHOT2.raw"); -var loadSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Guns/Gun_Reload_Weapon22.raw"); -var impactSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Guns/BulletImpact2.raw"); -var targetHitSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/hit.raw"); -var targetLaunchSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/shoot.raw"); +var fireSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/GUN-SHOT2.raw"); +var loadSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/Gun_Reload_Weapon22.raw"); +var impactSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/BulletImpact2.raw"); +var targetHitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/hit.raw"); +var targetLaunchSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/shoot.raw"); var gunModel = "http://public.highfidelity.io/models/attachments/HaloGun.fst"; diff --git a/examples/gun.js b/examples/gun.js index 76084ce013..fff78496b2 100644 --- a/examples/gun.js +++ b/examples/gun.js @@ -35,11 +35,11 @@ var RELOAD_INTERVAL = 5; var showScore = false; // Load some sound to use for loading and firing -var fireSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Guns/GUN-SHOT2.raw"); -var loadSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Guns/Gun_Reload_Weapon22.raw"); -var impactSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Guns/BulletImpact2.raw"); -var targetHitSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/hit.raw"); -var targetLaunchSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/shoot.raw"); +var fireSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/GUN-SHOT2.raw"); +var loadSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/Gun_Reload_Weapon22.raw"); +var impactSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/BulletImpact2.raw"); +var targetHitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/hit.raw"); +var targetLaunchSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/shoot.raw"); var gunModel = "http://public.highfidelity.io/models/attachments/HaloGun.fst"; diff --git a/examples/headMove.js b/examples/headMove.js index 957686bb20..56f42984e4 100644 --- a/examples/headMove.js +++ b/examples/headMove.js @@ -70,7 +70,7 @@ function activateWarp() { var WATCH_AVATAR_DISTANCE = 2.5; -var sound = new Sound("http://public.highfidelity.io/sounds/Footsteps/FootstepW2Right-12db.wav"); +var sound = SoundCache.getSound("http://public.highfidelity.io/sounds/Footsteps/FootstepW2Right-12db.wav"); function playSound() { Audio.playSound(sound, { position: MyAvatar.position diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html new file mode 100644 index 0000000000..408e83198f --- /dev/null +++ b/examples/html/entityProperties.html @@ -0,0 +1,561 @@ + + + + + + +
+ +
+
+
+ + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + X + Y + Z + +
+ +
+ + + X + Y + Z + +
+ +
+ + + + +
+
+ + + + +
+
+ + + + +
+ +
+ + + X + Y + Z + +
+
+ + + + +
+
+ + + Pitch + Roll + Yaw + +
+
+ + + + +
+ +
+ + + X + Y + Z + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ + +
+
+ + + Red + Green + Blue + +
+
+ + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + +
+
+ + + + +
+
+ + + Red + Green + Blue + +
+
+ + + Red + Green + Blue + +
+
+ + + Red + Green + Blue + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+
+ + + diff --git a/examples/html/gridControls.html b/examples/html/gridControls.html index e7bf1cdf8c..4cb935e600 100644 --- a/examples/html/gridControls.html +++ b/examples/html/gridControls.html @@ -1,5 +1,6 @@ + -
-
+
- -
-
-
- +
+ + + +
- -
-
-
- +
+ + + +
- -
-
-
- +
+ + + +
- -
-
-
- +
+ + + +
- -
-
-
+
+ + + + +
+ +
+ + +
+ +
+ + + +
+
+ + +
diff --git a/examples/html/style.css b/examples/html/style.css new file mode 100644 index 0000000000..1625fd094f --- /dev/null +++ b/examples/html/style.css @@ -0,0 +1,95 @@ +* { +} + +body { + margin: 0; + padding: 0; + + background-color: #efefef; + font-family: Sans-Serif; + font-size: 12px; + + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +input { + line-height: 2; +} + +.input-left { + display: inline-block; + width: 20px; +} + +.color-box { + display: inline-block; + width: 20px; + height: 20px; + border: 1px solid black; + margin: 2px; + cursor: pointer; +} + +.color-box.highlight { + width: 18px; + height: 18px; + border: 2px solid black; +} + +.section-header { + background: #AAA; + border-bottom: 1px solid #CCC; + background-color: #333333; + color: #999; + padding: 4px; +} + +.section-header label { + font-weight: bold; +} + +.multi-property-section { +} +.property-section { + display: block; + margin: 10 10; + height: 30px; +} + +.property-section label { + font-weight: bold; + vertical-align: middle; +} + +.property-section span { + float: right; +} + +.grid-section { + border-top: 1px solid #DDD; + background-color: #efefef; +} + +input[type=button] { + cursor: pointer; + background-color: #608e96; + border-color: #608e96; + border-radius: 5px; + padding: 5px 10px; + border: 0; + color: #fff; + font-weight: bold; + margin: 0 2px; + margin-top: 5px; + font-size: .9em; +} + +input.coord { + width: 6em; + height: 2em; +} diff --git a/examples/inWorldTestTone.js b/examples/inWorldTestTone.js index b3bf91d14d..4547309faa 100644 --- a/examples/inWorldTestTone.js +++ b/examples/inWorldTestTone.js @@ -13,7 +13,7 @@ Script.include("libraries/globals.js"); -var sound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/220Sine.wav"); +var sound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/220Sine.wav"); var soundPlaying = false; diff --git a/examples/leapHands.js b/examples/leapHands.js index 437637dc3f..04253c8ab6 100644 --- a/examples/leapHands.js +++ b/examples/leapHands.js @@ -17,12 +17,16 @@ var leapHands = (function () { LEAP_ON_HMD_MENU_ITEM = "Leap Motion on HMD", LEAP_OFFSET = 0.019, // Thickness of Leap Motion plus HMD clip HMD_OFFSET = 0.070, // Eyeballs to front surface of Oculus DK2 TODO: Confirm and make depend on device and eye relief + hasHandAndWristJoints, + handToWristOffset = [], // For avatars without a wrist joint we control an estimate of a proper hand joint position + HAND_OFFSET = 0.4, // Relative distance of wrist to hand versus wrist to index finger knuckle hands, wrists, NUM_HANDS = 2, // 0 = left; 1 = right fingers, NUM_FINGERS = 5, // 0 = thumb; ...; 4 = pinky THUMB = 0, + MIDDLE_FINGER = 2, NUM_FINGER_JOINTS = 3, // 0 = metacarpal(hand)-proximal(finger) joint; ...; 2 = intermediate-distal joint MAX_HAND_INACTIVE_COUNT = 20, calibrationStatus, @@ -123,30 +127,41 @@ var leapHands = (function () { function finishCalibration() { var avatarPosition, - avatarOrientation, - avatarHandPosition, + handPosition, + middleFingerPosition, leapHandHeight, h; - if (hands[0].controller.isActive() && hands[1].controller.isActive()) { - leapHandHeight = (hands[0].controller.getAbsTranslation().y + hands[1].controller.getAbsTranslation().y) / 2.0; - } else { - calibrationStatus = UNCALIBRATED; - return; + if (!isOnHMD) { + if (hands[0].controller.isActive() && hands[1].controller.isActive()) { + leapHandHeight = (hands[0].controller.getAbsTranslation().y + hands[1].controller.getAbsTranslation().y) / 2.0; + } else { + calibrationStatus = UNCALIBRATED; + return; + } } avatarPosition = MyAvatar.position; - avatarOrientation = MyAvatar.orientation; for (h = 0; h < NUM_HANDS; h += 1) { - avatarHandPosition = MyAvatar.getJointPosition(hands[h].jointName); - hands[h].zeroPosition = { - x: avatarHandPosition.x - avatarPosition.x, - y: avatarHandPosition.y - avatarPosition.y, - z: avatarPosition.z - avatarHandPosition.z - }; - hands[h].zeroPosition = Vec3.multiplyQbyV(MyAvatar.orientation, hands[h].zeroPosition); - hands[h].zeroPosition.y = hands[h].zeroPosition.y - leapHandHeight; + handPosition = MyAvatar.getJointPosition(hands[h].jointName); + if (!hasHandAndWristJoints) { + middleFingerPosition = MyAvatar.getJointPosition(fingers[h][MIDDLE_FINGER][0].jointName); + handToWristOffset[h] = Vec3.multiply(Vec3.subtract(handPosition, middleFingerPosition), 1.0 - HAND_OFFSET); + } + + if (isOnHMD) { + // Offset of Leap Motion origin from physical eye position + hands[h].zeroPosition = { x: 0.0, y: 0.0, z: HMD_OFFSET + LEAP_OFFSET }; + } else { + hands[h].zeroPosition = { + x: handPosition.x - avatarPosition.x, + y: handPosition.y - avatarPosition.y, + z: avatarPosition.z - handPosition.z + }; + hands[h].zeroPosition = Vec3.multiplyQbyV(MyAvatar.orientation, hands[h].zeroPosition); + hands[h].zeroPosition.y = hands[h].zeroPosition.y - leapHandHeight; + } } MyAvatar.clearJointData("LeftHand"); @@ -161,6 +176,8 @@ var leapHands = (function () { } function calibrate() { + var jointNames, + i; calibrationStatus = CALIBRATING; @@ -168,6 +185,13 @@ var leapHands = (function () { avatarFaceModelURL = MyAvatar.faceModelURL; avatarSkeletonModelURL = MyAvatar.skeletonModelURL; + // Does this skeleton have both wrist and hand joints? + hasHandAndWristJoints = false; + jointNames = MyAvatar.getJointNames(); + for (i = 0; i < jointNames.length; i += 1) { + hasHandAndWristJoints = hasHandAndWristJoints || jointNames[i].toLowerCase() === "leftwrist"; + } + // Set avatar arms vertical, forearms horizontal, as "zero" position for calibration MyAvatar.setJointData("LeftArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, -90.0)); MyAvatar.setJointData("LeftForeArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 180.0)); @@ -176,6 +200,7 @@ var leapHands = (function () { MyAvatar.setJointData("RightForeArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 180.0)); MyAvatar.setJointData("RightHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0)); + // Wait for arms to assume their positions before calculating Script.setTimeout(finishCalibration, CALIBRATION_TIME); } @@ -195,37 +220,34 @@ var leapHands = (function () { function setIsOnHMD() { isOnHMD = Menu.isOptionChecked(LEAP_ON_HMD_MENU_ITEM); - if (isOnHMD) { - print("Leap Motion: Is on HMD"); - - // Offset of Leap Motion origin from physical eye position - hands[0].zeroPosition = { x: 0.0, y: 0.0, z: HMD_OFFSET + LEAP_OFFSET }; - hands[1].zeroPosition = { x: 0.0, y: 0.0, z: HMD_OFFSET + LEAP_OFFSET }; - - calibrationStatus = CALIBRATED; - } else { - print("Leap Motion: Is on desk"); - calibrationStatus = UNCALIBRATED; - } + print("Leap Motion: " + (isOnHMD ? "Is on HMD" : "Is on desk")); } function checkSettings() { - // There is no "scale changed" event so we need check periodically. - if (!isOnHMD && calibrationStatus > UNCALIBRATED && (MyAvatar.scale !== avatarScale + if (calibrationStatus > UNCALIBRATED && (MyAvatar.scale !== avatarScale || MyAvatar.faceModelURL !== avatarFaceModelURL - || MyAvatar.skeletonModelURL !== avatarSkeletonModelURL)) { - print("Leap Motion: Recalibrate because avatar body or scale changed"); + || MyAvatar.skeletonModelURL !== avatarSkeletonModelURL + || Menu.isOptionChecked(LEAP_ON_HMD_MENU_ITEM) !== isOnHMD)) { + print("Leap Motion: Recalibrate..."); calibrationStatus = UNCALIBRATED; - } - // There is a "menu changed" event but we may as well check here. - if (isOnHMD !== Menu.isOptionChecked(LEAP_ON_HMD_MENU_ITEM)) { setIsOnHMD(); } } function setUp() { + wrists = [ + { + jointName: "LeftWrist", + controller: Controller.createInputController("Spatial", "joint_L_wrist") + }, + { + jointName: "RightWrist", + controller: Controller.createInputController("Spatial", "joint_R_wrist") + } + ]; + hands = [ { jointName: "LeftHand", @@ -239,11 +261,6 @@ var leapHands = (function () { } ]; - wrists = [ - { controller: Controller.createInputController("Spatial", "joint_L_wrist") }, - { controller: Controller.createInputController("Spatial", "joint_R_wrist") } - ]; - // The Leap controller's first joint is the hand-metacarpal joint but this joint's data is not used because it's too // dependent on the model skeleton exactly matching the Leap skeleton; using just the second and subsequent joints // seems to work better over all. @@ -306,6 +323,8 @@ var leapHands = (function () { setIsOnHMD(); settingsTimer = Script.setInterval(checkSettings, 2000); + + calibrationStatus = UNCALIBRATED; } function moveHands() { @@ -314,6 +333,7 @@ var leapHands = (function () { j, side, handOffset, + wristOffset, handRotation, locRotation, cameraOrientation, @@ -330,10 +350,18 @@ var leapHands = (function () { } // Hand position ... + handOffset = hands[h].controller.getAbsTranslation(); + handRotation = hands[h].controller.getAbsRotation(); + if (isOnHMD) { + // Adjust to control wrist position if "hand" joint is at wrist ... + if (!hasHandAndWristJoints) { + wristOffset = Vec3.multiplyQbyV(handRotation, handToWristOffset[h]); + handOffset = Vec3.sum(handOffset, wristOffset); + } + // Hand offset in camera coordinates ... - handOffset = hands[h].controller.getAbsTranslation(); handOffset = { x: hands[h].zeroPosition.x - handOffset.x, y: hands[h].zeroPosition.y - handOffset.z, @@ -353,7 +381,6 @@ var leapHands = (function () { handOffset.x = -handOffset.x; // Hand rotation in camera coordinates ... - handRotation = wrists[h].controller.getAbsRotation(); handRotation = { x: handRotation.z, y: handRotation.y, @@ -361,6 +388,7 @@ var leapHands = (function () { w: handRotation.w }; + // Hand rotation in avatar coordinates ... if (h === 0) { handRotation.x = -handRotation.x; handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation); @@ -371,7 +399,6 @@ var leapHands = (function () { handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 0, z: 1 }), handRotation); } - // Hand rotation in avatar coordinates ... cameraOrientation.x = -cameraOrientation.x; cameraOrientation.z = -cameraOrientation.z; handRotation = Quat.multiply(cameraOrientation, handRotation); @@ -379,14 +406,20 @@ var leapHands = (function () { } else { - handOffset = hands[h].controller.getAbsTranslation(); + // Adjust to control wrist position if "hand" joint is at wrist ... + if (!hasHandAndWristJoints) { + wristOffset = Vec3.multiplyQbyV(handRotation, handToWristOffset[h]); + handOffset = Vec3.sum(handOffset, wristOffset); + } + + // Hand offset in camera coordinates ... handOffset = { x: -handOffset.x, y: hands[h].zeroPosition.y + handOffset.y, z: hands[h].zeroPosition.z - handOffset.z }; - handRotation = wrists[h].controller.getAbsRotation(); + // Hand rotation in camera coordinates ... handRotation = { x: handRotation.z, y: handRotation.y, @@ -394,6 +427,7 @@ var leapHands = (function () { w: handRotation.w }; + // Hand rotation in avatar coordinates ... if (h === 0) { handRotation.x = -handRotation.x; handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 1, z: 0 }), @@ -405,9 +439,10 @@ var leapHands = (function () { } } + // Set hand position and orientation ... MyAvatar.setJointModelPositionAndOrientation(hands[h].jointName, handOffset, handRotation, true); - // Finger joints ... + // Set finger joints ... for (i = 0; i < NUM_FINGERS; i += 1) { for (j = 0; j < NUM_FINGER_JOINTS; j += 1) { if (fingers[h][i][j].controller !== null) { diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 22f75cb187..6733ccaf39 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -23,12 +23,13 @@ SelectionManager = (function() { that.savedProperties = {}; - that.eventListener = null; that.selections = []; // These are selections that don't have a known ID yet that.pendingSelections = []; var pendingSelectionTimer = null; + var listeners = []; + that.localRotation = Quat.fromPitchYawRollDegrees(0, 0, 0); that.localPosition = { x: 0, y: 0, z: 0 }; that.localDimensions = { x: 0, y: 0, z: 0 }; @@ -46,8 +47,8 @@ SelectionManager = (function() { } }; - that.setEventListener = function(func) { - that.eventListener = func; + that.addEventListener = function(func) { + listeners.push(func); }; that.hasSelection = function() { @@ -187,8 +188,12 @@ SelectionManager = (function() { SelectionDisplay.setSpaceMode(SPACE_WORLD); } - if (that.eventListener) { - that.eventListener(); + for (var i = 0; i < listeners.length; i++) { + try { + listeners[i](); + } catch (e) { + print("got exception"); + } } }; diff --git a/examples/libraries/gridTool.js b/examples/libraries/gridTool.js index 34e25d6733..d8b84babf9 100644 --- a/examples/libraries/gridTool.js +++ b/examples/libraries/gridTool.js @@ -31,6 +31,7 @@ Grid = function(opts) { that.getMajorIncrement = function() { return minorGridSpacing * majorGridEvery; }; that.visible = false; + that.enabled = false; that.getOrigin = function() { return origin; @@ -38,6 +39,11 @@ Grid = function(opts) { that.getSnapToGrid = function() { return snapToGrid; }; + that.setEnabled = function(enabled) { + that.enabled = enabled; + updateGrid(); + } + that.setVisible = function(visible, noUpdate) { that.visible = visible; updateGrid(); @@ -127,7 +133,7 @@ Grid = function(opts) { function updateGrid() { Overlays.editOverlay(gridOverlay, { position: { x: origin.y, y: origin.y, z: -origin.y }, - visible: that.visible, + visible: that.visible && that.enabled, minorGridWidth: minorGridSpacing, majorGridEvery: majorGridEvery, color: gridColor, @@ -159,7 +165,7 @@ GridTool = function(opts) { var listeners = []; var url = Script.resolvePath('html/gridControls.html'); - var webView = new WebWindow(url, 200, 280); + var webView = new WebWindow('Grid', url, 200, 280); horizontalGrid.addListener(function(data) { webView.eventBridge.emitScriptEvent(JSON.stringify(data)); @@ -174,6 +180,15 @@ GridTool = function(opts) { for (var i = 0; i < listeners.length; i++) { listeners[i](data); } + } else if (data.type == "action") { + var action = data.action; + if (action == "moveToAvatar") { + grid.setPosition(MyAvatar.position); + } else if (action == "moveToSelection") { + var newPosition = selectionManager.worldPosition; + newPosition = Vec3.subtract(newPosition, { x: 0, y: selectionManager.worldDimensions.y * 0.5, z: 0 }); + grid.setPosition(newPosition); + } } }); diff --git a/examples/libraries/walkApi.js b/examples/libraries/walkApi.js index 0175c56e18..e61335f6f0 100644 --- a/examples/libraries/walkApi.js +++ b/examples/libraries/walkApi.js @@ -335,12 +335,12 @@ walkAssets = (function () { // read in the sounds var _footsteps = []; - _footsteps.push(new Sound(_pathToSounds+"FootstepW2Left-12db.wav")); - _footsteps.push(new Sound(_pathToSounds+"FootstepW2Right-12db.wav")); - _footsteps.push(new Sound(_pathToSounds+"FootstepW3Left-12db.wav")); - _footsteps.push(new Sound(_pathToSounds+"FootstepW3Right-12db.wav")); - _footsteps.push(new Sound(_pathToSounds+"FootstepW5Left-12db.wav")); - _footsteps.push(new Sound(_pathToSounds+"FootstepW5Right-12db.wav")); + _footsteps.push(SoundCache.getSound(_pathToSounds+"FootstepW2Left-12db.wav")); + _footsteps.push(SoundCache.getSound(_pathToSounds+"FootstepW2Right-12db.wav")); + _footsteps.push(SoundCache.getSound(_pathToSounds+"FootstepW3Left-12db.wav")); + _footsteps.push(SoundCache.getSound(_pathToSounds+"FootstepW3Right-12db.wav")); + _footsteps.push(SoundCache.getSound(_pathToSounds+"FootstepW5Left-12db.wav")); + _footsteps.push(SoundCache.getSound(_pathToSounds+"FootstepW5Right-12db.wav")); // load the animation datafiles Script.include(pathToAssets+"animations/dd-female-standard-walk-animation.js"); diff --git a/examples/lobby.js b/examples/lobby.js index 1b6596efa7..bb033971b3 100644 --- a/examples/lobby.js +++ b/examples/lobby.js @@ -39,11 +39,11 @@ var ORB_SHIFT = { x: 0, y: -1.4, z: -0.8}; var HELMET_ATTACHMENT_URL = HIFI_PUBLIC_BUCKET + "models/attachments/IronManMaskOnly.fbx" -var droneSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/drone.raw") +var droneSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/drone.raw") var currentDrone = null; -var latinSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/latin.raw") -var elevatorSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/elevator.raw") +var latinSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/latin.raw") +var elevatorSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/elevator.raw") var currentMusak = null; function reticlePosition() { diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index 68e0d0c146..cc3c0fceda 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -39,7 +39,7 @@ Script.include("libraries/gridTool.js"); var grid = Grid(); gridTool = GridTool({ horizontalGrid: grid }); -selectionManager.setEventListener(selectionDisplay.updateHandles); +selectionManager.addEventListener(selectionDisplay.updateHandles); var windowDimensions = Controller.getViewportDimensions(); var toolIconUrl = HIFI_PUBLIC_BUCKET + "images/tools/"; @@ -55,13 +55,11 @@ var wantEntityGlow = false; var SPAWN_DISTANCE = 1; var DEFAULT_DIMENSION = 0.20; -var MENU_GRID_TOOL_ENABLED = 'Grid Tool'; var MENU_INSPECT_TOOL_ENABLED = "Inspect Tool"; var MENU_EASE_ON_FOCUS = "Ease Orientation on Focus"; var SETTING_INSPECT_TOOL_ENABLED = "inspectToolEnabled"; var SETTING_EASE_ON_FOCUS = "cameraEaseOnFocus"; -var SETTING_GRID_TOOL_ENABLED = 'GridToolEnabled'; var modelURLs = [ HIFI_PUBLIC_BUCKET + "models/entities/2-Terrain:%20Alder.fbx", @@ -89,12 +87,12 @@ var toolBar = (function () { browseModelsButton, loadURLMenuItem, loadFileMenuItem, - menuItemWidth = 125, + menuItemWidth, menuItemOffset, menuItemHeight, menuItemMargin = 5, menuTextColor = { red: 255, green: 255, blue: 255 }, - menuBackgoundColor = { red: 18, green: 66, blue: 66 }; + menuBackgroundColor = { red: 18, green: 66, blue: 66 }; function initialize() { toolBar = new ToolBar(0, 0, ToolBar.VERTICAL); @@ -131,9 +129,8 @@ var toolBar = (function () { loadURLMenuItem = Overlays.addOverlay("text", { x: newModelButton.x - menuItemWidth, y: newModelButton.y + menuItemOffset, - width: menuItemWidth, height: menuItemHeight, - backgroundColor: menuBackgoundColor, + backgroundColor: menuBackgroundColor, topMargin: menuItemMargin, text: "Model URL", alpha: 0.9, @@ -143,15 +140,19 @@ var toolBar = (function () { loadFileMenuItem = Overlays.addOverlay("text", { x: newModelButton.x - menuItemWidth, y: newModelButton.y + menuItemOffset + menuItemHeight, - width: menuItemWidth, height: menuItemHeight, - backgroundColor: menuBackgoundColor, + backgroundColor: menuBackgroundColor, topMargin: menuItemMargin, text: "Model File", alpha: 0.9, visible: false }); + menuItemWidth = Math.max(Overlays.textWidth(loadURLMenuItem, "Model URL"), + Overlays.textWidth(loadFileMenuItem, "Model File")) + 20; + Overlays.editOverlay(loadURLMenuItem, { width: menuItemWidth }); + Overlays.editOverlay(loadFileMenuItem, { width: menuItemWidth }); + newCubeButton = toolBar.addTool({ imageURL: toolIconUrl + "add-cube.svg", subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, @@ -269,11 +270,15 @@ var toolBar = (function () { isActive = !isActive; if (!isActive) { gridTool.setVisible(false); + grid.setEnabled(false); + propertiesTool.setVisible(false); selectionManager.clearSelections(); cameraManager.disable(); } else { cameraManager.enable(); - gridTool.setVisible(Menu.isOptionChecked(MENU_GRID_TOOL_ENABLED)); + gridTool.setVisible(true); + grid.setEnabled(true); + propertiesTool.setVisible(true); } return true; } @@ -605,10 +610,6 @@ function setupModelMenus() { Menu.addMenuItem({ menuName: "File", menuItemName: "Import Models", shortcutKey: "CTRL+META+I", afterItem: "Export Models" }); Menu.addMenuItem({ menuName: "Developer", menuItemName: "Debug Ryans Rotation Problems", isCheckable: true }); - Menu.addMenuItem({ menuName: "View", menuItemName: MENU_GRID_TOOL_ENABLED, afterItem: "Edit Entities Help...", isCheckable: true, - isChecked: Settings.getValue(SETTING_GRID_TOOL_ENABLED) == 'true'}); - Menu.addMenuItem({ menuName: "View", menuItemName: MENU_INSPECT_TOOL_ENABLED, afterItem: MENU_GRID_TOOL_ENABLED, - isCheckable: true, isChecked: Settings.getValue(SETTING_INSPECT_TOOL_ENABLED) == "true" }); Menu.addMenuItem({ menuName: "View", menuItemName: MENU_EASE_ON_FOCUS, afterItem: MENU_INSPECT_TOOL_ENABLED, isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" }); } @@ -633,8 +634,6 @@ function cleanupModelMenus() { Menu.removeMenuItem("File", "Import Models"); Menu.removeMenuItem("Developer", "Debug Ryans Rotation Problems"); - Settings.setValue(SETTING_GRID_TOOL_ENABLED, Menu.isOptionChecked(MENU_GRID_TOOL_ENABLED)); - Menu.removeMenuItem("View", MENU_GRID_TOOL_ENABLED); Menu.removeMenuItem("View", MENU_INSPECT_TOOL_ENABLED); Menu.removeMenuItem("View", MENU_EASE_ON_FOCUS); } @@ -746,10 +745,6 @@ function handeMenuEvent(menuItem) { } } else if (menuItem == "Import Models") { modelImporter.doImport(); - } else if (menuItem == MENU_GRID_TOOL_ENABLED) { - if (isActive) { - gridTool.setVisible(Menu.isOptionChecked(MENU_GRID_TOOL_ENABLED)); - } } tooltip.show(false); } @@ -908,3 +903,43 @@ function pushCommandForSelections(createdEntityData, deletedEntityData) { } UndoStack.pushCommand(applyEntityProperties, undoData, applyEntityProperties, redoData); } + +PropertiesTool = function(opts) { + var that = {}; + + var url = Script.resolvePath('html/entityProperties.html'); + var webView = new WebWindow('Entity Properties', url, 200, 280); + + var visible = false; + + webView.setVisible(visible); + + that.setVisible = function(newVisible) { + visible = newVisible; + webView.setVisible(visible); + }; + + selectionManager.addEventListener(function() { + data = { + type: 'update', + }; + if (selectionManager.hasSelection()) { + data.properties = Entities.getEntityProperties(selectionManager.selections[0]); + } + webView.eventBridge.emitScriptEvent(JSON.stringify(data)); + }); + + webView.eventBridge.webEventReceived.connect(function(data) { + print(data); + data = JSON.parse(data); + if (data.type == "update") { + Entities.editEntity(selectionManager.selections[0], data.properties); + selectionManager._update(); + } + }); + + return that; +}; + +propertiesTool = PropertiesTool(); + diff --git a/examples/overlaysExample.js b/examples/overlaysExample.js index c7bc28db96..0d47f7ca64 100644 --- a/examples/overlaysExample.js +++ b/examples/overlaysExample.js @@ -167,6 +167,51 @@ var clipboardPreview = Overlays.addOverlay("clipboard", { visible: true }); +// Demonstrate retrieving overlay properties +print("Text overlay text property value =\n" + Overlays.getProperty(text, "text")); +print("Text overlay alpha =\n" + Overlays.getProperty(text, "alpha")); +print("Text overlay visible =\n" + Overlays.getProperty(text, "visible")); +print("Text overlay font size =\n" + Overlays.getProperty(text, "font").size); +print("Text overlay anchor =\n" + Overlays.getProperty(text, "anchor")); +print("Text overlay unknown property value =\n" + Overlays.getProperty(text, "unknown")); // value = undefined +var sliderBounds = Overlays.getProperty(slider, "bounds"); +print("Slider overlay bounds =\n" + + "x: " + sliderBounds.x + "\n" + + "y: " + sliderBounds.y + "\n" + + "width: " + sliderBounds.width + "\n" + + "height: " + sliderBounds.height + ); + +var cubePosition = Overlays.getProperty(cube, "position"); +print("Cube overlay position =\n" + + "x: " + cubePosition.x + "\n" + + "y: " + cubePosition.y + "\n" + + "z: " + cubePosition.z + ); +var cubeColor = Overlays.getProperty(cube, "color"); +print("Cube overlay color =\n" + + "red: " + cubeColor.red + "\n" + + "green: " + cubeColor.green + "\n" + + "blue: " + cubeColor.blue + ); + +// This model overlay example causes intermittent crashes in NetworkGeometry::setTextureWithNameToURL() +//var modelOverlayProperties = { +// textures: { +// filename1: HIFI_PUBLIC_BUCKET + "images/testing-swatches.svg", +// filename2: HIFI_PUBLIC_BUCKET + "images/hifi-interface-tools.svg" +// } +//} +//var modelOverlay = Overlays.addOverlay("model", modelOverlayProperties); +//var textures = Overlays.getProperty(modelOverlay, "textures"); +//var textureValues = ""; +//for (key in textures) { +// textureValues += "\n" + key + ": " + textures[key]; +//} +//print("Model overlay textures =" + textureValues); +//Overlays.deleteOverlay(modelOverlay); + +print("Unknown overlay property =\n" + Overlays.getProperty(1000, "text")); // value = undefined // When our script shuts down, we should clean up all of our overlays function scriptEnding() { diff --git a/examples/playSound.js b/examples/playSound.js index efcda0b42b..bc21204665 100644 --- a/examples/playSound.js +++ b/examples/playSound.js @@ -11,7 +11,7 @@ Script.include("libraries/globals.js"); // First, load a sample sound from a URL -var bird = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Animals/bushtit_1.raw"); +var bird = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Animals/bushtit_1.raw"); function maybePlaySound(deltaTime) { if (Math.random() < 0.01) { diff --git a/examples/playSoundLoop.js b/examples/playSoundLoop.js index b84c475d1a..f7116cb615 100644 --- a/examples/playSoundLoop.js +++ b/examples/playSoundLoop.js @@ -15,9 +15,9 @@ Script.include("libraries/globals.js"); // A few sample files you may want to try: -var sound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Nylon+A.raw"); -//var sound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/220Sine.wav"); -//var sound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Cocktail+Party+Snippets/Bandcamp.wav"); +var sound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Nylon+A.raw"); +//var sound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/220Sine.wav"); +//var sound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Cocktail+Party+Snippets/Bandcamp.wav"); var soundPlaying = false; var options = { diff --git a/examples/playSoundOrbit.js b/examples/playSoundOrbit.js index d98f7d0768..16ba5e52af 100644 --- a/examples/playSoundOrbit.js +++ b/examples/playSoundOrbit.js @@ -11,7 +11,7 @@ Script.include("libraries/globals.js"); -var soundClip = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Voxels/voxel create 3.raw"); +var soundClip = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Voxels/voxel create 3.raw"); var currentTime = 1.570079; // pi/2 var deltaTime = 0.05; diff --git a/examples/playSoundWave.js b/examples/playSoundWave.js index c5e69f5cd6..0741b72ef0 100644 --- a/examples/playSoundWave.js +++ b/examples/playSoundWave.js @@ -11,7 +11,7 @@ Script.include("libraries/globals.js"); -var soundClip = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Cocktail%20Party%20Snippets/Walken1.wav"); +var soundClip = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Cocktail%20Party%20Snippets/Walken1.wav"); function playSound() { Audio.playSound(soundClip, { diff --git a/examples/radio.js b/examples/radio.js index fc09fb184e..0b62d78b0e 100644 --- a/examples/radio.js +++ b/examples/radio.js @@ -23,7 +23,7 @@ var audioOptions = { var injector = null; -var sound = new Sound(soundURL, audioOptions.isStereo); +var sound = SoundCache.getSound(soundURL, audioOptions.isStereo); var entity = null; var properties = null; diff --git a/examples/spaceInvadersExample.js b/examples/spaceInvadersExample.js index dd5ac9e875..832efe7e75 100644 --- a/examples/spaceInvadersExample.js +++ b/examples/spaceInvadersExample.js @@ -84,13 +84,13 @@ var missileFired = false; var myMissile; // sounds -var hitSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/hit.raw"); -var shootSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/shoot.raw"); +var hitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/hit.raw"); +var shootSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/shoot.raw"); var moveSounds = new Array(); -moveSounds[0] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/Lo1.raw"); -moveSounds[1] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/Lo2.raw"); -moveSounds[2] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/Lo3.raw"); -moveSounds[3] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/Lo4.raw"); +moveSounds[0] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/Lo1.raw"); +moveSounds[1] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/Lo2.raw"); +moveSounds[2] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/Lo3.raw"); +moveSounds[3] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/Lo4.raw"); var currentMoveSound = 0; var numberOfSounds = 4; var stepsPerSound = invaderStepsPerCycle / numberOfSounds; diff --git a/examples/toyball.js b/examples/toyball.js index 1cd6de16eb..e39ca9c8b4 100644 --- a/examples/toyball.js +++ b/examples/toyball.js @@ -39,9 +39,9 @@ var rightBallAlreadyInHand = false; var leftHandEntity; var rightHandEntity; -var newSound = new Sound("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/throw.raw"); -var catchSound = new Sound("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/catch.raw"); -var throwSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Switches%20and%20sliders/slider%20-%20whoosh1.raw"); +var newSound = SoundCache.getSound("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/throw.raw"); +var catchSound = SoundCache.getSound("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/catch.raw"); +var throwSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Switches%20and%20sliders/slider%20-%20whoosh1.raw"); var targetRadius = 1.0; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c3ab4fe115..2ecf45922b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -65,6 +65,7 @@ #include #include #include +#include #include #include @@ -123,7 +124,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt QString logMessage = LogHandler::getInstance().printMessage((LogMsgType) type, context, message); if (!logMessage.isEmpty()) { - Application::getInstance()->getLogger()->addMessage(qPrintable(logMessage)); + Application::getInstance()->getLogger()->addMessage(qPrintable(logMessage + "\n")); } } @@ -160,7 +161,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _lastQueriedViewFrustum(), _lastQueriedTime(usecTimestampNow()), _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), - _viewTransform(new gpu::Transform()), + _viewTransform(), _scaleMirror(1.0f), _rotateMirror(0.0f), _raiseMirror(0.0f), @@ -372,6 +373,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // enable mouse tracking; otherwise, we only get drag events _glWidget->setMouseTracking(true); + _toolWindow = new ToolWindow(); + _toolWindow->setWindowFlags(_toolWindow->windowFlags() | Qt::WindowStaysOnTopHint); + _toolWindow->setWindowTitle("Tools"); + // initialization continues in initializeGL when OpenGL context is ready // Tell our voxel edit sender about our known jurisdictions @@ -2911,13 +2916,13 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { // Equivalent to what is happening with _untranslatedViewMatrix and the _viewMatrixTranslation // the viewTransofmr object is updatded with the correct values and saved, // this is what is used for rendering the Entities and avatars - gpu::Transform viewTransform; + Transform viewTransform; viewTransform.setTranslation(whichCamera.getPosition()); viewTransform.setRotation(rotation); viewTransform.postTranslate(eyeOffsetPos); viewTransform.postRotate(eyeOffsetOrient); if (whichCamera.getMode() == CAMERA_MODE_MIRROR) { - viewTransform.setScale(gpu::Transform::Vec3(-1.0f, 1.0f, 1.0f)); + viewTransform.setScale(Transform::Vec3(-1.0f, 1.0f, 1.0f)); } setViewTransform(viewTransform); @@ -3117,8 +3122,8 @@ void Application::updateUntranslatedViewMatrix(const glm::vec3& viewMatrixTransl _viewMatrixTranslation = viewMatrixTranslation; } -void Application::setViewTransform(const gpu::Transform& view) { - (*_viewTransform) = view; +void Application::setViewTransform(const Transform& view) { + _viewTransform = view; } void Application::loadTranslatedViewMatrix(const glm::vec3& translation) { @@ -3901,7 +3906,9 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri connect(scriptEngine, SIGNAL(loadScript(const QString&, bool)), this, SLOT(loadScript(const QString&, bool))); scriptEngine->registerGlobalObject("Overlays", &_overlays); - qScriptRegisterMetaType(scriptEngine, RayToOverlayIntersectionResultToScriptValue, RayToOverlayIntersectionResultFromScriptValue); + qScriptRegisterMetaType(scriptEngine, OverlayPropertyResultToScriptValue, OverlayPropertyResultFromScriptValue); + qScriptRegisterMetaType(scriptEngine, RayToOverlayIntersectionResultToScriptValue, + RayToOverlayIntersectionResultFromScriptValue); QScriptValue windowValue = scriptEngine->registerGlobalObject("Window", WindowScriptingInterface::getInstance()); scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter, @@ -3916,6 +3923,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("AnimationCache", &_animationCache); + scriptEngine->registerGlobalObject("SoundCache", &SoundCache::getInstance()); scriptEngine->registerGlobalObject("AudioReflector", &_audioReflector); scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("Metavoxels", &_metavoxels); diff --git a/interface/src/Application.h b/interface/src/Application.h index 9ce857ee8a..d31833897f 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -82,6 +82,7 @@ #include "ui/overlays/Overlays.h" #include "ui/ApplicationOverlay.h" #include "ui/RunningScriptsWidget.h" +#include "ui/ToolWindow.h" #include "ui/VoxelImportDialog.h" #include "voxels/VoxelFade.h" #include "voxels/VoxelHideShowThread.h" @@ -232,8 +233,8 @@ public: const glm::vec3& getViewMatrixTranslation() const { return _viewMatrixTranslation; } void setViewMatrixTranslation(const glm::vec3& translation) { _viewMatrixTranslation = translation; } - const gpu::TransformPointer& getViewTransform() const { return _viewTransform; } - void setViewTransform(const gpu::Transform& view); + const Transform& getViewTransform() const { return _viewTransform; } + void setViewTransform(const Transform& view); /// if you need to access the application settings, use lockSettings()/unlockSettings() QSettings* lockSettings() { _settingsMutex.lock(); return _settings; } @@ -246,6 +247,8 @@ public: void lockOctreeSceneStats() { _octreeSceneStatsLock.lockForRead(); } void unlockOctreeSceneStats() { _octreeSceneStatsLock.unlock(); } + ToolWindow* getToolWindow() { return _toolWindow ; } + GeometryCache* getGeometryCache() { return &_geometryCache; } AnimationCache* getAnimationCache() { return &_animationCache; } TextureCache* getTextureCache() { return &_textureCache; } @@ -459,6 +462,8 @@ private: MainWindow* _window; GLCanvas* _glWidget; // our GLCanvas has a couple extra features + ToolWindow* _toolWindow; + BandwidthMeter _bandwidthMeter; QThread* _nodeThread; @@ -526,7 +531,7 @@ private: QRect _mirrorViewRect; RearMirrorTools* _rearMirrorTools; - gpu::TransformPointer _viewTransform; + Transform _viewTransform; glm::mat4 _untranslatedViewMatrix; glm::vec3 _viewMatrixTranslation; glm::mat4 _projectionMatrix; diff --git a/interface/src/FileLogger.cpp b/interface/src/FileLogger.cpp index 505cb6a029..4808842036 100644 --- a/interface/src/FileLogger.cpp +++ b/interface/src/FileLogger.cpp @@ -41,7 +41,7 @@ void FileLogger::addMessage(QString message) { QFile file(_fileName); if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) { QTextStream out(&file); - out << message << "\n"; + out << message; } } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index e0feb4e349..f7aabc8f06 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -247,6 +247,12 @@ Menu::Menu() : _chatWindow = new ChatWindow(Application::getInstance()->getWindow()); #endif + addActionToQMenuAndActionHash(toolsMenu, + MenuOption::ToolWindow, + Qt::CTRL | Qt::ALT | Qt::Key_T, + this, + SLOT(toggleToolWindow())); + addActionToQMenuAndActionHash(toolsMenu, MenuOption::Console, Qt::CTRL | Qt::ALT | Qt::Key_J, @@ -1464,6 +1470,11 @@ void Menu::toggleConsole() { _jsConsole->setVisible(!_jsConsole->isVisible()); } +void Menu::toggleToolWindow() { + QMainWindow* toolWindow = Application::getInstance()->getToolWindow(); + toolWindow->setVisible(!toolWindow->isVisible()); +} + void Menu::audioMuteToggled() { QAction *muteAction = _actionHash.value(MenuOption::MuteAudio); if (muteAction) { diff --git a/interface/src/Menu.h b/interface/src/Menu.h index b745246780..8590d8580e 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -224,6 +224,7 @@ private slots: void showScriptEditor(); void showChat(); void toggleConsole(); + void toggleToolWindow(); void toggleChat(); void audioMuteToggled(); void displayNameLocationResponse(const QString& errorString); @@ -490,6 +491,7 @@ namespace MenuOption { const QString StringHair = "String Hair"; const QString SuppressShortTimings = "Suppress Timings Less than 10ms"; const QString TestPing = "Test Ping"; + const QString ToolWindow = "Tool Window"; const QString TransmitterDrive = "Transmitter Drive"; const QString TurnWithHead = "Turn using Head"; const QString UploadAttachment = "Upload Attachment Model"; diff --git a/interface/src/devices/Leapmotion.cpp b/interface/src/devices/Leapmotion.cpp index a3794123ce..0c1433f216 100644 --- a/interface/src/devices/Leapmotion.cpp +++ b/interface/src/devices/Leapmotion.cpp @@ -70,8 +70,8 @@ Leapmotion::Leapmotion() : std::vector< Semantic > rootBones; rootBones.push_back("elbow"); - rootBones.push_back("hand"); rootBones.push_back("wrist"); + rootBones.push_back("hand"); std::vector< Semantic > fingers; fingers.push_back("thumb"); diff --git a/interface/src/gpu/Batch.cpp b/interface/src/gpu/Batch.cpp index 2bf92f43cf..30e591cc60 100644 --- a/interface/src/gpu/Batch.cpp +++ b/interface/src/gpu/Batch.cpp @@ -135,19 +135,19 @@ void Batch::setIndexBuffer(Type type, const BufferPointer& buffer, Offset offset _params.push_back(type); } -void Batch::setModelTransform(const TransformPointer& model) { +void Batch::setModelTransform(const Transform& model) { ADD_COMMAND(setModelTransform); _params.push_back(_transforms.cache(model)); } -void Batch::setViewTransform(const TransformPointer& view) { +void Batch::setViewTransform(const Transform& view) { ADD_COMMAND(setViewTransform); _params.push_back(_transforms.cache(view)); } -void Batch::setProjectionTransform(const TransformPointer& proj) { +void Batch::setProjectionTransform(const Transform& proj) { ADD_COMMAND(setProjectionTransform); _params.push_back(_transforms.cache(proj)); diff --git a/interface/src/gpu/Batch.h b/interface/src/gpu/Batch.h index 3c15fef63b..d006473b50 100644 --- a/interface/src/gpu/Batch.h +++ b/interface/src/gpu/Batch.h @@ -50,10 +50,6 @@ enum Primitive { NUM_PRIMITIVES, }; -typedef ::Transform Transform; -typedef QSharedPointer< ::gpu::Transform > TransformPointer; -typedef std::vector< TransformPointer > Transforms; - class Batch { public: typedef Stream::Slot Slot; @@ -87,9 +83,9 @@ public: // finaly projected into the clip space by the projection transform // WARNING: ViewTransform transform from eye space to world space, its inverse is composed // with the ModelTransformu to create the equivalent of the glModelViewMatrix - void setModelTransform(const TransformPointer& model); - void setViewTransform(const TransformPointer& view); - void setProjectionTransform(const TransformPointer& proj); + void setModelTransform(const Transform& model); + void setViewTransform(const Transform& view); + void setProjectionTransform(const Transform& proj); // TODO: As long as we have gl calls explicitely issued from interface @@ -258,35 +254,35 @@ public: template class Cache { public: - typedef QSharedPointer Pointer; - Pointer _pointer; - Cache(const Pointer& pointer) : _pointer(pointer) {} + typedef T Data; + Data _data; + Cache(const Data& data) : _data(data) {} class Vector { public: - std::vector< Cache > _pointers; + std::vector< Cache > _items; - uint32 cache(const Pointer& pointer) { - uint32 offset = _pointers.size(); - _pointers.push_back(Cache(pointer)); + uint32 cache(const Data& data) { + uint32 offset = _items.size(); + _items.push_back(Cache(data)); return offset; } - Pointer get(uint32 offset) { - if (offset >= _pointers.size()) { - return Pointer(); + Data get(uint32 offset) { + if (offset >= _items.size()) { + return Data(); } - return (_pointers.data() + offset)->_pointer; + return (_items.data() + offset)->_data; } void clear() { - _pointers.clear(); + _items.clear(); } }; }; - - typedef Cache::Vector BufferCaches; - typedef Cache::Vector StreamFormatCaches; + + typedef Cache::Vector BufferCaches; + typedef Cache::Vector StreamFormatCaches; typedef Cache::Vector TransformCaches; typedef unsigned char Byte; diff --git a/interface/src/gpu/GLBackend.cpp b/interface/src/gpu/GLBackend.cpp index 8921dc6d1c..d6e1a011a3 100644 --- a/interface/src/gpu/GLBackend.cpp +++ b/interface/src/gpu/GLBackend.cpp @@ -113,15 +113,7 @@ static const GLenum _elementTypeToGLType[NUM_TYPES]= { GLBackend::GLBackend() : - _needInputFormatUpdate(true), - _inputFormat(0), - _inputBuffersState(0), - _inputBuffers(_inputBuffersState.size(), BufferPointer(0)), - _inputBufferOffsets(_inputBuffersState.size(), 0), - _inputBufferStrides(_inputBuffersState.size(), 0), - _indexBuffer(0), - _indexBufferOffset(0), - _inputAttributeActivation(0), + _input(), _transform() { @@ -203,9 +195,9 @@ void GLBackend::do_drawIndexed(Batch& batch, uint32 paramOffset) { uint32 numIndices = batch._params[paramOffset + 1]._uint; uint32 startIndex = batch._params[paramOffset + 0]._uint; - GLenum glType = _elementTypeToGLType[_indexBufferType]; + GLenum glType = _elementTypeToGLType[_input._indexBufferType]; - glDrawElements(mode, numIndices, glType, reinterpret_cast(startIndex + _indexBufferOffset)); + glDrawElements(mode, numIndices, glType, reinterpret_cast(startIndex + _input._indexBufferOffset)); CHECK_GL_ERROR(); } @@ -220,9 +212,9 @@ void GLBackend::do_drawIndexedInstanced(Batch& batch, uint32 paramOffset) { void GLBackend::do_setInputFormat(Batch& batch, uint32 paramOffset) { Stream::FormatPointer format = batch._streamFormats.get(batch._params[paramOffset]._uint); - if (format != _inputFormat) { - _inputFormat = format; - _needInputFormatUpdate = true; + if (format != _input._format) { + _input._format = format; + _input._invalidFormat = true; } } @@ -233,10 +225,10 @@ void GLBackend::do_setInputBuffer(Batch& batch, uint32 paramOffset) { uint32 channel = batch._params[paramOffset + 3]._uint; if (channel < getNumInputBuffers()) { - _inputBuffers[channel] = buffer; - _inputBufferOffsets[channel] = offset; - _inputBufferStrides[channel] = stride; - _inputBuffersState.set(channel); + _input._buffers[channel] = buffer; + _input._bufferOffsets[channel] = offset; + _input._bufferStrides[channel] = stride; + _input._buffersState.set(channel); } } @@ -252,14 +244,14 @@ static const GLenum attributeSlotToClassicAttribName[NUM_CLASSIC_ATTRIBS] = { #endif void GLBackend::updateInput() { - if (_needInputFormatUpdate || _inputBuffersState.any()) { + if (_input._invalidFormat || _input._buffersState.any()) { - if (_needInputFormatUpdate) { - InputActivationCache newActivation; + if (_input._invalidFormat) { + InputStageState::ActivationCache newActivation; // Check expected activation - if (_inputFormat) { - const Stream::Format::AttributeMap& attributes = _inputFormat->getAttributes(); + if (_input._format) { + const Stream::Format::AttributeMap& attributes = _input._format->getAttributes(); for (Stream::Format::AttributeMap::const_iterator it = attributes.begin(); it != attributes.end(); it++) { const Stream::Attribute& attrib = (*it).second; newActivation.set(attrib._slot); @@ -269,7 +261,7 @@ void GLBackend::updateInput() { // Manage Activation what was and what is expected now for (unsigned int i = 0; i < newActivation.size(); i++) { bool newState = newActivation[i]; - if (newState != _inputAttributeActivation[i]) { + if (newState != _input._attributeActivation[i]) { #if defined(SUPPORT_LEGACY_OPENGL) if (i < NUM_CLASSIC_ATTRIBS) { if (newState) { @@ -290,31 +282,31 @@ void GLBackend::updateInput() { } CHECK_GL_ERROR(); - _inputAttributeActivation.flip(i); + _input._attributeActivation.flip(i); } } } // now we need to bind the buffers and assign the attrib pointers - if (_inputFormat) { - const Buffers& buffers = _inputBuffers; - const Offsets& offsets = _inputBufferOffsets; - const Offsets& strides = _inputBufferStrides; + if (_input._format) { + const Buffers& buffers = _input._buffers; + const Offsets& offsets = _input._bufferOffsets; + const Offsets& strides = _input._bufferStrides; - const Stream::Format::AttributeMap& attributes = _inputFormat->getAttributes(); + const Stream::Format::AttributeMap& attributes = _input._format->getAttributes(); - for (Stream::Format::ChannelMap::const_iterator channelIt = _inputFormat->getChannels().begin(); - channelIt != _inputFormat->getChannels().end(); + for (Stream::Format::ChannelMap::const_iterator channelIt = _input._format->getChannels().begin(); + channelIt != _input._format->getChannels().end(); channelIt++) { const Stream::Format::ChannelMap::value_type::second_type& channel = (*channelIt).second; if ((*channelIt).first < buffers.size()) { int bufferNum = (*channelIt).first; - if (_inputBuffersState.test(bufferNum) || _needInputFormatUpdate) { + if (_input._buffersState.test(bufferNum) || _input._invalidFormat) { GLuint vbo = gpu::GLBackend::getBufferID((*buffers[bufferNum])); glBindBuffer(GL_ARRAY_BUFFER, vbo); CHECK_GL_ERROR(); - _inputBuffersState[bufferNum] = false; + _input._buffersState[bufferNum] = false; for (unsigned int i = 0; i < channel._slots.size(); i++) { const Stream::Attribute& attrib = attributes.at(channel._slots[i]); @@ -354,7 +346,7 @@ void GLBackend::updateInput() { } } // everything format related should be in sync now - _needInputFormatUpdate = false; + _input._invalidFormat = false; } /* TODO: Fancy version GL4.4 @@ -415,10 +407,10 @@ void GLBackend::updateInput() { void GLBackend::do_setIndexBuffer(Batch& batch, uint32 paramOffset) { - _indexBufferType = (Type) batch._params[paramOffset + 2]._uint; + _input._indexBufferType = (Type) batch._params[paramOffset + 2]._uint; BufferPointer indexBuffer = batch._buffers.get(batch._params[paramOffset + 1]._uint); - _indexBufferOffset = batch._params[paramOffset + 0]._uint; - _indexBuffer = indexBuffer; + _input._indexBufferOffset = batch._params[paramOffset + 0]._uint; + _input._indexBuffer = indexBuffer; if (indexBuffer) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, getBufferID(*indexBuffer)); } else { @@ -430,30 +422,18 @@ void GLBackend::do_setIndexBuffer(Batch& batch, uint32 paramOffset) { // Transform Stage void GLBackend::do_setModelTransform(Batch& batch, uint32 paramOffset) { - TransformPointer modelTransform = batch._transforms.get(batch._params[paramOffset]._uint); - - if (_transform._model.isNull() || (modelTransform != _transform._model)) { - _transform._model = modelTransform; - _transform._invalidModel = true; - } + _transform._model = batch._transforms.get(batch._params[paramOffset]._uint); + _transform._invalidModel = true; } void GLBackend::do_setViewTransform(Batch& batch, uint32 paramOffset) { - TransformPointer viewTransform = batch._transforms.get(batch._params[paramOffset]._uint); - - if (_transform._view.isNull() || (viewTransform != _transform._view)) { - _transform._view = viewTransform; - _transform._invalidView = true; - } + _transform._view = batch._transforms.get(batch._params[paramOffset]._uint); + _transform._invalidView = true; } void GLBackend::do_setProjectionTransform(Batch& batch, uint32 paramOffset) { - TransformPointer projectionTransform = batch._transforms.get(batch._params[paramOffset]._uint); - - if (_transform._projection.isNull() || (projectionTransform != _transform._projection)) { - _transform._projection = projectionTransform; - _transform._invalidProj = true; - } + _transform._projection = batch._transforms.get(batch._params[paramOffset]._uint); + _transform._invalidProj = true; } void GLBackend::updateTransform() { @@ -468,28 +448,28 @@ void GLBackend::updateTransform() { } if (_transform._invalidModel || _transform._invalidView) { - if (!_transform._model.isNull()) { + if (!_transform._model.isIdentity()) { if (_transform._lastMode != GL_MODELVIEW) { glMatrixMode(GL_MODELVIEW); _transform._lastMode = GL_MODELVIEW; } Transform::Mat4 modelView; - if (!_transform._view.isNull()) { + if (!_transform._view.isIdentity()) { Transform mvx; - Transform::inverseMult(mvx, (*_transform._view), (*_transform._model)); + Transform::inverseMult(mvx, _transform._view, _transform._model); mvx.getMatrix(modelView); } else { - _transform._model->getMatrix(modelView); + _transform._model.getMatrix(modelView); } glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView)); } else { - if (!_transform._view.isNull()) { + if (!_transform._view.isIdentity()) { if (_transform._lastMode != GL_MODELVIEW) { glMatrixMode(GL_MODELVIEW); _transform._lastMode = GL_MODELVIEW; } Transform::Mat4 modelView; - _transform._view->getInverseMatrix(modelView); + _transform._view.getInverseMatrix(modelView); glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView)); } else { // TODO: eventually do something about the matrix when neither view nor model is specified? diff --git a/interface/src/gpu/GLBackend.h b/interface/src/gpu/GLBackend.h index 0f58ec192d..71869229fd 100644 --- a/interface/src/gpu/GLBackend.h +++ b/interface/src/gpu/GLBackend.h @@ -48,7 +48,7 @@ public: static const int MAX_NUM_ATTRIBUTES = Stream::NUM_INPUT_SLOTS; static const int MAX_NUM_INPUT_BUFFERS = 16; - uint32 getNumInputBuffers() const { return _inputBuffersState.size(); } + uint32 getNumInputBuffers() const { return _input._buffersState.size(); } protected: @@ -62,22 +62,39 @@ protected: void do_setInputFormat(Batch& batch, uint32 paramOffset); void do_setInputBuffer(Batch& batch, uint32 paramOffset); void do_setIndexBuffer(Batch& batch, uint32 paramOffset); + void updateInput(); - bool _needInputFormatUpdate; - Stream::FormatPointer _inputFormat; - typedef std::bitset InputBuffersState; - InputBuffersState _inputBuffersState; + struct InputStageState { + bool _invalidFormat; + Stream::FormatPointer _format; - Buffers _inputBuffers; - Offsets _inputBufferOffsets; - Offsets _inputBufferStrides; + typedef std::bitset BuffersState; + BuffersState _buffersState; - BufferPointer _indexBuffer; - Offset _indexBufferOffset; - Type _indexBufferType; + Buffers _buffers; + Offsets _bufferOffsets; + Offsets _bufferStrides; - typedef std::bitset InputActivationCache; - InputActivationCache _inputAttributeActivation; + BufferPointer _indexBuffer; + Offset _indexBufferOffset; + Type _indexBufferType; + + typedef std::bitset ActivationCache; + ActivationCache _attributeActivation; + + InputStageState() : + _invalidFormat(true), + _format(0), + _buffersState(0), + _buffers(_buffersState.size(), BufferPointer(0)), + _bufferOffsets(_buffersState.size(), 0), + _bufferStrides(_buffersState.size(), 0), + _indexBuffer(0), + _indexBufferOffset(0), + _indexBufferType(UINT32), + _attributeActivation(0) + {} + } _input; // Transform Stage void do_setModelTransform(Batch& batch, uint32 paramOffset); @@ -86,9 +103,9 @@ protected: void updateTransform(); struct TransformStageState { - TransformPointer _model; - TransformPointer _view; - TransformPointer _projection; + Transform _model; + Transform _view; + Transform _projection; bool _invalidModel; bool _invalidView; bool _invalidProj; @@ -96,9 +113,9 @@ protected: GLenum _lastMode; TransformStageState() : - _model(0), - _view(0), - _projection(0), + _model(), + _view(), + _projection(), _invalidModel(true), _invalidView(true), _invalidProj(true), diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 8b3c656ae7..09aaa63929 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -537,7 +537,7 @@ void Model::renderSetup(RenderArgs* args) { } } - if (!_meshGroupsKnown) { + if (!_meshGroupsKnown && isLoadedWithTextures()) { segregateMeshGroups(); } } @@ -567,11 +567,11 @@ bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) { // Capture the view matrix once for the rendering of this model if (_transforms.empty()) { - _transforms.push_back(gpu::TransformPointer(new gpu::Transform())); + _transforms.push_back(Transform()); } - (*_transforms[0]) = gpu::Transform((*Application::getInstance()->getViewTransform())); + _transforms[0] = Application::getInstance()->getViewTransform(); // apply entity translation offset to the viewTransform in one go (it's a preTranslate because viewTransform goes from world to eye space) - _transforms[0]->preTranslate(-_translation); + _transforms[0].preTranslate(-_translation); batch.setViewTransform(_transforms[0]); @@ -628,7 +628,7 @@ bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) { opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, false, args); opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, args); opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, false, args); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, args); + opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, true, args); // render translucent meshes afterwards //Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(false, true, true); @@ -649,7 +649,7 @@ bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) { translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, false, args); translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, true, args); translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, true, false, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, true, args); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, true, true, args); GLBATCH(glDisable)(GL_ALPHA_TEST); GLBATCH(glEnable)(GL_BLEND); @@ -673,7 +673,7 @@ bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) { translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, false, args); translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, true, args); translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, true, false, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, true, args); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, true, true, args); } GLBATCH(glDepthMask)(true); @@ -1493,10 +1493,10 @@ void Model::setupBatchTransform(gpu::Batch& batch) { // Capture the view matrix once for the rendering of this model if (_transforms.empty()) { - _transforms.push_back(gpu::TransformPointer(new gpu::Transform())); + _transforms.push_back(Transform()); } - (*_transforms[0]) = gpu::Transform((*Application::getInstance()->getViewTransform())); - _transforms[0]->preTranslate(-_translation); + _transforms[0] = Application::getInstance()->getViewTransform(); + _transforms[0].preTranslate(-_translation); batch.setViewTransform(_transforms[0]); } @@ -1553,46 +1553,14 @@ void Model::endScene(RenderMode mode, RenderArgs* args) { int opaqueMeshPartsRendered = 0; // now, for each model in the scene, render the mesh portions - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, args); - GLBATCH(glPopMatrix)(); - } - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, args); - GLBATCH(glPopMatrix)(); - } - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, args); - GLBATCH(glPopMatrix)(); - } - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, args); - GLBATCH(glPopMatrix)(); - } - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, false, args); - GLBATCH(glPopMatrix)(); - } - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, args); - GLBATCH(glPopMatrix)(); - } - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, false, args); - GLBATCH(glPopMatrix)(); - } - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - opaqueMeshPartsRendered += model->renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, args); - GLBATCH(glPopMatrix)(); - } + opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, args); + opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, args); + opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, args); + opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, args); + opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, false, args); + opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, args); + opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, false, args); + opaqueMeshPartsRendered += renderMeshesForModelsInScene(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, true, args); // render translucent meshes afterwards //Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(false, true, true); @@ -1606,46 +1574,14 @@ void Model::endScene(RenderMode mode, RenderArgs* args) { int translucentParts = 0; const float MOSTLY_OPAQUE_THRESHOLD = 0.75f; - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, args); - GLBATCH(glPopMatrix)(); - } - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, args); - GLBATCH(glPopMatrix)(); - } - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, args); - GLBATCH(glPopMatrix)(); - } - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, args); - GLBATCH(glPopMatrix)(); - } - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, false, args); - GLBATCH(glPopMatrix)(); - } - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, true, args); - GLBATCH(glPopMatrix)(); - } - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, true, false, args); - GLBATCH(glPopMatrix)(); - } - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, true, args); - GLBATCH(glPopMatrix)(); - } + translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, args); + translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, args); + translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, args); + translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, args); + translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, false, args); + translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, false, true, args); + translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, true, false, args); + translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, true, true, true, args); GLBATCH(glDisable)(GL_ALPHA_TEST); GLBATCH(glEnable)(GL_BLEND); @@ -1662,46 +1598,14 @@ void Model::endScene(RenderMode mode, RenderArgs* args) { if (mode == DEFAULT_RENDER_MODE || mode == DIFFUSE_RENDER_MODE) { const float MOSTLY_TRANSPARENT_THRESHOLD = 0.0f; - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, args); - GLBATCH(glPopMatrix)(); - } - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, args); - GLBATCH(glPopMatrix)(); - } - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, args); - GLBATCH(glPopMatrix)(); - } - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, args); - GLBATCH(glPopMatrix)(); - } - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, false, args); - GLBATCH(glPopMatrix)(); - } - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, true, args); - GLBATCH(glPopMatrix)(); - } - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, true, false, args); - GLBATCH(glPopMatrix)(); - } - foreach(Model* model, _modelsInScene) { - model->setupBatchTransform(batch); - translucentParts += model->renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, true, args); - GLBATCH(glPopMatrix)(); - } + translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, args); + translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, args); + translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, args); + translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, args); + translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, false, args); + translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, false, true, args); + translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, true, false, args); + translucentParts += renderMeshesForModelsInScene(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, true, true, true, args); } GLBATCH(glDepthMask)(true); @@ -1981,19 +1885,8 @@ void Model::segregateMeshGroups() { _meshGroupsKnown = true; } -int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, - bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args) { - +QVector* Model::pickMeshList(bool translucent, float alphaThreshold, bool hasTangents, bool hasSpecular, bool isSkinned) { PROFILE_RANGE(__FUNCTION__); - bool dontCullOutOfViewMeshParts = Menu::getInstance()->isOptionChecked(MenuOption::DontCullOutOfViewMeshParts); - bool cullTooSmallMeshParts = !Menu::getInstance()->isOptionChecked(MenuOption::DontCullTooSmallMeshParts); - bool dontReduceMaterialSwitches = Menu::getInstance()->isOptionChecked(MenuOption::DontReduceMaterialSwitches); - - QString lastMaterialID; - int meshPartsRendered = 0; - updateVisibleJointStates(); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - const QVector& networkMeshes = _geometry->getMeshes(); // depending on which parameters we were called with, pick the correct mesh group to render QVector* whichList = NULL; @@ -2032,23 +1925,18 @@ int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, fl } else { qDebug() << "unexpected!!! this mesh didn't fall into any or our groups???"; } - - if (!whichList) { - qDebug() << "unexpected!!! we don't know which list of meshes to render..."; - return 0; - } - QVector& list = *whichList; - - // If this list has nothing to render, then don't bother proceeding. This saves us on binding to programs - if (list.size() == 0) { - return 0; - } + return whichList; +} +void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, + bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args, + SkinLocations*& skinLocations, GLenum& specularTextureUnit) { + ProgramObject* program = &_program; Locations* locations = &_locations; ProgramObject* skinProgram = &_skinProgram; - SkinLocations* skinLocations = &_skinLocations; - GLenum specularTextureUnit = 0; + skinLocations = &_skinLocations; + specularTextureUnit = 0; if (mode == SHADOW_RENDER_MODE) { program = &_shadowProgram; skinProgram = &_skinShadowProgram; @@ -2091,8 +1979,84 @@ int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, fl if (!activeProgram->isLinked()) { activeProgram->link(); } + GLBATCH(glUseProgram)(activeProgram->programId()); GLBATCH(glUniform1f)(activeLocations->alphaThreshold, alphaThreshold); +} + +int Model::renderMeshesForModelsInScene(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, + bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args) { + + PROFILE_RANGE(__FUNCTION__); + int meshPartsRendered = 0; + + bool pickProgramsNeeded = true; + SkinLocations* skinLocations; + GLenum specularTextureUnit; + + foreach(Model* model, _modelsInScene) { + QVector* whichList = model->pickMeshList(translucent, alphaThreshold, hasTangents, hasSpecular, isSkinned); + if (whichList) { + QVector& list = *whichList; + if (list.size() > 0) { + if (pickProgramsNeeded) { + pickPrograms(batch, mode, translucent, alphaThreshold, hasTangents, hasSpecular, isSkinned, args, skinLocations, specularTextureUnit); + pickProgramsNeeded = false; + } + model->setupBatchTransform(batch); + meshPartsRendered += model->renderMeshesFromList(list, batch, mode, translucent, alphaThreshold, args, skinLocations, specularTextureUnit); + GLBATCH(glPopMatrix)(); + } + } + } + // if we selected a program, then unselect it + if (!pickProgramsNeeded) { + GLBATCH(glUseProgram)(0); + } + return meshPartsRendered; +} + +int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, + bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args) { + + PROFILE_RANGE(__FUNCTION__); + int meshPartsRendered = 0; + + QVector* whichList = pickMeshList(translucent, alphaThreshold, hasTangents, hasSpecular, isSkinned); + + if (!whichList) { + qDebug() << "unexpected!!! we don't know which list of meshes to render..."; + return 0; + } + QVector& list = *whichList; + + // If this list has nothing to render, then don't bother proceeding. This saves us on binding to programs + if (list.size() == 0) { + return 0; + } + + SkinLocations* skinLocations; + GLenum specularTextureUnit; + pickPrograms(batch, mode, translucent, alphaThreshold, hasTangents, hasSpecular, isSkinned, args, skinLocations, specularTextureUnit); + meshPartsRendered = renderMeshesFromList(list, batch, mode, translucent, alphaThreshold, args, skinLocations, specularTextureUnit); + GLBATCH(glUseProgram)(0); + + return meshPartsRendered; +} + + +int Model::renderMeshesFromList(QVector& list, gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, RenderArgs* args, + SkinLocations* skinLocations, GLenum specularTextureUnit) { + PROFILE_RANGE(__FUNCTION__); + bool dontCullOutOfViewMeshParts = Menu::getInstance()->isOptionChecked(MenuOption::DontCullOutOfViewMeshParts); + bool cullTooSmallMeshParts = !Menu::getInstance()->isOptionChecked(MenuOption::DontCullTooSmallMeshParts); + bool dontReduceMaterialSwitches = Menu::getInstance()->isOptionChecked(MenuOption::DontReduceMaterialSwitches); + + QString lastMaterialID; + int meshPartsRendered = 0; + updateVisibleJointStates(); + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const QVector& networkMeshes = _geometry->getMeshes(); // i is the "index" from the original networkMeshes QVector... foreach (int i, list) { @@ -2149,10 +2113,9 @@ int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, fl if (state.clusterMatrices.size() > 1) { GLBATCH(glUniformMatrix4fv)(skinLocations->clusterMatrices, state.clusterMatrices.size(), false, (const float*)state.clusterMatrices.constData()); - batch.setModelTransform(gpu::TransformPointer()); + batch.setModelTransform(Transform()); } else { - gpu::TransformPointer modelTransform(new gpu::Transform(state.clusterMatrices[0])); - batch.setModelTransform(modelTransform); + batch.setModelTransform(Transform(state.clusterMatrices[0])); } if (mesh.blendshapes.isEmpty()) { @@ -2268,7 +2231,5 @@ int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, fl } - GLBATCH(glUseProgram)(0); - return meshPartsRendered; } diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 8c74b1a222..32c072dc99 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -283,7 +283,7 @@ private: QUrl _url; gpu::Buffers _blendedVertexBuffers; - gpu::Transforms _transforms; + std::vector _transforms; gpu::Batch _renderBatch; QVector > > _dilatedTextures; @@ -409,6 +409,18 @@ private: int renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args = NULL); void setupBatchTransform(gpu::Batch& batch); + QVector* pickMeshList(bool translucent, float alphaThreshold, bool hasTangents, bool hasSpecular, bool isSkinned); + + int renderMeshesFromList(QVector& list, gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, + RenderArgs* args, SkinLocations* skinLocations, GLenum specularTextureUnit); + + static void pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, + bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args, + SkinLocations*& skinLocations, GLenum& specularTextureUnit); + + static int renderMeshesForModelsInScene(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, + bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args); + }; diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp index d280d8eecf..cc6f4fbfff 100644 --- a/interface/src/scripting/WebWindowClass.cpp +++ b/interface/src/scripting/WebWindowClass.cpp @@ -11,9 +11,13 @@ #include #include +#include +#include #include #include +#include +#include "Application.h" #include "WindowScriptingInterface.h" #include "WebWindowClass.h" @@ -28,29 +32,34 @@ void ScriptEventBridge::emitScriptEvent(const QString& data) { emit scriptEventReceived(data); } -WebWindowClass::WebWindowClass(const QString& url, int width, int height) + +WebWindowClass::WebWindowClass(const QString& title, const QString& url, int width, int height) : QObject(NULL), - _window(new QWidget(NULL, Qt::Tool)), _eventBridge(new ScriptEventBridge(this)) { - QWebView* webView = new QWebView(_window); + ToolWindow* toolWindow = Application::getInstance()->getToolWindow(); + + _dockWidget = new QDockWidget(title, toolWindow); + _dockWidget->setFeatures(QDockWidget::DockWidgetMovable); + QWebView* webView = new QWebView(_dockWidget); webView->page()->mainFrame()->addToJavaScriptWindowObject("EventBridge", _eventBridge); webView->setUrl(url); - QVBoxLayout* layout = new QVBoxLayout(_window); - _window->setLayout(layout); - layout->addWidget(webView); - layout->setSpacing(0); - layout->setContentsMargins(0, 0, 0, 0); - _window->setGeometry(0, 0, width, height); + _dockWidget->setWidget(webView); - connect(this, &WebWindowClass::destroyed, _window, &QWidget::deleteLater); + toolWindow->addDockWidget(Qt::RightDockWidgetArea, _dockWidget); + + connect(this, &WebWindowClass::destroyed, _dockWidget, &QWidget::deleteLater); } WebWindowClass::~WebWindowClass() { } void WebWindowClass::setVisible(bool visible) { - QMetaObject::invokeMethod(_window, "setVisible", Qt::BlockingQueuedConnection, Q_ARG(bool, visible)); + if (visible) { + QMetaObject::invokeMethod( + Application::getInstance()->getToolWindow(), "setVisible", Qt::BlockingQueuedConnection, Q_ARG(bool, visible)); + } + QMetaObject::invokeMethod(_dockWidget, "setVisible", Qt::BlockingQueuedConnection, Q_ARG(bool, visible)); } QScriptValue WebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) { @@ -59,8 +68,9 @@ QScriptValue WebWindowClass::constructor(QScriptContext* context, QScriptEngine* QMetaObject::invokeMethod(WindowScriptingInterface::getInstance(), "doCreateWebWindow", Qt::BlockingQueuedConnection, Q_RETURN_ARG(WebWindowClass*, retVal), Q_ARG(const QString&, file), - Q_ARG(int, context->argument(1).toInteger()), - Q_ARG(int, context->argument(2).toInteger())); + Q_ARG(QString, context->argument(1).toString()), + Q_ARG(int, context->argument(2).toInteger()), + Q_ARG(int, context->argument(3).toInteger())); connect(engine, &QScriptEngine::destroyed, retVal, &WebWindowClass::deleteLater); diff --git a/interface/src/scripting/WebWindowClass.h b/interface/src/scripting/WebWindowClass.h index 7b77299774..ae0d14ae06 100644 --- a/interface/src/scripting/WebWindowClass.h +++ b/interface/src/scripting/WebWindowClass.h @@ -34,7 +34,7 @@ class WebWindowClass : public QObject { Q_OBJECT Q_PROPERTY(QObject* eventBridge READ getEventBridge) public: - WebWindowClass(const QString& url, int width, int height); + WebWindowClass(const QString& title, const QString& url, int width, int height); ~WebWindowClass(); static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); @@ -44,7 +44,7 @@ public slots: ScriptEventBridge* getEventBridge() const { return _eventBridge; } private: - QWidget* _window; + QDockWidget* _dockWidget; ScriptEventBridge* _eventBridge; }; diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 8c2066f253..8a79cad6e1 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -34,8 +34,8 @@ WindowScriptingInterface::WindowScriptingInterface() : { } -WebWindowClass* WindowScriptingInterface::doCreateWebWindow(const QString& url, int width, int height) { - return new WebWindowClass(url, width, height); +WebWindowClass* WindowScriptingInterface::doCreateWebWindow(const QString& title, const QString& url, int width, int height) { + return new WebWindowClass(title, url, width, height); } QScriptValue WindowScriptingInterface::hasFocus() { diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 5529d31efd..0b320f23a1 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -78,7 +78,7 @@ private slots: void nonBlockingFormAccepted() { _nonBlockingFormActive = false; _formResult = QDialog::Accepted; emit nonBlockingFormClosed(); } void nonBlockingFormRejected() { _nonBlockingFormActive = false; _formResult = QDialog::Rejected; emit nonBlockingFormClosed(); } - WebWindowClass* doCreateWebWindow(const QString& url, int width, int height); + WebWindowClass* doCreateWebWindow(const QString& title, const QString& url, int width, int height); private: WindowScriptingInterface(); diff --git a/interface/src/ui/ToolWindow.cpp b/interface/src/ui/ToolWindow.cpp new file mode 100644 index 0000000000..1375ff1ea5 --- /dev/null +++ b/interface/src/ui/ToolWindow.cpp @@ -0,0 +1,82 @@ +// +// ToolWindow.cpp +// interface/src/ui +// +// Created by Ryan Huffman on 11/13/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Application.h" +#include "ToolWindow.h" +#include "UIUtil.h" + +const int DEFAULT_WIDTH = 300; + +ToolWindow::ToolWindow(QWidget* parent) : + QMainWindow(parent), + _hasShown(false), + _lastGeometry() { +} + +bool ToolWindow::event(QEvent* event) { + QEvent::Type type = event->type(); + if (type == QEvent::Show) { + if (!_hasShown) { + _hasShown = true; + + QMainWindow* mainWindow = Application::getInstance()->getWindow(); + QRect mainGeometry = mainWindow->geometry(); + + int titleBarHeight = UIUtil::getWindowTitleBarHeight(this); + int menuBarHeight = Menu::getInstance()->geometry().height(); + int topMargin = titleBarHeight + menuBarHeight; + + _lastGeometry = QRect(mainGeometry.topLeft().x(), mainGeometry.topLeft().y() + topMargin, + DEFAULT_WIDTH, mainGeometry.height() - topMargin); + } + setGeometry(_lastGeometry); + return true; + } else if (type == QEvent::Hide) { + _lastGeometry = geometry(); + return true; + } + + return QMainWindow::event(event); +} + +void ToolWindow::onChildVisibilityUpdated(bool visible) { + if (visible) { + setVisible(true); + } else { + bool hasVisible = false; + QList dockWidgets = findChildren(); + for (int i = 0; i < dockWidgets.count(); i++) { + if (dockWidgets[i]->isVisible()) { + hasVisible = true; + break; + } + } + setVisible(hasVisible); + } +} + +void ToolWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget) { + QMainWindow::addDockWidget(area, dockWidget); + + connect(dockWidget, &QDockWidget::visibilityChanged, this, &ToolWindow::onChildVisibilityUpdated); +} + +void ToolWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget, Qt::Orientation orientation) { + QMainWindow::addDockWidget(area, dockWidget, orientation); + + connect(dockWidget, &QDockWidget::visibilityChanged, this, &ToolWindow::onChildVisibilityUpdated); +} + +void ToolWindow::removeDockWidget(QDockWidget* dockWidget) { + QMainWindow::removeDockWidget(dockWidget); + + disconnect(dockWidget, &QDockWidget::visibilityChanged, this, &ToolWindow::onChildVisibilityUpdated); +} diff --git a/interface/src/ui/ToolWindow.h b/interface/src/ui/ToolWindow.h new file mode 100644 index 0000000000..87b94d46df --- /dev/null +++ b/interface/src/ui/ToolWindow.h @@ -0,0 +1,40 @@ +// +// ToolWindow.h +// interface/src/ui +// +// Created by Ryan Huffman on 11/13/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ToolWindow_h +#define hifi_ToolWindow_h + +#include +#include +#include +#include +#include + +class ToolWindow : public QMainWindow { + Q_OBJECT +public: + ToolWindow(QWidget* parent = NULL); + + virtual bool event(QEvent* event); + virtual void addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget); + virtual void addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget, Qt::Orientation orientation); + virtual void removeDockWidget(QDockWidget* dockWidget); + +public slots: + void onChildVisibilityUpdated(bool visible); + + +private: + bool _hasShown; + QRect _lastGeometry; +}; + +#endif // hifi_ToolWindow_h diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index 89dd4d4b01..a83772d01b 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -118,6 +118,32 @@ void Base3DOverlay::setProperties(const QScriptValue& properties) { } } +QScriptValue Base3DOverlay::getProperty(const QString& property) { + if (property == "position" || property == "start" || property == "p1" || property == "point") { + return vec3toScriptValue(_scriptEngine, _position); + } + if (property == "lineWidth") { + return _lineWidth; + } + if (property == "rotation") { + return quatToScriptValue(_scriptEngine, _rotation); + } + if (property == "isSolid" || property == "isFilled" || property == "solid" || property == "filed") { + return _isSolid; + } + if (property == "isWire" || property == "wire") { + return !_isSolid; + } + if (property == "isDashedLine" || property == "dashed") { + return _isDashedLine; + } + if (property == "ignoreRayIntersection") { + return _ignoreRayIntersection; + } + + return Overlay::getProperty(property); +} + bool Base3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const { return false; diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index 75ce6b303d..7b7fa1a8f4 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -45,6 +45,7 @@ public: void setIgnoreRayIntersection(bool value) { _ignoreRayIntersection = value; } virtual void setProperties(const QScriptValue& properties); + virtual QScriptValue getProperty(const QString& property); virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const; diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index c8d4877bb5..b36202cb04 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -157,6 +157,23 @@ void BillboardOverlay::setProperties(const QScriptValue &properties) { } } +QScriptValue BillboardOverlay::getProperty(const QString& property) { + if (property == "url") { + return _url; + } + if (property == "subImage") { + return qRectToScriptValue(_scriptEngine, _fromImage); + } + if (property == "scale") { + return _scale; + } + if (property == "isFacingAvatar") { + return _isFacingAvatar; + } + + return Base3DOverlay::getProperty(property); +} + void BillboardOverlay::setURL(const QString& url) { setBillboardURL(url); } diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h index 018ca5f5cf..3a22a247f0 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -32,6 +32,7 @@ public: virtual void setProperties(const QScriptValue& properties); void setClipFromSource(const QRect& bounds) { _fromImage = bounds; } + virtual QScriptValue getProperty(const QString& property); virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const; diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 144119c450..986088cebb 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -299,6 +299,45 @@ void Circle3DOverlay::setProperties(const QScriptValue &properties) { } } +QScriptValue Circle3DOverlay::getProperty(const QString& property) { + if (property == "startAt") { + return _startAt; + } + if (property == "endAt") { + return _endAt; + } + if (property == "outerRadius") { + return _outerRadius; + } + if (property == "innerRadius") { + return _innerRadius; + } + if (property == "hasTickMarks") { + return _hasTickMarks; + } + if (property == "majorTickMarksAngle") { + return _majorTickMarksAngle; + } + if (property == "minorTickMarksAngle") { + return _minorTickMarksAngle; + } + if (property == "majorTickMarksLength") { + return _majorTickMarksLength; + } + if (property == "minorTickMarksLength") { + return _minorTickMarksLength; + } + if (property == "majorTickMarksColor") { + return xColorToScriptValue(_scriptEngine, _majorTickMarksColor); + } + if (property == "minorTickMarksColor") { + return xColorToScriptValue(_scriptEngine, _minorTickMarksColor); + } + + return Planar3DOverlay::getProperty(property); +} + + bool Circle3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const { diff --git a/interface/src/ui/overlays/Circle3DOverlay.h b/interface/src/ui/overlays/Circle3DOverlay.h index 191a0d3100..816ed280f9 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.h +++ b/interface/src/ui/overlays/Circle3DOverlay.h @@ -21,6 +21,7 @@ public: ~Circle3DOverlay(); virtual void render(RenderArgs* args); virtual void setProperties(const QScriptValue& properties); + virtual QScriptValue getProperty(const QString& property); float getStartAt() const { return _startAt; } float getEndAt() const { return _endAt; } diff --git a/interface/src/ui/overlays/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp index c628199fe3..dee5d5d60a 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.cpp +++ b/interface/src/ui/overlays/Grid3DOverlay.cpp @@ -116,3 +116,14 @@ void Grid3DOverlay::setProperties(const QScriptValue& properties) { _majorGridEvery = properties.property("majorGridEvery").toVariant().toInt(); } } + +QScriptValue Grid3DOverlay::getProperty(const QString& property) { + if (property == "minorGridWidth") { + return _minorGridWidth; + } + if (property == "majorGridEvery") { + return _majorGridEvery; + } + + return Base3DOverlay::getProperty(property); +} diff --git a/interface/src/ui/overlays/Grid3DOverlay.h b/interface/src/ui/overlays/Grid3DOverlay.h index b1675f15d7..3e6a235d54 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.h +++ b/interface/src/ui/overlays/Grid3DOverlay.h @@ -33,6 +33,7 @@ public: virtual void render(RenderArgs* args); virtual void setProperties(const QScriptValue& properties); + virtual QScriptValue getProperty(const QString& property); private: float _minorGridWidth; diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index 3b9d95af2e..615872e6ef 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -151,4 +151,13 @@ void ImageOverlay::setProperties(const QScriptValue& properties) { } } +QScriptValue ImageOverlay::getProperty(const QString& property) { + if (property == "subImage") { + return qRectToScriptValue(_scriptEngine, _fromImage); + } + if (property == "imageURL") { + return _imageURL.toString(); + } + return Overlay2D::getProperty(property); +} diff --git a/interface/src/ui/overlays/ImageOverlay.h b/interface/src/ui/overlays/ImageOverlay.h index ef1ead8c02..bf4f2860ad 100644 --- a/interface/src/ui/overlays/ImageOverlay.h +++ b/interface/src/ui/overlays/ImageOverlay.h @@ -44,6 +44,7 @@ public: void setClipFromSource(const QRect& bounds) { _fromImage = bounds; _wantClipFromImage = true; } void setImageURL(const QUrl& url); virtual void setProperties(const QScriptValue& properties); + virtual QScriptValue getProperty(const QString& property); private slots: void replyFinished(); // we actually want to hide this... diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index 18671ea074..896ebd1e68 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -79,3 +79,11 @@ void Line3DOverlay::setProperties(const QScriptValue& properties) { } } } + +QScriptValue Line3DOverlay::getProperty(const QString& property) { + if (property == "end" || property == "endPoint" || property == "p2") { + return vec3toScriptValue(_scriptEngine, _end); + } + + return Base3DOverlay::getProperty(property); +} diff --git a/interface/src/ui/overlays/Line3DOverlay.h b/interface/src/ui/overlays/Line3DOverlay.h index f9c4e0d6d6..0148648c35 100644 --- a/interface/src/ui/overlays/Line3DOverlay.h +++ b/interface/src/ui/overlays/Line3DOverlay.h @@ -28,6 +28,7 @@ public: void setEnd(const glm::vec3& end) { _end = end; } virtual void setProperties(const QScriptValue& properties); + virtual QScriptValue getProperty(const QString& property); protected: glm::vec3 _end; diff --git a/interface/src/ui/overlays/LocalVoxelsOverlay.cpp b/interface/src/ui/overlays/LocalVoxelsOverlay.cpp index dcfc79f3b4..3ca79d548c 100644 --- a/interface/src/ui/overlays/LocalVoxelsOverlay.cpp +++ b/interface/src/ui/overlays/LocalVoxelsOverlay.cpp @@ -103,3 +103,14 @@ void LocalVoxelsOverlay::setProperties(const QScriptValue &properties) { } } +QScriptValue LocalVoxelsOverlay::getProperty(const QString& property) { + if (property == "scale") { + return vec3toScriptValue(_scriptEngine, getDimensions()); + } + if (property == "name") { + return _treeName; + } + + return Volume3DOverlay::getProperty(property); +} + diff --git a/interface/src/ui/overlays/LocalVoxelsOverlay.h b/interface/src/ui/overlays/LocalVoxelsOverlay.h index 46a88407af..25ad4738b9 100644 --- a/interface/src/ui/overlays/LocalVoxelsOverlay.h +++ b/interface/src/ui/overlays/LocalVoxelsOverlay.h @@ -38,7 +38,8 @@ public: virtual void render(RenderArgs* args); virtual void setProperties(const QScriptValue& properties); - + virtual QScriptValue getProperty(const QString& property); + private: static QMap _voxelSystemMap; // treeName/voxelSystem diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 150d40f15a..a2abfe77b1 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -14,6 +14,7 @@ ModelOverlay::ModelOverlay() : _model(), + _modelTextures(QVariantMap()), _scale(1.0f), _updateModel(false) { @@ -114,6 +115,8 @@ void ModelOverlay::setProperties(const QScriptValue &properties) { QMetaObject::invokeMethod(&_model, "setTextureWithNameToURL", Qt::AutoConnection, Q_ARG(const QString&, key), Q_ARG(const QUrl&, newTextureURL)); + + _modelTextures[key] = newTextureURL; // Keep local track of textures for getProperty() } } @@ -122,6 +125,34 @@ void ModelOverlay::setProperties(const QScriptValue &properties) { } } +QScriptValue ModelOverlay::getProperty(const QString& property) { + if (property == "url") { + return _url.toString(); + } + if (property == "scale") { + return _scale; + } + if (property == "rotation") { + return quatToScriptValue(_scriptEngine, _rotation); + } + if (property == "dimensions") { + return vec3toScriptValue(_scriptEngine, _model.getScaleToFitDimensions()); + } + if (property == "textures") { + if (_modelTextures.size() > 0) { + QScriptValue textures = _scriptEngine->newObject(); + foreach(const QString& key, _modelTextures.keys()) { + textures.setProperty(key, _modelTextures[key].toString()); + } + return textures; + } else { + return QScriptValue(); + } + } + + return Base3DOverlay::getProperty(property); +} + bool ModelOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const { diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index 26471a79e1..c9f6799e8c 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -24,6 +24,7 @@ public: virtual void update(float deltatime); virtual void render(RenderArgs* args); virtual void setProperties(const QScriptValue& properties); + virtual QScriptValue getProperty(const QString& property); virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const; virtual bool findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, QString& extraInfo) const; @@ -31,6 +32,7 @@ public: private: Model _model; + QVariantMap _modelTextures; QUrl _url; glm::quat _rotation; diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 215119374e..a7c38946b7 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -39,8 +39,9 @@ Overlay::Overlay() : { } -void Overlay::init(QGLWidget* parent) { +void Overlay::init(QGLWidget* parent, QScriptEngine* scriptEngine) { _parent = parent; + _scriptEngine = scriptEngine; } @@ -104,6 +105,44 @@ void Overlay::setProperties(const QScriptValue& properties) { } } +QScriptValue Overlay::getProperty(const QString& property) { + if (property == "color") { + return xColorToScriptValue(_scriptEngine, _color); + } + if (property == "alpha") { + return _alpha; + } + if (property == "glowLevel") { + return _glowLevel; + } + if (property == "pulseMax") { + return _pulseMax; + } + if (property == "pulseMin") { + return _pulseMin; + } + if (property == "pulsePeriod") { + return _pulsePeriod; + } + if (property == "glowLevelPulse") { + return _glowLevelPulse; + } + if (property == "alphaPulse") { + return _alphaPulse; + } + if (property == "colorPulse") { + return _colorPulse; + } + if (property == "visible") { + return _visible; + } + if (property == "anchor") { + return _anchor == MY_AVATAR ? "MyAvatar" : ""; + } + + return QScriptValue(); +} + xColor Overlay::getColor() { if (_colorPulse == 0.0f) { return _color; diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 81ddaf1a91..192bb5fd40 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -19,6 +19,7 @@ #include #include +#include #include // for xColor #include @@ -36,7 +37,7 @@ public: Overlay(); ~Overlay(); - void init(QGLWidget* parent); + void init(QGLWidget* parent, QScriptEngine* scriptEngine); virtual void update(float deltatime) {} virtual void render(RenderArgs* args) = 0; @@ -77,6 +78,7 @@ public: void setAlphaPulse(float value) { _alphaPulse = value; } virtual void setProperties(const QScriptValue& properties); + virtual QScriptValue getProperty(const QString& property); protected: float updatePulse(); @@ -100,6 +102,8 @@ protected: xColor _color; bool _visible; // should the overlay be drawn at all Anchor _anchor; + + QScriptEngine* _scriptEngine; }; diff --git a/interface/src/ui/overlays/Overlay2D.cpp b/interface/src/ui/overlays/Overlay2D.cpp index 0bdb8790cc..b7c0a3a3e4 100644 --- a/interface/src/ui/overlays/Overlay2D.cpp +++ b/interface/src/ui/overlays/Overlay2D.cpp @@ -64,3 +64,23 @@ void Overlay2D::setProperties(const QScriptValue& properties) { //qDebug() << "set bounds to " << getBounds(); } } + +QScriptValue Overlay2D::getProperty(const QString& property) { + if (property == "bounds") { + return qRectToScriptValue(_scriptEngine, _bounds); + } + if (property == "x") { + return _bounds.x(); + } + if (property == "y") { + return _bounds.y(); + } + if (property == "width") { + return _bounds.width(); + } + if (property == "height") { + return _bounds.height(); + } + + return Overlay::getProperty(property); +} diff --git a/interface/src/ui/overlays/Overlay2D.h b/interface/src/ui/overlays/Overlay2D.h index 283e7b7b23..d0d75c9397 100644 --- a/interface/src/ui/overlays/Overlay2D.h +++ b/interface/src/ui/overlays/Overlay2D.h @@ -47,6 +47,7 @@ public: void setBounds(const QRect& bounds) { _bounds = bounds; } virtual void setProperties(const QScriptValue& properties); + virtual QScriptValue getProperty(const QString& property); protected: QRect _bounds; // where on the screen to draw diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 0192f9c216..7bdddd6b42 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -9,8 +9,10 @@ // #include +#include #include #include +#include #include "BillboardOverlay.h" #include "Circle3DOverlay.h" @@ -55,6 +57,7 @@ Overlays::~Overlays() { void Overlays::init(QGLWidget* parent) { _parent = parent; + _scriptEngine = new QScriptEngine(); } void Overlays::update(float deltatime) { @@ -179,7 +182,7 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope } unsigned int Overlays::addOverlay(Overlay* overlay) { - overlay->init(_parent); + overlay->init(_parent, _scriptEngine); QWriteLocker lock(&_lock); unsigned int thisID = _nextOverlayID; @@ -241,6 +244,51 @@ unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) { return 0; // not found } +OverlayPropertyResult Overlays::getProperty(unsigned int id, const QString& property) { + OverlayPropertyResult result; + Overlay* thisOverlay = NULL; + QReadLocker lock(&_lock); + if (_overlays2D.contains(id)) { + thisOverlay = _overlays2D[id]; + } else if (_overlays3D.contains(id)) { + thisOverlay = _overlays3D[id]; + } + if (thisOverlay) { + result.value = thisOverlay->getProperty(property); + } + return result; +} + +OverlayPropertyResult::OverlayPropertyResult() : + value(QScriptValue()) +{ +} + +QScriptValue OverlayPropertyResultToScriptValue(QScriptEngine* engine, const OverlayPropertyResult& result) +{ + if (!result.value.isValid()) { + return QScriptValue::UndefinedValue; + } + + QScriptValue object = engine->newObject(); + if (result.value.isObject()) { + QScriptValueIterator it(result.value); + while (it.hasNext()) { + it.next(); + object.setProperty(it.name(), QScriptValue(it.value().toString())); + } + + } else { + object = result.value; + } + return object; +} + +void OverlayPropertyResultFromScriptValue(const QScriptValue& value, OverlayPropertyResult& result) +{ + result.value = value; +} + RayToOverlayIntersectionResult Overlays::findRayIntersection(const PickRay& ray) { float bestDistance = std::numeric_limits::max(); RayToOverlayIntersectionResult result; @@ -360,3 +408,19 @@ bool Overlays::isLoaded(unsigned int id) { return overlay->isLoaded(); } +float Overlays::textWidth(unsigned int id, const QString& text) const { + Overlay* thisOverlay = _overlays2D[id]; + if (thisOverlay) { + if (typeid(*thisOverlay) == typeid(TextOverlay)) { + return static_cast(thisOverlay)->textWidth(text); + } + } else { + thisOverlay = _overlays3D[id]; + if (thisOverlay) { + if (typeid(*thisOverlay) == typeid(Text3DOverlay)) { + return static_cast(thisOverlay)->textWidth(text); + } + } + } + return 0.0f; +} diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 686b998267..5a66eb8ff5 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -16,6 +16,17 @@ #include "Overlay.h" +class OverlayPropertyResult { +public: + OverlayPropertyResult(); + QScriptValue value; +}; + +Q_DECLARE_METATYPE(OverlayPropertyResult); + +QScriptValue OverlayPropertyResultToScriptValue(QScriptEngine* engine, const OverlayPropertyResult& value); +void OverlayPropertyResultFromScriptValue(const QScriptValue& object, OverlayPropertyResult& value); + class RayToOverlayIntersectionResult { public: RayToOverlayIntersectionResult(); @@ -27,6 +38,7 @@ public: QString extraInfo; }; + Q_DECLARE_METATYPE(RayToOverlayIntersectionResult); QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, const RayToOverlayIntersectionResult& value); @@ -59,12 +71,19 @@ public slots: /// returns the top most 2D overlay at the screen point, or 0 if not overlay at that point unsigned int getOverlayAtPoint(const glm::vec2& point); + /// returns the value of specified property, or null if there is no such property + OverlayPropertyResult getProperty(unsigned int id, const QString& property); + /// returns details about the closest 3D Overlay hit by the pick ray RayToOverlayIntersectionResult findRayIntersection(const PickRay& ray); /// returns whether the overlay's assets are loaded or not bool isLoaded(unsigned int id); + /// returns the width of the given text in the specified overlay if it is a text overlay: in pixels if it is a 2D text + /// overlay; in meters if it is a 3D text overlay + float textWidth(unsigned int id, const QString& text) const; + private: QMap _overlays2D; QMap _overlays3D; @@ -73,6 +92,7 @@ private: QGLWidget* _parent; QReadWriteLock _lock; QReadWriteLock _deleteLock; + QScriptEngine* _scriptEngine; }; diff --git a/interface/src/ui/overlays/Planar3DOverlay.cpp b/interface/src/ui/overlays/Planar3DOverlay.cpp index 91a3a023f7..ffd7d73531 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.cpp +++ b/interface/src/ui/overlays/Planar3DOverlay.cpp @@ -77,6 +77,14 @@ void Planar3DOverlay::setProperties(const QScriptValue& properties) { } } +QScriptValue Planar3DOverlay::getProperty(const QString& property) { + if (property == "dimensions" || property == "scale" || property == "size") { + return vec2toScriptValue(_scriptEngine, _dimensions); + } + + return Base3DOverlay::getProperty(property); +} + bool Planar3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const { diff --git a/interface/src/ui/overlays/Planar3DOverlay.h b/interface/src/ui/overlays/Planar3DOverlay.h index ee4bb3e05a..fe73cfbe08 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.h +++ b/interface/src/ui/overlays/Planar3DOverlay.h @@ -36,6 +36,7 @@ public: void setDimensions(const glm::vec2& value) { _dimensions = value; } virtual void setProperties(const QScriptValue& properties); + virtual QScriptValue getProperty(const QString& property); virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const; diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index 072b52768a..8a8581599a 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -17,6 +17,8 @@ const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 }; const float DEFAULT_MARGIN = 0.1f; +const int FIXED_FONT_POINT_SIZE = 40; +const float LINE_SCALE_RATIO = 1.2f; Text3DOverlay::Text3DOverlay() : _backgroundColor(DEFAULT_BACKGROUND_COLOR), @@ -87,11 +89,10 @@ void Text3DOverlay::render(RenderArgs* args) { glVertex3f(-halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND); glEnd(); - const int FIXED_FONT_POINT_SIZE = 40; const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 40.0f; // this is a ratio determined through experimentation + // Same font properties as textWidth() TextRenderer* textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE); - float LINE_SCALE_RATIO = 1.2f; float maxHeight = (float)textRenderer->calculateHeight("Xy") * LINE_SCALE_RATIO; float scaleFactor = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight; @@ -179,4 +180,38 @@ void Text3DOverlay::setProperties(const QScriptValue& properties) { } +QScriptValue Text3DOverlay::getProperty(const QString& property) { + if (property == "text") { + return _text; + } + if (property == "backgroundColor") { + return xColorToScriptValue(_scriptEngine, _backgroundColor); + } + if (property == "lineHeight") { + return _lineHeight; + } + if (property == "leftMargin") { + return _leftMargin; + } + if (property == "topMargin") { + return _topMargin; + } + if (property == "rightMargin") { + return _rightMargin; + } + if (property == "bottomMargin") { + return _bottomMargin; + } + if (property == "isFacingAvatar") { + return _isFacingAvatar; + } + return Planar3DOverlay::getProperty(property); +} + +float Text3DOverlay::textWidth(const QString& text) const { + QFont font(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE); // Same font properties as render() + QFontMetrics fontMetrics(font); + float scaleFactor = _lineHeight * LINE_SCALE_RATIO / (float)FIXED_FONT_POINT_SIZE; + return scaleFactor * (float)fontMetrics.width(qPrintable(text)); +} diff --git a/interface/src/ui/overlays/Text3DOverlay.h b/interface/src/ui/overlays/Text3DOverlay.h index 45e311c554..ceb52b492e 100644 --- a/interface/src/ui/overlays/Text3DOverlay.h +++ b/interface/src/ui/overlays/Text3DOverlay.h @@ -47,6 +47,9 @@ public: void setIsFacingAvatar(bool isFacingAvatar) { _isFacingAvatar = isFacingAvatar; } virtual void setProperties(const QScriptValue& properties); + virtual QScriptValue getProperty(const QString& property); + + float textWidth(const QString& text) const; // Meters private: void enableClipPlane(GLenum plane, float x, float y, float z, float w); diff --git a/interface/src/ui/overlays/TextOverlay.cpp b/interface/src/ui/overlays/TextOverlay.cpp index 85ecca6d32..1c3a626132 100644 --- a/interface/src/ui/overlays/TextOverlay.cpp +++ b/interface/src/ui/overlays/TextOverlay.cpp @@ -66,9 +66,8 @@ void TextOverlay::render(RenderArgs* args) { glVertex2f(_bounds.left(), _bounds.bottom()); glEnd(); - //TextRenderer(const char* family, int pointSize = -1, int weight = -1, bool italic = false, - // EffectType effect = NO_EFFECT, int effectThickness = 1); - TextRenderer* textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, _fontSize, 50); + // Same font properties as textWidth() + TextRenderer* textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT); const int leftAdjust = -1; // required to make text render relative to left edge of bounds const int topAdjust = -2; // required to make text render relative to top edge of bounds @@ -125,4 +124,31 @@ void TextOverlay::setProperties(const QScriptValue& properties) { } } +QScriptValue TextOverlay::getProperty(const QString& property) { + if (property == "font") { + QScriptValue font = _scriptEngine->newObject(); + font.setProperty("size", _fontSize); + return font; + } + if (property == "text") { + return _text; + } + if (property == "backgroundColor") { + return xColorToScriptValue(_scriptEngine, _backgroundColor); + } + if (property == "leftMargin") { + return _leftMargin; + } + if (property == "topMargin") { + return _topMargin; + } + return Overlay2D::getProperty(property); +} + + +float TextOverlay::textWidth(const QString& text) const { + QFont font(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT); // Same font properties as render() + QFontMetrics fontMetrics(font); + return fontMetrics.width(qPrintable(text)); +} diff --git a/interface/src/ui/overlays/TextOverlay.h b/interface/src/ui/overlays/TextOverlay.h index f7ff83e542..7559b33df5 100644 --- a/interface/src/ui/overlays/TextOverlay.h +++ b/interface/src/ui/overlays/TextOverlay.h @@ -30,6 +30,7 @@ const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 }; const int DEFAULT_MARGIN = 10; const int DEFAULT_FONTSIZE = 11; +const int DEFAULT_FONT_WEIGHT = 50; class TextOverlay : public Overlay2D { Q_OBJECT @@ -52,6 +53,9 @@ public: void setFontSize(int fontSize) { _fontSize = fontSize; } virtual void setProperties(const QScriptValue& properties); + virtual QScriptValue getProperty(const QString& property); + + float textWidth(const QString& text) const; // Pixels private: diff --git a/interface/src/ui/overlays/Volume3DOverlay.cpp b/interface/src/ui/overlays/Volume3DOverlay.cpp index cc12d41e2e..0940caea04 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.cpp +++ b/interface/src/ui/overlays/Volume3DOverlay.cpp @@ -85,6 +85,14 @@ void Volume3DOverlay::setProperties(const QScriptValue& properties) { } } +QScriptValue Volume3DOverlay::getProperty(const QString& property) { + if (property == "dimensions" || property == "scale" || property == "size") { + return vec3toScriptValue(_scriptEngine, _dimensions); + } + + return Base3DOverlay::getProperty(property); +} + bool Volume3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const { diff --git a/interface/src/ui/overlays/Volume3DOverlay.h b/interface/src/ui/overlays/Volume3DOverlay.h index 7cde169c30..8787759022 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.h +++ b/interface/src/ui/overlays/Volume3DOverlay.h @@ -38,6 +38,7 @@ public: void setDimensions(const glm::vec3& value) { _dimensions = value; } virtual void setProperties(const QScriptValue& properties); + virtual QScriptValue getProperty(const QString& property); virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const; diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 1743504883..1c9c93c00e 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -31,7 +31,6 @@ void injectorFromScriptValue(const QScriptValue& object, AudioInjector*& out) { AudioInjector::AudioInjector(QObject* parent) : QObject(parent), - _sound(NULL), _options(), _shouldStop(false), _loudness(0.0f), @@ -42,7 +41,7 @@ AudioInjector::AudioInjector(QObject* parent) : } AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions) : - _sound(sound), + _audioData(sound->getByteArray()), _options(injectorOptions), _shouldStop(false), _loudness(0.0f), @@ -52,6 +51,18 @@ AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorO { } +AudioInjector::AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions) : + _audioData(audioData), + _options(injectorOptions), + _shouldStop(false), + _loudness(0.0f), + _isFinished(false), + _currentSendPosition(0), + _localBuffer(NULL) +{ + +} + AudioInjector::~AudioInjector() { if (_localBuffer) { _localBuffer->stop(); @@ -76,11 +87,9 @@ void AudioInjector::injectAudio() { void AudioInjector::injectLocally() { bool success = false; - if (_localAudioInterface) { - const QByteArray& soundByteArray = _sound->getByteArray(); - - if (soundByteArray.size() > 0) { - _localBuffer = new AudioInjectorLocalBuffer(_sound->getByteArray(), this); + if (_localAudioInterface) { + if (_audioData.size() > 0) { + _localBuffer = new AudioInjectorLocalBuffer(_audioData, this); _localBuffer->open(QIODevice::ReadOnly); _localBuffer->setShouldLoop(_options.loop); @@ -114,15 +123,13 @@ void AudioInjector::injectLocally() { const uchar MAX_INJECTOR_VOLUME = 0xFF; void AudioInjector::injectToMixer() { - QByteArray soundByteArray = _sound->getByteArray(); - if (_currentSendPosition < 0 || - _currentSendPosition >= soundByteArray.size()) { + _currentSendPosition >= _audioData.size()) { _currentSendPosition = 0; } // make sure we actually have samples downloaded to inject - if (soundByteArray.size()) { + if (_audioData.size()) { // setup the packet for injected audio QByteArray injectAudioPacket = byteArrayWithPopulatedHeader(PacketTypeInjectAudio); @@ -172,15 +179,15 @@ void AudioInjector::injectToMixer() { // loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks quint16 outgoingInjectedAudioSequenceNumber = 0; - while (_currentSendPosition < soundByteArray.size() && !_shouldStop) { + while (_currentSendPosition < _audioData.size() && !_shouldStop) { int bytesToCopy = std::min(((_options.stereo) ? 2 : 1) * NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL, - soundByteArray.size() - _currentSendPosition); + _audioData.size() - _currentSendPosition); // Measure the loudness of this frame _loudness = 0.0f; for (int i = 0; i < bytesToCopy; i += sizeof(int16_t)) { - _loudness += abs(*reinterpret_cast(soundByteArray.data() + _currentSendPosition + i)) / + _loudness += abs(*reinterpret_cast(_audioData.data() + _currentSendPosition + i)) / (MAX_SAMPLE_VALUE / 2.0f); } _loudness /= (float)(bytesToCopy / sizeof(int16_t)); @@ -203,7 +210,7 @@ void AudioInjector::injectToMixer() { // copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet memcpy(injectAudioPacket.data() + numPreAudioDataBytes, - soundByteArray.data() + _currentSendPosition, bytesToCopy); + _audioData.data() + _currentSendPosition, bytesToCopy); // grab our audio mixer from the NodeList, if it exists NodeList* nodeList = NodeList::getInstance(); @@ -217,7 +224,7 @@ void AudioInjector::injectToMixer() { // send two packets before the first sleep so the mixer can start playback right away - if (_currentSendPosition != bytesToCopy && _currentSendPosition < soundByteArray.size()) { + if (_currentSendPosition != bytesToCopy && _currentSendPosition < _audioData.size()) { // not the first packet and not done // sleep for the appropriate time int usecToSleep = (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - timer.nsecsElapsed() / 1000; @@ -227,7 +234,7 @@ void AudioInjector::injectToMixer() { } } - if (shouldLoop && _currentSendPosition >= soundByteArray.size()) { + if (shouldLoop && _currentSendPosition >= _audioData.size()) { _currentSendPosition = 0; } } diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 13188c5977..257b538c11 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -29,6 +29,7 @@ class AudioInjector : public QObject { public: AudioInjector(QObject* parent); AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions); + AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions); ~AudioInjector(); bool isFinished() const { return _isFinished; } @@ -51,7 +52,7 @@ private: void injectToMixer(); void injectLocally(); - Sound* _sound; + QByteArray _audioData; AudioInjectorOptions _options; bool _shouldStop; float _loudness; diff --git a/libraries/audio/src/AudioScriptingInterface.cpp b/libraries/audio/src/AudioScriptingInterface.cpp index cb010ef11d..35a11f4dd4 100644 --- a/libraries/audio/src/AudioScriptingInterface.cpp +++ b/libraries/audio/src/AudioScriptingInterface.cpp @@ -43,28 +43,32 @@ void AudioScriptingInterface::stopAllInjectors() { } AudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions& injectorOptions) { - - AudioInjector* injector = new AudioInjector(sound, injectorOptions); - injector->setLocalAudioInterface(_localAudioInterface); - - QThread* injectorThread = new QThread(); - - injector->moveToThread(injectorThread); - - // start injecting when the injector thread starts - connect(injectorThread, &QThread::started, injector, &AudioInjector::injectAudio); - - // connect the right slots and signals so that the AudioInjector is killed once the injection is complete - connect(injector, &AudioInjector::finished, injector, &AudioInjector::deleteLater); - connect(injector, &AudioInjector::finished, injectorThread, &QThread::quit); - connect(injector, &AudioInjector::finished, this, &AudioScriptingInterface::injectorStopped); - connect(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater); - - injectorThread->start(); - - _activeInjectors.append(QPointer(injector)); - - return injector; + if (sound) { + AudioInjector* injector = new AudioInjector(sound, injectorOptions); + injector->setLocalAudioInterface(_localAudioInterface); + + QThread* injectorThread = new QThread(); + + injector->moveToThread(injectorThread); + + // start injecting when the injector thread starts + connect(injectorThread, &QThread::started, injector, &AudioInjector::injectAudio); + + // connect the right slots and signals so that the AudioInjector is killed once the injection is complete + connect(injector, &AudioInjector::finished, injector, &AudioInjector::deleteLater); + connect(injector, &AudioInjector::finished, injectorThread, &QThread::quit); + connect(injector, &AudioInjector::finished, this, &AudioScriptingInterface::injectorStopped); + connect(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater); + + injectorThread->start(); + + _activeInjectors.append(QPointer(injector)); + + return injector; + } else { + qDebug() << "AudioScriptingInterface::playSound called with null Sound object."; + return NULL; + } } void AudioScriptingInterface::stopInjector(AudioInjector* injector) { diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 4c520f27ce..ff54e262f8 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -29,96 +29,27 @@ #include "AudioEditBuffer.h" #include "Sound.h" - -QScriptValue soundToScriptValue(QScriptEngine* engine, Sound* const& in) { - return engine->newQObject(in); +QScriptValue soundToScriptValue(QScriptEngine* engine, SharedSoundPointer const& in) { + return engine->newQObject(in.data()); } -void soundFromScriptValue(const QScriptValue& object, Sound*& out) { - out = qobject_cast(object.toQObject()); +void soundFromScriptValue(const QScriptValue &object, SharedSoundPointer &out) { + out = SharedSoundPointer(qobject_cast(object.toQObject())); + qDebug() << "Sound from script value" << out.data(); } -// procedural audio version of Sound -Sound::Sound(float volume, float frequency, float duration, float decay, QObject* parent) : - QObject(parent), - _isStereo(false) -{ - static char monoAudioData[MAX_PACKET_SIZE]; - static int16_t* monoAudioSamples = (int16_t*)(monoAudioData); - - float t; - const float AUDIO_CALLBACK_MSECS = (float) NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL / (float)SAMPLE_RATE * 1000.0; - const float MAX_VOLUME = 32000.f; - const float MAX_DURATION = 2.f; - const float MIN_AUDIBLE_VOLUME = 0.001f; - const float NOISE_MAGNITUDE = 0.02f; - const int MAX_SAMPLE_VALUE = std::numeric_limits::max(); - const int MIN_SAMPLE_VALUE = std::numeric_limits::min(); - int numSamples = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; // we add sounds in chunks of this many samples - - int chunkStartingSample = 0; - float waveFrequency = (frequency / SAMPLE_RATE) * TWO_PI; - while (volume > 0.f) { - for (int i = 0; i < numSamples; i++) { - t = (float)chunkStartingSample + (float)i; - float sample = sinf(t * waveFrequency); - sample += ((randFloat() - 0.5f) * NOISE_MAGNITUDE); - sample *= volume * MAX_VOLUME; - - monoAudioSamples[i] = glm::clamp((int)sample, MIN_SAMPLE_VALUE, MAX_SAMPLE_VALUE); - volume *= (1.f - decay); - } - // add the monoAudioSamples to our actual output Byte Array - _byteArray.append(monoAudioData, numSamples * sizeof(int16_t)); - chunkStartingSample += numSamples; - duration = glm::clamp(duration - (AUDIO_CALLBACK_MSECS / 1000.f), 0.f, MAX_DURATION); - //qDebug() << "decaying... _duration=" << _duration; - if (duration == 0.f || (volume < MIN_AUDIBLE_VOLUME)) { - volume = 0.f; - } - } -} - -Sound::Sound(const QUrl& sampleURL, bool isStereo, QObject* parent) : - QObject(parent), +Sound::Sound(const QUrl& url, bool isStereo) : + Resource(url), _isStereo(isStereo), - _hasDownloaded(false) + _isReady(false) { - // assume we have a QApplication or QCoreApplication instance and use the - // QNetworkAccess manager to grab the raw audio file at the given URL - - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - - qDebug() << "Requesting audio file" << sampleURL.toDisplayString(); - QNetworkReply* soundDownload = networkAccessManager.get(QNetworkRequest(sampleURL)); - connect(soundDownload, &QNetworkReply::finished, this, &Sound::replyFinished); - connect(soundDownload, SIGNAL(error(QNetworkReply::NetworkError)), - this, SLOT(replyError(QNetworkReply::NetworkError))); } -Sound::Sound(const QByteArray byteArray, QObject* parent) : - QObject(parent), - _byteArray(byteArray), - _isStereo(false), - _hasDownloaded(true) -{ -} - -void Sound::append(const QByteArray byteArray) { - _byteArray.append(byteArray); -} - -void Sound::replyFinished() { - - QNetworkReply* reply = reinterpret_cast(sender()); - +void Sound::downloadFinished(QNetworkReply* reply) { // replace our byte array with the downloaded data QByteArray rawAudioByteArray = reply->readAll(); - // foreach(QByteArray b, reply->rawHeaderList()) - // qDebug() << b.constData() << ": " << reply->rawHeader(b).constData(); - if (reply->hasRawHeader("Content-Type")) { QByteArray headerContentType = reply->rawHeader("Content-Type"); @@ -141,12 +72,7 @@ void Sound::replyFinished() { qDebug() << "Network reply without 'Content-Type'."; } - _hasDownloaded = true; -} - -void Sound::replyError(QNetworkReply::NetworkError code) { - QNetworkReply* reply = reinterpret_cast(sender()); - qDebug() << "Error downloading sound file at" << reply->url().toString() << "-" << reply->errorString(); + _isReady = true; } void Sound::downSample(const QByteArray& rawAudioByteArray) { diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h index f7b51891f0..c78bf72ff7 100644 --- a/libraries/audio/src/Sound.h +++ b/libraries/audio/src/Sound.h @@ -16,38 +16,37 @@ #include #include -class Sound : public QObject { +#include + +class Sound : public Resource { Q_OBJECT - Q_PROPERTY(bool downloaded READ hasDownloaded) + Q_PROPERTY(bool downloaded READ isReady) public: - Sound(const QUrl& sampleURL, bool isStereo = false, QObject* parent = NULL); - Sound(float volume, float frequency, float duration, float decay, QObject* parent = NULL); - Sound(const QByteArray byteArray, QObject* parent = NULL); - void append(const QByteArray byteArray); + Sound(const QUrl& url, bool isStereo = false); bool isStereo() const { return _isStereo; } - bool hasDownloaded() const { return _hasDownloaded; } - + bool isReady() const { return _isReady; } + const QByteArray& getByteArray() { return _byteArray; } private: QByteArray _byteArray; bool _isStereo; - bool _hasDownloaded; + bool _isReady; void trimFrames(); void downSample(const QByteArray& rawAudioByteArray); void interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray); - -private slots: - void replyFinished(); - void replyError(QNetworkReply::NetworkError code); + + virtual void downloadFinished(QNetworkReply* reply); }; -Q_DECLARE_METATYPE(Sound*) +typedef QSharedPointer SharedSoundPointer; -QScriptValue soundToScriptValue(QScriptEngine* engine, Sound* const& in); -void soundFromScriptValue(const QScriptValue& object, Sound*& out); +Q_DECLARE_METATYPE(SharedSoundPointer) + +QScriptValue soundToScriptValue(QScriptEngine* engine, SharedSoundPointer const& in); +void soundFromScriptValue(const QScriptValue& object, SharedSoundPointer& out); #endif // hifi_Sound_h diff --git a/libraries/audio/src/SoundCache.cpp b/libraries/audio/src/SoundCache.cpp new file mode 100644 index 0000000000..4fbd98fea0 --- /dev/null +++ b/libraries/audio/src/SoundCache.cpp @@ -0,0 +1,43 @@ +// +// SoundCache.cpp +// libraries/audio/src +// +// Created by Stephen Birarda on 2014-11-13. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include "SoundCache.h" + +static int soundPointerMetaTypeId = qRegisterMetaType(); + +SoundCache& SoundCache::getInstance() { + static SoundCache staticInstance; + return staticInstance; +} + +SoundCache::SoundCache(QObject* parent) : + ResourceCache(parent) +{ + +} + +SharedSoundPointer SoundCache::getSound(const QUrl& url) { + if (QThread::currentThread() != thread()) { + SharedSoundPointer result; + QMetaObject::invokeMethod(this, "getSound", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(SharedSoundPointer, result), Q_ARG(const QUrl&, url)); + return result; + } + return getResource(url).staticCast(); +} + +QSharedPointer SoundCache::createResource(const QUrl& url, const QSharedPointer& fallback, + bool delayLoad, const void* extra) { + qDebug() << "Requesting sound at" << url.toString(); + return QSharedPointer(new Sound(url), &Resource::allReferencesCleared); +} \ No newline at end of file diff --git a/libraries/audio/src/SoundCache.h b/libraries/audio/src/SoundCache.h new file mode 100644 index 0000000000..f9fbf51c10 --- /dev/null +++ b/libraries/audio/src/SoundCache.h @@ -0,0 +1,34 @@ +// +// SoundCache.h +// libraries/audio/src +// +// Created by Stephen Birarda on 2014-11-13. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_SoundCache_h +#define hifi_SoundCache_h + +#include + +#include "Sound.h" + +/// Scriptable interface for sound loading. +class SoundCache : public ResourceCache { + Q_OBJECT +public: + static SoundCache& getInstance(); + + Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url); + +protected: + virtual QSharedPointer createResource(const QUrl& url, + const QSharedPointer& fallback, bool delayLoad, const void* extra); +private: + SoundCache(QObject* parent = NULL); +}; + +#endif // hifi_SoundCache_h \ No newline at end of file diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp index 63384371bd..9f01e98730 100644 --- a/libraries/avatars/src/Player.cpp +++ b/libraries/avatars/src/Player.cpp @@ -168,7 +168,7 @@ void Player::setupAudioThread() { _audioThread = new QThread(); _options.position = _avatar->getPosition(); _options.orientation = _avatar->getOrientation(); - _injector.reset(new AudioInjector(_recording->getAudio(), _options), &QObject::deleteLater); + _injector.reset(new AudioInjector(_recording->getAudioData(), _options), &QObject::deleteLater); _injector->moveToThread(_audioThread); _audioThread->start(); QMetaObject::invokeMethod(_injector.data(), "injectAudio", Qt::QueuedConnection); diff --git a/libraries/avatars/src/Recording.cpp b/libraries/avatars/src/Recording.cpp index 0d089a2bd2..b39421d03a 100644 --- a/libraries/avatars/src/Recording.cpp +++ b/libraries/avatars/src/Recording.cpp @@ -43,13 +43,6 @@ void RecordingFrame::setBlendshapeCoefficients(QVector blendshapeCoeffici _blendshapeCoefficients = blendshapeCoefficients; } -Recording::Recording() : _audio(NULL) { -} - -Recording::~Recording() { - delete _audio; -} - int Recording::getLength() const { if (_timestamps.isEmpty()) { return 0; @@ -77,19 +70,10 @@ void Recording::addFrame(int timestamp, RecordingFrame &frame) { _frames << frame; } -void Recording::addAudioPacket(const QByteArray& byteArray) { - if (!_audio) { - _audio = new Sound(byteArray); - return; - } - _audio->append(byteArray); -} - void Recording::clear() { _timestamps.clear(); _frames.clear(); - delete _audio; - _audio = NULL; + _audioData.clear(); } void writeVec3(QDataStream& stream, const glm::vec3& value) { @@ -324,7 +308,7 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { fileStream << buffer; } - fileStream << recording->_audio->getByteArray(); + fileStream << recording->getAudioData(); qint64 writingTime = timer.restart(); // Write data length and CRC-16 @@ -367,7 +351,7 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { qDebug() << "Recording:"; qDebug() << "Total frames:" << recording->getFrameNumber(); - qDebug() << "Audio array:" << recording->getAudio()->getByteArray().size(); + qDebug() << "Audio array:" << recording->getAudioData().size(); } qint64 checksumTime = timer.elapsed(); @@ -642,7 +626,7 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString qDebug() << "Recording:"; qDebug() << "Total frames:" << recording->getFrameNumber(); - qDebug() << "Audio array:" << recording->getAudio()->getByteArray().size(); + qDebug() << "Audio array:" << recording->getAudioData().size(); } diff --git a/libraries/avatars/src/Recording.h b/libraries/avatars/src/Recording.h index aa14dc5d76..d1da77560c 100644 --- a/libraries/avatars/src/Recording.h +++ b/libraries/avatars/src/Recording.h @@ -48,9 +48,6 @@ public: /// Stores a recording class Recording { public: - Recording(); - ~Recording(); - bool isEmpty() const { return _timestamps.isEmpty(); } int getLength() const; // in ms @@ -58,11 +55,11 @@ public: int getFrameNumber() const { return _frames.size(); } qint32 getFrameTimestamp(int i) const; const RecordingFrame& getFrame(int i) const; - Sound* getAudio() const { return _audio; } + const QByteArray& getAudioData() const { return _audioData; } protected: void addFrame(int timestamp, RecordingFrame& frame); - void addAudioPacket(const QByteArray& byteArray); + void addAudioPacket(const QByteArray& byteArray) { _audioData.append(byteArray); } void clear(); private: @@ -70,7 +67,7 @@ private: QVector _timestamps; QVector _frames; - Sound* _audio; + QByteArray _audioData; friend class Recorder; friend class Player; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index ef02aafbc8..d5790d88a7 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -28,6 +28,7 @@ const float EntityItem::DEFAULT_GLOW_LEVEL = 0.0f; const float EntityItem::DEFAULT_LOCAL_RENDER_ALPHA = 1.0f; const float EntityItem::DEFAULT_MASS = 1.0f; const float EntityItem::DEFAULT_LIFETIME = EntityItem::IMMORTAL; +const QString EntityItem::DEFAULT_USER_DATA = QString(""); const float EntityItem::DEFAULT_DAMPING = 0.5f; const glm::vec3 EntityItem::NO_VELOCITY = glm::vec3(0, 0, 0); const float EntityItem::EPSILON_VELOCITY_LENGTH = (1.0f / 1000.0f) / (float)TREE_SCALE; // really small: 1mm/second @@ -71,6 +72,7 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) { _gravity = DEFAULT_GRAVITY; _damping = DEFAULT_DAMPING; _lifetime = DEFAULT_LIFETIME; + _userData = DEFAULT_USER_DATA; _registrationPoint = DEFAULT_REGISTRATION_POINT; _angularVelocity = DEFAULT_ANGULAR_VELOCITY; _angularDamping = DEFAULT_ANGULAR_DAMPING; @@ -123,6 +125,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_IGNORE_FOR_COLLISIONS; requestedProperties += PROP_COLLISIONS_WILL_MOVE; requestedProperties += PROP_LOCKED; + requestedProperties += PROP_USER_DATA; return requestedProperties; } @@ -239,6 +242,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, appendValue, getIgnoreForCollisions()); APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, appendValue, getCollisionsWillMove()); APPEND_ENTITY_PROPERTY(PROP_LOCKED, appendValue, getLocked()); + APPEND_ENTITY_PROPERTY(PROP_USER_DATA, appendValue, getUserData()); appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, @@ -502,6 +506,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, bool, _ignoreForCollisions); READ_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, bool, _collisionsWillMove); READ_ENTITY_PROPERTY(PROP_LOCKED, bool, _locked); + READ_ENTITY_PROPERTY_STRING(PROP_USER_DATA,setUserData); if (wantDebug) { qDebug() << " readEntityDataFromBuffer() _registrationPoint:" << _registrationPoint; @@ -758,6 +763,7 @@ EntityItemProperties EntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(ignoreForCollisions, getIgnoreForCollisions); COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionsWillMove, getCollisionsWillMove); COPY_ENTITY_PROPERTY_TO_PROPERTIES(locked, getLocked); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(userData, getUserData); properties._defaultSettings = false; @@ -794,6 +800,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties, bool forc SET_ENTITY_PROPERTY_FROM_PROPERTIES(ignoreForCollisions, setIgnoreForCollisions); SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, setCollisionsWillMove); SET_ENTITY_PROPERTY_FROM_PROPERTIES(locked, setLocked); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData); if (somethingChanged) { somethingChangedNotification(); // notify derived classes that something has changed diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index adf72198be..cb153dee60 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -252,6 +252,10 @@ public: bool getLocked() const { return _locked; } void setLocked(bool value) { _locked = value; } + static const QString DEFAULT_USER_DATA; + const QString& getUserData() const { return _userData; } + void setUserData(const QString& value) { _userData = value; } + // TODO: We need to get rid of these users of getRadius()... float getRadius() const; @@ -292,6 +296,7 @@ protected: bool _ignoreForCollisions; bool _collisionsWillMove; bool _locked; + QString _userData; // NOTE: Radius support is obsolete, but these private helper functions are available for this class to // parse old data streams diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 0c184d5e35..75fa05032a 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -37,6 +37,7 @@ EntityItemProperties::EntityItemProperties() : _gravity(EntityItem::DEFAULT_GRAVITY), _damping(EntityItem::DEFAULT_DAMPING), _lifetime(EntityItem::DEFAULT_LIFETIME), + _userData(EntityItem::DEFAULT_USER_DATA), _script(EntityItem::DEFAULT_SCRIPT), _registrationPoint(EntityItem::DEFAULT_REGISTRATION_POINT), _angularVelocity(EntityItem::DEFAULT_ANGULAR_VELOCITY), @@ -53,6 +54,7 @@ EntityItemProperties::EntityItemProperties() : _gravityChanged(false), _dampingChanged(false), _lifetimeChanged(false), + _userDataChanged(false), _scriptChanged(false), _registrationPointChanged(false), _angularVelocityChanged(false), @@ -223,6 +225,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_CUTOFF, cutoff); CHECK_PROPERTY_CHANGE(PROP_LOCKED, locked); CHECK_PROPERTY_CHANGE(PROP_TEXTURES, textures); + CHECK_PROPERTY_CHANGE(PROP_USER_DATA, userData); return changedProperties; } @@ -276,6 +279,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons COPY_PROPERTY_TO_QSCRIPTVALUE(cutoff); COPY_PROPERTY_TO_QSCRIPTVALUE(locked); COPY_PROPERTY_TO_QSCRIPTVALUE(textures); + COPY_PROPERTY_TO_QSCRIPTVALUE(userData); // Sitting properties support QScriptValue sittingPoints = engine->newObject(); @@ -350,6 +354,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) { COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(cutoff, setCutoff); COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(locked, setLocked); COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(textures, setTextures); + COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(userData, setUserData); _lastEdited = usecTimestampNow(); } @@ -512,6 +517,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_LOCKED, appendValue, properties.getLocked()); APPEND_ENTITY_PROPERTY(PROP_TEXTURES, appendValue, properties.getTextures()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_SETTINGS, appendValue, properties.getAnimationSettings()); + APPEND_ENTITY_PROPERTY(PROP_USER_DATA, appendValue, properties.getUserData()); } if (propertyCount > 0) { int endOfEntityItemData = packetData->getUncompressedByteOffset(); @@ -722,6 +728,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCKED, bool, setLocked); READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_TEXTURES, setTextures); READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_ANIMATION_SETTINGS, setAnimationSettings); + READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_USER_DATA, setUserData); return valid; } @@ -764,6 +771,7 @@ void EntityItemProperties::markAllChanged() { _gravityChanged = true; _dampingChanged = true; _lifetimeChanged = true; + _userDataChanged = true; _scriptChanged = true; _registrationPointChanged = true; _angularVelocityChanged = true; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index d6b8181c28..273aedb18a 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -81,8 +81,9 @@ enum EntityPropertyList { // used by Model entities PROP_TEXTURES, PROP_ANIMATION_SETTINGS, + PROP_USER_DATA, - PROP_LAST_ITEM = PROP_ANIMATION_SETTINGS + PROP_LAST_ITEM = PROP_USER_DATA }; typedef PropertyFlags EntityPropertyFlags; @@ -156,9 +157,13 @@ public: float getDamping() const { return _damping; } void setDamping(float value) { _damping = value; _dampingChanged = true; } - + float getLifetime() const { return _lifetime; } /// get the lifetime in seconds for the entity void setLifetime(float value) { _lifetime = value; _lifetimeChanged = true; } /// set the lifetime in seconds for the entity + + const QString& getUserData() const { return _userData; } + void setUserData(const QString& value) { _userData = value; _userDataChanged = true; } + float getAge() const { return (float)(usecTimestampNow() - _created) / (float)USECS_PER_SECOND; } quint64 getCreated() const { return _created; } void setCreated(quint64 usecTime) { _created = usecTime; } @@ -216,6 +221,7 @@ public: bool gravityChanged() const { return _gravityChanged; } bool dampingChanged() const { return _dampingChanged; } bool lifetimeChanged() const { return _lifetimeChanged; } + bool userDataChanged() const { return _userDataChanged; } bool scriptChanged() const { return _scriptChanged; } bool dimensionsChanged() const { return _dimensionsChanged; } bool registrationPointChanged() const { return _registrationPointChanged; } @@ -315,6 +321,7 @@ private: glm::vec3 _gravity; float _damping; float _lifetime; + QString _userData; QString _script; glm::vec3 _registrationPoint; glm::vec3 _angularVelocity; @@ -331,6 +338,7 @@ private: bool _gravityChanged; bool _dampingChanged; bool _lifetimeChanged; + bool _userDataChanged; bool _scriptChanged; bool _registrationPointChanged; bool _angularVelocityChanged; diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 52b8f7e643..f50fe7866b 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -115,7 +115,9 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, animationIsPlaying); if (propertyFlags.getHasProperty(PROP_ANIMATION_PLAYING)) { - setAnimationIsPlaying(animationIsPlaying); + if (animationIsPlaying != getAnimationIsPlaying()) { + setAnimationIsPlaying(animationIsPlaying); + } } if (propertyFlags.getHasProperty(PROP_ANIMATION_FPS)) { setAnimationFPS(animationFPS); @@ -345,7 +347,6 @@ QVector ModelEntityItem::getAnimationFrame() { if (frameCount > 0) { int animationFrameIndex = (int)(glm::floor(getAnimationFrameIndex())) % frameCount; - if (animationFrameIndex < 0 || animationFrameIndex > frameCount) { animationFrameIndex = 0; } @@ -427,7 +428,9 @@ void ModelEntityItem::setAnimationSettings(const QString& value) { if (settingsMap.contains("running")) { bool running = settingsMap["running"].toBool(); - setAnimationIsPlaying(running); + if (running != getAnimationIsPlaying()) { + setAnimationIsPlaying(running); + } } if (settingsMap.contains("firstFrame")) { diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 9d74935fb4..b1c47c0ebf 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -75,7 +75,7 @@ PacketVersion versionForPacketType(PacketType type) { return 1; case PacketTypeEntityAddOrEdit: case PacketTypeEntityData: - return VERSION_ENTITIES_MODELS_HAVE_ANIMATION_SETTINGS; + return VERSION_ENTITIES_HAVE_USER_DATA; case PacketTypeEntityErase: return 2; case PacketTypeAudioStreamStats: diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 466aebd36b..d617f2243a 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -125,6 +125,7 @@ const PacketVersion VERSION_ENTITIES_SUPPORT_SPLIT_MTU = 3; const PacketVersion VERSION_ENTITIES_HAS_FILE_BREAKS = VERSION_ENTITIES_SUPPORT_SPLIT_MTU; const PacketVersion VERSION_ENTITIES_SUPPORT_DIMENSIONS = 4; const PacketVersion VERSION_ENTITIES_MODELS_HAVE_ANIMATION_SETTINGS = 5; +const PacketVersion VERSION_ENTITIES_HAVE_USER_DATA = 6; const PacketVersion VERSION_VOXELS_HAS_FILE_BREAKS = 1; #endif // hifi_PacketHeaders_h diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index bd0f1cae01..097ede23d0 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -22,7 +22,9 @@ ResourceCache::ResourceCache(QObject* parent) : QObject(parent), - _lastLRUKey(0) { + _lastLRUKey(0) +{ + } ResourceCache::~ResourceCache() { @@ -291,7 +293,7 @@ void Resource::makeRequest() { connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64))); connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError())); connect(_reply, SIGNAL(finished()), SLOT(handleReplyFinished())); - + _replyTimer = new QTimer(this); connect(_replyTimer, SIGNAL(timeout()), SLOT(handleReplyTimeout())); _replyTimer->setSingleShot(true); diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 4450689949..e9cf1158fa 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -74,7 +74,7 @@ void Octree::recurseElementWithOperation(OctreeElement* element, RecurseOctreeOp if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex( - "Octree::recurseElementWithOperation() reached DANGEROUSLY_DEEP_RECURSION, bailing!"); + "Octree::recurseElementWithOperation\\(\\) reached DANGEROUSLY_DEEP_RECURSION, bailing!"); qDebug() << "Octree::recurseElementWithOperation() reached DANGEROUSLY_DEEP_RECURSION, bailing!"; return; @@ -96,7 +96,7 @@ void Octree::recurseElementWithPostOperation(OctreeElement* element, RecurseOctr if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex( - "Octree::recurseElementWithPostOperation() reached DANGEROUSLY_DEEP_RECURSION, bailing!"); + "Octree::recurseElementWithPostOperation\\(\\) reached DANGEROUSLY_DEEP_RECURSION, bailing!"); qDebug() << "Octree::recurseElementWithPostOperation() reached DANGEROUSLY_DEEP_RECURSION, bailing!"; return; @@ -126,7 +126,7 @@ void Octree::recurseElementWithOperationDistanceSorted(OctreeElement* element, R if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex( - "Octree::recurseElementWithOperationDistanceSorted() reached DANGEROUSLY_DEEP_RECURSION, bailing!"); + "Octree::recurseElementWithOperationDistanceSorted\\(\\) reached DANGEROUSLY_DEEP_RECURSION, bailing!"); qDebug() << "Octree::recurseElementWithOperationDistanceSorted() reached DANGEROUSLY_DEEP_RECURSION, bailing!"; return; @@ -167,7 +167,7 @@ bool Octree::recurseElementWithOperator(OctreeElement* element, RecurseOctreeOpe if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex( - "Octree::recurseElementWithOperator() reached DANGEROUSLY_DEEP_RECURSION, bailing!"); + "Octree::recurseElementWithOperator\\(\\) reached DANGEROUSLY_DEEP_RECURSION, bailing!"); qDebug() << "Octree::recurseElementWithOperator() reached DANGEROUSLY_DEEP_RECURSION, bailing!"; return false; @@ -231,8 +231,18 @@ OctreeElement* Octree::nodeForOctalCode(OctreeElement* ancestorElement, } // returns the element created! -OctreeElement* Octree::createMissingElement(OctreeElement* lastParentElement, const unsigned char* codeToReach) { +OctreeElement* Octree::createMissingElement(OctreeElement* lastParentElement, const unsigned char* codeToReach, int recursionCount) { + + if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { + static QString repeatedMessage + = LogHandler::getInstance().addRepeatedMessageRegex( + "Octree::createMissingElement\\(\\) reached DANGEROUSLY_DEEP_RECURSION, bailing!"); + + qDebug() << "Octree::createMissingElement() reached DANGEROUSLY_DEEP_RECURSION, bailing!"; + return lastParentElement; + } int indexOfNewChild = branchIndexWithDescendant(lastParentElement->getOctalCode(), codeToReach); + // If this parent element is a leaf, then you know the child path doesn't exist, so deal with // breaking up the leaf first, which will also create a child path if (lastParentElement->requiresSplit()) { @@ -246,7 +256,7 @@ OctreeElement* Octree::createMissingElement(OctreeElement* lastParentElement, co if (*lastParentElement->getChildAtIndex(indexOfNewChild)->getOctalCode() == *codeToReach) { return lastParentElement->getChildAtIndex(indexOfNewChild); } else { - return createMissingElement(lastParentElement->getChildAtIndex(indexOfNewChild), codeToReach); + return createMissingElement(lastParentElement->getChildAtIndex(indexOfNewChild), codeToReach, recursionCount + 1); } } @@ -255,25 +265,20 @@ int Octree::readElementData(OctreeElement* destinationElement, const unsigned ch int bytesLeftToRead = bytesAvailable; int bytesRead = 0; - bool wantDebug = false; // give this destination element the child mask from the packet const unsigned char ALL_CHILDREN_ASSUMED_TO_EXIST = 0xFF; if ((size_t)bytesLeftToRead < sizeof(unsigned char)) { - if (wantDebug) { - qDebug() << "UNEXPECTED: readElementData() only had " << bytesLeftToRead << " bytes. " - "Not enough for meaningful data."; - } + qDebug() << "UNEXPECTED: readElementData() only had " << bytesLeftToRead << " bytes. " + "Not enough for meaningful data."; return bytesAvailable; // assume we read the entire buffer... } if (destinationElement->getScale() < SCALE_AT_DANGEROUSLY_DEEP_RECURSION) { - if (wantDebug) { - qDebug() << "UNEXPECTED: readElementData() destination element is unreasonably small [" - << destinationElement->getScale() * (float)TREE_SCALE << " meters] " - << " Discarding " << bytesAvailable << " remaining bytes."; - } + qDebug() << "UNEXPECTED: readElementData() destination element is unreasonably small [" + << destinationElement->getScale() * (float)TREE_SCALE << " meters] " + << " Discarding " << bytesAvailable << " remaining bytes."; return bytesAvailable; // assume we read the entire buffer... } @@ -322,7 +327,7 @@ int Octree::readElementData(OctreeElement* destinationElement, const unsigned ch : sizeof(childInBufferMask); if (bytesLeftToRead < bytesForMasks) { - if (wantDebug) { + if (bytesLeftToRead > 0) { qDebug() << "UNEXPECTED: readElementDataFromBuffer() only had " << bytesLeftToRead << " bytes before masks. " "Not enough for meaningful data."; } @@ -385,7 +390,6 @@ void Octree::readBitstreamToTree(const unsigned char * bitstream, unsigned long ReadBitstreamToTreeParams& args) { int bytesRead = 0; const unsigned char* bitstreamAt = bitstream; - bool wantDebug = false; // If destination element is not included, set it to root if (!args.destinationElement) { @@ -398,14 +402,24 @@ void Octree::readBitstreamToTree(const unsigned char * bitstream, unsigned long while (bitstreamAt < bitstream + bufferSizeBytes) { OctreeElement* bitstreamRootElement = nodeForOctalCode(args.destinationElement, (unsigned char *)bitstreamAt, NULL); - int numberOfThreeBitSectionsInStream = numberOfThreeBitSectionsInCode(bitstreamAt, bufferSizeBytes); + if (numberOfThreeBitSectionsInStream > UNREASONABLY_DEEP_RECURSION) { + static QString repeatedMessage + = LogHandler::getInstance().addRepeatedMessageRegex( + "UNEXPECTED: parsing of the octal code would make UNREASONABLY_DEEP_RECURSION... " + "numberOfThreeBitSectionsInStream: \\d+ This buffer is corrupt. Returning." + ); + + + qDebug() << "UNEXPECTED: parsing of the octal code would make UNREASONABLY_DEEP_RECURSION... " + "numberOfThreeBitSectionsInStream:" << numberOfThreeBitSectionsInStream << + "This buffer is corrupt. Returning."; + return; + } if (numberOfThreeBitSectionsInStream == OVERFLOWED_OCTCODE_BUFFER) { - if (wantDebug) { - qDebug() << "UNEXPECTED: parsing of the octal code would overflow the buffer. " - "This buffer is corrupt. Returning."; - } + qDebug() << "UNEXPECTED: parsing of the octal code would overflow the buffer. " + "This buffer is corrupt. Returning."; return; } diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 42cbb3c38d..4ac7e22d90 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -373,7 +373,7 @@ protected: static bool countOctreeElementsOperation(OctreeElement* element, void* extraData); OctreeElement* nodeForOctalCode(OctreeElement* ancestorElement, const unsigned char* needleCode, OctreeElement** parentOfFoundElement) const; - OctreeElement* createMissingElement(OctreeElement* lastParentElement, const unsigned char* codeToReach); + OctreeElement* createMissingElement(OctreeElement* lastParentElement, const unsigned char* codeToReach, int recursionCount = 0); int readElementData(OctreeElement *destinationElement, const unsigned char* nodeData, int bufferSizeBytes, ReadBitstreamToTreeParams& args); diff --git a/libraries/octree/src/OctreeConstants.h b/libraries/octree/src/OctreeConstants.h index 4186b90888..e924676771 100644 --- a/libraries/octree/src/OctreeConstants.h +++ b/libraries/octree/src/OctreeConstants.h @@ -35,10 +35,11 @@ const int MAX_TREE_SLICE_BYTES = 26; const float VIEW_FRUSTUM_FOV_OVERSEND = 60.0f; // These are guards to prevent our voxel tree recursive routines from spinning out of control -const int UNREASONABLY_DEEP_RECURSION = 20; // use this for something that you want to be shallow, but not spin out +const int UNREASONABLY_DEEP_RECURSION = 29; // use this for something that you want to be shallow, but not spin out const int DANGEROUSLY_DEEP_RECURSION = 200; // use this for something that needs to go deeper const float SCALE_AT_UNREASONABLY_DEEP_RECURSION = (1.0f / powf(2.0f, UNREASONABLY_DEEP_RECURSION)); const float SCALE_AT_DANGEROUSLY_DEEP_RECURSION = (1.0f / powf(2.0f, DANGEROUSLY_DEEP_RECURSION)); +const float SMALLEST_REASONABLE_OCTREE_ELEMENT_SCALE = SCALE_AT_UNREASONABLY_DEEP_RECURSION * 2.0f; // 0.00006103515 meter ~1/10,0000th const int DEFAULT_MAX_OCTREE_PPS = 600; // the default maximum PPS we think any octree based server should send to a client diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index a0a331beb3..e5db8b24f8 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -16,6 +16,7 @@ #include +#include #include #include #include @@ -1159,6 +1160,10 @@ OctreeElement* OctreeElement::addChildAtIndex(int childIndex) { bool OctreeElement::safeDeepDeleteChildAtIndex(int childIndex, int recursionCount) { bool deleteApproved = false; if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { + static QString repeatedMessage + = LogHandler::getInstance().addRepeatedMessageRegex( + "OctreeElement::safeDeepDeleteChildAtIndex\\(\\) reached DANGEROUSLY_DEEP_RECURSION, bailing!"); + qDebug() << "OctreeElement::safeDeepDeleteChildAtIndex() reached DANGEROUSLY_DEEP_RECURSION, bailing!"; return deleteApproved; } diff --git a/libraries/octree/src/OctreeElement.h b/libraries/octree/src/OctreeElement.h index 31a9dfddc1..2bd5e3ae1e 100644 --- a/libraries/octree/src/OctreeElement.h +++ b/libraries/octree/src/OctreeElement.h @@ -36,8 +36,6 @@ class ReadBitstreamToTreeParams; class Shape; class VoxelSystem; -const float SMALLEST_REASONABLE_OCTREE_ELEMENT_SCALE = (1.0f / TREE_SCALE) / 10000.0f; // 1/10,000th of a meter - // Callers who want delete hook callbacks should implement this class class OctreeElementDeleteHook { public: diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 2691b10273..e6002d7c10 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -47,14 +46,6 @@ VoxelsScriptingInterface ScriptEngine::_voxelsScriptingInterface; EntityScriptingInterface ScriptEngine::_entityScriptingInterface; -static QScriptValue soundConstructor(QScriptContext* context, QScriptEngine* engine) { - QUrl soundURL = QUrl(context->argument(0).toString()); - bool isStereo = context->argument(1).toBool(); - QScriptValue soundScriptValue = engine->newQObject(new Sound(soundURL, isStereo), QScriptEngine::ScriptOwnership); - - return soundScriptValue; -} - static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){ qDebug() << "script:print()<<" << context->argument(0).toString(); QString message = context->argument(0).toString() @@ -263,10 +254,6 @@ void ScriptEngine::init() { QScriptValue printConstructorValue = newFunction(debugPrint); globalObject().setProperty("print", printConstructorValue); - QScriptValue soundConstructorValue = newFunction(soundConstructor); - QScriptValue soundMetaObject = newQMetaObject(&Sound::staticMetaObject, soundConstructorValue); - globalObject().setProperty("Sound", soundMetaObject); - QScriptValue localVoxelsValue = scriptValueFromQMetaObject(); globalObject().setProperty("LocalVoxels", localVoxelsValue); diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 5867e2ef43..02b9d5c927 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -30,6 +31,7 @@ void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, vec3toScriptValue, vec3FromScriptValue); qScriptRegisterMetaType(engine, vec2toScriptValue, vec2FromScriptValue); qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue); + qScriptRegisterMetaType(engine, qRectToScriptValue, qRectFromScriptValue); qScriptRegisterMetaType(engine, xColorToScriptValue, xColorFromScriptValue); qScriptRegisterMetaType(engine, qColorToScriptValue, qColorFromScriptValue); qScriptRegisterMetaType(engine, qURLToScriptValue, qURLFromScriptValue); @@ -96,6 +98,22 @@ void quatFromScriptValue(const QScriptValue &object, glm::quat& quat) { quat.w = object.property("w").toVariant().toFloat(); } +QScriptValue qRectToScriptValue(QScriptEngine* engine, const QRect& rect) { + QScriptValue obj = engine->newObject(); + obj.setProperty("x", rect.x()); + obj.setProperty("y", rect.y()); + obj.setProperty("width", rect.width()); + obj.setProperty("height", rect.height()); + return obj; +} + +void qRectFromScriptValue(const QScriptValue &object, QRect& rect) { + rect.setX(object.property("x").toVariant().toInt()); + rect.setY(object.property("y").toVariant().toInt()); + rect.setWidth(object.property("width").toVariant().toInt()); + rect.setHeight(object.property("height").toVariant().toInt()); +} + QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color) { QScriptValue obj = engine->newObject(); obj.setProperty("red", color.red); diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index b8884be845..0fd3138b06 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -42,6 +42,9 @@ void vec2FromScriptValue(const QScriptValue &object, glm::vec2 &vec2); QScriptValue quatToScriptValue(QScriptEngine* engine, const glm::quat& quat); void quatFromScriptValue(const QScriptValue &object, glm::quat& quat); +QScriptValue qRectToScriptValue(QScriptEngine* engine, const QRect& rect); +void qRectFromScriptValue(const QScriptValue& object, QRect& rect); + QScriptValue xColorToScriptValue(QScriptEngine* engine, const xColor& color); void xColorFromScriptValue(const QScriptValue &object, xColor& color); diff --git a/libraries/shared/src/Transform.h b/libraries/shared/src/Transform.h index 6ad106063f..3c668adcd6 100644 --- a/libraries/shared/src/Transform.h +++ b/libraries/shared/src/Transform.h @@ -20,6 +20,8 @@ #include +#include + class Transform { public: typedef glm::mat4 Mat4; @@ -30,16 +32,16 @@ public: typedef glm::quat Quat; Transform() : - _translation(0), _rotation(1.0f, 0, 0, 0), _scale(1.0f), + _translation(0), _flags(FLAG_CACHE_INVALID_BITSET) // invalid cache { } Transform(const Transform& transform) : - _translation(transform._translation), _rotation(transform._rotation), _scale(transform._scale), + _translation(transform._translation), _flags(transform._flags) { invalidCache(); @@ -49,6 +51,15 @@ public: } ~Transform() {} + Transform& operator=(const Transform& transform) { + _rotation = transform._rotation; + _scale = transform._scale; + _translation = transform._translation; + _flags = transform._flags; + invalidCache(); + return (*this); + } + void setIdentity(); const Vec3& getTranslation() const; @@ -89,7 +100,6 @@ public: // Left will be inversed before the multiplication static Transform& inverseMult(Transform& result, const Transform& left, const Transform& right); - protected: enum Flag { @@ -111,14 +121,15 @@ protected: // TRS - Vec3 _translation; Quat _rotation; Vec3 _scale; + Vec3 _translation; mutable Flags _flags; // Cached transform - mutable Mat4 _matrix; + // TODO: replace this auto ptr by a "unique ptr" as soon as we are compiling in C++11 + mutable std::auto_ptr _matrix; bool isCacheInvalid() const { return _flags[FLAG_CACHE_INVALID]; } void validCache() const { _flags.set(FLAG_CACHE_INVALID, false); } @@ -135,6 +146,7 @@ protected: void flagNonUniform() { _flags.set(FLAG_NON_UNIFORM, true); } void updateCache() const; + Mat4& getCachedMatrix(Mat4& result) const; }; inline void Transform::setIdentity() { @@ -271,8 +283,25 @@ inline void Transform::postScale(const Vec3& scale) { } inline Transform::Mat4& Transform::getMatrix(Transform::Mat4& result) const { - updateCache(); - result = _matrix; + if (isRotating()) { + Mat3 rot = glm::mat3_cast(_rotation); + + if (isScaling()) { + rot[0] *= _scale.x; + rot[1] *= _scale.y; + rot[2] *= _scale.z; + } + + result[0] = Vec4(rot[0], 0.f); + result[1] = Vec4(rot[1], 0.f); + result[2] = Vec4(rot[2], 0.f); + } else { + result[0] = Vec4(_scale.x, 0.f, 0.f, 0.f); + result[1] = Vec4(0.f, _scale.y, 0.f, 0.f); + result[2] = Vec4(0.f, 0.f, _scale.z, 0.f); + } + + result[3] = Vec4(_translation, 1.0f); return result; } @@ -369,27 +398,18 @@ inline Transform& Transform::inverseMult( Transform& result, const Transform& le return result; } +inline Transform::Mat4& Transform::getCachedMatrix(Transform::Mat4& result) const { + updateCache(); + result = (*_matrix); + return result; +} + inline void Transform::updateCache() const { if (isCacheInvalid()) { - if (isRotating()) { - Mat3 rot = glm::mat3_cast(_rotation); - - if (isScaling()) { - rot[0] *= _scale.x; - rot[1] *= _scale.y; - rot[2] *= _scale.z; - } - - _matrix[0] = Vec4(rot[0], 0.f); - _matrix[1] = Vec4(rot[1], 0.f); - _matrix[2] = Vec4(rot[2], 0.f); - } else { - _matrix[0] = Vec4(_scale.x, 0.f, 0.f, 0.f); - _matrix[1] = Vec4(0.f, _scale.y, 0.f, 0.f); - _matrix[2] = Vec4(0.f, 0.f, _scale.z, 0.f); + if (!_matrix.get()) { + _matrix.reset(new Mat4()); } - - _matrix[3] = Vec4(_translation, 1.0f); + getMatrix((*_matrix)); validCache(); } }