2
Copyright (C) 2004 Erik Hjortsberg
4
This program is free software; you can redistribute it and/or modify
5
it under the terms of the GNU General Public License as published by
6
the Free Software Foundation; either version 2 of the License, or
7
(at your option) any later version.
9
This program is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
GNU General Public License for more details.
14
You should have received a copy of the GNU General Public License
15
along with this program; if not, write to the Free Software
16
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22
#include "EmberEntity.h"
24
#include "framework/Service.h"
25
#include "framework/ConsoleBackend.h"
26
#include "services/EmberServices.h"
27
#include "services/sound/SoundService.h"
28
#include "EmberEntityFactory.h"
29
#include "MotionManager.h"
30
#include "GUIManager.h"
31
#include "terrain/TerrainArea.h"
32
#include "terrain/TerrainMod.h"
33
#include "MathConverter.h"
35
#include "EmberOgre.h"
36
#include <OgreWireBoundingBox.h>
37
#include <OgreException.h>
39
#include "terrain/TerrainGenerator.h"
40
#include "terrain/TerrainPage.h"
42
#include <Mercator/Area.h>
43
#include <Mercator/TerrainMod.h>
44
//#include <Atlas/Objects/ObjectsFwd.h>
45
#include <Eris/TypeInfo.h>
46
#include <Eris/View.h>
47
#include <Atlas/Formatter.h>
48
#include <Atlas/Objects/Decoder.h>
49
#include <Atlas/Codecs/XML.h>
50
#include <Atlas/Message/MEncoder.h>
51
#include <Atlas/Message/QueuedDecoder.h>
54
#include "model/Model.h"
62
This is just like a WireBoundBox but not aligned to the axes, hence it will correctly line up according to it's orientation.
64
class OOBBWireBoundingBox : public WireBoundingBox
68
void getWorldTransforms( Matrix4* xform ) const
70
SimpleRenderable::getWorldTransforms(xform);
92
const std::string EmberEntity::MODE_STANDING("standing");
93
const std::string EmberEntity::MODE_RUNNING("running");
94
const std::string EmberEntity::MODE_WALKING("walking");
95
const std::string EmberEntity::MODE_SWIMMING("swimming");
96
const std::string EmberEntity::MODE_FLOATING("floating");
97
const std::string EmberEntity::MODE_FIXED("fixed");
99
const std::string EmberEntity::BboxMaterialName("BaseYellowNoLightning");
102
EmberEntity::EmberEntity(const std::string& id, Eris::TypeInfo* ty, Eris::View* vw,Ogre::SceneManager* sceneManager)
104
Eris::Entity(id, ty, vw)
105
, mIsInitialized(false)
106
, mIsInMotionManager(false)
107
, mErisEntityBoundingBox(0)
111
, mMovementMode(MM_DEFAULT)
113
createSceneNode(sceneManager);
116
EmberEntity::~EmberEntity()
118
///detach all children so we don't destroy them
119
while (getSceneNode()->numChildren()) {
120
getSceneNode()->removeChild((short unsigned int)0);
122
Ogre::SceneNode* parent = static_cast<Ogre::SceneNode*>(getSceneNode()->getParent());
124
parent->removeAndDestroyChild(getSceneNode()->getName());
126
getSceneNode()->getCreator()->destroySceneNode(getSceneNode()->getName());
128
///make sure it's not in the MotionManager
129
///TODO: keep a marker in the entity so we don't need to call this for all entities
130
MotionManager::getSingleton().removeEntity(this);
132
if (mErisEntityBoundingBox) {
133
mErisEntityBoundingBox->getParentSceneNode()->getCreator()->destroySceneNode(mErisEntityBoundingBox->getParentSceneNode()->getName());
135
OGRE_DELETE mErisEntityBoundingBox;
137
//mSceneManager->destroySceneNode(getSceneNode()->getName());
140
void EmberEntity::init(const Atlas::Objects::Entity::RootEntity &ge, bool fromCreateOp)
142
Eris::Entity::init(ge, fromCreateOp);
144
synchronizeWithServer();
146
// set the Ogre node position and orientation based on Atlas data
147
std::stringstream ss;
148
if (getPredictedPos().isValid()) {
149
ss << "Entity " << getId() << "(" << getName() << ") placed at (" << getPredictedPos().x() << "," << getPredictedPos().y() << "," << getPredictedPos().x() << ")";
151
S_LOG_VERBOSE( ss.str());
153
mIsInitialized = true;
155
///Delay the checking and creation of area and terrainmod, since if we do it in onAttrChanged before the entity is properly initialized we'll get problems with multiple reparsings, as well as problems with placing the areas or terrainmod before the entity has been moved to it's proper place.
156
///Another way of doing this would be by attaching listeners, but that require more memory. It might be in the end that that's a better approach, depending on how much memory is needed, contrasted with the extra cycles spent here for every entity.
157
if (hasAttr("area")) {
158
createDependentObject("area");
161
if (hasAttr("terrainmod")) {
162
createDependentObject("terrainmod");
167
bool EmberEntity::createDependentObject(const std::string& attributeName)
169
///if the area attribute has changed, and we _don't_ have any mTerrainArea instance, try to create one such.
170
///if we do have an mTerrainArea instance, all updates will be taken care of by that instead and we can ignore this
171
if (attributeName == "area" && !mTerrainArea.get()) {
172
mTerrainArea = std::auto_ptr<Terrain::TerrainArea>(new Terrain::TerrainArea(this));
173
if (mTerrainArea->init()) {
174
addArea(mTerrainArea.get());
177
///if we couldn't properly initialize, delete the instance now, and then hopefully the next time the "area" attribute is changed we'll be able to properly create an area
178
mTerrainArea.reset();
182
///if the area attribute has changed, and we _don't_ have any mTerrainMod instance, try to create one such.
183
///if we do have an mTerrainMod instance, all updates will be taken care of by that instead and we can ignore this
184
if (attributeName == "terrainmod" && !mTerrainMod.get()) {
185
mTerrainMod = std::auto_ptr<Terrain::TerrainMod>(new Terrain::TerrainMod(this));
186
if (mTerrainMod->init()) {
187
addTerrainMod(mTerrainMod.get());
190
///if we couldn't properly initialize, delete the instance now, and then hopefully the next time the "area" attribute is changed we'll be able to properly create a mod
198
void EmberEntity::synchronizeWithServer()
200
if (getPosition().isValid()) {
201
getSceneNode()->setPosition(Atlas2Ogre(getPredictedPos()));
204
if (getOrientation().isValid()) {
205
getSceneNode()->setOrientation(Atlas2Ogre(getOrientation()));
209
void EmberEntity::createSceneNode(Ogre::SceneManager* sceneManager)
211
EmberEntity* container = getEmberLocation();
212
if (container == 0) {
213
mOgreNode = sceneManager->createSceneNode(getId());
215
Ogre::SceneNode * node = container->getSceneNode();
216
mOgreNode = node->createChildSceneNode(getId());
220
void EmberEntity::updateMotion(Ogre::Real timeSlice)
222
getSceneNode()->setPosition(Atlas2Ogre(getPredictedPos()));
225
//if there's a debug bounding box for the eris entity, update it's position
226
if (mErisEntityBoundingBox) {
227
mErisEntityBoundingBox->getParentSceneNode()->setPosition(getSceneNode()->getPosition());
228
mErisEntityBoundingBox->getParentSceneNode()->setOrientation(getSceneNode()->getOrientation());
233
void EmberEntity::onMoved()
235
Eris::Entity::onMoved();
236
const WFMath::Quaternion& orient = getOrientation();
237
getSceneNode()->setOrientation(Atlas2Ogre(orient));
241
void EmberEntity::setMoving(bool moving)
243
// Call the overridden method
244
Eris::Entity::setMoving(moving);
246
MotionManager* motionManager = &MotionManager::getSingleton();
248
//the entity is moving
249
if (!mIsInMotionManager) {
250
motionManager->addEntity(this);
251
mIsInMotionManager = true;
254
//the entity has stopped moving
255
if (mIsInMotionManager) {
256
motionManager->removeEntity(this);
257
mIsInMotionManager = false;
262
void EmberEntity::onTalk(const Atlas::Objects::Operation::RootOperation& talkArgs)
264
const std::vector<Atlas::Objects::Root>& args = talkArgs->getArgs();
266
Eris::Entity::onTalk(talkArgs);
270
const Atlas::Objects::Root& talk = args.front();
273
if (!talk->hasAttr("say")) {
274
Eris::Entity::onTalk(talkArgs);
279
///some talk operations come with a predefined set of suitable responses, so we'll store those so that they can later on be queried by the GUI for example
280
mSuggestedResponses.clear();
281
if (talk->hasAttr("responses")) {
282
if (talk->getAttr("responses").isList()) {
283
const Atlas::Message::ListType & responseList = talk->getAttr("responses").asList();
284
Atlas::Message::ListType::const_iterator I = responseList.begin();
285
for(; I != responseList.end(); ++I) {
286
mSuggestedResponses.push_back(I->asString());
292
const std::string& msg = talk->getAttr("say").asString();
297
std::string message = "<";
298
message.append(getName());
300
const std::string& type = getType()->getName(); // Eris type as a string
301
message.append(type);
302
message.append("> ");
304
S_LOG_VERBOSE( "Entity says: [" << message << "]\n" );
306
/// Make the message appear in the chat box
307
GUIManager::getSingleton().AppendIGChatLine.emit(msg, this);
309
/// Make a sound in OpenAL
310
// Ember::EmberServices::getSingleton().getSoundService()->playTalk(msg,
311
// getPosition(),getOrientation());
313
/// Call the method of the base class (since we've overloaded it)
314
Eris::Entity::onTalk(talkArgs);
318
void EmberEntity::onSoundAction( const Atlas::Objects::Operation::RootOperation & op )
320
///We'll just catch the call and write something to both the log and the console, and then pass it on.
321
const std::list<std::string> &p = op->getParents();
322
std::list<std::string>::const_iterator I = p.begin();
326
const std::string& name = *I;
327
std::string message = getName() + " emits a " + name + ".";
329
Ember::ConsoleBackend::getSingletonPtr()->pushMessage(message);
331
S_LOG_VERBOSE( "Entity: " << this->getId() << " (" << this->getName() << ") sound action: " << name);
334
Eris::Entity::onSoundAction(op);
338
void EmberEntity::onVisibilityChanged(bool vis)
340
checkClientVisibility(vis);
341
Eris::Entity::onVisibilityChanged(vis);
344
void EmberEntity::checkClientVisibility(bool vis)
346
///since we don't want to show all entities solely by their server flags (for example, an inventory item belonging to a character might not be shown even though the server thinks it's visible) we have to some more checks before we decide whether to show this or not
347
EmberEntity* container = static_cast<EmberEntity*>(getLocation());
349
///check with the parent first if we should show ourselves
350
if (vis && container->allowVisibilityOfMember(this)) {
351
///don't cascade, only change the top node
352
setClientVisible(true);
354
setClientVisible(false);
358
setClientVisible(vis);
363
void EmberEntity::setClientVisible(bool visible)
365
///when entities are hidden, we detach them from the rendering scene graph altogether. this speeds up Ogre since it doesn't have to calculate visibility for nodes that are hidden anyway
367
if (getSceneNode()->getParent()) {
368
getSceneNode()->getParent()->removeChild(getSceneNode());
372
if (!getSceneNode()->getParent()) {
373
getEmberLocation()->getSceneNode()->addChild(getSceneNode());
377
EventClientVisibilityChanged.emit(visible);
378
// getSceneNode()->setVisible(visible && getLocation(), false);
382
void EmberEntity::adjustPosition()
384
if (getPredictedPos().isValid()) {
385
adjustPosition(Atlas2Ogre( getPredictedPos() ));
389
void EmberEntity::adjustPosition(const Ogre::Vector3& position)
391
if (mMovementMode == MM_FIXED) {
394
EmberEntity* container = getEmberLocation();
396
container->adjustPositionForContainedNode(this, position);
401
const Ogre::Vector3& EmberEntity::getOffsetForContainedNode(const Ogre::Vector3& localPosition, EmberEntity* const entity)
403
///send it upwards until we get a an entity which knows how to set the position (we'll in most cases end up in the world which will adjust the height a bit)
404
EmberEntity* container = getEmberLocation();
406
//TerrainPosition derivedPosition(getPredictedPos().x() + position.x(), getPredictedPos().y() + position.y());
407
return container->getOffsetForContainedNode(localPosition + getSceneNode()->getPosition(), entity);
409
return Ogre::Vector3::ZERO;
417
void EmberEntity::adjustPositionForContainedNode(EmberEntity* const entity, const Ogre::Vector3& position)
420
Ogre::SceneNode* sceneNode = entity->getSceneNode();
421
//Ogre::Vector3 position = sceneNode->getPosition();
422
const Ogre::Vector3& offset = getOffsetForContainedNode(position, entity);
423
if (offset != Ogre::Vector3::ZERO) {
425
sceneNode->setPosition(position + offset);
430
void EmberEntity::onLocationChanged(Eris::Entity *oldLocation)
433
if (getLocation() == oldLocation)
435
///If it's the same location, don't do anything, but do add a warning to the log since this isn't supposed to happen.
436
S_LOG_WARNING( "Same new location as old for entity: " << this->getId() << " (" << this->getName() << ")" );
437
return Eris::Entity::onLocationChanged(oldLocation);
439
Eris::Entity::onLocationChanged(oldLocation);
441
///Ff we're attached to something, detach from it.
444
///Before we detach ourselves from our current parent, we need to record our current position in the world. This will come in handy later on when we need to determine if we actually moved in the world space.
445
const Ogre::Vector3 oldWorldPosition = getSceneNode()->_getDerivedPosition();
447
///Get the new location. We use getEmberLocation() since we always know that all entities are of type EmberEntity.
448
EmberEntity* newLocationEntity = getEmberLocation();
449
if (getSceneNode()->getParentSceneNode()) {
450
///Detach from our current parent scene node, removing us from the scene node graph.
451
getSceneNode()->getParentSceneNode()->removeChild(getSceneNode()->getName());
453
if (!newLocationEntity) {
454
///If there's no new location, just leave now. We've already removed ourselves from the scene node graph, so this entity is in effect in limbo.
458
///Add ourselves to the scene node of our parent.
459
newLocationEntity->getSceneNode()->addChild(getSceneNode());
460
S_LOG_VERBOSE( "Entity: " << this->getId() << " (" << this->getName() << ") relocated to: "<< newLocationEntity->getId() << " (" << newLocationEntity->getName() << ")" );
461
if (getPosition().isValid()) {
462
///Note that in some instances, for instance when the avatar enters the sty, the position isn't updated yet, which will make the avatar "snap" to an incorrect position (since the parent node has changed) until next frame, when the position should have been updated.
463
getSceneNode()->setPosition(Atlas2Ogre(getPredictedPos()));
465
std::stringstream ss;
466
ss << getPredictedPos();
467
S_LOG_VERBOSE("New position for entity: " << this->getId() << " (" << this->getName() << " ) :" << ss.str());
469
if (getOrientation().isValid()) {
470
getSceneNode()->setOrientation(Atlas2Ogre(getOrientation()));
471
std::stringstream ss;
472
ss << getOrientation();
473
S_LOG_VERBOSE("New orientation for entity: " << this->getId() << " (" << this->getName() << " ) :" << ss.str());
476
checkClientVisibility(isVisible());
478
///we'll adjust the entity so it retains it's former position in the world, but only for moving entities
479
///since else we'll get a "gap" when we're waiting on updated positions from the server
480
///this isn't optimal
482
const Ogre::Vector3& newWorldPosition = getSceneNode()->_getDerivedPosition();
483
getSceneNode()->translate(oldWorldPosition - newWorldPosition);
489
void EmberEntity::onAction(const Atlas::Objects::Operation::RootOperation& act)
491
const std::list<std::string> &p = act->getParents();
492
std::list<std::string>::const_iterator I = p.begin();
496
const std::string& name = *I;
497
std::string message = getName() + " performs a " + name + ".";
499
Ember::ConsoleBackend::getSingletonPtr()->pushMessage(message);
501
S_LOG_VERBOSE( "Entity: " << this->getId() << " (" << this->getName() << ") action: " << name);
503
Entity::onAction(act);
506
void EmberEntity::onImaginary(const Atlas::Objects::Root& act)
508
Atlas::Message::Element attr;
509
if (act->copyAttr("description", attr) && attr.isString()) {
510
std::string message = getName() + " " + attr.asString() + ".";
512
Ember::ConsoleBackend::getSingletonPtr()->pushMessage(message);
514
S_LOG_VERBOSE("Entity: " << this->getId() << " (" << this->getName() << ") imaginary: " << attr.String());
517
Entity::onImaginary(act);
521
bool EmberEntity::allowVisibilityOfMember(EmberEntity* entity) {
525
const std::vector< std::string >& EmberEntity::getSuggestedResponses( ) const
527
return mSuggestedResponses;
530
bool EmberEntity::hasSuggestedResponses( ) const
532
return mSuggestedResponses.size() > 0;
536
void EmberEntity::addArea(Terrain::TerrainArea* area)
538
///A normal EmberEntity shouldn't know anything about the terrain, so we can't handle the area here.
539
///Intead we just pass it on to the parent until we get to someone who knows how to handle this (preferrably the terrain).
540
if (getEmberLocation()) {
541
getEmberLocation()->addArea(area);
545
void EmberEntity::addTerrainMod(Terrain::TerrainMod* mod)
547
///Same as addArea: pass it on to the parent until it gets to someone who knows how to handle this
548
if (getEmberLocation()) {
549
getEmberLocation()->addTerrainMod(mod);
553
void EmberEntity::onAttrChanged(const std::string& str, const Atlas::Message::Element& v)
557
} else if (str == "bbox") {
558
Entity::onAttrChanged(str, v);
563
///call this before checking for areas and terrainmods, since those instances created to handle that (TerrainMod and TerrainArea) will listen to the AttrChanged event, which would then be emitted after those have been created, causing duplicate regeneration from the same data
564
Entity::onAttrChanged(str, v);
566
///Only to this if the entity has been propely initialized, to avoid using incomplete entity data (for example an entity which haven't yet been moved to it's proper place, as well as avoiding duplicate parsing of the same data.
567
if (mIsInitialized) {
568
createDependentObject(str);
574
void EmberEntity::parseModeChange(const Atlas::Message::Element& v)
576
const std::string& mode = v.asString();
577
MovementMode newMode;
579
newMode = MM_DEFAULT;
580
} else if (mode == MODE_STANDING) {
581
newMode = MM_STANDING;
582
} else if (mode == MODE_RUNNING) {
583
newMode = MM_RUNNING;
584
} else if (mode == MODE_WALKING) {
585
newMode = MM_WALKING;
586
} else if (mode == MODE_SWIMMING) {
587
newMode = MM_SWIMMING;
588
} else if (mode == MODE_FLOATING) {
589
newMode = MM_FLOATING;
590
} else if (mode == MODE_FIXED) {
593
newMode = MM_DEFAULT;
596
onModeChanged(newMode);
599
void EmberEntity::onModeChanged(MovementMode newMode)
601
if (newMode == MM_FIXED) {
604
EventModeChanged.emit(newMode);
605
mMovementMode = newMode;
608
void EmberEntity::setVisualize(const std::string& visualization, bool visualize)
610
if (visualization == "OgreBBox") {
611
showOgreBoundingBox(visualize);
612
} else if (visualization == "ErisBBox") {
613
showErisBoundingBox(visualize);
617
bool EmberEntity::getVisualize(const std::string& visualization) const
619
if (visualization == "OgreBBox") {
620
return getShowOgreBoundingBox();
621
} else if (visualization == "ErisBBox") {
622
return getShowErisBoundingBox();
628
void EmberEntity::showOgreBoundingBox(bool show)
630
getSceneNode()->showBoundingBox(show);
633
void EmberEntity::showErisBoundingBox(bool show)
636
createErisBboxMaterial();
638
///if there's no bounding box, create one now
639
///allowing for some lazy loading
640
if (!mErisEntityBoundingBox) {
641
mErisEntityBoundingBox = OGRE_NEW Ogre::OOBBWireBoundingBox();
642
mErisEntityBoundingBox->setMaterial(BboxMaterialName);
643
Ogre::SceneNode* boundingBoxNode = EmberOgre::getSingleton().getWorldSceneNode()->createChildSceneNode();
644
boundingBoxNode->attachObject(mErisEntityBoundingBox);
646
///if there's no bounding box defined for this entity, show one that is 0.2 meters across in all direction
648
mErisEntityBoundingBox->setupBoundingBox(Atlas2Ogre(getBBox()));
650
mErisEntityBoundingBox->setupBoundingBox(Ogre::AxisAlignedBox(-0.1, -0.1, -0.1, 0.1, 0.1, 0.1));
653
boundingBoxNode->setPosition(Atlas2Ogre(getPredictedPos()));
654
boundingBoxNode->setOrientation(Atlas2Ogre(getOrientation()));
656
mErisEntityBoundingBox->setVisible(show);
660
void EmberEntity::createErisBboxMaterial()
662
if (!Ogre::MaterialManager::getSingleton().resourceExists(BboxMaterialName)) {
663
Ogre::MaterialPtr baseYellowNoLighting = Ogre::MaterialManager::getSingleton().create(BboxMaterialName,
664
Ogre::ResourceGroupManager::INTERNAL_RESOURCE_GROUP_NAME);
665
baseYellowNoLighting->setLightingEnabled(false);
666
baseYellowNoLighting->setAmbient(Ogre::ColourValue(1, 1, 0.7));
667
baseYellowNoLighting->setDiffuse(Ogre::ColourValue(1, 1, 0.7));
671
void EmberEntity::onBboxChanged()
673
if (mErisEntityBoundingBox) {
674
mErisEntityBoundingBox->setupBoundingBox(Atlas2Ogre(getBBox()));
679
bool EmberEntity::getShowOgreBoundingBox() const
681
return getSceneNode()->getShowBoundingBox();
683
bool EmberEntity::getShowErisBoundingBox() const
685
return (mErisEntityBoundingBox && mErisEntityBoundingBox->isVisible());
689
Ogre::SceneNode* EmberEntity::getSceneNode() const
694
EmberEntity* EmberEntity::getEmberLocation() const
696
return static_cast<EmberEntity*>(getLocation());
700
const Ogre::AxisAlignedBox& EmberEntity::getWorldBoundingBox(bool derive) const
702
static Ogre::AxisAlignedBox boundingBox(0,0,0,0,0,0);
706
const Ogre::Sphere & EmberEntity::getWorldBoundingSphere (bool derive) const
708
static Ogre::Sphere sphere;
712
std::vector<std::string> EmberEntity::getActions()
714
///get the actions from Eris and return them a simple vector of strings
715
std::vector<std::string> actions;
717
if (hasAttr("actions")) {
718
const Atlas::Message::Element& operations = valueOfAttr("actions");
719
if (operations.isList()) {
720
const Atlas::Message::ListType& list = operations.asList();
721
actions.reserve(list.size());
722
Atlas::Message::ListType::const_iterator J = list.begin();
723
for (; J != list.end(); ++J) {
725
actions.push_back(J->asString());
734
std::vector<std::string> EmberEntity::getDefaultUseOperators()
736
///get the use operations from Eris and return them a simple vector of strings
737
std::vector<std::string> operators;
739
Eris::TypeInfoArray types = getUseOperations();
741
for (Eris::TypeInfoArray::iterator I = types.begin(); I != types.end(); ++I) {
742
operators.push_back((*I)->getName());
748
Ogre::SceneManager* EmberEntity::getSceneManager()
751
return mOgreNode->getCreator();
754
void EmberEntity::dumpAttributes(std::iostream& outstream, std::ostream& logOutstream) const
756
logOutstream << "Dumping attributes for entity " << getId() << "(" << getName() << ")" << std::endl;
758
Atlas::Message::QueuedDecoder decoder;
761
Atlas::Codecs::XML codec(outstream, decoder);
762
Atlas::Formatter formatter(outstream, codec);
763
Atlas::Message::Encoder encoder(formatter);
764
formatter.streamBegin();
765
encoder.streamMessageElement(getAttributes());
767
formatter.streamEnd();