Merge branch 'master' of github.com:highfidelity/hifi into maximum-capacity

This commit is contained in:
Seth Alves 2015-03-18 13:26:46 -07:00
commit 405eceeda6
47 changed files with 787 additions and 190 deletions

View file

@ -1036,6 +1036,13 @@ void OctreeServer::readConfiguration() {
strcpy(_persistFilename, qPrintable(persistFilename)); strcpy(_persistFilename, qPrintable(persistFilename));
qDebug("persistFilename=%s", _persistFilename); qDebug("persistFilename=%s", _persistFilename);
QString persistAsFileType;
if (!readOptionString(QString("persistAsFileType"), settingsSectionObject, persistAsFileType)) {
persistAsFileType = "svo";
}
_persistAsFileType = persistAsFileType;
qDebug() << "persistAsFileType=" << _persistAsFileType;
_persistInterval = OctreePersistThread::DEFAULT_PERSIST_INTERVAL; _persistInterval = OctreePersistThread::DEFAULT_PERSIST_INTERVAL;
readOptionInt(QString("persistInterval"), settingsSectionObject, _persistInterval); readOptionInt(QString("persistInterval"), settingsSectionObject, _persistInterval);
qDebug() << "persistInterval=" << _persistInterval; qDebug() << "persistInterval=" << _persistInterval;
@ -1131,7 +1138,7 @@ void OctreeServer::run() {
// now set up PersistThread // now set up PersistThread
_persistThread = new OctreePersistThread(_tree, _persistFilename, _persistInterval, _persistThread = new OctreePersistThread(_tree, _persistFilename, _persistInterval,
_wantBackup, _settings, _debugTimestampNow); _wantBackup, _settings, _debugTimestampNow, _persistAsFileType);
if (_persistThread) { if (_persistThread) {
_persistThread->initialize(true); _persistThread->initialize(true);
} }

View file

@ -157,6 +157,7 @@ protected:
QString _statusHost; QString _statusHost;
char _persistFilename[MAX_FILENAME_LENGTH]; char _persistFilename[MAX_FILENAME_LENGTH];
QString _persistAsFileType;
int _packetsPerClientPerInterval; int _packetsPerClientPerInterval;
int _packetsTotalPerInterval; int _packetsTotalPerInterval;
Octree* _tree; // this IS a reaveraging tree Octree* _tree; // this IS a reaveraging tree

View file

@ -333,6 +333,24 @@
"default": "resources/models.svo", "default": "resources/models.svo",
"advanced": true "advanced": true
}, },
{
"name": "persistAsFileType",
"label": "File format for entity server's persistent data",
"help": "This defines how the entity server will save entities to disk.",
"default": "svo",
"type": "select",
"options": [
{
"value": "svo",
"label": "Entity server persists data as SVO"
},
{
"value": "json",
"label": "Entity server persists data as JSON"
}
],
"advanced": true
},
{ {
"name": "persistInterval", "name": "persistInterval",
"label": "Save Check Interval", "label": "Save Check Interval",

View file

@ -891,9 +891,10 @@ bool Application::event(QEvent* event) {
DependencyManager::get<AddressManager>()->handleLookupString(fileEvent->url().toString()); DependencyManager::get<AddressManager>()->handleLookupString(fileEvent->url().toString());
} else if (url.path().toLower().endsWith(SVO_EXTENSION)) { } else if (url.path().toLower().endsWith(SVO_EXTENSION)) {
emit svoImportRequested(url.url()); emit svoImportRequested(url.url());
} else if (url.path().toLower().endsWith(JS_EXTENSION)) {
askToLoadScript(url.toString());
} }
} }
return false; return false;
} }
@ -1454,17 +1455,12 @@ void Application::wheelEvent(QWheelEvent* event) {
void Application::dropEvent(QDropEvent *event) { void Application::dropEvent(QDropEvent *event) {
QString snapshotPath; QString snapshotPath;
const QMimeData *mimeData = event->mimeData(); const QMimeData *mimeData = event->mimeData();
bool atLeastOneFileAccepted = false;
foreach (QUrl url, mimeData->urls()) { foreach (QUrl url, mimeData->urls()) {
auto lower = url.path().toLower(); auto lower = url.path().toLower();
if (lower.endsWith(SNAPSHOT_EXTENSION)) { if (lower.endsWith(SNAPSHOT_EXTENSION)) {
snapshotPath = url.toLocalFile(); snapshotPath = url.toLocalFile();
break;
} else if (lower.endsWith(SVO_EXTENSION)) {
emit svoImportRequested(url.url());
event->acceptProposedAction();
return;
}
}
SnapshotMetaData* snapshotData = Snapshot::parseSnapshotData(snapshotPath); SnapshotMetaData* snapshotData = Snapshot::parseSnapshotData(snapshotPath);
if (snapshotData) { if (snapshotData) {
@ -1474,12 +1470,29 @@ void Application::dropEvent(QDropEvent *event) {
_myAvatar->setPosition(snapshotData->getLocation()); _myAvatar->setPosition(snapshotData->getLocation());
_myAvatar->setOrientation(snapshotData->getOrientation()); _myAvatar->setOrientation(snapshotData->getOrientation());
atLeastOneFileAccepted = true;
break; // don't process further files
} else { } else {
QMessageBox msgBox; QMessageBox msgBox;
msgBox.setText("No location details were found in this JPG, try dragging in an authentic Hifi snapshot."); msgBox.setText("No location details were found in the file "
+ snapshotPath + ", try dragging in an authentic Hifi snapshot.");
msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.exec(); msgBox.exec();
} }
} else if (lower.endsWith(SVO_EXTENSION)) {
emit svoImportRequested(url.url());
event->acceptProposedAction();
atLeastOneFileAccepted = true;
} else if (lower.endsWith(JS_EXTENSION)) {
askToLoadScript(url.url());
atLeastOneFileAccepted = true;
}
}
if (atLeastOneFileAccepted) {
event->acceptProposedAction();
}
} }
void Application::sendPingPackets() { void Application::sendPingPackets() {
@ -1798,7 +1811,7 @@ bool Application::importEntities(const QString& urlOrFilename) {
url = QUrl::fromLocalFile(urlOrFilename); url = QUrl::fromLocalFile(urlOrFilename);
} }
bool success = _entityClipboard.readFromSVOURL(url.toString()); bool success = _entityClipboard.readFromURL(url.toString());
if (success) { if (success) {
_entityClipboard.reaverageOctreeElements(); _entityClipboard.reaverageOctreeElements();
} }
@ -2795,7 +2808,31 @@ QImage Application::renderAvatarBillboard() {
return image; return image;
} }
// FIXME, preprocessor guard this check to occur only in DEBUG builds
static QThread * activeRenderingThread = nullptr;
ViewFrustum* Application::getViewFrustum() {
#ifdef DEBUG
if (QThread::currentThread() == activeRenderingThread) {
// FIXME, should this be an assert?
qWarning() << "Calling Application::getViewFrustum() from the active rendering thread, did you mean Application::getDisplayViewFrustum()?";
}
#endif
return &_viewFrustum;
}
ViewFrustum* Application::getDisplayViewFrustum() {
#ifdef DEBUG
if (QThread::currentThread() != activeRenderingThread) {
// FIXME, should this be an assert?
qWarning() << "Calling Application::getDisplayViewFrustum() from outside the active rendering thread or outside rendering, did you mean Application::getViewFrustum()?";
}
#endif
return &_displayViewFrustum;
}
void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs::RenderSide renderSide) { void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs::RenderSide renderSide) {
activeRenderingThread = QThread::currentThread();
PROFILE_RANGE(__FUNCTION__); PROFILE_RANGE(__FUNCTION__);
PerformanceTimer perfTimer("display"); PerformanceTimer perfTimer("display");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()");
@ -3020,6 +3057,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
glClear(GL_DEPTH_BUFFER_BIT); glClear(GL_DEPTH_BUFFER_BIT);
_overlays.renderWorld(true); _overlays.renderWorld(true);
} }
activeRenderingThread = nullptr;
} }
void Application::updateUntranslatedViewMatrix(const glm::vec3& viewMatrixTranslation) { void Application::updateUntranslatedViewMatrix(const glm::vec3& viewMatrixTranslation) {
@ -3112,18 +3150,26 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
_myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale());
} else { // HEAD zoom level } else { // HEAD zoom level
_mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); // degrees // FIXME note that the positioing of the camera relative to the avatar can suffer limited
if (_myAvatar->getSkeletonModel().isActive() && _myAvatar->getHead()->getFaceModel().isActive()) { // precision as the user's position moves further away from the origin. Thus at
// as a hack until we have a better way of dealing with coordinate precision issues, reposition the // /1e7,1e7,1e7 (well outside the buildable volume) the mirror camera veers and sways
// face/body so that the average eye position lies at the origin // wildly as you rotate your avatar because the floating point values are becoming
eyeRelativeCamera = true; // larger, squeezing out the available digits of precision you have available at the
_mirrorCamera.setPosition(_myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); // human scale for camera positioning.
} else { // Previously there was a hack to correct this using the mechanism of repositioning
// the avatar at the origin of the world for the purposes of rendering the mirror,
// but it resulted in failing to render the avatar's head model in the mirror view
// when in first person mode. Presumably this was because of some missed culling logic
// that was not accounted for in the hack.
// This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further
// investigated in order to adapt the technique while fixing the head rendering issue,
// but the complexity of the hack suggests that a better approach
_mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); // degrees
_mirrorCamera.setPosition(_myAvatar->getHead()->getEyePosition() + _mirrorCamera.setPosition(_myAvatar->getHead()->getEyePosition() +
_myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale());
} }
}
_mirrorCamera.setAspectRatio((float)region.width() / region.height()); _mirrorCamera.setAspectRatio((float)region.width() / region.height());
_mirrorCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); _mirrorCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f)));
@ -3149,58 +3195,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
// render rear mirror view // render rear mirror view
glPushMatrix(); glPushMatrix();
if (eyeRelativeCamera) {
// save absolute translations
glm::vec3 absoluteSkeletonTranslation = _myAvatar->getSkeletonModel().getTranslation();
glm::vec3 absoluteFaceTranslation = _myAvatar->getHead()->getFaceModel().getTranslation();
// get the neck position so we can translate the face relative to it
glm::vec3 neckPosition;
_myAvatar->getSkeletonModel().setTranslation(glm::vec3());
_myAvatar->getSkeletonModel().getNeckPosition(neckPosition);
// get the eye position relative to the body
glm::vec3 eyePosition = _myAvatar->getHead()->getEyePosition();
float eyeHeight = eyePosition.y - _myAvatar->getPosition().y;
// set the translation of the face relative to the neck position
_myAvatar->getHead()->getFaceModel().setTranslation(neckPosition - glm::vec3(0, eyeHeight, 0));
// translate the neck relative to the face
_myAvatar->getSkeletonModel().setTranslation(_myAvatar->getHead()->getFaceModel().getTranslation() -
neckPosition);
// update the attachments to match
QVector<glm::vec3> absoluteAttachmentTranslations;
glm::vec3 delta = _myAvatar->getSkeletonModel().getTranslation() - absoluteSkeletonTranslation;
foreach (Model* attachment, _myAvatar->getAttachmentModels()) {
absoluteAttachmentTranslations.append(attachment->getTranslation());
attachment->setTranslation(attachment->getTranslation() + delta);
}
// and lo, even the shadow matrices
glm::mat4 savedShadowMatrices[CASCADED_SHADOW_MATRIX_COUNT];
for (int i = 0; i < CASCADED_SHADOW_MATRIX_COUNT; i++) {
savedShadowMatrices[i] = _shadowMatrices[i];
_shadowMatrices[i] = glm::transpose(glm::transpose(_shadowMatrices[i]) * glm::translate(-delta));
}
displaySide(_mirrorCamera, true); displaySide(_mirrorCamera, true);
// restore absolute translations
_myAvatar->getSkeletonModel().setTranslation(absoluteSkeletonTranslation);
_myAvatar->getHead()->getFaceModel().setTranslation(absoluteFaceTranslation);
for (int i = 0; i < absoluteAttachmentTranslations.size(); i++) {
_myAvatar->getAttachmentModels().at(i)->setTranslation(absoluteAttachmentTranslations.at(i));
}
// restore the shadow matrices
for (int i = 0; i < CASCADED_SHADOW_MATRIX_COUNT; i++) {
_shadowMatrices[i] = savedShadowMatrices[i];
}
} else {
displaySide(_mirrorCamera, true);
}
glPopMatrix(); glPopMatrix();
if (!billboard) { if (!billboard) {
@ -3601,6 +3596,19 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
workerThread->start(); workerThread->start();
} }
void Application::askToLoadScript(const QString& scriptFilenameOrURL) {
QMessageBox::StandardButton reply;
QString message = "Would you like to run this script:\n" + scriptFilenameOrURL;
reply = QMessageBox::question(getWindow(), "Run Script", message, QMessageBox::Yes|QMessageBox::No);
if (reply == QMessageBox::Yes) {
qDebug() << "Chose to run the script: " << scriptFilenameOrURL;
loadScript(scriptFilenameOrURL);
} else {
qDebug() << "Declined to run the script: " << scriptFilenameOrURL;
}
}
ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded, ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded,
bool loadScriptFromEditor, bool activateMainWindow) { bool loadScriptFromEditor, bool activateMainWindow) {

View file

@ -95,6 +95,7 @@ static const float NODE_KILLED_BLUE = 0.0f;
static const QString SNAPSHOT_EXTENSION = ".jpg"; static const QString SNAPSHOT_EXTENSION = ".jpg";
static const QString SVO_EXTENSION = ".svo"; static const QString SVO_EXTENSION = ".svo";
static const QString JS_EXTENSION = ".js";
static const float BILLBOARD_FIELD_OF_VIEW = 30.0f; // degrees static const float BILLBOARD_FIELD_OF_VIEW = 30.0f; // degrees
static const float BILLBOARD_DISTANCE = 5.56f; // meters static const float BILLBOARD_DISTANCE = 5.56f; // meters
@ -174,8 +175,12 @@ public:
bool isThrottleRendering() const { return _glWidget->isThrottleRendering(); } bool isThrottleRendering() const { return _glWidget->isThrottleRendering(); }
Camera* getCamera() { return &_myCamera; } Camera* getCamera() { return &_myCamera; }
ViewFrustum* getViewFrustum() { return &_viewFrustum; } // Represents the current view frustum of the avatar.
ViewFrustum* getDisplayViewFrustum() { return &_displayViewFrustum; } ViewFrustum* getViewFrustum();
// Represents the view frustum of the current rendering pass,
// which might be different from the viewFrustum, i.e. shadowmap
// passes, mirror window passes, etc
ViewFrustum* getDisplayViewFrustum();
ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; } ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; }
const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; } const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
EntityTreeRenderer* getEntities() { return &_entities; } EntityTreeRenderer* getEntities() { return &_entities; }
@ -336,6 +341,7 @@ public slots:
void loadDialog(); void loadDialog();
void loadScriptURLDialog(); void loadScriptURLDialog();
void toggleLogDialog(); void toggleLogDialog();
void askToLoadScript(const QString& scriptFilenameOrURL);
ScriptEngine* loadScript(const QString& scriptFilename = QString(), bool isUserLoaded = true, ScriptEngine* loadScript(const QString& scriptFilename = QString(), bool isUserLoaded = true,
bool loadScriptFromEditor = false, bool activateMainWindow = false); bool loadScriptFromEditor = false, bool activateMainWindow = false);
void scriptFinished(const QString& scriptName); void scriptFinished(const QString& scriptName);

View file

@ -168,10 +168,10 @@ void GLCanvas::wheelEvent(QWheelEvent* event) {
} }
void GLCanvas::dragEnterEvent(QDragEnterEvent* event) { void GLCanvas::dragEnterEvent(QDragEnterEvent* event) {
const QMimeData *mimeData = event->mimeData(); const QMimeData* mimeData = event->mimeData();
foreach (QUrl url, mimeData->urls()) { foreach (QUrl url, mimeData->urls()) {
auto lower = url.path().toLower(); auto lower = url.path().toLower();
if (lower.endsWith(SNAPSHOT_EXTENSION) || lower.endsWith(SVO_EXTENSION)) { if (lower.endsWith(SNAPSHOT_EXTENSION) || lower.endsWith(SVO_EXTENSION) || lower.endsWith(JS_EXTENSION)) {
event->acceptProposedAction(); event->acceptProposedAction();
break; break;
} }

View file

@ -338,7 +338,7 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode, bool
// simple frustum check // simple frustum check
float boundingRadius = getBillboardSize(); float boundingRadius = getBillboardSize();
ViewFrustum* frustum = (renderMode == Avatar::SHADOW_RENDER_MODE) ? ViewFrustum* frustum = (renderMode == Avatar::SHADOW_RENDER_MODE) ?
Application::getInstance()->getShadowViewFrustum() : Application::getInstance()->getViewFrustum(); Application::getInstance()->getShadowViewFrustum() : Application::getInstance()->getDisplayViewFrustum();
if (frustum->sphereInFrustum(getPosition(), boundingRadius) == ViewFrustum::OUTSIDE) { if (frustum->sphereInFrustum(getPosition(), boundingRadius) == ViewFrustum::OUTSIDE) {
return; return;
} }
@ -1013,16 +1013,18 @@ float Avatar::getSkeletonHeight() const {
} }
float Avatar::getHeadHeight() const { float Avatar::getHeadHeight() const {
Extents extents = getHead()->getFaceModel().getBindExtents(); Extents extents = getHead()->getFaceModel().getMeshExtents();
if (!extents.isEmpty()) { if (!extents.isEmpty()) {
return extents.maximum.y - extents.minimum.y; return extents.maximum.y - extents.minimum.y;
} }
extents = _skeletonModel.getMeshExtents();
glm::vec3 neckPosition; glm::vec3 neckPosition;
glm::vec3 headPosition; if (!extents.isEmpty() && _skeletonModel.getNeckPosition(neckPosition)) {
if (_skeletonModel.getNeckPosition(neckPosition) && _skeletonModel.getHeadPosition(headPosition)) { return extents.maximum.y / 2.0f - neckPosition.y + _position.y;
return glm::distance(neckPosition, headPosition);
} }
const float DEFAULT_HEAD_HEIGHT = 0.1f;
const float DEFAULT_HEAD_HEIGHT = 0.25f;
return DEFAULT_HEAD_HEIGHT; return DEFAULT_HEAD_HEIGHT;
} }

View file

@ -37,6 +37,9 @@ bool DataWebPage::acceptNavigationRequest(QWebFrame* frame, const QNetworkReques
if (request.url().path().toLower().endsWith(SVO_EXTENSION)) { if (request.url().path().toLower().endsWith(SVO_EXTENSION)) {
Application::getInstance()->importSVOFromURL(request.url()); Application::getInstance()->importSVOFromURL(request.url());
return false; return false;
} else if (request.url().path().toLower().endsWith(JS_EXTENSION)) {
Application::getInstance()->askToLoadScript(request.url().toString());
return false;
} }
return true; return true;
} else { } else {

View file

@ -122,7 +122,7 @@ void Overlays::renderWorld(bool drawFront, RenderArgs::RenderMode renderMode, Re
float myAvatarScale = 1.0f; float myAvatarScale = 1.0f;
auto lodManager = DependencyManager::get<LODManager>(); auto lodManager = DependencyManager::get<LODManager>();
RenderArgs args = { NULL, Application::getInstance()->getViewFrustum(), RenderArgs args = { NULL, Application::getInstance()->getDisplayViewFrustum(),
lodManager->getOctreeSizeScale(), lodManager->getOctreeSizeScale(),
lodManager->getBoundaryLevelAdjust(), lodManager->getBoundaryLevelAdjust(),
renderMode, renderSide, renderMode, renderSide,

View file

@ -812,7 +812,7 @@ void AudioClient::handleAudioInput() {
} }
emit inputReceived(QByteArray(reinterpret_cast<const char*>(networkAudioSamples), emit inputReceived(QByteArray(reinterpret_cast<const char*>(networkAudioSamples),
AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL)); AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * sizeof(AudioConstants::AudioSample)));
} else { } else {
// our input loudness is 0, since we're muted // our input loudness is 0, since we're muted

View file

@ -169,6 +169,8 @@ void Player::setupAudioThread() {
_audioThread->setObjectName("Player Audio Thread"); _audioThread->setObjectName("Player Audio Thread");
_options.position = _avatar->getPosition(); _options.position = _avatar->getPosition();
_options.orientation = _avatar->getOrientation(); _options.orientation = _avatar->getOrientation();
_options.stereo = _recording->numberAudioChannel() == 2;
_injector.reset(new AudioInjector(_recording->getAudioData(), _options), &QObject::deleteLater); _injector.reset(new AudioInjector(_recording->getAudioData(), _options), &QObject::deleteLater);
_injector->moveToThread(_audioThread); _injector->moveToThread(_audioThread);
_audioThread->start(); _audioThread->start();
@ -309,7 +311,7 @@ void Player::setCurrentFrame(int currentFrame) {
if (isPlaying()) { if (isPlaying()) {
_timer.start(); _timer.start();
setAudionInjectorPosition(); setAudioInjectorPosition();
} else { } else {
_pausedFrame = _currentFrame; _pausedFrame = _currentFrame;
} }
@ -349,15 +351,7 @@ void Player::setCurrentTime(int currentTime) {
} }
} }
_currentFrame = lowestBound; setCurrentFrame(lowestBound);
_timerOffset = _recording->getFrameTimestamp(lowestBound);
if (isPlaying()) {
_timer.start();
setAudionInjectorPosition();
} else {
_pausedFrame = lowestBound;
}
} }
void Player::setVolume(float volume) { void Player::setVolume(float volume) {
@ -372,11 +366,9 @@ void Player::setAudioOffset(int audioOffset) {
_audioOffset = audioOffset; _audioOffset = audioOffset;
} }
void Player::setAudionInjectorPosition() { void Player::setAudioInjectorPosition() {
int MSEC_PER_SEC = 1000; int MSEC_PER_SEC = 1000;
int SAMPLE_SIZE = 2; // 16 bits int FRAME_SIZE = sizeof(AudioConstants::AudioSample) * _recording->numberAudioChannel();
int CHANNEL_COUNT = 1;
int FRAME_SIZE = SAMPLE_SIZE * CHANNEL_COUNT;
int currentAudioFrame = elapsed() * FRAME_SIZE * (AudioConstants::SAMPLE_RATE / MSEC_PER_SEC); int currentAudioFrame = elapsed() * FRAME_SIZE * (AudioConstants::SAMPLE_RATE / MSEC_PER_SEC);
_injector->setCurrentSendPosition(currentAudioFrame); _injector->setCurrentSendPosition(currentAudioFrame);
} }

View file

@ -61,7 +61,7 @@ private:
void setupAudioThread(); void setupAudioThread();
void cleanupAudioThread(); void cleanupAudioThread();
void loopRecording(); void loopRecording();
void setAudionInjectorPosition(); void setAudioInjectorPosition();
bool computeCurrentFrame(); bool computeCurrentFrame();
AvatarData* _avatar; AvatarData* _avatar;

View file

@ -65,6 +65,15 @@ const RecordingFrame& Recording::getFrame(int i) const {
return _frames[i]; return _frames[i];
} }
int Recording::numberAudioChannel() const {
// Check for stereo audio
int MSEC_PER_SEC = 1000;
int channelLength = (getLength() / MSEC_PER_SEC) *
AudioConstants::SAMPLE_RATE * sizeof(AudioConstants::AudioSample);
return glm::round((float)channelLength / (float)getAudioData().size());
}
void Recording::addFrame(int timestamp, RecordingFrame &frame) { void Recording::addFrame(int timestamp, RecordingFrame &frame) {
_timestamps << timestamp; _timestamps << timestamp;
_frames << frame; _frames << frame;

View file

@ -56,6 +56,7 @@ public:
qint32 getFrameTimestamp(int i) const; qint32 getFrameTimestamp(int i) const;
const RecordingFrame& getFrame(int i) const; const RecordingFrame& getFrame(int i) const;
const QByteArray& getAudioData() const { return _audioData; } const QByteArray& getAudioData() const { return _audioData; }
int numberAudioChannel() const;
protected: protected:
void addFrame(int timestamp, RecordingFrame& frame); void addFrame(int timestamp, RecordingFrame& frame);

View file

@ -266,4 +266,53 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori
return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, extraInfo, precisionPicking); return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, extraInfo, precisionPicking);
} }
bool RenderableModelEntityItem::isReadyToComputeShape() {
if (_collisionModelURL == "") {
// no model url, so we're ready to compute a shape.
return true;
}
if (! _collisionNetworkGeometry.isNull() && _collisionNetworkGeometry->isLoadedWithTextures()) {
// we have a _collisionModelURL AND a _collisionNetworkGeometry AND it's fully loaded.
return true;
}
if (_collisionNetworkGeometry.isNull()) {
// we have a _collisionModelURL but we don't yet have a _collisionNetworkGeometry.
_collisionNetworkGeometry =
DependencyManager::get<GeometryCache>()->getGeometry(_collisionModelURL, QUrl(), false, false);
if (! _collisionNetworkGeometry.isNull() && _collisionNetworkGeometry->isLoadedWithTextures()) {
// shortcut in case it's already loaded.
return true;
}
}
// the model is still being downloaded.
return false;
}
void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
if (_collisionModelURL == "") {
info.setParams(getShapeType(), 0.5f * getDimensions());
} else {
const FBXGeometry& fbxGeometry = _collisionNetworkGeometry->getFBXGeometry();
_points.clear();
foreach (const FBXMesh& mesh, fbxGeometry.meshes) {
_points << mesh.vertices;
}
info.setParams(getShapeType(), 0.5f * getDimensions(), NULL, _collisionModelURL);
info.setConvexHull(_points);
}
}
ShapeType RenderableModelEntityItem::getShapeType() const {
// XXX make hull an option in edit.js ?
if (_collisionModelURL != "") {
return SHAPE_TYPE_CONVEX_HULL;
} else {
return _shapeType;
}
}

View file

@ -30,7 +30,8 @@ public:
_needsInitialSimulation(true), _needsInitialSimulation(true),
_needsModelReload(true), _needsModelReload(true),
_myRenderer(NULL), _myRenderer(NULL),
_originalTexturesRead(false) { } _originalTexturesRead(false),
_collisionNetworkGeometry(QSharedPointer<NetworkGeometry>()) { }
virtual ~RenderableModelEntityItem(); virtual ~RenderableModelEntityItem();
@ -52,6 +53,10 @@ public:
bool needsToCallUpdate() const; bool needsToCallUpdate() const;
bool isReadyToComputeShape();
void computeShapeInfo(ShapeInfo& info);
ShapeType getShapeType() const;
private: private:
void remapTextures(); void remapTextures();
@ -62,6 +67,9 @@ private:
QString _currentTextures; QString _currentTextures;
QStringList _originalTextures; QStringList _originalTextures;
bool _originalTexturesRead; bool _originalTexturesRead;
QSharedPointer<NetworkGeometry> _collisionNetworkGeometry;
QVector<glm::vec3> _points;
}; };
#endif // hifi_RenderableModelEntityItem_h #endif // hifi_RenderableModelEntityItem_h

View file

@ -1002,7 +1002,7 @@ float EntityItem::getRadius() const {
return 0.5f * glm::length(_dimensions); return 0.5f * glm::length(_dimensions);
} }
void EntityItem::computeShapeInfo(ShapeInfo& info) const { void EntityItem::computeShapeInfo(ShapeInfo& info) {
info.setParams(getShapeType(), 0.5f * getDimensions()); info.setParams(getShapeType(), 0.5f * getDimensions());
} }

