Sofie
a real-time 3d-engine



General Comment: Sofie is still alpha software, so do not expect
Download files - to download files
Screenshots of the Demo
Quickstart - to get the demos running
Short story behind the current demo

Directory


Abstract

Sofie is a real-time 3d-engine, with which it is easy to construct objects consisting of textured polygons and let them interagate. It could be used to program games or virtual realities. Sofie is licensed under the GNU-General Public License, and therefore full source-code is provided.

Versions

This documentation belongs to Sofie-0.2 which was released 10.November 1997. Compared to Sofie-0.1, Version 0.2 comes with a new Object Manager, which is not quite ready, but it should work well with the demo. The main change is, that this new Object Manager can handle objects with immerge into another object. I promised in the documentation of Sofie-0.1 that Version 0.2 will have homing missiles. I postpone this for Sofie-0.3. The demo is now not so well-balanced. This will change. Graphics of the demo will be improved (if I find somebody who volunters), Computer Enemies will be improved. Some functions will be optimised. (there is still a lot to optimise). The object manager is still not ready. More OO methodes will be implemented. Particles in space will be implemented, to get impression of speed.

Requirements

To run Sofie you need

License

Sofie is copyrighted © 1997 by Stephan Schießling and distributed under the terms of the GENERAL PUBLIC LICENSE (GPL). If you do not know that, please contact the Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA.

History

Work on Sofie began in december 1996, it was intended as little project to learn basics of 3d graphics. The name Sofie was chosen, because at the same time the author became father of a little girl named Sofie. The first versions of sofie used SVGALIB. First there was only one polygon which could be rotated. At that time the author got to know a free software project called wt, which is a real time 3d-engine like doom. But unfortunately this project obviously stopped, there was no progress for years. The second bad thing about it was, that it was not possible to move in all directions of 3D-space. This is the difference between games like doom and games like descent. Therefore the author decided to write a free-direction 3d engine from scratch. wt had this nice stone textures, and they got used in Sofie. Programming with SVGALIB was really a pain, because if Sofie was buggy the hole system "crashed". Actually it did not crash, but the keyboard was reprogrammed, therefore one was not able to type in commands. Because of that limitation the author decided to switch to X and uses the MIT-SHM extension. This combination has proven to be equally fast as SVGALIB, but debugging was much easier. There is an interesting project out there in cyberspace, called GGI. This will substitute SVGALIB soon, and has not the limitations of SVGALIB. If GGI is ready, the author intends to support it.

FAQ

The demo is too slow!

No, your computer is to slow. But try resizing Sofies window, this may help.

The keys are not working!

Activate Sofies window by moving the mouse pointer into Sofies window. Activate NumLock, by pressing Num until NumLock light appears.

Joystick does not work!

The joystick can only work if the joystick driver is installed, it is a loadable module for the kernel. Per default the joystick is not activated by sofie. To activate, set the JOYSTICK label in the makefile src/Makefile.

After resizing, a lot of error messages occur!

You probably activated the cockpit, and resizing of the window implies resizing of the cockpit. Sofie uses for that operation the program pnmscale. Get this program and install it in your system. If you can't do that, deactivate cockpit. (use the key c for that)

Why is the window so tiny?

Then I ask you: Why is your resolution so high? Games usually use a resolution of 320x200 pixels, so do I. If your X uses 1024x768, then Sofie's Window is really tiny. But you simply could resize the window, if you have enough time... Another possibility is change the resolution X is running. The author uses normally 1024x768 but for Sofie he sometimes switches by some keypresses to 400x300.

How to use Sofie

What are Objects?

In a 3d scenario you encounter objects which are independent of other objects. For example there is one spacefighter and there are other spacefighters. Or there is a plane flying through a big room. The spacefighters, the plane and the room are examples of objects, which are represented by the class Object. Here you find a simplified version of that class:
		struct Object {
		    float power;
                    float mass;
		    int live_time;
    		    View view;
		    Point velocity;
		    Polygon_List polygons;
		    Pilot pilot;
		    String id;
                 }
      

How to create Objects?

Look at the world files. There should be a lot of examples. I'll show you an example here:

	Object * ship=new Object;
	// now the view coordinate sytem of the object
	ship->view.O.init( 10 , -10 , 5 ); // the origin (should be objects center)
	ship->view.Xv.init( 1 , 0 , 0 );   // x coordinate
	ship->view.Yv.init( 0 , 1 , 0 );   // y coordinate
	ship->view.Zv.init( 0 , 0 , 1 );   // z coordinate
	Pilot pilot; 
	ship->attach_pilot(pilot);
	ship->set_id("Enterprise");

	//here the shape of the object will be defined.
	//(see How to create the shape of an object)

        Object_Manager objects; // only necessary once

	objects.add(ship);

        
First there will be allocated dynamic memory for the object ship. Then the coordinate system will be defined. view.O is the origin (where the center of ship is located) and view.Xv, view.Yv, view.Zv are the three coordinate vectors. But you could leave out the definition of view.Xv,view.Yv and view.Zv. Otherwise you should be sure that these 3 vectors form a right handed normalized coordinate system. Then a pilot is defined. Here the generic pilot is used, this is the "lazy" pilot (who does nothing), mentioned above. Our object ship gets then the identifier "Enterprise". Finally we have to register the constructed object by the object manager. For a whole world there is only one such object manager necessary, but many objects.
The shape of the object is defined by a list of polygons (see ....), and this list could be very long. If there shall be several objects of the same shape, then it is annoying to define for every object the same shape by a polygon list. In this case one simply copies an existing object. This is achieved by using an other constructor:
	

      Object * ship2=new Object(ship);

      
Here ship2 is a copy of ship. Then you can alter the other entities like above.

What are subobjects?

What would be an airplane without rockets? An airplane is considered as an object. A rocket is considered as a subobject. Say an airplane has two rockets, then there will be two subobjects rocket1 and rocket2 defined. A subobject is again an object, but it is sub because it is associated to another object.

How to define polygons

A polygon in 3d-space is given by its vertices. All vertices must be contained in one affine plane. If one likes to define a triangle T, then first define its vertices T1,T2,T3, and then the Polygon:
	

      Point T1(0,0,0);
      Point T2(1,0,0);
      Point T3(0,1,0);
      Polygon T(T1,T2,T3);

      
If you want a polygon with 4,5,6 vertices, then the definition is analogous. For n vertices you have to something like that:
	
     int n=10;
     Point * field=new Point[n];
     field[0].init(0,0,1);
     ....
     field[9].init(0,1,0);
     Polygon T(field,n);
      
There is one restriction to the order of the vertices. You have to use one of the two (if starting vertex is fixed) possible circulations. Lets say you have a square defined by the Points P1,P2,P3,P4, where P2--P4 is a diagonal, then don't use the order Polygon(P1,P2,P4,P3); To draw a polygon, the polygon should have a color assigned to it. You assign a color by
	
     Pixel white=65535;
     T.color=white; 
      
(At the moment, color 0 has a special meaning, but this will probably change). Sofie can also deal with textured polygons. (For those who don't know it, a Texture is a wallpaper for the polygon). To define a texured polygon, lets better say mipmapped polygon, one has to define a mipmap first:
	
     Mipmap M("textures/imagexyz");
     
This mipmap could be used by many polygons. Therefore define each mipmap only once. For every polygon one has to define a texture coordinate system (two dimensional):
	
     Point TO(1,2,5); // origin
     Point TR(0,1,0); // first coordinate vector
     Point TU(0,0,1); // second coordinate vector
     
The affine coordinate system TO,TR,TU is a coordinate system for the plane in which the polygon lies. Actually Sofie does not insist on that, because it projects TO, TR, TU orthogonally on this plane to get such a coordinate system (if possible). In practice, one has the vertices of the polygon, and likes to use one vertex as texture origin, and the two adjacent edges as texture coordinates TR, TU. The length of TR and TU determine the size of a texel of the mipmap. How to achieve that:
     // define Points P1,P2,P3
     MPolygon P(P1,P2,P3); // not Polygon ! this is a mipmapped polygon	 
     Point TR,TU; // P1 should be the texture origin 
     TR.adjust(P1,P2,128)
     TU.adjust(P1,P3,64)
     TR.as_texture_coord();
     TU.as_texture_coord();
     
Here P1 is the texture origin, TR has direction from P1 to P2, and the length is such that 128 Pixel of the Mipmap will exactly fit in the edge from P1 to P2. Analogous for TU. And here is a complete definition of a mipmapped polygon:
     // define Points P1,P2,P3
     Point TR,TU; // P1 should be the texture origin 
     TR.adjust(P1,P2,128)
     TU.adjust(P1,P3,64)
     TR.as_texture_coord();
     TU.as_texture_coord();
     Mipmap M("textures/imagexyz");
     MPolygon P(P1,P2,P3); // not Polygon ! this is a mipmapped polygon	 
     P.attach_mipmap(P1,TR,TU,M);
     
The methode attach_mipmap(...) should be clear. Now P is a mipmapped Polygon. In most cases, a world is constructed such that a polygon can only be viewed from one side (imagine, that one object is a cube, and you never want to fly into that cube). Then you should tell that the polygon by
     P.backface=true; 
     
This will vastly increase the rendering speed. The attentive reader should have noticed, that there was no commitment which side of the polygon has to be drawn. This is described by the direction of the vertex circulation. This works with the right hand rule. With the four fingers follow on the direction of the vertex circulation. Then your thoumb is perpendicular to the polygon and points in a special direction. A viewer which looks from some location in that direction onto the polygon will see this polygon. A viewer which looks from some location in the opposite direction onto the polygon will not see this polygon. There are exactly to vertex circulations and each is corresponding by that rule to one side of the polygon. Now you know how to define polygons. Last the polygon should be added to the shape of Object myobject. This is done by
     myobject.registrate(&P); 
     
If all Polygons are registrated by the Object, then you should inform your object by
     myobject.shape->build(); 
     
Now your object is defined.

What is a radar?

A radar is a class which contains information about active objects in the world with exact location and their identification. Actually the object manager provides a global radar (object_loc), and each radar gets this information. It is possible to build the radar with a filter. That is, you can tell the radar to show only those object with specific identifications. The radar is importent for the pilot of an object. A computer pilot gets a radar to determine the positions of objects. To draw the cockpit, the radar is also necessary.

What is the class input for?

A pilot gets as input the radar. The output is an instance of class input. The objects gets information how to act by the class input.

What is a pilot?

An objects needs someone who steers the object. For example if you steer an object by the console, then your object has the console_pilot assigned to it. Or if you use a file with steering information to steer an object, than this object has the player_pilot assigned to it. In general a pilot is a class which provides a special methode
Input * process(Radar &radar);
ie, the pilot gets a radar as input and it provides as output a instance of class Input (=output of pilot). To write a pilot, one has to modify this methode.

What different pilots are there already?

Sofies internals

To read the source code, get the package doc++ and type make doc++. Then you load the file src/html/aindex.html in your html browser.
The screen is managed by the class Screen_Manager. It keeps track of all free (ie. not already drawn) pixels. With the screen manager it is possible to draw the polygons from front to back. And at the same time it is easy to implement a cockpit.
The shape of an object consists of polygons. The problem is the hidden surface removal. This is achieved with the BSP tree technique.

How to contribute

Of course Sofie is not a small software project, so that it is not easy to catch up. But there are some tasks (not all) which could be done independently.

Expression of thanks


Stephan Schießling
Last modified: Fri Nov 7 01:05:16 MET DST