HifiExperimentsscript entitiesefd26391 year agoHistory
362 lines · 14.3 KB · Text
RawDownload
////  Created by Bradley Austin Davis on 2016/05/09//  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 "ShapeEntityItem.h" #include <glm/gtx/transform.hpp> #include <QtCore/QDebug> #include <GeometryUtil.h> #include "EntitiesLogging.h"#include "EntityItemProperties.h"#include "EntityTree.h"#include "EntityTreeElement.h" // hullShapeCalculator is a hook for external code that knows how to configure a ShapeInfo// for given EntityShape and dimensionsShapeEntityItem::ShapeInfoCalculator hullShapeCalculator = nullptr; void ShapeEntityItem::setShapeInfoCalulator(ShapeEntityItem::ShapeInfoCalculator callback) {    hullShapeCalculator = callback;} ShapeEntityItem::Pointer ShapeEntityItem::baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {    Pointer entity(new ShapeEntityItem(entityID), [](ShapeEntityItem* ptr) { ptr->deleteLater(); });    entity->setProperties(properties);    return entity;} EntityItemPointer ShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {    return baseFactory(entityID, properties);} EntityItemPointer ShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {    auto result = baseFactory(entityID, properties);    result->setShape(EntityShape::Cube);    return result;} EntityItemPointer ShapeEntityItem::sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {    auto result = baseFactory(entityID, properties);    result->setShape(EntityShape::Sphere);    return result;} // our non-pure virtual subclass for now...ShapeEntityItem::ShapeEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) {    _type = EntityTypes::Shape;    _volumeMultiplier *= PI / 6.0f;} EntityItemProperties ShapeEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const {    EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class @Shape_ENTITY_COPY_TO@     return properties;} void ShapeEntityItem::setShape(EntityShape shape) {    switch (shape) {        case EntityShape::Cube:            _type = EntityTypes::Box;            break;        case EntityShape::Sphere:            _type = EntityTypes::Sphere;            break;        case EntityShape::Circle:            // Circle is implicitly flat so we enforce flat dimensions            setUnscaledDimensions(getUnscaledDimensions());            break;        case EntityShape::Quad:            // Quad is implicitly flat so we enforce flat dimensions            setUnscaledDimensions(getUnscaledDimensions());            break;        default:            _type = EntityTypes::Shape;            break;    }     if (shape != getShape()) {        // Internally grabs writeLock        markDirtyFlags(Simulation::DIRTY_SHAPE);        withWriteLock([&] {            _needsRenderUpdate = true;            _shape = shape;        });    }} EntityShape ShapeEntityItem::getShape() const {    return resultWithReadLock<EntityShape>([&] {        return _shape;    });} bool ShapeEntityItem::setSubClassProperties(const EntityItemProperties& properties) {    bool somethingChanged = false; @Shape_ENTITY_SET_FROM@     return somethingChanged;} EntityPropertyFlags ShapeEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {    EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); @Shape_REQUESTED_PROPS@     return requestedProperties;} void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,                                         EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,                                         EntityPropertyFlags& requestedProperties,                                         EntityPropertyFlags& propertyFlags,                                         EntityPropertyFlags& propertiesDidntFit,                                         int& propertyCount,                                         OctreeElement::AppendState& appendState) const {     bool successPropertyFits = true; @Shape_ENTITY_APPEND@ } int ShapeEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,                                                ReadBitstreamToTreeParams& args,                                                EntityPropertyFlags& propertyFlags, bool overwriteLocalData,                                                bool& somethingChanged) {     int bytesRead = 0;    const unsigned char* dataAt = data; @Shape_ENTITY_READ@     return bytesRead;} void ShapeEntityItem::setUnscaledDimensions(const glm::vec3& value) {    const float MAX_FLAT_DIMENSION = 0.0001f;    const auto shape = getShape();    if ((shape == EntityShape::Circle || shape == EntityShape::Quad) && value.y > MAX_FLAT_DIMENSION) {        // enforce flatness in Y        glm::vec3 newDimensions = value;        newDimensions.y = MAX_FLAT_DIMENSION;        EntityItem::setUnscaledDimensions(newDimensions);    } else {        EntityItem::setUnscaledDimensions(value);    }} bool ShapeEntityItem::supportsDetailedIntersection() const {    return getShape() == EntityShape::Sphere;} bool ShapeEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,                                                  const glm::vec3& viewFrustumPos, OctreeElementPointer& element,                                                  float& distance, BoxFace& face, glm::vec3& surfaceNormal,                                                  QVariantMap& extraInfo, bool precisionPicking) const {    glm::vec3 dimensions = getScaledDimensions();    BillboardMode billboardMode = getBillboardMode();    glm::quat rotation = billboardMode == BillboardMode::NONE ? getWorldOrientation() : getLocalOrientation();    glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()));    rotation = BillboardModeHelpers::getBillboardRotation(position, rotation, billboardMode, viewFrustumPos);     // determine the ray in the frame of the entity transformed from a unit sphere    glm::mat4 entityToWorldMatrix = glm::translate(position) * glm::mat4_cast(rotation) * glm::scale(dimensions);    glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);    glm::vec3 entityFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f));    glm::vec3 entityFrameDirection = glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f));     // NOTE: unit sphere has center of 0,0,0 and radius of 0.5    if (findRaySphereIntersection(entityFrameOrigin, entityFrameDirection, glm::vec3(0.0f), 0.5f, distance)) {        bool success;        glm::vec3 center = getCenterPosition(success);        if (success) {            // FIXME: this is only correct for uniformly scaled spheres            // determine where on the unit sphere the hit point occured            glm::vec3 hitAt = origin + (direction * distance);            surfaceNormal = glm::normalize(hitAt - center);        } else {            return false;        }        return true;    }    return false;} bool ShapeEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,                                                       const glm::vec3& viewFrustumPos, OctreeElementPointer& element, float& parabolicDistance,                                                       BoxFace& face, glm::vec3& surfaceNormal,                                                       QVariantMap& extraInfo, bool precisionPicking) const {    glm::vec3 dimensions = getScaledDimensions();    BillboardMode billboardMode = getBillboardMode();    glm::quat rotation = billboardMode == BillboardMode::NONE ? getWorldOrientation() : getLocalOrientation();    glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()));    rotation = BillboardModeHelpers::getBillboardRotation(position, rotation, billboardMode, viewFrustumPos);     // determine the parabola in the frame of the entity transformed from a unit sphere    glm::mat4 entityToWorldMatrix = glm::translate(position) * glm::mat4_cast(rotation) * glm::scale(dimensions);    glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);    glm::vec3 entityFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f));    glm::vec3 entityFrameVelocity = glm::vec3(worldToEntityMatrix * glm::vec4(velocity, 0.0f));    glm::vec3 entityFrameAcceleration = glm::vec3(worldToEntityMatrix * glm::vec4(acceleration, 0.0f));     // NOTE: unit sphere has center of 0,0,0 and radius of 0.5    if (findParabolaSphereIntersection(entityFrameOrigin, entityFrameVelocity, entityFrameAcceleration, glm::vec3(0.0f), 0.5f, parabolicDistance)) {        bool success;        glm::vec3 center = getCenterPosition(success);        if (success) {            // FIXME: this is only correct for uniformly scaled spheres            surfaceNormal = glm::normalize((origin + velocity * parabolicDistance + 0.5f * acceleration * parabolicDistance * parabolicDistance) - center);        } else {            return false;        }        return true;    }    return false;} bool ShapeEntityItem::getRotateForPicking() const {    auto shape = getShape();    return getBillboardMode() != BillboardMode::NONE && (shape < EntityShape::Cube || shape > EntityShape::Icosahedron);} void ShapeEntityItem::debugDump() const {    qCDebug(entities) << "ShapeEntityItem id:" << getEntityItemID() << "---------------------------------------------";    qCDebug(entities) << "    name:" << _name;    qCDebug(entities) << "    position:" << debugTreeVector(getWorldPosition());    qCDebug(entities) << "    dimensions:" << debugTreeVector(getScaledDimensions());    qCDebug(entities) << "    editedAgo:" << debugTime(getLastEdited(), usecTimestampNow());    qCDebug(entities) << "    pointer:" << this; @Shape_ENTITY_DEBUG@ } void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) {     // This will be called whenever DIRTY_SHAPE flag (set by dimension change, etc)    // is set.     const glm::vec3 entityDimensions = getScaledDimensions();    const auto shape = getShape();     switch (shape){        case EntityShape::Quad:            // Quads collide like flat Cubes        case EntityShape::Cube: {            _collisionShapeType = SHAPE_TYPE_BOX;        }        break;        case EntityShape::Sphere: {            float diameter = entityDimensions.x;            const float MIN_DIAMETER = 0.001f;            const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f;            if (diameter > MIN_DIAMETER                && fabsf(diameter - entityDimensions.y) / diameter < MIN_RELATIVE_SPHERICAL_ERROR                && fabsf(diameter - entityDimensions.z) / diameter < MIN_RELATIVE_SPHERICAL_ERROR) {                 _collisionShapeType = SHAPE_TYPE_SPHERE;            } else {                _collisionShapeType = SHAPE_TYPE_ELLIPSOID;            }        }        break;        case EntityShape::Circle:            // Circles collide like flat Cylinders        case EntityShape::Cylinder: {            float diameter = entityDimensions.x;            const float MIN_DIAMETER = 0.001f;            const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f;            if (diameter > MIN_DIAMETER                && fabsf(diameter - entityDimensions.z) / diameter < MIN_RELATIVE_SPHERICAL_ERROR) {                _collisionShapeType = SHAPE_TYPE_CYLINDER_Y;            } else if (hullShapeCalculator) {                hullShapeCalculator(this, info);                _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL;            } else {                // woops, someone forgot to hook up the hullShapeCalculator()!                // final fallback is ellipsoid                _collisionShapeType = SHAPE_TYPE_ELLIPSOID;            }        }        break;        case EntityShape::Cone: {            if (hullShapeCalculator) {                hullShapeCalculator(this, info);                _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL;            } else {                _collisionShapeType = SHAPE_TYPE_ELLIPSOID;            }        }        break;        // gons, ones, & angles built via GeometryCache::extrudePolygon        case EntityShape::Triangle:        case EntityShape::Hexagon:        case EntityShape::Octagon: {            if (hullShapeCalculator) {                hullShapeCalculator(this, info);                _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL;            } else {                _collisionShapeType = SHAPE_TYPE_ELLIPSOID;            }        }        break;        // hedrons built via GeometryCache::setUpFlatShapes        case EntityShape::Tetrahedron:        case EntityShape::Octahedron:        case EntityShape::Dodecahedron:        case EntityShape::Icosahedron: {            if ( hullShapeCalculator ) {                hullShapeCalculator(this, info);                _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL;            } else {                _collisionShapeType = SHAPE_TYPE_ELLIPSOID;            }        }        break;        case EntityShape::Torus: {            // Not in GeometryCache::buildShapes, unsupported.            _collisionShapeType = SHAPE_TYPE_CYLINDER_Y;            //TODO handle this shape more correctly        }        break;        default: {            _collisionShapeType = SHAPE_TYPE_ELLIPSOID;        }        break;    }     EntityItem::computeShapeInfo(info);} // This value specifies how the shape should be treated by physics calculations.ShapeType ShapeEntityItem::getShapeType() const {    return _collisionShapeType;} PulsePropertyGroup ShapeEntityItem::getPulseProperties() const {    return resultWithReadLock<PulsePropertyGroup>([&] {        return _pulseProperties;    });} void ShapeEntityItem::setUserData(const QString& value) {    bool changed = false;    withWriteLock([&] {        changed = _userData != value;        _needsRenderUpdate |= changed;    });    if (changed) {        EntityItem::setUserData(value);    }}