Transports - phase 2. Implementing the GameObject rotation quaternions.

- As long as we send the correct rotation quaternion to the client, the GAMEOBJECT_FACING must be set to 0
  - GameObjectModel internal rotation matrices now take into account the rotation quaternion
This commit is contained in:
H0zen 2016-12-18 12:38:38 +02:00
parent 350feb5889
commit e803c57c9f
5 changed files with 114 additions and 44 deletions

View File

@ -58,7 +58,7 @@
#include <map>
#include <typeinfo>
#include "Formulas.h"
#include "G3D/Quat.h" // for turning GO's
#include "TargetedMovementGenerator.h" // for HandleNpcUnFollowCommand
#include "MoveMap.h" // for mmap manager
#include "PathFinder.h" // for mmap commands
@ -947,11 +947,27 @@ bool ChatHandler::HandleGameObjectTurnCommand(char* args)
if (!ExtractOptFloat(&args, o, m_session->GetPlayer()->GetOrientation()))
{ return false; }
Map* map = obj->GetMap();
map->Remove(obj, false);
// ok, let's rotate the GO around Z axis
// we first get the original rotation quaternion
// then we'll create a rotation quat describing the rotation around Z
G3D::Quat original_rot;
obj->GetQuaternion(original_rot);
obj->Relocate(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), o);
obj->UpdateRotationFields();
// the rotation amount around Z-axis
float deltaO = o - obj->GetOrientationFromQuat(original_rot);
// multiplying 2 quaternions gives the final rotation
// quaternion multiplication is not commutative!
G3D::Quat final_rot = G3D::Quat(0.0f, 0.0f, sin(deltaO/2), cos(deltaO/2)) * original_rot;
// quaternion multiplication gives a non-unit quat
final_rot.unitize();
Map* map = obj->GetMap();
map->Remove(obj, false); //mandatory to remove GO model from m_dyn_tree
obj->SetQuaternion(final_rot); // this will update internal model rotation matrices
obj->Relocate(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), obj->GetOrientationFromQuat(final_rot));
map->Add(obj);

View File

@ -23,6 +23,7 @@
*/
#include "GameObject.h"
#include "G3D/Quat.h"
#include "QuestDef.h"
#include "ObjectMgr.h"
#include "PoolManager.h"
@ -145,10 +146,32 @@ void GameObject::CleanupsBeforeDelete()
WorldObject::CleanupsBeforeDelete();
}
bool GameObject::Create(uint32 guidlow, uint32 name_id, Map* map, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, GOState go_state)
bool GameObject::Create(uint32 guidlow, uint32 name_id, Map* map, float x, float y, float z, float ang, float r0, float r1, float r2, float r3, uint32 animprogress, GOState go_state)
{
MANGOS_ASSERT(map);
Relocate(x, y, z, ang);
if (!map)
{ return false; }
GameObjectInfo const* goinfo = ObjectMgr::GetGameObjectInfo(name_id);
if (!goinfo)
{
sLog.outErrorDb("Gameobject (GUID: %u) not created: Entry %u does not exist in `gameobject_template`", guidlow, name_id);
return false;
}
Object::_Create(guidlow, goinfo->id, HIGHGUID_GAMEOBJECT);
// let's make sure we don't send the client invalid quaternion
if (r0 == 0.0f && r1 == 0.0f && r2 == 0.0f)
{
r2 = sin(ang/2);
r3 = cos(ang/2);
}
G3D::Quat q(r0, r1, r2, r3);
q.unitize();
float o = GetOrientationFromQuat(q);
Relocate(x, y, z, o);
SetMap(map);
if (!IsPositionValid())
@ -157,14 +180,7 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map* map, float x, float
return false;
}
GameObjectInfo const* goinfo = ObjectMgr::GetGameObjectInfo(name_id);
if (!goinfo)
{
sLog.outErrorDb("Gameobject (GUID: %u) not created: Entry %u does not exist in `gameobject_template`. Map: %u (X: %f Y: %f Z: %f) ang: %f rotation0: %f rotation1: %f rotation2: %f rotation3: %f", guidlow, name_id, map->GetId(), x, y, z, ang, rotation0, rotation1, rotation2, rotation3);
return false;
}
Object::_Create(guidlow, goinfo->id, HIGHGUID_GAMEOBJECT);
SetQuaternion(q);
m_goInfo = goinfo;
@ -180,11 +196,6 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map* map, float x, float
SetFloatValue(GAMEOBJECT_POS_Y, y);
SetFloatValue(GAMEOBJECT_POS_Z, z);
SetFloatValue(GAMEOBJECT_ROTATION + 0, rotation0);
SetFloatValue(GAMEOBJECT_ROTATION + 1, rotation1);
UpdateRotationFields(rotation2, rotation3); // GAMEOBJECT_FACING, GAMEOBJECT_ROTATION+2/3
SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction);
SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags);
@ -1673,18 +1684,30 @@ const char* GameObject::GetNameForLocaleIdx(int32 loc_idx) const
return GetName();
}
void GameObject::UpdateRotationFields(float rotation2 /*=0.0f*/, float rotation3 /*=0.0f*/)
void GameObject::SetQuaternion(G3D::Quat const& q)
{
SetFloatValue(GAMEOBJECT_FACING, GetOrientation());
SetFloatValue(GAMEOBJECT_ROTATION + 0, q.x);
SetFloatValue(GAMEOBJECT_ROTATION + 1, q.y);
SetFloatValue(GAMEOBJECT_ROTATION + 2, q.z);
SetFloatValue(GAMEOBJECT_ROTATION + 3, q.w);
if (rotation2 == 0.0f && rotation3 == 0.0f)
{
rotation2 = sin(GetOrientation() / 2);
rotation3 = cos(GetOrientation() / 2);
if (m_model)
{ m_model->UpdateRotation(q); }
}
SetFloatValue(GAMEOBJECT_ROTATION + 2, rotation2);
SetFloatValue(GAMEOBJECT_ROTATION + 3, rotation3);
void GameObject::GetQuaternion(G3D::Quat& q) const
{
q.x = GetFloatValue(GAMEOBJECT_ROTATION + 0);
q.y = GetFloatValue(GAMEOBJECT_ROTATION + 1);
q.z = GetFloatValue(GAMEOBJECT_ROTATION + 2);
q.w = GetFloatValue(GAMEOBJECT_ROTATION + 3);
}
float GameObject::GetOrientationFromQuat(G3D::Quat const& q)
{
double t1 = +2.0f * (q.w * q.z + q.x * q.y);
double t2 = +1.0f - 2.0f * (q.y * q.y + q.z * q.z);
return MapManager::NormalizeOrientation(std::atan2(t1, t2));
}
bool GameObject::IsHostileTo(Unit const* unit) const

View File

@ -491,16 +491,16 @@ enum GOState
// from `gameobject`
struct GameObjectData
{
uint32 id; // entry in gamobject_template
uint32 id; // entry in gameobject_template
uint32 mapid;
float posX;
float posY;
float posZ;
float orientation;
float rotation0;
float rotation1;
float rotation2;
float rotation3;
float rotation0; // i component of rotation quaternion
float rotation1; // j
float rotation2; // k
float rotation3; // w
int32 spawntimesecs;
uint32 animprogress;
GOState go_state;
@ -538,6 +538,12 @@ enum CapturePointSliderValue
class Unit;
class GameObjectModel;
namespace G3D
{
class Quat;
};
struct GameObjectDisplayInfoEntry;
// 5 sec for bobber catch
@ -565,7 +571,12 @@ class GameObject : public WorldObject
bool HasStaticDBSpawnData() const; // listed in `gameobject` table and have fixed in DB guid
void UpdateRotationFields(float rotation2 = 0.0f, float rotation3 = 0.0f);
// rotation methods
void GetQuaternion(G3D::Quat& q) const;
void SetQuaternion(G3D::Quat const& q);
float GetOrientationFromQuat(G3D::Quat const& q);
void SetDisplayId(uint32 model_id);
// overwrite WorldObject function for proper name localization
const char* GetNameForLocaleIdx(int32 locale_idx) const override;
@ -636,7 +647,7 @@ class GameObject : public WorldObject
uint32 GetGoAnimProgress() const { return GetUInt32Value(GAMEOBJECT_ANIMPROGRESS); }
void SetGoAnimProgress(uint32 animprogress) { SetUInt32Value(GAMEOBJECT_ANIMPROGRESS, animprogress); }
uint32 GetDisplayId() const { return GetUInt32Value(GAMEOBJECT_DISPLAYID); }
void SetDisplayId(uint32 modelId);
void SetDisplayIdx(uint32 modelId);
float GetObjectBoundingRadius() const override; // overwrite WorldObject version

View File

@ -32,6 +32,7 @@
#include "GameObjectModel.h"
#include "DBCStores.h"
#include "Creature.h"
#include "G3D/Quat.h"
struct GameobjectModelData
{
@ -107,9 +108,9 @@ bool GameObjectModel::initialize(const GameObject* const pGo, const GameObjectDi
if (it == model_list.end())
{ return false; }
G3D::AABox mdl_box(it->second.bound);
iModelBound = it->second.bound;
// ignore models with no bounds
if (mdl_box == G3D::AABox::zero())
if (iModelBound == G3D::AABox::zero())
{
sLog.outDebug("Model %s has zero bounds, loading skipped", it->second.name.c_str());
return false;
@ -126,18 +127,30 @@ bool GameObjectModel::initialize(const GameObject* const pGo, const GameObjectDi
iScale = pGo->GetObjectScale();
iInvScale = 1.f / iScale;
iRot = G3D::Matrix3::fromEulerAnglesZYX(pGo->GetOrientation(), 0, 0);
G3D::Quat q;
pGo->GetQuaternion(q);
UpdateRotation(q);
return true;
}
void GameObjectModel::UpdateRotation(G3D::Quat const& q)
{
q.toRotationMatrix(iRot);
iInvRot = iRot.inverse();
G3D::AABox mdl_box(iModelBound);
// transform bounding box:
mdl_box = AABox(mdl_box.low() * iScale, mdl_box.high() * iScale);
AABox rotated_bounds;
G3D::AABox rotated_bounds;
for (int i = 0; i < 8; ++i)
{ rotated_bounds.merge(iRot * mdl_box.corner(i)); }
iBound = rotated_bounds + iPos;
return true;
}
GameObjectModel* GameObjectModel::Create(const GameObject* const pGo)

View File

@ -40,6 +40,11 @@ namespace VMAP
class WorldModel;
}
namespace G3D
{
class Quat;
}
/**
* @brief
*
@ -51,6 +56,7 @@ class GameObjectModel
std::string iName;
G3D::AABox iBound;
G3D::AABox iModelBound;
G3D::Vector3 iPos;
G3D::Matrix3 iRot;
float iScale;
@ -71,6 +77,7 @@ class GameObjectModel
~GameObjectModel();
const G3D::Vector3& GetPosition() const { return iPos;}
void UpdateRotation(G3D::Quat const& q);
const GameObject* GetOwner() const { return iOwner; }
void SetCollidable(bool enabled) { isCollidable = enabled; }