Merge branch 'master' of github.com:highfidelity/hifi into fix-hulls-for-non-default-registration

This commit is contained in:
Seth Alves 2016-03-05 09:54:38 -08:00
commit f03ddb856b
57 changed files with 2688 additions and 634 deletions

View file

@ -258,9 +258,26 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
if (onlyEditorsAreRezzers) {
canRez = isAllowedEditor;
}
QUuid hintNodeID;
// in case this is a node that's failing to connect
// double check we don't have a node whose sockets match exactly already in the list
limitedNodeList->eachNodeBreakable([&nodeConnection, &hintNodeID](const SharedNodePointer& node){
if (node->getPublicSocket() == nodeConnection.publicSockAddr
&& node->getLocalSocket() == nodeConnection.localSockAddr) {
// we have a node that already has these exact sockets - this occurs if a node
// is unable to connect to the domain
hintNodeID = node->getUUID();
return false;
}
return true;
});
// add the new node
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection);
// add the connecting node (or re-use the matched one from eachNodeBreakable above)
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection, hintNodeID);
// set the edit rights for this user
newNode->setIsAllowedEditor(isAllowedEditor);
@ -279,28 +296,29 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
return newNode;
}
SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection) {
SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection,
QUuid nodeID) {
HifiSockAddr discoveredSocket = nodeConnection.senderSockAddr;
SharedNetworkPeer connectedPeer = _icePeers.value(nodeConnection.connectUUID);
QUuid nodeUUID;
if (connectedPeer) {
// this user negotiated a connection with us via ICE, so re-use their ICE client ID
nodeUUID = nodeConnection.connectUUID;
nodeID = nodeConnection.connectUUID;
if (connectedPeer->getActiveSocket()) {
// set their discovered socket to whatever the activated socket on the network peer object was
discoveredSocket = *connectedPeer->getActiveSocket();
}
} else {
// we got a connectUUID we didn't recognize, just add the node with a new UUID
nodeUUID = QUuid::createUuid();
// we got a connectUUID we didn't recognize, either use the hinted node ID or randomly generate a new one
if (nodeID.isNull()) {
nodeID = QUuid::createUuid();
}
}
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
SharedNodePointer newNode = limitedNodeList->addOrUpdateNode(nodeUUID, nodeConnection.nodeType,
SharedNodePointer newNode = limitedNodeList->addOrUpdateNode(nodeID, nodeConnection.nodeType,
nodeConnection.publicSockAddr, nodeConnection.localSockAddr);
// So that we can send messages to this node at will - we need to activate the correct socket on this node now

View file

@ -59,7 +59,8 @@ private:
SharedNodePointer processAgentConnectRequest(const NodeConnectionData& nodeConnection,
const QString& username,
const QByteArray& usernameSignature);
SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection);
SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection,
QUuid nodeID = QUuid());
bool verifyUserSignature(const QString& username, const QByteArray& usernameSignature,
const HifiSockAddr& senderSockAddr);

View file

@ -0,0 +1,339 @@
//
// lightningEntity.js
// examples/entityScripts
//
// Created by Brad Hefta-Gaub on 3/1/16.
// Copyright 2016 High Fidelity, Inc.
//
// This is an example of an entity script which will randomly create a flash of lightning and a thunder sound
// effect, as well as a background rain sound effect. It can be applied to any entity, although it works best
// on a zone entity.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */
(function () {
"use strict";
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Various configurable settings.
//
// You can change these values to change some of the various effects of the rain storm.
// These values can also be controlled by setting user properties on the entity that you've attached this script to.
// add a "lightning" section to a JSON encoded portion of the user data... for example:
// {
// "lightning": {
// "flashMax": 20,
// "flashMin": 0,
// "flashMaxRandomness": 10,
// "flashIntensityStepRandomeness": 2,
// "averageLighteningStrikeGap": 120,
// "extraRandomRangeLightningStrikeGap": 10,
// "thunderURL": "atp:1336efe995398f5e0d46b37585785de8ba872fe9a9b718264db03748cd41c758.wav",
// "thunderVolume": 0.1,
// "rainURL": "atp:e0cc7438aca776636f6e6f731685781d9999b961c945e4e5760d937be5beecdd.wav",
// "rainVolume": 0.05
// }
// // NOTE: you can have other user data here as well, so long as it's JSON encoded, it won't impact the lightning script
// }
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////
var MAX_FLASH_INTENSITY = 20; // this controls how bright the lightning effect appears
var MIN_FLASH_INTENSITY = 0; // this is probably best at 0, but it could be higher, which will make the lightning not fade completely to darkness before going away.
var MAX_FLASH_INTENSITY_RANDOMNESS = 10; // this will add some randomness to the max brightness of the lightning
var FLASH_INTENSITY_STEP_RANDOMNESS = 2; // as the lightning goes from min to max back to min, this will make it more random in it's brightness
var AVERAGE_LIGHTNING_STRIKE_GAP_IN_SECONDS = 120; // how long on average between lighting
var EXTRA_RANDOM_RANGE_LIGHTNING_STRIKE_GAP_IN_SECONDS = 10; // some randomness to the lightning gap
var THUNDER_SOUND_URL = "https://s3.amazonaws.com/hifi-public/brad/rainstorm/thunder-48k.wav"; // thunder sound effect, must be 48k 16bit PCM
var THUNDER_VOLUME = 1; // adjust the volume of the thunder sound effect
var RAIN_SOUND_URL = "https://s3.amazonaws.com/hifi-public/brad/rainstorm/rain.wav"; // the background rain, this will loop
var RAIN_VOLUME = 0.05; // adjust the volume of the rain sound effect.
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Various constants and variables we need access to in all scopes
////////////////////////////////////////////////////////////////////////////////////////////////////////////
var MSECS_PER_SECOND = 1000; // number of milliseconds in a second, a universal truth, don't change this. :)
// This is a little trick for implementing entity scripts. Many of the entity callbacks will have the JavaScript
// "this" variable set, but if you connect to a signal like update, "this" won't correctly point to the instance
// of your entity script, and so we set "_this" and use it in cases we need to access "this". We need to define
// the variable in this scope so that it's not share among all entities.
var _this; // this is important here... or else the _this will be globally scoped.
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Various helper functions...
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Helper function for returning either a value, or the default value if the value is undefined. This is
// is handing in parsing JSON where you don't know if the values have been set or not.
function valueOrDefault(value, defaultValue) {
if (value !== undefined) {
return value;
}
return defaultValue;
}
// return a random float between high and low
function randFloat(low, high) {
return low + Math.random() * (high - low);
}
// the "constructor" for our class. pretty simple, it just sets our _this, so we can access it later.
function Lightning() {
_this = this;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// The class prototype
//
// This is the class definition/prototype for our class. Funtions declared here will be accessible
// via the instance of the entity
//
Lightning.prototype = {
// preload()
// This is called by every viewer/interface instance that "sees" the entity you've attached this script to.
// If this is a zone entity, then it will surely be called whenever someone enters the zone.
preload: function (entityID) {
// we always make a point of remember our entityID, so that we can access our entity later
_this.entityID = entityID;
// set up some of our time related state
var now = Date.now();
_this.lastUpdate = now;
_this.lastStrike = now;
// some of our other state related items
_this.lightningID = false; // this will be the entityID for any lightning that we create
_this.lightningActive = false; // are we actively managing lightning
// Get the entities userData property, to see if someone has overridden any of our default settings
var userDataText = Entities.getEntityProperties(entityID, ["userData"]).userData;
var userData = {};
if (userDataText !== "") {
userData = JSON.parse(userDataText);
}
var lightningUserData = valueOrDefault(userData.lightning, {});
_this.flashIntensityStepRandomeness = valueOrDefault(lightningUserData.flashIntensityStepRandomness, FLASH_INTENSITY_STEP_RANDOMNESS);
_this.flashMax = valueOrDefault(lightningUserData.flashMax, MAX_FLASH_INTENSITY);
_this.flashMin = valueOrDefault(lightningUserData.flashMin, MIN_FLASH_INTENSITY);
_this.flashMaxRandomness = valueOrDefault(lightningUserData.flashMaxRandomness, MAX_FLASH_INTENSITY_RANDOMNESS);
_this.averageLightningStrikeGap = valueOrDefault(lightningUserData.averageLightningStrikeGap, AVERAGE_LIGHTNING_STRIKE_GAP_IN_SECONDS);
_this.extraRandomRangeLightningStrikeGap = valueOrDefault(lightningUserData.extraRandomRangeLightningStrikeGap, EXTRA_RANDOM_RANGE_LIGHTNING_STRIKE_GAP_IN_SECONDS);
var thunderURL = valueOrDefault(lightningUserData.thunderURL, THUNDER_SOUND_URL);
_this.thunderSound = SoundCache.getSound(thunderURL); // start downloading the thunder into the cache in case we need it later
_this.thunderVolume = valueOrDefault(lightningUserData.thunderVolume, THUNDER_VOLUME);
var rainURL = valueOrDefault(lightningUserData.rainURL, RAIN_SOUND_URL);
_this.rainSound = SoundCache.getSound(rainURL); // start downloading the rain, we will be using it for sure
_this.rainVolume = valueOrDefault(lightningUserData.rainVolume, RAIN_VOLUME);
_this.rainPlaying = false;
Script.update.connect(_this.onUpdate); // connect our onUpdate to a regular update signal from the interface
},
// unload()
// This is called by every viewer/interface instance that "sees" the entity when you "stop knowing about" the
// entity. Usually this means the user has left then domain, or maybe the entity has been deleted. We need to
// clean up anything transient that we created here. In this case it means disconnecting from the update signal
// and stopping the rain sound if we started it.
unload: function ( /*entityID*/ ) {
Script.update.disconnect(_this.onUpdate);
if (_this.rainInjector !== undefined) {
_this.rainInjector.stop();
}
},
// plays the rain sound effect. This is played locally, which means it doesn't provide spatialization, which
// we don't really want for the ambient sound effect of rain. Also, since it's playing locally, we don't send
// the stream to the audio mixer. Another subtle side effect of playing locally is that the sound isn't in sync
// for all users in the domain, but that's ok for a sound effect like rain which is more of a white noise like
// sound effect.
playLocalRain: function () {
var myPosition = Entities.getEntityProperties(_this.entityID, "position").position;
_this.rainInjector = Audio.playSound(_this.rainSound, {
position: myPosition,
volume: _this.rainVolume,
loop: true,
localOnly: true
});
_this.rainPlaying = true;
},
// this handles a single "step" of the lightning flash effect. It assumes a light entity has already been created,
// and all it really does is change the intensity of the light based on the settings that are part of this entity's
// userData.
flashLightning: function (lightningID) {
var lightningProperties = Entities.getEntityProperties(lightningID, ["userData", "intensity"]);
var lightningParameters = JSON.parse(lightningProperties.userData);
var currentIntensity = lightningProperties.intensity;
var flashDirection = lightningParameters.flashDirection;
var flashMax = lightningParameters.flashMax;
var flashMin = lightningParameters.flashMin;
var flashIntensityStepRandomeness = lightningParameters.flashIntensityStepRandomeness;
var newIntensity = currentIntensity + flashDirection + randFloat(-flashIntensityStepRandomeness, flashIntensityStepRandomeness);
if (flashDirection > 0) {
if (newIntensity >= flashMax) {
flashDirection = -1; // reverse flash
newIntensity = flashMax;
}
} else {
if (newIntensity <= flashMin) {
flashDirection = 1; // reverse flash
newIntensity = flashMin;
}
}
// if we reached 0 intensity, then we're done with this strike...
if (newIntensity === 0) {
_this.lightningActive = false;
Entities.deleteEntity(lightningID);
}
// FIXME - we probably don't need to re-edit the userData of the light... we're only
// changing direction, the rest are the same... we could just store direction in our
// own local variable state
var newLightningParameters = JSON.stringify({
flashDirection: flashDirection,
flashIntensityStepRandomeness: flashIntensityStepRandomeness,
flashMax: flashMax,
flashMin: flashMin
});
// this is what actually creates the effect, changing the intensity of the light
Entities.editEntity(lightningID, {intensity: newIntensity, userData: newLightningParameters});
},
// findMyLightning() is designed to make the script more robust. Since we're an open editable platform
// it's possible that from the time that we started the lightning effect until "now" when we're attempting
// to change the light, some other client might have deleted our light. Before we proceed in editing
// the light, we check to see if it exists.
findMyLightning: function () {
if (_this.lightningID !== false) {
var lightningName = Entities.getEntityProperties(_this.lightningID, "name").name;
if (lightningName !== undefined) {
return _this.lightningID;
}
}
return false;
},
// findOtherLightning() is designed to allow this script to work in a "multi-user" environment, which we
// must assume we are in. Since every user/viewer/client that connect to the domain and "sees" our entity
// is going to run this script, any of them could be in charge of flashing the lightning. So before we
// start to flash the lightning, we will first check to see if someone else already is.
//
// returns true if some other lightning exists... likely because some other viewer is flashing it
// returns false if no other lightning exists...
findOtherLightning: function () {
var myPosition = Entities.getEntityProperties(_this.entityID, "position").position;
// find all entities near me...
var entities = Entities.findEntities(myPosition, 1000);
var checkEntityID;
var checkProperties;
var entity;
for (entity = 0; entity < entities.length; entity++) {
checkEntityID = entities[entity];
checkProperties = Entities.getEntityProperties(checkEntityID, ["name", "type"]);
// check to see if they are lightning
if (checkProperties.type === "Light" && checkProperties.name === "lightning for creator:" + _this.entityID) {
return true;
}
}
return false;
},
// createNewLightning() actually creates new lightning and plays the thunder sound
createNewLightning: function () {
var myPosition = Entities.getEntityProperties(_this.entityID, "position").position;
_this.lightningID = Entities.addEntity({
type: "Light",
name: "lightning for creator:" + _this.entityID,
userData: JSON.stringify({
flashDirection: 1,
flashIntensityStepRandomeness: _this.flashIntensityStepRandomeness,
flashMax: _this.flashMax + randFloat(-_this.flashMaxRandomness, _this.flashMaxRandomness),
flashMin: _this.flashMin
}),
falloffRadius: 1000,
intensity: 0,
position: myPosition,
collisionless: true,
dimensions: {x: 1000, y: 1000, z: 1000},
color: {red: 255, green: 255, blue: 255},
lifetime: 10 // lightning only lasts 10 seconds....
});
// play the thunder...
Audio.playSound(_this.thunderSound, {
position: myPosition,
volume: _this.thunderVolume
});
return _this.lightningID;
},
// onUpdate() this will be called regularly, approximately every frame of the simulation. We will use
// it to determine if we need to do a lightning/thunder strike
onUpdate: function () {
var now = Date.now();
// check to see if rain is downloaded and not yet playing, if so start playing
if (!_this.rainPlaying && _this.rainSound.downloaded) {
_this.playLocalRain();
}
// NOTE: _this.lightningActive will only be TRUE if we are the one who created
// the lightning and we are in charge of flashing it...
if (_this.lightningActive) {
var lightningID = _this.findMyLightning();
// if for some reason our lightning is gone... then just return to non-active state
if (lightningID === false) {
_this.lightningActive = false;
_this.lightningID = false;
} else {
// otherwise, flash our lightning...
_this.flashLightning(lightningID);
}
} else {
// whether or not it's time for us to strike, we always keep an eye out for anyone else
// striking... and if we see someone else striking, we will reset our lastStrike time
if (_this.findOtherLightning()) {
_this.lastStrike = now;
}
var sinceLastStrike = now - _this.lastStrike;
var nextRandomStrikeTime = _this.averageLightningStrikeGap
+ randFloat(-_this.extraRandomRangeLightningStrikeGap,
_this.extraRandomRangeLightningStrikeGap);
if (sinceLastStrike > nextRandomStrikeTime * MSECS_PER_SECOND) {
// so it's time for a strike... let's see if someone else has lightning...
// if no one else is flashing lightning... then we create it...
if (_this.findOtherLightning()) {
_this.lightningActive = false;
_this.lightningID = false;
} else {
_this.createNewLightning();
_this.lightningActive = true;
}
_this.lastStrike = now;
}
}
}
};
return new Lightning();
});

View file

@ -0,0 +1,125 @@
// Created by inigo quilez - iq/2013
// Turbulence and Day/Night cycle added by Michael Olson - OMGparticles/2015
// rain effect adapted from Rainy London by David Hoskins. - https://www.shadertoy.com/view/XdSGDc
// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
#line 6
const float PI = 3.14159;
uniform float rotationSpeed = 0.005;
uniform float gridLevel = 0.0;
uniform float constellationLevel = 0.0;
uniform float constellationBoundaryLevel = 0.0;
// Axial tilt
const float axialTilt = 0.408407;
const vec2 atsc = vec2(sin(axialTilt), cos(axialTilt));
const mat3 axialTiltMatrix = mat3(
vec3(atsc.y, -atsc.x, 0.0),
vec3(atsc.x, atsc.y, 0.0),
vec3(0.0, 0.0, 1.0));
vec2 directionToSpherical(in vec3 d) {
return vec2(atan(d.x, -d.z), asin(d.y)) * -1.0f;
}
vec2 directionToUv(in vec3 d) {
vec2 uv = directionToSpherical(d);
uv /= PI;
uv.x /= 2.0;
uv += 0.5;
return uv;
}
vec3 stars3(in vec3 d) {
//return rd;
vec3 dir = d * axialTiltMatrix;
float theta = rotationSpeed * iGlobalTime * 4.0;
vec2 sc = vec2(sin(theta), cos(theta));
dir = dir * mat3( vec3(sc.y, 0.0, sc.x),
vec3(0.0, 1.0, 0.0),
vec3(-sc.x, 0.0, sc.y));
vec2 uv = directionToUv(dir);
vec3 starColor = texture2D( iChannel0, uv).xyz;
starColor = pow(starColor, vec3(0.75));
if (gridLevel > 0.0) {
starColor += texture2D( iChannel1, uv).xyz * gridLevel;
}
return starColor;
}
//const vec3 vNightColor = vec3(.15, 0.3, 0.6);
uniform vec3 uDayColor = vec3(0.9,0.7,0.7);
const vec3 vNightColor = vec3(1.0, 1.0, 1.0);
const vec3 vHorizonColor = vec3(0.6, 0.3, 0.4);
const vec3 vSunColor = vec3(1.0,0.8,0.6);
const vec3 vSunRimColor = vec3(1.0,0.66,0.33);
vec4 render( in vec3 ro, in vec3 rd )
{
float fSunSpeed = (rotationSpeed * 10.0 * iGlobalTime) + PI / 2.0 * 3.0;
vec3 sundir = normalize( vec3(cos(fSunSpeed),sin(fSunSpeed),0.0) );
sundir = sundir * axialTiltMatrix;
float sun = clamp( dot(sundir,rd), 0.0, 1.0 );
float fSunHeight = sundir.y;
// below this height will be full night color
float fNightHeight = -0.8;
// above this height will be full day color
float fDayHeight = 0.3;
float fHorizonLength = fDayHeight - fNightHeight;
float fInverseHL = 1.0 / fHorizonLength;
float fHalfHorizonLength = fHorizonLength / 2.0;
float fInverseHHL = 1.0 / fHalfHorizonLength;
float fMidPoint = fNightHeight + fHalfHorizonLength;
float fNightContrib = clamp((fSunHeight - fMidPoint) * (-fInverseHHL), 0.0, 1.0);
float fHorizonContrib = -clamp(abs((fSunHeight - fMidPoint) * (-fInverseHHL)), 0.0, 1.0) + 1.0;
float fDayContrib = clamp((fSunHeight - fMidPoint) * ( fInverseHHL), 0.0, 1.0);
// sky color
vec3 vSkyColor = vec3(0.0);
vSkyColor += mix(vec3(0.0), vNightColor, fNightContrib); // Night
vSkyColor += mix(vec3(0.0), vHorizonColor, fHorizonContrib); // Horizon
vSkyColor += mix(vec3(0.0), uDayColor, fDayContrib); // Day
vec3 col = vSkyColor;
// atmosphere brighter near horizon
col -= clamp(rd.y, 0.0, 0.5);
// draw sun
col += 0.4 * vSunRimColor * pow( sun, 4.0 );
col += 1.0 * vSunColor * pow( sun, 2000.0 );
// stars
float fStarContrib = clamp((fSunHeight - fDayHeight) * (-fInverseHL), 0.0, 1.0);
if (fStarContrib > 0.0) {
vec3 vStarDir = rd;
col = mix(col, stars3(vStarDir), fStarContrib);
}
// Ten layers of rain sheets...
float rainBrightness = 0.15;
vec2 q = vec2(atan(rd.x, -rd.z), asin(rd.y));
float dis = 1;
int sheets = 12;
for (int i = 0; i < sheets; i++) {
float f = pow(dis, .45) + 0.25;
vec2 st = f * (q * vec2(2.0, .05) + vec2(-iGlobalTime*.01 + q.y * .05, iGlobalTime * 0.12));
f = texture2D(iChannel2, st * .5, -59.0).x;
f += texture2D(iChannel2, st*.284, -99.0).y;
f = clamp(pow(abs(f)*.5, 29.0) * 140.0, 0.00, q.y*.4+.05);
vec3 bri = vec3(rainBrightness);
col += bri*f;
dis += 3.5;
}
return vec4(clamp(col, 0.0, 1.0), 1.0);
}
vec3 getSkyboxColor() {
return render( vec3(0.0), normalize(_normal) ).rgb;
}

View file

@ -46,7 +46,7 @@ function emitter(jointName) {
x:0,
y: 0,
z: 0,
w: Math.PI
w: 1
},
emitRadiusStart: 0,
polarStart: 0,
@ -84,7 +84,7 @@ function emitter(jointName) {
alpha: 1,
alphaSpread: 0,
alphaStart: 1,
alphaFinish: 1
alphaFinish: 1
});
return newEmitter;
}

