I have designed this introductory tutorial as a first tutorial for a 3rd year games course I am running in February 1998. Whilst navigating my way through the PSX documentation, the demo programs, and the news groups I often wished (like I suspect many Yaroze newcomers do!) that I had a tutorial like this which would just tell me the bits I needed to get me programming in 3D from the bottom up rather that struggling with incomprehensible functions and sophisticated but large example programs. So I hope this will be of use to others of you out there and I only ask that you protect my copyright to this document, and if you use it for teaching that you let me know. The document is quite long and in a draft state but tells much of what you need to know to get going in 3D. It does not deal with sprites at all, which will be the subject of a further tutorial I may make available here but note that Ira Rainey has already are some good tutorials on 2D which can be found at ~shadow/ftp on the Yaroze server. I welcome your feedback and (constructive!) criticism so if you wish to contact me, email me at p.passmore@mdx.ac.uk. -------------------------------- UPDATE 1/12/97 #defined functions now bracketed to be ANSI C (& Code warrior compatible) - thanks to US Yarozer Matthew Hulett for pointing this out. NTSC users note that the video mode should be set to NTSC rather than PAL and the screen width and height set to an appropriate NTSC mode (eg: 320x240) - see page 10. Step 3 now includes an updated main function in the text, and function prototypes. -------------------------------- UPDATE 9/12/97 Variable matTmp defined as a matrix instead of a pointer to a matrix in functions AdvanceModel and RotateModel. Thanks to Craig Graham and Stefano Provenzano for catching this one. Tutorial programs have been updated accordingly. COM3311 Programming Interactive Graphical Systems Net Yaroze Tutorial TOC \o "1-2" INTRODUCTION GOTOBUTTON _Toc405642428 PAGEREF _Toc405642428 4 Acknowledgements GOTOBUTTON _Toc405642429 PAGEREF _Toc405642429 4 Aim. GOTOBUTTON _Toc405642430 PAGEREF _Toc405642430 4 The development process. GOTOBUTTON _Toc405642431 PAGEREF _Toc405642431 4 Makefiles and the gcc compiler. GOTOBUTTON _Toc405642432 PAGEREF _Toc405642432 5 STEP 1. Hello world on the PC. GOTOBUTTON _Toc405642433 PAGEREF _Toc405642433 6 The SIOCONS communications software. GOTOBUTTON _Toc405642434 PAGEREF _Toc405642434 6 STEP 2. Hello world on the PSX and double buffering. GOTOBUTTON _Toc405642435 PAGEREF _Toc405642435 7 An aside on PSX datastructures GOTOBUTTON _Toc405642436 PAGEREF _Toc405642436 7 Ordering Tables and Ordering Table Headers. GOTOBUTTON _Toc405642437 PAGEREF _Toc405642437 8 Packets GOTOBUTTON _Toc405642438 PAGEREF _Toc405642438 8 Reading the Joypad GOTOBUTTON _Toc405642439 PAGEREF _Toc405642439 9 STEP 3. Load and view a 3D model. GOTOBUTTON _Toc405642440 PAGEREF _Toc405642440 11 Loading a 3D object. GOTOBUTTON _Toc405642441 PAGEREF _Toc405642441 11 Assigning your model an address in memory. GOTOBUTTON _Toc405642442 PAGEREF _Toc405642442 12 Function prototypes. GOTOBUTTON _Toc405642443 PAGEREF _Toc405642443 12 Creating a player struct to hold your model. GOTOBUTTON _Toc405642444 PAGEREF _Toc405642444 13 Initialising the player struct. GOTOBUTTON _Toc405642445 PAGEREF _Toc405642445 14 Setting up lighting. GOTOBUTTON _Toc405642446 PAGEREF _Toc405642446 15 Setting up a viewing system. GOTOBUTTON _Toc405642447 PAGEREF _Toc405642447 16 Drawing the player. GOTOBUTTON _Toc405642448 PAGEREF _Toc405642448 17 Update the main function. GOTOBUTTON _Toc405642449 PAGEREF _Toc405642449 17 STEP 4. Transforming a 3D model. GOTOBUTTON _Toc405642450 PAGEREF _Toc405642450 18 Making the model move. GOTOBUTTON _Toc405642451 PAGEREF _Toc405642451 18 Understanding the MATRIX struct. GOTOBUTTON _Toc405642452 PAGEREF _Toc405642452 19 The specification of angles for rotation. GOTOBUTTON _Toc405642453 PAGEREF _Toc405642453 19 Rotating an object. GOTOBUTTON _Toc405642454 PAGEREF _Toc405642454 19 STEP 5. Odds and sods. GOTOBUTTON _Toc405642455 PAGEREF _Toc405642455 20 A joypad function. GOTOBUTTON _Toc405642456 PAGEREF _Toc405642456 20 Hierarchical coordinate systems. GOTOBUTTON _Toc405642457 PAGEREF _Toc405642457 21 Approximating the frame rate. GOTOBUTTON _Toc405642458 PAGEREF _Toc405642458 22 STEP 6. Precision problems. GOTOBUTTON _Toc405642459 PAGEREF _Toc405642459 23 Precision problems already. GOTOBUTTON _Toc405642460 PAGEREF _Toc405642460 23 A new rotate function. GOTOBUTTON _Toc405642461 PAGEREF _Toc405642461 23 STEP 7. Translating in the correct direction. GOTOBUTTON _Toc405642462 PAGEREF _Toc405642462 24 Making the car move in the direction it is pointing. GOTOBUTTON _Toc405642463 PAGEREF _Toc405642463 24 STEP 8. Texture mapping. GOTOBUTTON _Toc405642464 PAGEREF _Toc405642464 26 Introduction to texture mapping. GOTOBUTTON _Toc405642465 PAGEREF _Toc405642465 26 Loading a texture into memory. GOTOBUTTON _Toc405642466 PAGEREF _Toc405642466 27 Making your own texture files. GOTOBUTTON _Toc405642467 PAGEREF _Toc405642467 29 STEP 9. 3D Modelling GOTOBUTTON _Toc405642468 PAGEREF _Toc405642468 30 Making your own model. GOTOBUTTON _Toc405642469 PAGEREF _Toc405642469 30 Converting from dxf to rsd. GOTOBUTTON _Toc405642470 PAGEREF _Toc405642470 30 Assigning texture to the quadrilateral. GOTOBUTTON _Toc405642471 PAGEREF _Toc405642471 31 Creating a tmd file from an rsd file GOTOBUTTON _Toc405642472 PAGEREF _Toc405642472 33 STEP 10. Building a world. GOTOBUTTON _Toc405642473 PAGEREF _Toc405642473 35 Building a road to drive around on. GOTOBUTTON _Toc405642474 PAGEREF _Toc405642474 35 The world struct. GOTOBUTTON _Toc405642475 PAGEREF _Toc405642475 35 Creating a world map. GOTOBUTTON _Toc405642476 PAGEREF _Toc405642476 35 The AddModelToWorld function. GOTOBUTTON _Toc405642477 PAGEREF _Toc405642477 36 Allow a couple of views. GOTOBUTTON _Toc405642478 PAGEREF _Toc405642478 38 Project: Build the rest of the track and give the car some dynamics. GOTOBUTTON _Toc405642479 PAGEREF _Toc405642479 39 Tips. GOTOBUTTON _Toc405642480 PAGEREF _Toc405642480 39 INTRODUCTION Acknowledgements Firstly thanks go to Paul Holman and Sarah Bennet at Sony Computer Entertainment Europe (SCEE) for giving us a bunch of Yarozes to play with (erm... I mean study in a rigorous academic manner). Secondly thanks go to Sean Bulter who set up our first games module and developed the original versions of much of the code here before moving on to the games industry. Thanks are also due to a project student of mine Ian Frost who worked on one of the original Yarozes six months before the product was launched in Europe and is now also working in the industry. A year ago nearly all demos came from Japan but Ian managed to struggle through (in spite of ignoring my practical advice to try and find a Japanese girlfriend). Lastly thanks go to Lewis Evans (of SCEE) for reading through this tutorial and clearing up a couple of inaccuracies. Aim. The aim of this tutorial is to get you quickly up and running using the PSX and its library functions. The library seems obscure compared to say OpenGL, because it is closely tied to the hardware architecture. However for the most part you can quickly get into 3D graphics programming if you take the basic graphics initialisation, and object manipulation and other functions defined from step 2 onward on faith. Be thankful you haven’t had to discover these functions by yourself! It just turns out there are various curious things you just have to do to get the machine to play ball. As you become more competent and curious you should look into how the PSX functions I have shielded you from in these routines really work, and then come back and educate me. I am not an expert PSX programmer and am bound to program in a less that efficient manner, however you will get demonstrable results from this code. Enough said. The development process. The basic coding process involves using an editor on the PC (ideally a nice windows one like BC++4.5) to edit your code, using the gcc compiler with a make file to compile your code, and using a batch file and the siocons communications software to download and execute your program on the PlayStation (PSX). Makefiles and the gcc compiler. The compiler you will use for PSX development is the gcc shareware compiler which runs under dos. (gcc stands for GNU C Compiler, and GNU is a shareware version of Unix thus GNU stands for GNU Not Unix). Before you can use the compiler you have to be in DOS and have certain environment variables set. Hopefully this will occur automatically if not you can copy a batch file off the file server to set the environment accordingly. To use this compiler you have to use the ‘make’ function and ‘makefiles’ which you may or may not have come across before. Makefiles allow you control how your code is compiled and ensure that only changed files are recompiled. For further information on makefiles check the documentation, otherwise the minimal information you need to know is presented below. To create a makefile copy the example makefile of the server and edit it to make sure it looks like this: CFLAGS = -Wall LINKER = -Xlinker -Ttext -Xlinker 80140000 PROG = main OBJS = main.o pad.o all: $(PROG) main : $(OBJS) $(CC) $(LINKER) $(OBJS) -o $@ strip $@ .c.o: $(CC) $(CFLAGS) -funsigned-char -c $< clean: del $(PROG) del *.o The important details in the makefile from your point of view are ‘PROG = main’ which specifies the name of your program and ‘OBJS = main.o pad.o’ which specifies the names of the intermediate object files which must be built for each of which there should be a corresponding source fils in the current directory (eg: main.c and pad.c. Note that pad.c is a program to help you read which buttons have been pressed on the joypad, you can copy it and its associated header file pad.h from the file server). Once you have the c source files and the makefile you can compile your program type simply typing ‘make’. If you want to force recompilation of everything type ‘make clean’ and then type ‘make’. If your compilation was successful then the last line generated by the compiler should be: strip main If your code had an error in it, the last line will indicate it eg: make.exe: *** [main.o] Error 1 and further up will be indicated the nature of the error and the line that caused it. STEP 1. Hello world on the PC. In this step you will include the Yaroze development librarys, and write a ‘hello world’ type program that prints a message out to the PC screen when the program is run. Type in the following code and save it as ‘main.c’. /*************************************************************** main.c ***************************************************************/ #include #include #include #include #include "pad.h" void main() { printf("Program running, will now exit and reset Yaroze.\n\n\n"); exit(0); } Compile your program using ‘make’ as described in the ‘Makefiles and the gcc compiler’ section above. Errors and warnings generated by the compiler are written to the screen. (Note we didn’t really need to include pad.h as we don’t use the joypad yet). Now create a batch file that specifies what is to be loaded down to the PSX. Type in the following and save it in you current directory as ‘auto’. (Note by convention no file extention is used but you can use one if you wanted to). local load main go The SIOCONS communications software. To download your program to the PSX you run a program called siocons at the DOS prompt. If siocons manages to talk to the PSX properly you will get a prompt that looks like this: >>. If the prompt doesn’t appear after pressing return a few times then reset the PSX, press esc and try again, if you still have problems get help. D:\mygame\>siocons siocons -- PlayStation debug system console program for DTLH3000 1996/05/10 00:00:03 type F1 ----> display help when hung up try type ESC I/O addr = 0x02F8, IRQ=3(vect=0x000B,8259=20) BAUDRATE = 115200 Connected CIP ver2.0 Communication baud rate 115200 bps OK >> Once siocons is running press F3 and type in ‘auto’ and press return. A load of information comes up on the PC screen as shown below: >> Auto[1]: auto main [ .text] address:80140000-801402ef size:0002f0 0002f0: 0sec. main [.rdata] address:801402f0-8014032f size:000040 000040: 1sec. main [ .data] address:80140330-8014047f size:000150 000150: 1sec. main [.sdata] address:80140480-801404ff size:000080 000080: 2sec. PC=80140130, GP=80148480, SP=801ffff0 >>go Program running, will now exit and reset Yaroze. ResetGraph:jtb=8004829c,env=800482e4 ResetGraph:jtb=8004829c,env=800482e4 PS-X Control PAD Driver Ver 3.0 addr=8004d724 Connected CIP ver2.0 Communication baud rate 115200 bps OK >> Simultaneously the PSX displays similar messages until the word ‘go’ appears which causes the PSX to execute the program. The PSX screen goes blank, the text ‘Program running, will now exit and reset Yaroze.’ appears on the PC screen, the PSX resets itself, and the PC displays the siocons >> prompt again. Well done you’ve just created your first PSX program, and what a game it was... STEP 2. Hello world on the PSX and double buffering. In this step you get to write to the PSX screen but you have to do a lot more work and get exposed to the intricacies of PSX programming. This program simply writes a line of text to the PSX screen and exits the program when the ‘select’ button on the joypad is pressed. In order to do this we have to get into graphics mode and set up double buffering. The ability to print on the PSX screen is very useful for debugging as messages sent for printing to the PC screen can become garbled due to syncronisation problems between the PSX and PC. An aside on PSX datastructures We are about to start to learn about the PSX data structures necessary for getting on so a comment about them is timely. Looking at the manuals the structures often appear curious and incomprehesible - and looking at example programs some variables in structures never seem to get used by the programmer at all. Consequently the best initial approach is to develop an understanding of these datatypes on a ‘need to know’ basis eg: to initialise an ordering table header (see function InitialiseGraphics below) we have to set it’s length and *org variables, (if fact InitialiseGraphics does this for us), we can ignore the rest. Ordering Tables and Ordering Table Headers. The PSX implements a version of the painters algorithm for hidden surface removal, where all polygons are sorted in order of depth before being drawn to the screen. Using such as system the most distant polygons are drawn first and nearer ones last so that the hidden surface removal is easily achieved. A draw back to this approach is that problems occur when two polygons overlap in depth. The usual solution to this problem is to subdivide the polygons that overlap as seen from the viewpoint, however the PSX doesn’t implement this so you occasionally see polygons overlaping incorrectly during game play. The PSX does support object (as opposed to view based) polygon subdivision which can minimise overlap but doesn’t cure it generally and is expensive. (We will discuss solutions to this later). We have to specify the precision which is used for depth sorting which can be from 21 to 214 levels of depth. If the prescision is too low we risk having every thing randomly drawn onto the same plane, whilst higher precision will gobble more memory. We define the precision as a constant in the program so that if we want to change it we only have to do it in one place, eg: #define ORDERING_TABLE_LENGTH (12) The structure into which polygons are sorted is referred to as an ordering table and the ordering table is actually accessed via a structure called an ordering table header. We need two ordering tables, one for each buffer. When we implemented 2D double buffering we only had to consider the video memory needed to store each buffer. With the PSX we have to allocate the full set of datatypes associated with ordering tables for each buffer. Thus a ‘buffer’ now involves more memory that just the video buffer. Associated with an ordering table is an ordering table header, which we reference in dealing with ordering tables, and an array of packets (discussed below). We will need two of each of these three structures, one for each buffer. For convenience we use arrays, so the two ordering table headers are defined as an array of GsOT, the structure for ordering table headers: GsOT othWorld[2]; and the ordering tables are defined as an array of GsOT_TAG, the structure for ordering tables: GsOT_TAG otWorld[2][1<