From 1fd8a21f8e4e0a634066036c5ffe2f2b67037300 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 29 Nov 2018 11:38:42 -0800 Subject: [PATCH 01/27] cleanup --- .../entities/src/EntityItemProperties.cpp | 207 ++++++++++-------- libraries/entities/src/EntityItemProperties.h | 1 - libraries/entities/src/ModelEntityItem.cpp | 26 +-- .../entities/src/ParticleEffectEntityItem.cpp | 150 ++++++++----- libraries/entities/src/PolyLineEntityItem.cpp | 5 + libraries/entities/src/ShapeEntityItem.cpp | 4 +- libraries/entities/src/ZoneEntityItem.cpp | 42 ++-- .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- 9 files changed, 243 insertions(+), 197 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index c243f772e2..8970aed01a 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -219,15 +219,6 @@ QString EntityItemProperties::getBloomModeAsString() const { return getComponentModeAsString(_bloomMode); } -QString EntityItemProperties::getComponentModeString(uint32_t mode) { - // return "inherit" if mode is not valid - if (mode < COMPONENT_MODE_ITEM_COUNT) { - return COMPONENT_MODES[mode].second; - } else { - return COMPONENT_MODES[COMPONENT_MODE_INHERIT].second; - } -} - std::array::const_iterator EntityItemProperties::findComponent(const QString& mode) { return std::find_if(COMPONENT_MODES.begin(), COMPONENT_MODES.end(), [&](const ComponentPair& pair) { return (pair.second == mode); @@ -779,7 +770,6 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * materialData: JSON.stringify({ * materialVersion: 1, * materials: { - * // Can only set albedo on a Shape entity. * // Value overrides entity's "color" property. * albedo: [1.0, 1.0, 0] // Yellow * } @@ -1296,11 +1286,9 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROXY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_DYNAMIC, dynamic, collisionsWillMove, getDynamic()); // legacy support COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_HREF, href); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DESCRIPTION, description); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FACE_CAMERA, faceCamera); // Text only. COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ACTION_DATA, actionData); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCKED, locked); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_USER_DATA, userData); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VISIBLE_IN_SECONDARY_CAMERA, isVisibleInSecondaryCamera); // Certifiable Properties @@ -1321,33 +1309,44 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool // Particles only if (_type == EntityTypes::ParticleEffect) { - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMITTING_PARTICLES, isEmitting); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, getShapeTypeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MAX_PARTICLES, maxParticles); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LIFESPAN, lifespan); + + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMITTING_PARTICLES, isEmitting); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_RATE, emitRate); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_SPEED, emitSpeed); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SPEED_SPREAD, speedSpread); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_ORIENTATION, emitOrientation); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_DIMENSIONS, emitDimensions); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_RADIUS_START, emitRadiusStart); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_POLAR_START, polarStart); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_POLAR_FINISH, polarFinish); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_AZIMUTH_START, azimuthStart); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_AZIMUTH_FINISH, azimuthFinish); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMIT_ACCELERATION, emitAcceleration); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ACCELERATION_SPREAD, accelerationSpread); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARTICLE_RADIUS, particleRadius); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RADIUS_SPREAD, radiusSpread); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RADIUS_START, radiusStart); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RADIUS_FINISH, radiusFinish); + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR_SPREAD, colorSpread, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR_START, colorStart, vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR_FINISH, colorFinish, vec3Color); + + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_SPREAD, alphaSpread); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_START, alphaStart); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_FINISH, alphaFinish); + + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMITTER_SHOULD_TRAIL, emitterShouldTrail); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARTICLE_SPIN, particleSpin); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SPIN_SPREAD, spinSpread); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SPIN_START, spinStart); @@ -1357,22 +1356,21 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool // Models only if (_type == EntityTypes::Model) { + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MODEL_URL, modelURL); - if (!psuedoPropertyFlagsButDesiredEmpty) { - _animation.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); - } + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, getShapeTypeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_ROTATIONS_SET, jointRotationsSet); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_ROTATIONS, jointRotations); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_TRANSLATIONS_SET, jointTranslationsSet); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_TRANSLATIONS, jointTranslations); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RELAY_PARENT_JOINTS, relayParentJoints); - COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); + if (!psuedoPropertyFlagsButDesiredEmpty) { + _animation.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + } } - if (_type == EntityTypes::Model || _type == EntityTypes::Zone || _type == EntityTypes::ParticleEffect) { - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, getShapeTypeAsString()); - } - // FIXME: Shouldn't provide a shapeType property for Box and Sphere entities. if (_type == EntityTypes::Box) { COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, QString("Box")); @@ -1384,26 +1382,17 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool if (_type == EntityTypes::Box || _type == EntityTypes::Sphere || _type == EntityTypes::Shape) { COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SHAPE, shape); COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); - } - - // FIXME - it seems like ParticleEffect should also support this - if (_type == EntityTypes::Model || _type == EntityTypes::Zone) { - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); - } - - // Models & Particles - if (_type == EntityTypes::Model || _type == EntityTypes::ParticleEffect) { - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha); } // Lights only if (_type == EntityTypes::Light) { - COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IS_SPOTLIGHT, isSpotlight); + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_INTENSITY, intensity); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FALLOFF_RADIUS, falloffRadius); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EXPONENT, exponent); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CUTOFF, cutoff); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FALLOFF_RADIUS, falloffRadius); } // Text only @@ -1412,6 +1401,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_HEIGHT, lineHeight); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_TYPED(PROP_TEXT_COLOR, textColor, getTextColor(), u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_TYPED(PROP_BACKGROUND_COLOR, backgroundColor, getBackgroundColor(), u8vec3Color); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FACE_CAMERA, faceCamera); } // Zones only @@ -1419,24 +1409,22 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool if (!psuedoPropertyFlagsButDesiredEmpty) { _keyLight.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); _ambientLight.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); - _skybox.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + _haze.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + _bloom.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); } + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, getShapeTypeAsString()); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FLYING_ALLOWED, flyingAllowed); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GHOSTING_ALLOWED, ghostingAllowed); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FILTER_URL, filterURL); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_HAZE_MODE, hazeMode, getHazeModeAsString()); - if (!psuedoPropertyFlagsButDesiredEmpty) { - _haze.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); - } - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BLOOM_MODE, bloomMode, getBloomModeAsString()); - if (!psuedoPropertyFlagsButDesiredEmpty) { - _bloom.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); - } COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_KEY_LIGHT_MODE, keyLightMode, getKeyLightModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AMBIENT_LIGHT_MODE, ambientLightMode, getAmbientLightModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SKYBOX_MODE, skyboxMode, getSkyboxModeAsString()); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_HAZE_MODE, hazeMode, getHazeModeAsString()); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BLOOM_MODE, bloomMode, getBloomModeAsString()); } // Web only @@ -1464,15 +1452,22 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool } // Lines & PolyLines - if (_type == EntityTypes::Line || _type == EntityTypes::PolyLine) { + if (_type == EntityTypes::Line) { COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_WIDTH, lineWidth); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_POINTS, linePoints); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NORMALS, normals); // Polyline only. - COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_STROKE_COLORS, strokeColors, qVectorVec3Color); // Polyline only. - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_STROKE_WIDTHS, strokeWidths); // Polyline only. - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures); // Polyline only. - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IS_UV_MODE_STRETCH, isUVModeStretch); // Polyline only. + } + + if (_type == EntityTypes::PolyLine) { + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_WIDTH, lineWidth); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_POINTS, linePoints); + + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NORMALS, normals); + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_STROKE_COLORS, strokeColors, qVectorVec3Color); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_STROKE_WIDTHS, strokeWidths); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IS_UV_MODE_STRETCH, isUVModeStretch); } // Materials @@ -1615,7 +1610,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaSpread, float, setAlphaSpread); COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaStart, float, setAlphaStart); COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaFinish, float, setAlphaFinish); - COPY_PROPERTY_FROM_QSCRIPTVALUE(emitterShouldTrail , bool, setEmitterShouldTrail); + COPY_PROPERTY_FROM_QSCRIPTVALUE(emitterShouldTrail, bool, setEmitterShouldTrail); COPY_PROPERTY_FROM_QSCRIPTVALUE(modelURL, QString, setModelURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(compoundShapeURL, QString, setCompoundShapeURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(localRenderAlpha, float, setLocalRenderAlpha); @@ -1707,16 +1702,16 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(actionData, QByteArray, setActionData); COPY_PROPERTY_FROM_QSCRIPTVALUE(normals, qVectorVec3, setNormals); COPY_PROPERTY_FROM_QSCRIPTVALUE(strokeColors, qVectorVec3, setStrokeColors); - COPY_PROPERTY_FROM_QSCRIPTVALUE(strokeWidths,qVectorFloat, setStrokeWidths); + COPY_PROPERTY_FROM_QSCRIPTVALUE(strokeWidths, qVectorFloat, setStrokeWidths); COPY_PROPERTY_FROM_QSCRIPTVALUE(isUVModeStretch, bool, setIsUVModeStretch); if (!honorReadOnly) { // this is used by the json reader to set things that we don't want javascript to able to affect. COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(created, QDateTime, setCreated, [this]() { - auto result = QDateTime::fromMSecsSinceEpoch(_created / 1000, Qt::UTC); // usec per msec - return result; - }); + auto result = QDateTime::fromMSecsSinceEpoch(_created / 1000, Qt::UTC); // usec per msec + return result; + }); // TODO: expose this to QScriptValue for JSON saves? //COPY_PROPERTY_FROM_QSCRIPTVALUE(simulationOwner, ???, setSimulatorPriority); } @@ -1981,7 +1976,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue static std::once_flag initMap; - std::call_once(initMap, [](){ + std::call_once(initMap, []() { ADD_PROPERTY_TO_MAP(PROP_VISIBLE, Visible, visible, bool); ADD_PROPERTY_TO_MAP(PROP_CAN_CAST_SHADOW, CanCastShadow, canCastShadow, bool); ADD_PROPERTY_TO_MAP(PROP_POSITION, Position, position, vec3); @@ -2312,8 +2307,8 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy bool successPropertyFlagsFits = packetData->appendRawData(encodedPropertyFlags); int propertyCount = 0; - bool headerFits = successIDFits && successTypeFits && successLastEditedFits - && successLastUpdatedFits && successPropertyFlagsFits; + bool headerFits = successIDFits && successTypeFits && successLastEditedFits && + successLastUpdatedFits && successPropertyFlagsFits; int startOfEntityItemData = packetData->getUncompressedByteOffset(); @@ -2370,35 +2365,36 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy } if (properties.getType() == EntityTypes::Model) { + APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, properties.getModelURL()); APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, properties.getCompoundShapeURL()); APPEND_ENTITY_PROPERTY(PROP_TEXTURES, properties.getTextures()); APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)(properties.getShapeType())); - APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); - - _staticAnimation.setProperties(properties); - _staticAnimation.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); APPEND_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS_SET, properties.getJointRotationsSet()); APPEND_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS, properties.getJointRotations()); APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS_SET, properties.getJointTranslationsSet()); APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, properties.getJointTranslations()); APPEND_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, properties.getRelayParentJoints()); + + _staticAnimation.setProperties(properties); + _staticAnimation.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); } if (properties.getType() == EntityTypes::Light) { APPEND_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, properties.getIsSpotlight()); APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); APPEND_ENTITY_PROPERTY(PROP_INTENSITY, properties.getIntensity()); - APPEND_ENTITY_PROPERTY(PROP_FALLOFF_RADIUS, properties.getFalloffRadius()); APPEND_ENTITY_PROPERTY(PROP_EXPONENT, properties.getExponent()); APPEND_ENTITY_PROPERTY(PROP_CUTOFF, properties.getCutoff()); + APPEND_ENTITY_PROPERTY(PROP_FALLOFF_RADIUS, properties.getFalloffRadius()); } if (properties.getType() == EntityTypes::ParticleEffect) { - APPEND_ENTITY_PROPERTY(PROP_TEXTURES, properties.getTextures()); + APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)(properties.getShapeType())); APPEND_ENTITY_PROPERTY(PROP_MAX_PARTICLES, properties.getMaxParticles()); APPEND_ENTITY_PROPERTY(PROP_LIFESPAN, properties.getLifespan()); + APPEND_ENTITY_PROPERTY(PROP_EMITTING_PARTICLES, properties.getIsEmitting()); APPEND_ENTITY_PROPERTY(PROP_EMIT_RATE, properties.getEmitRate()); APPEND_ENTITY_PROPERTY(PROP_EMIT_SPEED, properties.getEmitSpeed()); @@ -2406,24 +2402,33 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_EMIT_ORIENTATION, properties.getEmitOrientation()); APPEND_ENTITY_PROPERTY(PROP_EMIT_DIMENSIONS, properties.getEmitDimensions()); APPEND_ENTITY_PROPERTY(PROP_EMIT_RADIUS_START, properties.getEmitRadiusStart()); + APPEND_ENTITY_PROPERTY(PROP_POLAR_START, properties.getPolarStart()); APPEND_ENTITY_PROPERTY(PROP_POLAR_FINISH, properties.getPolarFinish()); APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_START, properties.getAzimuthStart()); APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, properties.getAzimuthFinish()); + APPEND_ENTITY_PROPERTY(PROP_EMIT_ACCELERATION, properties.getEmitAcceleration()); APPEND_ENTITY_PROPERTY(PROP_ACCELERATION_SPREAD, properties.getAccelerationSpread()); + APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, properties.getParticleRadius()); APPEND_ENTITY_PROPERTY(PROP_RADIUS_SPREAD, properties.getRadiusSpread()); APPEND_ENTITY_PROPERTY(PROP_RADIUS_START, properties.getRadiusStart()); APPEND_ENTITY_PROPERTY(PROP_RADIUS_FINISH, properties.getRadiusFinish()); + APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); APPEND_ENTITY_PROPERTY(PROP_COLOR_SPREAD, properties.getColorSpread()); APPEND_ENTITY_PROPERTY(PROP_COLOR_START, properties.getColorStart()); APPEND_ENTITY_PROPERTY(PROP_COLOR_FINISH, properties.getColorFinish()); + + APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha()); APPEND_ENTITY_PROPERTY(PROP_ALPHA_SPREAD, properties.getAlphaSpread()); APPEND_ENTITY_PROPERTY(PROP_ALPHA_START, properties.getAlphaStart()); APPEND_ENTITY_PROPERTY(PROP_ALPHA_FINISH, properties.getAlphaFinish()); + + APPEND_ENTITY_PROPERTY(PROP_TEXTURES, properties.getTextures()); APPEND_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, properties.getEmitterShouldTrail()); + APPEND_ENTITY_PROPERTY(PROP_PARTICLE_SPIN, properties.getParticleSpin()); APPEND_ENTITY_PROPERTY(PROP_SPIN_SPREAD, properties.getSpinSpread()); APPEND_ENTITY_PROPERTY(PROP_SPIN_START, properties.getSpinStart()); @@ -2438,27 +2443,27 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy _staticAmbientLight.setProperties(properties); _staticAmbientLight.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); - APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)properties.getShapeType()); - APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, properties.getCompoundShapeURL()); - _staticSkybox.setProperties(properties); _staticSkybox.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + _staticHaze.setProperties(properties); + _staticHaze.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + + _staticBloom.setProperties(properties); + _staticBloom.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + + APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)properties.getShapeType()); + APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, properties.getCompoundShapeURL()); + APPEND_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, properties.getFlyingAllowed()); APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, properties.getGhostingAllowed()); APPEND_ENTITY_PROPERTY(PROP_FILTER_URL, properties.getFilterURL()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)properties.getHazeMode()); - _staticHaze.setProperties(properties); - _staticHaze.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); - - APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)properties.getBloomMode()); - _staticBloom.setProperties(properties); - _staticBloom.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); - APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, (uint32_t)properties.getKeyLightMode()); APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, (uint32_t)properties.getAmbientLightMode()); APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)properties.getSkyboxMode()); + APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)properties.getHazeMode()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)properties.getBloomMode()); } if (properties.getType() == EntityTypes::PolyVox) { @@ -2499,6 +2504,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy properties.getType() == EntityTypes::Sphere) { APPEND_ENTITY_PROPERTY(PROP_SHAPE, properties.getShape()); APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); + APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha()); } // Materials @@ -2516,7 +2522,6 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName()); APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, properties.getCollisionSoundURL()); APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, properties.getActionData()); - APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha()); // Certifiable Properties APPEND_ENTITY_PROPERTY(PROP_ITEM_NAME, properties.getItemName()); @@ -2547,8 +2552,8 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy encodedPropertyFlags = propertyFlags; int newPropertyFlagsLength = encodedPropertyFlags.length(); - packetData->updatePriorBytes(propertyFlagsOffset, - (const unsigned char*)encodedPropertyFlags.constData(), encodedPropertyFlags.length()); + packetData->updatePriorBytes(propertyFlagsOffset, (const unsigned char*)encodedPropertyFlags.constData(), + encodedPropertyFlags.length()); // if the size of the PropertyFlags shrunk, we need to shift everything down to front of packet. if (newPropertyFlagsLength < oldPropertyFlagsLength) { @@ -2763,34 +2768,35 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int } if (properties.getType() == EntityTypes::Model) { + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MODEL_URL, QString, setModelURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXTURES, QString, setTextures); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); - - properties.getAnimation().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_ROTATIONS_SET, QVector, setJointRotationsSet); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_ROTATIONS, QVector, setJointRotations); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_TRANSLATIONS_SET, QVector, setJointTranslationsSet); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_TRANSLATIONS, QVector, setJointTranslations); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints); + + properties.getAnimation().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); } if (properties.getType() == EntityTypes::Light) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IS_SPOTLIGHT, bool, setIsSpotlight); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_INTENSITY, float, setIntensity); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FALLOFF_RADIUS, float, setFalloffRadius); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EXPONENT, float, setExponent); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CUTOFF, float, setCutoff); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FALLOFF_RADIUS, float, setFalloffRadius); } if (properties.getType() == EntityTypes::ParticleEffect) { - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXTURES, QString, setTextures); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MAX_PARTICLES, quint32, setMaxParticles); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LIFESPAN, float, setLifespan); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMITTING_PARTICLES, bool, setIsEmitting); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_RATE, float, setEmitRate); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_SPEED, float, setEmitSpeed); @@ -2798,24 +2804,33 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_ORIENTATION, quat, setEmitOrientation); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_DIMENSIONS, vec3, setEmitDimensions); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_RADIUS_START, float, setEmitRadiusStart); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POLAR_START, float, setPolarStart); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POLAR_FINISH, float, setPolarFinish); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AZIMUTH_START, float, setAzimuthStart); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AZIMUTH_FINISH, float, setAzimuthFinish); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_ACCELERATION, vec3, setEmitAcceleration); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACCELERATION_SPREAD, vec3, setAccelerationSpread); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARTICLE_RADIUS, float, setParticleRadius); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RADIUS_SPREAD, float, setRadiusSpread); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RADIUS_START, float, setRadiusStart); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RADIUS_FINISH, float, setRadiusFinish); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR_SPREAD, u8vec3Color, setColorSpread); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR_START, vec3Color, setColorStart); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR_FINISH, vec3Color, setColorFinish); + + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_SPREAD, float, setAlphaSpread); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_START, float, setAlphaStart); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_FINISH, float, setAlphaFinish); + + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXTURES, QString, setTextures); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMITTER_SHOULD_TRAIL, bool, setEmitterShouldTrail); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARTICLE_SPIN, float, setParticleSpin); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SPIN_SPREAD, float, setSpinSpread); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SPIN_START, float, setSpinStart); @@ -2826,24 +2841,22 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int if (properties.getType() == EntityTypes::Zone) { properties.getKeyLight().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); properties.getAmbientLight().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + properties.getSkybox().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + properties.getHaze().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + properties.getBloom().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); - properties.getSkybox().decodeFromEditPacket(propertyFlags, dataAt , processedBytes); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FLYING_ALLOWED, bool, setFlyingAllowed); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FILTER_URL, QString, setFilterURL); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HAZE_MODE, uint32_t, setHazeMode); - properties.getHaze().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); - - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BLOOM_MODE, uint32_t, setBloomMode); - properties.getBloom().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEY_LIGHT_MODE, uint32_t, setKeyLightMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AMBIENT_LIGHT_MODE, uint32_t, setAmbientLightMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SKYBOX_MODE, uint32_t, setSkyboxMode); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HAZE_MODE, uint32_t, setHazeMode); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BLOOM_MODE, uint32_t, setBloomMode); } if (properties.getType() == EntityTypes::PolyVox) { @@ -2886,6 +2899,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int properties.getType() == EntityTypes::Sphere) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE, QString, setShape); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha); } // Materials @@ -2903,7 +2917,6 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACTION_DATA, QByteArray, setActionData); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha); // Certifiable Properties READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ITEM_NAME, QString, setItemName); @@ -2946,7 +2959,7 @@ QVector EntityItemProperties::unpackNormals(const QByteArray& normals) { j++; } } else { - qCDebug(entities) << "WARNING - Expected received size for normals does not match. Expected: " << (int)normals[0] + qCDebug(entities) << "WARNING - Expected received size for normals does not match. Expected: " << (int)normals[0] << " Received: " << (normals.size() / 6); } return unpackedNormals; @@ -2959,7 +2972,7 @@ void EntityItemProperties::setPackedStrokeColors(const QByteArray& value) { QVector EntityItemProperties::unpackStrokeColors(const QByteArray& strokeColors) { // the size of the vector is packed first QVector unpackedStrokeColors = QVector((int)strokeColors[0]); - + if ((int)strokeColors[0] == strokeColors.size() / 3) { int j = 0; for (int i = 1; i < strokeColors.size();) { @@ -2970,7 +2983,7 @@ QVector EntityItemProperties::unpackStrokeColors(const QByteArray& strokeC unpackedStrokeColors[j++] = vec3(r, g, b); } } else { - qCDebug(entities) << "WARNING - Expected received size for stroke colors does not match. Expected: " + qCDebug(entities) << "WARNING - Expected received size for stroke colors does not match. Expected: " << (int)strokeColors[0] << " Received: " << (strokeColors.size() / 3); } @@ -2987,7 +3000,7 @@ bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityIt int outputLength = 0; - if (buffer.size() < (int) (sizeof(numberOfIds) + NUM_BYTES_RFC4122_UUID)) { + if (buffer.size() < (int)(sizeof(numberOfIds) + NUM_BYTES_RFC4122_UUID)) { qCDebug(entities) << "ERROR - encodeEraseEntityMessage() called with buffer that is too small!"; return false; } @@ -3235,7 +3248,7 @@ AABox EntityItemProperties::getAABox() const { // _position represents the position of the registration point. vec3 registrationRemainder = vec3(1.0f) - _registrationPoint; - vec3 unrotatedMinRelativeToEntity = - (_dimensions * _registrationPoint); + vec3 unrotatedMinRelativeToEntity = -(_dimensions * _registrationPoint); vec3 unrotatedMaxRelativeToEntity = _dimensions * registrationRemainder; Extents unrotatedExtentsRelativeToRegistrationPoint = { unrotatedMinRelativeToEntity, unrotatedMaxRelativeToEntity }; Extents rotatedExtentsRelativeToRegistrationPoint = unrotatedExtentsRelativeToRegistrationPoint.getRotated(_rotation); @@ -3247,7 +3260,7 @@ AABox EntityItemProperties::getAABox() const { } bool EntityItemProperties::hasTransformOrVelocityChanges() const { - return _positionChanged ||_localPositionChanged + return _positionChanged || _localPositionChanged || _rotationChanged || _localRotationChanged || _velocityChanged || _localVelocityChanged || _angularVelocityChanged || _localAngularVelocityChanged @@ -3333,7 +3346,7 @@ uint8_t EntityItemProperties::computeSimulationBidPriority() const { if (_parentIDChanged || _parentJointIndexChanged) { // we need higher simulation ownership priority to chang parenting info priority = SCRIPT_GRAB_SIMULATION_PRIORITY; - } else if ( _positionChanged || _localPositionChanged + } else if (_positionChanged || _localPositionChanged || _rotationChanged || _localRotationChanged || _velocityChanged || _localVelocityChanged || _angularVelocityChanged || _localAngularVelocityChanged) { @@ -3954,4 +3967,4 @@ void EntityItemProperties::convertToCloneProperties(const EntityItemID& entityID setCloneLimit(ENTITY_ITEM_DEFAULT_CLONE_LIMIT); setCloneDynamic(ENTITY_ITEM_DEFAULT_CLONE_DYNAMIC); setCloneAvatarEntity(ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY); -} +} \ No newline at end of file diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index c91ccda5aa..4d45f280e2 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -298,7 +298,6 @@ public: DEFINE_PROPERTY_GROUP(Grab, grab, GrabPropertyGroup); - static QString getComponentModeString(uint32_t mode); static QString getComponentModeAsString(uint32_t mode); std::array::const_iterator findComponent(const QString& mode); diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index b4b00e57a7..9b69ed0f4e 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -120,6 +120,11 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_MODEL_URL, QString, setModelURL); READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures); + READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType); + READ_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS_SET, QVector, setJointRotationsSet); + READ_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS, QVector, setJointRotations); + READ_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS_SET, QVector, setJointTranslationsSet); + READ_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, QVector, setJointTranslations); READ_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints); // grab a local copy of _animationProperties to avoid multiple locks @@ -140,13 +145,6 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, bytesRead += bytesFromAnimation; dataAt += bytesFromAnimation; - READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType); - - READ_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS_SET, QVector, setJointRotationsSet); - READ_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS, QVector, setJointRotations); - READ_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS_SET, QVector, setJointTranslationsSet); - READ_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, QVector, setJointTranslations); - return bytesRead; } @@ -158,12 +156,12 @@ EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& requestedProperties += PROP_COMPOUND_SHAPE_URL; requestedProperties += PROP_TEXTURES; requestedProperties += PROP_SHAPE_TYPE; - requestedProperties += _animationProperties.getEntityProperties(params); requestedProperties += PROP_JOINT_ROTATIONS_SET; requestedProperties += PROP_JOINT_ROTATIONS; requestedProperties += PROP_JOINT_TRANSLATIONS_SET; requestedProperties += PROP_JOINT_TRANSLATIONS; requestedProperties += PROP_RELAY_PARENT_JOINTS; + requestedProperties += _animationProperties.getEntityProperties(params); return requestedProperties; } @@ -182,19 +180,17 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, getModelURL()); APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, getCompoundShapeURL()); APPEND_ENTITY_PROPERTY(PROP_TEXTURES, getTextures()); + APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)getShapeType()); + APPEND_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS_SET, getJointRotationsSet()); + APPEND_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS, getJointRotations()); + APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS_SET, getJointTranslationsSet()); + APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, getJointTranslations()); APPEND_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, getRelayParentJoints()); withReadLock([&] { _animationProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); }); - - APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)getShapeType()); - - APPEND_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS_SET, getJointRotationsSet()); - APPEND_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS, getJointRotations()); - APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS_SET, getJointTranslationsSet()); - APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, getJointTranslations()); } diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index acdeac0e93..2cafbd017e 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -409,11 +409,10 @@ void ParticleEffectEntityItem::computeAndUpdateDimensions() { EntityItemProperties ParticleEffectEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType); // FIXME - this doesn't appear to get used + COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType); COPY_ENTITY_PROPERTY_TO_PROPERTIES(maxParticles, getMaxParticles); COPY_ENTITY_PROPERTY_TO_PROPERTIES(lifespan, getLifespan); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(isEmitting, getIsEmitting); COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitRate, getEmitRate); COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitSpeed, getEmitSpeed); @@ -421,24 +420,33 @@ EntityItemProperties ParticleEffectEntityItem::getProperties(const EntityPropert COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitOrientation, getEmitOrientation); COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitDimensions, getEmitDimensions); COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitRadiusStart, getEmitRadiusStart); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(polarStart, getPolarStart); COPY_ENTITY_PROPERTY_TO_PROPERTIES(polarFinish, getPolarFinish); COPY_ENTITY_PROPERTY_TO_PROPERTIES(azimuthStart, getAzimuthStart); COPY_ENTITY_PROPERTY_TO_PROPERTIES(azimuthFinish, getAzimuthFinish); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitAcceleration, getEmitAcceleration); COPY_ENTITY_PROPERTY_TO_PROPERTIES(accelerationSpread, getAccelerationSpread); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(particleRadius, getParticleRadius); COPY_ENTITY_PROPERTY_TO_PROPERTIES(radiusSpread, getRadiusSpread); COPY_ENTITY_PROPERTY_TO_PROPERTIES(radiusStart, getRadiusStart); COPY_ENTITY_PROPERTY_TO_PROPERTIES(radiusFinish, getRadiusFinish); + + COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(colorSpread, getColorSpread); COPY_ENTITY_PROPERTY_TO_PROPERTIES(colorStart, getColorStart); COPY_ENTITY_PROPERTY_TO_PROPERTIES(colorFinish, getColorFinish); + + COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha); COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaSpread, getAlphaSpread); COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaStart, getAlphaStart); COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaFinish, getAlphaFinish); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures); COPY_ENTITY_PROPERTY_TO_PROPERTIES(emitterShouldTrail, getEmitterShouldTrail); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(particleSpin, getParticleSpin); COPY_ENTITY_PROPERTY_TO_PROPERTIES(spinSpread, getSpinSpread); COPY_ENTITY_PROPERTY_TO_PROPERTIES(spinStart, getSpinStart); @@ -451,11 +459,10 @@ EntityItemProperties ParticleEffectEntityItem::getProperties(const EntityPropert bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class - SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha); SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType); SET_ENTITY_PROPERTY_FROM_PROPERTIES(maxParticles, setMaxParticles); SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifespan, setLifespan); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(isEmitting, setIsEmitting); SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitRate, setEmitRate); SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitSpeed, setEmitSpeed); @@ -463,24 +470,33 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitOrientation, setEmitOrientation); SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitDimensions, setEmitDimensions); SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitRadiusStart, setEmitRadiusStart); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(polarStart, setPolarStart); SET_ENTITY_PROPERTY_FROM_PROPERTIES(polarFinish, setPolarFinish); SET_ENTITY_PROPERTY_FROM_PROPERTIES(azimuthStart, setAzimuthStart); SET_ENTITY_PROPERTY_FROM_PROPERTIES(azimuthFinish, setAzimuthFinish); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitAcceleration, setEmitAcceleration); SET_ENTITY_PROPERTY_FROM_PROPERTIES(accelerationSpread, setAccelerationSpread); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(particleRadius, setParticleRadius); SET_ENTITY_PROPERTY_FROM_PROPERTIES(radiusSpread, setRadiusSpread); SET_ENTITY_PROPERTY_FROM_PROPERTIES(radiusStart, setRadiusStart); SET_ENTITY_PROPERTY_FROM_PROPERTIES(radiusFinish, setRadiusFinish); + + SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); SET_ENTITY_PROPERTY_FROM_PROPERTIES(colorSpread, setColorSpread); SET_ENTITY_PROPERTY_FROM_PROPERTIES(colorStart, setColorStart); SET_ENTITY_PROPERTY_FROM_PROPERTIES(colorFinish, setColorFinish); + + SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha); SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaSpread, setAlphaSpread); SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaStart, setAlphaStart); SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaFinish, setAlphaFinish); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures); SET_ENTITY_PROPERTY_FROM_PROPERTIES(emitterShouldTrail, setEmitterShouldTrail); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(particleSpin, setParticleSpin); SET_ENTITY_PROPERTY_FROM_PROPERTIES(spinSpread, setSpinSpread); SET_ENTITY_PROPERTY_FROM_PROPERTIES(spinStart, setSpinStart); @@ -514,40 +530,42 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch int bytesRead = 0; const unsigned char* dataAt = data; - READ_ENTITY_PROPERTY(PROP_COLOR, u8vec3Color, setColor); - READ_ENTITY_PROPERTY(PROP_EMITTING_PARTICLES, bool, setIsEmitting); READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType); READ_ENTITY_PROPERTY(PROP_MAX_PARTICLES, quint32, setMaxParticles); READ_ENTITY_PROPERTY(PROP_LIFESPAN, float, setLifespan); + + READ_ENTITY_PROPERTY(PROP_EMITTING_PARTICLES, bool, setIsEmitting); READ_ENTITY_PROPERTY(PROP_EMIT_RATE, float, setEmitRate); - - READ_ENTITY_PROPERTY(PROP_EMIT_ACCELERATION, glm::vec3, setEmitAcceleration); - READ_ENTITY_PROPERTY(PROP_ACCELERATION_SPREAD, glm::vec3, setAccelerationSpread); - READ_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float, setParticleRadius); - READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures); - - READ_ENTITY_PROPERTY(PROP_RADIUS_SPREAD, float, setRadiusSpread); - READ_ENTITY_PROPERTY(PROP_RADIUS_START, float, setRadiusStart); - READ_ENTITY_PROPERTY(PROP_RADIUS_FINISH, float, setRadiusFinish); - - READ_ENTITY_PROPERTY(PROP_COLOR_SPREAD, u8vec3Color, setColorSpread); - READ_ENTITY_PROPERTY(PROP_COLOR_START, vec3Color, setColorStart); - READ_ENTITY_PROPERTY(PROP_COLOR_FINISH, vec3Color, setColorFinish); - READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha); - READ_ENTITY_PROPERTY(PROP_ALPHA_SPREAD, float, setAlphaSpread); - READ_ENTITY_PROPERTY(PROP_ALPHA_START, float, setAlphaStart); - READ_ENTITY_PROPERTY(PROP_ALPHA_FINISH, float, setAlphaFinish); - READ_ENTITY_PROPERTY(PROP_EMIT_SPEED, float, setEmitSpeed); READ_ENTITY_PROPERTY(PROP_SPEED_SPREAD, float, setSpeedSpread); READ_ENTITY_PROPERTY(PROP_EMIT_ORIENTATION, quat, setEmitOrientation); READ_ENTITY_PROPERTY(PROP_EMIT_DIMENSIONS, glm::vec3, setEmitDimensions); READ_ENTITY_PROPERTY(PROP_EMIT_RADIUS_START, float, setEmitRadiusStart); + READ_ENTITY_PROPERTY(PROP_POLAR_START, float, setPolarStart); READ_ENTITY_PROPERTY(PROP_POLAR_FINISH, float, setPolarFinish); READ_ENTITY_PROPERTY(PROP_AZIMUTH_START, float, setAzimuthStart); READ_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, float, setAzimuthFinish); + READ_ENTITY_PROPERTY(PROP_EMIT_ACCELERATION, glm::vec3, setEmitAcceleration); + READ_ENTITY_PROPERTY(PROP_ACCELERATION_SPREAD, glm::vec3, setAccelerationSpread); + + READ_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float, setParticleRadius); + READ_ENTITY_PROPERTY(PROP_RADIUS_SPREAD, float, setRadiusSpread); + READ_ENTITY_PROPERTY(PROP_RADIUS_START, float, setRadiusStart); + READ_ENTITY_PROPERTY(PROP_RADIUS_FINISH, float, setRadiusFinish); + + READ_ENTITY_PROPERTY(PROP_COLOR, u8vec3Color, setColor); + READ_ENTITY_PROPERTY(PROP_COLOR_SPREAD, u8vec3Color, setColorSpread); + READ_ENTITY_PROPERTY(PROP_COLOR_START, vec3Color, setColorStart); + READ_ENTITY_PROPERTY(PROP_COLOR_FINISH, vec3Color, setColorFinish); + + READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha); + READ_ENTITY_PROPERTY(PROP_ALPHA_SPREAD, float, setAlphaSpread); + READ_ENTITY_PROPERTY(PROP_ALPHA_START, float, setAlphaStart); + READ_ENTITY_PROPERTY(PROP_ALPHA_FINISH, float, setAlphaFinish); + + READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures); READ_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, bool, setEmitterShouldTrail); READ_ENTITY_PROPERTY(PROP_PARTICLE_SPIN, float, setParticleSpin); @@ -562,36 +580,44 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - requestedProperties += PROP_COLOR; requestedProperties += PROP_SHAPE_TYPE; requestedProperties += PROP_MAX_PARTICLES; requestedProperties += PROP_LIFESPAN; + requestedProperties += PROP_EMITTING_PARTICLES; requestedProperties += PROP_EMIT_RATE; - requestedProperties += PROP_EMIT_ACCELERATION; - requestedProperties += PROP_ACCELERATION_SPREAD; - requestedProperties += PROP_PARTICLE_RADIUS; - requestedProperties += PROP_TEXTURES; - requestedProperties += PROP_RADIUS_SPREAD; - requestedProperties += PROP_RADIUS_START; - requestedProperties += PROP_RADIUS_FINISH; - requestedProperties += PROP_COLOR_SPREAD; - requestedProperties += PROP_COLOR_START; - requestedProperties += PROP_COLOR_FINISH; - requestedProperties += PROP_ALPHA; - requestedProperties += PROP_ALPHA_SPREAD; - requestedProperties += PROP_ALPHA_START; - requestedProperties += PROP_ALPHA_FINISH; requestedProperties += PROP_EMIT_SPEED; requestedProperties += PROP_SPEED_SPREAD; requestedProperties += PROP_EMIT_ORIENTATION; requestedProperties += PROP_EMIT_DIMENSIONS; requestedProperties += PROP_EMIT_RADIUS_START; + requestedProperties += PROP_POLAR_START; requestedProperties += PROP_POLAR_FINISH; requestedProperties += PROP_AZIMUTH_START; requestedProperties += PROP_AZIMUTH_FINISH; + + requestedProperties += PROP_EMIT_ACCELERATION; + requestedProperties += PROP_ACCELERATION_SPREAD; + + requestedProperties += PROP_PARTICLE_RADIUS; + requestedProperties += PROP_RADIUS_SPREAD; + requestedProperties += PROP_RADIUS_START; + requestedProperties += PROP_RADIUS_FINISH; + + requestedProperties += PROP_COLOR; + requestedProperties += PROP_COLOR_SPREAD; + requestedProperties += PROP_COLOR_START; + requestedProperties += PROP_COLOR_FINISH; + + requestedProperties += PROP_ALPHA; + requestedProperties += PROP_ALPHA_SPREAD; + requestedProperties += PROP_ALPHA_START; + requestedProperties += PROP_ALPHA_FINISH; + + requestedProperties += PROP_TEXTURES; requestedProperties += PROP_EMITTER_SHOULD_TRAIL; + requestedProperties += PROP_PARTICLE_SPIN; requestedProperties += PROP_SPIN_SPREAD; requestedProperties += PROP_SPIN_START; @@ -610,36 +636,44 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, OctreeElement::AppendState& appendState) const { bool successPropertyFits = true; - APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor()); - APPEND_ENTITY_PROPERTY(PROP_EMITTING_PARTICLES, getIsEmitting()); APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)getShapeType()); APPEND_ENTITY_PROPERTY(PROP_MAX_PARTICLES, getMaxParticles()); APPEND_ENTITY_PROPERTY(PROP_LIFESPAN, getLifespan()); + APPEND_ENTITY_PROPERTY(PROP_EMITTING_PARTICLES, getIsEmitting()); + APPEND_ENTITY_PROPERTY(PROP_EMIT_RATE, getEmitRate()); - APPEND_ENTITY_PROPERTY(PROP_EMIT_ACCELERATION, getEmitAcceleration()); - APPEND_ENTITY_PROPERTY(PROP_ACCELERATION_SPREAD, getAccelerationSpread()); - APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, getParticleRadius()); - APPEND_ENTITY_PROPERTY(PROP_TEXTURES, getTextures()); - APPEND_ENTITY_PROPERTY(PROP_RADIUS_SPREAD, getRadiusSpread()); - APPEND_ENTITY_PROPERTY(PROP_RADIUS_START, getRadiusStart()); - APPEND_ENTITY_PROPERTY(PROP_RADIUS_FINISH, getRadiusFinish()); - APPEND_ENTITY_PROPERTY(PROP_COLOR_SPREAD, getColorSpread()); - APPEND_ENTITY_PROPERTY(PROP_COLOR_START, getColorStart()); - APPEND_ENTITY_PROPERTY(PROP_COLOR_FINISH, getColorFinish()); - APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); - APPEND_ENTITY_PROPERTY(PROP_ALPHA_SPREAD, getAlphaSpread()); - APPEND_ENTITY_PROPERTY(PROP_ALPHA_START, getAlphaStart()); - APPEND_ENTITY_PROPERTY(PROP_ALPHA_FINISH, getAlphaFinish()); APPEND_ENTITY_PROPERTY(PROP_EMIT_SPEED, getEmitSpeed()); APPEND_ENTITY_PROPERTY(PROP_SPEED_SPREAD, getSpeedSpread()); APPEND_ENTITY_PROPERTY(PROP_EMIT_ORIENTATION, getEmitOrientation()); APPEND_ENTITY_PROPERTY(PROP_EMIT_DIMENSIONS, getEmitDimensions()); APPEND_ENTITY_PROPERTY(PROP_EMIT_RADIUS_START, getEmitRadiusStart()); + APPEND_ENTITY_PROPERTY(PROP_POLAR_START, getPolarStart()); APPEND_ENTITY_PROPERTY(PROP_POLAR_FINISH, getPolarFinish()); APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_START, getAzimuthStart()); APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, getAzimuthFinish()); + + APPEND_ENTITY_PROPERTY(PROP_EMIT_ACCELERATION, getEmitAcceleration()); + APPEND_ENTITY_PROPERTY(PROP_ACCELERATION_SPREAD, getAccelerationSpread()); + + APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, getParticleRadius()); + APPEND_ENTITY_PROPERTY(PROP_RADIUS_SPREAD, getRadiusSpread()); + APPEND_ENTITY_PROPERTY(PROP_RADIUS_START, getRadiusStart()); + APPEND_ENTITY_PROPERTY(PROP_RADIUS_FINISH, getRadiusFinish()); + + APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor()); + APPEND_ENTITY_PROPERTY(PROP_COLOR_SPREAD, getColorSpread()); + APPEND_ENTITY_PROPERTY(PROP_COLOR_START, getColorStart()); + APPEND_ENTITY_PROPERTY(PROP_COLOR_FINISH, getColorFinish()); + + APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); + APPEND_ENTITY_PROPERTY(PROP_ALPHA_SPREAD, getAlphaSpread()); + APPEND_ENTITY_PROPERTY(PROP_ALPHA_START, getAlphaStart()); + APPEND_ENTITY_PROPERTY(PROP_ALPHA_FINISH, getAlphaFinish()); + + APPEND_ENTITY_PROPERTY(PROP_TEXTURES, getTextures()); APPEND_ENTITY_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, getEmitterShouldTrail()); + APPEND_ENTITY_PROPERTY(PROP_PARTICLE_SPIN, getParticleSpin()); APPEND_ENTITY_PROPERTY(PROP_SPIN_SPREAD, getSpinSpread()); APPEND_ENTITY_PROPERTY(PROP_SPIN_START, getSpinStart()); @@ -647,8 +681,6 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, APPEND_ENTITY_PROPERTY(PROP_PARTICLE_ROTATE_WITH_ENTITY, getRotateWithEntity()); } - - void ParticleEffectEntityItem::debugDump() const { quint64 now = usecTimestampNow(); qCDebug(entities) << "PA EFFECT EntityItem id:" << getEntityItemID() << "---------------------------------------------"; @@ -749,4 +781,4 @@ particle::Properties ParticleEffectEntityItem::getParticleProperties() const { } return result; -} +} \ No newline at end of file diff --git a/libraries/entities/src/PolyLineEntityItem.cpp b/libraries/entities/src/PolyLineEntityItem.cpp index c72256822d..d16a2f93d4 100644 --- a/libraries/entities/src/PolyLineEntityItem.cpp +++ b/libraries/entities/src/PolyLineEntityItem.cpp @@ -43,6 +43,7 @@ EntityItemProperties PolyLineEntityItem::getProperties(const EntityPropertyFlags COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(lineWidth, getLineWidth); COPY_ENTITY_PROPERTY_TO_PROPERTIES(linePoints, getLinePoints); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(normals, getNormals); COPY_ENTITY_PROPERTY_TO_PROPERTIES(strokeColors, getStrokeColors); COPY_ENTITY_PROPERTY_TO_PROPERTIES(strokeWidths, getStrokeWidths); @@ -59,6 +60,7 @@ bool PolyLineEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); SET_ENTITY_PROPERTY_FROM_PROPERTIES(lineWidth, setLineWidth); SET_ENTITY_PROPERTY_FROM_PROPERTIES(linePoints, setLinePoints); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(normals, setNormals); SET_ENTITY_PROPERTY_FROM_PROPERTIES(strokeColors, setStrokeColors); SET_ENTITY_PROPERTY_FROM_PROPERTIES(strokeWidths, setStrokeWidths); @@ -203,6 +205,7 @@ int PolyLineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* da READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor); READ_ENTITY_PROPERTY(PROP_LINE_WIDTH, float, setLineWidth); READ_ENTITY_PROPERTY(PROP_LINE_POINTS, QVector, setLinePoints); + READ_ENTITY_PROPERTY(PROP_NORMALS, QVector, setNormals); READ_ENTITY_PROPERTY(PROP_STROKE_COLORS, QVector, setStrokeColors); READ_ENTITY_PROPERTY(PROP_STROKE_WIDTHS, QVector, setStrokeWidths); @@ -217,6 +220,7 @@ EntityPropertyFlags PolyLineEntityItem::getEntityProperties(EncodeBitstreamParam requestedProperties += PROP_COLOR; requestedProperties += PROP_LINE_WIDTH; requestedProperties += PROP_LINE_POINTS; + requestedProperties += PROP_NORMALS; requestedProperties += PROP_STROKE_COLORS; requestedProperties += PROP_STROKE_WIDTHS; @@ -239,6 +243,7 @@ void PolyLineEntityItem::appendSubclassData(OctreePacketData* packetData, Encode APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor()); APPEND_ENTITY_PROPERTY(PROP_LINE_WIDTH, getLineWidth()); APPEND_ENTITY_PROPERTY(PROP_LINE_POINTS, getLinePoints()); + APPEND_ENTITY_PROPERTY(PROP_NORMALS, getNormals()); APPEND_ENTITY_PROPERTY(PROP_STROKE_COLORS, getStrokeColors()); APPEND_ENTITY_PROPERTY(PROP_STROKE_WIDTHS, getStrokeWidths()); diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index f67134da0a..08af12a289 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -158,9 +158,9 @@ void ShapeEntityItem::setShape(const entity::Shape& shape) { bool ShapeEntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class - SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); SET_ENTITY_PROPERTY_FROM_PROPERTIES(shape, setShape); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha); if (somethingChanged) { bool wantDebug = false; diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index 1b8675caac..ffd1dbba40 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -64,10 +64,10 @@ EntityItemProperties ZoneEntityItem::getProperties(const EntityPropertyFlags& de COPY_ENTITY_PROPERTY_TO_PROPERTIES(ghostingAllowed, getGhostingAllowed); COPY_ENTITY_PROPERTY_TO_PROPERTIES(filterURL, getFilterURL); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(hazeMode, getHazeMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightMode, getKeyLightMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(ambientLightMode, getAmbientLightMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(skyboxMode, getSkyboxMode); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(hazeMode, getHazeMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(bloomMode, getBloomMode); return properties; @@ -110,10 +110,10 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie SET_ENTITY_PROPERTY_FROM_PROPERTIES(ghostingAllowed, setGhostingAllowed); SET_ENTITY_PROPERTY_FROM_PROPERTIES(filterURL, setFilterURL); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(hazeMode, setHazeMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(keyLightMode, setKeyLightMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(ambientLightMode, setAmbientLightMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(skyboxMode, setSkyboxMode); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(hazeMode, setHazeMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(bloomMode, setBloomMode); somethingChanged = somethingChanged || _keyLightPropertiesChanged || _ambientLightPropertiesChanged || @@ -185,10 +185,10 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed); READ_ENTITY_PROPERTY(PROP_FILTER_URL, QString, setFilterURL); - READ_ENTITY_PROPERTY(PROP_HAZE_MODE, uint32_t, setHazeMode); READ_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, uint32_t, setKeyLightMode); READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, uint32_t, setAmbientLightMode); READ_ENTITY_PROPERTY(PROP_SKYBOX_MODE, uint32_t, setSkyboxMode); + READ_ENTITY_PROPERTY(PROP_HAZE_MODE, uint32_t, setHazeMode); READ_ENTITY_PROPERTY(PROP_BLOOM_MODE, uint32_t, setBloomMode); return bytesRead; @@ -197,11 +197,9 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - withReadLock([&] { - requestedProperties += _keyLightProperties.getEntityProperties(params); - requestedProperties += _ambientLightProperties.getEntityProperties(params); - requestedProperties += _skyboxProperties.getEntityProperties(params); - }); + requestedProperties += _keyLightProperties.getEntityProperties(params); + requestedProperties += _ambientLightProperties.getEntityProperties(params); + requestedProperties += _skyboxProperties.getEntityProperties(params); requestedProperties += _hazeProperties.getEntityProperties(params); requestedProperties += _bloomProperties.getEntityProperties(params); @@ -212,10 +210,10 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p requestedProperties += PROP_GHOSTING_ALLOWED; requestedProperties += PROP_FILTER_URL; - requestedProperties += PROP_HAZE_MODE; requestedProperties += PROP_KEY_LIGHT_MODE; requestedProperties += PROP_AMBIENT_LIGHT_MODE; requestedProperties += PROP_SKYBOX_MODE; + requestedProperties += PROP_HAZE_MODE; requestedProperties += PROP_BLOOM_MODE; return requestedProperties; @@ -231,12 +229,14 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits bool successPropertyFits = true; - _keyLightProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - _ambientLightProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - _skyboxProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); + withReadLock([&] { + _keyLightProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); + _ambientLightProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); + _skyboxProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); + }); _hazeProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); _bloomProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, @@ -249,10 +249,10 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, getGhostingAllowed()); APPEND_ENTITY_PROPERTY(PROP_FILTER_URL, getFilterURL()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)getHazeMode()); APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, (uint32_t)getKeyLightMode()); APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, (uint32_t)getAmbientLightMode()); APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)getSkyboxMode()); + APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)getHazeMode()); APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)getBloomMode()); } @@ -262,11 +262,11 @@ void ZoneEntityItem::debugDump() const { qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); - qCDebug(entities) << " _hazeMode:" << EntityItemProperties::getComponentModeString(_hazeMode); - qCDebug(entities) << " _keyLightMode:" << EntityItemProperties::getComponentModeString(_keyLightMode); - qCDebug(entities) << " _ambientLightMode:" << EntityItemProperties::getComponentModeString(_ambientLightMode); - qCDebug(entities) << " _skyboxMode:" << EntityItemProperties::getComponentModeString(_skyboxMode); - qCDebug(entities) << " _bloomMode:" << EntityItemProperties::getComponentModeString(_bloomMode); + qCDebug(entities) << " _hazeMode:" << EntityItemProperties::getComponentModeAsString(_hazeMode); + qCDebug(entities) << " _keyLightMode:" << EntityItemProperties::getComponentModeAsString(_keyLightMode); + qCDebug(entities) << " _ambientLightMode:" << EntityItemProperties::getComponentModeAsString(_ambientLightMode); + qCDebug(entities) << " _skyboxMode:" << EntityItemProperties::getComponentModeAsString(_skyboxMode); + qCDebug(entities) << " _bloomMode:" << EntityItemProperties::getComponentModeAsString(_bloomMode); _keyLightProperties.debugDump(); _ambientLightProperties.debugDump(); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 18e439aa77..9e3ecdfb39 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -33,7 +33,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return static_cast(EntityVersion::FixedLightSerialization); + return static_cast(EntityVersion::CleanupProperties); case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::ConicalFrustums); case PacketType::AvatarIdentity: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index f3bc115410..800ce7bbe7 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -244,7 +244,8 @@ enum class EntityVersion : PacketVersion { BloomEffect, GrabProperties, ScriptGlmVectors, - FixedLightSerialization + FixedLightSerialization, + CleanupProperties }; enum class EntityScriptCallMethodVersion : PacketVersion { From 3ce6f7f3d647e36e94fb936a57c5241e1185e0a7 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 3 Dec 2018 11:50:59 -0800 Subject: [PATCH 02/27] real image entities --- interface/resources/qml/hifi/AssetServer.qml | 20 +- .../qml/hifi/dialogs/TabletAssetServer.qml | 20 +- interface/src/Application.cpp | 15 +- interface/src/ui/overlays/Image3DOverlay.cpp | 18 +- .../src/EntityTreeRenderer.cpp | 1 + .../src/EntityTreeRenderer.h | 5 + .../src/RenderableEntityItem.cpp | 45 +-- .../src/RenderableGridEntityItem.cpp | 51 ++++ .../src/RenderableGridEntityItem.h | 36 +++ .../src/RenderableImageEntityItem.cpp | 218 ++++++++++++++ .../src/RenderableImageEntityItem.h | 56 ++++ .../src/RenderableModelEntityItem.cpp | 28 -- .../src/RenderableTextEntityItem.cpp | 20 +- .../src/RenderableTextEntityItem.h | 2 +- .../entities/src/EntityItemProperties.cpp | 246 +++++++++++++--- libraries/entities/src/EntityItemProperties.h | 28 +- .../entities/src/EntityItemPropertiesMacros.h | 9 + .../entities/src/EntityPropertyFlags.cpp | 2 +- libraries/entities/src/EntityPropertyFlags.h | 10 +- .../entities/src/EntityPsuedoPropertyFlags.h | 2 + .../entities/src/EntityScriptingInterface.cpp | 4 + libraries/entities/src/EntityTypes.cpp | 38 +-- libraries/entities/src/EntityTypes.h | 66 +++-- libraries/entities/src/GridEntityItem.cpp | 154 ++++++++++ libraries/entities/src/GridEntityItem.h | 56 ++++ libraries/entities/src/ImageEntityItem.cpp | 269 ++++++++++++++++++ libraries/entities/src/ImageEntityItem.h | 86 ++++++ libraries/entities/src/TextEntityItem.cpp | 24 +- libraries/entities/src/TextEntityItem.h | 7 +- .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- libraries/octree/src/OctreePacketData.cpp | 16 ++ libraries/octree/src/OctreePacketData.h | 6 + libraries/shared/src/BillboardMode.cpp | 25 ++ libraries/shared/src/BillboardMode.h | 26 ++ libraries/shared/src/RegisteredMetaTypes.cpp | 29 ++ libraries/shared/src/RegisteredMetaTypes.h | 2 + scripts/system/edit.js | 10 +- scripts/system/html/js/entityList.js | 4 - scripts/system/html/js/entityProperties.js | 28 +- scripts/system/libraries/entityList.js | 4 +- 41 files changed, 1453 insertions(+), 238 deletions(-) create mode 100644 libraries/entities-renderer/src/RenderableGridEntityItem.cpp create mode 100644 libraries/entities-renderer/src/RenderableGridEntityItem.h create mode 100644 libraries/entities-renderer/src/RenderableImageEntityItem.cpp create mode 100644 libraries/entities-renderer/src/RenderableImageEntityItem.h create mode 100644 libraries/entities/src/GridEntityItem.cpp create mode 100644 libraries/entities/src/GridEntityItem.h create mode 100644 libraries/entities/src/ImageEntityItem.cpp create mode 100644 libraries/entities/src/ImageEntityItem.h create mode 100644 libraries/shared/src/BillboardMode.cpp create mode 100644 libraries/shared/src/BillboardMode.h diff --git a/interface/resources/qml/hifi/AssetServer.qml b/interface/resources/qml/hifi/AssetServer.qml index ad337a6361..247a42428a 100644 --- a/interface/resources/qml/hifi/AssetServer.qml +++ b/interface/resources/qml/hifi/AssetServer.qml @@ -189,15 +189,17 @@ Windows.ScrollingWindow { var grabbable = MenuInterface.isOptionChecked("Create Entities As Grabbable (except Zones, Particles, and Lights)"); if (defaultURL.endsWith(".jpg") || defaultURL.endsWith(".png")) { - var name = assetProxyModel.data(treeView.selection.currentIndex); - var modelURL = "https://hifi-content.s3.amazonaws.com/DomainContent/production/default-image-model.fbx"; - var textures = JSON.stringify({ "tex.picture": defaultURL}); - var shapeType = "box"; - var dynamic = false; - var collisionless = true; - var position = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation))); - var gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0); - Entities.addModelEntity(name, modelURL, textures, shapeType, dynamic, collisionless, grabbable, position, gravity); + Entities.addEntity({ + type: "Image", + name: assetProxyModel.data(treeView.selection.currentIndex), + imageURL: defaultURL, + keepAspectRatio: false, + dynamic: false, + collisionless: true, + grabbable: grabbable, + position: Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation))), + gravity: Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0) + }); } else { var SHAPE_TYPE_NONE = 0; var SHAPE_TYPE_SIMPLE_HULL = 1; diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index f665032b01..b5374b2fe0 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -189,15 +189,17 @@ Rectangle { var grabbable = MenuInterface.isOptionChecked("Create Entities As Grabbable (except Zones, Particles, and Lights)"); if (defaultURL.endsWith(".jpg") || defaultURL.endsWith(".png")) { - var name = assetProxyModel.data(treeView.selection.currentIndex); - var modelURL = "https://hifi-content.s3.amazonaws.com/DomainContent/production/default-image-model.fbx"; - var textures = JSON.stringify({ "tex.picture": defaultURL}); - var shapeType = "box"; - var dynamic = false; - var collisionless = true; - var position = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation))); - var gravity = Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0); - Entities.addModelEntity(name, modelURL, textures, shapeType, dynamic, collisionless, grabbable, position, gravity); + Entities.addEntity({ + type: "Image", + name: assetProxyModel.data(treeView.selection.currentIndex), + imageURL: defaultURL, + keepAspectRatio: false, + dynamic: false, + collisionless: true, + grabbable: grabbable, + position: Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation))), + gravity: Vec3.multiply(Vec3.fromPolar(Math.PI / 2, 0), 0) + }); } else { var SHAPE_TYPE_NONE = 0; var SHAPE_TYPE_SIMPLE_HULL = 1; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 21af28ffcb..46b25a8f03 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2367,6 +2367,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo DependencyManager::get()->setPrecisionPicking(rayPickID, value); }); + EntityTreeRenderer::setGetAvatarUpOperator([] { + return DependencyManager::get()->getMyAvatar()->getWorldOrientation() * Vectors::UP; + }); + // Preload Tablet sounds DependencyManager::get()->preloadSounds(); DependencyManager::get()->createKeyboard(); @@ -7623,16 +7627,13 @@ void Application::addAssetToWorldSetMapping(QString filePath, QString mapping, Q void Application::addAssetToWorldAddEntity(QString filePath, QString mapping) { EntityItemProperties properties; - properties.setType(EntityTypes::Model); properties.setName(mapping.right(mapping.length() - 1)); if (filePath.toLower().endsWith(PNG_EXTENSION) || filePath.toLower().endsWith(JPG_EXTENSION)) { - QJsonObject textures { - {"tex.picture", QString("atp:" + mapping) } - }; - properties.setModelURL("https://hifi-content.s3.amazonaws.com/DomainContent/production/default-image-model.fbx"); - properties.setTextures(QJsonDocument(textures).toJson(QJsonDocument::Compact)); - properties.setShapeType(SHAPE_TYPE_BOX); + properties.setType(EntityTypes::Image); + properties.setImageURL(QString("atp:" + mapping)); + properties.setKeepAspectRatio(false); } else { + properties.setType(EntityTypes::Model); properties.setModelURL("atp:" + mapping); properties.setShapeType(SHAPE_TYPE_SIMPLE_COMPOUND); } diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index e24e3b3ed8..d3d0c873a4 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -82,18 +82,21 @@ void Image3DOverlay::render(RenderArgs* args) { float imageHeight = _texture->getHeight(); QRect fromImage; - if (_fromImage.isNull()) { + if (_fromImage.width() <= 0) { fromImage.setX(0); - fromImage.setY(0); fromImage.setWidth(imageWidth); - fromImage.setHeight(imageHeight); } else { float scaleX = imageWidth / _texture->getOriginalWidth(); - float scaleY = imageHeight / _texture->getOriginalHeight(); - fromImage.setX(scaleX * _fromImage.x()); - fromImage.setY(scaleY * _fromImage.y()); fromImage.setWidth(scaleX * _fromImage.width()); + } + + if (_fromImage.height() <= 0) { + fromImage.setY(0); + fromImage.setHeight(imageHeight); + } else { + float scaleY = imageHeight / _texture->getOriginalHeight(); + fromImage.setY(scaleY * _fromImage.y()); fromImage.setHeight(scaleY * _fromImage.height()); } @@ -247,9 +250,6 @@ QVariant Image3DOverlay::getProperty(const QString& property) { if (property == "subImage") { return _fromImage; } - if (property == "offsetPosition") { - return vec3toVariant(getOffsetPosition()); - } if (property == "emissive") { return _emissive; } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 980ff8834c..5fb94dd323 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -41,6 +41,7 @@ #include std::function EntityTreeRenderer::_entitiesShouldFadeFunction = []() { return true; }; +std::function EntityTreeRenderer::_getAvatarUpOperator = []() { return Vectors::UP; }; QString resolveScriptURL(const QString& scriptUrl) { auto normalizedScriptUrl = DependencyManager::get()->normalizeURL(scriptUrl); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 4ba1a0060b..243dfd9813 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -117,6 +117,9 @@ public: // Access the workload Space workload::SpacePointer getWorkloadSpace() const { return _space; } + static void setGetAvatarUpOperator(std::function getAvatarUpOperator) { _getAvatarUpOperator = getAvatarUpOperator; } + static glm::vec3 getAvatarUp() { return _getAvatarUpOperator(); } + signals: void enterEntity(const EntityItemID& entityItemID); void leaveEntity(const EntityItemID& entityItemID); @@ -262,6 +265,8 @@ private: mutable std::mutex _spaceLock; workload::SpacePointer _space{ new workload::Space() }; workload::Transaction::Updates _spaceUpdates; + + static std::function _getAvatarUpOperator; }; diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index aa335bb7d5..92387dafa6 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -25,6 +25,8 @@ #include "RenderableWebEntityItem.h" #include "RenderableZoneEntityItem.h" #include "RenderableMaterialEntityItem.h" +#include "RenderableImageEntityItem.h" +#include "RenderableGridEntityItem.h" using namespace render; @@ -214,22 +216,39 @@ EntityRenderer::Pointer EntityRenderer::addToScene(EntityTreeRenderer& renderer, using Type = EntityTypes::EntityType_t; auto type = entity->getType(); switch (type) { - case Type::Light: - result = make_renderer(entity); - break; - case Type::Line: - result = make_renderer(entity); + case Type::Shape: + case Type::Box: + case Type::Sphere: + result = make_renderer(entity); break; case Type::Model: result = make_renderer(entity); break; + case Type::Text: + result = make_renderer(entity); + break; + + case Type::Image: + result = make_renderer(entity); + break; + + case Type::Web: + if (!nsightActive()) { + result = make_renderer(entity); + } + break; + case Type::ParticleEffect: result = make_renderer(entity); break; + case Type::Line: + result = make_renderer(entity); + break; + case Type::PolyLine: result = make_renderer(entity); break; @@ -238,20 +257,12 @@ EntityRenderer::Pointer EntityRenderer::addToScene(EntityTreeRenderer& renderer, result = make_renderer(entity); break; - case Type::Shape: - case Type::Box: - case Type::Sphere: - result = make_renderer(entity); + case Type::Grid: + result = make_renderer(entity); break; - case Type::Text: - result = make_renderer(entity); - break; - - case Type::Web: - if (!nsightActive()) { - result = make_renderer(entity); - } + case Type::Light: + result = make_renderer(entity); break; case Type::Zone: diff --git a/libraries/entities-renderer/src/RenderableGridEntityItem.cpp b/libraries/entities-renderer/src/RenderableGridEntityItem.cpp new file mode 100644 index 0000000000..bd42a722fb --- /dev/null +++ b/libraries/entities-renderer/src/RenderableGridEntityItem.cpp @@ -0,0 +1,51 @@ +// +// Created by Sam Gondelman on 11/29/18 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "RenderableGridEntityItem.h" + +using namespace render; +using namespace render::entities; + +GridEntityRenderer::GridEntityRenderer(const EntityItemPointer& entity) : Parent(entity) { +} + +bool GridEntityRenderer::needsRenderUpdate() const { + return Parent::needsRenderUpdate(); +} + +bool GridEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { + return false; +} + +void GridEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { + +} + +ItemKey GridEntityRenderer::getKey() { + ItemKey::Builder builder; + builder.withTypeShape().withTypeMeta().withTagBits(getTagMask()); + + withReadLock([&] { + if (isTransparent()) { + builder.withTransparent(); + } else if (_canCastShadow) { + builder.withShadowCaster(); + } + }); + + return builder.build(); +} + +ShapeKey GridEntityRenderer::getShapeKey() { + ShapeKey::Builder builder; + return builder.build(); +} + +void GridEntityRenderer::doRender(RenderArgs* args) { + +} \ No newline at end of file diff --git a/libraries/entities-renderer/src/RenderableGridEntityItem.h b/libraries/entities-renderer/src/RenderableGridEntityItem.h new file mode 100644 index 0000000000..09c0de1d0d --- /dev/null +++ b/libraries/entities-renderer/src/RenderableGridEntityItem.h @@ -0,0 +1,36 @@ +// +// Created by Sam Gondelman on 11/29/18 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_RenderableGridEntityItem_h +#define hifi_RenderableGridEntityItem_h + +#include "RenderableEntityItem.h" + +#include + +namespace render { namespace entities { + +class GridEntityRenderer : public TypedEntityRenderer { + using Parent = TypedEntityRenderer; + using Pointer = std::shared_ptr; +public: + GridEntityRenderer(const EntityItemPointer& entity); + +protected: + ItemKey getKey() override; + ShapeKey getShapeKey() override; + +private: + virtual bool needsRenderUpdate() const override; + virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; + virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; + virtual void doRender(RenderArgs* args) override; +}; + +} } +#endif // hifi_RenderableGridEntityItem_h diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp new file mode 100644 index 0000000000..7c5b7fc0da --- /dev/null +++ b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp @@ -0,0 +1,218 @@ +// +// Created by Sam Gondelman on 11/29/18 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "RenderableImageEntityItem.h" + +#include +#include + +using namespace render; +using namespace render::entities; + +ImageEntityRenderer::ImageEntityRenderer(const EntityItemPointer& entity) : Parent(entity) { + _geometryId = DependencyManager::get()->allocateID(); +} + +ImageEntityRenderer::~ImageEntityRenderer() { + auto geometryCache = DependencyManager::get(); + if (geometryCache) { + geometryCache->releaseID(_geometryId); + } +} + +bool ImageEntityRenderer::isTransparent() const { + return Parent::isTransparent() || (_textureIsLoaded && _texture->getGPUTexture() && _texture->getGPUTexture()->getUsage().isAlpha()) || _alpha < 1.0f; +} + +bool ImageEntityRenderer::needsRenderUpdate() const { + bool textureLoadedChanged = resultWithReadLock([&] { + return (!_textureIsLoaded && _texture && _texture->isLoaded()); + }); + + if (textureLoadedChanged) { + return true; + } + + return Parent::needsRenderUpdate(); +} + +bool ImageEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { + bool needsUpdate = resultWithReadLock([&] { + if (_imageURL != entity->getImageURL()) { + return true; + } + + if (_emissive != entity->getEmissive()) { + return true; + } + + if (_keepAspectRatio != entity->getKeepAspectRatio()) { + return true; + } + + if (_billboardMode != entity->getBillboardMode()) { + return true; + } + + if (_subImage != entity->getSubImage()) { + return true; + } + + if (_color != entity->getColor()) { + return true; + } + + if (_alpha != entity->getAlpha()) { + return true; + } + + return false; + }); + + return needsUpdate; +} + +void ImageEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { + withWriteLock([&] { + auto imageURL = entity->getImageURL(); + if (_imageURL != imageURL) { + _imageURL = imageURL; + if (imageURL.isEmpty()) { + _texture.reset(); + } else { + _texture = DependencyManager::get()->getTexture(_imageURL); + } + _textureIsLoaded = false; + } + + _emissive = entity->getEmissive(); + _keepAspectRatio = entity->getKeepAspectRatio(); + _billboardMode = entity->getBillboardMode(); + _subImage = entity->getSubImage(); + + _color = entity->getColor(); + _alpha = entity->getAlpha(); + + if (!_textureIsLoaded && _texture && _texture->isLoaded()) { + _textureIsLoaded = true; + } + }); + + void* key = (void*)this; + AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity]() { + withWriteLock([&] { + _dimensions = entity->getScaledDimensions(); + updateModelTransformAndBound(); + _renderTransform = getModelTransform(); + }); + }); +} + +ShapeKey ImageEntityRenderer::getShapeKey() { + auto builder = render::ShapeKey::Builder().withoutCullFace().withDepthBias(); + if (isTransparent()) { + builder.withTranslucent(); + } + + withReadLock([&] { + if (_emissive) { + builder.withUnlit(); + } + }); + + return builder.build(); +} + +void ImageEntityRenderer::doRender(RenderArgs* args) { + NetworkTexturePointer texture; + QRect subImage; + glm::u8vec3 color; + glm::vec3 dimensions; + Transform transform; + withReadLock([&] { + texture = _texture; + subImage = _subImage; + color = _color; + dimensions = _dimensions; + transform = _renderTransform; + }); + + if (!_visible || !texture || !texture->isLoaded()) { + return; + } + + Q_ASSERT(args->_batch); + gpu::Batch* batch = args->_batch; + + if (_billboardMode == BillboardMode::YAW) { + //rotate about vertical to face the camera + glm::vec3 dPosition = args->getViewFrustum().getPosition() - transform.getTranslation(); + // If x and z are 0, atan(x, z) is undefined, so default to 0 degrees + float yawRotation = dPosition.x == 0.0f && dPosition.z == 0.0f ? 0.0f : glm::atan(dPosition.x, dPosition.z); + glm::quat orientation = glm::quat(glm::vec3(0.0f, yawRotation, 0.0f)); + transform.setRotation(orientation); + } else if (_billboardMode == BillboardMode::FULL) { + glm::vec3 billboardPos = transform.getTranslation(); + glm::vec3 cameraPos = args->getViewFrustum().getPosition(); + // use the referencial from the avatar, y isn't always up + glm::vec3 avatarUP = EntityTreeRenderer::getAvatarUp(); + // check to see if glm::lookAt will work / using glm::lookAt variable name + glm::highp_vec3 s(glm::cross(billboardPos - cameraPos, avatarUP)); + + // make sure s is not NaN for any component + if (glm::length2(s) > 0.0f) { + glm::quat rotation(conjugate(toQuat(glm::lookAt(cameraPos, billboardPos, avatarUP)))); + transform.setRotation(rotation); + } + } + transform.postScale(dimensions); + + batch->setModelTransform(transform); + batch->setResourceTexture(0, texture->getGPUTexture()); + + float imageWidth = texture->getWidth(); + float imageHeight = texture->getHeight(); + + QRect fromImage; + if (subImage.width() <= 0) { + fromImage.setX(0); + fromImage.setWidth(imageWidth); + } else { + float scaleX = imageWidth / texture->getOriginalWidth(); + fromImage.setX(scaleX * subImage.x()); + fromImage.setWidth(scaleX * subImage.width()); + } + + if (subImage.height() <= 0) { + fromImage.setY(0); + fromImage.setHeight(imageHeight); + } else { + float scaleY = imageHeight / texture->getOriginalHeight(); + fromImage.setY(scaleY * subImage.y()); + fromImage.setHeight(scaleY * subImage.height()); + } + + float maxSize = glm::max(fromImage.width(), fromImage.height()); + float x = _keepAspectRatio ? fromImage.width() / (2.0f * maxSize) : 0.5f; + float y = _keepAspectRatio ? -fromImage.height() / (2.0f * maxSize) : -0.5f; + + glm::vec2 topLeft(-x, -y); + glm::vec2 bottomRight(x, y); + glm::vec2 texCoordTopLeft((fromImage.x() + 0.5f) / imageWidth, (fromImage.y() + 0.5f) / imageHeight); + glm::vec2 texCoordBottomRight((fromImage.x() + fromImage.width() - 0.5f) / imageWidth, + (fromImage.y() + fromImage.height() - 0.5f) / imageHeight); + + glm::vec4 imageColor(toGlm(color), _alpha); + + DependencyManager::get()->renderQuad( + *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, + imageColor, _geometryId + ); + + batch->setResourceTexture(0, nullptr); +} \ No newline at end of file diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.h b/libraries/entities-renderer/src/RenderableImageEntityItem.h new file mode 100644 index 0000000000..669db13a22 --- /dev/null +++ b/libraries/entities-renderer/src/RenderableImageEntityItem.h @@ -0,0 +1,56 @@ +// +// Created by Sam Gondelman on 11/29/18 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_RenderableImageEntityItem_h +#define hifi_RenderableImageEntityItem_h + +#include "RenderableEntityItem.h" + +#include + +namespace render { + namespace entities { + + class ImageEntityRenderer : public TypedEntityRenderer { + using Parent = TypedEntityRenderer; + using Pointer = std::shared_ptr; + public: + ImageEntityRenderer(const EntityItemPointer& entity); + ~ImageEntityRenderer(); + + protected: + ShapeKey getShapeKey() override; + + bool isTransparent() const override; + + private: + virtual bool needsRenderUpdate() const override; + virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; + virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; + virtual void doRender(RenderArgs* args) override; + + QString _imageURL; + NetworkTexturePointer _texture; + bool _textureIsLoaded { false }; + + bool _emissive; + bool _keepAspectRatio; + BillboardMode _billboardMode; + QRect _subImage; + + glm::u8vec3 _color; + float _alpha; + + glm::vec3 _dimensions; + + int _geometryId { 0 }; + }; + + } +} +#endif // hifi_RenderableImageEntityItem_h diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 2b1d70f4d0..62aaf6f3a8 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -83,34 +83,6 @@ void RenderableModelEntityItem::setUnscaledDimensions(const glm::vec3& value) { } } -QVariantMap parseTexturesToMap(QString textures, const QVariantMap& defaultTextures) { - // If textures are unset, revert to original textures - if (textures.isEmpty()) { - return defaultTextures; - } - - // Legacy: a ,\n-delimited list of filename:"texturepath" - if (*textures.cbegin() != '{') { - textures = "{\"" + textures.replace(":\"", "\":\"").replace(",\n", ",\"") + "}"; - } - - QJsonParseError error; - QJsonDocument texturesJson = QJsonDocument::fromJson(textures.toUtf8(), &error); - // If textures are invalid, revert to original textures - if (error.error != QJsonParseError::NoError) { - qCWarning(entitiesrenderer) << "Could not evaluate textures property value:" << textures; - return defaultTextures; - } - - QVariantMap texturesMap = texturesJson.toVariant().toMap(); - // If textures are unset, revert to original textures - if (texturesMap.isEmpty()) { - return defaultTextures; - } - - return texturesJson.toVariant().toMap(); -} - void RenderableModelEntityItem::doInitialModelSimulation() { DETAILED_PROFILE_RANGE(simulation_physics, __FUNCTION__); ModelPointer model = getModel(); diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index ce9e7ab764..6e281081d2 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include "GLMHelpers.h" @@ -59,7 +58,7 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint return true; } - if (_faceCamera != entity->getFaceCamera()) { + if (_billboardMode != entity->getBillboardMode()) { return true; } return false; @@ -79,7 +78,7 @@ void TextEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen void TextEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { _textColor = toGlm(entity->getTextColor()); _backgroundColor = toGlm(entity->getBackgroundColor()); - _faceCamera = entity->getFaceCamera(); + _billboardMode = entity->getBillboardMode(); _lineHeight = entity->getLineHeight(); _text = entity->getText(); } @@ -110,13 +109,26 @@ void TextEntityRenderer::doRender(RenderArgs* args) { gpu::Batch& batch = *args->_batch; auto transformToTopLeft = modelTransform; - if (_faceCamera) { + if (_billboardMode == BillboardMode::YAW) { //rotate about vertical to face the camera glm::vec3 dPosition = args->getViewFrustum().getPosition() - modelTransform.getTranslation(); // If x and z are 0, atan(x, z) is undefined, so default to 0 degrees float yawRotation = dPosition.x == 0.0f && dPosition.z == 0.0f ? 0.0f : glm::atan(dPosition.x, dPosition.z); glm::quat orientation = glm::quat(glm::vec3(0.0f, yawRotation, 0.0f)); transformToTopLeft.setRotation(orientation); + } else if (_billboardMode == BillboardMode::FULL) { + glm::vec3 billboardPos = transformToTopLeft.getTranslation(); + glm::vec3 cameraPos = args->getViewFrustum().getPosition(); + // use the referencial from the avatar, y isn't always up + glm::vec3 avatarUP = EntityTreeRenderer::getAvatarUp(); + // check to see if glm::lookAt will work / using glm::lookAt variable name + glm::highp_vec3 s(glm::cross(billboardPos - cameraPos, avatarUP)); + + // make sure s is not NaN for any component + if (glm::length2(s) > 0.0f) { + glm::quat rotation(conjugate(toQuat(glm::lookAt(cameraPos, billboardPos, avatarUP)))); + transformToTopLeft.setRotation(rotation); + } } transformToTopLeft.postTranslate(dimensions * glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h index ac7f2b620f..a368f280c5 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.h +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h @@ -32,7 +32,7 @@ private: virtual void doRender(RenderArgs* args) override; int _geometryID{ 0 }; std::shared_ptr _textRenderer; - bool _faceCamera; + BillboardMode _billboardMode; glm::vec3 _dimensions; glm::vec3 _textColor; glm::vec3 _backgroundColor; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 8970aed01a..f24b14d3b7 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -297,6 +297,33 @@ void EntityItemProperties::setMaterialMappingModeFromString(const QString& mater } } +QHash stringToBillboardModeLookup; + +void addBillboardMode(BillboardMode mode) { + stringToBillboardModeLookup[BillboardModeHelpers::getNameForBillboardMode(mode)] = mode; +} + +void buildStringToBillboardModeLookup() { + addBillboardMode(BillboardMode::NONE); + addBillboardMode(BillboardMode::YAW); + addBillboardMode(BillboardMode::FULL); +} + +QString EntityItemProperties::getBillboardModeAsString() const { + return BillboardModeHelpers::getNameForBillboardMode(_billboardMode); +} + +void EntityItemProperties::setBillboardModeFromString(const QString& materialMappingMode) { + if (stringToBillboardModeLookup.empty()) { + buildStringToBillboardModeLookup(); + } + auto billboardModeItr = stringToBillboardModeLookup.find(materialMappingMode.toLower()); + if (billboardModeItr != stringToBillboardModeLookup.end()) { + _billboardMode = billboardModeItr.value(); + _billboardModeChanged = true; + } +} + EntityPropertyFlags EntityItemProperties::getChangedProperties() const { EntityPropertyFlags changedProperties; @@ -383,6 +410,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_SPIN_FINISH, spinFinish); CHECK_PROPERTY_CHANGE(PROP_PARTICLE_ROTATE_WITH_ENTITY, rotateWithEntity); + CHECK_PROPERTY_CHANGE(PROP_IMAGE_URL, imageURL); + CHECK_PROPERTY_CHANGE(PROP_EMISSIVE, emissive); + CHECK_PROPERTY_CHANGE(PROP_KEEP_ASPECT_RATIO, keepAspectRatio); + CHECK_PROPERTY_CHANGE(PROP_SUB_IMAGE, subImage); + // Certifiable Properties CHECK_PROPERTY_CHANGE(PROP_ITEM_NAME, itemName); CHECK_PROPERTY_CHANGE(PROP_ITEM_DESCRIPTION, itemDescription); @@ -412,7 +444,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_LINE_POINTS, linePoints); CHECK_PROPERTY_CHANGE(PROP_HREF, href); CHECK_PROPERTY_CHANGE(PROP_DESCRIPTION, description); - CHECK_PROPERTY_CHANGE(PROP_FACE_CAMERA, faceCamera); + CHECK_PROPERTY_CHANGE(PROP_BILLBOARD_MODE, billboardMode); CHECK_PROPERTY_CHANGE(PROP_ACTION_DATA, actionData); CHECK_PROPERTY_CHANGE(PROP_NORMALS, normals); CHECK_PROPERTY_CHANGE(PROP_STROKE_COLORS, strokeColors); @@ -642,18 +674,20 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * * @see The different entity types have additional properties as follows: * @see {@link Entities.EntityProperties-Box|EntityProperties-Box} - * @see {@link Entities.EntityProperties-Light|EntityProperties-Light} - * @see {@link Entities.EntityProperties-Line|EntityProperties-Line} - * @see {@link Entities.EntityProperties-Material|EntityProperties-Material} + * @see {@link Entities.EntityProperties-Sphere|EntityProperties-Sphere} + * @see {@link Entities.EntityProperties-Shape|EntityProperties-Shape} * @see {@link Entities.EntityProperties-Model|EntityProperties-Model} + * @see {@link Entities.EntityProperties-Text|EntityProperties-Text} + * @see {@link Entities.EntityProperties-Image|EntityProperties-Image} + * @see {@link Entities.EntityProperties-Web|EntityProperties-Web} * @see {@link Entities.EntityProperties-ParticleEffect|EntityProperties-ParticleEffect} + * @see {@link Entities.EntityProperties-Line|EntityProperties-Line} * @see {@link Entities.EntityProperties-PolyLine|EntityProperties-PolyLine} * @see {@link Entities.EntityProperties-PolyVox|EntityProperties-PolyVox} - * @see {@link Entities.EntityProperties-Shape|EntityProperties-Shape} - * @see {@link Entities.EntityProperties-Sphere|EntityProperties-Sphere} - * @see {@link Entities.EntityProperties-Text|EntityProperties-Text} - * @see {@link Entities.EntityProperties-Web|EntityProperties-Web} + * @see {@link Entities.EntityProperties-Grid|EntityProperties-Grid} + * @see {@link Entities.EntityProperties-Light|EntityProperties-Light} * @see {@link Entities.EntityProperties-Zone|EntityProperties-Zone} + * @see {@link Entities.EntityProperties-Material|EntityProperties-Material} */ /**jsdoc @@ -786,8 +820,6 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * {@link Entities.EntityProperties|naturalDimensions}. * @property {Color} color=255,255,255 - Currently not used. * @property {string} modelURL="" - The URL of the FBX of OBJ model. Baked FBX models' URLs end in ".baked.fbx".
- * Note: If the name ends with "default-image-model.fbx" then the entity is considered to be an "Image" - * entity, in which case the textures property should be set per the example. * @property {string} textures="" - A JSON string of texture name, URL pairs used when rendering the model in place of the * model's original textures. Use a texture name from the originalTextures property to override that texture. * Only the texture names and URLs to be overridden need be specified; original textures are used where there are no @@ -833,24 +865,6 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * dimensions: { x: 0.0945, y: 0.0921, z: 0.0423 }, * lifetime: 300 // Delete after 5 minutes. * }); - * @example Create an "Image" entity like you can in the Create app. - * var IMAGE_MODEL = "https://hifi-content.s3.amazonaws.com/DomainContent/production/default-image-model.fbx"; - * var DEFAULT_IMAGE = "https://hifi-content.s3.amazonaws.com/DomainContent/production/no-image.jpg"; - * var entity = Entities.addEntity({ - * type: "Model", - * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -3 })), - * rotation: MyAvatar.orientation, - * dimensions: { - * x: 0.5385, - * y: 0.2819, - * z: 0.0092 - * }, - * shapeType: "box", - * collisionless: true, - * modelURL: IMAGE_MODEL, - * textures: JSON.stringify({ "tex.picture": DEFAULT_IMAGE }), - * lifetime: 300 // Delete after 5 minutes - * }); */ /**jsdoc @@ -1091,8 +1105,10 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {number} lineHeight=0.1 - The height of each line of text (thus determining the font size). * @property {Color} textColor=255,255,255 - The color of the text. * @property {Color} backgroundColor=0,0,0 - The color of the background rectangle. - * @property {boolean} faceCamera=false - If true, the entity is oriented to face each user's camera (i.e., it - * differs for each user present). + * @property {BillboardMode} billboardMode="none" - If "none", the entity is not billboarded. If "yaw", the entity will be + * oriented to follow your camera around the y-axis. If "full" the entity will be oriented to face your camera. The following deprecated + * behavior is also supported: you can also set "faceCamera" to true to set billboardMode to "yaw", and you can set + * "isFacingAvatar" to true to set billboardMode to "full". Setting either to false sets the mode to "none" * @example Create a text entity. * var text = Entities.addEntity({ * type: "Text", @@ -1100,7 +1116,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * dimensions: { x: 0.6, y: 0.3, z: 0.01 }, * lineHeight: 0.12, * text: "Hello\nthere!", - * faceCamera: true, + * billboardMode: "yaw", * lifetime: 300 // Delete after 5 minutes. * }); */ @@ -1211,6 +1227,32 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * }); */ +/**jsdoc + * The "Image" {@link Entities.EntityType|EntityType} displays an image on a 2D rectangle in the domain. + * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * @typedef {object} Entities.EntityProperties-Image + * @property {string} imageURL="" - The URL of the image to use. + * @property {boolean} emissive=false - Whether or not the image should be emissive (unlit). + * @property {boolean} keepAspectRatio=true - Whether or not the image should maintain its aspect ratio. + * @property {BillboardMode} billboardMode="none" - If "none", the entity is not billboarded. If "yaw", the entity will be + * oriented to follow your camera around the y-axis. If "full" the entity will be oriented to face your camera. The following deprecated + * behavior is also supported: you can also set "faceCamera" to true to set billboardMode to "yaw", and you can set + * "isFacingAvatar" to true to set billboardMode to "full". Setting either to false sets the mode to "none" + * @property {Rect} subImage={ x: 0, y: 0, width: -1, height: -1 } - The portion of the image to display. If width or height are -1, defaults to + * the full image in that dimension. + * @property {Color} color=255,255,255 - The color of image. + * @property {number} alpha=1 - The alpha of the image. + * @example Create a image entity. + * var image = Entities.addEntity({ + * type: "Image", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * dimensions: { x: 0.6, y: 0.3, z: 0.01 }, + * imageURL: "https://images.pexels.com/photos/1020315/pexels-photo-1020315.jpeg", + * billboardMode: "yaw", + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool skipDefaults, bool allowUnknownCreateTime, bool strictSemantics, EntityPsuedoPropertyFlags psueudoPropertyFlags) const { @@ -1401,7 +1443,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_HEIGHT, lineHeight); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_TYPED(PROP_TEXT_COLOR, textColor, getTextColor(), u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_TYPED(PROP_BACKGROUND_COLOR, backgroundColor, getBackgroundColor(), u8vec3Color); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FACE_CAMERA, faceCamera); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BILLBOARD_MODE, billboardMode, getBillboardModeAsString()); } // Zones only @@ -1482,6 +1524,26 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_DATA, materialData); } + // Image only + if (_type == EntityTypes::Image) { + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IMAGE_URL, imageURL); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMISSIVE, emissive); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_KEEP_ASPECT_RATIO, keepAspectRatio); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BILLBOARD_MODE, billboardMode, getBillboardModeAsString()); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SUB_IMAGE, subImage); + + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha); + + // Handle conversions to old 'textures' property from "imageURL" + if (((!psuedoPropertyFlagsButDesiredEmpty && _desiredProperties.isEmpty()) || _desiredProperties.getHasProperty(PROP_IMAGE_URL)) && + (!skipDefaults || defaultEntityProperties._imageURL != _imageURL)) { + QScriptValue textures = engine->newObject(); + textures.setProperty("tex.picture", _imageURL); + properties.setProperty("textures", textures); + } + } + /**jsdoc * The axis-aligned bounding box of an entity. * @typedef {object} Entities.BoundingBox @@ -1570,6 +1632,13 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool properties.setProperty("owningAvatarID", convertScriptValue(engine, getOwningAvatarID())); } + if (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::FaceCamera)) { + properties.setProperty("faceCamera", convertScriptValue(engine, getBillboardMode() == BillboardMode::YAW)); + } + if (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::IsFacingAvatar)) { + properties.setProperty("isFacingAvatar", convertScriptValue(engine, getBillboardMode() == BillboardMode::FULL)); + } + // FIXME - I don't think these properties are supported any more //COPY_PROPERTY_TO_QSCRIPTVALUE(localRenderAlpha); @@ -1698,13 +1767,17 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(linePoints, qVectorVec3, setLinePoints); COPY_PROPERTY_FROM_QSCRIPTVALUE(href, QString, setHref); COPY_PROPERTY_FROM_QSCRIPTVALUE(description, QString, setDescription); - COPY_PROPERTY_FROM_QSCRIPTVALUE(faceCamera, bool, setFaceCamera); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(billboardMode, BillboardMode); COPY_PROPERTY_FROM_QSCRIPTVALUE(actionData, QByteArray, setActionData); COPY_PROPERTY_FROM_QSCRIPTVALUE(normals, qVectorVec3, setNormals); COPY_PROPERTY_FROM_QSCRIPTVALUE(strokeColors, qVectorVec3, setStrokeColors); COPY_PROPERTY_FROM_QSCRIPTVALUE(strokeWidths, qVectorFloat, setStrokeWidths); COPY_PROPERTY_FROM_QSCRIPTVALUE(isUVModeStretch, bool, setIsUVModeStretch); + COPY_PROPERTY_FROM_QSCRIPTVALUE(imageURL, QString, setImageURL); + COPY_PROPERTY_FROM_QSCRIPTVALUE(emissive, bool, setEmissive); + COPY_PROPERTY_FROM_QSCRIPTVALUE(keepAspectRatio, bool, setKeepAspectRatio); + COPY_PROPERTY_FROM_QSCRIPTVALUE(subImage, QRect, setSubImage); if (!honorReadOnly) { // this is used by the json reader to set things that we don't want javascript to able to affect. @@ -1768,6 +1841,47 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneAvatarEntity, bool, setCloneAvatarEntity); COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneOriginID, QUuid, setCloneOriginID); + // Handle conversions from old 'textures' property to "imageURL" + { + QScriptValue V = object.property("textures"); + if (V.isValid()) { + bool isValid = false; + QString textures = QString_convertFromScriptValue(V, isValid); + if (isValid) { + QVariantMap texturesMap = parseTexturesToMap(textures, QVariantMap()); + auto texPicture = texturesMap.find("tex.picture"); + if (texPicture != texturesMap.end()) { + auto imageURL = texPicture.value().toString(); + if (_defaultSettings || imageURL != _imageURL) { + setImageURL(imageURL); + } + } + } + } + } + + // Handle old "faceCamera" and "isFacingAvatar" props + { + QScriptValue P = object.property("faceCamera"); + if (P.isValid()) { + bool newValue = P.toVariant().toBool(); + bool oldValue = getBillboardMode() == BillboardMode::YAW; + if (_defaultSettings || newValue != oldValue) { + setBillboardMode(newValue ? BillboardMode::YAW : BillboardMode::NONE); + } + } + } + { + QScriptValue P = object.property("isFacingAvatar"); + if (P.isValid()) { + bool newValue = P.toVariant().toBool(); + bool oldValue = getBillboardMode() == BillboardMode::FULL; + if (_defaultSettings || newValue != oldValue) { + setBillboardMode(newValue ? BillboardMode::FULL : BillboardMode::NONE); + } + } + } + _lastEdited = usecTimestampNow(); } @@ -1844,6 +1958,11 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(spinFinish); COPY_PROPERTY_IF_CHANGED(rotateWithEntity); + COPY_PROPERTY_IF_CHANGED(imageURL); + COPY_PROPERTY_IF_CHANGED(emissive); + COPY_PROPERTY_IF_CHANGED(keepAspectRatio); + COPY_PROPERTY_IF_CHANGED(subImage); + // Certifiable Properties COPY_PROPERTY_IF_CHANGED(itemName); COPY_PROPERTY_IF_CHANGED(itemDescription); @@ -1874,7 +1993,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(linePoints); COPY_PROPERTY_IF_CHANGED(href); COPY_PROPERTY_IF_CHANGED(description); - COPY_PROPERTY_IF_CHANGED(faceCamera); + COPY_PROPERTY_IF_CHANGED(billboardMode); COPY_PROPERTY_IF_CHANGED(actionData); COPY_PROPERTY_IF_CHANGED(normals); COPY_PROPERTY_IF_CHANGED(strokeColors); @@ -2092,7 +2211,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_LINE_POINTS, LinePoints, linePoints, QVector); ADD_PROPERTY_TO_MAP(PROP_HREF, Href, href, QString); ADD_PROPERTY_TO_MAP(PROP_DESCRIPTION, Description, description, QString); - ADD_PROPERTY_TO_MAP(PROP_FACE_CAMERA, FaceCamera, faceCamera, bool); + ADD_PROPERTY_TO_MAP(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode); ADD_PROPERTY_TO_MAP(PROP_ACTION_DATA, ActionData, actionData, QByteArray); ADD_PROPERTY_TO_MAP(PROP_NORMALS, Normals, normals, QVector); ADD_PROPERTY_TO_MAP(PROP_STROKE_COLORS, StrokeColors, strokeColors, QVector); @@ -2198,6 +2317,11 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, Grab, grab, EquippableIndicatorOffset, equippableIndicatorOffset); + ADD_PROPERTY_TO_MAP(PROP_IMAGE_URL, ImageURL, imageURL, QString); + ADD_PROPERTY_TO_MAP(PROP_EMISSIVE, Emissive, emissive, bool); + ADD_PROPERTY_TO_MAP(PROP_KEEP_ASPECT_RATIO, KeepAspectRatio, keepAspectRatio, bool); + ADD_PROPERTY_TO_MAP(PROP_SUB_IMAGE, SubImage, subImage, QRect); + // FIXME - these are not yet handled //ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64); @@ -2361,7 +2485,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, properties.getLineHeight()); APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, properties.getTextColor()); APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, properties.getBackgroundColor()); - APPEND_ENTITY_PROPERTY(PROP_FACE_CAMERA, properties.getFaceCamera()); + APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)properties.getBillboardMode()); } if (properties.getType() == EntityTypes::Model) { @@ -2519,6 +2643,18 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_MATERIAL_DATA, properties.getMaterialData()); } + // Image + if (properties.getType() == EntityTypes::Image) { + APPEND_ENTITY_PROPERTY(PROP_IMAGE_URL, properties.getImageURL()); + APPEND_ENTITY_PROPERTY(PROP_EMISSIVE, properties.getEmissive()); + APPEND_ENTITY_PROPERTY(PROP_KEEP_ASPECT_RATIO, properties.getKeepAspectRatio()); + APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)properties.getBillboardMode()); + APPEND_ENTITY_PROPERTY(PROP_SUB_IMAGE, properties.getSubImage()); + + APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); + APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha()); + } + APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName()); APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, properties.getCollisionSoundURL()); APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, properties.getActionData()); @@ -2764,7 +2900,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_HEIGHT, float, setLineHeight); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_COLOR, u8vec3Color, setTextColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BACKGROUND_COLOR, u8vec3Color, setBackgroundColor); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FACE_CAMERA, bool, setFaceCamera); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); } if (properties.getType() == EntityTypes::Model) { @@ -2914,6 +3050,18 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MATERIAL_DATA, QString, setMaterialData); } + // Image + if (properties.getType() == EntityTypes::Image) { + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IMAGE_URL, QString, setImageURL); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMISSIVE, bool, setEmissive); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEEP_ASPECT_RATIO, bool, setKeepAspectRatio); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SUB_IMAGE, QRect, setSubImage); + + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha); + } + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACTION_DATA, QByteArray, setActionData); @@ -3190,7 +3338,7 @@ void EntityItemProperties::markAllChanged() { _hrefChanged = true; _descriptionChanged = true; - _faceCameraChanged = true; + _billboardModeChanged = true; _actionDataChanged = true; _normalsChanged = true; @@ -3240,6 +3388,11 @@ void EntityItemProperties::markAllChanged() { _cloneOriginIDChanged = true; _isVisibleInSecondaryCameraChanged = true; + + _imageURLChanged = true; + _emissiveChanged = true; + _keepAspectRatioChanged = true; + _subImageChanged = true; } // The minimum bounding box for the entity. @@ -3776,6 +3929,23 @@ QList EntityItemProperties::listChangedProperties() { out += "cloneOriginID"; } + if (imageURLChanged()) { + out += "imageURL"; + } + if (emissiveChanged()) { + out += "emissive"; + } + if (keepAspectRatioChanged()) { + out += "keepAspectRatio"; + } + if (subImageChanged()) { + out += "subImage"; + } + + if (billboardModeChanged()) { + out += "billboardMode"; + } + getAnimation().listChangedProperties(out); getKeyLight().listChangedProperties(out); getAmbientLight().listChangedProperties(out); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 4d45f280e2..a34af703fb 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -48,6 +48,7 @@ #include "ZoneEntityItem.h" #include "MaterialMappingMode.h" +#include "BillboardMode.h" const quint64 UNKNOWN_CREATED_TIME = 0; @@ -68,18 +69,20 @@ using u8vec3Color = glm::u8vec3; class EntityItemProperties { // TODO: consider removing these friend relationship and use public methods friend class EntityItem; - friend class ModelEntityItem; friend class BoxEntityItem; friend class SphereEntityItem; - friend class LightEntityItem; - friend class TextEntityItem; - friend class ParticleEffectEntityItem; - friend class ZoneEntityItem; - friend class WebEntityItem; - friend class LineEntityItem; - friend class PolyVoxEntityItem; - friend class PolyLineEntityItem; friend class ShapeEntityItem; + friend class ModelEntityItem; + friend class TextEntityItem; + friend class ImageEntityItem; + friend class WebEntityItem; + friend class ParticleEffectEntityItem; + friend class LineEntityItem; + friend class PolyLineEntityItem; + friend class PolyVoxEntityItem; + friend class GridEntityItem; + friend class LightEntityItem; + friend class ZoneEntityItem; friend class MaterialEntityItem; public: EntityItemProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()); @@ -213,7 +216,7 @@ public: DEFINE_PROPERTY_REF(LINE_POINTS, LinePoints, linePoints, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString, ""); DEFINE_PROPERTY_REF(PROP_DESCRIPTION, Description, description, QString, ""); - DEFINE_PROPERTY(PROP_FACE_CAMERA, FaceCamera, faceCamera, bool, TextEntityItem::DEFAULT_FACE_CAMERA); + DEFINE_PROPERTY_REF_ENUM(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode, BillboardMode::NONE); DEFINE_PROPERTY_REF(PROP_ACTION_DATA, ActionData, actionData, QByteArray, QByteArray()); DEFINE_PROPERTY(PROP_NORMALS, Normals, normals, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); DEFINE_PROPERTY(PROP_STROKE_COLORS, StrokeColors, strokeColors, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); @@ -250,6 +253,11 @@ public: DEFINE_PROPERTY(PROP_SPIN_FINISH, SpinFinish, spinFinish, float, particle::DEFAULT_SPIN_FINISH); DEFINE_PROPERTY(PROP_PARTICLE_ROTATE_WITH_ENTITY, RotateWithEntity, rotateWithEntity, bool, particle::DEFAULT_ROTATE_WITH_ENTITY); + DEFINE_PROPERTY_REF(PROP_IMAGE_URL, ImageURL, imageURL, QString, ""); + DEFINE_PROPERTY_REF(PROP_EMISSIVE, Emissive, emissive, bool, false); + DEFINE_PROPERTY_REF(PROP_KEEP_ASPECT_RATIO, KeepAspectRatio, keepAspectRatio, bool, true); + DEFINE_PROPERTY_REF(PROP_SUB_IMAGE, SubImage, subImage, QRect, QRect()); + // Certifiable Properties - related to Proof of Purchase certificates DEFINE_PROPERTY_REF(PROP_ITEM_NAME, ItemName, itemName, QString, ENTITY_ITEM_DEFAULT_ITEM_NAME); DEFINE_PROPERTY_REF(PROP_ITEM_DESCRIPTION, ItemDescription, itemDescription, QString, ENTITY_ITEM_DEFAULT_ITEM_DESCRIPTION); diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index d3a2dc6cec..225b77bd97 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -122,6 +122,8 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector& v) {return qVectorBoolToScriptValue(e, v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector& v) { return qVectorFloatToScriptValue(e, v); } +inline QScriptValue convertScriptValue(QScriptEngine* e, const QRect& v) { return qRectToScriptValue(e, v); } + inline QScriptValue convertScriptValue(QScriptEngine* e, const QByteArray& v) { QByteArray b64 = v.toBase64(); return QScriptValue(QString(b64)); @@ -323,6 +325,13 @@ inline glm::quat quat_convertFromScriptValue(const QScriptValue& v, bool& isVali return glm::quat(); } +inline QRect QRect_convertFromScriptValue(const QScriptValue& v, bool& isValid) { + isValid = true; + QRect rect; + qRectFromScriptValue(v, rect); + return rect; +} + #define COPY_PROPERTY_IF_CHANGED(P) \ { \ if (other._##P##Changed) { \ diff --git a/libraries/entities/src/EntityPropertyFlags.cpp b/libraries/entities/src/EntityPropertyFlags.cpp index c077b153b8..d4aeac3a78 100644 --- a/libraries/entities/src/EntityPropertyFlags.cpp +++ b/libraries/entities/src/EntityPropertyFlags.cpp @@ -67,7 +67,7 @@ QDebug& operator<<(QDebug& dbg, const EntityPropertyFlags& f) { result = f.getHasProperty(PROP_LINE_POINTS) ? result + "linePoints " : result; result = f.getHasProperty(PROP_HREF) ? result + "href " : result; result = f.getHasProperty(PROP_DESCRIPTION) ? result + "description " : result; - result = f.getHasProperty(PROP_FACE_CAMERA) ? result + "faceCamera " : result; + result = f.getHasProperty(PROP_BILLBOARD_MODE) ? result + "billboardMode " : result; result = f.getHasProperty(PROP_SCRIPT_TIMESTAMP) ? result + "scriptTimestamp " : result; result = f.getHasProperty(PROP_ACTION_DATA) ? result + "actionData " : result; result = f.getHasProperty(PROP_X_TEXTURE_URL) ? result + "xTextureUrl " : result; diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index d2f687fbd3..803d391e11 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -99,7 +99,7 @@ enum EntityPropertyList { PROP_HREF, PROP_DESCRIPTION, // 61 - PROP_FACE_CAMERA, + PROP_BILLBOARD_MODE, PROP_SCRIPT_TIMESTAMP, PROP_ACTION_DATA, @@ -275,6 +275,9 @@ enum EntityPropertyList { PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, + PROP_EMISSIVE, + PROP_SUB_IMAGE, + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line PROP_AFTER_LAST_ITEM, @@ -308,6 +311,11 @@ enum EntityPropertyList { // other properties which will never overlap with each other. PROP_EMITTING_PARTICLES = PROP_ANIMATION_PLAYING, + // Aliases/Piggyback properties for Image. These properties intentionally reuse the enum values for + // other properties which will never overlap with each other. + PROP_IMAGE_URL = PROP_MODEL_URL, + PROP_KEEP_ASPECT_RATIO = PROP_ANIMATION_PLAYING, + // WARNING!!! DO NOT ADD PROPS_xxx here unless you really really meant to.... Add them UP above }; diff --git a/libraries/entities/src/EntityPsuedoPropertyFlags.h b/libraries/entities/src/EntityPsuedoPropertyFlags.h index 0b051a4c74..cb5f4ebfba 100644 --- a/libraries/entities/src/EntityPsuedoPropertyFlags.h +++ b/libraries/entities/src/EntityPsuedoPropertyFlags.h @@ -32,6 +32,8 @@ namespace EntityPsuedoPropertyFlag { RenderInfo, ClientOnly, OwningAvatarID, + FaceCamera, + IsFacingAvatar, NumFlags }; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 3d68a27bc9..8ad1efd36e 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -681,6 +681,10 @@ QScriptValue EntityScriptingInterface::getMultipleEntityPropertiesInternal(QScri psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::ClientOnly); } else if (extendedPropertyString == "owningAvatarID") { psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::OwningAvatarID); + } else if (extendedPropertyString == "faceCamera") { + psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::FaceCamera); + } else if (extendedPropertyString == "isFacingAvatar") { + psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::IsFacingAvatar); } }; diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index 9611063f8b..e511af83b0 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -20,38 +20,42 @@ #include "EntityItemProperties.h" #include "EntitiesLogging.h" -#include "LightEntityItem.h" +#include "ShapeEntityItem.h" #include "ModelEntityItem.h" #include "ParticleEffectEntityItem.h" #include "TextEntityItem.h" +#include "ImageEntityItem.h" #include "WebEntityItem.h" -#include "ZoneEntityItem.h" #include "LineEntityItem.h" -#include "PolyVoxEntityItem.h" #include "PolyLineEntityItem.h" -#include "ShapeEntityItem.h" +#include "PolyVoxEntityItem.h" +#include "GridEntityItem.h" +#include "LightEntityItem.h" +#include "ZoneEntityItem.h" #include "MaterialEntityItem.h" QMap EntityTypes::_typeToNameMap; QMap EntityTypes::_nameToTypeMap; -EntityTypeFactory EntityTypes::_factories[EntityTypes::LAST + 1]; +EntityTypeFactory EntityTypes::_factories[EntityTypes::NUM_TYPES]; bool EntityTypes::_factoriesInitialized = false; const QString ENTITY_TYPE_NAME_UNKNOWN = "Unknown"; // Register Entity the default implementations of entity types here... -REGISTER_ENTITY_TYPE(Model) -REGISTER_ENTITY_TYPE(Web) -REGISTER_ENTITY_TYPE(Light) -REGISTER_ENTITY_TYPE(Text) -REGISTER_ENTITY_TYPE(ParticleEffect) -REGISTER_ENTITY_TYPE(Zone) -REGISTER_ENTITY_TYPE(Line) -REGISTER_ENTITY_TYPE(PolyVox) -REGISTER_ENTITY_TYPE(PolyLine) -REGISTER_ENTITY_TYPE(Shape) REGISTER_ENTITY_TYPE_WITH_FACTORY(Box, ShapeEntityItem::boxFactory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, ShapeEntityItem::sphereFactory) +REGISTER_ENTITY_TYPE(Shape) +REGISTER_ENTITY_TYPE(Model) +REGISTER_ENTITY_TYPE(Text) +REGISTER_ENTITY_TYPE(Image) +REGISTER_ENTITY_TYPE(Web) +REGISTER_ENTITY_TYPE(ParticleEffect) +REGISTER_ENTITY_TYPE(Line) +REGISTER_ENTITY_TYPE(PolyLine) +REGISTER_ENTITY_TYPE(PolyVox) +REGISTER_ENTITY_TYPE(Grid) +REGISTER_ENTITY_TYPE(Light) +REGISTER_ENTITY_TYPE(Zone) REGISTER_ENTITY_TYPE(Material) const QString& EntityTypes::getEntityTypeName(EntityType entityType) { @@ -80,7 +84,7 @@ bool EntityTypes::registerEntityType(EntityType entityType, const char* name, En memset(&_factories,0,sizeof(_factories)); _factoriesInitialized = true; } - if (entityType >= 0 && entityType <= LAST) { + if (entityType >= 0 && entityType < NUM_TYPES) { _factories[entityType] = factoryMethod; return true; } @@ -91,7 +95,7 @@ EntityItemPointer EntityTypes::constructEntityItem(EntityType entityType, const const EntityItemProperties& properties) { EntityItemPointer newEntityItem = NULL; EntityTypeFactory factory = NULL; - if (entityType >= 0 && entityType <= LAST) { + if (entityType >= 0 && entityType < NUM_TYPES) { factory = _factories[entityType]; } if (factory) { diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 954bdf8f17..604f2fe905 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -49,25 +49,6 @@ public: * "Cube". If an entity of type Shape or Sphere has its shape set * to "Cube" then its type will be reported as "Box". * {@link Entities.EntityProperties-Box|EntityProperties-Box} - * "Light"A local lighting effect. - * {@link Entities.EntityProperties-Light|EntityProperties-Light} - * "Line"A sequence of one or more simple straight lines. - * {@link Entities.EntityProperties-Line|EntityProperties-Line} - * "Material"Modifies the existing materials on Model entities, Shape entities (albedo - * only), {@link Overlays.OverlayType|model overlays}, and avatars. - * {@link Entities.EntityProperties-Material|EntityProperties-Material} - * "Model"A mesh model from an FBX or OBJ file. - * {@link Entities.EntityProperties-Model|EntityProperties-Model} - * "ParticleEffect"A particle system that can be used to simulate things such as fire, - * smoke, snow, magic spells, etc. - * {@link Entities.EntityProperties-ParticleEffect|EntityProperties-ParticleEffect} - * "PolyLine"A sequence of one or more textured straight lines. - * {@link Entities.EntityProperties-PolyLine|EntityProperties-PolyLine} - * "PolyVox"A set of textured voxels. - * {@link Entities.EntityProperties-PolyVox|EntityProperties-PolyVox} - * "Shape"A basic entity such as a cube. - * See also, the "Box" and "Sphere" entity types. - * {@link Entities.EntityProperties-Shape|EntityProperties-Shape} * "Sphere"A sphere. This is a synonym of "Shape" for the case * where the entity's shape property value is "Sphere".
* If an entity is created with its type @@ -75,32 +56,57 @@ public: * "Sphere". If an entity of type Box or Shape has its shape set * to "Sphere" then its type will be reported as "Sphere". * {@link Entities.EntityProperties-Sphere|EntityProperties-Sphere} + * "Shape"A basic entity such as a cube. + * See also, the "Box" and "Sphere" entity types. + * {@link Entities.EntityProperties-Shape|EntityProperties-Shape} + * "Model"A mesh model from an FBX or OBJ file. + * {@link Entities.EntityProperties-Model|EntityProperties-Model} * "Text"A pane of text oriented in space. * {@link Entities.EntityProperties-Text|EntityProperties-Text} + * "Image"An image oriented in space. + * {@link Entities.EntityProperties-Image|EntityProperties-Image} * "Web"A browsable Web page. * {@link Entities.EntityProperties-Web|EntityProperties-Web} + * "ParticleEffect"A particle system that can be used to simulate things such as fire, + * smoke, snow, magic spells, etc. + * {@link Entities.EntityProperties-ParticleEffect|EntityProperties-ParticleEffect} + * "Line"A sequence of one or more simple straight lines. + * {@link Entities.EntityProperties-Line|EntityProperties-Line} + * "PolyLine"A sequence of one or more textured straight lines. + * {@link Entities.EntityProperties-PolyLine|EntityProperties-PolyLine} + * "PolyVox"A set of textured voxels. + * {@link Entities.EntityProperties-PolyVox|EntityProperties-PolyVox} + * "Grid"A grid. + * {@link Entities.EntityProperties-Grid|EntityProperties-Grid} + * "Light"A local lighting effect. + * {@link Entities.EntityProperties-Light|EntityProperties-Light} * "Zone"A volume of lighting effects and avatar permissions. * {@link Entities.EntityProperties-Zone|EntityProperties-Zone} + * "Material"Modifies the existing materials on Model entities, Shape entities, + * {@link Overlays.OverlayType|model overlays}, and avatars. + * {@link Entities.EntityProperties-Material|EntityProperties-Material} * * * @typedef {string} Entities.EntityType */ typedef enum EntityType_t { Unknown, - Model, Box, Sphere, - Light, - Text, - ParticleEffect, - Zone, - Web, - Line, - PolyVox, - PolyLine, Shape, + Model, + Text, + Image, + Web, + ParticleEffect, + Line, + PolyLine, + PolyVox, + Grid, + Light, + Zone, Material, - LAST = Material + NUM_TYPES } EntityType; static const QString& getEntityTypeName(EntityType entityType); @@ -112,7 +118,7 @@ public: private: static QMap _typeToNameMap; static QMap _nameToTypeMap; - static EntityTypeFactory _factories[LAST + 1]; + static EntityTypeFactory _factories[NUM_TYPES]; static bool _factoriesInitialized; }; diff --git a/libraries/entities/src/GridEntityItem.cpp b/libraries/entities/src/GridEntityItem.cpp new file mode 100644 index 0000000000..cc59135e8d --- /dev/null +++ b/libraries/entities/src/GridEntityItem.cpp @@ -0,0 +1,154 @@ +// +// Created by Sam Gondelman on 11/29/18 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "GridEntityItem.h" + +#include "EntityItemProperties.h" + +EntityItemPointer GridEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + Pointer entity(new GridEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); + entity->setProperties(properties); + return entity; +} + +// our non-pure virtual subclass for now... +GridEntityItem::GridEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { + _type = EntityTypes::Grid; +} + +EntityItemProperties GridEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class + + //COPY_ENTITY_PROPERTY_TO_PROPERTIES(imageURL, getGridURL); + //COPY_ENTITY_PROPERTY_TO_PROPERTIES(emissive, getEmissive); + //COPY_ENTITY_PROPERTY_TO_PROPERTIES(keepAspectRatio, getKeepAspectRatio); + //COPY_ENTITY_PROPERTY_TO_PROPERTIES(faceCamera, getFaceCamera); + //COPY_ENTITY_PROPERTY_TO_PROPERTIES(subGrid, getSubGrid); + + return properties; +} + +bool GridEntityItem::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class + + //SET_ENTITY_PROPERTY_FROM_PROPERTIES(imageURL, setGridURL); + //SET_ENTITY_PROPERTY_FROM_PROPERTIES(emissive, setEmissive); + //SET_ENTITY_PROPERTY_FROM_PROPERTIES(keepAspectRatio, setKeepAspectRatio); + //SET_ENTITY_PROPERTY_FROM_PROPERTIES(faceCamera, setFaceCamera); + //SET_ENTITY_PROPERTY_FROM_PROPERTIES(subGrid, setSubGrid); + + if (somethingChanged) { + bool wantDebug = false; + if (wantDebug) { + uint64_t now = usecTimestampNow(); + int elapsed = now - getLastEdited(); + qCDebug(entities) << "GridEntityItem::setProperties() AFTER update... edited AGO=" << elapsed << + "now=" << now << " getLastEdited()=" << getLastEdited(); + } + setLastEdited(properties.getLastEdited()); + } + return somethingChanged; +} + +int GridEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + + //READ_ENTITY_PROPERTY(PROP_IMAGE_URL, QString, setGridURL); + //READ_ENTITY_PROPERTY(PROP_EMISSIVE, bool, setEmissive); + //READ_ENTITY_PROPERTY(PROP_KEEP_ASPECT_RATIO, bool, setKeepAspectRatio); + //READ_ENTITY_PROPERTY(PROP_FACE_CAMERA, bool, setFaceCamera); + //READ_ENTITY_PROPERTY(PROP_SUB_IMAGE, QRect, setSubGrid); + + return bytesRead; +} + +EntityPropertyFlags GridEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + + //requestedProperties += PROP_IMAGE_URL; + //requestedProperties += PROP_EMISSIVE; + //requestedProperties += PROP_KEEP_ASPECT_RATIO; + //requestedProperties += PROP_FACE_CAMERA; + //requestedProperties += PROP_SUB_IMAGE; + + return requestedProperties; +} + +void GridEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + + //APPEND_ENTITY_PROPERTY(PROP_IMAGE_URL, getGridURL()); + //APPEND_ENTITY_PROPERTY(PROP_EMISSIVE, getEmissive()); + //APPEND_ENTITY_PROPERTY(PROP_KEEP_ASPECT_RATIO, getKeepAspectRatio()); + //APPEND_ENTITY_PROPERTY(PROP_FACE_CAMERA, getFaceCamera()); + //APPEND_ENTITY_PROPERTY(PROP_SUB_IMAGE, getSubGrid()); +} + +bool GridEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + OctreeElementPointer& element, + float& distance, BoxFace& face, glm::vec3& surfaceNormal, + QVariantMap& extraInfo, bool precisionPicking) const { + glm::vec3 dimensions = getScaledDimensions(); + glm::vec2 xyDimensions(dimensions.x, dimensions.y); + glm::quat rotation = getWorldOrientation(); + glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); + + if (findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance)) { + glm::vec3 forward = rotation * Vectors::FRONT; + if (glm::dot(forward, direction) > 0.0f) { + face = MAX_Z_FACE; + surfaceNormal = -forward; + } else { + face = MIN_Z_FACE; + surfaceNormal = forward; + } + return true; + } + return false; +} + +bool GridEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, + OctreeElementPointer& element, float& parabolicDistance, + BoxFace& face, glm::vec3& surfaceNormal, + QVariantMap& extraInfo, bool precisionPicking) const { + glm::vec3 dimensions = getScaledDimensions(); + glm::vec2 xyDimensions(dimensions.x, dimensions.y); + glm::quat rotation = getWorldOrientation(); + glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); + + glm::quat inverseRot = glm::inverse(rotation); + glm::vec3 localOrigin = inverseRot * (origin - position); + glm::vec3 localVelocity = inverseRot * velocity; + glm::vec3 localAcceleration = inverseRot * acceleration; + + if (findParabolaRectangleIntersection(localOrigin, localVelocity, localAcceleration, xyDimensions, parabolicDistance)) { + float localIntersectionVelocityZ = localVelocity.z + localAcceleration.z * parabolicDistance; + glm::vec3 forward = rotation * Vectors::FRONT; + if (localIntersectionVelocityZ > 0.0f) { + face = MIN_Z_FACE; + surfaceNormal = forward; + } else { + face = MAX_Z_FACE; + surfaceNormal = -forward; + } + return true; + } + return false; +} \ No newline at end of file diff --git a/libraries/entities/src/GridEntityItem.h b/libraries/entities/src/GridEntityItem.h new file mode 100644 index 0000000000..4704d96f3d --- /dev/null +++ b/libraries/entities/src/GridEntityItem.h @@ -0,0 +1,56 @@ +// +// Created by Sam Gondelman on 11/29/18 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_GridEntityItem_h +#define hifi_GridEntityItem_h + +#include "EntityItem.h" + +class GridEntityItem : public EntityItem { + using Pointer = std::shared_ptr; +public: + static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + GridEntityItem(const EntityItemID& entityItemID); + + ALLOW_INSTANTIATION // This class can be instantiated + + // methods for getting/setting all properties of an entity + EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; + bool setProperties(const EntityItemProperties& properties) override; + + EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) override; + + virtual bool supportsDetailedIntersection() const override { return true; } + virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + OctreeElementPointer& element, float& distance, + BoxFace& face, glm::vec3& surfaceNormal, + QVariantMap& extraInfo, bool precisionPicking) const override; + virtual bool findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, + const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance, + BoxFace& face, glm::vec3& surfaceNormal, + QVariantMap& extraInfo, bool precisionPicking) const override; + +protected: + +}; + +#endif // hifi_GridEntityItem_h diff --git a/libraries/entities/src/ImageEntityItem.cpp b/libraries/entities/src/ImageEntityItem.cpp new file mode 100644 index 0000000000..98817a63ba --- /dev/null +++ b/libraries/entities/src/ImageEntityItem.cpp @@ -0,0 +1,269 @@ +// +// Created by Sam Gondelman on 11/29/18 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ImageEntityItem.h" + +#include "EntityItemProperties.h" + +EntityItemPointer ImageEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + Pointer entity(new ImageEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); + entity->setProperties(properties); + return entity; +} + +// our non-pure virtual subclass for now... +ImageEntityItem::ImageEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { + _type = EntityTypes::Image; +} + +void ImageEntityItem::setUnscaledDimensions(const glm::vec3& value) { + const float IMAGE_ENTITY_ITEM_FIXED_DEPTH = 0.01f; + // NOTE: Image Entities always have a "depth" of 1cm. + EntityItem::setUnscaledDimensions(glm::vec3(value.x, value.y, IMAGE_ENTITY_ITEM_FIXED_DEPTH)); +} + +EntityItemProperties ImageEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class + + COPY_ENTITY_PROPERTY_TO_PROPERTIES(imageURL, getImageURL); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(emissive, getEmissive); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(keepAspectRatio, getKeepAspectRatio); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(billboardMode, getBillboardMode); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(subImage, getSubImage); + + COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha); + + return properties; +} + +bool ImageEntityItem::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class + + SET_ENTITY_PROPERTY_FROM_PROPERTIES(imageURL, setImageURL); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(emissive, setEmissive); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(keepAspectRatio, setKeepAspectRatio); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(billboardMode, setBillboardMode); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(subImage, setSubImage); + + SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha); + + if (somethingChanged) { + bool wantDebug = false; + if (wantDebug) { + uint64_t now = usecTimestampNow(); + int elapsed = now - getLastEdited(); + qCDebug(entities) << "ImageEntityItem::setProperties() AFTER update... edited AGO=" << elapsed << + "now=" << now << " getLastEdited()=" << getLastEdited(); + } + setLastEdited(properties.getLastEdited()); + } + return somethingChanged; +} + +int ImageEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_IMAGE_URL, QString, setImageURL); + READ_ENTITY_PROPERTY(PROP_EMISSIVE, bool, setEmissive); + READ_ENTITY_PROPERTY(PROP_KEEP_ASPECT_RATIO, bool, setKeepAspectRatio); + READ_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); + READ_ENTITY_PROPERTY(PROP_SUB_IMAGE, QRect, setSubImage); + + READ_ENTITY_PROPERTY(PROP_COLOR, u8vec3Color, setColor); + READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha); + + return bytesRead; +} + +EntityPropertyFlags ImageEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + + requestedProperties += PROP_IMAGE_URL; + requestedProperties += PROP_EMISSIVE; + requestedProperties += PROP_KEEP_ASPECT_RATIO; + requestedProperties += PROP_BILLBOARD_MODE; + requestedProperties += PROP_SUB_IMAGE; + + requestedProperties += PROP_COLOR; + requestedProperties += PROP_ALPHA; + + return requestedProperties; +} + +void ImageEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_IMAGE_URL, getImageURL()); + APPEND_ENTITY_PROPERTY(PROP_EMISSIVE, getEmissive()); + APPEND_ENTITY_PROPERTY(PROP_KEEP_ASPECT_RATIO, getKeepAspectRatio()); + APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)getBillboardMode()); + APPEND_ENTITY_PROPERTY(PROP_SUB_IMAGE, getSubImage()); + + APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor()); + APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); +} + +bool ImageEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + OctreeElementPointer& element, + float& distance, BoxFace& face, glm::vec3& surfaceNormal, + QVariantMap& extraInfo, bool precisionPicking) const { + glm::vec3 dimensions = getScaledDimensions(); + glm::vec2 xyDimensions(dimensions.x, dimensions.y); + glm::quat rotation = getWorldOrientation(); + glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); + + if (findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance)) { + glm::vec3 forward = rotation * Vectors::FRONT; + if (glm::dot(forward, direction) > 0.0f) { + face = MAX_Z_FACE; + surfaceNormal = -forward; + } else { + face = MIN_Z_FACE; + surfaceNormal = forward; + } + return true; + } + return false; +} + +bool ImageEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, + OctreeElementPointer& element, float& parabolicDistance, + BoxFace& face, glm::vec3& surfaceNormal, + QVariantMap& extraInfo, bool precisionPicking) const { + glm::vec3 dimensions = getScaledDimensions(); + glm::vec2 xyDimensions(dimensions.x, dimensions.y); + glm::quat rotation = getWorldOrientation(); + glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); + + glm::quat inverseRot = glm::inverse(rotation); + glm::vec3 localOrigin = inverseRot * (origin - position); + glm::vec3 localVelocity = inverseRot * velocity; + glm::vec3 localAcceleration = inverseRot * acceleration; + + if (findParabolaRectangleIntersection(localOrigin, localVelocity, localAcceleration, xyDimensions, parabolicDistance)) { + float localIntersectionVelocityZ = localVelocity.z + localAcceleration.z * parabolicDistance; + glm::vec3 forward = rotation * Vectors::FRONT; + if (localIntersectionVelocityZ > 0.0f) { + face = MIN_Z_FACE; + surfaceNormal = forward; + } else { + face = MAX_Z_FACE; + surfaceNormal = -forward; + } + return true; + } + return false; +} + +QString ImageEntityItem::getImageURL() const { + QString result; + withReadLock([&] { + result = _imageURL; + }); + return result; +} + +void ImageEntityItem::setImageURL(const QString& url) { + withWriteLock([&] { + _imageURL = url; + }); +} + +bool ImageEntityItem::getEmissive() const { + bool result; + withReadLock([&] { + result = _emissive; + }); + return result; +} + +void ImageEntityItem::setEmissive(bool emissive) { + withWriteLock([&] { + _emissive = emissive; + }); +} + +bool ImageEntityItem::getKeepAspectRatio() const { + bool result; + withReadLock([&] { + result = _keepAspectRatio; + }); + return result; +} + +void ImageEntityItem::setKeepAspectRatio(bool keepAspectRatio) { + withWriteLock([&] { + _keepAspectRatio = keepAspectRatio; + }); +} + +BillboardMode ImageEntityItem::getBillboardMode() const { + BillboardMode result; + withReadLock([&] { + result = _billboardMode; + }); + return result; +} + +void ImageEntityItem::setBillboardMode(BillboardMode value) { + withWriteLock([&] { + _billboardMode = value; + }); +} + +QRect ImageEntityItem::getSubImage() const { + QRect result; + withReadLock([&] { + result = _subImage; + }); + return result; +} + +void ImageEntityItem::setSubImage(const QRect& subImage) { + withWriteLock([&] { + _subImage = subImage; + }); +} + +void ImageEntityItem::setColor(const glm::u8vec3& color) { + withWriteLock([&] { + _color = color; + }); +} + +glm::u8vec3 ImageEntityItem::getColor() const { + return resultWithReadLock([&] { + return _color; + }); +} + +void ImageEntityItem::setAlpha(float alpha) { + withWriteLock([&] { + _alpha = alpha; + }); +} + +float ImageEntityItem::getAlpha() const { + return resultWithReadLock([&] { + return _alpha; + }); +} \ No newline at end of file diff --git a/libraries/entities/src/ImageEntityItem.h b/libraries/entities/src/ImageEntityItem.h new file mode 100644 index 0000000000..228f86ca03 --- /dev/null +++ b/libraries/entities/src/ImageEntityItem.h @@ -0,0 +1,86 @@ +// +// Created by Sam Gondelman on 11/29/18 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ImageEntityItem_h +#define hifi_ImageEntityItem_h + +#include "EntityItem.h" + +class ImageEntityItem : public EntityItem { + using Pointer = std::shared_ptr; +public: + static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + ImageEntityItem(const EntityItemID& entityItemID); + + ALLOW_INSTANTIATION // This class can be instantiated + + virtual void setUnscaledDimensions(const glm::vec3& value) override; + + // methods for getting/setting all properties of an entity + EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; + bool setProperties(const EntityItemProperties& properties) override; + + EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) override; + + virtual bool supportsDetailedIntersection() const override { return true; } + virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + OctreeElementPointer& element, float& distance, + BoxFace& face, glm::vec3& surfaceNormal, + QVariantMap& extraInfo, bool precisionPicking) const override; + virtual bool findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, + const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance, + BoxFace& face, glm::vec3& surfaceNormal, + QVariantMap& extraInfo, bool precisionPicking) const override; + + void setImageURL(const QString& imageUrl); + QString getImageURL() const; + + void setEmissive(bool emissive); + bool getEmissive() const; + + void setKeepAspectRatio(bool keepAspectRatio); + bool getKeepAspectRatio() const; + + void setBillboardMode(BillboardMode value); + BillboardMode getBillboardMode() const; + + void setSubImage(const QRect& subImage); + QRect getSubImage() const; + + void setColor(const glm::u8vec3& color); + glm::u8vec3 getColor() const; + + void setAlpha(float alpha); + float getAlpha() const; + +protected: + QString _imageURL; + bool _emissive { false }; + bool _keepAspectRatio { true }; + BillboardMode _billboardMode; + QRect _subImage; + + glm::u8vec3 _color; + float _alpha; +}; + +#endif // hifi_ImageEntityItem_h diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp index 8dd4877ce2..b178ce33c3 100644 --- a/libraries/entities/src/TextEntityItem.cpp +++ b/libraries/entities/src/TextEntityItem.cpp @@ -27,7 +27,6 @@ const QString TextEntityItem::DEFAULT_TEXT(""); const float TextEntityItem::DEFAULT_LINE_HEIGHT = 0.1f; const glm::u8vec3 TextEntityItem::DEFAULT_TEXT_COLOR = { 255, 255, 255 }; const glm::u8vec3 TextEntityItem::DEFAULT_BACKGROUND_COLOR = { 0, 0, 0}; -const bool TextEntityItem::DEFAULT_FACE_CAMERA = false; EntityItemPointer TextEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity(new TextEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); @@ -39,9 +38,8 @@ TextEntityItem::TextEntityItem(const EntityItemID& entityItemID) : EntityItem(en _type = EntityTypes::Text; } -const float TEXT_ENTITY_ITEM_FIXED_DEPTH = 0.01f; - void TextEntityItem::setUnscaledDimensions(const glm::vec3& value) { + const float TEXT_ENTITY_ITEM_FIXED_DEPTH = 0.01f; // NOTE: Text Entities always have a "depth" of 1cm. EntityItem::setUnscaledDimensions(glm::vec3(value.x, value.y, TEXT_ENTITY_ITEM_FIXED_DEPTH)); } @@ -53,7 +51,7 @@ EntityItemProperties TextEntityItem::getProperties(const EntityPropertyFlags& de COPY_ENTITY_PROPERTY_TO_PROPERTIES(lineHeight, getLineHeight); COPY_ENTITY_PROPERTY_TO_PROPERTIES(textColor, getTextColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(backgroundColor, getBackgroundColor); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(faceCamera, getFaceCamera); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(billboardMode, getBillboardMode); return properties; } @@ -65,7 +63,7 @@ bool TextEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(lineHeight, setLineHeight); SET_ENTITY_PROPERTY_FROM_PROPERTIES(textColor, setTextColor); SET_ENTITY_PROPERTY_FROM_PROPERTIES(backgroundColor, setBackgroundColor); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(faceCamera, setFaceCamera); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(billboardMode, setBillboardMode); if (somethingChanged) { bool wantDebug = false; @@ -93,7 +91,7 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_LINE_HEIGHT, float, setLineHeight); READ_ENTITY_PROPERTY(PROP_TEXT_COLOR, glm::u8vec3, setTextColor); READ_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, glm::u8vec3, setBackgroundColor); - READ_ENTITY_PROPERTY(PROP_FACE_CAMERA, bool, setFaceCamera); + READ_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); return bytesRead; } @@ -104,7 +102,7 @@ EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& p requestedProperties += PROP_LINE_HEIGHT; requestedProperties += PROP_TEXT_COLOR; requestedProperties += PROP_BACKGROUND_COLOR; - requestedProperties += PROP_FACE_CAMERA; + requestedProperties += PROP_BILLBOARD_MODE; return requestedProperties; } @@ -122,7 +120,7 @@ void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, getLineHeight()); APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, getTextColor()); APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, getBackgroundColor()); - APPEND_ENTITY_PROPERTY(PROP_FACE_CAMERA, getFaceCamera()); + APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)getBillboardMode()); } @@ -230,17 +228,17 @@ glm::u8vec3 TextEntityItem::getBackgroundColor() const { }); } -bool TextEntityItem::getFaceCamera() const { - bool result; +BillboardMode TextEntityItem::getBillboardMode() const { + BillboardMode result; withReadLock([&] { - result = _faceCamera; + result = _billboardMode; }); return result; } -void TextEntityItem::setFaceCamera(bool value) { +void TextEntityItem::setBillboardMode(BillboardMode value) { withWriteLock([&] { - _faceCamera = value; + _billboardMode = value; }); } diff --git a/libraries/entities/src/TextEntityItem.h b/libraries/entities/src/TextEntityItem.h index 357697fdec..9abef4d198 100644 --- a/libraries/entities/src/TextEntityItem.h +++ b/libraries/entities/src/TextEntityItem.h @@ -71,16 +71,15 @@ public: glm::u8vec3 getBackgroundColor() const; void setBackgroundColor(const glm::u8vec3& value); - static const bool DEFAULT_FACE_CAMERA; - bool getFaceCamera() const; - void setFaceCamera(bool value); + BillboardMode getBillboardMode() const; + void setBillboardMode(BillboardMode value); private: QString _text; float _lineHeight; glm::u8vec3 _textColor; glm::u8vec3 _backgroundColor; - bool _faceCamera; + BillboardMode _billboardMode; }; #endif // hifi_TextEntityItem_h diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 9e3ecdfb39..3325faa95c 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -33,7 +33,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return static_cast(EntityVersion::CleanupProperties); + return static_cast(EntityVersion::ImageEntities); case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::ConicalFrustums); case PacketType::AvatarIdentity: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 800ce7bbe7..421c9a96c1 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -245,7 +245,8 @@ enum class EntityVersion : PacketVersion { GrabProperties, ScriptGlmVectors, FixedLightSerialization, - CleanupProperties + CleanupProperties, + ImageEntities }; enum class EntityScriptCallMethodVersion : PacketVersion { diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp index 88e83c01c8..6c0bba5ec6 100644 --- a/libraries/octree/src/OctreePacketData.cpp +++ b/libraries/octree/src/OctreePacketData.cpp @@ -546,6 +546,17 @@ bool OctreePacketData::appendValue(const AACube& aaCube) { return success; } +bool OctreePacketData::appendValue(const QRect& value) { + const unsigned char* data = (const unsigned char*)&value; + int length = sizeof(QRect); + bool success = append(data, length); + if (success) { + _bytesOfValues += length; + _totalBytesOfValues += length; + } + return success; +} + bool OctreePacketData::appendPosition(const glm::vec3& value) { const unsigned char* data = (const unsigned char*)&value; int length = sizeof(value); @@ -804,3 +815,8 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, AACube result = AACube(cube.corner, cube.scale); return sizeof(aaCubeData); } + +int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QRect& result) { + memcpy(&result, dataBytes, sizeof(result)); + return sizeof(result); +} \ No newline at end of file diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 46726d83a6..bd1abf8744 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -34,6 +34,7 @@ #include #include "MaterialMappingMode.h" +#include "BillboardMode.h" #include "OctreeConstants.h" #include "OctreeElement.h" @@ -197,6 +198,9 @@ public: /// appends an AACube value to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(const AACube& aaCube); + /// appends an QRect value to the end of the stream, may fail if new data stream is too long to fit in packet + bool appendValue(const QRect& rect); + /// appends a position to the end of the stream, may fail if new data stream is too long to fit in packet bool appendPosition(const glm::vec3& value); @@ -258,6 +262,7 @@ public: static int unpackDataFromBytes(const unsigned char* dataBytes, glm::quat& result) { int bytes = unpackOrientationQuatFromBytes(dataBytes, result); return bytes; } static int unpackDataFromBytes(const unsigned char* dataBytes, ShapeType& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, MaterialMappingMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } + static int unpackDataFromBytes(const unsigned char* dataBytes, BillboardMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec2& result); static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec3& result); static int unpackDataFromBytes(const unsigned char* dataBytes, glm::u8vec3& result); @@ -269,6 +274,7 @@ public: static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QByteArray& result); static int unpackDataFromBytes(const unsigned char* dataBytes, AACube& result); + static int unpackDataFromBytes(const unsigned char* dataBytes, QRect& result); private: /// appends raw bytes, might fail if byte would cause packet to be too large diff --git a/libraries/shared/src/BillboardMode.cpp b/libraries/shared/src/BillboardMode.cpp new file mode 100644 index 0000000000..56251f53f2 --- /dev/null +++ b/libraries/shared/src/BillboardMode.cpp @@ -0,0 +1,25 @@ +// +// Created by Sam Gondelman on 11/30/18 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "BillboardMode.h" + +const char* billboardModeNames[] = { + "none", + "yaw", + "full" +}; + +static const size_t MATERIAL_MODE_NAMES = (sizeof(billboardModeNames) / sizeof((billboardModeNames)[0])); + +QString BillboardModeHelpers::getNameForBillboardMode(BillboardMode mode) { + if (((int)mode <= 0) || ((int)mode >= (int)MATERIAL_MODE_NAMES)) { + mode = (BillboardMode)0; + } + + return billboardModeNames[(int)mode]; +} \ No newline at end of file diff --git a/libraries/shared/src/BillboardMode.h b/libraries/shared/src/BillboardMode.h new file mode 100644 index 0000000000..7f3e79d453 --- /dev/null +++ b/libraries/shared/src/BillboardMode.h @@ -0,0 +1,26 @@ +// +// Created by Sam Gondelman on 11/30/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_BillboardMode_h +#define hifi_BillboardMode_h + +#include "QString" + +enum class BillboardMode { + NONE = 0, + YAW, + FULL +}; + +class BillboardModeHelpers { +public: + static QString getNameForBillboardMode(BillboardMode mode); +}; + +#endif // hifi_BillboardMode_h + diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index dc84afff93..5394a0f448 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -24,6 +24,7 @@ #include #include #include +#include int vec2MetaTypeId = qRegisterMetaType(); int u8vec3MetaTypeId = qRegisterMetaType(); @@ -1245,3 +1246,31 @@ void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector result << meshFace; } } + +QVariantMap parseTexturesToMap(QString textures, const QVariantMap& defaultTextures) { + // If textures are unset, revert to original textures + if (textures.isEmpty()) { + return defaultTextures; + } + + // Legacy: a ,\n-delimited list of filename:"texturepath" + if (*textures.cbegin() != '{') { + textures = "{\"" + textures.replace(":\"", "\":\"").replace(",\n", ",\"") + "}"; + } + + QJsonParseError error; + QJsonDocument texturesJson = QJsonDocument::fromJson(textures.toUtf8(), &error); + // If textures are invalid, revert to original textures + if (error.error != QJsonParseError::NoError) { + qWarning() << "Could not evaluate textures property value:" << textures; + return defaultTextures; + } + + QVariantMap texturesMap = texturesJson.toVariant().toMap(); + // If textures are unset, revert to original textures + if (texturesMap.isEmpty()) { + return defaultTextures; + } + + return texturesJson.toVariant().toMap(); +} \ No newline at end of file diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index ed637fe771..9d5bca6b9f 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -705,5 +705,7 @@ void meshFaceFromScriptValue(const QScriptValue &object, MeshFace& meshFaceResul QScriptValue qVectorMeshFaceToScriptValue(QScriptEngine* engine, const QVector& vector); void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector& result); +QVariantMap parseTexturesToMap(QString textures, const QVariantMap& defaultTextures); + #endif // hifi_RegisteredMetaTypes_h diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 077d50ddde..8d5271093e 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -42,7 +42,6 @@ var TITLE_OFFSET = 60; var CREATE_TOOLS_WIDTH = 490; var MAX_DEFAULT_ENTITY_LIST_HEIGHT = 942; -var IMAGE_MODEL = "https://hifi-content.s3.amazonaws.com/DomainContent/production/default-image-model.fbx"; var DEFAULT_IMAGE = "https://hifi-content.s3.amazonaws.com/DomainContent/production/no-image.jpg"; var createToolsWindow = new CreateWindow( @@ -398,8 +397,8 @@ const DEFAULT_ENTITY_PROPERTIES = { }, shapeType: "box", collisionless: true, - modelURL: IMAGE_MODEL, - textures: JSON.stringify({ "tex.picture": "" }) + keepAspectRatio: false, + imageURL: DEFAULT_IMAGE }, Web: { dimensions: { @@ -495,9 +494,6 @@ var toolBar = (function () { var type = requestedProperties.type; if (type === "Box" || type === "Sphere") { applyProperties(properties, DEFAULT_ENTITY_PROPERTIES.Shape); - } else if (type === "Image") { - requestedProperties.type = "Model"; - applyProperties(properties, DEFAULT_ENTITY_PROPERTIES.Image); } else { applyProperties(properties, DEFAULT_ENTITY_PROPERTIES[type]); } @@ -515,7 +511,7 @@ var toolBar = (function () { } direction = Vec3.multiplyQbyV(direction, Vec3.UNIT_Z); - var PRE_ADJUST_ENTITY_TYPES = ["Box", "Sphere", "Shape", "Text", "Web", "Material"]; + var PRE_ADJUST_ENTITY_TYPES = ["Box", "Sphere", "Shape", "Text", "Image", "Web", "Material"]; if (PRE_ADJUST_ENTITY_TYPES.indexOf(properties.type) !== -1) { // Adjust position of entity per bounding box prior to creating it. diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 84ad59df36..a0520cda4f 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -11,7 +11,6 @@ const DESCENDING_SORT = -1; const ASCENDING_STRING = '▴'; const DESCENDING_STRING = '▾'; const BYTES_PER_MEGABYTE = 1024 * 1024; -const IMAGE_MODEL_NAME = 'default-image-model.fbx'; const COLLAPSE_EXTRA_INFO = "E"; const EXPAND_EXTRA_INFO = "D"; const FILTER_IN_VIEW_ATTRIBUTE = "pressed"; @@ -625,9 +624,6 @@ function loaded() { entityData.forEach(function(entity) { let type = entity.type; let filename = getFilename(entity.url); - if (filename === IMAGE_MODEL_NAME) { - type = "Image"; - } let entityData = { id: entity.id, diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 78e3cd4dc8..da16f6c504 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -471,7 +471,7 @@ const GROUPS = [ label: "Image", type: "string", placeholder: "URL", - propertyID: "image", + propertyID: "imageURL", }, ] }, @@ -1783,14 +1783,6 @@ function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElemen updateProperty(propertyName, propertyValue, isParticleProperty); } -function createImageURLUpdateFunction(property) { - return function () { - let newTextures = JSON.stringify({ "tex.picture": this.value }); - updateProperty(property.name, newTextures, property.isParticleProperty); - }; -} - - /** * PROPERTY ELEMENT CREATION FUNCTIONS */ @@ -3035,17 +3027,6 @@ function loaded() { // the event bridge and json parsing handle our avatar id string differently. lastEntityID = '"' + selectedEntityProperties.id + '"'; - // HTML workaround since image is not yet a separate entity type - let IMAGE_MODEL_NAME = 'default-image-model.fbx'; - if (selectedEntityProperties.type === "Model") { - let urlParts = selectedEntityProperties.modelURL.split('/'); - let propsFilename = urlParts[urlParts.length - 1]; - - if (propsFilename === IMAGE_MODEL_NAME) { - selectedEntityProperties.type = "Image"; - } - } - showGroupsForType(selectedEntityProperties.type); for (let propertyID in properties) { @@ -3171,10 +3152,7 @@ function loaded() { updateVisibleSpaceModeProperties(); - if (selectedEntityProperties.type === "Image") { - let imageLink = JSON.parse(selectedEntityProperties.textures)["tex.picture"]; - getPropertyInputElement("image").value = imageLink; - } else if (selectedEntityProperties.type === "Material") { + if (selectedEntityProperties.type === "Material") { let elParentMaterialNameString = getPropertyInputElement("materialNameToReplace"); let elParentMaterialNameNumber = getPropertyInputElement("submeshToReplace"); let elParentMaterialNameCheckbox = getPropertyInputElement("selectSubmesh"); @@ -3340,8 +3318,6 @@ function loaded() { } }); - getPropertyInputElement("image").addEventListener('change', createImageURLUpdateFunction(properties['textures'])); - // Collapsible sections let elCollapsible = document.getElementsByClassName("collapse-icon"); diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index eeb16fd60d..edc6bc3845 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -164,7 +164,7 @@ EntityListTool = function(shouldUseEditTabletApp) { var cameraPosition = Camera.position; PROFILE("getMultipleProperties", function () { var multipleProperties = Entities.getMultipleEntityProperties(ids, ['name', 'type', 'locked', - 'visible', 'renderInfo', 'modelURL', 'materialURL', 'script']); + 'visible', 'renderInfo', 'modelURL', 'materialURL', 'imageURL', 'script']); for (var i = 0; i < multipleProperties.length; i++) { var properties = multipleProperties[i]; @@ -174,6 +174,8 @@ EntityListTool = function(shouldUseEditTabletApp) { url = properties.modelURL; } else if (properties.type === "Material") { url = properties.materialURL; + } else if (properties.type === "Image") { + url = properties.imageURL; } entities.push({ id: ids[i], From 98f9dc0ca4f5f0056ec683c2b3344b1477bd44f3 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 3 Dec 2018 11:59:19 -0800 Subject: [PATCH 03/27] remove grid entity files --- .../src/RenderableEntityItem.cpp | 5 - .../src/RenderableGridEntityItem.cpp | 51 ------ .../src/RenderableGridEntityItem.h | 36 ---- .../entities/src/EntityItemProperties.cpp | 1 - libraries/entities/src/EntityItemProperties.h | 1 - libraries/entities/src/EntityTypes.cpp | 2 - libraries/entities/src/EntityTypes.h | 3 - libraries/entities/src/GridEntityItem.cpp | 154 ------------------ libraries/entities/src/GridEntityItem.h | 56 ------- 9 files changed, 309 deletions(-) delete mode 100644 libraries/entities-renderer/src/RenderableGridEntityItem.cpp delete mode 100644 libraries/entities-renderer/src/RenderableGridEntityItem.h delete mode 100644 libraries/entities/src/GridEntityItem.cpp delete mode 100644 libraries/entities/src/GridEntityItem.h diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 92387dafa6..6c979f8afe 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -26,7 +26,6 @@ #include "RenderableZoneEntityItem.h" #include "RenderableMaterialEntityItem.h" #include "RenderableImageEntityItem.h" -#include "RenderableGridEntityItem.h" using namespace render; @@ -257,10 +256,6 @@ EntityRenderer::Pointer EntityRenderer::addToScene(EntityTreeRenderer& renderer, result = make_renderer(entity); break; - case Type::Grid: - result = make_renderer(entity); - break; - case Type::Light: result = make_renderer(entity); break; diff --git a/libraries/entities-renderer/src/RenderableGridEntityItem.cpp b/libraries/entities-renderer/src/RenderableGridEntityItem.cpp deleted file mode 100644 index bd42a722fb..0000000000 --- a/libraries/entities-renderer/src/RenderableGridEntityItem.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// -// Created by Sam Gondelman on 11/29/18 -// Copyright 2018 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "RenderableGridEntityItem.h" - -using namespace render; -using namespace render::entities; - -GridEntityRenderer::GridEntityRenderer(const EntityItemPointer& entity) : Parent(entity) { -} - -bool GridEntityRenderer::needsRenderUpdate() const { - return Parent::needsRenderUpdate(); -} - -bool GridEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { - return false; -} - -void GridEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { - -} - -ItemKey GridEntityRenderer::getKey() { - ItemKey::Builder builder; - builder.withTypeShape().withTypeMeta().withTagBits(getTagMask()); - - withReadLock([&] { - if (isTransparent()) { - builder.withTransparent(); - } else if (_canCastShadow) { - builder.withShadowCaster(); - } - }); - - return builder.build(); -} - -ShapeKey GridEntityRenderer::getShapeKey() { - ShapeKey::Builder builder; - return builder.build(); -} - -void GridEntityRenderer::doRender(RenderArgs* args) { - -} \ No newline at end of file diff --git a/libraries/entities-renderer/src/RenderableGridEntityItem.h b/libraries/entities-renderer/src/RenderableGridEntityItem.h deleted file mode 100644 index 09c0de1d0d..0000000000 --- a/libraries/entities-renderer/src/RenderableGridEntityItem.h +++ /dev/null @@ -1,36 +0,0 @@ -// -// Created by Sam Gondelman on 11/29/18 -// Copyright 2018 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_RenderableGridEntityItem_h -#define hifi_RenderableGridEntityItem_h - -#include "RenderableEntityItem.h" - -#include - -namespace render { namespace entities { - -class GridEntityRenderer : public TypedEntityRenderer { - using Parent = TypedEntityRenderer; - using Pointer = std::shared_ptr; -public: - GridEntityRenderer(const EntityItemPointer& entity); - -protected: - ItemKey getKey() override; - ShapeKey getShapeKey() override; - -private: - virtual bool needsRenderUpdate() const override; - virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; - virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; - virtual void doRender(RenderArgs* args) override; -}; - -} } -#endif // hifi_RenderableGridEntityItem_h diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index f24b14d3b7..6f451f937a 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -684,7 +684,6 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @see {@link Entities.EntityProperties-Line|EntityProperties-Line} * @see {@link Entities.EntityProperties-PolyLine|EntityProperties-PolyLine} * @see {@link Entities.EntityProperties-PolyVox|EntityProperties-PolyVox} - * @see {@link Entities.EntityProperties-Grid|EntityProperties-Grid} * @see {@link Entities.EntityProperties-Light|EntityProperties-Light} * @see {@link Entities.EntityProperties-Zone|EntityProperties-Zone} * @see {@link Entities.EntityProperties-Material|EntityProperties-Material} diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index a34af703fb..8a9e22f28c 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -80,7 +80,6 @@ class EntityItemProperties { friend class LineEntityItem; friend class PolyLineEntityItem; friend class PolyVoxEntityItem; - friend class GridEntityItem; friend class LightEntityItem; friend class ZoneEntityItem; friend class MaterialEntityItem; diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index e511af83b0..30f9d9b541 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -29,7 +29,6 @@ #include "LineEntityItem.h" #include "PolyLineEntityItem.h" #include "PolyVoxEntityItem.h" -#include "GridEntityItem.h" #include "LightEntityItem.h" #include "ZoneEntityItem.h" #include "MaterialEntityItem.h" @@ -53,7 +52,6 @@ REGISTER_ENTITY_TYPE(ParticleEffect) REGISTER_ENTITY_TYPE(Line) REGISTER_ENTITY_TYPE(PolyLine) REGISTER_ENTITY_TYPE(PolyVox) -REGISTER_ENTITY_TYPE(Grid) REGISTER_ENTITY_TYPE(Light) REGISTER_ENTITY_TYPE(Zone) REGISTER_ENTITY_TYPE(Material) diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 604f2fe905..c85cb5b2dd 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -76,8 +76,6 @@ public: * {@link Entities.EntityProperties-PolyLine|EntityProperties-PolyLine} * "PolyVox"A set of textured voxels. * {@link Entities.EntityProperties-PolyVox|EntityProperties-PolyVox} - * "Grid"A grid. - * {@link Entities.EntityProperties-Grid|EntityProperties-Grid} * "Light"A local lighting effect. * {@link Entities.EntityProperties-Light|EntityProperties-Light} * "Zone"A volume of lighting effects and avatar permissions. @@ -102,7 +100,6 @@ public: Line, PolyLine, PolyVox, - Grid, Light, Zone, Material, diff --git a/libraries/entities/src/GridEntityItem.cpp b/libraries/entities/src/GridEntityItem.cpp deleted file mode 100644 index cc59135e8d..0000000000 --- a/libraries/entities/src/GridEntityItem.cpp +++ /dev/null @@ -1,154 +0,0 @@ -// -// Created by Sam Gondelman on 11/29/18 -// Copyright 2018 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "GridEntityItem.h" - -#include "EntityItemProperties.h" - -EntityItemPointer GridEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - Pointer entity(new GridEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); - entity->setProperties(properties); - return entity; -} - -// our non-pure virtual subclass for now... -GridEntityItem::GridEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { - _type = EntityTypes::Grid; -} - -EntityItemProperties GridEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { - EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - - //COPY_ENTITY_PROPERTY_TO_PROPERTIES(imageURL, getGridURL); - //COPY_ENTITY_PROPERTY_TO_PROPERTIES(emissive, getEmissive); - //COPY_ENTITY_PROPERTY_TO_PROPERTIES(keepAspectRatio, getKeepAspectRatio); - //COPY_ENTITY_PROPERTY_TO_PROPERTIES(faceCamera, getFaceCamera); - //COPY_ENTITY_PROPERTY_TO_PROPERTIES(subGrid, getSubGrid); - - return properties; -} - -bool GridEntityItem::setProperties(const EntityItemProperties& properties) { - bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class - - //SET_ENTITY_PROPERTY_FROM_PROPERTIES(imageURL, setGridURL); - //SET_ENTITY_PROPERTY_FROM_PROPERTIES(emissive, setEmissive); - //SET_ENTITY_PROPERTY_FROM_PROPERTIES(keepAspectRatio, setKeepAspectRatio); - //SET_ENTITY_PROPERTY_FROM_PROPERTIES(faceCamera, setFaceCamera); - //SET_ENTITY_PROPERTY_FROM_PROPERTIES(subGrid, setSubGrid); - - if (somethingChanged) { - bool wantDebug = false; - if (wantDebug) { - uint64_t now = usecTimestampNow(); - int elapsed = now - getLastEdited(); - qCDebug(entities) << "GridEntityItem::setProperties() AFTER update... edited AGO=" << elapsed << - "now=" << now << " getLastEdited()=" << getLastEdited(); - } - setLastEdited(properties.getLastEdited()); - } - return somethingChanged; -} - -int GridEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - //READ_ENTITY_PROPERTY(PROP_IMAGE_URL, QString, setGridURL); - //READ_ENTITY_PROPERTY(PROP_EMISSIVE, bool, setEmissive); - //READ_ENTITY_PROPERTY(PROP_KEEP_ASPECT_RATIO, bool, setKeepAspectRatio); - //READ_ENTITY_PROPERTY(PROP_FACE_CAMERA, bool, setFaceCamera); - //READ_ENTITY_PROPERTY(PROP_SUB_IMAGE, QRect, setSubGrid); - - return bytesRead; -} - -EntityPropertyFlags GridEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - - //requestedProperties += PROP_IMAGE_URL; - //requestedProperties += PROP_EMISSIVE; - //requestedProperties += PROP_KEEP_ASPECT_RATIO; - //requestedProperties += PROP_FACE_CAMERA; - //requestedProperties += PROP_SUB_IMAGE; - - return requestedProperties; -} - -void GridEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - //APPEND_ENTITY_PROPERTY(PROP_IMAGE_URL, getGridURL()); - //APPEND_ENTITY_PROPERTY(PROP_EMISSIVE, getEmissive()); - //APPEND_ENTITY_PROPERTY(PROP_KEEP_ASPECT_RATIO, getKeepAspectRatio()); - //APPEND_ENTITY_PROPERTY(PROP_FACE_CAMERA, getFaceCamera()); - //APPEND_ENTITY_PROPERTY(PROP_SUB_IMAGE, getSubGrid()); -} - -bool GridEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - OctreeElementPointer& element, - float& distance, BoxFace& face, glm::vec3& surfaceNormal, - QVariantMap& extraInfo, bool precisionPicking) const { - glm::vec3 dimensions = getScaledDimensions(); - glm::vec2 xyDimensions(dimensions.x, dimensions.y); - glm::quat rotation = getWorldOrientation(); - glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); - - if (findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance)) { - glm::vec3 forward = rotation * Vectors::FRONT; - if (glm::dot(forward, direction) > 0.0f) { - face = MAX_Z_FACE; - surfaceNormal = -forward; - } else { - face = MIN_Z_FACE; - surfaceNormal = forward; - } - return true; - } - return false; -} - -bool GridEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, - OctreeElementPointer& element, float& parabolicDistance, - BoxFace& face, glm::vec3& surfaceNormal, - QVariantMap& extraInfo, bool precisionPicking) const { - glm::vec3 dimensions = getScaledDimensions(); - glm::vec2 xyDimensions(dimensions.x, dimensions.y); - glm::quat rotation = getWorldOrientation(); - glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); - - glm::quat inverseRot = glm::inverse(rotation); - glm::vec3 localOrigin = inverseRot * (origin - position); - glm::vec3 localVelocity = inverseRot * velocity; - glm::vec3 localAcceleration = inverseRot * acceleration; - - if (findParabolaRectangleIntersection(localOrigin, localVelocity, localAcceleration, xyDimensions, parabolicDistance)) { - float localIntersectionVelocityZ = localVelocity.z + localAcceleration.z * parabolicDistance; - glm::vec3 forward = rotation * Vectors::FRONT; - if (localIntersectionVelocityZ > 0.0f) { - face = MIN_Z_FACE; - surfaceNormal = forward; - } else { - face = MAX_Z_FACE; - surfaceNormal = -forward; - } - return true; - } - return false; -} \ No newline at end of file diff --git a/libraries/entities/src/GridEntityItem.h b/libraries/entities/src/GridEntityItem.h deleted file mode 100644 index 4704d96f3d..0000000000 --- a/libraries/entities/src/GridEntityItem.h +++ /dev/null @@ -1,56 +0,0 @@ -// -// Created by Sam Gondelman on 11/29/18 -// Copyright 2018 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_GridEntityItem_h -#define hifi_GridEntityItem_h - -#include "EntityItem.h" - -class GridEntityItem : public EntityItem { - using Pointer = std::shared_ptr; -public: - static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); - - GridEntityItem(const EntityItemID& entityItemID); - - ALLOW_INSTANTIATION // This class can be instantiated - - // methods for getting/setting all properties of an entity - EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; - bool setProperties(const EntityItemProperties& properties) override; - - EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; - - void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const override; - - int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) override; - - virtual bool supportsDetailedIntersection() const override { return true; } - virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, - QVariantMap& extraInfo, bool precisionPicking) const override; - virtual bool findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, - const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance, - BoxFace& face, glm::vec3& surfaceNormal, - QVariantMap& extraInfo, bool precisionPicking) const override; - -protected: - -}; - -#endif // hifi_GridEntityItem_h From 090bc4fc0d268d24ca445c92b9bff472dfc955e8 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Wed, 5 Dec 2018 23:33:34 +0300 Subject: [PATCH 04/27] FB19355 - When I have avatar entities as children of my avatar, the Avatar app is not consistent in displaying wearables --- interface/resources/qml/hifi/avatarapp/AdjustWearables.qml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index 256d951a45..493bfa2a30 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -75,6 +75,10 @@ Rectangle { if(materialUrlOrJson) { wearable.text = 'Material: ' + materialUrlOrJson; } + } else if (wearable.sourceUrl) { + wearable.text = extractTitleFromUrl(wearable.sourceUrl); + } else if (wearable.name) { + wearable.text = wearable.name; } wearablesCombobox.model.append(wearable); } From abef5c4d6178b3e2ed7fa61c99df3c60ae6748d2 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 5 Dec 2018 15:05:01 -0800 Subject: [PATCH 05/27] fix grab billboarding bug --- libraries/entities/src/EntityItemProperties.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 6f451f937a..7af70b4da0 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -1843,7 +1843,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool // Handle conversions from old 'textures' property to "imageURL" { QScriptValue V = object.property("textures"); - if (V.isValid()) { + if (_type == EntityTypes::Image && V.isValid() && !object.property("imageURL").isValid()) { bool isValid = false; QString textures = QString_convertFromScriptValue(V, isValid); if (isValid) { @@ -1862,7 +1862,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool // Handle old "faceCamera" and "isFacingAvatar" props { QScriptValue P = object.property("faceCamera"); - if (P.isValid()) { + if (P.isValid() && !object.property("billboardMode").isValid()) { bool newValue = P.toVariant().toBool(); bool oldValue = getBillboardMode() == BillboardMode::YAW; if (_defaultSettings || newValue != oldValue) { @@ -1872,7 +1872,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool } { QScriptValue P = object.property("isFacingAvatar"); - if (P.isValid()) { + if (P.isValid() && !object.property("billboardMode").isValid() && !object.property("faceCamera").isValid()) { bool newValue = P.toVariant().toBool(); bool oldValue = getBillboardMode() == BillboardMode::FULL; if (_defaultSettings || newValue != oldValue) { From 645508de042037f15dbd25212e08649db1340038 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 6 Dec 2018 09:59:24 -0800 Subject: [PATCH 06/27] add jsdocs for BillboardMode --- libraries/shared/src/BillboardMode.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/libraries/shared/src/BillboardMode.h b/libraries/shared/src/BillboardMode.h index 7f3e79d453..050f939941 100644 --- a/libraries/shared/src/BillboardMode.h +++ b/libraries/shared/src/BillboardMode.h @@ -11,6 +11,21 @@ #include "QString" +/**jsdoc + *

How an entity is billboarded.

+ * + * + * + * + * + * + * + * + * + *
ValueDescription
noneThe entity will not be billboarded.
yawThe entity will yaw, but not pitch, to face the camera. Its actual rotation will be ignored.
fullThe entity will be billboarded to face the camera. Its actual rotation will be ignored.
+ * @typedef {string} BillboardMode + */ + enum class BillboardMode { NONE = 0, YAW, From 6aac596698db70e98813b3b4daaf831464e0dd3f Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 7 Dec 2018 15:25:21 -0800 Subject: [PATCH 07/27] updated entityscriptinginterface API and related functions --- interface/src/Application.cpp | 12 +- interface/src/Application.h | 2 +- interface/src/avatar/MyAvatar.cpp | 12 +- interface/src/raypick/LaserPointer.cpp | 6 +- .../raypick/LaserPointerScriptingInterface.h | 2 +- interface/src/raypick/ParabolaPick.cpp | 14 +- interface/src/raypick/PathPointer.cpp | 12 +- .../src/raypick/PickScriptingInterface.cpp | 22 +- .../src/raypick/PickScriptingInterface.h | 113 +++-- .../src/raypick/PointerScriptingInterface.h | 9 +- interface/src/raypick/RayPick.cpp | 13 +- .../src/raypick/RayPickScriptingInterface.h | 10 +- interface/src/raypick/StylusPointer.cpp | 2 +- interface/src/ui/Keyboard.cpp | 2 +- .../src/EntityTreeRenderer.cpp | 13 +- .../entities/src/EntityScriptingInterface.cpp | 122 ++---- .../entities/src/EntityScriptingInterface.h | 243 ++++++++--- libraries/entities/src/EntityTree.cpp | 408 ++++++++++-------- libraries/entities/src/EntityTree.h | 57 +-- libraries/entities/src/EntityTreeElement.cpp | 256 ++++++++--- libraries/entities/src/EntityTreeElement.h | 52 +-- libraries/pointers/src/Pick.cpp | 5 +- libraries/pointers/src/Pick.h | 79 +--- libraries/pointers/src/PickCacheOptimizer.h | 6 +- libraries/shared/src/PickFilter.h | 94 ++++ .../utilities/tests/entityPerfTest.js | 8 - .../DomainContent/Toybox/pistol/pistol.js | 2 +- 27 files changed, 915 insertions(+), 661 deletions(-) create mode 100644 libraries/shared/src/PickFilter.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0b53e24a8e..afe9cee5f9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4896,7 +4896,7 @@ void Application::calibrateEyeTracker5Points() { #endif bool Application::exportEntities(const QString& filename, - const QVector& entityIDs, + const QVector& entityIDs, const glm::vec3* givenOffset) { QHash entities; @@ -4971,16 +4971,12 @@ bool Application::exportEntities(const QString& filename, float x, float y, floa glm::vec3 minCorner = center - vec3(scale); float cubeSize = scale * 2; AACube boundingCube(minCorner, cubeSize); - QVector entities; - QVector ids; + QVector entities; auto entityTree = getEntities()->getTree(); entityTree->withReadLock([&] { - entityTree->findEntities(boundingCube, entities); - foreach(EntityItemPointer entity, entities) { - ids << entity->getEntityItemID(); - } + entityTree->evalEntitiesInCube(boundingCube, PickFilter(), entities); }); - return exportEntities(filename, ids, ¢er); + return exportEntities(filename, entities, ¢er); } void Application::loadSettings() { diff --git a/interface/src/Application.h b/interface/src/Application.h index ead7231ffc..12fe869f3b 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -345,7 +345,7 @@ signals: public slots: QVector pasteEntities(float x, float y, float z); - bool exportEntities(const QString& filename, const QVector& entityIDs, const glm::vec3* givenOffset = nullptr); + bool exportEntities(const QString& filename, const QVector& entityIDs, const glm::vec3* givenOffset = nullptr); bool exportEntities(const QString& filename, float x, float y, float z, float scale); bool importEntities(const QString& url, const bool isObservable = true, const qint64 callerId = -1); void updateThreadPoolCount() const; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 97014550a8..a2f005a8c3 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3186,17 +3186,15 @@ bool MyAvatar::requiresSafeLanding(const glm::vec3& positionIn, glm::vec3& bette OctreeElementPointer element; float distance; BoxFace face; - const bool visibleOnly = false; - // This isn't quite what we really want here. findRayIntersection always works on mesh, skipping entirely based on collidable. - // What we really want is to use the collision hull! - // See https://highfidelity.fogbugz.com/f/cases/5003/findRayIntersection-has-option-to-use-collidableOnly-but-doesn-t-actually-use-colliders - const bool collidableOnly = true; - const bool precisionPicking = true; const auto lockType = Octree::Lock; // Should we refactor to take a lock just once? bool* accurateResult = NULL; + // This isn't quite what we really want here. findRayIntersection always works on mesh, skipping entirely based on collidable. + // What we really want is to use the collision hull! + // See https://highfidelity.fogbugz.com/f/cases/5003/findRayIntersection-has-option-to-use-collidableOnly-but-doesn-t-actually-use-colliders QVariantMap extraInfo; - EntityItemID entityID = entityTree->findRayIntersection(startPointIn, directionIn, include, ignore, visibleOnly, collidableOnly, precisionPicking, + EntityItemID entityID = entityTree->evalRayIntersection(startPointIn, directionIn, include, ignore, + PickFilter(PickFilter::getBitMask(PickFilter::FlagBit::COLLIDABLE) | PickFilter::getBitMask(PickFilter::FlagBit::PRECISE)), element, distance, face, normalOut, extraInfo, lockType, accurateResult); if (entityID.isNull()) { return false; diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 1b53f9ab30..236512f2fe 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -125,7 +125,7 @@ LaserPointer::RenderState::RenderState(const OverlayID& startID, const OverlayID StartEndRenderState(startID, endID), _pathID(pathID) { if (!_pathID.isNull()) { - _pathIgnoreRays = qApp->getOverlays().getProperty(_pathID, "ignoreRayIntersection").value.toBool(); + _pathIgnoreRays = qApp->getOverlays().getProperty(_pathID, "ignorePickIntersection").value.toBool(); _lineWidth = qApp->getOverlays().getProperty(_pathID, "lineWidth").value.toFloat(); } } @@ -142,7 +142,7 @@ void LaserPointer::RenderState::disable() { if (!getPathID().isNull()) { QVariantMap pathProps; pathProps.insert("visible", false); - pathProps.insert("ignoreRayIntersection", true); + pathProps.insert("ignorePickIntersection", true); qApp->getOverlays().editOverlay(getPathID(), pathProps); } } @@ -156,7 +156,7 @@ void LaserPointer::RenderState::update(const glm::vec3& origin, const glm::vec3& pathProps.insert("start", vec3toVariant(origin)); pathProps.insert("end", endVariant); pathProps.insert("visible", true); - pathProps.insert("ignoreRayIntersection", doesPathIgnoreRays()); + pathProps.insert("ignorePickIntersection", doesPathIgnoreRays()); pathProps.insert("lineWidth", getLineWidth() * parentScale); qApp->getOverlays().editOverlay(getPathID(), pathProps); } diff --git a/interface/src/raypick/LaserPointerScriptingInterface.h b/interface/src/raypick/LaserPointerScriptingInterface.h index 5aaacd7960..d85e329e9a 100644 --- a/interface/src/raypick/LaserPointerScriptingInterface.h +++ b/interface/src/raypick/LaserPointerScriptingInterface.h @@ -21,7 +21,7 @@ class LaserPointerScriptingInterface : public QObject, public Dependency { SINGLETON_DEPENDENCY /**jsdoc - * Synonym for {@link Pointers} as used for laser pointers. + * Synonym for {@link Pointers} as used for laser pointers. Deprecated. * * @namespace LaserPointers * diff --git a/interface/src/raypick/ParabolaPick.cpp b/interface/src/raypick/ParabolaPick.cpp index 571f4a6ea6..b93ced17c6 100644 --- a/interface/src/raypick/ParabolaPick.cpp +++ b/interface/src/raypick/ParabolaPick.cpp @@ -9,6 +9,7 @@ #include "Application.h" #include "EntityScriptingInterface.h" +#include "PickScriptingInterface.h" #include "ui/overlays/Overlays.h" #include "avatar/AvatarManager.h" #include "scripting/HMDScriptingInterface.h" @@ -57,10 +58,15 @@ PickParabola ParabolaPick::getMathematicalPick() const { PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick) { if (glm::length2(pick.acceleration) > EPSILON && glm::length2(pick.velocity) > EPSILON) { - bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); + PickFilter searchFilter = getFilter(); + if (DependencyManager::get()->getForceCoarsePicking()) { + searchFilter.setFlag(PickFilter::COARSE, true); + searchFilter.setFlag(PickFilter::PRECISE, false); + } + ParabolaToEntityIntersectionResult entityRes = - DependencyManager::get()->findParabolaIntersectionVector(pick, precisionPicking, - getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); + DependencyManager::get()->evalParabolaIntersectionVector(pick, searchFilter, + getIncludeItemsAs(), getIgnoreItemsAs()); if (entityRes.intersects) { return std::make_shared(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.parabolicDistance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo); } @@ -70,7 +76,7 @@ PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick) PickResultPointer ParabolaPick::getOverlayIntersection(const PickParabola& pick) { if (glm::length2(pick.acceleration) > EPSILON && glm::length2(pick.velocity) > EPSILON) { - bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); + bool precisionPicking = !(getFilter().isCoarse() || DependencyManager::get()->getForceCoarsePicking()); ParabolaToOverlayIntersectionResult overlayRes = qApp->getOverlays().findParabolaIntersectionVector(pick, precisionPicking, getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); diff --git a/interface/src/raypick/PathPointer.cpp b/interface/src/raypick/PathPointer.cpp index 00ab32bde4..8cee6134c8 100644 --- a/interface/src/raypick/PathPointer.cpp +++ b/interface/src/raypick/PathPointer.cpp @@ -253,12 +253,12 @@ StartEndRenderState::StartEndRenderState(const OverlayID& startID, const Overlay _startID(startID), _endID(endID) { if (!_startID.isNull()) { _startDim = vec3FromVariant(qApp->getOverlays().getProperty(_startID, "dimensions").value); - _startIgnoreRays = qApp->getOverlays().getProperty(_startID, "ignoreRayIntersection").value.toBool(); + _startIgnoreRays = qApp->getOverlays().getProperty(_startID, "ignorePickIntersection").value.toBool(); } if (!_endID.isNull()) { _endDim = vec3FromVariant(qApp->getOverlays().getProperty(_endID, "dimensions").value); _endRot = quatFromVariant(qApp->getOverlays().getProperty(_endID, "rotation").value); - _endIgnoreRays = qApp->getOverlays().getProperty(_endID, "ignoreRayIntersection").value.toBool(); + _endIgnoreRays = qApp->getOverlays().getProperty(_endID, "ignorePickIntersection").value.toBool(); } } @@ -275,13 +275,13 @@ void StartEndRenderState::disable() { if (!getStartID().isNull()) { QVariantMap startProps; startProps.insert("visible", false); - startProps.insert("ignoreRayIntersection", true); + startProps.insert("ignorePickIntersection", true); qApp->getOverlays().editOverlay(getStartID(), startProps); } if (!getEndID().isNull()) { QVariantMap endProps; endProps.insert("visible", false); - endProps.insert("ignoreRayIntersection", true); + endProps.insert("ignorePickIntersection", true); qApp->getOverlays().editOverlay(getEndID(), endProps); } _enabled = false; @@ -294,7 +294,7 @@ void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end, startProps.insert("position", vec3toVariant(origin)); startProps.insert("visible", true); startProps.insert("dimensions", vec3toVariant(getStartDim() * parentScale)); - startProps.insert("ignoreRayIntersection", doesStartIgnoreRays()); + startProps.insert("ignorePickIntersection", doesStartIgnoreRays()); qApp->getOverlays().editOverlay(getStartID(), startProps); } @@ -346,7 +346,7 @@ void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end, endProps.insert("position", vec3toVariant(position)); endProps.insert("rotation", quatToVariant(rotation)); endProps.insert("visible", true); - endProps.insert("ignoreRayIntersection", doesEndIgnoreRays()); + endProps.insert("ignorePickIntersection", doesEndIgnoreRays()); qApp->getOverlays().editOverlay(getEndID(), endProps); } _enabled = true; diff --git a/interface/src/raypick/PickScriptingInterface.cpp b/interface/src/raypick/PickScriptingInterface.cpp index 6e979d2d91..e8f84e63fe 100644 --- a/interface/src/raypick/PickScriptingInterface.cpp +++ b/interface/src/raypick/PickScriptingInterface.cpp @@ -49,11 +49,17 @@ unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type, } } +PickFilter getPickFilter(unsigned int filter) { + // FIXME: Picks always intersect visible and collidable things right now + filter = filter | (PickScriptingInterface::PICK_INCLUDE_VISIBLE() | PickScriptingInterface::PICK_INCLUDE_COLLIDABLE()); + return PickFilter(filter); +} + /**jsdoc * A set of properties that can be passed to {@link Picks.createPick} to create a new Ray Pick. * @typedef {object} Picks.RayPickProperties * @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results. - * @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. + * @property {number} [filter=0] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. * @property {number} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid. * @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, an overlay, or a pick. * @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint) @@ -73,7 +79,7 @@ unsigned int PickScriptingInterface::createRayPick(const QVariant& properties) { PickFilter filter = PickFilter(); if (propMap["filter"].isValid()) { - filter = PickFilter(propMap["filter"].toUInt()); + filter = getPickFilter(propMap["filter"].toUInt()); } float maxDistance = 0.0f; @@ -111,7 +117,7 @@ unsigned int PickScriptingInterface::createRayPick(const QVariant& properties) { * @typedef {object} Picks.StylusPickProperties * @property {number} [hand=-1] An integer. 0 == left, 1 == right. Invalid otherwise. * @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results. - * @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. + * @property {number} [filter=0] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. * @property {number} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid. */ unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties) { @@ -132,7 +138,7 @@ unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties PickFilter filter = PickFilter(); if (propMap["filter"].isValid()) { - filter = PickFilter(propMap["filter"].toUInt()); + filter = getPickFilter(propMap["filter"].toUInt()); } float maxDistance = 0.0f; @@ -153,7 +159,7 @@ unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties * A set of properties that can be passed to {@link Picks.createPick} to create a new Parabola Pick. * @typedef {object} Picks.ParabolaPickProperties * @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results. - * @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. + * @property {number} [filter=0] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. * @property {number} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid. * @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, an overlay, or a pick. * @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint) @@ -178,7 +184,7 @@ unsigned int PickScriptingInterface::createParabolaPick(const QVariant& properti PickFilter filter = PickFilter(); if (propMap["filter"].isValid()) { - filter = PickFilter(propMap["filter"].toUInt()); + filter = getPickFilter(propMap["filter"].toUInt()); } float maxDistance = 0.0f; @@ -250,7 +256,7 @@ unsigned int PickScriptingInterface::createParabolaPick(const QVariant& properti * @typedef {object} Picks.CollisionPickProperties * @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results. -* @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. +* @property {number} [filter=0] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. * @property {Shape} shape - The information about the collision region's size and shape. Dimensions are in world space, but will scale with the parent if defined. * @property {Vec3} position - The position of the collision region, relative to a parent if defined. * @property {Quat} orientation - The orientation of the collision region, relative to a parent if defined. @@ -273,7 +279,7 @@ unsigned int PickScriptingInterface::createCollisionPick(const QVariant& propert PickFilter filter = PickFilter(); if (propMap["filter"].isValid()) { - filter = PickFilter(propMap["filter"].toUInt()); + filter = getPickFilter(propMap["filter"].toUInt()); } float maxDistance = 0.0f; diff --git a/interface/src/raypick/PickScriptingInterface.h b/interface/src/raypick/PickScriptingInterface.h index 94112d5fae..e795068cd3 100644 --- a/interface/src/raypick/PickScriptingInterface.h +++ b/interface/src/raypick/PickScriptingInterface.h @@ -14,6 +14,7 @@ #include #include #include +#include /**jsdoc * The Picks API lets you create and manage objects for repeatedly calculating intersections in different ways. @@ -23,41 +24,62 @@ * @hifi-interface * @hifi-client-entity * - * @property {number} PICK_NOTHING A filter flag. Don't intersect with anything. Read-only. - * @property {number} PICK_ENTITIES A filter flag. Include entities when intersecting. Read-only. - * @property {number} PICK_OVERLAYS A filter flag. Include overlays when intersecting. Read-only. - * @property {number} PICK_AVATARS A filter flag. Include avatars when intersecting. Read-only. - * @property {number} PICK_HUD A filter flag. Include the HUD sphere when intersecting in HMD mode. Read-only. - * @property {number} PICK_COARSE A filter flag. Pick against coarse meshes, instead of exact meshes. Read-only. - * @property {number} PICK_INCLUDE_INVISIBLE A filter flag. Include invisible objects when intersecting. Read-only. - * @property {number} PICK_INCLUDE_NONCOLLIDABLE A filter flag. Include non-collidable objects when intersecting. - * Read-only. - * @property {number} PICK_ALL_INTERSECTIONS Read-only. - * @property {number} INTERSECTED_NONE An intersection type. Intersected nothing with the given filter flags. - * Read-only. + * @property {number} PICK_ENTITIES A filter flag. Include domain and avatar entities when intersecting. Read-only.. Deprecated. + * @property {number} PICK_OVERLAYS A filter flag. Include local entities when intersecting. Read-only.. Deprecated. + * + * @property {number} PICK_DOMAIN_ENTITIES A filter flag. Include domain entities when intersecting. Read-only.. + * @property {number} PICK_AVATAR_ENTITIES A filter flag. Include avatar entities when intersecting. Read-only.. + * @property {number} PICK_LOCAL_ENTITIES A filter flag. Include local entities when intersecting. Read-only.. + * @property {number} PICK_AVATARS A filter flag. Include avatars when intersecting. Read-only.. + * @property {number} PICK_HUD A filter flag. Include the HUD sphere when intersecting in HMD mode. Read-only.. + * + * @property {number} PICK_INCLUDE_VISIBLE A filter flag. Include visible objects when intersecting. Read-only.. + * @property {number} PICK_INCLUDE_INVISIBLE A filter flag. Include invisible objects when intersecting. Read-only.. + * + * @property {number} PICK_INCLUDE_COLLIDABLE A filter flag. Include collidable objects when intersecting. Read-only.. + * @property {number} PICK_INCLUDE_NONCOLLIDABLE A filter flag. Include non-collidable objects when intersecting. Read-only.. + * + * @property {number} PICK_PRECISE A filter flag. Pick against exact meshes. Read-only.. + * @property {number} PICK_COARSE A filter flag. Pick against coarse meshes. Read-only.. + * + * @property {number} PICK_ALL_INTERSECTIONS Read-only.. + * + * @property {number} INTERSECTED_NONE An intersection type. Intersected nothing with the given filter flags. Read-only. * @property {number} INTERSECTED_ENTITY An intersection type. Intersected an entity. Read-only. * @property {number} INTERSECTED_OVERLAY An intersection type. Intersected an overlay. Read-only. * @property {number} INTERSECTED_AVATAR An intersection type. Intersected an avatar. Read-only. * @property {number} INTERSECTED_HUD An intersection type. Intersected the HUD sphere. Read-only. - * @property {number} perFrameTimeBudget - The max number of usec to spend per frame updating Pick results. Read-only. + * @property {number} perFrameTimeBudget - The max number of usec to spend per frame updating Pick results. */ class PickScriptingInterface : public QObject, public Dependency { Q_OBJECT - Q_PROPERTY(unsigned int PICK_NOTHING READ PICK_NOTHING CONSTANT) Q_PROPERTY(unsigned int PICK_ENTITIES READ PICK_ENTITIES CONSTANT) Q_PROPERTY(unsigned int PICK_OVERLAYS READ PICK_OVERLAYS CONSTANT) + + Q_PROPERTY(unsigned int PICK_DOMAIN_ENTITIES READ PICK_DOMAIN_ENTITIES CONSTANT) + Q_PROPERTY(unsigned int PICK_AVATAR_ENTITIES READ PICK_AVATAR_ENTITIES CONSTANT) + Q_PROPERTY(unsigned int PICK_LOCAL_ENTITIES READ PICK_LOCAL_ENTITIES CONSTANT) Q_PROPERTY(unsigned int PICK_AVATARS READ PICK_AVATARS CONSTANT) Q_PROPERTY(unsigned int PICK_HUD READ PICK_HUD CONSTANT) - Q_PROPERTY(unsigned int PICK_COARSE READ PICK_COARSE CONSTANT) + + Q_PROPERTY(unsigned int PICK_INCLUDE_VISIBLE READ PICK_INCLUDE_VISIBLE CONSTANT) Q_PROPERTY(unsigned int PICK_INCLUDE_INVISIBLE READ PICK_INCLUDE_INVISIBLE CONSTANT) + + Q_PROPERTY(unsigned int PICK_INCLUDE_COLLIDABLE READ PICK_INCLUDE_COLLIDABLE CONSTANT) Q_PROPERTY(unsigned int PICK_INCLUDE_NONCOLLIDABLE READ PICK_INCLUDE_NONCOLLIDABLE CONSTANT) + + Q_PROPERTY(unsigned int PICK_PRECISE READ PICK_PRECISE CONSTANT) + Q_PROPERTY(unsigned int PICK_COARSE READ PICK_COARSE CONSTANT) + Q_PROPERTY(unsigned int PICK_ALL_INTERSECTIONS READ PICK_ALL_INTERSECTIONS CONSTANT) + Q_PROPERTY(unsigned int INTERSECTED_NONE READ INTERSECTED_NONE CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_ENTITY READ INTERSECTED_ENTITY CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_OVERLAY READ INTERSECTED_OVERLAY CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_AVATAR READ INTERSECTED_AVATAR CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_HUD READ INTERSECTED_HUD CONSTANT) + Q_PROPERTY(unsigned int perFrameTimeBudget READ getPerFrameTimeBudget WRITE setPerFrameTimeBudget) SINGLETON_DEPENDENCY public: @@ -72,11 +94,13 @@ public: * Adds a new Pick. * Different {@link PickType}s use different properties, and within one PickType, the properties you choose can lead to a wide range of behaviors. For example, * with PickType.Ray, depending on which optional parameters you pass, you could create a Static Ray Pick, a Mouse Ray Pick, or a Joint Ray Pick. + * Picks created with this method always intersect at least visible and collidable things * @function Picks.createPick * @param {PickType} type A PickType that specifies the method of picking to use * @param {Picks.RayPickProperties|Picks.StylusPickProperties|Picks.ParabolaPickProperties|Picks.CollisionPickProperties} properties A PickProperties object, containing all the properties for initializing this Pick * @returns {number} The ID of the created Pick. Used for managing the Pick. 0 if invalid. */ + // TODO: expand Pointers to be able to be fully configurable with PickFilters Q_INVOKABLE unsigned int createPick(const PickQuery::PickType type, const QVariant& properties); /**jsdoc @@ -227,61 +251,80 @@ public: */ Q_INVOKABLE bool isMouse(unsigned int uid); - // FIXME: Move to other property definitions. - Q_PROPERTY(unsigned int perFrameTimeBudget READ getPerFrameTimeBudget WRITE setPerFrameTimeBudget) - unsigned int getPerFrameTimeBudget() const; void setPerFrameTimeBudget(unsigned int numUsecs); public slots: - /**jsdoc - * @function Picks.PICK_NOTHING - * @returns {number} - */ - static constexpr unsigned int PICK_NOTHING() { return 0; } - /**jsdoc * @function Picks.PICK_ENTITIES * @returns {number} */ - static constexpr unsigned int PICK_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_ENTITIES); } - + static constexpr unsigned int PICK_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); } /**jsdoc * @function Picks.PICK_OVERLAYS * @returns {number} */ - static constexpr unsigned int PICK_OVERLAYS() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_OVERLAYS); } + static constexpr unsigned int PICK_OVERLAYS() { return PickFilter::getBitMask(PickFilter::FlagBit::LOCAL_ENTITIES); } + /**jsdoc + * @function Picks.PICK_DOMAIN_ENTITIES + * @returns {number} + */ + static constexpr unsigned int PICK_DOMAIN_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES); } + /**jsdoc + * @function Picks.PICK_AVATAR_ENTITIES + * @returns {number} + */ + static constexpr unsigned int PICK_AVATAR_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); } + /**jsdoc + * @function Picks.PICK_LOCAL_ENTITIES + * @returns {number} + */ + static constexpr unsigned int PICK_LOCAL_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::LOCAL_ENTITIES); } /**jsdoc * @function Picks.PICK_AVATARS * @returns {number} */ - static constexpr unsigned int PICK_AVATARS() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_AVATARS); } - + static constexpr unsigned int PICK_AVATARS() { return PickFilter::getBitMask(PickFilter::FlagBit::AVATARS); } /**jsdoc * @function Picks.PICK_HUD * @returns {number} */ - static constexpr unsigned int PICK_HUD() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_HUD); } + static constexpr unsigned int PICK_HUD() { return PickFilter::getBitMask(PickFilter::FlagBit::HUD); } /**jsdoc - * @function Picks.PICK_COARSE + * @function Picks.PICK_INCLUDE_VISIBLE * @returns {number} */ - static constexpr unsigned int PICK_COARSE() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_COARSE); } - + static constexpr unsigned int PICK_INCLUDE_VISIBLE() { return PickFilter::getBitMask(PickFilter::FlagBit::VISIBLE); } /**jsdoc * @function Picks.PICK_INCLUDE_INVISIBLE * @returns {number} */ - static constexpr unsigned int PICK_INCLUDE_INVISIBLE() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_INCLUDE_INVISIBLE); } + static constexpr unsigned int PICK_INCLUDE_INVISIBLE() { return PickFilter::getBitMask(PickFilter::FlagBit::INVISIBLE); } + /**jsdoc + * @function Picks.PICK_INCLUDE_COLLIDABLE + * @returns {number} + */ + static constexpr unsigned int PICK_INCLUDE_COLLIDABLE() { return PickFilter::getBitMask(PickFilter::FlagBit::COLLIDABLE); } /**jsdoc * @function Picks.PICK_INCLUDE_NONCOLLIDABLE * @returns {number} */ - static constexpr unsigned int PICK_INCLUDE_NONCOLLIDABLE() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_INCLUDE_NONCOLLIDABLE); } + static constexpr unsigned int PICK_INCLUDE_NONCOLLIDABLE() { return PickFilter::getBitMask(PickFilter::FlagBit::NONCOLLIDABLE); } + + /**jsdoc + * @function Picks.PICK_PRECISE + * @returns {number} + */ + static constexpr unsigned int PICK_PRECISE() { return PickFilter::getBitMask(PickFilter::FlagBit::PRECISE); } + /**jsdoc + * @function Picks.PICK_COARSE + * @returns {number} + */ + static constexpr unsigned int PICK_COARSE() { return PickFilter::getBitMask(PickFilter::FlagBit::COARSE); } /**jsdoc * @function Picks.PICK_ALL_INTERSECTIONS diff --git a/interface/src/raypick/PointerScriptingInterface.h b/interface/src/raypick/PointerScriptingInterface.h index 2677f37fae..a21c1f2470 100644 --- a/interface/src/raypick/PointerScriptingInterface.h +++ b/interface/src/raypick/PointerScriptingInterface.h @@ -41,10 +41,12 @@ public: * @property {string} button Which button to trigger. "Primary", "Secondary", "Tertiary", and "Focus" are currently supported. Only "Primary" will trigger clicks on web surfaces. If "Focus" is triggered, * it will try to set the entity or overlay focus to the object at which the Pointer is aimed. Buttons besides the first three will still trigger events, but event.button will be "None". */ + /**jsdoc * Adds a new Pointer * Different {@link PickType}s use different properties, and within one PickType, the properties you choose can lead to a wide range of behaviors. For example, * with PickType.Ray, depending on which optional parameters you pass, you could create a Static Ray Pointer, a Mouse Ray Pointer, or a Joint Ray Pointer. + * Pointers created with this method always intersect at least visible and collidable things * @function Pointers.createPointer * @param {PickType} type A PickType that specifies the method of picking to use * @param {Pointers.LaserPointerProperties|Pointers.StylusPointerProperties|Pointers.ParabolaPointerProperties} properties A PointerProperties object, containing all the properties for initializing this Pointer and the {@link Picks.PickProperties} for the Pick that @@ -58,21 +60,21 @@ public: * dimensions: {x:0.5, y:0.5, z:0.5}, * solid: true, * color: {red:0, green:255, blue:0}, - * ignoreRayIntersection: true + * ignorePickIntersection: true * }; * var end2 = { * type: "sphere", * dimensions: {x:0.5, y:0.5, z:0.5}, * solid: true, * color: {red:255, green:0, blue:0}, - * ignoreRayIntersection: true + * ignorePickIntersection: true * }; * * var renderStates = [ {name: "test", end: end} ]; * var defaultRenderStates = [ {name: "test", distance: 10.0, end: end2} ]; * var pointer = Pointers.createPointer(PickType.Ray, { * joint: "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", - * filter: Picks.PICK_OVERLAYS | Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_NONCOLLIDABLE, + * filter: Picks.PICK_LOCAL_ENTITIES | Picks.PICK_DOMAIN_ENTITIES | Picks.PICK_INCLUDE_NONCOLLIDABLE, * renderStates: renderStates, * defaultRenderStates: defaultRenderStates, * distanceScaleEnd: true, @@ -82,6 +84,7 @@ public: * }); * Pointers.setRenderState(pointer, "test"); */ + // TODO: expand Pointers to be able to be fully configurable with PickFilters Q_INVOKABLE unsigned int createPointer(const PickQuery::PickType& type, const QVariant& properties); /**jsdoc diff --git a/interface/src/raypick/RayPick.cpp b/interface/src/raypick/RayPick.cpp index a48d858504..507e45b470 100644 --- a/interface/src/raypick/RayPick.cpp +++ b/interface/src/raypick/RayPick.cpp @@ -27,10 +27,15 @@ PickRay RayPick::getMathematicalPick() const { } PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) { - bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); + PickFilter searchFilter = getFilter(); + if (DependencyManager::get()->getForceCoarsePicking()) { + searchFilter.setFlag(PickFilter::COARSE, true); + searchFilter.setFlag(PickFilter::PRECISE, false); + } + RayToEntityIntersectionResult entityRes = - DependencyManager::get()->findRayIntersectionVector(pick, precisionPicking, - getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); + DependencyManager::get()->evalRayIntersectionVector(pick, searchFilter, + getIncludeItemsAs(), getIgnoreItemsAs()); if (entityRes.intersects) { return std::make_shared(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo); } else { @@ -39,7 +44,7 @@ PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) { } PickResultPointer RayPick::getOverlayIntersection(const PickRay& pick) { - bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); + bool precisionPicking = !(getFilter().isCoarse() || DependencyManager::get()->getForceCoarsePicking()); RayToOverlayIntersectionResult overlayRes = qApp->getOverlays().findRayIntersectionVector(pick, precisionPicking, getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); diff --git a/interface/src/raypick/RayPickScriptingInterface.h b/interface/src/raypick/RayPickScriptingInterface.h index d5e224018e..3ad0efd439 100644 --- a/interface/src/raypick/RayPickScriptingInterface.h +++ b/interface/src/raypick/RayPickScriptingInterface.h @@ -19,14 +19,13 @@ #include "PickScriptingInterface.h" /**jsdoc - * Synonym for {@link Picks} as used for ray picks. + * Synonym for {@link Picks} as used for ray picks. Deprecated. * * @namespace RayPick * * @hifi-interface * @hifi-client-entity * - * @property {number} PICK_NOTHING Read-only. * @property {number} PICK_ENTITIES Read-only. * @property {number} PICK_OVERLAYS Read-only. * @property {number} PICK_AVATARS Read-only. @@ -44,7 +43,6 @@ class RayPickScriptingInterface : public QObject, public Dependency { Q_OBJECT - Q_PROPERTY(unsigned int PICK_NOTHING READ PICK_NOTHING CONSTANT) Q_PROPERTY(unsigned int PICK_ENTITIES READ PICK_ENTITIES CONSTANT) Q_PROPERTY(unsigned int PICK_OVERLAYS READ PICK_OVERLAYS CONSTANT) Q_PROPERTY(unsigned int PICK_AVATARS READ PICK_AVATARS CONSTANT) @@ -140,12 +138,6 @@ public: public slots: - /**jsdoc - * @function RayPick.PICK_NOTHING - * @returns {number} - */ - static unsigned int PICK_NOTHING() { return PickScriptingInterface::PICK_NOTHING(); } - /**jsdoc * @function RayPick.PICK_ENTITIES * @returns {number} diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index 5595c54b71..867f896763 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -61,7 +61,7 @@ OverlayID StylusPointer::buildStylusOverlay(const QVariantMap& properties) { overlayProperties["loadPriority"] = 10.0f; overlayProperties["solid"] = true; overlayProperties["visible"] = false; - overlayProperties["ignoreRayIntersection"] = true; + overlayProperties["ignorePickIntersection"] = true; overlayProperties["drawInFront"] = false; return qApp->getOverlays().addOverlay("model", overlayProperties); diff --git a/interface/src/ui/Keyboard.cpp b/interface/src/ui/Keyboard.cpp index d647851a80..ebf9473868 100644 --- a/interface/src/ui/Keyboard.cpp +++ b/interface/src/ui/Keyboard.cpp @@ -742,7 +742,7 @@ void Keyboard::loadKeyboardFile(const QString& keyboardFile) { { "isSolid", true }, { "visible", false }, { "grabbable", true }, - { "ignoreRayIntersection", false }, + { "ignorePickIntersection", false }, { "dimensions", anchorObject["dimensions"].toVariant() }, { "position", anchorObject["position"].toVariant() }, { "orientation", anchorObject["rotation"].toVariant() } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 980ff8834c..adfd5e732a 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -498,20 +498,27 @@ void EntityTreeRenderer::handleSpaceUpdate(std::pair proxyUp bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QVector* entitiesContainingAvatar) { bool didUpdate = false; float radius = 0.01f; // for now, assume 0.01 meter radius, because we actually check the point inside later - QVector foundEntities; + QVector entityIDs; // find the entities near us // don't let someone else change our tree while we search _tree->withReadLock([&] { + auto entityTree = std::static_pointer_cast(_tree); // FIXME - if EntityTree had a findEntitiesContainingPoint() this could theoretically be a little faster - std::static_pointer_cast(_tree)->findEntities(_avatarPosition, radius, foundEntities); + entityTree->evalEntitiesInSphere(_avatarPosition, radius, + PickFilter(PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES)), entityIDs); LayeredZones oldLayeredZones(std::move(_layeredZones)); _layeredZones.clear(); // create a list of entities that actually contain the avatar's position - for (auto& entity : foundEntities) { + for (auto& entityID : entityIDs) { + auto entity = entityTree->findEntityByID(entityID); + if (!entity) { + continue; + } + auto isZone = entity->getType() == EntityTypes::Zone; auto hasScript = !entity->getScript().isEmpty(); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 4a634899c4..432ac2ad49 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1062,18 +1062,14 @@ void EntityScriptingInterface::handleEntityScriptCallMethodPacket(QSharedPointer -QUuid EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float radius) const { +QUuid EntityScriptingInterface::evalClosestEntity(const glm::vec3& center, float radius, unsigned int searchFilter) const { PROFILE_RANGE(script_entities, __FUNCTION__); EntityItemID result; if (_entityTree) { - EntityItemPointer closestEntity; _entityTree->withReadLock([&] { - closestEntity = _entityTree->findClosestEntity(center, radius); + result = _entityTree->evalClosestEntity(center, radius, PickFilter(searchFilter)); }); - if (closestEntity) { - result = closestEntity->getEntityItemID(); - } } return result; } @@ -1087,42 +1083,32 @@ void EntityScriptingInterface::dumpTree() const { } } -QVector EntityScriptingInterface::findEntities(const glm::vec3& center, float radius) const { +QVector EntityScriptingInterface::evalEntitiesInSphere(const glm::vec3& center, float radius, unsigned int searchFilter) const { PROFILE_RANGE(script_entities, __FUNCTION__); QVector result; if (_entityTree) { - QVector entities; _entityTree->withReadLock([&] { - _entityTree->findEntities(center, radius, entities); + _entityTree->evalEntitiesInSphere(center, radius, PickFilter(searchFilter), result); }); - - foreach (EntityItemPointer entity, entities) { - result << entity->getEntityItemID(); - } } return result; } -QVector EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const { +QVector EntityScriptingInterface::evalEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions, unsigned int searchFilter) const { PROFILE_RANGE(script_entities, __FUNCTION__); QVector result; if (_entityTree) { - QVector entities; _entityTree->withReadLock([&] { AABox box(corner, dimensions); - _entityTree->findEntities(box, entities); + _entityTree->evalEntitiesInBox(box, PickFilter(searchFilter), result); }); - - foreach (EntityItemPointer entity, entities) { - result << entity->getEntityItemID(); - } } return result; } -QVector EntityScriptingInterface::findEntitiesInFrustum(QVariantMap frustum) const { +QVector EntityScriptingInterface::evalEntitiesInFrustum(QVariantMap frustum, unsigned int searchFilter) const { PROFILE_RANGE(script_entities, __FUNCTION__); QVector result; @@ -1152,105 +1138,62 @@ QVector EntityScriptingInterface::findEntitiesInFrustum(QVariantMap frust viewFrustum.calculate(); if (_entityTree) { - QVector entities; _entityTree->withReadLock([&] { - _entityTree->findEntities(viewFrustum, entities); + _entityTree->evalEntitiesInFrustum(viewFrustum, PickFilter(searchFilter), result); }); - - foreach(EntityItemPointer entity, entities) { - result << entity->getEntityItemID(); - } } } return result; } -QVector EntityScriptingInterface::findEntitiesByType(const QString entityType, const glm::vec3& center, float radius) const { +QVector EntityScriptingInterface::evalEntitiesInSphereWithType(const QString entityType, const glm::vec3& center, float radius, unsigned int searchFilter) const { EntityTypes::EntityType type = EntityTypes::getEntityTypeFromName(entityType); QVector result; if (_entityTree) { - QVector entities; _entityTree->withReadLock([&] { - _entityTree->findEntities(center, radius, entities); + _entityTree->evalEntitiesInSphereWithType(center, radius, type, PickFilter(searchFilter), result); }); - - foreach(EntityItemPointer entity, entities) { - if (entity->getType() == type) { - result << entity->getEntityItemID().toString(); - } - } } return result; } -QVector EntityScriptingInterface::findEntitiesByName(const QString entityName, const glm::vec3& center, float radius, bool caseSensitiveSearch) const { - +QVector EntityScriptingInterface::evalEntitiesInSphereWithName(const QString entityName, const glm::vec3& center, float radius, bool caseSensitiveSearch, unsigned int searchFilter) const { QVector result; if (_entityTree) { - QVector entities; _entityTree->withReadLock([&] { - _entityTree->findEntities(center, radius, entities); + _entityTree->evalEntitiesInSphereWithName(center, radius, entityName, caseSensitiveSearch, PickFilter(searchFilter), result); }); - - if (caseSensitiveSearch) { - foreach(EntityItemPointer entity, entities) { - if (entity->getName() == entityName) { - result << entity->getEntityItemID(); - } - } - - } else { - QString entityNameLowerCase = entityName.toLower(); - - foreach(EntityItemPointer entity, entities) { - QString entityItemLowerCase = entity->getName().toLower(); - if (entityItemLowerCase == entityNameLowerCase) { - result << entity->getEntityItemID(); - } - } - } } return result; } -RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, - const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) { +RayToEntityIntersectionResult EntityScriptingInterface::evalRayIntersection(const PickRay& ray, unsigned int searchFilter, + const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard) { + PROFILE_RANGE(script_entities, __FUNCTION__); QVector entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude); QVector entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard); - return findRayIntersectionVector(ray, precisionPicking, entitiesToInclude, entitiesToDiscard, visibleOnly, collidableOnly); + return evalRayIntersectionWorker(ray, Octree::Lock, PickFilter(searchFilter), entitiesToInclude, entitiesToDiscard); } -RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionVector(const PickRay& ray, bool precisionPicking, - const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) { +RayToEntityIntersectionResult EntityScriptingInterface::evalRayIntersectionVector(const PickRay& ray, PickFilter searchFilter, + const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard) { PROFILE_RANGE(script_entities, __FUNCTION__); - return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly); + return evalRayIntersectionWorker(ray, Octree::Lock, searchFilter, entityIdsToInclude, entityIdsToDiscard); } -// FIXME - we should remove this API and encourage all users to use findRayIntersection() instead. We've changed -// findRayIntersection() to be blocking because it never makes sense for a script to get back a non-answer -RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking, - const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard) { - - qWarning() << "Entities.findRayIntersectionBlocking() is obsolete, use Entities.findRayIntersection() instead."; - const QVector& entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude); - const QVector entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard); - return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entitiesToInclude, entitiesToDiscard); -} - -RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorker(const PickRay& ray, - Octree::lockType lockType, bool precisionPicking, const QVector& entityIdsToInclude, - const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) { - +RayToEntityIntersectionResult EntityScriptingInterface::evalRayIntersectionWorker(const PickRay& ray, + Octree::lockType lockType, PickFilter searchFilter, const QVector& entityIdsToInclude, + const QVector& entityIdsToDiscard) { RayToEntityIntersectionResult result; if (_entityTree) { OctreeElementPointer element; - result.entityID = _entityTree->findRayIntersection(ray.origin, ray.direction, - entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly, precisionPicking, + result.entityID = _entityTree->evalRayIntersection(ray.origin, ray.direction, + entityIdsToInclude, entityIdsToDiscard, searchFilter, element, result.distance, result.face, result.surfaceNormal, result.extraInfo, lockType, &result.accurate); result.intersects = !result.entityID.isNull(); @@ -1261,23 +1204,22 @@ RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorke return result; } -ParabolaToEntityIntersectionResult EntityScriptingInterface::findParabolaIntersectionVector(const PickParabola& parabola, bool precisionPicking, - const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) { +ParabolaToEntityIntersectionResult EntityScriptingInterface::evalParabolaIntersectionVector(const PickParabola& parabola, PickFilter searchFilter, + const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard) { PROFILE_RANGE(script_entities, __FUNCTION__); - return findParabolaIntersectionWorker(parabola, Octree::Lock, precisionPicking, entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly); + return evalParabolaIntersectionWorker(parabola, Octree::Lock, searchFilter, entityIdsToInclude, entityIdsToDiscard); } -ParabolaToEntityIntersectionResult EntityScriptingInterface::findParabolaIntersectionWorker(const PickParabola& parabola, - Octree::lockType lockType, bool precisionPicking, const QVector& entityIdsToInclude, - const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) { - +ParabolaToEntityIntersectionResult EntityScriptingInterface::evalParabolaIntersectionWorker(const PickParabola& parabola, + Octree::lockType lockType, PickFilter searchFilter, const QVector& entityIdsToInclude, + const QVector& entityIdsToDiscard) { ParabolaToEntityIntersectionResult result; if (_entityTree) { OctreeElementPointer element; - result.entityID = _entityTree->findParabolaIntersection(parabola, - entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly, precisionPicking, + result.entityID = _entityTree->evalParabolaIntersection(parabola, + entityIdsToInclude, entityIdsToDiscard, searchFilter, element, result.intersection, result.distance, result.parabolicDistance, result.face, result.surfaceNormal, result.extraInfo, lockType, &result.accurate); result.intersects = !result.entityID.isNull(); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index ff1149fb06..9b1dbfe330 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "PolyVoxEntityItem.h" #include "LineEntityItem.h" @@ -56,8 +57,7 @@ private: }; /**jsdoc - * The result of a {@link PickRay} search using {@link Entities.findRayIntersection|findRayIntersection} or - * {@link Entities.findRayIntersectionBlocking|findRayIntersectionBlocking}. + * The result of a {@link PickRay} search using {@link Entities.evalRayIntersection|evalRayIntersection}. * @typedef {object} Entities.RayToEntityIntersectionResult * @property {boolean} intersects - true if the {@link PickRay} intersected an entity, otherwise * false. @@ -119,7 +119,6 @@ public: /// handles scripting of Entity commands from JS passed to assigned clients class EntityScriptingInterface : public OctreeScriptingInterface, public Dependency { Q_OBJECT - Q_PROPERTY(QUuid keyboardFocusEntity READ getKeyboardFocusEntity WRITE setKeyboardFocusEntity) friend EntityPropertyMetadataRequest; @@ -144,10 +143,10 @@ public: void resetActivityTracking(); ActivityTracking getActivityTracking() const { return _activityTracking; } - // TODO: expose to script? - ParabolaToEntityIntersectionResult findParabolaIntersectionVector(const PickParabola& parabola, bool precisionPicking, - const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, - bool visibleOnly, bool collidableOnly); + RayToEntityIntersectionResult evalRayIntersectionVector(const PickRay& ray, PickFilter searchFilter, + const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard); + ParabolaToEntityIntersectionResult evalParabolaIntersectionVector(const PickParabola& parabola, PickFilter searchFilter, + const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard); /**jsdoc * Get the properties of multiple entities. @@ -394,9 +393,23 @@ public slots: Q_INVOKABLE void callEntityClientMethod(QUuid clientSessionID, QUuid entityID, const QString& method, const QStringList& params = QStringList()); + /**jsdoc + * Find the entity with a position closest to a specified point and within a specified radius that matches the search filter. + * @function Entities.evalClosestEntity + * @param {Vec3} center - The point about which to search. + * @param {number} radius - The radius within which to search. + * @param {number} searchFilter - The search filter, constructed using filter flags combined using bitwise OR. + * @returns {Uuid} The ID of the entity that is closest to the center and within the radius if + * there is one, otherwise null. + * @example Find the closest visible avatar entity within 10m of your avatar. + * var entityID = Entities.evalClosestEntity(MyAvatar.position, 10, Picks.PICK_AVATAR_ENTITIES | Picks.PICK_INCLUDE_VISIBLE); + * print("Closest visible avatar entity: " + entityID); + */ + /// this function will not find any models in script engine contexts which don't have access to models + Q_INVOKABLE QUuid evalClosestEntity(const glm::vec3& center, float radius, unsigned int searchFilter) const; /**jsdoc - * Find the entity with a position closest to a specified point and within a specified radius. + * Find the non-local entity with a position closest to a specified point and within a specified radius. * @function Entities.findClosestEntity * @param {Vec3} center - The point about which to search. * @param {number} radius - The radius within which to search. @@ -407,10 +420,27 @@ public slots: * print("Closest entity: " + entityID); */ /// this function will not find any models in script engine contexts which don't have access to models - Q_INVOKABLE QUuid findClosestEntity(const glm::vec3& center, float radius) const; + Q_INVOKABLE QUuid findClosestEntity(const glm::vec3& center, float radius) const { + return evalClosestEntity(center, radius, PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES)); + } /**jsdoc - * Find all entities that intersect a sphere defined by a center point and radius. + * Find all entities that intersect a sphere defined by a center point and radius that match the search filter. + * @function Entities.evalEntitiesInRadius + * @param {Vec3} center - The point about which to search. + * @param {number} radius - The radius within which to search. + * @param {number} searchFilter - The search filter, constructed using filter flags combined using bitwise OR. + * @returns {Uuid[]} An array of entity IDs that were found that intersect the search sphere. The array is empty if no + * entities could be found. + * @example Report how many visible domain entities are within 10m of your avatar. + * var entityIDs = Entities.evalEntitiesInRadius(MyAvatar.position, 10, Picks.PICK_DOMAIN_ENTITIES | Picks.PICK_INCLUDE_VISIBLE); + * print("Number of visible domain entities within 10m: " + entityIDs.length); + */ + /// this function will not find any models in script engine contexts which don't have access to models + Q_INVOKABLE QVector evalEntitiesInSphere(const glm::vec3& center, float radius, unsigned int searchFilter) const; + + /**jsdoc + * Find all non-local entities that intersect a sphere defined by a center point and radius. * @function Entities.findEntities * @param {Vec3} center - The point about which to search. * @param {number} radius - The radius within which to search. @@ -421,51 +451,120 @@ public slots: * print("Number of entities within 10m: " + entityIDs.length); */ /// this function will not find any models in script engine contexts which don't have access to models - Q_INVOKABLE QVector findEntities(const glm::vec3& center, float radius) const; + Q_INVOKABLE QVector findEntities(const glm::vec3& center, float radius) const { + return evalEntitiesInSphere(center, radius, PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES)); + } /**jsdoc * Find all entities whose axis-aligned boxes intersect a search axis-aligned box defined by its minimum coordinates corner + * and dimensions that match the search filter. + * @function Entities.evalEntitiesInBox + * @param {Vec3} corner - The corner of the search AA box with minimum co-ordinate values. + * @param {Vec3} dimensions - The dimensions of the search AA box. + * @param {number} searchFilter - The search filter, constructed using filter flags combined using bitwise OR. + * @returns {Uuid[]} An array of entity IDs whose AA boxes intersect the search AA box. The array is empty if no entities + * could be found. + */ + /// this function will not find any models in script engine contexts which don't have access to models + Q_INVOKABLE QVector evalEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions, unsigned int searchFilter) const; + + /**jsdoc + * Find all non-local entities whose axis-aligned boxes intersect a search axis-aligned box defined by its minimum coordinates corner * and dimensions. * @function Entities.findEntitiesInBox * @param {Vec3} corner - The corner of the search AA box with minimum co-ordinate values. * @param {Vec3} dimensions - The dimensions of the search AA box. - * @returns {Uuid[]} An array of entity IDs whose AA boxes intersect the search AA box. The array is empty if no entities + * @returns {Uuid[]} An array of entity IDs whose AA boxes intersect the search AA box. The array is empty if no entities * could be found. */ /// this function will not find any models in script engine contexts which don't have access to models - Q_INVOKABLE QVector findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const; + Q_INVOKABLE QVector findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const { + return evalEntitiesInBox(corner, dimensions, PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES)); + } /**jsdoc - * Find all entities whose axis-aligned boxes intersect a search frustum. + * Find all entities whose axis-aligned boxes intersect a search frustum that match the search filter. + * @function Entities.evalEntitiesInFrustum + * @param {ViewFrustum} frustum - The frustum to search in. The position, orientation, + * projection, and centerRadius properties must be specified. + * @param {number} searchFilter - The search filter, constructed using filter flags combined using bitwise OR. + * @returns {Uuid[]} An array of entity IDs axis-aligned boxes intersect the frustum. The array is empty if no entities + * could be found. + * @example Report the number of visible, collidable entities in view. + * var entityIDs = Entities.evalEntitiesInFrustum(Camera.frustum, Picks.PICK_INCLUDE_COLLIDABLE | Picks.PICK_INCLUDE_VISIBLE); + * print("Number of visible, collidable entities in view: " + entityIDs.length); + */ + /// this function will not find any models in script engine contexts which don't have access to entities + Q_INVOKABLE QVector evalEntitiesInFrustum(QVariantMap frustum, unsigned int searchFilter) const; + + /**jsdoc + * Find all non-local entities whose axis-aligned boxes intersect a search frustum. * @function Entities.findEntitiesInFrustum * @param {ViewFrustum} frustum - The frustum to search in. The position, orientation, * projection, and centerRadius properties must be specified. - * @returns {Uuid[]} An array of entity IDs axis-aligned boxes intersect the frustum. The array is empty if no entities + * @returns {Uuid[]} An array of entity IDs axis-aligned boxes intersect the frustum. The array is empty if no entities * could be found. * @example Report the number of entities in view. * var entityIDs = Entities.findEntitiesInFrustum(Camera.frustum); * print("Number of entities in view: " + entityIDs.length); */ /// this function will not find any models in script engine contexts which don't have access to entities - Q_INVOKABLE QVector findEntitiesInFrustum(QVariantMap frustum) const; + Q_INVOKABLE QVector findEntitiesInFrustum(QVariantMap frustum) const { + return evalEntitiesInFrustum(frustum, PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES)); + } /**jsdoc - * Find all entities of a particular type that intersect a sphere defined by a center point and radius. + * Find all entities of a particular type that intersect a sphere defined by a center point and radius that match the search filter. + * @function Entities.evalEntitiesInRadiusWithType + * @param {Entities.EntityType} entityType - The type of entity to search for. + * @param {Vec3} center - The point about which to search. + * @param {number} radius - The radius within which to search. + * @param {number} searchFilter - The search filter, constructed using filter flags combined using bitwise OR. + * @returns {Uuid[]} An array of entity IDs of the specified type that intersect the search sphere. The array is empty if + * no entities could be found. + * @example Report the number of visible Model entities within 10m of your avatar. + * var entityIDs = Entities.evalEntitiesInRadiusWithType("Model", MyAvatar.position, 10, Picks.PICK_INCLUDE_VISIBLE); + * print("Number of visible Model entities within 10m: " + entityIDs.length); + */ + /// this function will not find any entities in script engine contexts which don't have access to entities + Q_INVOKABLE QVector evalEntitiesInSphereWithType(const QString entityType, const glm::vec3& center, float radius, unsigned int searchFilter) const; + + /**jsdoc + * Find all non-local entities of a particular type that intersect a sphere defined by a center point and radius. * @function Entities.findEntitiesByType * @param {Entities.EntityType} entityType - The type of entity to search for. * @param {Vec3} center - The point about which to search. * @param {number} radius - The radius within which to search. - * @returns {Uuid[]} An array of entity IDs of the specified type that intersect the search sphere. The array is empty if + * @returns {Uuid[]} An array of entity IDs of the specified type that intersect the search sphere. The array is empty if * no entities could be found. * @example Report the number of Model entities within 10m of your avatar. * var entityIDs = Entities.findEntitiesByType("Model", MyAvatar.position, 10); * print("Number of Model entities within 10m: " + entityIDs.length); */ /// this function will not find any entities in script engine contexts which don't have access to entities - Q_INVOKABLE QVector findEntitiesByType(const QString entityType, const glm::vec3& center, float radius) const; + Q_INVOKABLE QVector findEntitiesByType(const QString entityType, const glm::vec3& center, float radius) const { + return evalEntitiesInSphereWithType(entityType, center, radius, PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES)); + } + + /**jsdoc + * Find all entities with a particular name that intersect a sphere defined by a center point and radius and match the search filter. + * @function Entities.findEntitiesByName + * @param {string} entityName - The name of the entity to search for. + * @param {Vec3} center - The point about which to search. + * @param {number} radius - The radius within which to search. + * @param {boolean} caseSensitive - If true then the search is case-sensitive. + * @param {number} searchFilter - The search filter, constructed using filter flags combined using bitwise OR. + * @returns {Uuid[]} An array of entity IDs that have the specified name and intersect the search sphere. The array is empty + * if no entities could be found. + * @example Report the number of collidable entities with the name, "Light-Target". + * var entityIDs = Entities.evalEntitiesInRadiusByName("Light-Target", MyAvatar.position, 10, false, Picks.PICK_INCLUDE_COLLIDABLE); + * print("Number of collidable entities with the name Light-Target: " + entityIDs.length); + */ + Q_INVOKABLE QVector evalEntitiesInSphereWithName(const QString entityName, const glm::vec3& center, float radius, + bool caseSensitiveSearch, unsigned int searchFilter) const; /**jsdoc - * Find all entities of a particular name that intersect a sphere defined by a center point and radius. + * Find all non-local entities with a particular name that intersect a sphere defined by a center point and radius. * @function Entities.findEntitiesByName * @param {string} entityName - The name of the entity to search for. * @param {Vec3} center - The point about which to search. @@ -475,13 +574,44 @@ public slots: * if no entities could be found. * @example Report the number of entities with the name, "Light-Target". * var entityIDs = Entities.findEntitiesByName("Light-Target", MyAvatar.position, 10, false); - * print("Number of entities with the name "Light-Target": " + entityIDs.length); + * print("Number of entities with the name Light-Target: " + entityIDs.length); */ - Q_INVOKABLE QVector findEntitiesByName(const QString entityName, const glm::vec3& center, float radius, - bool caseSensitiveSearch = false ) const; + Q_INVOKABLE QVector findEntitiesByName(const QString entityName, const glm::vec3& center, float radius, + bool caseSensitiveSearch = false) const { + return evalEntitiesInSphereWithName(entityName, center, radius, caseSensitiveSearch, PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES)); + } /**jsdoc - * Find the first entity intersected by a {@link PickRay}. Light and Zone entities are not + * Find the first entity intersected by a {@link PickRay} that matches the search filter. Light and Zone entities are not + * intersected unless they've been configured as pickable using {@link Entities.setLightsArePickable|setLightsArePickable} + * and {@link Entities.setZonesArePickable|setZonesArePickable}, respectively.
+ * @function Entities.evalRayIntersection + * @param {PickRay} pickRay - The PickRay to use for finding entities. + * @param {number} searchFilter - The search filter, constructed using filter flags combined using bitwise OR. + * @param {Uuid[]} [entitiesToInclude=[]] - If not empty then the search is restricted to these entities. + * @param {Uuid[]} [entitiesToDiscard=[]] - Entities to ignore during the search. + * @returns {Entities.RayToEntityIntersectionResult} The result of the search for the first intersected entity. + * @example Find the entity directly in front of your avatar. + * var pickRay = { + * origin: MyAvatar.position, + * direction: Quat.getFront(MyAvatar.orientation) + * }; + * + * var intersection = Entities.evalRayIntersection(pickRay, Picks.PICK_PRECISE); + * if (intersection.intersects) { + * print("Entity in front of avatar: " + intersection.entityID); + * } else { + * print("No entity in front of avatar."); + * } + */ + /// If the scripting context has visible entities, this will determine a ray intersection, the results + /// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate + /// will be false. + Q_INVOKABLE RayToEntityIntersectionResult evalRayIntersection(const PickRay& ray, unsigned int searchFilter, + const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue()); + + /**jsdoc + * Find the first non-local entity intersected by a {@link PickRay}. Light and Zone entities are not * intersected unless they've been configured as pickable using {@link Entities.setLightsArePickable|setLightsArePickable} * and {@link Entities.setZonesArePickable|setZonesArePickable}, respectively.
* @function Entities.findRayIntersection @@ -512,33 +642,24 @@ public slots: /// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate /// will be false. Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false, - const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue(), - bool visibleOnly = false, bool collidableOnly = false); + const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue(), + bool visibleOnly = false, bool collidableOnly = false) { + unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); - /// Same as above but with QVectors - RayToEntityIntersectionResult findRayIntersectionVector(const PickRay& ray, bool precisionPicking, - const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, - bool visibleOnly, bool collidableOnly); + if (!precisionPicking) { + searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::COARSE); + } - /**jsdoc - * Find the first entity intersected by a {@link PickRay}. Light and Zone entities are not - * intersected unless they've been configured as pickable using {@link Entities.setLightsArePickable|setLightsArePickable} - * and {@link Entities.setZonesArePickable|setZonesArePickable}, respectively.
- * This is a synonym for {@link Entities.findRayIntersection|findRayIntersection}. - * @function Entities.findRayIntersectionBlocking - * @param {PickRay} pickRay - The PickRay to use for finding entities. - * @param {boolean} [precisionPicking=false] - If true and the intersected entity is a Model - * entity, the result's extraInfo property includes more information than it otherwise would. - * @param {Uuid[]} [entitiesToInclude=[]] - If not empty then the search is restricted to these entities. - * @param {Uuid[]} [entitiesToDiscard=[]] - Entities to ignore during the search. - * @deprecated This function is deprecated and will soon be removed. Use - * {@link Entities.findRayIntersection|findRayIntersection} instead; it blocks and performs the same function. - */ - /// If the scripting context has visible entities, this will determine a ray intersection, and will block in - /// order to return an accurate result - Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, - const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue()); + if (visibleOnly) { + searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::VISIBLE); + } + if (collidableOnly) { + searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::COLLIDABLE); + } + + return evalRayIntersection(ray, searchFilter, entityIdsToInclude, entityIdsToDiscard); + } /**jsdoc * Reloads an entity's server entity script such that the latest version re-downloaded. @@ -603,9 +724,7 @@ public slots: /**jsdoc * Set whether or not ray picks intersect the bounding box of {@link Entities.EntityType|Light} entities. By default, Light * entities are not intersected. The setting lasts for the Interface session. Ray picks are done using - * {@link Entities.findRayIntersection|findRayIntersection} or - * {@link Entities.findRayIntersectionBlocking|findRayIntersectionBlocking}, or the {@link Picks} and {@link RayPick} - * APIs. + * {@link Entities.evalRayIntersection|evalRayIntersection}, or the {@link Picks} API. * @function Entities.setLightsArePickable * @param {boolean} value - Set true to make ray picks intersect the bounding box of * {@link Entities.EntityType|Light} entities, otherwise false. @@ -615,9 +734,7 @@ public slots: /**jsdoc * Get whether or not ray picks intersect the bounding box of {@link Entities.EntityType|Light} entities. Ray picks are - * done using {@link Entities.findRayIntersection|findRayIntersection} or - * {@link Entities.findRayIntersectionBlocking|findRayIntersectionBlocking}, or the {@link Picks} and {@link RayPick} - * APIs. + * done using {@link Entities.evalRayIntersection|evalRayIntersection}, or the {@link Picks} API. * @function Entities.getLightsArePickable * @returns {boolean} true if ray picks intersect the bounding box of {@link Entities.EntityType|Light} * entities, otherwise false. @@ -628,9 +745,7 @@ public slots: /**jsdoc * Set whether or not ray picks intersect the bounding box of {@link Entities.EntityType|Zone} entities. By default, Light * entities are not intersected. The setting lasts for the Interface session. Ray picks are done using - * {@link Entities.findRayIntersection|findRayIntersection} or - * {@link Entities.findRayIntersectionBlocking|findRayIntersectionBlocking}, or the {@link Picks} and {@link RayPick} - * APIs. + * {@link Entities.evalRayIntersection|evalRayIntersection}, or the {@link Picks} API. * @function Entities.setZonesArePickable * @param {boolean} value - Set true to make ray picks intersect the bounding box of * {@link Entities.EntityType|Zone} entities, otherwise false. @@ -640,9 +755,7 @@ public slots: /**jsdoc * Get whether or not ray picks intersect the bounding box of {@link Entities.EntityType|Zone} entities. Ray picks are - * done using {@link Entities.findRayIntersection|findRayIntersection} or - * {@link Entities.findRayIntersectionBlocking|findRayIntersectionBlocking}, or the {@link Picks} and {@link RayPick} - * APIs. + * done using {@link Entities.evalRayIntersection|evalRayIntersection}, or the {@link Picks} API. * @function Entities.getZonesArePickable * @returns {boolean} true if ray picks intersect the bounding box of {@link Entities.EntityType|Zone} * entities, otherwise false. @@ -1951,14 +2064,12 @@ private: /// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode - RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, - bool precisionPicking, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, - bool visibleOnly = false, bool collidableOnly = false); + RayToEntityIntersectionResult evalRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, + PickFilter searchFilter, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard); /// actually does the work of finding the parabola intersection, can be called in locking mode or tryLock mode - ParabolaToEntityIntersectionResult findParabolaIntersectionWorker(const PickParabola& parabola, Octree::lockType lockType, - bool precisionPicking, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, - bool visibleOnly = false, bool collidableOnly = false); + ParabolaToEntityIntersectionResult evalParabolaIntersectionWorker(const PickParabola& parabola, Octree::lockType lockType, + PickFilter searchFilter, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard); EntityTreePointer _entityTree; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index b23be66ade..ab2c14339e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -43,50 +43,6 @@ static const quint64 DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER = USECS_PER_MSEC * 50; const float EntityTree::DEFAULT_MAX_TMP_ENTITY_LIFETIME = 60 * 60; // 1 hour -// combines the ray cast arguments into a single object -class RayArgs { -public: - // Inputs - glm::vec3 origin; - glm::vec3 direction; - glm::vec3 invDirection; - const QVector& entityIdsToInclude; - const QVector& entityIdsToDiscard; - bool visibleOnly; - bool collidableOnly; - bool precisionPicking; - - // Outputs - OctreeElementPointer& element; - float& distance; - BoxFace& face; - glm::vec3& surfaceNormal; - QVariantMap& extraInfo; - EntityItemID entityID; -}; - -class ParabolaArgs { -public: - // Inputs - glm::vec3 origin; - glm::vec3 velocity; - glm::vec3 acceleration; - const QVector& entityIdsToInclude; - const QVector& entityIdsToDiscard; - bool visibleOnly; - bool collidableOnly; - bool precisionPicking; - - // Outputs - OctreeElementPointer& element; - float& parabolicDistance; - BoxFace& face; - glm::vec3& surfaceNormal; - QVariantMap& extraInfo; - EntityItemID entityID; -}; - - EntityTree::EntityTree(bool shouldReaverage) : Octree(shouldReaverage) { @@ -777,59 +733,32 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) } } - -class FindNearPointArgs { +class RayArgs { public: - glm::vec3 position; - float targetRadius; - bool found; - EntityItemPointer closestEntity; - float closestEntityDistance; + // Inputs + glm::vec3 origin; + glm::vec3 direction; + glm::vec3 invDirection; + const QVector& entityIdsToInclude; + const QVector& entityIdsToDiscard; + PickFilter searchFilter; + + // Outputs + OctreeElementPointer& element; + float& distance; + BoxFace& face; + glm::vec3& surfaceNormal; + QVariantMap& extraInfo; + EntityItemID entityID; }; - -bool EntityTree::findNearPointOperation(const OctreeElementPointer& element, void* extraData) { - FindNearPointArgs* args = static_cast(extraData); - EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); - - glm::vec3 penetration; - bool sphereIntersection = entityTreeElement->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration); - - // If this entityTreeElement contains the point, then search it... - if (sphereIntersection) { - EntityItemPointer thisClosestEntity = entityTreeElement->getClosestEntity(args->position); - - // we may have gotten NULL back, meaning no entity was available - if (thisClosestEntity) { - glm::vec3 entityPosition = thisClosestEntity->getWorldPosition(); - float distanceFromPointToEntity = glm::distance(entityPosition, args->position); - - // If we're within our target radius - if (distanceFromPointToEntity <= args->targetRadius) { - // we are closer than anything else we've found - if (distanceFromPointToEntity < args->closestEntityDistance) { - args->closestEntity = thisClosestEntity; - args->closestEntityDistance = distanceFromPointToEntity; - args->found = true; - } - } - } - - // we should be able to optimize this... - return true; // keep searching in case children have closer entities - } - - // if this element doesn't contain the point, then none of its children can contain the point, so stop searching - return false; -} - -bool findRayIntersectionOp(const OctreeElementPointer& element, void* extraData) { +bool evalRayIntersectionOp(const OctreeElementPointer& element, void* extraData) { RayArgs* args = static_cast(extraData); bool keepSearching = true; EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); - EntityItemID entityID = entityTreeElementPointer->findRayIntersection(args->origin, args->direction, + EntityItemID entityID = entityTreeElementPointer->evalRayIntersection(args->origin, args->direction, args->element, args->distance, args->face, args->surfaceNormal, args->entityIdsToInclude, - args->entityIdsToDiscard, args->visibleOnly, args->collidableOnly, args->extraInfo, args->precisionPicking); + args->entityIdsToDiscard, args->searchFilter, args->extraInfo); if (!entityID.isNull()) { args->entityID = entityID; // We recurse OctreeElements in order, so if we hit something, we can stop immediately @@ -838,7 +767,7 @@ bool findRayIntersectionOp(const OctreeElementPointer& element, void* extraData) return keepSearching; } -float findRayIntersectionSortingOp(const OctreeElementPointer& element, void* extraData) { +float evalRayIntersectionSortingOp(const OctreeElementPointer& element, void* extraData) { RayArgs* args = static_cast(extraData); EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); float distance = FLT_MAX; @@ -859,19 +788,18 @@ float findRayIntersectionSortingOp(const OctreeElementPointer& element, void* ex return distance; } -EntityItemID EntityTree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, +EntityItemID EntityTree::evalRayIntersection(const glm::vec3& origin, const glm::vec3& direction, QVector entityIdsToInclude, QVector entityIdsToDiscard, - bool visibleOnly, bool collidableOnly, bool precisionPicking, - OctreeElementPointer& element, float& distance, + PickFilter searchFilter, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, Octree::lockType lockType, bool* accurateResult) { RayArgs args = { origin, direction, 1.0f / direction, entityIdsToInclude, entityIdsToDiscard, - visibleOnly, collidableOnly, precisionPicking, element, distance, face, surfaceNormal, extraInfo, EntityItemID() }; + searchFilter, element, distance, face, surfaceNormal, extraInfo, EntityItemID() }; distance = FLT_MAX; bool requireLock = lockType == Octree::Lock; bool lockResult = withReadLock([&]{ - recurseTreeWithOperationSorted(findRayIntersectionOp, findRayIntersectionSortingOp, &args); + recurseTreeWithOperationSorted(evalRayIntersectionOp, evalRayIntersectionSortingOp, &args); }, requireLock); if (accurateResult) { @@ -881,13 +809,32 @@ EntityItemID EntityTree::findRayIntersection(const glm::vec3& origin, const glm: return args.entityID; } -bool findParabolaIntersectionOp(const OctreeElementPointer& element, void* extraData) { +class ParabolaArgs { +public: + // Inputs + glm::vec3 origin; + glm::vec3 velocity; + glm::vec3 acceleration; + const QVector& entityIdsToInclude; + const QVector& entityIdsToDiscard; + PickFilter searchFilter; + + // Outputs + OctreeElementPointer& element; + float& parabolicDistance; + BoxFace& face; + glm::vec3& surfaceNormal; + QVariantMap& extraInfo; + EntityItemID entityID; +}; + +bool evalParabolaIntersectionOp(const OctreeElementPointer& element, void* extraData) { ParabolaArgs* args = static_cast(extraData); bool keepSearching = true; EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); - EntityItemID entityID = entityTreeElementPointer->findParabolaIntersection(args->origin, args->velocity, args->acceleration, + EntityItemID entityID = entityTreeElementPointer->evalParabolaIntersection(args->origin, args->velocity, args->acceleration, args->element, args->parabolicDistance, args->face, args->surfaceNormal, args->entityIdsToInclude, - args->entityIdsToDiscard, args->visibleOnly, args->collidableOnly, args->extraInfo, args->precisionPicking); + args->entityIdsToDiscard, args->searchFilter, args->extraInfo); if (!entityID.isNull()) { args->entityID = entityID; // We recurse OctreeElements in order, so if we hit something, we can stop immediately @@ -896,7 +843,7 @@ bool findParabolaIntersectionOp(const OctreeElementPointer& element, void* extra return keepSearching; } -float findParabolaIntersectionSortingOp(const OctreeElementPointer& element, void* extraData) { +float evalParabolaIntersectionSortingOp(const OctreeElementPointer& element, void* extraData) { ParabolaArgs* args = static_cast(extraData); EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); float distance = FLT_MAX; @@ -917,20 +864,20 @@ float findParabolaIntersectionSortingOp(const OctreeElementPointer& element, voi return distance; } -EntityItemID EntityTree::findParabolaIntersection(const PickParabola& parabola, +EntityItemID EntityTree::evalParabolaIntersection(const PickParabola& parabola, QVector entityIdsToInclude, QVector entityIdsToDiscard, - bool visibleOnly, bool collidableOnly, bool precisionPicking, + PickFilter searchFilter, OctreeElementPointer& element, glm::vec3& intersection, float& distance, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, Octree::lockType lockType, bool* accurateResult) { ParabolaArgs args = { parabola.origin, parabola.velocity, parabola.acceleration, entityIdsToInclude, entityIdsToDiscard, - visibleOnly, collidableOnly, precisionPicking, element, parabolicDistance, face, surfaceNormal, extraInfo, EntityItemID() }; + searchFilter, element, parabolicDistance, face, surfaceNormal, extraInfo, EntityItemID() }; parabolicDistance = FLT_MAX; distance = FLT_MAX; bool requireLock = lockType == Octree::Lock; bool lockResult = withReadLock([&] { - recurseTreeWithOperationSorted(findParabolaIntersectionOp, findParabolaIntersectionSortingOp, &args); + recurseTreeWithOperationSorted(evalParabolaIntersectionOp, evalParabolaIntersectionSortingOp, &args); }, requireLock); if (accurateResult) { @@ -945,33 +892,80 @@ EntityItemID EntityTree::findParabolaIntersection(const PickParabola& parabola, return args.entityID; } - -EntityItemPointer EntityTree::findClosestEntity(const glm::vec3& position, float targetRadius) { - FindNearPointArgs args = { position, targetRadius, false, NULL, FLT_MAX }; - withReadLock([&] { - // NOTE: This should use recursion, since this is a spatial operation - recurseTreeWithOperation(findNearPointOperation, &args); - }); - return args.closestEntity; -} - -class FindAllNearPointArgs { +class FindClosestEntityArgs { public: + // Inputs glm::vec3 position; float targetRadius; - QVector entities; + PickFilter searchFilter; + + // Outputs + QUuid closestEntity; + float closestEntityDistance; }; -bool EntityTree::findInSphereOperation(const OctreeElementPointer& element, void* extraData) { - FindAllNearPointArgs* args = static_cast(extraData); +bool evalClosestEntityOperation(const OctreeElementPointer& element, void* extraData) { + FindClosestEntityArgs* args = static_cast(extraData); + EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); + + glm::vec3 penetration; + bool sphereIntersection = entityTreeElement->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration); + + // If this entityTreeElement contains the point, then search it... + if (sphereIntersection) { + float closestDistanceSquared = FLT_MAX; + QUuid thisClosestEntity = entityTreeElement->evalClosetEntity(args->position, args->searchFilter, closestDistanceSquared); + + // we may have gotten NULL back, meaning no entity was available + if (!thisClosestEntity.isNull()) { + float distanceFromPointToEntity = glm::sqrt(closestDistanceSquared); + + // If we're within our target radius + if (distanceFromPointToEntity <= args->targetRadius) { + // we are closer than anything else we've found + if (distanceFromPointToEntity < args->closestEntityDistance) { + args->closestEntity = thisClosestEntity; + args->closestEntityDistance = distanceFromPointToEntity; + } + } + } + + // we should be able to optimize this... + return true; // keep searching in case children have closer entities + } + + // if this element doesn't contain the point, then none of its children can contain the point, so stop searching + return false; +} + +// NOTE: assumes caller has handled locking +QUuid EntityTree::evalClosestEntity(const glm::vec3& position, float targetRadius, PickFilter searchFilter) { + FindClosestEntityArgs args = { position, targetRadius, searchFilter, QUuid(), FLT_MAX }; + recurseTreeWithOperation(evalClosestEntityOperation, &args); + return args.closestEntity; +} + +class FindEntitiesInSphereArgs { +public: + // Inputs + glm::vec3 position; + float targetRadius; + PickFilter searchFilter; + + // Outputs + QVector entities; +}; + +bool evalInSphereOperation(const OctreeElementPointer& element, void* extraData) { + FindEntitiesInSphereArgs* args = static_cast(extraData); glm::vec3 penetration; bool sphereIntersection = element->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration); // If this element contains the point, then search it... if (sphereIntersection) { EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); - entityTreeElement->getEntities(args->position, args->targetRadius, args->entities); + entityTreeElement->evalEntitiesInSphere(args->position, args->targetRadius, args->searchFilter, args->entities); return true; // keep searching in case children have closer entities } @@ -980,102 +974,166 @@ bool EntityTree::findInSphereOperation(const OctreeElementPointer& element, void } // NOTE: assumes caller has handled locking -void EntityTree::findEntities(const glm::vec3& center, float radius, QVector& foundEntities) { - FindAllNearPointArgs args = { center, radius, QVector() }; - // NOTE: This should use recursion, since this is a spatial operation - recurseTreeWithOperation(findInSphereOperation, &args); +void EntityTree::evalEntitiesInSphere(const glm::vec3& center, float radius, PickFilter searchFilter, QVector& foundEntities) { + FindEntitiesInSphereArgs args = { center, radius, searchFilter, QVector() }; + recurseTreeWithOperation(evalInSphereOperation, &args); + foundEntities.swap(args.entities); +} - // swap the two lists of entity pointers instead of copy +class FindEntitiesInSphereWithTypeArgs { +public: + // Inputs + glm::vec3 position; + float targetRadius; + EntityTypes::EntityType type; + PickFilter searchFilter; + + // Outputs + QVector entities; +}; + +bool evalInSphereWithTypeOperation(const OctreeElementPointer& element, void* extraData) { + FindEntitiesInSphereWithTypeArgs* args = static_cast(extraData); + glm::vec3 penetration; + bool sphereIntersection = element->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration); + + // If this element contains the point, then search it... + if (sphereIntersection) { + EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); + entityTreeElement->evalEntitiesInSphereWithType(args->position, args->targetRadius, args->type, args->searchFilter, args->entities); + return true; // keep searching in case children have closer entities + } + + // if this element doesn't contain the point, then none of it's children can contain the point, so stop searching + return false; +} + +// NOTE: assumes caller has handled locking +void EntityTree::evalEntitiesInSphereWithType(const glm::vec3& center, float radius, EntityTypes::EntityType type, PickFilter searchFilter, QVector& foundEntities) { + FindEntitiesInSphereWithTypeArgs args = { center, radius, type, searchFilter, QVector() }; + recurseTreeWithOperation(evalInSphereWithTypeOperation, &args); + foundEntities.swap(args.entities); +} + +class FindEntitiesInSphereWithNameArgs { +public: + // Inputs + glm::vec3 position; + float targetRadius; + QString name; + bool caseSensitive; + PickFilter searchFilter; + + // Outputs + QVector entities; +}; + +bool evalInSphereWithNameOperation(const OctreeElementPointer& element, void* extraData) { + FindEntitiesInSphereWithNameArgs* args = static_cast(extraData); + glm::vec3 penetration; + bool sphereIntersection = element->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration); + + // If this element contains the point, then search it... + if (sphereIntersection) { + EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); + entityTreeElement->evalEntitiesInSphereWithName(args->position, args->targetRadius, args->name, args->caseSensitive, args->searchFilter, args->entities); + return true; // keep searching in case children have closer entities + } + + // if this element doesn't contain the point, then none of it's children can contain the point, so stop searching + return false; +} + +// NOTE: assumes caller has handled locking +void EntityTree::evalEntitiesInSphereWithName(const glm::vec3& center, float radius, const QString& name, bool caseSensitive, PickFilter searchFilter, QVector& foundEntities) { + FindEntitiesInSphereWithNameArgs args = { center, radius, name, caseSensitive, searchFilter, QVector() }; + recurseTreeWithOperation(evalInSphereWithNameOperation, &args); foundEntities.swap(args.entities); } class FindEntitiesInCubeArgs { public: - FindEntitiesInCubeArgs(const AACube& cube) - : _cube(cube), _foundEntities() { - } + // Inputs + AACube cube; + PickFilter searchFilter; - AACube _cube; - QVector _foundEntities; + // Outputs + QVector entities; }; -bool EntityTree::findInCubeOperation(const OctreeElementPointer& element, void* extraData) { +bool findInCubeOperation(const OctreeElementPointer& element, void* extraData) { FindEntitiesInCubeArgs* args = static_cast(extraData); - if (element->getAACube().touches(args->_cube)) { + if (element->getAACube().touches(args->cube)) { EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); - entityTreeElement->getEntities(args->_cube, args->_foundEntities); + entityTreeElement->evalEntitiesInCube(args->cube, args->searchFilter, args->entities); return true; } return false; } // NOTE: assumes caller has handled locking -void EntityTree::findEntities(const AACube& cube, QVector& foundEntities) { - FindEntitiesInCubeArgs args(cube); - // NOTE: This should use recursion, since this is a spatial operation +void EntityTree::evalEntitiesInCube(const AACube& cube, PickFilter searchFilter, QVector& foundEntities) { + FindEntitiesInCubeArgs args { cube, searchFilter, QVector() }; recurseTreeWithOperation(findInCubeOperation, &args); - // swap the two lists of entity pointers instead of copy - foundEntities.swap(args._foundEntities); + foundEntities.swap(args.entities); } class FindEntitiesInBoxArgs { public: - FindEntitiesInBoxArgs(const AABox& box) - : _box(box), _foundEntities() { - } + // Inputs + AABox box; + PickFilter searchFilter; - AABox _box; - QVector _foundEntities; + // Outputs + QVector entities; }; -bool EntityTree::findInBoxOperation(const OctreeElementPointer& element, void* extraData) { +bool findInBoxOperation(const OctreeElementPointer& element, void* extraData) { FindEntitiesInBoxArgs* args = static_cast(extraData); - if (element->getAACube().touches(args->_box)) { + if (element->getAACube().touches(args->box)) { EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); - entityTreeElement->getEntities(args->_box, args->_foundEntities); + entityTreeElement->evalEntitiesInBox(args->box, args->searchFilter, args->entities); return true; } return false; } // NOTE: assumes caller has handled locking -void EntityTree::findEntities(const AABox& box, QVector& foundEntities) { - FindEntitiesInBoxArgs args(box); +void EntityTree::evalEntitiesInBox(const AABox& box, PickFilter searchFilter, QVector& foundEntities) { + FindEntitiesInBoxArgs args { box, searchFilter, QVector() }; // NOTE: This should use recursion, since this is a spatial operation recurseTreeWithOperation(findInBoxOperation, &args); // swap the two lists of entity pointers instead of copy - foundEntities.swap(args._foundEntities); -} - -class FindInFrustumArgs { -public: - ViewFrustum frustum; - QVector entities; -}; - -bool EntityTree::findInFrustumOperation(const OctreeElementPointer& element, void* extraData) { - FindInFrustumArgs* args = static_cast(extraData); - if (element->isInView(args->frustum)) { - EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); - entityTreeElement->getEntities(args->frustum, args->entities); - return true; - } - return false; -} - -// NOTE: assumes caller has handled locking -void EntityTree::findEntities(const ViewFrustum& frustum, QVector& foundEntities) { - FindInFrustumArgs args = { frustum, QVector() }; - // NOTE: This should use recursion, since this is a spatial operation - recurseTreeWithOperation(findInFrustumOperation, &args); - // swap the two lists of entity pointers instead of copy foundEntities.swap(args.entities); } +class FindEntitiesInFrustumArgs { +public: + // Inputs + ViewFrustum frustum; + PickFilter searchFilter; + + // Outputs + QVector entities; +}; + +bool findInFrustumOperation(const OctreeElementPointer& element, void* extraData) { + FindEntitiesInFrustumArgs* args = static_cast(extraData); + if (element->isInView(args->frustum)) { + EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); + entityTreeElement->evalEntitiesInFrustum(args->frustum, args->searchFilter, args->entities); + return true; + } + return false; +} + // NOTE: assumes caller has handled locking -void EntityTree::findEntities(RecurseOctreeOperation& elementFilter, - QVector& foundEntities) { - recurseTreeWithOperation(elementFilter, nullptr); +void EntityTree::evalEntitiesInFrustum(const ViewFrustum& frustum, PickFilter searchFilter, QVector& foundEntities) { + FindEntitiesInFrustumArgs args = { frustum, searchFilter, QVector() }; + // NOTE: This should use recursion, since this is a spatial operation + recurseTreeWithOperation(findInFrustumOperation, &args); + // swap the two lists of entity pointers instead of copy + foundEntities.swap(args.entities); } EntityItemPointer EntityTree::findEntityByID(const QUuid& id) const { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index c6a590ec71..563b231753 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -45,7 +45,6 @@ public: QHash* map; }; - class EntityTree : public Octree, public SpatialParentTree { Q_OBJECT public: @@ -92,18 +91,16 @@ public: virtual void processChallengeOwnershipReplyPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) override; virtual void processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) override; - virtual EntityItemID findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + virtual EntityItemID evalRayIntersection(const glm::vec3& origin, const glm::vec3& direction, QVector entityIdsToInclude, QVector entityIdsToDiscard, - bool visibleOnly, bool collidableOnly, bool precisionPicking, - OctreeElementPointer& element, float& distance, + PickFilter searchFilter, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL); - virtual EntityItemID findParabolaIntersection(const PickParabola& parabola, + virtual EntityItemID evalParabolaIntersection(const PickParabola& parabola, QVector entityIdsToInclude, QVector entityIdsToDiscard, - bool visibleOnly, bool collidableOnly, bool precisionPicking, - OctreeElementPointer& element, glm::vec3& intersection, float& distance, float& parabolicDistance, - BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, + PickFilter searchFilter, OctreeElementPointer& element, glm::vec3& intersection, + float& distance, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL); virtual bool rootElementHasData() const override { return true; } @@ -128,44 +125,19 @@ public: void deleteEntity(const EntityItemID& entityID, bool force = false, bool ignoreWarnings = true); void deleteEntities(QSet entityIDs, bool force = false, bool ignoreWarnings = true); - /// \param position point of query in world-frame (meters) - /// \param targetRadius radius of query (meters) - EntityItemPointer findClosestEntity(const glm::vec3& position, float targetRadius); EntityItemPointer findEntityByID(const QUuid& id) const; EntityItemPointer findEntityByEntityItemID(const EntityItemID& entityID) const; virtual SpatiallyNestablePointer findByID(const QUuid& id) const override { return findEntityByID(id); } EntityItemID assignEntityID(const EntityItemID& entityItemID); /// Assigns a known ID for a creator token ID - - /// finds all entities that touch a sphere - /// \param center the center of the sphere in world-frame (meters) - /// \param radius the radius of the sphere in world-frame (meters) - /// \param foundEntities[out] vector of EntityItemPointer - /// \remark Side effect: any initial contents in foundEntities will be lost - void findEntities(const glm::vec3& center, float radius, QVector& foundEntities); - - /// finds all entities that touch a cube - /// \param cube the query cube in world-frame (meters) - /// \param foundEntities[out] vector of non-EntityItemPointer - /// \remark Side effect: any initial contents in entities will be lost - void findEntities(const AACube& cube, QVector& foundEntities); - - /// finds all entities that touch a box - /// \param box the query box in world-frame (meters) - /// \param foundEntities[out] vector of non-EntityItemPointer - /// \remark Side effect: any initial contents in entities will be lost - void findEntities(const AABox& box, QVector& foundEntities); - - /// finds all entities within a frustum - /// \parameter frustum the query frustum - /// \param foundEntities[out] vector of EntityItemPointer - void findEntities(const ViewFrustum& frustum, QVector& foundEntities); - - /// finds all entities that match scanOperator - /// \parameter scanOperator function that scans entities that match criteria - /// \parameter foundEntities[out] vector of EntityItemPointer - void findEntities(RecurseOctreeOperation& scanOperator, QVector& foundEntities); + QUuid evalClosestEntity(const glm::vec3& position, float targetRadius, PickFilter searchFilter); + void evalEntitiesInSphere(const glm::vec3& center, float radius, PickFilter searchFilter, QVector& foundEntities); + void evalEntitiesInSphereWithType(const glm::vec3& center, float radius, EntityTypes::EntityType type, PickFilter searchFilter, QVector& foundEntities); + void evalEntitiesInSphereWithName(const glm::vec3& center, float radius, const QString& name, bool caseSensitive, PickFilter searchFilter, QVector& foundEntities); + void evalEntitiesInCube(const AACube& cube, PickFilter searchFilter, QVector& foundEntities); + void evalEntitiesInBox(const AABox& box, PickFilter searchFilter, QVector& foundEntities); + void evalEntitiesInFrustum(const ViewFrustum& frustum, PickFilter searchFilter, QVector& foundEntities); void addNewlyCreatedHook(NewlyCreatedEntityHook* hook); void removeNewlyCreatedHook(NewlyCreatedEntityHook* hook); @@ -321,11 +293,6 @@ protected: void processRemovedEntities(const DeleteEntityOperator& theOperator); bool updateEntity(EntityItemPointer entity, const EntityItemProperties& properties, const SharedNodePointer& senderNode = SharedNodePointer(nullptr)); - static bool findNearPointOperation(const OctreeElementPointer& element, void* extraData); - static bool findInSphereOperation(const OctreeElementPointer& element, void* extraData); - static bool findInCubeOperation(const OctreeElementPointer& element, void* extraData); - static bool findInBoxOperation(const OctreeElementPointer& element, void* extraData); - static bool findInFrustumOperation(const OctreeElementPointer& element, void* extraData); static bool sendEntitiesOperation(const OctreeElementPointer& element, void* extraData); static void bumpTimestamp(EntityItemProperties& properties); diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 5b71010f75..2ece6835ea 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -139,10 +139,23 @@ bool EntityTreeElement::bestFitBounds(const glm::vec3& minPoint, const glm::vec3 return false; } -EntityItemID EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, - const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, - bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking) { +bool checkFilterSettings(const EntityItemPointer& entity, PickFilter searchFilter) { + bool visible = entity->isVisible(); + bool collidable = !entity->getCollisionless() && (entity->getShapeType() != SHAPE_TYPE_NONE); + if ((!searchFilter.doesPickVisible() && visible) || (!searchFilter.doesPickInvisible() && !visible) || + (!searchFilter.doesPickCollidable() && collidable) || (!searchFilter.doesPickNonCollidable() && !collidable) || + (!searchFilter.doesPickDomainEntities() && entity->isDomainEntity()) || + (!searchFilter.doesPickAvatarEntities() && entity->isAvatarEntity()) || + (!searchFilter.doesPickLocalEntities() && entity->isLocalEntity())) { + return false; + } + return true; +} + +EntityItemID EntityTreeElement::evalRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, + const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, + PickFilter searchFilter, QVariantMap& extraInfo) { EntityItemID result; BoxFace localFace { UNKNOWN_FACE }; @@ -154,9 +167,8 @@ EntityItemID EntityTreeElement::findRayIntersection(const glm::vec3& origin, con QVariantMap localExtraInfo; float distanceToElementDetails = distance; - EntityItemID entityID = findDetailedRayIntersection(origin, direction, element, distanceToElementDetails, - localFace, localSurfaceNormal, entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly, - localExtraInfo, precisionPicking); + EntityItemID entityID = evalDetailedRayIntersection(origin, direction, element, distanceToElementDetails, + localFace, localSurfaceNormal, entityIdsToInclude, entityIdsToDiscard, searchFilter, localExtraInfo); if (!entityID.isNull() && distanceToElementDetails < distance) { distance = distanceToElementDetails; face = localFace; @@ -167,13 +179,12 @@ EntityItemID EntityTreeElement::findRayIntersection(const glm::vec3& origin, con return result; } -EntityItemID EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, +EntityItemID EntityTreeElement::evalDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, const QVector& entityIDsToDiscard, - bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking) { + PickFilter searchFilter, QVariantMap& extraInfo) { // only called if we do intersect our bounding cube, but find if we actually intersect with entities... - int entityNumber = 0; EntityItemID entityID; forEachEntity([&](EntityItemPointer entity) { // use simple line-sphere for broadphase check @@ -187,11 +198,9 @@ EntityItemID EntityTreeElement::findDetailedRayIntersection(const glm::vec3& ori return; } - // check RayPick filter settings - if ((visibleOnly && !entity->isVisible()) - || (collidableOnly && (entity->getCollisionless() || entity->getShapeType() == SHAPE_TYPE_NONE)) - || (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID())) - || (entityIDsToDiscard.size() > 0 && entityIDsToDiscard.contains(entity->getID())) ) { + if (!checkFilterSettings(entity, searchFilter) || + (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID())) || + (entityIDsToDiscard.size() > 0 && entityIDsToDiscard.contains(entity->getID())) ) { return; } @@ -222,7 +231,7 @@ EntityItemID EntityTreeElement::findDetailedRayIntersection(const glm::vec3& ori if (entity->supportsDetailedIntersection()) { QVariantMap localExtraInfo; if (entity->findDetailedRayIntersection(origin, direction, element, localDistance, - localFace, localSurfaceNormal, localExtraInfo, precisionPicking)) { + localFace, localSurfaceNormal, localExtraInfo, searchFilter.isPrecise())) { if (localDistance < distance) { distance = localDistance; face = localFace; @@ -244,7 +253,6 @@ EntityItemID EntityTreeElement::findDetailedRayIntersection(const glm::vec3& ori } } } - entityNumber++; }); return entityID; } @@ -275,11 +283,10 @@ bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float rad return result; } -EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, +EntityItemID EntityTreeElement::evalParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, - const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly, - QVariantMap& extraInfo, bool precisionPicking) { + const QVector& entityIdsToDiscard, PickFilter searchFilter, QVariantMap& extraInfo) { EntityItemID result; BoxFace localFace; @@ -300,9 +307,8 @@ EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin } // Get the normal of the plane, the cross product of two vectors on the plane glm::vec3 normal = glm::normalize(glm::cross(vectorOnPlane, acceleration)); - EntityItemID entityID = findDetailedParabolaIntersection(origin, velocity, acceleration, normal, element, distanceToElementDetails, - localFace, localSurfaceNormal, entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly, - localExtraInfo, precisionPicking); + EntityItemID entityID = evalDetailedParabolaIntersection(origin, velocity, acceleration, normal, element, distanceToElementDetails, + localFace, localSurfaceNormal, entityIdsToInclude, entityIdsToDiscard, searchFilter, localExtraInfo); if (!entityID.isNull() && distanceToElementDetails < parabolicDistance) { parabolicDistance = distanceToElementDetails; face = localFace; @@ -313,13 +319,12 @@ EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin return result; } -EntityItemID EntityTreeElement::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, +EntityItemID EntityTreeElement::evalDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, const glm::vec3& normal, OctreeElementPointer& element, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, const QVector& entityIDsToDiscard, - bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking) { + PickFilter searchFilter, QVariantMap& extraInfo) { // only called if we do intersect our bounding cube, but find if we actually intersect with entities... - int entityNumber = 0; EntityItemID entityID; forEachEntity([&](EntityItemPointer entity) { // use simple line-sphere for broadphase check @@ -338,11 +343,9 @@ EntityItemID EntityTreeElement::findDetailedParabolaIntersection(const glm::vec3 return; } - // check RayPick filter settings - if ((visibleOnly && !entity->isVisible()) - || (collidableOnly && (entity->getCollisionless() || entity->getShapeType() == SHAPE_TYPE_NONE)) - || (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID())) - || (entityIDsToDiscard.size() > 0 && entityIDsToDiscard.contains(entity->getID())) ) { + if (!checkFilterSettings(entity, searchFilter) || + (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID())) || + (entityIDsToDiscard.size() > 0 && entityIDsToDiscard.contains(entity->getID()))) { return; } @@ -374,7 +377,7 @@ EntityItemID EntityTreeElement::findDetailedParabolaIntersection(const glm::vec3 if (entity->supportsDetailedIntersection()) { QVariantMap localExtraInfo; if (entity->findDetailedParabolaIntersection(origin, velocity, acceleration, element, localDistance, - localFace, localSurfaceNormal, localExtraInfo, precisionPicking)) { + localFace, localSurfaceNormal, localExtraInfo, searchFilter.isPrecise())) { if (localDistance < parabolicDistance) { parabolicDistance = localDistance; face = localFace; @@ -396,55 +399,56 @@ EntityItemID EntityTreeElement::findDetailedParabolaIntersection(const glm::vec3 } } } - entityNumber++; }); return entityID; } -EntityItemPointer EntityTreeElement::getClosestEntity(glm::vec3 position) const { - EntityItemPointer closestEntity = NULL; - float closestEntityDistance = FLT_MAX; - withReadLock([&] { - foreach(EntityItemPointer entity, _entityItems) { - float distanceToEntity = glm::distance2(position, entity->getWorldPosition()); - if (distanceToEntity < closestEntityDistance) { - closestEntity = entity; - } +QUuid EntityTreeElement::evalClosetEntity(const glm::vec3& position, PickFilter searchFilter, float& closestDistanceSquared) const { + QUuid closestEntity; + forEachEntity([&](EntityItemPointer entity) { + if (!checkFilterSettings(entity, searchFilter)) { + return; + } + + float distanceToEntity = glm::distance2(position, entity->getWorldPosition()); + if (distanceToEntity < closestDistanceSquared) { + closestEntity = entity->getID(); + closestDistanceSquared = distanceToEntity; } }); return closestEntity; } -// TODO: change this to use better bounding shape for entity than sphere -void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searchRadius, QVector& foundEntities) const { +void EntityTreeElement::evalEntitiesInSphere(const glm::vec3& position, float radius, PickFilter searchFilter, QVector& foundEntities) const { forEachEntity([&](EntityItemPointer entity) { + if (!checkFilterSettings(entity, searchFilter)) { + return; + } bool success; AABox entityBox = entity->getAABox(success); // if the sphere doesn't intersect with our world frame AABox, we don't need to consider the more complex case glm::vec3 penetration; - if (!success || entityBox.findSpherePenetration(searchPosition, searchRadius, penetration)) { + if (success && entityBox.findSpherePenetration(position, radius, penetration)) { glm::vec3 dimensions = entity->getRaycastDimensions(); // FIXME - consider allowing the entity to determine penetration so that - // entities could presumably dull actuall hull testing if they wanted to + // entities could presumably do actual hull testing if they wanted to // FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better in particular // can we handle the ellipsoid case better? We only currently handle perfect spheres // with centered registration points - if (entity->getShapeType() == SHAPE_TYPE_SPHERE && - (dimensions.x == dimensions.y && dimensions.y == dimensions.z)) { + if (entity->getShapeType() == SHAPE_TYPE_SPHERE && (dimensions.x == dimensions.y && dimensions.y == dimensions.z)) { // NOTE: entity->getRadius() doesn't return the true radius, it returns the radius of the // maximum bounding sphere, which is actually larger than our actual radius float entityTrueRadius = dimensions.x / 2.0f; bool success; - if (findSphereSpherePenetration(searchPosition, searchRadius, - entity->getCenterPosition(success), entityTrueRadius, penetration)) { + if (findSphereSpherePenetration(position, radius, entity->getCenterPosition(success), entityTrueRadius, penetration)) { if (success) { - foundEntities.push_back(entity); + foundEntities.push_back(entity->getID()); } } } else { @@ -460,17 +464,134 @@ void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searc AABox entityFrameBox(corner, dimensions); - glm::vec3 entityFrameSearchPosition = glm::vec3(worldToEntityMatrix * glm::vec4(searchPosition, 1.0f)); - if (entityFrameBox.findSpherePenetration(entityFrameSearchPosition, searchRadius, penetration)) { - foundEntities.push_back(entity); + glm::vec3 entityFrameSearchPosition = glm::vec3(worldToEntityMatrix * glm::vec4(position, 1.0f)); + if (entityFrameBox.findSpherePenetration(entityFrameSearchPosition, radius, penetration)) { + foundEntities.push_back(entity->getID()); } } } }); } -void EntityTreeElement::getEntities(const AACube& cube, QVector& foundEntities) { +void EntityTreeElement::evalEntitiesInSphereWithType(const glm::vec3& position, float radius, EntityTypes::EntityType type, PickFilter searchFilter, QVector& foundEntities) const { forEachEntity([&](EntityItemPointer entity) { + if (!checkFilterSettings(entity, searchFilter) || type != entity->getType()) { + return; + } + + bool success; + AABox entityBox = entity->getAABox(success); + + // if the sphere doesn't intersect with our world frame AABox, we don't need to consider the more complex case + glm::vec3 penetration; + if (success && entityBox.findSpherePenetration(position, radius, penetration)) { + + glm::vec3 dimensions = entity->getRaycastDimensions(); + + // FIXME - consider allowing the entity to determine penetration so that + // entities could presumably do actual hull testing if they wanted to + // FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better in particular + // can we handle the ellipsoid case better? We only currently handle perfect spheres + // with centered registration points + if (entity->getShapeType() == SHAPE_TYPE_SPHERE && (dimensions.x == dimensions.y && dimensions.y == dimensions.z)) { + + // NOTE: entity->getRadius() doesn't return the true radius, it returns the radius of the + // maximum bounding sphere, which is actually larger than our actual radius + float entityTrueRadius = dimensions.x / 2.0f; + + bool success; + if (findSphereSpherePenetration(position, radius, entity->getCenterPosition(success), entityTrueRadius, penetration)) { + if (success) { + foundEntities.push_back(entity->getID()); + } + } + } else { + // determine the worldToEntityMatrix that doesn't include scale because + // we're going to use the registration aware aa box in the entity frame + glm::mat4 rotation = glm::mat4_cast(entity->getWorldOrientation()); + glm::mat4 translation = glm::translate(entity->getWorldPosition()); + glm::mat4 entityToWorldMatrix = translation * rotation; + glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); + + glm::vec3 registrationPoint = entity->getRegistrationPoint(); + glm::vec3 corner = -(dimensions * registrationPoint); + + AABox entityFrameBox(corner, dimensions); + + glm::vec3 entityFrameSearchPosition = glm::vec3(worldToEntityMatrix * glm::vec4(position, 1.0f)); + if (entityFrameBox.findSpherePenetration(entityFrameSearchPosition, radius, penetration)) { + foundEntities.push_back(entity->getID()); + } + } + } + }); +} + +void EntityTreeElement::evalEntitiesInSphereWithName(const glm::vec3& position, float radius, const QString& name, bool caseSensitive, PickFilter searchFilter, QVector& foundEntities) const { + forEachEntity([&](EntityItemPointer entity) { + if (!checkFilterSettings(entity, searchFilter)) { + return; + } + + QString entityName = entity->getName(); + if ((caseSensitive && name != entityName) || (!caseSensitive && name.toLower() != entityName.toLower())) { + return; + } + + bool success; + AABox entityBox = entity->getAABox(success); + + // if the sphere doesn't intersect with our world frame AABox, we don't need to consider the more complex case + glm::vec3 penetration; + if (success && entityBox.findSpherePenetration(position, radius, penetration)) { + + glm::vec3 dimensions = entity->getRaycastDimensions(); + + // FIXME - consider allowing the entity to determine penetration so that + // entities could presumably do actual hull testing if they wanted to + // FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better in particular + // can we handle the ellipsoid case better? We only currently handle perfect spheres + // with centered registration points + if (entity->getShapeType() == SHAPE_TYPE_SPHERE && (dimensions.x == dimensions.y && dimensions.y == dimensions.z)) { + + // NOTE: entity->getRadius() doesn't return the true radius, it returns the radius of the + // maximum bounding sphere, which is actually larger than our actual radius + float entityTrueRadius = dimensions.x / 2.0f; + + bool success; + if (findSphereSpherePenetration(position, radius, entity->getCenterPosition(success), entityTrueRadius, penetration)) { + if (success) { + foundEntities.push_back(entity->getID()); + } + } + } else { + // determine the worldToEntityMatrix that doesn't include scale because + // we're going to use the registration aware aa box in the entity frame + glm::mat4 rotation = glm::mat4_cast(entity->getWorldOrientation()); + glm::mat4 translation = glm::translate(entity->getWorldPosition()); + glm::mat4 entityToWorldMatrix = translation * rotation; + glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); + + glm::vec3 registrationPoint = entity->getRegistrationPoint(); + glm::vec3 corner = -(dimensions * registrationPoint); + + AABox entityFrameBox(corner, dimensions); + + glm::vec3 entityFrameSearchPosition = glm::vec3(worldToEntityMatrix * glm::vec4(position, 1.0f)); + if (entityFrameBox.findSpherePenetration(entityFrameSearchPosition, radius, penetration)) { + foundEntities.push_back(entity->getID()); + } + } + } + }); +} + +void EntityTreeElement::evalEntitiesInCube(const AACube& cube, PickFilter searchFilter, QVector& foundEntities) const { + forEachEntity([&](EntityItemPointer entity) { + if (!checkFilterSettings(entity, searchFilter)) { + return; + } + bool success; AABox entityBox = entity->getAABox(success); // FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better @@ -489,14 +610,18 @@ void EntityTreeElement::getEntities(const AACube& cube, QVectorgetID()); } }); } -void EntityTreeElement::getEntities(const AABox& box, QVector& foundEntities) { +void EntityTreeElement::evalEntitiesInBox(const AABox& box, PickFilter searchFilter, QVector& foundEntities) const { forEachEntity([&](EntityItemPointer entity) { + if (!checkFilterSettings(entity, searchFilter)) { + return; + } + bool success; AABox entityBox = entity->getAABox(success); // FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better @@ -515,19 +640,24 @@ void EntityTreeElement::getEntities(const AABox& box, QVector // // If the entities AABox touches the search cube then consider it to be found - if (!success || entityBox.touches(box)) { - foundEntities.push_back(entity); + if (success && entityBox.touches(box)) { + foundEntities.push_back(entity->getID()); } }); } -void EntityTreeElement::getEntities(const ViewFrustum& frustum, QVector& foundEntities) { +void EntityTreeElement::evalEntitiesInFrustum(const ViewFrustum& frustum, PickFilter searchFilter, QVector& foundEntities) const { forEachEntity([&](EntityItemPointer entity) { + if (!checkFilterSettings(entity, searchFilter)) { + return; + } + bool success; AABox entityBox = entity->getAABox(success); + // FIXME - See FIXMEs for similar methods above. - if (!success || frustum.boxIntersectsFrustum(entityBox) || frustum.boxIntersectsKeyhole(entityBox)) { - foundEntities.push_back(entity); + if (success && (frustum.boxIntersectsFrustum(entityBox) || frustum.boxIntersectsKeyhole(entityBox))) { + foundEntities.push_back(entity->getID()); } }); } diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index 793340c9a4..aed19eed15 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -19,7 +19,8 @@ #include "EntityEditPacketSender.h" #include "EntityItem.h" -#include "EntityTree.h" + +#include class EntityTree; class EntityTreeElement; @@ -77,7 +78,6 @@ public: EntityEditPacketSender* packetSender; }; - class EntityTreeElement : public OctreeElement, ReadWriteLockable { friend class EntityTree; // to allow createElement to new us... @@ -135,28 +135,25 @@ public: virtual bool deleteApproved() const override { return !hasEntities(); } virtual bool canPickIntersect() const override { return hasEntities(); } - virtual EntityItemID findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + virtual EntityItemID evalRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, - bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking = false); - virtual EntityItemID findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + PickFilter searchFilter, QVariantMap& extraInfo); + virtual EntityItemID evalDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, - const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly, - QVariantMap& extraInfo, bool precisionPicking); + const QVector& entityIdsToDiscard, PickFilter searchFilter, QVariantMap& extraInfo); virtual bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, void** penetratedObject) const override; - virtual EntityItemID findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, + virtual EntityItemID evalParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, - const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly, - QVariantMap& extraInfo, bool precisionPicking = false); - virtual EntityItemID findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, + const QVector& entityIdsToDiscard, PickFilter searchFilter, QVariantMap& extraInfo); + virtual EntityItemID evalDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& normal, const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, - const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly, - QVariantMap& extraInfo, bool precisionPicking); + const QVector& entityIdsToDiscard, PickFilter searchFilter, QVariantMap& extraInfo); template void forEachEntity(F f) const { @@ -175,28 +172,13 @@ public: void addEntityItem(EntityItemPointer entity); - EntityItemPointer getClosestEntity(glm::vec3 position) const; - - /// finds all entities that touch a sphere - /// \param position the center of the query sphere - /// \param radius the radius of the query sphere - /// \param entities[out] vector of const EntityItemPointer - void getEntities(const glm::vec3& position, float radius, QVector& foundEntities) const; - - /// finds all entities that touch a box - /// \param box the query box - /// \param entities[out] vector of non-const EntityItemPointer - void getEntities(const AACube& cube, QVector& foundEntities); - - /// finds all entities that touch a box - /// \param box the query box - /// \param entities[out] vector of non-const EntityItemPointer - void getEntities(const AABox& box, QVector& foundEntities); - - /// finds all entities that touch a frustum - /// \param frustum the query frustum - /// \param entities[out] vector of non-const EntityItemPointer - void getEntities(const ViewFrustum& frustum, QVector& foundEntities); + QUuid evalClosetEntity(const glm::vec3& position, PickFilter searchFilter, float& closestDistanceSquared) const; + void evalEntitiesInSphere(const glm::vec3& position, float radius, PickFilter searchFilter, QVector& foundEntities) const; + void evalEntitiesInSphereWithType(const glm::vec3& position, float radius, EntityTypes::EntityType type, PickFilter searchFilter, QVector& foundEntities) const; + void evalEntitiesInSphereWithName(const glm::vec3& position, float radius, const QString& name, bool caseSensitive, PickFilter searchFilter, QVector& foundEntities) const; + void evalEntitiesInCube(const AACube& cube, PickFilter searchFilter, QVector& foundEntities) const; + void evalEntitiesInBox(const AABox& box, PickFilter searchFilter, QVector& foundEntities) const; + void evalEntitiesInFrustum(const ViewFrustum& frustum, PickFilter searchFilter, QVector& foundEntities) const; /// finds all entities that match filter /// \param filter function that adds matching entities to foundEntities diff --git a/libraries/pointers/src/Pick.cpp b/libraries/pointers/src/Pick.cpp index 7ac53a2643..e95721c04b 100644 --- a/libraries/pointers/src/Pick.cpp +++ b/libraries/pointers/src/Pick.cpp @@ -7,8 +7,6 @@ // #include "Pick.h" -const PickFilter PickFilter::NOTHING; - int pickTypeMetaTypeId = qRegisterMetaType("PickType"); PickQuery::PickQuery(const PickFilter& filter, const float maxDistance, const bool enabled) : @@ -41,7 +39,8 @@ bool PickQuery::isEnabled() const { void PickQuery::setPrecisionPicking(bool precisionPicking) { withWriteLock([&] { - _filter.setFlag(PickFilter::PICK_COARSE, !precisionPicking); + _filter.setFlag(PickFilter::PRECISE, precisionPicking); + _filter.setFlag(PickFilter::COARSE, !precisionPicking); }); } diff --git a/libraries/pointers/src/Pick.h b/libraries/pointers/src/Pick.h index 9ca0f14c5f..857a72caa8 100644 --- a/libraries/pointers/src/Pick.h +++ b/libraries/pointers/src/Pick.h @@ -18,6 +18,7 @@ #include #include +#include enum IntersectionType { NONE = 0, @@ -27,84 +28,6 @@ enum IntersectionType { HUD }; -class PickFilter { -public: - enum FlagBit { - PICK_ENTITIES = 0, - PICK_OVERLAYS, - PICK_AVATARS, - PICK_HUD, - - PICK_COARSE, // if not set, does precise intersection, otherwise, doesn't - - PICK_INCLUDE_INVISIBLE, // if not set, will not intersect invisible elements, otherwise, intersects both visible and invisible elements - PICK_INCLUDE_NONCOLLIDABLE, // if not set, will not intersect noncollidable elements, otherwise, intersects both collidable and noncollidable elements - - // NOT YET IMPLEMENTED - PICK_ALL_INTERSECTIONS, // if not set, returns closest intersection, otherwise, returns list of all intersections - - NUM_FLAGS, // Not a valid flag - }; - typedef std::bitset Flags; - - // The key is the Flags - Flags _flags; - - PickFilter() {} - PickFilter(const Flags& flags) : _flags(flags) {} - - bool operator== (const PickFilter& rhs) const { return _flags == rhs._flags; } - bool operator!= (const PickFilter& rhs) const { return _flags != rhs._flags; } - - void setFlag(FlagBit flag, bool value) { _flags[flag] = value; } - - bool doesPickNothing() const { return _flags == NOTHING._flags; } - bool doesPickEntities() const { return _flags[PICK_ENTITIES]; } - bool doesPickOverlays() const { return _flags[PICK_OVERLAYS]; } - bool doesPickAvatars() const { return _flags[PICK_AVATARS]; } - bool doesPickHUD() const { return _flags[PICK_HUD]; } - - bool doesPickCoarse() const { return _flags[PICK_COARSE]; } - bool doesPickInvisible() const { return _flags[PICK_INCLUDE_INVISIBLE]; } - bool doesPickNonCollidable() const { return _flags[PICK_INCLUDE_NONCOLLIDABLE]; } - - bool doesWantAllIntersections() const { return _flags[PICK_ALL_INTERSECTIONS]; } - - // Helpers for RayPickManager - Flags getEntityFlags() const { - unsigned int toReturn = getBitMask(PICK_ENTITIES); - if (doesPickInvisible()) { - toReturn |= getBitMask(PICK_INCLUDE_INVISIBLE); - } - if (doesPickNonCollidable()) { - toReturn |= getBitMask(PICK_INCLUDE_NONCOLLIDABLE); - } - if (doesPickCoarse()) { - toReturn |= getBitMask(PICK_COARSE); - } - return Flags(toReturn); - } - Flags getOverlayFlags() const { - unsigned int toReturn = getBitMask(PICK_OVERLAYS); - if (doesPickInvisible()) { - toReturn |= getBitMask(PICK_INCLUDE_INVISIBLE); - } - if (doesPickNonCollidable()) { - toReturn |= getBitMask(PICK_INCLUDE_NONCOLLIDABLE); - } - if (doesPickCoarse()) { - toReturn |= getBitMask(PICK_COARSE); - } - return Flags(toReturn); - } - Flags getAvatarFlags() const { return Flags(getBitMask(PICK_AVATARS)); } - Flags getHUDFlags() const { return Flags(getBitMask(PICK_HUD)); } - - static constexpr unsigned int getBitMask(FlagBit bit) { return 1 << bit; } - - static const PickFilter NOTHING; -}; - class PickResult { public: PickResult() {} diff --git a/libraries/pointers/src/PickCacheOptimizer.h b/libraries/pointers/src/PickCacheOptimizer.h index c74e84937b..e91283f02c 100644 --- a/libraries/pointers/src/PickCacheOptimizer.h +++ b/libraries/pointers/src/PickCacheOptimizer.h @@ -85,10 +85,10 @@ QVector4D PickCacheOptimizer::update(std::unordered_mapgetMathematicalPick(); PickResultPointer res = pick->getDefaultResult(mathematicalPick.toVariantMap()); - if (!pick->isEnabled() || pick->getFilter().doesPickNothing() || pick->getMaxDistance() < 0.0f || !mathematicalPick) { + if (!pick->isEnabled() || pick->getMaxDistance() < 0.0f || !mathematicalPick) { pick->setPickResult(res); } else { - if (pick->getFilter().doesPickEntities()) { + if (pick->getFilter().doesPickDomainEntities() || pick->getFilter().doesPickAvatarEntities()) { PickCacheKey entityKey = { pick->getFilter().getEntityFlags(), pick->getIncludeItems(), pick->getIgnoreItems() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, entityKey)) { PickResultPointer entityRes = pick->getEntityIntersection(mathematicalPick); @@ -99,7 +99,7 @@ QVector4D PickCacheOptimizer::update(std::unordered_mapgetFilter().doesPickOverlays()) { + if (pick->getFilter().doesPickLocalEntities()) { PickCacheKey overlayKey = { pick->getFilter().getOverlayFlags(), pick->getIncludeItems(), pick->getIgnoreItems() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, overlayKey)) { PickResultPointer overlayRes = pick->getOverlayIntersection(mathematicalPick); diff --git a/libraries/shared/src/PickFilter.h b/libraries/shared/src/PickFilter.h new file mode 100644 index 0000000000..22cf79b238 --- /dev/null +++ b/libraries/shared/src/PickFilter.h @@ -0,0 +1,94 @@ +// +// Created by Sam Gondelman on 12/7/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_PickFilter_h +#define hifi_PickFilter_h + +#include + +class PickFilter { +public: + enum FlagBit { + DOMAIN_ENTITIES = 0, + AVATAR_ENTITIES, + LOCAL_ENTITIES, + AVATARS, + HUD, + + VISIBLE, + INVISIBLE, + + COLLIDABLE, + NONCOLLIDABLE, + + PRECISE, + COARSE, + + // NOT YET IMPLEMENTED + PICK_ALL_INTERSECTIONS, // if not set, returns closest intersection, otherwise, returns list of all intersections + + NUM_FLAGS, // Not a valid flag + }; + typedef std::bitset Flags; + + // The key is the Flags + Flags _flags; + + PickFilter() {} + PickFilter(const Flags& flags) : _flags(flags) {} + + bool operator==(const PickFilter& rhs) const { return _flags == rhs._flags; } + bool operator!=(const PickFilter& rhs) const { return _flags != rhs._flags; } + + void setFlag(FlagBit flag, bool value) { _flags[flag] = value; } + + // There are different groups of related flags. If none of the flags in a group are set, the search filter includes them all. + bool doesPickDomainEntities() const { return _flags[DOMAIN_ENTITIES] || !(_flags[AVATAR_ENTITIES] || _flags[LOCAL_ENTITIES] || _flags[AVATARS] || _flags[HUD]); } + bool doesPickAvatarEntities() const { return _flags[AVATAR_ENTITIES] || !(_flags[DOMAIN_ENTITIES] || _flags[LOCAL_ENTITIES] || _flags[AVATARS] || _flags[HUD]); } + bool doesPickLocalEntities() const { return _flags[LOCAL_ENTITIES] || !(_flags[DOMAIN_ENTITIES] || _flags[AVATAR_ENTITIES] || _flags[AVATARS] || _flags[HUD]); } + bool doesPickAvatars() const { return _flags[AVATARS] || !(_flags[DOMAIN_ENTITIES] || _flags[AVATAR_ENTITIES] || _flags[LOCAL_ENTITIES] || _flags[HUD]); } + bool doesPickHUD() const { return _flags[HUD] || !(_flags[DOMAIN_ENTITIES] || _flags[AVATAR_ENTITIES] || _flags[LOCAL_ENTITIES] || _flags[AVATARS]); } + + bool doesPickVisible() const { return _flags[VISIBLE] || !_flags[INVISIBLE]; } + bool doesPickInvisible() const { return _flags[INVISIBLE] || !_flags[VISIBLE]; } + + bool doesPickCollidable() const { return _flags[COLLIDABLE] || !_flags[NONCOLLIDABLE]; } + bool doesPickNonCollidable() const { return _flags[NONCOLLIDABLE] || !_flags[COLLIDABLE]; } + + bool isPrecise() const { return _flags[PRECISE] || !_flags[COARSE]; } + bool isCoarse() const { return _flags[COARSE] || !_flags[PRECISE]; } + + bool doesWantAllIntersections() const { return _flags[PICK_ALL_INTERSECTIONS]; } + + // Helpers for RayPickManager + Flags getEntityFlags() const { + unsigned int toReturn = getBitMask(DOMAIN_ENTITIES) || getBitMask(AVATAR_ENTITIES); + for (int i = HUD + 1; i < NUM_FLAGS; i++) { + if (_flags[i]) { + toReturn |= getBitMask(FlagBit(i)); + } + } + return Flags(toReturn); + } + Flags getOverlayFlags() const { + unsigned int toReturn = getBitMask(LOCAL_ENTITIES); + for (int i = HUD + 1; i < NUM_FLAGS; i++) { + if (_flags[i]) { + toReturn |= getBitMask(FlagBit(i)); + } + } + return Flags(toReturn); + } + Flags getAvatarFlags() const { return Flags(getBitMask(AVATARS)); } + Flags getHUDFlags() const { return Flags(getBitMask(HUD)); } + + static constexpr unsigned int getBitMask(FlagBit bit) { return 1 << bit; } +}; + +#endif // hifi_PickFilter_h + diff --git a/scripts/developer/utilities/tests/entityPerfTest.js b/scripts/developer/utilities/tests/entityPerfTest.js index 181afcf360..d3846baba9 100644 --- a/scripts/developer/utilities/tests/entityPerfTest.js +++ b/scripts/developer/utilities/tests/entityPerfTest.js @@ -87,14 +87,6 @@ function makeEntity () { .withRay(function(){ Entities.findRayIntersection(this.ray, false); }); - entityTests.addTestCase('findRayIntersectionBlocking, precisionPicking=true') - .withRay(function(){ - Entities.findRayIntersectionBlocking(this.ray, true); - }) - entityTests.addTestCase('findRayIntersectionBlocking, precisionPicking=false') - .withRay(function(){ - Entities.findRayIntersectionBlocking(this.ray, false); - }) entityTests.addTestCase('no-op') .run(function(){}); diff --git a/unpublishedScripts/DomainContent/Toybox/pistol/pistol.js b/unpublishedScripts/DomainContent/Toybox/pistol/pistol.js index b408e4f464..038c53054c 100644 --- a/unpublishedScripts/DomainContent/Toybox/pistol/pistol.js +++ b/unpublishedScripts/DomainContent/Toybox/pistol/pistol.js @@ -137,7 +137,7 @@ direction: this.firingDirection }; this.createGunFireEffect(this.barrelPoint) - var intersection = Entities.findRayIntersectionBlocking(pickRay, true); + var intersection = Entities.findRayIntersection(pickRay, true); if (intersection.intersects) { this.createEntityHitEffect(intersection.intersection); if (Math.random() < this.playRichochetSoundChance) { From 7054188f210a888133a2fedd32de798720f021fd Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 7 Dec 2018 17:03:14 -0800 Subject: [PATCH 08/27] different entity flags for domain/avatar --- libraries/shared/src/PickFilter.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/PickFilter.h b/libraries/shared/src/PickFilter.h index 22cf79b238..96fbadf117 100644 --- a/libraries/shared/src/PickFilter.h +++ b/libraries/shared/src/PickFilter.h @@ -67,7 +67,12 @@ public: // Helpers for RayPickManager Flags getEntityFlags() const { - unsigned int toReturn = getBitMask(DOMAIN_ENTITIES) || getBitMask(AVATAR_ENTITIES); + unsigned int toReturn = 0; + for (int i = DOMAIN_ENTITIES; i < LOCAL_ENTITIES; i++) { + if (_flags[i]) { + toReturn |= getBitMask(FlagBit(i)); + } + } for (int i = HUD + 1; i < NUM_FLAGS; i++) { if (_flags[i]) { toReturn |= getBitMask(FlagBit(i)); From 5eccaad135937cab0bb6b4cea7b22d2e0c5ca024 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 10 Dec 2018 12:56:28 -0800 Subject: [PATCH 09/27] remove new API --- .../entities/src/EntityScriptingInterface.cpp | 40 +++- .../entities/src/EntityScriptingInterface.h | 176 ++---------------- 2 files changed, 44 insertions(+), 172 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 432ac2ad49..e1ac743a80 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1062,11 +1062,12 @@ void EntityScriptingInterface::handleEntityScriptCallMethodPacket(QSharedPointer -QUuid EntityScriptingInterface::evalClosestEntity(const glm::vec3& center, float radius, unsigned int searchFilter) const { +QUuid EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float radius) const { PROFILE_RANGE(script_entities, __FUNCTION__); EntityItemID result; if (_entityTree) { + unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); _entityTree->withReadLock([&] { result = _entityTree->evalClosestEntity(center, radius, PickFilter(searchFilter)); }); @@ -1083,11 +1084,12 @@ void EntityScriptingInterface::dumpTree() const { } } -QVector EntityScriptingInterface::evalEntitiesInSphere(const glm::vec3& center, float radius, unsigned int searchFilter) const { +QVector EntityScriptingInterface::findEntities(const glm::vec3& center, float radius) const { PROFILE_RANGE(script_entities, __FUNCTION__); QVector result; if (_entityTree) { + unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); _entityTree->withReadLock([&] { _entityTree->evalEntitiesInSphere(center, radius, PickFilter(searchFilter), result); }); @@ -1095,11 +1097,12 @@ QVector EntityScriptingInterface::evalEntitiesInSphere(const glm::vec3& c return result; } -QVector EntityScriptingInterface::evalEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions, unsigned int searchFilter) const { +QVector EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const { PROFILE_RANGE(script_entities, __FUNCTION__); QVector result; if (_entityTree) { + unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); _entityTree->withReadLock([&] { AABox box(corner, dimensions); _entityTree->evalEntitiesInBox(box, PickFilter(searchFilter), result); @@ -1108,7 +1111,7 @@ QVector EntityScriptingInterface::evalEntitiesInBox(const glm::vec3& corn return result; } -QVector EntityScriptingInterface::evalEntitiesInFrustum(QVariantMap frustum, unsigned int searchFilter) const { +QVector EntityScriptingInterface::findEntitiesInFrustum(QVariantMap frustum) const { PROFILE_RANGE(script_entities, __FUNCTION__); QVector result; @@ -1138,6 +1141,7 @@ QVector EntityScriptingInterface::evalEntitiesInFrustum(QVariantMap frust viewFrustum.calculate(); if (_entityTree) { + unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); _entityTree->withReadLock([&] { _entityTree->evalEntitiesInFrustum(viewFrustum, PickFilter(searchFilter), result); }); @@ -1147,11 +1151,12 @@ QVector EntityScriptingInterface::evalEntitiesInFrustum(QVariantMap frust return result; } -QVector EntityScriptingInterface::evalEntitiesInSphereWithType(const QString entityType, const glm::vec3& center, float radius, unsigned int searchFilter) const { +QVector EntityScriptingInterface::findEntitiesByType(const QString entityType, const glm::vec3& center, float radius) const { EntityTypes::EntityType type = EntityTypes::getEntityTypeFromName(entityType); QVector result; if (_entityTree) { + unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); _entityTree->withReadLock([&] { _entityTree->evalEntitiesInSphereWithType(center, radius, type, PickFilter(searchFilter), result); }); @@ -1159,22 +1164,37 @@ QVector EntityScriptingInterface::evalEntitiesInSphereWithType(const QStr return result; } -QVector EntityScriptingInterface::evalEntitiesInSphereWithName(const QString entityName, const glm::vec3& center, float radius, bool caseSensitiveSearch, unsigned int searchFilter) const { +QVector EntityScriptingInterface::findEntitiesByName(const QString entityName, const glm::vec3& center, float radius, bool caseSensitiveSearch) const { QVector result; if (_entityTree) { _entityTree->withReadLock([&] { + unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); _entityTree->evalEntitiesInSphereWithName(center, radius, entityName, caseSensitiveSearch, PickFilter(searchFilter), result); }); } return result; } -RayToEntityIntersectionResult EntityScriptingInterface::evalRayIntersection(const PickRay& ray, unsigned int searchFilter, - const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard) { +RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, + const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) const { PROFILE_RANGE(script_entities, __FUNCTION__); QVector entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude); QVector entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard); + unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); + + if (!precisionPicking) { + searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::COARSE); + } + + if (visibleOnly) { + searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::VISIBLE); + } + + if (collidableOnly) { + searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::COLLIDABLE); + } + return evalRayIntersectionWorker(ray, Octree::Lock, PickFilter(searchFilter), entitiesToInclude, entitiesToDiscard); } @@ -1187,7 +1207,7 @@ RayToEntityIntersectionResult EntityScriptingInterface::evalRayIntersectionVecto RayToEntityIntersectionResult EntityScriptingInterface::evalRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, PickFilter searchFilter, const QVector& entityIdsToInclude, - const QVector& entityIdsToDiscard) { + const QVector& entityIdsToDiscard) const { RayToEntityIntersectionResult result; if (_entityTree) { @@ -1213,7 +1233,7 @@ ParabolaToEntityIntersectionResult EntityScriptingInterface::evalParabolaInterse ParabolaToEntityIntersectionResult EntityScriptingInterface::evalParabolaIntersectionWorker(const PickParabola& parabola, Octree::lockType lockType, PickFilter searchFilter, const QVector& entityIdsToInclude, - const QVector& entityIdsToDiscard) { + const QVector& entityIdsToDiscard) const { ParabolaToEntityIntersectionResult result; if (_entityTree) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 9b1dbfe330..e0447bd6b6 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -57,7 +57,7 @@ private: }; /**jsdoc - * The result of a {@link PickRay} search using {@link Entities.evalRayIntersection|evalRayIntersection}. + * The result of a {@link PickRay} search using {@link Entities.findRayIntersection|findRayIntersection}. * @typedef {object} Entities.RayToEntityIntersectionResult * @property {boolean} intersects - true if the {@link PickRay} intersected an entity, otherwise * false. @@ -393,21 +393,6 @@ public slots: Q_INVOKABLE void callEntityClientMethod(QUuid clientSessionID, QUuid entityID, const QString& method, const QStringList& params = QStringList()); - /**jsdoc - * Find the entity with a position closest to a specified point and within a specified radius that matches the search filter. - * @function Entities.evalClosestEntity - * @param {Vec3} center - The point about which to search. - * @param {number} radius - The radius within which to search. - * @param {number} searchFilter - The search filter, constructed using filter flags combined using bitwise OR. - * @returns {Uuid} The ID of the entity that is closest to the center and within the radius if - * there is one, otherwise null. - * @example Find the closest visible avatar entity within 10m of your avatar. - * var entityID = Entities.evalClosestEntity(MyAvatar.position, 10, Picks.PICK_AVATAR_ENTITIES | Picks.PICK_INCLUDE_VISIBLE); - * print("Closest visible avatar entity: " + entityID); - */ - /// this function will not find any models in script engine contexts which don't have access to models - Q_INVOKABLE QUuid evalClosestEntity(const glm::vec3& center, float radius, unsigned int searchFilter) const; - /**jsdoc * Find the non-local entity with a position closest to a specified point and within a specified radius. * @function Entities.findClosestEntity @@ -420,24 +405,7 @@ public slots: * print("Closest entity: " + entityID); */ /// this function will not find any models in script engine contexts which don't have access to models - Q_INVOKABLE QUuid findClosestEntity(const glm::vec3& center, float radius) const { - return evalClosestEntity(center, radius, PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES)); - } - - /**jsdoc - * Find all entities that intersect a sphere defined by a center point and radius that match the search filter. - * @function Entities.evalEntitiesInRadius - * @param {Vec3} center - The point about which to search. - * @param {number} radius - The radius within which to search. - * @param {number} searchFilter - The search filter, constructed using filter flags combined using bitwise OR. - * @returns {Uuid[]} An array of entity IDs that were found that intersect the search sphere. The array is empty if no - * entities could be found. - * @example Report how many visible domain entities are within 10m of your avatar. - * var entityIDs = Entities.evalEntitiesInRadius(MyAvatar.position, 10, Picks.PICK_DOMAIN_ENTITIES | Picks.PICK_INCLUDE_VISIBLE); - * print("Number of visible domain entities within 10m: " + entityIDs.length); - */ - /// this function will not find any models in script engine contexts which don't have access to models - Q_INVOKABLE QVector evalEntitiesInSphere(const glm::vec3& center, float radius, unsigned int searchFilter) const; + Q_INVOKABLE QUuid findClosestEntity(const glm::vec3& center, float radius) const; /**jsdoc * Find all non-local entities that intersect a sphere defined by a center point and radius. @@ -451,22 +419,7 @@ public slots: * print("Number of entities within 10m: " + entityIDs.length); */ /// this function will not find any models in script engine contexts which don't have access to models - Q_INVOKABLE QVector findEntities(const glm::vec3& center, float radius) const { - return evalEntitiesInSphere(center, radius, PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES)); - } - - /**jsdoc - * Find all entities whose axis-aligned boxes intersect a search axis-aligned box defined by its minimum coordinates corner - * and dimensions that match the search filter. - * @function Entities.evalEntitiesInBox - * @param {Vec3} corner - The corner of the search AA box with minimum co-ordinate values. - * @param {Vec3} dimensions - The dimensions of the search AA box. - * @param {number} searchFilter - The search filter, constructed using filter flags combined using bitwise OR. - * @returns {Uuid[]} An array of entity IDs whose AA boxes intersect the search AA box. The array is empty if no entities - * could be found. - */ - /// this function will not find any models in script engine contexts which don't have access to models - Q_INVOKABLE QVector evalEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions, unsigned int searchFilter) const; + Q_INVOKABLE QVector findEntities(const glm::vec3& center, float radius) const; /**jsdoc * Find all non-local entities whose axis-aligned boxes intersect a search axis-aligned box defined by its minimum coordinates corner @@ -478,24 +431,7 @@ public slots: * could be found. */ /// this function will not find any models in script engine contexts which don't have access to models - Q_INVOKABLE QVector findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const { - return evalEntitiesInBox(corner, dimensions, PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES)); - } - - /**jsdoc - * Find all entities whose axis-aligned boxes intersect a search frustum that match the search filter. - * @function Entities.evalEntitiesInFrustum - * @param {ViewFrustum} frustum - The frustum to search in. The position, orientation, - * projection, and centerRadius properties must be specified. - * @param {number} searchFilter - The search filter, constructed using filter flags combined using bitwise OR. - * @returns {Uuid[]} An array of entity IDs axis-aligned boxes intersect the frustum. The array is empty if no entities - * could be found. - * @example Report the number of visible, collidable entities in view. - * var entityIDs = Entities.evalEntitiesInFrustum(Camera.frustum, Picks.PICK_INCLUDE_COLLIDABLE | Picks.PICK_INCLUDE_VISIBLE); - * print("Number of visible, collidable entities in view: " + entityIDs.length); - */ - /// this function will not find any models in script engine contexts which don't have access to entities - Q_INVOKABLE QVector evalEntitiesInFrustum(QVariantMap frustum, unsigned int searchFilter) const; + Q_INVOKABLE QVector findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const; /**jsdoc * Find all non-local entities whose axis-aligned boxes intersect a search frustum. @@ -509,25 +445,7 @@ public slots: * print("Number of entities in view: " + entityIDs.length); */ /// this function will not find any models in script engine contexts which don't have access to entities - Q_INVOKABLE QVector findEntitiesInFrustum(QVariantMap frustum) const { - return evalEntitiesInFrustum(frustum, PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES)); - } - - /**jsdoc - * Find all entities of a particular type that intersect a sphere defined by a center point and radius that match the search filter. - * @function Entities.evalEntitiesInRadiusWithType - * @param {Entities.EntityType} entityType - The type of entity to search for. - * @param {Vec3} center - The point about which to search. - * @param {number} radius - The radius within which to search. - * @param {number} searchFilter - The search filter, constructed using filter flags combined using bitwise OR. - * @returns {Uuid[]} An array of entity IDs of the specified type that intersect the search sphere. The array is empty if - * no entities could be found. - * @example Report the number of visible Model entities within 10m of your avatar. - * var entityIDs = Entities.evalEntitiesInRadiusWithType("Model", MyAvatar.position, 10, Picks.PICK_INCLUDE_VISIBLE); - * print("Number of visible Model entities within 10m: " + entityIDs.length); - */ - /// this function will not find any entities in script engine contexts which don't have access to entities - Q_INVOKABLE QVector evalEntitiesInSphereWithType(const QString entityType, const glm::vec3& center, float radius, unsigned int searchFilter) const; + Q_INVOKABLE QVector findEntitiesInFrustum(QVariantMap frustum) const; /**jsdoc * Find all non-local entities of a particular type that intersect a sphere defined by a center point and radius. @@ -542,26 +460,7 @@ public slots: * print("Number of Model entities within 10m: " + entityIDs.length); */ /// this function will not find any entities in script engine contexts which don't have access to entities - Q_INVOKABLE QVector findEntitiesByType(const QString entityType, const glm::vec3& center, float radius) const { - return evalEntitiesInSphereWithType(entityType, center, radius, PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES)); - } - - /**jsdoc - * Find all entities with a particular name that intersect a sphere defined by a center point and radius and match the search filter. - * @function Entities.findEntitiesByName - * @param {string} entityName - The name of the entity to search for. - * @param {Vec3} center - The point about which to search. - * @param {number} radius - The radius within which to search. - * @param {boolean} caseSensitive - If true then the search is case-sensitive. - * @param {number} searchFilter - The search filter, constructed using filter flags combined using bitwise OR. - * @returns {Uuid[]} An array of entity IDs that have the specified name and intersect the search sphere. The array is empty - * if no entities could be found. - * @example Report the number of collidable entities with the name, "Light-Target". - * var entityIDs = Entities.evalEntitiesInRadiusByName("Light-Target", MyAvatar.position, 10, false, Picks.PICK_INCLUDE_COLLIDABLE); - * print("Number of collidable entities with the name Light-Target: " + entityIDs.length); - */ - Q_INVOKABLE QVector evalEntitiesInSphereWithName(const QString entityName, const glm::vec3& center, float radius, - bool caseSensitiveSearch, unsigned int searchFilter) const; + Q_INVOKABLE QVector findEntitiesByType(const QString entityType, const glm::vec3& center, float radius) const; /**jsdoc * Find all non-local entities with a particular name that intersect a sphere defined by a center point and radius. @@ -577,38 +476,7 @@ public slots: * print("Number of entities with the name Light-Target: " + entityIDs.length); */ Q_INVOKABLE QVector findEntitiesByName(const QString entityName, const glm::vec3& center, float radius, - bool caseSensitiveSearch = false) const { - return evalEntitiesInSphereWithName(entityName, center, radius, caseSensitiveSearch, PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES)); - } - - /**jsdoc - * Find the first entity intersected by a {@link PickRay} that matches the search filter. Light and Zone entities are not - * intersected unless they've been configured as pickable using {@link Entities.setLightsArePickable|setLightsArePickable} - * and {@link Entities.setZonesArePickable|setZonesArePickable}, respectively.
- * @function Entities.evalRayIntersection - * @param {PickRay} pickRay - The PickRay to use for finding entities. - * @param {number} searchFilter - The search filter, constructed using filter flags combined using bitwise OR. - * @param {Uuid[]} [entitiesToInclude=[]] - If not empty then the search is restricted to these entities. - * @param {Uuid[]} [entitiesToDiscard=[]] - Entities to ignore during the search. - * @returns {Entities.RayToEntityIntersectionResult} The result of the search for the first intersected entity. - * @example Find the entity directly in front of your avatar. - * var pickRay = { - * origin: MyAvatar.position, - * direction: Quat.getFront(MyAvatar.orientation) - * }; - * - * var intersection = Entities.evalRayIntersection(pickRay, Picks.PICK_PRECISE); - * if (intersection.intersects) { - * print("Entity in front of avatar: " + intersection.entityID); - * } else { - * print("No entity in front of avatar."); - * } - */ - /// If the scripting context has visible entities, this will determine a ray intersection, the results - /// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate - /// will be false. - Q_INVOKABLE RayToEntityIntersectionResult evalRayIntersection(const PickRay& ray, unsigned int searchFilter, - const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue()); + bool caseSensitiveSearch = false) const; /**jsdoc * Find the first non-local entity intersected by a {@link PickRay}. Light and Zone entities are not @@ -643,23 +511,7 @@ public slots: /// will be false. Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue(), - bool visibleOnly = false, bool collidableOnly = false) { - unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); - - if (!precisionPicking) { - searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::COARSE); - } - - if (visibleOnly) { - searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::VISIBLE); - } - - if (collidableOnly) { - searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::COLLIDABLE); - } - - return evalRayIntersection(ray, searchFilter, entityIdsToInclude, entityIdsToDiscard); - } + bool visibleOnly = false, bool collidableOnly = false) const; /**jsdoc * Reloads an entity's server entity script such that the latest version re-downloaded. @@ -724,7 +576,7 @@ public slots: /**jsdoc * Set whether or not ray picks intersect the bounding box of {@link Entities.EntityType|Light} entities. By default, Light * entities are not intersected. The setting lasts for the Interface session. Ray picks are done using - * {@link Entities.evalRayIntersection|evalRayIntersection}, or the {@link Picks} API. + * {@link Entities.findRayIntersection|findRayIntersection}, or the {@link Picks} API. * @function Entities.setLightsArePickable * @param {boolean} value - Set true to make ray picks intersect the bounding box of * {@link Entities.EntityType|Light} entities, otherwise false. @@ -734,7 +586,7 @@ public slots: /**jsdoc * Get whether or not ray picks intersect the bounding box of {@link Entities.EntityType|Light} entities. Ray picks are - * done using {@link Entities.evalRayIntersection|evalRayIntersection}, or the {@link Picks} API. + * done using {@link Entities.findRayIntersection|findRayIntersection}, or the {@link Picks} API. * @function Entities.getLightsArePickable * @returns {boolean} true if ray picks intersect the bounding box of {@link Entities.EntityType|Light} * entities, otherwise false. @@ -745,7 +597,7 @@ public slots: /**jsdoc * Set whether or not ray picks intersect the bounding box of {@link Entities.EntityType|Zone} entities. By default, Light * entities are not intersected. The setting lasts for the Interface session. Ray picks are done using - * {@link Entities.evalRayIntersection|evalRayIntersection}, or the {@link Picks} API. + * {@link Entities.findRayIntersection|findRayIntersection}, or the {@link Picks} API. * @function Entities.setZonesArePickable * @param {boolean} value - Set true to make ray picks intersect the bounding box of * {@link Entities.EntityType|Zone} entities, otherwise false. @@ -755,7 +607,7 @@ public slots: /**jsdoc * Get whether or not ray picks intersect the bounding box of {@link Entities.EntityType|Zone} entities. Ray picks are - * done using {@link Entities.evalRayIntersection|evalRayIntersection}, or the {@link Picks} API. + * done using {@link Entities.findRayIntersection|findRayIntersection}, or the {@link Picks} API. * @function Entities.getZonesArePickable * @returns {boolean} true if ray picks intersect the bounding box of {@link Entities.EntityType|Zone} * entities, otherwise false. @@ -2065,11 +1917,11 @@ private: /// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode RayToEntityIntersectionResult evalRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, - PickFilter searchFilter, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard); + PickFilter searchFilter, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard) const; /// actually does the work of finding the parabola intersection, can be called in locking mode or tryLock mode ParabolaToEntityIntersectionResult evalParabolaIntersectionWorker(const PickParabola& parabola, Octree::lockType lockType, - PickFilter searchFilter, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard); + PickFilter searchFilter, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard) const; EntityTreePointer _entityTree; From d74f7eb0982d65e7b371a923268fbef75f5222b3 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 11 Dec 2018 15:41:07 -0800 Subject: [PATCH 10/27] Implement MS20187: Add trash can feature to Inventory --- .../hifi/commerce/purchases/PurchasedItem.qml | 38 +++++++++++++++++-- .../qml/hifi/commerce/purchases/Purchases.qml | 25 ++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index c8ec7238d6..12a9912e8e 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -176,6 +176,7 @@ Item { Item { property alias buttonGlyphText: buttonGlyph.text; property alias buttonText: buttonText.text; + property alias glyphSize: buttonGlyph.size; property string buttonColor: hifi.colors.black; property string buttonColor_hover: hifi.colors.blueHighlight; property alias enabled: buttonMouseArea.enabled; @@ -186,7 +187,8 @@ Item { anchors.top: parent.top; anchors.topMargin: 4; anchors.horizontalCenter: parent.horizontalCenter; - anchors.bottom: parent.verticalCenter; + anchors.bottom: buttonText.visible ? parent.verticalCenter : parent.bottom; + anchors.bottomMargin: buttonText.visible ? 0 : 4; width: parent.width; size: 40; horizontalAlignment: Text.AlignHCenter; @@ -196,6 +198,7 @@ Item { RalewayRegular { id: buttonText; + visible: text !== ""; anchors.top: parent.verticalCenter; anchors.topMargin: 4; anchors.bottom: parent.bottom; @@ -300,7 +303,7 @@ Item { anchors.right: certificateButton.left; anchors.top: parent.top; anchors.bottom: parent.bottom; - width: 78; + width: 72; onLoaded: { item.buttonGlyphText = hifi.glyphs.uninstall; @@ -319,7 +322,7 @@ Item { anchors.right: uninstallButton.visible ? uninstallButton.left : certificateButton.left; anchors.top: parent.top; anchors.bottom: parent.bottom; - width: 84; + width: 78; onLoaded: { item.buttonGlyphText = hifi.glyphs.update; @@ -340,6 +343,35 @@ Item { } } } + + Loader { + id: trashButton; + visible: root.itemEdition > 0; + sourceComponent: contextCardButton; + anchors.right: updateButton.visible ? updateButton.left : (uninstallButton.visible ? uninstallButton.left : certificateButton.left); + anchors.top: parent.top; + anchors.bottom: parent.bottom; + width: (updateButton.visible && uninstallButton.visible) ? 15 : 78; + + onLoaded: { + item.buttonGlyphText = hifi.glyphs.trash; + if (updateButton.visible && uninstallButton.visible) { + item.buttonText = ""; + item.glyphSize = 20; + } else { + item.buttonText = "Send to Trash"; + item.glyphSize = 30; + } + item.buttonClicked = function() { + sendToPurchases({method: 'showTrashLightbox', + isInstalled: root.isInstalled, + itemHref: root.itemHref, + itemName: root.itemName, + certID: root.certificateId + }); + } + } + } } Rectangle { diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 18d6bc9f78..dae93b3315 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -651,6 +651,31 @@ Rectangle { lightboxPopup.visible = false; }; lightboxPopup.visible = true; + } else if (msg.method === "showTrashLightbox") { + lightboxPopup.titleText = "Send \"" + msg.itemName + "\" to Trash"; + lightboxPopup.bodyText = "Sending this item to the Trash means you will no longer own this item " + + "and it will be inaccessible to you from Purchases.\n\nThis action cannot be undone."; + lightboxPopup.button1text = "CANCEL"; + lightboxPopup.button1method = function() { + lightboxPopup.visible = false; + } + lightboxPopup.button2text = "CONFIRM"; + lightboxPopup.button2method = function() { + if (msg.isInstalled) { + Commerce.uninstallApp(msg.itemHref); + } + Commerce.transferAssetToUsername("TrashCan", msg.certID, 1, "Sent " + msg.itemName + " to trash."); + + lightboxPopup.titleText = '"' + msg.itemName + '" Sent to Trash'; + lightboxPopup.button1text = "OK"; + lightboxPopup.button1method = function() { + lightboxPopup.visible = false; + getPurchases(); + } + lightboxPopup.button2text = ""; + lightboxPopup.bodyText = ""; + }; + lightboxPopup.visible = true; } else if (msg.method === "showChangeAvatarLightbox") { lightboxPopup.titleText = "Change Avatar"; lightboxPopup.bodyText = "This will change your current avatar to " + msg.itemName + " while retaining your wearables."; From dff7804cdc4f3834ad83bdc92c3e61ad3452cb62 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 11 Dec 2018 16:26:49 -0800 Subject: [PATCH 11/27] Change username to trashbot --- interface/resources/qml/hifi/commerce/purchases/Purchases.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index dae93b3315..f50ce0bda3 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -664,7 +664,7 @@ Rectangle { if (msg.isInstalled) { Commerce.uninstallApp(msg.itemHref); } - Commerce.transferAssetToUsername("TrashCan", msg.certID, 1, "Sent " + msg.itemName + " to trash."); + Commerce.transferAssetToUsername("trashbot", msg.certID, 1, "Sent " + msg.itemName + " to trash."); lightboxPopup.titleText = '"' + msg.itemName + '" Sent to Trash'; lightboxPopup.button1text = "OK"; From 77fa4e7d1334bb16388dcda1569ae7ed9cd70b22 Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Sun, 16 Dec 2018 17:29:16 -0800 Subject: [PATCH 12/27] took out the unnecessary hfm.jointindex code --- interface/src/avatar/MyAvatar.cpp | 4 +- interface/src/avatar/MySkeletonModel.cpp | 4 +- libraries/animation/src/Rig.cpp | 28 ++++---- .../src/avatars-renderer/Avatar.cpp | 4 +- .../src/avatars-renderer/SkeletonModel.cpp | 34 ++++----- .../src/avatars-renderer/SkeletonModel.h | 4 +- libraries/fbx/src/FBXSerializer.cpp | 71 +------------------ .../render-utils/src/CauterizedModel.cpp | 4 +- 8 files changed, 43 insertions(+), 110 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 97014550a8..6dbc430cc1 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2417,10 +2417,10 @@ void MyAvatar::attachmentDataToEntityProperties(const AttachmentData& data, Enti void MyAvatar::initHeadBones() { int neckJointIndex = -1; if (_skeletonModel->isLoaded()) { - neckJointIndex = _skeletonModel->getHFMModel().neckJointIndex; + neckJointIndex = getJointIndex("Neck"); } if (neckJointIndex == -1) { - neckJointIndex = (_skeletonModel->getHFMModel().headJointIndex - 1); + neckJointIndex = (getJointIndex("Head") - 1); if (neckJointIndex < 0) { // return if the head is not even there. can't cauterize!! return; diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index a8a7dd16c2..0c72766424 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -301,8 +301,8 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { eyeParams.eyeSaccade = head->getSaccade(); eyeParams.modelRotation = getRotation(); eyeParams.modelTranslation = getTranslation(); - eyeParams.leftEyeJointIndex = hfmModel.leftEyeJointIndex; - eyeParams.rightEyeJointIndex = hfmModel.rightEyeJointIndex; + eyeParams.leftEyeJointIndex = _rig.indexOfJoint("LeftEye"); + eyeParams.rightEyeJointIndex = _rig.indexOfJoint("RightEye");; _rig.updateFromEyeParameters(eyeParams); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 128ac05b81..44fdd8797f 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -343,18 +343,18 @@ void Rig::initJointStates(const HFMModel& hfmModel, const glm::mat4& modelOffset buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses); - _rootJointIndex = hfmModel.rootJointIndex; - _leftEyeJointIndex = hfmModel.leftEyeJointIndex; - _rightEyeJointIndex = hfmModel.rightEyeJointIndex; - _leftHandJointIndex = hfmModel.leftHandJointIndex; + _rootJointIndex = indexOfJoint("Hips"); + _leftEyeJointIndex = indexOfJoint("LeftEye"); + _rightEyeJointIndex = indexOfJoint("RightEye"); + _leftHandJointIndex = indexOfJoint("LeftHand"); _leftElbowJointIndex = _leftHandJointIndex >= 0 ? hfmModel.joints.at(_leftHandJointIndex).parentIndex : -1; _leftShoulderJointIndex = _leftElbowJointIndex >= 0 ? hfmModel.joints.at(_leftElbowJointIndex).parentIndex : -1; - _rightHandJointIndex = hfmModel.rightHandJointIndex; + _rightHandJointIndex = indexOfJoint("RightHand"); _rightElbowJointIndex = _rightHandJointIndex >= 0 ? hfmModel.joints.at(_rightHandJointIndex).parentIndex : -1; _rightShoulderJointIndex = _rightElbowJointIndex >= 0 ? hfmModel.joints.at(_rightElbowJointIndex).parentIndex : -1; - _leftEyeJointChildren = _animSkeleton->getChildrenOfJoint(hfmModel.leftEyeJointIndex); - _rightEyeJointChildren = _animSkeleton->getChildrenOfJoint(hfmModel.rightEyeJointIndex); + _leftEyeJointChildren = _animSkeleton->getChildrenOfJoint(indexOfJoint("LeftEye")); + _rightEyeJointChildren = _animSkeleton->getChildrenOfJoint(indexOfJoint("RightEye")); } void Rig::reset(const HFMModel& hfmModel) { @@ -390,18 +390,18 @@ void Rig::reset(const HFMModel& hfmModel) { buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses); - _rootJointIndex = hfmModel.rootJointIndex; - _leftEyeJointIndex = hfmModel.leftEyeJointIndex; - _rightEyeJointIndex = hfmModel.rightEyeJointIndex; - _leftHandJointIndex = hfmModel.leftHandJointIndex; + _rootJointIndex = indexOfJoint("Hips");; + _leftEyeJointIndex = indexOfJoint("LeftEye"); + _rightEyeJointIndex = indexOfJoint("RightEye"); + _leftHandJointIndex = indexOfJoint("LeftHand"); _leftElbowJointIndex = _leftHandJointIndex >= 0 ? hfmModel.joints.at(_leftHandJointIndex).parentIndex : -1; _leftShoulderJointIndex = _leftElbowJointIndex >= 0 ? hfmModel.joints.at(_leftElbowJointIndex).parentIndex : -1; - _rightHandJointIndex = hfmModel.rightHandJointIndex; + _rightHandJointIndex = indexOfJoint("RightHand"); _rightElbowJointIndex = _rightHandJointIndex >= 0 ? hfmModel.joints.at(_rightHandJointIndex).parentIndex : -1; _rightShoulderJointIndex = _rightElbowJointIndex >= 0 ? hfmModel.joints.at(_rightElbowJointIndex).parentIndex : -1; - _leftEyeJointChildren = _animSkeleton->getChildrenOfJoint(hfmModel.leftEyeJointIndex); - _rightEyeJointChildren = _animSkeleton->getChildrenOfJoint(hfmModel.rightEyeJointIndex); + _leftEyeJointChildren = _animSkeleton->getChildrenOfJoint(indexOfJoint("LeftEye")); + _rightEyeJointChildren = _animSkeleton->getChildrenOfJoint(indexOfJoint("RightEye")); if (!_animGraphURL.isEmpty()) { _animNode.reset(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index b4ea9c20f9..0761cdf597 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1314,7 +1314,7 @@ glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const { case CAMERA_MATRIX_INDEX: { glm::quat rotation; if (_skeletonModel && _skeletonModel->isActive()) { - int headJointIndex = _skeletonModel->getHFMModel().headJointIndex; + int headJointIndex = getJointIndex("Head"); if (headJointIndex >= 0) { _skeletonModel->getAbsoluteJointRotationInRigFrame(headJointIndex, rotation); } @@ -1363,7 +1363,7 @@ glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const { case CAMERA_MATRIX_INDEX: { glm::vec3 translation; if (_skeletonModel && _skeletonModel->isActive()) { - int headJointIndex = _skeletonModel->getHFMModel().headJointIndex; + int headJointIndex = getJointIndex("Head"); if (headJointIndex >= 0) { _skeletonModel->getAbsoluteJointTranslationInRigFrame(headJointIndex, translation); } diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 36e37dd3d4..f390bb257e 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -66,7 +66,7 @@ void SkeletonModel::initJointStates() { } // Determine the default eye position for avatar scale = 1.0 - int headJointIndex = hfmModel.headJointIndex; + int headJointIndex = _rig.indexOfJoint("Head"); if (0 > headJointIndex || headJointIndex >= _rig.getJointStateCount()) { qCWarning(avatars_renderer) << "Bad head joint! Got:" << headJointIndex << "jointCount:" << _rig.getJointStateCount(); } @@ -74,7 +74,7 @@ void SkeletonModel::initJointStates() { getEyeModelPositions(leftEyePosition, rightEyePosition); glm::vec3 midEyePosition = (leftEyePosition + rightEyePosition) / 2.0f; - int rootJointIndex = hfmModel.rootJointIndex; + int rootJointIndex = _rig.indexOfJoint("Hips"); glm::vec3 rootModelPosition; getJointPosition(rootJointIndex, rootModelPosition); @@ -124,7 +124,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // If the head is not positioned, updateEyeJoints won't get the math right glm::quat headOrientation; - _rig.getJointRotation(hfmModel.headJointIndex, headOrientation); + _rig.getJointRotation(_rig.indexOfJoint("Head"), headOrientation); glm::vec3 eulers = safeEulerAngles(headOrientation); head->setBasePitch(glm::degrees(-eulers.x)); head->setBaseYaw(glm::degrees(eulers.y)); @@ -135,8 +135,8 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { eyeParams.eyeSaccade = glm::vec3(0.0f); eyeParams.modelRotation = getRotation(); eyeParams.modelTranslation = getTranslation(); - eyeParams.leftEyeJointIndex = hfmModel.leftEyeJointIndex; - eyeParams.rightEyeJointIndex = hfmModel.rightEyeJointIndex; + eyeParams.leftEyeJointIndex = _rig.indexOfJoint("LeftEye"); + eyeParams.rightEyeJointIndex = _rig.indexOfJoint("RightEye"); _rig.updateFromEyeParameters(eyeParams); } @@ -259,15 +259,15 @@ bool SkeletonModel::getRightShoulderPosition(glm::vec3& position) const { } bool SkeletonModel::getHeadPosition(glm::vec3& headPosition) const { - return isActive() && getJointPositionInWorldFrame(getHFMModel().headJointIndex, headPosition); + return isActive() && getJointPositionInWorldFrame(_rig.indexOfJoint("Head"), headPosition); } bool SkeletonModel::getNeckPosition(glm::vec3& neckPosition) const { - return isActive() && getJointPositionInWorldFrame(getHFMModel().neckJointIndex, neckPosition); + return isActive() && getJointPositionInWorldFrame(_rig.indexOfJoint("Neck"), neckPosition); } bool SkeletonModel::getLocalNeckPosition(glm::vec3& neckPosition) const { - return isActive() && getJointPosition(getHFMModel().neckJointIndex, neckPosition); + return isActive() && getJointPosition(_rig.indexOfJoint("Neck"), neckPosition); } bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const { @@ -276,28 +276,28 @@ bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& } const HFMModel& hfmModel = getHFMModel(); - if (getJointPosition(hfmModel.leftEyeJointIndex, firstEyePosition) && - getJointPosition(hfmModel.rightEyeJointIndex, secondEyePosition)) { + if (getJointPosition(_rig.indexOfJoint("LeftEye"), firstEyePosition) && + getJointPosition(_rig.indexOfJoint("RightEye"), secondEyePosition)) { return true; } // no eye joints; try to estimate based on head/neck joints glm::vec3 neckPosition, headPosition; - if (getJointPosition(hfmModel.neckJointIndex, neckPosition) && - getJointPosition(hfmModel.headJointIndex, headPosition)) { + if (getJointPosition(_rig.indexOfJoint("Neck"), neckPosition) && + getJointPosition(_rig.indexOfJoint("Head"), headPosition)) { const float EYE_PROPORTION = 0.6f; glm::vec3 baseEyePosition = glm::mix(neckPosition, headPosition, EYE_PROPORTION); glm::quat headRotation; - getJointRotation(hfmModel.headJointIndex, headRotation); + getJointRotation(_rig.indexOfJoint("Head"), headRotation); const float EYES_FORWARD = 0.25f; const float EYE_SEPARATION = 0.1f; float headHeight = glm::distance(neckPosition, headPosition); firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight; secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight; return true; - } else if (getJointPosition(hfmModel.headJointIndex, headPosition)) { + } else if (getJointPosition(_rig.indexOfJoint("Head"), headPosition)) { glm::vec3 baseEyePosition = headPosition; glm::quat headRotation; - getJointRotation(hfmModel.headJointIndex, headRotation); + getJointRotation(_rig.indexOfJoint("Head"), headRotation); const float EYES_FORWARD_HEAD_ONLY = 0.30f; const float EYE_SEPARATION = 0.1f; firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD_HEAD_ONLY); @@ -331,7 +331,7 @@ void SkeletonModel::computeBoundingShape() { } const HFMModel& hfmModel = getHFMModel(); - if (hfmModel.joints.isEmpty() || hfmModel.rootJointIndex == -1) { + if (hfmModel.joints.isEmpty() || _rig.indexOfJoint("Hips") == -1) { // rootJointIndex == -1 if the avatar model has no skeleton return; } @@ -369,7 +369,7 @@ void SkeletonModel::renderBoundingCollisionShapes(RenderArgs* args, gpu::Batch& } bool SkeletonModel::hasSkeleton() { - return isActive() ? getHFMModel().rootJointIndex != -1 : false; + return isActive() ? _rig.indexOfJoint("Hips") != -1 : false; } void SkeletonModel::onInvalidate() { diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h index c53cf8d333..ef0e1e0fae 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h @@ -41,10 +41,10 @@ public: void updateAttitude(const glm::quat& orientation); /// Returns the index of the left hand joint, or -1 if not found. - int getLeftHandJointIndex() const { return isActive() ? getHFMModel().leftHandJointIndex : -1; } + int getLeftHandJointIndex() const { return isActive() ? _rig.indexOfJoint("LeftHand") : -1; } /// Returns the index of the right hand joint, or -1 if not found. - int getRightHandJointIndex() const { return isActive() ? getHFMModel().rightHandJointIndex : -1; } + int getRightHandJointIndex() const { return isActive() ? _rig.indexOfJoint("RightHand") : -1; } bool getLeftGrabPosition(glm::vec3& position) const; bool getRightGrabPosition(glm::vec3& position) const; diff --git a/libraries/fbx/src/FBXSerializer.cpp b/libraries/fbx/src/FBXSerializer.cpp index b425b6795d..38032653f4 100644 --- a/libraries/fbx/src/FBXSerializer.cpp +++ b/libraries/fbx/src/FBXSerializer.cpp @@ -21,6 +21,7 @@ #include #include + #include #include #include @@ -478,25 +479,6 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr std::map lights; QVariantHash joints = mapping.value("joint").toHash(); - QString jointEyeLeftName = "EyeLeft"; - QString jointEyeRightName = "EyeRight"; - QString jointNeckName = "Neck"; - QString jointRootName = "Hips"; - QString jointLeanName = "Spine"; - QString jointHeadName = "Head"; - QString jointLeftHandName = "LeftHand"; - QString jointRightHandName = "RightHand"; - QString jointEyeLeftID; - QString jointEyeRightID; - QString jointNeckID; - QString jointRootID; - QString jointLeanID; - QString jointHeadID; - QString jointLeftHandID; - QString jointRightHandID; - QString jointLeftToeID; - QString jointRightToeID; - QVector humanIKJointNames; for (int i = 0; i < (int) HUMANIK_JOINTS.size(); i++) { @@ -601,38 +583,7 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr if (modelname.startsWith("hifi")) { hifiGlobalNodeID = id; } - - if (name == jointEyeLeftName || name == "EyeL" || name == "joint_Leye" || (hfmModel.hfmToHifiJointNameMapping.contains(jointEyeLeftName) && (name == hfmModel.hfmToHifiJointNameMapping[jointEyeLeftName]))) { - jointEyeLeftID = getID(object.properties); - - } else if (name == jointEyeRightName || name == "EyeR" || name == "joint_Reye" || (hfmModel.hfmToHifiJointNameMapping.contains(jointEyeRightName) && (name == hfmModel.hfmToHifiJointNameMapping[jointEyeRightName]))) { - jointEyeRightID = getID(object.properties); - - } else if (name == jointNeckName || name == "NeckRot" || name == "joint_neck" || (hfmModel.hfmToHifiJointNameMapping.contains(jointNeckName) && (name == hfmModel.hfmToHifiJointNameMapping[jointNeckName]))) { - jointNeckID = getID(object.properties); - - } else if (name == jointRootName || (hfmModel.hfmToHifiJointNameMapping.contains(jointRootName) && (name == hfmModel.hfmToHifiJointNameMapping[jointRootName]))) { - jointRootID = getID(object.properties); - - } else if (name == jointLeanName || (hfmModel.hfmToHifiJointNameMapping.contains(jointLeanName) && (name == hfmModel.hfmToHifiJointNameMapping[jointLeanName]))) { - jointLeanID = getID(object.properties); - - } else if ((name == jointHeadName) || (hfmModel.hfmToHifiJointNameMapping.contains(jointHeadName) && (name == hfmModel.hfmToHifiJointNameMapping[jointHeadName]))) { - jointHeadID = getID(object.properties); - - } else if (name == jointLeftHandName || name == "LeftHand" || name == "joint_L_hand" || (hfmModel.hfmToHifiJointNameMapping.contains(jointLeftHandName) && (name == hfmModel.hfmToHifiJointNameMapping[jointLeftHandName]))) { - jointLeftHandID = getID(object.properties); - - } else if (name == jointRightHandName || name == "RightHand" || name == "joint_R_hand" || (hfmModel.hfmToHifiJointNameMapping.contains(jointRightHandName) && (name == hfmModel.hfmToHifiJointNameMapping[jointRightHandName]))) { - jointRightHandID = getID(object.properties); - - } else if (name == "LeftToe" || name == "joint_L_toe" || name == "LeftToe_End" || (hfmModel.hfmToHifiJointNameMapping.contains("LeftToe") && (name == hfmModel.hfmToHifiJointNameMapping["LeftToe"]))) { - jointLeftToeID = getID(object.properties); - - } else if (name == "RightToe" || name == "joint_R_toe" || name == "RightToe_End" || (hfmModel.hfmToHifiJointNameMapping.contains("RightToe") && (name == hfmModel.hfmToHifiJointNameMapping["RightToe"]))) { - jointRightToeID = getID(object.properties); - } - + int humanIKJointIndex = humanIKJointNames.indexOf(name); if (humanIKJointIndex != -1) { humanIKJointIDs[humanIKJointIndex] = getID(object.properties); @@ -1449,28 +1400,10 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr std::vector shapeVertices; shapeVertices.resize(std::max(1, hfmModel.joints.size()) ); - // find our special joints - hfmModel.leftEyeJointIndex = modelIDs.indexOf(jointEyeLeftID); - hfmModel.rightEyeJointIndex = modelIDs.indexOf(jointEyeRightID); - hfmModel.neckJointIndex = modelIDs.indexOf(jointNeckID); - hfmModel.rootJointIndex = modelIDs.indexOf(jointRootID); - hfmModel.leanJointIndex = modelIDs.indexOf(jointLeanID); - hfmModel.headJointIndex = modelIDs.indexOf(jointHeadID); - hfmModel.leftHandJointIndex = modelIDs.indexOf(jointLeftHandID); - hfmModel.rightHandJointIndex = modelIDs.indexOf(jointRightHandID); - hfmModel.leftToeJointIndex = modelIDs.indexOf(jointLeftToeID); - hfmModel.rightToeJointIndex = modelIDs.indexOf(jointRightToeID); - foreach (const QString& id, humanIKJointIDs) { hfmModel.humanIKJointIndices.append(modelIDs.indexOf(id)); } - // extract the translation component of the neck transform - if (hfmModel.neckJointIndex != -1) { - const glm::mat4& transform = hfmModel.joints.at(hfmModel.neckJointIndex).transform; - hfmModel.neckPivot = glm::vec3(transform[3][0], transform[3][1], transform[3][2]); - } - hfmModel.bindExtents.reset(); hfmModel.meshExtents.reset(); diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 86d4793aa5..3e32d19b49 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -137,7 +137,7 @@ void CauterizedModel::updateClusterMatrices() { // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. if (!_cauterizeBoneSet.empty()) { - AnimPose cauterizePose = _rig.getJointPose(hfmModel.neckJointIndex); + AnimPose cauterizePose = _rig.getJointPose(_rig.indexOfJoint("Neck")); cauterizePose.scale() = glm::vec3(0.0001f, 0.0001f, 0.0001f); static const glm::mat4 zeroScale( @@ -145,7 +145,7 @@ void CauterizedModel::updateClusterMatrices() { glm::vec4(0.0f, 0.0001f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0001f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); - auto cauterizeMatrix = _rig.getJointTransform(hfmModel.neckJointIndex) * zeroScale; + auto cauterizeMatrix = _rig.getJointTransform(_rig.indexOfJoint("Neck")) * zeroScale; for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; From 6e5c52972a6515e711dc02316d7501c95d5c2cfc Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 17 Dec 2018 10:42:08 -0800 Subject: [PATCH 13/27] removed member declarations in hfm.h and debug print statements --- libraries/fbx/src/FBXSerializer.cpp | 2 +- libraries/fbx/src/GLTFSerializer.cpp | 10 ---------- libraries/hfm/src/hfm/HFM.h | 11 ----------- 3 files changed, 1 insertion(+), 22 deletions(-) diff --git a/libraries/fbx/src/FBXSerializer.cpp b/libraries/fbx/src/FBXSerializer.cpp index 38032653f4..8ac0ff099d 100644 --- a/libraries/fbx/src/FBXSerializer.cpp +++ b/libraries/fbx/src/FBXSerializer.cpp @@ -583,7 +583,7 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr if (modelname.startsWith("hifi")) { hifiGlobalNodeID = id; } - + int humanIKJointIndex = humanIKJointNames.indexOf(name); if (humanIKJointIndex != -1) { humanIKJointIDs[humanIKJointIndex] = getID(object.properties); diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index e254a91eb0..0ff06fedf8 100644 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -1188,16 +1188,6 @@ void GLTFSerializer::hfmDebugDump(const HFMModel& hfmModel) { qCDebug(modelformat) << " hasSkeletonJoints =" << hfmModel.hasSkeletonJoints; qCDebug(modelformat) << " offset =" << hfmModel.offset; - qCDebug(modelformat) << " leftEyeJointIndex =" << hfmModel.leftEyeJointIndex; - qCDebug(modelformat) << " rightEyeJointIndex =" << hfmModel.rightEyeJointIndex; - qCDebug(modelformat) << " neckJointIndex =" << hfmModel.neckJointIndex; - qCDebug(modelformat) << " rootJointIndex =" << hfmModel.rootJointIndex; - qCDebug(modelformat) << " leanJointIndex =" << hfmModel.leanJointIndex; - qCDebug(modelformat) << " headJointIndex =" << hfmModel.headJointIndex; - qCDebug(modelformat) << " leftHandJointIndex" << hfmModel.leftHandJointIndex; - qCDebug(modelformat) << " rightHandJointIndex" << hfmModel.rightHandJointIndex; - qCDebug(modelformat) << " leftToeJointIndex" << hfmModel.leftToeJointIndex; - qCDebug(modelformat) << " rightToeJointIndex" << hfmModel.rightToeJointIndex; qCDebug(modelformat) << " leftEyeSize = " << hfmModel.leftEyeSize; qCDebug(modelformat) << " rightEyeSize = " << hfmModel.rightEyeSize; diff --git a/libraries/hfm/src/hfm/HFM.h b/libraries/hfm/src/hfm/HFM.h index de58d864b3..988a7a9838 100644 --- a/libraries/hfm/src/hfm/HFM.h +++ b/libraries/hfm/src/hfm/HFM.h @@ -270,17 +270,6 @@ public: glm::mat4 offset; // This includes offset, rotation, and scale as specified by the FST file - int leftEyeJointIndex = -1; - int rightEyeJointIndex = -1; - int neckJointIndex = -1; - int rootJointIndex = -1; - int leanJointIndex = -1; - int headJointIndex = -1; - int leftHandJointIndex = -1; - int rightHandJointIndex = -1; - int leftToeJointIndex = -1; - int rightToeJointIndex = -1; - float leftEyeSize = 0.0f; // Maximum mesh extents dimension float rightEyeSize = 0.0f; From defdc9fbcb2cb961d179f46feaf22e6c6dc82091 Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 17 Dec 2018 10:56:53 -0800 Subject: [PATCH 14/27] removed humanIK joint name code from fbx serializer --- libraries/fbx/src/FBXSerializer.cpp | 36 ---------------------------- libraries/fbx/src/GLTFSerializer.cpp | 3 --- libraries/hfm/src/hfm/HFM.h | 5 ---- 3 files changed, 44 deletions(-) diff --git a/libraries/fbx/src/FBXSerializer.cpp b/libraries/fbx/src/FBXSerializer.cpp index 8ac0ff099d..35da63cac7 100644 --- a/libraries/fbx/src/FBXSerializer.cpp +++ b/libraries/fbx/src/FBXSerializer.cpp @@ -127,26 +127,6 @@ QString getID(const QVariantList& properties, int index = 0) { return processID(properties.at(index).toString()); } -/// The names of the joints in the Maya HumanIK rig -static const std::array HUMANIK_JOINTS = {{ - "RightHand", - "RightForeArm", - "RightArm", - "Head", - "LeftArm", - "LeftForeArm", - "LeftHand", - "Neck", - "Spine", - "Hips", - "RightUpLeg", - "LeftUpLeg", - "RightLeg", - "LeftLeg", - "RightFoot", - "LeftFoot" -}}; - class FBXModel { public: QString name; @@ -480,13 +460,6 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr QVariantHash joints = mapping.value("joint").toHash(); - QVector humanIKJointNames; - for (int i = 0; i < (int) HUMANIK_JOINTS.size(); i++) { - QByteArray jointName = HUMANIK_JOINTS[i]; - humanIKJointNames.append(processID(getString(joints.value(jointName, jointName)))); - } - QVector humanIKJointIDs(humanIKJointNames.size()); - QVariantHash blendshapeMappings = mapping.value("bs").toHash(); QMultiHash blendshapeIndices; @@ -584,11 +557,6 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr hifiGlobalNodeID = id; } - int humanIKJointIndex = humanIKJointNames.indexOf(name); - if (humanIKJointIndex != -1) { - humanIKJointIDs[humanIKJointIndex] = getID(object.properties); - } - glm::vec3 translation; // NOTE: the euler angles as supplied by the FBX file are in degrees glm::vec3 rotationOffset; @@ -1400,10 +1368,6 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr std::vector shapeVertices; shapeVertices.resize(std::max(1, hfmModel.joints.size()) ); - foreach (const QString& id, humanIKJointIDs) { - hfmModel.humanIKJointIndices.append(modelIDs.indexOf(id)); - } - hfmModel.bindExtents.reset(); hfmModel.meshExtents.reset(); diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index 0ff06fedf8..ede2f2d63c 100644 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -1188,9 +1188,6 @@ void GLTFSerializer::hfmDebugDump(const HFMModel& hfmModel) { qCDebug(modelformat) << " hasSkeletonJoints =" << hfmModel.hasSkeletonJoints; qCDebug(modelformat) << " offset =" << hfmModel.offset; - qCDebug(modelformat) << " leftEyeSize = " << hfmModel.leftEyeSize; - qCDebug(modelformat) << " rightEyeSize = " << hfmModel.rightEyeSize; - qCDebug(modelformat) << " palmDirection = " << hfmModel.palmDirection; qCDebug(modelformat) << " neckPivot = " << hfmModel.neckPivot; diff --git a/libraries/hfm/src/hfm/HFM.h b/libraries/hfm/src/hfm/HFM.h index 988a7a9838..b6ddbd1e2d 100644 --- a/libraries/hfm/src/hfm/HFM.h +++ b/libraries/hfm/src/hfm/HFM.h @@ -270,11 +270,6 @@ public: glm::mat4 offset; // This includes offset, rotation, and scale as specified by the FST file - float leftEyeSize = 0.0f; // Maximum mesh extents dimension - float rightEyeSize = 0.0f; - - QVector humanIKJointIndices; - glm::vec3 palmDirection; glm::vec3 neckPivot; From 3e1beb2dcaa155918213b4b69099af69df5b64f7 Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 17 Dec 2018 15:05:51 -0800 Subject: [PATCH 15/27] removed white space and stray semi colon --- interface/src/avatar/MySkeletonModel.cpp | 2 +- libraries/fbx/src/FBXSerializer.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 0c72766424..356b365f93 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -302,7 +302,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { eyeParams.modelRotation = getRotation(); eyeParams.modelTranslation = getTranslation(); eyeParams.leftEyeJointIndex = _rig.indexOfJoint("LeftEye"); - eyeParams.rightEyeJointIndex = _rig.indexOfJoint("RightEye");; + eyeParams.rightEyeJointIndex = _rig.indexOfJoint("RightEye"); _rig.updateFromEyeParameters(eyeParams); diff --git a/libraries/fbx/src/FBXSerializer.cpp b/libraries/fbx/src/FBXSerializer.cpp index 35da63cac7..d1b5033461 100644 --- a/libraries/fbx/src/FBXSerializer.cpp +++ b/libraries/fbx/src/FBXSerializer.cpp @@ -21,7 +21,6 @@ #include #include - #include #include #include From 42a17980cb9598807947fcb3711a2207be291111 Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 17 Dec 2018 16:21:22 -0800 Subject: [PATCH 16/27] fixed mac build conflict, unused variable --- .../avatars-renderer/src/avatars-renderer/SkeletonModel.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index f390bb257e..7f2dbda3de 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -96,7 +96,6 @@ void SkeletonModel::initJointStates() { // Called within Model::simulate call, below. void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { assert(!_owningAvatar->isMyAvatar()); - const HFMModel& hfmModel = getHFMModel(); Head* head = _owningAvatar->getHead(); @@ -274,7 +273,6 @@ bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& if (!isActive()) { return false; } - const HFMModel& hfmModel = getHFMModel(); if (getJointPosition(_rig.indexOfJoint("LeftEye"), firstEyePosition) && getJointPosition(_rig.indexOfJoint("RightEye"), secondEyePosition)) { From b82ba8c085c97958a2bd0f91dc2a7dc76ed6202c Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 18 Dec 2018 12:51:01 -0800 Subject: [PATCH 17/27] do nothing with file>login in login state --- interface/src/ui/LoginDialog.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index b343d2a1ad..6e9c91785f 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -95,7 +95,12 @@ void LoginDialog::toggleAction() { } else { // change the menu item to login loginAction->setText("Log In / Sign Up"); - connection = connect(loginAction, &QAction::triggered, [] { LoginDialog::showWithSelection(); }); + connection = connect(loginAction, &QAction::triggered, [] { + // if not in login state, show. + if (!qApp->getLoginDialogPoppedUp()) { + LoginDialog::showWithSelection(); + } + }); } } From 7231116e43c1da7bd9f15717a8166d60e5ca6be1 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 18 Dec 2018 12:39:33 -0800 Subject: [PATCH 18/27] fix zone with invalid script not loading --- .../src/EntityTreeRenderer.cpp | 52 +++++++------------ .../src/EntityTreeRenderer.h | 18 +------ 2 files changed, 22 insertions(+), 48 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 980ff8834c..49d95ed536 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -61,8 +61,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf _lastPointerEventValid(false), _viewState(viewState), _scriptingServices(scriptingServices), - _displayModelBounds(false), - _layeredZones(this) + _displayModelBounds(false) { setMouseRayPickResultOperator([](unsigned int rayPickID) { RayToEntityIntersectionResult entityResult; @@ -516,41 +515,36 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QVectorgetScript().isEmpty(); // only consider entities that are zones or have scripts, all other entities can - // be ignored because they can have events fired on them. + // be ignored because they can't have events fired on them. // FIXME - this could be optimized further by determining if the script is loaded // and if it has either an enterEntity or leaveEntity method // // also, don't flag a scripted entity as containing the avatar until the script is loaded, // so that the script is awake in time to receive the "entityEntity" call (even if the entity is a zone). - if ((!hasScript && isZone) || - (hasScript && entity->isScriptPreloadFinished())) { - // now check to see if the point contains our entity, this can be expensive if - // the entity has a collision hull - if (entity->contains(_avatarPosition)) { + bool contains = false; + bool scriptHasLoaded = hasScript && entity->isScriptPreloadFinished(); + if (isZone || scriptHasLoaded) { + contains = entity->contains(_avatarPosition); + } + + if (contains) { + // if this entity is a zone and visible, add it to our layered zones + if (isZone && entity->getVisible() && renderableForEntity(entity)) { + _layeredZones.insert(std::dynamic_pointer_cast(entity)); + } + + if ((!hasScript && isZone) || scriptHasLoaded) { if (entitiesContainingAvatar) { *entitiesContainingAvatar << entity->getEntityItemID(); } - - // if this entity is a zone and visible, determine if it is the bestZone - if (isZone && entity->getVisible() && renderableForEntity(entity)) { - auto zone = std::dynamic_pointer_cast(entity); - _layeredZones.insert(zone); - } - } } } + } // check if our layered zones have changed - if (_layeredZones.empty()) { - if (oldLayeredZones.empty()) { - return; - } - } else if (!oldLayeredZones.empty()) { - if (_layeredZones.contains(oldLayeredZones)) { - return; - } + if ((_layeredZones.empty() && oldLayeredZones.empty()) || (!oldLayeredZones.empty() && _layeredZones.contains(oldLayeredZones))) { + return; } - _layeredZones.apply(); applyLayeredZones(); @@ -653,8 +647,8 @@ bool EntityTreeRenderer::applyLayeredZones() { } else { qCWarning(entitiesrenderer) << "EntityTreeRenderer::applyLayeredZones(), Unexpected null scene, possibly during application shutdown"; } - - return true; + + return true; } void EntityTreeRenderer::processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode) { @@ -1151,18 +1145,12 @@ std::pair EntityTreeRenderer:: return { it, success }; } -void EntityTreeRenderer::LayeredZones::apply() { - assert(_entityTreeRenderer); -} - void EntityTreeRenderer::LayeredZones::update(std::shared_ptr zone) { - assert(_entityTreeRenderer); bool isVisible = zone->isVisible(); if (empty() && isVisible) { // there are no zones: set this one insert(zone); - apply(); return; } else { LayeredZone zoneLayer(zone); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 4ba1a0060b..4cc2b07cc5 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -205,42 +205,28 @@ private: class LayeredZones : public std::set { public: - LayeredZones(EntityTreeRenderer* parent) : _entityTreeRenderer(parent) {} + LayeredZones() {}; LayeredZones(LayeredZones&& other); // avoid accidental misconstruction - LayeredZones() = delete; LayeredZones(const LayeredZones&) = delete; LayeredZones& operator=(const LayeredZones&) = delete; LayeredZones& operator=(LayeredZones&&) = delete; void clear(); std::pair insert(const LayeredZone& layer); - - void apply(); void update(std::shared_ptr zone); - bool contains(const LayeredZones& other); std::shared_ptr getZone() { return empty() ? nullptr : begin()->zone; } private: - void applyPartial(iterator layer); - std::map _map; - iterator _skyboxLayer{ end() }; - EntityTreeRenderer* _entityTreeRenderer; + iterator _skyboxLayer { end() }; }; LayeredZones _layeredZones; - QString _zoneUserData; - NetworkTexturePointer _ambientTexture; - NetworkTexturePointer _skyboxTexture; - QString _ambientTextureURL; - QString _skyboxTextureURL; float _avgRenderableUpdateCost { 0.0f }; - bool _pendingAmbientTexture { false }; - bool _pendingSkyboxTexture { false }; uint64_t _lastZoneCheck { 0 }; const uint64_t ZONE_CHECK_INTERVAL = USECS_PER_MSEC * 100; // ~10hz From 017eb9d1a33bace87fae14aa0a5e583d09b429a8 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 18 Dec 2018 16:32:44 -0800 Subject: [PATCH 19/27] adding fix for scripts not being saved --- interface/src/Application.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 36fb666f69..cce4b14868 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5252,7 +5252,8 @@ void Application::resumeAfterLoginDialogActionTaken() { // this will force the model the look at the correct directory (weird order of operations issue) scriptEngines->reloadLocalFiles(); - if (!_defaultScriptsLocation.exists()) { + // if the --scripts command-line argument was used. + if (!_defaultScriptsLocation.exists() && (arguments().indexOf(QString("--").append(SCRIPTS_SWITCH))) != -1) { scriptEngines->loadDefaultScripts(); scriptEngines->defaultScriptsLocationOverridden(true); } else { From f407042f3e983670771f2d5fcbd0a2ae815cefc0 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Dec 2018 17:11:10 -0800 Subject: [PATCH 20/27] Quick polish fix to prevent UI flashing --- interface/resources/qml/hifi/commerce/purchases/Purchases.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index f50ce0bda3..af61cfa410 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -39,7 +39,8 @@ Rectangle { property int numUpdatesAvailable: 0; // Style color: hifi.colors.white; - function getPurchases() { + function + () { root.activeView = "purchasesMain"; root.installedApps = Commerce.getInstalledApps(); purchasesModel.getFirstPage(); @@ -669,6 +670,7 @@ Rectangle { lightboxPopup.titleText = '"' + msg.itemName + '" Sent to Trash'; lightboxPopup.button1text = "OK"; lightboxPopup.button1method = function() { + root.purchasesReceived = false; lightboxPopup.visible = false; getPurchases(); } From 116edcc224c45edc023699b024a479173b514dcd Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 19 Dec 2018 09:36:05 -0800 Subject: [PATCH 21/27] I have no idea how this happened --- interface/resources/qml/hifi/commerce/purchases/Purchases.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index af61cfa410..67a61a2835 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -39,8 +39,7 @@ Rectangle { property int numUpdatesAvailable: 0; // Style color: hifi.colors.white; - function - () { + function getPurchases() { root.activeView = "purchasesMain"; root.installedApps = Commerce.getInstalledApps(); purchasesModel.getFirstPage(); From 7fee0fc1e452eeee1a2b0e56ab02dd15bbed38e9 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 19 Dec 2018 09:50:52 -0800 Subject: [PATCH 22/27] Fix MS20253: Only emit screenChanged once screen has changed --- .../resources/qml/hifi/tablet/TabletRoot.qml | 21 +++++++++++++++++++ .../resources/qml/hifi/tablet/WindowRoot.qml | 21 +++++++++++++++++++ .../ui/src/ui/TabletScriptingInterface.cpp | 8 ++----- 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index fa268ad6ee..a01d978b2f 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -15,6 +15,7 @@ Item { property var openBrowser: null; property string subMenu: "" signal showDesktop(); + signal screenChanged(var type, var url); property bool shown: true property int currentApp: -1; property alias tabletApps: tabletApps @@ -113,6 +114,8 @@ Item { if (loader.item.hasOwnProperty("gotoPreviousApp")) { loader.item.gotoPreviousApp = true; } + + screenChanged("Web", url) }); } } @@ -266,6 +269,24 @@ Item { if (callback) { callback(); } + + var type = "Unknown"; + if (newSource === "") { + type = "Closed"; + } else if (newSource === "hifi/tablet/TabletMenu.qml") { + type = "Menu"; + } else if (newSource === "hifi/tablet/TabletHome.qml") { + type = "Home"; + } else if (newSource === "hifi/tablet/TabletWebView.qml") { + // Handled in `callback()` + return; + } else if (newSource.toLowerCase().indexOf(".qml") > -1) { + type = "QML"; + } else { + console.log("newSource is of unknown type!"); + } + + screenChanged(type, newSource); }); } } diff --git a/interface/resources/qml/hifi/tablet/WindowRoot.qml b/interface/resources/qml/hifi/tablet/WindowRoot.qml index d55ec363f0..ef2df5e218 100644 --- a/interface/resources/qml/hifi/tablet/WindowRoot.qml +++ b/interface/resources/qml/hifi/tablet/WindowRoot.qml @@ -20,6 +20,7 @@ Windows.ScrollingWindow { id: tabletRoot objectName: "tabletRoot" property string username: "Unknown user" + signal screenChanged(var type, var url); property var rootMenu; property string subMenu: "" @@ -69,6 +70,8 @@ Windows.ScrollingWindow { if (loader.item.hasOwnProperty("closeButtonVisible")) { loader.item.closeButtonVisible = false; } + + screenChanged("Web", url); }); } @@ -179,7 +182,25 @@ Windows.ScrollingWindow { if (callback) { callback(); + } + + var type = "Unknown"; + if (newSource === "") { + type = "Closed"; + } else if (newSource === "hifi/tablet/TabletMenu.qml") { + type = "Menu"; + } else if (newSource === "hifi/tablet/TabletHome.qml") { + type = "Home"; + } else if (newSource === "hifi/tablet/TabletWebView.qml") { + // Handled in `callback()` + return; + } else if (newSource.toLowerCase().indexOf(".qml") > -1) { + type = "QML"; + } else { + console.log("newSource is of unknown type!"); } + + screenChanged(type, newSource); }); } } diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 13b0498e76..e75687c512 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -377,6 +377,7 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { QObject::connect(quickItem, SIGNAL(windowClosed()), this, SLOT(desktopWindowClosed())); QObject::connect(tabletRootWindow, SIGNAL(webEventReceived(QVariant)), this, SLOT(emitWebEvent(QVariant)), Qt::DirectConnection); + QObject::connect(quickItem, SIGNAL(screenChanged(QVariant, QVariant)), this, SIGNAL(screenChanged(QVariant, QVariant)), Qt::DirectConnection); // forward qml surface events to interface js connect(tabletRootWindow, &QmlWindowClass::fromQml, this, &TabletProxy::fromQml); @@ -488,6 +489,7 @@ void TabletProxy::setQmlTabletRoot(OffscreenQmlSurface* qmlOffscreenSurface) { _qmlTabletRoot = qmlOffscreenSurface ? qmlOffscreenSurface->getRootItem() : nullptr; if (_qmlTabletRoot && _qmlOffscreenSurface) { QObject::connect(_qmlOffscreenSurface, SIGNAL(webEventReceived(QVariant)), this, SLOT(emitWebEvent(QVariant))); + QObject::connect(_qmlTabletRoot, SIGNAL(screenChanged(QVariant, QVariant)), this, SIGNAL(screenChanged(QVariant, QVariant))); // forward qml surface events to interface js connect(_qmlOffscreenSurface, &OffscreenQmlSurface::fromQml, [this](QVariant message) { @@ -570,7 +572,6 @@ void TabletProxy::gotoMenuScreen(const QString& submenu) { QMetaObject::invokeMethod(root, "setMenuProperties", Q_ARG(QVariant, QVariant::fromValue(menu)), Q_ARG(const QVariant&, QVariant(submenu))); QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, QVariant(VRMENU_SOURCE_URL))); _state = State::Menu; - emit screenChanged(QVariant("Menu"), QVariant(VRMENU_SOURCE_URL)); _currentPathLoaded = VRMENU_SOURCE_URL; QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); if (_toolbarMode && _desktopWindow) { @@ -640,9 +641,6 @@ void TabletProxy::loadQMLSource(const QVariant& path, bool resizable) { if (root) { QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, path)); _state = State::QML; - if (path != _currentPathLoaded) { - emit screenChanged(QVariant("QML"), path); - } _currentPathLoaded = path; QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); if (_toolbarMode && _desktopWindow) { @@ -749,7 +747,6 @@ void TabletProxy::loadHomeScreen(bool forceOntoHomeScreen) { } } _state = State::Home; - emit screenChanged(QVariant("Home"), QVariant(TABLET_HOME_SOURCE_URL)); _currentPathLoaded = TABLET_HOME_SOURCE_URL; } } @@ -810,7 +807,6 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS QMetaObject::invokeMethod(root, "setResizable", Q_ARG(const QVariant&, QVariant(false))); } _state = State::Web; - emit screenChanged(QVariant("Web"), QVariant(url)); _currentPathLoaded = QVariant(url); } else { // tablet is not initialized yet, save information and load when From 50d57e8b60ae1ce979ca97fff8dea33d270f2a43 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 19 Dec 2018 12:37:08 -0800 Subject: [PATCH 23/27] sending user to previous location on second launch --- interface/src/Application.cpp | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cce4b14868..baa680c5a5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2460,6 +2460,10 @@ void Application::updateHeartbeat() const { void Application::onAboutToQuit() { emit beforeAboutToQuit(); + if (getLoginDialogPoppedUp() && _firstRun.get()) { + _firstRun.set(false); + } + foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { if (inputPlugin->isActive()) { inputPlugin->deactivate(); @@ -5261,39 +5265,25 @@ void Application::resumeAfterLoginDialogActionTaken() { } } - if (_firstRun.get()) { - // not first run anymore since action was taken. - _firstRun.set(false); - } - auto accountManager = DependencyManager::get(); auto addressManager = DependencyManager::get(); // restart domain handler. nodeList->getDomainHandler().resetting(); - if (!accountManager->isLoggedIn()) { + QVariant testProperty = property(hifi::properties::TEST); + if (testProperty.isValid()) { + const auto testScript = property(hifi::properties::TEST).toUrl(); + // Set last parameter to exit interface when the test script finishes, if so requested + DependencyManager::get()->loadScript(testScript, false, false, false, false, quitWhenFinished); + // This is done so we don't get a "connection time-out" message when we haven't passed in a URL. if (arguments().contains("--url")) { auto reply = SandboxUtils::getStatus(); connect(reply, &QNetworkReply::finished, this, [this, reply] { handleSandboxStatus(reply); }); - } else { - addressManager->goToEntry(); } } else { - QVariant testProperty = property(hifi::properties::TEST); - if (testProperty.isValid()) { - const auto testScript = property(hifi::properties::TEST).toUrl(); - // Set last parameter to exit interface when the test script finishes, if so requested - DependencyManager::get()->loadScript(testScript, false, false, false, false, quitWhenFinished); - // This is done so we don't get a "connection time-out" message when we haven't passed in a URL. - if (arguments().contains("--url")) { - auto reply = SandboxUtils::getStatus(); - connect(reply, &QNetworkReply::finished, this, [this, reply] { handleSandboxStatus(reply); }); - } - } else { - auto reply = SandboxUtils::getStatus(); - connect(reply, &QNetworkReply::finished, this, [this, reply] { handleSandboxStatus(reply); }); - } + auto reply = SandboxUtils::getStatus(); + connect(reply, &QNetworkReply::finished, this, [this, reply] { handleSandboxStatus(reply); }); } auto menu = Menu::getInstance(); From c36b3a4b070c01f6437cd6a5cb91e5390268bc73 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 19 Dec 2018 12:46:41 -0800 Subject: [PATCH 24/27] fix --- scripts/system/commerce/wallet.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index adc09b507f..19efdc042c 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -433,7 +433,6 @@ function fromQml(message) { } break; case 'needsLogIn_loginClicked': - ui.close(); openLoginWindow(); break; case 'disableHmdPreview': From d0998174ffc7ca028cc33274a1bcc9759713589d Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 19 Dec 2018 15:16:48 -0800 Subject: [PATCH 25/27] Un-wear things when trashing them if worn --- .../hifi/commerce/purchases/PurchasedItem.qml | 30 ++++++++++++++----- .../qml/hifi/commerce/purchases/Purchases.qml | 10 +++++++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index 12a9912e8e..f7dc26df5f 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -313,6 +313,10 @@ Item { Commerce.uninstallApp(root.itemHref); } } + + onVisibleChanged: { + trashButton.updateProperties(); + } } Loader { @@ -342,6 +346,10 @@ Item { }); } } + + onVisibleChanged: { + trashButton.updateProperties(); + } } Loader { @@ -355,6 +363,20 @@ Item { onLoaded: { item.buttonGlyphText = hifi.glyphs.trash; + updateProperties(); + item.buttonClicked = function() { + sendToPurchases({method: 'showTrashLightbox', + isInstalled: root.isInstalled, + itemHref: root.itemHref, + itemName: root.itemName, + certID: root.certificateId, + itemType: root.itemType, + wornEntityID: root.wornEntityID + }); + } + } + + function updateProperties() { if (updateButton.visible && uninstallButton.visible) { item.buttonText = ""; item.glyphSize = 20; @@ -362,14 +384,6 @@ Item { item.buttonText = "Send to Trash"; item.glyphSize = 30; } - item.buttonClicked = function() { - sendToPurchases({method: 'showTrashLightbox', - isInstalled: root.isInstalled, - itemHref: root.itemHref, - itemName: root.itemName, - certID: root.certificateId - }); - } } } } diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index f50ce0bda3..873e8bef1d 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -664,6 +664,16 @@ Rectangle { if (msg.isInstalled) { Commerce.uninstallApp(msg.itemHref); } + + if (MyAvatar.skeletonModelURL === msg.itemHref) { + MyAvatar.useFullAvatarURL(''); + } + + if (msg.itemType === "wearable" && msg.wornEntityID !== '') { + Entities.deleteEntity(msg.wornEntityID); + purchasesModel.setProperty(index, 'wornEntityID', ''); + } + Commerce.transferAssetToUsername("trashbot", msg.certID, 1, "Sent " + msg.itemName + " to trash."); lightboxPopup.titleText = '"' + msg.itemName + '" Sent to Trash'; From 52bfaafd911575d62f04312755eb64cc90765bb7 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 20 Dec 2018 13:25:19 -0700 Subject: [PATCH 26/27] Fix crash on glTF import when the file contains embedded buffers --- libraries/fbx/src/GLTFSerializer.cpp | 60 ++++++++++++++-------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index ede2f2d63c..3e6ba71399 100644 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -533,14 +533,13 @@ bool GLTFSerializer::parseGLTF(const QByteArray& data) { QJsonDocument d = QJsonDocument::fromJson(data); QJsonObject jsFile = d.object(); - - bool isvalid = setAsset(jsFile); - if (isvalid) { + bool success = setAsset(jsFile); + if (success) { QJsonArray accessors; if (getObjectArrayVal(jsFile, "accessors", accessors, _file.defined)) { foreach(const QJsonValue & accVal, accessors) { if (accVal.isObject()) { - addAccessor(accVal.toObject()); + success = success && addAccessor(accVal.toObject()); } } } @@ -549,7 +548,7 @@ bool GLTFSerializer::parseGLTF(const QByteArray& data) { if (getObjectArrayVal(jsFile, "animations", animations, _file.defined)) { foreach(const QJsonValue & animVal, accessors) { if (animVal.isObject()) { - addAnimation(animVal.toObject()); + success = success && addAnimation(animVal.toObject()); } } } @@ -558,7 +557,7 @@ bool GLTFSerializer::parseGLTF(const QByteArray& data) { if (getObjectArrayVal(jsFile, "bufferViews", bufferViews, _file.defined)) { foreach(const QJsonValue & bufviewVal, bufferViews) { if (bufviewVal.isObject()) { - addBufferView(bufviewVal.toObject()); + success = success && addBufferView(bufviewVal.toObject()); } } } @@ -567,7 +566,7 @@ bool GLTFSerializer::parseGLTF(const QByteArray& data) { if (getObjectArrayVal(jsFile, "buffers", buffers, _file.defined)) { foreach(const QJsonValue & bufVal, buffers) { if (bufVal.isObject()) { - addBuffer(bufVal.toObject()); + success = success && addBuffer(bufVal.toObject()); } } } @@ -576,7 +575,7 @@ bool GLTFSerializer::parseGLTF(const QByteArray& data) { if (getObjectArrayVal(jsFile, "cameras", cameras, _file.defined)) { foreach(const QJsonValue & camVal, cameras) { if (camVal.isObject()) { - addCamera(camVal.toObject()); + success = success && addCamera(camVal.toObject()); } } } @@ -585,7 +584,7 @@ bool GLTFSerializer::parseGLTF(const QByteArray& data) { if (getObjectArrayVal(jsFile, "images", images, _file.defined)) { foreach(const QJsonValue & imgVal, images) { if (imgVal.isObject()) { - addImage(imgVal.toObject()); + success = success && addImage(imgVal.toObject()); } } } @@ -594,7 +593,7 @@ bool GLTFSerializer::parseGLTF(const QByteArray& data) { if (getObjectArrayVal(jsFile, "materials", materials, _file.defined)) { foreach(const QJsonValue & matVal, materials) { if (matVal.isObject()) { - addMaterial(matVal.toObject()); + success = success && addMaterial(matVal.toObject()); } } } @@ -603,7 +602,7 @@ bool GLTFSerializer::parseGLTF(const QByteArray& data) { if (getObjectArrayVal(jsFile, "meshes", meshes, _file.defined)) { foreach(const QJsonValue & meshVal, meshes) { if (meshVal.isObject()) { - addMesh(meshVal.toObject()); + success = success && addMesh(meshVal.toObject()); } } } @@ -612,7 +611,7 @@ bool GLTFSerializer::parseGLTF(const QByteArray& data) { if (getObjectArrayVal(jsFile, "nodes", nodes, _file.defined)) { foreach(const QJsonValue & nodeVal, nodes) { if (nodeVal.isObject()) { - addNode(nodeVal.toObject()); + success = success && addNode(nodeVal.toObject()); } } } @@ -621,7 +620,7 @@ bool GLTFSerializer::parseGLTF(const QByteArray& data) { if (getObjectArrayVal(jsFile, "samplers", samplers, _file.defined)) { foreach(const QJsonValue & samVal, samplers) { if (samVal.isObject()) { - addSampler(samVal.toObject()); + success = success && addSampler(samVal.toObject()); } } } @@ -630,7 +629,7 @@ bool GLTFSerializer::parseGLTF(const QByteArray& data) { if (getObjectArrayVal(jsFile, "scenes", scenes, _file.defined)) { foreach(const QJsonValue & sceneVal, scenes) { if (sceneVal.isObject()) { - addScene(sceneVal.toObject()); + success = success && addScene(sceneVal.toObject()); } } } @@ -639,7 +638,7 @@ bool GLTFSerializer::parseGLTF(const QByteArray& data) { if (getObjectArrayVal(jsFile, "skins", skins, _file.defined)) { foreach(const QJsonValue & skinVal, skins) { if (skinVal.isObject()) { - addSkin(skinVal.toObject()); + success = success && addSkin(skinVal.toObject()); } } } @@ -648,15 +647,12 @@ bool GLTFSerializer::parseGLTF(const QByteArray& data) { if (getObjectArrayVal(jsFile, "textures", textures, _file.defined)) { foreach(const QJsonValue & texVal, textures) { if (texVal.isObject()) { - addTexture(texVal.toObject()); + success = success && addTexture(texVal.toObject()); } } } - } else { - qCDebug(modelformat) << "Error parsing GLTF file."; - return false; - } - return true; + } + return success; } glm::mat4 GLTFSerializer::getModelTransform(const GLTFNode& node) { @@ -927,16 +923,20 @@ HFMModel::Pointer GLTFSerializer::read(const QByteArray& data, const QVariantHas _url = QUrl(QFileInfo(localFileName).absoluteFilePath()); } - parseGLTF(data); - //_file.dump(); - auto hfmModelPtr = std::make_shared(); - HFMModel& hfmModel = *hfmModelPtr; + if (parseGLTF(data)) { + //_file.dump(); + auto hfmModelPtr = std::make_shared(); + HFMModel& hfmModel = *hfmModelPtr; - buildGeometry(hfmModel, _url); - - //hfmDebugDump(data); - return hfmModelPtr; - + buildGeometry(hfmModel, _url); + + //hfmDebugDump(data); + return hfmModelPtr; + } else { + qCDebug(modelformat) << "Error parsing GLTF file."; + } + + return nullptr; } bool GLTFSerializer::readBinary(const QString& url, QByteArray& outdata) { From 097d2d9503da50d972fc53b07bec53a7e3e90799 Mon Sep 17 00:00:00 2001 From: Sam Gondelman Date: Thu, 20 Dec 2018 16:53:33 -0800 Subject: [PATCH 27/27] Revert "Case 20200: Update EntityScriptingInterface to not find local entities" --- interface/src/Application.cpp | 12 +- interface/src/Application.h | 2 +- interface/src/avatar/MyAvatar.cpp | 12 +- interface/src/raypick/LaserPointer.cpp | 6 +- .../raypick/LaserPointerScriptingInterface.h | 2 +- interface/src/raypick/ParabolaPick.cpp | 14 +- interface/src/raypick/PathPointer.cpp | 12 +- .../src/raypick/PickScriptingInterface.cpp | 22 +- .../src/raypick/PickScriptingInterface.h | 131 +++--- .../src/raypick/PointerScriptingInterface.h | 9 +- interface/src/raypick/RayPick.cpp | 13 +- .../src/raypick/RayPickScriptingInterface.h | 10 +- interface/src/raypick/StylusPointer.cpp | 2 +- interface/src/ui/Keyboard.cpp | 2 +- .../src/EntityTreeRenderer.cpp | 13 +- .../entities/src/EntityScriptingInterface.cpp | 130 +++--- .../entities/src/EntityScriptingInterface.h | 95 +++-- libraries/entities/src/EntityTree.cpp | 374 ++++++++---------- libraries/entities/src/EntityTree.h | 57 ++- libraries/entities/src/EntityTreeElement.cpp | 256 +++--------- libraries/entities/src/EntityTreeElement.h | 52 ++- libraries/pointers/src/Pick.cpp | 5 +- libraries/pointers/src/Pick.h | 79 +++- libraries/pointers/src/PickCacheOptimizer.h | 6 +- libraries/shared/src/PickFilter.h | 99 ----- .../utilities/tests/entityPerfTest.js | 8 + .../DomainContent/Toybox/pistol/pistol.js | 2 +- 27 files changed, 647 insertions(+), 778 deletions(-) delete mode 100644 libraries/shared/src/PickFilter.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a3a8478b0d..5e41530d93 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4911,7 +4911,7 @@ void Application::calibrateEyeTracker5Points() { #endif bool Application::exportEntities(const QString& filename, - const QVector& entityIDs, + const QVector& entityIDs, const glm::vec3* givenOffset) { QHash entities; @@ -4986,12 +4986,16 @@ bool Application::exportEntities(const QString& filename, float x, float y, floa glm::vec3 minCorner = center - vec3(scale); float cubeSize = scale * 2; AACube boundingCube(minCorner, cubeSize); - QVector entities; + QVector entities; + QVector ids; auto entityTree = getEntities()->getTree(); entityTree->withReadLock([&] { - entityTree->evalEntitiesInCube(boundingCube, PickFilter(), entities); + entityTree->findEntities(boundingCube, entities); + foreach(EntityItemPointer entity, entities) { + ids << entity->getEntityItemID(); + } }); - return exportEntities(filename, entities, ¢er); + return exportEntities(filename, ids, ¢er); } void Application::loadSettings() { diff --git a/interface/src/Application.h b/interface/src/Application.h index a78df6afdd..fd45a594b5 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -351,7 +351,7 @@ signals: public slots: QVector pasteEntities(float x, float y, float z); - bool exportEntities(const QString& filename, const QVector& entityIDs, const glm::vec3* givenOffset = nullptr); + bool exportEntities(const QString& filename, const QVector& entityIDs, const glm::vec3* givenOffset = nullptr); bool exportEntities(const QString& filename, float x, float y, float z, float scale); bool importEntities(const QString& url, const bool isObservable = true, const qint64 callerId = -1); void updateThreadPoolCount() const; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 46c1f3dda1..397817cf60 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3187,15 +3187,17 @@ bool MyAvatar::requiresSafeLanding(const glm::vec3& positionIn, glm::vec3& bette OctreeElementPointer element; float distance; BoxFace face; - const auto lockType = Octree::Lock; // Should we refactor to take a lock just once? - bool* accurateResult = NULL; - + const bool visibleOnly = false; // This isn't quite what we really want here. findRayIntersection always works on mesh, skipping entirely based on collidable. // What we really want is to use the collision hull! // See https://highfidelity.fogbugz.com/f/cases/5003/findRayIntersection-has-option-to-use-collidableOnly-but-doesn-t-actually-use-colliders + const bool collidableOnly = true; + const bool precisionPicking = true; + const auto lockType = Octree::Lock; // Should we refactor to take a lock just once? + bool* accurateResult = NULL; + QVariantMap extraInfo; - EntityItemID entityID = entityTree->evalRayIntersection(startPointIn, directionIn, include, ignore, - PickFilter(PickFilter::getBitMask(PickFilter::FlagBit::COLLIDABLE) | PickFilter::getBitMask(PickFilter::FlagBit::PRECISE)), + EntityItemID entityID = entityTree->findRayIntersection(startPointIn, directionIn, include, ignore, visibleOnly, collidableOnly, precisionPicking, element, distance, face, normalOut, extraInfo, lockType, accurateResult); if (entityID.isNull()) { return false; diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 236512f2fe..1b53f9ab30 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -125,7 +125,7 @@ LaserPointer::RenderState::RenderState(const OverlayID& startID, const OverlayID StartEndRenderState(startID, endID), _pathID(pathID) { if (!_pathID.isNull()) { - _pathIgnoreRays = qApp->getOverlays().getProperty(_pathID, "ignorePickIntersection").value.toBool(); + _pathIgnoreRays = qApp->getOverlays().getProperty(_pathID, "ignoreRayIntersection").value.toBool(); _lineWidth = qApp->getOverlays().getProperty(_pathID, "lineWidth").value.toFloat(); } } @@ -142,7 +142,7 @@ void LaserPointer::RenderState::disable() { if (!getPathID().isNull()) { QVariantMap pathProps; pathProps.insert("visible", false); - pathProps.insert("ignorePickIntersection", true); + pathProps.insert("ignoreRayIntersection", true); qApp->getOverlays().editOverlay(getPathID(), pathProps); } } @@ -156,7 +156,7 @@ void LaserPointer::RenderState::update(const glm::vec3& origin, const glm::vec3& pathProps.insert("start", vec3toVariant(origin)); pathProps.insert("end", endVariant); pathProps.insert("visible", true); - pathProps.insert("ignorePickIntersection", doesPathIgnoreRays()); + pathProps.insert("ignoreRayIntersection", doesPathIgnoreRays()); pathProps.insert("lineWidth", getLineWidth() * parentScale); qApp->getOverlays().editOverlay(getPathID(), pathProps); } diff --git a/interface/src/raypick/LaserPointerScriptingInterface.h b/interface/src/raypick/LaserPointerScriptingInterface.h index d85e329e9a..5aaacd7960 100644 --- a/interface/src/raypick/LaserPointerScriptingInterface.h +++ b/interface/src/raypick/LaserPointerScriptingInterface.h @@ -21,7 +21,7 @@ class LaserPointerScriptingInterface : public QObject, public Dependency { SINGLETON_DEPENDENCY /**jsdoc - * Synonym for {@link Pointers} as used for laser pointers. Deprecated. + * Synonym for {@link Pointers} as used for laser pointers. * * @namespace LaserPointers * diff --git a/interface/src/raypick/ParabolaPick.cpp b/interface/src/raypick/ParabolaPick.cpp index b93ced17c6..571f4a6ea6 100644 --- a/interface/src/raypick/ParabolaPick.cpp +++ b/interface/src/raypick/ParabolaPick.cpp @@ -9,7 +9,6 @@ #include "Application.h" #include "EntityScriptingInterface.h" -#include "PickScriptingInterface.h" #include "ui/overlays/Overlays.h" #include "avatar/AvatarManager.h" #include "scripting/HMDScriptingInterface.h" @@ -58,15 +57,10 @@ PickParabola ParabolaPick::getMathematicalPick() const { PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick) { if (glm::length2(pick.acceleration) > EPSILON && glm::length2(pick.velocity) > EPSILON) { - PickFilter searchFilter = getFilter(); - if (DependencyManager::get()->getForceCoarsePicking()) { - searchFilter.setFlag(PickFilter::COARSE, true); - searchFilter.setFlag(PickFilter::PRECISE, false); - } - + bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); ParabolaToEntityIntersectionResult entityRes = - DependencyManager::get()->evalParabolaIntersectionVector(pick, searchFilter, - getIncludeItemsAs(), getIgnoreItemsAs()); + DependencyManager::get()->findParabolaIntersectionVector(pick, precisionPicking, + getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); if (entityRes.intersects) { return std::make_shared(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.parabolicDistance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo); } @@ -76,7 +70,7 @@ PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick) PickResultPointer ParabolaPick::getOverlayIntersection(const PickParabola& pick) { if (glm::length2(pick.acceleration) > EPSILON && glm::length2(pick.velocity) > EPSILON) { - bool precisionPicking = !(getFilter().isCoarse() || DependencyManager::get()->getForceCoarsePicking()); + bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); ParabolaToOverlayIntersectionResult overlayRes = qApp->getOverlays().findParabolaIntersectionVector(pick, precisionPicking, getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); diff --git a/interface/src/raypick/PathPointer.cpp b/interface/src/raypick/PathPointer.cpp index 8cee6134c8..00ab32bde4 100644 --- a/interface/src/raypick/PathPointer.cpp +++ b/interface/src/raypick/PathPointer.cpp @@ -253,12 +253,12 @@ StartEndRenderState::StartEndRenderState(const OverlayID& startID, const Overlay _startID(startID), _endID(endID) { if (!_startID.isNull()) { _startDim = vec3FromVariant(qApp->getOverlays().getProperty(_startID, "dimensions").value); - _startIgnoreRays = qApp->getOverlays().getProperty(_startID, "ignorePickIntersection").value.toBool(); + _startIgnoreRays = qApp->getOverlays().getProperty(_startID, "ignoreRayIntersection").value.toBool(); } if (!_endID.isNull()) { _endDim = vec3FromVariant(qApp->getOverlays().getProperty(_endID, "dimensions").value); _endRot = quatFromVariant(qApp->getOverlays().getProperty(_endID, "rotation").value); - _endIgnoreRays = qApp->getOverlays().getProperty(_endID, "ignorePickIntersection").value.toBool(); + _endIgnoreRays = qApp->getOverlays().getProperty(_endID, "ignoreRayIntersection").value.toBool(); } } @@ -275,13 +275,13 @@ void StartEndRenderState::disable() { if (!getStartID().isNull()) { QVariantMap startProps; startProps.insert("visible", false); - startProps.insert("ignorePickIntersection", true); + startProps.insert("ignoreRayIntersection", true); qApp->getOverlays().editOverlay(getStartID(), startProps); } if (!getEndID().isNull()) { QVariantMap endProps; endProps.insert("visible", false); - endProps.insert("ignorePickIntersection", true); + endProps.insert("ignoreRayIntersection", true); qApp->getOverlays().editOverlay(getEndID(), endProps); } _enabled = false; @@ -294,7 +294,7 @@ void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end, startProps.insert("position", vec3toVariant(origin)); startProps.insert("visible", true); startProps.insert("dimensions", vec3toVariant(getStartDim() * parentScale)); - startProps.insert("ignorePickIntersection", doesStartIgnoreRays()); + startProps.insert("ignoreRayIntersection", doesStartIgnoreRays()); qApp->getOverlays().editOverlay(getStartID(), startProps); } @@ -346,7 +346,7 @@ void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end, endProps.insert("position", vec3toVariant(position)); endProps.insert("rotation", quatToVariant(rotation)); endProps.insert("visible", true); - endProps.insert("ignorePickIntersection", doesEndIgnoreRays()); + endProps.insert("ignoreRayIntersection", doesEndIgnoreRays()); qApp->getOverlays().editOverlay(getEndID(), endProps); } _enabled = true; diff --git a/interface/src/raypick/PickScriptingInterface.cpp b/interface/src/raypick/PickScriptingInterface.cpp index e8f84e63fe..6e979d2d91 100644 --- a/interface/src/raypick/PickScriptingInterface.cpp +++ b/interface/src/raypick/PickScriptingInterface.cpp @@ -49,17 +49,11 @@ unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type, } } -PickFilter getPickFilter(unsigned int filter) { - // FIXME: Picks always intersect visible and collidable things right now - filter = filter | (PickScriptingInterface::PICK_INCLUDE_VISIBLE() | PickScriptingInterface::PICK_INCLUDE_COLLIDABLE()); - return PickFilter(filter); -} - /**jsdoc * A set of properties that can be passed to {@link Picks.createPick} to create a new Ray Pick. * @typedef {object} Picks.RayPickProperties * @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results. - * @property {number} [filter=0] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. + * @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. * @property {number} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid. * @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, an overlay, or a pick. * @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint) @@ -79,7 +73,7 @@ unsigned int PickScriptingInterface::createRayPick(const QVariant& properties) { PickFilter filter = PickFilter(); if (propMap["filter"].isValid()) { - filter = getPickFilter(propMap["filter"].toUInt()); + filter = PickFilter(propMap["filter"].toUInt()); } float maxDistance = 0.0f; @@ -117,7 +111,7 @@ unsigned int PickScriptingInterface::createRayPick(const QVariant& properties) { * @typedef {object} Picks.StylusPickProperties * @property {number} [hand=-1] An integer. 0 == left, 1 == right. Invalid otherwise. * @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results. - * @property {number} [filter=0] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. + * @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. * @property {number} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid. */ unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties) { @@ -138,7 +132,7 @@ unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties PickFilter filter = PickFilter(); if (propMap["filter"].isValid()) { - filter = getPickFilter(propMap["filter"].toUInt()); + filter = PickFilter(propMap["filter"].toUInt()); } float maxDistance = 0.0f; @@ -159,7 +153,7 @@ unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties * A set of properties that can be passed to {@link Picks.createPick} to create a new Parabola Pick. * @typedef {object} Picks.ParabolaPickProperties * @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results. - * @property {number} [filter=0] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. + * @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. * @property {number} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid. * @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, an overlay, or a pick. * @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint) @@ -184,7 +178,7 @@ unsigned int PickScriptingInterface::createParabolaPick(const QVariant& properti PickFilter filter = PickFilter(); if (propMap["filter"].isValid()) { - filter = getPickFilter(propMap["filter"].toUInt()); + filter = PickFilter(propMap["filter"].toUInt()); } float maxDistance = 0.0f; @@ -256,7 +250,7 @@ unsigned int PickScriptingInterface::createParabolaPick(const QVariant& properti * @typedef {object} Picks.CollisionPickProperties * @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results. -* @property {number} [filter=0] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. +* @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. * @property {Shape} shape - The information about the collision region's size and shape. Dimensions are in world space, but will scale with the parent if defined. * @property {Vec3} position - The position of the collision region, relative to a parent if defined. * @property {Quat} orientation - The orientation of the collision region, relative to a parent if defined. @@ -279,7 +273,7 @@ unsigned int PickScriptingInterface::createCollisionPick(const QVariant& propert PickFilter filter = PickFilter(); if (propMap["filter"].isValid()) { - filter = getPickFilter(propMap["filter"].toUInt()); + filter = PickFilter(propMap["filter"].toUInt()); } float maxDistance = 0.0f; diff --git a/interface/src/raypick/PickScriptingInterface.h b/interface/src/raypick/PickScriptingInterface.h index e795068cd3..94112d5fae 100644 --- a/interface/src/raypick/PickScriptingInterface.h +++ b/interface/src/raypick/PickScriptingInterface.h @@ -14,7 +14,6 @@ #include #include #include -#include /**jsdoc * The Picks API lets you create and manage objects for repeatedly calculating intersections in different ways. @@ -24,62 +23,41 @@ * @hifi-interface * @hifi-client-entity * - * @property {number} PICK_ENTITIES A filter flag. Include domain and avatar entities when intersecting. Read-only.. Deprecated. - * @property {number} PICK_OVERLAYS A filter flag. Include local entities when intersecting. Read-only.. Deprecated. - * - * @property {number} PICK_DOMAIN_ENTITIES A filter flag. Include domain entities when intersecting. Read-only.. - * @property {number} PICK_AVATAR_ENTITIES A filter flag. Include avatar entities when intersecting. Read-only.. - * @property {number} PICK_LOCAL_ENTITIES A filter flag. Include local entities when intersecting. Read-only.. - * @property {number} PICK_AVATARS A filter flag. Include avatars when intersecting. Read-only.. - * @property {number} PICK_HUD A filter flag. Include the HUD sphere when intersecting in HMD mode. Read-only.. - * - * @property {number} PICK_INCLUDE_VISIBLE A filter flag. Include visible objects when intersecting. Read-only.. - * @property {number} PICK_INCLUDE_INVISIBLE A filter flag. Include invisible objects when intersecting. Read-only.. - * - * @property {number} PICK_INCLUDE_COLLIDABLE A filter flag. Include collidable objects when intersecting. Read-only.. - * @property {number} PICK_INCLUDE_NONCOLLIDABLE A filter flag. Include non-collidable objects when intersecting. Read-only.. - * - * @property {number} PICK_PRECISE A filter flag. Pick against exact meshes. Read-only.. - * @property {number} PICK_COARSE A filter flag. Pick against coarse meshes. Read-only.. - * - * @property {number} PICK_ALL_INTERSECTIONS Read-only.. - * - * @property {number} INTERSECTED_NONE An intersection type. Intersected nothing with the given filter flags. Read-only. + * @property {number} PICK_NOTHING A filter flag. Don't intersect with anything. Read-only. + * @property {number} PICK_ENTITIES A filter flag. Include entities when intersecting. Read-only. + * @property {number} PICK_OVERLAYS A filter flag. Include overlays when intersecting. Read-only. + * @property {number} PICK_AVATARS A filter flag. Include avatars when intersecting. Read-only. + * @property {number} PICK_HUD A filter flag. Include the HUD sphere when intersecting in HMD mode. Read-only. + * @property {number} PICK_COARSE A filter flag. Pick against coarse meshes, instead of exact meshes. Read-only. + * @property {number} PICK_INCLUDE_INVISIBLE A filter flag. Include invisible objects when intersecting. Read-only. + * @property {number} PICK_INCLUDE_NONCOLLIDABLE A filter flag. Include non-collidable objects when intersecting. + * Read-only. + * @property {number} PICK_ALL_INTERSECTIONS Read-only. + * @property {number} INTERSECTED_NONE An intersection type. Intersected nothing with the given filter flags. + * Read-only. * @property {number} INTERSECTED_ENTITY An intersection type. Intersected an entity. Read-only. * @property {number} INTERSECTED_OVERLAY An intersection type. Intersected an overlay. Read-only. * @property {number} INTERSECTED_AVATAR An intersection type. Intersected an avatar. Read-only. * @property {number} INTERSECTED_HUD An intersection type. Intersected the HUD sphere. Read-only. - * @property {number} perFrameTimeBudget - The max number of usec to spend per frame updating Pick results. + * @property {number} perFrameTimeBudget - The max number of usec to spend per frame updating Pick results. Read-only. */ class PickScriptingInterface : public QObject, public Dependency { Q_OBJECT + Q_PROPERTY(unsigned int PICK_NOTHING READ PICK_NOTHING CONSTANT) Q_PROPERTY(unsigned int PICK_ENTITIES READ PICK_ENTITIES CONSTANT) Q_PROPERTY(unsigned int PICK_OVERLAYS READ PICK_OVERLAYS CONSTANT) - - Q_PROPERTY(unsigned int PICK_DOMAIN_ENTITIES READ PICK_DOMAIN_ENTITIES CONSTANT) - Q_PROPERTY(unsigned int PICK_AVATAR_ENTITIES READ PICK_AVATAR_ENTITIES CONSTANT) - Q_PROPERTY(unsigned int PICK_LOCAL_ENTITIES READ PICK_LOCAL_ENTITIES CONSTANT) Q_PROPERTY(unsigned int PICK_AVATARS READ PICK_AVATARS CONSTANT) Q_PROPERTY(unsigned int PICK_HUD READ PICK_HUD CONSTANT) - - Q_PROPERTY(unsigned int PICK_INCLUDE_VISIBLE READ PICK_INCLUDE_VISIBLE CONSTANT) - Q_PROPERTY(unsigned int PICK_INCLUDE_INVISIBLE READ PICK_INCLUDE_INVISIBLE CONSTANT) - - Q_PROPERTY(unsigned int PICK_INCLUDE_COLLIDABLE READ PICK_INCLUDE_COLLIDABLE CONSTANT) - Q_PROPERTY(unsigned int PICK_INCLUDE_NONCOLLIDABLE READ PICK_INCLUDE_NONCOLLIDABLE CONSTANT) - - Q_PROPERTY(unsigned int PICK_PRECISE READ PICK_PRECISE CONSTANT) Q_PROPERTY(unsigned int PICK_COARSE READ PICK_COARSE CONSTANT) - + Q_PROPERTY(unsigned int PICK_INCLUDE_INVISIBLE READ PICK_INCLUDE_INVISIBLE CONSTANT) + Q_PROPERTY(unsigned int PICK_INCLUDE_NONCOLLIDABLE READ PICK_INCLUDE_NONCOLLIDABLE CONSTANT) Q_PROPERTY(unsigned int PICK_ALL_INTERSECTIONS READ PICK_ALL_INTERSECTIONS CONSTANT) - Q_PROPERTY(unsigned int INTERSECTED_NONE READ INTERSECTED_NONE CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_ENTITY READ INTERSECTED_ENTITY CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_OVERLAY READ INTERSECTED_OVERLAY CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_AVATAR READ INTERSECTED_AVATAR CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_HUD READ INTERSECTED_HUD CONSTANT) - Q_PROPERTY(unsigned int perFrameTimeBudget READ getPerFrameTimeBudget WRITE setPerFrameTimeBudget) SINGLETON_DEPENDENCY public: @@ -94,13 +72,11 @@ public: * Adds a new Pick. * Different {@link PickType}s use different properties, and within one PickType, the properties you choose can lead to a wide range of behaviors. For example, * with PickType.Ray, depending on which optional parameters you pass, you could create a Static Ray Pick, a Mouse Ray Pick, or a Joint Ray Pick. - * Picks created with this method always intersect at least visible and collidable things * @function Picks.createPick * @param {PickType} type A PickType that specifies the method of picking to use * @param {Picks.RayPickProperties|Picks.StylusPickProperties|Picks.ParabolaPickProperties|Picks.CollisionPickProperties} properties A PickProperties object, containing all the properties for initializing this Pick * @returns {number} The ID of the created Pick. Used for managing the Pick. 0 if invalid. */ - // TODO: expand Pointers to be able to be fully configurable with PickFilters Q_INVOKABLE unsigned int createPick(const PickQuery::PickType type, const QVariant& properties); /**jsdoc @@ -251,80 +227,61 @@ public: */ Q_INVOKABLE bool isMouse(unsigned int uid); + // FIXME: Move to other property definitions. + Q_PROPERTY(unsigned int perFrameTimeBudget READ getPerFrameTimeBudget WRITE setPerFrameTimeBudget) + unsigned int getPerFrameTimeBudget() const; void setPerFrameTimeBudget(unsigned int numUsecs); public slots: + /**jsdoc + * @function Picks.PICK_NOTHING + * @returns {number} + */ + static constexpr unsigned int PICK_NOTHING() { return 0; } + /**jsdoc * @function Picks.PICK_ENTITIES * @returns {number} */ - static constexpr unsigned int PICK_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); } + static constexpr unsigned int PICK_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_ENTITIES); } + /**jsdoc * @function Picks.PICK_OVERLAYS * @returns {number} */ - static constexpr unsigned int PICK_OVERLAYS() { return PickFilter::getBitMask(PickFilter::FlagBit::LOCAL_ENTITIES); } + static constexpr unsigned int PICK_OVERLAYS() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_OVERLAYS); } - /**jsdoc - * @function Picks.PICK_DOMAIN_ENTITIES - * @returns {number} - */ - static constexpr unsigned int PICK_DOMAIN_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES); } - /**jsdoc - * @function Picks.PICK_AVATAR_ENTITIES - * @returns {number} - */ - static constexpr unsigned int PICK_AVATAR_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); } - /**jsdoc - * @function Picks.PICK_LOCAL_ENTITIES - * @returns {number} - */ - static constexpr unsigned int PICK_LOCAL_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::LOCAL_ENTITIES); } /**jsdoc * @function Picks.PICK_AVATARS * @returns {number} */ - static constexpr unsigned int PICK_AVATARS() { return PickFilter::getBitMask(PickFilter::FlagBit::AVATARS); } + static constexpr unsigned int PICK_AVATARS() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_AVATARS); } + /**jsdoc * @function Picks.PICK_HUD * @returns {number} */ - static constexpr unsigned int PICK_HUD() { return PickFilter::getBitMask(PickFilter::FlagBit::HUD); } + static constexpr unsigned int PICK_HUD() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_HUD); } - /**jsdoc - * @function Picks.PICK_INCLUDE_VISIBLE - * @returns {number} - */ - static constexpr unsigned int PICK_INCLUDE_VISIBLE() { return PickFilter::getBitMask(PickFilter::FlagBit::VISIBLE); } - /**jsdoc - * @function Picks.PICK_INCLUDE_INVISIBLE - * @returns {number} - */ - static constexpr unsigned int PICK_INCLUDE_INVISIBLE() { return PickFilter::getBitMask(PickFilter::FlagBit::INVISIBLE); } - - /**jsdoc - * @function Picks.PICK_INCLUDE_COLLIDABLE - * @returns {number} - */ - static constexpr unsigned int PICK_INCLUDE_COLLIDABLE() { return PickFilter::getBitMask(PickFilter::FlagBit::COLLIDABLE); } - /**jsdoc - * @function Picks.PICK_INCLUDE_NONCOLLIDABLE - * @returns {number} - */ - static constexpr unsigned int PICK_INCLUDE_NONCOLLIDABLE() { return PickFilter::getBitMask(PickFilter::FlagBit::NONCOLLIDABLE); } - - /**jsdoc - * @function Picks.PICK_PRECISE - * @returns {number} - */ - static constexpr unsigned int PICK_PRECISE() { return PickFilter::getBitMask(PickFilter::FlagBit::PRECISE); } /**jsdoc * @function Picks.PICK_COARSE * @returns {number} */ - static constexpr unsigned int PICK_COARSE() { return PickFilter::getBitMask(PickFilter::FlagBit::COARSE); } + static constexpr unsigned int PICK_COARSE() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_COARSE); } + + /**jsdoc + * @function Picks.PICK_INCLUDE_INVISIBLE + * @returns {number} + */ + static constexpr unsigned int PICK_INCLUDE_INVISIBLE() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_INCLUDE_INVISIBLE); } + + /**jsdoc + * @function Picks.PICK_INCLUDE_NONCOLLIDABLE + * @returns {number} + */ + static constexpr unsigned int PICK_INCLUDE_NONCOLLIDABLE() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_INCLUDE_NONCOLLIDABLE); } /**jsdoc * @function Picks.PICK_ALL_INTERSECTIONS diff --git a/interface/src/raypick/PointerScriptingInterface.h b/interface/src/raypick/PointerScriptingInterface.h index a21c1f2470..2677f37fae 100644 --- a/interface/src/raypick/PointerScriptingInterface.h +++ b/interface/src/raypick/PointerScriptingInterface.h @@ -41,12 +41,10 @@ public: * @property {string} button Which button to trigger. "Primary", "Secondary", "Tertiary", and "Focus" are currently supported. Only "Primary" will trigger clicks on web surfaces. If "Focus" is triggered, * it will try to set the entity or overlay focus to the object at which the Pointer is aimed. Buttons besides the first three will still trigger events, but event.button will be "None". */ - /**jsdoc * Adds a new Pointer * Different {@link PickType}s use different properties, and within one PickType, the properties you choose can lead to a wide range of behaviors. For example, * with PickType.Ray, depending on which optional parameters you pass, you could create a Static Ray Pointer, a Mouse Ray Pointer, or a Joint Ray Pointer. - * Pointers created with this method always intersect at least visible and collidable things * @function Pointers.createPointer * @param {PickType} type A PickType that specifies the method of picking to use * @param {Pointers.LaserPointerProperties|Pointers.StylusPointerProperties|Pointers.ParabolaPointerProperties} properties A PointerProperties object, containing all the properties for initializing this Pointer and the {@link Picks.PickProperties} for the Pick that @@ -60,21 +58,21 @@ public: * dimensions: {x:0.5, y:0.5, z:0.5}, * solid: true, * color: {red:0, green:255, blue:0}, - * ignorePickIntersection: true + * ignoreRayIntersection: true * }; * var end2 = { * type: "sphere", * dimensions: {x:0.5, y:0.5, z:0.5}, * solid: true, * color: {red:255, green:0, blue:0}, - * ignorePickIntersection: true + * ignoreRayIntersection: true * }; * * var renderStates = [ {name: "test", end: end} ]; * var defaultRenderStates = [ {name: "test", distance: 10.0, end: end2} ]; * var pointer = Pointers.createPointer(PickType.Ray, { * joint: "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", - * filter: Picks.PICK_LOCAL_ENTITIES | Picks.PICK_DOMAIN_ENTITIES | Picks.PICK_INCLUDE_NONCOLLIDABLE, + * filter: Picks.PICK_OVERLAYS | Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_NONCOLLIDABLE, * renderStates: renderStates, * defaultRenderStates: defaultRenderStates, * distanceScaleEnd: true, @@ -84,7 +82,6 @@ public: * }); * Pointers.setRenderState(pointer, "test"); */ - // TODO: expand Pointers to be able to be fully configurable with PickFilters Q_INVOKABLE unsigned int createPointer(const PickQuery::PickType& type, const QVariant& properties); /**jsdoc diff --git a/interface/src/raypick/RayPick.cpp b/interface/src/raypick/RayPick.cpp index 507e45b470..a48d858504 100644 --- a/interface/src/raypick/RayPick.cpp +++ b/interface/src/raypick/RayPick.cpp @@ -27,15 +27,10 @@ PickRay RayPick::getMathematicalPick() const { } PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) { - PickFilter searchFilter = getFilter(); - if (DependencyManager::get()->getForceCoarsePicking()) { - searchFilter.setFlag(PickFilter::COARSE, true); - searchFilter.setFlag(PickFilter::PRECISE, false); - } - + bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); RayToEntityIntersectionResult entityRes = - DependencyManager::get()->evalRayIntersectionVector(pick, searchFilter, - getIncludeItemsAs(), getIgnoreItemsAs()); + DependencyManager::get()->findRayIntersectionVector(pick, precisionPicking, + getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); if (entityRes.intersects) { return std::make_shared(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo); } else { @@ -44,7 +39,7 @@ PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) { } PickResultPointer RayPick::getOverlayIntersection(const PickRay& pick) { - bool precisionPicking = !(getFilter().isCoarse() || DependencyManager::get()->getForceCoarsePicking()); + bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); RayToOverlayIntersectionResult overlayRes = qApp->getOverlays().findRayIntersectionVector(pick, precisionPicking, getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); diff --git a/interface/src/raypick/RayPickScriptingInterface.h b/interface/src/raypick/RayPickScriptingInterface.h index 3ad0efd439..d5e224018e 100644 --- a/interface/src/raypick/RayPickScriptingInterface.h +++ b/interface/src/raypick/RayPickScriptingInterface.h @@ -19,13 +19,14 @@ #include "PickScriptingInterface.h" /**jsdoc - * Synonym for {@link Picks} as used for ray picks. Deprecated. + * Synonym for {@link Picks} as used for ray picks. * * @namespace RayPick * * @hifi-interface * @hifi-client-entity * + * @property {number} PICK_NOTHING Read-only. * @property {number} PICK_ENTITIES Read-only. * @property {number} PICK_OVERLAYS Read-only. * @property {number} PICK_AVATARS Read-only. @@ -43,6 +44,7 @@ class RayPickScriptingInterface : public QObject, public Dependency { Q_OBJECT + Q_PROPERTY(unsigned int PICK_NOTHING READ PICK_NOTHING CONSTANT) Q_PROPERTY(unsigned int PICK_ENTITIES READ PICK_ENTITIES CONSTANT) Q_PROPERTY(unsigned int PICK_OVERLAYS READ PICK_OVERLAYS CONSTANT) Q_PROPERTY(unsigned int PICK_AVATARS READ PICK_AVATARS CONSTANT) @@ -138,6 +140,12 @@ public: public slots: + /**jsdoc + * @function RayPick.PICK_NOTHING + * @returns {number} + */ + static unsigned int PICK_NOTHING() { return PickScriptingInterface::PICK_NOTHING(); } + /**jsdoc * @function RayPick.PICK_ENTITIES * @returns {number} diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index 867f896763..5595c54b71 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -61,7 +61,7 @@ OverlayID StylusPointer::buildStylusOverlay(const QVariantMap& properties) { overlayProperties["loadPriority"] = 10.0f; overlayProperties["solid"] = true; overlayProperties["visible"] = false; - overlayProperties["ignorePickIntersection"] = true; + overlayProperties["ignoreRayIntersection"] = true; overlayProperties["drawInFront"] = false; return qApp->getOverlays().addOverlay("model", overlayProperties); diff --git a/interface/src/ui/Keyboard.cpp b/interface/src/ui/Keyboard.cpp index 9e9a319802..9e3a3ce961 100644 --- a/interface/src/ui/Keyboard.cpp +++ b/interface/src/ui/Keyboard.cpp @@ -781,7 +781,7 @@ void Keyboard::loadKeyboardFile(const QString& keyboardFile) { { "isSolid", true }, { "visible", false }, { "grabbable", true }, - { "ignorePickIntersection", false }, + { "ignoreRayIntersection", false }, { "dimensions", anchorObject["dimensions"].toVariant() }, { "position", anchorObject["position"].toVariant() }, { "orientation", anchorObject["rotation"].toVariant() } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index a88a8de308..4fe77c90ad 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -498,27 +498,20 @@ void EntityTreeRenderer::handleSpaceUpdate(std::pair proxyUp bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QVector* entitiesContainingAvatar) { bool didUpdate = false; float radius = 0.01f; // for now, assume 0.01 meter radius, because we actually check the point inside later - QVector entityIDs; + QVector foundEntities; // find the entities near us // don't let someone else change our tree while we search _tree->withReadLock([&] { - auto entityTree = std::static_pointer_cast(_tree); // FIXME - if EntityTree had a findEntitiesContainingPoint() this could theoretically be a little faster - entityTree->evalEntitiesInSphere(_avatarPosition, radius, - PickFilter(PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES)), entityIDs); + std::static_pointer_cast(_tree)->findEntities(_avatarPosition, radius, foundEntities); LayeredZones oldLayeredZones(std::move(_layeredZones)); _layeredZones.clear(); // create a list of entities that actually contain the avatar's position - for (auto& entityID : entityIDs) { - auto entity = entityTree->findEntityByID(entityID); - if (!entity) { - continue; - } - + for (auto& entity : foundEntities) { auto isZone = entity->getType() == EntityTypes::Zone; auto hasScript = !entity->getScript().isEmpty(); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 303e6d54c4..33ec92b8de 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1085,10 +1085,13 @@ QUuid EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float EntityItemID result; if (_entityTree) { - unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); + EntityItemPointer closestEntity; _entityTree->withReadLock([&] { - result = _entityTree->evalClosestEntity(center, radius, PickFilter(searchFilter)); + closestEntity = _entityTree->findClosestEntity(center, radius); }); + if (closestEntity) { + result = closestEntity->getEntityItemID(); + } } return result; } @@ -1107,10 +1110,14 @@ QVector EntityScriptingInterface::findEntities(const glm::vec3& center, f QVector result; if (_entityTree) { - unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); + QVector entities; _entityTree->withReadLock([&] { - _entityTree->evalEntitiesInSphere(center, radius, PickFilter(searchFilter), result); + _entityTree->findEntities(center, radius, entities); }); + + foreach (EntityItemPointer entity, entities) { + result << entity->getEntityItemID(); + } } return result; } @@ -1120,11 +1127,15 @@ QVector EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corn QVector result; if (_entityTree) { - unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); + QVector entities; _entityTree->withReadLock([&] { AABox box(corner, dimensions); - _entityTree->evalEntitiesInBox(box, PickFilter(searchFilter), result); + _entityTree->findEntities(box, entities); }); + + foreach (EntityItemPointer entity, entities) { + result << entity->getEntityItemID(); + } } return result; } @@ -1159,10 +1170,14 @@ QVector EntityScriptingInterface::findEntitiesInFrustum(QVariantMap frust viewFrustum.calculate(); if (_entityTree) { - unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); + QVector entities; _entityTree->withReadLock([&] { - _entityTree->evalEntitiesInFrustum(viewFrustum, PickFilter(searchFilter), result); + _entityTree->findEntities(viewFrustum, entities); }); + + foreach(EntityItemPointer entity, entities) { + result << entity->getEntityItemID(); + } } } @@ -1174,64 +1189,86 @@ QVector EntityScriptingInterface::findEntitiesByType(const QString entity QVector result; if (_entityTree) { - unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); + QVector entities; _entityTree->withReadLock([&] { - _entityTree->evalEntitiesInSphereWithType(center, radius, type, PickFilter(searchFilter), result); + _entityTree->findEntities(center, radius, entities); }); + + foreach(EntityItemPointer entity, entities) { + if (entity->getType() == type) { + result << entity->getEntityItemID().toString(); + } + } } return result; } QVector EntityScriptingInterface::findEntitiesByName(const QString entityName, const glm::vec3& center, float radius, bool caseSensitiveSearch) const { + QVector result; if (_entityTree) { + QVector entities; _entityTree->withReadLock([&] { - unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); - _entityTree->evalEntitiesInSphereWithName(center, radius, entityName, caseSensitiveSearch, PickFilter(searchFilter), result); + _entityTree->findEntities(center, radius, entities); }); + + if (caseSensitiveSearch) { + foreach(EntityItemPointer entity, entities) { + if (entity->getName() == entityName) { + result << entity->getEntityItemID(); + } + } + + } else { + QString entityNameLowerCase = entityName.toLower(); + + foreach(EntityItemPointer entity, entities) { + QString entityItemLowerCase = entity->getName().toLower(); + if (entityItemLowerCase == entityNameLowerCase) { + result << entity->getEntityItemID(); + } + } + } } return result; } -RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, - const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) const { - PROFILE_RANGE(script_entities, __FUNCTION__); +RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, + const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) { QVector entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude); QVector entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard); - unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); - - if (!precisionPicking) { - searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::COARSE); - } - - if (visibleOnly) { - searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::VISIBLE); - } - - if (collidableOnly) { - searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::COLLIDABLE); - } - - return evalRayIntersectionWorker(ray, Octree::Lock, PickFilter(searchFilter), entitiesToInclude, entitiesToDiscard); + return findRayIntersectionVector(ray, precisionPicking, entitiesToInclude, entitiesToDiscard, visibleOnly, collidableOnly); } -RayToEntityIntersectionResult EntityScriptingInterface::evalRayIntersectionVector(const PickRay& ray, PickFilter searchFilter, - const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard) { +RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionVector(const PickRay& ray, bool precisionPicking, + const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) { PROFILE_RANGE(script_entities, __FUNCTION__); - return evalRayIntersectionWorker(ray, Octree::Lock, searchFilter, entityIdsToInclude, entityIdsToDiscard); + return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly); } -RayToEntityIntersectionResult EntityScriptingInterface::evalRayIntersectionWorker(const PickRay& ray, - Octree::lockType lockType, PickFilter searchFilter, const QVector& entityIdsToInclude, - const QVector& entityIdsToDiscard) const { +// FIXME - we should remove this API and encourage all users to use findRayIntersection() instead. We've changed +// findRayIntersection() to be blocking because it never makes sense for a script to get back a non-answer +RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking, + const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard) { + + qWarning() << "Entities.findRayIntersectionBlocking() is obsolete, use Entities.findRayIntersection() instead."; + const QVector& entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude); + const QVector entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard); + return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entitiesToInclude, entitiesToDiscard); +} + +RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorker(const PickRay& ray, + Octree::lockType lockType, bool precisionPicking, const QVector& entityIdsToInclude, + const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) { + RayToEntityIntersectionResult result; if (_entityTree) { OctreeElementPointer element; - result.entityID = _entityTree->evalRayIntersection(ray.origin, ray.direction, - entityIdsToInclude, entityIdsToDiscard, searchFilter, + result.entityID = _entityTree->findRayIntersection(ray.origin, ray.direction, + entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly, precisionPicking, element, result.distance, result.face, result.surfaceNormal, result.extraInfo, lockType, &result.accurate); result.intersects = !result.entityID.isNull(); @@ -1242,22 +1279,23 @@ RayToEntityIntersectionResult EntityScriptingInterface::evalRayIntersectionWorke return result; } -ParabolaToEntityIntersectionResult EntityScriptingInterface::evalParabolaIntersectionVector(const PickParabola& parabola, PickFilter searchFilter, - const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard) { +ParabolaToEntityIntersectionResult EntityScriptingInterface::findParabolaIntersectionVector(const PickParabola& parabola, bool precisionPicking, + const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) { PROFILE_RANGE(script_entities, __FUNCTION__); - return evalParabolaIntersectionWorker(parabola, Octree::Lock, searchFilter, entityIdsToInclude, entityIdsToDiscard); + return findParabolaIntersectionWorker(parabola, Octree::Lock, precisionPicking, entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly); } -ParabolaToEntityIntersectionResult EntityScriptingInterface::evalParabolaIntersectionWorker(const PickParabola& parabola, - Octree::lockType lockType, PickFilter searchFilter, const QVector& entityIdsToInclude, - const QVector& entityIdsToDiscard) const { +ParabolaToEntityIntersectionResult EntityScriptingInterface::findParabolaIntersectionWorker(const PickParabola& parabola, + Octree::lockType lockType, bool precisionPicking, const QVector& entityIdsToInclude, + const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) { + ParabolaToEntityIntersectionResult result; if (_entityTree) { OctreeElementPointer element; - result.entityID = _entityTree->evalParabolaIntersection(parabola, - entityIdsToInclude, entityIdsToDiscard, searchFilter, + result.entityID = _entityTree->findParabolaIntersection(parabola, + entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly, precisionPicking, element, result.intersection, result.distance, result.parabolicDistance, result.face, result.surfaceNormal, result.extraInfo, lockType, &result.accurate); result.intersects = !result.entityID.isNull(); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 1398c2ad1c..890c666010 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -24,7 +24,6 @@ #include #include #include -#include #include "PolyVoxEntityItem.h" #include "LineEntityItem.h" @@ -57,7 +56,8 @@ private: }; /**jsdoc - * The result of a {@link PickRay} search using {@link Entities.findRayIntersection|findRayIntersection}. + * The result of a {@link PickRay} search using {@link Entities.findRayIntersection|findRayIntersection} or + * {@link Entities.findRayIntersectionBlocking|findRayIntersectionBlocking}. * @typedef {object} Entities.RayToEntityIntersectionResult * @property {boolean} intersects - true if the {@link PickRay} intersected an entity, otherwise * false. @@ -119,6 +119,7 @@ public: /// handles scripting of Entity commands from JS passed to assigned clients class EntityScriptingInterface : public OctreeScriptingInterface, public Dependency { Q_OBJECT + Q_PROPERTY(QUuid keyboardFocusEntity READ getKeyboardFocusEntity WRITE setKeyboardFocusEntity) friend EntityPropertyMetadataRequest; @@ -143,10 +144,10 @@ public: void resetActivityTracking(); ActivityTracking getActivityTracking() const { return _activityTracking; } - RayToEntityIntersectionResult evalRayIntersectionVector(const PickRay& ray, PickFilter searchFilter, - const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard); - ParabolaToEntityIntersectionResult evalParabolaIntersectionVector(const PickParabola& parabola, PickFilter searchFilter, - const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard); + // TODO: expose to script? + ParabolaToEntityIntersectionResult findParabolaIntersectionVector(const PickParabola& parabola, bool precisionPicking, + const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, + bool visibleOnly, bool collidableOnly); /**jsdoc * Get the properties of multiple entities. @@ -393,8 +394,9 @@ public slots: Q_INVOKABLE void callEntityClientMethod(QUuid clientSessionID, QUuid entityID, const QString& method, const QStringList& params = QStringList()); + /**jsdoc - * Find the non-local entity with a position closest to a specified point and within a specified radius. + * Find the entity with a position closest to a specified point and within a specified radius. * @function Entities.findClosestEntity * @param {Vec3} center - The point about which to search. * @param {number} radius - The radius within which to search. @@ -408,7 +410,7 @@ public slots: Q_INVOKABLE QUuid findClosestEntity(const glm::vec3& center, float radius) const; /**jsdoc - * Find all non-local entities that intersect a sphere defined by a center point and radius. + * Find all entities that intersect a sphere defined by a center point and radius. * @function Entities.findEntities * @param {Vec3} center - The point about which to search. * @param {number} radius - The radius within which to search. @@ -422,23 +424,23 @@ public slots: Q_INVOKABLE QVector findEntities(const glm::vec3& center, float radius) const; /**jsdoc - * Find all non-local entities whose axis-aligned boxes intersect a search axis-aligned box defined by its minimum coordinates corner + * Find all entities whose axis-aligned boxes intersect a search axis-aligned box defined by its minimum coordinates corner * and dimensions. * @function Entities.findEntitiesInBox * @param {Vec3} corner - The corner of the search AA box with minimum co-ordinate values. * @param {Vec3} dimensions - The dimensions of the search AA box. - * @returns {Uuid[]} An array of entity IDs whose AA boxes intersect the search AA box. The array is empty if no entities + * @returns {Uuid[]} An array of entity IDs whose AA boxes intersect the search AA box. The array is empty if no entities * could be found. */ /// this function will not find any models in script engine contexts which don't have access to models Q_INVOKABLE QVector findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const; /**jsdoc - * Find all non-local entities whose axis-aligned boxes intersect a search frustum. + * Find all entities whose axis-aligned boxes intersect a search frustum. * @function Entities.findEntitiesInFrustum * @param {ViewFrustum} frustum - The frustum to search in. The position, orientation, * projection, and centerRadius properties must be specified. - * @returns {Uuid[]} An array of entity IDs axis-aligned boxes intersect the frustum. The array is empty if no entities + * @returns {Uuid[]} An array of entity IDs axis-aligned boxes intersect the frustum. The array is empty if no entities * could be found. * @example Report the number of entities in view. * var entityIDs = Entities.findEntitiesInFrustum(Camera.frustum); @@ -448,12 +450,12 @@ public slots: Q_INVOKABLE QVector findEntitiesInFrustum(QVariantMap frustum) const; /**jsdoc - * Find all non-local entities of a particular type that intersect a sphere defined by a center point and radius. + * Find all entities of a particular type that intersect a sphere defined by a center point and radius. * @function Entities.findEntitiesByType * @param {Entities.EntityType} entityType - The type of entity to search for. * @param {Vec3} center - The point about which to search. * @param {number} radius - The radius within which to search. - * @returns {Uuid[]} An array of entity IDs of the specified type that intersect the search sphere. The array is empty if + * @returns {Uuid[]} An array of entity IDs of the specified type that intersect the search sphere. The array is empty if * no entities could be found. * @example Report the number of Model entities within 10m of your avatar. * var entityIDs = Entities.findEntitiesByType("Model", MyAvatar.position, 10); @@ -463,7 +465,7 @@ public slots: Q_INVOKABLE QVector findEntitiesByType(const QString entityType, const glm::vec3& center, float radius) const; /**jsdoc - * Find all non-local entities with a particular name that intersect a sphere defined by a center point and radius. + * Find all entities of a particular name that intersect a sphere defined by a center point and radius. * @function Entities.findEntitiesByName * @param {string} entityName - The name of the entity to search for. * @param {Vec3} center - The point about which to search. @@ -473,13 +475,13 @@ public slots: * if no entities could be found. * @example Report the number of entities with the name, "Light-Target". * var entityIDs = Entities.findEntitiesByName("Light-Target", MyAvatar.position, 10, false); - * print("Number of entities with the name Light-Target: " + entityIDs.length); + * print("Number of entities with the name "Light-Target": " + entityIDs.length); */ - Q_INVOKABLE QVector findEntitiesByName(const QString entityName, const glm::vec3& center, float radius, - bool caseSensitiveSearch = false) const; + Q_INVOKABLE QVector findEntitiesByName(const QString entityName, const glm::vec3& center, float radius, + bool caseSensitiveSearch = false ) const; /**jsdoc - * Find the first non-local entity intersected by a {@link PickRay}. Light and Zone entities are not + * Find the first entity intersected by a {@link PickRay}. Light and Zone entities are not * intersected unless they've been configured as pickable using {@link Entities.setLightsArePickable|setLightsArePickable} * and {@link Entities.setZonesArePickable|setZonesArePickable}, respectively.
* @function Entities.findRayIntersection @@ -510,8 +512,33 @@ public slots: /// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate /// will be false. Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false, - const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue(), - bool visibleOnly = false, bool collidableOnly = false) const; + const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue(), + bool visibleOnly = false, bool collidableOnly = false); + + /// Same as above but with QVectors + RayToEntityIntersectionResult findRayIntersectionVector(const PickRay& ray, bool precisionPicking, + const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, + bool visibleOnly, bool collidableOnly); + + /**jsdoc + * Find the first entity intersected by a {@link PickRay}. Light and Zone entities are not + * intersected unless they've been configured as pickable using {@link Entities.setLightsArePickable|setLightsArePickable} + * and {@link Entities.setZonesArePickable|setZonesArePickable}, respectively.
+ * This is a synonym for {@link Entities.findRayIntersection|findRayIntersection}. + * @function Entities.findRayIntersectionBlocking + * @param {PickRay} pickRay - The PickRay to use for finding entities. + * @param {boolean} [precisionPicking=false] - If true and the intersected entity is a Model + * entity, the result's extraInfo property includes more information than it otherwise would. + * @param {Uuid[]} [entitiesToInclude=[]] - If not empty then the search is restricted to these entities. + * @param {Uuid[]} [entitiesToDiscard=[]] - Entities to ignore during the search. + * @deprecated This function is deprecated and will soon be removed. Use + * {@link Entities.findRayIntersection|findRayIntersection} instead; it blocks and performs the same function. + */ + /// If the scripting context has visible entities, this will determine a ray intersection, and will block in + /// order to return an accurate result + Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, + const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue()); + /**jsdoc * Reloads an entity's server entity script such that the latest version re-downloaded. @@ -576,7 +603,9 @@ public slots: /**jsdoc * Set whether or not ray picks intersect the bounding box of {@link Entities.EntityType|Light} entities. By default, Light * entities are not intersected. The setting lasts for the Interface session. Ray picks are done using - * {@link Entities.findRayIntersection|findRayIntersection}, or the {@link Picks} API. + * {@link Entities.findRayIntersection|findRayIntersection} or + * {@link Entities.findRayIntersectionBlocking|findRayIntersectionBlocking}, or the {@link Picks} and {@link RayPick} + * APIs. * @function Entities.setLightsArePickable * @param {boolean} value - Set true to make ray picks intersect the bounding box of * {@link Entities.EntityType|Light} entities, otherwise false. @@ -586,7 +615,9 @@ public slots: /**jsdoc * Get whether or not ray picks intersect the bounding box of {@link Entities.EntityType|Light} entities. Ray picks are - * done using {@link Entities.findRayIntersection|findRayIntersection}, or the {@link Picks} API. + * done using {@link Entities.findRayIntersection|findRayIntersection} or + * {@link Entities.findRayIntersectionBlocking|findRayIntersectionBlocking}, or the {@link Picks} and {@link RayPick} + * APIs. * @function Entities.getLightsArePickable * @returns {boolean} true if ray picks intersect the bounding box of {@link Entities.EntityType|Light} * entities, otherwise false. @@ -597,7 +628,9 @@ public slots: /**jsdoc * Set whether or not ray picks intersect the bounding box of {@link Entities.EntityType|Zone} entities. By default, Light * entities are not intersected. The setting lasts for the Interface session. Ray picks are done using - * {@link Entities.findRayIntersection|findRayIntersection}, or the {@link Picks} API. + * {@link Entities.findRayIntersection|findRayIntersection} or + * {@link Entities.findRayIntersectionBlocking|findRayIntersectionBlocking}, or the {@link Picks} and {@link RayPick} + * APIs. * @function Entities.setZonesArePickable * @param {boolean} value - Set true to make ray picks intersect the bounding box of * {@link Entities.EntityType|Zone} entities, otherwise false. @@ -607,7 +640,9 @@ public slots: /**jsdoc * Get whether or not ray picks intersect the bounding box of {@link Entities.EntityType|Zone} entities. Ray picks are - * done using {@link Entities.findRayIntersection|findRayIntersection}, or the {@link Picks} API. + * done using {@link Entities.findRayIntersection|findRayIntersection} or + * {@link Entities.findRayIntersectionBlocking|findRayIntersectionBlocking}, or the {@link Picks} and {@link RayPick} + * APIs. * @function Entities.getZonesArePickable * @returns {boolean} true if ray picks intersect the bounding box of {@link Entities.EntityType|Zone} * entities, otherwise false. @@ -1952,12 +1987,14 @@ private: /// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode - RayToEntityIntersectionResult evalRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, - PickFilter searchFilter, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard) const; + RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, + bool precisionPicking, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, + bool visibleOnly = false, bool collidableOnly = false); /// actually does the work of finding the parabola intersection, can be called in locking mode or tryLock mode - ParabolaToEntityIntersectionResult evalParabolaIntersectionWorker(const PickParabola& parabola, Octree::lockType lockType, - PickFilter searchFilter, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard) const; + ParabolaToEntityIntersectionResult findParabolaIntersectionWorker(const PickParabola& parabola, Octree::lockType lockType, + bool precisionPicking, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, + bool visibleOnly = false, bool collidableOnly = false); EntityTreePointer _entityTree; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index b23fbca6d1..dd020da5a0 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -43,6 +43,50 @@ static const quint64 DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER = USECS_PER_MSEC * 50; const float EntityTree::DEFAULT_MAX_TMP_ENTITY_LIFETIME = 60 * 60; // 1 hour +// combines the ray cast arguments into a single object +class RayArgs { +public: + // Inputs + glm::vec3 origin; + glm::vec3 direction; + glm::vec3 invDirection; + const QVector& entityIdsToInclude; + const QVector& entityIdsToDiscard; + bool visibleOnly; + bool collidableOnly; + bool precisionPicking; + + // Outputs + OctreeElementPointer& element; + float& distance; + BoxFace& face; + glm::vec3& surfaceNormal; + QVariantMap& extraInfo; + EntityItemID entityID; +}; + +class ParabolaArgs { +public: + // Inputs + glm::vec3 origin; + glm::vec3 velocity; + glm::vec3 acceleration; + const QVector& entityIdsToInclude; + const QVector& entityIdsToDiscard; + bool visibleOnly; + bool collidableOnly; + bool precisionPicking; + + // Outputs + OctreeElementPointer& element; + float& parabolicDistance; + BoxFace& face; + glm::vec3& surfaceNormal; + QVariantMap& extraInfo; + EntityItemID entityID; +}; + + EntityTree::EntityTree(bool shouldReaverage) : Octree(shouldReaverage) { @@ -734,32 +778,59 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) } } -class RayArgs { -public: - // Inputs - glm::vec3 origin; - glm::vec3 direction; - glm::vec3 invDirection; - const QVector& entityIdsToInclude; - const QVector& entityIdsToDiscard; - PickFilter searchFilter; - // Outputs - OctreeElementPointer& element; - float& distance; - BoxFace& face; - glm::vec3& surfaceNormal; - QVariantMap& extraInfo; - EntityItemID entityID; +class FindNearPointArgs { +public: + glm::vec3 position; + float targetRadius; + bool found; + EntityItemPointer closestEntity; + float closestEntityDistance; }; -bool evalRayIntersectionOp(const OctreeElementPointer& element, void* extraData) { + +bool EntityTree::findNearPointOperation(const OctreeElementPointer& element, void* extraData) { + FindNearPointArgs* args = static_cast(extraData); + EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); + + glm::vec3 penetration; + bool sphereIntersection = entityTreeElement->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration); + + // If this entityTreeElement contains the point, then search it... + if (sphereIntersection) { + EntityItemPointer thisClosestEntity = entityTreeElement->getClosestEntity(args->position); + + // we may have gotten NULL back, meaning no entity was available + if (thisClosestEntity) { + glm::vec3 entityPosition = thisClosestEntity->getWorldPosition(); + float distanceFromPointToEntity = glm::distance(entityPosition, args->position); + + // If we're within our target radius + if (distanceFromPointToEntity <= args->targetRadius) { + // we are closer than anything else we've found + if (distanceFromPointToEntity < args->closestEntityDistance) { + args->closestEntity = thisClosestEntity; + args->closestEntityDistance = distanceFromPointToEntity; + args->found = true; + } + } + } + + // we should be able to optimize this... + return true; // keep searching in case children have closer entities + } + + // if this element doesn't contain the point, then none of its children can contain the point, so stop searching + return false; +} + +bool findRayIntersectionOp(const OctreeElementPointer& element, void* extraData) { RayArgs* args = static_cast(extraData); bool keepSearching = true; EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); - EntityItemID entityID = entityTreeElementPointer->evalRayIntersection(args->origin, args->direction, + EntityItemID entityID = entityTreeElementPointer->findRayIntersection(args->origin, args->direction, args->element, args->distance, args->face, args->surfaceNormal, args->entityIdsToInclude, - args->entityIdsToDiscard, args->searchFilter, args->extraInfo); + args->entityIdsToDiscard, args->visibleOnly, args->collidableOnly, args->extraInfo, args->precisionPicking); if (!entityID.isNull()) { args->entityID = entityID; // We recurse OctreeElements in order, so if we hit something, we can stop immediately @@ -768,7 +839,7 @@ bool evalRayIntersectionOp(const OctreeElementPointer& element, void* extraData) return keepSearching; } -float evalRayIntersectionSortingOp(const OctreeElementPointer& element, void* extraData) { +float findRayIntersectionSortingOp(const OctreeElementPointer& element, void* extraData) { RayArgs* args = static_cast(extraData); EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); float distance = FLT_MAX; @@ -789,18 +860,19 @@ float evalRayIntersectionSortingOp(const OctreeElementPointer& element, void* ex return distance; } -EntityItemID EntityTree::evalRayIntersection(const glm::vec3& origin, const glm::vec3& direction, +EntityItemID EntityTree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, QVector entityIdsToInclude, QVector entityIdsToDiscard, - PickFilter searchFilter, OctreeElementPointer& element, float& distance, + bool visibleOnly, bool collidableOnly, bool precisionPicking, + OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, Octree::lockType lockType, bool* accurateResult) { RayArgs args = { origin, direction, 1.0f / direction, entityIdsToInclude, entityIdsToDiscard, - searchFilter, element, distance, face, surfaceNormal, extraInfo, EntityItemID() }; + visibleOnly, collidableOnly, precisionPicking, element, distance, face, surfaceNormal, extraInfo, EntityItemID() }; distance = FLT_MAX; bool requireLock = lockType == Octree::Lock; bool lockResult = withReadLock([&]{ - recurseTreeWithOperationSorted(evalRayIntersectionOp, evalRayIntersectionSortingOp, &args); + recurseTreeWithOperationSorted(findRayIntersectionOp, findRayIntersectionSortingOp, &args); }, requireLock); if (accurateResult) { @@ -810,32 +882,13 @@ EntityItemID EntityTree::evalRayIntersection(const glm::vec3& origin, const glm: return args.entityID; } -class ParabolaArgs { -public: - // Inputs - glm::vec3 origin; - glm::vec3 velocity; - glm::vec3 acceleration; - const QVector& entityIdsToInclude; - const QVector& entityIdsToDiscard; - PickFilter searchFilter; - - // Outputs - OctreeElementPointer& element; - float& parabolicDistance; - BoxFace& face; - glm::vec3& surfaceNormal; - QVariantMap& extraInfo; - EntityItemID entityID; -}; - -bool evalParabolaIntersectionOp(const OctreeElementPointer& element, void* extraData) { +bool findParabolaIntersectionOp(const OctreeElementPointer& element, void* extraData) { ParabolaArgs* args = static_cast(extraData); bool keepSearching = true; EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); - EntityItemID entityID = entityTreeElementPointer->evalParabolaIntersection(args->origin, args->velocity, args->acceleration, + EntityItemID entityID = entityTreeElementPointer->findParabolaIntersection(args->origin, args->velocity, args->acceleration, args->element, args->parabolicDistance, args->face, args->surfaceNormal, args->entityIdsToInclude, - args->entityIdsToDiscard, args->searchFilter, args->extraInfo); + args->entityIdsToDiscard, args->visibleOnly, args->collidableOnly, args->extraInfo, args->precisionPicking); if (!entityID.isNull()) { args->entityID = entityID; // We recurse OctreeElements in order, so if we hit something, we can stop immediately @@ -844,7 +897,7 @@ bool evalParabolaIntersectionOp(const OctreeElementPointer& element, void* extra return keepSearching; } -float evalParabolaIntersectionSortingOp(const OctreeElementPointer& element, void* extraData) { +float findParabolaIntersectionSortingOp(const OctreeElementPointer& element, void* extraData) { ParabolaArgs* args = static_cast(extraData); EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); float distance = FLT_MAX; @@ -865,20 +918,20 @@ float evalParabolaIntersectionSortingOp(const OctreeElementPointer& element, voi return distance; } -EntityItemID EntityTree::evalParabolaIntersection(const PickParabola& parabola, +EntityItemID EntityTree::findParabolaIntersection(const PickParabola& parabola, QVector entityIdsToInclude, QVector entityIdsToDiscard, - PickFilter searchFilter, + bool visibleOnly, bool collidableOnly, bool precisionPicking, OctreeElementPointer& element, glm::vec3& intersection, float& distance, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, Octree::lockType lockType, bool* accurateResult) { ParabolaArgs args = { parabola.origin, parabola.velocity, parabola.acceleration, entityIdsToInclude, entityIdsToDiscard, - searchFilter, element, parabolicDistance, face, surfaceNormal, extraInfo, EntityItemID() }; + visibleOnly, collidableOnly, precisionPicking, element, parabolicDistance, face, surfaceNormal, extraInfo, EntityItemID() }; parabolicDistance = FLT_MAX; distance = FLT_MAX; bool requireLock = lockType == Octree::Lock; bool lockResult = withReadLock([&] { - recurseTreeWithOperationSorted(evalParabolaIntersectionOp, evalParabolaIntersectionSortingOp, &args); + recurseTreeWithOperationSorted(findParabolaIntersectionOp, findParabolaIntersectionSortingOp, &args); }, requireLock); if (accurateResult) { @@ -893,80 +946,33 @@ EntityItemID EntityTree::evalParabolaIntersection(const PickParabola& parabola, return args.entityID; } -class FindClosestEntityArgs { -public: - // Inputs - glm::vec3 position; - float targetRadius; - PickFilter searchFilter; - // Outputs - QUuid closestEntity; - float closestEntityDistance; -}; - - -bool evalClosestEntityOperation(const OctreeElementPointer& element, void* extraData) { - FindClosestEntityArgs* args = static_cast(extraData); - EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); - - glm::vec3 penetration; - bool sphereIntersection = entityTreeElement->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration); - - // If this entityTreeElement contains the point, then search it... - if (sphereIntersection) { - float closestDistanceSquared = FLT_MAX; - QUuid thisClosestEntity = entityTreeElement->evalClosetEntity(args->position, args->searchFilter, closestDistanceSquared); - - // we may have gotten NULL back, meaning no entity was available - if (!thisClosestEntity.isNull()) { - float distanceFromPointToEntity = glm::sqrt(closestDistanceSquared); - - // If we're within our target radius - if (distanceFromPointToEntity <= args->targetRadius) { - // we are closer than anything else we've found - if (distanceFromPointToEntity < args->closestEntityDistance) { - args->closestEntity = thisClosestEntity; - args->closestEntityDistance = distanceFromPointToEntity; - } - } - } - - // we should be able to optimize this... - return true; // keep searching in case children have closer entities - } - - // if this element doesn't contain the point, then none of its children can contain the point, so stop searching - return false; -} - -// NOTE: assumes caller has handled locking -QUuid EntityTree::evalClosestEntity(const glm::vec3& position, float targetRadius, PickFilter searchFilter) { - FindClosestEntityArgs args = { position, targetRadius, searchFilter, QUuid(), FLT_MAX }; - recurseTreeWithOperation(evalClosestEntityOperation, &args); +EntityItemPointer EntityTree::findClosestEntity(const glm::vec3& position, float targetRadius) { + FindNearPointArgs args = { position, targetRadius, false, NULL, FLT_MAX }; + withReadLock([&] { + // NOTE: This should use recursion, since this is a spatial operation + recurseTreeWithOperation(findNearPointOperation, &args); + }); return args.closestEntity; } -class FindEntitiesInSphereArgs { +class FindAllNearPointArgs { public: - // Inputs glm::vec3 position; float targetRadius; - PickFilter searchFilter; - - // Outputs - QVector entities; + QVector entities; }; -bool evalInSphereOperation(const OctreeElementPointer& element, void* extraData) { - FindEntitiesInSphereArgs* args = static_cast(extraData); + +bool EntityTree::findInSphereOperation(const OctreeElementPointer& element, void* extraData) { + FindAllNearPointArgs* args = static_cast(extraData); glm::vec3 penetration; bool sphereIntersection = element->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration); // If this element contains the point, then search it... if (sphereIntersection) { EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); - entityTreeElement->evalEntitiesInSphere(args->position, args->targetRadius, args->searchFilter, args->entities); + entityTreeElement->getEntities(args->position, args->targetRadius, args->entities); return true; // keep searching in case children have closer entities } @@ -975,168 +981,104 @@ bool evalInSphereOperation(const OctreeElementPointer& element, void* extraData) } // NOTE: assumes caller has handled locking -void EntityTree::evalEntitiesInSphere(const glm::vec3& center, float radius, PickFilter searchFilter, QVector& foundEntities) { - FindEntitiesInSphereArgs args = { center, radius, searchFilter, QVector() }; - recurseTreeWithOperation(evalInSphereOperation, &args); - foundEntities.swap(args.entities); -} +void EntityTree::findEntities(const glm::vec3& center, float radius, QVector& foundEntities) { + FindAllNearPointArgs args = { center, radius, QVector() }; + // NOTE: This should use recursion, since this is a spatial operation + recurseTreeWithOperation(findInSphereOperation, &args); -class FindEntitiesInSphereWithTypeArgs { -public: - // Inputs - glm::vec3 position; - float targetRadius; - EntityTypes::EntityType type; - PickFilter searchFilter; - - // Outputs - QVector entities; -}; - -bool evalInSphereWithTypeOperation(const OctreeElementPointer& element, void* extraData) { - FindEntitiesInSphereWithTypeArgs* args = static_cast(extraData); - glm::vec3 penetration; - bool sphereIntersection = element->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration); - - // If this element contains the point, then search it... - if (sphereIntersection) { - EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); - entityTreeElement->evalEntitiesInSphereWithType(args->position, args->targetRadius, args->type, args->searchFilter, args->entities); - return true; // keep searching in case children have closer entities - } - - // if this element doesn't contain the point, then none of it's children can contain the point, so stop searching - return false; -} - -// NOTE: assumes caller has handled locking -void EntityTree::evalEntitiesInSphereWithType(const glm::vec3& center, float radius, EntityTypes::EntityType type, PickFilter searchFilter, QVector& foundEntities) { - FindEntitiesInSphereWithTypeArgs args = { center, radius, type, searchFilter, QVector() }; - recurseTreeWithOperation(evalInSphereWithTypeOperation, &args); - foundEntities.swap(args.entities); -} - -class FindEntitiesInSphereWithNameArgs { -public: - // Inputs - glm::vec3 position; - float targetRadius; - QString name; - bool caseSensitive; - PickFilter searchFilter; - - // Outputs - QVector entities; -}; - -bool evalInSphereWithNameOperation(const OctreeElementPointer& element, void* extraData) { - FindEntitiesInSphereWithNameArgs* args = static_cast(extraData); - glm::vec3 penetration; - bool sphereIntersection = element->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration); - - // If this element contains the point, then search it... - if (sphereIntersection) { - EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); - entityTreeElement->evalEntitiesInSphereWithName(args->position, args->targetRadius, args->name, args->caseSensitive, args->searchFilter, args->entities); - return true; // keep searching in case children have closer entities - } - - // if this element doesn't contain the point, then none of it's children can contain the point, so stop searching - return false; -} - -// NOTE: assumes caller has handled locking -void EntityTree::evalEntitiesInSphereWithName(const glm::vec3& center, float radius, const QString& name, bool caseSensitive, PickFilter searchFilter, QVector& foundEntities) { - FindEntitiesInSphereWithNameArgs args = { center, radius, name, caseSensitive, searchFilter, QVector() }; - recurseTreeWithOperation(evalInSphereWithNameOperation, &args); + // swap the two lists of entity pointers instead of copy foundEntities.swap(args.entities); } class FindEntitiesInCubeArgs { public: - // Inputs - AACube cube; - PickFilter searchFilter; + FindEntitiesInCubeArgs(const AACube& cube) + : _cube(cube), _foundEntities() { + } - // Outputs - QVector entities; + AACube _cube; + QVector _foundEntities; }; -bool findInCubeOperation(const OctreeElementPointer& element, void* extraData) { +bool EntityTree::findInCubeOperation(const OctreeElementPointer& element, void* extraData) { FindEntitiesInCubeArgs* args = static_cast(extraData); - if (element->getAACube().touches(args->cube)) { + if (element->getAACube().touches(args->_cube)) { EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); - entityTreeElement->evalEntitiesInCube(args->cube, args->searchFilter, args->entities); + entityTreeElement->getEntities(args->_cube, args->_foundEntities); return true; } return false; } // NOTE: assumes caller has handled locking -void EntityTree::evalEntitiesInCube(const AACube& cube, PickFilter searchFilter, QVector& foundEntities) { - FindEntitiesInCubeArgs args { cube, searchFilter, QVector() }; +void EntityTree::findEntities(const AACube& cube, QVector& foundEntities) { + FindEntitiesInCubeArgs args(cube); + // NOTE: This should use recursion, since this is a spatial operation recurseTreeWithOperation(findInCubeOperation, &args); - foundEntities.swap(args.entities); + // swap the two lists of entity pointers instead of copy + foundEntities.swap(args._foundEntities); } class FindEntitiesInBoxArgs { public: - // Inputs - AABox box; - PickFilter searchFilter; + FindEntitiesInBoxArgs(const AABox& box) + : _box(box), _foundEntities() { + } - // Outputs - QVector entities; + AABox _box; + QVector _foundEntities; }; -bool findInBoxOperation(const OctreeElementPointer& element, void* extraData) { +bool EntityTree::findInBoxOperation(const OctreeElementPointer& element, void* extraData) { FindEntitiesInBoxArgs* args = static_cast(extraData); - if (element->getAACube().touches(args->box)) { + if (element->getAACube().touches(args->_box)) { EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); - entityTreeElement->evalEntitiesInBox(args->box, args->searchFilter, args->entities); + entityTreeElement->getEntities(args->_box, args->_foundEntities); return true; } return false; } // NOTE: assumes caller has handled locking -void EntityTree::evalEntitiesInBox(const AABox& box, PickFilter searchFilter, QVector& foundEntities) { - FindEntitiesInBoxArgs args { box, searchFilter, QVector() }; +void EntityTree::findEntities(const AABox& box, QVector& foundEntities) { + FindEntitiesInBoxArgs args(box); // NOTE: This should use recursion, since this is a spatial operation recurseTreeWithOperation(findInBoxOperation, &args); // swap the two lists of entity pointers instead of copy - foundEntities.swap(args.entities); + foundEntities.swap(args._foundEntities); } -class FindEntitiesInFrustumArgs { +class FindInFrustumArgs { public: - // Inputs ViewFrustum frustum; - PickFilter searchFilter; - - // Outputs - QVector entities; + QVector entities; }; -bool findInFrustumOperation(const OctreeElementPointer& element, void* extraData) { - FindEntitiesInFrustumArgs* args = static_cast(extraData); +bool EntityTree::findInFrustumOperation(const OctreeElementPointer& element, void* extraData) { + FindInFrustumArgs* args = static_cast(extraData); if (element->isInView(args->frustum)) { EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); - entityTreeElement->evalEntitiesInFrustum(args->frustum, args->searchFilter, args->entities); + entityTreeElement->getEntities(args->frustum, args->entities); return true; } return false; } // NOTE: assumes caller has handled locking -void EntityTree::evalEntitiesInFrustum(const ViewFrustum& frustum, PickFilter searchFilter, QVector& foundEntities) { - FindEntitiesInFrustumArgs args = { frustum, searchFilter, QVector() }; +void EntityTree::findEntities(const ViewFrustum& frustum, QVector& foundEntities) { + FindInFrustumArgs args = { frustum, QVector() }; // NOTE: This should use recursion, since this is a spatial operation recurseTreeWithOperation(findInFrustumOperation, &args); // swap the two lists of entity pointers instead of copy foundEntities.swap(args.entities); } +// NOTE: assumes caller has handled locking +void EntityTree::findEntities(RecurseOctreeOperation& elementFilter, + QVector& foundEntities) { + recurseTreeWithOperation(elementFilter, nullptr); +} + EntityItemPointer EntityTree::findEntityByID(const QUuid& id) const { EntityItemID entityID(id); return findEntityByEntityItemID(entityID); diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index d08e21e4f0..d7f93b1eb2 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -45,6 +45,7 @@ public: QHash* map; }; + class EntityTree : public Octree, public SpatialParentTree { Q_OBJECT public: @@ -91,16 +92,18 @@ public: virtual void processChallengeOwnershipReplyPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) override; virtual void processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) override; - virtual EntityItemID evalRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + virtual EntityItemID findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, QVector entityIdsToInclude, QVector entityIdsToDiscard, - PickFilter searchFilter, OctreeElementPointer& element, float& distance, + bool visibleOnly, bool collidableOnly, bool precisionPicking, + OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL); - virtual EntityItemID evalParabolaIntersection(const PickParabola& parabola, + virtual EntityItemID findParabolaIntersection(const PickParabola& parabola, QVector entityIdsToInclude, QVector entityIdsToDiscard, - PickFilter searchFilter, OctreeElementPointer& element, glm::vec3& intersection, - float& distance, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, + bool visibleOnly, bool collidableOnly, bool precisionPicking, + OctreeElementPointer& element, glm::vec3& intersection, float& distance, float& parabolicDistance, + BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL); virtual bool rootElementHasData() const override { return true; } @@ -125,19 +128,44 @@ public: void deleteEntity(const EntityItemID& entityID, bool force = false, bool ignoreWarnings = true); void deleteEntities(QSet entityIDs, bool force = false, bool ignoreWarnings = true); + /// \param position point of query in world-frame (meters) + /// \param targetRadius radius of query (meters) + EntityItemPointer findClosestEntity(const glm::vec3& position, float targetRadius); EntityItemPointer findEntityByID(const QUuid& id) const; EntityItemPointer findEntityByEntityItemID(const EntityItemID& entityID) const; virtual SpatiallyNestablePointer findByID(const QUuid& id) const override { return findEntityByID(id); } EntityItemID assignEntityID(const EntityItemID& entityItemID); /// Assigns a known ID for a creator token ID - QUuid evalClosestEntity(const glm::vec3& position, float targetRadius, PickFilter searchFilter); - void evalEntitiesInSphere(const glm::vec3& center, float radius, PickFilter searchFilter, QVector& foundEntities); - void evalEntitiesInSphereWithType(const glm::vec3& center, float radius, EntityTypes::EntityType type, PickFilter searchFilter, QVector& foundEntities); - void evalEntitiesInSphereWithName(const glm::vec3& center, float radius, const QString& name, bool caseSensitive, PickFilter searchFilter, QVector& foundEntities); - void evalEntitiesInCube(const AACube& cube, PickFilter searchFilter, QVector& foundEntities); - void evalEntitiesInBox(const AABox& box, PickFilter searchFilter, QVector& foundEntities); - void evalEntitiesInFrustum(const ViewFrustum& frustum, PickFilter searchFilter, QVector& foundEntities); + + /// finds all entities that touch a sphere + /// \param center the center of the sphere in world-frame (meters) + /// \param radius the radius of the sphere in world-frame (meters) + /// \param foundEntities[out] vector of EntityItemPointer + /// \remark Side effect: any initial contents in foundEntities will be lost + void findEntities(const glm::vec3& center, float radius, QVector& foundEntities); + + /// finds all entities that touch a cube + /// \param cube the query cube in world-frame (meters) + /// \param foundEntities[out] vector of non-EntityItemPointer + /// \remark Side effect: any initial contents in entities will be lost + void findEntities(const AACube& cube, QVector& foundEntities); + + /// finds all entities that touch a box + /// \param box the query box in world-frame (meters) + /// \param foundEntities[out] vector of non-EntityItemPointer + /// \remark Side effect: any initial contents in entities will be lost + void findEntities(const AABox& box, QVector& foundEntities); + + /// finds all entities within a frustum + /// \parameter frustum the query frustum + /// \param foundEntities[out] vector of EntityItemPointer + void findEntities(const ViewFrustum& frustum, QVector& foundEntities); + + /// finds all entities that match scanOperator + /// \parameter scanOperator function that scans entities that match criteria + /// \parameter foundEntities[out] vector of EntityItemPointer + void findEntities(RecurseOctreeOperation& scanOperator, QVector& foundEntities); void addNewlyCreatedHook(NewlyCreatedEntityHook* hook); void removeNewlyCreatedHook(NewlyCreatedEntityHook* hook); @@ -294,6 +322,11 @@ protected: void processRemovedEntities(const DeleteEntityOperator& theOperator); bool updateEntity(EntityItemPointer entity, const EntityItemProperties& properties, const SharedNodePointer& senderNode = SharedNodePointer(nullptr)); + static bool findNearPointOperation(const OctreeElementPointer& element, void* extraData); + static bool findInSphereOperation(const OctreeElementPointer& element, void* extraData); + static bool findInCubeOperation(const OctreeElementPointer& element, void* extraData); + static bool findInBoxOperation(const OctreeElementPointer& element, void* extraData); + static bool findInFrustumOperation(const OctreeElementPointer& element, void* extraData); static bool sendEntitiesOperation(const OctreeElementPointer& element, void* extraData); static void bumpTimestamp(EntityItemProperties& properties); diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 2ece6835ea..5b71010f75 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -139,23 +139,10 @@ bool EntityTreeElement::bestFitBounds(const glm::vec3& minPoint, const glm::vec3 return false; } -bool checkFilterSettings(const EntityItemPointer& entity, PickFilter searchFilter) { - bool visible = entity->isVisible(); - bool collidable = !entity->getCollisionless() && (entity->getShapeType() != SHAPE_TYPE_NONE); - if ((!searchFilter.doesPickVisible() && visible) || (!searchFilter.doesPickInvisible() && !visible) || - (!searchFilter.doesPickCollidable() && collidable) || (!searchFilter.doesPickNonCollidable() && !collidable) || - (!searchFilter.doesPickDomainEntities() && entity->isDomainEntity()) || - (!searchFilter.doesPickAvatarEntities() && entity->isAvatarEntity()) || - (!searchFilter.doesPickLocalEntities() && entity->isLocalEntity())) { - return false; - } - return true; -} - -EntityItemID EntityTreeElement::evalRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, - const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, - PickFilter searchFilter, QVariantMap& extraInfo) { +EntityItemID EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, + const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, + bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking) { EntityItemID result; BoxFace localFace { UNKNOWN_FACE }; @@ -167,8 +154,9 @@ EntityItemID EntityTreeElement::evalRayIntersection(const glm::vec3& origin, con QVariantMap localExtraInfo; float distanceToElementDetails = distance; - EntityItemID entityID = evalDetailedRayIntersection(origin, direction, element, distanceToElementDetails, - localFace, localSurfaceNormal, entityIdsToInclude, entityIdsToDiscard, searchFilter, localExtraInfo); + EntityItemID entityID = findDetailedRayIntersection(origin, direction, element, distanceToElementDetails, + localFace, localSurfaceNormal, entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly, + localExtraInfo, precisionPicking); if (!entityID.isNull() && distanceToElementDetails < distance) { distance = distanceToElementDetails; face = localFace; @@ -179,12 +167,13 @@ EntityItemID EntityTreeElement::evalRayIntersection(const glm::vec3& origin, con return result; } -EntityItemID EntityTreeElement::evalDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, +EntityItemID EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, const QVector& entityIDsToDiscard, - PickFilter searchFilter, QVariantMap& extraInfo) { + bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking) { // only called if we do intersect our bounding cube, but find if we actually intersect with entities... + int entityNumber = 0; EntityItemID entityID; forEachEntity([&](EntityItemPointer entity) { // use simple line-sphere for broadphase check @@ -198,9 +187,11 @@ EntityItemID EntityTreeElement::evalDetailedRayIntersection(const glm::vec3& ori return; } - if (!checkFilterSettings(entity, searchFilter) || - (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID())) || - (entityIDsToDiscard.size() > 0 && entityIDsToDiscard.contains(entity->getID())) ) { + // check RayPick filter settings + if ((visibleOnly && !entity->isVisible()) + || (collidableOnly && (entity->getCollisionless() || entity->getShapeType() == SHAPE_TYPE_NONE)) + || (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID())) + || (entityIDsToDiscard.size() > 0 && entityIDsToDiscard.contains(entity->getID())) ) { return; } @@ -231,7 +222,7 @@ EntityItemID EntityTreeElement::evalDetailedRayIntersection(const glm::vec3& ori if (entity->supportsDetailedIntersection()) { QVariantMap localExtraInfo; if (entity->findDetailedRayIntersection(origin, direction, element, localDistance, - localFace, localSurfaceNormal, localExtraInfo, searchFilter.isPrecise())) { + localFace, localSurfaceNormal, localExtraInfo, precisionPicking)) { if (localDistance < distance) { distance = localDistance; face = localFace; @@ -253,6 +244,7 @@ EntityItemID EntityTreeElement::evalDetailedRayIntersection(const glm::vec3& ori } } } + entityNumber++; }); return entityID; } @@ -283,10 +275,11 @@ bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float rad return result; } -EntityItemID EntityTreeElement::evalParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, +EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, - const QVector& entityIdsToDiscard, PickFilter searchFilter, QVariantMap& extraInfo) { + const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly, + QVariantMap& extraInfo, bool precisionPicking) { EntityItemID result; BoxFace localFace; @@ -307,8 +300,9 @@ EntityItemID EntityTreeElement::evalParabolaIntersection(const glm::vec3& origin } // Get the normal of the plane, the cross product of two vectors on the plane glm::vec3 normal = glm::normalize(glm::cross(vectorOnPlane, acceleration)); - EntityItemID entityID = evalDetailedParabolaIntersection(origin, velocity, acceleration, normal, element, distanceToElementDetails, - localFace, localSurfaceNormal, entityIdsToInclude, entityIdsToDiscard, searchFilter, localExtraInfo); + EntityItemID entityID = findDetailedParabolaIntersection(origin, velocity, acceleration, normal, element, distanceToElementDetails, + localFace, localSurfaceNormal, entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly, + localExtraInfo, precisionPicking); if (!entityID.isNull() && distanceToElementDetails < parabolicDistance) { parabolicDistance = distanceToElementDetails; face = localFace; @@ -319,12 +313,13 @@ EntityItemID EntityTreeElement::evalParabolaIntersection(const glm::vec3& origin return result; } -EntityItemID EntityTreeElement::evalDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, +EntityItemID EntityTreeElement::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, const glm::vec3& normal, OctreeElementPointer& element, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, const QVector& entityIDsToDiscard, - PickFilter searchFilter, QVariantMap& extraInfo) { + bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking) { // only called if we do intersect our bounding cube, but find if we actually intersect with entities... + int entityNumber = 0; EntityItemID entityID; forEachEntity([&](EntityItemPointer entity) { // use simple line-sphere for broadphase check @@ -343,9 +338,11 @@ EntityItemID EntityTreeElement::evalDetailedParabolaIntersection(const glm::vec3 return; } - if (!checkFilterSettings(entity, searchFilter) || - (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID())) || - (entityIDsToDiscard.size() > 0 && entityIDsToDiscard.contains(entity->getID()))) { + // check RayPick filter settings + if ((visibleOnly && !entity->isVisible()) + || (collidableOnly && (entity->getCollisionless() || entity->getShapeType() == SHAPE_TYPE_NONE)) + || (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID())) + || (entityIDsToDiscard.size() > 0 && entityIDsToDiscard.contains(entity->getID())) ) { return; } @@ -377,7 +374,7 @@ EntityItemID EntityTreeElement::evalDetailedParabolaIntersection(const glm::vec3 if (entity->supportsDetailedIntersection()) { QVariantMap localExtraInfo; if (entity->findDetailedParabolaIntersection(origin, velocity, acceleration, element, localDistance, - localFace, localSurfaceNormal, localExtraInfo, searchFilter.isPrecise())) { + localFace, localSurfaceNormal, localExtraInfo, precisionPicking)) { if (localDistance < parabolicDistance) { parabolicDistance = localDistance; face = localFace; @@ -399,56 +396,55 @@ EntityItemID EntityTreeElement::evalDetailedParabolaIntersection(const glm::vec3 } } } + entityNumber++; }); return entityID; } -QUuid EntityTreeElement::evalClosetEntity(const glm::vec3& position, PickFilter searchFilter, float& closestDistanceSquared) const { - QUuid closestEntity; - forEachEntity([&](EntityItemPointer entity) { - if (!checkFilterSettings(entity, searchFilter)) { - return; - } - - float distanceToEntity = glm::distance2(position, entity->getWorldPosition()); - if (distanceToEntity < closestDistanceSquared) { - closestEntity = entity->getID(); - closestDistanceSquared = distanceToEntity; +EntityItemPointer EntityTreeElement::getClosestEntity(glm::vec3 position) const { + EntityItemPointer closestEntity = NULL; + float closestEntityDistance = FLT_MAX; + withReadLock([&] { + foreach(EntityItemPointer entity, _entityItems) { + float distanceToEntity = glm::distance2(position, entity->getWorldPosition()); + if (distanceToEntity < closestEntityDistance) { + closestEntity = entity; + } } }); return closestEntity; } -void EntityTreeElement::evalEntitiesInSphere(const glm::vec3& position, float radius, PickFilter searchFilter, QVector& foundEntities) const { +// TODO: change this to use better bounding shape for entity than sphere +void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searchRadius, QVector& foundEntities) const { forEachEntity([&](EntityItemPointer entity) { - if (!checkFilterSettings(entity, searchFilter)) { - return; - } bool success; AABox entityBox = entity->getAABox(success); // if the sphere doesn't intersect with our world frame AABox, we don't need to consider the more complex case glm::vec3 penetration; - if (success && entityBox.findSpherePenetration(position, radius, penetration)) { + if (!success || entityBox.findSpherePenetration(searchPosition, searchRadius, penetration)) { glm::vec3 dimensions = entity->getRaycastDimensions(); // FIXME - consider allowing the entity to determine penetration so that - // entities could presumably do actual hull testing if they wanted to + // entities could presumably dull actuall hull testing if they wanted to // FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better in particular // can we handle the ellipsoid case better? We only currently handle perfect spheres // with centered registration points - if (entity->getShapeType() == SHAPE_TYPE_SPHERE && (dimensions.x == dimensions.y && dimensions.y == dimensions.z)) { + if (entity->getShapeType() == SHAPE_TYPE_SPHERE && + (dimensions.x == dimensions.y && dimensions.y == dimensions.z)) { // NOTE: entity->getRadius() doesn't return the true radius, it returns the radius of the // maximum bounding sphere, which is actually larger than our actual radius float entityTrueRadius = dimensions.x / 2.0f; bool success; - if (findSphereSpherePenetration(position, radius, entity->getCenterPosition(success), entityTrueRadius, penetration)) { + if (findSphereSpherePenetration(searchPosition, searchRadius, + entity->getCenterPosition(success), entityTrueRadius, penetration)) { if (success) { - foundEntities.push_back(entity->getID()); + foundEntities.push_back(entity); } } } else { @@ -464,134 +460,17 @@ void EntityTreeElement::evalEntitiesInSphere(const glm::vec3& position, float ra AABox entityFrameBox(corner, dimensions); - glm::vec3 entityFrameSearchPosition = glm::vec3(worldToEntityMatrix * glm::vec4(position, 1.0f)); - if (entityFrameBox.findSpherePenetration(entityFrameSearchPosition, radius, penetration)) { - foundEntities.push_back(entity->getID()); + glm::vec3 entityFrameSearchPosition = glm::vec3(worldToEntityMatrix * glm::vec4(searchPosition, 1.0f)); + if (entityFrameBox.findSpherePenetration(entityFrameSearchPosition, searchRadius, penetration)) { + foundEntities.push_back(entity); } } } }); } -void EntityTreeElement::evalEntitiesInSphereWithType(const glm::vec3& position, float radius, EntityTypes::EntityType type, PickFilter searchFilter, QVector& foundEntities) const { +void EntityTreeElement::getEntities(const AACube& cube, QVector& foundEntities) { forEachEntity([&](EntityItemPointer entity) { - if (!checkFilterSettings(entity, searchFilter) || type != entity->getType()) { - return; - } - - bool success; - AABox entityBox = entity->getAABox(success); - - // if the sphere doesn't intersect with our world frame AABox, we don't need to consider the more complex case - glm::vec3 penetration; - if (success && entityBox.findSpherePenetration(position, radius, penetration)) { - - glm::vec3 dimensions = entity->getRaycastDimensions(); - - // FIXME - consider allowing the entity to determine penetration so that - // entities could presumably do actual hull testing if they wanted to - // FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better in particular - // can we handle the ellipsoid case better? We only currently handle perfect spheres - // with centered registration points - if (entity->getShapeType() == SHAPE_TYPE_SPHERE && (dimensions.x == dimensions.y && dimensions.y == dimensions.z)) { - - // NOTE: entity->getRadius() doesn't return the true radius, it returns the radius of the - // maximum bounding sphere, which is actually larger than our actual radius - float entityTrueRadius = dimensions.x / 2.0f; - - bool success; - if (findSphereSpherePenetration(position, radius, entity->getCenterPosition(success), entityTrueRadius, penetration)) { - if (success) { - foundEntities.push_back(entity->getID()); - } - } - } else { - // determine the worldToEntityMatrix that doesn't include scale because - // we're going to use the registration aware aa box in the entity frame - glm::mat4 rotation = glm::mat4_cast(entity->getWorldOrientation()); - glm::mat4 translation = glm::translate(entity->getWorldPosition()); - glm::mat4 entityToWorldMatrix = translation * rotation; - glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); - - glm::vec3 registrationPoint = entity->getRegistrationPoint(); - glm::vec3 corner = -(dimensions * registrationPoint); - - AABox entityFrameBox(corner, dimensions); - - glm::vec3 entityFrameSearchPosition = glm::vec3(worldToEntityMatrix * glm::vec4(position, 1.0f)); - if (entityFrameBox.findSpherePenetration(entityFrameSearchPosition, radius, penetration)) { - foundEntities.push_back(entity->getID()); - } - } - } - }); -} - -void EntityTreeElement::evalEntitiesInSphereWithName(const glm::vec3& position, float radius, const QString& name, bool caseSensitive, PickFilter searchFilter, QVector& foundEntities) const { - forEachEntity([&](EntityItemPointer entity) { - if (!checkFilterSettings(entity, searchFilter)) { - return; - } - - QString entityName = entity->getName(); - if ((caseSensitive && name != entityName) || (!caseSensitive && name.toLower() != entityName.toLower())) { - return; - } - - bool success; - AABox entityBox = entity->getAABox(success); - - // if the sphere doesn't intersect with our world frame AABox, we don't need to consider the more complex case - glm::vec3 penetration; - if (success && entityBox.findSpherePenetration(position, radius, penetration)) { - - glm::vec3 dimensions = entity->getRaycastDimensions(); - - // FIXME - consider allowing the entity to determine penetration so that - // entities could presumably do actual hull testing if they wanted to - // FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better in particular - // can we handle the ellipsoid case better? We only currently handle perfect spheres - // with centered registration points - if (entity->getShapeType() == SHAPE_TYPE_SPHERE && (dimensions.x == dimensions.y && dimensions.y == dimensions.z)) { - - // NOTE: entity->getRadius() doesn't return the true radius, it returns the radius of the - // maximum bounding sphere, which is actually larger than our actual radius - float entityTrueRadius = dimensions.x / 2.0f; - - bool success; - if (findSphereSpherePenetration(position, radius, entity->getCenterPosition(success), entityTrueRadius, penetration)) { - if (success) { - foundEntities.push_back(entity->getID()); - } - } - } else { - // determine the worldToEntityMatrix that doesn't include scale because - // we're going to use the registration aware aa box in the entity frame - glm::mat4 rotation = glm::mat4_cast(entity->getWorldOrientation()); - glm::mat4 translation = glm::translate(entity->getWorldPosition()); - glm::mat4 entityToWorldMatrix = translation * rotation; - glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); - - glm::vec3 registrationPoint = entity->getRegistrationPoint(); - glm::vec3 corner = -(dimensions * registrationPoint); - - AABox entityFrameBox(corner, dimensions); - - glm::vec3 entityFrameSearchPosition = glm::vec3(worldToEntityMatrix * glm::vec4(position, 1.0f)); - if (entityFrameBox.findSpherePenetration(entityFrameSearchPosition, radius, penetration)) { - foundEntities.push_back(entity->getID()); - } - } - } - }); -} - -void EntityTreeElement::evalEntitiesInCube(const AACube& cube, PickFilter searchFilter, QVector& foundEntities) const { - forEachEntity([&](EntityItemPointer entity) { - if (!checkFilterSettings(entity, searchFilter)) { - return; - } - bool success; AABox entityBox = entity->getAABox(success); // FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better @@ -610,18 +489,14 @@ void EntityTreeElement::evalEntitiesInCube(const AACube& cube, PickFilter search // // If the entities AABox touches the search cube then consider it to be found - if (success && entityBox.touches(cube)) { - foundEntities.push_back(entity->getID()); + if (!success || entityBox.touches(cube)) { + foundEntities.push_back(entity); } }); } -void EntityTreeElement::evalEntitiesInBox(const AABox& box, PickFilter searchFilter, QVector& foundEntities) const { +void EntityTreeElement::getEntities(const AABox& box, QVector& foundEntities) { forEachEntity([&](EntityItemPointer entity) { - if (!checkFilterSettings(entity, searchFilter)) { - return; - } - bool success; AABox entityBox = entity->getAABox(success); // FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better @@ -640,24 +515,19 @@ void EntityTreeElement::evalEntitiesInBox(const AABox& box, PickFilter searchFil // // If the entities AABox touches the search cube then consider it to be found - if (success && entityBox.touches(box)) { - foundEntities.push_back(entity->getID()); + if (!success || entityBox.touches(box)) { + foundEntities.push_back(entity); } }); } -void EntityTreeElement::evalEntitiesInFrustum(const ViewFrustum& frustum, PickFilter searchFilter, QVector& foundEntities) const { +void EntityTreeElement::getEntities(const ViewFrustum& frustum, QVector& foundEntities) { forEachEntity([&](EntityItemPointer entity) { - if (!checkFilterSettings(entity, searchFilter)) { - return; - } - bool success; AABox entityBox = entity->getAABox(success); - // FIXME - See FIXMEs for similar methods above. - if (success && (frustum.boxIntersectsFrustum(entityBox) || frustum.boxIntersectsKeyhole(entityBox))) { - foundEntities.push_back(entity->getID()); + if (!success || frustum.boxIntersectsFrustum(entityBox) || frustum.boxIntersectsKeyhole(entityBox)) { + foundEntities.push_back(entity); } }); } diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index aed19eed15..793340c9a4 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -19,8 +19,7 @@ #include "EntityEditPacketSender.h" #include "EntityItem.h" - -#include +#include "EntityTree.h" class EntityTree; class EntityTreeElement; @@ -78,6 +77,7 @@ public: EntityEditPacketSender* packetSender; }; + class EntityTreeElement : public OctreeElement, ReadWriteLockable { friend class EntityTree; // to allow createElement to new us... @@ -135,25 +135,28 @@ public: virtual bool deleteApproved() const override { return !hasEntities(); } virtual bool canPickIntersect() const override { return hasEntities(); } - virtual EntityItemID evalRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + virtual EntityItemID findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, - PickFilter searchFilter, QVariantMap& extraInfo); - virtual EntityItemID evalDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking = false); + virtual EntityItemID findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, - const QVector& entityIdsToDiscard, PickFilter searchFilter, QVariantMap& extraInfo); + const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly, + QVariantMap& extraInfo, bool precisionPicking); virtual bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, void** penetratedObject) const override; - virtual EntityItemID evalParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, + virtual EntityItemID findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, - const QVector& entityIdsToDiscard, PickFilter searchFilter, QVariantMap& extraInfo); - virtual EntityItemID evalDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, + const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly, + QVariantMap& extraInfo, bool precisionPicking = false); + virtual EntityItemID findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& normal, const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, - const QVector& entityIdsToDiscard, PickFilter searchFilter, QVariantMap& extraInfo); + const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly, + QVariantMap& extraInfo, bool precisionPicking); template void forEachEntity(F f) const { @@ -172,13 +175,28 @@ public: void addEntityItem(EntityItemPointer entity); - QUuid evalClosetEntity(const glm::vec3& position, PickFilter searchFilter, float& closestDistanceSquared) const; - void evalEntitiesInSphere(const glm::vec3& position, float radius, PickFilter searchFilter, QVector& foundEntities) const; - void evalEntitiesInSphereWithType(const glm::vec3& position, float radius, EntityTypes::EntityType type, PickFilter searchFilter, QVector& foundEntities) const; - void evalEntitiesInSphereWithName(const glm::vec3& position, float radius, const QString& name, bool caseSensitive, PickFilter searchFilter, QVector& foundEntities) const; - void evalEntitiesInCube(const AACube& cube, PickFilter searchFilter, QVector& foundEntities) const; - void evalEntitiesInBox(const AABox& box, PickFilter searchFilter, QVector& foundEntities) const; - void evalEntitiesInFrustum(const ViewFrustum& frustum, PickFilter searchFilter, QVector& foundEntities) const; + EntityItemPointer getClosestEntity(glm::vec3 position) const; + + /// finds all entities that touch a sphere + /// \param position the center of the query sphere + /// \param radius the radius of the query sphere + /// \param entities[out] vector of const EntityItemPointer + void getEntities(const glm::vec3& position, float radius, QVector& foundEntities) const; + + /// finds all entities that touch a box + /// \param box the query box + /// \param entities[out] vector of non-const EntityItemPointer + void getEntities(const AACube& cube, QVector& foundEntities); + + /// finds all entities that touch a box + /// \param box the query box + /// \param entities[out] vector of non-const EntityItemPointer + void getEntities(const AABox& box, QVector& foundEntities); + + /// finds all entities that touch a frustum + /// \param frustum the query frustum + /// \param entities[out] vector of non-const EntityItemPointer + void getEntities(const ViewFrustum& frustum, QVector& foundEntities); /// finds all entities that match filter /// \param filter function that adds matching entities to foundEntities diff --git a/libraries/pointers/src/Pick.cpp b/libraries/pointers/src/Pick.cpp index e95721c04b..7ac53a2643 100644 --- a/libraries/pointers/src/Pick.cpp +++ b/libraries/pointers/src/Pick.cpp @@ -7,6 +7,8 @@ // #include "Pick.h" +const PickFilter PickFilter::NOTHING; + int pickTypeMetaTypeId = qRegisterMetaType("PickType"); PickQuery::PickQuery(const PickFilter& filter, const float maxDistance, const bool enabled) : @@ -39,8 +41,7 @@ bool PickQuery::isEnabled() const { void PickQuery::setPrecisionPicking(bool precisionPicking) { withWriteLock([&] { - _filter.setFlag(PickFilter::PRECISE, precisionPicking); - _filter.setFlag(PickFilter::COARSE, !precisionPicking); + _filter.setFlag(PickFilter::PICK_COARSE, !precisionPicking); }); } diff --git a/libraries/pointers/src/Pick.h b/libraries/pointers/src/Pick.h index 857a72caa8..9ca0f14c5f 100644 --- a/libraries/pointers/src/Pick.h +++ b/libraries/pointers/src/Pick.h @@ -18,7 +18,6 @@ #include #include -#include enum IntersectionType { NONE = 0, @@ -28,6 +27,84 @@ enum IntersectionType { HUD }; +class PickFilter { +public: + enum FlagBit { + PICK_ENTITIES = 0, + PICK_OVERLAYS, + PICK_AVATARS, + PICK_HUD, + + PICK_COARSE, // if not set, does precise intersection, otherwise, doesn't + + PICK_INCLUDE_INVISIBLE, // if not set, will not intersect invisible elements, otherwise, intersects both visible and invisible elements + PICK_INCLUDE_NONCOLLIDABLE, // if not set, will not intersect noncollidable elements, otherwise, intersects both collidable and noncollidable elements + + // NOT YET IMPLEMENTED + PICK_ALL_INTERSECTIONS, // if not set, returns closest intersection, otherwise, returns list of all intersections + + NUM_FLAGS, // Not a valid flag + }; + typedef std::bitset Flags; + + // The key is the Flags + Flags _flags; + + PickFilter() {} + PickFilter(const Flags& flags) : _flags(flags) {} + + bool operator== (const PickFilter& rhs) const { return _flags == rhs._flags; } + bool operator!= (const PickFilter& rhs) const { return _flags != rhs._flags; } + + void setFlag(FlagBit flag, bool value) { _flags[flag] = value; } + + bool doesPickNothing() const { return _flags == NOTHING._flags; } + bool doesPickEntities() const { return _flags[PICK_ENTITIES]; } + bool doesPickOverlays() const { return _flags[PICK_OVERLAYS]; } + bool doesPickAvatars() const { return _flags[PICK_AVATARS]; } + bool doesPickHUD() const { return _flags[PICK_HUD]; } + + bool doesPickCoarse() const { return _flags[PICK_COARSE]; } + bool doesPickInvisible() const { return _flags[PICK_INCLUDE_INVISIBLE]; } + bool doesPickNonCollidable() const { return _flags[PICK_INCLUDE_NONCOLLIDABLE]; } + + bool doesWantAllIntersections() const { return _flags[PICK_ALL_INTERSECTIONS]; } + + // Helpers for RayPickManager + Flags getEntityFlags() const { + unsigned int toReturn = getBitMask(PICK_ENTITIES); + if (doesPickInvisible()) { + toReturn |= getBitMask(PICK_INCLUDE_INVISIBLE); + } + if (doesPickNonCollidable()) { + toReturn |= getBitMask(PICK_INCLUDE_NONCOLLIDABLE); + } + if (doesPickCoarse()) { + toReturn |= getBitMask(PICK_COARSE); + } + return Flags(toReturn); + } + Flags getOverlayFlags() const { + unsigned int toReturn = getBitMask(PICK_OVERLAYS); + if (doesPickInvisible()) { + toReturn |= getBitMask(PICK_INCLUDE_INVISIBLE); + } + if (doesPickNonCollidable()) { + toReturn |= getBitMask(PICK_INCLUDE_NONCOLLIDABLE); + } + if (doesPickCoarse()) { + toReturn |= getBitMask(PICK_COARSE); + } + return Flags(toReturn); + } + Flags getAvatarFlags() const { return Flags(getBitMask(PICK_AVATARS)); } + Flags getHUDFlags() const { return Flags(getBitMask(PICK_HUD)); } + + static constexpr unsigned int getBitMask(FlagBit bit) { return 1 << bit; } + + static const PickFilter NOTHING; +}; + class PickResult { public: PickResult() {} diff --git a/libraries/pointers/src/PickCacheOptimizer.h b/libraries/pointers/src/PickCacheOptimizer.h index e91283f02c..c74e84937b 100644 --- a/libraries/pointers/src/PickCacheOptimizer.h +++ b/libraries/pointers/src/PickCacheOptimizer.h @@ -85,10 +85,10 @@ QVector4D PickCacheOptimizer::update(std::unordered_mapgetMathematicalPick(); PickResultPointer res = pick->getDefaultResult(mathematicalPick.toVariantMap()); - if (!pick->isEnabled() || pick->getMaxDistance() < 0.0f || !mathematicalPick) { + if (!pick->isEnabled() || pick->getFilter().doesPickNothing() || pick->getMaxDistance() < 0.0f || !mathematicalPick) { pick->setPickResult(res); } else { - if (pick->getFilter().doesPickDomainEntities() || pick->getFilter().doesPickAvatarEntities()) { + if (pick->getFilter().doesPickEntities()) { PickCacheKey entityKey = { pick->getFilter().getEntityFlags(), pick->getIncludeItems(), pick->getIgnoreItems() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, entityKey)) { PickResultPointer entityRes = pick->getEntityIntersection(mathematicalPick); @@ -99,7 +99,7 @@ QVector4D PickCacheOptimizer::update(std::unordered_mapgetFilter().doesPickLocalEntities()) { + if (pick->getFilter().doesPickOverlays()) { PickCacheKey overlayKey = { pick->getFilter().getOverlayFlags(), pick->getIncludeItems(), pick->getIgnoreItems() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, overlayKey)) { PickResultPointer overlayRes = pick->getOverlayIntersection(mathematicalPick); diff --git a/libraries/shared/src/PickFilter.h b/libraries/shared/src/PickFilter.h deleted file mode 100644 index 96fbadf117..0000000000 --- a/libraries/shared/src/PickFilter.h +++ /dev/null @@ -1,99 +0,0 @@ -// -// Created by Sam Gondelman on 12/7/18. -// Copyright 2018 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_PickFilter_h -#define hifi_PickFilter_h - -#include - -class PickFilter { -public: - enum FlagBit { - DOMAIN_ENTITIES = 0, - AVATAR_ENTITIES, - LOCAL_ENTITIES, - AVATARS, - HUD, - - VISIBLE, - INVISIBLE, - - COLLIDABLE, - NONCOLLIDABLE, - - PRECISE, - COARSE, - - // NOT YET IMPLEMENTED - PICK_ALL_INTERSECTIONS, // if not set, returns closest intersection, otherwise, returns list of all intersections - - NUM_FLAGS, // Not a valid flag - }; - typedef std::bitset Flags; - - // The key is the Flags - Flags _flags; - - PickFilter() {} - PickFilter(const Flags& flags) : _flags(flags) {} - - bool operator==(const PickFilter& rhs) const { return _flags == rhs._flags; } - bool operator!=(const PickFilter& rhs) const { return _flags != rhs._flags; } - - void setFlag(FlagBit flag, bool value) { _flags[flag] = value; } - - // There are different groups of related flags. If none of the flags in a group are set, the search filter includes them all. - bool doesPickDomainEntities() const { return _flags[DOMAIN_ENTITIES] || !(_flags[AVATAR_ENTITIES] || _flags[LOCAL_ENTITIES] || _flags[AVATARS] || _flags[HUD]); } - bool doesPickAvatarEntities() const { return _flags[AVATAR_ENTITIES] || !(_flags[DOMAIN_ENTITIES] || _flags[LOCAL_ENTITIES] || _flags[AVATARS] || _flags[HUD]); } - bool doesPickLocalEntities() const { return _flags[LOCAL_ENTITIES] || !(_flags[DOMAIN_ENTITIES] || _flags[AVATAR_ENTITIES] || _flags[AVATARS] || _flags[HUD]); } - bool doesPickAvatars() const { return _flags[AVATARS] || !(_flags[DOMAIN_ENTITIES] || _flags[AVATAR_ENTITIES] || _flags[LOCAL_ENTITIES] || _flags[HUD]); } - bool doesPickHUD() const { return _flags[HUD] || !(_flags[DOMAIN_ENTITIES] || _flags[AVATAR_ENTITIES] || _flags[LOCAL_ENTITIES] || _flags[AVATARS]); } - - bool doesPickVisible() const { return _flags[VISIBLE] || !_flags[INVISIBLE]; } - bool doesPickInvisible() const { return _flags[INVISIBLE] || !_flags[VISIBLE]; } - - bool doesPickCollidable() const { return _flags[COLLIDABLE] || !_flags[NONCOLLIDABLE]; } - bool doesPickNonCollidable() const { return _flags[NONCOLLIDABLE] || !_flags[COLLIDABLE]; } - - bool isPrecise() const { return _flags[PRECISE] || !_flags[COARSE]; } - bool isCoarse() const { return _flags[COARSE] || !_flags[PRECISE]; } - - bool doesWantAllIntersections() const { return _flags[PICK_ALL_INTERSECTIONS]; } - - // Helpers for RayPickManager - Flags getEntityFlags() const { - unsigned int toReturn = 0; - for (int i = DOMAIN_ENTITIES; i < LOCAL_ENTITIES; i++) { - if (_flags[i]) { - toReturn |= getBitMask(FlagBit(i)); - } - } - for (int i = HUD + 1; i < NUM_FLAGS; i++) { - if (_flags[i]) { - toReturn |= getBitMask(FlagBit(i)); - } - } - return Flags(toReturn); - } - Flags getOverlayFlags() const { - unsigned int toReturn = getBitMask(LOCAL_ENTITIES); - for (int i = HUD + 1; i < NUM_FLAGS; i++) { - if (_flags[i]) { - toReturn |= getBitMask(FlagBit(i)); - } - } - return Flags(toReturn); - } - Flags getAvatarFlags() const { return Flags(getBitMask(AVATARS)); } - Flags getHUDFlags() const { return Flags(getBitMask(HUD)); } - - static constexpr unsigned int getBitMask(FlagBit bit) { return 1 << bit; } -}; - -#endif // hifi_PickFilter_h - diff --git a/scripts/developer/utilities/tests/entityPerfTest.js b/scripts/developer/utilities/tests/entityPerfTest.js index d3846baba9..181afcf360 100644 --- a/scripts/developer/utilities/tests/entityPerfTest.js +++ b/scripts/developer/utilities/tests/entityPerfTest.js @@ -87,6 +87,14 @@ function makeEntity () { .withRay(function(){ Entities.findRayIntersection(this.ray, false); }); + entityTests.addTestCase('findRayIntersectionBlocking, precisionPicking=true') + .withRay(function(){ + Entities.findRayIntersectionBlocking(this.ray, true); + }) + entityTests.addTestCase('findRayIntersectionBlocking, precisionPicking=false') + .withRay(function(){ + Entities.findRayIntersectionBlocking(this.ray, false); + }) entityTests.addTestCase('no-op') .run(function(){}); diff --git a/unpublishedScripts/DomainContent/Toybox/pistol/pistol.js b/unpublishedScripts/DomainContent/Toybox/pistol/pistol.js index 038c53054c..b408e4f464 100644 --- a/unpublishedScripts/DomainContent/Toybox/pistol/pistol.js +++ b/unpublishedScripts/DomainContent/Toybox/pistol/pistol.js @@ -137,7 +137,7 @@ direction: this.firingDirection }; this.createGunFireEffect(this.barrelPoint) - var intersection = Entities.findRayIntersection(pickRay, true); + var intersection = Entities.findRayIntersectionBlocking(pickRay, true); if (intersection.intersects) { this.createEntityHitEffect(intersection.intersection); if (Math.random() < this.playRichochetSoundChance) {