diff --git a/examples/example/entities/zoneEntityExample.js b/examples/example/entities/zoneEntityExample.js index 052de06a1d..b5831a2bb5 100644 --- a/examples/example/entities/zoneEntityExample.js +++ b/examples/example/entities/zoneEntityExample.js @@ -22,7 +22,8 @@ var zoneEntityA = Entities.addEntity({ dimensions: { x: 10, y: 10, z: 10 }, keyLightColor: { red: 255, green: 0, blue: 0 }, stageSunModelEnabled: false, - keyLightDirection: { x: 0, y: -1.0, z: 0 } + keyLightDirection: { x: 0, y: -1.0, z: 0 }, + shapeType: "sphere" }); print("zoneEntityA:" + zoneEntityA); @@ -51,7 +52,9 @@ var zoneEntityC = Entities.addEntity({ keyLightColor: { red: 0, green: 0, blue: 255 }, keyLightIntensity: 0.75, keyLightDirection: { x: 0, y: 0, z: -1 }, - stageSunModelEnabled: false + stageSunModelEnabled: false, + shapeType: "compound", + compoundShapeURL: "http://headache.hungry.com/~seth/hifi/cube.fbx" }); print("zoneEntityC:" + zoneEntityC); diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 348dc00df4..b1962bc18b 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -162,7 +162,7 @@ var elModelSections = document.querySelectorAll(".model-section"); var elModelURL = document.getElementById("property-model-url"); - var elCollisionModelURL = document.getElementById("property-collision-model-url"); + var elCompoundShapeURL = document.getElementById("property-compound-shape-url"); var elModelAnimationURL = document.getElementById("property-model-animation-url"); var elModelAnimationPlaying = document.getElementById("property-model-animation-playing"); var elModelAnimationFPS = document.getElementById("property-model-animation-fps"); @@ -323,7 +323,7 @@ } elModelURL.value = properties.modelURL; - elCollisionModelURL.value = properties.collisionModelURL; + elCompoundShapeURL.value = properties.compoundShapeURL; elModelAnimationURL.value = properties.animationURL; elModelAnimationPlaying.checked = properties.animationIsPlaying; elModelAnimationFPS.value = properties.animationFPS; @@ -487,7 +487,7 @@ elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff')); elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL')); - elCollisionModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('collisionModelURL')); + elCompoundShapeURL.addEventListener('change', createEmitTextPropertyUpdateFunction('compoundShapeURL')); elModelAnimationURL.addEventListener('change', createEmitTextPropertyUpdateFunction('animationURL')); elModelAnimationPlaying.addEventListener('change', createEmitCheckedPropertyUpdateFunction('animationIsPlaying')); elModelAnimationFPS.addEventListener('change', createEmitNumberPropertyUpdateFunction('animationFPS')); @@ -775,9 +775,9 @@
-
Collision Model URL
+
Compound Shape URL
- +
diff --git a/examples/libraries/ToolTip.js b/examples/libraries/ToolTip.js index 680f617436..2b0e125d4b 100644 --- a/examples/libraries/ToolTip.js +++ b/examples/libraries/ToolTip.js @@ -53,7 +53,7 @@ function Tooltip() { text += "ID: " + properties.id + "\n" if (properties.type == "Model") { text += "Model URL: " + properties.modelURL + "\n" - text += "Collision Model URL: " + properties.collisionModelURL + "\n" + text += "Compound Shape URL: " + properties.compoundShapeURL + "\n" text += "Animation URL: " + properties.animationURL + "\n" text += "Animation is playing: " + properties.animationIsPlaying + "\n" if (properties.sittingPoints && properties.sittingPoints.length > 0) { diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js index d8a91da8f9..255cec4265 100644 --- a/examples/libraries/entityPropertyDialogBox.js +++ b/examples/libraries/entityPropertyDialogBox.js @@ -52,7 +52,7 @@ EntityPropertyDialogBox = (function () { if (properties.type == "Model") { array.push({ label: "Model URL:", value: properties.modelURL }); index++; - array.push({ label: "Collision Model URL:", value: properties.collisionModelURL }); + array.push({ label: "Compound Shape URL:", value: properties.compoundShapeURL }); index++; array.push({ label: "Animation URL:", value: properties.animationURL }); index++; @@ -284,7 +284,7 @@ EntityPropertyDialogBox = (function () { properties.locked = array[index++].value; if (properties.type == "Model") { properties.modelURL = array[index++].value; - properties.collisionModelURL = array[index++].value; + properties.compoundShapeURL = array[index++].value; properties.animationURL = array[index++].value; var newAnimationIsPlaying = array[index++].value; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index f6557b5f9a..dde13552f3 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -34,8 +34,8 @@ #include "RenderableParticleEffectEntityItem.h" #include "RenderableSphereEntityItem.h" #include "RenderableTextEntityItem.h" +#include "RenderableZoneEntityItem.h" #include "EntitiesRendererLogging.h" -#include "ZoneEntityItem.h" EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState, AbstractScriptingServicesInterface* scriptingServices) : @@ -57,6 +57,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Text, RenderableTextEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(ParticleEffect, RenderableParticleEffectEntityItem::factory) + REGISTER_ENTITY_TYPE_WITH_FACTORY(Zone, RenderableZoneEntityItem::factory) _currentHoverOverEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID _currentClickingOnEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID @@ -496,7 +497,7 @@ const FBXGeometry* EntityTreeRenderer::getCollisionGeometryForEntity(const Entit if (entityItem->getType() == EntityTypes::Model) { const RenderableModelEntityItem* constModelEntityItem = dynamic_cast(entityItem); - if (constModelEntityItem->hasCollisionModel()) { + if (constModelEntityItem->hasCompoundShapeURL()) { RenderableModelEntityItem* modelEntityItem = const_cast(constModelEntityItem); Model* model = modelEntityItem->getModel(this); if (model) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 86b0be4dd8..8ca6562505 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -224,10 +224,10 @@ Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { // if we have a previously allocated model, but its URL doesn't match // then we need to let our renderer update our model for us. if (_model && QUrl(getModelURL()) != _model->getURL()) { - result = _model = _myRenderer->updateModel(_model, getModelURL(), getCollisionModelURL()); + result = _model = _myRenderer->updateModel(_model, getModelURL(), getCompoundShapeURL()); _needsInitialSimulation = true; } else if (!_model) { // if we don't yet have a model, then we want our renderer to allocate one - result = _model = _myRenderer->allocateModel(getModelURL(), getCollisionModelURL()); + result = _model = _myRenderer->allocateModel(getModelURL(), getCompoundShapeURL()); _needsInitialSimulation = true; } else { // we already have the model we want... result = _model; @@ -267,8 +267,8 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, extraInfo, precisionPicking); } -void RenderableModelEntityItem::setCollisionModelURL(const QString& url) { - ModelEntityItem::setCollisionModelURL(url); +void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) { + ModelEntityItem::setCompoundShapeURL(url); if (_model) { _model->setCollisionModelURL(QUrl(url)); } @@ -410,8 +410,17 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { } glm::vec3 collisionModelDimensions = box.getDimensions(); - info.setParams(type, collisionModelDimensions, _collisionModelURL); + info.setParams(type, collisionModelDimensions, _compoundShapeURL); info.setConvexHulls(_points); } } +bool RenderableModelEntityItem::contains(const glm::vec3& point) const { + if (EntityItem::contains(point) && _model && _model->getCollisionGeometry()) { + const QSharedPointer collisionNetworkGeometry = _model->getCollisionGeometry(); + const FBXGeometry& collisionGeometry = collisionNetworkGeometry->getFBXGeometry(); + return collisionGeometry.convexHullContains(worldToEntity(point)); + } + + return false; +} diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index b632357942..43f18af0db 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -52,10 +52,12 @@ public: bool needsToCallUpdate() const; - virtual void setCollisionModelURL(const QString& url); + virtual void setCompoundShapeURL(const QString& url); bool isReadyToComputeShape(); void computeShapeInfo(ShapeInfo& info); + + virtual bool contains(const glm::vec3& point) const; private: void remapTextures(); diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp new file mode 100644 index 0000000000..e4c56dc419 --- /dev/null +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -0,0 +1,57 @@ +// +// RenderableZoneEntityItem.cpp +// +// +// Created by Clement on 4/22/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "RenderableZoneEntityItem.h" + +#include +#include + +EntityItem* RenderableZoneEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + return new RenderableZoneEntityItem(entityID, properties); +} + +bool RenderableZoneEntityItem::setProperties(const EntityItemProperties& properties) { + QString oldShapeURL = getCompoundShapeURL(); + bool somethingChanged = ZoneEntityItem::setProperties(properties); + if (somethingChanged && oldShapeURL != getCompoundShapeURL()) { + _compoundShapeModel = DependencyManager::get()->getGeometry(getCompoundShapeURL(), QUrl(), true); + } + return somethingChanged; +} + +int RenderableZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData) { + QString oldShapeURL = getCompoundShapeURL(); + int bytesRead = ZoneEntityItem::readEntitySubclassDataFromBuffer(data, bytesLeftToRead, + args, propertyFlags, overwriteLocalData); + if (oldShapeURL != getCompoundShapeURL()) { + _compoundShapeModel = DependencyManager::get()->getGeometry(getCompoundShapeURL(), QUrl(), true); + } + return bytesRead; +} + +bool RenderableZoneEntityItem::contains(const glm::vec3& point) const { + if (getShapeType() != SHAPE_TYPE_COMPOUND) { + return EntityItem::contains(point); + } + if (!_compoundShapeModel && hasCompoundShapeURL()) { + const_cast(this)->_compoundShapeModel = DependencyManager::get()->getGeometry(getCompoundShapeURL(), QUrl(), true); + } + + if (EntityItem::contains(point) && _compoundShapeModel && _compoundShapeModel->isLoaded()) { + const FBXGeometry& geometry = _compoundShapeModel->getFBXGeometry(); + glm::vec3 meshDimensions = geometry.getUnscaledMeshExtents().maximum - geometry.getUnscaledMeshExtents().minimum; + return geometry.convexHullContains(worldToEntity(point) * meshDimensions); + } + + return false; +} \ No newline at end of file diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h new file mode 100644 index 0000000000..8d8d8b4b3f --- /dev/null +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h @@ -0,0 +1,39 @@ +// +// RenderableZoneEntityItem.h +// +// +// Created by Clement on 4/22/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_RenderableZoneEntityItem_h +#define hifi_RenderableZoneEntityItem_h + +#include + +class NetworkGeometry; + +class RenderableZoneEntityItem : public ZoneEntityItem { +public: + static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + RenderableZoneEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : + ZoneEntityItem(entityItemID, properties) + { } + + virtual bool setProperties(const EntityItemProperties& properties); + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData); + + virtual bool contains(const glm::vec3& point) const; + +private: + + QSharedPointer _compoundShapeModel; +}; + +#endif // hifi_RenderableZoneEntityItem_h \ No newline at end of file diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 9a4471cce6..b1bf25ae9d 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -11,6 +11,8 @@ #include +#include + #include #include #include @@ -843,7 +845,27 @@ bool EntityItem::isMoving() const { return hasVelocity() || hasAngularVelocity(); } -bool EntityItem::lifetimeHasExpired() const { +glm::mat4 EntityItem::getEntityToWorldMatrix() const { + glm::mat4 translation = glm::translate(getPosition()); + glm::mat4 rotation = glm::mat4_cast(getRotation()); + glm::mat4 scale = glm::scale(getDimensions()); + glm::mat4 registration = glm::translate(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()); + return translation * rotation * scale * registration; +} + +glm::mat4 EntityItem::getWorldToEntityMatrix() const { + return glm::inverse(getEntityToWorldMatrix()); +} + +glm::vec3 EntityItem::entityToWorld(const glm::vec3 point) const { + return glm::vec3(getEntityToWorldMatrix() * glm::vec4(point, 1.0f)); +} + +glm::vec3 EntityItem::worldToEntity(const glm::vec3 point) const { + return glm::vec3(getWorldToEntityMatrix() * glm::vec4(point, 1.0f)); +} + +bool EntityItem::lifetimeHasExpired() const { return isMortal() && (getAge() > getLifetime()); } @@ -1033,12 +1055,6 @@ AABox EntityItem::getAABox() const { return AABox(rotatedExtentsRelativeToRegistrationPoint); } -AABox EntityItem::getAABoxInDomainUnits() const { - AABox box = getAABox(); - box.scale(1.0f / (float)TREE_SCALE); - return box; -} - // NOTE: This should only be used in cases of old bitstreams which only contain radius data // 0,0,0 --> maxDimension,maxDimension,maxDimension // ... has a corner to corner distance of glm::length(maxDimension,maxDimension,maxDimension) @@ -1066,6 +1082,16 @@ float EntityItem::getRadius() const { return 0.5f * glm::length(_dimensions); } +bool EntityItem::contains(const glm::vec3& point) const { + if (getShapeType() == SHAPE_TYPE_COMPOUND) { + return getAABox().contains(point); + } else { + ShapeInfo info; + info.setParams(getShapeType(), glm::vec3(0.5f)); + return info.contains(worldToEntity(point)); + } +} + void EntityItem::computeShapeInfo(ShapeInfo& info) { info.setParams(getShapeType(), 0.5f * getDimensions()); } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 28f1c7790b..0ebcd06a77 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -145,25 +145,16 @@ public: // attributes applicable to all entity types EntityTypes::EntityType getType() const { return _type; } - glm::vec3 getPositionInDomainUnits() const { return _position / (float)TREE_SCALE; } /// get position in domain scale units (0.0 - 1.0) const glm::vec3& getPosition() const { return _position; } /// get position in meters - /// set position in domain scale units (0.0 - 1.0) - void setPositionInDomainUnits(const glm::vec3& value) - { setPosition(glm::clamp(value, 0.0f, 1.0f) * (float)TREE_SCALE); } void setPosition(const glm::vec3& value) { _position = value; } - glm::vec3 getCenterInDomainUnits() const { return getCenter() / (float) TREE_SCALE; } glm::vec3 getCenter() const; - glm::vec3 getDimensionsInDomainUnits() const { return _dimensions / (float)TREE_SCALE; } /// get dimensions in domain scale units (0.0 - 1.0) const glm::vec3& getDimensions() const { return _dimensions; } /// get dimensions in meters - /// set dimensions in domain scale units (0.0 - 1.0) - virtual void setDimensionsInDomainUnits(const glm::vec3& value) { _dimensions = glm::abs(value) * (float)TREE_SCALE; } - /// set dimensions in meter units (0.0 - TREE_SCALE) virtual void setDimensions(const glm::vec3& value) { _dimensions = glm::abs(value); } @@ -182,15 +173,11 @@ public: float getDensity() const { return _density; } - glm::vec3 getVelocityInDomainUnits() const { return _velocity / (float)TREE_SCALE; } /// velocity in domain scale units (0.0-1.0) per second const glm::vec3 getVelocity() const { return _velocity; } /// get velocity in meters - void setVelocityInDomainUnits(const glm::vec3& value) { _velocity = value * (float)TREE_SCALE; } /// velocity in domain scale units (0.0-1.0) per second void setVelocity(const glm::vec3& value) { _velocity = value; } /// velocity in meters bool hasVelocity() const { return _velocity != ENTITY_ITEM_ZERO_VEC3; } - glm::vec3 getGravityInDomainUnits() const { return _gravity / (float)TREE_SCALE; } /// gravity in domain scale units (0.0-1.0) per second squared const glm::vec3& getGravity() const { return _gravity; } /// get gravity in meters - void setGravityInDomainUnits(const glm::vec3& value) { _gravity = value * (float)TREE_SCALE; } /// gravity in domain scale units (0.0-1.0) per second squared void setGravity(const glm::vec3& value) { _gravity = value; } /// gravity in meters bool hasGravity() const { return _gravity != ENTITY_ITEM_ZERO_VEC3; } @@ -220,7 +207,6 @@ public: AACube getMaximumAACube() const; AACube getMinimumAACube() const; AABox getAABox() const; /// axis aligned bounding box in world-frame (meters) - AABox getAABoxInDomainUnits() const; /// axis aligned bounding box in domain scale units (0.0 - 1.0) const QString& getScript() const { return _script; } void setScript(const QString& value) { _script = value; } @@ -267,8 +253,7 @@ public: // TODO: get rid of users of getRadius()... float getRadius() const; - virtual bool contains(const glm::vec3& point) const { return getAABox().contains(point); } - virtual bool containsInDomainUnits(const glm::vec3& point) const { return getAABoxInDomainUnits().contains(point); } + virtual bool contains(const glm::vec3& point) const; virtual bool isReadyToComputeShape() { return true; } virtual void computeShapeInfo(ShapeInfo& info); @@ -312,6 +297,11 @@ public: static void setSendPhysicsUpdates(bool value) { _sendPhysicsUpdates = value; } static bool getSendPhysicsUpdates() { return _sendPhysicsUpdates; } + glm::mat4 getEntityToWorldMatrix() const; + glm::mat4 getWorldToEntityMatrix() const; + glm::vec3 worldToEntity(const glm::vec3 point) const; + glm::vec3 entityToWorld(const glm::vec3 point) const; + protected: static bool _sendPhysicsUpdates; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 5c834f71bf..7d5a40f6fd 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -44,7 +44,7 @@ EntityItemProperties::EntityItemProperties() : CONSTRUCT_PROPERTY(script, ENTITY_ITEM_DEFAULT_SCRIPT), CONSTRUCT_PROPERTY(color, ), CONSTRUCT_PROPERTY(modelURL, ""), - CONSTRUCT_PROPERTY(collisionModelURL, ""), + CONSTRUCT_PROPERTY(compoundShapeURL, ""), CONSTRUCT_PROPERTY(animationURL, ""), CONSTRUCT_PROPERTY(animationFPS, ModelEntityItem::DEFAULT_ANIMATION_FPS), CONSTRUCT_PROPERTY(animationFrameIndex, ModelEntityItem::DEFAULT_ANIMATION_FRAME_INDEX), @@ -175,7 +175,7 @@ void EntityItemProperties::debugDump() const { qCDebug(entities) << " _position=" << _position.x << "," << _position.y << "," << _position.z; qCDebug(entities) << " _dimensions=" << getDimensions(); qCDebug(entities) << " _modelURL=" << _modelURL; - qCDebug(entities) << " _collisionModelURL=" << _collisionModelURL; + qCDebug(entities) << " _compoundShapeURL=" << _compoundShapeURL; qCDebug(entities) << " changed properties..."; EntityPropertyFlags props = getChangedProperties(); props.debugDumpBits(); @@ -245,7 +245,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_SCRIPT, script); CHECK_PROPERTY_CHANGE(PROP_COLOR, color); CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL); - CHECK_PROPERTY_CHANGE(PROP_COLLISION_MODEL_URL, collisionModelURL); + CHECK_PROPERTY_CHANGE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_URL, animationURL); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_PLAYING, animationIsPlaying); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FRAME_INDEX, animationFrameIndex); @@ -322,7 +322,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons COPY_PROPERTY_TO_QSCRIPTVALUE(visible); COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR(color); COPY_PROPERTY_TO_QSCRIPTVALUE(modelURL); - COPY_PROPERTY_TO_QSCRIPTVALUE(collisionModelURL); + COPY_PROPERTY_TO_QSCRIPTVALUE(compoundShapeURL); COPY_PROPERTY_TO_QSCRIPTVALUE(animationURL); COPY_PROPERTY_TO_QSCRIPTVALUE(animationIsPlaying); COPY_PROPERTY_TO_QSCRIPTVALUE(animationFPS); @@ -417,7 +417,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) { COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(visible, setVisible); COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(color, setColor); COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(modelURL, setModelURL); - COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(collisionModelURL, setCollisionModelURL); + COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(compoundShapeURL, setCompoundShapeURL); COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(animationURL, setAnimationURL); COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(animationIsPlaying, setAnimationIsPlaying); COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(animationFPS, setAnimationFPS); @@ -618,7 +618,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem if (properties.getType() == EntityTypes::Model) { APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, appendValue, properties.getModelURL()); - APPEND_ENTITY_PROPERTY(PROP_COLLISION_MODEL_URL, appendValue, properties.getCollisionModelURL()); + APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, appendValue, properties.getCompoundShapeURL()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, appendValue, properties.getAnimationURL()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, appendValue, properties.getAnimationFPS()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, appendValue, properties.getAnimationFrameIndex()); @@ -657,6 +657,9 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, appendValue, properties.getStageAltitude()); APPEND_ENTITY_PROPERTY(PROP_STAGE_DAY, appendValue, properties.getStageDay()); APPEND_ENTITY_PROPERTY(PROP_STAGE_HOUR, appendValue, properties.getStageHour()); + + APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, appendValue, (uint32_t)properties.getShapeType()); + APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, appendValue, properties.getCompoundShapeURL()); } APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, appendValue, properties.getMarketplaceID()); @@ -864,7 +867,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int if (properties.getType() == EntityTypes::Model) { READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_MODEL_URL, setModelURL); - READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_COLLISION_MODEL_URL, setCollisionModelURL); + READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, setCompoundShapeURL); READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_ANIMATION_URL, setAnimationURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FPS, float, setAnimationFPS); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FRAME_INDEX, float, setAnimationFrameIndex); @@ -903,6 +906,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STAGE_ALTITUDE, float, setStageAltitude); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STAGE_DAY, quint16, setStageDay); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STAGE_HOUR, float, setStageHour); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType); + READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, setCompoundShapeURL); } READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_MARKETPLACE_ID, setMarketplaceID); @@ -958,7 +963,7 @@ void EntityItemProperties::markAllChanged() { _visibleChanged = true; _colorChanged = true; _modelURLChanged = true; - _collisionModelURLChanged = true; + _compoundShapeURLChanged = true; _animationURLChanged = true; _animationIsPlayingChanged = true; _animationFrameIndexChanged = true; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 5172b3abba..faec9e1206 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -93,7 +93,7 @@ enum EntityPropertyList { PROP_LOCAL_GRAVITY, PROP_PARTICLE_RADIUS, - PROP_COLLISION_MODEL_URL, + PROP_COMPOUND_SHAPE_URL, PROP_MARKETPLACE_ID, PROP_ACCELERATION, PROP_SIMULATOR_ID, @@ -197,7 +197,7 @@ public: DEFINE_PROPERTY_REF(PROP_SCRIPT, Script, script, QString); DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, xColor); DEFINE_PROPERTY_REF(PROP_MODEL_URL, ModelURL, modelURL, QString); - DEFINE_PROPERTY_REF(PROP_COLLISION_MODEL_URL, CollisionModelURL, collisionModelURL, QString); + DEFINE_PROPERTY_REF(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString); DEFINE_PROPERTY_REF(PROP_ANIMATION_URL, AnimationURL, animationURL, QString); DEFINE_PROPERTY(PROP_ANIMATION_FPS, AnimationFPS, animationFPS, float); DEFINE_PROPERTY(PROP_ANIMATION_FRAME_INDEX, AnimationFrameIndex, animationFrameIndex, float); @@ -340,7 +340,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, Script, script, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Color, color, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, ModelURL, modelURL, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, CollisionModelURL, collisionModelURL, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, CompoundShapeURL, compoundShapeURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationURL, animationURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationFPS, animationFPS, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationFrameIndex, animationFrameIndex, ""); diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index f95eeea8e4..1abb48abcd 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -21,7 +21,7 @@ #include "ModelEntityItem.h" const QString ModelEntityItem::DEFAULT_MODEL_URL = QString(""); -const QString ModelEntityItem::DEFAULT_COLLISION_MODEL_URL = QString(""); +const QString ModelEntityItem::DEFAULT_COMPOUND_SHAPE_URL = QString(""); const QString ModelEntityItem::DEFAULT_ANIMATION_URL = QString(""); const float ModelEntityItem::DEFAULT_ANIMATION_FRAME_INDEX = 0.0f; const bool ModelEntityItem::DEFAULT_ANIMATION_IS_PLAYING = false; @@ -47,7 +47,7 @@ EntityItemProperties ModelEntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(modelURL, getModelURL); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionModelURL, getCollisionModelURL); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationURL, getAnimationURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationIsPlaying, getAnimationIsPlaying); COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationFrameIndex, getAnimationFrameIndex); @@ -65,7 +65,7 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); SET_ENTITY_PROPERTY_FROM_PROPERTIES(modelURL, setModelURL); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionModelURL, setCollisionModelURL); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationURL, setAnimationURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationIsPlaying, setAnimationIsPlaying); SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationFrameIndex, setAnimationFrameIndex); @@ -98,11 +98,11 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY_COLOR(PROP_COLOR, _color); READ_ENTITY_PROPERTY_STRING(PROP_MODEL_URL, setModelURL); if (args.bitstreamVersion < VERSION_ENTITIES_HAS_COLLISION_MODEL) { - setCollisionModelURL(""); + setCompoundShapeURL(""); } else if (args.bitstreamVersion == VERSION_ENTITIES_HAS_COLLISION_MODEL) { - READ_ENTITY_PROPERTY_STRING(PROP_COLLISION_MODEL_URL_OLD_VERSION, setCollisionModelURL); + READ_ENTITY_PROPERTY_STRING(PROP_COMPOUND_SHAPE_URL, setCompoundShapeURL); } else { - READ_ENTITY_PROPERTY_STRING(PROP_COLLISION_MODEL_URL, setCollisionModelURL); + READ_ENTITY_PROPERTY_STRING(PROP_COMPOUND_SHAPE_URL, setCompoundShapeURL); } READ_ENTITY_PROPERTY_STRING(PROP_ANIMATION_URL, setAnimationURL); @@ -140,7 +140,7 @@ EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); requestedProperties += PROP_MODEL_URL; - requestedProperties += PROP_COLLISION_MODEL_URL; + requestedProperties += PROP_COMPOUND_SHAPE_URL; requestedProperties += PROP_ANIMATION_URL; requestedProperties += PROP_ANIMATION_FPS; requestedProperties += PROP_ANIMATION_FRAME_INDEX; @@ -164,7 +164,7 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, getColor()); APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, appendValue, getModelURL()); - APPEND_ENTITY_PROPERTY(PROP_COLLISION_MODEL_URL, appendValue, getCollisionModelURL()); + APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, appendValue, getCompoundShapeURL()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, appendValue, getAnimationURL()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, appendValue, getAnimationFPS()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, appendValue, getAnimationFrameIndex()); @@ -272,7 +272,7 @@ void ModelEntityItem::debugDump() const { qCDebug(entities) << " position:" << getPosition(); qCDebug(entities) << " dimensions:" << getDimensions(); qCDebug(entities) << " model URL:" << getModelURL(); - qCDebug(entities) << " collision model URL:" << getCollisionModelURL(); + qCDebug(entities) << " compound shape URL:" << getCompoundShapeURL(); } void ModelEntityItem::updateShapeType(ShapeType type) { @@ -280,8 +280,8 @@ void ModelEntityItem::updateShapeType(ShapeType type) { // we have allowed inconsistent ShapeType's to be stored in SVO files in the past (this was a bug) // but we are now enforcing the entity properties to be consistent. To make the possible we're // introducing a temporary workaround: we will ignore ShapeType updates that conflict with the - // _collisionModelURL. - if (hasCollisionModel()) { + // _compoundShapeURL. + if (hasCompoundShapeURL()) { type = SHAPE_TYPE_COMPOUND; } // END_TEMPORARY_WORKAROUND @@ -295,17 +295,17 @@ void ModelEntityItem::updateShapeType(ShapeType type) { // virtual ShapeType ModelEntityItem::getShapeType() const { if (_shapeType == SHAPE_TYPE_COMPOUND) { - return hasCollisionModel() ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE; + return hasCompoundShapeURL() ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE; } else { return _shapeType; } } -void ModelEntityItem::setCollisionModelURL(const QString& url) { - if (_collisionModelURL != url) { - _collisionModelURL = url; +void ModelEntityItem::setCompoundShapeURL(const QString& url) { + if (_compoundShapeURL != url) { + _compoundShapeURL = url; _dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS; - _shapeType = _collisionModelURL.isEmpty() ? SHAPE_TYPE_NONE : SHAPE_TYPE_COMPOUND; + _shapeType = _compoundShapeURL.isEmpty() ? SHAPE_TYPE_NONE : SHAPE_TYPE_COMPOUND; } } diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 057c5babaf..6fe2adc928 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -57,13 +57,13 @@ public: const rgbColor& getColor() const { return _color; } xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; } bool hasModel() const { return !_modelURL.isEmpty(); } - virtual bool hasCollisionModel() const { return !_collisionModelURL.isEmpty(); } + virtual bool hasCompoundShapeURL() const { return !_compoundShapeURL.isEmpty(); } static const QString DEFAULT_MODEL_URL; const QString& getModelURL() const { return _modelURL; } - static const QString DEFAULT_COLLISION_MODEL_URL; - const QString& getCollisionModelURL() const { return _collisionModelURL; } + static const QString DEFAULT_COMPOUND_SHAPE_URL; + const QString& getCompoundShapeURL() const { return _compoundShapeURL; } bool hasAnimation() const { return !_animationURL.isEmpty(); } static const QString DEFAULT_ANIMATION_URL; @@ -78,7 +78,7 @@ public: // model related properties void setModelURL(const QString& url) { _modelURL = url; } - virtual void setCollisionModelURL(const QString& url); + virtual void setCompoundShapeURL(const QString& url); void setAnimationURL(const QString& url); static const float DEFAULT_ANIMATION_FRAME_INDEX; void setAnimationFrameIndex(float value); @@ -126,7 +126,7 @@ protected: rgbColor _color; QString _modelURL; - QString _collisionModelURL; + QString _compoundShapeURL; quint64 _lastAnimated; QString _animationURL; diff --git a/libraries/entities/src/SphereEntityItem.cpp b/libraries/entities/src/SphereEntityItem.cpp index 132ad43336..d17e49cb59 100644 --- a/libraries/entities/src/SphereEntityItem.cpp +++ b/libraries/entities/src/SphereEntityItem.cpp @@ -96,11 +96,7 @@ bool SphereEntityItem::findDetailedRayIntersection(const glm::vec3& origin, cons bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject, bool precisionPicking) const { // determine the ray in the frame of the entity transformed from a unit sphere - glm::mat4 translation = glm::translate(getPosition()); - glm::mat4 rotation = glm::mat4_cast(getRotation()); - glm::mat4 scale = glm::scale(getDimensions()); - glm::mat4 registration = glm::translate(glm::vec3(0.5f, 0.5f, 0.5f) - getRegistrationPoint()); - glm::mat4 entityToWorldMatrix = translation * rotation * scale * registration; + glm::mat4 entityToWorldMatrix = getEntityToWorldMatrix(); glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); glm::vec3 entityFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f)); glm::vec3 entityFrameDirection = glm::normalize(glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f))); @@ -112,7 +108,7 @@ bool SphereEntityItem::findDetailedRayIntersection(const glm::vec3& origin, cons glm::vec3 entityFrameHitAt = entityFrameOrigin + (entityFrameDirection * localDistance); // then translate back to work coordinates glm::vec3 hitAt = glm::vec3(entityToWorldMatrix * glm::vec4(entityFrameHitAt, 1.0f)); - distance = glm::distance(origin,hitAt); + distance = glm::distance(origin, hitAt); return true; } return false; diff --git a/libraries/entities/src/SphereEntityItem.h b/libraries/entities/src/SphereEntityItem.h index f79a2db7ff..b6516b714f 100644 --- a/libraries/entities/src/SphereEntityItem.h +++ b/libraries/entities/src/SphereEntityItem.h @@ -50,9 +50,6 @@ public: _color[BLUE_INDEX] = value.blue; } - // TODO: implement proper contains for 3D ellipsoid - //virtual bool contains(const glm::vec3& point) const; - virtual ShapeType getShapeType() const { return SHAPE_TYPE_SPHERE; } virtual bool supportsDetailedRayIntersection() const { return true; } diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp index 39a4d48d96..34d0ce051c 100644 --- a/libraries/entities/src/TextEntityItem.cpp +++ b/libraries/entities/src/TextEntityItem.cpp @@ -48,11 +48,6 @@ void TextEntityItem::setDimensions(const glm::vec3& value) { _dimensions = glm::vec3(value.x, value.y, TEXT_ENTITY_ITEM_FIXED_DEPTH); } -void TextEntityItem::setDimensionsInDomainUnits(const glm::vec3& value) { - // NOTE: Text Entities always have a "depth" of 1cm. - _dimensions = glm::vec3(value.x * (float)TREE_SCALE, value.y * (float)TREE_SCALE, TEXT_ENTITY_ITEM_FIXED_DEPTH); -} - EntityItemProperties TextEntityItem::getProperties() const { EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class diff --git a/libraries/entities/src/TextEntityItem.h b/libraries/entities/src/TextEntityItem.h index 044975bdc8..d57b5442d6 100644 --- a/libraries/entities/src/TextEntityItem.h +++ b/libraries/entities/src/TextEntityItem.h @@ -24,7 +24,6 @@ public: /// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately virtual void setDimensions(const glm::vec3& value); - virtual void setDimensionsInDomainUnits(const glm::vec3& value); virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; } // methods for getting/setting all properties of an entity diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index 60a61e4e89..b1d440e5e5 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -31,6 +31,8 @@ const float ZoneEntityItem::DEFAULT_STAGE_LONGITUDE = 122.407f; const float ZoneEntityItem::DEFAULT_STAGE_ALTITUDE = 0.03f; const quint16 ZoneEntityItem::DEFAULT_STAGE_DAY = 60; const float ZoneEntityItem::DEFAULT_STAGE_HOUR = 12.0f; +const ShapeType ZoneEntityItem::DEFAULT_SHAPE_TYPE = SHAPE_TYPE_BOX; +const QString ZoneEntityItem::DEFAULT_COMPOUND_SHAPE_URL = ""; EntityItem* ZoneEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItem* result = new ZoneEntityItem(entityID, properties); @@ -56,6 +58,8 @@ ZoneEntityItem::ZoneEntityItem(const EntityItemID& entityItemID, const EntityIte _stageAltitude = DEFAULT_STAGE_ALTITUDE; _stageDay = DEFAULT_STAGE_DAY; _stageHour = DEFAULT_STAGE_HOUR; + _shapeType = DEFAULT_SHAPE_TYPE; + _compoundShapeURL = DEFAULT_COMPOUND_SHAPE_URL; setProperties(properties); } @@ -73,6 +77,8 @@ EntityItemProperties ZoneEntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(stageAltitude, getStageAltitude); COPY_ENTITY_PROPERTY_TO_PROPERTIES(stageDay, getStageDay); COPY_ENTITY_PROPERTY_TO_PROPERTIES(stageHour, getStageHour); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL); return properties; } @@ -91,6 +97,8 @@ bool ZoneEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(stageAltitude, setStageAltitude); SET_ENTITY_PROPERTY_FROM_PROPERTIES(stageDay, setStageDay); SET_ENTITY_PROPERTY_FROM_PROPERTIES(stageHour, setStageHour); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, updateShapeType); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL); if (somethingChanged) { bool wantDebug = false; @@ -122,6 +130,8 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, float, _stageAltitude); READ_ENTITY_PROPERTY(PROP_STAGE_DAY, quint16, _stageDay); READ_ENTITY_PROPERTY(PROP_STAGE_HOUR, float, _stageHour); + READ_ENTITY_PROPERTY_SETTER(PROP_SHAPE_TYPE, ShapeType, updateShapeType); + READ_ENTITY_PROPERTY_STRING(PROP_COMPOUND_SHAPE_URL, setCompoundShapeURL); return bytesRead; } @@ -141,6 +151,8 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p requestedProperties += PROP_STAGE_ALTITUDE; requestedProperties += PROP_STAGE_DAY; requestedProperties += PROP_STAGE_HOUR; + requestedProperties += PROP_SHAPE_TYPE; + requestedProperties += PROP_COMPOUND_SHAPE_URL; return requestedProperties; } @@ -165,6 +177,8 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits APPEND_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, appendValue, getStageAltitude()); APPEND_ENTITY_PROPERTY(PROP_STAGE_DAY, appendValue, getStageDay()); APPEND_ENTITY_PROPERTY(PROP_STAGE_HOUR, appendValue, getStageHour()); + APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, appendValue, (uint32_t)getShapeType()); + APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, appendValue, getCompoundShapeURL()); } void ZoneEntityItem::debugDump() const { @@ -185,6 +199,23 @@ void ZoneEntityItem::debugDump() const { qCDebug(entities) << " _stageHour:" << _stageHour; } +ShapeType ZoneEntityItem::getShapeType() const { + if (_shapeType == SHAPE_TYPE_COMPOUND) { + return hasCompoundShapeURL() ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE; + } else { + return _shapeType; + } +} + +void ZoneEntityItem::setCompoundShapeURL(const QString& url) { + _compoundShapeURL = url; + if (!_compoundShapeURL.isEmpty()) { + updateShapeType(SHAPE_TYPE_COMPOUND); + } else if (_shapeType == SHAPE_TYPE_COMPOUND) { + _shapeType = DEFAULT_SHAPE_TYPE; + } +} + bool ZoneEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject, bool precisionPicking) const { diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index bc1d77ab65..3577e69162 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -91,6 +91,14 @@ public: static bool getZonesArePickable() { return _zonesArePickable; } static void setZonesArePickable(bool value) { _zonesArePickable = value; } + + virtual bool isReadyToComputeShape() { return false; } + void updateShapeType(ShapeType type) { _shapeType = type; } + virtual ShapeType getShapeType() const; + + virtual bool hasCompoundShapeURL() const { return !_compoundShapeURL.isEmpty(); } + const QString getCompoundShapeURL() const { return _compoundShapeURL; } + virtual void setCompoundShapeURL(const QString& url); virtual bool supportsDetailedRayIntersection() const { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, @@ -109,6 +117,8 @@ public: static const float DEFAULT_STAGE_ALTITUDE; static const quint16 DEFAULT_STAGE_DAY; static const float DEFAULT_STAGE_HOUR; + static const ShapeType DEFAULT_SHAPE_TYPE; + static const QString DEFAULT_COMPOUND_SHAPE_URL; protected: // properties of the "sun" in the zone @@ -122,6 +132,9 @@ protected: float _stageAltitude; uint16_t _stageDay; float _stageHour; + + ShapeType _shapeType = SHAPE_TYPE_NONE; + QString _compoundShapeURL; static bool _zonesArePickable; }; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 25ea9ef8e1..1c89ee4067 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -125,6 +125,51 @@ Extents FBXGeometry::getUnscaledMeshExtents() const { return scaledExtents; } +// TODO: Move to model::Mesh when Sam's ready +bool FBXGeometry::convexHullContains(const glm::vec3& point) const { + if (!getUnscaledMeshExtents().containsPoint(point)) { + return false; + } + + auto checkEachPrimitive = [=](FBXMesh& mesh, QVector indices, int primitiveSize) -> bool { + // Check whether the point is "behind" all the primitives. + for (unsigned int j = 0; j < indices.size(); j += primitiveSize) { + if (!isPointBehindTrianglesPlane(point, + mesh.vertices[indices[j]], + mesh.vertices[indices[j + 1]], + mesh.vertices[indices[j + 2]])) { + // it's not behind at least one so we bail + return false; + } + } + return true; + }; + + // Check that the point is contained in at least one convex mesh. + for (auto mesh : meshes) { + bool insideMesh = true; + + // To be considered inside a convex mesh, + // the point needs to be "behind" all the primitives respective planes. + for (auto part : mesh.parts) { + // run through all the triangles and quads + if (!checkEachPrimitive(mesh, part.triangleIndices, 3) || + !checkEachPrimitive(mesh, part.quadIndices, 4)) { + // If not, the point is outside, bail for this mesh + insideMesh = false; + continue; + } + } + if (insideMesh) { + // It's inside this mesh, return true. + return true; + } + } + + // It wasn't in any mesh, return false. + return false; +} + QString FBXGeometry::getModelNameOfMesh(int meshIndex) const { if (meshIndicesToModelNames.contains(meshIndex)) { return meshIndicesToModelNames.value(meshIndex); @@ -132,8 +177,6 @@ QString FBXGeometry::getModelNameOfMesh(int meshIndex) const { return QString(); } - - static int fbxGeometryMetaTypeId = qRegisterMetaType(); static int fbxAnimationFrameMetaTypeId = qRegisterMetaType(); static int fbxAnimationFrameVectorMetaTypeId = qRegisterMetaType >(); diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index d1576bc02a..871f3d0581 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -252,6 +252,7 @@ public: /// Returns the unscaled extents of the model's mesh Extents getUnscaledMeshExtents() const; + bool convexHullContains(const glm::vec3& point) const; QHash meshIndicesToModelNames; diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 8f0cee10cf..8fedc6b979 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -74,7 +74,7 @@ PacketVersion versionForPacketType(PacketType type) { return 1; case PacketTypeEntityAddOrEdit: case PacketTypeEntityData: - return VERSION_ENTITIES_ZONE_ENTITIES_EXIST; + return VERSION_ENTITIES_ZONE_ENTITIES_HAVE_DYNAMIC_SHAPE; case PacketTypeEntityErase: return 2; case PacketTypeAudioStreamStats: diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index a05df968bd..01302f5568 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -138,5 +138,6 @@ const PacketVersion VERSION_ENTITIES_HAS_MARKETPLACE_ID = 14; const PacketVersion VERSION_ENTITIES_HAVE_ACCELERATION = 15; const PacketVersion VERSION_ENTITIES_HAVE_UUIDS = 16; const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_EXIST = 17; +const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_HAVE_DYNAMIC_SHAPE = 18; #endif // hifi_PacketHeaders_h diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index 4e8fb7d3cd..5d1b70275c 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -203,6 +203,13 @@ glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2) { return glm::angleAxis(angle, axis); } +bool isPointBehindTrianglesPlane(glm::vec3 point, glm::vec3 p0, glm::vec3 p1, glm::vec3 p2) { + glm::vec3 v1 = p0 - p1, v2 = p2 - p1; // Non-collinear vectors contained in the plane + glm::vec3 n = glm::cross(v1, v2); // Plane's normal vector, pointing out of the triangle + float d = -glm::dot(n, p0); // Compute plane's equation constant + return (glm::dot(n, point) + d) >= 0; +} + glm::vec3 extractTranslation(const glm::mat4& matrix) { return glm::vec3(matrix[3][0], matrix[3][1], matrix[3][2]); } diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index e5d22d67dc..dda57a9cd9 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -82,6 +82,8 @@ float angleBetween(const glm::vec3& v1, const glm::vec3& v2); glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2); +bool isPointBehindTrianglesPlane(glm::vec3 point, glm::vec3 p0, glm::vec3 p1, glm::vec3 p2); + glm::vec3 extractTranslation(const glm::mat4& matrix); void setTranslation(glm::mat4& matrix, const glm::vec3& translation); diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 544df35b86..1c86f109c5 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -114,7 +114,7 @@ float ShapeInfo::computeVolume() const { } case SHAPE_TYPE_CAPSULE_Y: { float radius = _halfExtents.x; - volume = PI * radius * radius * (2.0f * _halfExtents.y + 4.0f * radius / 3.0f); + volume = PI * radius * radius * (2.0f * (_halfExtents.y - _halfExtents.x) + 4.0f * radius / 3.0f); break; } default: @@ -124,6 +124,54 @@ float ShapeInfo::computeVolume() const { return volume; } +bool ShapeInfo::contains(const glm::vec3& point) const { + switch(_type) { + case SHAPE_TYPE_SPHERE: + return glm::length(point) <= _halfExtents.x; + case SHAPE_TYPE_ELLIPSOID: { + glm::vec3 scaledPoint = glm::abs(point) / _halfExtents; + return glm::length(scaledPoint) <= 1.0f; + } + case SHAPE_TYPE_CYLINDER_X: + return glm::length(glm::vec2(point.y, point.z)) <= _halfExtents.z; + case SHAPE_TYPE_CYLINDER_Y: + return glm::length(glm::vec2(point.x, point.z)) <= _halfExtents.x; + case SHAPE_TYPE_CYLINDER_Z: + return glm::length(glm::vec2(point.x, point.y)) <= _halfExtents.y; + case SHAPE_TYPE_CAPSULE_X: { + if (glm::abs(point.x) <= _halfExtents.x) { + return glm::length(glm::vec2(point.y, point.z)) <= _halfExtents.z; + } else { + glm::vec3 absPoint = glm::abs(point) - _halfExtents.x; + return glm::length(absPoint) <= _halfExtents.z; + } + } + case SHAPE_TYPE_CAPSULE_Y: { + if (glm::abs(point.y) <= _halfExtents.y) { + return glm::length(glm::vec2(point.x, point.z)) <= _halfExtents.x; + } else { + glm::vec3 absPoint = glm::abs(point) - _halfExtents.y; + return glm::length(absPoint) <= _halfExtents.x; + } + } + case SHAPE_TYPE_CAPSULE_Z: { + if (glm::abs(point.z) <= _halfExtents.z) { + return glm::length(glm::vec2(point.x, point.y)) <= _halfExtents.y; + } else { + glm::vec3 absPoint = glm::abs(point) - _halfExtents.z; + return glm::length(absPoint) <= _halfExtents.y; + } + } + case SHAPE_TYPE_BOX: + default: { + glm::vec3 absPoint = glm::abs(point); + return absPoint.x <= _halfExtents.x + && absPoint.y <= _halfExtents.y + && absPoint.z <= _halfExtents.z; + } + } +} + const DoubleHashKey& ShapeInfo::getHash() const { // NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance. if (_doubleHashKey.isNull() && _type != SHAPE_TYPE_NONE) { diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h index 8770ef62c7..114d209788 100644 --- a/libraries/shared/src/ShapeInfo.h +++ b/libraries/shared/src/ShapeInfo.h @@ -57,6 +57,10 @@ public: void appendToPoints (const QVector& newPoints) { _points << newPoints; } float computeVolume() const; + + /// Returns whether point is inside the shape + /// For compound shapes it will only return whether it is inside the bounding box + bool contains(const glm::vec3& point) const; const DoubleHashKey& getHash() const; diff --git a/tests/octree/src/OctreeTests.cpp b/tests/octree/src/OctreeTests.cpp index 705a50aa10..4cfadbccfc 100644 --- a/tests/octree/src/OctreeTests.cpp +++ b/tests/octree/src/OctreeTests.cpp @@ -74,7 +74,7 @@ void OctreeTests::propertyFlagsTests(bool verbose) { props.setHasProperty(PROP_POSITION); props.setHasProperty(PROP_RADIUS); props.setHasProperty(PROP_MODEL_URL); - props.setHasProperty(PROP_COLLISION_MODEL_URL); + props.setHasProperty(PROP_COMPOUND_SHAPE_URL); props.setHasProperty(PROP_ROTATION); QByteArray encoded = props.encode();