Site Journal

Bug World

Bug World is not quite 3D enough. I will leave this here while a write a new navigation script to give a reasonable view.

Practice Area

The previously missing Practice Area world has now been added. By a slim chance it was preserved by Paul Heinlein of OfficeIRC and discovered by Bread Smacker of the MS V-Chat Museum. It is available on his IRC server, has a page with a download link at Timigi’s V-Chat site, and is now added to the web based worlds here. I am very grateful to everyone for sharing this!

At the moment the X3D looks fairly close, but I think a few tweaks might be necessary – for example the coloured disks are probably better flat shaded, and the starting position could be a little higher. As usual I welcome any suggestions for improving its fidelity.

Avatar Work in Progress

I got to thinking that, short of a real multi-user environment, it would add interest to have some of the original avatars in the worlds – they could even wander about. I’ve done a few experiments and it’s starting to look fairly close to the original:

(apologies to JamesC for nicking his name, but I wanted a close comparison with one of the few clear images from the original application to survive on the www:

This turned out to be rather trickier than first expected or rather it is tricky to maintain the pixelated look. The Century Gothic font isn’t quite the same – maybe a bit of stretching is in order.

In the end I used an X3dOM RenderedTexture with an OrthoViewpoint for the text. I’ve also bundled it all up into a JavaScript function so that avatars can be created and inserted into worlds. I’ll detail this in a future post, mainly so I don’t forget how I did it.

Also I couldn’t recall whether the whole avatar should have a billboard so it always faces the viewer, or just the floating name, or neither. Since there is a back view in the original avatar frames, I opted for having just the name billboarded. Ultimately I’ll animate the avatars.

Sound Effects

The V-Chat worlds all automatically play their background sound loops. These were included in the originals as song1.wav and often song2.wav. After trying a few options I settled on using howler.js to play these sounds, which has proven really easy to use and works across every platform I’ve tested it on.

Today I added the original sound effects to the worlds. These are enter.wav and exit.wav, which used to play whenever anyone entered or left the chat room, and collide.wav which played whenever you bumped into the boundary of the world.

The entry and collision sounds react as you’d expect, but since there are no other users present they now play randomly.

I also reduced the volume of the background loops in several of the world, as they can get a little annoying!

Versioned Worlds

Most of the V-Chat worlds remained the same between the different versions of V-Chat – the original MSN/v1.1, the v2.0beta and the final v2.0 release. However, a few of the worlds were changed between releases.

Compass had a very minor visual change between v1.1 and v2.0beta, which replaced the Practice World billboard image on the grey spike with a Lobby image.

v2.0beta introduced Homespace and Lobby, both of which changed for the final v2.0 release. Both worlds had the look of their portals changed. Homespace also got new sounds.

Despite the changes being very minor, I’ve added these alternative versions of Compass, Homespace and Lobby just to complete the collection.

Worlds Published

After a couple of breakthroughs on converting to X3D, I have now published all of the Microsoft-released V-Chat worlds that I have. Rendering is as true to the originals as I have been able to achieve. It is particularly pleasing is when imperfections in the originals carry through to the conversions!

There are still a few issues here and there which I’ll mop up in slower time. It’s taken 18 years to get this far, so I’m in no hurry.

When I started this the best available technology was VRML97. My workflow went through converting the WDB file to VRML 1.0 with a homebrew VB utility (since all WDB transforms are in 4×4 matrices and VRML can swallow those without conversion), and using a utility to convert that to VRML97 with decomposed transforms. After leaving the project on the back burner for years X3D had matured and X3DOM had come along, so I started up again with a second stage of conversion. Ultimately I decided it was best to learn the maths to decompose the matrices and go straight from WDB to X3D. The rest was a matter of mainly trial and error to get the rendering reasonably close.

I realise that few people can remember these worlds, and very few people are interested in seeing them again. For my part the value has been in the challenge of puzzling it all out and in what I have learned on the way.

.wdb File Format (Part 3)

This covers the last of the structures found in the world.wdb files, which I call VCLight, though until quite recently it was called VCMystery. I’m not entirely sure it’s about lighting, I just can’t think of anything else it might be. It has the following structure:

Structure VCLight
  b0 As Byte		'unidentified byte, &H00, &H01, &H03, &H04
  b1 As Byte		'unidentified byte, always &H00
  b2 As Byte		'unidentified byte
  b3 As Byte		'unidentified byte
  b4 As Byte		'unidentified byte
  b5 As Byte		'unidentified byte
  b6 As Byte		'unidentified byte
  b7 As Byte		'unidentified byte, always &HFF
  s0 As Single		'unidentified IEEESP, always 1.0
  s1 As Single		'unidentified IEEESP, always 0.0
  s2 As Single		'unidentified IEEESP, always 0.0
  s3 As Single		'unidentified IEEESP
  s4 As Single		'unidentified IEEESP
End Structure

b0 is either &H00, &H01, &H03 or &H04.

b1 is always &H00.

B4, b5 and b6 usually have the same value, indicating that they might be colours.

b7 is always &HFF.

s0, s1, s2 are always (1.0, 0.0, 0.0), so look like a 3d vector.

s3, s4 are almost always (0.5, 0.4), except on three occasions:

  • fishbowl Node 5 (85.25, 44.0)
  • help Node 3 (74.75, 26.0)
  • paradiseisland Node 4 (113.75, 28.0)

These odd values always coincide with b0=3, though most b0=3 VCLight structures have the usual (0.5, 0.4) s3 and s4 values. They are indicated by the red digits in the table below.

There are clues in the V-Chat SDK document, which mentions “Microsoft Reality Labs general purpose rendering engine” and “RenderMorphics”. RenderMorphics Reality Lab was a 3D graphics API that was bought by Microsoft in 1995 and formed the basis for Direct3D, which first appeared as part of the DirectX 2.0 SDK. DirectX 2.0 implemented different types of light (Ambient, Directional, Parallel Point, Point and Spotlight). Trawling through the old SDK documentation unearths these _D3DRMLIGHTTYPE enumerations:

D3DRMLIGHT_AMBIENT = 0
D3DRMLIGHT_POINT = 1
D3DRMLIGHT_SPOT = 2
D3DRMLIGHT_DIRECTIONAL = 3
D3DRMLIGHT_PARALLELPOINT = 4

Its also worth noting that the VRML 1.0 standard that was around at the time had similar types of light node (DirectionalLight, PointLight and SpotLight).

As a starting point I am going to assume that these five basic types might have found their way into the .wdb file, though not necessarily with the same values.

Assuming b0 indicates the type of light, the number of each type appearing in each world is shown in the table:

Worldb0=0b0=1b0=3b0=4
basketballworld1
cartooncity
compass13
eurostadium1
fishbowl112
gallery1
hanami3
help11
homespace1
hutchspace11
kivaunderground1
kivaworld1
lavalovelounge1
littlehouse15
lobby1
lodge
lunarislands11
mall14
nshof1
outerworld1
paradiseisland111
redden1
standingstone1
tabletop11
theaterchat1
waterhole1
Number of VCLights by b0 Type in each world.wdb

Most worlds have just a single b0=0 light, and there is never more than one. Not only that, but it always lives in the first VCNode under an identity matrix, so has neither position nor location. This makes it a good candidate for an Ambient light. Only cartooncity, compass, hanami and lodge don’t have one. This is almost a fit with the VCTexDef.txb4 = &H01 worlds, with the addition of compass – though this is also the only world with a b0=1 light. So maybe this hints at some correspondence between material qualities and lighting type.

The b0=1 light in compass sits under what is in essence a translation matrix (it actually incorporates some axis-rearranging rotations, but there’s a few like that in compass). This is consistent with this being a Point or Parallel Point light i.e. its position matters, but not orientation.

The b0=3 lights sit beneath matrices with both translation and rotation. If both position and orientation matter, then this would indicate a Spot light. However, poking values into WDB files makes this look more like a DirectionalLight. In this implementation it has a position, but one that seems to be ignored by the renderer.

The b0=4 lights sit beneath translation matrices. Like the b0=1 light, this is consistent with this being a Point or Parallel Point light. There’s nothing in any of the world.wdb files that would allow the two to be distinguished. You could argue that parallel point lights are less computationally intensive, so might be used in preference to point lights. That would make b0=1 a Point light and b0=4 a Parallel Point light.

This is as far as I have got to date, but is enough to experiment with.

.wdb File Format (Part 2)

In the previous post I described the overall structure of the .wdb file format, including the file header, the collision fence and the scene node hierarchy. This time I will go into the VCGeometry structure.

Structure VCGeometry
  numPoints As UInt16		'number of 3D points in this geometry
  points() As VCPoint		'series of numPoints VCPoint structures
  numNormals As UInt16		'number of normals in this geometry
  normals() As VCNormal		'series of numNormals VCNormal structures
  numPolygons As UInt16		'number of polygons in this geometry
  lenIFS As UInt16		'number of bytes in the Indexed Face/Line Set
  polyDefs() As VCPolyDef	'series of numPolygons VCPolyDef structures
  finalIFS As UInt16		'final word of Indexed Face/Line Set (always 0000)
  w6 As UInt16			'render flags
  texCoords() As VCTexCoord	'series of numPoints texture coordinates
  ffs() As VCFFFlags		'series of numPoints 4-byte flag sets
  numTexDefs As Byte		'number of texture definitions to follow
  texDefs() As VCTexDef		'series of numTexDefs VCTexDef structures
  materials() As UInt16		'series of numPolygons material indices
End Structure

The VCGeometry starts by defining a list of numPoints 3D points, held in the file as a series of VCPoint structures:

Structure VCPoint
  x As Single
  y As Single
  z As Single
End Structure

Next comes a list of numNormals 3D vectors, held in the file as a series of VCNormal structures:

Structure VCNormal
  x As Single
  y As Single
  z As Single
End Structure

The geometry is defined in the form of an Indexed Face Set, though a later flag allows the same data to be interpreted as an Indexed Line Set. The following description assumes the IFS mode.

numPolygons defines how many polygons (or line segments) are defined in the IFS. The lenIFS value is the length of the IFS table in bytes, allowing for faster parsing of the file (i.e. removing the need to unpack the whole IFS to find its end, for example allowing the correct amount of memory to be allocated before reading the IFS.

Structure VCPolyDef
  numVerts As UInt16	'number of vertices in this polygon
  vertices() As UInt16	'series of numVerts indices into points table
  normals() As UInt16	'series of numVerts indices into normals table
End Structure

Each VCPolyDef structure defines a single polygon as a series of indices into the points() and normals() lists. The normals are therefore vertex normals – the renderer calculates face normals from the point data (points are in clockwise order when viewing the front of the face).

A zero word always follows the IFS.

The next word (w6) is at least partially a mystery. Across the set of published .wdb files this adopts one of four values: 0, 256, 768 or 1024.

w6=0 is he rarest, coinciding with “wirexx” nodes in the tabletop and lunarislands worlds (for the flower stems and walkway red edges respectively). This indicates that the geometry data should be rendered as an indexed line set.

w6=256 is by far the most common value. This seems to coincide in all cases with geometry that has a texture image.

w6=768 is used in VCGeometry nodes in outerworld, mall, lodge, lobby, homespace, hutchspace, hanami, and cartooncity and seems to be associated with flat-shaded geometry that does not use a texture image, though this is to be confirmed.

w6=1024 is used in VCGeometry nodes in compass, eurostadium, fishbowl, hanami, help, kivaunderground, lavalovelounge, littlehouse, lunarislands, nshof, paradiseisland and tabletop and appears to be associated with smooth-shaded objects that do not use texture images – again to be confirmed.

In practice, these last two values determine whether vertex normals or computed face normals are used by the renderer.

Texture coordinates are defined in texCoords(), which is a list of numPoints VCTexCoord structures. Each holds a (u,v) texture coordinate:

Structure VCTexCoord
  u As Single	'normalised and scaled texture u coordinate
  v As Single	'normalised and scaled texture v coordinate
End Structure

These are normalised and scaled to repeat the texture in both u- and v-directions.

The texCoords are followed by four bytes of &HFF.

Textures and materials are defined by a series of numTexDefs VCTexDef structures. These define an RGB colour, most also include the name of a texture image, and there are a couple of other bytes which have an unknown function:

Structure VCTexDef
  txBlue As Byte	'face colour (blue component)
  txGreen As Byte	'face colour (green component)
  txRed As Byte		'face colour (red component)
  txb3 As Byte		'unknown function, always &HFF
  txName As String	'file name of texture image
  txb4 As Byte		'unknown function
End Structure

txb4 is either &H00 or &H01. The &H01 value occurs much less often and only for (all) VCTexDef structures in cartooncity, hanami and lodge. They include VCTexDefs both with and without texture images, and include animated and non-animated items, so the purpose of this remains unclear. My guess at present is that this flags textures or materials that are not subject to lighting – more on this later.

Next comes a list of numPolygons material indices defining which material should be applied to each polygon. There is an oddity here in that numTexDefs is a Byte, but the members of materials() are UINT16. These nearly always range between 0 and 4, but there are a small number of entries in lodge that have the value 256 (associated with the wooden pillars). This indicates that perhaps the lower order byte identifies the material and the high order byte contains flags of unknown purpose.

That concludes the VCGeometry structure. Next time VCLight.