Vi skal i denne tutorial lære hvordan du kan få fram en spinnende kube på skjermen ved bruk av DirectX på en Windows Mobile 5.0 enhet.
Siden den er myntet på nybegynneren, forventes det lite av forkunnskaper i DirectX, Visual Basic og .NET Compact Framework som vi skal programmere i.
Du trenger:
- Visual Studio 2005. Dette kan være standard, professional eller team system, men ikke express.
- Windows Mobile 5 SDK for Pocket PC
- Active Sync/Mobile Device Center
- .NET Compact Framework 2.0
- En Windows Mobile 5 enhet.
Du kan laste ned trial versjon av Visual Studio, .NET CF 2.0, WM 5.0 SDK og Active Sync fra: www.msdn.com/downloads
Start Visual Studio og velg Create Project.
Velg så "Visual Basic" -> "Smart Device" -> "Windows Mobile 5.0 Pocket PC" -> "Device Application". Vi kan gi den navnet "MD3DMTutorial". Trykk på "OK".
Merk "Form1.vb" i Solution Explorer og trykk "View code" knappen eller trykk"F7".
Det er her vi skal skrive all koden vår.
For at Visual Studio skal snakke med DirectX må vi legge til en "referanse" til DirectX.
Høyreklikk på "MD3DMTutorial" i Solution Explorer og velg "Add Reference". Bla nedover i listen til du finner "Microsoft.WindowsMobile.DirectX" og trykk "OK".
Så må vi velge vår utviklingsplatform, nemlig "Windows Mobile 5.0 Pocket PC Device" fra nedtrekksmenyen øverst til venstre i skjermbildet. NB! Menyen kan ligge andre steder på din kopi av Visual Studio.
Tilbake til koden vår... Vi har nå en klasse som heter Form1. Skriv "Imports" setninger til DirectX så vi slipper å skrive det fulle namespacet hver gang vi skal skrive en DirectX funksjon.
Det første vi nå trenger er en Direct3D device. Koden ser nå sånn ut:
Imports Microsoft.WindowsMobile.DirectXImports
Microsoft.WindowsMobile.DirectX.Direct3D
Public Class Form1
Dim _device As Direct3D.Device
End Class
Nå skal vi lage et D3D vindu som vi kan se på skjermen. Da må vi først lage device objektet og så fortelle vår device om hva slags vindu dette er. Vi gjør dette med å mate den med parametere i form av "Presentation Parameters".
La oss lage vår device:
Sub LagDevice()
Dim pp As New Direct3D.PresentParameters()
pp.Windowed = True
pp.SwapEffect = Direct3D.SwapEffect.Discard
_device = New Device(0, DeviceType.Default, Me, CreateFlags.None, pp)
_device.RenderState.Lighting = False
End Sub
Her har vi valgt at vi skal ha et vanlig vindu og ikke full skjerm. Vi har også valgt å slå av DirectX sin håndtering av Lighting. Neste setning sier at når et nytt bilde blir vist på skjermen så blir det gamle fjernet.
Merk et sted i klassen og skriv "sub new" og trykk enter.
Da vil konstruktøren bli laget for deg. Etter InitializeComponent() kaller du opp metoden vår med å skrive "LagDevice()"
Rett før "End Class" skriver du: "Protected Overrides OnPaint" og trykk "TAB".
Da vil OnPaint metoden bli laget for deg, og skriv følgende inn i metoden så den ser sånn ut:
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
_device.Clear(ClearFlags.Target, Color.Blue, 1.0F, 0)
_device.BeginScene()
_device.EndScene()
_device.Present()
Me.Invalidate()
End Sub
I OnPaint nuller vi ut skjermbildet vårt med en blå farge, deklarerer en BeginScene() og EndScene(), og så til sist en Present() kommando. En ting å være klar over er at vi hele tiden jobber mot et "buffer" som vi ikke kan se. Det er først når vi forteller vår direct3D device at den kan presentere innholdet, at vi kan se det. Dette gjøres med _device.Present()
Det som skal stå mellom _device.BeginScene() og _device.EndScene() skal være koden vi bruker til å tegne kuben. Dette kommer vi tilbake til.
Til slutt står det Me.Invalidate(). Dette oppdaterer vinduet vårt, som betyr at vi kaller kontinuerlig opp Paint eventen vår. Da har vi vår "rendering loop" oppe og går.
Gjør det samme med "Protected Overrides OnPaintBackground". Sett en ' (kommentar tegn) foran MyBase.OnPaintBackground(e). Dette forhindrer programmet i å oppdatere bakgrunnen til formen. Da unngår vi flimring på skjermen.
Nå skal vi lage vår kube, som vi etterpå skal få litt fart på. Rett før "Sub New()" skriver du:
Dim _box As Mesh
Etter at vi har kalt opp "LagDevice()" i konstruktøren, lager vi kube objektet vårt med:
_box = Mesh.Box(_device, 1, 1, 1)
Det var det, nå har vi en kube med "1" i lengde, bredde og høyde.
For at vi skal kunne se den, må vi instruere DirectX om hvordan vi skal se vår 3d verden.
Dette gjøres ved å sette såkalte "Transforms", og hver av dem er en matrise.
Så hva er en matrise?
Dette er en mengde med tall, normalt en 4x4 array. Denne skal ganges med en annen matrise for at vi skal få det resultat vi ønsker. I DirectX vil det si at hvis vi f. eks vil snu oss rundt, så ganger vi world matrix med riktig rotation matrix, for å få den nye posisjonen.
Så det fins tre Transforms som vi må sette:
- World Transform - Beskriver hvor kuben vår er.
_device.Transform.World = Matrix.Identity
Matrix.Identity betyr en matrise som ikke forandrer noe. Akkurat som 2 * 1 = 2, så er world transform matrix * Identity matrix = world transform matrix.
- View Transform - Beskriver hvor kameraet er
Heldigvis gjør DirectX dette lett for oss. Nå skal vi lage en matrise som skal beskrive hvor i verden kameraet vårt er.
_device.Transform.View = Matrix.LookAtLH( _
New Vector3(0.0F, 0.0F, -3.0F), _
New Vector3(0.0F, 0.0F, 0.0F), _
New Vector3(0.0F, 1.0F, 0.0F))
End Sub
Det er en funksjon I Matrix klassen som heter LookAtLH som lager den matrisen vi trenger. Den har tre parametere, og alle er av typen Vector3.
Før vi går videre må vi forklare hva en vektor er. En Vector3 er en type som inneholder 3 floats. I 3d grafikk bruker vi hovedsakelig disse 3 verdiene til å beskrive en av to ting:
1. Vektoren har en lengde og retning, men vi vet ikke hvor i verden den er.
2. Vektoren beskriver et spesifikt punkt i 3d universet vårt.
Vektoren er som regel i formatet (x, y, z).
Tilbake til vår Matrix.LookAtLH funksjon, den første vektoren i parameter listen er en vektor som sier hvor kameraet befinner seg. Her har vi plassert kameraet 3 units tilbake i Z retning. Kameraet har da plassert seg lenger vekk fra kuben vår som ligger i 0,0,0.
Den neste vektoren sier hvilket punkt kameraet skal se på. Her skal kameraet se på kuben vår i 0,0,0.
Siste vektoren skal beskrive hva som er opp til kameraet vårt, og vi sier at opp er en positiv y nemlig (0,1,0).
- Projection Transform - Hvordan vi ser verden.
Denne trenger vi ikke legge så mye vekt på, bare at den gjør oss å i stand til å se vår 3d verden på skjermen, som er 2D.
_device.Transform.Projection = _
Matrix.PerspectiveFovLH(Math.PI / 4.0, _
1.0F, 1.0F, 100.0F)
Vi setter disse inn I en funksjon som vi kaller for SettMatriser:
Sub SettMatriser()
_device.Transform.World = Matrix.Identity
_device.Transform.View = Matrix.LookAtLH( _
New Vector3(0.0F, 0.0F, -3.0F), _
New Vector3(0.0F, 0.0F, 0.0F), _
New Vector3(0.0F, 1.0F, 0.0F))
_device.Transform.Projection = _
Matrix.PerspectiveFovLH(Math.PI / 4.0, _
1.0F, 1.0F, 100.0F)
End Sub
Denne kan vi kalle opp etter LagDevice() i konstruktøren.
Nå som vi har satt matrisene våre kan vi tegne boksen vår. Mellom _device.BeginScene() og _device.EndScene() i OnPaint, skriver vi:
_box.DrawSubset(0)
Parameteren 0 betyr bare at vi skal tegne det første "subset" i meshen. Vi har bare ett subset i vår mesh.
For å rotere boksen, ganger vi vår world matrise med en rotasjons matrise sånn:
_device.Transform.World *= Matrix.RotationY(0.02F)Dette skriver du rett før
_box.DrawSubset(0)
Nå kan du kjøre programmet med "F5" eller trykke på den grønne pilen.
På enheten vil du nå se en spinnende hvit kube på en blå bakgrunn:
For de av dere som har erfaring med DirectX på PC'en vil nok si at det ikke er mye som er forskjellig her. Det er det heller ikke, og DirectX på Windows Mobile ligner mye på DirectX 8 når det gjelder funksjonalitet. Noen av den mest avanserte funksjonaliteten mangler, og det fins enda ikke støtte for pixel og vertex shaders.
En annen forskjell er at det lønner seg å bruke "fixed" tall istedenfor floats/doubles. Dette fordi ARM prosessoren, som de fleste håndholdte kjører på, har ikke FPU (Floating Point Unit).
Da kan du bruke Vector3Fixed og MatrixFixed i stedet for Vector3 og Matrix.
F.eks:
_device.Transform.ViewFixed = New MatrixFixed(Matrix.LookAtLH( _
New Vector3(0.0F, 0.0F, -3.0F), _
New Vector3(0.0F, 0.0F, 0.0F), _
New Vector3(0.0F, 1.0F, 0.0F)))
Gratulerer med overstått tutorial!
Jeg har prøvd å gjøre denne tutorial så enkel som mulig med så lite kode som mulig, så det er bl.a ikke tatt hensyn til hva som skjer ved en device reset og generell error handling.
Prosjektet kan lastes ned her: MD3DMTutorial.zip