This commit is contained in:
ZappoMan 2014-01-23 21:46:13 -08:00
commit bb46435c38
55 changed files with 1355 additions and 576 deletions

View file

@ -807,7 +807,7 @@ AnimationServer::AnimationServer(int &argc, char **argv) :
pthread_create(&::animateVoxelThread, NULL, animateVoxels, NULL);
NodeList::getInstance()->setNodeTypesOfInterest(&NODE_TYPE_VOXEL_SERVER, 1);
NodeList::getInstance()->addNodeTypeToInterestSet(NODE_TYPE_VOXEL_SERVER);
QTimer* domainServerTimer = new QTimer(this);
connect(domainServerTimer, SIGNAL(timeout()), nodeList, SLOT(sendDomainServerCheckIn()));

View file

@ -52,13 +52,7 @@ void Agent::run() {
NodeList* nodeList = NodeList::getInstance();
nodeList->setOwnerType(NODE_TYPE_AGENT);
// XXXBHG - this seems less than ideal. There might be classes (like jurisdiction listeners, that need access to
// other node types, but for them to get access to those node types, we have to add them here. It seems like
// NodeList should support adding types of interest
const NODE_TYPE AGENT_NODE_TYPES_OF_INTEREST[] = { NODE_TYPE_VOXEL_SERVER, NODE_TYPE_PARTICLE_SERVER,
NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER };
nodeList->setNodeTypesOfInterest(AGENT_NODE_TYPES_OF_INTEREST, sizeof(AGENT_NODE_TYPES_OF_INTEREST));
nodeList->addSetOfNodeTypesToNodeInterestSet(QSet<NODE_TYPE>() << NODE_TYPE_AUDIO_MIXER << NODE_TYPE_AVATAR_MIXER);
// figure out the URL for the script for this agent assignment
QString scriptURLString("http://%1:8080/assignment/%2");

View file

@ -241,8 +241,7 @@ void AudioMixer::run() {
NodeList* nodeList = NodeList::getInstance();
const char AUDIO_MIXER_NODE_TYPES_OF_INTEREST[2] = { NODE_TYPE_AGENT, NODE_TYPE_AUDIO_INJECTOR };
nodeList->setNodeTypesOfInterest(AUDIO_MIXER_NODE_TYPES_OF_INTEREST, sizeof(AUDIO_MIXER_NODE_TYPES_OF_INTEREST));
nodeList->addNodeTypeToInterestSet(NODE_TYPE_AGENT);
nodeList->linkedDataCreateCallback = attachNewBufferToNode;

View file

@ -132,9 +132,9 @@ void AvatarMixer::processDatagram(const QByteArray& dataByteArray, const HifiSoc
// parse positional data from an node
nodeList->updateNodeWithData(avatarNode.data(), senderSockAddr,
(unsigned char*) dataByteArray.data(), dataByteArray.size());
} else {
break;
}
break;
}
case PACKET_TYPE_KILL_NODE:
default:
@ -149,7 +149,7 @@ void AvatarMixer::run() {
commonInit(AVATAR_MIXER_LOGGING_NAME, NODE_TYPE_AVATAR_MIXER);
NodeList* nodeList = NodeList::getInstance();
nodeList->setNodeTypesOfInterest(&NODE_TYPE_AGENT, 1);
nodeList->addNodeTypeToInterestSet(NODE_TYPE_AGENT);
nodeList->linkedDataCreateCallback = attachAvatarDataToNode;

View file

@ -18,8 +18,8 @@ function vMinus(a, b) {
// First, load two percussion sounds to be used on the sticks
var drum1 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/MusicalInstruments/drums/snare.raw");
var drum2 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/MusicalInstruments/drums/snare.raw");
var drum1 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Drums/RackTomHi.raw");
var drum2 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Drums/RackTomLo.raw");
// State Machine:
// 0 = not triggered

View file

@ -42,7 +42,7 @@ Voxels.setVoxel(position.x, 0, position.z, 0.5, 0, 0, 255);
var totalParticles = 0;
function makeFountain() {
if (Math.random() < 0.06) {
if (Math.random() < 0.10) {
//print("Made particle!\n");
var properties = {
position: position,
@ -51,9 +51,9 @@ function makeFountain() {
velocity: { x: (Math.random() * 1.0 - 0.5),
y: (1.0 + (Math.random() * 2.0)),
z: (Math.random() * 1.0 - 0.5) },
gravity: { x: 0, y: -0.5, z: 0 },
gravity: { x: 0, y: -0.1, z: 0 },
damping: 0.25,
lifetime: 2
lifetime: 1
}
Particles.addParticle(properties);

View file

@ -134,8 +134,10 @@ function checkControllerSide(whichSide) {
gravity: { x: 0, y: 0, z: 0},
inHand: true,
radius: 0.05,
damping: 0.999,
color: { red: 255, green: 0, blue: 0 },
lifetime: 10 // 10 seconds
lifetime: 10 // 10 seconds - same as default, not needed but here as an example
};
newParticle = Particles.addParticle(properties);

View file

@ -35,11 +35,11 @@ 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;
info.inputValues[normalIndex] = 0x007F00;
} else {
var scale = 127.0 / length;
info.attributeValues[normalIndex] =
info.inputValues[normalIndex] =
(Math.floor(vector[0] * scale) & 0xFF) << 16 |
(Math.floor(vector[1] * scale) & 0xFF) << 8 |
Math.floor(vector[2] * scale) & 0xFF;
@ -61,7 +61,7 @@ function guide(minimum, size, depth) {
maximum[2] <= sphereCenter[2] - sphereRadius) {
info.isLeaf = true;
if (colorIndex != -1) {
info.attributeValues[colorIndex] = 0x0;
info.inputValues[colorIndex] = 0x0;
}
visitor.visit(info);
return;
@ -110,7 +110,7 @@ function guide(minimum, size, depth) {
if (inside == 8) {
info.isLeaf = true;
if (colorIndex != -1) {
info.attributeValues[colorIndex] = sphereColor;
info.inputValues[colorIndex] = sphereColor;
}
setNormal(vector);
visitor.visit(info);
@ -122,13 +122,13 @@ function guide(minimum, size, depth) {
info.isLeaf = true;
if (inside >= 3) {
if (colorIndex != -1) {
info.attributeValues[colorIndex] = sphereColor;
info.inputValues[colorIndex] = sphereColor;
}
setNormal(vector);
} else {
if (colorIndex != -1) {
info.attributeValues[colorIndex] = 0x0;
info.inputValues[colorIndex] = 0x0;
}
}
visitor.visit(info);
@ -152,11 +152,11 @@ function guide(minimum, size, depth) {
}
(function(visitation) {
var attributes = visitation.visitor.getAttributes();
colorIndex = strictIndexOf(attributes, AttributeRegistry.colorAttribute);
normalIndex = strictIndexOf(attributes, AttributeRegistry.normalAttribute);
var inputs = visitation.visitor.getInputs();
colorIndex = strictIndexOf(inputs, AttributeRegistry.colorAttribute);
normalIndex = strictIndexOf(inputs, AttributeRegistry.normalAttribute);
visitor = visitation.visitor;
info = { attributeValues: new Array(attributes.length) };
info = { inputValues: new Array(inputs.length) };
// have the sphere orbit the center and pulse in size
var time = new Date().getTime();

View file

@ -0,0 +1,15 @@
#version 120
//
// grid.frag
// fragment shader
//
// Created by Andrzej Kapolka on 1/21/14.
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
//
void main(void) {
// use the standard exponential fog calculation
const float FOG_DENSITY = 0.5;
gl_FragColor = vec4(gl_Color.rgb, exp(-FOG_DENSITY / gl_FragCoord.w));
}

View file

@ -108,6 +108,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
QApplication(argc, argv),
_window(new QMainWindow(desktop())),
_glWidget(new GLCanvas()),
_nodeThread(new QThread(this)),
_datagramProcessor(),
_frameCount(0),
_fps(120.0f),
_justStarted(true),
@ -143,12 +145,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_enableProcessVoxelsThread(true),
_voxelProcessor(),
_voxelHideShowThread(&_voxels),
_voxelEditSender(this),
_particleEditSender(this),
_packetCount(0),
_packetsPerSecond(0),
_bytesPerSecond(0),
_bytesCount(0),
_recentMaxPackets(0),
_resetRecentMaxPacketsSoon(true),
_swatch(NULL),
@ -173,11 +171,21 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
if (portStr) {
listenPort = atoi(portStr);
}
// start the nodeThread so its event loop is running
_nodeThread->start();
// make sure the node thread is given highest priority
_nodeThread->setPriority(QThread::TimeCriticalPriority);
// put the NodeList and datagram processing on the node thread
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_AGENT, listenPort);
// connect our processDatagrams slot to the QUDPSocket readyRead() signal
connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), SLOT(processDatagrams()));
nodeList->moveToThread(_nodeThread);
_datagramProcessor.moveToThread(_nodeThread);
// connect the DataProcessor processDatagrams slot to the QUDPSocket readyRead() signal
connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), &_datagramProcessor, SLOT(processDatagrams()));
// put the audio processing on a separate thread
QThread* audioThread = new QThread(this);
@ -192,7 +200,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer)));
connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), &_voxels, SLOT(nodeAdded(SharedNodePointer)));
connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_voxels, SLOT(nodeKilled(SharedNodePointer)));
// read the ApplicationInfo.ini file for Name/Version/Domain information
QSettings applicationInfo("resources/info/ApplicationInfo.ini", QSettings::IniFormat);
@ -221,12 +229,18 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
#endif
// tell the NodeList instance who to tell the domain server we care about
const char nodeTypesOfInterest[] = {NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, NODE_TYPE_VOXEL_SERVER,
NODE_TYPE_PARTICLE_SERVER, NODE_TYPE_METAVOXEL_SERVER};
nodeList->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest));
nodeList->addSetOfNodeTypesToNodeInterestSet(QSet<NODE_TYPE>() << NODE_TYPE_AUDIO_MIXER << NODE_TYPE_AVATAR_MIXER
<< NODE_TYPE_VOXEL_SERVER << NODE_TYPE_PARTICLE_SERVER
<< NODE_TYPE_METAVOXEL_SERVER);
// connect to the packet sent signal of the _voxelEditSender and the _particleEditSender
connect(&_voxelEditSender, &VoxelEditPacketSender::packetSent, this, &Application::packetSent);
connect(&_particleEditSender, &ParticleEditPacketSender::packetSent, this, &Application::packetSent);
QTimer* silentNodeTimer = new QTimer(this);
// move the silentNodeTimer to the _nodeThread
QTimer* silentNodeTimer = new QTimer();
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
silentNodeTimer->moveToThread(_nodeThread);
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
@ -270,13 +284,35 @@ 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;
Menu::getInstance()->saveSettings();
_rearMirrorTools->saveSettings(_settings);
_settings->sync();
// let the avatar mixer know we're out
NodeList::getInstance()->sendKillNode(QSet<NODE_TYPE>() << NODE_TYPE_AVATAR_MIXER);
// ask the datagram processing thread to quit and wait until it is done
_nodeThread->quit();
_nodeThread->wait();
// ask the audio thread to quit and wait until it is done
_audio.thread()->quit();
_audio.thread()->wait();
_voxelProcessor.terminate();
_voxelHideShowThread.terminate();
_voxelEditSender.terminate();
_particleEditSender.terminate();
if (_persistThread) {
_persistThread->terminate();
_persistThread->deleteLater();
_persistThread = NULL;
}
storeSizeAndPosition();
saveScripts();
@ -361,9 +397,6 @@ void Application::initializeGL() {
qDebug("Voxel parsing thread created.");
}
// call terminate before exiting
connect(this, SIGNAL(aboutToQuit()), SLOT(terminate()));
// call our timer function every second
QTimer* timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), SLOT(timer()));
@ -612,21 +645,20 @@ void Application::resetProfile(const QString& username) {
}
void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes,
const char* nodeTypes, int numNodeTypes) {
Application* self = getInstance();
for (int i = 0; i < numNodeTypes; ++i) {
const QSet<NODE_TYPE>& destinationNodeTypes) {
foreach(NODE_TYPE type, destinationNodeTypes) {
// Intercept data to voxel server when voxels are disabled
if (nodeTypes[i] == NODE_TYPE_VOXEL_SERVER && !Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
if (type == NODE_TYPE_VOXEL_SERVER && !Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
continue;
}
// Perform the broadcast for one type
int nReceivingNodes = NodeList::getInstance()->broadcastToNodes(broadcastData, dataBytes, & nodeTypes[i], 1);
int nReceivingNodes = NodeList::getInstance()->broadcastToNodes(broadcastData, dataBytes,
QSet<NODE_TYPE>() << type);
// Feed number of bytes to corresponding channel of the bandwidth meter, if any (done otherwise)
BandwidthMeter::ChannelIndex channel;
switch (nodeTypes[i]) {
switch (type) {
case NODE_TYPE_AGENT:
case NODE_TYPE_AVATAR_MIXER:
channel = BandwidthMeter::AVATARS;
@ -637,7 +669,7 @@ void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_
default:
continue;
}
self->_bandwidthMeter.outputStream(channel).updateValue(nReceivingNodes * dataBytes);
_bandwidthMeter.outputStream(channel).updateValue(nReceivingNodes * dataBytes);
}
}
@ -1120,13 +1152,12 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
_seenMouseMove = true;
}
int deltaX = event->x() - _mouseX;
int deltaY = event->y() - _mouseY;
_mouseX = event->x();
_mouseY = event->y();
if (activeWindow() == _window) {
int deltaX = event->x() - _mouseX;
int deltaY = event->y() - _mouseY;
_mouseX = event->x();
_mouseY = event->y();
// orbit behavior
if (_mousePressed && !Menu::getInstance()->isVoxelModeActionChecked()) {
if (_lookatTargetAvatar) {
@ -1292,15 +1323,13 @@ void Application::wheelEvent(QWheelEvent* event) {
}
void Application::sendPingPackets() {
const char nodesToPing[] = {NODE_TYPE_VOXEL_SERVER, NODE_TYPE_PARTICLE_SERVER,
NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, NODE_TYPE_METAVOXEL_SERVER};
unsigned char pingPacket[MAX_PACKET_SIZE];
int length = NodeList::getInstance()->fillPingPacket(pingPacket);
getInstance()->controlledBroadcastToNodes(pingPacket, length,
nodesToPing, sizeof(nodesToPing));
getInstance()->controlledBroadcastToNodes(pingPacket, length, QSet<NODE_TYPE>()
<< NODE_TYPE_VOXEL_SERVER << NODE_TYPE_PARTICLE_SERVER
<< NODE_TYPE_AUDIO_MIXER << NODE_TYPE_AVATAR_MIXER
<< NODE_TYPE_METAVOXEL_SERVER);
}
// Every second, check the frame rates and other stuff
@ -1312,11 +1341,12 @@ void Application::timer() {
}
_fps = (float)_frameCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f);
_packetsPerSecond = (float)_packetCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f);
_bytesPerSecond = (float)_bytesCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f);
_packetsPerSecond = (float) _datagramProcessor.getPacketCount() / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f);
_bytesPerSecond = (float) _datagramProcessor.getByteCount() / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f);
_frameCount = 0;
_packetCount = 0;
_bytesCount = 0;
_datagramProcessor.resetCounters();
gettimeofday(&_timerStart, NULL);
@ -1395,28 +1425,6 @@ void Application::idle() {
}
}
void Application::terminate() {
// Close serial port
// close(serial_fd);
Menu::getInstance()->saveSettings();
_rearMirrorTools->saveSettings(_settings);
_settings->sync();
// let the avatar mixer know we're out
NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1);
_voxelProcessor.terminate();
_voxelHideShowThread.terminate();
_voxelEditSender.terminate();
_particleEditSender.terminate();
if (_persistThread) {
_persistThread->terminate();
_persistThread->deleteLater();
_persistThread = NULL;
}
}
void Application::checkBandwidthMeterClick() {
// ... to be called upon button release
@ -1840,25 +1848,23 @@ const float HEAD_SPHERE_RADIUS = 0.07f;
static QUuid DEFAULT_NODE_ID_REF;
void Application::updateLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection,
glm::vec3& eyePosition) {
void Application::updateLookatTargetAvatar(glm::vec3& eyePosition) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateLookatTargetAvatar()");
if (!_mousePressed) {
_lookatTargetAvatar = findLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, eyePosition, DEFAULT_NODE_ID_REF);
_lookatTargetAvatar = findLookatTargetAvatar(eyePosition, DEFAULT_NODE_ID_REF);
}
}
Avatar* Application::findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection,
glm::vec3& eyePosition, QUuid& nodeUUID = DEFAULT_NODE_ID_REF) {
Avatar* Application::findLookatTargetAvatar(glm::vec3& eyePosition, QUuid& nodeUUID = DEFAULT_NODE_ID_REF) {
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) {
Avatar* avatar = (Avatar*)node->getLinkedData();
float distance;
if (avatar->findRayIntersection(mouseRayOrigin, mouseRayDirection, distance)) {
if (avatar->findRayIntersection(_mouseRayOrigin, _mouseRayDirection, distance)) {
// rescale to compensate for head embiggening
eyePosition = (avatar->getHead().calculateAverageEyePosition() - avatar->getHead().getScalePivot()) *
(avatar->getScale() / avatar->getHead().getScale()) + avatar->getHead().getScalePivot();
@ -1909,7 +1915,7 @@ void Application::renderHighlightVoxel(VoxelDetail voxel) {
glPopMatrix();
}
void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::vec3 mouseRayDirection) {
void Application::updateAvatars(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateAvatars()");
@ -1921,7 +1927,7 @@ void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::
avatar->init();
}
avatar->simulate(deltaTime, NULL);
avatar->setMouseRay(mouseRayOrigin, mouseRayDirection);
avatar->setMouseRay(_mouseRayOrigin, _mouseRayDirection);
}
}
@ -1944,28 +1950,28 @@ void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::
}
}
void Application::updateMouseRay(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection) {
void Application::updateMouseRay() {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateMouseRay()");
_viewFrustum.computePickRay(_mouseX / (float)_glWidget->width(), _mouseY / (float)_glWidget->height(),
mouseRayOrigin, mouseRayDirection);
_mouseRayOrigin, _mouseRayDirection);
// adjust for mirroring
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
glm::vec3 mouseRayOffset = mouseRayOrigin - _viewFrustum.getPosition();
mouseRayOrigin -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), mouseRayOffset) +
glm::vec3 mouseRayOffset = _mouseRayOrigin - _viewFrustum.getPosition();
_mouseRayOrigin -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), mouseRayOffset) +
_viewFrustum.getRight() * glm::dot(_viewFrustum.getRight(), mouseRayOffset));
mouseRayDirection -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), mouseRayDirection) +
_viewFrustum.getRight() * glm::dot(_viewFrustum.getRight(), mouseRayDirection));
_mouseRayDirection -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), _mouseRayDirection) +
_viewFrustum.getRight() * glm::dot(_viewFrustum.getRight(), _mouseRayDirection));
}
// tell my avatar if the mouse is being pressed...
_myAvatar.setMousePressed(_mousePressed);
// tell my avatar the posiion and direction of the ray projected ino the world based on the mouse position
_myAvatar.setMouseRay(mouseRayOrigin, mouseRayDirection);
_myAvatar.setMouseRay(_mouseRayOrigin, _mouseRayDirection);
}
void Application::updateFaceshift() {
@ -1982,8 +1988,7 @@ void Application::updateFaceshift() {
}
}
void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& lookAtRayOrigin,
glm::vec3& lookAtRayDirection) {
void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateMyAvatarLookAtPosition()");
@ -1998,15 +2003,9 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3&
_viewFrustum.computePickRay(0.5f, 0.5f, rayOrigin, rayDirection);
lookAtSpot = rayOrigin + rayDirection * FAR_AWAY_STARE;
} else if (!_lookatTargetAvatar) {
if (_isHoverVoxel) {
// Look at the hovered voxel
lookAtSpot = getMouseVoxelWorldCoordinates(_hoverVoxel);
} else {
// Just look in direction of the mouse ray
lookAtSpot = lookAtRayOrigin + lookAtRayDirection * FAR_AWAY_STARE;
}
} else {
// just look in direction of the mouse ray
lookAtSpot = _mouseRayOrigin + _mouseRayDirection * FAR_AWAY_STARE;
}
if (_faceshift.isActive()) {
// deflect using Faceshift gaze data
@ -2020,8 +2019,7 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3&
_myAvatar.getHead().setLookAtPosition(lookAtSpot);
}
void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection,
float& distance, BoxFace& face) {
void Application::updateHoverVoxels(float deltaTime, float& distance, BoxFace& face) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateHoverVoxels()");
@ -2052,7 +2050,7 @@ void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin,
if (!(_voxels.treeIsBusy() || _mousePressed)) {
{
PerformanceWarning warn(showWarnings, "Application::updateHoverVoxels() _voxels.findRayIntersection()");
_isHoverVoxel = _voxels.findRayIntersection(mouseRayOrigin, mouseRayDirection, _hoverVoxel, distance, face);
_isHoverVoxel = _voxels.findRayIntersection(_mouseRayOrigin, _mouseRayDirection, _hoverVoxel, distance, face);
}
if (MAKE_SOUND_ON_VOXEL_HOVER && _isHoverVoxel &&
glm::vec4(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z, _hoverVoxel.s) != oldVoxel) {
@ -2068,8 +2066,7 @@ void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin,
}
}
void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection,
float& distance, BoxFace& face) {
void Application::updateMouseVoxels(float deltaTime, float& distance, BoxFace& face) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateMouseVoxels()");
@ -2081,7 +2078,7 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin,
fabs(_myAvatar.getVelocity().y) +
fabs(_myAvatar.getVelocity().z)) / 3 < MAX_AVATAR_EDIT_VELOCITY) {
if (_voxels.findRayIntersection(mouseRayOrigin, mouseRayDirection, _mouseVoxel, distance, face)) {
if (_voxels.findRayIntersection(_mouseRayOrigin, _mouseRayDirection, _mouseVoxel, distance, face)) {
if (distance < MAX_VOXEL_EDIT_DISTANCE) {
// set the voxel scale to that of the first moused-over voxel
if (!wasInitialized) {
@ -2101,7 +2098,7 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin,
glm::vec3 faceVector = getFaceVector(face);
if (_mouseVoxelScale < _mouseVoxel.s) {
// find the closest contained voxel
glm::vec3 pt = (mouseRayOrigin + mouseRayDirection * distance) / (float)TREE_SCALE -
glm::vec3 pt = (_mouseRayOrigin + _mouseRayDirection * distance) / (float)TREE_SCALE -
faceVector * (_mouseVoxelScale * 0.5f);
_mouseVoxel.x = _mouseVoxelScale * floorf(pt.x / _mouseVoxelScale);
_mouseVoxel.y = _mouseVoxelScale * floorf(pt.y / _mouseVoxelScale);
@ -2122,7 +2119,7 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin,
|| Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode)) {
// place the voxel a fixed distance away
float worldMouseVoxelScale = _mouseVoxelScale * TREE_SCALE;
glm::vec3 pt = mouseRayOrigin + mouseRayDirection * (2.0f + worldMouseVoxelScale * 0.5f);
glm::vec3 pt = _mouseRayOrigin + _mouseRayDirection * (2.0f + worldMouseVoxelScale * 0.5f);
_mouseVoxel.x = _mouseVoxelScale * floorf(pt.x / worldMouseVoxelScale);
_mouseVoxel.y = _mouseVoxelScale * floorf(pt.y / worldMouseVoxelScale);
_mouseVoxel.z = _mouseVoxelScale * floorf(pt.z / worldMouseVoxelScale);
@ -2356,29 +2353,28 @@ void Application::update(float deltaTime) {
PerformanceWarning warn(showWarnings, "Application::update()");
// check what's under the mouse and update the mouse voxel
glm::vec3 mouseRayOrigin, mouseRayDirection;
updateMouseRay(deltaTime, mouseRayOrigin, mouseRayDirection);
updateMouseRay();
// Set where I am looking based on my mouse ray (so that other people can see)
glm::vec3 lookAtSpot;
updateFaceshift();
updateLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, lookAtSpot);
updateMyAvatarLookAtPosition(lookAtSpot, mouseRayOrigin, mouseRayDirection);
updateLookatTargetAvatar(lookAtSpot);
updateMyAvatarLookAtPosition(lookAtSpot);
// Find the voxel we are hovering over, and respond if clicked
float distance;
BoxFace face;
updateHoverVoxels(deltaTime, mouseRayOrigin, mouseRayDirection, distance, face); // clicking on voxels and making sounds
updateMouseVoxels(deltaTime, mouseRayOrigin, mouseRayDirection, distance, face); // UI/UX related to voxels
updateHoverVoxels(deltaTime, distance, face); // clicking on voxels and making sounds
updateMouseVoxels(deltaTime, distance, face); // UI/UX related to voxels
updateHandAndTouch(deltaTime); // Update state for touch sensors
updateLeap(deltaTime); // Leap finger-sensing device
updateSixense(deltaTime); // Razer Hydra controllers
updateSerialDevices(deltaTime); // Read serial port interface devices
updateAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
updateAvatars(deltaTime, mouseRayOrigin, mouseRayDirection); //loop through all the other avatars and simulate them...
updateAvatars(deltaTime); //loop through all the other avatars and simulate them...
updateMyAvatarSimulation(deltaTime); // Simulate myself
updateParticles(deltaTime); // Simulate particle cloud movements
updateMetavoxels(deltaTime); // update metavoxels
@ -2449,9 +2445,8 @@ void Application::updateAvatar(float deltaTime) {
endOfBroadcastStringWrite += _myAvatar.getBroadcastData(endOfBroadcastStringWrite);
const char nodeTypesOfInterest[] = { NODE_TYPE_AVATAR_MIXER };
controlledBroadcastToNodes(broadcastString, endOfBroadcastStringWrite - broadcastString,
nodeTypesOfInterest, sizeof(nodeTypesOfInterest));
QSet<NODE_TYPE>() << NODE_TYPE_AVATAR_MIXER);
// Update _viewFrustum with latest camera and view frustum data...
// NOTE: we get this from the view frustum, to make it simpler, since the
@ -2862,10 +2857,10 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
if (!selfAvatarOnly) {
// draw a red sphere
float sphereRadius = 0.25f;
float originSphereRadius = 0.05f;
glColor3f(1,0,0);
glPushMatrix();
glutSolidSphere(sphereRadius, 15, 15);
glutSolidSphere(originSphereRadius, 15, 15);
glPopMatrix();
// disable specular lighting for ground and voxels
@ -3028,6 +3023,9 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
glPopMatrix();
}
// give external parties a change to hook in
emit renderingInWorldInterface();
}
}
@ -4052,94 +4050,7 @@ int Application::parseOctreeStats(unsigned char* messageData, ssize_t messageLen
return statsMessageLength;
}
// Receive packets from other nodes/servers and decide what to do with them!
void Application::processDatagrams() {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::networkReceive()");
HifiSockAddr senderSockAddr;
ssize_t bytesReceived;
while (NodeList::getInstance()->getNodeSocket().hasPendingDatagrams() &&
(bytesReceived = NodeList::getInstance()->getNodeSocket().readDatagram((char*) _incomingPacket,
MAX_PACKET_SIZE,
senderSockAddr.getAddressPointer(),
senderSockAddr.getPortPointer()))) {
_packetCount++;
_bytesCount += bytesReceived;
if (packetVersionMatch(_incomingPacket)) {
// only process this packet if we have a match on the packet version
switch (_incomingPacket[0]) {
case PACKET_TYPE_TRANSMITTER_DATA_V2:
// V2 = IOS transmitter app
_myTransmitter.processIncomingData(_incomingPacket, bytesReceived);
break;
case PACKET_TYPE_MIXED_AUDIO:
QMetaObject::invokeMethod(&_audio, "addReceivedAudioToBuffer", Qt::QueuedConnection,
Q_ARG(QByteArray, QByteArray((char*) _incomingPacket, bytesReceived)));
break;
case PACKET_TYPE_PARTICLE_ADD_RESPONSE:
// this will keep creatorTokenIDs to IDs mapped correctly
Particle::handleAddParticleResponse(_incomingPacket, bytesReceived);
break;
case PACKET_TYPE_PARTICLE_DATA:
case PACKET_TYPE_PARTICLE_ERASE:
case PACKET_TYPE_VOXEL_DATA:
case PACKET_TYPE_VOXEL_ERASE:
case PACKET_TYPE_OCTREE_STATS:
case PACKET_TYPE_ENVIRONMENT_DATA: {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::networkReceive()... _voxelProcessor.queueReceivedPacket()");
bool wantExtraDebugging = getLogger()->extraDebugging();
if (wantExtraDebugging && _incomingPacket[0] == PACKET_TYPE_VOXEL_DATA) {
int numBytesPacketHeader = numBytesForPacketHeader(_incomingPacket);
unsigned char* dataAt = _incomingPacket + numBytesPacketHeader;
dataAt += sizeof(VOXEL_PACKET_FLAGS);
VOXEL_PACKET_SEQUENCE sequence = (*(VOXEL_PACKET_SEQUENCE*)dataAt);
dataAt += sizeof(VOXEL_PACKET_SEQUENCE);
VOXEL_PACKET_SENT_TIME sentAt = (*(VOXEL_PACKET_SENT_TIME*)dataAt);
dataAt += sizeof(VOXEL_PACKET_SENT_TIME);
VOXEL_PACKET_SENT_TIME arrivedAt = usecTimestampNow();
int flightTime = arrivedAt - sentAt;
printf("got PACKET_TYPE_VOXEL_DATA, sequence:%d flightTime:%d\n", sequence, flightTime);
}
// add this packet to our list of voxel packets and process them on the voxel processing
_voxelProcessor.queueReceivedPacket(senderSockAddr, _incomingPacket, bytesReceived);
break;
}
case PACKET_TYPE_METAVOXEL_DATA:
_metavoxels.processData(QByteArray((const char*) _incomingPacket, bytesReceived),
senderSockAddr);
break;
case PACKET_TYPE_BULK_AVATAR_DATA:
NodeList::getInstance()->processBulkNodeData(senderSockAddr,
_incomingPacket,
bytesReceived);
getInstance()->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(bytesReceived);
break;
case PACKET_TYPE_DATA_SERVER_GET:
case PACKET_TYPE_DATA_SERVER_PUT:
case PACKET_TYPE_DATA_SERVER_SEND:
case PACKET_TYPE_DATA_SERVER_CONFIRM:
DataServerClient::processMessageFromDataServer(_incomingPacket, bytesReceived);
break;
default:
NodeList::getInstance()->processNodeData(senderSockAddr, _incomingPacket, bytesReceived);
break;
}
}
}
}
void Application::packetSentNotification(ssize_t length) {
void Application::packetSent(quint64 length) {
_bandwidthMeter.outputStream(BandwidthMeter::VOXELS).updateValue(length);
}

View file

@ -33,6 +33,7 @@
#include "BandwidthMeter.h"
#include "Camera.h"
#include "Cloud.h"
#include "DatagramProcessor.h"
#include "Environment.h"
#include "GLCanvas.h"
#include "MetavoxelSystem.h"
@ -91,11 +92,12 @@ static const float NODE_KILLED_RED = 1.0f;
static const float NODE_KILLED_GREEN = 0.0f;
static const float NODE_KILLED_BLUE = 0.0f;
class Application : public QApplication, public PacketSenderNotify {
class Application : public QApplication {
Q_OBJECT
friend class VoxelPacketProcessor;
friend class VoxelEditPacketSender;
friend class DatagramProcessor;
public:
static Application* getInstance() { return static_cast<Application*>(QCoreApplication::instance()); }
@ -145,10 +147,13 @@ public:
ViewFrustum* getViewFrustum() { return &_viewFrustum; }
VoxelSystem* getVoxels() { return &_voxels; }
ParticleTreeRenderer* getParticles() { return &_particles; }
MetavoxelSystem* getMetavoxels() { return &_metavoxels; }
VoxelSystem* getSharedVoxelSystem() { return &_sharedVoxelSystem; }
VoxelTree* getClipboard() { return &_clipboard; }
Environment* getEnvironment() { return &_environment; }
bool isMouseHidden() const { return _mouseHidden; }
const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; }
const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
Faceshift* getFaceshift() { return &_faceshift; }
SixenseManager* getSixenseManager() { return &_sixenseManager; }
BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; }
@ -169,8 +174,8 @@ public:
Profile* getProfile() { return &_profile; }
void resetProfile(const QString& username);
static void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes,
const char* nodeTypes, int numNodeTypes);
void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes,
const QSet<NODE_TYPE>& destinationNodeTypes);
void setupWorldLight();
@ -186,9 +191,6 @@ public:
void computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal,
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const;
virtual void packetSentNotification(ssize_t length);
VoxelShader& getVoxelShader() { return _voxelShader; }
PointShader& getPointShader() { return _pointShader; }
FileLogger* getLogger() { return _logger; }
@ -204,12 +206,16 @@ public:
void skipVersion(QString latestVersion);
signals:
/// Fired when we're rendering in-world interface elements; allows external parties to hook in.
void renderingInWorldInterface();
public slots:
void domainChanged(const QString& domainHostname);
void nodeKilled(SharedNodePointer node);
void processDatagrams();
void packetSent(quint64 length);
void exportVoxels();
void importVoxels();
void cutVoxels();
@ -230,7 +236,6 @@ private slots:
void timer();
void idle();
void terminate();
void setFullscreen(bool fullscreen);
void setEnable3DTVMode(bool enable3DTVMode);
@ -269,15 +274,12 @@ private:
void update(float deltaTime);
// Various helper functions called during update()
void updateMouseRay(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection);
void updateMouseRay();
void updateFaceshift();
void updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& lookAtRayOrigin, glm::vec3& lookAtRayDirection);
void updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection,
float& distance, BoxFace& face);
void updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection,
float& distance, BoxFace& face);
void updateLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection,
glm::vec3& eyePosition);
void updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot);
void updateHoverVoxels(float deltaTime, float& distance, BoxFace& face);
void updateMouseVoxels(float deltaTime, float& distance, BoxFace& face);
void updateLookatTargetAvatar(glm::vec3& eyePosition);
void updateHandAndTouch(float deltaTime);
void updateLeap(float deltaTime);
void updateSixense(float deltaTime);
@ -292,15 +294,14 @@ private:
void updateAudio(float deltaTime);
void updateCursor(float deltaTime);
Avatar* findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection,
glm::vec3& eyePosition, QUuid &nodeUUID);
Avatar* findLookatTargetAvatar(glm::vec3& eyePosition, QUuid &nodeUUID);
bool isLookingAtMyAvatar(Avatar* avatar);
void renderLookatIndicator(glm::vec3 pointOfInterest);
void renderHighlightVoxel(VoxelDetail voxel);
void updateAvatar(float deltaTime);
void updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::vec3 mouseRayDirection);
void updateAvatars(float deltaTime);
void queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, NodeToJurisdictionMap& jurisdictions);
void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
@ -331,6 +332,9 @@ private:
QGLWidget* _glWidget;
BandwidthMeter _bandwidthMeter;
QThread* _nodeThread;
DatagramProcessor _datagramProcessor;
QNetworkAccessManager* _networkAccessManager;
QSettings* _settings;
@ -402,6 +406,9 @@ private:
bool _mouseHidden;
bool _seenMouseMove;
glm::vec3 _mouseRayOrigin;
glm::vec3 _mouseRayDirection;
float _touchAvgX;
float _touchAvgY;
float _lastTouchAvgX;
@ -461,11 +468,8 @@ private:
VoxelEditPacketSender _voxelEditSender;
ParticleEditPacketSender _particleEditSender;
unsigned char _incomingPacket[MAX_PACKET_SIZE];
int _packetCount;
int _packetsPerSecond;
int _bytesPerSecond;
int _bytesCount;
int _recentMaxPackets; // recent max incoming voxel packets to process
bool _resetRecentMaxPacketsSoon;

