Ticket #1988: anchor.diff

File anchor.diff, 7.6 KB (added by sanderd17, 11 years ago)
  • binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry.xml

     
    4242  <Obstruction>
    4343    <Unit radius="1.0"/>
    4444  </Obstruction>
     45  <Position>
     46    <Anchor>pitch</Anchor>
     47  </Position>
    4548  <Selectable>
    4649    <Overlay>
    4750      <Texture>
  • binaries/data/mods/public/simulation/templates/template_unit_champion_elephant.xml

     
    3131  <Obstruction>
    3232    <Unit radius="4.0"/>
    3333  </Obstruction>
     34  <Position>
     35    <Anchor>pitch</Anchor>
     36  </Position>
    3437  <Selectable>
    3538    <Overlay>
    3639      <Texture>
  • binaries/data/mods/public/simulation/templates/units/maur_support_elephant.xml

     
    3838    <stone>10</stone>
    3939    <metal>10</metal>
    4040  </Loot>
     41  <Position>
     42    <Anchor>pitch</Anchor>
     43  </Position>
    4144  <ResourceDropsite>
    4245    <Types>food wood stone metal</Types>
    4346  </ResourceDropsite>
  • source/simulation2/components/CCmpPosition.cpp

     
    3030#include "maths/MathUtil.h"
    3131#include "maths/Matrix3D.h"
    3232#include "maths/Vector3D.h"
     33#include "maths/Vector2D.h"
    3334#include "ps/CLogger.h"
    3435
    3536/**
     
    5859        UPRIGHT = 0,
    5960        PITCH = 1,
    6061        PITCH_ROLL = 2,
     62        ROLL=3,
    6163    } m_AnchorType;
    6264
    6365    bool m_Floating;
     
    7274    entity_pos_t m_YOffset;
    7375    bool m_RelativeToGround; // whether m_YOffset is relative to terrain/water plane, or an absolute height
    7476
    75     entity_angle_t m_RotX, m_RotY, m_RotZ;
     77    CTerrain* m_Terrain;
     78
     79    entity_angle_t m_RotX, m_RotY, m_RotZ, m_LastRotX, m_LastRotZ;
    7680    float m_InterpolatedRotY, m_PrevInterpolatedRotY; // not serialized
     81    bool recalculateXZRotation;
    7782
     83    int widthInTiles, depthInTiles;
     84
    7885    static std::string GetSchema()
    7986    {
    8087        return
     
    8996                "<choice>"
    9097                    "<value a:help='Always stand straight up'>upright</value>"
    9198                    "<value a:help='Rotate backwards and forwards to follow the terrain'>pitch</value>"
     99                    "<value a:help='Rotate sideways follow the terrain'>roll</value>"
    92100                    "<value a:help='Rotate in all direction to follow the terrain'>pitch-roll</value>"
    93101                "</choice>"
    94102            "</element>"
     
    110118            m_AnchorType = PITCH;
    111119        else if (anchor == L"pitch-roll")
    112120            m_AnchorType = PITCH_ROLL;
     121        else if (anchor == L"roll")
     122            m_AnchorType = ROLL;
    113123        else
    114124            m_AnchorType = UPRIGHT;
    115125
     
    123133
    124134        m_RotX = m_RotY = m_RotZ = entity_angle_t::FromInt(0);
    125135        m_InterpolatedRotY = m_PrevInterpolatedRotY = 0;
     136
     137        m_LastRotX = entity_angle_t::Zero();
     138        m_LastRotZ = entity_angle_t::Zero();
     139
     140        m_Terrain = &GetSimContext().GetTerrain();
     141
     142        recalculateXZRotation = true;
    126143    }
    127144
    128145    virtual void Deinit()
     
    197214
    198215    virtual void MoveTo(entity_pos_t x, entity_pos_t z)
    199216    {
     217       
    200218        m_X = x;
    201219        m_Z = z;
    202220
     
    207225            m_LastZ = m_PrevZ = m_Z;
    208226        }
    209227
     228        recalculateXZRotation = true;
     229
    210230        AdvertisePositionChanges();
    211231    }
    212232
     
    216236        m_LastZ = m_PrevZ = m_Z = z;
    217237        m_InWorld = true;
    218238
     239        recalculateXZRotation = true;
     240
    219241        AdvertisePositionChanges();
    220242    }
    221243
     
    319341
    320342    virtual void TurnTo(entity_angle_t y)
    321343    {
     344
    322345        m_RotY = y;
    323346
    324347        AdvertisePositionChanges();
     
    330353        m_InterpolatedRotY = m_RotY.ToFloat();
    331354        m_PrevInterpolatedRotY = m_InterpolatedRotY;
    332355
     356        recalculateXZRotation = true;
     357
    333358        AdvertisePositionChanges();
    334359    }
    335360
     
    371396        rotY = m_InterpolatedRotY;
    372397    }
    373398
     399
     400    virtual void UpdateXZRotation()
     401    {
     402
     403        if (m_AnchorType == UPRIGHT)
     404            // change nothing if anchor is upright
     405            return;
     406       
     407        // TODO average normal (average all the tiles?) for big units or for buildings
     408        CFixedVector3D normal;
     409
     410        m_Terrain->CalcNormalFixed((m_X / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), (m_Z / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), normal);
     411       
     412        // rotate the normal so the positive x direction is in the direction of the unit
     413        CFixedVector2D projected = CFixedVector2D(normal.X, normal.Z);
     414        projected = projected.Rotate(-entity_pos_t::FromFloat(m_InterpolatedRotY));
     415
     416        normal.X = projected.X;
     417        normal.Z = projected.Y;
     418
     419        if (m_AnchorType == PITCH || m_AnchorType == PITCH_ROLL)
     420            // project and calculate the angle
     421            m_RotX = -atan2_approx(normal.Z, normal.Y);
     422
     423        if (m_AnchorType == ROLL || m_AnchorType == PITCH_ROLL)
     424            // project and calculate the angle
     425            m_RotZ = atan2_approx(normal.X,normal.Y);
     426       
     427        return;
     428    }
     429
    374430    virtual CMatrix3D GetInterpolatedTransform(float frameOffset, bool forceFloating)
    375431    {
     432
     433
    376434        if (!m_InWorld)
    377435        {
    378436            LOGERROR(L"CCmpPosition::GetInterpolatedTransform called on entity when IsInWorld is false");
     
    401459
    402460        float y = baseY + m_YOffset.ToFloat();
    403461
    404         // TODO: do something with m_AnchorType
    405462
    406         CMatrix3D m;
    407         m.SetXRotation(m_RotX.ToFloat());
    408         m.RotateZ(m_RotZ.ToFloat());
     463        CMatrix3D m;
     464       
     465        // linear interpolation is good enough (for RotX/Z).
     466        // As you always stay close to zero angle. 
     467        m.SetXRotation(Interpolate(m_LastRotX.ToFloat(), m_RotX.ToFloat(), frameOffset));
     468        m.RotateZ(Interpolate(m_LastRotZ.ToFloat(), m_RotZ.ToFloat(), frameOffset));
     469   
    409470        m.RotateY(rotY + (float)M_PI);
    410471        m.Translate(CVector3D(x, y, z));
    411472       
     
    418479        {
    419480        case MT_Interpolate:
    420481        {
     482
    421483            const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg);
    422484
    423485            float rotY = m_RotY.ToFloat();
    424             float delta = rotY - m_InterpolatedRotY;
    425             // Wrap delta to -M_PI..M_PI
    426             delta = fmodf(delta + (float)M_PI, 2*(float)M_PI); // range -2PI..2PI
    427             if (delta < 0) delta += 2*(float)M_PI; // range 0..2PI
    428             delta -= (float)M_PI; // range -M_PI..M_PI
    429             // Clamp to max rate
    430             float deltaClamped = clamp(delta, -m_RotYSpeed*msgData.deltaSimTime, +m_RotYSpeed*msgData.deltaSimTime);
    431             // Calculate new orientation, in a peculiar way in order to make sure the
    432             // result gets close to m_orientation (rather than being n*2*M_PI out)
    433             m_InterpolatedRotY = rotY + deltaClamped - delta;
     486            if (rotY != m_InterpolatedRotY)
     487            {
     488                float delta = rotY - m_InterpolatedRotY;
     489                // Wrap delta to -M_PI..M_PI
     490                delta = fmodf(delta + (float)M_PI, 2*(float)M_PI); // range -2PI..2PI
     491                if (delta < 0) delta += 2*(float)M_PI; // range 0..2PI
     492                delta -= (float)M_PI; // range -M_PI..M_PI
     493                // Clamp to max rate
     494                float deltaClamped = clamp(delta, -m_RotYSpeed*msgData.deltaSimTime, +m_RotYSpeed*msgData.deltaSimTime);
     495                // Calculate new orientation, in a peculiar way in order to make sure the
     496                // result gets close to m_orientation (rather than being n*2*M_PI out)
     497                m_InterpolatedRotY = rotY + deltaClamped - delta;
     498                recalculateXZRotation = true;
     499            }
    434500
     501            if (recalculateXZRotation)
     502            {
     503                m_LastRotX = m_RotX;
     504                m_LastRotZ = m_RotZ;
     505
     506                UpdateXZRotation();
     507
     508                recalculateXZRotation = false;
     509
     510            }
     511
    435512            break;
    436513        }
    437514        case MT_TurnStart: