[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ] [ Search: ]

C.7 Release Notes from 0.96 to 0.98

This section documents the major changes between versions 0.96 and 0.98 of Crystal Space.

Lighting Changes

The following methods have been removed:

Instead there is a new function iMeshWrapper::SetLightingUpdate() with which you can exhibit the same control. The big difference is that you don't have to call this function every time a light or object moves. This is all updated automatically now.

Also removed the CS_NLIGHT_ flags. iEngine::GetNearbyLights() has been modified so that the flags parameter is no longer accepted.

Bezier Curve Changes

The notion of curve templates has been reworked. ‘iCurveTemplate’ has been removed and it's functionality has moved to ‘iCurve’ itself. As a consequence of this change the bezier addon loader has been removed. Also curves have been separated from the thing plugin and now live in the ‘bezier’ plugin. So, a curve like this in the past:

    <v>0</v> <v>1</v> <v>2</v>
    <v>3</v> <v>4</v> <v>5</v>
    <v>6</v> <v>7</v> <v>8</v>
<meshfact name="tunnel">
    <curvecenter x="0" y="0" z="0" />
    <curvecontrol x="5" y="2" z="0" u="1" v="0" />
    <curve name="bez">b1</curve>

should now become (note the new plugin line):

<meshfact name="tunnel">
    <curvecenter x="0" y="0" z="0" />
    <curvecontrol x="5" y="2" z="0" u="1" v="0" />
    <curve name="b1">
      <v>0</v> <v>1</v> <v>2</v>
      <v>3</v> <v>4</v> <v>5</v>
      <v>6</v> <v>7</v> <v>8</v>

On the API side nothing much changes except that when you use the bezier mesh plugin you need to use ‘iBezierState’ and ‘iBezierFactoryState’.

The ‘iThingEnvironment’ no longer has code to maintain bezier curve templates.

iThingState and iPolygon3D Changes

The interface ‘iThingState’ no longer inherits from ‘iObject’, thus the QueryObject() function has been removed.

‘iThingState’ has been split into ‘iThingState’ and ‘iThingFactoryState’. A thing mesh no longer implements both a mesh object and a mesh object factory. Instead things now work like usual mesh objects where iMeshObjectType::NewFactory() creates a factory that implements ‘iThingFactoryState’ and iMeshObjectFactory::NewInstance() creates an instance that implements ‘iThingState’. The engine convenience functions to create a thing mesh will automatically create both the factory and mesh so you don't have to worry about that. You can use iThingState::GetFactory() from a thing mesh object to get the ‘iThingFactoryState’ so you can build the polygons. Note that SCF_QUERY_INTERFACE() of ‘iThingFactoryState’ no longer works on a thing mesh object. You must use GetFactory().

iThingState::GetVertexC() has been removed. It is no longer possible to get camera space information from a thing.

The method iThingFactoryState::GetFlags() has been removed. Use the new method iMeshObjectFactory::GetFlags() instead.

The interface ‘iPolygon3D’ is completely removed. The ‘iThingFactoryState’ now contains a lot of new functions to create and manipulate polygons. Here is an example:

First the old code:

  iPolygon3D* p;
  p = state->CreatePolygon ("First one");
  p->CreateVertex (csVector3 (...));
  p->CreateVertex (csVector3 (...));
  p->CreateVertex (csVector3 (...));
  p->CreateVertex (csVector3 (...));
  p->SetMaterial (mat);
  p = state->CreatePolygon ("Second one");
  p->CreateVertex (csVector3 (...));
  p->CreateVertex (csVector3 (...));
  p->CreateVertex (csVector3 (...));
  p->CreateVertex (csVector3 (...));
  p->SetMaterial (mat);

Here is the new code:

  state->AddQuad (
    csVector3 (...),
    csVector3 (...),
    csVector3 (...),
    csVector3 (...));
  state->SetPolygonName (CS_POLYRANGE_LAST, "First one");
  state->AddQuad (
    csVector3 (...),
    csVector3 (...),
    csVector3 (...),
    csVector3 (...));
  state->SetPolygonName (CS_POLYRANGE_LAST, "Second one");
  state->SetPolygonMaterial (CS_POLYRANGE_ALL, mat);

If you want to add a box that can be seen from the inside (typically a room) then you can do it like this:

  state->AddInsideBox (csVector3 (-5, 0, -5), csVector3 (5, 20, 5));
  state->SetPolygonMaterial (CS_POLYRANGE_LAST, mat);
  state->SetPolygonTextureMapping (CS_POLYRANGE_LAST, 3);

Light Changes

