From ea501331464bf10ebfeaf85f75e7cad263cdb8c1 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 18 Mar 2019 12:05:17 -0700 Subject: [PATCH 01/28] working on adding particle shape types --- .../src/RenderableModelEntityItem.cpp | 4 - .../RenderableParticleEffectEntityItem.cpp | 117 ++++++++++++------ .../src/RenderableParticleEffectEntityItem.h | 10 +- .../entities/src/EntityItemProperties.cpp | 26 ++-- .../entities/src/ParticleEffectEntityItem.cpp | 23 ++++ .../entities/src/ParticleEffectEntityItem.h | 9 +- libraries/entities/src/ZoneEntityItem.cpp | 4 - 7 files changed, 138 insertions(+), 55 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 643e5afb70..2fdffde8a3 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -307,10 +307,6 @@ void RenderableModelEntityItem::setShapeType(ShapeType type) { } void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) { - // because the caching system only allows one Geometry per url, and because this url might also be used - // as a visual model, we need to change this url in some way. We add a "collision-hull" query-arg so it - // will end up in a different hash-key in ResourceCache. TODO: It would be better to use the same URL and - // parse it twice. auto currentCompoundShapeURL = getCompoundShapeURL(); ModelEntityItem::setCompoundShapeURL(url); if (getCompoundShapeURL() != currentCompoundShapeURL || !getModel()) { diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index c139fbf320..2168347554 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -9,13 +9,11 @@ // #include "RenderableParticleEffectEntityItem.h" - #include #include #include - using namespace render; using namespace render::entities; @@ -79,6 +77,14 @@ bool ParticleEffectEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedE return true; } + if (_shapeType != entity->getShapeType()) { + return true; + } + + if (_compoundShapeURL != entity->getCompoundShapeURL()) { + return true; + } + return false; } @@ -87,11 +93,17 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi if (!newParticleProperties.valid()) { qCWarning(entitiesrenderer) << "Bad particle properties"; } - - if (resultWithReadLock([&]{ return _particleProperties != newParticleProperties; })) { + + if (resultWithReadLock([&] { return _particleProperties != newParticleProperties; })) { _timeUntilNextEmit = 0; - withWriteLock([&]{ + withWriteLock([&] { _particleProperties = newParticleProperties; + _shapeType = entity->getShapeType(); + QString compoundShapeURL = entity->getCompoundShapeURL(); + if (_compoundShapeURL != compoundShapeURL) { + _compoundShapeURL = compoundShapeURL; + fetchGeometryResource(); + } if (!_prevEmitterShouldTrailInitialized) { _prevEmitterShouldTrailInitialized = true; _prevEmitterShouldTrail = _particleProperties.emission.shouldTrail; @@ -104,10 +116,10 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi }); _emitting = entity->getIsEmitting(); - bool textureEmpty = resultWithReadLock([&]{ return _particleProperties.textures.isEmpty(); }); + bool textureEmpty = resultWithReadLock([&] { return _particleProperties.textures.isEmpty(); }); if (textureEmpty) { if (_networkTexture) { - withWriteLock([&] { + withWriteLock([&] { _networkTexture.reset(); }); } @@ -116,11 +128,11 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi entity->setVisuallyReady(true); }); } else { - bool textureNeedsUpdate = resultWithReadLock([&]{ + bool textureNeedsUpdate = resultWithReadLock([&] { return !_networkTexture || _networkTexture->getURL() != QUrl(_particleProperties.textures); }); if (textureNeedsUpdate) { - withWriteLock([&] { + withWriteLock([&] { _networkTexture = DependencyManager::get()->getTexture(_particleProperties.textures); }); } @@ -144,7 +156,7 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { // Fill in Uniforms structure ParticleUniforms particleUniforms; - withReadLock([&]{ + withReadLock([&] { particleUniforms.radius.start = _particleProperties.radius.range.start; particleUniforms.radius.middle = _particleProperties.radius.gradient.target; particleUniforms.radius.finish = _particleProperties.radius.range.finish; @@ -183,7 +195,8 @@ Item::Bound ParticleEffectEntityRenderer::getBound() { static const size_t VERTEX_PER_PARTICLE = 4; -ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties) { +ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties, + const ShapeType& shapeType, const GeometryResource::Pointer& geometryResource) { CpuParticle particle; const auto& accelerationSpread = particleProperties.emission.acceleration.spread; @@ -221,33 +234,53 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa float azimuth; if (azimuthFinish >= azimuthStart) { - azimuth = azimuthStart + (azimuthFinish - azimuthStart) * randFloat(); + azimuth = azimuthStart + (azimuthFinish - azimuthStart) * randFloat(); } else { azimuth = azimuthStart + (TWO_PI + azimuthFinish - azimuthStart) * randFloat(); } - if (emitDimensions == Vectors::ZERO) { + if (emitDimensions == Vectors::ZERO || shapeType == ShapeType::SHAPE_TYPE_NONE) { // Point emitDirection = glm::quat(glm::vec3(PI_OVER_TWO - elevation, 0.0f, azimuth)) * Vectors::UNIT_Z; } else { - // Ellipsoid - float radiusScale = 1.0f; - if (emitRadiusStart < 1.0f) { - float randRadius = - emitRadiusStart + randFloatInRange(0.0f, particle::MAXIMUM_EMIT_RADIUS_START - emitRadiusStart); - radiusScale = 1.0f - std::pow(1.0f - randRadius, 3.0f); + glm::vec3 emitPosition; + switch (shapeType) { + case ShapeType::SHAPE_TYPE_BOX: + + case ShapeType::SHAPE_TYPE_CAPSULE_X: + case ShapeType::SHAPE_TYPE_CAPSULE_Y: + case ShapeType::SHAPE_TYPE_CAPSULE_Z: + + case ShapeType::SHAPE_TYPE_CYLINDER_X: + case ShapeType::SHAPE_TYPE_CYLINDER_Y: + case ShapeType::SHAPE_TYPE_CYLINDER_Z: + + case ShapeType::SHAPE_TYPE_CIRCLE: + case ShapeType::SHAPE_TYPE_PLANE: + + case ShapeType::SHAPE_TYPE_COMPOUND: + + case ShapeType::SHAPE_TYPE_SPHERE: + case ShapeType::SHAPE_TYPE_ELLIPSOID: + default: { + float radiusScale = 1.0f; + if (emitRadiusStart < 1.0f) { + float randRadius = + emitRadiusStart + randFloatInRange(0.0f, particle::MAXIMUM_EMIT_RADIUS_START - emitRadiusStart); + radiusScale = 1.0f - std::pow(1.0f - randRadius, 3.0f); + } + + glm::vec3 radii = radiusScale * 0.5f * emitDimensions; + float x = radii.x * glm::cos(elevation) * glm::cos(azimuth); + float y = radii.y * glm::cos(elevation) * glm::sin(azimuth); + float z = radii.z * glm::sin(elevation); + emitPosition = glm::vec3(x, y, z); + emitDirection = glm::normalize(glm::vec3(radii.x > 0.0f ? x / (radii.x * radii.x) : 0.0f, + radii.y > 0.0f ? y / (radii.y * radii.y) : 0.0f, + radii.z > 0.0f ? z / (radii.z * radii.z) : 0.0f)); + } } - glm::vec3 radii = radiusScale * 0.5f * emitDimensions; - float x = radii.x * glm::cos(elevation) * glm::cos(azimuth); - float y = radii.y * glm::cos(elevation) * glm::sin(azimuth); - float z = radii.z * glm::sin(elevation); - glm::vec3 emitPosition = glm::vec3(x, y, z); - emitDirection = glm::normalize(glm::vec3( - radii.x > 0.0f ? x / (radii.x * radii.x) : 0.0f, - radii.y > 0.0f ? y / (radii.y * radii.y) : 0.0f, - radii.z > 0.0f ? z / (radii.z * radii.z) : 0.0f - )); particle.relativePosition += emitOrientation * emitPosition; } } @@ -267,20 +300,25 @@ void ParticleEffectEntityRenderer::stepSimulation() { const auto now = usecTimestampNow(); const auto interval = std::min(USECS_PER_SECOND / 60, now - _lastSimulated); _lastSimulated = now; - + particle::Properties particleProperties; - withReadLock([&]{ + ShapeType shapeType; + GeometryResource::Pointer geometryResource; + withReadLock([&] { particleProperties = _particleProperties; + shapeType = _shapeType; + geometryResource = _geometryResource; }); const auto& modelTransform = getModelTransform(); - if (_emitting && particleProperties.emitting()) { + if (_emitting && particleProperties.emitting() && + (_shapeType != ShapeType::SHAPE_TYPE_COMPOUND || (_geometryResource && _geometryResource->isLoaded()))) { uint64_t emitInterval = particleProperties.emitIntervalUsecs(); if (emitInterval > 0 && interval >= _timeUntilNextEmit) { auto timeRemaining = interval; while (timeRemaining > _timeUntilNextEmit) { // emit particle - _cpuParticles.push_back(createParticle(now, modelTransform, particleProperties)); + _cpuParticles.push_back(createParticle(now, modelTransform, particleProperties, shapeType, geometryResource)); _timeUntilNextEmit = emitInterval; if (emitInterval < timeRemaining) { timeRemaining -= emitInterval; @@ -297,7 +335,7 @@ void ParticleEffectEntityRenderer::stepSimulation() { } const float deltaTime = (float)interval / (float)USECS_PER_SECOND; - // update the particles + // update the particles for (auto& particle : _cpuParticles) { if (_prevEmitterShouldTrail != particleProperties.emission.shouldTrail) { if (_prevEmitterShouldTrail) { @@ -313,7 +351,7 @@ void ParticleEffectEntityRenderer::stepSimulation() { static GpuParticles gpuParticles; gpuParticles.clear(); gpuParticles.reserve(_cpuParticles.size()); // Reserve space - std::transform(_cpuParticles.begin(), _cpuParticles.end(), std::back_inserter(gpuParticles), [&particleProperties, &modelTransform](const CpuParticle& particle) { + std::transform(_cpuParticles.begin(), _cpuParticles.end(), std::back_inserter(gpuParticles), [&particleProperties, &modelTransform] (const CpuParticle& particle) { glm::vec3 position = particle.relativePosition + (particleProperties.emission.shouldTrail ? particle.basePosition : modelTransform.getTranslation()); return GpuParticle(position, glm::vec2(particle.lifetime, particle.seed)); }); @@ -358,3 +396,12 @@ void ParticleEffectEntityRenderer::doRender(RenderArgs* args) { auto numParticles = _particleBuffer->getSize() / sizeof(GpuParticle); batch.drawInstanced((gpu::uint32)numParticles, gpu::TRIANGLE_STRIP, (gpu::uint32)VERTEX_PER_PARTICLE); } + +void ParticleEffectEntityRenderer::fetchGeometryResource() { + QUrl hullURL(_compoundShapeURL); + if (hullURL.isEmpty()) { + _geometryResource.reset(); + } else { + _geometryResource = DependencyManager::get()->getCollisionGeometryResource(hullURL); + } +} \ No newline at end of file diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h index 853d5cac29..4a4e5e5cbc 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -81,7 +81,8 @@ private: glm::vec2 spare; }; - static CpuParticle createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties); + static CpuParticle createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties, + const ShapeType& shapeType, const GeometryResource::Pointer& geometryResource); void stepSimulation(); particle::Properties _particleProperties; @@ -90,11 +91,16 @@ private: CpuParticles _cpuParticles; bool _emitting { false }; uint64_t _timeUntilNextEmit { 0 }; - BufferPointer _particleBuffer{ std::make_shared() }; + BufferPointer _particleBuffer { std::make_shared() }; BufferView _uniformBuffer; quint64 _lastSimulated { 0 }; PulsePropertyGroup _pulseProperties; + ShapeType _shapeType; + QString _compoundShapeURL; + + void fetchGeometryResource(); + GeometryResource::Pointer _geometryResource; NetworkTexturePointer _networkTexture; ScenePointer _scene; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 3efedf02ec..0a6875b63d 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -1114,23 +1114,28 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * default, particles emit along the entity's local z-axis, and azimuthStart and azimuthFinish * are relative to the entity's local x-axis. The default value is a rotation of -90 degrees about the local x-axis, i.e., * the particles emit vertically. - * @property {Vec3} emitDimensions=0,0,0 - The dimensions of the ellipsoid from which particles are emitted. - * @property {number} emitRadiusStart=1 - The starting radius within the ellipsoid at which particles start being emitted; - * range 0.01.0 for the ellipsoid center to the ellipsoid surface, respectively. - * Particles are emitted from the portion of the ellipsoid that lies between emitRadiusStart and the - * ellipsoid's surface. + * @property {Vec3} emitDimensions=0,0,0 - The dimensions of the shape from which particles are emitted. The shape is specified with + * shapeType. + * @property {number} emitRadiusStart=1 - The starting radius within the shape at which particles start being emitted; + * range 0.01.0 for the center to the surface, respectively. + * Particles are emitted from the portion of the shape that lies between emitRadiusStart and the + * shape's surface. * @property {number} polarStart=0 - The angle in radians from the entity's local z-axis at which particles start being emitted * within the ellipsoid; range 0Math.PI. Particles are emitted from the portion of the - * ellipsoid that lies between polarStart and polarFinish. + * ellipsoid that lies between polarStart and polarFinish. Only used if shapeType is + * ellipsoid. * @property {number} polarFinish=0 - The angle in radians from the entity's local z-axis at which particles stop being emitted * within the ellipsoid; range 0Math.PI. Particles are emitted from the portion of the - * ellipsoid that lies between polarStart and polarFinish. + * ellipsoid that lies between polarStart and polarFinish. Only used if shapeType is + * ellipsoid. * @property {number} azimuthStart=-Math.PI - The angle in radians from the entity's local x-axis about the entity's local * z-axis at which particles start being emitted; range -Math.PIMath.PI. Particles are * emitted from the portion of the ellipsoid that lies between azimuthStart and azimuthFinish. + * Only used if shapeType is ellipsoid. * @property {number} azimuthFinish=Math.PI - The angle in radians from the entity's local x-axis about the entity's local * z-axis at which particles stop being emitted; range -Math.PIMath.PI. Particles are * emitted from the portion of the ellipsoid that lies between azimuthStart and azimuthFinish. + * Only used if shapeType is ellipsoid. * * @property {string} textures="" - The URL of a JPG or PNG image file to display for each particle. If you want transparency, * use PNG format. @@ -1170,7 +1175,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * up in the world. If true, they will point towards the entity's up vector, based on its orientation. * @property {Entities.Pulse} pulse - The pulse-related properties. Deprecated. * - * @property {ShapeType} shapeType="none" - Currently not used. Read-only. + * @property {ShapeType} shapeType="ellipsoid" - The shape of the collision hull used if collisions are enabled. + * @property {string} compoundShapeURL="" - The model file to use for the compound shape if shapeType is + * "compound". * * @example Create a ball of green smoke. * particles = Entities.addEntity({ @@ -1658,6 +1665,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool // Particles only if (_type == EntityTypes::ParticleEffect) { COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, getShapeTypeAsString()); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha); _pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); @@ -3104,6 +3112,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy if (properties.getType() == EntityTypes::ParticleEffect) { APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)(properties.getShapeType())); + APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, properties.getCompoundShapeURL()); APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha()); _staticPulse.setProperties(properties); @@ -3584,6 +3593,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int if (properties.getType() == EntityTypes::ParticleEffect) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha); properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index b916ecc3de..801cf56b32 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -410,6 +410,7 @@ EntityItemProperties ParticleEffectEntityItem::getProperties(const EntityPropert EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha); withReadLock([&] { @@ -464,6 +465,7 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha); withWriteLock([&] { @@ -540,6 +542,7 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch const unsigned char* dataAt = data; READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType); + READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); READ_ENTITY_PROPERTY(PROP_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha); withWriteLock([&] { @@ -598,6 +601,7 @@ EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstrea EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); requestedProperties += PROP_SHAPE_TYPE; + requestedProperties += PROP_COMPOUND_SHAPE_URL; requestedProperties += PROP_COLOR; requestedProperties += PROP_ALPHA; requestedProperties += _pulseProperties.getEntityProperties(params); @@ -656,6 +660,7 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, bool successPropertyFits = true; APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)getShapeType()); + APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, getCompoundShapeURL()); APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor()); APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); withReadLock([&] { @@ -726,6 +731,24 @@ void ParticleEffectEntityItem::setShapeType(ShapeType type) { }); } +ShapeType ParticleEffectEntityItem::getShapeType() const { + return resultWithReadLock([&] { + return _shapeType; + }); +} + +void ParticleEffectEntityItem::setCompoundShapeURL(const QString& compoundShapeURL) { + withWriteLock([&] { + _compoundShapeURL = compoundShapeURL; + }); +} + +QString ParticleEffectEntityItem::getCompoundShapeURL() const { + return resultWithReadLock([&] { + return _compoundShapeURL; + }); +} + void ParticleEffectEntityItem::setMaxParticles(quint32 maxParticles) { withWriteLock([&] { _particleProperties.maxParticles = glm::clamp(maxParticles, MINIMUM_MAX_PARTICLES, MAXIMUM_MAX_PARTICLES); diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index 0755d7868b..52f229201e 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -79,6 +79,7 @@ namespace particle { static const QString DEFAULT_TEXTURES = ""; static const bool DEFAULT_EMITTER_SHOULD_TRAIL = false; static const bool DEFAULT_ROTATE_WITH_ENTITY = false; + static const ShapeType DEFAULT_SHAPE_TYPE = ShapeType::SHAPE_TYPE_ELLIPSOID; template struct Range { @@ -255,7 +256,10 @@ public: float getAlphaSpread() const { return _particleProperties.alpha.gradient.spread; } void setShapeType(ShapeType type) override; - virtual ShapeType getShapeType() const override { return _shapeType; } + virtual ShapeType getShapeType() const override; + + QString getCompoundShapeURL() const; + virtual void setCompoundShapeURL(const QString& url); virtual void debugDump() const override; @@ -349,7 +353,8 @@ protected: PulsePropertyGroup _pulseProperties; bool _isEmitting { true }; - ShapeType _shapeType { SHAPE_TYPE_NONE }; + ShapeType _shapeType{ particle::DEFAULT_SHAPE_TYPE }; + QString _compoundShapeURL { "" }; }; #endif // hifi_ParticleEffectEntityItem_h diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index 98b18869fc..f243d59da0 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -13,7 +13,6 @@ #include #include -#include #include @@ -463,9 +462,6 @@ void ZoneEntityItem::fetchCollisionGeometryResource() { if (hullURL.isEmpty()) { _shapeResource.reset(); } else { - QUrlQuery queryArgs(hullURL); - queryArgs.addQueryItem("collision-hull", ""); - hullURL.setQuery(queryArgs); _shapeResource = DependencyManager::get()->getCollisionGeometryResource(hullURL); } } From 432a3f1610d0ccbe0004a0ab2fa14a3880727c56 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 19 Mar 2019 12:59:03 -0700 Subject: [PATCH 02/28] uniform sampling of ellipsoid points --- .../src/RenderableParticleEffectEntityItem.cpp | 9 +++++---- libraries/entities/src/ParticleEffectEntityItem.cpp | 13 +++++++++++++ libraries/networking/src/udt/PacketHeaders.h | 1 + 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 2168347554..df393e691c 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -239,7 +239,7 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa azimuth = azimuthStart + (TWO_PI + azimuthFinish - azimuthStart) * randFloat(); } - if (emitDimensions == Vectors::ZERO || shapeType == ShapeType::SHAPE_TYPE_NONE) { + if (emitDimensions == Vectors::ZERO) { // Point emitDirection = glm::quat(glm::vec3(PI_OVER_TWO - elevation, 0.0f, azimuth)) * Vectors::UNIT_Z; } else { @@ -265,9 +265,10 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa default: { float radiusScale = 1.0f; if (emitRadiusStart < 1.0f) { - float randRadius = - emitRadiusStart + randFloatInRange(0.0f, particle::MAXIMUM_EMIT_RADIUS_START - emitRadiusStart); - radiusScale = 1.0f - std::pow(1.0f - randRadius, 3.0f); + float innerRadiusCubed = emitRadiusStart * emitRadiusStart * emitRadiusStart; + float outerRadiusCubed = 1.0f; // pow(particle::MAXIMUM_EMIT_RADIUS_START, 3); + float randRadiusCubed = randFloatInRange(innerRadiusCubed, outerRadiusCubed); + radiusScale = std::cbrt(randRadiusCubed); } glm::vec3 radii = radiusScale * 0.5f * emitDimensions; diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 801cf56b32..b9f723d880 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -723,6 +723,19 @@ void ParticleEffectEntityItem::debugDump() const { } void ParticleEffectEntityItem::setShapeType(ShapeType type) { + switch (type) { + case SHAPE_TYPE_NONE: + case SHAPE_TYPE_HULL: + case SHAPE_TYPE_SIMPLE_HULL: + case SHAPE_TYPE_SIMPLE_COMPOUND: + case SHAPE_TYPE_STATIC_MESH: + // these types are unsupported for ParticleEffectEntity + type = particle::DEFAULT_SHAPE_TYPE; + break; + default: + break; + } + withWriteLock([&] { if (type != _shapeType) { _shapeType = type; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 0ec7c40ca4..48aecbc791 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -266,6 +266,7 @@ enum class EntityVersion : PacketVersion { ModelScale, ReOrderParentIDProperties, CertificateTypeProperty, + ParticleShapeType, // Add new versions above here NUM_PACKET_TYPE, From 194008c77a745e56b50128474a3baadebd231cd3 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 19 Mar 2019 14:14:37 -0700 Subject: [PATCH 03/28] box shapeType --- .../RenderableParticleEffectEntityItem.cpp | 83 ++++++++++++------- .../entities/src/ParticleEffectEntityItem.cpp | 3 + 2 files changed, 57 insertions(+), 29 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index df393e691c..1d384cc3b5 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -98,12 +98,6 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi _timeUntilNextEmit = 0; withWriteLock([&] { _particleProperties = newParticleProperties; - _shapeType = entity->getShapeType(); - QString compoundShapeURL = entity->getCompoundShapeURL(); - if (_compoundShapeURL != compoundShapeURL) { - _compoundShapeURL = compoundShapeURL; - fetchGeometryResource(); - } if (!_prevEmitterShouldTrailInitialized) { _prevEmitterShouldTrailInitialized = true; _prevEmitterShouldTrail = _particleProperties.emission.shouldTrail; @@ -113,6 +107,12 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi withWriteLock([&] { _pulseProperties = entity->getPulseProperties(); + _shapeType = entity->getShapeType(); + QString compoundShapeURL = entity->getCompoundShapeURL(); + if (_compoundShapeURL != compoundShapeURL) { + _compoundShapeURL = compoundShapeURL; + fetchGeometryResource(); + } }); _emitting = entity->getIsEmitting(); @@ -193,7 +193,28 @@ Item::Bound ParticleEffectEntityRenderer::getBound() { return _bound; } -static const size_t VERTEX_PER_PARTICLE = 4; +// FIXME: these methods assume uniform emitDimensions, need to importance sample based on dimensions +float importanceSample2Dimension(float startDim) { + float dimension = 1.0f; + if (startDim < 1.0f) { + float innerDimensionSquared = startDim * startDim; + float outerDimensionSquared = 1.0f; // pow(particle::MAXIMUM_EMIT_RADIUS_START, 2); + float randDimensionSquared = randFloatInRange(innerDimensionSquared, outerDimensionSquared); + dimension = std::cbrt(randDimensionSquared); + } + return dimension; +} + +float importanceSample3DDimension(float startDim) { + float dimension = 1.0f; + if (startDim < 1.0f) { + float innerDimensionCubed = startDim * startDim * startDim; + float outerDimensionCubed = 1.0f; // pow(particle::MAXIMUM_EMIT_RADIUS_START, 3); + float randDimensionCubed = randFloatInRange(innerDimensionCubed, outerDimensionCubed); + dimension = std::cbrt(randDimensionCubed); + } + return dimension; +} ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties, const ShapeType& shapeType, const GeometryResource::Pointer& geometryResource) { @@ -245,33 +266,35 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa } else { glm::vec3 emitPosition; switch (shapeType) { - case ShapeType::SHAPE_TYPE_BOX: + case SHAPE_TYPE_BOX: { + glm::vec3 dim = importanceSample3DDimension(emitRadiusStart) * 0.5f * emitDimensions; - case ShapeType::SHAPE_TYPE_CAPSULE_X: - case ShapeType::SHAPE_TYPE_CAPSULE_Y: - case ShapeType::SHAPE_TYPE_CAPSULE_Z: + int side = randIntInRange(0, 5); + int axis = side % 3; + float direction = side > 2 ? 1.0f : -1.0f; - case ShapeType::SHAPE_TYPE_CYLINDER_X: - case ShapeType::SHAPE_TYPE_CYLINDER_Y: - case ShapeType::SHAPE_TYPE_CYLINDER_Z: + emitDirection[axis] = direction; + emitPosition[axis] = direction * dim[axis]; + axis = (axis + 1) % 3; + emitPosition[axis] = dim[axis] * randFloatInRange(-1.0f, 1.0f); + axis = (axis + 1) % 3; + emitPosition[axis] = dim[axis] * randFloatInRange(-1.0f, 1.0f); + break; + } - case ShapeType::SHAPE_TYPE_CIRCLE: - case ShapeType::SHAPE_TYPE_PLANE: + case SHAPE_TYPE_CYLINDER_X: + case SHAPE_TYPE_CYLINDER_Y: + case SHAPE_TYPE_CYLINDER_Z: - case ShapeType::SHAPE_TYPE_COMPOUND: + case SHAPE_TYPE_CIRCLE: + case SHAPE_TYPE_PLANE: - case ShapeType::SHAPE_TYPE_SPHERE: - case ShapeType::SHAPE_TYPE_ELLIPSOID: + case SHAPE_TYPE_COMPOUND: + + case SHAPE_TYPE_SPHERE: + case SHAPE_TYPE_ELLIPSOID: default: { - float radiusScale = 1.0f; - if (emitRadiusStart < 1.0f) { - float innerRadiusCubed = emitRadiusStart * emitRadiusStart * emitRadiusStart; - float outerRadiusCubed = 1.0f; // pow(particle::MAXIMUM_EMIT_RADIUS_START, 3); - float randRadiusCubed = randFloatInRange(innerRadiusCubed, outerRadiusCubed); - radiusScale = std::cbrt(randRadiusCubed); - } - - glm::vec3 radii = radiusScale * 0.5f * emitDimensions; + glm::vec3 radii = importanceSample3DDimension(emitRadiusStart) * 0.5f * emitDimensions; float x = radii.x * glm::cos(elevation) * glm::cos(azimuth); float y = radii.y * glm::cos(elevation) * glm::sin(azimuth); float z = radii.z * glm::sin(elevation); @@ -279,6 +302,7 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa emitDirection = glm::normalize(glm::vec3(radii.x > 0.0f ? x / (radii.x * radii.x) : 0.0f, radii.y > 0.0f ? y / (radii.y * radii.y) : 0.0f, radii.z > 0.0f ? z / (radii.z * radii.z) : 0.0f)); + break; } } @@ -313,7 +337,7 @@ void ParticleEffectEntityRenderer::stepSimulation() { const auto& modelTransform = getModelTransform(); if (_emitting && particleProperties.emitting() && - (_shapeType != ShapeType::SHAPE_TYPE_COMPOUND || (_geometryResource && _geometryResource->isLoaded()))) { + (_shapeType != SHAPE_TYPE_COMPOUND || (_geometryResource && _geometryResource->isLoaded()))) { uint64_t emitInterval = particleProperties.emitIntervalUsecs(); if (emitInterval > 0 && interval >= _timeUntilNextEmit) { auto timeRemaining = interval; @@ -395,6 +419,7 @@ void ParticleEffectEntityRenderer::doRender(RenderArgs* args) { batch.setInputBuffer(0, _particleBuffer, 0, sizeof(GpuParticle)); auto numParticles = _particleBuffer->getSize() / sizeof(GpuParticle); + static const size_t VERTEX_PER_PARTICLE = 4; batch.drawInstanced((gpu::uint32)numParticles, gpu::TRIANGLE_STRIP, (gpu::uint32)VERTEX_PER_PARTICLE); } diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index b9f723d880..5f285ca91b 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -725,6 +725,9 @@ void ParticleEffectEntityItem::debugDump() const { void ParticleEffectEntityItem::setShapeType(ShapeType type) { switch (type) { case SHAPE_TYPE_NONE: + case SHAPE_TYPE_CAPSULE_X: + case SHAPE_TYPE_CAPSULE_Y: + case SHAPE_TYPE_CAPSULE_Z: case SHAPE_TYPE_HULL: case SHAPE_TYPE_SIMPLE_HULL: case SHAPE_TYPE_SIMPLE_COMPOUND: From 38864e5b6e581917cb1f172f39f6fde36ddcfd77 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 19 Mar 2019 18:04:32 -0700 Subject: [PATCH 04/28] plane, circle, cylinders --- .../RenderableParticleEffectEntityItem.cpp | 47 +++++++++++++++++-- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 1d384cc3b5..8883108f75 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -194,13 +194,13 @@ Item::Bound ParticleEffectEntityRenderer::getBound() { } // FIXME: these methods assume uniform emitDimensions, need to importance sample based on dimensions -float importanceSample2Dimension(float startDim) { +float importanceSample2DDimension(float startDim) { float dimension = 1.0f; if (startDim < 1.0f) { float innerDimensionSquared = startDim * startDim; float outerDimensionSquared = 1.0f; // pow(particle::MAXIMUM_EMIT_RADIUS_START, 2); float randDimensionSquared = randFloatInRange(innerDimensionSquared, outerDimensionSquared); - dimension = std::cbrt(randDimensionSquared); + dimension = std::sqrt(randDimensionSquared); } return dimension; } @@ -259,6 +259,7 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa } else { azimuth = azimuthStart + (TWO_PI + azimuthFinish - azimuthStart) * randFloat(); } + // TODO: azimuth and elevation are only used for ellipsoids, but could be used for other shapes too if (emitDimensions == Vectors::ZERO) { // Point @@ -284,10 +285,46 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa case SHAPE_TYPE_CYLINDER_X: case SHAPE_TYPE_CYLINDER_Y: - case SHAPE_TYPE_CYLINDER_Z: + case SHAPE_TYPE_CYLINDER_Z: { + glm::vec3 radii = importanceSample2DDimension(emitRadiusStart) * 0.5f * emitDimensions; + int axis = shapeType - SHAPE_TYPE_CYLINDER_X; - case SHAPE_TYPE_CIRCLE: - case SHAPE_TYPE_PLANE: + emitPosition[axis] = emitDimensions[axis] * randFloatInRange(-0.5f, 0.5f); + emitDirection[axis] = 0.0f; + axis = (axis + 1) % 3; + emitPosition[axis] = radii[axis] * glm::cos(azimuth); + emitDirection[axis] = radii[axis] > 0.0f ? emitPosition[axis] / (radii[axis] * radii[axis]) : 0.0f; + axis = (axis + 1) % 3; + emitPosition[axis] = radii[axis] * glm::sin(azimuth); + emitDirection[axis] = radii[axis] > 0.0f ? emitPosition[axis] / (radii[axis] * radii[axis]) : 0.0f; + emitDirection = glm::normalize(emitDirection); + break; + } + + case SHAPE_TYPE_CIRCLE: { // FIXME: SHAPE_TYPE_CIRCLE is not exposed to scripts in buildStringToShapeTypeLookup() + glm::vec2 radii = importanceSample2DDimension(emitRadiusStart) * 0.5f * glm::vec2(emitDimensions.x, emitDimensions.z); + float x = radii.x * glm::cos(azimuth); + float z = radii.y * glm::sin(azimuth); + emitPosition = glm::vec3(x, 0.0f, z); + emitDirection = Vectors::UP; + break; + } + case SHAPE_TYPE_PLANE: { + glm::vec2 dim = importanceSample2DDimension(emitRadiusStart) * 0.5f * glm::vec2(emitDimensions.x, emitDimensions.z); + + int side = randIntInRange(0, 3); + int axis = side % 2; + float direction = side > 1 ? 1.0f : -1.0f; + + glm::vec2 pos; + pos[axis] = direction * dim[axis]; + axis = (axis + 1) % 2; + pos[axis] = dim[axis] * randFloatInRange(-1.0f, 1.0f); + + emitPosition = glm::vec3(pos.x, 0.0f, pos.y); + emitDirection = Vectors::UP; + break; + } case SHAPE_TYPE_COMPOUND: From 3ff0770441a2ce24063063eff9b07264715bc5f9 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 20 Mar 2019 21:14:54 -0700 Subject: [PATCH 05/28] model emitters! --- .../RenderableParticleEffectEntityItem.cpp | 173 +++++++++++++++++- .../src/RenderableParticleEffectEntityItem.h | 12 +- .../entities/src/EntityItemProperties.cpp | 21 ++- libraries/entities/src/LineEntityItem.h | 2 - libraries/entities/src/ModelEntityItem.h | 2 +- libraries/entities/src/ShapeEntityItem.h | 2 +- libraries/entities/src/ZoneEntityItem.cpp | 2 +- libraries/entities/src/ZoneEntityItem.h | 2 +- libraries/shared/src/GeometryUtil.cpp | 6 + libraries/shared/src/GeometryUtil.h | 1 + .../system/assets/data/createAppTooltips.json | 8 + scripts/system/html/js/entityProperties.js | 15 ++ 12 files changed, 222 insertions(+), 24 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 8883108f75..d517ecd026 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -1,4 +1,4 @@ -// +// // RenderableParticleEffectEntityItem.cpp // interface/src // @@ -14,6 +14,8 @@ #include #include +#include + using namespace render; using namespace render::entities; @@ -111,6 +113,7 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi QString compoundShapeURL = entity->getCompoundShapeURL(); if (_compoundShapeURL != compoundShapeURL) { _compoundShapeURL = compoundShapeURL; + _hasComputedTriangles = false; fetchGeometryResource(); } }); @@ -217,7 +220,8 @@ float importanceSample3DDimension(float startDim) { } ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties, - const ShapeType& shapeType, const GeometryResource::Pointer& geometryResource) { + const ShapeType& shapeType, const GeometryResource::Pointer& geometryResource, + const TriangleInfo& triangleInfo) { CpuParticle particle; const auto& accelerationSpread = particleProperties.emission.acceleration.spread; @@ -259,7 +263,7 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa } else { azimuth = azimuthStart + (TWO_PI + azimuthFinish - azimuthStart) * randFloat(); } - // TODO: azimuth and elevation are only used for ellipsoids, but could be used for other shapes too + // TODO: azimuth and elevation are only used for ellipsoids/circles, but could be used for other shapes too if (emitDimensions == Vectors::ZERO) { // Point @@ -301,7 +305,7 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa break; } - case SHAPE_TYPE_CIRCLE: { // FIXME: SHAPE_TYPE_CIRCLE is not exposed to scripts in buildStringToShapeTypeLookup() + case SHAPE_TYPE_CIRCLE: { glm::vec2 radii = importanceSample2DDimension(emitRadiusStart) * 0.5f * glm::vec2(emitDimensions.x, emitDimensions.z); float x = radii.x * glm::cos(azimuth); float z = radii.y * glm::sin(azimuth); @@ -326,7 +330,43 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa break; } - case SHAPE_TYPE_COMPOUND: + case SHAPE_TYPE_COMPOUND: { + // if we get here we know that geometryResource is loaded + + size_t index = randFloat() * triangleInfo.totalSamples; + Triangle triangle; + for (size_t i = 0; i < triangleInfo.samplesPerTriangle.size(); i++) { + size_t numSamples = triangleInfo.samplesPerTriangle[i]; + if (index < numSamples) { + triangle = triangleInfo.triangles[i]; + break; + } + index -= numSamples; + } + + float edgeLength1 = glm::length(triangle.v1 - triangle.v0); + float edgeLength2 = glm::length(triangle.v2 - triangle.v1); + float edgeLength3 = glm::length(triangle.v0 - triangle.v2); + + float perimeter = edgeLength1 + edgeLength2 + edgeLength3; + float fraction1 = randFloatInRange(0.0f, 1.0f); + float fractionEdge1 = glm::min(fraction1 * perimeter / edgeLength1, 1.0f); + float fraction2 = fraction1 - edgeLength1 / perimeter; + float fractionEdge2 = glm::clamp(fraction2 * perimeter / edgeLength2, 0.0f, 1.0f); + float fraction3 = fraction2 - edgeLength2 / perimeter; + float fractionEdge3 = glm::clamp(fraction3 * perimeter / edgeLength3, 0.0f, 1.0f); + + float dim = importanceSample2DDimension(emitRadiusStart); + triangle = triangle * (glm::scale(emitDimensions) * triangleInfo.transform); + glm::vec3 center = (triangle.v0 + triangle.v1 + triangle.v2) / 3.0f; + glm::vec3 v0 = (dim * (triangle.v0 - center)) + center; + glm::vec3 v1 = (dim * (triangle.v1 - center)) + center; + glm::vec3 v2 = (dim * (triangle.v2 - center)) + center; + + emitPosition = glm::mix(v0, glm::mix(v1, glm::mix(v2, v0, fractionEdge3), fractionEdge2), fractionEdge1); + emitDirection = triangle.getNormal(); + break; + } case SHAPE_TYPE_SPHERE: case SHAPE_TYPE_ELLIPSOID: @@ -374,13 +414,16 @@ void ParticleEffectEntityRenderer::stepSimulation() { const auto& modelTransform = getModelTransform(); if (_emitting && particleProperties.emitting() && - (_shapeType != SHAPE_TYPE_COMPOUND || (_geometryResource && _geometryResource->isLoaded()))) { + (shapeType != SHAPE_TYPE_COMPOUND || (geometryResource && geometryResource->isLoaded()))) { uint64_t emitInterval = particleProperties.emitIntervalUsecs(); if (emitInterval > 0 && interval >= _timeUntilNextEmit) { auto timeRemaining = interval; while (timeRemaining > _timeUntilNextEmit) { + if (_shapeType == SHAPE_TYPE_COMPOUND && !_hasComputedTriangles) { + computeTriangles(geometryResource->getHFMModel()); + } // emit particle - _cpuParticles.push_back(createParticle(now, modelTransform, particleProperties, shapeType, geometryResource)); + _cpuParticles.push_back(createParticle(now, modelTransform, particleProperties, shapeType, geometryResource, _triangleInfo)); _timeUntilNextEmit = emitInterval; if (emitInterval < timeRemaining) { timeRemaining -= emitInterval; @@ -467,4 +510,120 @@ void ParticleEffectEntityRenderer::fetchGeometryResource() { } else { _geometryResource = DependencyManager::get()->getCollisionGeometryResource(hullURL); } +} + +// FIXME: this is very similar to Model::calculateTriangleSets +void ParticleEffectEntityRenderer::computeTriangles(const hfm::Model& hfmModel) { + PROFILE_RANGE(render, __FUNCTION__); + + int numberOfMeshes = hfmModel.meshes.size(); + + _hasComputedTriangles = true; + _triangleInfo.triangles.clear(); + _triangleInfo.samplesPerTriangle.clear(); + + std::vector areas; + float minArea = FLT_MAX; + AABox bounds; + + for (int i = 0; i < numberOfMeshes; i++) { + const HFMMesh& mesh = hfmModel.meshes.at(i); + + const int numberOfParts = mesh.parts.size(); + for (int j = 0; j < numberOfParts; j++) { + const HFMMeshPart& part = mesh.parts.at(j); + + const int INDICES_PER_TRIANGLE = 3; + const int INDICES_PER_QUAD = 4; + const int TRIANGLES_PER_QUAD = 2; + + // tell our triangleSet how many triangles to expect. + int numberOfQuads = part.quadIndices.size() / INDICES_PER_QUAD; + int numberOfTris = part.triangleIndices.size() / INDICES_PER_TRIANGLE; + int totalTriangles = (numberOfQuads * TRIANGLES_PER_QUAD) + numberOfTris; + _triangleInfo.triangles.reserve(_triangleInfo.triangles.size() + totalTriangles); + areas.reserve(areas.size() + totalTriangles); + + auto meshTransform = hfmModel.offset * mesh.modelTransform; + + if (part.quadIndices.size() > 0) { + int vIndex = 0; + for (int q = 0; q < numberOfQuads; q++) { + int i0 = part.quadIndices[vIndex++]; + int i1 = part.quadIndices[vIndex++]; + int i2 = part.quadIndices[vIndex++]; + int i3 = part.quadIndices[vIndex++]; + + // track the model space version... these points will be transformed by the FST's offset, + // which includes the scaling, rotation, and translation specified by the FST/FBX, + // this can't change at runtime, so we can safely store these in our TriangleSet + glm::vec3 v0 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i0], 1.0f)); + glm::vec3 v1 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i1], 1.0f)); + glm::vec3 v2 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i2], 1.0f)); + glm::vec3 v3 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i3], 1.0f)); + + Triangle tri1 = { v0, v1, v3 }; + Triangle tri2 = { v1, v2, v3 }; + _triangleInfo.triangles.push_back(tri1); + _triangleInfo.triangles.push_back(tri2); + + float area1 = tri1.getArea(); + areas.push_back(area1); + if (area1 > EPSILON) { + minArea = std::min(minArea, area1); + } + + float area2 = tri2.getArea(); + areas.push_back(area2); + if (area2 > EPSILON) { + minArea = std::min(minArea, area2); + } + + bounds += v0; + bounds += v1; + bounds += v2; + bounds += v3; + } + } + + if (part.triangleIndices.size() > 0) { + int vIndex = 0; + for (int t = 0; t < numberOfTris; t++) { + int i0 = part.triangleIndices[vIndex++]; + int i1 = part.triangleIndices[vIndex++]; + int i2 = part.triangleIndices[vIndex++]; + + // track the model space version... these points will be transformed by the FST's offset, + // which includes the scaling, rotation, and translation specified by the FST/FBX, + // this can't change at runtime, so we can safely store these in our TriangleSet + glm::vec3 v0 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i0], 1.0f)); + glm::vec3 v1 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i1], 1.0f)); + glm::vec3 v2 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i2], 1.0f)); + + Triangle tri = { v0, v1, v2 }; + _triangleInfo.triangles.push_back(tri); + + float area = tri.getArea(); + areas.push_back(area); + if (area > EPSILON) { + minArea = std::min(minArea, area); + } + + bounds += v0; + bounds += v1; + bounds += v2; + } + } + } + } + + _triangleInfo.totalSamples = 0; + for (auto& area : areas) { + size_t numSamples = area / minArea; + _triangleInfo.samplesPerTriangle.push_back(numSamples); + _triangleInfo.totalSamples += numSamples; + } + + glm::vec3 scale = bounds.getScale(); + _triangleInfo.transform = glm::scale(1.0f / scale) * glm::translate(-bounds.calcCenter()); } \ No newline at end of file diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h index 4a4e5e5cbc..d13c966e96 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -81,8 +81,18 @@ private: glm::vec2 spare; }; + void computeTriangles(const hfm::Model& hfmModel); + bool _hasComputedTriangles{ false }; + struct TriangleInfo { + std::vector triangles; + std::vector samplesPerTriangle; + size_t totalSamples; + glm::mat4 transform; + } _triangleInfo; + static CpuParticle createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties, - const ShapeType& shapeType, const GeometryResource::Pointer& geometryResource); + const ShapeType& shapeType, const GeometryResource::Pointer& geometryResource, + const TriangleInfo& triangleInfo); void stepSimulation(); particle::Properties _particleProperties; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 0a6875b63d..5958af66dd 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -127,6 +127,7 @@ void buildStringToShapeTypeLookup() { addShapeType(SHAPE_TYPE_SIMPLE_COMPOUND); addShapeType(SHAPE_TYPE_STATIC_MESH); addShapeType(SHAPE_TYPE_ELLIPSOID); + addShapeType(SHAPE_TYPE_CIRCLE); } QHash stringToMaterialMappingModeLookup; @@ -1121,21 +1122,21 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * Particles are emitted from the portion of the shape that lies between emitRadiusStart and the * shape's surface. * @property {number} polarStart=0 - The angle in radians from the entity's local z-axis at which particles start being emitted - * within the ellipsoid; range 0Math.PI. Particles are emitted from the portion of the - * ellipsoid that lies between polarStart and polarFinish. Only used if shapeType is - * ellipsoid. + * within the shape; range 0Math.PI. Particles are emitted from the portion of the + * shape that lies between polarStart and polarFinish. Only used if shapeType is + * ellipsoid or sphere. * @property {number} polarFinish=0 - The angle in radians from the entity's local z-axis at which particles stop being emitted - * within the ellipsoid; range 0Math.PI. Particles are emitted from the portion of the - * ellipsoid that lies between polarStart and polarFinish. Only used if shapeType is - * ellipsoid. + * within the shape; range 0Math.PI. Particles are emitted from the portion of the + * shape that lies between polarStart and polarFinish. Only used if shapeType is + * ellipsoid or sphere. * @property {number} azimuthStart=-Math.PI - The angle in radians from the entity's local x-axis about the entity's local * z-axis at which particles start being emitted; range -Math.PIMath.PI. Particles are - * emitted from the portion of the ellipsoid that lies between azimuthStart and azimuthFinish. - * Only used if shapeType is ellipsoid. + * emitted from the portion of the shape that lies between azimuthStart and azimuthFinish. + * Only used if shapeType is ellipsoid, sphere, or circle. * @property {number} azimuthFinish=Math.PI - The angle in radians from the entity's local x-axis about the entity's local * z-axis at which particles stop being emitted; range -Math.PIMath.PI. Particles are - * emitted from the portion of the ellipsoid that lies between azimuthStart and azimuthFinish. - * Only used if shapeType is ellipsoid. + * emitted from the portion of the shape that lies between azimuthStart and azimuthFinish. + * Only used if shapeType is ellipsoid, sphere, or circle.. * * @property {string} textures="" - The URL of a JPG or PNG image file to display for each particle. If you want transparency, * use PNG format. diff --git a/libraries/entities/src/LineEntityItem.h b/libraries/entities/src/LineEntityItem.h index 86ca6065bb..098183299f 100644 --- a/libraries/entities/src/LineEntityItem.h +++ b/libraries/entities/src/LineEntityItem.h @@ -49,8 +49,6 @@ class LineEntityItem : public EntityItem { QVector getLinePoints() const; - virtual ShapeType getShapeType() const override { return SHAPE_TYPE_NONE; } - // never have a ray intersection pick a LineEntityItem. virtual bool supportsDetailedIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 08468617ba..8f5e24ad76 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -175,7 +175,7 @@ protected: QString _textures; - ShapeType _shapeType = SHAPE_TYPE_NONE; + ShapeType _shapeType { SHAPE_TYPE_NONE } ; private: uint64_t _lastAnimated{ 0 }; diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index 363a7f39d1..fc590e06a4 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -112,7 +112,7 @@ protected: //! This is SHAPE_TYPE_ELLIPSOID rather than SHAPE_TYPE_NONE to maintain //! prior functionality where new or unsupported shapes are treated as //! ellipsoids. - ShapeType _collisionShapeType{ ShapeType::SHAPE_TYPE_ELLIPSOID }; + ShapeType _collisionShapeType { ShapeType::SHAPE_TYPE_ELLIPSOID }; }; #endif // hifi_ShapeEntityItem_h diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index f243d59da0..0771d9ad54 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -352,7 +352,7 @@ bool ZoneEntityItem::contains(const glm::vec3& point) const { Extents meshExtents = hfmModel.getMeshExtents(); glm::vec3 meshExtentsDiagonal = meshExtents.maximum - meshExtents.minimum; - glm::vec3 offset = -meshExtents.minimum- (meshExtentsDiagonal * getRegistrationPoint()); + glm::vec3 offset = -meshExtents.minimum - (meshExtentsDiagonal * getRegistrationPoint()); glm::vec3 scale(getScaledDimensions() / meshExtentsDiagonal); glm::mat4 hfmToEntityMatrix = glm::scale(scale) * glm::translate(offset); diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index df6ce50fd6..69e3227135 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -133,7 +133,7 @@ protected: KeyLightPropertyGroup _keyLightProperties; AmbientLightPropertyGroup _ambientLightProperties; - ShapeType _shapeType = DEFAULT_SHAPE_TYPE; + ShapeType _shapeType { DEFAULT_SHAPE_TYPE }; QString _compoundShapeURL; // The following 3 values are the defaults for zone creation diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index 3f2f7cd7fb..b6fca03403 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -358,6 +358,12 @@ glm::vec3 Triangle::getNormal() const { return glm::normalize(glm::cross(edge1, edge2)); } +float Triangle::getArea() const { + glm::vec3 edge1 = v1 - v0; + glm::vec3 edge2 = v2 - v0; + return 0.5f * glm::length(glm::cross(edge1, edge2)); +} + Triangle Triangle::operator*(const glm::mat4& transform) const { return { glm::vec3(transform * glm::vec4(v0, 1.0f)), diff --git a/libraries/shared/src/GeometryUtil.h b/libraries/shared/src/GeometryUtil.h index 8ec75f71bd..04c54fc32e 100644 --- a/libraries/shared/src/GeometryUtil.h +++ b/libraries/shared/src/GeometryUtil.h @@ -125,6 +125,7 @@ public: glm::vec3 v1; glm::vec3 v2; glm::vec3 getNormal() const; + float getArea() const; Triangle operator*(const glm::mat4& transform) const; }; diff --git a/scripts/system/assets/data/createAppTooltips.json b/scripts/system/assets/data/createAppTooltips.json index 7201cdecad..73c7504089 100644 --- a/scripts/system/assets/data/createAppTooltips.json +++ b/scripts/system/assets/data/createAppTooltips.json @@ -227,6 +227,14 @@ "speedSpread": { "tooltip": "The spread in speeds at which particles are emitted at, resulting in a variety of speeds." }, + "particleShapeType": { + "tooltip": "The shape of the surface from which to emit particles.", + "jsPropertyName": "shapeType" + }, + "particleCompoundShapeURL": { + "tooltip": "The model file to use for the particle emitter if Shape Type is \"Use Compound Shape URL\".", + "jsPropertyName": "compoundShapeURL" + }, "emitDimensions": { "tooltip": "The outer limit radius in dimensions that the particles can be emitted from." }, diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index f501df7933..15ab40e5ea 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -807,6 +807,21 @@ const GROUPS = [ decimals: 2, propertyID: "speedSpread", }, + { + label: "Shape Type", + type: "dropdown", + options: { "box": "Box", "ellipsoid": "Ellipsoid", + "cylinder-y": "Cylinder", "circle": "Circle", "plane": "Plane", + "compound": "Use Compound Shape URL" }, + propertyID: "particleShapeType", + propertyName: "shapeType", + }, + { + label: "Compound Shape URL", + type: "string", + propertyID: "particleCompoundShapeURL", + propertyName: "compoundShapeURL", + }, { label: "Emit Dimensions", type: "vec3", From 02129e0543919831cbfa26b44008f7ccac1a3035 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 25 Mar 2019 16:14:48 -0700 Subject: [PATCH 06/28] no-op refactor in prep for multiple entities per cert --- .../src/entities/EntityServer.cpp | 99 ++++++++----------- .../ui/overlays/ContextOverlayInterface.cpp | 3 +- libraries/entities/src/EntityTree.cpp | 21 ++-- libraries/entities/src/EntityTree.h | 4 +- 4 files changed, 54 insertions(+), 73 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 581d854909..f2cad1e400 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -474,73 +474,58 @@ void EntityServer::startDynamicDomainVerification() { QHash localMap(tree->getEntityCertificateIDMap()); QHashIterator i(localMap); - qCDebug(entities) << localMap.size() << "entities in _entityCertificateIDMap"; + qCDebug(entities) << localMap.size() << "certificates present."; while (i.hasNext()) { i.next(); const auto& certificateID = i.key(); const auto& entityID = i.value(); - EntityItemPointer entity = tree->findEntityByEntityItemID(entityID); + // Examine each cert: + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkRequest networkRequest; + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); + requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location"); + QJsonObject request; + request["certificate_id"] = certificateID; + networkRequest.setUrl(requestURL); - if (entity) { - if (!entity->getProperties().verifyStaticCertificateProperties()) { - qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << entityID << "failed" - << "static certificate verification."; - // Delete the entity if it doesn't pass static certificate verification - tree->withWriteLock([&] { - tree->deleteEntity(entityID, true); - }); - } else { - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkRequest networkRequest; - networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); - requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location"); - QJsonObject request; - request["certificate_id"] = certificateID; - networkRequest.setUrl(requestURL); + QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); - QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); + connect(networkReply, &QNetworkReply::finished, this, [this, entityID, networkReply] { + EntityTreePointer tree = std::static_pointer_cast(_tree); - connect(networkReply, &QNetworkReply::finished, this, [this, entityID, networkReply] { - EntityTreePointer tree = std::static_pointer_cast(_tree); + QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); + jsonObject = jsonObject["data"].toObject(); + networkReply->deleteLater(); - QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); - jsonObject = jsonObject["data"].toObject(); - - if (networkReply->error() == QNetworkReply::NoError) { - QString thisDomainID = DependencyManager::get()->getDomainID().remove(QRegExp("\\{|\\}")); - if (jsonObject["domain_id"].toString() != thisDomainID) { - EntityItemPointer entity = tree->findEntityByEntityItemID(entityID); - if (!entity) { - qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID; - networkReply->deleteLater(); - return; - } - if (entity->getAge() > (_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS/MSECS_PER_SECOND)) { - qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString() - << "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID; - tree->withWriteLock([&] { - tree->deleteEntity(entityID, true); - }); - } else { - qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID; - } - } else { - qCDebug(entities) << "Entity passed dynamic domain verification:" << entityID; - } - } else { - qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; NOT deleting entity" << entityID - << "More info:" << jsonObject; - } - - networkReply->deleteLater(); - }); + if (networkReply->error() != QNetworkReply::NoError) { + qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; NOT deleting entity" << entityID + << "More info:" << jsonObject; + return; } - } else { - qCWarning(entities) << "During DDV, an entity with ID" << entityID << "was NOT found in the Entity Tree!"; - } + QString thisDomainID = DependencyManager::get()->getDomainID().remove(QRegExp("\\{|\\}")); + if (jsonObject["domain_id"].toString() == thisDomainID) { + // Entity belongs here. Nothing to do. + return; + } + // Entity does not belong here: + EntityItemPointer entity = tree->findEntityByEntityItemID(entityID); + if (!entity) { + qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID; + return; + } + if (entity->getAge() <= (_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS / MSECS_PER_SECOND)) { + qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID; + return; + } + qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString() + << "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID; + tree->withWriteLock([&] { + tree->deleteEntity(entityID, true); + }); + }); } int nextInterval = qrand() % ((_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS + 1) - _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS) + _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS; diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index e5cec70f64..794feddd8a 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -422,8 +422,7 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer QString certID(packet->read(certIDByteArraySize)); QString text(packet->read(textByteArraySize)); - EntityItemID id; - bool verificationSuccess = DependencyManager::get()->getTree()->verifyNonce(certID, text, id); + bool verificationSuccess = DependencyManager::get()->getTree()->verifyNonce(certID, text); if (verificationSuccess) { emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 8bf7c92b1f..11e392f590 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1423,9 +1423,7 @@ bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) { void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) { QTimer* _challengeOwnershipTimeoutTimer = new QTimer(this); - connect(this, &EntityTree::killChallengeOwnershipTimeoutTimer, this, [=](const QString& certID) { - QReadLocker locker(&_entityCertificateIDMapLock); - EntityItemID id = _entityCertificateIDMap.value(certID); + connect(this, &EntityTree::killChallengeOwnershipTimeoutTimer, this, [=](const EntityItemID& id) { if (entityItemID == id && _challengeOwnershipTimeoutTimer) { _challengeOwnershipTimeoutTimer->stop(); _challengeOwnershipTimeoutTimer->deleteLater(); @@ -1455,12 +1453,7 @@ QByteArray EntityTree::computeNonce(const QString& certID, const QString ownerKe return nonceBytes; } -bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id) { - { - QReadLocker certIdMapLocker(&_entityCertificateIDMapLock); - id = _entityCertificateIDMap.value(certID); - } - +bool EntityTree::verifyNonce(const QString& certID, const QString& nonce) { QString actualNonce, key; { QWriteLocker locker(&_certNonceMapLock); @@ -1645,10 +1638,14 @@ void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const QString certID(message.read(certIDByteArraySize)); QString text(message.read(textByteArraySize)); - emit killChallengeOwnershipTimeoutTimer(certID); + EntityItemID id; + { + QReadLocker certIdMapLocker(&_entityCertificateIDMapLock); + id = _entityCertificateIDMap.value(certID); + } + emit killChallengeOwnershipTimeoutTimer(id); - EntityItemID id; - if (!verifyNonce(certID, text, id)) { + if (!verifyNonce(certID, text)) { if (!id.isNull()) { deleteEntity(id, true); } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index e627a07d13..63e1197970 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -253,7 +253,7 @@ public: static const float DEFAULT_MAX_TMP_ENTITY_LIFETIME; QByteArray computeNonce(const QString& certID, const QString ownerKey); - bool verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id); + bool verifyNonce(const QString& certID, const QString& nonce); QUuid getMyAvatarSessionUUID() { return _myAvatar ? _myAvatar->getSessionUUID() : QUuid(); } void setMyAvatar(std::shared_ptr myAvatar) { _myAvatar = myAvatar; } @@ -290,7 +290,7 @@ signals: void entityServerScriptChanging(const EntityItemID& entityItemID, const bool reload); void newCollisionSoundURL(const QUrl& url, const EntityItemID& entityID); void clearingEntities(); - void killChallengeOwnershipTimeoutTimer(const QString& certID); + void killChallengeOwnershipTimeoutTimer(const EntityItemID& certID); protected: From 91a165b4c3d92e958f318057ea67b67207f022a1 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 26 Mar 2019 11:49:08 -0700 Subject: [PATCH 07/28] separate out the certified entity map stuff (no-op refactor) --- .../src/entities/EntityServer.cpp | 58 +-------- libraries/entities/src/EntityTree.cpp | 119 +++++++++++++----- libraries/entities/src/EntityTree.h | 3 + 3 files changed, 95 insertions(+), 85 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index f2cad1e400..06632dabb0 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include "../AssignmentDynamicFactory.h" @@ -471,62 +470,7 @@ void EntityServer::startDynamicDomainVerification() { qCDebug(entities) << "Starting Dynamic Domain Verification..."; EntityTreePointer tree = std::static_pointer_cast(_tree); - QHash localMap(tree->getEntityCertificateIDMap()); - - QHashIterator i(localMap); - qCDebug(entities) << localMap.size() << "certificates present."; - while (i.hasNext()) { - i.next(); - const auto& certificateID = i.key(); - const auto& entityID = i.value(); - - // Examine each cert: - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkRequest networkRequest; - networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); - requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location"); - QJsonObject request; - request["certificate_id"] = certificateID; - networkRequest.setUrl(requestURL); - - QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); - - connect(networkReply, &QNetworkReply::finished, this, [this, entityID, networkReply] { - EntityTreePointer tree = std::static_pointer_cast(_tree); - - QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); - jsonObject = jsonObject["data"].toObject(); - networkReply->deleteLater(); - - if (networkReply->error() != QNetworkReply::NoError) { - qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; NOT deleting entity" << entityID - << "More info:" << jsonObject; - return; - } - QString thisDomainID = DependencyManager::get()->getDomainID().remove(QRegExp("\\{|\\}")); - if (jsonObject["domain_id"].toString() == thisDomainID) { - // Entity belongs here. Nothing to do. - return; - } - // Entity does not belong here: - EntityItemPointer entity = tree->findEntityByEntityItemID(entityID); - if (!entity) { - qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID; - return; - } - if (entity->getAge() <= (_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS / MSECS_PER_SECOND)) { - qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID; - return; - } - qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString() - << "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID; - tree->withWriteLock([&] { - tree->deleteEntity(entityID, true); - }); - }); - } + tree->startDynamicDomainVerificationOnServer((float) _MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS / MSECS_PER_SECOND); int nextInterval = qrand() % ((_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS + 1) - _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS) + _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS; qCDebug(entities) << "Restarting Dynamic Domain Verification timer for" << nextInterval / 1000 << "seconds"; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 11e392f590..55a8c42261 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include "EntitySimulation.h" #include "VariantMapToScriptValue.h" @@ -286,27 +287,7 @@ void EntityTree::postAddEntity(EntityItemPointer entity) { assert(entity); if (getIsServer()) { - QString certID(entity->getCertificateID()); - EntityItemID entityItemID = entity->getEntityItemID(); - EntityItemID existingEntityItemID; - - { - QWriteLocker locker(&_entityCertificateIDMapLock); - existingEntityItemID = _entityCertificateIDMap.value(certID); - if (!certID.isEmpty()) { - _entityCertificateIDMap.insert(certID, entityItemID); - qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID; - } - } - - // Delete an already-existing entity from the tree if it has the same - // CertificateID as the entity we're trying to add. - if (!existingEntityItemID.isNull() && !entity->getCertificateType().contains(DOMAIN_UNLIMITED)) { - qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID" - << existingEntityItemID << ". Deleting existing entity."; - deleteEntity(existingEntityItemID, true); - return; - } + addCertifiedEntityOnServer(entity); } // check to see if we need to simulate this entity.. @@ -764,13 +745,7 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) theEntity->die(); if (getIsServer()) { - { - QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock); - QString certID = theEntity->getCertificateID(); - if (theEntity->getEntityItemID() == _entityCertificateIDMap.value(certID)) { - _entityCertificateIDMap.remove(certID); - } - } + removeCertifiedEntityOnServer(theEntity); // set up the deleted entities ID QWriteLocker recentlyDeletedEntitiesLocker(&_recentlyDeletedEntitiesLock); @@ -1421,6 +1396,94 @@ bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) { return false; } +void EntityTree::addCertifiedEntityOnServer(EntityItemPointer entity) { + QString certID(entity->getCertificateID()); + EntityItemID entityItemID = entity->getEntityItemID(); + EntityItemID existingEntityItemID; + + { + QWriteLocker locker(&_entityCertificateIDMapLock); + existingEntityItemID = _entityCertificateIDMap.value(certID); + if (!certID.isEmpty()) { + _entityCertificateIDMap.insert(certID, entityItemID); + qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID; + } + } + + // Delete an already-existing entity from the tree if it has the same + // CertificateID as the entity we're trying to add. + if (!existingEntityItemID.isNull() && !entity->getCertificateType().contains(DOMAIN_UNLIMITED)) { + qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID" + << existingEntityItemID << ". Deleting existing entity."; + deleteEntity(existingEntityItemID, true); + } +} + +void EntityTree::removeCertifiedEntityOnServer(EntityItemPointer entity) { + QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock); + QString certID = entity->getCertificateID(); + if (entity->getEntityItemID() == _entityCertificateIDMap.value(certID)) { + _entityCertificateIDMap.remove(certID); + } +} + +void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove) { + QHash localMap(getEntityCertificateIDMap()); + QHashIterator i(localMap); + qCDebug(entities) << localMap.size() << "certificates present."; + while (i.hasNext()) { + i.next(); + const auto& certificateID = i.key(); + const auto& entityID = i.value(); + + // Examine each cert: + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkRequest networkRequest; + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); + requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location"); + QJsonObject request; + request["certificate_id"] = certificateID; + networkRequest.setUrl(requestURL); + + QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); + + connect(networkReply, &QNetworkReply::finished, this, [this, entityID, networkReply, minimumAgeToRemove] { + + QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); + jsonObject = jsonObject["data"].toObject(); + networkReply->deleteLater(); + + if (networkReply->error() != QNetworkReply::NoError) { + qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; NOT deleting entity" << entityID + << "More info:" << jsonObject; + return; + } + QString thisDomainID = DependencyManager::get()->getDomainID().remove(QRegExp("\\{|\\}")); + if (jsonObject["domain_id"].toString() == thisDomainID) { + // Entity belongs here. Nothing to do. + return; + } + // Entity does not belong here: + EntityItemPointer entity = findEntityByEntityItemID(entityID); + if (!entity) { + qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID; + return; + } + if (entity->getAge() <= minimumAgeToRemove) { + qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID; + return; + } + qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString() + << "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID; + withWriteLock([&] { + deleteEntity(entityID, true); + }); + }); + } +} + void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) { QTimer* _challengeOwnershipTimeoutTimer = new QTimer(this); connect(this, &EntityTree::killChallengeOwnershipTimeoutTimer, this, [=](const EntityItemID& id) { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 63e1197970..10809747b3 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -279,6 +279,7 @@ public: void updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, bool force, bool tellServer); + void startDynamicDomainVerificationOnServer(float minimumAgeToRemove); signals: void deletingEntity(const EntityItemID& entityID); @@ -377,6 +378,8 @@ protected: Q_INVOKABLE void startChallengeOwnershipTimer(const EntityItemID& entityItemID); private: + void addCertifiedEntityOnServer(EntityItemPointer entity); + void removeCertifiedEntityOnServer(EntityItemPointer entity); void sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); void sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode); void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); From be74218d931ec3adef8c885e935d3b1aebc55c72 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 26 Mar 2019 12:05:10 -0700 Subject: [PATCH 08/28] eliminate copy of hash table --- libraries/entities/src/EntityTree.cpp | 6 +++--- libraries/entities/src/EntityTree.h | 5 ----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 55a8c42261..63259e2c58 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1428,9 +1428,9 @@ void EntityTree::removeCertifiedEntityOnServer(EntityItemPointer entity) { } void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove) { - QHash localMap(getEntityCertificateIDMap()); - QHashIterator i(localMap); - qCDebug(entities) << localMap.size() << "certificates present."; + QReadLocker locker(&_entityCertificateIDMapLock); + QHashIterator i(_entityCertificateIDMap); + qCDebug(entities) << _entityCertificateIDMap.size() << "certificates present."; while (i.hasNext()) { i.next(); const auto& certificateID = i.key(); diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 10809747b3..fe6045f6f7 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -157,11 +157,6 @@ public: return _recentlyDeletedEntityItemIDs; } - QHash getEntityCertificateIDMap() const { - QReadLocker locker(&_entityCertificateIDMapLock); - return _entityCertificateIDMap; - } - void forgetEntitiesDeletedBefore(quint64 sinceTime); int processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode); From 4dfd0fbda3fa7771437c557fe17e4692ac048053 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 26 Mar 2019 15:02:13 -0700 Subject: [PATCH 09/28] challenge ownership packet has an id that gets reflected back. It doesn't have to be a cert. --- interface/src/commerce/Wallet.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 0e9ad7d79a..37f28960e5 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -816,18 +816,18 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack bool challengeOriginatedFromClient = packet->getType() == PacketType::ChallengeOwnershipRequest; int status; - int certIDByteArraySize; + int idByteArraySize; int textByteArraySize; int challengingNodeUUIDByteArraySize; - packet->readPrimitive(&certIDByteArraySize); + packet->readPrimitive(&idByteArraySize); packet->readPrimitive(&textByteArraySize); // returns a cast char*, size if (challengeOriginatedFromClient) { packet->readPrimitive(&challengingNodeUUIDByteArraySize); } // "encryptedText" is now a series of random bytes, a nonce - QByteArray certID = packet->read(certIDByteArraySize); + QByteArray id = packet->read(idByteArraySize); QByteArray text = packet->read(textByteArraySize); QByteArray challengingNodeUUID; if (challengeOriginatedFromClient) { @@ -853,32 +853,32 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack textByteArray = sig.toUtf8(); } textByteArraySize = textByteArray.size(); - int certIDSize = certID.size(); + int idSize = id.size(); // setup the packet if (challengeOriginatedFromClient) { auto textPacket = NLPacket::create(PacketType::ChallengeOwnershipReply, - certIDSize + textByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int), + idSize + textByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int), true); - textPacket->writePrimitive(certIDSize); + textPacket->writePrimitive(idSize); textPacket->writePrimitive(textByteArraySize); textPacket->writePrimitive(challengingNodeUUIDByteArraySize); - textPacket->write(certID); + textPacket->write(id); textPacket->write(textByteArray); textPacket->write(challengingNodeUUID); - qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for CertID" << certID; + qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for id" << id; nodeList->sendPacket(std::move(textPacket), *sendingNode); } else { - auto textPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDSize + textByteArraySize + 2 * sizeof(int), true); + auto textPacket = NLPacket::create(PacketType::ChallengeOwnership, idSize + textByteArraySize + 2 * sizeof(int), true); - textPacket->writePrimitive(certIDSize); + textPacket->writePrimitive(idSize); textPacket->writePrimitive(textByteArraySize); - textPacket->write(certID); + textPacket->write(id); textPacket->write(textByteArray); - qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for CertID" << certID; + qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for id" << id; nodeList->sendPacket(std::move(textPacket), *sendingNode); } From c9b79b24e3fc033949e21cd24eebd8d720090144 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 26 Mar 2019 15:23:13 -0700 Subject: [PATCH 10/28] track nonces by entity id instead of by cert --- .../ui/overlays/ContextOverlayInterface.cpp | 4 ++-- libraries/entities/src/EntityTree.cpp | 20 +++++++++---------- libraries/entities/src/EntityTree.h | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 794feddd8a..5a4ff52a84 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -329,7 +329,7 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID QString ownerKey = jsonObject["transfer_recipient_key"].toString(); QByteArray certID = entityProperties.getCertificateID().toUtf8(); - QByteArray text = DependencyManager::get()->getTree()->computeNonce(certID, ownerKey); + QByteArray text = DependencyManager::get()->getTree()->computeNonce(entityID, ownerKey); QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122(); int certIDByteArraySize = certID.length(); @@ -422,7 +422,7 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer QString certID(packet->read(certIDByteArraySize)); QString text(packet->read(textByteArraySize)); - bool verificationSuccess = DependencyManager::get()->getTree()->verifyNonce(certID, text); + bool verificationSuccess = DependencyManager::get()->getTree()->verifyNonce(_lastInspectedEntity, text); if (verificationSuccess) { emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 63259e2c58..41e0cbafc4 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1506,21 +1506,21 @@ void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) _challengeOwnershipTimeoutTimer->start(5000); } -QByteArray EntityTree::computeNonce(const QString& certID, const QString ownerKey) { +QByteArray EntityTree::computeNonce(const EntityItemID& entityID, const QString ownerKey) { QUuid nonce = QUuid::createUuid(); //random, 5-hex value, separated by "-" QByteArray nonceBytes = nonce.toByteArray(); - QWriteLocker locker(&_certNonceMapLock); - _certNonceMap.insert(certID, QPair(nonce, ownerKey)); + QWriteLocker locker(&_entityNonceMapLock); + _entityNonceMap.insert(entityID, QPair(nonce, ownerKey)); return nonceBytes; } -bool EntityTree::verifyNonce(const QString& certID, const QString& nonce) { +bool EntityTree::verifyNonce(const EntityItemID& entityID, const QString& nonce) { QString actualNonce, key; { - QWriteLocker locker(&_certNonceMapLock); - QPair sent = _certNonceMap.take(certID); + QWriteLocker locker(&_entityNonceMapLock); + QPair sent = _entityNonceMap.take(entityID); actualNonce = sent.first.toString(); key = sent.second; } @@ -1530,9 +1530,9 @@ bool EntityTree::verifyNonce(const QString& certID, const QString& nonce) { bool verificationSuccess = EntityItemProperties::verifySignature(annotatedKey.toUtf8(), hashedActualNonce, QByteArray::fromBase64(nonce.toUtf8())); if (verificationSuccess) { - qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded."; + qCDebug(entities) << "Ownership challenge for Entity ID" << entityID << "succeeded."; } else { - qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed. Actual nonce:" << actualNonce << + qCDebug(entities) << "Ownership challenge for Entity ID" << entityID << "failed. Actual nonce:" << actualNonce << "\nHashed actual nonce (digest):" << hashedActualNonce << "\nSent nonce (signature)" << nonce << "\nKey" << key; } @@ -1585,7 +1585,7 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri // 1. Obtain a nonce auto nodeList = DependencyManager::get(); - QByteArray text = computeNonce(certID, ownerKey); + QByteArray text = computeNonce(entityItemID, ownerKey); if (text == "") { qCDebug(entities) << "CRITICAL ERROR: Couldn't compute nonce. Deleting entity..."; @@ -1708,7 +1708,7 @@ void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const } emit killChallengeOwnershipTimeoutTimer(id); - if (!verifyNonce(certID, text)) { + if (!verifyNonce(id, text)) { if (!id.isNull()) { deleteEntity(id, true); } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index fe6045f6f7..327f06164e 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -247,8 +247,8 @@ public: static const float DEFAULT_MAX_TMP_ENTITY_LIFETIME; - QByteArray computeNonce(const QString& certID, const QString ownerKey); - bool verifyNonce(const QString& certID, const QString& nonce); + QByteArray computeNonce(const EntityItemID& entityID, const QString ownerKey); + bool verifyNonce(const EntityItemID& entityID, const QString& nonce); QUuid getMyAvatarSessionUUID() { return _myAvatar ? _myAvatar->getSessionUUID() : QUuid(); } void setMyAvatar(std::shared_ptr myAvatar) { _myAvatar = myAvatar; } @@ -325,8 +325,8 @@ protected: mutable QReadWriteLock _entityCertificateIDMapLock; QHash _entityCertificateIDMap; - mutable QReadWriteLock _certNonceMapLock; - QHash> _certNonceMap; + mutable QReadWriteLock _entityNonceMapLock; + QHash> _entityNonceMap; EntitySimulationPointer _simulation; From 57da21cec2beac3d62b25deca50bb3df849f20a7 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 26 Mar 2019 17:12:28 -0700 Subject: [PATCH 11/28] more about challenges use entity id as id, not cert id --- interface/src/commerce/Ledger.h | 3 +- interface/src/commerce/QmlCommerce.h | 3 +- .../ui/overlays/ContextOverlayInterface.cpp | 26 ++++----- libraries/entities/src/EntityTree.cpp | 55 ++++++++----------- libraries/entities/src/EntityTree.h | 2 +- 5 files changed, 42 insertions(+), 47 deletions(-) diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index 2e18f34c8d..64528e617d 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "AccountManager.h" @@ -65,7 +66,7 @@ signals: void availableUpdatesResult(QJsonObject result); void updateItemResult(QJsonObject result); - void updateCertificateStatus(const QString& certID, uint certStatus); + void updateCertificateStatus(const EntityItemID& entityID, uint certStatus); public slots: void buySuccess(QNetworkReply* reply); diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index 3217b8a1f9..99b3e32e8b 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -19,6 +19,7 @@ #include +#include #include class QmlCommerce : public QObject, public Dependency { @@ -49,7 +50,7 @@ signals: void availableUpdatesResult(QJsonObject result); void updateItemResult(QJsonObject result); - void updateCertificateStatus(const QString& certID, uint certStatus); + void updateCertificateStatus(const EntityItemID& entityID, uint certStatus); void transferAssetToNodeResult(QJsonObject result); void transferAssetToUsernameResult(QJsonObject result); diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 5a4ff52a84..0e4fa796d8 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -328,21 +328,21 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID } else { QString ownerKey = jsonObject["transfer_recipient_key"].toString(); - QByteArray certID = entityProperties.getCertificateID().toUtf8(); + QByteArray id = entityID.toByteArray(); QByteArray text = DependencyManager::get()->getTree()->computeNonce(entityID, ownerKey); QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122(); - int certIDByteArraySize = certID.length(); + int idByteArraySize = id.length(); int textByteArraySize = text.length(); int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length(); auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, - certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int), + idByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int), true); - challengeOwnershipPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipPacket->writePrimitive(idByteArraySize); challengeOwnershipPacket->writePrimitive(textByteArraySize); challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize); - challengeOwnershipPacket->write(certID); + challengeOwnershipPacket->write(id); challengeOwnershipPacket->write(text); challengeOwnershipPacket->write(nodeToChallengeByteArray); nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer); @@ -370,12 +370,12 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID // so they always pass Ownership Verification. It's necessary to emit this signal // so that the Inspection Certificate can continue its information-grabbing process. auto ledger = DependencyManager::get(); - emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); + emit ledger->updateCertificateStatus(entityID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); } } else { auto ledger = DependencyManager::get(); _challengeOwnershipTimeoutTimer.stop(); - emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED)); + emit ledger->updateCertificateStatus(entityID, (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED)); emit DependencyManager::get()->ownershipVerificationFailed(_lastInspectedEntity); qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!"; } @@ -401,7 +401,7 @@ void ContextOverlayInterface::startChallengeOwnershipTimer() { connect(&_challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() { qCDebug(entities) << "Ownership challenge timed out for" << _lastInspectedEntity; - emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_TIMEOUT)); + emit ledger->updateCertificateStatus(_lastInspectedEntity, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_TIMEOUT)); emit DependencyManager::get()->ownershipVerificationFailed(_lastInspectedEntity); }); @@ -413,22 +413,22 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer _challengeOwnershipTimeoutTimer.stop(); - int certIDByteArraySize; + int idByteArraySize; int textByteArraySize; - packet->readPrimitive(&certIDByteArraySize); + packet->readPrimitive(&idByteArraySize); packet->readPrimitive(&textByteArraySize); - QString certID(packet->read(certIDByteArraySize)); + EntityItemID id(packet->read(idByteArraySize)); QString text(packet->read(textByteArraySize)); bool verificationSuccess = DependencyManager::get()->getTree()->verifyNonce(_lastInspectedEntity, text); if (verificationSuccess) { - emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); + emit ledger->updateCertificateStatus(id, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); emit DependencyManager::get()->ownershipVerificationSuccess(_lastInspectedEntity); } else { - emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED)); + emit ledger->updateCertificateStatus(id, (uint)(ledger->CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED)); emit DependencyManager::get()->ownershipVerificationFailed(_lastInspectedEntity); } } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 41e0cbafc4..4fc234122b 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1540,42 +1540,42 @@ bool EntityTree::verifyNonce(const EntityItemID& entityID, const QString& nonce) } void EntityTree::processChallengeOwnershipRequestPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { - int certIDByteArraySize; + int idByteArraySize; int textByteArraySize; int nodeToChallengeByteArraySize; - message.readPrimitive(&certIDByteArraySize); + message.readPrimitive(&idByteArraySize); message.readPrimitive(&textByteArraySize); message.readPrimitive(&nodeToChallengeByteArraySize); - QByteArray certID(message.read(certIDByteArraySize)); + QByteArray id(message.read(idByteArraySize)); QByteArray text(message.read(textByteArraySize)); QByteArray nodeToChallenge(message.read(nodeToChallengeByteArraySize)); - sendChallengeOwnershipRequestPacket(certID, text, nodeToChallenge, sourceNode); + sendChallengeOwnershipRequestPacket(id, text, nodeToChallenge, sourceNode); } void EntityTree::processChallengeOwnershipReplyPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { auto nodeList = DependencyManager::get(); - int certIDByteArraySize; + int idByteArraySize; int textByteArraySize; int challengingNodeUUIDByteArraySize; - message.readPrimitive(&certIDByteArraySize); + message.readPrimitive(&idByteArraySize); message.readPrimitive(&textByteArraySize); message.readPrimitive(&challengingNodeUUIDByteArraySize); - QByteArray certID(message.read(certIDByteArraySize)); + QByteArray id(message.read(idByteArraySize)); QByteArray text(message.read(textByteArraySize)); QUuid challengingNode = QUuid::fromRfc4122(message.read(challengingNodeUUIDByteArraySize)); auto challengeOwnershipReplyPacket = NLPacket::create(PacketType::ChallengeOwnershipReply, - certIDByteArraySize + text.length() + 2 * sizeof(int), + idByteArraySize + text.length() + 2 * sizeof(int), true); - challengeOwnershipReplyPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipReplyPacket->writePrimitive(idByteArraySize); challengeOwnershipReplyPacket->writePrimitive(text.length()); - challengeOwnershipReplyPacket->write(certID); + challengeOwnershipReplyPacket->write(id); challengeOwnershipReplyPacket->write(text); nodeList->sendPacket(std::move(challengeOwnershipReplyPacket), *(nodeList->nodeWithUUID(challengingNode))); @@ -1595,14 +1595,14 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri } else { qCDebug(entities) << "Challenging ownership of Cert ID" << certID; // 2. Send the nonce to the rezzing avatar's node - QByteArray certIDByteArray = certID.toUtf8(); - int certIDByteArraySize = certIDByteArray.size(); + QByteArray idByteArray = entityItemID.toByteArray(); + int idByteArraySize = idByteArray.size(); auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, - certIDByteArraySize + text.length() + 2 * sizeof(int), + idByteArraySize + text.length() + 2 * sizeof(int), true); - challengeOwnershipPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipPacket->writePrimitive(idByteArraySize); challengeOwnershipPacket->writePrimitive(text.length()); - challengeOwnershipPacket->write(certIDByteArray); + challengeOwnershipPacket->write(idByteArray); challengeOwnershipPacket->write(text); nodeList->sendPacket(std::move(challengeOwnershipPacket), *senderNode); @@ -1616,24 +1616,24 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri } } -void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode) { +void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& id, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode) { auto nodeList = DependencyManager::get(); // In this case, Client A is challenging Client B. Client A is inspecting a certified entity that it wants // to make sure belongs to Avatar B. QByteArray senderNodeUUID = senderNode->getUUID().toRfc4122(); - int certIDByteArraySize = certID.length(); + int idByteArraySize = id.length(); int TextByteArraySize = text.length(); int senderNodeUUIDSize = senderNodeUUID.length(); auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, - certIDByteArraySize + TextByteArraySize + senderNodeUUIDSize + 3 * sizeof(int), + idByteArraySize + TextByteArraySize + senderNodeUUIDSize + 3 * sizeof(int), true); - challengeOwnershipPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipPacket->writePrimitive(idByteArraySize); challengeOwnershipPacket->writePrimitive(TextByteArraySize); challengeOwnershipPacket->writePrimitive(senderNodeUUIDSize); - challengeOwnershipPacket->write(certID); + challengeOwnershipPacket->write(id); challengeOwnershipPacket->write(text); challengeOwnershipPacket->write(senderNodeUUID); @@ -1692,26 +1692,19 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt } void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { - int certIDByteArraySize; + int idByteArraySize; int textByteArraySize; - message.readPrimitive(&certIDByteArraySize); + message.readPrimitive(&idByteArraySize); message.readPrimitive(&textByteArraySize); - QString certID(message.read(certIDByteArraySize)); + EntityItemID id(message.read(idByteArraySize)); QString text(message.read(textByteArraySize)); - EntityItemID id; - { - QReadLocker certIdMapLocker(&_entityCertificateIDMapLock); - id = _entityCertificateIDMap.value(certID); - } emit killChallengeOwnershipTimeoutTimer(id); if (!verifyNonce(id, text)) { - if (!id.isNull()) { - deleteEntity(id, true); - } + deleteEntity(id, true); } } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 327f06164e..2b3ee93ab7 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -376,7 +376,7 @@ private: void addCertifiedEntityOnServer(EntityItemPointer entity); void removeCertifiedEntityOnServer(EntityItemPointer entity); void sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); - void sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode); + void sendChallengeOwnershipRequestPacket(const QByteArray& id, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode); void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); std::shared_ptr _myAvatar{ nullptr }; From bf1982564c702c0f91ce27318ee2ae5a089b2108 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 26 Mar 2019 19:26:38 -0700 Subject: [PATCH 12/28] pass id around instead of relying on latest ivar value in a singleton --- .../ui/overlays/ContextOverlayInterface.cpp | 26 +++++++++---------- .../src/ui/overlays/ContextOverlayInterface.h | 5 ++-- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 0e4fa796d8..1c8a9019ea 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -292,7 +292,7 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID setLastInspectedEntity(entityID); - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags); + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityID, _entityPropertyFlags); auto nodeList = DependencyManager::get(); @@ -349,10 +349,10 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID // Kickoff a 10-second timeout timer that marks the cert if we don't get an ownership response in time if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer"); + QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer", Q_ARG(const EntityItemID&, entityID)); return; } else { - startChallengeOwnershipTimer(); + startChallengeOwnershipTimer(entityID); } } } else { @@ -376,8 +376,8 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID auto ledger = DependencyManager::get(); _challengeOwnershipTimeoutTimer.stop(); emit ledger->updateCertificateStatus(entityID, (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED)); - emit DependencyManager::get()->ownershipVerificationFailed(_lastInspectedEntity); - qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!"; + emit DependencyManager::get()->ownershipVerificationFailed(entityID); + qCDebug(context_overlay) << "Entity" << entityID << "failed static certificate verification!"; } } @@ -395,14 +395,14 @@ void ContextOverlayInterface::deletingEntity(const EntityItemID& entityID) { } } -void ContextOverlayInterface::startChallengeOwnershipTimer() { +void ContextOverlayInterface::startChallengeOwnershipTimer(const EntityItemID& entityItemID) { auto ledger = DependencyManager::get(); - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags); + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); connect(&_challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() { - qCDebug(entities) << "Ownership challenge timed out for" << _lastInspectedEntity; - emit ledger->updateCertificateStatus(_lastInspectedEntity, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_TIMEOUT)); - emit DependencyManager::get()->ownershipVerificationFailed(_lastInspectedEntity); + qCDebug(entities) << "Ownership challenge timed out for" << entityItemID; + emit ledger->updateCertificateStatus(entityItemID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_TIMEOUT)); + emit DependencyManager::get()->ownershipVerificationFailed(entityItemID); }); _challengeOwnershipTimeoutTimer.start(5000); @@ -422,13 +422,13 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer EntityItemID id(packet->read(idByteArraySize)); QString text(packet->read(textByteArraySize)); - bool verificationSuccess = DependencyManager::get()->getTree()->verifyNonce(_lastInspectedEntity, text); + bool verificationSuccess = DependencyManager::get()->getTree()->verifyNonce(id, text); if (verificationSuccess) { emit ledger->updateCertificateStatus(id, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); - emit DependencyManager::get()->ownershipVerificationSuccess(_lastInspectedEntity); + emit DependencyManager::get()->ownershipVerificationSuccess(id); } else { emit ledger->updateCertificateStatus(id, (uint)(ledger->CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED)); - emit DependencyManager::get()->ownershipVerificationFailed(_lastInspectedEntity); + emit DependencyManager::get()->ownershipVerificationFailed(id); } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index b1b62aa0c4..e688d1c115 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -46,7 +46,7 @@ public: ContextOverlayInterface(); Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } - void setLastInspectedEntity(const QUuid& entityID) { _challengeOwnershipTimeoutTimer.stop(); _lastInspectedEntity = entityID; } + void setLastInspectedEntity(const QUuid& entityID) { _challengeOwnershipTimeoutTimer.stop(); } void setEnabled(bool enabled); bool getEnabled() { return _enabled; } bool getIsInMarketplaceInspectionMode() { return _isInMarketplaceInspectionMode; } @@ -83,7 +83,6 @@ private: EntityItemID _mouseDownEntity; quint64 _mouseDownEntityTimestamp; EntityItemID _currentEntityWithContextOverlay; - EntityItemID _lastInspectedEntity; QString _entityMarketplaceID; bool _contextOverlayJustClicked { false }; @@ -94,7 +93,7 @@ private: void deletingEntity(const EntityItemID& entityItemID); - Q_INVOKABLE void startChallengeOwnershipTimer(); + Q_INVOKABLE void startChallengeOwnershipTimer(const EntityItemID& entityItemID); QTimer _challengeOwnershipTimeoutTimer; }; From 71111936a2d1eb38d1d1c2c6453ae9fe2403ddfb Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 27 Mar 2019 11:42:16 -0700 Subject: [PATCH 13/28] prep for entity lists --- libraries/entities/src/EntityTree.cpp | 69 +++++++++++++++------------ 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 4fc234122b..f4b82ad4b1 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1398,32 +1398,32 @@ bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) { void EntityTree::addCertifiedEntityOnServer(EntityItemPointer entity) { QString certID(entity->getCertificateID()); - EntityItemID entityItemID = entity->getEntityItemID(); EntityItemID existingEntityItemID; - - { + if (!certID.isEmpty()) { + EntityItemID entityItemID = entity->getEntityItemID(); QWriteLocker locker(&_entityCertificateIDMapLock); existingEntityItemID = _entityCertificateIDMap.value(certID); - if (!certID.isEmpty()) { - _entityCertificateIDMap.insert(certID, entityItemID); - qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID; - } + _entityCertificateIDMap.insert(certID, entityItemID); + qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID; } - // Delete an already-existing entity from the tree if it has the same // CertificateID as the entity we're trying to add. if (!existingEntityItemID.isNull() && !entity->getCertificateType().contains(DOMAIN_UNLIMITED)) { qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID" << existingEntityItemID << ". Deleting existing entity."; - deleteEntity(existingEntityItemID, true); + withWriteLock([&] { + deleteEntity(existingEntityItemID, true); + }); } } void EntityTree::removeCertifiedEntityOnServer(EntityItemPointer entity) { - QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock); QString certID = entity->getCertificateID(); - if (entity->getEntityItemID() == _entityCertificateIDMap.value(certID)) { - _entityCertificateIDMap.remove(certID); + if (!certID.isEmpty()) { + QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock); + if (entity->getEntityItemID() == _entityCertificateIDMap.value(certID)) { + _entityCertificateIDMap.remove(certID); + } } } @@ -1449,15 +1449,16 @@ void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); - connect(networkReply, &QNetworkReply::finished, this, [this, entityID, networkReply, minimumAgeToRemove] { + connect(networkReply, &QNetworkReply::finished, this, [this, entityID, networkReply, minimumAgeToRemove, &certificateID] { QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); jsonObject = jsonObject["data"].toObject(); + bool failure = networkReply->error() != QNetworkReply::NoError; + auto failureReason = networkReply->error(); networkReply->deleteLater(); - - if (networkReply->error() != QNetworkReply::NoError) { - qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; NOT deleting entity" << entityID - << "More info:" << jsonObject; + if (failure) { + qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << failureReason + << "; NOT deleting entity" << entityID << "More info:" << jsonObject; return; } QString thisDomainID = DependencyManager::get()->getDomainID().remove(QRegExp("\\{|\\}")); @@ -1466,20 +1467,26 @@ void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove return; } // Entity does not belong here: - EntityItemPointer entity = findEntityByEntityItemID(entityID); - if (!entity) { - qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID; - return; + { + EntityItemPointer entity = findEntityByEntityItemID(entityID); + if (!entity) { + qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID; + return; + } + if (entity->getAge() <= minimumAgeToRemove) { + qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID; + return; + } + qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString() + << "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID; + withWriteLock([&] { + deleteEntity(entityID, true); + }); } - if (entity->getAge() <= minimumAgeToRemove) { - qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID; - return; + { + QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock); + _entityCertificateIDMap.remove(certificateID); } - qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString() - << "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID; - withWriteLock([&] { - deleteEntity(entityID, true); - }); }); } } @@ -1704,7 +1711,9 @@ void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const emit killChallengeOwnershipTimeoutTimer(id); if (!verifyNonce(id, text)) { - deleteEntity(id, true); + withWriteLock([&] { + deleteEntity(id, true); + }); } } From f6ee77a6ae5b31881efcbe56d67bbaff1e36fe70 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 27 Mar 2019 12:23:02 -0700 Subject: [PATCH 14/28] an entity list of one --- libraries/entities/src/EntityTree.cpp | 27 ++++++++++++++++++--------- libraries/entities/src/EntityTree.h | 2 +- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index f4b82ad4b1..01c00b82e5 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1398,17 +1398,20 @@ bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) { void EntityTree::addCertifiedEntityOnServer(EntityItemPointer entity) { QString certID(entity->getCertificateID()); - EntityItemID existingEntityItemID; + QList entityList; if (!certID.isEmpty()) { EntityItemID entityItemID = entity->getEntityItemID(); QWriteLocker locker(&_entityCertificateIDMapLock); - existingEntityItemID = _entityCertificateIDMap.value(certID); - _entityCertificateIDMap.insert(certID, entityItemID); + entityList = _entityCertificateIDMap.value(certID); + QList newList; + newList << entityItemID; + _entityCertificateIDMap.insert(certID, newList); qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID; } // Delete an already-existing entity from the tree if it has the same // CertificateID as the entity we're trying to add. - if (!existingEntityItemID.isNull() && !entity->getCertificateType().contains(DOMAIN_UNLIMITED)) { + if (!entityList.isEmpty() && !entity->getCertificateType().contains(DOMAIN_UNLIMITED)) { + EntityItemID existingEntityItemID = entityList.first(); qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID" << existingEntityItemID << ". Deleting existing entity."; withWriteLock([&] { @@ -1421,7 +1424,8 @@ void EntityTree::removeCertifiedEntityOnServer(EntityItemPointer entity) { QString certID = entity->getCertificateID(); if (!certID.isEmpty()) { QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock); - if (entity->getEntityItemID() == _entityCertificateIDMap.value(certID)) { + QList entityList = _entityCertificateIDMap.value(certID); + if (!entityList.isEmpty() && (entity->getEntityItemID() == entityList.first())) { _entityCertificateIDMap.remove(certID); } } @@ -1429,12 +1433,16 @@ void EntityTree::removeCertifiedEntityOnServer(EntityItemPointer entity) { void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove) { QReadLocker locker(&_entityCertificateIDMapLock); - QHashIterator i(_entityCertificateIDMap); + QHashIterator> i(_entityCertificateIDMap); qCDebug(entities) << _entityCertificateIDMap.size() << "certificates present."; while (i.hasNext()) { i.next(); const auto& certificateID = i.key(); - const auto& entityID = i.value(); + const auto& entityIDs = i.value(); + + if (entityIDs.isEmpty()) { + continue; + } // Examine each cert: QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); @@ -1449,7 +1457,7 @@ void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); - connect(networkReply, &QNetworkReply::finished, this, [this, entityID, networkReply, minimumAgeToRemove, &certificateID] { + connect(networkReply, &QNetworkReply::finished, this, [this, entityIDs, networkReply, minimumAgeToRemove, &certificateID] { QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); jsonObject = jsonObject["data"].toObject(); @@ -1458,7 +1466,7 @@ void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove networkReply->deleteLater(); if (failure) { qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << failureReason - << "; NOT deleting entity" << entityID << "More info:" << jsonObject; + << "; NOT deleting cert" << certificateID << "More info:" << jsonObject; return; } QString thisDomainID = DependencyManager::get()->getDomainID().remove(QRegExp("\\{|\\}")); @@ -1468,6 +1476,7 @@ void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove } // Entity does not belong here: { + EntityItemID entityID = entityIDs.first(); EntityItemPointer entity = findEntityByEntityItemID(entityID); if (!entity) { qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 2b3ee93ab7..c80517c82b 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -323,7 +323,7 @@ protected: QHash _entityMap; mutable QReadWriteLock _entityCertificateIDMapLock; - QHash _entityCertificateIDMap; + QHash> _entityCertificateIDMap; mutable QReadWriteLock _entityNonceMapLock; QHash> _entityNonceMap; From 489fadda6ea3e406fbfd145125ef01119fe88bbc Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 27 Mar 2019 14:58:58 -0700 Subject: [PATCH 15/28] and now it's a list that cleans up after itself --- libraries/entities/src/EntityTree.cpp | 42 +++++++++++++++++---------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 01c00b82e5..1ccf3fcfa2 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1398,20 +1398,21 @@ bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) { void EntityTree::addCertifiedEntityOnServer(EntityItemPointer entity) { QString certID(entity->getCertificateID()); - QList entityList; + EntityItemID existingEntityItemID; if (!certID.isEmpty()) { EntityItemID entityItemID = entity->getEntityItemID(); QWriteLocker locker(&_entityCertificateIDMapLock); - entityList = _entityCertificateIDMap.value(certID); - QList newList; - newList << entityItemID; - _entityCertificateIDMap.insert(certID, newList); - qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID; + QList& entityList = _entityCertificateIDMap[certID]; // inserts it if needed. + if (!entityList.isEmpty() && !entity->getCertificateType().contains(DOMAIN_UNLIMITED)) { + existingEntityItemID = entityList.first(); // we will only care about the first, if any, below. + entityList.removeOne(existingEntityItemID); + } + entityList << entityItemID; // adds to list within hash because entityList is a reference. + qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID << "total" << entityList.size() << "entities."; } // Delete an already-existing entity from the tree if it has the same // CertificateID as the entity we're trying to add. - if (!entityList.isEmpty() && !entity->getCertificateType().contains(DOMAIN_UNLIMITED)) { - EntityItemID existingEntityItemID = entityList.first(); + if (!existingEntityItemID.isNull()) { qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID" << existingEntityItemID << ". Deleting existing entity."; withWriteLock([&] { @@ -1424,8 +1425,10 @@ void EntityTree::removeCertifiedEntityOnServer(EntityItemPointer entity) { QString certID = entity->getCertificateID(); if (!certID.isEmpty()) { QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock); - QList entityList = _entityCertificateIDMap.value(certID); - if (!entityList.isEmpty() && (entity->getEntityItemID() == entityList.first())) { + QList& entityList = _entityCertificateIDMap[certID]; + entityList.removeOne(entity->getEntityItemID()); + if (entityList.isEmpty()) { + // hmmm, do we to make it be a hash instead of a list, so that this is faster if you stamp out 1000 of a domainUnlimited? _entityCertificateIDMap.remove(certID); } } @@ -1439,7 +1442,6 @@ void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove i.next(); const auto& certificateID = i.key(); const auto& entityIDs = i.value(); - if (entityIDs.isEmpty()) { continue; } @@ -1475,16 +1477,18 @@ void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove return; } // Entity does not belong here: - { - EntityItemID entityID = entityIDs.first(); + QList retained; + for (int i = 0; i < entityIDs.size(); i++) { + EntityItemID entityID = entityIDs.at(i); EntityItemPointer entity = findEntityByEntityItemID(entityID); if (!entity) { qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID; - return; + continue; } if (entity->getAge() <= minimumAgeToRemove) { qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID; - return; + retained << entityID; + continue; } qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString() << "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID; @@ -1494,7 +1498,13 @@ void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove } { QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock); - _entityCertificateIDMap.remove(certificateID); + if (retained.isEmpty()) { + qCDebug(entities) << "Removed" << certificateID; + _entityCertificateIDMap.remove(certificateID); + } else { + qCDebug(entities) << "Retained" << retained.size() << "young entities for" << certificateID; + _entityCertificateIDMap[certificateID] = retained; + } } }); } From 6cc95fe8ac4f2e8e804add3686e319066abd8140 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 27 Mar 2019 15:46:25 -0700 Subject: [PATCH 16/28] CR --- libraries/entities/src/ModelEntityItem.h | 2 +- libraries/entities/src/ParticleEffectEntityItem.cpp | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 8f5e24ad76..d532fefe7e 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -175,7 +175,7 @@ protected: QString _textures; - ShapeType _shapeType { SHAPE_TYPE_NONE } ; + ShapeType _shapeType { SHAPE_TYPE_NONE }; private: uint64_t _lastAnimated{ 0 }; diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 5f285ca91b..12119f1466 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -740,10 +740,7 @@ void ParticleEffectEntityItem::setShapeType(ShapeType type) { } withWriteLock([&] { - if (type != _shapeType) { - _shapeType = type; - _flags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; - } + _shapeType = type; }); } From 5480c9f5ca0f85349d7a2631209df143034298ed Mon Sep 17 00:00:00 2001 From: raveenajain Date: Thu, 28 Mar 2019 15:27:49 -0700 Subject: [PATCH 17/28] skinning, skeleton for models, avatar --- libraries/fbx/src/GLTFSerializer.cpp | 270 +++++++++++++++++++++++---- libraries/fbx/src/GLTFSerializer.h | 2 + 2 files changed, 232 insertions(+), 40 deletions(-) diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index b8d4e53b65..1b6f5767f4 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -711,19 +711,19 @@ glm::mat4 GLTFSerializer::getModelTransform(const GLTFNode& node) { node.matrix[12], node.matrix[13], node.matrix[14], node.matrix[15]); } else { - if (node.defined["rotation"] && node.rotation.size() == 4) { - //quat(x,y,z,w) to quat(w,x,y,z) - glm::quat rotquat = glm::quat(node.rotation[3], node.rotation[0], node.rotation[1], node.rotation[2]); - tmat = glm::mat4_cast(rotquat) * tmat; - } - if (node.defined["scale"] && node.scale.size() == 3) { glm::vec3 scale = glm::vec3(node.scale[0], node.scale[1], node.scale[2]); glm::mat4 s = glm::mat4(1.0); s = glm::scale(s, scale); tmat = s * tmat; } - + + if (node.defined["rotation"] && node.rotation.size() == 4) { + //quat(x,y,z,w) to quat(w,x,y,z) + glm::quat rotquat = glm::quat(node.rotation[3], node.rotation[0], node.rotation[1], node.rotation[2]); + tmat = glm::mat4_cast(rotquat) * tmat; + } + if (node.defined["translation"] && node.translation.size() == 3) { glm::vec3 trans = glm::vec3(node.translation[0], node.translation[1], node.translation[2]); glm::mat4 t = glm::mat4(1.0); @@ -734,15 +734,58 @@ glm::mat4 GLTFSerializer::getModelTransform(const GLTFNode& node) { return tmat; } +std::vector> GLTFSerializer::getSkinInverseBindMatrices() { + std::vector> inverseBindMatrixValues; + for (auto &skin : _file.skins) { + GLTFAccessor& indicesAccessor = _file.accessors[skin.inverseBindMatrices]; + GLTFBufferView& indicesBufferview = _file.bufferviews[indicesAccessor.bufferView]; + GLTFBuffer& indicesBuffer = _file.buffers[indicesBufferview.buffer]; + int accBoffset = indicesAccessor.defined["byteOffset"] ? indicesAccessor.byteOffset : 0; + QVector matrices; + addArrayOfType(indicesBuffer.blob, + indicesBufferview.byteOffset + accBoffset, + indicesAccessor.count, + matrices, + indicesAccessor.type, + indicesAccessor.componentType); + inverseBindMatrixValues.push_back(matrices); + } + return inverseBindMatrixValues; +} + +QVector GLTFSerializer::nodeDFS(int n, std::vector& children, bool order) { + QVector result; + result.append(n); + int begin = 0; + int finish = children.size(); + if (order) { + begin = children.size() - 1; + finish = -1; + } + int index = begin; + while (index != finish) { + int c = children[index]; + std::vector nested = _file.nodes[c].children.toStdVector(); + if (nested.size() != 0) { + std::sort(nested.begin(), nested.end()); + result.append(nodeDFS(c, nested, order)); + } else { + result.append(c); + } + begin < finish ? index++ : index--; + } + return result; +} + + bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { + int nodesSize = _file.nodes.size(); //Build dependencies - QVector> nodeDependencies(_file.nodes.size()); + QVector> nodeDependencies(nodesSize); int nodecount = 0; - bool hasChildren = false; foreach(auto &node, _file.nodes) { //nodes_transforms.push_back(getModelTransform(node)); - hasChildren |= !node.children.isEmpty(); foreach(int child, node.children) nodeDependencies[child].push_back(nodecount); nodecount++; } @@ -764,26 +807,99 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { nodecount++; } - - HFMJoint joint; - joint.isSkeletonJoint = true; - joint.bindTransformFoundInCluster = false; - joint.distanceToParent = 0; - joint.parentIndex = -1; - hfmModel.joints.resize(_file.nodes.size()); - hfmModel.jointIndices["x"] = _file.nodes.size(); - int jointInd = 0; - for (auto& node : _file.nodes) { - int size = node.transforms.size(); - if (hasChildren) { size--; } - joint.preTransform = glm::mat4(1); - for (int i = 0; i < size; i++) { - joint.preTransform = node.transforms[i] * joint.preTransform; - } - joint.name = node.name; - hfmModel.joints[jointInd] = joint; - jointInd++; + + + // initialize order in which nodes will be parsed + QVector nodeQueue; + int start = 0; + int end = nodesSize; + if (!_file.scenes[_file.scene].nodes.contains(0)) { + end = -1; + start = nodesSize - 1; } + QVector init = _file.scenes[_file.scene].nodes; + std::sort(init.begin(), init.end()); + int begin = 0; + int finish = init.size(); + if (start > end) { + begin = init.size() - 1; + finish = -1; + } + int index = begin; + while (index != finish) { + int i = init[index]; + std::vector children = _file.nodes[i].children.toStdVector(); + std::sort(children.begin(), children.end()); + nodeQueue.append(nodeDFS(i, children, start > end)); + begin < finish ? index++ : index--; + } + + + // Build joints + HFMJoint joint; + joint.distanceToParent = 0; + hfmModel.jointIndices["x"] = nodesSize; + hfmModel.hasSkeletonJoints = false; + + for (int nodeIndex : nodeQueue) { + auto& node = _file.nodes[nodeIndex]; + + joint.parentIndex = -1; + if (!_file.scenes[_file.scene].nodes.contains(nodeIndex)) { + joint.parentIndex = nodeQueue.indexOf(nodeDependencies[nodeIndex][0]); + } + joint.transform = node.transforms.first(); + joint.postTransform = glm::mat4(); + glm:vec3 scale = extractScale(joint.transform); + joint.postTransform[0][0] = scale.x; + joint.postTransform[1][1] = scale.y; + joint.postTransform[2][2] = scale.z; + joint.rotation = glmExtractRotation(joint.transform); + joint.translation = extractTranslation(joint.transform); + + joint.name = node.name; + hfmModel.joints.push_back(joint); + } + + + // Build skeleton + int matrixIndex = 0; + std::vector> inverseBindValues = getSkinInverseBindMatrices(); + std::vector jointInverseBindTransforms; + jointInverseBindTransforms.resize(nodesSize); + + int jointIndex = end; + while (jointIndex != start) { + start < end ? jointIndex-- : jointIndex++; + int jOffset = nodeQueue[jointIndex]; + auto joint = hfmModel.joints[jointIndex]; + + joint.isSkeletonJoint = false; + if (!_file.skins.isEmpty()) { + hfmModel.hasSkeletonJoints = true; + for (int s = 0; s < _file.skins.size(); s++) { + auto skin = _file.skins[s]; + joint.isSkeletonJoint = skin.joints.contains(jOffset); + + if (joint.isSkeletonJoint) { + QVector value = inverseBindValues[s]; + int matrixCount = 16 * skin.joints.indexOf(jOffset); + jointInverseBindTransforms[jointIndex] = + glm::mat4(value[matrixCount], value[matrixCount + 1], value[matrixCount + 2], value[matrixCount + 3], + value[matrixCount + 4], value[matrixCount + 5], value[matrixCount + 6], value[matrixCount + 7], + value[matrixCount + 8], value[matrixCount + 9], value[matrixCount + 10], value[matrixCount + 11], + value[matrixCount + 12], value[matrixCount + 13], value[matrixCount + 14], value[matrixCount + 15]); + matrixIndex++; + } else { + jointInverseBindTransforms[jointIndex] = glm::mat4(); + } + } + glm::vec3 bindTranslation = extractTranslation(hfmModel.offset * glm::inverse(jointInverseBindTransforms[jointIndex])); + hfmModel.bindExtents.addPoint(bindTranslation); + } + hfmModel.joints[jointIndex] = joint; + } + //Build materials QVector materialIDs; @@ -803,23 +919,34 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { } - - nodecount = 0; // Build meshes - foreach(auto &node, _file.nodes) { + nodecount = 0; + int nodeIndex = start; + while (nodeIndex != end) { + auto& node = _file.nodes[nodeIndex]; if (node.defined["mesh"]) { qCDebug(modelformat) << "node_transforms" << node.transforms; foreach(auto &primitive, _file.meshes[node.mesh].primitives) { hfmModel.meshes.append(HFMMesh()); HFMMesh& mesh = hfmModel.meshes[hfmModel.meshes.size() - 1]; - HFMCluster cluster; - cluster.jointIndex = nodecount; - cluster.inverseBindMatrix = glm::mat4(1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); - mesh.clusters.append(cluster); + if (!hfmModel.hasSkeletonJoints) { + HFMCluster cluster; + cluster.jointIndex = nodecount; + cluster.inverseBindMatrix = glm::mat4(); + cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix); + mesh.clusters.append(cluster); + } else { + int j = start; + while (j != end) { + HFMCluster cluster; + cluster.jointIndex = j; + cluster.inverseBindMatrix = jointInverseBindTransforms[j]; + cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix); + mesh.clusters.append(cluster); + start < end ? j++ : j--; + } + } HFMMeshPart part = HFMMeshPart(); @@ -848,6 +975,8 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { } QList keys = primitive.attributes.values.keys(); + QVector clusterJoints; + QVector clusterWeights; foreach(auto &key, keys) { int accessorIdx = primitive.attributes.values[key]; @@ -949,8 +1078,68 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { for (int n = 0; n < texcoords.size(); n = n + 2) { mesh.texCoords1.push_back(glm::vec2(texcoords[n], texcoords[n + 1])); } + } else if (key == "JOINTS_0") { + QVector joints; + success = addArrayOfType(buffer.blob, + bufferview.byteOffset + accBoffset, + accessor.count, + joints, + accessor.type, + accessor.componentType); + if (!success) { + qWarning(modelformat) << "There was a problem reading glTF JOINTS_0 data for model " << _url; + continue; + } + for (int n = 0; n < joints.size(); n++) { + clusterJoints.push_back(joints[n]); + } + } else if (key == "WEIGHTS_0") { + QVector weights; + success = addArrayOfType(buffer.blob, + bufferview.byteOffset + accBoffset, + accessor.count, + weights, + accessor.type, + accessor.componentType); + if (!success) { + qWarning(modelformat) << "There was a problem reading glTF WEIGHTS_0 data for model " << _url; + continue; + } + for (int n = 0; n < weights.size(); n++) { + clusterWeights.push_back(weights[n]); + } + } + } + + // adapted from FBXSerializer.cpp + if (hfmModel.hasSkeletonJoints) { + int numClusterIndices = clusterJoints.size(); + const int WEIGHTS_PER_VERTEX = 4; + const float ALMOST_HALF = 0.499f; + int numVertices = mesh.vertices.size(); + mesh.clusterIndices.fill(0, numClusterIndices); + mesh.clusterWeights.fill(0, numClusterIndices); + + for (int c = 0; c < clusterJoints.size(); c++) { + mesh.clusterIndices[c] = _file.skins[node.skin].joints[clusterJoints[c]]; } + // normalize and compress to 16-bits + for (int i = 0; i < numVertices; ++i) { + int j = i * WEIGHTS_PER_VERTEX; + + float totalWeight = 0.0f; + for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) { + totalWeight += clusterWeights[k]; + } + const float ALMOST_HALF = 0.499f; + if (totalWeight > 0.0f) { + float weightScalingFactor = (float)(UINT16_MAX) / totalWeight; + for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) { + mesh.clusterWeights[k] = (uint16_t)(weightScalingFactor * clusterWeights[k] + ALMOST_HALF); + } + } + } } if (primitive.defined["material"]) { @@ -959,7 +1148,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { mesh.parts.push_back(part); // populate the texture coordinates if they don't exist - if (mesh.texCoords.size() == 0) { + if (mesh.texCoords.size() == 0 && !hfmModel.hasSkeletonJoints) { for (int i = 0; i < part.triangleIndices.size(); i++) mesh.texCoords.push_back(glm::vec2(0.0, 1.0)); } mesh.meshExtents.reset(); @@ -973,6 +1162,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { } nodecount++; + start < end ? nodeIndex++ : nodeIndex--; } diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h index 05dc526f79..331f5937ed 100755 --- a/libraries/fbx/src/GLTFSerializer.h +++ b/libraries/fbx/src/GLTFSerializer.h @@ -712,6 +712,8 @@ private: hifi::ByteArray _glbBinary; glm::mat4 getModelTransform(const GLTFNode& node); + std::vector> getSkinInverseBindMatrices(); + QVector nodeDFS(int n, std::vector& children, bool order); bool buildGeometry(HFMModel& hfmModel, const hifi::URL& url); bool parseGLTF(const hifi::ByteArray& data); From b283bb303d83fb9bf460216ffc99aedfa46ee1ee Mon Sep 17 00:00:00 2001 From: raveenajain Date: Thu, 28 Mar 2019 17:55:51 -0700 Subject: [PATCH 18/28] jenkins warning fixes --- libraries/fbx/src/GLTFSerializer.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index 1b6f5767f4..cf8d40cfc1 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -757,9 +757,9 @@ QVector GLTFSerializer::nodeDFS(int n, std::vector& children, bool ord QVector result; result.append(n); int begin = 0; - int finish = children.size(); + int finish = (int)children.size(); if (order) { - begin = children.size() - 1; + begin = (int)children.size() - 1; finish = -1; } int index = begin; @@ -850,7 +850,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { } joint.transform = node.transforms.first(); joint.postTransform = glm::mat4(); - glm:vec3 scale = extractScale(joint.transform); + glm::vec3 scale = extractScale(joint.transform); joint.postTransform[0][0] = scale.x; joint.postTransform[1][1] = scale.y; joint.postTransform[2][2] = scale.z; @@ -1132,7 +1132,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) { totalWeight += clusterWeights[k]; } - const float ALMOST_HALF = 0.499f; if (totalWeight > 0.0f) { float weightScalingFactor = (float)(UINT16_MAX) / totalWeight; for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) { From 269b910d24a8423ec2e73048bf40829c0115bc31 Mon Sep 17 00:00:00 2001 From: raveenajain Date: Fri, 29 Mar 2019 18:25:17 -0700 Subject: [PATCH 19/28] update variables, loops --- libraries/fbx/src/GLTFSerializer.cpp | 105 ++++++++++++++------------- libraries/fbx/src/GLTFSerializer.h | 4 +- 2 files changed, 56 insertions(+), 53 deletions(-) diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index cf8d40cfc1..d29c09992f 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -734,8 +734,8 @@ glm::mat4 GLTFSerializer::getModelTransform(const GLTFNode& node) { return tmat; } -std::vector> GLTFSerializer::getSkinInverseBindMatrices() { - std::vector> inverseBindMatrixValues; +std::vector> GLTFSerializer::getSkinInverseBindMatrices() { + std::vector> inverseBindMatrixValues; for (auto &skin : _file.skins) { GLTFAccessor& indicesAccessor = _file.accessors[skin.inverseBindMatrices]; GLTFBufferView& indicesBufferview = _file.bufferviews[indicesAccessor.bufferView]; @@ -748,14 +748,14 @@ std::vector> GLTFSerializer::getSkinInverseBindMatrices() { matrices, indicesAccessor.type, indicesAccessor.componentType); - inverseBindMatrixValues.push_back(matrices); + inverseBindMatrixValues.push_back(matrices.toStdVector()); } return inverseBindMatrixValues; } -QVector GLTFSerializer::nodeDFS(int n, std::vector& children, bool order) { - QVector result; - result.append(n); +std::vector GLTFSerializer::nodeDFS(int n, std::vector& children, bool order) { + std::vector result; + result.push_back(n); int begin = 0; int finish = (int)children.size(); if (order) { @@ -768,9 +768,11 @@ QVector GLTFSerializer::nodeDFS(int n, std::vector& children, bool ord std::vector nested = _file.nodes[c].children.toStdVector(); if (nested.size() != 0) { std::sort(nested.begin(), nested.end()); - result.append(nodeDFS(c, nested, order)); + for (int n : nodeDFS(c, nested, order)) { + result.push_back(n); + } } else { - result.append(c); + result.push_back(c); } begin < finish ? index++ : index--; } @@ -779,10 +781,10 @@ QVector GLTFSerializer::nodeDFS(int n, std::vector& children, bool ord bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { - int nodesSize = _file.nodes.size(); + int numNodes = _file.nodes.size(); //Build dependencies - QVector> nodeDependencies(nodesSize); + QVector> nodeDependencies(numNodes); int nodecount = 0; foreach(auto &node, _file.nodes) { //nodes_transforms.push_back(getModelTransform(node)); @@ -810,35 +812,40 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { // initialize order in which nodes will be parsed - QVector nodeQueue; - int start = 0; - int end = nodesSize; + QVector nodeQueue; + nodeQueue.reserve(numNodes); + int rootNode = 0; + int finalNode = numNodes; if (!_file.scenes[_file.scene].nodes.contains(0)) { - end = -1; - start = nodesSize - 1; + rootNode = numNodes - 1; + finalNode = -1; } - QVector init = _file.scenes[_file.scene].nodes; - std::sort(init.begin(), init.end()); - int begin = 0; - int finish = init.size(); - if (start > end) { - begin = init.size() - 1; - finish = -1; + bool rootAtStartOfList = rootNode < finalNode; + int nodeListStride = 1; + if (!rootAtStartOfList) { nodeListStride = -1; } + + QVector initialSceneNodes = _file.scenes[_file.scene].nodes; + std::sort(initialSceneNodes.begin(), initialSceneNodes.end()); + int sceneRootNode = 0; + int sceneFinalNode = initialSceneNodes.size(); + if (!rootAtStartOfList) { + sceneRootNode = initialSceneNodes.size() - 1; + sceneFinalNode = -1; } - int index = begin; - while (index != finish) { - int i = init[index]; + for (int index = sceneRootNode; index != sceneFinalNode; index += nodeListStride) { + int i = initialSceneNodes[index]; std::vector children = _file.nodes[i].children.toStdVector(); std::sort(children.begin(), children.end()); - nodeQueue.append(nodeDFS(i, children, start > end)); - begin < finish ? index++ : index--; + for (int n : nodeDFS(i, children, !rootAtStartOfList)) { + nodeQueue.append(n); + } } // Build joints HFMJoint joint; joint.distanceToParent = 0; - hfmModel.jointIndices["x"] = nodesSize; + hfmModel.jointIndices["x"] = numNodes; hfmModel.hasSkeletonJoints = false; for (int nodeIndex : nodeQueue) { @@ -858,46 +865,46 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { joint.translation = extractTranslation(joint.transform); joint.name = node.name; + joint.isSkeletonJoint = false; hfmModel.joints.push_back(joint); } // Build skeleton - int matrixIndex = 0; - std::vector> inverseBindValues = getSkinInverseBindMatrices(); std::vector jointInverseBindTransforms; - jointInverseBindTransforms.resize(nodesSize); + if (!_file.skins.isEmpty()) { + int matrixIndex = 0; + std::vector> inverseBindValues = getSkinInverseBindMatrices(); + jointInverseBindTransforms.resize(numNodes); - int jointIndex = end; - while (jointIndex != start) { - start < end ? jointIndex-- : jointIndex++; - int jOffset = nodeQueue[jointIndex]; - auto joint = hfmModel.joints[jointIndex]; + int jointIndex = finalNode; + while (jointIndex != rootNode) { + rootAtStartOfList ? jointIndex-- : jointIndex++; + int jOffset = nodeQueue[jointIndex]; + auto joint = hfmModel.joints[jointIndex]; - joint.isSkeletonJoint = false; - if (!_file.skins.isEmpty()) { hfmModel.hasSkeletonJoints = true; for (int s = 0; s < _file.skins.size(); s++) { auto skin = _file.skins[s]; joint.isSkeletonJoint = skin.joints.contains(jOffset); if (joint.isSkeletonJoint) { - QVector value = inverseBindValues[s]; + std::vector value = inverseBindValues[s]; int matrixCount = 16 * skin.joints.indexOf(jOffset); jointInverseBindTransforms[jointIndex] = - glm::mat4(value[matrixCount], value[matrixCount + 1], value[matrixCount + 2], value[matrixCount + 3], + glm::mat4(value[matrixCount], value[matrixCount + 1], value[matrixCount + 2], value[matrixCount + 3], value[matrixCount + 4], value[matrixCount + 5], value[matrixCount + 6], value[matrixCount + 7], - value[matrixCount + 8], value[matrixCount + 9], value[matrixCount + 10], value[matrixCount + 11], + value[matrixCount + 8], value[matrixCount + 9], value[matrixCount + 10], value[matrixCount + 11], value[matrixCount + 12], value[matrixCount + 13], value[matrixCount + 14], value[matrixCount + 15]); matrixIndex++; } else { jointInverseBindTransforms[jointIndex] = glm::mat4(); } + glm::vec3 bindTranslation = extractTranslation(hfmModel.offset * glm::inverse(jointInverseBindTransforms[jointIndex])); + hfmModel.bindExtents.addPoint(bindTranslation); } - glm::vec3 bindTranslation = extractTranslation(hfmModel.offset * glm::inverse(jointInverseBindTransforms[jointIndex])); - hfmModel.bindExtents.addPoint(bindTranslation); + hfmModel.joints[jointIndex] = joint; } - hfmModel.joints[jointIndex] = joint; } @@ -921,8 +928,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { // Build meshes nodecount = 0; - int nodeIndex = start; - while (nodeIndex != end) { + for (int nodeIndex = rootNode; nodeIndex != finalNode; nodeIndex += nodeListStride) { auto& node = _file.nodes[nodeIndex]; if (node.defined["mesh"]) { @@ -937,14 +943,12 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix); mesh.clusters.append(cluster); } else { - int j = start; - while (j != end) { + for (int j = rootNode; j != finalNode; j += nodeListStride) { HFMCluster cluster; cluster.jointIndex = j; cluster.inverseBindMatrix = jointInverseBindTransforms[j]; cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix); mesh.clusters.append(cluster); - start < end ? j++ : j--; } } @@ -1148,7 +1152,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { // populate the texture coordinates if they don't exist if (mesh.texCoords.size() == 0 && !hfmModel.hasSkeletonJoints) { - for (int i = 0; i < part.triangleIndices.size(); i++) mesh.texCoords.push_back(glm::vec2(0.0, 1.0)); + for (int i = 0; i < part.triangleIndices.size(); i++) { mesh.texCoords.push_back(glm::vec2(0.0, 1.0)); } } mesh.meshExtents.reset(); foreach(const glm::vec3& vertex, mesh.vertices) { @@ -1161,7 +1165,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { } nodecount++; - start < end ? nodeIndex++ : nodeIndex--; } diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h index 331f5937ed..e383e6581c 100755 --- a/libraries/fbx/src/GLTFSerializer.h +++ b/libraries/fbx/src/GLTFSerializer.h @@ -712,8 +712,8 @@ private: hifi::ByteArray _glbBinary; glm::mat4 getModelTransform(const GLTFNode& node); - std::vector> getSkinInverseBindMatrices(); - QVector nodeDFS(int n, std::vector& children, bool order); + std::vector> getSkinInverseBindMatrices(); + std::vector nodeDFS(int n, std::vector& children, bool order); bool buildGeometry(HFMModel& hfmModel, const hifi::URL& url); bool parseGLTF(const hifi::ByteArray& data); From 2920fdc966b93e0e50f498168a50a25454d99883 Mon Sep 17 00:00:00 2001 From: raveenajain Date: Mon, 1 Apr 2019 21:09:14 +0100 Subject: [PATCH 20/28] variables, cluster size --- libraries/fbx/src/GLTFSerializer.cpp | 33 ++++++++++++---------------- libraries/fbx/src/GLTFSerializer.h | 2 +- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index d29c09992f..1192952b9e 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -753,28 +753,26 @@ std::vector> GLTFSerializer::getSkinInverseBindMatrices() { return inverseBindMatrixValues; } -std::vector GLTFSerializer::nodeDFS(int n, std::vector& children, bool order) { +std::vector GLTFSerializer::nodeDFS(int n, std::vector& children, int stride) { std::vector result; result.push_back(n); - int begin = 0; - int finish = (int)children.size(); - if (order) { - begin = (int)children.size() - 1; - finish = -1; + int rootDFS = 0; + int finalDFS = (int)children.size(); + if (stride == -1) { + rootDFS = (int)children.size() - 1; + finalDFS = -1; } - int index = begin; - while (index != finish) { + for (int index = rootDFS; index != finalDFS; index += stride) { int c = children[index]; std::vector nested = _file.nodes[c].children.toStdVector(); if (nested.size() != 0) { std::sort(nested.begin(), nested.end()); - for (int n : nodeDFS(c, nested, order)) { + for (int n : nodeDFS(c, nested, stride)) { result.push_back(n); } } else { result.push_back(c); } - begin < finish ? index++ : index--; } return result; } @@ -836,7 +834,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { int i = initialSceneNodes[index]; std::vector children = _file.nodes[i].children.toStdVector(); std::sort(children.begin(), children.end()); - for (int n : nodeDFS(i, children, !rootAtStartOfList)) { + for (int n : nodeDFS(i, children, nodeListStride)) { nodeQueue.append(n); } } @@ -856,14 +854,11 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { joint.parentIndex = nodeQueue.indexOf(nodeDependencies[nodeIndex][0]); } joint.transform = node.transforms.first(); - joint.postTransform = glm::mat4(); - glm::vec3 scale = extractScale(joint.transform); - joint.postTransform[0][0] = scale.x; - joint.postTransform[1][1] = scale.y; - joint.postTransform[2][2] = scale.z; - joint.rotation = glmExtractRotation(joint.transform); joint.translation = extractTranslation(joint.transform); - + joint.rotation = glmExtractRotation(joint.transform); + glm::vec3 scale = extractScale(joint.transform); + joint.postTransform = glm::scale(glm::mat4(), scale); + joint.name = node.name; joint.isSkeletonJoint = false; hfmModel.joints.push_back(joint); @@ -1121,7 +1116,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { const int WEIGHTS_PER_VERTEX = 4; const float ALMOST_HALF = 0.499f; int numVertices = mesh.vertices.size(); - mesh.clusterIndices.fill(0, numClusterIndices); + mesh.clusterIndices.fill(mesh.clusters.size() - 1, numClusterIndices); mesh.clusterWeights.fill(0, numClusterIndices); for (int c = 0; c < clusterJoints.size(); c++) { diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h index e383e6581c..af91a1753d 100755 --- a/libraries/fbx/src/GLTFSerializer.h +++ b/libraries/fbx/src/GLTFSerializer.h @@ -713,7 +713,7 @@ private: glm::mat4 getModelTransform(const GLTFNode& node); std::vector> getSkinInverseBindMatrices(); - std::vector nodeDFS(int n, std::vector& children, bool order); + std::vector nodeDFS(int n, std::vector& children, int stride); bool buildGeometry(HFMModel& hfmModel, const hifi::URL& url); bool parseGLTF(const hifi::ByteArray& data); From 8439019c4eef997a05fe768aad22da3b83b65c6b Mon Sep 17 00:00:00 2001 From: raveenajain Date: Tue, 2 Apr 2019 19:56:17 +0100 Subject: [PATCH 21/28] update use of vectors --- libraries/fbx/src/GLTFSerializer.cpp | 43 ++++++++++++---------------- libraries/fbx/src/GLTFSerializer.h | 4 +-- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index 1192952b9e..84609be250 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -734,8 +734,7 @@ glm::mat4 GLTFSerializer::getModelTransform(const GLTFNode& node) { return tmat; } -std::vector> GLTFSerializer::getSkinInverseBindMatrices() { - std::vector> inverseBindMatrixValues; +void GLTFSerializer::getSkinInverseBindMatrices(std::vector>& inverseBindMatrixValues) { for (auto &skin : _file.skins) { GLTFAccessor& indicesAccessor = _file.accessors[skin.inverseBindMatrices]; GLTFBufferView& indicesBufferview = _file.bufferviews[indicesAccessor.bufferView]; @@ -750,31 +749,28 @@ std::vector> GLTFSerializer::getSkinInverseBindMatrices() { indicesAccessor.componentType); inverseBindMatrixValues.push_back(matrices.toStdVector()); } - return inverseBindMatrixValues; } -std::vector GLTFSerializer::nodeDFS(int n, std::vector& children, int stride) { - std::vector result; - result.push_back(n); - int rootDFS = 0; - int finalDFS = (int)children.size(); +void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector& children, int stride, std::vector& result) { + int startingIndex = 0; + int finalIndex = (int)children.size(); if (stride == -1) { - rootDFS = (int)children.size() - 1; - finalDFS = -1; + startingIndex = (int)children.size() - 1; + finalIndex = -1; } - for (int index = rootDFS; index != finalDFS; index += stride) { + for (int index = startingIndex; index != finalIndex; index += stride) { int c = children[index]; + result.push_back(c); std::vector nested = _file.nodes[c].children.toStdVector(); if (nested.size() != 0) { std::sort(nested.begin(), nested.end()); - for (int n : nodeDFS(c, nested, stride)) { - result.push_back(n); + for (int r : nested) { + if (result.end() == std::find(result.begin(), result.end(), r)) { + getNodeQueueByDepthFirstChildren(nested, stride, result); + } } - } else { - result.push_back(c); - } + } } - return result; } @@ -810,7 +806,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { // initialize order in which nodes will be parsed - QVector nodeQueue; + std::vector nodeQueue; nodeQueue.reserve(numNodes); int rootNode = 0; int finalNode = numNodes; @@ -832,14 +828,12 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { } for (int index = sceneRootNode; index != sceneFinalNode; index += nodeListStride) { int i = initialSceneNodes[index]; + nodeQueue.push_back(i); std::vector children = _file.nodes[i].children.toStdVector(); std::sort(children.begin(), children.end()); - for (int n : nodeDFS(i, children, nodeListStride)) { - nodeQueue.append(n); - } + getNodeQueueByDepthFirstChildren(children, nodeListStride, nodeQueue); } - // Build joints HFMJoint joint; joint.distanceToParent = 0; @@ -851,7 +845,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { joint.parentIndex = -1; if (!_file.scenes[_file.scene].nodes.contains(nodeIndex)) { - joint.parentIndex = nodeQueue.indexOf(nodeDependencies[nodeIndex][0]); + joint.parentIndex = std::distance(nodeQueue.begin(), std::find(nodeQueue.begin(), nodeQueue.end(), nodeDependencies[nodeIndex][0])); } joint.transform = node.transforms.first(); joint.translation = extractTranslation(joint.transform); @@ -869,7 +863,8 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { std::vector jointInverseBindTransforms; if (!_file.skins.isEmpty()) { int matrixIndex = 0; - std::vector> inverseBindValues = getSkinInverseBindMatrices(); + std::vector> inverseBindValues; + getSkinInverseBindMatrices(inverseBindValues); jointInverseBindTransforms.resize(numNodes); int jointIndex = finalNode; diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h index af91a1753d..d9c477bd99 100755 --- a/libraries/fbx/src/GLTFSerializer.h +++ b/libraries/fbx/src/GLTFSerializer.h @@ -712,8 +712,8 @@ private: hifi::ByteArray _glbBinary; glm::mat4 getModelTransform(const GLTFNode& node); - std::vector> getSkinInverseBindMatrices(); - std::vector nodeDFS(int n, std::vector& children, int stride); + void getSkinInverseBindMatrices(std::vector>& inverseBindMatrixValues); + void getNodeQueueByDepthFirstChildren(std::vector& children, int stride, std::vector& result); bool buildGeometry(HFMModel& hfmModel, const hifi::URL& url); bool parseGLTF(const hifi::ByteArray& data); From 14352452793f4e9d1b477c86176276b9e4fdbbcf Mon Sep 17 00:00:00 2001 From: raveenajain Date: Tue, 2 Apr 2019 21:18:16 +0100 Subject: [PATCH 22/28] root cluster --- libraries/fbx/src/GLTFSerializer.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index 84609be250..5e4716c56d 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -861,11 +861,11 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { // Build skeleton std::vector jointInverseBindTransforms; + jointInverseBindTransforms.resize(numNodes); if (!_file.skins.isEmpty()) { int matrixIndex = 0; std::vector> inverseBindValues; getSkinInverseBindMatrices(inverseBindValues); - jointInverseBindTransforms.resize(numNodes); int jointIndex = finalNode; while (jointIndex != rootNode) { @@ -941,6 +941,12 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { mesh.clusters.append(cluster); } } + HFMCluster root; + root.jointIndex = rootNode; + if (root.jointIndex == -1) { root.jointIndex = 0; } + root.inverseBindMatrix = jointInverseBindTransforms[root.jointIndex]; + root.inverseBindTransform = Transform(root.inverseBindMatrix); + mesh.clusters.append(root); HFMMeshPart part = HFMMeshPart(); @@ -1131,6 +1137,8 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) { mesh.clusterWeights[k] = (uint16_t)(weightScalingFactor * clusterWeights[k] + ALMOST_HALF); } + } else { + mesh.clusterWeights[j] = (uint16_t)((float)(UINT16_MAX) + ALMOST_HALF); } } } From 2d71ec1c7581fb6ddb44e8f13b34ce5704e9ed31 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Tue, 2 Apr 2019 13:19:06 -0700 Subject: [PATCH 23/28] Case20410 Automatic Content Archives doesn't restore correctly with old Tutorial content set The tutorial content set had a content set Id of Null, which caused a failure to parse the content set. --- domain-server/src/DomainServer.cpp | 2 +- libraries/octree/src/OctreeEntitiesFileParser.cpp | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 5f82700e9c..400dc3642d 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1734,7 +1734,7 @@ void DomainServer::processOctreeDataPersistMessage(QSharedPointer Date: Wed, 3 Apr 2019 00:09:36 +0100 Subject: [PATCH 24/28] minor syntax fix --- libraries/fbx/src/GLTFSerializer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index 5e4716c56d..1699722215 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -863,7 +863,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { std::vector jointInverseBindTransforms; jointInverseBindTransforms.resize(numNodes); if (!_file.skins.isEmpty()) { - int matrixIndex = 0; std::vector> inverseBindValues; getSkinInverseBindMatrices(inverseBindValues); @@ -886,7 +885,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { value[matrixCount + 4], value[matrixCount + 5], value[matrixCount + 6], value[matrixCount + 7], value[matrixCount + 8], value[matrixCount + 9], value[matrixCount + 10], value[matrixCount + 11], value[matrixCount + 12], value[matrixCount + 13], value[matrixCount + 14], value[matrixCount + 15]); - matrixIndex++; } else { jointInverseBindTransforms[jointIndex] = glm::mat4(); } @@ -943,7 +941,9 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { } HFMCluster root; root.jointIndex = rootNode; - if (root.jointIndex == -1) { root.jointIndex = 0; } + if (root.jointIndex == -1) { + root.jointIndex = 0; + } root.inverseBindMatrix = jointInverseBindTransforms[root.jointIndex]; root.inverseBindTransform = Transform(root.inverseBindMatrix); mesh.clusters.append(root); From ff3e5bbc6129762c1ce0fa18fb3cbad3e759d75a Mon Sep 17 00:00:00 2001 From: danteruiz Date: Tue, 2 Apr 2019 16:20:10 -0700 Subject: [PATCH 25/28] make sure to delete avatar when thier is no reason --- interface/src/avatar/AvatarManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 38bdd061c0..7a64edae11 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -497,7 +497,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar // it might not fire until after we create a new instance for the same remote avatar, which creates a race // on the creation of entities for that avatar instance and the deletion of entities for this instance avatar->removeAvatarEntitiesFromTree(); - if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) { + if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble || removalReason == KillAvatarReason::NoReason) { emit AvatarInputs::getInstance()->avatarEnteredIgnoreRadius(avatar->getSessionUUID()); emit DependencyManager::get()->enteredIgnoreRadius(); From ac97a82badc5ed679c650f4a6399e5863fec41a6 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Tue, 2 Apr 2019 17:25:00 -0700 Subject: [PATCH 26/28] Address a CR comment. --- .../octree/src/OctreeEntitiesFileParser.cpp | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/libraries/octree/src/OctreeEntitiesFileParser.cpp b/libraries/octree/src/OctreeEntitiesFileParser.cpp index 8e3fc76e2c..e82201adfd 100644 --- a/libraries/octree/src/OctreeEntitiesFileParser.cpp +++ b/libraries/octree/src/OctreeEntitiesFileParser.cpp @@ -108,22 +108,20 @@ bool OctreeEntitiesFileParser::parseEntities(QVariantMap& parsedEntities) { return false; } - if (idString == "{00000000-0000-0000-0000-000000000000}") { - // some older archives may have a null string id, so - // return success without setting parsedEntities, - // which will result in a new uuid for the restored - // archive. (not parsing and using isNull as parsing - // results in null if there is a corrupt string) - return true; - } + // some older archives may have a null string id, so + // return success without setting parsedEntities, + // which will result in a new uuid for the restored + // archive. (not parsing and using isNull as parsing + // results in null if there is a corrupt string) - QUuid idValue = QUuid::fromString(QLatin1String(idString.c_str()) ); - if (idValue.isNull()) { - _errorString = "Id value invalid UUID string: " + idString; - return false; + if (idString != "{00000000-0000-0000-0000-000000000000}") { + QUuid idValue = QUuid::fromString(QLatin1String(idString.c_str()) ); + if (idValue.isNull()) { + _errorString = "Id value invalid UUID string: " + idString; + return false; + } + parsedEntities["Id"] = idValue; } - - parsedEntities["Id"] = idValue; } else if (key == "Version") { if (gotVersion) { _errorString = "Duplicate Version entries"; From 1860e61a3014be9cee540b2765e099485abf2dd9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 3 Apr 2019 07:42:56 -0700 Subject: [PATCH 27/28] more correct collision group during grabs --- libraries/entities/src/EntityItem.cpp | 29 +++++---------------- libraries/entities/src/EntityItem.h | 2 +- libraries/physics/src/EntityMotionState.cpp | 5 ++-- 3 files changed, 10 insertions(+), 26 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index bd4c6e5c71..8a50c39da9 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1698,10 +1698,7 @@ AACube EntityItem::getQueryAACube(bool& success) const { } bool EntityItem::shouldPuffQueryAACube() const { - bool hasGrabs = _grabsLock.resultWithReadLock([&] { - return _grabs.count() > 0; - }); - return hasActions() || isChildOfMyAvatar() || isMovingRelativeToParent() || hasGrabs; + return hasActions() || isChildOfMyAvatar() || isMovingRelativeToParent(); } // TODO: get rid of all users of this function... @@ -1896,7 +1893,8 @@ void EntityItem::setScaledDimensions(const glm::vec3& value) { void EntityItem::setUnscaledDimensions(const glm::vec3& value) { glm::vec3 newDimensions = glm::max(value, glm::vec3(ENTITY_ITEM_MIN_DIMENSION)); - if (getUnscaledDimensions() != newDimensions) { + const float MIN_SCALE_CHANGE_SQUARED = 1.0e-6f; + if (glm::length2(getUnscaledDimensions() - newDimensions) > MIN_SCALE_CHANGE_SQUARED) { withWriteLock([&] { _unscaledDimensions = newDimensions; }); @@ -2086,7 +2084,7 @@ void EntityItem::computeCollisionGroupAndFinalMask(int32_t& group, int32_t& mask } else { if (getDynamic()) { group = BULLET_COLLISION_GROUP_DYNAMIC; - } else if (isMovingRelativeToParent() || hasActions()) { + } else if (hasActions() || isMovingRelativeToParent()) { group = BULLET_COLLISION_GROUP_KINEMATIC; } else { group = BULLET_COLLISION_GROUP_STATIC; @@ -3057,30 +3055,18 @@ bool EntityItem::getCollisionless() const { } uint16_t EntityItem::getCollisionMask() const { - uint16_t result; - withReadLock([&] { - result = _collisionMask; - }); - return result; + return _collisionMask; } bool EntityItem::getDynamic() const { if (SHAPE_TYPE_STATIC_MESH == getShapeType()) { return false; } - bool result; - withReadLock([&] { - result = _dynamic; - }); - return result; + return _dynamic; } bool EntityItem::getLocked() const { - bool result; - withReadLock([&] { - result = _locked; - }); - return result; + return _locked; } void EntityItem::setLocked(bool value) { @@ -3152,7 +3138,6 @@ uint32_t EntityItem::getDirtyFlags() const { return result; } - void EntityItem::markDirtyFlags(uint32_t mask) { withWriteLock([&] { mask &= Simulation::DIRTY_FLAGS; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 01ed949a0c..c57fd16a2e 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -448,7 +448,7 @@ public: bool clearActions(EntitySimulationPointer simulation); void setDynamicData(QByteArray dynamicData); const QByteArray getDynamicData() const; - bool hasActions() const { return !_objectActions.empty(); } + bool hasActions() const { return !_objectActions.empty() || !_grabActions.empty(); } QList getActionIDs() const { return _objectActions.keys(); } QVariantMap getActionArguments(const QUuid& actionID) const; void deserializeActions(); diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 4d210c96c5..6abe5c3899 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -217,9 +217,8 @@ PhysicsMotionType EntityMotionState::computePhysicsMotionType() const { } return MOTION_TYPE_DYNAMIC; } - if (_entity->isMovingRelativeToParent() || - _entity->hasActions() || - _entity->hasGrabs() || + if (_entity->hasActions() || + _entity->isMovingRelativeToParent() || _entity->hasAncestorOfType(NestableType::Avatar)) { return MOTION_TYPE_KINEMATIC; } From 44fc0d21dbc6542fcb56e26c86f02bed059d6e23 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 3 Apr 2019 13:43:39 -0700 Subject: [PATCH 28/28] Revert "Remove _compositeFramebuffer from display plugins" This reverts commit cb311408c68f2d465a80d76e0fdfe979cde96e13. --- .../Basic2DWindowOpenGLDisplayPlugin.cpp | 6 +- .../Basic2DWindowOpenGLDisplayPlugin.h | 2 +- .../display-plugins/OpenGLDisplayPlugin.cpp | 175 ++++++++++-------- .../src/display-plugins/OpenGLDisplayPlugin.h | 28 +-- .../hmd/DebugHmdDisplayPlugin.h | 2 +- .../display-plugins/hmd/HmdDisplayPlugin.cpp | 64 ++++--- .../display-plugins/hmd/HmdDisplayPlugin.h | 11 +- .../stereo/InterleavedStereoDisplayPlugin.cpp | 4 +- .../stereo/InterleavedStereoDisplayPlugin.h | 2 +- .../src/OculusMobileDisplayPlugin.cpp | 10 +- .../src/OculusMobileDisplayPlugin.h | 4 +- .../plugins/src/plugins/DisplayPlugin.cpp | 12 +- libraries/plugins/src/plugins/DisplayPlugin.h | 8 +- .../render-utils/src/RenderCommonTask.cpp | 4 +- libraries/render/src/render/Args.h | 2 +- plugins/oculus/src/OculusDebugDisplayPlugin.h | 2 +- plugins/oculus/src/OculusDisplayPlugin.cpp | 28 +-- plugins/oculus/src/OculusDisplayPlugin.h | 2 +- .../src/OculusLegacyDisplayPlugin.cpp | 4 +- .../src/OculusLegacyDisplayPlugin.h | 2 +- plugins/openvr/src/OpenVrDisplayPlugin.cpp | 16 +- plugins/openvr/src/OpenVrDisplayPlugin.h | 4 +- 22 files changed, 211 insertions(+), 181 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index f55f5919f5..9828a8beda 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -109,7 +109,7 @@ bool Basic2DWindowOpenGLDisplayPlugin::internalActivate() { return Parent::internalActivate(); } -void Basic2DWindowOpenGLDisplayPlugin::compositeExtra(const gpu::FramebufferPointer& compositeFramebuffer) { +void Basic2DWindowOpenGLDisplayPlugin::compositeExtra() { #if defined(Q_OS_ANDROID) auto& virtualPadManager = VirtualPad::Manager::instance(); if(virtualPadManager.getLeftVirtualPad()->isShown()) { @@ -121,7 +121,7 @@ void Basic2DWindowOpenGLDisplayPlugin::compositeExtra(const gpu::FramebufferPoin render([&](gpu::Batch& batch) { batch.enableStereo(false); - batch.setFramebuffer(compositeFramebuffer); + batch.setFramebuffer(_compositeFramebuffer); batch.resetViewTransform(); batch.setProjectionTransform(mat4()); batch.setPipeline(_cursorPipeline); @@ -140,7 +140,7 @@ void Basic2DWindowOpenGLDisplayPlugin::compositeExtra(const gpu::FramebufferPoin }); } #endif - Parent::compositeExtra(compositeFramebuffer); + Parent::compositeExtra(); } static const uint32_t MIN_THROTTLE_CHECK_FRAMES = 60; diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h index d4c321a571..cc304c19c2 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h @@ -33,7 +33,7 @@ public: virtual bool isThrottled() const override; - virtual void compositeExtra(const gpu::FramebufferPointer&) override; + virtual void compositeExtra() override; virtual void pluginUpdate() override {}; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 5bc84acc6a..c536e6b6e2 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -379,6 +379,14 @@ void OpenGLDisplayPlugin::customizeContext() { scissorState->setDepthTest(gpu::State::DepthTest(false)); scissorState->setScissorEnable(true); + { +#ifdef Q_OS_ANDROID + gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureGammaLinearToSRGB); +#else + gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTexture); +#endif + _simplePipeline = gpu::Pipeline::create(program, scissorState); + } { #ifdef Q_OS_ANDROID gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureGammaLinearToSRGB); @@ -388,59 +396,29 @@ void OpenGLDisplayPlugin::customizeContext() { _presentPipeline = gpu::Pipeline::create(program, scissorState); } - - // HUD operator { - gpu::PipelinePointer hudPipeline; - { - gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTexture); - hudPipeline = gpu::Pipeline::create(program, blendState); - } - - gpu::PipelinePointer hudMirrorPipeline; - { - gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureMirroredX); - hudMirrorPipeline = gpu::Pipeline::create(program, blendState); - } - - - _hudOperator = [=](gpu::Batch& batch, const gpu::TexturePointer& hudTexture, const gpu::FramebufferPointer& compositeFramebuffer, bool mirror) { - auto hudStereo = isStereo(); - auto hudCompositeFramebufferSize = compositeFramebuffer->getSize(); - std::array hudEyeViewports; - for_each_eye([&](Eye eye) { - hudEyeViewports[eye] = eyeViewport(eye); - }); - if (hudPipeline && hudTexture) { - batch.enableStereo(false); - batch.setPipeline(mirror ? hudMirrorPipeline : hudPipeline); - batch.setResourceTexture(0, hudTexture); - if (hudStereo) { - for_each_eye([&](Eye eye) { - batch.setViewportTransform(hudEyeViewports[eye]); - batch.draw(gpu::TRIANGLE_STRIP, 4); - }); - } else { - batch.setViewportTransform(ivec4(uvec2(0), hudCompositeFramebufferSize)); - batch.draw(gpu::TRIANGLE_STRIP, 4); - } - } - }; - + gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTexture); + _hudPipeline = gpu::Pipeline::create(program, blendState); + } + { + gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureMirroredX); + _mirrorHUDPipeline = gpu::Pipeline::create(program, blendState); } - { gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTransformedTexture); _cursorPipeline = gpu::Pipeline::create(program, blendState); } } + updateCompositeFramebuffer(); } void OpenGLDisplayPlugin::uncustomizeContext() { _presentPipeline.reset(); _cursorPipeline.reset(); - _hudOperator = DEFAULT_HUD_OPERATOR; + _hudPipeline.reset(); + _mirrorHUDPipeline.reset(); + _compositeFramebuffer.reset(); withPresentThreadLock([&] { _currentFrame.reset(); _lastFrame = nullptr; @@ -532,16 +510,24 @@ void OpenGLDisplayPlugin::captureFrame(const std::string& filename) const { }); } +void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor) { + renderFromTexture(batch, texture, viewport, scissor, nullptr); +} -void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& destFbo, const gpu::FramebufferPointer& copyFbo /*=gpu::FramebufferPointer()*/) { +void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& copyFbo /*=gpu::FramebufferPointer()*/) { + auto fbo = gpu::FramebufferPointer(); batch.enableStereo(false); batch.resetViewTransform(); - batch.setFramebuffer(destFbo); + batch.setFramebuffer(fbo); batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); batch.setStateScissorRect(scissor); batch.setViewportTransform(viewport); batch.setResourceTexture(0, texture); +#ifndef USE_GLES batch.setPipeline(_presentPipeline); +#else + batch.setPipeline(_simplePipeline); +#endif batch.draw(gpu::TRIANGLE_STRIP, 4); if (copyFbo) { gpu::Vec4i copyFboRect(0, 0, copyFbo->getWidth(), copyFbo->getHeight()); @@ -567,7 +553,7 @@ void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::Textur batch.setViewportTransform(copyFboRect); batch.setStateScissorRect(copyFboRect); batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, {0.0f, 0.0f, 0.0f, 1.0f}); - batch.blit(destFbo, sourceRect, copyFbo, copyRect); + batch.blit(fbo, sourceRect, copyFbo, copyRect); } } @@ -595,14 +581,41 @@ void OpenGLDisplayPlugin::updateFrameData() { }); } -void OpenGLDisplayPlugin::compositePointer(const gpu::FramebufferPointer& compositeFramebuffer) { +std::function OpenGLDisplayPlugin::getHUDOperator() { + auto hudPipeline = _hudPipeline; + auto hudMirrorPipeline = _mirrorHUDPipeline; + auto hudStereo = isStereo(); + auto hudCompositeFramebufferSize = _compositeFramebuffer->getSize(); + std::array hudEyeViewports; + for_each_eye([&](Eye eye) { + hudEyeViewports[eye] = eyeViewport(eye); + }); + return [=](gpu::Batch& batch, const gpu::TexturePointer& hudTexture, bool mirror) { + if (hudPipeline && hudTexture) { + batch.enableStereo(false); + batch.setPipeline(mirror ? hudMirrorPipeline : hudPipeline); + batch.setResourceTexture(0, hudTexture); + if (hudStereo) { + for_each_eye([&](Eye eye) { + batch.setViewportTransform(hudEyeViewports[eye]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); + } else { + batch.setViewportTransform(ivec4(uvec2(0), hudCompositeFramebufferSize)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + } + } + }; +} + +void OpenGLDisplayPlugin::compositePointer() { auto& cursorManager = Cursor::Manager::instance(); const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()]; auto cursorTransform = DependencyManager::get()->getReticleTransform(glm::mat4()); render([&](gpu::Batch& batch) { batch.enableStereo(false); batch.setProjectionTransform(mat4()); - batch.setFramebuffer(compositeFramebuffer); + batch.setFramebuffer(_compositeFramebuffer); batch.setPipeline(_cursorPipeline); batch.setResourceTexture(0, cursorData.texture); batch.resetViewTransform(); @@ -613,13 +626,34 @@ void OpenGLDisplayPlugin::compositePointer(const gpu::FramebufferPointer& compos batch.draw(gpu::TRIANGLE_STRIP, 4); }); } else { - batch.setViewportTransform(ivec4(uvec2(0), compositeFramebuffer->getSize())); + batch.setViewportTransform(ivec4(uvec2(0), _compositeFramebuffer->getSize())); batch.draw(gpu::TRIANGLE_STRIP, 4); } }); } -void OpenGLDisplayPlugin::compositeLayers(const gpu::FramebufferPointer& compositeFramebuffer) { +void OpenGLDisplayPlugin::compositeScene() { + render([&](gpu::Batch& batch) { + batch.enableStereo(false); + batch.setFramebuffer(_compositeFramebuffer); + batch.setViewportTransform(ivec4(uvec2(), _compositeFramebuffer->getSize())); + batch.setStateScissorRect(ivec4(uvec2(), _compositeFramebuffer->getSize())); + batch.resetViewTransform(); + batch.setProjectionTransform(mat4()); + batch.setPipeline(_simplePipeline); + batch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); +} + +void OpenGLDisplayPlugin::compositeLayers() { + updateCompositeFramebuffer(); + + { + PROFILE_RANGE_EX(render_detail, "compositeScene", 0xff0077ff, (uint64_t)presentCount()) + compositeScene(); + } + #ifdef HIFI_ENABLE_NSIGHT_DEBUG if (false) // do not draw the HUD if running nsight debug #endif @@ -633,35 +667,23 @@ void OpenGLDisplayPlugin::compositeLayers(const gpu::FramebufferPointer& composi { PROFILE_RANGE_EX(render_detail, "compositeExtra", 0xff0077ff, (uint64_t)presentCount()) - compositeExtra(compositeFramebuffer); + compositeExtra(); } // Draw the pointer last so it's on top of everything auto compositorHelper = DependencyManager::get(); if (compositorHelper->getReticleVisible()) { PROFILE_RANGE_EX(render_detail, "compositePointer", 0xff0077ff, (uint64_t)presentCount()) - compositePointer(compositeFramebuffer); + compositePointer(); } } -void OpenGLDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compositeFramebuffer) { +void OpenGLDisplayPlugin::internalPresent() { render([&](gpu::Batch& batch) { // Note: _displayTexture must currently be the same size as the display. uvec2 dims = _displayTexture ? uvec2(_displayTexture->getDimensions()) : getSurfacePixels(); auto viewport = ivec4(uvec2(0), dims); - - gpu::TexturePointer finalTexture; - if (_displayTexture) { - finalTexture = _displayTexture; - } else if (compositeFramebuffer) { - finalTexture = compositeFramebuffer->getRenderBuffer(0); - } else { - qCWarning(displayPlugins) << "No valid texture for output"; - } - - if (finalTexture) { - renderFromTexture(batch, finalTexture, viewport, viewport); - } + renderFromTexture(batch, _displayTexture ? _displayTexture : _compositeFramebuffer->getRenderBuffer(0), viewport, viewport); }); swapBuffers(); _presentRate.increment(); @@ -678,7 +700,7 @@ void OpenGLDisplayPlugin::present() { } incrementPresentCount(); - if (_currentFrame && _currentFrame->framebuffer) { + if (_currentFrame) { auto correction = getViewCorrection(); getGLBackend()->setCameraCorrection(correction, _prevRenderView); _prevRenderView = correction * _currentFrame->view; @@ -698,18 +720,18 @@ void OpenGLDisplayPlugin::present() { // Write all layers to a local framebuffer { PROFILE_RANGE_EX(render, "composite", 0xff00ffff, frameId) - compositeLayers(_currentFrame->framebuffer); + compositeLayers(); } // Take the composite framebuffer and send it to the output device { PROFILE_RANGE_EX(render, "internalPresent", 0xff00ffff, frameId) - internalPresent(_currentFrame->framebuffer); + internalPresent(); } gpu::Backend::freeGPUMemSize.set(gpu::gl::getFreeDedicatedMemory()); } else if (alwaysPresent()) { - internalPresent(nullptr); + internalPresent(); } _movingAveragePresent.addSample((float)(usecTimestampNow() - startPresent)); } @@ -766,12 +788,7 @@ bool OpenGLDisplayPlugin::setDisplayTexture(const QString& name) { } QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const { - if (!_currentFrame || !_currentFrame->framebuffer) { - return QImage(); - } - - auto compositeFramebuffer = _currentFrame->framebuffer; - auto size = compositeFramebuffer->getSize(); + auto size = _compositeFramebuffer->getSize(); if (isHmd()) { size.x /= 2; } @@ -789,7 +806,7 @@ QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const { auto glBackend = const_cast(*this).getGLBackend(); QImage screenshot(bestSize.x, bestSize.y, QImage::Format_ARGB32); withOtherThreadContext([&] { - glBackend->downloadFramebuffer(compositeFramebuffer, ivec4(corner, bestSize), screenshot); + glBackend->downloadFramebuffer(_compositeFramebuffer, ivec4(corner, bestSize), screenshot); }); return screenshot.mirrored(false, true); } @@ -841,7 +858,7 @@ bool OpenGLDisplayPlugin::beginFrameRender(uint32_t frameIndex) { } ivec4 OpenGLDisplayPlugin::eyeViewport(Eye eye) const { - auto vpSize = glm::uvec2(getRecommendedRenderSize()); + uvec2 vpSize = _compositeFramebuffer->getSize(); vpSize.x /= 2; uvec2 vpPos; if (eye == Eye::Right) { @@ -874,6 +891,14 @@ void OpenGLDisplayPlugin::render(std::function f) { OpenGLDisplayPlugin::~OpenGLDisplayPlugin() { } +void OpenGLDisplayPlugin::updateCompositeFramebuffer() { + auto renderSize = glm::uvec2(getRecommendedRenderSize()); + if (!_compositeFramebuffer || _compositeFramebuffer->getSize() != renderSize) { + _compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OpenGLDisplayPlugin::composite", gpu::Element::COLOR_RGBA_32, renderSize.x, renderSize.y)); + // _compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OpenGLDisplayPlugin::composite", gpu::Element::COLOR_SRGBA_32, renderSize.x, renderSize.y)); + } +} + void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer networkTexture, QOpenGLFramebufferObject* target, GLsync* fenceSync) { #if !defined(USE_GLES) auto glBackend = const_cast(*this).getGLBackend(); diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 3c48e8fc48..49a38ecb4c 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -94,10 +94,14 @@ protected: // is not populated virtual bool alwaysPresent() const { return false; } + void updateCompositeFramebuffer(); + virtual QThread::Priority getPresentPriority() { return QThread::HighPriority; } - virtual void compositeLayers(const gpu::FramebufferPointer&); - virtual void compositePointer(const gpu::FramebufferPointer&); - virtual void compositeExtra(const gpu::FramebufferPointer&) {}; + virtual void compositeLayers(); + virtual void compositeScene(); + virtual std::function getHUDOperator(); + virtual void compositePointer(); + virtual void compositeExtra() {}; // These functions must only be called on the presentation thread virtual void customizeContext(); @@ -112,10 +116,10 @@ protected: virtual void deactivateSession() {} // Plugin specific functionality to send the composed scene to the output window or device - virtual void internalPresent(const gpu::FramebufferPointer&); + virtual void internalPresent(); - - void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& destFbo = nullptr, const gpu::FramebufferPointer& copyFbo = nullptr); + void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& fbo); + void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor); virtual void updateFrameData(); virtual glm::mat4 getViewCorrection() { return glm::mat4(); } @@ -138,8 +142,14 @@ protected: gpu::FramePointer _currentFrame; gpu::Frame* _lastFrame { nullptr }; mat4 _prevRenderView; + gpu::FramebufferPointer _compositeFramebuffer; + gpu::PipelinePointer _hudPipeline; + gpu::PipelinePointer _mirrorHUDPipeline; + gpu::ShaderPointer _mirrorHUDPS; + gpu::PipelinePointer _simplePipeline; + gpu::PipelinePointer _presentPipeline; gpu::PipelinePointer _cursorPipeline; - gpu::TexturePointer _displayTexture; + gpu::TexturePointer _displayTexture{}; float _compositeHUDAlpha { 1.0f }; struct CursorData { @@ -175,9 +185,5 @@ protected: // be serialized through this mutex mutable Mutex _presentMutex; float _hudAlpha{ 1.0f }; - -private: - gpu::PipelinePointer _presentPipeline; - }; diff --git a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h index 95592cc490..f2b1f36419 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h @@ -24,7 +24,7 @@ public: protected: void updatePresentPose() override; - void hmdPresent(const gpu::FramebufferPointer&) override {} + void hmdPresent() override {} bool isHmdMounted() const override { return true; } bool internalActivate() override; private: diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index a515232b3f..321bcc3fd2 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -114,23 +114,20 @@ void HmdDisplayPlugin::internalDeactivate() { void HmdDisplayPlugin::customizeContext() { Parent::customizeContext(); - _hudOperator = _hudRenderer.build(); + _hudRenderer.build(); } void HmdDisplayPlugin::uncustomizeContext() { // This stops the weirdness where if the preview was disabled, on switching back to 2D, // the vsync was stuck in the disabled state. No idea why that happens though. _disablePreview = false; - if (_currentFrame && _currentFrame->framebuffer) { - render([&](gpu::Batch& batch) { - batch.enableStereo(false); - batch.resetViewTransform(); - batch.setFramebuffer(_currentFrame->framebuffer); - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); - }); - - } - _hudRenderer = {}; + render([&](gpu::Batch& batch) { + batch.enableStereo(false); + batch.resetViewTransform(); + batch.setFramebuffer(_compositeFramebuffer); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); + }); + _hudRenderer = HUDRenderer(); _previewTexture.reset(); Parent::uncustomizeContext(); } @@ -177,11 +174,11 @@ float HmdDisplayPlugin::getLeftCenterPixel() const { return leftCenterPixel; } -void HmdDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compositeFramebuffer) { +void HmdDisplayPlugin::internalPresent() { PROFILE_RANGE_EX(render, __FUNCTION__, 0xff00ff00, (uint64_t)presentCount()) // Composite together the scene, hud and mouse cursor - hmdPresent(compositeFramebuffer); + hmdPresent(); if (_displayTexture) { // Note: _displayTexture must currently be the same size as the display. @@ -263,7 +260,7 @@ void HmdDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compositeF viewport.z *= 2; } - renderFromTexture(batch, compositeFramebuffer->getRenderBuffer(0), viewport, scissor, nullptr, fbo); + renderFromTexture(batch, _compositeFramebuffer->getRenderBuffer(0), viewport, scissor, fbo); }); swapBuffers(); @@ -348,7 +345,7 @@ glm::mat4 HmdDisplayPlugin::getViewCorrection() { } } -DisplayPlugin::HUDOperator HmdDisplayPlugin::HUDRenderer::build() { +void HmdDisplayPlugin::HUDRenderer::build() { vertices = std::make_shared(); indices = std::make_shared(); @@ -383,7 +380,7 @@ DisplayPlugin::HUDOperator HmdDisplayPlugin::HUDRenderer::build() { indexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE; // Compute indices order - std::vector indexData; + std::vector indices; for (int i = 0; i < stacks - 1; i++) { for (int j = 0; j < slices - 1; j++) { GLushort bottomLeftIndex = i * slices + j; @@ -391,21 +388,24 @@ DisplayPlugin::HUDOperator HmdDisplayPlugin::HUDRenderer::build() { GLushort topLeftIndex = bottomLeftIndex + slices; GLushort topRightIndex = topLeftIndex + 1; // FIXME make a z-order curve for better vertex cache locality - indexData.push_back(topLeftIndex); - indexData.push_back(bottomLeftIndex); - indexData.push_back(topRightIndex); + indices.push_back(topLeftIndex); + indices.push_back(bottomLeftIndex); + indices.push_back(topRightIndex); - indexData.push_back(topRightIndex); - indexData.push_back(bottomLeftIndex); - indexData.push_back(bottomRightIndex); + indices.push_back(topRightIndex); + indices.push_back(bottomLeftIndex); + indices.push_back(bottomRightIndex); } } - indices->append(indexData); + this->indices->append(indices); format = std::make_shared(); // 1 for everyone format->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); format->setAttribute(gpu::Stream::TEXCOORD, gpu::Stream::TEXCOORD, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); uniformsBuffer = std::make_shared(sizeof(Uniforms), nullptr); + updatePipeline(); +} +void HmdDisplayPlugin::HUDRenderer::updatePipeline() { if (!pipeline) { auto program = gpu::Shader::createProgram(shader::render_utils::program::hmd_ui); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); @@ -416,6 +416,10 @@ DisplayPlugin::HUDOperator HmdDisplayPlugin::HUDRenderer::build() { pipeline = gpu::Pipeline::create(program, state); } +} + +std::function HmdDisplayPlugin::HUDRenderer::render(HmdDisplayPlugin& plugin) { + updatePipeline(); auto hudPipeline = pipeline; auto hudFormat = format; @@ -424,9 +428,9 @@ DisplayPlugin::HUDOperator HmdDisplayPlugin::HUDRenderer::build() { auto hudUniformBuffer = uniformsBuffer; auto hudUniforms = uniforms; auto hudIndexCount = indexCount; - return [=](gpu::Batch& batch, const gpu::TexturePointer& hudTexture, const gpu::FramebufferPointer&, const bool mirror) { - if (pipeline && hudTexture) { - batch.setPipeline(pipeline); + return [=](gpu::Batch& batch, const gpu::TexturePointer& hudTexture, bool mirror) { + if (hudPipeline && hudTexture) { + batch.setPipeline(hudPipeline); batch.setInputFormat(hudFormat); gpu::BufferView posView(hudVertices, VERTEX_OFFSET, hudVertices->getSize(), VERTEX_STRIDE, hudFormat->getAttributes().at(gpu::Stream::POSITION)._element); @@ -450,7 +454,7 @@ DisplayPlugin::HUDOperator HmdDisplayPlugin::HUDRenderer::build() { }; } -void HmdDisplayPlugin::compositePointer(const gpu::FramebufferPointer& compositeFramebuffer) { +void HmdDisplayPlugin::compositePointer() { auto& cursorManager = Cursor::Manager::instance(); const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()]; auto compositorHelper = DependencyManager::get(); @@ -459,7 +463,7 @@ void HmdDisplayPlugin::compositePointer(const gpu::FramebufferPointer& composite render([&](gpu::Batch& batch) { // FIXME use standard gpu stereo rendering for this. batch.enableStereo(false); - batch.setFramebuffer(compositeFramebuffer); + batch.setFramebuffer(_compositeFramebuffer); batch.setPipeline(_cursorPipeline); batch.setResourceTexture(0, cursorData.texture); batch.resetViewTransform(); @@ -474,6 +478,10 @@ void HmdDisplayPlugin::compositePointer(const gpu::FramebufferPointer& composite }); } +std::function HmdDisplayPlugin::getHUDOperator() { + return _hudRenderer.render(*this); +} + HmdDisplayPlugin::~HmdDisplayPlugin() { } diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h index 6755c5b7e0..d8c0ce8e1d 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h @@ -53,15 +53,16 @@ signals: void hmdVisibleChanged(bool visible); protected: - virtual void hmdPresent(const gpu::FramebufferPointer&) = 0; + virtual void hmdPresent() = 0; virtual bool isHmdMounted() const = 0; virtual void postPreview() {}; virtual void updatePresentPose(); bool internalActivate() override; void internalDeactivate() override; - void compositePointer(const gpu::FramebufferPointer&) override; - void internalPresent(const gpu::FramebufferPointer&) override; + std::function getHUDOperator() override; + void compositePointer() override; + void internalPresent() override; void customizeContext() override; void uncustomizeContext() override; void updateFrameData() override; @@ -119,6 +120,8 @@ private: static const size_t TEXTURE_OFFSET { offsetof(Vertex, uv) }; static const int VERTEX_STRIDE { sizeof(Vertex) }; - HUDOperator build(); + void build(); + void updatePipeline(); + std::function render(HmdDisplayPlugin& plugin); } _hudRenderer; }; diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp index 69aa7fc344..0ae0f9b1b6 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp @@ -37,13 +37,13 @@ glm::uvec2 InterleavedStereoDisplayPlugin::getRecommendedRenderSize() const { return result; } -void InterleavedStereoDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compositeFramebuffer) { +void InterleavedStereoDisplayPlugin::internalPresent() { render([&](gpu::Batch& batch) { batch.enableStereo(false); batch.resetViewTransform(); batch.setFramebuffer(gpu::FramebufferPointer()); batch.setViewportTransform(ivec4(uvec2(0), getSurfacePixels())); - batch.setResourceTexture(0, compositeFramebuffer->getRenderBuffer(0)); + batch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0)); batch.setPipeline(_interleavedPresentPipeline); batch.draw(gpu::TRIANGLE_STRIP, 4); }); diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h index 52dfa8f402..debd340f24 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h @@ -21,7 +21,7 @@ protected: // initialize OpenGL context settings needed by the plugin void customizeContext() override; void uncustomizeContext() override; - void internalPresent(const gpu::FramebufferPointer&) override; + void internalPresent() override; private: static const QString NAME; diff --git a/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.cpp b/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.cpp index 12a9b12adc..9809d02866 100644 --- a/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.cpp +++ b/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.cpp @@ -245,7 +245,7 @@ void OculusMobileDisplayPlugin::updatePresentPose() { }); } -void OculusMobileDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compsiteFramebuffer) { +void OculusMobileDisplayPlugin::internalPresent() { VrHandler::pollTask(); if (!vrActive()) { @@ -253,12 +253,8 @@ void OculusMobileDisplayPlugin::internalPresent(const gpu::FramebufferPointer& c return; } - GLuint sourceTexture = 0; - glm::uvec2 sourceSize; - if (compsiteFramebuffer) { - sourceTexture = getGLBackend()->getTextureID(compsiteFramebuffer->getRenderBuffer(0)); - sourceSize = { compsiteFramebuffer->getWidth(), compsiteFramebuffer->getHeight() }; - } + auto sourceTexture = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0)); + glm::uvec2 sourceSize{ _compositeFramebuffer->getWidth(), _compositeFramebuffer->getHeight() }; VrHandler::presentFrame(sourceTexture, sourceSize, presentTracking); _presentRate.increment(); } diff --git a/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.h b/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.h index b5f7aa57b0..a98989655e 100644 --- a/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.h +++ b/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.h @@ -54,8 +54,8 @@ protected: void uncustomizeContext() override; void updatePresentPose() override; - void internalPresent(const gpu::FramebufferPointer&) override; - void hmdPresent(const gpu::FramebufferPointer&) override { throw std::runtime_error("Unused"); } + void internalPresent() override; + void hmdPresent() override { throw std::runtime_error("Unused"); } bool isHmdMounted() const override; bool alwaysPresent() const override { return true; } diff --git a/libraries/plugins/src/plugins/DisplayPlugin.cpp b/libraries/plugins/src/plugins/DisplayPlugin.cpp index 71db87557c..47503e8f85 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.cpp +++ b/libraries/plugins/src/plugins/DisplayPlugin.cpp @@ -2,12 +2,6 @@ #include - -const DisplayPlugin::HUDOperator DisplayPlugin::DEFAULT_HUD_OPERATOR{ std::function() }; - -DisplayPlugin::DisplayPlugin() : _hudOperator{ DEFAULT_HUD_OPERATOR } { -} - int64_t DisplayPlugin::getPaintDelayUsecs() const { std::lock_guard lock(_paintDelayMutex); return _paintDelayTimer.isValid() ? _paintDelayTimer.nsecsElapsed() / NSECS_PER_USEC : 0; @@ -41,8 +35,8 @@ void DisplayPlugin::waitForPresent() { } } -std::function DisplayPlugin::getHUDOperator() { - HUDOperator hudOperator; +std::function DisplayPlugin::getHUDOperator() { + std::function hudOperator; { QMutexLocker locker(&_presentMutex); hudOperator = _hudOperator; @@ -54,5 +48,3 @@ glm::mat4 HmdDisplay::getEyeToHeadTransform(Eye eye) const { static const glm::mat4 xform; return xform; } - - diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index 9194fde3ac..aa52e57c3f 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -121,8 +121,6 @@ class DisplayPlugin : public Plugin, public HmdDisplay { Q_OBJECT using Parent = Plugin; public: - DisplayPlugin(); - virtual int getRequiredThreadCount() const { return 0; } virtual bool isHmd() const { return false; } virtual int getHmdScreen() const { return -1; } @@ -216,8 +214,7 @@ public: void waitForPresent(); float getAveragePresentTime() { return _movingAveragePresent.average / (float)USECS_PER_MSEC; } // in msec - using HUDOperator = std::function; - virtual HUDOperator getHUDOperator() final; + std::function getHUDOperator(); static const QString& MENU_PATH(); @@ -234,8 +231,7 @@ protected: gpu::ContextPointer _gpuContext; - static const HUDOperator DEFAULT_HUD_OPERATOR; - HUDOperator _hudOperator; + std::function _hudOperator { std::function() }; MovingAverage _movingAveragePresent; diff --git a/libraries/render-utils/src/RenderCommonTask.cpp b/libraries/render-utils/src/RenderCommonTask.cpp index e021465ff3..b1a62625b2 100644 --- a/libraries/render-utils/src/RenderCommonTask.cpp +++ b/libraries/render-utils/src/RenderCommonTask.cpp @@ -126,8 +126,8 @@ void CompositeHUD::run(const RenderContextPointer& renderContext, const gpu::Fra if (inputs) { batch.setFramebuffer(inputs); } - if (renderContext->args->_hudOperator && renderContext->args->_blitFramebuffer) { - renderContext->args->_hudOperator(batch, renderContext->args->_hudTexture, renderContext->args->_blitFramebuffer, renderContext->args->_renderMode == RenderArgs::RenderMode::MIRROR_RENDER_MODE); + if (renderContext->args->_hudOperator) { + renderContext->args->_hudOperator(batch, renderContext->args->_hudTexture, renderContext->args->_renderMode == RenderArgs::RenderMode::MIRROR_RENDER_MODE); } }); #endif diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index 8b2fff68c6..b5c98e3428 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -131,7 +131,7 @@ namespace render { render::ScenePointer _scene; int8_t _cameraMode { -1 }; - std::function _hudOperator; + std::function _hudOperator; gpu::TexturePointer _hudTexture; }; diff --git a/plugins/oculus/src/OculusDebugDisplayPlugin.h b/plugins/oculus/src/OculusDebugDisplayPlugin.h index 690a488b34..ec05cd92e2 100644 --- a/plugins/oculus/src/OculusDebugDisplayPlugin.h +++ b/plugins/oculus/src/OculusDebugDisplayPlugin.h @@ -16,7 +16,7 @@ public: bool isSupported() const override; protected: - void hmdPresent(const gpu::FramebufferPointer&) override {} + void hmdPresent() override {} bool isHmdMounted() const override { return true; } private: diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp index 48440ac80f..df01591639 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDisplayPlugin.cpp @@ -108,16 +108,13 @@ void OculusDisplayPlugin::customizeContext() { } void OculusDisplayPlugin::uncustomizeContext() { - #if 0 - if (_currentFrame && _currentFrame->framebuffer) { - // Present a final black frame to the HMD - _currentFrame->framebuffer->Bound(FramebufferTarget::Draw, [] { - Context::ClearColor(0, 0, 0, 1); - Context::Clear().ColorBuffer(); - }); - hmdPresent(); - } + // Present a final black frame to the HMD + _compositeFramebuffer->Bound(FramebufferTarget::Draw, [] { + Context::ClearColor(0, 0, 0, 1); + Context::Clear().ColorBuffer(); + }); + hmdPresent(); #endif ovr_DestroyTextureSwapChain(_session, _textureSwapChain); @@ -130,7 +127,7 @@ void OculusDisplayPlugin::uncustomizeContext() { static const uint64_t FRAME_BUDGET = (11 * USECS_PER_MSEC); static const uint64_t FRAME_OVER_BUDGET = (15 * USECS_PER_MSEC); -void OculusDisplayPlugin::hmdPresent(const gpu::FramebufferPointer& compositeFramebuffer) { +void OculusDisplayPlugin::hmdPresent() { static uint64_t lastSubmitEnd = 0; if (!_customized) { @@ -160,8 +157,15 @@ void OculusDisplayPlugin::hmdPresent(const gpu::FramebufferPointer& compositeFra auto fbo = getGLBackend()->getFramebufferID(_outputFramebuffer); glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, curTexId, 0); render([&](gpu::Batch& batch) { - auto viewport = ivec4(uvec2(), _outputFramebuffer->getSize()); - renderFromTexture(batch, compositeFramebuffer->getRenderBuffer(0), viewport, viewport, _outputFramebuffer); + batch.enableStereo(false); + batch.setFramebuffer(_outputFramebuffer); + batch.setViewportTransform(ivec4(uvec2(), _outputFramebuffer->getSize())); + batch.setStateScissorRect(ivec4(uvec2(), _outputFramebuffer->getSize())); + batch.resetViewTransform(); + batch.setProjectionTransform(mat4()); + batch.setPipeline(_presentPipeline); + batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0)); + batch.draw(gpu::TRIANGLE_STRIP, 4); }); glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, 0, 0); } diff --git a/plugins/oculus/src/OculusDisplayPlugin.h b/plugins/oculus/src/OculusDisplayPlugin.h index a0126d2e58..9209fd373e 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.h +++ b/plugins/oculus/src/OculusDisplayPlugin.h @@ -28,7 +28,7 @@ protected: QThread::Priority getPresentPriority() override { return QThread::TimeCriticalPriority; } bool internalActivate() override; - void hmdPresent(const gpu::FramebufferPointer&) override; + void hmdPresent() override; bool isHmdMounted() const override; void customizeContext() override; void uncustomizeContext() override; diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index a928887866..e6b555443f 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -237,7 +237,7 @@ void OculusLegacyDisplayPlugin::uncustomizeContext() { Parent::uncustomizeContext(); } -void OculusLegacyDisplayPlugin::hmdPresent(const gpu::FramebufferPointer& compositeFramebuffer) { +void OculusLegacyDisplayPlugin::hmdPresent() { if (!_hswDismissed) { ovrHSWDisplayState hswState; ovrHmd_GetHSWDisplayState(_hmd, &hswState); @@ -252,7 +252,7 @@ void OculusLegacyDisplayPlugin::hmdPresent(const gpu::FramebufferPointer& compos memset(eyePoses, 0, sizeof(ovrPosef) * 2); eyePoses[0].Orientation = eyePoses[1].Orientation = ovrRotation; - GLint texture = getGLBackend()->getTextureID(compositeFramebuffer->getRenderBuffer(0)); + GLint texture = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0)); auto sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); glFlush(); if (_hmdWindow->makeCurrent()) { diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h index 241d626f0c..36bdd1c792 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h @@ -39,7 +39,7 @@ protected: void customizeContext() override; void uncustomizeContext() override; - void hmdPresent(const gpu::FramebufferPointer&) override; + void hmdPresent() override; bool isHmdMounted() const override { return true; } private: diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 3d22268472..11d941dcd0 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -511,13 +511,13 @@ void OpenVrDisplayPlugin::customizeContext() { Parent::customizeContext(); if (_threadedSubmit) { -// _compositeInfos[0].texture = _compositeFramebuffer->getRenderBuffer(0); + _compositeInfos[0].texture = _compositeFramebuffer->getRenderBuffer(0); for (size_t i = 0; i < COMPOSITING_BUFFER_SIZE; ++i) { -// if (0 != i) { + if (0 != i) { _compositeInfos[i].texture = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, _renderTargetSize.x, _renderTargetSize.y, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT)); -// } + } _compositeInfos[i].textureID = getGLBackend()->getTextureID(_compositeInfos[i].texture); } _submitThread->_canvas = _submitCanvas; @@ -613,17 +613,17 @@ bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) { return Parent::beginFrameRender(frameIndex); } -void OpenVrDisplayPlugin::compositeLayers(const gpu::FramebufferPointer& compositeFramebuffer) { +void OpenVrDisplayPlugin::compositeLayers() { if (_threadedSubmit) { ++_renderingIndex; _renderingIndex %= COMPOSITING_BUFFER_SIZE; auto& newComposite = _compositeInfos[_renderingIndex]; newComposite.pose = _currentPresentFrameInfo.presentPose; - compositeFramebuffer->setRenderBuffer(0, newComposite.texture); + _compositeFramebuffer->setRenderBuffer(0, newComposite.texture); } - Parent::compositeLayers(compositeFramebuffer); + Parent::compositeLayers(); if (_threadedSubmit) { auto& newComposite = _compositeInfos[_renderingIndex]; @@ -645,13 +645,13 @@ void OpenVrDisplayPlugin::compositeLayers(const gpu::FramebufferPointer& composi } } -void OpenVrDisplayPlugin::hmdPresent(const gpu::FramebufferPointer& compositeFramebuffer) { +void OpenVrDisplayPlugin::hmdPresent() { PROFILE_RANGE_EX(render, __FUNCTION__, 0xff00ff00, (uint64_t)_currentFrame->frameIndex) if (_threadedSubmit) { _submitThread->waitForPresent(); } else { - GLuint glTexId = getGLBackend()->getTextureID(compositeFramebuffer->getRenderBuffer(0)); + GLuint glTexId = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0)); vr::Texture_t vrTexture{ (void*)(uintptr_t)glTexId, vr::TextureType_OpenGL, vr::ColorSpace_Auto }; vr::VRCompositor()->Submit(vr::Eye_Left, &vrTexture, &OPENVR_TEXTURE_BOUNDS_LEFT); vr::VRCompositor()->Submit(vr::Eye_Right, &vrTexture, &OPENVR_TEXTURE_BOUNDS_RIGHT); diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h index 923a0f7a8f..265f328920 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -72,8 +72,8 @@ protected: void internalDeactivate() override; void updatePresentPose() override; - void compositeLayers(const gpu::FramebufferPointer&) override; - void hmdPresent(const gpu::FramebufferPointer&) override; + void compositeLayers() override; + void hmdPresent() override; bool isHmdMounted() const override; void postPreview() override;