View3DX v0.4

June 16, 1997
Copyright Michael Wimmer




When I first got hold of my Diamond Monster3D, I was very eager to program something for that card, I even wanted to do a game for it. What I ended up with was a program that you can use to preview 3D Studio MAX meshes via an intermediary ASCII file-format, the 3DX file-format.

As 3DS MAX does not come with a driver for the 3DFX chipset, I decided that this viewer could be useful to some people and what you see now are my efforts to provide some documentation for a program which was not really intended to be released publicly.

The viewer has a view quite interesting features:

System Requirements

To run View3DX, you need the following:


Unzip all the files in the distribution package into a directory of your choice. Take care to preserve the directory structure when unzipping (i.e., if you use pkunzip.exe, run "pkunzip –d view3dx04").

After unzipping, the directory should contain the following files:


To invoke View3DX, use the following syntax:

view3dx [-nopentium] [scenefile.scn]


Use the "-nopentium" option to prevent usage of the undocumented Pentium clock cycle counter. This is probably necessary on any non-Pentium processor. With this option, the frame rate display will not be available, and movements assume a frame rate of 30 frames per second.

The scene file contains a description of the scene to be viewed. If this is omitted, "objects.scn" is assumed to be the scene file name.

Depending on the options, the program will take a while to start up. First, objects are loaded, which can take quite a while if BSP trees are to be generated (several minutes depending on the desired accuracy – see there), then the processor speed is determined on Pentium processors, which takes 5 seconds. After that, the program switches to fullscreen mode (which activates the passthrough on Voodoo-cards).

If you have a Voodoo card connected to a separate cable, you should switch to the Voodoo-output when the mouse-cursor disappears. Before that, only the primary video card is active.

You can switch back to Windows with Alt-Tab or Alt-Esc at any one time. As soon as you click on the View3DX-window or activate the program by any other means, it will continue to run and also activate the passthrough again on Voodoo-cards.

You can always exit the program by pressing "Esc" or "F12", except in the startup phase.

Setting up a scene

View3DX relies on information provided a scene file. The default scene file is called objects.scn. It contains information about global ambient lighting, camera position, lighting and actor positions. The easiest way to set up scenes is by creating them in 3DS MAX and exporting the objects and the scene into ASCII files with 3DXExp, a 3DS MAX plugin also written by me.

The scene file can be modified easily, a description of the file-format is given at the end of this document. Also a comprehensive description of the 3DX file-format is given there.


The viewer is designed to be used with a mouse. Some controls and all options are accessible via the keyboard. A summary help-screen can be invoked by pressing "F1".

In the following, the coordinate system assumed is: x and y-axis form the ground plane, z specifies the height. The initial camera orientation is parallel to the x-axis, looking along the negative y-axis.

The current object

As you can have more than one object, there is a notion of the current object. All controls control either the current object or the camera. Sometimes, it is a good idea to make the camera the current object, especially if you want to "fly around" in the scene. The current object can be selected by pressing one of the numbers on the alphanumeric keyboard (which means there is a maximum of 10 objects that you can control separately). Pressing "P" make the camera the current object.

Rotating and moving the current object with the mouse

Moving the mouse without any buttons pressed rotates the current object. Moving the mouse left and right rotates the object around the z-axis (yaw), moving the mouse forward and backward rotates the object around the x-axis (pitch). Pressing the left mouse button let’s you roll the current object (y-axis).

Pressing the right mouse button, you can move the object forward and backward by moving the mouse forward and backward, and you can move it left and right by moving the mouse left and right. It is impossible to move an object up or down except for the camera.

Moving the current object with the keyboard

An alternative way of moving forward and backward is using the "A" and "Z" (or "Y") key, which move the current object forward or backward depending on the current speed. If the camera is the current object, this can be used to fly around in a large scene without having to move the mouse all the time.

The speed of movement can be adjusted by "S"/"X" (increase/decrease by 10 units), "D"/"C" (100 units) and "F"/"V" (500 units).

Rotating and moving the camera with the mouse

The camera can only be rotated if it is the current object, and with the controls mentioned above. Yet the camera can be moved forward and backward (which allows zooming in and out even if an object is selected) by pressing the left mouse button and moving the mouse forward and backward.

The camera can be "panned", i.e., moved left, right, up and down by clicking the middle mouse button (if available) and moving the mouse in the appropriate direction. Of course the camera can also be moved left and right with the right mouse button if it is the current object.

Moving the current object with a joystick

A joystick can be used to control movement of the current object, if it is properly configured via the Windows Control Panel. Left and right movement of the joystick translates the current object left and right, forward and backward movement moves the object forward and backward. If the first joystick button is pressed, forward and backward movement of the joystick can be used to move the object up and down.


A quick overview over all options is always available by pressing the "F1" key.

Toggles antialiasing. Note: antialiasing needs a BSP-tree and some other options to work in any useful way. Please see the section on antialiasing

one of the most important speedups: any face pointing away from the viewer is not rendered. Also vertices attached to such faces are not transformed.

if on, all lightsources also illuminate object from the opposite side. This provides an inexpensive way to quickly illuminate a large, dark scene with fewer lightsources

Enables specular lighting (highlights) for objects that have specular lighting properties and are not texture mapped (for texture mapping, this would require two rendering passes or use of the alpha-iterator). This also means some performance hit.

This means that the texture map should be completely modulated by the original material color. Normally, this is rather controlled by the material’s "texamount" property

toggles bilinear filtering (both minification and magnification) for all textures

toggles mip-map dithering, a method to overcome mip-map banding (artefacts between different mip-map levels) without the cost of mip-map blending (trilinear filtering). The performance penalty is low, but so is the quality.

Allow two-sided materials, i.e., don’t use backface culling for materials which have the two-sided flag set. This can slow down things quite a bit, thus it is turned off by default.

toggle fogging. An exponential fog-table (the second one available in Glide) with a factor of 0.0002 is used. If everything looks very dark (most likely in large scenes), turn this off.

allows turning on/off all directional lights

allows turning on/off all point lights (including spot lights)

with this, you can toggle texture mapping

switches to an (unoptimized) wireframe display. Here, you can also visualize the BSP-partitioning

toggles w-buffering. If this is off, a BSP-tree is needed to depth sort the scene

toggles mipmapping

can be used to adjust the global specularity (shininess) factor (default: 400)

this can be used to compare the speedup formula used for highlights to the accurate solution, which is cos(N, H) raised to the power of (shininess * specularity). The original formula produces smaller highlights than the speedup, but is much slower due to the exponential.

If in wireframe mode and there is a BSP tree for some objects, show the BSP-partitioning. Red faces signify faces that have actually been used to split other faces, and green faces signify faces actually split by other faces.

Use the BSP tree (if available) to depth sort the scene. Correct depth sorting is only possible of objects do not overlap, as BSP trees are local to objects and objects are only sorted by their center points.

BSP sorting is necessary for antialiasing or if no w-buffering is used.

If antialiasing is on, do antialiasing by redrawing all relevant edges AFTER all faces of an object have been drawn. Only faces of the silhouette and between smoothing groups are antialiased.

If the previous option is on, this option causes the BSP-tree (if available) to be used to determine the order in which antialiased edges are drawn.

Antialias ALL edges. By default, only edges of the silhouette of an object and between smoothing groups and material boundaries are antialiased. This causes a huge performance hit.

causes antialiasing to be done by calls to grAADrawLine, i.e., after each face is rendered, relevant edges are drawn again with antialiasing on. This is necessary because of a bug in grAADrawTriangle which causes ugly artifacts for steep color slopes in RGB.

Try to overcome the bug mentioned in the previous option by reducing the slope of colors by clamping them to the range [20,240]. This doesn’t really help, though.

Description of the displayed numbers

One of the interesting features of View3DX is it’s ability to provide rendering statistics in realtime. They are displayed at the top and at the bottom of the screen and will be described here. The first line contains statistics about faces in the scene, the second one about vertices.

Face line:


Vertex line:


Bottom line:


Texture mapping

View3DX supports texture mapping. All the texture bitmaps have to be in the proprietary 3DFX 3DF file-format, though. You can have textured objects by defining u/v-mappings for them in 3DS MAX, applying bitmap textures and exporting them with 3DXExp. After that, just convert all bitmap texture files to the 3DF file-format. View3DX will look for 3DF-files automatically, even if the 3DX-file contains references to other file-formats. If a texture does not exist as 3DF-file, View3DX will tell you so.

You can convert many files to 3DF-files. To do this, download Texus either from my page or use the one included in the complete Glide SDK library.


I want to address the topic performance with a few words here. I am completely aware that this program is not the fastest possible viewer for 3DS MAX meshes. Even Glide includes a program that can display some objects nearly 10 times as fast. On the other hand, I think this program provides some nice features to play around with not found in other applications.

I have spent considerable time and effort in making this program as fast as possible. Yet in the end, I had to make a decision whether to include some additional features or whether to optimize any further. I have decided on the former, so what you see is maybe not so fast, but it includes features like colored lighting, lighting with attenuation, spotlights and other things.

On a P133 (my development platform), the maximum achievable triangle count is about 300.000 polygons per second if you have only one monochromatic directional light, turn off specular lighting and if the backface culling situation is good. With 2 or 3 general lights (including spotlights) and specular lighting, performance can go down to 100.000 polygons per second. This means that you should strive to keep the polygon count below 3000 polygons per frame if you want to achieve constant 30 frames per second. To evaluate performance correctly, waiting for vertical sync has been turned off in this version.

Profiling the application reveals that in a scene of about 3000 triangles, with two lights (one directional, one spotlight) and specular lighting turned on, about 20% of the CPU time is consumed by Glide, more than 55% of the time goes into lighting vertices and the rest is in vertex transformation and triangle setup. Coding the time-critical parts of the lighting code in assembler would probably help a bit, but I must admit that I don’t have the time anymore to put much effort into optimization of this program, so you will probably have to live with the way it is now.


One of my goals in this viewer was to provide a useful method for antialiasing. Unfortunately I must admit that I have failed. It seems quite impossible to implement antialiasing on the 3DFX hardware in a way that doesn’t slow down everything to a crawl and which produces no artifacts. The main problems I have run into were the following:


I cannot say for sure which is the best way to do antialiasing. There seems to be none that is fast and correct. I have obtained the best results by turning off w-buffering, using a BSP tree for depth sorting and redrawing the edges with grAADrawLine immediately. Your mileage may vary... In any case, antialiasing is very slow.

BSP trees

In the scene file, you can specify for each object whether a BSP tree should be built for that object. BSP trees (binary space partition trees) are a binary tree data structure to speed up depth sorting and other algorithms by preprocessing a mesh and creating a tree where every node represents a plane which splits space into one half in front of it and one half at the backside of it. All triangles below that node must either be completely in front or to the back of that plane, and if a triangle does not satisfy that criteria, it is split along the partitioning plane. BSP trees are numerically not very stable, so you must specify an accuracy level for BSP generation.

The accuracy level specifies the maximum value of the cosine of the angle between a point and the normal vector so that this point can still be classified as lying on the opposite side. This is important because for BSP trees, you need to split a large number of triangles. If you decide to not split triangles where only one point lies slightly on the wrong side of the splitting plane, you end up with a substantially lower number of split triangles.

Specifying a value of 0.1 usually gives a fast BSP tree, but not a correct one. Problems can be observed when turning of w-buffering ("U") and turning on BSP-sorting ("N") and rotating the object. A value of 0.01 usually suffices for a correct tree, but takes considerably longer to compute. Depending on the complexity of the model, BSP computation can take half a minute up to a few minutes or even longer, if a greater level of accuracy is specified. The problem is that a greater level of accuracy also creates a much bigger number of triangles: specifying 0.01 usually adds half the number of triangles of the original mesh, sometimes it even doubles the number of triangles. This causes a considerable performance hit!

Please note that edge adjacency lists (necessary for antialiasing) are only created if a BSP tree is also created! The edge list is used to prevent T-edges when splitting triangles, so that you always split the edges of both triangles if they are adjacent to each other.

The algorithm for generation of the BSP tree works as follows:

  1. Test 20 random candidates from the set of input triangles – determine how many splits each of them would produce if it were selected as a splitting plane
  2. Take the best candidate from 1. and insert all triangles either into the front list of the back list of the splitting triangle.
  3. Do 1. recursively for the front list and the back list.


A BSP tree can easily be used to solve the visibility problem:

if (triangle is facing away from the viewer)

draw all triangles that are in front of the triangle

draw the triangle itself

draw all the triangles that are behind the triangle


do it the other way round


Thus, all triangles are painted from back to front, leaving only the foremost triangles in the framebuffer.

Please see the description of the scene file at the end of this document for further information on how to generate BSP trees.

Description of the 3DX file-format

The 3DX file-format is completely in ASCII and easily machine-readable. The file is case-sensitive. Each line can contain either some specified data at the beginning of the line, be completely empty (newline) or contain some comment after one of the following comment characters: " " (space), ":", "#" and ";". I will describe the file-format by placing comments in a sample file:

; Compulsory header:

3DX File 1.0


; Number of objects in the file:

NumObjects: 1


; First object description (includes 3DS MAX-name):

Object 0: Box01


; Object properties:

Vertices: 8 TVertices: 0 Faces: 2 Materials: 1 Normals: 0

; number of vertices, number of texture vertices (if this is

; >0, also texture faces will be in the file), number of

; materials (if >1, each face will have a material index),

; info whether face normals are stored in the file ([0|1])


; ---------------------------------------- Material section

; The following material-section appears only if materials>0:

Material list:


; One material info, including 3DS MAX name:

Material 0: graymtl

; Diffuse R/G/B-color and Alpha (=opacity) value:

Diffuse: R: 0,333333 G: 0,313726 B: 0,309804 A: 1,000000

; specular component only if selected in Export Options:

Specular: R: 0,298039 G: 0,298039 B: 0,298039 Shininess: 0,110000 Shiningstrength: 0,170000

; R/G/B color of specular component. Shininess specifies size

; of highlight (it is the x in (N.H)^x), Shiningstrength

; specifies the strength of the highlight


; Is this material two-sided or not ([1|0]) (i.e., is backface

; culling allowed?)

Twosided: 0


; the Texture can be a valid bitmap-filename,

; "NODIFFUSE" if there is no diffuse map at all, and

; "NOBITMAP" if the map is no bitmap (e.g., a procedural map)



; ... insert other materials here


; -------------------------------------------- Vertex section

; Compulsory:

Vertex list:


; One vertex information:

Vertex 0: X: -31,090403 Y: -31,958305 Z: -23,771324

; X/Y/Z information comes with 6 digits of precisions (more

; doesn’t make sense, as 3DS MAX uses float)


; ... insert other vertices here


; The Texture-Vertex section will be only inserted if

; TVertices > 0:

TVertex list:


; U/V coordinates for one Texture Vertex:

TVertex 0: U: 0,300000 V: 0,400000


; ... insert other TVertices here



; ----------------------------------------------- Face section

; Compulsory:

Face list:


; One face information:

Face 0: A: 3 B: 0 C: 1 AB: 1 BC: 1 CA: 0

; A/B/C are indices into the vertex list. AB/BC/CA specify

; whether the corresponding edge should be drawn or not

; (interior edges will have 0 here)

Smoothing: 1

; a 32-bit integer containing the bitwise OR of the 32

; possible smoothing groups for that face (so "3" would

; indicate that the face belongs to group 1 and 2). "0"

; specifies that the face should be flat-shaded.


; the following Texture Faces appears only after each face

; if TVertices > 0:

TVFace 0: TA: 0 TB: 2 TC: 1

; TA/TB/TC are indices into the Texture Vertex list


; the material index is only inserted if Materials > 1:

Material: 2

; the index can be > Materials, it will simply be wrapped

; (taken modulo Materials)


; ... insert other faces (+tvfaces) (+material indices) here


; ------------------------------ End of one object definition


; ... insert other object definitions here


; ------------------------------ End of 3DX file


Description of the .SCN file-format

The .scn file-format is used to provide information about the contents of a scene. It has information about:


On demand, the 3DX exporter can generate a standard .SCN-file which sets the camera at an initial location, specifies a default global ambient light, contains all the lights in the 3DS MAX scene (but not with all their properties), and contains exactly one actor, which is the .3DX-file currently exported. I will again describe the file format via an annotated example.

Again, the file is case sensitive, comment rules are the same as for the .3DX file-format. Some of the lines are optional, but if they appear, they must be in the order shown (i.e., you cannot exchange to optional lines).

; Compulsory header:

Actor Database


; Optional: path to .3DX and texture bitmap files.

; this path can be relative

ObjectFilePath: objects\


; Optional: a global specularity factor to multiply with

; each individual material specularity to obtain the specular

; exponent. The default value for this is 400

Specularity: 390


; Optional: global ambient color (R/G/B values):

Ambient Color: R: 1 G: 1 B: 1


; Optional: global ambient color intensity. If a color is also

; specified, this is a multiplier. If not, it is interpreted

; as monochromatic global ambient color:

Intensity: 0.02


; Compulsory: Number of actors and number of Lights:

Actors: 1 Lights: 2


; Compulsory: initial camera location. Will be set to

; the following by the 3DX exporter:

Camera: X: 0 Y: -1000 Z: 0


; ------------------------------------------ Light section


; Compulsory (even if Lights == 0): light list:

Light List:


; One light description (including 3DS MAX name):

Light 0: Light01



; The Type can be one of the following:

; DIRECTIONAL: monochromatic, directional light

; OMNI: monochromatic, not attenuated light

; (those two are not generated by the exporter – they

; allow for faster rendering, but are not as flexible)

; GLDIRECTIONAL: OpenGL-like directional light with

; optional color and intensity

; GLOMNI: OpenGL-like point light source with optional

; color, intensity and compulsory attenuation

; GLSPOTLIGHT: OpenGL-like spot light source with all

; properties of GLOMNI, plus a direction, Cutoff angle

; and spotlight exponent




; for OMNI, GLOMNI and GLSPOTLIGHT: light position:

Position: X: 0 Y: 0 Z: 0



; direction (need not be normalized):

Direction: X: 0 Y: 0 Z: 1


; for GLOMNI and GLSPOTLIGHT: Attenuation and bounding sphere:

; A/B/C specify the constant/linear/quadratic attenuation for

; the following formula, if d is the distance to the light:

; "attenuation = 1 / (A + B * d + C * d^2)", which will be

; multiplied to the lighting factor. "Radius" specifies a

; bounding sphere for the light, any objects farther away

; are not lit by this light.

Attenuation: A: 1.0 B: 0.005 C: 0 Radius: 5000


; for GLSPOTLIGHT: Cutoff angle (in degrees) and spotlight

; exponent. Any vertex which deviates more than the cutoff

; angle from the light direction will not be lit. This defines

; the spotlight cone. The spotlight exponent specifies how

; bright the spotlight is in the center and at the border:

; "factor = (cosine of angle between light direction

; and vertex) raised to the power of Spotexponent"

; View3DFX only calculates this accurately for powers of 2,

; (0, 1, 2, 4, 8, ...), for other values a (fast)

; approximation is used.

Cutoff: 30 Spotexponent: 31


; Optional for any GL-light: RGB-color

Color: R: 0.3 G: 0.3 B: 0


; Optional for any light: Intensity (if no color specified,

; monochromatic light is assumed).

; NOTE: the Intensity of a light may assume NEGATIVE values!

; this allows to specifies areas of darkness!

Intensity: 0.7


; optional for any light: should this light be enabled or not

; ([1|0]):

Enable: 1


; ... insert other lights here


; ---------------------------------------- Actor section


; Compulsory keyword:

Actor list:



; one Actor definition, including initial position:

Actor 1: X: 0 Y: 0 Z: 500


; Filename for the Actor 3DX-file (including extension):

Filename: 1701d.3dx


; Optional: Specify an RGB-diffuse color for this actor,

; which is used if it contains no materials. Also the

; Alpha-value (= opacity) must be specified, then.

; The following default will be used if it is not specified:

Color: R: 0.5 G: 1 B: 1 A: 1


; Optional: Specify an RGB-specular color for this Actor

; which will be used if it contains no materials.

; The following default will be used if it is not specified:

Specular: R: 0.7 G: 0.7 B: 0.7


; Optional: Specify a scale for the object with which all

; Vertex coordinates are multiplied when loading

Scale: 1


; Optional: specify whether all normals should be reversed

; when loading this actor ([0|1]):

Flipnormals: 0


; Optional: Specific to the program View3DFX, this specifies

; that a BSP-tree should be generated on startup for this

; actor. This is mainly to allow antialiasing. The number

; controls the accuracy (and the computing time) for the

; BSP tree. 0.1 usually is quite fast, but will have errors,

; 0.01 is slow but will be correct almost always. Anything

; lower than 0.01 will take forever if the mesh is complicated

GenerateBSP 0.1


; ... insert other actors here


; Compulsory end of file:

End Database

What’s new in this release (v0.4, June 16, 1997)

This version has been substantially improved over the version previously available from my site (v0.0, May 02, 1997). Some of the major changes include:

Version 0.4 (public release):

colored omni and spotlights with attenuation, sqrt-table for speedup and light bounding spheres, global ambient light
Note: the scene file format has been changed to allow for better lighting!


Version 0.3 (internal version):


Version 0.2 (internal version):


Version 0.1: initial internal version in version control system, resembles the version on the Web quite well.

Future work, comments, bug reports

I do not have that much time at hand anymore, so I probably won’t be able to make any major changes to this program. I have tried to make it useable as it is by removing some dependencies (Pentium for example) and providing some documention (which you are reading now). What I would like to have implemented but didn’t have the time to was:


As much as I would like to implement all that, I won’t have the time to. What I want to do, though, is hear your suggestions about the program, and make sure you can make the most use out of it. This means if for some reason it doesn’t work on your platform, I will try to fix the bug, or if you find any other problems or bugs, I will also try to fix them. Also if you have suggestions on what to change to make it more useful for you, I will try my best to incorporate those changes. I would be happy to hear from you what you think about this program!

If you think you have found a bug, please send me some kind of bug report, which should at least include the following:


Under no circumstances will Michael Wimmer be liable for any damages or losses arising from the use or misuse of this software. If you use this software, you agree to these terms.

This production is the intellectual property of Michael Wimmer except for the referenced software parts made by other companies (which I won’t list here, I think it is obvious!).

The sample mesh included (1701d.3dx) was originally devised by Bob McGee, please go to the MeshMart object catalog ( to download the original and/or see all the copyright issues associated with this mesh.

All trademarks are the copyright of their respective owners.


This production is freeware, and must be distributed freely through public information exchange mediums like BBSs and Internet provided it is unmodified and distributed along with all accompanying files. No monetary gain is allowed through the distribution of this production. Distribution on CD-ROMs or any other forms sold for money requires special permission granted by Michael Wimmer.