Robot
Konvertering og lasting av modeller
Da det var klart at jeg ville bruke tegnede 3D modeller begynte jeg se etter modeller av roboten. Roboten som str ved IR avdelingen i Sarpsborg er fra 92-93, s denne var det ikke s mye data og finne om p ABB sine hjemmesider. Valget falt derfor p en nyere og mer sofistikert robot. Denne modellen var det allerede laget CAD modeller av (produksjonstegninger). Disse tegningene var tilgjengelig i en mengde formater og mange av verktyene for behandlig av disse er desverre ekstremt bde dyre og avanserte. Etter ha testet omtrent de shareware versjonene som finnes av CAD programvare, fant jeg endelig programmet "Rhinoceros" som kunne laste en CAD modell i fra formatet *.igs. Dette programmet kunne ogs eksportere geometrien som en 3D Studio MAX eller LightWave fil.
Likevel var det mer konvertering som mtte til, da 3D Studio MAX og Lightwave har relativt komplekse format p sine filer ville jeg ndig mtte parse dette formatet direkte. Neste oppgave var da finne et egnet filformat for innlesing i programmet, da flesteparten av disse formatene (3D Studio Max og Lightwave) er vanskelige lese inn, og er ofte properitre eller uten tilgjengelig dokumentasjon. Et standard ASCII filformat ville vre ideelt for innlesing og parsing via Java, og i min sken fant jeg etter en stund programmet MilkShape. Programmet sttter importering av mange kjente filformat og kan eksportere til et relativt enkelt ASCII format som dessuten var dokumentert godt. Milkshape er et enkelt modelleringsprogram opprinnelig utviklet for kunne behandle modeller fra spillet Half-Life. Programmet har etter hvert ftt sttte for mange flere formater og foretrekkes i dag av svrt mange p.g.a. lav pris og at det er enkelt i bruk. Milkshape har som de fleste andre 3D programmer støtte for å sette materialegenskaper og lysegenskaper, og har også støtte for teksturer. På denne måten kan man gjøre seg ferdig med modellen i 3D programmet , og så bruke geometri, materialegenskaper og teksturnavn fra ASCII fila for tegning av modellen i OpenGL senere. Det skal nevnes at modellene brukt er fra byggetegninger og derfor er svrt detaljerte og relativt tunge rendre i OpenGL uten et skikkelig grafikk-kort.
Milkshape etter ha lastet inn en modell av en av delene av roboten.
Etter ha forsttt formatet var det finne en grei mte representere modellene internt som objekter i programmet. En scene bestr av en eller flere modeller, som igjen har en eller flere mesh'er, som igjen kan ha et materiale, som igjen kan bestr av overflateverdier for refleksjon, farge osv og evt en tekstur. Det viser seg at det blir et lite hierarki av objekter. For mest mulig oversiktelig kunne bygge opp en scene ble det derfor utviklet en klasser som representerer disse.
Oversikt over hvordan en scene er bygget opp i programmet.
Videre ble det laget en egen Texture klasse. Jeg valgte PNG som format for teksturer. Texture klassen kan bygges ut til sttte f.eks BMP og JPG, men jeg har ikke tatt meg tid til dette. Da det er godt mulig at flere modeller bruker de samme texturene ble det implementert en teksturliste slik at man kan dele tilgang til en tekstur. Dette gjr at man unngr lasting av en tekstur flere ganger, noe som igjen frer til raskere oppstart og sparer minne.
Merk: Teksturene ligger under 'program/build/data/textures'. Modellene ligger under 'program/build/data/models'.
Tegning av roboten
Nr geometrien for delene av roboten er lest inn er neste steg tegne opp modellen p skjermen. Siden det er rimelig komplekse modeller som skal tegnes blir det mange triangler tegne. tegne alt p via kode hver gang eller legge det i en "display list", skal visstnok ikke ha noen betydning for ytelsen p moderne grafikk-kort. Likevel merket jeg i JOGL en 30-40% ytelseskning ved bruke display lister. Dette kan komme av at display listene blir bufret og tegnet via native kode i JOGl, og man slipper bruke Java's minnehndtering og iterasjonsmetoder ved opptegning.
Nr programmet starter opp laster det inn alle modeller og lager en liste over hvilke teksturer som m lastes inn. Nr alle modellene er lastet inn, er neste steg laste inn alle ndvendige teksturer og gi referansene til disse til hvert Mesh objekt som bruker den gitte teksturen. Neste steg er lage display listeene for hver modell som er nevnt i avsnittet over. Scene objektet holder oversikt over hvilken ID modellen har i display lista internt i OpenGL og man kan hente ut denne nr man vil tegne opp en modell i OpenGL.
I kode vil dette se slik ut:
// Load models String modelPath = "data/models/"; String[] modelFiles = new String[] { "foot.txt", "rotation.txt", "lower_arm.txt" "elbow.txt", "upper_arm.txt", "floor.txt", "mount.txt" }; // Init scene this.scene = new Scene(); for (int i = 0; i < modelFiles.length; i++) { this.scene.addModel(new MilkShapeModel(modelPath + modelFiles[i], modelFiles[i])); }
N nr alle modellene er lastet inn (ogs teksturer), s kan vi gjre klar scenen til rendering. Dette gjres ved kalle "prepareScene" i Scene klassen fra init i OpenGL:
public void init (GLDrawable drawable) { // Save references this.gl = drawable.getGL(); this.glu = drawable.getGLU(); this.glDrawable = drawable; // Prepare scene this.scene.prepareScene(gl, glu);
Dette gjr at displaylistene blir generert og OpenGL id-ene til listene blir lagret i Scene objektet slik at vi kan finne tilbake til riktig liste for en modell nr vi skal tegne opp modellen. Opptegningen av roboten utfres p denne mten:
public void display(GLDrawable drawable) { // Clear scene gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); // Reset view gl.glLoadIdentity(); // Update view if user has rotated or zoomed the scene gl.glTranslatef(0.0f, -12.0f, zoom); gl.glRotated(xrot, 1.0, 0.0, 0.0); gl.glRotated(yrot, 0.0, 1.0, 0.0); // Draw models // Draw robot mount gl.glPushMatrix(); gl.glTranslatef(-7.2f, 0f, -4.6f); gl.glScalef(0.3f, 0.3f, 0.4f); drawModel(scene.getDisplayListIndex("mount.txt"), gl); // Foot gl.glPopMatrix(); gl.glPushMatrix(); drawModel(scene.getDisplayListIndex("foot.txt"), gl); // Rotation model gl.glTranslatef(0.0f, 2.0f, 0.0f); gl.glRotatef(robotStatus.getAxisAngle(0), 0.0f, 1.0f, 0.0f); drawModel(scene.getDisplayListIndex("rotation.txt"), gl); // Lower arm gl.glTranslatef(3.3f, 5.8f, 1.35f); gl.glRotatef(robotStatus.getAxisAngle(1), 0.0f, 0.0f, 1.0f); drawModel(scene.getDisplayListIndex("lower_arm.txt"), gl); ...
Kontroll av roboten
Roboten kan kontrolleres p to mter. Enten helt manuelt via GUI, eller den kan forhndsprogrammeres til utfre en serie bevegelser. Manuell kontroll av roboten skulle vre ganske intuitivt, s jeg konsentrer meg om automatisk kjring. Dette utfres ved at roboten leser en XML fil med spesifikasjoner for hvilke bevegelser som skal utfres ved forskjellige tidspunkt. Alle ledd kan beveges uavhengig av hverandre til samme tid, med forskjellige hastigheter hvis det skulle vre nskelig. Filen har et meget enkelt format, og forklares best med et eksempel:
<?xml version="1.0" encoding="UTF-8"?> <robotprogram axisCount="4"> <move time="2000"> <axis id="0" abs="0">-40.0</axis> <axis id="1" abs="0">-80.0</axis> <axis id="2" abs="0">80.0</axis> <axis id="3" abs="0">180.0,-180.0</axis> </move> <move time="2000"> <axis id="0" abs="0">40.0</axis> <axis id="1" abs="0">80.0</axis> <axis id="2" abs="0">-80.0</axis> <axis id="3" abs="0">-180.0,180.0</axis> </move> </robotprogram>
Filen begynner med spesifisere hvor mange akser roboten har (axisCount="4"). Deretter kommer robotens frste sammensatte bevegelse: Akse 0 skal roteres -40 grader, akse 1 skal rotere -80 grader osv. Alt dette skal utfres p tiden som er angitt med attributtet "time" i "move" elementet, i dette tilfellet 2000 millisekunder. Attributtet "abs" angir om det er relative eller absolutte posisjoner for aksene. Forelpig er bare relative posisjoner implementert.
Nr roboten skal utfre et slikt "program" laster den frst inn hele fila i minnet og deler opp bevegelsen i sm fragmenter som legges i et multidimensjonalt array. Nr programmet s er ferdig bearbeidet, kan det enkelt utfres med en enkel program trd, da alle aksebevegelser allerede er kalkulert. Desverre finnes det ikke noen editor for lage fila automatisk via f.eks. en makro opptaker. Dette hper jeg f implementert i en fremtidig versjon.
Konklusjon og videre arbeid
Kravet til prosjektet som ble satt ved kursets start, burde vre oppfylt med dette. Likevel er det fortsatt rom for forbedringer og utvidelser. se en robot utfre "ekte" oppgaver er selvsagt fristende, men jeg har ikke latt roboten f noen spesifikk oppgave, da f.eks. flytting av gjenstander ville mtte hardkodes sammen med bevegelsen p roboten. Dette kun vrt lst med implementasjon av fysiske lover, slik at objekter som kasser, esker osv. vil falle ned p gulvet, og at man kan dytte ting unna hvis de ikke er for tunge og/eller har for stor friksjon. Dette er en stor oppgave i seg selv, og jeg har ikke hatt tid til mer enn s vidt lage et "skall" for hvordan slik funksjonalitet kan implementeres. Prosjektet har blitt relativt solid, og burde vre en grei lsning bygge videre p. Det har blitt en del kode (ca. 3000 linjer), men koden er godt kommentert og skulle vre mulig orientere seg i. Mange av klassene kan brukes til annet enn robot animasjoner. Klassene er oppdelt i "packages" etter hvilken funksjonalitet de har, mye likt Java's eget klasse bibliotek.
Videre vil det som allerede nevnt vre nskelig lage en makro opptaker som kunne ta opp/redigere alle manuelle bevegelser utfrt med roboten, slik at de kan lagres og spilles av senere som et vanlig XML "program". Siden koden for innlasting av nye modeller n er spass enkel, burde det kunne g an bygge et noenlunde troverdig milj der en eller flere roboter arbeider.