mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-04 03:24:46 +02:00
472 lines
16 KiB
C++
472 lines
16 KiB
C++
//
|
|
// Particle.cpp
|
|
// hifi
|
|
//
|
|
// Created by Brad Hefta-Gaub on 12/4/13.
|
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
|
//
|
|
//
|
|
|
|
#include <QtScript/QScriptEngine>
|
|
#include <QtCore/QObject>
|
|
|
|
#include <RegisteredMetaTypes.h>
|
|
#include <SharedUtil.h> // usecTimestampNow()
|
|
|
|
#include "Particle.h"
|
|
|
|
uint32_t Particle::_nextID = 0;
|
|
|
|
|
|
Particle::Particle(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, glm::vec3 gravity,
|
|
float damping, bool inHand, QString updateScript, uint32_t id) {
|
|
|
|
init(position, radius, color, velocity, gravity, damping, inHand, updateScript, id);
|
|
}
|
|
|
|
Particle::Particle() {
|
|
rgbColor noColor = { 0, 0, 0 };
|
|
init(glm::vec3(0,0,0), 0, noColor, glm::vec3(0,0,0),
|
|
DEFAULT_GRAVITY, DEFAULT_DAMPING, NOT_IN_HAND, DEFAULT_SCRIPT, NEW_PARTICLE);
|
|
}
|
|
|
|
Particle::~Particle() {
|
|
}
|
|
|
|
void Particle::init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, glm::vec3 gravity,
|
|
float damping, bool inHand, QString updateScript, uint32_t id) {
|
|
if (id == NEW_PARTICLE) {
|
|
_id = _nextID;
|
|
_nextID++;
|
|
} else {
|
|
_id = id;
|
|
}
|
|
uint64_t now = usecTimestampNow();
|
|
_lastEdited = now;
|
|
_lastSimulated = now;
|
|
_created = now; // will get updated as appropriate in setLifetime()
|
|
|
|
_position = position;
|
|
_radius = radius;
|
|
memcpy(_color, color, sizeof(_color));
|
|
_velocity = velocity;
|
|
_damping = damping;
|
|
_gravity = gravity;
|
|
_updateScript = updateScript;
|
|
_inHand = inHand;
|
|
}
|
|
|
|
|
|
bool Particle::appendParticleData(OctreePacketData* packetData) const {
|
|
|
|
bool success = packetData->appendValue(getID());
|
|
|
|
//printf("Particle::appendParticleData()... getID()=%d\n", getID());
|
|
|
|
if (success) {
|
|
success = packetData->appendValue(getLifetime());
|
|
}
|
|
if (success) {
|
|
success = packetData->appendValue(getLastEdited());
|
|
}
|
|
if (success) {
|
|
success = packetData->appendValue(getRadius());
|
|
}
|
|
if (success) {
|
|
success = packetData->appendPosition(getPosition());
|
|
}
|
|
if (success) {
|
|
success = packetData->appendColor(getColor());
|
|
}
|
|
if (success) {
|
|
success = packetData->appendValue(getVelocity());
|
|
}
|
|
if (success) {
|
|
success = packetData->appendValue(getGravity());
|
|
}
|
|
if (success) {
|
|
success = packetData->appendValue(getDamping());
|
|
}
|
|
if (success) {
|
|
success = packetData->appendValue(getInHand());
|
|
}
|
|
if (success) {
|
|
uint16_t scriptLength = _updateScript.size() + 1; // include NULL
|
|
success = packetData->appendValue(scriptLength);
|
|
if (success) {
|
|
success = packetData->appendRawData((const unsigned char*)qPrintable(_updateScript), scriptLength);
|
|
}
|
|
}
|
|
return success;
|
|
}
|
|
|
|
int Particle::expectedBytes() {
|
|
int expectedBytes = sizeof(uint32_t) // id
|
|
+ sizeof(float) // lifetime
|
|
+ sizeof(uint64_t) // lastedited
|
|
+ sizeof(float) // radius
|
|
+ sizeof(glm::vec3) // position
|
|
+ sizeof(rgbColor) // color
|
|
+ sizeof(glm::vec3) // velocity
|
|
+ sizeof(glm::vec3) // gravity
|
|
+ sizeof(float) // damping
|
|
+ sizeof(bool); // inhand
|
|
// potentially more...
|
|
return expectedBytes;
|
|
}
|
|
|
|
int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) {
|
|
int bytesRead = 0;
|
|
if (bytesLeftToRead >= expectedBytes()) {
|
|
const unsigned char* dataAt = data;
|
|
|
|
// id
|
|
memcpy(&_id, dataAt, sizeof(_id));
|
|
dataAt += sizeof(_id);
|
|
bytesRead += sizeof(_id);
|
|
|
|
// lifetime
|
|
float lifetime;
|
|
memcpy(&lifetime, dataAt, sizeof(lifetime));
|
|
dataAt += sizeof(lifetime);
|
|
bytesRead += sizeof(lifetime);
|
|
setLifetime(lifetime);
|
|
|
|
// _lastEdited
|
|
memcpy(&_lastEdited, dataAt, sizeof(_lastEdited));
|
|
dataAt += sizeof(_lastEdited);
|
|
bytesRead += sizeof(_lastEdited);
|
|
|
|
// radius
|
|
memcpy(&_radius, dataAt, sizeof(_radius));
|
|
dataAt += sizeof(_radius);
|
|
bytesRead += sizeof(_radius);
|
|
|
|
// position
|
|
memcpy(&_position, dataAt, sizeof(_position));
|
|
dataAt += sizeof(_position);
|
|
bytesRead += sizeof(_position);
|
|
|
|
// color
|
|
memcpy(_color, dataAt, sizeof(_color));
|
|
dataAt += sizeof(_color);
|
|
bytesRead += sizeof(_color);
|
|
|
|
// velocity
|
|
memcpy(&_velocity, dataAt, sizeof(_velocity));
|
|
dataAt += sizeof(_velocity);
|
|
bytesRead += sizeof(_velocity);
|
|
|
|
// gravity
|
|
memcpy(&_gravity, dataAt, sizeof(_gravity));
|
|
dataAt += sizeof(_gravity);
|
|
bytesRead += sizeof(_gravity);
|
|
|
|
// damping
|
|
memcpy(&_damping, dataAt, sizeof(_damping));
|
|
dataAt += sizeof(_damping);
|
|
bytesRead += sizeof(_damping);
|
|
|
|
// inHand
|
|
memcpy(&_inHand, dataAt, sizeof(_inHand));
|
|
dataAt += sizeof(_inHand);
|
|
bytesRead += sizeof(_inHand);
|
|
|
|
// script
|
|
uint16_t scriptLength;
|
|
memcpy(&scriptLength, dataAt, sizeof(scriptLength));
|
|
dataAt += sizeof(scriptLength);
|
|
bytesRead += sizeof(scriptLength);
|
|
QString tempString((const char*)dataAt);
|
|
_updateScript = tempString;
|
|
dataAt += scriptLength;
|
|
bytesRead += scriptLength;
|
|
|
|
//printf("Particle::readParticleDataFromBuffer()... "); debugDump();
|
|
}
|
|
return bytesRead;
|
|
}
|
|
|
|
|
|
Particle Particle::fromEditPacket(unsigned char* data, int length, int& processedBytes) {
|
|
Particle newParticle; // id and _lastSimulated will get set here...
|
|
unsigned char* dataAt = data;
|
|
processedBytes = 0;
|
|
|
|
// the first part of the data is our octcode...
|
|
int octets = numberOfThreeBitSectionsInCode(data);
|
|
int lengthOfOctcode = bytesRequiredForCodeLength(octets);
|
|
|
|
// we don't actually do anything with this octcode...
|
|
dataAt += lengthOfOctcode;
|
|
processedBytes += lengthOfOctcode;
|
|
|
|
// id
|
|
uint32_t editID;
|
|
memcpy(&editID, dataAt, sizeof(editID));
|
|
dataAt += sizeof(editID);
|
|
processedBytes += sizeof(editID);
|
|
|
|
// special case for handling "new" particles
|
|
if (editID == NEW_PARTICLE) {
|
|
// If this is a NEW_PARTICLE, then we assume that there's an additional uint32_t creatorToken, that
|
|
// we want to send back to the creator as an map to the actual id
|
|
uint32_t creatorTokenID;
|
|
memcpy(&creatorTokenID, dataAt, sizeof(creatorTokenID));
|
|
dataAt += sizeof(creatorTokenID);
|
|
processedBytes += sizeof(creatorTokenID);
|
|
newParticle.setCreatorTokenID(creatorTokenID);
|
|
newParticle._newlyCreated = true;
|
|
|
|
newParticle.setLifetime(0); // this guy is new!
|
|
|
|
} else {
|
|
newParticle._id = editID;
|
|
newParticle._newlyCreated = false;
|
|
}
|
|
|
|
// lastEdited
|
|
memcpy(&newParticle._lastEdited, dataAt, sizeof(newParticle._lastEdited));
|
|
dataAt += sizeof(newParticle._lastEdited);
|
|
processedBytes += sizeof(newParticle._lastEdited);
|
|
|
|
// radius
|
|
memcpy(&newParticle._radius, dataAt, sizeof(newParticle._radius));
|
|
dataAt += sizeof(newParticle._radius);
|
|
processedBytes += sizeof(newParticle._radius);
|
|
|
|
// position
|
|
memcpy(&newParticle._position, dataAt, sizeof(newParticle._position));
|
|
dataAt += sizeof(newParticle._position);
|
|
processedBytes += sizeof(newParticle._position);
|
|
|
|
// color
|
|
memcpy(newParticle._color, dataAt, sizeof(newParticle._color));
|
|
dataAt += sizeof(newParticle._color);
|
|
processedBytes += sizeof(newParticle._color);
|
|
|
|
// velocity
|
|
memcpy(&newParticle._velocity, dataAt, sizeof(newParticle._velocity));
|
|
dataAt += sizeof(newParticle._velocity);
|
|
processedBytes += sizeof(newParticle._velocity);
|
|
|
|
// gravity
|
|
memcpy(&newParticle._gravity, dataAt, sizeof(newParticle._gravity));
|
|
dataAt += sizeof(newParticle._gravity);
|
|
processedBytes += sizeof(newParticle._gravity);
|
|
|
|
// damping
|
|
memcpy(&newParticle._damping, dataAt, sizeof(newParticle._damping));
|
|
dataAt += sizeof(newParticle._damping);
|
|
processedBytes += sizeof(newParticle._damping);
|
|
|
|
// inHand
|
|
memcpy(&newParticle._inHand, dataAt, sizeof(newParticle._inHand));
|
|
dataAt += sizeof(newParticle._inHand);
|
|
processedBytes += sizeof(newParticle._inHand);
|
|
|
|
// script
|
|
uint16_t scriptLength;
|
|
memcpy(&scriptLength, dataAt, sizeof(scriptLength));
|
|
dataAt += sizeof(scriptLength);
|
|
processedBytes += sizeof(scriptLength);
|
|
QString tempString((const char*)dataAt);
|
|
newParticle._updateScript = tempString;
|
|
dataAt += scriptLength;
|
|
processedBytes += scriptLength;
|
|
|
|
const bool wantDebugging = false;
|
|
if (wantDebugging) {
|
|
printf("Particle::fromEditPacket()...\n");
|
|
printf(" Particle id in packet:%u\n", editID);
|
|
newParticle.debugDump();
|
|
}
|
|
|
|
return newParticle;
|
|
}
|
|
|
|
void Particle::debugDump() const {
|
|
printf("Particle id :%u\n", _id);
|
|
printf(" lifetime:%f\n", getLifetime());
|
|
printf(" last edited:%llu\n", _lastEdited);
|
|
printf(" position:%f,%f,%f\n", _position.x, _position.y, _position.z);
|
|
printf(" velocity:%f,%f,%f\n", _velocity.x, _velocity.y, _velocity.z);
|
|
printf(" gravity:%f,%f,%f\n", _gravity.x, _gravity.y, _gravity.z);
|
|
}
|
|
|
|
bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, const ParticleDetail* details,
|
|
unsigned char* bufferOut, int sizeIn, int& sizeOut) {
|
|
|
|
bool success = true; // assume the best
|
|
unsigned char* copyAt = bufferOut;
|
|
sizeOut = 0;
|
|
|
|
for (int i = 0; i < count && success; i++) {
|
|
// get the octal code for the particle
|
|
unsigned char* octcode = pointToOctalCode(details[i].position.x, details[i].position.y,
|
|
details[i].position.z, details[i].radius);
|
|
|
|
int octets = numberOfThreeBitSectionsInCode(octcode);
|
|
int lengthOfOctcode = bytesRequiredForCodeLength(octets);
|
|
int lenfthOfEditData = lengthOfOctcode + expectedBytes();
|
|
|
|
// make sure we have room to copy this particle
|
|
if (sizeOut + lenfthOfEditData > sizeIn) {
|
|
success = false;
|
|
} else {
|
|
// add it to our message
|
|
memcpy(copyAt, octcode, lengthOfOctcode);
|
|
copyAt += lengthOfOctcode;
|
|
sizeOut += lengthOfOctcode;
|
|
|
|
// Now add our edit content details...
|
|
|
|
// id
|
|
memcpy(copyAt, &details[i].id, sizeof(details[i].id));
|
|
copyAt += sizeof(details[i].id);
|
|
sizeOut += sizeof(details[i].id);
|
|
// special case for handling "new" particles
|
|
if (details[i].id == NEW_PARTICLE) {
|
|
// If this is a NEW_PARTICLE, then we assume that there's an additional uint32_t creatorToken, that
|
|
// we want to send back to the creator as an map to the actual id
|
|
memcpy(copyAt, &details[i].creatorTokenID, sizeof(details[i].creatorTokenID));
|
|
copyAt += sizeof(details[i].creatorTokenID);
|
|
sizeOut += sizeof(details[i].creatorTokenID);
|
|
}
|
|
|
|
// lastEdited
|
|
memcpy(copyAt, &details[i].lastEdited, sizeof(details[i].lastEdited));
|
|
copyAt += sizeof(details[i].lastEdited);
|
|
sizeOut += sizeof(details[i].lastEdited);
|
|
|
|
// radius
|
|
memcpy(copyAt, &details[i].radius, sizeof(details[i].radius));
|
|
copyAt += sizeof(details[i].radius);
|
|
sizeOut += sizeof(details[i].radius);
|
|
|
|
// position
|
|
memcpy(copyAt, &details[i].position, sizeof(details[i].position));
|
|
copyAt += sizeof(details[i].position);
|
|
sizeOut += sizeof(details[i].position);
|
|
|
|
// color
|
|
memcpy(copyAt, details[i].color, sizeof(details[i].color));
|
|
copyAt += sizeof(details[i].color);
|
|
sizeOut += sizeof(details[i].color);
|
|
|
|
// velocity
|
|
memcpy(copyAt, &details[i].velocity, sizeof(details[i].velocity));
|
|
copyAt += sizeof(details[i].velocity);
|
|
sizeOut += sizeof(details[i].velocity);
|
|
|
|
// gravity
|
|
memcpy(copyAt, &details[i].gravity, sizeof(details[i].gravity));
|
|
copyAt += sizeof(details[i].gravity);
|
|
sizeOut += sizeof(details[i].gravity);
|
|
|
|
// damping
|
|
memcpy(copyAt, &details[i].damping, sizeof(details[i].damping));
|
|
copyAt += sizeof(details[i].damping);
|
|
sizeOut += sizeof(details[i].damping);
|
|
|
|
// inHand
|
|
memcpy(copyAt, &details[i].inHand, sizeof(details[i].inHand));
|
|
copyAt += sizeof(details[i].inHand);
|
|
sizeOut += sizeof(details[i].inHand);
|
|
|
|
// script
|
|
uint16_t scriptLength = details[i].updateScript.size() + 1;
|
|
memcpy(copyAt, &scriptLength, sizeof(scriptLength));
|
|
copyAt += sizeof(scriptLength);
|
|
sizeOut += sizeof(scriptLength);
|
|
memcpy(copyAt, qPrintable(details[i].updateScript), scriptLength);
|
|
copyAt += scriptLength;
|
|
sizeOut += scriptLength;
|
|
|
|
bool wantDebugging = false;
|
|
if (wantDebugging) {
|
|
printf("encodeParticleEditMessageDetails()....\n");
|
|
printf("Particle id :%u\n", details[i].id);
|
|
printf(" nextID:%u\n", _nextID);
|
|
}
|
|
}
|
|
// cleanup
|
|
delete[] octcode;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
|
|
void Particle::update() {
|
|
uint64_t now = usecTimestampNow();
|
|
uint64_t elapsed = now - _lastSimulated;
|
|
uint64_t USECS_PER_SECOND = 1000 * 1000;
|
|
float timeElapsed = (float)((float)elapsed/(float)USECS_PER_SECOND);
|
|
|
|
// calculate our default shouldDie state... then allow script to change it if it wants...
|
|
float velocityScalar = glm::length(getVelocity());
|
|
const float STILL_MOVING = 0.05 / TREE_SCALE;
|
|
bool isStillMoving = (velocityScalar > STILL_MOVING);
|
|
const uint64_t REALLY_OLD = 30 * 1000 * 1000;
|
|
bool isReallyOld = (getLifetime() > REALLY_OLD);
|
|
bool isInHand = getInHand();
|
|
bool shouldDie = !isInHand && !isStillMoving && isReallyOld;
|
|
setShouldDie(shouldDie);
|
|
|
|
runScript(); // allow the javascript to alter our state
|
|
|
|
// If the ball is in hand, it doesn't move or have gravity effect it
|
|
if (!isInHand) {
|
|
_position += _velocity * timeElapsed;
|
|
|
|
// handle bounces off the ground...
|
|
if (_position.y <= 0) {
|
|
_velocity = _velocity * glm::vec3(1,-1,1);
|
|
_position.y = 0;
|
|
}
|
|
|
|
// handle gravity....
|
|
_velocity += _gravity * timeElapsed;
|
|
|
|
// handle damping
|
|
glm::vec3 dampingResistance = _velocity * _damping;
|
|
_velocity -= dampingResistance * timeElapsed;
|
|
//printf("applying damping to Particle timeElapsed=%f\n",timeElapsed);
|
|
}
|
|
|
|
_lastSimulated = now;
|
|
}
|
|
|
|
void Particle::runScript() {
|
|
if (!_updateScript.isEmpty()) {
|
|
|
|
//qDebug() << "Script: " << _updateScript << "\n";
|
|
|
|
QScriptEngine engine;
|
|
|
|
// register meta-type for glm::vec3 and rgbColor conversions
|
|
registerMetaTypes(&engine);
|
|
|
|
ParticleScriptObject particleScriptable(this);
|
|
QScriptValue particleValue = engine.newQObject(&particleScriptable);
|
|
engine.globalObject().setProperty("Particle", particleValue);
|
|
|
|
QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE));
|
|
engine.globalObject().setProperty("TREE_SCALE", TREE_SCALE);
|
|
|
|
//qDebug() << "Downloaded script:" << _updateScript << "\n";
|
|
QScriptValue result = engine.evaluate(_updateScript);
|
|
//qDebug() << "Evaluated script.\n";
|
|
|
|
if (engine.hasUncaughtException()) {
|
|
int line = engine.uncaughtExceptionLineNumber();
|
|
qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
void Particle::setLifetime(float lifetime) {
|
|
uint64_t lifetimeInUsecs = lifetime * USECS_PER_SECOND;
|
|
_created = usecTimestampNow() - lifetimeInUsecs;
|
|
}
|