mirror of
https://github.com/lubosz/overte.git
synced 2025-04-24 09:43:25 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into authentication
This commit is contained in:
commit
05f086ae74
49 changed files with 2568 additions and 545 deletions
135
examples/cameraExample.js
Normal file
135
examples/cameraExample.js
Normal file
|
@ -0,0 +1,135 @@
|
|||
//
|
||||
// cameraExample.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 2/6/14.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// This is an example script that demonstrates use of the Camera class
|
||||
//
|
||||
//
|
||||
|
||||
var damping = 0.9;
|
||||
var yaw = 0.0;
|
||||
var pitch = 0.0;
|
||||
var roll = 0.0;
|
||||
var thrust = { x: 0, y: 0, z: 0 };
|
||||
var velocity = { x: 0, y: 0, z: 0 };
|
||||
var position = { x: MyAvatar.position.x, y: MyAvatar.position.y + 1, z: MyAvatar.position.z };
|
||||
var joysticksCaptured = false;
|
||||
var THRUST_CONTROLLER = 0;
|
||||
var VIEW_CONTROLLER = 1;
|
||||
|
||||
function checkCamera() {
|
||||
if (Camera.getMode() == "independent") {
|
||||
var deltaTime = 1/60; // approximately our FPS - maybe better to be elapsed time since last call
|
||||
var THRUST_MAG_UP = 800.0;
|
||||
var THRUST_MAG_DOWN = 300.0;
|
||||
var THRUST_MAG_FWD = 500.0;
|
||||
var THRUST_MAG_BACK = 300.0;
|
||||
var THRUST_MAG_LATERAL = 250.0;
|
||||
var THRUST_JUMP = 120.0;
|
||||
var scale = 1.0;
|
||||
var thrustMultiplier = 1.0; // maybe increase this as you hold it down?
|
||||
|
||||
var YAW_MAG = 500.0;
|
||||
var PITCH_MAG = 100.0;
|
||||
var THRUST_MAG_HAND_JETS = THRUST_MAG_FWD;
|
||||
var JOYSTICK_YAW_MAG = YAW_MAG;
|
||||
var JOYSTICK_PITCH_MAG = PITCH_MAG * 0.5;
|
||||
|
||||
var thrustJoystickPosition = Controller.getJoystickPosition(THRUST_CONTROLLER);
|
||||
|
||||
var currentOrientation = Camera.getOrientation();
|
||||
|
||||
var front = Quat.getFront(currentOrientation);
|
||||
var right = Quat.getRight(currentOrientation);
|
||||
var up = Quat.getUp(currentOrientation);
|
||||
|
||||
var thrustFront = Vec3.multiply(front, scale * THRUST_MAG_HAND_JETS * thrustJoystickPosition.y * thrustMultiplier * deltaTime);
|
||||
var thrustRight = Vec3.multiply(right, scale * THRUST_MAG_HAND_JETS * thrustJoystickPosition.x * thrustMultiplier * deltaTime);
|
||||
|
||||
thrust = Vec3.sum(thrust, thrustFront);
|
||||
thrust = Vec3.sum(thrust, thrustRight);
|
||||
|
||||
// add thrust to velocity
|
||||
velocity = Vec3.sum(velocity, Vec3.multiply(thrust, deltaTime));
|
||||
|
||||
// add velocity to position
|
||||
position = Vec3.sum(position, Vec3.multiply(velocity, deltaTime));
|
||||
Camera.setPosition(position);
|
||||
|
||||
// reset thrust
|
||||
thrust = { x: 0, y: 0, z: 0 };
|
||||
|
||||
// damp velocity
|
||||
velocity = Vec3.multiply(velocity, damping);
|
||||
|
||||
// View Controller
|
||||
var viewJoystickPosition = Controller.getJoystickPosition(VIEW_CONTROLLER);
|
||||
yaw -= viewJoystickPosition.x * JOYSTICK_YAW_MAG * deltaTime;
|
||||
pitch += viewJoystickPosition.y * JOYSTICK_PITCH_MAG * deltaTime;
|
||||
var orientation = Quat.fromPitchYawRoll(pitch, yaw, roll);
|
||||
Camera.setOrientation(orientation);
|
||||
}
|
||||
}
|
||||
|
||||
Script.willSendVisualDataCallback.connect(checkCamera);
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
print("mouseMoveEvent event.x,y=" + event.x + ", " + event.y);
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
print("called Camera.computePickRay()");
|
||||
print("computePickRay origin=" + pickRay.origin.x + ", " + pickRay.origin.y + ", " + pickRay.origin.z);
|
||||
print("computePickRay direction=" + pickRay.direction.x + ", " + pickRay.direction.y + ", " + pickRay.direction.z);
|
||||
}
|
||||
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
|
||||
|
||||
function keyPressEvent(event) {
|
||||
if (joysticksCaptured) {
|
||||
Controller.releaseJoystick(THRUST_CONTROLLER);
|
||||
Controller.releaseJoystick(VIEW_CONTROLLER);
|
||||
joysticksCaptured = false;
|
||||
}
|
||||
|
||||
if (event.text == "1") {
|
||||
Camera.setMode("first person");
|
||||
}
|
||||
|
||||
if (event.text == "2") {
|
||||
Camera.setMode("mirror");
|
||||
}
|
||||
|
||||
if (event.text == "3") {
|
||||
Camera.setMode("third person");
|
||||
}
|
||||
|
||||
if (event.text == "4") {
|
||||
Camera.setMode("independent");
|
||||
joysticksCaptured = true;
|
||||
Controller.captureJoystick(THRUST_CONTROLLER);
|
||||
Controller.captureJoystick(VIEW_CONTROLLER);
|
||||
position = { x: MyAvatar.position.x, y: MyAvatar.position.y + 1, z: MyAvatar.position.z };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Map keyPress and mouse move events to our callbacks
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Controller.captureKeyEvents({ text: "1" });
|
||||
Controller.captureKeyEvents({ text: "2" });
|
||||
Controller.captureKeyEvents({ text: "3" });
|
||||
Controller.captureKeyEvents({ text: "4" });
|
||||
function scriptEnding() {
|
||||
// re-enabled the standard application for touch events
|
||||
Controller.releaseKeyEvents({ text: "1" });
|
||||
Controller.releaseKeyEvents({ text: "2" });
|
||||
Controller.releaseKeyEvents({ text: "3" });
|
||||
Controller.releaseKeyEvents({ text: "4" });
|
||||
Controller.releaseJoystick(THRUST_CONTROLLER);
|
||||
Controller.releaseJoystick(VIEW_CONTROLLER);
|
||||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
|
@ -1,171 +0,0 @@
|
|||
//
|
||||
// sphere.js
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 12/17/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
function strictIndexOf(array, element) {
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
if (array[i] == element) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
var colorIndex;
|
||||
var normalIndex;
|
||||
var visitor;
|
||||
var info;
|
||||
|
||||
var MAX_DEPTH = 4;
|
||||
|
||||
var sphereCenter = [ 0.5, 0.5, 0.5 ];
|
||||
var sphereColor = 0xFFFF00FF;
|
||||
var sphereRadius = 0.25;
|
||||
var sphereRadiusSquared = sphereRadius * sphereRadius;
|
||||
|
||||
function lengthSquared(x, y, z) {
|
||||
return x*x + y*y + z*z;
|
||||
}
|
||||
|
||||
function setNormal(vector) {
|
||||
if (normalIndex != -1) {
|
||||
var length = Math.sqrt(lengthSquared(vector[0], vector[1], vector[2]));
|
||||
if (length == 0.0) {
|
||||
info.inputValues[normalIndex] = 0x007F00;
|
||||
|
||||
} else {
|
||||
var scale = 127.0 / length;
|
||||
info.inputValues[normalIndex] =
|
||||
(Math.floor(vector[0] * scale) & 0xFF) << 16 |
|
||||
(Math.floor(vector[1] * scale) & 0xFF) << 8 |
|
||||
Math.floor(vector[2] * scale) & 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function guide(minimum, size, depth) {
|
||||
info.minimum = minimum;
|
||||
info.size = size;
|
||||
|
||||
// start with a relative fast bounding volume test to find most non-intersecting states
|
||||
var maximum = [ minimum[0] + size, minimum[1] + size, minimum[2] + size ];
|
||||
if (minimum[0] >= sphereCenter[0] + sphereRadius ||
|
||||
minimum[1] >= sphereCenter[1] + sphereRadius ||
|
||||
minimum[2] >= sphereCenter[2] + sphereRadius ||
|
||||
maximum[0] <= sphereCenter[0] - sphereRadius ||
|
||||
maximum[1] <= sphereCenter[1] - sphereRadius ||
|
||||
maximum[2] <= sphereCenter[2] - sphereRadius) {
|
||||
info.isLeaf = true;
|
||||
if (colorIndex != -1) {
|
||||
info.inputValues[colorIndex] = 0x0;
|
||||
}
|
||||
visitor.visit(info);
|
||||
return;
|
||||
}
|
||||
|
||||
var halfSize = size / 2;
|
||||
var center = [ minimum[0] + halfSize, minimum[1] + halfSize, minimum[2] + halfSize ];
|
||||
var vector = [ center[0] - sphereCenter[0], center[1] - sphereCenter[1], center[2] - sphereCenter[2] ];
|
||||
|
||||
// count the number of points inside the sphere
|
||||
var inside = 0;
|
||||
if (lengthSquared(sphereCenter[0] - minimum[0], sphereCenter[1] - minimum[1], sphereCenter[2] - minimum[2]) <=
|
||||
sphereRadiusSquared) {
|
||||
inside++;
|
||||
}
|
||||
if (lengthSquared(sphereCenter[0] - maximum[0], sphereCenter[1] - minimum[1], sphereCenter[2] - minimum[2]) <=
|
||||
sphereRadiusSquared) {
|
||||
inside++;
|
||||
}
|
||||
if (lengthSquared(sphereCenter[0] - minimum[0], sphereCenter[1] - maximum[1], sphereCenter[2] - minimum[2]) <=
|
||||
sphereRadiusSquared) {
|
||||
inside++;
|
||||
}
|
||||
if (lengthSquared(sphereCenter[0] - maximum[0], sphereCenter[1] - maximum[1], sphereCenter[2] - minimum[2]) <=
|
||||
sphereRadiusSquared) {
|
||||
inside++;
|
||||
}
|
||||
if (lengthSquared(sphereCenter[0] - minimum[0], sphereCenter[1] - minimum[1], sphereCenter[2] - maximum[2]) <=
|
||||
sphereRadiusSquared) {
|
||||
inside++;
|
||||
}
|
||||
if (lengthSquared(sphereCenter[0] - maximum[0], sphereCenter[1] - minimum[1], sphereCenter[2] - maximum[2]) <=
|
||||
sphereRadiusSquared) {
|
||||
inside++;
|
||||
}
|
||||
if (lengthSquared(sphereCenter[0] - minimum[0], sphereCenter[1] - maximum[1], sphereCenter[2] - maximum[2]) <=
|
||||
sphereRadiusSquared) {
|
||||
inside++;
|
||||
}
|
||||
if (lengthSquared(sphereCenter[0] - maximum[0], sphereCenter[1] - maximum[1], sphereCenter[2] - maximum[2]) <=
|
||||
sphereRadiusSquared) {
|
||||
inside++;
|
||||
}
|
||||
|
||||
// see if all points are in the sphere
|
||||
if (inside == 8) {
|
||||
info.isLeaf = true;
|
||||
if (colorIndex != -1) {
|
||||
info.inputValues[colorIndex] = sphereColor;
|
||||
}
|
||||
setNormal(vector);
|
||||
visitor.visit(info);
|
||||
return;
|
||||
}
|
||||
|
||||
// if we've reached max depth, compute alpha using a volume estimate
|
||||
if (depth == MAX_DEPTH) {
|
||||
info.isLeaf = true;
|
||||
if (inside >= 3) {
|
||||
if (colorIndex != -1) {
|
||||
info.inputValues[colorIndex] = sphereColor;
|
||||
}
|
||||
setNormal(vector);
|
||||
|
||||
} else {
|
||||
if (colorIndex != -1) {
|
||||
info.inputValues[colorIndex] = 0x0;
|
||||
}
|
||||
}
|
||||
visitor.visit(info);
|
||||
return;
|
||||
}
|
||||
|
||||
// recurse
|
||||
info.isLeaf = false;
|
||||
if (!visitor.visit(info)) {
|
||||
return;
|
||||
}
|
||||
depth += 1;
|
||||
guide(minimum, halfSize, depth);
|
||||
guide([ center[0], minimum[1], minimum[2] ], halfSize, depth);
|
||||
guide([ minimum[0], center[1], minimum[2] ], halfSize, depth);
|
||||
guide([ center[0], center[1], minimum[2] ], halfSize, depth);
|
||||
guide([ minimum[0], minimum[1], center[2] ], halfSize, depth);
|
||||
guide([ center[0], minimum[1], center[2] ], halfSize, depth);
|
||||
guide([ minimum[0], center[1], center[2] ], halfSize, depth);
|
||||
guide([ center[0], center[1], center[2] ], halfSize, depth);
|
||||
}
|
||||
|
||||
(function(visitation) {
|
||||
var inputs = visitation.visitor.getInputs();
|
||||
colorIndex = strictIndexOf(inputs, AttributeRegistry.colorAttribute);
|
||||
normalIndex = strictIndexOf(inputs, AttributeRegistry.normalAttribute);
|
||||
visitor = visitation.visitor;
|
||||
info = { inputValues: new Array(inputs.length) };
|
||||
|
||||
// have the sphere orbit the center and pulse in size
|
||||
var time = new Date().getTime();
|
||||
var ROTATE_PERIOD = 400.0;
|
||||
sphereCenter[0] = 0.5 + 0.25 * Math.cos(time / ROTATE_PERIOD);
|
||||
sphereCenter[2] = 0.5 + 0.25 * Math.sin(time / ROTATE_PERIOD);
|
||||
var PULSE_PERIOD = 300.0;
|
||||
sphereRadius = 0.25 + 0.0625 * Math.cos(time / PULSE_PERIOD);
|
||||
sphereRadiusSquared = sphereRadius * sphereRadius;
|
||||
|
||||
guide(visitation.info.minimum, visitation.info.size, 0);
|
||||
})
|
|
@ -441,9 +441,9 @@ void Application::paintGL() {
|
|||
glEnable(GL_LINE_SMOOTH);
|
||||
|
||||
if (OculusManager::isConnected()) {
|
||||
_myCamera.setUpShift (0.0f);
|
||||
_myCamera.setDistance (0.0f);
|
||||
_myCamera.setTightness (0.0f); // Camera is directly connected to head without smoothing
|
||||
_myCamera.setUpShift(0.0f);
|
||||
_myCamera.setDistance(0.0f);
|
||||
_myCamera.setTightness(0.0f); // Camera is directly connected to head without smoothing
|
||||
_myCamera.setTargetPosition(_myAvatar->getHead().calculateAverageEyePosition());
|
||||
_myCamera.setTargetRotation(_myAvatar->getHead().getOrientation());
|
||||
|
||||
|
@ -453,7 +453,7 @@ void Application::paintGL() {
|
|||
_myCamera.setTargetRotation(_myAvatar->getHead().getCameraOrientation());
|
||||
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||
_myCamera.setTightness (0.0f); // Camera is directly connected to head without smoothing
|
||||
_myCamera.setTightness(0.0f); // Camera is directly connected to head without smoothing
|
||||
_myCamera.setTargetPosition(_myAvatar->getUprightHeadPosition());
|
||||
_myCamera.setTargetRotation(_myAvatar->getHead().getCameraOrientation());
|
||||
|
||||
|
@ -1964,8 +1964,13 @@ void Application::updateMouseRay() {
|
|||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateMouseRay()");
|
||||
|
||||
_viewFrustum.computePickRay(_mouseX / (float)_glWidget->width(), _mouseY / (float)_glWidget->height(),
|
||||
_mouseRayOrigin, _mouseRayDirection);
|
||||
// if the mouse pointer isn't visible, act like it's at the center of the screen
|
||||
float x = 0.5f, y = 0.5f;
|
||||
if (!_mouseHidden) {
|
||||
x = _mouseX / (float)_glWidget->width();
|
||||
y = _mouseY / (float)_glWidget->height();
|
||||
}
|
||||
_viewFrustum.computePickRay(x, y, _mouseRayOrigin, _mouseRayDirection);
|
||||
|
||||
// adjust for mirroring
|
||||
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
|
@ -2002,18 +2007,20 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot) {
|
|||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateMyAvatarLookAtPosition()");
|
||||
|
||||
const float FAR_AWAY_STARE = TREE_SCALE;
|
||||
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
lookAtSpot = _myCamera.getPosition();
|
||||
|
||||
} else if (_mouseHidden) {
|
||||
// if the mouse cursor is hidden, just look straight ahead
|
||||
glm::vec3 rayOrigin, rayDirection;
|
||||
_viewFrustum.computePickRay(0.5f, 0.5f, rayOrigin, rayDirection);
|
||||
lookAtSpot = rayOrigin + rayDirection * FAR_AWAY_STARE;
|
||||
} else {
|
||||
// just look in direction of the mouse ray
|
||||
lookAtSpot = _mouseRayOrigin + _mouseRayDirection * FAR_AWAY_STARE;
|
||||
// look in direction of the mouse ray, but use distance from intersection, if any
|
||||
float distance = TREE_SCALE;
|
||||
if (_myAvatar->getLookAtTargetAvatar()) {
|
||||
distance = glm::distance(_mouseRayOrigin,
|
||||
static_cast<Avatar*>(_myAvatar->getLookAtTargetAvatar())->getHead().calculateAverageEyePosition());
|
||||
|
||||
} else if (_isHoverVoxel) {
|
||||
distance = glm::distance(_mouseRayOrigin, getMouseVoxelWorldCoordinates(_hoverVoxel));
|
||||
}
|
||||
lookAtSpot = _mouseRayOrigin + _mouseRayDirection * distance;
|
||||
}
|
||||
if (_faceshift.isActive()) {
|
||||
// deflect using Faceshift gaze data
|
||||
|
@ -2221,28 +2228,30 @@ void Application::updateMetavoxels(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::cameraMenuChanged() {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
|
||||
_myCamera.setMode(CAMERA_MODE_MIRROR);
|
||||
_myCamera.setModeShiftRate(100.0f);
|
||||
}
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) {
|
||||
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
|
||||
_myCamera.setModeShiftRate(1.0f);
|
||||
}
|
||||
} else {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) {
|
||||
_myCamera.setMode(CAMERA_MODE_THIRD_PERSON);
|
||||
_myCamera.setModeShiftRate(1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Application::updateCamera(float deltaTime) {
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateCamera()");
|
||||
|
||||
if (!OculusManager::isConnected() && !TV3DManager::isConnected()) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
|
||||
_myCamera.setMode(CAMERA_MODE_MIRROR);
|
||||
_myCamera.setModeShiftRate(100.0f);
|
||||
}
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) {
|
||||
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
|
||||
_myCamera.setModeShiftRate(1.0f);
|
||||
}
|
||||
} else {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) {
|
||||
_myCamera.setMode(CAMERA_MODE_THIRD_PERSON);
|
||||
_myCamera.setModeShiftRate(1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::OffAxisProjection)) {
|
||||
float xSign = _myCamera.getMode() == CAMERA_MODE_MIRROR ? 1.0f : -1.0f;
|
||||
if (_faceshift.isActive()) {
|
||||
|
@ -4098,6 +4107,10 @@ void Application::loadScript(const QString& fileNameString) {
|
|||
// hook our avatar object into this script engine
|
||||
scriptEngine->setAvatarData( static_cast<Avatar*>(_myAvatar), "MyAvatar");
|
||||
|
||||
CameraScriptableObject* cameraScriptable = new CameraScriptableObject(&_myCamera, &_viewFrustum);
|
||||
scriptEngine->registerGlobalObject("Camera", cameraScriptable);
|
||||
connect(scriptEngine, SIGNAL(finished(const QString&)), cameraScriptable, SLOT(deleteLater()));
|
||||
|
||||
QThread* workerThread = new QThread(this);
|
||||
|
||||
// when the worker thread is started, call our engine's run..
|
||||
|
|
|
@ -168,6 +168,7 @@ public:
|
|||
GeometryCache* getGeometryCache() { return &_geometryCache; }
|
||||
TextureCache* getTextureCache() { return &_textureCache; }
|
||||
GlowEffect* getGlowEffect() { return &_glowEffect; }
|
||||
ControllerScriptingInterface* getControllerScriptingInterface() { return &_controllerScriptingInterface; }
|
||||
|
||||
AvatarManager& getAvatarManager() { return _avatarManager; }
|
||||
Profile* getProfile() { return &_profile; }
|
||||
|
@ -238,6 +239,7 @@ private slots:
|
|||
|
||||
void setFullscreen(bool fullscreen);
|
||||
void setEnable3DTVMode(bool enable3DTVMode);
|
||||
void cameraMenuChanged();
|
||||
|
||||
void renderThrustAtVoxel(const glm::vec3& thrust);
|
||||
|
||||
|
|
|
@ -8,8 +8,11 @@
|
|||
|
||||
#include <SharedUtil.h>
|
||||
#include <VoxelConstants.h>
|
||||
#include <EventTypes.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Camera.h"
|
||||
#include "Menu.h"
|
||||
#include "Util.h"
|
||||
|
||||
const float CAMERA_MINIMUM_MODE_SHIFT_RATE = 0.5f;
|
||||
|
@ -18,6 +21,10 @@ const float CAMERA_FIRST_PERSON_MODE_UP_SHIFT = 0.0f;
|
|||
const float CAMERA_FIRST_PERSON_MODE_DISTANCE = 0.0f;
|
||||
const float CAMERA_FIRST_PERSON_MODE_TIGHTNESS = 100.0f;
|
||||
|
||||
const float CAMERA_INDEPENDENT_MODE_UP_SHIFT = 0.0f;
|
||||
const float CAMERA_INDEPENDENT_MODE_DISTANCE = 0.0f;
|
||||
const float CAMERA_INDEPENDENT_MODE_TIGHTNESS = 100.0f;
|
||||
|
||||
const float CAMERA_THIRD_PERSON_MODE_UP_SHIFT = -0.2f;
|
||||
const float CAMERA_THIRD_PERSON_MODE_DISTANCE = 1.5f;
|
||||
const float CAMERA_THIRD_PERSON_MODE_TIGHTNESS = 8.0f;
|
||||
|
@ -27,33 +34,32 @@ const float CAMERA_MIRROR_MODE_DISTANCE = 0.17f;
|
|||
const float CAMERA_MIRROR_MODE_TIGHTNESS = 100.0f;
|
||||
|
||||
|
||||
Camera::Camera() {
|
||||
|
||||
_needsToInitialize = true;
|
||||
_frustumNeedsReshape = true;
|
||||
|
||||
_modeShift = 1.0f;
|
||||
_modeShiftRate = 1.0f;
|
||||
_linearModeShift = 0.0f;
|
||||
_mode = CAMERA_MODE_THIRD_PERSON;
|
||||
_prevMode = CAMERA_MODE_THIRD_PERSON;
|
||||
_tightness = 10.0f; // default
|
||||
_fieldOfView = DEFAULT_FIELD_OF_VIEW_DEGREES;
|
||||
_aspectRatio = 16.f/9.f;
|
||||
_nearClip = 0.08f; // default
|
||||
_farClip = 50.0f * TREE_SCALE; // default
|
||||
_upShift = 0.0f;
|
||||
_distance = 0.0f;
|
||||
_previousUpShift = 0.0f;
|
||||
_previousDistance = 0.0f;
|
||||
_previousTightness = 0.0f;
|
||||
_newUpShift = 0.0f;
|
||||
_newDistance = 0.0f;
|
||||
_newTightness = 0.0f;
|
||||
_targetPosition = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
_position = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
_idealPosition = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
_scale = 1.0f;
|
||||
Camera::Camera() :
|
||||
_needsToInitialize(true),
|
||||
_mode(CAMERA_MODE_THIRD_PERSON),
|
||||
_prevMode(CAMERA_MODE_THIRD_PERSON),
|
||||
_frustumNeedsReshape(true),
|
||||
_position(0.0f, 0.0f, 0.0f),
|
||||
_idealPosition(0.0f, 0.0f, 0.0f),
|
||||
_targetPosition(0.0f, 0.0f, 0.0f),
|
||||
_fieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES),
|
||||
_aspectRatio(16.f/9.f),
|
||||
_nearClip(0.08f), // default
|
||||
_farClip(50.0f * TREE_SCALE), // default
|
||||
_upShift(0.0f),
|
||||
_distance(0.0f),
|
||||
_tightness(10.0f), // default
|
||||
_previousUpShift(0.0f),
|
||||
_previousDistance(0.0f),
|
||||
_previousTightness(0.0f),
|
||||
_newUpShift(0.0f),
|
||||
_newDistance(0.0f),
|
||||
_newTightness(0.0f),
|
||||
_modeShift(1.0f),
|
||||
_linearModeShift(0.0f),
|
||||
_modeShiftRate(1.0f),
|
||||
_scale(1.0f)
|
||||
{
|
||||
}
|
||||
|
||||
void Camera::update(float deltaTime) {
|
||||
|
@ -66,12 +72,9 @@ void Camera::update(float deltaTime) {
|
|||
|
||||
// use iterative forces to keep the camera at the desired position and angle
|
||||
void Camera::updateFollowMode(float deltaTime) {
|
||||
|
||||
if (_linearModeShift < 1.0f) {
|
||||
_linearModeShift += _modeShiftRate * deltaTime;
|
||||
|
||||
_modeShift = ONE_HALF - ONE_HALF * cosf(_linearModeShift * PIE );
|
||||
|
||||
_upShift = _previousUpShift * (1.0f - _modeShift) + _newUpShift * _modeShift;
|
||||
_distance = _previousDistance * (1.0f - _modeShift) + _newDistance * _modeShift;
|
||||
_tightness = _previousTightness * (1.0f - _modeShift) + _newTightness * _modeShift;
|
||||
|
@ -98,7 +101,6 @@ void Camera::updateFollowMode(float deltaTime) {
|
|||
_idealPosition = _targetPosition + _scale * (_rotation * glm::vec3(0.0f, _upShift, _distance));
|
||||
_position = _idealPosition;
|
||||
_needsToInitialize = false;
|
||||
|
||||
} else {
|
||||
// pull rotation towards ideal
|
||||
_rotation = safeMix(_rotation, _targetRotation, t);
|
||||
|
@ -137,16 +139,19 @@ void Camera::setMode(CameraMode m) {
|
|||
_newUpShift = CAMERA_THIRD_PERSON_MODE_UP_SHIFT;
|
||||
_newDistance = CAMERA_THIRD_PERSON_MODE_DISTANCE;
|
||||
_newTightness = CAMERA_THIRD_PERSON_MODE_TIGHTNESS;
|
||||
|
||||
} else if (_mode == CAMERA_MODE_FIRST_PERSON) {
|
||||
_newUpShift = CAMERA_FIRST_PERSON_MODE_UP_SHIFT;
|
||||
_newDistance = CAMERA_FIRST_PERSON_MODE_DISTANCE;
|
||||
_newTightness = CAMERA_FIRST_PERSON_MODE_TIGHTNESS;
|
||||
|
||||
} else if (_mode == CAMERA_MODE_MIRROR) {
|
||||
_newUpShift = CAMERA_MIRROR_MODE_UP_SHIFT;
|
||||
_newDistance = CAMERA_MIRROR_MODE_DISTANCE;
|
||||
_newTightness = CAMERA_MIRROR_MODE_TIGHTNESS;
|
||||
} else if (_mode == CAMERA_MODE_INDEPENDENT) {
|
||||
_newUpShift = CAMERA_INDEPENDENT_MODE_UP_SHIFT;
|
||||
_newDistance = CAMERA_INDEPENDENT_MODE_DISTANCE;
|
||||
_newTightness = CAMERA_INDEPENDENT_MODE_TIGHTNESS;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,22 +170,22 @@ void Camera::setAspectRatio(float a) {
|
|||
_frustumNeedsReshape = true;
|
||||
}
|
||||
|
||||
void Camera::setNearClip (float n) {
|
||||
void Camera::setNearClip(float n) {
|
||||
_nearClip = n;
|
||||
_frustumNeedsReshape = true;
|
||||
}
|
||||
|
||||
void Camera::setFarClip (float f) {
|
||||
void Camera::setFarClip(float f) {
|
||||
_farClip = f;
|
||||
_frustumNeedsReshape = true;
|
||||
}
|
||||
|
||||
void Camera::setEyeOffsetPosition (const glm::vec3& p) {
|
||||
void Camera::setEyeOffsetPosition(const glm::vec3& p) {
|
||||
_eyeOffsetPosition = p;
|
||||
_frustumNeedsReshape = true;
|
||||
}
|
||||
|
||||
void Camera::setEyeOffsetOrientation (const glm::quat& o) {
|
||||
void Camera::setEyeOffsetOrientation(const glm::quat& o) {
|
||||
_eyeOffsetOrientation = o;
|
||||
_frustumNeedsReshape = true;
|
||||
}
|
||||
|
@ -218,4 +223,65 @@ void Camera::setFrustumWasReshaped() {
|
|||
}
|
||||
|
||||
|
||||
CameraScriptableObject::CameraScriptableObject(Camera* camera, ViewFrustum* viewFrustum) :
|
||||
_camera(camera), _viewFrustum(viewFrustum)
|
||||
{
|
||||
}
|
||||
|
||||
PickRay CameraScriptableObject::computePickRay(float x, float y) {
|
||||
float screenWidth = Application::getInstance()->getGLWidget()->width();
|
||||
float screenHeight = Application::getInstance()->getGLWidget()->height();
|
||||
PickRay result;
|
||||
_viewFrustum->computePickRay(x / screenWidth, y / screenHeight, result.origin, result.direction);
|
||||
return result;
|
||||
}
|
||||
|
||||
QString CameraScriptableObject::getMode() const {
|
||||
QString mode("unknown");
|
||||
switch(_camera->getMode()) {
|
||||
case CAMERA_MODE_THIRD_PERSON:
|
||||
mode = "third person";
|
||||
break;
|
||||
case CAMERA_MODE_FIRST_PERSON:
|
||||
mode = "first person";
|
||||
break;
|
||||
case CAMERA_MODE_MIRROR:
|
||||
mode = "mirror";
|
||||
break;
|
||||
case CAMERA_MODE_INDEPENDENT:
|
||||
mode = "independent";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
void CameraScriptableObject::setMode(const QString& mode) {
|
||||
CameraMode currentMode = _camera->getMode();
|
||||
CameraMode targetMode = currentMode;
|
||||
if (mode == "third person") {
|
||||
targetMode = CAMERA_MODE_THIRD_PERSON;
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, false);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, false);
|
||||
} else if (mode == "first person") {
|
||||
targetMode = CAMERA_MODE_FIRST_PERSON;
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, false);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true);
|
||||
} else if (mode == "mirror") {
|
||||
targetMode = CAMERA_MODE_MIRROR;
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, true);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, false);
|
||||
} else if (mode == "independent") {
|
||||
targetMode = CAMERA_MODE_INDEPENDENT;
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, false);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, false);
|
||||
}
|
||||
if (currentMode != targetMode) {
|
||||
_camera->setMode(targetMode);
|
||||
_camera->setModeShiftRate(10.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -10,8 +10,10 @@
|
|||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <ViewFrustum.h>
|
||||
|
||||
const float DEFAULT_FIELD_OF_VIEW_DEGREES = 90.0f;
|
||||
const float DEFAULT_FIELD_OF_VIEW_DEGREES = 90.0f;
|
||||
|
||||
enum CameraMode
|
||||
{
|
||||
|
@ -19,11 +21,12 @@ enum CameraMode
|
|||
CAMERA_MODE_THIRD_PERSON,
|
||||
CAMERA_MODE_FIRST_PERSON,
|
||||
CAMERA_MODE_MIRROR,
|
||||
CAMERA_MODE_INDEPENDENT,
|
||||
NUM_CAMERA_MODES
|
||||
};
|
||||
|
||||
class Camera
|
||||
{
|
||||
class Camera {
|
||||
|
||||
public:
|
||||
Camera();
|
||||
|
||||
|
@ -31,73 +34,95 @@ public:
|
|||
|
||||
void update( float deltaTime );
|
||||
|
||||
void setUpShift ( float u ) { _upShift = u; }
|
||||
void setDistance ( float d ) { _distance = d; }
|
||||
void setTargetPosition( const glm::vec3& t ) { _targetPosition = t; }
|
||||
void setPosition ( const glm::vec3& p ) { _position = p; }
|
||||
void setTightness ( float t ) { _tightness = t; }
|
||||
void setTargetRotation( const glm::quat& rotation );
|
||||
void setUpShift(float u) { _upShift = u; }
|
||||
void setDistance(float d) { _distance = d; }
|
||||
void setPosition(const glm::vec3& p) { _position = p; }
|
||||
void setTargetPosition(const glm::vec3& t) { _targetPosition = t; }
|
||||
void setTightness(float t) { _tightness = t; }
|
||||
void setTargetRotation(const glm::quat& rotation);
|
||||
|
||||
void setMode ( CameraMode m );
|
||||
void setModeShiftRate ( float r );
|
||||
void setFieldOfView ( float f );
|
||||
void setAspectRatio ( float a );
|
||||
void setNearClip ( float n );
|
||||
void setFarClip ( float f );
|
||||
void setEyeOffsetPosition ( const glm::vec3& p );
|
||||
void setEyeOffsetOrientation( const glm::quat& o );
|
||||
void setScale ( const float s );
|
||||
void setMode(CameraMode m);
|
||||
void setModeShiftRate(float r);
|
||||
void setFieldOfView(float f);
|
||||
void setAspectRatio(float a);
|
||||
void setNearClip(float n);
|
||||
void setFarClip(float f);
|
||||
void setEyeOffsetPosition(const glm::vec3& p);
|
||||
void setEyeOffsetOrientation(const glm::quat& o);
|
||||
void setScale(const float s);
|
||||
|
||||
const glm::vec3& getTargetPosition () const { return _targetPosition; }
|
||||
const glm::vec3& getPosition () const { return _position; }
|
||||
const glm::quat& getTargetRotation () const { return _targetRotation; }
|
||||
const glm::quat& getRotation () const { return _rotation; }
|
||||
CameraMode getMode () const { return _mode; }
|
||||
float getFieldOfView () const { return _fieldOfView; }
|
||||
float getAspectRatio () const { return _aspectRatio; }
|
||||
float getNearClip () const { return _scale * _nearClip; }
|
||||
float getFarClip () const;
|
||||
const glm::vec3& getEyeOffsetPosition () const { return _eyeOffsetPosition; }
|
||||
const glm::quat& getEyeOffsetOrientation () const { return _eyeOffsetOrientation; }
|
||||
float getScale () const { return _scale; }
|
||||
const glm::vec3& getPosition() const { return _position; }
|
||||
const glm::quat& getRotation() const { return _rotation; }
|
||||
CameraMode getMode() const { return _mode; }
|
||||
const glm::vec3& getTargetPosition() const { return _targetPosition; }
|
||||
const glm::quat& getTargetRotation() const { return _targetRotation; }
|
||||
float getFieldOfView() const { return _fieldOfView; }
|
||||
float getAspectRatio() const { return _aspectRatio; }
|
||||
float getNearClip() const { return _scale * _nearClip; }
|
||||
float getFarClip() const;
|
||||
const glm::vec3& getEyeOffsetPosition() const { return _eyeOffsetPosition; }
|
||||
const glm::quat& getEyeOffsetOrientation() const { return _eyeOffsetOrientation; }
|
||||
float getScale() const { return _scale; }
|
||||
|
||||
CameraMode getInterpolatedMode() const;
|
||||
|
||||
bool getFrustumNeedsReshape() const; // call to find out if the view frustum needs to be reshaped
|
||||
void setFrustumWasReshaped(); // call this after reshaping the view frustum.
|
||||
|
||||
|
||||
private:
|
||||
|
||||
bool _needsToInitialize;
|
||||
CameraMode _mode;
|
||||
CameraMode _prevMode;
|
||||
bool _frustumNeedsReshape;
|
||||
glm::vec3 _position;
|
||||
glm::vec3 _idealPosition;
|
||||
glm::vec3 _targetPosition;
|
||||
float _fieldOfView;
|
||||
float _aspectRatio;
|
||||
float _nearClip;
|
||||
float _farClip;
|
||||
glm::vec3 _eyeOffsetPosition;
|
||||
glm::quat _eyeOffsetOrientation;
|
||||
glm::quat _rotation;
|
||||
glm::quat _targetRotation;
|
||||
float _upShift;
|
||||
float _distance;
|
||||
float _tightness;
|
||||
float _previousUpShift;
|
||||
float _previousDistance;
|
||||
float _previousTightness;
|
||||
float _newUpShift;
|
||||
float _newDistance;
|
||||
float _newTightness;
|
||||
float _modeShift;
|
||||
float _linearModeShift;
|
||||
float _modeShiftRate;
|
||||
float _scale;
|
||||
|
||||
void updateFollowMode( float deltaTime );
|
||||
bool _needsToInitialize;
|
||||
CameraMode _mode;
|
||||
CameraMode _prevMode;
|
||||
bool _frustumNeedsReshape;
|
||||
glm::vec3 _position;
|
||||
glm::vec3 _idealPosition;
|
||||
glm::vec3 _targetPosition;
|
||||
float _fieldOfView;
|
||||
float _aspectRatio;
|
||||
float _nearClip;
|
||||
float _farClip;
|
||||
glm::vec3 _eyeOffsetPosition;
|
||||
glm::quat _eyeOffsetOrientation;
|
||||
glm::quat _rotation;
|
||||
glm::quat _targetRotation;
|
||||
float _upShift;
|
||||
float _distance;
|
||||
float _tightness;
|
||||
float _previousUpShift;
|
||||
float _previousDistance;
|
||||
float _previousTightness;
|
||||
float _newUpShift;
|
||||
float _newDistance;
|
||||
float _newTightness;
|
||||
float _modeShift;
|
||||
float _linearModeShift;
|
||||
float _modeShiftRate;
|
||||
float _scale;
|
||||
|
||||
void updateFollowMode(float deltaTime);
|
||||
};
|
||||
|
||||
|
||||
class CameraScriptableObject : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
CameraScriptableObject(Camera* camera, ViewFrustum* viewFrustum);
|
||||
|
||||
public slots:
|
||||
QString getMode() const;
|
||||
void setMode(const QString& mode);
|
||||
void setPosition(const glm::vec3& value) { _camera->setTargetPosition(value);}
|
||||
|
||||
glm::vec3 getPosition() const { return _camera->getPosition(); }
|
||||
|
||||
void setOrientation(const glm::quat& value) { _camera->setTargetRotation(value); }
|
||||
glm::quat getOrientation() const { return _camera->getRotation(); }
|
||||
|
||||
PickRay computePickRay(float x, float y);
|
||||
|
||||
private:
|
||||
Camera* _camera;
|
||||
ViewFrustum* _viewFrustum;
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -219,3 +219,19 @@ void ControllerScriptingInterface::releaseKeyEvents(const KeyEvent& event) {
|
|||
}
|
||||
}
|
||||
|
||||
bool ControllerScriptingInterface::isJoystickCaptured(int joystickIndex) const {
|
||||
return _capturedJoysticks.contains(joystickIndex);
|
||||
}
|
||||
|
||||
void ControllerScriptingInterface::captureJoystick(int joystickIndex) {
|
||||
if (!isJoystickCaptured(joystickIndex)) {
|
||||
_capturedJoysticks.insert(joystickIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void ControllerScriptingInterface::releaseJoystick(int joystickIndex) {
|
||||
if (isJoystickCaptured(joystickIndex)) {
|
||||
_capturedJoysticks.remove(joystickIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ public:
|
|||
bool isMouseCaptured() const { return _mouseCaptured; }
|
||||
bool isTouchCaptured() const { return _touchCaptured; }
|
||||
bool isWheelCaptured() const { return _wheelCaptured; }
|
||||
|
||||
bool isJoystickCaptured(int joystickIndex) const;
|
||||
|
||||
public slots:
|
||||
virtual bool isPrimaryButtonPressed() const;
|
||||
|
@ -70,6 +70,9 @@ public slots:
|
|||
virtual void captureWheelEvents() { _wheelCaptured = true; }
|
||||
virtual void releaseWheelEvents() { _wheelCaptured = false; }
|
||||
|
||||
virtual void captureJoystick(int joystickIndex);
|
||||
virtual void releaseJoystick(int joystickIndex);
|
||||
|
||||
private:
|
||||
const PalmData* getPrimaryPalm() const;
|
||||
const PalmData* getPalm(int palmIndex) const;
|
||||
|
@ -80,6 +83,7 @@ private:
|
|||
bool _touchCaptured;
|
||||
bool _wheelCaptured;
|
||||
QMultiMap<int,KeyEvent> _capturedKeys;
|
||||
QSet<int> _capturedJoysticks;
|
||||
};
|
||||
|
||||
const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip
|
||||
|
|
|
@ -232,9 +232,12 @@ Menu::Menu() :
|
|||
false,
|
||||
appInstance,
|
||||
SLOT(setFullscreen(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true,
|
||||
appInstance,SLOT(cameraMenuChanged()));
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H,false,
|
||||
appInstance,SLOT(cameraMenuChanged()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Enable3DTVMode, 0,
|
||||
false,
|
||||
appInstance,
|
||||
|
@ -690,6 +693,9 @@ void Menu::removeAction(QMenu* menu, const QString& actionName) {
|
|||
menu->removeAction(_actionHash.value(actionName));
|
||||
}
|
||||
|
||||
void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) {
|
||||
return _actionHash.value(menuOption)->setChecked(isChecked);
|
||||
}
|
||||
|
||||
bool Menu::isOptionChecked(const QString& menuOption) {
|
||||
return _actionHash.value(menuOption)->isChecked();
|
||||
|
|
|
@ -48,6 +48,7 @@ public:
|
|||
~Menu();
|
||||
|
||||
bool isOptionChecked(const QString& menuOption);
|
||||
void setIsOptionChecked(const QString& menuOption, bool isChecked);
|
||||
void triggerOption(const QString& menuOption);
|
||||
QAction* getActionForOption(const QString& menuOption);
|
||||
bool isVoxelModeActionChecked();
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <SharedUtil.h>
|
||||
|
||||
#include <MetavoxelUtil.h>
|
||||
#include <ScriptCache.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "MetavoxelSystem.h"
|
||||
|
@ -37,6 +38,9 @@ void MetavoxelSystem::init() {
|
|||
_program.link();
|
||||
|
||||
_pointScaleLocation = _program.uniformLocation("pointScale");
|
||||
|
||||
// let the script cache know to use our common access manager
|
||||
ScriptCache::getInstance()->setNetworkAccessManager(Application::getInstance()->getNetworkAccessManager());
|
||||
}
|
||||
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
|
|
@ -805,23 +805,27 @@ void MyAvatar::updateThrust(float deltaTime) {
|
|||
const int VIEW_CONTROLLER = 1;
|
||||
for (size_t i = 0; i < getHand().getPalms().size(); ++i) {
|
||||
PalmData& palm = getHand().getPalms()[i];
|
||||
if (palm.isActive() && (palm.getSixenseID() == THRUST_CONTROLLER)) {
|
||||
if (palm.getJoystickY() != 0.f) {
|
||||
FingerData& finger = palm.getFingers()[0];
|
||||
if (finger.isActive()) {
|
||||
|
||||
// If the script hasn't captured this joystick, then let the default behavior work
|
||||
if (!Application::getInstance()->getControllerScriptingInterface()->isJoystickCaptured(palm.getSixenseID())) {
|
||||
if (palm.isActive() && (palm.getSixenseID() == THRUST_CONTROLLER)) {
|
||||
if (palm.getJoystickY() != 0.f) {
|
||||
FingerData& finger = palm.getFingers()[0];
|
||||
if (finger.isActive()) {
|
||||
}
|
||||
_thrust += front * _scale * THRUST_MAG_HAND_JETS * palm.getJoystickY() * _thrustMultiplier * deltaTime;
|
||||
}
|
||||
if (palm.getJoystickX() != 0.f) {
|
||||
_thrust += right * _scale * THRUST_MAG_HAND_JETS * palm.getJoystickX() * _thrustMultiplier * deltaTime;
|
||||
}
|
||||
} else if (palm.isActive() && (palm.getSixenseID() == VIEW_CONTROLLER)) {
|
||||
if (palm.getJoystickX() != 0.f) {
|
||||
_bodyYawDelta -= palm.getJoystickX() * JOYSTICK_YAW_MAG * deltaTime;
|
||||
}
|
||||
if (palm.getJoystickY() != 0.f) {
|
||||
getHand().setPitchUpdate(getHand().getPitchUpdate() +
|
||||
(palm.getJoystickY() * JOYSTICK_PITCH_MAG * deltaTime));
|
||||
}
|
||||
_thrust += front * _scale * THRUST_MAG_HAND_JETS * palm.getJoystickY() * _thrustMultiplier * deltaTime;
|
||||
}
|
||||
if (palm.getJoystickX() != 0.f) {
|
||||
_thrust += right * _scale * THRUST_MAG_HAND_JETS * palm.getJoystickX() * _thrustMultiplier * deltaTime;
|
||||
}
|
||||
} else if (palm.isActive() && (palm.getSixenseID() == VIEW_CONTROLLER)) {
|
||||
if (palm.getJoystickX() != 0.f) {
|
||||
_bodyYawDelta -= palm.getJoystickX() * JOYSTICK_YAW_MAG * deltaTime;
|
||||
}
|
||||
if (palm.getJoystickY() != 0.f) {
|
||||
getHand().setPitchUpdate(getHand().getPitchUpdate() +
|
||||
(palm.getJoystickY() * JOYSTICK_PITCH_MAG * deltaTime));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <QListWidget>
|
||||
#include <QMetaProperty>
|
||||
#include <QPushButton>
|
||||
#include <QScrollArea>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include <AttributeRegistry.h>
|
||||
|
@ -80,6 +81,9 @@ MetavoxelEditor::MetavoxelEditor() :
|
|||
QVBoxLayout* valueLayout = new QVBoxLayout();
|
||||
_value->setLayout(valueLayout);
|
||||
|
||||
valueLayout->addWidget(_valueArea = new QScrollArea());
|
||||
_valueArea->setWidgetResizable(true);
|
||||
|
||||
updateAttributes();
|
||||
|
||||
connect(Application::getInstance(), SIGNAL(renderingInWorldInterface()), SLOT(render()));
|
||||
|
@ -145,14 +149,14 @@ void MetavoxelEditor::updateValueEditor() {
|
|||
}
|
||||
_value->setVisible(true);
|
||||
|
||||
if (!_value->layout()->isEmpty()) {
|
||||
delete _value->layout()->takeAt(0);
|
||||
if (_valueArea->widget()) {
|
||||
delete _valueArea->widget();
|
||||
}
|
||||
|
||||
AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(selected);
|
||||
QWidget* editor = attribute->createEditor();
|
||||
if (editor) {
|
||||
_value->layout()->addWidget(editor);
|
||||
_valueArea->setWidget(editor);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -274,9 +278,13 @@ void MetavoxelEditor::render() {
|
|||
glutSolidCube(1.0);
|
||||
glDisable(GL_CULL_FACE);
|
||||
}
|
||||
glColor3f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS);
|
||||
glutWireCube(1.0);
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
} else {
|
||||
glColor3f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS);
|
||||
}
|
||||
|
||||
glLineWidth(1.0f);
|
||||
|
@ -292,7 +300,6 @@ void MetavoxelEditor::render() {
|
|||
|
||||
_gridProgram.bind();
|
||||
|
||||
glColor3f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS);
|
||||
Application::getInstance()->getGeometryCache()->renderGrid(GRID_DIVISIONS, GRID_DIVISIONS);
|
||||
|
||||
_gridProgram.release();
|
||||
|
@ -363,11 +370,8 @@ void MetavoxelEditor::applyValue(const glm::vec3& minimum, const glm::vec3& maxi
|
|||
}
|
||||
|
||||
QVariant MetavoxelEditor::getValue() const {
|
||||
if (_value->layout()->isEmpty()) {
|
||||
return QVariant();
|
||||
}
|
||||
QWidget* editor = _value->layout()->itemAt(0)->widget();
|
||||
return editor->metaObject()->userProperty().read(editor);
|
||||
QWidget* editor = _valueArea->widget();
|
||||
return editor ? editor->metaObject()->userProperty().read(editor) : QVariant();
|
||||
}
|
||||
|
||||
ProgramObject MetavoxelEditor::_gridProgram;
|
||||
|
|
|
@ -17,6 +17,7 @@ class QComboBox;
|
|||
class QDoubleSpinBox;
|
||||
class QGroupBox;
|
||||
class QListWidget;
|
||||
class QScrollArea;
|
||||
|
||||
/// Allows editing metavoxels.
|
||||
class MetavoxelEditor : public QDialog {
|
||||
|
@ -52,6 +53,7 @@ private:
|
|||
QDoubleSpinBox* _gridSpacing;
|
||||
QDoubleSpinBox* _gridPosition;
|
||||
QGroupBox* _value;
|
||||
QScrollArea* _valueArea;
|
||||
|
||||
enum State { HOVERING_STATE, DRAGGING_STATE, RAISING_STATE };
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cm
|
|||
|
||||
set(TARGET_NAME metavoxels)
|
||||
|
||||
find_package(Qt5Network REQUIRED)
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
|
||||
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
|
||||
|
@ -16,7 +17,7 @@ setup_hifi_library(${TARGET_NAME})
|
|||
include(${MACRO_DIR}/AutoMTC.cmake)
|
||||
auto_mtc(${TARGET_NAME} ${ROOT_DIR})
|
||||
|
||||
qt5_use_modules(${TARGET_NAME} Widgets Script)
|
||||
qt5_use_modules(${TARGET_NAME} Network Script Widgets)
|
||||
|
||||
include(${MACRO_DIR}/IncludeGLM.cmake)
|
||||
include_glm(${TARGET_NAME} ${ROOT_DIR})
|
||||
|
|
|
@ -6,15 +6,13 @@
|
|||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QColorDialog>
|
||||
#include <QPushButton>
|
||||
#include <QScriptEngine>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "AttributeRegistry.h"
|
||||
#include "MetavoxelData.h"
|
||||
|
||||
REGISTER_META_OBJECT(QRgbAttribute)
|
||||
REGISTER_META_OBJECT(SharedObjectAttribute)
|
||||
|
||||
AttributeRegistry* AttributeRegistry::getInstance() {
|
||||
static AttributeRegistry registry;
|
||||
|
@ -22,17 +20,29 @@ AttributeRegistry* AttributeRegistry::getInstance() {
|
|||
}
|
||||
|
||||
AttributeRegistry::AttributeRegistry() :
|
||||
_guideAttribute(registerAttribute(new PolymorphicAttribute("guide", PolymorphicDataPointer(new DefaultMetavoxelGuide())))),
|
||||
_guideAttribute(registerAttribute(new SharedObjectAttribute("guide", &MetavoxelGuide::staticMetaObject,
|
||||
SharedObjectPointer(new DefaultMetavoxelGuide())))),
|
||||
_colorAttribute(registerAttribute(new QRgbAttribute("color"))),
|
||||
_normalAttribute(registerAttribute(new QRgbAttribute("normal", qRgb(0, 127, 0)))) {
|
||||
}
|
||||
|
||||
static QScriptValue qDebugFunction(QScriptContext* context, QScriptEngine* engine) {
|
||||
QDebug debug = qDebug();
|
||||
|
||||
for (int i = 0; i < context->argumentCount(); i++) {
|
||||
debug << context->argument(i).toString();
|
||||
}
|
||||
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
void AttributeRegistry::configureScriptEngine(QScriptEngine* engine) {
|
||||
QScriptValue registry = engine->newObject();
|
||||
registry.setProperty("colorAttribute", engine->newQObject(_colorAttribute.data()));
|
||||
registry.setProperty("normalAttribute", engine->newQObject(_normalAttribute.data()));
|
||||
registry.setProperty("getAttribute", engine->newFunction(getAttribute, 1));
|
||||
engine->globalObject().setProperty("AttributeRegistry", registry);
|
||||
engine->globalObject().setProperty("qDebug", engine->newFunction(qDebugFunction, 1));
|
||||
}
|
||||
|
||||
AttributePointer AttributeRegistry::registerAttribute(AttributePointer attribute) {
|
||||
|
@ -84,6 +94,10 @@ OwnedAttributeValue::OwnedAttributeValue(const AttributeValue& other) :
|
|||
AttributeValue(other.getAttribute(), other.getAttribute() ? other.copy() : NULL) {
|
||||
}
|
||||
|
||||
OwnedAttributeValue::OwnedAttributeValue(const OwnedAttributeValue& other) :
|
||||
AttributeValue(other.getAttribute(), other.getAttribute() ? other.copy() : NULL) {
|
||||
}
|
||||
|
||||
OwnedAttributeValue::~OwnedAttributeValue() {
|
||||
if (_attribute) {
|
||||
_attribute->destroy(_value);
|
||||
|
@ -100,6 +114,16 @@ OwnedAttributeValue& OwnedAttributeValue::operator=(const AttributeValue& other)
|
|||
return *this;
|
||||
}
|
||||
|
||||
OwnedAttributeValue& OwnedAttributeValue::operator=(const OwnedAttributeValue& other) {
|
||||
if (_attribute) {
|
||||
_attribute->destroy(_value);
|
||||
}
|
||||
if ((_attribute = other.getAttribute())) {
|
||||
_value = other.copy();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Attribute::Attribute(const QString& name) {
|
||||
setObjectName(name);
|
||||
}
|
||||
|
@ -146,41 +170,48 @@ void* QRgbAttribute::createFromVariant(const QVariant& value) const {
|
|||
}
|
||||
|
||||
QWidget* QRgbAttribute::createEditor(QWidget* parent) const {
|
||||
QRgbEditor* editor = new QRgbEditor(parent);
|
||||
QColorEditor* editor = new QColorEditor(parent);
|
||||
editor->setColor(QColor::fromRgba(_defaultValue));
|
||||
return editor;
|
||||
}
|
||||
|
||||
QRgbEditor::QRgbEditor(QWidget* parent) : QWidget(parent) {
|
||||
setLayout(new QVBoxLayout());
|
||||
layout()->addWidget(_button = new QPushButton());
|
||||
connect(_button, SIGNAL(clicked()), SLOT(selectColor()));
|
||||
SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject,
|
||||
const SharedObjectPointer& defaultValue) :
|
||||
InlineAttribute<SharedObjectPointer>(name, defaultValue),
|
||||
_metaObject(metaObject) {
|
||||
|
||||
}
|
||||
|
||||
void QRgbEditor::setColor(const QColor& color) {
|
||||
QString name = (_color = color).name();
|
||||
_button->setStyleSheet(QString("background: %1; color: %2").arg(name, QColor::fromRgb(~color.rgb()).name()));
|
||||
_button->setText(name);
|
||||
}
|
||||
|
||||
void QRgbEditor::selectColor() {
|
||||
QColor color = QColorDialog::getColor(_color, this, QString(), QColorDialog::ShowAlphaChannel);
|
||||
if (color.isValid()) {
|
||||
setColor(color);
|
||||
void SharedObjectAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
|
||||
if (isLeaf) {
|
||||
in >> *((SharedObjectPointer*)&value);
|
||||
}
|
||||
}
|
||||
|
||||
PolymorphicData::~PolymorphicData() {
|
||||
void SharedObjectAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
|
||||
if (isLeaf) {
|
||||
out << decodeInline<SharedObjectPointer>(value);
|
||||
}
|
||||
}
|
||||
|
||||
template<> PolymorphicData* QExplicitlySharedDataPointer<PolymorphicData>::clone() {
|
||||
return d->clone();
|
||||
bool SharedObjectAttribute::merge(void*& parent, void* children[]) const {
|
||||
SharedObjectPointer firstChild = decodeInline<SharedObjectPointer>(children[0]);
|
||||
for (int i = 1; i < MERGE_COUNT; i++) {
|
||||
if (firstChild != decodeInline<SharedObjectPointer>(children[i])) {
|
||||
*(SharedObjectPointer*)&parent = _defaultValue;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*(SharedObjectPointer*)&parent = firstChild;
|
||||
return true;
|
||||
}
|
||||
|
||||
PolymorphicAttribute::PolymorphicAttribute(const QString& name, const PolymorphicDataPointer& defaultValue) :
|
||||
InlineAttribute<PolymorphicDataPointer>(name, defaultValue) {
|
||||
void* SharedObjectAttribute::createFromVariant(const QVariant& value) const {
|
||||
return create(encodeInline(value.value<SharedObjectPointer>()));
|
||||
}
|
||||
|
||||
bool PolymorphicAttribute::merge(void*& parent, void* children[]) const {
|
||||
return false;
|
||||
QWidget* SharedObjectAttribute::createEditor(QWidget* parent) const {
|
||||
SharedObjectEditor* editor = new SharedObjectEditor(_metaObject, parent);
|
||||
editor->setObject(_defaultValue);
|
||||
return editor;
|
||||
}
|
||||
|
|
|
@ -9,18 +9,15 @@
|
|||
#ifndef __interface__AttributeRegistry__
|
||||
#define __interface__AttributeRegistry__
|
||||
|
||||
#include <QColor>
|
||||
#include <QExplicitlySharedDataPointer>
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QSharedData>
|
||||
#include <QSharedPointer>
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
|
||||
#include "Bitstream.h"
|
||||
#include "SharedObject.h"
|
||||
|
||||
class QPushButton;
|
||||
class QScriptContext;
|
||||
class QScriptEngine;
|
||||
class QScriptValue;
|
||||
|
@ -127,11 +124,17 @@ public:
|
|||
/// Creates an owned attribute with a copy of the specified other value.
|
||||
OwnedAttributeValue(const AttributeValue& other);
|
||||
|
||||
/// Creates an owned attribute with a copy of the specified other value.
|
||||
OwnedAttributeValue(const OwnedAttributeValue& other);
|
||||
|
||||
/// Destroys the current value, if any.
|
||||
~OwnedAttributeValue();
|
||||
|
||||
/// Destroys the current value, if any, and copies the specified other value.
|
||||
OwnedAttributeValue& operator=(const AttributeValue& other);
|
||||
|
||||
/// Destroys the current value, if any, and copies the specified other value.
|
||||
OwnedAttributeValue& operator=(const OwnedAttributeValue& other);
|
||||
};
|
||||
|
||||
/// Represents a registered attribute.
|
||||
|
@ -247,104 +250,28 @@ public:
|
|||
virtual QWidget* createEditor(QWidget* parent = NULL) const;
|
||||
};
|
||||
|
||||
/// Editor for RGBA values.
|
||||
class QRgbEditor : public QWidget {
|
||||
/// An attribute that takes the form of QObjects of a given meta-type (a subclass of SharedObject).
|
||||
class SharedObjectAttribute : public InlineAttribute<SharedObjectPointer> {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QColor color MEMBER _color WRITE setColor USER true)
|
||||
|
||||
Q_PROPERTY(const QMetaObject* metaObject MEMBER _metaObject)
|
||||
|
||||
public:
|
||||
|
||||
QRgbEditor(QWidget* parent);
|
||||
Q_INVOKABLE SharedObjectAttribute(const QString& name = QString(), const QMetaObject* metaObject = NULL,
|
||||
const SharedObjectPointer& defaultValue = SharedObjectPointer());
|
||||
|
||||
public slots:
|
||||
|
||||
void setColor(const QColor& color);
|
||||
|
||||
private slots:
|
||||
|
||||
void selectColor();
|
||||
|
||||
private:
|
||||
|
||||
QPushButton* _button;
|
||||
QColor _color;
|
||||
};
|
||||
|
||||
/// An attribute class that stores pointers to its values.
|
||||
template<class T> class PointerAttribute : public Attribute {
|
||||
public:
|
||||
|
||||
PointerAttribute(const QString& name, T defaultValue = T()) : Attribute(name), _defaultValue(defaultValue) { }
|
||||
|
||||
virtual void* create(void* copy) const { new T(*static_cast<T*>(copy)); }
|
||||
virtual void destroy(void* value) const { delete static_cast<T*>(value); }
|
||||
|
||||
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
|
||||
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
|
||||
|
||||
virtual bool equal(void* first, void* second) const { return *static_cast<T*>(first) == *static_cast<T*>(second); }
|
||||
|
||||
virtual void* getDefaultValue() const { return const_cast<void*>((void*)&_defaultValue); }
|
||||
virtual bool merge(void*& parent, void* children[]) const;
|
||||
|
||||
virtual void* createFromVariant(const QVariant& value) const;
|
||||
|
||||
virtual QWidget* createEditor(QWidget* parent = NULL) const;
|
||||
|
||||
private:
|
||||
|
||||
T _defaultValue;
|
||||
};
|
||||
|
||||
template<class T> inline void PointerAttribute<T>::read(Bitstream& in, void*& value, bool isLeaf) const {
|
||||
if (isLeaf) {
|
||||
in.read(value, sizeof(T) * 8);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T> inline void PointerAttribute<T>::write(Bitstream& out, void* value, bool isLeaf) const {
|
||||
if (isLeaf) {
|
||||
out.write(value, sizeof(T) * 8);
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides merging using the =, ==, += and /= operators.
|
||||
template<class T> class SimplePointerAttribute : public PointerAttribute<T> {
|
||||
public:
|
||||
|
||||
SimplePointerAttribute(const QString& name, T defaultValue = T()) : PointerAttribute<T>(name, defaultValue) { }
|
||||
|
||||
virtual bool merge(void*& parent, void* children[]) const;
|
||||
};
|
||||
|
||||
template<class T> inline bool SimplePointerAttribute<T>::merge(void*& parent, void* children[]) const {
|
||||
T& merged = *static_cast<T*>(parent);
|
||||
merged = *static_cast<T*>(children[0]);
|
||||
bool allChildrenEqual = true;
|
||||
for (int i = 1; i < Attribute::MERGE_COUNT; i++) {
|
||||
merged += *static_cast<T*>(children[i]);
|
||||
allChildrenEqual &= (*static_cast<T*>(children[0]) == *static_cast<T*>(children[i]));
|
||||
}
|
||||
merged /= Attribute::MERGE_COUNT;
|
||||
return allChildrenEqual;
|
||||
}
|
||||
|
||||
/// Base class for polymorphic attribute data.
|
||||
class PolymorphicData : public QSharedData {
|
||||
public:
|
||||
|
||||
virtual ~PolymorphicData();
|
||||
|
||||
/// Creates a new clone of this object.
|
||||
virtual PolymorphicData* clone() const = 0;
|
||||
};
|
||||
|
||||
template<> PolymorphicData* QExplicitlySharedDataPointer<PolymorphicData>::clone();
|
||||
|
||||
typedef QExplicitlySharedDataPointer<PolymorphicData> PolymorphicDataPointer;
|
||||
|
||||
/// Provides polymorphic streaming and averaging.
|
||||
class PolymorphicAttribute : public InlineAttribute<PolymorphicDataPointer> {
|
||||
public:
|
||||
|
||||
PolymorphicAttribute(const QString& name, const PolymorphicDataPointer& defaultValue = PolymorphicDataPointer());
|
||||
|
||||
virtual bool merge(void*& parent, void* children[]) const;
|
||||
const QMetaObject* _metaObject;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__AttributeRegistry__) */
|
||||
|
|
|
@ -10,16 +10,31 @@
|
|||
|
||||
#include <QDataStream>
|
||||
#include <QMetaProperty>
|
||||
#include <QMetaType>
|
||||
#include <QUrl>
|
||||
#include <QtDebug>
|
||||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "AttributeRegistry.h"
|
||||
#include "Bitstream.h"
|
||||
#include "ScriptCache.h"
|
||||
|
||||
REGISTER_SIMPLE_TYPE_STREAMER(QByteArray)
|
||||
REGISTER_SIMPLE_TYPE_STREAMER(QString)
|
||||
REGISTER_SIMPLE_TYPE_STREAMER(QVariantList)
|
||||
REGISTER_SIMPLE_TYPE_STREAMER(bool)
|
||||
REGISTER_SIMPLE_TYPE_STREAMER(int)
|
||||
REGISTER_SIMPLE_TYPE_STREAMER(float)
|
||||
REGISTER_SIMPLE_TYPE_STREAMER(QByteArray)
|
||||
REGISTER_SIMPLE_TYPE_STREAMER(QColor)
|
||||
REGISTER_SIMPLE_TYPE_STREAMER(QString)
|
||||
REGISTER_SIMPLE_TYPE_STREAMER(QUrl)
|
||||
REGISTER_SIMPLE_TYPE_STREAMER(QVariantList)
|
||||
REGISTER_SIMPLE_TYPE_STREAMER(QVariantHash)
|
||||
|
||||
// some types don't quite work with our macro
|
||||
static int vec3Streamer = Bitstream::registerTypeStreamer(qMetaTypeId<glm::vec3>(), new SimpleTypeStreamer<glm::vec3>());
|
||||
static int metaObjectStreamer = Bitstream::registerTypeStreamer(qMetaTypeId<const QMetaObject*>(),
|
||||
new SimpleTypeStreamer<const QMetaObject*>());
|
||||
|
||||
IDStreamer::IDStreamer(Bitstream& stream) :
|
||||
_stream(stream),
|
||||
|
@ -52,6 +67,11 @@ IDStreamer& IDStreamer::operator>>(int& value) {
|
|||
|
||||
int Bitstream::registerMetaObject(const char* className, const QMetaObject* metaObject) {
|
||||
getMetaObjects().insert(className, metaObject);
|
||||
|
||||
// register it as a subclass of all of its superclasses
|
||||
for (const QMetaObject* superClass = metaObject->superClass(); superClass != NULL; superClass = superClass->superClass()) {
|
||||
getMetaObjectSubClasses().insert(superClass, metaObject);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -61,16 +81,22 @@ int Bitstream::registerTypeStreamer(int type, TypeStreamer* streamer) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
Bitstream::Bitstream(QDataStream& underlying) :
|
||||
QList<const QMetaObject*> Bitstream::getMetaObjectSubClasses(const QMetaObject* metaObject) {
|
||||
return getMetaObjectSubClasses().values(metaObject);
|
||||
}
|
||||
|
||||
Bitstream::Bitstream(QDataStream& underlying, QObject* parent) :
|
||||
QObject(parent),
|
||||
_underlying(underlying),
|
||||
_byte(0),
|
||||
_position(0),
|
||||
_metaObjectStreamer(*this),
|
||||
_typeStreamerStreamer(*this),
|
||||
_attributeStreamer(*this) {
|
||||
_attributeStreamer(*this),
|
||||
_scriptStringStreamer(*this),
|
||||
_sharedObjectStreamer(*this) {
|
||||
}
|
||||
|
||||
const int BITS_IN_BYTE = 8;
|
||||
const int LAST_BIT_POSITION = BITS_IN_BYTE - 1;
|
||||
|
||||
Bitstream& Bitstream::write(const void* data, int bits, int offset) {
|
||||
|
@ -124,7 +150,9 @@ void Bitstream::reset() {
|
|||
Bitstream::WriteMappings Bitstream::getAndResetWriteMappings() {
|
||||
WriteMappings mappings = { _metaObjectStreamer.getAndResetTransientOffsets(),
|
||||
_typeStreamerStreamer.getAndResetTransientOffsets(),
|
||||
_attributeStreamer.getAndResetTransientOffsets() };
|
||||
_attributeStreamer.getAndResetTransientOffsets(),
|
||||
_scriptStringStreamer.getAndResetTransientOffsets(),
|
||||
_sharedObjectStreamer.getAndResetTransientOffsets() };
|
||||
return mappings;
|
||||
}
|
||||
|
||||
|
@ -132,12 +160,24 @@ void Bitstream::persistWriteMappings(const WriteMappings& mappings) {
|
|||
_metaObjectStreamer.persistTransientOffsets(mappings.metaObjectOffsets);
|
||||
_typeStreamerStreamer.persistTransientOffsets(mappings.typeStreamerOffsets);
|
||||
_attributeStreamer.persistTransientOffsets(mappings.attributeOffsets);
|
||||
_scriptStringStreamer.persistTransientOffsets(mappings.scriptStringOffsets);
|
||||
_sharedObjectStreamer.persistTransientOffsets(mappings.sharedObjectOffsets);
|
||||
|
||||
// find out when shared objects' reference counts drop to one in order to clear their mappings
|
||||
for (QHash<SharedObjectPointer, int>::const_iterator it = mappings.sharedObjectOffsets.constBegin();
|
||||
it != mappings.sharedObjectOffsets.constEnd(); it++) {
|
||||
if (it.key()) {
|
||||
connect(it.key().data(), SIGNAL(referenceCountDroppedToOne()), SLOT(clearSharedObject()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bitstream::ReadMappings Bitstream::getAndResetReadMappings() {
|
||||
ReadMappings mappings = { _metaObjectStreamer.getAndResetTransientValues(),
|
||||
_typeStreamerStreamer.getAndResetTransientValues(),
|
||||
_attributeStreamer.getAndResetTransientValues() };
|
||||
_attributeStreamer.getAndResetTransientValues(),
|
||||
_scriptStringStreamer.getAndResetTransientValues(),
|
||||
_sharedObjectStreamer.getAndResetTransientValues() };
|
||||
return mappings;
|
||||
}
|
||||
|
||||
|
@ -145,6 +185,8 @@ void Bitstream::persistReadMappings(const ReadMappings& mappings) {
|
|||
_metaObjectStreamer.persistTransientValues(mappings.metaObjectValues);
|
||||
_typeStreamerStreamer.persistTransientValues(mappings.typeStreamerValues);
|
||||
_attributeStreamer.persistTransientValues(mappings.attributeValues);
|
||||
_scriptStringStreamer.persistTransientValues(mappings.scriptStringValues);
|
||||
_sharedObjectStreamer.persistTransientValues(mappings.sharedObjectValues);
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(bool value) {
|
||||
|
@ -205,6 +247,17 @@ Bitstream& Bitstream::operator>>(QByteArray& string) {
|
|||
return read(string.data(), size * BITS_IN_BYTE);
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(const QColor& color) {
|
||||
return *this << (int)color.rgba();
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator>>(QColor& color) {
|
||||
int rgba;
|
||||
*this >> rgba;
|
||||
color.setRgba(rgba);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(const QString& string) {
|
||||
*this << string.size();
|
||||
return write(string.constData(), string.size() * sizeof(QChar) * BITS_IN_BYTE);
|
||||
|
@ -217,6 +270,17 @@ Bitstream& Bitstream::operator>>(QString& string) {
|
|||
return read(string.data(), size * sizeof(QChar) * BITS_IN_BYTE);
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(const QUrl& url) {
|
||||
return *this << url.toString();
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator>>(QUrl& url) {
|
||||
QString string;
|
||||
*this >> string;
|
||||
url = string;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(const QVariant& value) {
|
||||
const TypeStreamer* streamer = getTypeStreamers().value(value.userType());
|
||||
if (streamer) {
|
||||
|
@ -302,11 +366,61 @@ Bitstream& Bitstream::operator>>(QObject*& object) {
|
|||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(const QMetaObject* metaObject) {
|
||||
return *this << (metaObject ? QByteArray::fromRawData(
|
||||
metaObject->className(), strlen(metaObject->className())) : QByteArray());
|
||||
_metaObjectStreamer << metaObject;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator>>(const QMetaObject*& metaObject) {
|
||||
_metaObjectStreamer >> metaObject;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(const TypeStreamer* streamer) {
|
||||
_typeStreamerStreamer << streamer;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator>>(const TypeStreamer*& streamer) {
|
||||
_typeStreamerStreamer >> streamer;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(const AttributePointer& attribute) {
|
||||
_attributeStreamer << attribute;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator>>(AttributePointer& attribute) {
|
||||
_attributeStreamer >> attribute;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(const QScriptString& string) {
|
||||
_scriptStringStreamer << string;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator>>(QScriptString& string) {
|
||||
_scriptStringStreamer >> string;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(const SharedObjectPointer& object) {
|
||||
_sharedObjectStreamer << object;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator>>(SharedObjectPointer& object) {
|
||||
_sharedObjectStreamer >> object;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<(const QMetaObject* metaObject) {
|
||||
return *this << (metaObject ? QByteArray::fromRawData(metaObject->className(),
|
||||
strlen(metaObject->className())) : QByteArray());
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator>(const QMetaObject*& metaObject) {
|
||||
QByteArray className;
|
||||
*this >> className;
|
||||
if (className.isEmpty()) {
|
||||
|
@ -320,12 +434,12 @@ Bitstream& Bitstream::operator>>(const QMetaObject*& metaObject) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(const TypeStreamer* streamer) {
|
||||
Bitstream& Bitstream::operator<(const TypeStreamer* streamer) {
|
||||
const char* typeName = QMetaType::typeName(streamer->getType());
|
||||
return *this << QByteArray::fromRawData(typeName, strlen(typeName));
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator>>(const TypeStreamer*& streamer) {
|
||||
Bitstream& Bitstream::operator>(const TypeStreamer*& streamer) {
|
||||
QByteArray typeName;
|
||||
*this >> typeName;
|
||||
streamer = getTypeStreamers().value(QMetaType::type(typeName.constData()));
|
||||
|
@ -335,22 +449,55 @@ Bitstream& Bitstream::operator>>(const TypeStreamer*& streamer) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(const AttributePointer& attribute) {
|
||||
Bitstream& Bitstream::operator<(const AttributePointer& attribute) {
|
||||
return *this << (QObject*)attribute.data();
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator>>(AttributePointer& attribute) {
|
||||
Bitstream& Bitstream::operator>(AttributePointer& attribute) {
|
||||
QObject* object;
|
||||
*this >> object;
|
||||
attribute = AttributeRegistry::getInstance()->registerAttribute(static_cast<Attribute*>(object));
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<(const QScriptString& string) {
|
||||
return *this << string.toString();
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator>(QScriptString& string) {
|
||||
QString rawString;
|
||||
*this >> rawString;
|
||||
string = ScriptCache::getInstance()->getEngine()->toStringHandle(rawString);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<(const SharedObjectPointer& object) {
|
||||
return *this << object.data();
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator>(SharedObjectPointer& object) {
|
||||
QObject* rawObject;
|
||||
*this >> rawObject;
|
||||
object = static_cast<SharedObject*>(rawObject);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Bitstream::clearSharedObject() {
|
||||
SharedObjectPointer object(static_cast<SharedObject*>(sender()));
|
||||
object->disconnect(this);
|
||||
emit sharedObjectCleared(_sharedObjectStreamer.takePersistentID(object));
|
||||
}
|
||||
|
||||
QHash<QByteArray, const QMetaObject*>& Bitstream::getMetaObjects() {
|
||||
static QHash<QByteArray, const QMetaObject*> metaObjects;
|
||||
return metaObjects;
|
||||
}
|
||||
|
||||
QMultiHash<const QMetaObject*, const QMetaObject*>& Bitstream::getMetaObjectSubClasses() {
|
||||
static QMultiHash<const QMetaObject*, const QMetaObject*> metaObjectSubClasses;
|
||||
return metaObjectSubClasses;
|
||||
}
|
||||
|
||||
QHash<int, const TypeStreamer*>& Bitstream::getTypeStreamers() {
|
||||
static QHash<int, const TypeStreamer*> typeStreamers;
|
||||
return typeStreamers;
|
||||
|
|
|
@ -11,16 +11,19 @@
|
|||
|
||||
#include <QHash>
|
||||
#include <QMetaType>
|
||||
#include <QScriptString>
|
||||
#include <QSharedPointer>
|
||||
#include <QVariant>
|
||||
#include <QtDebug>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "SharedObject.h"
|
||||
|
||||
class QByteArray;
|
||||
class QColor;
|
||||
class QDataStream;
|
||||
struct QMetaObject;
|
||||
class QObject;
|
||||
class QUrl;
|
||||
|
||||
class Attribute;
|
||||
class AttributeValue;
|
||||
|
@ -65,6 +68,10 @@ public:
|
|||
|
||||
void persistTransientValues(const QHash<int, T>& transientValues);
|
||||
|
||||
int takePersistentID(T value) { return _persistentIDs.take(value); }
|
||||
|
||||
void removePersistentValue(int id) { _persistentValues.remove(id); }
|
||||
|
||||
RepeatedValueStreamer& operator<<(T value);
|
||||
RepeatedValueStreamer& operator>>(T& value);
|
||||
|
||||
|
@ -126,7 +133,7 @@ template<class T> inline RepeatedValueStreamer<T>& RepeatedValueStreamer<T>::ope
|
|||
int& offset = _transientOffsets[value];
|
||||
if (offset == 0) {
|
||||
_idStreamer << (_lastPersistentID + (offset = ++_lastTransientOffset));
|
||||
_stream << value;
|
||||
_stream < value;
|
||||
|
||||
} else {
|
||||
_idStreamer << (_lastPersistentID + offset);
|
||||
|
@ -147,7 +154,7 @@ template<class T> inline RepeatedValueStreamer<T>& RepeatedValueStreamer<T>::ope
|
|||
int offset = id - _lastPersistentID;
|
||||
typename QHash<int, T>::iterator it = _transientValues.find(offset);
|
||||
if (it == _transientValues.end()) {
|
||||
_stream >> value;
|
||||
_stream > value;
|
||||
_transientValues.insert(offset, value);
|
||||
|
||||
} else {
|
||||
|
@ -158,7 +165,9 @@ template<class T> inline RepeatedValueStreamer<T>& RepeatedValueStreamer<T>::ope
|
|||
}
|
||||
|
||||
/// A stream for bit-aligned data.
|
||||
class Bitstream {
|
||||
class Bitstream : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
class WriteMappings {
|
||||
|
@ -166,6 +175,8 @@ public:
|
|||
QHash<const QMetaObject*, int> metaObjectOffsets;
|
||||
QHash<const TypeStreamer*, int> typeStreamerOffsets;
|
||||
QHash<AttributePointer, int> attributeOffsets;
|
||||
QHash<QScriptString, int> scriptStringOffsets;
|
||||
QHash<SharedObjectPointer, int> sharedObjectOffsets;
|
||||
};
|
||||
|
||||
class ReadMappings {
|
||||
|
@ -173,6 +184,8 @@ public:
|
|||
QHash<int, const QMetaObject*> metaObjectValues;
|
||||
QHash<int, const TypeStreamer*> typeStreamerValues;
|
||||
QHash<int, AttributePointer> attributeValues;
|
||||
QHash<int, QScriptString> scriptStringValues;
|
||||
QHash<int, SharedObjectPointer> sharedObjectValues;
|
||||
};
|
||||
|
||||
/// Registers a metaobject under its name so that instances of it can be streamed.
|
||||
|
@ -183,8 +196,11 @@ public:
|
|||
/// \return zero; the function only returns a value so that it can be used in static initialization
|
||||
static int registerTypeStreamer(int type, TypeStreamer* streamer);
|
||||
|
||||
/// Returns the list of registered subclasses for the supplied meta-object.
|
||||
static QList<const QMetaObject*> getMetaObjectSubClasses(const QMetaObject* metaObject);
|
||||
|
||||
/// Creates a new bitstream. Note: the stream may be used for reading or writing, but not both.
|
||||
Bitstream(QDataStream& underlying);
|
||||
Bitstream(QDataStream& underlying, QObject* parent = NULL);
|
||||
|
||||
/// Writes a set of bits to the underlying stream.
|
||||
/// \param bits the number of bits to write
|
||||
|
@ -202,9 +218,6 @@ public:
|
|||
/// Resets to the initial state.
|
||||
void reset();
|
||||
|
||||
/// Returns a reference to the attribute streamer.
|
||||
RepeatedValueStreamer<AttributePointer>& getAttributeStreamer() { return _attributeStreamer; }
|
||||
|
||||
/// Returns the set of transient mappings gathered during writing and resets them.
|
||||
WriteMappings getAndResetWriteMappings();
|
||||
|
||||
|
@ -217,6 +230,9 @@ public:
|
|||
/// Persists a set of read mappings recorded earlier.
|
||||
void persistReadMappings(const ReadMappings& mappings);
|
||||
|
||||
/// Removes a shared object from the read mappings.
|
||||
void clearSharedObject(int id) { _sharedObjectStreamer.removePersistentValue(id); }
|
||||
|
||||
Bitstream& operator<<(bool value);
|
||||
Bitstream& operator>>(bool& value);
|
||||
|
||||
|
@ -232,9 +248,15 @@ public:
|
|||
Bitstream& operator<<(const QByteArray& string);
|
||||
Bitstream& operator>>(QByteArray& string);
|
||||
|
||||
Bitstream& operator<<(const QColor& color);
|
||||
Bitstream& operator>>(QColor& color);
|
||||
|
||||
Bitstream& operator<<(const QString& string);
|
||||
Bitstream& operator>>(QString& string);
|
||||
|
||||
Bitstream& operator<<(const QUrl& url);
|
||||
Bitstream& operator>>(QUrl& url);
|
||||
|
||||
Bitstream& operator<<(const QVariant& value);
|
||||
Bitstream& operator>>(QVariant& value);
|
||||
|
||||
|
@ -244,6 +266,9 @@ public:
|
|||
template<class T> Bitstream& operator<<(const QList<T>& list);
|
||||
template<class T> Bitstream& operator>>(QList<T>& list);
|
||||
|
||||
template<class K, class V> Bitstream& operator<<(const QHash<K, V>& hash);
|
||||
template<class K, class V> Bitstream& operator>>(QHash<K, V>& hash);
|
||||
|
||||
Bitstream& operator<<(const QObject* object);
|
||||
Bitstream& operator>>(QObject*& object);
|
||||
|
||||
|
@ -256,6 +281,35 @@ public:
|
|||
Bitstream& operator<<(const AttributePointer& attribute);
|
||||
Bitstream& operator>>(AttributePointer& attribute);
|
||||
|
||||
Bitstream& operator<<(const QScriptString& string);
|
||||
Bitstream& operator>>(QScriptString& string);
|
||||
|
||||
Bitstream& operator<<(const SharedObjectPointer& object);
|
||||
Bitstream& operator>>(SharedObjectPointer& object);
|
||||
|
||||
Bitstream& operator<(const QMetaObject* metaObject);
|
||||
Bitstream& operator>(const QMetaObject*& metaObject);
|
||||
|
||||
Bitstream& operator<(const TypeStreamer* streamer);
|
||||
Bitstream& operator>(const TypeStreamer*& streamer);
|
||||
|
||||
Bitstream& operator<(const AttributePointer& attribute);
|
||||
Bitstream& operator>(AttributePointer& attribute);
|
||||
|
||||
Bitstream& operator<(const QScriptString& string);
|
||||
Bitstream& operator>(QScriptString& string);
|
||||
|
||||
Bitstream& operator<(const SharedObjectPointer& object);
|
||||
Bitstream& operator>(SharedObjectPointer& object);
|
||||
|
||||
signals:
|
||||
|
||||
void sharedObjectCleared(int id);
|
||||
|
||||
private slots:
|
||||
|
||||
void clearSharedObject();
|
||||
|
||||
private:
|
||||
|
||||
QDataStream& _underlying;
|
||||
|
@ -265,8 +319,11 @@ private:
|
|||
RepeatedValueStreamer<const QMetaObject*> _metaObjectStreamer;
|
||||
RepeatedValueStreamer<const TypeStreamer*> _typeStreamerStreamer;
|
||||
RepeatedValueStreamer<AttributePointer> _attributeStreamer;
|
||||
RepeatedValueStreamer<QScriptString> _scriptStringStreamer;
|
||||
RepeatedValueStreamer<SharedObjectPointer> _sharedObjectStreamer;
|
||||
|
||||
static QHash<QByteArray, const QMetaObject*>& getMetaObjects();
|
||||
static QMultiHash<const QMetaObject*, const QMetaObject*>& getMetaObjectSubClasses();
|
||||
static QHash<int, const TypeStreamer*>& getTypeStreamers();
|
||||
};
|
||||
|
||||
|
@ -291,6 +348,32 @@ template<class T> inline Bitstream& Bitstream::operator>>(QList<T>& list) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
template<class K, class V> inline Bitstream& Bitstream::operator<<(const QHash<K, V>& hash) {
|
||||
*this << hash.size();
|
||||
for (typename QHash<K, V>::const_iterator it = hash.constBegin(); it != hash.constEnd(); it++) {
|
||||
*this << it.key();
|
||||
*this << it.value();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class K, class V> inline Bitstream& Bitstream::operator>>(QHash<K, V>& hash) {
|
||||
int size;
|
||||
*this >> size;
|
||||
hash.clear();
|
||||
hash.reserve(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
K key;
|
||||
V value;
|
||||
*this >> key;
|
||||
*this >> value;
|
||||
hash.insertMulti(key, value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(const QMetaObject*)
|
||||
|
||||
/// Macro for registering streamable meta-objects.
|
||||
#define REGISTER_META_OBJECT(x) static int x##Registration = Bitstream::registerMetaObject(#x, &x::staticMetaObject);
|
||||
|
||||
|
|
|
@ -10,9 +10,15 @@
|
|||
|
||||
#include <QtDebug>
|
||||
|
||||
#include "DatagramSequencer.h"
|
||||
#include <SharedUtil.h>
|
||||
|
||||
const int MAX_DATAGRAM_SIZE = 1500;
|
||||
#include "DatagramSequencer.h"
|
||||
#include "MetavoxelMessages.h"
|
||||
|
||||
// in sequencer parlance, a "packet" may consist of multiple datagrams. clarify when we refer to actual datagrams
|
||||
const int MAX_DATAGRAM_SIZE = MAX_PACKET_SIZE;
|
||||
|
||||
const int DEFAULT_MAX_PACKET_SIZE = 3000;
|
||||
|
||||
DatagramSequencer::DatagramSequencer(const QByteArray& datagramHeader) :
|
||||
_outgoingPacketStream(&_outgoingPacketData, QIODevice::WriteOnly),
|
||||
|
@ -26,13 +32,16 @@ DatagramSequencer::DatagramSequencer(const QByteArray& datagramHeader) :
|
|||
_incomingPacketNumber(0),
|
||||
_incomingPacketStream(&_incomingPacketData, QIODevice::ReadOnly),
|
||||
_inputStream(_incomingPacketStream),
|
||||
_receivedHighPriorityMessages(0) {
|
||||
_receivedHighPriorityMessages(0),
|
||||
_maxPacketSize(DEFAULT_MAX_PACKET_SIZE) {
|
||||
|
||||
_outgoingPacketStream.setByteOrder(QDataStream::LittleEndian);
|
||||
_incomingDatagramStream.setByteOrder(QDataStream::LittleEndian);
|
||||
_incomingPacketStream.setByteOrder(QDataStream::LittleEndian);
|
||||
_outgoingDatagramStream.setByteOrder(QDataStream::LittleEndian);
|
||||
|
||||
connect(&_outputStream, SIGNAL(sharedObjectCleared(int)), SLOT(sendClearSharedObjectMessage(int)));
|
||||
|
||||
memcpy(_outgoingDatagram.data(), datagramHeader.constData(), _datagramHeaderSize);
|
||||
}
|
||||
|
||||
|
@ -41,6 +50,22 @@ void DatagramSequencer::sendHighPriorityMessage(const QVariant& data) {
|
|||
_highPriorityMessages.append(message);
|
||||
}
|
||||
|
||||
ReliableChannel* DatagramSequencer::getReliableOutputChannel(int index) {
|
||||
ReliableChannel*& channel = _reliableOutputChannels[index];
|
||||
if (!channel) {
|
||||
channel = new ReliableChannel(this, index, true);
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
ReliableChannel* DatagramSequencer::getReliableInputChannel(int index) {
|
||||
ReliableChannel*& channel = _reliableInputChannels[index];
|
||||
if (!channel) {
|
||||
channel = new ReliableChannel(this, index, false);
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
Bitstream& DatagramSequencer::startPacket() {
|
||||
// start with the list of acknowledgements
|
||||
_outgoingPacketStream << (quint32)_receiveRecords.size();
|
||||
|
@ -60,7 +85,18 @@ Bitstream& DatagramSequencer::startPacket() {
|
|||
|
||||
void DatagramSequencer::endPacket() {
|
||||
_outputStream.flush();
|
||||
sendPacket(QByteArray::fromRawData(_outgoingPacketData.constData(), _outgoingPacketStream.device()->pos()));
|
||||
|
||||
// if we have space remaining, send some data from our reliable channels
|
||||
int remaining = _maxPacketSize - _outgoingPacketStream.device()->pos();
|
||||
const int MINIMUM_RELIABLE_SIZE = sizeof(quint32) * 5; // count, channel number, segment count, offset, size
|
||||
QVector<ChannelSpan> spans;
|
||||
if (remaining > MINIMUM_RELIABLE_SIZE) {
|
||||
appendReliableData(remaining, spans);
|
||||
} else {
|
||||
_outgoingPacketStream << (quint32)0;
|
||||
}
|
||||
|
||||
sendPacket(QByteArray::fromRawData(_outgoingPacketData.constData(), _outgoingPacketStream.device()->pos()), spans);
|
||||
_outgoingPacketStream.device()->seek(0);
|
||||
}
|
||||
|
||||
|
@ -145,13 +181,23 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) {
|
|||
QVariant data;
|
||||
_inputStream >> data;
|
||||
if (i >= _receivedHighPriorityMessages) {
|
||||
emit receivedHighPriorityMessage(data);
|
||||
handleHighPriorityMessage(data);
|
||||
}
|
||||
}
|
||||
_receivedHighPriorityMessages = highPriorityMessageCount;
|
||||
|
||||
// alert external parties so that they can read the rest
|
||||
// alert external parties so that they can read the middle
|
||||
emit readyToRead(_inputStream);
|
||||
|
||||
// read the reliable data, if any
|
||||
quint32 reliableChannels;
|
||||
_incomingPacketStream >> reliableChannels;
|
||||
for (int i = 0; i < reliableChannels; i++) {
|
||||
quint32 channelIndex;
|
||||
_incomingPacketStream >> channelIndex;
|
||||
getReliableOutputChannel(channelIndex)->readData(_incomingPacketStream);
|
||||
}
|
||||
|
||||
_incomingPacketStream.device()->seek(0);
|
||||
_inputStream.reset();
|
||||
|
||||
|
@ -160,6 +206,12 @@ void DatagramSequencer::receivedDatagram(const QByteArray& datagram) {
|
|||
_receiveRecords.append(record);
|
||||
}
|
||||
|
||||
void DatagramSequencer::sendClearSharedObjectMessage(int id) {
|
||||
// for now, high priority
|
||||
ClearSharedObjectMessage message = { id };
|
||||
sendHighPriorityMessage(QVariant::fromValue(message));
|
||||
}
|
||||
|
||||
void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) {
|
||||
// stop acknowledging the recorded packets
|
||||
while (!_receiveRecords.isEmpty() && _receiveRecords.first().packetNumber <= record.lastReceivedPacketNumber) {
|
||||
|
@ -178,9 +230,46 @@ void DatagramSequencer::sendRecordAcknowledged(const SendRecord& record) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// acknowledge the received spans
|
||||
foreach (const ChannelSpan& span, record.spans) {
|
||||
getReliableOutputChannel(span.channel)->spanAcknowledged(span);
|
||||
}
|
||||
}
|
||||
|
||||
void DatagramSequencer::sendPacket(const QByteArray& packet) {
|
||||
void DatagramSequencer::appendReliableData(int bytes, QVector<ChannelSpan>& spans) {
|
||||
// gather total number of bytes to write, priority
|
||||
int totalBytes = 0;
|
||||
float totalPriority = 0.0f;
|
||||
int totalChannels = 0;
|
||||
foreach (ReliableChannel* channel, _reliableOutputChannels) {
|
||||
int channelBytes = channel->getBytesAvailable();
|
||||
if (channelBytes > 0) {
|
||||
totalBytes += channelBytes;
|
||||
totalPriority += channel->getPriority();
|
||||
totalChannels++;
|
||||
}
|
||||
}
|
||||
_outgoingPacketStream << (quint32)totalChannels;
|
||||
if (totalChannels == 0) {
|
||||
return;
|
||||
}
|
||||
totalBytes = qMin(bytes, totalBytes);
|
||||
|
||||
foreach (ReliableChannel* channel, _reliableOutputChannels) {
|
||||
int channelBytes = channel->getBytesAvailable();
|
||||
if (channelBytes == 0) {
|
||||
continue;
|
||||
}
|
||||
_outgoingPacketStream << (quint32)channel->getIndex();
|
||||
channelBytes = qMin(channelBytes, (int)(totalBytes * channel->getPriority() / totalPriority));
|
||||
channel->writeData(_outgoingPacketStream, channelBytes, spans);
|
||||
totalBytes -= channelBytes;
|
||||
totalPriority -= channel->getPriority();
|
||||
}
|
||||
}
|
||||
|
||||
void DatagramSequencer::sendPacket(const QByteArray& packet, const QVector<ChannelSpan>& spans) {
|
||||
QIODeviceOpener opener(&_outgoingDatagramBuffer, QIODevice::WriteOnly);
|
||||
|
||||
// increment the packet number
|
||||
|
@ -188,7 +277,7 @@ void DatagramSequencer::sendPacket(const QByteArray& packet) {
|
|||
|
||||
// record the send
|
||||
SendRecord record = { _outgoingPacketNumber, _receiveRecords.isEmpty() ? 0 : _receiveRecords.last().packetNumber,
|
||||
_outputStream.getAndResetWriteMappings() };
|
||||
_outputStream.getAndResetWriteMappings(), spans };
|
||||
_sendRecords.append(record);
|
||||
|
||||
// write the sequence number and size, which are the same between all fragments
|
||||
|
@ -213,3 +302,228 @@ void DatagramSequencer::sendPacket(const QByteArray& packet) {
|
|||
} while(offset < packet.size());
|
||||
}
|
||||
|
||||
void DatagramSequencer::handleHighPriorityMessage(const QVariant& data) {
|
||||
if (data.userType() == ClearSharedObjectMessage::Type) {
|
||||
_inputStream.clearSharedObject(data.value<ClearSharedObjectMessage>().id);
|
||||
|
||||
} else {
|
||||
emit receivedHighPriorityMessage(data);
|
||||
}
|
||||
}
|
||||
|
||||
SpanList::SpanList() : _totalSet(0) {
|
||||
}
|
||||
|
||||
int SpanList::set(int offset, int length) {
|
||||
// if we intersect the front of the list, consume beginning spans and return advancement
|
||||
if (offset <= 0) {
|
||||
int intersection = offset + length;
|
||||
return (intersection > 0) ? setSpans(_spans.begin(), intersection) : 0;
|
||||
}
|
||||
|
||||
// look for an intersection within the list
|
||||
int position = 0;
|
||||
for (QList<Span>::iterator it = _spans.begin(); it != _spans.end(); it++) {
|
||||
// if we intersect the unset portion, contract it
|
||||
position += it->unset;
|
||||
if (offset <= position) {
|
||||
int remove = position - offset;
|
||||
it->unset -= remove;
|
||||
|
||||
// if we continue into the set portion, expand it and consume following spans
|
||||
int extra = offset + length - position;
|
||||
if (extra >= 0) {
|
||||
int amount = setSpans(it + 1, extra);
|
||||
it->set += amount;
|
||||
_totalSet += amount;
|
||||
|
||||
// otherwise, insert a new span
|
||||
} else {
|
||||
Span span = { it->unset, length + extra };
|
||||
_spans.insert(it, span);
|
||||
it->unset = -extra;
|
||||
_totalSet += span.set;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// if we intersect the set portion, expand it and consume following spans
|
||||
position += it->set;
|
||||
if (offset <= position) {
|
||||
int extra = offset + length - position;
|
||||
int amount = setSpans(it + 1, extra);
|
||||
it->set += amount;
|
||||
_totalSet += amount;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// add to end of list
|
||||
Span span = { offset - position, length };
|
||||
_spans.append(span);
|
||||
_totalSet += length;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SpanList::setSpans(QList<Span>::iterator it, int length) {
|
||||
int remainingLength = length;
|
||||
int totalRemoved = 0;
|
||||
for (; it != _spans.end(); it++) {
|
||||
if (remainingLength < it->unset) {
|
||||
it->unset -= remainingLength;
|
||||
totalRemoved += remainingLength;
|
||||
break;
|
||||
}
|
||||
int combined = it->unset + it->set;
|
||||
remainingLength = qMax(remainingLength - combined, 0);
|
||||
totalRemoved += combined;
|
||||
it = _spans.erase(it);
|
||||
_totalSet -= it->set;
|
||||
}
|
||||
return qMax(length, totalRemoved);
|
||||
}
|
||||
|
||||
int ReliableChannel::getBytesAvailable() const {
|
||||
return _buffer.size() - _acknowledged.getTotalSet();
|
||||
}
|
||||
|
||||
void ReliableChannel::sendMessage(const QVariant& message) {
|
||||
_bitstream << message;
|
||||
}
|
||||
|
||||
void ReliableChannel::sendClearSharedObjectMessage(int id) {
|
||||
ClearSharedObjectMessage message = { id };
|
||||
sendMessage(QVariant::fromValue(message));
|
||||
}
|
||||
|
||||
ReliableChannel::ReliableChannel(DatagramSequencer* sequencer, int index, bool output) :
|
||||
QObject(sequencer),
|
||||
_index(index),
|
||||
_dataStream(&_buffer),
|
||||
_bitstream(_dataStream),
|
||||
_priority(1.0f),
|
||||
_offset(0),
|
||||
_writePosition(0) {
|
||||
|
||||
_buffer.open(output ? QIODevice::WriteOnly : QIODevice::ReadOnly);
|
||||
_dataStream.setByteOrder(QDataStream::LittleEndian);
|
||||
|
||||
connect(&_bitstream, SIGNAL(sharedObjectCleared(int)), SLOT(sendClearSharedObjectMessage(int)));
|
||||
}
|
||||
|
||||
void ReliableChannel::writeData(QDataStream& out, int bytes, QVector<DatagramSequencer::ChannelSpan>& spans) {
|
||||
// find out how many spans we want to write
|
||||
int spanCount = 0;
|
||||
int remainingBytes = bytes;
|
||||
bool first = true;
|
||||
while (remainingBytes > 0) {
|
||||
int position = 0;
|
||||
foreach (const SpanList::Span& span, _acknowledged.getSpans()) {
|
||||
if (remainingBytes <= 0) {
|
||||
break;
|
||||
}
|
||||
spanCount++;
|
||||
|
||||
remainingBytes -= getBytesToWrite(first, span.unset);
|
||||
position += (span.unset + span.set);
|
||||
}
|
||||
int leftover = _buffer.pos() - position;
|
||||
if (remainingBytes > 0 && leftover > 0) {
|
||||
spanCount++;
|
||||
remainingBytes -= getBytesToWrite(first, leftover);
|
||||
}
|
||||
}
|
||||
|
||||
// write the count and the spans
|
||||
out << (quint32)spanCount;
|
||||
remainingBytes = bytes;
|
||||
first = true;
|
||||
while (remainingBytes > 0) {
|
||||
int position = 0;
|
||||
foreach (const SpanList::Span& span, _acknowledged.getSpans()) {
|
||||
if (remainingBytes <= 0) {
|
||||
break;
|
||||
}
|
||||
remainingBytes -= writeSpan(out, first, position, qMin(remainingBytes, span.unset), spans);
|
||||
position += (span.unset + span.set);
|
||||
}
|
||||
if (remainingBytes > 0 && position < _buffer.pos()) {
|
||||
remainingBytes -= writeSpan(out, first, position, qMin(remainingBytes, (int)(_buffer.pos() - position)), spans);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ReliableChannel::getBytesToWrite(bool& first, int length) const {
|
||||
if (first) {
|
||||
first = false;
|
||||
return length - (_writePosition % length);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
int ReliableChannel::writeSpan(QDataStream& out, bool& first, int position, int length, QVector<DatagramSequencer::ChannelSpan>& spans) {
|
||||
if (first) {
|
||||
first = false;
|
||||
position = _writePosition % length;
|
||||
length -= position;
|
||||
_writePosition += length;
|
||||
}
|
||||
DatagramSequencer::ChannelSpan span = { _index, _offset + position, length };
|
||||
spans.append(span);
|
||||
out << (quint32)span.offset;
|
||||
out << (quint32)length;
|
||||
out.writeRawData(_buffer.data().constData() + position, length);
|
||||
return length;
|
||||
}
|
||||
|
||||
void ReliableChannel::spanAcknowledged(const DatagramSequencer::ChannelSpan& span) {
|
||||
int advancement = _acknowledged.set(span.offset - _offset, span.length);
|
||||
if (advancement > 0) {
|
||||
// TODO: better way of pruning buffer
|
||||
_buffer.buffer() = _buffer.buffer().right(_buffer.size() - advancement);
|
||||
_buffer.seek(_buffer.size());
|
||||
_offset += advancement;
|
||||
_writePosition = qMax(_writePosition - advancement, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void ReliableChannel::readData(QDataStream& in) {
|
||||
quint32 segments;
|
||||
in >> segments;
|
||||
for (int i = 0; i < segments; i++) {
|
||||
quint32 offset, size;
|
||||
in >> offset >> size;
|
||||
|
||||
int position = offset - _offset;
|
||||
int end = position + size;
|
||||
if (_assemblyBuffer.size() < end) {
|
||||
_assemblyBuffer.resize(end);
|
||||
}
|
||||
if (end <= 0) {
|
||||
in.skipRawData(size);
|
||||
} else if (position < 0) {
|
||||
in.skipRawData(-position);
|
||||
in.readRawData(_assemblyBuffer.data(), size + position);
|
||||
} else {
|
||||
in.readRawData(_assemblyBuffer.data() + position, size);
|
||||
}
|
||||
int advancement = _acknowledged.set(position, size);
|
||||
if (advancement > 0) {
|
||||
// TODO: better way of pruning buffer
|
||||
_buffer.buffer().append(_assemblyBuffer.constData(), advancement);
|
||||
emit _buffer.readyRead();
|
||||
_assemblyBuffer = _assemblyBuffer.right(_assemblyBuffer.size() - advancement);
|
||||
_offset += advancement;
|
||||
}
|
||||
}
|
||||
|
||||
// when the read head is sufficiently advanced into the buffer, prune it off. this along
|
||||
// with other buffer usages should be replaced with a circular buffer
|
||||
const int PRUNE_SIZE = 8192;
|
||||
if (_buffer.pos() > PRUNE_SIZE) {
|
||||
_buffer.buffer() = _buffer.buffer().right(_buffer.size() - _buffer.pos());
|
||||
_buffer.seek(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,9 +14,12 @@
|
|||
#include <QByteArray>
|
||||
#include <QList>
|
||||
#include <QSet>
|
||||
#include <QVector>
|
||||
|
||||
#include "Bitstream.h"
|
||||
|
||||
class ReliableChannel;
|
||||
|
||||
/// Performs simple datagram sequencing, packet fragmentation and reassembly.
|
||||
class DatagramSequencer : public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -46,6 +49,18 @@ public:
|
|||
/// Returns a reference to the list of high priority messages not yet acknowledged.
|
||||
const QList<HighPriorityMessage>& getHighPriorityMessages() const { return _highPriorityMessages; }
|
||||
|
||||
/// Sets the maximum packet size. This is a soft limit that determines how much
|
||||
/// reliable data we include with each transmission.
|
||||
void setMaxPacketSize(int maxPacketSize) { _maxPacketSize = maxPacketSize; }
|
||||
|
||||
int getMaxPacketSize() const { return _maxPacketSize; }
|
||||
|
||||
/// Returns the output channel at the specified index, creating it if necessary.
|
||||
ReliableChannel* getReliableOutputChannel(int index = 0);
|
||||
|
||||
/// Returns the intput channel at the
|
||||
ReliableChannel* getReliableInputChannel(int index = 0);
|
||||
|
||||
/// Starts a new packet for transmission.
|
||||
/// \return a reference to the Bitstream to use for writing to the packet
|
||||
Bitstream& startPacket();
|
||||
|
@ -75,14 +90,28 @@ signals:
|
|||
/// Emitted when our acknowledgement of a received packet has been acknowledged by the remote side.
|
||||
/// \param index the index of the packet in our list of receive records
|
||||
void receiveAcknowledged(int index);
|
||||
|
||||
private slots:
|
||||
|
||||
void sendClearSharedObjectMessage(int id);
|
||||
|
||||
private:
|
||||
|
||||
friend class ReliableChannel;
|
||||
|
||||
class ChannelSpan {
|
||||
public:
|
||||
int channel;
|
||||
int offset;
|
||||
int length;
|
||||
};
|
||||
|
||||
class SendRecord {
|
||||
public:
|
||||
int packetNumber;
|
||||
int lastReceivedPacketNumber;
|
||||
Bitstream::WriteMappings mappings;
|
||||
QVector<ChannelSpan> spans;
|
||||
};
|
||||
|
||||
class ReceiveRecord {
|
||||
|
@ -97,9 +126,14 @@ private:
|
|||
/// Notes that the described send was acknowledged by the other party.
|
||||
void sendRecordAcknowledged(const SendRecord& record);
|
||||
|
||||
/// Appends some reliable data to the outgoing packet.
|
||||
void appendReliableData(int bytes, QVector<ChannelSpan>& spans);
|
||||
|
||||
/// Sends a packet to the other party, fragmenting it into multiple datagrams (and emitting
|
||||
/// readyToWrite) as necessary.
|
||||
void sendPacket(const QByteArray& packet);
|
||||
void sendPacket(const QByteArray& packet, const QVector<ChannelSpan>& spans);
|
||||
|
||||
void handleHighPriorityMessage(const QVariant& data);
|
||||
|
||||
QList<SendRecord> _sendRecords;
|
||||
QList<ReceiveRecord> _receiveRecords;
|
||||
|
@ -126,6 +160,92 @@ private:
|
|||
|
||||
QList<HighPriorityMessage> _highPriorityMessages;
|
||||
int _receivedHighPriorityMessages;
|
||||
|
||||
int _maxPacketSize;
|
||||
|
||||
QHash<int, ReliableChannel*> _reliableOutputChannels;
|
||||
QHash<int, ReliableChannel*> _reliableInputChannels;
|
||||
};
|
||||
|
||||
/// A list of contiguous spans, alternating between set and unset. Conceptually, the list is preceeded by a set
|
||||
/// span of infinite length and followed by an unset span of infinite length. Within those bounds, it alternates
|
||||
/// between unset and set.
|
||||
class SpanList {
|
||||
public:
|
||||
|
||||
class Span {
|
||||
public:
|
||||
int unset;
|
||||
int set;
|
||||
};
|
||||
|
||||
SpanList();
|
||||
|
||||
const QList<Span>& getSpans() const { return _spans; }
|
||||
|
||||
/// Returns the total length set.
|
||||
int getTotalSet() const { return _totalSet; }
|
||||
|
||||
/// Sets a region of the list.
|
||||
/// \return the advancement of the set length at the beginning of the list
|
||||
int set(int offset, int length);
|
||||
|
||||
private:
|
||||
|
||||
/// Sets the spans starting at the specified iterator, consuming at least the given length.
|
||||
/// \return the actual amount set, which may be greater if we ran into an existing set span
|
||||
int setSpans(QList<Span>::iterator it, int length);
|
||||
|
||||
QList<Span> _spans;
|
||||
int _totalSet;
|
||||
};
|
||||
|
||||
/// Represents a single reliable channel multiplexed onto the datagram sequence.
|
||||
class ReliableChannel : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
int getIndex() const { return _index; }
|
||||
|
||||
QDataStream& getDataStream() { return _dataStream; }
|
||||
Bitstream& getBitstream() { return _bitstream; }
|
||||
|
||||
void setPriority(float priority) { _priority = priority; }
|
||||
float getPriority() const { return _priority; }
|
||||
|
||||
int getBytesAvailable() const;
|
||||
|
||||
void sendMessage(const QVariant& message);
|
||||
|
||||
private slots:
|
||||
|
||||
void sendClearSharedObjectMessage(int id);
|
||||
|
||||
private:
|
||||
|
||||
friend class DatagramSequencer;
|
||||
|
||||
ReliableChannel(DatagramSequencer* sequencer, int index, bool output);
|
||||
|
||||
void writeData(QDataStream& out, int bytes, QVector<DatagramSequencer::ChannelSpan>& spans);
|
||||
int getBytesToWrite(bool& first, int length) const;
|
||||
int writeSpan(QDataStream& out, bool& first, int position, int length, QVector<DatagramSequencer::ChannelSpan>& spans);
|
||||
|
||||
void spanAcknowledged(const DatagramSequencer::ChannelSpan& span);
|
||||
|
||||
void readData(QDataStream& in);
|
||||
|
||||
int _index;
|
||||
QBuffer _buffer;
|
||||
QByteArray _assemblyBuffer;
|
||||
QDataStream _dataStream;
|
||||
Bitstream _bitstream;
|
||||
float _priority;
|
||||
|
||||
int _offset;
|
||||
int _writePosition;
|
||||
SpanList _acknowledged;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__DatagramSequencer__) */
|
||||
|
|
|
@ -6,11 +6,18 @@
|
|||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QScriptEngine>
|
||||
#include <QtDebug>
|
||||
|
||||
#include "MetavoxelData.h"
|
||||
#include "MetavoxelUtil.h"
|
||||
#include "ScriptCache.h"
|
||||
|
||||
REGISTER_META_OBJECT(MetavoxelGuide)
|
||||
REGISTER_META_OBJECT(DefaultMetavoxelGuide)
|
||||
REGISTER_META_OBJECT(ScriptedMetavoxelGuide)
|
||||
REGISTER_META_OBJECT(ThrobbingMetavoxelGuide)
|
||||
|
||||
MetavoxelData::MetavoxelData() : _size(1.0f) {
|
||||
}
|
||||
|
@ -61,7 +68,7 @@ void MetavoxelData::guide(MetavoxelVisitor& visitor) {
|
|||
firstVisitation.outputNodes[i] = node;
|
||||
}
|
||||
static_cast<MetavoxelGuide*>(firstVisitation.info.inputValues.last().getInlineValue<
|
||||
PolymorphicDataPointer>().data())->guide(firstVisitation);
|
||||
SharedObjectPointer>().data())->guide(firstVisitation);
|
||||
for (int i = 0; i < outputs.size(); i++) {
|
||||
AttributeValue& value = firstVisitation.info.outputValues[i];
|
||||
if (!value.getAttribute()) {
|
||||
|
@ -129,7 +136,7 @@ void MetavoxelData::read(Bitstream& in) {
|
|||
in >> rootCount;
|
||||
for (int i = 0; i < rootCount; i++) {
|
||||
AttributePointer attribute;
|
||||
in.getAttributeStreamer() >> attribute;
|
||||
in >> attribute;
|
||||
MetavoxelNode*& root = _roots[attribute];
|
||||
root = new MetavoxelNode(attribute);
|
||||
root->read(attribute, in);
|
||||
|
@ -140,7 +147,7 @@ void MetavoxelData::write(Bitstream& out) const {
|
|||
out << _size;
|
||||
out << _roots.size();
|
||||
for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) {
|
||||
out.getAttributeStreamer() << it.key();
|
||||
out << it.key();
|
||||
it.value()->write(it.key(), out);
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +176,7 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, Bitstream& in) {
|
|||
in >> changedCount;
|
||||
for (int i = 0; i < changedCount; i++) {
|
||||
AttributePointer attribute;
|
||||
in.getAttributeStreamer() >> attribute;
|
||||
in >> attribute;
|
||||
MetavoxelNode*& root = _roots[attribute];
|
||||
if (root) {
|
||||
MetavoxelNode* oldRoot = root;
|
||||
|
@ -187,7 +194,7 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, Bitstream& in) {
|
|||
in >> removedCount;
|
||||
for (int i = 0; i < removedCount; i++) {
|
||||
AttributePointer attribute;
|
||||
in.getAttributeStreamer() >> attribute;
|
||||
in >> attribute;
|
||||
_roots.take(attribute)->decrementReferenceCount(attribute);
|
||||
}
|
||||
}
|
||||
|
@ -227,7 +234,7 @@ void MetavoxelData::writeDelta(const MetavoxelData& reference, Bitstream& out) c
|
|||
for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) {
|
||||
MetavoxelNode* referenceRoot = expandedReference->_roots.value(it.key());
|
||||
if (it.value() != referenceRoot) {
|
||||
out.getAttributeStreamer() << it.key();
|
||||
out << it.key();
|
||||
if (referenceRoot) {
|
||||
it.value()->writeDelta(it.key(), *referenceRoot, out);
|
||||
} else {
|
||||
|
@ -248,7 +255,7 @@ void MetavoxelData::writeDelta(const MetavoxelData& reference, Bitstream& out) c
|
|||
for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = expandedReference->_roots.constBegin();
|
||||
it != expandedReference->_roots.constEnd(); it++) {
|
||||
if (!_roots.contains(it.key())) {
|
||||
out.getAttributeStreamer() << it.key();
|
||||
out << it.key();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -425,8 +432,7 @@ MetavoxelVisitor::MetavoxelVisitor(const QVector<AttributePointer>& inputs, cons
|
|||
MetavoxelVisitor::~MetavoxelVisitor() {
|
||||
}
|
||||
|
||||
PolymorphicData* DefaultMetavoxelGuide::clone() const {
|
||||
return new DefaultMetavoxelGuide();
|
||||
DefaultMetavoxelGuide::DefaultMetavoxelGuide() {
|
||||
}
|
||||
|
||||
void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
|
||||
|
@ -470,7 +476,7 @@ void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
|
|||
(i & Y_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f,
|
||||
(i & Z_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f);
|
||||
static_cast<MetavoxelGuide*>(nextVisitation.info.inputValues.last().getInlineValue<
|
||||
PolymorphicDataPointer>().data())->guide(nextVisitation);
|
||||
SharedObjectPointer>().data())->guide(nextVisitation);
|
||||
for (int j = 0; j < nextVisitation.outputNodes.size(); j++) {
|
||||
AttributeValue& value = nextVisitation.info.outputValues[j];
|
||||
if (!value.getAttribute()) {
|
||||
|
@ -514,6 +520,25 @@ void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
|
|||
}
|
||||
}
|
||||
|
||||
ThrobbingMetavoxelGuide::ThrobbingMetavoxelGuide() : _rate(10.0) {
|
||||
}
|
||||
|
||||
void ThrobbingMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
|
||||
AttributePointer colorAttribute = AttributeRegistry::getInstance()->getColorAttribute();
|
||||
for (int i = 0; i < visitation.info.inputValues.size(); i++) {
|
||||
AttributeValue& attributeValue = visitation.info.inputValues[i];
|
||||
if (attributeValue.getAttribute() == colorAttribute) {
|
||||
QRgb base = attributeValue.getInlineValue<QRgb>();
|
||||
double seconds = QDateTime::currentMSecsSinceEpoch() / 1000.0;
|
||||
double amplitude = sin(_rate * seconds) * 0.5 + 0.5;
|
||||
attributeValue.setInlineValue<QRgb>(qRgba(qRed(base) * amplitude, qGreen(base) * amplitude,
|
||||
qBlue(base) * amplitude, qAlpha(base)));
|
||||
}
|
||||
}
|
||||
|
||||
DefaultMetavoxelGuide::guide(visitation);
|
||||
}
|
||||
|
||||
static QScriptValue getAttributes(QScriptEngine* engine, ScriptedMetavoxelGuide* guide,
|
||||
const QVector<AttributePointer>& attributes) {
|
||||
|
||||
|
@ -569,35 +594,47 @@ QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngin
|
|||
return result;
|
||||
}
|
||||
|
||||
ScriptedMetavoxelGuide::ScriptedMetavoxelGuide(const QScriptValue& guideFunction) :
|
||||
_guideFunction(guideFunction),
|
||||
_minimumHandle(guideFunction.engine()->toStringHandle("minimum")),
|
||||
_sizeHandle(guideFunction.engine()->toStringHandle("size")),
|
||||
_inputValuesHandle(guideFunction.engine()->toStringHandle("inputValues")),
|
||||
_outputValuesHandle(guideFunction.engine()->toStringHandle("outputValues")),
|
||||
_isLeafHandle(guideFunction.engine()->toStringHandle("isLeaf")),
|
||||
_getInputsFunction(guideFunction.engine()->newFunction(getInputs, 0)),
|
||||
_getOutputsFunction(guideFunction.engine()->newFunction(getOutputs, 0)),
|
||||
_visitFunction(guideFunction.engine()->newFunction(visit, 1)),
|
||||
_info(guideFunction.engine()->newObject()),
|
||||
_minimum(guideFunction.engine()->newArray(3)) {
|
||||
|
||||
_arguments.append(guideFunction.engine()->newObject());
|
||||
QScriptValue visitor = guideFunction.engine()->newObject();
|
||||
visitor.setProperty("getInputs", _getInputsFunction);
|
||||
visitor.setProperty("getOutputs", _getOutputsFunction);
|
||||
visitor.setProperty("visit", _visitFunction);
|
||||
_arguments[0].setProperty("visitor", visitor);
|
||||
_arguments[0].setProperty("info", _info);
|
||||
_info.setProperty(_minimumHandle, _minimum);
|
||||
}
|
||||
|
||||
PolymorphicData* ScriptedMetavoxelGuide::clone() const {
|
||||
return new ScriptedMetavoxelGuide(_guideFunction);
|
||||
ScriptedMetavoxelGuide::ScriptedMetavoxelGuide() {
|
||||
}
|
||||
|
||||
void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
|
||||
QScriptValue data = _guideFunction.engine()->newVariant(QVariant::fromValue<void*>(this));
|
||||
QScriptValue guideFunction;
|
||||
if (_guideFunction) {
|
||||
guideFunction = _guideFunction->getValue();
|
||||
|
||||
} else if (_url.isValid()) {
|
||||
_guideFunction = ScriptCache::getInstance()->getValue(_url);
|
||||
guideFunction = _guideFunction->getValue();
|
||||
}
|
||||
if (!guideFunction.isValid()) {
|
||||
// before we load, just use the default behavior
|
||||
DefaultMetavoxelGuide::guide(visitation);
|
||||
return;
|
||||
}
|
||||
QScriptEngine* engine = guideFunction.engine();
|
||||
if (!_minimumHandle.isValid()) {
|
||||
_minimumHandle = engine->toStringHandle("minimum");
|
||||
_sizeHandle = engine->toStringHandle("size");
|
||||
_inputValuesHandle = engine->toStringHandle("inputValues");
|
||||
_outputValuesHandle = engine->toStringHandle("outputValues");
|
||||
_isLeafHandle = engine->toStringHandle("isLeaf");
|
||||
_getInputsFunction = engine->newFunction(getInputs, 0);
|
||||
_getOutputsFunction = engine->newFunction(getOutputs, 0);
|
||||
_visitFunction = engine->newFunction(visit, 1);
|
||||
_info = engine->newObject();
|
||||
_minimum = engine->newArray(3);
|
||||
|
||||
_arguments.clear();
|
||||
_arguments.append(engine->newObject());
|
||||
QScriptValue visitor = engine->newObject();
|
||||
visitor.setProperty("getInputs", _getInputsFunction);
|
||||
visitor.setProperty("getOutputs", _getOutputsFunction);
|
||||
visitor.setProperty("visit", _visitFunction);
|
||||
_arguments[0].setProperty("visitor", visitor);
|
||||
_arguments[0].setProperty("info", _info);
|
||||
_info.setProperty(_minimumHandle, _minimum);
|
||||
}
|
||||
QScriptValue data = engine->newVariant(QVariant::fromValue<void*>(this));
|
||||
_getInputsFunction.setData(data);
|
||||
_visitFunction.setData(data);
|
||||
_minimum.setProperty(0, visitation.info.minimum.x);
|
||||
|
@ -606,12 +643,18 @@ void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
|
|||
_info.setProperty(_sizeHandle, visitation.info.size);
|
||||
_info.setProperty(_isLeafHandle, visitation.info.isLeaf);
|
||||
_visitation = &visitation;
|
||||
_guideFunction.call(QScriptValue(), _arguments);
|
||||
if (_guideFunction.engine()->hasUncaughtException()) {
|
||||
qDebug() << "Script error: " << _guideFunction.engine()->uncaughtException().toString();
|
||||
guideFunction.call(QScriptValue(), _arguments);
|
||||
if (engine->hasUncaughtException()) {
|
||||
qDebug() << "Script error: " << engine->uncaughtException().toString();
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptedMetavoxelGuide::setURL(const ParameterizedURL& url) {
|
||||
_url = url;
|
||||
_guideFunction.reset();
|
||||
_minimumHandle = QScriptString();
|
||||
}
|
||||
|
||||
bool MetavoxelVisitation::allInputNodesLeaves() const {
|
||||
foreach (MetavoxelNode* node, inputNodes) {
|
||||
if (node != NULL && !node->isLeaf()) {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <QBitArray>
|
||||
#include <QHash>
|
||||
#include <QSharedData>
|
||||
#include <QSharedPointer>
|
||||
#include <QScriptString>
|
||||
#include <QScriptValue>
|
||||
#include <QVector>
|
||||
|
@ -19,13 +20,14 @@
|
|||
#include <glm/glm.hpp>
|
||||
|
||||
#include "AttributeRegistry.h"
|
||||
#include "MetavoxelUtil.h"
|
||||
|
||||
class QScriptContext;
|
||||
|
||||
class Box;
|
||||
class MetavoxelNode;
|
||||
class MetavoxelVisitation;
|
||||
class MetavoxelVisitor;
|
||||
class NetworkValue;
|
||||
|
||||
/// The base metavoxel representation shared between server and client.
|
||||
class MetavoxelData {
|
||||
|
@ -150,7 +152,9 @@ protected:
|
|||
typedef QSharedPointer<MetavoxelVisitor> MetavoxelVisitorPointer;
|
||||
|
||||
/// Interface for objects that guide metavoxel visitors.
|
||||
class MetavoxelGuide : public PolymorphicData {
|
||||
class MetavoxelGuide : public SharedObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/// Guides the specified visitor to the contained voxels.
|
||||
|
@ -159,30 +163,56 @@ public:
|
|||
|
||||
/// Guides visitors through the explicit content of the system.
|
||||
class DefaultMetavoxelGuide : public MetavoxelGuide {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
virtual PolymorphicData* clone() const;
|
||||
Q_INVOKABLE DefaultMetavoxelGuide();
|
||||
|
||||
virtual void guide(MetavoxelVisitation& visitation);
|
||||
};
|
||||
|
||||
/// Represents a guide implemented in Javascript.
|
||||
class ScriptedMetavoxelGuide : public MetavoxelGuide {
|
||||
/// A temporary test guide that just makes the existing voxels throb with delight.
|
||||
class ThrobbingMetavoxelGuide : public DefaultMetavoxelGuide {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float rate MEMBER _rate)
|
||||
|
||||
public:
|
||||
|
||||
ScriptedMetavoxelGuide(const QScriptValue& guideFunction);
|
||||
|
||||
virtual PolymorphicData* clone() const;
|
||||
|
||||
Q_INVOKABLE ThrobbingMetavoxelGuide();
|
||||
|
||||
virtual void guide(MetavoxelVisitation& visitation);
|
||||
|
||||
private:
|
||||
|
||||
float _rate;
|
||||
};
|
||||
|
||||
/// Represents a guide implemented in Javascript.
|
||||
class ScriptedMetavoxelGuide : public DefaultMetavoxelGuide {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(ParameterizedURL url MEMBER _url WRITE setURL)
|
||||
|
||||
public:
|
||||
|
||||
Q_INVOKABLE ScriptedMetavoxelGuide();
|
||||
|
||||
virtual void guide(MetavoxelVisitation& visitation);
|
||||
|
||||
public slots:
|
||||
|
||||
void setURL(const ParameterizedURL& url);
|
||||
|
||||
private:
|
||||
|
||||
static QScriptValue getInputs(QScriptContext* context, QScriptEngine* engine);
|
||||
static QScriptValue getOutputs(QScriptContext* context, QScriptEngine* engine);
|
||||
static QScriptValue visit(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
QScriptValue _guideFunction;
|
||||
ParameterizedURL _url;
|
||||
|
||||
QSharedPointer<NetworkValue> _guideFunction;
|
||||
|
||||
QScriptString _minimumHandle;
|
||||
QScriptString _sizeHandle;
|
||||
QScriptString _inputValuesHandle;
|
||||
|
|
|
@ -21,6 +21,17 @@ class CloseSessionMessage {
|
|||
|
||||
DECLARE_STREAMABLE_METATYPE(CloseSessionMessage)
|
||||
|
||||
/// Clears the mapping for a shared object.
|
||||
class ClearSharedObjectMessage {
|
||||
STREAMABLE
|
||||
|
||||
public:
|
||||
|
||||
STREAM int id;
|
||||
};
|
||||
|
||||
DECLARE_STREAMABLE_METATYPE(ClearSharedObjectMessage)
|
||||
|
||||
/// A message containing the state of a client.
|
||||
class ClientStateMessage {
|
||||
STREAMABLE
|
||||
|
|
|
@ -7,12 +7,104 @@
|
|||
//
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QColorDialog>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QFormLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QItemEditorFactory>
|
||||
#include <QLineEdit>
|
||||
#include <QMetaType>
|
||||
#include <QPushButton>
|
||||
#include <QScriptEngine>
|
||||
#include <QStandardItemEditorCreator>
|
||||
#include <QVBoxLayout>
|
||||
#include <QtDebug>
|
||||
|
||||
#include <HifiSockAddr.h>
|
||||
#include <PacketHeaders.h>
|
||||
|
||||
#include "MetavoxelUtil.h"
|
||||
#include "ScriptCache.h"
|
||||
|
||||
static int scriptHashType = qRegisterMetaType<ScriptHash>();
|
||||
static int parameterizedURLType = qRegisterMetaType<ParameterizedURL>();
|
||||
|
||||
REGISTER_SIMPLE_TYPE_STREAMER(ScriptHash)
|
||||
REGISTER_SIMPLE_TYPE_STREAMER(ParameterizedURL)
|
||||
|
||||
class DelegatingItemEditorFactory : public QItemEditorFactory {
|
||||
public:
|
||||
|
||||
DelegatingItemEditorFactory();
|
||||
|
||||
virtual QWidget* createEditor(int userType, QWidget* parent) const;
|
||||
virtual QByteArray valuePropertyName(int userType) const;
|
||||
|
||||
private:
|
||||
|
||||
const QItemEditorFactory* _parentFactory;
|
||||
};
|
||||
|
||||
class DoubleEditor : public QDoubleSpinBox {
|
||||
public:
|
||||
|
||||
DoubleEditor(QWidget* parent = NULL);
|
||||
};
|
||||
|
||||
DoubleEditor::DoubleEditor(QWidget* parent) : QDoubleSpinBox(parent) {
|
||||
setMinimum(-FLT_MAX);
|
||||
}
|
||||
|
||||
DelegatingItemEditorFactory::DelegatingItemEditorFactory() :
|
||||
_parentFactory(QItemEditorFactory::defaultFactory()) {
|
||||
|
||||
QItemEditorFactory::setDefaultFactory(this);
|
||||
}
|
||||
|
||||
QWidget* DelegatingItemEditorFactory::createEditor(int userType, QWidget* parent) const {
|
||||
QWidget* editor = QItemEditorFactory::createEditor(userType, parent);
|
||||
return (editor == NULL) ? _parentFactory->createEditor(userType, parent) : editor;
|
||||
}
|
||||
|
||||
QByteArray DelegatingItemEditorFactory::valuePropertyName(int userType) const {
|
||||
QByteArray propertyName = QItemEditorFactory::valuePropertyName(userType);
|
||||
return propertyName.isNull() ? _parentFactory->valuePropertyName(userType) : propertyName;
|
||||
}
|
||||
|
||||
static QItemEditorFactory* getItemEditorFactory() {
|
||||
static QItemEditorFactory* factory = new DelegatingItemEditorFactory();
|
||||
return factory;
|
||||
}
|
||||
|
||||
static QItemEditorCreatorBase* createDoubleEditorCreator() {
|
||||
QItemEditorCreatorBase* creator = new QStandardItemEditorCreator<DoubleEditor>();
|
||||
getItemEditorFactory()->registerEditor(qMetaTypeId<double>(), creator);
|
||||
getItemEditorFactory()->registerEditor(qMetaTypeId<float>(), creator);
|
||||
return creator;
|
||||
}
|
||||
|
||||
static QItemEditorCreatorBase* createQColorEditorCreator() {
|
||||
QItemEditorCreatorBase* creator = new QStandardItemEditorCreator<QColorEditor>();
|
||||
getItemEditorFactory()->registerEditor(qMetaTypeId<QColor>(), creator);
|
||||
return creator;
|
||||
}
|
||||
|
||||
static QItemEditorCreatorBase* createVec3EditorCreator() {
|
||||
QItemEditorCreatorBase* creator = new QStandardItemEditorCreator<Vec3Editor>();
|
||||
getItemEditorFactory()->registerEditor(qMetaTypeId<glm::vec3>(), creator);
|
||||
return creator;
|
||||
}
|
||||
|
||||
static QItemEditorCreatorBase* createParameterizedURLEditorCreator() {
|
||||
QItemEditorCreatorBase* creator = new QStandardItemEditorCreator<ParameterizedURLEditor>();
|
||||
getItemEditorFactory()->registerEditor(qMetaTypeId<ParameterizedURL>(), creator);
|
||||
return creator;
|
||||
}
|
||||
|
||||
static QItemEditorCreatorBase* doubleEditorCreator = createDoubleEditorCreator();
|
||||
static QItemEditorCreatorBase* qColorEditorCreator = createQColorEditorCreator();
|
||||
static QItemEditorCreatorBase* vec3EditorCreator = createVec3EditorCreator();
|
||||
static QItemEditorCreatorBase* parameterizedURLEditorCreator = createParameterizedURLEditorCreator();
|
||||
|
||||
QUuid readSessionID(const QByteArray& data, const HifiSockAddr& sender, int& headerPlusIDSize) {
|
||||
// get the header size
|
||||
|
@ -33,3 +125,185 @@ bool Box::contains(const Box& other) const {
|
|||
other.minimum.y >= minimum.y && other.maximum.y <= maximum.y &&
|
||||
other.minimum.z >= minimum.z && other.maximum.z <= maximum.z;
|
||||
}
|
||||
|
||||
QColorEditor::QColorEditor(QWidget* parent) : QWidget(parent) {
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
layout->setContentsMargins(QMargins());
|
||||
layout->setAlignment(Qt::AlignTop);
|
||||
setLayout(layout);
|
||||
layout->addWidget(_button = new QPushButton());
|
||||
connect(_button, SIGNAL(clicked()), SLOT(selectColor()));
|
||||
}
|
||||
|
||||
void QColorEditor::setColor(const QColor& color) {
|
||||
QString name = (_color = color).name();
|
||||
_button->setStyleSheet(QString("background: %1; color: %2").arg(name, QColor::fromRgb(~color.rgb()).name()));
|
||||
_button->setText(name);
|
||||
}
|
||||
|
||||
void QColorEditor::selectColor() {
|
||||
QColor color = QColorDialog::getColor(_color, this, QString(), QColorDialog::ShowAlphaChannel);
|
||||
if (color.isValid()) {
|
||||
setColor(color);
|
||||
emit colorChanged(color);
|
||||
}
|
||||
}
|
||||
|
||||
Vec3Editor::Vec3Editor(QWidget* parent) : QWidget(parent) {
|
||||
QHBoxLayout* layout = new QHBoxLayout();
|
||||
layout->setContentsMargins(QMargins());
|
||||
setLayout(layout);
|
||||
|
||||
layout->addWidget(_x = createComponentBox());
|
||||
layout->addWidget(_y = createComponentBox());
|
||||
layout->addWidget(_z = createComponentBox());
|
||||
}
|
||||
|
||||
void Vec3Editor::setVector(const glm::vec3& vector) {
|
||||
_vector = vector;
|
||||
_x->setValue(vector.x);
|
||||
_y->setValue(vector.y);
|
||||
_z->setValue(vector.z);
|
||||
}
|
||||
|
||||
void Vec3Editor::updateVector() {
|
||||
emit vectorChanged(_vector = glm::vec3(_x->value(), _y->value(), _z->value()));
|
||||
}
|
||||
|
||||
QDoubleSpinBox* Vec3Editor::createComponentBox() {
|
||||
QDoubleSpinBox* box = new QDoubleSpinBox();
|
||||
box->setMinimum(-FLT_MAX);
|
||||
box->setMaximumWidth(100);
|
||||
connect(box, SIGNAL(valueChanged(double)), SLOT(updateVector()));
|
||||
return box;
|
||||
}
|
||||
|
||||
ParameterizedURL::ParameterizedURL(const QUrl& url, const ScriptHash& parameters) :
|
||||
_url(url),
|
||||
_parameters(parameters) {
|
||||
}
|
||||
|
||||
bool ParameterizedURL::operator==(const ParameterizedURL& other) const {
|
||||
return _url == other._url && _parameters == other._parameters;
|
||||
}
|
||||
|
||||
bool ParameterizedURL::operator!=(const ParameterizedURL& other) const {
|
||||
return _url != other._url || _parameters != other._parameters;
|
||||
}
|
||||
|
||||
uint qHash(const ParameterizedURL& url, uint seed) {
|
||||
// just hash on the URL, for now
|
||||
return qHash(url.getURL(), seed);
|
||||
}
|
||||
|
||||
Bitstream& operator<<(Bitstream& out, const ParameterizedURL& url) {
|
||||
out << url.getURL();
|
||||
out << url.getParameters();
|
||||
return out;
|
||||
}
|
||||
|
||||
Bitstream& operator>>(Bitstream& in, ParameterizedURL& url) {
|
||||
QUrl qurl;
|
||||
in >> qurl;
|
||||
ScriptHash parameters;
|
||||
in >> parameters;
|
||||
url = ParameterizedURL(qurl, parameters);
|
||||
return in;
|
||||
}
|
||||
|
||||
ParameterizedURLEditor::ParameterizedURLEditor(QWidget* parent) :
|
||||
QWidget(parent) {
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
layout->setContentsMargins(QMargins());
|
||||
setLayout(layout);
|
||||
|
||||
QWidget* lineContainer = new QWidget();
|
||||
layout->addWidget(lineContainer);
|
||||
|
||||
QHBoxLayout* lineLayout = new QHBoxLayout();
|
||||
lineContainer->setLayout(lineLayout);
|
||||
lineLayout->setContentsMargins(QMargins());
|
||||
|
||||
lineLayout->addWidget(_line = new QLineEdit(), 1);
|
||||
connect(_line, SIGNAL(textChanged(const QString&)), SLOT(updateURL()));
|
||||
|
||||
QPushButton* refresh = new QPushButton("...");
|
||||
connect(refresh, SIGNAL(clicked(bool)), SLOT(updateParameters()));
|
||||
lineLayout->addWidget(refresh);
|
||||
}
|
||||
|
||||
void ParameterizedURLEditor::setURL(const ParameterizedURL& url) {
|
||||
_url = url;
|
||||
_line->setText(url.getURL().toString());
|
||||
updateParameters();
|
||||
}
|
||||
|
||||
void ParameterizedURLEditor::updateURL() {
|
||||
ScriptHash parameters;
|
||||
if (layout()->count() > 1) {
|
||||
QFormLayout* form = static_cast<QFormLayout*>(layout()->itemAt(1));
|
||||
for (int i = 0; i < form->rowCount(); i++) {
|
||||
QWidget* widget = form->itemAt(i, QFormLayout::FieldRole)->widget();
|
||||
QByteArray valuePropertyName = widget->property("valuePropertyName").toByteArray();
|
||||
const QMetaObject* widgetMetaObject = widget->metaObject();
|
||||
QMetaProperty widgetProperty = widgetMetaObject->property(widgetMetaObject->indexOfProperty(valuePropertyName));
|
||||
parameters.insert(ScriptCache::getInstance()->getEngine()->toStringHandle(
|
||||
widget->property("parameterName").toString()), widgetProperty.read(widget));
|
||||
}
|
||||
}
|
||||
emit urlChanged(_url = ParameterizedURL(_line->text(), parameters));
|
||||
if (_program) {
|
||||
_program->disconnect(this);
|
||||
}
|
||||
}
|
||||
|
||||
void ParameterizedURLEditor::updateParameters() {
|
||||
if (_program) {
|
||||
_program->disconnect(this);
|
||||
}
|
||||
_program = ScriptCache::getInstance()->getProgram(_url.getURL());
|
||||
if (_program->isLoaded()) {
|
||||
continueUpdatingParameters();
|
||||
} else {
|
||||
connect(_program.data(), SIGNAL(loaded()), SLOT(continueUpdatingParameters()));
|
||||
}
|
||||
}
|
||||
|
||||
void ParameterizedURLEditor::continueUpdatingParameters() {
|
||||
QVBoxLayout* layout = static_cast<QVBoxLayout*>(this->layout());
|
||||
if (layout->count() > 1) {
|
||||
QFormLayout* form = static_cast<QFormLayout*>(layout->takeAt(1));
|
||||
for (int i = form->count() - 1; i >= 0; i--) {
|
||||
QLayoutItem* item = form->takeAt(i);
|
||||
if (item->widget()) {
|
||||
delete item->widget();
|
||||
}
|
||||
delete item;
|
||||
}
|
||||
delete form;
|
||||
}
|
||||
QSharedPointer<NetworkValue> value = ScriptCache::getInstance()->getValue(_url.getURL());
|
||||
const QList<ParameterInfo>& parameters = static_cast<RootNetworkValue*>(value.data())->getParameterInfo();
|
||||
if (parameters.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
QFormLayout* form = new QFormLayout();
|
||||
layout->addLayout(form);
|
||||
foreach (const ParameterInfo& parameter, parameters) {
|
||||
QWidget* widget = QItemEditorFactory::defaultFactory()->createEditor(parameter.type, NULL);
|
||||
if (widget) {
|
||||
form->addRow(parameter.name.toString() + ":", widget);
|
||||
QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(parameter.type);
|
||||
widget->setProperty("parameterName", parameter.name.toString());
|
||||
widget->setProperty("valuePropertyName", valuePropertyName);
|
||||
const QMetaObject* widgetMetaObject = widget->metaObject();
|
||||
QMetaProperty widgetProperty = widgetMetaObject->property(widgetMetaObject->indexOfProperty(valuePropertyName));
|
||||
widgetProperty.write(widget, _url.getParameters().value(parameter.name));
|
||||
if (widgetProperty.hasNotifySignal()) {
|
||||
connect(widget, QByteArray(SIGNAL()).append(widgetProperty.notifySignal().methodSignature()),
|
||||
SLOT(updateURL()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,13 +9,23 @@
|
|||
#ifndef __interface__MetavoxelUtil__
|
||||
#define __interface__MetavoxelUtil__
|
||||
|
||||
#include <QColor>
|
||||
#include <QSharedPointer>
|
||||
#include <QUrl>
|
||||
#include <QUuid>
|
||||
#include <QWidget>
|
||||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
#include "Bitstream.h"
|
||||
|
||||
class QByteArray;
|
||||
class QDoubleSpinBox;
|
||||
class QLineEdit;
|
||||
class QPushButton;
|
||||
|
||||
class HifiSockAddr;
|
||||
class NetworkProgram;
|
||||
|
||||
/// Reads and returns the session ID from a datagram.
|
||||
/// \param[out] headerPlusIDSize the size of the header (including the session ID) within the data
|
||||
|
@ -36,4 +46,127 @@ public:
|
|||
|
||||
DECLARE_STREAMABLE_METATYPE(Box)
|
||||
|
||||
/// Editor for color values.
|
||||
class QColorEditor : public QWidget {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QColor color MEMBER _color WRITE setColor NOTIFY colorChanged USER true)
|
||||
|
||||
public:
|
||||
|
||||
QColorEditor(QWidget* parent);
|
||||
|
||||
signals:
|
||||
|
||||
void colorChanged(const QColor& color);
|
||||
|
||||
public slots:
|
||||
|
||||
void setColor(const QColor& color);
|
||||
|
||||
private slots:
|
||||
|
||||
void selectColor();
|
||||
|
||||
private:
|
||||
|
||||
QPushButton* _button;
|
||||
QColor _color;
|
||||
};
|
||||
|
||||
/// Editor for vector values.
|
||||
class Vec3Editor : public QWidget {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(glm::vec3 vector MEMBER _vector WRITE setVector NOTIFY vectorChanged USER true)
|
||||
|
||||
public:
|
||||
|
||||
Vec3Editor(QWidget* parent);
|
||||
|
||||
signals:
|
||||
|
||||
void vectorChanged(const glm::vec3& vector);
|
||||
|
||||
public slots:
|
||||
|
||||
void setVector(const glm::vec3& vector);
|
||||
|
||||
private slots:
|
||||
|
||||
void updateVector();
|
||||
|
||||
private:
|
||||
|
||||
QDoubleSpinBox* createComponentBox();
|
||||
|
||||
QDoubleSpinBox* _x;
|
||||
QDoubleSpinBox* _y;
|
||||
QDoubleSpinBox* _z;
|
||||
glm::vec3 _vector;
|
||||
};
|
||||
|
||||
typedef QHash<QScriptString, QVariant> ScriptHash;
|
||||
|
||||
Q_DECLARE_METATYPE(ScriptHash)
|
||||
|
||||
/// Combines a URL with a set of typed parameters.
|
||||
class ParameterizedURL {
|
||||
public:
|
||||
|
||||
ParameterizedURL(const QUrl& url = QUrl(), const ScriptHash& parameters = ScriptHash());
|
||||
|
||||
bool isValid() const { return _url.isValid(); }
|
||||
|
||||
void setURL(const QUrl& url) { _url = url; }
|
||||
const QUrl& getURL() const { return _url; }
|
||||
|
||||
void setParameters(const ScriptHash& parameters) { _parameters = parameters; }
|
||||
const ScriptHash& getParameters() const { return _parameters; }
|
||||
|
||||
bool operator==(const ParameterizedURL& other) const;
|
||||
bool operator!=(const ParameterizedURL& other) const;
|
||||
|
||||
private:
|
||||
|
||||
QUrl _url;
|
||||
ScriptHash _parameters;
|
||||
};
|
||||
|
||||
uint qHash(const ParameterizedURL& url, uint seed = 0);
|
||||
|
||||
Bitstream& operator<<(Bitstream& out, const ParameterizedURL& url);
|
||||
Bitstream& operator>>(Bitstream& in, ParameterizedURL& url);
|
||||
|
||||
Q_DECLARE_METATYPE(ParameterizedURL)
|
||||
|
||||
/// Allows editing parameterized URLs.
|
||||
class ParameterizedURLEditor : public QWidget {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(ParameterizedURL url MEMBER _url WRITE setURL NOTIFY urlChanged USER true)
|
||||
|
||||
public:
|
||||
|
||||
ParameterizedURLEditor(QWidget* parent = NULL);
|
||||
|
||||
signals:
|
||||
|
||||
void urlChanged(const ParameterizedURL& url);
|
||||
|
||||
public slots:
|
||||
|
||||
void setURL(const ParameterizedURL& url);
|
||||
|
||||
private slots:
|
||||
|
||||
void updateURL();
|
||||
void updateParameters();
|
||||
void continueUpdatingParameters();
|
||||
|
||||
private:
|
||||
|
||||
ParameterizedURL _url;
|
||||
QSharedPointer<NetworkProgram> _program;
|
||||
|
||||
QLineEdit* _line;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__MetavoxelUtil__) */
|
||||
|
|
177
libraries/metavoxels/src/ScriptCache.cpp
Normal file
177
libraries/metavoxels/src/ScriptCache.cpp
Normal file
|
@ -0,0 +1,177 @@
|
|||
//
|
||||
// ScriptCache.cpp
|
||||
// metavoxels
|
||||
//
|
||||
// Created by Andrzej Kapolka on 2/4/14.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <QNetworkReply>
|
||||
#include <QScriptEngine>
|
||||
#include <QTextStream>
|
||||
#include <QTimer>
|
||||
#include <QtDebug>
|
||||
|
||||
#include "AttributeRegistry.h"
|
||||
#include "ScriptCache.h"
|
||||
|
||||
ScriptCache* ScriptCache::getInstance() {
|
||||
static ScriptCache cache;
|
||||
return &cache;
|
||||
}
|
||||
|
||||
ScriptCache::ScriptCache() :
|
||||
_networkAccessManager(NULL),
|
||||
_engine(NULL) {
|
||||
|
||||
setEngine(new QScriptEngine(this));
|
||||
}
|
||||
|
||||
void ScriptCache::setEngine(QScriptEngine* engine) {
|
||||
if (_engine && _engine->parent() == this) {
|
||||
delete _engine;
|
||||
}
|
||||
AttributeRegistry::getInstance()->configureScriptEngine(_engine = engine);
|
||||
_parametersString = engine->toStringHandle("parameters");
|
||||
_lengthString = engine->toStringHandle("length");
|
||||
_nameString = engine->toStringHandle("name");
|
||||
_typeString = engine->toStringHandle("type");
|
||||
_generatorString = engine->toStringHandle("generator");
|
||||
}
|
||||
|
||||
QSharedPointer<NetworkProgram> ScriptCache::getProgram(const QUrl& url) {
|
||||
QSharedPointer<NetworkProgram> program = _networkPrograms.value(url);
|
||||
if (program.isNull()) {
|
||||
program = QSharedPointer<NetworkProgram>(new NetworkProgram(this, url));
|
||||
_networkPrograms.insert(url, program);
|
||||
}
|
||||
return program;
|
||||
}
|
||||
|
||||
QSharedPointer<NetworkValue> ScriptCache::getValue(const ParameterizedURL& url) {
|
||||
QSharedPointer<NetworkValue> value = _networkValues.value(url);
|
||||
if (value.isNull()) {
|
||||
value = QSharedPointer<NetworkValue>(url.getParameters().isEmpty() ?
|
||||
(NetworkValue*)new RootNetworkValue(getProgram(url.getURL())) :
|
||||
(NetworkValue*)new DerivedNetworkValue(getValue(url.getURL()), url.getParameters()));
|
||||
_networkValues.insert(url, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
NetworkProgram::NetworkProgram(ScriptCache* cache, const QUrl& url) :
|
||||
_cache(cache),
|
||||
_request(url),
|
||||
_reply(NULL),
|
||||
_attempts(0) {
|
||||
|
||||
if (!url.isValid()) {
|
||||
return;
|
||||
}
|
||||
_request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
||||
makeRequest();
|
||||
}
|
||||
|
||||
NetworkProgram::~NetworkProgram() {
|
||||
if (_reply != NULL) {
|
||||
delete _reply;
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkProgram::makeRequest() {
|
||||
QNetworkAccessManager* manager = _cache->getNetworkAccessManager();
|
||||
if (manager == NULL) {
|
||||
return;
|
||||
}
|
||||
_reply = manager->get(_request);
|
||||
|
||||
connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64)));
|
||||
connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError()));
|
||||
}
|
||||
|
||||
void NetworkProgram::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
||||
if (bytesReceived < bytesTotal && !_reply->isFinished()) {
|
||||
return;
|
||||
}
|
||||
_program = QScriptProgram(QTextStream(_reply).readAll(), _reply->url().toString());
|
||||
|
||||
_reply->disconnect(this);
|
||||
_reply->deleteLater();
|
||||
_reply = NULL;
|
||||
|
||||
emit loaded();
|
||||
}
|
||||
|
||||
void NetworkProgram::handleReplyError() {
|
||||
QDebug debug = qDebug() << _reply->errorString();
|
||||
|
||||
_reply->disconnect(this);
|
||||
_reply->deleteLater();
|
||||
_reply = NULL;
|
||||
|
||||
// retry with increasing delays
|
||||
const int MAX_ATTEMPTS = 8;
|
||||
const int BASE_DELAY_MS = 1000;
|
||||
if (++_attempts < MAX_ATTEMPTS) {
|
||||
QTimer::singleShot(BASE_DELAY_MS * (int)pow(2.0, _attempts), this, SLOT(makeRequest()));
|
||||
debug << " -- retrying...";
|
||||
}
|
||||
}
|
||||
|
||||
NetworkValue::~NetworkValue() {
|
||||
}
|
||||
|
||||
RootNetworkValue::RootNetworkValue(const QSharedPointer<NetworkProgram>& program) :
|
||||
_program(program) {
|
||||
}
|
||||
|
||||
QScriptValue& RootNetworkValue::getValue() {
|
||||
if (!_value.isValid() && _program->isLoaded()) {
|
||||
_value = _program->getCache()->getEngine()->evaluate(_program->getProgram());
|
||||
}
|
||||
return _value;
|
||||
}
|
||||
|
||||
const QList<ParameterInfo>& RootNetworkValue::getParameterInfo() {
|
||||
if (isLoaded() && _parameterInfo.isEmpty()) {
|
||||
ScriptCache* cache = _program->getCache();
|
||||
QScriptEngine* engine = cache->getEngine();
|
||||
QScriptValue parameters = _value.property(cache->getParametersString());
|
||||
if (parameters.isArray()) {
|
||||
int length = parameters.property(cache->getLengthString()).toInt32();
|
||||
for (int i = 0; i < length; i++) {
|
||||
QScriptValue parameter = parameters.property(i);
|
||||
ParameterInfo info = { engine->toStringHandle(parameter.property(cache->getNameString()).toString()),
|
||||
QMetaType::type(parameter.property(cache->getTypeString()).toString().toUtf8().constData()) };
|
||||
_parameterInfo.append(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
return _parameterInfo;
|
||||
}
|
||||
|
||||
DerivedNetworkValue::DerivedNetworkValue(const QSharedPointer<NetworkValue>& baseValue, const ScriptHash& parameters) :
|
||||
_baseValue(baseValue),
|
||||
_parameters(parameters) {
|
||||
}
|
||||
|
||||
QScriptValue& DerivedNetworkValue::getValue() {
|
||||
if (!_value.isValid() && _baseValue->isLoaded()) {
|
||||
RootNetworkValue* root = static_cast<RootNetworkValue*>(_baseValue.data());
|
||||
ScriptCache* cache = root->getProgram()->getCache();
|
||||
QScriptValue generator = _baseValue->getValue().property(cache->getGeneratorString());
|
||||
if (generator.isFunction()) {
|
||||
QScriptValueList arguments;
|
||||
foreach (const ParameterInfo& parameter, root->getParameterInfo()) {
|
||||
arguments.append(cache->getEngine()->newVariant(_parameters.value(parameter.name)));
|
||||
}
|
||||
_value = generator.call(QScriptValue(), arguments);
|
||||
|
||||
} else {
|
||||
_value = _baseValue->getValue();
|
||||
}
|
||||
}
|
||||
return _value;
|
||||
}
|
159
libraries/metavoxels/src/ScriptCache.h
Normal file
159
libraries/metavoxels/src/ScriptCache.h
Normal file
|
@ -0,0 +1,159 @@
|
|||
//
|
||||
// ScriptCache.h
|
||||
// metavoxels
|
||||
//
|
||||
// Created by Andrzej Kapolka on 2/4/14.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__ScriptCache__
|
||||
#define __interface__ScriptCache__
|
||||
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
#include <QNetworkRequest>
|
||||
#include <QObject>
|
||||
#include <QScriptProgram>
|
||||
#include <QScriptValue>
|
||||
#include <QSharedPointer>
|
||||
#include <QWeakPointer>
|
||||
|
||||
#include "MetavoxelUtil.h"
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
class QScriptEngine;
|
||||
|
||||
class NetworkProgram;
|
||||
class NetworkValue;
|
||||
|
||||
/// Maintains a cache of loaded scripts.
|
||||
class ScriptCache : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
static ScriptCache* getInstance();
|
||||
|
||||
ScriptCache();
|
||||
|
||||
void setNetworkAccessManager(QNetworkAccessManager* manager) { _networkAccessManager = manager; }
|
||||
QNetworkAccessManager* getNetworkAccessManager() const { return _networkAccessManager; }
|
||||
|
||||
void setEngine(QScriptEngine* engine);
|
||||
QScriptEngine* getEngine() const { return _engine; }
|
||||
|
||||
/// Loads a script program from the specified URL.
|
||||
QSharedPointer<NetworkProgram> getProgram(const QUrl& url);
|
||||
|
||||
/// Loads a script value from the specified URL.
|
||||
QSharedPointer<NetworkValue> getValue(const ParameterizedURL& url);
|
||||
|
||||
const QScriptString& getParametersString() const { return _parametersString; }
|
||||
const QScriptString& getLengthString() const { return _lengthString; }
|
||||
const QScriptString& getNameString() const { return _nameString; }
|
||||
const QScriptString& getTypeString() const { return _typeString; }
|
||||
const QScriptString& getGeneratorString() const { return _generatorString; }
|
||||
|
||||
private:
|
||||
|
||||
QNetworkAccessManager* _networkAccessManager;
|
||||
QScriptEngine* _engine;
|
||||
QHash<QUrl, QWeakPointer<NetworkProgram> > _networkPrograms;
|
||||
QHash<ParameterizedURL, QWeakPointer<NetworkValue> > _networkValues;
|
||||
QScriptString _parametersString;
|
||||
QScriptString _lengthString;
|
||||
QScriptString _nameString;
|
||||
QScriptString _typeString;
|
||||
QScriptString _generatorString;
|
||||
};
|
||||
|
||||
/// A program loaded from the network.
|
||||
class NetworkProgram : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
NetworkProgram(ScriptCache* cache, const QUrl& url);
|
||||
~NetworkProgram();
|
||||
|
||||
ScriptCache* getCache() const { return _cache; }
|
||||
|
||||
bool isLoaded() const { return !_program.isNull(); }
|
||||
|
||||
const QScriptProgram& getProgram() const { return _program; }
|
||||
|
||||
signals:
|
||||
|
||||
void loaded();
|
||||
|
||||
private slots:
|
||||
|
||||
void makeRequest();
|
||||
void handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||
void handleReplyError();
|
||||
|
||||
private:
|
||||
|
||||
ScriptCache* _cache;
|
||||
QNetworkRequest _request;
|
||||
QNetworkReply* _reply;
|
||||
int _attempts;
|
||||
QScriptProgram _program;
|
||||
};
|
||||
|
||||
/// Abstract base class of values loaded from the network.
|
||||
class NetworkValue {
|
||||
public:
|
||||
|
||||
virtual ~NetworkValue();
|
||||
|
||||
bool isLoaded() { return getValue().isValid(); }
|
||||
|
||||
virtual QScriptValue& getValue() = 0;
|
||||
|
||||
protected:
|
||||
|
||||
QScriptValue _value;
|
||||
};
|
||||
|
||||
/// Contains information about a script parameter.
|
||||
class ParameterInfo {
|
||||
public:
|
||||
QScriptString name;
|
||||
int type;
|
||||
};
|
||||
|
||||
/// The direct result of running a program.
|
||||
class RootNetworkValue : public NetworkValue {
|
||||
public:
|
||||
|
||||
RootNetworkValue(const QSharedPointer<NetworkProgram>& program);
|
||||
|
||||
const QSharedPointer<NetworkProgram>& getProgram() const { return _program; }
|
||||
|
||||
virtual QScriptValue& getValue();
|
||||
|
||||
const QList<ParameterInfo>& getParameterInfo();
|
||||
|
||||
private:
|
||||
|
||||
QSharedPointer<NetworkProgram> _program;
|
||||
QList<ParameterInfo> _parameterInfo;
|
||||
};
|
||||
|
||||
/// The result of running a program's generator using a set of arguments.
|
||||
class DerivedNetworkValue : public NetworkValue {
|
||||
public:
|
||||
|
||||
DerivedNetworkValue(const QSharedPointer<NetworkValue>& baseValue, const ScriptHash& parameters);
|
||||
|
||||
virtual QScriptValue& getValue();
|
||||
|
||||
private:
|
||||
|
||||
QSharedPointer<NetworkValue> _baseValue;
|
||||
ScriptHash _parameters;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__ScriptCache__) */
|
228
libraries/metavoxels/src/SharedObject.cpp
Normal file
228
libraries/metavoxels/src/SharedObject.cpp
Normal file
|
@ -0,0 +1,228 @@
|
|||
//
|
||||
// SharedObject.cpp
|
||||
// metavoxels
|
||||
//
|
||||
// Created by Andrzej Kapolka on 2/5/14.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QFormLayout>
|
||||
#include <QItemEditorFactory>
|
||||
#include <QMetaProperty>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "Bitstream.h"
|
||||
#include "SharedObject.h"
|
||||
|
||||
SharedObject::SharedObject() : _referenceCount(0) {
|
||||
}
|
||||
|
||||
void SharedObject::incrementReferenceCount() {
|
||||
_referenceCount++;
|
||||
}
|
||||
|
||||
void SharedObject::decrementReferenceCount() {
|
||||
if (--_referenceCount == 0) {
|
||||
delete this;
|
||||
|
||||
} else if (_referenceCount == 1) {
|
||||
emit referenceCountDroppedToOne();
|
||||
}
|
||||
}
|
||||
|
||||
SharedObject* SharedObject::clone() const {
|
||||
// default behavior is to make a copy using the no-arg constructor and copy the stored properties
|
||||
const QMetaObject* metaObject = this->metaObject();
|
||||
SharedObject* newObject = static_cast<SharedObject*>(metaObject->newInstance());
|
||||
for (int i = 0; i < metaObject->propertyCount(); i++) {
|
||||
QMetaProperty property = metaObject->property(i);
|
||||
if (property.isStored()) {
|
||||
property.write(newObject, property.read(this));
|
||||
}
|
||||
}
|
||||
foreach (const QByteArray& propertyName, dynamicPropertyNames()) {
|
||||
newObject->setProperty(propertyName, property(propertyName));
|
||||
}
|
||||
return newObject;
|
||||
}
|
||||
|
||||
bool SharedObject::equals(const SharedObject* other) const {
|
||||
// default behavior is to compare the properties
|
||||
const QMetaObject* metaObject = this->metaObject();
|
||||
if (metaObject != other->metaObject()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < metaObject->propertyCount(); i++) {
|
||||
QMetaProperty property = metaObject->property(i);
|
||||
if (property.isStored() && property.read(this) != property.read(other)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
QList<QByteArray> dynamicPropertyNames = this->dynamicPropertyNames();
|
||||
if (dynamicPropertyNames.size() != other->dynamicPropertyNames().size()) {
|
||||
return false;
|
||||
}
|
||||
foreach (const QByteArray& propertyName, dynamicPropertyNames) {
|
||||
if (property(propertyName) != other->property(propertyName)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SharedObjectPointer::SharedObjectPointer(SharedObject* data) : _data(data) {
|
||||
if (_data) {
|
||||
_data->incrementReferenceCount();
|
||||
}
|
||||
}
|
||||
|
||||
SharedObjectPointer::SharedObjectPointer(const SharedObjectPointer& other) : _data(other._data) {
|
||||
if (_data) {
|
||||
_data->incrementReferenceCount();
|
||||
}
|
||||
}
|
||||
|
||||
SharedObjectPointer::~SharedObjectPointer() {
|
||||
if (_data) {
|
||||
_data->decrementReferenceCount();
|
||||
}
|
||||
}
|
||||
|
||||
void SharedObjectPointer::detach() {
|
||||
if (_data && _data->getReferenceCount() > 1) {
|
||||
_data->decrementReferenceCount();
|
||||
(_data = _data->clone())->incrementReferenceCount();
|
||||
}
|
||||
}
|
||||
|
||||
void SharedObjectPointer::reset() {
|
||||
if (_data) {
|
||||
_data->decrementReferenceCount();
|
||||
}
|
||||
_data = NULL;
|
||||
}
|
||||
|
||||
SharedObjectPointer& SharedObjectPointer::operator=(SharedObject* data) {
|
||||
if (_data) {
|
||||
_data->decrementReferenceCount();
|
||||
}
|
||||
if ((_data = data)) {
|
||||
_data->incrementReferenceCount();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
SharedObjectPointer& SharedObjectPointer::operator=(const SharedObjectPointer& other) {
|
||||
if (_data) {
|
||||
_data->decrementReferenceCount();
|
||||
}
|
||||
if ((_data = other._data)) {
|
||||
_data->incrementReferenceCount();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint qHash(const SharedObjectPointer& pointer, uint seed) {
|
||||
return qHash(pointer.data(), seed);
|
||||
}
|
||||
|
||||
SharedObjectEditor::SharedObjectEditor(const QMetaObject* metaObject, QWidget* parent) : QWidget(parent) {
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
layout->setAlignment(Qt::AlignTop);
|
||||
setLayout(layout);
|
||||
|
||||
QFormLayout* form = new QFormLayout();
|
||||
layout->addLayout(form);
|
||||
|
||||
form->addRow("Type:", _type = new QComboBox());
|
||||
_type->addItem("(none)");
|
||||
foreach (const QMetaObject* metaObject, Bitstream::getMetaObjectSubClasses(metaObject)) {
|
||||
_type->addItem(metaObject->className(), QVariant::fromValue(metaObject));
|
||||
}
|
||||
connect(_type, SIGNAL(currentIndexChanged(int)), SLOT(updateType()));
|
||||
}
|
||||
|
||||
void SharedObjectEditor::setObject(const SharedObjectPointer& object) {
|
||||
_object = object;
|
||||
const QMetaObject* metaObject = object ? object->metaObject() : NULL;
|
||||
int index = _type->findData(QVariant::fromValue(metaObject));
|
||||
if (index != -1) {
|
||||
// ensure that we call updateType to obtain the values
|
||||
if (_type->currentIndex() == index) {
|
||||
updateType();
|
||||
} else {
|
||||
_type->setCurrentIndex(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const QMetaObject* getOwningAncestor(const QMetaObject* metaObject, int propertyIndex) {
|
||||
while (propertyIndex < metaObject->propertyOffset()) {
|
||||
metaObject = metaObject->superClass();
|
||||
}
|
||||
return metaObject;
|
||||
}
|
||||
|
||||
void SharedObjectEditor::updateType() {
|
||||
// delete the existing rows
|
||||
if (layout()->count() > 1) {
|
||||
QFormLayout* form = static_cast<QFormLayout*>(layout()->takeAt(1));
|
||||
while (!form->isEmpty()) {
|
||||
QLayoutItem* item = form->takeAt(0);
|
||||
if (item->widget()) {
|
||||
delete item->widget();
|
||||
}
|
||||
delete item;
|
||||
}
|
||||
delete form;
|
||||
}
|
||||
const QMetaObject* metaObject = _type->itemData(_type->currentIndex()).value<const QMetaObject*>();
|
||||
if (metaObject == NULL) {
|
||||
_object.reset();
|
||||
return;
|
||||
}
|
||||
QObject* oldObject = static_cast<SharedObject*>(_object.data());
|
||||
const QMetaObject* oldMetaObject = oldObject ? oldObject->metaObject() : NULL;
|
||||
QObject* newObject = metaObject->newInstance();
|
||||
|
||||
QFormLayout* form = new QFormLayout();
|
||||
static_cast<QVBoxLayout*>(layout())->addLayout(form);
|
||||
for (int i = QObject::staticMetaObject.propertyCount(); i < metaObject->propertyCount(); i++) {
|
||||
QMetaProperty property = metaObject->property(i);
|
||||
if (oldMetaObject && i < oldMetaObject->propertyCount() &&
|
||||
getOwningAncestor(metaObject, i) == getOwningAncestor(oldMetaObject, i)) {
|
||||
// copy the state of the shared ancestry
|
||||
property.write(newObject, property.read(oldObject));
|
||||
}
|
||||
QWidget* widget = QItemEditorFactory::defaultFactory()->createEditor(property.userType(), NULL);
|
||||
if (widget) {
|
||||
widget->setProperty("propertyIndex", i);
|
||||
form->addRow(QByteArray(property.name()) + ':', widget);
|
||||
QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(property.userType());
|
||||
const QMetaObject* widgetMetaObject = widget->metaObject();
|
||||
QMetaProperty widgetProperty = widgetMetaObject->property(widgetMetaObject->indexOfProperty(valuePropertyName));
|
||||
widgetProperty.write(widget, property.read(newObject));
|
||||
if (widgetProperty.hasNotifySignal()) {
|
||||
connect(widget, QByteArray(SIGNAL()).append(widgetProperty.notifySignal().methodSignature()),
|
||||
SLOT(propertyChanged()));
|
||||
}
|
||||
}
|
||||
}
|
||||
_object = static_cast<SharedObject*>(newObject);
|
||||
}
|
||||
|
||||
void SharedObjectEditor::propertyChanged() {
|
||||
QFormLayout* form = static_cast<QFormLayout*>(layout()->itemAt(1));
|
||||
for (int i = 0; i < form->rowCount(); i++) {
|
||||
QWidget* widget = form->itemAt(i, QFormLayout::FieldRole)->widget();
|
||||
if (widget != sender()) {
|
||||
continue;
|
||||
}
|
||||
_object.detach();
|
||||
QObject* object = _object.data();
|
||||
QMetaProperty property = object->metaObject()->property(widget->property("propertyIndex").toInt());
|
||||
QByteArray valuePropertyName = QItemEditorFactory::defaultFactory()->valuePropertyName(property.userType());
|
||||
property.write(object, widget->property(valuePropertyName));
|
||||
}
|
||||
}
|
115
libraries/metavoxels/src/SharedObject.h
Normal file
115
libraries/metavoxels/src/SharedObject.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
//
|
||||
// SharedObject.h
|
||||
// metavoxels
|
||||
//
|
||||
// Created by Andrzej Kapolka on 2/5/14.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__SharedObject__
|
||||
#define __interface__SharedObject__
|
||||
|
||||
#include <QMetaType>
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
|
||||
class QComboBox;
|
||||
|
||||
/// A QObject that may be shared over the network.
|
||||
class SharedObject : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
SharedObject();
|
||||
|
||||
int getReferenceCount() const { return _referenceCount; }
|
||||
void incrementReferenceCount();
|
||||
void decrementReferenceCount();
|
||||
|
||||
/// Creates a new clone of this object.
|
||||
virtual SharedObject* clone() const;
|
||||
|
||||
/// Tests this object for equality with another.
|
||||
virtual bool equals(const SharedObject* other) const;
|
||||
|
||||
signals:
|
||||
|
||||
/// Emitted when the reference count drops to one.
|
||||
void referenceCountDroppedToOne();
|
||||
|
||||
private:
|
||||
|
||||
int _referenceCount;
|
||||
};
|
||||
|
||||
/// A pointer to a shared object.
|
||||
class SharedObjectPointer {
|
||||
public:
|
||||
|
||||
SharedObjectPointer(SharedObject* data = NULL);
|
||||
SharedObjectPointer(const SharedObjectPointer& other);
|
||||
~SharedObjectPointer();
|
||||
|
||||
SharedObject* data() { return _data; }
|
||||
const SharedObject* data() const { return _data; }
|
||||
const SharedObject* constData() const { return _data; }
|
||||
|
||||
void detach();
|
||||
|
||||
void swap(SharedObjectPointer& other) { qSwap(_data, other._data); }
|
||||
|
||||
void reset();
|
||||
|
||||
operator SharedObject*() { return _data; }
|
||||
operator const SharedObject*() const { return _data; }
|
||||
|
||||
bool operator!() const { return !_data; }
|
||||
|
||||
bool operator!=(const SharedObjectPointer& other) const { return _data != other._data; }
|
||||
|
||||
SharedObject& operator*() { return *_data; }
|
||||
const SharedObject& operator*() const { return *_data; }
|
||||
|
||||
SharedObject* operator->() { return _data; }
|
||||
const SharedObject* operator->() const { return _data; }
|
||||
|
||||
SharedObjectPointer& operator=(SharedObject* data);
|
||||
SharedObjectPointer& operator=(const SharedObjectPointer& other);
|
||||
|
||||
bool operator==(const SharedObjectPointer& other) const { return _data == other._data; }
|
||||
|
||||
private:
|
||||
|
||||
SharedObject* _data;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(SharedObjectPointer)
|
||||
|
||||
uint qHash(const SharedObjectPointer& pointer, uint seed = 0);
|
||||
|
||||
/// Allows editing shared object instances.
|
||||
class SharedObjectEditor : public QWidget {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(SharedObjectPointer object MEMBER _object WRITE setObject USER true)
|
||||
|
||||
public:
|
||||
|
||||
SharedObjectEditor(const QMetaObject* metaObject, QWidget* parent);
|
||||
|
||||
public slots:
|
||||
|
||||
void setObject(const SharedObjectPointer& object);
|
||||
|
||||
private slots:
|
||||
|
||||
void updateType();
|
||||
void propertyChanged();
|
||||
|
||||
private:
|
||||
|
||||
QComboBox* _type;
|
||||
SharedObjectPointer _object;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__SharedObject__) */
|
|
@ -224,4 +224,4 @@ private:
|
|||
static quint64 _totalBytesOfRawData;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__OctreePacketData__) */
|
||||
#endif /* defined(__hifi__OctreePacketData__) */
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include <NodeList.h>
|
||||
#include <SharedUtil.h>
|
||||
#include "JurisdictionMap.h"
|
||||
|
||||
#define GREENISH 0x40ff40d0
|
||||
|
|
|
@ -124,7 +124,7 @@ QScriptValue keyEventToScriptValue(QScriptEngine* engine, const KeyEvent& event)
|
|||
return obj;
|
||||
}
|
||||
|
||||
void keyEventFromScriptValue(const QScriptValue &object, KeyEvent& event) {
|
||||
void keyEventFromScriptValue(const QScriptValue& object, KeyEvent& event) {
|
||||
|
||||
event.isValid = false; // assume the worst
|
||||
event.isMeta = object.property("isMeta").toVariant().toBool();
|
||||
|
@ -231,7 +231,7 @@ QScriptValue mouseEventToScriptValue(QScriptEngine* engine, const MouseEvent& ev
|
|||
return obj;
|
||||
}
|
||||
|
||||
void mouseEventFromScriptValue(const QScriptValue &object, MouseEvent& event) {
|
||||
void mouseEventFromScriptValue(const QScriptValue& object, MouseEvent& event) {
|
||||
// nothing for now...
|
||||
}
|
||||
|
||||
|
@ -242,7 +242,7 @@ QScriptValue touchEventToScriptValue(QScriptEngine* engine, const TouchEvent& ev
|
|||
return obj;
|
||||
}
|
||||
|
||||
void touchEventFromScriptValue(const QScriptValue &object, TouchEvent& event) {
|
||||
void touchEventFromScriptValue(const QScriptValue& object, TouchEvent& event) {
|
||||
// nothing for now...
|
||||
}
|
||||
|
||||
|
@ -253,6 +253,8 @@ QScriptValue wheelEventToScriptValue(QScriptEngine* engine, const WheelEvent& ev
|
|||
return obj;
|
||||
}
|
||||
|
||||
void wheelEventFromScriptValue(const QScriptValue &object, WheelEvent& event) {
|
||||
void wheelEventFromScriptValue(const QScriptValue& object, WheelEvent& event) {
|
||||
// nothing for now...
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -65,7 +65,6 @@ public:
|
|||
int y;
|
||||
};
|
||||
|
||||
|
||||
Q_DECLARE_METATYPE(KeyEvent)
|
||||
Q_DECLARE_METATYPE(MouseEvent)
|
||||
Q_DECLARE_METATYPE(TouchEvent)
|
||||
|
@ -74,15 +73,15 @@ Q_DECLARE_METATYPE(WheelEvent)
|
|||
void registerEventTypes(QScriptEngine* engine);
|
||||
|
||||
QScriptValue keyEventToScriptValue(QScriptEngine* engine, const KeyEvent& event);
|
||||
void keyEventFromScriptValue(const QScriptValue &object, KeyEvent& event);
|
||||
void keyEventFromScriptValue(const QScriptValue& object, KeyEvent& event);
|
||||
|
||||
QScriptValue mouseEventToScriptValue(QScriptEngine* engine, const MouseEvent& event);
|
||||
void mouseEventFromScriptValue(const QScriptValue &object, MouseEvent& event);
|
||||
void mouseEventFromScriptValue(const QScriptValue& object, MouseEvent& event);
|
||||
|
||||
QScriptValue touchEventToScriptValue(QScriptEngine* engine, const TouchEvent& event);
|
||||
void touchEventFromScriptValue(const QScriptValue &object, TouchEvent& event);
|
||||
void touchEventFromScriptValue(const QScriptValue& object, TouchEvent& event);
|
||||
|
||||
QScriptValue wheelEventToScriptValue(QScriptEngine* engine, const WheelEvent& event);
|
||||
void wheelEventFromScriptValue(const QScriptValue &object, WheelEvent& event);
|
||||
void wheelEventFromScriptValue(const QScriptValue& object, WheelEvent& event);
|
||||
|
||||
#endif // __hifi_EventTypes_h__
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
//
|
||||
//
|
||||
|
||||
#include <OctreeConstants.h>
|
||||
#include "Quat.h"
|
||||
|
||||
glm::quat Quat::multiply(const glm::quat& q1, const glm::quat& q2) {
|
||||
|
@ -18,3 +19,19 @@ glm::quat Quat::multiply(const glm::quat& q1, const glm::quat& q2) {
|
|||
glm::quat Quat::fromVec3(const glm::vec3& vec3) {
|
||||
return glm::quat(vec3);
|
||||
}
|
||||
|
||||
glm::quat Quat::fromPitchYawRoll(float pitch, float yaw, float roll) {
|
||||
return glm::quat(glm::radians(glm::vec3(pitch, yaw, roll)));
|
||||
}
|
||||
|
||||
glm::vec3 Quat::getFront(const glm::quat& orientation) {
|
||||
return orientation * IDENTITY_FRONT;
|
||||
}
|
||||
|
||||
glm::vec3 Quat::getRight(const glm::quat& orientation) {
|
||||
return orientation * IDENTITY_RIGHT;
|
||||
}
|
||||
|
||||
glm::vec3 Quat::getUp(const glm::quat& orientation) {
|
||||
return orientation * IDENTITY_UP;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,10 @@ class Quat : public QObject {
|
|||
public slots:
|
||||
glm::quat multiply(const glm::quat& q1, const glm::quat& q2);
|
||||
glm::quat fromVec3(const glm::vec3& vec3);
|
||||
glm::quat fromPitchYawRoll(float pitch, float yaw, float roll);
|
||||
glm::vec3 getFront(const glm::quat& orientation);
|
||||
glm::vec3 getRight(const glm::quat& orientation);
|
||||
glm::vec3 getUp(const glm::quat& orientation);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -138,6 +138,7 @@ void ScriptEngine::init() {
|
|||
registerGlobalObject("Controller", _controllerScriptingInterface);
|
||||
registerGlobalObject("Particles", &_particlesScriptingInterface);
|
||||
registerGlobalObject("Quat", &_quatLibrary);
|
||||
registerGlobalObject("Vec3", &_vec3Library);
|
||||
|
||||
registerGlobalObject("Voxels", &_voxelsScriptingInterface);
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ class ParticlesScriptingInterface;
|
|||
|
||||
#include "AbstractControllerScriptingInterface.h"
|
||||
#include "Quat.h"
|
||||
#include "Vec3.h"
|
||||
|
||||
const QString NO_SCRIPT("");
|
||||
|
||||
|
@ -100,6 +101,7 @@ private:
|
|||
AbstractMenuInterface* _menu;
|
||||
static int _scriptNumber;
|
||||
Quat _quatLibrary;
|
||||
Vec3 _vec3Library;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__ScriptEngine__) */
|
||||
|
|
24
libraries/script-engine/src/Vec3.cpp
Normal file
24
libraries/script-engine/src/Vec3.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// Vec3.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 1/29/14
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Scriptable Vec3 class library.
|
||||
//
|
||||
//
|
||||
|
||||
#include "Vec3.h"
|
||||
|
||||
glm::vec3 Vec3::multiply(const glm::vec3& v1, const glm::vec3& v2) {
|
||||
return v1 * v2;
|
||||
}
|
||||
|
||||
glm::vec3 Vec3::multiply(const glm::vec3& v1, float f) {
|
||||
return v1 * f;
|
||||
}
|
||||
|
||||
glm::vec3 Vec3::sum(const glm::vec3& v1, const glm::vec3& v2) {
|
||||
return v1 + v2;
|
||||
}
|
31
libraries/script-engine/src/Vec3.h
Normal file
31
libraries/script-engine/src/Vec3.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// Vec3.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 1/29/14
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Scriptable Vec3 class library.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef __hifi__Vec3__
|
||||
#define __hifi__Vec3__
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <QtCore/QObject>
|
||||
|
||||
/// Scriptable interface a Vec3ernion helper class object. Used exclusively in the JavaScript API
|
||||
class Vec3 : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public slots:
|
||||
glm::vec3 multiply(const glm::vec3& v1, const glm::vec3& v2);
|
||||
glm::vec3 multiply(const glm::vec3& v1, float f);
|
||||
glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* defined(__hifi__Vec3__) */
|
|
@ -126,4 +126,4 @@ void Logging::verboseMessageHandler(QtMsgType type, const QMessageLogContext& co
|
|||
}
|
||||
|
||||
fprintf(stdout, "%s %s\n", prefixString.toLocal8Bit().constData(), message.toLocal8Bit().constData());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,4 +48,4 @@ NetworkPacket& NetworkPacket::operator=(NetworkPacket&& other) {
|
|||
copyContents(other.getDestinationNode(), other.getByteArray());
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include "HifiSockAddr.h"
|
||||
|
||||
#include "NodeList.h" // for MAX_PACKET_SIZE
|
||||
#include "SharedUtil.h" // for MAX_PACKET_SIZE
|
||||
|
||||
/// Storage of not-yet processed inbound, or not yet sent outbound generic UDP network packet
|
||||
class NetworkPacket {
|
||||
|
|
|
@ -30,8 +30,6 @@
|
|||
|
||||
#include "Node.h"
|
||||
|
||||
const int MAX_PACKET_SIZE = 1500;
|
||||
|
||||
const quint64 NODE_SILENCE_THRESHOLD_USECS = 2 * 1000 * 1000;
|
||||
const quint64 DOMAIN_SERVER_CHECK_IN_USECS = 1 * 1000000;
|
||||
const quint64 PING_INACTIVE_NODE_INTERVAL_USECS = 1 * 1000 * 1000;
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <string.h>
|
||||
#include <QString>
|
||||
|
||||
const int BITS_IN_BYTE = 8;
|
||||
const int BITS_IN_OCTAL = 3;
|
||||
const int NUMBER_OF_COLORS = 3; // RGB!
|
||||
const int SIZE_OF_COLOR_DATA = NUMBER_OF_COLORS * sizeof(unsigned char); // size in bytes
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "GenericThread.h"
|
||||
#include "NetworkPacket.h"
|
||||
#include "NodeList.h"
|
||||
#include "SharedUtil.h"
|
||||
|
||||
/// Generalized threaded processor for queueing and sending of outbound packets.
|
||||
|
|
|
@ -15,6 +15,7 @@ void registerMetaTypes(QScriptEngine* engine) {
|
|||
qScriptRegisterMetaType(engine, vec2toScriptValue, vec2FromScriptValue);
|
||||
qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, xColorToScriptValue, xColorFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, pickRayToScriptValue, pickRayFromScriptValue);
|
||||
}
|
||||
|
||||
QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3) {
|
||||
|
@ -73,3 +74,28 @@ void xColorFromScriptValue(const QScriptValue &object, xColor& color) {
|
|||
color.blue = object.property("blue").toVariant().toInt();
|
||||
}
|
||||
|
||||
QScriptValue pickRayToScriptValue(QScriptEngine* engine, const PickRay& pickRay) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
QScriptValue origin = vec3toScriptValue(engine, pickRay.origin);
|
||||
obj.setProperty("origin", origin);
|
||||
QScriptValue direction = vec3toScriptValue(engine, pickRay.direction);
|
||||
obj.setProperty("direction", direction);
|
||||
return obj;
|
||||
|
||||
}
|
||||
|
||||
void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay) {
|
||||
QScriptValue originValue = object.property("origin");
|
||||
if (originValue.isValid()) {
|
||||
pickRay.origin.x = originValue.property("x").toVariant().toFloat();
|
||||
pickRay.origin.y = originValue.property("y").toVariant().toFloat();
|
||||
pickRay.origin.z = originValue.property("z").toVariant().toFloat();
|
||||
}
|
||||
QScriptValue directionValue = object.property("direction");
|
||||
if (directionValue.isValid()) {
|
||||
pickRay.direction.x = directionValue.property("x").toVariant().toFloat();
|
||||
pickRay.direction.y = directionValue.property("y").toVariant().toFloat();
|
||||
pickRay.direction.z = directionValue.property("z").toVariant().toFloat();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@ Q_DECLARE_METATYPE(glm::vec2)
|
|||
Q_DECLARE_METATYPE(glm::quat)
|
||||
Q_DECLARE_METATYPE(xColor)
|
||||
|
||||
|
||||
void registerMetaTypes(QScriptEngine* engine);
|
||||
|
||||
QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3);
|
||||
|
@ -37,4 +36,15 @@ void quatFromScriptValue(const QScriptValue &object, glm::quat& quat);
|
|||
QScriptValue xColorToScriptValue(QScriptEngine* engine, const xColor& color);
|
||||
void xColorFromScriptValue(const QScriptValue &object, xColor& color);
|
||||
|
||||
class PickRay {
|
||||
public:
|
||||
PickRay() : origin(0), direction(0) { };
|
||||
glm::vec3 origin;
|
||||
glm::vec3 direction;
|
||||
};
|
||||
Q_DECLARE_METATYPE(PickRay)
|
||||
QScriptValue pickRayToScriptValue(QScriptEngine* engine, const PickRay& pickRay);
|
||||
void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -61,6 +61,10 @@ static const quint64 USECS_PER_MSEC = 1000;
|
|||
static const quint64 MSECS_PER_SECOND = 1000;
|
||||
static const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND;
|
||||
|
||||
const int BITS_IN_BYTE = 8;
|
||||
|
||||
const int MAX_PACKET_SIZE = 1500;
|
||||
|
||||
quint64 usecTimestamp(const timeval *time);
|
||||
quint64 usecTimestampNow();
|
||||
void usecTimestampNowForceClockSkew(int clockSkew);
|
||||
|
|
Loading…
Reference in a new issue