View file

@ -415,7 +415,7 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) {
_totalPacketsReceived++;
double timeDiff = diffclock(&_lastReceiveTime, &currentReceiveTime);
// Discard first few received packets for computing jitter (often they pile up on start)
if (_totalPacketsReceived > NUM_INITIAL_PACKETS_DISCARD) {
_stdev.addValue(timeDiff);
@ -443,6 +443,15 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) {
QByteArray outputBuffer;
outputBuffer.resize(numRequiredOutputSamples * sizeof(int16_t));
if (!_ringBuffer.isStarved() && _audioOutput->bytesFree() == _audioOutput->bufferSize()) {
// we don't have any audio data left in the output buffer
// we just starved
qDebug() << "Audio output just starved.";
_ringBuffer.setIsStarved(true);
_numFramesDisplayStarve = 10;
}
// if there is anything in the ring buffer, decide what to do
if (_ringBuffer.samplesAvailable() > 0) {
if (!_ringBuffer.isNotStarvedOrHasMinimumSamples(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO
@ -515,12 +524,6 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) {
}
}
} else if (_audioOutput->bytesFree() == _audioOutput->bufferSize()) {
// we don't have any audio data left in the output buffer, and the ring buffer from
// the network has nothing in it either - we just starved
qDebug() << "Audio output just starved.";
_ringBuffer.setIsStarved(true);
_numFramesDisplayStarve = 10;
}
Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::AUDIO).updateValue(audioByteArray.size());

View file

@ -0,0 +1,110 @@
//
// DatagramProcessor.cpp
// hifi
//
// Created by Stephen Birarda on 1/23/2014.
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
#include <PerfStat.h>
#include "Application.h"
#include "Menu.h"
#include "DatagramProcessor.h"
DatagramProcessor::DatagramProcessor(QObject* parent) :
QObject(parent)
{
}
void DatagramProcessor::processDatagrams() {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"DatagramProcessor::processDatagrams()");
HifiSockAddr senderSockAddr;
ssize_t bytesReceived;
static unsigned char incomingPacket[MAX_PACKET_SIZE];
Application* application = Application::getInstance();
while (NodeList::getInstance()->getNodeSocket().hasPendingDatagrams() &&
(bytesReceived = NodeList::getInstance()->getNodeSocket().readDatagram((char*) incomingPacket,
MAX_PACKET_SIZE,
senderSockAddr.getAddressPointer(),
senderSockAddr.getPortPointer()))) {
_packetCount++;
_byteCount += bytesReceived;
if (packetVersionMatch(incomingPacket)) {
// only process this packet if we have a match on the packet version
switch (incomingPacket[0]) {
case PACKET_TYPE_TRANSMITTER_DATA_V2:
// V2 = IOS transmitter app
application->_myTransmitter.processIncomingData(incomingPacket, bytesReceived);
break;
case PACKET_TYPE_MIXED_AUDIO:
QMetaObject::invokeMethod(&application->_audio, "addReceivedAudioToBuffer", Qt::QueuedConnection,
Q_ARG(QByteArray, QByteArray((char*) incomingPacket, bytesReceived)));
break;
case PACKET_TYPE_PARTICLE_ADD_RESPONSE:
// this will keep creatorTokenIDs to IDs mapped correctly
Particle::handleAddParticleResponse(incomingPacket, bytesReceived);
break;
case PACKET_TYPE_PARTICLE_DATA:
case PACKET_TYPE_PARTICLE_ERASE:
case PACKET_TYPE_VOXEL_DATA:
case PACKET_TYPE_VOXEL_ERASE:
case PACKET_TYPE_OCTREE_STATS:
case PACKET_TYPE_ENVIRONMENT_DATA: {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::networkReceive()... _voxelProcessor.queueReceivedPacket()");
bool wantExtraDebugging = application->getLogger()->extraDebugging();
if (wantExtraDebugging && incomingPacket[0] == PACKET_TYPE_VOXEL_DATA) {
int numBytesPacketHeader = numBytesForPacketHeader(incomingPacket);
unsigned char* dataAt = incomingPacket + numBytesPacketHeader;
dataAt += sizeof(VOXEL_PACKET_FLAGS);
VOXEL_PACKET_SEQUENCE sequence = (*(VOXEL_PACKET_SEQUENCE*)dataAt);
dataAt += sizeof(VOXEL_PACKET_SEQUENCE);
VOXEL_PACKET_SENT_TIME sentAt = (*(VOXEL_PACKET_SENT_TIME*)dataAt);
dataAt += sizeof(VOXEL_PACKET_SENT_TIME);
VOXEL_PACKET_SENT_TIME arrivedAt = usecTimestampNow();
int flightTime = arrivedAt - sentAt;
printf("got PACKET_TYPE_VOXEL_DATA, sequence:%d flightTime:%d\n", sequence, flightTime);
}
// add this packet to our list of voxel packets and process them on the voxel processing
application->_voxelProcessor.queueReceivedPacket(senderSockAddr, incomingPacket, bytesReceived);
break;
}
case PACKET_TYPE_METAVOXEL_DATA:
application->_metavoxels.processData(QByteArray((const char*) incomingPacket, bytesReceived),
senderSockAddr);
break;
case PACKET_TYPE_BULK_AVATAR_DATA:
NodeList::getInstance()->processBulkNodeData(senderSockAddr,
incomingPacket,
bytesReceived);
application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(bytesReceived);
break;
case PACKET_TYPE_DATA_SERVER_GET:
case PACKET_TYPE_DATA_SERVER_PUT:
case PACKET_TYPE_DATA_SERVER_SEND:
case PACKET_TYPE_DATA_SERVER_CONFIRM:
DataServerClient::processMessageFromDataServer(incomingPacket, bytesReceived);
break;
default:
NodeList::getInstance()->processNodeData(senderSockAddr, incomingPacket, bytesReceived);
break;
}
}
}
}

View file

@ -0,0 +1,31 @@
//
// DatagramProcessor.h
// hifi
//
// Created by Stephen Birarda on 1/23/2014.
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
#ifndef __hifi__DatagramProcessor__
#define __hifi__DatagramProcessor__
#include <QtCore/QObject>
class DatagramProcessor : public QObject {
Q_OBJECT
public:
DatagramProcessor(QObject* parent = 0);
int getPacketCount() const { return _packetCount; }
int getByteCount() const { return _byteCount; }
void resetCounters() { _packetCount = 0; _byteCount = 0; }
public slots:
void processDatagrams();
private:
int _packetCount;
int _byteCount;
};
#endif /* defined(__hifi__DatagramProcessor__) */

View file

@ -32,6 +32,7 @@
#include "Menu.h"
#include "Util.h"
#include "InfoView.h"
#include "ui/MetavoxelEditor.h"
Menu* Menu::_instance = NULL;
@ -220,6 +221,8 @@ Menu::Menu() :
SLOT(increaseVoxelSize()));
addActionToQMenuAndActionHash(toolsMenu, MenuOption::ResetSwatchColors, 0, this, SLOT(resetSwatchColors()));
addActionToQMenuAndActionHash(toolsMenu, MenuOption::MetavoxelEditor, 0, this, SLOT(showMetavoxelEditor()));
QMenu* viewMenu = addMenu("View");
@ -352,6 +355,7 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::VoxelDrumming, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::PlaySlaps, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::HandsCollideWithSelf, 0, false);
addDisabledActionAndSeparator(developerMenu, "Testing");
@ -898,7 +902,7 @@ void Menu::goToDomain() {
}
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1);
NodeList::getInstance()->sendKillNode(QSet<NODE_TYPE>() << NODE_TYPE_AVATAR_MIXER);
// give our nodeList the new domain-server hostname
NodeList::getInstance()->setDomainHostname(domainDialog.textValue());
@ -940,7 +944,7 @@ void Menu::goToLocation() {
if (newAvatarPos != avatarPos) {
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1);
NodeList::getInstance()->sendKillNode(QSet<NODE_TYPE>() << NODE_TYPE_AVATAR_MIXER);
qDebug("Going To Location: %f, %f, %f...", x, y, z);
myAvatar->setPosition(newAvatarPos);
@ -1009,6 +1013,13 @@ void Menu::bandwidthDetails() {
_bandwidthDialog->raise();
}
void Menu::showMetavoxelEditor() {
if (!_MetavoxelEditor) {
_MetavoxelEditor = new MetavoxelEditor();
}
_MetavoxelEditor->raise();
}
void Menu::audioMuteToggled() {
QAction *muteAction = _actionHash.value(MenuOption::MuteAudio);
muteAction->setChecked(Application::getInstance()->getAudio()->getMuted());

View file

@ -12,6 +12,7 @@
#include <QMenuBar>
#include <QHash>
#include <QKeySequence>
#include <QPointer>
#include <AbstractMenuInterface.h>
@ -36,8 +37,9 @@ struct ViewFrustumOffset {
class QSettings;
class BandwidthDialog;
class VoxelStatsDialog;
class LodToolsDialog;
class MetavoxelEditor;
class VoxelStatsDialog;
class Menu : public QMenuBar, public AbstractMenuInterface {
Q_OBJECT
@ -107,6 +109,7 @@ private slots:
void chooseVoxelPaintColor();
void runTests();
void resetSwatchColors();
void showMetavoxelEditor();
void audioMuteToggled();
private:
@ -140,6 +143,7 @@ private:
FrustumDrawMode _frustumDrawMode;
ViewFrustumOffset _viewFrustumOffset;
QActionGroup* _voxelModeActionsGroup;
QPointer<MetavoxelEditor> _MetavoxelEditor;
VoxelStatsDialog* _voxelStatsDialog;
LodToolsDialog* _lodToolsDialog;
int _maxVoxels;
@ -188,6 +192,7 @@ namespace MenuOption {
const QString ExportVoxels = "Export Voxels";
const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes";
const QString HeadMouse = "Head Mouse";
const QString HandsCollideWithSelf = "Collide With Self";
const QString FaceshiftTCP = "Faceshift (TCP)";
const QString FalseColorByDistance = "FALSE Color By Distance";
const QString FalseColorBySource = "FALSE Color By Source";
@ -218,6 +223,7 @@ namespace MenuOption {
const QString Login = "Login";
const QString LookAtIndicator = "Look-at Indicator";
const QString LookAtVectors = "Look-at Vectors";
const QString MetavoxelEditor = "Metavoxel Editor...";
const QString Metavoxels = "Metavoxels";
const QString Mirror = "Mirror";
const QString MoveWithLean = "Move with Lean";

View file

@ -6,8 +6,6 @@
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <QFile>
#include <QTextStream>
#include <QtDebug>
#include <SharedUtil.h>
@ -40,14 +38,6 @@ void MetavoxelSystem::init() {
connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), SLOT(nodeAdded(SharedNodePointer)));
connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer)));
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();
}
@ -156,16 +146,17 @@ void MetavoxelSystem::receivedData(const QByteArray& data, const HifiSockAddr& s
MetavoxelSystem::PointVisitor::PointVisitor(QVector<Point>& points) :
MetavoxelVisitor(QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getColorAttribute() <<
AttributeRegistry::getInstance()->getNormalAttribute()),
AttributeRegistry::getInstance()->getNormalAttribute(),
QVector<AttributePointer>()),
_points(points) {
}
bool MetavoxelSystem::PointVisitor::visit(const MetavoxelInfo& info) {
bool MetavoxelSystem::PointVisitor::visit(MetavoxelInfo& info) {
if (!info.isLeaf) {
return true;
}
QRgb color = info.attributeValues.at(0).getInlineValue<QRgb>();
QRgb normal = info.attributeValues.at(1).getInlineValue<QRgb>();
QRgb color = info.inputValues.at(0).getInlineValue<QRgb>();
QRgb normal = info.inputValues.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),

View file

@ -11,7 +11,6 @@
#include <QList>
#include <QOpenGLBuffer>
#include <QScriptEngine>
#include <QVector>
#include <glm/glm.hpp>
@ -30,18 +29,23 @@ class MetavoxelSystem : public QObject {
Q_OBJECT
public:
MetavoxelSystem();
void init();
MetavoxelData& getData() { return _data; }
void processData(const QByteArray& data, const HifiSockAddr& sender);
void simulate(float deltaTime);
void render();
public slots:
void nodeAdded(SharedNodePointer node);
void nodeKilled(SharedNodePointer node);
private:
Q_INVOKABLE void addClient(const QUuid& uuid, const HifiSockAddr& address);
@ -58,7 +62,7 @@ private:
class PointVisitor : public MetavoxelVisitor {
public:
PointVisitor(QVector<Point>& points);
virtual bool visit(const MetavoxelInfo& info);
virtual bool visit(MetavoxelInfo& info);
private:
QVector<Point>& _points;
@ -67,7 +71,6 @@ private:
static ProgramObject _program;
static int _pointScaleLocation;
QScriptEngine _scriptEngine;
MetavoxelData _data;
QVector<Point> _points;
PointVisitor _pointVisitor;

View file

@ -332,27 +332,29 @@ void renderWorldBox() {
glVertex3f(TREE_SCALE, 0, TREE_SCALE);
glVertex3f(TREE_SCALE, 0, 0);
glEnd();
// Draw marker dots at very end
// Draw meter markers along the 3 axis to help with measuring things
const float MARKER_DISTANCE = 1.f;
const float MARKER_RADIUS = 0.05f;
glEnable(GL_LIGHTING);
glPushMatrix();
glTranslatef(TREE_SCALE, 0, 0);
glTranslatef(MARKER_DISTANCE, 0, 0);
glColor3fv(red);
glutSolidSphere(0.125, 10, 10);
glutSolidSphere(MARKER_RADIUS, 10, 10);
glPopMatrix();
glPushMatrix();
glTranslatef(0, TREE_SCALE, 0);
glTranslatef(0, MARKER_DISTANCE, 0);
glColor3fv(green);
glutSolidSphere(0.125, 10, 10);
glutSolidSphere(MARKER_RADIUS, 10, 10);
glPopMatrix();
glPushMatrix();
glTranslatef(0, 0, TREE_SCALE);
glTranslatef(0, 0, MARKER_DISTANCE);
glColor3fv(blue);
glutSolidSphere(0.125, 10, 10);
glutSolidSphere(MARKER_RADIUS, 10, 10);
glPopMatrix();
glPushMatrix();
glColor3fv(gray);
glTranslatef(TREE_SCALE, 0, TREE_SCALE);
glutSolidSphere(0.125, 10, 10);
glTranslatef(MARKER_DISTANCE, 0, MARKER_DISTANCE);
glutSolidSphere(MARKER_RADIUS, 10, 10);
glPopMatrix();
}

View file

@ -231,14 +231,16 @@ void Hand::updateCollisions() {
}
}
// and the current avatar (ignoring everything below the parent of the parent of the last free joint)
glm::vec3 owningPenetration;
const Model& skeletonModel = _owningAvatar->getSkeletonModel();
int skipIndex = skeletonModel.getParentJointIndex(skeletonModel.getParentJointIndex(
skeletonModel.getLastFreeJointIndex((i == leftPalmIndex) ? skeletonModel.getLeftHandJointIndex() :
(i == rightPalmIndex) ? skeletonModel.getRightHandJointIndex() : -1)));
if (_owningAvatar->findSpherePenetration(palm.getPosition(), scaledPalmRadius, owningPenetration, skipIndex)) {
totalPenetration = addPenetrations(totalPenetration, owningPenetration);
if (Menu::getInstance()->isOptionChecked(MenuOption::HandsCollideWithSelf)) {
// and the current avatar (ignoring everything below the parent of the parent of the last free joint)
glm::vec3 owningPenetration;
const Model& skeletonModel = _owningAvatar->getSkeletonModel();
int skipIndex = skeletonModel.getParentJointIndex(skeletonModel.getParentJointIndex(
skeletonModel.getLastFreeJointIndex((i == leftPalmIndex) ? skeletonModel.getLeftHandJointIndex() :
(i == rightPalmIndex) ? skeletonModel.getRightHandJointIndex() : -1)));
if (_owningAvatar->findSpherePenetration(palm.getPosition(), scaledPalmRadius, owningPenetration, skipIndex)) {
totalPenetration = addPenetrations(totalPenetration, owningPenetration);
}
}
// un-penetrate

View file

@ -198,7 +198,7 @@ void Profile::processDataServerResponse(const QString& userString, const QString
if (coordinateItems.size() == 3 && orientationItems.size() == 3) {
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1);
NodeList::getInstance()->sendKillNode(QSet<NODE_TYPE>() << NODE_TYPE_AVATAR_MIXER);
qDebug() << "Changing domain to" << valueList[i].toLocal8Bit().constData() <<
", position to" << valueList[i + 1].toLocal8Bit().constData() <<

View file

@ -7,7 +7,11 @@
#include <cmath>
// include this before QOpenGLBuffer, which includes an earlier version of OpenGL
#include "InterfaceConfig.h"
#include <QNetworkReply>
#include <QOpenGLBuffer>
#include "Application.h"
#include "GeometryCache.h"
@ -241,6 +245,50 @@ void GeometryCache::renderHalfCylinder(int slices, int stacks) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
void GeometryCache::renderGrid(int xDivisions, int yDivisions) {
QOpenGLBuffer& buffer = _gridBuffers[IntPair(xDivisions, yDivisions)];
int vertices = (xDivisions + 1 + yDivisions + 1) * 2;
if (!buffer.isCreated()) {
GLfloat* vertexData = new GLfloat[vertices * 2];
GLfloat* vertex = vertexData;
for (int i = 0; i <= xDivisions; i++) {
float x = (float)i / xDivisions;
*(vertex++) = x;
*(vertex++) = 0.0f;
*(vertex++) = x;
*(vertex++) = 1.0f;
}
for (int i = 0; i <= yDivisions; i++) {
float y = (float)i / yDivisions;
*(vertex++) = 0.0f;
*(vertex++) = y;
*(vertex++) = 1.0f;
*(vertex++) = y;
}
buffer.create();
buffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
buffer.bind();
buffer.allocate(vertexData, vertices * 2 * sizeof(GLfloat));
delete[] vertexData;
} else {
buffer.bind();
}
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, 0);
glDrawArrays(GL_LINES, 0, vertices);
glDisableClientState(GL_VERTEX_ARRAY);
buffer.release();
}
QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url) {
QSharedPointer<NetworkGeometry> geometry = _networkGeometry.value(url);
if (geometry.isNull()) {

View file

@ -19,6 +19,7 @@
#include "InterfaceConfig.h"
class QNetworkReply;
class QOpenGLBuffer;
class NetworkGeometry;
class NetworkMesh;
@ -33,6 +34,7 @@ public:
void renderHemisphere(int slices, int stacks);
void renderSquare(int xDivisions, int yDivisions);
void renderHalfCylinder(int slices, int stacks);
void renderGrid(int xDivisions, int yDivisions);
/// Loads geometry from the specified URL.
QSharedPointer<NetworkGeometry> getGeometry(const QUrl& url);
@ -45,6 +47,7 @@ private:
QHash<IntPair, VerticesIndices> _hemisphereVBOs;
QHash<IntPair, VerticesIndices> _squareVBOs;
QHash<IntPair, VerticesIndices> _halfCylinderVBOs;
QHash<IntPair, QOpenGLBuffer> _gridBuffers;
QHash<QUrl, QWeakPointer<NetworkGeometry> > _networkGeometry;
};

View file

@ -0,0 +1,406 @@
//
// MetavoxelEditor.cpp
// interface
//
// Created by Andrzej Kapolka on 1/21/14.
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
#include <QComboBox>
#include <QDialogButtonBox>
#include <QDoubleSpinBox>
#include <QFormLayout>
#include <QGroupBox>
#include <QLineEdit>
#include <QListWidget>
#include <QMetaProperty>
#include <QPushButton>
#include <QVBoxLayout>
#include <AttributeRegistry.h>
#include "Application.h"
#include "MetavoxelEditor.h"
enum GridPlane {
GRID_PLANE_XY, GRID_PLANE_XZ, GRID_PLANE_YZ
};
const glm::vec2 INVALID_VECTOR(FLT_MAX, FLT_MAX);
MetavoxelEditor::MetavoxelEditor() :
QDialog(Application::getInstance()->getGLWidget()) {
setWindowTitle("Metavoxel Editor");
setAttribute(Qt::WA_DeleteOnClose);
QVBoxLayout* topLayout = new QVBoxLayout();
setLayout(topLayout);
QGroupBox* attributeGroup = new QGroupBox();
attributeGroup->setTitle("Attributes");
topLayout->addWidget(attributeGroup);
QVBoxLayout* attributeLayout = new QVBoxLayout();
attributeGroup->setLayout(attributeLayout);
attributeLayout->addWidget(_attributes = new QListWidget());
connect(_attributes, SIGNAL(itemSelectionChanged()), SLOT(updateValueEditor()));
QPushButton* newAttribute = new QPushButton("New...");
attributeLayout->addWidget(newAttribute);
connect(newAttribute, SIGNAL(clicked()), SLOT(createNewAttribute()));
QFormLayout* formLayout = new QFormLayout();
topLayout->addLayout(formLayout);
formLayout->addRow("Grid Plane:", _gridPlane = new QComboBox());
_gridPlane->addItem("X/Y");
_gridPlane->addItem("X/Z");
_gridPlane->addItem("Y/Z");
_gridPlane->setCurrentIndex(GRID_PLANE_XZ);
formLayout->addRow("Grid Spacing:", _gridSpacing = new QDoubleSpinBox());
_gridSpacing->setValue(0.1);
_gridSpacing->setMaximum(FLT_MAX);
_gridSpacing->setSingleStep(0.01);
connect(_gridSpacing, SIGNAL(valueChanged(double)), SLOT(updateGridPosition()));
formLayout->addRow("Grid Position:", _gridPosition = new QDoubleSpinBox());
_gridPosition->setSingleStep(0.1);
_gridPosition->setMinimum(-FLT_MAX);
_gridPosition->setMaximum(FLT_MAX);
_value = new QGroupBox();
_value->setTitle("Value");
topLayout->addWidget(_value);
QVBoxLayout* valueLayout = new QVBoxLayout();
_value->setLayout(valueLayout);
updateAttributes();
connect(Application::getInstance(), SIGNAL(renderingInWorldInterface()), SLOT(render()));
Application::getInstance()->getGLWidget()->installEventFilter(this);
resetState();
show();
if (_gridProgram.isLinked()) {
return;
}
switchToResourcesParentIfRequired();
_gridProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/grid.frag");
_gridProgram.link();
}
bool MetavoxelEditor::eventFilter(QObject* watched, QEvent* event) {
switch (_state) {
case HOVERING_STATE:
if (event->type() == QEvent::MouseButtonPress && _startPosition != INVALID_VECTOR) {
_state = DRAGGING_STATE;
return true;
}
break;
case DRAGGING_STATE:
if (event->type() == QEvent::MouseButtonRelease) {
_state = RAISING_STATE;
return true;
}
break;
case RAISING_STATE:
if (event->type() == QEvent::MouseButtonPress) {
if (_height != 0) {
// find the start and end corners in X/Y
float base = _gridPosition->value();
float top = base + _height;
glm::quat rotation = getGridRotation();
glm::vec3 start = rotation * glm::vec3(glm::min(_startPosition, _endPosition), glm::min(base, top));
float spacing = _gridSpacing->value();
glm::vec3 end = rotation * glm::vec3(glm::max(_startPosition, _endPosition) +
glm::vec2(spacing, spacing), glm::max(base, top));
// find the minimum and maximum extents after rotation
applyValue(glm::min(start, end), glm::max(start, end));
}
resetState();
return true;
}
break;
}
return false;
}
void MetavoxelEditor::updateValueEditor() {
QString selected = getSelectedAttribute();
if (selected.isNull()) {
_value->setVisible(false);
return;
}
_value->setVisible(true);
if (!_value->layout()->isEmpty()) {
delete _value->layout()->takeAt(0);
}
AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(selected);
QWidget* editor = attribute->createEditor();
if (editor) {
_value->layout()->addWidget(editor);
}
}
void MetavoxelEditor::createNewAttribute() {
QDialog dialog(this);
dialog.setWindowTitle("New Attribute");
QVBoxLayout layout;
dialog.setLayout(&layout);
QFormLayout form;
layout.addLayout(&form);
QLineEdit name;
form.addRow("Name:", &name);
QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
dialog.connect(&buttons, SIGNAL(accepted()), SLOT(accept()));
dialog.connect(&buttons, SIGNAL(rejected()), SLOT(reject()));
layout.addWidget(&buttons);
if (!dialog.exec()) {
return;
}
QString nameText = name.text().trimmed();
AttributeRegistry::getInstance()->registerAttribute(new QRgbAttribute(nameText));
updateAttributes(nameText);
}
void MetavoxelEditor::updateGridPosition() {
// make sure our grid position matches our grid spacing
double step = _gridSpacing->value();
if (step > 0.0) {
_gridPosition->setSingleStep(step);
_gridPosition->setValue(step * floor(_gridPosition->value() / step));
}
}
void MetavoxelEditor::render() {
QString selected = getSelectedAttribute();
if (selected.isNull()) {
resetState();
return;
}
glDisable(GL_LIGHTING);
glDepthMask(GL_FALSE);
glPushMatrix();
glm::quat rotation = getGridRotation();
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z);
glm::quat inverseRotation = glm::inverse(rotation);
glm::vec3 rayOrigin = inverseRotation * Application::getInstance()->getMouseRayOrigin();
glm::vec3 rayDirection = inverseRotation * Application::getInstance()->getMouseRayDirection();
float spacing = _gridSpacing->value();
float position = _gridPosition->value();
if (_state == RAISING_STATE) {
// find the plane at the mouse position, orthogonal to the plane, facing the eye position
glLineWidth(4.0f);
glm::vec3 eyePosition = inverseRotation * Application::getInstance()->getViewFrustum()->getOffsetPosition();
glm::vec3 mousePoint = glm::vec3(_mousePosition, position);
glm::vec3 right = glm::cross(glm::vec3(0.0f, 0.0f, 1.0f), eyePosition - mousePoint);
glm::vec3 normal = glm::cross(right, glm::vec3(0.0f, 0.0f, 1.0f));
float divisor = glm::dot(normal, rayDirection);
if (fabs(divisor) > EPSILON) {
float distance = (glm::dot(normal, mousePoint) - glm::dot(normal, rayOrigin)) / divisor;
float projection = rayOrigin.z + distance * rayDirection.z;
_height = spacing * roundf(projection / spacing) - position;
}
} else if (fabs(rayDirection.z) > EPSILON) {
// find the intersection of the rotated mouse ray with the plane
float distance = (position - rayOrigin.z) / rayDirection.z;
_mousePosition = glm::vec2(rayOrigin + rayDirection * distance);
glm::vec2 snappedPosition = spacing * glm::floor(_mousePosition / spacing);
if (_state == HOVERING_STATE) {
_startPosition = _endPosition = snappedPosition;
glLineWidth(2.0f);
} else if (_state == DRAGGING_STATE) {
_endPosition = snappedPosition;
glLineWidth(4.0f);
}
} else {
// cancel any operation in progress
resetState();
}
const float GRID_BRIGHTNESS = 0.5f;
if (_startPosition != INVALID_VECTOR) {
glm::vec2 minimum = glm::min(_startPosition, _endPosition);
glm::vec2 maximum = glm::max(_startPosition, _endPosition);
glPushMatrix();
glTranslatef(minimum.x, minimum.y, position);
glScalef(maximum.x + spacing - minimum.x, maximum.y + spacing - minimum.y, _height);
glTranslatef(0.5f, 0.5f, 0.5f);
if (_state != HOVERING_STATE) {
const float BOX_ALPHA = 0.25f;
QColor color = getValue().value<QColor>();
if (color.isValid()) {
glColor4f(color.redF(), color.greenF(), color.blueF(), BOX_ALPHA);
} else {
glColor4f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS, BOX_ALPHA);
}
glEnable(GL_CULL_FACE);
glutSolidCube(1.0);
glDisable(GL_CULL_FACE);
}
glutWireCube(1.0);
glPopMatrix();
}
glLineWidth(1.0f);
// center the grid around the camera position on the plane
glm::vec3 rotated = inverseRotation * Application::getInstance()->getCamera()->getPosition();
const int GRID_DIVISIONS = 300;
glTranslatef(spacing * (floorf(rotated.x / spacing) - GRID_DIVISIONS / 2),
spacing * (floorf(rotated.y / spacing) - GRID_DIVISIONS / 2), position);
float scale = GRID_DIVISIONS * spacing;
glScalef(scale, scale, scale);
_gridProgram.bind();
glColor3f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS);
Application::getInstance()->getGeometryCache()->renderGrid(GRID_DIVISIONS, GRID_DIVISIONS);
_gridProgram.release();
glPopMatrix();
glEnable(GL_LIGHTING);
glDepthMask(GL_TRUE);
}
void MetavoxelEditor::updateAttributes(const QString& select) {
// remember the selection in order to preserve it
QString selected = select.isNull() ? getSelectedAttribute() : select;
_attributes->clear();
// sort the names for consistent ordering
QList<QString> names = AttributeRegistry::getInstance()->getAttributes().keys();
qSort(names);
foreach (const QString& name, names) {
QListWidgetItem* item = new QListWidgetItem(name);
_attributes->addItem(item);
if (name == selected || selected.isNull()) {
item->setSelected(true);
selected = name;
}
}
}
QString MetavoxelEditor::getSelectedAttribute() const {
QList<QListWidgetItem*> selectedItems = _attributes->selectedItems();
return selectedItems.isEmpty() ? QString() : selectedItems.first()->text();
}
glm::quat MetavoxelEditor::getGridRotation() const {
// for simplicity, we handle the other two planes by rotating them onto X/Y and performing computation there
switch (_gridPlane->currentIndex()) {
case GRID_PLANE_XY:
return glm::quat();
case GRID_PLANE_XZ:
return glm::angleAxis(-90.0f, 1.0f, 0.0f, 0.0f);
case GRID_PLANE_YZ:
default:
return glm::angleAxis(90.0f, 0.0f, 1.0f, 0.0f);
}
}
void MetavoxelEditor::resetState() {
_state = HOVERING_STATE;
_startPosition = INVALID_VECTOR;
_height = 0.0f;
}
class Applier : public MetavoxelVisitor {
public:
Applier(const glm::vec3& minimum, const glm::vec3& maximum, float granularity, const AttributeValue& value);
virtual bool visit(MetavoxelInfo& info);
protected:
glm::vec3 _minimum;
glm::vec3 _maximum;
float _granularity;
AttributeValue _value;
};
Applier::Applier(const glm::vec3& minimum, const glm::vec3& maximum, float granularity, const AttributeValue& value) :
MetavoxelVisitor(QVector<AttributePointer>(), QVector<AttributePointer>() << value.getAttribute()),
_minimum(minimum),
_maximum(maximum),
_granularity(granularity),
_value(value) {
}
bool Applier::visit(MetavoxelInfo& info) {
// find the intersection between volume and voxel
glm::vec3 minimum = glm::max(info.minimum, _minimum);
glm::vec3 maximum = glm::min(info.minimum + glm::vec3(info.size, info.size, info.size), _maximum);
glm::vec3 size = maximum - minimum;
if (size.x <= 0.0f || size.y <= 0.0f || size.z <= 0.0f) {
return false; // disjoint
}
float volume = (size.x * size.y * size.z) / (info.size * info.size * info.size);
if (volume >= 1.0f) {
info.outputValues[0] = _value;
return false; // entirely contained
}
if (info.size <= _granularity) {
if (volume > 0.5f) {
info.outputValues[0] = _value;
}
return false; // reached granularity limit; take best guess
}
return true; // subdivide
}
void MetavoxelEditor::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) {
AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(getSelectedAttribute());
if (!attribute) {
return;
}
OwnedAttributeValue value(attribute, attribute->createFromVariant(getValue()));
Applier applier(minimum, maximum, _gridSpacing->value(), value);
Application::getInstance()->getMetavoxels()->getData().guide(applier);
}
QVariant MetavoxelEditor::getValue() const {
if (_value->layout()->isEmpty()) {
return QVariant();
}
QWidget* editor = _value->layout()->itemAt(0)->widget();
return editor->metaObject()->userProperty().read(editor);
}
ProgramObject MetavoxelEditor::_gridProgram;

View file

@ -0,0 +1,67 @@
//
// MetavoxelEditor.h
// interface
//
// Created by Andrzej Kapolka on 1/21/14.
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
//
#ifndef __interface__MetavoxelEditor__
#define __interface__MetavoxelEditor__
#include <QDialog>
#include "renderer/ProgramObject.h"
class QComboBox;
class QDoubleSpinBox;
class QGroupBox;
class QListWidget;
/// Allows editing metavoxels.
class MetavoxelEditor : public QDialog {
Q_OBJECT
public:
MetavoxelEditor();
virtual bool eventFilter(QObject* watched, QEvent* event);
private slots:
void updateValueEditor();
void createNewAttribute();
void updateGridPosition();
void render();
private:
void updateAttributes(const QString& select = QString());
QString getSelectedAttribute() const;
glm::quat getGridRotation() const;
void resetState();
void applyValue(const glm::vec3& minimum, const glm::vec3& maximum);
QVariant getValue() const;
QListWidget* _attributes;
QComboBox* _gridPlane;
QDoubleSpinBox* _gridSpacing;
QDoubleSpinBox* _gridPosition;
QGroupBox* _value;
enum State { HOVERING_STATE, DRAGGING_STATE, RAISING_STATE };
State _state;
glm::vec2 _mousePosition; ///< the position of the mouse in rotated space
glm::vec2 _startPosition; ///< the first corner of the selection base
glm::vec2 _endPosition; ///< the second corner of the selection base
float _height; ///< the selection height
static ProgramObject _gridProgram;
};
#endif /* defined(__interface__MetavoxelEditor__) */

