mirror of
https://github.com/overte-org/overte.git
synced 2025-04-17 05:30:41 +02:00
Merge pull request #6997 from jagwire/WorkingEnergy
Working energy usage for entity manipulation.
This commit is contained in:
commit
a98efbc660
6 changed files with 221 additions and 43 deletions
63
examples/example/ui/MyEnergyBar.js
Normal file
63
examples/example/ui/MyEnergyBar.js
Normal 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);
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue