Merge pull request #4406 from ZappoMan/svoFromURL

Support for importing from a network URL
This commit is contained in:
Seth Alves 2015-03-10 15:41:41 -07:00
commit cf20596d6d
6 changed files with 190 additions and 123 deletions

View file

@ -699,7 +699,7 @@ function setupModelMenus() {
Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" });
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Entities", shortcutKey: "CTRL+META+E", afterItem: "Models" });
Menu.addMenuItem({ menuName: "File", menuItemName: "Import Entities", shortcutKey: "CTRL+META+I", afterItem: "Export Entities" });
Menu.addMenuItem({ menuName: "File", menuItemName: "Import Entities from URL", shortcutKey: "CTRL+META+U", afterItem: "Import Entities" });
Menu.addMenuItem({ menuName: "View", menuItemName: MENU_AUTO_FOCUS_ON_SELECT, afterItem: MENU_INSPECT_TOOL_ENABLED,
isCheckable: true, isChecked: Settings.getValue(SETTING_AUTO_FOCUS_ON_SELECT) == "true" });
@ -726,6 +726,7 @@ function cleanupModelMenus() {
Menu.removeSeparator("File", "Models");
Menu.removeMenuItem("File", "Export Entities");
Menu.removeMenuItem("File", "Import Entities");
Menu.removeMenuItem("File", "Import Entities from URL");
Menu.removeMenuItem("View", MENU_INSPECT_TOOL_ENABLED);
Menu.removeMenuItem("View", MENU_AUTO_FOCUS_ON_SELECT);
@ -795,10 +796,17 @@ function handeMenuEvent(menuItem) {
}
}
}
} else if (menuItem == "Import Entities") {
var filename = Window.browse("Select models to import", "", "*.svo")
if (filename) {
var success = Clipboard.importEntities(filename);
} else if (menuItem == "Import Entities" || menuItem == "Import Entities from URL") {
var importURL;
if (menuItem == "Import Entities") {
importURL = Window.browse("Select models to import", "", "*.svo");
} else {
importURL = Window.prompt("URL of SVO to import", "");
}
if (importURL) {
var success = Clipboard.importEntities(importURL);
if (success) {
var distance = cameraManager.enabled ? cameraManager.zoomDistance : DEFAULT_ENTITY_DRAG_DROP_DISTANCE;

View file

@ -1724,9 +1724,17 @@ void Application::saveSettings() {
_myAvatar->saveData();
}
bool Application::importEntities(const QString& filename) {
bool Application::importEntities(const QString& urlOrFilename) {
_entityClipboard.eraseAllOctreeElements();
bool success = _entityClipboard.readFromSVOFile(filename.toLocal8Bit().constData());
QUrl url(urlOrFilename);
// if the URL appears to be invalid or relative, then it is probably a local file
if (!url.isValid() || url.isRelative()) {
url = QUrl::fromLocalFile(urlOrFilename);
}
bool success = _entityClipboard.readFromSVOURL(url.toString());
if (success) {
_entityClipboard.reaverageOctreeElements();
}

View file

@ -325,7 +325,7 @@ public slots:
QVector<EntityItemID> pasteEntities(float x, float y, float z);
bool exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs);
bool exportEntities(const QString& filename, float x, float y, float z, float scale);
bool importEntities(const QString& filename);
bool importEntities(const QString& url);
void setLowVelocityFilter(bool lowVelocityFilter);
void loadDialog();

View file

@ -338,9 +338,6 @@ void ModelUploader::populateBasicMapping(QVariantHash& mapping, QString filename
geometry.blendshapeChannelNames.contains("Blink_Right") &&
geometry.blendshapeChannelNames.contains("Squint_Right"));
qDebug() << "likelyMixamoFile:" << likelyMixamoFile;
qDebug() << "geometry.blendshapeChannelNames:" << geometry.blendshapeChannelNames;
if (!mapping.contains(BLENDSHAPE_FIELD) && likelyMixamoFile) {
QVariantHash blendshapes;
blendshapes.insertMulti("BrowsD_L", QVariantList() << "BrowsDown_Left" << 1.0);

View file

@ -18,12 +18,20 @@
#include <cmath>
#include <fstream> // to load voxels from file
#include <QDataStream>
#include <QDebug>
#include <QEventLoop>
#include <QFile>
#include <QFileInfo>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QVector>
#include <GeometryUtil.h>
#include <OctalCode.h>
#include <LogHandler.h>
#include <NetworkAccessManager.h>
#include <OctalCode.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <Shape.h>
@ -1836,141 +1844,184 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element,
bool Octree::readFromSVOFile(const char* fileName) {
bool fileOk = false;
PacketVersion gotVersion = 0;
std::ifstream file(fileName, std::ios::in|std::ios::binary|std::ios::ate);
QFile file(fileName);
fileOk = file.open(QIODevice::ReadOnly);
if(fileOk) {
QDataStream fileInputStream(&file);
QFileInfo fileInfo(fileName);
unsigned long fileLength = fileInfo.size();
if(file.is_open()) {
emit importSize(1.0f, 1.0f, 1.0f);
emit importProgress(0);
qDebug("Loading file %s...", fileName);
fileOk = readFromStream(fileLength, fileInputStream);
// get file length....
unsigned long fileLength = file.tellg();
file.seekg( 0, std::ios::beg );
emit importProgress(100);
file.close();
}
return fileOk;
}
bool Octree::readFromSVOURL(const QString& urlString) {
bool readOk = false;
// determine if this is a local file or a network resource
QUrl url(urlString);
if (url.isLocalFile()) {
readOk = readFromSVOFile(qPrintable(url.toLocalFile()));
} else {
QNetworkRequest request;
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
request.setUrl(url);
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkReply* reply = networkAccessManager.get(request);
qDebug() << "Downloading svo at" << qPrintable(urlString);
QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
if (reply->error() == QNetworkReply::NoError) {
int resourceSize = reply->bytesAvailable();
QDataStream inputStream(reply);
readOk = readFromStream(resourceSize, inputStream);
}
}
return readOk;
}
bool Octree::readFromStream(unsigned long streamLength, QDataStream& inputStream) {
bool fileOk = false;
PacketVersion gotVersion = 0;
unsigned long headerLength = 0; // bytes in the header
bool wantImportProgress = true;
PacketType expectedType = expectedDataPacketType();
PacketVersion expectedVersion = versionForPacketType(expectedType);
bool hasBufferBreaks = versionHasSVOfileBreaks(expectedVersion);
// before reading the file, check to see if this version of the Octree supports file versions
if (getWantSVOfileVersions()) {
// read just enough of the file to parse the header...
const unsigned long HEADER_LENGTH = sizeof(PacketType) + sizeof(PacketVersion);
unsigned char fileHeader[HEADER_LENGTH];
inputStream.readRawData((char*)&fileHeader, HEADER_LENGTH);
unsigned long headerLength = 0; // bytes in the header
headerLength = HEADER_LENGTH; // we need this later to skip to the data
unsigned char* dataAt = (unsigned char*)&fileHeader;
unsigned long dataLength = HEADER_LENGTH;
// if so, read the first byte of the file and see if it matches the expected version code
PacketType gotType;
memcpy(&gotType, dataAt, sizeof(gotType));
dataAt += sizeof(expectedType);
dataLength -= sizeof(expectedType);
gotVersion = *dataAt;
bool wantImportProgress = true;
if (gotType == expectedType) {
if (canProcessVersion(gotVersion)) {
dataAt += sizeof(gotVersion);
dataLength -= sizeof(gotVersion);
fileOk = true;
qDebug("SVO file version match. Expected: %d Got: %d",
versionForPacketType(expectedDataPacketType()), gotVersion);
PacketType expectedType = expectedDataPacketType();
PacketVersion expectedVersion = versionForPacketType(expectedType);
bool hasBufferBreaks = versionHasSVOfileBreaks(expectedVersion);
// before reading the file, check to see if this version of the Octree supports file versions
if (getWantSVOfileVersions()) {
// read just enough of the file to parse the header...
const unsigned long HEADER_LENGTH = sizeof(PacketType) + sizeof(PacketVersion);
unsigned char fileHeader[HEADER_LENGTH];
file.read((char*)&fileHeader, HEADER_LENGTH);
headerLength = HEADER_LENGTH; // we need this later to skip to the data
unsigned char* dataAt = (unsigned char*)&fileHeader;
unsigned long dataLength = HEADER_LENGTH;
// if so, read the first byte of the file and see if it matches the expected version code
PacketType gotType;
memcpy(&gotType, dataAt, sizeof(gotType));
dataAt += sizeof(expectedType);
dataLength -= sizeof(expectedType);
gotVersion = *dataAt;
if (gotType == expectedType) {
if (canProcessVersion(gotVersion)) {
dataAt += sizeof(gotVersion);
dataLength -= sizeof(gotVersion);
fileOk = true;
qDebug("SVO file version match. Expected: %d Got: %d",
versionForPacketType(expectedDataPacketType()), gotVersion);
hasBufferBreaks = versionHasSVOfileBreaks(gotVersion);
} else {
qDebug("SVO file version mismatch. Expected: %d Got: %d",
versionForPacketType(expectedDataPacketType()), gotVersion);
}
hasBufferBreaks = versionHasSVOfileBreaks(gotVersion);
} else {
qDebug() << "SVO file type mismatch. Expected: " << nameForPacketType(expectedType)
<< " Got: " << nameForPacketType(gotType);
qDebug("SVO file version mismatch. Expected: %d Got: %d",
versionForPacketType(expectedDataPacketType()), gotVersion);
}
} else {
qDebug() << " NOTE: this file type does not include type and version information.";
fileOk = true; // assume the file is ok
}
if (hasBufferBreaks) {
qDebug() << " this version includes buffer breaks";
} else {
qDebug() << " this version does not include buffer breaks";
qDebug() << "SVO file type mismatch. Expected: " << nameForPacketType(expectedType)
<< " Got: " << nameForPacketType(gotType);
}
if (fileOk) {
} else {
qDebug() << " NOTE: this file type does not include type and version information.";
fileOk = true; // assume the file is ok
}
if (hasBufferBreaks) {
qDebug() << " this version includes buffer breaks";
} else {
qDebug() << " this version does not include buffer breaks";
}
if (fileOk) {
// if this version of the file does not include buffer breaks, then we need to load the entire file at once
if (!hasBufferBreaks) {
// if this version of the file does not include buffer breaks, then we need to load the entire file at once
if (!hasBufferBreaks) {
// read the entire file into a buffer, WHAT!? Why not.
unsigned long dataLength = streamLength - headerLength;
unsigned char* entireFileDataSection = new unsigned char[dataLength];
inputStream.readRawData((char*)entireFileDataSection, dataLength);
unsigned char* dataAt = entireFileDataSection;
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0,
SharedNodePointer(), wantImportProgress, gotVersion);
readBitstreamToTree(dataAt, dataLength, args);
delete[] entireFileDataSection;
} else {
// read the entire file into a buffer, WHAT!? Why not.
unsigned long dataLength = fileLength - headerLength;
unsigned char* entireFileDataSection = new unsigned char[dataLength];
file.read((char*)entireFileDataSection, dataLength);
unsigned char* dataAt = entireFileDataSection;
unsigned long dataLength = streamLength - headerLength;
unsigned long remainingLength = dataLength;
const unsigned long MAX_CHUNK_LENGTH = MAX_OCTREE_PACKET_SIZE * 2;
unsigned char* fileChunk = new unsigned char[MAX_CHUNK_LENGTH];
while (remainingLength > 0) {
quint16 chunkLength = 0;
inputStream.readRawData((char*)&chunkLength, sizeof(chunkLength));
remainingLength -= sizeof(chunkLength);
if (chunkLength > remainingLength) {
qDebug() << "UNEXPECTED chunk size of:" << chunkLength
<< "greater than remaining length:" << remainingLength;
break;
}
if (chunkLength > MAX_CHUNK_LENGTH) {
qDebug() << "UNEXPECTED chunk size of:" << chunkLength
<< "greater than MAX_CHUNK_LENGTH:" << MAX_CHUNK_LENGTH;
break;
}
inputStream.readRawData((char*)fileChunk, chunkLength);
remainingLength -= chunkLength;
unsigned char* dataAt = fileChunk;
unsigned long dataLength = chunkLength;
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0,
SharedNodePointer(), wantImportProgress, gotVersion);
readBitstreamToTree(dataAt, dataLength, args);
delete[] entireFileDataSection;
} else {
unsigned long dataLength = fileLength - headerLength;
unsigned long remainingLength = dataLength;
const unsigned long MAX_CHUNK_LENGTH = MAX_OCTREE_PACKET_SIZE * 2;
unsigned char* fileChunk = new unsigned char[MAX_CHUNK_LENGTH];
while (remainingLength > 0) {
quint16 chunkLength = 0;
file.read((char*)&chunkLength, sizeof(chunkLength)); // read the chunk size from the file
remainingLength -= sizeof(chunkLength);
if (chunkLength > remainingLength) {
qDebug() << "UNEXPECTED chunk size of:" << chunkLength
<< "greater than remaining length:" << remainingLength;
break;
}
if (chunkLength > MAX_CHUNK_LENGTH) {
qDebug() << "UNEXPECTED chunk size of:" << chunkLength
<< "greater than MAX_CHUNK_LENGTH:" << MAX_CHUNK_LENGTH;
break;
}
file.read((char*)fileChunk, chunkLength); // read in a section of the file larger than the chunk;
remainingLength -= chunkLength;
unsigned char* dataAt = fileChunk;
unsigned long dataLength = chunkLength;
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0,
SharedNodePointer(), wantImportProgress, gotVersion);
readBitstreamToTree(dataAt, dataLength, args);
}
delete[] fileChunk;
}
delete[] fileChunk;
}
emit importProgress(100);
file.close();
}
return fileOk;
}

View file

@ -326,7 +326,10 @@ public:
// these will read/write files that match the wireformat, excluding the 'V' leading
void writeToSVOFile(const char* filename, OctreeElement* element = NULL);
bool readFromSVOFile(const char* filename);
bool readFromSVOURL(const QString& url); // will support file urls as well...
bool readFromStream(unsigned long streamLength, QDataStream& inputStream);
unsigned long getOctreeElementsCount();