View file

@ -6,7 +6,10 @@
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <QColorDialog>
#include <QPushButton>
#include <QScriptEngine>
#include <QVBoxLayout>
#include "AttributeRegistry.h"
#include "MetavoxelData.h"
@ -69,12 +72,12 @@ 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, value) {
}
OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute, void* value) :
AttributeValue(attribute, attribute ? attribute->create(value) : NULL) {
OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute) :
AttributeValue(attribute, attribute ? attribute->create() : NULL) {
}
OwnedAttributeValue::OwnedAttributeValue(const AttributeValue& other) :
@ -92,7 +95,7 @@ OwnedAttributeValue& OwnedAttributeValue::operator=(const AttributeValue& other)
_attribute->destroy(_value);
}
if ((_attribute = other.getAttribute())) {
_value = _attribute->create(other.getValue());
_value = other.copy();
}
return *this;
}
@ -132,6 +135,41 @@ void* QRgbAttribute::createFromScript(const QScriptValue& value, QScriptEngine*
return encodeInline((QRgb)value.toUInt32());
}
void* QRgbAttribute::createFromVariant(const QVariant& value) const {
switch (value.userType()) {
case QMetaType::QColor:
return encodeInline(value.value<QColor>().rgba());
default:
return encodeInline((QRgb)value.toUInt());
}
}
QWidget* QRgbAttribute::createEditor(QWidget* parent) const {
QRgbEditor* editor = new QRgbEditor(parent);
editor->setColor(QColor::fromRgba(_defaultValue));
return editor;
}
QRgbEditor::QRgbEditor(QWidget* parent) : QWidget(parent) {
setLayout(new QVBoxLayout());
layout()->addWidget(_button = new QPushButton());
connect(_button, SIGNAL(clicked()), SLOT(selectColor()));
}
void QRgbEditor::setColor(const QColor& color) {
QString name = (_color = color).name();
_button->setStyleSheet(QString("background: %1; color: %2").arg(name, QColor::fromRgb(~color.rgb()).name()));
_button->setText(name);
}
void QRgbEditor::selectColor() {
QColor color = QColorDialog::getColor(_color, this, QString(), QColorDialog::ShowAlphaChannel);
if (color.isValid()) {
setColor(color);
}
}
PolymorphicData::~PolymorphicData() {
}

View file

@ -16,9 +16,11 @@
#include <QSharedData>
#include <QSharedPointer>
#include <QString>
#include <QWidget>
#include "Bitstream.h"
class QPushButton;
class QScriptContext;
class QScriptEngine;
class QScriptValue;
@ -52,6 +54,9 @@ public:
/// Retrieves an attribute by name.
AttributePointer getAttribute(const QString& name) const { return _attributes.value(name); }
/// Returns a reference to the attribute hash.
const QHash<QString, AttributePointer>& getAttributes() const { return _attributes; }
/// Returns a reference to the standard PolymorphicDataPointer "guide" attribute.
const AttributePointer& getGuideAttribute() const { return _guideAttribute; }
@ -113,11 +118,19 @@ protected:
class OwnedAttributeValue : public AttributeValue {
public:
OwnedAttributeValue(const AttributePointer& attribute = AttributePointer());
/// Assumes ownership of the specified value. It will be destroyed when this is destroyed or reassigned.
OwnedAttributeValue(const AttributePointer& attribute, void* value);
/// Creates an owned attribute with a copy of the specified attribute's default value.
OwnedAttributeValue(const AttributePointer& attribute = AttributePointer());
/// Creates an owned attribute with a copy of the specified other value.
OwnedAttributeValue(const AttributeValue& other);
/// Destroys the current value, if any.
~OwnedAttributeValue();
/// Destroys the current value, if any, and copies the specified other value.
OwnedAttributeValue& operator=(const AttributeValue& other);
};
@ -153,6 +166,12 @@ public:
virtual void* getDefaultValue() const = 0;
virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const { return create(); }
virtual void* createFromVariant(const QVariant& value) const { return create(); }
/// Creates a widget to use to edit values of this attribute, or returns NULL if the attribute isn't editable.
/// The widget should have a single "user" property that will be used to get/set the value.
virtual QWidget* createEditor(QWidget* parent = NULL) const { return NULL; }
};
/// A simple attribute class that stores its values inline.
@ -222,6 +241,33 @@ public:
virtual bool merge(void*& parent, void* children[]) const;
virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const;
virtual void* createFromVariant(const QVariant& value) const;
virtual QWidget* createEditor(QWidget* parent = NULL) const;
};
/// Editor for RGBA values.
class QRgbEditor : public QWidget {
Q_OBJECT
Q_PROPERTY(QColor color MEMBER _color WRITE setColor USER true)
public:
QRgbEditor(QWidget* parent);
public slots:
void setColor(const QColor& color);
private slots:
void selectColor();
private:
QPushButton* _button;
QColor _color;
};
/// An attribute class that stores pointers to its values.

View file

