4.2.1.3 ”ワールド”の作成


"ワールド"の作成

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.

黒いウィンドウを開き、ESCキーを押すと終了する、とてもエキサイティングなアプリケーションがあります。このアプリケーションが、あなたの望むアプリケーションではありませんか?違いますか?OK、それでは、いくつかの3Dの要素を作成してみましょう。

We'll add a texture manager, a room (technically called a sector) and some lights. First, add a pointer to our main sector and a function CreateRoom?() to the `Simple' class header file:

次の3つを追加します。

  1. テクスチャマネージャー
  2. ルーム(技術的にはセクター)
  3. いくつかの光源

まず、`Simple`クラスのヘッダファイルに、メインセクターのための1つのポインタとCreateRoom?()機能を追加します。

...
struct iSector;
...
class Simple
{
private:
 ...
 iSector* room;
 float rotX, rotY;
 ...
 void CreateRoom ();
 ...

Now add these chunks of code (texture manager, room, lights) to `simple.cpp':

そして、これらぶつ切りのコード(テクスチャマネージャ、ルーム、光源)を`simple.cpp`に追加します。

bool Simple::Application ()
{
 ...
 // First disable the lighting cache. Our app is simple enough
 // not to need this.
 engine->SetLightingCacheMode (0);
 ...
 // These are used store the current orientation of the camera.
 rotY = rotX = 0;
 ...
 CreateRoom ()
 ...
}
...
void Simple::CreateRoom ()
{
 // 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"))
   ReportError("Error loading 'stone4' texture!");

 iMaterialWrapper* tm =
   engine->GetMaterialList ()->FindByName ("stone");

 room = engine->CreateSector ("room");
 csRef<iMeshWrapper> walls (
   engine->CreateSectorWallsMesh (room, "walls"));
 iMeshObject* walls_object = walls->GetMeshObject ();
 iMeshObjectFactory* walls_factory = walls_object->GetFactory();
 csRef<iThingFactoryState> walls_state = 
   scfQueryInterface<iThingFactoryState> (walls_factory);
 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 ();
}

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 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 Crystal Space will scale them automatically if this requirement is not 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?().

追加したコードは、まず「LoadTexture?()」でテクスチャをロードします。第一パラメータは、エンジンが知っているテクスチャの名前です。第二パラメータは、VFSボリュームの実ファイル名です(see section Virtual File System(VFS))。注意して下さい。もし`stone4.gif`をもっていない時は、別のテクスチャを使用することができます。テクスチャに必要なことは、テクスチャのサイズが2の累乗でなければならないことです(例えば64x64)。注意して下さい。もしサイズが2の累乗でなく、クオリティを軽減することができる時は、Crystal Spaceは自動でテクスチャを縮小・拡大します。この機能は、`iTextureWrapper?`を返します。`iTextureWrapper?`は私たちは使用しません。代わりに、`iMaterialWrapper?`を使用します。これは、「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 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 maps or buildings.

そして、「CreateSector?()」でルームを作成します。ルームは、はじめは空っぽです。 Crystal Spaceのルームは、`iSector`で表されます。`iSector`は、幾何学的なオブジェクトを保持することのできる基本的なコンテナです。Crystal Spaceのオブジェクトは、mesh objectsで表されます(see section Mesh Object Plug-In System)。Crystal Spaceのmesh objectsには、いくつかのタイプがあります。mesh objectのすべてのタイプは、ジオメトリを表現する方法にいくつかの違いがあります。このチュートリアルでは、"thing" mesh objectタイプのみを使用します。このmesh objectタイプは、インドアマップや建物の壁に非常に便利です。

Now, we want to 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. The geometry of a mesh is generally stored in its factory, thus we need to obtain an interface to the "thing" factory.

今、ルームの6枚の壁を作成したいです。まず、thing mesh objectを作成します。thing mesh objectはエンジンの便利な機能があるため、作成することが普通です。便利な機能とは、CreateSectorWallsMesh?()です。CreateSectorWallsMash?()は、thing meshを作成し、与えられたセクタに追加します。thing mesh objectを作成した後は、meshにポリゴンを追加します。メッシュの配列は、そのファクトリに保管することが一般的です。従って、"thing" ファクトリにインターフェイスを含める必要があります。

To do this we first need to obtain the interface to the `iMeshObject?' interface. The `iMeshWrapper?' interface, as returned by CreateSectorWallsMesh?(), has a method GetMeshObject?() which gives us just that.

これをするために、`iMeshObject?`インターフェイスへのインターフェイスを取得する必要があります。`iMeshWrapper?`インターフェイスは、GetMeshObject?()を持っています。GetMeshObject?()により、`iMeshObject?`へのインターフェイスを取得します。`iMeshWrapper?`インターフェイスは、CreateSectorWallsMesh?()の戻り値です。

In the next step, we need to query the mesh factory. Again, the previously obtained `iMeshObject?' has a method which returns an interface to the mesh factory.

次に、meshファクトリを実行する必要があります。この場合も、前もって取得済みの`iMeshObject?`はメソッドを持っています。このメソッドは、meshファクトリのインターフェイスを戻します。

And lastly, to obtain the interface to control "thing"-specific aspects of the mesh factory, we query the interface called `iThingFactoryState?' from the mesh factory interface. We use the function scfQueryInterface?<>() which is part of SCF (see section Shared Class Facility (SCF)). This will see if the mesh factory actually implements `iThingFactoryState?' (which should be the case here) and, if so, it will return a pointer to the implementation of `iThingFactoryState?'. All mesh objects and factories implement some kind of state interface which is used to set up or query the state of that mesh object or factory. Note that all interfaces which you query using scfQueryInterface?<>() must be assigned to a variable of type csRef<>. This ensures that the reference to the interface is released once it is no longer needed.

そして最後に、meshファクトリの固有の面である"thing"をコントロールするためのインターフェイスを得るために、mashファクトリインターフェイスから`iThingFactoryState?`インターフェイスを実行します。私達は、scfQueryInterface?<>()機能を使用します。これは、SCF(see section Shared Class Facility(SCF))の一部です。ここでは、mashファクトリが実際に`iThingFactoryState?`を実装していることを確認できます。そして、`iThingFactoryState?`の変数に、ポインタを返します。すべてのmeshオブジェクトとファクトリは、いくつかの状態インターフェイスを実装しています。状態インターフェイスは、セットアップや、メッシュオブジェクトやファクトリの状態を実行するために 使われます。注意して下さい。scfQueryInterface?<>()を使用して実行するすべてのインターフェイスは、csRef<>タイプの変数に割り当てなければなりません。これは、インターフェイスへの参照が、必要なくなったときに一度解放されることを保証します。

We now have 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 convenience 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).

今、ポリゴンを作ることに使うことができる`iThingFactoryState?`を持っています。 個々のポリゴンを作成する様々なファンクションがありますが、 この例では、中から見たとき1つの箱になっているものを作成する便利な機能を使います。それは、部屋の壁のような場合、役に立ちます。AddInsideBox?()機能がこれです。 それは、6つの配置されたポリゴンを作成します。そして、それらは内側から見ることができます(注意して下さい。Crystal Spaceでは、ポリゴンは右回りの方向の頂点である場合、見ることができます)。AddInsideBox?()で与えられた箱の座標は、オブジェクト空間 にあります(ワールド空間とカメラ空間によって対比します)。

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

SetPolygonMaterial?()機能は、ポリゴンの構成要素をセットします。1つめのパラメータは幅です。ここでは、幅の指定に`CS_POLYRANGE_LAST`を使用します。なぜなら、最後に作成されたポリゴンの構成要素を設定することに関心があるためです。(すなわち、すべてのポリゴンは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 each 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 3rd parameter indicates that the texture will be scaled so that one texture tile is exactly 3x3 world units in size.

テクスチャは、SetPolygonTextureMapping?()を使用しポリゴンに位置ずけられます。この機能は、いくつかの種類があります。このチュートリアルで使用する1つの機能は、もっとも単純ではあるが、最小限の管理を提供してくれます。この特別な例では、まずはじめに、箱を作るためのそれぞれのポリゴンの2つの頂点を指定し、それをテクスチャのu-axisとして使用します。v軸は、u軸の垂線として計算されます。3つ目のパラメータは、テクスチャが拡大縮小されるため、テクスチャタイルはワールド単位のサイズで3x3になることを示します。

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 represented by an object implementing `iLightList?'. To get this list you call iSector::GetLights?().

最後に、壁を見れるようにするために、ルームにいくつかのライトを作成します。`iLight`インターフェイスは1つのライトを表します。この例では、いくつかの移動せず、光の強さも変わることのない固定ライトを作成しました。このような3つのライトを作成し、AddLight?()でルームに追加します。注意して下さい。1つのセクタ内のライト一覧は、`iLightList?`を実装した1つのオブジェクトによって表されます。このリストを取得するためには、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.

ライトを生成するとき、いくつかのパラメータを使用します。第1引数は、ライトの名前です。これは、あまり使用されません。大抵は0をセットします。第2引数はワールドでのライトの位置です。次は半径です。ライトは、ライトの中心と半径から求められる球の、外側にあるポリゴンに対して影響を与えません。次のパラメータは、RGBフォーマットのライトの色です(<1,1,1>は白、<0,0,0>は黒を意味します)。最後のパラメータは、ライトがシェードダイナミックライトであるかどうかを示します。シェードダイナミックライトはまだ動かすことはできません。しかし、強度を変更することはできます。シェードダイナミックライトを使用すると、多少のコストがかかるため、デフォルトでは無効にします。

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.

Prepare()を呼ぶことで、エンジンは、シーンのレンダを準備します。必要であれば、すべてのテクスチャを準備し、すべてのライトマップを作成します。これを呼んだら、ワールドのレンダを始めることしかできません。なぜなら、ライトマップは選択した3Dレンダーに最も適したフォーマットに変換されなければならないからです。

Ok, now we have created our room and properly initialized it. If you compile and run this application you would still see a black screen. Why? Because we have not created a camera through which you can view the room.

さて、ルームを作成し、きちんと初期化もしました。しかし、このアプリケーションをコンパイルし実行しても、あなたは黒いスクリーンを見るでしょう。なぜ?なぜなら、ルームを見るためのカメラを作成していないから。