Crystal Space: C.2 Release Notes from 0.96 to 0.98

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

## C.2 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:

- `iMeshWrapper::DeferUpdateLighting()`
- `iMeshWrapper::UpdateLighting()`

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:

    <addon>
      <plugin>crystalspace.mesh.loader.thing.bezier</plugin>
      <params>
        <name>b1</name>
        <material>mosaic</material>
        <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>
      </params>
    </addon>
    <meshfact name="tunnel">
      <plugin>crystalspace.mesh.loader.factory.thing</plugin>
      <params>
        <curvecenter x="0" y="0" z="0" />
        <curvescale>80</curvescale>
        <curvecontrol x="5" y="2" z="0" u="1" v="0" />
        ...
        <curve name="bez">b1</curve>
      </params>
    </meshfact>

should now become (note the new plugin line):

    <meshfact name="tunnel">
      <plugin>crystalspace.mesh.loader.factory.bezier</plugin>
      <params>
        <curvecenter x="0" y="0" z="0" />
        <curvescale>80</curvescale>
        <curvecontrol x="5" y="2" z="0" u="1" v="0" />
        ...
        <curve name="b1">
          <material>mosaic</material>
          <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>
        </curve>
      </params>
    </meshfact>

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 (),
            portal);

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.)

### Regions

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
    {
    public:
      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>
    {
    public:
      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 = ...;
    v.Sort(MyVector::Compare);
    ...
    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 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 = ...;
    v.Sort();
    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(...);
    eventq->Post(e);

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(...));
    eventq->Post(e);

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:

- `SCF_EXPORT_CLASS_TABLE()`
- `SCF_EXPORT_CLASS()`
- `SCF_EXPORT_CLASS_DEP()`
- `SCF_EXPORT_CLASS_TABLE_END`

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:

    SCF_EXPORT_CLASS_TABLE(foo)
      SCF_EXPORT_CLASS(
        MyClass1,
        "myproj.myplugin.foo1",
        "My first custom foo class")
      SCF_EXPORT_CLASS_DEP(
        MyClass2,
        "myproj.myplugin.foo2",
        "My second custom foo class",
        "myproj.myplugin.bar1,myproj.myplugin.bar2")
    SCF_EXPORT_CLASS_TABLE_END

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 -->
    <plugin>
      <scf>
        <classes>
          <class>
            <name>myproj.myplugin.foo1</name>
            <implementation>MyClass1</implementation>
            <description>My first custom foo class</description>
          </class>
          <class>
            <name>myproj.myplugin.foo2</name>
            <implementation>MyClass2</implementation>
            <description>My second custom foo class</description>
            <requires>
              <class>myproj.myplugin.bar1</class>
              <class>myproj.myplugin.bar2</class>
            </requires>
          </class>
        </classes>
      </scf>
    </plugin>

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 -->
    <plugin>
      <scf>...</scf>
      <imageloader>
        <imagetype>
          <class>crystalspace.graphic.image.io.png</class>
          <identify>
            <mimetype>image/png</mimetype>
            <extension>png</extension>
            <extension>PNG</extension>
            <scan length="4" bytes="\0x89PNG"/>
         </identify>
        </imagetype>
      </imageloader>
    </plugin>

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

- By checking MIME type, if available.
- By checking file extension, if available.
- By checking for the magic identification string ``\0x89PNG'` in the raw image data.

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:

- The application resource directory as returned by `csGetResourceDir()`. On most platforms, this is the same as the directory containing the application, however, on MacOS/X, it is the ``Resources'` directory within the Cocoa application wrapper.
- The directory containing the application (or containing the Cocoa application wrapper for MacOS/X) as returned by `csGetAppDir()`.
- The installed Crystal Space resource directories as returned by `csGetConfigPath()`. This is often the value of the ``CRYSTAL'` environment variable, or the ``CrystalSpaceRoot'` default setting on MacOS/X (which is often stored within the ``NSGlobalDomain'` domain).

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

- `scfInitialize(csPluginPaths const*)`
- `iSCF::ScanPluginsPath(char const*, bool, char const*)`

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:

    SCF_REGISTER_STATIC_CLASS(
      C++Class,
      "scf.class.name",
      "description",
      "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:

    SCF_REGISTER_STATIC_CLASS(
      MyClass2,
      "myproj.myplugin.foo2",
      "My second custom foo class",
      "myproj.myplugin.bar1,myproj.myplugin.bar2")

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:

Unix

  `EMBED_META = yes`
  `LIBBFD.AVAILABLE = yes`
  `OBJCOPY.AVAILABLE = yes`
  `CMD.OBJCOPY = objcopy`

Windows

  `EMBED_META = yes`

MacOS/X (not yet supported)

  `EMBED_META = yes`

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:

- `GetID()`--Retrieves the interface's low-level identifier as an ``scfInterfaceID'`, which is typically a small integer. SCF identifies interfaces internally by an ID for performance reasons.
- `GetName()`--Retrieves the interface's name as a null-terminated C-string.
- `GetVersion()`--Retrieves the interface's current version number.

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:

- `iFoo_VERSION` => `scfInterface<iFoo>::GetVersion()`
- `iFoo_scfGetID()` => `scfInterface<iFoo>::GetID()`

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:

- Disappearance of the static link library ``libcssys.a'` (Unix) or ``cssys.lib'` (Windows). To account for this change, simply eliminate the reference to this library from your build scripts, makefiles, or project files.
- Disappearance of the ``CS/include/cssys'` directory. Headers have been moved to the ``csutil'` include directory. To account for this change, simply change `#include <cssys/header.h>` to `#include <csutil/header.h>`.

### 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
    CS_IMPLEMENT_APPLICATION

    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.

`csGetAppPath()`

  Returns the absolute path of the application executable file.

`csGetAppDir()`

  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.

`csGetResourceDir()`

  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'`.

- `iCommandLineParser::GetAppPath()`
- `iCommandLineParser::GetAppDir()`
- `iCommandLineParser::GetResourceDir()`

### 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'`.

### Internationalization

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:

- Console
  - `csPrintf()`
  - `csPrintfV()`
  - `csFPutErr()`
- Canvas
  - `iGraphics2D::Write()`
- Reporter
  - `csReport()`
  - `csReportV()`
  - `iReporter::Report()`
  - `iReporter::ReportV()`
- Native Window
  - `iNativeWindowManager::Alert()`
  - `iNativeWindowManager::AlertV()`
  - `iNativeWindow::SetTitle()`

### 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:
        Foo();
        break;
      case csevKeyUp:
        Bar();
        break;
    }

Change to:

    switch (e.Type)
    {
      case csevKeyboard:
        if (csKeyEventHelper::GetEventType(&e) == csKeyEventTypeDown)
          Foo();
        else
          Bar();
        break;
    }

### 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.

- `STATIC_CAST()` => `CS_STATIC_CAST()`
- `DYNAMIC_CAST()` => `CS_DYNAMIC_CAST()`
- `REINTERPRET_CAST()` => `CS_REINTERPRET_CAST()`
- `CONST_CAST()` => `CS_CONST_CAST()`

### 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:

- `iSkeletonState`
- `iSkeletonLimbState`
- `iSkeletonConnection`
- `iSkeletonConnectionState`
- `iSkeletonLimb`

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

- `iMotionTemplate`
- `iMotionManager`
- `iSkeletonBone`

### Skyboxes

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!).

### CS_FX_GOURAUD removed

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:

http://sourceforge.net/mailarchive/message.php?msg_id=8645194
http://sourceforge.net/mailarchive/message.php?msg_id=8296024
http://sourceforge.net/mailarchive/message.php?msg_id=8296017
http://sourceforge.net/mailarchive/message.php?msg_id=8295883

- `terrbig` (use new terrain features in the new renderer)
- skeletal sprites (use cal3d)
- `cloth`
- `cocoa2d` graphics driver for MacOSX
- `rle` video codec
- `milk2spr` (use the cal3d conversion)
- `svgalib` graphics driver
- `cssocket` (provided `iNetworkDriver`, `iNetworkListener`, and `iNetworkConnection`)
- `ensocket` (provided `iNetworkDriver2` and `iNetworkSocket2`)
- `netman`
- `picogui`

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ]

This document was generated using texi2html 1.76.