[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Now we have a very exciting application which opens a black window and waits for the ESC key to quit. We assume this is the application you always wanted to have? No? Ok then, let's create some 3D stuff.
We'll add a texture manager, a room (technically called a sector) and some lights. First, add a pointer to our main sector to the `Simple' class header file:
... struct iSector; ... class Simple { private: ... iSector* room; ... |
Now add these chunks of code (texture manager, room, lights) to `simple.cpp':
bool Simple::Initialize () { ... csReport (object_reg, CS_REPORTER_SEVERITY_NOTIFY, "crystalspace.application.simple", "Simple Crystal Space Application version 0.1."); // Setup the texture manager iTextureManager* txtmgr = g3d->GetTextureManager (); txtmgr->SetVerbose (true); ... // First disable the lighting cache. Our app is simple enough // not to need this. engine->SetLightingCacheMode (0); // Load the texture from the standard library. This is located in // CS/data/standard.zip and mounted as /lib/std using the Virtual // File System (VFS) plugin. if (!loader->LoadTexture ("stone", "/lib/std/stone4.gif")) { csReport (object_reg, CS_REPORTER_SEVERITY_ERROR, "crystalspace.application.simple", "Error loading 'stone4' texture!"); return false; } iMaterialWrapper* tm = engine->GetMaterialList ()->FindByName ("stone"); // these are used store the current orientation of the camera rotY = rotX = 0; room = engine->CreateSector ("room"); csRef<iMeshWrapper> walls ( engine->CreateSectorWallsMesh (room, "walls")); csRef<iThingState> thing_state ( SCF_QUERY_INTERFACE (walls->GetMeshObject (), iThingState)); iThingFactoryState* walls_state = thing_state->GetFactory (); walls_state->AddInsideBox ( csVector3 (-5, 0, -5), csVector3 (5, 20, 5)); walls_state->SetPolygonMaterial (CS_POLYRANGE_LAST, tm); walls_state->SetPolygonTextureMapping (CS_POLYRANGE_LAST, 3); csRef<iLight> light; iLightList* ll = room->GetLights (); light = engine->CreateLight (0, csVector3 (-3, 5, 0), 10, csColor (1, 0, 0)); ll->Add (light); light = engine->CreateLight (0, csVector3 (3, 5, 0), 10, csColor (0, 0, 1)); ll->Add (light); light = engine->CreateLight (0, csVector3 (0, 5, -3), 10, csColor (0, 1, 0)); ll->Add (light); engine->Prepare (); return true; } |
This extra code first loads a texture with LoadTexture()
.
The first parameter is the name of the texture as it will be known in the
engine; and the second is the
actual filename on the VFS volume (see section 7.2 Virtual File System (VFS)). Note, if you don't have
the `stone4.gif' texture you can use another one. The only requirement
is that it must have sizes which are a power of 2 (e.g. 64x64) (note that
CS will scale them automatically if this requirement isn't met but this can
reduce quality). This function returns a `iTextureWrapper' which we
don't use. Instead we use the `iMaterialWrapper' which is created
automatically by LoadTexture()
.
Then we create our room with CreateSector()
. This room will initially
be empty. A room in Crystal Space is represented by `iSector' which is
basically a container which can hold geometrical objects. Objects
in Crystal Space are represented by MESH OBJECTS (see section 7.9 Mesh Object Plug-In System).
There are several types of mesh objects in Crystal Space. Every type of
mesh object represents some different way to represent geometry. In this
tutorial we are only going to use the 'thing' mesh object type. This mesh
object type is very useful for walls of indoor type levels or buildings.
Now we want create the six walls of our room. First we make our thing mesh
object. Because this is a very common case there is a convenience function
in the engine (called CreateSectorWallsMesh()
) which will create a
thing mesh and add it to the given sector. The only thing that has to be
done after this is add polygons to that mesh. To do this we first query
the interface called `iThingState' from the thing mesh object.
We use the macro SCF_QUERY_INTERFACE()
which is part of SCF
(see section 6.2 Shared Class Facility (SCF)). This will see if the mesh object (which is wrapped by
the mesh wrapper) actually implements `iThingState' (which should
be the case here) and if so it will return a pointer to the implementation
of `iThingState'. All mesh objects implement some kind of state
interface which is used to set up or query the state of that mesh object.
Note that all interfaces which you query using SCF_QUERY_INTERFACE()
should be released when you no longer need them (by calling DecRef()
).
However in this example we assign them to a csRef<>
which takes care of
DecRef()
for us.
From the returned thing state we now get the factory state
(`iThingFactoryState') which we can now use to create polygons.
There are various functions to create individual polygons if you want that
but in this case we use a conveniance function to create a box that can
be seen from the inside. That will serve as the walls of our room.
The AddInsideBox()
function does this. It will create six polygons
arranged so that they are visible from inside.
(note that in Crystal Space a polygon is visible if vertices are oriented
clock-wise). The box coordinates given to AddInsideBox()
are in object
space (in contrast with world space and camera space).
The SetPolygonMaterial()
function will set the material of the
polygons. The first parameter is a range. We use `CS_POLYRANGE_LAST'
here to indicate that we are interested in setting the materials of the last
created polygons (i.e. all polygons created with AddInsideBox()
).
The texture is mapped onto the polygon using SetPolygonTextureMapping()
.
There are several versions of this function. The one we use in this tutorial
is one of the simplest but it offers the least control. In this particular
case we take the first two vertices of every polygon that was created for the
box and use that as the u-axis of the texture. The v-axis will be calculated
perpendicular to the u-axis. The parameter 3 indicates that the texture will
be scaled so that one texture tile is exactly 3x3 world units in size.
Finally we create some lights in our room to make sure that we actually are
able to see the walls. The interface `iLight' represents a light.
In this case we created some static lights which can not move and change
intensity. We create three such lights
and add them to the room with AddLight()
. Note that the list of lights
in a sector is presented by an object implementing `iLightList'. To
get this list you call iSector::GetLights()
.
When creating a light we use several parameters.
First we have the name of the light. This is not used
often and mostly you can set this to 0. The second parameter is
the location of the light in the world. Then follows a radius. The light will
not affect polygons which are outside the sphere described by the center of
the light and the radius. The next parameter is the color of the light in
RGB format (<1,1,1>
means white and <0,0,0>
means black).
The last parameter indicates whether or not we want to have a pseudo-dynamic
light. A pseudo-dynamic light still cannot move but it can change intensity.
There are some performance costs associated with pseudo-dynamic lights so
it is not enabled by default.
The call to Prepare()
prepares the engine for rendering your scene. It
will prepare all textures and create all lightmaps if needed. Only after this
call can you start rendering your world, because lightmaps may have to be
converted to a format more suitable for the chosen 3D renderer.
The last code we added allocates the palette with the texture manager. Note that is needed even if you are running on a true-color display which has no palette. That's because the code does some other things beside setting up a palette.
Ok, now we have created our room and properly initialized it. If you would compile and run this application you would still see a black screen. Why? Because we have not created a camera through which you can see.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |