From 1125cb183eb41aca71150c49f7d71e7ea3b36b6b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 16 Jul 2014 09:25:54 -0700 Subject: [PATCH] splitting up a bunch of Entity classes into different files --- libraries/entities/src/EntityItem.cpp | 335 +--------------- libraries/entities/src/EntityItem.h | 281 +------------- libraries/entities/src/EntityItemID.cpp | 32 ++ libraries/entities/src/EntityItemID.h | 82 ++++ .../entities/src/EntityItemProperties.cpp | 360 ++++++++++++++++++ libraries/entities/src/EntityItemProperties.h | 187 +++++++++ .../entities/src/EntityScriptingInterface.h | 10 +- libraries/entities/src/EntityTree.cpp | 18 +- libraries/entities/src/EntityTypes.cpp | 311 +++++++++++++++ libraries/entities/src/EntityTypes.h | 50 +++ 10 files changed, 1055 insertions(+), 611 deletions(-) create mode 100644 libraries/entities/src/EntityItemID.cpp create mode 100644 libraries/entities/src/EntityItemID.h create mode 100644 libraries/entities/src/EntityItemProperties.cpp create mode 100644 libraries/entities/src/EntityItemProperties.h create mode 100644 libraries/entities/src/EntityTypes.cpp create mode 100644 libraries/entities/src/EntityTypes.h diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index ab8f380194..28368328f2 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -29,74 +29,6 @@ #include "EntityItem.h" #include "EntityTree.h" -QHash EntityTypes::_typeNameHash; - -const QString UNKNOWN_EntityType_t_NAME = "Unknown"; -const QString& EntityTypes::getEntityTypeName(EntityType_t entityType) { - QHash::iterator matchedTypeName = _typeNameHash.find(entityType); - return matchedTypeName != _typeNameHash.end() ? matchedTypeName.value() : UNKNOWN_EntityType_t_NAME; -} - -bool EntityTypes::registerEntityType(EntityType_t entityType, const QString& name) { - _typeNameHash.insert(entityType, name); - return true; -} - -EntityItem* EntityTypes::constructEntityItem(EntityType_t entityType, const EntityItemID& entityID, const EntityItemProperties& properties) { - EntityItem* newEntityItem = NULL; - - // switch statement for now, needs to support registration of constructor - switch (entityType) { - // Base, // ??? not supported? - case Model: - newEntityItem = new ModelEntityItem(entityID, properties); - break; - - case Particle: - newEntityItem = new ParticleEntityItem(entityID, properties); - break; - - case Box: - newEntityItem = new BoxEntityItem(entityID, properties); - break; - - case Sphere: - newEntityItem = new SphereEntityItem(entityID, properties); - break; - - case Plane: - newEntityItem = new PlaneEntityItem(entityID, properties); - break; - - case Cylinder: - newEntityItem = new CylinderEntityItem(entityID, properties); - break; - - case Pyramid: - newEntityItem = new PyramidEntityItem(entityID, properties); - break; - - default: - newEntityItem = new ModelEntityItem(entityID, properties); - break; - } - return newEntityItem; -} - -EntityItem* EntityTypes::constructEntityItem(const unsigned char* data, int bytesToRead) { - return NULL; // TODO Implement this for real! -} - -bool EntityTypes::decodEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes, - const EntityItemID& entityID, const EntityItemProperties& properties) { - bool valid = false; - return valid; -} - - -bool registered = EntityTypes::registerEntityType(EntityTypes::Base, "Base") - && EntityTypes::registerEntityType(EntityTypes::Model, "Model"); // TODO: move this to model subclass - uint32_t EntityItem::_nextID = 0; // for locally created models @@ -827,241 +759,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef return bytesRead; } -EntityItem* EntityItem::fromEditPacket(const unsigned char* data, int length, int& processedBytes, EntityTree* tree, bool& valid) { - EntityItem* result = NULL; - - bool wantDebug = false; - if (wantDebug) { - qDebug() << "EntityItem EntityItem::fromEditPacket() length=" << length; - } - - const unsigned char* dataAt = data; - processedBytes = 0; - - // the first part of the data is an octcode, this is a required element of the edit packet format, but we don't - // actually use it, we do need to skip it and read to the actual data we care about. - int octets = numberOfThreeBitSectionsInCode(data); - int lengthOfOctcode = bytesRequiredForCodeLength(octets); - - if (wantDebug) { - qDebug() << "EntityItem EntityItem::fromEditPacket() lengthOfOctcode=" << lengthOfOctcode; - } - - // we don't actually do anything with this octcode... - dataAt += lengthOfOctcode; - processedBytes += lengthOfOctcode; - - // Edit packets have a last edited time stamp immediately following the octcode. - // NOTE: the edit times have been set by the editor to match out clock, so we don't need to adjust - // these times for clock skew at this point. - quint64 lastEdited; - memcpy(&lastEdited, dataAt, sizeof(lastEdited)); - dataAt += sizeof(lastEdited); - processedBytes += sizeof(lastEdited); - - // encoded id - QByteArray encodedID((const char*)dataAt, (length - processedBytes)); - ByteCountCoded idCoder = encodedID; - quint32 editID = idCoder; - encodedID = idCoder; // determine true length - dataAt += encodedID.size(); - processedBytes += encodedID.size(); - - if (wantDebug) { - qDebug() << "EntityItem EntityItem::fromEditPacket() editID=" << editID; - } - - bool isNewEntityItem = (editID == NEW_ENTITY); - - if (isNewEntityItem) { - // If this is a NEW_ENTITY, then we assume that there's an additional uint32_t creatorToken, that - // we want to send back to the creator as an map to the actual id - - QByteArray encodedToken((const char*)dataAt, (length - processedBytes)); - ByteCountCoded tokenCoder = encodedToken; - quint32 creatorTokenID = tokenCoder; - encodedToken = tokenCoder; // determine true length - dataAt += encodedToken.size(); - processedBytes += encodedToken.size(); - - //newEntityItem.setCreatorTokenID(creatorTokenID); - //newEntityItem._newlyCreated = true; - - valid = true; - - } else { - // look up the existing entityItem - const EntityItem* existingEntityItem = tree->findEntityByID(editID, true); - - // copy existing properties before over-writing with new properties - if (existingEntityItem) { - - // TODO: how do we want to handle these edits to existing packets??? - - //newEntityItem = *existingEntityItem; - - valid = true; - } else { - // the user attempted to edit a entityItem that doesn't exist - qDebug() << "user attempted to edit a entityItem that doesn't exist... editID=" << editID; - tree->debugDumpMap(); - valid = false; - - // NOTE: Even though we know item is not valid, we still need to parse the rest - // of the edit packet so that we don't end up out of sync on our bitstream - // fall through.... - } - - //newEntityItem._id = editID; - //newEntityItem._newlyCreated = false; - } - - // Entity Type... - QByteArray encodedType((const char*)dataAt, (length - processedBytes)); - ByteCountCoded typeCoder = encodedType; - quint32 entityTypeCode = typeCoder; - EntityTypes::EntityType_t entityType = (EntityTypes::EntityType_t)entityTypeCode; - encodedType = typeCoder; // determine true length - dataAt += encodedType.size(); - processedBytes += encodedType.size(); - - // Update Delta - when was this item updated relative to last edit... this really should be 0 - // TODO: Should we get rid of this in this in edit packets, since this has to always be 0? - // last updated is stored as ByteCountCoded delta from lastEdited - QByteArray encodedUpdateDelta((const char*)dataAt, (length - processedBytes)); - ByteCountCoded updateDeltaCoder = encodedUpdateDelta; - quint64 updateDelta = updateDeltaCoder; - quint64 lastUpdated = lastEdited + updateDelta; // don't adjust for clock skew since we already did that for lastEdited - - encodedUpdateDelta = updateDeltaCoder; // determine true length - dataAt += encodedUpdateDelta.size(); - processedBytes += encodedUpdateDelta.size(); - - // Property Flags... - QByteArray encodedPropertyFlags((const char*)dataAt, (length - processedBytes)); - EntityPropertyFlags propertyFlags = encodedPropertyFlags; - dataAt += propertyFlags.getEncodedLength(); - processedBytes += propertyFlags.getEncodedLength(); - -/**** - // All of the remaining items are optional, and may or may not be included based on their included values in the - // properties included bits - uint16_t packetContainsBits = 0; - if (!isNewEntityItem) { - memcpy(&packetContainsBits, dataAt, sizeof(packetContainsBits)); - dataAt += sizeof(packetContainsBits); - processedBytes += sizeof(packetContainsBits); - - // only applies to editing of existing models - if (!packetContainsBits) { - //qDebug() << "edit packet didn't contain any information ignore it..."; - valid = false; - //return newEntityItem; - return NULL; - } - } - - // position - if (isNewEntityItem || ((packetContainsBits & ENTITY_PACKET_CONTAINS_POSITION) == ENTITY_PACKET_CONTAINS_POSITION)) { - memcpy(&newEntityItem._position, dataAt, sizeof(newEntityItem._position)); - dataAt += sizeof(newEntityItem._position); - processedBytes += sizeof(newEntityItem._position); - } - - // radius - if (isNewEntityItem || ((packetContainsBits & ENTITY_PACKET_CONTAINS_RADIUS) == ENTITY_PACKET_CONTAINS_RADIUS)) { - memcpy(&newEntityItem._radius, dataAt, sizeof(newEntityItem._radius)); - dataAt += sizeof(newEntityItem._radius); - processedBytes += sizeof(newEntityItem._radius); - } - - // rotation - if (isNewEntityItem || ((packetContainsBits & - ENTITY_PACKET_CONTAINS_ROTATION) == ENTITY_PACKET_CONTAINS_ROTATION)) { - int bytes = unpackOrientationQuatFromBytes(dataAt, newEntityItem._rotation); - dataAt += bytes; - processedBytes += bytes; - } - - // shouldBeDeleted - if (isNewEntityItem || ((packetContainsBits & ENTITY_PACKET_CONTAINS_SHOULDDIE) == ENTITY_PACKET_CONTAINS_SHOULDDIE)) { - memcpy(&newEntityItem._shouldBeDeleted, dataAt, sizeof(newEntityItem._shouldBeDeleted)); - dataAt += sizeof(newEntityItem._shouldBeDeleted); - processedBytes += sizeof(newEntityItem._shouldBeDeleted); - } - -#ifdef HIDE_SUBCLASS_METHODS - // color - if (isNewEntityItem || ((packetContainsBits & ENTITY_PACKET_CONTAINS_COLOR) == ENTITY_PACKET_CONTAINS_COLOR)) { - memcpy(newEntityItem._color, dataAt, sizeof(newEntityItem._color)); - dataAt += sizeof(newEntityItem._color); - processedBytes += sizeof(newEntityItem._color); - } - - // modelURL - if (isNewEntityItem || ((packetContainsBits & ENTITY_PACKET_CONTAINS_MODEL_URL) == ENTITY_PACKET_CONTAINS_MODEL_URL)) { - uint16_t modelURLLength; - memcpy(&modelURLLength, dataAt, sizeof(modelURLLength)); - dataAt += sizeof(modelURLLength); - processedBytes += sizeof(modelURLLength); - QString tempString((const char*)dataAt); - newEntityItem._modelURL = tempString; - dataAt += modelURLLength; - processedBytes += modelURLLength; - } - - // animationURL - if (isNewEntityItem || ((packetContainsBits & ENTITY_PACKET_CONTAINS_ANIMATION_URL) == ENTITY_PACKET_CONTAINS_ANIMATION_URL)) { - uint16_t animationURLLength; - memcpy(&animationURLLength, dataAt, sizeof(animationURLLength)); - dataAt += sizeof(animationURLLength); - processedBytes += sizeof(animationURLLength); - QString tempString((const char*)dataAt); - newEntityItem._animationURL = tempString; - dataAt += animationURLLength; - processedBytes += animationURLLength; - } - - // animationIsPlaying - if (isNewEntityItem || ((packetContainsBits & - ENTITY_PACKET_CONTAINS_ANIMATION_PLAYING) == ENTITY_PACKET_CONTAINS_ANIMATION_PLAYING)) { - - memcpy(&newEntityItem._animationIsPlaying, dataAt, sizeof(newEntityItem._animationIsPlaying)); - dataAt += sizeof(newEntityItem._animationIsPlaying); - processedBytes += sizeof(newEntityItem._animationIsPlaying); - } - - // animationFrameIndex - if (isNewEntityItem || ((packetContainsBits & - ENTITY_PACKET_CONTAINS_ANIMATION_FRAME) == ENTITY_PACKET_CONTAINS_ANIMATION_FRAME)) { - - memcpy(&newEntityItem._animationFrameIndex, dataAt, sizeof(newEntityItem._animationFrameIndex)); - dataAt += sizeof(newEntityItem._animationFrameIndex); - processedBytes += sizeof(newEntityItem._animationFrameIndex); - } - - // animationFPS - if (isNewEntityItem || ((packetContainsBits & - ENTITY_PACKET_CONTAINS_ANIMATION_FPS) == ENTITY_PACKET_CONTAINS_ANIMATION_FPS)) { - - memcpy(&newEntityItem._animationFPS, dataAt, sizeof(newEntityItem._animationFPS)); - dataAt += sizeof(newEntityItem._animationFPS); - processedBytes += sizeof(newEntityItem._animationFPS); - } -#endif - -***/ - const bool wantDebugging = false; - if (wantDebugging) { - qDebug("EntityItem::fromEditPacket()..."); - qDebug() << " EntityItem id in packet:" << editID; - //newEntityItem.debugDump(); - } - - // TODO: need to make this actually return something... - return result; -} - void EntityItem::debugDump() const { qDebug() << "EntityItem id:" << getEntityItemID(); qDebug(" edited ago:%f", getEditedAgo()); @@ -1081,9 +778,6 @@ void EntityItem::debugDump() const { - - - ////////////////////////////////////////////////////// ////////////////////////////////////////////////////// ////////////////////////////////////////////////////// @@ -1614,6 +1308,8 @@ EntityItemProperties EntityItem::getProperties() const { properties._id = getID(); properties._idSet = true; + properties._type = getType(); + properties._position = getPosition() * (float) TREE_SCALE; properties._radius = getRadius() * (float) TREE_SCALE; properties._rotation = getRotation(); @@ -1726,6 +1422,7 @@ EntityItemProperties::EntityItemProperties() : _id(UNKNOWN_ENTITY_ID), _idSet(false), _lastEdited(usecTimestampNow()), + _type(EntityTypes::Base), _position(0), _radius(ENTITY_DEFAULT_RADIUS), @@ -1908,6 +1605,13 @@ qDebug() << "EntityItemProperties::copyToScriptValue()... isKnownID=" << isKnown void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) { + QScriptValue typeScriptValue = object.property("type"); + if (typeScriptValue.isValid()) { + QString typeName; + typeName = typeScriptValue.toVariant().toString(); + _type = EntityTypes::getEntityTypeFromName(typeName); + } + QScriptValue position = object.property("position"); if (position.isValid()) { QScriptValue x = position.property("x"); @@ -2055,22 +1759,3 @@ QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const Enti void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemProperties& properties) { properties.copyFromScriptValue(object); } - - -QScriptValue EntityItemIDtoScriptValue(QScriptEngine* engine, const EntityItemID& id) { - QScriptValue obj = engine->newObject(); - obj.setProperty("id", id.id); - obj.setProperty("creatorTokenID", id.creatorTokenID); - obj.setProperty("isKnownID", id.isKnownID); -qDebug() << "EntityItemIDtoScriptValue()... isKnownID=" << id.isKnownID << "id=" << id.id << "creatorTokenID=" << id.creatorTokenID; - return obj; -} - -void EntityItemIDfromScriptValue(const QScriptValue &object, EntityItemID& id) { - id.id = object.property("id").toVariant().toUInt(); - id.creatorTokenID = object.property("creatorTokenID").toVariant().toUInt(); - id.isKnownID = object.property("isKnownID").toVariant().toBool(); -} - - - diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index e11214325b..7b4cbeb51c 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -1,6 +1,6 @@ // // EntityItem.h -// libraries/models/src +// libraries/entities/src // // Created by Brad Hefta-Gaub on 12/4/13. // Copyright 2013 High Fidelity, Inc. @@ -18,266 +18,16 @@ #include -#include -#include - -#include -#include +#include // for Animation, AnimationCache, and AnimationPointer classes +#include // for EncodeBitstreamParams class +#include // for OctreeElement::AppendState #include -#include -#include -#include +#include "EntityItemID.h" +#include "EntityItemProperties.h" +#include "EntityTypes.h" -class EntityItem; -class EntityItemID; -class EntityEditPacketSender; -class EntityItemProperties; -class EntitysScriptingInterface; -class EntityTree; class EntityTreeElementExtraEncodeData; -class ScriptEngine; -class VoxelEditPacketSender; -class VoxelsScriptingInterface; -struct VoxelDetail; - -const uint32_t NEW_ENTITY = 0xFFFFFFFF; -const uint32_t UNKNOWN_ENTITY_TOKEN = 0xFFFFFFFF; -const uint32_t UNKNOWN_ENTITY_ID = 0xFFFFFFFF; - -const uint16_t ENTITY_PACKET_CONTAINS_RADIUS = 1; -const uint16_t ENTITY_PACKET_CONTAINS_POSITION = 2; -const uint16_t ENTITY_PACKET_CONTAINS_COLOR = 4; -const uint16_t ENTITY_PACKET_CONTAINS_SHOULDDIE = 8; -const uint16_t ENTITY_PACKET_CONTAINS_MODEL_URL = 16; -const uint16_t ENTITY_PACKET_CONTAINS_ROTATION = 32; -const uint16_t ENTITY_PACKET_CONTAINS_ANIMATION_URL = 64; -const uint16_t ENTITY_PACKET_CONTAINS_ANIMATION_PLAYING = 128; -const uint16_t ENTITY_PACKET_CONTAINS_ANIMATION_FRAME = 256; -const uint16_t ENTITY_PACKET_CONTAINS_ANIMATION_FPS = 512; - -const float ENTITY_DEFAULT_RADIUS = 0.1f / TREE_SCALE; -const float ENTITY_MINIMUM_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container -const QString ENTITY_DEFAULT_MODEL_URL(""); -const glm::quat ENTITY_DEFAULT_ROTATION; -const QString ENTITY_DEFAULT_ANIMATION_URL(""); -const float ENTITY_DEFAULT_ANIMATION_FPS = 30.0f; - -// PropertyFlags support - -class EntityTypes { -public: - typedef enum EntityType { - Base, - Model, - Particle, - Box, - Sphere, - Plane, - Cylinder, - Pyramid - } EntityType_t; - - static const QString& getEntityTypeName(EntityType_t entityType); - static bool registerEntityType(EntityType_t entityType, const QString& name); - - static EntityItem* constructEntityItem(EntityType_t entityType, const EntityItemID& entityID, const EntityItemProperties& properties); - static EntityItem* constructEntityItem(const unsigned char* data, int bytesToRead); - static bool decodEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes, - const EntityItemID& entityID, const EntityItemProperties& properties); -private: - static QHash _typeNameHash; -}; - -// PropertyFlags support -enum EntityPropertyList { - PROP_PAGED_PROPERTY, - PROP_CUSTOM_PROPERTIES_INCLUDED, - PROP_VISIBLE, - PROP_POSITION, - PROP_RADIUS, - PROP_ROTATION, - PROP_SCRIPT, - PROP_MODEL_URL, - PROP_COLOR, - PROP_ANIMATION_URL, - PROP_ANIMATION_FPS, - PROP_ANIMATION_FRAME_INDEX, - PROP_ANIMATION_PLAYING, - PROP_SHOULD_BE_DELETED, - PROP_LAST_ITEM = PROP_SHOULD_BE_DELETED -}; - -typedef PropertyFlags EntityPropertyFlags; - - -/// A collection of properties of an entity item used in the scripting API. Translates between the actual properties of an -/// entity and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete -/// set of entity item properties via JavaScript hashes/QScriptValues -/// all units for position, radius, etc are in meter units -class EntityItemProperties { - friend class EntityItem; // TODO: consider removing this friend relationship and have EntityItem use public methods -public: - EntityItemProperties(); - virtual ~EntityItemProperties() { }; - - virtual QScriptValue copyToScriptValue(QScriptEngine* engine) const; - virtual void copyFromScriptValue(const QScriptValue& object); - - // editing related features supported by all entities - quint64 getLastEdited() const { return _lastEdited; } - uint16_t getChangedBits() const; - EntityPropertyFlags getChangedProperties() const; - - /// used by EntityScriptingInterface to return EntityItemProperties for unknown models - void setIsUnknownID() { _id = UNKNOWN_ENTITY_ID; _idSet = true; } - - glm::vec3 getMinimumPointMeters() const { return _position - glm::vec3(_radius, _radius, _radius); } - glm::vec3 getMaximumPointMeters() const { return _position + glm::vec3(_radius, _radius, _radius); } - AACube getAACubeMeters() const { return AACube(getMinimumPointMeters(), getMaxDimension()); } /// AACube in meter units - - glm::vec3 getMinimumPointTreeUnits() const { return getMinimumPointMeters() / (float)TREE_SCALE; } - glm::vec3 getMaximumPointTreeUnits() const { return getMaximumPointMeters() / (float)TREE_SCALE; } - AACube getAACubeTreeUnits() const { return AACube(getMinimumPointMeters()/(float)TREE_SCALE, getMaxDimension()/(float)TREE_SCALE); } /// AACube in domain scale units (0.0 - 1.0) - void debugDump() const; - - // properties of all entities - EntityTypes::EntityType_t getType() const { return _type; } - const glm::vec3& getPosition() const { return _position; } - float getRadius() const { return _radius; } - float getMaxDimension() const { return _radius * 2.0f; } - glm::vec3 getDimensions() const { return glm::vec3(_radius, _radius, _radius) * 2.0f; } - const glm::quat& getRotation() const { return _rotation; } - bool getShouldBeDeleted() const { return _shouldBeDeleted; } - - /// set position in meter units - void setPosition(const glm::vec3& value) { _position = value; _positionChanged = true; } - void setRadius(float value) { _radius = value; _radiusChanged = true; } - void setShouldBeDeleted(bool shouldBeDeleted) { _shouldBeDeleted = shouldBeDeleted; _shouldBeDeletedChanged = true; } - - // NOTE: how do we handle _defaultSettings??? - bool containsBoundsProperties() const { return (_positionChanged || _radiusChanged); } - -#if 0 // def HIDE_SUBCLASS_METHODS - // properties we want to move to just models and particles - xColor getColor() const { return _color; } - const QString& getModelURL() const { return _modelURL; } - const QString& getAnimationURL() const { return _animationURL; } - float getAnimationFrameIndex() const { return _animationFrameIndex; } - bool getAnimationIsPlaying() const { return _animationIsPlaying; } - float getAnimationFPS() const { return _animationFPS; } - float getGlowLevel() const { return _glowLevel; } - - // model related properties - void setColor(const xColor& value) { _color = value; _colorChanged = true; } - void setModelURL(const QString& url) { _modelURL = url; _modelURLChanged = true; } - void setRotation(const glm::quat& rotation) { _rotation = rotation; _rotationChanged = true; } - void setAnimationURL(const QString& url) { _animationURL = url; _animationURLChanged = true; } - void setAnimationFrameIndex(float value) { _animationFrameIndex = value; _animationFrameIndexChanged = true; } - void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; _animationIsPlayingChanged = true; } - void setAnimationFPS(float value) { _animationFPS = value; _animationFPSChanged = true; } - void setGlowLevel(float value) { _glowLevel = value; _glowLevelChanged = true; } -#endif - -private: - quint32 _id; - bool _idSet; - quint64 _lastEdited; - - EntityTypes::EntityType_t _type; - glm::vec3 _position; - float _radius; - glm::quat _rotation; - bool _shouldBeDeleted; - - bool _positionChanged; - bool _radiusChanged; - bool _rotationChanged; - bool _shouldBeDeletedChanged; - -#if 0 // def HIDE_SUBCLASS_METHODS - xColor _color; - QString _modelURL; - QString _animationURL; - bool _animationIsPlaying; - float _animationFrameIndex; - float _animationFPS; - float _glowLevel; - QVector _sittingPoints; - - bool _colorChanged; - bool _modelURLChanged; - bool _animationURLChanged; - bool _animationIsPlayingChanged; - bool _animationFrameIndexChanged; - bool _animationFPSChanged; - bool _glowLevelChanged; -#endif - - bool _defaultSettings; -}; -Q_DECLARE_METATYPE(EntityItemProperties); -QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties); -void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemProperties& properties); - - -/// Abstract ID for editing model items. Used in EntityItem JS API - When models are created in the JS api, they are given a -/// local creatorTokenID, the actual id for the model is not known until the server responds to the creator with the -/// correct mapping. This class works with the scripting API an allows the developer to edit models they created. -class EntityItemID { -public: - EntityItemID() : - id(NEW_ENTITY), creatorTokenID(UNKNOWN_ENTITY_TOKEN), isKnownID(false) { - //qDebug() << "EntityItemID::EntityItemID()... isKnownID=" << isKnownID << "id=" << id << "creatorTokenID=" << creatorTokenID; - }; - - EntityItemID(uint32_t id, uint32_t creatorTokenID, bool isKnownID) : - id(id), creatorTokenID(creatorTokenID), isKnownID(isKnownID) { - //qDebug() << "EntityItemID::EntityItemID(uint32_t id, uint32_t creatorTokenID, bool isKnownID)... isKnownID=" << isKnownID << "id=" << id << "creatorTokenID=" << creatorTokenID; - }; - - EntityItemID(uint32_t id) : - id(id), creatorTokenID(UNKNOWN_ENTITY_TOKEN), isKnownID(true) { - //qDebug() << "EntityItemID::EntityItemID(uint32_t id)... isKnownID=" << isKnownID << "id=" << id << "creatorTokenID=" << creatorTokenID; - }; - - uint32_t id; - uint32_t creatorTokenID; - bool isKnownID; -}; - -inline bool operator<(const EntityItemID& a, const EntityItemID& b) { - return (a.id == b.id) ? (a.creatorTokenID < b.creatorTokenID) : (a.id < b.id); -} - -inline bool operator==(const EntityItemID& a, const EntityItemID& b) { - if (a.id == UNKNOWN_ENTITY_ID && b.id == UNKNOWN_ENTITY_ID) { - return a.creatorTokenID == b.creatorTokenID; - } - return a.id == b.id; -} - -inline uint qHash(const EntityItemID& a, uint seed) { - qint64 temp; - if (a.id == UNKNOWN_ENTITY_ID) { - temp = -a.creatorTokenID; - } else { - temp = a.id; - } - return qHash(temp, seed); -} - -inline QDebug operator<<(QDebug debug, const EntityItemID& id) { - debug << "[ id:" << id.id << ", creatorTokenID:" << id.creatorTokenID << ", isKnownID:" << id.isKnownID << "]"; - return debug; -} - -Q_DECLARE_METATYPE(EntityItemID); -Q_DECLARE_METATYPE(QVector); -QScriptValue EntityItemIDtoScriptValue(QScriptEngine* engine, const EntityItemID& properties); -void EntityItemIDfromScriptValue(const QScriptValue &object, EntityItemID& properties); - - /// EntityItem class - this is the actual model item class. class EntityItem { @@ -287,9 +37,6 @@ public: EntityItem(const EntityItemID& entityItemID); EntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); - /// creates an NEW model from an model add or edit message data buffer - static EntityItem* fromEditPacket(const unsigned char* data, int length, int& processedBytes, EntityTree* tree, bool& valid); - virtual ~EntityItem(); virtual void somePureVirtualFunction() = 0; @@ -444,7 +191,7 @@ protected: class ModelEntityItem : public EntityItem { public: ModelEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : - EntityItem(entityItemID, properties) { }; + EntityItem(entityItemID, properties) { _type = EntityTypes::Model; } virtual void somePureVirtualFunction() { }; // allow this class to be constructed }; @@ -452,7 +199,7 @@ public: class ParticleEntityItem : public EntityItem { public: ParticleEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : - EntityItem(entityItemID, properties) { }; + EntityItem(entityItemID, properties) { _type = EntityTypes::Particle; } virtual void somePureVirtualFunction() { }; // allow this class to be constructed }; @@ -460,7 +207,7 @@ public: class BoxEntityItem : public EntityItem { public: BoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : - EntityItem(entityItemID, properties) { }; + EntityItem(entityItemID, properties) { _type = EntityTypes::Box; } virtual void somePureVirtualFunction() { }; // allow this class to be constructed }; @@ -468,7 +215,7 @@ public: class SphereEntityItem : public EntityItem { public: SphereEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : - EntityItem(entityItemID, properties) { }; + EntityItem(entityItemID, properties) { _type = EntityTypes::Sphere; } virtual void somePureVirtualFunction() { }; // allow this class to be constructed }; @@ -476,7 +223,7 @@ public: class PlaneEntityItem : public EntityItem { public: PlaneEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : - EntityItem(entityItemID, properties) { }; + EntityItem(entityItemID, properties) { _type = EntityTypes::Plane; } virtual void somePureVirtualFunction() { }; // allow this class to be constructed }; @@ -484,7 +231,7 @@ public: class CylinderEntityItem : public EntityItem { public: CylinderEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : - EntityItem(entityItemID, properties) { }; + EntityItem(entityItemID, properties) { _type = EntityTypes::Cylinder; } virtual void somePureVirtualFunction() { }; // allow this class to be constructed }; @@ -492,7 +239,7 @@ public: class PyramidEntityItem : public EntityItem { public: PyramidEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : - EntityItem(entityItemID, properties) { }; + EntityItem(entityItemID, properties) { _type = EntityTypes::Pyramid; } virtual void somePureVirtualFunction() { }; // allow this class to be constructed }; diff --git a/libraries/entities/src/EntityItemID.cpp b/libraries/entities/src/EntityItemID.cpp new file mode 100644 index 0000000000..910b69edd5 --- /dev/null +++ b/libraries/entities/src/EntityItemID.cpp @@ -0,0 +1,32 @@ +// +// EntityItemID.cpp +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright 2013 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 +#include + +#include "EntityItemID.h" + +QScriptValue EntityItemIDtoScriptValue(QScriptEngine* engine, const EntityItemID& id) { + QScriptValue obj = engine->newObject(); + obj.setProperty("id", id.id); + obj.setProperty("creatorTokenID", id.creatorTokenID); + obj.setProperty("isKnownID", id.isKnownID); + return obj; +} + +void EntityItemIDfromScriptValue(const QScriptValue &object, EntityItemID& id) { + id.id = object.property("id").toVariant().toUInt(); + id.creatorTokenID = object.property("creatorTokenID").toVariant().toUInt(); + id.isKnownID = object.property("isKnownID").toVariant().toBool(); +} + + + diff --git a/libraries/entities/src/EntityItemID.h b/libraries/entities/src/EntityItemID.h new file mode 100644 index 0000000000..0675389da7 --- /dev/null +++ b/libraries/entities/src/EntityItemID.h @@ -0,0 +1,82 @@ +// +// EntityItemID.h +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright 2013 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_EntityItemID_h +#define hifi_EntityItemID_h + +#include + +#include +#include + +const uint32_t NEW_ENTITY = 0xFFFFFFFF; +const uint32_t UNKNOWN_ENTITY_TOKEN = 0xFFFFFFFF; +const uint32_t UNKNOWN_ENTITY_ID = 0xFFFFFFFF; + + +/// Abstract ID for editing model items. Used in EntityItem JS API - When models are created in the JS api, they are given a +/// local creatorTokenID, the actual id for the model is not known until the server responds to the creator with the +/// correct mapping. This class works with the scripting API an allows the developer to edit models they created. +class EntityItemID { +public: + EntityItemID() : + id(NEW_ENTITY), creatorTokenID(UNKNOWN_ENTITY_TOKEN), isKnownID(false) { + //qDebug() << "EntityItemID::EntityItemID()... isKnownID=" << isKnownID << "id=" << id << "creatorTokenID=" << creatorTokenID; + }; + + EntityItemID(uint32_t id, uint32_t creatorTokenID, bool isKnownID) : + id(id), creatorTokenID(creatorTokenID), isKnownID(isKnownID) { + //qDebug() << "EntityItemID::EntityItemID(uint32_t id, uint32_t creatorTokenID, bool isKnownID)... isKnownID=" << isKnownID << "id=" << id << "creatorTokenID=" << creatorTokenID; + }; + + EntityItemID(uint32_t id) : + id(id), creatorTokenID(UNKNOWN_ENTITY_TOKEN), isKnownID(true) { + //qDebug() << "EntityItemID::EntityItemID(uint32_t id)... isKnownID=" << isKnownID << "id=" << id << "creatorTokenID=" << creatorTokenID; + }; + + uint32_t id; + uint32_t creatorTokenID; + bool isKnownID; +}; + +inline bool operator<(const EntityItemID& a, const EntityItemID& b) { + return (a.id == b.id) ? (a.creatorTokenID < b.creatorTokenID) : (a.id < b.id); +} + +inline bool operator==(const EntityItemID& a, const EntityItemID& b) { + if (a.id == UNKNOWN_ENTITY_ID && b.id == UNKNOWN_ENTITY_ID) { + return a.creatorTokenID == b.creatorTokenID; + } + return a.id == b.id; +} + +inline uint qHash(const EntityItemID& a, uint seed) { + qint64 temp; + if (a.id == UNKNOWN_ENTITY_ID) { + temp = -a.creatorTokenID; + } else { + temp = a.id; + } + return qHash(temp, seed); +} + +inline QDebug operator<<(QDebug debug, const EntityItemID& id) { + debug << "[ id:" << id.id << ", creatorTokenID:" << id.creatorTokenID << ", isKnownID:" << id.isKnownID << "]"; + return debug; +} + +Q_DECLARE_METATYPE(EntityItemID); +Q_DECLARE_METATYPE(QVector); +QScriptValue EntityItemIDtoScriptValue(QScriptEngine* engine, const EntityItemID& properties); +void EntityItemIDfromScriptValue(const QScriptValue &object, EntityItemID& properties); + + +#endif // hifi_EntityItemID_h diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp new file mode 100644 index 0000000000..beb7840249 --- /dev/null +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -0,0 +1,360 @@ +// +// EntityItemProperties.cpp +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright 2013 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 +#include + +#include + +#include "EntityItemProperties.h" + +EntityItemProperties::EntityItemProperties() : + + _id(UNKNOWN_ENTITY_ID), + _idSet(false), + _lastEdited(usecTimestampNow()), + _type(EntityTypes::Base), + + _position(0), + _radius(ENTITY_DEFAULT_RADIUS), + _rotation(ENTITY_DEFAULT_ROTATION), + _shouldBeDeleted(false), + + _positionChanged(false), + _radiusChanged(false), + _rotationChanged(false), + _shouldBeDeletedChanged(false), + + +#if 0 //def HIDE_SUBCLASS_METHODS + _color(), + _modelURL(""), + _animationURL(""), + _animationIsPlaying(false), + _animationFrameIndex(0.0), + _animationFPS(ENTITY_DEFAULT_ANIMATION_FPS), + _glowLevel(0.0f), + + _colorChanged(false), + _modelURLChanged(false), + _animationURLChanged(false), + _animationIsPlayingChanged(false), + _animationFrameIndexChanged(false), + _animationFPSChanged(false), + _glowLevelChanged(false), +#endif + + _defaultSettings(true) +{ +} + +void EntityItemProperties::debugDump() const { + qDebug() << "EntityItemProperties..."; + qDebug() << " _id=" << _id; + qDebug() << " _idSet=" << _idSet; + qDebug() << " _position=" << _position.x << "," << _position.y << "," << _position.z; + qDebug() << " _radius=" << _radius; +} + + +uint16_t EntityItemProperties::getChangedBits() const { + uint16_t changedBits = 0; + if (_radiusChanged) { + changedBits += ENTITY_PACKET_CONTAINS_RADIUS; + } + + if (_positionChanged) { + changedBits += ENTITY_PACKET_CONTAINS_POSITION; + } + + if (_rotationChanged) { + changedBits += ENTITY_PACKET_CONTAINS_ROTATION; + } + + if (_shouldBeDeletedChanged) { + changedBits += ENTITY_PACKET_CONTAINS_SHOULDDIE; + } + +#if 0 //def HIDE_SUBCLASS_METHODS + if (_colorChanged) { + changedBits += ENTITY_PACKET_CONTAINS_COLOR; + } + + if (_modelURLChanged) { + changedBits += ENTITY_PACKET_CONTAINS_MODEL_URL; + } + + if (_animationURLChanged) { + changedBits += ENTITY_PACKET_CONTAINS_ANIMATION_URL; + } + + if (_animationIsPlayingChanged) { + changedBits += ENTITY_PACKET_CONTAINS_ANIMATION_PLAYING; + } + + if (_animationFrameIndexChanged) { + changedBits += ENTITY_PACKET_CONTAINS_ANIMATION_FRAME; + } + + if (_animationFPSChanged) { + changedBits += ENTITY_PACKET_CONTAINS_ANIMATION_FPS; + } +#endif + + return changedBits; +} + +EntityPropertyFlags EntityItemProperties::getChangedProperties() const { + EntityPropertyFlags changedProperties; + if (_radiusChanged) { + changedProperties += PROP_RADIUS; + } + + if (_positionChanged) { + changedProperties += PROP_POSITION; + } + + if (_rotationChanged) { + changedProperties += PROP_ROTATION; + } + + if (_shouldBeDeletedChanged) { + changedProperties += PROP_SHOULD_BE_DELETED; + } + +#if 0 //def HIDE_SUBCLASS_METHODS + if (_colorChanged) { + changedProperties += PROP_COLOR; + } + + if (_modelURLChanged) { + changedProperties += PROP_MODEL_URL; + } + + if (_animationURLChanged) { + changedProperties += PROP_ANIMATION_URL; + } + + if (_animationIsPlayingChanged) { + changedProperties += PROP_ANIMATION_PLAYING; + } + + if (_animationFrameIndexChanged) { + changedProperties += PROP_ANIMATION_FRAME_INDEX; + } + + if (_animationFPSChanged) { + changedProperties += PROP_ANIMATION_FPS; + } +#endif + + return changedProperties; +} + +QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) const { + QScriptValue properties = engine->newObject(); + + if (_idSet) { + properties.setProperty("id", _id); + bool isKnownID = (_id != UNKNOWN_ENTITY_ID); + properties.setProperty("isKnownID", isKnownID); +qDebug() << "EntityItemProperties::copyToScriptValue()... isKnownID=" << isKnownID << "id=" << _id; + } + + QScriptValue position = vec3toScriptValue(engine, _position); + properties.setProperty("position", position); + properties.setProperty("radius", _radius); + QScriptValue rotation = quatToScriptValue(engine, _rotation); + properties.setProperty("rotation", rotation); + properties.setProperty("shouldBeDeleted", _shouldBeDeleted); + +#if 0 // def HIDE_SUBCLASS_METHODS + QScriptValue color = xColorToScriptValue(engine, _color); + properties.setProperty("color", color); + properties.setProperty("modelURL", _modelURL); + properties.setProperty("animationURL", _animationURL); + properties.setProperty("animationIsPlaying", _animationIsPlaying); + properties.setProperty("animationFrameIndex", _animationFrameIndex); + properties.setProperty("animationFPS", _animationFPS); + properties.setProperty("glowLevel", _glowLevel); + + // Sitting properties support + QScriptValue sittingPoints = engine->newObject(); + for (int i = 0; i < _sittingPoints.size(); ++i) { + QScriptValue sittingPoint = engine->newObject(); + sittingPoint.setProperty("name", _sittingPoints[i].name); + sittingPoint.setProperty("position", vec3toScriptValue(engine, _sittingPoints[i].position)); + sittingPoint.setProperty("rotation", quatToScriptValue(engine, _sittingPoints[i].rotation)); + sittingPoints.setProperty(i, sittingPoint); + } + sittingPoints.setProperty("length", _sittingPoints.size()); + properties.setProperty("sittingPoints", sittingPoints); +#endif // HIDE_SUBCLASS_METHODS + + return properties; +} + +void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) { + + QScriptValue typeScriptValue = object.property("type"); + if (typeScriptValue.isValid()) { + QString typeName; + typeName = typeScriptValue.toVariant().toString(); + _type = EntityTypes::getEntityTypeFromName(typeName); + } + + QScriptValue position = object.property("position"); + if (position.isValid()) { + QScriptValue x = position.property("x"); + QScriptValue y = position.property("y"); + QScriptValue z = position.property("z"); + if (x.isValid() && y.isValid() && z.isValid()) { + glm::vec3 newPosition; + newPosition.x = x.toVariant().toFloat(); + newPosition.y = y.toVariant().toFloat(); + newPosition.z = z.toVariant().toFloat(); + if (_defaultSettings || newPosition != _position) { + _position = newPosition; + _positionChanged = true; + } + } + } + + QScriptValue radius = object.property("radius"); + if (radius.isValid()) { + float newRadius; + newRadius = radius.toVariant().toFloat(); + if (_defaultSettings || newRadius != _radius) { + _radius = newRadius; + _radiusChanged = true; + } + } + + QScriptValue rotation = object.property("rotation"); + if (rotation.isValid()) { + QScriptValue x = rotation.property("x"); + QScriptValue y = rotation.property("y"); + QScriptValue z = rotation.property("z"); + QScriptValue w = rotation.property("w"); + if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { + glm::quat newRotation; + newRotation.x = x.toVariant().toFloat(); + newRotation.y = y.toVariant().toFloat(); + newRotation.z = z.toVariant().toFloat(); + newRotation.w = w.toVariant().toFloat(); + if (_defaultSettings || newRotation != _rotation) { + _rotation = newRotation; + _rotationChanged = true; + } + } + } + + QScriptValue shouldBeDeleted = object.property("shouldBeDeleted"); + if (shouldBeDeleted.isValid()) { + bool newShouldBeDeleted; + newShouldBeDeleted = shouldBeDeleted.toVariant().toBool(); + if (_defaultSettings || newShouldBeDeleted != _shouldBeDeleted) { + _shouldBeDeleted = newShouldBeDeleted; + _shouldBeDeletedChanged = true; + } + } + +#if 0 //def HIDE_SUBCLASS_METHODS + QScriptValue color = object.property("color"); + if (color.isValid()) { + QScriptValue red = color.property("red"); + QScriptValue green = color.property("green"); + QScriptValue blue = color.property("blue"); + if (red.isValid() && green.isValid() && blue.isValid()) { + xColor newColor; + newColor.red = red.toVariant().toInt(); + newColor.green = green.toVariant().toInt(); + newColor.blue = blue.toVariant().toInt(); + if (_defaultSettings || (newColor.red != _color.red || + newColor.green != _color.green || + newColor.blue != _color.blue)) { + _color = newColor; + _colorChanged = true; + } + } + } + + QScriptValue modelURL = object.property("modelURL"); + if (modelURL.isValid()) { + QString newModelURL; + newModelURL = modelURL.toVariant().toString(); + if (_defaultSettings || newModelURL != _modelURL) { + _modelURL = newModelURL; + _modelURLChanged = true; + } + } + + QScriptValue animationURL = object.property("animationURL"); + if (animationURL.isValid()) { + QString newAnimationURL; + newAnimationURL = animationURL.toVariant().toString(); + if (_defaultSettings || newAnimationURL != _animationURL) { + _animationURL = newAnimationURL; + _animationURLChanged = true; + } + } + + QScriptValue animationIsPlaying = object.property("animationIsPlaying"); + if (animationIsPlaying.isValid()) { + bool newIsAnimationPlaying; + newIsAnimationPlaying = animationIsPlaying.toVariant().toBool(); + if (_defaultSettings || newIsAnimationPlaying != _animationIsPlaying) { + _animationIsPlaying = newIsAnimationPlaying; + _animationIsPlayingChanged = true; + } + } + + QScriptValue animationFrameIndex = object.property("animationFrameIndex"); + if (animationFrameIndex.isValid()) { + float newFrameIndex; + newFrameIndex = animationFrameIndex.toVariant().toFloat(); + if (_defaultSettings || newFrameIndex != _animationFrameIndex) { + _animationFrameIndex = newFrameIndex; + _animationFrameIndexChanged = true; + } + } + + QScriptValue animationFPS = object.property("animationFPS"); + if (animationFPS.isValid()) { + float newFPS; + newFPS = animationFPS.toVariant().toFloat(); + if (_defaultSettings || newFPS != _animationFPS) { + _animationFPS = newFPS; + _animationFPSChanged = true; + } + } + + QScriptValue glowLevel = object.property("glowLevel"); + if (glowLevel.isValid()) { + float newGlowLevel; + newGlowLevel = glowLevel.toVariant().toFloat(); + if (_defaultSettings || newGlowLevel != _glowLevel) { + _glowLevel = newGlowLevel; + _glowLevelChanged = true; + } + } +#endif + + _lastEdited = usecTimestampNow(); +} + +QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties) { + return properties.copyToScriptValue(engine); +} + +void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemProperties& properties) { + properties.copyFromScriptValue(object); +} diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h new file mode 100644 index 0000000000..ffed253d30 --- /dev/null +++ b/libraries/entities/src/EntityItemProperties.h @@ -0,0 +1,187 @@ +// +// EntityItemProperties.h +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright 2013 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_EntityItemProperties_h +#define hifi_EntityItemProperties_h + +#define HIDE_SUBCLASS_METHODS 1 + +#include + +#include + +#include +#include +#include +#include + +#include +#include // for SittingPoint +#include +#include + + +#include "EntityItemID.h" +#include "EntityTypes.h" + + +const uint16_t ENTITY_PACKET_CONTAINS_RADIUS = 1; +const uint16_t ENTITY_PACKET_CONTAINS_POSITION = 2; +const uint16_t ENTITY_PACKET_CONTAINS_COLOR = 4; +const uint16_t ENTITY_PACKET_CONTAINS_SHOULDDIE = 8; +const uint16_t ENTITY_PACKET_CONTAINS_MODEL_URL = 16; +const uint16_t ENTITY_PACKET_CONTAINS_ROTATION = 32; +const uint16_t ENTITY_PACKET_CONTAINS_ANIMATION_URL = 64; +const uint16_t ENTITY_PACKET_CONTAINS_ANIMATION_PLAYING = 128; +const uint16_t ENTITY_PACKET_CONTAINS_ANIMATION_FRAME = 256; +const uint16_t ENTITY_PACKET_CONTAINS_ANIMATION_FPS = 512; + +const float ENTITY_DEFAULT_RADIUS = 0.1f / TREE_SCALE; +const float ENTITY_MINIMUM_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container +const QString ENTITY_DEFAULT_MODEL_URL(""); +const glm::quat ENTITY_DEFAULT_ROTATION; +const QString ENTITY_DEFAULT_ANIMATION_URL(""); +const float ENTITY_DEFAULT_ANIMATION_FPS = 30.0f; + +// PropertyFlags support +enum EntityPropertyList { + PROP_PAGED_PROPERTY, + PROP_CUSTOM_PROPERTIES_INCLUDED, + PROP_VISIBLE, + PROP_POSITION, + PROP_RADIUS, + PROP_ROTATION, + PROP_SCRIPT, + PROP_MODEL_URL, + PROP_COLOR, + PROP_ANIMATION_URL, + PROP_ANIMATION_FPS, + PROP_ANIMATION_FRAME_INDEX, + PROP_ANIMATION_PLAYING, + PROP_SHOULD_BE_DELETED, + PROP_LAST_ITEM = PROP_SHOULD_BE_DELETED +}; + +typedef PropertyFlags EntityPropertyFlags; + + +/// A collection of properties of an entity item used in the scripting API. Translates between the actual properties of an +/// entity and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete +/// set of entity item properties via JavaScript hashes/QScriptValues +/// all units for position, radius, etc are in meter units +class EntityItemProperties { + friend class EntityItem; // TODO: consider removing this friend relationship and have EntityItem use public methods +public: + EntityItemProperties(); + virtual ~EntityItemProperties() { }; + + virtual QScriptValue copyToScriptValue(QScriptEngine* engine) const; + virtual void copyFromScriptValue(const QScriptValue& object); + + // editing related features supported by all entities + quint64 getLastEdited() const { return _lastEdited; } + uint16_t getChangedBits() const; + EntityPropertyFlags getChangedProperties() const; + + /// used by EntityScriptingInterface to return EntityItemProperties for unknown models + void setIsUnknownID() { _id = UNKNOWN_ENTITY_ID; _idSet = true; } + + glm::vec3 getMinimumPointMeters() const { return _position - glm::vec3(_radius, _radius, _radius); } + glm::vec3 getMaximumPointMeters() const { return _position + glm::vec3(_radius, _radius, _radius); } + AACube getAACubeMeters() const { return AACube(getMinimumPointMeters(), getMaxDimension()); } /// AACube in meter units + + glm::vec3 getMinimumPointTreeUnits() const { return getMinimumPointMeters() / (float)TREE_SCALE; } + glm::vec3 getMaximumPointTreeUnits() const { return getMaximumPointMeters() / (float)TREE_SCALE; } + AACube getAACubeTreeUnits() const { return AACube(getMinimumPointMeters()/(float)TREE_SCALE, getMaxDimension()/(float)TREE_SCALE); } /// AACube in domain scale units (0.0 - 1.0) + void debugDump() const; + + // properties of all entities + EntityTypes::EntityType_t getType() const { return _type; } + const glm::vec3& getPosition() const { return _position; } + float getRadius() const { return _radius; } + float getMaxDimension() const { return _radius * 2.0f; } + glm::vec3 getDimensions() const { return glm::vec3(_radius, _radius, _radius) * 2.0f; } + const glm::quat& getRotation() const { return _rotation; } + bool getShouldBeDeleted() const { return _shouldBeDeleted; } + + void setType(EntityTypes::EntityType_t type) { _type = type; } + /// set position in meter units + void setPosition(const glm::vec3& value) { _position = value; _positionChanged = true; } + void setRadius(float value) { _radius = value; _radiusChanged = true; } + void setRotation(const glm::quat& rotation) { _rotation = rotation; _rotationChanged = true; } + void setShouldBeDeleted(bool shouldBeDeleted) { _shouldBeDeleted = shouldBeDeleted; _shouldBeDeletedChanged = true; } + + // NOTE: how do we handle _defaultSettings??? + bool containsBoundsProperties() const { return (_positionChanged || _radiusChanged); } + +#ifdef HIDE_SUBCLASS_METHODS + // properties we want to move to just models and particles + xColor getColor() const { return _color; } + const QString& getModelURL() const { return _modelURL; } + const QString& getAnimationURL() const { return _animationURL; } + float getAnimationFrameIndex() const { return _animationFrameIndex; } + bool getAnimationIsPlaying() const { return _animationIsPlaying; } + float getAnimationFPS() const { return _animationFPS; } + float getGlowLevel() const { return _glowLevel; } + + // model related properties + void setColor(const xColor& value) { _color = value; _colorChanged = true; } + void setModelURL(const QString& url) { _modelURL = url; _modelURLChanged = true; } + void setAnimationURL(const QString& url) { _animationURL = url; _animationURLChanged = true; } + void setAnimationFrameIndex(float value) { _animationFrameIndex = value; _animationFrameIndexChanged = true; } + void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; _animationIsPlayingChanged = true; } + void setAnimationFPS(float value) { _animationFPS = value; _animationFPSChanged = true; } + void setGlowLevel(float value) { _glowLevel = value; _glowLevelChanged = true; } +#endif + +private: + quint32 _id; + bool _idSet; + quint64 _lastEdited; + + EntityTypes::EntityType_t _type; + glm::vec3 _position; + float _radius; + glm::quat _rotation; + bool _shouldBeDeleted; + + bool _positionChanged; + bool _radiusChanged; + bool _rotationChanged; + bool _shouldBeDeletedChanged; + +#ifdef HIDE_SUBCLASS_METHODS + xColor _color; + QString _modelURL; + QString _animationURL; + bool _animationIsPlaying; + float _animationFrameIndex; + float _animationFPS; + float _glowLevel; + QVector _sittingPoints; + + bool _colorChanged; + bool _modelURLChanged; + bool _animationURLChanged; + bool _animationIsPlayingChanged; + bool _animationFrameIndexChanged; + bool _animationFPSChanged; + bool _glowLevelChanged; +#endif + + bool _defaultSettings; +}; +Q_DECLARE_METATYPE(EntityItemProperties); +QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties); +void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemProperties& properties); + + +#endif // hifi_EntityItemProperties_h diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index bd3f3856af..0a79aa87d9 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -8,6 +8,8 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// TODO: How will we handle collision callbacks with Entities +// #ifndef hifi_EntityScriptingInterface_h #define hifi_EntityScriptingInterface_h @@ -21,6 +23,10 @@ #include "EntityEditPacketSender.h" + +class EntityTree; + + class RayToEntityIntersectionResult { public: RayToEntityIntersectionResult(); @@ -91,10 +97,6 @@ public slots: Q_INVOKABLE void dumpTree() const; -signals: - void modelCollisionWithVoxel(const EntityItemID& entityID, const VoxelDetail& voxel, const CollisionInfo& collision); - void modelCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const CollisionInfo& collision); - private: void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 72d7364d63..5939c975b4 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -797,7 +797,7 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char EntityItemID entityItemID; EntityItemProperties properties; - bool validEditPacket = EntityTypes::decodEntityEditPacket(editData, maxLength, + bool validEditPacket = EntityTypes::decodeEntityEditPacket(editData, maxLength, processedBytes, entityItemID, properties); // If we got a valid edit packet, then it could be a new entity or it could be an update to @@ -816,20 +816,8 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char } } } - - -#if 0 ////// OLD CODE... - bool isValid = false; - EntityItem* newEntity = NULL; // EntityItem::fromEditPacket(editData, maxLength, processedBytes, this, isValid); - if (isValid) { - storeEntity(newEntity, senderNode); - if (newEntity.isNewlyCreated()) { - notifyNewlyCreatedEntity(newEntity, senderNode); - } - } -#endif - - } break; + break; + } default: processedBytes = 0; diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp new file mode 100644 index 0000000000..892f758f39 --- /dev/null +++ b/libraries/entities/src/EntityTypes.cpp @@ -0,0 +1,311 @@ +// +// EntityTypes.cpp +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright 2013 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 + +#include + +#include "EntityItem.h" +#include "EntityItemProperties.h" +#include "EntityTypes.h" + +QHash EntityTypes::_typeNameHash; + +const QString UNKNOWN_EntityType_t_NAME = "Unknown"; +const QString& EntityTypes::getEntityTypeName(EntityType_t entityType) { + QHash::iterator matchedTypeName = _typeNameHash.find(entityType); + return matchedTypeName != _typeNameHash.end() ? matchedTypeName.value() : UNKNOWN_EntityType_t_NAME; +} + +bool EntityTypes::registerEntityType(EntityType_t entityType, const QString& name) { + _typeNameHash.insert(entityType, name); + return true; +} + +EntityTypes::EntityType_t EntityTypes::getEntityTypeFromName(const QString& name) { + return Base; // TODO: support registration +} + +EntityItem* EntityTypes::constructEntityItem(EntityType_t entityType, const EntityItemID& entityID, const EntityItemProperties& properties) { + EntityItem* newEntityItem = NULL; + + // switch statement for now, needs to support registration of constructor + switch (entityType) { + // Base, // ??? not supported? + case Model: + newEntityItem = new ModelEntityItem(entityID, properties); + break; + + case Particle: + newEntityItem = new ParticleEntityItem(entityID, properties); + break; + + case Box: + newEntityItem = new BoxEntityItem(entityID, properties); + break; + + case Sphere: + newEntityItem = new SphereEntityItem(entityID, properties); + break; + + case Plane: + newEntityItem = new PlaneEntityItem(entityID, properties); + break; + + case Cylinder: + newEntityItem = new CylinderEntityItem(entityID, properties); + break; + + case Pyramid: + newEntityItem = new PyramidEntityItem(entityID, properties); + break; + + default: + newEntityItem = new ModelEntityItem(entityID, properties); + break; + } + return newEntityItem; +} + +EntityItem* EntityTypes::constructEntityItem(const unsigned char* data, int bytesToRead) { + return NULL; // TODO Implement this for real! +} + + +// TODO: +// how to handle lastEdited? +// how to handle lastUpdated? +// consider handling case where no properties are included... we should just ignore this packet... +bool EntityTypes::decodeEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes, + EntityItemID& entityID, EntityItemProperties& properties) { + bool valid = false; + + bool wantDebug = false; + if (wantDebug) { + qDebug() << "EntityItem EntityItem::decodeEntityEditPacket() bytesToRead=" << bytesToRead; + } + + const unsigned char* dataAt = data; + processedBytes = 0; + + // the first part of the data is an octcode, this is a required element of the edit packet format, but we don't + // actually use it, we do need to skip it and read to the actual data we care about. + int octets = numberOfThreeBitSectionsInCode(data); + int bytesToReadOfOctcode = bytesRequiredForCodeLength(octets); + + if (wantDebug) { + qDebug() << "EntityItem EntityItem::decodeEntityEditPacket() bytesToReadOfOctcode=" << bytesToReadOfOctcode; + } + + // we don't actually do anything with this octcode... + dataAt += bytesToReadOfOctcode; + processedBytes += bytesToReadOfOctcode; + + // Edit packets have a last edited time stamp immediately following the octcode. + // NOTE: the edit times have been set by the editor to match out clock, so we don't need to adjust + // these times for clock skew at this point. + quint64 lastEdited; + memcpy(&lastEdited, dataAt, sizeof(lastEdited)); + dataAt += sizeof(lastEdited); + processedBytes += sizeof(lastEdited); + + // encoded id + QByteArray encodedID((const char*)dataAt, (bytesToRead - processedBytes)); + ByteCountCoded idCoder = encodedID; + quint32 editID = idCoder; + encodedID = idCoder; // determine true bytesToRead + dataAt += encodedID.size(); + processedBytes += encodedID.size(); + + if (wantDebug) { + qDebug() << "EntityItem EntityItem::decodeEntityEditPacket() editID=" << editID; + } + + bool isNewEntityItem = (editID == NEW_ENTITY); + + if (isNewEntityItem) { + // If this is a NEW_ENTITY, then we assume that there's an additional uint32_t creatorToken, that + // we want to send back to the creator as an map to the actual id + + QByteArray encodedToken((const char*)dataAt, (bytesToRead - processedBytes)); + ByteCountCoded tokenCoder = encodedToken; + quint32 creatorTokenID = tokenCoder; + encodedToken = tokenCoder; // determine true bytesToRead + dataAt += encodedToken.size(); + processedBytes += encodedToken.size(); + + //newEntityItem.setCreatorTokenID(creatorTokenID); + //newEntityItem._newlyCreated = true; + + entityID.id = NEW_ENTITY; + entityID.creatorTokenID = creatorTokenID; + entityID.isKnownID = false; + + valid = true; + + } else { + entityID.id = editID; + entityID.creatorTokenID = UNKNOWN_ENTITY_TOKEN; + entityID.isKnownID = true; + valid = true; + } + + // Entity Type... + QByteArray encodedType((const char*)dataAt, (bytesToRead - processedBytes)); + ByteCountCoded typeCoder = encodedType; + quint32 entityTypeCode = typeCoder; + properties.setType((EntityTypes::EntityType_t)entityTypeCode); + encodedType = typeCoder; // determine true bytesToRead + dataAt += encodedType.size(); + processedBytes += encodedType.size(); + + + // Update Delta - when was this item updated relative to last edit... this really should be 0 + // TODO: Should we get rid of this in this in edit packets, since this has to always be 0? + // TODO: do properties need to handle lastupdated??? + + // last updated is stored as ByteCountCoded delta from lastEdited + QByteArray encodedUpdateDelta((const char*)dataAt, (bytesToRead - processedBytes)); + ByteCountCoded updateDeltaCoder = encodedUpdateDelta; + quint64 updateDelta = updateDeltaCoder; + quint64 lastUpdated = lastEdited + updateDelta; // don't adjust for clock skew since we already did that for lastEdited + encodedUpdateDelta = updateDeltaCoder; // determine true bytesToRead + dataAt += encodedUpdateDelta.size(); + processedBytes += encodedUpdateDelta.size(); + + // Property Flags... + QByteArray encodedPropertyFlags((const char*)dataAt, (bytesToRead - processedBytes)); + EntityPropertyFlags propertyFlags = encodedPropertyFlags; + dataAt += propertyFlags.getEncodedLength(); + processedBytes += propertyFlags.getEncodedLength(); + + + // PROP_POSITION + if (propertyFlags.getHasProperty(PROP_POSITION)) { + glm::vec3 position; + memcpy(&position, dataAt, sizeof(position)); + dataAt += sizeof(position); + processedBytes += sizeof(position); + properties.setPosition(position); + } + + // PROP_RADIUS + if (propertyFlags.getHasProperty(PROP_RADIUS)) { + float radius; + memcpy(&radius, dataAt, sizeof(radius)); + dataAt += sizeof(radius); + processedBytes += sizeof(radius); + properties.setRadius(radius); + } + + // PROP_ROTATION + if (propertyFlags.getHasProperty(PROP_ROTATION)) { + glm::quat rotation; + int bytes = unpackOrientationQuatFromBytes(dataAt, rotation); + dataAt += bytes; + processedBytes += bytes; + properties.setRotation(rotation); + } + + // PROP_SHOULD_BE_DELETED + if (propertyFlags.getHasProperty(PROP_SHOULD_BE_DELETED)) { + bool shouldBeDeleted; + memcpy(&shouldBeDeleted, dataAt, sizeof(shouldBeDeleted)); + dataAt += sizeof(shouldBeDeleted); + processedBytes += sizeof(shouldBeDeleted); + properties.setShouldBeDeleted(shouldBeDeleted); + } + + // PROP_SCRIPT + // script would go here... + + +#ifdef HIDE_SUBCLASS_METHODS + // PROP_COLOR + if (propertyFlags.getHasProperty(PROP_COLOR)) { + xColor color; + memcpy(&color, dataAt, sizeof(color)); + dataAt += sizeof(color); + processedBytes += sizeof(color); + properties.setColor(color); + } + + // PROP_MODEL_URL + if (propertyFlags.getHasProperty(PROP_MODEL_URL)) { + + // TODO: fix to new format... + uint16_t modelURLbytesToRead; + memcpy(&modelURLbytesToRead, dataAt, sizeof(modelURLbytesToRead)); + dataAt += sizeof(modelURLbytesToRead); + processedBytes += sizeof(modelURLbytesToRead); + QString modelURLString((const char*)dataAt); + dataAt += modelURLbytesToRead; + processedBytes += modelURLbytesToRead; + + properties.setModelURL(modelURLString); + } + + // PROP_ANIMATION_URL + if (propertyFlags.getHasProperty(PROP_ANIMATION_URL)) { + // animationURL + uint16_t animationURLbytesToRead; + memcpy(&animationURLbytesToRead, dataAt, sizeof(animationURLbytesToRead)); + dataAt += sizeof(animationURLbytesToRead); + processedBytes += sizeof(animationURLbytesToRead); + QString animationURLString((const char*)dataAt); + dataAt += animationURLbytesToRead; + processedBytes += animationURLbytesToRead; + properties.setAnimationURL(animationURLString); + } + + // PROP_ANIMATION_FPS + if (propertyFlags.getHasProperty(PROP_ANIMATION_FPS)) { + float animationFPS; + memcpy(&animationFPS, dataAt, sizeof(animationFPS)); + dataAt += sizeof(animationFPS); + processedBytes += sizeof(animationFPS); + properties.setAnimationFPS(animationFPS); + } + + // PROP_ANIMATION_FRAME_INDEX + if (propertyFlags.getHasProperty(PROP_ANIMATION_FRAME_INDEX)) { + float animationFrameIndex; // we keep this as a float and round to int only when we need the exact index + memcpy(&animationFrameIndex, dataAt, sizeof(animationFrameIndex)); + dataAt += sizeof(animationFrameIndex); + processedBytes += sizeof(animationFrameIndex); + properties.setAnimationFrameIndex(animationFrameIndex); + } + + // PROP_ANIMATION_PLAYING + if (propertyFlags.getHasProperty(PROP_ANIMATION_PLAYING)) { + bool animationIsPlaying; + memcpy(&animationIsPlaying, dataAt, sizeof(animationIsPlaying)); + dataAt += sizeof(animationIsPlaying); + processedBytes += sizeof(animationIsPlaying); + properties.setAnimationIsPlaying(animationIsPlaying); + } +#endif + + const bool wantDebugging = false; + if (wantDebugging) { + qDebug("EntityItem::fromEditPacket()..."); + qDebug() << " EntityItem id in packet:" << editID; + //newEntityItem.debugDump(); + } + + return valid; +} + + +bool registered = EntityTypes::registerEntityType(EntityTypes::Base, "Base") + && EntityTypes::registerEntityType(EntityTypes::Model, "Model"); // TODO: move this to model subclass + + diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h new file mode 100644 index 0000000000..2e9fe81f92 --- /dev/null +++ b/libraries/entities/src/EntityTypes.h @@ -0,0 +1,50 @@ +// +// EntityTypes.h +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright 2013 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_EntityTypes_h +#define hifi_EntityTypes_h + +#include + +#include +#include + +class EntityItem; +class EntityItemID; +class EntityItemProperties; + + +class EntityTypes { +public: + typedef enum EntityType { + Base, + Model, + Particle, + Box, + Sphere, + Plane, + Cylinder, + Pyramid + } EntityType_t; + + static const QString& getEntityTypeName(EntityType_t entityType); + static bool registerEntityType(EntityType_t entityType, const QString& name); + static EntityTypes::EntityType_t getEntityTypeFromName(const QString& name); + + static EntityItem* constructEntityItem(EntityType_t entityType, const EntityItemID& entityID, const EntityItemProperties& properties); + static EntityItem* constructEntityItem(const unsigned char* data, int bytesToRead); + static bool decodeEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes, + EntityItemID& entityID, EntityItemProperties& properties); +private: + static QHash _typeNameHash; +}; + +#endif // hifi_EntityTypes_h