View file

@ -256,7 +256,9 @@ public:
virtual bool contains(const glm::vec3& point) const { return getAABox().contains(point); } virtual bool contains(const glm::vec3& point) const { return getAABox().contains(point); }
virtual bool containsInDomainUnits(const glm::vec3& point) const { return getAABoxInDomainUnits().contains(point); } virtual bool containsInDomainUnits(const glm::vec3& point) const { return getAABoxInDomainUnits().contains(point); }
virtual void computeShapeInfo(ShapeInfo& info) const;
virtual bool isReadyToComputeShape() { return true; }
virtual void computeShapeInfo(ShapeInfo& info);
/// return preferred shape type (actual physical shape may differ) /// return preferred shape type (actual physical shape may differ)
virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; } virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; }
@ -295,7 +297,6 @@ public:
static void setSendPhysicsUpdates(bool value) { _sendPhysicsUpdates = value; } static void setSendPhysicsUpdates(bool value) { _sendPhysicsUpdates = value; }
static bool getSendPhysicsUpdates() { return _sendPhysicsUpdates; } static bool getSendPhysicsUpdates() { return _sendPhysicsUpdates; }
protected: protected:
static bool _sendPhysicsUpdates; static bool _sendPhysicsUpdates;

View file

@ -176,7 +176,8 @@ void EntityItemProperties::setLastEdited(quint64 usecTime) {
_lastEdited = usecTime > _created ? usecTime : _created; _lastEdited = usecTime > _created ? usecTime : _created;
} }
const char* shapeTypeNames[] = {"none", "box", "sphere"}; const char* shapeTypeNames[] = {"none", "box", "sphere", "ellipsoid", "convex-hull", "plane", "compound", "capsule-x",
"capsule-y", "capsule-z", "cylinder-x", "cylinder-y", "cylinder-z"};
QHash<QString, ShapeType> stringToShapeTypeLookup; QHash<QString, ShapeType> stringToShapeTypeLookup;
@ -184,10 +185,22 @@ void buildStringToShapeTypeLookup() {
stringToShapeTypeLookup["none"] = SHAPE_TYPE_NONE; stringToShapeTypeLookup["none"] = SHAPE_TYPE_NONE;
stringToShapeTypeLookup["box"] = SHAPE_TYPE_BOX; stringToShapeTypeLookup["box"] = SHAPE_TYPE_BOX;
stringToShapeTypeLookup["sphere"] = SHAPE_TYPE_SPHERE; stringToShapeTypeLookup["sphere"] = SHAPE_TYPE_SPHERE;
stringToShapeTypeLookup["ellipsoid"] = SHAPE_TYPE_ELLIPSOID;
stringToShapeTypeLookup["convex-hull"] = SHAPE_TYPE_CONVEX_HULL;
stringToShapeTypeLookup["plane"] = SHAPE_TYPE_PLANE;
stringToShapeTypeLookup["compound"] = SHAPE_TYPE_COMPOUND;
stringToShapeTypeLookup["capsule-x"] = SHAPE_TYPE_CAPSULE_X;
stringToShapeTypeLookup["capsule-y"] = SHAPE_TYPE_CAPSULE_Y;
stringToShapeTypeLookup["capsule-z"] = SHAPE_TYPE_CAPSULE_Z;
stringToShapeTypeLookup["cylinder-x"] = SHAPE_TYPE_CYLINDER_X;
stringToShapeTypeLookup["cylinder-y"] = SHAPE_TYPE_CYLINDER_Y;
stringToShapeTypeLookup["cylinder-z"] = SHAPE_TYPE_CYLINDER_Z;
} }
QString EntityItemProperties::getShapeTypeAsString() const { QString EntityItemProperties::getShapeTypeAsString() const {
if (_shapeType < sizeof(shapeTypeNames) / sizeof(char *))
return QString(shapeTypeNames[_shapeType]); return QString(shapeTypeNames[_shapeType]);
return QString("none");
} }
void EntityItemProperties::setShapeTypeFromString(const QString& shapeName) { void EntityItemProperties::setShapeTypeFromString(const QString& shapeName) {

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include <VariantMapToScriptValue.h>
#include "EntityScriptingInterface.h" #include "EntityScriptingInterface.h"
#include "EntityTree.h" #include "EntityTree.h"
#include "LightEntityItem.h" #include "LightEntityItem.h"

View file

@ -10,13 +10,18 @@
// //
#include <PerfStat.h> #include <PerfStat.h>
#include <QDateTime>
#include <QtScript/QScriptEngine>
#include "EntityTree.h" #include "EntityTree.h"
#include "EntitySimulation.h" #include "EntitySimulation.h"
#include "VariantMapToScriptValue.h"
#include "AddEntityOperator.h" #include "AddEntityOperator.h"
#include "MovingEntitiesOperator.h" #include "MovingEntitiesOperator.h"
#include "UpdateEntityOperator.h" #include "UpdateEntityOperator.h"
#include "QVariantGLM.h"
#include "RecurseOctreeToMapOperator.h"
EntityTree::EntityTree(bool shouldReaverage) : EntityTree::EntityTree(bool shouldReaverage) :
Octree(shouldReaverage), Octree(shouldReaverage),
@ -466,7 +471,7 @@ bool EntityTree::findNearPointOperation(OctreeElement* element, void* extraData)
return true; // keep searching in case children have closer entities return true; // keep searching in case children have closer entities
} }
// if this element doesn't contain the point, then none of it's children can contain the point, so stop searching // if this element doesn't contain the point, then none of its children can contain the point, so stop searching
return false; return false;
} }
@ -1080,3 +1085,41 @@ bool EntityTree::sendEntitiesOperation(OctreeElement* element, void* extraData)
return true; return true;
} }
bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElement* element) {
entityDescription["Entities"] = QVariantList();
QScriptEngine scriptEngine;
RecurseOctreeToMapOperator theOperator(entityDescription, element, &scriptEngine);
recurseTreeWithOperator(&theOperator);
return true;
}
bool EntityTree::readFromMap(QVariantMap& map) {
// map will have a top-level list keyed as "Entities". This will be extracted
// and iterated over. Each member of this list is converted to a QVariantMap, then
// to a QScriptValue, and then to EntityItemProperties. These properties are used
// to add the new entity to the EnitytTree.
QVariantList entitiesQList = map["Entities"].toList();
QScriptEngine scriptEngine;
foreach (QVariant entityVariant, entitiesQList) {
// QVariantMap --> QScriptValue --> EntityItemProperties --> Entity
QVariantMap entityMap = entityVariant.toMap();
QScriptValue entityScriptValue = variantMapToScriptValue(entityMap, scriptEngine);
EntityItemProperties properties;
EntityItemPropertiesFromScriptValue(entityScriptValue, properties);
EntityItemID entityItemID;
if (entityMap.contains("id")) {
entityItemID = EntityItemID(QUuid(entityMap["id"].toString()));
} else {
entityItemID = EntityItemID(QUuid::createUuid());
}
EntityItem* entity = addEntity(entityItemID, properties);
if (!entity) {
qDebug() << "adding Entity failed:" << entityItemID << entity->getType();
}
}
return true;
}

View file

@ -164,6 +164,9 @@ public:
bool wantEditLogging() const { return _wantEditLogging; } bool wantEditLogging() const { return _wantEditLogging; }
void setWantEditLogging(bool value) { _wantEditLogging = value; } void setWantEditLogging(bool value) { _wantEditLogging = value; }
bool writeToMap(QVariantMap& entityDescription, OctreeElement* element);
bool readFromMap(QVariantMap& entityDescription);
signals: signals:
void deletingEntity(const EntityItemID& entityID); void deletingEntity(const EntityItemID& entityID);
void addingEntity(const EntityItemID& entityID); void addingEntity(const EntityItemID& entityID);

View file

@ -17,6 +17,7 @@
#include "EntityTree.h" #include "EntityTree.h"
#include "EntityTreeElement.h" #include "EntityTreeElement.h"
#include "ModelEntityItem.h" #include "ModelEntityItem.h"
#include "ResourceCache.h"
const QString ModelEntityItem::DEFAULT_MODEL_URL = QString(""); const QString ModelEntityItem::DEFAULT_MODEL_URL = QString("");
const QString ModelEntityItem::DEFAULT_COLLISION_MODEL_URL = QString(""); const QString ModelEntityItem::DEFAULT_COLLISION_MODEL_URL = QString("");

View file

@ -0,0 +1,54 @@
//
// RecurseOctreeToMapOperator.cpp
// libraries/entities/src
//
// Created by Seth Alves on 3/16/15.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "RecurseOctreeToMapOperator.h"
RecurseOctreeToMapOperator::RecurseOctreeToMapOperator(QVariantMap& map, OctreeElement *top, QScriptEngine *engine) :
RecurseOctreeOperator(),
_map(map),
_top(top),
_engine(engine)
{
// if some element "top" was given, only save information for that element and it's children.
if (_top) {
_withinTop = false;
} else {
// top was NULL, export entire tree.
_withinTop = true;
}
};
bool RecurseOctreeToMapOperator::preRecursion(OctreeElement* element) {
if (element == _top) {
_withinTop = true;
}
return true;
}
bool RecurseOctreeToMapOperator::postRecursion(OctreeElement* element) {
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
const QList<EntityItem*>& entities = entityTreeElement->getEntities();
QVariantList entitiesQList = qvariant_cast<QVariantList>(_map["Entities"]);
foreach (EntityItem* entityItem, entities) {
EntityItemProperties properties = entityItem->getProperties();
QScriptValue qScriptValues = EntityItemPropertiesToScriptValue(_engine, properties);
entitiesQList << qScriptValues.toVariant();
}
_map["Entities"] = entitiesQList;
if (element == _top) {
_withinTop = false;
}
return true;
}

