diff --git a/examples/edit.js b/examples/edit.js index 7983a49575..6055400289 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -1,4 +1,3 @@ - // newEditEntities.js // examples // @@ -139,6 +138,7 @@ var toolBar = (function () { newTextButton, newWebButton, newZoneButton, + newPolyVoxButton, browseMarketplaceButton; function initialize() { @@ -224,6 +224,15 @@ var toolBar = (function () { visible: false }); + newPolyVoxButton = toolBar.addTool({ + imageURL: toolIconUrl + "polyvox.svg", + subImage: { x: 0, y: 0, width: 256, height: 256 }, + width: toolWidth, + height: toolHeight, + alpha: 0.9, + visible: false + }); + that.setActive(false); } @@ -266,6 +275,7 @@ var toolBar = (function () { toolBar.showTool(newTextButton, doShow); toolBar.showTool(newWebButton, doShow); toolBar.showTool(newZoneButton, doShow); + toolBar.showTool(newPolyVoxButton, doShow); }; var RESIZE_INTERVAL = 50; @@ -468,6 +478,24 @@ var toolBar = (function () { return true; } + if (newPolyVoxButton === toolBar.clicked(clickedOverlay)) { + var position = getPositionToCreateEntity(); + + if (position.x > 0 && position.y > 0 && position.z > 0) { + placingEntityID = Entities.addEntity({ + type: "PolyVox", + position: grid.snapToSurface(grid.snapToGrid(position, false, DEFAULT_DIMENSIONS), + DEFAULT_DIMENSIONS), + dimensions: { x: 10, y: 10, z: 10 }, + voxelVolumeSize: {x:16, y:16, z:16}, + voxelSurfaceStyle: 1 + }); + } else { + print("Can't create PolyVox: would be out of bounds."); + } + return true; + } + return false; }; @@ -920,7 +948,7 @@ function selectAllEtitiesInCurrentSelectionBox(keepIfTouching) { var boundingBoxCorner = Vec3.subtract(selectionManager.worldPosition, Vec3.multiply(selectionManager.worldDimensions, 0.5)); var entities = Entities.findEntitiesInBox(boundingBoxCorner, selectionManager.worldDimensions); - + if (!keepIfTouching) { var isValid; if (selectionManager.localPosition === null) { @@ -992,7 +1020,7 @@ function handeMenuEvent(menuItem) { } } } else if (menuItem == "Import Entities" || menuItem == "Import Entities from URL") { - + var importURL; if (menuItem == "Import Entities") { importURL = Window.browse("Select models to import", "", "*.json"); diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 7c214624c2..d9cad0feff 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -21,11 +21,11 @@ els[i].setAttribute('disabled', 'disabled'); } } - + function showElements(els, show) { for (var i = 0; i < els.length; i++) { els[i].style.display = (show) ? 'block' : 'none'; - } + } } function createEmitCheckedPropertyUpdateFunction(propertyName) { @@ -344,7 +344,7 @@ var elZoneAtmosphereCenterY = document.getElementById("property-zone-atmosphere-center-y"); var elZoneAtmosphereCenterZ = document.getElementById("property-zone-atmosphere-center-z"); var elCenterAtmosphereToZone = document.getElementById("center-atmosphere-in-zone"); - + var elZoneAtmosphereInnerRadius = document.getElementById("property-zone-atmosphere-inner-radius"); var elZoneAtmosphereOuterRadius = document.getElementById("property-zone-atmosphere-outer-radius"); var elZoneAtmosphereMieScattering = document.getElementById("property-zone-atmosphere-mie-scattering"); @@ -354,6 +354,12 @@ var elZoneAtmosphereScatteringWavelengthsZ = document.getElementById("property-zone-atmosphere-scattering-wavelengths-z"); var elZoneAtmosphereHasStars = document.getElementById("property-zone-atmosphere-has-stars"); + var elPolyVoxSelections = document.querySelectorAll(".poly-vox-section"); + var elVoxelVolumeSizeX = document.getElementById("property-voxel-volume-size-x"); + var elVoxelVolumeSizeY = document.getElementById("property-voxel-volume-size-y"); + var elVoxelVolumeSizeZ = document.getElementById("property-voxel-volume-size-z"); + var elVoxelSurfaceStyle = document.getElementById("property-voxel-surface-style"); + if (window.EventBridge !== undefined) { EventBridge.scriptEventReceived.connect(function(data) { @@ -571,7 +577,7 @@ elZoneAtmosphereScatteringWavelengthsY.value = properties.atmosphere.scatteringWavelengths.y; elZoneAtmosphereScatteringWavelengthsZ.value = properties.atmosphere.scatteringWavelengths.z; elZoneAtmosphereHasStars.checked = properties.atmosphere.hasStars; - + showElements(document.getElementsByClassName('skybox-section'), elZoneBackgroundMode.value == 'skybox'); showElements(document.getElementsByClassName('atmosphere-section'), elZoneBackgroundMode.value == 'atmosphere'); } else if (properties.type == "ParticleEffect") { @@ -588,6 +594,11 @@ elParticleEmitStrength.value = properties.emitStrength.toFixed(2); elParticleLocalGravity.value = properties.localGravity.toFixed(2); elParticleRadius.value = properties.particleRadius.toFixed(3); + } else if (properties.type == "PolyVox") { + elVoxelVolumeSizeX.value = properties.voxelVolumeSize.x.toFixed(2); + elVoxelVolumeSizeY.value = properties.voxelVolumeSize.y.toFixed(2); + elVoxelVolumeSizeZ.value = properties.voxelVolumeSize.z.toFixed(2); + elVoxelSurfaceStyle.value = properties.voxelSurfaceStyle; } if (selected) { @@ -704,7 +715,7 @@ elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff')); elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl')); - + elParticleMaxParticles.addEventListener('change', createEmitNumberPropertyUpdateFunction('maxParticles')); elParticleLifeSpan.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifespan')); elParticleEmitRate.addEventListener('change', createEmitNumberPropertyUpdateFunction('emitRate')); @@ -810,7 +821,7 @@ emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'skybox'); } }); - + elZoneSkyboxURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('skybox','url')); var zoneAtmosphereCenterChangeFunction = createEmitGroupVec3PropertyUpdateFunction( @@ -818,8 +829,8 @@ elZoneAtmosphereCenterX.addEventListener('change', zoneAtmosphereCenterChangeFunction); elZoneAtmosphereCenterY.addEventListener('change', zoneAtmosphereCenterChangeFunction); elZoneAtmosphereCenterZ.addEventListener('change', zoneAtmosphereCenterChangeFunction); - - + + elZoneAtmosphereInnerRadius.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('atmosphere','innerRadius')); elZoneAtmosphereOuterRadius.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('atmosphere','outerRadius')); elZoneAtmosphereMieScattering.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('atmosphere','mieScattering')); @@ -832,6 +843,13 @@ elZoneAtmosphereScatteringWavelengthsZ.addEventListener('change', zoneAtmosphereScatterWavelengthsChangeFunction); elZoneAtmosphereHasStars.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('atmosphere','hasStars')); + var voxelVolumeSizeChangeFunction = createEmitVec3PropertyUpdateFunction( + 'voxelVolumeSize', elVoxelVolumeSizeX, elVoxelVolumeSizeY, elVoxelVolumeSizeZ); + elVoxelVolumeSizeX.addEventListener('change', voxelVolumeSizeChangeFunction); + elVoxelVolumeSizeY.addEventListener('change', voxelVolumeSizeChangeFunction); + elVoxelVolumeSizeZ.addEventListener('change', voxelVolumeSizeChangeFunction); + elVoxelSurfaceStyle.addEventListener('change', createEmitTextPropertyUpdateFunction('voxelSurfaceStyle')); + elMoveSelectionToGrid.addEventListener("click", function() { EventBridge.emitWebEvent(JSON.stringify({ @@ -973,6 +991,24 @@ +
+
Voxel Volume Size
+
+
X
+
Y
+
Z
+
+ +
Surface Extractor
+
+ +
+
+
Rotation
@@ -1107,19 +1143,19 @@
- +
Max Particles
-
+
Particle Life Span
-
+
Particle Emission Rate
@@ -1152,7 +1188,7 @@
- +
Model URL
@@ -1167,7 +1203,7 @@ - +
@@ -1346,7 +1382,7 @@
- +
Stage Day
diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js index f9f8d57a51..1b1a6a9c12 100644 --- a/examples/libraries/entityPropertyDialogBox.js +++ b/examples/libraries/entityPropertyDialogBox.js @@ -98,6 +98,21 @@ EntityPropertyDialogBox = (function () { index++; } + if (properties.type == "PolyVox") { + array.push({ label: "Voxel Space Size:", type: "header" }); + index++; + + array.push({ label: "X:", value: properties.voxelVolumeSize.x.toFixed(decimals) }); + index++; + array.push({ label: "Y:", value: properties.voxelVolumeSize.y.toFixed(decimals) }); + index++; + array.push({ label: "Z:", value: properties.voxelVolumeSize.z.toFixed(decimals) }); + index++; + + array.push({ label: "Surface Extractor", value: properties.voxelSurfaceStyle }); + index++; + } + array.push({ label: "Position:", type: "header" }); index++; array.push({ label: "X:", value: properties.position.x.toFixed(decimals) }); @@ -333,6 +348,16 @@ EntityPropertyDialogBox = (function () { properties.backgroundColor.blue = array[index++].value; } + if (properties.type == "PolyVox") { + properties.shapeType = array[index++].value; + + index++; // skip header + properties.voxelVolumeSize.x = array[index++].value; + properties.voxelVolumeSize.y = array[index++].value; + properties.voxelVolumeSize.z = array[index++].value; + properties.voxelSurfaceStyle = array[index++].value; + } + index++; // skip header properties.position.x = array[index++].value; properties.position.y = array[index++].value; diff --git a/examples/voxels.js b/examples/voxels.js index e274f1c4cc..799af04bef 100644 --- a/examples/voxels.js +++ b/examples/voxels.js @@ -1,5 +1,5 @@ - var controlHeld = false; +var shiftHeld = false; function mousePressEvent(event) { @@ -15,9 +15,11 @@ function mousePressEvent(event) { for (var i = 0; i < ids.length; i++) { var id = ids[i]; if (controlHeld) { - Entities.setVoxelSphere(id, intersection.intersection, 1.2, 0); + Entities.setVoxelSphere(id, intersection.intersection, 1.0, 0); + } else if (shiftHeld) { + Entities.setAllVoxels(id, 255); } else { - Entities.setVoxelSphere(id, intersection.intersection, 1.2, 255); + Entities.setVoxelSphere(id, intersection.intersection, 1.0, 255); } } } @@ -28,6 +30,9 @@ function keyPressEvent(event) { if (event.text == "CONTROL") { controlHeld = true; } + if (event.text == "SHIFT") { + shiftHeld = true; + } } @@ -35,6 +40,9 @@ function keyReleaseEvent(event) { if (event.text == "CONTROL") { controlHeld = false; } + if (event.text == "SHIFT") { + shiftHeld = false; + } } diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index d3f7a573b8..e51522bb7f 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -507,7 +507,6 @@ QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const { QPoint rv; auto canvasSize = qApp->getCanvasSize(); if (qApp->isHMDMode()) { - float t; glm::vec2 polar = getPolarCoordinates(*palm); glm::vec2 point = sphericalToScreen(-polar); rv.rx() = point.x; diff --git a/interface/src/ui/AvatarAppearanceDialog.cpp b/interface/src/ui/AvatarAppearanceDialog.cpp index ceaaf140c4..3ab99c141d 100644 --- a/interface/src/ui/AvatarAppearanceDialog.cpp +++ b/interface/src/ui/AvatarAppearanceDialog.cpp @@ -82,7 +82,10 @@ void AvatarAppearanceDialog::setUseFullAvatar(bool useFullAvatar) { ui.useFullAvatar->setChecked(_useFullAvatar); ui.useSeparateBodyAndHead->setChecked(!_useFullAvatar); - DependencyManager::get()->getPreferencesDialog()->avatarDescriptionChanged(); + QPointer prefs = DependencyManager::get()->getPreferencesDialog(); + if (prefs) { // Preferences dialog may have been closed + prefs->avatarDescriptionChanged(); + } } void AvatarAppearanceDialog::headURLChanged(const QString& newValue, const QString& modelName) { @@ -106,7 +109,10 @@ void AvatarAppearanceDialog::fullAvatarURLChanged(const QString& newValue, const void AvatarAppearanceDialog::accept() { saveAvatarAppearance(); - DependencyManager::get()->getPreferencesDialog()->avatarDescriptionChanged(); + QPointer prefs = DependencyManager::get()->getPreferencesDialog(); + if (prefs) { // Preferences dialog may have been closed + prefs->avatarDescriptionChanged(); + } close(); delete _marketplaceWindow; diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp index 8981d149ae..45abe36fdb 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -29,10 +30,26 @@ void RenderableLineEntityItem::render(RenderArgs* args) { glm::vec3 p1 = ENTITY_ITEM_ZERO_VEC3; glm::vec3 p2 = getDimensions(); glm::vec4 lineColor(toGlm(getXColor()), getLocalRenderAlpha()); + Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; batch.setModelTransform(getTransformToCenter()); - DependencyManager::get()->renderLine(batch, p1, p2, lineColor, lineColor); + + glLineWidth(getLineWidth()); + auto geometryCache = DependencyManager::get(); + if (_lineVerticesID == GeometryCache::UNKNOWN_ID) { + _lineVerticesID = geometryCache ->allocateID(); + } + + //TODO: Figure out clean , efficient way to do relative line positioning. For now we'll just use absolute positioning. + //glTranslatef(position.x, position.y, position.z); + //glm::vec3 axis = glm::axis(rotation); + //glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + if (_pointsChanged) { + geometryCache->updateVertices(_lineVerticesID, getLinePoints(), lineColor); + _pointsChanged = false; + } + geometryCache->renderVertices(gpu::LINE_STRIP, _lineVerticesID); RenderableDebugableEntityItem::render(this, args); }; diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.h b/libraries/entities-renderer/src/RenderableLineEntityItem.h index eb23b3ee48..8a25196ec5 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.h @@ -15,17 +15,23 @@ #include #include "RenderableDebugableEntityItem.h" #include "RenderableEntityItem.h" +#include class RenderableLineEntityItem : public LineEntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); RenderableLineEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : - LineEntityItem(entityItemID, properties) { } + LineEntityItem(entityItemID, properties), + _lineVerticesID(GeometryCache::UNKNOWN_ID) + { } virtual void render(RenderArgs* args); SIMPLE_RENDERABLE() + +protected: + int _lineVerticesID; }; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 014a9dd995..4d85e73bcc 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -33,22 +33,63 @@ #include "EntityTreeRenderer.h" #include "RenderablePolyVoxEntityItem.h" - EntityItemPointer RenderablePolyVoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { return EntityItemPointer(new RenderablePolyVoxEntityItem(entityID, properties)); } +RenderablePolyVoxEntityItem::RenderablePolyVoxEntityItem(const EntityItemID& entityItemID, + const EntityItemProperties& properties) : + PolyVoxEntityItem(entityItemID, properties) { + + model::Mesh* mesh = new model::Mesh(); + model::MeshPointer meshPtr(mesh); + _modelGeometry.setMesh(meshPtr); + + setVoxelVolumeSize(_voxelVolumeSize); +} + RenderablePolyVoxEntityItem::~RenderablePolyVoxEntityItem() { delete _volData; } -void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { +bool inUserBounds(const PolyVox::SimpleVolume* vol, PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle, + int x, int y, int z) { + // x, y, z are in user voxel-coords, not adjusted-for-edge voxel-coords. + switch (surfaceStyle) { + case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: + case PolyVoxEntityItem::SURFACE_CUBIC: + if (x < 0 || y < 0 || z < 0 || + x >= vol->getWidth() || y >= vol->getHeight() || z >= vol->getDepth()) { + return false; + } + return true; + case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: + if (x < 0 || y < 0 || z < 0 || + x >= vol->getWidth() - 2 || y >= vol->getHeight() - 2 || z >= vol->getDepth() - 2) { + return false; + } + return true; + } + + return false; +} + + +bool inBounds(const PolyVox::SimpleVolume* vol, int x, int y, int z) { + // x, y, z are in polyvox volume coords + return !(x < 0 || y < 0 || z < 0 || x >= vol->getWidth() || y >= vol->getHeight() || z >= vol->getDepth()); +} + + +void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { if (_volData && voxelVolumeSize == _voxelVolumeSize) { return; } - qDebug() << "resetting voxel-space size"; + #ifdef WANT_DEBUG + qDebug() << "resetting voxel-space size" << voxelVolumeSize.x << voxelVolumeSize.y << voxelVolumeSize.z; + #endif PolyVoxEntityItem::setVoxelVolumeSize(voxelVolumeSize); @@ -56,14 +97,69 @@ void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) delete _volData; } - PolyVox::Vector3DInt32 lowCorner(0, 0, 0); - PolyVox::Vector3DInt32 highCorner(_voxelVolumeSize[0] - 1, // -1 because these corners are inclusive - _voxelVolumeSize[1] - 1, - _voxelVolumeSize[2] - 1); + _onCount = 0; - _volData = new PolyVox::SimpleVolume(PolyVox::Region(lowCorner, highCorner)); + if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC) { + // with _EDGED_ we maintain an extra box of voxels around those that the user asked for. This + // changes how the surface extractor acts -- mainly it becomes impossible to have holes in the + // generated mesh. The non _EDGED_ modes will leave holes in the mesh at the edges of the + // voxel space. + PolyVox::Vector3DInt32 lowCorner(0, 0, 0); + PolyVox::Vector3DInt32 highCorner(_voxelVolumeSize.x + 1, // -1 + 2 because these corners are inclusive + _voxelVolumeSize.y + 1, + _voxelVolumeSize.z + 1); + _volData = new PolyVox::SimpleVolume(PolyVox::Region(lowCorner, highCorner)); + } else { + PolyVox::Vector3DInt32 lowCorner(0, 0, 0); + PolyVox::Vector3DInt32 highCorner(_voxelVolumeSize.x - 1, // -1 because these corners are inclusive + _voxelVolumeSize.y - 1, + _voxelVolumeSize.z - 1); + _volData = new PolyVox::SimpleVolume(PolyVox::Region(lowCorner, highCorner)); + } + + // having the "outside of voxel-space" value be 255 has helped me notice some problems. + _volData->setBorderValue(255); + + #ifdef WANT_DEBUG + qDebug() << " new size is" << _volData->getWidth() << _volData->getHeight() << _volData->getDepth(); + #endif + + // I'm not sure this is needed... the docs say that each element is initialized with its default + // constructor. I'll leave it here for now. + for (int z = 0; z < _volData->getDepth(); z++) { + for (int y = 0; y < _volData->getHeight(); y++) { + for (int x = 0; x < _volData->getWidth(); x++) { + _volData->setVoxelAt(x, y, z, 0); + } + } + } + + // It's okay to decompress the old data here, because the data includes its original dimensions along + // with the voxel data, and writing voxels outside the bounds of the new space is harmless. This allows + // adjusting of the voxel-space size without overly mangling the shape. Shrinking the space and then + // restoring the previous size (without any edits in between) will put the original shape back. + decompressVolumeData(); } +void RenderablePolyVoxEntityItem::setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) { + if (voxelSurfaceStyle == _voxelSurfaceStyle) { + return; + } + + // if we are switching to or from "edged" we need to force a resize of _volData. + if (voxelSurfaceStyle == SURFACE_EDGED_CUBIC || + _voxelSurfaceStyle == SURFACE_EDGED_CUBIC) { + if (_volData) { + delete _volData; + } + _volData = nullptr; + PolyVoxEntityItem::setVoxelSurfaceStyle(voxelSurfaceStyle); + setVoxelVolumeSize(_voxelVolumeSize); + } else { + PolyVoxEntityItem::setVoxelSurfaceStyle(voxelSurfaceStyle); + } + _needsModelReload = true; +} void RenderablePolyVoxEntityItem::setVoxelData(QByteArray voxelData) { if (voxelData == _voxelData) { @@ -73,57 +169,138 @@ void RenderablePolyVoxEntityItem::setVoxelData(QByteArray voxelData) { decompressVolumeData(); } +glm::vec3 RenderablePolyVoxEntityItem::getSurfacePositionAdjustment() const { + glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units + switch (_voxelSurfaceStyle) { + case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: + return scale / 2.0f; + case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: + return scale / -2.0f; + case PolyVoxEntityItem::SURFACE_CUBIC: + return scale / 2.0f; + } + return glm::vec3(0.0f, 0.0f, 0.0f); +} + +glm::mat4 RenderablePolyVoxEntityItem::voxelToLocalMatrix() const { + glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units + glm::vec3 center = getCenterPosition(); + glm::vec3 position = getPosition(); + glm::vec3 positionToCenter = center - position; + positionToCenter -= getDimensions() * glm::vec3(0.5f, 0.5f, 0.5f) - getSurfacePositionAdjustment(); + glm::mat4 centerToCorner = glm::translate(glm::mat4(), positionToCenter); + glm::mat4 scaled = glm::scale(centerToCorner, scale); + return scaled; +} glm::mat4 RenderablePolyVoxEntityItem::voxelToWorldMatrix() const { - glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units - glm::mat4 scaled = glm::scale(glm::mat4(), scale); - glm::mat4 centerToCorner = glm::translate(scaled, _voxelVolumeSize / -2.0f); glm::mat4 rotation = glm::mat4_cast(getRotation()); - glm::mat4 translation = glm::translate(getCenterPosition()); - return translation * rotation * centerToCorner; + glm::mat4 translation = glm::translate(getPosition()); + return translation * rotation * voxelToLocalMatrix(); } glm::mat4 RenderablePolyVoxEntityItem::worldToVoxelMatrix() const { glm::mat4 worldToModelMatrix = glm::inverse(voxelToWorldMatrix()); return worldToModelMatrix; +} +uint8_t RenderablePolyVoxEntityItem::getVoxel(int x, int y, int z) { + assert(_volData); + if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) { + return 0; + } + + // if _voxelSurfaceStyle is SURFACE_EDGED_CUBIC, we maintain an extra layer of + // voxels all around the requested voxel space. Having the empty voxels around + // the edges changes how the surface extractor behaves. + + if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC) { + return _volData->getVoxelAt(x + 1, y + 1, z + 1); + } + return _volData->getVoxelAt(x, y, z); +} + +void RenderablePolyVoxEntityItem::setVoxel(int x, int y, int z, uint8_t toValue) { + assert(_volData); + if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) { + return; + } + + if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC) { + _volData->setVoxelAt(x + 1, y + 1, z + 1, toValue); + } else { + _volData->setVoxelAt(x, y, z, toValue); + } +} + + +void RenderablePolyVoxEntityItem::updateOnCount(int x, int y, int z, uint8_t toValue) { + // keep _onCount up to date + if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) { + return; + } + + uint8_t uVoxelValue = getVoxel(x, y, z); + if (toValue != 0) { + if (uVoxelValue == 0) { + _onCount++; + } + } else { + // toValue == 0 + if (uVoxelValue != 0) { + _onCount--; + assert(_onCount >= 0); + } + } +} + +void RenderablePolyVoxEntityItem::setAll(uint8_t toValue) { + for (int z = 0; z < _voxelVolumeSize.z; z++) { + for (int y = 0; y < _voxelVolumeSize.y; y++) { + for (int x = 0; x < _voxelVolumeSize.x; x++) { + updateOnCount(x, y, z, toValue); + setVoxel(x, y, z, toValue); + } + } + } + compressVolumeData(); +} + +void RenderablePolyVoxEntityItem::setVoxelInVolume(glm::vec3 position, uint8_t toValue) { + updateOnCount(position.x, position.y, position.z, toValue); + setVoxel(position.x, position.y, position.z, toValue); } void RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue) { // This three-level for loop iterates over every voxel in the volume - for (int z = 0; z < _volData->getDepth(); z++) { - for (int y = 0; y < _volData->getHeight(); y++) { - for (int x = 0; x < _volData->getWidth(); x++) { + for (int z = 0; z < _voxelVolumeSize.z; z++) { + for (int y = 0; y < _voxelVolumeSize.y; y++) { + for (int x = 0; x < _voxelVolumeSize.x; x++) { // Store our current position as a vector... - glm::vec3 pos(x, y, z); + glm::vec3 pos(x + 0.5f, y + 0.5f, z + 0.5f); // consider voxels cenetered on their coordinates // And compute how far the current position is from the center of the volume float fDistToCenter = glm::distance(pos, center); // If the current voxel is less than 'radius' units from the center then we make it solid. if (fDistToCenter <= radius) { - _volData->setVoxelAt(x, y, z, toValue); + updateOnCount(x, y, z, toValue); + setVoxel(x, y, z, toValue); } } } } compressVolumeData(); - _needsModelReload = true; } void RenderablePolyVoxEntityItem::setSphere(glm::vec3 centerWorldCoords, float radiusWorldCoords, uint8_t toValue) { // glm::vec3 centerVoxelCoords = worldToVoxelCoordinates(centerWorldCoords); glm::vec4 centerVoxelCoords = worldToVoxelMatrix() * glm::vec4(centerWorldCoords, 1.0f); glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units - float scaleY = scale[0]; + float scaleY = scale.y; float radiusVoxelCoords = radiusWorldCoords / scaleY; setSphereInVolume(glm::vec3(centerVoxelCoords), radiusVoxelCoords, toValue); } void RenderablePolyVoxEntityItem::getModel() { - if (!_volData) { - // this will cause the allocation of _volData - setVoxelVolumeSize(_voxelVolumeSize); - } - // A mesh object to hold the result of surface extraction PolyVox::SurfaceMesh polyVoxMesh; @@ -134,6 +311,7 @@ void RenderablePolyVoxEntityItem::getModel() { surfaceExtractor.execute(); break; } + case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: case PolyVoxEntityItem::SURFACE_CUBIC: { PolyVox::CubicSurfaceExtractorWithNormals> surfaceExtractor (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); @@ -143,24 +321,25 @@ void RenderablePolyVoxEntityItem::getModel() { } // convert PolyVox mesh to a Sam mesh - model::Mesh* mesh = new model::Mesh(); - model::MeshPointer meshPtr(mesh); + auto mesh = _modelGeometry.getMesh(); const std::vector& vecIndices = polyVoxMesh.getIndices(); auto indexBuffer = new gpu::Buffer(vecIndices.size() * sizeof(uint32_t), (gpu::Byte*)vecIndices.data()); auto indexBufferPtr = gpu::BufferPointer(indexBuffer); - mesh->setIndexBuffer(gpu::BufferView(indexBufferPtr, gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW))); + auto indexBufferView = new gpu::BufferView(indexBufferPtr, gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW)); + mesh->setIndexBuffer(*indexBufferView); const std::vector& vecVertices = polyVoxMesh.getVertices(); auto vertexBuffer = new gpu::Buffer(vecVertices.size() * sizeof(PolyVox::PositionMaterialNormal), (gpu::Byte*)vecVertices.data()); auto vertexBufferPtr = gpu::BufferPointer(vertexBuffer); - mesh->setVertexBuffer(gpu::BufferView(vertexBufferPtr, - 0, - vertexBufferPtr->getSize() - sizeof(float) * 3, - sizeof(PolyVox::PositionMaterialNormal), - gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW))); + auto vertexBufferView = new gpu::BufferView(vertexBufferPtr, + 0, + vertexBufferPtr->getSize() - sizeof(float) * 3, + sizeof(PolyVox::PositionMaterialNormal), + gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW)); + mesh->setVertexBuffer(*vertexBufferView); mesh->addAttribute(gpu::Stream::NORMAL, gpu::BufferView(vertexBufferPtr, sizeof(float) * 3, @@ -168,15 +347,28 @@ void RenderablePolyVoxEntityItem::getModel() { sizeof(PolyVox::PositionMaterialNormal), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW))); + + // auto normalAttrib = mesh->getAttributeBuffer(gpu::Stream::NORMAL); // for (auto normal = normalAttrib.begin(); normal != normalAttrib.end(); normal++) { // (*normal) = -(*normal); // } + + // mesh->addAttribute(gpu::Stream::TEXCOORD, + // gpu::BufferView(vertexBufferPtr, + // sizeof(float) * 2, + // vertexBufferPtr->getSize() - sizeof(float) * 2, + // sizeof(PolyVox::PositionMaterialNormal), + // gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::RAW))); + + + + #ifdef WANT_DEBUG qDebug() << "---- vecIndices.size() =" << vecIndices.size(); qDebug() << "---- vecVertices.size() =" << vecVertices.size(); + #endif - _modelGeometry.setMesh(meshPtr); _needsModelReload = false; } @@ -211,9 +403,20 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { class RaycastFunctor { public: - RaycastFunctor() : _result(glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)) { } + RaycastFunctor(PolyVox::SimpleVolume* vol) : + _result(glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)), + _vol(vol) { + } bool operator()(PolyVox::SimpleVolume::Sampler& sampler) { + int x = sampler.getPosition().getX(); + int y = sampler.getPosition().getY(); + int z = sampler.getPosition().getZ(); + + if (!inBounds(_vol, x, y, z)) { + return true; + } + if (sampler.getVoxel() == 0) { return true; // keep raycasting } @@ -222,6 +425,7 @@ public: return false; } glm::vec4 _result; + const PolyVox::SimpleVolume* _vol = nullptr; }; bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& origin, @@ -237,33 +441,44 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o return true; } + // the PolyVox ray intersection code requires a near and far point. glm::mat4 wtvMatrix = worldToVoxelMatrix(); - glm::vec3 farPoint = origin + direction; - glm::vec4 originInVoxel = wtvMatrix * glm::vec4(origin, 1.0f); - glm::vec4 farInVoxel = wtvMatrix * glm::vec4(farPoint, 1.0f); - glm::vec4 directionInVoxel = farInVoxel - originInVoxel; - - PolyVox::Vector3DFloat start(originInVoxel[0], originInVoxel[1], originInVoxel[2]); - PolyVox::Vector3DFloat pvDirection(directionInVoxel[0], directionInVoxel[1], directionInVoxel[2]); - pvDirection.normalise(); + glm::vec3 normDirection = glm::normalize(direction); // the PolyVox ray intersection code requires a near and far point. glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units + // set ray cast length to long enough to cover all of the voxel space float distanceToEntity = glm::distance(origin, getPosition()); - float largestDimension = glm::max(getDimensions()[0], getDimensions()[1], getDimensions()[2]); - // set ray cast length to long enough to cover all of the voxel space - pvDirection *= (distanceToEntity + largestDimension) / glm::min(scale[0], scale[1], scale[2]); + float largestDimension = glm::max(getDimensions().x, getDimensions().y, getDimensions().z) * 2.0f; + glm::vec3 farPoint = origin + normDirection * (distanceToEntity + largestDimension); + glm::vec4 originInVoxel = wtvMatrix * glm::vec4(origin, 1.0f); + glm::vec4 farInVoxel = wtvMatrix * glm::vec4(farPoint, 1.0f); + + PolyVox::Vector3DFloat startPoint(originInVoxel.x, originInVoxel.y, originInVoxel.z); + PolyVox::Vector3DFloat endPoint(farInVoxel.x, farInVoxel.y, farInVoxel.z); PolyVox::RaycastResult raycastResult; - RaycastFunctor callback; - raycastResult = PolyVox::raycastWithDirection(_volData, start, pvDirection, callback); + RaycastFunctor callback(_volData); + raycastResult = PolyVox::raycastWithEndpoints(_volData, startPoint, endPoint, callback); if (raycastResult == PolyVox::RaycastResults::Completed) { // the ray completed its path -- nothing was hit. return false; } - glm::vec4 intersectedWorldPosition = voxelToWorldMatrix() * callback._result; + glm::vec4 result = callback._result; + switch (_voxelSurfaceStyle) { + case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: + result -= glm::vec4(1, 1, 1, 0); // compensate for the extra voxel border + break; + case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: + case PolyVoxEntityItem::SURFACE_CUBIC: + break; + } + + result -= glm::vec4(0.5f, 0.5f, 0.5f, 0.0f); + + glm::vec4 intersectedWorldPosition = voxelToWorldMatrix() * result; distance = glm::distance(glm::vec3(intersectedWorldPosition), origin); @@ -276,51 +491,195 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o // compress the data in _volData and save the results. The compressed form is used during // saves to disk and for transmission over the wire void RenderablePolyVoxEntityItem::compressVolumeData() { - int rawSize = _volData->getDepth() * _volData->getHeight() * _volData->getWidth(); + quint16 voxelXSize = _voxelVolumeSize.x; + quint16 voxelYSize = _voxelVolumeSize.y; + quint16 voxelZSize = _voxelVolumeSize.z; + int rawSize = voxelXSize * voxelYSize * voxelZSize; + QByteArray uncompressedData = QByteArray(rawSize, '\0'); - for (int z = 0; z < _volData->getDepth(); z++) { - for (int y = 0; y < _volData->getHeight(); y++) { - for (int x = 0; x < _volData->getWidth(); x++) { - uint8_t uVoxelValue = _volData->getVoxelAt(x, y, z); - int uncompressedIndex = z * _volData->getHeight() * _volData->getWidth() + y * _volData->getWidth() + x; + for (int z = 0; z < voxelZSize; z++) { + for (int y = 0; y < voxelYSize; y++) { + for (int x = 0; x < voxelXSize; x++) { + uint8_t uVoxelValue = getVoxel(x, y, z); + int uncompressedIndex = + z * voxelYSize * voxelXSize + + y * voxelXSize + + x; uncompressedData[uncompressedIndex] = uVoxelValue; } } } - QByteArray newVoxelData = qCompress(uncompressedData, 9); - // HACK -- until we have a way to allow for properties larger than MTU, don't update. - if (newVoxelData.length() < 1200) { + QByteArray newVoxelData; + QDataStream writer(&newVoxelData, QIODevice::WriteOnly | QIODevice::Truncate); + writer << voxelXSize << voxelYSize << voxelZSize; + + QByteArray compressedData = qCompress(uncompressedData, 9); + writer << compressedData; + + // make sure the compressed data can be sent over the wire-protocol + if (newVoxelData.size() < 1150) { _voxelData = newVoxelData; + #ifdef WANT_DEBUG qDebug() << "-------------- voxel compresss --------------"; qDebug() << "raw-size =" << rawSize << " compressed-size =" << newVoxelData.size(); + #endif } else { + // HACK -- until we have a way to allow for properties larger than MTU, don't update. + #ifdef WANT_DEBUG qDebug() << "voxel data too large, reverting change."; - // revert + #endif + // revert the active voxel-space to the last version that fit. decompressVolumeData(); } + + _dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS; + _needsModelReload = true; } -// take compressed data and decompreess it into _volData. +// take compressed data and expand it into _volData. void RenderablePolyVoxEntityItem::decompressVolumeData() { - int rawSize = _volData->getDepth() * _volData->getHeight() * _volData->getWidth(); - QByteArray uncompressedData = QByteArray(rawSize, '\0'); + QDataStream reader(_voxelData); + quint16 voxelXSize, voxelYSize, voxelZSize; + reader >> voxelXSize; + reader >> voxelYSize; + reader >> voxelZSize; - uncompressedData = qUncompress(_voxelData); + if (voxelXSize == 0 || voxelXSize > MAX_VOXEL_DIMENSION || + voxelYSize == 0 || voxelYSize > MAX_VOXEL_DIMENSION || + voxelZSize == 0 || voxelZSize > MAX_VOXEL_DIMENSION) { + qDebug() << "voxelSize is not reasonable, skipping decompressions." + << voxelXSize << voxelYSize << voxelZSize; + return; + } + + int rawSize = voxelXSize * voxelYSize * voxelZSize; + + QByteArray compressedData; + reader >> compressedData; + QByteArray uncompressedData = qUncompress(compressedData); - for (int z = 0; z < _volData->getDepth(); z++) { - for (int y = 0; y < _volData->getHeight(); y++) { - for (int x = 0; x < _volData->getWidth(); x++) { - int uncompressedIndex = z * _volData->getHeight() * _volData->getWidth() + y * _volData->getWidth() + x; - _volData->setVoxelAt(x, y, z, uncompressedData[uncompressedIndex]); + if (uncompressedData.size() != rawSize) { + qDebug() << "PolyVox decompress -- size is (" << voxelXSize << voxelYSize << voxelZSize << ")" << + "so expected uncompressed length of" << rawSize << "but length is" << uncompressedData.size(); + return; + } + + for (int z = 0; z < voxelZSize; z++) { + for (int y = 0; y < voxelYSize; y++) { + for (int x = 0; x < voxelXSize; x++) { + int uncompressedIndex = (z * voxelYSize * voxelXSize) + (y * voxelZSize) + x; + updateOnCount(x, y, z, uncompressedData[uncompressedIndex]); + setVoxel(x, y, z, uncompressedData[uncompressedIndex]); } } } - _needsModelReload = true; - + #ifdef WANT_DEBUG qDebug() << "--------------- voxel decompress ---------------"; qDebug() << "raw-size =" << rawSize << " compressed-size =" << _voxelData.size(); + #endif + + _dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS; + _needsModelReload = true; + getModel(); +} + +// virtual +ShapeType RenderablePolyVoxEntityItem::getShapeType() const { + if (_onCount > 0) { + return SHAPE_TYPE_COMPOUND; + } + return SHAPE_TYPE_NONE; +} + + +bool RenderablePolyVoxEntityItem::isReadyToComputeShape() { + if (_needsModelReload) { + return false; + } + + #ifdef WANT_DEBUG + qDebug() << "RenderablePolyVoxEntityItem::isReadyToComputeShape" << (!_needsModelReload); + #endif + return true; +} + +void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) { + #ifdef WANT_DEBUG + qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfo"; + #endif + ShapeType type = getShapeType(); + if (type != SHAPE_TYPE_COMPOUND) { + EntityItem::computeShapeInfo(info); + return; + } + + _points.clear(); + unsigned int i = 0; + + glm::mat4 wToM = voxelToLocalMatrix(); + + AABox box; + + for (int z = 0; z < _voxelVolumeSize.z; z++) { + for (int y = 0; y < _voxelVolumeSize.y; y++) { + for (int x = 0; x < _voxelVolumeSize.x; x++) { + if (getVoxel(x, y, z) > 0) { + QVector pointsInPart; + + float offL = -0.5f; + float offH = 0.5f; + + // float offL = 0.0f; + // float offH = 1.0f; + + glm::vec3 p000 = glm::vec3(wToM * glm::vec4(x + offL, y + offL, z + offL, 1.0f)); + glm::vec3 p001 = glm::vec3(wToM * glm::vec4(x + offL, y + offL, z + offH, 1.0f)); + glm::vec3 p010 = glm::vec3(wToM * glm::vec4(x + offL, y + offH, z + offL, 1.0f)); + glm::vec3 p011 = glm::vec3(wToM * glm::vec4(x + offL, y + offH, z + offH, 1.0f)); + glm::vec3 p100 = glm::vec3(wToM * glm::vec4(x + offH, y + offL, z + offL, 1.0f)); + glm::vec3 p101 = glm::vec3(wToM * glm::vec4(x + offH, y + offL, z + offH, 1.0f)); + glm::vec3 p110 = glm::vec3(wToM * glm::vec4(x + offH, y + offH, z + offL, 1.0f)); + glm::vec3 p111 = glm::vec3(wToM * glm::vec4(x + offH, y + offH, z + offH, 1.0f)); + + box += p000; + box += p001; + box += p010; + box += p011; + box += p100; + box += p101; + box += p110; + box += p111; + + pointsInPart << p000; + pointsInPart << p001; + pointsInPart << p010; + pointsInPart << p011; + pointsInPart << p100; + pointsInPart << p101; + pointsInPart << p110; + pointsInPart << p111; + + // add next convex hull + QVector newMeshPoints; + _points << newMeshPoints; + // add points to the new convex hull + _points[i++] << pointsInPart; + } + } + } + } + + if (_points.isEmpty()) { + EntityItem::computeShapeInfo(info); + return; + } + + glm::vec3 collisionModelDimensions = box.getDimensions(); + QByteArray b64 = _voxelData.toBase64(); + info.setParams(type, collisionModelDimensions, QString(b64)); + info.setConvexHulls(_points); } diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index b04b32996b..c8f1b4a49d 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -21,8 +21,7 @@ class RenderablePolyVoxEntityItem : public PolyVoxEntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); - RenderablePolyVoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : - PolyVoxEntityItem(entityItemID, properties) { } + RenderablePolyVoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); virtual ~RenderablePolyVoxEntityItem(); @@ -34,6 +33,10 @@ public: // _needsModelReload = true; } + virtual uint8_t getVoxel(int x, int y, int z); + virtual void setVoxel(int x, int y, int z, uint8_t toValue); + + void updateOnCount(int x, int y, int z, uint8_t new_value); void render(RenderArgs* args); virtual bool supportsDetailedRayIntersection() const { return true; } @@ -41,27 +44,47 @@ public: bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject, bool precisionPicking) const; + virtual void setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle); + void getModel(); virtual void setVoxelData(QByteArray voxelData); virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize); + glm::vec3 getSurfacePositionAdjustment() const; glm::mat4 voxelToWorldMatrix() const; + glm::mat4 voxelToLocalMatrix() const; glm::mat4 worldToVoxelMatrix() const; + virtual ShapeType getShapeType() const; + virtual bool isReadyToComputeShape(); + virtual void computeShapeInfo(ShapeInfo& info); + + // coords are in voxel-volume space virtual void setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue); // coords are in world-space virtual void setSphere(glm::vec3 center, float radius, uint8_t toValue); + virtual void setAll(uint8_t toValue); + + virtual void setVoxelInVolume(glm::vec3 position, uint8_t toValue); + private: + // The PolyVoxEntityItem class has _voxelData which contains dimensions and compressed voxel data. The dimensions + // may not match _voxelVolumeSize. + void compressVolumeData(); void decompressVolumeData(); PolyVox::SimpleVolume* _volData = nullptr; model::Geometry _modelGeometry; bool _needsModelReload = true; + + QVector> _points; // XXX + + int _onCount = 0; // how many non-zero voxels are in _volData }; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 6977f6ff04..1617131b8e 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -904,6 +904,7 @@ EntityItemProperties EntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(damping, getDamping); COPY_ENTITY_PROPERTY_TO_PROPERTIES(restitution, getRestitution); COPY_ENTITY_PROPERTY_TO_PROPERTIES(friction, getFriction); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(created, getCreated); COPY_ENTITY_PROPERTY_TO_PROPERTIES(lifetime, getLifetime); COPY_ENTITY_PROPERTY_TO_PROPERTIES(script, getScript); COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionSoundURL, getCollisionSoundURL); @@ -954,6 +955,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(damping, updateDamping); SET_ENTITY_PROPERTY_FROM_PROPERTIES(restitution, updateRestitution); SET_ENTITY_PROPERTY_FROM_PROPERTIES(friction, updateFriction); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(created, updateCreated); SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, updateLifetime); SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript); SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionSoundURL, setCollisionSoundURL); @@ -1002,11 +1004,13 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { } void EntityItem::recordCreationTime() { - //assert(_created == UNKNOWN_CREATED_TIME); - _created = usecTimestampNow(); + if (_created == UNKNOWN_CREATED_TIME) { + _created = usecTimestampNow(); + } + auto now = usecTimestampNow(); _lastEdited = _created; - _lastUpdated = _created; - _lastSimulated = _created; + _lastUpdated = now; + _lastSimulated = now; } void EntityItem::setCenterPosition(const glm::vec3& position) { @@ -1341,6 +1345,13 @@ void EntityItem::updateLifetime(float value) { } } +void EntityItem::updateCreated(uint64_t value) { + if (_created != value) { + _created = value; + _dirtyFlags |= EntityItem::DIRTY_LIFETIME; + } +} + void EntityItem::setSimulatorID(const QUuid& value) { _simulatorID = value; _simulatorIDChangedTime = usecTimestampNow(); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index de34e89ea4..878d0bed7c 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -247,6 +247,9 @@ public: float getLifetime() const { return _lifetime; } /// get the lifetime in seconds for the entity void setLifetime(float value) { _lifetime = value; } /// set the lifetime in seconds for the entity + quint64 getCreated() const { return _created; } /// get the created-time in useconds for the entity + void setCreated(quint64 value) { _created = value; } /// set the created-time in useconds for the entity + /// is this entity immortal, in that it has no lifetime set, and will exist until manually deleted bool isImmortal() const { return _lifetime == ENTITY_ITEM_IMMORTAL_LIFETIME; } @@ -344,6 +347,7 @@ public: void updateIgnoreForCollisions(bool value); void updateCollisionsWillMove(bool value); void updateLifetime(float value); + void updateCreated(uint64_t value); virtual void updateShapeType(ShapeType type) { /* do nothing */ } uint32_t getDirtyFlags() const { return _dirtyFlags; } diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 583cf15b9c..90f2b22698 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -27,6 +27,7 @@ #include "TextEntityItem.h" #include "ZoneEntityItem.h" #include "PolyVoxEntityItem.h" +#include "LineEntityItem.h" AtmospherePropertyGroup EntityItemProperties::_staticAtmosphere; SkyboxPropertyGroup EntityItemProperties::_staticSkybox; @@ -48,6 +49,7 @@ CONSTRUCT_PROPERTY(damping, ENTITY_ITEM_DEFAULT_DAMPING), CONSTRUCT_PROPERTY(restitution, ENTITY_ITEM_DEFAULT_RESTITUTION), CONSTRUCT_PROPERTY(friction, ENTITY_ITEM_DEFAULT_FRICTION), CONSTRUCT_PROPERTY(lifetime, ENTITY_ITEM_DEFAULT_LIFETIME), +CONSTRUCT_PROPERTY(created, UNKNOWN_CREATED_TIME), CONSTRUCT_PROPERTY(script, ENTITY_ITEM_DEFAULT_SCRIPT), CONSTRUCT_PROPERTY(collisionSoundURL, ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL), CONSTRUCT_PROPERTY(color, ), @@ -94,12 +96,13 @@ CONSTRUCT_PROPERTY(voxelSurfaceStyle, PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_S CONSTRUCT_PROPERTY(name, ENTITY_ITEM_DEFAULT_NAME), CONSTRUCT_PROPERTY(backgroundMode, BACKGROUND_MODE_INHERIT), CONSTRUCT_PROPERTY(sourceUrl, ""), +CONSTRUCT_PROPERTY(lineWidth, LineEntityItem::DEFAULT_LINE_WIDTH), +CONSTRUCT_PROPERTY(linePoints, QVector()), _id(UNKNOWN_ENTITY_ID), _idSet(false), _lastEdited(0), -_created(UNKNOWN_CREATED_TIME), _type(EntityTypes::Unknown), _glowLevel(0.0f), @@ -176,6 +179,11 @@ QString EntityItemProperties::getAnimationSettings() const { return jsonByteString; } +void EntityItemProperties::setCreated(QDateTime &v) { + _created = v.toMSecsSinceEpoch() * 1000; // usec per msec + qDebug() << "EntityItemProperties::setCreated QDateTime" << v << _created; +} + void EntityItemProperties::debugDump() const { qCDebug(entities) << "EntityItemProperties..."; qCDebug(entities) << " _type=" << EntityTypes::getEntityTypeName(_type); @@ -194,13 +202,6 @@ void EntityItemProperties::debugDump() const { props.debugDumpBits(); } -void EntityItemProperties::setCreated(quint64 usecTime) { - _created = usecTime; - if (_lastEdited < _created) { - _lastEdited = _created; - } -} - void EntityItemProperties::setLastEdited(quint64 usecTime) { _lastEdited = usecTime > _created ? usecTime : _created; } @@ -344,7 +345,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize); CHECK_PROPERTY_CHANGE(PROP_VOXEL_DATA, voxelData); CHECK_PROPERTY_CHANGE(PROP_VOXEL_SURFACE_STYLE, voxelSurfaceStyle); - + CHECK_PROPERTY_CHANGE(PROP_LINE_WIDTH, lineWidth); + CHECK_PROPERTY_CHANGE(PROP_LINE_POINTS, linePoints); changedProperties += _stage.getChangedProperties(); changedProperties += _atmosphere.getChangedProperties(); changedProperties += _skybox.getChangedProperties(); @@ -375,10 +377,16 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(friction); COPY_PROPERTY_TO_QSCRIPTVALUE(density); COPY_PROPERTY_TO_QSCRIPTVALUE(lifetime); - if (!skipDefaults) { + + if (!skipDefaults || _lifetime != defaultEntityProperties._lifetime) { COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(age, getAge()); // gettable, but not settable COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(ageAsText, formatSecondsElapsed(getAge())); // gettable, but not settable } + + auto created = QDateTime::fromMSecsSinceEpoch(getCreated() / 1000.0f, Qt::UTC); // usec per msec + created.setTimeSpec(Qt::OffsetFromUTC); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(created, created.toString(Qt::ISODate)); + COPY_PROPERTY_TO_QSCRIPTVALUE(script); COPY_PROPERTY_TO_QSCRIPTVALUE(registrationPoint); COPY_PROPERTY_TO_QSCRIPTVALUE(angularVelocity); @@ -426,10 +434,11 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(keyLightDirection); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(backgroundMode, getBackgroundModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(sourceUrl); - COPY_PROPERTY_TO_QSCRIPTVALUE(voxelVolumeSize); COPY_PROPERTY_TO_QSCRIPTVALUE(voxelData); COPY_PROPERTY_TO_QSCRIPTVALUE(voxelSurfaceStyle); + COPY_PROPERTY_TO_QSCRIPTVALUE(lineWidth); + COPY_PROPERTY_TO_QSCRIPTVALUE(linePoints); // Sitting properties support if (!skipDefaults) { @@ -471,7 +480,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool return properties; } -void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) { +void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool honorReadOnly) { QScriptValue typeScriptValue = object.property("type"); if (typeScriptValue.isValid()) { setType(typeScriptValue.toVariant().toString()); @@ -512,7 +521,6 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) { COPY_PROPERTY_FROM_QSCRIPTVALUE(locked, bool, setLocked); COPY_PROPERTY_FROM_QSCRIPTVALUE(textures, QString, setTextures); COPY_PROPERTY_FROM_QSCRIPTVALUE(userData, QString, setUserData); - //COPY_PROPERTY_FROM_QSCRIPTVALUE(simulatorID, QUuid, setSimulatorID); DO NOT accept this info from QScriptValue COPY_PROPERTY_FROM_QSCRIPTVALUE(text, QString, setText); COPY_PROPERTY_FROM_QSCRIPTVALUE(lineHeight, float, setLineHeight); COPY_PROPERTY_FROM_QSCRIPTVALUE(textColor, xColor, setTextColor); @@ -535,10 +543,20 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) { COPY_PROPERTY_FROM_QSCRIPTVALUE(keyLightDirection, glmVec3, setKeyLightDirection); COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(backgroundMode, BackgroundMode); COPY_PROPERTY_FROM_QSCRIPTVALUE(sourceUrl, QString, setSourceUrl); - COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, glmVec3, setVoxelVolumeSize); COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelData, QByteArray, setVoxelData); COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelSurfaceStyle, uint16_t, setVoxelSurfaceStyle); + COPY_PROPERTY_FROM_QSCRIPTVALUE(lineWidth, float, setLineWidth); + COPY_PROPERTY_FROM_QSCRIPTVALUE(linePoints, qVectorVec3, setLinePoints); + + if (!honorReadOnly) { + // this is used by the json reader to set things that we don't want javascript to able to affect. + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(created, QDateTime, setCreated, [this]() { + auto result = QDateTime::fromMSecsSinceEpoch(_created / 1000, Qt::UTC); // usec per msec + return result; + }); + COPY_PROPERTY_FROM_QSCRIPTVALUE(simulatorID, QUuid, setSimulatorID); + } _stage.copyFromScriptValue(object, _defaultSettings); _atmosphere.copyFromScriptValue(object, _defaultSettings); @@ -554,10 +572,15 @@ QScriptValue EntityItemNonDefaultPropertiesToScriptValue(QScriptEngine* engine, return properties.copyToScriptValue(engine, true); } -void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemProperties& properties) { - properties.copyFromScriptValue(object); +void EntityItemPropertiesFromScriptValueIgnoreReadOnly(const QScriptValue &object, EntityItemProperties& properties) { + properties.copyFromScriptValue(object, false); } +void EntityItemPropertiesFromScriptValueHonorReadOnly(const QScriptValue &object, EntityItemProperties& properties) { + properties.copyFromScriptValue(object, true); +} + + // TODO: Implement support for edit packets that can span an MTU sized buffer. We need to implement a mechanism for the // encodeEntityEditPacket() method to communicate the the caller which properties couldn't fit in the buffer. Similar // to how we handle this in the Octree streaming case. @@ -763,6 +786,11 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_VOXEL_SURFACE_STYLE, properties.getVoxelSurfaceStyle()); } + if (properties.getType() == EntityTypes::Line) { + APPEND_ENTITY_PROPERTY(PROP_LINE_WIDTH, properties.getLineWidth()); + APPEND_ENTITY_PROPERTY(PROP_LINE_POINTS, properties.getLinePoints()); + } + APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, properties.getMarketplaceID()); APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName()); APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, properties.getCollisionSoundURL()); @@ -1003,6 +1031,11 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_SURFACE_STYLE, uint16_t, setVoxelSurfaceStyle); } + if (properties.getType() == EntityTypes::Line) { + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_WIDTH, float, setLineWidth); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_POINTS, QVector, setLinePoints); + } + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MARKETPLACE_ID, QString, setMarketplaceID); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL); @@ -1107,10 +1140,13 @@ void EntityItemProperties::markAllChanged() { _skybox.markAllChanged(); _sourceUrlChanged = true; - _voxelVolumeSizeChanged = true; _voxelDataChanged = true; _voxelSurfaceStyleChanged = true; + + _lineWidthChanged = true; + _linePointsChanged = true; + } /// The maximum bounding cube for the entity, independent of it's rotation. diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index dbe2e926c9..3c8133af8f 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -63,7 +63,7 @@ public: void setType(EntityTypes::EntityType type) { _type = type; } virtual QScriptValue copyToScriptValue(QScriptEngine* engine, bool skipDefaults) const; - virtual void copyFromScriptValue(const QScriptValue& object); + virtual void copyFromScriptValue(const QScriptValue& object, bool honorReadOnly); // editing related features supported by all entities quint64 getLastEdited() const { return _lastEdited; } @@ -96,6 +96,7 @@ public: DEFINE_PROPERTY(PROP_RESTITUTION, Restitution, restitution, float); DEFINE_PROPERTY(PROP_FRICTION, Friction, friction, float); DEFINE_PROPERTY(PROP_LIFETIME, Lifetime, lifetime, float); + DEFINE_PROPERTY(PROP_CREATED, Created, created, quint64); DEFINE_PROPERTY_REF(PROP_SCRIPT, Script, script, QString); DEFINE_PROPERTY_REF(PROP_COLLISION_SOUND_URL, CollisionSoundURL, collisionSoundURL, QString); DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, xColor); @@ -145,6 +146,8 @@ public: DEFINE_PROPERTY_GROUP(Atmosphere, atmosphere, AtmospherePropertyGroup); DEFINE_PROPERTY_GROUP(Skybox, skybox, SkyboxPropertyGroup); DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString); + DEFINE_PROPERTY(PROP_LINE_WIDTH, LineWidth, lineWidth, float); + DEFINE_PROPERTY_REF(LINE_POINTS, LinePoints, linePoints, QVector); static QString getBackgroundModeString(BackgroundMode mode); @@ -153,8 +156,6 @@ public: float getMaxDimension() const { return glm::max(_dimensions.x, _dimensions.y, _dimensions.z); } float getAge() const { return (float)(usecTimestampNow() - _created) / (float)USECS_PER_SECOND; } - quint64 getCreated() const { return _created; } - void setCreated(quint64 usecTime); bool hasCreatedTime() const { return (_created != UNKNOWN_CREATED_TIME); } bool containsBoundsProperties() const { return (_positionChanged || _dimensionsChanged); } @@ -195,13 +196,14 @@ public: void setVoxelDataDirty() { _voxelDataChanged = true; } + void setCreated(QDateTime& v); + bool hasTerseUpdateChanges() const; private: QUuid _id; bool _idSet; quint64 _lastEdited; - quint64 _created; EntityTypes::EntityType _type; void setType(const QString& typeName) { _type = EntityTypes::getEntityTypeFromName(typeName); } @@ -221,7 +223,8 @@ private: Q_DECLARE_METATYPE(EntityItemProperties); QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties); QScriptValue EntityItemNonDefaultPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties); -void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemProperties& properties); +void EntityItemPropertiesFromScriptValueIgnoreReadOnly(const QScriptValue &object, EntityItemProperties& properties); +void EntityItemPropertiesFromScriptValueHonorReadOnly(const QScriptValue &object, EntityItemProperties& properties); // define these inline here so the macros work diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 33abc59e4d..0f3459e5d2 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #ifndef hifi_EntityItemPropertiesMacros_h #define hifi_EntityItemPropertiesMacros_h @@ -95,14 +97,13 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const QString& v) { ret inline QScriptValue convertScriptValue(QScriptEngine* e, const xColor& v) { return xColorToScriptValue(e, v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::quat& v) { return quatToScriptValue(e, v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const QScriptValue& v) { return v; } +inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector& v) {return qVectorVec3ToScriptValue(e, v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const QByteArray& v) { QByteArray b64 = v.toBase64(); return QScriptValue(QString(b64)); } - - #define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(G,g,P,p) \ if (!skipDefaults || defaultEntityProperties.get##G().get##P() != get##P()) { \ QScriptValue groupProperties = properties.property(#g); \ @@ -131,6 +132,7 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const QByteArray& v) { typedef glm::vec3 glmVec3; typedef glm::quat glmQuat; +typedef QVector qVectorVec3; inline float float_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toFloat(&isValid); } inline uint16_t uint16_t_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toInt(&isValid); } inline int int_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toInt(&isValid); } @@ -138,6 +140,14 @@ inline bool bool_convertFromScriptValue(const QScriptValue& v, bool& isValid) { inline QString QString_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toString().trimmed(); } inline QUuid QUuid_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toUuid(); } +inline QDateTime QDateTime_convertFromScriptValue(const QScriptValue& v, bool& isValid) { + isValid = true; + auto result = QDateTime::fromString(v.toVariant().toString().trimmed(), Qt::ISODate); + // result.setTimeSpec(Qt::OffsetFromUTC); + return result; +} + + inline QByteArray QByteArray_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; @@ -145,8 +155,6 @@ inline QByteArray QByteArray_convertFromScriptValue(const QScriptValue& v, bool& return QByteArray::fromBase64(b64.toUtf8()); } - - inline glmVec3 glmVec3_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = false; /// assume it can't be converted QScriptValue x = v.property("x"); @@ -167,6 +175,11 @@ inline glmVec3 glmVec3_convertFromScriptValue(const QScriptValue& v, bool& isVal return glm::vec3(0); } +inline qVectorVec3 qVectorVec3_convertFromScriptValue(const QScriptValue& v, bool& isValid) { + isValid = true; + return qVectorVec3FromScriptValue(v); +} + inline glmQuat glmQuat_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = false; /// assume it can't be converted QScriptValue x = v.property("x"); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index bcb1ec0886..8eb09fece0 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -114,6 +114,10 @@ enum EntityPropertyList { PROP_VOXEL_DATA, PROP_VOXEL_SURFACE_STYLE, + //for lines + PROP_LINE_WIDTH, + PROP_LINE_POINTS, + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties ABOVE this line PROP_AFTER_LAST_ITEM, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index d684fbf2fc..6866505d58 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -17,7 +17,6 @@ #include "ModelEntityItem.h" #include "ZoneEntityItem.h" #include "EntitiesLogging.h" -#include "PolyVoxEntityItem.h" EntityScriptingInterface::EntityScriptingInterface() : @@ -370,7 +369,7 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra quuidFromScriptValue(entityIDValue, value.entityID); QScriptValue entityPropertiesValue = object.property("properties"); if (entityPropertiesValue.isValid()) { - EntityItemPropertiesFromScriptValue(entityPropertiesValue, value.properties); + EntityItemPropertiesFromScriptValueHonorReadOnly(entityPropertiesValue, value.properties); } value.distance = object.property("distance").toVariant().toFloat(); @@ -395,7 +394,8 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra } -bool EntityScriptingInterface::setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value) { +bool EntityScriptingInterface::setVoxels(QUuid entityID, + std::function actor) { if (!_entityTree) { return false; } @@ -415,7 +415,7 @@ bool EntityScriptingInterface::setVoxelSphere(QUuid entityID, const glm::vec3& c PolyVoxEntityItem* polyVoxEntity = static_cast(entity.get()); _entityTree->lockForWrite(); - polyVoxEntity->setSphere(center, radius, value); + actor(*polyVoxEntity); entity->setLastEdited(now); entity->setLastBroadcast(now); _entityTree->unlock(); @@ -430,3 +430,24 @@ bool EntityScriptingInterface::setVoxelSphere(QUuid entityID, const glm::vec3& c queueEntityMessage(PacketTypeEntityEdit, entityID, properties); return true; } + + +bool EntityScriptingInterface::setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value) { + return setVoxels(entityID, [center, radius, value](PolyVoxEntityItem& polyVoxEntity) { + polyVoxEntity.setSphere(center, radius, value); + }); +} + + +bool EntityScriptingInterface::setVoxel(QUuid entityID, const glm::vec3& position, int value) { + return setVoxels(entityID, [position, value](PolyVoxEntityItem& polyVoxEntity) { + polyVoxEntity.setVoxelInVolume(position, value); + }); +} + + +bool EntityScriptingInterface::setAllVoxels(QUuid entityID, int value) { + return setVoxels(entityID, [value](PolyVoxEntityItem& polyVoxEntity) { + polyVoxEntity.setAll(value); + }); +} diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 6c2dc06579..7761effe2f 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -21,6 +21,7 @@ #include #include #include +#include "PolyVoxEntityItem.h" #include "EntityEditPacketSender.h" @@ -117,7 +118,10 @@ public slots: Q_INVOKABLE void setSendPhysicsUpdates(bool value); Q_INVOKABLE bool getSendPhysicsUpdates() const; + bool setVoxels(QUuid entityID, std::function actor); Q_INVOKABLE bool setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value); + Q_INVOKABLE bool setVoxel(QUuid entityID, const glm::vec3& position, int value); + Q_INVOKABLE bool setAllVoxels(QUuid entityID, int value); Q_INVOKABLE void dumpTree() const; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 5eec93e8f5..937472820e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1055,7 +1055,7 @@ bool EntityTree::readFromMap(QVariantMap& map) { QVariantMap entityMap = entityVariant.toMap(); QScriptValue entityScriptValue = variantMapToScriptValue(entityMap, scriptEngine); EntityItemProperties properties; - EntityItemPropertiesFromScriptValue(entityScriptValue, properties); + EntityItemPropertiesFromScriptValueIgnoreReadOnly(entityScriptValue, properties); EntityItemID entityItemID; if (entityMap.contains("id")) { diff --git a/libraries/entities/src/LineEntityItem.cpp b/libraries/entities/src/LineEntityItem.cpp index 1aa79a4e6f..72f403a4e0 100644 --- a/libraries/entities/src/LineEntityItem.cpp +++ b/libraries/entities/src/LineEntityItem.cpp @@ -20,24 +20,41 @@ #include "EntityTreeElement.h" + +const float LineEntityItem::DEFAULT_LINE_WIDTH = 2.0f; + + EntityItemPointer LineEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer result { new LineEntityItem(entityID, properties) }; return result; } LineEntityItem::LineEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : - EntityItem(entityItemID) + EntityItem(entityItemID) , + _lineWidth(DEFAULT_LINE_WIDTH), + _pointsChanged(true), + _points(QVector(0)) { _type = EntityTypes::Line; _created = properties.getCreated(); setProperties(properties); + + } EntityItemProperties LineEntityItem::getProperties() const { + EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class + properties._color = getXColor(); properties._colorChanged = false; + + + COPY_ENTITY_PROPERTY_TO_PROPERTIES(lineWidth, getLineWidth); + + COPY_ENTITY_PROPERTY_TO_PROPERTIES(linePoints, getLinePoints); + properties._glowLevel = getGlowLevel(); properties._glowLevelChanged = false; @@ -48,8 +65,11 @@ EntityItemProperties LineEntityItem::getProperties() const { bool LineEntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = false; somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class - + SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(lineWidth, setLineWidth); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(linePoints, setLinePoints); + if (somethingChanged) { bool wantDebug = false; @@ -64,7 +84,12 @@ bool LineEntityItem::setProperties(const EntityItemProperties& properties) { return somethingChanged; } -int LineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, +void LineEntityItem::setLinePoints(const QVector& points) { + _points = points; + _pointsChanged = true; +} + +int LineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData) { @@ -72,6 +97,9 @@ int LineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, const unsigned char* dataAt = data; READ_ENTITY_PROPERTY(PROP_COLOR, rgbColor, setColor); + READ_ENTITY_PROPERTY(PROP_LINE_WIDTH, float, setLineWidth); + READ_ENTITY_PROPERTY(PROP_LINE_POINTS, QVector, setLinePoints); + return bytesRead; } @@ -81,6 +109,8 @@ int LineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, EntityPropertyFlags LineEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); requestedProperties += PROP_COLOR; + requestedProperties += PROP_LINE_WIDTH; + requestedProperties += PROP_LINE_POINTS; return requestedProperties; } @@ -95,6 +125,8 @@ void LineEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits bool successPropertyFits = true; APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor()); + APPEND_ENTITY_PROPERTY(PROP_LINE_WIDTH, getLineWidth()); + APPEND_ENTITY_PROPERTY(PROP_LINE_POINTS, getLinePoints()); } void LineEntityItem::debugDump() const { diff --git a/libraries/entities/src/LineEntityItem.h b/libraries/entities/src/LineEntityItem.h index 29b2c834ae..5151244b3c 100644 --- a/libraries/entities/src/LineEntityItem.h +++ b/libraries/entities/src/LineEntityItem.h @@ -51,6 +51,13 @@ class LineEntityItem : public EntityItem { _color[BLUE_INDEX] = value.blue; } + void setLineWidth(float lineWidth){ _lineWidth = lineWidth; } + float getLineWidth() const{ return _lineWidth; } + + void setLinePoints(const QVector& points); + + const QVector& getLinePoints() const{ return _points; } + virtual ShapeType getShapeType() const { return SHAPE_TYPE_LINE; } // never have a ray intersection pick a LineEntityItem. @@ -60,9 +67,13 @@ class LineEntityItem : public EntityItem { void** intersectedObject, bool precisionPicking) const { return false; } virtual void debugDump() const; + static const float DEFAULT_LINE_WIDTH; protected: rgbColor _color; + float _lineWidth; + bool _pointsChanged; + QVector _points; }; #endif // hifi_LineEntityItem_h diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index c60b0f863a..95ab7d1035 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -22,7 +22,8 @@ const glm::vec3 PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE = glm::vec3(32, 32, 32); -const QByteArray PolyVoxEntityItem::DEFAULT_VOXEL_DATA(qCompress(QByteArray(0), 9)); +const float PolyVoxEntityItem::MAX_VOXEL_DIMENSION = 32.0f; +const QByteArray PolyVoxEntityItem::DEFAULT_VOXEL_DATA(qCompress(QByteArray(0), 9)); // XXX const PolyVoxEntityItem::PolyVoxSurfaceStyle PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE = PolyVoxEntityItem::SURFACE_MARCHING_CUBES; @@ -41,6 +42,40 @@ PolyVoxEntityItem::PolyVoxEntityItem(const EntityItemID& entityItemID, const Ent setProperties(properties); } +void PolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { + assert((int)_voxelVolumeSize.x == _voxelVolumeSize.x); + assert((int)_voxelVolumeSize.y == _voxelVolumeSize.y); + assert((int)_voxelVolumeSize.z == _voxelVolumeSize.z); + + _voxelVolumeSize = voxelVolumeSize; + if (_voxelVolumeSize.x < 1) { + qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping x of" << _voxelVolumeSize.x << "to 1"; + _voxelVolumeSize.x = 1; + } + if (_voxelVolumeSize.x > MAX_VOXEL_DIMENSION) { + qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping x of" << _voxelVolumeSize.x << "to max"; + _voxelVolumeSize.x = MAX_VOXEL_DIMENSION; + } + + if (_voxelVolumeSize.y < 1) { + qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping y of" << _voxelVolumeSize.y << "to 1"; + _voxelVolumeSize.y = 1; + } + if (_voxelVolumeSize.y > MAX_VOXEL_DIMENSION) { + qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping y of" << _voxelVolumeSize.y << "to max"; + _voxelVolumeSize.y = MAX_VOXEL_DIMENSION; + } + + if (_voxelVolumeSize.z < 1) { + qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping z of" << _voxelVolumeSize.z << "to 1"; + _voxelVolumeSize.z = 1; + } + if (_voxelVolumeSize.z > MAX_VOXEL_DIMENSION) { + qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping z of" << _voxelVolumeSize.z << "to max"; + _voxelVolumeSize.z = MAX_VOXEL_DIMENSION; + } +} + EntityItemProperties PolyVoxEntityItem::getProperties() const { EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class COPY_ENTITY_PROPERTY_TO_PROPERTIES(voxelVolumeSize, getVoxelVolumeSize); @@ -114,4 +149,3 @@ void PolyVoxEntityItem::debugDump() const { qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions()); qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); } - diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index 53675e6efa..bf8214675b 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -40,19 +40,7 @@ class PolyVoxEntityItem : public EntityItem { virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData); - - const rgbColor& getColor() const { return _color; } - xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; } - - void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); } - void setXColor(const xColor& value) { - _color[RED_INDEX] = value.red; - _color[GREEN_INDEX] = value.green; - _color[BLUE_INDEX] = value.blue; - } - virtual ShapeType getShapeType() const { return SHAPE_TYPE_POLYVOX; } - // never have a ray intersection pick a PolyVoxEntityItem. virtual bool supportsDetailedRayIntersection() const { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, @@ -61,7 +49,7 @@ class PolyVoxEntityItem : public EntityItem { virtual void debugDump() const; - virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { _voxelVolumeSize = voxelVolumeSize; } + virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize); virtual const glm::vec3& getVoxelVolumeSize() const { return _voxelVolumeSize; } virtual void setVoxelData(QByteArray voxelData) { _voxelData = voxelData; } @@ -69,16 +57,19 @@ class PolyVoxEntityItem : public EntityItem { enum PolyVoxSurfaceStyle { SURFACE_MARCHING_CUBES, - SURFACE_CUBIC + SURFACE_CUBIC, + SURFACE_EDGED_CUBIC }; virtual void setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) { _voxelSurfaceStyle = voxelSurfaceStyle; } virtual void setVoxelSurfaceStyle(uint16_t voxelSurfaceStyle) { - _voxelSurfaceStyle = (PolyVoxSurfaceStyle) voxelSurfaceStyle; + setVoxelSurfaceStyle((PolyVoxSurfaceStyle) voxelSurfaceStyle); } virtual PolyVoxSurfaceStyle getVoxelSurfaceStyle() const { return _voxelSurfaceStyle; } static const glm::vec3 DEFAULT_VOXEL_VOLUME_SIZE; + static const float MAX_VOXEL_DIMENSION; + static const QByteArray DEFAULT_VOXEL_DATA; static const PolyVoxSurfaceStyle DEFAULT_VOXEL_SURFACE_STYLE; @@ -88,8 +79,15 @@ class PolyVoxEntityItem : public EntityItem { // coords are in world-space virtual void setSphere(glm::vec3 center, float radius, uint8_t toValue) {} + virtual void setAll(uint8_t toValue) {} + + virtual void setVoxelInVolume(glm::vec3 position, uint8_t toValue) {} + + virtual uint8_t getVoxel(int x, int y, int z) { return 0; } + virtual void setVoxel(int x, int y, int z, uint8_t toValue) {} + + protected: - rgbColor _color; glm::vec3 _voxelVolumeSize; // this is always 3 bytes QByteArray _voxelData; PolyVoxSurfaceStyle _voxelSurfaceStyle; diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 4b02a32cad..78bb3f11e1 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -73,7 +73,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketTypeEntityAdd: case PacketTypeEntityEdit: case PacketTypeEntityData: - return VERSION_NO_ENTITY_ID_SWAP; + return VERSION_ENTITIES_LINE_POINTS; case PacketTypeEntityErase: return 2; case PacketTypeAudioStreamStats: @@ -135,7 +135,6 @@ QString nameForPacketType(PacketType packetType) { PACKET_TYPE_NAME_LOOKUP(PacketTypeUnverifiedPingReply); PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityAdd); PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityEdit); - PACKET_TYPE_NAME_LOOKUP(PacketTypeParticleEntitiesFix); default: return QString("Type: ") + QString::number((int)packetType); } diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index cf1a323741..228df1cdde 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -79,8 +79,7 @@ enum PacketType { PacketTypeSignedTransactionPayment, PacketTypeIceServerHeartbeat, // 50 PacketTypeUnverifiedPing, - PacketTypeUnverifiedPingReply, - PacketTypeParticleEntitiesFix + PacketTypeUnverifiedPingReply }; typedef char PacketVersion; @@ -183,5 +182,6 @@ const PacketVersion VERSION_ENTITIES_HAVE_COLLISION_SOUND_URL = 25; const PacketVersion VERSION_ENTITIES_HAVE_FRICTION = 26; const PacketVersion VERSION_NO_ENTITY_ID_SWAP = 27; const PacketVersion VERSION_ENTITIES_PARTICLE_FIX = 28; +const PacketVersion VERSION_ENTITIES_LINE_POINTS = 29; #endif // hifi_PacketHeaders_h diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index d53d29e444..adc2040b8a 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -2096,7 +2096,15 @@ void Octree::writeToJSONFile(const char* fileName, OctreeElement* element) { top = _rootElement; } + // include the "bitstream" version + PacketType expectedType = expectedDataPacketType(); + PacketVersion expectedVersion = versionForPacketType(expectedType); + entityDescription["Version"] = (int) expectedVersion; + + // store the entity data bool entityDescriptionSuccess = writeToMap(entityDescription, top, true); + + // convert the QVariantMap to JSON if (entityDescriptionSuccess && persistFile.open(QIODevice::WriteOnly)) { persistFile.write(QJsonDocument::fromVariant(entityDescription).toJson()); } else { diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp index 19fc278088..7c977210fc 100644 --- a/libraries/octree/src/OctreePacketData.cpp +++ b/libraries/octree/src/OctreePacketData.cpp @@ -325,6 +325,7 @@ bool OctreePacketData::appendValue(uint8_t value) { bool OctreePacketData::appendValue(uint16_t value) { const unsigned char* data = (const unsigned char*)&value; + int length = sizeof(value); bool success = append(data, length); if (success) { @@ -358,6 +359,7 @@ bool OctreePacketData::appendValue(quint64 value) { } bool OctreePacketData::appendValue(float value) { + const unsigned char* data = (const unsigned char*)&value; int length = sizeof(value); bool success = append(data, length); @@ -379,6 +381,17 @@ bool OctreePacketData::appendValue(const glm::vec3& value) { return success; } +bool OctreePacketData::appendValue(const QVector& value) { + uint16_t qVecSize = value.size(); + bool success = appendValue(qVecSize); + success = append((const unsigned char*)value.constData(), qVecSize * sizeof(glm::vec3)); + if (success) { + _bytesOfValues += qVecSize * sizeof(glm::vec3); + _totalBytesOfValues += qVecSize * sizeof(glm::vec3); + } + return success; +} + bool OctreePacketData::appendValue(const glm::quat& value) { const size_t VALUES_PER_QUAT = 4; const size_t PACKED_QUAT_SIZE = sizeof(uint16_t) * VALUES_PER_QUAT; @@ -585,6 +598,15 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, xColor return sizeof(rgbColor); } + +int OctreePacketData::unpackDataFromBytes(const unsigned char *dataBytes, QVector& result) { + uint16_t length; + memcpy(&length, dataBytes, sizeof(uint16_t)); + dataBytes += sizeof(length); + result.resize(length); + memcpy(result.data(), dataBytes, length * sizeof(glm::vec3)); + return sizeof(uint16_t) + length * sizeof(glm::vec3); +} int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QByteArray& result) { uint16_t length; memcpy(&length, dataBytes, sizeof(length)); diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 5becb26ca2..9476fe024e 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -162,6 +162,9 @@ public: /// appends a non-position vector to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(const glm::vec3& value); + + //appends a QVector of vec3's to the end of the stream, may fail if new data stream is too long to fit in packet + bool appendValue(const QVector& value); /// appends a packed quat to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(const glm::quat& value); @@ -185,7 +188,7 @@ public: bool appendRawData(const unsigned char* data, int length); bool appendRawData(QByteArray data); - /// returns a byte offset from beginning of the uncompressed stream based on offset from end. + /// returns a byte offset from beginning of the uncompressed stream based on offset from end. /// Positive offsetFromEnd returns that many bytes before the end of uncompressed stream int getUncompressedByteOffset(int offsetFromEnd = 0) const { return _bytesInUse - offsetFromEnd; } @@ -241,8 +244,10 @@ public: static int unpackDataFromBytes(const unsigned char* dataBytes, QString& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QUuid& result); static int unpackDataFromBytes(const unsigned char* dataBytes, xColor& result); + static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QByteArray& result); + private: /// appends raw bytes, might fail if byte would cause packet to be too large bool append(const unsigned char* data, int length); diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index cf5a0d58da..303d63bef8 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -650,7 +650,6 @@ void GeometryCache::updateVertices(int id, const QVector& points, con void GeometryCache::updateVertices(int id, const QVector& points, const glm::vec4& color) { BatchItemDetails& details = _registeredVertices[id]; - if (details.isCreated) { details.clear(); #ifdef WANT_DEBUG diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index dcbdddcb0a..08385ffb61 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -551,7 +551,6 @@ private: Q_DECLARE_METATYPE(QPointer) Q_DECLARE_METATYPE(QWeakPointer) -Q_DECLARE_METATYPE(QVector) /// Handle management of pending models that need blending class ModelBlender : public QObject, public Dependency { diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 91a4e3c397..30c3fbe8e4 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -318,7 +318,7 @@ void ScriptEngine::init() { registerAvatarTypes(this); registerAudioMetaTypes(this); - qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValue); + qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValueHonorReadOnly); qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue); qScriptRegisterMetaType(this, RayToEntityIntersectionResultToScriptValue, RayToEntityIntersectionResultFromScriptValue); qScriptRegisterSequenceMetaType>(this); diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index eb60e1c31d..241f835a46 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -27,6 +27,7 @@ static int pickRayMetaTypeId = qRegisterMetaType(); static int collisionMetaTypeId = qRegisterMetaType(); + void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, vec4toScriptValue, vec4FromScriptValue); qScriptRegisterMetaType(engine, vec3toScriptValue, vec3FromScriptValue); @@ -72,6 +73,26 @@ void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3) { vec3.z = object.property("z").toVariant().toFloat(); } +QScriptValue qVectorVec3ToScriptValue(QScriptEngine* engine, const QVector& vector){ + QScriptValue array = engine->newArray(); + for (int i = 0; i < vector.size(); i++) { + array.setProperty(i, vec3toScriptValue(engine, vector.at(i))); + } + return array; +} + +QVector qVectorVec3FromScriptValue(const QScriptValue& array){ + QVector newVector; + int length = array.property("length").toInteger(); + + for (int i = 0; i < length; i++) { + glm::vec3 newVec3 = glm::vec3(); + vec3FromScriptValue(array.property(i), newVec3); + newVector << newVec3; + } + return newVector; +} + QScriptValue vec2toScriptValue(QScriptEngine* engine, const glm::vec2 &vec2) { QScriptValue obj = engine->newObject(); obj.setProperty("x", vec2.x); diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 14f30c20fc..48eecba227 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -28,6 +28,7 @@ Q_DECLARE_METATYPE(glm::vec3) Q_DECLARE_METATYPE(glm::vec2) Q_DECLARE_METATYPE(glm::quat) Q_DECLARE_METATYPE(xColor) +Q_DECLARE_METATYPE(QVector) void registerMetaTypes(QScriptEngine* engine); @@ -55,6 +56,9 @@ void qColorFromScriptValue(const QScriptValue& object, QColor& color); QScriptValue qURLToScriptValue(QScriptEngine* engine, const QUrl& url); void qURLFromScriptValue(const QScriptValue& object, QUrl& url); +QScriptValue qVectorVec3ToScriptValue(QScriptEngine* engine, const QVector& vector); +QVector qVectorVec3FromScriptValue( const QScriptValue& array); + class PickRay { public: PickRay() : origin(0.0f), direction(0.0f) { } diff --git a/libraries/shared/src/SettingInterface.cpp b/libraries/shared/src/SettingInterface.cpp index 26cbe3ca13..c14fd33565 100644 --- a/libraries/shared/src/SettingInterface.cpp +++ b/libraries/shared/src/SettingInterface.cpp @@ -46,7 +46,7 @@ namespace Setting { QCoreApplication::setOrganizationName(applicationInfo.value("organizationName").toString()); QCoreApplication::setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); - // Let's set up the settings Private instance on it's own thread + // Let's set up the settings Private instance on its own thread QThread* thread = new QThread(); Q_CHECK_PTR(thread); thread->setObjectName("Settings Thread"); diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h index e2a77fbba2..a3fbe55f36 100644 --- a/libraries/shared/src/ShapeInfo.h +++ b/libraries/shared/src/ShapeInfo.h @@ -35,8 +35,7 @@ enum ShapeType { SHAPE_TYPE_CYLINDER_X, SHAPE_TYPE_CYLINDER_Y, SHAPE_TYPE_CYLINDER_Z, - SHAPE_TYPE_LINE, - SHAPE_TYPE_POLYVOX + SHAPE_TYPE_LINE }; class ShapeInfo {