@ -32,46 +32,36 @@ MetavoxelData& MetavoxelData::operator=(const MetavoxelData& other) {
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];
const QVector<AttributePointer>& inputs = visitor.getInputs();
const QVector<AttributePointer>& outputs = visitor.getOutputs();
MetavoxelVisitation firstVisitation = { this, NULL, visitor, QVector<MetavoxelNode*>(inputs.size() + 1),
QVector<MetavoxelNode*>(outputs.size()), { glm::vec3(), TOP_LEVEL_SIZE,
QVector<AttributeValue>(inputs.size() + 1), QVector<AttributeValue>(outputs.size()) } };
for (int i = 0; i < inputs.size(); i++) {
MetavoxelNode* node = _roots.value(inputs.at(i));
firstVisitation.inputNodes[i] = node;
firstVisitation.info.inputValues[i] = node ? node->getAttributeValue(inputs[i]) : inputs[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<
firstVisitation.inputNodes.last() = node;
firstVisitation.info.inputValues.last() = node ? node->getAttributeValue(guideAttribute) : guideAttribute;
for (int i = 0; i < outputs.size(); i++) {
MetavoxelNode* node = _roots.value(outputs.at(i));
firstVisitation.outputNodes[i] = node;
}
static_cast<MetavoxelGuide*>(firstVisitation.info.inputValues.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->decrementReferenceCount(attributeValue.getAttribute());
_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);
for (int i = 0; i < outputs.size(); i++) {
AttributeValue& value = firstVisitation.info.outputValues[i];
if (value.getAttribute()) {
MetavoxelNode* node = firstVisitation.outputNodes.at(i);
if (node->isLeaf() && value.isDefault()) {
node->decrementReferenceCount(value.getAttribute());
_roots.remove(value.getAttribute());
}
}
node = child;
}
return node->getAttributeValue(attribute);
}
void MetavoxelData::read(Bitstream& in) {
@ -80,7 +70,7 @@ void MetavoxelData::read(Bitstream& in) {
_roots.clear();
// read in the new roots, reusing old ones where appropriate
qint32 rootCount;
int rootCount;
in >> rootCount;
for (int i = 0; i < rootCount; i++) {
AttributePointer attribute;
@ -100,7 +90,7 @@ void MetavoxelData::read(Bitstream& in) {
}
void MetavoxelData::write(Bitstream& out) const {
out << (qint32)_roots.size();
out << _roots.size();
for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) {
out.getAttributeStreamer() << it.key();
it.value()->write(it.key(), out);
@ -108,9 +98,70 @@ void MetavoxelData::write(Bitstream& out) const {
}
void MetavoxelData::readDelta(const MetavoxelData& reference, Bitstream& in) {
int changedCount;
in >> changedCount;
for (int i = 0; i < changedCount; i++) {
AttributePointer attribute;
in.getAttributeStreamer() >> attribute;
MetavoxelNode*& root = _roots[attribute];
if (!root) {
root = new MetavoxelNode(attribute);
}
MetavoxelNode* referenceRoot = reference._roots.value(attribute);
if (referenceRoot) {
root->readDelta(attribute, *referenceRoot, in);
} else {
root->read(attribute, in);
}
}
int removedCount;
in >> removedCount;
for (int i = 0; i < removedCount; i++) {
AttributePointer attribute;
in.getAttributeStreamer() >> attribute;
}
}
void MetavoxelData::writeDelta(const MetavoxelData& reference, Bitstream& out) const {
// count the number of roots added/changed, then write
int changedCount = 0;
for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) {
MetavoxelNode* referenceRoot = reference._roots.value(it.key());
if (it.value() != referenceRoot) {
changedCount++;
}
}
out << changedCount;
for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) {
MetavoxelNode* referenceRoot = reference._roots.value(it.key());
if (it.value() != referenceRoot) {
out.getAttributeStreamer() << it.key();
if (referenceRoot) {
it.value()->writeDelta(it.key(), *referenceRoot, out);
} else {
it.value()->write(it.key(), out);
}
}
}
// same with nodes removed
int removedCount = 0;
for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = reference._roots.constBegin();
it != reference._roots.constEnd(); it++) {
if (!_roots.contains(it.key())) {
removedCount++;
}
}
out << removedCount;
for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = reference._roots.constBegin();
it != reference._roots.constEnd(); it++) {
if (!_roots.contains(it.key())) {
out.getAttributeStreamer() << it.key();
}
}
}
void MetavoxelData::incrementRootReferenceCounts() {
@ -128,10 +179,11 @@ void MetavoxelData::decrementRootReferenceCounts() {
void writeDelta(const MetavoxelDataPointer& data, const MetavoxelDataPointer& reference, Bitstream& out) {
if (data == reference) {
out << false;
return;
} else {
out << true;
data->writeDelta(*reference, out);
}
out << true;
data->writeDelta(*reference, out);
}
void readDelta(MetavoxelDataPointer& data, const MetavoxelDataPointer& reference, Bitstream& in) {
@ -140,6 +192,9 @@ void readDelta(MetavoxelDataPointer& data, const MetavoxelDataPointer& reference
if (changed) {
data.detach();
data->readDelta(*reference, in);
} else {
data = reference;
}
}
@ -150,34 +205,6 @@ MetavoxelNode::MetavoxelNode(const AttributeValue& attributeValue) : _referenceC
}
}
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();
@ -188,6 +215,18 @@ AttributeValue MetavoxelNode::getAttributeValue(const AttributePointer& attribut
return AttributeValue(attribute, _attributeValue);
}
void MetavoxelNode::mergeChildren(const AttributePointer& attribute) {
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 (attribute->merge(_attributeValue, childValues) && allLeaves) {
clearChildren(attribute);
}
}
bool MetavoxelNode::isLeaf() const {
for (int i = 0; i < CHILD_COUNT; i++) {
if (_children[i]) {
@ -229,11 +268,6 @@ void MetavoxelNode::write(const AttributePointer& attribute, Bitstream& out) con
}
void MetavoxelNode::readDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& in) {
bool different;
in >> different;
if (!different) {
return;
}
bool leaf;
in >> leaf;
attribute->readDelta(in, _attributeValue, reference._attributeValue, leaf);
@ -254,11 +288,6 @@ void MetavoxelNode::readDelta(const AttributePointer& attribute, const Metavoxel
}
void MetavoxelNode::writeDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& out) const {
if (this == &reference) {
out << false;
return;
}
out << true;
bool leaf = isLeaf();
out << leaf;
attribute->writeDelta(out, _attributeValue, reference._attributeValue, leaf);
@ -300,18 +329,9 @@ void MetavoxelNode::clearChildren(const AttributePointer& attribute) {
}
}
int MetavoxelPath::operator[](int index) const {
return (int)_array.at(index * BITS_PER_ELEMENT) | ((int)_array.at(index * BITS_PER_ELEMENT + 1) << 1) |
((int)_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;
MetavoxelVisitor::MetavoxelVisitor(const QVector<AttributePointer>& inputs, const QVector<AttributePointer>& outputs) :
_inputs(inputs),
_outputs(outputs) {
}
PolymorphicData* DefaultMetavoxelGuide::clone() const {
@ -323,42 +343,84 @@ 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) {
visitation.info.isLeaf = visitation.allInputNodesLeaves();
bool keepGoing = visitation.visitor.visit(visitation.info);
for (int i = 0; i < visitation.outputNodes.size(); i++) {
AttributeValue& value = visitation.info.outputValues[i];
if (value.getAttribute()) {
MetavoxelNode*& node = visitation.outputNodes[i];
if (!node) {
node = visitation.createOutputNode(i);
}
node->setAttributeValue(value);
}
}
if (!keepGoing) {
return;
}
MetavoxelVisitation nextVisitation = { visitation.visitor, QVector<MetavoxelNode*>(visitation.nodes.size()),
{ glm::vec3(), visitation.info.size * 0.5f, QVector<AttributeValue>(visitation.nodes.size()) } };
MetavoxelVisitation nextVisitation = { visitation.data, &visitation, visitation.visitor,
QVector<MetavoxelNode*>(visitation.inputNodes.size()), QVector<MetavoxelNode*>(visitation.outputNodes.size()),
{ glm::vec3(), visitation.info.size * 0.5f, QVector<AttributeValue>(visitation.inputNodes.size()),
QVector<AttributeValue>(visitation.outputNodes.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);
for (int j = 0; j < visitation.inputNodes.size(); j++) {
MetavoxelNode* node = visitation.inputNodes.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.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ?
child->getAttributeValue(visitation.info.inputValues[j].getAttribute()) :
visitation.info.inputValues[j];
}
for (int j = 0; j < visitation.outputNodes.size(); j++) {
MetavoxelNode* node = visitation.outputNodes.at(j);
MetavoxelNode* child = node ? node->getChild(i) : NULL;
nextVisitation.outputNodes[j] = child;
}
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<
nextVisitation.childIndex = i;
static_cast<MetavoxelGuide*>(nextVisitation.info.inputValues.last().getInlineValue<
PolymorphicDataPointer>().data())->guide(nextVisitation);
for (int j = 0; j < nextVisitation.outputNodes.size(); j++) {
AttributeValue& value = nextVisitation.info.outputValues[j];
if (value.getAttribute()) {
visitation.info.outputValues[j] = value;
value = AttributeValue();
}
}
}
for (int i = 0; i < visitation.outputNodes.size(); i++) {
AttributeValue& value = visitation.info.outputValues[i];
if (value.getAttribute()) {
MetavoxelNode* node = visitation.outputNodes.at(i);
node->mergeChildren(value.getAttribute());
value = node->getAttributeValue(value.getAttribute());
}
}
}
QScriptValue ScriptedMetavoxelGuide::getAttributes(QScriptContext* context, QScriptEngine* engine) {
ScriptedMetavoxelGuide* guide = static_cast<ScriptedMetavoxelGuide*>(context->callee().data().toVariant().value<void*>());
static QScriptValue getAttributes(QScriptEngine* engine, ScriptedMetavoxelGuide* guide,
const QVector<AttributePointer>& attributes) {
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::getInputs(QScriptContext* context, QScriptEngine* engine) {
ScriptedMetavoxelGuide* guide = static_cast<ScriptedMetavoxelGuide*>(context->callee().data().toVariant().value<void*>());
return getAttributes(engine, guide, guide->_visitation->visitor.getInputs());
}
QScriptValue ScriptedMetavoxelGuide::getOutputs(QScriptContext* context, QScriptEngine* engine) {
ScriptedMetavoxelGuide* guide = static_cast<ScriptedMetavoxelGuide*>(context->callee().data().toVariant().value<void*>());
return getAttributes(engine, guide, guide->_visitation->visitor.getOutputs());
}
QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngine* engine) {
ScriptedMetavoxelGuide* guide = static_cast<ScriptedMetavoxelGuide*>(context->callee().data().toVariant().value<void*>());
@ -367,26 +429,26 @@ QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngin
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() };
infoValue.property(guide->_sizeHandle).toNumber(), guide->_visitation->info.inputValues,
guide->_visitation->info.outputValues, 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);
QScriptValue inputValues = infoValue.property(guide->_inputValuesHandle);
const QVector<AttributePointer>& inputs = guide->_visitation->visitor.getInputs();
for (int i = 0; i < inputs.size(); i++) {
QScriptValue attributeValue = inputValues.property(i);
if (attributeValue.isValid()) {
info.attributeValues[i] = AttributeValue(attributes.at(i),
attributes.at(i)->createFromScript(attributeValue, engine));
info.inputValues[i] = AttributeValue(inputs.at(i),
inputs.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());
for (int i = 0; i < inputs.size(); i++) {
if (inputValues.property(i).isValid()) {
info.inputValues[i].getAttribute()->destroy(info.inputValues[i].getValue());
}
}
@ -397,16 +459,19 @@ ScriptedMetavoxelGuide::ScriptedMetavoxelGuide(const QScriptValue& guideFunction
_guideFunction(guideFunction),
_minimumHandle(guideFunction.engine()->toStringHandle("minimum")),
_sizeHandle(guideFunction.engine()->toStringHandle("size")),
_attributeValuesHandle(guideFunction.engine()->toStringHandle("attributeValues")),
_inputValuesHandle(guideFunction.engine()->toStringHandle("inputValues")),
_outputValuesHandle(guideFunction.engine()->toStringHandle("outputValues")),
_isLeafHandle(guideFunction.engine()->toStringHandle("isLeaf")),
_getAttributesFunction(guideFunction.engine()->newFunction(getAttributes, 0)),
_getInputsFunction(guideFunction.engine()->newFunction(getInputs, 0)),
_getOutputsFunction(guideFunction.engine()->newFunction(getOutputs, 0)),
_visitFunction(guideFunction.engine()->newFunction(visit, 1)),
_info(guideFunction.engine()->newObject()),
_minimum(guideFunction.engine()->newArray(3)) {
_arguments.append(guideFunction.engine()->newObject());
QScriptValue visitor = guideFunction.engine()->newObject();
visitor.setProperty("getAttributes", _getAttributesFunction);
visitor.setProperty("getInputs", _getInputsFunction);
visitor.setProperty("getOutputs", _getOutputsFunction);
visitor.setProperty("visit", _visitFunction);
_arguments[0].setProperty("visitor", visitor);
_arguments[0].setProperty("info", _info);
@ -419,7 +484,7 @@ PolymorphicData* ScriptedMetavoxelGuide::clone() const {
void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
QScriptValue data = _guideFunction.engine()->newVariant(QVariant::fromValue<void*>(this));
_getAttributesFunction.setData(data);
_getInputsFunction.setData(data);
_visitFunction.setData(data);
_minimum.setProperty(0, visitation.info.minimum.x);
_minimum.setProperty(1, visitation.info.minimum.y);
@ -433,11 +498,29 @@ void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
}
}
bool MetavoxelVisitation::allNodesLeaves() const {
foreach (MetavoxelNode* node, nodes) {
bool MetavoxelVisitation::allInputNodesLeaves() const {
foreach (MetavoxelNode* node, inputNodes) {
if (node != NULL && !node->isLeaf()) {
return false;
}
}
return true;
}
MetavoxelNode* MetavoxelVisitation::createOutputNode(int index) {
const AttributePointer& attribute = visitor.getOutputs().at(index);
if (previous) {
MetavoxelNode*& parent = previous->outputNodes[index];
if (!parent) {
parent = previous->createOutputNode(index);
}
AttributeValue value = parent->getAttributeValue(attribute);
for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) {
parent->_children[i] = new MetavoxelNode(value);
}
return parent->_children[childIndex];
} else {
return data->_roots[attribute] = new MetavoxelNode(attribute);
}
}

View file

@ -24,7 +24,6 @@
class QScriptContext;
class MetavoxelNode;
class MetavoxelPath;
class MetavoxelVisitation;
class MetavoxelVisitor;
@ -41,12 +40,6 @@ public:
/// 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;
void read(Bitstream& in);
void write(Bitstream& out) const;
@ -54,6 +47,8 @@ public:
void writeDelta(const MetavoxelData& reference, Bitstream& out) const;
private:
friend class MetavoxelVisitation;
void incrementRootReferenceCounts();
void decrementRootReferenceCounts();
@ -74,17 +69,13 @@ 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;
void mergeChildren(const AttributePointer& attribute);
MetavoxelNode* getChild(int index) const { return _children[index]; }
void setChild(int index, MetavoxelNode* child) { _children[index] = child; }
@ -108,6 +99,8 @@ public:
private:
Q_DISABLE_COPY(MetavoxelNode)
friend class MetavoxelVisitation;
void clearChildren(const AttributePointer& attribute);
int _referenceCount;
@ -115,31 +108,14 @@ private:
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;
QVector<AttributeValue> inputValues;
QVector<AttributeValue> outputValues;
bool isLeaf;
};
@ -147,19 +123,23 @@ public:
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; }
MetavoxelVisitor(const QVector<AttributePointer>& inputs, const QVector<AttributePointer>& outputs);
/// Returns a reference to the list of input attributes desired.
const QVector<AttributePointer>& getInputs() const { return _inputs; }
/// Returns a reference to the list of output attributes provided.
const QVector<AttributePointer>& getOutputs() const { return _outputs; }
/// Visits a metavoxel.
/// \param info the metavoxel ata
/// \param if true, continue descending; if false, stop
virtual bool visit(const MetavoxelInfo& info) = 0;
/// \param info the metavoxel data
/// \return if true, continue descending; if false, stop
virtual bool visit(MetavoxelInfo& info) = 0;
protected:
QVector<AttributePointer> _attributes;
QVector<AttributePointer> _inputs;
QVector<AttributePointer> _outputs;
};
/// Interface for objects that guide metavoxel visitors.
@ -191,16 +171,19 @@ public:
private:
static QScriptValue getAttributes(QScriptContext* context, QScriptEngine* engine);
static QScriptValue getInputs(QScriptContext* context, QScriptEngine* engine);
static QScriptValue getOutputs(QScriptContext* context, QScriptEngine* engine);
static QScriptValue visit(QScriptContext* context, QScriptEngine* engine);
QScriptValue _guideFunction;
QScriptString _minimumHandle;
QScriptString _sizeHandle;
QScriptString _attributeValuesHandle;
QScriptString _inputValuesHandle;
QScriptString _outputValuesHandle;
QScriptString _isLeafHandle;
QScriptValueList _arguments;
QScriptValue _getAttributesFunction;
QScriptValue _getInputsFunction;
QScriptValue _getOutputsFunction;
QScriptValue _visitFunction;
QScriptValue _info;
QScriptValue _minimum;
@ -212,11 +195,16 @@ private:
class MetavoxelVisitation {
public:
MetavoxelData* data;
MetavoxelVisitation* previous;
MetavoxelVisitor& visitor;
QVector<MetavoxelNode*> nodes;
QVector<MetavoxelNode*> inputNodes;
QVector<MetavoxelNode*> outputNodes;
MetavoxelInfo info;
int childIndex;
bool allNodesLeaves() const;
bool allInputNodesLeaves() const;
MetavoxelNode* createOutputNode(int index);
};
#endif /* defined(__interface__MetavoxelData__) */

View file

@ -569,8 +569,7 @@ void OctreeServer::run() {
nodeList->setOwnerType(getMyNodeType());
// we need to ask the DS about agents so we can ping/reply with them
const char nodeTypesOfInterest[] = { NODE_TYPE_AGENT, NODE_TYPE_ANIMATION_SERVER};
nodeList->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest));
nodeList->addNodeTypeToInterestSet(NODE_TYPE_AGENT);
setvbuf(stdout, NULL, _IOLBF, 0);

View file

@ -15,14 +15,17 @@
#include <PacketHeaders.h>
#include "JurisdictionListener.h"
JurisdictionListener::JurisdictionListener(NODE_TYPE type, PacketSenderNotify* notify) :
_packetSender(notify, JurisdictionListener::DEFAULT_PACKETS_PER_SECOND)
JurisdictionListener::JurisdictionListener(NODE_TYPE type) :
_packetSender(JurisdictionListener::DEFAULT_PACKETS_PER_SECOND)
{
_nodeType = type;
ReceivedPacketProcessor::_dontSleep = true; // we handle sleeping so this class doesn't need to
connect(NodeList::getInstance(), &NodeList::nodeKilled, this, &JurisdictionListener::nodeKilled);
//qDebug("JurisdictionListener::JurisdictionListener(NODE_TYPE type=%c)", type);
// tell our NodeList we want to hear about nodes with our node type
NodeList::getInstance()->addNodeTypeToInterestSet(type);
}
void JurisdictionListener::nodeKilled(SharedNodePointer node) {

View file

@ -27,7 +27,7 @@ public:
static const int DEFAULT_PACKETS_PER_SECOND = 1;
static const int NO_SERVER_CHECK_RATE = 60; // if no servers yet detected, keep checking at 60fps
JurisdictionListener(NODE_TYPE type = NODE_TYPE_VOXEL_SERVER, PacketSenderNotify* notify = NULL);
JurisdictionListener(NODE_TYPE type = NODE_TYPE_VOXEL_SERVER);
virtual bool process();

View file

@ -16,10 +16,10 @@
#include "JurisdictionSender.h"
JurisdictionSender::JurisdictionSender(JurisdictionMap* map, NODE_TYPE type, PacketSenderNotify* notify) :
JurisdictionSender::JurisdictionSender(JurisdictionMap* map, NODE_TYPE type) :
ReceivedPacketProcessor(),
_jurisdictionMap(map),
_packetSender(notify, JurisdictionSender::DEFAULT_PACKETS_PER_SECOND)
_packetSender(JurisdictionSender::DEFAULT_PACKETS_PER_SECOND)
{
_nodeType = type;
}

View file

@ -26,7 +26,7 @@ class JurisdictionSender : public ReceivedPacketProcessor {
public:
static const int DEFAULT_PACKETS_PER_SECOND = 1;
JurisdictionSender(JurisdictionMap* map, NODE_TYPE type = NODE_TYPE_VOXEL_SERVER, PacketSenderNotify* notify = NULL);
JurisdictionSender(JurisdictionMap* map, NODE_TYPE type = NODE_TYPE_VOXEL_SERVER);
~JurisdictionSender();
void setJurisdiction(JurisdictionMap* map) { _jurisdictionMap = map; }

View file

@ -592,6 +592,7 @@ bool findSpherePenetrationOp(OctreeElement* element, void* extraData) {
if (element->hasContent()) {
glm::vec3 elementPenetration;
if (element->findSpherePenetration(args->center, args->radius, elementPenetration, &args->penetratedObject)) {
// NOTE: it is possible for this penetration accumulation algorithm to produce a final penetration vector with zero length.
args->penetration = addPenetrations(args->penetration, elementPenetration * (float)(TREE_SCALE));
args->found = true;
}
@ -1390,9 +1391,8 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* node) {
bytesWritten = encodeTreeBitstream(subTree, &packetData, nodeBag, params);
unlock();
// if bytesWritten == 0, then it means that the subTree couldn't fit, and so we should reset the packet
// and reinsert the node in our bag and try again...
if (bytesWritten == 0) {
// if the subTree couldn't fit, and so we should reset the packet and reinsert the node in our bag and try again...
if (bytesWritten == 0 && (params.stopReason == EncodeBitstreamParams::DIDNT_FIT)) {
if (packetData.hasContent()) {
file.write((const char*)packetData.getFinalizedData(), packetData.getFinalizedSize());
lastPacketWritten = true;

View file

@ -27,8 +27,8 @@ EditPacketBuffer::EditPacketBuffer(PACKET_TYPE type, unsigned char* buffer, ssiz
const int OctreeEditPacketSender::DEFAULT_MAX_PENDING_MESSAGES = PacketSender::DEFAULT_PACKETS_PER_SECOND;
OctreeEditPacketSender::OctreeEditPacketSender(PacketSenderNotify* notify) :
PacketSender(notify),
OctreeEditPacketSender::OctreeEditPacketSender() :
PacketSender(),
_shouldSend(true),
_maxPendingMessages(DEFAULT_MAX_PENDING_MESSAGES),
_releaseQueuedMessagesPending(false),

View file

@ -28,8 +28,9 @@ public:
/// Utility for processing, packing, queueing and sending of outbound edit messages.
class OctreeEditPacketSender : public PacketSender {
Q_OBJECT
public:
OctreeEditPacketSender(PacketSenderNotify* notify = NULL);
OctreeEditPacketSender();
~OctreeEditPacketSender();
/// Queues a single edit message. Will potentially send a pending multi-command packet. Determines which server

View file

@ -6,6 +6,8 @@
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#include <QCoreApplication>
#include "OctreeScriptingInterface.h"
OctreeScriptingInterface::OctreeScriptingInterface(OctreeEditPacketSender* packetSender,
@ -16,18 +18,21 @@ OctreeScriptingInterface::OctreeScriptingInterface(OctreeEditPacketSender* packe
}
OctreeScriptingInterface::~OctreeScriptingInterface() {
//printf("OctreeScriptingInterface::~OctreeScriptingInterface()\n");
cleanupManagedObjects();
}
void OctreeScriptingInterface::cleanupManagedObjects() {
if (_managedJurisdictionListener) {
//printf("OctreeScriptingInterface::~OctreeScriptingInterface() _managedJurisdictionListener... _jurisdictionListener->terminate()\n");
_jurisdictionListener->terminate();
//printf("OctreeScriptingInterface::~OctreeScriptingInterface() _managedJurisdictionListener... deleting _jurisdictionListener\n");
delete _jurisdictionListener;
_jurisdictionListener->deleteLater();
_managedJurisdictionListener = false;
_jurisdictionListener = NULL;
}
if (_managedPacketSender) {
//printf("OctreeScriptingInterface::~OctreeScriptingInterface() _managedJurisdictionListener... _packetSender->terminate()\n");
_packetSender->terminate();
//printf("OctreeScriptingInterface::~OctreeScriptingInterface() _managedPacketSender... deleting _packetSender\n");
delete _packetSender;
_packetSender->deleteLater();
_managedPacketSender = false;
_packetSender = NULL;
}
}
@ -40,13 +45,11 @@ void OctreeScriptingInterface::setJurisdictionListener(JurisdictionListener* jur
}
void OctreeScriptingInterface::init() {
//printf("OctreeScriptingInterface::init()\n");
if (_jurisdictionListener) {
_managedJurisdictionListener = false;
} else {
_managedJurisdictionListener = true;
_jurisdictionListener = new JurisdictionListener(getServerNodeType());
//qDebug("OctreeScriptingInterface::init() _managedJurisdictionListener=true, creating _jurisdictionListener=%p", _jurisdictionListener);
_jurisdictionListener->initialize(true);
}
@ -55,7 +58,11 @@ void OctreeScriptingInterface::init() {
} else {
_managedPacketSender = true;
_packetSender = createPacketSender();
//qDebug("OctreeScriptingInterface::init() _managedPacketSender=true, creating _packetSender=%p", _packetSender);
_packetSender->setServerJurisdictions(_jurisdictionListener->getJurisdictions());
}
if (QCoreApplication::instance()) {
connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(cleanupManagedObjects()));
}
}

View file

@ -84,6 +84,9 @@ public slots:
/// returns the total bytes queued by this object over its lifetime
long long unsigned int getLifetimeBytesQueued() const { return _packetSender->getLifetimeBytesQueued(); }
// TODO: hmmm... we don't want this called from JS, how to handle that?
void cleanupManagedObjects();
protected:
/// attached OctreeEditPacketSender that handles queuing and sending of packets to VS
OctreeEditPacketSender* _packetSender;

View file

@ -682,7 +682,7 @@ void Particle::update(const uint64_t& now) {
const uint64_t REALLY_OLD = 30 * USECS_PER_SECOND; // 30 seconds
bool isReallyOld = ((now - _created) > REALLY_OLD);
bool isInHand = getInHand();
bool shouldDie = getShouldDie() || (!isInHand && isStopped && isReallyOld);
bool shouldDie = (getAge() > getLifetime()) || getShouldDie() || (!isInHand && isStopped && isReallyOld);
setShouldDie(shouldDie);
runUpdateScript(); // allow the javascript to alter our state

View file

@ -42,7 +42,7 @@ const uint16_t PACKET_CONTAINS_INHAND = 128;
const uint16_t PACKET_CONTAINS_SCRIPT = 256;
const uint16_t PACKET_CONTAINS_SHOULDDIE = 512;
const float DEFAULT_LIFETIME = 60.0f * 60.0f * 24.0f; // particles live for 1 day by default
const float DEFAULT_LIFETIME = 10.0f; // particles live for 10 seconds by default
const float DEFAULT_DAMPING = 0.99f;
const float DEFAULT_RADIUS = 0.1f / TREE_SCALE;
const float MINIMUM_PARTICLE_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container

View file

@ -79,8 +79,8 @@ void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) {
// let the particles run their collision scripts if they have them
particle->collisionWithVoxel(voxelDetails);
collisionInfo._penetration /= (float)(TREE_SCALE);
updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
collisionInfo._penetration /= (float)(TREE_SCALE);
applyHardCollision(particle, ELASTICITY, DAMPING, collisionInfo);
delete voxelDetails; // cleanup returned details
@ -134,7 +134,6 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA)
_packetSender->releaseQueuedMessages();
penetration /= (float)(TREE_SCALE);
updateCollisionSound(particleA, penetration, COLLISION_FREQUENCY);
}
}
@ -184,8 +183,8 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
}
// HACK END
collisionInfo._penetration /= (float)(TREE_SCALE);
updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
collisionInfo._penetration /= (float)(TREE_SCALE);
applyHardCollision(particle, elasticity, damping, collisionInfo);
}
}
@ -216,8 +215,8 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
}
// HACK END
collisionInfo._penetration /= (float)(TREE_SCALE);
updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
collisionInfo._penetration /= (float)(TREE_SCALE);
applyHardCollision(particle, ELASTICITY, damping, collisionInfo);
}
}
@ -276,7 +275,7 @@ void ParticleCollisionSystem::applyHardCollision(Particle* particle, float elast
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.1f;
const float AUDIBLE_COLLISION_THRESHOLD = 0.3f;
const float COLLISION_LOUDNESS = 1.f;
const float DURATION_SCALING = 0.004f;
const float NOISE_SCALING = 0.1f;
@ -293,18 +292,20 @@ void ParticleCollisionSystem::updateCollisionSound(Particle* particle, const glm
velocity -= _scale * glm::length(gravity) * GRAVITY_EARTH * deltaTime * glm::normalize(gravity);
}
*/
float velocityTowardCollision = glm::dot(velocity, glm::normalize(penetration));
float velocityTangentToCollision = glm::length(velocity) - velocityTowardCollision;
float normalSpeed = glm::dot(velocity, glm::normalize(penetration));
// NOTE: it is possible for normalSpeed to be NaN at this point
// (sometimes the average penetration of a bunch of voxels is a zero length vector which cannot be normalized)
// however the check below will fail (NaN comparisons always fail) and everything will be fine.
if (velocityTowardCollision > AUDIBLE_COLLISION_THRESHOLD) {
if (normalSpeed > AUDIBLE_COLLISION_THRESHOLD) {
// Volume is proportional to collision velocity
// Base frequency is modified upward by the angle of the collision
// Noise is a function of the angle of collision
// Duration of the sound is a function of both base frequency and velocity of impact
_audio->startCollisionSound(
std::min(COLLISION_LOUDNESS * velocityTowardCollision, 1.f),
frequency * (1.f + velocityTangentToCollision / velocityTowardCollision),
std::min(velocityTangentToCollision / velocityTowardCollision * NOISE_SCALING, 1.f),
1.f - DURATION_SCALING * powf(frequency, 0.5f) / velocityTowardCollision, false);
float tangentialSpeed = glm::length(velocity) - normalSpeed;
_audio->startCollisionSound( std::min(COLLISION_LOUDNESS * normalSpeed, 1.f),
frequency * (1.f + tangentialSpeed / normalSpeed),
std::min(tangentialSpeed / normalSpeed * NOISE_SCALING, 1.f),
1.f - DURATION_SCALING * powf(frequency, 0.5f) / normalSpeed, false);
}
}

View file

@ -16,10 +16,8 @@
/// Utility for processing, packing, queueing and sending of outbound edit voxel messages.
class ParticleEditPacketSender : public OctreeEditPacketSender {
Q_OBJECT
public:
ParticleEditPacketSender(PacketSenderNotify* notify = NULL) : OctreeEditPacketSender(notify) { }
~ParticleEditPacketSender() { }
/// Send particle add message immediately
/// NOTE: ParticleProperties assumes that all distances are in meter units
void sendEditParticleMessage(PACKET_TYPE type, ParticleID particleID, const ParticleProperties& properties);

View file

@ -390,17 +390,29 @@ bool ParticleTree::encodeParticlesDeletedSince(uint64_t& sinceTime, unsigned cha
// called by the server when it knows all nodes have been sent deleted packets
void ParticleTree::forgetParticlesDeletedBefore(uint64_t sinceTime) {
//qDebug() << "forgetParticlesDeletedBefore()";
QSet<uint64_t> keysToRemove;
_recentlyDeletedParticlesLock.lockForWrite();
QMultiMap<uint64_t, uint32_t>::const_iterator iterator = _recentlyDeletedParticleIDs.constBegin();
while (iterator != _recentlyDeletedParticleIDs.constEnd()) {
QMultiMap<uint64_t, uint32_t>::iterator iterator = _recentlyDeletedParticleIDs.begin();
// First find all the keys in the map that are older and need to be deleted
while (iterator != _recentlyDeletedParticleIDs.end()) {
//qDebug() << "considering... time/key:" << iterator.key();
if (iterator.key() <= sinceTime) {
//qDebug() << "YES older... time/key:" << iterator.key();
_recentlyDeletedParticleIDs.remove(iterator.key());
keysToRemove << iterator.key();
}
++iterator;
}
// Now run through the keysToRemove and remove them
foreach (uint64_t value, keysToRemove) {
//qDebug() << "removing the key, _recentlyDeletedParticleIDs.remove(value); time/key:" << value;
_recentlyDeletedParticleIDs.remove(value);
}
_recentlyDeletedParticlesLock.unlock();
//qDebug() << "DONE forgetParticlesDeletedBefore()";
}

View file

@ -250,7 +250,7 @@ void ScriptEngine::run() {
int numAvatarPacketBytes = _avatarData->getBroadcastData(avatarPacket + numAvatarHeaderBytes) + numAvatarHeaderBytes;
nodeList->broadcastToNodes(avatarPacket, numAvatarPacketBytes, &NODE_TYPE_AVATAR_MIXER, 1);
nodeList->broadcastToNodes(avatarPacket, numAvatarPacketBytes, QSet<NODE_TYPE>() << NODE_TYPE_AVATAR_MIXER);
}
if (willSendVisualDataCallBack) {

View file

@ -20,7 +20,10 @@ GenericThread::GenericThread() :
}
GenericThread::~GenericThread() {
terminate();
// we only need to call terminate() if we're actually threaded and still running
if (isStillRunning() && isThreaded()) {
terminate();
}
}
void GenericThread::initialize(bool isThreaded) {
@ -45,6 +48,7 @@ void GenericThread::terminate() {
if (_thread) {
_thread->wait();
_thread->deleteLater();
_thread = NULL;
}
}
}

View file

@ -74,8 +74,6 @@ const char* Node::getTypeName() const {
return NODE_TYPE_NAME_AUDIO_MIXER;
case NODE_TYPE_AVATAR_MIXER:
return NODE_TYPE_NAME_AVATAR_MIXER;
case NODE_TYPE_AUDIO_INJECTOR:
return NODE_TYPE_NAME_AUDIO_INJECTOR;
case NODE_TYPE_ANIMATION_SERVER:
return NODE_TYPE_NAME_ANIMATION_SERVER;
case NODE_TYPE_UNASSIGNED:

View file

@ -56,12 +56,12 @@ NodeList* NodeList::getInstance() {
NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) :
_nodeHash(),
_nodeHashMutex(),
_nodeHashMutex(QMutex::Recursive),
_domainHostname(DEFAULT_DOMAIN_HOSTNAME),
_domainSockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)),
_nodeSocket(),
_nodeSocket(this),
_ownerType(newOwnerType),
_nodeTypesOfInterest(NULL),
_nodeTypesOfInterest(),
_ownerUUID(QUuid::createUuid()),
_numNoReplyDomainCheckIns(0),
_assignmentServerSocket(),
@ -75,8 +75,6 @@ NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) :
NodeList::~NodeList() {
delete _nodeTypesOfInterest;
clear();
}
@ -235,8 +233,12 @@ int NodeList::updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr,
node->setLastHeardMicrostamp(usecTimestampNow());
if (!senderSockAddr.isNull()) {
activateSocketFromNodeCommunication(senderSockAddr);
if (!senderSockAddr.isNull() && !node->getActiveSocket()) {
if (senderSockAddr == node->getPublicSocket()) {
node->activatePublicSocket();
} else if (senderSockAddr == node->getLocalSocket()) {
node->activateLocalSocket();
}
}
if (node->getActiveSocket() || senderSockAddr.isNull()) {
@ -257,7 +259,7 @@ int NodeList::updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr,
SharedNodePointer NodeList::nodeWithAddress(const HifiSockAddr &senderSockAddr) {
// naively returns the first node that has a matching active HifiSockAddr
// note that there can be multiple nodes that have a matching active socket, so this isn't a good way to uniquely identify
foreach (const SharedNodePointer& node, _nodeHash) {
foreach (const SharedNodePointer& node, getNodeHash()) {
if (node->getActiveSocket() && *node->getActiveSocket() == senderSockAddr) {
return node;
}
@ -293,19 +295,18 @@ void NodeList::reset() {
clear();
_numNoReplyDomainCheckIns = 0;
delete _nodeTypesOfInterest;
_nodeTypesOfInterest = NULL;
_nodeTypesOfInterest.clear();
// refresh the owner UUID
_ownerUUID = QUuid::createUuid();
}
void NodeList::setNodeTypesOfInterest(const char* nodeTypesOfInterest, int numNodeTypesOfInterest) {
delete _nodeTypesOfInterest;
void NodeList::addNodeTypeToInterestSet(NODE_TYPE nodeTypeToAdd) {
_nodeTypesOfInterest << nodeTypeToAdd;
}
_nodeTypesOfInterest = new char[numNodeTypesOfInterest + sizeof(char)];
memcpy(_nodeTypesOfInterest, nodeTypesOfInterest, numNodeTypesOfInterest);
_nodeTypesOfInterest[numNodeTypesOfInterest] = '\0';
void NodeList::addSetOfNodeTypesToNodeInterestSet(const QSet<NODE_TYPE>& setOfNodeTypes) {
_nodeTypesOfInterest.unite(setOfNodeTypes);
}
const uint32_t RFC_5389_MAGIC_COOKIE = 0x2112A442;
@ -459,7 +460,7 @@ NodeHash::iterator NodeList::killNodeAtHashIterator(NodeHash::iterator& nodeItem
return _nodeHash.erase(nodeItemToKill);
}
void NodeList::sendKillNode(const char* nodeTypes, int numNodeTypes) {
void NodeList::sendKillNode(const QSet<NODE_TYPE>& destinationNodeTypes) {
unsigned char packet[MAX_PACKET_SIZE];
unsigned char* packetPosition = packet;
@ -469,7 +470,7 @@ void NodeList::sendKillNode(const char* nodeTypes, int numNodeTypes) {
memcpy(packetPosition, rfcUUID.constData(), rfcUUID.size());
packetPosition += rfcUUID.size();
broadcastToNodes(packet, packetPosition - packet, nodeTypes, numNodeTypes);
broadcastToNodes(packet, packetPosition - packet, destinationNodeTypes);
}
void NodeList::processKillNode(unsigned char* packetData, size_t dataBytes) {
@ -522,7 +523,7 @@ void NodeList::sendDomainServerCheckIn() {
sendSTUNRequest();
} else {
// construct the DS check in packet if we need to
int numBytesNodesOfInterest = _nodeTypesOfInterest ? strlen((char*) _nodeTypesOfInterest) : 0;
int numBytesNodesOfInterest = _nodeTypesOfInterest.size();
const int IP_ADDRESS_BYTES = 4;
@ -563,11 +564,8 @@ void NodeList::sendDomainServerCheckIn() {
*(packetPosition++) = numBytesNodesOfInterest;
// copy over the bytes for node types of interest, if required
if (numBytesNodesOfInterest > 0) {
memcpy(packetPosition,
_nodeTypesOfInterest,
numBytesNodesOfInterest);
packetPosition += numBytesNodesOfInterest;
foreach (NODE_TYPE nodeTypeOfInterest, _nodeTypesOfInterest) {
*(packetPosition++) = nodeTypeOfInterest;
}
_nodeSocket.writeDatagram((char*) checkInPacket, packetPosition - checkInPacket,
@ -735,12 +733,13 @@ SharedNodePointer NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType,
}
}
unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes) {
unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataBytes,
const QSet<NODE_TYPE>& destinationNodeTypes) {
unsigned n = 0;
foreach (const SharedNodePointer& node, _nodeHash) {
foreach (const SharedNodePointer& node, getNodeHash()) {
// only send to the NodeTypes we are asked to send to.
if (memchr(nodeTypes, node->getType(), numNodeTypes)) {
if (destinationNodeTypes.contains(node->getType())) {
if (getNodeActiveSocketOrPing(node.data())) {
// we know which socket is good for this node, send there
_nodeSocket.writeDatagram((char*) broadcastData, dataBytes,
@ -754,7 +753,7 @@ unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataByt
}
void NodeList::pingInactiveNodes() {
foreach (const SharedNodePointer& node, _nodeHash) {
foreach (const SharedNodePointer& node, getNodeHash()) {
if (!node->getActiveSocket()) {
// we don't have an active link to this node, ping it to set that up
pingPublicAndLocalSocketsForInactiveNode(node.data());
@ -791,7 +790,7 @@ void NodeList::activateSocketFromNodeCommunication(const HifiSockAddr& nodeAddre
SharedNodePointer NodeList::soloNodeOfType(char nodeType) {
if (memchr(SOLO_NODE_TYPES, nodeType, sizeof(SOLO_NODE_TYPES)) != NULL) {
foreach (const SharedNodePointer& node, _nodeHash) {
foreach (const SharedNodePointer& node, getNodeHash()) {
if (node->getType() == nodeType) {
return node;
}

View file

@ -22,6 +22,7 @@
#endif
#include <QtCore/QMutex>
#include <QtCore/QSet>
#include <QtCore/QSettings>
#include <QtCore/QSharedPointer>
#include <QtNetwork/QHostAddress>
@ -87,10 +88,11 @@ public:
int getNumNoReplyDomainCheckIns() const { return _numNoReplyDomainCheckIns; }
void clear();
void reset();
void setNodeTypesOfInterest(const char* nodeTypesOfInterest, int numNodeTypesOfInterest);
const QSet<NODE_TYPE>& getNodeInterestSet() const { return _nodeTypesOfInterest; }
void addNodeTypeToInterestSet(NODE_TYPE nodeTypeToAdd);
void addSetOfNodeTypesToNodeInterestSet(const QSet<NODE_TYPE>& setOfNodeTypes);
int processDomainServerList(unsigned char *packetData, size_t dataBytes);
@ -103,7 +105,7 @@ public:
int fillPingReplyPacket(unsigned char* pingBuffer, unsigned char* replyBuffer);
void pingPublicAndLocalSocketsForInactiveNode(Node* node);
void sendKillNode(const char* nodeTypes, int numNodeTypes);
void sendKillNode(const QSet<NODE_TYPE>& destinationNodeTypes);
SharedNodePointer nodeWithAddress(const HifiSockAddr& senderSockAddr);
SharedNodePointer nodeWithUUID(const QUuid& nodeUUID);
@ -115,7 +117,7 @@ public:
int updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr, unsigned char *packetData, int dataBytes);
unsigned broadcastToNodes(unsigned char *broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes);
unsigned broadcastToNodes(unsigned char *broadcastData, size_t dataBytes, const QSet<NODE_TYPE>& destinationNodeTypes);
SharedNodePointer soloNodeOfType(char nodeType);
void loadData(QSettings* settings);
@ -151,7 +153,7 @@ private:
HifiSockAddr _domainSockAddr;
QUdpSocket _nodeSocket;
char _ownerType;
char* _nodeTypesOfInterest;
QSet<NODE_TYPE> _nodeTypesOfInterest;
QUuid _ownerUUID;
int _numNoReplyDomainCheckIns;
HifiSockAddr _assignmentServerSocket;
@ -163,6 +165,7 @@ private:
void timePingReply(const HifiSockAddr& nodeAddress, unsigned char *packetData);
void resetDomainData(char domainField[], const char* domainData);
void domainLookup();
void clear();
};
#endif /* defined(__hifi__NodeList__) */

View file

@ -25,7 +25,6 @@ const NODE_TYPE NODE_TYPE_ENVIRONMENT_SERVER = 'E';
const NODE_TYPE NODE_TYPE_AGENT = 'I';
const NODE_TYPE NODE_TYPE_AUDIO_MIXER = 'M';
const NODE_TYPE NODE_TYPE_AVATAR_MIXER = 'W';
const NODE_TYPE NODE_TYPE_AUDIO_INJECTOR = 'A';
const NODE_TYPE NODE_TYPE_ANIMATION_SERVER = 'a';
const NODE_TYPE NODE_TYPE_UNASSIGNED = 1;

View file

@ -27,13 +27,12 @@ const int PacketSender::MINIMAL_SLEEP_INTERVAL = (USECS_PER_SECOND / TARGET_FPS)
const int AVERAGE_CALL_TIME_SAMPLES = 10;
PacketSender::PacketSender(PacketSenderNotify* notify, int packetsPerSecond) :
PacketSender::PacketSender(int packetsPerSecond) :
_packetsPerSecond(packetsPerSecond),
_usecsPerProcessCallHint(0),
_lastProcessCallTime(0),
_averageProcessCallTime(AVERAGE_CALL_TIME_SAMPLES),
_lastSendTime(0), // Note: we set this to 0 to indicate we haven't yet sent something
_notify(notify),
_lastPPSCheck(0),
_packetsOverCheckInterval(0),
_started(usecTimestampNow()),
@ -271,10 +270,9 @@ bool PacketSender::nonThreadedProcess() {
_packetsOverCheckInterval++;
_totalPacketsSent++;
_totalBytesSent += temporary.getLength();
if (_notify) {
_notify->packetSentNotification(temporary.getLength());
}
emit packetSent(temporary.getLength());
_lastSendTime = now;
}
return isStillRunning();

View file

@ -15,15 +15,9 @@
#include "NetworkPacket.h"
#include "SharedUtil.h"
/// Notification Hook for packets being sent by a PacketSender
class PacketSenderNotify {
public:
virtual void packetSentNotification(ssize_t length) = 0;
};
/// Generalized threaded processor for queueing and sending of outbound packets.
class PacketSender : public GenericThread {
Q_OBJECT
public:
static const uint64_t USECS_PER_SECOND;
@ -35,7 +29,7 @@ public:
static const int MINIMUM_PACKETS_PER_SECOND;
static const int MINIMAL_SLEEP_INTERVAL;
PacketSender(PacketSenderNotify* notify = NULL, int packetsPerSecond = DEFAULT_PACKETS_PER_SECOND);
PacketSender(int packetsPerSecond = DEFAULT_PACKETS_PER_SECOND);
~PacketSender();
/// Add packet to outbound queue.
@ -48,9 +42,6 @@ public:
void setPacketsPerSecond(int packetsPerSecond);
int getPacketsPerSecond() const { return _packetsPerSecond; }
void setPacketSenderNotify(PacketSenderNotify* notify) { _notify = notify; }
PacketSenderNotify* getPacketSenderNotify() const { return _notify; }
virtual bool process();
/// are there packets waiting in the send queue to be sent
@ -97,7 +88,8 @@ public:
/// returns the total bytes queued by this object over its lifetime
uint64_t getLifetimeBytesQueued() const { return _totalBytesQueued; }
signals:
void packetSent(quint64);
protected:
int _packetsPerSecond;
int _usecsPerProcessCallHint;
@ -107,7 +99,6 @@ protected:
private:
std::vector<NetworkPacket> _packets;
uint64_t _lastSendTime;
PacketSenderNotify* _notify;
bool threadedProcess();
bool nonThreadedProcess();

View file

@ -68,4 +68,6 @@ void VoxelServer::beforeRun() {
qDebug("Using Minimal Environment=%s", debug::valueOf(_sendMinimalEnvironment));
}
qDebug("Sending environments=%s", debug::valueOf(_sendEnvironments));
NodeList::getInstance()->addNodeTypeToInterestSet(NODE_TYPE_ANIMATION_SERVER);
}

View file

@ -15,10 +15,8 @@
/// Utility for processing, packing, queueing and sending of outbound edit voxel messages.
class VoxelEditPacketSender : public OctreeEditPacketSender {
Q_OBJECT
public:
VoxelEditPacketSender(PacketSenderNotify* notify = NULL) : OctreeEditPacketSender(notify) { }
~VoxelEditPacketSender() { }
/// Send voxel edit message immediately
void sendVoxelEditMessage(PACKET_TYPE type, VoxelDetail& detail);

View file

@ -19,7 +19,7 @@
/// handles scripting of voxel commands from JS passed to assigned clients
class VoxelsScriptingInterface : public OctreeScriptingInterface {
Q_OBJECT
public:
public:
VoxelEditPacketSender* getVoxelPacketSender() { return (VoxelEditPacketSender*)getPacketSender(); }
virtual NODE_TYPE getServerNodeType() const { return NODE_TYPE_VOXEL_SERVER; }