View file

@ -0,0 +1,689 @@
//
// flappyAvatars.js
// examples/toybox
//
// Created by Clement 3/2/16
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
// Constants
var TRIGGER_CONTROLS = [
Controller.Standard.LT,
Controller.Standard.RT,
];
var G = 4.0;
Number.prototype.clamp = function(min, max) {
return Math.min(Math.max(this, min), max);
};
var entityManager = new EntityManager();
// Class definitions
function Avatar(DEFAULT_X, DEFAULT_Y, rotation, to3DPosition) {
var DIMENSION = 0.15;
var JUMP_VELOCITY = 1.0;
var xPosition = DEFAULT_X;
var color = { red: 0, green: 0, blue: 255 };
var dimensionsSet = false;
var dimensions = { x: DIMENSION, y: DIMENSION, z: DIMENSION };
var yPosition = DEFAULT_Y;
var yVelocity = 0.0;
var yAcceleration = -G;
var airSwipeSound = SoundCache.getSound("https://s3-us-west-1.amazonaws.com/hifi-content/clement/production/Air Swipe 05.wav");
var injector = null;
this.position = function() {
return { x: xPosition, y: yPosition };
}
this.dimensions = function() {
return dimensions;
}
var id = entityManager.add({
type: "Model",
modelURL: MyAvatar.skeletonModelURL,
animation: {
url: "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/fly.fbx",
running: true,
fps: 30,
firstFrame: 1.0,
lastFrame: 80.0,
currentFrame: 1.0,
loop: true,
hold: false
},
position: to3DPosition(this.position()),
rotation: rotation,
dimensions: dimensions
});
this.changeModel = function(modelURL) {
dimensionsSet = false;
dimensions = { x: 0.10, y: 0.10, z: 0.01 };
Entities.editEntity(id, {
modelURL: modelURL,
rotation: Quat.multiply(rotation, Quat.fromPitchYawRollDegrees(0, -90, 0)),
dimensions: dimensions,
animation: {running: false}
});
airSwipeSound = SoundCache.getSound("https://s3-us-west-1.amazonaws.com/hifi-content/clement/production/8bit Jump 03.wav");
injector = null;
}
this.jump = function() {
yVelocity = JUMP_VELOCITY;
if (airSwipeSound.downloaded && !injector) {
injector = Audio.playSound(airSwipeSound, { position: to3DPosition(this.position()), volume: 0.05 });
} else if (injector) {
injector.restart();
}
}
this.update = function(deltaTime) {
if (!dimensionsSet) {
var properties = Entities.getEntityProperties(id, ["naturalDimensions"]);
var naturalDimensions = properties.naturalDimensions;
if (naturalDimensions.x != 1 || naturalDimensions.y != 1 || naturalDimensions.z != 1) {
var max = Math.max(naturalDimensions.x, Math.max(naturalDimensions.y, naturalDimensions.z));
dimensions.x = naturalDimensions.x / max * dimensions.x;
dimensions.y = naturalDimensions.y / max * dimensions.y;
dimensions.z = naturalDimensions.z / max * dimensions.z;
dimensionsSet = true;
Entities.editEntity(id, {
dimensions: dimensions
});
}
} else {
dimensions = Entities.getEntityProperties(id, ["dimensions"]).dimensions;
}
yPosition += deltaTime * (yVelocity + deltaTime * yAcceleration / 2.0);
yVelocity += deltaTime * yAcceleration;
}
this.draw = function() {
Entities.editEntity(id, {
position: to3DPosition(this.position())
});
}
this.reset = function() {
yPosition = DEFAULT_Y;
yVelocity = 0.0;
}
}
function Coin(xPosition, yPosition, to3DPosition) {
var velocity = 0.4;
var dimensions = { x: 0.0625, y: 0.0625, z: 0.0088 };
this.position = function() {
return { x: xPosition, y: yPosition };
}
var id = entityManager.add({
type: "Model",
modelURL: "https://s3-us-west-1.amazonaws.com/hifi-content/clement/production/coin.fbx",
angularVelocity: { x: 0, y: 20, z: 0 },
position: to3DPosition(this.position()),
dimensions:dimensions
});
this.update = function(deltaTime) {
xPosition -= deltaTime * velocity;
}
this.isColliding = function(avatar) {
var deltaX = Math.abs(this.position().x - avatar.position().x);
var deltaY = Math.abs(this.position().Y - avatar.position().Y);
if (deltaX < (avatar.dimensions().x + dimensions.x) / 2.0 &&
deltaX < (avatar.dimensions().y + dimensions.y) / 2.0) {
return true;
}
return false;
}
this.draw = function() {
Entities.editEntity(id, { position: to3DPosition({ x: xPosition, y: yPosition }) });
}
this.clear = function() {
entityManager.remove(id);
}
}
function Pipe(xPosition, yPosition, height, gap, to3DPosition) {
var velocity = 0.4;
var width = 0.1;
this.position = function() {
return xPosition;
}
var upHeight = yPosition - (height + gap);
var upYPosition = height + gap + upHeight / 2.0;
var idUp = entityManager.add({
type: "Model",
modelURL: "https://s3-us-west-1.amazonaws.com/hifi-content/clement/production/greenPipe.fbx",
rotation: Quat.fromPitchYawRollDegrees(180, 0, 0),
position: to3DPosition({ x: xPosition, y: upYPosition }),
dimensions: { x: width, y: upHeight, z: width }
});
var idDown = entityManager.add({
type: "Model",
modelURL: "https://s3-us-west-1.amazonaws.com/hifi-content/clement/production/greenPipe.fbx",
position: to3DPosition({ x: xPosition, y: height / 2.0 }),
dimensions: { x: width, y: height, z: width }
});
this.update = function(deltaTime) {
xPosition -= deltaTime * velocity;
}
this.isColliding = function(avatar) {
var deltaX = Math.abs(this.position() - avatar.position().x);
if (deltaX < (avatar.dimensions().z + width) / 2.0) {
var factor = 0.8;
var upDistance = (yPosition - upHeight) - (avatar.position().y + avatar.dimensions().y * factor);
var downDistance = (avatar.position().y - avatar.dimensions().y * factor) - height;
if (upDistance <= 0 || downDistance <= 0) {
return true;
}
}
return false;
}
this.draw = function() {
Entities.editEntity(idUp, { position: to3DPosition({ x: xPosition, y: upYPosition }) });
Entities.editEntity(idDown, { position: to3DPosition({ x: xPosition, y: height / 2.0 }) });
}
this.clear = function() {
entityManager.remove(idUp);
entityManager.remove(idDown);
}
}
function Pipes(newPipesPosition, newPipesHeight, to3DPosition, moveScore) {
var lastPipe = 0;
var pipesInterval = 2.0;
var pipes = new Array();
var coins = new Array();
var coinsSound = SoundCache.getSound("https://s3-us-west-1.amazonaws.com/hifi-content/clement/production/Coin.wav");
var injector = null;
this.update = function(deltaTime, gameTime, startedPlaying) {
// Move pipes forward
pipes.forEach(function(element) {
element.update(deltaTime);
});
// Move coins forward
coins.forEach(function(element) {
element.update(deltaTime);
});
// Delete pipes over the end
var count = 0;
while(count < pipes.length && pipes[count].position() <= 0.0) {
pipes[count].clear();
count++;
}
if (count > 0) {
pipes = pipes.splice(count);
}
// Delete coins over the end
count = 0;
while(count < coins.length && coins[count].position() <= 0.0) {
coins[count].clear();
count++;
}
if (count > 0) {
coins = coins.splice(count);
}
// Make new pipes and coins
if (startedPlaying && gameTime - lastPipe > pipesInterval) {
var min = 0.4;
var max = 0.7;
var height = Math.random() * (max - min) + min;
pipes.push(new Pipe(newPipesPosition, newPipesHeight, height, 0.5, to3DPosition));
coins.push(new Coin(newPipesPosition, height + 0.5 / 2.0, to3DPosition));
lastPipe = gameTime;
}
}
this.isColliding = function(avatar) {
// Check coin collection
var collected = -1;
coins.forEach(function(element, index) {
if (element.isColliding(avatar)) {
element.clear();
collected = index;
moveScore(1);
if (coinsSound.downloaded && !injector) {
injector = Audio.playSound(coinsSound, { position: to3DPosition({ x: newPipesPosition, y: newPipesHeight }), volume: 0.1 });
} else if (injector) {
injector.restart();
}
}
});
if (collected > -1) {
coins.splice(collected, 1);
}
// Check collisions
var isColliding = false;
pipes.forEach(function(element) {
isColliding |= element.isColliding(avatar);
});
return isColliding;
}
this.draw = function() {
// Drawing pipes
pipes.forEach(function(element) {
element.draw();
});
// Drawing coins
coins.forEach(function(element) {
element.draw();
});
}
this.clear = function() {
// Clearing pipes
pipes.forEach(function(element) {
element.clear();
});
pipes = new Array();
// Clearing coins
coins.forEach(function(element) {
element.clear();
});
coins = new Array();
}
}
function Score(space, bestScore) {
var score = 0;
var highScore = bestScore;
var topOffset = Vec3.multiplyQbyV(space.orientation, { x: -0.1, y: 0.2, z: -0.2 });
var topLeft = Vec3.sum(space.position, topOffset);
var bottomOffset = Vec3.multiplyQbyV(space.orientation, { x: -0.1, y: 0.0, z: -0.2 });
var bottomLeft = Vec3.sum(space.position, bottomOffset);
var numberDimensions = { x: 0.0660, y: 0.1050, z: 0.0048 };
function numberUrl(number) {
return "https://s3-us-west-1.amazonaws.com/hifi-content/clement/production/" + number + ".fbx"
}
function digitPosition(digit) {
return Vec3.multiplyQbyV(space.orientation, { x: 0.3778 + digit * (numberDimensions.x + 0.01), y: 0.0, z: 0.0 });
}
this.score = function() {
return score;
}
this.highScore = function() {
return highScore;
}
var numDigits = 3;
var bestId = entityManager.add({
type: "Model",
modelURL: "https://s3-us-west-1.amazonaws.com/hifi-content/clement/production/best.fbx",
position: topLeft,
rotation: Quat.multiply(space.orientation, Quat.fromPitchYawRollDegrees(90, 0, 0)),
dimensions: { x: 0.2781, y: 0.0063, z: 0.1037 }
});
var bestDigitsId = []
for (var i = 0; i < numDigits; i++) {
bestDigitsId[i] = entityManager.add({
type: "Model",
modelURL: numberUrl(0),
position: Vec3.sum(topLeft, digitPosition(i)),
rotation: space.orientation,
dimensions: numberDimensions
});
}
var scoreId = entityManager.add({
type: "Model",
modelURL: "https://s3-us-west-1.amazonaws.com/hifi-content/clement/production/score.fbx",
position: bottomLeft,
rotation: Quat.multiply(space.orientation, Quat.fromPitchYawRollDegrees(90, 0, 0)),
dimensions: { x: 0.3678, y: 0.0063, z: 0.1037 }
});
var scoreDigitsId = []
for (var i = 0; i < numDigits; i++) {
scoreDigitsId[i] = entityManager.add({
type: "Model",
modelURL: numberUrl(0),
position: Vec3.sum(bottomLeft, digitPosition(i)),
rotation: space.orientation,
dimensions: numberDimensions
});
}
this.moveScore = function(delta) {
score += delta;
if (score > highScore) {
highScore = score;
}
}
this.resetScore = function() {
score = 0;
}
this.draw = function() {
for (var i = 0; i < numDigits; i++) {
Entities.editEntity(bestDigitsId[i], { modelURL: numberUrl(Math.floor((highScore / Math.pow(10, numDigits- i - 1)) % 10)) });
}
for (var i = 0; i < numDigits; i++) {
Entities.editEntity(scoreDigitsId[i], { modelURL: numberUrl(Math.floor(score / Math.pow(10, numDigits - i - 1)) % 10) });
}
}
}
function Game(bestScore) {
// public methods
this.start = function() {
if (!isRunning) {
isRunning = true;
setup();
}
}
this.stop = function() {
if (isRunning) {
cleanup();
isRunning = false;
}
}
// Game loop setup
var timestamp = 0;
this.idle = function(triggerValue) {
var now = Date.now();
var deltaTime = (now - timestamp) / 1000.0;
if (timestamp === 0) {
deltaTime = 0;
}
gameTime += deltaTime;
inputs(triggerValue);
update(deltaTime);
draw();
timestamp = now;
}
// Constants
var spaceDimensions = { x: 2.0, y: 1.5, z: 0.01 };
var spaceDistance = 1.5;
var spaceYOffset = 0.6;
// Private game state
var that = this;
var isRunning = false;
var startedPlaying = false;
var coolDown = 1.5;
var lastLost = -coolDown;
var gameTime = 0;
var isJumping = false;
var lastJumpValue = 0.0;
var lastTriggerValue = 0.0;
var TRIGGER_THRESHOLD = 0.9;
var space = null;
var avatar = null;
var pipes = null;
var score = null;
var gameOverSound = SoundCache.getSound("https://s3-us-west-1.amazonaws.com/hifi-content/clement/production/Game Over.wav");
var injector = null;
var directions = ["UP", "DOWN", "LEFT", "RIGHT"];
var sequence = [directions[0], directions[0], directions[1], directions[1], directions[2], directions[3], directions[2], directions[3], "b", "a"];
var current = 0;
function keyPress(event) {
if (event.text === sequence[current]) {
++current;
} else {
current = 0;
}
if (current === sequence.length) {
avatar.changeModel("https://s3-us-west-1.amazonaws.com/hifi-content/clement/production/mario.fbx");
current = 0;
}
}
var isBoardReset = true;
function moveScore(delta) {
score.moveScore(delta);
}
this.score = function() {
return score.score();
}
this.highScore = function() {
return score.highScore();
}
function setup() {
space = {
position: getSpacePosition(),
orientation: getSpaceOrientation(),
dimensions: getSpaceDimensions()
}
var rotation = Quat.multiply(space.orientation, Quat.fromPitchYawRollDegrees(0, 90, 0));
avatar = new Avatar(space.dimensions.x / 2.0, space.dimensions.y / 2.0, rotation, to3DPosition);
pipes = new Pipes(space.dimensions.x, space.dimensions.y, to3DPosition, moveScore);
score = new Score(space, bestScore);
Controller.keyPressEvent.connect(keyPress);
}
function inputs(triggerValue) {
if (!startedPlaying && !isBoardReset && (gameTime - lastLost) > coolDown) {
score.resetScore();
avatar.reset();
pipes.clear();
isBoardReset = true;
}
if (triggerValue > TRIGGER_THRESHOLD &&
lastTriggerValue < TRIGGER_THRESHOLD &&
(gameTime - lastLost) > coolDown) {
isJumping = true;
startedPlaying = true;
}
lastTriggerValue = triggerValue;
}
function update(deltaTime) {
// Keep entities alive
entityManager.update(deltaTime);
if (!startedPlaying && (gameTime - lastLost) < coolDown && !isBoardReset) {
return;
}
// Update Avatar
if (!startedPlaying && avatar.position().y < spaceDimensions.y / 2.0) {
isJumping = true;
}
// Apply jumps
if (isJumping) {
avatar.jump();
isJumping = false;
}
avatar.update(deltaTime);
pipes.update(deltaTime, gameTime, startedPlaying);
// Check lost
var hasLost = avatar.position().y < 0.0 ||
avatar.position().y > space.dimensions.y ||
pipes.isColliding(avatar);
// Cleanup
if (hasLost) {
if (gameOverSound.downloaded && !injector) {
injector = Audio.playSound(gameOverSound, { position: space.position, volume: 0.4 });
} else if (injector) {
injector.restart();
}
isBoardReset = false;
startedPlaying = false;
lastLost = gameTime;
}
}
function draw() {
avatar.draw();
pipes.draw();
score.draw();
}
function cleanup() {
entityManager.removeAll();
Controller.keyPressEvent.disconnect(keyPress);
}
// Private methods
function getSpacePosition() {
var forward = Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.FRONT);
var spacePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(spaceDistance, forward));
return Vec3.sum(spacePosition, Vec3.multiply(spaceYOffset, Vec3.UP));
}
function getSpaceOrientation() {
return MyAvatar.orientation;
}
function getSpaceDimensions() {
return spaceDimensions;
}
function to3DPosition(position) {
var position2D = {
x: position.x - space.dimensions.x / 2.0,
y: position.y - space.dimensions.y / 2.0,
z: 0.0
}
return Vec3.sum(space.position, Vec3.multiplyQbyV(space.orientation, position2D));
}
}
function EntityManager() {
var OBJECTS_LIFETIME = 1;
var entities = new Array();
var lifetime = OBJECTS_LIFETIME;
this.setLifetime = function(newLifetime) {
lifetime = newLifetime;
this.update();
}
this.add = function(properties) {
// Add to scene
properties.lifetime = lifetime;
var entityID = Entities.addEntity(properties);
// Add to array
entities.push({ id: entityID, properties: properties });
return entityID;
}
this.update = function(deltaTime) {
entities.forEach(function(element) {
// Get entity's age
var properties = Entities.getEntityProperties(element.id, ["age"]);
// Update entity's lifetime
Entities.editEntity(element.id, { lifetime: properties.age + lifetime });
});
}
this.remove = function(entityID) {
// Remove from scene
Entities.deleteEntity(entityID);
// Remove from array
entities = entities.filter(function(element) {
return element.id !== entityID;
});
}
this.removeAll = function() {
// Remove all from scene
entities.forEach(function(element) {
Entities.deleteEntity(element.id);
});
// Remove all from array
entities = new Array();
}
}
PartableGame = function() {
this.entityID = null;
this.equipped = false;
this.triggerValue = 0.0;
this.hand = 0;
this.game = null;
};
PartableGame.prototype = {
preload: function(entityID) {
this.entityID = entityID;
},
unload: function() {
},
startEquip: function(id, params) {
this.equipped = true;
this.hand = params[0] == "left" ? 0 : 1;
var bestScore = 0;
var properties = Entities.getEntityProperties(this.entityID, ["userData"]);
var userData = JSON.parse(properties.userData);
if (userData.highScore) {
bestScore = userData.highScore;
}
this.game = new Game(bestScore);
this.game.start();
},
releaseEquip: function(id, params) {
this.equipped = false;
var properties = Entities.getEntityProperties(this.entityID, ["userData"]);
var userData = JSON.parse(properties.userData);
userData.highScore = this.game.highScore();
properties.userData = JSON.stringify(userData);
Entities.editEntity(this.entityID, properties);
this.game.stop();
delete this.game;
},
continueEquip: function(id, params) {
if (!this.equipped) {
return;
}
this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[this.hand]);
this.game.idle(this.triggerValue);
},
};
// entity scripts always need to return a newly constructed object of our type
return new PartableGame();
});

View file

@ -0,0 +1,34 @@
{
"Entities": [
{
"collisionsWillMove": 1,
"created": "2016-03-03T19:00:10Z",
"dimensions": {
"x": 0.11497055739164352,
"y": 0.11497056484222412,
"z": 0.11497056484222412
},
"dynamic": 1,
"id": "{ee5b25e6-aca2-4dc7-9462-51537d89c126}",
"modelURL": "https://s3-us-west-1.amazonaws.com/hifi-content/clement/production/cube.fbx",
"queryAACube": {
"scale": 0.5974045991897583,
"x": -5.1575918197631836,
"y": 23.078603744506836,
"z": 16.521066665649414
},
"rotation": {
"w": 0.92288088798522949,
"x": -0.10148775577545166,
"y": -0.13279926776885986,
"z": 0.34688329696655273
},
"script": "https://raw.githubusercontent.com/Atlante45/hifi/feat/hackaton/examples/toybox/flappyAvatars/flappyAvatars.js",
"scriptTimestamp": 1457031937425,
"shapeType": "box",
"type": "Model",
"userData": "{\"wearable\":{\"joints\":{\"RightHand\":[{\"x\":0.07079616189002991,\"y\":0.20177987217903137,\"z\":0.06374628841876984},{\"x\":-0.5863648653030396,\"y\":-0.46007341146469116,\"z\":0.46949487924575806,\"w\":-0.4733745753765106}],\"LeftHand\":[{\"x\":-0.018704339861869812,\"y\":0.20499876141548157,\"z\":0.08445858210325241},{\"x\":0.2061777561903,\"y\":-0.6629757881164551,\"z\":0.5865269303321838,\"w\":0.41706138849258423}]}},\"grabbableKey\":{\"invertSolidWhileHeld\":true},\"resetMe\":{\"resetMe\":true},\"highScore\":0}"
}
],
"Version": 57
}

View file

@ -0,0 +1,27 @@
{
"Entities": [
{
"backgroundMode": "skybox",
"dimensions": {
"x": 10000,
"y": 10000,
"z": 10000
},
"name": "Rainy Day/Night Cycle",
"rotation": {
"w": 1,
"x": 0,
"y": 0,
"z": 0
},
"script": "https://s3.amazonaws.com/hifi-public/brad/rainstorm/lightningEntity.js",
"shapeType": "box",
"skybox": {
"url": "https://hifi-public.s3.amazonaws.com/images/SkyboxTextures/CloudyDay1.jpg"
},
"type": "Zone",
"userData":"{\n\"ProceduralEntity\":{\n\"version\":2,\n\"shaderUrl\":\"https://s3.amazonaws.com/hifi-public/brad/rainstorm/rainyDayNightSkybox.fs\",\n\"channels\":[\n\"https://hifi-public.s3.amazonaws.com/austin/assets/images/skybox/starmap_8k.jpg\",\n\"https://hifi-public.s3.amazonaws.com/austin/assets/images/skybox/celestial_grid.jpg\",\n\"https://s3.amazonaws.com/hifi-public/brad/rainstorm/noise.jpg\",\n\"https://s3.amazonaws.com/hifi-public/brad/noise.jpg\"\n],\n\"uniforms\":{\n\"rotationSpeed\":0.001,\n\"uDayColor\":[0.4,0.3,0.3],\n\"constellationLevel\":0.0,\n\"constellationBoundaryLevel\":0.00,\n\"gridLevel\":0.0\n}\n},\n\"lightning\":{\n\"flashMax\":20,\n\"flashMin\":0,\n\"flashIntensityStepRandomeness\":2,\n\"flashMaxRandomness\":10,\n\"averageLightningStrikeGap\":120,\n\"extraRandomRangeLightningStrikeGap\":5,\n\"thunderURL\":\"https://s3.amazonaws.com/hifi-public/brad/rainstorm/thunder-48k.wav\",\n\"thunderVolume\":0.1,\n\"rainURL\":\"https://s3.amazonaws.com/hifi-public/brad/rainstorm/rain.wav\",\n\"rainVolume\":0.05\n}\n}"
}
],
"Version": 57
}

View file

@ -1097,7 +1097,8 @@ void MyAvatar::prepareForPhysicsSimulation() {
_characterController.setTargetVelocity(getTargetVelocity());
_characterController.setPositionAndOrientation(getPosition(), getOrientation());
if (qApp->isHMDMode()) {
_follow.prePhysicsUpdate(*this, deriveBodyFromHMDSensor(), _bodySensorMatrix);
bool hasDriveInput = fabsf(_driveKeys[TRANSLATE_X]) > 0.0f || fabsf(_driveKeys[TRANSLATE_Z]) > 0.0f;
_follow.prePhysicsUpdate(*this, deriveBodyFromHMDSensor(), _bodySensorMatrix, hasDriveInput);
} else {
_follow.deactivate();
}
@ -1339,11 +1340,11 @@ void MyAvatar::preRender(RenderArgs* renderArgs) {
_prevShouldDrawHead = shouldDrawHead;
}
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.6f;
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.3f;
bool MyAvatar::cameraInsideHead() const {
const glm::vec3 cameraPosition = qApp->getCamera()->getPosition();
return glm::length(cameraPosition - getDefaultEyePosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getUniformScale());
return glm::length(cameraPosition - getHeadPosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getUniformScale());
}
bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const {
@ -1884,58 +1885,119 @@ void MyAvatar::lateUpdatePalms() {
static const float FOLLOW_TIME = 0.5f;
void MyAvatar::FollowHelper::deactivate() {
_timeRemaining = 0.0f;
MyAvatar::FollowHelper::FollowHelper() {
deactivate();
}
void MyAvatar::FollowHelper::activate() {
void MyAvatar::FollowHelper::deactivate() {
for (int i = 0; i < NumFollowTypes; i++) {
deactivate((FollowType)i);
}
}
void MyAvatar::FollowHelper::deactivate(FollowType type) {
assert(type >= 0 && type < NumFollowTypes);
_timeRemaining[(int)type] = 0.0f;
}
void MyAvatar::FollowHelper::activate(FollowType type) {
assert(type >= 0 && type < NumFollowTypes);
// TODO: Perhaps, the follow time should be proportional to the displacement.
_timeRemaining = FOLLOW_TIME;
_timeRemaining[(int)type] = FOLLOW_TIME;
}
bool MyAvatar::FollowHelper::isActive(FollowType type) const {
assert(type >= 0 && type < NumFollowTypes);
return _timeRemaining[(int)type] > 0.0f;
}
bool MyAvatar::FollowHelper::isActive() const {
return _timeRemaining > 0.0f;
for (int i = 0; i < NumFollowTypes; i++) {
if (isActive((FollowType)i)) {
return true;
}
}
return false;
}
bool MyAvatar::FollowHelper::shouldActivate(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 4.0f);
glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix);
if (glm::dot(myAvatar.getHMDSensorFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD) {
return true;
float MyAvatar::FollowHelper::getMaxTimeRemaining() const {
float max = 0.0f;
for (int i = 0; i < NumFollowTypes; i++) {
if (_timeRemaining[i] > max) {
max = _timeRemaining[i];
}
}
return max;
}
const float CYLINDER_TOP = 0.1f;
const float CYLINDER_BOTTOM = -0.5f;
const float CYLINDER_RADIUS = 0.15f;
void MyAvatar::FollowHelper::decrementTimeRemaining(float dt) {
for (int i = 0; i < NumFollowTypes; i++) {
_timeRemaining[i] -= dt;
}
}
bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees
glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix);
return glm::dot(myAvatar.getHMDSensorFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD;
}
bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
const float CYLINDER_RADIUS = 0.3f;
glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix);
glm::vec3 radialOffset(offset.x, 0.0f, offset.z);
float radialDistance = glm::length(radialOffset);
return (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM) || (radialDistance > CYLINDER_RADIUS);
return radialDistance > CYLINDER_RADIUS;
}
void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) {
bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
const float CYLINDER_TOP = 0.1f;
const float CYLINDER_BOTTOM = -1.5f;
glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix);
return (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM);
}
void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput) {
_desiredBodyMatrix = desiredBodyMatrix;
if (!isActive() && shouldActivate(myAvatar, desiredBodyMatrix, currentBodyMatrix)) {
activate();
if (!isActive(Rotation) && shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix)) {
activate(Rotation);
}
if (!isActive(Horizontal) && shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix)) {
activate(Horizontal);
}
if (!isActive(Vertical) && (shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
activate(Vertical);
}
if (isActive()) {
glm::mat4 desiredWorldMatrix = myAvatar.getSensorToWorldMatrix() * _desiredBodyMatrix;
myAvatar.getCharacterController()->setFollowParameters(desiredWorldMatrix, _timeRemaining);
} else {
glm::mat4 currentWorldMatrix = myAvatar.getSensorToWorldMatrix() * currentBodyMatrix;
myAvatar.getCharacterController()->setFollowParameters(currentWorldMatrix, 0.0f);
glm::mat4 desiredWorldMatrix = myAvatar.getSensorToWorldMatrix() * _desiredBodyMatrix;
glm::mat4 currentWorldMatrix = myAvatar.getSensorToWorldMatrix() * currentBodyMatrix;
AnimPose followWorldPose(currentWorldMatrix);
if (isActive(Rotation)) {
followWorldPose.rot = glmExtractRotation(desiredWorldMatrix);
}
if (isActive(Horizontal)) {
glm::vec3 desiredTranslation = extractTranslation(desiredWorldMatrix);
followWorldPose.trans.x = desiredTranslation.x;
followWorldPose.trans.z = desiredTranslation.z;
}
if (isActive(Vertical)) {
glm::vec3 desiredTranslation = extractTranslation(desiredWorldMatrix);
followWorldPose.trans.y = desiredTranslation.y;
}
myAvatar.getCharacterController()->setFollowParameters(followWorldPose, getMaxTimeRemaining());
}
glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix) {
if (isActive()) {
float dt = myAvatar.getCharacterController()->getFollowTime();
_timeRemaining -= dt;
decrementTimeRemaining(dt);
// apply follow displacement to the body matrix.
glm::vec3 worldLinearDisplacement = myAvatar.getCharacterController()->getFollowLinearDisplacement();

View file

@ -392,14 +392,29 @@ private:
glm::mat4 _sensorToWorldMatrix;
struct FollowHelper {
FollowHelper();
enum FollowType {
Rotation = 0,
Horizontal,
Vertical,
NumFollowTypes
};
glm::mat4 _desiredBodyMatrix;
float _timeRemaining { 0.0f };
float _timeRemaining[NumFollowTypes];
void deactivate();
void deactivate(FollowType type);
void activate();
void activate(FollowType type);
bool isActive() const;
bool shouldActivate(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
void prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& bodySensorMatrix, const glm::mat4& currentBodyMatrix);
bool isActive(FollowType followType) const;
float getMaxTimeRemaining() const;
void decrementTimeRemaining(float dt);
bool shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
bool shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
bool shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const;
void prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& bodySensorMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput);
glm::mat4 postPhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix);
};
FollowHelper _follow;

View file