View file

@ -0,0 +1,24 @@
//
// RecurseOctreeToMapOperator.h
// libraries/entities/src
//
// Created by Seth Alves on 3/16/15.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "EntityTree.h"
class RecurseOctreeToMapOperator : public RecurseOctreeOperator {
public:
RecurseOctreeToMapOperator(QVariantMap& map, OctreeElement *top, QScriptEngine *engine);
bool preRecursion(OctreeElement* element);
bool postRecursion(OctreeElement* element);
private:
QVariantMap& _map;
OctreeElement *_top;
QScriptEngine *_engine;
bool _withinTop;
};

View file

@ -48,14 +48,26 @@ void ResourceCache::refresh(const QUrl& url) {
} }
} }
QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl& fallback, bool delayLoad, void* extra) { QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl& fallback,
bool delayLoad, void* extra, bool block) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
// This will re-call this method in the main thread. If block is true and the main thread
// is waiting on a lock, we'll deadlock here.
if (block) {
QSharedPointer<Resource> result; QSharedPointer<Resource> result;
QMetaObject::invokeMethod(this, "getResource", Qt::BlockingQueuedConnection, QMetaObject::invokeMethod(this, "getResource", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QSharedPointer<Resource>, result), Q_ARG(const QUrl&, url), Q_ARG(const QUrl&, fallback), Q_RETURN_ARG(QSharedPointer<Resource>, result), Q_ARG(const QUrl&, url),
Q_ARG(bool, delayLoad), Q_ARG(void*, extra)); Q_ARG(const QUrl&, fallback), Q_ARG(bool, delayLoad), Q_ARG(void*, extra));
return result; return result;
} else {
// Queue the re-invocation of this method, but if the main thread is blocked, don't wait. The
// return value may be NULL -- it's expected that this will be called again later, in order
// to receive the actual Resource.
QMetaObject::invokeMethod(this, "getResource", Qt::QueuedConnection,
Q_ARG(const QUrl&, url),
Q_ARG(const QUrl&, fallback), Q_ARG(bool, delayLoad), Q_ARG(void*, extra));
return _resources.value(url);
}
} }
if (!url.isValid() && !url.isEmpty() && fallback.isValid()) { if (!url.isValid() && !url.isEmpty() && fallback.isValid()) {

View file

@ -89,7 +89,7 @@ protected:
/// \param delayLoad if true, don't load the resource immediately; wait until load is first requested /// \param delayLoad if true, don't load the resource immediately; wait until load is first requested
/// \param extra extra data to pass to the creator, if appropriate /// \param extra extra data to pass to the creator, if appropriate
Q_INVOKABLE QSharedPointer<Resource> getResource(const QUrl& url, const QUrl& fallback = QUrl(), Q_INVOKABLE QSharedPointer<Resource> getResource(const QUrl& url, const QUrl& fallback = QUrl(),
bool delayLoad = false, void* extra = NULL); bool delayLoad = false, void* extra = NULL, bool block = true);
/// Creates a new resource. /// Creates a new resource.
virtual QSharedPointer<Resource> createResource(const QUrl& url, virtual QSharedPointer<Resource> createResource(const QUrl& url,

View file

@ -27,6 +27,10 @@
#include <QNetworkReply> #include <QNetworkReply>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QVector> #include <QVector>
#include <QFile>
#include <QJsonDocument>
#include <QFileInfo>
#include <QString>
#include <GeometryUtil.h> #include <GeometryUtil.h>
#include <LogHandler.h> #include <LogHandler.h>
@ -35,6 +39,7 @@
#include <PacketHeaders.h> #include <PacketHeaders.h>
#include <SharedUtil.h> #include <SharedUtil.h>
#include <Shape.h> #include <Shape.h>
#include <PathUtils.h>
#include "CoverageMap.h" #include "CoverageMap.h"
#include "OctreeConstants.h" #include "OctreeConstants.h"
@ -42,6 +47,9 @@
#include "Octree.h" #include "Octree.h"
#include "ViewFrustum.h" #include "ViewFrustum.h"
QVector<QString> PERSIST_EXTENSIONS = {"svo", "json"};
float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale) { float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale) {
return voxelSizeScale / powf(2, renderLevel); return voxelSizeScale / powf(2, renderLevel);
} }
@ -1841,21 +1849,22 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element,
return bytesAtThisLevel; return bytesAtThisLevel;
} }
bool Octree::readFromSVOFile(const char* fileName) { bool Octree::readFromFile(const char* fileName) {
bool fileOk = false; bool fileOk = false;
QFile file(fileName); QString qFileName = findMostRecentFileExtension(fileName, PERSIST_EXTENSIONS);
QFile file(qFileName);
fileOk = file.open(QIODevice::ReadOnly); fileOk = file.open(QIODevice::ReadOnly);
if(fileOk) { if(fileOk) {
QDataStream fileInputStream(&file); QDataStream fileInputStream(&file);
QFileInfo fileInfo(fileName); QFileInfo fileInfo(qFileName);
unsigned long fileLength = fileInfo.size(); unsigned long fileLength = fileInfo.size();
emit importSize(1.0f, 1.0f, 1.0f); emit importSize(1.0f, 1.0f, 1.0f);
emit importProgress(0); emit importProgress(0);
qDebug("Loading file %s...", fileName); qDebug() << "Loading file" << qFileName << "...";
fileOk = readFromStream(fileLength, fileInputStream); fileOk = readFromStream(fileLength, fileInputStream);
@ -1866,14 +1875,14 @@ bool Octree::readFromSVOFile(const char* fileName) {
return fileOk; return fileOk;
} }
bool Octree::readFromSVOURL(const QString& urlString) { bool Octree::readFromURL(const QString& urlString) {
bool readOk = false; bool readOk = false;
// determine if this is a local file or a network resource // determine if this is a local file or a network resource
QUrl url(urlString); QUrl url(urlString);
if (url.isLocalFile()) { if (url.isLocalFile()) {
readOk = readFromSVOFile(qPrintable(url.toLocalFile())); readOk = readFromFile(qPrintable(url.toLocalFile()));
} else { } else {
QNetworkRequest request; QNetworkRequest request;
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
@ -1899,6 +1908,23 @@ bool Octree::readFromSVOURL(const QString& urlString) {
bool Octree::readFromStream(unsigned long streamLength, QDataStream& inputStream) { bool Octree::readFromStream(unsigned long streamLength, QDataStream& inputStream) {
// decide if this is SVO or JSON
QIODevice *device = inputStream.device();
char firstChar;
device->getChar(&firstChar);
device->ungetChar(firstChar);
if (firstChar == (char) PacketTypeEntityData) {
return readSVOFromStream(streamLength, inputStream);
} else {
return readJSONFromStream(streamLength, inputStream);
}
}
bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStream) {
bool fileOk = false; bool fileOk = false;
PacketVersion gotVersion = 0; PacketVersion gotVersion = 0;
@ -2026,6 +2052,53 @@ bool Octree::readFromStream(unsigned long streamLength, QDataStream& inputStream
return fileOk; return fileOk;
} }
bool Octree::readJSONFromStream(unsigned long streamLength, QDataStream& inputStream) {
char *rawData = new char[streamLength];
inputStream.readRawData(rawData, streamLength);
QJsonDocument d = QJsonDocument::fromJson(rawData);
QVariant v = d.toVariant();
QVariantMap m = v.toMap();
readFromMap(m);
delete rawData;
return true;
}
void Octree::writeToFile(const char* fileName, OctreeElement* element, QString persistAsFileType) {
// make the sure file extension makes sense
QString qFileName = fileNameWithoutExtension(QString(fileName), PERSIST_EXTENSIONS) + "." + persistAsFileType;
QByteArray byteArray = qFileName.toUtf8();
const char* cFileName = byteArray.constData();
if (persistAsFileType == "svo") {
writeToSVOFile(fileName, element);
} else if (persistAsFileType == "json") {
writeToJSONFile(cFileName, element);
} else {
qDebug() << "unable to write octree to file of type" << persistAsFileType;
}
}
void Octree::writeToJSONFile(const char* fileName, OctreeElement* element) {
QFile persistFile(fileName);
QVariantMap entityDescription;
qDebug("Saving to file %s...", fileName);
OctreeElement* top;
if (element) {
top = element;
} else {
top = _rootElement;
}
bool entityDescriptionSuccess = writeToMap(entityDescription, top);
if (entityDescriptionSuccess && persistFile.open(QIODevice::WriteOnly)) {
persistFile.write(QJsonDocument::fromVariant(entityDescription).toJson());
} else {
qCritical("Could not write to JSON description of entities.");
}
}
void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) { void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) {
std::ofstream file(fileName, std::ios::out|std::ios::binary); std::ofstream file(fileName, std::ios::out|std::ios::binary);

View file

@ -35,6 +35,9 @@ class Shape;
#include <QObject> #include <QObject>
#include <QReadWriteLock> #include <QReadWriteLock>
extern QVector<QString> PERSIST_EXTENSIONS;
/// derive from this class to use the Octree::recurseTreeWithOperator() method /// derive from this class to use the Octree::recurseTreeWithOperator() method
class RecurseOctreeOperator { class RecurseOctreeOperator {
public: public:
@ -324,13 +327,19 @@ public:
// Note: this assumes the fileFormat is the HIO individual voxels code files // Note: this assumes the fileFormat is the HIO individual voxels code files
void loadOctreeFile(const char* fileName, bool wantColorRandomizer); void loadOctreeFile(const char* fileName, bool wantColorRandomizer);
// these will read/write files that match the wireformat, excluding the 'V' leading // Octree exporters
void writeToFile(const char* filename, OctreeElement* element = NULL, QString persistAsFileType = "svo");
void writeToJSONFile(const char* filename, OctreeElement* element = NULL);
void writeToSVOFile(const char* filename, OctreeElement* element = NULL); void writeToSVOFile(const char* filename, OctreeElement* element = NULL);
virtual bool writeToMap(QVariantMap& entityDescription, OctreeElement* element) = 0;
bool readFromSVOFile(const char* filename); // Octree importers
bool readFromSVOURL(const QString& url); // will support file urls as well... bool readFromFile(const char* filename);
bool readFromURL(const QString& url); // will support file urls as well...
bool readFromStream(unsigned long streamLength, QDataStream& inputStream); bool readFromStream(unsigned long streamLength, QDataStream& inputStream);
bool readSVOFromStream(unsigned long streamLength, QDataStream& inputStream);
bool readJSONFromStream(unsigned long streamLength, QDataStream& inputStream);
virtual bool readFromMap(QVariantMap& entityDescription) = 0;
unsigned long getOctreeElementsCount(); unsigned long getOctreeElementsCount();

View file

@ -20,16 +20,19 @@
#include <QFile> #include <QFile>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonObject> #include <QJsonObject>
#include <QJsonDocument>
#include <PerfStat.h> #include <PerfStat.h>
#include <SharedUtil.h> #include <SharedUtil.h>
#include <PathUtils.h>
#include "OctreePersistThread.h" #include "OctreePersistThread.h"
const int OctreePersistThread::DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds const int OctreePersistThread::DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds
OctreePersistThread::OctreePersistThread(Octree* tree, const QString& filename, int persistInterval, OctreePersistThread::OctreePersistThread(Octree* tree, const QString& filename, int persistInterval,
bool wantBackup, const QJsonObject& settings, bool debugTimestampNow) : bool wantBackup, const QJsonObject& settings, bool debugTimestampNow,
QString persistAsFileType) :
_tree(tree), _tree(tree),
_filename(filename), _filename(filename),
_persistInterval(persistInterval), _persistInterval(persistInterval),
@ -38,9 +41,14 @@ OctreePersistThread::OctreePersistThread(Octree* tree, const QString& filename,
_lastCheck(0), _lastCheck(0),
_wantBackup(wantBackup), _wantBackup(wantBackup),
_debugTimestampNow(debugTimestampNow), _debugTimestampNow(debugTimestampNow),
_lastTimeDebug(0) _lastTimeDebug(0),
_persistAsFileType(persistAsFileType)
{ {
parseSettings(settings); parseSettings(settings);
// in case the persist filename has an extension that doesn't match the file type
QString sansExt = fileNameWithoutExtension(_filename, PERSIST_EXTENSIONS);
_filename = sansExt + "." + _persistAsFileType;
} }
void OctreePersistThread::parseSettings(const QJsonObject& settings) { void OctreePersistThread::parseSettings(const QJsonObject& settings) {
@ -140,7 +148,7 @@ bool OctreePersistThread::process() {
qDebug() << "Loading Octree... lock file removed:" << lockFileName; qDebug() << "Loading Octree... lock file removed:" << lockFileName;
} }
persistantFileRead = _tree->readFromSVOFile(_filename.toLocal8Bit().constData()); persistantFileRead = _tree->readFromFile(qPrintable(_filename.toLocal8Bit()));
_tree->pruneTree(); _tree->pruneTree();
} }
_tree->unlock(); _tree->unlock();
@ -242,9 +250,7 @@ void OctreePersistThread::persist() {
if(lockFile.is_open()) { if(lockFile.is_open()) {
qDebug() << "saving Octree lock file created at:" << lockFileName; qDebug() << "saving Octree lock file created at:" << lockFileName;
qDebug() << "saving Octree to file " << _filename << "..."; _tree->writeToFile(qPrintable(_filename), NULL, _persistAsFileType);
_tree->writeToSVOFile(qPrintable(_filename));
time(&_lastPersistTime); time(&_lastPersistTime);
_tree->clearDirtyBit(); // tree is clean after saving _tree->clearDirtyBit(); // tree is clean after saving
qDebug() << "DONE saving Octree to file..."; qDebug() << "DONE saving Octree to file...";
@ -347,7 +353,7 @@ void OctreePersistThread::rollOldBackupVersions(const BackupRule& rule) {
backupExtensionN.replace(QString("%N"), QString::number(n)); backupExtensionN.replace(QString("%N"), QString::number(n));
backupExtensionNplusOne.replace(QString("%N"), QString::number(n+1)); backupExtensionNplusOne.replace(QString("%N"), QString::number(n+1));
QString backupFilenameN = _filename + backupExtensionN; QString backupFilenameN = findMostRecentFileExtension(_filename, PERSIST_EXTENSIONS) + backupExtensionN;
QString backupFilenameNplusOne = _filename + backupExtensionNplusOne; QString backupFilenameNplusOne = _filename + backupExtensionNplusOne;
QFile backupFileN(backupFilenameN); QFile backupFileN(backupFilenameN);

View file

@ -35,7 +35,7 @@ public:
OctreePersistThread(Octree* tree, const QString& filename, int persistInterval = DEFAULT_PERSIST_INTERVAL, OctreePersistThread(Octree* tree, const QString& filename, int persistInterval = DEFAULT_PERSIST_INTERVAL,
bool wantBackup = false, const QJsonObject& settings = QJsonObject(), bool wantBackup = false, const QJsonObject& settings = QJsonObject(),
bool debugTimestampNow = false); bool debugTimestampNow = false, QString persistAsFileType="svo");
bool isInitialLoadComplete() const { return _initialLoadComplete; } bool isInitialLoadComplete() const { return _initialLoadComplete; }
quint64 getLoadElapsedTime() const { return _loadTimeUSecs; } quint64 getLoadElapsedTime() const { return _loadTimeUSecs; }
@ -72,6 +72,8 @@ private:
bool _debugTimestampNow; bool _debugTimestampNow;
quint64 _lastTimeDebug; quint64 _lastTimeDebug;
QString _persistAsFileType;
}; };
#endif // hifi_OctreePersistThread_h #endif // hifi_OctreePersistThread_h

View file

@ -63,6 +63,8 @@ void PhysicsEngine::addEntityInternal(EntityItem* entity) {
assert(entity); assert(entity);
void* physicsInfo = entity->getPhysicsInfo(); void* physicsInfo = entity->getPhysicsInfo();
if (!physicsInfo) { if (!physicsInfo) {
qDebug() << "PhysicsEngine::addEntityInternal(" << entity;
if (entity->isReadyToComputeShape()) {
ShapeInfo shapeInfo; ShapeInfo shapeInfo;
entity->computeShapeInfo(shapeInfo); entity->computeShapeInfo(shapeInfo);
btCollisionShape* shape = _shapeManager.getShape(shapeInfo); btCollisionShape* shape = _shapeManager.getShape(shapeInfo);
@ -82,6 +84,7 @@ void PhysicsEngine::addEntityInternal(EntityItem* entity) {
//qDebug() << "failed to add entity " << entity->getEntityItemID() << " to physics engine"; //qDebug() << "failed to add entity " << entity->getEntityItemID() << " to physics engine";
} }
} }
}
} }
void PhysicsEngine::removeEntityInternal(EntityItem* entity) { void PhysicsEngine::removeEntityInternal(EntityItem* entity) {
@ -322,16 +325,21 @@ void PhysicsEngine::stepSimulation() {
// //
// TODO: untangle these lock sequences. // TODO: untangle these lock sequences.
_entityTree->lockForWrite(); _entityTree->lockForWrite();
_avatarData->lockForWrite();
lock(); lock();
_dynamicsWorld->synchronizeMotionStates(); _dynamicsWorld->synchronizeMotionStates();
if (_avatarData->isPhysicsEnabled()) { _avatarData->lockForRead();
bool avatarHasPhysicsEnabled = _avatarData->isPhysicsEnabled();
_avatarData->unlock();
if (avatarHasPhysicsEnabled) {
const btTransform& avatarTransform = _avatarGhostObject->getWorldTransform(); const btTransform& avatarTransform = _avatarGhostObject->getWorldTransform();
glm::quat rotation = bulletToGLM(avatarTransform.getRotation()); glm::quat rotation = bulletToGLM(avatarTransform.getRotation());
glm::vec3 offset = rotation * _avatarShapeLocalOffset; glm::vec3 offset = rotation * _avatarShapeLocalOffset;
_avatarData->lockForWrite();
_avatarData->setOrientation(rotation); _avatarData->setOrientation(rotation);
_avatarData->setPosition(bulletToGLM(avatarTransform.getOrigin()) - offset); _avatarData->setPosition(bulletToGLM(avatarTransform.getOrigin()) - offset);
_avatarData->unlock();
} }
unlock(); unlock();

View file

@ -26,6 +26,9 @@ int ShapeInfoUtil::toBulletShapeType(int shapeInfoType) {
case SHAPE_TYPE_CAPSULE_Y: case SHAPE_TYPE_CAPSULE_Y:
bulletShapeType = CAPSULE_SHAPE_PROXYTYPE; bulletShapeType = CAPSULE_SHAPE_PROXYTYPE;
break; break;
case SHAPE_TYPE_CONVEX_HULL:
bulletShapeType = CONVEX_HULL_SHAPE_PROXYTYPE;
break;
} }
return bulletShapeType; return bulletShapeType;
} }
@ -42,6 +45,9 @@ int ShapeInfoUtil::fromBulletShapeType(int bulletShapeType) {
case CAPSULE_SHAPE_PROXYTYPE: case CAPSULE_SHAPE_PROXYTYPE:
shapeInfoType = SHAPE_TYPE_CAPSULE_Y; shapeInfoType = SHAPE_TYPE_CAPSULE_Y;
break; break;
case CONVEX_HULL_SHAPE_PROXYTYPE:
shapeInfoType = SHAPE_TYPE_CONVEX_HULL;
break;
} }
return shapeInfoType; return shapeInfoType;
} }
@ -60,8 +66,21 @@ void ShapeInfoUtil::collectInfoFromShape(const btCollisionShape* shape, ShapeInf
info.setSphere(sphereShape->getRadius()); info.setSphere(sphereShape->getRadius());
} }
break; break;
default: case SHAPE_TYPE_CONVEX_HULL: {
const btConvexHullShape* convexHullShape = static_cast<const btConvexHullShape*>(shape);
const int numPoints = convexHullShape->getNumPoints();
const btVector3* btPoints = convexHullShape->getUnscaledPoints();
QVector<glm::vec3> points;
for (int i = 0; i < numPoints; i++) {
glm::vec3 point(btPoints->getX(), btPoints->getY(), btPoints->getZ());
points << point;
}
info.setConvexHull(points);
}
break;
default: {
info.clear(); info.clear();
}
break; break;
} }
} else { } else {
@ -88,6 +107,15 @@ btCollisionShape* ShapeInfoUtil::createShapeFromInfo(const ShapeInfo& info) {
shape = new btCapsuleShape(radius, height); shape = new btCapsuleShape(radius, height);
} }
break; break;
case SHAPE_TYPE_CONVEX_HULL: {
shape = new btConvexHullShape();
const QVector<glm::vec3>& points = info.getPoints();
foreach (glm::vec3 point, points) {
btVector3 btPoint(point[0], point[1], point[2]);
static_cast<btConvexHullShape*>(shape)->addPoint(btPoint);
}
}
break;
} }
return shape; return shape;
} }

