mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:44:02 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into injected-audio
This commit is contained in:
commit
9c39bcba4c
53 changed files with 2179 additions and 284 deletions
|
@ -9,11 +9,12 @@ set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
|
|||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/")
|
||||
|
||||
find_package(Qt5Network REQUIRED)
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
|
||||
include(${MACRO_DIR}/SetupHifiProject.cmake)
|
||||
setup_hifi_project(${TARGET_NAME} TRUE)
|
||||
|
||||
qt5_use_modules(${TARGET_NAME} Network)
|
||||
qt5_use_modules(${TARGET_NAME} Network Widgets)
|
||||
|
||||
# include glm
|
||||
include(${MACRO_DIR}/IncludeGLM.cmake)
|
||||
|
@ -30,6 +31,7 @@ link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR})
|
|||
link_hifi_library(octree-server ${TARGET_NAME} ${ROOT_DIR})
|
||||
link_hifi_library(particle-server ${TARGET_NAME} ${ROOT_DIR})
|
||||
link_hifi_library(voxel-server ${TARGET_NAME} ${ROOT_DIR})
|
||||
link_hifi_library(script-engine ${TARGET_NAME} ${ROOT_DIR})
|
||||
#testing
|
||||
|
||||
include_directories(${ROOT_DIR}/externals/civetweb/include)
|
||||
|
|
|
@ -24,8 +24,6 @@
|
|||
Agent::Agent(const unsigned char* dataBuffer, int numBytes) :
|
||||
ThreadedAssignment(dataBuffer, numBytes)
|
||||
{
|
||||
_particleScriptingInterface.init();
|
||||
_voxelScriptingInterface.init();
|
||||
}
|
||||
|
||||
void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
|
||||
|
@ -34,12 +32,12 @@ void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr&
|
|||
// PACKET_TYPE_JURISDICTION, first byte is the node type...
|
||||
switch (dataByteArray[headerBytes]) {
|
||||
case NODE_TYPE_VOXEL_SERVER:
|
||||
_voxelScriptingInterface.getJurisdictionListener()->queueReceivedPacket(senderSockAddr,
|
||||
_scriptEngine.getVoxelScriptingInterface()->getJurisdictionListener()->queueReceivedPacket(senderSockAddr,
|
||||
(unsigned char*) dataByteArray.data(),
|
||||
dataByteArray.size());
|
||||
break;
|
||||
case NODE_TYPE_PARTICLE_SERVER:
|
||||
_particleScriptingInterface.getJurisdictionListener()->queueReceivedPacket(senderSockAddr,
|
||||
_scriptEngine.getParticleScriptingInterface()->getJurisdictionListener()->queueReceivedPacket(senderSockAddr,
|
||||
(unsigned char*) dataByteArray.data(),
|
||||
dataByteArray.size());
|
||||
break;
|
||||
|
@ -76,43 +74,12 @@ void Agent::run() {
|
|||
loop.exec();
|
||||
|
||||
QString scriptContents(reply->readAll());
|
||||
QScriptEngine engine;
|
||||
|
||||
// register meta-type for glm::vec3 conversions
|
||||
registerMetaTypes(&engine);
|
||||
|
||||
QScriptValue agentValue = engine.newQObject(this);
|
||||
engine.globalObject().setProperty("Agent", agentValue);
|
||||
|
||||
QScriptValue voxelScripterValue = engine.newQObject(&_voxelScriptingInterface);
|
||||
engine.globalObject().setProperty("Voxels", voxelScripterValue);
|
||||
|
||||
QScriptValue particleScripterValue = engine.newQObject(&_particleScriptingInterface);
|
||||
engine.globalObject().setProperty("Particles", particleScripterValue);
|
||||
|
||||
QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE));
|
||||
engine.globalObject().setProperty("TREE_SCALE", treeScaleValue);
|
||||
|
||||
const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000;
|
||||
|
||||
// let the VoxelPacketSender know how frequently we plan to call it
|
||||
_voxelScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS);
|
||||
_particleScriptingInterface.getParticlePacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS);
|
||||
|
||||
qDebug() << "Downloaded script:" << scriptContents << "\n";
|
||||
QScriptValue result = engine.evaluate(scriptContents);
|
||||
qDebug() << "Evaluated script.\n";
|
||||
|
||||
if (engine.hasUncaughtException()) {
|
||||
int line = engine.uncaughtExceptionLineNumber();
|
||||
qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n";
|
||||
}
|
||||
|
||||
timeval startTime;
|
||||
gettimeofday(&startTime, NULL);
|
||||
|
||||
int thisFrame = 0;
|
||||
|
||||
QTimer* domainServerTimer = new QTimer(this);
|
||||
connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit()));
|
||||
domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000);
|
||||
|
@ -124,50 +91,7 @@ void Agent::run() {
|
|||
QTimer* pingNodesTimer = new QTimer(this);
|
||||
connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes()));
|
||||
pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000);
|
||||
|
||||
while (!_isFinished) {
|
||||
|
||||
int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * VISUAL_DATA_CALLBACK_USECS) - usecTimestampNow();
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
}
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
bool willSendVisualDataCallBack = false;
|
||||
|
||||
if (_voxelScriptingInterface.getVoxelPacketSender()->voxelServersExist()) {
|
||||
// allow the scripter's call back to setup visual data
|
||||
willSendVisualDataCallBack = true;
|
||||
|
||||
// release the queue of edit voxel messages.
|
||||
_voxelScriptingInterface.getVoxelPacketSender()->releaseQueuedMessages();
|
||||
|
||||
// since we're in non-threaded mode, call process so that the packets are sent
|
||||
_voxelScriptingInterface.getVoxelPacketSender()->process();
|
||||
}
|
||||
|
||||
if (_particleScriptingInterface.getParticlePacketSender()->serversExist()) {
|
||||
// allow the scripter's call back to setup visual data
|
||||
willSendVisualDataCallBack = true;
|
||||
|
||||
// release the queue of edit voxel messages.
|
||||
_particleScriptingInterface.getParticlePacketSender()->releaseQueuedMessages();
|
||||
|
||||
// since we're in non-threaded mode, call process so that the packets are sent
|
||||
_particleScriptingInterface.getParticlePacketSender()->process();
|
||||
}
|
||||
|
||||
if (willSendVisualDataCallBack) {
|
||||
emit willSendVisualDataCallback();
|
||||
}
|
||||
|
||||
|
||||
if (engine.hasUncaughtException()) {
|
||||
int line = engine.uncaughtExceptionLineNumber();
|
||||
qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
_scriptEngine.setScriptContents(scriptContents);
|
||||
_scriptEngine.run();
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <QtCore/QObject>
|
||||
#include <QtCore/QUrl>
|
||||
|
||||
#include <ScriptEngine.h>
|
||||
#include <ThreadedAssignment.h>
|
||||
|
||||
#include <VoxelScriptingInterface.h>
|
||||
|
@ -33,8 +34,7 @@ signals:
|
|||
void willSendAudioDataCallback();
|
||||
void willSendVisualDataCallback();
|
||||
private:
|
||||
VoxelScriptingInterface _voxelScriptingInterface;
|
||||
ParticleScriptingInterface _particleScriptingInterface;
|
||||
ScriptEngine _scriptEngine;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Agent__) */
|
||||
|
|
|
@ -220,12 +220,12 @@ void DomainServer::readAvailableDatagrams() {
|
|||
}
|
||||
} else if (packetData[0] == PACKET_TYPE_REQUEST_ASSIGNMENT) {
|
||||
|
||||
qDebug("Received a request for assignment.\n");
|
||||
|
||||
if (_assignmentQueue.size() > 0) {
|
||||
// construct the requested assignment from the packet data
|
||||
Assignment requestAssignment(packetData, receivedBytes);
|
||||
|
||||
qDebug("Received a request for assignment type %i from %s.\n", requestAssignment.getType(), qPrintable(senderSockAddr.getAddress().toString()));
|
||||
|
||||
Assignment* assignmentToDeploy = deployableAssignmentForRequest(requestAssignment);
|
||||
|
||||
if (assignmentToDeploy) {
|
||||
|
@ -243,6 +243,8 @@ void DomainServer::readAvailableDatagrams() {
|
|||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
qDebug("Received an invalid assignment request from %s.\n", qPrintable(senderSockAddr.getAddress().toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,10 +93,11 @@ include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
|||
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
|
||||
link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR})
|
||||
link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})
|
||||
link_hifi_library(metavoxels ${TARGET_NAME} ${ROOT_DIR})
|
||||
link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR})
|
||||
link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR})
|
||||
link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR})
|
||||
link_hifi_library(scriptengine ${TARGET_NAME} ${ROOT_DIR})
|
||||
link_hifi_library(script-engine ${TARGET_NAME} ${ROOT_DIR})
|
||||
|
||||
# find required libraries
|
||||
find_package(Faceshift)
|
||||
|
@ -135,7 +136,7 @@ if (LIBOVR_FOUND AND NOT DISABLE_LIBOVR)
|
|||
target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES})
|
||||
endif (LIBOVR_FOUND AND NOT DISABLE_LIBOVR)
|
||||
|
||||
qt5_use_modules(${TARGET_NAME} Core Gui Multimedia Network OpenGL Svg WebKit WebKitWidgets)
|
||||
qt5_use_modules(${TARGET_NAME} Core Gui Multimedia Network OpenGL Script Svg WebKit WebKitWidgets)
|
||||
|
||||
# include headers for interface and InterfaceConfig.
|
||||
include_directories(
|
||||
|
|
171
interface/resources/scripts/sphere.js
Normal file
171
interface/resources/scripts/sphere.js
Normal file
|
@ -0,0 +1,171 @@
|
|||
//
|
||||
// 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.attributeValues[normalIndex] = 0x007F00;
|
||||
|
||||
} else {
|
||||
var scale = 127.0 / length;
|
||||
info.attributeValues[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.attributeValues[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.attributeValues[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.attributeValues[colorIndex] = sphereColor;
|
||||
}
|
||||
setNormal(vector);
|
||||
|
||||
} else {
|
||||
if (colorIndex != -1) {
|
||||
info.attributeValues[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 attributes = visitation.visitor.getAttributes();
|
||||
colorIndex = strictIndexOf(attributes, AttributeRegistry.colorAttribute);
|
||||
normalIndex = strictIndexOf(attributes, AttributeRegistry.normalAttribute);
|
||||
visitor = visitation.visitor;
|
||||
info = { attributeValues: new Array(attributes.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);
|
||||
})
|
25
interface/resources/shaders/metavoxel_point.vert
Normal file
25
interface/resources/shaders/metavoxel_point.vert
Normal file
|
@ -0,0 +1,25 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// metavoxel_point.vert
|
||||
// vertex shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 12/12/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
uniform float pointScale;
|
||||
|
||||
void main(void) {
|
||||
|
||||
// standard diffuse lighting
|
||||
gl_FrontColor = vec4(gl_Color.rgb * (gl_LightModel.ambient.rgb + gl_LightSource[0].ambient.rgb +
|
||||
gl_LightSource[0].diffuse.rgb * max(0.0, dot(gl_NormalMatrix * gl_Normal, gl_LightSource[0].position.xyz))),
|
||||
gl_Color.a);
|
||||
|
||||
// extract the first three components of the vertex for position
|
||||
gl_Position = gl_ModelViewProjectionMatrix * vec4(gl_Vertex.xyz, 1.0);
|
||||
|
||||
// the final component is the size in world space
|
||||
gl_PointSize = pointScale * gl_Vertex.w / gl_Position.w;
|
||||
}
|
BIN
interface/resources/styles/Inconsolata.otf
Normal file
BIN
interface/resources/styles/Inconsolata.otf
Normal file
Binary file not shown.
7
interface/resources/styles/log_dialog.qss
Normal file
7
interface/resources/styles/log_dialog.qss
Normal file
|
@ -0,0 +1,7 @@
|
|||
QPlainTextEdit {
|
||||
font-family: Inconsolata, Lucida Console, Andale Mono, Monaco;
|
||||
font-size: 16px;
|
||||
padding-left: 28px;
|
||||
color: #333333;
|
||||
background-color: #FFFFFF;
|
||||
}
|
|
@ -143,11 +143,17 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_pasteMode(false)
|
||||
{
|
||||
_applicationStartupTime = startup_time;
|
||||
|
||||
switchToResourcesParentIfRequired();
|
||||
QFontDatabase::addApplicationFont("resources/styles/Inconsolata.otf");
|
||||
_window->setWindowTitle("Interface");
|
||||
|
||||
qDebug( "[VERSION] Build sequence: %i", BUILD_VERSION);
|
||||
|
||||
|
||||
qInstallMessageHandler(messageHandler);
|
||||
|
||||
// call Menu getInstance static method to set up the menu
|
||||
_window->setMenuBar(Menu::getInstance());
|
||||
|
||||
qDebug("[VERSION] Build sequence: %i", BUILD_VERSION);
|
||||
|
||||
unsigned int listenPort = 0; // bind to an ephemeral port by default
|
||||
const char** constArgv = const_cast<const char**>(argv);
|
||||
|
@ -173,16 +179,9 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
|
||||
// network receive thread and voxel parsing thread are both controlled by the --nonblocking command line
|
||||
_enableProcessVoxelsThread = _enableNetworkThread = !cmdOptionExists(argc, constArgv, "--nonblocking");
|
||||
|
||||
// setup QSettings
|
||||
#ifdef Q_OS_MAC
|
||||
QString resourcesPath = QCoreApplication::applicationDirPath() + "/../Resources";
|
||||
#else
|
||||
QString resourcesPath = QCoreApplication::applicationDirPath() + "/resources";
|
||||
#endif
|
||||
|
||||
|
||||
// read the ApplicationInfo.ini file for Name/Version/Domain information
|
||||
QSettings applicationInfo(resourcesPath + "/info/ApplicationInfo.ini", QSettings::IniFormat);
|
||||
QSettings applicationInfo("resources/info/ApplicationInfo.ini", QSettings::IniFormat);
|
||||
|
||||
// set the associated application properties
|
||||
applicationInfo.beginGroup("INFO");
|
||||
|
@ -191,11 +190,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
setApplicationVersion(applicationInfo.value("version").toString());
|
||||
setOrganizationName(applicationInfo.value("organizationName").toString());
|
||||
setOrganizationDomain(applicationInfo.value("organizationDomain").toString());
|
||||
|
||||
|
||||
_settings = new QSettings(this);
|
||||
|
||||
// call Menu getInstance static method to set up the menu
|
||||
_window->setMenuBar(Menu::getInstance());
|
||||
|
||||
// Check to see if the user passed in a command line option for loading a local
|
||||
// Voxel File.
|
||||
|
@ -251,6 +247,9 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
}
|
||||
|
||||
Application::~Application() {
|
||||
|
||||
qInstallMessageHandler(NULL);
|
||||
|
||||
// make sure we don't call the idle timer any more
|
||||
delete idleTimer;
|
||||
|
||||
|
@ -1858,6 +1857,8 @@ void Application::init() {
|
|||
_particles.init();
|
||||
_particles.setViewFrustum(getViewFrustum());
|
||||
|
||||
_metavoxels.init();
|
||||
|
||||
_particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio, &_myAvatar);
|
||||
|
||||
_palette.init(_glWidget->width(), _glWidget->height());
|
||||
|
@ -2376,6 +2377,15 @@ void Application::updateParticles(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::updateMetavoxels(float deltaTime) {
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateMetavoxels()");
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Metavoxels)) {
|
||||
_metavoxels.simulate(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::updateTransmitter(float deltaTime) {
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateTransmitter()");
|
||||
|
@ -2521,6 +2531,7 @@ void Application::update(float deltaTime) {
|
|||
updateAvatars(deltaTime, mouseRayOrigin, mouseRayDirection); //loop through all the other avatars and simulate them...
|
||||
updateMyAvatarSimulation(deltaTime); // Simulate myself
|
||||
updateParticles(deltaTime); // Simulate particle cloud movements
|
||||
updateMetavoxels(deltaTime); // update metavoxels
|
||||
updateTransmitter(deltaTime); // transmitter drive or pick
|
||||
updateCamera(deltaTime); // handle various camera tweaks like off axis projection
|
||||
updateDialogs(deltaTime); // update various stats dialogs if present
|
||||
|
@ -3068,6 +3079,13 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
}
|
||||
}
|
||||
|
||||
// also, metavoxels
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Metavoxels)) {
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... metavoxels...");
|
||||
_metavoxels.render();
|
||||
}
|
||||
|
||||
// render particles...
|
||||
_particles.render();
|
||||
|
||||
|
@ -4432,7 +4450,8 @@ void Application::loadScript() {
|
|||
bool wantMenuItems = true; // tells the ScriptEngine object to add menu items for itself
|
||||
|
||||
|
||||
ScriptEngine* scriptEngine = new ScriptEngine(script, wantMenuItems, fileName, Menu::getInstance());
|
||||
ScriptEngine* scriptEngine = new ScriptEngine(script, wantMenuItems, fileName, Menu::getInstance(),
|
||||
&_controllerScriptingInterface);
|
||||
scriptEngine->setupMenuItems();
|
||||
|
||||
// setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so
|
||||
|
@ -4460,3 +4479,12 @@ void Application::loadScript() {
|
|||
// restore the main window's active state
|
||||
_window->activateWindow();
|
||||
}
|
||||
|
||||
void Application::toggleLogDialog() {
|
||||
if (! _logDialog) {
|
||||
_logDialog = new LogDialog(_glWidget);
|
||||
_logDialog->show();
|
||||
} else {
|
||||
_logDialog->close();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <QSettings>
|
||||
#include <QTouchEvent>
|
||||
#include <QList>
|
||||
#include <QPointer>
|
||||
|
||||
#include <NetworkPacket.h>
|
||||
#include <NodeList.h>
|
||||
|
@ -58,16 +59,20 @@
|
|||
#include "renderer/AmbientOcclusionEffect.h"
|
||||
#include "renderer/GeometryCache.h"
|
||||
#include "renderer/GlowEffect.h"
|
||||
#include "renderer/VoxelShader.h"
|
||||
#include "renderer/MetavoxelSystem.h"
|
||||
#include "renderer/PointShader.h"
|
||||
#include "renderer/TextureCache.h"
|
||||
#include "renderer/VoxelShader.h"
|
||||
#include "ui/BandwidthDialog.h"
|
||||
#include "ui/ChatEntry.h"
|
||||
#include "ui/VoxelStatsDialog.h"
|
||||
#include "ui/RearMirrorTools.h"
|
||||
#include "ui/LodToolsDialog.h"
|
||||
#include "ui/LogDialog.h"
|
||||
#include "ParticleTreeRenderer.h"
|
||||
#include "ParticleEditHandle.h"
|
||||
#include "ControllerScriptingInterface.h"
|
||||
|
||||
|
||||
class QAction;
|
||||
class QActionGroup;
|
||||
|
@ -221,7 +226,7 @@ public slots:
|
|||
void decreaseVoxelSize();
|
||||
void increaseVoxelSize();
|
||||
void loadScript();
|
||||
|
||||
void toggleLogDialog();
|
||||
|
||||
private slots:
|
||||
|
||||
|
@ -282,6 +287,7 @@ private:
|
|||
void updateThreads(float deltaTime);
|
||||
void updateMyAvatarSimulation(float deltaTime);
|
||||
void updateParticles(float deltaTime);
|
||||
void updateMetavoxels(float deltaTime);
|
||||
void updateTransmitter(float deltaTime);
|
||||
void updateCamera(float deltaTime);
|
||||
void updateDialogs(float deltaTime);
|
||||
|
@ -362,6 +368,8 @@ private:
|
|||
QByteArray _voxelsFilename;
|
||||
bool _wantToKillLocalVoxels;
|
||||
|
||||
MetavoxelSystem _metavoxels;
|
||||
|
||||
ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc.
|
||||
|
||||
Oscilloscope _audioScope;
|
||||
|
@ -499,6 +507,8 @@ private:
|
|||
|
||||
std::vector<VoxelFade> _voxelFades;
|
||||
std::vector<Avatar*> _avatarFades;
|
||||
ControllerScriptingInterface _controllerScriptingInterface;
|
||||
QPointer<LogDialog> _logDialog;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__Application__) */
|
||||
|
|
|
@ -174,9 +174,8 @@ void linearResampling(int16_t* sourceSamples, int16_t* destinationSamples,
|
|||
if (sourceAudioFormat == destinationAudioFormat) {
|
||||
memcpy(destinationSamples, sourceSamples, numSourceSamples * sizeof(int16_t));
|
||||
} else {
|
||||
int destinationChannels = (destinationAudioFormat.channelCount() >= 2) ? 2 : destinationAudioFormat.channelCount();
|
||||
float sourceToDestinationFactor = (sourceAudioFormat.sampleRate() / (float) destinationAudioFormat.sampleRate())
|
||||
* (sourceAudioFormat.channelCount() / (float) destinationChannels);
|
||||
* (sourceAudioFormat.channelCount() / (float) destinationAudioFormat.channelCount());
|
||||
|
||||
// take into account the number of channels in source and destination
|
||||
// accomodate for the case where have an output with > 2 channels
|
||||
|
@ -203,14 +202,15 @@ void linearResampling(int16_t* sourceSamples, int16_t* destinationSamples,
|
|||
// upsample from 24 to 48
|
||||
// for now this only supports a stereo to stereo conversion - this is our case for network audio to output
|
||||
int sourceIndex = 0;
|
||||
int destinationToSourceFactor = (1 / sourceToDestinationFactor);
|
||||
int dtsSampleRateFactor = (destinationAudioFormat.sampleRate() / sourceAudioFormat.sampleRate());
|
||||
int sampleShift = destinationAudioFormat.channelCount() * dtsSampleRateFactor;
|
||||
int destinationToSourceFactor = (1 / sourceToDestinationFactor);
|
||||
|
||||
for (int i = 0; i < numDestinationSamples; i += destinationAudioFormat.channelCount() * dtsSampleRateFactor) {
|
||||
for (int i = 0; i < numDestinationSamples; i += sampleShift) {
|
||||
sourceIndex = (i / destinationToSourceFactor);
|
||||
|
||||
// fill the L/R channels and make the rest silent
|
||||
for (int j = i; j < i + (dtsSampleRateFactor * destinationAudioFormat.channelCount()); j++) {
|
||||
for (int j = i; j < i + sampleShift; j++) {
|
||||
if (j % destinationAudioFormat.channelCount() == 0) {
|
||||
// left channel
|
||||
destinationSamples[j] = sourceSamples[sourceIndex];
|
||||
|
|
183
interface/src/ControllerScriptingInterface.cpp
Normal file
183
interface/src/ControllerScriptingInterface.cpp
Normal file
|
@ -0,0 +1,183 @@
|
|||
//
|
||||
// ControllerScriptingInterface.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/17/13
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <HandData.h>
|
||||
#include "Application.h"
|
||||
#include "ControllerScriptingInterface.h"
|
||||
|
||||
const PalmData* ControllerScriptingInterface::getPrimaryPalm() const {
|
||||
int leftPalmIndex, rightPalmIndex;
|
||||
|
||||
const HandData* handData = Application::getInstance()->getAvatar()->getHandData();
|
||||
handData->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
|
||||
|
||||
if (rightPalmIndex != -1) {
|
||||
return &handData->getPalms()[rightPalmIndex];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ControllerScriptingInterface::getNumberOfActivePalms() const {
|
||||
const HandData* handData = Application::getInstance()->getAvatar()->getHandData();
|
||||
int numberOfPalms = handData->getNumPalms();
|
||||
int numberOfActivePalms = 0;
|
||||
for (int i = 0; i < numberOfPalms; i++) {
|
||||
if (getPalm(i)->isActive()) {
|
||||
numberOfActivePalms++;
|
||||
}
|
||||
}
|
||||
return numberOfActivePalms;
|
||||
}
|
||||
|
||||
const PalmData* ControllerScriptingInterface::getPalm(int palmIndex) const {
|
||||
const HandData* handData = Application::getInstance()->getAvatar()->getHandData();
|
||||
return &handData->getPalms()[palmIndex];
|
||||
}
|
||||
|
||||
const PalmData* ControllerScriptingInterface::getActivePalm(int palmIndex) const {
|
||||
const HandData* handData = Application::getInstance()->getAvatar()->getHandData();
|
||||
int numberOfPalms = handData->getNumPalms();
|
||||
int numberOfActivePalms = 0;
|
||||
for (int i = 0; i < numberOfPalms; i++) {
|
||||
if (getPalm(i)->isActive()) {
|
||||
if (numberOfActivePalms == palmIndex) {
|
||||
return &handData->getPalms()[i];
|
||||
}
|
||||
numberOfActivePalms++;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool ControllerScriptingInterface::isPrimaryButtonPressed() const {
|
||||
const PalmData* primaryPalm = getPrimaryPalm();
|
||||
if (primaryPalm) {
|
||||
if (primaryPalm->getControllerButtons() & BUTTON_FWD) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
glm::vec2 ControllerScriptingInterface::getPrimaryJoystickPosition() const {
|
||||
const PalmData* primaryPalm = getPrimaryPalm();
|
||||
if (primaryPalm) {
|
||||
return glm::vec2(primaryPalm->getJoystickX(), primaryPalm->getJoystickY());
|
||||
}
|
||||
|
||||
return glm::vec2(0);
|
||||
}
|
||||
|
||||
int ControllerScriptingInterface::getNumberOfButtons() const {
|
||||
return getNumberOfActivePalms() * NUMBER_OF_BUTTONS_PER_PALM;
|
||||
}
|
||||
|
||||
bool ControllerScriptingInterface::isButtonPressed(int buttonIndex) const {
|
||||
int palmIndex = buttonIndex / NUMBER_OF_BUTTONS_PER_PALM;
|
||||
int buttonOnPalm = buttonIndex % NUMBER_OF_BUTTONS_PER_PALM;
|
||||
const PalmData* palmData = getActivePalm(palmIndex);
|
||||
if (palmData) {
|
||||
switch (buttonOnPalm) {
|
||||
case 0:
|
||||
return palmData->getControllerButtons() & BUTTON_0;
|
||||
case 1:
|
||||
return palmData->getControllerButtons() & BUTTON_1;
|
||||
case 2:
|
||||
return palmData->getControllerButtons() & BUTTON_2;
|
||||
case 3:
|
||||
return palmData->getControllerButtons() & BUTTON_3;
|
||||
case 4:
|
||||
return palmData->getControllerButtons() & BUTTON_4;
|
||||
case 5:
|
||||
return palmData->getControllerButtons() & BUTTON_FWD;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int ControllerScriptingInterface::getNumberOfTriggers() const {
|
||||
return getNumberOfActivePalms() * NUMBER_OF_TRIGGERS_PER_PALM;
|
||||
}
|
||||
|
||||
float ControllerScriptingInterface::getTriggerValue(int triggerIndex) const {
|
||||
// we know there's one trigger per palm, so the triggerIndex is the palm Index
|
||||
int palmIndex = triggerIndex;
|
||||
const PalmData* palmData = getActivePalm(palmIndex);
|
||||
if (palmData) {
|
||||
return palmData->getTrigger();
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
int ControllerScriptingInterface::getNumberOfJoysticks() const {
|
||||
return getNumberOfActivePalms() * NUMBER_OF_JOYSTICKS_PER_PALM;
|
||||
}
|
||||
|
||||
glm::vec2 ControllerScriptingInterface::getJoystickPosition(int joystickIndex) const {
|
||||
// we know there's one joystick per palm, so the joystickIndex is the palm Index
|
||||
int palmIndex = joystickIndex;
|
||||
const PalmData* palmData = getActivePalm(palmIndex);
|
||||
if (palmData) {
|
||||
return glm::vec2(palmData->getJoystickX(), palmData->getJoystickY());
|
||||
}
|
||||
return glm::vec2(0);
|
||||
}
|
||||
|
||||
int ControllerScriptingInterface::getNumberOfSpatialControls() const {
|
||||
return getNumberOfActivePalms() * NUMBER_OF_SPATIALCONTROLS_PER_PALM;
|
||||
}
|
||||
|
||||
glm::vec3 ControllerScriptingInterface::getSpatialControlPosition(int controlIndex) const {
|
||||
int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM;
|
||||
int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM;
|
||||
const PalmData* palmData = getActivePalm(palmIndex);
|
||||
if (palmData) {
|
||||
switch (controlOfPalm) {
|
||||
case PALM_SPATIALCONTROL:
|
||||
return palmData->getPosition();
|
||||
case TIP_SPATIALCONTROL:
|
||||
return palmData->getTipPosition();
|
||||
}
|
||||
}
|
||||
return glm::vec3(0); // bad index
|
||||
}
|
||||
|
||||
glm::vec3 ControllerScriptingInterface::getSpatialControlVelocity(int controlIndex) const {
|
||||
int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM;
|
||||
int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM;
|
||||
const PalmData* palmData = getActivePalm(palmIndex);
|
||||
if (palmData) {
|
||||
switch (controlOfPalm) {
|
||||
case PALM_SPATIALCONTROL:
|
||||
return palmData->getVelocity();
|
||||
case TIP_SPATIALCONTROL:
|
||||
return palmData->getTipVelocity();
|
||||
}
|
||||
}
|
||||
return glm::vec3(0); // bad index
|
||||
}
|
||||
|
||||
glm::vec3 ControllerScriptingInterface::getSpatialControlNormal(int controlIndex) const {
|
||||
int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM;
|
||||
int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM;
|
||||
const PalmData* palmData = getActivePalm(palmIndex);
|
||||
if (palmData) {
|
||||
switch (controlOfPalm) {
|
||||
case PALM_SPATIALCONTROL:
|
||||
return palmData->getNormal();
|
||||
case TIP_SPATIALCONTROL:
|
||||
return palmData->getNormal(); // currently the tip doesn't have a unique normal, use the palm normal
|
||||
}
|
||||
}
|
||||
return glm::vec3(0); // bad index
|
||||
}
|
||||
|
||||
|
||||
|
52
interface/src/ControllerScriptingInterface.h
Normal file
52
interface/src/ControllerScriptingInterface.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// ControllerScriptingInterface.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/17/13
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__ControllerScriptingInterface__
|
||||
#define __hifi__ControllerScriptingInterface__
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
#include <AbstractControllerScriptingInterface.h>
|
||||
|
||||
/// handles scripting of input controller commands from JS
|
||||
class ControllerScriptingInterface : public AbstractControllerScriptingInterface {
|
||||
Q_OBJECT
|
||||
|
||||
public slots:
|
||||
virtual bool isPrimaryButtonPressed() const;
|
||||
virtual glm::vec2 getPrimaryJoystickPosition() const;
|
||||
|
||||
virtual int getNumberOfButtons() const;
|
||||
virtual bool isButtonPressed(int buttonIndex) const;
|
||||
|
||||
virtual int getNumberOfTriggers() const;
|
||||
virtual float getTriggerValue(int triggerIndex) const;
|
||||
|
||||
virtual int getNumberOfJoysticks() const;
|
||||
virtual glm::vec2 getJoystickPosition(int joystickIndex) const;
|
||||
|
||||
virtual int getNumberOfSpatialControls() const;
|
||||
virtual glm::vec3 getSpatialControlPosition(int controlIndex) const;
|
||||
virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const;
|
||||
virtual glm::vec3 getSpatialControlNormal(int controlIndex) const;
|
||||
|
||||
private:
|
||||
const PalmData* getPrimaryPalm() const;
|
||||
const PalmData* getPalm(int palmIndex) const;
|
||||
int getNumberOfActivePalms() const;
|
||||
const PalmData* getActivePalm(int palmIndex) const;
|
||||
};
|
||||
|
||||
const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip
|
||||
const int NUMBER_OF_JOYSTICKS_PER_PALM = 1;
|
||||
const int NUMBER_OF_TRIGGERS_PER_PALM = 1;
|
||||
const int NUMBER_OF_BUTTONS_PER_PALM = 6;
|
||||
const int PALM_SPATIALCONTROL = 0;
|
||||
const int TIP_SPATIALCONTROL = 1;
|
||||
|
||||
#endif /* defined(__hifi__ControllerScriptingInterface__) */
|
|
@ -6,12 +6,13 @@
|
|||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "LogDisplay.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
#include "LogDisplay.h"
|
||||
#include "Util.h"
|
||||
|
||||
using namespace std;
|
||||
|
@ -91,6 +92,7 @@ void LogDisplay::setCharacterSize(unsigned width, unsigned height) {
|
|||
void LogDisplay::addMessage(const char* ptr) {
|
||||
|
||||
pthread_mutex_lock(& _mutex);
|
||||
emit logReceived(ptr);
|
||||
|
||||
// T-pipe, if requested
|
||||
if (_stream != 0l) {
|
||||
|
@ -118,7 +120,7 @@ void LogDisplay::addMessage(const char* ptr) {
|
|||
_writePos = _chars;
|
||||
}
|
||||
|
||||
if (++_writtenInLine >= _lineLength || c == '\0') {
|
||||
if (c == '\0') {
|
||||
|
||||
// new line? store its start to the line buffer and mark next line as empty
|
||||
++_lastLinePos;
|
||||
|
@ -148,13 +150,24 @@ void LogDisplay::addMessage(const char* ptr) {
|
|||
|
||||
// remember start position in character buffer for next line and reset character count
|
||||
_writeLineStartPos = _writePos;
|
||||
_writtenInLine = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(& _mutex);
|
||||
}
|
||||
|
||||
QStringList LogDisplay::getLogData() {
|
||||
// wait for adding new log data whilr iterating over _lines
|
||||
pthread_mutex_lock(& _mutex);
|
||||
QStringList list;
|
||||
int i = 0;
|
||||
while (_lines[i] != *_lastLinePos) {
|
||||
list.append(_lines[i++]);
|
||||
}
|
||||
pthread_mutex_unlock(& _mutex);
|
||||
return list;
|
||||
}
|
||||
|
||||
//
|
||||
// Rendering
|
||||
//
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
|
||||
#include "ui/TextRenderer.h"
|
||||
|
||||
class LogDisplay {
|
||||
class LogDisplay : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
static LogDisplay instance;
|
||||
|
@ -43,6 +44,11 @@ public:
|
|||
static unsigned const LINE_BUFFER_SIZE = 256; // number of lines that are buffered
|
||||
static unsigned const MAX_MESSAGE_LENGTH = 512; // maximum number of characters for a message
|
||||
|
||||
QStringList getLogData();
|
||||
|
||||
signals:
|
||||
void logReceived(QString message);
|
||||
|
||||
private:
|
||||
// use static 'instance' to access the single instance
|
||||
LogDisplay();
|
||||
|
|
|
@ -265,7 +265,7 @@ Menu::Menu() :
|
|||
|
||||
addDisabledActionAndSeparator(viewMenu, "Stats");
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L);
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L, appInstance, SLOT(toggleLogDialog()));
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Oscilloscope, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Bandwidth, 0, true);
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, this, SLOT(bandwidthDetails()));
|
||||
|
@ -285,7 +285,8 @@ Menu::Menu() :
|
|||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ParticleCloud, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Shadows, 0, false);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, false);
|
||||
|
||||
|
||||
QMenu* voxelOptionsMenu = developerMenu->addMenu("Voxel Options");
|
||||
|
||||
|
@ -701,7 +702,7 @@ QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu,
|
|||
action = destinationMenu->addAction(actionName);
|
||||
action->setShortcut(shortcut);
|
||||
}
|
||||
action->setMenuRole(role);
|
||||
action->setMenuRole(role);
|
||||
|
||||
_actionHash.insert(actionName, action);
|
||||
|
||||
|
|
|
@ -146,7 +146,6 @@ private:
|
|||
int _boundaryLevelAdjust;
|
||||
QAction* _useVoxelShader;
|
||||
int _maxVoxelPacketsPerSecond;
|
||||
|
||||
QMenu* _activeScriptsMenu;
|
||||
};
|
||||
|
||||
|
@ -219,6 +218,7 @@ namespace MenuOption {
|
|||
const QString Login = "Login";
|
||||
const QString LookAtIndicator = "Look-at Indicator";
|
||||
const QString LookAtVectors = "Look-at Vectors";
|
||||
const QString Metavoxels = "Metavoxels";
|
||||
const QString Mirror = "Mirror";
|
||||
const QString MoveWithLean = "Move with Lean";
|
||||
const QString NewVoxelCullingMode = "New Voxel Culling Mode";
|
||||
|
|
|
@ -338,8 +338,6 @@ void Avatar::render(bool forceRenderHead) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// returns true if the Leap controls any of the avatar's hands.
|
||||
bool Avatar::updateLeapHandPositions() {
|
||||
bool returnValue = false;
|
||||
|
@ -443,8 +441,6 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
|
|||
return glm::angleAxis(angle * proportion, axis);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Avatar::renderBody(bool forceRenderHead) {
|
||||
|
||||
if (_head.getVideoFace().isFullFrame()) {
|
||||
|
|
|
@ -24,11 +24,11 @@ using namespace std;
|
|||
const float FINGERTIP_VOXEL_SIZE = 0.05;
|
||||
const int TOY_BALL_HAND = 1;
|
||||
const float TOY_BALL_RADIUS = 0.05f;
|
||||
const float TOY_BALL_DAMPING = 0.99f;
|
||||
const float TOY_BALL_DAMPING = 0.1f;
|
||||
const glm::vec3 NO_VELOCITY = glm::vec3(0,0,0);
|
||||
const glm::vec3 NO_GRAVITY = glm::vec3(0,0,0);
|
||||
const float NO_DAMPING = 0.f;
|
||||
const glm::vec3 TOY_BALL_GRAVITY = glm::vec3(0,-0.5,0);
|
||||
const glm::vec3 TOY_BALL_GRAVITY = glm::vec3(0,-2.0,0);
|
||||
const QString TOY_BALL_UPDATE_SCRIPT("");
|
||||
const float PALM_COLLISION_RADIUS = 0.03f;
|
||||
const float CATCH_RADIUS = 0.2f;
|
||||
|
@ -98,7 +98,7 @@ void Hand::simulateToyBall(PalmData& palm, const glm::vec3& fingerTipPosition, f
|
|||
|
||||
// update the particle with it's new state...
|
||||
#ifdef DEBUG_HAND
|
||||
qDebug("Update caught particle!\n");
|
||||
qDebug("Caught!\n");
|
||||
#endif
|
||||
caughtParticle->updateParticle(newPosition,
|
||||
closestParticle->getRadius(),
|
||||
|
@ -108,6 +108,7 @@ void Hand::simulateToyBall(PalmData& palm, const glm::vec3& fingerTipPosition, f
|
|||
NO_DAMPING,
|
||||
IN_HAND, // we just grabbed it!
|
||||
closestParticle->getUpdateScript());
|
||||
|
||||
|
||||
// now tell our hand about us having caught it...
|
||||
_toyBallInHand[handID] = true;
|
||||
|
@ -115,6 +116,11 @@ void Hand::simulateToyBall(PalmData& palm, const glm::vec3& fingerTipPosition, f
|
|||
//printf(">>>>>>> caught... handID:%d particle ID:%d _toyBallInHand[handID] = true\n", handID, closestParticle->getID());
|
||||
_ballParticleEditHandles[handID] = caughtParticle;
|
||||
caughtParticle = NULL;
|
||||
// Play a catch sound!
|
||||
Application::getInstance()->getAudio()->startDrumSound(1.0,
|
||||
300,
|
||||
0.5,
|
||||
0.05);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,11 +164,17 @@ void Hand::simulateToyBall(PalmData& palm, const glm::vec3& fingerTipPosition, f
|
|||
TOY_BALL_DAMPING,
|
||||
IN_HAND,
|
||||
TOY_BALL_UPDATE_SCRIPT);
|
||||
// Play a new ball sound
|
||||
Application::getInstance()->getAudio()->startDrumSound(1.0,
|
||||
2000,
|
||||
0.5,
|
||||
0.02);
|
||||
|
||||
}
|
||||
} else {
|
||||
// Ball is in hand
|
||||
#ifdef DEBUG_HAND
|
||||
qDebug("Ball in hand\n");
|
||||
//qDebug("Ball in hand\n");
|
||||
#endif
|
||||
glm::vec3 ballPosition = ballFromHand ? palm.getPosition() : fingerTipPosition;
|
||||
_ballParticleEditHandles[handID]->updateParticle(ballPosition / (float)TREE_SCALE,
|
||||
|
@ -178,13 +190,14 @@ void Hand::simulateToyBall(PalmData& palm, const glm::vec3& fingerTipPosition, f
|
|||
// If toy ball just released, add velocity to it!
|
||||
if (_toyBallInHand[handID]) {
|
||||
|
||||
const float THROWN_VELOCITY_SCALING = 1.5f;
|
||||
_toyBallInHand[handID] = false;
|
||||
glm::vec3 ballPosition = ballFromHand ? palm.getPosition() : fingerTipPosition;
|
||||
glm::vec3 ballVelocity = ballFromHand ? palm.getRawVelocity() : palm.getTipVelocity();
|
||||
glm::quat avatarRotation = _owningAvatar->getOrientation();
|
||||
ballVelocity = avatarRotation * ballVelocity;
|
||||
ballVelocity *= THROWN_VELOCITY_SCALING;
|
||||
|
||||
// ball is no longer in hand...
|
||||
#ifdef DEBUG_HAND
|
||||
qDebug("Threw ball, v = %.3f\n", glm::length(ballVelocity));
|
||||
#endif
|
||||
|
@ -201,6 +214,13 @@ void Hand::simulateToyBall(PalmData& palm, const glm::vec3& fingerTipPosition, f
|
|||
// note: deleting the edit handle doesn't effect the actual particle
|
||||
delete _ballParticleEditHandles[handID];
|
||||
_ballParticleEditHandles[handID] = NULL;
|
||||
|
||||
// Play a throw sound
|
||||
Application::getInstance()->getAudio()->startDrumSound(1.0,
|
||||
3000,
|
||||
0.5,
|
||||
0.02);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,11 +19,23 @@
|
|||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
int main(int argc, const char * argv[]) {
|
||||
timeval startup_time;
|
||||
gettimeofday(&startup_time, NULL);
|
||||
|
||||
// Debug option to demonstrate that the client's local time does not
|
||||
// need to be in sync with any other network node. This forces clock
|
||||
// skew for the individual client
|
||||
const char* CLOCK_SKEW = "--clockSkew";
|
||||
const char* clockSkewOption = getCmdOption(argc, argv, CLOCK_SKEW);
|
||||
if (clockSkewOption) {
|
||||
int clockSkew = atoi(clockSkewOption);
|
||||
usecTimestampNowForceClockSkew(clockSkew);
|
||||
qDebug("clockSkewOption=%s clockSkew=%d\n", clockSkewOption, clockSkew);
|
||||
}
|
||||
|
||||
int exitCode;
|
||||
{
|
||||
Application app(argc, const_cast<char**>(argv), startup_time);
|
||||
|
|
122
interface/src/renderer/MetavoxelSystem.cpp
Normal file
122
interface/src/renderer/MetavoxelSystem.cpp
Normal file
|
@ -0,0 +1,122 @@
|
|||
//
|
||||
// MetavoxelSystem.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 12/10/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QtDebug>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "MetavoxelSystem.h"
|
||||
|
||||
ProgramObject MetavoxelSystem::_program;
|
||||
int MetavoxelSystem::_pointScaleLocation;
|
||||
|
||||
MetavoxelSystem::MetavoxelSystem() :
|
||||
_pointVisitor(_points),
|
||||
_buffer(QOpenGLBuffer::VertexBuffer) {
|
||||
}
|
||||
|
||||
void MetavoxelSystem::init() {
|
||||
if (!_program.isLinked()) {
|
||||
switchToResourcesParentIfRequired();
|
||||
_program.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/metavoxel_point.vert");
|
||||
_program.link();
|
||||
|
||||
_pointScaleLocation = _program.uniformLocation("pointScale");
|
||||
}
|
||||
|
||||
AttributeRegistry::getInstance()->configureScriptEngine(&_scriptEngine);
|
||||
|
||||
QFile scriptFile("resources/scripts/sphere.js");
|
||||
scriptFile.open(QIODevice::ReadOnly);
|
||||
QScriptValue guideFunction = _scriptEngine.evaluate(QTextStream(&scriptFile).readAll());
|
||||
_data.setAttributeValue(MetavoxelPath(), AttributeValue(AttributeRegistry::getInstance()->getGuideAttribute(),
|
||||
encodeInline(PolymorphicDataPointer(new ScriptedMetavoxelGuide(guideFunction)))));
|
||||
|
||||
_buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw);
|
||||
_buffer.create();
|
||||
}
|
||||
|
||||
void MetavoxelSystem::simulate(float deltaTime) {
|
||||
_points.clear();
|
||||
_data.guide(_pointVisitor);
|
||||
|
||||
_buffer.bind();
|
||||
int bytes = _points.size() * sizeof(Point);
|
||||
if (_buffer.size() < bytes) {
|
||||
_buffer.allocate(_points.constData(), bytes);
|
||||
} else {
|
||||
_buffer.write(0, _points.constData(), bytes);
|
||||
}
|
||||
_buffer.release();
|
||||
}
|
||||
|
||||
void MetavoxelSystem::render() {
|
||||
int viewport[4];
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
const int VIEWPORT_WIDTH_INDEX = 2;
|
||||
const int VIEWPORT_HEIGHT_INDEX = 3;
|
||||
float viewportWidth = viewport[VIEWPORT_WIDTH_INDEX];
|
||||
float viewportHeight = viewport[VIEWPORT_HEIGHT_INDEX];
|
||||
float viewportDiagonal = sqrtf(viewportWidth*viewportWidth + viewportHeight*viewportHeight);
|
||||
float worldDiagonal = glm::distance(Application::getInstance()->getViewFrustum()->getNearBottomLeft(),
|
||||
Application::getInstance()->getViewFrustum()->getNearTopRight());
|
||||
|
||||
_program.bind();
|
||||
_program.setUniformValue(_pointScaleLocation, viewportDiagonal *
|
||||
Application::getInstance()->getViewFrustum()->getNearClip() / worldDiagonal);
|
||||
|
||||
_buffer.bind();
|
||||
|
||||
Point* pt = 0;
|
||||
glVertexPointer(4, GL_FLOAT, sizeof(Point), &pt->vertex);
|
||||
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Point), &pt->color);
|
||||
glNormalPointer(GL_BYTE, sizeof(Point), &pt->normal);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
glEnableClientState(GL_NORMAL_ARRAY);
|
||||
|
||||
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
|
||||
|
||||
glDrawArrays(GL_POINTS, 0, _points.size());
|
||||
|
||||
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glDisableClientState(GL_NORMAL_ARRAY);
|
||||
|
||||
_buffer.release();
|
||||
|
||||
_program.release();
|
||||
}
|
||||
|
||||
MetavoxelSystem::PointVisitor::PointVisitor(QVector<Point>& points) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() <<
|
||||
AttributeRegistry::getInstance()->getColorAttribute() <<
|
||||
AttributeRegistry::getInstance()->getNormalAttribute()),
|
||||
_points(points) {
|
||||
}
|
||||
|
||||
bool MetavoxelSystem::PointVisitor::visit(const MetavoxelInfo& info) {
|
||||
if (!info.isLeaf) {
|
||||
return true;
|
||||
}
|
||||
QRgb color = info.attributeValues.at(0).getInlineValue<QRgb>();
|
||||
QRgb normal = info.attributeValues.at(1).getInlineValue<QRgb>();
|
||||
int alpha = qAlpha(color);
|
||||
if (alpha > 0) {
|
||||
Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size),
|
||||
{ qRed(color), qGreen(color), qBlue(color), alpha }, { qRed(normal), qGreen(normal), qBlue(normal) } };
|
||||
_points.append(point);
|
||||
}
|
||||
return false;
|
||||
}
|
61
interface/src/renderer/MetavoxelSystem.h
Normal file
61
interface/src/renderer/MetavoxelSystem.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// MetavoxelSystem.h
|
||||
// interface
|
||||
//
|
||||
// Created by Andrzej Kapolka on 12/10/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__MetavoxelSystem__
|
||||
#define __interface__MetavoxelSystem__
|
||||
|
||||
#include <QOpenGLBuffer>
|
||||
#include <QScriptEngine>
|
||||
#include <QVector>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <MetavoxelData.h>
|
||||
|
||||
#include "ProgramObject.h"
|
||||
|
||||
/// Renders a metavoxel tree.
|
||||
class MetavoxelSystem {
|
||||
public:
|
||||
|
||||
MetavoxelSystem();
|
||||
|
||||
void init();
|
||||
|
||||
void simulate(float deltaTime);
|
||||
void render();
|
||||
|
||||
private:
|
||||
|
||||
class Point {
|
||||
public:
|
||||
glm::vec4 vertex;
|
||||
quint8 color[4];
|
||||
quint8 normal[3];
|
||||
};
|
||||
|
||||
class PointVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
PointVisitor(QVector<Point>& points);
|
||||
virtual bool visit(const MetavoxelInfo& info);
|
||||
|
||||
private:
|
||||
QVector<Point>& _points;
|
||||
};
|
||||
|
||||
static ProgramObject _program;
|
||||
static int _pointScaleLocation;
|
||||
|
||||
QScriptEngine _scriptEngine;
|
||||
MetavoxelData _data;
|
||||
QVector<Point> _points;
|
||||
PointVisitor _pointVisitor;
|
||||
QOpenGLBuffer _buffer;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__MetavoxelSystem__) */
|
75
interface/src/ui/LogDialog.cpp
Normal file
75
interface/src/ui/LogDialog.cpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
//
|
||||
// LogDialog.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Stojce Slavkovski on 12/12/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QDesktopWidget>
|
||||
#include <QTextBlock>
|
||||
#include <QtGui>
|
||||
|
||||
#include "SharedUtil.h"
|
||||
#include "ui/LogDialog.h"
|
||||
#include "LogDisplay.h"
|
||||
|
||||
const int INITIAL_WIDTH = 720;
|
||||
const float INITIAL_HEIGHT_RATIO = 0.6f;
|
||||
|
||||
int cursorMeta = qRegisterMetaType<QTextCursor>("QTextCursor");
|
||||
int blockMeta = qRegisterMetaType<QTextBlock>("QTextBlock");
|
||||
|
||||
LogDialog::LogDialog(QWidget* parent) : QDialog(parent, Qt::Dialog) {
|
||||
|
||||
setWindowTitle("Log");
|
||||
|
||||
_logTextBox = new QPlainTextEdit(this);
|
||||
_logTextBox->setReadOnly(true);
|
||||
_logTextBox->show();
|
||||
|
||||
switchToResourcesParentIfRequired();
|
||||
QFile styleSheet("resources/styles/log_dialog.qss");
|
||||
|
||||
if (styleSheet.open(QIODevice::ReadOnly)) {
|
||||
setStyleSheet(styleSheet.readAll());
|
||||
}
|
||||
|
||||
QDesktopWidget desktop;
|
||||
QRect screen = desktop.screenGeometry();
|
||||
resize(INITIAL_WIDTH, static_cast<int>(screen.height() * INITIAL_HEIGHT_RATIO));
|
||||
move(screen.center() - rect().center());
|
||||
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
}
|
||||
|
||||
LogDialog::~LogDialog() {
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void LogDialog::showEvent(QShowEvent *e) {
|
||||
_logTextBox->clear();
|
||||
|
||||
pthread_mutex_lock(& _mutex);
|
||||
QStringList _logData = LogDisplay::instance.getLogData();
|
||||
|
||||
connect(&LogDisplay::instance, &LogDisplay::logReceived, this, &LogDialog::appendLogLine);
|
||||
for(int i = 0; i < _logData.size(); ++i) {
|
||||
appendLogLine(_logData[i]);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(& _mutex);
|
||||
}
|
||||
|
||||
void LogDialog::resizeEvent(QResizeEvent *e) {
|
||||
_logTextBox->resize(width(), height());
|
||||
}
|
||||
|
||||
void LogDialog::appendLogLine(QString logLine) {
|
||||
if (isVisible()) {
|
||||
pthread_mutex_lock(& _mutex);
|
||||
_logTextBox->appendPlainText(logLine.simplified());
|
||||
pthread_mutex_unlock(& _mutex);
|
||||
_logTextBox->ensureCursorVisible();
|
||||
}
|
||||
}
|
36
interface/src/ui/LogDialog.h
Normal file
36
interface/src/ui/LogDialog.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// LogDialog.h
|
||||
// interface
|
||||
//
|
||||
// Created by Stojce Slavkovski on 12/12/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__LogDialog__
|
||||
#define __interface__LogDialog__
|
||||
|
||||
#include <QDialog>
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
class LogDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LogDialog(QWidget* parent);
|
||||
~LogDialog();
|
||||
|
||||
public slots:
|
||||
void appendLogLine(QString logLine);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* e);
|
||||
void showEvent(QShowEvent* e);
|
||||
|
||||
private:
|
||||
QPlainTextEdit* _logTextBox;
|
||||
pthread_mutex_t _mutex;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -169,6 +169,9 @@ int HandData::decodeRemoteData(unsigned char* sourceBuffer) {
|
|||
palm.setRawNormal(handNormal);
|
||||
palm.setActive(true);
|
||||
|
||||
// For received data, set the sixense controller ID to match the order initialized and sent - 0 Left, 1 Right
|
||||
palm.setSixenseID(handIndex);
|
||||
|
||||
for (unsigned int fingerIndex = 0; fingerIndex < numFingers; ++fingerIndex) {
|
||||
if (fingerIndex < palm.getNumFingers()) {
|
||||
FingerData& finger = palm.getFingers()[fingerIndex];
|
||||
|
|
|
@ -53,9 +53,10 @@ public:
|
|||
glm::vec3 worldPositionToLeapPosition(const glm::vec3& worldPosition) const;
|
||||
glm::vec3 worldVectorToLeapVector(const glm::vec3& worldVector) const;
|
||||
|
||||
std::vector<PalmData>& getPalms() { return _palms; }
|
||||
size_t getNumPalms() { return _palms.size(); }
|
||||
PalmData& addNewPalm();
|
||||
std::vector<PalmData>& getPalms() { return _palms; }
|
||||
const std::vector<PalmData>& getPalms() const { return _palms; }
|
||||
size_t getNumPalms() const { return _palms.size(); }
|
||||
PalmData& addNewPalm();
|
||||
|
||||
/// Finds the indices of the left and right palms according to their locations, or -1 if either or
|
||||
/// both is not found.
|
||||
|
@ -137,17 +138,18 @@ public:
|
|||
|
||||
const glm::vec3& getRawPosition() const { return _rawPosition; }
|
||||
const glm::vec3& getRawNormal() const { return _rawNormal; }
|
||||
bool isActive() const { return _isActive; }
|
||||
int getLeapID() const { return _leapID; }
|
||||
int getSixenseID() const { return _sixenseID; }
|
||||
bool isActive() const { return _isActive; }
|
||||
int getLeapID() const { return _leapID; }
|
||||
int getSixenseID() const { return _sixenseID; }
|
||||
|
||||
|
||||
std::vector<FingerData>& getFingers() { return _fingers; }
|
||||
size_t getNumFingers() { return _fingers.size(); }
|
||||
std::vector<FingerData>& getFingers() { return _fingers; }
|
||||
const std::vector<FingerData>& getFingers() const { return _fingers; }
|
||||
size_t getNumFingers() const { return _fingers.size(); }
|
||||
|
||||
void setActive(bool active) { _isActive = active; }
|
||||
void setLeapID(int id) { _leapID = id; }
|
||||
void setSixenseID(int id) { _sixenseID = id; }
|
||||
void setActive(bool active) { _isActive = active; }
|
||||
void setLeapID(int id) { _leapID = id; }
|
||||
void setSixenseID(int id) { _sixenseID = id; }
|
||||
|
||||
void setRawRotation(const glm::quat rawRotation) { _rawRotation = rawRotation; };
|
||||
glm::quat getRawRotation() const { return _rawRotation; }
|
||||
|
@ -164,27 +166,27 @@ public:
|
|||
const glm::vec3& getTipVelocity() const { return _tipVelocity; }
|
||||
void setTipVelocity(const glm::vec3& velocity) { _tipVelocity = velocity; }
|
||||
|
||||
void incrementFramesWithoutData() { _numFramesWithoutData++; }
|
||||
void resetFramesWithoutData() { _numFramesWithoutData = 0; }
|
||||
int getFramesWithoutData() const { return _numFramesWithoutData; }
|
||||
void incrementFramesWithoutData() { _numFramesWithoutData++; }
|
||||
void resetFramesWithoutData() { _numFramesWithoutData = 0; }
|
||||
int getFramesWithoutData() const { return _numFramesWithoutData; }
|
||||
|
||||
// Controller buttons
|
||||
void setControllerButtons(int controllerButtons) { _controllerButtons = controllerButtons; }
|
||||
void setLastControllerButtons(int controllerButtons) { _lastControllerButtons = controllerButtons; }
|
||||
|
||||
int getControllerButtons() { return _controllerButtons; }
|
||||
int getLastControllerButtons() { return _lastControllerButtons; }
|
||||
int getControllerButtons() const { return _controllerButtons; }
|
||||
int getLastControllerButtons() const { return _lastControllerButtons; }
|
||||
|
||||
void setTrigger(float trigger) { _trigger = trigger; }
|
||||
float getTrigger() { return _trigger; }
|
||||
float getTrigger() const { return _trigger; }
|
||||
void setJoystick(float joystickX, float joystickY) { _joystickX = joystickX; _joystickY = joystickY; }
|
||||
float getJoystickX() { return _joystickX; }
|
||||
float getJoystickY() { return _joystickY; }
|
||||
float getJoystickX() const { return _joystickX; }
|
||||
float getJoystickY() const { return _joystickY; }
|
||||
|
||||
bool getIsCollidingWithVoxel() { return _isCollidingWithVoxel; }
|
||||
bool getIsCollidingWithVoxel() const { return _isCollidingWithVoxel; }
|
||||
void setIsCollidingWithVoxel(bool isCollidingWithVoxel) { _isCollidingWithVoxel = isCollidingWithVoxel; }
|
||||
|
||||
bool getIsCollidingWithPalm() { return _isCollidingWithPalm; }
|
||||
bool getIsCollidingWithPalm() const { return _isCollidingWithPalm; }
|
||||
void setIsCollidingWithPalm(bool isCollidingWithPalm) { _isCollidingWithPalm = isCollidingWithPalm; }
|
||||
|
||||
private:
|
||||
|
|
20
libraries/metavoxels/CMakeLists.txt
Normal file
20
libraries/metavoxels/CMakeLists.txt
Normal file
|
@ -0,0 +1,20 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
set(ROOT_DIR ../..)
|
||||
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
|
||||
|
||||
# setup for find modules
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
|
||||
|
||||
set(TARGET_NAME metavoxels)
|
||||
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
|
||||
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
|
||||
setup_hifi_library(${TARGET_NAME})
|
||||
|
||||
qt5_use_modules(${TARGET_NAME} Widgets Script)
|
||||
|
||||
include(${MACRO_DIR}/IncludeGLM.cmake)
|
||||
include_glm(${TARGET_NAME} ${ROOT_DIR})
|
||||
|
143
libraries/metavoxels/src/AttributeRegistry.cpp
Normal file
143
libraries/metavoxels/src/AttributeRegistry.cpp
Normal file
|
@ -0,0 +1,143 @@
|
|||
//
|
||||
// AttributeRegistry.cpp
|
||||
// metavoxels
|
||||
//
|
||||
// Created by Andrzej Kapolka on 12/6/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QScriptEngine>
|
||||
|
||||
#include "AttributeRegistry.h"
|
||||
#include "MetavoxelData.h"
|
||||
|
||||
AttributeRegistry AttributeRegistry::_instance;
|
||||
|
||||
AttributeRegistry::AttributeRegistry() :
|
||||
_guideAttribute(registerAttribute(new PolymorphicAttribute("guide", PolymorphicDataPointer(new DefaultMetavoxelGuide())))),
|
||||
_colorAttribute(registerAttribute(new QRgbAttribute("color"))),
|
||||
_normalAttribute(registerAttribute(new QRgbAttribute("normal", qRgb(0, 127, 0)))) {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
AttributePointer AttributeRegistry::registerAttribute(AttributePointer attribute) {
|
||||
AttributePointer& pointer = _attributes[attribute->getName()];
|
||||
if (!pointer) {
|
||||
pointer = attribute;
|
||||
}
|
||||
return pointer;
|
||||
}
|
||||
|
||||
QScriptValue AttributeRegistry::getAttribute(QScriptContext* context, QScriptEngine* engine) {
|
||||
return engine->newQObject(_instance.getAttribute(context->argument(0).toString()).data(), QScriptEngine::QtOwnership,
|
||||
QScriptEngine::PreferExistingWrapperObject);
|
||||
}
|
||||
|
||||
AttributeValue::AttributeValue(const AttributePointer& attribute) :
|
||||
_attribute(attribute), _value(attribute ? attribute->getDefaultValue() : NULL) {
|
||||
}
|
||||
|
||||
AttributeValue::AttributeValue(const AttributePointer& attribute, void* value) :
|
||||
_attribute(attribute), _value(value) {
|
||||
}
|
||||
|
||||
void* AttributeValue::copy() const {
|
||||
return _attribute->create(_value);
|
||||
}
|
||||
|
||||
bool AttributeValue::isDefault() const {
|
||||
return !_attribute || _attribute->equal(_value, _attribute->getDefaultValue());
|
||||
}
|
||||
|
||||
bool AttributeValue::operator==(const AttributeValue& other) const {
|
||||
return _attribute == other._attribute && (!_attribute || _attribute->equal(_value, other._value));
|
||||
}
|
||||
|
||||
bool AttributeValue::operator==(void* other) const {
|
||||
return _attribute && _attribute->equal(_value, other);
|
||||
}
|
||||
|
||||
OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute) :
|
||||
AttributeValue(attribute, attribute ? attribute->create() : NULL) {
|
||||
}
|
||||
|
||||
OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute, void* value) :
|
||||
AttributeValue(attribute, attribute ? attribute->create(value) : NULL) {
|
||||
}
|
||||
|
||||
OwnedAttributeValue::OwnedAttributeValue(const AttributeValue& other) :
|
||||
AttributeValue(other.getAttribute(), other.getAttribute() ? other.copy() : NULL) {
|
||||
}
|
||||
|
||||
OwnedAttributeValue::~OwnedAttributeValue() {
|
||||
if (_attribute) {
|
||||
_attribute->destroy(_value);
|
||||
}
|
||||
}
|
||||
|
||||
OwnedAttributeValue& OwnedAttributeValue::operator=(const AttributeValue& other) {
|
||||
if (_attribute) {
|
||||
_attribute->destroy(_value);
|
||||
}
|
||||
if ((_attribute = other.getAttribute())) {
|
||||
_value = _attribute->create(other.getValue());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Attribute::Attribute(const QString& name) {
|
||||
setObjectName(name);
|
||||
}
|
||||
|
||||
Attribute::~Attribute() {
|
||||
}
|
||||
|
||||
QRgbAttribute::QRgbAttribute(const QString& name, QRgb defaultValue) :
|
||||
InlineAttribute<QRgb>(name, defaultValue) {
|
||||
}
|
||||
|
||||
bool QRgbAttribute::merge(void*& parent, void* children[]) const {
|
||||
QRgb firstValue = decodeInline<QRgb>(children[0]);
|
||||
int totalRed = qRed(firstValue);
|
||||
int totalGreen = qGreen(firstValue);
|
||||
int totalBlue = qBlue(firstValue);
|
||||
int totalAlpha = qAlpha(firstValue);
|
||||
bool allChildrenEqual = true;
|
||||
for (int i = 1; i < Attribute::MERGE_COUNT; i++) {
|
||||
QRgb value = decodeInline<QRgb>(children[i]);
|
||||
totalRed += qRed(value);
|
||||
totalGreen += qGreen(value);
|
||||
totalBlue += qBlue(value);
|
||||
totalAlpha += qAlpha(value);
|
||||
allChildrenEqual &= (firstValue == value);
|
||||
}
|
||||
parent = encodeInline(qRgba(totalRed / MERGE_COUNT, totalGreen / MERGE_COUNT,
|
||||
totalBlue / MERGE_COUNT, totalAlpha / MERGE_COUNT));
|
||||
return allChildrenEqual;
|
||||
}
|
||||
|
||||
void* QRgbAttribute::createFromScript(const QScriptValue& value, QScriptEngine* engine) const {
|
||||
return encodeInline((QRgb)value.toUInt32());
|
||||
}
|
||||
|
||||
PolymorphicData::~PolymorphicData() {
|
||||
}
|
||||
|
||||
template<> PolymorphicData* QExplicitlySharedDataPointer<PolymorphicData>::clone() {
|
||||
return d->clone();
|
||||
}
|
||||
|
||||
PolymorphicAttribute::PolymorphicAttribute(const QString& name, const PolymorphicDataPointer& defaultValue) :
|
||||
InlineAttribute<PolymorphicDataPointer>(name, defaultValue) {
|
||||
}
|
||||
|
||||
bool PolymorphicAttribute::merge(void*& parent, void* children[]) const {
|
||||
return false;
|
||||
}
|
275
libraries/metavoxels/src/AttributeRegistry.h
Normal file
275
libraries/metavoxels/src/AttributeRegistry.h
Normal file
|
@ -0,0 +1,275 @@
|
|||
//
|
||||
// AttributeRegistry.h
|
||||
// metavoxels
|
||||
//
|
||||
// Created by Andrzej Kapolka on 12/6/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__AttributeRegistry__
|
||||
#define __interface__AttributeRegistry__
|
||||
|
||||
#include <QColor>
|
||||
#include <QExplicitlySharedDataPointer>
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QSharedData>
|
||||
#include <QSharedPointer>
|
||||
#include <QString>
|
||||
|
||||
#include "Bitstream.h"
|
||||
|
||||
class QScriptContext;
|
||||
class QScriptEngine;
|
||||
class QScriptValue;
|
||||
|
||||
class Attribute;
|
||||
|
||||
typedef QSharedPointer<Attribute> AttributePointer;
|
||||
|
||||
/// Maintains information about metavoxel attribute types.
|
||||
class AttributeRegistry {
|
||||
public:
|
||||
|
||||
/// Returns a pointer to the singleton registry instance.
|
||||
static AttributeRegistry* getInstance() { return &_instance; }
|
||||
|
||||
AttributeRegistry();
|
||||
|
||||
/// Configures the supplied script engine with the global AttributeRegistry property.
|
||||
void configureScriptEngine(QScriptEngine* engine);
|
||||
|
||||
/// Registers an attribute with the system. The registry assumes ownership of the object.
|
||||
/// \return either the pointer passed as an argument, if the attribute wasn't already registered, or the existing
|
||||
/// attribute
|
||||
AttributePointer registerAttribute(Attribute* attribute) { return registerAttribute(AttributePointer(attribute)); }
|
||||
|
||||
/// Registers an attribute with the system.
|
||||
/// \return either the pointer passed as an argument, if the attribute wasn't already registered, or the existing
|
||||
/// attribute
|
||||
AttributePointer registerAttribute(AttributePointer attribute);
|
||||
|
||||
/// Retrieves an attribute by name.
|
||||
AttributePointer getAttribute(const QString& name) const { return _attributes.value(name); }
|
||||
|
||||
/// Returns a reference to the standard PolymorphicDataPointer "guide" attribute.
|
||||
const AttributePointer& getGuideAttribute() const { return _guideAttribute; }
|
||||
|
||||
/// Returns a reference to the standard QRgb "color" attribute.
|
||||
const AttributePointer& getColorAttribute() const { return _colorAttribute; }
|
||||
|
||||
/// Returns a reference to the standard QRgb "normal" attribute.
|
||||
const AttributePointer& getNormalAttribute() const { return _normalAttribute; }
|
||||
|
||||
private:
|
||||
|
||||
static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
static AttributeRegistry _instance;
|
||||
|
||||
QHash<QString, AttributePointer> _attributes;
|
||||
AttributePointer _guideAttribute;
|
||||
AttributePointer _colorAttribute;
|
||||
AttributePointer _normalAttribute;
|
||||
};
|
||||
|
||||
/// Converts a value to a void pointer.
|
||||
template<class T> inline void* encodeInline(T value) {
|
||||
return *(void**)&value;
|
||||
}
|
||||
|
||||
/// Extracts a value from a void pointer.
|
||||
template<class T> inline T decodeInline(void* value) {
|
||||
return *(T*)&value;
|
||||
}
|
||||
|
||||
/// Pairs an attribute value with its type.
|
||||
class AttributeValue {
|
||||
public:
|
||||
|
||||
AttributeValue(const AttributePointer& attribute = AttributePointer());
|
||||
AttributeValue(const AttributePointer& attribute, void* value);
|
||||
|
||||
AttributePointer getAttribute() const { return _attribute; }
|
||||
void* getValue() const { return _value; }
|
||||
|
||||
template<class T> void setInlineValue(T value) { _value = encodeInline(value); }
|
||||
template<class T> T getInlineValue() const { return decodeInline<T>(_value); }
|
||||
|
||||
template<class T> T* getPointerValue() const { return static_cast<T*>(_value); }
|
||||
|
||||
void* copy() const;
|
||||
|
||||
bool isDefault() const;
|
||||
|
||||
bool operator==(const AttributeValue& other) const;
|
||||
bool operator==(void* other) const;
|
||||
|
||||
protected:
|
||||
|
||||
AttributePointer _attribute;
|
||||
void* _value;
|
||||
};
|
||||
|
||||
// Assumes ownership of an attribute value.
|
||||
class OwnedAttributeValue : public AttributeValue {
|
||||
public:
|
||||
|
||||
OwnedAttributeValue(const AttributePointer& attribute = AttributePointer());
|
||||
OwnedAttributeValue(const AttributePointer& attribute, void* value);
|
||||
OwnedAttributeValue(const AttributeValue& other);
|
||||
~OwnedAttributeValue();
|
||||
|
||||
OwnedAttributeValue& operator=(const AttributeValue& other);
|
||||
};
|
||||
|
||||
/// Represents a registered attribute.
|
||||
class Attribute : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
static const int MERGE_COUNT = 8;
|
||||
|
||||
Attribute(const QString& name);
|
||||
virtual ~Attribute();
|
||||
|
||||
Q_INVOKABLE QString getName() const { return objectName(); }
|
||||
|
||||
void* create() const { return create(getDefaultValue()); }
|
||||
virtual void* create(void* copy) const = 0;
|
||||
virtual void destroy(void* value) const = 0;
|
||||
|
||||
virtual bool read(Bitstream& in, void*& value) const = 0;
|
||||
virtual bool write(Bitstream& out, void* value) const = 0;
|
||||
|
||||
virtual bool equal(void* first, void* second) const = 0;
|
||||
|
||||
/// Merges the value of a parent and its children.
|
||||
/// \return whether or not the children and parent values are all equal
|
||||
virtual bool merge(void*& parent, void* children[]) const = 0;
|
||||
|
||||
virtual void* getDefaultValue() const = 0;
|
||||
|
||||
virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const { return create(); }
|
||||
};
|
||||
|
||||
/// A simple attribute class that stores its values inline.
|
||||
template<class T, int bits = 32> class InlineAttribute : public Attribute {
|
||||
public:
|
||||
|
||||
InlineAttribute(const QString& name, const T& defaultValue = T()) : Attribute(name), _defaultValue(defaultValue) { }
|
||||
|
||||
virtual void* create(void* copy) const { void* value; new (&value) T(*(T*)©); return value; }
|
||||
virtual void destroy(void* value) const { ((T*)&value)->~T(); }
|
||||
|
||||
virtual bool read(Bitstream& in, void*& value) const { value = getDefaultValue(); in.read(&value, bits); return false; }
|
||||
virtual bool write(Bitstream& out, void* value) const { out.write(&value, bits); return false; }
|
||||
|
||||
virtual bool equal(void* first, void* second) const { return decodeInline<T>(first) == decodeInline<T>(second); }
|
||||
|
||||
virtual void* getDefaultValue() const { return encodeInline(_defaultValue); }
|
||||
|
||||
private:
|
||||
|
||||
T _defaultValue;
|
||||
};
|
||||
|
||||
/// Provides merging using the =, ==, += and /= operators.
|
||||
template<class T, int bits = 32> class SimpleInlineAttribute : public InlineAttribute<T, bits> {
|
||||
public:
|
||||
|
||||
SimpleInlineAttribute(const QString& name, T defaultValue = T()) : InlineAttribute<T, bits>(name, defaultValue) { }
|
||||
|
||||
virtual bool merge(void*& parent, void* children[]) const;
|
||||
};
|
||||
|
||||
template<class T, int bits> inline bool SimpleInlineAttribute<T, bits>::merge(void*& parent, void* children[]) const {
|
||||
T& merged = *(T*)&parent;
|
||||
merged = decodeInline<T>(children[0]);
|
||||
bool allChildrenEqual = true;
|
||||
for (int i = 1; i < Attribute::MERGE_COUNT; i++) {
|
||||
merged += decodeInline<T>(children[i]);
|
||||
allChildrenEqual &= (decodeInline<T>(children[0]) == decodeInline<T>(children[i]));
|
||||
}
|
||||
merged /= Attribute::MERGE_COUNT;
|
||||
return allChildrenEqual;
|
||||
}
|
||||
|
||||
/// Provides appropriate averaging for RGBA values.
|
||||
class QRgbAttribute : public InlineAttribute<QRgb> {
|
||||
public:
|
||||
|
||||
QRgbAttribute(const QString& name, QRgb defaultValue = QRgb());
|
||||
|
||||
virtual bool merge(void*& parent, void* children[]) const;
|
||||
|
||||
virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const;
|
||||
};
|
||||
|
||||
/// 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 bool read(Bitstream& in, void*& value) const { in >> *static_cast<T*>(value); return true; }
|
||||
virtual bool write(Bitstream& out, void* value) const { out << *static_cast<T*>(value); return true; }
|
||||
|
||||
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); }
|
||||
|
||||
private:
|
||||
|
||||
T _defaultValue;
|
||||
};
|
||||
|
||||
/// 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;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__AttributeRegistry__) */
|
81
libraries/metavoxels/src/Bitstream.cpp
Normal file
81
libraries/metavoxels/src/Bitstream.cpp
Normal file
|
@ -0,0 +1,81 @@
|
|||
//
|
||||
// Bitstream.cpp
|
||||
// metavoxels
|
||||
//
|
||||
// Created by Andrzej Kapolka on 12/2/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QDataStream>
|
||||
|
||||
#include "Bitstream.h"
|
||||
|
||||
Bitstream::Bitstream(QDataStream& underlying)
|
||||
: _underlying(underlying), _byte(0), _position(0) {
|
||||
}
|
||||
|
||||
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) {
|
||||
const quint8* source = (const quint8*)data;
|
||||
while (bits > 0) {
|
||||
int bitsToWrite = qMin(BITS_IN_BYTE - _position, qMin(BITS_IN_BYTE - offset, bits));
|
||||
_byte |= ((*source >> offset) & ((1 << bitsToWrite) - 1)) << _position;
|
||||
if ((_position += bitsToWrite) == BITS_IN_BYTE) {
|
||||
flush();
|
||||
}
|
||||
if ((offset += bitsToWrite) == BITS_IN_BYTE) {
|
||||
source++;
|
||||
offset = 0;
|
||||
}
|
||||
bits -= bitsToWrite;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::read(void* data, int bits, int offset) {
|
||||
quint8* dest = (quint8*)data;
|
||||
while (bits > 0) {
|
||||
if (_position == 0) {
|
||||
_underlying >> _byte;
|
||||
}
|
||||
int bitsToRead = qMin(BITS_IN_BYTE - _position, qMin(BITS_IN_BYTE - offset, bits));
|
||||
*dest |= ((_byte >> _position) & ((1 << bitsToRead) - 1)) << offset;
|
||||
_position = (_position + bitsToRead) & LAST_BIT_POSITION;
|
||||
if ((offset += bitsToRead) == BITS_IN_BYTE) {
|
||||
dest++;
|
||||
offset = 0;
|
||||
}
|
||||
bits -= bitsToRead;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Bitstream::flush() {
|
||||
if (_position != 0) {
|
||||
_underlying << _byte;
|
||||
_byte = 0;
|
||||
_position = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Bitstream& Bitstream::operator<<(bool value) {
|
||||
if (value) {
|
||||
_byte |= (1 << _position);
|
||||
}
|
||||
if (++_position == BITS_IN_BYTE) {
|
||||
flush();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator>>(bool& value) {
|
||||
if (_position == 0) {
|
||||
_underlying >> _byte;
|
||||
}
|
||||
value = _byte & (1 << _position);
|
||||
_position = (_position + 1) & LAST_BIT_POSITION;
|
||||
return *this;
|
||||
}
|
45
libraries/metavoxels/src/Bitstream.h
Normal file
45
libraries/metavoxels/src/Bitstream.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// Bitstream.h
|
||||
// metavoxels
|
||||
//
|
||||
// Created by Andrzej Kapolka on 12/2/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__Bitstream__
|
||||
#define __interface__Bitstream__
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
class QDataStream;
|
||||
|
||||
/// A stream for bit-aligned data.
|
||||
class Bitstream {
|
||||
public:
|
||||
|
||||
Bitstream(QDataStream& underlying);
|
||||
|
||||
/// Writes a set of bits to the underlying stream.
|
||||
/// \param bits the number of bits to write
|
||||
/// \param offset the offset of the first bit
|
||||
Bitstream& write(const void* data, int bits, int offset = 0);
|
||||
|
||||
/// Reads a set of bits from the underlying stream.
|
||||
/// \param bits the number of bits to read
|
||||
/// \param offset the offset of the first bit
|
||||
Bitstream& read(void* data, int bits, int offset = 0);
|
||||
|
||||
/// Flushes any unwritten bits to the underlying stream.
|
||||
void flush();
|
||||
|
||||
Bitstream& operator<<(bool value);
|
||||
Bitstream& operator>>(bool& value);
|
||||
|
||||
private:
|
||||
|
||||
QDataStream& _underlying;
|
||||
quint8 _byte;
|
||||
int _position;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__Bitstream__) */
|
281
libraries/metavoxels/src/MetavoxelData.cpp
Normal file
281
libraries/metavoxels/src/MetavoxelData.cpp
Normal file
|
@ -0,0 +1,281 @@
|
|||
//
|
||||
// MetavoxelData.cpp
|
||||
// metavoxels
|
||||
//
|
||||
// Created by Andrzej Kapolka on 12/6/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QScriptEngine>
|
||||
#include <QtDebug>
|
||||
|
||||
#include "MetavoxelData.h"
|
||||
|
||||
MetavoxelData::~MetavoxelData() {
|
||||
for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) {
|
||||
it.value()->destroy(it.key());
|
||||
delete it.value();
|
||||
}
|
||||
}
|
||||
|
||||
void MetavoxelData::guide(MetavoxelVisitor& visitor) {
|
||||
// start with the root values/defaults (plus the guide attribute)
|
||||
const float TOP_LEVEL_SIZE = 1.0f;
|
||||
const QVector<AttributePointer>& attributes = visitor.getAttributes();
|
||||
MetavoxelVisitation firstVisitation = { visitor, QVector<MetavoxelNode*>(attributes.size() + 1),
|
||||
{ glm::vec3(), TOP_LEVEL_SIZE, QVector<AttributeValue>(attributes.size() + 1) } };
|
||||
for (int i = 0; i < attributes.size(); i++) {
|
||||
MetavoxelNode* node = _roots.value(attributes[i]);
|
||||
firstVisitation.nodes[i] = node;
|
||||
firstVisitation.info.attributeValues[i] = node ? node->getAttributeValue(attributes[i]) : attributes[i];
|
||||
}
|
||||
AttributePointer guideAttribute = AttributeRegistry::getInstance()->getGuideAttribute();
|
||||
MetavoxelNode* node = _roots.value(guideAttribute);
|
||||
firstVisitation.nodes.last() = node;
|
||||
firstVisitation.info.attributeValues.last() = node ? node->getAttributeValue(guideAttribute) : guideAttribute;
|
||||
static_cast<MetavoxelGuide*>(firstVisitation.info.attributeValues.last().getInlineValue<
|
||||
PolymorphicDataPointer>().data())->guide(firstVisitation);
|
||||
}
|
||||
|
||||
void MetavoxelData::setAttributeValue(const MetavoxelPath& path, const AttributeValue& attributeValue) {
|
||||
MetavoxelNode*& node = _roots[attributeValue.getAttribute()];
|
||||
if (node == NULL) {
|
||||
node = new MetavoxelNode(attributeValue.getAttribute());
|
||||
}
|
||||
if (node->setAttributeValue(path, 0, attributeValue) && attributeValue.isDefault()) {
|
||||
node->destroy(attributeValue.getAttribute());
|
||||
delete node;
|
||||
_roots.remove(attributeValue.getAttribute());
|
||||
}
|
||||
}
|
||||
|
||||
AttributeValue MetavoxelData::getAttributeValue(const MetavoxelPath& path, const AttributePointer& attribute) const {
|
||||
MetavoxelNode* node = _roots.value(attribute);
|
||||
if (node == NULL) {
|
||||
return AttributeValue(attribute);
|
||||
}
|
||||
for (int i = 0, n = path.getSize(); i < n; i++) {
|
||||
MetavoxelNode* child = node->getChild(path[i]);
|
||||
if (child == NULL) {
|
||||
return node->getAttributeValue(attribute);
|
||||
}
|
||||
node = child;
|
||||
}
|
||||
return node->getAttributeValue(attribute);
|
||||
}
|
||||
|
||||
MetavoxelNode::MetavoxelNode(const AttributeValue& attributeValue) {
|
||||
_attributeValue = attributeValue.copy();
|
||||
for (int i = 0; i < CHILD_COUNT; i++) {
|
||||
_children[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool MetavoxelNode::setAttributeValue(const MetavoxelPath& path, int index, const AttributeValue& attributeValue) {
|
||||
if (index == path.getSize()) {
|
||||
setAttributeValue(attributeValue);
|
||||
return true;
|
||||
}
|
||||
int element = path[index];
|
||||
if (_children[element] == NULL) {
|
||||
AttributeValue ownAttributeValue = getAttributeValue(attributeValue.getAttribute());
|
||||
for (int i = 0; i < CHILD_COUNT; i++) {
|
||||
_children[i] = new MetavoxelNode(ownAttributeValue);
|
||||
}
|
||||
}
|
||||
_children[element]->setAttributeValue(path, index + 1, attributeValue);
|
||||
|
||||
void* childValues[CHILD_COUNT];
|
||||
bool allLeaves = true;
|
||||
for (int i = 0; i < CHILD_COUNT; i++) {
|
||||
childValues[i] = _children[i]->_attributeValue;
|
||||
allLeaves &= _children[i]->isLeaf();
|
||||
}
|
||||
if (attributeValue.getAttribute()->merge(_attributeValue, childValues) && allLeaves) {
|
||||
clearChildren(attributeValue.getAttribute());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MetavoxelNode::setAttributeValue(const AttributeValue& attributeValue) {
|
||||
attributeValue.getAttribute()->destroy(_attributeValue);
|
||||
_attributeValue = attributeValue.copy();
|
||||
clearChildren(attributeValue.getAttribute());
|
||||
}
|
||||
|
||||
AttributeValue MetavoxelNode::getAttributeValue(const AttributePointer& attribute) const {
|
||||
return AttributeValue(attribute, _attributeValue);
|
||||
}
|
||||
|
||||
bool MetavoxelNode::isLeaf() const {
|
||||
for (int i = 0; i < CHILD_COUNT; i++) {
|
||||
if (_children[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MetavoxelNode::destroy(const AttributePointer& attribute) {
|
||||
attribute->destroy(_attributeValue);
|
||||
for (int i = 0; i < CHILD_COUNT; i++) {
|
||||
if (_children[i]) {
|
||||
_children[i]->destroy(attribute);
|
||||
delete _children[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MetavoxelNode::clearChildren(const AttributePointer& attribute) {
|
||||
for (int i = 0; i < CHILD_COUNT; i++) {
|
||||
if (_children[i]) {
|
||||
_children[i]->destroy(attribute);
|
||||
delete _children[i];
|
||||
_children[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int MetavoxelPath::operator[](int index) const {
|
||||
return _array.at(index * BITS_PER_ELEMENT) | (_array.at(index * BITS_PER_ELEMENT + 1) << 1) |
|
||||
(_array.at(index * BITS_PER_ELEMENT + 2) << 2);
|
||||
}
|
||||
|
||||
MetavoxelPath& MetavoxelPath::operator+=(int element) {
|
||||
int offset = _array.size();
|
||||
_array.resize(offset + BITS_PER_ELEMENT);
|
||||
_array.setBit(offset, element & 0x01);
|
||||
_array.setBit(offset + 1, (element >> 1) & 0x01);
|
||||
_array.setBit(offset + 2, element >> 2);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PolymorphicData* DefaultMetavoxelGuide::clone() const {
|
||||
return new DefaultMetavoxelGuide();
|
||||
}
|
||||
|
||||
const int X_MAXIMUM_FLAG = 1;
|
||||
const int Y_MAXIMUM_FLAG = 2;
|
||||
const int Z_MAXIMUM_FLAG = 4;
|
||||
|
||||
void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
|
||||
visitation.info.isLeaf = visitation.allNodesLeaves();
|
||||
if (!visitation.visitor.visit(visitation.info) || visitation.info.isLeaf) {
|
||||
return;
|
||||
}
|
||||
MetavoxelVisitation nextVisitation = { visitation.visitor, QVector<MetavoxelNode*>(visitation.nodes.size()),
|
||||
{ glm::vec3(), visitation.info.size * 0.5f, QVector<AttributeValue>(visitation.nodes.size()) } };
|
||||
for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) {
|
||||
for (int j = 0; j < visitation.nodes.size(); j++) {
|
||||
MetavoxelNode* node = visitation.nodes.at(j);
|
||||
MetavoxelNode* child = node ? node->getChild(i) : NULL;
|
||||
nextVisitation.info.attributeValues[j] = ((nextVisitation.nodes[j] = child)) ?
|
||||
child->getAttributeValue(visitation.info.attributeValues[j].getAttribute()) :
|
||||
visitation.info.attributeValues[j];
|
||||
}
|
||||
nextVisitation.info.minimum = visitation.info.minimum + glm::vec3(
|
||||
(i & X_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f,
|
||||
(i & Y_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f,
|
||||
(i & Z_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f);
|
||||
static_cast<MetavoxelGuide*>(nextVisitation.info.attributeValues.last().getInlineValue<
|
||||
PolymorphicDataPointer>().data())->guide(nextVisitation);
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue ScriptedMetavoxelGuide::getAttributes(QScriptContext* context, QScriptEngine* engine) {
|
||||
ScriptedMetavoxelGuide* guide = static_cast<ScriptedMetavoxelGuide*>(context->callee().data().toVariant().value<void*>());
|
||||
|
||||
const QVector<AttributePointer>& attributes = guide->_visitation->visitor.getAttributes();
|
||||
QScriptValue attributesValue = engine->newArray(attributes.size());
|
||||
for (int i = 0; i < attributes.size(); i++) {
|
||||
attributesValue.setProperty(i, engine->newQObject(attributes.at(i).data(), QScriptEngine::QtOwnership,
|
||||
QScriptEngine::PreferExistingWrapperObject));
|
||||
}
|
||||
|
||||
return attributesValue;
|
||||
}
|
||||
|
||||
QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngine* engine) {
|
||||
ScriptedMetavoxelGuide* guide = static_cast<ScriptedMetavoxelGuide*>(context->callee().data().toVariant().value<void*>());
|
||||
|
||||
// start with the basics, including inherited attribute values
|
||||
QScriptValue infoValue = context->argument(0);
|
||||
QScriptValue minimum = infoValue.property(guide->_minimumHandle);
|
||||
MetavoxelInfo info = {
|
||||
glm::vec3(minimum.property(0).toNumber(), minimum.property(1).toNumber(), minimum.property(2).toNumber()),
|
||||
infoValue.property(guide->_sizeHandle).toNumber(), guide->_visitation->info.attributeValues,
|
||||
infoValue.property(guide->_isLeafHandle).toBool() };
|
||||
|
||||
// extract and convert the values provided by the script
|
||||
QScriptValue attributeValues = infoValue.property(guide->_attributeValuesHandle);
|
||||
const QVector<AttributePointer>& attributes = guide->_visitation->visitor.getAttributes();
|
||||
for (int i = 0; i < attributes.size(); i++) {
|
||||
QScriptValue attributeValue = attributeValues.property(i);
|
||||
if (attributeValue.isValid()) {
|
||||
info.attributeValues[i] = AttributeValue(attributes.at(i),
|
||||
attributes.at(i)->createFromScript(attributeValue, engine));
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue result = guide->_visitation->visitor.visit(info);
|
||||
|
||||
// destroy any created values
|
||||
for (int i = 0; i < attributes.size(); i++) {
|
||||
if (attributeValues.property(i).isValid()) {
|
||||
info.attributeValues[i].getAttribute()->destroy(info.attributeValues[i].getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ScriptedMetavoxelGuide::ScriptedMetavoxelGuide(const QScriptValue& guideFunction) :
|
||||
_guideFunction(guideFunction),
|
||||
_minimumHandle(guideFunction.engine()->toStringHandle("minimum")),
|
||||
_sizeHandle(guideFunction.engine()->toStringHandle("size")),
|
||||
_attributeValuesHandle(guideFunction.engine()->toStringHandle("attributeValues")),
|
||||
_isLeafHandle(guideFunction.engine()->toStringHandle("isLeaf")),
|
||||
_getAttributesFunction(guideFunction.engine()->newFunction(getAttributes, 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("getAttributes", _getAttributesFunction);
|
||||
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);
|
||||
}
|
||||
|
||||
void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
|
||||
QScriptValue data = _guideFunction.engine()->newVariant(QVariant::fromValue<void*>(this));
|
||||
_getAttributesFunction.setData(data);
|
||||
_visitFunction.setData(data);
|
||||
_minimum.setProperty(0, visitation.info.minimum.x);
|
||||
_minimum.setProperty(1, visitation.info.minimum.y);
|
||||
_minimum.setProperty(2, visitation.info.minimum.z);
|
||||
_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() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
bool MetavoxelVisitation::allNodesLeaves() const {
|
||||
foreach (MetavoxelNode* node, nodes) {
|
||||
if (node != NULL && !node->isLeaf()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
187
libraries/metavoxels/src/MetavoxelData.h
Normal file
187
libraries/metavoxels/src/MetavoxelData.h
Normal file
|
@ -0,0 +1,187 @@
|
|||
//
|
||||
// MetavoxelData.h
|
||||
// metavoxels
|
||||
//
|
||||
// Created by Andrzej Kapolka on 12/6/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__MetavoxelData__
|
||||
#define __interface__MetavoxelData__
|
||||
|
||||
#include <QBitArray>
|
||||
#include <QHash>
|
||||
#include <QScriptString>
|
||||
#include <QScriptValue>
|
||||
#include <QVector>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "AttributeRegistry.h"
|
||||
|
||||
class QScriptContext;
|
||||
|
||||
class MetavoxelNode;
|
||||
class MetavoxelPath;
|
||||
class MetavoxelVisitation;
|
||||
class MetavoxelVisitor;
|
||||
|
||||
/// The base metavoxel representation shared between server and client.
|
||||
class MetavoxelData {
|
||||
public:
|
||||
|
||||
~MetavoxelData();
|
||||
|
||||
/// Applies the specified visitor to the contained voxels.
|
||||
void guide(MetavoxelVisitor& visitor);
|
||||
|
||||
/// Sets the attribute value corresponding to the specified path.
|
||||
void setAttributeValue(const MetavoxelPath& path, const AttributeValue& attributeValue);
|
||||
|
||||
/// Retrieves the attribute value corresponding to the specified path.
|
||||
AttributeValue getAttributeValue(const MetavoxelPath& path, const AttributePointer& attribute) const;
|
||||
|
||||
private:
|
||||
|
||||
QHash<AttributePointer, MetavoxelNode*> _roots;
|
||||
};
|
||||
|
||||
/// A single node within a metavoxel layer.
|
||||
class MetavoxelNode {
|
||||
public:
|
||||
|
||||
static const int CHILD_COUNT = 8;
|
||||
|
||||
MetavoxelNode(const AttributeValue& attributeValue);
|
||||
|
||||
/// Descends the voxel tree in order to set the value of a node.
|
||||
/// \param path the path to follow
|
||||
/// \param index the position in the path
|
||||
/// \return whether or not the node is entirely equal to the value
|
||||
bool setAttributeValue(const MetavoxelPath& path, int index, const AttributeValue& attributeValue);
|
||||
|
||||
void setAttributeValue(const AttributeValue& attributeValue);
|
||||
|
||||
AttributeValue getAttributeValue(const AttributePointer& attribute) const;
|
||||
|
||||
MetavoxelNode* getChild(int index) const { return _children[index]; }
|
||||
void setChild(int index, MetavoxelNode* child) { _children[index] = child; }
|
||||
|
||||
bool isLeaf() const;
|
||||
|
||||
void destroy(const AttributePointer& attribute);
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(MetavoxelNode)
|
||||
|
||||
void clearChildren(const AttributePointer& attribute);
|
||||
|
||||
void* _attributeValue;
|
||||
MetavoxelNode* _children[CHILD_COUNT];
|
||||
};
|
||||
|
||||
/// A path down an octree.
|
||||
class MetavoxelPath {
|
||||
public:
|
||||
|
||||
int getSize() const { return _array.size() / BITS_PER_ELEMENT; }
|
||||
bool isEmpty() const { return _array.isEmpty(); }
|
||||
|
||||
int operator[](int index) const;
|
||||
|
||||
MetavoxelPath& operator+=(int element);
|
||||
|
||||
private:
|
||||
|
||||
static const int BITS_PER_ELEMENT = 3;
|
||||
|
||||
QBitArray _array;
|
||||
};
|
||||
|
||||
/// Contains information about a metavoxel (explicit or procedural).
|
||||
class MetavoxelInfo {
|
||||
public:
|
||||
|
||||
glm::vec3 minimum; ///< the minimum extent of the area covered by the voxel
|
||||
float size; ///< the size of the voxel in all dimensions
|
||||
QVector<AttributeValue> attributeValues;
|
||||
bool isLeaf;
|
||||
};
|
||||
|
||||
/// Interface for visitors to metavoxels.
|
||||
class MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
MetavoxelVisitor(const QVector<AttributePointer>& attributes) : _attributes(attributes) { }
|
||||
|
||||
/// Returns a reference to the list of attributes desired.
|
||||
const QVector<AttributePointer>& getAttributes() const { return _attributes; }
|
||||
|
||||
/// Visits a metavoxel.
|
||||
/// \param info the metavoxel ata
|
||||
/// \param if true, continue descending; if false, stop
|
||||
virtual bool visit(const MetavoxelInfo& info) = 0;
|
||||
|
||||
protected:
|
||||
|
||||
QVector<AttributePointer> _attributes;
|
||||
};
|
||||
|
||||
/// Interface for objects that guide metavoxel visitors.
|
||||
class MetavoxelGuide : public PolymorphicData {
|
||||
public:
|
||||
|
||||
/// Guides the specified visitor to the contained voxels.
|
||||
virtual void guide(MetavoxelVisitation& visitation) = 0;
|
||||
};
|
||||
|
||||
/// Guides visitors through the explicit content of the system.
|
||||
class DefaultMetavoxelGuide : public MetavoxelGuide {
|
||||
public:
|
||||
|
||||
virtual PolymorphicData* clone() const;
|
||||
|
||||
virtual void guide(MetavoxelVisitation& visitation);
|
||||
};
|
||||
|
||||
/// Represents a guide implemented in Javascript.
|
||||
class ScriptedMetavoxelGuide : public MetavoxelGuide {
|
||||
public:
|
||||
|
||||
ScriptedMetavoxelGuide(const QScriptValue& guideFunction);
|
||||
|
||||
virtual PolymorphicData* clone() const;
|
||||
|
||||
virtual void guide(MetavoxelVisitation& visitation);
|
||||
|
||||
private:
|
||||
|
||||
static QScriptValue getAttributes(QScriptContext* context, QScriptEngine* engine);
|
||||
static QScriptValue visit(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
QScriptValue _guideFunction;
|
||||
QScriptString _minimumHandle;
|
||||
QScriptString _sizeHandle;
|
||||
QScriptString _attributeValuesHandle;
|
||||
QScriptString _isLeafHandle;
|
||||
QScriptValueList _arguments;
|
||||
QScriptValue _getAttributesFunction;
|
||||
QScriptValue _visitFunction;
|
||||
QScriptValue _info;
|
||||
QScriptValue _minimum;
|
||||
|
||||
MetavoxelVisitation* _visitation;
|
||||
};
|
||||
|
||||
/// Contains the state associated with a visit to a metavoxel system.
|
||||
class MetavoxelVisitation {
|
||||
public:
|
||||
|
||||
MetavoxelVisitor& visitor;
|
||||
QVector<MetavoxelNode*> nodes;
|
||||
MetavoxelInfo info;
|
||||
|
||||
bool allNodesLeaves() const;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__MetavoxelData__) */
|
|
@ -656,6 +656,17 @@ void OctreeServer::run() {
|
|||
_persistThread->initialize(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Debug option to demonstrate that the server's local time does not
|
||||
// need to be in sync with any other network node. This forces clock
|
||||
// skew for the individual server node
|
||||
const char* CLOCK_SKEW = "--clockSkew";
|
||||
const char* clockSkewOption = getCmdOption(_argc, _argv, CLOCK_SKEW);
|
||||
if (clockSkewOption) {
|
||||
int clockSkew = atoi(clockSkewOption);
|
||||
usecTimestampNowForceClockSkew(clockSkew);
|
||||
qDebug("clockSkewOption=%s clockSkew=%d\n", clockSkewOption, clockSkew);
|
||||
}
|
||||
|
||||
// Check to see if the user passed in a command line option for setting packet send rate
|
||||
const char* PACKETS_PER_SECOND = "--packetsPerSecond";
|
||||
|
|
|
@ -36,15 +36,16 @@ Particle::~Particle() {
|
|||
void Particle::init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, glm::vec3 gravity,
|
||||
float damping, bool inHand, QString updateScript, uint32_t id) {
|
||||
if (id == NEW_PARTICLE) {
|
||||
_created = usecTimestampNow();
|
||||
_id = _nextID;
|
||||
_nextID++;
|
||||
} else {
|
||||
_id = id;
|
||||
}
|
||||
_lastUpdated = usecTimestampNow();
|
||||
_lastEdited = _lastUpdated;
|
||||
|
||||
uint64_t now = usecTimestampNow();
|
||||
_edited = now;
|
||||
_lastSimulated = now;
|
||||
_created = now; // will get updated as appropriate in setLifetime()
|
||||
|
||||
_position = position;
|
||||
_radius = radius;
|
||||
memcpy(_color, color, sizeof(_color));
|
||||
|
@ -63,13 +64,10 @@ bool Particle::appendParticleData(OctreePacketData* packetData) const {
|
|||
//printf("Particle::appendParticleData()... getID()=%d\n", getID());
|
||||
|
||||
if (success) {
|
||||
success = packetData->appendValue(getCreated());
|
||||
success = packetData->appendValue(getLifetime());
|
||||
}
|
||||
if (success) {
|
||||
success = packetData->appendValue(getLastUpdated());
|
||||
}
|
||||
if (success) {
|
||||
success = packetData->appendValue(getLastEdited());
|
||||
success = packetData->appendValue(getEditedAgo());
|
||||
}
|
||||
if (success) {
|
||||
success = packetData->appendValue(getRadius());
|
||||
|
@ -103,9 +101,30 @@ bool Particle::appendParticleData(OctreePacketData* packetData) const {
|
|||
}
|
||||
|
||||
int Particle::expectedBytes() {
|
||||
int expectedBytes = sizeof(uint32_t) + sizeof(uint64_t) + sizeof(uint64_t) + sizeof(uint64_t) + sizeof(float) +
|
||||
sizeof(glm::vec3) + sizeof(rgbColor) + sizeof(glm::vec3) +
|
||||
sizeof(glm::vec3) + sizeof(float) + sizeof(bool);
|
||||
int expectedBytes = sizeof(uint32_t) // id
|
||||
+ sizeof(float) // lifetime
|
||||
+ sizeof(float) // edited ago
|
||||
+ sizeof(float) // radius
|
||||
+ sizeof(glm::vec3) // position
|
||||
+ sizeof(rgbColor) // color
|
||||
+ sizeof(glm::vec3) // velocity
|
||||
+ sizeof(glm::vec3) // gravity
|
||||
+ sizeof(float) // damping
|
||||
+ sizeof(bool); // inhand
|
||||
// potentially more...
|
||||
return expectedBytes;
|
||||
}
|
||||
|
||||
int Particle::expectedEditMessageBytes() {
|
||||
int expectedBytes = sizeof(uint32_t) // id
|
||||
+ sizeof(float) // radius
|
||||
+ sizeof(glm::vec3) // position
|
||||
+ sizeof(rgbColor) // color
|
||||
+ sizeof(glm::vec3) // velocity
|
||||
+ sizeof(glm::vec3) // gravity
|
||||
+ sizeof(float) // damping
|
||||
+ sizeof(bool); // inhand
|
||||
// potentially more...
|
||||
return expectedBytes;
|
||||
}
|
||||
|
||||
|
@ -119,20 +138,19 @@ int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
dataAt += sizeof(_id);
|
||||
bytesRead += sizeof(_id);
|
||||
|
||||
// created
|
||||
memcpy(&_created, dataAt, sizeof(_created));
|
||||
dataAt += sizeof(_created);
|
||||
bytesRead += sizeof(_created);
|
||||
// lifetime
|
||||
float lifetime;
|
||||
memcpy(&lifetime, dataAt, sizeof(lifetime));
|
||||
dataAt += sizeof(lifetime);
|
||||
bytesRead += sizeof(lifetime);
|
||||
setLifetime(lifetime);
|
||||
|
||||
// lastupdated
|
||||
memcpy(&_lastUpdated, dataAt, sizeof(_lastUpdated));
|
||||
dataAt += sizeof(_lastUpdated);
|
||||
bytesRead += sizeof(_lastUpdated);
|
||||
|
||||
// _lastEdited
|
||||
memcpy(&_lastEdited, dataAt, sizeof(_lastEdited));
|
||||
dataAt += sizeof(_lastEdited);
|
||||
bytesRead += sizeof(_lastEdited);
|
||||
// edited ago
|
||||
float editedAgo;
|
||||
memcpy(&editedAgo, dataAt, sizeof(editedAgo));
|
||||
dataAt += sizeof(editedAgo);
|
||||
bytesRead += sizeof(editedAgo);
|
||||
setEditedAgo(editedAgo);
|
||||
|
||||
// radius
|
||||
memcpy(&_radius, dataAt, sizeof(_radius));
|
||||
|
@ -186,7 +204,7 @@ int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
|
||||
|
||||
Particle Particle::fromEditPacket(unsigned char* data, int length, int& processedBytes) {
|
||||
Particle newParticle; // id and lastUpdated will get set here...
|
||||
Particle newParticle; // id and _lastSimulated will get set here...
|
||||
unsigned char* dataAt = data;
|
||||
processedBytes = 0;
|
||||
|
||||
|
@ -214,26 +232,17 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe
|
|||
processedBytes += sizeof(creatorTokenID);
|
||||
newParticle.setCreatorTokenID(creatorTokenID);
|
||||
newParticle._newlyCreated = true;
|
||||
|
||||
newParticle.setLifetime(0); // this guy is new!
|
||||
|
||||
} else {
|
||||
newParticle._id = editID;
|
||||
newParticle._newlyCreated = false;
|
||||
}
|
||||
|
||||
// created
|
||||
memcpy(&newParticle._created, dataAt, sizeof(newParticle._created));
|
||||
dataAt += sizeof(newParticle._created);
|
||||
processedBytes += sizeof(newParticle._created);
|
||||
|
||||
// lastUpdated
|
||||
memcpy(&newParticle._lastUpdated, dataAt, sizeof(newParticle._lastUpdated));
|
||||
dataAt += sizeof(newParticle._lastUpdated);
|
||||
processedBytes += sizeof(newParticle._lastUpdated);
|
||||
|
||||
// lastEdited
|
||||
memcpy(&newParticle._lastEdited, dataAt, sizeof(newParticle._lastEdited));
|
||||
dataAt += sizeof(newParticle._lastEdited);
|
||||
processedBytes += sizeof(newParticle._lastEdited);
|
||||
|
||||
// clearly we just edited it
|
||||
newParticle.setEditedAgo(0);
|
||||
|
||||
// radius
|
||||
memcpy(&newParticle._radius, dataAt, sizeof(newParticle._radius));
|
||||
dataAt += sizeof(newParticle._radius);
|
||||
|
@ -291,9 +300,8 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe
|
|||
|
||||
void Particle::debugDump() const {
|
||||
printf("Particle id :%u\n", _id);
|
||||
printf(" created:%llu\n", _created);
|
||||
printf(" last updated:%llu\n", _lastUpdated);
|
||||
printf(" last edited:%llu\n", _lastEdited);
|
||||
printf(" lifetime:%f\n", getLifetime());
|
||||
printf(" edited ago:%f\n", getEditedAgo());
|
||||
printf(" position:%f,%f,%f\n", _position.x, _position.y, _position.z);
|
||||
printf(" velocity:%f,%f,%f\n", _velocity.x, _velocity.y, _velocity.z);
|
||||
printf(" gravity:%f,%f,%f\n", _gravity.x, _gravity.y, _gravity.z);
|
||||
|
@ -313,7 +321,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count,
|
|||
|
||||
int octets = numberOfThreeBitSectionsInCode(octcode);
|
||||
int lengthOfOctcode = bytesRequiredForCodeLength(octets);
|
||||
int lenfthOfEditData = lengthOfOctcode + expectedBytes();
|
||||
int lenfthOfEditData = lengthOfOctcode + expectedEditMessageBytes();
|
||||
|
||||
// make sure we have room to copy this particle
|
||||
if (sizeOut + lenfthOfEditData > sizeIn) {
|
||||
|
@ -325,7 +333,6 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count,
|
|||
sizeOut += lengthOfOctcode;
|
||||
|
||||
// Now add our edit content details...
|
||||
uint64_t created = usecTimestampNow();
|
||||
|
||||
// id
|
||||
memcpy(copyAt, &details[i].id, sizeof(details[i].id));
|
||||
|
@ -338,25 +345,8 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count,
|
|||
memcpy(copyAt, &details[i].creatorTokenID, sizeof(details[i].creatorTokenID));
|
||||
copyAt += sizeof(details[i].creatorTokenID);
|
||||
sizeOut += sizeof(details[i].creatorTokenID);
|
||||
} else {
|
||||
created = 0;
|
||||
}
|
||||
|
||||
// created
|
||||
memcpy(copyAt, &created, sizeof(created));
|
||||
copyAt += sizeof(created);
|
||||
sizeOut += sizeof(created);
|
||||
|
||||
// lastUpdated
|
||||
memcpy(copyAt, &details[i].lastUpdated, sizeof(details[i].lastUpdated));
|
||||
copyAt += sizeof(details[i].lastUpdated);
|
||||
sizeOut += sizeof(details[i].lastUpdated);
|
||||
|
||||
// lastEdited
|
||||
memcpy(copyAt, &details[i].lastEdited, sizeof(details[i].lastEdited));
|
||||
copyAt += sizeof(details[i].lastEdited);
|
||||
sizeOut += sizeof(details[i].lastEdited);
|
||||
|
||||
// radius
|
||||
memcpy(copyAt, &details[i].radius, sizeof(details[i].radius));
|
||||
copyAt += sizeof(details[i].radius);
|
||||
|
@ -405,7 +395,6 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count,
|
|||
if (wantDebugging) {
|
||||
printf("encodeParticleEditMessageDetails()....\n");
|
||||
printf("Particle id :%u\n", details[i].id);
|
||||
printf(" last updated:%llu\n", details[i].lastUpdated);
|
||||
printf(" nextID:%u\n", _nextID);
|
||||
}
|
||||
}
|
||||
|
@ -418,21 +407,30 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count,
|
|||
|
||||
|
||||
void Particle::update() {
|
||||
|
||||
uint64_t now = usecTimestampNow();
|
||||
uint64_t elapsed = now - _lastUpdated;
|
||||
uint64_t USECS_PER_SECOND = 1000 * 1000;
|
||||
uint64_t elapsed = now - _lastSimulated;
|
||||
float timeElapsed = (float)((float)elapsed/(float)USECS_PER_SECOND);
|
||||
|
||||
|
||||
// calculate our default shouldDie state... then allow script to change it if it wants...
|
||||
float velocityScalar = glm::length(getVelocity());
|
||||
const float STILL_MOVING = 0.05 / TREE_SCALE;
|
||||
bool isStillMoving = (velocityScalar > STILL_MOVING);
|
||||
const uint64_t REALLY_OLD = 30 * 1000 * 1000;
|
||||
const float REALLY_OLD = 30.0f; // 30 seconds
|
||||
bool isReallyOld = (getLifetime() > REALLY_OLD);
|
||||
bool isInHand = getInHand();
|
||||
bool shouldDie = !isInHand && !isStillMoving && isReallyOld;
|
||||
setShouldDie(shouldDie);
|
||||
|
||||
|
||||
bool wantDebug = false;
|
||||
if (wantDebug) {
|
||||
printf("Particle::update()... timeElapsed: %f lifeTime:%f editedAgo:%f "
|
||||
"isInHand:%s isStillMoveing:%s isReallyOld:%s shouldDie:%s\n",
|
||||
timeElapsed, getLifetime(), getEditedAgo(), debug::valueOf(isInHand), debug::valueOf(isStillMoving),
|
||||
debug::valueOf(isReallyOld), debug::valueOf(shouldDie));
|
||||
}
|
||||
|
||||
runScript(); // allow the javascript to alter our state
|
||||
|
||||
// If the ball is in hand, it doesn't move or have gravity effect it
|
||||
|
@ -454,7 +452,7 @@ void Particle::update() {
|
|||
//printf("applying damping to Particle timeElapsed=%f\n",timeElapsed);
|
||||
}
|
||||
|
||||
_lastUpdated = now;
|
||||
_lastSimulated = now;
|
||||
}
|
||||
|
||||
void Particle::runScript() {
|
||||
|
@ -484,3 +482,19 @@ void Particle::runScript() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Particle::setLifetime(float lifetime) {
|
||||
uint64_t lifetimeInUsecs = lifetime * USECS_PER_SECOND;
|
||||
_created = usecTimestampNow() - lifetimeInUsecs;
|
||||
}
|
||||
|
||||
void Particle::setEditedAgo(float editedAgo) {
|
||||
uint64_t editedAgoInUsecs = editedAgo * USECS_PER_SECOND;
|
||||
_edited = usecTimestampNow() - editedAgoInUsecs;
|
||||
}
|
||||
|
||||
void Particle::copyChangedProperties(const Particle& other) {
|
||||
float lifetime = getLifetime();
|
||||
*this = other;
|
||||
setLifetime(lifetime);
|
||||
}
|
||||
|
|
|
@ -25,8 +25,6 @@ const uint32_t UNKNOWN_TOKEN = 0xFFFFFFFF;
|
|||
class ParticleDetail {
|
||||
public:
|
||||
uint32_t id;
|
||||
uint64_t lastUpdated;
|
||||
uint64_t lastEdited;
|
||||
glm::vec3 position;
|
||||
float radius;
|
||||
rgbColor color;
|
||||
|
@ -68,10 +66,11 @@ public:
|
|||
const glm::vec3& getGravity() const { return _gravity; }
|
||||
bool getInHand() const { return _inHand; }
|
||||
float getDamping() const { return _damping; }
|
||||
uint64_t getCreated() const { return _created; }
|
||||
uint64_t getLifetime() const { return usecTimestampNow() - _created; }
|
||||
uint64_t getLastUpdated() const { return _lastUpdated; }
|
||||
uint64_t getLastEdited() const { return _lastEdited; }
|
||||
|
||||
/// lifetime of the particle in seconds
|
||||
float getLifetime() const { return (float)(usecTimestampNow() - _created) / (float)USECS_PER_SECOND; }
|
||||
/// seconds since last edited
|
||||
float getEditedAgo() const { return (float)(usecTimestampNow() - _edited) / (float)USECS_PER_SECOND; }
|
||||
uint32_t getID() const { return _id; }
|
||||
bool getShouldDie() const { return _shouldDie; }
|
||||
QString getUpdateScript() const { return _updateScript; }
|
||||
|
@ -93,11 +92,11 @@ public:
|
|||
void setShouldDie(bool shouldDie) { _shouldDie = shouldDie; }
|
||||
void setUpdateScript(QString updateScript) { _updateScript = updateScript; }
|
||||
void setCreatorTokenID(uint32_t creatorTokenID) { _creatorTokenID = creatorTokenID; }
|
||||
void setCreated(uint64_t created) { _created = created; }
|
||||
|
||||
bool appendParticleData(OctreePacketData* packetData) const;
|
||||
int readParticleDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args);
|
||||
static int expectedBytes();
|
||||
static int expectedEditMessageBytes();
|
||||
|
||||
static bool encodeParticleEditMessageDetails(PACKET_TYPE command, int count, const ParticleDetail* details,
|
||||
unsigned char* bufferOut, int sizeIn, int& sizeOut);
|
||||
|
@ -105,20 +104,24 @@ public:
|
|||
void update();
|
||||
|
||||
void debugDump() const;
|
||||
|
||||
// similar to assignment/copy, but it handles keeping lifetime accurate
|
||||
void copyChangedProperties(const Particle& other);
|
||||
|
||||
protected:
|
||||
void runScript();
|
||||
static QScriptValue vec3toScriptValue(QScriptEngine *engine, const glm::vec3 &vec3);
|
||||
static void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3);
|
||||
static QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color);
|
||||
static void xColorFromScriptValue(const QScriptValue &object, xColor& color);
|
||||
|
||||
void setLifetime(float lifetime);
|
||||
void setEditedAgo(float editedAgo);
|
||||
|
||||
glm::vec3 _position;
|
||||
rgbColor _color;
|
||||
float _radius;
|
||||
glm::vec3 _velocity;
|
||||
uint64_t _lastUpdated;
|
||||
uint64_t _created;
|
||||
uint64_t _lastEdited;
|
||||
uint32_t _id;
|
||||
static uint32_t _nextID;
|
||||
bool _shouldDie;
|
||||
|
@ -129,6 +132,12 @@ protected:
|
|||
|
||||
uint32_t _creatorTokenID;
|
||||
bool _newlyCreated;
|
||||
|
||||
// these are never included in wire time
|
||||
uint64_t _lastSimulated;
|
||||
uint64_t _created;
|
||||
uint64_t _edited;
|
||||
|
||||
};
|
||||
|
||||
class ParticleScriptObject : public QObject {
|
||||
|
@ -144,9 +153,7 @@ public slots:
|
|||
float getDamping() const { return _particle->getDamping(); }
|
||||
float getRadius() const { return _particle->getRadius(); }
|
||||
bool getShouldDie() { return _particle->getShouldDie(); }
|
||||
float getCreated() const { return ((float)_particle->getCreated() / (float)USECS_PER_SECOND); }
|
||||
float getLifetime() const { return ((float)_particle->getLifetime() / (float)USECS_PER_SECOND); }
|
||||
|
||||
float getLifetime() const { return _particle->getLifetime(); }
|
||||
|
||||
void setPosition(glm::vec3 value) { _particle->setPosition(value); }
|
||||
void setVelocity(glm::vec3 value) { _particle->setVelocity(value); }
|
||||
|
|
|
@ -206,7 +206,7 @@ void ParticleCollisionSystem::applyHardCollision(Particle* particle, const glm::
|
|||
void ParticleCollisionSystem::updateCollisionSound(Particle* particle, const glm::vec3 &penetration, float frequency) {
|
||||
|
||||
// consider whether to have the collision make a sound
|
||||
const float AUDIBLE_COLLISION_THRESHOLD = 0.02f;
|
||||
const float AUDIBLE_COLLISION_THRESHOLD = 0.1f;
|
||||
const float COLLISION_LOUDNESS = 1.f;
|
||||
const float DURATION_SCALING = 0.004f;
|
||||
const float NOISE_SCALING = 0.1f;
|
||||
|
@ -235,6 +235,6 @@ void ParticleCollisionSystem::updateCollisionSound(Particle* particle, const glm
|
|||
fmin(COLLISION_LOUDNESS * velocityTowardCollision, 1.f),
|
||||
frequency * (1.f + velocityTangentToCollision / velocityTowardCollision),
|
||||
fmin(velocityTangentToCollision / velocityTowardCollision * NOISE_SCALING, 1.f),
|
||||
1.f - DURATION_SCALING * powf(frequency, 0.5f) / velocityTowardCollision, true);
|
||||
1.f - DURATION_SCALING * powf(frequency, 0.5f) / velocityTowardCollision, false);
|
||||
}
|
||||
}
|
|
@ -44,8 +44,7 @@ void ParticleEditHandle::createParticle(glm::vec3 position, float radius, xColor
|
|||
glm::vec3 gravity, float damping, bool inHand, QString updateScript) {
|
||||
|
||||
// setup a ParticleDetail struct with the data
|
||||
uint64_t now = usecTimestampNow();
|
||||
ParticleDetail addParticleDetail = { NEW_PARTICLE, now, now,
|
||||
ParticleDetail addParticleDetail = { NEW_PARTICLE,
|
||||
position, radius, {color.red, color.green, color.blue },
|
||||
velocity, gravity, damping, inHand, updateScript, _creatorTokenID };
|
||||
|
||||
|
@ -70,8 +69,7 @@ bool ParticleEditHandle::updateParticle(glm::vec3 position, float radius, xColor
|
|||
}
|
||||
|
||||
// setup a ParticleDetail struct with the data
|
||||
uint64_t now = usecTimestampNow();
|
||||
ParticleDetail newParticleDetail = { _id, now, now,
|
||||
ParticleDetail newParticleDetail = { _id,
|
||||
position, radius, {color.red, color.green, color.blue },
|
||||
velocity, gravity, damping, inHand, updateScript, _creatorTokenID };
|
||||
|
||||
|
|
|
@ -22,8 +22,7 @@ unsigned int ParticleScriptingInterface::queueParticleAdd(glm::vec3 position, fl
|
|||
_nextCreatorTokenID++;
|
||||
|
||||
// setup a ParticleDetail struct with the data
|
||||
uint64_t now = usecTimestampNow();
|
||||
ParticleDetail addParticleDetail = { NEW_PARTICLE, now, now,
|
||||
ParticleDetail addParticleDetail = { NEW_PARTICLE,
|
||||
position, radius, {color.red, color.green, color.blue }, velocity,
|
||||
gravity, damping, inHand, updateScript, creatorTokenID };
|
||||
|
||||
|
|
|
@ -119,33 +119,19 @@ bool ParticleTreeElement::updateParticle(const Particle& particle) {
|
|||
uint16_t numberOfParticles = _particles.size();
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
if (_particles[i].getID() == particle.getID()) {
|
||||
int difference = _particles[i].getLastUpdated() - particle.getLastUpdated();
|
||||
|
||||
bool changedOnServer = _particles[i].getLastEdited() < particle.getLastEdited();
|
||||
bool localOlder = _particles[i].getLastUpdated() < particle.getLastUpdated();
|
||||
|
||||
if (changedOnServer || localOlder) {
|
||||
|
||||
bool changedOnServer = _particles[i].getEditedAgo() > particle.getEditedAgo();
|
||||
if (changedOnServer) {
|
||||
if (wantDebug) {
|
||||
printf("local particle [id:%d] %s and %s than server particle by %d, particle.isNewlyCreated()=%s\n",
|
||||
printf("local particle [id:%d] %s, particle.isNewlyCreated()=%s\n",
|
||||
particle.getID(), (changedOnServer ? "CHANGED" : "same"),
|
||||
(localOlder ? "OLDER" : "NEWER"),
|
||||
difference, debug::valueOf(particle.isNewlyCreated()) );
|
||||
debug::valueOf(particle.isNewlyCreated()) );
|
||||
}
|
||||
|
||||
uint64_t actuallyCreated = particle.getCreated();
|
||||
if (!particle.isNewlyCreated()) {
|
||||
actuallyCreated = _particles[i].getCreated();
|
||||
}
|
||||
_particles[i] = particle;
|
||||
_particles[i].setCreated(actuallyCreated);
|
||||
_particles[i].copyChangedProperties(particle);
|
||||
} else {
|
||||
if (wantDebug) {
|
||||
printf(">>> NO CHANGE <<< -- local particle [id:%d] %s and %s than server particle by %d, "
|
||||
"particle.isNewlyCreated()=%s\n",
|
||||
printf(">>> NO CHANGE <<< -- local particle [id:%d] %s particle.isNewlyCreated()=%s\n",
|
||||
particle.getID(), (changedOnServer ? "CHANGED" : "same"),
|
||||
(localOlder ? "OLDER" : "NEWER"),
|
||||
difference, debug::valueOf(particle.isNewlyCreated()) );
|
||||
debug::valueOf(particle.isNewlyCreated()) );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -6,7 +6,7 @@ set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
|
|||
# setup for find modules
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
|
||||
|
||||
set(TARGET_NAME scriptengine)
|
||||
set(TARGET_NAME script-engine)
|
||||
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// AbstractControllerScriptingInterface.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/17/13
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__AbstractControllerScriptingInterface__
|
||||
#define __hifi__AbstractControllerScriptingInterface__
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
/// handles scripting of input controller commands from JS
|
||||
class AbstractControllerScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public slots:
|
||||
virtual bool isPrimaryButtonPressed() const = 0;
|
||||
virtual glm::vec2 getPrimaryJoystickPosition() const = 0;
|
||||
|
||||
virtual int getNumberOfButtons() const = 0;
|
||||
virtual bool isButtonPressed(int buttonIndex) const = 0;
|
||||
|
||||
virtual int getNumberOfTriggers() const = 0;
|
||||
virtual float getTriggerValue(int triggerIndex) const = 0;
|
||||
|
||||
virtual int getNumberOfJoysticks() const = 0;
|
||||
virtual glm::vec2 getJoystickPosition(int joystickIndex) const = 0;
|
||||
|
||||
virtual int getNumberOfSpatialControls() const = 0;
|
||||
virtual glm::vec3 getSpatialControlPosition(int controlIndex) const = 0;
|
||||
virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const = 0;
|
||||
virtual glm::vec3 getSpatialControlNormal(int controlIndex) const = 0;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__AbstractControllerScriptingInterface__) */
|
|
@ -24,10 +24,14 @@
|
|||
|
||||
int ScriptEngine::_scriptNumber = 1;
|
||||
|
||||
ScriptEngine::ScriptEngine(QString scriptContents, bool wantMenuItems,
|
||||
const char* scriptMenuName, AbstractMenuInterface* menu) {
|
||||
ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems,
|
||||
const char* scriptMenuName, AbstractMenuInterface* menu,
|
||||
AbstractControllerScriptingInterface* controllerScriptingInterface) {
|
||||
_scriptContents = scriptContents;
|
||||
_isFinished = false;
|
||||
_isRunning = false;
|
||||
|
||||
// some clients will use these menu features
|
||||
_wantMenuItems = wantMenuItems;
|
||||
if (scriptMenuName) {
|
||||
_scriptMenuName = "Stop ";
|
||||
|
@ -38,6 +42,7 @@ ScriptEngine::ScriptEngine(QString scriptContents, bool wantMenuItems,
|
|||
_scriptMenuName.append(_scriptNumber);
|
||||
}
|
||||
_menu = menu;
|
||||
_controllerScriptingInterface = controllerScriptingInterface;
|
||||
}
|
||||
|
||||
ScriptEngine::~ScriptEngine() {
|
||||
|
@ -57,10 +62,16 @@ void ScriptEngine::cleanMenuItems() {
|
|||
}
|
||||
}
|
||||
|
||||
void ScriptEngine::run() {
|
||||
bool ScriptEngine::setScriptContents(const QString& scriptContents) {
|
||||
if (_isRunning) {
|
||||
return false;
|
||||
}
|
||||
_scriptContents = scriptContents;
|
||||
return true;
|
||||
}
|
||||
|
||||
//setupMenuItems();
|
||||
|
||||
void ScriptEngine::run() {
|
||||
_isRunning = true;
|
||||
QScriptEngine engine;
|
||||
|
||||
_voxelScriptingInterface.init();
|
||||
|
@ -78,6 +89,11 @@ void ScriptEngine::run() {
|
|||
QScriptValue particleScripterValue = engine.newQObject(&_particleScriptingInterface);
|
||||
engine.globalObject().setProperty("Particles", particleScripterValue);
|
||||
|
||||
if (_controllerScriptingInterface) {
|
||||
QScriptValue controllerScripterValue = engine.newQObject(_controllerScriptingInterface);
|
||||
engine.globalObject().setProperty("Controller", controllerScripterValue);
|
||||
}
|
||||
|
||||
QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE));
|
||||
engine.globalObject().setProperty("TREE_SCALE", treeScaleValue);
|
||||
|
||||
|
@ -107,16 +123,14 @@ void ScriptEngine::run() {
|
|||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
}
|
||||
|
||||
|
||||
if (_isFinished) {
|
||||
//qDebug() << "line: " << __LINE__ << " _isFinished... breaking loop\n";
|
||||
break;
|
||||
}
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
if (_isFinished) {
|
||||
//qDebug() << "line: " << __LINE__ << " _isFinished... breaking loop\n";
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -129,7 +143,9 @@ void ScriptEngine::run() {
|
|||
_voxelScriptingInterface.getVoxelPacketSender()->releaseQueuedMessages();
|
||||
|
||||
// since we're in non-threaded mode, call process so that the packets are sent
|
||||
//_voxelScriptingInterface.getVoxelPacketSender()->process();
|
||||
if (!_voxelScriptingInterface.getVoxelPacketSender()->isThreaded()) {
|
||||
_voxelScriptingInterface.getVoxelPacketSender()->process();
|
||||
}
|
||||
}
|
||||
|
||||
if (_particleScriptingInterface.getParticlePacketSender()->serversExist()) {
|
||||
|
@ -140,14 +156,15 @@ void ScriptEngine::run() {
|
|||
_particleScriptingInterface.getParticlePacketSender()->releaseQueuedMessages();
|
||||
|
||||
// since we're in non-threaded mode, call process so that the packets are sent
|
||||
//_particleScriptingInterface.getParticlePacketSender()->process();
|
||||
if (!_particleScriptingInterface.getParticlePacketSender()->isThreaded()) {
|
||||
_particleScriptingInterface.getParticlePacketSender()->process();
|
||||
}
|
||||
}
|
||||
|
||||
if (willSendVisualDataCallBack) {
|
||||
emit willSendVisualDataCallback();
|
||||
}
|
||||
|
||||
|
||||
if (engine.hasUncaughtException()) {
|
||||
int line = engine.uncaughtExceptionLineNumber();
|
||||
qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n";
|
||||
|
@ -161,9 +178,11 @@ void ScriptEngine::run() {
|
|||
}
|
||||
|
||||
emit finished();
|
||||
_isRunning = false;
|
||||
}
|
||||
|
||||
void ScriptEngine::stop() {
|
||||
_isFinished = true;
|
||||
}
|
||||
|
||||
|
|
@ -18,12 +18,16 @@
|
|||
#include <AbstractMenuInterface.h>
|
||||
#include <ParticleScriptingInterface.h>
|
||||
#include <VoxelScriptingInterface.h>
|
||||
#include "AbstractControllerScriptingInterface.h"
|
||||
|
||||
const QString NO_SCRIPT("");
|
||||
|
||||
class ScriptEngine : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ScriptEngine(QString scriptContents, bool wantMenuItems = false,
|
||||
const char* scriptMenuName = NULL, AbstractMenuInterface* menu = NULL);
|
||||
ScriptEngine(const QString& scriptContents = NO_SCRIPT, bool wantMenuItems = false,
|
||||
const char* scriptMenuName = NULL, AbstractMenuInterface* menu = NULL,
|
||||
AbstractControllerScriptingInterface* controllerScriptingInterface = NULL);
|
||||
|
||||
~ScriptEngine();
|
||||
|
||||
|
@ -33,6 +37,9 @@ public:
|
|||
/// Access the ParticleScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener
|
||||
ParticleScriptingInterface* getParticleScriptingInterface() { return &_particleScriptingInterface; }
|
||||
|
||||
/// sets the script contents, will return false if failed, will fail if script is already running
|
||||
bool setScriptContents(const QString& scriptContents);
|
||||
|
||||
void setupMenuItems();
|
||||
void cleanMenuItems();
|
||||
|
||||
|
@ -47,11 +54,13 @@ signals:
|
|||
protected:
|
||||
QString _scriptContents;
|
||||
bool _isFinished;
|
||||
bool _isRunning;
|
||||
|
||||
|
||||
private:
|
||||
VoxelScriptingInterface _voxelScriptingInterface;
|
||||
ParticleScriptingInterface _particleScriptingInterface;
|
||||
AbstractControllerScriptingInterface* _controllerScriptingInterface;
|
||||
bool _wantMenuItems;
|
||||
QString _scriptMenuName;
|
||||
AbstractMenuInterface* _menu;
|
|
@ -7,11 +7,12 @@ set(TARGET_NAME shared)
|
|||
project(${TARGET_NAME})
|
||||
|
||||
find_package(Qt5Network REQUIRED)
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
|
||||
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
|
||||
setup_hifi_library(${TARGET_NAME})
|
||||
|
||||
qt5_use_modules(${TARGET_NAME} Network)
|
||||
qt5_use_modules(${TARGET_NAME} Network Widgets)
|
||||
|
||||
# include GLM
|
||||
include(${MACRO_DIR}/IncludeGLM.cmake)
|
||||
|
|
|
@ -10,9 +10,12 @@
|
|||
#ifndef __hifi__AbstractMenuInterface__
|
||||
#define __hifi__AbstractMenuInterface__
|
||||
|
||||
#include <QMenuBar>
|
||||
//#include <QHash>
|
||||
//#include <QKeySequence>
|
||||
#include <QAction>
|
||||
|
||||
class QMenu;
|
||||
class QString;
|
||||
class QObject;
|
||||
class QKeySequence;
|
||||
|
||||
class AbstractMenuInterface {
|
||||
public:
|
||||
|
@ -26,4 +29,4 @@ public:
|
|||
virtual void removeAction(QMenu* menu, const QString& actionName) = 0;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__AbstractMenuInterface__) */
|
||||
#endif /* defined(__hifi__AbstractMenuInterface__) */
|
||||
|
|
|
@ -33,6 +33,8 @@ public:
|
|||
/// Override this function to do whatever your class actually does, return false to exit thread early.
|
||||
virtual bool process() = 0;
|
||||
|
||||
bool isThreaded() const { return _isThreaded; }
|
||||
|
||||
protected:
|
||||
|
||||
/// Locks all the resources of the thread.
|
||||
|
@ -43,8 +45,6 @@ protected:
|
|||
|
||||
bool isStillRunning() const { return !_stopThread; }
|
||||
|
||||
bool isThreaded() const { return _isThreaded; }
|
||||
|
||||
private:
|
||||
pthread_mutex_t _mutex;
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
|
|||
return 2;
|
||||
|
||||
case PACKET_TYPE_PARTICLE_DATA:
|
||||
return 3;
|
||||
return 4;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
void registerMetaTypes(QScriptEngine* engine) {
|
||||
qScriptRegisterMetaType(engine, vec3toScriptValue, vec3FromScriptValue);
|
||||
qScriptRegisterMetaType(engine, vec2toScriptValue, vec2FromScriptValue);
|
||||
qScriptRegisterMetaType(engine, xColorToScriptValue, xColorFromScriptValue);
|
||||
}
|
||||
|
||||
|
@ -29,6 +30,19 @@ void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3) {
|
|||
vec3.z = object.property("z").toVariant().toFloat();
|
||||
}
|
||||
|
||||
QScriptValue vec2toScriptValue(QScriptEngine* engine, const glm::vec2 &vec2) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("x", vec2.x);
|
||||
obj.setProperty("y", vec2.y);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void vec2FromScriptValue(const QScriptValue &object, glm::vec2 &vec2) {
|
||||
vec2.x = object.property("x").toVariant().toFloat();
|
||||
vec2.y = object.property("y").toVariant().toFloat();
|
||||
}
|
||||
|
||||
|
||||
QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("red", color.red);
|
||||
|
|
|
@ -17,11 +17,17 @@
|
|||
#include "SharedUtil.h"
|
||||
|
||||
Q_DECLARE_METATYPE(glm::vec3)
|
||||
Q_DECLARE_METATYPE(glm::vec2)
|
||||
Q_DECLARE_METATYPE(xColor)
|
||||
|
||||
void registerMetaTypes(QScriptEngine* engine);
|
||||
|
||||
QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3);
|
||||
void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3);
|
||||
|
||||
QScriptValue vec2toScriptValue(QScriptEngine* engine, const glm::vec2 &vec2);
|
||||
void vec2FromScriptValue(const QScriptValue &object, glm::vec2 &vec2);
|
||||
|
||||
QScriptValue xColorToScriptValue(QScriptEngine* engine, const xColor& color);
|
||||
void xColorFromScriptValue(const QScriptValue &object, xColor& color);
|
||||
|
||||
|
|
|
@ -30,10 +30,15 @@ uint64_t usecTimestamp(const timeval *time) {
|
|||
return (time->tv_sec * 1000000 + time->tv_usec);
|
||||
}
|
||||
|
||||
int usecTimestampNowAdjust = 0;
|
||||
void usecTimestampNowForceClockSkew(int clockSkew) {
|
||||
::usecTimestampNowAdjust = clockSkew;
|
||||
}
|
||||
|
||||
uint64_t usecTimestampNow() {
|
||||
timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
return (now.tv_sec * 1000000 + now.tv_usec);
|
||||
return (now.tv_sec * 1000000 + now.tv_usec) + ::usecTimestampNowAdjust;
|
||||
}
|
||||
|
||||
float randFloat () {
|
||||
|
|
|
@ -56,6 +56,7 @@ static const uint64_t USECS_PER_SECOND = 1000 * 1000;
|
|||
|
||||
uint64_t usecTimestamp(const timeval *time);
|
||||
uint64_t usecTimestampNow();
|
||||
void usecTimestampNowForceClockSkew(int clockSkew);
|
||||
|
||||
float randFloat();
|
||||
int randIntInRange (int min, int max);
|
||||
|
|
Loading…
Reference in a new issue