The interfaces ‘iStatLight’ and ‘iDynLight’ have been removed and their functionality has been merged into ‘iLight’ (this includes the function iDynLight::Setup(). There is now a new function iLight::GetDynamicType() which returns one of the following constants depending on the type of the light:

The method iLight::IsDynamic() was removed; use iLight::GetDynamicType() to get the same information.

The following functions have been modified to work with ‘iLight’ instead of ‘iStatLight’ and ‘iDynLight’:

The last parameter of iEngine::CreateLight() routine has changed to be the dynamic type of the light (one of the above ‘CS_LIGHT_DYNAMICTYPE’ constants) instead of a boolean. Thus, the iEngine::CreateDynLight() method has been superceded by this function.

All functions related to dynamic lights have been removed. Dynamic lights are now treated exactly like static lights. For example, you have to call iSector::GetLights() and then Add() to add the light to the sector. The iLight::Setup() function remains, however, and is specific to dynamic lights.

Polygon Texture Mapping Changes

The following interfaces have been removed: ‘iPolyTexNone’, ‘iPolyTexGouraud’, ‘iPolyTexFlat’, and ‘iPolyTexLightMap’. The combined API from ‘iPolyTexNone’ and the ‘iPolyTexLightMap’ have moved to ‘iThingFactoryState’.

It is no longer possible to set mixmode and alpha for individual polygons Instead use the new iThingState::SetMixMode() function to set the mixmode globally. If needed you will have to separate the transparent polygon in a separate thing mesh.

It is also no longer possible to get camera space information from polygons.

The <shading> keyword in polygons in map files now accepts a boolean value instead of ‘NONE’, ‘FLAT’, ‘GOURAUD’, or ‘LIGHTMAP’. With this you can enable/disable lightmapping.

If you want gouraud shaded polygons you should use the ‘genmesh’ mesh object plugin instead.

Polygon Planes

Polygon planes are removed. So it is no longer possible to use the plane addon loader to define texture mapping for a polygon outside of the polygon itself. To fix this you must specify all texture mapping information in the ‘texmap’ statement of the polygon. ‘map2cs’ now correctly outputs polygons like this (no longer outputs planes) and ‘levtool’ can convert old style maps to new format like this:

levtool -planes yourmap.zip

‘iThingEnvironment’ no longer has code to maintain polygon texture mapping planes.

The plane loader and saver addons have been removed from the thing loader plugin.

Removed ‘iPolyTxtPlane’ interface.

Portal Changes

Several methods in ‘iPortal’ have changed slightly. For example, SetMirror() now expects a plane instead of a polygon.

In the map loader the syntax for specifying warp vectors for portals has changed from ‘v’ to ‘wv’ and ‘w’ to ‘ww’.

Portals in thing have been removed completely! Instead there is now a separate portal container object which belongs in the engine (otherwise it is just a mesh like any other mesh). To create a portal you can use the convenience functions iEngine::CreatePortal() and iEngine::CreatePortalContainer(). Here is an example of old code and how to change it to new code:

  ... create thing in sourceSector ...
  iPolygon3D* p = state->CreatePolygon ();
  p->CreateVertex (csVector3 (...));
  p->CreateVertex (csVector3 (...));
  p->CreateVertex (csVector3 (...));
  p->CreateVertex (csVector3 (...));
  iPortal* portal = p->CreatePortal (destSector);

New code:

  csPoly3D poly;
  poly.AddVertex (csVector3 (...));
  poly.AddVertex (csVector3 (...));
  poly.AddVertex (csVector3 (...));
  poly.AddVertex (csVector3 (...));
  iPortal* portal;
  csRef<iMeshWrapper> portal_mesh = engine->CreatePortal (
        "my_portal", sourceSector, csVector3 (0, 0, 0),
        destSector, poly.GetVertices (), poly.GetVertexCount (),

In map files the old way to create portals in a polygon is still supported. Internally it will also create a portal container (as a child mesh of the thing it is in). The new way to create portals is by using the new ‘portals’ and ‘portal’ keywords in sectors or as children of other meshes.

Datatype Changes

The ‘csSome’, ‘csConstSome’ and ‘uint’ types have been removed. Use ‘void*’, ‘const void*’ and ‘unsigned int’ instead.

Procedural Textures

Instead of only built-in types, procedural textures have been moved into plugins. That means that the <type> token now works the same way as the <plugin> token for meshes. That is, either specify a full class ID (e.g. ‘crystalspace.proctex.loader.fire’), or a shortcut specified in the <plugins> token of the world. To simulate the old <type> behaviour, paste the following lines into a map's <plugin> section:

<plugin name="dots">crystalspace.texture.loader.dots</plugin>
<plugin name="fire">crystalspace.texture.loader.fire</plugin>
<plugin name="water">crystalspace.texture.loader.water</plugin>
<plugin name="plasma">crystalspace.texture.loader.plasma</plugin>

This requires that you move the <plugin> block in front of the <textures> block, as otherwise the shortcuts won't be recognised.

The behaviour of procedural textures also differs as a material of the same name was created along with the texture; this isn't the case any more, you have to create a material which uses the procedural texture manually.

In addition, the loader now doesn't distinguish between normal and procedural textures any more, both are loaded with the same <texture> block. The syntax recognized is a combination of both old <texture> and <proctex> tokens—so renaming all <proctex> to <texture> tags is sufficient to convert a level (apart from fixing the <type> tokens.)


Region handling has changed considerably. The engine no longer has the concept of a current region. The engine still manages all regions though. Objects also no longer register themselves to the current region. It is the responsability of the object creator to do that. The standard loader has support for adding objects to a region now.

The following functions have been removed from ‘iEngine’:

There is one new function iEngine::CreateRegion() which will create a new region.

In ‘iLoader’ the functions LoadMapFile() and ThreadedLoadMapFile() now expect an optional pointer to a region in addition with a boolean to restrict searching to that region.

In addition to all these changes the <region> keyword in map files is no longer supported.

Mesh Objects / Factories and iPolygonMesh

Some mesh factories used to support the querying of the ‘iPolygonMesh’ interface (e.g. Thing, Sprite3D, Genmesh). Due to the way this was implemented internally this caused leaks. So, querying ‘iPolygonMesh’ directly from the object is now discouraged. Although it may still work in some cases, this feature may be dropped without further notice and may not work in all cases.

Similar for mesh objects. Querying ‘iPolygonMesh’ is discouraged here, as well.

Instead, if you want the polygon mesh from an object, use the GetObjectModel() method from the ‘iMeshObject’ interface; respectively query for the ‘iObjectModel’ interface in case of a factory, and utilize one of the GetPolygonMeshXXX() methods, depending on what you need (the old interface query returned the collision detection mesh.) This has also the advantages that you can get different meshes for different purposes (currently, visibility culling and collision detection in addition the the base mesh), and that those meshes can be overridden by user-defined meshes (e.g. if the collision detection mesh needs to have a shape different from the visible mesh.)

In ‘iPolygonMesh’ the IsDeformable() method has been replaced with the more general GetFlags(). In addition to that Cleanup() is removed and instead Lock() and Unlock() are added.

‘iPolygonMesh’ now has GetTriangles() and GetTriangleCount(). Check out the API docs for implementation details.

Iterator Changes

Changed the following iterators to conform to the following iterator standard:

bool iterator->HasNext() // returns true if there are more items.
T* iterator->Next()      // returns next element or 0.
void Reset()             // resets iterator (not all implement this).

AWS Changes

iAWS::CreateCustomCanvas() was removed. To set up AWS with a canvas, use iAWS::SetupCanvas() instead.

iAWS::CreateTransition() and iAWS::CreateTransitionEx() have been changed to take a delay parameter specified in ‘csTicks’ instead of using a step_size parameter. This change ensures that transitions take the same amount of time on computers having different processor speeds. iAWS::ComponentIsInTransition() has been added to allow users to query if a specified ‘iAwsComponent’ is in the midst of a transition.

Array Changes

All arrays have been modified to inherit from ‘csArray’. This means that there are some slight API changes because some methods were not consistent with ‘csArray’.

‘csGrowingArray’ has been renamed to ‘csDirtyAccessArray’ in order to better reflect its intention, which is that it publishes a method allowing the client to obtain access to the raw memory block containing the items in contiguous memory. This potentially unsafe operation is not available in the other array templates. Use this class only in very special-purpose cases where you need to construct a list of objects dynamically and then pass the address of the raw memory containing those objects to some foreign function which does not understand csArray<>.

csPArray Removed

‘csPArray’ has been removed in favor of the csArray<> template. So, use csArray<T*> instead of csPArray<T>.

csStrVector and iStrVector Removed

‘csStrVector’ and ‘iStrVector’ have been removed in favor of ‘csStringArray’ and ‘iStringArray’. This means that functions like iVFS::MountRoot() and iVFS::FindFiles() now return an ‘iStringArray’.

csVector and csBasicVector Removed

‘csVector’ and ‘csBasicVector’ have been removed. Instead you should use one of the templated arrays: csArray<>, csPDelArray<>, or csRefArray<>.

If you were using a regular ‘csVector’ then this can usually be replaced directly by csArray<type*> with ‘type’ the type you were storing in the vector.

If you subclassed from ‘csVector’ in order to override FreeItem() then you have to decide what to do depending on the code in FreeItem(). If that code performed a ‘delete’ then you can use csPDelArray<>. If that code performed a DecRef() then you can use csRefArray<> but you still have to be careful because csRefArray<> will automatically invoke IncRef() for objects that are pushed on the array. In other cases you probably need to handle deletion manually.

‘csVector’ also allowed you to override Compare() and CompareKey(), and these methods were utilized automatically by QuickSort(), FindSortedKey(), InsertSorted(), and FindKey(). csArray<>, on the other hand, accepts a comparison function as an argument to its sorting and searching methods, thus providing much greater flexibility than ‘csVector’. You can transform your csVector-based code as follows. First, the old code:

class MyVector : public csVector
  virtual ~MyVector ()
    DeleteAll ();
  virtual void FreeItem (void* item)
    delete (MyType*)item;
  virtual int Compare (void* i1, void* i2, int mode) const
  virtual int CompareKey (void* i, const void* key, int mode) const

This can be transformed roughly to the following class:

class MyVector : public csPDelArray<MyType>
  static int Compare (MyType const& i1, MyType const& i2)
  static int CompareKey (MyType const& i, MyKey const& key)

The methods csArray<>::Sort() and csArray<>::InsertSorted() accept an optional comparison function (MyVector::Compare(), in the example). To use a custom comparison function, invoke these functions like this:

MyVector v = ...;
MyType o = ...;
v.InsertSorted(o, MyVector::Compare);

If you do not provide a comparison function, then the array elements are compared against each other with T::operator<(T) where ‘T’ is the type of the element contained in the array. This default comparison semantic works nicely for all built-in types, as well as for complex types which define a ‘<’ operator.

The methods csArray<>::FindKey() and csArray<>::FindSortedKey() accept a comparison functor which is constructed from the key for which you are searching and an optional comparison function (MyVector::CompareKey(), in the example). Invoke these functions like this:

int r;
MyKey key = ...;
MyVector v = ...;
r = v.FindKey(csArrayCmp<MyType,MyKey>(key, MyVector::CompareKey));
r = v.FindSortedKey(
              csArrayCmp<MyType,MyKey>(key, MyVector::CompareKey));

In this example, the key's type is ‘MyKey’ (the type expected by MyVector::CompareKey()). It is possible and often convenient for ‘MyType’ and ‘MyKey’ to be the same type (for instance, you might search for an integer in an array of integers). When the types of ‘MyType’ and ‘MyKey’ differ, ‘MyType’ is often a composite type, and ‘MyKey’ is the type of one attribute of ‘MyType’, however you are not in any way limited to this arrangements. If you do not provide a comparison function when constructing the functor, then the elements are compared with the search key via T::operator<(K) and K::operator<(T) where ‘T’ and ‘K’ are the type of the contained element and the type of the key, respectively. This default comparison semantic is especially useful when the contained elements and key have the same type, provided that the type supports a useful ‘<’ operator. For example, if you have an array of integers which you want to sort and then search, you can use the default comparison semantics, rather than writing a custom comparison function, as shown here:

csArray<int> v = ...;
int r = v.FindSortedKey(csArrayCmp<int,int>(33));

Hash and Set Templates

The new template classes csHash<> and csSet<> are available for use by new code; and older code will be upgraded over time to use these template classes. The older ‘csHashMap’ and ‘csHashSet’ classes are now deprecated.

csHashMap Changes

The deprecated ‘csHashIterator’ can no longer iterate over all objects. To do that you need to use ‘csGlobalHashIterator’.

Event-Related Changes

Signatures of the iEvent::Find() methods have been unified so that they all now accept a reference into which to store the found item. Previously, some Find() methods used a reference for this purpose, while others used a pointer. In addition, rather than accepting a pointer to a pointer of type ‘iEvent’, the Find() method for finding an event now takes a reference to a csRef<iEvent>.

Previously, iEventQueue::Post() had a special case where it assumed that the caller was giving up ownership of the posted ‘iEvent’ if the caller had allocated the event manually, rather than obtaining the event via iEventQueue::CreateEvent() or iEventOutlet::CreateEvent(). In this case, Post() would hijack the caller's reference to the ‘iEvent’ for itself. This special case allowed the caller to write code, such as the following, which appeared to leak a reference to the event, but which did not in fact do so.

iEvent* e = new csEvent(...);

Now, however, iEventQueue::Post() takes the more sane approach of treating all incoming events identically. It never hijacks ownership of the event by stealing the caller's reference. Instead, Post() uniformly invokes IncRef() on each incoming event in order to claim its own reference. This means that callers are no longer burdened by having to determine whether or not the caller's reference is going to be hijacked. Thus, the above code should now be re-written as:

csRef<iEvent> e;
e.AttachNew(new csEvent(...));

Events created with iEventQueue::CreateEvent() or iEventOutlet::CreateEvent() are unaffected by this change since the client has never had to worry about Post() hijacking the reference in these cases.

New Renderer-Related Changes

The mesh object API has changed considerably, thus we recommend looking at the documentation on that to see how to modify your own mesh object for the new API. In this section we describe API modifications that are relevant for user applications.

For ‘iLight’: SetRadius(), GetRadius(), and GetSquaredRadius() have been replaced with SetInfluenceRadius(), GetInfluenceRadius(), and GetInfluenceRadiusSq().

For ‘iMaterialEngine’ and related: GetTextureWrapper() for layers now expects a ‘csStringID’ instead of an integer.

SCF Changes

Written by Eric Sunshine, sunshine@sunshineco.com.

The monolithic and inflexible plugin-registry database, ‘scf.cfg’, has been eliminated. Instead, plugin modules are now self-describing via a meta-information resource associated with each module. It is possible to access this meta-information without actually loading the plugin, thus avoiding a time-consuming and costly operation.

A plugin's meta-information is now stored in an XML-format file rather than being hard-coded via the plugin's C++ code. The meta-information file is named after the associated plugin module, except with filename extension ‘.csplugin’. For instance, the meta-information for the ‘vfs.so’ (or ‘vfs.dll’) plugin will be named ‘vfs.csplugin’.

Since the meta-information is now maintained via an external resource, the following SCF macros, which were used to export this information from the C++ code, have been removed:

To deal with this change in your own code, simply remove the entire SCF_EXPORT_CLASS_TABLE() block from the C++ code which implements the plugin module.

Creation of the meta-information resource file involves a straight-forward translation of the information from the obsolete SCF macros into a structured XML-format file. For example:

    "My first custom foo class")
    "My second custom foo class",

This table exports two C++ classes, ‘MyClass1’ and ‘MyClass2’ under the SCF class names ‘myproj.myplugin.foo1’ and ‘myproj.myplugin.foo2’, respectively. Furthermore, the second exported class has a dependency upon two other SCF classes, ‘myproj.myplugin.bar1’ and ‘myproj.myplugin.bar2’. To convert this to an XML-format meta-information resource, just copy the above values into the appropriate XML nodes. For instance:

<?xml version="1.0"?>
<!-- myplugin.csplugin -->
        <description>My first custom foo class</description>
        <description>My second custom foo class</description>

The top-level node of a meta-information file is named <plugin>. All SCF-related information is contained within an <scf> child node. Plugin modules can export multiple named SCF classes. Each exported class is represented by a <class> node within the <classes> group. The <name> node of a <class> is the class' SCF name. The <implementation> node references the C++ class which actually implements the named SCF class. This is the same name that is provided as an argument to the SCF_IMPLEMENT_FACTORY() macro. When an SCF class depends upon other SCF classes, the dependencies are indicated via the optional <requires> group, which contains one <class> node per dependency.

Meta-information in the ‘.csplugin’ file is extensible; it is not restricted to SCF-only usage. Plugin authors can choose to publish supplementary information about plugins in addition to the SCF information already published. As a hypothetical example, image loading plugins might desire to publish image indentification information which would allow the image loading multiplexor to selectively request image loading plugins on-demand, rather than requesting all plugins unconditionally, even if they are not needed. Here is a possible meta-information table for a PNG image loader (with the <scf> node collapsed to ... for the sake of illustration):

<?xml version="1.0"?>
<!-- cspngimg.csplugin -->
        <scan length="4" bytes="\0x89PNG"/>

In this example, the PNG loader meta-information tells the multiplexor several different ways to identify a PNG image:

If the hypothetical multiplexor identifies the image as PNG, only then will it actually request the PNG loader plugin.

If you know the physical pathname of a plugin module, then you can retrieve its meta-information via csGetPluginMetadata() (‘csutil/csshlib.h’), which returns an ‘iDocument’. Alternately, if you know the name of an SCF class which a plugin exports, then you can retrieve the plugin's meta-information, by calling iSCF::GetPluginMetadata().

At program launch time, SCF discovers plugins automatically by searching a set of directories, and creates an internal database associating available SCF class names with the plugin modules which implement them. The directories which SCF searches by default are:

If you would like SCF to scan additional directories, you can invoke either of these two functions (multiple times, if necessary):

Finally, if you need to manually register a single plugin module with SCF for which you know the native pathname (not a VFS pathname), you can invoke iSCF::RegisterPlugin().

For those relatively rare cases when a named SCF class is built directly into an application, rather than being implemented via a plugin, the class must be registered with SCF manually since SCF will not otherwise be able to discover it automatically as is the case with named classes exported from plugin modules. Manual registration is accomplished with the SCF_REGISTER_STATIC_CLASS() macro. This macro existed previously, but accepted a different set of arguments. The new arguments are:

  "comma-separated dependency list" or NULL)