View file

@ -1770,8 +1770,8 @@ void GeometryCache::renderLine(const glm::vec2& p1, const glm::vec2& p2,
} }
QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, const QUrl& fallback, bool delayLoad) { QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, const QUrl& fallback, bool delayLoad, bool block) {
return getResource(url, fallback, delayLoad).staticCast<NetworkGeometry>(); return getResource(url, fallback, delayLoad, NULL, block).staticCast<NetworkGeometry>();
} }
QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url, QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url,

View file

@ -21,8 +21,8 @@
#include <DependencyManager.h> #include <DependencyManager.h>
#include <ResourceCache.h> #include <ResourceCache.h>
#include <FBXReader.h> #include "FBXReader.h"
#include <OBJReader.h> #include "OBJReader.h"
#include <AnimationCache.h> #include <AnimationCache.h>
@ -203,7 +203,8 @@ public:
/// Loads geometry from the specified URL. /// Loads geometry from the specified URL.
/// \param fallback a fallback URL to load if the desired one is unavailable /// \param fallback a fallback URL to load if the desired one is unavailable
/// \param delayLoad if true, don't load the geometry immediately; wait until load is first requested /// \param delayLoad if true, don't load the geometry immediately; wait until load is first requested
QSharedPointer<NetworkGeometry> getGeometry(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false); QSharedPointer<NetworkGeometry> getGeometry(const QUrl& url, const QUrl& fallback = QUrl(),
bool delayLoad = false, bool block = true);
protected: protected:

View file

@ -24,7 +24,7 @@
#include <gpu/GLBackend.h> #include <gpu/GLBackend.h>
#include <PathUtils.h> #include <PathUtils.h>
#include <PerfStat.h> #include <PerfStat.h>
#include <PhysicsEntity.h> #include "PhysicsEntity.h"
#include <ShapeCollider.h> #include <ShapeCollider.h>
#include <SphereShape.h> #include <SphereShape.h>
#include <ViewFrustum.h> #include <ViewFrustum.h>
@ -1101,6 +1101,14 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo
} }
} }
void Model::setCollisionModelURL(const QUrl& url, const QUrl& fallback, bool delayLoad) {
if (_collisionUrl == url) {
return;
}
_collisionUrl = url;
_collisionGeometry = DependencyManager::get<GeometryCache>()->getGeometry(url, fallback, delayLoad);
}
bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) { if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return false; return false;

View file

@ -24,7 +24,7 @@
#include <GeometryUtil.h> #include <GeometryUtil.h>
#include <gpu/Stream.h> #include <gpu/Stream.h>
#include <gpu/Batch.h> #include <gpu/Batch.h>
#include <PhysicsEntity.h> #include "PhysicsEntity.h"
#include <Transform.h> #include <Transform.h>
#include "AnimationHandle.h" #include "AnimationHandle.h"
@ -107,6 +107,9 @@ public:
Q_INVOKABLE void setURL(const QUrl& url, const QUrl& fallback = QUrl(), Q_INVOKABLE void setURL(const QUrl& url, const QUrl& fallback = QUrl(),
bool retainCurrent = false, bool delayLoad = false); bool retainCurrent = false, bool delayLoad = false);
// Set the model to use for collisions
Q_INVOKABLE void setCollisionModelURL(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false);
const QUrl& getURL() const { return _url; } const QUrl& getURL() const { return _url; }
/// Sets the distance parameter used for LOD computations. /// Sets the distance parameter used for LOD computations.
@ -288,10 +291,13 @@ private:
float _lodHysteresis; float _lodHysteresis;
float _nextLODHysteresis; float _nextLODHysteresis;
QSharedPointer<NetworkGeometry> _collisionGeometry;
float _pupilDilation; float _pupilDilation;
QVector<float> _blendshapeCoefficients; QVector<float> _blendshapeCoefficients;
QUrl _url; QUrl _url;
QUrl _collisionUrl;
gpu::Buffers _blendedVertexBuffers; gpu::Buffers _blendedVertexBuffers;
std::vector<Transform> _transforms; std::vector<Transform> _transforms;

View file

@ -11,6 +11,9 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <QString> #include <QString>
#include <QVector>
#include <QDateTime>
#include <QFileInfo>
#include "PathUtils.h" #include "PathUtils.h"
@ -23,3 +26,30 @@ QString& PathUtils::resourcesPath() {
#endif #endif
return staticResourcePath; return staticResourcePath;
} }
QString fileNameWithoutExtension(const QString& fileName, const QVector<QString> possibleExtensions) {
foreach (const QString possibleExtension, possibleExtensions) {
if (fileName.endsWith(possibleExtension) ||
fileName.endsWith(possibleExtension.toUpper()) ||
fileName.endsWith(possibleExtension.toLower())) {
return fileName.left(fileName.count() - possibleExtension.count() - 1);
}
}
return fileName;
}
QString findMostRecentFileExtension(const QString& originalFileName, QVector<QString> possibleExtensions) {
QString sansExt = fileNameWithoutExtension(originalFileName, possibleExtensions);
QString newestFileName = originalFileName;
QDateTime newestTime = QDateTime::fromMSecsSinceEpoch(0);
foreach (QString possibleExtension, possibleExtensions) {
QString fileName = sansExt + "." + possibleExtension;
QFileInfo fileInfo(fileName);
if (fileInfo.exists() && fileInfo.lastModified() > newestTime) {
newestFileName = fileName;
newestTime = fileInfo.lastModified();
}
}
return newestFileName;
}

View file

@ -19,4 +19,7 @@ namespace PathUtils {
QString& resourcesPath(); QString& resourcesPath();
} }
QString fileNameWithoutExtension(const QString& fileName, const QVector<QString> possibleExtensions);
QString findMostRecentFileExtension(const QString& originalFileName, QVector<QString> possibleExtensions);
#endif // hifi_PathUtils_h #endif // hifi_PathUtils_h

View file