@ -11,8 +11,6 @@
#include "Base3DOverlay.h"
#include <QScriptValue>
#include <RegisteredMetaTypes.h>
#include <SharedUtil.h>
@ -41,103 +39,78 @@ Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
{
}
void Base3DOverlay::setProperties(const QScriptValue& properties) {
void Base3DOverlay::setProperties(const QVariantMap& properties) {
Overlay::setProperties(properties);
QScriptValue drawInFront = properties.property("drawInFront");
auto drawInFront = properties["drawInFront"];
if (drawInFront.isValid()) {
bool value = drawInFront.toVariant().toBool();
bool value = drawInFront.toBool();
setDrawInFront(value);
}
QScriptValue position = properties.property("position");
auto position = properties["position"];
// if "position" property was not there, check to see if they included aliases: point, p1
if (!position.isValid()) {
position = properties.property("p1");
position = properties["p1"];
if (!position.isValid()) {
position = properties.property("point");
position = properties["point"];
}
}
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();
setPosition(newPosition);
}
setPosition(vec3FromVariant(position));
}
if (properties.property("lineWidth").isValid()) {
setLineWidth(properties.property("lineWidth").toVariant().toFloat());
if (properties["lineWidth"].isValid()) {
setLineWidth(properties["lineWidth"].toFloat());
}
QScriptValue rotation = properties.property("rotation");
auto rotation = properties["rotation"];
if (rotation.isValid()) {
glm::quat newRotation;
// size, scale, dimensions is special, it might just be a single scalar, or it might be a vector, check that here
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()) {
newRotation.x = x.toVariant().toFloat();
newRotation.y = y.toVariant().toFloat();
newRotation.z = z.toVariant().toFloat();
newRotation.w = w.toVariant().toFloat();
setRotation(newRotation);
}
setRotation(quatFromVariant(rotation));
}
if (properties.property("isSolid").isValid()) {
setIsSolid(properties.property("isSolid").toVariant().toBool());
if (properties["isSolid"].isValid()) {
setIsSolid(properties["isSolid"].toBool());
}
if (properties.property("isFilled").isValid()) {
setIsSolid(properties.property("isSolid").toVariant().toBool());
if (properties["isFilled"].isValid()) {
setIsSolid(properties["isSolid"].toBool());
}
if (properties.property("isWire").isValid()) {
setIsSolid(!properties.property("isWire").toVariant().toBool());
if (properties["isWire"].isValid()) {
setIsSolid(!properties["isWire"].toBool());
}
if (properties.property("solid").isValid()) {
setIsSolid(properties.property("solid").toVariant().toBool());
if (properties["solid"].isValid()) {
setIsSolid(properties["solid"].toBool());
}
if (properties.property("filled").isValid()) {
setIsSolid(properties.property("filled").toVariant().toBool());
if (properties["filled"].isValid()) {
setIsSolid(properties["filled"].toBool());
}
if (properties.property("wire").isValid()) {
setIsSolid(!properties.property("wire").toVariant().toBool());
if (properties["wire"].isValid()) {
setIsSolid(!properties["wire"].toBool());
}
if (properties.property("isDashedLine").isValid()) {
setIsDashedLine(properties.property("isDashedLine").toVariant().toBool());
if (properties["isDashedLine"].isValid()) {
setIsDashedLine(properties["isDashedLine"].toBool());
}
if (properties.property("dashed").isValid()) {
setIsDashedLine(properties.property("dashed").toVariant().toBool());
if (properties["dashed"].isValid()) {
setIsDashedLine(properties["dashed"].toBool());
}
if (properties.property("ignoreRayIntersection").isValid()) {
setIgnoreRayIntersection(properties.property("ignoreRayIntersection").toVariant().toBool());
if (properties["ignoreRayIntersection"].isValid()) {
setIgnoreRayIntersection(properties["ignoreRayIntersection"].toBool());
}
}
QScriptValue Base3DOverlay::getProperty(const QString& property) {
QVariant Base3DOverlay::getProperty(const QString& property) {
if (property == "position" || property == "start" || property == "p1" || property == "point") {
return vec3toScriptValue(_scriptEngine, getPosition());
return vec3toVariant(getPosition());
}
if (property == "lineWidth") {
return _lineWidth;
}
if (property == "rotation") {
return quatToScriptValue(_scriptEngine, getRotation());
return quatToVariant(getRotation());
}
if (property == "isSolid" || property == "isFilled" || property == "solid" || property == "filed") {
return _isSolid;

View file

@ -52,8 +52,8 @@ public:
virtual AABox getBounds() const = 0;
virtual void setProperties(const QScriptValue& properties);
virtual QScriptValue getProperty(const QString& property);
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, glm::vec3& surfaceNormal);

View file

@ -18,19 +18,19 @@ Billboard3DOverlay::Billboard3DOverlay(const Billboard3DOverlay* billboard3DOver
{
}
void Billboard3DOverlay::setProperties(const QScriptValue &properties) {
void Billboard3DOverlay::setProperties(const QVariantMap& properties) {
Planar3DOverlay::setProperties(properties);
PanelAttachable::setProperties(properties);
Billboardable::setProperties(properties);
}
QScriptValue Billboard3DOverlay::getProperty(const QString &property) {
QScriptValue value;
value = Billboardable::getProperty(_scriptEngine, property);
QVariant Billboard3DOverlay::getProperty(const QString &property) {
QVariant value;
value = Billboardable::getProperty(property);
if (value.isValid()) {
return value;
}
value = PanelAttachable::getProperty(_scriptEngine, property);
value = PanelAttachable::getProperty(property);
if (value.isValid()) {
return value;
}

View file

@ -23,8 +23,8 @@ public:
Billboard3DOverlay() {}
Billboard3DOverlay(const Billboard3DOverlay* billboard3DOverlay);
virtual void setProperties(const QScriptValue& properties);
virtual QScriptValue getProperty(const QString& property);
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
protected:
virtual void applyTransformTo(Transform& transform, bool force = false);

View file

@ -14,18 +14,18 @@
#include <Application.h>
#include <Transform.h>
void Billboardable::setProperties(const QScriptValue &properties) {
QScriptValue isFacingAvatar = properties.property("isFacingAvatar");
void Billboardable::setProperties(const QVariantMap& properties) {
auto isFacingAvatar = properties["isFacingAvatar"];
if (isFacingAvatar.isValid()) {
setIsFacingAvatar(isFacingAvatar.toVariant().toBool());
setIsFacingAvatar(isFacingAvatar.toBool());
}
}
QScriptValue Billboardable::getProperty(QScriptEngine* scriptEngine, const QString &property) {
QVariant Billboardable::getProperty(const QString &property) {
if (property == "isFacingAvatar") {
return isFacingAvatar();
}
return QScriptValue();
return QVariant();
}
void Billboardable::pointTransformAtCamera(Transform& transform, glm::quat offsetRotation) {

View file

@ -12,11 +12,9 @@
#ifndef hifi_Billboardable_h
#define hifi_Billboardable_h
#include <QScriptValue>
#include <glm/gtc/quaternion.hpp>
#include <QVariant>
class QScriptEngine;
class QString;
class Transform;
@ -26,8 +24,8 @@ public:
void setIsFacingAvatar(bool isFacingAvatar) { _isFacingAvatar = isFacingAvatar; }
protected:
void setProperties(const QScriptValue& properties);
QScriptValue getProperty(QScriptEngine* scriptEngine, const QString& property);
void setProperties(const QVariantMap& properties);
QVariant getProperty(const QString& property);
void pointTransformAtCamera(Transform& transform, glm::quat offsetRotation = {1, 0, 0, 0});

View file

@ -286,83 +286,76 @@ const render::ShapeKey Circle3DOverlay::getShapeKey() {
return builder.build();
}
void Circle3DOverlay::setProperties(const QScriptValue &properties) {
void Circle3DOverlay::setProperties(const QVariantMap& properties) {
Planar3DOverlay::setProperties(properties);
QScriptValue startAt = properties.property("startAt");
QVariant startAt = properties["startAt"];
if (startAt.isValid()) {
setStartAt(startAt.toVariant().toFloat());
setStartAt(startAt.toFloat());
}
QScriptValue endAt = properties.property("endAt");
QVariant endAt = properties["endAt"];
if (endAt.isValid()) {
setEndAt(endAt.toVariant().toFloat());
setEndAt(endAt.toFloat());
}
QScriptValue outerRadius = properties.property("radius");
QVariant outerRadius = properties["radius"];
if (!outerRadius.isValid()) {
outerRadius = properties.property("outerRadius");
outerRadius = properties["outerRadius"];
}
if (outerRadius.isValid()) {
setOuterRadius(outerRadius.toVariant().toFloat());
setOuterRadius(outerRadius.toFloat());
}
QScriptValue innerRadius = properties.property("innerRadius");
QVariant innerRadius = properties["innerRadius"];
if (innerRadius.isValid()) {
setInnerRadius(innerRadius.toVariant().toFloat());
setInnerRadius(innerRadius.toFloat());
}
QScriptValue hasTickMarks = properties.property("hasTickMarks");
QVariant hasTickMarks = properties["hasTickMarks"];
if (hasTickMarks.isValid()) {
setHasTickMarks(hasTickMarks.toVariant().toBool());
setHasTickMarks(hasTickMarks.toBool());
}
QScriptValue majorTickMarksAngle = properties.property("majorTickMarksAngle");
QVariant majorTickMarksAngle = properties["majorTickMarksAngle"];
if (majorTickMarksAngle.isValid()) {
setMajorTickMarksAngle(majorTickMarksAngle.toVariant().toFloat());
setMajorTickMarksAngle(majorTickMarksAngle.toFloat());
}
QScriptValue minorTickMarksAngle = properties.property("minorTickMarksAngle");
QVariant minorTickMarksAngle = properties["minorTickMarksAngle"];
if (minorTickMarksAngle.isValid()) {
setMinorTickMarksAngle(minorTickMarksAngle.toVariant().toFloat());
setMinorTickMarksAngle(minorTickMarksAngle.toFloat());
}
QScriptValue majorTickMarksLength = properties.property("majorTickMarksLength");
QVariant majorTickMarksLength = properties["majorTickMarksLength"];
if (majorTickMarksLength.isValid()) {
setMajorTickMarksLength(majorTickMarksLength.toVariant().toFloat());
setMajorTickMarksLength(majorTickMarksLength.toFloat());
}
QScriptValue minorTickMarksLength = properties.property("minorTickMarksLength");
QVariant minorTickMarksLength = properties["minorTickMarksLength"];
if (minorTickMarksLength.isValid()) {
setMinorTickMarksLength(minorTickMarksLength.toVariant().toFloat());
setMinorTickMarksLength(minorTickMarksLength.toFloat());
}
QScriptValue majorTickMarksColor = properties.property("majorTickMarksColor");
bool valid;
auto majorTickMarksColor = properties["majorTickMarksColor"];
if (majorTickMarksColor.isValid()) {
QScriptValue red = majorTickMarksColor.property("red");
QScriptValue green = majorTickMarksColor.property("green");
QScriptValue blue = majorTickMarksColor.property("blue");
if (red.isValid() && green.isValid() && blue.isValid()) {
_majorTickMarksColor.red = red.toVariant().toInt();
_majorTickMarksColor.green = green.toVariant().toInt();
_majorTickMarksColor.blue = blue.toVariant().toInt();
auto color = xColorFromVariant(majorTickMarksColor, valid);
if (valid) {
_majorTickMarksColor = color;
}
}
QScriptValue minorTickMarksColor = properties.property("minorTickMarksColor");
auto minorTickMarksColor = properties["minorTickMarksColor"];
if (minorTickMarksColor.isValid()) {
QScriptValue red = minorTickMarksColor.property("red");
QScriptValue green = minorTickMarksColor.property("green");
QScriptValue blue = minorTickMarksColor.property("blue");
if (red.isValid() && green.isValid() && blue.isValid()) {
_minorTickMarksColor.red = red.toVariant().toInt();
_minorTickMarksColor.green = green.toVariant().toInt();
_minorTickMarksColor.blue = blue.toVariant().toInt();
auto color = xColorFromVariant(majorTickMarksColor, valid);
if (valid) {
_minorTickMarksColor = color;
}
}
}
QScriptValue Circle3DOverlay::getProperty(const QString& property) {
QVariant Circle3DOverlay::getProperty(const QString& property) {
if (property == "startAt") {
return _startAt;
}
@ -394,10 +387,10 @@ QScriptValue Circle3DOverlay::getProperty(const QString& property) {
return _minorTickMarksLength;
}
if (property == "majorTickMarksColor") {
return xColorToScriptValue(_scriptEngine, _majorTickMarksColor);
return xColorToVariant(_majorTickMarksColor);
}
if (property == "minorTickMarksColor") {
return xColorToScriptValue(_scriptEngine, _minorTickMarksColor);
return xColorToVariant(_minorTickMarksColor);
}
return Planar3DOverlay::getProperty(property);

View file

@ -26,8 +26,8 @@ public:
virtual void render(RenderArgs* args);
virtual const render::ShapeKey getShapeKey() override;
virtual void setProperties(const QScriptValue& properties);
virtual QScriptValue getProperty(const QString& property);
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
float getStartAt() const { return _startAt; }
float getEndAt() const { return _endAt; }

View file

@ -11,8 +11,6 @@
// include this before QGLWidget, which includes an earlier version of OpenGL
#include "Cube3DOverlay.h"
#include <QScriptValue>
#include <SharedUtil.h>
#include <StreamUtils.h>
#include <GeometryCache.h>
@ -110,18 +108,18 @@ Cube3DOverlay* Cube3DOverlay::createClone() const {
return new Cube3DOverlay(this);
}
void Cube3DOverlay::setProperties(const QScriptValue& properties) {
void Cube3DOverlay::setProperties(const QVariantMap& properties) {
Volume3DOverlay::setProperties(properties);
QScriptValue borderSize = properties.property("borderSize");
auto borderSize = properties["borderSize"];
if (borderSize.isValid()) {
float value = borderSize.toVariant().toFloat();
float value = borderSize.toFloat();
setBorderSize(value);
}
}
QScriptValue Cube3DOverlay::getProperty(const QString& property) {
QVariant Cube3DOverlay::getProperty(const QString& property) {
if (property == "borderSize") {
return _borderSize;
}

View file

@ -32,8 +32,8 @@ public:
void setBorderSize(float value) { _borderSize = value; }
virtual void setProperties(const QScriptValue& properties);
virtual QScriptValue getProperty(const QString& property);
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
private:
float _borderSize;

View file

@ -11,10 +11,7 @@
#include "Grid3DOverlay.h"
#include <QScriptValue>
#include <OctreeConstants.h>
#include <DependencyManager.h>
#include <GeometryCache.h>
#include <PathUtils.h>
@ -92,24 +89,24 @@ const render::ShapeKey Grid3DOverlay::getShapeKey() {
return render::ShapeKey::Builder().withOwnPipeline();
}
void Grid3DOverlay::setProperties(const QScriptValue& properties) {
void Grid3DOverlay::setProperties(const QVariantMap& properties) {
Planar3DOverlay::setProperties(properties);
if (properties.property("followCamera").isValid()) {
_followCamera = properties.property("followCamera").toVariant().toBool();
if (properties["followCamera"].isValid()) {
_followCamera = properties["followCamera"].toBool();
}
if (properties.property("majorGridEvery").isValid()) {
_majorGridEvery = properties.property("majorGridEvery").toVariant().toInt();
if (properties["majorGridEvery"].isValid()) {
_majorGridEvery = properties["majorGridEvery"].toInt();
}
if (properties.property("minorGridEvery").isValid()) {
_minorGridEvery = properties.property("minorGridEvery").toVariant().toFloat();
if (properties["minorGridEvery"].isValid()) {
_minorGridEvery = properties["minorGridEvery"].toFloat();
}
updateGrid();
}
QScriptValue Grid3DOverlay::getProperty(const QString& property) {
QVariant Grid3DOverlay::getProperty(const QString& property) {
if (property == "followCamera") {
return _followCamera;
}

View file

@ -28,8 +28,8 @@ public:
virtual void render(RenderArgs* args);
virtual const render::ShapeKey getShapeKey() override;
virtual void setProperties(const QScriptValue& properties);
virtual QScriptValue getProperty(const QString& property);
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
virtual Grid3DOverlay* createClone() const;

View file

@ -12,8 +12,6 @@
#include "Image3DOverlay.h"
#include <QScriptValue>
#include <DependencyManager.h>
#include <GeometryCache.h>
#include <gpu/Batch.h>
@ -114,41 +112,42 @@ const render::ShapeKey Image3DOverlay::getShapeKey() {
return builder.build();
}
void Image3DOverlay::setProperties(const QScriptValue &properties) {
void Image3DOverlay::setProperties(const QVariantMap& properties) {
Billboard3DOverlay::setProperties(properties);
QScriptValue urlValue = properties.property("url");
auto urlValue = properties["url"];
if (urlValue.isValid()) {
QString newURL = urlValue.toVariant().toString();
QString newURL = urlValue.toString();
if (newURL != _url) {
setURL(newURL);
}
}
QScriptValue subImageBounds = properties.property("subImage");
if (subImageBounds.isValid()) {
if (subImageBounds.isNull()) {
auto subImageBoundsVar = properties["subImage"];
if (subImageBoundsVar.isValid()) {
if (subImageBoundsVar.isNull()) {
_fromImage = QRect();
} else {
QRect oldSubImageRect = _fromImage;
QRect subImageRect = _fromImage;
if (subImageBounds.property("x").isValid()) {
subImageRect.setX(subImageBounds.property("x").toVariant().toInt());
auto subImageBounds = subImageBoundsVar.toMap();
if (subImageBounds["x"].isValid()) {
subImageRect.setX(subImageBounds["x"].toInt());
} else {
subImageRect.setX(oldSubImageRect.x());
}
if (subImageBounds.property("y").isValid()) {
subImageRect.setY(subImageBounds.property("y").toVariant().toInt());
if (subImageBounds["y"].isValid()) {
subImageRect.setY(subImageBounds["y"].toInt());
} else {
subImageRect.setY(oldSubImageRect.y());
}
if (subImageBounds.property("width").isValid()) {
subImageRect.setWidth(subImageBounds.property("width").toVariant().toInt());
if (subImageBounds["width"].isValid()) {
subImageRect.setWidth(subImageBounds["width"].toInt());
} else {
subImageRect.setWidth(oldSubImageRect.width());
}
if (subImageBounds.property("height").isValid()) {
subImageRect.setHeight(subImageBounds.property("height").toVariant().toInt());
if (subImageBounds["height"].isValid()) {
subImageRect.setHeight(subImageBounds["height"].toInt());
} else {
subImageRect.setHeight(oldSubImageRect.height());
}
@ -156,21 +155,21 @@ void Image3DOverlay::setProperties(const QScriptValue &properties) {
}
}
QScriptValue emissiveValue = properties.property("emissive");
auto emissiveValue = properties["emissive"];
if (emissiveValue.isValid()) {
_emissive = emissiveValue.toBool();
}
}
QScriptValue Image3DOverlay::getProperty(const QString& property) {
QVariant Image3DOverlay::getProperty(const QString& property) {
if (property == "url") {
return _url;
}
if (property == "subImage") {
return qRectToScriptValue(_scriptEngine, _fromImage);
return _fromImage;
}
if (property == "offsetPosition") {
return vec3toScriptValue(_scriptEngine, getOffsetPosition());
return vec3toVariant(getOffsetPosition());
}
if (property == "emissive") {
return _emissive;

View file

@ -37,8 +37,8 @@ public:
void setURL(const QString& url);
void setClipFromSource(const QRect& bounds) { _fromImage = bounds; }
virtual void setProperties(const QScriptValue& properties);
virtual QScriptValue getProperty(const QString& property);
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, glm::vec3& surfaceNormal);

View file

@ -71,49 +71,34 @@ const render::ShapeKey Line3DOverlay::getShapeKey() {
return builder.build();
}
void Line3DOverlay::setProperties(const QScriptValue& properties) {
void Line3DOverlay::setProperties(const QVariantMap& properties) {
Base3DOverlay::setProperties(properties);
QScriptValue start = properties.property("start");
auto start = properties["start"];
// if "start" property was not there, check to see if they included aliases: startPoint
if (!start.isValid()) {
start = properties.property("startPoint");
start = properties["startPoint"];
}
if (start.isValid()) {
QScriptValue x = start.property("x");
QScriptValue y = start.property("y");
QScriptValue z = start.property("z");
if (x.isValid() && y.isValid() && z.isValid()) {
glm::vec3 newStart;
newStart.x = x.toVariant().toFloat();
newStart.y = y.toVariant().toFloat();
newStart.z = z.toVariant().toFloat();
setStart(newStart);
}
setStart(vec3FromVariant(start));
}
QScriptValue end = properties.property("end");
auto end = properties["end"];
// if "end" property was not there, check to see if they included aliases: endPoint
if (!end.isValid()) {
end = properties.property("endPoint");
end = properties["endPoint"];
}
if (end.isValid()) {
QScriptValue x = end.property("x");
QScriptValue y = end.property("y");
QScriptValue z = end.property("z");
if (x.isValid() && y.isValid() && z.isValid()) {
glm::vec3 newEnd;
newEnd.x = x.toVariant().toFloat();
newEnd.y = y.toVariant().toFloat();
newEnd.z = z.toVariant().toFloat();
setEnd(newEnd);
}
setEnd(vec3FromVariant(end));
}
}
QScriptValue Line3DOverlay::getProperty(const QString& property) {
QVariant Line3DOverlay::getProperty(const QString& property) {
if (property == "start" || property == "startPoint" || property == "p1") {
return vec3toVariant(_start);
}
if (property == "end" || property == "endPoint" || property == "p2") {
return vec3toScriptValue(_scriptEngine, _end);
return vec3toVariant(_end);
}
return Base3DOverlay::getProperty(property);

View file

@ -35,8 +35,8 @@ public:
void setStart(const glm::vec3& start) { _start = start; }
void setEnd(const glm::vec3& end) { _end = end; }
virtual void setProperties(const QScriptValue& properties);
virtual QScriptValue getProperty(const QString& property);
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
virtual Line3DOverlay* createClone() const;

View file

@ -84,7 +84,7 @@ void ModelOverlay::render(RenderArgs* args) {
}
}
void ModelOverlay::setProperties(const QScriptValue &properties) {
void ModelOverlay::setProperties(const QVariantMap& properties) {
auto position = getPosition();
auto rotation = getRotation();
auto scale = getDimensions();
@ -105,16 +105,16 @@ void ModelOverlay::setProperties(const QScriptValue &properties) {
}
}
QScriptValue urlValue = properties.property("url");
if (urlValue.isValid() && urlValue.isString()) {
auto urlValue = properties["url"];
if (urlValue.isValid() && urlValue.canConvert<QString>()) {
_url = urlValue.toString();
_updateModel = true;
_isLoaded = false;
}
QScriptValue texturesValue = properties.property("textures");
if (texturesValue.isValid() && texturesValue.toVariant().canConvert(QVariant::Map)) {
QVariantMap textureMap = texturesValue.toVariant().toMap();
auto texturesValue = properties["textures"];
if (texturesValue.isValid() && texturesValue.canConvert(QVariant::Map)) {
QVariantMap textureMap = texturesValue.toMap();
foreach(const QString& key, textureMap.keys()) {
QUrl newTextureURL = textureMap[key].toUrl();
@ -129,22 +129,22 @@ void ModelOverlay::setProperties(const QScriptValue &properties) {
}
}
QScriptValue ModelOverlay::getProperty(const QString& property) {
QVariant ModelOverlay::getProperty(const QString& property) {
if (property == "url") {
return _url.toString();
}
if (property == "dimensions" || property == "scale" || property == "size") {
return vec3toScriptValue(_scriptEngine, _model.getScaleToFitDimensions());
return vec3toVariant(_model.getScaleToFitDimensions());
}
if (property == "textures") {
if (_modelTextures.size() > 0) {
QScriptValue textures = _scriptEngine->newObject();
QVariantMap textures;
foreach(const QString& key, _modelTextures.keys()) {
textures.setProperty(key, _modelTextures[key].toString());
textures[key] = _modelTextures[key].toString();
}
return textures;
} else {
return QScriptValue();
return QVariant();
}
}

View file

@ -27,9 +27,9 @@ public:
virtual void update(float deltatime);
virtual void render(RenderArgs* args);
virtual void setProperties(const QScriptValue& properties);
virtual QScriptValue getProperty(const QString& property);
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, glm::vec3& surfaceNormal);
virtual bool findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction,
float& distance, BoxFace& face, glm::vec3& surfaceNormal, QString& extraInfo);

View file

@ -52,70 +52,68 @@ Overlay::Overlay(const Overlay* overlay) :
_colorPulse(overlay->_colorPulse),
_color(overlay->_color),
_visible(overlay->_visible),
_anchor(overlay->_anchor),
_scriptEngine(NULL)
_anchor(overlay->_anchor)
{
}
void Overlay::init(QScriptEngine* scriptEngine) {
_scriptEngine = scriptEngine;
}
Overlay::~Overlay() {
}
void Overlay::setProperties(const QScriptValue& properties) {
QScriptValue color = properties.property("color");
xColorFromScriptValue(properties.property("color"), _color);
void Overlay::setProperties(const QVariantMap& properties) {
bool valid;
auto color = xColorFromVariant(properties["color"], valid);
if (valid) {
_color = color;
}
if (properties.property("alpha").isValid()) {
setAlpha(properties.property("alpha").toVariant().toFloat());
if (properties["alpha"].isValid()) {
setAlpha(properties["alpha"].toFloat());
}
if (properties.property("glowLevel").isValid()) {
setGlowLevel(properties.property("glowLevel").toVariant().toFloat());
if (properties["glowLevel"].isValid()) {
setGlowLevel(properties["glowLevel"].toFloat());
}
if (properties.property("pulseMax").isValid()) {
setPulseMax(properties.property("pulseMax").toVariant().toFloat());
if (properties["pulseMax"].isValid()) {
setPulseMax(properties["pulseMax"].toFloat());
}
if (properties.property("pulseMin").isValid()) {
setPulseMin(properties.property("pulseMin").toVariant().toFloat());
if (properties["pulseMin"].isValid()) {
setPulseMin(properties["pulseMin"].toFloat());
}
if (properties.property("pulsePeriod").isValid()) {
setPulsePeriod(properties.property("pulsePeriod").toVariant().toFloat());
if (properties["pulsePeriod"].isValid()) {
setPulsePeriod(properties["pulsePeriod"].toFloat());
}
if (properties.property("glowLevelPulse").isValid()) {
setGlowLevelPulse(properties.property("glowLevelPulse").toVariant().toFloat());
if (properties["glowLevelPulse"].isValid()) {
setGlowLevelPulse(properties["glowLevelPulse"].toFloat());
}
if (properties.property("alphaPulse").isValid()) {
setAlphaPulse(properties.property("alphaPulse").toVariant().toFloat());
if (properties["alphaPulse"].isValid()) {
setAlphaPulse(properties["alphaPulse"].toFloat());
}
if (properties.property("colorPulse").isValid()) {
setColorPulse(properties.property("colorPulse").toVariant().toFloat());
if (properties["colorPulse"].isValid()) {
setColorPulse(properties["colorPulse"].toFloat());
}
if (properties.property("visible").isValid()) {
bool visible = properties.property("visible").toVariant().toBool();
if (properties["visible"].isValid()) {
bool visible = properties["visible"].toBool();
setVisible(visible);
}
if (properties.property("anchor").isValid()) {
QString property = properties.property("anchor").toVariant().toString();
if (properties["anchor"].isValid()) {
QString property = properties["anchor"].toString();
if (property == "MyAvatar") {
setAnchor(MY_AVATAR);
}
}
}
QScriptValue Overlay::getProperty(const QString& property) {
QVariant Overlay::getProperty(const QString& property) {
if (property == "color") {
return xColorToScriptValue(_scriptEngine, _color);
return xColorToVariant(_color);
}
if (property == "alpha") {
return _alpha;
@ -148,7 +146,7 @@ QScriptValue Overlay::getProperty(const QString& property) {
return _anchor == MY_AVATAR ? "MyAvatar" : "";
}
return QScriptValue();
return QVariant();
}
xColor Overlay::getColor() {

View file

@ -15,9 +15,6 @@
#include <SharedUtil.h> // for xColor
#include <render/Scene.h>
class QScriptEngine;
class QScriptValue;
class Overlay : public QObject {
Q_OBJECT
@ -34,7 +31,6 @@ public:
Overlay();
Overlay(const Overlay* overlay);
~Overlay();
void init(QScriptEngine* scriptEngine);
virtual void update(float deltatime) {}
virtual void render(RenderArgs* args) = 0;
@ -82,9 +78,9 @@ public:
void setColorPulse(float value) { _colorPulse = value; }
void setAlphaPulse(float value) { _alphaPulse = value; }
virtual void setProperties(const QScriptValue& properties);
virtual void setProperties(const QVariantMap& properties);
virtual Overlay* createClone() const = 0;
virtual QScriptValue getProperty(const QString& property);
virtual QVariant getProperty(const QString& property);
render::ItemID getRenderItemID() const { return _renderItemID; }
void setRenderItemID(render::ItemID renderItemID) { _renderItemID = renderItemID; }
@ -112,8 +108,6 @@ protected:
xColor _color;
bool _visible; // should the overlay be drawn at all
Anchor _anchor;
QScriptEngine* _scriptEngine;
};
namespace render {

View file

@ -23,49 +23,44 @@ AABox Overlay2D::getBounds() const {
glm::vec3(_bounds.width(), _bounds.height(), 0.01f));
}
void Overlay2D::setProperties(const QScriptValue& properties) {
void Overlay2D::setProperties(const QVariantMap& properties) {
Overlay::setProperties(properties);
QScriptValue bounds = properties.property("bounds");
auto bounds = properties["bounds"];
if (bounds.isValid()) {
QRect boundsRect;
boundsRect.setX(bounds.property("x").toVariant().toInt());
boundsRect.setY(bounds.property("y").toVariant().toInt());
boundsRect.setWidth(bounds.property("width").toVariant().toInt());
boundsRect.setHeight(bounds.property("height").toVariant().toInt());
setBounds(boundsRect);
bool valid;
auto rect = qRectFromVariant(bounds, valid);
setBounds(rect);
} else {
QRect oldBounds = _bounds;
QRect newBounds = oldBounds;
if (properties.property("x").isValid()) {
newBounds.setX(properties.property("x").toVariant().toInt());
if (properties["x"].isValid()) {
newBounds.setX(properties["x"].toInt());
} else {
newBounds.setX(oldBounds.x());
}
if (properties.property("y").isValid()) {
newBounds.setY(properties.property("y").toVariant().toInt());
if (properties["y"].isValid()) {
newBounds.setY(properties["y"].toInt());
} else {
newBounds.setY(oldBounds.y());
}
if (properties.property("width").isValid()) {
newBounds.setWidth(properties.property("width").toVariant().toInt());
if (properties["width"].isValid()) {
newBounds.setWidth(properties["width"].toInt());
} else {
newBounds.setWidth(oldBounds.width());
}
if (properties.property("height").isValid()) {
newBounds.setHeight(properties.property("height").toVariant().toInt());
if (properties["height"].isValid()) {
newBounds.setHeight(properties["height"].toInt());
} else {
newBounds.setHeight(oldBounds.height());
}
setBounds(newBounds);
//qDebug() << "set bounds to " << getBounds();
}
}
QScriptValue Overlay2D::getProperty(const QString& property) {
QVariant Overlay2D::getProperty(const QString& property) {
if (property == "bounds") {
return qRectToScriptValue(_scriptEngine, _bounds);
return qRectToVariant(_bounds);
}
if (property == "x") {
return _bounds.x();

View file

@ -40,8 +40,8 @@ public:
void setHeight(int height) { _bounds.setHeight(height); }
void setBounds(const QRect& bounds) { _bounds = bounds; }
virtual void setProperties(const QScriptValue& properties);
virtual QScriptValue getProperty(const QString& property);
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
protected:
QRect _bounds; // where on the screen to draw

View file

@ -26,26 +26,27 @@ PropertyBinding::PropertyBinding(QString avatar, QUuid entity) :
{
}
QScriptValue propertyBindingToScriptValue(QScriptEngine* engine, const PropertyBinding& value) {
QScriptValue obj = engine->newObject();
QVariant propertyBindingToVariant(const PropertyBinding& value) {
QVariantMap obj;
if (value.avatar == "MyAvatar") {
obj.setProperty("avatar", "MyAvatar");
obj["avatar"] = "MyAvatar";
} else if (!value.entity.isNull()) {
obj.setProperty("entity", engine->newVariant(value.entity));
obj["entity"] = value.entity;
}
return obj;
}
void propertyBindingFromScriptValue(const QScriptValue& object, PropertyBinding& value) {
QScriptValue avatar = object.property("avatar");
QScriptValue entity = object.property("entity");
void propertyBindingFromVariant(const QVariant& objectVar, PropertyBinding& value) {
auto object = objectVar.toMap();
auto avatar = object["avatar"];
auto entity = object["entity"];
if (avatar.isValid() && !avatar.isNull()) {
value.avatar = avatar.toVariant().toString();
value.avatar = avatar.toString();
} else if (entity.isValid() && !entity.isNull()) {
value.entity = entity.toVariant().toUuid();
value.entity = entity.toUuid();
}
}
@ -62,103 +63,82 @@ void OverlayPanel::removeChild(unsigned int childId) {
}
}
QScriptValue OverlayPanel::getProperty(const QString &property) {
QVariant OverlayPanel::getProperty(const QString &property) {
if (property == "anchorPosition") {
return vec3toScriptValue(_scriptEngine, getAnchorPosition());
return vec3toVariant(getAnchorPosition());
}
if (property == "anchorPositionBinding") {
return propertyBindingToScriptValue(_scriptEngine,
PropertyBinding(_anchorPositionBindMyAvatar ?
return propertyBindingToVariant(PropertyBinding(_anchorPositionBindMyAvatar ?
"MyAvatar" : "",
_anchorPositionBindEntity));
}
if (property == "anchorRotation") {
return quatToScriptValue(_scriptEngine, getAnchorRotation());
return quatToVariant(getAnchorRotation());
}
if (property == "anchorRotationBinding") {
return propertyBindingToScriptValue(_scriptEngine,
PropertyBinding(_anchorRotationBindMyAvatar ?
return propertyBindingToVariant(PropertyBinding(_anchorRotationBindMyAvatar ?
"MyAvatar" : "",
_anchorRotationBindEntity));
}
if (property == "anchorScale") {
return vec3toScriptValue(_scriptEngine, getAnchorScale());
return vec3toVariant(getAnchorScale());
}
if (property == "visible") {
return getVisible();
}
if (property == "children") {
QScriptValue array = _scriptEngine->newArray(_children.length());
QVariantList array;
for (int i = 0; i < _children.length(); i++) {
array.setProperty(i, _children[i]);
array.append(_children[i]);
}
return array;
}
QScriptValue value = Billboardable::getProperty(_scriptEngine, property);
auto value = Billboardable::getProperty(property);
if (value.isValid()) {
return value;
}
return PanelAttachable::getProperty(_scriptEngine, property);
return PanelAttachable::getProperty(property);
}
void OverlayPanel::setProperties(const QScriptValue &properties) {
void OverlayPanel::setProperties(const QVariantMap& properties) {
PanelAttachable::setProperties(properties);
Billboardable::setProperties(properties);
QScriptValue anchorPosition = properties.property("anchorPosition");
if (anchorPosition.isValid() &&
anchorPosition.property("x").isValid() &&
anchorPosition.property("y").isValid() &&
anchorPosition.property("z").isValid()) {
glm::vec3 newPosition;
vec3FromScriptValue(anchorPosition, newPosition);
setAnchorPosition(newPosition);
auto anchorPosition = properties["anchorPosition"];
if (anchorPosition.isValid()) {
setAnchorPosition(vec3FromVariant(anchorPosition));
}
QScriptValue anchorPositionBinding = properties.property("anchorPositionBinding");
auto anchorPositionBinding = properties["anchorPositionBinding"];
if (anchorPositionBinding.isValid()) {
PropertyBinding binding = {};
propertyBindingFromScriptValue(anchorPositionBinding, binding);
propertyBindingFromVariant(anchorPositionBinding, binding);
_anchorPositionBindMyAvatar = binding.avatar == "MyAvatar";
_anchorPositionBindEntity = binding.entity;
}
QScriptValue anchorRotation = properties.property("anchorRotation");
if (anchorRotation.isValid() &&
anchorRotation.property("x").isValid() &&
anchorRotation.property("y").isValid() &&
anchorRotation.property("z").isValid() &&
anchorRotation.property("w").isValid()) {
glm::quat newRotation;
quatFromScriptValue(anchorRotation, newRotation);
setAnchorRotation(newRotation);
auto anchorRotation = properties["anchorRotation"];
if (anchorRotation.isValid()) {
setAnchorRotation(quatFromVariant(anchorRotation));
}
QScriptValue anchorRotationBinding = properties.property("anchorRotationBinding");
auto anchorRotationBinding = properties["anchorRotationBinding"];
if (anchorRotationBinding.isValid()) {
PropertyBinding binding = {};
propertyBindingFromScriptValue(anchorPositionBinding, binding);
propertyBindingFromVariant(anchorPositionBinding, binding);
_anchorRotationBindMyAvatar = binding.avatar == "MyAvatar";
_anchorRotationBindEntity = binding.entity;
}
QScriptValue anchorScale = properties.property("anchorScale");
auto anchorScale = properties["anchorScale"];
if (anchorScale.isValid()) {
if (anchorScale.property("x").isValid() &&
anchorScale.property("y").isValid() &&
anchorScale.property("z").isValid()) {
glm::vec3 newScale;
vec3FromScriptValue(anchorScale, newScale);
setAnchorScale(newScale);
} else {
setAnchorScale(anchorScale.toVariant().toFloat());
}
setAnchorScale(vec3FromVariant(anchorScale));
}
QScriptValue visible = properties.property("visible");
auto visible = properties["visible"];
if (visible.isValid()) {
setVisible(visible.toVariant().toBool());
setVisible(visible.toBool());
}
}

View file

@ -16,7 +16,6 @@
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <QScriptValue>
#include <QUuid>
#include "PanelAttachable.h"
@ -30,8 +29,8 @@ public:
QUuid entity;
};
QScriptValue propertyBindingToScriptValue(QScriptEngine* engine, const PropertyBinding& value);
void propertyBindingFromScriptValue(const QScriptValue& object, PropertyBinding& value);
QVariant propertyBindingToVariant(const PropertyBinding& value);
void propertyBindingFromVariant(const QVariant& object, PropertyBinding& value);
class OverlayPanel : public QObject, public PanelAttachable, public Billboardable {
@ -60,8 +59,8 @@ public:
void removeChild(unsigned int childId);
unsigned int popLastChild() { return _children.takeLast(); }
QScriptValue getProperty(const QString& property);
void setProperties(const QScriptValue& properties);
void setProperties(const QVariantMap& properties);
QVariant getProperty(const QString& property);
virtual void applyTransformTo(Transform& transform, bool force = false);

View file

@ -153,7 +153,7 @@ Overlay::Pointer Overlays::getOverlay(unsigned int id) const {
return nullptr;
}
unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) {
unsigned int Overlays::addOverlay(const QString& type, const QVariant& properties) {
Overlay::Pointer thisOverlay = nullptr;
if (type == ImageOverlay::TYPE) {
@ -187,15 +187,13 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope
}
if (thisOverlay) {
thisOverlay->setProperties(properties);
thisOverlay->setProperties(properties.toMap());
return addOverlay(thisOverlay);
}
return 0;
}
unsigned int Overlays::addOverlay(Overlay::Pointer overlay) {
overlay->init(_scriptEngine);
QWriteLocker lock(&_lock);
unsigned int thisID = _nextOverlayID;
_nextOverlayID++;
@ -228,7 +226,7 @@ unsigned int Overlays::cloneOverlay(unsigned int id) {
return 0; // Not found
}
bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) {
bool Overlays::editOverlay(unsigned int id, const QVariant& properties) {
QWriteLocker lock(&_lock);
Overlay::Pointer thisOverlay = getOverlay(id);
@ -236,7 +234,7 @@ bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) {
if (thisOverlay->is3D()) {
render::ItemKey oldItemKey = render::payloadGetKey(thisOverlay);
thisOverlay->setProperties(properties);
thisOverlay->setProperties(properties.toMap());
render::ItemKey itemKey = render::payloadGetKey(thisOverlay);
if (itemKey != oldItemKey) {
@ -249,7 +247,7 @@ bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) {
}
}
} else {
thisOverlay->setProperties(properties);
thisOverlay->setProperties(properties.toMap());
}
return true;
@ -380,36 +378,21 @@ OverlayPropertyResult Overlays::getProperty(unsigned int id, const QString& prop
return result;
}
OverlayPropertyResult::OverlayPropertyResult() :
value(QScriptValue())
{
OverlayPropertyResult::OverlayPropertyResult() {
}
QScriptValue OverlayPropertyResultToScriptValue(QScriptEngine* engine, const OverlayPropertyResult& result)
{
if (!result.value.isValid()) {
QScriptValue OverlayPropertyResultToScriptValue(QScriptEngine* engine, const OverlayPropertyResult& value) {
if (!value.value.isValid()) {
return QScriptValue::UndefinedValue;
}
QScriptValue object = engine->newObject();
if (result.value.isObject()) {
QScriptValueIterator it(result.value);
while (it.hasNext()) {
it.next();
object.setProperty(it.name(), QScriptValue(it.value().toString()));
}
} else {
object = result.value;
}
return object;
return engine->newVariant(value.value);
}
void OverlayPropertyResultFromScriptValue(const QScriptValue& value, OverlayPropertyResult& result)
{
result.value = value;
void OverlayPropertyResultFromScriptValue(const QScriptValue& object, OverlayPropertyResult& value) {
value.value = object.toVariant();
}
RayToOverlayIntersectionResult Overlays::findRayIntersection(const PickRay& ray) {
float bestDistance = std::numeric_limits<float>::max();
bool bestIsFront = false;
@ -456,7 +439,7 @@ RayToOverlayIntersectionResult::RayToOverlayIntersectionResult() :
}
QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, const RayToOverlayIntersectionResult& value) {
QScriptValue obj = engine->newObject();
auto obj = engine->newObject();
obj.setProperty("intersects", value.intersects);
obj.setProperty("overlayID", value.overlayID);
obj.setProperty("distance", value.distance);
@ -488,18 +471,19 @@ QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine,
break;
}
obj.setProperty("face", faceName);
QScriptValue intersection = vec3toScriptValue(engine, value.intersection);
auto intersection = vec3toScriptValue(engine, value.intersection);
obj.setProperty("intersection", intersection);
obj.setProperty("extraInfo", value.extraInfo);
return obj;
}
void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, RayToOverlayIntersectionResult& value) {
value.intersects = object.property("intersects").toVariant().toBool();
value.overlayID = object.property("overlayID").toVariant().toInt();
value.distance = object.property("distance").toVariant().toFloat();
void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& objectVar, RayToOverlayIntersectionResult& value) {
QVariantMap object = objectVar.toVariant().toMap();
value.intersects = object["intersects"].toBool();
value.overlayID = object["overlayID"].toInt();
value.distance = object["distance"].toFloat();
QString faceName = object.property("face").toVariant().toString();
QString faceName = object["face"].toString();
if (faceName == "MIN_X_FACE") {
value.face = MIN_X_FACE;
} else if (faceName == "MAX_X_FACE") {
@ -515,11 +499,15 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R
} else {
value.face = UNKNOWN_FACE;
};
QScriptValue intersection = object.property("intersection");
auto intersection = object["intersection"];
if (intersection.isValid()) {
vec3FromScriptValue(intersection, value.intersection);
bool valid;
auto newIntersection = vec3FromVariant(intersection, valid);
if (valid) {
value.intersection = newIntersection;
}
}
value.extraInfo = object.property("extraInfo").toVariant().toString();
value.extraInfo = object["extraInfo"].toString();
}
bool Overlays::isLoaded(unsigned int id) {
@ -556,16 +544,16 @@ unsigned int Overlays::addPanel(OverlayPanel::Pointer panel) {
return thisID;
}
unsigned int Overlays::addPanel(const QScriptValue& properties) {
unsigned int Overlays::addPanel(const QVariant& properties) {
OverlayPanel::Pointer panel = std::make_shared<OverlayPanel>();
panel->init(_scriptEngine);
panel->setProperties(properties);
panel->setProperties(properties.toMap());
return addPanel(panel);
}
void Overlays::editPanel(unsigned int panelId, const QScriptValue& properties) {
void Overlays::editPanel(unsigned int panelId, const QVariant& properties) {
if (_panels.contains(panelId)) {
_panels[panelId]->setProperties(properties);
_panels[panelId]->setProperties(properties.toMap());
}
}

View file

@ -31,7 +31,7 @@ class PickRay;
class OverlayPropertyResult {
public:
OverlayPropertyResult();
QScriptValue value;
QVariant value;
};
Q_DECLARE_METATYPE(OverlayPropertyResult);
@ -75,7 +75,7 @@ public:
public slots:
/// adds an overlay with the specific properties
unsigned int addOverlay(const QString& type, const QScriptValue& properties);
unsigned int addOverlay(const QString& type, const QVariant& properties);
/// adds an overlay that's already been created
unsigned int addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); }
@ -86,7 +86,7 @@ public slots:
/// edits an overlay updating only the included properties, will return the identified OverlayID in case of
/// successful edit, if the input id is for an unknown overlay this function will have no effect
bool editOverlay(unsigned int id, const QScriptValue& properties);
bool editOverlay(unsigned int id, const QVariant& properties);
/// deletes a particle
void deleteOverlay(unsigned int id);
@ -122,10 +122,10 @@ public slots:
unsigned int addPanel(OverlayPanel::Pointer panel);
/// creates and adds a panel based on a set of properties
unsigned int addPanel(const QScriptValue& properties);
unsigned int addPanel(const QVariant& properties);
/// edit the properties of a panel
void editPanel(unsigned int panelId, const QScriptValue& properties);
void editPanel(unsigned int panelId, const QVariant& properties);
/// get a property of a panel
OverlayPropertyResult getPanelProperty(unsigned int panelId, const QString& property);

View file

@ -8,8 +8,6 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QScriptValueIterator>
#include <limits>
#include <typeinfo>

View file

@ -23,53 +23,38 @@ bool PanelAttachable::getParentVisible() const {
}
}
QScriptValue PanelAttachable::getProperty(QScriptEngine* scriptEngine, const QString &property) {
QVariant PanelAttachable::getProperty(const QString& property) {
if (property == "offsetPosition") {
return vec3toScriptValue(scriptEngine, getOffsetPosition());
return vec3toVariant(getOffsetPosition());
}
if (property == "offsetRotation") {
return quatToScriptValue(scriptEngine, getOffsetRotation());
return quatToVariant(getOffsetRotation());
}
if (property == "offsetScale") {
return vec3toScriptValue(scriptEngine, getOffsetScale());
return vec3toVariant(getOffsetScale());
}
return QScriptValue();
return QVariant();
}
void PanelAttachable::setProperties(const QScriptValue &properties) {
QScriptValue offsetPosition = properties.property("offsetPosition");
if (offsetPosition.isValid() &&
offsetPosition.property("x").isValid() &&
offsetPosition.property("y").isValid() &&
offsetPosition.property("z").isValid()) {
glm::vec3 newPosition;
vec3FromScriptValue(offsetPosition, newPosition);
setOffsetPosition(newPosition);
}
QScriptValue offsetRotation = properties.property("offsetRotation");
if (offsetRotation.isValid() &&
offsetRotation.property("x").isValid() &&
offsetRotation.property("y").isValid() &&
offsetRotation.property("z").isValid() &&
offsetRotation.property("w").isValid()) {
glm::quat newRotation;
quatFromScriptValue(offsetRotation, newRotation);
setOffsetRotation(newRotation);
}
QScriptValue offsetScale = properties.property("offsetScale");
if (offsetScale.isValid()) {
if (offsetScale.property("x").isValid() &&
offsetScale.property("y").isValid() &&
offsetScale.property("z").isValid()) {
glm::vec3 newScale;
vec3FromScriptValue(offsetScale, newScale);
setOffsetScale(newScale);
} else {
setOffsetScale(offsetScale.toVariant().toFloat());
void PanelAttachable::setProperties(const QVariantMap& properties) {
auto offsetPosition = properties["offsetPosition"];
bool valid;
if (offsetPosition.isValid()) {
glm::vec3 newPosition = vec3FromVariant(offsetPosition, valid);
if (valid) {
setOffsetPosition(newPosition);
}
}
auto offsetRotation = properties["offsetRotation"];
if (offsetRotation.isValid()) {
setOffsetRotation(quatFromVariant(offsetRotation));
}
auto offsetScale = properties["offsetScale"];
if (offsetScale.isValid()) {
setOffsetScale(vec3FromVariant(offsetScale));
}
}
void PanelAttachable::applyTransformTo(Transform& transform, bool force) {

View file

@ -57,8 +57,8 @@ public:
void setOffsetScale(const glm::vec3& scale) { _offset.setScale(scale); }
protected:
QScriptValue getProperty(QScriptEngine* scriptEngine, const QString& property);
void setProperties(const QScriptValue& properties);
void setProperties(const QVariantMap& properties);
QVariant getProperty(const QString& property);
/// set position, rotation and scale on transform based on offsets, and parent panel offsets
/// if force is false, only apply transform if it hasn't been applied in the last .1 seconds

View file

@ -35,57 +35,27 @@ AABox Planar3DOverlay::getBounds() const {
return AABox(extents);
}
void Planar3DOverlay::setProperties(const QScriptValue& properties) {
void Planar3DOverlay::setProperties(const QVariantMap& properties) {
Base3DOverlay::setProperties(properties);
QScriptValue dimensions = properties.property("dimensions");
auto dimensions = properties["dimensions"];
// if "dimensions" property was not there, check to see if they included aliases: scale
if (!dimensions.isValid()) {
dimensions = properties.property("scale");
dimensions = properties["scale"];
if (!dimensions.isValid()) {
dimensions = properties.property("size");
dimensions = properties["size"];
}
}
if (dimensions.isValid()) {
bool validDimensions = false;
glm::vec2 newDimensions;
QScriptValue x = dimensions.property("x");
QScriptValue y = dimensions.property("y");
if (x.isValid() && y.isValid()) {
newDimensions.x = x.toVariant().toFloat();
newDimensions.y = y.toVariant().toFloat();
validDimensions = true;
} else {
QScriptValue width = dimensions.property("width");
QScriptValue height = dimensions.property("height");
if (width.isValid() && height.isValid()) {
newDimensions.x = width.toVariant().toFloat();
newDimensions.y = height.toVariant().toFloat();
validDimensions = true;
}
}
// size, scale, dimensions is special, it might just be a single scalar, check that here
if (!validDimensions && dimensions.isNumber()) {
float size = dimensions.toVariant().toFloat();
newDimensions.x = size;
newDimensions.y = size;
validDimensions = true;
}
if (validDimensions) {
setDimensions(newDimensions);
}
setDimensions(vec2FromVariant(dimensions));
}
}
QScriptValue Planar3DOverlay::getProperty(const QString& property) {
QVariant Planar3DOverlay::getProperty(const QString& property) {
if (property == "dimensions" || property == "scale" || property == "size") {
return vec2toScriptValue(_scriptEngine, getDimensions());
return vec2toVariant(getDimensions());
}
return Base3DOverlay::getProperty(property);

View file

@ -26,8 +26,8 @@ public:
void setDimensions(float value) { _dimensions = glm::vec2(value); }
void setDimensions(const glm::vec2& value) { _dimensions = value; }
virtual void setProperties(const QScriptValue& properties);
virtual QScriptValue getProperty(const QString& property);
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, glm::vec3& surfaceNormal);

View file

@ -57,7 +57,7 @@ QmlOverlay::~QmlOverlay() {
}
}
void QmlOverlay::setProperties(const QScriptValue& properties) {
void QmlOverlay::setProperties(const QVariantMap& properties) {
Overlay2D::setProperties(properties);
auto bounds = _bounds;
std::weak_ptr<QQuickItem> weakQmlElement;
@ -71,7 +71,7 @@ void QmlOverlay::setProperties(const QScriptValue& properties) {
_qmlElement->setHeight(bounds.height());
}
});
QMetaObject::invokeMethod(_qmlElement.get(), "updatePropertiesFromScript", Q_ARG(QVariant, properties.toVariant()));
QMetaObject::invokeMethod(_qmlElement.get(), "updatePropertiesFromScript", Q_ARG(QVariant, properties));
}
void QmlOverlay::render(RenderArgs* args) {

View file

@ -28,7 +28,7 @@ public:
// Cannot fetch properties from QML based overlays due to race conditions
bool supportsGetProperty() const override { return false; }
void setProperties(const QScriptValue& properties) override;
void setProperties(const QVariantMap& properties) override;
void render(RenderArgs* args) override;
private:

View file

@ -96,7 +96,7 @@ const render::ShapeKey Rectangle3DOverlay::getShapeKey() {
return builder.build();
}
void Rectangle3DOverlay::setProperties(const QScriptValue &properties) {
void Rectangle3DOverlay::setProperties(const QVariantMap& properties) {
Planar3DOverlay::setProperties(properties);
}

View file

@ -25,7 +25,7 @@ public:
~Rectangle3DOverlay();
virtual void render(RenderArgs* args);
virtual const render::ShapeKey getShapeKey() override;
virtual void setProperties(const QScriptValue& properties);
void setProperties(const QVariantMap& properties) override;
virtual Rectangle3DOverlay* createClone() const;
private:

View file

@ -121,57 +121,54 @@ const render::ShapeKey Text3DOverlay::getShapeKey() {
return builder.build();
}
void Text3DOverlay::setProperties(const QScriptValue& properties) {
void Text3DOverlay::setProperties(const QVariantMap& properties) {
Billboard3DOverlay::setProperties(properties);
QScriptValue text = properties.property("text");
auto text = properties["text"];
if (text.isValid()) {
setText(text.toVariant().toString());
setText(text.toString());
}
QScriptValue textAlpha = properties.property("textAlpha");
auto textAlpha = properties["textAlpha"];
if (textAlpha.isValid()) {
setTextAlpha(textAlpha.toVariant().toFloat());
setTextAlpha(textAlpha.toFloat());
}
QScriptValue backgroundColor = properties.property("backgroundColor");
bool valid;
auto backgroundColor = properties["backgroundColor"];
if (backgroundColor.isValid()) {
QScriptValue red = backgroundColor.property("red");
QScriptValue green = backgroundColor.property("green");
QScriptValue blue = backgroundColor.property("blue");
if (red.isValid() && green.isValid() && blue.isValid()) {
_backgroundColor.red = red.toVariant().toInt();
_backgroundColor.green = green.toVariant().toInt();
_backgroundColor.blue = blue.toVariant().toInt();
auto color = xColorFromVariant(backgroundColor, valid);
if (valid) {
_backgroundColor = color;
}
}
if (properties.property("backgroundAlpha").isValid()) {
setAlpha(properties.property("backgroundAlpha").toVariant().toFloat());
if (properties["backgroundAlpha"].isValid()) {
setAlpha(properties["backgroundAlpha"].toFloat());
}
if (properties.property("lineHeight").isValid()) {
setLineHeight(properties.property("lineHeight").toVariant().toFloat());
if (properties["lineHeight"].isValid()) {
setLineHeight(properties["lineHeight"].toFloat());
}
if (properties.property("leftMargin").isValid()) {
setLeftMargin(properties.property("leftMargin").toVariant().toFloat());
if (properties["leftMargin"].isValid()) {
setLeftMargin(properties["leftMargin"].toFloat());
}
if (properties.property("topMargin").isValid()) {
setTopMargin(properties.property("topMargin").toVariant().toFloat());
if (properties["topMargin"].isValid()) {
setTopMargin(properties["topMargin"].toFloat());
}
if (properties.property("rightMargin").isValid()) {
setRightMargin(properties.property("rightMargin").toVariant().toFloat());
if (properties["rightMargin"].isValid()) {
setRightMargin(properties["rightMargin"].toFloat());
}
if (properties.property("bottomMargin").isValid()) {
setBottomMargin(properties.property("bottomMargin").toVariant().toFloat());
if (properties["bottomMargin"].isValid()) {
setBottomMargin(properties["bottomMargin"].toFloat());
}
}
QScriptValue Text3DOverlay::getProperty(const QString& property) {
QVariant Text3DOverlay::getProperty(const QString& property) {
if (property == "text") {
return _text;
}
@ -179,7 +176,7 @@ QScriptValue Text3DOverlay::getProperty(const QString& property) {
return _textAlpha;
}
if (property == "backgroundColor") {
return xColorToScriptValue(_scriptEngine, _backgroundColor);
return xColorToVariant(_backgroundColor);
}
if (property == "backgroundAlpha") {
return Billboard3DOverlay::getProperty("alpha");

View file

@ -53,8 +53,8 @@ public:
void setRightMargin(float margin) { _rightMargin = margin; }
void setBottomMargin(float margin) { _bottomMargin = margin; }
virtual void setProperties(const QScriptValue& properties);
virtual QScriptValue getProperty(const QString& property);
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
QSizeF textSize(const QString& test) const; // Meters

View file

@ -26,67 +26,27 @@ AABox Volume3DOverlay::getBounds() const {
return AABox(extents);
}
void Volume3DOverlay::setProperties(const QScriptValue& properties) {
void Volume3DOverlay::setProperties(const QVariantMap& properties) {
Base3DOverlay::setProperties(properties);
QScriptValue dimensions = properties.property("dimensions");
auto dimensions = properties["dimensions"];
// if "dimensions" property was not there, check to see if they included aliases: scale
if (!dimensions.isValid()) {
dimensions = properties.property("scale");
dimensions = properties["scale"];
if (!dimensions.isValid()) {
dimensions = properties.property("size");
dimensions = properties["size"];
}
}
if (dimensions.isValid()) {
bool validDimensions = false;
glm::vec3 newDimensions;
QScriptValue x = dimensions.property("x");
QScriptValue y = dimensions.property("y");
QScriptValue z = dimensions.property("z");
if (x.isValid() && x.isNumber() &&
y.isValid() && y.isNumber() &&
z.isValid() && z.isNumber()) {
newDimensions.x = x.toNumber();
newDimensions.y = y.toNumber();
newDimensions.z = z.toNumber();
validDimensions = true;
} else {
QScriptValue width = dimensions.property("width");
QScriptValue height = dimensions.property("height");
QScriptValue depth = dimensions.property("depth");
if (width.isValid() && width.isNumber() &&
height.isValid() && height.isNumber() &&
depth.isValid() && depth.isNumber()) {
newDimensions.x = width.toNumber();
newDimensions.y = height.toNumber();
newDimensions.z = depth.toNumber();
validDimensions = true;
}
}
// size, scale, dimensions is special, it might just be a single scalar, check that here
if (!validDimensions && dimensions.isNumber()) {
float size = dimensions.toNumber();
newDimensions.x = size;
newDimensions.y = size;
newDimensions.z = size;
validDimensions = true;
}
if (validDimensions) {
setDimensions(newDimensions);
}
setDimensions(vec3FromVariant(dimensions));
}
}
QScriptValue Volume3DOverlay::getProperty(const QString& property) {
QVariant Volume3DOverlay::getProperty(const QString& property) {
if (property == "dimensions" || property == "scale" || property == "size") {
return vec3toScriptValue(_scriptEngine, getDimensions());
return vec3toVariant(getDimensions());
}
return Base3DOverlay::getProperty(property);

View file

@ -26,8 +26,8 @@ public:
void setDimensions(float value) { _localBoundingBox.setBox(glm::vec3(-value / 2.0f), value); }
void setDimensions(const glm::vec3& value) { _localBoundingBox.setBox(-value / 2.0f, value); }
virtual void setProperties(const QScriptValue& properties);
virtual QScriptValue getProperty(const QString& property);
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, glm::vec3& surfaceNormal);

View file

@ -12,7 +12,6 @@
#include "Web3DOverlay.h"
#include <QtScript/QScriptValue>
#include <QtGui/QOpenGLContext>
#include <QtQuick/QQuickItem>
@ -113,30 +112,34 @@ const render::ShapeKey Web3DOverlay::getShapeKey() {
return builder.build();
}
void Web3DOverlay::setProperties(const QScriptValue &properties) {
void Web3DOverlay::setProperties(const QVariantMap& properties) {
Billboard3DOverlay::setProperties(properties);
QScriptValue urlValue = properties.property("url");
auto urlValue = properties["url"];
if (urlValue.isValid()) {
QString newURL = urlValue.toVariant().toString();
QString newURL = urlValue.toString();
if (newURL != _url) {
setURL(newURL);
}
}
QScriptValue resolution = properties.property("resolution");
auto resolution = properties["resolution"];
if (resolution.isValid()) {
vec2FromScriptValue(resolution, _resolution);
bool valid;
auto res = vec2FromVariant(resolution, valid);
if (valid) {
_resolution = res;
}
}
QScriptValue dpi = properties.property("dpi");
auto dpi = properties["dpi"];
if (dpi.isValid()) {
_dpi = dpi.toVariant().toFloat();
_dpi = dpi.toFloat();
}
}
QScriptValue Web3DOverlay::getProperty(const QString& property) {
QVariant Web3DOverlay::getProperty(const QString& property) {
if (property == "url") {
return _url;
}

View file

@ -32,8 +32,8 @@ public:
// setters
void setURL(const QString& url);
virtual void setProperties(const QScriptValue& properties);
virtual QScriptValue getProperty(const QString& property);
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, glm::vec3& surfaceNormal);

View file

@ -627,6 +627,9 @@ void AnimInverseKinematics::initConstraints() {
} else if (0 == baseName.compare("Hand", Qt::CaseSensitive)) {
SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot);
stConstraint->setTwistLimits(0.0f, 0.0f); // max == min, disables twist limits
/* KEEP THIS CODE for future experimentation -- twist limits for hands
const float MAX_HAND_TWIST = 3.0f * PI / 5.0f;
const float MIN_HAND_TWIST = -PI / 2.0f;
if (isLeft) {
@ -634,8 +637,9 @@ void AnimInverseKinematics::initConstraints() {
} else {
stConstraint->setTwistLimits(MIN_HAND_TWIST, MAX_HAND_TWIST);
}
*/
/* KEEP THIS CODE for future experimentation
/* KEEP THIS CODE for future experimentation -- non-symmetrical swing limits for wrist
* a more complicated wrist with asymmetric cone
// these directions are approximate swing limits in parent-frame
// NOTE: they don't need to be normalized
@ -670,7 +674,7 @@ void AnimInverseKinematics::initConstraints() {
stConstraint->setTwistLimits(-MAX_SHOULDER_TWIST, MAX_SHOULDER_TWIST);
std::vector<float> minDots;
const float MAX_SHOULDER_SWING = PI / 6.0f;
const float MAX_SHOULDER_SWING = PI / 20.0f;
minDots.push_back(cosf(MAX_SHOULDER_SWING));
stConstraint->setSwingLimits(minDots);

View file

@ -182,49 +182,52 @@ bool SwingTwistConstraint::apply(glm::quat& rotation) const {
glm::vec3 twistedX = twistRotation * xAxis;
twistAngle *= copysignf(1.0f, glm::dot(glm::cross(xAxis, twistedX), yAxis));
// adjust measured twistAngle according to clamping history
switch (_lastTwistBoundary) {
case LAST_CLAMP_LOW_BOUNDARY:
// clamp to min
if (twistAngle > _maxTwist) {
twistAngle -= TWO_PI;
}
break;
case LAST_CLAMP_HIGH_BOUNDARY:
// clamp to max
if (twistAngle < _minTwist) {
twistAngle += TWO_PI;
}
break;
default: // LAST_CLAMP_NO_BOUNDARY
// clamp to nearest boundary
float midBoundary = 0.5f * (_maxTwist + _minTwist + TWO_PI);
if (twistAngle > midBoundary) {
// lower boundary is closer --> phase down one cycle
twistAngle -= TWO_PI;
} else if (twistAngle < midBoundary - TWO_PI) {
// higher boundary is closer --> phase up one cycle
twistAngle += TWO_PI;
}
break;
}
bool somethingClamped = false;
if (_minTwist != _maxTwist) {
// adjust measured twistAngle according to clamping history
switch (_lastTwistBoundary) {
case LAST_CLAMP_LOW_BOUNDARY:
// clamp to min
if (twistAngle > _maxTwist) {
twistAngle -= TWO_PI;
}
break;
case LAST_CLAMP_HIGH_BOUNDARY:
// clamp to max
if (twistAngle < _minTwist) {
twistAngle += TWO_PI;
}
break;
default: // LAST_CLAMP_NO_BOUNDARY
// clamp to nearest boundary
float midBoundary = 0.5f * (_maxTwist + _minTwist + TWO_PI);
if (twistAngle > midBoundary) {
// lower boundary is closer --> phase down one cycle
twistAngle -= TWO_PI;
} else if (twistAngle < midBoundary - TWO_PI) {
// higher boundary is closer --> phase up one cycle
twistAngle += TWO_PI;
}
break;
}
// clamp twistAngle
float clampedTwistAngle = glm::clamp(twistAngle, _minTwist, _maxTwist);
bool twistWasClamped = (twistAngle != clampedTwistAngle);
// clamp twistAngle
float clampedTwistAngle = glm::clamp(twistAngle, _minTwist, _maxTwist);
somethingClamped = (twistAngle != clampedTwistAngle);
// remember twist's clamp boundary history
if (twistWasClamped) {
_lastTwistBoundary = (twistAngle > clampedTwistAngle) ? LAST_CLAMP_HIGH_BOUNDARY : LAST_CLAMP_LOW_BOUNDARY;
} else {
_lastTwistBoundary = LAST_CLAMP_NO_BOUNDARY;
// remember twist's clamp boundary history
if (somethingClamped) {
_lastTwistBoundary = (twistAngle > clampedTwistAngle) ? LAST_CLAMP_HIGH_BOUNDARY : LAST_CLAMP_LOW_BOUNDARY;
twistAngle = clampedTwistAngle;
} else {
_lastTwistBoundary = LAST_CLAMP_NO_BOUNDARY;
}
}
// clamp the swing
// The swingAxis is always perpendicular to the reference axis (yAxis in the constraint's frame).
glm::vec3 swungY = swingRotation * yAxis;
glm::vec3 swingAxis = glm::cross(yAxis, swungY);
bool swingWasClamped = false;
float axisLength = glm::length(swingAxis);
if (axisLength > EPSILON) {
// The limit of swing is a function of "theta" which can be computed from the swingAxis
@ -236,13 +239,13 @@ bool SwingTwistConstraint::apply(glm::quat& rotation) const {
// use it to supply a new rotation.
swingAxis /= axisLength;
swingRotation = glm::angleAxis(acosf(minDot), swingAxis);
swingWasClamped = true;
somethingClamped = true;
}
}
if (swingWasClamped || twistWasClamped) {
if (somethingClamped) {
// update the rotation
twistRotation = glm::angleAxis(clampedTwistAngle, yAxis);
twistRotation = glm::angleAxis(twistAngle, yAxis);
rotation = swingRotation * twistRotation * _referenceRotation;
return true;
}

View file

@ -0,0 +1,717 @@
//
// AudioLimiter.cpp
// libraries/audio/src
//
// Created by Ken Cooke on 2/11/15.
// Copyright 2016 High Fidelity, Inc.
//
#include <math.h>
#include <assert.h>
#include "AudioLimiter.h"
#ifndef MAX
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
#ifdef _MSC_VER
#include <intrin.h>
#define MUL64(a,b) __emul((a), (b))
#define MULHI(a,b) ((int)(MUL64(a, b) >> 32))
#define MULQ31(a,b) ((int)(MUL64(a, b) >> 31))
#else
#define MUL64(a,b) ((long long)(a) * (b))
#define MULHI(a,b) ((int)(MUL64(a, b) >> 32))
#define MULQ31(a,b) ((int)(MUL64(a, b) >> 31))
#endif // _MSC_VER
//
// on x86 architecture, assume that SSE2 is present
//
#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__)
#include <xmmintrin.h>
// convert float to int using round-to-nearest
static inline int32_t floatToInt(float x) {
return _mm_cvt_ss2si(_mm_load_ss(&x));
}
#else
// convert float to int using round-to-nearest
static inline int32_t floatToInt(float x) {
x += (x < 0.0f ? -0.5f : 0.5f); // round
return (int32_t)x;
}
#endif // _M_IX86
static const double FIXQ31 = 2147483648.0; // convert float to Q31
static const double DB_TO_LOG2 = 0.16609640474436813; // convert dB to log2
// convert dB to amplitude
static double dBToGain(double dB) {
return pow(10.0, dB / 20.0);
}
// convert milliseconds to first-order time constant
static int32_t msToTc(double ms, double sampleRate) {
double tc = exp(-1000.0 / (ms * sampleRate));
return (int32_t)(FIXQ31 * tc); // Q31
}
// log2 domain values are Q26
static const int LOG2_INTBITS = 5;
static const int LOG2_FRACBITS = 31 - LOG2_INTBITS;
// log2 domain headroom bits above 0dB
static const int LOG2_HEADROOM = 15;
// log2 domain offsets so error < 0
static const int32_t LOG2_BIAS = 347;
static const int32_t EXP2_BIAS = 64;
//
// P(x) = log2(1+x) for x=[0,1]
// scaled by 1, 0.5, 0.25
//
// |error| < 347 ulp, smooth
//
static const int LOG2_TABBITS = 4;
static const int32_t log2Table[1 << LOG2_TABBITS][3] = {
{ -0x56dfe26d, 0x5c46daff, 0x00000000 },
{ -0x4d397571, 0x5bae58e7, 0x00025a75 },
{ -0x4518f84b, 0x5aabcac4, 0x000a62db },
{ -0x3e3075ec, 0x596168c0, 0x0019d0e6 },
{ -0x384486e9, 0x57e769c7, 0x00316109 },
{ -0x332742ba, 0x564f1461, 0x00513776 },
{ -0x2eb4bad4, 0x54a4cdfe, 0x00791de2 },
{ -0x2ad07c6c, 0x52f18320, 0x00a8aa46 },
{ -0x2763c4d6, 0x513ba123, 0x00df574c },
{ -0x245c319b, 0x4f87c5c4, 0x011c9399 },
{ -0x21aac79f, 0x4dd93bef, 0x015fcb52 },
{ -0x1f433872, 0x4c325584, 0x01a86ddc },
{ -0x1d1b54b4, 0x4a94ac6e, 0x01f5f13e },
{ -0x1b2a9f81, 0x4901524f, 0x0247d3f2 },
{ -0x1969fa57, 0x4778f3a7, 0x029d9dbf },
{ -0x17d36370, 0x45fbf1e8, 0x02f6dfe8 },
};
//
// P(x) = exp2(x) for x=[0,1]
// scaled by 2, 1, 0.5
// Uses exp2(-x) = exp2(1-x)/2
//
// |error| < 1387 ulp, smooth
//
static const int EXP2_TABBITS = 4;
static const int32_t exp2Table[1 << EXP2_TABBITS][3] = {
{ 0x3ed838c8, 0x58b574b7, 0x40000000 },
{ 0x41a0821c, 0x5888db8f, 0x4000b2b7 },
{ 0x4488548d, 0x582bcbc6, 0x40039be1 },
{ 0x4791158a, 0x579a1128, 0x400a71ae },
{ 0x4abc3a53, 0x56cf3089, 0x4017212e },
{ 0x4e0b48af, 0x55c66396, 0x402bd31b },
{ 0x517fd7a7, 0x547a946d, 0x404af0ec },
{ 0x551b9049, 0x52e658f9, 0x40772a57 },
{ 0x58e02e75, 0x5103ee08, 0x40b37b31 },
{ 0x5ccf81b1, 0x4ecd321f, 0x410331b5 },
{ 0x60eb6e09, 0x4c3ba007, 0x4169f548 },
{ 0x6535ecf9, 0x49484909, 0x41ebcdaf },
{ 0x69b10e5b, 0x45ebcede, 0x428d2acd },
{ 0x6e5ef96c, 0x421e5d48, 0x4352ece7 },
{ 0x7341edcb, 0x3dd7a354, 0x44426d7b },
{ 0x785c4499, 0x390ecc3a, 0x456188bd },
};
static const int IEEE754_FABS_MASK = 0x7fffffff;
static const int IEEE754_MANT_BITS = 23;
static const int IEEE754_EXPN_BIAS = 127;
//
// Peak detection and -log2(x) for float input (mono)
// x < 2^(31-LOG2_HEADROOM) returns 0x7fffffff
// x > 2^LOG2_HEADROOM undefined
//
static inline int32_t peaklog2(float* input) {
// float as integer bits
int32_t u = *(int32_t*)input;
// absolute value
int32_t peak = u & IEEE754_FABS_MASK;
// split into e and x - 1.0
int32_t e = IEEE754_EXPN_BIAS - (peak >> IEEE754_MANT_BITS) + LOG2_HEADROOM;
int32_t x = (peak << (31 - IEEE754_MANT_BITS)) & 0x7fffffff;
// saturate
if (e > 31) {
return 0x7fffffff;
}
int k = x >> (31 - LOG2_TABBITS);
// polynomial for log2(1+x) over x=[0,1]
int32_t c0 = log2Table[k][0];
int32_t c1 = log2Table[k][1];
int32_t c2 = log2Table[k][2];
c1 += MULHI(c0, x);
c2 += MULHI(c1, x);
// reconstruct result in Q26
return (e << LOG2_FRACBITS) - (c2 >> 3);
}
//
// Peak detection and -log2(x) for float input (stereo)
// x < 2^(31-LOG2_HEADROOM) returns 0x7fffffff
// x > 2^LOG2_HEADROOM undefined
//
static inline int32_t peaklog2(float* input0, float* input1) {
// float as integer bits
int32_t u0 = *(int32_t*)input0;
int32_t u1 = *(int32_t*)input1;
// max absolute value
u0 &= IEEE754_FABS_MASK;
u1 &= IEEE754_FABS_MASK;
int32_t peak = MAX(u0, u1);
// split into e and x - 1.0
int32_t e = IEEE754_EXPN_BIAS - (peak >> IEEE754_MANT_BITS) + LOG2_HEADROOM;
int32_t x = (peak << (31 - IEEE754_MANT_BITS)) & 0x7fffffff;
// saturate
if (e > 31) {
return 0x7fffffff;
}
int k = x >> (31 - LOG2_TABBITS);
// polynomial for log2(1+x) over x=[0,1]
int32_t c0 = log2Table[k][0];
int32_t c1 = log2Table[k][1];
int32_t c2 = log2Table[k][2];
c1 += MULHI(c0, x);
c2 += MULHI(c1, x);
// reconstruct result in Q26
return (e << LOG2_FRACBITS) - (c2 >> 3);
}
//
// Compute exp2(-x) for x=[0,32] in Q26, result in Q31
// x < 0 undefined
//
static inline int32_t fixexp2(int32_t x) {
// split into e and 1.0 - x
int32_t e = x >> LOG2_FRACBITS;
x = ~(x << LOG2_INTBITS) & 0x7fffffff;
int k = x >> (31 - EXP2_TABBITS);
// polynomial for exp2(x)
int32_t c0 = exp2Table[k][0];
int32_t c1 = exp2Table[k][1];
int32_t c2 = exp2Table[k][2];
c1 += MULHI(c0, x);
c2 += MULHI(c1, x);
// reconstruct result in Q31
return c2 >> e;
}
// fast TPDF dither in [-1.0f, 1.0f]
static inline float dither() {
static uint32_t rz = 0;
rz = rz * 69069 + 1;
int32_t r0 = rz & 0xffff;
int32_t r1 = rz >> 16;
return (int32_t)(r0 - r1) * (1/65536.0f);
}
//
// Peak-hold lowpass filter
//
// Bandlimits the gain control signal to greatly reduce the modulation distortion,
// while still reaching the peak attenuation after exactly N-1 samples of delay.
// N completely determines the limiter attack time.
//
template<int N, int CIC1, int CIC2>
class PeakFilterT {
static_assert((N & (N - 1)) == 0, "N must be a power of 2");
static_assert((CIC1 - 1) + (CIC2 - 1) == (N - 1), "Total CIC delay must be N-1");
int32_t _buffer[2*N] = {}; // shared FIFO
int _index = 0;
int32_t _acc1 = 0; // CIC1 integrator
int32_t _acc2 = 0; // CIC2 integrator
public:
PeakFilterT() {
// fill history
for (int n = 0; n < N-1; n++) {
process(0x7fffffff);
}
}
int32_t process(int32_t x) {
const int MASK = 2*N - 1; // buffer wrap
int i = _index;
// Fast peak-hold using a running-min filter. Finds the peak (min) value
// in the sliding window of N-1 samples, using only log2(N) comparisons.
// Hold time of N-1 samples exactly cancels the step response of FIR filter.
for (int n = 1; n < N; n <<= 1) {
_buffer[i] = x;
i = (i + n) & MASK;
x = MIN(x, _buffer[i]);
}
// Fast FIR attack/lowpass filter using a 2-stage CIC filter.
// The step response reaches final value after N-1 samples.
const int32_t CICGAIN = 0xffffffff / (CIC1 * CIC2); // Q32
x = MULHI(x, CICGAIN);
_buffer[i] = _acc1;
_acc1 += x; // integrator
i = (i + CIC1 - 1) & MASK;
x = _acc1 - _buffer[i]; // comb
_buffer[i] = _acc2;
_acc2 += x; // integrator
i = (i + CIC2 - 1) & MASK;
x = _acc2 - _buffer[i]; // comb
_index = (i + 1) & MASK; // skip unused tap
return x;
}
};
//
// Specializations that define the optimum lowpass filter for each length.
//
template<int N> class PeakFilter;
template<> class PeakFilter< 16> : public PeakFilterT< 16, 7, 10> {};
template<> class PeakFilter< 32> : public PeakFilterT< 32, 14, 19> {};
template<> class PeakFilter< 64> : public PeakFilterT< 64, 27, 38> {};
template<> class PeakFilter<128> : public PeakFilterT<128, 53, 76> {};
template<> class PeakFilter<256> : public PeakFilterT<256, 106, 151> {};
//
// N-1 sample delay (mono)
//
template<int N>
class MonoDelay {
static_assert((N & (N - 1)) == 0, "N must be a power of 2");
float _buffer[N] = {};
int _index = 0;
public:
void process(float& x) {
const int MASK = N - 1; // buffer wrap
int i = _index;
_buffer[i] = x;
i = (i + (N - 1)) & MASK;
x = _buffer[i];
_index = i;
}
};
//
// N-1 sample delay (stereo)
//
template<int N>
class StereoDelay {
static_assert((N & (N - 1)) == 0, "N must be a power of 2");
float _buffer[2*N] = {};
int _index = 0;
public:
void process(float& x0, float& x1) {
const int MASK = 2*N - 1; // buffer wrap
int i = _index;
_buffer[i+0] = x0;
_buffer[i+1] = x1;
i = (i + 2*(N - 1)) & MASK;
x0 = _buffer[i+0];
x1 = _buffer[i+1];
_index = i;
}
};
//
// Limiter (common)
//
class LimiterImpl {
protected:
static const int NARC = 64;
int32_t _holdTable[NARC];
int32_t _releaseTable[NARC];
int32_t _rmsAttack = 0x7fffffff;
int32_t _rmsRelease = 0x7fffffff;
int32_t _arcRelease = 0x7fffffff;
int32_t _threshold = 0;
int32_t _attn = 0;
int32_t _rms = 0;
int32_t _arc = 0;
int _sampleRate;
float _outGain = 0.0f;
public:
LimiterImpl(int sampleRate);
virtual ~LimiterImpl() {}
void setThreshold(float threshold);
void setRelease(float release);
int32_t envelope(int32_t attn);
virtual void process(float* input, int16_t* output, int numFrames) = 0;
};
LimiterImpl::LimiterImpl(int sampleRate) {
sampleRate = MAX(sampleRate, 8000);
sampleRate = MIN(sampleRate, 96000);
_sampleRate = sampleRate;
// defaults
setThreshold(0.0);
setRelease(250.0);
}
//
// Set the limiter threshold (dB)
// Brickwall limiting will begin when the signal exceeds the threshold.
// Makeup gain is applied, to reach but never exceed the output ceiling.
//
void LimiterImpl::setThreshold(float threshold) {
const double OUT_CEILING = -0.3;
const double Q31_TO_Q15 = 32768 / 2147483648.0;
// limiter threshold = -48dB to 0dB
threshold = MAX(threshold, -48.0f);
threshold = MIN(threshold, 0.0f);
// limiter threshold in log2 domain
_threshold = (int32_t)(-(double)threshold * DB_TO_LOG2 * (1 << LOG2_FRACBITS));
_threshold += LOG2_BIAS + EXP2_BIAS;
_threshold += LOG2_HEADROOM << LOG2_FRACBITS;
// makeup gain and conversion to 16-bit
_outGain = (float)(dBToGain(OUT_CEILING - (double)threshold) * Q31_TO_Q15);
}
//
// Set the limiter release time (milliseconds)
// This is a base value that scales the adaptive hold and release algorithms.
//
void LimiterImpl::setRelease(float release) {
const double MAXHOLD = 0.100; // max hold = 100ms
const double MINREL = 0.025; // min release = 0.025 * release
const int NHOLD = 16; // adaptive hold to adaptive release transition
// limiter release = 50 to 5000ms
release = MAX(release, 50.0f);
release = MIN(release, 5000.0f);
int32_t maxRelease = msToTc((double)release, _sampleRate);
_rmsAttack = msToTc(0.1 * (double)release, _sampleRate);
_rmsRelease = maxRelease;
// Compute ARC tables, working from low peak/rms to high peak/rms.
//
// At low peak/rms, release = max and hold is progressive to max
// At high peak/rms, hold = 0 and release is progressive to min
double x = MAXHOLD * _sampleRate;
double xstep = x / NHOLD; // 1.0 to 1.0/NHOLD
int i = 0;
for (; i < NHOLD; i++) {
// max release
_releaseTable[i] = maxRelease;
// progressive hold
_holdTable[i] = (int32_t)((maxRelease - 0x7fffffff) / x);
_holdTable[i] = MIN(_holdTable[i], -1); // prevent 0 on long releases
x -= xstep;
x = MAX(x, 1.0);
}
x = release;
xstep = x * (1.0-MINREL) / (NARC-NHOLD-1); // 1.0 to MINREL
for (; i < NARC; i++) {
// progressive release
_releaseTable[i] = msToTc(x, _sampleRate);
// min hold
_holdTable[i] = (_releaseTable[i] - 0x7fffffff); // 1 sample
x -= xstep;
}
}
//
// Limiter envelope processing
// zero attack, adaptive hold and release
//
int32_t LimiterImpl::envelope(int32_t attn) {
// table of (1/attn) for 1dB to 6dB, rounded to prevent overflow
static const int16_t invTable[64] = {
0x6000, 0x6000, 0x6000, 0x6000, 0x6000, 0x6000, 0x6000, 0x6000,
0x6000, 0x6000, 0x5d17, 0x5555, 0x4ec4, 0x4924, 0x4444, 0x4000,
0x3c3c, 0x38e3, 0x35e5, 0x3333, 0x30c3, 0x2e8b, 0x2c85, 0x2aaa,
0x28f5, 0x2762, 0x25ed, 0x2492, 0x234f, 0x2222, 0x2108, 0x2000,
0x1f07, 0x1e1e, 0x1d41, 0x1c71, 0x1bac, 0x1af2, 0x1a41, 0x1999,
0x18f9, 0x1861, 0x17d0, 0x1745, 0x16c1, 0x1642, 0x15c9, 0x1555,
0x14e5, 0x147a, 0x1414, 0x13b1, 0x1352, 0x12f6, 0x129e, 0x1249,
0x11f7, 0x11a7, 0x115b, 0x1111, 0x10c9, 0x1084, 0x1041, 0x1000,
};
if (attn < _attn) {
// RELEASE
// update release before use, to implement hold = 0
_arcRelease += _holdTable[_arc]; // update progressive hold
_arcRelease = MAX(_arcRelease, _releaseTable[_arc]); // saturate at final value
attn += MULQ31((_attn - attn), _arcRelease); // apply release
} else {
// ATTACK
// update ARC with normalized peak/rms
//
// arc = (attn-rms)*6/1 for attn < 1dB
// arc = (attn-rms)*6/attn for attn = 1dB to 6dB
// arc = (attn-rms)*6/6 for attn > 6dB
int bits = MIN(attn >> 20, 0x3f); // saturate 1/attn at 6dB
_arc = MAX(attn - _rms, 0); // peak/rms = (attn-rms)
_arc = MULHI(_arc, invTable[bits]); // normalized peak/rms = (attn-rms)/attn
_arc = MIN(_arc, NARC - 1); // saturate at 6dB
_arcRelease = 0x7fffffff; // reset release
}
_attn = attn;
// Update the RMS estimate after release is applied.
// The feedback loop with adaptive hold will damp any sustained modulation distortion.
int32_t tc = (attn > _rms) ? _rmsAttack : _rmsRelease;
_rms = attn + MULQ31((_rms - attn), tc);
return attn;
}
//
// Limiter (mono)
//
template<int N>
class LimiterMono : public LimiterImpl {
PeakFilter<N> _filter;
MonoDelay<N> _delay;
public:
LimiterMono(int sampleRate) : LimiterImpl(sampleRate) {}
void process(float* input, int16_t* output, int numFrames);
};
template<int N>
void LimiterMono<N>::process(float* input, int16_t* output, int numFrames)
{
for (int n = 0; n < numFrames; n++) {
// peak detect and convert to log2 domain
int32_t peak = peaklog2(&input[n]);
// compute limiter attenuation
int32_t attn = MAX(_threshold - peak, 0);
// apply envelope
attn = envelope(attn);
// convert from log2 domain
attn = fixexp2(attn);
// lowpass filter
attn = _filter.process(attn);
float gain = attn * _outGain;
// delay audio
float x = input[n];
_delay.process(x);
// apply gain
x *= gain;
// apply dither
x += dither();
// store 16-bit output
output[n] = (int16_t)floatToInt(x);
}
}
//
// Limiter (stereo)
//
template<int N>
class LimiterStereo : public LimiterImpl {
PeakFilter<N> _filter;
StereoDelay<N> _delay;
public:
LimiterStereo(int sampleRate) : LimiterImpl(sampleRate) {}
// interleaved stereo input/output
void process(float* input, int16_t* output, int numFrames);
};
template<int N>
void LimiterStereo<N>::process(float* input, int16_t* output, int numFrames)
{
for (int n = 0; n < numFrames; n++) {
// peak detect and convert to log2 domain
int32_t peak = peaklog2(&input[2*n+0], &input[2*n+1]);
// compute limiter attenuation
int32_t attn = MAX(_threshold - peak, 0);
// apply envelope
attn = envelope(attn);
// convert from log2 domain
attn = fixexp2(attn);
// lowpass filter
attn = _filter.process(attn);
float gain = attn * _outGain;
// delay audio
float x0 = input[2*n+0];
float x1 = input[2*n+1];
_delay.process(x0, x1);
// apply gain
x0 *= gain;
x1 *= gain;
// apply dither
float d = dither();
x0 += d;
x1 += d;
// store 16-bit output
output[2*n+0] = (int16_t)floatToInt(x0);
output[2*n+1] = (int16_t)floatToInt(x1);
}
}
//
// Public API
//
AudioLimiter::AudioLimiter(int sampleRate, int numChannels) {
if (numChannels == 1) {
// ~1.5ms lookahead for all rates
if (sampleRate < 16000) {
_impl = new LimiterMono<16>(sampleRate);
} else if (sampleRate < 32000) {
_impl = new LimiterMono<32>(sampleRate);
} else if (sampleRate < 64000) {
_impl = new LimiterMono<64>(sampleRate);
} else {
_impl = new LimiterMono<128>(sampleRate);
}
} else if (numChannels == 2) {
// ~1.5ms lookahead for all rates
if (sampleRate < 16000) {
_impl = new LimiterStereo<16>(sampleRate);
} else if (sampleRate < 32000) {
_impl = new LimiterStereo<32>(sampleRate);
} else if (sampleRate < 64000) {
_impl = new LimiterStereo<64>(sampleRate);
} else {
_impl = new LimiterStereo<128>(sampleRate);
}
} else {
assert(0); // unsupported
}
}
AudioLimiter::~AudioLimiter() {
delete _impl;
}
void AudioLimiter::render(float* input, int16_t* output, int numFrames) {
_impl->process(input, output, numFrames);
}
void AudioLimiter::setThreshold(float threshold) {
_impl->setThreshold(threshold);
}
void AudioLimiter::setRelease(float release) {
_impl->setRelease(release);
}

View file

@ -0,0 +1,30 @@
//
// AudioLimiter.h
// libraries/audio/src
//
// Created by Ken Cooke on 2/11/15.
// Copyright 2016 High Fidelity, Inc.
//
#ifndef hifi_AudioLimiter_h
#define hifi_AudioLimiter_h
#include "stdint.h"
class LimiterImpl;
class AudioLimiter {
public:
AudioLimiter(int sampleRate, int numChannels);
~AudioLimiter();
void render(float* input, int16_t* output, int numFrames);
void setThreshold(float threshold);
void setRelease(float release);
private:
LimiterImpl* _impl;
};
#endif // hifi_AudioLimiter_h

View file

@ -14,6 +14,7 @@
#include <QtCore/QRect>
#include <QtCore/QVariant>
#include <QtGui/QColor>
#include <QtGui/QVector2D>
#include <QtGui/QVector3D>
#include <QtGui/QQuaternion>
#include <glm/gtc/quaternion.hpp>
@ -86,6 +87,19 @@ void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3) {
vec3.z = object.property("z").toVariant().toFloat();
}
QVariant vec3toVariant(const glm::vec3 &vec3) {
if (vec3.x != vec3.x || vec3.y != vec3.y || vec3.z != vec3.z) {
// if vec3 contains a NaN don't try to convert it
return QVariant();
}
QVariantMap result;
result["x"] = vec3.x;
result["y"] = vec3.y;
result["z"] = vec3.z;
return result;
}
QScriptValue qVectorVec3ToScriptValue(QScriptEngine* engine, const QVector<glm::vec3>& vector) {
QScriptValue array = engine->newArray();
for (int i = 0; i < vector.size(); i++) {
@ -94,6 +108,7 @@ QScriptValue qVectorVec3ToScriptValue(QScriptEngine* engine, const QVector<glm::
return array;
}
glm::vec3 vec3FromVariant(const QVariant &object, bool& valid) {
glm::vec3 v;
valid = false;
@ -138,7 +153,6 @@ glm::vec3 vec3FromVariant(const QVariant &object) {
return vec3FromVariant(object, valid);
}
QScriptValue quatToScriptValue(QScriptEngine* engine, const glm::quat &quat) {
QScriptValue obj = engine->newObject();
if (quat.x != quat.x || quat.y != quat.y || quat.z != quat.z || quat.w != quat.w) {
@ -195,6 +209,19 @@ glm::quat quatFromVariant(const QVariant &object) {
return quatFromVariant(object, valid);
}
QVariant quatToVariant(const glm::quat &quat) {
if (quat.x != quat.x || quat.y != quat.y || quat.z != quat.z) {
// if vec3 contains a NaN don't try to convert it
return QVariant();
}
QVariantMap result;
result["x"] = quat.x;
result["y"] = quat.y;
result["z"] = quat.z;
result["w"] = quat.w;
return result;
}
QScriptValue qVectorQuatToScriptValue(QScriptEngine* engine, const QVector<glm::quat>& vector) {
QScriptValue array = engine->newArray();
for (int i = 0; i < vector.size(); i++) {
@ -333,6 +360,51 @@ void vec2FromScriptValue(const QScriptValue &object, glm::vec2 &vec2) {
vec2.y = object.property("y").toVariant().toFloat();
}
QVariant vec2toVariant(const glm::vec2 &vec2) {
if (vec2.x != vec2.x || vec2.y != vec2.y) {
// if vec2 contains a NaN don't try to convert it
return QVariant();
}
QVariantMap result;
result["x"] = vec2.x;
result["y"] = vec2.y;
return result;
}
glm::vec2 vec2FromVariant(const QVariant &object, bool& isValid) {
isValid = false;
glm::vec2 result;
if (object.canConvert<float>()) {
result = glm::vec2(object.toFloat());
} else if (object.canConvert<QVector2D>()) {
auto qvec2 = qvariant_cast<QVector2D>(object);
result.x = qvec2.x();
result.y = qvec2.y();
} else {
auto map = object.toMap();
auto x = map["x"];
if (!x.isValid()) {
x = map["width"];
}
auto y = map["y"];
if (!y.isValid()) {
y = map["height"];
}
if (x.isValid() && y.isValid()) {
result.x = x.toFloat(&isValid);
if (isValid) {
result.y = y.toFloat(&isValid);
}
}
}
return result;
}
glm::vec2 vec2FromVariant(const QVariant &object) {
bool valid;
return vec2FromVariant(object, valid);
}
QScriptValue qRectToScriptValue(QScriptEngine* engine, const QRect& rect) {
QScriptValue obj = engine->newObject();
obj.setProperty("x", rect.x());
@ -357,6 +429,38 @@ QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color) {
return obj;
}
QVariant qRectToVariant(const QRect& rect) {
QVariantMap obj;
obj["x"] = rect.x();
obj["y"] = rect.y();
obj["width"] = rect.width();
obj["height"] = rect.height();
return obj;
}
QRect qRectFromVariant(const QVariant& objectVar, bool& valid) {
QVariantMap object = objectVar.toMap();
QRect rect;
valid = false;
rect.setX(object["x"].toInt(&valid));
if (valid) {
rect.setY(object["y"].toInt(&valid));
}
if (valid) {
rect.setWidth(object["width"].toInt(&valid));
}
if (valid) {
rect.setHeight(object["height"].toInt(&valid));
}
return rect;
}
QRect qRectFromVariant(const QVariant& object) {
bool valid;
return qRectFromVariant(object, valid);
}
void xColorFromScriptValue(const QScriptValue &object, xColor& color) {
if (!object.isValid()) {
return;
@ -377,6 +481,59 @@ void xColorFromScriptValue(const QScriptValue &object, xColor& color) {
}
}
QVariant xColorToVariant(const xColor& color) {
QVariantMap obj;
obj["red"] = color.red;
obj["green"] = color.green;
obj["blue"] = color.blue;
return obj;
}
xColor xColorFromVariant(const QVariant &object, bool& isValid) {
isValid = false;
xColor color { 0, 0, 0 };
if (!object.isValid()) {
return color;
}
if (object.canConvert<int>()) {
isValid = true;
color.red = color.green = color.blue = (uint8_t)object.toInt();
} else if (object.canConvert<QString>()) {
QColor qcolor(object.toString());
if (qcolor.isValid()) {
isValid = true;
color.red = (uint8_t)qcolor.red();
color.blue = (uint8_t)qcolor.blue();
color.green = (uint8_t)qcolor.green();
}
} else if (object.canConvert<QColor>()) {
QColor qcolor = qvariant_cast<QColor>(object);
if (qcolor.isValid()) {
isValid = true;
color.red = (uint8_t)qcolor.red();
color.blue = (uint8_t)qcolor.blue();
color.green = (uint8_t)qcolor.green();
}
} else {
QVariantMap map = object.toMap();
color.red = map["red"].toInt(&isValid);
if (isValid) {
color.green = map["green"].toInt(&isValid);
}
if (isValid) {
color.blue = map["blue"].toInt(&isValid);
}
}
return color;
}
xColor xColorFromVariant(const QVariant &object) {
bool valid;
return xColorFromVariant(object, valid);
}
QScriptValue qColorToScriptValue(QScriptEngine* engine, const QColor& color) {
QScriptValue object = engine->newObject();
object.setProperty("red", color.red());

View file

@ -35,48 +35,71 @@ Q_DECLARE_METATYPE(AACube)
void registerMetaTypes(QScriptEngine* engine);
// Vec4
QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4);
void vec4FromScriptValue(const QScriptValue& object, glm::vec4& vec4);
// Vec3
QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3);
void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3);
QVariant vec3toVariant(const glm::vec3 &vec3);
glm::vec3 vec3FromVariant(const QVariant &object, bool& valid);
glm::vec3 vec3FromVariant(const QVariant &object);
// Vec2
QScriptValue vec2toScriptValue(QScriptEngine* engine, const glm::vec2 &vec2);
void vec2FromScriptValue(const QScriptValue &object, glm::vec2 &vec2);
QVariant vec2toVariant(const glm::vec2 &vec2);
glm::vec2 vec2FromVariant(const QVariant &object, bool& valid);
glm::vec2 vec2FromVariant(const QVariant &object);
// Quaternions
QScriptValue quatToScriptValue(QScriptEngine* engine, const glm::quat& quat);
void quatFromScriptValue(const QScriptValue &object, glm::quat& quat);
QVariant quatToVariant(const glm::quat& quat);
glm::quat quatFromVariant(const QVariant &object, bool& isValid);
glm::quat quatFromVariant(const QVariant &object);
// Rect
QScriptValue qRectToScriptValue(QScriptEngine* engine, const QRect& rect);
void qRectFromScriptValue(const QScriptValue& object, QRect& rect);
QVariant qRectToVariant(const QRect& rect);
QRect qRectFromVariant(const QVariant& object, bool& isValid);
// xColor
QScriptValue xColorToScriptValue(QScriptEngine* engine, const xColor& color);
void xColorFromScriptValue(const QScriptValue &object, xColor& color);
QVariant xColorToVariant(const xColor& color);
xColor xColorFromVariant(const QVariant &object, bool& isValid);
// QColor
QScriptValue qColorToScriptValue(QScriptEngine* engine, const QColor& color);
void qColorFromScriptValue(const QScriptValue& object, QColor& color);
QScriptValue qURLToScriptValue(QScriptEngine* engine, const QUrl& url);
void qURLFromScriptValue(const QScriptValue& object, QUrl& url);
// vector<vec3>
QScriptValue qVectorVec3ToScriptValue(QScriptEngine* engine, const QVector<glm::vec3>& vector);
void qVectorVec3FromScriptValue(const QScriptValue& array, QVector<glm::vec3>& vector);
QVector<glm::vec3> qVectorVec3FromScriptValue(const QScriptValue& array);
// vector<quat>
QScriptValue qVectorQuatToScriptValue(QScriptEngine* engine, const QVector<glm::quat>& vector);
void qVectorQuatFromScriptValue(const QScriptValue& array, QVector<glm::quat>& vector);
QVector<glm::quat> qVectorQuatFromScriptValue(const QScriptValue& array);
// vector<bool>
QScriptValue qVectorBoolToScriptValue(QScriptEngine* engine, const QVector<bool>& vector);
void qVectorBoolFromScriptValue(const QScriptValue& array, QVector<bool>& vector);
QVector<bool> qVectorBoolFromScriptValue(const QScriptValue& array);
// vector<float>
QScriptValue qVectorFloatToScriptValue(QScriptEngine* engine, const QVector<float>& vector);
void qVectorFloatFromScriptValue(const QScriptValue& array, QVector<float>& vector);
QVector<float> qVectorFloatFromScriptValue(const QScriptValue& array);