Invoke this macro in one of the source files of your application; usually in the source file which implements the class. This macro should be invoked at file scope (that is, not from inside a function). For instance:

  "My second custom foo class",

If you used to use SCF_REGISTER_STATIC_LIBRARY() or SCF_REGISTER_STATIC_CLASS_DEP() for this same purpose, you must now instead use SCF_REGISTER_STATIC_CLASS(). (A macro named SCF_REGISTER_STATIC_LIBRARY() still exists, but it has a completely different purpose than the original version, and is typically only used by very low-level mechanisms, rather than by end-users. See ‘CS/include/csutil/scf.h’ for the gory details, if you are curious.)

The Crystal Space ‘configure’ script option ‘--enable-meta-info-embedding’ controls whether or not the build system embeds the plugin meta-information into plugin modules (if supported by the platform), and whether or not the plugin loader looks for embedded meta-information. If this option is disabled, or if embedding is not supported by the platform, then the meta-information is laid down alongside the built plugin module (‘.so’ or ‘.dll’) in a text file with the same name as the plugin module, except with extension ‘.csplugin’. The meta-information embedding option is enabled by default for most platforms, but is disabled by default for Unix platforms.

Warning: Meta-information embedding on Unix is accomplished via the ‘libbfd’ library which carries a GPL license. This license is incompatible with Crystal Space's LGPL license. Unlike the LGPL which is compatible with closed-source projects, the GPL license is not. For this reason, embedding is disabled by default on Unix, and must be enabled explicitly with the ‘configure’ script's ‘--enable-meta-info-embedding’ option. Enable this option on Unix only if you are certain that your project is compatible with the GPL license.

