[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Crystal Space is a package of components and libraries which can all be useful for creating computer games. Although some of the packages are intended more for 3D gaming this is not as such a restriction of the package. Components as the sound driver function just as well in a 2D gaming environment and the networking component can even be used in a text oriented environment. This highlights one of the important characteristics of Crystal Space: the components and libraries are more or less independent of each other. If you don't want networking, then just don't use the networking drivers. If you don't want scripting then don't include that. All packages are designed to be functional on their own or with a minimal number of other packages. There are some dependencies of course. For example, the 3D Engine requires a 3D Rasterizer to display its output. On the other hand, you could very well use the 3D Rasterizer without the 3D Engine.
Although there is a high level of independence, there is also a high level of integration. The components and libraries were also designed to be used together and as such offer a flexible scheme of integration.
If you are interested in developing a program using Crystal Space, then it is important to understand that Crystal Space is not a single monolithic library, but instead consists of several libraries and plug-in modules. This manual uses the term module for both libraries and plug-ins when the distinction between the two is not important.
A plug-in module is similar to a library, but has some advantages over a library. All plug-ins can be accessed in a common fashion. They have a very clear interface, and they can provide this interface even if they are extracted to a DLL. So they are the better solution as long as speed is not the dictator of all decisions as in the math library (access to plug-in functions uses virtual methods). The main difference between the interface of a library and a plug-in is that a library may use SCF and C++ classes; plug-ins may only use SCF.
The main SCF object is the Interface. An interface is the
solution to strictly decouple the public methods of an object from their
implementation. You only get a pointer to an abstract class with only virtual
methods, called the interface, so your program doesn't know about the actual
object behind the pointer. This object is called an Implementation of
the interface. You as the user of Crystal Space will call functions that
create the actual object, but only return the interface. After that you can
use the interface like a C++ object. When you don't need it anymore, don't
`delete' it, but call DecRef()
. When you pass a pointer to the
interface to anywhere, call AddRef()
from there, and DecRef()
when you don't need the interface there anymore.
As the user you'll only have to include a header that defines the interface, but not the implementation. Despite the obvious advantage of having a very clear structure in your code, there is the advantage that linking the interface to the implementation can be done at run-time, but more about this later.
A library is just a normal C++ library as you know them. A library can optionally provide SCF interfaces. In the case of a library this is just a way to define a clear structure. But as their main interface, libraries provide C++ classes.
A plug-in, on the other hand, will only provide SCF interfaces, no normal C++ classes. The plug-in itself will also appear as an SCF interface. This is part of the definition of a plug-in. A plug-in can be organized as static library or DLL; this only makes a small difference in how you use it in your program.
As the user of Crystal Space, you have to do the following steps to use a plug-in:
Registering means to tell SCF the name of the plug-in that was given to it by its author, and a class or a dynamic library to associate it with. For example, to use the software graphics driver you must tell SCF that the plug-in with the name `crystalspace.graphics3d.software' can be found in `soft3d.dll' or `soft3d.so'. Actually the extension will be added automatically, so you just have to use `soft3d' as the name of the library. You can find examples for this in `scf.cfg'.
How you must register a library depends on whether it is a static library or a dynamic library (`.dll' or `.so'). For a static library, put this line at toplevel into one of your C++ files and link to the static library:
SCF_REGISTER_STATIC_LIBRARY(name) |
For a dynamic library, use `scfreg' at your command line:
scfreg name.dll # For Windows or DOS scfreg libname.so # For most Unix platforms |
To load a plug-in, you must tell the system driver two things: The first thing is the name of the plug-in as it was registered in the previous step. The second name is the so-called functionality identifier. This identifier is used to access the plug-in from your program. You could for example load the software graphics module with the identifier `GFXDriver'. When you want to use the driver in your program you ask the system driver for the plug-in with this identifier.
Of course you may not load any driver at this identifier. For example, it doesn't make sense to load a sound driver and then use it as a graphics driver. Here we come back to the concept of SCF interfaces: All graphics drivers use one interface, and all sound drivers use one interface, but graphics and sound use different interfaces. Now you have loaded a driver with the `GFXDriver' identifier. In your program, you actually ask for a plug-in that is loaded with this identifier and implements the interface of the graphics driver. Alternatively, you may also search all identifiers for the first plug-in that implements the graphics driver's interface.
If you loaded the sound driver instead, it doesn't implement the graphics interface, so you can't use it. To your program this looks like if no driver was loaded at all for this identifier.
Note that the decision which graphics driver you use (e.g. Software or Direct3D) is done at the time you load the plug-in by passing the name of that driver. At the time you ask for the plug-in interface and use use it in your program, this does not make a difference anymore. This makes it possible to exchange the driver simply by loading another driver, but without changing your main program.
The CS modules themselves look for some standard plug-ins at standardized identifiers. For example, the 3d engine looks for the graphics driver at the `VideoDriver' identifier. So the engine will only work correctly if you use these standardized identifiers for the drivers. These are:
VFS
VideoDriver
SoundRender
NetDriver
Console
Engine
Now how can you actually load the plug-in if you know the functionality
identifier and the name? There are three ways how to do this (they can also
be mixed). The first way is to add the following line in the [Plugins]
section in your config file: `FuncID = PluginName'. This is
used in most cases.
The second way is to call
SystemDriver->RequestPlugin("PluginName:id")
.
You must do this before calling YourSysDriver->Initialize()
. If you
use this method, you may also load the plug-in without a functionality
identifier, but then you will only be able to use it if you search through
all identifiers for an interface. You can do this with
SystemDriver->RequestPlugin("PluginName")
.
This way the name of the plug-in is just hardcoded, not stored in a config
file. This can be used if you know that you will always need these plug-ins.
At the time you call SystemDriver->Initialize()
, all requested plugins
are loaded. As said before, there are dependencies between the plug-ins, i.e.
some plug-ins require others to work. Because of this, plug-ins are sorted
in the correct order before they are loaded, so that if a plug-in is
required by another, the first one is loaded first, no matter whether it
appears first in the config file (or in the first RequestPlugin
line).
Note that this function does not work correctly yet, because it
requires all plug-ins to correctly say which plug-ins they rely on, which only
a few plug-ins do at this time.
The third possibility to load a plug-in is to use CS_LOAD_PLUGIN()
.
This can also be used after the system was initialized. The disadvantage is
that a plug-in loaded this way is not included in the sorting process
described above. So you must make sure that all plug-ins required by the one
you want to load are already loaded at the time you use this. Also, if you
use this function there is no need to query the plug-in anymore, as
CS_LOAD_PLUGIN()
directly returns the plug-in interface.
This is the last step before you can use the plug-in. It means that inside
your program you ask the system driver for the plug-in interface:
CS_QUERY_PLUGIN_ID(SystemDriver,FuncID,iInterface)
.
This macro returns the pointer or NULL if the interface is not found. If you
want to search through all identifiers for an interface, you can use
CS_QUERY_PLUGIN(YourSysDriver,iSomething)
.
To sum it up, SCF is mainly used to provide common interfaces for DLLs, but it can also be used by statically linked libraries. If you want to know how to write a plug-in yourself, you should read the complete SCF documentation. See section 6.4 Shared Class Facility (SCF).
For further information about modules and plug-in drivers, see the sections on Libraries (see section 6. Libraries) and plug-in modules (see section 7. Plug-In Modules and Drivers).
Now that you have learned some basics about the Crystal Space environment, you can try writing your first program.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |