Back to the future for a tutorial from 2002 using VB6 and DirectX 8.0!!!
Download final sample code 559k
The absolutely enormous bug tutorial
Well, here we go. It’s time for another huge tutorial. This
one covers DX8 cameras, lights, meshes, animated meshes, vector
and matrix arithmetic, a verlet physics engine, dds textures and
alpha blending….. gasp
Inspiration for bugs
My inspiration for this tutorial was to build a physics engine
after reading a gamasutra article by Thomas Jakobsen of IO
Interactive. The first actual work was to design a model for my
physics entities. I was aiming for some kind of insect.


In the end the wasp character looked a bit complex, so I ended
up going for the beetle. The model was constructed using IK
chains to hold the various meshes together. I then key framed
an animation of the legs moving (in my interpretation of a bugs
walk).

Then I made textures from a photograph of some insects.

These textures where then applied to the appropriate meshes. I
ended up simply going for a diffuse dark grey for the legs
rather than try and map any textures.
Once that was done I used the DX SDK to convert my mesh file
into a .X file.
Getting started
The good news is that Microsoft have already done a lot of hard
work for you creating a pretty good framework of modules and
classes along with the D3DX library. Overall I found it made
the experience of creating a 3D app pretty painless. Some
explorations of matrix math later on hurt a lot more however : )
The first step was to be able to initialise the DX8 engine and
draw me some lines. The idea is that this will provide the
‘wireframe’ view of all my bouncing boxes once I create the
physics engine.
First I included the files provided by MS. D3danimaition.cls,
d3dframe.cls, d3dinit.bas, d3dmesh.cls, d3dpick.cls,
d3dshader.bas, d3dutil.bas
Then I made a form, pasted on a picture control, and added some
declarations.
Private Type CUSTOMVERTEX
v As D3DVECTOR
color As Long
tu As Single
tv As Single
End Type
Const D3DFVF_COLORVERTEX = (D3DFVF_XYZ Or D3DFVF_DIFFUSE Or D3DFVF_TEX1)
Dim m_ParticleTexture As Direct3DTexture8
Dim m_VertB As Direct3DVertexBuffer8
Dim m_Verts() As CUSTOMVERTEX
This gives us a texture for the vertexes of our objects
(a Particle texture), a vertex buffer, and a array of vertexes
to fill with data.
From here added a routine to initialise the 3d stuff. Luckily
MS already provide a bunch of stuff in the d3dinit.bas routines,
so it ends up fairly simple..
Private Sub initialiseD3D()
'setup D3d
m_binit = D3DUtil_Init(Picture1.hwnd, True, 0, 0, D3DDEVTYPE_HAL, Me)
'Set up some lights and camera
g_lWindowWidth = Picture1.ScaleWidth
g_lWindowHeight = Picture1.ScaleHeight
D3DUtil_SetupDefaultScene
'position the camera
D3DUtil_SetupCamera vec3(0, 60, -300), vec3(0, 60, -200), vec3(0, 1, 0)
Dim v As CUSTOMVERTEX
Set m_ParticleTexture = D3DUtil_CreateTexture(g_dev, App.path & "\Particle.bmp", D3DFMT_UNKNOWN)
Set m_VertB = Nothing
Set m_VertB = g_dev.CreateVertexBuffer(4 * m_MaxParticles * Len(v), 0, D3DFVF_COLORVERTEX, D3DPOOL_MANAGED)
End Sub
I called this routine from the Form_Load subroutine.
I also added a command button to the page so I could have a way
to get it all started. This is the basic DX8 render loop below.
Private Sub Command1_Click()
Do
'Start the Scene
g_dev.BeginScene
'clear the buffer
D3DUtil_ClearAll QBColor(8)
'DO THE DRAWING HERE….
'End the scene
g_dev.EndScene
'update the screen...
D3DUtil_PresentAll g_focushwnd
DoEvents
Loop Until m_run = False
End Sub
Still nothing much to see though… Against my very visual
development style, the next stop is a physics simulation which
will be temporarily invisible!
Bouncing Verlet boxes
There is heaps of ‘interesting’ literature I reviewed before
deciding where to go with this simulation. Some of the
excellent work by Chris Heckler and Jeff Lander was very useful
as were some more theoretical papers by Baraff, Mirtich, and
Witkin.
My original intention was to perform correct collision detection
and restoration, but I ran out of time in my busy schedule, so
maybe later perhaps : )
So why did I choose Verlet instead of a more traditional ODE
solver? Well I chose it because of its simplicity and
stability. Using the ODE techniques you have to use integration
approximations to work out what the new position of objects will
be after a given timestep. In comparison Verlet implies a known
position before and after the timestep, and it is the velocity
that is an approximation.
To model real 3D objects using verlet, they are built out of
particles with constraints. The constraints I have used are
simply sticks of a given length between the particles. By
arranging them carefully you end up with boxes, pyramids, even
stick figures.