The platform-specific plugin loaders are capable of reading embedded plugin meta-information, as well as meta-information in stand-alone ‘.csplugin’ files. Even when configured for embedding, the plugin loaders will still be able to recognize and utilize external ‘.csplugin’ resources. This means that Crystal Space-based projects with unsophisticated build systems, which are incapable of embedding meta-information into the plugin module, can still create usable plugins simply by placing a copy of the ‘.csplugin’ file alongside the plugin executable (‘.so’ or ‘.dll’).

External projects which are based upon Crystal Space's Jam build system (‘CS/mk/jam’) inherit, for free, the capability of embedding meta-information within plugin modules (if supported by the platform). Simply grab the newer ‘.jam’ files from ‘CS/mk/jam’, and add a few definitions to the project's ‘Jamconfig’ file. The exact set of definitions is platform-specific, so consult the appropriate Jam file (‘unix.jam’, ‘win32.jam’, or ‘macosx.jam’) to determine precisely which which definitions are required. Here is a list of definitions required at the time of writing:


CMD.OBJCOPY = objcopy



MacOS/X (not yet supported)


The ‘scfreg’ tool whose job was to manipulate the monolithic SCF plugin-registry database, ‘scf.cfg’, has been eliminated since it is no longer required.

Other SCF Changes

The macros SCF_DESTRUCT_IBASE() and SCF_DESTRUCT_EMBEDDED_IBASE() have been introduced. Just as a destructor reverses initialization performed by a constructor, these new macros reverse the initialization performed by the corresponding SCF_CONSTRUCT_IBASE() and SCF_CONSTRUCT_EMBEDDED_IBASE() macros. Typically, you should invoke these macros within your class' destructor, just as you invoke the corresponding SCF construction macros in your class' constructor.

The SCF_CREATE_INSTANCE() macro now returns csPtr<> to be consistent with the other SCF instantiation and query macros. This means that you should now assign the result of SCF_CREATE_INSTANCE() to a csRef<>.

The method iSCF::QueryClassList(), which used to return a raw ‘iStringArray*’ that the client had to dispose of manually, now returns a csRef<iStringArray>.

The macros SCF_SET_REF(), SCF_INC_REF(), and SCF_DEC_REF() have been removed. Instead you should use csRef<>.

The method iSCF::GetInterfaceName(scfInterfaceID) has been added to complement the existing iSCF::GetInterfaceID().

The query template class scfInterface<> has been introduced. This class provides a means to query static information about SCF interfaces, such as an interface's version number. Presently, this class allows access to the following static information:

Here is an example illustrating how to retrieve various pieces of information about an interface.

int ver = scfInterface<iFoo>::GetVersion();
scfInterfaceID ident = scfInterface<iFoo>::GetID();
char const* name = scfInterface<iFoo>::GetName();

An important benefit of the new scfInterface<> template class is that it is now possible for template authors to perform queries for interface-related information. For example, the author of a new template class may know an SCF interface via only the opaque identifier T, yet it is still possible to query T's version using scfInterface<T>::GetVersion().

The old hidden, semi-private API for obtaining interface information via the global ‘name_VERSION’ constant and global name_scfGetID() function has been removed. Conversion from the old API to the new is straight forward. For example:

A consequence of this change is that it is no longer possible to invoke the SCF_VERSION() macro inside an alternate namespace. If previously you were invoking this macro within your project's own namespace, you must relocate the invocation so that it appears outside the namespace. For example:

#include <csutil/scf.h>

namespace MyProject
  struct iMyInterface : public iBase

SCF_VERSION(MyProject::iMyInterface, 1, 0, 0);

csSys Library Removed

The ‘cssys’ library has been merged into the ‘csutil’ library. This eliminates many problems resulting from the large number of circular dependencies which existed between these two libraries. From the client viewpoint, this change manifests in two ways:

csEngine Library Removed

The ‘csengine’ library has been removed. Direct use of this library has long been deprecated, so its removal will probably go unnoticed by most or all projects. Any projects which were using this library must now instead interact with the 3D engine via the ‘engine’ plugin and the SCF interfaces in the ‘CS/include/iengine’ directory.

csAppFrame Library Removed

The ‘csappframe’ library has been merged into the ‘cstool’ library. Furthermore, the main() function, which client application's got for free when linking against this library, has been removed since it proved to be an obstacle to making Crystal Space libraries available as shared libraries. Instead, you must now provide your own main() function, like this:

// main.cpp

class MyApp : public csApplicationFramework
  /* ... */

int main (int argc, char* argv[]) 
  MyApp myApp;
  return myApp.Main (argc, argv);

csString and iString Changes

The csString::strlwr() method was renamed to Downcase(). A complementary Upcase() method was added for completeness. These methods were also added to ‘iString’. A Slice() method was added to ‘iString’ and ‘csString’ which copies a sub-portion of a string. Slice() is similar to the existing SubString() method, but follows a more natural calling convention. Several ‘iString’ methods which were incorrectly returning raw ‘iString*’ or ‘iString&’ now correctly return csRef<iString>.

‘csString’ now makes a distinction between a null-string (think ‘(char const*)0’), and a zero-length string (think ""). Client code can check for these conditions in the normal fashion by invoking csString::GetData() or operator char const*() to retrieve a C-string representation of the string. If the result is zero, then it is a null-string. If the result is non-zero but csString::Length() returns zero, then it is a zero-length string. The new convenience method csString::GetDataSafe() will always return a valid C-string, even if the underlying ‘csString’ represents a null-string (in which case a zero-length C-string "" will be returned). This means that the result of GetDataSafe() can be used directly without performing a null check.