@ -0,0 +1,48 @@
//
// QVariantGLM.cpp
// libraries/shared/src
//
// Created by Seth Alves on 3/9/15.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "QVariantGLM.h"
#include "OctalCode.h"
QVariantList glmToQList(const glm::vec3& g) {
return QVariantList() << g[0] << g[1] << g[2];
}
QVariantList glmToQList(const glm::quat& g) {
return QVariantList() << g.x << g.y << g.z << g.w;
}
QVariantList rgbColorToQList(rgbColor& v) {
return QVariantList() << (int)(v[0]) << (int)(v[1]) << (int)(v[2]);
}
glm::vec3 qListToGlmVec3(const QVariant q) {
QVariantList qList = q.toList();
return glm::vec3(qList[RED_INDEX].toFloat(), qList[GREEN_INDEX].toFloat(), qList[BLUE_INDEX].toFloat());
}
glm::quat qListToGlmQuat(const QVariant q) {
QVariantList qList = q.toList();
float x = qList[0].toFloat();
float y = qList[1].toFloat();
float z = qList[2].toFloat();
float w = qList[3].toFloat();
return glm::quat(w, x, y, z);
}
void qListtoRgbColor(const QVariant q, rgbColor returnValue) {
QVariantList qList = q.toList();
returnValue[RED_INDEX] = qList[RED_INDEX].toInt();
returnValue[GREEN_INDEX] = qList[GREEN_INDEX].toInt();
returnValue[BLUE_INDEX] = qList[BLUE_INDEX].toInt();
}

View file

@ -0,0 +1,26 @@
//
// QVariantGLM.h
// libraries/shared/src
//
// Created by Seth Alves on 3/9/15.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QList>
#include <QVariant>
#include <QVariantList>
#include <glm/glm.hpp>
#include <glm/gtx/quaternion.hpp>
#include "SharedUtil.h"
QVariantList glmToQList(const glm::vec3& g);
QVariantList glmToQList(const glm::quat& g);
QVariantList rgbColorToQList(rgbColor& v);
glm::vec3 qListToGlmVec3(const QVariant q);
glm::quat qListToGlmQuat(const QVariant q);
void qListtoRgbColor(const QVariant q, rgbColor returnValue);

View file

@ -22,7 +22,7 @@ void ShapeInfo::clear() {
_externalData = NULL; _externalData = NULL;
} }
void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QVector<glm::vec3>* data) { void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QVector<glm::vec3>* data, QString url) {
_type = type; _type = type;
switch(type) { switch(type) {
case SHAPE_TYPE_NONE: case SHAPE_TYPE_NONE:
@ -37,8 +37,13 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QVector<
_halfExtents = glm::vec3(radius); _halfExtents = glm::vec3(radius);
break; break;
} }
case SHAPE_TYPE_CONVEX_HULL:
_url = QUrl(url);
_halfExtents = halfExtents;
break;
default: default:
_halfExtents = halfExtents; _halfExtents = halfExtents;
break;
} }
_externalData = data; _externalData = data;
} }
@ -61,6 +66,11 @@ void ShapeInfo::setEllipsoid(const glm::vec3& halfExtents) {
_doubleHashKey.clear(); _doubleHashKey.clear();
} }
void ShapeInfo::setConvexHull(const QVector<glm::vec3>& points) {
_type = SHAPE_TYPE_CONVEX_HULL;
_points = points;
}
void ShapeInfo::setCapsuleY(float radius, float halfHeight) { void ShapeInfo::setCapsuleY(float radius, float halfHeight) {
_type = SHAPE_TYPE_CAPSULE_Y; _type = SHAPE_TYPE_CAPSULE_Y;
_halfExtents = glm::vec3(radius, halfHeight, radius); _halfExtents = glm::vec3(radius, halfHeight, radius);
@ -130,6 +140,10 @@ const DoubleHashKey& ShapeInfo::getHash() const {
thisPtr->_doubleHashKey.setHash(hash); thisPtr->_doubleHashKey.setHash(hash);
// compute hash2 // compute hash2
QString url = _url.toString();
if (url == "") {
hash = _doubleHashKey.getHash2(); hash = _doubleHashKey.getHash2();
for (int i = 0; i < numData; ++i) { for (int i = 0; i < numData; ++i) {
tmpData = (*data)[i]; tmpData = (*data)[i];
@ -146,6 +160,11 @@ const DoubleHashKey& ShapeInfo::getHash() const {
hash = (hash << 16) | (hash >> 16); hash = (hash << 16) | (hash >> 16);
} }
} }
} else {
QByteArray baUrl = url.toLocal8Bit();
const char *cUrl = baUrl.data();
hash = qChecksum(cUrl, baUrl.count());
}
thisPtr->_doubleHashKey.setHash2(hash); thisPtr->_doubleHashKey.setHash2(hash);
} else { } else {
// this shape info has no external data so type+extents should be enough to generate a unique hash // this shape info has no external data so type+extents should be enough to generate a unique hash

View file

@ -13,6 +13,8 @@
#define hifi_ShapeInfo_h #define hifi_ShapeInfo_h
#include <QVector> #include <QVector>
#include <QString>
#include <QUrl>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include "DoubleHashKey.h" #include "DoubleHashKey.h"
@ -22,7 +24,7 @@ enum ShapeType {
SHAPE_TYPE_BOX, SHAPE_TYPE_BOX,
SHAPE_TYPE_SPHERE, SHAPE_TYPE_SPHERE,
SHAPE_TYPE_ELLIPSOID, SHAPE_TYPE_ELLIPSOID,
SHAPE_TYPE_HULL, SHAPE_TYPE_CONVEX_HULL,
SHAPE_TYPE_PLANE, SHAPE_TYPE_PLANE,
SHAPE_TYPE_COMPOUND, SHAPE_TYPE_COMPOUND,
SHAPE_TYPE_CAPSULE_X, SHAPE_TYPE_CAPSULE_X,
@ -34,14 +36,15 @@ enum ShapeType {
}; };
class ShapeInfo { class ShapeInfo {
public: public:
void clear(); void clear();
void setParams(ShapeType type, const glm::vec3& halfExtents, QVector<glm::vec3>* data = NULL); void setParams(ShapeType type, const glm::vec3& halfExtents, QVector<glm::vec3>* data = NULL, QString url="");
void setBox(const glm::vec3& halfExtents); void setBox(const glm::vec3& halfExtents);
void setSphere(float radius); void setSphere(float radius);
void setEllipsoid(const glm::vec3& halfExtents); void setEllipsoid(const glm::vec3& halfExtents);
//void setHull(); // TODO: implement this void setConvexHull(const QVector<glm::vec3>& points);
void setCapsuleY(float radius, float halfHeight); void setCapsuleY(float radius, float halfHeight);
const int getType() const { return _type; } const int getType() const { return _type; }
@ -51,6 +54,11 @@ public:
void setData(const QVector<glm::vec3>* data) { _externalData = data; } void setData(const QVector<glm::vec3>* data) { _externalData = data; }
const QVector<glm::vec3>* getData() const { return _externalData; } const QVector<glm::vec3>* getData() const { return _externalData; }
const QVector<glm::vec3>& getPoints() const { return _points; }
void clearPoints () { _points.clear(); }
void appendToPoints (const QVector<glm::vec3>& newPoints) { _points << newPoints; }
float computeVolume() const; float computeVolume() const;
const DoubleHashKey& getHash() const; const DoubleHashKey& getHash() const;
@ -60,6 +68,8 @@ protected:
glm::vec3 _halfExtents = glm::vec3(0.0f); glm::vec3 _halfExtents = glm::vec3(0.0f);
DoubleHashKey _doubleHashKey; DoubleHashKey _doubleHashKey;
const QVector<glm::vec3>* _externalData = NULL; const QVector<glm::vec3>* _externalData = NULL;
QVector<glm::vec3> _points; // points for convex collision hull
QUrl _url; // url for model of convex collision hull
}; };
#endif // hifi_ShapeInfo_h #endif // hifi_ShapeInfo_h

View file

@ -0,0 +1,47 @@
//
// VariantMapToScriptValue.cpp
// libraries/shared/src/
//
// Created by Brad Hefta-Gaub on 12/6/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QDebug>
#include "VariantMapToScriptValue.h"
QScriptValue variantMapToScriptValue(QVariantMap& variantMap, QScriptEngine& scriptEngine) {
QScriptValue scriptValue = scriptEngine.newObject();
for (QVariantMap::const_iterator iter = variantMap.begin(); iter != variantMap.end(); ++iter) {
QString key = iter.key();
QVariant qValue = iter.value();
switch(qValue.type()) {
case QVariant::Bool:
scriptValue.setProperty(key, qValue.toBool());
break;
case QVariant::Int:
scriptValue.setProperty(key, qValue.toInt());
break;
case QVariant::Double:
scriptValue.setProperty(key, qValue.toDouble());
break;
case QVariant::String: {
scriptValue.setProperty(key, scriptEngine.newVariant(qValue));
break;
}
case QVariant::Map: {
QVariantMap childMap = qValue.toMap();
scriptValue.setProperty(key, variantMapToScriptValue(childMap, scriptEngine));
break;
}
default:
qDebug() << "unhandled QScript type" << qValue.type();
}
}
return scriptValue;
}

View file

@ -0,0 +1,16 @@
//
// VariantMapToScriptValue.h
// libraries/shared/src/
//
// Created by Brad Hefta-Gaub on 12/6/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QVariant>
#include <QScriptValue>
#include <QScriptEngine>
QScriptValue variantMapToScriptValue(QVariantMap& variantMap, QScriptEngine& scriptEngine);