XML World File Format at a Glance
This article intends to breakdown the Initialisation Section of the CS world file into it's different parts in-order to explain syntax and common usage although much of this is covered in the documentation some parts are not up to date and often aren't explained properly so this article hopes to pick up where the documentation leaves of so to speak.
First of all, because the CS world format has the advantage of being in an ACSII format we can open in up in a standard text editor preferably one with XML syntax highlighting for simplification. So either start a new document or open an existing world file for editing (many examples can be found in the CS/data/ sub-directories)
In the beginning...
All CS world File must start with the <world> tag and end with </world>. So to start us off we'll have:
Note that if you want to make a library then just replace world with library. The rest of this tutorial is also valid for libraries except that libraries can only contain textures, materials, and mesh factories and no sectors.
Adding a Texture
Next we initialise which textures and materials we intend to use this is done by specifying VFS paths. VFS paths are a way of defining real paths without using Operating-System specific functions, so you must mount the directories that your textures are in on the VFS in order for them to be found. VFS (Virtual File System) paths in case you were wondering are not just ordinary file system paths but paths that CS has set to ensure multi-platform compatibility, real paths are usually mounted to VFS paths in the vfs.cfg for example /lib/stdtex is actually a zip file called stdtex.zip in CS/Data which has been mounted to /lib/ VFS path, this comes in handy as you can refer to zip files as folders just by mounting them. We start by specifying that we are talking about textures using the <textures> tag and then specify which texture we are using with the <texture> tag, and specify it's location using the <file> tag.
<textures> <texture name="oldmetal"> <file>/lib/stdtex/oldmetal.jpg</file> </texture> </textures>
This should load a texture which can now be referred to as oldmetal from that VFS path /lib/stdtex/.
Other specifics relating to special cases in <textures> such as cubemaps and key colours can be read about in the official CS documentation or refer to cs/data/flarge/
Shaders will come into play in the material section. But before you can use a shader in a material we have to make sure the engine knows to load it.
<shaders> <shader> <file>/shader/std_lighting_detail.xml</file> </shader> </shaders>
Adding Materials With Shaders
Materials are a way of applying surface properties by name not just textures. An example of this can be for example applying a pure colour with no texture.
<materials> <material name="green_color"> <color red="0" green="1" blue="0" /> </material> </materials>
This would make whatever the material was applied to a green colour. But most of the time you will want to specify a texture like so.
<material name="oldmetal"> <texture>oldmetal</texture> </material>
Another example are shaders, I will not get into shader specifics but applying a shader to a material is a simple process. Lets refer to the shader we used earlier.
<material name="oldmetal"> <texture>oldmetal</texture> <shader type="standard">std_lighting_detail</shader> </material>
This would apply the shader, but if we were to examine the actual shader we would notice that it also takes 2 variables which we can specify here. One is a texture that will be overlayed on the existing texture, and the other is a 2-D vector which basically shrinks down that texture to that scale. To use these variables we would do the following:
<material name="oldmetal"> <texture>oldmetal</texture> <shader type="standard">std_lighting_detail</shader> <shadervar name="tex detail" type="texture">oldmetal</shadervar> <shadervar name="detail texture scale" type="vector2">10,10</shadervar> </material>
Luckily for us this shader is a standard shader and the default renderloop already includes standard shaders, had this been another kind of shader we would have needed to change the renderloop. I decided to keep the same texture as the detail texture to avoid having to specify another texture and shrunk it by 10 UV's. If you are confused as to how i got the variable names and types, you can check cs/data/shaders/std_lighting_detail.xml for the type and names of the shader variables it takes. This is the same for all shaders.
So far we have specified a simple texture, material and shader-setup which looks like this:
<world> <textures> <texture name="oldmetal"> <file>/lib/stdtex/oldmetal.jpg</file> </texture> </textures> <shaders> <shader> <file>/shader/std_lighting_detail.xml</file> </shader> </shaders> <materials> <material name="oldmetal"> <texture>oldmetal</texture> <shader type="standard">std_lighting_detail</shader> <shadervar name="tex detail" type="texture">oldmetal</shadervar> <shadervar name="detail texture scale" type="vector2">10,10</shadervar> </material> </materials> </world>
Adding sounds is much like everything else in CS, very simple. It goes something like this:
<sounds> <sound name="tada.wav"> <file>/lib/std/tada.wav</file> </sound> </sounds>
Adding A Library
Another way of defining almost all of these things, is the use of libraries. A library is structured very much like a world file only it starts with <library> and ends in </library> it also cannot accept start locations and sectors. It is a good way of grouping certain things for loading. Also if specifying mesh factories in a library you will be able to load them into viewmesh. To include a library in a world file we would use:
<library path="/lib/std" file="library" />
There are an array of plugins that need to be handled in CS, only including the ones you need is the best way to go about things.For this reason we shall only include the thing-loader and factory plugin for this example.
<plugins> <plugin name="thing">crystalspace.mesh.loader.thing</plugin> <plugin name="thingFact">crystalspace.mesh.loader.factory.thing</plugin> </plugins>
We do this to allow us to use the full potential of it's factory and mesh setup. Thing meshes are usually low-poly meshes that use pre-calculated light-mapped surfaces. Their downside is that they are less efficient than a general mesh.
Settings are there to over-ride some default engine settings such as ambient light and lightmap size you can read about them in the documentation.
The Factory/meshobject scenario is often important if you wish to repeat meshes several times. But not always necessary if you wish to just have one instance of a mesh. For this article I will be taking advantage of the Factory/Meshobject setup to better familiarize users with it.
In a factory we usually tell the engine what factory will be called, the plugin used, the geometry information and also material(s) used on the mesh. With a thing mesh we have the option of mutliple materials based on the polygons you assign it to. This factory was taken from the simple map tutorial in the documentation. It is a simple cube with a single material applied.
<meshfact name="walls"> <plugin>thingFact</plugin> <zfill /> <params> <v x="-10" y="-1" z="10"/> <v x="10" y="-1" z="10"/> <v x="-10" y="-1" z="-10"/> <v x="10" y="-1" z="-10"/> <v x="-10" y="4" z="10"/> <v x="10" y="4" z="10"/> <v x="-10" y="4" z="-10"/> <v x="10" y="4" z="-10"/> <material>wood</material> <texlen>4</texlen> <p name="up"> <v>6</v> <v>7</v> <v>5</v> <v>4</v> </p> <p name="down"> <v>3</v> <v>2</v> <v>0</v> <v>1</v> </p> <p name="back"> <v>7</v> <v>6</v> <v>2</v> <v>3</v> </p> <p name="front"> <v>4</v> <v>5</v> <v>1</v> <v>0</v> </p> <p name="left"> <v>6</v> <v>4</v> <v>0</v> <v>2</v> </p> <p name="right"> <v>5</v> <v>7</v> <v>3</v> <v>1</v> </p> </params> </meshfact>
The first line initilises and names the meshfactory. The second tells the loader what plugin we are using in this case a thing factory becuase we are defining geometry. <zfill /> is used so that the card ignores the zbuffer and draws the Z value from the thing polygons over the old Z values present on screen. This is used by the outermost walls because it is a faster mode than <zuse/> as it does not need to read the zbuffer. It will be the first thing rendered in a back2front render. The next block is actually defining the position of vertexes. This box is made up of 8 vertexes. Next is the material. We specify the material we created earlier (old metal). Now we set <texlen> to 4 meaning that for every 4*4 world units one texture will be tiled. Next we define polygons by specifying the vertices that make up that polygon but remember that vertices start to count at 0. These polygons can also have names for debugging reasons mostly but they can have other uses.
And that brings a close to our initialisation section of a world file. In the rest of this tutorial I will tell you how to use these to create a sector in a world that can be loaded by walktest.