This is the infamous box I am planning on making (could even be
considered a crate). The 8 particles make up its corners, and
12 sticks make up its sides. However it don’t hold up that well
with only these 12 sticks, so we need to add cross bracing… more
like this.

Right time to see how all this hangs together (literally).
Rather than avoid the issue here is the code for the Verlet
integration engine;
'**************************************************
' RIGID BODY PHYSICS SIMULATION
'**************************************************
' David Brebner, Unlimited Realities (2001)
'**************************************************
' Modeled using particles with join
' Thanks to Jakobsen, Baraff, Mirtich, Witkin, Verlet
'
' note : verlet fixed time integration is used in
' this simulation for stablility
'**************************************************
Public num_particles As Integer
Public num_join As Integer
Public join_p1(1000) As Long
Public join_p2(1000) As Long
Public join_rl(1000) As Long
Public m_curp(1000) As D3DVECTOR
'position right now
Public m_oldp(1000) As D3DVECTOR
'the old position
Public m_forc(1000) As D3DVECTOR
'Force accumulation
Public m_grav As D3DVECTOR
'Gravity
Public m_timeStep As Double
'the time between integrations
Public Function TimeStep()
'this executed the requirements of the physics
'simulation for the current timestep
AccumulateForces
Verlet
SatisfyConstraints
End Function
Private Function Verlet()
Dim i As Integer
Dim tmp As D3DVECTOR
'time to increment the positions based on the applied forces
'during the specified timestep...
For i = 1 To num_particles
tmp = m_curp(i)
m_curp(i).x = (m_curp(i).x * 2 - m_oldp(i).x) + (m_forc(i).x * m_timeStep)
m_curp(i).y = (m_curp(i).y * 2 - m_oldp(i).y) + (m_forc(i).y * m_timeStep)
m_curp(i).z = (m_curp(i).z * 2 - m_oldp(i).z) + (m_forc(i).z * m_timeStep)
m_oldp(i) = tmp
Next
End Function
Private Function SatisfyConstraints()
Dim delta As D3DVECTOR
Dim vscale As Double
Dim rl As Long
'constrain the box to the ground..
For i = 1 To num_particles
If m_curp(i).y < 0 Then
'we have hit the ground...
m_curp(i).y = 0
m_oldp(i).x = m_oldp(i).x + (m_curp(i).x - m_oldp(i).x) * 0.4
m_oldp(i).z = m_oldp(i).z + (m_curp(i).z - m_oldp(i).z) * 0.4
End If
Next
'constrain sticks based on there length
For i = 1 To num_join
D3DXVec3Subtract delta, m_curp(join_p2(i)), m_curp(join_p1(i))
rl = join_rl(i) * join_rl(i)
vscale = rl / (delta.x * delta.x + delta.y * delta.y + delta.z * delta.z + rl) - 0.5
D3DXVec3Scale delta, delta, vscale
D3DXVec3Subtract m_curp(join_p1(i)), m_curp(join_p1(i)), delta
D3DXVec3Add m_curp(join_p2(i)), m_curp(join_p2(i)), delta
Next
End Function
Private Function AccumulateForces()
Dim i As Integer
'All particles are influenced by gravity
For i = 0 To num_particles
m_forc(i) = m_grav
Next
End Function
Hopefully that’s surprisingly little (I was certainly
surprised). The main heartbeat is the Timestep function. It
calls the tree helper functions in turn, AccumulateForces,
Verlet, SatisfyConstraints.
The Accumulate Forces function currently only adds gravity to
each of the points. Eventually this would be used to apply
other external forces like explosions or wind. The Verlet
function performs a simple progressive approximation of the new
position. The position is found by taking the current position,
adding the change in position (current – old), and finally,
adding the distance due to the accumulated forces.
The last step is to satisfy any constraints that have been
established. In this case if any points are falling through the
floor (y < 0). The other constraints are the ‘sticks’. These
constraints are applied to ensure the distance between two
particles is equal to the length of the stick.
To perform this distance manipulation I used the D3DX helper
libraries which have a number of vector manipulation functions
built in. In this case I check the length of the stick by
finding the delta (difference) between the two points on the
stick. This distance may have x, y and z components.
Now a restoration vector is made. This is a scaled version of
the delta vector. The scale will attempt to restore the stick
to its constrained length. To keep things all equal half the
restoration is applied to both ends of the stick.
Bouncing Bits…