In previous releases, ‘csString’ would also sometimes return a null pointer or a zero-length string from csString::GetData() or operator char const*(), but there was no controlled way to know when one or the other would be returned since ‘csString’ was doing so haphazardly. Worse, several of the ‘csString’ methods failed to check the underlying representation, and would crash if the string's state was not what the methods expected. Now, however, ‘csString’ itself makes a proper distinction between these cases, and the user has direct (and documented) control over when a ‘csString’ will represent a null-string or a zero-length string.

csMD5 Changes

A csMD5(csString const&) constructor was added to complement the existing csMD5(char const*) constructor. The new method csMD5::Digest:HexString() returns a hexadecimal string representation of the MD5 digest using lowercase hexadecimal characters. csMD5::Digest::HEXString() returns uppercase hexadecimal characters.

Beam Intersection Changes

IntersectSegment() for ‘iThingFactoryState’ and ‘iThingState’ has been removed. Use iMeshObject::HitBeamObject() instead.

iMeshObject::HitBeamObject() now has an optional polygon index parameter so you can get the index of the polygon that was hit.

iSector::HitBeam() which doesn't support portals now returns a polygon index instead of a polygon pointer.

iSector::HitBeam() that supports portals has been renamed to HitBeamPortals(). It additionally returns a polygon index. Same for iPortal::HitBeam() which has also been renamed to HitBeamPortals().

iCamera::GetHit() has been removed. Instead, use the completely equivalent function iSector::HitBeamPortals().

iVisibilityCuller::IntersectSegment() now returns a polygon index instead of a portal.

Directory and Path Queries

The following low-level functions, declared in ‘csutil/syspath.h’ can be used to query various directories and paths. Each of these functions expects to be passed argv[0] obtained from the program's main() function.


Returns the absolute path of the application executable file.


Returns the absolute path of the directory containing the application executable file; or the path of the directory containing the Cocoa application wrapper on MacOS/X for GUI applications.


Returns the absolute path of the directory in which the application's resources reside. On many platforms, resources, such as configuration files, data files, etc., reside in the same directory as the application itself. On such platforms, this function will typically return the same directory as csGetAppDir(). On MacOS/X, however, for GUI applications, this function will return the ‘Resources’ directory within the Cocoa application wrapper.

Since it is rarely convenient to squirrel-away argv[0], the following higher-level methods, declared in ‘iutil/cmdline.h’, are also available for obtaining the same information once the application has been initialized. These methods do not require access to argv[0]. You can obtain a handle to the shared ‘iCommandLineParser’ from the object registry, ‘iObjectRegistry’, which is declared in ‘iutil/objreg.h’.

VFS Changes

VFS (see section Virtual File System (VFS)) now understands two new pseudo-variables, ‘$*’ and ‘$^’, in its configuration file, ‘vfs.cfg’, and during programmatic mounts. The full list of pseudo-variables is:


Platform-specific path delimiter (‘/’ on Unix and MacOS/X; ‘\’ on Windows).


Crystal Space installation directory; same as csGetConfigPath().


Application resource directory; same as csGetResourceDir().


Directory where application resides, or directory containing Cocoa application wrapper on MacOS/X; same as csGetAppDir().

The expansions of the ‘$@’, ‘$*’, and ‘$^’ variables always contain a trailing path delimiter.

At initialization time, VFS now searches for its configuration file, ‘vfs.cfg’, in the application resource directory (csGetResourceDir()), then in the directory containing the application (csGetAppDir()), and finally in the Crystal Space installation directory (csGetConfigPath()). The first ‘vfs.cfg’ file found during this search is the one used to initialize the facility. In the past, VFS searched for ‘vfs.cfg’ only in the Crystal Space installation directory.

The VFS volume ‘/temp’ has been renamed to ‘/varia’ in the default ‘vfs.cfg’ file in order to avoid confusion with the like-named VFS volume ‘/tmp’, which represents user-writable temporary storage. The ‘/varia’ volume (née ‘/temp’), on the other hand, resides within ‘${prefix}/etc/crystal’ (where ‘${prefix}’ is the installation location), and this location usually is not writable by the typical user. External projects may, of course, still create any VFS volume desired, including ‘/temp’, by supplying a project-specific ‘vfs.cfg’.


Some work to improve internationalization support in Crystal Space has been performed. Crystal Space now has more complete support for Unicode input and output.

Unicode Helpers

The ‘csUnicodeTransform’ class provides functions to deal with and convert between UTF-8, UTF-16, and UTF-32 encoded strings.

UTF-8 strings

Several portions of the CrystalSpace API now accept UTF-8-encoded strings. These include:

Keyboard Events

Keyboard events have changed. The event data related to keyboard events is no longer stored in a structure inside ‘iEvent’, but instead now resides within ‘iEvent’'s property bag. That means the way to access the event data has changed slightly; you now either query for the data via iEvent::Find() to get a specific property, or you access it via the the ‘csKeyEventHelper’ class.

