2
Avatar.cpp by Erik Hjortsberg
3
Copyright (C) 2002 Miguel Guzman, Erik Hjortsberg & The Worldforge
5
This program is free software; you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation; either version 2 of the License, or
8
(at your option) any later version.
10
This program is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
GNU General Public License for more details.
15
You should have received a copy of the GNU General Public License
16
along with this program; if not, write to the Free Software
17
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26
#include "components/ogre/EmberOgre.h"
27
#include "components/ogre/EmberEntity.h"
28
#include "components/ogre/Convert.h"
29
#include "components/ogre/AvatarLogger.h"
30
#include "components/ogre/NodeAttachment.h"
31
#include "components/ogre/AvatarAttachmentController.h"
32
#include "components/ogre/Scene.h"
34
#include "components/ogre/camera/MainCamera.h"
35
#include "components/ogre/camera/ThirdPersonCameraMount.h"
37
#include "components/ogre/model/Model.h"
38
#include "components/ogre/model/ModelRepresentation.h"
39
#include "components/ogre/model/ModelRepresentationManager.h"
41
#include "components/ogre/authoring/EntityMaker.h"
43
#include "services/EmberServices.h"
44
#include "services/server/ServerService.h"
45
#include "services/input/Input.h"
47
#include "framework/Tokeniser.h"
48
#include "framework/Time.h"
50
#include "main/Application.h"
52
#include <Eris/Avatar.h>
53
#include <Eris/Connection.h>
54
#include <Eris/TypeInfo.h>
58
#include <OgreTagPoint.h>
62
#include <wfmath/stream.h>
69
Avatar::Avatar(EmberEntity& erisAvatarEntity, Scene& scene, const Camera::CameraSettings& cameraSettings) :
70
SetAttachedOrientation("setattachedorientation", this, "Sets the orientation of an item attached to the avatar: <attachpointname> <x> <y> <z> <degrees>"), mErisAvatarEntity(erisAvatarEntity), mMaxSpeed(5), mAvatarAttachmentController(new AvatarAttachmentController(*this)), mCameraMount(new Camera::ThirdPersonCameraMount(cameraSettings, scene.getSceneManager())), mIsAdmin(false), mHasChangedLocation(false), mChatLoggerParent(0), mIsMovingServerOnly(false), mScene(scene), mEntityMaker(new Authoring::EntityMaker(erisAvatarEntity, *EmberServices::getSingleton().getServerService().getConnection()))
72
setMinIntervalOfRotationChanges(1000); //milliseconds
74
Application::getSingleton().EventAfterInputProcessing.connect(sigc::mem_fun(*this, &Avatar::application_AfterInputProcessing));
76
registerConfigListener("general", "logchatmessages", sigc::mem_fun(*this, &Avatar::Config_LogChatMessages));
77
registerConfigListener("general", "avatarrotationupdatefrequency", sigc::mem_fun(*this, &Avatar::Config_AvatarRotationUpdateFrequency));
78
registerConfigListener("input", "maxspeed", sigc::mem_fun(*this, &Avatar::Config_MaxSpeed));
80
mCurrentMovementState.movement = WFMath::Vector<3>::ZERO();
81
mCurrentMovementState.orientation = WFMath::Quaternion().identity();
82
mCurrentMovementState.position = erisAvatarEntity.getPredictedPos();
84
//check if the user is of type "creator" and thus an admin
85
Eris::TypeService* typeService = EmberServices::getSingleton().getServerService().getConnection()->getTypeService();
86
if (mErisAvatarEntity.getType()->isA(typeService->getTypeByName("creator"))) {
88
EmberServices::getSingleton().getServerService().getAvatar()->setIsAdmin(true);
93
mErisAvatarEntity.LocationChanged.connect(sigc::mem_fun(*this, &Avatar::avatar_LocationChanged));
94
mErisAvatarEntity.Moved.connect(sigc::mem_fun(*this, &Avatar::avatar_Moved));
95
mErisAvatarEntity.ChildAdded.connect(sigc::mem_fun(*this, &Avatar::entity_ChildAdded));
96
mErisAvatarEntity.ChildRemoved.connect(sigc::mem_fun(*this, &Avatar::entity_ChildRemoved));
98
mClientSideAvatarOrientation = mErisAvatarEntity.getOrientation();
99
mClientSideAvatarPosition = mErisAvatarEntity.getPosition();
101
mErisAvatarEntity.setAttachmentControlDelegate(mAvatarAttachmentController);
103
mCameraMount->attachToNode(getAvatarSceneNode());
108
mErisAvatarEntity.setAttachmentControlDelegate(0);
109
delete mAvatarAttachmentController;
113
void Avatar::runCommand(const std::string &command, const std::string &args)
115
if (SetAttachedOrientation == command) {
117
tokeniser.initTokens(args);
118
std::string attachPointName = tokeniser.nextToken();
119
if (attachPointName != "") {
120
std::string x = tokeniser.nextToken();
121
std::string y = tokeniser.nextToken();
122
std::string z = tokeniser.nextToken();
123
std::string degrees = tokeniser.nextToken();
124
if (x != "" && y != "" && z != "" && degrees != "") {
125
Ogre::Degree ogreDegrees(Ogre::StringConverter::parseReal(degrees));
126
Ogre::Quaternion rotation(ogreDegrees, Ogre::Vector3(Ogre::StringConverter::parseReal( x), Ogre::StringConverter::parseReal( y), Ogre::StringConverter::parseReal( z)));
127
Model::ModelRepresentation* modelRepresentation = Model::ModelRepresentationManager::getSingleton().getRepresentationForEntity(mErisAvatarEntity);
128
if (modelRepresentation) {
129
Model::Model& model = modelRepresentation->getModel();
130
const Model::Model::AttachPointWrapperStore* attachPoints = model.getAttachedPoints();
132
for (Model::Model::AttachPointWrapperStore::const_iterator I = attachPoints->begin(); I != attachPoints->end(); ++I) {
133
if (I->Definition.Name == attachPointName) {
134
I->TagPoint->setOrientation(rotation);
144
Camera::ThirdPersonCameraMount& Avatar::getCameraMount() const
146
return *mCameraMount;
149
void Avatar::setMinIntervalOfRotationChanges(Ogre::Real milliseconds)
151
mMinIntervalOfRotationChanges = milliseconds;
154
void Avatar::application_AfterInputProcessing(float timeSinceLastEvent)
159
void Avatar::moveClientSide(const WFMath::Quaternion& orientation, const WFMath::Vector<3>& movement, float timeslice)
161
mCurrentMovement = movement * mMaxSpeed;
162
if (movement != WFMath::Vector<3>::ZERO()) {
164
if (isOkayToSendRotationMovementChangeToServer()) {
165
//We need to constraint the orientation to only around the z axis.
166
WFMath::Vector<3> rotator(1.0, 0.0, 0.0);
167
rotator.rotate(orientation);
168
WFMath::Quaternion adjustedOrientation;
169
adjustedOrientation.fromRotMatrix(WFMath::RotMatrix<3>().rotationZ(atan2(rotator.y(), rotator.x())));
171
mClientSideAvatarOrientation = adjustedOrientation;
172
//For some not quite explained reason we need to rotate the orientation 90 degrees around the z axis for the orientation to be correct.
173
mClientSideAvatarOrientation.rotate(WFMath::Quaternion(WFMath::Vector<3>(0, 0, 1), WFMath::Pi / 2));
175
//...and then adjust the rotation 90 degrees in the other direction when calculating how to rotate the movement direction
176
WFMath::Quaternion adjustedOrientation = mClientSideAvatarOrientation;
177
adjustedOrientation.rotate(WFMath::Quaternion(WFMath::Vector<3>(0, 0, -1), WFMath::Pi / 2));
178
mCurrentMovement.rotate(adjustedOrientation);
179
mClientSideAvatarPosition += mCurrentMovement * timeslice;
181
if (mErisAvatarEntity.getAttachment()) {
182
mErisAvatarEntity.getAttachment()->updatePosition();
187
void Avatar::attemptMove()
190
//first we'll register the current state in newMovementState and compare to mCurrentMovementState
191
//that way we'll only send stuff to the server if our movement changes
192
AvatarMovementState newMovementState;
193
newMovementState.orientation = mClientSideAvatarOrientation;
194
newMovementState.movement = mCurrentMovement;
195
newMovementState.position = mClientSideAvatarPosition;
197
bool isMoving = mCurrentMovement.isValid() && mCurrentMovement != WFMath::Vector<3>::ZERO();
198
bool wasMoving = mCurrentMovementState.movement.isValid() && mCurrentMovementState.movement != WFMath::Vector<3>::ZERO();
199
bool sendToServer = false;
201
//first we check if we are already moving
203
//we are not moving. Should we start to move?
206
//let's send the movement command to the server
211
//we are already moving
212
//let's see if we've changed speed or direction or even stopped
214
//we have stopped; we must alert the server
216
} else if (newMovementState.movement != mCurrentMovementState.movement || newMovementState.orientation != mCurrentMovementState.orientation) {
217
//either the speed or the direction has changed
223
std::stringstream ss;
224
ss << "Sending move op to server, direction: " << newMovementState.movement << ", orientation: " << newMovementState.orientation << ", speed: " << sqrt(newMovementState.movement.sqrMag()) << ".";
225
S_LOG_VERBOSE(ss.str());
227
//Save the ten latest orientations sent to the server, so we can later when we receive an update from the server we can recognize that it's our own updates and ignore them.
228
long long currentTime = Time::currentTimeMillis();
229
mLastTransmittedMovements.push_back(TimedMovementStateList::value_type(currentTime, newMovementState));
230
if (mLastTransmittedMovements.size() > 10) {
231
mLastTransmittedMovements.erase(mLastTransmittedMovements.begin());
233
EmberServices::getSingleton().getServerService().moveInDirection(newMovementState.movement, newMovementState.orientation);
237
mCurrentMovementState = newMovementState;
239
//After we're done, set the current movement to zero. This of course means that you can only call this method once per frame.
240
mCurrentMovement = WFMath::Vector<3>::ZERO();
244
EmberEntity& Avatar::getEmberEntity()
246
return mErisAvatarEntity;
249
bool Avatar::isOkayToSendRotationMovementChangeToServer()
251
//Just check if we've recently sent something to the server
252
if (!mLastTransmittedMovements.size()) {
255
long long currentTime = Time::currentTimeMillis();
256
if ((currentTime - mLastTransmittedMovements.rbegin()->first) > mMinIntervalOfRotationChanges) {
262
Ogre::Node* Avatar::getAvatarSceneNode() const
264
NodeAttachment* attachment = dynamic_cast<NodeAttachment*> (mErisAvatarEntity.getAttachment());
266
return attachment->getNode();
271
Scene& Avatar::getScene() const
277
void Avatar::movedInWorld()
279
//only snap the avatar to the postition and orientation sent from the server if we're not moving or if we're not recently changed location
280
//The main reason when moving is that we don't want to have the avatar snapping back in the case of lag
281
//However, if we've just recently changed location, we need to also update the orientation to work with the new location.
282
// if (!mCurrentMovementState.isMoving || mHasChangedLocation)
284
// const WFMath::Quaternion& orient = mErisAvatarEntity->getOrientation();
285
// bool isOwnRotation = false;
286
// //Check if the new orientation is one that we sent ourself, and if so ignore it
287
// for (std::list<WFMath::Quaternion>::const_iterator I = mLastOrientations.begin(); I != mLastOrientations.end(); ++I) {
288
// if (orient == *I) {
289
// isOwnRotation = true;
293
// if (!isOwnRotation) {
294
// mAvatarNode->setOrientation(Convert::toOgre(orient));
296
// mAvatarNode->setPosition(Convert::toOgre(mErisAvatarEntity->getPosition()));
297
// //we must set this, else ember will think that we've rotated the avatar ourselves and try to send an update to the server
298
// mMovementStateAtLastServerMessage.orientation = mAvatarNode->getOrientation();
299
// mHasChangedLocation = false;
303
void Avatar::avatar_LocationChanged(Eris::Entity* entity)
305
mCameraMount->attachToNode(getAvatarSceneNode());
307
//if we've changed location, we need to update the orientation. This is done on the next onMoved event, which is why we must honour the updated values on next onMoved event, even though we might be moving.
308
mHasChangedLocation = true;
309
mClientSideAvatarOrientation = mErisAvatarEntity.getOrientation();
310
mClientSideAvatarPosition = mErisAvatarEntity.getPredictedPos();
313
void Avatar::avatar_Moved()
315
//For now this is disabled, since there are a couple of issues with it
316
// for (TimedMovementStateList::iterator I = mLastTransmittedMovements.begin(); I != mLastTransmittedMovements.end(); ++I) {
317
// float distance = WFMath::Distance(I->second.position, mErisAvatarEntity.getPosition());
318
// if (I->second.movement != WFMath::Vector<3>::ZERO() && distance < 0.3) {
319
// mLastTransmittedMovements.erase(I);
320
// S_LOG_VERBOSE("Ignoring server movement update since it's sent by us.");
324
if (mCurrentMovementState.movement == WFMath::Vector<3>::ZERO()) {
325
mClientSideAvatarOrientation = mErisAvatarEntity.getOrientation();
326
mClientSideAvatarPosition = mErisAvatarEntity.getPosition();
327
if (mErisAvatarEntity.getVelocity() != WFMath::Vector<3>::ZERO()) {
328
mIsMovingServerOnly = true;
330
mIsMovingServerOnly = false;
333
mIsMovingServerOnly = false;
337
void Avatar::entity_ChildAdded(Eris::Entity* childEntity)
339
EventAddedEntityToInventory.emit(static_cast<EmberEntity*> (childEntity));
342
void Avatar::entity_ChildRemoved(Eris::Entity* childEntity)
344
EventRemovedEntityFromInventory.emit(static_cast<EmberEntity*> (childEntity));
347
void Avatar::Config_AvatarRotationUpdateFrequency(const std::string& section, const std::string& key, varconf::Variable& variable)
349
setMinIntervalOfRotationChanges(static_cast<double> (variable));
352
void Avatar::Config_MaxSpeed(const std::string& section, const std::string& key, varconf::Variable& variable)
354
mMaxSpeed = static_cast<double> (variable);
357
void Avatar::Config_LogChatMessages(const std::string& section, const std::string& key, varconf::Variable& variable)
359
if (static_cast<bool> (variable)) {
360
mChatLoggerParent = std::auto_ptr<AvatarLoggerParent>(new AvatarLoggerParent(*this));
362
mChatLoggerParent.reset();
366
WFMath::Point<3> Avatar::getClientSideAvatarPosition() const
368
//NOTE: for now we've deactivated the client side prediction as it doesn't really work as it should
369
WFMath::Point<3> pos = mErisAvatarEntity.getPredictedPos();
370
return pos.isValid() ? pos : WFMath::Point<3>::ZERO();
371
// //If the avatar entity is moving, we're note moving on the client side, and we haven't sent something to the server lately, we should assume that we're moving as a result of server side actions, and therefore use the server side position
372
// // if (mCurrentMovement == WFMath::Vector<3>::ZERO() && mErisAvatarEntity.isMoving()) {
373
// // bool clientSideMovement = false;
374
// // if (mLastTransmittedMovements.size()) {
375
// // long long currentTime = EmberServices::getSingleton().getTimeService().currentTimeMillis();
376
// // if ((currentTime - mLastTransmittedMovements.rbegin()->first) < 1000) {
377
// // clientSideMovement = true;
380
// // if (!clientSideMovement) {
381
// // return mErisAvatarEntity.getPredictedPos();
385
// if (mIsMovingServerOnly) {
386
// return mErisAvatarEntity.getPredictedPos();
388
// return mClientSideAvatarPosition;
392
WFMath::Quaternion Avatar::getClientSideAvatarOrientation() const
394
//NOTE: for now we've deactivated the client side prediction as it doesn't really work as it should
395
return mErisAvatarEntity.getOrientation().isValid() ? mErisAvatarEntity.getOrientation() : WFMath::Quaternion().identity();
396
// if (mIsMovingServerOnly) {
397
// return mErisAvatarEntity.getOrientation();
399
// return mClientSideAvatarOrientation;
403
WFMath::Vector<3> Avatar::getClientSideAvatarVelocity() const
405
//NOTE: for now we've deactivated the client side prediction as it doesn't really work as it should
406
return mErisAvatarEntity.getVelocity().isValid() ? mErisAvatarEntity.getVelocity() : WFMath::Vector<3>::ZERO();
407
// if (mIsMovingServerOnly) {
408
// return mErisAvatarEntity.getVelocity();
410
// return mCurrentMovementState.movement;