I can already imagine the strained attention spans, so as a
compromise to my sanity (and yours) here are some bouncing bits.
Remember that basic render loop. Well we are going to add some
guts to it.
Private Sub Command1_Click()
Dim a%, cnt As Long
Dim v As CUSTOMVERTEX
Dim t As Long
If m_run = False Then
m_run = True
t = GetTickCount - 10
Do
m_timeStep = (GetTickCount() - t) / 10000
t = GetTickCount
'do a physics engine tick
TimeStep
'Start the Scene
g_dev.BeginScene
'clear the buffer
D3DUtil_ClearAll QBColor(8)
g_dev.SetRenderState D3DRS_ZWRITEENABLE, 1
g_dev.SetRenderState D3DRS_ZENABLE, 1 'False
g_dev.SetRenderState D3DRS_ALPHABLENDENABLE, 1
g_dev.SetRenderState D3DRS_SRCBLEND, D3DBLEND_ONE
g_dev.SetRenderState D3DRS_DESTBLEND, D3DBLEND_ONE
g_dev.SetTextureStageState 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE
g_dev.SetRenderState D3DRS_POINTSPRITE_ENABLE, 1 'True
g_dev.SetRenderState D3DRS_POINTSCALE_ENABLE, 1
g_dev.SetTexture 0, m_ParticleTexture
g_dev.SetStreamSource 0, m_VertB, Len(v)
g_dev.SetVertexShader D3DFVF_COLORVERTEX
Dim DWFloat0 As Long
Dim DWFloat1 As Long
Dim DWFloatp08 As Long
DWFloat0 = FtoDW(0.1)
DWFloat1 = FtoDW(1)
DWFloatp08 = FtoDW(5)
' Set the render states for using point sprites
g_dev.SetRenderState D3DRS_POINTSIZE, DWFloatp08
g_dev.SetRenderState D3DRS_POINTSIZE_MIN, DWFloat0
g_dev.SetRenderState D3DRS_POINTSCALE_A, DWFloat0
g_dev.SetRenderState D3DRS_POINTSCALE_B, DWFloat0
g_dev.SetRenderState D3DRS_POINTSCALE_C, DWFloat1
ReDim m_Verts(m_MaxParticles * 2)
'Render the corners
cnt = 0
For a% = 0 To num_particles - 1
m_Verts(cnt).v = m_curp(a% + 1)
m_Verts(cnt).color = &HFFFFFFFF
cnt = cnt + 1
If cnt = m_MaxParticles Then
D3DVertexBuffer8SetData m_VertB, 0, Len(v) * cnt, 0, m_Verts(0)
g_dev.DrawPrimitive D3DPT_POINTLIST, 0, cnt
cnt = 0
End If
Next
D3DVertexBuffer8SetData m_VertB, 0, Len(v) * cnt, 0, m_Verts(0)
g_dev.DrawPrimitive D3DPT_POINTLIST, 0, cnt
g_dev.SetRenderState D3DRS_POINTSPRITE_ENABLE, 0
g_dev.SetRenderState D3DRS_POINTSCALE_ENABLE, 0
g_dev.SetRenderState D3DRS_ALPHABLENDENABLE, 0
'Render the edges
cnt = 0
For a% = 0 To num_join - 1
m_Verts(cnt * 2).v = m_curp(join_p1(a% + 1))
m_Verts(cnt * 2).color = &HFFFFFFFF
m_Verts(cnt * 2 + 1).v = m_curp(join_p2(a% + 1))
m_Verts(cnt * 2 + 1).color = &HFFFFFFFF
cnt = cnt + 1
If cnt = m_MaxParticles Then
D3DVertexBuffer8SetData m_VertB, 0, Len(v) * cnt * 2, 0, m_Verts(0)
g_dev.DrawPrimitive D3DPT_LINELIST, 0, cnt * 2
cnt = 0
End If
Next
'whats left
D3DVertexBuffer8SetData m_VertB, 0, Len(v) * cnt * 2, 0, m_Verts(0)
g_dev.DrawPrimitive D3DPT_LINELIST, 0, cnt * 2
'End the scene
g_dev.EndScene
'update the screen...
D3DUtil_PresentAll g_focushwnd
DoEvents
Loop Until m_run = False
End If
End Sub
Despite the slightly intimidating nature of the code,
it’s really just 3 types of stuff going on.
1)
Calls to our physics engine.
2)
DX3D configuration code. This stuff sets up the render states,
and assigns textures to our points.
3)
Stuffing our vertex buffers full of bouncing points from our
physics simulator, and then rendering the vertex buffer
primitive.
You can try out the sample program to see how it all hangs
together.
Not all graphics cards will display the point lists correctly,
but these are little alpha globes used to mark the points at the
vertex of the objects.
The line list meanwhile should work fine, this is the sticks
which hold the objects together.
Bounding Box
We have our objects, but it’s not really much of a physics
simulation because all our objects simply bounce through each
other. The only collisions are currently performed with the
floor!
The collision detection technique I wanted to use is a plate
method. This essentially treats each face of an object as an
infinite plate extending in all directions. Then by checking
for a combination of collisions of plates between objects you
can determine a collision.
This becomes more difficult as you try and manage edge or point
collisions with a plate, and detecting exactly where the forces
should be applied.
Hence rather than do maths, I have cheated in a very nasty quick
and dirty way!!! We are using my patented ‘Bounding Box
collisions for the maths impared’ technique.
1)
We essentially figure out the biggest box that will enclose our
object.
2)
We check if two bounding boxes touch each other.
3)
We figure out the centre of that box (our fake centre of mass)
4)
We calculate the distance between the centre of mass of the two
objects.
5)
We repulse each other in opposite directions by a fraction of
the distance (the closer they get, the more they are repulsed)
So like I say, not very scientific, but try out the demo and see
the boxes ‘collide’.
To accommodate the bounding box, and the centre of mass, I added
a few new vectors to the body type.
Public Type body_type
Part_Min As Long
Part_Max As Long
Join_Max As Long
Join_Min As Long
bound_min As D3DVECTOR
bound_max As D3DVECTOR
centre_mass As D3DVECTOR
mesh As Long
End Type
Now we add the Detect Collisions routine to the main
physics timestep.
Public Function TimeStep()
'this executed the requirements of the physics
'simulation for the current timestep
AccumulateForces
Verlet
DetectCollisions
SatisfyConstraints
End Function
And finally the actual code to detect collisions itself.
As you can see it is fairly simple. Initially working out the
bounding box, and hence the centre of mass.
The routine checks for overlaps, and pushes each point in the
object away from the other by half of the collision force. The
collision force is estimated from the distance of their centre
of masses.
Private Function DetectCollisions()
'collide with the ground...
Dim i As Long
Dim j As Long
Dim a As Long
Dim delta As D3DVECTOR
For i = 1 To num_particles
If m_curp(i).y < 0 Then
'we have hit the ground...
m_curp(i).y = 0
m_oldp(i).x = m_oldp(i).x + (m_curp(i).x - m_oldp(i).x) * 0.4
m_oldp(i).z = m_oldp(i).z + (m_curp(i).z - m_oldp(i).z) * 0.4
End If
Next
'first precomputations
For i = 0 To num_body - 1
'bounding box
g_d3dx.ComputeBoundingBox m_curp(body(i).Part_Min), body(i).Part_Max - body(i).Part_Min, D3DFVF_NORMAL, _
body(i).bound_min, body(i).bound_max
'ridiculously cheap centre of mass
body(i).centre_mass.x = (body(i).bound_min.x + body(i).bound_max.x) / 2
body(i).centre_mass.y = (body(i).bound_min.y + body(i).bound_max.y) / 2
body(i).centre_mass.z = (body(i).bound_min.z + body(i).bound_max.z) / 2
Next
'now check all the bodies if their bounding boxes overlap...
For i = 0 To num_body - 1
For j = i To num_body - 1
If (body(i).bound_min.x > body(j).bound_min.x And body(i).bound_min.x < body(j).bound_max.x)
Or (body(i).bound_max.x > body(j).bound_min.x And body(i).bound_max.x < body(j).bound_max.x) Then
If (body(i).bound_min.y > body(j).bound_min.y And body(i).bound_min.y < body(j).bound_max.y)
Or (body(i).bound_max.y > body(j).bound_min.y And body(i).bound_max.y < body(j).bound_max.y) Then
If (body(i).bound_min.z > body(j).bound_min.z And body(i).bound_min.z < body(j).bound_max.z)
Or (body(i).bound_max.z > body(j).bound_min.z And body(i).bound_max.z < body(j).bound_max.z) Then
'we have a bounding box collision, now examine in more detail...
'super bogus for now...
D3DXVec3Subtract delta, body(i).centre_mass, body(j).centre_mass
D3DXVec3Scale delta, delta, 0.001
'delta.x = 0.03 / delta.x
'delta.y = 0.03 / delta.y
'delta.z = 0.03 / delta.z
For a = body(i).Part_Min To body(i).Part_Max
'm_curp(a).x = m_curp(a).x - 5
D3DXVec3Add m_curp(a), m_curp(a), delta
Next
For a = body(j).Part_Min To body(j).Part_Max
'm_curp(a).x = m_curp(a).x - 5
D3DXVec3Subtract m_curp(a), m_curp(a), delta
Next
End If
End If
End If
Next
Next
End Function
Textured Bugs

