mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 12:04:18 +02:00
Merge pull request #1731 from ZappoMan/more_scripting
I looked the code over and also did some basic testing (started servers on localhost, logged in with interface).
This commit is contained in:
commit
3cfca795e2
45 changed files with 1582 additions and 346 deletions
|
@ -43,6 +43,12 @@ void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr&
|
|||
dataByteArray.size());
|
||||
break;
|
||||
}
|
||||
} else if (dataByteArray[0] == PACKET_TYPE_PARTICLE_ADD_RESPONSE) {
|
||||
// this will keep creatorTokenIDs to IDs mapped correctly
|
||||
Particle::handleAddParticleResponse((unsigned char*) dataByteArray.data(), dataByteArray.size());
|
||||
|
||||
// also give our local particle tree a chance to remap any internal locally created particles
|
||||
_particleTree.handleAddParticleResponse((unsigned char*) dataByteArray.data(), dataByteArray.size());
|
||||
} else {
|
||||
NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size());
|
||||
}
|
||||
|
@ -88,6 +94,9 @@ void Agent::run() {
|
|||
connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes()));
|
||||
pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000);
|
||||
|
||||
// tell our script engine about our local particle tree
|
||||
_scriptEngine.getParticlesScriptingInterface()->setParticleTree(&_particleTree);
|
||||
|
||||
// setup an Avatar for the script to use
|
||||
AvatarData scriptedAvatar;
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <QtCore/QObject>
|
||||
#include <QtCore/QUrl>
|
||||
|
||||
#include <ParticleTree.h>
|
||||
#include <ScriptEngine.h>
|
||||
#include <ThreadedAssignment.h>
|
||||
|
||||
|
@ -32,6 +33,7 @@ signals:
|
|||
void willSendVisualDataCallback();
|
||||
private:
|
||||
ScriptEngine _scriptEngine;
|
||||
ParticleTree _particleTree;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Agent__) */
|
||||
|
|
105
examples/controllerExample.js
Normal file
105
examples/controllerExample.js
Normal file
|
@ -0,0 +1,105 @@
|
|||
//
|
||||
// controllerExample.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 1/28/14.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// This is an example script that demonstrates use of the Controller class
|
||||
//
|
||||
//
|
||||
|
||||
// initialize our triggers
|
||||
var triggerPulled = new Array();
|
||||
var numberOfTriggers = Controller.getNumberOfTriggers();
|
||||
for (t = 0; t < numberOfTriggers; t++) {
|
||||
triggerPulled[t] = false;
|
||||
}
|
||||
|
||||
function checkController() {
|
||||
var numberOfTriggers = Controller.getNumberOfTriggers();
|
||||
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
|
||||
var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
|
||||
|
||||
// this is expected for hydras
|
||||
if (numberOfTriggers == 2 && controllersPerTrigger == 2) {
|
||||
for (var t = 0; t < numberOfTriggers; t++) {
|
||||
var triggerValue = Controller.getTriggerValue(t);
|
||||
|
||||
if (triggerPulled[t]) {
|
||||
// must release to at least 0.1
|
||||
if (triggerValue < 0.1) {
|
||||
triggerPulled[t] = false; // unpulled
|
||||
}
|
||||
} else {
|
||||
// must pull to at least 0.9
|
||||
if (triggerValue > 0.9) {
|
||||
triggerPulled[t] = true; // pulled
|
||||
triggerToggled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (triggerToggled) {
|
||||
print("a trigger was toggled");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function keyPressEvent(event) {
|
||||
print("keyPressEvent event.key=" + event.key);
|
||||
if (event.key == "A".charCodeAt(0)) {
|
||||
print("the A key was pressed");
|
||||
}
|
||||
if (event.key == " ".charCodeAt(0)) {
|
||||
print("the <space> key was pressed");
|
||||
}
|
||||
}
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
print("mouseMoveEvent event.x,y=" + event.x + ", " + event.y);
|
||||
}
|
||||
|
||||
function touchBeginEvent(event) {
|
||||
print("touchBeginEvent event.x,y=" + event.x + ", " + event.y);
|
||||
}
|
||||
|
||||
function touchUpdateEvent(event) {
|
||||
print("touchUpdateEvent event.x,y=" + event.x + ", " + event.y);
|
||||
}
|
||||
|
||||
function touchEndEvent(event) {
|
||||
print("touchEndEvent event.x,y=" + event.x + ", " + event.y);
|
||||
}
|
||||
|
||||
// register the call back so it fires before each data send
|
||||
Agent.willSendVisualDataCallback.connect(checkController);
|
||||
|
||||
// Map keyPress and mouse move events to our callbacks
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
var AKeyEvent = {
|
||||
key: "A".charCodeAt(0),
|
||||
isShifted: false,
|
||||
isMeta: false
|
||||
};
|
||||
|
||||
// prevent the A key from going through to the application
|
||||
Controller.captureKeyEvents(AKeyEvent);
|
||||
|
||||
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
|
||||
// Map touch events to our callbacks
|
||||
Controller.touchBeginEvent.connect(touchBeginEvent);
|
||||
Controller.touchUpdateEvent.connect(touchUpdateEvent);
|
||||
Controller.touchEndEvent.connect(touchEndEvent);
|
||||
|
||||
// disable the standard application for touch events
|
||||
Controller.captureTouchEvents();
|
||||
|
||||
function scriptEnding() {
|
||||
// re-enabled the standard application for touch events
|
||||
Controller.releaseTouchEvents();
|
||||
}
|
||||
|
||||
Agent.scriptEnding.connect(scriptEnding);
|
|
@ -9,6 +9,9 @@
|
|||
//
|
||||
|
||||
var count = 0;
|
||||
var moveUntil = 2000;
|
||||
var stopAfter = moveUntil + 100;
|
||||
var expectedLifetime = (moveUntil/60) + 1; // 1 second after done moving...
|
||||
|
||||
var originalProperties = {
|
||||
position: { x: 10,
|
||||
|
@ -28,7 +31,9 @@ var originalProperties = {
|
|||
|
||||
color: { red: 0,
|
||||
green: 255,
|
||||
blue: 0 }
|
||||
blue: 0 },
|
||||
|
||||
lifetime: expectedLifetime
|
||||
|
||||
};
|
||||
|
||||
|
@ -38,17 +43,17 @@ var positionDelta = { x: 0.05, y: 0, z: 0 };
|
|||
var particleID = Particles.addParticle(originalProperties);
|
||||
|
||||
function moveParticle() {
|
||||
if (count >= 100) {
|
||||
if (count >= moveUntil) {
|
||||
//Agent.stop();
|
||||
|
||||
// delete it...
|
||||
if (count == 100) {
|
||||
if (count == moveUntil) {
|
||||
print("calling Particles.deleteParticle()");
|
||||
Particles.deleteParticle(particleID);
|
||||
}
|
||||
|
||||
// stop it...
|
||||
if (count >= 200) {
|
||||
if (count >= stopAfter) {
|
||||
print("calling Agent.stop()");
|
||||
Agent.stop();
|
||||
}
|
||||
|
@ -77,16 +82,6 @@ function moveParticle() {
|
|||
print("newProperties.position = " + newProperties.position.x + "," + newProperties.position.y+ "," + newProperties.position.z);
|
||||
|
||||
Particles.editParticle(particleID, newProperties);
|
||||
|
||||
// also check to see if we can "find" particles...
|
||||
var searchAt = { x: 9, y: 0, z: 0};
|
||||
var searchRadius = 2;
|
||||
var foundParticle = Particles.findClosestParticle(searchAt, searchRadius);
|
||||
if (foundParticle.isKnownID) {
|
||||
print("found particle:" + foundParticle.id);
|
||||
} else {
|
||||
print("could not find particle in or around x=9 to x=11:");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
74
examples/lookWithMouse.js
Normal file
74
examples/lookWithMouse.js
Normal file
|
@ -0,0 +1,74 @@
|
|||
//
|
||||
// lookWithMouse.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 1/28/14.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// This is an example script that demonstrates use of the Controller class
|
||||
//
|
||||
//
|
||||
|
||||
var isMouseDown = false;
|
||||
var lastX = 0;
|
||||
var lastY = 0;
|
||||
var yawFromMouse = 0;
|
||||
var pitchFromMouse = 0;
|
||||
|
||||
function mousePressEvent(event) {
|
||||
print("mousePressEvent event.x,y=" + event.x + ", " + event.y);
|
||||
isMouseDown = true;
|
||||
lastX = event.x;
|
||||
lastY = event.y;
|
||||
}
|
||||
|
||||
function mouseReleaseEvent(event) {
|
||||
print("mouseReleaseEvent event.x,y=" + event.x + ", " + event.y);
|
||||
isMouseDown = false;
|
||||
}
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
print("mouseMoveEvent event.x,y=" + event.x + ", " + event.y);
|
||||
|
||||
if (isMouseDown) {
|
||||
print("isMouseDown... attempting to change pitch...");
|
||||
var MOUSE_YAW_SCALE = -0.25;
|
||||
var MOUSE_PITCH_SCALE = -12.5;
|
||||
var FIXED_MOUSE_TIMESTEP = 0.016;
|
||||
yawFromMouse += ((event.x - lastX) * MOUSE_YAW_SCALE * FIXED_MOUSE_TIMESTEP);
|
||||
pitchFromMouse += ((event.y - lastY) * MOUSE_PITCH_SCALE * FIXED_MOUSE_TIMESTEP);
|
||||
lastX = event.x;
|
||||
lastY = event.y;
|
||||
}
|
||||
}
|
||||
|
||||
function update() {
|
||||
// rotate body yaw for yaw received from mouse
|
||||
MyAvatar.orientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3( { x: 0, y: yawFromMouse, z: 0 } ));
|
||||
yawFromMouse = 0;
|
||||
|
||||
// apply pitch from mouse
|
||||
MyAvatar.headPitch = MyAvatar.headPitch + pitchFromMouse;
|
||||
pitchFromMouse = 0;
|
||||
}
|
||||
|
||||
// Map the mouse events to our functions
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||
|
||||
// disable the standard application for mouse events
|
||||
Controller.captureMouseEvents();
|
||||
|
||||
function scriptEnding() {
|
||||
// re-enabled the standard application for mouse events
|
||||
Controller.releaseMouseEvents();
|
||||
}
|
||||
|
||||
MyAvatar.bodyYaw = 0;
|
||||
MyAvatar.bodyPitch = 0;
|
||||
MyAvatar.bodyRoll = 0;
|
||||
|
||||
// would be nice to change to update
|
||||
Agent.willSendVisualDataCallback.connect(update);
|
||||
Agent.scriptEnding.connect(scriptEnding);
|
51
examples/particleModelExample.js
Normal file
51
examples/particleModelExample.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
// particleModelExample.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 1/28/14.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// This is an example script that demonstrates creating and editing a particle
|
||||
//
|
||||
|
||||
var count = 0;
|
||||
var stopAfter = 100;
|
||||
|
||||
var modelProperties = {
|
||||
position: { x: 1, y: 1, z: 1 },
|
||||
velocity: { x: 0.5, y: 0, z: 0.5 },
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
damping: 0,
|
||||
radius : 0.25,
|
||||
modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX",
|
||||
lifetime: 20
|
||||
};
|
||||
|
||||
var ballProperties = {
|
||||
position: { x: 1, y: 0.5, z: 1 },
|
||||
velocity: { x: 0.5, y: 0, z: 0.5 },
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
damping: 0,
|
||||
radius : 0.25,
|
||||
color: { red: 255, green: 0, blue: 0 },
|
||||
lifetime: 20
|
||||
};
|
||||
|
||||
var modelParticleID = Particles.addParticle(modelProperties);
|
||||
var ballParticleID = Particles.addParticle(ballProperties);
|
||||
|
||||
function endAfterAWhile() {
|
||||
// stop it...
|
||||
if (count >= stopAfter) {
|
||||
print("calling Agent.stop()");
|
||||
Agent.stop();
|
||||
}
|
||||
|
||||
print("count =" + count);
|
||||
count++;
|
||||
}
|
||||
|
||||
|
||||
// register the call back so it fires before each data send
|
||||
Agent.willSendVisualDataCallback.connect(endAfterAWhile);
|
||||
|
|
@ -112,6 +112,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
QApplication(argc, argv),
|
||||
_window(new QMainWindow(desktop())),
|
||||
_glWidget(new GLCanvas()),
|
||||
_statsExpanded(false),
|
||||
_nodeThread(new QThread(this)),
|
||||
_datagramProcessor(),
|
||||
_frameCount(0),
|
||||
|
@ -155,8 +156,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_swatch(NULL),
|
||||
_pasteMode(false),
|
||||
_logger(new FileLogger()),
|
||||
_persistThread(NULL),
|
||||
_statsExpanded(false)
|
||||
_persistThread(NULL)
|
||||
{
|
||||
_applicationStartupTime = startup_time;
|
||||
|
||||
|
@ -254,7 +254,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_window->setCentralWidget(_glWidget);
|
||||
|
||||
restoreSizeAndPosition();
|
||||
loadScripts();
|
||||
|
||||
QFontDatabase fontDatabase;
|
||||
fontDatabase.addApplicationFont("resources/styles/Inconsolata.otf");
|
||||
|
@ -284,6 +283,9 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_sixenseManager.setFilter(Menu::getInstance()->isOptionChecked(MenuOption::FilterSixense));
|
||||
|
||||
checkVersion();
|
||||
|
||||
// do this as late as possible so that all required subsystems are inialized
|
||||
loadScripts();
|
||||
}
|
||||
|
||||
Application::~Application() {
|
||||
|
@ -679,6 +681,14 @@ void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_
|
|||
}
|
||||
|
||||
void Application::keyPressEvent(QKeyEvent* event) {
|
||||
|
||||
_controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
if (_controllerScriptingInterface.isKeyCaptured(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (activeWindow() == _window) {
|
||||
if (_chatEntryOn) {
|
||||
if (_chatEntry.keyPressEvent(event)) {
|
||||
|
@ -1090,6 +1100,15 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
}
|
||||
|
||||
void Application::keyReleaseEvent(QKeyEvent* event) {
|
||||
|
||||
_controllerScriptingInterface.emitKeyReleaseEvent(event); // send events to any registered scripts
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
if (_controllerScriptingInterface.isKeyCaptured(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (activeWindow() == _window) {
|
||||
if (_chatEntryOn) {
|
||||
_myAvatar.setKeyState(NO_KEY_DOWN);
|
||||
|
@ -1152,6 +1171,14 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
|
|||
}
|
||||
|
||||
void Application::mouseMoveEvent(QMouseEvent* event) {
|
||||
_controllerScriptingInterface.emitMouseMoveEvent(event); // send events to any registered scripts
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
if (_controllerScriptingInterface.isMouseCaptured()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
_lastMouseMove = usecTimestampNow();
|
||||
if (_mouseHidden) {
|
||||
getGLWidget()->setCursor(Qt::ArrowCursor);
|
||||
|
@ -1198,6 +1225,14 @@ const float HOVER_VOXEL_FREQUENCY = 7040.f;
|
|||
const float HOVER_VOXEL_DECAY = 0.999f;
|
||||
|
||||
void Application::mousePressEvent(QMouseEvent* event) {
|
||||
_controllerScriptingInterface.emitMousePressEvent(event); // send events to any registered scripts
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
if (_controllerScriptingInterface.isMouseCaptured()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (activeWindow() == _window) {
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
_mouseX = event->x();
|
||||
|
@ -1265,6 +1300,13 @@ void Application::mousePressEvent(QMouseEvent* event) {
|
|||
}
|
||||
|
||||
void Application::mouseReleaseEvent(QMouseEvent* event) {
|
||||
_controllerScriptingInterface.emitMouseReleaseEvent(event); // send events to any registered scripts
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
if (_controllerScriptingInterface.isMouseCaptured()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (activeWindow() == _window) {
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
_mouseX = event->x();
|
||||
|
@ -1281,6 +1323,13 @@ void Application::mouseReleaseEvent(QMouseEvent* event) {
|
|||
}
|
||||
|
||||
void Application::touchUpdateEvent(QTouchEvent* event) {
|
||||
_controllerScriptingInterface.emitTouchUpdateEvent(event); // send events to any registered scripts
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
if (_controllerScriptingInterface.isTouchCaptured()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool validTouch = false;
|
||||
if (activeWindow() == _window) {
|
||||
const QList<QTouchEvent::TouchPoint>& tPoints = event->touchPoints();
|
||||
|
@ -1305,19 +1354,46 @@ void Application::touchUpdateEvent(QTouchEvent* event) {
|
|||
}
|
||||
|
||||
void Application::touchBeginEvent(QTouchEvent* event) {
|
||||
_controllerScriptingInterface.emitTouchBeginEvent(event); // send events to any registered scripts
|
||||
|
||||
touchUpdateEvent(event);
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
if (_controllerScriptingInterface.isTouchCaptured()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// put any application specific touch behavior below here..
|
||||
_lastTouchAvgX = _touchAvgX;
|
||||
_lastTouchAvgY = _touchAvgY;
|
||||
|
||||
}
|
||||
|
||||
void Application::touchEndEvent(QTouchEvent* event) {
|
||||
_controllerScriptingInterface.emitTouchEndEvent(event); // send events to any registered scripts
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
if (_controllerScriptingInterface.isTouchCaptured()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// put any application specific touch behavior below here..
|
||||
_touchDragStartedAvgX = _touchAvgX;
|
||||
_touchDragStartedAvgY = _touchAvgY;
|
||||
_isTouchPressed = false;
|
||||
|
||||
}
|
||||
|
||||
const bool USE_MOUSEWHEEL = false;
|
||||
void Application::wheelEvent(QWheelEvent* event) {
|
||||
|
||||
_controllerScriptingInterface.emitWheelEvent(event); // send events to any registered scripts
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
if (_controllerScriptingInterface.isWheelCaptured()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Wheel Events disabled for now because they are also activated by touch look pitch up/down.
|
||||
if (USE_MOUSEWHEEL && (activeWindow() == _window)) {
|
||||
if (!Menu::getInstance()->isVoxelModeActionChecked()) {
|
||||
|
@ -2328,10 +2404,7 @@ void Application::updateAvatar(float deltaTime) {
|
|||
_yawFromTouch = 0.f;
|
||||
|
||||
// apply pitch from touch
|
||||
_myAvatar.getHead().setMousePitch(_myAvatar.getHead().getMousePitch() +
|
||||
_myAvatar.getHand().getPitchUpdate() +
|
||||
_pitchFromTouch);
|
||||
_myAvatar.getHand().setPitchUpdate(0.f);
|
||||
_myAvatar.getHead().setPitch(_myAvatar.getHead().getPitch() + _pitchFromTouch);
|
||||
_pitchFromTouch = 0.0f;
|
||||
|
||||
// Update my avatar's state from gyros
|
||||
|
|
|
@ -10,6 +10,14 @@
|
|||
#include "Application.h"
|
||||
#include "ControllerScriptingInterface.h"
|
||||
|
||||
ControllerScriptingInterface::ControllerScriptingInterface() :
|
||||
_mouseCaptured(false),
|
||||
_touchCaptured(false),
|
||||
_wheelCaptured(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
const PalmData* ControllerScriptingInterface::getPrimaryPalm() const {
|
||||
int leftPalmIndex, rightPalmIndex;
|
||||
|
||||
|
@ -179,5 +187,35 @@ glm::vec3 ControllerScriptingInterface::getSpatialControlNormal(int controlIndex
|
|||
return glm::vec3(0); // bad index
|
||||
}
|
||||
|
||||
bool ControllerScriptingInterface::isKeyCaptured(QKeyEvent* event) const {
|
||||
return isKeyCaptured(KeyEvent(*event));
|
||||
}
|
||||
|
||||
bool ControllerScriptingInterface::isKeyCaptured(const KeyEvent& event) const {
|
||||
// if we've captured some combination of this key it will be in the map
|
||||
if (_capturedKeys.contains(event.key, event)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ControllerScriptingInterface::captureKeyEvents(const KeyEvent& event) {
|
||||
// if it's valid
|
||||
if (event.isValid) {
|
||||
// and not already captured
|
||||
if (!isKeyCaptured(event)) {
|
||||
// then add this KeyEvent record to the captured combos for this key
|
||||
_capturedKeys.insert(event.key, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ControllerScriptingInterface::releaseKeyEvents(const KeyEvent& event) {
|
||||
if (event.isValid) {
|
||||
// and not already captured
|
||||
if (isKeyCaptured(event)) {
|
||||
_capturedKeys.remove(event.key, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
//
|
||||
// ControllerScriptingInterface.h
|
||||
// hifi
|
||||
|
@ -18,6 +19,27 @@ class PalmData;
|
|||
class ControllerScriptingInterface : public AbstractControllerScriptingInterface {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ControllerScriptingInterface();
|
||||
void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); }
|
||||
void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); }
|
||||
|
||||
void emitMouseMoveEvent(QMouseEvent* event) { emit mouseMoveEvent(MouseEvent(*event)); }
|
||||
void emitMousePressEvent(QMouseEvent* event) { emit mousePressEvent(MouseEvent(*event)); }
|
||||
void emitMouseReleaseEvent(QMouseEvent* event) { emit mouseReleaseEvent(MouseEvent(*event)); }
|
||||
|
||||
void emitTouchBeginEvent(QTouchEvent* event) { emit touchBeginEvent(*event); }
|
||||
void emitTouchEndEvent(QTouchEvent* event) { emit touchEndEvent(*event); }
|
||||
void emitTouchUpdateEvent(QTouchEvent* event) { emit touchUpdateEvent(*event); }
|
||||
void emitWheelEvent(QWheelEvent* event) { emit wheelEvent(*event); }
|
||||
|
||||
bool isKeyCaptured(QKeyEvent* event) const;
|
||||
bool isKeyCaptured(const KeyEvent& event) const;
|
||||
bool isMouseCaptured() const { return _mouseCaptured; }
|
||||
bool isTouchCaptured() const { return _touchCaptured; }
|
||||
bool isWheelCaptured() const { return _wheelCaptured; }
|
||||
|
||||
|
||||
public slots:
|
||||
virtual bool isPrimaryButtonPressed() const;
|
||||
virtual glm::vec2 getPrimaryJoystickPosition() const;
|
||||
|
@ -36,11 +58,28 @@ public slots:
|
|||
virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const;
|
||||
virtual glm::vec3 getSpatialControlNormal(int controlIndex) const;
|
||||
|
||||
virtual void captureKeyEvents(const KeyEvent& event);
|
||||
virtual void releaseKeyEvents(const KeyEvent& event);
|
||||
|
||||
virtual void captureMouseEvents() { _mouseCaptured = true; }
|
||||
virtual void releaseMouseEvents() { _mouseCaptured = false; }
|
||||
|
||||
virtual void captureTouchEvents() { _touchCaptured = true; }
|
||||
virtual void releaseTouchEvents() { _touchCaptured = false; }
|
||||
|
||||
virtual void captureWheelEvents() { _wheelCaptured = true; }
|
||||
virtual void releaseWheelEvents() { _wheelCaptured = false; }
|
||||
|
||||
private:
|
||||
const PalmData* getPrimaryPalm() const;
|
||||
const PalmData* getPalm(int palmIndex) const;
|
||||
int getNumberOfActivePalms() const;
|
||||
const PalmData* getActivePalm(int palmIndex) const;
|
||||
|
||||
bool _mouseCaptured;
|
||||
bool _touchCaptured;
|
||||
bool _wheelCaptured;
|
||||
QMultiMap<int,KeyEvent> _capturedKeys;
|
||||
};
|
||||
|
||||
const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip
|
||||
|
|
|
@ -57,6 +57,7 @@ void DatagramProcessor::processDatagrams() {
|
|||
case PACKET_TYPE_PARTICLE_ADD_RESPONSE:
|
||||
// this will keep creatorTokenIDs to IDs mapped correctly
|
||||
Particle::handleAddParticleResponse(incomingPacket, bytesReceived);
|
||||
application->getParticles()->getTree()->handleAddParticleResponse(incomingPacket, bytesReceived);
|
||||
break;
|
||||
|
||||
case PACKET_TYPE_PARTICLE_DATA:
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
//
|
||||
//
|
||||
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#include "ParticleTreeRenderer.h"
|
||||
|
@ -16,8 +18,18 @@ ParticleTreeRenderer::ParticleTreeRenderer() :
|
|||
}
|
||||
|
||||
ParticleTreeRenderer::~ParticleTreeRenderer() {
|
||||
// delete the models in _particleModels
|
||||
foreach(Model* model, _particleModels) {
|
||||
delete model;
|
||||
}
|
||||
_particleModels.clear();
|
||||
}
|
||||
|
||||
void ParticleTreeRenderer::init() {
|
||||
OctreeRenderer::init();
|
||||
}
|
||||
|
||||
|
||||
void ParticleTreeRenderer::update() {
|
||||
if (_tree) {
|
||||
ParticleTree* tree = (ParticleTree*)_tree;
|
||||
|
@ -27,6 +39,25 @@ void ParticleTreeRenderer::update() {
|
|||
}
|
||||
}
|
||||
|
||||
void ParticleTreeRenderer::render() {
|
||||
OctreeRenderer::render();
|
||||
}
|
||||
|
||||
Model* ParticleTreeRenderer::getModel(const QString& url) {
|
||||
Model* model = NULL;
|
||||
|
||||
// if we don't already have this model then create it and initialize it
|
||||
if (_particleModels.find(url) == _particleModels.end()) {
|
||||
model = new Model();
|
||||
model->init();
|
||||
model->setURL(QUrl(url));
|
||||
_particleModels[url] = model;
|
||||
} else {
|
||||
model = _particleModels[url];
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) {
|
||||
// actually render it here...
|
||||
// we need to iterate the actual particles of the element
|
||||
|
@ -36,27 +67,48 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg
|
|||
|
||||
uint16_t numberOfParticles = particles.size();
|
||||
|
||||
bool drawAsSphere = true;
|
||||
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
const Particle& particle = particles[i];
|
||||
// render particle aspoints
|
||||
glm::vec3 position = particle.getPosition() * (float)TREE_SCALE;
|
||||
glColor3ub(particle.getColor()[RED_INDEX],particle.getColor()[GREEN_INDEX],particle.getColor()[BLUE_INDEX]);
|
||||
float sphereRadius = particle.getRadius() * (float)TREE_SCALE;
|
||||
float radius = particle.getRadius() * (float)TREE_SCALE;
|
||||
|
||||
bool drawAsModel = particle.hasModel();
|
||||
|
||||
args->_renderedItems++;
|
||||
|
||||
if (drawAsSphere) {
|
||||
if (drawAsModel) {
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glutSolidSphere(sphereRadius, 15, 15);
|
||||
const float alpha = 1.0f;
|
||||
|
||||
Model* model = getModel(particle.getModelURL());
|
||||
|
||||
glm::vec3 translationAdjustment = particle.getModelTranslation();
|
||||
|
||||
// set the position
|
||||
glm::vec3 translation(position.x, position.y, position.z);
|
||||
model->setTranslation(translation + translationAdjustment);
|
||||
|
||||
// set the rotation
|
||||
glm::quat rotation = particle.getModelRotation();
|
||||
model->setRotation(rotation);
|
||||
|
||||
// scale
|
||||
// TODO: need to figure out correct scale adjust, this was arbitrarily set to make a couple models work
|
||||
const float MODEL_SCALE = 0.00575f;
|
||||
glm::vec3 scale(1.0f,1.0f,1.0f);
|
||||
model->setScale(scale * MODEL_SCALE * radius * particle.getModelScale());
|
||||
|
||||
model->simulate(0.0f);
|
||||
model->render(alpha); // TODO: should we allow particles to have alpha on their models?
|
||||
|
||||
glPopMatrix();
|
||||
} else {
|
||||
glPointSize(sphereRadius);
|
||||
glBegin(GL_POINTS);
|
||||
glVertex3f(position.x, position.y, position.z);
|
||||
glEnd();
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glutSolidSphere(radius, 15, 15);
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <OctreeRenderer.h>
|
||||
#include <ParticleTree.h>
|
||||
#include <ViewFrustum.h>
|
||||
#include "renderer/Model.h"
|
||||
|
||||
// Generic client side Octree renderer class.
|
||||
class ParticleTreeRenderer : public OctreeRenderer {
|
||||
|
@ -39,7 +40,13 @@ public:
|
|||
|
||||
void processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode);
|
||||
|
||||
virtual void init();
|
||||
virtual void render();
|
||||
|
||||
protected:
|
||||
Model* getModel(const QString& url);
|
||||
|
||||
QMap<QString, Model*> _particleModels;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__ParticleTreeRenderer__) */
|
|
@ -105,34 +105,6 @@ glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2) {
|
|||
return glm::angleAxis(angle, axis);
|
||||
}
|
||||
|
||||
// Safe version of glm::eulerAngles; uses the factorization method described in David Eberly's
|
||||
// http://www.geometrictools.com/Documentation/EulerAngles.pdf (via Clyde,
|
||||
// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java)
|
||||
glm::vec3 safeEulerAngles(const glm::quat& q) {
|
||||
float sy = 2.0f * (q.y * q.w - q.x * q.z);
|
||||
if (sy < 1.0f - EPSILON) {
|
||||
if (sy > -1.0f + EPSILON) {
|
||||
return glm::degrees(glm::vec3(
|
||||
atan2f(q.y * q.z + q.x * q.w, 0.5f - (q.x * q.x + q.y * q.y)),
|
||||
asinf(sy),
|
||||
atan2f(q.x * q.y + q.z * q.w, 0.5f - (q.y * q.y + q.z * q.z))));
|
||||
|
||||
} else {
|
||||
// not a unique solution; x + z = atan2(-m21, m11)
|
||||
return glm::degrees(glm::vec3(
|
||||
0.0f,
|
||||
PIf * -0.5f,
|
||||
atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z))));
|
||||
}
|
||||
} else {
|
||||
// not a unique solution; x - z = atan2(-m21, m11)
|
||||
return glm::degrees(glm::vec3(
|
||||
0.0f,
|
||||
PIf * 0.5f,
|
||||
-atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z))));
|
||||
}
|
||||
}
|
||||
|
||||
// Safe version of glm::mix; based on the code in Nick Bobick's article,
|
||||
// http://www.gamasutra.com/features/19980703/quaternions_01.htm (via Clyde,
|
||||
// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java)
|
||||
|
|
|
@ -54,8 +54,6 @@ float angleBetween(const glm::vec3& v1, const glm::vec3& v2);
|
|||
|
||||
glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2);
|
||||
|
||||
glm::vec3 safeEulerAngles(const glm::quat& q);
|
||||
|
||||
glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha);
|
||||
|
||||
glm::vec3 extractTranslation(const glm::mat4& matrix);
|
||||
|
|
|
@ -37,8 +37,6 @@ Head::Head(Avatar* owningAvatar) :
|
|||
_leftEyeBlinkVelocity(0.0f),
|
||||
_rightEyeBlinkVelocity(0.0f),
|
||||
_timeWithoutTalking(0.0f),
|
||||
_cameraPitch(_pitch),
|
||||
_mousePitch(0.f),
|
||||
_cameraYaw(_yaw),
|
||||
_isCameraMoving(false),
|
||||
_faceModel(this)
|
||||
|
@ -52,7 +50,6 @@ void Head::init() {
|
|||
|
||||
void Head::reset() {
|
||||
_yaw = _pitch = _roll = 0.0f;
|
||||
_mousePitch = 0.0f;
|
||||
_leanForward = _leanSideways = 0.0f;
|
||||
_faceModel.reset();
|
||||
}
|
||||
|
@ -186,13 +183,6 @@ void Head::setScale (float scale) {
|
|||
_scale = scale;
|
||||
}
|
||||
|
||||
void Head::setMousePitch(float mousePitch) {
|
||||
const float MAX_PITCH = 90.0f;
|
||||
_mousePitch = glm::clamp(mousePitch, -MAX_PITCH, MAX_PITCH);
|
||||
}
|
||||
|
||||
|
||||
|
||||
glm::quat Head::getOrientation() const {
|
||||
return glm::quat(glm::radians(_bodyRotation)) * glm::quat(glm::radians(glm::vec3(_pitch, _yaw, _roll)));
|
||||
}
|
||||
|
@ -200,7 +190,7 @@ glm::quat Head::getOrientation() const {
|
|||
glm::quat Head::getCameraOrientation () const {
|
||||
Avatar* owningAvatar = static_cast<Avatar*>(_owningAvatar);
|
||||
return owningAvatar->getWorldAlignedOrientation()
|
||||
* glm::quat(glm::radians(glm::vec3(_cameraPitch + _mousePitch, _cameraYaw, 0.0f)));
|
||||
* glm::quat(glm::radians(glm::vec3(_pitch, _cameraYaw, 0.0f)));
|
||||
}
|
||||
|
||||
glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const {
|
||||
|
|
|
@ -46,9 +46,6 @@ public:
|
|||
void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; }
|
||||
void setRenderLookatVectors(bool onOff) { _renderLookatVectors = onOff; }
|
||||
|
||||
float getMousePitch() const { return _mousePitch; }
|
||||
void setMousePitch(float mousePitch);
|
||||
|
||||
glm::quat getOrientation() const;
|
||||
glm::quat getCameraOrientation () const;
|
||||
const glm::vec3& getAngularVelocity() const { return _angularVelocity; }
|
||||
|
@ -99,8 +96,6 @@ private:
|
|||
float _leftEyeBlinkVelocity;
|
||||
float _rightEyeBlinkVelocity;
|
||||
float _timeWithoutTalking;
|
||||
float _cameraPitch; // Used to position the camera differently from the head
|
||||
float _mousePitch;
|
||||
float _cameraYaw;
|
||||
bool _isCameraMoving;
|
||||
FaceModel _faceModel;
|
||||
|
|
|
@ -238,7 +238,7 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
// Adjust body yaw by yaw from controller
|
||||
setOrientation(glm::angleAxis(-euler.y, glm::vec3(0, 1, 0)) * getOrientation());
|
||||
// Adjust head pitch from controller
|
||||
getHead().setMousePitch(getHead().getMousePitch() - euler.x);
|
||||
getHead().setPitch(getHead().getPitch() - euler.x);
|
||||
|
||||
_position += _velocity * deltaTime;
|
||||
|
||||
|
@ -284,8 +284,6 @@ void MyAvatar::updateFromGyros(bool turnWithHead) {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
_head.setPitch(_head.getMousePitch());
|
||||
|
||||
// restore rotation, lean to neutral positions
|
||||
const float RESTORE_RATE = 0.05f;
|
||||
_head.setYaw(glm::mix(_head.getYaw(), 0.0f, RESTORE_RATE));
|
||||
|
@ -434,7 +432,7 @@ void MyAvatar::saveData(QSettings* settings) {
|
|||
settings->setValue("bodyPitch", _bodyPitch);
|
||||
settings->setValue("bodyRoll", _bodyRoll);
|
||||
|
||||
settings->setValue("mousePitch", _head.getMousePitch());
|
||||
settings->setValue("headPitch", _head.getPitch());
|
||||
|
||||
settings->setValue("position_x", _position.x);
|
||||
settings->setValue("position_y", _position.y);
|
||||
|
@ -456,7 +454,7 @@ void MyAvatar::loadData(QSettings* settings) {
|
|||
_bodyPitch = loadSetting(settings, "bodyPitch", 0.0f);
|
||||
_bodyRoll = loadSetting(settings, "bodyRoll", 0.0f);
|
||||
|
||||
_head.setMousePitch(loadSetting(settings, "mousePitch", 0.0f));
|
||||
_head.setPitch(loadSetting(settings, "headPitch", 0.0f));
|
||||
|
||||
_position.x = loadSetting(settings, "position_x", 0.0f);
|
||||
_position.y = loadSetting(settings, "position_y", 0.0f);
|
||||
|
@ -497,9 +495,10 @@ void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) {
|
|||
setOrientation(orientation);
|
||||
|
||||
// then vertically
|
||||
float oldMousePitch = _head.getMousePitch();
|
||||
_head.setMousePitch(oldMousePitch + deltaY * -ANGULAR_SCALE);
|
||||
rotation = glm::angleAxis(_head.getMousePitch() - oldMousePitch, orientation * IDENTITY_RIGHT);
|
||||
float oldPitch = _head.getPitch();
|
||||
_head.setPitch(oldPitch + deltaY * -ANGULAR_SCALE);
|
||||
rotation = glm::angleAxis(_head.getPitch() - oldPitch, orientation * IDENTITY_RIGHT);
|
||||
|
||||
setPosition(position + rotation * (getPosition() - position));
|
||||
}
|
||||
|
||||
|
@ -549,7 +548,7 @@ void MyAvatar::updateThrust(float deltaTime, Transmitter * transmitter) {
|
|||
_thrust -= _driveKeys[DOWN] * _scale * THRUST_MAG_DOWN * _thrustMultiplier * deltaTime * up;
|
||||
_bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_MAG * deltaTime;
|
||||
_bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_MAG * deltaTime;
|
||||
_head.setMousePitch(_head.getMousePitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_MAG * deltaTime);
|
||||
_head.setPitch(_head.getPitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_MAG * deltaTime);
|
||||
|
||||
// If thrust keys are being held down, slowly increase thrust to allow reaching great speeds
|
||||
if (_driveKeys[FWD] || _driveKeys[BACK] || _driveKeys[RIGHT] || _driveKeys[LEFT] || _driveKeys[UP] || _driveKeys[DOWN]) {
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
#ifndef __interface__world__
|
||||
#define __interface__world__
|
||||
|
||||
|
||||
#ifndef PIf
|
||||
#define PIf 3.14159265f
|
||||
#endif
|
||||
|
||||
const float GRAVITY_EARTH = 9.80665f;
|
||||
const float EDGE_SIZE_GROUND_PLANE = 20.f;
|
||||
|
|
|
@ -276,3 +276,10 @@ void AvatarData::setClampedTargetScale(float targetScale) {
|
|||
_targetScale = targetScale;
|
||||
qDebug() << "Changed scale to " << _targetScale;
|
||||
}
|
||||
|
||||
void AvatarData::setOrientation(const glm::quat& orientation) {
|
||||
glm::vec3 eulerAngles = safeEulerAngles(orientation);
|
||||
_bodyPitch = eulerAngles.x;
|
||||
_bodyYaw = eulerAngles.y;
|
||||
_bodyRoll = eulerAngles.z;
|
||||
}
|
||||
|
|
|
@ -69,6 +69,10 @@ class AvatarData : public NodeData {
|
|||
Q_PROPERTY(float bodyPitch READ getBodyPitch WRITE setBodyPitch)
|
||||
Q_PROPERTY(float bodyRoll READ getBodyRoll WRITE setBodyRoll)
|
||||
Q_PROPERTY(QString chatMessage READ getQStringChatMessage WRITE setChatMessage)
|
||||
|
||||
Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation)
|
||||
Q_PROPERTY(float headPitch READ getHeadPitch WRITE setHeadPitch)
|
||||
|
||||
public:
|
||||
AvatarData();
|
||||
~AvatarData();
|
||||
|
@ -91,6 +95,11 @@ public:
|
|||
void setBodyRoll(float bodyRoll) { _bodyRoll = bodyRoll; }
|
||||
|
||||
glm::quat getOrientation() const { return glm::quat(glm::radians(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll))); }
|
||||
void setOrientation(const glm::quat& orientation);
|
||||
|
||||
// access to Head().set/getMousePitch
|
||||
float getHeadPitch() const { return _headData->getPitch(); }
|
||||
void setHeadPitch(float value) { _headData->setPitch(value); };
|
||||
|
||||
// Scale
|
||||
float getTargetScale() const { return _targetScale; }
|
||||
|
|
|
@ -43,6 +43,7 @@ Octree::Octree(bool shouldReaverage) :
|
|||
_shouldReaverage(shouldReaverage),
|
||||
_stopImport(false) {
|
||||
_rootNode = NULL;
|
||||
_isViewing = false;
|
||||
}
|
||||
|
||||
Octree::~Octree() {
|
||||
|
|
|
@ -258,6 +258,9 @@ public:
|
|||
void recurseNodeWithOperationDistanceSorted(OctreeElement* node, RecurseOctreeOperation operation,
|
||||
const glm::vec3& point, void* extraData, int recursionCount = 0);
|
||||
|
||||
bool getIsViewing() const { return _isViewing; }
|
||||
void setIsViewing(bool isViewing) { _isViewing = isViewing; }
|
||||
|
||||
signals:
|
||||
void importSize(float x, float y, float z);
|
||||
void importProgress(int progress);
|
||||
|
@ -321,6 +324,9 @@ protected:
|
|||
void emptyDeleteQueue();
|
||||
|
||||
QReadWriteLock lock;
|
||||
|
||||
/// This tree is receiving inbound viewer datagrams.
|
||||
bool _isViewing;
|
||||
};
|
||||
|
||||
float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale);
|
||||
|
|
|
@ -305,6 +305,19 @@ bool OctreePacketData::appendValue(const glm::vec3& value) {
|
|||
return success;
|
||||
}
|
||||
|
||||
bool OctreePacketData::appendValue(const glm::quat& value) {
|
||||
const size_t VALUES_PER_QUAT = 4;
|
||||
const size_t PACKED_QUAT_SIZE = sizeof(uint16_t) * VALUES_PER_QUAT;
|
||||
unsigned char data[PACKED_QUAT_SIZE];
|
||||
int length = packOrientationQuatToBytes(data, value);
|
||||
bool success = append(data, length);
|
||||
if (success) {
|
||||
_bytesOfValues += length;
|
||||
_totalBytesOfValues += length;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool OctreePacketData::appendValue(bool value) {
|
||||
bool success = append((uint8_t)value); // used unsigned char version
|
||||
if (success) {
|
||||
|
|
|
@ -130,6 +130,9 @@ public:
|
|||
/// appends a non-position vector to the end of the stream, may fail if new data stream is too long to fit in packet
|
||||
bool appendValue(const glm::vec3& value);
|
||||
|
||||
/// appends a packed quat to the end of the stream, may fail if new data stream is too long to fit in packet
|
||||
bool appendValue(const glm::quat& value);
|
||||
|
||||
/// appends a bool value to the end of the stream, may fail if new data stream is too long to fit in packet
|
||||
bool appendValue(bool value);
|
||||
|
||||
|
|
|
@ -42,6 +42,10 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Hifi
|
|||
|
||||
if(command == expectedType) {
|
||||
PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram expected PACKET_TYPE",showTimingDetails);
|
||||
|
||||
// if we are getting inbound packets, then our tree is also viewing, and we should remember that fact.
|
||||
_tree->setIsViewing(true);
|
||||
|
||||
|
||||
const unsigned char* dataAt = packetData + numBytesPacketHeader;
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ public:
|
|||
virtual void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode);
|
||||
|
||||
/// initialize and GPU/rendering related resources
|
||||
void init();
|
||||
virtual void init();
|
||||
|
||||
/// render the content of the octree
|
||||
virtual void render();
|
||||
|
|
|
@ -65,19 +65,39 @@ void Particle::handleAddParticleResponse(unsigned char* packetData , int packetL
|
|||
}
|
||||
|
||||
|
||||
|
||||
Particle::Particle(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, glm::vec3 gravity,
|
||||
float damping, float lifetime, bool inHand, QString updateScript, uint32_t id) {
|
||||
|
||||
init(position, radius, color, velocity, gravity, damping, lifetime, 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, DEFAULT_LIFETIME, NOT_IN_HAND, DEFAULT_SCRIPT, NEW_PARTICLE);
|
||||
}
|
||||
|
||||
Particle::Particle(const ParticleID& particleID, const ParticleProperties& properties) {
|
||||
_id = particleID.id;
|
||||
_creatorTokenID = particleID.creatorTokenID;
|
||||
|
||||
// init values with defaults before calling setProperties
|
||||
uint64_t now = usecTimestampNow();
|
||||
_lastEdited = now;
|
||||
_lastUpdated = now;
|
||||
_created = now; // will get updated as appropriate in setAge()
|
||||
|
||||
_position = glm::vec3(0,0,0);
|
||||
_radius = 0;
|
||||
_mass = 1.0f;
|
||||
rgbColor noColor = { 0, 0, 0 };
|
||||
memcpy(_color, noColor, sizeof(_color));
|
||||
_velocity = glm::vec3(0,0,0);
|
||||
_damping = DEFAULT_DAMPING;
|
||||
_lifetime = DEFAULT_LIFETIME;
|
||||
_gravity = DEFAULT_GRAVITY;
|
||||
_script = DEFAULT_SCRIPT;
|
||||
_inHand = NOT_IN_HAND;
|
||||
_shouldDie = false;
|
||||
|
||||
setProperties(properties);
|
||||
}
|
||||
|
||||
|
||||
Particle::~Particle() {
|
||||
}
|
||||
|
||||
|
@ -86,10 +106,8 @@ void Particle::init(glm::vec3 position, float radius, rgbColor color, glm::vec3
|
|||
if (id == NEW_PARTICLE) {
|
||||
_id = _nextID;
|
||||
_nextID++;
|
||||
//qDebug() << "Particle::init()... assigning new id... _id=" << _id;
|
||||
} else {
|
||||
_id = id;
|
||||
//qDebug() << "Particle::init()... assigning id from init... _id=" << _id;
|
||||
}
|
||||
uint64_t now = usecTimestampNow();
|
||||
_lastEdited = now;
|
||||
|
@ -157,7 +175,6 @@ bool Particle::appendParticleData(OctreePacketData* packetData) const {
|
|||
if (success) {
|
||||
success = packetData->appendValue(getShouldDie());
|
||||
}
|
||||
|
||||
if (success) {
|
||||
uint16_t scriptLength = _script.size() + 1; // include NULL
|
||||
success = packetData->appendValue(scriptLength);
|
||||
|
@ -165,6 +182,28 @@ bool Particle::appendParticleData(OctreePacketData* packetData) const {
|
|||
success = packetData->appendRawData((const unsigned char*)qPrintable(_script), scriptLength);
|
||||
}
|
||||
}
|
||||
|
||||
// modelURL
|
||||
if (success) {
|
||||
uint16_t modelURLLength = _modelURL.size() + 1; // include NULL
|
||||
success = packetData->appendValue(modelURLLength);
|
||||
if (success) {
|
||||
success = packetData->appendRawData((const unsigned char*)qPrintable(_modelURL), modelURLLength);
|
||||
}
|
||||
}
|
||||
// modelTranslation
|
||||
if (success) {
|
||||
success = packetData->appendValue(getModelTranslation());
|
||||
}
|
||||
// modelRotation
|
||||
if (success) {
|
||||
success = packetData->appendValue(getModelRotation());
|
||||
}
|
||||
// modelScale
|
||||
if (success) {
|
||||
success = packetData->appendValue(getModelScale());
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
@ -185,21 +224,6 @@ int Particle::expectedBytes() {
|
|||
return expectedBytes;
|
||||
}
|
||||
|
||||
int Particle::expectedEditMessageBytes() {
|
||||
int expectedBytes = sizeof(uint32_t) // id
|
||||
+ sizeof(uint64_t) // lasted edited
|
||||
+ sizeof(float) // radius
|
||||
+ sizeof(glm::vec3) // position
|
||||
+ sizeof(rgbColor) // color
|
||||
+ sizeof(glm::vec3) // velocity
|
||||
+ sizeof(glm::vec3) // gravity
|
||||
+ sizeof(float) // damping
|
||||
+ sizeof(float) // lifetime
|
||||
+ sizeof(bool); // inhand
|
||||
// potentially more...
|
||||
return expectedBytes;
|
||||
}
|
||||
|
||||
int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) {
|
||||
int bytesRead = 0;
|
||||
if (bytesLeftToRead >= expectedBytes()) {
|
||||
|
@ -286,6 +310,31 @@ int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
dataAt += scriptLength;
|
||||
bytesRead += scriptLength;
|
||||
|
||||
// modelURL
|
||||
uint16_t modelURLLength;
|
||||
memcpy(&modelURLLength, dataAt, sizeof(modelURLLength));
|
||||
dataAt += sizeof(modelURLLength);
|
||||
bytesRead += sizeof(modelURLLength);
|
||||
QString modelURLString((const char*)dataAt);
|
||||
_modelURL = modelURLString;
|
||||
dataAt += modelURLLength;
|
||||
bytesRead += modelURLLength;
|
||||
|
||||
// modelTranslation
|
||||
memcpy(&_modelTranslation, dataAt, sizeof(_modelTranslation));
|
||||
dataAt += sizeof(_modelTranslation);
|
||||
bytesRead += sizeof(_modelTranslation);
|
||||
|
||||
// modelRotation
|
||||
int bytes = unpackOrientationQuatFromBytes(dataAt, _modelRotation);
|
||||
dataAt += bytes;
|
||||
bytesRead += bytes;
|
||||
|
||||
// modelScale
|
||||
memcpy(&_modelScale, dataAt, sizeof(_modelScale));
|
||||
dataAt += sizeof(_modelScale);
|
||||
bytesRead += sizeof(_modelScale);
|
||||
|
||||
//printf("Particle::readParticleDataFromBuffer()... "); debugDump();
|
||||
}
|
||||
return bytesRead;
|
||||
|
@ -366,49 +415,49 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe
|
|||
|
||||
|
||||
// radius
|
||||
if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_RADIUS) == PACKET_CONTAINS_RADIUS)) {
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_RADIUS) == CONTAINS_RADIUS)) {
|
||||
memcpy(&newParticle._radius, dataAt, sizeof(newParticle._radius));
|
||||
dataAt += sizeof(newParticle._radius);
|
||||
processedBytes += sizeof(newParticle._radius);
|
||||
}
|
||||
|
||||
// position
|
||||
if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_POSITION) == PACKET_CONTAINS_POSITION)) {
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_POSITION) == CONTAINS_POSITION)) {
|
||||
memcpy(&newParticle._position, dataAt, sizeof(newParticle._position));
|
||||
dataAt += sizeof(newParticle._position);
|
||||
processedBytes += sizeof(newParticle._position);
|
||||
}
|
||||
|
||||
// color
|
||||
if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_COLOR) == PACKET_CONTAINS_COLOR)) {
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_COLOR) == CONTAINS_COLOR)) {
|
||||
memcpy(newParticle._color, dataAt, sizeof(newParticle._color));
|
||||
dataAt += sizeof(newParticle._color);
|
||||
processedBytes += sizeof(newParticle._color);
|
||||
}
|
||||
|
||||
// velocity
|
||||
if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_VELOCITY) == PACKET_CONTAINS_VELOCITY)) {
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_VELOCITY) == CONTAINS_VELOCITY)) {
|
||||
memcpy(&newParticle._velocity, dataAt, sizeof(newParticle._velocity));
|
||||
dataAt += sizeof(newParticle._velocity);
|
||||
processedBytes += sizeof(newParticle._velocity);
|
||||
}
|
||||
|
||||
// gravity
|
||||
if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_GRAVITY) == PACKET_CONTAINS_GRAVITY)) {
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_GRAVITY) == CONTAINS_GRAVITY)) {
|
||||
memcpy(&newParticle._gravity, dataAt, sizeof(newParticle._gravity));
|
||||
dataAt += sizeof(newParticle._gravity);
|
||||
processedBytes += sizeof(newParticle._gravity);
|
||||
}
|
||||
|
||||
// damping
|
||||
if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_DAMPING) == PACKET_CONTAINS_DAMPING)) {
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_DAMPING) == CONTAINS_DAMPING)) {
|
||||
memcpy(&newParticle._damping, dataAt, sizeof(newParticle._damping));
|
||||
dataAt += sizeof(newParticle._damping);
|
||||
processedBytes += sizeof(newParticle._damping);
|
||||
}
|
||||
|
||||
// lifetime
|
||||
if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_LIFETIME) == PACKET_CONTAINS_LIFETIME)) {
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_LIFETIME) == CONTAINS_LIFETIME)) {
|
||||
memcpy(&newParticle._lifetime, dataAt, sizeof(newParticle._lifetime));
|
||||
dataAt += sizeof(newParticle._lifetime);
|
||||
processedBytes += sizeof(newParticle._lifetime);
|
||||
|
@ -416,21 +465,21 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe
|
|||
|
||||
// TODO: make inHand and shouldDie into single bits
|
||||
// inHand
|
||||
if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_INHAND) == PACKET_CONTAINS_INHAND)) {
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_INHAND) == CONTAINS_INHAND)) {
|
||||
memcpy(&newParticle._inHand, dataAt, sizeof(newParticle._inHand));
|
||||
dataAt += sizeof(newParticle._inHand);
|
||||
processedBytes += sizeof(newParticle._inHand);
|
||||
}
|
||||
|
||||
// shouldDie
|
||||
if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_SHOULDDIE) == PACKET_CONTAINS_SHOULDDIE)) {
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_SHOULDDIE) == CONTAINS_SHOULDDIE)) {
|
||||
memcpy(&newParticle._shouldDie, dataAt, sizeof(newParticle._shouldDie));
|
||||
dataAt += sizeof(newParticle._shouldDie);
|
||||
processedBytes += sizeof(newParticle._shouldDie);
|
||||
}
|
||||
|
||||
// script
|
||||
if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_SCRIPT) == PACKET_CONTAINS_SCRIPT)) {
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_SCRIPT) == CONTAINS_SCRIPT)) {
|
||||
uint16_t scriptLength;
|
||||
memcpy(&scriptLength, dataAt, sizeof(scriptLength));
|
||||
dataAt += sizeof(scriptLength);
|
||||
|
@ -441,6 +490,39 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe
|
|||
processedBytes += scriptLength;
|
||||
}
|
||||
|
||||
// modelURL
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_URL) == CONTAINS_MODEL_URL)) {
|
||||
uint16_t modelURLLength;
|
||||
memcpy(&modelURLLength, dataAt, sizeof(modelURLLength));
|
||||
dataAt += sizeof(modelURLLength);
|
||||
processedBytes += sizeof(modelURLLength);
|
||||
QString tempString((const char*)dataAt);
|
||||
newParticle._modelURL = tempString;
|
||||
dataAt += modelURLLength;
|
||||
processedBytes += modelURLLength;
|
||||
}
|
||||
|
||||
// modelTranslation
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_TRANSLATION) == CONTAINS_MODEL_TRANSLATION)) {
|
||||
memcpy(&newParticle._modelTranslation, dataAt, sizeof(newParticle._modelTranslation));
|
||||
dataAt += sizeof(newParticle._modelTranslation);
|
||||
processedBytes += sizeof(newParticle._modelTranslation);
|
||||
}
|
||||
|
||||
// modelRotation
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_ROTATION) == CONTAINS_MODEL_ROTATION)) {
|
||||
int bytes = unpackOrientationQuatFromBytes(dataAt, newParticle._modelRotation);
|
||||
dataAt += bytes;
|
||||
processedBytes += bytes;
|
||||
}
|
||||
|
||||
// modelScale
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_SCALE) == CONTAINS_MODEL_SCALE)) {
|
||||
memcpy(&newParticle._modelScale, dataAt, sizeof(newParticle._modelScale));
|
||||
dataAt += sizeof(newParticle._modelScale);
|
||||
processedBytes += sizeof(newParticle._modelScale);
|
||||
}
|
||||
|
||||
const bool wantDebugging = false;
|
||||
if (wantDebugging) {
|
||||
qDebug("Particle::fromEditPacket()...");
|
||||
|
@ -487,138 +569,166 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID
|
|||
|
||||
int octets = numberOfThreeBitSectionsInCode(octcode);
|
||||
int lengthOfOctcode = bytesRequiredForCodeLength(octets);
|
||||
int lenfthOfEditData = lengthOfOctcode + expectedEditMessageBytes();
|
||||
|
||||
// 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;
|
||||
// add it to our message
|
||||
memcpy(copyAt, octcode, lengthOfOctcode);
|
||||
copyAt += lengthOfOctcode;
|
||||
sizeOut += lengthOfOctcode;
|
||||
|
||||
// Now add our edit content details...
|
||||
bool isNewParticle = (id.id == NEW_PARTICLE);
|
||||
// Now add our edit content details...
|
||||
bool isNewParticle = (id.id == NEW_PARTICLE);
|
||||
|
||||
// id
|
||||
memcpy(copyAt, &id.id, sizeof(id.id));
|
||||
copyAt += sizeof(id.id);
|
||||
sizeOut += sizeof(id.id);
|
||||
// id
|
||||
memcpy(copyAt, &id.id, sizeof(id.id));
|
||||
copyAt += sizeof(id.id);
|
||||
sizeOut += sizeof(id.id);
|
||||
|
||||
// special case for handling "new" particles
|
||||
if (isNewParticle) {
|
||||
// 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, &id.creatorTokenID, sizeof(id.creatorTokenID));
|
||||
copyAt += sizeof(id.creatorTokenID);
|
||||
sizeOut += sizeof(id.creatorTokenID);
|
||||
}
|
||||
// special case for handling "new" particles
|
||||
if (isNewParticle) {
|
||||
// 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, &id.creatorTokenID, sizeof(id.creatorTokenID));
|
||||
copyAt += sizeof(id.creatorTokenID);
|
||||
sizeOut += sizeof(id.creatorTokenID);
|
||||
}
|
||||
|
||||
// lastEdited
|
||||
uint64_t lastEdited = properties.getLastEdited();
|
||||
memcpy(copyAt, &lastEdited, sizeof(lastEdited));
|
||||
copyAt += sizeof(lastEdited);
|
||||
sizeOut += sizeof(lastEdited);
|
||||
// lastEdited
|
||||
uint64_t lastEdited = properties.getLastEdited();
|
||||
memcpy(copyAt, &lastEdited, sizeof(lastEdited));
|
||||
copyAt += sizeof(lastEdited);
|
||||
sizeOut += sizeof(lastEdited);
|
||||
|
||||
// For new particles, all remaining items are mandatory, for an edited particle, All of the remaining items are
|
||||
// optional, and may or may not be included based on their included values in the properties included bits
|
||||
uint16_t packetContainsBits = properties.getChangedBits();
|
||||
if (!isNewParticle) {
|
||||
memcpy(copyAt, &packetContainsBits, sizeof(packetContainsBits));
|
||||
copyAt += sizeof(packetContainsBits);
|
||||
sizeOut += sizeof(packetContainsBits);
|
||||
}
|
||||
// For new particles, all remaining items are mandatory, for an edited particle, All of the remaining items are
|
||||
// optional, and may or may not be included based on their included values in the properties included bits
|
||||
uint16_t packetContainsBits = properties.getChangedBits();
|
||||
if (!isNewParticle) {
|
||||
memcpy(copyAt, &packetContainsBits, sizeof(packetContainsBits));
|
||||
copyAt += sizeof(packetContainsBits);
|
||||
sizeOut += sizeof(packetContainsBits);
|
||||
}
|
||||
|
||||
// radius
|
||||
if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_RADIUS) == PACKET_CONTAINS_RADIUS)) {
|
||||
float radius = properties.getRadius() / (float) TREE_SCALE;
|
||||
memcpy(copyAt, &radius, sizeof(radius));
|
||||
copyAt += sizeof(radius);
|
||||
sizeOut += sizeof(radius);
|
||||
}
|
||||
// radius
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_RADIUS) == CONTAINS_RADIUS)) {
|
||||
float radius = properties.getRadius() / (float) TREE_SCALE;
|
||||
memcpy(copyAt, &radius, sizeof(radius));
|
||||
copyAt += sizeof(radius);
|
||||
sizeOut += sizeof(radius);
|
||||
}
|
||||
|
||||
// position
|
||||
if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_POSITION) == PACKET_CONTAINS_POSITION)) {
|
||||
glm::vec3 position = properties.getPosition() / (float)TREE_SCALE;
|
||||
memcpy(copyAt, &position, sizeof(position));
|
||||
copyAt += sizeof(position);
|
||||
sizeOut += sizeof(position);
|
||||
}
|
||||
// position
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_POSITION) == CONTAINS_POSITION)) {
|
||||
glm::vec3 position = properties.getPosition() / (float)TREE_SCALE;
|
||||
memcpy(copyAt, &position, sizeof(position));
|
||||
copyAt += sizeof(position);
|
||||
sizeOut += sizeof(position);
|
||||
}
|
||||
|
||||
// color
|
||||
if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_COLOR) == PACKET_CONTAINS_COLOR)) {
|
||||
rgbColor color = { properties.getColor().red, properties.getColor().green, properties.getColor().blue };
|
||||
memcpy(copyAt, color, sizeof(color));
|
||||
copyAt += sizeof(color);
|
||||
sizeOut += sizeof(color);
|
||||
}
|
||||
// color
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_COLOR) == CONTAINS_COLOR)) {
|
||||
rgbColor color = { properties.getColor().red, properties.getColor().green, properties.getColor().blue };
|
||||
memcpy(copyAt, color, sizeof(color));
|
||||
copyAt += sizeof(color);
|
||||
sizeOut += sizeof(color);
|
||||
}
|
||||
|
||||
// velocity
|
||||
if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_VELOCITY) == PACKET_CONTAINS_VELOCITY)) {
|
||||
glm::vec3 velocity = properties.getVelocity() / (float)TREE_SCALE;
|
||||
memcpy(copyAt, &velocity, sizeof(velocity));
|
||||
copyAt += sizeof(velocity);
|
||||
sizeOut += sizeof(velocity);
|
||||
}
|
||||
// velocity
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_VELOCITY) == CONTAINS_VELOCITY)) {
|
||||
glm::vec3 velocity = properties.getVelocity() / (float)TREE_SCALE;
|
||||
memcpy(copyAt, &velocity, sizeof(velocity));
|
||||
copyAt += sizeof(velocity);
|
||||
sizeOut += sizeof(velocity);
|
||||
}
|
||||
|
||||
// gravity
|
||||
if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_GRAVITY) == PACKET_CONTAINS_GRAVITY)) {
|
||||
glm::vec3 gravity = properties.getGravity() / (float)TREE_SCALE;
|
||||
memcpy(copyAt, &gravity, sizeof(gravity));
|
||||
copyAt += sizeof(gravity);
|
||||
sizeOut += sizeof(gravity);
|
||||
}
|
||||
// gravity
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_GRAVITY) == CONTAINS_GRAVITY)) {
|
||||
glm::vec3 gravity = properties.getGravity() / (float)TREE_SCALE;
|
||||
memcpy(copyAt, &gravity, sizeof(gravity));
|
||||
copyAt += sizeof(gravity);
|
||||
sizeOut += sizeof(gravity);
|
||||
}
|
||||
|
||||
// damping
|
||||
if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_DAMPING) == PACKET_CONTAINS_DAMPING)) {
|
||||
float damping = properties.getDamping();
|
||||
memcpy(copyAt, &damping, sizeof(damping));
|
||||
copyAt += sizeof(damping);
|
||||
sizeOut += sizeof(damping);
|
||||
}
|
||||
// damping
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_DAMPING) == CONTAINS_DAMPING)) {
|
||||
float damping = properties.getDamping();
|
||||
memcpy(copyAt, &damping, sizeof(damping));
|
||||
copyAt += sizeof(damping);
|
||||
sizeOut += sizeof(damping);
|
||||
}
|
||||
|
||||
// lifetime
|
||||
if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_LIFETIME) == PACKET_CONTAINS_LIFETIME)) {
|
||||
float lifetime = properties.getLifetime();
|
||||
memcpy(copyAt, &lifetime, sizeof(lifetime));
|
||||
copyAt += sizeof(lifetime);
|
||||
sizeOut += sizeof(lifetime);
|
||||
}
|
||||
// lifetime
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_LIFETIME) == CONTAINS_LIFETIME)) {
|
||||
float lifetime = properties.getLifetime();
|
||||
memcpy(copyAt, &lifetime, sizeof(lifetime));
|
||||
copyAt += sizeof(lifetime);
|
||||
sizeOut += sizeof(lifetime);
|
||||
}
|
||||
|
||||
// inHand
|
||||
if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_INHAND) == PACKET_CONTAINS_INHAND)) {
|
||||
bool inHand = properties.getInHand();
|
||||
memcpy(copyAt, &inHand, sizeof(inHand));
|
||||
copyAt += sizeof(inHand);
|
||||
sizeOut += sizeof(inHand);
|
||||
}
|
||||
// inHand
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_INHAND) == CONTAINS_INHAND)) {
|
||||
bool inHand = properties.getInHand();
|
||||
memcpy(copyAt, &inHand, sizeof(inHand));
|
||||
copyAt += sizeof(inHand);
|
||||
sizeOut += sizeof(inHand);
|
||||
}
|
||||
|
||||
// shoulDie
|
||||
if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_SHOULDDIE) == PACKET_CONTAINS_SHOULDDIE)) {
|
||||
bool shouldDie = properties.getShouldDie();
|
||||
memcpy(copyAt, &shouldDie, sizeof(shouldDie));
|
||||
copyAt += sizeof(shouldDie);
|
||||
sizeOut += sizeof(shouldDie);
|
||||
}
|
||||
// shoulDie
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_SHOULDDIE) == CONTAINS_SHOULDDIE)) {
|
||||
bool shouldDie = properties.getShouldDie();
|
||||
memcpy(copyAt, &shouldDie, sizeof(shouldDie));
|
||||
copyAt += sizeof(shouldDie);
|
||||
sizeOut += sizeof(shouldDie);
|
||||
}
|
||||
|
||||
// script
|
||||
if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_SCRIPT) == PACKET_CONTAINS_SCRIPT)) {
|
||||
uint16_t scriptLength = properties.getScript().size() + 1;
|
||||
memcpy(copyAt, &scriptLength, sizeof(scriptLength));
|
||||
copyAt += sizeof(scriptLength);
|
||||
sizeOut += sizeof(scriptLength);
|
||||
memcpy(copyAt, qPrintable(properties.getScript()), scriptLength);
|
||||
copyAt += scriptLength;
|
||||
sizeOut += scriptLength;
|
||||
}
|
||||
// script
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_SCRIPT) == CONTAINS_SCRIPT)) {
|
||||
uint16_t scriptLength = properties.getScript().size() + 1;
|
||||
memcpy(copyAt, &scriptLength, sizeof(scriptLength));
|
||||
copyAt += sizeof(scriptLength);
|
||||
sizeOut += sizeof(scriptLength);
|
||||
memcpy(copyAt, qPrintable(properties.getScript()), scriptLength);
|
||||
copyAt += scriptLength;
|
||||
sizeOut += scriptLength;
|
||||
}
|
||||
|
||||
bool wantDebugging = false;
|
||||
if (wantDebugging) {
|
||||
printf("encodeParticleEditMessageDetails()....\n");
|
||||
printf("Particle id :%u\n", id.id);
|
||||
printf(" nextID:%u\n", _nextID);
|
||||
}
|
||||
// modelURL
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_URL) == CONTAINS_MODEL_URL)) {
|
||||
uint16_t urlLength = properties.getModelURL().size() + 1;
|
||||
memcpy(copyAt, &urlLength, sizeof(urlLength));
|
||||
copyAt += sizeof(urlLength);
|
||||
sizeOut += sizeof(urlLength);
|
||||
memcpy(copyAt, qPrintable(properties.getModelURL()), urlLength);
|
||||
copyAt += urlLength;
|
||||
sizeOut += urlLength;
|
||||
}
|
||||
|
||||
// modelTranslation
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_TRANSLATION) == CONTAINS_MODEL_TRANSLATION)) {
|
||||
glm::vec3 modelTranslation = properties.getModelTranslation(); // should this be relative to TREE_SCALE??
|
||||
memcpy(copyAt, &modelTranslation, sizeof(modelTranslation));
|
||||
copyAt += sizeof(modelTranslation);
|
||||
sizeOut += sizeof(modelTranslation);
|
||||
}
|
||||
|
||||
// modelRotation
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_ROTATION) == CONTAINS_MODEL_ROTATION)) {
|
||||
int bytes = packOrientationQuatToBytes(copyAt, properties.getModelRotation());
|
||||
copyAt += bytes;
|
||||
sizeOut += bytes;
|
||||
}
|
||||
|
||||
// modelScale
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_SCALE) == CONTAINS_MODEL_SCALE)) {
|
||||
float modelScale = properties.getModelScale();
|
||||
memcpy(copyAt, &modelScale, sizeof(modelScale));
|
||||
copyAt += sizeof(modelScale);
|
||||
sizeOut += sizeof(modelScale);
|
||||
}
|
||||
|
||||
bool wantDebugging = false;
|
||||
if (wantDebugging) {
|
||||
printf("encodeParticleEditMessageDetails()....\n");
|
||||
printf("Particle id :%u\n", id.id);
|
||||
printf(" nextID:%u\n", _nextID);
|
||||
}
|
||||
|
||||
// cleanup
|
||||
|
@ -875,10 +985,15 @@ ParticleProperties::ParticleProperties() :
|
|||
_script(""),
|
||||
_inHand(false),
|
||||
_shouldDie(false),
|
||||
_modelURL(""),
|
||||
_modelTranslation(DEFAULT_MODEL_TRANSLATION),
|
||||
_modelRotation(DEFAULT_MODEL_ROTATION),
|
||||
_modelScale(DEFAULT_MODEL_SCALE),
|
||||
|
||||
_id(UNKNOWN_PARTICLE_ID),
|
||||
_idSet(false),
|
||||
_lastEdited(usecTimestampNow()),
|
||||
|
||||
_positionChanged(false),
|
||||
_colorChanged(false),
|
||||
_radiusChanged(false),
|
||||
|
@ -889,6 +1004,10 @@ ParticleProperties::ParticleProperties() :
|
|||
_scriptChanged(false),
|
||||
_inHandChanged(false),
|
||||
_shouldDieChanged(false),
|
||||
_modelURLChanged(false),
|
||||
_modelTranslationChanged(false),
|
||||
_modelRotationChanged(false),
|
||||
_modelScaleChanged(false),
|
||||
_defaultSettings(true)
|
||||
{
|
||||
}
|
||||
|
@ -897,44 +1016,59 @@ ParticleProperties::ParticleProperties() :
|
|||
uint16_t ParticleProperties::getChangedBits() const {
|
||||
uint16_t changedBits = 0;
|
||||
if (_radiusChanged) {
|
||||
changedBits += PACKET_CONTAINS_RADIUS;
|
||||
changedBits += CONTAINS_RADIUS;
|
||||
}
|
||||
|
||||
if (_positionChanged) {
|
||||
changedBits += PACKET_CONTAINS_POSITION;
|
||||
changedBits += CONTAINS_POSITION;
|
||||
}
|
||||
|
||||
if (_colorChanged) {
|
||||
changedBits += PACKET_CONTAINS_COLOR;
|
||||
changedBits += CONTAINS_COLOR;
|
||||
}
|
||||
|
||||
if (_velocityChanged) {
|
||||
changedBits += PACKET_CONTAINS_VELOCITY;
|
||||
changedBits += CONTAINS_VELOCITY;
|
||||
}
|
||||
|
||||
if (_gravityChanged) {
|
||||
changedBits += PACKET_CONTAINS_GRAVITY;
|
||||
changedBits += CONTAINS_GRAVITY;
|
||||
}
|
||||
|
||||
if (_dampingChanged) {
|
||||
changedBits += PACKET_CONTAINS_DAMPING;
|
||||
changedBits += CONTAINS_DAMPING;
|
||||
}
|
||||
|
||||
if (_lifetimeChanged) {
|
||||
changedBits += PACKET_CONTAINS_LIFETIME;
|
||||
changedBits += CONTAINS_LIFETIME;
|
||||
}
|
||||
|
||||
if (_inHandChanged) {
|
||||
changedBits += PACKET_CONTAINS_INHAND;
|
||||
changedBits += CONTAINS_INHAND;
|
||||
}
|
||||
|
||||
if (_scriptChanged) {
|
||||
changedBits += PACKET_CONTAINS_SCRIPT;
|
||||
changedBits += CONTAINS_SCRIPT;
|
||||
}
|
||||
|
||||
// how do we want to handle this?
|
||||
if (_shouldDieChanged) {
|
||||
changedBits += PACKET_CONTAINS_SHOULDDIE;
|
||||
changedBits += CONTAINS_SHOULDDIE;
|
||||
}
|
||||
|
||||
if (_modelURLChanged) {
|
||||
changedBits += CONTAINS_MODEL_URL;
|
||||
}
|
||||
|
||||
if (_modelTranslationChanged) {
|
||||
changedBits += CONTAINS_MODEL_TRANSLATION;
|
||||
}
|
||||
|
||||
if (_modelRotationChanged) {
|
||||
changedBits += CONTAINS_MODEL_ROTATION;
|
||||
}
|
||||
|
||||
if (_modelScaleChanged) {
|
||||
changedBits += CONTAINS_MODEL_SCALE;
|
||||
}
|
||||
|
||||
return changedBits;
|
||||
|
@ -963,7 +1097,18 @@ QScriptValue ParticleProperties::copyToScriptValue(QScriptEngine* engine) const
|
|||
properties.setProperty("script", _script);
|
||||
properties.setProperty("inHand", _inHand);
|
||||
properties.setProperty("shouldDie", _shouldDie);
|
||||
|
||||
|
||||
properties.setProperty("modelURL", _modelURL);
|
||||
|
||||
QScriptValue modelTranslation = vec3toScriptValue(engine, _modelTranslation);
|
||||
properties.setProperty("modelTranslation", modelTranslation);
|
||||
|
||||
QScriptValue modelRotation = quatToScriptValue(engine, _modelRotation);
|
||||
properties.setProperty("modelRotation", modelRotation);
|
||||
|
||||
properties.setProperty("modelScale", _modelScale);
|
||||
|
||||
|
||||
if (_idSet) {
|
||||
properties.setProperty("id", _id);
|
||||
properties.setProperty("isKnownID", (_id == UNKNOWN_PARTICLE_ID));
|
||||
|
@ -1104,48 +1249,147 @@ void ParticleProperties::copyFromScriptValue(const QScriptValue &object) {
|
|||
}
|
||||
}
|
||||
|
||||
QScriptValue modelURL = object.property("modelURL");
|
||||
if (modelURL.isValid()) {
|
||||
QString newModelURL;
|
||||
newModelURL = modelURL.toVariant().toString();
|
||||
if (_defaultSettings || newModelURL != _modelURL) {
|
||||
_modelURL = newModelURL;
|
||||
_modelURLChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue modelTranslation = object.property("modelTranslation");
|
||||
if (modelTranslation.isValid()) {
|
||||
QScriptValue x = modelTranslation.property("x");
|
||||
QScriptValue y = modelTranslation.property("y");
|
||||
QScriptValue z = modelTranslation.property("z");
|
||||
if (x.isValid() && y.isValid() && z.isValid()) {
|
||||
glm::vec3 newModelTranslation;
|
||||
newModelTranslation.x = x.toVariant().toFloat();
|
||||
newModelTranslation.y = y.toVariant().toFloat();
|
||||
newModelTranslation.z = z.toVariant().toFloat();
|
||||
if (_defaultSettings || newModelTranslation != _modelTranslation) {
|
||||
_modelTranslation = newModelTranslation;
|
||||
_modelTranslationChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QScriptValue modelRotation = object.property("modelRotation");
|
||||
if (modelRotation.isValid()) {
|
||||
QScriptValue x = modelRotation.property("x");
|
||||
QScriptValue y = modelRotation.property("y");
|
||||
QScriptValue z = modelRotation.property("z");
|
||||
QScriptValue w = modelRotation.property("w");
|
||||
if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) {
|
||||
glm::quat newModelRotation;
|
||||
newModelRotation.x = x.toVariant().toFloat();
|
||||
newModelRotation.y = y.toVariant().toFloat();
|
||||
newModelRotation.z = z.toVariant().toFloat();
|
||||
newModelRotation.w = w.toVariant().toFloat();
|
||||
if (_defaultSettings || newModelRotation != _modelRotation) {
|
||||
_modelRotation = newModelRotation;
|
||||
_modelRotationChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue modelScale = object.property("modelScale");
|
||||
if (modelScale.isValid()) {
|
||||
float newModelScale;
|
||||
newModelScale = modelScale.toVariant().toFloat();
|
||||
if (_defaultSettings || newModelScale != _modelScale) {
|
||||
_modelScale = newModelScale;
|
||||
_modelScaleChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
_lastEdited = usecTimestampNow();
|
||||
}
|
||||
|
||||
void ParticleProperties::copyToParticle(Particle& particle) const {
|
||||
bool somethingChanged = false;
|
||||
if (_positionChanged) {
|
||||
particle.setPosition(_position / (float) TREE_SCALE);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (_colorChanged) {
|
||||
particle.setColor(_color);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (_radiusChanged) {
|
||||
particle.setRadius(_radius / (float) TREE_SCALE);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (_velocityChanged) {
|
||||
particle.setVelocity(_velocity / (float) TREE_SCALE);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (_gravityChanged) {
|
||||
particle.setGravity(_gravity / (float) TREE_SCALE);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (_dampingChanged) {
|
||||
particle.setDamping(_damping);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (_lifetimeChanged) {
|
||||
particle.setLifetime(_lifetime);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (_scriptChanged) {
|
||||
particle.setScript(_script);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (_inHandChanged) {
|
||||
particle.setInHand(_inHand);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (_shouldDieChanged) {
|
||||
particle.setShouldDie(_shouldDie);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (_modelURLChanged) {
|
||||
particle.setModelURL(_modelURL);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (_modelTranslationChanged) {
|
||||
particle.setModelTranslation(_modelTranslation);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (_modelRotationChanged) {
|
||||
particle.setModelRotation(_modelRotation);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (_modelScaleChanged) {
|
||||
particle.setModelScale(_modelScale);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (somethingChanged) {
|
||||
bool wantDebug = false;
|
||||
if (wantDebug) {
|
||||
uint64_t now = usecTimestampNow();
|
||||
int elapsed = now - _lastEdited;
|
||||
qDebug() << "ParticleProperties::copyToParticle() AFTER update... edited AGO=" << elapsed <<
|
||||
"now=" << now << " _lastEdited=" << _lastEdited;
|
||||
}
|
||||
particle.setLastEdited(_lastEdited);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1160,6 +1404,10 @@ void ParticleProperties::copyFromParticle(const Particle& particle) {
|
|||
_script = particle.getScript();
|
||||
_inHand = particle.getInHand();
|
||||
_shouldDie = particle.getShouldDie();
|
||||
_modelURL = particle.getModelURL();
|
||||
_modelTranslation = particle.getModelTranslation();
|
||||
_modelRotation = particle.getModelRotation();
|
||||
_modelScale = particle.getModelScale();
|
||||
|
||||
_id = particle.getID();
|
||||
_idSet = true;
|
||||
|
@ -1174,6 +1422,10 @@ void ParticleProperties::copyFromParticle(const Particle& particle) {
|
|||
_scriptChanged = false;
|
||||
_inHandChanged = false;
|
||||
_shouldDieChanged = false;
|
||||
_modelURLChanged = false;
|
||||
_modelTranslationChanged = false;
|
||||
_modelRotationChanged = false;
|
||||
_modelScaleChanged = false;
|
||||
_defaultSettings = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,16 +32,20 @@ const uint32_t NEW_PARTICLE = 0xFFFFFFFF;
|
|||
const uint32_t UNKNOWN_TOKEN = 0xFFFFFFFF;
|
||||
const uint32_t UNKNOWN_PARTICLE_ID = 0xFFFFFFFF;
|
||||
|
||||
const uint16_t PACKET_CONTAINS_RADIUS = 1;
|
||||
const uint16_t PACKET_CONTAINS_POSITION = 2;
|
||||
const uint16_t PACKET_CONTAINS_COLOR = 4;
|
||||
const uint16_t PACKET_CONTAINS_VELOCITY = 8;
|
||||
const uint16_t PACKET_CONTAINS_GRAVITY = 16;
|
||||
const uint16_t PACKET_CONTAINS_DAMPING = 32;
|
||||
const uint16_t PACKET_CONTAINS_LIFETIME = 64;
|
||||
const uint16_t PACKET_CONTAINS_INHAND = 128;
|
||||
const uint16_t PACKET_CONTAINS_SCRIPT = 256;
|
||||
const uint16_t PACKET_CONTAINS_SHOULDDIE = 512;
|
||||
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;
|
||||
|
@ -49,9 +53,13 @@ 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
|
||||
|
@ -76,6 +84,10 @@ public:
|
|||
const QString& getScript() const { return _script; }
|
||||
bool getInHand() const { return _inHand; }
|
||||
bool getShouldDie() const { return _shouldDie; }
|
||||
const QString& getModelURL() const { return _modelURL; }
|
||||
const glm::vec3& getModelTranslation() const { return _modelTranslation; }
|
||||
const glm::quat& getModelRotation() const { return _modelRotation; }
|
||||
float getModelScale() const { return _modelScale; }
|
||||
|
||||
uint64_t getLastEdited() const { return _lastEdited; }
|
||||
uint16_t getChangedBits() const;
|
||||
|
@ -94,7 +106,14 @@ public:
|
|||
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(QString updateScript) { _script = updateScript; _scriptChanged = true; }
|
||||
void setScript(const QString& updateScript) { _script = updateScript; _scriptChanged = true; }
|
||||
|
||||
// model related properties
|
||||
void setModelURL(const QString& url) { _modelURL = url; _modelURLChanged = true; }
|
||||
void setModelTranslation(const glm::vec3& translation) { _modelTranslation = translation;
|
||||
_modelTranslationChanged = true; }
|
||||
void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; _modelRotationChanged = true; }
|
||||
void setModelScale(float scale) { _modelScale = scale; _modelScaleChanged = true; }
|
||||
|
||||
/// used by ParticleScriptingInterface to return ParticleProperties for unknown particles
|
||||
void setIsUnknownID() { _id = UNKNOWN_PARTICLE_ID; _idSet = true; }
|
||||
|
@ -110,10 +129,15 @@ private:
|
|||
QString _script;
|
||||
bool _inHand;
|
||||
bool _shouldDie;
|
||||
QString _modelURL;
|
||||
glm::vec3 _modelTranslation;
|
||||
glm::quat _modelRotation;
|
||||
float _modelScale;
|
||||
|
||||
uint32_t _id;
|
||||
bool _idSet;
|
||||
uint64_t _lastEdited;
|
||||
|
||||
bool _positionChanged;
|
||||
bool _colorChanged;
|
||||
bool _radiusChanged;
|
||||
|
@ -124,6 +148,10 @@ private:
|
|||
bool _scriptChanged;
|
||||
bool _inHandChanged;
|
||||
bool _shouldDieChanged;
|
||||
bool _modelURLChanged;
|
||||
bool _modelTranslationChanged;
|
||||
bool _modelRotationChanged;
|
||||
bool _modelScaleChanged;
|
||||
bool _defaultSettings;
|
||||
};
|
||||
Q_DECLARE_METATYPE(ParticleProperties);
|
||||
|
@ -162,12 +190,9 @@ class Particle {
|
|||
|
||||
public:
|
||||
Particle();
|
||||
|
||||
/// all position, velocity, gravity, radius units are in domain units (0.0 to 1.0)
|
||||
Particle(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);
|
||||
|
||||
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(unsigned char* data, int length, int& processedBytes, ParticleTree* tree, bool& valid);
|
||||
|
||||
|
@ -195,6 +220,14 @@ public:
|
|||
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; }
|
||||
const glm::vec3& getModelTranslation() const { return _modelTranslation; }
|
||||
const glm::quat& getModelRotation() const { return _modelRotation; }
|
||||
float getModelScale() const { return _modelScale; }
|
||||
|
||||
ParticleProperties getProperties() const;
|
||||
|
||||
/// The last updated/simulated time of this particle from the time perspective of the authoritative server/source
|
||||
|
@ -202,11 +235,13 @@ public:
|
|||
|
||||
/// The last edited time of this particle from the time perspective of the authoritative server/source
|
||||
uint64_t getLastEdited() const { return _lastEdited; }
|
||||
void setLastEdited(uint64_t 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; }
|
||||
|
@ -235,12 +270,18 @@ public:
|
|||
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 setModelTranslation(const glm::vec3& translation) { _modelTranslation = translation; }
|
||||
void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; }
|
||||
void setModelScale(float scale) { _modelScale = scale; }
|
||||
|
||||
void setProperties(const ParticleProperties& properties);
|
||||
|
||||
bool appendParticleData(OctreePacketData* packetData) const;
|
||||
int readParticleDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args);
|
||||
static int expectedBytes();
|
||||
static int expectedEditMessageBytes();
|
||||
|
||||
static bool encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID id, const ParticleProperties& details,
|
||||
unsigned char* bufferOut, int sizeIn, int& sizeOut);
|
||||
|
@ -299,6 +340,12 @@ protected:
|
|||
QString _script;
|
||||
bool _inHand;
|
||||
|
||||
// model related items
|
||||
QString _modelURL;
|
||||
glm::vec3 _modelTranslation;
|
||||
glm::quat _modelRotation;
|
||||
float _modelScale;
|
||||
|
||||
uint32_t _creatorTokenID;
|
||||
bool _newlyCreated;
|
||||
|
||||
|
|
|
@ -81,8 +81,8 @@ public:
|
|||
bool ParticleTree::findAndUpdateOperation(OctreeElement* element, void* extraData) {
|
||||
FindAndUpdateParticleArgs* args = static_cast<FindAndUpdateParticleArgs*>(extraData);
|
||||
ParticleTreeElement* particleTreeElement = static_cast<ParticleTreeElement*>(element);
|
||||
if (particleTreeElement->containsParticle(args->searchParticle)) {
|
||||
particleTreeElement->updateParticle(args->searchParticle);
|
||||
// Note: updateParticle() will only operate on correctly found particles
|
||||
if (particleTreeElement->updateParticle(args->searchParticle)) {
|
||||
args->found = true;
|
||||
return false; // stop searching
|
||||
}
|
||||
|
@ -106,6 +106,119 @@ void ParticleTree::storeParticle(const Particle& particle, Node* senderNode) {
|
|||
_isDirty = true;
|
||||
}
|
||||
|
||||
class FindAndUpdateParticleWithIDandPropertiesArgs {
|
||||
public:
|
||||
const ParticleID& particleID;
|
||||
const ParticleProperties& properties;
|
||||
bool found;
|
||||
};
|
||||
|
||||
bool ParticleTree::findAndUpdateWithIDandPropertiesOperation(OctreeElement* element, void* extraData) {
|
||||
FindAndUpdateParticleWithIDandPropertiesArgs* args = static_cast<FindAndUpdateParticleWithIDandPropertiesArgs*>(extraData);
|
||||
ParticleTreeElement* particleTreeElement = static_cast<ParticleTreeElement*>(element);
|
||||
// Note: updateParticle() will only operate on correctly found particles
|
||||
if (particleTreeElement->updateParticle(args->particleID, args->properties)) {
|
||||
args->found = true;
|
||||
return false; // stop searching
|
||||
}
|
||||
|
||||
// if we've found our particle stop searching
|
||||
if (args->found) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ParticleTree::updateParticle(const ParticleID& particleID, const ParticleProperties& properties) {
|
||||
// First, look for the existing particle in the tree..
|
||||
FindAndUpdateParticleWithIDandPropertiesArgs args = { particleID, properties, false };
|
||||
recurseTreeWithOperation(findAndUpdateWithIDandPropertiesOperation, &args);
|
||||
// if we found it in the tree, then mark the tree as dirty
|
||||
if (args.found) {
|
||||
_isDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleTree::addParticle(const ParticleID& particleID, const ParticleProperties& properties) {
|
||||
// This only operates on locally created particles
|
||||
if (particleID.isKnownID) {
|
||||
return; // not allowed
|
||||
}
|
||||
Particle particle(particleID, properties);
|
||||
glm::vec3 position = particle.getPosition();
|
||||
float size = std::max(MINIMUM_PARTICLE_ELEMENT_SIZE, particle.getRadius());
|
||||
ParticleTreeElement* element = (ParticleTreeElement*)getOrCreateChildElementAt(position.x, position.y, position.z, size);
|
||||
|
||||
element->storeParticle(particle);
|
||||
|
||||
_isDirty = true;
|
||||
}
|
||||
|
||||
void ParticleTree::deleteParticle(const ParticleID& particleID) {
|
||||
if (particleID.isKnownID) {
|
||||
FindAndDeleteParticlesArgs args;
|
||||
args._idsToDelete.push_back(particleID.id);
|
||||
recurseTreeWithOperation(findAndDeleteOperation, &args);
|
||||
}
|
||||
}
|
||||
|
||||
// scans the tree and handles mapping locally created particles to know IDs.
|
||||
// in the event that this tree is also viewing the scene, then we need to also
|
||||
// search the tree to make sure we don't have a duplicate particle from the viewing
|
||||
// operation.
|
||||
bool ParticleTree::findAndUpdateParticleIDOperation(OctreeElement* element, void* extraData) {
|
||||
bool keepSearching = true;
|
||||
|
||||
FindAndUpdateParticleIDArgs* args = static_cast<FindAndUpdateParticleIDArgs*>(extraData);
|
||||
ParticleTreeElement* particleTreeElement = static_cast<ParticleTreeElement*>(element);
|
||||
|
||||
// Note: updateParticleID() will only operate on correctly found particles
|
||||
particleTreeElement->updateParticleID(args);
|
||||
|
||||
// if we've found and replaced both the creatorTokenID and the viewedParticle, then we
|
||||
// can stop looking, otherwise we will keep looking
|
||||
if (args->creatorTokenFound && args->viewedParticleFound) {
|
||||
keepSearching = false;
|
||||
}
|
||||
|
||||
return keepSearching;
|
||||
}
|
||||
|
||||
void ParticleTree::handleAddParticleResponse(unsigned char* packetData , int packetLength) {
|
||||
unsigned char* dataAt = packetData;
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
|
||||
dataAt += numBytesPacketHeader;
|
||||
|
||||
uint32_t creatorTokenID;
|
||||
memcpy(&creatorTokenID, dataAt, sizeof(creatorTokenID));
|
||||
dataAt += sizeof(creatorTokenID);
|
||||
|
||||
uint32_t particleID;
|
||||
memcpy(&particleID, dataAt, sizeof(particleID));
|
||||
dataAt += sizeof(particleID);
|
||||
|
||||
// update particles in our tree
|
||||
bool assumeParticleFound = !getIsViewing(); // if we're not a viewing tree, then we don't have to find the actual particle
|
||||
FindAndUpdateParticleIDArgs args = {
|
||||
particleID,
|
||||
creatorTokenID,
|
||||
false,
|
||||
assumeParticleFound,
|
||||
getIsViewing()
|
||||
};
|
||||
|
||||
const bool wantDebug = false;
|
||||
if (wantDebug) {
|
||||
qDebug() << "looking for creatorTokenID=" << creatorTokenID << " particleID=" << particleID
|
||||
<< " getIsViewing()=" << getIsViewing();
|
||||
}
|
||||
lockForWrite();
|
||||
recurseTreeWithOperation(findAndUpdateParticleIDOperation, &args);
|
||||
unlock();
|
||||
}
|
||||
|
||||
|
||||
class FindNearPointArgs {
|
||||
public:
|
||||
glm::vec3 position;
|
||||
|
@ -413,11 +526,9 @@ bool ParticleTree::encodeParticlesDeletedSince(uint64_t& sinceTime, unsigned cha
|
|||
while (iterator != _recentlyDeletedParticleIDs.constEnd()) {
|
||||
QList<uint32_t> values = _recentlyDeletedParticleIDs.values(iterator.key());
|
||||
for (int valueItem = 0; valueItem < values.size(); ++valueItem) {
|
||||
//qDebug() << "considering... " << iterator.key() << ": " << values.at(valueItem);
|
||||
|
||||
// if the timestamp is more recent then out last sent time, include it
|
||||
if (iterator.key() > sinceTime) {
|
||||
//qDebug() << "including... " << iterator.key() << ": " << values.at(valueItem);
|
||||
uint32_t particleID = values.at(valueItem);
|
||||
memcpy(copyAt, &particleID, sizeof(particleID));
|
||||
copyAt += sizeof(particleID);
|
||||
|
@ -455,16 +566,14 @@ bool ParticleTree::encodeParticlesDeletedSince(uint64_t& sinceTime, unsigned cha
|
|||
|
||||
// called by the server when it knows all nodes have been sent deleted packets
|
||||
void ParticleTree::forgetParticlesDeletedBefore(uint64_t sinceTime) {
|
||||
//qDebug() << "forgetParticlesDeletedBefore()";
|
||||
QSet<uint64_t> keysToRemove;
|
||||
|
||||
_recentlyDeletedParticlesLock.lockForWrite();
|
||||
QMultiMap<uint64_t, uint32_t>::iterator iterator = _recentlyDeletedParticleIDs.begin();
|
||||
|
||||
// First find all the keys in the map that are older and need to be deleted
|
||||
while (iterator != _recentlyDeletedParticleIDs.end()) {
|
||||
//qDebug() << "considering... time/key:" << iterator.key();
|
||||
if (iterator.key() <= sinceTime) {
|
||||
//qDebug() << "YES older... time/key:" << iterator.key();
|
||||
keysToRemove << iterator.key();
|
||||
}
|
||||
++iterator;
|
||||
|
@ -472,18 +581,15 @@ void ParticleTree::forgetParticlesDeletedBefore(uint64_t sinceTime) {
|
|||
|
||||
// Now run through the keysToRemove and remove them
|
||||
foreach (uint64_t value, keysToRemove) {
|
||||
//qDebug() << "removing the key, _recentlyDeletedParticleIDs.remove(value); time/key:" << value;
|
||||
_recentlyDeletedParticleIDs.remove(value);
|
||||
}
|
||||
|
||||
_recentlyDeletedParticlesLock.unlock();
|
||||
//qDebug() << "DONE forgetParticlesDeletedBefore()";
|
||||
}
|
||||
|
||||
|
||||
void ParticleTree::processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr,
|
||||
Node* sourceNode) {
|
||||
//qDebug() << "ParticleTree::processEraseMessage()...";
|
||||
|
||||
const unsigned char* packetData = (const unsigned char*)dataByteArray.constData();
|
||||
const unsigned char* dataAt = packetData;
|
||||
|
@ -498,14 +604,11 @@ void ParticleTree::processEraseMessage(const QByteArray& dataByteArray, const Hi
|
|||
dataAt += sizeof(numberOfIds);
|
||||
processedBytes += sizeof(numberOfIds);
|
||||
|
||||
//qDebug() << "got erase message for numberOfIds:" << numberOfIds;
|
||||
|
||||
if (numberOfIds > 0) {
|
||||
FindAndDeleteParticlesArgs args;
|
||||
|
||||
for (size_t i = 0; i < numberOfIds; i++) {
|
||||
if (processedBytes + sizeof(uint32_t) > packetLength) {
|
||||
//qDebug() << "bailing?? processedBytes:" << processedBytes << " packetLength:" << packetLength;
|
||||
break; // bail to prevent buffer overflow
|
||||
}
|
||||
|
||||
|
@ -514,12 +617,10 @@ void ParticleTree::processEraseMessage(const QByteArray& dataByteArray, const Hi
|
|||
dataAt += sizeof(particleID);
|
||||
processedBytes += sizeof(particleID);
|
||||
|
||||
//qDebug() << "got erase message for particleID:" << particleID;
|
||||
args._idsToDelete.push_back(particleID);
|
||||
}
|
||||
|
||||
// calling recurse to actually delete the particles
|
||||
//qDebug() << "calling recurse to actually delete the particles";
|
||||
recurseTreeWithOperation(findAndDeleteOperation, &args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,9 @@ public:
|
|||
virtual void update();
|
||||
|
||||
void storeParticle(const Particle& particle, Node* senderNode = NULL);
|
||||
void updateParticle(const ParticleID& particleID, const ParticleProperties& properties);
|
||||
void addParticle(const ParticleID& particleID, const ParticleProperties& properties);
|
||||
void deleteParticle(const ParticleID& particleID);
|
||||
const Particle* findClosestParticle(glm::vec3 position, float targetRadius);
|
||||
const Particle* findParticleByID(uint32_t id, bool alreadyLocked = false);
|
||||
|
||||
|
@ -65,16 +68,19 @@ public:
|
|||
void forgetParticlesDeletedBefore(uint64_t sinceTime);
|
||||
|
||||
void processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode);
|
||||
void handleAddParticleResponse(unsigned char* packetData , int packetLength);
|
||||
|
||||
private:
|
||||
|
||||
static bool updateOperation(OctreeElement* element, void* extraData);
|
||||
static bool findAndUpdateOperation(OctreeElement* element, void* extraData);
|
||||
static bool findAndUpdateWithIDandPropertiesOperation(OctreeElement* element, void* extraData);
|
||||
static bool findNearPointOperation(OctreeElement* element, void* extraData);
|
||||
static bool findInSphereOperation(OctreeElement* element, void* extraData);
|
||||
static bool pruneOperation(OctreeElement* element, void* extraData);
|
||||
static bool findByIDOperation(OctreeElement* element, void* extraData);
|
||||
static bool findAndDeleteOperation(OctreeElement* element, void* extraData);
|
||||
static bool findAndUpdateParticleIDOperation(OctreeElement* element, void* extraData);
|
||||
|
||||
void notifyNewlyCreatedParticle(const Particle& newParticle, Node* senderNode);
|
||||
|
||||
|
|
|
@ -125,18 +125,6 @@ bool ParticleTreeElement::findSpherePenetration(const glm::vec3& center, float r
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ParticleTreeElement::containsParticle(const Particle& particle) const {
|
||||
// TODO: remove this method and force callers to use getParticleWithID() instead
|
||||
uint16_t numberOfParticles = _particles->size();
|
||||
uint32_t particleID = particle.getID();
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
if ((*_particles)[i].getID() == particleID) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ParticleTreeElement::updateParticle(const Particle& particle) {
|
||||
// NOTE: this method must first lookup the particle by ID, hence it is O(N)
|
||||
// and "particle is not found" is worst-case (full N) but maybe we don't care?
|
||||
|
@ -172,6 +160,63 @@ bool ParticleTreeElement::updateParticle(const Particle& particle) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ParticleTreeElement::updateParticle(const ParticleID& particleID, const ParticleProperties& properties) {
|
||||
uint16_t numberOfParticles = _particles->size();
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
// note: unlike storeParticle() which is called from inbound packets, this is only called by local editors
|
||||
// and therefore we can be confident that this change is higher priority and should be honored
|
||||
Particle& thisParticle = (*_particles)[i];
|
||||
|
||||
bool found = false;
|
||||
if (particleID.isKnownID) {
|
||||
found = thisParticle.getID() == particleID.id;
|
||||
} else {
|
||||
found = thisParticle.getCreatorTokenID() == particleID.creatorTokenID;
|
||||
}
|
||||
if (found) {
|
||||
thisParticle.setProperties(properties);
|
||||
|
||||
const bool wantDebug = false;
|
||||
if (wantDebug) {
|
||||
uint64_t now = usecTimestampNow();
|
||||
int elapsed = now - thisParticle.getLastEdited();
|
||||
|
||||
qDebug() << "ParticleTreeElement::updateParticle() AFTER update... edited AGO=" << elapsed <<
|
||||
"now=" << now << " thisParticle.getLastEdited()=" << thisParticle.getLastEdited();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ParticleTreeElement::updateParticleID(FindAndUpdateParticleIDArgs* args) {
|
||||
uint16_t numberOfParticles = _particles->size();
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
Particle& thisParticle = (*_particles)[i];
|
||||
|
||||
if (!args->creatorTokenFound) {
|
||||
// first, we're looking for matching creatorTokenIDs, if we find that, then we fix it to know the actual ID
|
||||
if (thisParticle.getCreatorTokenID() == args->creatorTokenID) {
|
||||
thisParticle.setID(args->particleID);
|
||||
args->creatorTokenFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if we're in an isViewing tree, we also need to look for an kill any viewed particles
|
||||
if (!args->viewedParticleFound && args->isViewing) {
|
||||
if (thisParticle.getCreatorTokenID() == UNKNOWN_TOKEN && thisParticle.getID() == args->particleID) {
|
||||
_particles->removeAt(i); // remove the particle at this index
|
||||
numberOfParticles--; // this means we have 1 fewer particle in this list
|
||||
i--; // and we actually want to back up i as well.
|
||||
args->viewedParticleFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const Particle* ParticleTreeElement::getClosestParticle(glm::vec3 position) const {
|
||||
const Particle* closestParticle = NULL;
|
||||
float closestParticleDistance = FLT_MAX;
|
||||
|
|
|
@ -26,6 +26,17 @@ public:
|
|||
QList<Particle> _movingParticles;
|
||||
};
|
||||
|
||||
class FindAndUpdateParticleIDArgs {
|
||||
public:
|
||||
uint32_t particleID;
|
||||
uint32_t creatorTokenID;
|
||||
bool creatorTokenFound;
|
||||
bool viewedParticleFound;
|
||||
bool isViewing;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class ParticleTreeElement : public OctreeElement {
|
||||
friend class ParticleTree; // to allow createElement to new us...
|
||||
|
||||
|
@ -86,8 +97,10 @@ public:
|
|||
void update(ParticleTreeUpdateArgs& args);
|
||||
void setTree(ParticleTree* tree) { _myTree = tree; }
|
||||
|
||||
bool containsParticle(const Particle& particle) const;
|
||||
bool updateParticle(const Particle& particle);
|
||||
bool updateParticle(const ParticleID& particleID, const ParticleProperties& properties);
|
||||
void updateParticleID(FindAndUpdateParticleIDArgs* args);
|
||||
|
||||
const Particle* getClosestParticle(glm::vec3 position) const;
|
||||
|
||||
/// finds all particles that touch a sphere
|
||||
|
|
|
@ -31,6 +31,13 @@ ParticleID ParticlesScriptingInterface::addParticle(const ParticleProperties& pr
|
|||
// queue the packet
|
||||
queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, id, properties);
|
||||
|
||||
// If we have a local particle tree set, then also update it.
|
||||
if (_particleTree) {
|
||||
_particleTree->lockForWrite();
|
||||
_particleTree->addParticle(id, properties);
|
||||
_particleTree->unlock();
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
|
@ -57,8 +64,10 @@ ParticleProperties ParticlesScriptingInterface::getParticleProperties(ParticleID
|
|||
return results;
|
||||
}
|
||||
if (_particleTree) {
|
||||
_particleTree->lockForRead();
|
||||
const Particle* particle = _particleTree->findParticleByID(identity.id);
|
||||
results.copyFromParticle(*particle);
|
||||
_particleTree->unlock();
|
||||
}
|
||||
|
||||
return results;
|
||||
|
@ -68,38 +77,26 @@ ParticleProperties ParticlesScriptingInterface::getParticleProperties(ParticleID
|
|||
|
||||
ParticleID ParticlesScriptingInterface::editParticle(ParticleID particleID, const ParticleProperties& properties) {
|
||||
uint32_t actualID = particleID.id;
|
||||
|
||||
// if the particle is unknown, attempt to look it up
|
||||
if (!particleID.isKnownID) {
|
||||
actualID = Particle::getIDfromCreatorTokenID(particleID.creatorTokenID);
|
||||
if (actualID == UNKNOWN_PARTICLE_ID) {
|
||||
return particleID; // bailing early
|
||||
}
|
||||
}
|
||||
|
||||
particleID.id = actualID;
|
||||
particleID.isKnownID = true;
|
||||
//qDebug() << "ParticlesScriptingInterface::editParticle()... FOUND IT!!! actualID=" << actualID;
|
||||
|
||||
bool wantDebugging = false;
|
||||
if (wantDebugging) {
|
||||
uint16_t containsBits = properties.getChangedBits();
|
||||
qDebug() << "ParticlesScriptingInterface::editParticle()... containsBits=" << containsBits;
|
||||
if ((containsBits & PACKET_CONTAINS_POSITION) == PACKET_CONTAINS_POSITION) {
|
||||
qDebug() << "ParticlesScriptingInterface::editParticle()... properties.getPositon()="
|
||||
<< properties.getPosition().x << ", "
|
||||
<< properties.getPosition().y << ", "
|
||||
<< properties.getPosition().z << "...";
|
||||
}
|
||||
if ((containsBits & PACKET_CONTAINS_VELOCITY) == PACKET_CONTAINS_VELOCITY) {
|
||||
qDebug() << "ParticlesScriptingInterface::editParticle()... properties.getVelocity()="
|
||||
<< properties.getVelocity().x << ", "
|
||||
<< properties.getVelocity().y << ", "
|
||||
<< properties.getVelocity().z << "...";
|
||||
}
|
||||
if ((containsBits & PACKET_CONTAINS_INHAND) == PACKET_CONTAINS_INHAND) {
|
||||
qDebug() << "ParticlesScriptingInterface::editParticle()... properties.getInHand()=" << properties.getInHand();
|
||||
}
|
||||
// if at this point, we know the id, send the update to the particle server
|
||||
if (actualID != UNKNOWN_PARTICLE_ID) {
|
||||
particleID.id = actualID;
|
||||
particleID.isKnownID = true;
|
||||
queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties);
|
||||
}
|
||||
|
||||
// If we have a local particle tree set, then also update it. We can do this even if we don't know
|
||||
// the actual id, because we can edit out local particles just with creatorTokenID
|
||||
if (_particleTree) {
|
||||
_particleTree->lockForWrite();
|
||||
_particleTree->updateParticle(particleID, properties);
|
||||
_particleTree->unlock();
|
||||
}
|
||||
queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties);
|
||||
return particleID;
|
||||
}
|
||||
|
||||
|
@ -130,14 +127,22 @@ void ParticlesScriptingInterface::deleteParticle(ParticleID particleID) {
|
|||
|
||||
//qDebug() << "ParticlesScriptingInterface::deleteParticle(), queueParticleMessage......";
|
||||
queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties);
|
||||
|
||||
// If we have a local particle tree set, then also update it.
|
||||
if (_particleTree) {
|
||||
_particleTree->lockForWrite();
|
||||
_particleTree->deleteParticle(particleID);
|
||||
_particleTree->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
ParticleID ParticlesScriptingInterface::findClosestParticle(const glm::vec3& center, float radius) const {
|
||||
ParticleID result(UNKNOWN_PARTICLE_ID, UNKNOWN_TOKEN, false);
|
||||
if (_particleTree) {
|
||||
_particleTree->lockForRead();
|
||||
const Particle* closestParticle = _particleTree->findClosestParticle(center/(float)TREE_SCALE,
|
||||
radius/(float)TREE_SCALE);
|
||||
|
||||
_particleTree->unlock();
|
||||
if (closestParticle) {
|
||||
result.id = closestParticle->getID();
|
||||
result.isKnownID = true;
|
||||
|
@ -150,8 +155,10 @@ ParticleID ParticlesScriptingInterface::findClosestParticle(const glm::vec3& cen
|
|||
QVector<ParticleID> ParticlesScriptingInterface::findParticles(const glm::vec3& center, float radius) const {
|
||||
QVector<ParticleID> result;
|
||||
if (_particleTree) {
|
||||
_particleTree->lockForRead();
|
||||
QVector<const Particle*> particles;
|
||||
_particleTree->findParticles(center/(float)TREE_SCALE, radius/(float)TREE_SCALE, particles);
|
||||
_particleTree->unlock();
|
||||
|
||||
foreach (const Particle* particle, particles) {
|
||||
ParticleID thisParticleID(particle->getID(), UNKNOWN_TOKEN, true);
|
||||
|
|
|
@ -10,8 +10,12 @@
|
|||
#define __hifi__AbstractControllerScriptingInterface__
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "EventTypes.h"
|
||||
|
||||
|
||||
/// handles scripting of input controller commands from JS
|
||||
class AbstractControllerScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -33,6 +37,34 @@ public slots:
|
|||
virtual glm::vec3 getSpatialControlPosition(int controlIndex) const = 0;
|
||||
virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const = 0;
|
||||
virtual glm::vec3 getSpatialControlNormal(int controlIndex) const = 0;
|
||||
|
||||
virtual void captureKeyEvents(const KeyEvent& event) = 0;
|
||||
virtual void releaseKeyEvents(const KeyEvent& event) = 0;
|
||||
|
||||
virtual void captureMouseEvents() = 0;
|
||||
virtual void releaseMouseEvents() = 0;
|
||||
|
||||
virtual void captureTouchEvents() = 0;
|
||||
virtual void releaseTouchEvents() = 0;
|
||||
|
||||
virtual void captureWheelEvents() = 0;
|
||||
virtual void releaseWheelEvents() = 0;
|
||||
|
||||
|
||||
signals:
|
||||
void keyPressEvent(const KeyEvent& event);
|
||||
void keyReleaseEvent(const KeyEvent& event);
|
||||
|
||||
void mouseMoveEvent(const MouseEvent& event);
|
||||
void mousePressEvent(const MouseEvent& event);
|
||||
void mouseReleaseEvent(const MouseEvent& event);
|
||||
|
||||
void touchBeginEvent(const TouchEvent& event);
|
||||
void touchEndEvent(const TouchEvent& event);
|
||||
void touchUpdateEvent(const TouchEvent& event);
|
||||
|
||||
void wheelEvent(const WheelEvent& event);
|
||||
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__AbstractControllerScriptingInterface__) */
|
||||
|
|
111
libraries/script-engine/src/EventTypes.cpp
Normal file
111
libraries/script-engine/src/EventTypes.cpp
Normal file
|
@ -0,0 +1,111 @@
|
|||
//
|
||||
// EventTypes.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 1/28/14.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Used to register meta-types with Qt for very various event types so that they can be exposed to our
|
||||
// scripting engine
|
||||
|
||||
#include "EventTypes.h"
|
||||
|
||||
|
||||
KeyEvent::KeyEvent() {
|
||||
key = 0;
|
||||
isShifted = false;
|
||||
isMeta = false;
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
|
||||
KeyEvent::KeyEvent(const QKeyEvent& event) {
|
||||
key = event.key();
|
||||
isShifted = event.modifiers().testFlag(Qt::ShiftModifier);
|
||||
isMeta = event.modifiers().testFlag(Qt::ControlModifier);
|
||||
isValid = true;
|
||||
}
|
||||
|
||||
MouseEvent::MouseEvent(const QMouseEvent& event) {
|
||||
x = event.x();
|
||||
y = event.y();
|
||||
}
|
||||
|
||||
TouchEvent::TouchEvent(const QTouchEvent& event) {
|
||||
// convert the touch points into an average
|
||||
const QList<QTouchEvent::TouchPoint>& tPoints = event.touchPoints();
|
||||
float touchAvgX = 0.0f;
|
||||
float touchAvgY = 0.0f;
|
||||
int numTouches = tPoints.count();
|
||||
if (numTouches > 1) {
|
||||
for (int i = 0; i < numTouches; ++i) {
|
||||
touchAvgX += tPoints[i].pos().x();
|
||||
touchAvgY += tPoints[i].pos().y();
|
||||
}
|
||||
touchAvgX /= (float)(numTouches);
|
||||
touchAvgY /= (float)(numTouches);
|
||||
}
|
||||
x = touchAvgX;
|
||||
y = touchAvgY;
|
||||
}
|
||||
|
||||
WheelEvent::WheelEvent(const QWheelEvent& event) {
|
||||
x = event.x();
|
||||
y = event.y();
|
||||
}
|
||||
|
||||
|
||||
void registerEventTypes(QScriptEngine* engine) {
|
||||
qScriptRegisterMetaType(engine, keyEventToScriptValue, keyEventFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, mouseEventToScriptValue, mouseEventFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, touchEventToScriptValue, touchEventFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, wheelEventToScriptValue, wheelEventFromScriptValue);
|
||||
}
|
||||
|
||||
QScriptValue keyEventToScriptValue(QScriptEngine* engine, const KeyEvent& event) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("key", event.key);
|
||||
obj.setProperty("isShifted", event.isShifted);
|
||||
obj.setProperty("isMeta", event.isMeta);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void keyEventFromScriptValue(const QScriptValue &object, KeyEvent& event) {
|
||||
event.key = object.property("key").toVariant().toInt();
|
||||
event.isShifted = object.property("isShifted").toVariant().toBool();
|
||||
event.isMeta = object.property("isMeta").toVariant().toBool();
|
||||
event.isValid = object.property("key").isValid();
|
||||
}
|
||||
|
||||
QScriptValue mouseEventToScriptValue(QScriptEngine* engine, const MouseEvent& event) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("x", event.x);
|
||||
obj.setProperty("y", event.y);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void mouseEventFromScriptValue(const QScriptValue &object, MouseEvent& event) {
|
||||
// nothing for now...
|
||||
}
|
||||
|
||||
QScriptValue touchEventToScriptValue(QScriptEngine* engine, const TouchEvent& event) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("x", event.x);
|
||||
obj.setProperty("y", event.y);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void touchEventFromScriptValue(const QScriptValue &object, TouchEvent& event) {
|
||||
// nothing for now...
|
||||
}
|
||||
|
||||
QScriptValue wheelEventToScriptValue(QScriptEngine* engine, const WheelEvent& event) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("x", event.x);
|
||||
obj.setProperty("y", event.y);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void wheelEventFromScriptValue(const QScriptValue &object, WheelEvent& event) {
|
||||
// nothing for now...
|
||||
}
|
79
libraries/script-engine/src/EventTypes.h
Normal file
79
libraries/script-engine/src/EventTypes.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
//
|
||||
// EventTypes.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 1/28/14.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi_EventTypes_h__
|
||||
#define __hifi_EventTypes_h__
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <QtScript/QScriptEngine>
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QTouchEvent>
|
||||
#include <QWheelEvent>
|
||||
|
||||
|
||||
class KeyEvent {
|
||||
public:
|
||||
KeyEvent();
|
||||
KeyEvent(const QKeyEvent& event);
|
||||
inline bool operator==(const KeyEvent& other) const {
|
||||
return other.key == key && other.isShifted == isShifted && other.isMeta == isMeta; }
|
||||
int key;
|
||||
bool isShifted;
|
||||
bool isMeta;
|
||||
bool isValid;
|
||||
};
|
||||
|
||||
|
||||
class MouseEvent {
|
||||
public:
|
||||
MouseEvent() : x(0), y(0) { };
|
||||
MouseEvent(const QMouseEvent& event);
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
class TouchEvent {
|
||||
public:
|
||||
TouchEvent() : x(0), y(0) { };
|
||||
TouchEvent(const QTouchEvent& event);
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
class WheelEvent {
|
||||
public:
|
||||
WheelEvent() : x(0), y(0) { };
|
||||
WheelEvent(const QWheelEvent& event);
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
|
||||
Q_DECLARE_METATYPE(KeyEvent)
|
||||
Q_DECLARE_METATYPE(MouseEvent)
|
||||
Q_DECLARE_METATYPE(TouchEvent)
|
||||
Q_DECLARE_METATYPE(WheelEvent)
|
||||
|
||||
void registerEventTypes(QScriptEngine* engine);
|
||||
|
||||
QScriptValue keyEventToScriptValue(QScriptEngine* engine, const KeyEvent& event);
|
||||
void keyEventFromScriptValue(const QScriptValue &object, KeyEvent& event);
|
||||
|
||||
QScriptValue mouseEventToScriptValue(QScriptEngine* engine, const MouseEvent& event);
|
||||
void mouseEventFromScriptValue(const QScriptValue &object, MouseEvent& event);
|
||||
|
||||
QScriptValue touchEventToScriptValue(QScriptEngine* engine, const TouchEvent& event);
|
||||
void touchEventFromScriptValue(const QScriptValue &object, TouchEvent& event);
|
||||
|
||||
QScriptValue wheelEventToScriptValue(QScriptEngine* engine, const WheelEvent& event);
|
||||
void wheelEventFromScriptValue(const QScriptValue &object, WheelEvent& event);
|
||||
|
||||
#endif // __hifi_EventTypes_h__
|
20
libraries/script-engine/src/Quat.cpp
Normal file
20
libraries/script-engine/src/Quat.cpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// Quat.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 1/29/14
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Scriptable Quaternion class library.
|
||||
//
|
||||
//
|
||||
|
||||
#include "Quat.h"
|
||||
|
||||
glm::quat Quat::multiply(const glm::quat& q1, const glm::quat& q2) {
|
||||
return q1 * q2;
|
||||
}
|
||||
|
||||
glm::quat Quat::fromVec3(const glm::vec3& vec3) {
|
||||
return glm::quat(vec3);
|
||||
}
|
29
libraries/script-engine/src/Quat.h
Normal file
29
libraries/script-engine/src/Quat.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// Quat.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 1/29/14
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Scriptable Quaternion class library.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef __hifi__Quat__
|
||||
#define __hifi__Quat__
|
||||
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <QtCore/QObject>
|
||||
|
||||
/// Scriptable interface a Quaternion helper class object. Used exclusively in the JavaScript API
|
||||
class Quat : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public slots:
|
||||
glm::quat multiply(const glm::quat& q1, const glm::quat& q2);
|
||||
glm::quat fromVec3(const glm::vec3& vec3);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* defined(__hifi__Quat__) */
|
|
@ -116,19 +116,12 @@ void ScriptEngine::init() {
|
|||
|
||||
// register meta-type for glm::vec3 conversions
|
||||
registerMetaTypes(&_engine);
|
||||
|
||||
registerEventTypes(&_engine);
|
||||
qScriptRegisterMetaType(&_engine, ParticlePropertiesToScriptValue, ParticlePropertiesFromScriptValue);
|
||||
qScriptRegisterMetaType(&_engine, ParticleIDtoScriptValue, ParticleIDfromScriptValue);
|
||||
qScriptRegisterSequenceMetaType<QVector<ParticleID> >(&_engine);
|
||||
|
||||
QScriptValue agentValue = _engine.newQObject(this);
|
||||
_engine.globalObject().setProperty("Agent", agentValue);
|
||||
|
||||
QScriptValue voxelScripterValue = _engine.newQObject(&_voxelsScriptingInterface);
|
||||
_engine.globalObject().setProperty("Voxels", voxelScripterValue);
|
||||
|
||||
QScriptValue particleScripterValue = _engine.newQObject(&_particlesScriptingInterface);
|
||||
_engine.globalObject().setProperty("Particles", particleScripterValue);
|
||||
|
||||
QScriptValue soundConstructorValue = _engine.newFunction(soundConstructor);
|
||||
QScriptValue soundMetaObject = _engine.newQMetaObject(&Sound::staticMetaObject, soundConstructorValue);
|
||||
_engine.globalObject().setProperty("Sound", soundMetaObject);
|
||||
|
@ -136,15 +129,14 @@ void ScriptEngine::init() {
|
|||
QScriptValue injectionOptionValue = _engine.scriptValueFromQMetaObject<AudioInjectorOptions>();
|
||||
_engine.globalObject().setProperty("AudioInjectionOptions", injectionOptionValue);
|
||||
|
||||
QScriptValue audioScriptingInterfaceValue = _engine.newQObject(&_audioScriptingInterface);
|
||||
_engine.globalObject().setProperty("Audio", audioScriptingInterfaceValue);
|
||||
|
||||
registerGlobalObject("Agent", this);
|
||||
registerGlobalObject("Audio", &_audioScriptingInterface);
|
||||
registerGlobalObject("Controller", _controllerScriptingInterface);
|
||||
registerGlobalObject("Data", &_dataServerScriptingInterface);
|
||||
registerGlobalObject("Particles", &_particlesScriptingInterface);
|
||||
registerGlobalObject("Quat", &_quatLibrary);
|
||||
|
||||
if (_controllerScriptingInterface) {
|
||||
QScriptValue controllerScripterValue = _engine.newQObject(_controllerScriptingInterface);
|
||||
_engine.globalObject().setProperty("Controller", controllerScripterValue);
|
||||
}
|
||||
registerGlobalObject("Voxels", &_voxelsScriptingInterface);
|
||||
|
||||
QScriptValue treeScaleValue = _engine.newVariant(QVariant(TREE_SCALE));
|
||||
_engine.globalObject().setProperty("TREE_SCALE", treeScaleValue);
|
||||
|
@ -157,8 +149,10 @@ void ScriptEngine::init() {
|
|||
}
|
||||
|
||||
void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) {
|
||||
QScriptValue value = _engine.newQObject(object);
|
||||
_engine.globalObject().setProperty(name, value);
|
||||
if (object) {
|
||||
QScriptValue value = _engine.newQObject(object);
|
||||
_engine.globalObject().setProperty(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngine::evaluate() {
|
||||
|
|
|
@ -25,6 +25,7 @@ class ParticlesScriptingInterface;
|
|||
|
||||
#include "AbstractControllerScriptingInterface.h"
|
||||
#include "DataServerScriptingInterface.h"
|
||||
#include "Quat.h"
|
||||
|
||||
const QString NO_SCRIPT("");
|
||||
|
||||
|
@ -94,6 +95,7 @@ private:
|
|||
QString _fileNameString;
|
||||
AbstractMenuInterface* _menu;
|
||||
static int _scriptNumber;
|
||||
Quat _quatLibrary;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__ScriptEngine__) */
|
||||
|
|
|
@ -45,10 +45,10 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
|
|||
return 1;
|
||||
|
||||
case PACKET_TYPE_PARTICLE_ADD_OR_EDIT:
|
||||
return 4;
|
||||
return 5;
|
||||
|
||||
case PACKET_TYPE_PARTICLE_DATA:
|
||||
return 8;
|
||||
return 9;
|
||||
|
||||
case PACKET_TYPE_PING_REPLY:
|
||||
return 1;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
void registerMetaTypes(QScriptEngine* engine) {
|
||||
qScriptRegisterMetaType(engine, vec3toScriptValue, vec3FromScriptValue);
|
||||
qScriptRegisterMetaType(engine, vec2toScriptValue, vec2FromScriptValue);
|
||||
qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, xColorToScriptValue, xColorFromScriptValue);
|
||||
}
|
||||
|
||||
|
@ -42,6 +43,21 @@ void vec2FromScriptValue(const QScriptValue &object, glm::vec2 &vec2) {
|
|||
vec2.y = object.property("y").toVariant().toFloat();
|
||||
}
|
||||
|
||||
QScriptValue quatToScriptValue(QScriptEngine* engine, const glm::quat& quat) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("x", quat.x);
|
||||
obj.setProperty("y", quat.y);
|
||||
obj.setProperty("z", quat.z);
|
||||
obj.setProperty("w", quat.w);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void quatFromScriptValue(const QScriptValue &object, glm::quat& quat) {
|
||||
quat.x = object.property("x").toVariant().toFloat();
|
||||
quat.y = object.property("y").toVariant().toFloat();
|
||||
quat.z = object.property("z").toVariant().toFloat();
|
||||
quat.w = object.property("w").toVariant().toFloat();
|
||||
}
|
||||
|
||||
QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
Q_DECLARE_METATYPE(glm::vec3)
|
||||
Q_DECLARE_METATYPE(glm::vec2)
|
||||
Q_DECLARE_METATYPE(glm::quat)
|
||||
Q_DECLARE_METATYPE(xColor)
|
||||
|
||||
void registerMetaTypes(QScriptEngine* engine);
|
||||
|
@ -29,6 +30,9 @@ void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3);
|
|||
QScriptValue vec2toScriptValue(QScriptEngine* engine, const glm::vec2 &vec2);
|
||||
void vec2FromScriptValue(const QScriptValue &object, glm::vec2 &vec2);
|
||||
|
||||
QScriptValue quatToScriptValue(QScriptEngine* engine, const glm::quat& quat);
|
||||
void quatFromScriptValue(const QScriptValue &object, glm::quat& quat);
|
||||
|
||||
QScriptValue xColorToScriptValue(QScriptEngine* engine, const xColor& color);
|
||||
void xColorFromScriptValue(const QScriptValue &object, xColor& color);
|
||||
|
||||
|
|
|
@ -607,7 +607,7 @@ int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput
|
|||
return sizeof(quatParts);
|
||||
}
|
||||
|
||||
int unpackOrientationQuatFromBytes(unsigned char* buffer, glm::quat& quatOutput) {
|
||||
int unpackOrientationQuatFromBytes(const unsigned char* buffer, glm::quat& quatOutput) {
|
||||
uint16_t quatParts[4];
|
||||
memcpy(&quatParts, buffer, sizeof(quatParts));
|
||||
|
||||
|
@ -715,3 +715,31 @@ void debug::checkDeadBeef(void* memoryVoid, int size) {
|
|||
assert(memcmp(memoryAt, DEADBEEF, std::min(size, DEADBEEF_SIZE)) != 0);
|
||||
}
|
||||
|
||||
// Safe version of glm::eulerAngles; uses the factorization method described in David Eberly's
|
||||
// http://www.geometrictools.com/Documentation/EulerAngles.pdf (via Clyde,
|
||||
// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java)
|
||||
glm::vec3 safeEulerAngles(const glm::quat& q) {
|
||||
float sy = 2.0f * (q.y * q.w - q.x * q.z);
|
||||
if (sy < 1.0f - EPSILON) {
|
||||
if (sy > -1.0f + EPSILON) {
|
||||
return glm::degrees(glm::vec3(
|
||||
atan2f(q.y * q.z + q.x * q.w, 0.5f - (q.x * q.x + q.y * q.y)),
|
||||
asinf(sy),
|
||||
atan2f(q.x * q.y + q.z * q.w, 0.5f - (q.y * q.y + q.z * q.z))));
|
||||
|
||||
} else {
|
||||
// not a unique solution; x + z = atan2(-m21, m11)
|
||||
return glm::degrees(glm::vec3(
|
||||
0.0f,
|
||||
PIf * -0.5f,
|
||||
atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z))));
|
||||
}
|
||||
} else {
|
||||
// not a unique solution; x - z = atan2(-m21, m11)
|
||||
return glm::degrees(glm::vec3(
|
||||
0.0f,
|
||||
PIf * 0.5f,
|
||||
-atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z))));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -152,7 +152,7 @@ int unpackFloatAngleFromTwoByte(uint16_t* byteAnglePointer, float* destinationPo
|
|||
// Orientation Quats are known to have 4 normalized components be between -1.0 and 1.0
|
||||
// this allows us to encode each component in 16bits with great accuracy
|
||||
int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput);
|
||||
int unpackOrientationQuatFromBytes(unsigned char* buffer, glm::quat& quatOutput);
|
||||
int unpackOrientationQuatFromBytes(const unsigned char* buffer, glm::quat& quatOutput);
|
||||
|
||||
// Ratios need the be highly accurate when less than 10, but not very accurate above 10, and they
|
||||
// are never greater than 1000 to 1, this allows us to encode each component in 16bits
|
||||
|
@ -172,8 +172,14 @@ int unpackFloatFromByte(unsigned char* buffer, float& value, float scaleBy);
|
|||
int packFloatScalarToSignedTwoByteFixed(unsigned char* buffer, float scalar, int radix);
|
||||
int unpackFloatScalarFromSignedTwoByteFixed(int16_t* byteFixedPointer, float* destinationPointer, int radix);
|
||||
|
||||
// A convenience for sending vec3's as fixed-poimt floats
|
||||
// A convenience for sending vec3's as fixed-point floats
|
||||
int packFloatVec3ToSignedTwoByteFixed(unsigned char* destBuffer, const glm::vec3& srcVector, int radix);
|
||||
int unpackFloatVec3FromSignedTwoByteFixed(unsigned char* sourceBuffer, glm::vec3& destination, int radix);
|
||||
|
||||
#ifndef PIf
|
||||
#define PIf 3.14159265f
|
||||
#endif
|
||||
|
||||
glm::vec3 safeEulerAngles(const glm::quat& q);
|
||||
|
||||
#endif /* defined(__hifi__SharedUtil__) */
|
||||
|
|
Loading…
Reference in a new issue