Merge pull request #6997 from jagwire/WorkingEnergy

Working energy usage for entity manipulation.
This commit is contained in:
Philip Rosedale 2016-02-05 17:17:45 -08:00
commit a98efbc660
6 changed files with 221 additions and 43 deletions

View file

@ -0,0 +1,63 @@
Script.include("../../libraries/utils.js");
var energyColor = {red: 0, green: 200, blue: 0};
var lowEnergyColor = {red: 255, green: 0, blue: 0};
var totalWidth = 200;
var paddingRight = 50;
var xPosition = Window.innerWidth - totalWidth - paddingRight;
var lowEnergyThreshold = 0.3;
var currentEnergy = 1.0;
var energyLossRate = 0.003;
var energyChargeRate = 0.003;
var isGrabbing = false;
var refractoryPeriod = 2000;
var lastAvatarVelocity = MyAvatar.getVelocity();
var lastAvatarPosition = MyAvatar.position;
var background = Overlays.addOverlay("text", {
x: xPosition,
y: 20,
width: totalWidth,
height: 10,
backgroundColor: {red: 255, green: 0, blue: 0}
})
var bar = Overlays.addOverlay("text", {
x: xPosition,
y: 20,
width: totalWidth,
height: 10,
backgroundColor: energyColor
});
// Takes an energy value between 0 and 1 and sets energy bar width appropriately
function setEnergy(energy) {
energy = clamp(energy, 0, 1);
var barWidth = totalWidth * energy;
var color = energy <= lowEnergyThreshold ? lowEnergyColor: energyColor;
Overlays.editOverlay(bar, { width: barWidth, backgroundColor: color});
}
function update() {
currentEnergy = clamp(MyAvatar.energy, 0, 1);
setEnergy(currentEnergy);
}
function cleanup() {
Overlays.deleteOverlay(background);
Overlays.deleteOverlay(bar);
}
function energyChanged(newValue) {
Entities.currentAvatarEnergy = newValue;
}
function debitAvatarEnergy(value) {
MyAvatar.energy = MyAvatar.energy - value;
}
Entities.costMultiplier = 0.02;
Entities.debitEnergySource.connect(debitAvatarEnergy);
MyAvatar.energyChanged.connect(energyChanged);
Script.update.connect(update);
Script.scriptEnding.connect(cleanup);

View file

@ -51,45 +51,8 @@ function setEnergy(energy) {
Overlays.editOverlay(bar, { width: barWidth, backgroundColor: color});
}
function avatarAccelerationEnergy() {
var AVATAR_MOVEMENT_ENERGY_CONSTANT = 0.001;
var velocity = MyAvatar.getVelocity();
var dV = Math.abs(Vec3.length(velocity) - Vec3.length(lastAvatarVelocity));
var dE = Vec3.length(lastAvatarVelocity) * dV * AVATAR_MOVEMENT_ENERGY_CONSTANT;
lastAvatarVelocity = velocity;
return dE;
}
function teleported() {
var MAX_AVATAR_MOVEMENT_PER_FRAME = 30.0;
var position = MyAvatar.position;
var dP = Vec3.length(Vec3.subtract(position, lastAvatarPosition));
lastAvatarPosition = position;
return (dP > MAX_AVATAR_MOVEMENT_PER_FRAME);
}
function audioEnergy() {
var AUDIO_ENERGY_CONSTANT = 0.000001;
return MyAvatar.audioLoudness * AUDIO_ENERGY_CONSTANT;
}
function update() {
// refill energy
currentEnergy += energyChargeRate;
// Avatar acceleration
currentEnergy -= avatarAccelerationEnergy();
// Teleport cost
if (teleported()) {
currentEnergy = 0;
}
// Making sounds
currentEnergy -= audioEnergy();
currentEnergy = clamp(currentEnergy, 0, 1);
currentEnergy = clamp(MyAvatar.energy, 0, 1);
setEnergy(currentEnergy);
}
@ -98,5 +61,10 @@ function cleanup() {
Overlays.deleteOverlay(bar);
}
function energyChanged(newValue) {
Entities.currentAvatarEnergy = newValue;
}
MyAvatar.energyChanged.connect(energyChanged);
Script.update.connect(update);
Script.scriptEnding.connect(cleanup);

View file