Well physics (even cheap replicas) is pretty cool, but I feel
the need for some bugs. Its time to take the mesh designed at
the start of the tutorial – then get it bouncing along with the
boxes.
It sounds like a simple thing…. and in the end it was simple,
but I spent a lot of time reading text books and looking at
source code before the answer became obvious.
The problem is to create a matrix that will orientate (in this
case rotate) a mesh to line up with a 6 normalised vectors
describing the corners of a box. When I say normalised, this
means they are scaled to 1, and aligned with the origin so that
translation and scaling to not mess up our maths.
After going through all sorts of mathematical torture it became
evident, slightly unbelievably, that it is exactly these vector
values that make up the matrix… as this is basically the proof
of a rotation matrix.
If we consider only three points of the six, which is enough to
form an axis, you can see from this diagram how they fit into
the matrix.

The end result is a rotation matrix. We then add the
translation and scaling back to the matrix, and ‘bingo’ we can
reposition our mesh aligned with the points in our physics
engine.
For i = 0 To num_body - 1
If body(i).Part_Max - body(i).Part_Min = 7 Then
p1 = m_curp(body(i).Part_Min)
p2 = m_curp(body(i).Part_Min + 1)
p4 = m_curp(body(i).Part_Min + 3)
p5 = m_curp(body(i).Part_Min + 4)
D3DXVec3Subtract vx, p4, p1
D3DXVec3Subtract vy, p2, p1
D3DXVec3Subtract vz, p5, p1
D3DXVec3Normalize vx, vx
D3DXVec3Normalize vy, vy
D3DXVec3Normalize vz, vz
m3.m11 = vx.x
m3.m12 = vx.y
m3.m13 = vx.z
m3.m21 = vy.x
m3.m22 = vy.y
m3.m23 = vy.z
m3.m31 = vz.x
m3.m32 = vz.y
m3.m33 = vz.z
m3.m44 = 1
D3DXMatrixTranslation m2, m_curp(body(i).Part_Min).x,
m_curp(body(i).Part_Min).y, m_curp(body(i).Part_Min).z
D3DXMatrixScaling m, (join_rl(body(i).Join_Min + 1)) * 0.013,
(join_rl(body(i).Join_Min)) * 0.04, (join_rl(body(i).Join_Min + 9)) * 0.015
D3DXMatrixMultiply m, m, m3
D3DXMatrixMultiply m, m, m2
m_frame.SetMatrix m
m_frame.Render g_dev
End If
Next
The next elements of interest is the code which actually
handles the mesh. By using the existing Microsoft mesh class
provided it is reasonably easy.
'load our mesh...
Set m_frame = New CD3DFrame
m_frame.InitFromFile g_dev, App.path + "\bug.x", Nothing, Nothing
This loads a mesh into a frame, then the calls in the
matrix mesh code above (m_frame.SetMatrix m and
m_frame.Render g_dev) actually do the alignment and rendering.
To get some camera control I have also added a quick bit of
camera manipulation. You can see this here;
Private Sub Picture1_MouseDown(Button As Integer, Shift As Integer, x As Single, y As Single)
dwn% = True
mx = x
my = y
End Sub
Private Sub Picture1_MouseMove(Button As Integer, Shift As Integer, x As Single, y As Single)
If dwn% Then
ox = ox - (mx - x)
oy = oy - (my - y)
mx = x
my = y
D3DUtil_SetupCamera vec3(0, 200, (oy)), vec3((ox), 0, 0), vec3(0, 1, 0)
End If
End Sub
Private Sub Picture1_MouseUp(Button As Integer, Shift As Integer, x As Single, y As Single)
dwn% = False
End Sub
Shadows

Well things are hotting up. We now have bouncing, colliding
bugs. But with our overhead light, it gets hard to see exactly
where our bugs are. The idea of adding a shadow allows the user
to see intuitively the position of the bug when it is off the
ground.
We essentially cast a spherical shadow from the bugs centre of
mass down to the floor. The size and intensity of the shadow
will vary in proximity to the floor.
'draw a shadow..
Dim sz As Double
Dim cl As Long
ReDim m_Verts(num_body * 6)
For i = 0 To num_body - 1
If body(i).Part_Max - body(i).Part_Min = 7 Then 'the primitive is a cube...
sz = join_rl(body(i).Join_Min + 1) / Log(body(i).centre_mass.y + 0.01)
cl = body(i).centre_mass.y
If cl < 100 Then cl = 100
If cl > 255 Then cl = 255
cl = RGB(cl, cl, cl)
'project the surface down the y axis...
m_Verts(i * 6 + 0).v.x = body(i).centre_mass.x - sz
m_Verts(i * 6 + 0).v.z = body(i).centre_mass.z - sz
m_Verts(i * 6 + 0).v.y = 1
m_Verts(i * 6 + 0).n.y = 1
m_Verts(i * 6 + 0).tu = 0
m_Verts(i * 6 + 0).tv = 0
m_Verts(i * 6 + 0).color = cl
m_Verts(i * 6 + 1).v.x = body(i).centre_mass.x - sz
m_Verts(i * 6 + 1).v.z = body(i).centre_mass.z + sz
m_Verts(i * 6 + 1).v.y = 1
m_Verts(i * 6 + 1).n.y = 1
m_Verts(i * 6 + 1).tu = 0
m_Verts(i * 6 + 1).tv = 1
m_Verts(i * 6 + 1).color = cl
m_Verts(i * 6 + 2).v.x = body(i).centre_mass.x + sz
m_Verts(i * 6 + 2).v.z = body(i).centre_mass.z + sz
m_Verts(i * 6 + 2).v.y = 1
m_Verts(i * 6 + 2).n.y = 1
m_Verts(i * 6 + 2).tu = 1
m_Verts(i * 6 + 2).tv = 1
m_Verts(i * 6 + 2).color = cl
m_Verts(i * 6 + 3).v.x = body(i).centre_mass.x - sz
m_Verts(i * 6 + 3).v.z = body(i).centre_mass.z - sz
m_Verts(i * 6 + 3).v.y = 1
m_Verts(i * 6 + 3).n.y = 1
m_Verts(i * 6 + 3).tu = 0
m_Verts(i * 6 + 3).tv = 0
m_Verts(i * 6 + 3).color = cl
m_Verts(i * 6 + 4).v.x = body(i).centre_mass.x + sz
m_Verts(i * 6 + 4).v.z = body(i).centre_mass.z + sz
m_Verts(i * 6 + 4).v.y = 1
m_Verts(i * 6 + 4).n.y = 1
m_Verts(i * 6 + 4).tu = 1
m_Verts(i * 6 + 4).tv = 1
m_Verts(i * 6 + 4).color = cl
m_Verts(i * 6 + 5).v.x = body(i).centre_mass.x + sz
m_Verts(i * 6 + 5).v.z = body(i).centre_mass.z - sz
m_Verts(i * 6 + 5).v.y = 1
m_Verts(i * 6 + 5).n.y = 1
m_Verts(i * 6 + 5).tu = 1
m_Verts(i * 6 + 5).tv = 0
m_Verts(i * 6 + 5).color = cl
End If
Next
g_dev.SetTexture 0, m_ShadowTexture
g_dev.SetRenderState D3DRS_ALPHABLENDENABLE, 1
g_dev.SetTextureStageState 0, D3DTSS_ALPHAOP, D3DTOP_SUBTRACT
g_dev.SetRenderState D3DRS_SRCBLEND, D3DBLEND_SRCALPHA
g_dev.SetRenderState D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA
g_dev.SetVertexShader D3DFVF_COLORVERTEX
g_dev.SetStreamSource 0, m_VertB, Len(v)
D3DVertexBuffer8SetData m_VertB, 0, Len(v) * (num_body) * 6, 0, m_Verts(0)
g_dev.DrawPrimitive D3DPT_TRIANGLELIST, 0, (num_body) * 2
This code sets up the ‘quad’ that is used to draw the
shadow. A quad is simply two triangles that are next to each
other to form a square (4 corners = quad).
The blend colour intensity and size of the quad is determined by
the proximity to the ground.
Driving
It’s time to get control of our bugs. There is no fun in a
totally passive simulation. We want to drive our bugs around
our scene to test out our collision algorithms.

