mirror of
https://github.com/overte-org/overte.git
synced 2025-04-25 00:56:48 +02:00
Merge branch 'master' of https://github.com/worklist/hifi
This commit is contained in:
commit
bb46435c38
55 changed files with 1355 additions and 576 deletions
|
@ -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()));
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
15
interface/resources/shaders/grid.frag
Normal file
15
interface/resources/shaders/grid.frag
Normal 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));
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -415,7 +415,7 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) {
|
|||
_totalPacketsReceived++;
|
||||
|
||||
double timeDiff = diffclock(&_lastReceiveTime, ¤tReceiveTime);
|
||||
|
||||
|
||||
// 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());
|
||||
|
|
110
interface/src/DatagramProcessor.cpp
Normal file
110
interface/src/DatagramProcessor.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
31
interface/src/DatagramProcessor.h
Normal file
31
interface/src/DatagramProcessor.h
Normal 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__) */
|
|
@ -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());
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() <<
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
406
interface/src/ui/MetavoxelEditor.cpp
Normal file
406
interface/src/ui/MetavoxelEditor.cpp
Normal 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;
|
67
interface/src/ui/MetavoxelEditor.h
Normal file
67
interface/src/ui/MetavoxelEditor.h
Normal 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__) */
|
|
@ -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() {
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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__) */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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__) */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
Loading…
Reference in a new issue