overte/libraries/particles/src/Particle.h

429 lines
17 KiB
C++

//
// Particle.h
// hifi
//
// Created by Brad Hefta-Gaub on 12/4/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#ifndef __hifi__Particle__
#define __hifi__Particle__
#include <glm/glm.hpp>
#include <stdint.h>
#include <QtScript/QScriptEngine>
#include <QtCore/QObject>
#include <CollisionInfo.h>
#include <SharedUtil.h>
#include <OctreePacketData.h>
class Particle;
class ParticleEditPacketSender;
class ParticleProperties;
class ParticlesScriptingInterface;
class ParticleScriptObject;
class ParticleTree;
class ScriptEngine;
class VoxelEditPacketSender;
class VoxelsScriptingInterface;
struct VoxelDetail;
const uint32_t NEW_PARTICLE = 0xFFFFFFFF;
const uint32_t UNKNOWN_TOKEN = 0xFFFFFFFF;
const uint32_t UNKNOWN_PARTICLE_ID = 0xFFFFFFFF;
const uint16_t CONTAINS_RADIUS = 1;
const uint16_t CONTAINS_POSITION = 2;
const uint16_t CONTAINS_COLOR = 4;
const uint16_t CONTAINS_VELOCITY = 8;
const uint16_t CONTAINS_GRAVITY = 16;
const uint16_t CONTAINS_DAMPING = 32;
const uint16_t CONTAINS_LIFETIME = 64;
const uint16_t CONTAINS_INHAND = 128;
const uint16_t CONTAINS_SCRIPT = 256;
const uint16_t CONTAINS_SHOULDDIE = 512;
const uint16_t CONTAINS_MODEL_URL = 1024;
const uint16_t CONTAINS_MODEL_TRANSLATION = 1024;
const uint16_t CONTAINS_MODEL_ROTATION = 2048;
const uint16_t CONTAINS_MODEL_SCALE = 4096;
const float DEFAULT_LIFETIME = 10.0f; // particles live for 10 seconds by default
const float DEFAULT_DAMPING = 0.99f;
const float DEFAULT_RADIUS = 0.1f / TREE_SCALE;
const float MINIMUM_PARTICLE_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container
const glm::vec3 DEFAULT_GRAVITY(0, (-9.8f / TREE_SCALE), 0);
const QString DEFAULT_SCRIPT("");
const glm::vec3 DEFAULT_MODEL_TRANSLATION(0, 0, 0);
const glm::quat DEFAULT_MODEL_ROTATION(0, 0, 0, 0);
const float DEFAULT_MODEL_SCALE = 1.0f;
const bool IN_HAND = true; // it's in a hand
const bool NOT_IN_HAND = !IN_HAND; // it's not in a hand
/// A collection of properties of a particle used in the scripting API. Translates between the actual properties of a particle
/// and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete set of
/// particle properties via JavaScript hashes/QScriptValues
/// all units for position, velocity, gravity, radius, etc are in meter units
class ParticleProperties {
public:
ParticleProperties();
QScriptValue copyToScriptValue(QScriptEngine* engine) const;
void copyFromScriptValue(const QScriptValue& object);
void copyToParticle(Particle& particle) const;
void copyFromParticle(const Particle& particle);
const glm::vec3& getPosition() const { return _position; }
xColor getColor() const { return _color; }
float getRadius() const { return _radius; }
const glm::vec3& getVelocity() const { return _velocity; }
const glm::vec3& getGravity() const { return _gravity; }
float getDamping() const { return _damping; }
float getLifetime() const { return _lifetime; }
const QString& getScript() const { return _script; }
bool getInHand() const { return _inHand; }
bool getShouldDie() const { return _shouldDie; }
const QString& getModelURL() const { return _modelURL; }
float getModelScale() const { return _modelScale; }
const glm::vec3& getModelTranslation() const { return _modelTranslation; }
const glm::quat& getModelRotation() const { return _modelRotation; }
quint64 getLastEdited() const { return _lastEdited; }
uint16_t getChangedBits() const;
/// set position in meter units
void setPosition(const glm::vec3& value) { _position = value; _positionChanged = true; }
/// set velocity in meter units
void setVelocity(const glm::vec3& value) { _velocity = value; _velocityChanged = true; }
void setColor(const xColor& value) { _color = value; _colorChanged = true; }
void setRadius(float value) { _radius = value; _radiusChanged = true; }
/// set gravity in meter units
void setGravity(const glm::vec3& value) { _gravity = value; _gravityChanged = true; }
void setInHand(bool inHand) { _inHand = inHand; _inHandChanged = true; }
void setDamping(float value) { _damping = value; _dampingChanged = true; }
void setShouldDie(bool shouldDie) { _shouldDie = shouldDie; _shouldDieChanged = true; }
void setLifetime(float value) { _lifetime = value; _lifetimeChanged = true; }
void setScript(const QString& updateScript) { _script = updateScript; _scriptChanged = true; }
// model related properties
void setModelURL(const QString& url) { _modelURL = url; _modelURLChanged = true; }
void setModelScale(float scale) { _modelScale = scale; _modelScaleChanged = true; }
void setModelTranslation(const glm::vec3& translation) { _modelTranslation = translation;
_modelTranslationChanged = true; }
void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; _modelRotationChanged = true; }
/// used by ParticleScriptingInterface to return ParticleProperties for unknown particles
void setIsUnknownID() { _id = UNKNOWN_PARTICLE_ID; _idSet = true; }
private:
glm::vec3 _position;
xColor _color;
float _radius;
glm::vec3 _velocity;
glm::vec3 _gravity;
float _damping;
float _lifetime;
QString _script;
bool _inHand;
bool _shouldDie;
QString _modelURL;
float _modelScale;
glm::vec3 _modelTranslation;
glm::quat _modelRotation;
uint32_t _id;
bool _idSet;
quint64 _lastEdited;
bool _positionChanged;
bool _colorChanged;
bool _radiusChanged;
bool _velocityChanged;
bool _gravityChanged;
bool _dampingChanged;
bool _lifetimeChanged;
bool _scriptChanged;
bool _inHandChanged;
bool _shouldDieChanged;
bool _modelURLChanged;
bool _modelScaleChanged;
bool _modelTranslationChanged;
bool _modelRotationChanged;
bool _defaultSettings;
};
Q_DECLARE_METATYPE(ParticleProperties);
QScriptValue ParticlePropertiesToScriptValue(QScriptEngine* engine, const ParticleProperties& properties);
void ParticlePropertiesFromScriptValue(const QScriptValue &object, ParticleProperties& properties);
/// Abstract ID for editing particles. Used in Particle JS API - When particles are created in the JS api, they are given a
/// local creatorTokenID, the actual id for the particle is not known until the server responds to the creator with the
/// correct mapping. This class works with the scripting API an allows the developer to edit particles they created.
class ParticleID {
public:
ParticleID() :
id(NEW_PARTICLE), creatorTokenID(UNKNOWN_TOKEN), isKnownID(false) { };
ParticleID(uint32_t id, uint32_t creatorTokenID, bool isKnownID) :
id(id), creatorTokenID(creatorTokenID), isKnownID(isKnownID) { };
ParticleID(uint32_t id) :
id(id), creatorTokenID(UNKNOWN_TOKEN), isKnownID(true) { };
uint32_t id;
uint32_t creatorTokenID;
bool isKnownID;
};
Q_DECLARE_METATYPE(ParticleID);
Q_DECLARE_METATYPE(QVector<ParticleID>);
QScriptValue ParticleIDtoScriptValue(QScriptEngine* engine, const ParticleID& properties);
void ParticleIDfromScriptValue(const QScriptValue &object, ParticleID& properties);
/// Particle class - this is the actual particle class.
class Particle {
public:
Particle();
Particle(const ParticleID& particleID, const ParticleProperties& properties);
/// creates an NEW particle from an PACKET_TYPE_PARTICLE_ADD_OR_EDIT edit data buffer
static Particle fromEditPacket(const unsigned char* data, int length, int& processedBytes, ParticleTree* tree, bool& valid);
virtual ~Particle();
virtual void init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity,
glm::vec3 gravity = DEFAULT_GRAVITY, float damping = DEFAULT_DAMPING, float lifetime = DEFAULT_LIFETIME,
bool inHand = NOT_IN_HAND, QString updateScript = DEFAULT_SCRIPT, uint32_t id = NEW_PARTICLE);
/// get position in domain scale units (0.0 - 1.0)
const glm::vec3& getPosition() const { return _position; }
const rgbColor& getColor() const { return _color; }
xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; }
/// get radius in domain scale units (0.0 - 1.0)
float getRadius() const { return _radius; }
float getMass() const { return _mass; }
/// get velocity in domain scale units (0.0 - 1.0)
const glm::vec3& getVelocity() const { return _velocity; }
/// get gravity in domain scale units (0.0 - 1.0)
const glm::vec3& getGravity() const { return _gravity; }
bool getInHand() const { return _inHand; }
float getDamping() const { return _damping; }
float getLifetime() const { return _lifetime; }
// model related properties
bool hasModel() const { return !_modelURL.isEmpty(); }
const QString& getModelURL() const { return _modelURL; }
float getModelScale() const { return _modelScale; }
const glm::vec3& getModelTranslation() const { return _modelTranslation; }
const glm::quat& getModelRotation() const { return _modelRotation; }
ParticleID getParticleID() const { return ParticleID(getID(), getCreatorTokenID(), getID() != UNKNOWN_PARTICLE_ID); }
ParticleProperties getProperties() const;
/// The last updated/simulated time of this particle from the time perspective of the authoritative server/source
quint64 getLastUpdated() const { return _lastUpdated; }
/// The last edited time of this particle from the time perspective of the authoritative server/source
quint64 getLastEdited() const { return _lastEdited; }
void setLastEdited(quint64 lastEdited) { _lastEdited = lastEdited; }
/// lifetime of the particle in seconds
float getAge() const { return static_cast<float>(usecTimestampNow() - _created) / static_cast<float>(USECS_PER_SECOND); }
float getEditedAgo() const { return static_cast<float>(usecTimestampNow() - _lastEdited) / static_cast<float>(USECS_PER_SECOND); }
uint32_t getID() const { return _id; }
void setID(uint32_t id) { _id = id; }
bool getShouldDie() const { return _shouldDie; }
QString getScript() const { return _script; }
uint32_t getCreatorTokenID() const { return _creatorTokenID; }
bool isNewlyCreated() const { return _newlyCreated; }
/// set position in domain scale units (0.0 - 1.0)
void setPosition(const glm::vec3& value) { _position = value; }
/// set velocity in domain scale units (0.0 - 1.0)
void setVelocity(const glm::vec3& value) { _velocity = value; }
void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); }
void setColor(const xColor& value) {
_color[RED_INDEX] = value.red;
_color[GREEN_INDEX] = value.green;
_color[BLUE_INDEX] = value.blue;
}
/// set radius in domain scale units (0.0 - 1.0)
void setRadius(float value) { _radius = value; }
void setMass(float value);
/// set gravity in domain scale units (0.0 - 1.0)
void setGravity(const glm::vec3& value) { _gravity = value; }
void setInHand(bool inHand) { _inHand = inHand; }
void setDamping(float value) { _damping = value; }
void setShouldDie(bool shouldDie) { _shouldDie = shouldDie; }
void setLifetime(float value) { _lifetime = value; }
void setScript(QString updateScript) { _script = updateScript; }
void setCreatorTokenID(uint32_t creatorTokenID) { _creatorTokenID = creatorTokenID; }
// model related properties
void setModelURL(const QString& url) { _modelURL = url; }
void setModelScale(float scale) { _modelScale = scale; }
void setModelTranslation(const glm::vec3& translation) { _modelTranslation = translation; }
void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; }
void setProperties(const ParticleProperties& properties);
bool appendParticleData(OctreePacketData* packetData) const;
int readParticleDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args);
static int expectedBytes();
static bool encodeParticleEditMessageDetails(PacketType command, ParticleID id, const ParticleProperties& details,
unsigned char* bufferOut, int sizeIn, int& sizeOut);
static void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew);
void applyHardCollision(const CollisionInfo& collisionInfo);
void update(const quint64& now);
void collisionWithParticle(Particle* other);
void collisionWithVoxel(VoxelDetail* voxel);
void debugDump() const;
// similar to assignment/copy, but it handles keeping lifetime accurate
void copyChangedProperties(const Particle& other);
static VoxelEditPacketSender* getVoxelEditPacketSender() { return _voxelEditSender; }
static ParticleEditPacketSender* getParticleEditPacketSender() { return _particleEditSender; }
static void setVoxelEditPacketSender(VoxelEditPacketSender* senderInterface)
{ _voxelEditSender = senderInterface; }
static void setParticleEditPacketSender(ParticleEditPacketSender* senderInterface)
{ _particleEditSender = senderInterface; }
// these methods allow you to create particles, and later edit them.
static uint32_t getIDfromCreatorTokenID(uint32_t creatorTokenID);
static uint32_t getNextCreatorTokenID();
static void handleAddParticleResponse(const QByteArray& packet);
protected:
static VoxelEditPacketSender* _voxelEditSender;
static ParticleEditPacketSender* _particleEditSender;
void startParticleScriptContext(ScriptEngine& engine, ParticleScriptObject& particleScriptable);
void endParticleScriptContext(ScriptEngine& engine, ParticleScriptObject& particleScriptable);
void executeUpdateScripts();
void setAge(float age);
glm::vec3 _position;
rgbColor _color;
float _radius;
float _mass;
glm::vec3 _velocity;
uint32_t _id;
static uint32_t _nextID;
bool _shouldDie;
glm::vec3 _gravity;
float _damping;
float _lifetime;
QString _script;
bool _inHand;
// model related items
QString _modelURL;
float _modelScale;
glm::vec3 _modelTranslation;
glm::quat _modelRotation;
uint32_t _creatorTokenID;
bool _newlyCreated;
quint64 _lastUpdated;
quint64 _lastEdited;
// this doesn't go on the wire, we send it as lifetime
quint64 _created;
// used by the static interfaces for creator token ids
static uint32_t _nextCreatorTokenID;
static std::map<uint32_t,uint32_t> _tokenIDsToIDs;
};
/// Scriptable interface to a single Particle object. Used exclusively in the JavaScript API for interacting with single
/// Particles.
class ParticleScriptObject : public QObject {
Q_OBJECT
public:
ParticleScriptObject(Particle* particle) { _particle = particle; }
//~ParticleScriptObject() { qDebug() << "~ParticleScriptObject() this=" << this; }
void emitUpdate() { emit update(); }
void emitCollisionWithParticle(QObject* other) { emit collisionWithParticle(other); }
void emitCollisionWithVoxel(const VoxelDetail& voxel) { emit collisionWithVoxel(voxel); }
public slots:
unsigned int getID() const { return _particle->getID(); }
/// get position in meter units
glm::vec3 getPosition() const { return _particle->getPosition() * (float)TREE_SCALE; }
/// get velocity in meter units
glm::vec3 getVelocity() const { return _particle->getVelocity() * (float)TREE_SCALE; }
xColor getColor() const { return _particle->getXColor(); }
/// get gravity in meter units
glm::vec3 getGravity() const { return _particle->getGravity() * (float)TREE_SCALE; }
float getDamping() const { return _particle->getDamping(); }
/// get radius in meter units
float getRadius() const { return _particle->getRadius() * (float)TREE_SCALE; }
bool getShouldDie() { return _particle->getShouldDie(); }
float getAge() const { return _particle->getAge(); }
float getLifetime() const { return _particle->getLifetime(); }
ParticleProperties getProperties() const { return _particle->getProperties(); }
/// set position in meter units
void setPosition(glm::vec3 value) { _particle->setPosition(value / (float)TREE_SCALE); }
/// set velocity in meter units
void setVelocity(glm::vec3 value) { _particle->setVelocity(value / (float)TREE_SCALE); }
/// set gravity in meter units
void setGravity(glm::vec3 value) { _particle->setGravity(value / (float)TREE_SCALE); }
void setDamping(float value) { _particle->setDamping(value); }
void setColor(xColor value) { _particle->setColor(value); }
/// set radius in meter units
void setRadius(float value) { _particle->setRadius(value / (float)TREE_SCALE); }
void setShouldDie(bool value) { _particle->setShouldDie(value); }
void setScript(const QString& script) { _particle->setScript(script); }
void setLifetime(float value) const { return _particle->setLifetime(value); }
void setProperties(const ParticleProperties& properties) { return _particle->setProperties(properties); }
signals:
void update();
void collisionWithVoxel(const VoxelDetail& voxel);
void collisionWithParticle(QObject* other);
private:
Particle* _particle;
};
#endif /* defined(__hifi__Particle__) */