Motion Files TGA Images 3D Programming Home

Creating Objects

In a minute, I'll show how qbasic can be used to create VideoScape .geo object files that LightWave can load. But I should at least mention the more conventional methods available for creating LightWave object geometry.

The LightWave SDK fully documents the native LWO2 object format introduced with LightWave 6.0. The SDK also includes sample code for loading both LWO2s and older LWOB files. And you can create and modify geometry through Modeler plug-ins (loaders, commands, meshes, tools) and scripts.

For this article, I've written a Modeler plug-in that loads and saves .geo files (Layout loads them without the need for a plug-in). It's an adaptation of Stuart Ferguson's vsformat code included with the SDK prior to LightWave 6. It works in all versions of LightWave and has been compiled for all of the platforms on which LightWave runs. See my free plug-ins page for links.

VideoScape .geo Object Format

VideoScape, an early 3D animation program for the Amiga, is LightWave Layout's predecessor (Allen Hastings is the author of both programs).

The .geo object format is among the simplest imaginable. The files are ASCII text. Line 1 contains the identifier 3DG1. Line 2 is a count of the number of points in the object. The point list begins on line 3, with one point per line. The point list is followed by a polygon list, with one polygon per line. The last number in each polygon record is a color code.

Here's an example .geo file, a gray pyramid with four triangular sides and a square base.

   3DG1             .geo version 1 file
   5                5 points
   0 1 0            point list begins here
   .5 0 -.5
   -.5 0 -.5
   -.5 0 .5
   .5 0 .5
   3 0 1 2 7        polygon list begins here
   3 0 2 3 7        color code 7 is matte gray
   3 0 3 4 7
   3 0 4 1 7
   4 1 4 3 2 7

Each point is defined by three numbers, the (x, y, z) floating-point coordinates, separated by spaces. As in LightWave, the coordinate system is left-handed, with y increasing upward. The number of point lines should match the point count on line 2. Each polygon line contains a vertex count, a list of point indices, and a color code. A point index is a number that refers to one of the points in the preceeding point list (the first point in the list has an index of 0). The number of point indices listed for a polygon should match the vertex count. Points are listed in clockwise order.

The color code is an integer that defines the color and other surface attributes of each polygon. The base color is limited to one of 16 entries in a fixed palette, with codes from 0 to 15.

Attributes such as "glossy," "transparent" and "smooth" are specified by adding multiples of 16 to the base color (equivalent to setting bits 4 through 7 of the color code). A few color codes, with values between 256 and 260, are used for colorless surface effects, e.g. "chrome." When LightWave Layout loads a VideoScape object file, it translates the color codes into appropriate surface settings.

A BASIC Program that Creates a Checkered Ball

The following qbasic program creates a red and white checkered ball that might remind some people of the early days of the Amiga. Of course, you could do something equivalent by applying a single surface with a checker pattern in LightWave, but if for some reason you needed two different surfaces applied to alternating polygons, it'd be a little tedious to do through the user interface.

qbasic keywords appear in red, comments in green.

   ' boingbal.bas
   ' Ernie Wright  16 Aug 97

qbasic allows you to implicitly declare variable types according to the first letter of every variable's name. This is an idea borrowed from FORTRAN, and we'll use it to reduce the size of the program a bit. We also declare pi as a constant.

   DEFSNG A-H, O-Z
   DEFINT I-N

   CONST pi = 3.141592653589793#

Parameters that control the creation of an object can easily be modified in the qbasic editor before running the program. For your own use, at least, you ordinarily won't need to write a user interface just to set values like these. The numbers assigned to ic1 and ic2 are VideoScape color codes.

   nsides = 16                         'number of sides
   nsegments = 8                       'number of segments
   rx = 1                              'x radius
   ry = 1                              'y radius
   rz = 1                              'z radius
   ic1 = 128 + 15                      'smooth white
   ic2 = 128 + 12                      'smooth red
   filename$ = "boing.geo"             'output filename

   npts = 2 + nsides * (nsegments - 1)

VideoScape .geo files begin with 3DG1 on the first line and the number of points in the object on the second line.

   OPEN filename$ FOR OUTPUT AS 1
   PRINT #1, "3DG1"
   PRINT #1, npts

The first two lines are followed by a list of point coordinates, one point per line, with the x, y, and z values separated by spaces. This part cycles through the spherical coordinates of the points and converts them to rectangular coordinates before writing them out.

   FOR j = 0 TO nsegments
      v = j * pi / nsegments - pi / 2
      sv = SIN(v)
      cv = COS(v)
      y = ry * sv

      FOR i = 0 TO nsides - 1
         u = i * 2 * pi / nsides - pi
         su = SIN(u)
         cu = COS(u)
         x = rx * cv * cu
         z = rz * cv * su
         PRINT #1, x; y; z
         IF (j = 0) OR (j = nsegments) THEN EXIT FOR
      NEXT i
   NEXT j

The point list is followed by a polygon list, with one polygon definition per line. A polygon is defined by a vertex count, a list of vertices, and a color code, all separated by spaces. Each vertex is an index into the point list, and an index of 0 refers to the first point written in the file. Polygons are written here in three separate loops. The first creates the triangles around the top segment, the second creates the quads for most of the ball, and the last creates the triangles around the bottom. As you'd expect, the vertex count and the length of the point index list are both 3 for the triangle loops and 4 for the quad loop.

   FOR i = 1 TO nsides
      IF (i MOD 2) = 1 THEN ic = ic1 ELSE ic = ic2
      PRINT #1, "3 0 "; i; (i MOD nsides) + 1; ic
   NEXT i

   k = 1
   FOR j = 1 TO nsegments - 2
      FOR i = 0 TO nsides - 1
         IF ((i + j) MOD 2) = 0 THEN ic = ic1 ELSE ic = ic2
         PRINT #1, "4 ";
         PRINT #1, k + i;
         PRINT #1, k + nsides + i;
         PRINT #1, k + nsides + ((i + 1) MOD nsides);
         PRINT #1, k + ((i + 1) MOD nsides);
         PRINT #1, ic
      NEXT i
      k = k + nsides
   NEXT j

   k = npts - nsides - 1
   FOR i = 0 TO nsides - 1
      IF ((i + nsegments) MOD 2) = 1 THEN ic = ic1 ELSE ic = ic2
      PRINT #1, "3 "; npts - 1; k + ((i + 1) MOD nsides); k + i; ic
   NEXT i

We're done.

   CLOSE #1

© Ernie Wright