From 88c850afa233882d2cb68effd43822502707e755 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 26 Feb 2017 12:56:17 -0800 Subject: [PATCH] voxel-paint paints with capsules rather than spheres --- .../src/RenderablePolyVoxEntityItem.cpp | 53 +++++++++++++++++++ .../src/RenderablePolyVoxEntityItem.h | 2 + .../entities/src/EntityScriptingInterface.cpp | 18 +++++++ .../entities/src/EntityScriptingInterface.h | 6 +++ libraries/entities/src/PolyVoxEntityItem.h | 2 + libraries/shared/src/GeometryUtil.cpp | 27 ++++++++++ libraries/shared/src/GeometryUtil.h | 3 ++ 7 files changed, 111 insertions(+) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index d3023c573a..a5b4896d45 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -437,6 +437,59 @@ bool RenderablePolyVoxEntityItem::setSphere(glm::vec3 centerWorldCoords, float r return result; } +bool RenderablePolyVoxEntityItem::setCapsule(glm::vec3 startWorldCoords, glm::vec3 endWorldCoords, + float radiusWorldCoords, uint8_t toValue) { + bool result = false; + if (_locked) { + return result; + } + + glm::mat4 vtwMatrix = voxelToWorldMatrix(); + glm::mat4 wtvMatrix = glm::inverse(vtwMatrix); + + glm::vec3 dimensions = getDimensions(); + glm::vec3 voxelSize = dimensions / _voxelVolumeSize; + float smallestDimensionSize = voxelSize.x; + smallestDimensionSize = glm::min(smallestDimensionSize, voxelSize.y); + smallestDimensionSize = glm::min(smallestDimensionSize, voxelSize.z); + + glm::vec3 maxRadiusInVoxelCoords = glm::vec3(radiusWorldCoords / smallestDimensionSize); + + glm::vec3 startInVoxelCoords = wtvMatrix * glm::vec4(startWorldCoords, 1.0f); + glm::vec3 endInVoxelCoords = wtvMatrix * glm::vec4(endWorldCoords, 1.0f); + + glm::vec3 low = glm::min(glm::floor(startInVoxelCoords - maxRadiusInVoxelCoords), + glm::floor(endInVoxelCoords - maxRadiusInVoxelCoords)); + glm::vec3 high = glm::max(glm::ceil(startInVoxelCoords + maxRadiusInVoxelCoords), + glm::ceil(endInVoxelCoords + maxRadiusInVoxelCoords)); + + glm::ivec3 lowI = glm::clamp(low, glm::vec3(0.0f), _voxelVolumeSize); + glm::ivec3 highI = glm::clamp(high, glm::vec3(0.0f), _voxelVolumeSize); + + // This three-level for loop iterates over every voxel in the volume that might be in the capsule + withWriteLock([&] { + for (int z = lowI.z; z < highI.z; z++) { + for (int y = lowI.y; y < highI.y; y++) { + for (int x = lowI.x; x < highI.x; x++) { + // Store our current position as a vector... + glm::vec4 pos(x + 0.5f, y + 0.5f, z + 0.5f, 1.0); // consider voxels cenetered on their coordinates + // convert to world coordinates + glm::vec3 worldPos = glm::vec3(vtwMatrix * pos); + if (pointInCapsule(worldPos, startWorldCoords, endWorldCoords, radiusWorldCoords)) { + result |= setVoxelInternal(x, y, z, toValue); + } + } + } + } + }); + + if (result) { + compressVolumeDataAndSendEditPacket(); + } + return result; +} + + class RaycastFunctor { public: diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 278f658a46..45842c2fb9 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -94,6 +94,8 @@ public: // coords are in world-space virtual bool setSphere(glm::vec3 center, float radius, uint8_t toValue) override; + virtual bool setCapsule(glm::vec3 startWorldCoords, glm::vec3 endWorldCoords, + float radiusWorldCoords, uint8_t toValue) override; virtual bool setAll(uint8_t toValue) override; virtual bool setCuboid(const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int toValue) override; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index fa1b53e324..eb634568f0 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -891,6 +891,16 @@ bool EntityScriptingInterface::setVoxelSphere(QUuid entityID, const glm::vec3& c }); } +bool EntityScriptingInterface::setVoxelCapsule(QUuid entityID, + const glm::vec3& start, const glm::vec3& end, + float radius, int value) { + PROFILE_RANGE(script_entities, __FUNCTION__); + + return setVoxels(entityID, [start, end, radius, value](PolyVoxEntityItem& polyVoxEntity) { + return polyVoxEntity.setCapsule(start, end, radius, value); + }); +} + bool EntityScriptingInterface::setVoxel(QUuid entityID, const glm::vec3& position, int value) { PROFILE_RANGE(script_entities, __FUNCTION__); @@ -1524,3 +1534,11 @@ QObject* EntityScriptingInterface::getWebViewRoot(const QUuid& entityID) { return nullptr; } } + +// TODO move this someplace that makes more sense... +bool EntityScriptingInterface::AABoxIntersectsCapsule(const glm::vec3& low, const glm::vec3& dimensions, + const glm::vec3& start, const glm::vec3& end, float radius) { + glm::vec3 penetration; + AABox aaBox(low, dimensions); + return aaBox.findCapsulePenetration(start, end, radius, penetration); +} diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 0353fa08a8..e9f0637830 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -223,6 +223,8 @@ public slots: Q_INVOKABLE bool getDrawZoneBoundaries() const; Q_INVOKABLE bool setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value); + Q_INVOKABLE bool setVoxelCapsule(QUuid entityID, const glm::vec3& start, const glm::vec3& end, float radius, int value); + Q_INVOKABLE bool setVoxel(QUuid entityID, const glm::vec3& position, int value); Q_INVOKABLE bool setAllVoxels(QUuid entityID, int value); Q_INVOKABLE bool setVoxelsInCuboid(QUuid entityID, const glm::vec3& lowPosition, @@ -287,6 +289,10 @@ public slots: Q_INVOKABLE QObject* getWebViewRoot(const QUuid& entityID); + Q_INVOKABLE bool AABoxIntersectsCapsule(const glm::vec3& low, const glm::vec3& dimensions, + const glm::vec3& start, const glm::vec3& end, float radius); + + signals: void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index 4f478c8bf7..910d8eff88 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -86,6 +86,8 @@ class PolyVoxEntityItem : public EntityItem { // coords are in world-space virtual bool setSphere(glm::vec3 center, float radius, uint8_t toValue) { return false; } + virtual bool setCapsule(glm::vec3 startWorldCoords, glm::vec3 endWorldCoords, + float radiusWorldCoords, uint8_t toValue) { return false; } virtual bool setAll(uint8_t toValue) { return false; } virtual bool setCuboid(const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int value) { return false; } diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index 92fe138021..c137ebd438 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -205,6 +205,33 @@ bool findRaySphereIntersection(const glm::vec3& origin, const glm::vec3& directi return true; } +bool pointInSphere(const glm::vec3& origin, const glm::vec3& center, float radius) { + glm::vec3 relativeOrigin = origin - center; + float c = glm::dot(relativeOrigin, relativeOrigin) - radius * radius; + return c <= 0.0f; +} + + +bool pointInCapsule(const glm::vec3& origin, const glm::vec3& start, const glm::vec3& end, float radius) { + glm::vec3 relativeOrigin = origin - start; + glm::vec3 relativeEnd = end - start; + float capsuleLength = glm::length(relativeEnd); + relativeEnd /= capsuleLength; + float originProjection = glm::dot(relativeEnd, relativeOrigin); + glm::vec3 constant = relativeOrigin - relativeEnd * originProjection; + float c = glm::dot(constant, constant) - radius * radius; + if (c < 0.0f) { // starts inside cylinder + if (originProjection < 0.0f) { // below start + return pointInSphere(origin, start, radius); + } else if (originProjection > capsuleLength) { // above end + return pointInSphere(origin, end, radius); + } else { // between start and end + return true; + } + } + return false; +} + bool findRayCapsuleIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& start, const glm::vec3& end, float radius, float& distance) { if (start == end) { diff --git a/libraries/shared/src/GeometryUtil.h b/libraries/shared/src/GeometryUtil.h index 1c951ca09a..2fdc1aa25f 100644 --- a/libraries/shared/src/GeometryUtil.h +++ b/libraries/shared/src/GeometryUtil.h @@ -73,6 +73,9 @@ glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3& bool findRaySphereIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& center, float radius, float& distance); +bool pointInSphere(const glm::vec3& origin, const glm::vec3& center, float radius); +bool pointInCapsule(const glm::vec3& origin, const glm::vec3& start, const glm::vec3& end, float radius); + bool findRayCapsuleIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& start, const glm::vec3& end, float radius, float& distance);