mirror of
https://github.com/JulianGro/overte.git
synced 2025-08-23 06:11:58 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into fix-hulls-for-non-default-registration
This commit is contained in:
commit
f03ddb856b
57 changed files with 2688 additions and 634 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
339
examples/entityScripts/lightningEntity.js
Normal file
339
examples/entityScripts/lightningEntity.js
Normal 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();
|
||||
});
|
125
examples/shaders/rainyDayNightSkybox.fs
Normal file
125
examples/shaders/rainyDayNightSkybox.fs
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
689
examples/toybox/flappyAvatars/flappyAvatars.js
Normal file
689
examples/toybox/flappyAvatars/flappyAvatars.js
Normal 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();
|
||||
});
|
34
examples/toybox/flappyAvatars/flappyAvatars.json
Normal file
34
examples/toybox/flappyAvatars/flappyAvatars.json
Normal 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
|
||||
}
|
27
examples/zones/RainyDayNightZone.json
Normal file
27
examples/zones/RainyDayNightZone.json
Normal 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
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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});
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
717
libraries/audio/src/AudioLimiter.cpp
Normal file
717
libraries/audio/src/AudioLimiter.cpp
Normal 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);
|
||||
}
|
30
libraries/audio/src/AudioLimiter.h
Normal file
30
libraries/audio/src/AudioLimiter.h
Normal 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
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue