From f563b2aeba603553d97e41b8a4cf6e4c35e5bbf6 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 19 Aug 2015 19:15:04 -0700 Subject: [PATCH 01/22] clean up debugging prints --- .../src/RenderablePolyVoxEntityItem.cpp | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 5d139a719a..44e7bae53a 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -530,10 +530,6 @@ void RenderablePolyVoxEntityItem::compressVolumeData() { QByteArray newVoxelData; QDataStream writer(&newVoxelData, QIODevice::WriteOnly | QIODevice::Truncate); - #ifdef WANT_DEBUG - qDebug() << "compressing voxel data of size:" << voxelXSize << voxelYSize << voxelZSize; - #endif - writer << voxelXSize << voxelYSize << voxelZSize; QByteArray compressedData = qCompress(uncompressedData, 9); @@ -542,10 +538,6 @@ void RenderablePolyVoxEntityItem::compressVolumeData() { // 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 @@ -559,13 +551,19 @@ void RenderablePolyVoxEntityItem::compressVolumeData() { _needsModelReload = true; #ifdef WANT_DEBUG - qDebug() << "RenderablePolyVoxEntityItem::compressVolumeData" << (usecTimestampNow() - startTime) << getName(); + qDebug() << "RenderablePolyVoxEntityItem::compressVolumeData" << (usecTimestampNow() - startTime) << getName() + << voxelXSize << voxelYSize << voxelZSize + << "raw-size =" << rawSize << " compressed-size =" << newVoxelData.size(); #endif } // take compressed data and expand it into _volData. void RenderablePolyVoxEntityItem::decompressVolumeData() { + #ifdef WANT_DEBUG + auto startTime = usecTimestampNow(); + #endif + QDataStream reader(_voxelData); quint16 voxelXSize, voxelYSize, voxelZSize; reader >> voxelXSize; @@ -610,6 +608,10 @@ void RenderablePolyVoxEntityItem::decompressVolumeData() { _dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS; _needsModelReload = true; getModel(); + + #ifdef WANT_DEBUG + qDebug() << "RenderablePolyVoxEntityItem::decompressVolumeData" << (usecTimestampNow() - startTime) << getName(); + #endif } // virtual @@ -634,11 +636,15 @@ bool RenderablePolyVoxEntityItem::isReadyToComputeShape() { void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) { #ifdef WANT_DEBUG - qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfo"; + auto startTime = usecTimestampNow(); #endif + ShapeType type = getShapeType(); if (type != SHAPE_TYPE_COMPOUND) { EntityItem::computeShapeInfo(info); + #ifdef WANT_DEBUG + qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfo -- shape isn't compound."; + #endif return; } @@ -756,6 +762,9 @@ void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) { if (_points.isEmpty()) { EntityItem::computeShapeInfo(info); + #ifdef WANT_DEBUG + qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfo -- no points."; + #endif return; } @@ -763,6 +772,9 @@ void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) { QByteArray b64 = _voxelData.toBase64(); info.setParams(type, collisionModelDimensions, QString(b64)); info.setConvexHulls(_points); + #ifdef WANT_DEBUG + qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfo" << (usecTimestampNow() - startTime) << getName(); + #endif } void RenderablePolyVoxEntityItem::setXTextureURL(QString xTextureURL) { @@ -830,15 +842,12 @@ void RenderablePolyVoxEntityItem::getModel() { sizeof(PolyVox::PositionMaterialNormal), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW))); - #ifdef WANT_DEBUG - qDebug() << "---- vecIndices.size() =" << vecIndices.size(); - qDebug() << "---- vecVertices.size() =" << vecVertices.size(); - #endif - _needsModelReload = false; #ifdef WANT_DEBUG - qDebug() << "RenderablePolyVoxEntityItem::getModel" << (usecTimestampNow() - startTime) << getName(); + qDebug() << "RenderablePolyVoxEntityItem::getModel" << (usecTimestampNow() - startTime) << getName() + << "vecIndices.size() =" << vecIndices.size() + << "vecVertices.size() =" << vecVertices.size(); #endif } From ef6116465d516de9d8f0dfa98a81bcdc98e96b1c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 19 Aug 2015 19:15:36 -0700 Subject: [PATCH 02/22] make edged-cubic be the default polyvox surface style --- examples/edit.js | 2 +- libraries/entities/src/PolyVoxEntityItem.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index deda035d5e..60666a54a5 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -452,7 +452,7 @@ var toolBar = (function () { type: "PolyVox", dimensions: { x: 10, y: 10, z: 10 }, voxelVolumeSize: {x:16, y:16, z:16}, - voxelSurfaceStyle: 1 + voxelSurfaceStyle: 2 }); return true; diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index 30dded3714..bb9a63cf4c 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -25,7 +25,7 @@ const glm::vec3 PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE = glm::vec3(32, 32, const float PolyVoxEntityItem::MAX_VOXEL_DIMENSION = 128.0f; const QByteArray PolyVoxEntityItem::DEFAULT_VOXEL_DATA(PolyVoxEntityItem::makeEmptyVoxelData()); const PolyVoxEntityItem::PolyVoxSurfaceStyle PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE = - PolyVoxEntityItem::SURFACE_MARCHING_CUBES; + PolyVoxEntityItem::SURFACE_EDGED_CUBIC; const QString PolyVoxEntityItem::DEFAULT_X_TEXTURE_URL = QString(""); const QString PolyVoxEntityItem::DEFAULT_Y_TEXTURE_URL = QString(""); const QString PolyVoxEntityItem::DEFAULT_Z_TEXTURE_URL = QString(""); From 6db0442fda75b899566619b4dfe467301021948d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 21 Aug 2015 10:48:26 -0700 Subject: [PATCH 03/22] allow for asynchronous building of collision shape --- interface/src/Application.cpp | 3 ++- interface/src/avatar/AvatarMotionState.cpp | 9 +++++-- interface/src/avatar/AvatarMotionState.h | 4 ++- libraries/physics/src/EntityMotionState.cpp | 27 +++++++++++++++---- libraries/physics/src/EntityMotionState.h | 8 +++--- libraries/physics/src/ObjectMotionState.cpp | 13 ++++++--- libraries/physics/src/ObjectMotionState.h | 8 +++--- .../physics/src/PhysicalEntitySimulation.cpp | 6 +++++ .../physics/src/PhysicalEntitySimulation.h | 1 + libraries/physics/src/PhysicsEngine.cpp | 18 +++++++++---- libraries/physics/src/PhysicsEngine.h | 2 +- 11 files changed, 75 insertions(+), 24 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5f8b17f178..0b794deb2b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2804,7 +2804,8 @@ void Application::update(float deltaTime) { _entities.getTree()->lockForWrite(); _entitySimulation.lock(); - _physicsEngine.changeObjects(_entitySimulation.getObjectsToChange()); + VectorOfMotionStates stillNeedChange = _physicsEngine.changeObjects(_entitySimulation.getObjectsToChange()); + _entitySimulation.setObjectsToChange(stillNeedChange); _entitySimulation.unlock(); _entities.getTree()->unlock(); diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp index b106ee6398..cabe545f5a 100644 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -28,15 +28,20 @@ AvatarMotionState::~AvatarMotionState() { } // virtual -uint32_t AvatarMotionState::getAndClearIncomingDirtyFlags() { +uint32_t AvatarMotionState::getIncomingDirtyFlags() { uint32_t dirtyFlags = 0; if (_body && _avatar) { dirtyFlags = _dirtyFlags; - _dirtyFlags = 0; } return dirtyFlags; } +void AvatarMotionState::clearIncomingDirtyFlags() { + if (_body && _avatar) { + _dirtyFlags = 0; + } +} + MotionType AvatarMotionState::computeObjectMotionType() const { // TODO?: support non-DYNAMIC motion for avatars? (e.g. when sitting) return MOTION_TYPE_DYNAMIC; diff --git a/interface/src/avatar/AvatarMotionState.h b/interface/src/avatar/AvatarMotionState.h index ac813e764c..1c49705f23 100644 --- a/interface/src/avatar/AvatarMotionState.h +++ b/interface/src/avatar/AvatarMotionState.h @@ -25,7 +25,8 @@ public: virtual MotionType getMotionType() const { return _motionType; } - virtual uint32_t getAndClearIncomingDirtyFlags(); + virtual uint32_t getIncomingDirtyFlags(); + virtual void clearIncomingDirtyFlags(); virtual MotionType computeObjectMotionType() const; @@ -65,6 +66,7 @@ public: friend class AvatarManager; protected: + virtual bool isReadyToComputeShape() { return true; } virtual btCollisionShape* computeNewShape(); virtual void clearObjectBackPointer(); Avatar* _avatar; diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 4e6eb8353c..dc3c0ea4e8 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -99,7 +99,7 @@ void EntityMotionState::updateServerPhysicsVariables() { } // virtual -void EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) { +bool EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) { assert(entityTreeIsLocked()); updateServerPhysicsVariables(); ObjectMotionState::handleEasyChanges(flags, engine); @@ -131,13 +131,15 @@ void EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) if ((flags & EntityItem::DIRTY_PHYSICS_ACTIVATION) && !_body->isActive()) { _body->activate(); } + + return true; } // virtual -void EntityMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine) { +bool EntityMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine) { updateServerPhysicsVariables(); - ObjectMotionState::handleHardAndEasyChanges(flags, engine); + return ObjectMotionState::handleHardAndEasyChanges(flags, engine); } void EntityMotionState::clearObjectBackPointer() { @@ -222,6 +224,15 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { #endif } + +// virtual and protected +bool EntityMotionState::isReadyToComputeShape() { + if (_entity) { + return _entity->isReadyToComputeShape(); + } + return false; +} + // virtual and protected btCollisionShape* EntityMotionState::computeNewShape() { if (_entity) { @@ -493,12 +504,11 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q _lastStep = step; } -uint32_t EntityMotionState::getAndClearIncomingDirtyFlags() { +uint32_t EntityMotionState::getIncomingDirtyFlags() { assert(entityTreeIsLocked()); uint32_t dirtyFlags = 0; if (_body && _entity) { dirtyFlags = _entity->getDirtyFlags(); - _entity->clearDirtyFlags(); // we add DIRTY_MOTION_TYPE if the body's motion type disagrees with entity velocity settings int bodyFlags = _body->getCollisionFlags(); bool isMoving = _entity->isMoving(); @@ -510,6 +520,13 @@ uint32_t EntityMotionState::getAndClearIncomingDirtyFlags() { return dirtyFlags; } +void EntityMotionState::clearIncomingDirtyFlags() { + assert(entityTreeIsLocked()); + if (_body && _entity) { + _entity->clearDirtyFlags(); + } +} + // virtual quint8 EntityMotionState::getSimulationPriority() const { if (_entity) { diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 009c931dba..f3a2e80070 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -29,8 +29,8 @@ public: virtual ~EntityMotionState(); void updateServerPhysicsVariables(); - virtual void handleEasyChanges(uint32_t flags, PhysicsEngine* engine); - virtual void handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine); + virtual bool handleEasyChanges(uint32_t flags, PhysicsEngine* engine); + virtual bool handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine); /// \return MOTION_TYPE_DYNAMIC or MOTION_TYPE_STATIC based on params set in EntityItem virtual MotionType computeObjectMotionType() const; @@ -48,7 +48,8 @@ public: bool shouldSendUpdate(uint32_t simulationStep, const QUuid& sessionID); void sendUpdate(OctreeEditPacketSender* packetSender, const QUuid& sessionID, uint32_t step); - virtual uint32_t getAndClearIncomingDirtyFlags(); + virtual uint32_t getIncomingDirtyFlags(); + virtual void clearIncomingDirtyFlags(); void incrementAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount++; } void resetAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount = 0; } @@ -91,6 +92,7 @@ protected: bool entityTreeIsLocked() const; #endif + virtual bool isReadyToComputeShape(); virtual btCollisionShape* computeNewShape(); virtual void clearObjectBackPointer(); virtual void setMotionType(MotionType motionType); diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index aeec85f7ea..85bddd0495 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -125,7 +125,7 @@ void ObjectMotionState::setRigidBody(btRigidBody* body) { } } -void ObjectMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) { +bool ObjectMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) { if (flags & EntityItem::DIRTY_POSITION) { btTransform worldTrans; if (flags & EntityItem::DIRTY_ROTATION) { @@ -156,11 +156,16 @@ void ObjectMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) if (flags & EntityItem::DIRTY_MASS) { updateBodyMassProperties(); } + + return true; } -void ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine) { +bool ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine) { if (flags & EntityItem::DIRTY_SHAPE) { // make sure the new shape is valid + if (!isReadyToComputeShape()) { + return false; + } btCollisionShape* newShape = computeNewShape(); if (!newShape) { qCDebug(physics) << "Warning: failed to generate new shape!"; @@ -172,7 +177,7 @@ void ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* if (flags & EASY_DIRTY_PHYSICS_FLAGS) { handleEasyChanges(flags, engine); } - return; + return true; } } getShapeManager()->releaseShape(_shape); @@ -192,6 +197,8 @@ void ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* if (flags & HARD_DIRTY_PHYSICS_FLAGS) { engine->reinsertObject(this); } + + return true; } void ObjectMotionState::updateBodyMaterialProperties() { diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 30394ef5fc..1bdf8b6372 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -71,8 +71,8 @@ public: ObjectMotionState(btCollisionShape* shape); ~ObjectMotionState(); - virtual void handleEasyChanges(uint32_t flags, PhysicsEngine* engine); - virtual void handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine); + virtual bool handleEasyChanges(uint32_t flags, PhysicsEngine* engine); + virtual bool handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine); void updateBodyMaterialProperties(); void updateBodyVelocities(); @@ -92,7 +92,8 @@ public: glm::vec3 getBodyAngularVelocity() const; virtual glm::vec3 getObjectLinearVelocityChange() const; - virtual uint32_t getAndClearIncomingDirtyFlags() = 0; + virtual uint32_t getIncomingDirtyFlags() = 0; + virtual void clearIncomingDirtyFlags() = 0; virtual MotionType computeObjectMotionType() const = 0; @@ -132,6 +133,7 @@ public: friend class PhysicsEngine; protected: + virtual bool isReadyToComputeShape() = 0; virtual btCollisionShape* computeNewShape() = 0; void setMotionType(MotionType motionType); diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index f6f02b8573..97cf6a549a 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -173,6 +173,12 @@ VectorOfMotionStates& PhysicalEntitySimulation::getObjectsToAdd() { return _tempVector; } +void PhysicalEntitySimulation::setObjectsToChange(VectorOfMotionStates& objectsToChange) { + for (auto object : objectsToChange) { + _pendingChanges.insert(static_cast(object)); + } +} + VectorOfMotionStates& PhysicalEntitySimulation::getObjectsToChange() { _tempVector.clear(); for (auto stateItr : _pendingChanges) { diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index 9c439c53b2..dc65128ac5 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -46,6 +46,7 @@ protected: // only called by EntitySimulation public: VectorOfMotionStates& getObjectsToDelete(); VectorOfMotionStates& getObjectsToAdd(); + void setObjectsToChange(VectorOfMotionStates& objectsToChange); VectorOfMotionStates& getObjectsToChange(); void handleOutgoingChanges(VectorOfMotionStates& motionStates, const QUuid& sessionID); diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 022468633f..e8e6f68c6b 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -140,7 +140,7 @@ void PhysicsEngine::addObject(ObjectMotionState* motionState) { int16_t group = motionState->computeCollisionGroup(); _dynamicsWorld->addRigidBody(body, group, getCollisionMask(group)); - motionState->getAndClearIncomingDirtyFlags(); + motionState->clearIncomingDirtyFlags(); } void PhysicsEngine::removeObject(ObjectMotionState* object) { @@ -188,15 +188,23 @@ void PhysicsEngine::addObjects(VectorOfMotionStates& objects) { } } -void PhysicsEngine::changeObjects(VectorOfMotionStates& objects) { +VectorOfMotionStates PhysicsEngine::changeObjects(VectorOfMotionStates& objects) { + VectorOfMotionStates stillNeedChange; for (auto object : objects) { - uint32_t flags = object->getAndClearIncomingDirtyFlags() & DIRTY_PHYSICS_FLAGS; + uint32_t flags = object->getIncomingDirtyFlags() & DIRTY_PHYSICS_FLAGS; if (flags & HARD_DIRTY_PHYSICS_FLAGS) { - object->handleHardAndEasyChanges(flags, this); + if (object->handleHardAndEasyChanges(flags, this)) { + object->clearIncomingDirtyFlags(); + stillNeedChange.push_back(object); + } } else if (flags & EASY_DIRTY_PHYSICS_FLAGS) { - object->handleEasyChanges(flags, this); + if (object->handleEasyChanges(flags, this)) { + object->clearIncomingDirtyFlags(); + stillNeedChange.push_back(object); + } } } + return stillNeedChange; } void PhysicsEngine::reinsertObject(ObjectMotionState* object) { diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index a974a02e51..67b38323cc 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -60,7 +60,7 @@ public: void deleteObjects(VectorOfMotionStates& objects); void deleteObjects(SetOfMotionStates& objects); // only called during teardown void addObjects(VectorOfMotionStates& objects); - void changeObjects(VectorOfMotionStates& objects); + VectorOfMotionStates changeObjects(VectorOfMotionStates& objects); void reinsertObject(ObjectMotionState* object); void stepSimulation(); From 2c840ae0ccb8a1f82253a6c2fb4b3950db4f2767 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 21 Aug 2015 11:02:11 -0700 Subject: [PATCH 04/22] change default polyvox surface to edged-cubic and turn some voxels on when a polyvox is first rezzed --- examples/edit.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/examples/edit.js b/examples/edit.js index 60666a54a5..f2260f14c2 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -448,12 +448,31 @@ var toolBar = (function () { } if (newPolyVoxButton === toolBar.clicked(clickedOverlay)) { - createNewEntity({ + var polyVoxId = createNewEntity({ type: "PolyVox", dimensions: { x: 10, y: 10, z: 10 }, voxelVolumeSize: {x:16, y:16, z:16}, voxelSurfaceStyle: 2 }); + for (var x = 1; x <= 14; x++) { + Entities.setVoxel(polyVoxId, {x: x, y: 1, z: 1}, 255); + Entities.setVoxel(polyVoxId, {x: x, y: 14, z: 1}, 255); + Entities.setVoxel(polyVoxId, {x: x, y: 1, z: 14}, 255); + Entities.setVoxel(polyVoxId, {x: x, y: 14, z: 14}, 255); + } + for (var y = 2; y <= 13; y++) { + Entities.setVoxel(polyVoxId, {x: 1, y: y, z: 1}, 255); + Entities.setVoxel(polyVoxId, {x: 14, y: y, z: 1}, 255); + Entities.setVoxel(polyVoxId, {x: 1, y: y, z: 14}, 255); + Entities.setVoxel(polyVoxId, {x: 14, y: y, z: 14}, 255); + } + for (var z = 2; z <= 13; z++) { + Entities.setVoxel(polyVoxId, {x: 1, y: 1, z: z}, 255); + Entities.setVoxel(polyVoxId, {x: 14, y: 1, z: z}, 255); + Entities.setVoxel(polyVoxId, {x: 1, y: 14, z: z}, 255); + Entities.setVoxel(polyVoxId, {x: 14, y: 14, z: z}, 255); + } + return true; } From 99374167299f987484142426d1b5d1963118461f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 21 Aug 2015 11:23:13 -0700 Subject: [PATCH 05/22] attempt to move some time-consuming polyvox stuff off of the main thread --- .../src/RenderablePolyVoxEntityItem.cpp | 271 +++++++++++++----- .../src/RenderablePolyVoxEntityItem.h | 29 +- 2 files changed, 221 insertions(+), 79 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 44e7bae53a..459f901a2d 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -9,8 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#define WANT_DEBUG 1 + #include #include +#include #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push @@ -64,7 +67,9 @@ RenderablePolyVoxEntityItem::RenderablePolyVoxEntityItem(const EntityItemID& ent } RenderablePolyVoxEntityItem::~RenderablePolyVoxEntityItem() { + _volDataLock.lockForWrite(); delete _volData; + _volDataLock.unlock(); } bool inUserBounds(const PolyVox::SimpleVolume* vol, PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle, @@ -99,7 +104,9 @@ bool inBounds(const PolyVox::SimpleVolume* vol, int x, int y, int z) { void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { + _volDataLock.lockForWrite(); if (_volData && voxelVolumeSize == _voxelVolumeSize) { + _volDataLock.unlock(); return; } @@ -150,6 +157,8 @@ void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) } } + _volDataLock.unlock(); + // 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 @@ -163,16 +172,19 @@ void RenderablePolyVoxEntityItem::updateVoxelSurfaceStyle(PolyVoxSurfaceStyle vo bool willBeEdged = (voxelSurfaceStyle == SURFACE_EDGED_CUBIC || voxelSurfaceStyle == SURFACE_EDGED_MARCHING_CUBES); if (wasEdged != willBeEdged) { + _volDataLock.lockForWrite(); if (_volData) { delete _volData; } _volData = nullptr; _voxelSurfaceStyle = voxelSurfaceStyle; + _volDataLock.unlock(); setVoxelVolumeSize(_voxelVolumeSize); } else { _voxelSurfaceStyle = voxelSurfaceStyle; } - _needsModelReload = true; + bumpDataVersion(); + getModel(); } void RenderablePolyVoxEntityItem::setVoxelData(QByteArray voxelData) { @@ -224,8 +236,10 @@ glm::mat4 RenderablePolyVoxEntityItem::worldToVoxelMatrix() const { } uint8_t RenderablePolyVoxEntityItem::getVoxel(int x, int y, int z) { + _volDataLock.lockForRead(); assert(_volData); if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) { + _volDataLock.unlock(); return 0; } @@ -233,48 +247,55 @@ uint8_t RenderablePolyVoxEntityItem::getVoxel(int x, int y, int z) { // voxels all around the requested voxel space. Having the empty voxels around // the edges changes how the surface extractor behaves. + uint8_t result; if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC || _voxelSurfaceStyle == SURFACE_EDGED_MARCHING_CUBES) { - return _volData->getVoxelAt(x + 1, y + 1, z + 1); + result = _volData->getVoxelAt(x + 1, y + 1, z + 1); + } else { + result = _volData->getVoxelAt(x, y, z); } - return _volData->getVoxelAt(x, y, z); + + _volDataLock.unlock(); + return result; } bool RenderablePolyVoxEntityItem::setVoxelInternal(int x, int y, int z, uint8_t toValue) { // set a voxel without recompressing the voxel data - assert(_volData); bool result = false; if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) { - return false; + return result; } result = updateOnCount(x, y, z, toValue); + _volDataLock.lockForWrite(); + assert(_volData); if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC || _voxelSurfaceStyle == SURFACE_EDGED_MARCHING_CUBES) { _volData->setVoxelAt(x + 1, y + 1, z + 1, toValue); } else { _volData->setVoxelAt(x, y, z, toValue); } + _volDataLock.unlock(); return result; } -void RenderablePolyVoxEntityItem::clearEdges() { - // if we are in an edged mode, make sure the outside surfaces are zeroed out. - if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC || _voxelSurfaceStyle == SURFACE_EDGED_MARCHING_CUBES) { - for (int z = 0; z < _volData->getDepth(); z++) { - for (int y = 0; y < _volData->getHeight(); y++) { - for (int x = 0; x < _volData->getWidth(); x++) { - if (x == 0 || y == 0 || z == 0 || - x == _volData->getWidth() - 1 || - y == _volData->getHeight() - 1 || - z == _volData->getDepth() - 1) { - _volData->setVoxelAt(x, y, z, 0); - } - } - } - } - } -} +// void RenderablePolyVoxEntityItem::clearEdges() { +// // if we are in an edged mode, make sure the outside surfaces are zeroed out. +// if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC || _voxelSurfaceStyle == SURFACE_EDGED_MARCHING_CUBES) { +// for (int z = 0; z < _volData->getDepth(); z++) { +// for (int y = 0; y < _volData->getHeight(); y++) { +// for (int x = 0; x < _volData->getWidth(); x++) { +// if (x == 0 || y == 0 || z == 0 || +// x == _volData->getWidth() - 1 || +// y == _volData->getHeight() - 1 || +// z == _volData->getDepth() - 1) { +// _volData->setVoxelAt(x, y, z, 0); +// } +// } +// } +// } +// } +// } bool RenderablePolyVoxEntityItem::setVoxel(int x, int y, int z, uint8_t toValue) { if (_locked) { @@ -413,7 +434,7 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o { // TODO -- correctly pick against marching-cube generated meshes - if (_needsModelReload || !precisionPicking) { + if (getDataVersion() != _modelVersion || !precisionPicking) { // just intersect with bounding box return true; } @@ -435,8 +456,11 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o PolyVox::Vector3DFloat endPoint(farInVoxel.x, farInVoxel.y, farInVoxel.z); PolyVox::RaycastResult raycastResult; + + _volDataLock.lockForRead(); RaycastFunctor callback(_volData); raycastResult = PolyVox::raycastWithEndpoints(_volData, startPoint, endPoint, callback); + _volDataLock.unlock(); if (raycastResult == PolyVox::RaycastResults::Completed) { // the ray completed its path -- nothing was hit. @@ -548,7 +572,8 @@ void RenderablePolyVoxEntityItem::compressVolumeData() { } _dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS; - _needsModelReload = true; + bumpDataVersion(); + getModel(); #ifdef WANT_DEBUG qDebug() << "RenderablePolyVoxEntityItem::compressVolumeData" << (usecTimestampNow() - startTime) << getName() @@ -598,7 +623,7 @@ void RenderablePolyVoxEntityItem::decompressVolumeData() { } } } - clearEdges(); + // clearEdges(); #ifdef WANT_DEBUG qDebug() << "--------------- voxel decompress ---------------"; @@ -606,7 +631,7 @@ void RenderablePolyVoxEntityItem::decompressVolumeData() { #endif _dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS; - _needsModelReload = true; + bumpDataVersion(); getModel(); #ifdef WANT_DEBUG @@ -622,41 +647,70 @@ ShapeType RenderablePolyVoxEntityItem::getShapeType() const { return SHAPE_TYPE_NONE; } - bool RenderablePolyVoxEntityItem::isReadyToComputeShape() { - if (_needsModelReload) { + #ifdef WANT_DEBUG + qDebug() << "RenderablePolyVoxEntityItem::isReadyToComputeShape" << getName() + << ", _shapeInfoWorkerRunning is" << _shapeInfoWorkerRunning + << ", _shapeInfoReady is" << _shapeInfoReady; + #endif + + getModel(); + + if (getDataVersion() != _modelVersion) { + qDebug() << "RenderablePolyVoxEntityItem::isReadyToComputeShape getModel running"; return false; } - #ifdef WANT_DEBUG - qDebug() << "RenderablePolyVoxEntityItem::isReadyToComputeShape" << (!_needsModelReload); - #endif - return true; + if (_shapeInfoWorkerRunning) { + if (_shapeInfoReady) { + bool result = _shapeInfoWorker.result(); + _shapeInfoWorkerRunning = false; + qDebug() << "RenderablePolyVoxEntityItem::isReadyToComputeShape returning" << result; + return result; + } else { + qDebug() << "RenderablePolyVoxEntityItem::isReadyToComputeShape _shapeInfo isn't ready"; + return false; + } + } + + _shapeInfoReady = false; + _shapeInfoWorkerRunning = true; + _shapeInfoWorker = QtConcurrent::run(this, &RenderablePolyVoxEntityItem::computeShapeInfoWorker); + qDebug() << "RenderablePolyVoxEntityItem::isReadyToComputeShape spawning thread."; + return false; } -void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) { - #ifdef WANT_DEBUG - auto startTime = usecTimestampNow(); - #endif +bool RenderablePolyVoxEntityItem::computeShapeInfoWorker() { + // #ifdef WANT_DEBUG + // auto startTime = usecTimestampNow(); + // #endif + + // qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfoWorker starting" << getName(); ShapeType type = getShapeType(); if (type != SHAPE_TYPE_COMPOUND) { - EntityItem::computeShapeInfo(info); - #ifdef WANT_DEBUG - qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfo -- shape isn't compound."; - #endif - return; + EntityItem::computeShapeInfo(_shapeInfo); + // #ifdef WANT_DEBUG + // qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfo -- shape isn't compound." << getName(); + // #endif + _shapeInfoReady = true; + // qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfoWorker ending, type != SHAPE_TYPE_COMPOUND" << getName(); + return true; } - _points.clear(); + QVector> points; AABox box; glm::mat4 vtoM = voxelToLocalMatrix(); if (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_MARCHING_CUBES || _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES) { + /* pull each triangle in the mesh into a polyhedron which can be collided with */ unsigned int i = 0; - /* pull top-facing triangles into polyhedrons so they can be walked on */ - const model::MeshPointer& mesh = _modelGeometry.getMesh(); + + _modelGeometryLock.lockForRead(); + const model::MeshPointer mesh = _modelGeometry.getMesh(); + _modelGeometryLock.unlock(); + const gpu::BufferView vertexBufferView = mesh->getVertexBuffer(); const gpu::BufferView& indexBufferView = mesh->getIndexBuffer(); gpu::BufferView::Iterator it = indexBufferView.cbegin(); @@ -690,9 +744,9 @@ void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) { pointsInPart << p3Model; // add next convex hull QVector newMeshPoints; - _points << newMeshPoints; + points << newMeshPoints; // add points to the new convex hull - _points[i++] << pointsInPart; + points[i++] << pointsInPart; } } else { unsigned int i = 0; @@ -751,30 +805,48 @@ void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) { // add next convex hull QVector newMeshPoints; - _points << newMeshPoints; + points << newMeshPoints; // add points to the new convex hull - _points[i++] << pointsInPart; + points[i++] << pointsInPart; } } } } } - if (_points.isEmpty()) { - EntityItem::computeShapeInfo(info); - #ifdef WANT_DEBUG - qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfo -- no points."; - #endif - return; + if (points.isEmpty()) { + EntityItem::computeShapeInfo(_shapeInfo); + // #ifdef WANT_DEBUG + // qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfo -- no points."; + // #endif + _shapeInfoReady = true; + // qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfoWorker ending, no points" << getName(); + return true; } glm::vec3 collisionModelDimensions = box.getDimensions(); QByteArray b64 = _voxelData.toBase64(); - info.setParams(type, collisionModelDimensions, QString(b64)); - info.setConvexHulls(_points); - #ifdef WANT_DEBUG - qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfo" << (usecTimestampNow() - startTime) << getName(); - #endif + _shapeInfo.setParams(type, collisionModelDimensions, QString(b64)); + _shapeInfo.setConvexHulls(points); + + // #ifdef WANT_DEBUG + // qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfo" << (usecTimestampNow() - startTime) << getName(); + // #endif + + _shapeInfoReady = true; + // qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfoWorker ending, success" << getName(); + return true; +} + +void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) { + qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfo _shapeInfoReady is" << _shapeInfoReady; + + if (_shapeInfoReady) { + info = _shapeInfo; + } else { + qWarning() << "RenderablePolyVoxEntityItem::computeShapeInfo called when _shapeInfoReady was false"; + assert(false); + } } void RenderablePolyVoxEntityItem::setXTextureURL(QString xTextureURL) { @@ -789,14 +861,31 @@ void RenderablePolyVoxEntityItem::setZTextureURL(QString zTextureURL) { PolyVoxEntityItem::setZTextureURL(zTextureURL); } -void RenderablePolyVoxEntityItem::getModel() { - #ifdef WANT_DEBUG - auto startTime = usecTimestampNow(); - #endif +quint64 RenderablePolyVoxEntityItem::getDataVersion() const { + _dataVersionLock.lockForRead(); + auto result = _dataVersion; + _dataVersionLock.unlock(); + return result; +} + +void RenderablePolyVoxEntityItem::bumpDataVersion() { + qDebug() << "data version going from" << _dataVersion << "to" << (_dataVersion + 1); + _dataVersionLock.lockForWrite(); + _dataVersion ++; + _dataVersionLock.unlock(); +} + +bool RenderablePolyVoxEntityItem::getModelWorker() { + model::Mesh* mesh = new model::Mesh(); + model::MeshPointer meshPtr(mesh); // A mesh object to hold the result of surface extraction PolyVox::SurfaceMesh polyVoxMesh; + _volDataLock.lockForRead(); + _dataVersionLock.lockForRead(); + _modelWorkerVersion = _dataVersion; + _dataVersionLock.unlock(); switch (_voxelSurfaceStyle) { case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: { @@ -813,10 +902,9 @@ void RenderablePolyVoxEntityItem::getModel() { break; } } + _volDataLock.unlock(); // convert PolyVox mesh to a Sam mesh - auto mesh = _modelGeometry.getMesh(); - const std::vector& vecIndices = polyVoxMesh.getIndices(); auto indexBuffer = std::make_shared(vecIndices.size() * sizeof(uint32_t), (gpu::Byte*)vecIndices.data()); @@ -824,7 +912,6 @@ void RenderablePolyVoxEntityItem::getModel() { 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 = std::make_shared(vecVertices.size() * sizeof(PolyVox::PositionMaterialNormal), (gpu::Byte*)vecVertices.data()); @@ -842,13 +929,41 @@ void RenderablePolyVoxEntityItem::getModel() { sizeof(PolyVox::PositionMaterialNormal), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW))); - _needsModelReload = false; + _modelGeometryLock.lockForWrite(); + _modelGeometry.setMesh(meshPtr); + _modelGeometryLock.unlock(); - #ifdef WANT_DEBUG - qDebug() << "RenderablePolyVoxEntityItem::getModel" << (usecTimestampNow() - startTime) << getName() - << "vecIndices.size() =" << vecIndices.size() - << "vecVertices.size() =" << vecVertices.size(); - #endif + qDebug() << "******* RenderablePolyVoxEntityItem::getModelWorker is done *********" << _modelVersion << _modelWorkerVersion; + + _getModelWorkerDone = true; + _modelVersion = _modelWorkerVersion; + return true; +} + +void RenderablePolyVoxEntityItem::getModel() { + // if (QThread::currentThread() != thread()) { + // QMetaObject::invokeMethod(this, "getModel"); + // return; + // } + + if (getDataVersion() == _modelVersion) { + return; + } + + qDebug() << "RenderablePolyVoxEntityItem::getModel _getModelWorkerRunning =" << _getModelWorkerRunning; + + if (_getModelWorkerRunning) { + if (_getModelWorkerDone) { + if (!_getModelWorker.result()) { + qWarning() << "RenderablePolyVoxEntityItem::getModel worker failed"; + } + _getModelWorkerRunning = false; + } + return; + } + _getModelWorkerRunning = true; + _getModelWorkerDone = false; + _getModelWorker = QtConcurrent::run(this, &RenderablePolyVoxEntityItem::getModelWorker); } void RenderablePolyVoxEntityItem::render(RenderArgs* args) { @@ -856,6 +971,8 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { assert(getType() == EntityTypes::PolyVox); Q_ASSERT(args->_batch); + getModel(); + if (!_pipeline) { gpu::ShaderPointer vertexShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(polyvox_vert))); gpu::ShaderPointer pixelShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(polyvox_frag))); @@ -876,14 +993,18 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { _pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); } - if (_needsModelReload) { - getModel(); - } +// if (_needsModelReload) { +// getModel(); +// // return; +// } gpu::Batch& batch = *args->_batch; batch.setPipeline(_pipeline); + _modelGeometryLock.lockForRead(); auto mesh = _modelGeometry.getMesh(); + _modelGeometryLock.unlock(); + Transform transform(voxelToWorldMatrix()); batch.setModelTransform(transform); batch.setInputFormat(mesh->getVertexFormat()); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index e2fcdedc31..40375ce337 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -12,6 +12,8 @@ #ifndef hifi_RenderablePolyVoxEntityItem_h #define hifi_RenderablePolyVoxEntityItem_h +#include + #include #include @@ -36,7 +38,6 @@ namespace render { template <> void payloadRender(const PolyVoxPayload::Pointer& payload, RenderArgs* args); } - class RenderablePolyVoxEntityItem : public PolyVoxEntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); @@ -64,7 +65,10 @@ public: bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject, bool precisionPicking) const; + quint64 getDataVersion() const; + void bumpDataVersion(); void getModel(); + bool getModelWorker(); virtual void setVoxelData(QByteArray voxelData); @@ -78,6 +82,7 @@ public: virtual ShapeType getShapeType() const; virtual bool isReadyToComputeShape(); virtual void computeShapeInfo(ShapeInfo& info); + virtual bool computeShapeInfoWorker(); virtual glm::vec3 voxelCoordsToWorldCoords(glm::vec3& voxelCoords) const; virtual glm::vec3 worldCoordsToVoxelCoords(glm::vec3& worldCoords) const; @@ -116,10 +121,21 @@ private: void clearEdges(); PolyVox::SimpleVolume* _volData = nullptr; - model::Geometry _modelGeometry; - bool _needsModelReload = true; + mutable QReadWriteLock _volDataLock; // lock for _volData and _dataVersion - QVector> _points; // XXX + model::Geometry _modelGeometry; + mutable QReadWriteLock _modelGeometryLock; + + // set data version higher than model version so that getModelWorker must complete once before they are equal. + quint64 _dataVersion = 1; + mutable QReadWriteLock _dataVersionLock; + + quint64 _modelVersion = 0; + bool _getModelWorkerRunning = false; + bool _getModelWorkerDone = false; + quint64 _modelWorkerVersion = 0; + + QFuture _getModelWorker; NetworkTexturePointer _xTexture; NetworkTexturePointer _yTexture; @@ -130,6 +146,11 @@ private: const int MATERIAL_GPU_SLOT = 3; render::ItemID _myItem; static gpu::PipelinePointer _pipeline; + + ShapeInfo _shapeInfo; + bool _shapeInfoReady = false; + bool _shapeInfoWorkerRunning = false; + QFuture _shapeInfoWorker; }; From dc31c7e233cf48ccbaee7baa23f52513fdea703e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 23 Aug 2015 08:14:05 -0700 Subject: [PATCH 06/22] back out previous attempt at theading polyvox code. lay groundwork for threading that can be reasoned about --- .../src/RenderablePolyVoxEntityItem.cpp | 1009 ++++++++--------- .../src/RenderablePolyVoxEntityItem.h | 86 +- .../entities/src/EntityScriptingInterface.cpp | 13 - libraries/entities/src/PolyVoxEntityItem.cpp | 9 +- libraries/entities/src/PolyVoxEntityItem.h | 6 +- .../physics/src/PhysicalEntitySimulation.h | 2 + 6 files changed, 525 insertions(+), 600 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 459f901a2d..1b7d53549f 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -9,9 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#define WANT_DEBUG 1 - #include +#include #include #include @@ -36,7 +35,6 @@ #include #include #include -#include #include #include "model/Geometry.h" @@ -45,6 +43,8 @@ #include "polyvox_vert.h" #include "polyvox_frag.h" #include "RenderablePolyVoxEntityItem.h" +#include "EntityEditPacketSender.h" +#include "PhysicalEntitySimulation.h" gpu::PipelinePointer RenderablePolyVoxEntityItem::_pipeline = nullptr; const float MARCHING_CUBE_COLLISION_HULL_OFFSET = 0.5; @@ -58,143 +58,84 @@ RenderablePolyVoxEntityItem::RenderablePolyVoxEntityItem(const EntityItemID& ent PolyVoxEntityItem(entityItemID, properties), _xTexture(nullptr), _yTexture(nullptr), - _zTexture(nullptr) { + _zTexture(nullptr), + _async(DEFAULT_VOXEL_SURFACE_STYLE, DEFAULT_VOXEL_VOLUME_SIZE, this) { + model::Mesh* mesh = new model::Mesh(); model::MeshPointer meshPtr(mesh); - _modelGeometry.setMesh(meshPtr); - - setVoxelVolumeSize(_voxelVolumeSize); + receiveNewMesh(meshPtr, _modelVersion); } RenderablePolyVoxEntityItem::~RenderablePolyVoxEntityItem() { - _volDataLock.lockForWrite(); - delete _volData; - _volDataLock.unlock(); -} - -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: - case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: - 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::receiveNewVoxelData(QByteArray newVoxelData, uint64_t dataVersion) { + _voxelData = newVoxelData; + _dataVersion = dataVersion; + + auto now = usecTimestampNow(); + setLastEdited(now); + setLastBroadcast(now); + + EntityItemProperties properties = getProperties(); + properties.setVoxelDataDirty(); + properties.setLastEdited(now); + + EntityTreeElement* element = getElement(); + EntityTree* tree = element ? element->getTree() : nullptr; + EntitySimulation* simulation = tree ? tree->getSimulation() : nullptr; + PhysicalEntitySimulation* peSimulation = static_cast(simulation); + EntityEditPacketSender* packetSender = peSimulation ? peSimulation->getPacketSender() : nullptr; + if (packetSender) { + packetSender->queueEditEntityMessage(PacketType::EntityEdit, _id, properties); + } } -void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { - _volDataLock.lockForWrite(); - if (_volData && voxelVolumeSize == _voxelVolumeSize) { - _volDataLock.unlock(); - return; - } +void RenderablePolyVoxEntityItem::receiveNewMesh(model::MeshPointer newMeshPtr, uint64_t meshVersion) { + _dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS; + _modelGeometryLock.lockForWrite(); + _modelGeometry.setMesh(newMeshPtr); + _meshVersion = meshVersion; + _modelGeometryLock.unlock(); - #ifdef WANT_DEBUG - qDebug() << "resetting voxel-space size" << voxelVolumeSize.x << voxelVolumeSize.y << voxelVolumeSize.z; - #endif - - PolyVoxEntityItem::setVoxelVolumeSize(voxelVolumeSize); - - if (_volData) { - delete _volData; - } - - _onCount = 0; - - if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC || _voxelSurfaceStyle == SURFACE_EDGED_MARCHING_CUBES) { - // 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 voxel-space 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); - } - } - } - - _volDataLock.unlock(); - - // 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::updateVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) { - // if we are switching to or from "edged" we need to force a resize of _volData. - bool wasEdged = (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC || _voxelSurfaceStyle == SURFACE_EDGED_MARCHING_CUBES); - bool willBeEdged = (voxelSurfaceStyle == SURFACE_EDGED_CUBIC || voxelSurfaceStyle == SURFACE_EDGED_MARCHING_CUBES); - - if (wasEdged != willBeEdged) { - _volDataLock.lockForWrite(); - if (_volData) { - delete _volData; - } - _volData = nullptr; - _voxelSurfaceStyle = voxelSurfaceStyle; - _volDataLock.unlock(); - setVoxelVolumeSize(_voxelVolumeSize); - } else { - _voxelSurfaceStyle = voxelSurfaceStyle; - } - bumpDataVersion(); - getModel(); + computeShapeInfoWorker(); } void RenderablePolyVoxEntityItem::setVoxelData(QByteArray voxelData) { if (voxelData == _voxelData) { return; } - PolyVoxEntityItem::setVoxelData(voxelData); - decompressVolumeData(); + + _voxelData = voxelData; + _async.decompressVolumeData(_voxelData, ++_modelVersion); } +void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { + if (voxelVolumeSize == _voxelVolumeSize) { + return; + } + + PolyVoxEntityItem::setVoxelVolumeSize(voxelVolumeSize); + _async.setVoxelVolumeSize(_voxelVolumeSize); + // 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. + _async.decompressVolumeData(_voxelData, ++_modelVersion); +} + +void RenderablePolyVoxEntityItem::setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) { + if (_voxelSurfaceStyle == voxelSurfaceStyle) { + return; + } + + _voxelSurfaceStyle = voxelSurfaceStyle; + _async.updateVoxelSurfaceStyle(_voxelSurfaceStyle, ++_modelVersion); +} + + glm::vec3 RenderablePolyVoxEntityItem::getSurfacePositionAdjustment() const { glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units switch (_voxelSurfaceStyle) { @@ -236,118 +177,21 @@ glm::mat4 RenderablePolyVoxEntityItem::worldToVoxelMatrix() const { } uint8_t RenderablePolyVoxEntityItem::getVoxel(int x, int y, int z) { - _volDataLock.lockForRead(); - assert(_volData); - if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) { - _volDataLock.unlock(); - 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. - - uint8_t result; - if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC || _voxelSurfaceStyle == SURFACE_EDGED_MARCHING_CUBES) { - result = _volData->getVoxelAt(x + 1, y + 1, z + 1); - } else { - result = _volData->getVoxelAt(x, y, z); - } - - _volDataLock.unlock(); - return result; + return _async.getVoxel(x, y, z); } -bool RenderablePolyVoxEntityItem::setVoxelInternal(int x, int y, int z, uint8_t toValue) { - // set a voxel without recompressing the voxel data - bool result = false; - if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) { - return result; - } - - result = updateOnCount(x, y, z, toValue); - - _volDataLock.lockForWrite(); - assert(_volData); - if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC || _voxelSurfaceStyle == SURFACE_EDGED_MARCHING_CUBES) { - _volData->setVoxelAt(x + 1, y + 1, z + 1, toValue); - } else { - _volData->setVoxelAt(x, y, z, toValue); - } - - _volDataLock.unlock(); - return result; -} - -// void RenderablePolyVoxEntityItem::clearEdges() { -// // if we are in an edged mode, make sure the outside surfaces are zeroed out. -// if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC || _voxelSurfaceStyle == SURFACE_EDGED_MARCHING_CUBES) { -// for (int z = 0; z < _volData->getDepth(); z++) { -// for (int y = 0; y < _volData->getHeight(); y++) { -// for (int x = 0; x < _volData->getWidth(); x++) { -// if (x == 0 || y == 0 || z == 0 || -// x == _volData->getWidth() - 1 || -// y == _volData->getHeight() - 1 || -// z == _volData->getDepth() - 1) { -// _volData->setVoxelAt(x, y, z, 0); -// } -// } -// } -// } -// } -// } - bool RenderablePolyVoxEntityItem::setVoxel(int x, int y, int z, uint8_t toValue) { if (_locked) { return false; } - bool result = setVoxelInternal(x, y, z, toValue); - if (result) { - compressVolumeData(); - } - return result; -} - -bool RenderablePolyVoxEntityItem::updateOnCount(int x, int y, int z, uint8_t toValue) { - // keep _onCount up to date - if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) { - return false; - } - - uint8_t uVoxelValue = getVoxel(x, y, z); - if (toValue != 0) { - if (uVoxelValue == 0) { - _onCount++; - return true; - } - } else { - // toValue == 0 - if (uVoxelValue != 0) { - _onCount--; - assert(_onCount >= 0); - return true; - } - } - return false; + return _async.setVoxel(x, y, z, toValue, ++_modelVersion); } bool RenderablePolyVoxEntityItem::setAll(uint8_t toValue) { - bool result = false; if (_locked) { return false; } - - for (int z = 0; z < _voxelVolumeSize.z; z++) { - for (int y = 0; y < _voxelVolumeSize.y; y++) { - for (int x = 0; x < _voxelVolumeSize.x; x++) { - result |= setVoxelInternal(x, y, z, toValue); - } - } - } - if (result) { - compressVolumeData(); - } - return result; + return _async.setAll(toValue, ++_modelVersion); } bool RenderablePolyVoxEntityItem::setVoxelInVolume(glm::vec3 position, uint8_t toValue) { @@ -360,30 +204,10 @@ bool RenderablePolyVoxEntityItem::setVoxelInVolume(glm::vec3 position, uint8_t t } bool RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue) { - bool result = false; if (_locked) { return false; } - - // This three-level for loop iterates over every voxel in the volume - 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 + 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) { - result |= setVoxelInternal(x, y, z, toValue); - } - } - } - } - if (result) { - compressVolumeData(); - } - return result; + return _async.setSphereInVolume(center, radius, toValue, ++_modelVersion); } bool RenderablePolyVoxEntityItem::setSphere(glm::vec3 centerWorldCoords, float radiusWorldCoords, uint8_t toValue) { @@ -402,6 +226,13 @@ public: _result(glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)), _vol(vol) { } + + static 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()); + } + + bool operator()(PolyVox::SimpleVolume::Sampler& sampler) { PolyVox::Vector3DInt32 positionIndex = sampler.getPosition(); @@ -433,8 +264,7 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o bool precisionPicking) const { // TODO -- correctly pick against marching-cube generated meshes - - if (getDataVersion() != _modelVersion || !precisionPicking) { + if (!precisionPicking) { // just intersect with bounding box return true; } @@ -452,24 +282,13 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o 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; - - _volDataLock.lockForRead(); - RaycastFunctor callback(_volData); - raycastResult = PolyVox::raycastWithEndpoints(_volData, startPoint, endPoint, callback); - _volDataLock.unlock(); - + glm::vec4 result; + PolyVox::RaycastResult raycastResult = _async.doRayCast(originInVoxel, farInVoxel, result); if (raycastResult == PolyVox::RaycastResults::Completed) { // the ray completed its path -- nothing was hit. return false; } - // result is in voxel-space coordinates. - glm::vec4 result = callback._result - glm::vec4(0.5f, 0.5f, 0.5f, 0.0f); - // set up ray tests against each face of the voxel. glm::vec3 minXPosition = glm::vec3(vtwMatrix * (result + glm::vec4(0.0f, 0.5f, 0.5f, 0.0f))); glm::vec3 maxXPosition = glm::vec3(vtwMatrix * (result + glm::vec4(1.0f, 0.5f, 0.5f, 0.0f))); @@ -523,125 +342,9 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o return true; } - -// 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() { - #ifdef WANT_DEBUG - auto startTime = usecTimestampNow(); - #endif - - 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 < 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; - 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; - } 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."; - #endif - // revert the active voxel-space to the last version that fit. - decompressVolumeData(); - } - - _dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS; - bumpDataVersion(); - getModel(); - - #ifdef WANT_DEBUG - qDebug() << "RenderablePolyVoxEntityItem::compressVolumeData" << (usecTimestampNow() - startTime) << getName() - << voxelXSize << voxelYSize << voxelZSize - << "raw-size =" << rawSize << " compressed-size =" << newVoxelData.size(); - #endif -} - - -// take compressed data and expand it into _volData. -void RenderablePolyVoxEntityItem::decompressVolumeData() { - #ifdef WANT_DEBUG - auto startTime = usecTimestampNow(); - #endif - - QDataStream reader(_voxelData); - quint16 voxelXSize, voxelYSize, voxelZSize; - reader >> voxelXSize; - reader >> voxelYSize; - reader >> voxelZSize; - - 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); - - 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; - setVoxelInternal(x, y, z, uncompressedData[uncompressedIndex]); - } - } - } - // clearEdges(); - - #ifdef WANT_DEBUG - qDebug() << "--------------- voxel decompress ---------------"; - qDebug() << "raw-size =" << rawSize << " compressed-size =" << _voxelData.size(); - #endif - - _dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS; - bumpDataVersion(); - getModel(); - - #ifdef WANT_DEBUG - qDebug() << "RenderablePolyVoxEntityItem::decompressVolumeData" << (usecTimestampNow() - startTime) << getName(); - #endif -} - // virtual ShapeType RenderablePolyVoxEntityItem::getShapeType() const { - if (_onCount > 0) { + if (_async.getOnCount() > 0) { return SHAPE_TYPE_COMPOUND; } return SHAPE_TYPE_NONE; @@ -649,52 +352,16 @@ ShapeType RenderablePolyVoxEntityItem::getShapeType() const { bool RenderablePolyVoxEntityItem::isReadyToComputeShape() { #ifdef WANT_DEBUG - qDebug() << "RenderablePolyVoxEntityItem::isReadyToComputeShape" << getName() - << ", _shapeInfoWorkerRunning is" << _shapeInfoWorkerRunning - << ", _shapeInfoReady is" << _shapeInfoReady; + qDebug() << "RenderablePolyVoxEntityItem::isReadyToComputeShape" << getName() << _shapeVersion << _modelVersion; #endif - getModel(); - - if (getDataVersion() != _modelVersion) { - qDebug() << "RenderablePolyVoxEntityItem::isReadyToComputeShape getModel running"; - return false; - } - - if (_shapeInfoWorkerRunning) { - if (_shapeInfoReady) { - bool result = _shapeInfoWorker.result(); - _shapeInfoWorkerRunning = false; - qDebug() << "RenderablePolyVoxEntityItem::isReadyToComputeShape returning" << result; - return result; - } else { - qDebug() << "RenderablePolyVoxEntityItem::isReadyToComputeShape _shapeInfo isn't ready"; - return false; - } - } - - _shapeInfoReady = false; - _shapeInfoWorkerRunning = true; - _shapeInfoWorker = QtConcurrent::run(this, &RenderablePolyVoxEntityItem::computeShapeInfoWorker); - qDebug() << "RenderablePolyVoxEntityItem::isReadyToComputeShape spawning thread."; - return false; + return (_shapeVersion == _modelVersion); } bool RenderablePolyVoxEntityItem::computeShapeInfoWorker() { - // #ifdef WANT_DEBUG - // auto startTime = usecTimestampNow(); - // #endif - - // qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfoWorker starting" << getName(); - ShapeType type = getShapeType(); if (type != SHAPE_TYPE_COMPOUND) { EntityItem::computeShapeInfo(_shapeInfo); - // #ifdef WANT_DEBUG - // qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfo -- shape isn't compound." << getName(); - // #endif - _shapeInfoReady = true; - // qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfoWorker ending, type != SHAPE_TYPE_COMPOUND" << getName(); return true; } @@ -816,11 +483,6 @@ bool RenderablePolyVoxEntityItem::computeShapeInfoWorker() { if (points.isEmpty()) { EntityItem::computeShapeInfo(_shapeInfo); - // #ifdef WANT_DEBUG - // qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfo -- no points."; - // #endif - _shapeInfoReady = true; - // qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfoWorker ending, no points" << getName(); return true; } @@ -829,24 +491,12 @@ bool RenderablePolyVoxEntityItem::computeShapeInfoWorker() { _shapeInfo.setParams(type, collisionModelDimensions, QString(b64)); _shapeInfo.setConvexHulls(points); - // #ifdef WANT_DEBUG - // qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfo" << (usecTimestampNow() - startTime) << getName(); - // #endif - - _shapeInfoReady = true; - // qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfoWorker ending, success" << getName(); + _shapeVersion = _modelVersion; return true; } void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) { - qDebug() << "RenderablePolyVoxEntityItem::computeShapeInfo _shapeInfoReady is" << _shapeInfoReady; - - if (_shapeInfoReady) { - info = _shapeInfo; - } else { - qWarning() << "RenderablePolyVoxEntityItem::computeShapeInfo called when _shapeInfoReady was false"; - assert(false); - } + info = _shapeInfo; } void RenderablePolyVoxEntityItem::setXTextureURL(QString xTextureURL) { @@ -861,118 +511,11 @@ void RenderablePolyVoxEntityItem::setZTextureURL(QString zTextureURL) { PolyVoxEntityItem::setZTextureURL(zTextureURL); } -quint64 RenderablePolyVoxEntityItem::getDataVersion() const { - _dataVersionLock.lockForRead(); - auto result = _dataVersion; - _dataVersionLock.unlock(); - return result; -} - -void RenderablePolyVoxEntityItem::bumpDataVersion() { - qDebug() << "data version going from" << _dataVersion << "to" << (_dataVersion + 1); - _dataVersionLock.lockForWrite(); - _dataVersion ++; - _dataVersionLock.unlock(); -} - -bool RenderablePolyVoxEntityItem::getModelWorker() { - model::Mesh* mesh = new model::Mesh(); - model::MeshPointer meshPtr(mesh); - - // A mesh object to hold the result of surface extraction - PolyVox::SurfaceMesh polyVoxMesh; - - _volDataLock.lockForRead(); - _dataVersionLock.lockForRead(); - _modelWorkerVersion = _dataVersion; - _dataVersionLock.unlock(); - switch (_voxelSurfaceStyle) { - case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: - case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: { - PolyVox::MarchingCubesSurfaceExtractor> surfaceExtractor - (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); - surfaceExtractor.execute(); - break; - } - case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: - case PolyVoxEntityItem::SURFACE_CUBIC: { - PolyVox::CubicSurfaceExtractorWithNormals> surfaceExtractor - (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); - surfaceExtractor.execute(); - break; - } - } - _volDataLock.unlock(); - - // convert PolyVox mesh to a Sam mesh - const std::vector& vecIndices = polyVoxMesh.getIndices(); - auto indexBuffer = std::make_shared(vecIndices.size() * sizeof(uint32_t), - (gpu::Byte*)vecIndices.data()); - auto indexBufferPtr = gpu::BufferPointer(indexBuffer); - 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 = std::make_shared(vecVertices.size() * sizeof(PolyVox::PositionMaterialNormal), - (gpu::Byte*)vecVertices.data()); - auto vertexBufferPtr = gpu::BufferPointer(vertexBuffer); - 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, - vertexBufferPtr->getSize() - sizeof(float) * 3, - sizeof(PolyVox::PositionMaterialNormal), - gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW))); - - _modelGeometryLock.lockForWrite(); - _modelGeometry.setMesh(meshPtr); - _modelGeometryLock.unlock(); - - qDebug() << "******* RenderablePolyVoxEntityItem::getModelWorker is done *********" << _modelVersion << _modelWorkerVersion; - - _getModelWorkerDone = true; - _modelVersion = _modelWorkerVersion; - return true; -} - -void RenderablePolyVoxEntityItem::getModel() { - // if (QThread::currentThread() != thread()) { - // QMetaObject::invokeMethod(this, "getModel"); - // return; - // } - - if (getDataVersion() == _modelVersion) { - return; - } - - qDebug() << "RenderablePolyVoxEntityItem::getModel _getModelWorkerRunning =" << _getModelWorkerRunning; - - if (_getModelWorkerRunning) { - if (_getModelWorkerDone) { - if (!_getModelWorker.result()) { - qWarning() << "RenderablePolyVoxEntityItem::getModel worker failed"; - } - _getModelWorkerRunning = false; - } - return; - } - _getModelWorkerRunning = true; - _getModelWorkerDone = false; - _getModelWorker = QtConcurrent::run(this, &RenderablePolyVoxEntityItem::getModelWorker); -} - void RenderablePolyVoxEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderablePolyVoxEntityItem::render"); assert(getType() == EntityTypes::PolyVox); Q_ASSERT(args->_batch); - getModel(); - if (!_pipeline) { gpu::ShaderPointer vertexShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(polyvox_vert))); gpu::ShaderPointer pixelShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(polyvox_frag))); @@ -993,11 +536,6 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { _pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); } -// if (_needsModelReload) { -// getModel(); -// // return; -// } - gpu::Batch& batch = *args->_batch; batch.setPipeline(_pipeline); @@ -1104,3 +642,390 @@ glm::vec3 RenderablePolyVoxEntityItem::voxelCoordsToLocalCoords(glm::vec3& voxel glm::vec3 RenderablePolyVoxEntityItem::localCoordsToVoxelCoords(glm::vec3& localCoords) const { return glm::vec3(localToVoxelMatrix() * glm::vec4(localCoords, 0.0f)); } + + +RenderablePolyVoxAsynchronous::RenderablePolyVoxAsynchronous(PolyVoxEntityItem::PolyVoxSurfaceStyle voxelSurfaceStyle, + glm::vec3 voxelVolumeSize, + RenderablePolyVoxEntityItem* owner) : + _voxelSurfaceStyle(voxelSurfaceStyle), + _owner(owner) { + setVoxelVolumeSize(voxelVolumeSize); +} + + +RenderablePolyVoxAsynchronous::~RenderablePolyVoxAsynchronous() { + delete _volData; +} + + +void RenderablePolyVoxAsynchronous::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { + #ifdef WANT_DEBUG + qDebug() << "resetting voxel-space size" << voxelVolumeSize.x << voxelVolumeSize.y << voxelVolumeSize.z; + #endif + + _voxelVolumeSize = voxelVolumeSize; + + if (_volData) { + delete _volData; + } + _onCount = 0; + + if (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || + _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES) { + // 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, // 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 voxel-space size is" << _volData->getWidth() << _volData->getHeight() << _volData->getDepth(); + #endif +} + + +void RenderablePolyVoxAsynchronous::updateVoxelSurfaceStyle(PolyVoxEntityItem::PolyVoxSurfaceStyle voxelSurfaceStyle, + uint64_t modelVersion) { + // if we are switching to or from "edged" we need to force a resize of _volData. + bool wasEdged = (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || + _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES); + bool willBeEdged = (voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || + voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES); + + if (wasEdged != willBeEdged) { + if (_volData) { + delete _volData; + } + _volData = nullptr; + _voxelSurfaceStyle = voxelSurfaceStyle; + setVoxelVolumeSize(_voxelVolumeSize); + } else { + _voxelSurfaceStyle = voxelSurfaceStyle; + } + getModel(modelVersion); +} + + + +bool RenderablePolyVoxAsynchronous::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: + case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: + 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; +} + + +uint8_t RenderablePolyVoxAsynchronous::getVoxel(int x, int y, int z) { + 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. + + uint8_t result; + if (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || + _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES) { + result = _volData->getVoxelAt(x + 1, y + 1, z + 1); + } else { + result = _volData->getVoxelAt(x, y, z); + } + + return result; +} + + +bool RenderablePolyVoxAsynchronous::setVoxelInternal(int x, int y, int z, uint8_t toValue) { + // set a voxel without recompressing the voxel data + bool result = false; + if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) { + return result; + } + + result = updateOnCount(x, y, z, toValue); + + assert(_volData); + if (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || + _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES) { + _volData->setVoxelAt(x + 1, y + 1, z + 1, toValue); + } else { + _volData->setVoxelAt(x, y, z, toValue); + } + + return result; +} + + +bool RenderablePolyVoxAsynchronous::updateOnCount(int x, int y, int z, uint8_t toValue) { + // keep _onCount up to date + if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) { + return false; + } + + uint8_t uVoxelValue = getVoxel(x, y, z); + if (toValue != 0) { + if (uVoxelValue == 0) { + _onCount++; + return true; + } + } else { + // toValue == 0 + if (uVoxelValue != 0) { + _onCount--; + assert(_onCount >= 0); + return true; + } + } + return false; +} + + +bool RenderablePolyVoxAsynchronous::setVoxel(int x, int y, int z, uint8_t toValue, uint64_t modelVersion) { + bool result = setVoxelInternal(x, y, z, toValue); + if (result) { + compressVolumeData(modelVersion); + getModel(modelVersion); + } else { + // nothing changed. + _owner->receiveNewVoxelData(_owner->getVoxelData(), modelVersion); + } + return result; +} + + +bool RenderablePolyVoxAsynchronous::setAll(uint8_t toValue, uint64_t modelVersion) { + bool result = false; + + for (int z = 0; z < _voxelVolumeSize.z; z++) { + for (int y = 0; y < _voxelVolumeSize.y; y++) { + for (int x = 0; x < _voxelVolumeSize.x; x++) { + result |= setVoxelInternal(x, y, z, toValue); + } + } + } + if (result) { + compressVolumeData(modelVersion); + } else { + // nothing changed. + _owner->receiveNewVoxelData(_owner->getVoxelData(), modelVersion); + } + return result; +} + + +bool RenderablePolyVoxAsynchronous::setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue, uint64_t modelVersion) { + bool result = false; + + // This three-level for loop iterates over every voxel in the volume + 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 + 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) { + result |= setVoxelInternal(x, y, z, toValue); + } + } + } + } + if (result) { + compressVolumeData(modelVersion); + } else { + // nothing changed. + _owner->receiveNewVoxelData(_owner->getVoxelData(), modelVersion); + } + + return result; +} + + +PolyVox::RaycastResult RenderablePolyVoxAsynchronous::doRayCast(glm::vec4 originInVoxel, glm::vec4 farInVoxel, + glm::vec4& result) const { + PolyVox::Vector3DFloat startPoint(originInVoxel.x, originInVoxel.y, originInVoxel.z); + PolyVox::Vector3DFloat endPoint(farInVoxel.x, farInVoxel.y, farInVoxel.z); + + RaycastFunctor callback(_volData); + PolyVox::RaycastResult raycastResult = PolyVox::raycastWithEndpoints(_volData, startPoint, endPoint, callback); + + // result is in voxel-space coordinates. + result = callback._result - glm::vec4(0.5f, 0.5f, 0.5f, 0.0f); + return raycastResult; +} + + +// take compressed data and expand it into _volData. +void RenderablePolyVoxAsynchronous::decompressVolumeData(QByteArray voxelData, uint64_t modelVersion) { + QDataStream reader(voxelData); + quint16 voxelXSize, voxelYSize, voxelZSize; + reader >> voxelXSize; + reader >> voxelYSize; + reader >> voxelZSize; + + if (voxelXSize == 0 || voxelXSize > PolyVoxEntityItem::MAX_VOXEL_DIMENSION || + voxelYSize == 0 || voxelYSize > PolyVoxEntityItem::MAX_VOXEL_DIMENSION || + voxelZSize == 0 || voxelZSize > PolyVoxEntityItem::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); + + 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; + setVoxelInternal(x, y, z, uncompressedData[uncompressedIndex]); + } + } + } + + getModel(modelVersion); +} + + +void RenderablePolyVoxAsynchronous::getModel(uint64_t modelVersion) { + model::Mesh* mesh = new model::Mesh(); + model::MeshPointer meshPtr(mesh); + + // A mesh object to hold the result of surface extraction + PolyVox::SurfaceMesh polyVoxMesh; + + switch (_voxelSurfaceStyle) { + case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: + case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: { + PolyVox::MarchingCubesSurfaceExtractor> surfaceExtractor + (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); + surfaceExtractor.execute(); + break; + } + case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: + case PolyVoxEntityItem::SURFACE_CUBIC: { + PolyVox::CubicSurfaceExtractorWithNormals> surfaceExtractor + (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); + surfaceExtractor.execute(); + break; + } + } + + // convert PolyVox mesh to a Sam mesh + const std::vector& vecIndices = polyVoxMesh.getIndices(); + auto indexBuffer = std::make_shared(vecIndices.size() * sizeof(uint32_t), + (gpu::Byte*)vecIndices.data()); + auto indexBufferPtr = gpu::BufferPointer(indexBuffer); + 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 = std::make_shared(vecVertices.size() * sizeof(PolyVox::PositionMaterialNormal), + (gpu::Byte*)vecVertices.data()); + auto vertexBufferPtr = gpu::BufferPointer(vertexBuffer); + 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, + vertexBufferPtr->getSize() - sizeof(float) * 3, + sizeof(PolyVox::PositionMaterialNormal), + gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW))); + + _owner->receiveNewMesh(meshPtr, modelVersion); +} + +int RenderablePolyVoxAsynchronous::getOnCount() const { + // XXX get rid of this method + return _onCount; +} + + +// 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 RenderablePolyVoxAsynchronous::compressVolumeData(uint64_t modelVersion) { + 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 < 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; + 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) { + _owner->receiveNewVoxelData(newVoxelData, modelVersion); + } else { + // HACK -- until we have a way to allow for properties larger than MTU, don't update. + // revert the active voxel-space to the last version that fit. + qDebug() << "voxel data too large, reverting change."; + decompressVolumeData(_owner->getVoxelData(), modelVersion); + } +} diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 40375ce337..4a767382d7 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -15,6 +15,7 @@ #include #include +#include #include #include "PolyVoxEntityItem.h" @@ -38,8 +39,47 @@ namespace render { template <> void payloadRender(const PolyVoxPayload::Pointer& payload, RenderArgs* args); } +class RenderablePolyVoxEntityItem; + + +class RenderablePolyVoxAsynchronous { + + public: + RenderablePolyVoxAsynchronous(PolyVoxEntityItem::PolyVoxSurfaceStyle voxelSurfaceStyle, glm::vec3 voxelVolumeSize, + RenderablePolyVoxEntityItem* owner); + ~RenderablePolyVoxAsynchronous(); + + void setVoxelVolumeSize(glm::vec3 voxelVolumeSize); + void updateVoxelSurfaceStyle(PolyVoxEntityItem::PolyVoxSurfaceStyle voxelSurfaceStyle, uint64_t modelVersion); + uint8_t getVoxel(int x, int y, int z); + bool setVoxel(int x, int y, int z, uint8_t toValue, uint64_t modelVersion); + bool setAll(uint8_t toValue, uint64_t modelVersion); + bool setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue, uint64_t modelVersion); + PolyVox::RaycastResult doRayCast(glm::vec4 originInVoxel, glm::vec4 farInVoxel, glm::vec4& result) const; + int getOnCount() const; + void decompressVolumeData(QByteArray voxelData, uint64_t modelVersion); + +private: + void getModel(uint64_t modelVersion); + bool updateOnCount(int x, int y, int z, uint8_t new_value); + bool setVoxelInternal(int x, int y, int z, uint8_t toValue); + void compressVolumeData(uint64_t modelVersion); + static bool inUserBounds(const PolyVox::SimpleVolume* vol, PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle, + int x, int y, int z); + + PolyVoxEntityItem::PolyVoxSurfaceStyle _voxelSurfaceStyle; + glm::vec3 _voxelVolumeSize; + PolyVox::SimpleVolume* _volData = nullptr; + mutable QReadWriteLock _volDataLock; // lock for _volData + int _onCount = 0; // how many non-zero voxels are in _volData + + RenderablePolyVoxEntityItem* _owner = nullptr; +}; + + class RenderablePolyVoxEntityItem : public PolyVoxEntityItem { -public: + + public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); RenderablePolyVoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); @@ -57,22 +97,16 @@ public: virtual uint8_t getVoxel(int x, int y, int z); virtual bool setVoxel(int x, int y, int z, uint8_t toValue); - bool updateOnCount(int x, int y, int z, uint8_t new_value); - void render(RenderArgs* args); virtual bool supportsDetailedRayIntersection() const { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject, bool precisionPicking) const; - quint64 getDataVersion() const; - void bumpDataVersion(); - void getModel(); - bool getModelWorker(); - virtual void setVoxelData(QByteArray voxelData); - virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize); + virtual void setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle); + glm::vec3 getSurfacePositionAdjustment() const; glm::mat4 voxelToWorldMatrix() const; glm::mat4 worldToVoxelMatrix() const; @@ -108,49 +142,37 @@ public: std::shared_ptr scene, render::PendingChanges& pendingChanges); -protected: - virtual void updateVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle); + void receiveNewVoxelData(QByteArray newVoxelData, uint64_t dataVersion); + void receiveNewMesh(model::MeshPointer newMeshPtr, uint64_t meshVersion); private: // The PolyVoxEntityItem class has _voxelData which contains dimensions and compressed voxel data. The dimensions // may not match _voxelVolumeSize. - bool setVoxelInternal(int x, int y, int z, uint8_t toValue); - void compressVolumeData(); - void decompressVolumeData(); - void clearEdges(); - - PolyVox::SimpleVolume* _volData = nullptr; - mutable QReadWriteLock _volDataLock; // lock for _volData and _dataVersion - model::Geometry _modelGeometry; mutable QReadWriteLock _modelGeometryLock; - // set data version higher than model version so that getModelWorker must complete once before they are equal. - quint64 _dataVersion = 1; - mutable QReadWriteLock _dataVersionLock; - - quint64 _modelVersion = 0; - bool _getModelWorkerRunning = false; - bool _getModelWorkerDone = false; - quint64 _modelWorkerVersion = 0; - QFuture _getModelWorker; NetworkTexturePointer _xTexture; NetworkTexturePointer _yTexture; NetworkTexturePointer _zTexture; - int _onCount = 0; // how many non-zero voxels are in _volData - const int MATERIAL_GPU_SLOT = 3; render::ItemID _myItem; static gpu::PipelinePointer _pipeline; ShapeInfo _shapeInfo; - bool _shapeInfoReady = false; - bool _shapeInfoWorkerRunning = false; QFuture _shapeInfoWorker; + + // this does work outside of the main thread. + RenderablePolyVoxAsynchronous _async; + + uint64_t _modelVersion = 0; // local idea of how many changes have happened + // the following are compared against _modelVersion + uint64_t _meshVersion = 0; // version of most recently computed mesh + uint64_t _dataVersion = 0; // version of most recently compressed voxel data + uint64_t _shapeVersion = 0; // version of most recently computed collision shape }; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 41ca6a91ac..fee2055bd0 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -431,23 +431,10 @@ bool EntityScriptingInterface::setVoxels(QUuid entityID, return false; } - auto now = usecTimestampNow(); - auto polyVoxEntity = std::dynamic_pointer_cast(entity); _entityTree->lockForWrite(); bool result = actor(*polyVoxEntity); - entity->setLastEdited(now); - entity->setLastBroadcast(now); _entityTree->unlock(); - - _entityTree->lockForRead(); - EntityItemProperties properties = entity->getProperties(); - _entityTree->unlock(); - - properties.setVoxelDataDirty(); - properties.setLastEdited(now); - - queueEntityMessage(PacketType::EntityEdit, entityID, properties); return result; } diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index bb9a63cf4c..1e42f26ff8 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -66,7 +66,7 @@ void PolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { assert((int)_voxelVolumeSize.y == _voxelVolumeSize.y); assert((int)_voxelVolumeSize.z == _voxelVolumeSize.z); - _voxelVolumeSize = voxelVolumeSize; + _voxelVolumeSize = glm::vec3(roundf(voxelVolumeSize.x), roundf(voxelVolumeSize.y), roundf(voxelVolumeSize.z)); if (_voxelVolumeSize.x < 1) { qDebug() << "PolyVoxEntityItem::setVoxelVolumeSize clamping x of" << _voxelVolumeSize.x << "to 1"; _voxelVolumeSize.x = 1; @@ -184,10 +184,3 @@ void PolyVoxEntityItem::debugDump() const { qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions()); qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); } - -void PolyVoxEntityItem::setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) { - if (voxelSurfaceStyle == _voxelSurfaceStyle) { - return; - } - updateVoxelSurfaceStyle(voxelSurfaceStyle); -} diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index 9fbaade407..8e159900cd 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -62,7 +62,7 @@ class PolyVoxEntityItem : public EntityItem { SURFACE_EDGED_MARCHING_CUBES }; - void setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle); + virtual void setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) { _voxelSurfaceStyle = voxelSurfaceStyle; } // this other version of setVoxelSurfaceStyle is needed for SET_ENTITY_PROPERTY_FROM_PROPERTIES void setVoxelSurfaceStyle(uint16_t voxelSurfaceStyle) { setVoxelSurfaceStyle((PolyVoxSurfaceStyle) voxelSurfaceStyle); } virtual PolyVoxSurfaceStyle getVoxelSurfaceStyle() const { return _voxelSurfaceStyle; } @@ -104,10 +104,6 @@ class PolyVoxEntityItem : public EntityItem { virtual const QString& getZTextureURL() const { return _zTextureURL; } protected: - virtual void updateVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) { - _voxelSurfaceStyle = voxelSurfaceStyle; - } - glm::vec3 _voxelVolumeSize; // this is always 3 bytes QByteArray _voxelData; PolyVoxSurfaceStyle _voxelSurfaceStyle; diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index dc65128ac5..7599c7d1b5 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -52,6 +52,8 @@ public: void handleOutgoingChanges(VectorOfMotionStates& motionStates, const QUuid& sessionID); void handleCollisionEvents(CollisionEvents& collisionEvents); + EntityEditPacketSender* getPacketSender() { return _entityPacketSender; } + private: // incoming changes SetOfEntityMotionStates _pendingRemoves; // EntityMotionStates to be removed from PhysicsEngine (and deleted) From 7cb99688646fe15c6bb780941611ce219f71d0ab Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 25 Aug 2015 05:53:36 -0700 Subject: [PATCH 07/22] more work on polyvox threading --- .../src/RenderablePolyVoxEntityItem.cpp | 282 ++++++++++++++---- .../src/RenderablePolyVoxEntityItem.h | 58 ++-- libraries/entities/src/PolyVoxEntityItem.cpp | 14 + libraries/entities/src/PolyVoxEntityItem.h | 5 +- 4 files changed, 281 insertions(+), 78 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 1b7d53549f..cc0a4f3e91 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -38,7 +38,6 @@ #include #include "model/Geometry.h" -#include "gpu/Context.h" #include "EntityTreeRenderer.h" #include "polyvox_vert.h" #include "polyvox_frag.h" @@ -46,6 +45,8 @@ #include "EntityEditPacketSender.h" #include "PhysicalEntitySimulation.h" +#define THREAD_POLYVOX 1 + gpu::PipelinePointer RenderablePolyVoxEntityItem::_pipeline = nullptr; const float MARCHING_CUBE_COLLISION_HULL_OFFSET = 0.5; @@ -56,21 +57,46 @@ EntityItemPointer RenderablePolyVoxEntityItem::factory(const EntityItemID& entit RenderablePolyVoxEntityItem::RenderablePolyVoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : PolyVoxEntityItem(entityItemID, properties), + _mesh(new model::Mesh()), _xTexture(nullptr), _yTexture(nullptr), _zTexture(nullptr), _async(DEFAULT_VOXEL_SURFACE_STYLE, DEFAULT_VOXEL_VOLUME_SIZE, this) { - model::Mesh* mesh = new model::Mesh(); - model::MeshPointer meshPtr(mesh); - receiveNewMesh(meshPtr, _modelVersion); + // model::Mesh* mesh = new model::Mesh(); + // model::MeshPointer meshPtr(mesh); + + // mesh->setIndexBuffer(nullptr); + + // auto vertexBuffer = std::make_shared(0, (gpu::Byte*)""); + // auto vertexBufferPtr = gpu::BufferPointer(vertexBuffer); + // 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 = meshPtr; + // _modelGeometry.setMesh(meshPtr); + + // qDebug() << "5d301155-faf9-44dd-8e8b-061a03d42c0f" << "IN CONSTRUCTOR" << getID() << _voxelData; + // _async.decompressVolumeData(_voxelData, ++_modelVersion); + // _async.updateVoxelSurfaceStyle(_voxelSurfaceStyle, ++_modelVersion); } RenderablePolyVoxEntityItem::~RenderablePolyVoxEntityItem() { } -void RenderablePolyVoxEntityItem::receiveNewVoxelData(QByteArray newVoxelData, uint64_t dataVersion) { +void RenderablePolyVoxEntityItem::receiveNewVoxelData(QByteArray newVoxelData, quint64 dataVersion) { + if (dataVersion <= _dataVersion) { + // qDebug() << _id << "OKOKOK NOPE" << QString(__PRETTY_FUNCTION__) << getName() << dataVersion << _dataVersion; + return; + } + + // qDebug() << _id << "OKOKOK!!!" << QString(__PRETTY_FUNCTION__) << getName() << dataVersion << _dataVersion << "mesh-size is" << _mesh->getNumVertices(); + _voxelData = newVoxelData; _dataVersion = dataVersion; @@ -93,44 +119,79 @@ void RenderablePolyVoxEntityItem::receiveNewVoxelData(QByteArray newVoxelData, u } -void RenderablePolyVoxEntityItem::receiveNewMesh(model::MeshPointer newMeshPtr, uint64_t meshVersion) { +void RenderablePolyVoxEntityItem::receiveNewMesh(model::MeshPointer newMeshPtr, quint64 meshVersion) { + if (meshVersion <= _meshVersion) { + // qDebug() << _id << "OKOKOK NOPE" << QString(__PRETTY_FUNCTION__) << getName() << meshVersion << _meshVersion; + return; + } + + // int meshSize = -1; + // const model::MeshPointer mesh = _modelGeometry.getMesh(); + // if (mesh) { + // mesh->getVertexBuffer()._size; + // } + + // qDebug() << _id << "OKOKOK!!!" << QString(__PRETTY_FUNCTION__) << getName() << meshVersion << _meshVersion << "mesh-size is" << meshSize; + // qDebug() << _id << "OKOKOK!!!" << QString(__PRETTY_FUNCTION__) << getName() << meshVersion << _meshVersion << "mesh-size is" << _mesh->getNumVertices() << "new mesh-size is" << newMeshPtr->getNumVertices(); + _dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS; _modelGeometryLock.lockForWrite(); - _modelGeometry.setMesh(newMeshPtr); + // _modelGeometry.setMesh(newMeshPtr); + _mesh = newMeshPtr; _meshVersion = meshVersion; _modelGeometryLock.unlock(); + // qDebug() << _id << "OKOKOK after" << getName() << " mesh-size is" << _modelGeometry.getMesh()->getVertexBuffer()._size << "\n"; + // qDebug() << _id << "OKOKOK after" << getName() << " mesh-size is" << _mesh->getNumVertices(); + // qDebug() << _id << "OKOKOK after" << getName() << " mesh-size is" << newMeshPtr->getNumVertices(); + computeShapeInfoWorker(); } void RenderablePolyVoxEntityItem::setVoxelData(QByteArray voxelData) { - if (voxelData == _voxelData) { + // qDebug() << "5d301155-faf9-44dd-8e8b-061a03d42c0f setVoxelData for" << getName() << getID() << ((void *)this); + + _voxelDataLock.lockForWrite(); + if (/*_dataVersion > 1 &&*/ _voxelData == voxelData) { + // qDebug() << _id << "OKOKOK NOPE" << QString(__PRETTY_FUNCTION__) << getName() << _dataVersion << getID() << ((void *)this); + _voxelDataLock.unlock(); return; } + // qDebug() << _id << "OKOKOK" << QString(__PRETTY_FUNCTION__) << getName() << _dataVersion; + _voxelData = voxelData; _async.decompressVolumeData(_voxelData, ++_modelVersion); + _voxelDataLock.unlock(); } void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { - if (voxelVolumeSize == _voxelVolumeSize) { + if (/*_meshVersion > 1 && _shapeVersion > 1 &&*/ _voxelVolumeSize == voxelVolumeSize) { + // qDebug() << _id << "OKOKOK NOPE" << QString(__PRETTY_FUNCTION__) << getName() << _meshVersion << ((void *)this); return; } + // qDebug() << _id << "OKOKOK" << QString(__PRETTY_FUNCTION__) << getName() << ((void *)this); + PolyVoxEntityItem::setVoxelVolumeSize(voxelVolumeSize); _async.setVoxelVolumeSize(_voxelVolumeSize); // 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. + _voxelDataLock.lockForRead(); _async.decompressVolumeData(_voxelData, ++_modelVersion); + _voxelDataLock.unlock(); } void RenderablePolyVoxEntityItem::setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) { - if (_voxelSurfaceStyle == voxelSurfaceStyle) { + if (/*_meshVersion > 1 && _shapeVersion > 1 &&*/ _voxelSurfaceStyle == voxelSurfaceStyle) { + // qDebug() << _id << "OKOKOK NOPE" << QString(__PRETTY_FUNCTION__) << getName() << _meshVersion; return; } + // qDebug() << _id << "OKOKOK" << QString(__PRETTY_FUNCTION__) << getName(); + _voxelSurfaceStyle = voxelSurfaceStyle; _async.updateVoxelSurfaceStyle(_voxelSurfaceStyle, ++_modelVersion); } @@ -351,17 +412,15 @@ ShapeType RenderablePolyVoxEntityItem::getShapeType() const { } bool RenderablePolyVoxEntityItem::isReadyToComputeShape() { - #ifdef WANT_DEBUG - qDebug() << "RenderablePolyVoxEntityItem::isReadyToComputeShape" << getName() << _shapeVersion << _modelVersion; - #endif - return (_shapeVersion == _modelVersion); } bool RenderablePolyVoxEntityItem::computeShapeInfoWorker() { ShapeType type = getShapeType(); if (type != SHAPE_TYPE_COMPOUND) { + _shapeInfoLock.lockForWrite(); EntityItem::computeShapeInfo(_shapeInfo); + _shapeInfoLock.unlock(); return true; } @@ -374,12 +433,16 @@ bool RenderablePolyVoxEntityItem::computeShapeInfoWorker() { /* pull each triangle in the mesh into a polyhedron which can be collided with */ unsigned int i = 0; - _modelGeometryLock.lockForRead(); - const model::MeshPointer mesh = _modelGeometry.getMesh(); - _modelGeometryLock.unlock(); + // _modelGeometryLock.lockForRead(); + // const model::MeshPointer mesh = _modelGeometry.getMesh(); + // _modelGeometryLock.unlock(); + model::MeshPointer mesh = _mesh; + _modelGeometryLock.lockForRead(); const gpu::BufferView vertexBufferView = mesh->getVertexBuffer(); const gpu::BufferView& indexBufferView = mesh->getIndexBuffer(); + _modelGeometryLock.unlock(); + gpu::BufferView::Iterator it = indexBufferView.cbegin(); while (it != indexBufferView.cend()) { uint32_t p0Index = *(it++); @@ -482,21 +545,26 @@ bool RenderablePolyVoxEntityItem::computeShapeInfoWorker() { } if (points.isEmpty()) { + _shapeInfoLock.lockForWrite(); EntityItem::computeShapeInfo(_shapeInfo); + _shapeInfoLock.unlock(); return true; } glm::vec3 collisionModelDimensions = box.getDimensions(); QByteArray b64 = _voxelData.toBase64(); + _shapeInfoLock.lockForWrite(); _shapeInfo.setParams(type, collisionModelDimensions, QString(b64)); _shapeInfo.setConvexHulls(points); - _shapeVersion = _modelVersion; + _shapeInfoLock.unlock(); return true; } void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) { + _shapeInfoLock.lockForRead(); info = _shapeInfo; + _shapeInfoLock.unlock(); } void RenderablePolyVoxEntityItem::setXTextureURL(QString xTextureURL) { @@ -516,6 +584,18 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { assert(getType() == EntityTypes::PolyVox); Q_ASSERT(args->_batch); + if (_meshVersion < _modelVersion || + _dataVersion < _modelVersion) { + _async.decompressVolumeData(_voxelData, _modelVersion); + _async.updateVoxelSurfaceStyle(_voxelSurfaceStyle, _modelVersion); + } + + model::MeshPointer mesh = _mesh; + + // if (_meshVersion == 0) { + // return; // we have no mesh + // } + if (!_pipeline) { gpu::ShaderPointer vertexShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(polyvox_vert))); gpu::ShaderPointer pixelShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(polyvox_frag))); @@ -539,9 +619,8 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { gpu::Batch& batch = *args->_batch; batch.setPipeline(_pipeline); - _modelGeometryLock.lockForRead(); - auto mesh = _modelGeometry.getMesh(); - _modelGeometryLock.unlock(); + // _modelGeometryLock.lockForRead(); + // auto mesh = _modelGeometry.getMesh(); Transform transform(voxelToWorldMatrix()); batch.setModelTransform(transform); @@ -583,6 +662,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { batch._glUniform3f(voxelVolumeSizeLocation, _voxelVolumeSize.x, _voxelVolumeSize.y, _voxelVolumeSize.z); batch.drawIndexed(gpu::TRIANGLES, mesh->getNumIndices(), 0); + // _modelGeometryLock.unlock(); RenderableDebugableEntityItem::render(this, args); } @@ -648,21 +728,23 @@ RenderablePolyVoxAsynchronous::RenderablePolyVoxAsynchronous(PolyVoxEntityItem:: glm::vec3 voxelVolumeSize, RenderablePolyVoxEntityItem* owner) : _voxelSurfaceStyle(voxelSurfaceStyle), + _onCount(0), _owner(owner) { setVoxelVolumeSize(voxelVolumeSize); } RenderablePolyVoxAsynchronous::~RenderablePolyVoxAsynchronous() { + _volDataLock.lockForWrite(); delete _volData; + _volDataLock.unlock(); } void RenderablePolyVoxAsynchronous::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { - #ifdef WANT_DEBUG - qDebug() << "resetting voxel-space size" << voxelVolumeSize.x << voxelVolumeSize.y << voxelVolumeSize.z; - #endif + // qDebug() << _owner->getID() << "OKOKOK" << QString(__PRETTY_FUNCTION__) << _owner->getName(); + _volDataLock.lockForWrite(); _voxelVolumeSize = voxelVolumeSize; if (_volData) { @@ -691,15 +773,16 @@ void RenderablePolyVoxAsynchronous::setVoxelVolumeSize(glm::vec3 voxelVolumeSize // having the "outside of voxel-space" value be 255 has helped me notice some problems. _volData->setBorderValue(255); - - #ifdef WANT_DEBUG - qDebug() << " new voxel-space size is" << _volData->getWidth() << _volData->getHeight() << _volData->getDepth(); - #endif + _volDataLock.unlock(); } void RenderablePolyVoxAsynchronous::updateVoxelSurfaceStyle(PolyVoxEntityItem::PolyVoxSurfaceStyle voxelSurfaceStyle, - uint64_t modelVersion) { + quint64 modelVersion) { + + // qDebug() << _owner->getID() << "OKOKOK" << QString(__PRETTY_FUNCTION__) << _owner->getName(); + + _volDataLock.lockForWrite(); // if we are switching to or from "edged" we need to force a resize of _volData. bool wasEdged = (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES); @@ -712,11 +795,23 @@ void RenderablePolyVoxAsynchronous::updateVoxelSurfaceStyle(PolyVoxEntityItem::P } _volData = nullptr; _voxelSurfaceStyle = voxelSurfaceStyle; + _volDataLock.unlock(); setVoxelVolumeSize(_voxelVolumeSize); +# ifdef THREAD_POLYVOX + QtConcurrent::run(this, &RenderablePolyVoxAsynchronous::decompressVolumeDataAsync, + _owner->getVoxelData(), modelVersion); +# else + decompressVolumeDataAsync(_owner->getVoxelData(), modelVersion); +# endif } else { _voxelSurfaceStyle = voxelSurfaceStyle; + _volDataLock.unlock(); +# ifdef THREAD_POLYVOX + QtConcurrent::run(this, &RenderablePolyVoxAsynchronous::getMeshAsync, modelVersion); +# else + getMeshAsync(modelVersion); +# endif } - getModel(modelVersion); } @@ -748,6 +843,13 @@ bool RenderablePolyVoxAsynchronous::inUserBounds(const PolyVox::SimpleVolumereceiveNewVoxelData(_owner->getVoxelData(), modelVersion); + // _owner->receiveNewVoxelData(_owner->getVoxelData(), modelVersion); + _owner->setDataVersion(modelVersion); } return result; } -bool RenderablePolyVoxAsynchronous::setAll(uint8_t toValue, uint64_t modelVersion) { +bool RenderablePolyVoxAsynchronous::setAll(uint8_t toValue, quint64 modelVersion) { bool result = false; + _volDataLock.lockForWrite(); for (int z = 0; z < _voxelVolumeSize.z; z++) { for (int y = 0; y < _voxelVolumeSize.y; y++) { for (int x = 0; x < _voxelVolumeSize.x; x++) { @@ -836,20 +947,29 @@ bool RenderablePolyVoxAsynchronous::setAll(uint8_t toValue, uint64_t modelVersio } } } + _volDataLock.unlock(); if (result) { +# ifdef THREAD_POLYVOX + QtConcurrent::run(this, &RenderablePolyVoxAsynchronous::compressVolumeData, modelVersion); + QtConcurrent::run(this, &RenderablePolyVoxAsynchronous::getMeshAsync, modelVersion); +# else compressVolumeData(modelVersion); + getMeshAsync(modelVersion); +# endif } else { // nothing changed. - _owner->receiveNewVoxelData(_owner->getVoxelData(), modelVersion); + // _owner->receiveNewVoxelData(_owner->getVoxelData(), modelVersion); + _owner->setDataVersion(modelVersion); } return result; } -bool RenderablePolyVoxAsynchronous::setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue, uint64_t modelVersion) { +bool RenderablePolyVoxAsynchronous::setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue, quint64 modelVersion) { bool result = false; // This three-level for loop iterates over every voxel in the volume + _volDataLock.lockForWrite(); for (int z = 0; z < _voxelVolumeSize.z; z++) { for (int y = 0; y < _voxelVolumeSize.y; y++) { for (int x = 0; x < _voxelVolumeSize.x; x++) { @@ -864,11 +984,19 @@ bool RenderablePolyVoxAsynchronous::setSphereInVolume(glm::vec3 center, float ra } } } + _volDataLock.unlock(); if (result) { +# ifdef THREAD_POLYVOX + QtConcurrent::run(this, &RenderablePolyVoxAsynchronous::compressVolumeData, modelVersion); + QtConcurrent::run(this, &RenderablePolyVoxAsynchronous::getMeshAsync, modelVersion); +# else compressVolumeData(modelVersion); + getMeshAsync(modelVersion); +# endif } else { // nothing changed. - _owner->receiveNewVoxelData(_owner->getVoxelData(), modelVersion); + // _owner->receiveNewVoxelData(_owner->getVoxelData(), modelVersion); + _owner->setDataVersion(modelVersion); } return result; @@ -880,8 +1008,10 @@ PolyVox::RaycastResult RenderablePolyVoxAsynchronous::doRayCast(glm::vec4 origin PolyVox::Vector3DFloat startPoint(originInVoxel.x, originInVoxel.y, originInVoxel.z); PolyVox::Vector3DFloat endPoint(farInVoxel.x, farInVoxel.y, farInVoxel.z); + _volDataLock.lockForRead(); RaycastFunctor callback(_volData); PolyVox::RaycastResult raycastResult = PolyVox::raycastWithEndpoints(_volData, startPoint, endPoint, callback); + _volDataLock.unlock(); // result is in voxel-space coordinates. result = callback._result - glm::vec4(0.5f, 0.5f, 0.5f, 0.0f); @@ -890,7 +1020,21 @@ PolyVox::RaycastResult RenderablePolyVoxAsynchronous::doRayCast(glm::vec4 origin // take compressed data and expand it into _volData. -void RenderablePolyVoxAsynchronous::decompressVolumeData(QByteArray voxelData, uint64_t modelVersion) { +void RenderablePolyVoxAsynchronous::decompressVolumeData(QByteArray voxelData, quint64 modelVersion) { + // qDebug() << _owner->getID() << "OKOKOK" << QString(__PRETTY_FUNCTION__) << _owner->getName() << modelVersion; + +# ifdef THREAD_POLYVOX + QtConcurrent::run(this, &RenderablePolyVoxAsynchronous::decompressVolumeDataAsync, voxelData, modelVersion); +# else + decompressVolumeDataAsync(voxelData, modelVersion); +# endif +} + + +void RenderablePolyVoxAsynchronous::decompressVolumeDataAsync(QByteArray voxelData, quint64 modelVersion) { + // qDebug() << _owner->getID() << "OKOKOK" << QString(__PRETTY_FUNCTION__) + // << _owner->getName() << _owner->getID() << modelVersion; + QDataStream reader(voxelData); quint16 voxelXSize, voxelYSize, voxelZSize; reader >> voxelXSize; @@ -901,7 +1045,10 @@ void RenderablePolyVoxAsynchronous::decompressVolumeData(QByteArray voxelData, u voxelYSize == 0 || voxelYSize > PolyVoxEntityItem::MAX_VOXEL_DIMENSION || voxelZSize == 0 || voxelZSize > PolyVoxEntityItem::MAX_VOXEL_DIMENSION) { qDebug() << "voxelSize is not reasonable, skipping decompressions." - << voxelXSize << voxelYSize << voxelZSize; + << voxelXSize << voxelYSize << voxelZSize << _owner->getName() << _owner->getID() << modelVersion + << "5d301155-faf9-44dd-8e8b-061a03d42c0f"; + // _owner->receiveNewVoxelData(_owner->getVoxelData(), modelVersion); + _owner->setDataVersion(modelVersion); return; } @@ -912,11 +1059,15 @@ void RenderablePolyVoxAsynchronous::decompressVolumeData(QByteArray voxelData, u QByteArray uncompressedData = qUncompress(compressedData); if (uncompressedData.size() != rawSize) { - qDebug() << "PolyVox decompress -- size is (" << voxelXSize << voxelYSize << voxelZSize << ")" << - "so expected uncompressed length of" << rawSize << "but length is" << uncompressedData.size(); + qDebug() << "PolyVox decompress -- size is (" << voxelXSize << voxelYSize << voxelZSize << ")" + << "so expected uncompressed length of" << rawSize << "but length is" << uncompressedData.size() + << _owner->getName() << _owner->getID() << modelVersion << "5d301155-faf9-44dd-8e8b-061a03d42c0f"; + // _owner->receiveNewVoxelData(_owner->getVoxelData(), modelVersion); + _owner->setDataVersion(modelVersion); return; } + _volDataLock.lockForWrite(); for (int z = 0; z < voxelZSize; z++) { for (int y = 0; y < voxelYSize; y++) { for (int x = 0; x < voxelXSize; x++) { @@ -925,18 +1076,27 @@ void RenderablePolyVoxAsynchronous::decompressVolumeData(QByteArray voxelData, u } } } + _volDataLock.unlock(); - getModel(modelVersion); + _owner->receiveNewVoxelData(voxelData, modelVersion); + +# ifdef THREAD_POLYVOX + QFuture future = QtConcurrent::run(this, &RenderablePolyVoxAsynchronous::getMeshAsync, modelVersion); +# else + getMeshAsync(modelVersion); +# endif } -void RenderablePolyVoxAsynchronous::getModel(uint64_t modelVersion) { - model::Mesh* mesh = new model::Mesh(); - model::MeshPointer meshPtr(mesh); +void RenderablePolyVoxAsynchronous::getMeshAsync(quint64 modelVersion) { + // qDebug() << _owner->getID() << "OKOKOK" << QString(__PRETTY_FUNCTION__) << _owner->getName() << modelVersion; + + model::MeshPointer mesh(new model::Mesh()); // A mesh object to hold the result of surface extraction PolyVox::SurfaceMesh polyVoxMesh; + _volDataLock.lockForRead(); switch (_voxelSurfaceStyle) { case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: { @@ -953,6 +1113,7 @@ void RenderablePolyVoxAsynchronous::getModel(uint64_t modelVersion) { break; } } + _volDataLock.unlock(); // convert PolyVox mesh to a Sam mesh const std::vector& vecIndices = polyVoxMesh.getIndices(); @@ -966,10 +1127,11 @@ void RenderablePolyVoxAsynchronous::getModel(uint64_t modelVersion) { auto vertexBuffer = std::make_shared(vecVertices.size() * sizeof(PolyVox::PositionMaterialNormal), (gpu::Byte*)vecVertices.data()); auto vertexBufferPtr = gpu::BufferPointer(vertexBuffer); - auto vertexBufferView = new gpu::BufferView(vertexBufferPtr, - 0, - vertexBufferPtr->getSize() - sizeof(float) * 3, - sizeof(PolyVox::PositionMaterialNormal), + gpu::Resource::Size vertexBufferSize = 0; + if (vertexBufferPtr->getSize() > sizeof(float) * 3) { + vertexBufferSize = vertexBufferPtr->getSize() - sizeof(float) * 3; + } + auto vertexBufferView = new gpu::BufferView(vertexBufferPtr, 0, vertexBufferSize, sizeof(PolyVox::PositionMaterialNormal), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW)); mesh->setVertexBuffer(*vertexBufferView); mesh->addAttribute(gpu::Stream::NORMAL, @@ -979,7 +1141,10 @@ void RenderablePolyVoxAsynchronous::getModel(uint64_t modelVersion) { sizeof(PolyVox::PositionMaterialNormal), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW))); - _owner->receiveNewMesh(meshPtr, modelVersion); + // qDebug() << _owner->getID() << "OKOKOK -- MADE NEW MESH" << "index-count =" << vecIndices.size() + // << "vertex-count =" << vecVertices.size() << "mesh-vertg-size =" << mesh->getVertexBuffer()._size; + + _owner->receiveNewMesh(mesh, modelVersion); } int RenderablePolyVoxAsynchronous::getOnCount() const { @@ -990,7 +1155,9 @@ int RenderablePolyVoxAsynchronous::getOnCount() const { // 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 RenderablePolyVoxAsynchronous::compressVolumeData(uint64_t modelVersion) { +void RenderablePolyVoxAsynchronous::compressVolumeData(quint64 modelVersion) { + // qDebug() << _owner->getID() << "OKOKOK" << QString(__PRETTY_FUNCTION__) << _owner->getName() << modelVersion; + quint16 voxelXSize = _voxelVolumeSize.x; quint16 voxelYSize = _voxelVolumeSize.y; quint16 voxelZSize = _voxelVolumeSize.z; @@ -998,10 +1165,11 @@ void RenderablePolyVoxAsynchronous::compressVolumeData(uint64_t modelVersion) { QByteArray uncompressedData = QByteArray(rawSize, '\0'); + _volDataLock.lockForRead(); 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); + uint8_t uVoxelValue = getVoxelInternal(x, y, z); int uncompressedIndex = z * voxelYSize * voxelXSize + y * voxelXSize + @@ -1010,6 +1178,7 @@ void RenderablePolyVoxAsynchronous::compressVolumeData(uint64_t modelVersion) { } } } + _volDataLock.unlock(); QByteArray newVoxelData; QDataStream writer(&newVoxelData, QIODevice::WriteOnly | QIODevice::Truncate); @@ -1025,7 +1194,10 @@ void RenderablePolyVoxAsynchronous::compressVolumeData(uint64_t modelVersion) { } else { // HACK -- until we have a way to allow for properties larger than MTU, don't update. // revert the active voxel-space to the last version that fit. - qDebug() << "voxel data too large, reverting change."; - decompressVolumeData(_owner->getVoxelData(), modelVersion); + // QtConcurrent::run(this, &RenderablePolyVoxAsynchronous::decompressVolumeDataAsync, + // _owner->getVoxelData(), modelVersion); + // decompressVolumeDataAsync(_owner->getVoxelData(), modelVersion); + // _owner->receiveNewVoxelData(_owner->getVoxelData(), modelVersion); + _owner->setDataVersion(modelVersion); } } diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 4a767382d7..405cdd07ff 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -13,15 +13,17 @@ #define hifi_RenderablePolyVoxEntityItem_h #include +#include #include #include + #include #include "PolyVoxEntityItem.h" #include "RenderableDebugableEntityItem.h" #include "RenderableEntityItem.h" - +#include "gpu/Context.h" class PolyVoxPayload { public: @@ -42,7 +44,8 @@ namespace render { class RenderablePolyVoxEntityItem; -class RenderablePolyVoxAsynchronous { +class RenderablePolyVoxAsynchronous : public QObject { + Q_OBJECT public: RenderablePolyVoxAsynchronous(PolyVoxEntityItem::PolyVoxSurfaceStyle voxelSurfaceStyle, glm::vec3 voxelVolumeSize, @@ -50,20 +53,29 @@ class RenderablePolyVoxAsynchronous { ~RenderablePolyVoxAsynchronous(); void setVoxelVolumeSize(glm::vec3 voxelVolumeSize); - void updateVoxelSurfaceStyle(PolyVoxEntityItem::PolyVoxSurfaceStyle voxelSurfaceStyle, uint64_t modelVersion); + void updateVoxelSurfaceStyle(PolyVoxEntityItem::PolyVoxSurfaceStyle voxelSurfaceStyle, quint64 modelVersion); uint8_t getVoxel(int x, int y, int z); - bool setVoxel(int x, int y, int z, uint8_t toValue, uint64_t modelVersion); - bool setAll(uint8_t toValue, uint64_t modelVersion); - bool setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue, uint64_t modelVersion); + bool setVoxel(int x, int y, int z, uint8_t toValue, quint64 modelVersion); + bool setAll(uint8_t toValue, quint64 modelVersion); + bool setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue, quint64 modelVersion); PolyVox::RaycastResult doRayCast(glm::vec4 originInVoxel, glm::vec4 farInVoxel, glm::vec4& result) const; int getOnCount() const; - void decompressVolumeData(QByteArray voxelData, uint64_t modelVersion); + void decompressVolumeData(QByteArray voxelData, quint64 modelVersion); + +// signals: +// void doDecompressVolumeDataAsync(QByteArray voxelData, quint64 modelVersion); +// void doGetMeshAsync(quint64 modelVersion); + + private: - void getModel(uint64_t modelVersion); + void decompressVolumeDataAsync(QByteArray voxelData, quint64 modelVersion); + void getMeshAsync(quint64 modelVersion); + + uint8_t getVoxelInternal(int x, int y, int z); bool updateOnCount(int x, int y, int z, uint8_t new_value); bool setVoxelInternal(int x, int y, int z, uint8_t toValue); - void compressVolumeData(uint64_t modelVersion); + void compressVolumeData(quint64 modelVersion); static bool inUserBounds(const PolyVox::SimpleVolume* vol, PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle, int x, int y, int z); @@ -71,7 +83,7 @@ private: glm::vec3 _voxelVolumeSize; PolyVox::SimpleVolume* _volData = nullptr; mutable QReadWriteLock _volDataLock; // lock for _volData - int _onCount = 0; // how many non-zero voxels are in _volData + std::atomic_int _onCount; // how many non-zero voxels are in _volData RenderablePolyVoxEntityItem* _owner = nullptr; }; @@ -116,7 +128,6 @@ class RenderablePolyVoxEntityItem : public PolyVoxEntityItem { virtual ShapeType getShapeType() const; virtual bool isReadyToComputeShape(); virtual void computeShapeInfo(ShapeInfo& info); - virtual bool computeShapeInfoWorker(); virtual glm::vec3 voxelCoordsToWorldCoords(glm::vec3& voxelCoords) const; virtual glm::vec3 worldCoordsToVoxelCoords(glm::vec3& worldCoords) const; @@ -142,17 +153,21 @@ class RenderablePolyVoxEntityItem : public PolyVoxEntityItem { std::shared_ptr scene, render::PendingChanges& pendingChanges); - void receiveNewVoxelData(QByteArray newVoxelData, uint64_t dataVersion); - void receiveNewMesh(model::MeshPointer newMeshPtr, uint64_t meshVersion); + void receiveNewVoxelData(QByteArray newVoxelData, quint64 dataVersion); + void receiveNewMesh(model::MeshPointer newMeshPtr, quint64 meshVersion); + void setDataVersion(quint64 dataVersion) { _dataVersion = dataVersion; } private: // The PolyVoxEntityItem class has _voxelData which contains dimensions and compressed voxel data. The dimensions // may not match _voxelVolumeSize. - model::Geometry _modelGeometry; - mutable QReadWriteLock _modelGeometryLock; + virtual bool computeShapeInfoWorker(); - QFuture _getModelWorker; + // model::Geometry _modelGeometry; + mutable QReadWriteLock _modelGeometryLock; + model::MeshPointer _mesh; + + QFuture _getMeshWorker; NetworkTexturePointer _xTexture; NetworkTexturePointer _yTexture; @@ -163,16 +178,17 @@ private: static gpu::PipelinePointer _pipeline; ShapeInfo _shapeInfo; - QFuture _shapeInfoWorker; + // QFuture _shapeInfoWorker; + mutable QReadWriteLock _shapeInfoLock; // this does work outside of the main thread. RenderablePolyVoxAsynchronous _async; - uint64_t _modelVersion = 0; // local idea of how many changes have happened + quint64 _modelVersion = 1; // local idea of how many changes have happened // the following are compared against _modelVersion - uint64_t _meshVersion = 0; // version of most recently computed mesh - uint64_t _dataVersion = 0; // version of most recently compressed voxel data - uint64_t _shapeVersion = 0; // version of most recently computed collision shape + quint64 _meshVersion = 0; // version of most recently computed mesh + quint64 _dataVersion = 0; // version of most recently compressed voxel data + quint64 _shapeVersion = 0; // version of most recently computed collision shape }; diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index 1e42f26ff8..df3b6d1608 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -184,3 +184,17 @@ void PolyVoxEntityItem::debugDump() const { qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions()); qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); } + +void PolyVoxEntityItem::setVoxelData(QByteArray voxelData) +{ + qDebug() << "5d301155-faf9-44dd-8e8b-061a03d42c0f parent setVoxelData for" << getName() << getID() << ((void *)this) + << typeid(*this).name(); + + _voxelDataLock.lockForWrite(); + _voxelData = voxelData; + _voxelDataLock.unlock(); +} + +const QByteArray PolyVoxEntityItem::getVoxelData() const { + return _voxelData; +} diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index 8e159900cd..118f6cc99b 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -52,8 +52,8 @@ class PolyVoxEntityItem : public EntityItem { virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize); virtual const glm::vec3& getVoxelVolumeSize() const { return _voxelVolumeSize; } - virtual void setVoxelData(QByteArray voxelData) { _voxelData = voxelData; } - virtual const QByteArray& getVoxelData() const { return _voxelData; } + virtual void setVoxelData(QByteArray voxelData); + virtual const QByteArray getVoxelData() const; enum PolyVoxSurfaceStyle { SURFACE_MARCHING_CUBES, @@ -105,6 +105,7 @@ class PolyVoxEntityItem : public EntityItem { protected: glm::vec3 _voxelVolumeSize; // this is always 3 bytes + mutable QReadWriteLock _voxelDataLock; QByteArray _voxelData; PolyVoxSurfaceStyle _voxelSurfaceStyle; From caafea6e3b546ccaccb719d12e439eb3beba002b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 25 Aug 2015 11:46:34 -0700 Subject: [PATCH 08/22] allow compound collision shapes with less than 2 sub-shapes --- libraries/physics/src/ShapeFactory.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index cfe7abd4db..f138587030 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -94,7 +94,6 @@ btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) { if (numSubShapes == 1) { shape = createConvexHull(info.getPoints()[0]); } else { - assert(numSubShapes > 1); auto compound = new btCompoundShape(); btTransform trans; trans.setIdentity(); From f6c440756c4661d13bfca40c798d817f1a680f4f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 25 Aug 2015 11:46:51 -0700 Subject: [PATCH 09/22] do slow polyvox operations on a thread --- .../src/RenderablePolyVoxEntityItem.cpp | 1402 ++++++++--------- .../src/RenderablePolyVoxEntityItem.h | 92 +- libraries/entities/src/PolyVoxEntityItem.cpp | 7 +- libraries/entities/src/PolyVoxEntityItem.h | 3 + 4 files changed, 652 insertions(+), 852 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index cc0a4f3e91..c35a80f188 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -58,142 +58,57 @@ RenderablePolyVoxEntityItem::RenderablePolyVoxEntityItem(const EntityItemID& ent const EntityItemProperties& properties) : PolyVoxEntityItem(entityItemID, properties), _mesh(new model::Mesh()), + _meshDirty(true), _xTexture(nullptr), _yTexture(nullptr), - _zTexture(nullptr), - _async(DEFAULT_VOXEL_SURFACE_STYLE, DEFAULT_VOXEL_VOLUME_SIZE, this) { - - // model::Mesh* mesh = new model::Mesh(); - // model::MeshPointer meshPtr(mesh); - - // mesh->setIndexBuffer(nullptr); - - // auto vertexBuffer = std::make_shared(0, (gpu::Byte*)""); - // auto vertexBufferPtr = gpu::BufferPointer(vertexBuffer); - // 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 = meshPtr; - // _modelGeometry.setMesh(meshPtr); - - // qDebug() << "5d301155-faf9-44dd-8e8b-061a03d42c0f" << "IN CONSTRUCTOR" << getID() << _voxelData; - // _async.decompressVolumeData(_voxelData, ++_modelVersion); - // _async.updateVoxelSurfaceStyle(_voxelSurfaceStyle, ++_modelVersion); + _zTexture(nullptr) { + setVoxelVolumeSize(_voxelVolumeSize); } RenderablePolyVoxEntityItem::~RenderablePolyVoxEntityItem() { } -void RenderablePolyVoxEntityItem::receiveNewVoxelData(QByteArray newVoxelData, quint64 dataVersion) { - if (dataVersion <= _dataVersion) { - // qDebug() << _id << "OKOKOK NOPE" << QString(__PRETTY_FUNCTION__) << getName() << dataVersion << _dataVersion; - return; - } - - // qDebug() << _id << "OKOKOK!!!" << QString(__PRETTY_FUNCTION__) << getName() << dataVersion << _dataVersion << "mesh-size is" << _mesh->getNumVertices(); - - _voxelData = newVoxelData; - _dataVersion = dataVersion; - - auto now = usecTimestampNow(); - setLastEdited(now); - setLastBroadcast(now); - - EntityItemProperties properties = getProperties(); - properties.setVoxelDataDirty(); - properties.setLastEdited(now); - - EntityTreeElement* element = getElement(); - EntityTree* tree = element ? element->getTree() : nullptr; - EntitySimulation* simulation = tree ? tree->getSimulation() : nullptr; - PhysicalEntitySimulation* peSimulation = static_cast(simulation); - EntityEditPacketSender* packetSender = peSimulation ? peSimulation->getPacketSender() : nullptr; - if (packetSender) { - packetSender->queueEditEntityMessage(PacketType::EntityEdit, _id, properties); - } -} - - -void RenderablePolyVoxEntityItem::receiveNewMesh(model::MeshPointer newMeshPtr, quint64 meshVersion) { - if (meshVersion <= _meshVersion) { - // qDebug() << _id << "OKOKOK NOPE" << QString(__PRETTY_FUNCTION__) << getName() << meshVersion << _meshVersion; - return; - } - - // int meshSize = -1; - // const model::MeshPointer mesh = _modelGeometry.getMesh(); - // if (mesh) { - // mesh->getVertexBuffer()._size; - // } - - // qDebug() << _id << "OKOKOK!!!" << QString(__PRETTY_FUNCTION__) << getName() << meshVersion << _meshVersion << "mesh-size is" << meshSize; - // qDebug() << _id << "OKOKOK!!!" << QString(__PRETTY_FUNCTION__) << getName() << meshVersion << _meshVersion << "mesh-size is" << _mesh->getNumVertices() << "new mesh-size is" << newMeshPtr->getNumVertices(); - - _dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS; - _modelGeometryLock.lockForWrite(); - // _modelGeometry.setMesh(newMeshPtr); - _mesh = newMeshPtr; - _meshVersion = meshVersion; - _modelGeometryLock.unlock(); - - // qDebug() << _id << "OKOKOK after" << getName() << " mesh-size is" << _modelGeometry.getMesh()->getVertexBuffer()._size << "\n"; - // qDebug() << _id << "OKOKOK after" << getName() << " mesh-size is" << _mesh->getNumVertices(); - // qDebug() << _id << "OKOKOK after" << getName() << " mesh-size is" << newMeshPtr->getNumVertices(); - - computeShapeInfoWorker(); -} - void RenderablePolyVoxEntityItem::setVoxelData(QByteArray voxelData) { - // qDebug() << "5d301155-faf9-44dd-8e8b-061a03d42c0f setVoxelData for" << getName() << getID() << ((void *)this); - _voxelDataLock.lockForWrite(); - if (/*_dataVersion > 1 &&*/ _voxelData == voxelData) { - // qDebug() << _id << "OKOKOK NOPE" << QString(__PRETTY_FUNCTION__) << getName() << _dataVersion << getID() << ((void *)this); + if (_voxelData == voxelData) { _voxelDataLock.unlock(); return; } - // qDebug() << _id << "OKOKOK" << QString(__PRETTY_FUNCTION__) << getName() << _dataVersion; - _voxelData = voxelData; - _async.decompressVolumeData(_voxelData, ++_modelVersion); + _voxelDataDirty = true; _voxelDataLock.unlock(); + decompressVolumeData(); } -void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { - if (/*_meshVersion > 1 && _shapeVersion > 1 &&*/ _voxelVolumeSize == voxelVolumeSize) { - // qDebug() << _id << "OKOKOK NOPE" << QString(__PRETTY_FUNCTION__) << getName() << _meshVersion << ((void *)this); - return; - } - - // qDebug() << _id << "OKOKOK" << QString(__PRETTY_FUNCTION__) << getName() << ((void *)this); - - PolyVoxEntityItem::setVoxelVolumeSize(voxelVolumeSize); - _async.setVoxelVolumeSize(_voxelVolumeSize); - // 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. - _voxelDataLock.lockForRead(); - _async.decompressVolumeData(_voxelData, ++_modelVersion); - _voxelDataLock.unlock(); -} void RenderablePolyVoxEntityItem::setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) { - if (/*_meshVersion > 1 && _shapeVersion > 1 &&*/ _voxelSurfaceStyle == voxelSurfaceStyle) { - // qDebug() << _id << "OKOKOK NOPE" << QString(__PRETTY_FUNCTION__) << getName() << _meshVersion; + if (_voxelSurfaceStyle == voxelSurfaceStyle) { return; } - // qDebug() << _id << "OKOKOK" << QString(__PRETTY_FUNCTION__) << getName(); + // if we are switching to or from "edged" we need to force a resize of _volData. + bool wasEdged = (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || + _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES); + bool willBeEdged = (voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || + voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES); - _voxelSurfaceStyle = voxelSurfaceStyle; - _async.updateVoxelSurfaceStyle(_voxelSurfaceStyle, ++_modelVersion); + if (wasEdged != willBeEdged) { + _volDataLock.lockForWrite(); + _volDataDirty = true; + if (_volData) { + delete _volData; + } + _volData = nullptr; + _voxelSurfaceStyle = voxelSurfaceStyle; + _volDataLock.unlock(); + setVoxelVolumeSize(_voxelVolumeSize); + decompressVolumeData(); + } else { + _voxelSurfaceStyle = voxelSurfaceStyle; + getMesh(); + } } @@ -210,6 +125,7 @@ glm::vec3 RenderablePolyVoxEntityItem::getSurfacePositionAdjustment() const { 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(); @@ -237,24 +153,47 @@ glm::mat4 RenderablePolyVoxEntityItem::worldToVoxelMatrix() const { return worldToModelMatrix; } -uint8_t RenderablePolyVoxEntityItem::getVoxel(int x, int y, int z) { - return _async.getVoxel(x, y, z); -} bool RenderablePolyVoxEntityItem::setVoxel(int x, int y, int z, uint8_t toValue) { + auto now = usecTimestampNow(); if (_locked) { return false; } - return _async.setVoxel(x, y, z, toValue, ++_modelVersion); + + _volDataLock.lockForWrite(); + bool result = setVoxelInternal(x, y, z, toValue); + _volDataDirty = true; + _volDataLock.unlock(); + compressVolumeDataAndSendEditPacket(); + + auto timeSpent = usecTimestampNow() - now; + qDebug() << "RenderablePolyVoxEntityItem::setVoxel timeSpent =" << timeSpent; + return result; } + bool RenderablePolyVoxEntityItem::setAll(uint8_t toValue) { + bool result = false; if (_locked) { - return false; + return result; } - return _async.setAll(toValue, ++_modelVersion); + + _volDataLock.lockForWrite(); + _volDataDirty = true; + for (int z = 0; z < _voxelVolumeSize.z; z++) { + for (int y = 0; y < _voxelVolumeSize.y; y++) { + for (int x = 0; x < _voxelVolumeSize.x; x++) { + result |= setVoxelInternal(x, y, z, toValue); + } + } + } + _volDataLock.unlock(); + compressVolumeDataAndSendEditPacket(); + return result; } + + bool RenderablePolyVoxEntityItem::setVoxelInVolume(glm::vec3 position, uint8_t toValue) { if (_locked) { return false; @@ -265,10 +204,31 @@ bool RenderablePolyVoxEntityItem::setVoxelInVolume(glm::vec3 position, uint8_t t } bool RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue) { + bool result = false; if (_locked) { - return false; + return result; } - return _async.setSphereInVolume(center, radius, toValue, ++_modelVersion); + + // This three-level for loop iterates over every voxel in the volume + _volDataLock.lockForWrite(); + _volDataDirty = true; + 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 + 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 set its value + if (fDistToCenter <= radius) { + result |= setVoxelInternal(x, y, z, toValue); + } + } + } + } + _volDataLock.unlock(); + compressVolumeDataAndSendEditPacket(); + return result; } bool RenderablePolyVoxEntityItem::setSphere(glm::vec3 centerWorldCoords, float radiusWorldCoords, uint8_t toValue) { @@ -344,7 +304,7 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o glm::vec4 farInVoxel = wtvMatrix * glm::vec4(farPoint, 1.0f); glm::vec4 result; - PolyVox::RaycastResult raycastResult = _async.doRayCast(originInVoxel, farInVoxel, result); + PolyVox::RaycastResult raycastResult = doRayCast(originInVoxel, farInVoxel, result); if (raycastResult == PolyVox::RaycastResults::Completed) { // the ray completed its path -- nothing was hit. return false; @@ -405,25 +365,539 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o // virtual ShapeType RenderablePolyVoxEntityItem::getShapeType() const { - if (_async.getOnCount() > 0) { - return SHAPE_TYPE_COMPOUND; - } - return SHAPE_TYPE_NONE; + return SHAPE_TYPE_COMPOUND; } bool RenderablePolyVoxEntityItem::isReadyToComputeShape() { - return (_shapeVersion == _modelVersion); + _meshLock.lockForRead(); + if (_meshDirty) { + _meshLock.unlock(); + computeShapeInfoWorker(); + return false; + } + _meshLock.unlock(); + return true; } -bool RenderablePolyVoxEntityItem::computeShapeInfoWorker() { - ShapeType type = getShapeType(); - if (type != SHAPE_TYPE_COMPOUND) { - _shapeInfoLock.lockForWrite(); - EntityItem::computeShapeInfo(_shapeInfo); - _shapeInfoLock.unlock(); - return true; +void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) { + _shapeInfoLock.lockForRead(); + info = _shapeInfo; + _shapeInfoLock.unlock(); +} + +void RenderablePolyVoxEntityItem::setXTextureURL(QString xTextureURL) { + PolyVoxEntityItem::setXTextureURL(xTextureURL); +} + +void RenderablePolyVoxEntityItem::setYTextureURL(QString yTextureURL) { + PolyVoxEntityItem::setYTextureURL(yTextureURL); +} + +void RenderablePolyVoxEntityItem::setZTextureURL(QString zTextureURL) { + PolyVoxEntityItem::setZTextureURL(zTextureURL); +} + +void RenderablePolyVoxEntityItem::render(RenderArgs* args) { + PerformanceTimer perfTimer("RenderablePolyVoxEntityItem::render"); + assert(getType() == EntityTypes::PolyVox); + Q_ASSERT(args->_batch); + + _volDataLock.lockForRead(); + if (_volDataDirty) { + getMesh(); + } + _volDataLock.unlock(); + + _meshLock.lockForRead(); + model::MeshPointer mesh = _mesh; + _meshLock.unlock(); + + if (!_pipeline) { + gpu::ShaderPointer vertexShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(polyvox_vert))); + gpu::ShaderPointer pixelShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(polyvox_frag))); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("materialBuffer"), MATERIAL_GPU_SLOT)); + slotBindings.insert(gpu::Shader::Binding(std::string("xMap"), 0)); + slotBindings.insert(gpu::Shader::Binding(std::string("yMap"), 1)); + slotBindings.insert(gpu::Shader::Binding(std::string("zMap"), 2)); + + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vertexShader, pixelShader)); + gpu::Shader::makeProgram(*program, slotBindings); + + auto state = std::make_shared(); + state->setCullMode(gpu::State::CULL_BACK); + state->setDepthTest(true, true, gpu::LESS_EQUAL); + + _pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); } + gpu::Batch& batch = *args->_batch; + batch.setPipeline(_pipeline); + + Transform transform(voxelToWorldMatrix()); + batch.setModelTransform(transform); + batch.setInputFormat(mesh->getVertexFormat()); + batch.setInputBuffer(gpu::Stream::POSITION, mesh->getVertexBuffer()); + batch.setInputBuffer(gpu::Stream::NORMAL, + mesh->getVertexBuffer()._buffer, + sizeof(float) * 3, + mesh->getVertexBuffer()._stride); + batch.setIndexBuffer(gpu::UINT32, mesh->getIndexBuffer()._buffer, 0); + + if (!_xTextureURL.isEmpty() && !_xTexture) { + _xTexture = DependencyManager::get()->getTexture(_xTextureURL); + } + if (!_yTextureURL.isEmpty() && !_yTexture) { + _yTexture = DependencyManager::get()->getTexture(_yTextureURL); + } + if (!_zTextureURL.isEmpty() && !_zTexture) { + _zTexture = DependencyManager::get()->getTexture(_zTextureURL); + } + + if (_xTexture) { + batch.setResourceTexture(0, _xTexture->getGPUTexture()); + } else { + batch.setResourceTexture(0, DependencyManager::get()->getWhiteTexture()); + } + if (_yTexture) { + batch.setResourceTexture(1, _yTexture->getGPUTexture()); + } else { + batch.setResourceTexture(1, DependencyManager::get()->getWhiteTexture()); + } + if (_zTexture) { + batch.setResourceTexture(2, _zTexture->getGPUTexture()); + } else { + batch.setResourceTexture(2, DependencyManager::get()->getWhiteTexture()); + } + + int voxelVolumeSizeLocation = _pipeline->getProgram()->getUniforms().findLocation("voxelVolumeSize"); + batch._glUniform3f(voxelVolumeSizeLocation, _voxelVolumeSize.x, _voxelVolumeSize.y, _voxelVolumeSize.z); + + batch.drawIndexed(gpu::TRIANGLES, mesh->getNumIndices(), 0); + + RenderableDebugableEntityItem::render(this, args); +} + +bool RenderablePolyVoxEntityItem::addToScene(EntityItemPointer self, + std::shared_ptr scene, + render::PendingChanges& pendingChanges) { + _myItem = scene->allocateID(); + + auto renderItem = std::make_shared(shared_from_this()); + auto renderData = PolyVoxPayload::Pointer(renderItem); + auto renderPayload = std::make_shared(renderData); + + pendingChanges.resetItem(_myItem, renderPayload); + + return true; +} + +void RenderablePolyVoxEntityItem::removeFromScene(EntityItemPointer self, + std::shared_ptr scene, + render::PendingChanges& pendingChanges) { + pendingChanges.removeItem(_myItem); +} + +namespace render { + template <> const ItemKey payloadGetKey(const PolyVoxPayload::Pointer& payload) { + return ItemKey::Builder::opaqueShape(); + } + + template <> const Item::Bound payloadGetBound(const PolyVoxPayload::Pointer& payload) { + if (payload && payload->_owner) { + auto polyVoxEntity = std::dynamic_pointer_cast(payload->_owner); + return polyVoxEntity->getAABox(); + } + return render::Item::Bound(); + } + + template <> void payloadRender(const PolyVoxPayload::Pointer& payload, RenderArgs* args) { + if (args && payload && payload->_owner) { + payload->_owner->render(args); + } + } +} + + +glm::vec3 RenderablePolyVoxEntityItem::voxelCoordsToWorldCoords(glm::vec3& voxelCoords) const { + return glm::vec3(voxelToWorldMatrix() * glm::vec4(voxelCoords, 1.0f)); +} + +glm::vec3 RenderablePolyVoxEntityItem::worldCoordsToVoxelCoords(glm::vec3& worldCoords) const { + return glm::vec3(worldToVoxelMatrix() * glm::vec4(worldCoords, 1.0f)); +} + +glm::vec3 RenderablePolyVoxEntityItem::voxelCoordsToLocalCoords(glm::vec3& voxelCoords) const { + return glm::vec3(voxelToLocalMatrix() * glm::vec4(voxelCoords, 0.0f)); +} + +glm::vec3 RenderablePolyVoxEntityItem::localCoordsToVoxelCoords(glm::vec3& localCoords) const { + return glm::vec3(localToVoxelMatrix() * glm::vec4(localCoords, 0.0f)); +} + + +void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { + if (_volData && _voxelVolumeSize == voxelVolumeSize) { + return; + } + + _volDataLock.lockForWrite(); + _volDataDirty = true; + _voxelVolumeSize = voxelVolumeSize; + + if (_volData) { + delete _volData; + } + _onCount = 0; + + if (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || + _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES) { + // 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, // 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); + _volDataLock.unlock(); + decompressVolumeData(); +} + + +bool RenderablePolyVoxEntityItem::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: + case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: + 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; +} + + +uint8_t RenderablePolyVoxEntityItem::getVoxel(int x, int y, int z) { + _volDataLock.lockForRead(); + auto result = getVoxelInternal(x, y, z); + _volDataLock.unlock(); + return result; +} + + +uint8_t RenderablePolyVoxEntityItem::getVoxelInternal(int x, int y, int z) { + 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. + + uint8_t result; + if (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || + _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES) { + result = _volData->getVoxelAt(x + 1, y + 1, z + 1); + } else { + result = _volData->getVoxelAt(x, y, z); + } + + return result; +} + + +bool RenderablePolyVoxEntityItem::setVoxelInternal(int x, int y, int z, uint8_t toValue) { + // set a voxel without recompressing the voxel data + bool result = false; + if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) { + return result; + } + + result = updateOnCount(x, y, z, toValue); + + assert(_volData); + if (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || + _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES) { + _volData->setVoxelAt(x + 1, y + 1, z + 1, toValue); + } else { + _volData->setVoxelAt(x, y, z, toValue); + } + + return result; +} + + +bool RenderablePolyVoxEntityItem::updateOnCount(int x, int y, int z, uint8_t toValue) { + // keep _onCount up to date + if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) { + return false; + } + + uint8_t uVoxelValue = getVoxelInternal(x, y, z); + if (toValue != 0) { + if (uVoxelValue == 0) { + _onCount++; + return true; + } + } else { + // toValue == 0 + if (uVoxelValue != 0) { + _onCount--; + assert(_onCount >= 0); + return true; + } + } + return false; +} + + +PolyVox::RaycastResult RenderablePolyVoxEntityItem::doRayCast(glm::vec4 originInVoxel, + glm::vec4 farInVoxel, + glm::vec4& result) const { + PolyVox::Vector3DFloat startPoint(originInVoxel.x, originInVoxel.y, originInVoxel.z); + PolyVox::Vector3DFloat endPoint(farInVoxel.x, farInVoxel.y, farInVoxel.z); + + _volDataLock.lockForRead(); + RaycastFunctor callback(_volData); + PolyVox::RaycastResult raycastResult = PolyVox::raycastWithEndpoints(_volData, startPoint, endPoint, callback); + _volDataLock.unlock(); + + // result is in voxel-space coordinates. + result = callback._result - glm::vec4(0.5f, 0.5f, 0.5f, 0.0f); + return raycastResult; +} + + + +void RenderablePolyVoxEntityItem::decompressVolumeData() { + _threadRunning.acquire(); + QtConcurrent::run(this, &RenderablePolyVoxEntityItem::decompressVolumeDataAsync); +} + + +// take compressed data and expand it into _volData. +void RenderablePolyVoxEntityItem::decompressVolumeDataAsync() { + _voxelDataLock.lockForRead(); + QDataStream reader(_voxelData); + quint16 voxelXSize, voxelYSize, voxelZSize; + reader >> voxelXSize; + reader >> voxelYSize; + reader >> voxelZSize; + + if (voxelXSize == 0 || voxelXSize > PolyVoxEntityItem::MAX_VOXEL_DIMENSION || + voxelYSize == 0 || voxelYSize > PolyVoxEntityItem::MAX_VOXEL_DIMENSION || + voxelZSize == 0 || voxelZSize > PolyVoxEntityItem::MAX_VOXEL_DIMENSION) { + qDebug() << "voxelSize is not reasonable, skipping decompressions." + << voxelXSize << voxelYSize << voxelZSize << getName() << getID(); + _voxelDataDirty = false; + _voxelDataLock.unlock(); + _threadRunning.release(); + return; + } + + int rawSize = voxelXSize * voxelYSize * voxelZSize; + + QByteArray compressedData; + reader >> compressedData; + _voxelDataDirty = false; + _voxelDataLock.unlock(); + QByteArray uncompressedData = qUncompress(compressedData); + + if (uncompressedData.size() != rawSize) { + qDebug() << "PolyVox decompress -- size is (" << voxelXSize << voxelYSize << voxelZSize << ")" + << "so expected uncompressed length of" << rawSize << "but length is" << uncompressedData.size() + << getName() << getID(); + _threadRunning.release(); + return; + } + + _volDataLock.lockForWrite(); + _volDataDirty = true; + 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; + setVoxelInternal(x, y, z, uncompressedData[uncompressedIndex]); + } + } + } + _volDataLock.unlock(); + _threadRunning.release(); +} + +void RenderablePolyVoxEntityItem::compressVolumeDataAndSendEditPacket() { + _threadRunning.acquire(); + QtConcurrent::run(this, &RenderablePolyVoxEntityItem::compressVolumeDataAndSendEditPacketAsync); +} + + +// 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::compressVolumeDataAndSendEditPacketAsync() { + quint16 voxelXSize = _voxelVolumeSize.x; + quint16 voxelYSize = _voxelVolumeSize.y; + quint16 voxelZSize = _voxelVolumeSize.z; + int rawSize = voxelXSize * voxelYSize * voxelZSize; + + QByteArray uncompressedData = QByteArray(rawSize, '\0'); + + _volDataLock.lockForRead(); + for (int z = 0; z < voxelZSize; z++) { + for (int y = 0; y < voxelYSize; y++) { + for (int x = 0; x < voxelXSize; x++) { + uint8_t uVoxelValue = getVoxelInternal(x, y, z); + int uncompressedIndex = + z * voxelYSize * voxelXSize + + y * voxelXSize + + x; + uncompressedData[uncompressedIndex] = uVoxelValue; + } + } + } + _volDataLock.unlock(); + + 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) { + // HACK -- until we have a way to allow for properties larger than MTU, don't update. + // revert the active voxel-space to the last version that fit. + // XXX + qDebug() << "compressed voxel data is too large" << getName() << getID(); + _threadRunning.release(); + return; + } + + auto now = usecTimestampNow(); + setLastEdited(now); + setLastBroadcast(now); + + _voxelDataLock.lockForWrite(); + _voxelDataDirty = true; + _voxelData = newVoxelData; + _voxelDataLock.unlock(); + + EntityItemProperties properties = getProperties(); + properties.setVoxelDataDirty(); + properties.setLastEdited(now); + + EntityTreeElement* element = getElement(); + EntityTree* tree = element ? element->getTree() : nullptr; + EntitySimulation* simulation = tree ? tree->getSimulation() : nullptr; + PhysicalEntitySimulation* peSimulation = static_cast(simulation); + EntityEditPacketSender* packetSender = peSimulation ? peSimulation->getPacketSender() : nullptr; + if (packetSender) { + packetSender->queueEditEntityMessage(PacketType::EntityEdit, _id, properties); + } + _threadRunning.release(); +} + +void RenderablePolyVoxEntityItem::getMesh() { + _threadRunning.acquire(); + QtConcurrent::run(this, &RenderablePolyVoxEntityItem::getMeshAsync); +} + + +void RenderablePolyVoxEntityItem::getMeshAsync() { + model::MeshPointer mesh(new model::Mesh()); + + // A mesh object to hold the result of surface extraction + PolyVox::SurfaceMesh polyVoxMesh; + + _volDataLock.lockForRead(); + switch (_voxelSurfaceStyle) { + case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: + case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: { + PolyVox::MarchingCubesSurfaceExtractor> surfaceExtractor + (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); + surfaceExtractor.execute(); + break; + } + case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: + case PolyVoxEntityItem::SURFACE_CUBIC: { + PolyVox::CubicSurfaceExtractorWithNormals> surfaceExtractor + (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); + surfaceExtractor.execute(); + break; + } + } + + // convert PolyVox mesh to a Sam mesh + const std::vector& vecIndices = polyVoxMesh.getIndices(); + auto indexBuffer = std::make_shared(vecIndices.size() * sizeof(uint32_t), + (gpu::Byte*)vecIndices.data()); + auto indexBufferPtr = gpu::BufferPointer(indexBuffer); + 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 = std::make_shared(vecVertices.size() * sizeof(PolyVox::PositionMaterialNormal), + (gpu::Byte*)vecVertices.data()); + auto vertexBufferPtr = gpu::BufferPointer(vertexBuffer); + gpu::Resource::Size vertexBufferSize = 0; + if (vertexBufferPtr->getSize() > sizeof(float) * 3) { + vertexBufferSize = vertexBufferPtr->getSize() - sizeof(float) * 3; + } + auto vertexBufferView = new gpu::BufferView(vertexBufferPtr, 0, vertexBufferSize, 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, + vertexBufferPtr->getSize() - sizeof(float) * 3, + sizeof(PolyVox::PositionMaterialNormal), + gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW))); + + _meshLock.lockForWrite(); + _dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS; + _mesh = mesh; + _meshDirty = true; + _meshLock.unlock(); + _volDataDirty = false; + _volDataLock.unlock(); + + _threadRunning.release(); +} + +void RenderablePolyVoxEntityItem::computeShapeInfoWorker() { + _threadRunning.acquire(); + QtConcurrent::run(this, &RenderablePolyVoxEntityItem::computeShapeInfoWorkerAsync); +} + + +void RenderablePolyVoxEntityItem::computeShapeInfoWorkerAsync() { QVector> points; AABox box; glm::mat4 vtoM = voxelToLocalMatrix(); @@ -433,15 +907,11 @@ bool RenderablePolyVoxEntityItem::computeShapeInfoWorker() { /* pull each triangle in the mesh into a polyhedron which can be collided with */ unsigned int i = 0; - // _modelGeometryLock.lockForRead(); - // const model::MeshPointer mesh = _modelGeometry.getMesh(); - // _modelGeometryLock.unlock(); - + _meshLock.lockForRead(); model::MeshPointer mesh = _mesh; - _modelGeometryLock.lockForRead(); const gpu::BufferView vertexBufferView = mesh->getVertexBuffer(); const gpu::BufferView& indexBufferView = mesh->getIndexBuffer(); - _modelGeometryLock.unlock(); + _meshLock.unlock(); gpu::BufferView::Iterator it = indexBufferView.cbegin(); while (it != indexBufferView.cend()) { @@ -548,656 +1018,20 @@ bool RenderablePolyVoxEntityItem::computeShapeInfoWorker() { _shapeInfoLock.lockForWrite(); EntityItem::computeShapeInfo(_shapeInfo); _shapeInfoLock.unlock(); - return true; + _threadRunning.release(); + return; } glm::vec3 collisionModelDimensions = box.getDimensions(); QByteArray b64 = _voxelData.toBase64(); _shapeInfoLock.lockForWrite(); - _shapeInfo.setParams(type, collisionModelDimensions, QString(b64)); + _shapeInfo.setParams(SHAPE_TYPE_COMPOUND, collisionModelDimensions, QString(b64)); _shapeInfo.setConvexHulls(points); - _shapeVersion = _modelVersion; _shapeInfoLock.unlock(); - return true; -} - -void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) { - _shapeInfoLock.lockForRead(); - info = _shapeInfo; - _shapeInfoLock.unlock(); -} - -void RenderablePolyVoxEntityItem::setXTextureURL(QString xTextureURL) { - PolyVoxEntityItem::setXTextureURL(xTextureURL); -} - -void RenderablePolyVoxEntityItem::setYTextureURL(QString yTextureURL) { - PolyVoxEntityItem::setYTextureURL(yTextureURL); -} - -void RenderablePolyVoxEntityItem::setZTextureURL(QString zTextureURL) { - PolyVoxEntityItem::setZTextureURL(zTextureURL); -} - -void RenderablePolyVoxEntityItem::render(RenderArgs* args) { - PerformanceTimer perfTimer("RenderablePolyVoxEntityItem::render"); - assert(getType() == EntityTypes::PolyVox); - Q_ASSERT(args->_batch); - - if (_meshVersion < _modelVersion || - _dataVersion < _modelVersion) { - _async.decompressVolumeData(_voxelData, _modelVersion); - _async.updateVoxelSurfaceStyle(_voxelSurfaceStyle, _modelVersion); - } - - model::MeshPointer mesh = _mesh; - - // if (_meshVersion == 0) { - // return; // we have no mesh - // } - - if (!_pipeline) { - gpu::ShaderPointer vertexShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(polyvox_vert))); - gpu::ShaderPointer pixelShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(polyvox_frag))); - - gpu::Shader::BindingSet slotBindings; - slotBindings.insert(gpu::Shader::Binding(std::string("materialBuffer"), MATERIAL_GPU_SLOT)); - slotBindings.insert(gpu::Shader::Binding(std::string("xMap"), 0)); - slotBindings.insert(gpu::Shader::Binding(std::string("yMap"), 1)); - slotBindings.insert(gpu::Shader::Binding(std::string("zMap"), 2)); - - gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vertexShader, pixelShader)); - gpu::Shader::makeProgram(*program, slotBindings); - - auto state = std::make_shared(); - state->setCullMode(gpu::State::CULL_BACK); - state->setDepthTest(true, true, gpu::LESS_EQUAL); - - _pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); - } - - gpu::Batch& batch = *args->_batch; - batch.setPipeline(_pipeline); - - // _modelGeometryLock.lockForRead(); - // auto mesh = _modelGeometry.getMesh(); - - Transform transform(voxelToWorldMatrix()); - batch.setModelTransform(transform); - batch.setInputFormat(mesh->getVertexFormat()); - batch.setInputBuffer(gpu::Stream::POSITION, mesh->getVertexBuffer()); - batch.setInputBuffer(gpu::Stream::NORMAL, - mesh->getVertexBuffer()._buffer, - sizeof(float) * 3, - mesh->getVertexBuffer()._stride); - batch.setIndexBuffer(gpu::UINT32, mesh->getIndexBuffer()._buffer, 0); - - if (!_xTextureURL.isEmpty() && !_xTexture) { - _xTexture = DependencyManager::get()->getTexture(_xTextureURL); - } - if (!_yTextureURL.isEmpty() && !_yTexture) { - _yTexture = DependencyManager::get()->getTexture(_yTextureURL); - } - if (!_zTextureURL.isEmpty() && !_zTexture) { - _zTexture = DependencyManager::get()->getTexture(_zTextureURL); - } - - if (_xTexture) { - batch.setResourceTexture(0, _xTexture->getGPUTexture()); - } else { - batch.setResourceTexture(0, DependencyManager::get()->getWhiteTexture()); - } - if (_yTexture) { - batch.setResourceTexture(1, _yTexture->getGPUTexture()); - } else { - batch.setResourceTexture(1, DependencyManager::get()->getWhiteTexture()); - } - if (_zTexture) { - batch.setResourceTexture(2, _zTexture->getGPUTexture()); - } else { - batch.setResourceTexture(2, DependencyManager::get()->getWhiteTexture()); - } - - int voxelVolumeSizeLocation = _pipeline->getProgram()->getUniforms().findLocation("voxelVolumeSize"); - batch._glUniform3f(voxelVolumeSizeLocation, _voxelVolumeSize.x, _voxelVolumeSize.y, _voxelVolumeSize.z); - - batch.drawIndexed(gpu::TRIANGLES, mesh->getNumIndices(), 0); - // _modelGeometryLock.unlock(); - - RenderableDebugableEntityItem::render(this, args); -} - -bool RenderablePolyVoxEntityItem::addToScene(EntityItemPointer self, - std::shared_ptr scene, - render::PendingChanges& pendingChanges) { - _myItem = scene->allocateID(); - - auto renderItem = std::make_shared(shared_from_this()); - auto renderData = PolyVoxPayload::Pointer(renderItem); - auto renderPayload = std::make_shared(renderData); - - pendingChanges.resetItem(_myItem, renderPayload); - - return true; -} - -void RenderablePolyVoxEntityItem::removeFromScene(EntityItemPointer self, - std::shared_ptr scene, - render::PendingChanges& pendingChanges) { - pendingChanges.removeItem(_myItem); -} - -namespace render { - template <> const ItemKey payloadGetKey(const PolyVoxPayload::Pointer& payload) { - return ItemKey::Builder::opaqueShape(); - } - - template <> const Item::Bound payloadGetBound(const PolyVoxPayload::Pointer& payload) { - if (payload && payload->_owner) { - auto polyVoxEntity = std::dynamic_pointer_cast(payload->_owner); - return polyVoxEntity->getAABox(); - } - return render::Item::Bound(); - } - - template <> void payloadRender(const PolyVoxPayload::Pointer& payload, RenderArgs* args) { - if (args && payload && payload->_owner) { - payload->_owner->render(args); - } - } -} - -glm::vec3 RenderablePolyVoxEntityItem::voxelCoordsToWorldCoords(glm::vec3& voxelCoords) const { - return glm::vec3(voxelToWorldMatrix() * glm::vec4(voxelCoords, 1.0f)); -} - -glm::vec3 RenderablePolyVoxEntityItem::worldCoordsToVoxelCoords(glm::vec3& worldCoords) const { - return glm::vec3(worldToVoxelMatrix() * glm::vec4(worldCoords, 1.0f)); -} - -glm::vec3 RenderablePolyVoxEntityItem::voxelCoordsToLocalCoords(glm::vec3& voxelCoords) const { - return glm::vec3(voxelToLocalMatrix() * glm::vec4(voxelCoords, 0.0f)); -} - -glm::vec3 RenderablePolyVoxEntityItem::localCoordsToVoxelCoords(glm::vec3& localCoords) const { - return glm::vec3(localToVoxelMatrix() * glm::vec4(localCoords, 0.0f)); -} - - -RenderablePolyVoxAsynchronous::RenderablePolyVoxAsynchronous(PolyVoxEntityItem::PolyVoxSurfaceStyle voxelSurfaceStyle, - glm::vec3 voxelVolumeSize, - RenderablePolyVoxEntityItem* owner) : - _voxelSurfaceStyle(voxelSurfaceStyle), - _onCount(0), - _owner(owner) { - setVoxelVolumeSize(voxelVolumeSize); -} - - -RenderablePolyVoxAsynchronous::~RenderablePolyVoxAsynchronous() { - _volDataLock.lockForWrite(); - delete _volData; - _volDataLock.unlock(); -} - - -void RenderablePolyVoxAsynchronous::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { - // qDebug() << _owner->getID() << "OKOKOK" << QString(__PRETTY_FUNCTION__) << _owner->getName(); - - _volDataLock.lockForWrite(); - _voxelVolumeSize = voxelVolumeSize; - - if (_volData) { - delete _volData; - } - _onCount = 0; - - if (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || - _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES) { - // 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, // 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); - _volDataLock.unlock(); -} - - -void RenderablePolyVoxAsynchronous::updateVoxelSurfaceStyle(PolyVoxEntityItem::PolyVoxSurfaceStyle voxelSurfaceStyle, - quint64 modelVersion) { - - // qDebug() << _owner->getID() << "OKOKOK" << QString(__PRETTY_FUNCTION__) << _owner->getName(); - - _volDataLock.lockForWrite(); - // if we are switching to or from "edged" we need to force a resize of _volData. - bool wasEdged = (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || - _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES); - bool willBeEdged = (voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || - voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES); - - if (wasEdged != willBeEdged) { - if (_volData) { - delete _volData; - } - _volData = nullptr; - _voxelSurfaceStyle = voxelSurfaceStyle; - _volDataLock.unlock(); - setVoxelVolumeSize(_voxelVolumeSize); -# ifdef THREAD_POLYVOX - QtConcurrent::run(this, &RenderablePolyVoxAsynchronous::decompressVolumeDataAsync, - _owner->getVoxelData(), modelVersion); -# else - decompressVolumeDataAsync(_owner->getVoxelData(), modelVersion); -# endif - } else { - _voxelSurfaceStyle = voxelSurfaceStyle; - _volDataLock.unlock(); -# ifdef THREAD_POLYVOX - QtConcurrent::run(this, &RenderablePolyVoxAsynchronous::getMeshAsync, modelVersion); -# else - getMeshAsync(modelVersion); -# endif - } -} - - - -bool RenderablePolyVoxAsynchronous::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: - case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: - 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; -} - - -uint8_t RenderablePolyVoxAsynchronous::getVoxel(int x, int y, int z) { - _volDataLock.lockForRead(); - auto result = getVoxelInternal(x, y, z); - _volDataLock.unlock(); - return result; -} - -uint8_t RenderablePolyVoxAsynchronous::getVoxelInternal(int x, int y, int z) { - 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. - - uint8_t result; - if (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || - _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES) { - result = _volData->getVoxelAt(x + 1, y + 1, z + 1); - } else { - result = _volData->getVoxelAt(x, y, z); - } - - return result; -} - - -bool RenderablePolyVoxAsynchronous::setVoxelInternal(int x, int y, int z, uint8_t toValue) { - // set a voxel without recompressing the voxel data - bool result = false; - if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) { - return result; - } - - result = updateOnCount(x, y, z, toValue); - - assert(_volData); - if (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC || - _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES) { - _volData->setVoxelAt(x + 1, y + 1, z + 1, toValue); - } else { - _volData->setVoxelAt(x, y, z, toValue); - } - - return result; -} - - -bool RenderablePolyVoxAsynchronous::updateOnCount(int x, int y, int z, uint8_t toValue) { - // keep _onCount up to date - if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) { - return false; - } - - uint8_t uVoxelValue = getVoxelInternal(x, y, z); - if (toValue != 0) { - if (uVoxelValue == 0) { - _onCount++; - return true; - } - } else { - // toValue == 0 - if (uVoxelValue != 0) { - _onCount--; - assert(_onCount >= 0); - return true; - } - } - return false; -} - - -bool RenderablePolyVoxAsynchronous::setVoxel(int x, int y, int z, uint8_t toValue, quint64 modelVersion) { - _volDataLock.lockForWrite(); - bool result = setVoxelInternal(x, y, z, toValue); - _volDataLock.unlock(); - if (result) { -# ifdef THREAD_POLYVOX - QtConcurrent::run(this, &RenderablePolyVoxAsynchronous::compressVolumeData, modelVersion); - QtConcurrent::run(this, &RenderablePolyVoxAsynchronous::getMeshAsync, modelVersion); -# else - compressVolumeData(modelVersion); - getMeshAsync(modelVersion); -# endif - } else { - // nothing changed. - // _owner->receiveNewVoxelData(_owner->getVoxelData(), modelVersion); - _owner->setDataVersion(modelVersion); - } - return result; -} - - -bool RenderablePolyVoxAsynchronous::setAll(uint8_t toValue, quint64 modelVersion) { - bool result = false; - - _volDataLock.lockForWrite(); - for (int z = 0; z < _voxelVolumeSize.z; z++) { - for (int y = 0; y < _voxelVolumeSize.y; y++) { - for (int x = 0; x < _voxelVolumeSize.x; x++) { - result |= setVoxelInternal(x, y, z, toValue); - } - } - } - _volDataLock.unlock(); - if (result) { -# ifdef THREAD_POLYVOX - QtConcurrent::run(this, &RenderablePolyVoxAsynchronous::compressVolumeData, modelVersion); - QtConcurrent::run(this, &RenderablePolyVoxAsynchronous::getMeshAsync, modelVersion); -# else - compressVolumeData(modelVersion); - getMeshAsync(modelVersion); -# endif - } else { - // nothing changed. - // _owner->receiveNewVoxelData(_owner->getVoxelData(), modelVersion); - _owner->setDataVersion(modelVersion); - } - return result; -} - - -bool RenderablePolyVoxAsynchronous::setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue, quint64 modelVersion) { - bool result = false; - - // This three-level for loop iterates over every voxel in the volume - _volDataLock.lockForWrite(); - 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 + 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) { - result |= setVoxelInternal(x, y, z, toValue); - } - } - } - } - _volDataLock.unlock(); - if (result) { -# ifdef THREAD_POLYVOX - QtConcurrent::run(this, &RenderablePolyVoxAsynchronous::compressVolumeData, modelVersion); - QtConcurrent::run(this, &RenderablePolyVoxAsynchronous::getMeshAsync, modelVersion); -# else - compressVolumeData(modelVersion); - getMeshAsync(modelVersion); -# endif - } else { - // nothing changed. - // _owner->receiveNewVoxelData(_owner->getVoxelData(), modelVersion); - _owner->setDataVersion(modelVersion); - } - - return result; -} - - -PolyVox::RaycastResult RenderablePolyVoxAsynchronous::doRayCast(glm::vec4 originInVoxel, glm::vec4 farInVoxel, - glm::vec4& result) const { - PolyVox::Vector3DFloat startPoint(originInVoxel.x, originInVoxel.y, originInVoxel.z); - PolyVox::Vector3DFloat endPoint(farInVoxel.x, farInVoxel.y, farInVoxel.z); - - _volDataLock.lockForRead(); - RaycastFunctor callback(_volData); - PolyVox::RaycastResult raycastResult = PolyVox::raycastWithEndpoints(_volData, startPoint, endPoint, callback); - _volDataLock.unlock(); - - // result is in voxel-space coordinates. - result = callback._result - glm::vec4(0.5f, 0.5f, 0.5f, 0.0f); - return raycastResult; -} - - -// take compressed data and expand it into _volData. -void RenderablePolyVoxAsynchronous::decompressVolumeData(QByteArray voxelData, quint64 modelVersion) { - // qDebug() << _owner->getID() << "OKOKOK" << QString(__PRETTY_FUNCTION__) << _owner->getName() << modelVersion; - -# ifdef THREAD_POLYVOX - QtConcurrent::run(this, &RenderablePolyVoxAsynchronous::decompressVolumeDataAsync, voxelData, modelVersion); -# else - decompressVolumeDataAsync(voxelData, modelVersion); -# endif -} - - -void RenderablePolyVoxAsynchronous::decompressVolumeDataAsync(QByteArray voxelData, quint64 modelVersion) { - // qDebug() << _owner->getID() << "OKOKOK" << QString(__PRETTY_FUNCTION__) - // << _owner->getName() << _owner->getID() << modelVersion; - - QDataStream reader(voxelData); - quint16 voxelXSize, voxelYSize, voxelZSize; - reader >> voxelXSize; - reader >> voxelYSize; - reader >> voxelZSize; - - if (voxelXSize == 0 || voxelXSize > PolyVoxEntityItem::MAX_VOXEL_DIMENSION || - voxelYSize == 0 || voxelYSize > PolyVoxEntityItem::MAX_VOXEL_DIMENSION || - voxelZSize == 0 || voxelZSize > PolyVoxEntityItem::MAX_VOXEL_DIMENSION) { - qDebug() << "voxelSize is not reasonable, skipping decompressions." - << voxelXSize << voxelYSize << voxelZSize << _owner->getName() << _owner->getID() << modelVersion - << "5d301155-faf9-44dd-8e8b-061a03d42c0f"; - // _owner->receiveNewVoxelData(_owner->getVoxelData(), modelVersion); - _owner->setDataVersion(modelVersion); - return; - } - - int rawSize = voxelXSize * voxelYSize * voxelZSize; - - QByteArray compressedData; - reader >> compressedData; - QByteArray uncompressedData = qUncompress(compressedData); - - if (uncompressedData.size() != rawSize) { - qDebug() << "PolyVox decompress -- size is (" << voxelXSize << voxelYSize << voxelZSize << ")" - << "so expected uncompressed length of" << rawSize << "but length is" << uncompressedData.size() - << _owner->getName() << _owner->getID() << modelVersion << "5d301155-faf9-44dd-8e8b-061a03d42c0f"; - // _owner->receiveNewVoxelData(_owner->getVoxelData(), modelVersion); - _owner->setDataVersion(modelVersion); - return; - } - - _volDataLock.lockForWrite(); - 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; - setVoxelInternal(x, y, z, uncompressedData[uncompressedIndex]); - } - } - } - _volDataLock.unlock(); - - _owner->receiveNewVoxelData(voxelData, modelVersion); - -# ifdef THREAD_POLYVOX - QFuture future = QtConcurrent::run(this, &RenderablePolyVoxAsynchronous::getMeshAsync, modelVersion); -# else - getMeshAsync(modelVersion); -# endif -} - - -void RenderablePolyVoxAsynchronous::getMeshAsync(quint64 modelVersion) { - // qDebug() << _owner->getID() << "OKOKOK" << QString(__PRETTY_FUNCTION__) << _owner->getName() << modelVersion; - - model::MeshPointer mesh(new model::Mesh()); - - // A mesh object to hold the result of surface extraction - PolyVox::SurfaceMesh polyVoxMesh; - - _volDataLock.lockForRead(); - switch (_voxelSurfaceStyle) { - case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: - case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: { - PolyVox::MarchingCubesSurfaceExtractor> surfaceExtractor - (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); - surfaceExtractor.execute(); - break; - } - case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: - case PolyVoxEntityItem::SURFACE_CUBIC: { - PolyVox::CubicSurfaceExtractorWithNormals> surfaceExtractor - (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); - surfaceExtractor.execute(); - break; - } - } - _volDataLock.unlock(); - - // convert PolyVox mesh to a Sam mesh - const std::vector& vecIndices = polyVoxMesh.getIndices(); - auto indexBuffer = std::make_shared(vecIndices.size() * sizeof(uint32_t), - (gpu::Byte*)vecIndices.data()); - auto indexBufferPtr = gpu::BufferPointer(indexBuffer); - 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 = std::make_shared(vecVertices.size() * sizeof(PolyVox::PositionMaterialNormal), - (gpu::Byte*)vecVertices.data()); - auto vertexBufferPtr = gpu::BufferPointer(vertexBuffer); - gpu::Resource::Size vertexBufferSize = 0; - if (vertexBufferPtr->getSize() > sizeof(float) * 3) { - vertexBufferSize = vertexBufferPtr->getSize() - sizeof(float) * 3; - } - auto vertexBufferView = new gpu::BufferView(vertexBufferPtr, 0, vertexBufferSize, 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, - vertexBufferPtr->getSize() - sizeof(float) * 3, - sizeof(PolyVox::PositionMaterialNormal), - gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW))); - - // qDebug() << _owner->getID() << "OKOKOK -- MADE NEW MESH" << "index-count =" << vecIndices.size() - // << "vertex-count =" << vecVertices.size() << "mesh-vertg-size =" << mesh->getVertexBuffer()._size; - - _owner->receiveNewMesh(mesh, modelVersion); -} - -int RenderablePolyVoxAsynchronous::getOnCount() const { - // XXX get rid of this method - return _onCount; -} - - -// 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 RenderablePolyVoxAsynchronous::compressVolumeData(quint64 modelVersion) { - // qDebug() << _owner->getID() << "OKOKOK" << QString(__PRETTY_FUNCTION__) << _owner->getName() << modelVersion; - - quint16 voxelXSize = _voxelVolumeSize.x; - quint16 voxelYSize = _voxelVolumeSize.y; - quint16 voxelZSize = _voxelVolumeSize.z; - int rawSize = voxelXSize * voxelYSize * voxelZSize; - - QByteArray uncompressedData = QByteArray(rawSize, '\0'); - - _volDataLock.lockForRead(); - for (int z = 0; z < voxelZSize; z++) { - for (int y = 0; y < voxelYSize; y++) { - for (int x = 0; x < voxelXSize; x++) { - uint8_t uVoxelValue = getVoxelInternal(x, y, z); - int uncompressedIndex = - z * voxelYSize * voxelXSize + - y * voxelXSize + - x; - uncompressedData[uncompressedIndex] = uVoxelValue; - } - } - } - _volDataLock.unlock(); - - 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) { - _owner->receiveNewVoxelData(newVoxelData, modelVersion); - } else { - // HACK -- until we have a way to allow for properties larger than MTU, don't update. - // revert the active voxel-space to the last version that fit. - // QtConcurrent::run(this, &RenderablePolyVoxAsynchronous::decompressVolumeDataAsync, - // _owner->getVoxelData(), modelVersion); - // decompressVolumeDataAsync(_owner->getVoxelData(), modelVersion); - // _owner->receiveNewVoxelData(_owner->getVoxelData(), modelVersion); - _owner->setDataVersion(modelVersion); - } + + _meshLock.lockForWrite(); + _meshDirty = false; + _meshLock.unlock(); + _threadRunning.release(); + return; } diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 405cdd07ff..33a5e86398 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -12,7 +12,7 @@ #ifndef hifi_RenderablePolyVoxEntityItem_h #define hifi_RenderablePolyVoxEntityItem_h -#include +#include #include #include @@ -41,56 +41,8 @@ namespace render { template <> void payloadRender(const PolyVoxPayload::Pointer& payload, RenderArgs* args); } -class RenderablePolyVoxEntityItem; - - -class RenderablePolyVoxAsynchronous : public QObject { - Q_OBJECT - - public: - RenderablePolyVoxAsynchronous(PolyVoxEntityItem::PolyVoxSurfaceStyle voxelSurfaceStyle, glm::vec3 voxelVolumeSize, - RenderablePolyVoxEntityItem* owner); - ~RenderablePolyVoxAsynchronous(); - - void setVoxelVolumeSize(glm::vec3 voxelVolumeSize); - void updateVoxelSurfaceStyle(PolyVoxEntityItem::PolyVoxSurfaceStyle voxelSurfaceStyle, quint64 modelVersion); - uint8_t getVoxel(int x, int y, int z); - bool setVoxel(int x, int y, int z, uint8_t toValue, quint64 modelVersion); - bool setAll(uint8_t toValue, quint64 modelVersion); - bool setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue, quint64 modelVersion); - PolyVox::RaycastResult doRayCast(glm::vec4 originInVoxel, glm::vec4 farInVoxel, glm::vec4& result) const; - int getOnCount() const; - void decompressVolumeData(QByteArray voxelData, quint64 modelVersion); - -// signals: -// void doDecompressVolumeDataAsync(QByteArray voxelData, quint64 modelVersion); -// void doGetMeshAsync(quint64 modelVersion); - - - -private: - void decompressVolumeDataAsync(QByteArray voxelData, quint64 modelVersion); - void getMeshAsync(quint64 modelVersion); - - uint8_t getVoxelInternal(int x, int y, int z); - bool updateOnCount(int x, int y, int z, uint8_t new_value); - bool setVoxelInternal(int x, int y, int z, uint8_t toValue); - void compressVolumeData(quint64 modelVersion); - static bool inUserBounds(const PolyVox::SimpleVolume* vol, PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle, - int x, int y, int z); - - PolyVoxEntityItem::PolyVoxSurfaceStyle _voxelSurfaceStyle; - glm::vec3 _voxelVolumeSize; - PolyVox::SimpleVolume* _volData = nullptr; - mutable QReadWriteLock _volDataLock; // lock for _volData - std::atomic_int _onCount; // how many non-zero voxels are in _volData - - RenderablePolyVoxEntityItem* _owner = nullptr; -}; - class RenderablePolyVoxEntityItem : public PolyVoxEntityItem { - public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); @@ -153,21 +105,13 @@ class RenderablePolyVoxEntityItem : public PolyVoxEntityItem { std::shared_ptr scene, render::PendingChanges& pendingChanges); - void receiveNewVoxelData(QByteArray newVoxelData, quint64 dataVersion); - void receiveNewMesh(model::MeshPointer newMeshPtr, quint64 meshVersion); - void setDataVersion(quint64 dataVersion) { _dataVersion = dataVersion; } - private: // The PolyVoxEntityItem class has _voxelData which contains dimensions and compressed voxel data. The dimensions // may not match _voxelVolumeSize. - virtual bool computeShapeInfoWorker(); - - // model::Geometry _modelGeometry; - mutable QReadWriteLock _modelGeometryLock; model::MeshPointer _mesh; - - QFuture _getMeshWorker; + bool _meshDirty; // does collision-shape need to be recomputed? + mutable QReadWriteLock _meshLock{QReadWriteLock::Recursive}; NetworkTexturePointer _xTexture; NetworkTexturePointer _yTexture; @@ -178,17 +122,31 @@ private: static gpu::PipelinePointer _pipeline; ShapeInfo _shapeInfo; - // QFuture _shapeInfoWorker; mutable QReadWriteLock _shapeInfoLock; - // this does work outside of the main thread. - RenderablePolyVoxAsynchronous _async; + PolyVox::SimpleVolume* _volData = nullptr; + mutable QReadWriteLock _volDataLock{QReadWriteLock::Recursive}; // lock for _volData + bool _volDataDirty = false; // does getMesh need to be called? + int _onCount; // how many non-zero voxels are in _volData - quint64 _modelVersion = 1; // local idea of how many changes have happened - // the following are compared against _modelVersion - quint64 _meshVersion = 0; // version of most recently computed mesh - quint64 _dataVersion = 0; // version of most recently compressed voxel data - quint64 _shapeVersion = 0; // version of most recently computed collision shape + bool inUserBounds(const PolyVox::SimpleVolume* vol, PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle, + int x, int y, int z); + uint8_t getVoxelInternal(int x, int y, int z); + bool setVoxelInternal(int x, int y, int z, uint8_t toValue); + bool updateOnCount(int x, int y, int z, uint8_t toValue); + PolyVox::RaycastResult doRayCast(glm::vec4 originInVoxel, glm::vec4 farInVoxel, glm::vec4& result) const; + + // these are run off the main thread + void decompressVolumeData(); + void decompressVolumeDataAsync(); + void compressVolumeDataAndSendEditPacket(); + void compressVolumeDataAndSendEditPacketAsync(); + void getMesh(); + void getMeshAsync(); + void computeShapeInfoWorker(); + void computeShapeInfoWorkerAsync(); + + QSemaphore _threadRunning{1}; }; diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index df3b6d1608..eb81c122e7 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -52,6 +52,7 @@ PolyVoxEntityItem::PolyVoxEntityItem(const EntityItemID& entityItemID, const Ent EntityItem(entityItemID), _voxelVolumeSize(PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE), _voxelData(PolyVoxEntityItem::DEFAULT_VOXEL_DATA), + _voxelDataDirty(true), _voxelSurfaceStyle(PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE), _xTextureURL(PolyVoxEntityItem::DEFAULT_X_TEXTURE_URL), _yTextureURL(PolyVoxEntityItem::DEFAULT_Y_TEXTURE_URL), @@ -192,9 +193,13 @@ void PolyVoxEntityItem::setVoxelData(QByteArray voxelData) _voxelDataLock.lockForWrite(); _voxelData = voxelData; + _voxelDataDirty = true; _voxelDataLock.unlock(); } const QByteArray PolyVoxEntityItem::getVoxelData() const { - return _voxelData; + _voxelDataLock.lockForRead(); + auto result = _voxelData; + _voxelDataLock.unlock(); + return result; } diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index 118f6cc99b..c84dc9f4c1 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -105,8 +105,11 @@ class PolyVoxEntityItem : public EntityItem { protected: glm::vec3 _voxelVolumeSize; // this is always 3 bytes + mutable QReadWriteLock _voxelDataLock; QByteArray _voxelData; + bool _voxelDataDirty; + PolyVoxSurfaceStyle _voxelSurfaceStyle; QString _xTextureURL; From 3c35d90908a906b7c90f50f98dcf521c120ca4d4 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 25 Aug 2015 13:13:01 -0700 Subject: [PATCH 10/22] don't recompute meshes if nothing changed --- .../src/RenderablePolyVoxEntityItem.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index c35a80f188..54ccab0c5e 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -162,9 +162,13 @@ bool RenderablePolyVoxEntityItem::setVoxel(int x, int y, int z, uint8_t toValue) _volDataLock.lockForWrite(); bool result = setVoxelInternal(x, y, z, toValue); - _volDataDirty = true; + if (result) { + _volDataDirty = true; + } _volDataLock.unlock(); - compressVolumeDataAndSendEditPacket(); + if (result) { + compressVolumeDataAndSendEditPacket(); + } auto timeSpent = usecTimestampNow() - now; qDebug() << "RenderablePolyVoxEntityItem::setVoxel timeSpent =" << timeSpent; @@ -188,7 +192,9 @@ bool RenderablePolyVoxEntityItem::setAll(uint8_t toValue) { } } _volDataLock.unlock(); - compressVolumeDataAndSendEditPacket(); + if (result) { + compressVolumeDataAndSendEditPacket(); + } return result; } @@ -227,7 +233,9 @@ bool RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radi } } _volDataLock.unlock(); - compressVolumeDataAndSendEditPacket(); + if (result) { + compressVolumeDataAndSendEditPacket(); + } return result; } From da6a1c958bdcfbc3c6e88934c0ecb7b496a5b72c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 25 Aug 2015 16:40:58 -0700 Subject: [PATCH 11/22] clean up debugging prints --- .../entities-renderer/src/RenderablePolyVoxEntityItem.cpp | 4 +--- libraries/entities/src/PolyVoxEntityItem.cpp | 3 --- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 54ccab0c5e..f220518e7e 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -63,6 +63,7 @@ RenderablePolyVoxEntityItem::RenderablePolyVoxEntityItem(const EntityItemID& ent _yTexture(nullptr), _zTexture(nullptr) { setVoxelVolumeSize(_voxelVolumeSize); + getMeshAsync(); } RenderablePolyVoxEntityItem::~RenderablePolyVoxEntityItem() { @@ -155,7 +156,6 @@ glm::mat4 RenderablePolyVoxEntityItem::worldToVoxelMatrix() const { bool RenderablePolyVoxEntityItem::setVoxel(int x, int y, int z, uint8_t toValue) { - auto now = usecTimestampNow(); if (_locked) { return false; } @@ -170,8 +170,6 @@ bool RenderablePolyVoxEntityItem::setVoxel(int x, int y, int z, uint8_t toValue) compressVolumeDataAndSendEditPacket(); } - auto timeSpent = usecTimestampNow() - now; - qDebug() << "RenderablePolyVoxEntityItem::setVoxel timeSpent =" << timeSpent; return result; } diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index eb81c122e7..d65e05464b 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -188,9 +188,6 @@ void PolyVoxEntityItem::debugDump() const { void PolyVoxEntityItem::setVoxelData(QByteArray voxelData) { - qDebug() << "5d301155-faf9-44dd-8e8b-061a03d42c0f parent setVoxelData for" << getName() << getID() << ((void *)this) - << typeid(*this).name(); - _voxelDataLock.lockForWrite(); _voxelData = voxelData; _voxelDataDirty = true; From d2cfca0424843720a31be5194f1c89eb5915460b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 25 Aug 2015 16:41:10 -0700 Subject: [PATCH 12/22] fix comment typo --- examples/libraries/toolBars.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/libraries/toolBars.js b/examples/libraries/toolBars.js index abe8de8cc3..7ab68cab1c 100644 --- a/examples/libraries/toolBars.js +++ b/examples/libraries/toolBars.js @@ -10,7 +10,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -Overlay2D = function(properties, overlay) { // overlay is an optionnal variable +Overlay2D = function(properties, overlay) { // overlay is an optional variable if (!(typeof(properties) === 'undefined')) { if(typeof(overlay) === 'undefined') { overlay = Overlays.addOverlay("image", properties); From 405c9828b8a50ad6e8cbf307957787272f635524 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 25 Aug 2015 16:41:38 -0700 Subject: [PATCH 13/22] cargo-cult a voxel-editor tool-bar from edit.js --- examples/voxels.js | 264 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 256 insertions(+), 8 deletions(-) diff --git a/examples/voxels.js b/examples/voxels.js index cc3453202a..893aa264d8 100644 --- a/examples/voxels.js +++ b/examples/voxels.js @@ -1,10 +1,233 @@ var controlHeld = false; var shiftHeld = false; + +Script.include([ + "libraries/toolBars.js", +]); + + +// http://headache.hungry.com/~seth/hifi/voxel-add.svg +// http://headache.hungry.com/~seth/hifi/voxel-add.svg +// http://headache.hungry.com/~seth/hifi/voxel-delete.svg +// http://headache.hungry.com/~seth/hifi/voxel-terrain.svg + +var isActive = false; +var toolIconUrl = "http://headache.hungry.com/~seth/hifi/"; +var toolHeight = 50; +var toolWidth = 50; + +var addingVoxels = false; +var deletingVoxels = false; + +offAlpha = 0.5; +onAlpha = 0.9; + + function floorVector(v) { return {x: Math.floor(v.x), y: Math.floor(v.y), z: Math.floor(v.z)}; } +function vectorToString(v){ + return "{" + v.x + ", " + v.x + ", " + v.x + "}"; +} + +var toolBar = (function () { + var that = {}, + toolBar, + activeButton, + addVoxelButton, + deleteVoxelButton, + addTerrainButton; + + function initialize() { + toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.voxel.toolbar", function (windowDimensions, toolbar) { + return { + x: windowDimensions.x - 8*2 - toolbar.width * 2, + y: (windowDimensions.y - toolbar.height) / 2 + }; + }); + + activeButton = toolBar.addTool({ + imageURL: "http://s3.amazonaws.com/hifi-public/images/tools/polyvox.svg", + width: toolWidth, + height: toolHeight, + alpha: onAlpha, + visible: true, + }); + + addVoxelButton = toolBar.addTool({ + imageURL: toolIconUrl + "voxel-add.svg", + subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, + height: toolHeight, + alpha: offAlpha, + visible: false + }); + + deleteVoxelButton = toolBar.addTool({ + imageURL: toolIconUrl + "voxel-delete.svg", + subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, + height: toolHeight, + alpha: offAlpha, + visible: false + }); + + addTerrainButton = toolBar.addTool({ + imageURL: toolIconUrl + "voxel-terrain.svg", + subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, + height: toolHeight, + alpha: onAlpha, + visible: false + }); + + that.setActive(false); + } + + + that.setActive = function(active) { + if (active != isActive) { + isActive = active; + that.showTools(isActive); + } + toolBar.selectTool(activeButton, isActive); + }; + + // Sets visibility of tool buttons, excluding the power button + that.showTools = function(doShow) { + toolBar.showTool(addVoxelButton, doShow); + toolBar.showTool(deleteVoxelButton, doShow); + toolBar.showTool(addTerrainButton, doShow); + }; + + that.mousePressEvent = function (event) { + var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + + if (activeButton === toolBar.clicked(clickedOverlay)) { + that.setActive(!isActive); + return true; + } + + if (addVoxelButton === toolBar.clicked(clickedOverlay)) { + if (addingVoxels) { + addingVoxels = false; + deletingVoxels = false; + toolBar.setAlpha(offAlpha, addVoxelButton); + toolBar.setAlpha(offAlpha, deleteVoxelButton); + toolBar.selectTool(addVoxelButton, false); + toolBar.selectTool(deleteVoxelButton, false); + } else { + addingVoxels = true; + deletingVoxels = false; + toolBar.setAlpha(onAlpha, addVoxelButton); + toolBar.setAlpha(offAlpha, deleteVoxelButton); + } + return true; + } + + if (deleteVoxelButton === toolBar.clicked(clickedOverlay)) { + if (deletingVoxels) { + deletingVoxels = false; + addingVoxels = false; + toolBar.setAlpha(offAlpha, addVoxelButton); + toolBar.setAlpha(offAlpha, deleteVoxelButton); + } else { + deletingVoxels = true; + addingVoxels = false; + toolBar.setAlpha(offAlpha, addVoxelButton); + toolBar.setAlpha(onAlpha, deleteVoxelButton); + } + return true; + } + + if (addTerrainButton === toolBar.clicked(clickedOverlay)) { + addTerrainBlock(); + return true; + } + } + + Window.domainChanged.connect(function() { + that.setActive(false); + }); + + that.cleanup = function () { + toolBar.cleanup(); + // Overlays.deleteOverlay(activeButton); + }; + + + initialize(); + return that; +}()); + + +function addTerrainBlock() { + + var myPosDiv16 = Vec3.multiply(Vec3.sum(MyAvatar.position, {x:8, x:8, z:8}), 1.0 / 16.0); + var myPosDiv16Floored = floorVector(myPosDiv16); + var baseLocation = Vec3.multiply(myPosDiv16Floored, 16.0); + + if (baseLocation.y + 8 > MyAvatar.position.y) { + baseLocation.y -= 16; + } + + print("myPosDiv16 is " + vectorToString(myPosDiv16)); + print("MyPosDiv16Floored is " + vectorToString(myPosDiv16Floored)); + print("baseLocation is " + vectorToString(baseLocation)); + + alreadyThere = Entities.findEntities(baseLocation, 1.0); + for (var i = 0; i < alreadyThere.length; i++) { + var id = alreadyThere[i]; + var properties = Entities.getEntityProperties(id); + if (properties.name == "terrain") { + print("already terrain there"); + return; + } + } + + var polyVoxId = Entities.addEntity({ + type: "PolyVox", + name: "terrain", + position: baseLocation, + dimensions: { x: 16, y: 16, z: 16 }, + voxelVolumeSize: {x:16, y:16, z:16}, + voxelSurfaceStyle: 3 + }); + Entities.setAllVoxels(polyVoxId, 255); + + for (var y = 8; y < 16; y++) { + for (var x = 0; x < 16; x++) { + for (var z = 0; z < 16; z++) { + Entities.setVoxel(polyVoxId, {x: x, y: y, z: z}, 0); + } + } + } + + // for (var x = 1; x <= 14; x++) { + // Entities.setVoxel(polyVoxId, {x: x, y: 1, z: 1}, 255); + // Entities.setVoxel(polyVoxId, {x: x, y: 14, z: 1}, 255); + // Entities.setVoxel(polyVoxId, {x: x, y: 1, z: 14}, 255); + // Entities.setVoxel(polyVoxId, {x: x, y: 14, z: 14}, 255); + // } + // for (var y = 2; y <= 13; y++) { + // Entities.setVoxel(polyVoxId, {x: 1, y: y, z: 1}, 255); + // Entities.setVoxel(polyVoxId, {x: 14, y: y, z: 1}, 255); + // Entities.setVoxel(polyVoxId, {x: 1, y: y, z: 14}, 255); + // Entities.setVoxel(polyVoxId, {x: 14, y: y, z: 14}, 255); + // } + // for (var z = 2; z <= 13; z++) { + // Entities.setVoxel(polyVoxId, {x: 1, y: 1, z: z}, 255); + // Entities.setVoxel(polyVoxId, {x: 14, y: 1, z: z}, 255); + // Entities.setVoxel(polyVoxId, {x: 1, y: 14, z: z}, 255); + // Entities.setVoxel(polyVoxId, {x: 14, y: 14, z: z}, 255); + // } + + return true; +} + + function attemptVoxelChange(pickRayDir, intersection) { var properties = Entities.getEntityProperties(intersection.entityID); @@ -12,25 +235,37 @@ function attemptVoxelChange(pickRayDir, intersection) { return false; } + if (addingVoxels == false && deletingVoxels == false) { + return false; + } + var voxelPosition = Entities.worldCoordsToVoxelCoords(intersection.entityID, intersection.intersection); voxelPosition = Vec3.subtract(voxelPosition, {x: 0.5, y: 0.5, z: 0.5}); var pickRayDirInVoxelSpace = Entities.localCoordsToVoxelCoords(intersection.entityID, pickRayDir); pickRayDirInVoxelSpace = Vec3.normalize(pickRayDirInVoxelSpace); + var doAdd = addingVoxels; + var doDelete = deletingVoxels; + if (controlHeld) { - // hold control to erase a voxel + doAdd = deletingVoxels; + doDelete = addingVoxels; + } + + // } else if (shiftHeld) { + // // return Entities.setAllVoxels(intersection.entityID, 255); + // } + + // Entities.setVoxelSphere(id, intersection.intersection, radius, 0) + + if (doDelete) { var toErasePosition = Vec3.sum(voxelPosition, Vec3.multiply(pickRayDirInVoxelSpace, 0.1)); return Entities.setVoxel(intersection.entityID, floorVector(toErasePosition), 0); - } else if (shiftHeld) { - // hold shift to set all voxels to 255 - return Entities.setAllVoxels(intersection.entityID, 255); - } else { - // no modifier key to add a voxel + } + if (doAdd) { var toDrawPosition = Vec3.subtract(voxelPosition, Vec3.multiply(pickRayDirInVoxelSpace, 0.1)); return Entities.setVoxel(intersection.entityID, floorVector(toDrawPosition), 255); } - - // Entities.setVoxelSphere(id, intersection.intersection, radius, 0) } function mousePressEvent(event) { @@ -38,6 +273,10 @@ function mousePressEvent(event) { return; } + if (toolBar.mousePressEvent(event)) { + return; + } + var pickRay = Camera.computePickRay(event.x, event.y); var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking @@ -76,6 +315,15 @@ function keyReleaseEvent(event) { } +function cleanup() { + for (var i = 0; i < overlays.length; i++) { + Overlays.deleteOverlay(overlays[i]); + } + toolBar.cleanup(); +} + + Controller.mousePressEvent.connect(mousePressEvent); Controller.keyPressEvent.connect(keyPressEvent); Controller.keyReleaseEvent.connect(keyReleaseEvent); +Script.scriptEnding.connect(cleanup); From 6f4683206936b124d092a4d5ed75e355f4a95a71 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 25 Aug 2015 21:30:38 -0700 Subject: [PATCH 14/22] clean up some commented code. attempt to fix ray-casting in non-edged polyvox -- still isn't correct --- examples/voxels.js | 35 +------------- .../src/RenderablePolyVoxEntityItem.cpp | 46 +++++++++++-------- 2 files changed, 29 insertions(+), 52 deletions(-) diff --git a/examples/voxels.js b/examples/voxels.js index 893aa264d8..4c344a7eee 100644 --- a/examples/voxels.js +++ b/examples/voxels.js @@ -1,17 +1,10 @@ var controlHeld = false; var shiftHeld = false; - Script.include([ "libraries/toolBars.js", ]); - -// http://headache.hungry.com/~seth/hifi/voxel-add.svg -// http://headache.hungry.com/~seth/hifi/voxel-add.svg -// http://headache.hungry.com/~seth/hifi/voxel-delete.svg -// http://headache.hungry.com/~seth/hifi/voxel-terrain.svg - var isActive = false; var toolIconUrl = "http://headache.hungry.com/~seth/hifi/"; var toolHeight = 50; @@ -23,7 +16,6 @@ var deletingVoxels = false; offAlpha = 0.5; onAlpha = 0.9; - function floorVector(v) { return {x: Math.floor(v.x), y: Math.floor(v.y), z: Math.floor(v.z)}; } @@ -193,7 +185,7 @@ function addTerrainBlock() { position: baseLocation, dimensions: { x: 16, y: 16, z: 16 }, voxelVolumeSize: {x:16, y:16, z:16}, - voxelSurfaceStyle: 3 + voxelSurfaceStyle: 2 }); Entities.setAllVoxels(polyVoxId, 255); @@ -205,25 +197,6 @@ function addTerrainBlock() { } } - // for (var x = 1; x <= 14; x++) { - // Entities.setVoxel(polyVoxId, {x: x, y: 1, z: 1}, 255); - // Entities.setVoxel(polyVoxId, {x: x, y: 14, z: 1}, 255); - // Entities.setVoxel(polyVoxId, {x: x, y: 1, z: 14}, 255); - // Entities.setVoxel(polyVoxId, {x: x, y: 14, z: 14}, 255); - // } - // for (var y = 2; y <= 13; y++) { - // Entities.setVoxel(polyVoxId, {x: 1, y: y, z: 1}, 255); - // Entities.setVoxel(polyVoxId, {x: 14, y: y, z: 1}, 255); - // Entities.setVoxel(polyVoxId, {x: 1, y: y, z: 14}, 255); - // Entities.setVoxel(polyVoxId, {x: 14, y: y, z: 14}, 255); - // } - // for (var z = 2; z <= 13; z++) { - // Entities.setVoxel(polyVoxId, {x: 1, y: 1, z: z}, 255); - // Entities.setVoxel(polyVoxId, {x: 14, y: 1, z: z}, 255); - // Entities.setVoxel(polyVoxId, {x: 1, y: 14, z: z}, 255); - // Entities.setVoxel(polyVoxId, {x: 14, y: 14, z: z}, 255); - // } - return true; } @@ -252,12 +225,6 @@ function attemptVoxelChange(pickRayDir, intersection) { doDelete = addingVoxels; } - // } else if (shiftHeld) { - // // return Entities.setAllVoxels(intersection.entityID, 255); - // } - - // Entities.setVoxelSphere(id, intersection.intersection, radius, 0) - if (doDelete) { var toErasePosition = Vec3.sum(voxelPosition, Vec3.multiply(pickRayDirInVoxelSpace, 0.1)); return Entities.setVoxel(intersection.entityID, floorVector(toErasePosition), 0); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index f220518e7e..e986866bce 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -369,6 +369,34 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o return true; } + +PolyVox::RaycastResult RenderablePolyVoxEntityItem::doRayCast(glm::vec4 originInVoxel, + glm::vec4 farInVoxel, + glm::vec4& result) const { + PolyVox::Vector3DFloat startPoint(originInVoxel.x, originInVoxel.y, originInVoxel.z); + PolyVox::Vector3DFloat endPoint(farInVoxel.x, farInVoxel.y, farInVoxel.z); + + _volDataLock.lockForRead(); + RaycastFunctor callback(_volData); + PolyVox::RaycastResult raycastResult = PolyVox::raycastWithEndpoints(_volData, startPoint, endPoint, callback); + _volDataLock.unlock(); + + // result is in voxel-space coordinates. + switch (_voxelSurfaceStyle) { + case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: + case PolyVoxEntityItem::SURFACE_CUBIC: + result = callback._result + glm::vec4(0.5f, 0.5f, 0.5f, 0.0f); + break; + case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: + case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: + result = callback._result - glm::vec4(0.5f, 0.5f, 0.5f, 0.0f); + break; + } + + return raycastResult; +} + + // virtual ShapeType RenderablePolyVoxEntityItem::getShapeType() const { return SHAPE_TYPE_COMPOUND; @@ -683,24 +711,6 @@ bool RenderablePolyVoxEntityItem::updateOnCount(int x, int y, int z, uint8_t toV } -PolyVox::RaycastResult RenderablePolyVoxEntityItem::doRayCast(glm::vec4 originInVoxel, - glm::vec4 farInVoxel, - glm::vec4& result) const { - PolyVox::Vector3DFloat startPoint(originInVoxel.x, originInVoxel.y, originInVoxel.z); - PolyVox::Vector3DFloat endPoint(farInVoxel.x, farInVoxel.y, farInVoxel.z); - - _volDataLock.lockForRead(); - RaycastFunctor callback(_volData); - PolyVox::RaycastResult raycastResult = PolyVox::raycastWithEndpoints(_volData, startPoint, endPoint, callback); - _volDataLock.unlock(); - - // result is in voxel-space coordinates. - result = callback._result - glm::vec4(0.5f, 0.5f, 0.5f, 0.0f); - return raycastResult; -} - - - void RenderablePolyVoxEntityItem::decompressVolumeData() { _threadRunning.acquire(); QtConcurrent::run(this, &RenderablePolyVoxEntityItem::decompressVolumeDataAsync); From b09de4ff653a7f50d25a1f4f25e59d89265ab851 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 26 Aug 2015 21:51:28 -0700 Subject: [PATCH 15/22] fix worldCoordsToVoxelCoords for non-edged voxels. voxel.js now adds/delete where you click rather than near to there. --- examples/voxels.js | 1 - .../src/RenderablePolyVoxEntityItem.cpp | 30 +++++++++++-------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/examples/voxels.js b/examples/voxels.js index 4c344a7eee..d9049e2b0c 100644 --- a/examples/voxels.js +++ b/examples/voxels.js @@ -213,7 +213,6 @@ function attemptVoxelChange(pickRayDir, intersection) { } var voxelPosition = Entities.worldCoordsToVoxelCoords(intersection.entityID, intersection.intersection); - voxelPosition = Vec3.subtract(voxelPosition, {x: 0.5, y: 0.5, z: 0.5}); var pickRayDirInVoxelSpace = Entities.localCoordsToVoxelCoords(intersection.entityID, pickRayDir); pickRayDirInVoxelSpace = Vec3.normalize(pickRayDirInVoxelSpace); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index e986866bce..9f3d4faec5 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -382,17 +382,7 @@ PolyVox::RaycastResult RenderablePolyVoxEntityItem::doRayCast(glm::vec4 originIn _volDataLock.unlock(); // result is in voxel-space coordinates. - switch (_voxelSurfaceStyle) { - case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: - case PolyVoxEntityItem::SURFACE_CUBIC: - result = callback._result + glm::vec4(0.5f, 0.5f, 0.5f, 0.0f); - break; - case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: - case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: - result = callback._result - glm::vec4(0.5f, 0.5f, 0.5f, 0.0f); - break; - } - + result = callback._result - glm::vec4(0.5f, 0.5f, 0.5f, 0.0f); return raycastResult; } @@ -559,7 +549,19 @@ glm::vec3 RenderablePolyVoxEntityItem::voxelCoordsToWorldCoords(glm::vec3& voxel } glm::vec3 RenderablePolyVoxEntityItem::worldCoordsToVoxelCoords(glm::vec3& worldCoords) const { - return glm::vec3(worldToVoxelMatrix() * glm::vec4(worldCoords, 1.0f)); + glm::vec3 result = glm::vec3(worldToVoxelMatrix() * glm::vec4(worldCoords, 1.0f)); + switch (_voxelSurfaceStyle) { + case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: + case PolyVoxEntityItem::SURFACE_CUBIC: + result += glm::vec3(0.5f, 0.5f, 0.5f); + break; + case PolyVoxEntityItem::SURFACE_EDGED_CUBIC: + case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES: + result -= glm::vec3(0.5f, 0.5f, 0.5f); + break; + } + + return result; } glm::vec3 RenderablePolyVoxEntityItem::voxelCoordsToLocalCoords(glm::vec3& voxelCoords) const { @@ -754,6 +756,10 @@ void RenderablePolyVoxEntityItem::decompressVolumeDataAsync() { } _volDataLock.lockForWrite(); + if (!_volData) { + _volDataLock.unlock(); + return; + } _volDataDirty = true; for (int z = 0; z < voxelZSize; z++) { for (int y = 0; y < voxelYSize; y++) { From 0bcd6b8ec505d2438c789472463e03c849edaeaf Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 28 Aug 2015 10:30:56 -0700 Subject: [PATCH 16/22] remove no-longer-used #define --- libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 9f3d4faec5..199b7b90aa 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -45,8 +45,6 @@ #include "EntityEditPacketSender.h" #include "PhysicalEntitySimulation.h" -#define THREAD_POLYVOX 1 - gpu::PipelinePointer RenderablePolyVoxEntityItem::_pipeline = nullptr; const float MARCHING_CUBE_COLLISION_HULL_OFFSET = 0.5; From 92b5fe34577ad4729f69cd462ba935269d94c71c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 28 Aug 2015 10:31:28 -0700 Subject: [PATCH 17/22] fix thinko -- if the changes didn't happen, put them back on the list and don't clear their dirty flags --- libraries/physics/src/PhysicsEngine.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index e8e6f68c6b..040055b313 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -195,11 +195,13 @@ VectorOfMotionStates PhysicsEngine::changeObjects(VectorOfMotionStates& objects) if (flags & HARD_DIRTY_PHYSICS_FLAGS) { if (object->handleHardAndEasyChanges(flags, this)) { object->clearIncomingDirtyFlags(); + } else { stillNeedChange.push_back(object); } } else if (flags & EASY_DIRTY_PHYSICS_FLAGS) { if (object->handleEasyChanges(flags, this)) { object->clearIncomingDirtyFlags(); + } else { stillNeedChange.push_back(object); } } From 1ac5c19f44741785a40a173b5eaa7ad42fb888ec Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 28 Aug 2015 10:39:23 -0700 Subject: [PATCH 18/22] if someone else changes a polyvox, we need to localy recompute the collision shape --- libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 199b7b90aa..f03c80a36d 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -79,6 +79,7 @@ void RenderablePolyVoxEntityItem::setVoxelData(QByteArray voxelData) { _voxelDataDirty = true; _voxelDataLock.unlock(); decompressVolumeData(); + computeShapeInfoWorker(); } From a5cd3ff04603e0c6ee5653d695c046764f0fba0f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 28 Aug 2015 11:04:56 -0700 Subject: [PATCH 19/22] release _threadRunning if decompressVolumeDataAsync exits early --- libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index f03c80a36d..258b4a600e 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -757,6 +757,7 @@ void RenderablePolyVoxEntityItem::decompressVolumeDataAsync() { _volDataLock.lockForWrite(); if (!_volData) { _volDataLock.unlock(); + _threadRunning.release(); return; } _volDataDirty = true; From 540af4afcbdcbc24000a19e642da0e60e35d27b5 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 28 Aug 2015 12:47:04 -0700 Subject: [PATCH 20/22] remove unneeded call to computeShapeInfoWorker --- libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 258b4a600e..46c4986fa8 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -79,7 +79,6 @@ void RenderablePolyVoxEntityItem::setVoxelData(QByteArray voxelData) { _voxelDataDirty = true; _voxelDataLock.unlock(); decompressVolumeData(); - computeShapeInfoWorker(); } From 7614cababb950c87199a67762f7fd933733604e8 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 28 Aug 2015 13:21:34 -0700 Subject: [PATCH 21/22] adjust whitespace --- libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 33a5e86398..110e8f8ab4 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -43,7 +43,7 @@ namespace render { class RenderablePolyVoxEntityItem : public PolyVoxEntityItem { - public: +public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); RenderablePolyVoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); From f6fbf4f3242aac0253c8960cc71c2f5f712476c3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 28 Aug 2015 14:08:30 -0700 Subject: [PATCH 22/22] fix whitespace --- libraries/entities/src/PolyVoxEntityItem.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index d65e05464b..e765afd430 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -186,8 +186,7 @@ void PolyVoxEntityItem::debugDump() const { qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); } -void PolyVoxEntityItem::setVoxelData(QByteArray voxelData) -{ +void PolyVoxEntityItem::setVoxelData(QByteArray voxelData) { _voxelDataLock.lockForWrite(); _voxelData = voxelData; _voxelDataDirty = true;