mirror of
https://github.com/lubosz/overte.git
synced 2025-04-27 09:55:45 +02:00
merge upstream/master into andrew/inertia
Conflicts: interface/src/avatar/MyAvatar.cpp
This commit is contained in:
commit
f2882618be
137 changed files with 5667 additions and 2118 deletions
assignment-client/src/octree
domain-server/src
examples
botProceduralWayPoints.jsbutterflies.jsdefaultScripts.jseditModels.jsglobalServicesExample.jsgracefulControls.jsheadMove.jshydraMove.jsleapOfFaith.js
interface
src
Application.cppApplication.hAudio.cppAudio.hCamera.cppGLCanvas.cppGLCanvas.hMenu.cppMenu.hMetavoxelSystem.cppModelUploader.cppModelUploader.hUserLocationsModel.cppUserLocationsModel.hUtil.cpp
avatar
devices
Faceshift.cppOculusManager.cppOculusManager.hPrioVR.cppSixenseManager.cppTV3DManager.cppTV3DManager.h
entities
EntityTreeRenderer.cppEntityTreeRenderer.hRenderableBoxEntityItem.cppRenderableModelEntityItem.cppRenderableSphereEntityItem.cpp
location
renderer
scripting
ControllerScriptingInterface.cppControllerScriptingInterface.hGlobalServicesScriptingInterface.cppGlobalServicesScriptingInterface.hLocationScriptingInterface.cppLocationScriptingInterface.hWindowScriptingInterface.cppWindowScriptingInterface.h
ui
ui
libraries
|
@ -878,10 +878,10 @@ void OctreeServer::setupDatagramProcessingThread() {
|
|||
// we do not want this event loop to be the handler for UDP datagrams, so disconnect
|
||||
disconnect(&nodeList->getNodeSocket(), 0, this, 0);
|
||||
|
||||
// setup a QThread with us as parent that will house the AudioMixerDatagramProcessor
|
||||
// setup a QThread with us as parent that will house the OctreeServerDatagramProcessor
|
||||
_datagramProcessingThread = new QThread(this);
|
||||
|
||||
// create an AudioMixerDatagramProcessor and move it to that thread
|
||||
// create an OctreeServerDatagramProcessor and move it to that thread
|
||||
OctreeServerDatagramProcessor* datagramProcessor = new OctreeServerDatagramProcessor(nodeList->getNodeSocket(), thread());
|
||||
datagramProcessor->moveToThread(_datagramProcessingThread);
|
||||
|
||||
|
|
|
@ -189,6 +189,12 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
|
|||
populateDefaultStaticAssignmentsExcludingTypes(parsedTypes);
|
||||
|
||||
LimitedNodeList* nodeList = LimitedNodeList::createInstance(domainServerPort, domainServerDTLSPort);
|
||||
|
||||
const QString DOMAIN_CONFIG_ID_KEY = "id";
|
||||
|
||||
// set our LimitedNodeList UUID to match the UUID from our config
|
||||
// nodes will currently use this to add resources to data-web that relate to our domain
|
||||
nodeList->setSessionUUID(_argumentVariantMap.value(DOMAIN_CONFIG_ID_KEY).toString());
|
||||
|
||||
connect(nodeList, &LimitedNodeList::nodeAdded, this, &DomainServer::nodeAdded);
|
||||
connect(nodeList, &LimitedNodeList::nodeKilled, this, &DomainServer::nodeKilled);
|
||||
|
|
737
examples/botProceduralWayPoints.js
Normal file
737
examples/botProceduralWayPoints.js
Normal file
|
@ -0,0 +1,737 @@
|
|||
// botProceduralWayPoints.js
|
||||
|
||||
// modified by Adrian McCarlie for worklist job hifi: #19977
|
||||
// 1 September 2014.
|
||||
// this script enables a user to define unlimited number of way points on the Domain,
|
||||
// these points form a path that the bot will follow, once the final way point is reached,
|
||||
// the bot will return to the start and continue until script is stopped.
|
||||
// pause times for each way point can be set individually.
|
||||
// User must input the x, y, z co-ords for wayPoints[] and time in milliseconds for pauseTimes[].
|
||||
//
|
||||
// original script
|
||||
// bot_procedural.js
|
||||
// hifi
|
||||
// Created by Ben Arnold on 7/29/2013
|
||||
//
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// This is an example script that demonstrates an NPC avatar.
|
||||
//
|
||||
//
|
||||
|
||||
//For procedural walk animation
|
||||
Script.include("http://s3-us-west-1.amazonaws.com/highfidelity-public/scripts/proceduralAnimationAPI.js");
|
||||
|
||||
var procAnimAPI = new ProcAnimAPI();
|
||||
|
||||
function getRandomFloat(min, max) {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
|
||||
function getRandomInt (min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
function printVector(string, vector) {
|
||||
print(string + " " + vector.x + ", " + vector.y + ", " + vector.z);
|
||||
}
|
||||
|
||||
//input co-ords for start position and 7 other positions
|
||||
|
||||
var AVATAR_PELVIS_HEIGHT = 0.84;//only change this if you have an odd size avatar
|
||||
|
||||
var wayPoints = []; //input locations for all the waypoints
|
||||
wayPoints[0] = {x:8131.5, y:202.0, z:8261.5}; //input the location of the start position
|
||||
wayPoints[1] = {x: 8160.5, y: 202.0, z: 8261.5}; //input the location of the first way point
|
||||
wayPoints[2] = {x: 8160.5, y: 203.0, z: 8270.5};
|
||||
wayPoints[3] = {x: 8142.5, y: 204.0, z: 8270.5};
|
||||
wayPoints[4] = {x: 8142.5, y: 204.0, z: 8272.5};
|
||||
wayPoints[5] = {x: 8160.5, y: 203.0, z: 8272.5};
|
||||
wayPoints[6] = {x: 8160.5, y: 202.0, z: 8284.5};
|
||||
wayPoints[7] = {x: 8111.5, y: 202.0, z: 8284.5};// continue to add locations and add or remove lines as needed.
|
||||
|
||||
var pauseTimes = []; // the number of pauseTimes must equal the number of wayPoints. Time is in milliseconds
|
||||
pauseTimes[0] = 5000; //waiting to go to wayPoint0 (startPoint)
|
||||
pauseTimes[1] = 10000; //waiting to go to wayPoint1
|
||||
pauseTimes[2] = 3000;
|
||||
pauseTimes[3] = 3000;
|
||||
pauseTimes[4] = 8000;
|
||||
pauseTimes[5] = 6000;
|
||||
pauseTimes[6] = 3000;
|
||||
pauseTimes[7] = 3000;// add or delete to match way points.
|
||||
|
||||
|
||||
wayPoints[0].y = wayPoints[0].y + AVATAR_PELVIS_HEIGHT;
|
||||
wayPoints[1].y = wayPoints[1].y + AVATAR_PELVIS_HEIGHT;
|
||||
wayPoints[2].y = wayPoints[2].y + AVATAR_PELVIS_HEIGHT;
|
||||
wayPoints[3].y = wayPoints[3].y + AVATAR_PELVIS_HEIGHT;
|
||||
wayPoints[4].y = wayPoints[4].y + AVATAR_PELVIS_HEIGHT;
|
||||
wayPoints[5].y = wayPoints[5].y + AVATAR_PELVIS_HEIGHT;
|
||||
wayPoints[6].y = wayPoints[6].y + AVATAR_PELVIS_HEIGHT;
|
||||
wayPoints[7].y = wayPoints[7].y + AVATAR_PELVIS_HEIGHT;
|
||||
|
||||
var CHANCE_OF_MOVING = 0.005;
|
||||
var CHANCE_OF_SOUND = 0.005;
|
||||
var CHANCE_OF_HEAD_TURNING = 0.01;
|
||||
var CHANCE_OF_BIG_MOVE = 1.0;
|
||||
|
||||
var isMoving = false;
|
||||
var isTurningHead = false;
|
||||
var isPlayingAudio = false;
|
||||
|
||||
|
||||
var AVATAR_PELVIS_HEIGHT = 1.84;
|
||||
var MAX_PELVIS_DELTA = 2.5;
|
||||
|
||||
var MOVE_RANGE_SMALL = 3.0;
|
||||
var MOVE_RANGE_BIG = 10.0;
|
||||
var TURN_RANGE = 70.0;
|
||||
var STOP_TOLERANCE = 0.05;
|
||||
var MOVE_RATE = 0.05;
|
||||
var TURN_RATE = 0.2;
|
||||
var HEAD_TURN_RATE = 0.05;
|
||||
var PITCH_RANGE = 15.0;
|
||||
var YAW_RANGE = 35.0;
|
||||
|
||||
var firstPosition = wayPoints[0];
|
||||
var targetPosition = { x: 0, y: 0, z: 0 };
|
||||
var targetOrientation = { x: 0, y: 0, z: 0, w: 0 };
|
||||
var currentOrientation = { x: 0, y: 0, z: 0, w: 0 };
|
||||
var targetHeadPitch = 0.0;
|
||||
var targetHeadYaw = 0.0;
|
||||
|
||||
var basePelvisHeight = 0.0;
|
||||
var pelvisOscillatorPosition = 0.0;
|
||||
var pelvisOscillatorVelocity = 0.0;
|
||||
|
||||
function clamp(val, min, max){
|
||||
return Math.max(min, Math.min(max, val))
|
||||
}
|
||||
|
||||
//Array of all valid bot numbers
|
||||
var validBotNumbers = [];
|
||||
|
||||
// right now we only use bot 63, since many other bots have messed up skeletons and LOD issues
|
||||
var botNumber = 63;//getRandomInt(0, 99);
|
||||
|
||||
var newFaceFilePrefix = "ron";
|
||||
|
||||
var newBodyFilePrefix = "bot" + botNumber;
|
||||
|
||||
// set the face model fst using the bot number
|
||||
// there is no need to change the body model - we're using the default
|
||||
Avatar.faceModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newFaceFilePrefix + ".fst";
|
||||
Avatar.skeletonModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newBodyFilePrefix + "_a.fst";
|
||||
Avatar.billboardURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/billboards/bot" + botNumber + ".png";
|
||||
|
||||
Agent.isAvatar = true;
|
||||
Agent.isListeningToAudioStream = true;
|
||||
|
||||
// change the avatar's position to wayPoints[0]
|
||||
Avatar.position = firstPosition;
|
||||
basePelvisHeight = firstPosition.y;
|
||||
printVector("New dancer, position = ", Avatar.position);
|
||||
|
||||
function loadSounds() {
|
||||
var sound_filenames = ["AB1.raw", "Anchorman2.raw", "B1.raw", "B1.raw", "Bale1.raw", "Bandcamp.raw",
|
||||
"Big1.raw", "Big2.raw", "Brian1.raw", "Buster1.raw", "CES1.raw", "CES2.raw", "CES3.raw", "CES4.raw",
|
||||
"Carrie1.raw", "Carrie3.raw", "Charlotte1.raw", "EN1.raw", "EN2.raw", "EN3.raw", "Eugene1.raw", "Francesco1.raw",
|
||||
"Italian1.raw", "Japanese1.raw", "Leigh1.raw", "Lucille1.raw", "Lucille2.raw", "MeanGirls.raw", "Murray2.raw",
|
||||
"Nigel1.raw", "PennyLane.raw", "Pitt1.raw", "Ricardo.raw", "SN.raw", "Sake1.raw", "Samantha1.raw", "Samantha2.raw",
|
||||
"Spicoli1.raw", "Supernatural.raw", "Swearengen1.raw", "TheDude.raw", "Tony.raw", "Triumph1.raw", "Uma1.raw",
|
||||
"Walken1.raw", "Walken2.raw", "Z1.raw", "Z2.raw"
|
||||
];
|
||||
|
||||
var footstep_filenames = ["FootstepW2Left-12db.wav", "FootstepW2Right-12db.wav", "FootstepW3Left-12db.wav", "FootstepW3Right-12db.wav",
|
||||
"FootstepW5Left-12db.wav", "FootstepW5Right-12db.wav"];
|
||||
|
||||
var SOUND_BASE_URL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Cocktail+Party+Snippets/Raws/";
|
||||
|
||||
var FOOTSTEP_BASE_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/Footsteps/";
|
||||
|
||||
for (var i = 0; i < sound_filenames.length; i++) {
|
||||
sounds.push(new Sound(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]));
|
||||
}
|
||||
}
|
||||
|
||||
var sounds = [];
|
||||
var footstepSounds = [];
|
||||
loadSounds();
|
||||
|
||||
|
||||
function playRandomSound() {
|
||||
if (!Agent.isPlayingAvatarSound) {
|
||||
var whichSound = Math.floor((Math.random() * sounds.length));
|
||||
Agent.playAvatarSound(sounds[whichSound]);
|
||||
}
|
||||
}
|
||||
|
||||
function playRandomFootstepSound() {
|
||||
|
||||
var whichSound = Math.floor((Math.random() * footstepSounds.length));
|
||||
var options = new AudioInjectionOptions();
|
||||
options.position = Avatar.position;
|
||||
options.volume = 1.0;
|
||||
Audio.playSound(footstepSounds[whichSound], options);
|
||||
|
||||
}
|
||||
|
||||
// Facial Animation
|
||||
var allBlendShapes = [];
|
||||
var targetBlendCoefficient = [];
|
||||
var currentBlendCoefficient = [];
|
||||
|
||||
//Blendshape constructor
|
||||
function addBlendshapeToPose(pose, shapeIndex, val) {
|
||||
var index = pose.blendShapes.length;
|
||||
pose.blendShapes[index] = {shapeIndex: shapeIndex, val: val };
|
||||
}
|
||||
//The mood of the avatar, determines face. 0 = happy, 1 = angry, 2 = sad.
|
||||
|
||||
//Randomly pick avatar mood. 80% happy, 10% mad 10% sad
|
||||
var randMood = Math.floor(Math.random() * 11);
|
||||
var avatarMood;
|
||||
if (randMood == 0) {
|
||||
avatarMood = 1;
|
||||
} else if (randMood == 2) {
|
||||
avatarMood = 2;
|
||||
} else {
|
||||
avatarMood = 0;
|
||||
}
|
||||
|
||||
var currentExpression = -1;
|
||||
//Face pose constructor
|
||||
var happyPoses = [];
|
||||
|
||||
happyPoses[0] = {blendShapes: []};
|
||||
addBlendshapeToPose(happyPoses[0], 28, 0.7); //MouthSmile_L
|
||||
addBlendshapeToPose(happyPoses[0], 29, 0.7); //MouthSmile_R
|
||||
|
||||
happyPoses[1] = {blendShapes: []};
|
||||
addBlendshapeToPose(happyPoses[1], 28, 1.0); //MouthSmile_L
|
||||
addBlendshapeToPose(happyPoses[1], 29, 1.0); //MouthSmile_R
|
||||
addBlendshapeToPose(happyPoses[1], 21, 0.2); //JawOpen
|
||||
|
||||
happyPoses[2] = {blendShapes: []};
|
||||
addBlendshapeToPose(happyPoses[2], 28, 1.0); //MouthSmile_L
|
||||
addBlendshapeToPose(happyPoses[2], 29, 1.0); //MouthSmile_R
|
||||
addBlendshapeToPose(happyPoses[2], 21, 0.5); //JawOpen
|
||||
addBlendshapeToPose(happyPoses[2], 46, 1.0); //CheekSquint_L
|
||||
addBlendshapeToPose(happyPoses[2], 47, 1.0); //CheekSquint_R
|
||||
addBlendshapeToPose(happyPoses[2], 17, 1.0); //BrowsU_L
|
||||
addBlendshapeToPose(happyPoses[2], 18, 1.0); //BrowsU_R
|
||||
|
||||
var angryPoses = [];
|
||||
|
||||
angryPoses[0] = {blendShapes: []};
|
||||
addBlendshapeToPose(angryPoses[0], 26, 0.6); //MouthFrown_L
|
||||
addBlendshapeToPose(angryPoses[0], 27, 0.6); //MouthFrown_R
|
||||
addBlendshapeToPose(angryPoses[0], 14, 0.6); //BrowsD_L
|
||||
addBlendshapeToPose(angryPoses[0], 15, 0.6); //BrowsD_R
|
||||
|
||||
angryPoses[1] = {blendShapes: []};
|
||||
addBlendshapeToPose(angryPoses[1], 26, 0.9); //MouthFrown_L
|
||||
addBlendshapeToPose(angryPoses[1], 27, 0.9); //MouthFrown_R
|
||||
addBlendshapeToPose(angryPoses[1], 14, 0.9); //BrowsD_L
|
||||
addBlendshapeToPose(angryPoses[1], 15, 0.9); //BrowsD_R
|
||||
|
||||
angryPoses[2] = {blendShapes: []};
|
||||
addBlendshapeToPose(angryPoses[2], 26, 1.0); //MouthFrown_L
|
||||
addBlendshapeToPose(angryPoses[2], 27, 1.0); //MouthFrown_R
|
||||
addBlendshapeToPose(angryPoses[2], 14, 1.0); //BrowsD_L
|
||||
addBlendshapeToPose(angryPoses[2], 15, 1.0); //BrowsD_R
|
||||
addBlendshapeToPose(angryPoses[2], 21, 0.5); //JawOpen
|
||||
addBlendshapeToPose(angryPoses[2], 46, 1.0); //CheekSquint_L
|
||||
addBlendshapeToPose(angryPoses[2], 47, 1.0); //CheekSquint_R
|
||||
|
||||
var sadPoses = [];
|
||||
|
||||
sadPoses[0] = {blendShapes: []};
|
||||
addBlendshapeToPose(sadPoses[0], 26, 0.6); //MouthFrown_L
|
||||
addBlendshapeToPose(sadPoses[0], 27, 0.6); //MouthFrown_R
|
||||
addBlendshapeToPose(sadPoses[0], 16, 0.2); //BrowsU_C
|
||||
addBlendshapeToPose(sadPoses[0], 2, 0.6); //EyeSquint_L
|
||||
addBlendshapeToPose(sadPoses[0], 3, 0.6); //EyeSquint_R
|
||||
|
||||
sadPoses[1] = {blendShapes: []};
|
||||
addBlendshapeToPose(sadPoses[1], 26, 0.9); //MouthFrown_L
|
||||
addBlendshapeToPose(sadPoses[1], 27, 0.9); //MouthFrown_R
|
||||
addBlendshapeToPose(sadPoses[1], 16, 0.6); //BrowsU_C
|
||||
addBlendshapeToPose(sadPoses[1], 2, 0.9); //EyeSquint_L
|
||||
addBlendshapeToPose(sadPoses[1], 3, 0.9); //EyeSquint_R
|
||||
|
||||
sadPoses[2] = {blendShapes: []};
|
||||
addBlendshapeToPose(sadPoses[2], 26, 1.0); //MouthFrown_L
|
||||
addBlendshapeToPose(sadPoses[2], 27, 1.0); //MouthFrown_R
|
||||
addBlendshapeToPose(sadPoses[2], 16, 0.1); //BrowsU_C
|
||||
addBlendshapeToPose(sadPoses[2], 2, 1.0); //EyeSquint_L
|
||||
addBlendshapeToPose(sadPoses[2], 3, 1.0); //EyeSquint_R
|
||||
addBlendshapeToPose(sadPoses[2], 21, 0.3); //JawOpen
|
||||
|
||||
var facePoses = [];
|
||||
facePoses[0] = happyPoses;
|
||||
facePoses[1] = angryPoses;
|
||||
facePoses[2] = sadPoses;
|
||||
|
||||
|
||||
function addBlendShape(s) {
|
||||
allBlendShapes[allBlendShapes.length] = s;
|
||||
}
|
||||
|
||||
//It is imperative that the following blendshapes are all present and are in the correct order
|
||||
addBlendShape("EyeBlink_L"); //0
|
||||
addBlendShape("EyeBlink_R"); //1
|
||||
addBlendShape("EyeSquint_L"); //2
|
||||
addBlendShape("EyeSquint_R"); //3
|
||||
addBlendShape("EyeDown_L"); //4
|
||||
addBlendShape("EyeDown_R"); //5
|
||||
addBlendShape("EyeIn_L"); //6
|
||||
addBlendShape("EyeIn_R"); //7
|
||||
addBlendShape("EyeOpen_L"); //8
|
||||
addBlendShape("EyeOpen_R"); //9
|
||||
addBlendShape("EyeOut_L"); //10
|
||||
addBlendShape("EyeOut_R"); //11
|
||||
addBlendShape("EyeUp_L"); //12
|
||||
addBlendShape("EyeUp_R"); //13
|
||||
addBlendShape("BrowsD_L"); //14
|
||||
addBlendShape("BrowsD_R"); //15
|
||||
addBlendShape("BrowsU_C"); //16
|
||||
addBlendShape("BrowsU_L"); //17
|
||||
addBlendShape("BrowsU_R"); //18
|
||||
addBlendShape("JawFwd"); //19
|
||||
addBlendShape("JawLeft"); //20
|
||||
addBlendShape("JawOpen"); //21
|
||||
addBlendShape("JawChew"); //22
|
||||
addBlendShape("JawRight"); //23
|
||||
addBlendShape("MouthLeft"); //24
|
||||
addBlendShape("MouthRight"); //25
|
||||
addBlendShape("MouthFrown_L"); //26
|
||||
addBlendShape("MouthFrown_R"); //27
|
||||
addBlendShape("MouthSmile_L"); //28
|
||||
addBlendShape("MouthSmile_R"); //29
|
||||
addBlendShape("MouthDimple_L"); //30
|
||||
addBlendShape("MouthDimple_R"); //31
|
||||
addBlendShape("LipsStretch_L"); //32
|
||||
addBlendShape("LipsStretch_R"); //33
|
||||
addBlendShape("LipsUpperClose"); //34
|
||||
addBlendShape("LipsLowerClose"); //35
|
||||
addBlendShape("LipsUpperUp"); //36
|
||||
addBlendShape("LipsLowerDown"); //37
|
||||
addBlendShape("LipsUpperOpen"); //38
|
||||
addBlendShape("LipsLowerOpen"); //39
|
||||
addBlendShape("LipsFunnel"); //40
|
||||
addBlendShape("LipsPucker"); //41
|
||||
addBlendShape("ChinLowerRaise"); //42
|
||||
addBlendShape("ChinUpperRaise"); //43
|
||||
addBlendShape("Sneer"); //44
|
||||
addBlendShape("Puff"); //45
|
||||
addBlendShape("CheekSquint_L"); //46
|
||||
addBlendShape("CheekSquint_R"); //47
|
||||
|
||||
for (var i = 0; i < allBlendShapes.length; i++) {
|
||||
targetBlendCoefficient[i] = 0;
|
||||
currentBlendCoefficient[i] = 0;
|
||||
}
|
||||
|
||||
function setRandomExpression() {
|
||||
|
||||
//Clear all expression data for current expression
|
||||
if (currentExpression != -1) {
|
||||
var expression = facePoses[avatarMood][currentExpression];
|
||||
for (var i = 0; i < expression.blendShapes.length; i++) {
|
||||
targetBlendCoefficient[expression.blendShapes[i].shapeIndex] = 0.0;
|
||||
}
|
||||
}
|
||||
//Get a new current expression
|
||||
currentExpression = Math.floor(Math.random() * facePoses[avatarMood].length);
|
||||
var expression = facePoses[avatarMood][currentExpression];
|
||||
for (var i = 0; i < expression.blendShapes.length; i++) {
|
||||
targetBlendCoefficient[expression.blendShapes[i].shapeIndex] = expression.blendShapes[i].val;
|
||||
}
|
||||
}
|
||||
|
||||
var expressionChangeSpeed = 0.1;
|
||||
function updateBlendShapes(deltaTime) {
|
||||
|
||||
for (var i = 0; i < allBlendShapes.length; i++) {
|
||||
currentBlendCoefficient[i] += (targetBlendCoefficient[i] - currentBlendCoefficient[i]) * expressionChangeSpeed;
|
||||
Avatar.setBlendshape(allBlendShapes[i], currentBlendCoefficient[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var BLINK_SPEED = 0.15;
|
||||
var CHANCE_TO_BLINK = 0.0025;
|
||||
var MAX_BLINK = 0.85;
|
||||
var blink = 0.0;
|
||||
var isBlinking = false;
|
||||
function updateBlinking(deltaTime) {
|
||||
if (isBlinking == false) {
|
||||
if (Math.random() < CHANCE_TO_BLINK) {
|
||||
isBlinking = true;
|
||||
} else {
|
||||
blink -= BLINK_SPEED;
|
||||
if (blink < 0.0) blink = 0.0;
|
||||
}
|
||||
} else {
|
||||
blink += BLINK_SPEED;
|
||||
if (blink > MAX_BLINK) {
|
||||
blink = MAX_BLINK;
|
||||
isBlinking = false;
|
||||
}
|
||||
}
|
||||
|
||||
currentBlendCoefficient[0] = blink;
|
||||
currentBlendCoefficient[1] = blink;
|
||||
targetBlendCoefficient[0] = blink;
|
||||
targetBlendCoefficient[1] = blink;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
//Procedural walk animation using two keyframes
|
||||
//We use a separate array for front and back joints
|
||||
//Pitch, yaw, and roll for the joints
|
||||
var rightAngles = [];
|
||||
var leftAngles = [];
|
||||
//for non mirrored joints such as the spine
|
||||
var middleAngles = [];
|
||||
|
||||
//Actual joint mappings
|
||||
var SHOULDER_JOINT_NUMBER = 15;
|
||||
var ELBOW_JOINT_NUMBER = 16;
|
||||
var JOINT_R_HIP = 1;
|
||||
var JOINT_R_KNEE = 2;
|
||||
var JOINT_L_HIP = 6;
|
||||
var JOINT_L_KNEE = 7;
|
||||
var JOINT_R_ARM = 15;
|
||||
var JOINT_R_FOREARM = 16;
|
||||
var JOINT_L_ARM = 39;
|
||||
var JOINT_L_FOREARM = 40;
|
||||
var JOINT_SPINE = 11;
|
||||
var JOINT_R_FOOT = 3;
|
||||
var JOINT_L_FOOT = 8;
|
||||
var JOINT_R_TOE = 4;
|
||||
var JOINT_L_TOE = 9;
|
||||
|
||||
// Animation Is Defined Below
|
||||
|
||||
var NUM_FRAMES = 2;
|
||||
for (var i = 0; i < NUM_FRAMES; i++) {
|
||||
rightAngles[i] = [];
|
||||
leftAngles[i] = [];
|
||||
middleAngles[i] = [];
|
||||
}
|
||||
//Joint order for actual joint mappings, should be interleaved R,L,R,L,...S,S,S for R = right, L = left, S = single
|
||||
var JOINT_ORDER = [];
|
||||
//*** right / left joints ***
|
||||
var HIP = 0;
|
||||
JOINT_ORDER.push(JOINT_R_HIP);
|
||||
JOINT_ORDER.push(JOINT_L_HIP);
|
||||
var KNEE = 1;
|
||||
JOINT_ORDER.push(JOINT_R_KNEE);
|
||||
JOINT_ORDER.push(JOINT_L_KNEE);
|
||||
var ARM = 2;
|
||||
JOINT_ORDER.push(JOINT_R_ARM);
|
||||
JOINT_ORDER.push(JOINT_L_ARM);
|
||||
var FOREARM = 3;
|
||||
JOINT_ORDER.push(JOINT_R_FOREARM);
|
||||
JOINT_ORDER.push(JOINT_L_FOREARM);
|
||||
var FOOT = 4;
|
||||
JOINT_ORDER.push(JOINT_R_FOOT);
|
||||
JOINT_ORDER.push(JOINT_L_FOOT);
|
||||
var TOE = 5;
|
||||
JOINT_ORDER.push(JOINT_R_TOE);
|
||||
JOINT_ORDER.push(JOINT_L_TOE);
|
||||
//*** middle joints ***
|
||||
var SPINE = 0;
|
||||
JOINT_ORDER.push(JOINT_SPINE);
|
||||
|
||||
//We have to store the angles so we can invert yaw and roll when making the animation
|
||||
//symmetrical
|
||||
|
||||
//Front refers to leg, not arm.
|
||||
//Legs Extending
|
||||
rightAngles[0][HIP] = [30.0, 0.0, 8.0];
|
||||
rightAngles[0][KNEE] = [-15.0, 0.0, 0.0];
|
||||
rightAngles[0][ARM] = [85.0, -25.0, 0.0];
|
||||
rightAngles[0][FOREARM] = [0.0, 0.0, -15.0];
|
||||
rightAngles[0][FOOT] = [0.0, 0.0, 0.0];
|
||||
rightAngles[0][TOE] = [0.0, 0.0, 0.0];
|
||||
|
||||
leftAngles[0][HIP] = [-15, 0.0, 8.0];
|
||||
leftAngles[0][KNEE] = [-26, 0.0, 0.0];
|
||||
leftAngles[0][ARM] = [85.0, 20.0, 0.0];
|
||||
leftAngles[0][FOREARM] = [10.0, 0.0, -25.0];
|
||||
leftAngles[0][FOOT] = [-13.0, 0.0, 0.0];
|
||||
leftAngles[0][TOE] = [34.0, 0.0, 0.0];
|
||||
|
||||
middleAngles[0][SPINE] = [0.0, -15.0, 5.0];
|
||||
|
||||
//Legs Passing
|
||||
rightAngles[1][HIP] = [6.0, 0.0, 8.0];
|
||||
rightAngles[1][KNEE] = [-12.0, 0.0, 0.0];
|
||||
rightAngles[1][ARM] = [85.0, 0.0, 0.0];
|
||||
rightAngles[1][FOREARM] = [0.0, 0.0, -15.0];
|
||||
rightAngles[1][FOOT] = [6.0, -8.0, 0.0];
|
||||
rightAngles[1][TOE] = [0.0, 0.0, 0.0];
|
||||
|
||||
leftAngles[1][HIP] = [10.0, 0.0, 8.0];
|
||||
leftAngles[1][KNEE] = [-60.0, 0.0, 0.0];
|
||||
leftAngles[1][ARM] = [85.0, 0.0, 0.0];
|
||||
leftAngles[1][FOREARM] = [0.0, 0.0, -15.0];
|
||||
leftAngles[1][FOOT] = [0.0, 0.0, 0.0];
|
||||
leftAngles[1][TOE] = [0.0, 0.0, 0.0];
|
||||
|
||||
middleAngles[1][SPINE] = [0.0, 0.0, 0.0];
|
||||
|
||||
//Actual keyframes for the animation
|
||||
var walkKeyFrames = procAnimAPI.generateKeyframes(rightAngles, leftAngles, middleAngles, NUM_FRAMES);
|
||||
|
||||
// Animation Is Defined Above
|
||||
|
||||
// Standing Key Frame
|
||||
//We don't have to do any mirroring or anything, since this is just a single pose.
|
||||
var rightQuats = [];
|
||||
var leftQuats = [];
|
||||
var middleQuats = [];
|
||||
|
||||
rightQuats[HIP] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 7.0);
|
||||
rightQuats[KNEE] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0);
|
||||
rightQuats[ARM] = Quat.fromPitchYawRollDegrees(85.0, 0.0, 0.0);
|
||||
rightQuats[FOREARM] = Quat.fromPitchYawRollDegrees(0.0, 0.0, -10.0);
|
||||
rightQuats[FOOT] = Quat.fromPitchYawRollDegrees(0.0, -8.0, 0.0);
|
||||
rightQuats[TOE] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0);
|
||||
|
||||
leftQuats[HIP] = Quat.fromPitchYawRollDegrees(0, 0.0, -7.0);
|
||||
leftQuats[KNEE] = Quat.fromPitchYawRollDegrees(0, 0.0, 0.0);
|
||||
leftQuats[ARM] = Quat.fromPitchYawRollDegrees(85.0, 0.0, 0.0);
|
||||
leftQuats[FOREARM] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 10.0);
|
||||
leftQuats[FOOT] = Quat.fromPitchYawRollDegrees(0.0, 8.0, 0.0);
|
||||
leftQuats[TOE] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0);
|
||||
|
||||
middleQuats[SPINE] = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0);
|
||||
|
||||
var standingKeyFrame = new procAnimAPI.KeyFrame(rightQuats, leftQuats, middleQuats);
|
||||
|
||||
//
|
||||
|
||||
var currentFrame = 0;
|
||||
|
||||
var walkTime = 0.0;
|
||||
|
||||
var walkWheelRadius = 0.5;
|
||||
var walkWheelRate = 2.0 * 3.141592 * walkWheelRadius / 8.0;
|
||||
|
||||
var avatarAcceleration = 0.75;
|
||||
var avatarVelocity = 0.0;
|
||||
var avatarMaxVelocity = 1.4;
|
||||
|
||||
function handleAnimation(deltaTime) {
|
||||
|
||||
updateBlinking(deltaTime);
|
||||
updateBlendShapes(deltaTime);
|
||||
|
||||
if (Math.random() < 0.01) {
|
||||
setRandomExpression();
|
||||
}
|
||||
|
||||
if (avatarVelocity == 0.0) {
|
||||
walkTime = 0.0;
|
||||
currentFrame = 0;
|
||||
} else {
|
||||
walkTime += avatarVelocity * deltaTime;
|
||||
if (walkTime > walkWheelRate) {
|
||||
walkTime = 0.0;
|
||||
currentFrame++;
|
||||
if (currentFrame % 2 == 1) {
|
||||
playRandomFootstepSound();
|
||||
}
|
||||
if (currentFrame > 3) {
|
||||
currentFrame = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var frame = walkKeyFrames[currentFrame];
|
||||
|
||||
var walkInterp = walkTime / walkWheelRate;
|
||||
var animInterp = avatarVelocity / (avatarMaxVelocity / 1.3);
|
||||
if (animInterp > 1.0) animInterp = 1.0;
|
||||
|
||||
for (var i = 0; i < JOINT_ORDER.length; i++) {
|
||||
var walkJoint = procAnimAPI.deCasteljau(frame.rotations[i], frame.nextFrame.rotations[i], frame.controlPoints[i][0], frame.controlPoints[i][1], walkInterp);
|
||||
var standJoint = standingKeyFrame.rotations[i];
|
||||
var finalJoint = Quat.mix(standJoint, walkJoint, animInterp);
|
||||
Avatar.setJointData(JOINT_ORDER[i], finalJoint);
|
||||
}
|
||||
}
|
||||
|
||||
function jumpWithLoudness(deltaTime) {
|
||||
// potentially change pelvis height depending on trailing average loudness
|
||||
|
||||
pelvisOscillatorVelocity += deltaTime * Agent.lastReceivedAudioLoudness * 700.0 ;
|
||||
|
||||
pelvisOscillatorVelocity -= pelvisOscillatorPosition * 0.75;
|
||||
pelvisOscillatorVelocity *= 0.97;
|
||||
pelvisOscillatorPosition += deltaTime * pelvisOscillatorVelocity;
|
||||
Avatar.headPitch = pelvisOscillatorPosition * 60.0;
|
||||
|
||||
var pelvisPosition = Avatar.position;
|
||||
pelvisPosition.y = (Y_PELVIS - 0.35) + pelvisOscillatorPosition;
|
||||
|
||||
if (pelvisPosition.y < Y_PELVIS) {
|
||||
pelvisPosition.y = Y_PELVIS;
|
||||
} else if (pelvisPosition.y > Y_PELVIS + 1.0) {
|
||||
pelvisPosition.y = Y_PELVIS + 1.0;
|
||||
}
|
||||
|
||||
Avatar.position = pelvisPosition;
|
||||
}
|
||||
|
||||
var forcedMove = false;
|
||||
|
||||
var wasMovingLastFrame = false;
|
||||
|
||||
function handleHeadTurn() {
|
||||
if (!isTurningHead && (Math.random() < CHANCE_OF_HEAD_TURNING)) {
|
||||
targetHeadPitch = getRandomFloat(-PITCH_RANGE, PITCH_RANGE);
|
||||
targetHeadYaw = getRandomFloat(-YAW_RANGE, YAW_RANGE);
|
||||
isTurningHead = true;
|
||||
} else {
|
||||
Avatar.headPitch = Avatar.headPitch + (targetHeadPitch - Avatar.headPitch) * HEAD_TURN_RATE;
|
||||
Avatar.headYaw = Avatar.headYaw + (targetHeadYaw - Avatar.headYaw) * HEAD_TURN_RATE;
|
||||
if (Math.abs(Avatar.headPitch - targetHeadPitch) < STOP_TOLERANCE &&
|
||||
Math.abs(Avatar.headYaw - targetHeadYaw) < STOP_TOLERANCE) {
|
||||
isTurningHead = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function stopWalking() {
|
||||
avatarVelocity = 0.0;
|
||||
isMoving = false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
var pauseTimer;
|
||||
|
||||
function pause(checkPoint, rotation, delay){
|
||||
|
||||
pauseTimer = Script.setTimeout(function() {
|
||||
targetPosition = checkPoint;
|
||||
targetOrientation = rotation;
|
||||
|
||||
isMoving = true;
|
||||
}, delay);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
function handleWalking(deltaTime) {
|
||||
|
||||
|
||||
if (!isMoving){
|
||||
if(targetPosition.x == 0){targetPosition = wayPoints[1]; isMoving = true;} //Start by heading for wayPoint1
|
||||
|
||||
else{
|
||||
for (var j = 0; j <= wayPoints.length; j++) {
|
||||
if (targetPosition == wayPoints[j]) {
|
||||
|
||||
if(j == wayPoints.length -1){ j= -1;}
|
||||
var k = j + 1;
|
||||
var toTarget = Vec3.normalize(Vec3.subtract(wayPoints[k], Avatar.position));
|
||||
var localVector = Vec3.multiplyQbyV(Avatar.orientation, { x: 0, y: 0, z: -1 });
|
||||
toTarget.y = 0; // I recommend doing that if you don't want weird rotation to occur that are not around Y.
|
||||
|
||||
var axis = Vec3.normalize(Vec3.cross(toTarget, localVector));
|
||||
var angle = Math.acos(Vec3.dot(toTarget, localVector)) * 180 / Math.PI;
|
||||
|
||||
if (Vec3.dot(Vec3.cross(axis, localVector), toTarget) < 0) {
|
||||
angle = -angle;
|
||||
}
|
||||
var delta = 1;
|
||||
|
||||
var quat = Quat.angleAxis(angle, axis);
|
||||
Avatar.orientation = Quat.multiply(quat, Avatar.orientation);
|
||||
|
||||
|
||||
pause(wayPoints[k], Avatar.orientation, pauseTimes[k]);
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
if (isMoving) {
|
||||
|
||||
var targetVector = Vec3.subtract(targetPosition, Avatar.position);
|
||||
var distance = Vec3.length(targetVector);
|
||||
if (distance <= avatarVelocity * deltaTime) {
|
||||
Avatar.position = targetPosition;
|
||||
stopWalking();
|
||||
} else {
|
||||
var direction = Vec3.normalize(targetVector);
|
||||
//Figure out if we should be slowing down
|
||||
var t = avatarVelocity / avatarAcceleration;
|
||||
var d = (avatarVelocity / 2.0) * t;
|
||||
if (distance < d) {
|
||||
avatarVelocity -= avatarAcceleration * deltaTime;
|
||||
if (avatarVelocity <= 0) {
|
||||
stopWalking();
|
||||
}
|
||||
} else {
|
||||
avatarVelocity += avatarAcceleration * deltaTime;
|
||||
if (avatarVelocity > avatarMaxVelocity) avatarVelocity = avatarMaxVelocity;
|
||||
}
|
||||
Avatar.position = Vec3.sum(Avatar.position, Vec3.multiply(direction, avatarVelocity * deltaTime));
|
||||
|
||||
|
||||
wasMovingLastFrame = true;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleTalking() {
|
||||
if (Math.random() < CHANCE_OF_SOUND) {
|
||||
playRandomSound();
|
||||
}
|
||||
}
|
||||
|
||||
function changePelvisHeight(newHeight) {
|
||||
var newPosition = Avatar.position;
|
||||
newPosition.y = newHeight;
|
||||
Avatar.position = newPosition;
|
||||
}
|
||||
|
||||
function updateBehavior(deltaTime) {
|
||||
|
||||
if (AvatarList.containsAvatarWithDisplayName("mrdj")) {
|
||||
if (wasMovingLastFrame) {
|
||||
isMoving = false;
|
||||
}
|
||||
|
||||
// we have a DJ, shouldn't we be dancing?
|
||||
jumpWithLoudness(deltaTime);
|
||||
} else {
|
||||
|
||||
// no DJ, let's just chill on the dancefloor - randomly walking and talking
|
||||
handleHeadTurn();
|
||||
handleAnimation(deltaTime);
|
||||
handleWalking(deltaTime);
|
||||
handleTalking();
|
||||
}
|
||||
}
|
||||
|
||||
Script.update.connect(updateBehavior);
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// butterflyFlockTest1.js
|
||||
// butterflies.js
|
||||
//
|
||||
//
|
||||
// Created by Adrian McCarlie on August 2, 2014
|
||||
|
@ -23,9 +23,6 @@ function vScalarMult(v, s) {
|
|||
return rval;
|
||||
}
|
||||
|
||||
function printVector(v) {
|
||||
print(v.x + ", " + v.y + ", " + v.z + "\n");
|
||||
}
|
||||
// Create a random vector with individual lengths between a,b
|
||||
function randVector(a, b) {
|
||||
var rval = { x: a + Math.random() * (b - a), y: a + Math.random() * (b - a), z: a + Math.random() * (b - a) };
|
||||
|
@ -40,7 +37,8 @@ function vInterpolate(a, b, fraction) {
|
|||
|
||||
var startTimeInSeconds = new Date().getTime() / 1000;
|
||||
|
||||
var lifeTime = 60; // lifetime of the butterflies in seconds!
|
||||
var NATURAL_SIZE_OF_BUTTERFLY = { x: 9.512, y: 4.427, z: 1.169 };
|
||||
var lifeTime = 600; // lifetime of the butterflies in seconds
|
||||
var range = 1.0; // Over what distance in meters do you want the flock to fly around
|
||||
var frame = 0;
|
||||
|
||||
|
@ -49,7 +47,7 @@ var BUTTERFLY_GRAVITY = 0;//-0.06;
|
|||
var BUTTERFLY_FLAP_SPEED = 1.0;
|
||||
var BUTTERFLY_VELOCITY = 0.55;
|
||||
var DISTANCE_IN_FRONT_OF_ME = 1.5;
|
||||
var DISTANCE_ABOVE_ME = 1.5;
|
||||
var DISTANCE_ABOVE_ME = 1.0;
|
||||
var flockPosition = Vec3.sum(MyAvatar.position,Vec3.sum(
|
||||
Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_ABOVE_ME),
|
||||
Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_IN_FRONT_OF_ME)));
|
||||
|
@ -81,18 +79,13 @@ function addButterfly() {
|
|||
var color = { red: 100, green: 100, blue: 100 };
|
||||
var size = 0;
|
||||
|
||||
var which = Math.random();
|
||||
if (which < 0.2) {
|
||||
size = 0.08;
|
||||
} else if (which < 0.4) {
|
||||
size = 0.09;
|
||||
} else if (which < 0.6) {
|
||||
size = 0.8;
|
||||
} else if (which < 0.8) {
|
||||
size = 0.8;
|
||||
} else {
|
||||
size = 0.8;
|
||||
}
|
||||
var minSize = 0.06;
|
||||
var randomSize = 0.2;
|
||||
var maxSize = minSize + randomSize;
|
||||
|
||||
size = 0.06 + Math.random() * 0.2;
|
||||
|
||||
var dimensions = Vec3.multiply(NATURAL_SIZE_OF_BUTTERFLY, (size / maxSize));
|
||||
|
||||
flockPosition = Vec3.sum(MyAvatar.position,Vec3.sum(
|
||||
Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_ABOVE_ME),
|
||||
|
@ -105,7 +98,7 @@ function addButterfly() {
|
|||
velocity: { x: 0, y: 0.0, z: 0 },
|
||||
gravity: { x: 0, y: 1.0, z: 0 },
|
||||
damping: 0.1,
|
||||
radius : size,
|
||||
dimensions: dimensions,
|
||||
color: color,
|
||||
rotation: rotation,
|
||||
animationURL: "http://business.ozblog.me/objects/butterfly/newButterfly2.fbx",
|
||||
|
@ -212,12 +205,13 @@ function updateButterflies(deltaTime) {
|
|||
var desiredVelocity = Vec3.subtract(butterflies[i].targetPosition, properties.position);
|
||||
desiredVelocity = vScalarMult(Vec3.normalize(desiredVelocity), BUTTERFLY_VELOCITY);
|
||||
|
||||
properties.velocity = vInterpolate(properties.velocity, desiredVelocity, 0.2);
|
||||
properties.velocity = vInterpolate(properties.velocity, desiredVelocity, 0.5);
|
||||
properties.velocity.y = holding ;
|
||||
|
||||
|
||||
// If we are near the target, we should get a new target
|
||||
if (Vec3.length(Vec3.subtract(properties.position, butterflies[i].targetPosition)) < (properties.radius / 1.0)) {
|
||||
var halfLargestDimension = Vec3.length(properties.dimensions) / 2.0;
|
||||
if (Vec3.length(Vec3.subtract(properties.position, butterflies[i].targetPosition)) < (halfLargestDimension)) {
|
||||
butterflies[i].moving = false;
|
||||
}
|
||||
|
||||
|
@ -228,7 +222,7 @@ function updateButterflies(deltaTime) {
|
|||
}
|
||||
|
||||
// Use a cosine wave offset to make it look like its flapping.
|
||||
var offset = Math.cos(nowTimeInSeconds * BUTTERFLY_FLAP_SPEED) * (properties.radius);
|
||||
var offset = Math.cos(nowTimeInSeconds * BUTTERFLY_FLAP_SPEED) * (halfLargestDimension);
|
||||
properties.position.y = properties.position.y + (offset - butterflies[i].previousFlapOffset);
|
||||
// Change position relative to previous offset.
|
||||
butterflies[i].previousFlapOffset = offset;
|
||||
|
@ -238,4 +232,11 @@ function updateButterflies(deltaTime) {
|
|||
}
|
||||
|
||||
// register the call back so it fires before each data send
|
||||
Script.update.connect(updateButterflies);
|
||||
Script.update.connect(updateButterflies);
|
||||
|
||||
// Delete our little friends if script is stopped
|
||||
Script.scriptEnding.connect(function() {
|
||||
for (var i = 0; i < numButterflies; i++) {
|
||||
Entities.deleteEntity(butterflies[i].entityID);
|
||||
}
|
||||
});
|
|
@ -13,4 +13,5 @@ Script.load("editVoxels.js");
|
|||
Script.load("editModels.js");
|
||||
Script.load("selectAudioDevice.js");
|
||||
Script.load("hydraMove.js");
|
||||
Script.load("headMove.js");
|
||||
Script.load("inspect.js");
|
||||
|
|
|
@ -40,7 +40,7 @@ var LEFT = 0;
|
|||
var RIGHT = 1;
|
||||
|
||||
var SPAWN_DISTANCE = 1;
|
||||
var DEFAULT_RADIUS = 0.10;
|
||||
var DEFAULT_DIMENSION = 0.20;
|
||||
|
||||
var modelURLs = [
|
||||
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX",
|
||||
|
@ -1147,10 +1147,10 @@ var toolBar = (function () {
|
|||
}, true, false);
|
||||
|
||||
browseModelsButton = toolBar.addTool({
|
||||
imageURL: toolIconUrl + "list-icon.png",
|
||||
imageURL: toolIconUrl + "list-icon.svg",
|
||||
width: toolWidth,
|
||||
height: toolHeight,
|
||||
alpha: 0.7,
|
||||
alpha: 0.9,
|
||||
visible: true
|
||||
});
|
||||
|
||||
|
@ -1220,7 +1220,7 @@ var toolBar = (function () {
|
|||
Entities.addEntity({
|
||||
type: "Model",
|
||||
position: position,
|
||||
radius: DEFAULT_RADIUS,
|
||||
dimensions: { x: DEFAULT_DIMENSION, y: DEFAULT_DIMENSION, z: DEFAULT_DIMENSION },
|
||||
modelURL: url
|
||||
});
|
||||
print("Model added: " + url);
|
||||
|
@ -1311,8 +1311,9 @@ var toolBar = (function () {
|
|||
Entities.addEntity({
|
||||
type: "Box",
|
||||
position: position,
|
||||
radius: DEFAULT_RADIUS,
|
||||
dimensions: { x: DEFAULT_DIMENSION, y: DEFAULT_DIMENSION, z: DEFAULT_DIMENSION },
|
||||
color: { red: 255, green: 0, blue: 0 }
|
||||
|
||||
});
|
||||
} else {
|
||||
print("Can't create box: Box would be out of bounds.");
|
||||
|
@ -1327,7 +1328,7 @@ var toolBar = (function () {
|
|||
Entities.addEntity({
|
||||
type: "Sphere",
|
||||
position: position,
|
||||
radius: DEFAULT_RADIUS,
|
||||
dimensions: { x: DEFAULT_DIMENSION, y: DEFAULT_DIMENSION, z: DEFAULT_DIMENSION },
|
||||
color: { red: 255, green: 0, blue: 0 }
|
||||
});
|
||||
} else {
|
||||
|
@ -1804,7 +1805,7 @@ function controller(wichSide) {
|
|||
this.modelURL = "";
|
||||
this.oldModelRotation;
|
||||
this.oldModelPosition;
|
||||
this.oldModelRadius;
|
||||
this.oldModelHalfDiagonal;
|
||||
|
||||
this.positionAtGrab;
|
||||
this.rotationAtGrab;
|
||||
|
@ -1864,7 +1865,7 @@ function controller(wichSide) {
|
|||
|
||||
this.oldModelPosition = properties.position;
|
||||
this.oldModelRotation = properties.rotation;
|
||||
this.oldModelRadius = properties.radius;
|
||||
this.oldModelHalfDiagonal = Vec3.length(properties.dimensions) / 2.0;
|
||||
|
||||
this.positionAtGrab = this.palmPosition;
|
||||
this.rotationAtGrab = this.rotation;
|
||||
|
@ -1873,7 +1874,7 @@ function controller(wichSide) {
|
|||
this.jointsIntersectingFromStart = [];
|
||||
for (var i = 0; i < jointList.length; i++) {
|
||||
var distance = Vec3.distance(MyAvatar.getJointPosition(jointList[i]), this.oldModelPosition);
|
||||
if (distance < this.oldModelRadius) {
|
||||
if (distance < this.oldModelHalfDiagonal) {
|
||||
this.jointsIntersectingFromStart.push(i);
|
||||
}
|
||||
}
|
||||
|
@ -1897,10 +1898,10 @@ function controller(wichSide) {
|
|||
|
||||
if (closestJointIndex != -1) {
|
||||
print("closestJoint: " + jointList[closestJointIndex]);
|
||||
print("closestJointDistance (attach max distance): " + closestJointDistance + " (" + this.oldModelRadius + ")");
|
||||
print("closestJointDistance (attach max distance): " + closestJointDistance + " (" + this.oldModelHalfDiagonal + ")");
|
||||
}
|
||||
|
||||
if (closestJointDistance < this.oldModelRadius) {
|
||||
if (closestJointDistance < this.oldModelHalfDiagonal) {
|
||||
|
||||
if (this.jointsIntersectingFromStart.indexOf(closestJointIndex) != -1 ||
|
||||
(leftController.grabbing && rightController.grabbing &&
|
||||
|
@ -1916,7 +1917,7 @@ function controller(wichSide) {
|
|||
var attachmentRotation = Quat.multiply(Quat.inverse(jointRotation), this.oldModelRotation);
|
||||
|
||||
MyAvatar.attach(this.modelURL, jointList[closestJointIndex],
|
||||
attachmentOffset, attachmentRotation, 2.0 * this.oldModelRadius,
|
||||
attachmentOffset, attachmentRotation, 2.0 * this.oldModelHalfDiagonal,
|
||||
true, false);
|
||||
Entities.deleteEntity(this.entityID);
|
||||
}
|
||||
|
@ -1970,11 +1971,12 @@ function controller(wichSide) {
|
|||
var z = Vec3.dot(Vec3.subtract(P, A), this.right);
|
||||
var X = Vec3.sum(A, Vec3.multiply(B, x));
|
||||
var d = Vec3.length(Vec3.subtract(P, X));
|
||||
var halfDiagonal = Vec3.length(properties.dimensions) / 2.0;
|
||||
|
||||
var angularSize = 2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14;
|
||||
var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14;
|
||||
if (0 < x && angularSize > MIN_ANGULAR_SIZE) {
|
||||
if (angularSize > MAX_ANGULAR_SIZE) {
|
||||
print("Angular size too big: " + 2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14);
|
||||
print("Angular size too big: " + angularSize);
|
||||
return { valid: false };
|
||||
}
|
||||
|
||||
|
@ -2021,7 +2023,10 @@ function controller(wichSide) {
|
|||
origin: this.palmPosition,
|
||||
direction: this.front
|
||||
});
|
||||
var angularSize = 2 * Math.atan(intersection.properties.radius / Vec3.distance(Camera.getPosition(), intersection.properties.position)) * 180 / 3.14;
|
||||
|
||||
var halfDiagonal = Vec3.length(intersection.properties.dimensions) / 2.0;
|
||||
|
||||
var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), intersection.properties.position)) * 180 / 3.14;
|
||||
if (intersection.accurate && intersection.entityID.isKnownID && angularSize > MIN_ANGULAR_SIZE && angularSize < MAX_ANGULAR_SIZE) {
|
||||
this.glowedIntersectingModel = intersection.entityID;
|
||||
Entities.editEntity(this.glowedIntersectingModel, { glowLevel: 0.25 });
|
||||
|
@ -2099,7 +2104,7 @@ function controller(wichSide) {
|
|||
var indicesToRemove = [];
|
||||
for (var i = 0; i < this.jointsIntersectingFromStart.length; ++i) {
|
||||
var distance = Vec3.distance(MyAvatar.getJointPosition(this.jointsIntersectingFromStart[i]), this.oldModelPosition);
|
||||
if (distance >= this.oldModelRadius) {
|
||||
if (distance >= this.oldModelHalfDiagonal) {
|
||||
indicesToRemove.push(this.jointsIntersectingFromStart[i]);
|
||||
}
|
||||
|
||||
|
@ -2192,7 +2197,12 @@ function controller(wichSide) {
|
|||
Vec3.multiplyQbyV(MyAvatar.getJointCombinedRotation(attachments[attachmentIndex].jointName), attachments[attachmentIndex].translation)),
|
||||
rotation: Quat.multiply(MyAvatar.getJointCombinedRotation(attachments[attachmentIndex].jointName),
|
||||
attachments[attachmentIndex].rotation),
|
||||
radius: attachments[attachmentIndex].scale / 2.0,
|
||||
|
||||
// TODO: how do we know the correct dimensions for detachment???
|
||||
dimensions: { x: attachments[attachmentIndex].scale / 2.0,
|
||||
y: attachments[attachmentIndex].scale / 2.0,
|
||||
z: attachments[attachmentIndex].scale / 2.0 },
|
||||
|
||||
modelURL: attachments[attachmentIndex].modelURL
|
||||
};
|
||||
|
||||
|
@ -2310,15 +2320,21 @@ function moveEntities() {
|
|||
Entities.editEntity(leftController.entityID, {
|
||||
position: newPosition,
|
||||
rotation: rotation,
|
||||
radius: leftController.oldModelRadius * ratio
|
||||
// TODO: how do we know the correct dimensions for detachment???
|
||||
//radius: leftController.oldModelHalfDiagonal * ratio
|
||||
dimensions: { x: leftController.oldModelHalfDiagonal * ratio,
|
||||
y: leftController.oldModelHalfDiagonal * ratio,
|
||||
z: leftController.oldModelHalfDiagonal * ratio }
|
||||
|
||||
|
||||
});
|
||||
leftController.oldModelPosition = newPosition;
|
||||
leftController.oldModelRotation = rotation;
|
||||
leftController.oldModelRadius *= ratio;
|
||||
leftController.oldModelHalfDiagonal *= ratio;
|
||||
|
||||
rightController.oldModelPosition = newPosition;
|
||||
rightController.oldModelRotation = rotation;
|
||||
rightController.oldModelRadius *= ratio;
|
||||
rightController.oldModelHalfDiagonal *= ratio;
|
||||
return;
|
||||
}
|
||||
leftController.moveEntity();
|
||||
|
@ -2379,7 +2395,7 @@ function Tooltip() {
|
|||
this.x = 285;
|
||||
this.y = 115;
|
||||
this.width = 500;
|
||||
this.height = 145;
|
||||
this.height = 180; // 145;
|
||||
this.margin = 5;
|
||||
this.decimals = 3;
|
||||
|
||||
|
@ -2407,7 +2423,14 @@ function Tooltip() {
|
|||
text += "Pitch: " + angles.x.toFixed(this.decimals) + "\n"
|
||||
text += "Yaw: " + angles.y.toFixed(this.decimals) + "\n"
|
||||
text += "Roll: " + angles.z.toFixed(this.decimals) + "\n"
|
||||
text += "Scale: " + 2 * properties.radius.toFixed(this.decimals) + "\n"
|
||||
text += "Dimensions: " + properties.dimensions.x.toFixed(this.decimals) + ", "
|
||||
+ properties.dimensions.y.toFixed(this.decimals) + ", "
|
||||
+ properties.dimensions.z.toFixed(this.decimals) + "\n";
|
||||
|
||||
text += "Natural Dimensions: " + properties.naturalDimensions.x.toFixed(this.decimals) + ", "
|
||||
+ properties.naturalDimensions.y.toFixed(this.decimals) + ", "
|
||||
+ properties.naturalDimensions.z.toFixed(this.decimals) + "\n";
|
||||
|
||||
text += "ID: " + properties.id + "\n"
|
||||
if (properties.type == "Model") {
|
||||
text += "Model URL: " + properties.modelURL + "\n"
|
||||
|
@ -2426,6 +2449,7 @@ function Tooltip() {
|
|||
text += "Lifetime: " + properties.lifetime + "\n"
|
||||
}
|
||||
text += "Age: " + properties.ageAsText + "\n"
|
||||
text += "Script: " + properties.script + "\n"
|
||||
|
||||
|
||||
Overlays.editOverlay(this.textOverlay, { text: text });
|
||||
|
@ -2477,7 +2501,9 @@ function mousePressEvent(event) {
|
|||
if (isLocked(properties)) {
|
||||
print("Model locked " + properties.id);
|
||||
} else {
|
||||
print("Checking properties: " + properties.id + " " + properties.isKnownID);
|
||||
var halfDiagonal = Vec3.length(properties.dimensions) / 2.0;
|
||||
|
||||
print("Checking properties: " + properties.id + " " + properties.isKnownID + " - Half Diagonal:" + halfDiagonal);
|
||||
// P P - Model
|
||||
// /| A - Palm
|
||||
// / | d B - unit vector toward tip
|
||||
|
@ -2496,8 +2522,9 @@ function mousePressEvent(event) {
|
|||
var x = Vec3.dot(Vec3.subtract(P, A), B);
|
||||
var X = Vec3.sum(A, Vec3.multiply(B, x));
|
||||
var d = Vec3.length(Vec3.subtract(P, X));
|
||||
var halfDiagonal = Vec3.length(properties.dimensions) / 2.0;
|
||||
|
||||
var angularSize = 2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14;
|
||||
var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14;
|
||||
if (0 < x && angularSize > MIN_ANGULAR_SIZE) {
|
||||
if (angularSize < MAX_ANGULAR_SIZE) {
|
||||
entitySelected = true;
|
||||
|
@ -2506,13 +2533,13 @@ function mousePressEvent(event) {
|
|||
orientation = MyAvatar.orientation;
|
||||
intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation));
|
||||
} else {
|
||||
print("Angular size too big: " + 2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14);
|
||||
print("Angular size too big: " + angularSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (entitySelected) {
|
||||
selectedEntityProperties.oldRadius = selectedEntityProperties.radius;
|
||||
selectedEntityProperties.oldDimensions = selectedEntityProperties.dimensions;
|
||||
selectedEntityProperties.oldPosition = {
|
||||
x: selectedEntityProperties.position.x,
|
||||
y: selectedEntityProperties.position.y,
|
||||
|
@ -2550,8 +2577,12 @@ function mouseMoveEvent(event) {
|
|||
glowedEntityID.id = -1;
|
||||
glowedEntityID.isKnownID = false;
|
||||
}
|
||||
|
||||
var halfDiagonal = Vec3.length(entityIntersection.properties.dimensions) / 2.0;
|
||||
|
||||
var angularSize = 2 * Math.atan(entityIntersection.properties.radius / Vec3.distance(Camera.getPosition(), entityIntersection.properties.position)) * 180 / 3.14;
|
||||
var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(),
|
||||
entityIntersection.properties.position)) * 180 / 3.14;
|
||||
|
||||
if (entityIntersection.entityID.isKnownID && angularSize > MIN_ANGULAR_SIZE && angularSize < MAX_ANGULAR_SIZE) {
|
||||
Entities.editEntity(entityIntersection.entityID, { glowLevel: 0.25 });
|
||||
glowedEntityID = entityIntersection.entityID;
|
||||
|
@ -2573,7 +2604,7 @@ function mouseMoveEvent(event) {
|
|||
}
|
||||
pickRay = Camera.computePickRay(event.x, event.y);
|
||||
if (wasShifted != event.isShifted || modifier != oldModifier) {
|
||||
selectedEntityProperties.oldRadius = selectedEntityProperties.radius;
|
||||
selectedEntityProperties.oldDimensions = selectedEntityProperties.dimensions;
|
||||
|
||||
selectedEntityProperties.oldPosition = {
|
||||
x: selectedEntityProperties.position.x,
|
||||
|
@ -2603,9 +2634,12 @@ function mouseMoveEvent(event) {
|
|||
return;
|
||||
case 1:
|
||||
// Let's Scale
|
||||
selectedEntityProperties.radius = (selectedEntityProperties.oldRadius *
|
||||
selectedEntityProperties.dimensions = Vec3.multiply(selectedEntityProperties.dimensions,
|
||||
(1.0 + (mouseLastPosition.y - event.y) / SCALE_FACTOR));
|
||||
if (selectedEntityProperties.radius < 0.01) {
|
||||
|
||||
var halfDiagonal = Vec3.length(selectedEntityProperties.dimensions) / 2.0;
|
||||
|
||||
if (halfDiagonal < 0.01) {
|
||||
print("Scale too small ... bailling.");
|
||||
return;
|
||||
}
|
||||
|
@ -2753,6 +2787,13 @@ Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
|||
|
||||
setupModelMenus();
|
||||
|
||||
var propertiesForEditedEntity;
|
||||
var editEntityFormArray;
|
||||
var editModelID = -1;
|
||||
var dimensionX;
|
||||
var dimensionY;
|
||||
var dimensionZ;
|
||||
|
||||
function handeMenuEvent(menuItem) {
|
||||
print("menuItemEvent() in JS... menuItem=" + menuItem);
|
||||
if (menuItem == "Delete") {
|
||||
|
@ -2781,7 +2822,7 @@ function handeMenuEvent(menuItem) {
|
|||
print(" Delete Entity.... not holding...");
|
||||
}
|
||||
} else if (menuItem == "Edit Properties...") {
|
||||
var editModelID = -1;
|
||||
editModelID = -1;
|
||||
if (leftController.grabbing) {
|
||||
print(" Edit Properties.... leftController.entityID="+ leftController.entityID);
|
||||
editModelID = leftController.entityID;
|
||||
|
@ -2797,79 +2838,111 @@ function handeMenuEvent(menuItem) {
|
|||
if (editModelID != -1) {
|
||||
print(" Edit Properties.... about to edit properties...");
|
||||
|
||||
var properties = Entities.getEntityProperties(editModelID);
|
||||
propertiesForEditedEntity = Entities.getEntityProperties(editModelID);
|
||||
var properties = propertiesForEditedEntity;
|
||||
|
||||
var array = new Array();
|
||||
var index = 0;
|
||||
var decimals = 3;
|
||||
if (properties.type == "Model") {
|
||||
array.push({ label: "Model URL:", value: properties.modelURL });
|
||||
index++;
|
||||
array.push({ label: "Animation URL:", value: properties.animationURL });
|
||||
index++;
|
||||
array.push({ label: "Animation is playing:", value: properties.animationIsPlaying });
|
||||
index++;
|
||||
array.push({ label: "Animation FPS:", value: properties.animationFPS });
|
||||
index++;
|
||||
array.push({ label: "Animation Frame:", value: properties.animationFrameIndex });
|
||||
index++;
|
||||
}
|
||||
array.push({ label: "Position:", type: "header" });
|
||||
index++;
|
||||
array.push({ label: "X:", value: properties.position.x.toFixed(decimals) });
|
||||
index++;
|
||||
array.push({ label: "Y:", value: properties.position.y.toFixed(decimals) });
|
||||
index++;
|
||||
array.push({ label: "Z:", value: properties.position.z.toFixed(decimals) });
|
||||
index++;
|
||||
|
||||
array.push({ label: "Registration X:", value: properties.registrationPoint.x.toFixed(decimals) });
|
||||
index++;
|
||||
array.push({ label: "Registration Y:", value: properties.registrationPoint.y.toFixed(decimals) });
|
||||
index++;
|
||||
array.push({ label: "Registration Z:", value: properties.registrationPoint.z.toFixed(decimals) });
|
||||
index++;
|
||||
|
||||
array.push({ label: "Rotation:", type: "header" });
|
||||
index++;
|
||||
var angles = Quat.safeEulerAngles(properties.rotation);
|
||||
array.push({ label: "Pitch:", value: angles.x.toFixed(decimals) });
|
||||
index++;
|
||||
array.push({ label: "Yaw:", value: angles.y.toFixed(decimals) });
|
||||
index++;
|
||||
array.push({ label: "Roll:", value: angles.z.toFixed(decimals) });
|
||||
array.push({ label: "Scale:", value: 2 * properties.radius.toFixed(decimals) });
|
||||
index++;
|
||||
|
||||
array.push({ label: "Velocity X:", value: properties.velocity.x.toFixed(decimals) });
|
||||
array.push({ label: "Velocity Y:", value: properties.velocity.y.toFixed(decimals) });
|
||||
array.push({ label: "Velocity Z:", value: properties.velocity.z.toFixed(decimals) });
|
||||
array.push({ label: "Damping:", value: properties.damping.toFixed(decimals) });
|
||||
array.push({ label: "Dimensions:", type: "header" });
|
||||
index++;
|
||||
array.push({ label: "Width:", value: properties.dimensions.x.toFixed(decimals) });
|
||||
dimensionX = index;
|
||||
index++;
|
||||
array.push({ label: "Height:", value: properties.dimensions.y.toFixed(decimals) });
|
||||
dimensionY = index;
|
||||
index++;
|
||||
array.push({ label: "Depth:", value: properties.dimensions.z.toFixed(decimals) });
|
||||
dimensionZ = index;
|
||||
index++;
|
||||
array.push({ label: "", type: "inlineButton", buttonLabel: "Reset to Natural Dimensions", name: "resetDimensions" });
|
||||
index++;
|
||||
|
||||
array.push({ label: "Velocity:", type: "header" });
|
||||
index++;
|
||||
array.push({ label: "Linear X:", value: properties.velocity.x.toFixed(decimals) });
|
||||
index++;
|
||||
array.push({ label: "Linear Y:", value: properties.velocity.y.toFixed(decimals) });
|
||||
index++;
|
||||
array.push({ label: "Linear Z:", value: properties.velocity.z.toFixed(decimals) });
|
||||
index++;
|
||||
array.push({ label: "Linear Damping:", value: properties.damping.toFixed(decimals) });
|
||||
index++;
|
||||
array.push({ label: "Angular Pitch:", value: properties.angularVelocity.x.toFixed(decimals) });
|
||||
index++;
|
||||
array.push({ label: "Angular Yaw:", value: properties.angularVelocity.y.toFixed(decimals) });
|
||||
index++;
|
||||
array.push({ label: "Angular Roll:", value: properties.angularVelocity.z.toFixed(decimals) });
|
||||
index++;
|
||||
array.push({ label: "Angular Damping:", value: properties.angularDamping.toFixed(decimals) });
|
||||
index++;
|
||||
|
||||
array.push({ label: "Gravity X:", value: properties.gravity.x.toFixed(decimals) });
|
||||
index++;
|
||||
array.push({ label: "Gravity Y:", value: properties.gravity.y.toFixed(decimals) });
|
||||
index++;
|
||||
array.push({ label: "Gravity Z:", value: properties.gravity.z.toFixed(decimals) });
|
||||
index++;
|
||||
|
||||
array.push({ label: "Lifetime:", value: properties.lifetime.toFixed(decimals) });
|
||||
index++;
|
||||
|
||||
array.push({ label: "Visible:", value: properties.visible });
|
||||
index++;
|
||||
|
||||
if (properties.type == "Box" || properties.type == "Sphere") {
|
||||
array.push({ label: "Color:", type: "header" });
|
||||
index++;
|
||||
array.push({ label: "Red:", value: properties.color.red });
|
||||
index++;
|
||||
array.push({ label: "Green:", value: properties.color.green });
|
||||
index++;
|
||||
array.push({ label: "Blue:", value: properties.color.blue });
|
||||
index++;
|
||||
}
|
||||
array.push({ button: "Cancel" });
|
||||
|
||||
if (Window.form("Edit Properties", array)) {
|
||||
var index = 0;
|
||||
if (properties.type == "Model") {
|
||||
properties.modelURL = array[index++].value;
|
||||
properties.animationURL = array[index++].value;
|
||||
properties.animationIsPlaying = array[index++].value;
|
||||
properties.animationFPS = array[index++].value;
|
||||
properties.animationFrameIndex = array[index++].value;
|
||||
}
|
||||
properties.position.x = array[index++].value;
|
||||
properties.position.y = array[index++].value;
|
||||
properties.position.z = array[index++].value;
|
||||
angles.x = array[index++].value;
|
||||
angles.y = array[index++].value;
|
||||
angles.z = array[index++].value;
|
||||
properties.rotation = Quat.fromVec3Degrees(angles);
|
||||
properties.radius = array[index++].value / 2;
|
||||
index++;
|
||||
|
||||
properties.velocity.x = array[index++].value;
|
||||
properties.velocity.y = array[index++].value;
|
||||
properties.velocity.z = array[index++].value;
|
||||
properties.damping = array[index++].value;
|
||||
properties.gravity.x = array[index++].value;
|
||||
properties.gravity.y = array[index++].value;
|
||||
properties.gravity.z = array[index++].value;
|
||||
properties.lifetime = array[index++].value; // give ourselves that many more seconds
|
||||
|
||||
if (properties.type == "Box" || properties.type == "Sphere") {
|
||||
properties.color.red = array[index++].value;
|
||||
properties.color.green = array[index++].value;
|
||||
properties.color.blue = array[index++].value;
|
||||
}
|
||||
Entities.editEntity(editModelID, properties);
|
||||
}
|
||||
modelSelected = false;
|
||||
editEntityFormArray = array;
|
||||
Window.nonBlockingForm("Edit Properties", array);
|
||||
}
|
||||
} else if (menuItem == "Paste Models") {
|
||||
modelImporter.paste();
|
||||
|
@ -2930,3 +3003,76 @@ Controller.keyReleaseEvent.connect(function (event) {
|
|||
handeMenuEvent("Delete");
|
||||
}
|
||||
});
|
||||
|
||||
Window.inlineButtonClicked.connect(function (name) {
|
||||
if (name == "resetDimensions") {
|
||||
var decimals = 3;
|
||||
Window.reloadNonBlockingForm([
|
||||
{ value: propertiesForEditedEntity.naturalDimensions.x.toFixed(decimals), oldIndex: dimensionX },
|
||||
{ value: propertiesForEditedEntity.naturalDimensions.y.toFixed(decimals), oldIndex: dimensionY },
|
||||
{ value: propertiesForEditedEntity.naturalDimensions.z.toFixed(decimals), oldIndex: dimensionZ }
|
||||
]);
|
||||
}
|
||||
});
|
||||
Window.nonBlockingFormClosed.connect(function() {
|
||||
array = editEntityFormArray;
|
||||
if (Window.getNonBlockingFormResult(array)) {
|
||||
var properties = propertiesForEditedEntity;
|
||||
var index = 0;
|
||||
if (properties.type == "Model") {
|
||||
properties.modelURL = array[index++].value;
|
||||
properties.animationURL = array[index++].value;
|
||||
properties.animationIsPlaying = array[index++].value;
|
||||
properties.animationFPS = array[index++].value;
|
||||
properties.animationFrameIndex = array[index++].value;
|
||||
}
|
||||
index++; // skip header
|
||||
properties.position.x = array[index++].value;
|
||||
properties.position.y = array[index++].value;
|
||||
properties.position.z = array[index++].value;
|
||||
properties.registrationPoint.x = array[index++].value;
|
||||
properties.registrationPoint.y = array[index++].value;
|
||||
properties.registrationPoint.z = array[index++].value;
|
||||
|
||||
index++; // skip header
|
||||
var angles = Quat.safeEulerAngles(properties.rotation);
|
||||
angles.x = array[index++].value;
|
||||
angles.y = array[index++].value;
|
||||
angles.z = array[index++].value;
|
||||
properties.rotation = Quat.fromVec3Degrees(angles);
|
||||
|
||||
index++; // skip header
|
||||
properties.dimensions.x = array[index++].value;
|
||||
properties.dimensions.y = array[index++].value;
|
||||
properties.dimensions.z = array[index++].value;
|
||||
index++; // skip reset button
|
||||
|
||||
index++; // skip header
|
||||
properties.velocity.x = array[index++].value;
|
||||
properties.velocity.y = array[index++].value;
|
||||
properties.velocity.z = array[index++].value;
|
||||
properties.damping = array[index++].value;
|
||||
|
||||
properties.angularVelocity.x = array[index++].value;
|
||||
properties.angularVelocity.y = array[index++].value;
|
||||
properties.angularVelocity.z = array[index++].value;
|
||||
properties.angularDamping = array[index++].value;
|
||||
|
||||
properties.gravity.x = array[index++].value;
|
||||
properties.gravity.y = array[index++].value;
|
||||
properties.gravity.z = array[index++].value;
|
||||
properties.lifetime = array[index++].value;
|
||||
properties.visible = array[index++].value;
|
||||
|
||||
if (properties.type == "Box" || properties.type == "Sphere") {
|
||||
index++; // skip header
|
||||
properties.color.red = array[index++].value;
|
||||
properties.color.green = array[index++].value;
|
||||
properties.color.blue = array[index++].value;
|
||||
}
|
||||
Entities.editEntity(editModelID, properties);
|
||||
}
|
||||
modelSelected = false;
|
||||
});
|
||||
|
||||
|
||||
|
|
57
examples/globalServicesExample.js
Normal file
57
examples/globalServicesExample.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
//
|
||||
// globalServicesExample.js
|
||||
// examples
|
||||
//
|
||||
// Created by Thijs Wenker on 9/12/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Example usage of the GlobalServices object. You could use it to make your own chatbox.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
function onConnected() {
|
||||
if (GlobalServices.onlineUsers.length > 0) {
|
||||
sendMessageForm()
|
||||
return;
|
||||
}
|
||||
Script.setTimeout(function() { sendMessageForm(); }, 5000);
|
||||
}
|
||||
|
||||
function onDisconnected(reason) {
|
||||
switch(reason) {
|
||||
case "logout":
|
||||
Window.alert("logged out!");
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function onOnlineUsersChanged(users) {
|
||||
print(users);
|
||||
}
|
||||
|
||||
function onIncommingMessage(user, message) {
|
||||
print(user + ": " + message);
|
||||
if (message === "hello") {
|
||||
GlobalServices.chat("hello, @" + user + "!");
|
||||
}
|
||||
}
|
||||
|
||||
function sendMessageForm() {
|
||||
var form =
|
||||
[
|
||||
{ label: "To:", options: ["(noone)"].concat(GlobalServices.onlineUsers) },
|
||||
{ label: "Message:", value: "Enter message here" }
|
||||
];
|
||||
if (Window.form("Send message on public chat", form)) {
|
||||
GlobalServices.chat(form[0].value == "(noone)" ? form[1].value : "@" + form[0].value + ", " + form[1].value);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
GlobalServices.connected.connect(onConnected);
|
||||
GlobalServices.disconnected.connect(onDisconnected);
|
||||
GlobalServices.onlineUsersChanged.connect(onOnlineUsersChanged);
|
||||
GlobalServices.incomingMessage.connect(onIncommingMessage);
|
216
examples/gracefulControls.js
Normal file
216
examples/gracefulControls.js
Normal file
|
@ -0,0 +1,216 @@
|
|||
//
|
||||
// gracefulControls.js
|
||||
// examples
|
||||
//
|
||||
// Created by Ryan Huffman on 9/11/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
|
||||
//
|
||||
|
||||
var DEFAULT_PARAMETERS = {
|
||||
// Coefficient to use for linear drag. Higher numbers will cause motion to
|
||||
// slow down more quickly.
|
||||
DRAG_COEFFICIENT: 0.9,
|
||||
MAX_SPEED: 40.0,
|
||||
ACCELERATION: 1.0,
|
||||
|
||||
MOUSE_YAW_SCALE: -0.125,
|
||||
MOUSE_PITCH_SCALE: -0.125,
|
||||
MOUSE_SENSITIVITY: 0.5,
|
||||
|
||||
// Damping frequency, adjust to change mouse look behavior
|
||||
W: 4.2,
|
||||
}
|
||||
|
||||
var BRAKE_PARAMETERS = {
|
||||
DRAG_COEFFICIENT: 4.9,
|
||||
MAX_SPEED: DEFAULT_PARAMETERS.MAX_SPEED,
|
||||
ACCELERATION: 0,
|
||||
|
||||
W: 1.0,
|
||||
MOUSE_YAW_SCALE: -0.125,
|
||||
MOUSE_PITCH_SCALE: -0.125,
|
||||
MOUSE_SENSITIVITY: 0.5,
|
||||
}
|
||||
|
||||
var movementParameters = DEFAULT_PARAMETERS;
|
||||
|
||||
// Movement keys
|
||||
var KEY_BRAKE = "q";
|
||||
var KEY_FORWARD = "w";
|
||||
var KEY_BACKWARD = "s";
|
||||
var KEY_LEFT = "a";
|
||||
var KEY_RIGHT = "d";
|
||||
var KEY_UP = "e";
|
||||
var KEY_DOWN = "c";
|
||||
var KEY_ENABLE = "SPACE";
|
||||
var CAPTURED_KEYS = [KEY_BRAKE, KEY_FORWARD, KEY_BACKWARD, KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_ENABLE];
|
||||
|
||||
// Global Variables
|
||||
var keys = {};
|
||||
var velocity = { x: 0, y: 0, z: 0 };
|
||||
var velocityVertical = 0;
|
||||
var enabled = false;
|
||||
|
||||
var lastX = Window.getCursorPositionX();
|
||||
var lastY = Window.getCursorPositionY();
|
||||
var yawFromMouse = 0;
|
||||
var pitchFromMouse = 0;
|
||||
|
||||
var yawSpeed = 0;
|
||||
var pitchSpeed = 0;
|
||||
|
||||
function keyPressEvent(event) {
|
||||
if (event.text == "ESC") {
|
||||
disable();
|
||||
} else if (event.text == KEY_ENABLE) {
|
||||
if (Window.hasFocus()) {
|
||||
enable();
|
||||
}
|
||||
} else if (event.text == KEY_BRAKE) {
|
||||
movementParameters = BRAKE_PARAMETERS;
|
||||
}
|
||||
keys[event.text] = true;
|
||||
}
|
||||
|
||||
function keyReleaseEvent(event) {
|
||||
if (event.text == KEY_BRAKE) {
|
||||
movementParameters = DEFAULT_PARAMETERS;
|
||||
}
|
||||
|
||||
delete keys[event.text];
|
||||
}
|
||||
|
||||
function update(dt) {
|
||||
var maxMove = 3.0 * dt;
|
||||
var targetVelocity = { x: 0, y: 0, z: 0 };
|
||||
var targetVelocityVertical = 0;
|
||||
var acceleration = movementParameters.ACCELERATION;
|
||||
|
||||
if (keys[KEY_FORWARD]) {
|
||||
targetVelocity.z -= acceleration * dt;
|
||||
}
|
||||
if (keys[KEY_LEFT]) {
|
||||
targetVelocity.x -= acceleration * dt;
|
||||
}
|
||||
if (keys[KEY_BACKWARD]) {
|
||||
targetVelocity.z += acceleration * dt;
|
||||
}
|
||||
if (keys[KEY_RIGHT]) {
|
||||
targetVelocity.x += acceleration * dt;
|
||||
}
|
||||
if (keys[KEY_UP]) {
|
||||
targetVelocityVertical += acceleration * dt;
|
||||
}
|
||||
if (keys[KEY_DOWN]) {
|
||||
targetVelocityVertical -= acceleration * dt;
|
||||
}
|
||||
|
||||
if (enabled && Window.hasFocus()) {
|
||||
var x = Window.getCursorPositionX();
|
||||
var y = Window.getCursorPositionY();
|
||||
|
||||
yawFromMouse += ((x - lastX) * movementParameters.MOUSE_YAW_SCALE * movementParameters.MOUSE_SENSITIVITY);
|
||||
pitchFromMouse += ((y - lastY) * movementParameters.MOUSE_PITCH_SCALE * movementParameters.MOUSE_SENSITIVITY);
|
||||
pitchFromMouse = Math.max(-180, Math.min(180, pitchFromMouse));
|
||||
|
||||
resetCursorPosition();
|
||||
}
|
||||
|
||||
// Here we use a linear damping model - http://en.wikipedia.org/wiki/Damping#Linear_damping
|
||||
// Because we are using a critically damped model (no oscillation), ζ = 1 and
|
||||
// so we derive the formula: acceleration = -(2 * w0 * v) - (w0^2 * x)
|
||||
var W = movementParameters.W;
|
||||
yawAccel = (W * W * yawFromMouse) - (2 * W * yawSpeed);
|
||||
pitchAccel = (W * W * pitchFromMouse) - (2 * W * pitchSpeed);
|
||||
|
||||
yawSpeed += yawAccel * dt;
|
||||
var yawMove = yawSpeed * dt;
|
||||
var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3Degrees( { x: 0, y: yawMove, z: 0 } ));
|
||||
MyAvatar.orientation = newOrientation;
|
||||
yawFromMouse -= yawMove;
|
||||
|
||||
pitchSpeed += pitchAccel * dt;
|
||||
var pitchMove = pitchSpeed * dt;
|
||||
var newPitch = MyAvatar.headPitch + pitchMove;
|
||||
MyAvatar.headPitch = newPitch;
|
||||
pitchFromMouse -= pitchMove;
|
||||
|
||||
// If force isn't being applied in a direction, add drag;
|
||||
if (targetVelocity.x == 0) {
|
||||
targetVelocity.x -= (velocity.x * movementParameters.DRAG_COEFFICIENT * dt);
|
||||
}
|
||||
if (targetVelocity.z == 0) {
|
||||
targetVelocity.z -= (velocity.z * movementParameters.DRAG_COEFFICIENT * dt);
|
||||
}
|
||||
velocity = Vec3.sum(velocity, targetVelocity);
|
||||
|
||||
var maxSpeed = movementParameters.MAX_SPEED;
|
||||
velocity.x = Math.max(-maxSpeed, Math.min(maxSpeed, velocity.x));
|
||||
velocity.z = Math.max(-maxSpeed, Math.min(maxSpeed, velocity.z));
|
||||
var v = Vec3.multiplyQbyV(MyAvatar.headOrientation, velocity);
|
||||
|
||||
if (targetVelocityVertical == 0) {
|
||||
targetVelocityVertical -= (velocityVertical * movementParameters.DRAG_COEFFICIENT * dt);
|
||||
}
|
||||
velocityVertical += targetVelocityVertical;
|
||||
velocityVertical = Math.max(-maxSpeed, Math.min(maxSpeed, velocityVertical));
|
||||
v.y += velocityVertical;
|
||||
|
||||
MyAvatar.setVelocity(v);
|
||||
}
|
||||
|
||||
function vecToString(vec) {
|
||||
return vec.x + ", " + vec.y + ", " + vec.z;
|
||||
}
|
||||
|
||||
function scriptEnding() {
|
||||
disable();
|
||||
}
|
||||
|
||||
function resetCursorPosition() {
|
||||
var newX = Window.x + Window.innerWidth / 2;
|
||||
var newY = Window.y + Window.innerHeight / 2;
|
||||
Window.setCursorPosition(newX, newY);
|
||||
lastX = newX;
|
||||
lastY = newY;
|
||||
}
|
||||
|
||||
function enable() {
|
||||
if (!enabled) {
|
||||
enabled = true;
|
||||
|
||||
resetCursorPosition();
|
||||
|
||||
// Reset movement variables
|
||||
yawFromMouse = 0;
|
||||
pitchFromMouse = 0;
|
||||
yawSpeed = 0;
|
||||
pitchSpeed = 0;
|
||||
velocityVertical = 0;
|
||||
|
||||
for (var i = 0; i < CAPTURED_KEYS.length; i++) {
|
||||
Controller.captureKeyEvents({ text: CAPTURED_KEYS[i] });
|
||||
}
|
||||
Window.setCursorVisible(false);
|
||||
Script.update.connect(update);
|
||||
}
|
||||
}
|
||||
|
||||
function disable() {
|
||||
if (enabled) {
|
||||
enabled = false;
|
||||
for (var i = 0; i < CAPTURED_KEYS.length; i++) {
|
||||
Controller.releaseKeyEvents({ text: CAPTURED_KEYS[i] });
|
||||
}
|
||||
Window.setCursorVisible(true);
|
||||
Script.update.disconnect(update);
|
||||
}
|
||||
}
|
||||
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||
|
||||
Script.scriptEnding.connect(scriptEnding);
|
139
examples/headMove.js
Normal file
139
examples/headMove.js
Normal file
|
@ -0,0 +1,139 @@
|
|||
//
|
||||
// headMove.js
|
||||
// examples
|
||||
//
|
||||
// Created by Philip Rosedale on September 8, 2014
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Press the spacebar and move/turn your head to move around.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var debug = false;
|
||||
|
||||
var movingWithHead = false;
|
||||
var headStartPosition, headStartDeltaPitch, headStartFinalPitch, headStartRoll, headStartYaw;
|
||||
|
||||
var HEAD_MOVE_DEAD_ZONE = 0.0;
|
||||
var HEAD_STRAFE_DEAD_ZONE = 0.0;
|
||||
var HEAD_ROTATE_DEAD_ZONE = 0.0;
|
||||
var HEAD_THRUST_FWD_SCALE = 12000.0;
|
||||
var HEAD_THRUST_STRAFE_SCALE = 2000.0;
|
||||
var HEAD_YAW_RATE = 1.0;
|
||||
var HEAD_PITCH_RATE = 1.0;
|
||||
var HEAD_ROLL_THRUST_SCALE = 75.0;
|
||||
var HEAD_PITCH_LIFT_THRUST = 3.0;
|
||||
var WALL_BOUNCE = 4000.0;
|
||||
|
||||
// If these values are set to something
|
||||
var maxVelocity = 1.25;
|
||||
var noFly = true;
|
||||
|
||||
//var roomLimits = { xMin: 618, xMax: 635.5, zMin: 528, zMax: 552.5 };
|
||||
var roomLimits = { xMin: -1, xMax: 0, zMin: 0, zMax: 0 };
|
||||
|
||||
function isInRoom(position) {
|
||||
var BUFFER = 2.0;
|
||||
if (roomLimits.xMin < 0) {
|
||||
return false;
|
||||
}
|
||||
if ((position.x > (roomLimits.xMin - BUFFER)) &&
|
||||
(position.x < (roomLimits.xMax + BUFFER)) &&
|
||||
(position.z > (roomLimits.zMin - BUFFER)) &&
|
||||
(position.z < (roomLimits.zMax + BUFFER)))
|
||||
{
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function moveWithHead(deltaTime) {
|
||||
var thrust = { x: 0, y: 0, z: 0 };
|
||||
var position = MyAvatar.position;
|
||||
if (movingWithHead) {
|
||||
var deltaYaw = MyAvatar.getHeadFinalYaw() - headStartYaw;
|
||||
var deltaPitch = MyAvatar.getHeadDeltaPitch() - headStartDeltaPitch;
|
||||
var deltaRoll = MyAvatar.getHeadFinalRoll() - headStartRoll;
|
||||
var velocity = MyAvatar.getVelocity();
|
||||
var bodyLocalCurrentHeadVector = Vec3.subtract(MyAvatar.getHeadPosition(), MyAvatar.position);
|
||||
bodyLocalCurrentHeadVector = Vec3.multiplyQbyV(Quat.angleAxis(-deltaYaw, {x:0, y: 1, z:0}), bodyLocalCurrentHeadVector);
|
||||
var headDelta = Vec3.subtract(bodyLocalCurrentHeadVector, headStartPosition);
|
||||
headDelta = Vec3.multiplyQbyV(Quat.inverse(Camera.getOrientation()), headDelta);
|
||||
headDelta.y = 0.0; // Don't respond to any of the vertical component of head motion
|
||||
|
||||
var forward = Quat.getFront(Camera.getOrientation());
|
||||
var right = Quat.getRight(Camera.getOrientation());
|
||||
var up = Quat.getUp(Camera.getOrientation());
|
||||
if (noFly) {
|
||||
forward.y = 0.0;
|
||||
forward = Vec3.normalize(forward);
|
||||
right.y = 0.0;
|
||||
right = Vec3.normalize(right);
|
||||
up = { x: 0, y: 1, z: 0};
|
||||
}
|
||||
|
||||
// Thrust based on leaning forward and side-to-side
|
||||
if (Math.abs(headDelta.z) > HEAD_MOVE_DEAD_ZONE) {
|
||||
if (Math.abs(Vec3.dot(velocity, forward)) < maxVelocity) {
|
||||
thrust = Vec3.sum(thrust, Vec3.multiply(forward, -headDelta.z * HEAD_THRUST_FWD_SCALE * deltaTime));
|
||||
}
|
||||
}
|
||||
if (Math.abs(headDelta.x) > HEAD_STRAFE_DEAD_ZONE) {
|
||||
if (Math.abs(Vec3.dot(velocity, right)) < maxVelocity) {
|
||||
thrust = Vec3.sum(thrust, Vec3.multiply(right, headDelta.x * HEAD_THRUST_STRAFE_SCALE * deltaTime));
|
||||
}
|
||||
}
|
||||
if (Math.abs(deltaYaw) > HEAD_ROTATE_DEAD_ZONE) {
|
||||
var orientation = Quat.multiply(Quat.angleAxis((deltaYaw + deltaRoll) * HEAD_YAW_RATE * deltaTime, {x:0, y: 1, z:0}), MyAvatar.orientation);
|
||||
MyAvatar.orientation = orientation;
|
||||
}
|
||||
// Thrust Up/Down based on head pitch
|
||||
if (!noFly) {
|
||||
if ((Math.abs(Vec3.dot(velocity, up)) < maxVelocity)) {
|
||||
thrust = Vec3.sum(thrust, Vec3.multiply({ x:0, y:1, z:0 }, (MyAvatar.getHeadFinalPitch() - headStartFinalPitch) * HEAD_PITCH_LIFT_THRUST * deltaTime));
|
||||
}
|
||||
}
|
||||
// For head trackers, adjust pitch by head pitch
|
||||
MyAvatar.headPitch += deltaPitch * HEAD_PITCH_RATE * deltaTime;
|
||||
|
||||
}
|
||||
if (isInRoom(position)) {
|
||||
// Impose constraints to keep you in the space
|
||||
if (position.x < roomLimits.xMin) {
|
||||
thrust.x += (roomLimits.xMin - position.x) * WALL_BOUNCE * deltaTime;
|
||||
} else if (position.x > roomLimits.xMax) {
|
||||
thrust.x += (roomLimits.xMax - position.x) * WALL_BOUNCE * deltaTime;
|
||||
}
|
||||
if (position.z < roomLimits.zMin) {
|
||||
thrust.z += (roomLimits.zMin - position.z) * WALL_BOUNCE * deltaTime;
|
||||
} else if (position.z > roomLimits.zMax) {
|
||||
thrust.z += (roomLimits.zMax - position.z) * WALL_BOUNCE * deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
// Check against movement box limits
|
||||
|
||||
MyAvatar.addThrust(thrust);
|
||||
}
|
||||
|
||||
Controller.keyPressEvent.connect(function(event) {
|
||||
if (event.text == "SPACE" && !movingWithHead) {
|
||||
movingWithHead = true;
|
||||
headStartPosition = Vec3.subtract(MyAvatar.getHeadPosition(), MyAvatar.position);
|
||||
headStartDeltaPitch = MyAvatar.getHeadDeltaPitch();
|
||||
headStartFinalPitch = MyAvatar.getHeadFinalPitch();
|
||||
headStartRoll = MyAvatar.getHeadFinalRoll();
|
||||
headStartYaw = MyAvatar.getHeadFinalYaw();
|
||||
}
|
||||
});
|
||||
Controller.keyReleaseEvent.connect(function(event) {
|
||||
if (event.text == "SPACE") {
|
||||
movingWithHead = false;
|
||||
}
|
||||
});
|
||||
|
||||
Script.update.connect(moveWithHead);
|
||||
|
|
@ -2,7 +2,9 @@
|
|||
// hydraMove.js
|
||||
// examples
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 2/10/14.
|
||||
// Created by Brad Hefta-Gaub on February 10, 2014
|
||||
// Updated by Philip Rosedale on September 8, 2014
|
||||
//
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// This is an example script that demonstrates use of the Controller and MyAvatar classes to implement
|
||||
|
@ -34,8 +36,7 @@ var grabbingWithRightHand = false;
|
|||
var wasGrabbingWithRightHand = false;
|
||||
var grabbingWithLeftHand = false;
|
||||
var wasGrabbingWithLeftHand = false;
|
||||
var movingWithHead = false;
|
||||
var headStartPosition, headStartDeltaPitch, headStartFinalPitch, headStartRoll, headStartYaw;
|
||||
|
||||
var EPSILON = 0.000001;
|
||||
var velocity = { x: 0, y: 0, z: 0};
|
||||
var THRUST_MAG_UP = 100.0;
|
||||
|
@ -243,47 +244,6 @@ function handleGrabBehavior(deltaTime) {
|
|||
wasGrabbingWithLeftHand = grabbingWithLeftHand;
|
||||
}
|
||||
|
||||
var HEAD_MOVE_DEAD_ZONE = 0.0;
|
||||
var HEAD_STRAFE_DEAD_ZONE = 0.0;
|
||||
var HEAD_ROTATE_DEAD_ZONE = 0.0;
|
||||
var HEAD_THRUST_FWD_SCALE = 12000.0;
|
||||
var HEAD_THRUST_STRAFE_SCALE = 1000.0;
|
||||
var HEAD_YAW_RATE = 2.0;
|
||||
var HEAD_PITCH_RATE = 1.0;
|
||||
var HEAD_ROLL_THRUST_SCALE = 75.0;
|
||||
var HEAD_PITCH_LIFT_THRUST = 3.0;
|
||||
|
||||
function moveWithHead(deltaTime) {
|
||||
if (movingWithHead) {
|
||||
var deltaYaw = MyAvatar.getHeadFinalYaw() - headStartYaw;
|
||||
var deltaPitch = MyAvatar.getHeadDeltaPitch() - headStartDeltaPitch;
|
||||
|
||||
var bodyLocalCurrentHeadVector = Vec3.subtract(MyAvatar.getHeadPosition(), MyAvatar.position);
|
||||
bodyLocalCurrentHeadVector = Vec3.multiplyQbyV(Quat.angleAxis(-deltaYaw, {x:0, y: 1, z:0}), bodyLocalCurrentHeadVector);
|
||||
var headDelta = Vec3.subtract(bodyLocalCurrentHeadVector, headStartPosition);
|
||||
headDelta = Vec3.multiplyQbyV(Quat.inverse(Camera.getOrientation()), headDelta);
|
||||
headDelta.y = 0.0; // Don't respond to any of the vertical component of head motion
|
||||
|
||||
// Thrust based on leaning forward and side-to-side
|
||||
if (Math.abs(headDelta.z) > HEAD_MOVE_DEAD_ZONE) {
|
||||
MyAvatar.addThrust(Vec3.multiply(Quat.getFront(Camera.getOrientation()), -headDelta.z * HEAD_THRUST_FWD_SCALE * deltaTime));
|
||||
}
|
||||
if (Math.abs(headDelta.x) > HEAD_STRAFE_DEAD_ZONE) {
|
||||
MyAvatar.addThrust(Vec3.multiply(Quat.getRight(Camera.getOrientation()), headDelta.x * HEAD_THRUST_STRAFE_SCALE * deltaTime));
|
||||
}
|
||||
if (Math.abs(deltaYaw) > HEAD_ROTATE_DEAD_ZONE) {
|
||||
var orientation = Quat.multiply(Quat.angleAxis(deltaYaw * HEAD_YAW_RATE * deltaTime, {x:0, y: 1, z:0}), MyAvatar.orientation);
|
||||
MyAvatar.orientation = orientation;
|
||||
}
|
||||
// Thrust Up/Down based on head pitch
|
||||
MyAvatar.addThrust(Vec3.multiply({ x:0, y:1, z:0 }, (MyAvatar.getHeadFinalPitch() - headStartFinalPitch) * HEAD_PITCH_LIFT_THRUST * deltaTime));
|
||||
// For head trackers, adjust pitch by head pitch
|
||||
MyAvatar.headPitch += deltaPitch * HEAD_PITCH_RATE * deltaTime;
|
||||
// Thrust strafe based on roll ange
|
||||
MyAvatar.addThrust(Vec3.multiply(Quat.getRight(Camera.getOrientation()), -(MyAvatar.getHeadFinalRoll() - headStartRoll) * HEAD_ROLL_THRUST_SCALE * deltaTime));
|
||||
}
|
||||
}
|
||||
|
||||
// Update for joysticks and move button
|
||||
function flyWithHydra(deltaTime) {
|
||||
var thrustJoystickPosition = Controller.getJoystickPosition(THRUST_CONTROLLER);
|
||||
|
@ -318,12 +278,10 @@ function flyWithHydra(deltaTime) {
|
|||
MyAvatar.orientation = Quat.multiply(orientation, deltaOrientation);
|
||||
|
||||
// change the headPitch based on our x controller
|
||||
//pitch += viewJoystickPosition.y * JOYSTICK_PITCH_MAG * deltaTime;
|
||||
var newPitch = MyAvatar.headPitch + (viewJoystickPosition.y * JOYSTICK_PITCH_MAG * deltaTime);
|
||||
MyAvatar.headPitch = newPitch;
|
||||
}
|
||||
handleGrabBehavior(deltaTime);
|
||||
moveWithHead(deltaTime);
|
||||
displayDebug();
|
||||
|
||||
}
|
||||
|
@ -340,19 +298,4 @@ function scriptEnding() {
|
|||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
||||
Controller.keyPressEvent.connect(function(event) {
|
||||
if (event.text == "SPACE" && !movingWithHead) {
|
||||
movingWithHead = true;
|
||||
headStartPosition = Vec3.subtract(MyAvatar.getHeadPosition(), MyAvatar.position);
|
||||
headStartDeltaPitch = MyAvatar.getHeadDeltaPitch();
|
||||
headStartFinalPitch = MyAvatar.getHeadFinalPitch();
|
||||
headStartRoll = MyAvatar.getHeadFinalRoll();
|
||||
headStartYaw = MyAvatar.getHeadFinalYaw();
|
||||
}
|
||||
});
|
||||
Controller.keyReleaseEvent.connect(function(event) {
|
||||
if (event.text == "SPACE") {
|
||||
movingWithHead = false;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -255,4 +255,8 @@ Script.update.connect(function(deltaTime) {
|
|||
});
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
var i;
|
||||
for (i = 0; i < jointControllers.length; i += 1) {
|
||||
Controller.releaseInputController(jointControllers[i].c);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include <QShortcut>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
#include <QWindow>
|
||||
#include <QtDebug>
|
||||
#include <QFileDialog>
|
||||
#include <QDesktopServices>
|
||||
|
@ -50,6 +51,7 @@
|
|||
#include <QMimeData>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <AddressManager.h>
|
||||
#include <AccountManager.h>
|
||||
#include <AudioInjector.h>
|
||||
#include <EntityScriptingInterface.h>
|
||||
|
@ -79,10 +81,11 @@
|
|||
#include "scripting/AccountScriptingInterface.h"
|
||||
#include "scripting/AudioDeviceScriptingInterface.h"
|
||||
#include "scripting/ClipboardScriptingInterface.h"
|
||||
#include "scripting/GlobalServicesScriptingInterface.h"
|
||||
#include "scripting/LocationScriptingInterface.h"
|
||||
#include "scripting/MenuScriptingInterface.h"
|
||||
#include "scripting/SettingsScriptingInterface.h"
|
||||
#include "scripting/WindowScriptingInterface.h"
|
||||
#include "scripting/LocationScriptingInterface.h"
|
||||
|
||||
#include "ui/InfoView.h"
|
||||
#include "ui/OAuthWebViewHandler.h"
|
||||
|
@ -288,6 +291,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
|
||||
// once the event loop has started, check and signal for an access token
|
||||
QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection);
|
||||
|
||||
AddressManager& addressManager = AddressManager::getInstance();
|
||||
|
||||
// connect to the domainChangeRequired signal on AddressManager
|
||||
connect(&addressManager, &AddressManager::possibleDomainChangeRequired,
|
||||
this, &Application::changeDomainHostname);
|
||||
|
||||
// when -url in command line, teleport to location
|
||||
addressManager.handleLookupString(getCmdOption(argc, constArgv, "-url"));
|
||||
|
||||
_settings = new QSettings(this);
|
||||
_numChangedSettings = 0;
|
||||
|
@ -357,9 +369,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
Particle::setVoxelEditPacketSender(&_voxelEditSender);
|
||||
Particle::setParticleEditPacketSender(&_particleEditSender);
|
||||
|
||||
// when -url in command line, teleport to location
|
||||
urlGoTo(argc, constArgv);
|
||||
|
||||
// For now we're going to set the PPS for outbound packets to be super high, this is
|
||||
// probably not the right long term solution. But for now, we're going to do this to
|
||||
// allow you to move a particle around in your hand
|
||||
|
@ -405,9 +414,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
connect(_window, &MainWindow::windowGeometryChanged,
|
||||
_runningScriptsWidget, &RunningScriptsWidget::setBoundary);
|
||||
|
||||
//When -url in command line, teleport to location
|
||||
urlGoTo(argc, constArgv);
|
||||
|
||||
// call the OAuthWebviewHandler static getter so that its instance lives in our thread
|
||||
OAuthWebViewHandler::getInstance();
|
||||
// make sure the High Fidelity root CA is in our list of trusted certs
|
||||
|
@ -805,13 +811,16 @@ bool Application::event(QEvent* event) {
|
|||
|
||||
// handle custom URL
|
||||
if (event->type() == QEvent::FileOpen) {
|
||||
|
||||
QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event);
|
||||
bool isHifiSchemeURL = !fileEvent->url().isEmpty() && fileEvent->url().toLocalFile().startsWith(CUSTOM_URL_SCHEME);
|
||||
if (isHifiSchemeURL) {
|
||||
Menu::getInstance()->goToURL(fileEvent->url().toLocalFile());
|
||||
|
||||
if (!fileEvent->url().isEmpty()) {
|
||||
AddressManager::getInstance().handleLookupString(fileEvent->url().toLocalFile());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return QApplication::event(event);
|
||||
}
|
||||
|
||||
|
@ -904,12 +913,19 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
break;
|
||||
|
||||
case Qt::Key_D:
|
||||
_myAvatar->setDriveKeys(ROT_RIGHT, 1.f);
|
||||
if (!isMeta) {
|
||||
_myAvatar->setDriveKeys(ROT_RIGHT, 1.f);
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_Return:
|
||||
case Qt::Key_Enter:
|
||||
Menu::getInstance()->triggerOption(MenuOption::Chat);
|
||||
if (isMeta) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::AddressBar);
|
||||
} else {
|
||||
Menu::getInstance()->triggerOption(MenuOption::Chat);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Qt::Key_Up:
|
||||
|
@ -1056,10 +1072,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
case Qt::Key_Equal:
|
||||
_myAvatar->resetSize();
|
||||
break;
|
||||
|
||||
case Qt::Key_At:
|
||||
Menu::getInstance()->goTo();
|
||||
break;
|
||||
default:
|
||||
event->ignore();
|
||||
break;
|
||||
|
@ -1072,7 +1084,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
|
|||
_keysPressed.remove(event->key());
|
||||
|
||||
_controllerScriptingInterface.emitKeyReleaseEvent(event); // send events to any registered scripts
|
||||
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
if (_controllerScriptingInterface.isKeyCaptured(event)) {
|
||||
return;
|
||||
|
@ -1124,7 +1136,12 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
|
|||
_myAvatar->setDriveKeys(RIGHT, 0.f);
|
||||
_myAvatar->setDriveKeys(ROT_RIGHT, 0.f);
|
||||
break;
|
||||
|
||||
case Qt::Key_Control:
|
||||
case Qt::Key_Shift:
|
||||
case Qt::Key_Meta:
|
||||
case Qt::Key_Alt:
|
||||
_myAvatar->clearDriveKeys();
|
||||
break;
|
||||
default:
|
||||
event->ignore();
|
||||
break;
|
||||
|
@ -1152,8 +1169,7 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
showMouse = false;
|
||||
}
|
||||
|
||||
QMouseEvent deviceEvent = getDeviceEvent(event, deviceID);
|
||||
_controllerScriptingInterface.emitMouseMoveEvent(&deviceEvent, deviceID); // send events to any registered scripts
|
||||
_controllerScriptingInterface.emitMouseMoveEvent(event, deviceID); // send events to any registered scripts
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
if (_controllerScriptingInterface.isMouseCaptured()) {
|
||||
|
@ -1168,13 +1184,12 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
_seenMouseMove = true;
|
||||
}
|
||||
|
||||
_mouseX = deviceEvent.x();
|
||||
_mouseY = deviceEvent.y();
|
||||
_mouseX = event->x();
|
||||
_mouseY = event->y();
|
||||
}
|
||||
|
||||
void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||
QMouseEvent deviceEvent = getDeviceEvent(event, deviceID);
|
||||
_controllerScriptingInterface.emitMousePressEvent(&deviceEvent); // send events to any registered scripts
|
||||
_controllerScriptingInterface.emitMousePressEvent(event); // send events to any registered scripts
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
if (_controllerScriptingInterface.isMouseCaptured()) {
|
||||
|
@ -1184,8 +1199,8 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
|
||||
if (activeWindow() == _window) {
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
_mouseX = deviceEvent.x();
|
||||
_mouseY = deviceEvent.y();
|
||||
_mouseX = event->x();
|
||||
_mouseY = event->y();
|
||||
_mouseDragStartedX = _mouseX;
|
||||
_mouseDragStartedY = _mouseY;
|
||||
_mousePressed = true;
|
||||
|
@ -1207,8 +1222,7 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
}
|
||||
|
||||
void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||
QMouseEvent deviceEvent = getDeviceEvent(event, deviceID);
|
||||
_controllerScriptingInterface.emitMouseReleaseEvent(&deviceEvent); // send events to any registered scripts
|
||||
_controllerScriptingInterface.emitMouseReleaseEvent(event); // send events to any registered scripts
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
if (_controllerScriptingInterface.isMouseCaptured()) {
|
||||
|
@ -1217,8 +1231,8 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
|
||||
if (activeWindow() == _window) {
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
_mouseX = deviceEvent.x();
|
||||
_mouseY = deviceEvent.y();
|
||||
_mouseX = event->x();
|
||||
_mouseY = event->y();
|
||||
_mousePressed = false;
|
||||
checkBandwidthMeterClick();
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
|
||||
|
@ -1316,7 +1330,7 @@ void Application::dropEvent(QDropEvent *event) {
|
|||
SnapshotMetaData* snapshotData = Snapshot::parseSnapshotData(snapshotPath);
|
||||
if (snapshotData) {
|
||||
if (!snapshotData->getDomain().isEmpty()) {
|
||||
Menu::getInstance()->goToDomain(snapshotData->getDomain());
|
||||
changeDomainHostname(snapshotData->getDomain());
|
||||
}
|
||||
|
||||
_myAvatar->setPosition(snapshotData->getLocation());
|
||||
|
@ -1417,7 +1431,7 @@ void Application::checkBandwidthMeterClick() {
|
|||
if (Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth) &&
|
||||
glm::compMax(glm::abs(glm::ivec2(_mouseX - _mouseDragStartedX, _mouseY - _mouseDragStartedY)))
|
||||
<= BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH
|
||||
&& _bandwidthMeter.isWithinArea(_mouseX, _mouseY, _glWidget->getDeviceWidth(), _glWidget->getDeviceHeight())) {
|
||||
&& _bandwidthMeter.isWithinArea(_mouseX, _mouseY, _glWidget->width(), _glWidget->height())) {
|
||||
|
||||
// The bandwidth meter is visible, the click didn't get dragged too far and
|
||||
// we actually hit the bandwidth meter
|
||||
|
@ -1735,8 +1749,8 @@ void Application::init() {
|
|||
_voxelShader.init();
|
||||
_pointShader.init();
|
||||
|
||||
_mouseX = _glWidget->getDeviceWidth() / 2;
|
||||
_mouseY = _glWidget->getDeviceHeight() / 2;
|
||||
_mouseX = _glWidget->width() / 2;
|
||||
_mouseY = _glWidget->height() / 2;
|
||||
QCursor::setPos(_mouseX, _mouseY);
|
||||
|
||||
// TODO: move _myAvatar out of Application. Move relevant code to MyAvataar or AvatarManager
|
||||
|
@ -1891,8 +1905,8 @@ void Application::updateMouseRay() {
|
|||
// if the mouse pointer isn't visible, act like it's at the center of the screen
|
||||
float x = 0.5f, y = 0.5f;
|
||||
if (!_mouseHidden) {
|
||||
x = _mouseX / (float)_glWidget->getDeviceWidth();
|
||||
y = _mouseY / (float)_glWidget->getDeviceHeight();
|
||||
x = _mouseX / (float)_glWidget->width();
|
||||
y = _mouseY / (float)_glWidget->height();
|
||||
}
|
||||
_viewFrustum.computePickRay(x, y, _mouseRayOrigin, _mouseRayDirection);
|
||||
|
||||
|
@ -2332,14 +2346,6 @@ int Application::sendNackPackets() {
|
|||
return packetsSent;
|
||||
}
|
||||
|
||||
QMouseEvent Application::getDeviceEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||
if (deviceID > 0) {
|
||||
return *event;
|
||||
}
|
||||
return QMouseEvent(event->type(), QPointF(_glWidget->getDeviceX(event->x()), _glWidget->getDeviceY(event->y())),
|
||||
event->windowPos(), event->screenPos(), event->button(), event->buttons(), event->modifiers());
|
||||
}
|
||||
|
||||
void Application::queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions) {
|
||||
|
||||
//qDebug() << ">>> inside... queryOctree()... _viewFrustum.getFieldOfView()=" << _viewFrustum.getFieldOfView();
|
||||
|
@ -2734,6 +2740,9 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()");
|
||||
// transform by eye offset
|
||||
|
||||
// load the view frustum
|
||||
loadViewFrustum(whichCamera, _displayViewFrustum);
|
||||
|
||||
// flip x if in mirror mode (also requires reversing winding order for backface culling)
|
||||
if (whichCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
glScalef(-1.0f, 1.0f, 1.0f);
|
||||
|
@ -2981,7 +2990,14 @@ void Application::getProjectionMatrix(glm::dmat4* projectionMatrix) {
|
|||
void Application::computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal,
|
||||
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const {
|
||||
|
||||
// allow 3DTV/Oculus to override parameters from camera
|
||||
_viewFrustum.computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
|
||||
if (OculusManager::isConnected()) {
|
||||
OculusManager::overrideOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
|
||||
|
||||
} else if (TV3DManager::isConnected()) {
|
||||
TV3DManager::overrideOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) {
|
||||
|
@ -3041,8 +3057,16 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
|
|||
_mirrorCamera.update(1.0f/_fps);
|
||||
|
||||
// set the bounds of rear mirror view
|
||||
glViewport(region.x(), _glWidget->getDeviceHeight() - region.y() - region.height(), region.width(), region.height());
|
||||
glScissor(region.x(), _glWidget->getDeviceHeight() - region.y() - region.height(), region.width(), region.height());
|
||||
if (billboard) {
|
||||
glViewport(region.x(), _glWidget->getDeviceHeight() - region.y() - region.height(), region.width(), region.height());
|
||||
glScissor(region.x(), _glWidget->getDeviceHeight() - region.y() - region.height(), region.width(), region.height());
|
||||
} else {
|
||||
// if not rendering the billboard, the region is in device independent coordinates; must convert to device
|
||||
float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio();
|
||||
int x = region.x() * ratio, y = region.y() * ratio, width = region.width() * ratio, height = region.height() * ratio;
|
||||
glViewport(x, _glWidget->getDeviceHeight() - y - height, width, height);
|
||||
glScissor(x, _glWidget->getDeviceHeight() - y - height, width, height);
|
||||
}
|
||||
bool updateViewFrustum = false;
|
||||
updateProjectionMatrix(_mirrorCamera, updateViewFrustum);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
|
@ -3291,8 +3315,8 @@ void Application::deleteVoxelAt(const VoxelDetail& voxel) {
|
|||
|
||||
|
||||
void Application::resetSensors() {
|
||||
_mouseX = _glWidget->getDeviceWidth() / 2;
|
||||
_mouseY = _glWidget->getDeviceHeight() / 2;
|
||||
_mouseX = _glWidget->width() / 2;
|
||||
_mouseY = _glWidget->height() / 2;
|
||||
|
||||
_faceplus.reset();
|
||||
_faceshift.reset();
|
||||
|
@ -3359,31 +3383,56 @@ void Application::updateWindowTitle(){
|
|||
title += " - ₵" + creditBalanceString;
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
// crashes with vs2013/win32
|
||||
qDebug("Application title set to: %s", title.toStdString().c_str());
|
||||
#endif
|
||||
_window->setWindowTitle(title);
|
||||
}
|
||||
|
||||
void Application::updateLocationInServer() {
|
||||
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
||||
if (accountManager.isLoggedIn()) {
|
||||
const QUuid& domainUUID = NodeList::getInstance()->getDomainHandler().getUUID();
|
||||
|
||||
if (accountManager.isLoggedIn() && !domainUUID.isNull()) {
|
||||
|
||||
// construct a QJsonObject given the user's current address information
|
||||
QJsonObject updatedLocationObject;
|
||||
QJsonObject rootObject;
|
||||
|
||||
QJsonObject addressObject;
|
||||
addressObject.insert("position", QString(createByteArray(_myAvatar->getPosition())));
|
||||
addressObject.insert("orientation", QString(createByteArray(glm::degrees(safeEulerAngles(_myAvatar->getOrientation())))));
|
||||
addressObject.insert("domain", NodeList::getInstance()->getDomainHandler().getHostname());
|
||||
QJsonObject locationObject;
|
||||
|
||||
QString pathString = AddressManager::pathForPositionAndOrientation(_myAvatar->getPosition(),
|
||||
true,
|
||||
_myAvatar->getOrientation());
|
||||
|
||||
const QString LOCATION_KEY_IN_ROOT = "location";
|
||||
const QString PATH_KEY_IN_LOCATION = "path";
|
||||
const QString DOMAIN_ID_KEY_IN_LOCATION = "domain_id";
|
||||
|
||||
locationObject.insert(PATH_KEY_IN_LOCATION, pathString);
|
||||
locationObject.insert(DOMAIN_ID_KEY_IN_LOCATION, domainUUID.toString());
|
||||
|
||||
updatedLocationObject.insert("address", addressObject);
|
||||
rootObject.insert(LOCATION_KEY_IN_ROOT, locationObject);
|
||||
|
||||
accountManager.authenticatedRequest("/api/v1/users/address", QNetworkAccessManager::PutOperation,
|
||||
JSONCallbackParameters(), QJsonDocument(updatedLocationObject).toJson());
|
||||
accountManager.authenticatedRequest("/api/v1/users/location", QNetworkAccessManager::PutOperation,
|
||||
JSONCallbackParameters(), QJsonDocument(rootObject).toJson());
|
||||
}
|
||||
}
|
||||
|
||||
void Application::changeDomainHostname(const QString &newDomainHostname) {
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
if (!nodeList->getDomainHandler().isCurrentHostname(newDomainHostname)) {
|
||||
// tell the MyAvatar object to send a kill packet so that it dissapears from its old avatar mixer immediately
|
||||
_myAvatar->sendKillAvatar();
|
||||
|
||||
// call the domain hostname change as a queued connection on the nodelist
|
||||
QMetaObject::invokeMethod(&NodeList::getInstance()->getDomainHandler(), "setHostname",
|
||||
Q_ARG(const QString&, newDomainHostname));
|
||||
}
|
||||
}
|
||||
|
||||
void Application::domainChanged(const QString& domainHostname) {
|
||||
updateWindowTitle();
|
||||
|
||||
|
@ -3767,12 +3816,11 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser
|
|||
|
||||
QScriptValue windowValue = scriptEngine->registerGlobalObject("Window", WindowScriptingInterface::getInstance());
|
||||
scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
|
||||
LocationScriptingInterface::locationSetter, windowValue);
|
||||
|
||||
LocationScriptingInterface::locationSetter, windowValue);
|
||||
// register `location` on the global object.
|
||||
scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
|
||||
LocationScriptingInterface::locationSetter);
|
||||
|
||||
LocationScriptingInterface::locationSetter);
|
||||
|
||||
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance());
|
||||
|
@ -3780,9 +3828,11 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser
|
|||
scriptEngine->registerGlobalObject("AudioReflector", &_audioReflector);
|
||||
scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("Metavoxels", &_metavoxels);
|
||||
|
||||
|
||||
scriptEngine->registerGlobalObject("GlobalServices", GlobalServicesScriptingInterface::getInstance());
|
||||
|
||||
scriptEngine->registerGlobalObject("AvatarManager", &_avatarManager);
|
||||
|
||||
|
||||
#ifdef HAVE_RTMIDI
|
||||
scriptEngine->registerGlobalObject("MIDI", &MIDIManager::getInstance());
|
||||
#endif
|
||||
|
@ -3900,6 +3950,17 @@ void Application::uploadAttachment() {
|
|||
uploadModel(ATTACHMENT_MODEL);
|
||||
}
|
||||
|
||||
void Application::openUrl(const QUrl& url) {
|
||||
if (!url.isEmpty()) {
|
||||
if (url.scheme() == HIFI_URL_SCHEME) {
|
||||
AddressManager::getInstance().handleLookupString(url.toString());
|
||||
} else {
|
||||
// address manager did not handle - ask QDesktopServices to handle
|
||||
QDesktopServices::openUrl(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject) {
|
||||
|
||||
// from the domain-handler, figure out the satoshi cost per voxel and per meter cubed
|
||||
|
@ -4070,6 +4131,14 @@ void Application::skipVersion(QString latestVersion) {
|
|||
skipFile.write(latestVersion.toStdString().c_str());
|
||||
}
|
||||
|
||||
void Application::setCursorVisible(bool visible) {
|
||||
if (visible) {
|
||||
restoreOverrideCursor();
|
||||
} else {
|
||||
setOverrideCursor(Qt::BlankCursor);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::takeSnapshot() {
|
||||
QMediaPlayer* player = new QMediaPlayer();
|
||||
QFileInfo inf = QFileInfo(Application::resourcesPath() + "sounds/snap.wav");
|
||||
|
@ -4088,37 +4157,3 @@ void Application::takeSnapshot() {
|
|||
}
|
||||
_snapshotShareDialog->show();
|
||||
}
|
||||
|
||||
void Application::urlGoTo(int argc, const char * constArgv[]) {
|
||||
//Gets the url (hifi://domain/destination/orientation)
|
||||
QString customUrl = getCmdOption(argc, constArgv, "-url");
|
||||
if(customUrl.startsWith(CUSTOM_URL_SCHEME + "//")) {
|
||||
QStringList urlParts = customUrl.remove(0, CUSTOM_URL_SCHEME.length() + 2).split('/', QString::SkipEmptyParts);
|
||||
if (urlParts.count() == 1) {
|
||||
// location coordinates or place name
|
||||
QString domain = urlParts[0];
|
||||
Menu::goToDomain(domain);
|
||||
} else if (urlParts.count() > 1) {
|
||||
// if url has 2 or more parts, the first one is domain name
|
||||
QString domain = urlParts[0];
|
||||
|
||||
// second part is either a destination coordinate or
|
||||
// a place name
|
||||
QString destination = urlParts[1];
|
||||
|
||||
// any third part is an avatar orientation.
|
||||
QString orientation = urlParts.count() > 2 ? urlParts[2] : QString();
|
||||
|
||||
Menu::goToDomain(domain);
|
||||
|
||||
// goto either @user, #place, or x-xx,y-yy,z-zz
|
||||
// style co-ordinate.
|
||||
Menu::goTo(destination);
|
||||
|
||||
if (!orientation.isEmpty()) {
|
||||
// location orientation
|
||||
Menu::goToOrientation(orientation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,7 +114,6 @@ static const float NODE_KILLED_GREEN = 0.0f;
|
|||
static const float NODE_KILLED_BLUE = 0.0f;
|
||||
|
||||
static const QString SNAPSHOT_EXTENSION = ".jpg";
|
||||
static const QString CUSTOM_URL_SCHEME = "hifi:";
|
||||
|
||||
static const float BILLBOARD_FIELD_OF_VIEW = 30.0f; // degrees
|
||||
static const float BILLBOARD_DISTANCE = 5.0f; // meters
|
||||
|
@ -153,7 +152,6 @@ public:
|
|||
void initializeGL();
|
||||
void paintGL();
|
||||
void resizeGL(int width, int height);
|
||||
void urlGoTo(int argc, const char * constArgv[]);
|
||||
|
||||
void keyPressEvent(QKeyEvent* event);
|
||||
void keyReleaseEvent(QKeyEvent* event);
|
||||
|
@ -192,6 +190,7 @@ public:
|
|||
const AudioReflector* getAudioReflector() const { return &_audioReflector; }
|
||||
Camera* getCamera() { return &_myCamera; }
|
||||
ViewFrustum* getViewFrustum() { return &_viewFrustum; }
|
||||
ViewFrustum* getDisplayViewFrustum() { return &_displayViewFrustum; }
|
||||
ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; }
|
||||
VoxelImporter* getVoxelImporter() { return &_voxelImporter; }
|
||||
VoxelSystem* getVoxels() { return &_voxels; }
|
||||
|
@ -298,6 +297,8 @@ public:
|
|||
QStringList getRunningScripts() { return _scriptEnginesHash.keys(); }
|
||||
ScriptEngine* getScriptEngine(QString scriptHash) { return _scriptEnginesHash.contains(scriptHash) ? _scriptEnginesHash[scriptHash] : NULL; }
|
||||
|
||||
void setCursorVisible(bool visible);
|
||||
|
||||
signals:
|
||||
|
||||
/// Fired when we're simulating; allows external parties to hook in.
|
||||
|
@ -313,6 +314,7 @@ signals:
|
|||
void importDone();
|
||||
|
||||
public slots:
|
||||
void changeDomainHostname(const QString& newDomainHostname);
|
||||
void domainChanged(const QString& domainHostname);
|
||||
void updateWindowTitle();
|
||||
void updateLocationInServer();
|
||||
|
@ -352,6 +354,8 @@ public slots:
|
|||
void uploadHead();
|
||||
void uploadSkeleton();
|
||||
void uploadAttachment();
|
||||
|
||||
void openUrl(const QUrl& url);
|
||||
|
||||
void bumpSettings() { ++_numChangedSettings; }
|
||||
|
||||
|
@ -436,8 +440,6 @@ private:
|
|||
|
||||
int sendNackPackets();
|
||||
|
||||
QMouseEvent getDeviceEvent(QMouseEvent* event, unsigned int deviceID);
|
||||
|
||||
MainWindow* _window;
|
||||
GLCanvas* _glWidget; // our GLCanvas has a couple extra features
|
||||
|
||||
|
@ -488,6 +490,7 @@ private:
|
|||
|
||||
ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc.
|
||||
ViewFrustum _lastQueriedViewFrustum; /// last view frustum used to query octree servers (voxels, particles)
|
||||
ViewFrustum _displayViewFrustum;
|
||||
ViewFrustum _shadowViewFrustum;
|
||||
quint64 _lastQueriedTime;
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include <glm/glm.hpp>
|
||||
|
||||
#include "Audio.h"
|
||||
|
||||
#include "Menu.h"
|
||||
#include "Util.h"
|
||||
#include "PositionalAudioStream.h"
|
||||
|
@ -82,7 +83,7 @@ Audio::Audio(QObject* parent) :
|
|||
_noiseGateSampleCounter(0),
|
||||
_noiseGateOpen(false),
|
||||
_noiseGateEnabled(true),
|
||||
_toneInjectionEnabled(false),
|
||||
_audioSourceInjectEnabled(false),
|
||||
_noiseGateFramesToClose(0),
|
||||
_totalInputAudioSamples(0),
|
||||
_collisionSoundMagnitude(0.0f),
|
||||
|
@ -101,6 +102,8 @@ Audio::Audio(QObject* parent) :
|
|||
_scopeOutputOffset(0),
|
||||
_framesPerScope(DEFAULT_FRAMES_PER_SCOPE),
|
||||
_samplesPerScope(NETWORK_SAMPLES_PER_FRAME * _framesPerScope),
|
||||
_noiseSourceEnabled(false),
|
||||
_toneSourceEnabled(true),
|
||||
_peqEnabled(false),
|
||||
_scopeInput(0),
|
||||
_scopeOutputLeft(0),
|
||||
|
@ -137,6 +140,10 @@ void Audio::reset() {
|
|||
_receivedAudioStream.reset();
|
||||
resetStats();
|
||||
_peq.reset();
|
||||
_noiseSource.reset();
|
||||
_toneSource.reset();
|
||||
_sourceGain.reset();
|
||||
_inputGain.reset();
|
||||
}
|
||||
|
||||
void Audio::resetStats() {
|
||||
|
@ -157,14 +164,24 @@ void Audio::audioMixerKilled() {
|
|||
resetStats();
|
||||
}
|
||||
|
||||
|
||||
QAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& deviceName) {
|
||||
QAudioDeviceInfo result;
|
||||
#ifdef WIN32
|
||||
// NOTE
|
||||
// this is a workaround for a windows only QtBug https://bugreports.qt-project.org/browse/QTBUG-16117
|
||||
// static QAudioDeviceInfo objects get deallocated when QList<QAudioDevieInfo> objects go out of scope
|
||||
result = (mode == QAudio::AudioInput) ?
|
||||
QAudioDeviceInfo::defaultInputDevice() :
|
||||
QAudioDeviceInfo::defaultOutputDevice();
|
||||
#else
|
||||
foreach(QAudioDeviceInfo audioDevice, QAudioDeviceInfo::availableDevices(mode)) {
|
||||
qDebug() << audioDevice.deviceName() << " " << deviceName;
|
||||
if (audioDevice.deviceName().trimmed() == deviceName.trimmed()) {
|
||||
result = audioDevice;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -270,6 +287,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
|
|||
pMMDeviceEnumerator = NULL;
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
qDebug() << "DEBUG [" << deviceName << "] [" << getNamedAudioDeviceForMode(mode, deviceName).deviceName() << "]";
|
||||
|
||||
return getNamedAudioDeviceForMode(mode, deviceName);
|
||||
|
@ -424,14 +442,25 @@ void Audio::start() {
|
|||
qDebug() << "Unable to set up audio output because of a problem with output format.";
|
||||
}
|
||||
|
||||
_peq.initialize( _inputFormat.sampleRate(), _audioInput->bufferSize() );
|
||||
|
||||
_inputFrameBuffer.initialize( _inputFormat.channelCount(), _audioInput->bufferSize() * 8 );
|
||||
_peq.initialize( _inputFormat.sampleRate() );
|
||||
_inputGain.initialize();
|
||||
_sourceGain.initialize();
|
||||
_noiseSource.initialize();
|
||||
_toneSource.initialize();
|
||||
_sourceGain.setParameters(0.25f,0.0f);
|
||||
_inputGain.setParameters(1.0f,0.0f);
|
||||
}
|
||||
|
||||
void Audio::stop() {
|
||||
|
||||
_inputFrameBuffer.finalize();
|
||||
_peq.finalize();
|
||||
|
||||
_inputGain.finalize();
|
||||
_sourceGain.finalize();
|
||||
_noiseSource.finalize();
|
||||
_toneSource.finalize();
|
||||
|
||||
// "switch" to invalid devices in order to shut down the state
|
||||
switchInputToAudioDevice(QAudioDeviceInfo());
|
||||
switchOutputToAudioDevice(QAudioDeviceInfo());
|
||||
|
@ -477,14 +506,30 @@ void Audio::handleAudioInput() {
|
|||
|
||||
QByteArray inputByteArray = _inputDevice->readAll();
|
||||
|
||||
int16_t* inputFrameData = (int16_t*)inputByteArray.data();
|
||||
const int inputFrameCount = inputByteArray.size() / sizeof(int16_t);
|
||||
|
||||
_inputFrameBuffer.copyFrames(1, inputFrameCount, inputFrameData, false /*copy in*/);
|
||||
|
||||
_inputGain.render(_inputFrameBuffer); // input/mic gain+mute
|
||||
|
||||
// Add audio source injection if enabled
|
||||
if (_audioSourceInjectEnabled && !_muted) {
|
||||
|
||||
if (_toneSourceEnabled) { // sine generator
|
||||
_toneSource.render(_inputFrameBuffer);
|
||||
}
|
||||
else if(_noiseSourceEnabled) { // pink noise generator
|
||||
_noiseSource.render(_inputFrameBuffer);
|
||||
}
|
||||
_sourceGain.render(_inputFrameBuffer); // post gain
|
||||
}
|
||||
if (_peqEnabled && !_muted) {
|
||||
// we wish to pre-filter our captured input, prior to loopback
|
||||
|
||||
int16_t* ioBuffer = (int16_t*)inputByteArray.data();
|
||||
|
||||
_peq.render(ioBuffer, ioBuffer, inputByteArray.size() / sizeof(int16_t));
|
||||
_peq.render(_inputFrameBuffer); // 3-band parametric eq
|
||||
}
|
||||
|
||||
_inputFrameBuffer.copyFrames(1, inputFrameCount, inputFrameData, true /*copy out*/);
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::EchoLocalAudio) && !_muted && _audioOutput) {
|
||||
// if this person wants local loopback add that to the locally injected audio
|
||||
|
||||
|
@ -522,7 +567,7 @@ void Audio::handleAudioInput() {
|
|||
|
||||
int16_t* inputAudioSamples = new int16_t[inputSamplesRequired];
|
||||
_inputRingBuffer.readSamples(inputAudioSamples, inputSamplesRequired);
|
||||
|
||||
|
||||
const int numNetworkBytes = _isStereoInput ? NETWORK_BUFFER_LENGTH_BYTES_STEREO : NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL;
|
||||
const int numNetworkSamples = _isStereoInput ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
|
||||
|
||||
|
@ -599,20 +644,10 @@ void Audio::handleAudioInput() {
|
|||
_dcOffset = DC_OFFSET_AVERAGING * _dcOffset + (1.0f - DC_OFFSET_AVERAGING) * measuredDcOffset;
|
||||
}
|
||||
|
||||
// Add tone injection if enabled
|
||||
const float TONE_FREQ = 220.0f / SAMPLE_RATE * TWO_PI;
|
||||
const float QUARTER_VOLUME = 8192.0f;
|
||||
if (_toneInjectionEnabled) {
|
||||
loudness = 0.0f;
|
||||
for (int i = 0; i < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) {
|
||||
networkAudioSamples[i] = QUARTER_VOLUME * sinf(TONE_FREQ * (float)(i + _proceduralEffectSample));
|
||||
loudness += fabsf(networkAudioSamples[i]);
|
||||
}
|
||||
}
|
||||
_lastInputLoudness = fabs(loudness / NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
|
||||
|
||||
// If Noise Gate is enabled, check and turn the gate on and off
|
||||
if (!_toneInjectionEnabled && _noiseGateEnabled) {
|
||||
if (!_audioSourceInjectEnabled && _noiseGateEnabled) {
|
||||
float averageOfAllSampleFrames = 0.0f;
|
||||
_noiseSampleFrames[_noiseGateSampleCounter++] = _lastInputLoudness;
|
||||
if (_noiseGateSampleCounter == NUMBER_OF_NOISE_SAMPLE_FRAMES) {
|
||||
|
@ -1041,8 +1076,18 @@ void Audio::processProceduralAudio(int16_t* monoInput, int numSamples) {
|
|||
}
|
||||
}
|
||||
|
||||
void Audio::toggleToneInjection() {
|
||||
_toneInjectionEnabled = !_toneInjectionEnabled;
|
||||
void Audio::toggleAudioSourceInject() {
|
||||
_audioSourceInjectEnabled = !_audioSourceInjectEnabled;
|
||||
}
|
||||
|
||||
void Audio::selectAudioSourcePinkNoise() {
|
||||
_noiseSourceEnabled = Menu::getInstance()->isOptionChecked(MenuOption::AudioSourcePinkNoise);
|
||||
_toneSourceEnabled = !_noiseSourceEnabled;
|
||||
}
|
||||
|
||||
void Audio::selectAudioSourceSine440() {
|
||||
_toneSourceEnabled = Menu::getInstance()->isOptionChecked(MenuOption::AudioSourceSine440);
|
||||
_noiseSourceEnabled = !_toneSourceEnabled;
|
||||
}
|
||||
|
||||
void Audio::toggleAudioSpatialProcessing() {
|
||||
|
|
|
@ -20,6 +20,13 @@
|
|||
#include "Recorder.h"
|
||||
#include "RingBufferHistory.h"
|
||||
#include "MovingMinMaxAvg.h"
|
||||
#include "AudioRingBuffer.h"
|
||||
#include "AudioFormat.h"
|
||||
#include "AudioBuffer.h"
|
||||
#include "AudioSourceTone.h"
|
||||
#include "AudioSourceNoise.h"
|
||||
#include "AudioGain.h"
|
||||
#include "AudioPan.h"
|
||||
#include "AudioFilter.h"
|
||||
#include "AudioFilterBank.h"
|
||||
|
||||
|
@ -116,7 +123,9 @@ public slots:
|
|||
void audioMixerKilled();
|
||||
void toggleMute();
|
||||
void toggleAudioNoiseReduction();
|
||||
void toggleToneInjection();
|
||||
void toggleAudioSourceInject();
|
||||
void selectAudioSourcePinkNoise();
|
||||
void selectAudioSourceSine440();
|
||||
void toggleScope();
|
||||
void toggleScopePause();
|
||||
void toggleStats();
|
||||
|
@ -199,7 +208,8 @@ private:
|
|||
int _noiseGateSampleCounter;
|
||||
bool _noiseGateOpen;
|
||||
bool _noiseGateEnabled;
|
||||
bool _toneInjectionEnabled;
|
||||
bool _audioSourceInjectEnabled;
|
||||
|
||||
int _noiseGateFramesToClose;
|
||||
int _totalInputAudioSamples;
|
||||
|
||||
|
@ -282,10 +292,27 @@ private:
|
|||
int _framesPerScope;
|
||||
int _samplesPerScope;
|
||||
|
||||
// Multi-band parametric EQ
|
||||
bool _peqEnabled;
|
||||
AudioFilterPEQ3m _peq;
|
||||
// Input framebuffer
|
||||
AudioBufferFloat32 _inputFrameBuffer;
|
||||
|
||||
// Input gain
|
||||
AudioGain _inputGain;
|
||||
|
||||
// Post tone/pink noise generator gain
|
||||
AudioGain _sourceGain;
|
||||
|
||||
// Pink noise source
|
||||
bool _noiseSourceEnabled;
|
||||
AudioSourcePinkNoise _noiseSource;
|
||||
|
||||
// Tone source
|
||||
bool _toneSourceEnabled;
|
||||
AudioSourceTone _toneSource;
|
||||
|
||||
// Multi-band parametric EQ
|
||||
bool _peqEnabled;
|
||||
AudioFilterPEQ3m _peq;
|
||||
|
||||
QMutex _guard;
|
||||
QByteArray* _scopeInput;
|
||||
QByteArray* _scopeOutputLeft;
|
||||
|
|
|
@ -262,8 +262,8 @@ CameraScriptableObject::CameraScriptableObject(Camera* camera, ViewFrustum* view
|
|||
}
|
||||
|
||||
PickRay CameraScriptableObject::computePickRay(float x, float y) {
|
||||
float screenWidth = Application::getInstance()->getGLWidget()->getDeviceWidth();
|
||||
float screenHeight = Application::getInstance()->getGLWidget()->getDeviceHeight();
|
||||
float screenWidth = Application::getInstance()->getGLWidget()->width();
|
||||
float screenHeight = Application::getInstance()->getGLWidget()->height();
|
||||
PickRay result;
|
||||
if (OculusManager::isConnected()) {
|
||||
result.origin = _camera->getPosition();
|
||||
|
|
|
@ -43,14 +43,6 @@ int GLCanvas::getDeviceHeight() const {
|
|||
return height() * (windowHandle() ? windowHandle()->devicePixelRatio() : 1.0f);
|
||||
}
|
||||
|
||||
int GLCanvas::getDeviceX(int x) const {
|
||||
return x * getDeviceWidth() / width();
|
||||
}
|
||||
|
||||
int GLCanvas::getDeviceY(int y) const {
|
||||
return y * getDeviceHeight() / height();
|
||||
}
|
||||
|
||||
void GLCanvas::initializeGL() {
|
||||
Application::getInstance()->initializeGL();
|
||||
setAttribute(Qt::WA_AcceptTouchEvents);
|
||||
|
|
|
@ -26,9 +26,6 @@ public:
|
|||
int getDeviceHeight() const;
|
||||
QSize getDeviceSize() const { return QSize(getDeviceWidth(), getDeviceHeight()); }
|
||||
|
||||
int getDeviceX(int x) const;
|
||||
int getDeviceY(int y) const;
|
||||
|
||||
protected:
|
||||
|
||||
QTimer _frameTimer;
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <QDesktopServices>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <AddressManager.h>
|
||||
#include <XmppClient.h>
|
||||
#include <UUID.h>
|
||||
#include <UserActivityLogger.h>
|
||||
|
@ -70,6 +71,7 @@ Menu* Menu::getInstance() {
|
|||
|
||||
const ViewFrustumOffset DEFAULT_FRUSTUM_OFFSET = {-135.0f, 0.0f, 0.0f, 25.0f, 0.0f};
|
||||
const float DEFAULT_FACESHIFT_EYE_DEFLECTION = 0.25f;
|
||||
const QString DEFAULT_FACESHIFT_HOSTNAME = "localhost";
|
||||
const float DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER = 1.0f;
|
||||
const int ONE_SECOND_OF_FRAMES = 60;
|
||||
const int FIVE_SECONDS_OF_FRAMES = 5 * ONE_SECOND_OF_FRAMES;
|
||||
|
@ -87,6 +89,7 @@ Menu::Menu() :
|
|||
_fieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES),
|
||||
_realWorldFieldOfView(DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES),
|
||||
_faceshiftEyeDeflection(DEFAULT_FACESHIFT_EYE_DEFLECTION),
|
||||
_faceshiftHostname(DEFAULT_FACESHIFT_HOSTNAME),
|
||||
_frustumDrawMode(FRUSTUM_DRAW_MODE_ALL),
|
||||
_viewFrustumOffset(DEFAULT_FRUSTUM_OFFSET),
|
||||
_jsConsole(NULL),
|
||||
|
@ -139,9 +142,14 @@ Menu::Menu() :
|
|||
// call our toggle login function now so the menu option is setup properly
|
||||
toggleLoginMenuItem();
|
||||
|
||||
// connect to the appropriate slots of the AccountManager so that we can change the Login/Logout menu item
|
||||
// connect to the appropriate signal of the AccountManager so that we can change the Login/Logout menu item
|
||||
connect(&accountManager, &AccountManager::profileChanged, this, &Menu::toggleLoginMenuItem);
|
||||
connect(&accountManager, &AccountManager::logoutComplete, this, &Menu::toggleLoginMenuItem);
|
||||
|
||||
// connect to signal of account manager so we can tell user when the user/place they looked at is offline
|
||||
AddressManager& addressManager = AddressManager::getInstance();
|
||||
connect(&addressManager, &AddressManager::lookupResultIsOffline, this, &Menu::displayAddressOfflineMessage);
|
||||
connect(&addressManager, &AddressManager::lookupResultIsNotFound, this, &Menu::displayAddressNotFoundMessage);
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "Scripts");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O, appInstance, SLOT(loadDialog()));
|
||||
|
@ -153,21 +161,6 @@ Menu::Menu() :
|
|||
appInstance, SLOT(toggleRunningScriptsWidget()));
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "Go");
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::GoHome,
|
||||
Qt::CTRL | Qt::Key_G,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(goHome()));
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::GoToDomain,
|
||||
Qt::CTRL | Qt::Key_D,
|
||||
this,
|
||||
SLOT(goToDomainDialog()));
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::GoToLocation,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_L,
|
||||
this,
|
||||
SLOT(goToLocation()));
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::NameLocation,
|
||||
Qt::CTRL | Qt::Key_N,
|
||||
|
@ -179,12 +172,10 @@ Menu::Menu() :
|
|||
this,
|
||||
SLOT(toggleLocationList()));
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::GoTo,
|
||||
Qt::Key_At,
|
||||
MenuOption::AddressBar,
|
||||
Qt::CTRL | Qt::Key_Enter,
|
||||
this,
|
||||
SLOT(goTo()));
|
||||
connect(&LocationManager::getInstance(), &LocationManager::multipleDestinationsFound,
|
||||
this, &Menu::multipleDestinationsDecision);
|
||||
SLOT(toggleAddressBar()));
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "Upload Avatar Model");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadHead, 0, Application::getInstance(), SLOT(uploadHead()));
|
||||
|
@ -546,11 +537,31 @@ Menu::Menu() :
|
|||
0,
|
||||
this,
|
||||
SLOT(muteEnvironment()));
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioToneInjection,
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioSourceInject,
|
||||
0,
|
||||
false,
|
||||
appInstance->getAudio(),
|
||||
SLOT(toggleToneInjection()));
|
||||
SLOT(toggleAudioSourceInject()));
|
||||
QMenu* audioSourceMenu = audioDebugMenu->addMenu("Generated Audio Source");
|
||||
{
|
||||
QAction *pinkNoise = addCheckableActionToQMenuAndActionHash(audioSourceMenu, MenuOption::AudioSourcePinkNoise,
|
||||
0,
|
||||
false,
|
||||
appInstance->getAudio(),
|
||||
SLOT(selectAudioSourcePinkNoise()));
|
||||
|
||||
QAction *sine440 = addCheckableActionToQMenuAndActionHash(audioSourceMenu, MenuOption::AudioSourceSine440,
|
||||
0,
|
||||
true,
|
||||
appInstance->getAudio(),
|
||||
SLOT(selectAudioSourceSine440()));
|
||||
|
||||
QActionGroup* audioSourceGroup = new QActionGroup(audioSourceMenu);
|
||||
audioSourceGroup->addAction(pinkNoise);
|
||||
audioSourceGroup->addAction(sine440);
|
||||
}
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScope,
|
||||
Qt::CTRL | Qt::Key_P, false,
|
||||
appInstance->getAudio(),
|
||||
|
@ -694,6 +705,7 @@ void Menu::loadSettings(QSettings* settings) {
|
|||
_fieldOfView = loadSetting(settings, "fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES);
|
||||
_realWorldFieldOfView = loadSetting(settings, "realWorldFieldOfView", DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES);
|
||||
_faceshiftEyeDeflection = loadSetting(settings, "faceshiftEyeDeflection", DEFAULT_FACESHIFT_EYE_DEFLECTION);
|
||||
_faceshiftHostname = settings->value("faceshiftHostname", DEFAULT_FACESHIFT_HOSTNAME).toString();
|
||||
_maxVoxels = loadSetting(settings, "maxVoxels", DEFAULT_MAX_VOXELS_PER_SYSTEM);
|
||||
_maxVoxelPacketsPerSecond = loadSetting(settings, "maxVoxelsPPS", DEFAULT_MAX_VOXEL_PPS);
|
||||
_voxelSizeScale = loadSetting(settings, "voxelSizeScale", DEFAULT_OCTREE_SIZE_SCALE);
|
||||
|
@ -759,6 +771,7 @@ void Menu::saveSettings(QSettings* settings) {
|
|||
|
||||
settings->setValue("fieldOfView", _fieldOfView);
|
||||
settings->setValue("faceshiftEyeDeflection", _faceshiftEyeDeflection);
|
||||
settings->setValue("faceshiftHostname", _faceshiftHostname);
|
||||
settings->setValue("maxVoxels", _maxVoxels);
|
||||
settings->setValue("maxVoxelsPPS", _maxVoxelPacketsPerSecond);
|
||||
settings->setValue("voxelSizeScale", _voxelSizeScale);
|
||||
|
@ -1133,145 +1146,33 @@ void Menu::changePrivateKey() {
|
|||
sendFakeEnterEvent();
|
||||
}
|
||||
|
||||
void Menu::goToDomain(const QString newDomain) {
|
||||
if (NodeList::getInstance()->getDomainHandler().getHostname() != newDomain) {
|
||||
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
|
||||
Application::getInstance()->getAvatar()->sendKillAvatar();
|
||||
void Menu::toggleAddressBar() {
|
||||
|
||||
// give our nodeList the new domain-server hostname
|
||||
NodeList::getInstance()->getDomainHandler().setHostname(newDomain);
|
||||
QInputDialog addressBarDialog(Application::getInstance()->getWindow());
|
||||
addressBarDialog.setWindowTitle("Address Bar");
|
||||
addressBarDialog.setWindowFlags(Qt::Sheet);
|
||||
addressBarDialog.setLabelText("place, domain, @user, example.com, /position/orientation");
|
||||
|
||||
addressBarDialog.resize(addressBarDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW,
|
||||
addressBarDialog.size().height());
|
||||
|
||||
int dialogReturn = addressBarDialog.exec();
|
||||
if (dialogReturn == QDialog::Accepted && !addressBarDialog.textValue().isEmpty()) {
|
||||
// let the AddressManger figure out what to do with this
|
||||
AddressManager::getInstance().handleLookupString(addressBarDialog.textValue());
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::goToDomainDialog() {
|
||||
|
||||
QString currentDomainHostname = NodeList::getInstance()->getDomainHandler().getHostname();
|
||||
|
||||
if (NodeList::getInstance()->getDomainHandler().getPort() != DEFAULT_DOMAIN_SERVER_PORT) {
|
||||
// add the port to the currentDomainHostname string if it is custom
|
||||
currentDomainHostname.append(QString(":%1").arg(NodeList::getInstance()->getDomainHandler().getPort()));
|
||||
}
|
||||
|
||||
QInputDialog domainDialog(Application::getInstance()->getWindow());
|
||||
domainDialog.setWindowTitle("Go to Domain");
|
||||
domainDialog.setLabelText("Domain server:");
|
||||
domainDialog.setTextValue(currentDomainHostname);
|
||||
domainDialog.resize(domainDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, domainDialog.size().height());
|
||||
|
||||
int dialogReturn = domainDialog.exec();
|
||||
if (dialogReturn == QDialog::Accepted) {
|
||||
QString newHostname(DEFAULT_DOMAIN_HOSTNAME);
|
||||
|
||||
if (domainDialog.textValue().size() > 0) {
|
||||
// the user input a new hostname, use that
|
||||
newHostname = domainDialog.textValue();
|
||||
}
|
||||
|
||||
goToDomain(newHostname);
|
||||
}
|
||||
|
||||
|
||||
sendFakeEnterEvent();
|
||||
}
|
||||
|
||||
void Menu::goToOrientation(QString orientation) {
|
||||
LocationManager::getInstance().goToOrientation(orientation);
|
||||
void Menu::displayAddressOfflineMessage() {
|
||||
QMessageBox::information(Application::getInstance()->getWindow(), "Address offline",
|
||||
"That user or place is currently offline.");
|
||||
}
|
||||
|
||||
bool Menu::goToDestination(QString destination) {
|
||||
return LocationManager::getInstance().goToDestination(destination);
|
||||
}
|
||||
|
||||
void Menu::goTo(QString destination) {
|
||||
LocationManager::getInstance().goTo(destination);
|
||||
}
|
||||
|
||||
void Menu::goTo() {
|
||||
|
||||
QInputDialog gotoDialog(Application::getInstance()->getWindow());
|
||||
gotoDialog.setWindowTitle("Go to");
|
||||
gotoDialog.setLabelText("Destination or URL:\n @user, #place, hifi://domain/location/orientation");
|
||||
QString destination = QString();
|
||||
|
||||
gotoDialog.setTextValue(destination);
|
||||
gotoDialog.resize(gotoDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, gotoDialog.size().height());
|
||||
|
||||
int dialogReturn = gotoDialog.exec();
|
||||
if (dialogReturn == QDialog::Accepted && !gotoDialog.textValue().isEmpty()) {
|
||||
QString desiredDestination = gotoDialog.textValue();
|
||||
if (!goToURL(desiredDestination)) {;
|
||||
goTo(desiredDestination);
|
||||
}
|
||||
}
|
||||
sendFakeEnterEvent();
|
||||
}
|
||||
|
||||
bool Menu::goToURL(QString location) {
|
||||
if (location.startsWith(CUSTOM_URL_SCHEME + "/")) {
|
||||
QStringList urlParts = location.remove(0, CUSTOM_URL_SCHEME.length()).split('/', QString::SkipEmptyParts);
|
||||
|
||||
if (urlParts.count() > 1) {
|
||||
// if url has 2 or more parts, the first one is domain name
|
||||
QString domain = urlParts[0];
|
||||
|
||||
// second part is either a destination coordinate or
|
||||
// a place name
|
||||
QString destination = urlParts[1];
|
||||
|
||||
// any third part is an avatar orientation.
|
||||
QString orientation = urlParts.count() > 2 ? urlParts[2] : QString();
|
||||
|
||||
goToDomain(domain);
|
||||
|
||||
// goto either @user, #place, or x-xx,y-yy,z-zz
|
||||
// style co-ordinate.
|
||||
goTo(destination);
|
||||
|
||||
if (!orientation.isEmpty()) {
|
||||
// location orientation
|
||||
goToOrientation(orientation);
|
||||
}
|
||||
} else if (urlParts.count() == 1) {
|
||||
QString destination = urlParts[0];
|
||||
|
||||
// If this starts with # or @, treat it as a user/location, otherwise treat it as a domain
|
||||
if (destination[0] == '#' || destination[0] == '@') {
|
||||
goTo(destination);
|
||||
} else {
|
||||
goToDomain(destination);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Menu::goToUser(const QString& user) {
|
||||
LocationManager::getInstance().goTo(user);
|
||||
}
|
||||
|
||||
/// Open a url, shortcutting any "hifi" scheme URLs to the local application.
|
||||
void Menu::openUrl(const QUrl& url) {
|
||||
if (url.scheme() == "hifi") {
|
||||
goToURL(url.toString());
|
||||
} else {
|
||||
QDesktopServices::openUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::multipleDestinationsDecision(const QJsonObject& userData, const QJsonObject& placeData) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText("Both user and location exists with same name");
|
||||
msgBox.setInformativeText("Where you wanna go?");
|
||||
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Open);
|
||||
msgBox.button(QMessageBox::Ok)->setText("User");
|
||||
msgBox.button(QMessageBox::Open)->setText("Place");
|
||||
int userResponse = msgBox.exec();
|
||||
|
||||
if (userResponse == QMessageBox::Ok) {
|
||||
Application::getInstance()->getAvatar()->goToLocationFromAddress(userData["address"].toObject());
|
||||
} else if (userResponse == QMessageBox::Open) {
|
||||
Application::getInstance()->getAvatar()->goToLocationFromAddress(placeData["address"].toObject());
|
||||
}
|
||||
void Menu::displayAddressNotFoundMessage() {
|
||||
QMessageBox::information(Application::getInstance()->getWindow(), "Address not found",
|
||||
"There is no address information for that user or place.");
|
||||
}
|
||||
|
||||
void Menu::muteEnvironment() {
|
||||
|
@ -1298,43 +1199,13 @@ void Menu::muteEnvironment() {
|
|||
free(packet);
|
||||
}
|
||||
|
||||
void Menu::goToLocation() {
|
||||
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
|
||||
glm::vec3 avatarPos = myAvatar->getPosition();
|
||||
QString currentLocation = QString("%1, %2, %3").arg(QString::number(avatarPos.x),
|
||||
QString::number(avatarPos.y), QString::number(avatarPos.z));
|
||||
void Menu::displayNameLocationResponse(const QString& errorString) {
|
||||
|
||||
QInputDialog coordinateDialog(Application::getInstance()->getWindow());
|
||||
coordinateDialog.setWindowTitle("Go to Location");
|
||||
coordinateDialog.setLabelText("Coordinate as x,y,z:");
|
||||
coordinateDialog.setTextValue(currentLocation);
|
||||
coordinateDialog.resize(coordinateDialog.parentWidget()->size().width() * 0.30, coordinateDialog.size().height());
|
||||
|
||||
int dialogReturn = coordinateDialog.exec();
|
||||
if (dialogReturn == QDialog::Accepted && !coordinateDialog.textValue().isEmpty()) {
|
||||
goToDestination(coordinateDialog.textValue());
|
||||
}
|
||||
|
||||
sendFakeEnterEvent();
|
||||
}
|
||||
|
||||
void Menu::namedLocationCreated(LocationManager::NamedLocationCreateResponse response) {
|
||||
|
||||
if (response == LocationManager::Created) {
|
||||
return;
|
||||
}
|
||||
|
||||
QMessageBox msgBox;
|
||||
switch (response) {
|
||||
case LocationManager::AlreadyExists:
|
||||
msgBox.setText("That name has been already claimed, try something else.");
|
||||
break;
|
||||
default:
|
||||
msgBox.setText("An unexpected error has occurred, please try again later.");
|
||||
break;
|
||||
}
|
||||
|
||||
msgBox.exec();
|
||||
if (!errorString.isEmpty()) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText(errorString);
|
||||
msgBox.exec();
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::toggleLocationList() {
|
||||
|
@ -1352,23 +1223,34 @@ void Menu::nameLocation() {
|
|||
// check if user is logged in or show login dialog if not
|
||||
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
||||
if (!accountManager.isLoggedIn()) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText("We need to tie this location to your username.");
|
||||
msgBox.setInformativeText("Please login first, then try naming the location again.");
|
||||
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
|
||||
msgBox.button(QMessageBox::Ok)->setText("Login");
|
||||
|
||||
if (msgBox.exec() == QMessageBox::Ok) {
|
||||
loginForCurrentDomain();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
DomainHandler& domainHandler = NodeList::getInstance()->getDomainHandler();
|
||||
if (domainHandler.getUUID().isNull()) {
|
||||
const QString UNREGISTERED_DOMAIN_MESSAGE = "This domain is not registered with High Fidelity."
|
||||
"\n\nYou cannot create a global location in an unregistered domain.";
|
||||
QMessageBox::critical(this, "Unregistered Domain", UNREGISTERED_DOMAIN_MESSAGE);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
QInputDialog nameDialog(Application::getInstance()->getWindow());
|
||||
nameDialog.setWindowTitle("Name this location");
|
||||
nameDialog.setLabelText("Name this location, then share that name with others.\n"
|
||||
"When they come here, they'll be in the same location and orientation\n"
|
||||
"When they come here, they'll have the same viewpoint\n"
|
||||
"(wherever you are standing and looking now) as you.\n\n"
|
||||
"Location name:");
|
||||
|
||||
|
@ -1383,10 +1265,10 @@ void Menu::nameLocation() {
|
|||
|
||||
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
|
||||
LocationManager* manager = new LocationManager();
|
||||
connect(manager, &LocationManager::creationCompleted, this, &Menu::namedLocationCreated);
|
||||
connect(manager, &LocationManager::creationCompleted, this, &Menu::displayNameLocationResponse);
|
||||
NamedLocation* location = new NamedLocation(locationName,
|
||||
myAvatar->getPosition(), myAvatar->getOrientation(),
|
||||
NodeList::getInstance()->getDomainHandler().getHostname());
|
||||
domainHandler.getUUID());
|
||||
manager->createNamedLocation(location);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,6 +104,8 @@ public:
|
|||
|
||||
float getFaceshiftEyeDeflection() const { return _faceshiftEyeDeflection; }
|
||||
void setFaceshiftEyeDeflection(float faceshiftEyeDeflection) { _faceshiftEyeDeflection = faceshiftEyeDeflection; bumpSettings(); }
|
||||
const QString& getFaceshiftHostname() const { return _faceshiftHostname; }
|
||||
void setFaceshiftHostname(const QString& hostname) { _faceshiftHostname = hostname; bumpSettings(); }
|
||||
QString getSnapshotsLocation() const;
|
||||
void setSnapshotsLocation(QString snapshotsLocation) { _snapshotsLocation = snapshotsLocation; bumpSettings(); }
|
||||
|
||||
|
@ -162,11 +164,6 @@ public:
|
|||
int menuItemLocation = UNSPECIFIED_POSITION);
|
||||
|
||||
void removeAction(QMenu* menu, const QString& actionName);
|
||||
|
||||
bool static goToDestination(QString destination);
|
||||
void static goToOrientation(QString orientation);
|
||||
void static goToDomain(const QString newDomain);
|
||||
void static goTo(QString destination);
|
||||
|
||||
const QByteArray& getWalletPrivateKey() const { return _walletPrivateKey; }
|
||||
|
||||
|
@ -185,11 +182,8 @@ public slots:
|
|||
void saveSettings(QSettings* settings = NULL);
|
||||
void importSettings();
|
||||
void exportSettings();
|
||||
void goTo();
|
||||
bool goToURL(QString location);
|
||||
void goToUser(const QString& user);
|
||||
void toggleAddressBar();
|
||||
void pasteToVoxel();
|
||||
void openUrl(const QUrl& url);
|
||||
|
||||
void toggleLoginMenuItem();
|
||||
|
||||
|
@ -211,8 +205,6 @@ private slots:
|
|||
void editAttachments();
|
||||
void editAnimations();
|
||||
void changePrivateKey();
|
||||
void goToDomainDialog();
|
||||
void goToLocation();
|
||||
void nameLocation();
|
||||
void toggleLocationList();
|
||||
void bandwidthDetailsClosed();
|
||||
|
@ -226,8 +218,9 @@ private slots:
|
|||
void toggleConsole();
|
||||
void toggleChat();
|
||||
void audioMuteToggled();
|
||||
void namedLocationCreated(LocationManager::NamedLocationCreateResponse response);
|
||||
void multipleDestinationsDecision(const QJsonObject& userData, const QJsonObject& placeData);
|
||||
void displayNameLocationResponse(const QString& errorString);
|
||||
void displayAddressOfflineMessage();
|
||||
void displayAddressNotFoundMessage();
|
||||
void muteEnvironment();
|
||||
|
||||
private:
|
||||
|
@ -271,6 +264,7 @@ private:
|
|||
float _fieldOfView; /// in Degrees, doesn't apply to HMD like Oculus
|
||||
float _realWorldFieldOfView; // The actual FOV set by the user's monitor size and view distance
|
||||
float _faceshiftEyeDeflection;
|
||||
QString _faceshiftHostname;
|
||||
FrustumDrawMode _frustumDrawMode;
|
||||
ViewFrustumOffset _viewFrustumOffset;
|
||||
QPointer<MetavoxelEditor> _MetavoxelEditor;
|
||||
|
@ -315,6 +309,7 @@ private:
|
|||
|
||||
namespace MenuOption {
|
||||
const QString AboutApp = "About Interface";
|
||||
const QString AddressBar = "Show Address Bar";
|
||||
const QString AlignForearmsWithWrists = "Align Forearms with Wrists";
|
||||
const QString AlternateIK = "Alternate IK";
|
||||
const QString AmbientOcclusion = "Ambient Occlusion";
|
||||
|
@ -347,7 +342,9 @@ namespace MenuOption {
|
|||
const QString AudioSpatialProcessingSlightlyRandomSurfaces = "Slightly Random Surfaces";
|
||||
const QString AudioSpatialProcessingStereoSource = "Stereo Source";
|
||||
const QString AudioSpatialProcessingWithDiffusions = "With Diffusions";
|
||||
const QString AudioToneInjection = "Inject Test Tone";
|
||||
const QString AudioSourceInject = "Generated Audio";
|
||||
const QString AudioSourcePinkNoise = "Pink Noise";
|
||||
const QString AudioSourceSine440 = "Sine 440hz";
|
||||
const QString Avatars = "Avatars";
|
||||
const QString AvatarsReceiveShadows = "Avatars Receive Shadows";
|
||||
const QString Bandwidth = "Bandwidth Display";
|
||||
|
@ -398,10 +395,7 @@ namespace MenuOption {
|
|||
const QString FullscreenMirror = "Fullscreen Mirror";
|
||||
const QString GlowMode = "Cycle Glow Mode";
|
||||
const QString GlowWhenSpeaking = "Glow When Speaking";
|
||||
const QString GoHome = "Go Home";
|
||||
const QString GoToDomain = "Go To Domain...";
|
||||
const QString GoTo = "Go To...";
|
||||
const QString GoToLocation = "Go To Location...";
|
||||
const QString GoToUser = "Go To User";
|
||||
const QString HeadMouse = "Head Mouse";
|
||||
const QString IncreaseAvatarSize = "Increase Avatar Size";
|
||||
const QString IncreaseVoxelSize = "Increase Voxel Size";
|
||||
|
|
|
@ -133,7 +133,7 @@ const GLenum COLOR_NORMAL_DRAW_BUFFERS[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTA
|
|||
|
||||
void MetavoxelSystem::render() {
|
||||
// update the frustum
|
||||
ViewFrustum* viewFrustum = Application::getInstance()->getViewFrustum();
|
||||
ViewFrustum* viewFrustum = Application::getInstance()->getDisplayViewFrustum();
|
||||
_frustum.set(viewFrustum->getFarTopLeft(), viewFrustum->getFarTopRight(), viewFrustum->getFarBottomLeft(),
|
||||
viewFrustum->getFarBottomRight(), viewFrustum->getNearTopLeft(), viewFrustum->getNearTopRight(),
|
||||
viewFrustum->getNearBottomLeft(), viewFrustum->getNearBottomRight());
|
||||
|
@ -181,6 +181,14 @@ void MetavoxelSystem::render() {
|
|||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getPrimaryNormalTextureID());
|
||||
|
||||
// get the viewport side (left, right, both)
|
||||
int viewport[4];
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
const int VIEWPORT_X_INDEX = 0;
|
||||
const int VIEWPORT_WIDTH_INDEX = 2;
|
||||
float sMin = viewport[VIEWPORT_X_INDEX] / (float)primaryFBO->width();
|
||||
float sWidth = viewport[VIEWPORT_WIDTH_INDEX] / (float)primaryFBO->width();
|
||||
|
||||
if (Menu::getInstance()->getShadowsEnabled()) {
|
||||
glActiveTexture(GL_TEXTURE2);
|
||||
glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getPrimaryDepthTextureID());
|
||||
|
@ -210,10 +218,13 @@ void MetavoxelSystem::render() {
|
|||
program->setUniformValue(locations->nearLocation, nearVal);
|
||||
program->setUniformValue(locations->depthScale, (farVal - nearVal) / farVal);
|
||||
float nearScale = -1.0f / nearVal;
|
||||
program->setUniformValue(locations->depthTexCoordOffset, left * nearScale, bottom * nearScale);
|
||||
program->setUniformValue(locations->depthTexCoordScale, (right - left) * nearScale, (top - bottom) * nearScale);
|
||||
float sScale = 1.0f / sWidth;
|
||||
float depthTexCoordScaleS = (right - left) * nearScale * sScale;
|
||||
program->setUniformValue(locations->depthTexCoordOffset, left * nearScale - sMin * depthTexCoordScaleS,
|
||||
bottom * nearScale);
|
||||
program->setUniformValue(locations->depthTexCoordScale, depthTexCoordScaleS, (top - bottom) * nearScale);
|
||||
|
||||
renderFullscreenQuad();
|
||||
renderFullscreenQuad(sMin, sMin + sWidth);
|
||||
|
||||
program->release();
|
||||
|
||||
|
@ -226,7 +237,7 @@ void MetavoxelSystem::render() {
|
|||
|
||||
} else {
|
||||
_directionalLight.bind();
|
||||
renderFullscreenQuad();
|
||||
renderFullscreenQuad(sMin, sMin + sWidth);
|
||||
_directionalLight.release();
|
||||
}
|
||||
|
||||
|
@ -245,7 +256,7 @@ void MetavoxelSystem::render() {
|
|||
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
renderFullscreenQuad();
|
||||
renderFullscreenQuad(sMin, sMin + sWidth);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
@ -2176,7 +2187,7 @@ private:
|
|||
SpannerRenderVisitor::SpannerRenderVisitor(const MetavoxelLOD& lod) :
|
||||
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(),
|
||||
QVector<AttributePointer>(), QVector<AttributePointer>(), QVector<AttributePointer>(),
|
||||
lod, encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())),
|
||||
lod, encodeOrder(Application::getInstance()->getDisplayViewFrustum()->getDirection())),
|
||||
_containmentDepth(INT_MAX) {
|
||||
}
|
||||
|
||||
|
@ -2212,7 +2223,7 @@ private:
|
|||
|
||||
BufferRenderVisitor::BufferRenderVisitor(const AttributePointer& attribute) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() << attribute),
|
||||
_order(encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())),
|
||||
_order(encodeOrder(Application::getInstance()->getDisplayViewFrustum()->getDirection())),
|
||||
_containmentDepth(INT_MAX) {
|
||||
}
|
||||
|
||||
|
@ -2246,12 +2257,12 @@ void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, Metavox
|
|||
float viewportWidth = viewport[VIEWPORT_WIDTH_INDEX];
|
||||
float viewportHeight = viewport[VIEWPORT_HEIGHT_INDEX];
|
||||
float viewportDiagonal = sqrtf(viewportWidth * viewportWidth + viewportHeight * viewportHeight);
|
||||
float worldDiagonal = glm::distance(Application::getInstance()->getViewFrustum()->getNearBottomLeft(),
|
||||
Application::getInstance()->getViewFrustum()->getNearTopRight());
|
||||
float worldDiagonal = glm::distance(Application::getInstance()->getDisplayViewFrustum()->getNearBottomLeft(),
|
||||
Application::getInstance()->getDisplayViewFrustum()->getNearTopRight());
|
||||
|
||||
_pointProgram.bind();
|
||||
_pointProgram.setUniformValue(_pointScaleLocation, viewportDiagonal *
|
||||
Application::getInstance()->getViewFrustum()->getNearClip() / worldDiagonal);
|
||||
Application::getInstance()->getDisplayViewFrustum()->getNearClip() / worldDiagonal);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
|
|
|
@ -48,6 +48,7 @@ static const QString TRANSLATION_Y_FIELD = "ty";
|
|||
static const QString TRANSLATION_Z_FIELD = "tz";
|
||||
static const QString JOINT_FIELD = "joint";
|
||||
static const QString FREE_JOINT_FIELD = "freeJoint";
|
||||
static const QString BLENDSHAPE_FIELD = "bs";
|
||||
|
||||
static const QString S3_URL = "http://public.highfidelity.io";
|
||||
static const QString MODEL_URL = "/api/v1/models";
|
||||
|
@ -192,6 +193,45 @@ bool ModelUploader::zip() {
|
|||
mapping.insertMulti(FREE_JOINT_FIELD, "RightForeArm");
|
||||
}
|
||||
|
||||
// mixamo blendshapes
|
||||
if (!mapping.contains(BLENDSHAPE_FIELD) && geometry.applicationName == "mixamo.com") {
|
||||
QVariantHash blendshapes;
|
||||
blendshapes.insert("EyeBlink_L", QVariantList() << "Blink_Left" << 1.0);
|
||||
blendshapes.insert("EyeBlink_R", QVariantList() << "Blink_Right" << 1.0);
|
||||
blendshapes.insert("EyeSquint_L", QVariantList() << "Squint_Left" << 1.0);
|
||||
blendshapes.insert("EyeSquint_R", QVariantList() << "Squint_Right" << 1.0);
|
||||
blendshapes.insert("EyeOpen_L", QVariantList() << "EyesWide_Left" << 1.0);
|
||||
blendshapes.insert("EyeOpen_R", QVariantList() << "EyesWide_Right" << 1.0);
|
||||
blendshapes.insert("BrowsD_L", QVariantList() << "BrowsDown_Left" << 1.0);
|
||||
blendshapes.insert("BrowsD_R", QVariantList() << "BrowsDown_Right" << 1.0);
|
||||
blendshapes.insert("BrowsU_L", QVariantList() << "BrowsUp_Left" << 1.0);
|
||||
blendshapes.insert("BrowsU_R", QVariantList() << "BrowsUp_Right" << 1.0);
|
||||
blendshapes.insert("JawFwd", QVariantList() << "JawForeward" << 1.0);
|
||||
blendshapes.insert("JawOpen", QVariantList() << "Jaw_Down" << 1.0);
|
||||
blendshapes.insert("JawLeft", QVariantList() << "Jaw_Left" << 1.0);
|
||||
blendshapes.insert("JawRight", QVariantList() << "Jaw_Right" << 1.0);
|
||||
blendshapes.insert("JawChew", QVariantList() << "Jaw_Up" << 1.0);
|
||||
blendshapes.insert("MouthLeft", QVariantList() << "Midmouth_Left" << 1.0);
|
||||
blendshapes.insert("MouthRight", QVariantList() << "Midmouth_Right" << 1.0);
|
||||
blendshapes.insert("MouthFrown_L", QVariantList() << "Frown_Left" << 1.0);
|
||||
blendshapes.insert("MouthFrown_R", QVariantList() << "Frown_Right" << 1.0);
|
||||
blendshapes.insert("MouthSmile_L", QVariantList() << "Smile_Left" << 1.0);
|
||||
blendshapes.insert("MouthSmile_R", QVariantList() << "Smile_Right" << 1.0);
|
||||
blendshapes.insert("LipsUpperUp", QVariantList() << "UpperLipUp_Left" << 0.5);
|
||||
blendshapes.insertMulti("LipsUpperUp", QVariantList() << "UpperLipUp_Right" << 0.5);
|
||||
blendshapes.insert("Puff", QVariantList() << "CheekPuff_Left" << 0.5);
|
||||
blendshapes.insertMulti("Puff", QVariantList() << "CheekPuff_Right" << 0.5);
|
||||
blendshapes.insert("Sneer", QVariantList() << "NoseScrunch_Left" << 0.5);
|
||||
blendshapes.insertMulti("Sneer", QVariantList() << "NoseScrunch_Right" << 0.5);
|
||||
blendshapes.insert("CheekSquint_L", QVariantList() << "Squint_Left" << 1.0);
|
||||
blendshapes.insert("CheekSquint_R", QVariantList() << "Squint_Right" << 1.0);
|
||||
blendshapes.insert("LipsPucker", QVariantList() << "MouthNarrow_Left" << 0.5);
|
||||
blendshapes.insertMulti("LipsPucker", QVariantList() << "MouthNarrow_Right" << 0.5);
|
||||
blendshapes.insert("LipsLowerDown", QVariantList() << "LowerLipDown_Left" << 0.5);
|
||||
blendshapes.insertMulti("LipsLowerDown", QVariantList() << "LowerLipDown_Right" << 0.5);
|
||||
mapping.insert(BLENDSHAPE_FIELD, blendshapes);
|
||||
}
|
||||
|
||||
// open the dialog to configure the rest
|
||||
ModelPropertiesDialog properties(_modelType, mapping, basePath, geometry);
|
||||
if (properties.exec() == QDialog::Rejected) {
|
||||
|
@ -381,11 +421,11 @@ void ModelUploader::uploadSuccess(const QJsonObject& jsonResponse) {
|
|||
checkS3();
|
||||
}
|
||||
|
||||
void ModelUploader::uploadFailed(QNetworkReply::NetworkError errorCode, const QString& errorString) {
|
||||
void ModelUploader::uploadFailed(QNetworkReply& errorReply) {
|
||||
if (_progressDialog) {
|
||||
_progressDialog->reject();
|
||||
}
|
||||
qDebug() << "Model upload failed (" << errorCode << "): " << errorString;
|
||||
qDebug() << "Model upload failed (" << errorReply.error() << "): " << errorReply.errorString();
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::uploadFailed()"),
|
||||
QString("There was a problem with your upload, please try again later."),
|
||||
|
|
|
@ -43,7 +43,7 @@ private slots:
|
|||
void checkJSON(const QJsonObject& jsonResponse);
|
||||
void uploadUpdate(qint64 bytesSent, qint64 bytesTotal);
|
||||
void uploadSuccess(const QJsonObject& jsonResponse);
|
||||
void uploadFailed(QNetworkReply::NetworkError errorCode, const QString& errorString);
|
||||
void uploadFailed(QNetworkReply& errorReply);
|
||||
void checkS3();
|
||||
void processCheck();
|
||||
|
||||
|
|
|
@ -18,14 +18,13 @@
|
|||
#include "Application.h"
|
||||
#include "UserLocationsModel.h"
|
||||
|
||||
static const QString PLACES_GET = "/api/v1/places";
|
||||
static const QString PLACES_UPDATE = "/api/v1/places/%1";
|
||||
static const QString PLACES_DELETE= "/api/v1/places/%1";
|
||||
static const QString LOCATIONS_GET = "/api/v1/locations";
|
||||
static const QString LOCATION_UPDATE_OR_DELETE = "/api/v1/locations/%1";
|
||||
|
||||
UserLocation::UserLocation(QString id, QString name, QString location) :
|
||||
UserLocation::UserLocation(const QString& id, const QString& name, const QString& address) :
|
||||
_id(id),
|
||||
_name(name),
|
||||
_location(location),
|
||||
_address(address),
|
||||
_previousName(name),
|
||||
_updating(false) {
|
||||
}
|
||||
|
@ -35,10 +34,15 @@ void UserLocation::requestRename(const QString& newName) {
|
|||
_updating = true;
|
||||
|
||||
JSONCallbackParameters callbackParams(this, "handleRenameResponse", this, "handleRenameError");
|
||||
|
||||
QJsonObject jsonNameObject;
|
||||
jsonNameObject.insert("name", QJsonValue(newName));
|
||||
jsonNameObject.insert("name", newName);
|
||||
|
||||
QJsonObject locationObject;
|
||||
locationObject.insert("location", jsonNameObject);
|
||||
|
||||
QJsonDocument jsonDocument(jsonNameObject);
|
||||
AccountManager::getInstance().authenticatedRequest(PLACES_UPDATE.arg(_id),
|
||||
AccountManager::getInstance().authenticatedRequest(LOCATION_UPDATE_OR_DELETE.arg(_id),
|
||||
QNetworkAccessManager::PutOperation,
|
||||
callbackParams,
|
||||
jsonDocument.toJson());
|
||||
|
@ -54,7 +58,9 @@ void UserLocation::handleRenameResponse(const QJsonObject& responseData) {
|
|||
|
||||
QJsonValue status = responseData["status"];
|
||||
if (!status.isUndefined() && status.toString() == "success") {
|
||||
QString updatedName = responseData["data"].toObject()["name"].toString();
|
||||
qDebug() << responseData;
|
||||
QString updatedName = responseData["data"].toObject()["location"].toObject()["name"].toString();
|
||||
qDebug() << "The updated name is" << updatedName;
|
||||
_name = updatedName;
|
||||
} else {
|
||||
_name = _previousName;
|
||||
|
@ -75,10 +81,10 @@ void UserLocation::handleRenameResponse(const QJsonObject& responseData) {
|
|||
emit updated(_name);
|
||||
}
|
||||
|
||||
void UserLocation::handleRenameError(QNetworkReply::NetworkError error, const QString& errorString) {
|
||||
void UserLocation::handleRenameError(QNetworkReply& errorReply) {
|
||||
_updating = false;
|
||||
|
||||
QString msg = "There was an error renaming location '" + _name + "': " + errorString;
|
||||
QString msg = "There was an error renaming location '" + _name + "': " + errorReply.errorString();
|
||||
qDebug() << msg;
|
||||
QMessageBox::warning(Application::getInstance()->getWindow(), "Error", msg);
|
||||
|
||||
|
@ -90,7 +96,7 @@ void UserLocation::requestDelete() {
|
|||
_updating = true;
|
||||
|
||||
JSONCallbackParameters callbackParams(this, "handleDeleteResponse", this, "handleDeleteError");
|
||||
AccountManager::getInstance().authenticatedRequest(PLACES_DELETE.arg(_id),
|
||||
AccountManager::getInstance().authenticatedRequest(LOCATION_UPDATE_OR_DELETE.arg(_id),
|
||||
QNetworkAccessManager::DeleteOperation,
|
||||
callbackParams);
|
||||
}
|
||||
|
@ -109,10 +115,10 @@ void UserLocation::handleDeleteResponse(const QJsonObject& responseData) {
|
|||
}
|
||||
}
|
||||
|
||||
void UserLocation::handleDeleteError(QNetworkReply::NetworkError error, const QString& errorString) {
|
||||
void UserLocation::handleDeleteError(QNetworkReply& errorReply) {
|
||||
_updating = false;
|
||||
|
||||
QString msg = "There was an error deleting location '" + _name + "': " + errorString;
|
||||
QString msg = "There was an error deleting location '" + _name + "': " + errorReply.errorString();
|
||||
qDebug() << msg;
|
||||
QMessageBox::warning(Application::getInstance()->getWindow(), "Error", msg);
|
||||
}
|
||||
|
@ -153,7 +159,7 @@ void UserLocationsModel::refresh() {
|
|||
endResetModel();
|
||||
|
||||
JSONCallbackParameters callbackParams(this, "handleLocationsResponse");
|
||||
AccountManager::getInstance().authenticatedRequest(PLACES_GET,
|
||||
AccountManager::getInstance().authenticatedRequest(LOCATIONS_GET,
|
||||
QNetworkAccessManager::GetOperation,
|
||||
callbackParams);
|
||||
}
|
||||
|
@ -165,14 +171,13 @@ void UserLocationsModel::handleLocationsResponse(const QJsonObject& responseData
|
|||
QJsonValue status = responseData["status"];
|
||||
if (!status.isUndefined() && status.toString() == "success") {
|
||||
beginResetModel();
|
||||
QJsonArray locations = responseData["data"].toObject()["places"].toArray();
|
||||
QJsonArray locations = responseData["data"].toObject()["locations"].toArray();
|
||||
for (QJsonArray::const_iterator it = locations.constBegin(); it != locations.constEnd(); it++) {
|
||||
QJsonObject location = (*it).toObject();
|
||||
QJsonObject address = location["address"].toObject();
|
||||
QString locationAddress = "hifi://" + location["domain"].toObject()["name"].toString()
|
||||
+ location["path"].toString();
|
||||
UserLocation* userLocation = new UserLocation(location["id"].toString(), location["name"].toString(),
|
||||
"hifi://" + address["domain"].toString()
|
||||
+ "/" + address["position"].toString()
|
||||
+ "/" + address["orientation"].toString());
|
||||
locationAddress);
|
||||
_locations.append(userLocation);
|
||||
connect(userLocation, &UserLocation::deleted, this, &UserLocationsModel::removeLocation);
|
||||
connect(userLocation, &UserLocation::updated, this, &UserLocationsModel::update);
|
||||
|
@ -214,8 +219,8 @@ QVariant UserLocationsModel::data(const QModelIndex& index, int role) const {
|
|||
return QVariant();
|
||||
} else if (index.column() == NameColumn) {
|
||||
return _locations[index.row()]->name();
|
||||
} else if (index.column() == LocationColumn) {
|
||||
return QVariant(_locations[index.row()]->location());
|
||||
} else if (index.column() == AddressColumn) {
|
||||
return QVariant(_locations[index.row()]->address());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,7 +231,7 @@ QVariant UserLocationsModel::headerData(int section, Qt::Orientation orientation
|
|||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
|
||||
switch (section) {
|
||||
case NameColumn: return "Name";
|
||||
case LocationColumn: return "Location";
|
||||
case AddressColumn: return "Address";
|
||||
default: return QVariant();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,20 +20,20 @@
|
|||
class UserLocation : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
UserLocation(QString id, QString name, QString location);
|
||||
UserLocation(const QString& id, const QString& name, const QString& address);
|
||||
bool isUpdating() { return _updating; }
|
||||
void requestRename(const QString& newName);
|
||||
void requestDelete();
|
||||
|
||||
QString id() { return _id; }
|
||||
QString name() { return _name; }
|
||||
QString location() { return _location; }
|
||||
const QString& id() { return _id; }
|
||||
const QString& name() { return _name; }
|
||||
const QString& address() { return _address; }
|
||||
|
||||
public slots:
|
||||
void handleRenameResponse(const QJsonObject& responseData);
|
||||
void handleRenameError(QNetworkReply::NetworkError error, const QString& errorString);
|
||||
void handleRenameError(QNetworkReply& errorReply);
|
||||
void handleDeleteResponse(const QJsonObject& responseData);
|
||||
void handleDeleteError(QNetworkReply::NetworkError error, const QString& errorString);
|
||||
void handleDeleteError(QNetworkReply& errorReply);
|
||||
|
||||
signals:
|
||||
void updated(const QString& name);
|
||||
|
@ -42,7 +42,7 @@ signals:
|
|||
private:
|
||||
QString _id;
|
||||
QString _name;
|
||||
QString _location;
|
||||
QString _address;
|
||||
QString _previousName;
|
||||
bool _updating;
|
||||
|
||||
|
@ -65,7 +65,7 @@ public:
|
|||
|
||||
enum Columns {
|
||||
NameColumn = 0,
|
||||
LocationColumn
|
||||
AddressColumn
|
||||
};
|
||||
|
||||
public slots:
|
||||
|
|
|
@ -181,9 +181,10 @@ const glm::vec3 randVector() {
|
|||
}
|
||||
|
||||
static TextRenderer* textRenderer(int mono) {
|
||||
static TextRenderer* monoRenderer = new TextRenderer(MONO_FONT_FAMILY);
|
||||
static TextRenderer* proportionalRenderer = new TextRenderer(SANS_FONT_FAMILY, -1, -1, false, TextRenderer::SHADOW_EFFECT);
|
||||
static TextRenderer* inconsolataRenderer = new TextRenderer(INCONSOLATA_FONT_FAMILY, -1, QFont::Bold, false);
|
||||
static TextRenderer* monoRenderer = TextRenderer::getInstance(MONO_FONT_FAMILY);
|
||||
static TextRenderer* proportionalRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY,
|
||||
-1, -1, false, TextRenderer::SHADOW_EFFECT);
|
||||
static TextRenderer* inconsolataRenderer = TextRenderer::getInstance(INCONSOLATA_FONT_FAMILY, -1, QFont::Bold, false);
|
||||
switch (mono) {
|
||||
case 1:
|
||||
return monoRenderer;
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
#include <QDesktopWidget>
|
||||
#include <QWindow>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <glm/gtx/vector_angle.hpp>
|
||||
|
@ -50,8 +53,8 @@ Avatar::Avatar() :
|
|||
AvatarData(),
|
||||
_skeletonModel(this),
|
||||
_bodyYawDelta(0.0f),
|
||||
_lastPosition(0.0f),
|
||||
_velocity(0.0f),
|
||||
_positionDeltaAccumulator(0.0f),
|
||||
_lastVelocity(0.0f),
|
||||
_acceleration(0.0f),
|
||||
_angularVelocity(0.0f),
|
||||
|
@ -204,15 +207,31 @@ void Avatar::simulate(float deltaTime) {
|
|||
_displayNameAlpha = abs(_displayNameAlpha - _displayNameTargetAlpha) < 0.01f ? _displayNameTargetAlpha : _displayNameAlpha;
|
||||
}
|
||||
|
||||
_position += _velocity * deltaTime;
|
||||
// NOTE: we shouldn't extrapolate an Avatar instance forward in time...
|
||||
// until velocity is included in AvatarData update message.
|
||||
//_position += _velocity * deltaTime;
|
||||
measureMotionDerivatives(deltaTime);
|
||||
}
|
||||
|
||||
void Avatar::slamPosition(const glm::vec3& newPosition) {
|
||||
AvatarData::setPosition(newPosition);
|
||||
_positionDeltaAccumulator = glm::vec3(0.0f);
|
||||
_velocity = glm::vec3(0.0f);
|
||||
_lastVelocity = glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
void Avatar::applyPositionDelta(const glm::vec3& delta) {
|
||||
_position += delta;
|
||||
_positionDeltaAccumulator += delta;
|
||||
}
|
||||
|
||||
void Avatar::measureMotionDerivatives(float deltaTime) {
|
||||
// linear
|
||||
float invDeltaTime = 1.0f / deltaTime;
|
||||
_velocity = (_position - _lastPosition) * invDeltaTime;
|
||||
_lastPosition = _position;
|
||||
// Floating point error prevents us from computing velocity in a naive way
|
||||
// (e.g. vel = (pos - oldPos) / dt) so instead we use _positionOffsetAccumulator.
|
||||
_velocity = _positionDeltaAccumulator * invDeltaTime;
|
||||
_positionDeltaAccumulator = glm::vec3(0.0f);
|
||||
_acceleration = (_velocity - _lastVelocity) * invDeltaTime;
|
||||
_lastVelocity = _velocity;
|
||||
// angular
|
||||
|
@ -223,20 +242,6 @@ void Avatar::measureMotionDerivatives(float deltaTime) {
|
|||
_lastOrientation = getOrientation();
|
||||
}
|
||||
|
||||
void Avatar::setPosition(const glm::vec3 position, bool overideReferential) {
|
||||
AvatarData::setPosition(position, overideReferential);
|
||||
_lastPosition = position;
|
||||
_velocity = glm::vec3(0.0f);
|
||||
_lastVelocity = glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
void Avatar::slamPosition(const glm::vec3& newPosition) {
|
||||
_position = newPosition;
|
||||
_lastPosition = newPosition;
|
||||
_velocity = glm::vec3(0.0f);
|
||||
_lastVelocity = glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
void Avatar::setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction) {
|
||||
_mouseRayOrigin = origin;
|
||||
_mouseRayDirection = direction;
|
||||
|
@ -248,8 +253,9 @@ enum TextRendererType {
|
|||
};
|
||||
|
||||
static TextRenderer* textRenderer(TextRendererType type) {
|
||||
static TextRenderer* chatRenderer = new TextRenderer(SANS_FONT_FAMILY, 24, -1, false, TextRenderer::SHADOW_EFFECT);
|
||||
static TextRenderer* displayNameRenderer = new TextRenderer(SANS_FONT_FAMILY, 12, -1, false, TextRenderer::NO_EFFECT);
|
||||
static TextRenderer* chatRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, 24, -1,
|
||||
false, TextRenderer::SHADOW_EFFECT);
|
||||
static TextRenderer* displayNameRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, 12);
|
||||
|
||||
switch(type) {
|
||||
case CHAT:
|
||||
|
@ -667,7 +673,8 @@ void Avatar::renderDisplayName() {
|
|||
|
||||
if (success) {
|
||||
double textWindowHeight = abs(result1[1] - result0[1]);
|
||||
float scaleFactor = (textWindowHeight > EPSILON) ? 1.0f / textWindowHeight : 1.0f;
|
||||
float scaleFactor = QApplication::desktop()->windowHandle()->devicePixelRatio() *
|
||||
((textWindowHeight > EPSILON) ? 1.0f / textWindowHeight : 1.0f);
|
||||
glScalef(scaleFactor, scaleFactor, 1.0);
|
||||
|
||||
glScalef(1.0f, -1.0f, 1.0f); // TextRenderer::draw paints the text upside down in y axis
|
||||
|
|
|
@ -161,9 +161,13 @@ public:
|
|||
/// \param vector position to be scaled. Will store the result
|
||||
void scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const;
|
||||
|
||||
void setPosition(const glm::vec3 position, bool overideReferential = false);
|
||||
void slamPosition(const glm::vec3& newPosition);
|
||||
|
||||
void slamPosition(const glm::vec3& position);
|
||||
|
||||
// Call this when updating Avatar position with a delta. This will allow us to
|
||||
// _accurately_ measure position changes and compute the resulting velocity
|
||||
// (otherwise floating point error will cause problems at large positions).
|
||||
void applyPositionDelta(const glm::vec3& delta);
|
||||
|
||||
public slots:
|
||||
void updateCollisionGroups();
|
||||
|
||||
|
@ -176,12 +180,15 @@ protected:
|
|||
QVector<Model*> _attachmentModels;
|
||||
float _bodyYawDelta;
|
||||
|
||||
glm::vec3 _velocity;
|
||||
|
||||
// These position histories and derivatives are in the world-frame.
|
||||
// The derivatives are the MEASURED results of all external and internal forces
|
||||
// and are therefor READ-ONLY --> motion control of the Avatar is NOT obtained
|
||||
// by setting these values.
|
||||
glm::vec3 _lastPosition;
|
||||
glm::vec3 _velocity;
|
||||
// Floating point error prevents us from accurately measuring velocity using a naive approach
|
||||
// (e.g. vel = (pos - lastPos)/dt) so instead we use _positionDeltaAccumulator.
|
||||
glm::vec3 _positionDeltaAccumulator;
|
||||
glm::vec3 _lastVelocity;
|
||||
glm::vec3 _acceleration;
|
||||
glm::vec3 _angularVelocity;
|
||||
|
|
|
@ -33,7 +33,7 @@ ModelReferential::ModelReferential(Referential* referential, EntityTree* tree, A
|
|||
|
||||
const EntityItem* item = _tree->findEntityByID(_entityID);
|
||||
if (item != NULL) {
|
||||
_refScale = item->getRadius();
|
||||
_refScale = item->getLargestDimension();
|
||||
_refRotation = item->getRotation();
|
||||
_refPosition = item->getPosition() * (float)TREE_SCALE;
|
||||
update();
|
||||
|
@ -52,7 +52,7 @@ ModelReferential::ModelReferential(const QUuid& entityID, EntityTree* tree, Avat
|
|||
return;
|
||||
}
|
||||
|
||||
_refScale = item->getRadius();
|
||||
_refScale = item->getLargestDimension();
|
||||
_refRotation = item->getRotation();
|
||||
_refPosition = item->getPosition() * (float)TREE_SCALE;
|
||||
|
||||
|
@ -69,8 +69,8 @@ void ModelReferential::update() {
|
|||
}
|
||||
|
||||
bool somethingChanged = false;
|
||||
if (item->getRadius() != _refScale) {
|
||||
_refScale = item->getRadius();
|
||||
if (item->getLargestDimension() != _refScale) {
|
||||
_refScale = item->getLargestDimension();
|
||||
_avatar->setTargetScale(_refScale * _scale, true);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ JointReferential::JointReferential(Referential* referential, EntityTree* tree, A
|
|||
const EntityItem* item = _tree->findEntityByID(_entityID);
|
||||
const Model* model = getModel(item);
|
||||
if (!isValid() || model == NULL || _jointIndex >= (uint32_t)(model->getJointStateCount())) {
|
||||
_refScale = item->getRadius();
|
||||
_refScale = item->getLargestDimension();
|
||||
model->getJointRotationInWorldFrame(_jointIndex, _refRotation);
|
||||
model->getJointPositionInWorldFrame(_jointIndex, _refPosition);
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ JointReferential::JointReferential(uint32_t jointIndex, const QUuid& entityID, E
|
|||
return;
|
||||
}
|
||||
|
||||
_refScale = item->getRadius();
|
||||
_refScale = item->getLargestDimension();
|
||||
model->getJointRotationInWorldFrame(_jointIndex, _refRotation);
|
||||
model->getJointPositionInWorldFrame(_jointIndex, _refPosition);
|
||||
|
||||
|
@ -147,8 +147,8 @@ void JointReferential::update() {
|
|||
}
|
||||
|
||||
bool somethingChanged = false;
|
||||
if (item->getRadius() != _refScale) {
|
||||
_refScale = item->getRadius();
|
||||
if (item->getLargestDimension() != _refScale) {
|
||||
_refScale = item->getLargestDimension();
|
||||
_avatar->setTargetScale(_refScale * _scale, true);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <QtCore/QTimer>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <AddressManager.h>
|
||||
#include <GeometryUtil.h>
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
|
@ -90,6 +91,9 @@ MyAvatar::MyAvatar() :
|
|||
|
||||
_skeletonModel.setEnableShapes(true);
|
||||
_skeletonModel.buildRagdoll();
|
||||
|
||||
// connect to AddressManager signal for location jumps
|
||||
connect(&AddressManager::getInstance(), &AddressManager::locationChangeRequired, this, &MyAvatar::goToLocation);
|
||||
}
|
||||
|
||||
MyAvatar::~MyAvatar() {
|
||||
|
@ -226,7 +230,7 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
const float MAX_RAGDOLL_DISPLACEMENT_2 = 1.0f;
|
||||
float length2 = glm::length2(ragdollDisplacement);
|
||||
if (length2 > EPSILON && length2 < MAX_RAGDOLL_DISPLACEMENT_2) {
|
||||
setPosition(getPosition() + ragdollDisplacement);
|
||||
applyPositionDelta(ragdollDisplacement);
|
||||
}
|
||||
} else {
|
||||
_skeletonModel.moveShapesTowardJoints(1.0f);
|
||||
|
@ -796,9 +800,11 @@ void MyAvatar::loadData(QSettings* settings) {
|
|||
|
||||
getHead()->setBasePitch(loadSetting(settings, "headPitch", 0.0f));
|
||||
|
||||
_position.x = loadSetting(settings, "position_x", START_LOCATION.x);
|
||||
_position.y = loadSetting(settings, "position_y", START_LOCATION.y);
|
||||
_position.z = loadSetting(settings, "position_z", START_LOCATION.z);
|
||||
glm::vec3 newPosition;
|
||||
newPosition.x = loadSetting(settings, "position_x", START_LOCATION.x);
|
||||
newPosition.y = loadSetting(settings, "position_y", START_LOCATION.y);
|
||||
newPosition.z = loadSetting(settings, "position_z", START_LOCATION.z);
|
||||
slamPosition(newPosition);
|
||||
|
||||
getHead()->setPupilDilation(loadSetting(settings, "pupilDilation", 0.0f));
|
||||
|
||||
|
@ -1265,15 +1271,11 @@ void MyAvatar::updatePosition(float deltaTime) {
|
|||
if (_motionBehaviors & AVATAR_MOTION_MOTOR_ENABLED) {
|
||||
glm::vec3 targetVelocity = _motorVelocity;
|
||||
if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) {
|
||||
// rotate _motorVelocity into world frame
|
||||
// rotate targetVelocity into world frame
|
||||
glm::quat rotation = getHead()->getCameraOrientation();
|
||||
targetVelocity = rotation * _motorVelocity;
|
||||
}
|
||||
|
||||
glm::vec3 targetDirection(0.0f);
|
||||
if (glm::length2(targetVelocity) > EPSILON) {
|
||||
targetDirection = glm::normalize(targetVelocity);
|
||||
}
|
||||
glm::vec3 deltaVelocity = targetVelocity - velocity;
|
||||
|
||||
if (_motionBehaviors & AVATAR_MOTION_MOTOR_COLLISION_SURFACE_ONLY && glm::length2(_gravity) > EPSILON) {
|
||||
|
@ -1305,7 +1307,7 @@ void MyAvatar::updatePosition(float deltaTime) {
|
|||
// update position
|
||||
const float MIN_AVATAR_SPEED = 0.075f;
|
||||
if (speed > MIN_AVATAR_SPEED) {
|
||||
_position += velocity * deltaTime;
|
||||
applyPositionDelta(deltaTime * velocity);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1783,11 +1785,6 @@ void MyAvatar::maybeUpdateBillboard() {
|
|||
sendBillboardPacket();
|
||||
}
|
||||
|
||||
void MyAvatar::goHome() {
|
||||
qDebug("Going Home!");
|
||||
slamPosition(START_LOCATION);
|
||||
}
|
||||
|
||||
void MyAvatar::increaseSize() {
|
||||
if ((1.0f + SCALING_RATIO) * _targetScale < MAX_AVATAR_SCALE) {
|
||||
_targetScale *= (1.0f + SCALING_RATIO);
|
||||
|
@ -1807,45 +1804,26 @@ void MyAvatar::resetSize() {
|
|||
qDebug("Reseted scale to %f", _targetScale);
|
||||
}
|
||||
|
||||
void MyAvatar::goToLocationFromResponse(const QJsonObject& jsonObject) {
|
||||
QJsonObject locationObject = jsonObject["data"].toObject()["address"].toObject();
|
||||
bool isOnline = jsonObject["data"].toObject()["online"].toBool();
|
||||
if (isOnline ) {
|
||||
goToLocationFromAddress(locationObject);
|
||||
} else {
|
||||
QMessageBox::warning(Application::getInstance()->getWindow(), "", "The user is not online.");
|
||||
void MyAvatar::goToLocation(const glm::vec3& newPosition, bool hasOrientation, const glm::vec3& newOrientation) {
|
||||
glm::quat quatOrientation = getOrientation();
|
||||
|
||||
qDebug().nospace() << "MyAvatar goToLocation - moving to " << newPosition.x << ", "
|
||||
<< newPosition.y << ", " << newPosition.z;
|
||||
|
||||
if (hasOrientation) {
|
||||
qDebug().nospace() << "MyAvatar goToLocation - new orientation is "
|
||||
<< newOrientation.x << ", " << newOrientation.y << ", " << newOrientation.z;
|
||||
|
||||
// orient the user to face the target
|
||||
glm::quat quatOrientation = glm::quat(glm::radians(newOrientation))
|
||||
* glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
setOrientation(quatOrientation);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::goToLocationFromAddress(const QJsonObject& locationObject) {
|
||||
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
|
||||
sendKillAvatar();
|
||||
|
||||
QString positionString = locationObject["position"].toString();
|
||||
QString orientationString = locationObject["orientation"].toString();
|
||||
QString domainHostnameString = locationObject["domain"].toString();
|
||||
|
||||
qDebug() << "Changing domain to" << domainHostnameString <<
|
||||
", position to" << positionString <<
|
||||
", and orientation to" << orientationString;
|
||||
|
||||
QStringList coordinateItems = positionString.split(',');
|
||||
QStringList orientationItems = orientationString.split(',');
|
||||
|
||||
NodeList::getInstance()->getDomainHandler().setHostname(domainHostnameString);
|
||||
|
||||
// orient the user to face the target
|
||||
glm::quat newOrientation = glm::quat(glm::radians(glm::vec3(orientationItems[0].toFloat(),
|
||||
orientationItems[1].toFloat(),
|
||||
orientationItems[2].toFloat())))
|
||||
* glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
setOrientation(newOrientation);
|
||||
|
||||
// move the user a couple units away
|
||||
const float DISTANCE_TO_USER = 2.0f;
|
||||
glm::vec3 newPosition = glm::vec3(coordinateItems[0].toFloat(), coordinateItems[1].toFloat(),
|
||||
coordinateItems[2].toFloat()) - newOrientation * IDENTITY_FRONT * DISTANCE_TO_USER;
|
||||
slamPosition(newPosition);
|
||||
glm::vec3 shiftedPosition = newPosition - quatOrientation * IDENTITY_FRONT * DISTANCE_TO_USER;
|
||||
slamPosition(shiftedPosition);
|
||||
emit transformChanged();
|
||||
}
|
||||
|
||||
|
@ -1992,3 +1970,9 @@ glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) {
|
|||
|
||||
return palm->getPosition();
|
||||
}
|
||||
|
||||
void MyAvatar::clearDriveKeys() {
|
||||
for (int i = 0; i < MAX_DRIVE_KEYS; ++i) {
|
||||
_driveKeys[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,6 +97,7 @@ public:
|
|||
AttachmentData loadAttachmentData(const QUrl& modelURL, const QString& jointName = QString()) const;
|
||||
|
||||
// Set what driving keys are being pressed to control thrust levels
|
||||
void clearDriveKeys();
|
||||
void setDriveKeys(int key, float val) { _driveKeys[key] = val; };
|
||||
bool getDriveKeys(int key) { return _driveKeys[key] != 0.f; };
|
||||
void jump() { _shouldJump = true; };
|
||||
|
@ -149,19 +150,19 @@ public:
|
|||
const PlayerPointer getPlayer() const { return _player; }
|
||||
|
||||
public slots:
|
||||
void goHome();
|
||||
void increaseSize();
|
||||
void decreaseSize();
|
||||
void resetSize();
|
||||
|
||||
void goToLocationFromResponse(const QJsonObject& jsonObject);
|
||||
void goToLocationFromAddress(const QJsonObject& jsonObject);
|
||||
void goToLocation(const glm::vec3& newPosition, bool hasOrientation = false, const glm::vec3& newOrientation = glm::vec3());
|
||||
|
||||
// Set/Get update the thrust that will move the avatar around
|
||||
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
|
||||
glm::vec3 getThrust() { return _thrust; };
|
||||
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
|
||||
|
||||
void setVelocity(const glm::vec3 velocity) { _velocity = velocity; }
|
||||
|
||||
void updateMotionBehaviorsFromMenu();
|
||||
void onToggleRagdoll();
|
||||
|
||||
|
|
|
@ -60,6 +60,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
setRotation(_owningAvatar->getOrientation() * refOrientation);
|
||||
const float MODEL_SCALE = 0.0006f;
|
||||
setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale() * MODEL_SCALE);
|
||||
setBlendshapeCoefficients(_owningAvatar->getHead()->getBlendshapeCoefficients());
|
||||
|
||||
Model::simulate(deltaTime, fullUpdate);
|
||||
|
||||
|
|
|
@ -151,7 +151,7 @@ void Faceshift::connectSocket() {
|
|||
qDebug("Faceshift: Connecting...");
|
||||
}
|
||||
|
||||
_tcpSocket.connectToHost("localhost", FACESHIFT_PORT);
|
||||
_tcpSocket.connectToHost(Menu::getInstance()->getFaceshiftHostname(), FACESHIFT_PORT);
|
||||
_tracking = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ unsigned int OculusManager::_frameIndex = 0;
|
|||
bool OculusManager::_frameTimingActive = false;
|
||||
bool OculusManager::_programInitialized = false;
|
||||
Camera* OculusManager::_camera = NULL;
|
||||
int OculusManager::_activeEyeIndex = -1;
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -330,6 +331,8 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p
|
|||
|
||||
//Render each eye into an fbo
|
||||
for (int eyeIndex = 0; eyeIndex < ovrEye_Count; eyeIndex++) {
|
||||
_activeEyeIndex = eyeIndex;
|
||||
|
||||
#if defined(__APPLE__) || defined(_WIN32)
|
||||
ovrEyeType eye = _ovrHmd->EyeRenderOrder[eyeIndex];
|
||||
#else
|
||||
|
@ -363,6 +366,7 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p
|
|||
Application::getInstance()->displaySide(*_camera);
|
||||
|
||||
applicationOverlay.displayOverlayTextureOculus(*_camera);
|
||||
_activeEyeIndex = -1;
|
||||
}
|
||||
|
||||
//Wait till time-warp to reduce latency
|
||||
|
@ -528,3 +532,16 @@ QSize OculusManager::getRenderTargetSize() {
|
|||
return QSize(100, 100);
|
||||
#endif
|
||||
}
|
||||
|
||||
void OculusManager::overrideOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal,
|
||||
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) {
|
||||
#ifdef HAVE_LIBOVR
|
||||
if (_activeEyeIndex != -1) {
|
||||
const ovrFovPort& port = _eyeFov[_activeEyeIndex];
|
||||
right = nearVal * port.RightTan;
|
||||
left = -nearVal * port.LeftTan;
|
||||
top = nearVal * port.UpTan;
|
||||
bottom = -nearVal * port.DownTan;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -43,6 +43,9 @@ public:
|
|||
static glm::vec3 getRelativePosition();
|
||||
static QSize getRenderTargetSize();
|
||||
|
||||
static void overrideOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal,
|
||||
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane);
|
||||
|
||||
private:
|
||||
#ifdef HAVE_LIBOVR
|
||||
static void generateDistortionMesh();
|
||||
|
@ -92,6 +95,7 @@ private:
|
|||
static bool _frameTimingActive;
|
||||
static bool _programInitialized;
|
||||
static Camera* _camera;
|
||||
static int _activeEyeIndex;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -207,10 +207,11 @@ void PrioVR::renderCalibrationCountdown() {
|
|||
Application::getInstance()->disconnect(this);
|
||||
return;
|
||||
}
|
||||
static TextRenderer textRenderer(MONO_FONT_FAMILY, 18, QFont::Bold, false, TextRenderer::OUTLINE_EFFECT, 2);
|
||||
static TextRenderer* textRenderer = TextRenderer::getInstance(MONO_FONT_FAMILY, 18, QFont::Bold,
|
||||
false, TextRenderer::OUTLINE_EFFECT, 2);
|
||||
QByteArray text = "Assume T-Pose in " + QByteArray::number(secondsRemaining) + "...";
|
||||
textRenderer.draw((Application::getInstance()->getGLWidget()->getDeviceWidth() -
|
||||
textRenderer.computeWidth(text.constData())) / 2, Application::getInstance()->getGLWidget()->getDeviceHeight() / 2,
|
||||
textRenderer->draw((Application::getInstance()->getGLWidget()->width() -
|
||||
textRenderer->computeWidth(text.constData())) / 2, Application::getInstance()->getGLWidget()->height() / 2,
|
||||
text);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -396,10 +396,10 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) {
|
|||
float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2));
|
||||
|
||||
// Get the pixel range over which the xAngle and yAngle are scaled
|
||||
float cursorRange = widget->getDeviceWidth() * getCursorPixelRangeMult();
|
||||
float cursorRange = widget->width() * getCursorPixelRangeMult();
|
||||
|
||||
pos.setX(widget->getDeviceWidth() / 2.0f + cursorRange * xAngle);
|
||||
pos.setY(widget->getDeviceHeight() / 2.0f + cursorRange * yAngle);
|
||||
pos.setX(widget->width() / 2.0f + cursorRange * xAngle);
|
||||
pos.setY(widget->height() / 2.0f + cursorRange * yAngle);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ int TV3DManager::_screenHeight = 1;
|
|||
double TV3DManager::_aspect = 1.0;
|
||||
eyeFrustum TV3DManager::_leftEye;
|
||||
eyeFrustum TV3DManager::_rightEye;
|
||||
eyeFrustum* TV3DManager::_activeEye = NULL;
|
||||
|
||||
|
||||
bool TV3DManager::isConnected() {
|
||||
|
@ -93,8 +94,6 @@ void TV3DManager::display(Camera& whichCamera) {
|
|||
int portalW = Application::getInstance()->getGLWidget()->getDeviceWidth() / 2;
|
||||
int portalH = Application::getInstance()->getGLWidget()->getDeviceHeight();
|
||||
|
||||
const bool glowEnabled = Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect);
|
||||
|
||||
ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay();
|
||||
|
||||
// We only need to render the overlays to a texture once, then we just render the texture as a quad
|
||||
|
@ -102,9 +101,7 @@ void TV3DManager::display(Camera& whichCamera) {
|
|||
applicationOverlay.renderOverlay(true);
|
||||
const bool displayOverlays = Menu::getInstance()->isOptionChecked(MenuOption::UserInterface);
|
||||
|
||||
if (glowEnabled) {
|
||||
Application::getInstance()->getGlowEffect()->prepare();
|
||||
}
|
||||
Application::getInstance()->getGlowEffect()->prepare();
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
|
@ -115,7 +112,7 @@ void TV3DManager::display(Camera& whichCamera) {
|
|||
|
||||
glPushMatrix();
|
||||
{
|
||||
|
||||
_activeEye = &_leftEye;
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity(); // reset projection matrix
|
||||
glFrustum(_leftEye.left, _leftEye.right, _leftEye.bottom, _leftEye.top, nearZ, farZ); // set left view frustum
|
||||
|
@ -132,6 +129,7 @@ void TV3DManager::display(Camera& whichCamera) {
|
|||
if (displayOverlays) {
|
||||
applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov);
|
||||
}
|
||||
_activeEye = NULL;
|
||||
}
|
||||
glPopMatrix();
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
@ -144,6 +142,7 @@ void TV3DManager::display(Camera& whichCamera) {
|
|||
glScissor(portalX, portalY, portalW, portalH);
|
||||
glPushMatrix();
|
||||
{
|
||||
_activeEye = &_rightEye;
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity(); // reset projection matrix
|
||||
glFrustum(_rightEye.left, _rightEye.right, _rightEye.bottom, _rightEye.top, nearZ, farZ); // set left view frustum
|
||||
|
@ -160,6 +159,7 @@ void TV3DManager::display(Camera& whichCamera) {
|
|||
if (displayOverlays) {
|
||||
applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov);
|
||||
}
|
||||
_activeEye = NULL;
|
||||
}
|
||||
glPopMatrix();
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
@ -168,7 +168,15 @@ void TV3DManager::display(Camera& whichCamera) {
|
|||
glViewport(0, 0, Application::getInstance()->getGLWidget()->getDeviceWidth(),
|
||||
Application::getInstance()->getGLWidget()->getDeviceHeight());
|
||||
|
||||
if (glowEnabled) {
|
||||
Application::getInstance()->getGlowEffect()->render();
|
||||
Application::getInstance()->getGlowEffect()->render();
|
||||
}
|
||||
|
||||
void TV3DManager::overrideOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal,
|
||||
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) {
|
||||
if (_activeEye) {
|
||||
left = _activeEye->left;
|
||||
right = _activeEye->right;
|
||||
bottom = _activeEye->bottom;
|
||||
top = _activeEye->top;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include <iostream>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
class Camera;
|
||||
|
||||
struct eyeFrustum {
|
||||
|
@ -32,6 +34,8 @@ public:
|
|||
static bool isConnected();
|
||||
static void configureCamera(Camera& camera, int screenWidth, int screenHeight);
|
||||
static void display(Camera& whichCamera);
|
||||
static void overrideOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal,
|
||||
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane);
|
||||
private:
|
||||
static void setFrustum(Camera& whichCamera);
|
||||
static int _screenWidth;
|
||||
|
@ -39,6 +43,7 @@ private:
|
|||
static double _aspect;
|
||||
static eyeFrustum _leftEye;
|
||||
static eyeFrustum _rightEye;
|
||||
static eyeFrustum* _activeEye;
|
||||
};
|
||||
|
||||
#endif // hifi_TV3DManager_h
|
||||
|
|
|
@ -192,7 +192,71 @@ bool EntityTreeRenderer::shouldRenderEntity(float largestDimension, float distan
|
|||
return (distanceToCamera <= visibleDistanceAtScale);
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::renderProxies(const EntityItem* entity, RenderArgs* args) {
|
||||
bool isShadowMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE;
|
||||
bool displayModelBounds = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelBounds);
|
||||
if (!isShadowMode && displayModelBounds) {
|
||||
PerformanceTimer perfTimer("renderProxies");
|
||||
|
||||
AACube maxCube = entity->getMaximumAACube();
|
||||
AACube minCube = entity->getMinimumAACube();
|
||||
AABox entityBox = entity->getAABox();
|
||||
|
||||
maxCube.scale((float)TREE_SCALE);
|
||||
minCube.scale((float)TREE_SCALE);
|
||||
entityBox.scale((float)TREE_SCALE);
|
||||
|
||||
glm::vec3 maxCenter = maxCube.calcCenter();
|
||||
glm::vec3 minCenter = minCube.calcCenter();
|
||||
glm::vec3 entityBoxCenter = entityBox.calcCenter();
|
||||
glm::vec3 entityBoxScale = entityBox.getScale();
|
||||
|
||||
// draw the max bounding cube
|
||||
glColor4f(1.0f, 1.0f, 0.0f, 1.0f);
|
||||
glPushMatrix();
|
||||
glTranslatef(maxCenter.x, maxCenter.y, maxCenter.z);
|
||||
glutWireCube(maxCube.getScale());
|
||||
glPopMatrix();
|
||||
|
||||
// draw the min bounding cube
|
||||
glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
glPushMatrix();
|
||||
glTranslatef(minCenter.x, minCenter.y, minCenter.z);
|
||||
glutWireCube(minCube.getScale());
|
||||
glPopMatrix();
|
||||
|
||||
// draw the entityBox bounding box
|
||||
glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
glPushMatrix();
|
||||
glTranslatef(entityBoxCenter.x, entityBoxCenter.y, entityBoxCenter.z);
|
||||
glScalef(entityBoxScale.x, entityBoxScale.y, entityBoxScale.z);
|
||||
glutWireCube(1.0f);
|
||||
glPopMatrix();
|
||||
|
||||
|
||||
glm::vec3 position = entity->getPosition() * (float)TREE_SCALE;
|
||||
glm::vec3 center = entity->getCenter() * (float)TREE_SCALE;
|
||||
glm::vec3 dimensions = entity->getDimensions() * (float)TREE_SCALE;
|
||||
glm::quat rotation = entity->getRotation();
|
||||
|
||||
glColor4f(1.0f, 0.0f, 1.0f, 1.0f);
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
glPushMatrix();
|
||||
glm::vec3 positionToCenter = center - position;
|
||||
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
|
||||
glScalef(dimensions.x, dimensions.y, dimensions.z);
|
||||
glutWireCube(1.0f);
|
||||
glPopMatrix();
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) {
|
||||
bool wantDebug = false;
|
||||
|
||||
args->_elementsTouched++;
|
||||
// actually render it here...
|
||||
// we need to iterate the actual entityItems of the element
|
||||
|
@ -214,26 +278,48 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
|
|||
|
||||
for (uint16_t i = 0; i < numberOfEntities; i++) {
|
||||
EntityItem* entityItem = entityItems[i];
|
||||
// render entityItem
|
||||
AACube entityCube = entityItem->getAACube();
|
||||
entityCube.scale(TREE_SCALE);
|
||||
|
||||
// TODO: some entity types (like lights) might want to be rendered even
|
||||
// when they are outside of the view frustum...
|
||||
float distance = distanceToCamera(entityCube.calcCenter(), *args->_viewFrustum);
|
||||
if (shouldRenderEntity(entityCube.getLargestDimension(), distance) &&
|
||||
args->_viewFrustum->cubeInFrustum(entityCube) != ViewFrustum::OUTSIDE) {
|
||||
if (entityItem->isVisible()) {
|
||||
// render entityItem
|
||||
AABox entityBox = entityItem->getAABox();
|
||||
|
||||
Glower* glower = NULL;
|
||||
if (entityItem->getGlowLevel() > 0.0f) {
|
||||
glower = new Glower(entityItem->getGlowLevel());
|
||||
entityBox.scale(TREE_SCALE);
|
||||
|
||||
// TODO: some entity types (like lights) might want to be rendered even
|
||||
// when they are outside of the view frustum...
|
||||
float distance = distanceToCamera(entityBox.calcCenter(), *args->_viewFrustum);
|
||||
|
||||
if (wantDebug) {
|
||||
qDebug() << "------- renderElement() ----------";
|
||||
qDebug() << " type:" << EntityTypes::getEntityTypeName(entityItem->getType());
|
||||
if (entityItem->getType() == EntityTypes::Model) {
|
||||
ModelEntityItem* modelEntity = static_cast<ModelEntityItem*>(entityItem);
|
||||
qDebug() << " url:" << modelEntity->getModelURL();
|
||||
}
|
||||
qDebug() << " entityBox:" << entityBox;
|
||||
qDebug() << " dimensions:" << entityItem->getDimensionsInMeters() << "in meters";
|
||||
qDebug() << " largestDimension:" << entityBox.getLargestDimension() << "in meters";
|
||||
qDebug() << " shouldRender:" << shouldRenderEntity(entityBox.getLargestDimension(), distance);
|
||||
qDebug() << " in frustum:" << (args->_viewFrustum->boxInFrustum(entityBox) != ViewFrustum::OUTSIDE);
|
||||
}
|
||||
entityItem->render(args);
|
||||
if (glower) {
|
||||
delete glower;
|
||||
|
||||
if (shouldRenderEntity(entityBox.getLargestDimension(), distance) &&
|
||||
args->_viewFrustum->boxInFrustum(entityBox) != ViewFrustum::OUTSIDE) {
|
||||
|
||||
|
||||
renderProxies(entityItem, args);
|
||||
|
||||
Glower* glower = NULL;
|
||||
if (entityItem->getGlowLevel() > 0.0f) {
|
||||
glower = new Glower(entityItem->getGlowLevel());
|
||||
}
|
||||
entityItem->render(args);
|
||||
if (glower) {
|
||||
delete glower;
|
||||
}
|
||||
} else {
|
||||
args->_itemsOutOfView++;
|
||||
}
|
||||
} else {
|
||||
args->_itemsOutOfView++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,6 +79,7 @@ private:
|
|||
|
||||
float distanceToCamera(const glm::vec3& center, const ViewFrustum& viewFrustum) const;
|
||||
bool shouldRenderEntity(float largestDimension, float distanceToCamera) const;
|
||||
void renderProxies(const EntityItem* entity, RenderArgs* args);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -32,12 +32,13 @@ EntityItem* RenderableBoxEntityItem::factory(const EntityItemID& entityID, const
|
|||
void RenderableBoxEntityItem::render(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RenderableBoxEntityItem::render");
|
||||
assert(getType() == EntityTypes::Box);
|
||||
glm::vec3 position = getPosition() * (float)TREE_SCALE;
|
||||
float size = getSize() * (float)TREE_SCALE;
|
||||
glm::vec3 position = getPositionInMeters();
|
||||
glm::vec3 center = getCenter() * (float)TREE_SCALE;
|
||||
glm::vec3 dimensions = getDimensions() * (float)TREE_SCALE;
|
||||
glm::vec3 halfDimensions = dimensions / 2.0f;
|
||||
glm::quat rotation = getRotation();
|
||||
|
||||
|
||||
const bool useGlutCube = false;
|
||||
const bool useGlutCube = true;
|
||||
|
||||
if (useGlutCube) {
|
||||
glColor3ub(getColor()[RED_INDEX], getColor()[GREEN_INDEX], getColor()[BLUE_INDEX]);
|
||||
|
@ -45,10 +46,14 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
|
|||
glTranslatef(position.x, position.y, position.z);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
glutSolidCube(size);
|
||||
glPushMatrix();
|
||||
glm::vec3 positionToCenter = center - position;
|
||||
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
|
||||
glScalef(dimensions.x, dimensions.y, dimensions.z);
|
||||
glutSolidCube(1.0f);
|
||||
glPopMatrix();
|
||||
glPopMatrix();
|
||||
} else {
|
||||
|
||||
static GLfloat vertices[] = { 1, 1, 1, -1, 1, 1, -1,-1, 1, 1,-1, 1, // v0,v1,v2,v3 (front)
|
||||
1, 1, 1, 1,-1, 1, 1,-1,-1, 1, 1,-1, // v0,v3,v4,v5 (right)
|
||||
1, 1, 1, 1, 1,-1, -1, 1,-1, -1, 1, 1, // v0,v5,v6,v1 (top)
|
||||
|
@ -79,20 +84,19 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
|
|||
glNormalPointer(GL_FLOAT, 0, normals);
|
||||
glVertexPointer(3, GL_FLOAT, 0, vertices);
|
||||
|
||||
//glEnable(GL_BLEND);
|
||||
|
||||
glColor3ub(getColor()[RED_INDEX], getColor()[GREEN_INDEX], getColor()[BLUE_INDEX]);
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
|
||||
// we need to do half the size because the geometry in the VBOs are from -1,-1,-1 to 1,1,1
|
||||
float halfSize = size/2.0f;
|
||||
|
||||
glScalef(halfSize, halfSize, halfSize);
|
||||
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, indices);
|
||||
glPushMatrix();
|
||||
glm::vec3 positionToCenter = center - position;
|
||||
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
|
||||
// we need to do half the size because the geometry in the VBOs are from -1,-1,-1 to 1,1,1
|
||||
glScalef(halfDimensions.x, halfDimensions.y, halfDimensions.z);
|
||||
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, indices);
|
||||
glPopMatrix();
|
||||
glPopMatrix();
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY); // disable vertex arrays
|
||||
|
|
|
@ -65,8 +65,8 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
bool drawAsModel = hasModel();
|
||||
|
||||
glm::vec3 position = getPosition() * (float)TREE_SCALE;
|
||||
float radius = getRadius() * (float)TREE_SCALE;
|
||||
float size = getSize() * (float)TREE_SCALE;
|
||||
glm::vec3 dimensions = getDimensions() * (float)TREE_SCALE;
|
||||
|
||||
if (drawAsModel) {
|
||||
glPushMatrix();
|
||||
|
@ -98,8 +98,8 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
|
||||
glm::quat rotation = getRotation();
|
||||
if (needsSimulation() && _model->isActive()) {
|
||||
_model->setScaleToFit(true, radius * 2.0f);
|
||||
_model->setSnapModelToCenter(true);
|
||||
_model->setScaleToFit(true, dimensions);
|
||||
_model->setSnapModelToRegistrationPoint(true, getRegistrationPoint());
|
||||
_model->setRotation(rotation);
|
||||
_model->setTranslation(position);
|
||||
|
||||
|
@ -128,52 +128,6 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
glutWireCube(size);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
bool isShadowMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE;
|
||||
bool displayModelBounds = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelBounds);
|
||||
|
||||
if (!isShadowMode && displayModelBounds) {
|
||||
PerformanceTimer perfTimer("displayModelBounds");
|
||||
|
||||
glm::vec3 unRotatedMinimum = _model->getUnscaledMeshExtents().minimum;
|
||||
glm::vec3 unRotatedMaximum = _model->getUnscaledMeshExtents().maximum;
|
||||
glm::vec3 unRotatedExtents = unRotatedMaximum - unRotatedMinimum;
|
||||
|
||||
float width = unRotatedExtents.x;
|
||||
float height = unRotatedExtents.y;
|
||||
float depth = unRotatedExtents.z;
|
||||
|
||||
Extents rotatedExtents = _model->getUnscaledMeshExtents();
|
||||
calculateRotatedExtents(rotatedExtents, rotation);
|
||||
|
||||
glm::vec3 rotatedSize = rotatedExtents.maximum - rotatedExtents.minimum;
|
||||
|
||||
const glm::vec3& modelScale = _model->getScale();
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
|
||||
// draw the orignal bounding cube
|
||||
glColor4f(1.0f, 1.0f, 0.0f, 1.0f);
|
||||
glutWireCube(size);
|
||||
|
||||
// draw the rotated bounding cube
|
||||
glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
glPushMatrix();
|
||||
glScalef(rotatedSize.x * modelScale.x, rotatedSize.y * modelScale.y, rotatedSize.z * modelScale.z);
|
||||
glutWireCube(1.0);
|
||||
glPopMatrix();
|
||||
|
||||
// draw the model relative bounding box
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
glScalef(width * modelScale.x, height * modelScale.y, depth * modelScale.z);
|
||||
glColor3f(0.0f, 1.0f, 0.0f);
|
||||
glutWireCube(1.0);
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
}
|
||||
} else {
|
||||
// if we couldn't get a model, then just draw a cube
|
||||
glColor3ub(getColor()[RED_INDEX],getColor()[GREEN_INDEX],getColor()[BLUE_INDEX]);
|
||||
|
|
|
@ -31,12 +31,24 @@ EntityItem* RenderableSphereEntityItem::factory(const EntityItemID& entityID, co
|
|||
void RenderableSphereEntityItem::render(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RenderableSphereEntityItem::render");
|
||||
assert(getType() == EntityTypes::Sphere);
|
||||
glm::vec3 position = getPosition() * (float)TREE_SCALE;
|
||||
float radius = getRadius() * (float)TREE_SCALE;
|
||||
glm::vec3 position = getPositionInMeters();
|
||||
glm::vec3 center = getCenterInMeters();
|
||||
glm::vec3 dimensions = getDimensions() * (float)TREE_SCALE;
|
||||
glm::quat rotation = getRotation();
|
||||
|
||||
glColor3ub(getColor()[RED_INDEX], getColor()[GREEN_INDEX], getColor()[BLUE_INDEX]);
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glutSolidSphere(radius, 15, 15);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
|
||||
|
||||
glPushMatrix();
|
||||
glm::vec3 positionToCenter = center - position;
|
||||
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
|
||||
|
||||
glScalef(dimensions.x, dimensions.y, dimensions.z);
|
||||
glutSolidSphere(0.5f, 15, 15);
|
||||
glPopMatrix();
|
||||
glPopMatrix();
|
||||
};
|
||||
|
|
|
@ -9,43 +9,33 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <qjsonobject.h>
|
||||
|
||||
#include <AccountManager.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "LocationManager.h"
|
||||
#include <UserActivityLogger.h>
|
||||
|
||||
const QString GET_USER_ADDRESS = "/api/v1/users/%1/address";
|
||||
const QString GET_PLACE_ADDRESS = "/api/v1/places/%1";
|
||||
const QString GET_ADDRESSES = "/api/v1/addresses/%1";
|
||||
const QString POST_PLACE_CREATE = "/api/v1/places/";
|
||||
|
||||
|
||||
LocationManager::LocationManager() {
|
||||
|
||||
};
|
||||
const QString POST_LOCATION_CREATE = "/api/v1/locations/";
|
||||
|
||||
LocationManager& LocationManager::getInstance() {
|
||||
static LocationManager sharedInstance;
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
const QString UNKNOWN_ERROR_MESSAGE = "Unknown error creating named location. Please try again!";
|
||||
|
||||
void LocationManager::namedLocationDataReceived(const QJsonObject& data) {
|
||||
if (data.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.contains("status") && data["status"].toString() == "success") {
|
||||
emit creationCompleted(LocationManager::Created);
|
||||
emit creationCompleted(QString());
|
||||
} else {
|
||||
emit creationCompleted(LocationManager::AlreadyExists);
|
||||
emit creationCompleted(UNKNOWN_ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
void LocationManager::errorDataReceived(QNetworkReply::NetworkError error, const QString& message) {
|
||||
emit creationCompleted(LocationManager::SystemError);
|
||||
}
|
||||
|
||||
void LocationManager::createNamedLocation(NamedLocation* namedLocation) {
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
if (accountManager.isLoggedIn()) {
|
||||
|
@ -55,185 +45,45 @@ void LocationManager::createNamedLocation(NamedLocation* namedLocation) {
|
|||
callbackParams.errorCallbackReceiver = this;
|
||||
callbackParams.errorCallbackMethod = "errorDataReceived";
|
||||
|
||||
accountManager.authenticatedRequest(POST_PLACE_CREATE, QNetworkAccessManager::PostOperation,
|
||||
accountManager.authenticatedRequest(POST_LOCATION_CREATE, QNetworkAccessManager::PostOperation,
|
||||
callbackParams, namedLocation->toJsonString().toUtf8());
|
||||
}
|
||||
}
|
||||
|
||||
void LocationManager::goTo(QString destination) {
|
||||
const QString USER_DESTINATION_TYPE = "user";
|
||||
const QString PLACE_DESTINATION_TYPE = "place";
|
||||
const QString OTHER_DESTINATION_TYPE = "coordinate_or_username";
|
||||
void LocationManager::errorDataReceived(QNetworkReply& errorReply) {
|
||||
|
||||
if (destination.startsWith("@")) {
|
||||
// remove '@' and go to user
|
||||
QString destinationUser = destination.remove(0, 1);
|
||||
UserActivityLogger::getInstance().wentTo(USER_DESTINATION_TYPE, destinationUser);
|
||||
goToUser(destinationUser);
|
||||
return;
|
||||
}
|
||||
|
||||
if (destination.startsWith("#")) {
|
||||
// remove '#' and go to named place
|
||||
QString destinationPlace = destination.remove(0, 1);
|
||||
UserActivityLogger::getInstance().wentTo(PLACE_DESTINATION_TYPE, destinationPlace);
|
||||
goToPlace(destinationPlace);
|
||||
return;
|
||||
}
|
||||
|
||||
// go to coordinate destination or to Username
|
||||
if (!goToDestination(destination)) {
|
||||
destination = QString(QUrl::toPercentEncoding(destination));
|
||||
UserActivityLogger::getInstance().wentTo(OTHER_DESTINATION_TYPE, destination);
|
||||
if (errorReply.header(QNetworkRequest::ContentTypeHeader).toString().startsWith("application/json")) {
|
||||
// we have some JSON error data we can parse for our error message
|
||||
QJsonDocument responseJson = QJsonDocument::fromJson(errorReply.readAll());
|
||||
|
||||
JSONCallbackParameters callbackParams;
|
||||
callbackParams.jsonCallbackReceiver = this;
|
||||
callbackParams.jsonCallbackMethod = "goToAddressFromResponse";
|
||||
callbackParams.errorCallbackReceiver = this;
|
||||
callbackParams.errorCallbackMethod = "handleAddressLookupError";
|
||||
QJsonObject dataObject = responseJson.object()["data"].toObject();
|
||||
|
||||
AccountManager::getInstance().authenticatedRequest(GET_ADDRESSES.arg(destination),
|
||||
QNetworkAccessManager::GetOperation,
|
||||
callbackParams);
|
||||
}
|
||||
}
|
||||
|
||||
void LocationManager::goToAddressFromResponse(const QJsonObject& responseData) {
|
||||
QJsonValue status = responseData["status"];
|
||||
|
||||
const QJsonObject& data = responseData["data"].toObject();
|
||||
const QJsonValue& userObject = data["user"];
|
||||
const QJsonValue& placeObject = data["place"];
|
||||
|
||||
if (!placeObject.isUndefined() && !userObject.isUndefined()) {
|
||||
emit multipleDestinationsFound(userObject.toObject(), placeObject.toObject());
|
||||
} else if (placeObject.isUndefined()) {
|
||||
Application::getInstance()->getAvatar()->goToLocationFromAddress(userObject.toObject()["address"].toObject());
|
||||
} else {
|
||||
Application::getInstance()->getAvatar()->goToLocationFromAddress(placeObject.toObject()["address"].toObject());
|
||||
}
|
||||
}
|
||||
|
||||
void LocationManager::goToUser(QString userName) {
|
||||
JSONCallbackParameters callbackParams;
|
||||
callbackParams.jsonCallbackReceiver = Application::getInstance()->getAvatar();
|
||||
callbackParams.jsonCallbackMethod = "goToLocationFromResponse";
|
||||
callbackParams.errorCallbackReceiver = this;
|
||||
callbackParams.errorCallbackMethod = "handleAddressLookupError";
|
||||
|
||||
userName = QString(QUrl::toPercentEncoding(userName));
|
||||
AccountManager::getInstance().authenticatedRequest(GET_USER_ADDRESS.arg(userName),
|
||||
QNetworkAccessManager::GetOperation,
|
||||
callbackParams);
|
||||
}
|
||||
|
||||
void LocationManager::goToPlace(QString placeName) {
|
||||
JSONCallbackParameters callbackParams;
|
||||
callbackParams.jsonCallbackReceiver = Application::getInstance()->getAvatar();
|
||||
callbackParams.jsonCallbackMethod = "goToLocationFromResponse";
|
||||
callbackParams.errorCallbackReceiver = this;
|
||||
callbackParams.errorCallbackMethod = "handleAddressLookupError";
|
||||
|
||||
placeName = QString(QUrl::toPercentEncoding(placeName));
|
||||
AccountManager::getInstance().authenticatedRequest(GET_PLACE_ADDRESS.arg(placeName),
|
||||
QNetworkAccessManager::GetOperation,
|
||||
callbackParams);
|
||||
}
|
||||
|
||||
void LocationManager::goToOrientation(QString orientation) {
|
||||
if (orientation.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList orientationItems = orientation.remove(' ').split(QRegExp("_|,"), QString::SkipEmptyParts);
|
||||
|
||||
const int NUMBER_OF_ORIENTATION_ITEMS = 4;
|
||||
const int W_ITEM = 0;
|
||||
const int X_ITEM = 1;
|
||||
const int Y_ITEM = 2;
|
||||
const int Z_ITEM = 3;
|
||||
|
||||
if (orientationItems.size() == NUMBER_OF_ORIENTATION_ITEMS) {
|
||||
|
||||
// replace last occurrence of '_' with decimal point
|
||||
replaceLastOccurrence('-', '.', orientationItems[W_ITEM]);
|
||||
replaceLastOccurrence('-', '.', orientationItems[X_ITEM]);
|
||||
replaceLastOccurrence('-', '.', orientationItems[Y_ITEM]);
|
||||
replaceLastOccurrence('-', '.', orientationItems[Z_ITEM]);
|
||||
|
||||
double w = orientationItems[W_ITEM].toDouble();
|
||||
double x = orientationItems[X_ITEM].toDouble();
|
||||
double y = orientationItems[Y_ITEM].toDouble();
|
||||
double z = orientationItems[Z_ITEM].toDouble();
|
||||
|
||||
glm::quat newAvatarOrientation(w, x, y, z);
|
||||
|
||||
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
|
||||
glm::quat avatarOrientation = myAvatar->getOrientation();
|
||||
if (newAvatarOrientation != avatarOrientation) {
|
||||
myAvatar->setOrientation(newAvatarOrientation);
|
||||
emit myAvatar->transformChanged();
|
||||
qDebug() << dataObject;
|
||||
|
||||
QString errorString = "There was a problem creating that location.\n";
|
||||
|
||||
// construct the error string from the returned attribute errors
|
||||
foreach(const QString& key, dataObject.keys()) {
|
||||
errorString += "\n\u2022 " + key + " - ";
|
||||
|
||||
QJsonValue keyedErrorValue = dataObject[key];
|
||||
|
||||
if (keyedErrorValue.isArray()) {
|
||||
foreach(const QJsonValue& attributeErrorValue, keyedErrorValue.toArray()) {
|
||||
errorString += attributeErrorValue.toString() + ", ";
|
||||
}
|
||||
|
||||
// remove the trailing comma at end of error list
|
||||
errorString.remove(errorString.length() - 2, 2);
|
||||
} else if (keyedErrorValue.isString()) {
|
||||
errorString += keyedErrorValue.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LocationManager::goToDestination(QString destination) {
|
||||
|
||||
QStringList coordinateItems = destination.remove(' ').split(QRegExp("_|,"), QString::SkipEmptyParts);
|
||||
|
||||
const int NUMBER_OF_COORDINATE_ITEMS = 3;
|
||||
const int X_ITEM = 0;
|
||||
const int Y_ITEM = 1;
|
||||
const int Z_ITEM = 2;
|
||||
if (coordinateItems.size() == NUMBER_OF_COORDINATE_ITEMS) {
|
||||
|
||||
// replace last occurrence of '_' with decimal point
|
||||
replaceLastOccurrence('-', '.', coordinateItems[X_ITEM]);
|
||||
replaceLastOccurrence('-', '.', coordinateItems[Y_ITEM]);
|
||||
replaceLastOccurrence('-', '.', coordinateItems[Z_ITEM]);
|
||||
|
||||
double x = coordinateItems[X_ITEM].toDouble();
|
||||
double y = coordinateItems[Y_ITEM].toDouble();
|
||||
double z = coordinateItems[Z_ITEM].toDouble();
|
||||
|
||||
glm::vec3 newAvatarPos(x, y, z);
|
||||
|
||||
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
|
||||
glm::vec3 avatarPos = myAvatar->getPosition();
|
||||
if (newAvatarPos != avatarPos) {
|
||||
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
|
||||
MyAvatar::sendKillAvatar();
|
||||
|
||||
qDebug("Going To Location: %f, %f, %f...", x, y, z);
|
||||
myAvatar->setPosition(newAvatarPos);
|
||||
emit myAvatar->transformChanged();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// no coordinates were parsed
|
||||
return false;
|
||||
}
|
||||
|
||||
void LocationManager::handleAddressLookupError(QNetworkReply::NetworkError networkError,
|
||||
const QString& errorString) {
|
||||
QString messageBoxString;
|
||||
|
||||
if (networkError == QNetworkReply::ContentNotFoundError) {
|
||||
messageBoxString = "That address could not be found.";
|
||||
|
||||
// emit our creationCompleted signal with the error
|
||||
emit creationCompleted(errorString);
|
||||
|
||||
} else {
|
||||
messageBoxString = errorString;
|
||||
}
|
||||
|
||||
QMessageBox::warning(Application::getInstance()->getWindow(), "", messageBoxString);
|
||||
}
|
||||
|
||||
void LocationManager::replaceLastOccurrence(const QChar search, const QChar replace, QString& string) {
|
||||
int lastIndex;
|
||||
lastIndex = string.lastIndexOf(search);
|
||||
if (lastIndex > 0) {
|
||||
lastIndex = string.lastIndexOf(search);
|
||||
string.replace(lastIndex, 1, replace);
|
||||
creationCompleted(UNKNOWN_ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
#define hifi_LocationManager_h
|
||||
|
||||
#include <QtCore>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
#include "AccountManager.h"
|
||||
#include "NamedLocation.h"
|
||||
|
||||
class LocationManager : public QObject {
|
||||
|
@ -29,29 +29,14 @@ public:
|
|||
SystemError
|
||||
};
|
||||
|
||||
LocationManager();
|
||||
void createNamedLocation(NamedLocation* namedLocation);
|
||||
|
||||
void goTo(QString destination);
|
||||
void goToUser(QString userName);
|
||||
void goToPlace(QString placeName);
|
||||
void goToOrientation(QString orientation);
|
||||
bool goToDestination(QString destination);
|
||||
|
||||
public slots:
|
||||
void handleAddressLookupError(QNetworkReply::NetworkError networkError, const QString& errorString);
|
||||
|
||||
private:
|
||||
void replaceLastOccurrence(const QChar search, const QChar replace, QString& string);
|
||||
|
||||
signals:
|
||||
void creationCompleted(LocationManager::NamedLocationCreateResponse response);
|
||||
void multipleDestinationsFound(const QJsonObject& userData, const QJsonObject& placeData);
|
||||
void creationCompleted(const QString& errorMessage);
|
||||
|
||||
private slots:
|
||||
void namedLocationDataReceived(const QJsonObject& data);
|
||||
void errorDataReceived(QNetworkReply::NetworkError error, const QString& message);
|
||||
void goToAddressFromResponse(const QJsonObject& jsonObject);
|
||||
void errorDataReceived(QNetworkReply& errorReply);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -9,19 +9,25 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <AddressManager.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "NamedLocation.h"
|
||||
|
||||
const QString JSON_FORMAT = "{\"address\":{\"position\":\"%1,%2,%3\","
|
||||
"\"orientation\":\"%4,%5,%6,%7\",\"domain\":\"%8\"},\"name\":\"%9\"}";
|
||||
NamedLocation::NamedLocation(const QString& name,
|
||||
const glm::vec3& position, const glm::quat& orientation,
|
||||
const QUuid& domainID) :
|
||||
_name(name),
|
||||
_position(position),
|
||||
_orientation(orientation),
|
||||
_domainID(domainID)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
const QString JSON_FORMAT = "{\"location\":{\"path\":\"%1\",\"domain_id\":\"%2\",\"name\":\"%3\"}}";
|
||||
|
||||
QString NamedLocation::toJsonString() {
|
||||
return JSON_FORMAT.arg(QString::number(_location.x),
|
||||
QString::number(_location.y),
|
||||
QString::number(_location.z),
|
||||
QString::number(_orientation.w),
|
||||
QString::number(_orientation.x),
|
||||
QString::number(_orientation.y),
|
||||
QString::number(_orientation.z),
|
||||
_domain,
|
||||
_locationName);
|
||||
return JSON_FORMAT.arg(AddressManager::pathForPositionAndOrientation(_position, true, _orientation),
|
||||
uuidStringWithoutCurlyBraces(_domainID), _name);
|
||||
}
|
||||
|
|
|
@ -22,39 +22,33 @@ class NamedLocation : public QObject {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
NamedLocation(QString locationName, glm::vec3 location, glm::quat orientation, QString domain) {
|
||||
_locationName = locationName;
|
||||
_location = location;
|
||||
_orientation = orientation;
|
||||
_domain = domain;
|
||||
}
|
||||
NamedLocation(const QString& name, const glm::vec3& position, const glm::quat& orientation, const QUuid& domainID);
|
||||
|
||||
QString toJsonString();
|
||||
|
||||
bool isEmpty() { return _locationName.isNull() || _locationName.isEmpty(); }
|
||||
bool isEmpty() { return _name.isNull() || _name.isEmpty(); }
|
||||
|
||||
void setLocationName(QString locationName) { _locationName = locationName; }
|
||||
QString locationName() { return _locationName; }
|
||||
void setName(QString name) { _name = name; }
|
||||
const QString& getName() const { return _name; }
|
||||
|
||||
void setLocation(glm::vec3 location) { _location = location; }
|
||||
glm::vec3 location() { return _location; }
|
||||
void setLocation(glm::vec3 position) { _position = position; }
|
||||
const glm::vec3& getPosition() const { return _position; }
|
||||
|
||||
void setOrientation(glm::quat orentation) { _orientation = orentation; }
|
||||
glm::quat orientation() { return _orientation; }
|
||||
void setOrientation(const glm::quat& orentation) { _orientation = orentation; }
|
||||
const glm::quat& getOrientation() const { return _orientation; }
|
||||
|
||||
void setDomain(QString domain) { _domain = domain; }
|
||||
QString domain() { return _domain; }
|
||||
void setDomainID(const QUuid& domainID) { _domainID = domainID; }
|
||||
const QUuid& getDomainID() const { return _domainID; }
|
||||
|
||||
signals:
|
||||
void dataReceived(bool locationExists);
|
||||
|
||||
private:
|
||||
|
||||
QString _locationName;
|
||||
QString _name;
|
||||
QString _createdBy;
|
||||
glm::vec3 _location;
|
||||
glm::vec3 _position;
|
||||
glm::quat _orientation;
|
||||
QString _domain;
|
||||
QUuid _domainID;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -116,9 +116,9 @@ void AmbientOcclusionEffect::render() {
|
|||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
const int VIEWPORT_X_INDEX = 0;
|
||||
const int VIEWPORT_WIDTH_INDEX = 2;
|
||||
QSize widgetSize = Application::getInstance()->getGLWidget()->getDeviceSize();
|
||||
float sMin = viewport[VIEWPORT_X_INDEX] / (float)widgetSize.width();
|
||||
float sWidth = viewport[VIEWPORT_WIDTH_INDEX] / (float)widgetSize.width();
|
||||
QOpenGLFramebufferObject* primaryFBO = Application::getInstance()->getTextureCache()->getPrimaryFramebufferObject();
|
||||
float sMin = viewport[VIEWPORT_X_INDEX] / (float)primaryFBO->width();
|
||||
float sWidth = viewport[VIEWPORT_WIDTH_INDEX] / (float)primaryFBO->width();
|
||||
|
||||
_occlusionProgram->bind();
|
||||
_occlusionProgram->setUniformValue(_nearLocation, nearVal);
|
||||
|
@ -126,7 +126,7 @@ void AmbientOcclusionEffect::render() {
|
|||
_occlusionProgram->setUniformValue(_leftBottomLocation, left, bottom);
|
||||
_occlusionProgram->setUniformValue(_rightTopLocation, right, top);
|
||||
_occlusionProgram->setUniformValue(_noiseScaleLocation, viewport[VIEWPORT_WIDTH_INDEX] / (float)ROTATION_WIDTH,
|
||||
widgetSize.height() / (float)ROTATION_HEIGHT);
|
||||
primaryFBO->height() / (float)ROTATION_HEIGHT);
|
||||
_occlusionProgram->setUniformValue(_texCoordOffsetLocation, sMin, 0.0f);
|
||||
_occlusionProgram->setUniformValue(_texCoordScaleLocation, sWidth, 1.0f);
|
||||
|
||||
|
@ -148,7 +148,7 @@ void AmbientOcclusionEffect::render() {
|
|||
glBindTexture(GL_TEXTURE_2D, freeFBO->texture());
|
||||
|
||||
_blurProgram->bind();
|
||||
_blurProgram->setUniformValue(_blurScaleLocation, 1.0f / widgetSize.width(), 1.0f / widgetSize.height());
|
||||
_blurProgram->setUniformValue(_blurScaleLocation, 1.0f / primaryFBO->width(), 1.0f / primaryFBO->height());
|
||||
|
||||
renderFullscreenQuad(sMin, sMin + sWidth);
|
||||
|
||||
|
|
|
@ -298,8 +298,8 @@ QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, cons
|
|||
|
||||
void GeometryCache::setBlendedVertices(const QPointer<Model>& model, const QWeakPointer<NetworkGeometry>& geometry,
|
||||
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals) {
|
||||
if (!model.isNull() && model->getGeometry() == geometry) {
|
||||
model->setBlendedVertices(vertices, normals);
|
||||
if (!model.isNull()) {
|
||||
model->setBlendedVertices(geometry, vertices, normals);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,14 +54,17 @@ Model::Model(QObject* parent) :
|
|||
QObject(parent),
|
||||
_scale(1.0f, 1.0f, 1.0f),
|
||||
_scaleToFit(false),
|
||||
_scaleToFitLargestDimension(0.0f),
|
||||
_scaleToFitDimensions(0.0f),
|
||||
_scaledToFit(false),
|
||||
_snapModelToCenter(false),
|
||||
_snappedToCenter(false),
|
||||
_snapModelToRegistrationPoint(false),
|
||||
_snappedToRegistrationPoint(false),
|
||||
_showTrueJointTransforms(true),
|
||||
_lodDistance(0.0f),
|
||||
_pupilDilation(0.0f),
|
||||
_url("http://invalid.com") {
|
||||
_url("http://invalid.com"),
|
||||
_blenderPending(false),
|
||||
_blendRequired(false) {
|
||||
|
||||
// we may have been created in the network thread, but we live in the main thread
|
||||
moveToThread(Application::getInstance()->thread());
|
||||
}
|
||||
|
@ -157,8 +160,8 @@ void Model::setOffset(const glm::vec3& offset) {
|
|||
_offset = offset;
|
||||
|
||||
// if someone manually sets our offset, then we are no longer snapped to center
|
||||
_snapModelToCenter = false;
|
||||
_snappedToCenter = false;
|
||||
_snapModelToRegistrationPoint = false;
|
||||
_snappedToRegistrationPoint = false;
|
||||
}
|
||||
|
||||
void Model::initProgram(ProgramObject& program, Model::Locations& locations,
|
||||
|
@ -845,8 +848,8 @@ Blender::Blender(Model* model, const QWeakPointer<NetworkGeometry>& geometry,
|
|||
}
|
||||
|
||||
void Blender::run() {
|
||||
// make sure the model/geometry still exists
|
||||
if (_model.isNull() || _geometry.isNull()) {
|
||||
// make sure the model still exists
|
||||
if (_model.isNull()) {
|
||||
return;
|
||||
}
|
||||
QVector<glm::vec3> vertices, normals;
|
||||
|
@ -882,50 +885,57 @@ void Blender::run() {
|
|||
Q_ARG(const QVector<glm::vec3>&, vertices), Q_ARG(const QVector<glm::vec3>&, normals));
|
||||
}
|
||||
|
||||
void Model::setScaleToFit(bool scaleToFit, float largestDimension) {
|
||||
if (_scaleToFit != scaleToFit || _scaleToFitLargestDimension != largestDimension) {
|
||||
void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions) {
|
||||
if (_scaleToFit != scaleToFit || _scaleToFitDimensions != dimensions) {
|
||||
_scaleToFit = scaleToFit;
|
||||
_scaleToFitLargestDimension = largestDimension;
|
||||
_scaleToFitDimensions = dimensions;
|
||||
_scaledToFit = false; // force rescaling
|
||||
}
|
||||
}
|
||||
|
||||
void Model::setScaleToFit(bool scaleToFit, float largestDimension) {
|
||||
setScaleToFit(scaleToFit, glm::vec3(largestDimension, largestDimension, largestDimension));
|
||||
}
|
||||
|
||||
void Model::scaleToFit() {
|
||||
Extents modelMeshExtents = getUnscaledMeshExtents();
|
||||
|
||||
// size is our "target size in world space"
|
||||
// we need to set our model scale so that the extents of the mesh, fit in a cube that size...
|
||||
float maxDimension = glm::distance(modelMeshExtents.maximum, modelMeshExtents.minimum);
|
||||
float maxScale = _scaleToFitLargestDimension / maxDimension;
|
||||
glm::vec3 scale(maxScale, maxScale, maxScale);
|
||||
setScaleInternal(scale);
|
||||
glm::vec3 meshDimensions = modelMeshExtents.maximum - modelMeshExtents.minimum;
|
||||
glm::vec3 rescaleDimensions = _scaleToFitDimensions / meshDimensions;
|
||||
setScaleInternal(rescaleDimensions);
|
||||
_scaledToFit = true;
|
||||
}
|
||||
|
||||
void Model::setSnapModelToCenter(bool snapModelToCenter) {
|
||||
if (_snapModelToCenter != snapModelToCenter) {
|
||||
_snapModelToCenter = snapModelToCenter;
|
||||
_snappedToCenter = false; // force re-centering
|
||||
void Model::setSnapModelToRegistrationPoint(bool snapModelToRegistrationPoint, const glm::vec3& registrationPoint) {
|
||||
glm::vec3 clampedRegistrationPoint = glm::clamp(registrationPoint, 0.0f, 1.0f);
|
||||
if (_snapModelToRegistrationPoint != snapModelToRegistrationPoint || _registrationPoint != clampedRegistrationPoint) {
|
||||
_snapModelToRegistrationPoint = snapModelToRegistrationPoint;
|
||||
_registrationPoint = clampedRegistrationPoint;
|
||||
_snappedToRegistrationPoint = false; // force re-centering
|
||||
}
|
||||
}
|
||||
|
||||
void Model::snapToCenter() {
|
||||
void Model::snapToRegistrationPoint() {
|
||||
Extents modelMeshExtents = getUnscaledMeshExtents();
|
||||
glm::vec3 halfDimensions = (modelMeshExtents.maximum - modelMeshExtents.minimum) * 0.5f;
|
||||
glm::vec3 offset = -modelMeshExtents.minimum - halfDimensions;
|
||||
glm::vec3 dimensions = (modelMeshExtents.maximum - modelMeshExtents.minimum);
|
||||
glm::vec3 offset = -modelMeshExtents.minimum - (dimensions * _registrationPoint);
|
||||
_offset = offset;
|
||||
_snappedToCenter = true;
|
||||
_snappedToRegistrationPoint = true;
|
||||
}
|
||||
|
||||
void Model::simulate(float deltaTime, bool fullUpdate) {
|
||||
fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit) || (_snapModelToCenter && !_snappedToCenter);
|
||||
fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit)
|
||||
|| (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint);
|
||||
|
||||
if (isActive() && fullUpdate) {
|
||||
// check for scale to fit
|
||||
if (_scaleToFit && !_scaledToFit) {
|
||||
scaleToFit();
|
||||
}
|
||||
if (_snapModelToCenter && !_snappedToCenter) {
|
||||
snapToCenter();
|
||||
if (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint) {
|
||||
snapToRegistrationPoint();
|
||||
}
|
||||
simulateInternal(deltaTime);
|
||||
}
|
||||
|
@ -991,9 +1001,15 @@ void Model::simulateInternal(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
// post the blender
|
||||
// post the blender if we're not currently waiting for one to finish
|
||||
if (geometry.hasBlendedMeshes()) {
|
||||
QThreadPool::globalInstance()->start(new Blender(this, _geometry, geometry.meshes, _blendshapeCoefficients));
|
||||
if (_blenderPending) {
|
||||
_blendRequired = true;
|
||||
} else {
|
||||
_blendRequired = false;
|
||||
_blenderPending = true;
|
||||
QThreadPool::globalInstance()->start(new Blender(this, _geometry, geometry.meshes, _blendshapeCoefficients));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1256,14 +1272,25 @@ void Model::renderJointCollisionShapes(float alpha) {
|
|||
// implement this when we have shapes for regular models
|
||||
}
|
||||
|
||||
void Model::setBlendedVertices(const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals) {
|
||||
if (_blendedVertexBuffers.isEmpty()) {
|
||||
void Model::setBlendedVertices(const QWeakPointer<NetworkGeometry>& geometry, const QVector<glm::vec3>& vertices,
|
||||
const QVector<glm::vec3>& normals) {
|
||||
_blenderPending = false;
|
||||
|
||||
// start the next blender if required
|
||||
const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry();
|
||||
if (_blendRequired) {
|
||||
_blendRequired = false;
|
||||
if (fbxGeometry.hasBlendedMeshes()) {
|
||||
_blenderPending = true;
|
||||
QThreadPool::globalInstance()->start(new Blender(this, _geometry, fbxGeometry.meshes, _blendshapeCoefficients));
|
||||
}
|
||||
}
|
||||
if (_geometry != geometry || _blendedVertexBuffers.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
int index = 0;
|
||||
for (int i = 0; i < geometry.meshes.size(); i++) {
|
||||
const FBXMesh& mesh = geometry.meshes.at(i);
|
||||
for (int i = 0; i < fbxGeometry.meshes.size(); i++) {
|
||||
const FBXMesh& mesh = fbxGeometry.meshes.at(i);
|
||||
if (mesh.blendshapes.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -52,10 +52,18 @@ public:
|
|||
void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f);
|
||||
bool getScaleToFit() const { return _scaleToFit; } /// is scale to fit enabled
|
||||
bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit
|
||||
bool getScaleToFitDimension() const { return _scaleToFitLargestDimension; } /// the dimension model is scaled to
|
||||
const glm::vec3& getScaleToFitDimensions() const { return _scaleToFitDimensions; } /// the dimensions model is scaled to
|
||||
void setScaleToFit(bool scaleToFit, const glm::vec3& dimensions);
|
||||
|
||||
void setSnapModelToCenter(bool snapModelToCenter);
|
||||
bool getSnapModelToCenter() { return _snapModelToCenter; }
|
||||
void setSnapModelToCenter(bool snapModelToCenter) {
|
||||
setSnapModelToRegistrationPoint(snapModelToCenter, glm::vec3(0.5f,0.5f,0.5f));
|
||||
};
|
||||
bool getSnapModelToCenter() {
|
||||
return _snapModelToRegistrationPoint && _registrationPoint == glm::vec3(0.5f,0.5f,0.5f);
|
||||
}
|
||||
|
||||
void setSnapModelToRegistrationPoint(bool snapModelToRegistrationPoint, const glm::vec3& registrationPoint);
|
||||
bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; }
|
||||
|
||||
void setScale(const glm::vec3& scale);
|
||||
const glm::vec3& getScale() const { return _scale; }
|
||||
|
@ -158,7 +166,8 @@ public:
|
|||
virtual void renderJointCollisionShapes(float alpha);
|
||||
|
||||
/// Sets blended vertices computed in a separate thread.
|
||||
void setBlendedVertices(const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
|
||||
void setBlendedVertices(const QWeakPointer<NetworkGeometry>& geometry, const QVector<glm::vec3>& vertices,
|
||||
const QVector<glm::vec3>& normals);
|
||||
|
||||
class LocalLight {
|
||||
public:
|
||||
|
@ -181,11 +190,13 @@ protected:
|
|||
glm::vec3 _offset;
|
||||
|
||||
bool _scaleToFit; /// If you set scaleToFit, we will calculate scale based on MeshExtents
|
||||
float _scaleToFitLargestDimension; /// this is the dimension that scale to fit will use
|
||||
glm::vec3 _scaleToFitDimensions; /// this is the dimensions that scale to fit will use
|
||||
bool _scaledToFit; /// have we scaled to fit
|
||||
|
||||
bool _snapModelToCenter; /// is the model's offset automatically adjusted to center around 0,0,0 in model space
|
||||
bool _snappedToCenter; /// are we currently snapped to center
|
||||
bool _snapModelToRegistrationPoint; /// is the model's offset automatically adjusted to a registration point in model space
|
||||
bool _snappedToRegistrationPoint; /// are we currently snapped to a registration point
|
||||
glm::vec3 _registrationPoint; /// the point in model space our center is snapped to
|
||||
|
||||
bool _showTrueJointTransforms;
|
||||
|
||||
QVector<LocalLight> _localLights;
|
||||
|
@ -206,7 +217,7 @@ protected:
|
|||
|
||||
void setScaleInternal(const glm::vec3& scale);
|
||||
void scaleToFit();
|
||||
void snapToCenter();
|
||||
void snapToRegistrationPoint();
|
||||
|
||||
void simulateInternal(float deltaTime);
|
||||
|
||||
|
@ -274,6 +285,9 @@ private:
|
|||
glm::vec4 _localLightColors[MAX_LOCAL_LIGHTS];
|
||||
glm::vec4 _localLightDirections[MAX_LOCAL_LIGHTS];
|
||||
|
||||
bool _blenderPending;
|
||||
bool _blendRequired;
|
||||
|
||||
static ProgramObject _program;
|
||||
static ProgramObject _normalMapProgram;
|
||||
static ProgramObject _specularMapProgram;
|
||||
|
|
|
@ -497,13 +497,9 @@ void NetworkTexture::setImage(const QImage& image, bool translucent, const QColo
|
|||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.width(), image.height(), 0,
|
||||
GL_RGB, GL_UNSIGNED_BYTE, image.constBits());
|
||||
}
|
||||
if (_type == SPLAT_TEXTURE) {
|
||||
// generate mipmaps for splat textures
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
} else {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
}
|
||||
// generate mipmaps
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
|
@ -541,7 +537,8 @@ QSharedPointer<Texture> DilatableNetworkTexture::getDilatedTexture(float dilatio
|
|||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, dilatedImage.width(), dilatedImage.height(), 0,
|
||||
GL_RGB, GL_UNSIGNED_BYTE, dilatedImage.constBits());
|
||||
}
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -257,7 +257,7 @@ void ControllerScriptingInterface::releaseJoystick(int joystickIndex) {
|
|||
|
||||
glm::vec2 ControllerScriptingInterface::getViewportDimensions() const {
|
||||
GLCanvas* widget = Application::getInstance()->getGLWidget();
|
||||
return glm::vec2(widget->getDeviceWidth(), widget->getDeviceHeight());
|
||||
return glm::vec2(widget->width(), widget->height());
|
||||
}
|
||||
|
||||
AbstractInputController* ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) {
|
||||
|
@ -297,6 +297,10 @@ AbstractInputController* ControllerScriptingInterface::createInputController(con
|
|||
}
|
||||
}
|
||||
|
||||
void ControllerScriptingInterface::releaseInputController(AbstractInputController* input) {
|
||||
_inputControllers.erase(input->getKey());
|
||||
}
|
||||
|
||||
void ControllerScriptingInterface::updateInputControllers() {
|
||||
//TODO C++11 for (auto it = _inputControllers.begin(); it != _inputControllers.end(); it++) {
|
||||
for (InputControllerMap::iterator it = _inputControllers.begin(); it != _inputControllers.end(); it++) {
|
||||
|
|
|
@ -76,8 +76,6 @@ public:
|
|||
|
||||
void updateInputControllers();
|
||||
|
||||
void releaseInputController(AbstractInputController* input);
|
||||
|
||||
public slots:
|
||||
virtual bool isPrimaryButtonPressed() const;
|
||||
virtual glm::vec2 getPrimaryJoystickPosition() const;
|
||||
|
@ -116,6 +114,8 @@ public slots:
|
|||
/// Factory to create an InputController
|
||||
virtual AbstractInputController* createInputController(const QString& deviceName, const QString& tracker);
|
||||
|
||||
virtual void releaseInputController(AbstractInputController* input);
|
||||
|
||||
private:
|
||||
const PalmData* getPrimaryPalm() const;
|
||||
const PalmData* getPalm(int palmIndex) const;
|
||||
|
|
113
interface/src/scripting/GlobalServicesScriptingInterface.cpp
Normal file
113
interface/src/scripting/GlobalServicesScriptingInterface.cpp
Normal file
|
@ -0,0 +1,113 @@
|
|||
//
|
||||
// GlobalServicesScriptingInterface.cpp
|
||||
// interface/src/scripting
|
||||
//
|
||||
// Created by Thijs Wenker on 9/10/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 "AccountManager.h"
|
||||
#include "XmppClient.h"
|
||||
|
||||
#include "GlobalServicesScriptingInterface.h"
|
||||
|
||||
GlobalServicesScriptingInterface::GlobalServicesScriptingInterface() {
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
connect(&accountManager, &AccountManager::usernameChanged, this, &GlobalServicesScriptingInterface::myUsernameChanged);
|
||||
connect(&accountManager, &AccountManager::logoutComplete, this, &GlobalServicesScriptingInterface::loggedOut);
|
||||
#ifdef HAVE_QXMPP
|
||||
const XmppClient& xmppClient = XmppClient::getInstance();
|
||||
connect(&xmppClient, &XmppClient::joinedPublicChatRoom, this, &GlobalServicesScriptingInterface::connected);
|
||||
connect(&xmppClient, &XmppClient::joinedPublicChatRoom, this, &GlobalServicesScriptingInterface::onConnected);
|
||||
const QXmppClient& qxmppClient = XmppClient::getInstance().getXMPPClient();
|
||||
connect(&qxmppClient, &QXmppClient::messageReceived, this, &GlobalServicesScriptingInterface::messageReceived);
|
||||
#endif // HAVE_QXMPP
|
||||
}
|
||||
|
||||
GlobalServicesScriptingInterface::~GlobalServicesScriptingInterface() {
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
disconnect(&accountManager, &AccountManager::usernameChanged, this, &GlobalServicesScriptingInterface::myUsernameChanged);
|
||||
disconnect(&accountManager, &AccountManager::logoutComplete, this, &GlobalServicesScriptingInterface::loggedOut);
|
||||
#ifdef HAVE_QXMPP
|
||||
const XmppClient& xmppClient = XmppClient::getInstance();
|
||||
disconnect(&xmppClient, &XmppClient::joinedPublicChatRoom, this, &GlobalServicesScriptingInterface::connected);
|
||||
disconnect(&xmppClient, &XmppClient::joinedPublicChatRoom, this, &GlobalServicesScriptingInterface::onConnected);
|
||||
const QXmppClient& qxmppClient = XmppClient::getInstance().getXMPPClient();
|
||||
disconnect(&qxmppClient, &QXmppClient::messageReceived, this, &GlobalServicesScriptingInterface::messageReceived);
|
||||
const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom();
|
||||
disconnect(publicChatRoom, &QXmppMucRoom::participantsChanged, this, &GlobalServicesScriptingInterface::participantsChanged);
|
||||
#endif // HAVE_QXMPP
|
||||
}
|
||||
|
||||
void GlobalServicesScriptingInterface::onConnected() {
|
||||
#ifdef HAVE_QXMPP
|
||||
const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom();
|
||||
connect(publicChatRoom, &QXmppMucRoom::participantsChanged, this, &GlobalServicesScriptingInterface::participantsChanged, Qt::UniqueConnection);
|
||||
#endif // HAVE_QXMPP
|
||||
}
|
||||
|
||||
void GlobalServicesScriptingInterface::participantsChanged() {
|
||||
#ifdef HAVE_QXMPP
|
||||
emit GlobalServicesScriptingInterface::onlineUsersChanged(this->getOnlineUsers());
|
||||
#endif // HAVE_QXMPP
|
||||
}
|
||||
|
||||
GlobalServicesScriptingInterface* GlobalServicesScriptingInterface::getInstance() {
|
||||
static GlobalServicesScriptingInterface sharedInstance;
|
||||
return &sharedInstance;
|
||||
}
|
||||
|
||||
bool GlobalServicesScriptingInterface::isConnected() {
|
||||
#ifdef HAVE_QXMPP
|
||||
return XmppClient::getInstance().getXMPPClient().isConnected();
|
||||
#else
|
||||
return false;
|
||||
#endif // HAVE_QXMPP
|
||||
}
|
||||
|
||||
QScriptValue GlobalServicesScriptingInterface::chat(const QString& message) {
|
||||
#ifdef HAVE_QXMPP
|
||||
if (XmppClient::getInstance().getXMPPClient().isConnected()) {
|
||||
const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom();
|
||||
QXmppMessage messageObject;
|
||||
messageObject.setTo(publicChatRoom->jid());
|
||||
messageObject.setType(QXmppMessage::GroupChat);
|
||||
messageObject.setBody(message);
|
||||
return XmppClient::getInstance().getXMPPClient().sendPacket(messageObject);
|
||||
}
|
||||
#endif // HAVE_QXMPP
|
||||
return false;
|
||||
}
|
||||
|
||||
QString GlobalServicesScriptingInterface::getMyUsername() {
|
||||
return AccountManager::getInstance().getAccountInfo().getUsername();
|
||||
}
|
||||
|
||||
QStringList GlobalServicesScriptingInterface::getOnlineUsers() {
|
||||
#ifdef HAVE_QXMPP
|
||||
if (XmppClient::getInstance().getXMPPClient().isConnected()) {
|
||||
QStringList usernames;
|
||||
const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom();
|
||||
foreach(const QString& participant, XmppClient::getInstance().getPublicChatRoom()->participants()) {
|
||||
usernames.append(participant.right(participant.count() - 1 - publicChatRoom->jid().count()));
|
||||
}
|
||||
return usernames;
|
||||
}
|
||||
#endif // HAVE_QXMPP
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
void GlobalServicesScriptingInterface::loggedOut() {
|
||||
emit GlobalServicesScriptingInterface::disconnected(QString("logout"));
|
||||
}
|
||||
|
||||
void GlobalServicesScriptingInterface::messageReceived(const QXmppMessage& message) {
|
||||
if (message.type() != QXmppMessage::GroupChat) {
|
||||
return;
|
||||
}
|
||||
const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom();
|
||||
emit GlobalServicesScriptingInterface::incomingMessage(message.from().right(message.from().count() - 1 - publicChatRoom->jid().count()), message.body());
|
||||
}
|
60
interface/src/scripting/GlobalServicesScriptingInterface.h
Normal file
60
interface/src/scripting/GlobalServicesScriptingInterface.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// GlobalServicesScriptingInterface.h
|
||||
// interface/src/scripting
|
||||
//
|
||||
// Created by Thijs Wenker on 9/10/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_GlobalServicesScriptingInterface_h
|
||||
#define hifi_GlobalServicesScriptingInterface_h
|
||||
|
||||
#include <QObject>
|
||||
#include <QScriptContext>
|
||||
#include <QScriptEngine>
|
||||
#include <QScriptValue>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#ifdef HAVE_QXMPP
|
||||
|
||||
#include <QXmppClient.h>
|
||||
#include <QXmppMessage.h>
|
||||
|
||||
#endif
|
||||
|
||||
class GlobalServicesScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool isConnected READ isConnected)
|
||||
Q_PROPERTY(QString myUsername READ getMyUsername)
|
||||
Q_PROPERTY(QStringList onlineUsers READ getOnlineUsers)
|
||||
GlobalServicesScriptingInterface();
|
||||
~GlobalServicesScriptingInterface();
|
||||
public:
|
||||
static GlobalServicesScriptingInterface* getInstance();
|
||||
|
||||
bool isConnected();
|
||||
QString getMyUsername();
|
||||
QStringList getOnlineUsers();
|
||||
|
||||
public slots:
|
||||
QScriptValue chat(const QString& message);
|
||||
|
||||
private slots:
|
||||
void loggedOut();
|
||||
void onConnected();
|
||||
void participantsChanged();
|
||||
void messageReceived(const QXmppMessage& message);
|
||||
|
||||
signals:
|
||||
void connected();
|
||||
void disconnected(const QString& reason);
|
||||
void incomingMessage(const QString& username, const QString& message);
|
||||
void onlineUsersChanged(const QStringList& usernames);
|
||||
void myUsernameChanged(const QString& username);
|
||||
};
|
||||
|
||||
#endif // hifi_GlobalServicesScriptingInterface_h
|
|
@ -29,10 +29,9 @@ QString LocationScriptingInterface::getHref() {
|
|||
}
|
||||
|
||||
QString LocationScriptingInterface::getPathname() {
|
||||
const glm::vec3& position = Application::getInstance()->getAvatar()->getPosition();
|
||||
QString path;
|
||||
path.sprintf("/%.4f,%.4f,%.4f", position.x, position.y, position.z);
|
||||
return path;
|
||||
MyAvatar* applicationAvatar = Application::getInstance()->getAvatar();
|
||||
return AddressManager::pathForPositionAndOrientation(applicationAvatar->getPosition(),
|
||||
true, applicationAvatar->getOrientation());
|
||||
}
|
||||
|
||||
QString LocationScriptingInterface::getHostname() {
|
||||
|
@ -40,7 +39,7 @@ QString LocationScriptingInterface::getHostname() {
|
|||
}
|
||||
|
||||
void LocationScriptingInterface::assign(const QString& url) {
|
||||
QMetaObject::invokeMethod(Menu::getInstance(), "goToURL", Q_ARG(const QString&, url));
|
||||
QMetaObject::invokeMethod(&AddressManager::getInstance(), "handleLookupString", Q_ARG(const QString&, url));
|
||||
}
|
||||
|
||||
QScriptValue LocationScriptingInterface::locationGetter(QScriptContext* context, QScriptEngine* engine) {
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include <QScriptValue>
|
||||
#include <QString>
|
||||
|
||||
#include <AddressManager.h>
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
class LocationScriptingInterface : public QObject {
|
||||
|
@ -33,7 +35,7 @@ public:
|
|||
|
||||
bool isConnected();
|
||||
QString getHref();
|
||||
QString getProtocol() { return CUSTOM_URL_SCHEME; };
|
||||
QString getProtocol() { return HIFI_URL_SCHEME; };
|
||||
QString getPathname();
|
||||
QString getHostname();
|
||||
|
||||
|
|
|
@ -27,7 +27,31 @@ WindowScriptingInterface* WindowScriptingInterface::getInstance() {
|
|||
return &sharedInstance;
|
||||
}
|
||||
|
||||
WindowScriptingInterface::WindowScriptingInterface() {
|
||||
WindowScriptingInterface::WindowScriptingInterface() :
|
||||
_editDialog(NULL),
|
||||
_nonBlockingFormActive(false),
|
||||
_formResult(QDialog::Rejected)
|
||||
{
|
||||
}
|
||||
|
||||
QScriptValue WindowScriptingInterface::hasFocus() {
|
||||
return Application::getInstance()->getGLWidget()->hasFocus();
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::setCursorVisible(bool visible) {
|
||||
Application::getInstance()->setCursorVisible(visible);
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::setCursorPosition(int x, int y) {
|
||||
QCursor::setPos(x, y);
|
||||
}
|
||||
|
||||
QScriptValue WindowScriptingInterface::getCursorPositionX() {
|
||||
return QCursor::pos().x();
|
||||
}
|
||||
|
||||
QScriptValue WindowScriptingInterface::getCursorPositionY() {
|
||||
return QCursor::pos().y();
|
||||
}
|
||||
|
||||
QScriptValue WindowScriptingInterface::alert(const QString& message) {
|
||||
|
@ -84,6 +108,26 @@ QScriptValue WindowScriptingInterface::s3Browse(const QString& nameFilter) {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::nonBlockingForm(const QString& title, QScriptValue form) {
|
||||
QMetaObject::invokeMethod(this, "showNonBlockingForm", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(const QString&, title), Q_ARG(QScriptValue, form));
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::reloadNonBlockingForm(QScriptValue newValues) {
|
||||
QMetaObject::invokeMethod(this, "doReloadNonBlockingForm", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(QScriptValue, newValues));
|
||||
}
|
||||
|
||||
|
||||
QScriptValue WindowScriptingInterface::getNonBlockingFormResult(QScriptValue form) {
|
||||
QScriptValue retVal;
|
||||
QMetaObject::invokeMethod(this, "doGetNonBlockingFormResult", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QScriptValue, retVal),
|
||||
Q_ARG(QScriptValue, form));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
/// Display an alert box
|
||||
/// \param const QString& message message to display
|
||||
/// \return QScriptValue::UndefinedValue
|
||||
|
@ -126,12 +170,131 @@ void WindowScriptingInterface::chooseDirectory() {
|
|||
button->setText(buttonText);
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::inlineButtonClicked() {
|
||||
QPushButton* button = reinterpret_cast<QPushButton*>(sender());
|
||||
QString name = button->property("name").toString();
|
||||
emit inlineButtonClicked(name);
|
||||
}
|
||||
|
||||
QString WindowScriptingInterface::jsRegExp2QtRegExp(QString string) {
|
||||
// Converts string representation of RegExp from JavaScript format to Qt format.
|
||||
return string.mid(1, string.length() - 2) // No enclosing slashes.
|
||||
.replace("\\/", "/"); // No escaping of forward slash.
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::showNonBlockingForm(const QString& title, QScriptValue form) {
|
||||
if (!form.isArray() || (form.isArray() && form.property("length").toInt32() <= 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// what should we do if someone calls us while we still think we have a dialog showing???
|
||||
if (_editDialog) {
|
||||
qDebug() << "Show Non-Blocking Form called when form already active.";
|
||||
return;
|
||||
}
|
||||
|
||||
_form = form;
|
||||
_editDialog = createForm(title, _form);
|
||||
_nonBlockingFormActive = true;
|
||||
|
||||
connect(_editDialog, SIGNAL(accepted()), this, SLOT(nonBlockingFormAccepted()));
|
||||
connect(_editDialog, SIGNAL(rejected()), this, SLOT(nonBlockingFormRejected()));
|
||||
|
||||
_editDialog->setModal(true);
|
||||
_editDialog->show();
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::doReloadNonBlockingForm(QScriptValue newValues) {
|
||||
if (!newValues.isArray() || (newValues.isArray() && newValues.property("length").toInt32() <= 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// what should we do if someone calls us while we still think we have a dialog showing???
|
||||
if (!_editDialog) {
|
||||
qDebug() << "Reload Non-Blocking Form called when no form is active.";
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < newValues.property("length").toInt32(); ++i) {
|
||||
QScriptValue item = newValues.property(i);
|
||||
|
||||
if (item.property("oldIndex").isValid()) {
|
||||
int oldIndex = item.property("oldIndex").toInt32();
|
||||
QScriptValue oldItem = _form.property(oldIndex);
|
||||
if (oldItem.isValid()) {
|
||||
QLineEdit* originalEdit = _edits[oldItem.property("editIndex").toInt32()];
|
||||
originalEdit->setText(item.property("value").toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool WindowScriptingInterface::nonBlockingFormActive() {
|
||||
return _nonBlockingFormActive;
|
||||
}
|
||||
|
||||
QScriptValue WindowScriptingInterface::doGetNonBlockingFormResult(QScriptValue array) {
|
||||
QScriptValue retVal;
|
||||
|
||||
if (_formResult == QDialog::Accepted) {
|
||||
int e = -1;
|
||||
int d = -1;
|
||||
int c = -1;
|
||||
for (int i = 0; i < _form.property("length").toInt32(); ++i) {
|
||||
QScriptValue item = _form.property(i);
|
||||
QScriptValue value = item.property("value");
|
||||
|
||||
if (item.property("button").toString() != "") {
|
||||
// Nothing to do
|
||||
} else if (item.property("type").toString() == "inlineButton") {
|
||||
// Nothing to do
|
||||
} else if (item.property("type").toString() == "header") {
|
||||
// Nothing to do
|
||||
} else if (item.property("directory").toString() != "") {
|
||||
d += 1;
|
||||
value = _directories.at(d)->property("path").toString();
|
||||
item.setProperty("directory", value);
|
||||
_form.setProperty(i, item);
|
||||
} else if (item.property("options").isArray()) {
|
||||
c += 1;
|
||||
item.setProperty("value", _combos.at(c)->currentText());
|
||||
_form.setProperty(i, item);
|
||||
} else {
|
||||
e += 1;
|
||||
bool ok = true;
|
||||
if (value.isNumber()) {
|
||||
value = _edits.at(e)->text().toDouble(&ok);
|
||||
} else if (value.isString()) {
|
||||
value = _edits.at(e)->text();
|
||||
} else if (value.isBool()) {
|
||||
if (_edits.at(e)->text() == "true") {
|
||||
value = true;
|
||||
} else if (_edits.at(e)->text() == "false") {
|
||||
value = false;
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
item.setProperty("value", value);
|
||||
_form.setProperty(i, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete _editDialog;
|
||||
_editDialog = NULL;
|
||||
_form = QScriptValue();
|
||||
_edits.clear();
|
||||
_directories.clear();
|
||||
|
||||
array = _form;
|
||||
return (_formResult == QDialog::Accepted);
|
||||
}
|
||||
|
||||
|
||||
/// Display a form layout with an edit box
|
||||
/// \param const QString& title title to display
|
||||
/// \param const QScriptValue form to display as an array of objects:
|
||||
|
@ -140,129 +303,166 @@ QString WindowScriptingInterface::jsRegExp2QtRegExp(QString string) {
|
|||
/// - button ("Cancel")
|
||||
/// \return QScriptValue `true` if 'OK' was clicked, `false` otherwise
|
||||
QScriptValue WindowScriptingInterface::showForm(const QString& title, QScriptValue form) {
|
||||
if (form.isArray() && form.property("length").toInt32() <= 0) {
|
||||
return false;
|
||||
}
|
||||
QDialog* editDialog = createForm(title, form);
|
||||
|
||||
if (form.isArray() && form.property("length").toInt32() > 0) {
|
||||
QDialog* editDialog = new QDialog(Application::getInstance()->getWindow());
|
||||
editDialog->setWindowTitle(title);
|
||||
|
||||
bool cancelButton = false;
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
editDialog->setLayout(layout);
|
||||
|
||||
QScrollArea* area = new QScrollArea();
|
||||
layout->addWidget(area);
|
||||
area->setWidgetResizable(true);
|
||||
QWidget* container = new QWidget();
|
||||
QFormLayout* formLayout = new QFormLayout();
|
||||
container->setLayout(formLayout);
|
||||
container->sizePolicy().setHorizontalStretch(1);
|
||||
formLayout->setRowWrapPolicy(QFormLayout::DontWrapRows);
|
||||
formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
|
||||
formLayout->setFormAlignment(Qt::AlignHCenter | Qt::AlignTop);
|
||||
formLayout->setLabelAlignment(Qt::AlignLeft);
|
||||
|
||||
area->setWidget(container);
|
||||
|
||||
QVector<QLineEdit*> edits;
|
||||
QVector<QPushButton*> directories;
|
||||
int result = editDialog->exec();
|
||||
|
||||
if (result == QDialog::Accepted) {
|
||||
int e = -1;
|
||||
int d = -1;
|
||||
int c = -1;
|
||||
for (int i = 0; i < form.property("length").toInt32(); ++i) {
|
||||
QScriptValue item = form.property(i);
|
||||
QScriptValue value = item.property("value");
|
||||
|
||||
if (item.property("button").toString() != "") {
|
||||
cancelButton = cancelButton || item.property("button").toString().toLower() == "cancel";
|
||||
|
||||
// Nothing to do
|
||||
} else if (item.property("type").toString() == "inlineButton") {
|
||||
// Nothing to do
|
||||
} else if (item.property("type").toString() == "header") {
|
||||
// Nothing to do
|
||||
} else if (item.property("directory").toString() != "") {
|
||||
QString path = item.property("directory").toString();
|
||||
QString title = item.property("title").toString();
|
||||
if (title == "") {
|
||||
title = "Choose Directory";
|
||||
}
|
||||
QString displayAsString = item.property("displayAs").toString();
|
||||
QRegExp displayAs = QRegExp(displayAsString != "" ? jsRegExp2QtRegExp(displayAsString) : "^(.*)$");
|
||||
QString validateAsString = item.property("validateAs").toString();
|
||||
QRegExp validateAs = QRegExp(validateAsString != "" ? jsRegExp2QtRegExp(validateAsString) : ".*");
|
||||
QString errorMessage = item.property("errorMessage").toString();
|
||||
if (errorMessage == "") {
|
||||
errorMessage = "Invalid directory";
|
||||
}
|
||||
|
||||
QPushButton* directory = new QPushButton(displayAs.cap(1));
|
||||
directory->setProperty("title", title);
|
||||
directory->setProperty("path", path);
|
||||
directory->setProperty("displayAs", displayAs);
|
||||
directory->setProperty("validateAs", validateAs);
|
||||
directory->setProperty("errorMessage", errorMessage);
|
||||
displayAs.indexIn(path);
|
||||
directory->setText(displayAs.cap(1) != "" ? displayAs.cap(1) : ".");
|
||||
|
||||
directory->setMinimumWidth(200);
|
||||
directories.push_back(directory);
|
||||
|
||||
formLayout->addRow(new QLabel(item.property("label").toString()), directory);
|
||||
connect(directory, SIGNAL(clicked(bool)), SLOT(chooseDirectory()));
|
||||
|
||||
d += 1;
|
||||
value = _directories.at(d)->property("path").toString();
|
||||
item.setProperty("directory", value);
|
||||
form.setProperty(i, item);
|
||||
} else if (item.property("options").isArray()) {
|
||||
c += 1;
|
||||
item.setProperty("value", _combos.at(c)->currentText());
|
||||
_form.setProperty(i, item);
|
||||
} else {
|
||||
QLineEdit* edit = new QLineEdit(item.property("value").toString());
|
||||
edit->setMinimumWidth(200);
|
||||
edits.push_back(edit);
|
||||
formLayout->addRow(new QLabel(item.property("label").toString()), edit);
|
||||
}
|
||||
}
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(
|
||||
QDialogButtonBox::Ok
|
||||
| (cancelButton ? QDialogButtonBox::Cancel : QDialogButtonBox::NoButton)
|
||||
);
|
||||
connect(buttons, SIGNAL(accepted()), editDialog, SLOT(accept()));
|
||||
connect(buttons, SIGNAL(rejected()), editDialog, SLOT(reject()));
|
||||
layout->addWidget(buttons);
|
||||
|
||||
int result = editDialog->exec();
|
||||
if (result == QDialog::Accepted) {
|
||||
int e = -1;
|
||||
int d = -1;
|
||||
for (int i = 0; i < form.property("length").toInt32(); ++i) {
|
||||
QScriptValue item = form.property(i);
|
||||
QScriptValue value = item.property("value");
|
||||
|
||||
if (item.property("button").toString() != "") {
|
||||
// Nothing to do
|
||||
} else if (item.property("directory").toString() != "") {
|
||||
d += 1;
|
||||
value = directories.at(d)->property("path").toString();
|
||||
item.setProperty("directory", value);
|
||||
e += 1;
|
||||
bool ok = true;
|
||||
if (value.isNumber()) {
|
||||
value = _edits.at(e)->text().toDouble(&ok);
|
||||
} else if (value.isString()) {
|
||||
value = _edits.at(e)->text();
|
||||
} else if (value.isBool()) {
|
||||
if (_edits.at(e)->text() == "true") {
|
||||
value = true;
|
||||
} else if (_edits.at(e)->text() == "false") {
|
||||
value = false;
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
item.setProperty("value", value);
|
||||
form.setProperty(i, item);
|
||||
} else {
|
||||
e += 1;
|
||||
bool ok = true;
|
||||
if (value.isNumber()) {
|
||||
value = edits.at(e)->text().toDouble(&ok);
|
||||
} else if (value.isString()) {
|
||||
value = edits.at(e)->text();
|
||||
} else if (value.isBool()) {
|
||||
if (edits.at(e)->text() == "true") {
|
||||
value = true;
|
||||
} else if (edits.at(e)->text() == "false") {
|
||||
value = false;
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
item.setProperty("value", value);
|
||||
form.setProperty(i, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete editDialog;
|
||||
|
||||
return (result == QDialog::Accepted);
|
||||
}
|
||||
|
||||
return false;
|
||||
delete editDialog;
|
||||
_edits.clear();
|
||||
_directories.clear();
|
||||
return (result == QDialog::Accepted);
|
||||
}
|
||||
|
||||
|
||||
|
||||
QDialog* WindowScriptingInterface::createForm(const QString& title, QScriptValue form) {
|
||||
QDialog* editDialog = new QDialog(Application::getInstance()->getWindow());
|
||||
editDialog->setWindowTitle(title);
|
||||
|
||||
bool cancelButton = false;
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
editDialog->setLayout(layout);
|
||||
|
||||
QScrollArea* area = new QScrollArea();
|
||||
layout->addWidget(area);
|
||||
area->setWidgetResizable(true);
|
||||
QWidget* container = new QWidget();
|
||||
QFormLayout* formLayout = new QFormLayout();
|
||||
container->setLayout(formLayout);
|
||||
container->sizePolicy().setHorizontalStretch(1);
|
||||
formLayout->setRowWrapPolicy(QFormLayout::DontWrapRows);
|
||||
formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
|
||||
formLayout->setFormAlignment(Qt::AlignHCenter | Qt::AlignTop);
|
||||
formLayout->setLabelAlignment(Qt::AlignLeft);
|
||||
|
||||
area->setWidget(container);
|
||||
|
||||
for (int i = 0; i < form.property("length").toInt32(); ++i) {
|
||||
QScriptValue item = form.property(i);
|
||||
|
||||
if (item.property("button").toString() != "") {
|
||||
cancelButton = cancelButton || item.property("button").toString().toLower() == "cancel";
|
||||
|
||||
} else if (item.property("directory").toString() != "") {
|
||||
QString path = item.property("directory").toString();
|
||||
QString title = item.property("title").toString();
|
||||
if (title == "") {
|
||||
title = "Choose Directory";
|
||||
}
|
||||
QString displayAsString = item.property("displayAs").toString();
|
||||
QRegExp displayAs = QRegExp(displayAsString != "" ? jsRegExp2QtRegExp(displayAsString) : "^(.*)$");
|
||||
QString validateAsString = item.property("validateAs").toString();
|
||||
QRegExp validateAs = QRegExp(validateAsString != "" ? jsRegExp2QtRegExp(validateAsString) : ".*");
|
||||
QString errorMessage = item.property("errorMessage").toString();
|
||||
if (errorMessage == "") {
|
||||
errorMessage = "Invalid directory";
|
||||
}
|
||||
|
||||
QPushButton* directory = new QPushButton(displayAs.cap(1));
|
||||
directory->setProperty("title", title);
|
||||
directory->setProperty("path", path);
|
||||
directory->setProperty("displayAs", displayAs);
|
||||
directory->setProperty("validateAs", validateAs);
|
||||
directory->setProperty("errorMessage", errorMessage);
|
||||
displayAs.indexIn(path);
|
||||
directory->setText(displayAs.cap(1) != "" ? displayAs.cap(1) : ".");
|
||||
|
||||
directory->setMinimumWidth(200);
|
||||
_directories.push_back(directory);
|
||||
|
||||
formLayout->addRow(new QLabel(item.property("label").toString()), directory);
|
||||
connect(directory, SIGNAL(clicked(bool)), SLOT(chooseDirectory()));
|
||||
|
||||
} else if (item.property("type").toString() == "inlineButton") {
|
||||
QString buttonLabel = item.property("buttonLabel").toString();
|
||||
|
||||
QPushButton* inlineButton = new QPushButton(buttonLabel);
|
||||
inlineButton->setMinimumWidth(200);
|
||||
inlineButton->setProperty("name", item.property("name").toString());
|
||||
formLayout->addRow(new QLabel(item.property("label").toString()), inlineButton);
|
||||
connect(inlineButton, SIGNAL(clicked(bool)), SLOT(inlineButtonClicked()));
|
||||
|
||||
} else if (item.property("type").toString() == "header") {
|
||||
formLayout->addRow(new QLabel(item.property("label").toString()));
|
||||
} else if (item.property("options").isArray()) {
|
||||
QComboBox* combo = new QComboBox();
|
||||
combo->setMinimumWidth(200);
|
||||
QStringList options = item.property("options").toVariant().toStringList();
|
||||
for (QStringList::const_iterator it = options.begin(); it != options.end(); it += 1) {
|
||||
combo->addItem(*it);
|
||||
}
|
||||
_combos.push_back(combo);
|
||||
formLayout->addRow(new QLabel(item.property("label").toString()), combo);
|
||||
} else {
|
||||
QLineEdit* edit = new QLineEdit(item.property("value").toString());
|
||||
edit->setMinimumWidth(200);
|
||||
int editIndex = _edits.size();
|
||||
_edits.push_back(edit);
|
||||
item.setProperty("editIndex", editIndex);
|
||||
formLayout->addRow(new QLabel(item.property("label").toString()), edit);
|
||||
}
|
||||
}
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(
|
||||
QDialogButtonBox::Ok
|
||||
| (cancelButton ? QDialogButtonBox::Cancel : QDialogButtonBox::NoButton)
|
||||
);
|
||||
connect(buttons, SIGNAL(accepted()), editDialog, SLOT(accept()));
|
||||
connect(buttons, SIGNAL(rejected()), editDialog, SLOT(reject()));
|
||||
layout->addWidget(buttons);
|
||||
|
||||
return editDialog;
|
||||
}
|
||||
|
||||
/// Display a prompt with a text box
|
||||
|
@ -336,3 +536,11 @@ int WindowScriptingInterface::getInnerWidth() {
|
|||
int WindowScriptingInterface::getInnerHeight() {
|
||||
return Application::getInstance()->getWindow()->geometry().height();
|
||||
}
|
||||
|
||||
int WindowScriptingInterface::getX() {
|
||||
return Application::getInstance()->getWindow()->x();
|
||||
}
|
||||
|
||||
int WindowScriptingInterface::getY() {
|
||||
return Application::getInstance()->getWindow()->y();
|
||||
}
|
||||
|
|
|
@ -20,12 +20,21 @@ class WindowScriptingInterface : public QObject {
|
|||
Q_OBJECT
|
||||
Q_PROPERTY(int innerWidth READ getInnerWidth)
|
||||
Q_PROPERTY(int innerHeight READ getInnerHeight)
|
||||
Q_PROPERTY(int x READ getX)
|
||||
Q_PROPERTY(int y READ getY)
|
||||
public:
|
||||
static WindowScriptingInterface* getInstance();
|
||||
int getInnerWidth();
|
||||
int getInnerHeight();
|
||||
int getX();
|
||||
int getY();
|
||||
|
||||
public slots:
|
||||
QScriptValue getCursorPositionX();
|
||||
QScriptValue getCursorPositionY();
|
||||
void setCursorPosition(int x, int y);
|
||||
void setCursorVisible(bool visible);
|
||||
QScriptValue hasFocus();
|
||||
QScriptValue alert(const QString& message = "");
|
||||
QScriptValue confirm(const QString& message = "");
|
||||
QScriptValue form(const QString& title, QScriptValue array);
|
||||
|
@ -34,6 +43,14 @@ public slots:
|
|||
QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
|
||||
QScriptValue s3Browse(const QString& nameFilter = "");
|
||||
|
||||
void nonBlockingForm(const QString& title, QScriptValue array);
|
||||
void reloadNonBlockingForm(QScriptValue array);
|
||||
QScriptValue getNonBlockingFormResult(QScriptValue array);
|
||||
|
||||
signals:
|
||||
void inlineButtonClicked(const QString& name);
|
||||
void nonBlockingFormClosed();
|
||||
|
||||
private slots:
|
||||
QScriptValue showAlert(const QString& message);
|
||||
QScriptValue showConfirm(const QString& message);
|
||||
|
@ -42,12 +59,30 @@ private slots:
|
|||
QScriptValue showBrowse(const QString& title, const QString& directory, const QString& nameFilter,
|
||||
QFileDialog::AcceptMode acceptMode = QFileDialog::AcceptOpen);
|
||||
QScriptValue showS3Browse(const QString& nameFilter);
|
||||
|
||||
void showNonBlockingForm(const QString& title, QScriptValue array);
|
||||
void doReloadNonBlockingForm(QScriptValue array);
|
||||
bool nonBlockingFormActive();
|
||||
QScriptValue doGetNonBlockingFormResult(QScriptValue array);
|
||||
|
||||
void chooseDirectory();
|
||||
void inlineButtonClicked();
|
||||
|
||||
void nonBlockingFormAccepted() { _nonBlockingFormActive = false; _formResult = QDialog::Accepted; emit nonBlockingFormClosed(); }
|
||||
void nonBlockingFormRejected() { _nonBlockingFormActive = false; _formResult = QDialog::Rejected; emit nonBlockingFormClosed(); }
|
||||
|
||||
private:
|
||||
WindowScriptingInterface();
|
||||
|
||||
QString jsRegExp2QtRegExp(QString string);
|
||||
QDialog* createForm(const QString& title, QScriptValue form);
|
||||
|
||||
QDialog* _editDialog;
|
||||
QScriptValue _form;
|
||||
bool _nonBlockingFormActive;
|
||||
int _formResult;
|
||||
QVector<QComboBox*> _combos;
|
||||
QVector<QLineEdit*> _edits;
|
||||
QVector<QPushButton*> _directories;
|
||||
};
|
||||
|
||||
#endif // hifi_WindowScriptingInterface_h
|
||||
|
|
|
@ -99,14 +99,14 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) {
|
|||
glPushMatrix();
|
||||
|
||||
glLoadIdentity();
|
||||
gluOrtho2D(0, glWidget->getDeviceWidth(), glWidget->getDeviceHeight(), 0);
|
||||
gluOrtho2D(0, glWidget->width(), glWidget->height(), 0);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
renderAudioMeter();
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse)) {
|
||||
myAvatar->renderHeadMouse(glWidget->getDeviceWidth(), glWidget->getDeviceHeight());
|
||||
myAvatar->renderHeadMouse(glWidget->width(), glWidget->height());
|
||||
}
|
||||
|
||||
renderStatsAndLogs();
|
||||
|
@ -305,8 +305,8 @@ QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const {
|
|||
float u = asin(collisionPos.x) / (_textureFov)+0.5f;
|
||||
float v = 1.0 - (asin(collisionPos.y) / (_textureFov)+0.5f);
|
||||
|
||||
rv.setX(u * glWidget->getDeviceWidth());
|
||||
rv.setY(v * glWidget->getDeviceHeight());
|
||||
rv.setX(u * glWidget->width());
|
||||
rv.setY(v * glWidget->height());
|
||||
}
|
||||
} else {
|
||||
//if they did not click on the overlay, just set the coords to INT_MAX
|
||||
|
@ -323,8 +323,8 @@ QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const {
|
|||
ndcSpacePos = glm::vec3(clipSpacePos) / clipSpacePos.w;
|
||||
}
|
||||
|
||||
rv.setX(((ndcSpacePos.x + 1.0) / 2.0) * glWidget->getDeviceWidth());
|
||||
rv.setY((1.0 - ((ndcSpacePos.y + 1.0) / 2.0)) * glWidget->getDeviceHeight());
|
||||
rv.setX(((ndcSpacePos.x + 1.0) / 2.0) * glWidget->width());
|
||||
rv.setY((1.0 - ((ndcSpacePos.y + 1.0) / 2.0)) * glWidget->height());
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
@ -414,7 +414,7 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) {
|
|||
|
||||
renderTexturedHemisphere();
|
||||
|
||||
renderPointersOculus(whichCamera.getPosition());
|
||||
renderPointersOculus(myAvatar->getHead()->getEyePosition());
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
@ -496,11 +496,11 @@ void ApplicationOverlay::displayOverlayTexture3DTV(Camera& whichCamera, float as
|
|||
//draw the mouse pointer
|
||||
glBindTexture(GL_TEXTURE_2D, _crosshairTexture);
|
||||
|
||||
const float reticleSize = 40.0f / application->getGLWidget()->getDeviceWidth() * quadWidth;
|
||||
const float reticleSize = 40.0f / application->getGLWidget()->width() * quadWidth;
|
||||
x -= reticleSize / 2.0f;
|
||||
y += reticleSize / 2.0f;
|
||||
const float mouseX = (application->getMouseX() / (float)application->getGLWidget()->getDeviceWidth()) * quadWidth;
|
||||
const float mouseY = (1.0 - (application->getMouseY() / (float)application->getGLWidget()->getDeviceHeight())) * quadHeight;
|
||||
const float mouseX = (application->getMouseX() / (float)application->getGLWidget()->width()) * quadWidth;
|
||||
const float mouseY = (1.0 - (application->getMouseY() / (float)application->getGLWidget()->height())) * quadHeight;
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
|
@ -671,14 +671,14 @@ void ApplicationOverlay::renderControllerPointers() {
|
|||
float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2));
|
||||
|
||||
// Get the pixel range over which the xAngle and yAngle are scaled
|
||||
float cursorRange = glWidget->getDeviceWidth() * application->getSixenseManager()->getCursorPixelRangeMult();
|
||||
float cursorRange = glWidget->width() * application->getSixenseManager()->getCursorPixelRangeMult();
|
||||
|
||||
mouseX = (glWidget->getDeviceWidth() / 2.0f + cursorRange * xAngle);
|
||||
mouseY = (glWidget->getDeviceHeight() / 2.0f + cursorRange * yAngle);
|
||||
mouseX = (glWidget->width() / 2.0f + cursorRange * xAngle);
|
||||
mouseY = (glWidget->height() / 2.0f + cursorRange * yAngle);
|
||||
}
|
||||
|
||||
//If the cursor is out of the screen then don't render it
|
||||
if (mouseX < 0 || mouseX >= glWidget->getDeviceWidth() || mouseY < 0 || mouseY >= glWidget->getDeviceHeight()) {
|
||||
if (mouseX < 0 || mouseX >= glWidget->width() || mouseY < 0 || mouseY >= glWidget->height()) {
|
||||
_reticleActive[index] = false;
|
||||
continue;
|
||||
}
|
||||
|
@ -709,8 +709,8 @@ void ApplicationOverlay::renderPointersOculus(const glm::vec3& eyePos) {
|
|||
GLCanvas* glWidget = application->getGLWidget();
|
||||
glm::vec3 cursorVerts[4];
|
||||
|
||||
const int widgetWidth = glWidget->getDeviceWidth();
|
||||
const int widgetHeight = glWidget->getDeviceHeight();
|
||||
const int widgetWidth = glWidget->width();
|
||||
const int widgetHeight = glWidget->height();
|
||||
|
||||
const float reticleSize = 50.0f;
|
||||
|
||||
|
@ -850,8 +850,8 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY, float sizeMult,
|
|||
Application* application = Application::getInstance();
|
||||
GLCanvas* glWidget = application->getGLWidget();
|
||||
|
||||
const int widgetWidth = glWidget->getDeviceWidth();
|
||||
const int widgetHeight = glWidget->getDeviceHeight();
|
||||
const int widgetWidth = glWidget->width();
|
||||
const int widgetHeight = glWidget->height();
|
||||
|
||||
const float magnifyWidth = MAGNIFY_WIDTH * sizeMult;
|
||||
const float magnifyHeight = MAGNIFY_HEIGHT * sizeMult;
|
||||
|
@ -968,7 +968,7 @@ void ApplicationOverlay::renderAudioMeter() {
|
|||
float collisionSoundMagnitude = audio->getCollisionSoundMagnitude();
|
||||
const float VISIBLE_COLLISION_SOUND_MAGNITUDE = 0.5f;
|
||||
if (collisionSoundMagnitude > VISIBLE_COLLISION_SOUND_MAGNITUDE) {
|
||||
renderCollisionOverlay(glWidget->getDeviceWidth(), glWidget->getDeviceHeight(),
|
||||
renderCollisionOverlay(glWidget->width(), glWidget->height(),
|
||||
audio->getCollisionSoundMagnitude());
|
||||
}
|
||||
}
|
||||
|
@ -1019,16 +1019,16 @@ void ApplicationOverlay::renderAudioMeter() {
|
|||
if ((audio->getTimeSinceLastClip() > 0.f) && (audio->getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)) {
|
||||
const float MAX_MAGNITUDE = 0.7f;
|
||||
float magnitude = MAX_MAGNITUDE * (1 - audio->getTimeSinceLastClip() / CLIPPING_INDICATOR_TIME);
|
||||
renderCollisionOverlay(glWidget->getDeviceWidth(), glWidget->getDeviceHeight(), magnitude, 1.0f);
|
||||
renderCollisionOverlay(glWidget->width(), glWidget->height(), magnitude, 1.0f);
|
||||
}
|
||||
|
||||
audio->renderToolBox(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP,
|
||||
audioMeterY,
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::Mirror));
|
||||
|
||||
audio->renderScope(glWidget->getDeviceWidth(), glWidget->getDeviceHeight());
|
||||
audio->renderScope(glWidget->width(), glWidget->height());
|
||||
|
||||
audio->renderStats(WHITE_TEXT, glWidget->getDeviceWidth(), glWidget->getDeviceHeight());
|
||||
audio->renderStats(WHITE_TEXT, glWidget->width(), glWidget->height());
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
if (isClipping) {
|
||||
|
@ -1108,8 +1108,8 @@ void ApplicationOverlay::renderStatsAndLogs() {
|
|||
application->getPacketsPerSecond(), application->getBytesPerSecond(), voxelPacketsToProcess);
|
||||
// Bandwidth meter
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth)) {
|
||||
Stats::drawBackground(0x33333399, glWidget->getDeviceWidth() - 296, glWidget->getDeviceHeight() - 68, 296, 68);
|
||||
bandwidthMeter->render(glWidget->getDeviceWidth(), glWidget->getDeviceHeight());
|
||||
Stats::drawBackground(0x33333399, glWidget->width() - 296, glWidget->height() - 68, 296, 68);
|
||||
bandwidthMeter->render(glWidget->width(), glWidget->height());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1122,7 +1122,7 @@ void ApplicationOverlay::renderStatsAndLogs() {
|
|||
(Menu::getInstance()->isOptionChecked(MenuOption::Stats) &&
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth))
|
||||
? 80 : 20;
|
||||
drawText(glWidget->getDeviceWidth() - 100, glWidget->getDeviceHeight() - timerBottom,
|
||||
drawText(glWidget->width() - 100, glWidget->height() - timerBottom,
|
||||
0.30f, 0.0f, 0, frameTimer, WHITE_TEXT);
|
||||
}
|
||||
nodeBoundsDisplay.drawOverlay();
|
||||
|
@ -1247,8 +1247,8 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder() {
|
|||
|
||||
if (nodeList && !nodeList->getDomainHandler().isConnected()) {
|
||||
GLCanvas* glWidget = Application::getInstance()->getGLWidget();
|
||||
int right = glWidget->getDeviceWidth();
|
||||
int bottom = glWidget->getDeviceHeight();
|
||||
int right = glWidget->width();
|
||||
int bottom = glWidget->height();
|
||||
|
||||
glColor3f(CONNECTION_STATUS_BORDER_COLOR[0],
|
||||
CONNECTION_STATUS_BORDER_COLOR[1],
|
||||
|
|
|
@ -50,7 +50,7 @@ BandwidthMeter::ChannelInfo BandwidthMeter::_CHANNELS[] = {
|
|||
};
|
||||
|
||||
BandwidthMeter::BandwidthMeter() :
|
||||
_textRenderer(INCONSOLATA_FONT_FAMILY, -1, QFont::Bold, false),
|
||||
_textRenderer(TextRenderer::getInstance(INCONSOLATA_FONT_FAMILY, -1, QFont::Bold, false)),
|
||||
_scaleMaxIndex(INITIAL_SCALE_MAXIMUM_INDEX) {
|
||||
|
||||
_channels = static_cast<ChannelInfo*>( malloc(sizeof(_CHANNELS)) );
|
||||
|
@ -140,7 +140,7 @@ void BandwidthMeter::render(int screenWidth, int screenHeight) {
|
|||
float totalMax = glm::max(totalIn, totalOut);
|
||||
|
||||
// Get font / caption metrics
|
||||
QFontMetrics const& fontMetrics = _textRenderer.metrics();
|
||||
QFontMetrics const& fontMetrics = _textRenderer->metrics();
|
||||
int fontDescent = fontMetrics.descent();
|
||||
int labelWidthIn = fontMetrics.width(CAPTION_IN);
|
||||
int labelWidthOut = fontMetrics.width(CAPTION_OUT);
|
||||
|
@ -163,9 +163,9 @@ void BandwidthMeter::render(int screenWidth, int screenHeight) {
|
|||
|
||||
// Render captions
|
||||
setColorRGBA(COLOR_TEXT);
|
||||
_textRenderer.draw(barWidth + SPACING_LEFT_CAPTION_UNIT, textYcenteredLine, CAPTION_UNIT);
|
||||
_textRenderer.draw(-labelWidthIn - SPACING_RIGHT_CAPTION_IN_OUT, textYupperLine, CAPTION_IN);
|
||||
_textRenderer.draw(-labelWidthOut - SPACING_RIGHT_CAPTION_IN_OUT, textYlowerLine, CAPTION_OUT);
|
||||
_textRenderer->draw(barWidth + SPACING_LEFT_CAPTION_UNIT, textYcenteredLine, CAPTION_UNIT);
|
||||
_textRenderer->draw(-labelWidthIn - SPACING_RIGHT_CAPTION_IN_OUT, textYupperLine, CAPTION_IN);
|
||||
_textRenderer->draw(-labelWidthOut - SPACING_RIGHT_CAPTION_IN_OUT, textYlowerLine, CAPTION_OUT);
|
||||
|
||||
// Render vertical lines for the frame
|
||||
setColorRGBA(COLOR_FRAME);
|
||||
|
@ -229,11 +229,11 @@ void BandwidthMeter::render(int screenWidth, int screenHeight) {
|
|||
char fmtBuf[8];
|
||||
setColorRGBA(COLOR_TEXT);
|
||||
sprintf(fmtBuf, "%0.1f", totalIn);
|
||||
_textRenderer.draw(glm::max(xIn - fontMetrics.width(fmtBuf) - PADDING_HORIZ_VALUE,
|
||||
_textRenderer->draw(glm::max(xIn - fontMetrics.width(fmtBuf) - PADDING_HORIZ_VALUE,
|
||||
PADDING_HORIZ_VALUE),
|
||||
textYupperLine, fmtBuf);
|
||||
sprintf(fmtBuf, "%0.1f", totalOut);
|
||||
_textRenderer.draw(glm::max(xOut - fontMetrics.width(fmtBuf) - PADDING_HORIZ_VALUE,
|
||||
_textRenderer->draw(glm::max(xOut - fontMetrics.width(fmtBuf) - PADDING_HORIZ_VALUE,
|
||||
PADDING_HORIZ_VALUE),
|
||||
textYlowerLine, fmtBuf);
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ private:
|
|||
|
||||
static ChannelInfo _CHANNELS[];
|
||||
|
||||
TextRenderer _textRenderer;
|
||||
TextRenderer* _textRenderer;
|
||||
ChannelInfo* _channels;
|
||||
Stream _streams[N_STREAMS];
|
||||
int _scaleMaxIndex;
|
||||
|
|
|
@ -19,8 +19,8 @@ ChatMessageArea::ChatMessageArea(bool useFixedHeight) : QTextBrowser(), _useFixe
|
|||
|
||||
connect(document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged,
|
||||
this, &ChatMessageArea::updateLayout);
|
||||
connect(this, &QTextBrowser::anchorClicked,
|
||||
Menu::getInstance(), &Menu::openUrl);
|
||||
|
||||
connect(this, &QTextBrowser::anchorClicked, Application::getInstance(), &Application::openUrl);
|
||||
}
|
||||
|
||||
void ChatMessageArea::setHtml(const QString& html) {
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
#include <QSizePolicy>
|
||||
#include <QTimer>
|
||||
|
||||
#include <AddressManager.h>
|
||||
#include <AccountManager.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "ChatMessageArea.h"
|
||||
#include "FlowLayout.h"
|
||||
|
@ -28,7 +31,6 @@
|
|||
#include "ChatWindow.h"
|
||||
|
||||
|
||||
|
||||
const int NUM_MESSAGES_TO_TIME_STAMP = 20;
|
||||
|
||||
const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?)|(?:hifi))://\\S+)");
|
||||
|
@ -169,7 +171,7 @@ bool ChatWindow::eventFilter(QObject* sender, QEvent* event) {
|
|||
} else if (event->type() == QEvent::MouseButtonRelease) {
|
||||
QVariant userVar = sender->property("user");
|
||||
if (userVar.isValid()) {
|
||||
Menu::getInstance()->goToUser("@" + userVar.toString());
|
||||
AddressManager::getInstance().goToUser(userVar.toString());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <limits>
|
||||
|
||||
// include this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
|
@ -37,6 +39,8 @@
|
|||
#include "Application.h"
|
||||
#include "MetavoxelEditor.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
enum GridPlane {
|
||||
GRID_PLANE_XY, GRID_PLANE_XZ, GRID_PLANE_YZ
|
||||
};
|
||||
|
@ -959,7 +963,8 @@ void HeightfieldTool::render() {
|
|||
}
|
||||
|
||||
ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) :
|
||||
HeightfieldTool(editor, "Import Heightfield") {
|
||||
HeightfieldTool(editor, "Import Heightfield"),
|
||||
_loadingImage(false) {
|
||||
|
||||
_form->addRow("Block Size:", _blockSize = new QSpinBox());
|
||||
_blockSize->setPrefix("2^");
|
||||
|
@ -970,6 +975,32 @@ ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) :
|
|||
&ImportHeightfieldTool::updatePreview);
|
||||
_form->addRow("Height:", _height = new QPushButton());
|
||||
connect(_height, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectHeightFile);
|
||||
|
||||
_form->addRow(_rawOptions = new QWidget());
|
||||
QHBoxLayout* rawLayout = new QHBoxLayout();
|
||||
_rawOptions->setLayout(rawLayout);
|
||||
_rawOptions->setVisible(false);
|
||||
|
||||
rawLayout->addStretch(1);
|
||||
rawLayout->addWidget(new QLabel("Scale:"));
|
||||
rawLayout->addWidget(_heightScale = new QDoubleSpinBox());
|
||||
const double MAX_OFFSET_SCALE = 100000.0;
|
||||
_heightScale->setMaximum(MAX_OFFSET_SCALE);
|
||||
_heightScale->setSingleStep(0.0001);
|
||||
_heightScale->setDecimals(4);
|
||||
_heightScale->setValue(1.0);
|
||||
connect(_heightScale, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this,
|
||||
&ImportHeightfieldTool::updateHeightImage);
|
||||
|
||||
rawLayout->addSpacing(15);
|
||||
rawLayout->addWidget(new QLabel("Offset:"));
|
||||
rawLayout->addWidget(_heightOffset = new QDoubleSpinBox());
|
||||
_heightOffset->setMinimum(-MAX_OFFSET_SCALE);
|
||||
_heightOffset->setMaximum(MAX_OFFSET_SCALE);
|
||||
_heightOffset->setDecimals(4);
|
||||
connect(_heightOffset, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this,
|
||||
&ImportHeightfieldTool::updateHeightImage);
|
||||
|
||||
_form->addRow("Color:", _color = new QPushButton());
|
||||
connect(_color, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectColorFile);
|
||||
|
||||
|
@ -1012,22 +1043,50 @@ void ImportHeightfieldTool::apply() {
|
|||
}
|
||||
}
|
||||
|
||||
const float EIGHT_BIT_MAXIMUM = 255.0f;
|
||||
|
||||
void ImportHeightfieldTool::selectHeightFile() {
|
||||
QString filename = QFileDialog::getOpenFileName(this, "Select Height Image", QString(), "Images (*.png *.jpg)");
|
||||
QString filename = QFileDialog::getOpenFileName(this, "Select Height Image", QString(),
|
||||
"Images (*.png *.jpg *.bmp *.raw)");
|
||||
if (filename.isNull()) {
|
||||
return;
|
||||
}
|
||||
if (filename.toLower().endsWith(".raw")) {
|
||||
QFile input(filename);
|
||||
input.open(QIODevice::ReadOnly);
|
||||
QDataStream in(&input);
|
||||
in.setByteOrder(QDataStream::LittleEndian);
|
||||
_rawHeight.clear();
|
||||
int minHeight = numeric_limits<quint16>::max();
|
||||
int maxHeight = numeric_limits<quint16>::min();
|
||||
while (!in.atEnd()) {
|
||||
quint16 height;
|
||||
in >> height;
|
||||
_rawHeight.append(height);
|
||||
minHeight = qMin(minHeight, (int)height);
|
||||
maxHeight = qMax(maxHeight, (int)height);
|
||||
}
|
||||
_height->setText(filename);
|
||||
_rawOptions->setVisible(true);
|
||||
_loadingImage = true;
|
||||
_heightScale->setValue((EIGHT_BIT_MAXIMUM - 1.0f) / (maxHeight - minHeight));
|
||||
_heightOffset->setValue(-minHeight * _heightScale->value() + 1.0);
|
||||
_loadingImage = false;
|
||||
updateHeightImage();
|
||||
return;
|
||||
}
|
||||
if (!_heightImage.load(filename)) {
|
||||
QMessageBox::warning(this, "Invalid Image", "The selected image could not be read.");
|
||||
return;
|
||||
}
|
||||
_rawOptions->setVisible(false);
|
||||
_heightImage = _heightImage.convertToFormat(QImage::Format_RGB888);
|
||||
_height->setText(filename);
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
void ImportHeightfieldTool::selectColorFile() {
|
||||
QString filename = QFileDialog::getOpenFileName(this, "Select Color Image", QString(), "Images (*.png *.jpg)");
|
||||
QString filename = QFileDialog::getOpenFileName(this, "Select Color Image", QString(), "Images (*.png *.jpg *.bmp)");
|
||||
if (filename.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
@ -1040,6 +1099,26 @@ void ImportHeightfieldTool::selectColorFile() {
|
|||
updatePreview();
|
||||
}
|
||||
|
||||
void ImportHeightfieldTool::updateHeightImage() {
|
||||
if (_loadingImage) {
|
||||
return;
|
||||
}
|
||||
int size = glm::sqrt(_rawHeight.size());
|
||||
_heightImage = QImage(size, size, QImage::Format_RGB888);
|
||||
const quint16* src = _rawHeight.constData();
|
||||
float scale = _heightScale->value(), offset = _heightOffset->value();
|
||||
for (int y = 0; y < size; y++) {
|
||||
uchar* dest = _heightImage.scanLine(y);
|
||||
for (const quint16* end = src + size; src != end; src++) {
|
||||
uchar height = glm::clamp(*src * scale + offset, 1.0f, EIGHT_BIT_MAXIMUM);
|
||||
*dest++ = height;
|
||||
*dest++ = height;
|
||||
*dest++ = height;
|
||||
}
|
||||
}
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
void ImportHeightfieldTool::updatePreview() {
|
||||
QVector<BufferDataPointer> buffers;
|
||||
if (_heightImage.width() > 0 && _heightImage.height() > 0) {
|
||||
|
@ -1061,7 +1140,7 @@ void ImportHeightfieldTool::updatePreview() {
|
|||
uchar* src = _heightImage.scanLine(extendedI + y) + extendedJ * DataBlock::COLOR_BYTES;
|
||||
char* dest = height.data() + (y + offsetY) * heightSize + offsetX;
|
||||
for (int x = 0; x < columns; x++) {
|
||||
*dest++ = *src;
|
||||
*dest++ = qMax((uchar)1, *src);
|
||||
src += DataBlock::COLOR_BYTES;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -291,6 +291,7 @@ private slots:
|
|||
|
||||
void selectHeightFile();
|
||||
void selectColorFile();
|
||||
void updateHeightImage();
|
||||
void updatePreview();
|
||||
void renderPreview();
|
||||
|
||||
|
@ -299,8 +300,14 @@ private:
|
|||
QSpinBox* _blockSize;
|
||||
|
||||
QPushButton* _height;
|
||||
QWidget* _rawOptions;
|
||||
QDoubleSpinBox* _heightScale;
|
||||
QDoubleSpinBox* _heightOffset;
|
||||
bool _loadingImage;
|
||||
|
||||
QPushButton* _color;
|
||||
|
||||
QVector<quint16> _rawHeight;
|
||||
QImage _heightImage;
|
||||
QImage _colorImage;
|
||||
|
||||
|
|
|
@ -41,8 +41,8 @@ void NodeBounds::draw() {
|
|||
// itself after the cursor disappears.
|
||||
Application* application = Application::getInstance();
|
||||
GLCanvas* glWidget = application->getGLWidget();
|
||||
float mouseX = application->getMouseX() / (float)glWidget->getDeviceWidth();
|
||||
float mouseY = application->getMouseY() / (float)glWidget->getDeviceHeight();
|
||||
float mouseX = application->getMouseX() / (float)glWidget->width();
|
||||
float mouseY = application->getMouseY() / (float)glWidget->height();
|
||||
glm::vec3 mouseRayOrigin;
|
||||
glm::vec3 mouseRayDirection;
|
||||
application->getViewFrustum()->computePickRay(mouseX, mouseY, mouseRayOrigin, mouseRayDirection);
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include <QtWebKitWidgets/QWebView>
|
||||
|
||||
#include <AccountManager.h>
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
#include "OAuthWebViewHandler.h"
|
||||
|
|
|
@ -121,6 +121,8 @@ void PreferencesDialog::loadPreferences() {
|
|||
ui.faceshiftEyeDeflectionSider->setValue(menuInstance->getFaceshiftEyeDeflection() *
|
||||
ui.faceshiftEyeDeflectionSider->maximum());
|
||||
|
||||
ui.faceshiftHostnameEdit->setText(menuInstance->getFaceshiftHostname());
|
||||
|
||||
const InboundAudioStream::Settings& streamSettings = menuInstance->getReceivedAudioStreamSettings();
|
||||
|
||||
ui.dynamicJitterBuffersCheckBox->setChecked(streamSettings._dynamicJitterBuffers);
|
||||
|
@ -165,19 +167,29 @@ void PreferencesDialog::savePreferences() {
|
|||
}
|
||||
|
||||
QUrl faceModelURL(ui.faceURLEdit->text());
|
||||
if (faceModelURL.toString() != _faceURLString) {
|
||||
// change the faceModelURL in the profile, it will also update this user's BlendFace
|
||||
myAvatar->setFaceModelURL(faceModelURL);
|
||||
UserActivityLogger::getInstance().changedModel("head", faceModelURL.toString());
|
||||
shouldDispatchIdentityPacket = true;
|
||||
QString faceModelURLString = faceModelURL.toString();
|
||||
if (faceModelURLString != _faceURLString) {
|
||||
if (faceModelURLString.isEmpty() || faceModelURLString.toLower().endsWith(".fst")) {
|
||||
// change the faceModelURL in the profile, it will also update this user's BlendFace
|
||||
myAvatar->setFaceModelURL(faceModelURL);
|
||||
UserActivityLogger::getInstance().changedModel("head", faceModelURLString);
|
||||
shouldDispatchIdentityPacket = true;
|
||||
} else {
|
||||
qDebug() << "ERROR: Head model not FST or blank - " << faceModelURLString;
|
||||
}
|
||||
}
|
||||
|
||||
QUrl skeletonModelURL(ui.skeletonURLEdit->text());
|
||||
if (skeletonModelURL.toString() != _skeletonURLString) {
|
||||
// change the skeletonModelURL in the profile, it will also update this user's Body
|
||||
myAvatar->setSkeletonModelURL(skeletonModelURL);
|
||||
UserActivityLogger::getInstance().changedModel("skeleton", skeletonModelURL.toString());
|
||||
shouldDispatchIdentityPacket = true;
|
||||
QString skeletonModelURLString = skeletonModelURL.toString();
|
||||
if (skeletonModelURLString != _skeletonURLString) {
|
||||
if (skeletonModelURLString.isEmpty() || skeletonModelURLString.toLower().endsWith(".fst")) {
|
||||
// change the skeletonModelURL in the profile, it will also update this user's Body
|
||||
myAvatar->setSkeletonModelURL(skeletonModelURL);
|
||||
UserActivityLogger::getInstance().changedModel("skeleton", skeletonModelURLString);
|
||||
shouldDispatchIdentityPacket = true;
|
||||
} else {
|
||||
qDebug() << "ERROR: Skeleton model not FST or blank - " << skeletonModelURLString;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldDispatchIdentityPacket) {
|
||||
|
@ -211,6 +223,9 @@ void PreferencesDialog::savePreferences() {
|
|||
|
||||
Menu::getInstance()->setFaceshiftEyeDeflection(ui.faceshiftEyeDeflectionSider->value() /
|
||||
(float)ui.faceshiftEyeDeflectionSider->maximum());
|
||||
|
||||
Menu::getInstance()->setFaceshiftHostname(ui.faceshiftHostnameEdit->text());
|
||||
|
||||
Menu::getInstance()->setMaxVoxelPacketsPerSecond(ui.maxVoxelsPPSSpin->value());
|
||||
|
||||
Menu::getInstance()->setOculusUIAngularSize(ui.oculusUIAngularSizeSpin->value());
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
#include <NetworkAccessManager.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "ScriptHighlighting.h"
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ Stats::Stats():
|
|||
_metavoxelReceiveTotal(0)
|
||||
{
|
||||
GLCanvas* glWidget = Application::getInstance()->getGLWidget();
|
||||
resetWidth(glWidget->getDeviceWidth(), 0);
|
||||
resetWidth(glWidget->width(), 0);
|
||||
}
|
||||
|
||||
void Stats::toggleExpanded() {
|
||||
|
@ -114,7 +114,7 @@ void Stats::checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseD
|
|||
// top-right stats click
|
||||
lines = _expanded ? 11 : 3;
|
||||
statsHeight = lines * STATS_PELS_PER_LINE + 10;
|
||||
statsWidth = glWidget->getDeviceWidth() - statsX;
|
||||
statsWidth = glWidget->width() - statsX;
|
||||
if (mouseX > statsX && mouseX < statsX + statsWidth && mouseY > statsY && mouseY < statsY + statsHeight) {
|
||||
toggleExpanded();
|
||||
return;
|
||||
|
@ -123,7 +123,7 @@ void Stats::checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseD
|
|||
|
||||
void Stats::resetWidth(int width, int horizontalOffset) {
|
||||
GLCanvas* glWidget = Application::getInstance()->getGLWidget();
|
||||
int extraSpace = glWidget->getDeviceWidth() - horizontalOffset -2
|
||||
int extraSpace = glWidget->width() - horizontalOffset -2
|
||||
- STATS_GENERAL_MIN_WIDTH
|
||||
- (Menu::getInstance()->isOptionChecked(MenuOption::TestPing) ? STATS_PING_MIN_WIDTH -1 : 0)
|
||||
- STATS_GEO_MIN_WIDTH
|
||||
|
@ -147,7 +147,7 @@ void Stats::resetWidth(int width, int horizontalOffset) {
|
|||
_pingStatsWidth += (int) extraSpace / panels;
|
||||
}
|
||||
_geoStatsWidth += (int) extraSpace / panels;
|
||||
_voxelStatsWidth += glWidget->getDeviceWidth() - (_generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3);
|
||||
_voxelStatsWidth += glWidget->width() - (_generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,7 +210,7 @@ void Stats::display(
|
|||
std::stringstream voxelStats;
|
||||
|
||||
if (_lastHorizontalOffset != horizontalOffset) {
|
||||
resetWidth(glWidget->getDeviceWidth(), horizontalOffset);
|
||||
resetWidth(glWidget->width(), horizontalOffset);
|
||||
_lastHorizontalOffset = horizontalOffset;
|
||||
}
|
||||
|
||||
|
@ -410,7 +410,7 @@ void Stats::display(
|
|||
}
|
||||
}
|
||||
|
||||
drawBackground(backgroundColor, horizontalOffset, 0, glWidget->getDeviceWidth() - horizontalOffset,
|
||||
drawBackground(backgroundColor, horizontalOffset, 0, glWidget->width() - horizontalOffset,
|
||||
lines * STATS_PELS_PER_LINE + 10);
|
||||
horizontalOffset += 5;
|
||||
|
||||
|
|
|
@ -9,11 +9,14 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDesktopWidget>
|
||||
#include <QFont>
|
||||
#include <QPaintEngine>
|
||||
#include <QtDebug>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QWindow>
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
#include "TextRenderer.h"
|
||||
|
@ -21,15 +24,23 @@
|
|||
// the width/height of the cached glyph textures
|
||||
const int IMAGE_SIZE = 256;
|
||||
|
||||
Glyph::Glyph(int textureID, const QPoint& location, const QRect& bounds, int width) :
|
||||
_textureID(textureID), _location(location), _bounds(bounds), _width(width) {
|
||||
static uint qHash(const TextRenderer::Properties& key, uint seed = 0) {
|
||||
// can be switched to qHash(key.font, seed) when we require Qt 5.3+
|
||||
return qHash(key.font.family(), qHash(key.font.pointSize(), seed));
|
||||
}
|
||||
|
||||
TextRenderer::TextRenderer(const char* family, int pointSize, int weight,
|
||||
bool italic, EffectType effectType, int effectThickness, QColor color)
|
||||
: _font(family, pointSize, weight, italic), _metrics(_font), _effectType(effectType),
|
||||
_effectThickness(effectThickness), _x(IMAGE_SIZE), _y(IMAGE_SIZE), _rowHeight(0), _color(color) {
|
||||
_font.setKerning(false);
|
||||
static bool operator==(const TextRenderer::Properties& p1, const TextRenderer::Properties& p2) {
|
||||
return p1.font == p2.font && p1.effect == p2.effect && p1.effectThickness == p2.effectThickness && p1.color == p2.color;
|
||||
}
|
||||
|
||||
TextRenderer* TextRenderer::getInstance(const char* family, int pointSize, int weight, bool italic,
|
||||
EffectType effect, int effectThickness, const QColor& color) {
|
||||
Properties properties = { QFont(family, pointSize, weight, italic), effect, effectThickness, color };
|
||||
TextRenderer*& instance = _instances[properties];
|
||||
if (!instance) {
|
||||
instance = new TextRenderer(properties);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
TextRenderer::~TextRenderer() {
|
||||
|
@ -73,7 +84,7 @@ int TextRenderer::draw(int x, int y, const char* str) {
|
|||
int bottom = y + glyph.bounds().y();
|
||||
int top = y + glyph.bounds().y() + glyph.bounds().height();
|
||||
|
||||
float scale = 1.0 / IMAGE_SIZE;
|
||||
float scale = QApplication::desktop()->windowHandle()->devicePixelRatio() / IMAGE_SIZE;
|
||||
float ls = glyph.location().x() * scale;
|
||||
float rs = (glyph.location().x() + glyph.bounds().width()) * scale;
|
||||
float bt = glyph.location().y() * scale;
|
||||
|
@ -112,6 +123,19 @@ int TextRenderer::computeWidth(const char* str)
|
|||
return width;
|
||||
}
|
||||
|
||||
TextRenderer::TextRenderer(const Properties& properties) :
|
||||
_font(properties.font),
|
||||
_metrics(_font),
|
||||
_effectType(properties.effect),
|
||||
_effectThickness(properties.effectThickness),
|
||||
_x(IMAGE_SIZE),
|
||||
_y(IMAGE_SIZE),
|
||||
_rowHeight(0),
|
||||
_color(properties.color) {
|
||||
|
||||
_font.setKerning(false);
|
||||
}
|
||||
|
||||
const Glyph& TextRenderer::getGlyph(char c) {
|
||||
Glyph& glyph = _glyphs[c];
|
||||
if (glyph.isValid()) {
|
||||
|
@ -119,21 +143,25 @@ const Glyph& TextRenderer::getGlyph(char c) {
|
|||
}
|
||||
// we use 'J' as a representative size for the solid block character
|
||||
QChar ch = (c == SOLID_BLOCK_CHAR) ? QChar('J') : QChar(c);
|
||||
QRect bounds = _metrics.boundingRect(ch);
|
||||
if (bounds.isEmpty()) {
|
||||
QRect baseBounds = _metrics.boundingRect(ch);
|
||||
if (baseBounds.isEmpty()) {
|
||||
glyph = Glyph(0, QPoint(), QRect(), _metrics.width(ch));
|
||||
return glyph;
|
||||
}
|
||||
// grow the bounds to account for effect, if any
|
||||
if (_effectType == SHADOW_EFFECT) {
|
||||
bounds.adjust(-_effectThickness, 0, 0, _effectThickness);
|
||||
baseBounds.adjust(-_effectThickness, 0, 0, _effectThickness);
|
||||
|
||||
} else if (_effectType == OUTLINE_EFFECT) {
|
||||
bounds.adjust(-_effectThickness, -_effectThickness, _effectThickness, _effectThickness);
|
||||
baseBounds.adjust(-_effectThickness, -_effectThickness, _effectThickness, _effectThickness);
|
||||
}
|
||||
|
||||
// grow the bounds to account for antialiasing
|
||||
bounds.adjust(-1, -1, 1, 1);
|
||||
baseBounds.adjust(-1, -1, 1, 1);
|
||||
|
||||
// adjust bounds for device pixel scaling
|
||||
float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio();
|
||||
QRect bounds(baseBounds.x() * ratio, baseBounds.y() * ratio, baseBounds.width() * ratio, baseBounds.height() * ratio);
|
||||
|
||||
if (_x + bounds.width() > IMAGE_SIZE) {
|
||||
// we can't fit it on the current row; move to next
|
||||
|
@ -162,9 +190,16 @@ const Glyph& TextRenderer::getGlyph(char c) {
|
|||
} else {
|
||||
image.fill(0);
|
||||
QPainter painter(&image);
|
||||
painter.setFont(_font);
|
||||
QFont font = _font;
|
||||
if (ratio == 1.0f) {
|
||||
painter.setFont(_font);
|
||||
} else {
|
||||
QFont enlargedFont = _font;
|
||||
enlargedFont.setPointSize(_font.pointSize() * ratio);
|
||||
painter.setFont(enlargedFont);
|
||||
}
|
||||
if (_effectType == SHADOW_EFFECT) {
|
||||
for (int i = 0; i < _effectThickness; i++) {
|
||||
for (int i = 0; i < _effectThickness * ratio; i++) {
|
||||
painter.drawText(-bounds.x() - 1 - i, -bounds.y() + 1 + i, ch);
|
||||
}
|
||||
} else if (_effectType == OUTLINE_EFFECT) {
|
||||
|
@ -173,7 +208,7 @@ const Glyph& TextRenderer::getGlyph(char c) {
|
|||
font.setStyleStrategy(QFont::ForceOutline);
|
||||
path.addText(-bounds.x() - 0.5, -bounds.y() + 0.5, font, ch);
|
||||
QPen pen;
|
||||
pen.setWidth(_effectThickness);
|
||||
pen.setWidth(_effectThickness * ratio);
|
||||
pen.setJoinStyle(Qt::RoundJoin);
|
||||
pen.setCapStyle(Qt::RoundCap);
|
||||
painter.setPen(pen);
|
||||
|
@ -185,10 +220,17 @@ const Glyph& TextRenderer::getGlyph(char c) {
|
|||
}
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, bounds.width(), bounds.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.constBits());
|
||||
|
||||
glyph = Glyph(_currentTextureID, QPoint(_x, _y), bounds, _metrics.width(ch));
|
||||
glyph = Glyph(_currentTextureID, QPoint(_x / ratio, _y / ratio), baseBounds, _metrics.width(ch));
|
||||
_x += bounds.width();
|
||||
_rowHeight = qMax(_rowHeight, bounds.height());
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
return glyph;
|
||||
}
|
||||
|
||||
QHash<TextRenderer::Properties, TextRenderer*> TextRenderer::_instances;
|
||||
|
||||
Glyph::Glyph(int textureID, const QPoint& location, const QRect& bounds, int width) :
|
||||
_textureID(textureID), _location(location), _bounds(bounds), _width(width) {
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,6 @@ const char SOLID_BLOCK_CHAR = 127;
|
|||
// the Inconsolata font family
|
||||
#define INCONSOLATA_FONT_FAMILY "Inconsolata"
|
||||
|
||||
|
||||
class Glyph;
|
||||
|
||||
class TextRenderer {
|
||||
|
@ -41,9 +40,17 @@ public:
|
|||
|
||||
enum EffectType { NO_EFFECT, SHADOW_EFFECT, OUTLINE_EFFECT };
|
||||
|
||||
TextRenderer(const char* family, int pointSize = -1, int weight = -1, bool italic = false,
|
||||
EffectType effect = NO_EFFECT, int effectThickness = 1,
|
||||
QColor color = QColor(255, 255, 255));
|
||||
class Properties {
|
||||
public:
|
||||
QFont font;
|
||||
EffectType effect;
|
||||
int effectThickness;
|
||||
QColor color;
|
||||
};
|
||||
|
||||
static TextRenderer* getInstance(const char* family, int pointSize = -1, int weight = -1, bool italic = false,
|
||||
EffectType effect = NO_EFFECT, int effectThickness = 1, const QColor& color = QColor(255, 255, 255));
|
||||
|
||||
~TextRenderer();
|
||||
|
||||
const QFontMetrics& metrics() const { return _metrics; }
|
||||
|
@ -59,7 +66,9 @@ public:
|
|||
|
||||
private:
|
||||
|
||||
const Glyph& getGlyph (char c);
|
||||
TextRenderer(const Properties& properties);
|
||||
|
||||
const Glyph& getGlyph(char c);
|
||||
|
||||
// the font to render
|
||||
QFont _font;
|
||||
|
@ -90,6 +99,8 @@ private:
|
|||
|
||||
// text color
|
||||
QColor _color;
|
||||
|
||||
static QHash<Properties, TextRenderer*> _instances;
|
||||
};
|
||||
|
||||
class Glyph {
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include <QInputDialog>
|
||||
#include <QPushButton>
|
||||
|
||||
#include <AddressManager.h>
|
||||
|
||||
#include "Menu.h"
|
||||
#include "UserLocationsDialog.h"
|
||||
|
||||
|
@ -51,8 +53,8 @@ void UserLocationsDialog::updateEnabled() {
|
|||
}
|
||||
|
||||
void UserLocationsDialog::goToModelIndex(const QModelIndex& index) {
|
||||
QVariant location = _proxyModel.data(index.sibling(index.row(), UserLocationsModel::LocationColumn));
|
||||
Menu::getInstance()->goToURL(location.toString());
|
||||
QVariant address = _proxyModel.data(index.sibling(index.row(), UserLocationsModel::AddressColumn));
|
||||
AddressManager::getInstance().handleLookupString(address.toString());
|
||||
}
|
||||
|
||||
void UserLocationsDialog::deleteSelection() {
|
||||
|
|
|
@ -9,7 +9,9 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "../../Application.h"
|
||||
#include <NetworkAccessManager.h>
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
#include "BillboardOverlay.h"
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ void ModelOverlay::render() {
|
|||
float depth = unRotatedExtents.z;
|
||||
|
||||
Extents rotatedExtents = _model.getUnscaledMeshExtents();
|
||||
calculateRotatedExtents(rotatedExtents, _rotation);
|
||||
rotatedExtents.rotate(_rotation);
|
||||
|
||||
glm::vec3 rotatedSize = rotatedExtents.maximum - rotatedExtents.minimum;
|
||||
|
||||
|
|
|
@ -45,22 +45,21 @@ void TextOverlay::render() {
|
|||
|
||||
//TextRenderer(const char* family, int pointSize = -1, int weight = -1, bool italic = false,
|
||||
// EffectType effect = NO_EFFECT, int effectThickness = 1);
|
||||
TextRenderer textRenderer(SANS_FONT_FAMILY, _fontSize, 50, false, TextRenderer::NO_EFFECT, 1,
|
||||
QColor(_color.red, _color.green, _color.blue));
|
||||
TextRenderer* textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, _fontSize, 50);
|
||||
|
||||
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
|
||||
int x = _bounds.left() + _leftMargin + leftAdjust;
|
||||
int y = _bounds.top() + _topMargin + topAdjust;
|
||||
|
||||
glColor3f(1.0f, 1.0f, 1.0f);
|
||||
glColor3f(_color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR);
|
||||
QStringList lines = _text.split("\n");
|
||||
int lineOffset = 0;
|
||||
foreach(QString thisLine, lines) {
|
||||
if (lineOffset == 0) {
|
||||
lineOffset = textRenderer.calculateHeight(qPrintable(thisLine));
|
||||
lineOffset = textRenderer->calculateHeight(qPrintable(thisLine));
|
||||
}
|
||||
lineOffset += textRenderer.draw(x, y + lineOffset, qPrintable(thisLine));
|
||||
lineOffset += textRenderer->draw(x, y + lineOffset, qPrintable(thisLine));
|
||||
|
||||
const int lineGap = 2;
|
||||
lineOffset += lineGap;
|
||||
|
|
|
@ -1726,6 +1726,70 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_999">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>7</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>7</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_999">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Faceshift hostname</string>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>faceshiftHostnameEdit</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_999">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="faceshiftHostnameEdit">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>localhost</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="voxelsTitleLabel">
|
||||
<property name="sizePolicy">
|
||||
|
|
212
libraries/audio/src/AudioBuffer.h
Normal file
212
libraries/audio/src/AudioBuffer.h
Normal file
|
@ -0,0 +1,212 @@
|
|||
//
|
||||
// AudioBuffer.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Craig Hansen-Sturm on 8/29/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_AudioBuffer_h
|
||||
#define hifi_AudioBuffer_h
|
||||
|
||||
#include <typeinfo>
|
||||
|
||||
template< typename T >
|
||||
class AudioFrameBuffer {
|
||||
|
||||
uint16_t _channelCount;
|
||||
uint16_t _channelCountMax;
|
||||
uint16_t _frameCount;
|
||||
uint16_t _frameCountMax;
|
||||
|
||||
T** _frameBuffer;
|
||||
|
||||
void allocateFrames() {
|
||||
_frameBuffer = new T*[_channelCountMax];
|
||||
if (_frameBuffer) {
|
||||
for (uint16_t i = 0; i < _channelCountMax; ++i) {
|
||||
_frameBuffer[i] = new T[_frameCountMax];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void deallocateFrames() {
|
||||
if (_frameBuffer) {
|
||||
for (uint16_t i = 0; i < _channelCountMax; ++i) {
|
||||
delete _frameBuffer[i];
|
||||
}
|
||||
delete _frameBuffer;
|
||||
}
|
||||
_frameBuffer = NULL;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
AudioFrameBuffer() :
|
||||
_channelCount(0),
|
||||
_frameCount(0),
|
||||
_frameCountMax(0),
|
||||
_frameBuffer(NULL) {
|
||||
}
|
||||
|
||||
AudioFrameBuffer(const uint16_t channelCount, const uint16_t frameCount) :
|
||||
_channelCount(channelCount),
|
||||
_channelCountMax(channelCount),
|
||||
_frameCount(frameCount),
|
||||
_frameCountMax(frameCount),
|
||||
_frameBuffer(NULL) {
|
||||
allocateFrames();
|
||||
}
|
||||
|
||||
~AudioFrameBuffer() {
|
||||
finalize();
|
||||
}
|
||||
|
||||
void initialize(const uint16_t channelCount, const uint16_t frameCount) {
|
||||
if (_frameBuffer) {
|
||||
finalize();
|
||||
}
|
||||
_channelCount = channelCount;
|
||||
_channelCountMax = channelCount;
|
||||
_frameCount = frameCount;
|
||||
_frameCountMax = frameCount;
|
||||
allocateFrames();
|
||||
}
|
||||
|
||||
void finalize() {
|
||||
deallocateFrames();
|
||||
_channelCount = 0;
|
||||
_channelCountMax = 0;
|
||||
_frameCount = 0;
|
||||
_frameCountMax = 0;
|
||||
}
|
||||
|
||||
T**& getFrameData() {
|
||||
return _frameBuffer;
|
||||
}
|
||||
|
||||
uint16_t getChannelCount() {
|
||||
return _channelCount;
|
||||
}
|
||||
|
||||
uint16_t getFrameCount() {
|
||||
return _frameCount;
|
||||
}
|
||||
|
||||
void zeroFrames() {
|
||||
if (!_frameBuffer) {
|
||||
return;
|
||||
}
|
||||
for (uint16_t i = 0; i < _channelCountMax; ++i) {
|
||||
memset(_frameBuffer[i], 0, sizeof(T)*_frameCountMax);
|
||||
}
|
||||
}
|
||||
|
||||
template< typename S >
|
||||
void copyFrames(uint16_t channelCount, const uint16_t frameCount, S* frames, const bool copyOut = false) {
|
||||
if ( !_frameBuffer || !frames) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (channelCount <=_channelCountMax && frameCount <=_frameCountMax) {
|
||||
// We always allow copying fewer frames than we have allocated
|
||||
_frameCount = frameCount;
|
||||
_channelCount = channelCount;
|
||||
}
|
||||
else {
|
||||
//
|
||||
// However we do not attempt to copy more frames than we've allocated ;-) This is a framing error caused by either
|
||||
// a/ the platform audio driver not correctly queuing and regularly smoothing device IO capture frames -or-
|
||||
// b/ our IO processing thread (currently running on a Qt GUI thread) has been delayed/scheduled too late.
|
||||
//
|
||||
// The fix is not to make the problem worse by allocating additional frames on this thread, rather, it is to handle
|
||||
// dynamic re-sizing off the IO processing thread. While a/ is not in our control, we will address the off thread
|
||||
// re-sizing,, as well as b/, in later releases.
|
||||
//
|
||||
// For now, we log this condition, and do our best to recover by copying as many frames as we have allocated.
|
||||
// Unfortunately, this will result (temporarily), in an audible discontinuity.
|
||||
//
|
||||
// If you repeatedly receive this error, contact craig@highfidelity.io and send me what audio device you are using,
|
||||
// what audio-stack you are using (pulse/alsa, core audio, ...), what OS, and what the reported frame/channel
|
||||
// counts are. In addition, any information about what you were doing at the time of the discontinuity, would be
|
||||
// useful (e.g., accessing any client features/menus)
|
||||
//
|
||||
qDebug() << "Audio framing error: _channelCount="
|
||||
<< _channelCount
|
||||
<< "channelCountMax="
|
||||
<< _channelCountMax
|
||||
<< "_frameCount="
|
||||
<< _frameCount
|
||||
<< "frameCountMax="
|
||||
<< _frameCountMax;
|
||||
|
||||
|
||||
_channelCount = std::min(_channelCount,_channelCountMax);
|
||||
_frameCount = std::min(_frameCount,_frameCountMax);
|
||||
}
|
||||
|
||||
if (copyOut) {
|
||||
S* dst = frames;
|
||||
|
||||
if(typeid(T) == typeid(S)) { // source and destination types are the same
|
||||
for (int i = 0; i < _frameCount; ++i) {
|
||||
for (int j = 0; j < _channelCount; ++j) {
|
||||
*dst++ = _frameBuffer[j][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(typeid(T) == typeid(float32_t) &&
|
||||
typeid(S) == typeid(int16_t)) {
|
||||
|
||||
const int scale = (2 << ((8 * sizeof(S)) - 1));
|
||||
|
||||
for (int i = 0; i < _frameCount; ++i) {
|
||||
for (int j = 0; j < _channelCount; ++j) {
|
||||
*dst++ = (S)(_frameBuffer[j][i] * scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert(0); // currently unsupported conversion
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // copyIn
|
||||
S* src = frames;
|
||||
|
||||
if(typeid(T) == typeid(S)) { // source and destination types are the same
|
||||
for (int i = 0; i < _frameCount; ++i) {
|
||||
for (int j = 0; j < _channelCount; ++j) {
|
||||
_frameBuffer[j][i] = *src++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(typeid(T) == typeid(float32_t) &&
|
||||
typeid(S) == typeid(int16_t)) {
|
||||
|
||||
const int scale = (2 << ((8 * sizeof(S)) - 1));
|
||||
|
||||
for (int i = 0; i < _frameCount; ++i) {
|
||||
for (int j = 0; j < _channelCount; ++j) {
|
||||
_frameBuffer[j][i] = ((T)(*src++)) / scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert(0); // currently unsupported conversion
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef AudioFrameBuffer< float32_t > AudioBufferFloat32;
|
||||
typedef AudioFrameBuffer< int32_t > AudioBufferSInt32;
|
||||
|
||||
#endif // hifi_AudioBuffer_h
|
||||
|
|
@ -37,11 +37,11 @@ public:
|
|||
//
|
||||
// ctor/dtor
|
||||
//
|
||||
AudioBiquad()
|
||||
: _xm1(0.)
|
||||
, _xm2(0.)
|
||||
, _ym1(0.)
|
||||
, _ym2(0.) {
|
||||
AudioBiquad() :
|
||||
_xm1(0.),
|
||||
_xm2(0.),
|
||||
_ym1(0.),
|
||||
_ym2(0.) {
|
||||
setParameters(0.,0.,0.,0.,0.);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,9 +9,12 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <SharedUtil.h>
|
||||
#include "AudioRingBuffer.h"
|
||||
#include "AudioFormat.h"
|
||||
#include "AudioBuffer.h"
|
||||
#include "AudioFilter.h"
|
||||
#include "AudioFilterBank.h"
|
||||
|
||||
|
|
|
@ -49,9 +49,9 @@ public:
|
|||
//
|
||||
// ctor/dtor
|
||||
//
|
||||
AudioFilterBank()
|
||||
: _sampleRate(0.)
|
||||
, _frameCount(0) {
|
||||
AudioFilterBank() :
|
||||
_sampleRate(0.0f),
|
||||
_frameCount(0) {
|
||||
for (int i = 0; i < _channelCount; ++i) {
|
||||
_buffer[ i ] = NULL;
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ public:
|
|||
//
|
||||
// public interface
|
||||
//
|
||||
void initialize(const float sampleRate, const int frameCount) {
|
||||
void initialize(const float sampleRate, const int frameCount = 0) {
|
||||
finalize();
|
||||
|
||||
for (int i = 0; i < _channelCount; ++i) {
|
||||
|
@ -141,6 +141,16 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void render(AudioBufferFloat32& frameBuffer) {
|
||||
|
||||
float32_t** samples = frameBuffer.getFrameData();
|
||||
for (uint16_t j = 0; j < frameBuffer.getChannelCount(); ++j) {
|
||||
for (int i = 0; i < _filterCount; ++i) {
|
||||
_filters[i][j].render( samples[j], samples[j], frameBuffer.getFrameCount() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
for (int i = 0; i < _filterCount; ++i) {
|
||||
for (int j = 0; j < _channelCount; ++j) {
|
||||
|
|
85
libraries/audio/src/AudioFormat.h
Normal file
85
libraries/audio/src/AudioFormat.h
Normal file
|
@ -0,0 +1,85 @@
|
|||
//
|
||||
// AudioFormat.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Craig Hansen-Sturm on 8/28/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_AudioFormat_h
|
||||
#define hifi_AudioFormat_h
|
||||
|
||||
#ifndef _FLOAT32_T
|
||||
#define _FLOAT32_T
|
||||
typedef float float32_t;
|
||||
#endif
|
||||
|
||||
#ifndef _FLOAT64_T
|
||||
#define _FLOAT64_T
|
||||
typedef double float64_t;
|
||||
#endif
|
||||
|
||||
//
|
||||
// Audio format structure (currently for uncompressed streams only)
|
||||
//
|
||||
|
||||
struct AudioFormat {
|
||||
|
||||
struct Flags {
|
||||
uint32_t _isFloat : 1;
|
||||
uint32_t _isSigned : 1;
|
||||
uint32_t _isInterleaved : 1;
|
||||
uint32_t _isBigEndian : 1;
|
||||
uint32_t _isPacked : 1;
|
||||
uint32_t _reserved : 27;
|
||||
} _flags;
|
||||
|
||||
uint32_t _bytesPerFrame;
|
||||
uint32_t _channelsPerFrame;
|
||||
uint32_t _bitsPerChannel;
|
||||
float64_t _sampleRate;
|
||||
|
||||
|
||||
AudioFormat() {
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
~AudioFormat() { }
|
||||
|
||||
AudioFormat& operator=(const AudioFormat& fmt) {
|
||||
memcpy(this, &fmt, sizeof(*this));
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const AudioFormat& fmt) {
|
||||
return memcmp(this, &fmt, sizeof(*this)) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const AudioFormat& fmt) {
|
||||
return memcmp(this, &fmt, sizeof(*this)) != 0;
|
||||
}
|
||||
|
||||
void setCanonicalFloat32(uint32_t channels) {
|
||||
assert(channels > 0 && channels <= 2);
|
||||
_sampleRate = SAMPLE_RATE; // todo: create audio constants header
|
||||
_bitsPerChannel = sizeof(float32_t) * 8;
|
||||
_channelsPerFrame = channels;
|
||||
_bytesPerFrame = _channelsPerFrame * _bitsPerChannel / 8;
|
||||
_flags._isFloat = true;
|
||||
_flags._isInterleaved = _channelsPerFrame > 1;
|
||||
}
|
||||
|
||||
void setCanonicalInt16(uint32_t channels) {
|
||||
assert(channels > 0 && channels <= 2);
|
||||
_sampleRate = SAMPLE_RATE; // todo: create audio constants header
|
||||
_bitsPerChannel = sizeof(int16_t) * 8;
|
||||
_channelsPerFrame = channels;
|
||||
_bytesPerFrame = _channelsPerFrame * _bitsPerChannel / 8;
|
||||
_flags._isSigned = true;
|
||||
_flags._isInterleaved = _channelsPerFrame > 1;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // hifi_AudioFormat_h
|
137
libraries/audio/src/AudioGain.h
Normal file
137
libraries/audio/src/AudioGain.h
Normal file
|
@ -0,0 +1,137 @@
|
|||
//
|
||||
// AudioGain.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Craig Hansen-Sturm on 9/1/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_AudioGain_h
|
||||
#define hifi_AudioGain_h
|
||||
|
||||
class AudioGain
|
||||
{
|
||||
float32_t _gain;
|
||||
bool _mute;
|
||||
|
||||
public:
|
||||
AudioGain() {
|
||||
initialize();
|
||||
}
|
||||
|
||||
~AudioGain() {
|
||||
finalize();
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
setParameters(1.0f,0.0f);
|
||||
}
|
||||
|
||||
void finalize() {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
initialize();
|
||||
}
|
||||
|
||||
void setParameters(const float gain, const float mute) {
|
||||
_gain = std::min(std::max(gain, 0.0f), 1.0f);
|
||||
_mute = mute != 0.0f;
|
||||
|
||||
}
|
||||
|
||||
void getParameters(float& gain, float& mute) {
|
||||
gain = _gain;
|
||||
mute = _mute ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
void render(AudioBufferFloat32& frameBuffer) {
|
||||
if (_mute) {
|
||||
frameBuffer.zeroFrames();
|
||||
return;
|
||||
}
|
||||
|
||||
float32_t** samples = frameBuffer.getFrameData();
|
||||
|
||||
bool frameAlignment16 = (frameBuffer.getFrameCount() & 0x0F) == 0;
|
||||
if (frameAlignment16) {
|
||||
|
||||
if (frameBuffer.getChannelCount() == 1) {
|
||||
|
||||
for (uint16_t i = 0; i < frameBuffer.getFrameCount(); i += 16) {
|
||||
samples[0][i + 0] *= _gain;
|
||||
samples[0][i + 1] *= _gain;
|
||||
samples[0][i + 2] *= _gain;
|
||||
samples[0][i + 3] *= _gain;
|
||||
samples[0][i + 4] *= _gain;
|
||||
samples[0][i + 5] *= _gain;
|
||||
samples[0][i + 6] *= _gain;
|
||||
samples[0][i + 7] *= _gain;
|
||||
samples[0][i + 8] *= _gain;
|
||||
samples[0][i + 9] *= _gain;
|
||||
samples[0][i + 10] *= _gain;
|
||||
samples[0][i + 11] *= _gain;
|
||||
samples[0][i + 12] *= _gain;
|
||||
samples[0][i + 13] *= _gain;
|
||||
samples[0][i + 14] *= _gain;
|
||||
samples[0][i + 15] *= _gain;
|
||||
}
|
||||
}
|
||||
else if (frameBuffer.getChannelCount() == 2) {
|
||||
|
||||
for (uint16_t i = 0; i < frameBuffer.getFrameCount(); i += 16) {
|
||||
samples[0][i + 0] *= _gain;
|
||||
samples[0][i + 1] *= _gain;
|
||||
samples[0][i + 2] *= _gain;
|
||||
samples[0][i + 3] *= _gain;
|
||||
samples[0][i + 4] *= _gain;
|
||||
samples[0][i + 5] *= _gain;
|
||||
samples[0][i + 6] *= _gain;
|
||||
samples[0][i + 7] *= _gain;
|
||||
samples[0][i + 8] *= _gain;
|
||||
samples[0][i + 9] *= _gain;
|
||||
samples[0][i + 10] *= _gain;
|
||||
samples[0][i + 11] *= _gain;
|
||||
samples[0][i + 12] *= _gain;
|
||||
samples[0][i + 13] *= _gain;
|
||||
samples[0][i + 14] *= _gain;
|
||||
samples[0][i + 15] *= _gain;
|
||||
samples[1][i + 0] *= _gain;
|
||||
samples[1][i + 1] *= _gain;
|
||||
samples[1][i + 2] *= _gain;
|
||||
samples[1][i + 3] *= _gain;
|
||||
samples[1][i + 4] *= _gain;
|
||||
samples[1][i + 5] *= _gain;
|
||||
samples[1][i + 6] *= _gain;
|
||||
samples[1][i + 7] *= _gain;
|
||||
samples[1][i + 8] *= _gain;
|
||||
samples[1][i + 9] *= _gain;
|
||||
samples[1][i + 10] *= _gain;
|
||||
samples[1][i + 11] *= _gain;
|
||||
samples[1][i + 12] *= _gain;
|
||||
samples[1][i + 13] *= _gain;
|
||||
samples[1][i + 14] *= _gain;
|
||||
samples[1][i + 15] *= _gain;
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert("unsupported channel format");
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
for (uint16_t j = 0; j < frameBuffer.getChannelCount(); ++j) {
|
||||
for (uint16_t i = 0; i < frameBuffer.getFrameCount(); i += 1) {
|
||||
samples[j][i] *= _gain;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // AudioGain_h
|
||||
|
||||
|
23
libraries/audio/src/AudioPan.cpp
Normal file
23
libraries/audio/src/AudioPan.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// AudioSourceTone.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Craig Hansen-Sturm on 8/10/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 <assert.h>
|
||||
#include <math.h>
|
||||
#include <SharedUtil.h>
|
||||
#include "AudioRingBuffer.h"
|
||||
#include "AudioFormat.h"
|
||||
#include "AudioBuffer.h"
|
||||
#include "AudioPan.h"
|
||||
|
||||
float32_t AudioPan::ONE_MINUS_EPSILON = 1.0f - EPSILON;
|
||||
float32_t AudioPan::ZERO_PLUS_EPSILON = 0.0f + EPSILON;
|
||||
float32_t AudioPan::ONE_HALF_MINUS_EPSILON = 0.5f - EPSILON;
|
||||
float32_t AudioPan::ONE_HALF_PLUS_EPSILON = 0.5f + EPSILON;
|
141
libraries/audio/src/AudioPan.h
Normal file
141
libraries/audio/src/AudioPan.h
Normal file
|
@ -0,0 +1,141 @@
|
|||
//
|
||||
// AudioPan.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Craig Hansen-Sturm on 9/1/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_AudioPan_h
|
||||
#define hifi_AudioPan_h
|
||||
|
||||
class AudioPan
|
||||
{
|
||||
float32_t _pan;
|
||||
float32_t _gainLeft;
|
||||
float32_t _gainRight;
|
||||
|
||||
static float32_t ONE_MINUS_EPSILON;
|
||||
static float32_t ZERO_PLUS_EPSILON;
|
||||
static float32_t ONE_HALF_MINUS_EPSILON;
|
||||
static float32_t ONE_HALF_PLUS_EPSILON;
|
||||
|
||||
void updateCoefficients() {
|
||||
|
||||
// implement constant power sin^2 + cos^2 = 1 panning law
|
||||
|
||||
if (_pan >= ONE_MINUS_EPSILON) { // full right
|
||||
_gainLeft = 0.0f;
|
||||
_gainRight = 1.0f;
|
||||
}
|
||||
else if (_pan <= ZERO_PLUS_EPSILON) { // full left
|
||||
_gainLeft = 1.0f;
|
||||
_gainRight = 0.0f;
|
||||
}
|
||||
else if ((_pan >= ONE_HALF_MINUS_EPSILON) && (_pan <= ONE_HALF_PLUS_EPSILON)) { // center
|
||||
_gainLeft = 1.0f / SQUARE_ROOT_OF_2;
|
||||
_gainRight = 1.0f / SQUARE_ROOT_OF_2;
|
||||
}
|
||||
else { // intermediate cases
|
||||
_gainLeft = cosf( TWO_PI * _pan );
|
||||
_gainRight = sinf( TWO_PI * _pan );
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
AudioPan() {
|
||||
initialize();
|
||||
}
|
||||
|
||||
~AudioPan() {
|
||||
finalize();
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
setParameters(0.5f);
|
||||
}
|
||||
|
||||
void finalize() {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
initialize();
|
||||
}
|
||||
|
||||
void setParameters(const float32_t pan) {
|
||||
// pan ranges between 0.0 and 1.0f inclusive. 0.5f is midpoint between full left and full right
|
||||
_pan = std::min(std::max(pan, 0.0f), 1.0f);
|
||||
updateCoefficients();
|
||||
}
|
||||
|
||||
void getParameters(float32_t& pan) {
|
||||
pan = _pan;
|
||||
}
|
||||
|
||||
void render(AudioBufferFloat32& frameBuffer) {
|
||||
|
||||
if (frameBuffer.getChannelCount() != 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
float32_t** samples = frameBuffer.getFrameData();
|
||||
|
||||
bool frameAlignment16 = (frameBuffer.getFrameCount() & 0x0F) == 0;
|
||||
if (frameAlignment16) {
|
||||
|
||||
if (frameBuffer.getChannelCount() == 2) {
|
||||
|
||||
for (uint16_t i = 0; i < frameBuffer.getFrameCount(); i += 16) {
|
||||
samples[0][i + 0] *= _gainLeft;
|
||||
samples[0][i + 1] *= _gainLeft;
|
||||
samples[0][i + 2] *= _gainLeft;
|
||||
samples[0][i + 3] *= _gainLeft;
|
||||
samples[0][i + 4] *= _gainLeft;
|
||||
samples[0][i + 5] *= _gainLeft;
|
||||
samples[0][i + 6] *= _gainLeft;
|
||||
samples[0][i + 7] *= _gainLeft;
|
||||
samples[0][i + 8] *= _gainLeft;
|
||||
samples[0][i + 9] *= _gainLeft;
|
||||
samples[0][i + 10] *= _gainLeft;
|
||||
samples[0][i + 11] *= _gainLeft;
|
||||
samples[0][i + 12] *= _gainLeft;
|
||||
samples[0][i + 13] *= _gainLeft;
|
||||
samples[0][i + 14] *= _gainLeft;
|
||||
samples[0][i + 15] *= _gainLeft;
|
||||
samples[1][i + 0] *= _gainRight;
|
||||
samples[1][i + 1] *= _gainRight;
|
||||
samples[1][i + 2] *= _gainRight;
|
||||
samples[1][i + 3] *= _gainRight;
|
||||
samples[1][i + 4] *= _gainRight;
|
||||
samples[1][i + 5] *= _gainRight;
|
||||
samples[1][i + 6] *= _gainRight;
|
||||
samples[1][i + 7] *= _gainRight;
|
||||
samples[1][i + 8] *= _gainRight;
|
||||
samples[1][i + 9] *= _gainRight;
|
||||
samples[1][i + 10] *= _gainRight;
|
||||
samples[1][i + 11] *= _gainRight;
|
||||
samples[1][i + 12] *= _gainRight;
|
||||
samples[1][i + 13] *= _gainRight;
|
||||
samples[1][i + 14] *= _gainRight;
|
||||
samples[1][i + 15] *= _gainRight;
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert("unsupported channel format");
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (uint16_t i = 0; i < frameBuffer.getFrameCount(); i += 1) {
|
||||
samples[0][i] *= _gainLeft;
|
||||
samples[1][i] *= _gainRight;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // AudioPan_h
|
||||
|
||||
|
21
libraries/audio/src/AudioSourceNoise.cpp
Normal file
21
libraries/audio/src/AudioSourceNoise.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// AudioSourceNoise.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Craig Hansen-Sturm on 8/10/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 <assert.h>
|
||||
#include <math.h>
|
||||
#include <SharedUtil.h>
|
||||
#include "AudioRingBuffer.h"
|
||||
#include "AudioFormat.h"
|
||||
#include "AudioBuffer.h"
|
||||
#include "AudioSourceNoise.h"
|
||||
|
||||
template<>
|
||||
uint32_t AudioSourcePinkNoise::_randomSeed = 1974; // a truly random number
|
103
libraries/audio/src/AudioSourceNoise.h
Normal file
103
libraries/audio/src/AudioSourceNoise.h
Normal file
|
@ -0,0 +1,103 @@
|
|||
//
|
||||
// AudioSourceNoise.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Craig Hansen-Sturm on 9/1/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
|
||||
//
|
||||
// Adapted from code by Phil Burk http://www.firstpr.com.au/dsp/pink-noise/
|
||||
//
|
||||
|
||||
#ifndef hifi_AudioSourceNoise_h
|
||||
#define hifi_AudioSourceNoise_h
|
||||
|
||||
template< const uint16_t N = 30>
|
||||
class AudioSourceNoise
|
||||
{
|
||||
static const uint16_t _randomRows = N;
|
||||
static const uint16_t _randomBits = 24;
|
||||
static const uint16_t _randomShift = (sizeof(int32_t) * 8) - _randomBits;
|
||||
|
||||
static uint32_t _randomSeed;
|
||||
|
||||
int32_t _rows[_randomRows];
|
||||
int32_t _runningSum; // used to optimize summing of generators.
|
||||
uint16_t _index; // incremented each sample.
|
||||
uint16_t _indexMask; // index wrapped by ANDing with this mask.
|
||||
float32_t _scale; // used to scale within range of -1.0 to +1.0
|
||||
|
||||
static uint32_t generateRandomNumber() {
|
||||
_randomSeed = (_randomSeed * 196314165) + 907633515;
|
||||
return _randomSeed >> _randomShift;
|
||||
}
|
||||
|
||||
public:
|
||||
AudioSourceNoise() {
|
||||
initialize();
|
||||
}
|
||||
|
||||
~AudioSourceNoise() {
|
||||
finalize();
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
memset(_rows, 0, _randomRows * sizeof(int32_t));
|
||||
|
||||
_runningSum = 0;
|
||||
_index = 0;
|
||||
_indexMask = (1 << _randomRows) - 1;
|
||||
_scale = 1.0f / ((_randomRows + 1) * (1 << (_randomBits - 1)));
|
||||
}
|
||||
|
||||
void finalize() {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
initialize();
|
||||
}
|
||||
|
||||
void setParameters(void) {
|
||||
}
|
||||
|
||||
void getParameters(void) {
|
||||
}
|
||||
|
||||
void render(AudioBufferFloat32& frameBuffer) {
|
||||
|
||||
uint32_t randomNumber;
|
||||
|
||||
float32_t** samples = frameBuffer.getFrameData();
|
||||
for (uint16_t i = 0; i < frameBuffer.getFrameCount(); ++i) {
|
||||
for (uint16_t j = 0; j < frameBuffer.getChannelCount(); ++j) {
|
||||
|
||||
_index = (_index + 1) & _indexMask; // increment and mask index.
|
||||
if (_index != 0) { // if index is zero, don't update any random values.
|
||||
|
||||
uint32_t numZeros = 0; // determine how many trailing zeros in _index
|
||||
uint32_t tmp = _index;
|
||||
while ((tmp & 1) == 0) {
|
||||
tmp >>= 1;
|
||||
numZeros++;
|
||||
}
|
||||
// replace the indexed _rows random value. subtract and add back to _runningSum instead
|
||||
// of adding all the random values together. only one value changes each time.
|
||||
_runningSum -= _rows[numZeros];
|
||||
randomNumber = generateRandomNumber();
|
||||
_runningSum += randomNumber;
|
||||
_rows[numZeros] = randomNumber;
|
||||
}
|
||||
|
||||
// add extra white noise value and scale between -1.0 and +1.0
|
||||
samples[j][i] = (_runningSum + generateRandomNumber()) * _scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef AudioSourceNoise<> AudioSourcePinkNoise;
|
||||
|
||||
#endif // AudioSourceNoise_h
|
||||
|
20
libraries/audio/src/AudioSourceTone.cpp
Normal file
20
libraries/audio/src/AudioSourceTone.cpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// AudioSourceTone.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Craig Hansen-Sturm on 8/10/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 <assert.h>
|
||||
#include <math.h>
|
||||
#include <SharedUtil.h>
|
||||
#include "AudioRingBuffer.h"
|
||||
#include "AudioFormat.h"
|
||||
#include "AudioBuffer.h"
|
||||
#include "AudioSourceTone.h"
|
||||
|
||||
uint32_t AudioSourceTone::_frameOffset = 0;
|
72
libraries/audio/src/AudioSourceTone.h
Normal file
72
libraries/audio/src/AudioSourceTone.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
//
|
||||
// AudioSourceTone.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Craig Hansen-Sturm on 9/1/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_AudioSourceTone_h
|
||||
#define hifi_AudioSourceTone_h
|
||||
|
||||
class AudioSourceTone
|
||||
{
|
||||
static uint32_t _frameOffset;
|
||||
float32_t _frequency;
|
||||
float32_t _amplitude;
|
||||
float32_t _sampleRate;
|
||||
float32_t _omega;
|
||||
|
||||
public:
|
||||
AudioSourceTone() {
|
||||
initialize();
|
||||
}
|
||||
|
||||
~AudioSourceTone() {
|
||||
finalize();
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
_frameOffset = 0;
|
||||
setParameters(SAMPLE_RATE, 220.0f, 0.9f);
|
||||
}
|
||||
|
||||
void finalize() {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
_frameOffset = 0;
|
||||
}
|
||||
|
||||
void setParameters(const float32_t sampleRate, const float32_t frequency, const float32_t amplitude) {
|
||||
_sampleRate = std::max(sampleRate, 1.0f);
|
||||
_frequency = std::max(frequency, 1.0f);
|
||||
_amplitude = std::max(amplitude, 1.0f);
|
||||
_omega = _frequency / _sampleRate * TWO_PI;
|
||||
}
|
||||
|
||||
void getParameters(float32_t& sampleRate, float32_t& frequency, float32_t& amplitude) {
|
||||
sampleRate = _sampleRate;
|
||||
frequency = _frequency;
|
||||
amplitude = _amplitude;
|
||||
}
|
||||
|
||||
void render(AudioBufferFloat32& frameBuffer) {
|
||||
|
||||
// note: this is a placeholder implementation. final version will not include any transcendental ops in our render loop
|
||||
|
||||
float32_t** samples = frameBuffer.getFrameData();
|
||||
for (uint16_t i = 0; i < frameBuffer.getFrameCount(); ++i) {
|
||||
for (uint16_t j = 0; j < frameBuffer.getChannelCount(); ++j) {
|
||||
samples[j][i] = sinf((i + _frameOffset) * _omega);
|
||||
}
|
||||
}
|
||||
_frameOffset += frameBuffer.getFrameCount();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -16,6 +16,8 @@
|
|||
#include <AABox.h>
|
||||
|
||||
#include "InboundAudioStream.h"
|
||||
#include "AudioFormat.h"
|
||||
#include "AudioBuffer.h"
|
||||
#include "AudioFilter.h"
|
||||
#include "AudioFilterBank.h"
|
||||
|
||||
|
|
|
@ -35,7 +35,8 @@ using namespace std;
|
|||
|
||||
AvatarData::AvatarData() :
|
||||
_sessionUUID(),
|
||||
_handPosition(0,0,0),
|
||||
_position(0.0f),
|
||||
_handPosition(0.0f),
|
||||
_referential(NULL),
|
||||
_bodyYaw(-90.f),
|
||||
_bodyPitch(0.0f),
|
||||
|
@ -326,7 +327,7 @@ int AvatarData::parseDataAtOffset(const QByteArray& packet, int offset) {
|
|||
}
|
||||
return maxAvailableSize;
|
||||
}
|
||||
_position = position;
|
||||
setPosition(position);
|
||||
|
||||
// rotation (NOTE: This needs to become a quaternion to save two bytes)
|
||||
float yaw, pitch, roll;
|
||||
|
|
|
@ -25,8 +25,7 @@ AddEntityOperator::AddEntityOperator(EntityTree* tree,
|
|||
{
|
||||
// caller must have verified existence of newEntity
|
||||
assert(_newEntity);
|
||||
|
||||
_newEntityBox = _newEntity->getAACube().clamp(0.0f, 1.0f);
|
||||
_newEntityBox = _newEntity->getMaximumAACube().clamp(0.0f, 1.0f);
|
||||
}
|
||||
|
||||
bool AddEntityOperator::preRecursion(OctreeElement* element) {
|
||||
|
|
|
@ -20,13 +20,15 @@
|
|||
|
||||
|
||||
EntityItem* BoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return new BoxEntityItem(entityID, properties);
|
||||
EntityItem* result = new BoxEntityItem(entityID, properties);
|
||||
return result;
|
||||
}
|
||||
|
||||
BoxEntityItem::BoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
|
||||
EntityItem(entityItemID, properties)
|
||||
{
|
||||
EntityItem(entityItemID)
|
||||
{
|
||||
_type = EntityTypes::Box;
|
||||
_created = properties.getCreated();
|
||||
setProperties(properties, true);
|
||||
}
|
||||
|
||||
|
@ -44,18 +46,9 @@ EntityItemProperties BoxEntityItem::getProperties() const {
|
|||
|
||||
bool BoxEntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) {
|
||||
bool somethingChanged = false;
|
||||
|
||||
somethingChanged = EntityItem::setProperties(properties, forceCopy); // set the properties in our base class
|
||||
|
||||
if (properties._colorChanged || forceCopy) {
|
||||
setColor(properties._color);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (properties._glowLevelChanged || forceCopy) {
|
||||
setGlowLevel(properties._glowLevel);
|
||||
somethingChanged = true;
|
||||
}
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
|
||||
|
||||
if (somethingChanged) {
|
||||
bool wantDebug = false;
|
||||
|
|
|
@ -27,14 +27,21 @@ const float EntityItem::IMMORTAL = -1.0f; /// special lifetime which means the e
|
|||
const float EntityItem::DEFAULT_GLOW_LEVEL = 0.0f;
|
||||
const float EntityItem::DEFAULT_MASS = 1.0f;
|
||||
const float EntityItem::DEFAULT_LIFETIME = EntityItem::IMMORTAL;
|
||||
const float EntityItem::DEFAULT_DAMPING = 0.99f;
|
||||
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 / 10000.0f) / (float)TREE_SCALE; // really small
|
||||
const float EntityItem::EPSILON_VELOCITY_LENGTH = (1.0f / 1000.0f) / (float)TREE_SCALE; // really small: 1mm/second
|
||||
const glm::vec3 EntityItem::DEFAULT_VELOCITY = EntityItem::NO_VELOCITY;
|
||||
const glm::vec3 EntityItem::NO_GRAVITY = glm::vec3(0, 0, 0);
|
||||
const glm::vec3 EntityItem::REGULAR_GRAVITY = glm::vec3(0, (-9.8f / TREE_SCALE), 0);
|
||||
const glm::vec3 EntityItem::DEFAULT_GRAVITY = EntityItem::NO_GRAVITY;
|
||||
const QString EntityItem::DEFAULT_SCRIPT = QString("");
|
||||
const glm::quat EntityItem::DEFAULT_ROTATION;
|
||||
const glm::vec3 EntityItem::DEFAULT_DIMENSIONS = glm::vec3(0.1f, 0.1f, 0.1f);
|
||||
const glm::vec3 EntityItem::DEFAULT_REGISTRATION_POINT = glm::vec3(0.5f, 0.5f, 0.5f); // center
|
||||
const glm::vec3 EntityItem::NO_ANGULAR_VELOCITY = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
const glm::vec3 EntityItem::DEFAULT_ANGULAR_VELOCITY = NO_ANGULAR_VELOCITY;
|
||||
const float EntityItem::DEFAULT_ANGULAR_DAMPING = 0.5f;
|
||||
const bool EntityItem::DEFAULT_VISIBLE = true;
|
||||
|
||||
void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) {
|
||||
_id = entityItemID.id;
|
||||
|
@ -50,8 +57,8 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) {
|
|||
_created = 0; // TODO: when do we actually want to make this "now"
|
||||
|
||||
_position = glm::vec3(0,0,0);
|
||||
_radius = 0;
|
||||
_rotation = ENTITY_DEFAULT_ROTATION;
|
||||
_rotation = DEFAULT_ROTATION;
|
||||
_dimensions = DEFAULT_DIMENSIONS;
|
||||
|
||||
_glowLevel = DEFAULT_GLOW_LEVEL;
|
||||
_mass = DEFAULT_MASS;
|
||||
|
@ -59,6 +66,20 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) {
|
|||
_gravity = DEFAULT_GRAVITY;
|
||||
_damping = DEFAULT_DAMPING;
|
||||
_lifetime = DEFAULT_LIFETIME;
|
||||
_registrationPoint = DEFAULT_REGISTRATION_POINT;
|
||||
_angularVelocity = DEFAULT_ANGULAR_VELOCITY;
|
||||
_angularDamping = DEFAULT_ANGULAR_DAMPING;
|
||||
_visible = DEFAULT_VISIBLE;
|
||||
}
|
||||
|
||||
EntityItem::EntityItem(const EntityItemID& entityItemID) {
|
||||
_type = EntityTypes::Unknown;
|
||||
_lastEdited = 0;
|
||||
_lastEditedFromRemote = 0;
|
||||
_lastEditedFromRemoteInRemoteTime = 0;
|
||||
_lastUpdated = 0;
|
||||
_created = 0;
|
||||
initFromEntityItemID(entityItemID);
|
||||
}
|
||||
|
||||
EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) {
|
||||
|
@ -76,7 +97,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
|
|||
EntityPropertyFlags requestedProperties;
|
||||
|
||||
requestedProperties += PROP_POSITION;
|
||||
requestedProperties += PROP_RADIUS;
|
||||
requestedProperties += PROP_DIMENSIONS; // NOTE: PROP_RADIUS obsolete
|
||||
requestedProperties += PROP_ROTATION;
|
||||
requestedProperties += PROP_MASS;
|
||||
requestedProperties += PROP_VELOCITY;
|
||||
|
@ -84,6 +105,10 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
|
|||
requestedProperties += PROP_DAMPING;
|
||||
requestedProperties += PROP_LIFETIME;
|
||||
requestedProperties += PROP_SCRIPT;
|
||||
requestedProperties += PROP_REGISTRATION_POINT;
|
||||
requestedProperties += PROP_ANGULAR_VELOCITY;
|
||||
requestedProperties += PROP_ANGULAR_DAMPING;
|
||||
requestedProperties += PROP_VISIBLE;
|
||||
|
||||
return requestedProperties;
|
||||
}
|
||||
|
@ -178,10 +203,14 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
|||
// These items would go here once supported....
|
||||
// PROP_PAGED_PROPERTY,
|
||||
// PROP_CUSTOM_PROPERTIES_INCLUDED,
|
||||
// PROP_VISIBLE,
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_POSITION, appendPosition, getPosition());
|
||||
APPEND_ENTITY_PROPERTY(PROP_RADIUS, appendValue, getRadius());
|
||||
APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, appendValue, getDimensions()); // NOTE: PROP_RADIUS obsolete
|
||||
|
||||
if (wantDebug) {
|
||||
qDebug() << " APPEND_ENTITY_PROPERTY() PROP_DIMENSIONS:" << getDimensions();
|
||||
}
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_ROTATION, appendValue, getRotation());
|
||||
APPEND_ENTITY_PROPERTY(PROP_MASS, appendValue, getMass());
|
||||
APPEND_ENTITY_PROPERTY(PROP_VELOCITY, appendValue, getVelocity());
|
||||
|
@ -189,6 +218,10 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
|||
APPEND_ENTITY_PROPERTY(PROP_DAMPING, appendValue, getDamping());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LIFETIME, appendValue, getLifetime());
|
||||
APPEND_ENTITY_PROPERTY(PROP_SCRIPT, appendValue, getScript());
|
||||
APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, appendValue, getRegistrationPoint());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, appendValue, getAngularVelocity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, appendValue, getAngularDamping());
|
||||
APPEND_ENTITY_PROPERTY(PROP_VISIBLE, appendValue, getVisible());
|
||||
|
||||
appendSubclassData(packetData, params, entityTreeElementExtraEncodeData,
|
||||
requestedProperties,
|
||||
|
@ -408,9 +441,36 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
EntityPropertyFlags propertyFlags = encodedPropertyFlags;
|
||||
dataAt += propertyFlags.getEncodedLength();
|
||||
bytesRead += propertyFlags.getEncodedLength();
|
||||
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, _position);
|
||||
READ_ENTITY_PROPERTY(PROP_RADIUS, float, _radius);
|
||||
|
||||
// Old bitstreams had PROP_RADIUS, new bitstreams have PROP_DIMENSIONS
|
||||
if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_DIMENSIONS) {
|
||||
if (propertyFlags.getHasProperty(PROP_RADIUS)) {
|
||||
float fromBuffer;
|
||||
memcpy(&fromBuffer, dataAt, sizeof(fromBuffer));
|
||||
dataAt += sizeof(fromBuffer);
|
||||
bytesRead += sizeof(fromBuffer);
|
||||
if (overwriteLocalData) {
|
||||
setRadius(fromBuffer);
|
||||
}
|
||||
|
||||
if (wantDebug) {
|
||||
qDebug() << " readEntityDataFromBuffer() OLD FORMAT... found PROP_RADIUS";
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, _dimensions);
|
||||
if (wantDebug) {
|
||||
qDebug() << " readEntityDataFromBuffer() NEW FORMAT... look for PROP_DIMENSIONS";
|
||||
}
|
||||
}
|
||||
|
||||
if (wantDebug) {
|
||||
qDebug() << " readEntityDataFromBuffer() _dimensions:" << getDimensionsInMeters() << " in meters";
|
||||
}
|
||||
|
||||
READ_ENTITY_PROPERTY_QUAT(PROP_ROTATION, _rotation);
|
||||
READ_ENTITY_PROPERTY(PROP_MASS, float, _mass);
|
||||
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, _velocity);
|
||||
|
@ -418,6 +478,15 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
READ_ENTITY_PROPERTY(PROP_DAMPING, float, _damping);
|
||||
READ_ENTITY_PROPERTY(PROP_LIFETIME, float, _lifetime);
|
||||
READ_ENTITY_PROPERTY_STRING(PROP_SCRIPT,setScript);
|
||||
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, _registrationPoint);
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, _angularVelocity);
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, _angularDamping);
|
||||
READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, _visible);
|
||||
|
||||
if (wantDebug) {
|
||||
qDebug() << " readEntityDataFromBuffer() _registrationPoint:" << _registrationPoint;
|
||||
qDebug() << " readEntityDataFromBuffer() _visible:" << _visible;
|
||||
}
|
||||
|
||||
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData);
|
||||
|
||||
|
@ -429,7 +498,7 @@ void EntityItem::debugDump() const {
|
|||
qDebug() << "EntityItem id:" << getEntityItemID();
|
||||
qDebug(" edited ago:%f", getEditedAgo());
|
||||
qDebug(" position:%f,%f,%f", _position.x, _position.y, _position.z);
|
||||
qDebug(" radius:%f", getRadius());
|
||||
qDebug() << " dimensions:" << _dimensions;
|
||||
}
|
||||
|
||||
// adjust any internal timestamps to fix clock skew for this server
|
||||
|
@ -453,9 +522,14 @@ void EntityItem::adjustEditPacketForClockSkew(unsigned char* editPacketBuffer, s
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: we probably want to change this to make "down" be the direction of the entity's gravity vector
|
||||
// for now, this is always true DOWN even if entity has non-down gravity.
|
||||
// TODO: the old code had "&& _velocity.y >= -EPSILON && _velocity.y <= EPSILON" --- what was I thinking?
|
||||
bool EntityItem::isRestingOnSurface() const {
|
||||
return _position.y <= _radius
|
||||
&& _velocity.y >= -EPSILON && _velocity.y <= EPSILON
|
||||
glm::vec3 downwardVelocity = glm::vec3(0.0f, _velocity.y, 0.0f);
|
||||
|
||||
return _position.y <= getDistanceToBottomOfEntity()
|
||||
&& (glm::length(downwardVelocity) <= EPSILON_VELOCITY_LENGTH)
|
||||
&& _gravity.y < 0.0f;
|
||||
}
|
||||
|
||||
|
@ -466,9 +540,39 @@ void EntityItem::update(const quint64& updateTime) {
|
|||
|
||||
if (wantDebug) {
|
||||
qDebug() << "********** EntityItem::update()";
|
||||
qDebug() << " entity ID=" << getEntityItemID();
|
||||
qDebug() << " updateTime=" << updateTime;
|
||||
qDebug() << " _lastUpdated=" << _lastUpdated;
|
||||
qDebug() << " timeElapsed=" << timeElapsed;
|
||||
qDebug() << " hasVelocity=" << hasVelocity();
|
||||
qDebug() << " hasGravity=" << hasGravity();
|
||||
qDebug() << " isRestingOnSurface=" << isRestingOnSurface();
|
||||
qDebug() << " hasAngularVelocity=" << hasAngularVelocity();
|
||||
qDebug() << " getAngularVelocity=" << getAngularVelocity();
|
||||
qDebug() << " isMortal=" << isMortal();
|
||||
qDebug() << " getAge()=" << getAge();
|
||||
qDebug() << " getLifetime()=" << getLifetime();
|
||||
|
||||
|
||||
if (hasVelocity() || (hasGravity() && !isRestingOnSurface())) {
|
||||
qDebug() << " MOVING...=";
|
||||
qDebug() << " hasVelocity=" << hasVelocity();
|
||||
qDebug() << " hasGravity=" << hasGravity();
|
||||
qDebug() << " isRestingOnSurface=" << isRestingOnSurface();
|
||||
qDebug() << " hasAngularVelocity=" << hasAngularVelocity();
|
||||
qDebug() << " getAngularVelocity=" << getAngularVelocity();
|
||||
}
|
||||
if (hasAngularVelocity()) {
|
||||
qDebug() << " CHANGING...=";
|
||||
qDebug() << " hasAngularVelocity=" << hasAngularVelocity();
|
||||
qDebug() << " getAngularVelocity=" << getAngularVelocity();
|
||||
}
|
||||
if (isMortal()) {
|
||||
qDebug() << " MORTAL...=";
|
||||
qDebug() << " isMortal=" << isMortal();
|
||||
qDebug() << " getAge()=" << getAge();
|
||||
qDebug() << " getLifetime()=" << getLifetime();
|
||||
}
|
||||
}
|
||||
|
||||
_lastUpdated = updateTime;
|
||||
|
@ -477,22 +581,54 @@ void EntityItem::update(const quint64& updateTime) {
|
|||
qDebug() << "********** EntityItem::update() .... SETTING _lastUpdated=" << _lastUpdated;
|
||||
}
|
||||
|
||||
if (hasAngularVelocity()) {
|
||||
glm::quat rotation = getRotation();
|
||||
glm::vec3 angularVelocity = glm::radians(getAngularVelocity());
|
||||
float angularSpeed = glm::length(angularVelocity);
|
||||
|
||||
if (angularSpeed < EPSILON_VELOCITY_LENGTH) {
|
||||
setAngularVelocity(NO_ANGULAR_VELOCITY);
|
||||
} else {
|
||||
float angle = timeElapsed * angularSpeed;
|
||||
glm::quat dQ = glm::angleAxis(angle, glm::normalize(angularVelocity));
|
||||
rotation = dQ * rotation;
|
||||
setRotation(rotation);
|
||||
|
||||
// handle damping for angular velocity
|
||||
if (getAngularDamping() > 0.0f) {
|
||||
glm::vec3 dampingResistance = getAngularVelocity() * getAngularDamping();
|
||||
glm::vec3 newAngularVelocity = getAngularVelocity() - (dampingResistance * timeElapsed);
|
||||
setAngularVelocity(newAngularVelocity);
|
||||
if (wantDebug) {
|
||||
qDebug() << " getDamping():" << getDamping();
|
||||
qDebug() << " dampingResistance:" << dampingResistance;
|
||||
qDebug() << " newAngularVelocity:" << newAngularVelocity;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasVelocity() || hasGravity()) {
|
||||
glm::vec3 position = getPosition();
|
||||
glm::vec3 velocity = getVelocity();
|
||||
glm::vec3 newPosition = position + (velocity * timeElapsed);
|
||||
|
||||
if (wantDebug) {
|
||||
qDebug() << "EntityItem::update()....";
|
||||
qDebug() << " timeElapsed:" << timeElapsed;
|
||||
qDebug() << " old AACube:" << getAACube();
|
||||
qDebug() << " old AACube:" << getMaximumAACube();
|
||||
qDebug() << " old position:" << position;
|
||||
qDebug() << " old velocity:" << velocity;
|
||||
qDebug() << " old getAABox:" << getAABox();
|
||||
qDebug() << " getDistanceToBottomOfEntity():" << getDistanceToBottomOfEntity() * (float)TREE_SCALE << " in meters";
|
||||
qDebug() << " newPosition:" << newPosition;
|
||||
qDebug() << " glm::distance(newPosition, position):" << glm::distance(newPosition, position);
|
||||
}
|
||||
|
||||
position += velocity * timeElapsed;
|
||||
position = newPosition;
|
||||
|
||||
// handle bounces off the ground... We bounce at the height of our radius...
|
||||
if (position.y <= _radius) {
|
||||
// handle bounces off the ground... We bounce at the distance to the bottom of our entity
|
||||
if (position.y <= getDistanceToBottomOfEntity()) {
|
||||
velocity = velocity * glm::vec3(1,-1,1);
|
||||
|
||||
// if we've slowed considerably, then just stop moving
|
||||
|
@ -500,7 +636,7 @@ void EntityItem::update(const quint64& updateTime) {
|
|||
velocity = NO_VELOCITY;
|
||||
}
|
||||
|
||||
position.y = _radius;
|
||||
position.y = getDistanceToBottomOfEntity();
|
||||
}
|
||||
|
||||
// handle gravity....
|
||||
|
@ -512,10 +648,10 @@ void EntityItem::update(const quint64& updateTime) {
|
|||
// "ground" plane of the domain, but for now it
|
||||
if (hasGravity() && isRestingOnSurface()) {
|
||||
velocity.y = 0.0f;
|
||||
position.y = _radius;
|
||||
position.y = getDistanceToBottomOfEntity();
|
||||
}
|
||||
|
||||
// handle damping
|
||||
// handle damping for velocity
|
||||
glm::vec3 dampingResistance = velocity * getDamping();
|
||||
if (wantDebug) {
|
||||
qDebug() << " getDamping():" << getDamping();
|
||||
|
@ -526,21 +662,23 @@ void EntityItem::update(const quint64& updateTime) {
|
|||
|
||||
if (wantDebug) {
|
||||
qDebug() << " velocity AFTER dampingResistance:" << velocity;
|
||||
qDebug() << " glm::length(velocity):" << glm::length(velocity);
|
||||
qDebug() << " EPSILON_VELOCITY_LENGTH:" << EPSILON_VELOCITY_LENGTH;
|
||||
}
|
||||
|
||||
// round velocity to zero if it's close enough...
|
||||
if (glm::length(velocity) <= EPSILON_VELOCITY_LENGTH) {
|
||||
velocity = NO_VELOCITY;
|
||||
}
|
||||
|
||||
setPosition(position);
|
||||
setVelocity(velocity);
|
||||
|
||||
if (wantDebug) {
|
||||
qDebug() << " new position:" << position;
|
||||
qDebug() << " new velocity:" << velocity;
|
||||
}
|
||||
setPosition(position);
|
||||
setVelocity(velocity);
|
||||
if (wantDebug) {
|
||||
qDebug() << " new AACube:" << getAACube();
|
||||
qDebug() << " new AACube:" << getMaximumAACube();
|
||||
qDebug() << " old getAABox:" << getAABox();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -549,6 +687,9 @@ EntityItem::SimulationState EntityItem::getSimulationState() const {
|
|||
if (hasVelocity() || (hasGravity() && !isRestingOnSurface())) {
|
||||
return EntityItem::Moving;
|
||||
}
|
||||
if (hasAngularVelocity()) {
|
||||
return EntityItem::Changing;
|
||||
}
|
||||
if (isMortal()) {
|
||||
return EntityItem::Mortal;
|
||||
}
|
||||
|
@ -566,33 +707,26 @@ void EntityItem::copyChangedProperties(const EntityItem& other) {
|
|||
|
||||
EntityItemProperties EntityItem::getProperties() const {
|
||||
EntityItemProperties properties;
|
||||
|
||||
properties._id = getID();
|
||||
properties._idSet = true;
|
||||
properties._created = _created;
|
||||
|
||||
properties._type = getType();
|
||||
|
||||
properties._position = getPosition() * (float) TREE_SCALE;
|
||||
properties._radius = getRadius() * (float) TREE_SCALE;
|
||||
properties._rotation = getRotation();
|
||||
|
||||
properties._mass = getMass();
|
||||
properties._velocity = getVelocity() * (float) TREE_SCALE;
|
||||
properties._gravity = getGravity() * (float) TREE_SCALE;
|
||||
properties._damping = getDamping();
|
||||
properties._lifetime = getLifetime();
|
||||
properties._script = getScript();
|
||||
|
||||
properties._positionChanged = false;
|
||||
properties._radiusChanged = false;
|
||||
properties._rotationChanged = false;
|
||||
properties._massChanged = false;
|
||||
properties._velocityChanged = false;
|
||||
properties._gravityChanged = false;
|
||||
properties._dampingChanged = false;
|
||||
properties._lifetimeChanged = false;
|
||||
properties._scriptChanged = false;
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(position, getPositionInMeters);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(dimensions, getDimensionsInMeters); // NOTE: radius is obsolete
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(rotation, getRotation);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(mass, getMass);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(velocity, getVelocityInMeters);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(gravity, getGravityInMeters);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(damping, getDamping);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(lifetime, getLifetime);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(script, getScript);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(registrationPoint, getRegistrationPoint);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(angularVelocity, getAngularVelocity);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(angularDamping, getAngularDamping);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(glowLevel, getGlowLevel);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(visible, getVisible);
|
||||
|
||||
properties._defaultSettings = false;
|
||||
|
||||
|
@ -610,55 +744,21 @@ bool EntityItem::setProperties(const EntityItemProperties& properties, bool forc
|
|||
_created = properties.getCreated();
|
||||
}
|
||||
}
|
||||
|
||||
if (properties._positionChanged || forceCopy) {
|
||||
// clamp positions to the domain to prevent someone from moving an entity out of the domain
|
||||
setPosition(glm::clamp(properties._position / (float) TREE_SCALE, 0.0f, 1.0f));
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (properties._radiusChanged || forceCopy) {
|
||||
setRadius(properties._radius / (float) TREE_SCALE);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (properties._rotationChanged || forceCopy) {
|
||||
setRotation(properties._rotation);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (properties._massChanged || forceCopy) {
|
||||
setMass(properties._mass);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (properties._velocityChanged || forceCopy) {
|
||||
setVelocity(properties._velocity / (float) TREE_SCALE);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (properties._massChanged || forceCopy) {
|
||||
setMass(properties._mass);
|
||||
somethingChanged = true;
|
||||
}
|
||||
if (properties._gravityChanged || forceCopy) {
|
||||
setGravity(properties._gravity / (float) TREE_SCALE);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (properties._dampingChanged || forceCopy) {
|
||||
setDamping(properties._damping);
|
||||
somethingChanged = true;
|
||||
}
|
||||
if (properties._lifetimeChanged || forceCopy) {
|
||||
setLifetime(properties._lifetime);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (properties._scriptChanged || forceCopy) {
|
||||
setScript(properties._script);
|
||||
somethingChanged = true;
|
||||
}
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, setPositionInMeters);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(dimensions, setDimensionsInMeters); // NOTE: radius is obsolete
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, setRotation);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(mass, setMass);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocity, setVelocityInMeters);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(gravity, setGravityInMeters);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(damping, setDamping);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, setLifetime);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(registrationPoint, setRegistrationPoint);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularVelocity, setAngularVelocity);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularDamping, setAngularDamping);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(glowLevel, setGlowLevel);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(visible, setVisible);
|
||||
|
||||
if (somethingChanged) {
|
||||
somethingChangedNotification(); // notify derived classes that something has changed
|
||||
|
@ -675,3 +775,127 @@ bool EntityItem::setProperties(const EntityItemProperties& properties, bool forc
|
|||
return somethingChanged;
|
||||
}
|
||||
|
||||
|
||||
// TODO: is this really correct? how do we use size, does it need to handle rotation?
|
||||
float EntityItem::getSize() const {
|
||||
return glm::length(_dimensions);
|
||||
}
|
||||
|
||||
float EntityItem::getDistanceToBottomOfEntity() const {
|
||||
glm::vec3 minimumPoint = getAABox().getMinimumPoint();
|
||||
return getPosition().y - minimumPoint.y;
|
||||
}
|
||||
|
||||
// TODO: doesn't this need to handle rotation?
|
||||
glm::vec3 EntityItem::getCenter() const {
|
||||
return _position + (_dimensions * (glm::vec3(0.5f,0.5f,0.5f) - _registrationPoint));
|
||||
}
|
||||
|
||||
/// The maximum bounding cube for the entity, independent of it's rotation.
|
||||
/// This accounts for the registration point (upon which rotation occurs around).
|
||||
///
|
||||
AACube EntityItem::getMaximumAACube() const {
|
||||
// * we know that the position is the center of rotation
|
||||
glm::vec3 centerOfRotation = _position; // also where _registration point is
|
||||
|
||||
// * we know that the registration point is the center of rotation
|
||||
// * we can calculate the length of the furthest extent from the registration point
|
||||
// as the dimensions * max (registrationPoint, (1.0,1.0,1.0) - registrationPoint)
|
||||
glm::vec3 registrationPoint = (_dimensions * _registrationPoint);
|
||||
glm::vec3 registrationRemainder = (_dimensions * (glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint));
|
||||
glm::vec3 furthestExtentFromRegistration = glm::max(registrationPoint, registrationRemainder);
|
||||
|
||||
// * we know that if you rotate in any direction you would create a sphere
|
||||
// that has a radius of the length of furthest extent from registration point
|
||||
float radius = glm::length(furthestExtentFromRegistration);
|
||||
|
||||
// * we know that the minimum bounding cube of this maximum possible sphere is
|
||||
// (center - radius) to (center + radius)
|
||||
glm::vec3 minimumCorner = centerOfRotation - glm::vec3(radius, radius, radius);
|
||||
|
||||
AACube boundingCube(minimumCorner, radius * 2.0f);
|
||||
return boundingCube;
|
||||
}
|
||||
|
||||
/// The minimum bounding cube for the entity accounting for it's rotation.
|
||||
/// This accounts for the registration point (upon which rotation occurs around).
|
||||
///
|
||||
AACube EntityItem::getMinimumAACube() const {
|
||||
// _position represents the position of the registration point.
|
||||
glm::vec3 registrationRemainder = glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint;
|
||||
|
||||
glm::vec3 unrotatedMinRelativeToEntity = glm::vec3(0.0f, 0.0f, 0.0f) - (_dimensions * _registrationPoint);
|
||||
glm::vec3 unrotatedMaxRelativeToEntity = _dimensions * registrationRemainder;
|
||||
Extents unrotatedExtentsRelativeToRegistrationPoint = { unrotatedMinRelativeToEntity, unrotatedMaxRelativeToEntity };
|
||||
Extents rotatedExtentsRelativeToRegistrationPoint = unrotatedExtentsRelativeToRegistrationPoint.getRotated(getRotation());
|
||||
|
||||
// shift the extents to be relative to the position/registration point
|
||||
rotatedExtentsRelativeToRegistrationPoint.shiftBy(_position);
|
||||
|
||||
// the cube that best encompasses extents is...
|
||||
AABox box(rotatedExtentsRelativeToRegistrationPoint);
|
||||
glm::vec3 centerOfBox = box.calcCenter();
|
||||
float longestSide = box.getLargestDimension();
|
||||
float halfLongestSide = longestSide / 2.0f;
|
||||
glm::vec3 cornerOfCube = centerOfBox - glm::vec3(halfLongestSide, halfLongestSide, halfLongestSide);
|
||||
|
||||
|
||||
// old implementation... not correct!!!
|
||||
return AACube(cornerOfCube, longestSide);
|
||||
}
|
||||
|
||||
AABox EntityItem::getAABox() const {
|
||||
|
||||
// _position represents the position of the registration point.
|
||||
glm::vec3 registrationRemainder = glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint;
|
||||
|
||||
glm::vec3 unrotatedMinRelativeToEntity = glm::vec3(0.0f, 0.0f, 0.0f) - (_dimensions * _registrationPoint);
|
||||
glm::vec3 unrotatedMaxRelativeToEntity = _dimensions * registrationRemainder;
|
||||
Extents unrotatedExtentsRelativeToRegistrationPoint = { unrotatedMinRelativeToEntity, unrotatedMaxRelativeToEntity };
|
||||
Extents rotatedExtentsRelativeToRegistrationPoint = unrotatedExtentsRelativeToRegistrationPoint.getRotated(getRotation());
|
||||
|
||||
// shift the extents to be relative to the position/registration point
|
||||
rotatedExtentsRelativeToRegistrationPoint.shiftBy(_position);
|
||||
|
||||
return AABox(rotatedExtentsRelativeToRegistrationPoint);
|
||||
}
|
||||
|
||||
|
||||
// NOTE: This should only be used in cases of old bitstreams which only contain radius data
|
||||
// 0,0,0 --> maxDimension,maxDimension,maxDimension
|
||||
// ... has a corner to corner distance of glm::length(maxDimension,maxDimension,maxDimension)
|
||||
// ... radius = cornerToCornerLength / 2.0f
|
||||
// ... radius * 2.0f = cornerToCornerLength
|
||||
// ... cornerToCornerLength = sqrt(3 x maxDimension ^ 2)
|
||||
// ... cornerToCornerLength = sqrt(3 x maxDimension ^ 2)
|
||||
// ... radius * 2.0f = sqrt(3 x maxDimension ^ 2)
|
||||
// ... (radius * 2.0f) ^2 = 3 x maxDimension ^ 2
|
||||
// ... ((radius * 2.0f) ^2) / 3 = maxDimension ^ 2
|
||||
// ... sqrt(((radius * 2.0f) ^2) / 3) = maxDimension
|
||||
// ... sqrt((diameter ^2) / 3) = maxDimension
|
||||
//
|
||||
void EntityItem::setRadius(float value) {
|
||||
float diameter = value * 2.0f;
|
||||
float maxDimension = sqrt((diameter * diameter) / 3.0f);
|
||||
_dimensions = glm::vec3(maxDimension, maxDimension, maxDimension);
|
||||
|
||||
bool wantDebug = false;
|
||||
if (wantDebug) {
|
||||
qDebug() << "EntityItem::setRadius()...";
|
||||
qDebug() << " radius:" << value;
|
||||
qDebug() << " diameter:" << diameter;
|
||||
qDebug() << " maxDimension:" << maxDimension;
|
||||
qDebug() << " _dimensions:" << _dimensions;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: get rid of all users of this function...
|
||||
// ... radius = cornerToCornerLength / 2.0f
|
||||
// ... cornerToCornerLength = sqrt(3 x maxDimension ^ 2)
|
||||
// ... radius = sqrt(3 x maxDimension ^ 2) / 2.0f;
|
||||
float EntityItem::getRadius() const {
|
||||
float length = glm::length(_dimensions);
|
||||
float radius = length / 2.0f;
|
||||
return radius;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "EntityItemProperties.h"
|
||||
#include "EntityTypes.h"
|
||||
|
||||
class EntityTreeElement;
|
||||
class EntityTreeElementExtraEncodeData;
|
||||
|
||||
#define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0;
|
||||
|
@ -39,6 +40,7 @@ class EntityItem {
|
|||
public:
|
||||
DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly
|
||||
|
||||
EntityItem(const EntityItemID& entityItemID);
|
||||
EntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
|
||||
virtual ~EntityItem() { }
|
||||
|
||||
|
@ -119,11 +121,27 @@ public:
|
|||
// attributes applicable to all entity types
|
||||
EntityTypes::EntityType getType() const { return _type; }
|
||||
const glm::vec3& getPosition() const { return _position; } /// get position in domain scale units (0.0 - 1.0)
|
||||
glm::vec3 getPositionInMeters() const { return _position * (float) TREE_SCALE; } /// get position in meters
|
||||
void setPosition(const glm::vec3& value) { _position = value; } /// set position in domain scale units (0.0 - 1.0)
|
||||
void setPositionInMeters(const glm::vec3& value) /// set position in meter units (0.0 - TREE_SCALE)
|
||||
{ setPosition(glm::clamp(value / (float) TREE_SCALE, 0.0f, 1.0f)); }
|
||||
|
||||
float getRadius() const { return _radius; } /// get radius in domain scale units (0.0 - 1.0)
|
||||
void setRadius(float value) { _radius = value; } /// set radius in domain scale units (0.0 - 1.0)
|
||||
glm::vec3 getCenter() const; /// calculates center of the entity in domain scale units (0.0 - 1.0)
|
||||
glm::vec3 getCenterInMeters() const { return getCenter() * (float) TREE_SCALE; }
|
||||
|
||||
static const glm::vec3 DEFAULT_DIMENSIONS;
|
||||
const glm::vec3& getDimensions() const { return _dimensions; } /// get dimensions in domain scale units (0.0 - 1.0)
|
||||
glm::vec3 getDimensionsInMeters() const { return _dimensions * (float) TREE_SCALE; } /// get dimensions in meters
|
||||
float getDistanceToBottomOfEntity() const; /// get the distance from the position of the entity to its "bottom" in y axis
|
||||
float getLargestDimension() const { return glm::length(_dimensions); } /// get the largest possible dimension
|
||||
|
||||
/// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately
|
||||
void setDimensions(const glm::vec3& value) { _dimensions = value; }
|
||||
|
||||
/// set dimensions in meter units (0.0 - TREE_SCALE) this will also reset radius appropriately
|
||||
void setDimensionsInMeters(const glm::vec3& value) { setDimensions(value / (float) TREE_SCALE); }
|
||||
|
||||
static const glm::quat DEFAULT_ROTATION;
|
||||
const glm::quat& getRotation() const { return _rotation; }
|
||||
void setRotation(const glm::quat& rotation) { _rotation = rotation; }
|
||||
|
||||
|
@ -138,15 +156,19 @@ public:
|
|||
static const glm::vec3 DEFAULT_VELOCITY;
|
||||
static const glm::vec3 NO_VELOCITY;
|
||||
static const float EPSILON_VELOCITY_LENGTH;
|
||||
const glm::vec3& getVelocity() const { return _velocity; } /// velocity in domain scale units (0.0-1.0) per second
|
||||
const glm::vec3 getVelocity() const { return _velocity; } /// velocity in domain scale units (0.0-1.0) per second
|
||||
glm::vec3 getVelocityInMeters() const { return _velocity * (float) TREE_SCALE; } /// get velocity in meters
|
||||
void setVelocity(const glm::vec3& value) { _velocity = value; } /// velocity in domain scale units (0.0-1.0) per second
|
||||
void setVelocityInMeters(const glm::vec3& value) { _velocity = value / (float) TREE_SCALE; } /// velocity in meters
|
||||
bool hasVelocity() const { return _velocity != NO_VELOCITY; }
|
||||
|
||||
static const glm::vec3 DEFAULT_GRAVITY;
|
||||
static const glm::vec3 REGULAR_GRAVITY;
|
||||
static const glm::vec3 NO_GRAVITY;
|
||||
const glm::vec3& getGravity() const { return _gravity; } /// gravity in domain scale units (0.0-1.0) per second squared
|
||||
glm::vec3 getGravityInMeters() const { return _gravity * (float) TREE_SCALE; } /// get gravity in meters
|
||||
void setGravity(const glm::vec3& value) { _gravity = value; } /// gravity in domain scale units (0.0-1.0) per second squared
|
||||
void setGravityInMeters(const glm::vec3& value) { _gravity = value / (float) TREE_SCALE; } /// gravity in meters
|
||||
bool hasGravity() const { return _gravity != NO_GRAVITY; }
|
||||
|
||||
// TODO: this should eventually be updated to support resting on collisions with other surfaces
|
||||
|
@ -173,14 +195,38 @@ public:
|
|||
bool lifetimeHasExpired() const;
|
||||
|
||||
// position, size, and bounds related helpers
|
||||
float getSize() const { return _radius * 2.0f; } /// get maximum dimension in domain scale units (0.0 - 1.0)
|
||||
glm::vec3 getMinimumPoint() const { return _position - glm::vec3(_radius, _radius, _radius); }
|
||||
glm::vec3 getMaximumPoint() const { return _position + glm::vec3(_radius, _radius, _radius); }
|
||||
AACube getAACube() const { return AACube(getMinimumPoint(), getSize()); } /// AACube in domain scale units (0.0 - 1.0)
|
||||
float getSize() const; /// get maximum dimension in domain scale units (0.0 - 1.0)
|
||||
AACube getMaximumAACube() const;
|
||||
AACube getMinimumAACube() const;
|
||||
AABox getAABox() const; /// axis aligned bounding box in domain scale units (0.0 - 1.0)
|
||||
|
||||
static const QString DEFAULT_SCRIPT;
|
||||
const QString& getScript() const { return _script; }
|
||||
void setScript(const QString& value) { _script = value; }
|
||||
|
||||
static const glm::vec3 DEFAULT_REGISTRATION_POINT;
|
||||
const glm::vec3& getRegistrationPoint() const { return _registrationPoint; } /// registration point as ratio of entity
|
||||
void setRegistrationPoint(const glm::vec3& value) { _registrationPoint = glm::clamp(value, 0.0f, 1.0f); } /// registration point as ratio of entity
|
||||
|
||||
static const glm::vec3 NO_ANGULAR_VELOCITY;
|
||||
static const glm::vec3 DEFAULT_ANGULAR_VELOCITY;
|
||||
const glm::vec3& getAngularVelocity() const { return _angularVelocity; }
|
||||
void setAngularVelocity(const glm::vec3& value) { _angularVelocity = value; }
|
||||
bool hasAngularVelocity() const { return _angularVelocity != NO_ANGULAR_VELOCITY; }
|
||||
|
||||
static const float DEFAULT_ANGULAR_DAMPING;
|
||||
float getAngularDamping() const { return _angularDamping; }
|
||||
void setAngularDamping(float value) { _angularDamping = value; }
|
||||
|
||||
static const bool DEFAULT_VISIBLE;
|
||||
bool getVisible() const { return _visible; }
|
||||
void setVisible(bool value) { _visible = value; }
|
||||
bool isVisible() const { return _visible; }
|
||||
bool isInvisible() const { return !_visible; }
|
||||
|
||||
// TODO: We need to get rid of these users of getRadius()...
|
||||
float getRadius() const;
|
||||
|
||||
|
||||
protected:
|
||||
virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init
|
||||
|
@ -196,7 +242,7 @@ protected:
|
|||
quint64 _created;
|
||||
|
||||
glm::vec3 _position;
|
||||
float _radius;
|
||||
glm::vec3 _dimensions;
|
||||
glm::quat _rotation;
|
||||
float _glowLevel;
|
||||
float _mass;
|
||||
|
@ -205,6 +251,17 @@ protected:
|
|||
float _damping;
|
||||
float _lifetime;
|
||||
QString _script;
|
||||
glm::vec3 _registrationPoint;
|
||||
glm::vec3 _angularVelocity;
|
||||
float _angularDamping;
|
||||
bool _visible;
|
||||
|
||||
// NOTE: Radius support is obsolete, but these private helper functions are available for this class to
|
||||
// parse old data streams
|
||||
|
||||
/// set radius in domain scale units (0.0 - 1.0) this will also reset dimensions to be equal for each axis
|
||||
void setRadius(float value);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include "EntityItem.h"
|
||||
#include "EntityItemProperties.h"
|
||||
#include "ModelEntityItem.h"
|
||||
|
||||
EntityItemProperties::EntityItemProperties() :
|
||||
|
||||
|
@ -28,17 +29,21 @@ EntityItemProperties::EntityItemProperties() :
|
|||
_type(EntityTypes::Unknown),
|
||||
|
||||
_position(0),
|
||||
_radius(ENTITY_DEFAULT_RADIUS),
|
||||
_rotation(ENTITY_DEFAULT_ROTATION),
|
||||
_dimensions(EntityItem::DEFAULT_DIMENSIONS),
|
||||
_rotation(EntityItem::DEFAULT_ROTATION),
|
||||
_mass(EntityItem::DEFAULT_MASS),
|
||||
_velocity(EntityItem::DEFAULT_VELOCITY),
|
||||
_gravity(EntityItem::DEFAULT_GRAVITY),
|
||||
_damping(EntityItem::DEFAULT_DAMPING),
|
||||
_lifetime(EntityItem::DEFAULT_LIFETIME),
|
||||
_script(EntityItem::DEFAULT_SCRIPT),
|
||||
_registrationPoint(EntityItem::DEFAULT_REGISTRATION_POINT),
|
||||
_angularVelocity(EntityItem::DEFAULT_ANGULAR_VELOCITY),
|
||||
_angularDamping(EntityItem::DEFAULT_ANGULAR_DAMPING),
|
||||
_visible(EntityItem::DEFAULT_VISIBLE),
|
||||
|
||||
_positionChanged(false),
|
||||
_radiusChanged(false),
|
||||
_dimensionsChanged(false),
|
||||
_rotationChanged(false),
|
||||
_massChanged(false),
|
||||
_velocityChanged(false),
|
||||
|
@ -46,15 +51,20 @@ EntityItemProperties::EntityItemProperties() :
|
|||
_dampingChanged(false),
|
||||
_lifetimeChanged(false),
|
||||
_scriptChanged(false),
|
||||
_registrationPointChanged(false),
|
||||
_angularVelocityChanged(false),
|
||||
_angularDampingChanged(false),
|
||||
_visibleChanged(false),
|
||||
|
||||
_color(),
|
||||
_modelURL(""),
|
||||
_animationURL(""),
|
||||
_animationIsPlaying(false),
|
||||
_animationFrameIndex(0.0),
|
||||
_animationFPS(ENTITY_DEFAULT_ANIMATION_FPS),
|
||||
_animationIsPlaying(ModelEntityItem::DEFAULT_ANIMATION_IS_PLAYING),
|
||||
_animationFrameIndex(ModelEntityItem::DEFAULT_ANIMATION_FRAME_INDEX),
|
||||
_animationFPS(ModelEntityItem::DEFAULT_ANIMATION_FPS),
|
||||
_glowLevel(0.0f),
|
||||
|
||||
_naturalDimensions(1.0f, 1.0f, 1.0f),
|
||||
_colorChanged(false),
|
||||
_modelURLChanged(false),
|
||||
_animationURLChanged(false),
|
||||
|
@ -73,7 +83,7 @@ void EntityItemProperties::debugDump() const {
|
|||
qDebug() << " _id=" << _id;
|
||||
qDebug() << " _idSet=" << _idSet;
|
||||
qDebug() << " _position=" << _position.x << "," << _position.y << "," << _position.z;
|
||||
qDebug() << " _radius=" << _radius;
|
||||
qDebug() << " _dimensions=" << getDimensions();
|
||||
qDebug() << " _modelURL=" << _modelURL;
|
||||
qDebug() << " changed properties...";
|
||||
EntityPropertyFlags props = getChangedProperties();
|
||||
|
@ -82,65 +92,26 @@ void EntityItemProperties::debugDump() const {
|
|||
|
||||
EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
||||
EntityPropertyFlags changedProperties;
|
||||
if (_radiusChanged) {
|
||||
changedProperties += PROP_RADIUS;
|
||||
}
|
||||
|
||||
if (_positionChanged) {
|
||||
changedProperties += PROP_POSITION;
|
||||
}
|
||||
|
||||
if (_rotationChanged) {
|
||||
changedProperties += PROP_ROTATION;
|
||||
}
|
||||
|
||||
if (_massChanged) {
|
||||
changedProperties += PROP_MASS;
|
||||
}
|
||||
|
||||
if (_velocityChanged) {
|
||||
changedProperties += PROP_VELOCITY;
|
||||
}
|
||||
|
||||
if (_gravityChanged) {
|
||||
changedProperties += PROP_GRAVITY;
|
||||
}
|
||||
|
||||
if (_dampingChanged) {
|
||||
changedProperties += PROP_DAMPING;
|
||||
}
|
||||
|
||||
if (_lifetimeChanged) {
|
||||
changedProperties += PROP_LIFETIME;
|
||||
}
|
||||
|
||||
if (_scriptChanged) {
|
||||
changedProperties += PROP_SCRIPT;
|
||||
}
|
||||
|
||||
if (_colorChanged) {
|
||||
changedProperties += PROP_COLOR;
|
||||
}
|
||||
|
||||
if (_modelURLChanged) {
|
||||
changedProperties += PROP_MODEL_URL;
|
||||
}
|
||||
|
||||
if (_animationURLChanged) {
|
||||
changedProperties += PROP_ANIMATION_URL;
|
||||
}
|
||||
|
||||
if (_animationIsPlayingChanged) {
|
||||
changedProperties += PROP_ANIMATION_PLAYING;
|
||||
}
|
||||
|
||||
if (_animationFrameIndexChanged) {
|
||||
changedProperties += PROP_ANIMATION_FRAME_INDEX;
|
||||
}
|
||||
|
||||
if (_animationFPSChanged) {
|
||||
changedProperties += PROP_ANIMATION_FPS;
|
||||
}
|
||||
|
||||
CHECK_PROPERTY_CHANGE(PROP_DIMENSIONS, dimensions);
|
||||
CHECK_PROPERTY_CHANGE(PROP_POSITION, position);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ROTATION, rotation);
|
||||
CHECK_PROPERTY_CHANGE(PROP_MASS, mass);
|
||||
CHECK_PROPERTY_CHANGE(PROP_VELOCITY, velocity);
|
||||
CHECK_PROPERTY_CHANGE(PROP_GRAVITY, gravity);
|
||||
CHECK_PROPERTY_CHANGE(PROP_DAMPING, damping);
|
||||
CHECK_PROPERTY_CHANGE(PROP_LIFETIME, lifetime);
|
||||
CHECK_PROPERTY_CHANGE(PROP_SCRIPT, script);
|
||||
CHECK_PROPERTY_CHANGE(PROP_COLOR, color);
|
||||
CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_URL, animationURL);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_PLAYING, animationIsPlaying);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FRAME_INDEX, animationFrameIndex);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FPS, animationFPS);
|
||||
CHECK_PROPERTY_CHANGE(PROP_VISIBLE, visible);
|
||||
CHECK_PROPERTY_CHANGE(PROP_REGISTRATION_POINT, registrationPoint);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ANGULAR_VELOCITY, angularVelocity);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ANGULAR_DAMPING, angularDamping);
|
||||
|
||||
return changedProperties;
|
||||
}
|
||||
|
@ -149,38 +120,35 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
|
|||
QScriptValue properties = engine->newObject();
|
||||
|
||||
if (_idSet) {
|
||||
properties.setProperty("id", _id.toString());
|
||||
bool isKnownID = (_id != UNKNOWN_ENTITY_ID);
|
||||
properties.setProperty("isKnownID", isKnownID);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(id, _id.toString());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(isKnownID, (_id != UNKNOWN_ENTITY_ID));
|
||||
} else {
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(isKnownID, false);
|
||||
}
|
||||
|
||||
properties.setProperty("type", EntityTypes::getEntityTypeName(_type));
|
||||
|
||||
QScriptValue position = vec3toScriptValue(engine, _position);
|
||||
properties.setProperty("position", position);
|
||||
properties.setProperty("radius", _radius);
|
||||
QScriptValue rotation = quatToScriptValue(engine, _rotation);
|
||||
properties.setProperty("rotation", rotation);
|
||||
properties.setProperty("mass", _mass);
|
||||
QScriptValue velocity = vec3toScriptValue(engine, _velocity);
|
||||
properties.setProperty("velocity", velocity);
|
||||
QScriptValue gravity = vec3toScriptValue(engine, _gravity);
|
||||
properties.setProperty("gravity", gravity);
|
||||
properties.setProperty("damping", _damping);
|
||||
properties.setProperty("lifetime", _lifetime);
|
||||
properties.setProperty("age", getAge()); // gettable, but not settable
|
||||
properties.setProperty("ageAsText", formatSecondsElapsed(getAge())); // gettable, but not settable
|
||||
properties.setProperty("script", _script);
|
||||
|
||||
QScriptValue color = xColorToScriptValue(engine, _color);
|
||||
properties.setProperty("color", color);
|
||||
properties.setProperty("modelURL", _modelURL);
|
||||
|
||||
properties.setProperty("animationURL", _animationURL);
|
||||
properties.setProperty("animationIsPlaying", _animationIsPlaying);
|
||||
properties.setProperty("animationFrameIndex", _animationFrameIndex);
|
||||
properties.setProperty("animationFPS", _animationFPS);
|
||||
properties.setProperty("glowLevel", _glowLevel);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(type, EntityTypes::getEntityTypeName(_type));
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(position);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(dimensions);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(naturalDimensions); // gettable, but not settable
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_QUAT(rotation);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(velocity);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(gravity);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(damping);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(lifetime);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(age, getAge()); // gettable, but not settable
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(ageAsText, formatSecondsElapsed(getAge())); // gettable, but not settable
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(script);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(registrationPoint);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(angularVelocity);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(angularDamping);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(visible);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR(color);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(modelURL);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(animationURL);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(animationIsPlaying);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(animationFrameIndex);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(animationFPS);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(glowLevel);
|
||||
|
||||
// Sitting properties support
|
||||
QScriptValue sittingPoints = engine->newObject();
|
||||
|
@ -192,217 +160,39 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
|
|||
sittingPoints.setProperty(i, sittingPoint);
|
||||
}
|
||||
sittingPoints.setProperty("length", _sittingPoints.size());
|
||||
properties.setProperty("sittingPoints", sittingPoints);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(sittingPoints, sittingPoints); // gettable, but not settable
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) {
|
||||
|
||||
|
||||
QScriptValue typeScriptValue = object.property("type");
|
||||
if (typeScriptValue.isValid()) {
|
||||
QString typeName;
|
||||
typeName = typeScriptValue.toVariant().toString();
|
||||
_type = EntityTypes::getEntityTypeFromName(typeName);
|
||||
setType(typeScriptValue.toVariant().toString());
|
||||
}
|
||||
|
||||
QScriptValue position = object.property("position");
|
||||
if (position.isValid()) {
|
||||
QScriptValue x = position.property("x");
|
||||
QScriptValue y = position.property("y");
|
||||
QScriptValue z = position.property("z");
|
||||
if (x.isValid() && y.isValid() && z.isValid()) {
|
||||
glm::vec3 newPosition;
|
||||
newPosition.x = x.toVariant().toFloat();
|
||||
newPosition.y = y.toVariant().toFloat();
|
||||
newPosition.z = z.toVariant().toFloat();
|
||||
if (_defaultSettings || newPosition != _position) {
|
||||
setPosition(newPosition); // gives us automatic clamping
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue radius = object.property("radius");
|
||||
if (radius.isValid()) {
|
||||
float newRadius;
|
||||
newRadius = radius.toVariant().toFloat();
|
||||
if (_defaultSettings || newRadius != _radius) {
|
||||
_radius = newRadius;
|
||||
_radiusChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue rotation = object.property("rotation");
|
||||
if (rotation.isValid()) {
|
||||
QScriptValue x = rotation.property("x");
|
||||
QScriptValue y = rotation.property("y");
|
||||
QScriptValue z = rotation.property("z");
|
||||
QScriptValue w = rotation.property("w");
|
||||
if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) {
|
||||
glm::quat newRotation;
|
||||
newRotation.x = x.toVariant().toFloat();
|
||||
newRotation.y = y.toVariant().toFloat();
|
||||
newRotation.z = z.toVariant().toFloat();
|
||||
newRotation.w = w.toVariant().toFloat();
|
||||
if (_defaultSettings || newRotation != _rotation) {
|
||||
_rotation = newRotation;
|
||||
_rotationChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue mass = object.property("mass");
|
||||
if (mass.isValid()) {
|
||||
float newValue;
|
||||
newValue = mass.toVariant().toFloat();
|
||||
if (_defaultSettings || newValue != _mass) {
|
||||
_mass = newValue;
|
||||
_massChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue velocity = object.property("velocity");
|
||||
if (velocity.isValid()) {
|
||||
QScriptValue x = velocity.property("x");
|
||||
QScriptValue y = velocity.property("y");
|
||||
QScriptValue z = velocity.property("z");
|
||||
if (x.isValid() && y.isValid() && z.isValid()) {
|
||||
glm::vec3 newValue;
|
||||
newValue.x = x.toVariant().toFloat();
|
||||
newValue.y = y.toVariant().toFloat();
|
||||
newValue.z = z.toVariant().toFloat();
|
||||
if (_defaultSettings || newValue != _velocity) {
|
||||
_velocity = newValue;
|
||||
_velocityChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue gravity = object.property("gravity");
|
||||
if (gravity.isValid()) {
|
||||
QScriptValue x = gravity.property("x");
|
||||
QScriptValue y = gravity.property("y");
|
||||
QScriptValue z = gravity.property("z");
|
||||
if (x.isValid() && y.isValid() && z.isValid()) {
|
||||
glm::vec3 newValue;
|
||||
newValue.x = x.toVariant().toFloat();
|
||||
newValue.y = y.toVariant().toFloat();
|
||||
newValue.z = z.toVariant().toFloat();
|
||||
if (_defaultSettings || newValue != _gravity) {
|
||||
_gravity = newValue;
|
||||
_gravityChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue damping = object.property("damping");
|
||||
if (damping.isValid()) {
|
||||
float newValue;
|
||||
newValue = damping.toVariant().toFloat();
|
||||
if (_defaultSettings || newValue != _damping) {
|
||||
_damping = newValue;
|
||||
_dampingChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue lifetime = object.property("lifetime");
|
||||
if (lifetime.isValid()) {
|
||||
float newValue;
|
||||
newValue = lifetime.toVariant().toFloat();
|
||||
if (_defaultSettings || newValue != _lifetime) {
|
||||
_lifetime = newValue;
|
||||
_lifetimeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue script = object.property("script");
|
||||
if (script.isValid()) {
|
||||
QString newValue;
|
||||
newValue = script.toVariant().toString();
|
||||
if (_defaultSettings || newValue != _script) {
|
||||
_script = newValue;
|
||||
_scriptChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue color = object.property("color");
|
||||
if (color.isValid()) {
|
||||
QScriptValue red = color.property("red");
|
||||
QScriptValue green = color.property("green");
|
||||
QScriptValue blue = color.property("blue");
|
||||
if (red.isValid() && green.isValid() && blue.isValid()) {
|
||||
xColor newColor;
|
||||
newColor.red = red.toVariant().toInt();
|
||||
newColor.green = green.toVariant().toInt();
|
||||
newColor.blue = blue.toVariant().toInt();
|
||||
if (_defaultSettings || (newColor.red != _color.red ||
|
||||
newColor.green != _color.green ||
|
||||
newColor.blue != _color.blue)) {
|
||||
_color = newColor;
|
||||
_colorChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue modelURL = object.property("modelURL");
|
||||
if (modelURL.isValid()) {
|
||||
QString newModelURL;
|
||||
newModelURL = modelURL.toVariant().toString();
|
||||
if (_defaultSettings || newModelURL != _modelURL) {
|
||||
_modelURL = newModelURL;
|
||||
_modelURLChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue animationURL = object.property("animationURL");
|
||||
if (animationURL.isValid()) {
|
||||
QString newAnimationURL;
|
||||
newAnimationURL = animationURL.toVariant().toString();
|
||||
if (_defaultSettings || newAnimationURL != _animationURL) {
|
||||
_animationURL = newAnimationURL;
|
||||
_animationURLChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue animationIsPlaying = object.property("animationIsPlaying");
|
||||
if (animationIsPlaying.isValid()) {
|
||||
bool newIsAnimationPlaying;
|
||||
newIsAnimationPlaying = animationIsPlaying.toVariant().toBool();
|
||||
if (_defaultSettings || newIsAnimationPlaying != _animationIsPlaying) {
|
||||
_animationIsPlaying = newIsAnimationPlaying;
|
||||
_animationIsPlayingChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue animationFrameIndex = object.property("animationFrameIndex");
|
||||
if (animationFrameIndex.isValid()) {
|
||||
float newFrameIndex;
|
||||
newFrameIndex = animationFrameIndex.toVariant().toFloat();
|
||||
if (_defaultSettings || newFrameIndex != _animationFrameIndex) {
|
||||
_animationFrameIndex = newFrameIndex;
|
||||
_animationFrameIndexChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue animationFPS = object.property("animationFPS");
|
||||
if (animationFPS.isValid()) {
|
||||
float newFPS;
|
||||
newFPS = animationFPS.toVariant().toFloat();
|
||||
if (_defaultSettings || newFPS != _animationFPS) {
|
||||
_animationFPS = newFPS;
|
||||
_animationFPSChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue glowLevel = object.property("glowLevel");
|
||||
if (glowLevel.isValid()) {
|
||||
float newGlowLevel;
|
||||
newGlowLevel = glowLevel.toVariant().toFloat();
|
||||
if (_defaultSettings || newGlowLevel != _glowLevel) {
|
||||
_glowLevel = newGlowLevel;
|
||||
_glowLevelChanged = true;
|
||||
}
|
||||
}
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(position, setPosition);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(dimensions, setDimensions);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_QUAT(rotation, setRotation);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(mass, setMass);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(velocity, setVelocity);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(gravity, setGravity);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(damping, setDamping);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(lifetime, setLifetime);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(script, setScript);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(registrationPoint, setRegistrationPoint);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(angularVelocity, setAngularVelocity);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(angularDamping, setAngularDamping);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(visible, setVisible);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(color, setColor);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(modelURL, setModelURL);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(animationURL, setAnimationURL);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(animationIsPlaying, setAnimationIsPlaying);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(animationFPS, setAnimationFPS);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(animationFrameIndex, setAnimationFrameIndex);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(glowLevel, setGlowLevel);
|
||||
|
||||
_lastEdited = usecTimestampNow();
|
||||
}
|
||||
|
@ -432,7 +222,6 @@ void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemP
|
|||
//
|
||||
bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties,
|
||||
unsigned char* bufferOut, int sizeIn, int& sizeOut) {
|
||||
|
||||
OctreePacketData ourDataPacket(false, sizeIn); // create a packetData object to add out packet details too.
|
||||
OctreePacketData* packetData = &ourDataPacket; // we want a pointer to this so we can use our APPEND_ENTITY_PROPERTY macro
|
||||
|
||||
|
@ -532,23 +321,26 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
// These items would go here once supported....
|
||||
// PROP_PAGED_PROPERTY,
|
||||
// PROP_CUSTOM_PROPERTIES_INCLUDED,
|
||||
// PROP_VISIBLE,
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_POSITION, appendPosition, properties.getPosition());
|
||||
APPEND_ENTITY_PROPERTY(PROP_RADIUS, appendValue, properties.getRadius());
|
||||
APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, appendValue, properties.getDimensions()); // NOTE: PROP_RADIUS obsolete
|
||||
APPEND_ENTITY_PROPERTY(PROP_ROTATION, appendValue, properties.getRotation());
|
||||
APPEND_ENTITY_PROPERTY(PROP_MASS, appendValue, properties.getMass());
|
||||
APPEND_ENTITY_PROPERTY(PROP_VELOCITY, appendValue, properties.getVelocity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_GRAVITY, appendValue, properties.getGravity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_DAMPING, appendValue, properties.getDamping());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LIFETIME, appendValue, properties.getLifetime());
|
||||
//APPEND_ENTITY_PROPERTY(PROP_SCRIPT, appendValue, properties.getScript()); // not supported by edit messages
|
||||
APPEND_ENTITY_PROPERTY(PROP_SCRIPT, appendValue, properties.getScript());
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, properties.getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, appendValue, properties.getModelURL());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, appendValue, properties.getAnimationURL());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, appendValue, properties.getAnimationFPS());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, appendValue, properties.getAnimationFrameIndex());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, appendValue, properties.getAnimationIsPlaying());
|
||||
APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, appendValue, properties.getRegistrationPoint());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, appendValue, properties.getAngularVelocity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, appendValue, properties.getAngularDamping());
|
||||
APPEND_ENTITY_PROPERTY(PROP_VISIBLE, appendValue, properties.getVisible());
|
||||
}
|
||||
if (propertyCount > 0) {
|
||||
int endOfEntityItemData = packetData->getUncompressedByteOffset();
|
||||
|
@ -727,20 +519,24 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
processedBytes += propertyFlags.getEncodedLength();
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, glm::vec3, setPosition);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RADIUS, float, setRadius);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DIMENSIONS, glm::vec3, setDimensions); // NOTE: PROP_RADIUS obsolete
|
||||
READ_ENTITY_PROPERTY_QUAT_TO_PROPERTIES(PROP_ROTATION, setRotation);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MASS, float, setMass);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VELOCITY, glm::vec3, setVelocity);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GRAVITY, glm::vec3, setGravity);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DAMPING, float, setDamping);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LIFETIME, float, setLifetime);
|
||||
//READ_ENTITY_PROPERTY_STRING(PROP_SCRIPT,setScript); // not yet supported by edit messages...
|
||||
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_SCRIPT,setScript);
|
||||
READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_COLOR, setColor);
|
||||
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_MODEL_URL, setModelURL);
|
||||
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_ANIMATION_URL, setAnimationURL);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FPS, float, setAnimationFPS);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FRAME_INDEX, float, setAnimationFrameIndex);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_PLAYING, bool, setAnimationIsPlaying);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_VELOCITY, glm::vec3, setAngularVelocity);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_DAMPING, float, setAngularDamping);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VISIBLE, bool, setVisible);
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
@ -774,10 +570,8 @@ bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityIt
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
void EntityItemProperties::markAllChanged() {
|
||||
_positionChanged = true;
|
||||
_radiusChanged = true;
|
||||
_rotationChanged = true;
|
||||
_massChanged = true;
|
||||
_velocityChanged = true;
|
||||
|
@ -785,7 +579,10 @@ void EntityItemProperties::markAllChanged() {
|
|||
_dampingChanged = true;
|
||||
_lifetimeChanged = true;
|
||||
_scriptChanged = true;
|
||||
|
||||
_registrationPointChanged = true;
|
||||
_angularVelocityChanged = true;
|
||||
_angularDampingChanged = true;
|
||||
_visibleChanged = true;
|
||||
_colorChanged = true;
|
||||
_modelURLChanged = true;
|
||||
_animationURLChanged = true;
|
||||
|
@ -793,5 +590,36 @@ void EntityItemProperties::markAllChanged() {
|
|||
_animationFrameIndexChanged = true;
|
||||
_animationFPSChanged = true;
|
||||
_glowLevelChanged = true;
|
||||
|
||||
}
|
||||
|
||||
AACube EntityItemProperties::getMaximumAACubeInTreeUnits() const {
|
||||
AACube maxCube = getMaximumAACubeInMeters();
|
||||
maxCube.scale(1 / (float)TREE_SCALE);
|
||||
return maxCube;
|
||||
}
|
||||
|
||||
/// The maximum bounding cube for the entity, independent of it's rotation.
|
||||
/// This accounts for the registration point (upon which rotation occurs around).
|
||||
///
|
||||
AACube EntityItemProperties::getMaximumAACubeInMeters() const {
|
||||
// * we know that the position is the center of rotation
|
||||
glm::vec3 centerOfRotation = _position; // also where _registration point is
|
||||
|
||||
// * we know that the registration point is the center of rotation
|
||||
// * we can calculate the length of the furthest extent from the registration point
|
||||
// as the dimensions * max (registrationPoint, (1.0,1.0,1.0) - registrationPoint)
|
||||
glm::vec3 registrationPoint = (_dimensions * _registrationPoint);
|
||||
glm::vec3 registrationRemainder = (_dimensions * (glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint));
|
||||
glm::vec3 furthestExtentFromRegistration = glm::max(registrationPoint, registrationRemainder);
|
||||
|
||||
// * we know that if you rotate in any direction you would create a sphere
|
||||
// that has a radius of the length of furthest extent from registration point
|
||||
float radius = glm::length(furthestExtentFromRegistration);
|
||||
|
||||
// * we know that the minimum bounding cube of this maximum possible sphere is
|
||||
// (center - radius) to (center + radius)
|
||||
glm::vec3 minimumCorner = centerOfRotation - glm::vec3(radius, radius, radius);
|
||||
float diameter = radius * 2.0f;
|
||||
|
||||
return AACube(minimumCorner, diameter);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/extented_min_max.hpp>
|
||||
|
||||
#include <QtScript/QScriptEngine>
|
||||
#include <QtCore/QObject>
|
||||
|
@ -28,20 +29,9 @@
|
|||
|
||||
|
||||
#include "EntityItemID.h"
|
||||
#include "EntityItemPropertiesMacros.h"
|
||||
#include "EntityTypes.h"
|
||||
|
||||
|
||||
// TODO: should these be static members of EntityItem or EntityItemProperties?
|
||||
const float ENTITY_DEFAULT_RADIUS = 0.1f / TREE_SCALE;
|
||||
const float ENTITY_MINIMUM_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container
|
||||
const QString ENTITY_DEFAULT_MODEL_URL("");
|
||||
const glm::quat ENTITY_DEFAULT_ROTATION;
|
||||
const QString ENTITY_DEFAULT_ANIMATION_URL("");
|
||||
const float ENTITY_DEFAULT_ANIMATION_FPS = 30.0f;
|
||||
|
||||
const quint64 UNKNOWN_CREATED_TIME = (quint64)(-1);
|
||||
const quint64 USE_EXISTING_CREATED_TIME = (quint64)(-2);
|
||||
|
||||
// PropertyFlags support
|
||||
enum EntityPropertyList {
|
||||
PROP_PAGED_PROPERTY,
|
||||
|
@ -50,7 +40,8 @@ enum EntityPropertyList {
|
|||
// these properties are supported by the EntityItem base class
|
||||
PROP_VISIBLE,
|
||||
PROP_POSITION,
|
||||
PROP_RADIUS,
|
||||
PROP_RADIUS, // NOTE: PROP_RADIUS is obsolete and only included in old format streams
|
||||
PROP_DIMENSIONS = PROP_RADIUS,
|
||||
PROP_ROTATION,
|
||||
PROP_MASS,
|
||||
PROP_VELOCITY,
|
||||
|
@ -67,16 +58,24 @@ enum EntityPropertyList {
|
|||
PROP_ANIMATION_FRAME_INDEX,
|
||||
PROP_ANIMATION_PLAYING,
|
||||
|
||||
PROP_LAST_ITEM = PROP_ANIMATION_PLAYING
|
||||
// these properties are supported by the EntityItem base class
|
||||
PROP_REGISTRATION_POINT,
|
||||
PROP_ANGULAR_VELOCITY,
|
||||
PROP_ANGULAR_DAMPING,
|
||||
|
||||
PROP_LAST_ITEM = PROP_ANGULAR_DAMPING
|
||||
};
|
||||
|
||||
typedef PropertyFlags<EntityPropertyList> EntityPropertyFlags;
|
||||
|
||||
const quint64 UNKNOWN_CREATED_TIME = (quint64)(-1);
|
||||
const quint64 USE_EXISTING_CREATED_TIME = (quint64)(-2);
|
||||
|
||||
|
||||
/// A collection of properties of an entity item used in the scripting API. Translates between the actual properties of an
|
||||
/// entity and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete
|
||||
/// set of entity item properties via JavaScript hashes/QScriptValues
|
||||
/// all units for position, radius, etc are in meter units
|
||||
/// all units for position, dimensions, etc are in meter units
|
||||
class EntityItemProperties {
|
||||
friend class EntityItem; // TODO: consider removing this friend relationship and use public methods
|
||||
friend class ModelEntityItem; // TODO: consider removing this friend relationship and use public methods
|
||||
|
@ -96,31 +95,27 @@ public:
|
|||
/// used by EntityScriptingInterface to return EntityItemProperties for unknown models
|
||||
void setIsUnknownID() { _id = UNKNOWN_ENTITY_ID; _idSet = true; }
|
||||
|
||||
glm::vec3 getMinimumPointMeters() const { return _position - glm::vec3(_radius, _radius, _radius); }
|
||||
glm::vec3 getMaximumPointMeters() const { return _position + glm::vec3(_radius, _radius, _radius); }
|
||||
AACube getAACubeMeters() const { return AACube(getMinimumPointMeters(), getMaxDimension()); } /// AACube in meter units
|
||||
|
||||
glm::vec3 getMinimumPointTreeUnits() const { return getMinimumPointMeters() / (float)TREE_SCALE; }
|
||||
glm::vec3 getMaximumPointTreeUnits() const { return getMaximumPointMeters() / (float)TREE_SCALE; }
|
||||
/// AACube in domain scale units (0.0 - 1.0)
|
||||
AACube getAACubeTreeUnits() const {
|
||||
return AACube(getMinimumPointMeters() / (float)TREE_SCALE, getMaxDimension() / (float)TREE_SCALE);
|
||||
}
|
||||
AACube getMaximumAACubeInTreeUnits() const;
|
||||
AACube getMaximumAACubeInMeters() const;
|
||||
|
||||
void debugDump() const;
|
||||
|
||||
|
||||
// properties of all entities
|
||||
EntityTypes::EntityType getType() const { return _type; }
|
||||
const glm::vec3& getPosition() const { return _position; }
|
||||
float getRadius() const { return _radius; }
|
||||
float getMaxDimension() const { return _radius * 2.0f; }
|
||||
glm::vec3 getDimensions() const { return glm::vec3(_radius, _radius, _radius) * 2.0f; }
|
||||
const glm::quat& getRotation() const { return _rotation; }
|
||||
|
||||
void setType(EntityTypes::EntityType type) { _type = type; }
|
||||
|
||||
const glm::vec3& getPosition() const { return _position; }
|
||||
/// set position in meter units, will be clamped to domain bounds
|
||||
void setPosition(const glm::vec3& value) { _position = glm::clamp(value, 0.0f, (float)TREE_SCALE); _positionChanged = true; }
|
||||
void setRadius(float value) { _radius = value; _radiusChanged = true; }
|
||||
|
||||
|
||||
const glm::vec3& getDimensions() const { return _dimensions; }
|
||||
void setDimensions(const glm::vec3& value) { _dimensions = value; _dimensionsChanged = true; }
|
||||
float getMaxDimension() const { return glm::max(_dimensions.x, _dimensions.y, _dimensions.z); }
|
||||
|
||||
const glm::quat& getRotation() const { return _rotation; }
|
||||
void setRotation(const glm::quat& rotation) { _rotation = rotation; _rotationChanged = true; }
|
||||
|
||||
float getMass() const { return _mass; }
|
||||
|
@ -148,9 +143,9 @@ public:
|
|||
|
||||
|
||||
// NOTE: how do we handle _defaultSettings???
|
||||
bool containsBoundsProperties() const { return (_positionChanged || _radiusChanged); }
|
||||
bool containsBoundsProperties() const { return (_positionChanged || _dimensionsChanged); }
|
||||
bool containsPositionChange() const { return _positionChanged; }
|
||||
bool containsRadiusChange() const { return _radiusChanged; }
|
||||
bool containsDimensionsChange() const { return _dimensionsChanged; }
|
||||
|
||||
// TODO: this need to be more generic. for now, we're going to have the properties class support these as
|
||||
// named getter/setters, but we want to move them to generic types...
|
||||
|
@ -162,6 +157,7 @@ public:
|
|||
bool getAnimationIsPlaying() const { return _animationIsPlaying; }
|
||||
float getAnimationFPS() const { return _animationFPS; }
|
||||
float getGlowLevel() const { return _glowLevel; }
|
||||
const QString& getScript() const { return _script; }
|
||||
|
||||
// model related properties
|
||||
void setColor(const xColor& value) { _color = value; _colorChanged = true; }
|
||||
|
@ -171,6 +167,7 @@ public:
|
|||
void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; _animationIsPlayingChanged = true; }
|
||||
void setAnimationFPS(float value) { _animationFPS = value; _animationFPSChanged = true; }
|
||||
void setGlowLevel(float value) { _glowLevel = value; _glowLevelChanged = true; }
|
||||
void setScript(const QString& value) { _script = value; _scriptChanged = true; }
|
||||
|
||||
|
||||
static bool encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties,
|
||||
|
@ -184,7 +181,6 @@ public:
|
|||
EntityItemID& entityID, EntityItemProperties& properties);
|
||||
|
||||
bool positionChanged() const { return _positionChanged; }
|
||||
bool radiusChanged() const { return _radiusChanged; }
|
||||
bool rotationChanged() const { return _rotationChanged; }
|
||||
bool massChanged() const { return _massChanged; }
|
||||
bool velocityChanged() const { return _velocityChanged; }
|
||||
|
@ -192,6 +188,8 @@ public:
|
|||
bool dampingChanged() const { return _dampingChanged; }
|
||||
bool lifetimeChanged() const { return _lifetimeChanged; }
|
||||
bool scriptChanged() const { return _scriptChanged; }
|
||||
bool dimensionsChanged() const { return _dimensionsChanged; }
|
||||
bool registrationPointChanged() const { return _registrationPointChanged; }
|
||||
bool colorChanged() const { return _colorChanged; }
|
||||
bool modelURLChanged() const { return _modelURLChanged; }
|
||||
bool animationURLChanged() const { return _animationURLChanged; }
|
||||
|
@ -203,6 +201,24 @@ public:
|
|||
void clearID() { _id = UNKNOWN_ENTITY_ID; _idSet = false; }
|
||||
void markAllChanged();
|
||||
|
||||
QVector<SittingPoint> getSittingPoints() const { return _sittingPoints; }
|
||||
void setSittingPoints(QVector<SittingPoint> sittingPoints) { _sittingPoints = sittingPoints; }
|
||||
|
||||
const glm::vec3& getNaturalDimensions() const { return _naturalDimensions; }
|
||||
void setNaturalDimensions(const glm::vec3& value) { _naturalDimensions = value; }
|
||||
|
||||
const glm::vec3& getRegistrationPoint() const { return _registrationPoint; }
|
||||
void setRegistrationPoint(const glm::vec3& value) { _registrationPoint = value; _registrationPointChanged = true; }
|
||||
|
||||
const glm::vec3& getAngularVelocity() const { return _angularVelocity; }
|
||||
void setAngularVelocity(const glm::vec3& value) { _angularVelocity = value; _angularVelocityChanged = true; }
|
||||
|
||||
float getAngularDamping() const { return _angularDamping; }
|
||||
void setAngularDamping(float value) { _angularDamping = value; _angularDampingChanged = true; }
|
||||
|
||||
bool getVisible() const { return _visible; }
|
||||
void setVisible(bool value) { _visible = value; _visibleChanged = true; }
|
||||
|
||||
private:
|
||||
void setLastEdited(quint64 usecTime) { _lastEdited = usecTime; }
|
||||
|
||||
|
@ -212,8 +228,11 @@ private:
|
|||
quint64 _created;
|
||||
|
||||
EntityTypes::EntityType _type;
|
||||
|
||||
void setType(const QString& typeName) { _type = EntityTypes::getEntityTypeFromName(typeName); }
|
||||
|
||||
glm::vec3 _position;
|
||||
float _radius;
|
||||
glm::vec3 _dimensions;
|
||||
glm::quat _rotation;
|
||||
float _mass;
|
||||
glm::vec3 _velocity;
|
||||
|
@ -221,9 +240,13 @@ private:
|
|||
float _damping;
|
||||
float _lifetime;
|
||||
QString _script;
|
||||
glm::vec3 _registrationPoint;
|
||||
glm::vec3 _angularVelocity;
|
||||
float _angularDamping;
|
||||
bool _visible;
|
||||
|
||||
bool _positionChanged;
|
||||
bool _radiusChanged;
|
||||
bool _dimensionsChanged;
|
||||
bool _rotationChanged;
|
||||
bool _massChanged;
|
||||
bool _velocityChanged;
|
||||
|
@ -231,6 +254,10 @@ private:
|
|||
bool _dampingChanged;
|
||||
bool _lifetimeChanged;
|
||||
bool _scriptChanged;
|
||||
bool _registrationPointChanged;
|
||||
bool _angularVelocityChanged;
|
||||
bool _angularDampingChanged;
|
||||
bool _visibleChanged;
|
||||
|
||||
// TODO: this need to be more generic. for now, we're going to have the properties class support these as
|
||||
// named getter/setters, but we want to move them to generic types...
|
||||
|
@ -242,6 +269,7 @@ private:
|
|||
float _animationFPS;
|
||||
float _glowLevel;
|
||||
QVector<SittingPoint> _sittingPoints;
|
||||
glm::vec3 _naturalDimensions;
|
||||
|
||||
bool _colorChanged;
|
||||
bool _modelURLChanged;
|
||||
|
@ -257,107 +285,4 @@ Q_DECLARE_METATYPE(EntityItemProperties);
|
|||
QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties);
|
||||
void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemProperties& properties);
|
||||
|
||||
#define APPEND_ENTITY_PROPERTY(P,O,V) \
|
||||
if (requestedProperties.getHasProperty(P)) { \
|
||||
LevelDetails propertyLevel = packetData->startLevel(); \
|
||||
successPropertyFits = packetData->O(V); \
|
||||
if (successPropertyFits) { \
|
||||
propertyFlags |= P; \
|
||||
propertiesDidntFit -= P; \
|
||||
propertyCount++; \
|
||||
packetData->endLevel(propertyLevel); \
|
||||
} else { \
|
||||
packetData->discardLevel(propertyLevel); \
|
||||
appendState = OctreeElement::PARTIAL; \
|
||||
} \
|
||||
} else { \
|
||||
propertiesDidntFit -= P; \
|
||||
}
|
||||
|
||||
#define READ_ENTITY_PROPERTY(P,T,M) \
|
||||
if (propertyFlags.getHasProperty(P)) { \
|
||||
T fromBuffer; \
|
||||
memcpy(&fromBuffer, dataAt, sizeof(fromBuffer)); \
|
||||
dataAt += sizeof(fromBuffer); \
|
||||
bytesRead += sizeof(fromBuffer); \
|
||||
if (overwriteLocalData) { \
|
||||
M = fromBuffer; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define READ_ENTITY_PROPERTY_QUAT(P,M) \
|
||||
if (propertyFlags.getHasProperty(P)) { \
|
||||
glm::quat fromBuffer; \
|
||||
int bytes = unpackOrientationQuatFromBytes(dataAt, fromBuffer); \
|
||||
dataAt += bytes; \
|
||||
bytesRead += bytes; \
|
||||
if (overwriteLocalData) { \
|
||||
M = fromBuffer; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define READ_ENTITY_PROPERTY_STRING(P,O) \
|
||||
if (propertyFlags.getHasProperty(P)) { \
|
||||
uint16_t length; \
|
||||
memcpy(&length, dataAt, sizeof(length)); \
|
||||
dataAt += sizeof(length); \
|
||||
bytesRead += sizeof(length); \
|
||||
QString value((const char*)dataAt); \
|
||||
dataAt += length; \
|
||||
bytesRead += length; \
|
||||
if (overwriteLocalData) { \
|
||||
O(value); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define READ_ENTITY_PROPERTY_COLOR(P,M) \
|
||||
if (propertyFlags.getHasProperty(P)) { \
|
||||
if (overwriteLocalData) { \
|
||||
memcpy(M, dataAt, sizeof(M)); \
|
||||
} \
|
||||
dataAt += sizeof(rgbColor); \
|
||||
bytesRead += sizeof(rgbColor); \
|
||||
}
|
||||
|
||||
#define READ_ENTITY_PROPERTY_TO_PROPERTIES(P,T,O) \
|
||||
if (propertyFlags.getHasProperty(P)) { \
|
||||
T fromBuffer; \
|
||||
memcpy(&fromBuffer, dataAt, sizeof(fromBuffer)); \
|
||||
dataAt += sizeof(fromBuffer); \
|
||||
processedBytes += sizeof(fromBuffer); \
|
||||
properties.O(fromBuffer); \
|
||||
}
|
||||
|
||||
#define READ_ENTITY_PROPERTY_QUAT_TO_PROPERTIES(P,O) \
|
||||
if (propertyFlags.getHasProperty(P)) { \
|
||||
glm::quat fromBuffer; \
|
||||
int bytes = unpackOrientationQuatFromBytes(dataAt, fromBuffer); \
|
||||
dataAt += bytes; \
|
||||
processedBytes += bytes; \
|
||||
properties.O(fromBuffer); \
|
||||
}
|
||||
|
||||
#define READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(P,O) \
|
||||
if (propertyFlags.getHasProperty(P)) { \
|
||||
uint16_t length; \
|
||||
memcpy(&length, dataAt, sizeof(length)); \
|
||||
dataAt += sizeof(length); \
|
||||
processedBytes += sizeof(length); \
|
||||
QString value((const char*)dataAt); \
|
||||
dataAt += length; \
|
||||
processedBytes += length; \
|
||||
properties.O(value); \
|
||||
}
|
||||
|
||||
#define READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(P,O) \
|
||||
if (propertyFlags.getHasProperty(P)) { \
|
||||
xColor color; \
|
||||
memcpy(&color, dataAt, sizeof(color)); \
|
||||
dataAt += sizeof(color); \
|
||||
processedBytes += sizeof(color); \
|
||||
properties.O(color); \
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // hifi_EntityItemProperties_h
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue