home *** CD-ROM | disk | FTP | other *** search
-
- "Knowledge is Power, Power is Money"
-
- 3d Vectors Source
-
- by John McCarthy (with a little help from his mommy:eg food)
- 1316 Redwood Lane
- Pickering, Ontario, Canada
- L1X 1C5
-
- (905) 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
- resolutions 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 the maximum number of surfaces an
- object can have. The constant maxpolys determines the maximum number of
- connections a surface can have.
-
- Sample shape data:
-
- headerthing dd -1 ; distance that first resolution is visable, -1 = last
- dd offset thing - offset $ - 4
-
- thing dw 6 ; number of points
- dw 4 ; number of surfaces
- dw 25 dup (?) ; future use
-
- dw x,y,z ; point 0
- dw x,y,z ; point 1
- dw x,y,z
- ...
-
- dw command
- dw texture for side 1
- dw texture for side 2
- dw colour for side 1
- dw colour for side 2
- dw connection data eg (1,2,3,4,1)
- dw [?,?,?] [optional surface normal if shade command used]
- dw more connection data...
- ...
-
- There are several commands one can use for each surface. Commands like
- steel texture, always visible, 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 himap 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 an object, use
- dw himap/lomap, 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. Object shapes have
- abcd prefixes. Therefore, as object gets farther from camera, less
- points/surface must be calculated. You must define shapes for every
- distance. You can have only one distance/shape if you want or you can
- have hundreds.
-
- eg dd distance1
- dd offset thing1 - offset $ - 4
- dd distance2
- dd offset thing2 - offset $ - 4
- dd -1 ; <- -1 is last distance flag
- dd offset thing3 - offset $ - 4
-
- Surface data must be entered counter clockwize so side will be visible.
- Clockwize surfaces are visible 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.
-
- 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 an
- 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 public routines: (not all, just some) See the top of the .asm files
- for complete lists of public routines.
-
- arctan +/*% arctan(rise/run)=arctan(cx/ax). any quadrant, 16bit
- calc_angles +/*% calculate xy angles between object di and object si
- calc_middle +/*% calculate xy angles between object di and ebx,ecx,ebp
- checkfront +/*% test points (di,bp) (si,qds) (dx,qes) for 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
- drawvect + draw list of vectors using points, sides and order
- 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
- get_displacement +/*% calculate difference between objects
- 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
- point_it +/* point object si at object di
- point_dir +/* point object si in direction it is moving
- point_to +/* point object si at location ebx,ecx,ebp
- set_speed +/* calculate velocity based on angles
- point_time +/* point obj di to bx,cx,bp in di frames
- put_object +/* put object esi at location ebx,ecx,ebp
- re_sort + sorts objects based on "finalzed" values
- rotate +/*% rotate bx,cx,bp (x,y,z) through matrix vrotate, 16bit
- set_finall +/* calculate xsfinal for object (location)
- set_finala +/* calculate vxsfinal for object (angles)
- setmakeorder # resets order for makeobjs - for initialization
- setsincose +/ set sin and cos multipliers for eye rotations
- setupbase # set up object base pointers to shapes
- set_object_on /* turn object si on
- set_object_off /* opposite
- sine +/*% ax=sin(ax), 16bit input, 32bit output
- show_stars +/ display stars in background
- sort_list + sorts list of sides of polygon
- twist_si +/* set angular velocity based on ebx,ecx,ebp and di(time)
- updvectors +/ updates vector xyz's and angles
- where_si +/*% return location of where object will be in di frames
-
- 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. Also check out
- poly.inc for more 3d functions and math.inc for general math functions.
-
- Drawvect 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. If you need/want
- to use this line drawing routine it has been 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 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 7 months ago (march '93), i had trouble programming a
- batch file!
-
- Shape data can be almost as large as you need it 'till it crashes. try a cube
- 20000x20000x20000. Calculations use 32 bit registers and can handle
- up to 16 bit locations. keeping the object size small will allow a larger
- visible space. but larger objects will allow you to get closer with more
- accuracy in the mathematics of rotations.
-
- List of command bits to date: (for object definitions)
-
- note: "visible" = "points appear counter-clockwise"
-
- texture definitions:
-
- 0 - normal surface, no features, constant colour.
- wavey - steel texture for surface 0 = none, colour offset determines
- screen offset for texture. eg 16+7 will use colour block 16-31
- but make the sine wave texture 14 (7*2) lines down. this is so
- all sine wave textures do not appear on the same line.
- windows and engines look good with this feature.
- shade - lambert shading bit, must have normal calculated or at least
- have free space for pre_cal_lambert to use:
- eg 128,16*1,1,2,3,1, ?,?,?<- these 3 words are surface normal!
- inverse - inversion bit for shading option. 0=normal shading, 1=inverse
- if option +4 is used, inversion automatically occures when
- other side is displayed.
- glow - =shade+inverse
- last - colour has same colour as previous surface (used when
- you want gourad shading, but want to avoid duplicate
- calculations - don't set gourad bit if this is what
- you use it for.) when this is used, the colour number
- determines the new colour block to use. the shading
- of this colour will be the same as the surface before
- it, but the colour block can be different.
- mesh - screen door style of surface
- stone - texture map style surface, I just liked the way it looked..
- glenz - glenz vector surface - uses cross referancing palette for
- changing on screen colours.
-
- commands:
-
- point - defines a single point; must be repeated! eg dw 64,col,3,3
- line - if used, defines a line (must be set to define a true line)
- himap - if set, defines a bitmap,eg: point #, bitmap #, x scale,y scale
- lomap - uses 1/4 scaled bitmap (every 4'th pixel is sampled), fast
-
- iterate - generate iteration if side visible (iteration = sub-object)
-
- both - side is always visible no matter angle, skips counter-clowise test
- - "both sides have same texture"
- double - side is always visible but other side has high byte colour
- "double sided surface"
- note: if this is used, option "both" must not be used!!
- onscr - test if side is on screen - don't use if all points are
- outside clipping parameters.
- check - dont plot this side, just use as test points for visibility.
- this is mostly used with iterations.
- matrix - generate new matrix
- sub-object - make subobject
- gosub - transfer loading of surface data elsewhere
- return - return from gosub
-
-
- There are a kazillion more options - look in the objects.inc file for the
- surface types and example objects.
-
- There are two kinds of bitmaps and points. Those which are inside objects
- and those which are seperate objects themselves. if userotate object command
- is set to himap/point,then the entire object is considered as a point
- or bitmap. But if userotate is not set this way, then a normal object is
- drawn and bitmaps then come from within the object definitions (below). this
- way, bitmaps and points can be either part of a larger object, or they are
- computed fast on their own. (eg explosions and bullets as seperate objects)
-
- Note: When writing surface descriptions, try to make the first value unique
- from any other first value. this way, the sort routine will give a more
- accurate sorting of sides. eg 1,3,6,1 2,4,1,2 rather than 1,3,6,1 1,2,4,1
-
- to recap:
-
- 0 = constant colour, only visible from counter-clockwise side
- wavey = sine texture
- shade = shading - requires 3 blank words for surface normal eg dw 0,0,0
- inverse = invert the shading direction, 0=normal, 1=sun is other way.
- last = use intensity from previous surface (not colour, only intensity)
- point = point
- line = line
- himap = bitmap (scalable, non-rotatable)
- lomap = bitmap (scalable, non-rotatable)
- iterate = generate iteration if side visible
- both = always visible
- double = always visible but other side has high byte colour,"double sided"
- onscr = plot side only if all the following points are on the screen
- check = dont plot side but use the following points as a test for visiblity
-
- What you can't mix on a single surface: "double" with "both"!!
-
- You do not have to define a point for the center of the object. the point
- 0 defines the center of the object. This is different from earlier versions
-
- Remember that negative y (-y) is up, +y is down. This is opposite from our
- regular grade 13 mathematics but it is consistant with the computers screen
- arrangement. If your objects look funny, make sure you have this correct.
-
- The shading for objects requires that 3 words be present after the side
- definition. These 3 words represent the surface normal to the side and can
- be either set by you (slow and tedious) by using the normal.bas program, or
- can be set up by the routine pre_cal_lambert. This routine scans the object
- to find surfaces that have the shading bit set (128). The surface normal
- will be calculated for that surface and stored in the 3 words set aside by
- you. If you remove the shading feature from a surface, make sure you remove
- the extra 3 words or it will screw up. But you knew that right. To use the
- routine pre_cal_lambert, load si with the object you wish to scan, and call.
- make sure objbase points to the offset of the object. si will be the object
- number, eg 0,1,2,3, not the offset. The offset will come from objbase.
-
- Each on-screen object can have it's own colour palette scheme by setting the
- palxref offsets to a cross reference palette. this uses the xlat command to
- take the objects colour, and xlat it into a new colour based on the xref
- table you provide. each object can have it own xref table. This way, many
- on-screen objects can use the same shape data while each is coloured with a
- different colour scheme (5 stuka airplanes all coloured differently, for
- example) You will find the palxref tables (for each object remember) in the
- file equ.inc. A routine set_xref_palette has been provided for you so you
- can set the xref offset (this routine is in poly.inc)
-
- To have a bitmap on the screen (scalable but non-rotatable) use option himap
- in either userotate or as an object command. Lomap uses the same bitmap
- method but only draws every 4'th pixel - good for explosions and smoke.
-
- onoff commands:
-
- 0 = object off eg mov onoff[esi],2 ; turn on sub object
- 1 = main object on
- 2 = sub object on
-
- userotate object commands:
-
- 0 = all rotations supported - full object
-
- 1 = camera rotations only - no compound, new loadpoints
-
- object is same as when userotate=0 but will not allow any object specific
- rotations. this is used to speed up rendering of objects that are
- stationary or objects that will always be pointing in the same direction.
- make1obj routine then assumes angles = 0x, 0y, 0z
-
- if this is used to define a sub-object (see main.asm for that green box
- with those two sub-object blocks) then +1 option in userotate makes the
- box angle/rotation independant from the main object.
-
- himap = bitmap - no compound, no loadpoints, no sort and no drawvect
- lomap = bitmap, 1/4 scale, faster than 32
-
- if object is bitmap, then:
-
- whatshape - indexer to which bitmap in bitbase list
- xs,ys,zs - point to bitmap location in space
- vxs - bitmap scaling (how big is bitmap). note: bitmap is already
- scaled based on distance so you don't have to change this
- as the bitmap gets farther away.
-
- point = point - no compound, no loadpoints, no sort and no drawvect
-
- used for bullets. could be used for stars but if you do want to make
- stars, make a specialized bitmap routine. making stars as objects would
- be too slow. right now, bullets all have same colour, see constant in
- equ.inc. note:sept 29/93, stars routines now are a seperate specialized
- assembley routine. - stars.asm
-
- xs,ys,zs - point to bullet location in space
-
- The locations of sub_objects are x/256 because of the way conversion between
- real space co-ordinates and object co-ordinates are calculated. Objects
- are 256 times larger than their appearance in real space. This allows much
- greater accuracy in real space!! Eg , if you have a cube 500x500x
- 500 (which is really 1000 wide cause -500 to +500 is 1000 units) then, in
- order to "move" accros the surface, you should only have to move 1000 units,
- right? Well here, you'll have to move 256000 units even though the cube is
- only 1000 units wide/tall/deep. This is because the real space co-ordinates
- are 24 bit (32bit/256). This gives you greater accuracy in placing objects
- where you want them in space but still allows you to have very large objects
-
- If your objects appear jittery when viewing close-up, make them physically
- larger. For example, double all the point data (makes object twice the
- size). This way, the multiply routine has more accuracy when calculating
- rotations/3dpoints.
-
- Example of how to draw a single polygon:
-
- p1x equ -50
- p1y equ -50
- p2x equ -90
- p2y equ 70
- p3x equ 60
- p3y equ 80
-
- mov x1,p1x
- mov y1,p1y
- mov x2,p2x
- mov y2,p2y
- call fakeline ; draw in buffer
-
- mov x1,p2x
- mov y1,p2y
- mov x2,p3x
- mov y2,p3y
- call fakeline ; draw in buffer
-
- mov x1,p3x
- mov y1,p3y
- mov x2,p1x
- mov y2,p1y
- call fakeline ; draw in buffer
-
- mov colq,7
- mov steel,-1
- call poly_fill ; fill polygon on screen
-
- call flip_page ; show it...
-
-
- A little note about polygon construction:
-
- Polygons can only have angles less than 180 degrees. That is, they must be
- round like circles, not hooky like a sickle. eg:
-
- /\
- /\ \ \ invalid polygon
- \ \ valid polygon / \
- \ \ \/\ \
- \/ \/
- ^
- |---------this angle is greater than 180degrees.
-
- if you want to make this here^^^^, you must split it up into 2:
- /\ /\
- \ \ \ \
- /\ + \ \ = / \
- \/ \ \ \/\ \
- \/ \/
-
- So, an object like so: ____
- / __ \
- /_/ \_\
-
- must be drawn like so:
- ____ ____
- /\ + /\ + \__/ = / __ \
- /_/ \_\ /_/ \_\
-
-
- Question: How come my objects appear upside-down
- Answer: Thats because the Y axis is inverted. Positive X points to the
- right, positive Z points away from you and positive Y points down.
-
- Question: Ok, I make the Y value point down, now they still don't look
- right - the surfaces are connected correctly but it looks wrong.
- Answer: The most probable reason for this type of error is that the
- connection data has been entered in reverse order. Eg 3,4,5,2,7,3
- should probably have been entered as 3,7,2,5,4,3. Remember, enter
- the surfaces in Counter-Clock-Wise order.
-
- If you don't like manual data entry (which I don't) you can try out the
- converter DXF23DV which should be supplied with this package. Now just draw
- your objects in AutoCAD and it will convert them into assembley connections
- Remember, in AutoCAD +z points up, +y point away, in my package, +z points
- away, +y points down. Draw the objects in Autocad as if +z is up and my
- converter will change the co-ordinate system for you.
-
- Question: The objects look great from far away, but up close, the appear
- "jumpy" or the whole thing disappears.
- Answer: Your using too small a scale factor. If you have a cube 10x10x10,
- the math routines dont have much room to calculate a rotated object
- Try making your objects bigger, like 1000x1000x1000. The reason
- why the entire object may disappear is that the center of the
- object has passed to close to the camera. If you have large
- objects, you may want to cut them up into seperate sections and
- piece them together as a seperate objects.
-
-
-
-