Ejemplo n.º 1
0
void LoadEqualizer::_assign( Compound* compound, const Viewport& vp,
                             const Range& range )
{
    LBASSERTINFO( vp == Viewport::FULL || range == Range::ALL,
                  "Mixed 2D/DB load-balancing not implemented" );

    compound->setViewport( vp );
    compound->setRange( range );
    LBLOG( LOG_LB2 ) << compound->getChannel()->getName() << " set " << vp
                     << ", " << range << std::endl;

    if( getDamping() >= 1.f )
        return;

    // save data for later use
    Data data;
    data.vp      = vp;
    data.range   = range;
    data.channel = compound->getChannel();
    data.taskID  = compound->getTaskID();

    const Compound* destCompound = getCompound();
    if( destCompound->getChannel() == compound->getChannel( ))
        data.destTaskID  = destCompound->getTaskID();

    LBASSERT( data.taskID > 0 );

    if( !vp.hasArea() || !range.hasData( )) // will not render
        data.time = 0;

    LBFrameData& frameData = _history.back();
    LBDatas& items = frameData.second;

    items.push_back( data );
}
Ejemplo n.º 2
0
void Force::MobilityLinearDamperImpl::
calcForce(const State& state, Vector_<SpatialVec>& bodyForces, 
          Vector_<Vec3>& particleForces, Vector& mobilityForces) const 
{
    const MobilizedBody& mb = m_matter.getMobilizedBody(m_mobodIx);
    const Real u = mb.getOneU(state, m_whichU);
    const Real damping = getDamping(state);
    const Real frc = -damping*u;
    mb.applyOneMobilityForce(state, m_whichU, frc, mobilityForces);
}
Ejemplo n.º 3
0
int64_t LoadEqualizer::_getAssembleTime( )
{
    if( getDamping() >= 1.f )
        return 0;

    const LBFrameData& frameData = _history.front();
    const LBDatas& items = frameData.second;

    int64_t assembleTime = 0;
    for( LBDatas::const_iterator i = items.begin(); i != items.end(); ++i )
    {
        const Data& data = *i;
        LBASSERT( assembleTime == 0 || data.assembleTime == 0 );
        assembleTime += data.assembleTime;
    }
    return assembleTime;
}
Ejemplo n.º 4
0
void LoadEqualizer::_computeSplit( Node* node, const float time,
                                   LBDatas* datas, const Viewport& vp,
                                   const Range& range )
{
    LBLOG( LOG_LB2 ) << "_computeSplit " << vp << ", " << range << " time "
                    << time << std::endl;
    LBASSERTINFO( vp.isValid(), vp );
    LBASSERTINFO( range.isValid(), range );
    LBASSERTINFO( node->resources > 0.f || !vp.hasArea() || !range.hasData(),
                  "Assigning " << node->resources <<
                  " work to viewport " << vp << ", " << range );

    Compound* compound = node->compound;
    if( compound )
    {
        _assign( compound, vp, range );
        return;
    }

    LBASSERT( node->left && node->right );

    LBDatas workingSet = datas[ node->mode ];
    const float leftTime = node->resources > 0 ?
                           time * node->left->resources / node->resources : 0.f;
    float timeLeft = LB_MIN( leftTime, time ); // correct for fp rounding error

    switch( node->mode )
    {
        case MODE_VERTICAL:
        {
            LBASSERT( range == Range::ALL );

            float splitPos = vp.x;
            const float end = vp.getXEnd();

            while( timeLeft > std::numeric_limits< float >::epsilon() &&
                   splitPos < end )
            {
                LBLOG( LOG_LB2 ) << timeLeft << "ms left using "
                                << workingSet.size() << " tiles" << std::endl;

                // remove all irrelevant items from working set
                for( LBDatas::iterator i = workingSet.begin();
                     i != workingSet.end(); )
                {
                    const Data& data = *i;
                    if( data.vp.getXEnd() > splitPos )
                        ++i;
                    else
                        i = workingSet.erase( i );
                }
                if( workingSet.empty( ))
                    break;

                // find next 'discontinouity' in loads
                float currentPos = 1.0f;
                for( LBDatas::const_iterator i = workingSet.begin();
                     i != workingSet.end(); ++i )
                {
                    const Data& data = *i;
                    if( data.vp.x > splitPos && data.vp.x < currentPos )
                        currentPos = data.vp.x;
                    const float xEnd = data.vp.getXEnd();
                    if( xEnd > splitPos && xEnd < currentPos )
                        currentPos = xEnd;
                }

                const float width = currentPos - splitPos;
                LBASSERTINFO( width > 0.f, currentPos << "<=" << splitPos );
                LBASSERT( currentPos <= 1.0f );

                // accumulate normalized load in splitPos...currentPos
                LBLOG( LOG_LB2 ) << "Computing load in X " << splitPos << "..."
                                 << currentPos << std::endl;
                float currentTime = 0.f;
                for( LBDatas::const_iterator i = workingSet.begin();
                     i != workingSet.end(); ++i )
                {
                    const Data& data = *i;

                    if( data.vp.x >= currentPos ) // not yet needed data sets
                        break;

                    float yContrib = data.vp.h;
                    if( data.vp.y < vp.y )
                        yContrib -= (vp.y - data.vp.y);

                    const float dataEnd = data.vp.getYEnd();
                    const float vpEnd   = vp.getYEnd();
                    if( dataEnd > vpEnd )
                        yContrib -= (dataEnd - vpEnd);

                    if( yContrib > 0.f )
                    {
                        const float percentage = ( width / data.vp.w ) *
                                                 ( yContrib / data.vp.h );
                        currentTime += ( data.time * percentage );

                        LBLOG( LOG_LB2 ) << data.vp << " contributes "
                                         << yContrib << " in " << vp.h << " ("
                                         << percentage << ") with " << data.time
                                         << ": " << ( data.time * percentage )
                                         << " vp.y " << vp.y << " dataEnd "
                                         << dataEnd << " vpEnd " << vpEnd
                                         << std::endl;
                        LBASSERT( percentage < 1.01f )
                    }
                }

                LBLOG( LOG_LB2 ) << splitPos << "..." << currentPos << ": t="
                                 << currentTime << " of " << timeLeft
                                 << std::endl;

                if( currentTime >= timeLeft ) // found last region
                {
                    splitPos += ( width * timeLeft / currentTime );
                    timeLeft = 0.0f;
                }
                else
                {
                    timeLeft -= currentTime;
                    splitPos  = currentPos;
                }
            }

            LBLOG( LOG_LB2 ) << "Should split at X " << splitPos << std::endl;
            if( getDamping() < 1.f )
                splitPos = (1.f - getDamping()) * splitPos +
                            getDamping() * node->split;
            LBLOG( LOG_LB2 ) << "Dampened split at X " << splitPos << std::endl;

            // There might be more time left due to MIN_PIXEL rounding by parent
            // LBASSERTINFO( timeLeft <= .001f, timeLeft );

            // Ensure minimum size
            const Compound* root = getCompound();
            const float pvpW = static_cast< float >(
                root->getInheritPixelViewport().w );
            const float boundary = static_cast< float >( node->boundary2i.x()) /
                                       pvpW;
            if( node->left->resources == 0.f )
                splitPos = vp.x;
            else if( node->right->resources == 0.f )
                splitPos = end;
            else if( boundary > 0 )
            {
                const float lengthRight = vp.getXEnd() - splitPos;
                const float lengthLeft = splitPos - vp.x;
                const float maxRight =
                    static_cast< float >( node->right->maxSize.x( )) / pvpW;
                const float maxLeft =
                    static_cast< float >( node->left->maxSize.x( )) / pvpW;
                if( lengthRight > maxRight )
                    splitPos = end - maxRight;
                else if( lengthLeft > maxLeft )
                    splitPos = vp.x + maxLeft;

                if( (splitPos - vp.x) < boundary )
                    splitPos = vp.x + boundary;
                if( (end - splitPos) < boundary )
                    splitPos = end - boundary;

                const uint32_t ratio =
                           static_cast< uint32_t >( splitPos / boundary + .5f );
                splitPos = ratio * boundary;
            }

            splitPos = LB_MAX( splitPos, vp.x );
            splitPos = LB_MIN( splitPos, end);

            const float newPixelW = pvpW * splitPos;
            const float oldPixelW = pvpW * node->split;
            if( int( fabs(newPixelW - oldPixelW) ) < node->resistance2i.x( ))
                splitPos = node->split;
            else
                node->split = splitPos;

            LBLOG( LOG_LB2 ) << "Constrained split " << vp << " at X "
                             << splitPos << std::endl;

            // balance children
            Viewport childVP = vp;
            childVP.w = (splitPos - vp.x);
            _computeSplit( node->left, leftTime, datas, childVP, range );

            childVP.x = childVP.getXEnd();
            childVP.w = end - childVP.x;
            // Fix 2994111: Rounding errors with 2D LB and 16 sources
            //   Floating point rounding may create a width for the 'right'
            //   child which is slightly below the parent width. Correct it.
            while( childVP.getXEnd() < end )
                childVP.w += std::numeric_limits< float >::epsilon();
            _computeSplit( node->right, time-leftTime, datas, childVP, range );
            break;
        }

        case MODE_HORIZONTAL:
        {
            LBASSERT( range == Range::ALL );
            float splitPos = vp.y;
            const float end = vp.getYEnd();

            while( timeLeft > std::numeric_limits< float >::epsilon() &&
                   splitPos < end )
            {
                LBLOG( LOG_LB2 ) << timeLeft << "ms left using "
                                 << workingSet.size() << " tiles" << std::endl;

                // remove all unrelevant items from working set
                for( LBDatas::iterator i = workingSet.begin();
                     i != workingSet.end(); )
                {
                    const Data& data = *i;
                    if( data.vp.getYEnd() > splitPos )
                        ++i;
                    else
                        i = workingSet.erase( i );
                }
                if( workingSet.empty( ))
                    break;

                // find next 'discontinuouity' in loads
                float currentPos = 1.0f;
                for( LBDatas::const_iterator i = workingSet.begin();
                     i != workingSet.end(); ++i )
                {
                    const Data& data = *i;
                    if( data.vp.y > splitPos && data.vp.y < currentPos )
                        currentPos = data.vp.y;
                    const float yEnd = data.vp.getYEnd();
                    if( yEnd > splitPos && yEnd < currentPos )
                        currentPos = yEnd;
                }

                const float height = currentPos - splitPos;
                LBASSERTINFO( height > 0.f, currentPos << "<=" << splitPos );
                LBASSERT( currentPos <= 1.0f );

                // accumulate normalized load in splitPos...currentPos
                LBLOG( LOG_LB2 ) << "Computing load in Y " << splitPos << "..."
                                << currentPos << std::endl;
                float currentTime = 0.f;
                for( LBDatas::const_iterator i = workingSet.begin();
                     i != workingSet.end(); ++i )
                {
                    const Data& data = *i;

                    if( data.vp.y >= currentPos ) // not yet needed data sets
                        break;

                    float xContrib = data.vp.w;

                    if( data.vp.x < vp.x )
                        xContrib -= (vp.x - data.vp.x);

                    const float dataEnd = data.vp.getXEnd();
                    const float vpEnd   = vp.getXEnd();
                    if( dataEnd > vpEnd )
                        xContrib -= (dataEnd - vpEnd);

                    if( xContrib > 0.f )
                    {
                        const float percentage = ( height / data.vp.h ) *
                                                 ( xContrib / data.vp.w );
                        currentTime += ( data.time * percentage );

                        LBLOG( LOG_LB2 ) << data.vp << " contributes "
                                         << xContrib << " in " << vp.w << " ("
                                         << percentage << ") with " << data.time
                                         << ": " << ( data.time * percentage )
                                         << " total " << currentTime << " vp.x "
                                         << vp.x << " dataEnd " << dataEnd
                                         << " vpEnd " << vpEnd << std::endl;
                        LBASSERT( percentage < 1.01f )
                    }
                }

                LBLOG( LOG_LB2 ) << splitPos << "..." << currentPos << ": t="
                                 << currentTime << " of " << timeLeft
                                 << std::endl;

                if( currentTime >= timeLeft ) // found last region
                {
                    splitPos += (height * timeLeft / currentTime );
                    timeLeft = 0.0f;
                }
                else
                {
                    timeLeft -= currentTime;
                    splitPos  = currentPos;
                }
            }

            LBLOG( LOG_LB2 ) << "Should split at Y " << splitPos << std::endl;
            if( getDamping() < 1.f )
                splitPos = (1.f - getDamping( )) * splitPos +
                            getDamping() * node->split;
            LBLOG( LOG_LB2 ) << "Dampened split at Y " << splitPos << std::endl;

            const Compound* root = getCompound();

            const float pvpH = static_cast< float >(
                root->getInheritPixelViewport().h );
            const float boundary = static_cast< float >(node->boundary2i.y( )) /
                                       pvpH;

            if( node->left->resources == 0.f )
                splitPos = vp.y;
            else if( node->right->resources == 0.f )
                splitPos = end;
            else if ( boundary > 0 )
            {
                const float lengthRight = vp.getYEnd() - splitPos;
                const float lengthLeft = splitPos - vp.y;
                const float maxRight =
                    static_cast< float >( node->right->maxSize.y( )) / pvpH;
                const float maxLeft =
                    static_cast< float >( node->left->maxSize.y( )) / pvpH;
                if( lengthRight > maxRight )
                    splitPos = end - maxRight;
                else if( lengthLeft > maxLeft )
                    splitPos = vp.y + maxLeft;

                if( (splitPos - vp.y) < boundary )
                    splitPos = vp.y + boundary;
                if( (end - splitPos) < boundary )
                    splitPos = end - boundary;

                const uint32_t ratio =
                           static_cast< uint32_t >( splitPos / boundary + .5f );
                splitPos = ratio * boundary;
            }

            splitPos = LB_MAX( splitPos, vp.y );
            splitPos = LB_MIN( splitPos, end );

            const float newPixelH = pvpH * splitPos;
            const float oldPixelH = pvpH * node->split;
            if( int( fabs(newPixelH - oldPixelH) ) < node->resistance2i.y( ))
                splitPos = node->split;
            else
                node->split = splitPos;

            LBLOG( LOG_LB2 ) << "Constrained split " << vp << " at Y "
                             << splitPos << std::endl;

            Viewport childVP = vp;
            childVP.h = (splitPos - vp.y);
            _computeSplit( node->left, leftTime, datas, childVP, range );

            childVP.y = childVP.getYEnd();
            childVP.h = end - childVP.y;
            while( childVP.getYEnd() < end )
                childVP.h += std::numeric_limits< float >::epsilon();
            _computeSplit( node->right, time - leftTime, datas, childVP, range);
            break;
        }

        case MODE_DB:
        {
            LBASSERT( vp == Viewport::FULL );
            float splitPos = range.start;
            const float end = range.end;

            while( timeLeft > std::numeric_limits< float >::epsilon() &&
                   splitPos < end )
            {
                LBLOG( LOG_LB2 ) << timeLeft << "ms left using "
                                 << workingSet.size() << " tiles" << std::endl;

                // remove all irrelevant items from working set
                for( LBDatas::iterator i = workingSet.begin();
                     i != workingSet.end(); )
                {
                    const Data& data = *i;
                    if( data.range.end > splitPos )
                        ++i;
                    else
                        i = workingSet.erase( i );
                }
                if( workingSet.empty( ))
                    break;

                // find next 'discontinouity' in loads
                float currentPos = 1.0f;
                for( LBDatas::const_iterator i = workingSet.begin();
                     i != workingSet.end(); ++i )
                {
                    const Data& data = *i;
                    currentPos = LB_MIN( currentPos, data.range.end );
                }

                const float size = currentPos - splitPos;
                LBASSERTINFO( size > 0.f, currentPos << "<=" << splitPos );
                LBASSERT( currentPos <= 1.0f );

                // accumulate normalized load in splitPos...currentPos
                LBLOG( LOG_LB2 ) << "Computing load in range " << splitPos
                                << "..." << currentPos << std::endl;
                float currentTime = 0.f;
                for( LBDatas::const_iterator i = workingSet.begin();
                     i != workingSet.end(); ++i )
                {
                    const Data& data = *i;

                    if( data.range.start >= currentPos ) // not yet needed data
                        break;
#if 0
                    // make sure we cover full area
                    LBASSERTINFO(  data.range.start <= splitPos,
                                   data.range.start << " > " << splitPos );
                    LBASSERTINFO( data.range.end >= currentPos,
                                  data.range.end << " < " << currentPos);
#endif
                    currentTime += data.time * size / data.range.getSize();
                }

                LBLOG( LOG_LB2 ) << splitPos << "..." << currentPos << ": t="
                                 << currentTime << " of " << timeLeft
                                 << std::endl;

                if( currentTime >= timeLeft ) // found last region
                {
                    const float width = currentPos - splitPos;
                    splitPos += (width * timeLeft / currentTime );
                    timeLeft = 0.0f;
                }
                else
                {
                    timeLeft -= currentTime;
                    splitPos  = currentPos;
                }
            }
            LBLOG( LOG_LB2 ) << "Should split at " << splitPos << std::endl;
            if( getDamping() < 1.f )
                splitPos = (1.f - getDamping( )) * splitPos +
                            getDamping() * node->split;
            LBLOG( LOG_LB2 ) << "Dampened split at " << splitPos << std::endl;

            const float boundary( node->boundaryf );
            if( node->left->resources == 0.f )
                splitPos = range.start;
            else if( node->right->resources == 0.f )
                splitPos = end;

            const uint32_t ratio = static_cast< uint32_t >
                      ( splitPos / boundary + .5f );
            splitPos = ratio * boundary;
            if( (splitPos - range.start) < boundary )
                splitPos = range.start;
            if( (end - splitPos) < boundary )
                splitPos = end;

            if( fabs( splitPos - node->split ) < node->resistancef )
                splitPos = node->split;
            else
                node->split = splitPos;

            LBLOG( LOG_LB2 ) << "Constrained split " << range << " at pos "
                             << splitPos << std::endl;

            Range childRange = range;
            childRange.end = splitPos;
            _computeSplit( node->left, leftTime, datas, vp, childRange );

            childRange.start = childRange.end;
            childRange.end   = range.end;
            _computeSplit( node->right, time - leftTime, datas, vp, childRange);
            break;
        }

        default:
            LBUNIMPLEMENTED;
    }
}
//--------------------------------------------------------------
void ofxBulletBaseShape::setAngularDamping( float a_angular_damp ) {
	setDamping( getDamping(), a_angular_damp );
}
Ejemplo n.º 6
0
void EntityItem::update(const quint64& updateTime) {
    bool wantDebug = false;
    
    if (_lastUpdated == 0) {
        _lastUpdated = updateTime;
    }

    float timeElapsed = (float)(updateTime - _lastUpdated) / (float)(USECS_PER_SECOND);

    if (wantDebug) {
        qDebug() << "********** EntityItem::update()";
        qDebug() << "    entity ID=" << getEntityItemID();
        qDebug() << "    updateTime=" << updateTime;
        qDebug() << "    _lastUpdated=" << _lastUpdated;
        qDebug() << "    timeElapsed=" << timeElapsed;
        qDebug() << "    hasVelocity=" << hasVelocity();
        qDebug() << "    hasGravity=" << hasGravity();
        qDebug() << "    isRestingOnSurface=" << isRestingOnSurface();
        qDebug() << "    hasAngularVelocity=" << hasAngularVelocity();
        qDebug() << "    getAngularVelocity=" << getAngularVelocity();
        qDebug() << "    isMortal=" << isMortal();
        qDebug() << "    getAge()=" << getAge();
        qDebug() << "    getLifetime()=" << getLifetime();


        if (hasVelocity() || (hasGravity() && !isRestingOnSurface())) {
            qDebug() << "    MOVING...=";
            qDebug() << "        hasVelocity=" << hasVelocity();
            qDebug() << "        hasGravity=" << hasGravity();
            qDebug() << "        isRestingOnSurface=" << isRestingOnSurface();
            qDebug() << "        hasAngularVelocity=" << hasAngularVelocity();
            qDebug() << "        getAngularVelocity=" << getAngularVelocity();
        }
        if (hasAngularVelocity()) {
            qDebug() << "    CHANGING...=";
            qDebug() << "        hasAngularVelocity=" << hasAngularVelocity();
            qDebug() << "        getAngularVelocity=" << getAngularVelocity();
        }
        if (isMortal()) {
            qDebug() << "    MORTAL...=";
            qDebug() << "        isMortal=" << isMortal();
            qDebug() << "        getAge()=" << getAge();
            qDebug() << "        getLifetime()=" << getLifetime();
        }
    }

    _lastUpdated = updateTime;

    if (wantDebug) {
        qDebug() << "     ********** EntityItem::update() .... SETTING _lastUpdated=" << _lastUpdated;
    }

    if (hasAngularVelocity()) {
        glm::quat rotation = getRotation();
        glm::vec3 angularVelocity = glm::radians(getAngularVelocity());
        float angularSpeed = glm::length(angularVelocity);
        
        if (angularSpeed < EPSILON_VELOCITY_LENGTH) {
            setAngularVelocity(NO_ANGULAR_VELOCITY);
        } else {
            float angle = timeElapsed * angularSpeed;
            glm::quat  dQ = glm::angleAxis(angle, glm::normalize(angularVelocity));
            rotation = dQ * rotation;
            setRotation(rotation);

            // handle damping for angular velocity
            if (getAngularDamping() > 0.0f) {
                glm::vec3 dampingResistance = getAngularVelocity() * getAngularDamping();
                glm::vec3 newAngularVelocity = getAngularVelocity() - (dampingResistance * timeElapsed);
                setAngularVelocity(newAngularVelocity);
                if (wantDebug) {        
                    qDebug() << "    getDamping():" << getDamping();
                    qDebug() << "    dampingResistance:" << dampingResistance;
                    qDebug() << "    newAngularVelocity:" << newAngularVelocity;
                }
            }
        }
    }

    if (hasVelocity() || hasGravity()) {
        glm::vec3 position = getPosition();
        glm::vec3 velocity = getVelocity();
        glm::vec3 newPosition = position + (velocity * timeElapsed);

        if (wantDebug) {        
            qDebug() << "  EntityItem::update()....";
            qDebug() << "    timeElapsed:" << timeElapsed;
            qDebug() << "    old AACube:" << getMaximumAACube();
            qDebug() << "    old position:" << position;
            qDebug() << "    old velocity:" << velocity;
            qDebug() << "    old getAABox:" << getAABox();
            qDebug() << "    getDistanceToBottomOfEntity():" << getDistanceToBottomOfEntity() * (float)TREE_SCALE << " in meters";
            qDebug() << "    newPosition:" << newPosition;
            qDebug() << "    glm::distance(newPosition, position):" << glm::distance(newPosition, position);
        }
        
        position = newPosition;

        // handle bounces off the ground... We bounce at the distance to the bottom of our entity
        if (position.y <= getDistanceToBottomOfEntity()) {
            velocity = velocity * glm::vec3(1,-1,1);

            // if we've slowed considerably, then just stop moving
            if (glm::length(velocity) <= EPSILON_VELOCITY_LENGTH) {
                velocity = NO_VELOCITY;
            }
            
            position.y = getDistanceToBottomOfEntity();
        }

        // handle gravity....
        if (hasGravity() && !isRestingOnSurface()) {
            velocity += getGravity() * timeElapsed;
        }

        // handle resting on surface case, this is definitely a bit of a hack, and it only works on the
        // "ground" plane of the domain, but for now it
        if (hasGravity() && isRestingOnSurface()) {
            velocity.y = 0.0f;
            position.y = getDistanceToBottomOfEntity();
        }

        // handle damping for velocity
        glm::vec3 dampingResistance = velocity * getDamping();
        if (wantDebug) {        
            qDebug() << "    getDamping():" << getDamping();
            qDebug() << "    dampingResistance:" << dampingResistance;
            qDebug() << "    dampingResistance * timeElapsed:" << dampingResistance * timeElapsed;
        }
        velocity -= dampingResistance * timeElapsed;

        if (wantDebug) {        
            qDebug() << "    velocity AFTER dampingResistance:" << velocity;
            qDebug() << "    glm::length(velocity):" << glm::length(velocity);
            qDebug() << "    EPSILON_VELOCITY_LENGTH:" << EPSILON_VELOCITY_LENGTH;
        }
        
        // round velocity to zero if it's close enough...
        if (glm::length(velocity) <= EPSILON_VELOCITY_LENGTH) {
            velocity = NO_VELOCITY;
        }

        setPosition(position); // this will automatically recalculate our collision shape
        setVelocity(velocity);
        
        if (wantDebug) {        
            qDebug() << "    new position:" << position;
            qDebug() << "    new velocity:" << velocity;
            qDebug() << "    new AACube:" << getMaximumAACube();
            qDebug() << "    old getAABox:" << getAABox();
        }
    }
}
Ejemplo n.º 7
0
OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packetData, EncodeBitstreamParams& params, 
                                            EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData) const {
                                            
    // ALL this fits...
    //    object ID [16 bytes]
    //    ByteCountCoded(type code) [~1 byte]
    //    last edited [8 bytes]
    //    ByteCountCoded(last_edited to last_updated delta) [~1-8 bytes]
    //    PropertyFlags<>( everything ) [1-2 bytes]
    // ~27-35 bytes...
    
    OctreeElement::AppendState appendState = OctreeElement::COMPLETED; // assume the best

    // encode our ID as a byte count coded byte stream
    QByteArray encodedID = getID().toRfc4122();

    // encode our type as a byte count coded byte stream
    ByteCountCoded<quint32> typeCoder = getType();
    QByteArray encodedType = typeCoder;

    quint64 updateDelta = getLastUpdated() <= getLastEdited() ? 0 : getLastUpdated() - getLastEdited();
    ByteCountCoded<quint64> updateDeltaCoder = updateDelta;
    QByteArray encodedUpdateDelta = updateDeltaCoder;
    EntityPropertyFlags propertyFlags(PROP_LAST_ITEM);
    EntityPropertyFlags requestedProperties = getEntityProperties(params);
    EntityPropertyFlags propertiesDidntFit = requestedProperties;

    // If we are being called for a subsequent pass at appendEntityData() that failed to completely encode this item,
    // then our entityTreeElementExtraEncodeData should include data about which properties we need to append.
    if (entityTreeElementExtraEncodeData && entityTreeElementExtraEncodeData->entities.contains(getEntityItemID())) {
        requestedProperties = entityTreeElementExtraEncodeData->entities.value(getEntityItemID());
    }

    LevelDetails entityLevel = packetData->startLevel();

    quint64 lastEdited = getLastEdited();

    const bool wantDebug = false;
    if (wantDebug) {
        float editedAgo = getEditedAgo();
        QString agoAsString = formatSecondsElapsed(editedAgo);
        qDebug() << "Writing entity " << getEntityItemID() << " to buffer, lastEdited =" << lastEdited 
                        << " ago=" << editedAgo << "seconds - " << agoAsString;
    }

    bool successIDFits = false;
    bool successTypeFits = false;
    bool successCreatedFits = false;
    bool successLastEditedFits = false;
    bool successLastUpdatedFits = false;
    bool successPropertyFlagsFits = false;
    int propertyFlagsOffset = 0;
    int oldPropertyFlagsLength = 0;
    QByteArray encodedPropertyFlags;
    int propertyCount = 0;

    successIDFits = packetData->appendValue(encodedID);
    if (successIDFits) {
        successTypeFits = packetData->appendValue(encodedType);
    }
    if (successTypeFits) {
        successCreatedFits = packetData->appendValue(_created);
    }
    if (successCreatedFits) {
        successLastEditedFits = packetData->appendValue(lastEdited);
    }
    if (successLastEditedFits) {
        successLastUpdatedFits = packetData->appendValue(encodedUpdateDelta);
    }
    
    if (successLastUpdatedFits) {
        propertyFlagsOffset = packetData->getUncompressedByteOffset();
        encodedPropertyFlags = propertyFlags;
        oldPropertyFlagsLength = encodedPropertyFlags.length();
        successPropertyFlagsFits = packetData->appendValue(encodedPropertyFlags);
    }

    bool headerFits = successIDFits && successTypeFits && successCreatedFits && successLastEditedFits 
                              && successLastUpdatedFits && successPropertyFlagsFits;

    int startOfEntityItemData = packetData->getUncompressedByteOffset();

    if (headerFits) {
        bool successPropertyFits;

        propertyFlags -= PROP_LAST_ITEM; // clear the last item for now, we may or may not set it as the actual item

        // These items would go here once supported....
        //      PROP_PAGED_PROPERTY,
        //      PROP_CUSTOM_PROPERTIES_INCLUDED,

        APPEND_ENTITY_PROPERTY(PROP_POSITION, appendPosition, getPosition());
        APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, appendValue, getDimensions()); // NOTE: PROP_RADIUS obsolete

        if (wantDebug) {
            qDebug() << "    APPEND_ENTITY_PROPERTY() PROP_DIMENSIONS:" << getDimensions();
        }

        APPEND_ENTITY_PROPERTY(PROP_ROTATION, appendValue, getRotation());
        APPEND_ENTITY_PROPERTY(PROP_MASS, appendValue, getMass());
        APPEND_ENTITY_PROPERTY(PROP_VELOCITY, appendValue, getVelocity());
        APPEND_ENTITY_PROPERTY(PROP_GRAVITY, appendValue, getGravity());
        APPEND_ENTITY_PROPERTY(PROP_DAMPING, appendValue, getDamping());
        APPEND_ENTITY_PROPERTY(PROP_LIFETIME, appendValue, getLifetime());
        APPEND_ENTITY_PROPERTY(PROP_SCRIPT, appendValue, getScript());
        APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, appendValue, getRegistrationPoint());
        APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, appendValue, getAngularVelocity());
        APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, appendValue, getAngularDamping());
        APPEND_ENTITY_PROPERTY(PROP_VISIBLE, appendValue, getVisible());
        APPEND_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, appendValue, getIgnoreForCollisions());
        APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, appendValue, getCollisionsWillMove());

        appendSubclassData(packetData, params, entityTreeElementExtraEncodeData,
                                requestedProperties,
                                propertyFlags,
                                propertiesDidntFit,
                                propertyCount,
                                appendState);
    }

    if (propertyCount > 0) {
        int endOfEntityItemData = packetData->getUncompressedByteOffset();
        encodedPropertyFlags = propertyFlags;
        int newPropertyFlagsLength = encodedPropertyFlags.length();
        packetData->updatePriorBytes(propertyFlagsOffset, 
                (const unsigned char*)encodedPropertyFlags.constData(), encodedPropertyFlags.length());
        
        // if the size of the PropertyFlags shrunk, we need to shift everything down to front of packet.
        if (newPropertyFlagsLength < oldPropertyFlagsLength) {
            int oldSize = packetData->getUncompressedSize();
            const unsigned char* modelItemData = packetData->getUncompressedData(propertyFlagsOffset + oldPropertyFlagsLength);
            int modelItemDataLength = endOfEntityItemData - startOfEntityItemData;
            int newEntityItemDataStart = propertyFlagsOffset + newPropertyFlagsLength;
            packetData->updatePriorBytes(newEntityItemDataStart, modelItemData, modelItemDataLength);
            int newSize = oldSize - (oldPropertyFlagsLength - newPropertyFlagsLength);
            packetData->setUncompressedSize(newSize);

        } else {
            assert(newPropertyFlagsLength == oldPropertyFlagsLength); // should not have grown
        }
       
        packetData->endLevel(entityLevel);
    } else {
        packetData->discardLevel(entityLevel);
        appendState = OctreeElement::NONE; // if we got here, then we didn't include the item
    }
    
    // If any part of the model items didn't fit, then the element is considered partial
    if (appendState != OctreeElement::COMPLETED) {
        // add this item into our list for the next appendElementData() pass
        entityTreeElementExtraEncodeData->entities.insert(getEntityItemID(), propertiesDidntFit);
    }

    return appendState;
}