3d Vectors Source by John McCarthy (with a little help from his mommy) 1316 Redwood Lane Pickering, Ontario, Canada L1X 1C5 (416) 831-1944 (voice, always willing to talk, but do not call at 2am) documentation is in no defined order. sorry, i just sorta lumped my ideas together and ended up with this file. routines support any x mode, - but page flipping is not allowed in resolutons which allow only 1 page - see "pages" constant. full clipping is performed to user defind areas - see constants in equ.inc. they have been changed to memory locations for variable windowing or multiple screens. for windowing, the last z locations for that window must be remembered along with a slew of other locations, see vars.inc for that info. to change a window, save the lastz information, reset with old lastz information and then call set_clip to change the border clipping and screen center data. the theoretical screen is considered to be (x,y) with 0,0 being the center of the screen!. so -100,-100 is somewhere on the top left! actual screen goes from (0,0) to (320,200) - or whatever mode size you select. Matt Pritchard's routines (xmode.asm) assume 0,0 to be the top left of the screen while my routines (me = John = 3d.asm) consider the screen center to be the constants xcenter and ycenter. visible space is -4628196 to +4628196 on all axis (approx). object locations are 32 bit, vector routines are 16 bit, objects must be smaller than 16 bit but are visable within about a 32 bit range. (4 million, as it is now, is very very far). since the camera is always at (0,0,0) (relative), objects with (relative) negative z values are not seen. this cuts the z space to 0 to 4mil. visible space is always divided by 256 so decimals can be allowed in adding, and moving of objects. visible space therefore, is actually from -1.024 billion to +1.024 billion with the lower byte having no effect on the location. non-visible space is where objects can be but won't appear on screen. this space is a 256 *256*256 cube. to racap: you have 32 bit x,y,z axis with a visual range of 28 bits, where the lower 8 bits don't affect the location. (lower 8 bits don't count because locations are shr'ed) i say that the visable space is "about" 4mil only because of the code in the make3d routine: this code multiplies by a constant and then performs divide by z distance. we cannot allow the multiply to overflow and therfore must truncate our maximum distance to prevent this. the constants for multiplication are the screen ratio constants and the calculation to test for an overflow is as such -2^32/2/256/(largest constant). the constant I have used is 464 for the y ratio. I have used this because of my desire to use the 320x400 mode resolution. therfore, 4.3gig/2/256/464 = about 4 million - our maximum visual distance. like, trust me, you don't really need a larger universe. fixing the make 3d routine wont allow you to see farther because then you would have to fix the rotate routine, etc, etc. when defining a location: ebx = x, ecx = y, ebp = z when defining a rotation: x = pitch, y = heading, z = yaw si refers to object number, di refers to time. rotations occure in order: zobject,xobject,yobject,ycamera,xcamera,zcamera - rotations are compounded in matrix for faster computation. vmatrix is the matrix for object rotation. ematrix is the matrix for camera rotation. if you want know where a point in space will show up on the screen, load ebx, ecx, ebp with your x,y,z point, subtract camera location and call erotate (eye rotate). the point will be rotated according to current camera angles. make sure that a call to setsincose has taken place to set the eye rotation matrix (ematrix). polygon can handle any number of sides. to draw a triangle, make last point equal to first point, eg 1,4,5,1. number of sides of a polygon is determined so that the polygon is not finished until the last side equals the first side: eg 1,7,6,14,13,4,2,1 would be a 7 sided polygon. the constant maxsurfaces determines that maximum number of surfaces an object can have. the constant maxpolys determines the maximum number of connections a surface can have. sample shape data: thing dw 6 ; number of points dw 4 ; number of surfaces dw x,y,z ; point 0 dw x,y,z ; point 1 dw x,y,z ... dw 0,1,2,3,0, col,com ; surface from point 0-1,1-2,2-3,3-0, colour and command byte dw 2,4,1,2 , col,com ; triangle from points 241, and command byte dw 2,7,2 , col,com ; line from 2 to 7 dw 6,5,9,13,25,23,1,24,14,29,12,6, col,com ; multi-sided polygon ... there are several commands one can use for each surface. commands like steel texture, always visable, opposite colours, etc. view the objects include file to see what/how to use them. bitmaps can be part of an object or be made as seperate objects. i will be using the bitmaps for things like explosions, smoke (from damaged planes/spaceships) and distant suns/solar system (u know, like in x-wing) set the values bitx and bity to the scaling to be used for each bitmap and set userotate to 2 as this is the command to define a bitmaped object. vxs and vys are the additional scaling used for individual objects (vxs+bitx = final scaling factor). when part of and object, use dw 32 (bitmap), point #, x scale, y scale. remember, scaling is added to bitx and bity so objects have a base scale plus some individual scale. complex objects don't cut it for speed! keep your objects simple and you can have more of them on screen at once! maximum speed is found with low resolutions. high resolutions with clipped borders also provide adaquate speed. a shallow but wide screen (small y, big x) provides better usage of cpu time than a tall and thin screen. one big object is faster to compute than many small objects (if same surface area) an object viewed from the side takes signifiganly less time to compute than if viewed from the top due to the shallow y, large x idea. small option has been added for objects farther than smalldist distance. object shapes have abcd prefixes. therefore, as object gets farther from camera, less points/surface must be calculated. you must define four shapes for every shape. hi-res shape is a, and lo-res shape is d. eg dd offset athing,offset bthing, offset cthing, offset dthing surface data must be entered clockwize so side will be visible. counter clockwize surfaces are visable from other side and will not be plotted (unless you use a surface command override, see objects.inc) an increase in screen objects increases cpu time. however, if you know that you will always have the screen filled (in the case of floors, and runways.) you can disable the clear_fill routine during those parts! if the screen will be covered with background walls and such, there is no purpose to call the clear routine to compute the next part! i have therefore added a flag for the clear_fill routine to use: when your animation comes to the part when your looking at the ground or walls (and there are NO empty spaces) toggle the flag to skip clear_fill and get more cpu time. this also works if you are approaching an object or large surface, since the new object will totaly cover the previous one. another time trick is to have your main background object include the sky (or area to be cleared) as part of the object. if you are going to have walls that go halfway up the screen, have them go halfway with the regular walls and then make another surface that goes to the top of the screen (or above if you want to move around) with the colour 0. you can then deactivate the clear_fill routine and still have the animation appear as if the walls are completely seperate objects. sorting routine for objects (as opposed to sides) uses last z value to re-sort for the next plot. if you plan on drawing static pictures you may want to call makobjs twice to: 1) draw and find zeds, sort, then 2) re-draw. this will be the only way (and easiest way) to plot an accurate picture of what we have. don't worry about calling twice during animations as the first picture will be the only picture that is not sorted. during animations, all objects are sorted properly, based on previous z. routines which are expected to be used in animations have been optimized but routines intended for use as background and title draw routines are not intended to be fast. if you find any areas that can be optimized please let me know or send me your changes. what i really want to know is if the theory can be optimized! if i am loosing a cycle here or there, so what. but if i am loosing thousands due to lack of initial insight,now i really want to know! if you send me your changes on a disk, i will make sure you get your disk back with a tonne (metric) of new assembler routines. PLEASE DOCUMENT YOUR CHANGES!! newfollow routine does not handle object lock on well if object is accelerating. the routine calculates where the object will be in di frames and attempts to point the camera to it in di frames. however, if the object is accelerating, then the object will not be where it was expected to be at that time. so the camera must re-lock on to its target. this loop commences until the camera actually has locked on to the target object, from this point on, the camera will follow the object regardless of motion. the re-lock on sequence takes the last number of frames and divides it by two, so the re-lock on loop will move toward the accelerating object at an accelerating rate. general overview: locations are 32 bit -2.1Gig to +2.1Gig, angles are 16bit from 0-65535 degrees, 4 quadrants - 4096 entries each quadrant. variables in vector routine are 16bit. cosine and sine list are words but get converted into doublewords when used. some routines: (not all, just some) arctan +/*% arctan(rise/run)=arctan(cx/ax). any quadrant, 16bit checkfront +/*% check if points (di,bp) (si,ds) (dx,es) are clockwize clear_fill +/ clears write page using xupdate and yupdate variables compound + *% compounds angles of eye and angles of object into matrix cosine +/*% eax=cos(eax), 16bit input, 32bit output erotate +/*% rotate for angles of eye, 32bit, uses ematrix fakedraw +/ draw line in firstbyte and lastbyte tables from xy1 to xy2 flip_page +/ flip between pages 0 and 1, wait for vertical sync drawvect + draw list of vectors using points, sides and order initfont # initialize font pointers initpages # initialize x-mode pages for flip_page to page 0 loadpoints + *% load points into array, rotate and translate as we go loadsurfs + * load surfaces, check if visible as we go look_at_it +/* immediatly force eyeax, eyeay to look at object wherelook make1obj + * make object si make3d +/*% make bx,cx,bp into bx,cx 2d pair, 16bit makeobjs + make all objects then sorts based on last z location move_to # /* move object si to bx,cx,bp - time di frames newfollow # /* forces camera to follow object si, time to get there di poly_fill +/ uses oney,firstbyte and lastbyte to draw one surface put_at_top / put eax at top of screen in box (for debugging) re_sort + sorts objects based on "finalzed" values rotate +/*% rotate bx,cx,bp (x,y,z) through matrix vrotate, 16bit setmakeorder # resets order for makeobjs - for initialization setsincose +/ set sin and cos multipliers for eye rotations setupbase # set up object base pointers to shapes sine +/*% ax=sin(ax), 16bit input, 32bit output sort_list + sorts list of sides of polygon updvectors +/ updates vector xyz's, angles legend: # used for initialization of code or new scene + used regularly in animation loop / can be used by user outside of animation loop if needed * routine requires parameters passed in registers % routine exits with results in registers > routine wipes harddrive there are more routines at the end of 3d.asm for more general functions like find the camera displacement and finding rotational offsets between two objects. u figure them out - fairly self explanatory. divide overflows are generally caused by having an object behind the screen (or too close and trying to calculate where it is on the screen. obviously this cannot be calculated (since is it off the screen) when this happens, ztruncate takes over in make3d routine. minz is set to more than the maximum distance any point on an object can be from its center of gravity. use ztruncate to truncate underflow of z to higher values to prevent overflows due to the object being too wide for the camera. increasing the zmin value prevents object from coming too close to the screen. however, large objects (like battlecruisers, landing strips) must be allowed to come close, as the camera pans over the object, in this case, zmin is low but ztruncate takes over to "warp" the object to an appropiate on screen location. ztruncate used in make3d routine does not provide a true rendition of what a very-close object would look like but it's fast and simple. generally, these values do not need to be changed unless close objects appear flat or objects disappear when they become too close. objects disappear when they move to the other side of the camera. draw_vect routine has a seperate routine for drawing lines (as opposed to surfaces). the fake_line routine and poly_fill routine could do the job but they were too slow. the line was drawn twice then filled just like a polygon but now a seperate routine clipps and draws. you will not need to use this line drawing routine but if you want, it could be seperated from the draw_vect routine. I do not use the xmode line draw by Matt Pritchard as it does not allow for clipping. sin and cosin tables - 90 degrees is now 16384, 180=32768... move_si routine - to move an object around, load up ebx, ecx and ebp with the x,y,z locations of where you want the object to end up. load di with the time you would like the object to take to get there. load si with the object number you want to move and call move_si. the updvectors routine does the rest! to look at an object. either 1) put the object number in wherelook. or 2) load si with the object to look at, load di with the time to move the camera to the object, and call new_follow. just think, only 4 months ago (march '93), i had trouble programming a batch file!