Another change is that the ‘csevKeyDown’ and ‘csevKeyUp’ events have been collapsed to a single ‘csevKeyboard’ event; the up/down information is transported along with the event data.

The notion of scan code and characters has been replaced with raw and cooked codes. Basically, the raw code identifies the key uniquely, while the cooked code is a processed version of it. An important aspect of this is that both the raw and cooked codes are Unicode characters. Special keys (such as function keys) are encoded as characters from a Unicode private use area. Almost all event handling code should consult the raw code because the raw code is invariant; that is, an a is an `a' even if the ALT or SHIFT key, or both, are depressed. This is useful for games, for instance, which need to map keys to particular actions. Cooked codes are useful typically only for text input, such as within a text input field.

You can find additional information in Crystal Space's public API documentation in the Event Handling and Keyboard Events sections.

To understand how to adjust your code for these changes, see the following examples.

iEvent& e = ...;
if (e.Type == csevKeyDown && e.Key.Code == CSKEY_ESC)

Change to:

if (e.Type == csevKeyboard &&
    csKeyEventHelper::GetEventType(&e) == csKeyEventTypeDown) &&
    csKeyEventHelper::GetCookedCode(&e) == CSKEY_ESC))

Another example:

iEvent& e = ...;
switch (e.Type)
  case csevKeyDown:
  case csevKeyUp:

Change to:

switch (e.Type)
  case csevKeyboard:
    if (csKeyEventHelper::GetEventType(&e) == csKeyEventTypeDown)

iGraphics2D::WriteBaseline() Deprecated

The functionality provided by WriteBaseline() is now available by specifying the ‘CS_WRITE_BASELINE’ flag when calling iGraphics2D::Write(). Hence, WriteBaseline() is superfluous and has been deprecated.

csInitializer Changes

csInitializer::RequestPlugins(csArray<csPluginRequest>) has been added to complement the existing RequestPlugins(...) which accepts a variable number of arguments. The new overload allows the list of requested plugins to be composed at run-time, whereas the older variable-argument method required the list be known at compile-time, which was not always convenient or possible.

Pollution Reduction

The following symbols have been renamed in order to avoid pollution of the global namespace.

Texture manager change

The iTextureManager::SetVerbose() function has been removed. It was not useful.

Font changes

iFont::SetSize() has been removed. Instead, the font size is supplied when the font is created with iFontServer::LoadFont(). This change has been made to fix the problem that multiple LoadFont() calls were returning the same instance of a font, and thus subsequent calls of SetSize() affected a font globally (e.g. all Arial fonts would be the same size). The new behaviour is to return only an existing instance of a font when both the font name and font size match. If there is no existing instance with the requested name and size, then a new instance is created and returned.

Font-related client code, such as the following:

csRef<iFont> font = fontserver->LoadFont ("myfont.ttf");
font->SetSize (23);
g2d->Write (font, ...);

Should be changed to:

csRef<iFont> font = fontserver->LoadFont ("myfont.ttf", 23);
g2d->Write (font, ...);

Mesh flag change

There is a new flag CS_ENTITY_INVISIBLEMESH which controls visibility (and ONLY visibility) of a mesh. There is also a new flag CS_ENTITY_NOHITBEAM which controls if calling HitBeam() will ever return that mesh. The old CS_ENTITY_INVISIBLE has been changed so that it is now a combination of both flags.

An important change is that setting the CS_ENTITY_INVISIBLEMESH flag on a parent in a hierarchy will no longer make the children invisible. You now have to set the invisible flag manually on all children in the hierarchy.

Skeletal Sprites

Support for skeletal sprites has been removed, along with the following related interfaces. Use the ‘sprcal3d’ mesh object instead. It is more flexible and works correctly. These interfaces have been retired:

In addition, the motion manager plugin has been removed, along with the following related interfaces:


Meshes that use the CS_ENTITY_CAMERA flag no longer have to be placed in a render priority that has the ‘camera’ flag set. This render priority flag is no longer required (but CS_ENTITY_CAMERA still is!).


Gouraud is much more commonly used than flat shading and is thus the default now. To achieve the opposite behaviour (ie the behaviour when CS_FX_GOURAUD was not present), use the CS_FX_FLAT flag.

Collision Detection Changes

The most important change is that OPCODE is now prefered over RAPID. The OPCODE plugin is free of restrictions (as opposed to RAPID which can't be used in commercial programs), it is faster, and uses a lot less memory. In future we will remove the RAPID plugin.

The iCollideSystem::CollidePath() utility method has been relocated to csColliderHelper::CollidePath(). In addition there are a few extra overloads that can make life easier for calculating collisions on a path.

Isometric Engine

The isometric engine has been removed. It was beyond repair. The ‘isotest’ application remains, though, and demonstrates how you can simulate an isometric look with the normal 3D engine.

Some Plugins Removed

The following plugins and programs also have been removed (along with any parts of the API they provided exclusively), because they are unmaintained and not currently usable. For more discussion see mailing list posts at:


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

This document was generated using texi2html 1.76.