| Prev: Creating custom sensors or motors | Up: Home | Next: Implementing a new robot |
|---|---|---|
Table of contents
In this page we will talk about WObject and related classes. WObject is the base for all classes modelling objects that live in the simulated world, both physical and purely graphical entities. We will also introduce helper classes to easily create graphical entities and briefly talk about physical entities
All the entities the can be placed in the simulated World inherit from the WObject class. This includes both physical objects and objects that only have a graphical representation, as well as objects that are perceived by robot sensors (e.g. a light bulb or a coloured area on the ground). In the next sections we will first introduce the WObject class, and then its main subclasses.
Every WObject has several properties, here is the list:
The API documentation lists the functions you can use to manipulate these properties. WObjects also have owner/owned relationship: each object can have one owner and can own several other objects. It is the responsability of the owner to delete an owned object (this is done automatically unless configured otherwise).
The constructor of the WObject class has one parameter that deserves special care. It is the last one, a boolean value called addToWorld, which determines whether the WObject constructor should add the object being constructed to the World or not. Of course all objects should be added to the World, so that they are correctly simulated. This should happend, however, after all the constructors in the hierarchy are called (because otherwise the type of the object that is added is not correctly identified). If, for example, a class called MyObject inherits from WObject, when an instance of MyObject is created, the new object should be added to the World only at the end of the constructor of MyObject. The addToWorld parameter implements a workaround to this problem: is set to false, the constructor of WObject does not add the object to the World, and the constructor of the child class is responsible to perform this action (calling the pushObject function of the World class). Moreover, the same mechanism should be implemented by WObject subclasses that are meant to be subclassed. The parameter should only be set explicitly when calling the constructor of the parent class, never when instantiating a new object (in this case the default value should be used). Here is an example:
:::C++
class MyObject : public WObject
{
[...]
MyObject(World* world, QString name="unamed", const wMatrix& tm = wMatrix::identity(), bool addToWorld = true)
: WObject(world, name, tm, false)
, [...]
{
if (addToWorld) {
worldv->pushObject(this);
}
}
[...]
};
When an instance of MyObject is created, the constructor is executed with the addToWorld parameter set to true, so that the object is added to the World at the end of the MyObject constructor. The WObject constructor, instead, is called with the addToWorld parameter set to false. The addToWorld parameter can be omitted in constructors of classes that are never subclassed or where sublcasses share the same RenderWObject (see below) of the parent class: in this case the constructor always adds the object to the world. (The addToWorld parameter will be removed in the future major release of FARSA).
The WObject class has some virtual functions that can be implemented in subclasses. The preUpdate and postUpdate methods are called respectively before the physic is advanced by one step and after the step is completed. These are useful, for example, to update the tranformation matrix or, in the case of robots implementations, to respectively actuate the robot motors and to read the robot sensors (see this page for more information on custom robots). The other virtual function is changedMatrix, which is called whenever the position matrix of the object is changed.
Each WObject has an associated 3D graphical representation. A subclass of RenderWObject is delegated to render a WObject subclass via the registerRenderWObjectFor static function of the class RenderWObjectContainer. This function takes as template parameter the type of the object performing the rendering, while as the only function parameter the name of the class to be rendered (the latter is a string because the QT metaobject system is used).
The RenderWObject class has one virtual function that must be implemented, namely render. The rendering is performed via standard OpenGL calls. See API documentation for more information on the class and on the other functions that can be implemented. If you need to create a purely graphical object, however, It is generally easier to use the GraphicalWObject class, described below.
The GraphicalWObject class provides some facilities that are useful when dealing with world objects that are purely graphical elements. First of all there is no need to create a separate class to which rendering is delegated. The class provides virtual functions that can be implemented to render the object, analogous to the functions of RenderWObject. Furthermore there is the possibility to "attach" the object to another object. This means that the GraphicalWObject always maintans the relative position to the object to which it is attached. This is useful, for example, to attach graphical elements to a robot.
The file graphicalmarkers.h contains few examples of GraphicalWObject subclasses. One of them is a simple circular disk. The source code is the following one:
:::C++
class CircularGraphicalMarker : public GraphicalWObject
{
Q_OBJECT
public:
/**
* \brief Constructor
*
* \param radius the radius of the disk
* \param world the world the object lives in
* \param name the name of this object
* \param tm the transformation matrix for this object
*/
CircularGraphicalMarker(real radius, World* world, QString name = "unamed", const wMatrix& tm = wMatrix::identity());
/**
* \brief Destructor
*/
virtual ~CircularGraphicalMarker();
protected:
/**
* \brief Performs the actual drawing
*
* Reimplement to draw what you need
* \param renderer the RenderWObject object associated with this one.
* Use it e.g. to access the container
* \param gw the OpenGL context
*/
virtual void render(RenderWObject* renderer, QGLContext* gw);
private:
/**
* \brief The radius of the disk
*/
const real m_radius;
};
CircularGraphicalMarker::CircularGraphicalMarker(real radius, World* world, QString name, const wMatrix& tm) :
GraphicalWObject(world, name, tm),
m_radius(radius)
{
}
CircularGraphicalMarker::~CircularGraphicalMarker()
{
}
void CircularGraphicalMarker::render(RenderWObject* renderer, QGLContext* gw)
{
// Pushing our transformation matrix
renderer->container()->setupColorTexture(gw, renderer);
glPushMatrix();
GLMultMatrix(&tm[0][0]);
// Drawing the disk. We disable lightining
glPushAttrib(GL_LIGHTING_BIT);
glDisable(GL_LIGHTING);
glColor3f(color().redF(), color().greenF(), color().blueF());
// Actually drawing the disk
GLUquadricObj *pObj = gluNewQuadric();
gluDisk(pObj, 0.0f, m_radius, 20, 1);
gluDeleteQuadric(pObj);
// Restoring lighting status
glPopAttrib();
glPopMatrix();
}
The example above shows that standard OpenGL instructions are used to draw the circle. Please note that when the render function or other rendering functions are executed, the transformation matrix has already been changed to reflect the current position in the world (i.e. it already takes into account the position of the object to which the GraphicalWObject is attached).
Objects with physical properties are all subclasses of PhyObject, which in turn inheriths WObject. We will not give details on how to create new physical objects, because it requires a deep understanding of FARSA internals and knowledge of the Newton Game Dynamics library. You should never need to do something like this, however, because most of the capabilities of the physics library are already available as methods of the World class or via PhyObject and PhyJoint subclasses.
Manual: APIDoc
Manual: CustomSensorMotor
Manual: Home
Manual: NewRobot
Anonymous