diff --git a/source/maths/MathUtil.h b/source/maths/MathUtil.h index 21927d3465..a39eabdf9a 100644 --- a/source/maths/MathUtil.h +++ b/source/maths/MathUtil.h @@ -10,7 +10,7 @@ #define SQR(x) ((x) * (x)) template -T Interpolate(T& a, T& b, float l) +T Interpolate(const T& a, const T& b, float l) { return a + (b - a) * l; } diff --git a/source/simulation/Entity.cpp b/source/simulation/Entity.cpp index e98daa1f49..aab2002d2a 100644 --- a/source/simulation/Entity.cpp +++ b/source/simulation/Entity.cpp @@ -33,7 +33,7 @@ #include -extern int g_xres, g_yres; +const float MAX_ROTATION_RATE = 2*PI; // radians per second CEntity::CEntity( CEntityTemplate* base, CVector3D position, float orientation, const std::set& actorSelections, const CStrW* building ) { @@ -46,7 +46,7 @@ CEntity::CEntity( CEntityTemplate* base, CVector3D position, float orientation, m_ahead.x = sin( m_orientation.Y ); m_ahead.y = cos( m_orientation.Y ); m_position_previous = m_position; - m_orientation_previous = m_orientation; + m_orientation_smoothed = m_orientation_previous = m_orientation; m_player = NULL; m_productionQueue = new CProductionQueue( this ); @@ -268,6 +268,7 @@ void CEntity::kill(bool keepActor) m_graphics_position = m_position; m_position_previous = m_position; m_graphics_orientation = m_orientation; + m_orientation_smoothed = m_orientation; m_orientation_previous = m_orientation; snapToGround(); @@ -373,7 +374,7 @@ void CEntity::update( size_t timestep ) if( !m_extant ) return; m_position_previous = m_position; - m_orientation_previous = m_orientation; + m_orientation_previous = m_orientation_smoothed; CalculateRegen( timestep ); @@ -401,6 +402,31 @@ void CEntity::update( size_t timestep ) PROFILE_END( "aura processing" ); + updateOrders( timestep ); + + + // Calculate smoothed rotation: rotate around Y by at most MAX_ROTATION_RATE per second + + float delta = m_orientation.Y - m_orientation_smoothed.Y; + // Wrap delta to -PI..PI + delta = fmod(delta + PI, 2*PI); // range -2PI..2PI + if (delta < 0) delta += 2*PI; // range 0..2PI + delta -= PI; // range -PI..PI + // Clamp to max rate + float deltaClamped = clamp(delta, -MAX_ROTATION_RATE*timestep/1000.f, +MAX_ROTATION_RATE*timestep/1000.f); + // Calculate new orientation, in a peculiar way in order to make sure the + // result gets close to m_orientation (rather than being n*2*PI out) + float newY = m_orientation.Y + deltaClamped - delta; + // Apply the smoothed rotation + m_orientation_smoothed = CVector3D( + m_orientation.X, + newY, + m_orientation.Z + ); +} + +void CEntity::updateOrders( size_t timestep ) +{ // The process[...] functions return 'true' if the order at the top of the stack // still needs to be (re-)evaluated; else 'false' to terminate the processing of // this entity in this timestep. @@ -846,7 +872,9 @@ void CEntity::repath() void CEntity::reorient() { - m_orientation = m_graphics_orientation; + m_graphics_orientation = m_orientation; + m_orientation_previous = m_orientation; + m_orientation_smoothed = m_orientation; m_ahead.x = sin( m_orientation.Y ); m_ahead.y = cos( m_orientation.Y ); @@ -857,11 +885,16 @@ void CEntity::reorient() void CEntity::teleport() { - m_position = m_graphics_position; + m_position_previous = m_position; + m_graphics_position = m_position; m_bounds->setPosition( m_position.X, m_position.Z ); updateActorTransforms(); updateCollisionPatch(); - repath(); + + // TODO: repath breaks things - entities get sent to (0,0) if they're moved in + // Atlas. I can't see teleport being used anywhere else important, so + // hopefully it won't hurt to just remove it for now... +// repath(); } void CEntity::stanceChanged() @@ -940,7 +973,7 @@ void CEntity::interpolate( float relativeoffset ) updateXZOrientation(); - m_graphics_orientation = Interpolate( m_orientation_previous, m_orientation, relativeoffset ); + m_graphics_orientation = Interpolate( m_orientation_previous, m_orientation_smoothed, relativeoffset ); // Mark the actor transform data as invalid if the entity has moved since // the last call to 'interpolate'. diff --git a/source/simulation/Entity.h b/source/simulation/Entity.h index 1e99c31c88..20fdd5e02e 100644 --- a/source/simulation/Entity.h +++ b/source/simulation/Entity.h @@ -181,7 +181,8 @@ public: //-- Interpolated property CVector3D m_orientation; - CVector3D m_orientation_previous; + CVector3D m_orientation_smoothed; // used for slow graphical-only rotation - tends towards m_orientation + CVector3D m_orientation_previous; // previous smoothed value CVector3D m_graphics_orientation; CVector2D m_orientation_unclamped; @@ -245,6 +246,8 @@ private: bool shouldRun( float distance ); // Given our distance to a target, can we be running? + void updateOrders( size_t timestep_millis ); + public: ~CEntity(); diff --git a/source/simulation/EntityScriptInterface.cpp b/source/simulation/EntityScriptInterface.cpp index f467186cfd..b56174bbe6 100644 --- a/source/simulation/EntityScriptInterface.cpp +++ b/source/simulation/EntityScriptInterface.cpp @@ -101,7 +101,7 @@ void CEntity::ScriptingInit() AddClassProperty( L"group", &CEntity::m_grouped, false, (NotifyFn)&CEntity::checkGroup ); AddClassProperty( L"traits.extant", &CEntity::m_extant ); AddClassProperty( L"actions.move.turningRadius", &CEntity::m_turningRadius ); - AddClassProperty( L"position", &CEntity::m_graphics_position, false, (NotifyFn)&CEntity::teleport ); + AddClassProperty( L"position", &CEntity::m_position, false, (NotifyFn)&CEntity::teleport ); AddClassProperty( L"orientation", &CEntity::m_orientation, false, (NotifyFn)&CEntity::reorient ); AddClassProperty( L"player", (GetFn)&CEntity::JSI_GetPlayer, (SetFn)&CEntity::JSI_SetPlayer ); AddClassProperty( L"traits.health.curr", &CEntity::m_healthCurr ); diff --git a/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp index 4af9babce7..b1919a1f36 100644 --- a/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp @@ -577,13 +577,8 @@ BEGIN_COMMAND(MoveObject) if (unit->GetEntity()) { - // Set the current position, and also set the previous position so - // CEntity::interpolate puts the entity in the right place (without - // having to call CEntity::update before it'll look right) unit->GetEntity()->m_position = pos; - unit->GetEntity()->m_position_previous = pos; - unit->GetEntity()->m_bounds->setPosition(pos.X, pos.Z); - unit->GetEntity()->updateCollisionPatch(); + unit->GetEntity()->teleport(); if (unit->GetEntity()->m_base->m_isTerritoryCentre) g_Game->GetWorld()->GetTerritoryManager()->DelayedRecalculate(); @@ -679,8 +674,7 @@ BEGIN_COMMAND(RotateObject) if (unit->GetEntity()) { unit->GetEntity()->m_orientation.Y = angle; - // TODO: set bounds orientation? (but we'd have to work out whether - // it's an orientable type first) + unit->GetEntity()->reorient(); } else {