@ -298,8 +298,20 @@ void MyAvatar::update(float deltaTime) {
auto audio = DependencyManager::get<AudioClient>();
head->setAudioLoudness(audio->getLastInputLoudness());
head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness());
simulate(deltaTime);
simulate(deltaTime);
currentEnergy += energyChargeRate;
currentEnergy -= getAccelerationEnergy();
currentEnergy -= getAudioEnergy();
if(didTeleport()) {
currentEnergy = 0.0f;
}
currentEnergy = max(0.0f, min(currentEnergy,1.0f));
emit energyChanged(currentEnergy);
}
extern QByteArray avatarStateToFrame(const AvatarData* _avatar);
@ -1892,3 +1904,31 @@ glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(const MyAvatar& myAvatar, co
}
}
float MyAvatar::getAccelerationEnergy() {
glm::vec3 velocity = getVelocity();
int changeInVelocity = abs(velocity.length() - priorVelocity.length());
float changeInEnergy = priorVelocity.length() * changeInVelocity * AVATAR_MOVEMENT_ENERGY_CONSTANT;
priorVelocity = velocity;
return changeInEnergy;
}
float MyAvatar::getEnergy() {
return currentEnergy;
}
void MyAvatar::setEnergy(float value) {
currentEnergy = value;
}
float MyAvatar::getAudioEnergy() {
return getAudioLoudness() * AUDIO_ENERGY_CONSTANT;
}
bool MyAvatar::didTeleport() {
glm::vec3 pos = getPosition();
glm::vec3 changeInPosition = pos - lastPosition;
lastPosition = pos;
return (changeInPosition.length() > MAX_AVATAR_MOVEMENT_PER_FRAME);
}

View file

@ -78,6 +78,7 @@ class MyAvatar : public Avatar {
Q_PROPERTY(controller::Pose rightHandPose READ getRightHandPose)
Q_PROPERTY(controller::Pose leftHandTipPose READ getLeftHandTipPose)
Q_PROPERTY(controller::Pose rightHandTipPose READ getRightHandTipPose)
Q_PROPERTY(float energy READ getEnergy WRITE setEnergy)
public:
MyAvatar(RigPointer rig);
@ -276,8 +277,10 @@ signals:
void transformChanged();
void newCollisionSoundURL(const QUrl& url);
void collisionWithEntity(const Collision& collision);
void energyChanged(float newEnergy);
void positionGoneTo();
private:
glm::vec3 getWorldBodyPosition() const;
@ -413,9 +416,21 @@ private:
AtRestDetector _hmdAtRestDetector;
bool _lastIsMoving { false };
bool _hoverReferenceCameraFacingIsCaptured { false };
glm::vec3 _hoverReferenceCameraFacing; // hmd sensor space
float AVATAR_MOVEMENT_ENERGY_CONSTANT { 0.001f };
float AUDIO_ENERGY_CONSTANT { 0.000001f };
float MAX_AVATAR_MOVEMENT_PER_FRAME { 30.0f };
float currentEnergy { 0.0f };
float energyChargeRate { 0.003f };
glm::vec3 priorVelocity;
glm::vec3 lastPosition;
float getAudioEnergy();
float getAccelerationEnergy();
float getEnergy();
void setEnergy(float value);
bool didTeleport();
};
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);

View file

@ -8,7 +8,6 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "EntityScriptingInterface.h"
#include "EntityItemID.h"
@ -123,6 +122,20 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
EntityItemProperties propertiesWithSimID = convertLocationFromScriptSemantics(properties);
propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged());
auto dimensions = propertiesWithSimID.getDimensions();
float volume = dimensions.x * dimensions.y * dimensions.z;
auto density = propertiesWithSimID.getDensity();
auto newVelocity = propertiesWithSimID.getVelocity().length();
float cost = calculateCost(density * volume, 0, newVelocity);
cost *= costMultiplier;
if(cost > _currentAvatarEnergy) {
return QUuid();
} else {
//debit the avatar energy and continue
emit debitEnergySource(cost);
}
EntityItemID id = EntityItemID(QUuid::createUuid());
// If we have a local entity tree set, then also update it.
@ -215,9 +228,28 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit
QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& scriptSideProperties) {
EntityItemProperties properties = scriptSideProperties;
auto dimensions = properties.getDimensions();
float volume = dimensions.x * dimensions.y * dimensions.z;
auto density = properties.getDensity();
auto newVelocity = properties.getVelocity().length();
float oldVelocity = { 0.0f };
EntityItemID entityID(id);
if (!_entityTree) {
queueEntityMessage(PacketType::EntityEdit, entityID, properties);
//if there is no local entity entity tree, no existing velocity, use 0.
float cost = calculateCost(density * volume, oldVelocity, newVelocity);
cost *= costMultiplier;
if(cost > _currentAvatarEnergy) {
return QUuid();
} else {
//debit the avatar energy and continue
emit debitEnergySource(cost);
}
return id;
}
// If we have a local entity tree set, then also update it.
@ -231,6 +263,9 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
if (!entity) {
return;
}
//existing entity, retrieve old velocity for check down below
oldVelocity = entity->getVelocity().length();
if (!scriptSideProperties.parentIDChanged()) {
properties.setParentID(entity->getParentID());
}
@ -246,6 +281,16 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
}
properties = convertLocationFromScriptSemantics(properties);
updatedEntity = _entityTree->updateEntity(entityID, properties);
float cost = calculateCost(density * volume, oldVelocity, newVelocity);
cost *= costMultiplier;
if(cost > _currentAvatarEnergy) {
updatedEntity = false;
} else {
//debit the avatar energy and continue
emit debitEnergySource(cost);
}
});
if (!updatedEntity) {
@ -320,6 +365,21 @@ void EntityScriptingInterface::deleteEntity(QUuid id) {
_entityTree->withWriteLock([&] {
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
if (entity) {
auto dimensions = entity->getDimensions();
float volume = dimensions.x * dimensions.y * dimensions.z;
auto density = entity->getDensity();
auto velocity = entity->getVelocity().length();
float cost = calculateCost(density * volume, velocity, 0);
cost *= costMultiplier;
if(cost > _currentAvatarEnergy) {
return;
} else {
//debit the avatar energy and continue
emit debitEnergySource(cost);
}
if (entity->getLocked()) {
shouldDelete = false;
} else {
@ -996,3 +1056,20 @@ QStringList EntityScriptingInterface::getJointNames(const QUuid& entityID) {
Q_RETURN_ARG(QStringList, result), Q_ARG(QUuid, entityID));
return result;
}
float EntityScriptingInterface::calculateCost(float mass, float oldVelocity, float newVelocity) {
return std::abs(mass * (newVelocity - oldVelocity));
}
void EntityScriptingInterface::setCurrentAvatarEnergy(float energy) {
// qCDebug(entities) << "NEW AVATAR ENERGY IN ENTITY SCRIPTING INTERFACE: " << energy;
_currentAvatarEnergy = energy;
}
float EntityScriptingInterface::getCostMultiplier() {
return costMultiplier;
}
void EntityScriptingInterface::setCostMultiplier(float value) {
costMultiplier = value;
}

View file

@ -16,6 +16,8 @@
#include <QtCore/QObject>
#include <QtCore/QStringList>
#include <QtQml/QJSValue>
#include <QtQml/QJSValueList>
#include <DependencyManager.h>
#include <Octree.h>
@ -57,6 +59,9 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra
/// handles scripting of Entity commands from JS passed to assigned clients
class EntityScriptingInterface : public OctreeScriptingInterface, public Dependency {
Q_OBJECT
Q_PROPERTY(float currentAvatarEnergy READ getCurrentAvatarEnergy WRITE setCurrentAvatarEnergy)
Q_PROPERTY(float costMultiplier READ getCostMultiplier WRITE setCostMultiplier)
public:
EntityScriptingInterface(bool bidOnSimulationOwnership);
@ -67,7 +72,7 @@ public:
void setEntityTree(EntityTreePointer modelTree);
EntityTreePointer getEntityTree() { return _entityTree; }
void setEntitiesScriptEngine(EntitiesScriptEngineProvider* engine) { _entitiesScriptEngine = engine; }
float calculateCost(float mass, float oldVelocity, float newVelocity);
public slots:
// returns true if the DomainServer will allow this Node/Avatar to make changes
@ -163,6 +168,7 @@ public slots:
Q_INVOKABLE int getJointIndex(const QUuid& entityID, const QString& name);
Q_INVOKABLE QStringList getJointNames(const QUuid& entityID);
signals:
void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
@ -188,6 +194,7 @@ signals:
void deletingEntity(const EntityItemID& entityID);
void addingEntity(const EntityItemID& entityID);
void clearingEntities();
void debitEnergySource(float value);
private:
bool actionWorker(const QUuid& entityID, std::function<bool(EntitySimulation*, EntityItemPointer)> actor);
@ -205,7 +212,15 @@ private:
EntityTreePointer _entityTree;
EntitiesScriptEngineProvider* _entitiesScriptEngine { nullptr };
bool _bidOnSimulationOwnership { false };
float _currentAvatarEnergy = { FLT_MAX };
float getCurrentAvatarEnergy() { return _currentAvatarEnergy; }
void setCurrentAvatarEnergy(float energy);
float costMultiplier = { 0.01f };
float getCostMultiplier();
void setCostMultiplier(float value);
};
#endif // hifi_EntityScriptingInterface_h