diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 5b778b0552..1a63d2c4d5 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -143,6 +143,8 @@ void Agent::run() { int thisFrame = 0; + NodeList::getInstance()->startSilentNodeRemovalThread(); + while (!_shouldStop) { // if we're not hearing from the domain-server we should stop running @@ -159,26 +161,25 @@ void Agent::run() { // find the audio-mixer in the NodeList so we can inject audio at it Node* audioMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AUDIO_MIXER); + if (audioMixer && audioMixer->getActiveSocket()) { emit willSendAudioDataCallback(); + } + + int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * INJECT_INTERVAL_USECS) - usecTimestampNow(); + if (usecToSleep > 0) { + usleep(usecToSleep); + } + + if (audioMixer && audioMixer->getActiveSocket() && scriptedAudioInjector.hasSamplesToInject()) { + // we have an audio mixer and samples to inject, send those off + scriptedAudioInjector.injectAudio(NodeList::getInstance()->getNodeSocket(), audioMixer->getActiveSocket()); - if (scriptedAudioInjector.hasSamplesToInject()) { - int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * INJECT_INTERVAL_USECS) - usecTimestampNow(); - if (usecToSleep > 0) { - usleep(usecToSleep); - } - - scriptedAudioInjector.injectAudio(NodeList::getInstance()->getNodeSocket(), audioMixer->getActiveSocket()); - - // clear out the audio injector so that it doesn't re-send what we just sent - scriptedAudioInjector.clear(); - } - } else if (audioMixer) { - int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * INJECT_INTERVAL_USECS) - usecTimestampNow(); - if (usecToSleep > 0) { - usleep(usecToSleep); - } - + // clear out the audio injector so that it doesn't re-send what we just sent + scriptedAudioInjector.clear(); + } + + if (audioMixer && !audioMixer->getActiveSocket()) { // don't have an active socket for the audio-mixer, ping it now NodeList::getInstance()->pingPublicAndLocalSocketsForInactiveNode(audioMixer); } @@ -202,9 +203,17 @@ void Agent::run() { while (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes) && packetVersionMatch(receivedData)) { - NodeList::getInstance()->processNodeData((sockaddr*) &senderAddress, receivedData, receivedBytes); + if (receivedData[0] == PACKET_TYPE_VOXEL_JURISDICTION) { + voxelScripter.getJurisdictionListener()->queueReceivedPacket((sockaddr&) senderAddress, + receivedData, + receivedBytes); + } else { + NodeList::getInstance()->processNodeData((sockaddr*) &senderAddress, receivedData, receivedBytes); + } } } + + NodeList::getInstance()->stopSilentNodeRemovalThread(); } else { // error in curl_easy_perform diff --git a/assignment-client/src/voxels/VoxelScriptingInterface.cpp b/assignment-client/src/voxels/VoxelScriptingInterface.cpp index e94f87264a..5b1a296aa7 100644 --- a/assignment-client/src/voxels/VoxelScriptingInterface.cpp +++ b/assignment-client/src/voxels/VoxelScriptingInterface.cpp @@ -8,6 +8,11 @@ #include "VoxelScriptingInterface.h" +VoxelScriptingInterface::VoxelScriptingInterface() { + _jurisdictionListener.initialize(true); + _voxelPacketSender.setVoxelServerJurisdictions(_jurisdictionListener.getJurisdictions()); +} + void VoxelScriptingInterface::queueVoxelAdd(PACKET_TYPE addPacketType, VoxelDetail& addVoxelDetails) { _voxelPacketSender.queueVoxelEditMessages(addPacketType, 1, &addVoxelDetails); } @@ -35,4 +40,4 @@ void VoxelScriptingInterface::queueVoxelDelete(float x, float y, float z, float VoxelDetail deleteVoxelDetail = {x, y, z, scale, 0, 0, 0}; _voxelPacketSender.queueVoxelEditMessages(PACKET_TYPE_ERASE_VOXEL, 1, &deleteVoxelDetail); -} +} \ No newline at end of file diff --git a/assignment-client/src/voxels/VoxelScriptingInterface.h b/assignment-client/src/voxels/VoxelScriptingInterface.h index ac1de6928d..a2c18b8018 100644 --- a/assignment-client/src/voxels/VoxelScriptingInterface.h +++ b/assignment-client/src/voxels/VoxelScriptingInterface.h @@ -11,13 +11,17 @@ #include +#include #include /// handles scripting of voxel commands from JS passed to assigned clients class VoxelScriptingInterface : public QObject { Q_OBJECT public: + VoxelScriptingInterface(); + VoxelEditPacketSender* getVoxelPacketSender() { return &_voxelPacketSender; } + JurisdictionListener* getJurisdictionListener() { return &_jurisdictionListener; } public slots: /// queues the creation of a voxel which will be sent by calling process on the PacketSender /// \param x the x-coordinate of the voxel (in VS space) @@ -48,6 +52,7 @@ public slots: private: /// attached VoxelEditPacketSender that handles queuing and sending of packets to VS VoxelEditPacketSender _voxelPacketSender; + JurisdictionListener _jurisdictionListener; void queueVoxelAdd(PACKET_TYPE addPacketType, VoxelDetail& addVoxelDetails); }; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 8f02c30b4c..d91d020a22 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -724,7 +724,7 @@ int DomainServer::run() { Assignment* assignmentToDeploy = deployableAssignmentForRequest(requestAssignment); if (assignmentToDeploy) { - + // give this assignment out, either the type matches or the requestor said they will take any int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_CREATE_ASSIGNMENT); int numAssignmentBytes = assignmentToDeploy->packToBuffer(broadcastPacket + numHeaderBytes); diff --git a/interface/resources/shaders/skin_model.vert b/interface/resources/shaders/skin_model.vert index 950283107f..2d1b98c4d7 100644 --- a/interface/resources/shaders/skin_model.vert +++ b/interface/resources/shaders/skin_model.vert @@ -32,7 +32,7 @@ void main(void) { normal = normalize(gl_ModelViewMatrix * normal); // pass along the vertex color - gl_FrontColor = gl_Color; + gl_FrontColor = vec4(1.0, 1.0, 1.0, 1.0); // and the texture coordinates gl_TexCoord[0] = gl_MultiTexCoord0; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9b66b80c4c..952f6fff8c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -194,7 +194,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : // call Menu getInstance static method to set up the menu _window->setMenuBar(Menu::getInstance()); - + // Check to see if the user passed in a command line option for loading a local // Voxel File. _voxelsFilename = getCmdOption(argc, constArgv, "-i"); @@ -435,9 +435,8 @@ void Application::paintGL() { _glowEffect.render(); if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { - glm::vec3 targetPosition = _myAvatar.getUprightHeadPosition(); - _mirrorCamera.setDistance(0.2f); - _mirrorCamera.setTargetPosition(targetPosition); + _mirrorCamera.setDistance(0.3f); + _mirrorCamera.setTargetPosition(_myAvatar.getHead().calculateAverageEyePosition()); _mirrorCamera.setTargetRotation(_myAvatar.getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f))); _mirrorCamera.update(1.0f/_fps); @@ -534,6 +533,7 @@ void Application::resetProfile(const QString& username) { // call the destructor on the old profile and construct a new one (&_profile)->~Profile(); new (&_profile) Profile(username); + updateWindowTitle(); } void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, @@ -1674,7 +1674,7 @@ void Application::init() { _mirrorCamera.setMode(CAMERA_MODE_MIRROR); _mirrorCamera.setAspectRatio((float)MIRROR_VIEW_WIDTH / (float)MIRROR_VIEW_HEIGHT); - _mirrorCamera.setFieldOfView(70); + _mirrorCamera.setFieldOfView(30); _mirrorViewRect = QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT); switchToResourcesParentIfRequired(); @@ -3791,13 +3791,25 @@ void Application::attachNewHeadToNode(Node* newNode) { } } +void Application::updateWindowTitle(){ + QString title = ""; + QString username = _profile.getUsername(); + if(!username.isEmpty()){ + title += _profile.getUsername(); + title += " @ "; + } + title += _profile.getLastDomain(); + + qDebug("Application title set to: %s.\n", title.toStdString().c_str()); + _window->setWindowTitle(title); +} + void Application::domainChanged(QString domain) { - qDebug("Application title set to: %s.\n", domain.toStdString().c_str()); - _window->setWindowTitle(domain); - // update the user's last domain in their Profile (which will propagate to data-server) _profile.updateDomain(domain); + updateWindowTitle(); + // reset the environment so that we don't erroneously end up with multiple _environment.resetToDefault(); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 6118bfb8b3..fb258f0353 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -109,6 +109,8 @@ public: void touchEndEvent(QTouchEvent* event); void touchUpdateEvent(QTouchEvent* event); + void updateWindowTitle(); + void wheelEvent(QWheelEvent* event); const glm::vec3 getMouseVoxelWorldCoordinates(const VoxelDetail _mouseVoxel); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 1775c250f1..a31ff61ae8 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -532,6 +532,7 @@ void Menu::loadSettings(QSettings* settings) { Application::getInstance()->getAvatar()->loadData(settings); Application::getInstance()->getSwatch()->loadData(settings); Application::getInstance()->getProfile()->loadData(settings); + Application::getInstance()->updateWindowTitle(); NodeList::getInstance()->loadData(settings); } diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index dcc7b82f9d..fa3de1a52e 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -62,5 +62,8 @@ void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJ glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getOrientation() * IDENTITY_FRONT, 0.0f)); glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(_owningHead->getLookAtPosition() + _owningHead->getSaccade(), 1.0f)); - state.rotation = rotationBetween(front, lookAt) * joint.rotation; + glm::quat between = rotationBetween(front, lookAt); + const float MAX_ANGLE = 22.5f; + state.rotation = glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) * + joint.rotation; } diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index fa5da82686..ab308faea9 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -415,6 +415,16 @@ glm::vec3 parseVec3(const QString& string) { return value; } +QString processID(const QString& id) { + // Blender (at least) prepends a type to the ID, so strip it out + int index = id.indexOf("::"); + return (index == -1) ? id : id.mid(index + 2); +} + +QString getID(const QVariantList& properties, int index = 0) { + return processID(properties.at(index).toString()); +} + const char* FACESHIFT_BLENDSHAPES[] = { "EyeBlink_L", "EyeBlink_R", @@ -469,7 +479,7 @@ const char* FACESHIFT_BLENDSHAPES[] = { class FBXModel { public: - QByteArray name; + QString name; int parentIndex; @@ -680,12 +690,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) QHash bumpTextures; QVariantHash joints = mapping.value("joint").toHash(); - QByteArray jointEyeLeftName = joints.value("jointEyeLeft", "jointEyeLeft").toByteArray(); - QByteArray jointEyeRightName = joints.value("jointEyeRight", "jointEyeRight").toByteArray(); - QByteArray jointNeckName = joints.value("jointNeck", "jointNeck").toByteArray(); - QByteArray jointRootName = joints.value("jointRoot", "jointRoot").toByteArray(); - QByteArray jointLeanName = joints.value("jointLean", "jointLean").toByteArray(); - QByteArray jointHeadName = joints.value("jointHead", "jointHead").toByteArray(); + QString jointEyeLeftName = processID(joints.value("jointEyeLeft", "jointEyeLeft").toString()); + QString jointEyeRightName = processID(joints.value("jointEyeRight", "jointEyeRight").toString()); + QString jointNeckName = processID(joints.value("jointNeck", "jointNeck").toString()); + QString jointRootName = processID(joints.value("jointRoot", "jointRoot").toString()); + QString jointLeanName = processID(joints.value("jointLean", "jointLean").toString()); + QString jointHeadName = processID(joints.value("jointHead", "jointHead").toString()); QString jointEyeLeftID; QString jointEyeRightID; QString jointNeckID; @@ -718,10 +728,10 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) foreach (const FBXNode& object, child.children) { if (object.name == "Geometry") { if (object.properties.at(2) == "Mesh") { - meshes.insert(object.properties.at(0).toString(), extractMesh(object)); + meshes.insert(getID(object.properties), extractMesh(object)); } else { // object.properties.at(2) == "Shape" - ExtractedBlendshape extracted = { object.properties.at(0).toString() }; + ExtractedBlendshape extracted = { getID(object.properties) }; foreach (const FBXNode& data, object.children) { if (data.name == "Indexes") { @@ -740,31 +750,31 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) blendshapes.append(extracted); } } else if (object.name == "Model") { - QByteArray name; + QString name; if (object.properties.size() == 3) { - name = object.properties.at(1).toByteArray(); - name = name.left(name.indexOf('\0')); + name = object.properties.at(1).toString(); + name = name.left(name.indexOf(QChar('\0'))); } else { - name = object.properties.at(0).toByteArray(); + name = getID(object.properties); } if (name == jointEyeLeftName || name == "EyeL" || name == "joint_Leye") { - jointEyeLeftID = object.properties.at(0).toString(); + jointEyeLeftID = getID(object.properties); } else if (name == jointEyeRightName || name == "EyeR" || name == "joint_Reye") { - jointEyeRightID = object.properties.at(0).toString(); + jointEyeRightID = getID(object.properties); } else if (name == jointNeckName || name == "NeckRot" || name == "joint_neck") { - jointNeckID = object.properties.at(0).toString(); + jointNeckID = getID(object.properties); } else if (name == jointRootName) { - jointRootID = object.properties.at(0).toString(); + jointRootID = getID(object.properties); } else if (name == jointLeanName) { - jointLeanID = object.properties.at(0).toString(); + jointLeanID = getID(object.properties); } else if (name == jointHeadName) { - jointHeadID = object.properties.at(0).toString(); + jointHeadID = getID(object.properties); } glm::vec3 translation; glm::vec3 rotationOffset; @@ -817,7 +827,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } } else if (subobject.name == "Vertices") { // it's a mesh as well as a model - meshes.insert(object.properties.at(0).toString(), extractMesh(object)); + meshes.insert(getID(object.properties), extractMesh(object)); } } // see FBX documentation, http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/index.html @@ -828,7 +838,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) model.postRotation = glm::quat(glm::radians(postRotation)); model.postTransform = glm::translate(-rotationPivot) * glm::translate(scalePivot) * glm::scale(scale) * glm::translate(-scalePivot); - models.insert(object.properties.at(0).toString(), model); + models.insert(getID(object.properties), model); } else if (object.name == "Texture") { foreach (const FBXNode& subobject, object.children) { @@ -836,7 +846,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) // trim off any path information QByteArray filename = subobject.properties.at(0).toByteArray(); filename = filename.mid(qMax(filename.lastIndexOf('\\'), filename.lastIndexOf('/')) + 1); - textureFilenames.insert(object.properties.at(0).toString(), filename); + textureFilenames.insert(getID(object.properties), filename); } } } else if (object.name == "Material") { @@ -871,7 +881,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } } } - materials.insert(object.properties.at(0).toString(), material); + materials.insert(getID(object.properties), material); } else if (object.name == "Deformer") { if (object.properties.last() == "Cluster") { @@ -888,7 +898,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) cluster.transformLink = createMat4(values); } } - clusters.insert(object.properties.at(0).toString(), cluster); + clusters.insert(getID(object.properties), cluster); } else if (object.properties.last() == "BlendShapeChannel") { QByteArray name = object.properties.at(1).toByteArray(); @@ -897,8 +907,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) // try everything after the dot name = name.mid(name.lastIndexOf('.') + 1); } - blendshapeChannelIndices.insert(object.properties.at(0).toString(), - blendshapeIndices.value(name)); + blendshapeChannelIndices.insert(getID(object.properties), blendshapeIndices.value(name)); } } } @@ -908,16 +917,14 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) if (connection.properties.at(0) == "OP") { QByteArray type = connection.properties.at(3).toByteArray().toLower(); if (type.contains("diffuse")) { - diffuseTextures.insert(connection.properties.at(2).toString(), - connection.properties.at(1).toString()); + diffuseTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); } else if (type.contains("bump")) { - bumpTextures.insert(connection.properties.at(2).toString(), - connection.properties.at(1).toString()); + bumpTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); } } - parentMap.insert(connection.properties.at(1).toString(), connection.properties.at(2).toString()); - childMap.insert(connection.properties.at(2).toString(), connection.properties.at(1).toString()); + parentMap.insert(getID(connection.properties, 1), getID(connection.properties, 2)); + childMap.insert(getID(connection.properties, 2), getID(connection.properties, 1)); } } } @@ -1159,12 +1166,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) QVariantHash attachments = mapping.value("attach").toHash(); for (QVariantHash::const_iterator it = attachments.constBegin(); it != attachments.constEnd(); it++) { FBXAttachment attachment; - attachment.jointIndex = modelIDs.indexOf(it.key()); + attachment.jointIndex = modelIDs.indexOf(processID(it.key())); attachment.scale = glm::vec3(1.0f, 1.0f, 1.0f); QVariantList properties = it->toList(); if (properties.isEmpty()) { - attachment.url = it->toUrl(); + attachment.url = it->toString(); } else { attachment.url = properties.at(0).toString(); diff --git a/libraries/shared/src/Assignment.cpp b/libraries/shared/src/Assignment.cpp index 223488e4c5..07e96e4963 100644 --- a/libraries/shared/src/Assignment.cpp +++ b/libraries/shared/src/Assignment.cpp @@ -90,6 +90,7 @@ Assignment::Assignment(const unsigned char* dataBuffer, int numBytes) : if (dataBuffer[numBytesRead] != '\0') { // read the pool from the data buffer setPool((const char*) dataBuffer + numBytesRead); + numBytesRead += strlen(_pool) + sizeof('\0'); } else { // skip past the null pool and null out our pool setPool(NULL); @@ -192,7 +193,7 @@ int Assignment::packToBuffer(unsigned char* buffer) { numPackedBytes += NUM_BYTES_RFC4122_UUID; } - if (_pool) { + if (hasPool()) { // pack the pool for this assignment, it exists int numBytesNullTerminatedPool = strlen(_pool) + sizeof('\0'); memcpy(buffer + numPackedBytes, _pool, numBytesNullTerminatedPool); diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index fb0b5176cf..fcd55116ad 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -492,10 +492,10 @@ void NodeList::sendDomainServerCheckIn() { // check in packet has header, optional UUID, node type, port, IP, node types of interest, null termination int numPacketBytes = sizeof(PACKET_TYPE) + sizeof(PACKET_VERSION) + sizeof(NODE_TYPE) + - NUM_BYTES_RFC4122_UUID + (2 * (sizeof(uint16_t) + IP_ADDRESS_BYTES)) + - numBytesNodesOfInterest + sizeof(unsigned char); + NUM_BYTES_RFC4122_UUID + (2 * (sizeof(uint16_t) + IP_ADDRESS_BYTES)) + + numBytesNodesOfInterest + sizeof(unsigned char); - unsigned char* checkInPacket = new unsigned char[numPacketBytes]; + unsigned char checkInPacket[numPacketBytes]; unsigned char* packetPosition = checkInPacket; PACKET_TYPE nodePacketType = (memchr(SOLO_NODE_TYPES, _ownerType, sizeof(SOLO_NODE_TYPES))) @@ -533,8 +533,6 @@ void NodeList::sendDomainServerCheckIn() { _nodeSocket.send(_domainIP.toString().toLocal8Bit().constData(), _domainPort, checkInPacket, packetPosition - checkInPacket); - - delete[] checkInPacket; // clean up const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5; static unsigned int numDomainCheckins = 0; diff --git a/libraries/shared/src/PacketSender.cpp b/libraries/shared/src/PacketSender.cpp index 5f6de128ba..0b41a34caf 100644 --- a/libraries/shared/src/PacketSender.cpp +++ b/libraries/shared/src/PacketSender.cpp @@ -74,6 +74,10 @@ bool PacketSender::process() { // we can determine how many packets we need to send per call to achieve our desired // packets per second send rate. int callsPerSecond = USECS_PER_SECOND / averageCallTime; + + // make sure our number of calls per second doesn't cause a divide by zero + callsPerSecond = glm::clamp(callsPerSecond, 1, _packetsPerSecond); + packetsPerCall = ceil(_packetsPerSecond / callsPerSecond); // send at least one packet per call, if we have it diff --git a/libraries/voxels/src/VoxelNode.cpp b/libraries/voxels/src/VoxelNode.cpp index 7245d2aa56..e83ffc1c16 100644 --- a/libraries/voxels/src/VoxelNode.cpp +++ b/libraries/voxels/src/VoxelNode.cpp @@ -351,6 +351,7 @@ VoxelNode* VoxelNode::getChildAtIndex(int childIndex) const { int indexTwo = getNthBit(_childBitmask, 2); if (_childrenExternal) { + //assert(_children.external); if (indexOne == childIndex) { result = _children.external[0]; } else if (indexTwo == childIndex) { @@ -375,6 +376,7 @@ VoxelNode* VoxelNode::getChildAtIndex(int childIndex) const { int indexThree = getNthBit(_childBitmask, 3); if (_childrenExternal) { + //assert(_children.external); if (indexOne == childIndex) { result = _children.external[0]; } else if (indexTwo == childIndex) { @@ -407,7 +409,12 @@ VoxelNode* VoxelNode::getChildAtIndex(int childIndex) const { for (int ordinal = 1; ordinal <= childCount; ordinal++) { int index = getNthBit(_childBitmask, ordinal); if (index == childIndex) { - result = _children.external[ordinal-1]; + int externalIndex = ordinal-1; + if (externalIndex < childCount && externalIndex >= 0) { + result = _children.external[externalIndex]; + } else { + qDebug("getChildAtIndex() attempt to access external client out of bounds externalIndex=%d <<<<<<<<<< WARNING!!! \n",externalIndex); + } break; } } @@ -430,9 +437,11 @@ void VoxelNode::storeTwoChildren(VoxelNode* childOne, VoxelNode* childTwo) { const int64_t minOffset = std::numeric_limits::min(); const int64_t maxOffset = std::numeric_limits::max(); - if (isBetween(offsetOne, maxOffset, minOffset) && isBetween(offsetTwo, maxOffset, minOffset)) { + bool forceExternal = true; + if (!forceExternal && isBetween(offsetOne, maxOffset, minOffset) && isBetween(offsetTwo, maxOffset, minOffset)) { // if previously external, then clean it up... if (_childrenExternal) { + //assert(_children.external); const int previousChildCount = 2; _externalChildrenMemoryUsage -= previousChildCount * sizeof(VoxelNode*); delete[] _children.external; @@ -539,7 +548,9 @@ void VoxelNode::storeThreeChildren(VoxelNode* childOne, VoxelNode* childTwo, Vox const int64_t minOffset = -1048576; // what can fit in 20 bits // std::numeric_limits::min(); const int64_t maxOffset = 1048576; // what can fit in 20 bits // std::numeric_limits::max(); - if (isBetween(offsetOne, maxOffset, minOffset) && + bool forceExternal = true; + if (!forceExternal && + isBetween(offsetOne, maxOffset, minOffset) && isBetween(offsetTwo, maxOffset, minOffset) && isBetween(offsetThree, maxOffset, minOffset)) { // if previously external, then clean it up... @@ -601,7 +612,9 @@ void VoxelNode::checkStoreFourChildren(VoxelNode* childOne, VoxelNode* childTwo, const int64_t minOffset = std::numeric_limits::min(); const int64_t maxOffset = std::numeric_limits::max(); - if (isBetween(offsetOne, maxOffset, minOffset) && + bool forceExternal = true; + if (!forceExternal && + isBetween(offsetOne, maxOffset, minOffset) && isBetween(offsetTwo, maxOffset, minOffset) && isBetween(offsetThree, maxOffset, minOffset) && isBetween(offsetFour, maxOffset, minOffset) @@ -720,7 +733,7 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) { storeTwoChildren(childOne, childTwo); } else if (previousChildCount == 2 && newChildCount == 1) { // If we had 2 children, and we're removing one, then we know we can go down to single mode - assert(child == NULL); // this is the only logical case + //assert(child == NULL); // this is the only logical case int indexTwo = getNthBit(previousChildMask, 2); bool keepChildOne = indexTwo == childIndex; @@ -743,31 +756,19 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) { int indexOne = getNthBit(previousChildMask, 1); bool replaceChildOne = indexOne == childIndex; - // If we previously had an external array, then just replace the right one... that's easy. - if (_childrenExternal) { - // technically, we could look to see if these are now in the offsets to handle be encoded, but - // we're going to go ahead and keep this as an array. - if (replaceChildOne) { - _children.external[0] = child; - } else { - _children.external[1] = child; - } - } else { - // If we were previously encoded as offsets, then we need to see if we can still encode as offsets - VoxelNode* childOne; - VoxelNode* childTwo; - - if (replaceChildOne) { - childOne = child; - childTwo = (VoxelNode*)((uint8_t*)this + _children.offsetsTwoChildren[1]); - } else { - childOne = (VoxelNode*)((uint8_t*)this + _children.offsetsTwoChildren[0]); - childTwo = child; - } + // Get the existing two children out of their encoding... + VoxelNode* childOne; + VoxelNode* childTwo; + retrieveTwoChildren(childOne, childTwo); - _twoChildrenOffsetCount--; // will end up one or the other - storeTwoChildren(childOne, childTwo); + if (replaceChildOne) { + childOne = child; + } else { + childTwo = child; } + + storeTwoChildren(childOne, childTwo); + } else if (previousChildCount == 2 && newChildCount == 3) { // If we had 2 children, and now have 3, then we know we are going to an external case... @@ -893,7 +894,7 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) { _externalChildrenCount++; } else if (previousChildCount == 4 && newChildCount == 3) { // If we had 4 children, and now have 3, then we know we are going from an external case to a potential internal case - assert(_childrenExternal); + //assert(_children.external && _childrenExternal && previousChildCount == 4); // We need to determine which children we had, and which one we got rid of... int indexOne = getNthBit(previousChildMask, 1); @@ -928,10 +929,11 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) { _children.external = NULL; _externalChildrenCount--; _externalChildrenMemoryUsage -= previousChildCount * sizeof(VoxelNode*); - - storeThreeChildren(childOne, childTwo, childThree); } else if (previousChildCount == newChildCount) { + //assert(_children.external && _childrenExternal && previousChildCount >= 4); + //assert(previousChildCount == newChildCount); + // 4 or more children, one item being replaced, we know we're stored externally, we just need to find the one // that needs to be replaced and replace it. for (int ordinal = 1; ordinal <= 8; ordinal++) { @@ -944,6 +946,10 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) { } } } else if (previousChildCount < newChildCount) { + // Growing case... previous must be 4 or greater + //assert(_children.external && _childrenExternal && previousChildCount >= 4); + //assert(previousChildCount == newChildCount-1); + // 4 or more children, one item being added, we know we're stored externally, we just figure out where to insert // this child pointer into our external list VoxelNode** newExternalList = new VoxelNode*[newChildCount]; @@ -975,13 +981,16 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) { _externalChildrenMemoryUsage += newChildCount * sizeof(VoxelNode*); } else if (previousChildCount > newChildCount) { + //assert(_children.external && _childrenExternal && previousChildCount >= 4); + //assert(previousChildCount == newChildCount+1); + // 4 or more children, one item being removed, we know we're stored externally, we just figure out which // item to remove from our external list VoxelNode** newExternalList = new VoxelNode*[newChildCount]; for (int ordinal = 1; ordinal <= previousChildCount; ordinal++) { int index = getNthBit(previousChildMask, ordinal); - assert(index != -1); + //assert(index != -1); if (index < childIndex) { newExternalList[ordinal - 1] = _children.external[ordinal - 1]; } else { @@ -997,7 +1006,7 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) { _externalChildrenMemoryUsage -= previousChildCount * sizeof(VoxelNode*); _externalChildrenMemoryUsage += newChildCount * sizeof(VoxelNode*); } else { - assert(false); + //assert(false); qDebug("THIS SHOULD NOT HAPPEN previousChildCount == %d && newChildCount == %d\n",previousChildCount, newChildCount); } @@ -1024,7 +1033,6 @@ VoxelNode* VoxelNode::addChildAtIndex(int childIndex) { childAt = new VoxelNode(childOctalCode(getOctalCode(), childIndex)); childAt->setVoxelSystem(getVoxelSystem()); // our child is always part of our voxel system NULL ok - setChildAtIndex(childIndex, childAt); _isDirty = true;