To enable us to trap the state of multiple keys, I use a key
array state trick. This works like this;
For any key we can now check if it is currently held
down. The code that the bug actually responds to is in the
check_keys routines. It looks something like this;
Private Sub check_keys()
Dim v As D3DVECTOR
Dim v2 As D3DVECTOR
Dim m As D3DMATRIX
Dim k As Long
If keyarray(vbKeyLeft) Then
D3DXMatrixIdentity m
D3DXMatrixRotationY m, -10 * m_timeStep
For k = 1 To 88
D3DXVec3Subtract v, m_curp(k), body(0).centre_mass
'v is now relative to the centre of the box
D3DXVec3TransformNormal v, v, m
'now put it back
D3DXVec3Add m_curp(k), v, body(0).centre_mass
'update the old particle position
m_oldp(k).x = m_curp(k).x
m_oldp(k).z = m_curp(k).z
Next
End If
If keyarray(vbKeyRight) Then
D3DXMatrixIdentity m
D3DXMatrixRotationY m, 10 * m_timeStep
For k = 1 To 8
D3DXVec3Subtract v, m_curp(k), body(0).centre_mass
'v is now relative to the centre of the box
D3DXVec3TransformNormal v, v, m
'now put it back
D3DXVec3Add m_curp(k), v, body(0).centre_mass
m_oldp(k) = m_curp(k)
Next
End If
If keyarray(vbKeyUp) Then
D3DXVec3Subtract v, m_curp(5), m_curp(1)
D3DXVec3Scale v2, v, -19.9 * m_timeStep
D3DXVec3Scale v, v, -20 * m_timeStep
v.y = 0
v2.y = 0
If m_curp(1).y > m_curp(2).y Then
'whoops upside down..
m_curp(1).y = m_curp(1).y + (m_curp(1).y - m_curp(2).y) / 50
m_curp(4).y = m_curp(4).y + (m_curp(1).y - m_curp(2).y) / 50
End If
For k = 1 To 8
D3DXVec3Add m_curp(k), m_curp(k), v
D3DXVec3Add m_oldp(k), m_oldp(k), v2
Next
End If
If keyarray(vbKeyDown) Then
D3DXVec3Subtract v, m_curp(5), m_curp(1)
D3DXVec3Scale v2, v, 19.9 * m_timeStep
D3DXVec3Scale v, v, 20 * m_timeStep
v.y = 0
v2.y = 0
For k = 1 To 8
D3DXVec3Add m_curp(k), m_curp(k), v
D3DXVec3Add m_oldp(k), m_oldp(k), v2
Next
End If
End Sub
The end result is that the bug moves forward in the
direction the bug is pointing. Pressing the right or left keys
will rotate the bug slightly in that direction.
Camera
Hmm, I was busy trying to knock another bug off the map, and I
ran off-screen. It’s hard to control your bug when you can’t
see it. Definitely time to add camera control.

As you can see from the picture, the bug stays on screen, as the
rest of the scene moves around.
In-fact we ended up with 3 camera modes. This one is
camera_mode 2, the far away camera that pans to follow our bug.
This is possibly the simplist. We calculate the vector between
the distant camera and the bug, and then point the camera in
that direction.
In mode 1 we move the camera around to follow the bug. It does
this with a slight delay. This way the camera essentially drags
along behind the bug.
Finally in mode 0 (first bug mode) the camera is always mounted
on the front of our bugs antennae. The hardest part of this was
finding a small enough camera.
Source code;
'position the camera
If camera_mode = 0 Then
'first bug mode...
D3DXVec3Subtract vec, m_curp(4), m_curp(1)
D3DXVec3Scale vec, vec, 0.5
D3DXVec3Add vec_to, m_curp(6), vec
D3DXVec3Add vec_from, m_curp(2), vec
D3DUtil_SetupCamera vec_to, vec_from, vec3(0, 1, 0)
ElseIf camera_mode = 1 Then
'3rd bug mode
D3DXVec3Subtract vec, vec_from, body(0).centre_mass
D3DXVec3Scale vec, vec, 0.25
D3DXVec3Subtract vec_to, vec_from, vec
D3DUtil_SetupCamera vec_from, vec_to, vec3(0, 1, 0)
'animate the vec_from...
D3DXVec3Normalize vec, vec
D3DXVec3Scale vec, vec, 60
D3DXVec3Add vec, body(0).centre_mass, vec
vec.y = vec.y + 25
D3DXVec3Lerp vec_from, vec_from, vec, 0.02
ElseIf camera_mode = 2 Then
'far away camera...
vec_from = vec3(0, 120, -400)
D3DXVec3Subtract vec, vec_from, body(0).centre_mass
D3DXVec3Scale vec, vec, 0.25
D3DXVec3Subtract vec_to, vec_from, vec
D3DUtil_SetupCamera vec_from, vec_to, vec3(0, 1, 0)
End If
Animation
Potentially the most disconcerting part of the current tutorial
is the way the bugs are simply gliding along on the concrete
floor. It’s time to introduce some animation.
I originally built and exported some basic keyframe animation
for the legs of the bugs.
To reproduce this animation in VB I found a few problems with
some of the class libraries provided, but it all worked out in
the end.

