diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6e7a405181..87d4db9936 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -120,6 +120,7 @@ #include #include #include +#include #include #include #include @@ -4228,6 +4229,10 @@ void Application::init() { // fire off an immediate domain-server check in now that settings are loaded DependencyManager::get()->sendDomainServerCheckIn(); + // This allows collision to be set up properly for shape entities supported by GeometryCache. + // This is before entity setup to ensure that it's ready for whenever instance collision is initialized. + ShapeEntityItem::setShapeInfoCalulator(ShapeEntityItem::ShapeInfoCalculator(&shapeInfoCalculator)); + getEntities()->init(); getEntities()->setEntityLoadingPriorityFunction([this](const EntityItem& item) { auto dims = item.getDimensions(); diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 7822b78244..aad7768b99 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include "InterfaceLogging.h" #include "world.h" @@ -393,4 +395,20 @@ void runUnitTests() { } } +void shapeInfoCalculator(const ShapeEntityItem * const shapeEntity, ShapeInfo &shapeInfo) { + + if (shapeEntity == nullptr) { + + //--EARLY EXIT-- + return; + } + + ShapeInfo::PointCollection pointCollection; + ShapeInfo::PointList points; + pointCollection.push_back(points); + + GeometryCache::computeSimpleHullPointListForShape((int)shapeEntity->getShape(), shapeEntity->getDimensions(), pointCollection.back()); + shapeInfo.setPointCollection(pointCollection); +} + diff --git a/interface/src/Util.h b/interface/src/Util.h index 48acb53936..976a26ce82 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -18,6 +18,9 @@ #include #include +class ShapeEntityItem; +class ShapeInfo; + void renderWorldBox(RenderArgs* args, gpu::Batch& batch); void runTimingTests(); @@ -28,4 +31,6 @@ bool rayIntersectsSphere(const glm::vec3& rayStarting, const glm::vec3& rayNorma bool pointInSphere(glm::vec3& point, glm::vec3& sphereCenter, double sphereRadius); +void shapeInfoCalculator(const ShapeEntityItem * const shapeEntity, ShapeInfo &shapeInfo); + #endif // hifi_Util_h diff --git a/libraries/baking/src/JSBaker.cpp b/libraries/baking/src/JSBaker.cpp index 75811bea49..a97a7fe5b3 100644 --- a/libraries/baking/src/JSBaker.cpp +++ b/libraries/baking/src/JSBaker.cpp @@ -72,7 +72,7 @@ void JSBaker::bake() { bool JSBaker::bakeJS(const QByteArray& inputFile, QByteArray& outputFile) { // Read from inputFile and write to outputFile per character QTextStream in(inputFile, QIODevice::ReadOnly); - QTextStream out(outputFile, QIODevice::WriteOnly); + QTextStream out(&outputFile, QIODevice::WriteOnly); // Algorithm requires the knowledge of previous and next character for each character read QChar currentCharacter; diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp index 2f57cc29d0..7b639e8308 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp @@ -234,7 +234,8 @@ void CompositorHelper::handleLeaveEvent() { bool CompositorHelper::handleRealMouseMoveEvent(bool sendFakeEvent) { // If the mouse move came from a capture mouse related move, we completely ignore it. - if (_ignoreMouseMove) { + // Note: if not going to synthesize event - do not touch _ignoreMouseMove flag + if (_ignoreMouseMove && sendFakeEvent) { _ignoreMouseMove = false; return true; // swallow the event } @@ -246,7 +247,12 @@ bool CompositorHelper::handleRealMouseMoveEvent(bool sendFakeEvent) { auto changeInRealMouse = newPosition - _lastKnownRealMouse; auto newReticlePosition = _reticlePositionInHMD + toGlm(changeInRealMouse); setReticlePosition(newReticlePosition, sendFakeEvent); - _ignoreMouseMove = true; + + // Note: if not going to synthesize event - do not touch _ignoreMouseMove flag + if (sendFakeEvent) { + _ignoreMouseMove = true; + } + QCursor::setPos(QPoint(_lastKnownRealMouse.x(), _lastKnownRealMouse.y())); // move cursor back to where it was return true; // swallow the event } else { diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 7d7de0c08f..332d87f930 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -30,23 +30,6 @@ using namespace render::entities; // is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down. static const float SPHERE_ENTITY_SCALE = 0.5f; -static std::array MAPPING { { - GeometryCache::Triangle, - GeometryCache::Quad, - GeometryCache::Hexagon, - GeometryCache::Octagon, - GeometryCache::Circle, - GeometryCache::Cube, - GeometryCache::Sphere, - GeometryCache::Tetrahedron, - GeometryCache::Octahedron, - GeometryCache::Dodecahedron, - GeometryCache::Icosahedron, - GeometryCache::Torus, - GeometryCache::Cone, - GeometryCache::Cylinder, -} }; - ShapeEntityRenderer::ShapeEntityRenderer(const EntityItemPointer& entity) : Parent(entity) { _procedural._vertexSource = simple_vert; @@ -137,11 +120,12 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { gpu::Batch& batch = *args->_batch; + auto geometryCache = DependencyManager::get(); GeometryCache::Shape geometryShape; bool proceduralRender = false; glm::vec4 outColor; withReadLock([&] { - geometryShape = MAPPING[_shape]; + geometryShape = geometryCache->getShapeForEntityShape(_shape); batch.setModelTransform(_renderTransform); // use a transform with scale, rotation, registration point and translation outColor = _color; if (_procedural.isReady()) { @@ -155,14 +139,13 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { if (proceduralRender) { batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a); if (render::ShapeKey(args->_globalShapeKey).isWireframe()) { - DependencyManager::get()->renderWireShape(batch, geometryShape); + geometryCache->renderWireShape(batch, geometryShape); } else { - DependencyManager::get()->renderShape(batch, geometryShape); + geometryCache->renderShape(batch, geometryShape); } } else { // FIXME, support instanced multi-shape rendering using multidraw indirect outColor.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; - auto geometryCache = DependencyManager::get(); auto pipeline = outColor.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline(); if (render::ShapeKey(args->_globalShapeKey).isWireframe()) { geometryCache->renderWireShapeInstance(args, batch, geometryShape, outColor, pipeline); @@ -171,6 +154,6 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { } } - static const auto triCount = DependencyManager::get()->getShapeTriangleCount(geometryShape); + const auto triCount = geometryCache->getShapeTriangleCount(geometryShape); args->_details._trianglesRendered += (int)triCount; } diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 51ed66bb23..3bbd6ce8e6 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -84,28 +84,11 @@ void EntityItemProperties::setLastEdited(quint64 usecTime) { _lastEdited = usecTime > _created ? usecTime : _created; } -const char* shapeTypeNames[] = { - "none", - "box", - "sphere", - "capsule-x", - "capsule-y", - "capsule-z", - "cylinder-x", - "cylinder-y", - "cylinder-z", - "hull", - "plane", - "compound", - "simple-hull", - "simple-compound", - "static-mesh" -}; QHash stringToShapeTypeLookup; void addShapeType(ShapeType type) { - stringToShapeTypeLookup[shapeTypeNames[type]] = type; + stringToShapeTypeLookup[ShapeInfo::getNameForShapeType(type)] = type; } void buildStringToShapeTypeLookup() { @@ -180,9 +163,7 @@ void EntityItemProperties::setCollisionMaskFromString(const QString& maskString) } QString EntityItemProperties::getShapeTypeAsString() const { - if (_shapeType < sizeof(shapeTypeNames) / sizeof(char *)) - return QString(shapeTypeNames[_shapeType]); - return QString(shapeTypeNames[SHAPE_TYPE_NONE]); + return ShapeInfo::getNameForShapeType(_shapeType); } void EntityItemProperties::setShapeTypeFromString(const QString& shapeName) { diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index 6e3bdc27a4..9a1a500a54 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -51,6 +51,14 @@ namespace entity { } } +// shapeCalculator is a hook for external code that knows how to configure a ShapeInfo +// for given entity::Shape and dimensions +ShapeEntityItem::ShapeInfoCalculator shapeCalculator = nullptr; + +void ShapeEntityItem::setShapeInfoCalulator(ShapeEntityItem::ShapeInfoCalculator callback) { + shapeCalculator = callback; +} + ShapeEntityItem::Pointer ShapeEntityItem::baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { Pointer entity(new ShapeEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); @@ -87,6 +95,7 @@ EntityItemProperties ShapeEntityItem::getProperties(EntityPropertyFlags desiredP } void ShapeEntityItem::setShape(const entity::Shape& shape) { + const entity::Shape prevShape = _shape; _shape = shape; switch (_shape) { case entity::Shape::Cube: @@ -99,6 +108,11 @@ void ShapeEntityItem::setShape(const entity::Shape& shape) { _type = EntityTypes::Shape; break; } + + if (_shape != prevShape) { + // Internally grabs writeLock + markDirtyFlags(Simulation::DIRTY_SHAPE); + } } bool ShapeEntityItem::setProperties(const EntityItemProperties& properties) { @@ -219,6 +233,7 @@ void ShapeEntityItem::debugDump() const { qCDebug(entities) << "SHAPE EntityItem id:" << getEntityItemID() << "---------------------------------------------"; qCDebug(entities) << " name:" << _name; qCDebug(entities) << " shape:" << stringFromShape(_shape) << " (EnumId: " << _shape << " )"; + qCDebug(entities) << " collisionShapeType:" << ShapeInfo::getNameForShapeType(getShapeType()); qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2]; qCDebug(entities) << " position:" << debugTreeVector(getPosition()); qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions()); @@ -233,73 +248,101 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) { const glm::vec3 entityDimensions = getDimensions(); - switch (_shape) { - case entity::Shape::Quad: - case entity::Shape::Cube: - { - _collisionShapeType = SHAPE_TYPE_BOX; + switch (_shape){ + case entity::Shape::Quad: { + // Not in GeometryCache::buildShapes, unsupported. + _collisionShapeType = SHAPE_TYPE_ELLIPSOID; + //TODO WL21389: Add a SHAPE_TYPE_QUAD ShapeType and treat + // as a special box (later if desired support) + } + break; + case entity::Shape::Cube: { + _collisionShapeType = SHAPE_TYPE_BOX; + } + break; + case entity::Shape::Sphere: { + + float diameter = entityDimensions.x; + const float MIN_DIAMETER = 0.001f; + const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; + if (diameter > MIN_DIAMETER + && fabsf(diameter - entityDimensions.y) / diameter < MIN_RELATIVE_SPHERICAL_ERROR + && fabsf(diameter - entityDimensions.z) / diameter < MIN_RELATIVE_SPHERICAL_ERROR) { + + _collisionShapeType = SHAPE_TYPE_SPHERE; + } else { + _collisionShapeType = SHAPE_TYPE_ELLIPSOID; } - break; - case entity::Shape::Sphere: - { + } + break; + case entity::Shape::Circle: { + _collisionShapeType = SHAPE_TYPE_CIRCLE; + } + break; + case entity::Shape::Cylinder: { + _collisionShapeType = SHAPE_TYPE_CYLINDER_Y; + // TODO WL21389: determine if rotation is axis-aligned + //const Transform::Quat & rot = _transform.getRotation(); - float diameter = entityDimensions.x; - const float MIN_DIAMETER = 0.001f; - const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; - if (diameter > MIN_DIAMETER - && fabsf(diameter - entityDimensions.y) / diameter < MIN_RELATIVE_SPHERICAL_ERROR - && fabsf(diameter - entityDimensions.z) / diameter < MIN_RELATIVE_SPHERICAL_ERROR) { + // TODO WL21389: some way to tell apart SHAPE_TYPE_CYLINDER_Y, _X, _Z based on rotation and + // hull ( or dimensions, need circular cross section) + // Should allow for minor variance along axes? - _collisionShapeType = SHAPE_TYPE_SPHERE; - } else { - _collisionShapeType = SHAPE_TYPE_ELLIPSOID; - } + } + break; + case entity::Shape::Cone: { + if (shapeCalculator) { + shapeCalculator(this, info); + // shapeCalculator only supports convex shapes (e.g. SHAPE_TYPE_HULL) + _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; + } else { + _collisionShapeType = SHAPE_TYPE_ELLIPSOID; } - break; - case entity::Shape::Cylinder: - { - _collisionShapeType = SHAPE_TYPE_CYLINDER_Y; - // TODO WL21389: determine if rotation is axis-aligned - //const Transform::Quat & rot = _transform.getRotation(); - - // TODO WL21389: some way to tell apart SHAPE_TYPE_CYLINDER_Y, _X, _Z based on rotation and - // hull ( or dimensions, need circular cross section) - // Should allow for minor variance along axes? - - } - break; + } + break; + // gons, ones, & angles built via GeometryCache::extrudePolygon case entity::Shape::Triangle: case entity::Shape::Hexagon: - case entity::Shape::Octagon: - case entity::Shape::Circle: + case entity::Shape::Octagon: { + if (shapeCalculator) { + shapeCalculator(this, info); + // shapeCalculator only supports convex shapes (e.g. SHAPE_TYPE_HULL) + _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; + } else { + _collisionShapeType = SHAPE_TYPE_ELLIPSOID; + } + } + break; + // hedrons built via GeometryCache::setUpFlatShapes case entity::Shape::Tetrahedron: case entity::Shape::Octahedron: case entity::Shape::Dodecahedron: - case entity::Shape::Icosahedron: - case entity::Shape::Cone: - { - //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later) + case entity::Shape::Icosahedron: { + if ( shapeCalculator ) { + shapeCalculator(this, info); + // shapeCalculator only supports convex shapes (e.g. SHAPE_TYPE_HULL) + _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; + } else { _collisionShapeType = SHAPE_TYPE_ELLIPSOID; } - break; - case entity::Shape::Torus: - { - // Not in GeometryCache::buildShapes, unsupported. - _collisionShapeType = SHAPE_TYPE_ELLIPSOID; - //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later if desired support) - } - break; - default: - { - _collisionShapeType = SHAPE_TYPE_ELLIPSOID; - } - break; + } + break; + case entity::Shape::Torus: { + // Not in GeometryCache::buildShapes, unsupported. + _collisionShapeType = SHAPE_TYPE_ELLIPSOID; + //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later if desired support) + } + break; + default: { + _collisionShapeType = SHAPE_TYPE_ELLIPSOID; + } + break; } EntityItem::computeShapeInfo(info); } -// This value specifes how the shape should be treated by physics calculations. +// This value specifies how the shape should be treated by physics calculations. ShapeType ShapeEntityItem::getShapeType() const { return _collisionShapeType; } diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index a20cecb60b..a88a2098e9 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -34,7 +34,6 @@ namespace entity { ::QString stringFromShape(Shape shape); } - class ShapeEntityItem : public EntityItem { using Pointer = std::shared_ptr; static Pointer baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties); @@ -43,6 +42,9 @@ public: static EntityItemPointer sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties); static EntityItemPointer boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties); + using ShapeInfoCalculator = std::function; + static void setShapeInfoCalulator(ShapeInfoCalculator callback); + ShapeEntityItem(const EntityItemID& entityItemID); void pureVirtualFunctionPlaceHolder() override { }; diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 81bfbc72b4..0b91ede574 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -66,7 +66,7 @@ class PhysicsEngine; class ObjectMotionState : public btMotionState { public: - // These poroperties of the PhysicsEngine are "global" within the context of all ObjectMotionStates + // These properties of the PhysicsEngine are "global" within the context of all ObjectMotionStates // (assuming just one PhysicsEngine). They are cached as statics for fast calculations in the // ObjectMotionState context. static void setWorldOffset(const glm::vec3& offset); diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 18dfd2317e..cd0fba848a 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -314,6 +314,7 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape = new btCylinderShapeZ(btHalfExtents); } break; + case SHAPE_TYPE_CIRCLE: case SHAPE_TYPE_CYLINDER_Y: { const glm::vec3 halfExtents = info.getHalfExtents(); const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index cf8e268681..f35fb9f830 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -52,6 +52,46 @@ //#define WANT_DEBUG +// @note: Originally size entity::NUM_SHAPES +// As of Commit b93e91b9, render-utils no longer retains knowledge of +// entity lib, and thus doesn't know about entity::NUM_SHAPES. Should +// the enumerations be altered, this will need to be updated. +// @see ShapeEntityItem.h +static std::array MAPPING{ { + GeometryCache::Triangle, + GeometryCache::Quad, + GeometryCache::Hexagon, + GeometryCache::Octagon, + GeometryCache::Circle, + GeometryCache::Cube, + GeometryCache::Sphere, + GeometryCache::Tetrahedron, + GeometryCache::Octahedron, + GeometryCache::Dodecahedron, + GeometryCache::Icosahedron, + GeometryCache::Torus, + GeometryCache::Cone, + GeometryCache::Cylinder, +} }; + +static const std::array GEOCACHE_SHAPE_STRINGS{ { + "Line", + "Triangle", + "Quad", + "Hexagon", + "Octagon", + "Circle", + "Cube", + "Sphere", + "Tetrahedron", + "Octahedron", + "Dodecahedron", + "Icosahedron", + "Torus", + "Cone", + "Cylinder" + } }; + const int GeometryCache::UNKNOWN_ID = -1; @@ -69,6 +109,51 @@ static gpu::Stream::FormatPointer INSTANCED_SOLID_FADE_STREAM_FORMAT; static const uint SHAPE_VERTEX_STRIDE = sizeof(glm::vec3) * 2; // vertices and normals static const uint SHAPE_NORMALS_OFFSET = sizeof(glm::vec3); +void GeometryCache::computeSimpleHullPointListForShape(const int entityShape, const glm::vec3 &entityExtents, QVector &outPointList) { + + auto geometryCache = DependencyManager::get(); + const GeometryCache::Shape geometryShape = GeometryCache::getShapeForEntityShape( entityShape ); + const GeometryCache::ShapeData * shapeData = geometryCache->getShapeData( geometryShape ); + if (!shapeData){ + //--EARLY EXIT--( data isn't ready for some reason... ) + return; + } + + const gpu::BufferView & shapeVerts = shapeData->_positionView; + const gpu::BufferView::Size numItems = shapeVerts.getNumElements(); + + outPointList.reserve((int)numItems); + QVector uniqueVerts; + uniqueVerts.reserve((int)numItems); + + const float MAX_INCLUSIVE_FILTER_DISTANCE_SQUARED = 1.0e-6f; //< 1mm^2 + for (gpu::BufferView::Index i = 0; i < (gpu::BufferView::Index)numItems; ++i) { + const int numUniquePoints = (int)uniqueVerts.size(); + const geometry::Vec &curVert = shapeVerts.get(i); + bool isUniquePoint = true; + + for (int uniqueIndex = 0; uniqueIndex < numUniquePoints; ++uniqueIndex) { + const geometry::Vec knownVert = uniqueVerts[uniqueIndex]; + const float distToKnownPoint = glm::length2(knownVert - curVert); + + if (distToKnownPoint <= MAX_INCLUSIVE_FILTER_DISTANCE_SQUARED) { + isUniquePoint = false; + break; + } + } + + if (!isUniquePoint) { + + //--EARLY ITERATION EXIT-- + continue; + } + + + uniqueVerts.push_back(curVert); + outPointList.push_back(curVert * entityExtents); + } +} + template std::vector polygon() { std::vector result; @@ -85,7 +170,7 @@ void GeometryCache::ShapeData::setupVertices(gpu::BufferPointer& vertexBuffer, c gpu::Buffer::Size offset = vertexBuffer->getSize(); vertexBuffer->append(vertices); - gpu::Buffer::Size viewSize = vertices.size() * 2 * sizeof(glm::vec3); + gpu::Buffer::Size viewSize = vertices.size() * sizeof(glm::vec3); _positionView = gpu::BufferView(vertexBuffer, offset, viewSize, SHAPE_VERTEX_STRIDE, POSITION_ELEMENT); @@ -441,12 +526,46 @@ void GeometryCache::buildShapes() { extrudePolygon<64>(_shapes[Cone], _shapeVertices, _shapeIndices, true); //Circle drawCircle(_shapes[Circle], _shapeVertices, _shapeIndices); - // Not implememented yet: + // Not implemented yet: //Quad, //Torus, } +const GeometryCache::ShapeData * GeometryCache::getShapeData(const Shape shape) const { + if (((int)shape < 0) || ((int)shape >= (int)_shapes.size())) { + qCWarning(renderutils) << "GeometryCache::getShapeData - Invalid shape " << shape << " specified. Returning default fallback."; + + //--EARLY EXIT--( No valid shape data for shape ) + return nullptr; + } + + return &_shapes[shape]; +} + +GeometryCache::Shape GeometryCache::getShapeForEntityShape(int entityShape) { + if ((entityShape < 0) || (entityShape >= (int)MAPPING.size())) { + qCWarning(renderutils) << "GeometryCache::getShapeForEntityShape - Invalid shape " << entityShape << " specified. Returning default fallback."; + + //--EARLY EXIT--( fall back to default assumption ) + return GeometryCache::Sphere; + } + + return MAPPING[entityShape]; +} + +QString GeometryCache::stringFromShape(GeometryCache::Shape geoShape) +{ + if (((int)geoShape < 0) || ((int)geoShape >= (int)GeometryCache::NUM_SHAPES)) { + qCWarning(renderutils) << "GeometryCache::stringFromShape - Invalid shape " << geoShape << " specified."; + + //--EARLY EXIT-- + return "INVALID_GEOCACHE_SHAPE"; + } + + return GEOCACHE_SHAPE_STRINGS[geoShape]; +} + gpu::Stream::FormatPointer& getSolidStreamFormat() { if (!SOLID_STREAM_FORMAT) { SOLID_STREAM_FORMAT = std::make_shared(); // 1 for everyone diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 5a437cf5e9..cd8c43f1df 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -147,6 +147,16 @@ public: NUM_SHAPES, }; + /// @param entityShapeEnum: The entity::Shape enumeration for the shape + /// whose GeometryCache::Shape is desired. + /// @return GeometryCache::NUM_SHAPES in the event of an error; otherwise, + /// the GeometryCache::Shape enum which aligns with the + /// specified entityShapeEnum + static GeometryCache::Shape getShapeForEntityShape(int entityShapeEnum); + static QString stringFromShape(GeometryCache::Shape geoShape); + + static void computeSimpleHullPointListForShape(int entityShape, const glm::vec3 &entityExtents, QVector &outPointList); + static uint8_t CUSTOM_PIPELINE_NUMBER; static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key); static void registerShapePipeline() { @@ -355,15 +365,21 @@ public: using VShape = std::array; - VShape _shapes; + /// returns ShapeData associated with the specified shape, + /// otherwise nullptr in the event of an error. + const ShapeData * getShapeData(Shape shape) const; private: + GeometryCache(); virtual ~GeometryCache(); void buildShapes(); typedef QPair IntPair; typedef QPair VerticesIndices; + + + VShape _shapes; gpu::PipelinePointer _standardDrawPipeline; gpu::PipelinePointer _standardDrawPipelineNoBlend; diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index f3dd9d11a6..36ce38335a 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -15,9 +15,38 @@ #include "NumericalConstants.h" // for MILLIMETERS_PER_METER +// Originally within EntityItemProperties.cpp +const char* shapeTypeNames[] = { + "none", + "box", + "sphere", + "capsule-x", + "capsule-y", + "capsule-z", + "cylinder-x", + "cylinder-y", + "cylinder-z", + "hull", + "plane", + "compound", + "simple-hull", + "simple-compound", + "static-mesh" +}; + +static const size_t SHAPETYPE_NAME_COUNT = (sizeof(shapeTypeNames) / sizeof((shapeTypeNames)[0])); + // Bullet doesn't support arbitrarily small shapes const float MIN_HALF_EXTENT = 0.005f; // 0.5 cm +QString ShapeInfo::getNameForShapeType(ShapeType type) { + if (((int)type <= 0) || ((int)type >= (int)SHAPETYPE_NAME_COUNT)) { + type = (ShapeType)0; + } + + return shapeTypeNames[(int)type]; +} + void ShapeInfo::clear() { _url.clear(); _pointCollection.clear(); @@ -29,7 +58,6 @@ void ShapeInfo::clear() { } void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) { - //TODO WL21389: Does this need additional cases and handling added? _url = ""; _type = type; setHalfExtents(halfExtents); @@ -38,6 +66,7 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString _halfExtents = glm::vec3(0.0f); break; case SHAPE_TYPE_BOX: + case SHAPE_TYPE_HULL: break; case SHAPE_TYPE_SPHERE: { float radius = glm::length(halfExtents) / SQUARE_ROOT_OF_3; @@ -45,6 +74,10 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString _halfExtents = glm::vec3(radius); } break; + case SHAPE_TYPE_CIRCLE: { + _halfExtents = glm::vec3(_halfExtents.x, MIN_HALF_EXTENT, _halfExtents.z); + } + break; case SHAPE_TYPE_COMPOUND: case SHAPE_TYPE_SIMPLE_HULL: case SHAPE_TYPE_SIMPLE_COMPOUND: @@ -58,9 +91,6 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString } void ShapeInfo::setBox(const glm::vec3& halfExtents) { - //TODO WL21389: Should this pointlist clearance added in case - // this is a re-purposed instance? - // See https://github.com/highfidelity/hifi/pull/11024#discussion_r128885491 _url = ""; _type = SHAPE_TYPE_BOX; setHalfExtents(halfExtents); @@ -68,7 +98,6 @@ void ShapeInfo::setBox(const glm::vec3& halfExtents) { } void ShapeInfo::setSphere(float radius) { - //TODO WL21389: See comment in setBox regarding clearance _url = ""; _type = SHAPE_TYPE_SPHERE; radius = glm::max(radius, MIN_HALF_EXTENT); @@ -77,14 +106,11 @@ void ShapeInfo::setSphere(float radius) { } void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollection) { - //TODO WL21389: May need to skip resetting type here. _pointCollection = pointCollection; - _type = (_pointCollection.size() > 0) ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE; _doubleHashKey.clear(); } void ShapeInfo::setCapsuleY(float radius, float halfHeight) { - //TODO WL21389: See comment in setBox regarding clearance _url = ""; _type = SHAPE_TYPE_CAPSULE_Y; radius = glm::max(radius, MIN_HALF_EXTENT); @@ -125,8 +151,15 @@ int ShapeInfo::getLargestSubshapePointCount() const { return numPoints; } +float computeCylinderVolume(const float radius, const float height) { + return PI * radius * radius * 2.0f * height; +} + +float computeCapsuleVolume(const float radius, const float cylinderHeight) { + return PI * radius * radius * (cylinderHeight + 4.0f * radius / 3.0f); +} + float ShapeInfo::computeVolume() const { - //TODO WL21389: Add support for other ShapeTypes( CYLINDER_X, CYLINDER_Y, etc). const float DEFAULT_VOLUME = 1.0f; float volume = DEFAULT_VOLUME; switch(_type) { @@ -139,17 +172,37 @@ float ShapeInfo::computeVolume() const { volume = 4.0f * PI * _halfExtents.x * _halfExtents.y * _halfExtents.z / 3.0f; break; } + case SHAPE_TYPE_CYLINDER_X: { + volume = computeCylinderVolume(_halfExtents.y, _halfExtents.x); + break; + } case SHAPE_TYPE_CYLINDER_Y: { - float radius = _halfExtents.x; - volume = PI * radius * radius * 2.0f * _halfExtents.y; + volume = computeCylinderVolume(_halfExtents.x, _halfExtents.y); + break; + } + case SHAPE_TYPE_CYLINDER_Z: { + volume = computeCylinderVolume(_halfExtents.x, _halfExtents.z); + break; + } + case SHAPE_TYPE_CAPSULE_X: { + // Need to offset halfExtents.x by y to account for the system treating + // the x extent of the capsule as the cylindrical height + spherical radius. + const float cylinderHeight = 2.0f * (_halfExtents.x - _halfExtents.y); + volume = computeCapsuleVolume(_halfExtents.y, cylinderHeight); break; } case SHAPE_TYPE_CAPSULE_Y: { - float radius = _halfExtents.x; // Need to offset halfExtents.y by x to account for the system treating // the y extent of the capsule as the cylindrical height + spherical radius. - float cylinderHeight = 2.0f * (_halfExtents.y - _halfExtents.x); - volume = PI * radius * radius * (cylinderHeight + 4.0f * radius / 3.0f); + const float cylinderHeight = 2.0f * (_halfExtents.y - _halfExtents.x); + volume = computeCapsuleVolume(_halfExtents.x, cylinderHeight); + break; + } + case SHAPE_TYPE_CAPSULE_Z: { + // Need to offset halfExtents.z by x to account for the system treating + // the z extent of the capsule as the cylindrical height + spherical radius. + const float cylinderHeight = 2.0f * (_halfExtents.z - _halfExtents.x); + volume = computeCapsuleVolume(_halfExtents.x, cylinderHeight); break; } default: @@ -160,7 +213,6 @@ float ShapeInfo::computeVolume() const { } bool ShapeInfo::contains(const glm::vec3& point) const { - //TODO WL21389: Add support for other ShapeTypes like Ellipsoid/Compound. switch(_type) { case SHAPE_TYPE_SPHERE: return glm::length(point) <= _halfExtents.x; @@ -205,7 +257,6 @@ bool ShapeInfo::contains(const glm::vec3& point) const { } const DoubleHashKey& ShapeInfo::getHash() const { - //TODO WL21389: Need to include the pointlist for SIMPLE_HULL in hash // NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance. if (_doubleHashKey.isNull() && _type != SHAPE_TYPE_NONE) { bool useOffset = glm::length2(_offset) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET; @@ -216,41 +267,82 @@ const DoubleHashKey& ShapeInfo::getHash() const { uint32_t primeIndex = 0; _doubleHashKey.computeHash((uint32_t)_type, primeIndex++); - // compute hash1 - uint32_t hash = _doubleHashKey.getHash(); - for (int j = 0; j < 3; ++j) { - // NOTE: 0.49f is used to bump the float up almost half a millimeter - // so the cast to int produces a round() effect rather than a floor() - hash ^= DoubleHashKey::hashFunction( + if (_type != SHAPE_TYPE_SIMPLE_HULL) { + // compute hash1 + uint32_t hash = _doubleHashKey.getHash(); + for (int j = 0; j < 3; ++j) { + // NOTE: 0.49f is used to bump the float up almost half a millimeter + // so the cast to int produces a round() effect rather than a floor() + hash ^= DoubleHashKey::hashFunction( (uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f), primeIndex++); - if (useOffset) { - hash ^= DoubleHashKey::hashFunction( + if (useOffset) { + hash ^= DoubleHashKey::hashFunction( (uint32_t)(_offset[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _offset[j]) * 0.49f), primeIndex++); + } } - } - _doubleHashKey.setHash(hash); + _doubleHashKey.setHash(hash); - // compute hash2 - hash = _doubleHashKey.getHash2(); - for (int j = 0; j < 3; ++j) { - // NOTE: 0.49f is used to bump the float up almost half a millimeter - // so the cast to int produces a round() effect rather than a floor() - uint32_t floatHash = DoubleHashKey::hashFunction2( + // compute hash2 + hash = _doubleHashKey.getHash2(); + for (int j = 0; j < 3; ++j) { + // NOTE: 0.49f is used to bump the float up almost half a millimeter + // so the cast to int produces a round() effect rather than a floor() + uint32_t floatHash = DoubleHashKey::hashFunction2( (uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f)); - if (useOffset) { - floatHash ^= DoubleHashKey::hashFunction2( + if (useOffset) { + floatHash ^= DoubleHashKey::hashFunction2( (uint32_t)(_offset[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _offset[j]) * 0.49f)); + } + hash += ~(floatHash << 17); + hash ^= (floatHash >> 11); + hash += (floatHash << 4); + hash ^= (floatHash >> 7); + hash += ~(floatHash << 10); + hash = (hash << 16) | (hash >> 16); } - hash += ~(floatHash << 17); - hash ^= (floatHash >> 11); - hash += (floatHash << 4); - hash ^= (floatHash >> 7); - hash += ~(floatHash << 10); - hash = (hash << 16) | (hash >> 16); + _doubleHashKey.setHash2(hash); + } else { + + assert(_pointCollection.size() == (size_t)1); + const PointList & points = _pointCollection.back(); + const int numPoints = (int)points.size(); + uint32_t hash = _doubleHashKey.getHash(); + uint32_t hash2 = _doubleHashKey.getHash2(); + + for (int pointIndex = 0; pointIndex < numPoints; ++pointIndex) { + // compute hash1 & 2 + const glm::vec3 &curPoint = points[pointIndex]; + for (int vecCompIndex = 0; vecCompIndex < 3; ++vecCompIndex) { + + // NOTE: 0.49f is used to bump the float up almost half a millimeter + // so the cast to int produces a round() effect rather than a floor() + uint32_t valueToHash = (uint32_t)(curPoint[vecCompIndex] * MILLIMETERS_PER_METER + copysignf(1.0f, curPoint[vecCompIndex]) * 0.49f); + + hash ^= DoubleHashKey::hashFunction(valueToHash, primeIndex++); + uint32_t floatHash = DoubleHashKey::hashFunction2(valueToHash); + + if (useOffset) { + + const uint32_t offsetValToHash = (uint32_t)(_offset[vecCompIndex] * MILLIMETERS_PER_METER + copysignf(1.0f, _offset[vecCompIndex])* 0.49f); + + hash ^= DoubleHashKey::hashFunction(offsetValToHash, primeIndex++); + floatHash ^= DoubleHashKey::hashFunction2(offsetValToHash); + } + + hash2 += ~(floatHash << 17); + hash2 ^= (floatHash >> 11); + hash2 += (floatHash << 4); + hash2 ^= (floatHash >> 7); + hash2 += ~(floatHash << 10); + hash2 = (hash2 << 16) | (hash2 >> 16); + } + } + + _doubleHashKey.setHash(hash); + _doubleHashKey.setHash2(hash2); } - _doubleHashKey.setHash2(hash); QString url = _url.toString(); if (!url.isEmpty()) { @@ -268,7 +360,7 @@ const DoubleHashKey& ShapeInfo::getHash() const { numHulls = 1; } if (numHulls > 0) { - hash = DoubleHashKey::hashFunction(numHulls, primeIndex++); + uint32_t hash = DoubleHashKey::hashFunction(numHulls, primeIndex++); _doubleHashKey.setHash(_doubleHashKey.getHash() ^ hash); hash = DoubleHashKey::hashFunction2(numHulls); _doubleHashKey.setHash2(_doubleHashKey.getHash2() ^ hash); diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h index 0ffdf1310d..d658b936a3 100644 --- a/libraries/shared/src/ShapeInfo.h +++ b/libraries/shared/src/ShapeInfo.h @@ -46,7 +46,8 @@ enum ShapeType { SHAPE_TYPE_SIMPLE_HULL, SHAPE_TYPE_SIMPLE_COMPOUND, SHAPE_TYPE_STATIC_MESH, - SHAPE_TYPE_ELLIPSOID + SHAPE_TYPE_ELLIPSOID, + SHAPE_TYPE_CIRCLE }; class ShapeInfo { @@ -57,6 +58,8 @@ public: using PointCollection = QVector; using TriangleIndices = QVector; + static QString getNameForShapeType(ShapeType type); + void clear(); void setParams(ShapeType type, const glm::vec3& halfExtents, QString url=""); @@ -66,7 +69,7 @@ public: void setCapsuleY(float radius, float halfHeight); void setOffset(const glm::vec3& offset); - int getType() const { return _type; } + ShapeType getType() const { return _type; } const glm::vec3& getHalfExtents() const { return _halfExtents; } const glm::vec3& getOffset() const { return _offset; } diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp index 68c99a9753..ce7dd18921 100644 --- a/libraries/shared/src/TriangleSet.cpp +++ b/libraries/shared/src/TriangleSet.cpp @@ -104,8 +104,9 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersectionInternal(const glm::vec if (_bounds.findRayIntersection(origin, direction, boxDistance, face, surfaceNormal)) { // if our bounding box intersects at a distance greater than the current known - // best distance, than we can safely not check any of our triangles - if (boxDistance > bestDistance) { + // best distance, and our origin isn't inside the boounds, then we can safely + // not check any of our triangles + if (boxDistance > bestDistance && !_bounds.contains(origin)) { return false; } diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index ee2db6f6e0..e34855d521 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -119,6 +119,7 @@ Script.include("/~/system/libraries/controllers.js"); this.actionID = null; // action this script created... this.entityWithContextOverlay = false; this.contextOverlayTimer = false; + this.previousCollisionStatus = false; this.reticleMinX = MARGIN; this.reticleMaxX; this.reticleMinY = MARGIN; @@ -342,7 +343,9 @@ Script.include("/~/system/libraries/controllers.js"); if (this.madeDynamic) { var props = {}; props.dynamic = false; + props.collisionless = this.previousCollisionStatus; props.localVelocity = {x: 0, y: 0, z: 0}; + props.localRotation = {x: 0, y: 0, z: 0}; Entities.editEntity(this.grabbedThingID, props); this.madeDynamic = false; } @@ -507,10 +510,12 @@ Script.include("/~/system/libraries/controllers.js"); if (entityIsGrabbable(targetProps)) { if (!entityIsDistanceGrabbable(targetProps)) { targetProps.dynamic = true; + this.previousCollisionStatus = targetProps.collisionless; + targetProps.collisionless = true; Entities.editEntity(entityID, targetProps); this.madeDynamic = true; } - + if (!this.distanceRotating) { this.grabbedThingID = entityID; this.grabbedDistance = rayPickInfo.distance; diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index b1e5599dc6..a015eed714 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1373,7 +1373,7 @@ function loaded() { elShape.addEventListener('change', createEmitTextPropertyUpdateFunction('shape')); elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl')); - elWebDPI.addEventListener('change', createEmitNumberPropertyUpdateFunction('dpi')); + elWebDPI.addEventListener('change', createEmitNumberPropertyUpdateFunction('dpi', 0)); elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL')); elShapeType.addEventListener('change', createEmitTextPropertyUpdateFunction('shapeType')); diff --git a/tests/gpu-test/src/TestInstancedShapes.cpp b/tests/gpu-test/src/TestInstancedShapes.cpp index 6a98ee58b9..da50f8521f 100644 --- a/tests/gpu-test/src/TestInstancedShapes.cpp +++ b/tests/gpu-test/src/TestInstancedShapes.cpp @@ -10,18 +10,18 @@ gpu::Stream::FormatPointer& getInstancedSolidStreamFormat(); -static const size_t TYPE_COUNT = 4; -static const size_t ITEM_COUNT = 50; -static const float SHAPE_INTERVAL = (PI * 2.0f) / ITEM_COUNT; -static const float ITEM_INTERVAL = SHAPE_INTERVAL / TYPE_COUNT; - -static GeometryCache::Shape SHAPE[TYPE_COUNT] = { +static GeometryCache::Shape SHAPE[] = { GeometryCache::Icosahedron, GeometryCache::Cube, GeometryCache::Sphere, GeometryCache::Tetrahedron, }; +static const size_t TYPE_COUNT = (sizeof(SHAPE) / sizeof((SHAPE)[0])); +static const size_t ITEM_COUNT = 50; +static const float SHAPE_INTERVAL = (PI * 2.0f) / ITEM_COUNT; +static const float ITEM_INTERVAL = SHAPE_INTERVAL / TYPE_COUNT; + const gpu::Element POSITION_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ }; const gpu::Element NORMAL_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ }; const gpu::Element COLOR_ELEMENT { gpu::VEC4, gpu::NUINT8, gpu::RGBA }; @@ -34,8 +34,6 @@ TestInstancedShapes::TestInstancedShapes() { static const float ITEM_RADIUS = 20; static const vec3 ITEM_TRANSLATION { 0, 0, -ITEM_RADIUS }; for (size_t i = 0; i < TYPE_COUNT; ++i) { - GeometryCache::Shape shape = SHAPE[i]; - GeometryCache::ShapeData shapeData = geometryCache->_shapes[shape]; //indirectCommand._count float startingInterval = ITEM_INTERVAL * i; std::vector typeTransforms; @@ -62,7 +60,12 @@ void TestInstancedShapes::renderTest(size_t testId, RenderArgs* args) { batch.setInputFormat(getInstancedSolidStreamFormat()); for (size_t i = 0; i < TYPE_COUNT; ++i) { GeometryCache::Shape shape = SHAPE[i]; - GeometryCache::ShapeData shapeData = geometryCache->_shapes[shape]; + const GeometryCache::ShapeData *shapeData = geometryCache->getShapeData( shape ); + if (!shapeData) { + + //--EARLY ITERATION EXIT--( didn't have shape data yet ) + continue; + } std::string namedCall = __FUNCTION__ + std::to_string(i); @@ -71,13 +74,13 @@ void TestInstancedShapes::renderTest(size_t testId, RenderArgs* args) { batch.setModelTransform(transforms[i][j]); batch.setupNamedCalls(namedCall, [=](gpu::Batch& batch, gpu::Batch::NamedBatchData&) { batch.setInputBuffer(gpu::Stream::COLOR, gpu::BufferView(colorBuffer, i * ITEM_COUNT * 4, colorBuffer->getSize(), COLOR_ELEMENT)); - shapeData.drawInstances(batch, ITEM_COUNT); + shapeData->drawInstances(batch, ITEM_COUNT); }); } //for (size_t j = 0; j < ITEM_COUNT; ++j) { // batch.setModelTransform(transforms[j + i * ITEM_COUNT]); - // shapeData.draw(batch); + // shapeData->draw(batch); //} } }