diff --git a/examples/localVoxelsExample.js b/examples/localVoxelsExample.js new file mode 100644 index 0000000000..7d9007f590 --- /dev/null +++ b/examples/localVoxelsExample.js @@ -0,0 +1,54 @@ + +var TREE_SCALE = 16384; +var tree = LocalVoxels("tree"); +tree.setVoxel(0, 0, 0, + 0.5 * TREE_SCALE, + 255, 0, 0); +tree.setVoxel(0.5 * TREE_SCALE, + 0.5 * TREE_SCALE, + 0.5 * TREE_SCALE, + 0.5 * TREE_SCALE, + 0, 255, 0); + +var copy = LocalVoxels("copy"); +tree.pasteFrom(0, 0, 0, TREE_SCALE, "copy"); +tree.pasteFrom(0, 0, 0, TREE_SCALE, "clipboard"); + +var overlay1 = Overlays.addOverlay("localvoxels", { + position: {x: 1, y: 1, z: 1}, + size: 1, + name: "tree" + }); +var overlay2 = Overlays.addOverlay("localvoxels", { + position: {x: 1, y: 2, z: 1}, + size: 1, + name: "tree" + }); +var overlay3 = Overlays.addOverlay("localvoxels", { + position: {x: 1, y: 3, z: 1}, + size: 1, + name: "tree" + }); +var overlay4 = Overlays.addOverlay("localvoxels", { + position: {x: 1, y: 4, z: 1}, + size: 1, + name: "copy" + }); + +var clipboard = Overlays.addOverlay("localvoxels", { + position: {x: 1, y: 5, z: 1}, + size: 1, + name: "clipboard" + }); + + + +// When our script shuts down, we should clean up all of our overlays +function scriptEnding() { + Overlays.deleteOverlay(overlay1); + Overlays.deleteOverlay(overlay2); + Overlays.deleteOverlay(overlay3); + Overlays.deleteOverlay(overlay4); + Overlays.deleteOverlay(clipboard); +} +Script.scriptEnding.connect(scriptEnding); \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b21d660452..d743c2a8c8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -61,6 +61,7 @@ #include #include #include +#include #include "Application.h" #include "ClipboardScriptingInterface.h" @@ -311,7 +312,10 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : checkVersion(); _overlays.init(_glWidget); // do this before scripts load - + + LocalVoxelsList::getInstance()->addPersistantTree(DOMAIN_TREE_NAME, _voxels.getTree()); + LocalVoxelsList::getInstance()->addPersistantTree(CLIPBOARD_TREE_NAME, &_clipboard); + // do this as late as possible so that all required subsystems are inialized loadScripts(); } @@ -1424,6 +1428,7 @@ void Application::pasteVoxelsToOctalCode(const unsigned char* octalCodeDestinati args.newBaseOctCode = octalCodeDestination; _sharedVoxelSystem.getTree()->recurseTreeWithOperation(sendVoxelsOperation, &args); + // Switch back to clipboard if it was an import if (_sharedVoxelSystem.getTree() != &_clipboard) { _sharedVoxelSystem.killLocalVoxels(); _sharedVoxelSystem.changeTree(&_clipboard); @@ -1885,6 +1890,8 @@ void Application::update(float deltaTime) { _particles.update(); // update the particles... _particleCollisionSystem.update(); // collide the particles... + _overlays.update(deltaTime); + // let external parties know we're updating emit simulating(deltaTime); } diff --git a/interface/src/ClipboardScriptingInterface.cpp b/interface/src/ClipboardScriptingInterface.cpp index c669b465ad..d9a8f04d90 100644 --- a/interface/src/ClipboardScriptingInterface.cpp +++ b/interface/src/ClipboardScriptingInterface.cpp @@ -90,5 +90,4 @@ void ClipboardScriptingInterface::nudgeVoxel(float x, float y, float z, float s, s / (float)TREE_SCALE }; Application::getInstance()->nudgeVoxelsByVector(sourceVoxel, nudgeVecInTreeSpace); -} - +} \ No newline at end of file diff --git a/interface/src/VoxelImporter.cpp b/interface/src/VoxelImporter.cpp index 2daafedcbb..9d8b8ad811 100644 --- a/interface/src/VoxelImporter.cpp +++ b/interface/src/VoxelImporter.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -31,6 +32,8 @@ VoxelImporter::VoxelImporter(QWidget* parent) : _task(NULL), _didImport(false) { + LocalVoxelsList::getInstance()->addPersistantTree(IMPORT_TREE_NAME, &_voxelTree); + connect(&_voxelTree, SIGNAL(importProgress(int)), &_importDialog, SLOT(setProgressBarValue(int))); connect(&_importDialog, SIGNAL(canceled()), this, SLOT(cancel())); connect(&_importDialog, SIGNAL(accepted()), this, SLOT(import())); diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index e60165647f..1d5a238622 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -53,7 +53,7 @@ GLubyte identityIndicesRight[] = { 1, 2, 6, 1, 6, 5 }; GLubyte identityIndicesFront[] = { 0, 2, 1, 0, 3, 2 }; GLubyte identityIndicesBack[] = { 4, 5, 6, 4, 6, 7 }; -VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) +VoxelSystem::VoxelSystem(float treeScale, int maxVoxels, VoxelTree* tree) : NodeData(), _treeScale(treeScale), _maxVoxels(maxVoxels), @@ -69,7 +69,7 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) _voxelsInReadArrays = _voxelsInWriteArrays = _voxelsUpdated = 0; _writeRenderFullVBO = true; _readRenderFullVBO = true; - _tree = new VoxelTree(); + _tree = (tree) ? tree : new VoxelTree(); _tree->getRoot()->setVoxelSystem(this); diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index b49891f8d9..49b47c6f25 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -44,7 +44,7 @@ class VoxelSystem : public NodeData, public OctreeElementDeleteHook, public Octr friend class VoxelHideShowThread; public: - VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = DEFAULT_MAX_VOXELS_PER_SYSTEM); + VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = DEFAULT_MAX_VOXELS_PER_SYSTEM, VoxelTree* tree = NULL); ~VoxelSystem(); void setDataSourceUUID(const QUuid& dataSourceUUID) { _dataSourceUUID = dataSourceUUID; } @@ -52,8 +52,8 @@ public: int parseData(const QByteArray& packet); + bool isInitialized() { return _initialized; } virtual void init(); - void simulate(float deltaTime) { } void render(); void changeTree(VoxelTree* newTree); diff --git a/interface/src/ui/ClipboardOverlay.cpp b/interface/src/ui/ClipboardOverlay.cpp deleted file mode 100644 index 7eee318ec9..0000000000 --- a/interface/src/ui/ClipboardOverlay.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// -// ClipboardOverlay.cpp -// hifi -// -// Created by Clément Brisset on 2/20/14. -// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. -// - -// include this before QGLWidget, which includes an earlier version of OpenGL -#include "InterfaceConfig.h" - -#include -#include - -#include "ClipboardOverlay.h" -#include "../Application.h" - -static int lastVoxelCount = 0; - -ClipboardOverlay::ClipboardOverlay() { -} - -ClipboardOverlay::~ClipboardOverlay() { -} - -void ClipboardOverlay::render() { - if (!_visible) { - return; // do nothing if we're not visible - } - - VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem(); - VoxelTree* clipboard = Application::getInstance()->getClipboard(); - if (voxelSystem->getTree() != clipboard) { - voxelSystem->changeTree(clipboard); - } - - glPushMatrix(); - glTranslatef(_position.x, _position.y, _position.z); - glScalef(_size, _size, _size); - - // We only force the redraw when the clipboard content has changed - if (lastVoxelCount != clipboard->getOctreeElementsCount()) { - voxelSystem->forceRedrawEntireTree(); - lastVoxelCount = clipboard->getOctreeElementsCount(); - } - - voxelSystem->render(); - - glPopMatrix(); -} \ No newline at end of file diff --git a/interface/src/ui/ClipboardOverlay.h b/interface/src/ui/ClipboardOverlay.h deleted file mode 100644 index 49daa73c20..0000000000 --- a/interface/src/ui/ClipboardOverlay.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// ClipboardOverlay.h -// hifi -// -// Created by Clément Brisset on 2/20/14. -// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. -// - -#ifndef __interface__ClipboardOverlay__ -#define __interface__ClipboardOverlay__ - -#include "Volume3DOverlay.h" - -class ClipboardOverlay : public Volume3DOverlay { - Q_OBJECT - -public: - ClipboardOverlay(); - ~ClipboardOverlay(); - - virtual void render(); -}; - - -#endif /* defined(__interface__ClipboardOverlay__) */ diff --git a/interface/src/ui/LocalVoxelsOverlay.cpp b/interface/src/ui/LocalVoxelsOverlay.cpp new file mode 100644 index 0000000000..01a885dedc --- /dev/null +++ b/interface/src/ui/LocalVoxelsOverlay.cpp @@ -0,0 +1,84 @@ +// +// LocalVoxelsOverlay.cpp +// hifi +// +// Created by Clément Brisset on 2/28/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// +// +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include +#include + +#include +#include + +#include "LocalVoxelsOverlay.h" + +QMap LocalVoxelsOverlay::_voxelSystemMap; + +LocalVoxelsOverlay::LocalVoxelsOverlay() : + Volume3DOverlay(), + _voxelCount(0) +{ +} + +LocalVoxelsOverlay::~LocalVoxelsOverlay() { + _voxelSystem->changeTree(new VoxelTree()); + _voxelSystem.clear(); + if (_voxelSystemMap.value(_treeName).isNull()) { + _voxelSystemMap.remove(_treeName); + } + _tree.clear(); + LocalVoxelsList::getInstance()->remove(_treeName); +} + +void LocalVoxelsOverlay::update(float deltatime) { + if (!_voxelSystem->isInitialized()) { + _voxelSystem->init(); + } + + if (_voxelCount != _tree->getOctreeElementsCount()) { + _voxelCount = _tree->getOctreeElementsCount(); + _voxelSystem->forceRedrawEntireTree(); + } +} + +void LocalVoxelsOverlay::render() { + if (_visible && _size > 0 && _voxelSystem && _voxelSystem->isInitialized()) { + glPushMatrix(); { + glTranslatef(_position.x, _position.y, _position.z); + glScalef(_size, _size, _size); + _voxelSystem->render(); + } glPopMatrix(); + } +} + +void LocalVoxelsOverlay::setProperties(const QScriptValue &properties) { + Volume3DOverlay::setProperties(properties); + + QScriptValue treeName = properties.property("name"); + // if "end" property was not there, check to see if they included aliases: endPoint, or p2 + if (treeName.isValid()) { + if ((_treeName = treeName.toString()) == DOMAIN_TREE_NAME) { + qDebug() << "addOverlay(): Can't create overlay from domain tree"; + return; + } + _tree = LocalVoxelsList::getInstance()->getTree(_treeName); + if (_tree.isNull()) { + qDebug() << "addOverlay(): Invalid tree name"; + return; + } + + _voxelSystem = _voxelSystemMap[_treeName]; + if (_voxelSystem.isNull()) { + _voxelSystem = StrongVoxelSystemPointer(new VoxelSystem(1, + DEFAULT_MAX_VOXELS_PER_SYSTEM, + _tree.data())); + _voxelSystemMap.insert(_treeName, _voxelSystem); + } + } +} + diff --git a/interface/src/ui/LocalVoxelsOverlay.h b/interface/src/ui/LocalVoxelsOverlay.h new file mode 100644 index 0000000000..c5582a54eb --- /dev/null +++ b/interface/src/ui/LocalVoxelsOverlay.h @@ -0,0 +1,50 @@ +// +// LocalVoxelsOverlay.h +// hifi +// +// Created by Clément Brisset on 2/28/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// +// Scriptable interface for LocalVoxels +// + +#ifndef __hifi__LocalVoxelsOverlay__ +#define __hifi__LocalVoxelsOverlay__ + +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include +#include +#include +#include +#include + +#include + +#include "Volume3DOverlay.h" + +typedef QSharedPointer StrongVoxelSystemPointer; +typedef QWeakPointer WeakVoxelSystemPointer; + +class LocalVoxelsOverlay : public Volume3DOverlay { + Q_OBJECT +public: + LocalVoxelsOverlay(); + ~LocalVoxelsOverlay(); + + virtual void update(float deltatime); + virtual void render(); + + virtual void setProperties(const QScriptValue& properties); + +private: + static QMap _voxelSystemMap; // treeName/voxelSystem + + QString _treeName; + StrongVoxelTreePointer _tree; // so that the tree doesn't get freed + int _voxelCount; + StrongVoxelSystemPointer _voxelSystem; +}; + +#endif /* defined(__hifi__LocalVoxelsOverlay__) */ diff --git a/interface/src/ui/Overlay.h b/interface/src/ui/Overlay.h index df898ec741..ad1084e889 100644 --- a/interface/src/ui/Overlay.h +++ b/interface/src/ui/Overlay.h @@ -28,6 +28,7 @@ public: Overlay(); ~Overlay(); void init(QGLWidget* parent); + virtual void update(float deltatime) {} virtual void render() = 0; // getters diff --git a/interface/src/ui/Overlays.cpp b/interface/src/ui/Overlays.cpp index 84944332f1..0c9415fa73 100644 --- a/interface/src/ui/Overlays.cpp +++ b/interface/src/ui/Overlays.cpp @@ -12,20 +12,41 @@ #include "Overlays.h" #include "Sphere3DOverlay.h" #include "TextOverlay.h" -#include "ClipboardOverlay.h" +#include "LocalVoxelsOverlay.h" -unsigned int Overlays::_nextOverlayID = 1; - -Overlays::Overlays() { +Overlays::Overlays() : _nextOverlayID(1) { } Overlays::~Overlays() { + QMap::iterator it; + for (it = _overlays2D.begin(); it != _overlays2D.end(); ++it) { + delete _overlays2D.take(it.key()); + } + for (it = _overlays3D.begin(); it != _overlays3D.end(); ++it) { + delete _overlays3D.take(it.key()); + } + while (!_overlaysToDelete.isEmpty()) { + delete _overlaysToDelete.takeLast(); + } } void Overlays::init(QGLWidget* parent) { _parent = parent; } +void Overlays::update(float deltatime) { + foreach (Overlay* thisOverlay, _overlays2D) { + thisOverlay->update(deltatime); + } + foreach (Overlay* thisOverlay, _overlays3D) { + thisOverlay->update(deltatime); + } + while (!_overlaysToDelete.isEmpty()) { + delete _overlaysToDelete.takeLast(); + } + +} + void Overlays::render2D() { foreach(Overlay* thisOverlay, _overlays2D) { thisOverlay->render(); @@ -73,8 +94,8 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope thisOverlay->setProperties(properties); created = true; is3D = true; - } else if (type == "clipboard") { - thisOverlay = new ClipboardOverlay(); + } else if (type == "localvoxels") { + thisOverlay = new LocalVoxelsOverlay(); thisOverlay->init(_parent); thisOverlay->setProperties(properties); created = true; @@ -111,11 +132,16 @@ bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { // TODO: make multi-threaded safe void Overlays::deleteOverlay(unsigned int id) { + Overlay* overlayToDelete; if (_overlays2D.contains(id)) { - _overlays2D.erase(_overlays2D.find(id)); + overlayToDelete = _overlays2D.take(id); } else if (_overlays3D.contains(id)) { - _overlays3D.erase(_overlays3D.find(id)); + overlayToDelete = _overlays3D.take(id); + } else { + return; } + + _overlaysToDelete.push_back(overlayToDelete); } unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) { diff --git a/interface/src/ui/Overlays.h b/interface/src/ui/Overlays.h index cfd84fd44b..c28f3ab83b 100644 --- a/interface/src/ui/Overlays.h +++ b/interface/src/ui/Overlays.h @@ -18,6 +18,7 @@ public: Overlays(); ~Overlays(); void init(QGLWidget* parent); + void update(float deltatime); void render3D(); void render2D(); @@ -38,7 +39,8 @@ public slots: private: QMap _overlays2D; QMap _overlays3D; - static unsigned int _nextOverlayID; + QList _overlaysToDelete; + unsigned int _nextOverlayID; QGLWidget* _parent; }; diff --git a/libraries/script-engine/src/LocalVoxels.cpp b/libraries/script-engine/src/LocalVoxels.cpp new file mode 100644 index 0000000000..075dfb9e9a --- /dev/null +++ b/libraries/script-engine/src/LocalVoxels.cpp @@ -0,0 +1,153 @@ +// +// LocalVoxels.cpp +// hifi +// +// Created by Clément Brisset on 2/24/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#include "LocalVoxels.h" + +LocalVoxels::LocalVoxels(QString name) : + QObject(NULL), + _name(name), + _tree(new VoxelTree(true)) +{ + LocalVoxelsList::getInstance()->insert(_name, _tree); +} + +LocalVoxels::~LocalVoxels() { + _tree.clear(); + LocalVoxelsList::getInstance()->remove(_name); +} + +VoxelDetail LocalVoxels::getVoxelAt(float x, float y, float z, float scale) { + // setup a VoxelDetail struct with the data + VoxelDetail result = {0,0,0,0,0,0,0}; + if (_tree) { + _tree->lockForRead(); + + VoxelTreeElement* voxel = static_cast(_tree->getOctreeElementAt(x / (float)TREE_SCALE, + y / (float)TREE_SCALE, + z / (float)TREE_SCALE, + scale / (float)TREE_SCALE)); + _tree->unlock(); + if (voxel) { + // Note: these need to be in voxel space because the VoxelDetail -> js converter will upscale + result.x = voxel->getCorner().x; + result.y = voxel->getCorner().y; + result.z = voxel->getCorner().z; + result.s = voxel->getScale(); + result.red = voxel->getColor()[RED_INDEX]; + result.green = voxel->getColor()[GREEN_INDEX]; + result.blue = voxel->getColor()[BLUE_INDEX]; + } + } + return result; +} + +void LocalVoxels::setVoxelNonDestructive(float x, float y, float z, float scale, + uchar red, uchar green, uchar blue) { + if (_name == DOMAIN_TREE_NAME) { + qDebug() << "LocalVoxels::setVoxelNonDestructive(): Please use the \"Voxels\" interface to modify the domain tree."; + return; + } + if (_tree ) { + _tree->createVoxel(x / (float)TREE_SCALE, + y / (float)TREE_SCALE, + z / (float)TREE_SCALE, + scale / (float)TREE_SCALE, + red, green, blue, false); + } +} + +void LocalVoxels::setVoxel(float x, float y, float z, float scale, + uchar red, uchar green, uchar blue) { + if (_name == DOMAIN_TREE_NAME) { + qDebug() << "LocalVoxels::setVoxel(): Please use the \"Voxels\" interface to modify the domain tree."; + return; + } + if (_tree ) { + _tree->createVoxel(x / (float)TREE_SCALE, + y / (float)TREE_SCALE, + z / (float)TREE_SCALE, + scale / (float)TREE_SCALE, + red, green, blue, true); + } +} + +void LocalVoxels::eraseVoxel(float x, float y, float z, float scale) { + if (_name == DOMAIN_TREE_NAME) { + qDebug() << "LocalVoxels::eraseVoxel(): Please use the \"Voxels\" interface to modify the domain tree."; + return; + } + if (_tree ) { + _tree->deleteVoxelAt(x / (float)TREE_SCALE, + y / (float)TREE_SCALE, + z / (float)TREE_SCALE, + scale / (float)TREE_SCALE); + } +} + +void LocalVoxels::copyTo(float x, float y, float z, float scale, const QString destination) { + if (destination == DOMAIN_TREE_NAME) { + qDebug() << "LocalVoxels::copyTo(): Please use the \"Voxels\" interface to modify the domain tree."; + return; + } + StrongVoxelTreePointer destinationTree = LocalVoxelsList::getInstance()->getTree(destination); + VoxelTreeElement* destinationNode = destinationTree->getVoxelAt(x / (float)TREE_SCALE, + y / (float)TREE_SCALE, + z / (float)TREE_SCALE, + scale / (float)TREE_SCALE); + destinationTree->copyFromTreeIntoSubTree(_tree.data(), destinationNode); +} + +void LocalVoxels::pasteFrom(float x, float y, float z, float scale, const QString source) { + if (_name == DOMAIN_TREE_NAME) { + qDebug() << "LocalVoxels::pasteFrom(): Please use the \"Voxels\" interface to modify the domain tree."; + return; + } + StrongVoxelTreePointer sourceTree = LocalVoxelsList::getInstance()->getTree(source); + VoxelTreeElement* sourceNode = _tree->getVoxelAt(x / (float)TREE_SCALE, + y / (float)TREE_SCALE, + z / (float)TREE_SCALE, + scale / (float)TREE_SCALE); + _tree->copySubTreeIntoNewTree(sourceNode, sourceTree.data(), true); +} + +RayToVoxelIntersectionResult LocalVoxels::findRayIntersection(const PickRay& ray) { + RayToVoxelIntersectionResult result; + if (_tree) { + OctreeElement* element; + result.intersects = _tree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face); + if (result.intersects) { + VoxelTreeElement* voxel = (VoxelTreeElement*)element; + result.voxel.x = voxel->getCorner().x; + result.voxel.y = voxel->getCorner().y; + result.voxel.z = voxel->getCorner().z; + result.voxel.s = voxel->getScale(); + result.voxel.red = voxel->getColor()[0]; + result.voxel.green = voxel->getColor()[1]; + result.voxel.blue = voxel->getColor()[2]; + result.intersection = ray.origin + (ray.direction * result.distance); + } + } + return result; +} + +glm::vec3 LocalVoxels::getFaceVector(const QString& face) { + if (face == "MIN_X_FACE") { + return glm::vec3(-1, 0, 0); + } else if (face == "MAX_X_FACE") { + return glm::vec3(1, 0, 0); + } else if (face == "MIN_Y_FACE") { + return glm::vec3(0, -1, 0); + } else if (face == "MAX_Y_FACE") { + return glm::vec3(0, 1, 0); + } else if (face == "MIN_Z_FACE") { + return glm::vec3(0, 0, -1); + } else if (face == "MAX_Z_FACE") { + return glm::vec3(0, 0, 1); + } + return glm::vec3(0, 0, 0); //error case +} diff --git a/libraries/script-engine/src/LocalVoxels.h b/libraries/script-engine/src/LocalVoxels.h new file mode 100644 index 0000000000..c64379ab27 --- /dev/null +++ b/libraries/script-engine/src/LocalVoxels.h @@ -0,0 +1,90 @@ +// +// LocalVoxels.h +// hifi +// +// Created by Clément Brisset on 2/24/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __hifi__LocalVoxels__ +#define __hifi__LocalVoxels__ + +#include + +#include +#include + + +/// object allowing JS scripters to use their own local trees +class LocalVoxels : public QObject { + Q_OBJECT + +public: + LocalVoxels(QString name); + ~LocalVoxels(); + + /// checks the local voxel tree for a voxel at the specified location and scale + /// \param x the x-coordinate of the voxel (in meter units) + /// \param y the y-coordinate of the voxel (in meter units) + /// \param z the z-coordinate of the voxel (in meter units) + /// \param scale the scale of the voxel (in meter units) + Q_INVOKABLE VoxelDetail getVoxelAt(float x, float y, float z, float scale); + + /// creates a non destructive voxel in the local tree + /// \param x the x-coordinate of the voxel (in meter units) + /// \param y the y-coordinate of the voxel (in meter units) + /// \param z the z-coordinate of the voxel (in meter units) + /// \param scale the scale of the voxel (in meter units) + /// \param red the R value for RGB color of voxel + /// \param green the G value for RGB color of voxel + /// \param blue the B value for RGB color of voxel + Q_INVOKABLE void setVoxelNonDestructive(float x, float y, float z, float scale, uchar red, uchar green, uchar blue); + + /// creates a voxel in the local tree + /// \param x the x-coordinate of the voxel (in meter units) + /// \param y the y-coordinate of the voxel (in meter units) + /// \param z the z-coordinate of the voxel (in meter units) + /// \param scale the scale of the voxel (in meter units) + /// \param red the R value for RGB color of voxel + /// \param green the G value for RGB color of voxel + /// \param blue the B value for RGB color of voxel + Q_INVOKABLE void setVoxel(float x, float y, float z, float scale, uchar red, uchar green, uchar blue); + + /// erase the voxel and its children at the given coordinate + /// \param x the x-coordinate of the voxel (in meter units) + /// \param y the y-coordinate of the voxel (in meter units) + /// \param z the z-coordinate of the voxel (in meter units) + /// \param scale the scale of the voxel (in meter units) + Q_INVOKABLE void eraseVoxel(float x, float y, float z, float scale); + + /// copy the given subtree onto destination's root node + /// \param x the x-coordinate of the subtree (in meter units) + /// \param y the y-coordinate of the subtree (in meter units) + /// \param z the z-coordinate of the subtree (in meter units) + /// \param scale the scale of the subtree (in meter units) + /// \param destination LocalVoxels' destination tree + Q_INVOKABLE void copyTo(float x, float y, float z, float scale, const QString destination); + + ///copy source in the given subtree + /// \param x the x-coordinate of the subtree (in meter units) + /// \param y the y-coordinate of the subtree (in meter units) + /// \param z the z-coordinate of the subtree (in meter units) + /// \param scale the scale of the subtree (in meter units) + /// \param source LocalVoxels' source tree + Q_INVOKABLE void pasteFrom(float x, float y, float z, float scale, const QString source); + + /// If the scripting context has visible voxels, this will determine a ray intersection + Q_INVOKABLE RayToVoxelIntersectionResult findRayIntersection(const PickRay& ray); + + /// returns a voxel space axis aligned vector for the face, useful in doing voxel math + Q_INVOKABLE glm::vec3 getFaceVector(const QString& face); + +private: + QString _name; + StrongVoxelTreePointer _tree; +}; + + + + +#endif /* defined(__hifi__LocalVoxels__) */ diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index e08317f8b4..718e5331dd 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -25,6 +25,7 @@ #include #include "MenuItemProperties.h" +#include "LocalVoxels.h" #include "ScriptEngine.h" const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000; @@ -118,6 +119,7 @@ bool ScriptEngine::setScriptContents(const QString& scriptContents) { } Q_SCRIPT_DECLARE_QMETAOBJECT(AudioInjectorOptions, QObject*) +Q_SCRIPT_DECLARE_QMETAOBJECT(LocalVoxels, QString) void ScriptEngine::init() { if (_isInitialized) { @@ -146,6 +148,9 @@ void ScriptEngine::init() { QScriptValue injectionOptionValue = _engine.scriptValueFromQMetaObject(); _engine.globalObject().setProperty("AudioInjectionOptions", injectionOptionValue); + + QScriptValue localVoxelsValue = _engine.scriptValueFromQMetaObject(); + _engine.globalObject().setProperty("LocalVoxels", localVoxelsValue); registerGlobalObject("Script", this); registerGlobalObject("Audio", &_audioScriptingInterface); diff --git a/libraries/voxels/src/LocalVoxelsList.cpp b/libraries/voxels/src/LocalVoxelsList.cpp new file mode 100644 index 0000000000..e8f5a09d6d --- /dev/null +++ b/libraries/voxels/src/LocalVoxelsList.cpp @@ -0,0 +1,64 @@ +// +// LocalVoxelsList.cpp +// hifi +// +// Created by Clément Brisset on 2/24/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#include "LocalVoxelsList.h" + +static void doNothing(VoxelTree* t) { + // do nothing +} + +LocalVoxelsList* LocalVoxelsList::_instance = NULL; + +LocalVoxelsList* LocalVoxelsList::getInstance() { + if (!_instance) { + _instance = new LocalVoxelsList(); + } + + return _instance; +} + +LocalVoxelsList::LocalVoxelsList() { +} + +LocalVoxelsList::~LocalVoxelsList() { + _instance = NULL; +} + +StrongVoxelTreePointer LocalVoxelsList::getTree(QString treeName) { + return _trees.value(treeName); +} + +void LocalVoxelsList::addPersistantTree(QString treeName, VoxelTree* tree) { + StrongVoxelTreePointer treePtr(tree, doNothing); + _persistantTrees.push_back(treePtr); + _trees.insert(treeName, treePtr); + qDebug() << "[DEBUG] LocalVoxelsList : added persistant tree (" << treeName << ")"; +} + +void LocalVoxelsList::insert(QString treeName, StrongVoxelTreePointer& tree) { + // If the key don't already exist or the value is null + if (!_trees.contains(treeName) || !_trees.value(treeName)) { + _trees.insert(treeName, tree); + qDebug() << "[DEBUG] LocalVoxelsList : added local tree (" << treeName << ")"; + } else { + // if not we replace the tree created by the user with the existing one + tree = _trees.value(treeName); + qDebug() << "[DEBUG] LocalVoxelsList : local tree already exist (" << treeName << ")"; + } +} + +void LocalVoxelsList::remove(QString treeName) { + // if the tree is not used anymore (no strong pointer) + if (!_trees.value(treeName)) { + // then remove it from the list + qDebug() << "[DEBUG] LocalVoxelsList : removed unused tree (" << treeName << ")"; + _trees.remove(treeName); + } else { + qDebug() << "[DEBUG] LocalVoxelsList : tree still in use (" << treeName << ")"; + } +} \ No newline at end of file diff --git a/libraries/voxels/src/LocalVoxelsList.h b/libraries/voxels/src/LocalVoxelsList.h new file mode 100644 index 0000000000..e4b4decf8e --- /dev/null +++ b/libraries/voxels/src/LocalVoxelsList.h @@ -0,0 +1,59 @@ +// +// LocalVoxelsList.h +// hifi +// +// Created by Clément Brisset on 2/24/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __hifi__LocalVoxelsList__ +#define __hifi__LocalVoxelsList__ + +#include +#include +#include +#include + +#include "VoxelTree.h" + +typedef QSharedPointer StrongVoxelTreePointer; +typedef QWeakPointer WeakVoxelTreePointer; + +static const QString DOMAIN_TREE_NAME = "domain"; +static const QString CLIPBOARD_TREE_NAME = "clipboard"; +static const QString IMPORT_TREE_NAME = "import"; + +/// Handles the the storage and cleanup of local named trees used by JS +class LocalVoxelsList { +public: + static LocalVoxelsList* getInstance(); + ~LocalVoxelsList(); + + /// Lookup up a tree in the QHash and return a strong pointer to it. + /// \param treeName name of the tree to look up + StrongVoxelTreePointer getTree(QString treeName); + + /// Add a that will stay in the list until destruction of the instance and won't be destroyed then either. + /// \param treeName name to give to the tree in the list + /// \param tree standard pointer to the tree + void addPersistantTree(QString treeName, VoxelTree* tree); + + /// insert a local tree in the list + /// \param treeName name to give to the tree in the list + /// \param tree strong pointer to the tree + void insert(QString treeName, StrongVoxelTreePointer& tree); + + /// remove a tree from the list if it's not being used anymore + /// \param treeName name of the tree to remove + void remove(QString treeName); + +private: + static LocalVoxelsList* _instance; + LocalVoxelsList(); + + QHash _trees; + + QList _persistantTrees; +}; + +#endif /* defined(__hifi__LocalVoxelsList__) */