The end result is we load the animation into the frame like so;
'load our mesh...
Set m_frame = New CD3DFrame
Set Animation = New CD3DAnimation
m_frame.InitFromFile g_dev, App.path + "\bug.x", Nothing, Animation
Then we set which animation to use like so;
Animation.SetTime Int(anim) - 0.00005
The end result is by increasing the animation counter we
can proceed to cycle through our animation, and the legs wiggle.
Final
Download final sample code 559k
Well here we go, the final application. I decided to add a nice
‘help screen’ billboard with a bit of alpha-blending to make it
look interesting. (notice how the bug in the bottom left shows
through)

This is drawn using another quad. I should probably put this
into a helper function to save wasting lines of code, but here
it is anyway : )
'draw the help...
If m_help Then
g_dev.SetTexture 0, m_HelpTexture
D3DXMatrixLookAtLH g_viewMatrix, vec3(0, 12, -30),
vec3(Cos(t / 1000), 10 + Sin(t / 1000), 0), vec3(0, 1, 0)
g_dev.SetTransform D3DTS_VIEW, g_viewMatrix
m_Verts(0).v.x = 10
m_Verts(0).v.z = -5
m_Verts(0).v.y = 0
m_Verts(0).n.z = 1
m_Verts(0).tu = 1
m_Verts(0).tv = 1
m_Verts(0).color = RGB(255, 255, 255)
m_Verts(1).v.x = -10
m_Verts(1).v.z = -5
m_Verts(1).v.y = 0
m_Verts(1).n.z = 1
m_Verts(1).tu = 0
m_Verts(1).tv = 1
m_Verts(1).color = RGB(255, 255, 255)
m_Verts(2).v.x = 10
m_Verts(2).v.z = -5
m_Verts(2).v.y = 20
m_Verts(2).n.z = 1
m_Verts(2).tu = 1
m_Verts(2).tv = 0
m_Verts(2).color = RGB(255, 255, 255)
m_Verts(3).v.x = 10
m_Verts(3).v.z = -5
m_Verts(3).v.y = 20
m_Verts(3).n.z = 1
m_Verts(3).tu = 1
m_Verts(3).tv = 0
m_Verts(3).color = RGB(255, 255, 255)
m_Verts(4).v.x = -10
m_Verts(4).v.z = -5
m_Verts(4).v.y = 0
m_Verts(4).n.z = 1
m_Verts(4).tu = 0
m_Verts(4).tv = 1
m_Verts(4).color = RGB(255, 255, 255)
m_Verts(5).v.x = -10
m_Verts(5).v.z = -5
m_Verts(5).v.y = 20
m_Verts(5).n.z = 1
m_Verts(5).tu = 0
m_Verts(5).tv = 0
m_Verts(5).color = RGB(255, 255, 255)
g_dev.SetRenderState D3DRS_ALPHABLENDENABLE, 1
g_dev.SetTextureStageState 0, D3DTSS_ALPHAOP, D3DTOP_SUBTRACT
g_dev.SetRenderState D3DRS_SRCBLEND, D3DBLEND_SRCALPHA
g_dev.SetRenderState D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA
g_dev.SetVertexShader D3DFVF_COLORVERTEX
g_dev.SetStreamSource 0, m_VertB, Len(v)
D3DVertexBuffer8SetData m_VertB, 0, Len(v) * 6, 0, m_Verts(0)
g_dev.DrawPrimitive D3DPT_TRIANGLELIST, 0, 2
End If