mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 07:57:30 +02:00
Merge branch 'master' of https://github.com/worklist/hifi
This commit is contained in:
commit
37b60a9366
18 changed files with 391 additions and 164 deletions
|
@ -33,3 +33,7 @@ find_package(STK REQUIRED)
|
|||
target_link_libraries(${TARGET_NAME} ${STK_LIBRARIES})
|
||||
include_directories(${STK_INCLUDE_DIRS})
|
||||
|
||||
# link curl for synchronous script downloads
|
||||
find_package(CURL REQUIRED)
|
||||
include_directories(${CURL_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${CURL_LIBRARY})
|
|
@ -6,8 +6,9 @@
|
|||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <QtScript/QScriptEngine>
|
||||
#include <QtNetwork/QtNetwork>
|
||||
|
||||
#include <AvatarData.h>
|
||||
#include <NodeList.h>
|
||||
|
@ -23,6 +24,22 @@ Agent::Agent(const unsigned char* dataBuffer, int numBytes) :
|
|||
|
||||
}
|
||||
|
||||
void Agent::stop() {
|
||||
_shouldStop = true;
|
||||
}
|
||||
|
||||
static size_t writeScriptDataToString(void *contents, size_t size, size_t nmemb, void *userdata) {
|
||||
size_t realSize = size * nmemb;
|
||||
|
||||
QString* scriptContents = (QString*) userdata;
|
||||
|
||||
// append this chunk to the scriptContents
|
||||
scriptContents->append(QByteArray((char*) contents, realSize));
|
||||
|
||||
// return the amount of data read
|
||||
return realSize;
|
||||
}
|
||||
|
||||
void Agent::run() {
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
nodeList->setOwnerType(NODE_TYPE_AGENT);
|
||||
|
@ -30,101 +47,125 @@ void Agent::run() {
|
|||
|
||||
nodeList->getNodeSocket()->setBlocking(false);
|
||||
|
||||
QNetworkAccessManager manager;
|
||||
|
||||
// figure out the URL for the script for this agent assignment
|
||||
QString scriptURLString("http://%1:8080/assignment/%2");
|
||||
scriptURLString = scriptURLString.arg(NodeList::getInstance()->getDomainIP().toString(),
|
||||
this->getUUIDStringWithoutCurlyBraces());
|
||||
QUrl scriptURL(scriptURLString);
|
||||
|
||||
qDebug() << "Attemping download of " << scriptURL << "\n";
|
||||
// setup curl for script download
|
||||
CURLcode curlResult;
|
||||
|
||||
QNetworkReply* reply = manager.get(QNetworkRequest(scriptURL));
|
||||
CURL* curlHandle = curl_easy_init();
|
||||
|
||||
QEventLoop loop;
|
||||
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||
loop.exec();
|
||||
// tell curl which file to grab
|
||||
curl_easy_setopt(curlHandle, CURLOPT_URL, scriptURLString.toStdString().c_str());
|
||||
|
||||
QString scriptString = QString(reply->readAll());
|
||||
// send the data to the WriteMemoryCallback function
|
||||
curl_easy_setopt(curlHandle, CURLOPT_WRITEFUNCTION, writeScriptDataToString);
|
||||
|
||||
QScriptEngine engine;
|
||||
QString scriptContents;
|
||||
|
||||
QScriptValue agentValue = engine.newQObject(this);
|
||||
engine.globalObject().setProperty("Agent", agentValue);
|
||||
// pass the scriptContents QString to append data to
|
||||
curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, (void *)&scriptContents);
|
||||
|
||||
VoxelScriptingInterface voxelScripter;
|
||||
QScriptValue voxelScripterValue = engine.newQObject(&voxelScripter);
|
||||
engine.globalObject().setProperty("Voxels", voxelScripterValue);
|
||||
// send a user agent since some servers will require it
|
||||
curl_easy_setopt(curlHandle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
|
||||
|
||||
QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE));
|
||||
engine.globalObject().setProperty("TREE_SCALE", treeScaleValue);
|
||||
qDebug() << "Downloading script at" << scriptURLString << "\n";
|
||||
|
||||
const long long VISUAL_DATA_SEND_INTERVAL_USECS = (1 / 60.0f) * 1000 * 1000;
|
||||
|
||||
QScriptValue visualSendIntervalValue = engine.newVariant((QVariant(VISUAL_DATA_SEND_INTERVAL_USECS / 1000)));
|
||||
engine.globalObject().setProperty("VISUAL_DATA_SEND_INTERVAL_MS", visualSendIntervalValue);
|
||||
|
||||
qDebug() << "Downloaded script:" << scriptString << "\n";
|
||||
QScriptValue result = engine.evaluate(scriptString);
|
||||
qDebug() << "Evaluated script.\n";
|
||||
|
||||
if (engine.hasUncaughtException()) {
|
||||
int line = engine.uncaughtExceptionLineNumber();
|
||||
qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n";
|
||||
}
|
||||
|
||||
timeval thisSend;
|
||||
timeval lastDomainServerCheckIn = {};
|
||||
int numMicrosecondsSleep = 0;
|
||||
|
||||
sockaddr_in senderAddress;
|
||||
unsigned char receivedData[MAX_PACKET_SIZE];
|
||||
ssize_t receivedBytes;
|
||||
|
||||
bool hasVoxelServer = false;
|
||||
|
||||
while (!_shouldStop) {
|
||||
// update the thisSend timeval to the current time
|
||||
gettimeofday(&thisSend, NULL);
|
||||
// blocking get for JS file
|
||||
curlResult = curl_easy_perform(curlHandle);
|
||||
|
||||
if (curlResult == CURLE_OK) {
|
||||
// cleanup curl
|
||||
curl_easy_cleanup(curlHandle);
|
||||
curl_global_cleanup();
|
||||
|
||||
// if we're not hearing from the domain-server we should stop running
|
||||
if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
||||
break;
|
||||
QScriptEngine engine;
|
||||
|
||||
QScriptValue agentValue = engine.newQObject(this);
|
||||
engine.globalObject().setProperty("Agent", agentValue);
|
||||
|
||||
VoxelScriptingInterface voxelScripter;
|
||||
QScriptValue voxelScripterValue = engine.newQObject(&voxelScripter);
|
||||
engine.globalObject().setProperty("Voxels", voxelScripterValue);
|
||||
|
||||
QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE));
|
||||
engine.globalObject().setProperty("TREE_SCALE", treeScaleValue);
|
||||
|
||||
const long long VISUAL_DATA_SEND_INTERVAL_USECS = (1 / 60.0f) * 1000 * 1000;
|
||||
|
||||
QScriptValue visualSendIntervalValue = engine.newVariant((QVariant(VISUAL_DATA_SEND_INTERVAL_USECS / 1000)));
|
||||
engine.globalObject().setProperty("VISUAL_DATA_SEND_INTERVAL_MS", visualSendIntervalValue);
|
||||
|
||||
qDebug() << "Downloaded script:" << scriptContents << "\n";
|
||||
QScriptValue result = engine.evaluate(scriptContents);
|
||||
qDebug() << "Evaluated script.\n";
|
||||
|
||||
if (engine.hasUncaughtException()) {
|
||||
int line = engine.uncaughtExceptionLineNumber();
|
||||
qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n";
|
||||
}
|
||||
|
||||
// send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed
|
||||
if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) {
|
||||
gettimeofday(&lastDomainServerCheckIn, NULL);
|
||||
NodeList::getInstance()->sendDomainServerCheckIn();
|
||||
}
|
||||
timeval thisSend;
|
||||
timeval lastDomainServerCheckIn = {};
|
||||
int numMicrosecondsSleep = 0;
|
||||
|
||||
if (!hasVoxelServer) {
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
if (node->getType() == NODE_TYPE_VOXEL_SERVER) {
|
||||
hasVoxelServer = true;
|
||||
sockaddr_in senderAddress;
|
||||
unsigned char receivedData[MAX_PACKET_SIZE];
|
||||
ssize_t receivedBytes;
|
||||
|
||||
bool hasVoxelServer = false;
|
||||
|
||||
while (!_shouldStop) {
|
||||
// update the thisSend timeval to the current time
|
||||
gettimeofday(&thisSend, NULL);
|
||||
|
||||
// if we're not hearing from the domain-server we should stop running
|
||||
if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
||||
break;
|
||||
}
|
||||
|
||||
// send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed
|
||||
if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) {
|
||||
gettimeofday(&lastDomainServerCheckIn, NULL);
|
||||
NodeList::getInstance()->sendDomainServerCheckIn();
|
||||
}
|
||||
|
||||
if (!hasVoxelServer) {
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
if (node->getType() == NODE_TYPE_VOXEL_SERVER) {
|
||||
hasVoxelServer = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// allow the scripter's call back to setup visual data
|
||||
emit willSendVisualDataCallback();
|
||||
|
||||
if (engine.hasUncaughtException()) {
|
||||
int line = engine.uncaughtExceptionLineNumber();
|
||||
qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n";
|
||||
} else {
|
||||
// allow the scripter's call back to setup visual data
|
||||
emit willSendVisualDataCallback();
|
||||
|
||||
if (engine.hasUncaughtException()) {
|
||||
int line = engine.uncaughtExceptionLineNumber();
|
||||
qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n";
|
||||
}
|
||||
|
||||
voxelScripter.getVoxelPacketSender()->processWithoutSleep();
|
||||
}
|
||||
|
||||
voxelScripter.getVoxelPacketSender()->processWithoutSleep();
|
||||
while (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes)) {
|
||||
NodeList::getInstance()->processNodeData((sockaddr*) &senderAddress, receivedData, receivedBytes);
|
||||
}
|
||||
|
||||
// sleep for the correct amount of time to have data send be consistently timed
|
||||
if ((numMicrosecondsSleep = VISUAL_DATA_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&thisSend))) > 0) {
|
||||
usleep(numMicrosecondsSleep);
|
||||
}
|
||||
}
|
||||
|
||||
while (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes)) {
|
||||
NodeList::getInstance()->processNodeData((sockaddr*) &senderAddress, receivedData, receivedBytes);
|
||||
}
|
||||
} else {
|
||||
// error in curl_easy_perform
|
||||
qDebug() << "curl_easy_perform for JS failed:" << curl_easy_strerror(curlResult) << "\n";
|
||||
|
||||
// sleep for the correct amount of time to have data send be consistently timed
|
||||
if ((numMicrosecondsSleep = VISUAL_DATA_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&thisSend))) > 0) {
|
||||
usleep(numMicrosecondsSleep);
|
||||
}
|
||||
// cleanup curl
|
||||
curl_easy_cleanup(curlHandle);
|
||||
curl_global_cleanup();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,11 +19,13 @@ class Agent : public Assignment {
|
|||
public:
|
||||
Agent(const unsigned char* dataBuffer, int numBytes);
|
||||
|
||||
bool volatile _shouldStop;
|
||||
|
||||
void run();
|
||||
public slots:
|
||||
void stop();
|
||||
signals:
|
||||
void willSendVisualDataCallback();
|
||||
private:
|
||||
bool volatile _shouldStop;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Operative__) */
|
||||
|
|
|
@ -36,8 +36,11 @@ sockaddr_in customAssignmentSocket = {};
|
|||
int numForks = 0;
|
||||
Assignment::Type overiddenAssignmentType = Assignment::AllTypes;
|
||||
|
||||
int argc = 0;
|
||||
char** argv = NULL;
|
||||
|
||||
void childClient() {
|
||||
// this is one of the child forks or there is a single assignment client, continue assignment-client execution
|
||||
QCoreApplication application(::argc, ::argv);
|
||||
|
||||
// set the logging target to the the CHILD_TARGET_NAME
|
||||
Logging::setTargetName(CHILD_TARGET_NAME);
|
||||
|
@ -171,9 +174,9 @@ void parentMonitor() {
|
|||
delete[] ::childForks;
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
|
||||
QCoreApplication app(argc, (char**) argv);
|
||||
int main(int argc, char* argv[]) {
|
||||
::argc = argc;
|
||||
::argv = argv;
|
||||
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
|
||||
|
@ -187,8 +190,8 @@ int main(int argc, const char* argv[]) {
|
|||
const char CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION[] = "-p";
|
||||
|
||||
// grab the overriden assignment-server hostname from argv, if it exists
|
||||
const char* customAssignmentServerHostname = getCmdOption(argc, argv, CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION);
|
||||
const char* customAssignmentServerPortString = getCmdOption(argc, argv, CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION);
|
||||
const char* customAssignmentServerHostname = getCmdOption(argc, (const char**)argv, CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION);
|
||||
const char* customAssignmentServerPortString = getCmdOption(argc,(const char**)argv, CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION);
|
||||
|
||||
if (customAssignmentServerHostname || customAssignmentServerPortString) {
|
||||
|
||||
|
@ -205,7 +208,7 @@ int main(int argc, const char* argv[]) {
|
|||
}
|
||||
|
||||
const char ASSIGNMENT_TYPE_OVVERIDE_OPTION[] = "-t";
|
||||
const char* assignmentTypeString = getCmdOption(argc, argv, ASSIGNMENT_TYPE_OVVERIDE_OPTION);
|
||||
const char* assignmentTypeString = getCmdOption(argc, (const char**)argv, ASSIGNMENT_TYPE_OVVERIDE_OPTION);
|
||||
|
||||
if (assignmentTypeString) {
|
||||
// the user is asking to only be assigned to a particular type of assignment
|
||||
|
@ -214,7 +217,7 @@ int main(int argc, const char* argv[]) {
|
|||
}
|
||||
|
||||
const char* NUM_FORKS_PARAMETER = "-n";
|
||||
const char* numForksString = getCmdOption(argc, argv, NUM_FORKS_PARAMETER);
|
||||
const char* numForksString = getCmdOption(argc, (const char**)argv, NUM_FORKS_PARAMETER);
|
||||
|
||||
int processID = 0;
|
||||
|
||||
|
|
|
@ -1541,6 +1541,9 @@ void Application::init() {
|
|||
_sharedVoxelSystemViewFrustum.setOrientation(glm::quat());
|
||||
_sharedVoxelSystemViewFrustum.calculate();
|
||||
_sharedVoxelSystem.setViewFrustum(&_sharedVoxelSystemViewFrustum);
|
||||
|
||||
VoxelNode::removeUpdateHook(&_sharedVoxelSystem);
|
||||
|
||||
_sharedVoxelSystem.init();
|
||||
VoxelTree* tmpTree = _sharedVoxelSystem.getTree();
|
||||
_sharedVoxelSystem.changeTree(&_clipboard);
|
||||
|
@ -1588,6 +1591,7 @@ void Application::init() {
|
|||
// Set up VoxelSystem after loading preferences so we can get the desired max voxel count
|
||||
_voxels.setMaxVoxels(Menu::getInstance()->getMaxVoxels());
|
||||
_voxels.setUseVoxelShader(Menu::getInstance()->isOptionChecked(MenuOption::UseVoxelShader));
|
||||
_voxels.setUseFastVoxelPipeline(Menu::getInstance()->isOptionChecked(MenuOption::FastVoxelPipeline));
|
||||
_voxels.init();
|
||||
|
||||
|
||||
|
|
|
@ -253,6 +253,9 @@ Menu::Menu() :
|
|||
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AmbientOcclusion);
|
||||
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::UseVoxelShader, 0,
|
||||
false, this, SLOT(switchVoxelShader()));
|
||||
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::FastVoxelPipeline, 0,
|
||||
false, appInstance->getVoxels(), SLOT(setUseFastVoxelPipeline(bool)));
|
||||
|
||||
|
||||
QMenu* avatarOptionsMenu = developerMenu->addMenu("Avatar Options");
|
||||
|
||||
|
|
|
@ -152,6 +152,7 @@ namespace MenuOption {
|
|||
const QString FalseColorOccludedV2 = "FALSE Color Occluded V2 Voxels";
|
||||
const QString FalseColorOutOfView = "FALSE Color Voxel Out of View";
|
||||
const QString FalseColorRandomly = "FALSE Color Voxels Randomly";
|
||||
const QString FastVoxelPipeline = "Fast Voxel Pipeline";
|
||||
const QString FirstPerson = "First Person";
|
||||
const QString FrameTimer = "Show Timer";
|
||||
const QString FrustumRenderMode = "Render Mode";
|
||||
|
|
|
@ -70,10 +70,12 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels)
|
|||
_writeRenderFullVBO = true;
|
||||
_readRenderFullVBO = true;
|
||||
_tree = new VoxelTree();
|
||||
_tree->rootNode->setVoxelSystem(this);
|
||||
pthread_mutex_init(&_bufferWriteLock, NULL);
|
||||
pthread_mutex_init(&_treeLock, NULL);
|
||||
|
||||
VoxelNode::addDeleteHook(this);
|
||||
VoxelNode::addUpdateHook(this);
|
||||
_abandonedVBOSlots = 0;
|
||||
_falseColorizeBySource = false;
|
||||
_dataSourceID = UNKNOWN_NODE_ID;
|
||||
|
@ -96,14 +98,70 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels)
|
|||
_writeVoxelDirtyArray = NULL;
|
||||
_readVoxelDirtyArray = NULL;
|
||||
|
||||
_inSetupNewVoxelsForDrawing = false;
|
||||
_useFastVoxelPipeline = false;
|
||||
}
|
||||
|
||||
void VoxelSystem::nodeDeleted(VoxelNode* node) {
|
||||
void VoxelSystem::voxelDeleted(VoxelNode* node) {
|
||||
if (node->isKnownBufferIndex() && (node->getVoxelSystem() == this)) {
|
||||
freeBufferIndex(node->getBufferIndex());
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelSystem::setUseFastVoxelPipeline(bool useFastVoxelPipeline) {
|
||||
_useFastVoxelPipeline = useFastVoxelPipeline;
|
||||
printf("setUseFastVoxelPipeline() _useFastVoxelPipeline=%s\n", debug::valueOf(_useFastVoxelPipeline));
|
||||
setupNewVoxelsForDrawing();
|
||||
}
|
||||
|
||||
void VoxelSystem::voxelUpdated(VoxelNode* node) {
|
||||
|
||||
//printf("VoxelSystem::voxelUpdated() _useFastVoxelPipeline=%s\n", debug::valueOf(_useFastVoxelPipeline));
|
||||
|
||||
// If we're in SetupNewVoxelsForDrawing() or _writeRenderFullVBO then bail..
|
||||
if (!_useFastVoxelPipeline || _inSetupNewVoxelsForDrawing || _writeRenderFullVBO) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->getVoxelSystem() == this) {
|
||||
//printf("VoxelSystem::voxelUpdated()... node->getVoxelSystem() == this\n");
|
||||
|
||||
bool shouldRender = false; // assume we don't need to render it
|
||||
// if it's colored, we might need to render it!
|
||||
shouldRender = node->calculateShouldRender(_viewFrustum);
|
||||
|
||||
node->setShouldRender(shouldRender);
|
||||
|
||||
if (!node->isLeaf()) {
|
||||
|
||||
// As we check our children, see if any of them went from shouldRender to NOT shouldRender
|
||||
// then we probably dropped LOD and if we don't have color, we want to average our children
|
||||
// for a new color.
|
||||
int childrenGotHiddenCount = 0;
|
||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
VoxelNode* childNode = node->getChildAtIndex(i);
|
||||
if (childNode) {
|
||||
bool wasShouldRender = childNode->getShouldRender();
|
||||
bool isShouldRender = childNode->calculateShouldRender(_viewFrustum);
|
||||
if (wasShouldRender && !isShouldRender) {
|
||||
childrenGotHiddenCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (childrenGotHiddenCount > 0) {
|
||||
node->setColorFromAverageOfChildren();
|
||||
}
|
||||
}
|
||||
|
||||
updateNodeInArraysAsPartialVBO(node);
|
||||
_voxelsUpdated++;
|
||||
|
||||
node->clearDirtyBit(); // clear the dirty bit, do this before we potentially delete things.
|
||||
|
||||
setupNewVoxelsForDrawingSingleNode();
|
||||
}
|
||||
}
|
||||
|
||||
// returns an available index, starts by reusing a previously freed index, but if there isn't one available
|
||||
// it will use the end of the VBO array and grow our accounting of that array.
|
||||
// and makes the index available for some other node to use
|
||||
|
@ -147,6 +205,7 @@ VoxelSystem::~VoxelSystem() {
|
|||
pthread_mutex_destroy(&_treeLock);
|
||||
|
||||
VoxelNode::removeDeleteHook(this);
|
||||
VoxelNode::removeUpdateHook(this);
|
||||
}
|
||||
|
||||
void VoxelSystem::setMaxVoxels(int maxVoxels) {
|
||||
|
@ -462,7 +521,10 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
|
|||
break;
|
||||
}
|
||||
|
||||
setupNewVoxelsForDrawing();
|
||||
|
||||
if (!_useFastVoxelPipeline || _writeRenderFullVBO) {
|
||||
setupNewVoxelsForDrawing();
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&_treeLock);
|
||||
|
||||
|
@ -485,25 +547,9 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
|
|||
return; // bail early, it hasn't been long enough since the last time we ran
|
||||
}
|
||||
|
||||
uint64_t sinceLastViewCulling = (start - _lastViewCulling) / 1000;
|
||||
// If the view frustum is no longer changing, but has changed, since last time, then remove nodes that are out of view
|
||||
if ((sinceLastViewCulling >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS))
|
||||
&& !isViewChanging()) {
|
||||
_lastViewCulling = start;
|
||||
_inSetupNewVoxelsForDrawing = true;
|
||||
|
||||
// When we call removeOutOfView() voxels, we don't actually remove the voxels from the VBOs, but we do remove
|
||||
// them from tree, this makes our tree caclulations faster, but doesn't require us to fully rebuild the VBOs (which
|
||||
// can be expensive).
|
||||
removeOutOfView();
|
||||
|
||||
// Once we call cleanupRemovedVoxels() we do need to rebuild our VBOs (if anything was actually removed). So,
|
||||
// we should consider putting this someplace else... as this might be able to occur less frequently, and save us on
|
||||
// VBO reubuilding. Possibly we should do this only if our actual VBO usage crosses some lower boundary.
|
||||
cleanupRemovedVoxels();
|
||||
|
||||
uint64_t endViewCulling = usecTimestampNow();
|
||||
_lastViewCullingElapsed = (endViewCulling - start) / 1000;
|
||||
}
|
||||
checkForCulling(); // check for out of view and deleted voxels...
|
||||
|
||||
bool didWriteFullVBO = _writeRenderFullVBO;
|
||||
if (_tree->isDirty()) {
|
||||
|
@ -547,12 +593,80 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
|
|||
int elapsedmsec = (end - start) / 1000;
|
||||
_setupNewVoxelsForDrawingLastFinished = end;
|
||||
_setupNewVoxelsForDrawingLastElapsed = elapsedmsec;
|
||||
_inSetupNewVoxelsForDrawing = false;
|
||||
}
|
||||
|
||||
void VoxelSystem::setupNewVoxelsForDrawingSingleNode() {
|
||||
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"setupNewVoxelsForDrawingSingleNode()"); // would like to include _voxelsInArrays, _voxelsUpdated
|
||||
|
||||
uint64_t start = usecTimestampNow();
|
||||
uint64_t sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000;
|
||||
|
||||
// clear up the VBOs for any nodes that have been recently deleted.
|
||||
clearFreeBufferIndexes();
|
||||
|
||||
bool iAmDebugging = false; // if you're debugging set this to true, so you won't get skipped for slow debugging
|
||||
if (!iAmDebugging && sinceLastTime <= std::max((float) _setupNewVoxelsForDrawingLastElapsed, SIXTY_FPS_IN_MILLISECONDS)) {
|
||||
return; // bail early, it hasn't been long enough since the last time we ran
|
||||
}
|
||||
|
||||
|
||||
checkForCulling(); // check for out of view and deleted voxels...
|
||||
|
||||
// lock on the buffer write lock so we can't modify the data when the GPU is reading it
|
||||
pthread_mutex_lock(&_bufferWriteLock);
|
||||
|
||||
_voxelsDirty = true; // if we got this far, then we can assume some voxels are dirty
|
||||
|
||||
// copy the newly written data to the arrays designated for reading, only does something if _voxelsDirty && _voxelsUpdated
|
||||
copyWrittenDataToReadArrays(_writeRenderFullVBO);
|
||||
|
||||
// after...
|
||||
_voxelsUpdated = 0;
|
||||
|
||||
pthread_mutex_unlock(&_bufferWriteLock);
|
||||
|
||||
uint64_t end = usecTimestampNow();
|
||||
int elapsedmsec = (end - start) / 1000;
|
||||
_setupNewVoxelsForDrawingLastFinished = end;
|
||||
_setupNewVoxelsForDrawingLastElapsed = elapsedmsec;
|
||||
}
|
||||
|
||||
void VoxelSystem::checkForCulling() {
|
||||
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "checkForCulling()");
|
||||
uint64_t start = usecTimestampNow();
|
||||
uint64_t sinceLastViewCulling = (start - _lastViewCulling) / 1000;
|
||||
// If the view frustum is no longer changing, but has changed, since last time, then remove nodes that are out of view
|
||||
if ((sinceLastViewCulling >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS))
|
||||
&& !isViewChanging()) {
|
||||
|
||||
_lastViewCulling = start;
|
||||
|
||||
// When we call removeOutOfView() voxels, we don't actually remove the voxels from the VBOs, but we do remove
|
||||
// them from tree, this makes our tree caclulations faster, but doesn't require us to fully rebuild the VBOs (which
|
||||
// can be expensive).
|
||||
removeOutOfView();
|
||||
|
||||
// Once we call cleanupRemovedVoxels() we do need to rebuild our VBOs (if anything was actually removed). So,
|
||||
// we should consider putting this someplace else... as this might be able to occur less frequently, and save us on
|
||||
// VBO reubuilding. Possibly we should do this only if our actual VBO usage crosses some lower boundary.
|
||||
cleanupRemovedVoxels();
|
||||
|
||||
uint64_t endViewCulling = usecTimestampNow();
|
||||
_lastViewCullingElapsed = (endViewCulling - start) / 1000;
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelSystem::cleanupRemovedVoxels() {
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "cleanupRemovedVoxels()");
|
||||
// This handles cleanup of voxels that were culled as part of our regular out of view culling operation
|
||||
if (!_removedVoxels.isEmpty()) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) {
|
||||
qDebug() << "cleanupRemovedVoxels().. _removedVoxels=" << _removedVoxels.count() << "\n";
|
||||
}
|
||||
while (!_removedVoxels.isEmpty()) {
|
||||
delete _removedVoxels.extract();
|
||||
}
|
||||
|
@ -560,8 +674,12 @@ void VoxelSystem::cleanupRemovedVoxels() {
|
|||
}
|
||||
// we also might have VBO slots that have been abandoned, if too many of our VBO slots
|
||||
// are abandonded we want to rerender our full VBOs
|
||||
const float TOO_MANY_ABANDONED_RATIO = 0.25f;
|
||||
const float TOO_MANY_ABANDONED_RATIO = 0.5f;
|
||||
if (!_writeRenderFullVBO && (_abandonedVBOSlots > (_voxelsInWriteArrays * TOO_MANY_ABANDONED_RATIO))) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) {
|
||||
qDebug() << "cleanupRemovedVoxels().. _abandonedVBOSlots ["
|
||||
<< _abandonedVBOSlots << "] > TOO_MANY_ABANDONED_RATIO \n";
|
||||
}
|
||||
_writeRenderFullVBO = true;
|
||||
}
|
||||
}
|
||||
|
@ -704,7 +822,6 @@ int VoxelSystem::updateNodeInArraysAsFullVBO(VoxelNode* node) {
|
|||
return 1; // rendered
|
||||
} else {
|
||||
node->setBufferIndex(GLBUFFER_INDEX_UNKNOWN);
|
||||
node->setVoxelSystem(NULL);
|
||||
}
|
||||
|
||||
return 0; // not-rendered
|
||||
|
@ -805,6 +922,9 @@ void VoxelSystem::init() {
|
|||
// VBO for the verticesArray
|
||||
initVoxelMemory();
|
||||
_initialized = true;
|
||||
|
||||
// our own _removedVoxels doesn't need to be notified of voxel deletes
|
||||
VoxelNode::removeDeleteHook(&_removedVoxels);
|
||||
}
|
||||
|
||||
void VoxelSystem::changeTree(VoxelTree* newTree) {
|
||||
|
@ -812,6 +932,7 @@ void VoxelSystem::changeTree(VoxelTree* newTree) {
|
|||
|
||||
_tree = newTree;
|
||||
_tree->setDirtyBit();
|
||||
_tree->rootNode->setVoxelSystem(this);
|
||||
|
||||
connect(_tree, SIGNAL(importSize(float,float,float)), SIGNAL(importSize(float,float,float)));
|
||||
connect(_tree, SIGNAL(importProgress(int)), SIGNAL(importProgress(int)));
|
||||
|
@ -1031,7 +1152,7 @@ int VoxelSystem::_nodeCount = 0;
|
|||
void VoxelSystem::killLocalVoxels() {
|
||||
_tree->eraseAllVoxels();
|
||||
_voxelsInWriteArrays = _voxelsInReadArrays = 0; // better way to do this??
|
||||
//setupNewVoxelsForDrawing();
|
||||
setupNewVoxelsForDrawing();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ struct VoxelShaderVBOData
|
|||
};
|
||||
|
||||
|
||||
class VoxelSystem : public NodeData, public VoxelNodeDeleteHook, public NodeListHook {
|
||||
class VoxelSystem : public NodeData, public VoxelNodeDeleteHook, public VoxelNodeUpdateHook, public NodeListHook {
|
||||
Q_OBJECT
|
||||
public:
|
||||
VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = DEFAULT_MAX_VOXELS_PER_SYSTEM);
|
||||
|
@ -109,7 +109,8 @@ public:
|
|||
CoverageMapV2 myCoverageMapV2;
|
||||
CoverageMap myCoverageMap;
|
||||
|
||||
virtual void nodeDeleted(VoxelNode* node);
|
||||
virtual void voxelDeleted(VoxelNode* node);
|
||||
virtual void voxelUpdated(VoxelNode* node);
|
||||
virtual void nodeAdded(Node* node);
|
||||
virtual void nodeKilled(Node* node);
|
||||
|
||||
|
@ -134,6 +135,8 @@ public slots:
|
|||
void clearAllNodesBufferIndex();
|
||||
|
||||
void cancelImport();
|
||||
|
||||
void setUseFastVoxelPipeline(bool useFastVoxelPipeline);
|
||||
|
||||
protected:
|
||||
float _treeScale;
|
||||
|
@ -141,6 +144,8 @@ protected:
|
|||
VoxelTree* _tree;
|
||||
|
||||
void setupNewVoxelsForDrawing();
|
||||
void setupNewVoxelsForDrawingSingleNode();
|
||||
void checkForCulling();
|
||||
|
||||
glm::vec3 computeVoxelVertex(const glm::vec3& startVertex, float voxelScale, int index) const;
|
||||
|
||||
|
@ -268,6 +273,9 @@ private:
|
|||
unsigned long _memoryUsageVBO;
|
||||
unsigned long _initialMemoryUsageGPU;
|
||||
bool _hasMemoryUsageGPU;
|
||||
|
||||
bool _inSetupNewVoxelsForDrawing;
|
||||
bool _useFastVoxelPipeline;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -496,7 +496,7 @@ void Avatar::render(bool lookingInMirror, bool renderAvatarBalls) {
|
|||
|
||||
if (!_chatMessage.empty()) {
|
||||
int width = 0;
|
||||
int lastWidth;
|
||||
int lastWidth = 0;
|
||||
for (string::iterator it = _chatMessage.begin(); it != _chatMessage.end(); it++) {
|
||||
width += (lastWidth = textRenderer()->computeWidth(*it));
|
||||
}
|
||||
|
@ -734,11 +734,7 @@ void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
|||
&& (b != BODY_BALL_RIGHT_SHOULDER)) {
|
||||
glColor3fv(DARK_SKIN_COLOR);
|
||||
|
||||
float r1 = _bodyBall[_bodyBall[b].parentBall].radius * 0.8;
|
||||
float r2 = _bodyBall[b].radius * 0.8;
|
||||
if (b == BODY_BALL_HEAD_BASE) {
|
||||
r1 *= 0.5f;
|
||||
}
|
||||
renderJointConnectingCone(_bodyBall[_bodyBall[b].parentBall].position, _bodyBall[b].position, r2, r2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,6 +83,8 @@ void AvatarVoxelSystem::init() {
|
|||
_boneIndicesLocation = _skinProgram.attributeLocation("boneIndices");
|
||||
_boneWeightsLocation = _skinProgram.attributeLocation("boneWeights");
|
||||
|
||||
VoxelNode::removeUpdateHook(this); // we don't want this
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -468,7 +468,7 @@ void MyAvatar::render(bool lookingInMirror, bool renderAvatarBalls) {
|
|||
|
||||
if (!_chatMessage.empty()) {
|
||||
int width = 0;
|
||||
int lastWidth;
|
||||
int lastWidth = 0;
|
||||
for (string::iterator it = _chatMessage.begin(); it != _chatMessage.end(); it++) {
|
||||
width += (lastWidth = textRenderer()->computeWidth(*it));
|
||||
}
|
||||
|
@ -621,16 +621,9 @@ void MyAvatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
|||
&& (b != BODY_BALL_RIGHT_SHOULDER)) {
|
||||
glColor3fv(DARK_SKIN_COLOR);
|
||||
|
||||
float r1 = _bodyBall[_bodyBall[b].parentBall].radius * 0.8;
|
||||
float r2 = _bodyBall[b].radius * 0.8;
|
||||
if (b == BODY_BALL_HEAD_BASE) {
|
||||
r1 *= 0.5f;
|
||||
}
|
||||
renderJointConnectingCone
|
||||
(
|
||||
_bodyBall[_bodyBall[b].parentBall].position,
|
||||
_bodyBall[b].position, r2, r2
|
||||
);
|
||||
|
||||
renderJointConnectingCone(_bodyBall[_bodyBall[b].parentBall].position, _bodyBall[b].position, r2, r2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,8 +54,8 @@ void VoxelNode::init(unsigned char * octalCode) {
|
|||
_isDirty = true;
|
||||
_shouldRender = false;
|
||||
_sourceID = UNKNOWN_NODE_ID;
|
||||
markWithChangedTime();
|
||||
calculateAABox();
|
||||
markWithChangedTime();
|
||||
}
|
||||
|
||||
VoxelNode::~VoxelNode() {
|
||||
|
@ -71,6 +71,11 @@ VoxelNode::~VoxelNode() {
|
|||
}
|
||||
}
|
||||
|
||||
void VoxelNode::markWithChangedTime() {
|
||||
_lastChanged = usecTimestampNow();
|
||||
notifyUpdateHooks(); // if the node has changed, notify our hooks
|
||||
}
|
||||
|
||||
// This method is called by VoxelTree when the subtree below this node
|
||||
// is known to have changed. It's intended to be used as a place to do
|
||||
// bookkeeping that a node may need to do when the subtree below it has
|
||||
|
@ -78,14 +83,13 @@ VoxelNode::~VoxelNode() {
|
|||
// localized, because this method will get called for every node in an
|
||||
// recursive unwinding case like delete or add voxel
|
||||
void VoxelNode::handleSubtreeChanged(VoxelTree* myTree) {
|
||||
markWithChangedTime();
|
||||
|
||||
// here's a good place to do color re-averaging...
|
||||
if (myTree->getShouldReaverage()) {
|
||||
setColorFromAverageOfChildren();
|
||||
}
|
||||
|
||||
recalculateSubTreeNodeCount();
|
||||
markWithChangedTime();
|
||||
}
|
||||
|
||||
void VoxelNode::recalculateSubTreeNodeCount() {
|
||||
|
@ -134,8 +138,8 @@ void VoxelNode::deleteChildAtIndex(int childIndex) {
|
|||
delete _children[childIndex];
|
||||
_children[childIndex] = NULL;
|
||||
_isDirty = true;
|
||||
markWithChangedTime();
|
||||
_childCount--;
|
||||
markWithChangedTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,8 +149,8 @@ VoxelNode* VoxelNode::removeChildAtIndex(int childIndex) {
|
|||
if (_children[childIndex]) {
|
||||
_children[childIndex] = NULL;
|
||||
_isDirty = true;
|
||||
markWithChangedTime();
|
||||
_childCount--;
|
||||
markWithChangedTime();
|
||||
}
|
||||
return returnedChild;
|
||||
}
|
||||
|
@ -154,9 +158,10 @@ VoxelNode* VoxelNode::removeChildAtIndex(int childIndex) {
|
|||
VoxelNode* VoxelNode::addChildAtIndex(int childIndex) {
|
||||
if (!_children[childIndex]) {
|
||||
_children[childIndex] = new VoxelNode(childOctalCode(_octalCode, childIndex));
|
||||
_children[childIndex]->setVoxelSystem(_voxelSystem); // our child is always part of our voxel system NULL ok
|
||||
_isDirty = true;
|
||||
markWithChangedTime();
|
||||
_childCount++;
|
||||
markWithChangedTime();
|
||||
}
|
||||
return _children[childIndex];
|
||||
}
|
||||
|
@ -242,9 +247,8 @@ void VoxelNode::setFalseColored(bool isFalseColored) {
|
|||
}
|
||||
_falseColored = isFalseColored;
|
||||
_isDirty = true;
|
||||
markWithChangedTime();
|
||||
_density = 1.0f; // If color set, assume leaf, re-averaging will update density if needed.
|
||||
|
||||
markWithChangedTime();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -256,8 +260,8 @@ void VoxelNode::setColor(const nodeColor& color) {
|
|||
memcpy(&_currentColor,&color,sizeof(nodeColor));
|
||||
}
|
||||
_isDirty = true;
|
||||
markWithChangedTime();
|
||||
_density = 1.0f; // If color set, assume leaf, re-averaging will update density if needed.
|
||||
markWithChangedTime();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -403,23 +407,44 @@ float VoxelNode::distanceToPoint(const glm::vec3& point) const {
|
|||
return distance;
|
||||
}
|
||||
|
||||
std::vector<VoxelNodeDeleteHook*> VoxelNode::_hooks;
|
||||
std::vector<VoxelNodeDeleteHook*> VoxelNode::_deleteHooks;
|
||||
|
||||
void VoxelNode::addDeleteHook(VoxelNodeDeleteHook* hook) {
|
||||
_hooks.push_back(hook);
|
||||
_deleteHooks.push_back(hook);
|
||||
}
|
||||
|
||||
void VoxelNode::removeDeleteHook(VoxelNodeDeleteHook* hook) {
|
||||
for (int i = 0; i < _hooks.size(); i++) {
|
||||
if (_hooks[i] == hook) {
|
||||
_hooks.erase(_hooks.begin() + i);
|
||||
for (int i = 0; i < _deleteHooks.size(); i++) {
|
||||
if (_deleteHooks[i] == hook) {
|
||||
_deleteHooks.erase(_deleteHooks.begin() + i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelNode::notifyDeleteHooks() {
|
||||
for (int i = 0; i < _hooks.size(); i++) {
|
||||
_hooks[i]->nodeDeleted(this);
|
||||
for (int i = 0; i < _deleteHooks.size(); i++) {
|
||||
_deleteHooks[i]->voxelDeleted(this);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<VoxelNodeUpdateHook*> VoxelNode::_updateHooks;
|
||||
|
||||
void VoxelNode::addUpdateHook(VoxelNodeUpdateHook* hook) {
|
||||
_updateHooks.push_back(hook);
|
||||
}
|
||||
|
||||
void VoxelNode::removeUpdateHook(VoxelNodeUpdateHook* hook) {
|
||||
for (int i = 0; i < _updateHooks.size(); i++) {
|
||||
if (_updateHooks[i] == hook) {
|
||||
_updateHooks.erase(_updateHooks.begin() + i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelNode::notifyUpdateHooks() {
|
||||
for (int i = 0; i < _updateHooks.size(); i++) {
|
||||
_updateHooks[i]->voxelUpdated(this);
|
||||
}
|
||||
}
|
|
@ -25,9 +25,16 @@ typedef unsigned char rgbColor[3];
|
|||
// Callers who want delete hook callbacks should implement this class
|
||||
class VoxelNodeDeleteHook {
|
||||
public:
|
||||
virtual void nodeDeleted(VoxelNode* node) = 0;
|
||||
virtual void voxelDeleted(VoxelNode* node) = 0;
|
||||
};
|
||||
|
||||
// Callers who want update hook callbacks should implement this class
|
||||
class VoxelNodeUpdateHook {
|
||||
public:
|
||||
virtual void voxelUpdated(VoxelNode* node) = 0;
|
||||
};
|
||||
|
||||
|
||||
class VoxelNode {
|
||||
public:
|
||||
VoxelNode(); // root node constructor
|
||||
|
@ -72,7 +79,7 @@ public:
|
|||
void clearDirtyBit() { _isDirty = false; }
|
||||
void setDirtyBit() { _isDirty = true; }
|
||||
bool hasChangedSince(uint64_t time) const { return (_lastChanged > time); }
|
||||
void markWithChangedTime() { _lastChanged = usecTimestampNow(); }
|
||||
void markWithChangedTime();
|
||||
uint64_t getLastChanged() const { return _lastChanged; }
|
||||
void handleSubtreeChanged(VoxelTree* myTree);
|
||||
|
||||
|
@ -111,6 +118,9 @@ public:
|
|||
|
||||
static void addDeleteHook(VoxelNodeDeleteHook* hook);
|
||||
static void removeDeleteHook(VoxelNodeDeleteHook* hook);
|
||||
|
||||
static void addUpdateHook(VoxelNodeUpdateHook* hook);
|
||||
static void removeUpdateHook(VoxelNodeUpdateHook* hook);
|
||||
|
||||
void recalculateSubTreeNodeCount();
|
||||
unsigned long getSubTreeNodeCount() const { return _subtreeNodeCount; }
|
||||
|
@ -121,6 +131,7 @@ private:
|
|||
void calculateAABox();
|
||||
void init(unsigned char * octalCode);
|
||||
void notifyDeleteHooks();
|
||||
void notifyUpdateHooks();
|
||||
|
||||
nodeColor _trueColor;
|
||||
#ifndef NO_FALSE_COLOR // !NO_FALSE_COLOR means, does have false color
|
||||
|
@ -141,7 +152,8 @@ private:
|
|||
float _density; // If leaf: density = 1, if internal node: 0-1 density of voxels inside
|
||||
uint16_t _sourceID;
|
||||
|
||||
static std::vector<VoxelNodeDeleteHook*> _hooks;
|
||||
static std::vector<VoxelNodeDeleteHook*> _deleteHooks;
|
||||
static std::vector<VoxelNodeUpdateHook*> _updateHooks;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__VoxelNode__) */
|
||||
|
|
|
@ -127,7 +127,7 @@ void VoxelNodeBag::remove(VoxelNode* node) {
|
|||
}
|
||||
|
||||
|
||||
void VoxelNodeBag::nodeDeleted(VoxelNode* node) {
|
||||
void VoxelNodeBag::voxelDeleted(VoxelNode* node) {
|
||||
remove(node); // note: remove can safely handle nodes that aren't in it, so we don't need to check contains()
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ public:
|
|||
|
||||
static void voxelNodeDeleteHook(VoxelNode* node, void* extraData);
|
||||
|
||||
virtual void nodeDeleted(VoxelNode* node);
|
||||
virtual void voxelDeleted(VoxelNode* node);
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -214,10 +214,15 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData,
|
|||
memcpy(newColor, nodeData + bytesRead, 3);
|
||||
bytesRead += 3;
|
||||
}
|
||||
bool nodeWasDirty = destinationNode->getChildAtIndex(i)->isDirty();
|
||||
destinationNode->getChildAtIndex(i)->setColor(newColor);
|
||||
destinationNode->getChildAtIndex(i)->setSourceID(args.sourceID);
|
||||
bool nodeIsDirty = destinationNode->getChildAtIndex(i)->isDirty();
|
||||
VoxelNode* childNodeAt = destinationNode->getChildAtIndex(i);
|
||||
bool nodeWasDirty = false;
|
||||
bool nodeIsDirty = false;
|
||||
if (childNodeAt) {
|
||||
nodeWasDirty = childNodeAt->isDirty();
|
||||
childNodeAt->setColor(newColor);
|
||||
childNodeAt->setSourceID(args.sourceID);
|
||||
nodeIsDirty = childNodeAt->isDirty();
|
||||
}
|
||||
if (nodeIsDirty) {
|
||||
_isDirty = true;
|
||||
}
|
||||
|
@ -316,7 +321,9 @@ void VoxelTree::readBitstreamToTree(unsigned char * bitstream, unsigned long int
|
|||
bitstreamAt += theseBytesRead;
|
||||
bytesRead += theseBytesRead;
|
||||
|
||||
emit importProgress((100 * (bitstreamAt - bitstream)) / bufferSizeBytes);
|
||||
if (args.wantImportProgress) {
|
||||
emit importProgress((100 * (bitstreamAt - bitstream)) / bufferSizeBytes);
|
||||
}
|
||||
}
|
||||
|
||||
this->voxelsBytesRead += bufferSizeBytes;
|
||||
|
@ -1539,7 +1546,8 @@ bool VoxelTree::readFromSVOFile(const char* fileName) {
|
|||
// read the entire file into a buffer, WHAT!? Why not.
|
||||
unsigned char* entireFile = new unsigned char[fileLength];
|
||||
file.read((char*)entireFile, fileLength);
|
||||
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS);
|
||||
bool wantImportProgress = true;
|
||||
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, UNKNOWN_NODE_ID, wantImportProgress);
|
||||
readBitstreamToTree(entireFile, fileLength, args);
|
||||
delete[] entireFile;
|
||||
|
||||
|
@ -1798,7 +1806,8 @@ void VoxelTree::copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destin
|
|||
bytesWritten = sourceTree->encodeTreeBitstream(subTree, &outputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, nodeBag, params);
|
||||
|
||||
// ask destination tree to read the bitstream
|
||||
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, destinationNode);
|
||||
bool wantImportProgress = true;
|
||||
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, destinationNode, UNKNOWN_NODE_ID, wantImportProgress);
|
||||
readBitstreamToTree(&outputBuffer[0], bytesWritten, args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,20 +98,23 @@ public:
|
|||
|
||||
class ReadBitstreamToTreeParams {
|
||||
public:
|
||||
bool includeColor;
|
||||
bool includeExistsBits;
|
||||
VoxelNode* destinationNode;
|
||||
uint16_t sourceID;
|
||||
bool includeColor;
|
||||
bool includeExistsBits;
|
||||
VoxelNode* destinationNode;
|
||||
uint16_t sourceID;
|
||||
bool wantImportProgress;
|
||||
|
||||
ReadBitstreamToTreeParams(
|
||||
bool includeColor = WANT_COLOR,
|
||||
bool includeExistsBits = WANT_EXISTS_BITS,
|
||||
VoxelNode* destinationNode = NULL,
|
||||
uint16_t sourceID = UNKNOWN_NODE_ID) :
|
||||
includeColor (includeColor),
|
||||
includeExistsBits (includeExistsBits),
|
||||
destinationNode (destinationNode),
|
||||
sourceID (sourceID)
|
||||
bool includeColor = WANT_COLOR,
|
||||
bool includeExistsBits = WANT_EXISTS_BITS,
|
||||
VoxelNode* destinationNode = NULL,
|
||||
uint16_t sourceID = UNKNOWN_NODE_ID,
|
||||
bool wantImportProgress = false) :
|
||||
includeColor(includeColor),
|
||||
includeExistsBits(includeExistsBits),
|
||||
destinationNode(destinationNode),
|
||||
sourceID(sourceID),
|
||||
wantImportProgress(wantImportProgress)
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue