; LKS 3-Dimensional Object Management System (3DOMS) version 0.15 ; 3DOMS.INC - LKS Created: 22/Dec/1994 Now: 12/Jan/1995. ;----------------------------- Documentation --------------------------------; comment | The logon and logoff routines must be called at the start and end of the host program respectively. Everything else is done through INT 3Dh, with the function number in AH. If a function can't do what it was asked, it returns with the carry flag set and an error code in AX, and nothing in the scene has been changed. ;-------- Structure. Point <- Face <- Shape <- FORM <- Object <- Object ... <- UNIVERSE Everything lives on a heap of 32-byte cells, one structure to a cell, and structures are passed around as the segments of their cells. All co-ordinates are double words with 8 bits after the point, so 100h is 1.0, and they must be between -32768.0 and 32768.0. Angles are bytes, with 256 to a full turn. POINTS Points are managed behind the scenes. A point shared between many faces has all its calculations done once per form, not once per face. When a face is created, each of its corners is looked up in its form's list of points, and a new point is only made if it isn't there already. FACES Faces are the most basic pieces of a 3-dimensional thing. To avoid any programming difficulties on my part and to make rendering as fast as possible, all faces are triangular. If higher polygons are required, they must be made from more than one face. Seen from the front, the corners of a face go anticlockwise. Faces seen from the back are not drawn. SHAPES Shapes are the basic building blocks of complicated objects. A shape must be totally convex otherwise rendering will be inaccurate. If you need to make a convex object, it must consist of more than one shape. For example, if you want to make a plane, you may have each wing as one shape, the body as another and the tail as another. Center co-ordinates are first used in shapes. These are used to tell 3DOMS which order to put shapes on the screen if they overlap. It starts by drawing the one with a center furthest from the viewer and works forward. The programmer has to specify the center co-ordinates themselves - they should be centralised and 'inside' the shape if possible. FORMS Shapes and faces are constants - if you want to have more than one plane flying around, as long as all the planes look the same, they can all use the same shapes and faces. A form is basically a descriptor for a linked list of shapes, and it keeps the list of points its faces use. When you create a new object, you give it a form, which will be used when the object is drawn. A form can't be deleted while any object is using it. OBJECTS Collections of shapes of a particular form. All the planes would be objects. Objects can also hold other objects, which move and turn with them. A plane may be an object with the body as its form, holding some wheel and flap objects, as the wheels and flaps are not fixed in position on the plane. An object's own form and the objects inside it are drawn starting with the one with a center furthest from the viewer, the same as shapes. The form goes by its own center, and each object by its center. Objects can be put inside each other 8 deep. Things are only ever sorted against the others in the same object. An object is drawn all in one go, with everything inside it, at the place its own center puts it among the things next to it. So parts that can come in front of each other should be in the same object. CENTERS Two centers are the same distance from the viewer anywhere on the plane halfway between them, so which side of that plane the viewer is on decides which is drawn first. If two shapes or objects touch along a flat side, put their centers the same distance either side of it, and they will always be drawn the right way round. Centers can be moved with Function 12h. UNIVERSE There is one universe, made when you log on. It holds the objects at the top of the scene, and it has a position and angles like an object, which place the whole scene in front of the camera. CAMERA The camera is at the origin, looking along the z axis, with x to the right and y down the screen. Nothing is moved in front of it for you: give the universe, or an object, a position that puts things where they can be seen. At a distance of 128, one unit across is one pixel on the screen. A point can't be drawn if it is less than 1.0 in front of the camera, or if it is more than 16 times as far out to the side, up or down as it is in front. Faces with a corner like that are left out. TEXTURES When I have perfected the basic rendering, I will work on texturing methods including bitmapping, RGB colours (using dithering and an orthogonal palette), and reflectiveness and surface normal smooth shading (for fast computers only!). Other graphical effects which I hope to include to add realism to scenes are light source shading and distance fading. ;-------- Logon_3DOMS Inputs: BX - Smallest heap that will do, in cells (at least 16). DX - Largest heap wanted, in cells (at most 7FFFh). Returns: Carry clear - Success AX - Segment of heap, which is also the universe. CX - Size of heap in cells. Carry set - Error AX - Error code. CX - Most cells DOS could give, if the error is ERROR_HEAPOUT. Allocates the heap, makes the universe and points INT 3Dh at 3DOMS. This is a normal near call, not an interrupt function. ;-------- Logoff_3DOMS Inputs: (none) Returns: Carry set if 3DOMS wasn't logged on. Frees the heap and puts the old INT 3Dh vector back. ;-------- Function 00h - Get Version And Run-Time Info Inputs: AH - 00h Returns: AH - Major version number. AL - Minor version number. BX - Segment of heap. CX - Size of heap in 32-byte cells. DX - Number of 32-byte cells free. This function returns information about the status of LKS 3DOMS. ;-------- Function 10h - Move Inputs: AH - 10h DS:SI - Pointer to position and angles: 3 double word co-ordinates, 3 rotation bytes and one rotation order byte - 16 bytes in all. ES - Segment of object or universe. Returns: Carry clear - Success Carry set - Error AX - Error code. Changes the position and angles of an object, or of the universe. ;-------- Function 11h - Change Object Form Inputs: AH - 11h BX - Segment of new form, or 0 to take the form away. ES - Segment of object. Returns: Carry clear - Success AX - Segment of old form, or 0 if it had none. Carry set - Error AX - Error code. Changes the form an object is drawn with. ;-------- Function 12h - Change Center Inputs: AH - 12h DS:SI - Pointer to new center: 3 double word co-ordinates. ES - Segment of object, form or shape. Returns: Carry clear - Success Carry set - Error AX - Error code. Moves the center that an object, form or shape is sorted by. ;-------- Function 18h - Delete Object Inputs: AH - 18h ES - Segment of object. Returns: Carry clear - Success AX - Segment of the object or universe it was in. Carry set - Error AX - Error code. Takes an object out of the scene and frees its cells. It mustn't have any objects inside it. ;-------- Function 19h - Delete Form Inputs: AH - 19h ES - Segment of form. Returns: Carry clear - Success Carry set - Error AX - Error code. Frees a form and all its shapes, faces and points. No object may be using it. ;-------- Function 1Bh - Create Object Inputs: AH - 1Bh DS:SI - Pointer to object data: 3 double word co-ordinates for center of object. ES - Segment of the object or universe to put it in. Returns: Carry clear - Success AX - Segment of created object. Carry set - Error AX - Error code. Creates a new object, at the origin of the one it's in and not turned, and with no form. ;-------- Function 1Ch - Create Form Inputs: AH - 1Ch DS:SI - Pointer to form data: 3 double word co-ordinates for center of form. Returns: Carry clear - Success AX - Segment of created form. Carry set - Error AX - Error code. Creates a new, empty form. ;-------- Function 1Dh - Create Shape Inputs: AH - 1Dh DS:SI - Pointer to shape data: 3 double word co-ordinates for center of shape. ES - Segment of parent form. Returns: Carry clear - Success AX - Segment of created shape. Carry set - Error AX - Error code. Creates a new shape in the form specified. ;-------- Function 1Eh - Create Face Inputs: AH - 1Eh AL - Colour (palette index). DS:SI - Pointer to face data: 9 double word co-ordinates, the x, y and z of each corner - 36 bytes in all. ES - Segment of parent shape. Returns: Carry clear - Success AX - Segment of created face. Carry set - Error AX - Error code. Creates a new face in the shape specified, sharing any corners that are already points in the form. ;-------- Function 20h - Render Inputs: AH - 20h DS - Segment of universe. ES - Segment of display buffer (this may be 0A000h or any other 64k buffer which will be overlayed with a 320x200 bitmap). Returns: Carry clear - Success Carry set - Error AX - Error code. Draws the whole scene in a 320x200x256 bitmap. It is usually desirable to draw the screen into a buffer or a hidden screen, then display it so that the user cannot see the polygons being drawn. The buffer isn't cleared first. ;-------- Function 2Ch - Triangle Inputs: AH - 2Ch AL - Palette index. BX, CX - x and y of first corner. SI, BP - x and y of second corner. DX, DI - x and y of third corner. ES - Segment of output bitmap. Returns: Carry set if ES overlaps the heap or 3DOMS. Draws a filled triangle. Anything off the screen is left out. ;-------- Function 2Dh - Line Inputs: AH - 2Dh AL - Palette index. BX, CX - x and y of start. SI, BP - x and y of end. ES - Segment of output bitmap. Returns: Carry set if either end is off the screen, or ES overlaps the heap or 3DOMS. Draws a line, including both ends. | int3D_first label byte ;----------------------------- Constants ------------------------------------; ;-------- Errors. ERROR_HEAPOUT equ -1 ; Not enough free cells, or not enough memory. ERROR_BADFACE equ -2 ; A segment isn't the structure that was ERROR_BADSHAPE equ -3 ; wanted. ERROR_BADFORM equ -4 ERROR_BADOBJECT equ -5 ERROR_BADPOINT equ -6 ERROR_BADUNIVERSE equ -7 ERROR_BADPARENT equ -8 ; The parent isn't an object or the universe. ERROR_TOOMANY equ -16 ; The parent already holds as many as it can. ERROR_TOODEEP equ -17 ; Objects can't be put inside each other ; any deeper. ERROR_HASCHILDREN equ -18 ; The object still has objects in it. ERROR_INUSE equ -19 ; The form is still used by an object. ERROR_BADLIST equ -32 ; A linked list doesn't match its count. ERROR_BADBUFFER equ -48 ; The data at DS:SI runs off the end of the ; segment. ERROR_BADOUTPUT equ -49 ; The output buffer overlaps the heap or ; 3DOMS. ERROR_BADCOORD equ -50 ; A co-ordinate is out of range. ERROR_BADORDER equ -51 ; Not one of the six rotation orders. ERROR_OFFSCREEN equ -52 ; A line doesn't fit on the screen. ERROR_BADSIZE equ -64 ; The heap sizes asked for don't make sense. ERROR_LOGGEDON equ -65 ; 3DOMS is already logged on. ERROR_NOTLOGGEDON equ -66 ; 3DOMS isn't logged on. ERROR_BADFUNCTION equ -99 ; There's no function with that number. ;-------- Rotation Order constants. RORDER_XYZ equ 00000000b RORDER_XZY equ 00000100b RORDER_YXZ equ 00000001b RORDER_YZX equ 00000101b RORDER_ZXY equ 00000010b RORDER_ZYX equ 00000110b ;-------- Magics. MAGIC_FREE equ 00h MAGIC_POINT equ 'P' MAGIC_FACE equ 'F' MAGIC_SHAPE equ 'S' MAGIC_FORM equ 'M' MAGIC_OBJECT equ 'O' MAGIC_UNIVERSE equ 'U' MAGIC_TAKEN equ 0FFh ; Taken, but not filled in yet. ;-------- Common offsets. ; Offsets which are common to all structures. All structures take up 32 ; bytes (2 paragraphs) - one cell. _MAGIC equ 0000h ; (B) Magic - indicates type of structure. _NUM equ 0001h ; (B) Number of child structures. _PTR equ 0002h ; (W) Segment of first child in linked list. _NEXT equ 0004h ; (W) Segment of next structure in list, or 0. _PARENT equ 0006h ; (W) Segment of parent structure. _CENTRE equ 0008h ; (W) Segment of center point. ;-------- Points. P_MAGIC equ 0000h ; (B) Magic byte. P_OFF equ 0001h ; (B) 1 if it couldn't be put on the screen. P_NEXT equ 0004h ; (W) Segment of next point in form, or 0. P_PARENT equ 0006h ; (W) Segment of form, shape or object. P_X equ 000Ch ; (D) Co-ordinates. P_Y equ 0010h P_Z equ 0014h P_SX equ 0018h ; (W) Where it went on the screen. P_SY equ 001Ah ;-------- Faces. F_MAGIC equ 0000h ; (B) Magic byte. F_NEXT equ 0004h ; (W) Segment of next face, or 0 if none. F_PARENT equ 0006h ; (W) Segment of parent shape. F_COL equ 0008h ; (B) Colour of face (palette index). F_P1 equ 000Ah ; (W) Segments of corner points. F_P2 equ 000Ch F_P3 equ 000Eh ;-------- Shapes. S_MAGIC equ 0000h ; (B) Magic byte. S_NUM equ 0001h ; (B) Number of faces. S_PTR equ 0002h ; (W) Segment of first face in linked list. S_NEXT equ 0004h ; (W) Segment of next shape, or 0 if none. S_PARENT equ 0006h ; (W) Segment of parent form. S_CENTRE equ 0008h ; (W) Segment of center point. ;-------- Forms. M_MAGIC equ 0000h ; (B) Magic byte. M_NUM equ 0001h ; (B) Number of shapes in form. M_PTR equ 0002h ; (W) Segment of first shape in linked list. M_CENTRE equ 0008h ; (W) Segment of center point. M_NUMPTS equ 000Ah ; (B) Number of points in form. M_PTS equ 000Bh ; (W) Segment of first point in linked list. M_USERS equ 000Dh ; (W) Number of objects which use this form. ;-------- Objects. O_MAGIC equ 0000h ; (B) Magic byte. O_NUM equ 0001h ; (B) Number of objects inside it. O_PTR equ 0002h ; (W) Segment of first object in linked list. O_NEXT equ 0004h ; (W) Segment of next object, or 0 if none. O_PARENT equ 0006h ; (W) Segment of object or universe it's in. O_CENTRE equ 0008h ; (W) Segment of center point. O_FORM equ 000Ah ; (W) Segment of form, or 0 if none. O_LEVEL equ 000Ch ; (B) How deep it is - 1 if in the universe. O_X equ 0010h ; (D) Transformations. O_Y equ 0014h O_Z equ 0018h O_RX equ 001Ch ; (B) Rotation about the axes. O_RY equ 001Dh O_RZ equ 001Eh O_RORDER equ 001Fh ; (B) Order of rotations. ;-------- Universe. ; The universe is laid out like an object, with no parent, center or form, ; and a level of 0. U_MAGIC equ 0000h ; (B) Magic byte. U_NUM equ 0001h ; (B) Number of objects in it. U_PTR equ 0002h ; (W) Segment of first object in linked list. U_X equ 0010h ; (D) Transformations. U_Y equ 0014h U_Z equ 0018h U_RX equ 001Ch ; (B) Rotation about the axes. U_RY equ 001Dh U_RZ equ 001Eh U_RORDER equ 001Fh ; (B) Order of rotations. ;-------- Limits. MAX_CHILDREN equ 07Fh ; Most objects inside an object. MAX_DEPTH equ 8 ; Deepest objects can be inside each other. MAX_SHAPES equ 07Fh ; Most shapes in a form. MAX_FACES equ 07Fh ; Most faces in a shape. MAX_POINTS equ 0FFh ; Most points in a form. MAX_COORD equ 007FFFFFh ; Biggest co-ordinate, just under 32768.0. MIN_CELLS equ 16 ; Smallest heap. MAX_CELLS equ 7FFFh ; Biggest heap. ;-------- Camera. SCR2EYE equ 00008000h ; Screen to eye distance, 128.0. NEAR_Z equ 00000100h ; Nearest a point can be to be drawn, 1.0. VIEW_CX equ 160 ; Center of the screen. VIEW_CY equ 100 ;-------- Rendering. ; The transformation stack has one level for the universe and each object ; being drawn, and one more for an object whose center is being measured. TSTACK_LEVELS equ MAX_DEPTH+2 ; Each entry in the draw list is the distance squared (a quad word) and the ; segment of what to draw. The longest the list can get is all the objects ; and forms in every object down to the deepest, and the shapes of one form. SORT_LO equ 0000h ; (D) Distance squared, low double word. SORT_HI equ 0004h ; (D) Distance squared, high double word. SORT_SEG equ 0008h ; (W) Segment, or 0 when it's been drawn. SORT_SIZE equ 10 SORT_ENTRIES equ (MAX_DEPTH+1)*(MAX_CHILDREN+1)+MAX_SHAPES STACK_SIZE equ 2048 ; Stack used while rendering. ;----------------------------- 3DOMS Data Area ------------------------------; _3doms_loggedon db 0 ; 1 when logged on. ; Storage for old INT 3Dh vector. _3doms_int3Dseg dw 0000h _3doms_int3Doff dw 0000h ; Requested min and max size of heap on call to logon routine. _3doms_minheap dw 0000h _3doms_maxheap dw 0000h ; Heap descriptors. _3doms_heapseg dw 0000h ; Segment of allocated memory. _3doms_heapsize dw 0000h ; Size in 32-byte cells of heap. _3doms_heapfree dw 0000h ; Number of free 32-byte cells. _3doms_heapend dw 0000h ; First segment after the heap. ; Segment of universe. _3doms_universe dw 0000h ; Co-ordinates read in by the create functions. _3doms_xyz dd 9 dup (0) ; Rendering. _3doms_callss dw 0000h ; Caller's stack. _3doms_callsp dw 0000h _3doms_failsp dw 0000h ; Stack to go back to on an error. _3doms_tdepth dw 0000h ; Levels on the transformation stack. _3doms_sortptr dw 0000h ; End of the draw list. ; Triangle being drawn. _tri_x1 dw 0000h _tri_y1 dw 0000h _tri_x2 dw 0000h _tri_y2 dw 0000h _tri_x3 dw 0000h _tri_y3 dw 0000h _tri_col db 00h _tri_y dw 0000h ; Row being drawn. _tri_yend dw 0000h ; Last row to draw. ; Line being drawn. _line_len dw 0000h ;----------------------------- Lookup Tables --------------------------------; ; Sine and cosine table - 256 double word entries for each, but the tables ; overlap quite substantially. _3doms_sin dd 000000000h, 000000006h, 00000000Ch, 000000012h ; 0 deg dd 000000019h, 00000001Fh, 000000025h, 00000002Bh dd 000000032h, 000000038h, 00000003Eh, 000000044h dd 00000004Ah, 000000050h, 000000056h, 00000005Ch dd 000000062h, 000000067h, 00000006Dh, 000000073h dd 000000078h, 00000007Eh, 000000083h, 000000089h dd 00000008Eh, 000000093h, 000000098h, 00000009Dh dd 0000000A2h, 0000000A7h, 0000000ACh, 0000000B0h dd 0000000B5h, 0000000B9h, 0000000BDh, 0000000C2h dd 0000000C6h, 0000000CAh, 0000000CDh, 0000000D1h dd 0000000D5h, 0000000D8h, 0000000DBh, 0000000DFh dd 0000000E2h, 0000000E4h, 0000000E7h, 0000000EAh dd 0000000ECh, 0000000EFh, 0000000F1h, 0000000F3h dd 0000000F5h, 0000000F7h, 0000000F8h, 0000000FAh dd 0000000FBh, 0000000FCh, 0000000FDh, 0000000FEh dd 0000000FFh, 0000000FFh, 0000000FFh, 000000100h _3doms_cos dd 000000100h, 000000100h, 0000000FFh, 0000000FFh ; 90 deg dd 0000000FFh, 0000000FEh, 0000000FDh, 0000000FCh dd 0000000FBh, 0000000FAh, 0000000F8h, 0000000F7h dd 0000000F5h, 0000000F3h, 0000000F1h, 0000000EFh dd 0000000ECh, 0000000EAh, 0000000E7h, 0000000E4h dd 0000000E2h, 0000000DFh, 0000000DBh, 0000000D8h dd 0000000D5h, 0000000D1h, 0000000CDh, 0000000CAh dd 0000000C6h, 0000000C2h, 0000000BDh, 0000000B9h dd 0000000B5h, 0000000B0h, 0000000ACh, 0000000A7h dd 0000000A2h, 00000009Dh, 000000098h, 000000093h dd 00000008Eh, 000000089h, 000000083h, 00000007Eh dd 000000078h, 000000073h, 00000006Dh, 000000067h dd 000000062h, 00000005Ch, 000000056h, 000000050h dd 00000004Ah, 000000044h, 00000003Eh, 000000038h dd 000000032h, 00000002Bh, 000000025h, 00000001Fh dd 000000019h, 000000012h, 00000000Ch, 000000006h dd 000000000h, 0FFFFFFFAh, 0FFFFFFF4h, 0FFFFFFEEh ; 180 deg dd 0FFFFFFE7h, 0FFFFFFE1h, 0FFFFFFDBh, 0FFFFFFD5h dd 0FFFFFFCEh, 0FFFFFFC8h, 0FFFFFFC2h, 0FFFFFFBCh dd 0FFFFFFB6h, 0FFFFFFB0h, 0FFFFFFAAh, 0FFFFFFA4h dd 0FFFFFF9Eh, 0FFFFFF99h, 0FFFFFF93h, 0FFFFFF8Dh dd 0FFFFFF88h, 0FFFFFF82h, 0FFFFFF7Dh, 0FFFFFF77h dd 0FFFFFF72h, 0FFFFFF6Dh, 0FFFFFF68h, 0FFFFFF63h dd 0FFFFFF5Eh, 0FFFFFF59h, 0FFFFFF54h, 0FFFFFF50h dd 0FFFFFF4Bh, 0FFFFFF47h, 0FFFFFF43h, 0FFFFFF3Eh dd 0FFFFFF3Ah, 0FFFFFF36h, 0FFFFFF33h, 0FFFFFF2Fh dd 0FFFFFF2Bh, 0FFFFFF28h, 0FFFFFF25h, 0FFFFFF21h dd 0FFFFFF1Eh, 0FFFFFF1Ch, 0FFFFFF19h, 0FFFFFF16h dd 0FFFFFF14h, 0FFFFFF11h, 0FFFFFF0Fh, 0FFFFFF0Dh dd 0FFFFFF0Bh, 0FFFFFF09h, 0FFFFFF08h, 0FFFFFF06h dd 0FFFFFF05h, 0FFFFFF04h, 0FFFFFF03h, 0FFFFFF02h dd 0FFFFFF01h, 0FFFFFF01h, 0FFFFFF01h, 0FFFFFF00h dd 0FFFFFF00h, 0FFFFFF00h, 0FFFFFF01h, 0FFFFFF01h ; 270 deg dd 0FFFFFF01h, 0FFFFFF02h, 0FFFFFF03h, 0FFFFFF04h dd 0FFFFFF05h, 0FFFFFF06h, 0FFFFFF08h, 0FFFFFF09h dd 0FFFFFF0Bh, 0FFFFFF0Dh, 0FFFFFF0Fh, 0FFFFFF11h dd 0FFFFFF14h, 0FFFFFF16h, 0FFFFFF19h, 0FFFFFF1Ch dd 0FFFFFF1Eh, 0FFFFFF21h, 0FFFFFF25h, 0FFFFFF28h dd 0FFFFFF2Bh, 0FFFFFF2Fh, 0FFFFFF33h, 0FFFFFF36h dd 0FFFFFF3Ah, 0FFFFFF3Eh, 0FFFFFF43h, 0FFFFFF47h dd 0FFFFFF4Bh, 0FFFFFF50h, 0FFFFFF54h, 0FFFFFF59h dd 0FFFFFF5Eh, 0FFFFFF63h, 0FFFFFF68h, 0FFFFFF6Dh dd 0FFFFFF72h, 0FFFFFF77h, 0FFFFFF7Dh, 0FFFFFF82h dd 0FFFFFF88h, 0FFFFFF8Dh, 0FFFFFF93h, 0FFFFFF99h dd 0FFFFFF9Eh, 0FFFFFFA4h, 0FFFFFFAAh, 0FFFFFFB0h dd 0FFFFFFB6h, 0FFFFFFBCh, 0FFFFFFC2h, 0FFFFFFC8h dd 0FFFFFFCEh, 0FFFFFFD5h, 0FFFFFFDBh, 0FFFFFFE1h dd 0FFFFFFE7h, 0FFFFFFEEh, 0FFFFFFF4h, 0FFFFFFFAh dd 000000000h, 000000006h, 00000000Ch, 000000012h ; 360 deg dd 000000019h, 00000001Fh, 000000025h, 00000002Bh dd 000000032h, 000000038h, 00000003Eh, 000000044h dd 00000004Ah, 000000050h, 000000056h, 00000005Ch dd 000000062h, 000000067h, 00000006Dh, 000000073h dd 000000078h, 00000007Eh, 000000083h, 000000089h dd 00000008Eh, 000000093h, 000000098h, 00000009Dh dd 0000000A2h, 0000000A7h, 0000000ACh, 0000000B0h dd 0000000B5h, 0000000B9h, 0000000BDh, 0000000C2h dd 0000000C6h, 0000000CAh, 0000000CDh, 0000000D1h dd 0000000D5h, 0000000D8h, 0000000DBh, 0000000DFh dd 0000000E2h, 0000000E4h, 0000000E7h, 0000000EAh dd 0000000ECh, 0000000EFh, 0000000F1h, 0000000F3h dd 0000000F5h, 0000000F7h, 0000000F8h, 0000000FAh dd 0000000FBh, 0000000FCh, 0000000FDh, 0000000FEh dd 0000000FFh, 0000000FFh, 0000000FFh, 000000100h ;----------------------------- Initialisation Code --------------------------; ;-------- Logon_3DOMS ; Sets up the heap, the universe and the interrupt vector. ; Inputs: ; BX - Min cells to allocate. ; DX - Max cells to allocate. ; Returns: ; Carry clear: ; AX - Segment of heap. ; CX - Size of heap in cells. ; Carry set: ; AX - Error code. ; CX - Cells available, if the error is ERROR_HEAPOUT. logon_3doms: ; Only log on once. cmp _3doms_loggedon, 0 je logon_3doms_notyet mov ax, ERROR_LOGGEDON stc ret logon_3doms_notyet: ; Make sure the sizes make sense. cmp bx, MIN_CELLS jb logon_3doms_badsize cmp dx, MAX_CELLS ja logon_3doms_badsize cmp bx, dx jbe logon_3doms_sizeok logon_3doms_badsize: mov ax, ERROR_BADSIZE stc ret logon_3doms_sizeok: push bx push dx push di push ds push es ; Allocate memory. ; Save parameters. mov _3doms_minheap, bx mov _3doms_maxheap, dx ; First try the maximum size requested. A cell is two paragraphs. mov ah, 48h mov bx, dx shl bx, 1 int 21h ; Was that allocation successful? jnc logon_3doms_memok ; If not that much memory free, BX says how much there is. See if the ; minimum can be allocated. shr bx, 1 cmp bx, _3doms_minheap jb logon_3doms_nomem ; Allocate all that can be allocated. shl bx, 1 mov ah, 48h int 21h jnc logon_3doms_memok shr bx, 1 logon_3doms_nomem: ; There is not enough memory to satisfy the minimum memory requirements. mov cx, bx mov ax, ERROR_HEAPOUT pop es pop ds pop di pop dx pop bx stc ret logon_3doms_memok: mov _3doms_heapseg, ax mov _3doms_universe, ax add ax, bx mov _3doms_heapend, ax shr bx, 1 mov _3doms_heapsize, bx mov _3doms_heapfree, bx ; Set all cells on heap to free. mov ax, _3doms_heapseg mov cx, bx logon_3doms_freeheap: mov ds, ax mov byte ptr ds: [_MAGIC], MAGIC_FREE add ax, 2 loop logon_3doms_freeheap ; Set up universe structure in the first cell, at the origin and not ; turned. mov es, _3doms_heapseg xor di, di xor ax, ax mov cx, 16 cld rep stosw mov byte ptr es: [U_MAGIC], MAGIC_UNIVERSE dec _3doms_heapfree ; Change interrupt. ; Get old interrupt vector. mov ax, 353Dh int 21h mov _3doms_int3Dseg, es mov _3doms_int3Doff, bx ; Set interrupt vector. mov ax, 253Dh push cs pop ds mov dx, offset int3D_intvect int 21h mov _3doms_loggedon, 1 pop es pop ds pop di pop dx pop bx mov ax, _3doms_heapseg mov cx, _3doms_heapsize clc ret ;-------- Logoff_3DOMS ; Frees the heap and restores interrupt vector. ; Returns: ; Carry set if not logged on. logoff_3doms: cmp _3doms_loggedon, 0 jne logoff_3doms_on mov ax, ERROR_NOTLOGGEDON stc ret logoff_3doms_on: push dx push ds push es ; Free allocated memory. mov ah, 49h mov es, _3doms_heapseg int 21h ; Reset interrupt vector. mov ax, 253Dh mov dx, _3doms_int3Doff mov ds, _3doms_int3Dseg int 21h mov _3doms_loggedon, 0 pop es pop ds pop dx clc ret ;----------------------------- The 3DOMS Interrupt --------------------------; ; 3DOMS Identifier - look for to see if 3DOMS TSR loaded. db '3DOMSv', 00h, 15h ; The 3DOMS interrupt handler. int3D_intvect: sti cmp ah, 00h je int3D_info ; Every other function keeps all the registers apart from AX. push ebx push ecx push edx push esi push edi push ebp push ds push es cld call int3D_function pop es pop ds pop ebp pop edi pop esi pop edx pop ecx pop ebx jc int3D_error jmp int3D_end ; Set carry flag on stack and IRET. int3D_error: push bp mov bp, sp or word ptr ss: [bp+6], 0001h pop bp iret ; Clear carry flag on stack and IRET. int3D_end: push bp mov bp, sp and word ptr ss: [bp+6], 0FFFEh pop bp iret ;-------- Branch to the required function. ; Each function returns with carry clear, or with carry set and an error code ; in AX. int3D_function: cmp ah, 10h je int3D_move cmp ah, 11h je int3D_formobject cmp ah, 12h je int3D_centre cmp ah, 18h je int3D_delobject cmp ah, 19h je int3D_delform cmp ah, 1Bh je int3D_newobject cmp ah, 1Ch je int3D_newform cmp ah, 1Dh je int3D_newshape cmp ah, 1Eh je int3D_newface cmp ah, 20h je int3D_render cmp ah, 2Ch je int3D_triangle cmp ah, 2Dh je int3D_line mov ax, ERROR_BADFUNCTION stc ret ;----------------------------- The 3DOMS Functions --------------------------; ;-------- Function 00h - Info. ; Inputs: ; AH - 00h ; Returns: ; AH - Major version number. ; AL - Minor version number. ; BX - Segment of heap. ; CX - Size of heap in 32-byte cells. ; DX - Number of free 32-byte cells in heap. int3D_info: mov ax, 0015h ; Version 0.15 (00.15). mov bx, _3doms_heapseg mov cx, _3doms_heapsize mov dx, _3doms_heapfree jmp int3D_end ;-------- Function 10h - Move an object or the universe. ; Inputs: ; AH - 10h ; DS:SI - Position and angles, 16 bytes. ; ES - Object or universe. int3D_move: ; Make sure it's an object or the universe. mov ax, es call int3D_checkholder jnc int3D_move_ok mov ax, ERROR_BADOBJECT stc ret int3D_move_ok: ; Check the data: all 16 bytes have to be there, the position has to be ; in range and the rotation order has to be one of the six. mov cx, 16 call int3D_checkbuffer jnc int3D_move_bufferok mov ax, ERROR_BADBUFFER stc ret int3D_move_bufferok: mov cx, 3 call int3D_readxyz jnc int3D_move_coordok ret int3D_move_coordok: mov al, ds: [si+15] test al, 11111000b jnz int3D_move_badorder and al, 00000011b cmp al, 00000011b jne int3D_move_orderok int3D_move_badorder: mov ax, ERROR_BADORDER stc ret int3D_move_orderok: ; Copy the position and angles. mov di, O_X mov cx, 8 rep movsw clc ret ;-------- Function 11h - Change the form of an object. ; Inputs: ; AH - 11h ; BX - New form (or 0000h to take the form away). ; ES - Object. ; Returns: ; AX - Old form, or 0. int3D_formobject: ; Make sure object is valid. mov ax, es mov dl, MAGIC_OBJECT call int3D_checkcell jnc int3D_formobject_objectok mov ax, ERROR_BADOBJECT stc ret int3D_formobject_objectok: ; Check new form is valid. or bx, bx jz int3D_formobject_newok mov ax, bx mov dl, MAGIC_FORM call int3D_checkcell jnc int3D_formobject_newok mov ax, ERROR_BADFORM stc ret int3D_formobject_newok: ; The old form has one less object using it. mov ax, es: [O_FORM] call int3D_unuseform ; And the new one has one more. or bx, bx jz int3D_formobject_nonew mov ds, bx inc word ptr ds: [M_USERS] int3D_formobject_nonew: mov es: [O_FORM], bx clc ret ;-------- Function 12h - Change the center of an object, form or shape. ; Inputs: ; AH - 12h ; DS:SI - New center, 3 double words. ; ES - Object, form or shape. int3D_centre: ; Make sure it's something with a center. mov ax, es mov dl, MAGIC_OBJECT call int3D_checkcell jnc int3D_centre_ok mov dl, MAGIC_FORM call int3D_checkcell jnc int3D_centre_ok mov dl, MAGIC_SHAPE call int3D_checkcell jnc int3D_centre_ok mov ax, ERROR_BADOBJECT stc ret int3D_centre_ok: ; Read the new center. mov cx, 3 call int3D_readxyz jnc int3D_centre_coordok ret int3D_centre_coordok: ; Objects, forms and shapes all keep their center point in the same ; place. mov ax, es: [_CENTRE] mov dl, MAGIC_POINT call int3D_checkcell jnc int3D_centre_pointok mov ax, ERROR_BADPOINT stc ret int3D_centre_pointok: mov ds, ax mov eax, _3doms_xyz mov ds: [P_X], eax mov eax, _3doms_xyz+4 mov ds: [P_Y], eax mov eax, _3doms_xyz+8 mov ds: [P_Z], eax clc ret ;-------- Function 18h - Delete an object. ; Inputs: ; AH - 18h ; ES - Object. ; Returns: ; AX - Object or universe it was in. int3D_delobject: ; Make sure object is valid, and empty. mov ax, es mov dl, MAGIC_OBJECT call int3D_checkcell jnc int3D_delobject_objectok mov ax, ERROR_BADOBJECT stc ret int3D_delobject_objectok: cmp byte ptr es: [O_NUM], 0 je int3D_delobject_empty mov ax, ERROR_HASCHILDREN stc ret int3D_delobject_empty: ; Check its parent. mov ax, es: [O_PARENT] call int3D_checkholder jnc int3D_delobject_parentok mov ax, ERROR_BADPARENT stc ret int3D_delobject_parentok: mov ds, ax ; Find it in its parent's list, keeping the one before it in DI. xor cx, cx mov cl, ds: [O_NUM] mov bx, es xor di, di mov ax, ds: [O_PTR] int3D_delobject_find: jcxz int3D_delobject_badlist cmp ax, bx je int3D_delobject_found mov dl, MAGIC_OBJECT call int3D_checkcell jc int3D_delobject_badlist mov di, ax push ds mov ds, ax mov ax, ds: [O_NEXT] pop ds dec cx jmp int3D_delobject_find int3D_delobject_badlist: mov ax, ERROR_BADLIST stc ret int3D_delobject_found: ; Take it out of the list. mov ax, es: [O_NEXT] or di, di jnz int3D_delobject_notfirst mov ds: [O_PTR], ax jmp int3D_delobject_unlinked int3D_delobject_notfirst: push ds mov ds, di mov ds: [O_NEXT], ax pop ds int3D_delobject_unlinked: dec byte ptr ds: [O_NUM] ; Its form has one less object using it. mov ax, es: [O_FORM] call int3D_unuseform ; Free its center and the object. mov ax, es: [O_CENTRE] mov dl, MAGIC_POINT call int3D_checkcell jc int3D_delobject_nocentre call int3D_freecell int3D_delobject_nocentre: mov ax, es call int3D_freecell mov ax, ds clc ret ;-------- Function 19h - Delete a form. ; Inputs: ; AH - 19h ; ES - Form. int3D_delform: ; Check if form is valid, and not used. mov ax, es mov dl, MAGIC_FORM call int3D_checkcell jnc int3D_delform_formok mov ax, ERROR_BADFORM stc ret int3D_delform_formok: cmp word ptr es: [M_USERS], 0 je int3D_delform_unused mov ax, ERROR_INUSE stc ret int3D_delform_unused: ; Go through everything in it once to check it, so that nothing is freed ; unless all of it can be, then again to free it. xor bp, bp call int3D_walkform jc int3D_delform_end mov bp, 1 call int3D_walkform ; Free its center and the form. mov ax, es: [M_CENTRE] call int3D_freecell mov ax, es call int3D_freecell clc int3D_delform_end: ret ;-------- Function 1Bh - Create an object. ; Inputs: ; AH - 1Bh ; DS:SI - Center, 3 double words. ; ES - Object or universe to put it in. ; Returns: ; AX - Object. int3D_newobject: ; Make sure parent structure is valid, has room for another object and ; isn't too deep. mov ax, es call int3D_checkholder jnc int3D_newobject_parentok mov ax, ERROR_BADPARENT stc ret int3D_newobject_parentok: cmp byte ptr es: [O_NUM], MAX_CHILDREN jb int3D_newobject_numok mov ax, ERROR_TOOMANY stc ret int3D_newobject_numok: cmp byte ptr es: [O_LEVEL], MAX_DEPTH jb int3D_newobject_levelok mov ax, ERROR_TOODEEP stc ret int3D_newobject_levelok: ; Read the center. mov cx, 3 call int3D_readxyz jnc int3D_newobject_coordok ret int3D_newobject_coordok: ; Make sure there are cells for the center and the object. cmp _3doms_heapfree, 2 jae int3D_newobject_memok mov ax, ERROR_HEAPOUT stc ret int3D_newobject_memok: ; Make the center and the object. mov si, offset _3doms_xyz call int3D_newpoint mov bx, ax call int3D_takecell mov ds, ax mov byte ptr ds: [O_MAGIC], MAGIC_OBJECT mov ds: [O_CENTRE], bx mov dl, es: [O_LEVEL] inc dl mov ds: [O_LEVEL], dl ; Put it at the front of its parent's list. mov ds: [O_PARENT], es mov dx, es: [O_PTR] mov ds: [O_NEXT], dx mov es: [O_PTR], ax inc byte ptr es: [O_NUM] ; The center belongs to the object. mov ds, bx mov ds: [P_PARENT], ax clc ret ;-------- Function 1Ch - Create a form. ; Inputs: ; AH - 1Ch ; DS:SI - Center, 3 double words. ; Returns: ; AX - Form. int3D_newform: ; Read the center. mov cx, 3 call int3D_readxyz jnc int3D_newform_coordok ret int3D_newform_coordok: ; Make sure there are cells for the center and the form. cmp _3doms_heapfree, 2 jae int3D_newform_memok mov ax, ERROR_HEAPOUT stc ret int3D_newform_memok: ; Make the center and the form. mov si, offset _3doms_xyz call int3D_newpoint mov bx, ax call int3D_takecell mov ds, ax mov byte ptr ds: [M_MAGIC], MAGIC_FORM mov ds: [M_CENTRE], bx ; The center belongs to the form. mov ds, bx mov ds: [P_PARENT], ax clc ret ;-------- Function 1Dh - Create a shape. ; Inputs: ; AH - 1Dh ; DS:SI - Center, 3 double words. ; ES - Form. ; Returns: ; AX - Shape. int3D_newshape: ; Make sure parent form is valid and has room for another shape. mov ax, es mov dl, MAGIC_FORM call int3D_checkcell jnc int3D_newshape_formok mov ax, ERROR_BADFORM stc ret int3D_newshape_formok: cmp byte ptr es: [M_NUM], MAX_SHAPES jb int3D_newshape_numok mov ax, ERROR_TOOMANY stc ret int3D_newshape_numok: ; Read the center. mov cx, 3 call int3D_readxyz jnc int3D_newshape_coordok ret int3D_newshape_coordok: ; Make sure there are cells for the center and the shape. cmp _3doms_heapfree, 2 jae int3D_newshape_memok mov ax, ERROR_HEAPOUT stc ret int3D_newshape_memok: ; Make the center and the shape. mov si, offset _3doms_xyz call int3D_newpoint mov bx, ax call int3D_takecell mov ds, ax mov byte ptr ds: [S_MAGIC], MAGIC_SHAPE mov ds: [S_CENTRE], bx ; Put it at the front of the form's list. mov ds: [S_PARENT], es mov dx, es: [M_PTR] mov ds: [S_NEXT], dx mov es: [M_PTR], ax inc byte ptr es: [M_NUM] ; The center belongs to the shape. mov ds, bx mov ds: [P_PARENT], ax clc ret ;-------- Function 1Eh - Create a face. ; Inputs: ; AH - 1Eh ; AL - Colour. ; DS:SI - Corners, 9 double words. ; ES - Shape. ; Returns: ; AX - Face. int3D_newface: mov bl, al ; Make sure parent shape is valid and has room for another face. mov ax, es mov dl, MAGIC_SHAPE call int3D_checkcell jnc int3D_newface_shapeok mov ax, ERROR_BADSHAPE stc ret int3D_newface_shapeok: cmp byte ptr es: [S_NUM], MAX_FACES jb int3D_newface_numok mov ax, ERROR_TOOMANY stc ret int3D_newface_numok: ; The form the shape is in has to have room for three more points. mov ax, es: [S_PARENT] mov dl, MAGIC_FORM call int3D_checkcell jnc int3D_newface_formok mov ax, ERROR_BADFORM stc ret int3D_newface_formok: mov bp, ax push ds mov ds, ax cmp byte ptr ds: [M_NUMPTS], MAX_POINTS-2 pop ds jb int3D_newface_pointsok mov ax, ERROR_TOOMANY stc ret int3D_newface_pointsok: ; Read the corners. mov cx, 9 call int3D_readxyz jnc int3D_newface_coordok ret int3D_newface_coordok: ; Make sure there are cells for the face and three new points. cmp _3doms_heapfree, 4 jae int3D_newface_memok mov ax, ERROR_HEAPOUT stc ret int3D_newface_memok: ; Make the face. call int3D_takecell mov di, ax mov ds, ax mov byte ptr ds: [F_MAGIC], MAGIC_FACE mov ds: [F_COL], bl ; Find its corners among the form's points, or add them. push es mov es, bp mov si, offset _3doms_xyz call int3D_findpoint mov ds: [F_P1], ax add si, 12 call int3D_findpoint mov ds: [F_P2], ax add si, 12 call int3D_findpoint mov ds: [F_P3], ax pop es ; Put it at the front of the shape's list. mov ds: [F_PARENT], es mov ax, es: [S_PTR] mov ds: [F_NEXT], ax mov es: [S_PTR], di inc byte ptr es: [S_NUM] mov ax, di clc ret ;-------- Function 20h - Render. ; Inputs: ; AH - 20h ; DS - Universe. ; ES - Output Segment. int3D_render: ; Check universe and output buffer. mov ax, ds cmp ax, _3doms_universe jne int3D_render_baduniverse cmp byte ptr ds: [U_MAGIC], MAGIC_UNIVERSE je int3D_render_universeok int3D_render_baduniverse: mov ax, ERROR_BADUNIVERSE stc ret int3D_render_universeok: call int3D_checkoutput jnc int3D_render_outputok mov ax, ERROR_BADOUTPUT stc ret int3D_render_outputok: ; Render on 3DOMS's own stack, so there is always room. mov _3doms_callss, ss mov _3doms_callsp, sp mov ax, cs mov ss, ax mov sp, offset _3doms_stacktop mov _3doms_tdepth, 0 mov _3doms_sortptr, offset _3doms_sorttab call int3D_drawstart ; Back to the caller's stack, keeping AX and the carry flag. mov ss, _3doms_callss mov sp, _3doms_callsp ret ; Start drawing at the universe. If anything goes wrong while drawing, ; int3D_drawfail comes straight back here, however deep it is. int3D_drawstart: mov _3doms_failsp, sp call int3D_drawobject clc ret int3D_drawfail: mov sp, _3doms_failsp stc ret ;-------- Function 2Ch - Triangle. ; Inputs: ; AH - 2Ch ; AL - Colour. ; BX, CX - First corner. ; SI, BP - Second corner. ; DX, DI - Third corner. ; ES - Output segment. int3D_triangle: call int3D_checkoutput jnc int3D_triangle_outputok mov ax, ERROR_BADOUTPUT stc ret int3D_triangle_outputok: mov _tri_x1, bx mov _tri_y1, cx mov _tri_x2, si mov _tri_y2, bp mov _tri_x3, dx mov _tri_y3, di call int3D_tri clc ret ;-------- Function 2Dh - Line. ; Inputs: ; AH - 2Dh ; AL - Colour. ; BX, CX - Start. ; SI, BP - End. ; ES - Output segment. int3D_line: call int3D_checkoutput jnc int3D_line_outputok mov ax, ERROR_BADOUTPUT stc ret int3D_line_outputok: ; Both ends have to be on the screen. Unsigned compares catch the ; negative ones as well. cmp bx, 319 ja int3D_line_off cmp si, 319 ja int3D_line_off cmp cx, 199 ja int3D_line_off cmp bp, 199 jbe int3D_line_on int3D_line_off: mov ax, ERROR_OFFSCREEN stc ret int3D_line_on: mov _tri_col, al ; Offset of the start in the buffer. mov ax, 320 mul cx add ax, bx mov di, ax ; How far it goes, and which way, along x and y. mov ax, si sub ax, bx mov bx, 1 jge int3D_line_right neg ax mov bx, -1 int3D_line_right: mov dx, bp sub dx, cx mov cx, 320 jge int3D_line_down neg dx mov cx, -320 int3D_line_down: ; Step along whichever is longer, and the other way when the error ; term runs out. cmp ax, dx jae int3D_line_major xchg ax, dx xchg bx, cx int3D_line_major: mov _line_len, ax mov si, ax shr si, 1 mov bp, ax inc bp mov al, _tri_col int3D_line_loop: mov es: [di], al add di, bx sub si, dx jge int3D_line_noadj add si, _line_len add di, cx int3D_line_noadj: dec bp jnz int3D_line_loop clc ret ;----------------------------- Structure Routines ---------------------------; ;-------- Routine: Check cell. ; Makes sure a segment is a cell on the heap holding the structure wanted. ; Inputs: ; AX - Segment. ; DL - Magic wanted. ; Returns: ; Carry clear if it is, carry set if not. int3D_checkcell: push ax push ds cmp ax, _3doms_heapseg jb int3D_checkcell_bad cmp ax, _3doms_heapend jae int3D_checkcell_bad mov ds, ax sub ax, _3doms_heapseg test al, 01h jnz int3D_checkcell_bad cmp ds: [_MAGIC], dl jne int3D_checkcell_bad pop ds pop ax clc ret int3D_checkcell_bad: pop ds pop ax stc ret ;-------- Routine: Check holder. ; Makes sure a segment is an object or the universe - something that can ; hold objects. ; Inputs: ; AX - Segment. ; Returns: ; Carry clear if it is, carry set if not. int3D_checkholder: push dx mov dl, MAGIC_OBJECT call int3D_checkcell jnc int3D_checkholder_end mov dl, MAGIC_UNIVERSE call int3D_checkcell int3D_checkholder_end: pop dx ret ;-------- Routine: Check buffer. ; Makes sure CX bytes at SI fit before the end of the segment. ; Returns: ; Carry set if they don't. int3D_checkbuffer: push ax mov ax, cx dec ax add ax, si pop ax ret ;-------- Routine: Check output. ; Makes sure the 64000-byte buffer at ES doesn't overlap the heap or 3DOMS. ; Returns: ; Carry set if it does. int3D_checkoutput: push eax push ebx push ecx push edx ; First and last paragraphs of the buffer. xor eax, eax mov ax, es mov edx, eax add edx, 0F9Fh ; The heap. xor ebx, ebx mov bx, _3doms_heapseg xor ecx, ecx mov cx, _3doms_heapend dec ecx call int3D_overlap jc int3D_checkoutput_end ; 3DOMS. xor ebx, ebx mov bx, cs xor ecx, ecx mov cx, offset int3D_last add ecx, 15 shr ecx, 4 add ecx, ebx push ecx xor ecx, ecx mov cx, offset int3D_first shr ecx, 4 add ebx, ecx pop ecx call int3D_overlap int3D_checkoutput_end: pop edx pop ecx pop ebx pop eax ret ; Carry set if paragraphs EAX to EDX overlap EBX to ECX. int3D_overlap: cmp edx, ebx jb int3D_overlap_no cmp eax, ecx ja int3D_overlap_no stc ret int3D_overlap_no: clc ret ;-------- Routine: Read co-ordinates. ; Reads CX double word co-ordinates from DS:SI into _3doms_xyz, making sure ; they are all there and in range. ; Returns: ; Carry set and AX - Error code if they aren't. int3D_readxyz: push cx push si push di push cx shl cx, 2 call int3D_checkbuffer pop cx jc int3D_readxyz_badbuffer mov di, offset _3doms_xyz int3D_readxyz_loop: lodsd cmp eax, MAX_COORD jg int3D_readxyz_badcoord cmp eax, -MAX_COORD jl int3D_readxyz_badcoord mov cs: [di], eax add di, 4 loop int3D_readxyz_loop pop di pop si pop cx clc ret int3D_readxyz_badbuffer: mov ax, ERROR_BADBUFFER jmp int3D_readxyz_fail int3D_readxyz_badcoord: mov ax, ERROR_BADCOORD int3D_readxyz_fail: pop di pop si pop cx stc ret ;-------- Routine: Take cell. ; Takes the first free cell on the heap and clears it. Callers have already ; made sure there are enough free cells. ; Returns: ; AX - Segment of cell. int3D_takecell: push cx push di push es mov ax, _3doms_heapseg mov cx, _3doms_heapsize int3D_takecell_loop: mov es, ax cmp byte ptr es: [_MAGIC], MAGIC_FREE je int3D_takecell_found add ax, 2 loop int3D_takecell_loop int3D_takecell_found: push ax xor di, di xor ax, ax mov cx, 16 rep stosw pop ax mov byte ptr es: [_MAGIC], MAGIC_TAKEN dec _3doms_heapfree pop es pop di pop cx ret ;-------- Routine: Unuse form. ; Counts one less object using a form. ; Inputs: ; AX - Form, or 0. int3D_unuseform: push dx push ds mov dl, MAGIC_FORM call int3D_checkcell jc int3D_unuseform_end mov ds, ax cmp word ptr ds: [M_USERS], 0 je int3D_unuseform_end dec word ptr ds: [M_USERS] int3D_unuseform_end: pop ds pop dx ret ;-------- Routine: Free cell. ; Inputs: ; AX - Segment of cell. int3D_freecell: push ds mov ds, ax mov byte ptr ds: [_MAGIC], MAGIC_FREE inc _3doms_heapfree pop ds ret ;-------- Routine: New point. ; Inputs: ; CS:SI - x, y and z. ; Returns: ; AX - Segment of point. int3D_newpoint: push edx push ds call int3D_takecell mov ds, ax mov byte ptr ds: [P_MAGIC], MAGIC_POINT mov byte ptr ds: [P_OFF], 1 mov edx, cs: [si] mov ds: [P_X], edx mov edx, cs: [si+4] mov ds: [P_Y], edx mov edx, cs: [si+8] mov ds: [P_Z], edx pop ds pop edx ret ;-------- Routine: Find point. ; Finds the point in a form at the co-ordinates given, or adds a new one. ; Inputs: ; CS:SI - x, y and z. ; ES - Form. ; Returns: ; AX - Segment of point. int3D_findpoint: push cx push edx push ds xor cx, cx mov cl, es: [M_NUMPTS] mov ax, es: [M_PTS] jcxz int3D_findpoint_new int3D_findpoint_loop: mov ds, ax mov edx, cs: [si] cmp edx, ds: [P_X] jne int3D_findpoint_next mov edx, cs: [si+4] cmp edx, ds: [P_Y] jne int3D_findpoint_next mov edx, cs: [si+8] cmp edx, ds: [P_Z] je int3D_findpoint_end int3D_findpoint_next: mov ax, ds: [P_NEXT] loop int3D_findpoint_loop ; It isn't there, so make it and put it at the front of the form's list. int3D_findpoint_new: call int3D_newpoint mov ds, ax mov ds: [P_PARENT], es mov dx, es: [M_PTS] mov ds: [P_NEXT], dx mov es: [M_PTS], ax inc byte ptr es: [M_NUMPTS] int3D_findpoint_end: pop ds pop edx pop cx ret ;-------- Routine: Walk form. ; Goes through everything in a form, checking every structure in it and that ; every list matches its count. If BP is 1, it frees them as well. The ; form's own cell and center are left alone. ; Inputs: ; BP - 0 to check, 1 to check and free. ; ES - Form. ; Returns: ; Carry set and AX - ERROR_BADLIST if anything is wrong. int3D_walkform: push bx push cx push dx push ds ; Its center. mov ax, es: [M_CENTRE] mov dl, MAGIC_POINT call int3D_checkcell jc int3D_walkform_bad ; Its shapes. xor cx, cx mov cl, es: [M_NUM] mov ax, es: [M_PTR] int3D_walkform_shape: jcxz int3D_walkform_shapesdone mov dl, MAGIC_SHAPE call int3D_checkcell jc int3D_walkform_bad mov ds, ax ; The shape's faces. push cx xor cx, cx mov cl, ds: [S_NUM] mov ax, ds: [S_PTR] int3D_walkform_face: jcxz int3D_walkform_facesdone mov dl, MAGIC_FACE call int3D_checkcell jc int3D_walkform_badface push ds mov ds, ax mov bx, ds: [F_NEXT] pop ds or bp, bp jz int3D_walkform_keepface call int3D_freecell int3D_walkform_keepface: mov ax, bx dec cx jmp int3D_walkform_face int3D_walkform_facesdone: pop cx or ax, ax jnz int3D_walkform_bad ; The shape's center, then the shape. mov ax, ds: [S_CENTRE] mov dl, MAGIC_POINT call int3D_checkcell jc int3D_walkform_bad or bp, bp jz int3D_walkform_keepcentre call int3D_freecell int3D_walkform_keepcentre: mov bx, ds: [S_NEXT] or bp, bp jz int3D_walkform_keepshape mov ax, ds call int3D_freecell int3D_walkform_keepshape: mov ax, bx dec cx jmp int3D_walkform_shape int3D_walkform_shapesdone: or ax, ax jnz int3D_walkform_bad ; Its points. xor cx, cx mov cl, es: [M_NUMPTS] mov ax, es: [M_PTS] int3D_walkform_point: jcxz int3D_walkform_pointsdone mov dl, MAGIC_POINT call int3D_checkcell jc int3D_walkform_bad push ds mov ds, ax mov bx, ds: [P_NEXT] pop ds or bp, bp jz int3D_walkform_keeppoint call int3D_freecell int3D_walkform_keeppoint: mov ax, bx dec cx jmp int3D_walkform_point int3D_walkform_pointsdone: or ax, ax jnz int3D_walkform_bad pop ds pop dx pop cx pop bx clc ret int3D_walkform_badface: pop cx int3D_walkform_bad: mov ax, ERROR_BADLIST pop ds pop dx pop cx pop bx stc ret ;----------------------------- Rendering Routines ---------------------------; ;-------- Routine: Draw object. ; Draws an object, or the universe, and everything in it. Its own form and ; the objects in it are put in a list by how far their centers are from the ; camera, and drawn starting with the furthest. ; Inputs: ; DS - Object or universe. ; ES - Output segment. int3D_drawobject: call int3D_pushtransform push _3doms_sortptr ; Its own form goes in the list by the distance of the form's center. ; The universe has no form. cmp byte ptr ds: [_MAGIC], MAGIC_OBJECT jne int3D_drawobject_children mov ax, ds: [O_FORM] or ax, ax jz int3D_drawobject_children mov dl, MAGIC_FORM call int3D_checkcell jc int3D_drawobject_badform mov bx, ax push ds mov ds, ax mov ax, ds: [M_CENTRE] pop ds call int3D_measure ; So does each object in it, by its center with its own position and ; angles as well. int3D_drawobject_children: xor cx, cx mov cl, ds: [O_NUM] mov ax, ds: [O_PTR] int3D_drawobject_child: jcxz int3D_drawobject_listed mov dl, MAGIC_OBJECT call int3D_checkcell jc int3D_drawobject_badobject push ds mov ds, ax mov bx, ax call int3D_pushtransform mov ax, ds: [O_CENTRE] call int3D_measure dec _3doms_tdepth mov ax, ds: [O_NEXT] pop ds dec cx jmp int3D_drawobject_child int3D_drawobject_listed: or ax, ax jnz int3D_drawobject_badlist ; Draw them, furthest first. int3D_drawobject_draw: mov bp, sp mov si, ss: [bp] call int3D_takefurthest or ax, ax jz int3D_drawobject_done push ds mov ds, ax cmp byte ptr ds: [_MAGIC], MAGIC_OBJECT jne int3D_drawobject_form call int3D_drawobject jmp int3D_drawobject_drawn int3D_drawobject_form: call int3D_drawform int3D_drawobject_drawn: pop ds jmp int3D_drawobject_draw int3D_drawobject_done: pop _3doms_sortptr dec _3doms_tdepth ret int3D_drawobject_badform: mov ax, ERROR_BADFORM jmp int3D_drawfail int3D_drawobject_badobject: mov ax, ERROR_BADOBJECT jmp int3D_drawfail int3D_drawobject_badlist: mov ax, ERROR_BADLIST jmp int3D_drawfail ;-------- Routine: Draw form. ; Works out where every point in the form goes on the screen, then puts the ; shapes in a list by how far their centers are from the camera and draws ; them, starting with the furthest. ; Inputs: ; DS - Form. ; ES - Output segment. int3D_drawform: ; Every point, once. xor cx, cx mov cl, ds: [M_NUMPTS] mov ax, ds: [M_PTS] int3D_drawform_point: jcxz int3D_drawform_pointsdone mov dl, MAGIC_POINT call int3D_checkcell jc int3D_drawform_badpoint push ds mov ds, ax mov esi, ds: [P_X] mov edi, ds: [P_Y] mov ebp, ds: [P_Z] call int3D_transform call int3D_project mov ax, ds: [P_NEXT] pop ds dec cx jmp int3D_drawform_point int3D_drawform_pointsdone: or ax, ax jnz int3D_drawform_badlist ; The shapes go in the list by the distance of their centers. push _3doms_sortptr xor cx, cx mov cl, ds: [M_NUM] mov ax, ds: [M_PTR] int3D_drawform_shape: jcxz int3D_drawform_listed mov dl, MAGIC_SHAPE call int3D_checkcell jc int3D_drawform_badshape push ds mov ds, ax mov bx, ax mov ax, ds: [S_CENTRE] call int3D_measure mov ax, ds: [S_NEXT] pop ds dec cx jmp int3D_drawform_shape int3D_drawform_listed: or ax, ax jnz int3D_drawform_badlist ; Draw them, furthest first. int3D_drawform_draw: mov bp, sp mov si, ss: [bp] call int3D_takefurthest or ax, ax jz int3D_drawform_done push ds mov ds, ax call int3D_drawshape pop ds jmp int3D_drawform_draw int3D_drawform_done: pop _3doms_sortptr ret int3D_drawform_badpoint: mov ax, ERROR_BADPOINT jmp int3D_drawfail int3D_drawform_badshape: mov ax, ERROR_BADSHAPE jmp int3D_drawfail int3D_drawform_badlist: mov ax, ERROR_BADLIST jmp int3D_drawfail ;-------- Routine: Draw shape. ; Draws every face in a shape. A shape is convex, so leaving out the faces ; that face away is enough, and the rest can be drawn in any order. ; Inputs: ; DS - Shape. ; ES - Output segment. int3D_drawshape: xor cx, cx mov cl, ds: [S_NUM] mov ax, ds: [S_PTR] int3D_drawshape_face: jcxz int3D_drawshape_done mov dl, MAGIC_FACE call int3D_checkcell jc int3D_drawshape_badface push ds mov ds, ax call int3D_drawface mov ax, ds: [F_NEXT] pop ds dec cx jmp int3D_drawshape_face int3D_drawshape_done: or ax, ax jnz int3D_drawshape_badlist ret int3D_drawshape_badface: mov ax, ERROR_BADFACE jmp int3D_drawfail int3D_drawshape_badlist: mov ax, ERROR_BADLIST jmp int3D_drawfail ;-------- Routine: Draw face. ; Inputs: ; DS - Face. ; ES - Output segment. int3D_drawface: push cx ; Get where its corners went on the screen. If any of them couldn't be ; put on the screen, the face is left out. mov ax, ds: [F_P1] mov di, offset _tri_x1 call int3D_getcorner jc int3D_drawface_nodraw mov ax, ds: [F_P2] mov di, offset _tri_x2 call int3D_getcorner jc int3D_drawface_nodraw mov ax, ds: [F_P3] mov di, offset _tri_x3 call int3D_getcorner jc int3D_drawface_nodraw ; Calculate cross vector product - if it isn't -ive then the face is ; facing away, or edge on - don't draw it. movsx eax, _tri_x2 movsx ecx, _tri_x1 sub eax, ecx movsx ecx, _tri_y3 movsx edx, _tri_y2 sub ecx, edx imul eax, ecx movsx ecx, _tri_x3 movsx edx, _tri_x2 sub ecx, edx movsx edx, _tri_y2 movsx esi, _tri_y1 sub edx, esi imul ecx, edx sub eax, ecx jge int3D_drawface_nodraw ; Draw face. mov al, ds: [F_COL] call int3D_tri int3D_drawface_nodraw: pop cx ret ;-------- Routine: Get corner. ; Inputs: ; AX - Point. ; CS:DI - Where to put its x and y on the screen. ; Returns: ; Carry set if it couldn't be put on the screen. int3D_getcorner: mov dl, MAGIC_POINT call int3D_checkcell jnc int3D_getcorner_ok mov ax, ERROR_BADPOINT jmp int3D_drawfail int3D_getcorner_ok: push ds mov ds, ax cmp byte ptr ds: [P_OFF], 0 jne int3D_getcorner_off mov ax, ds: [P_SX] mov cs: [di], ax mov ax, ds: [P_SY] mov cs: [di+2], ax pop ds clc ret int3D_getcorner_off: pop ds stc ret ;-------- Routine: Measure. ; Works out how far a center is from the camera and adds it to the draw list. ; Inputs: ; AX - Center point. ; BX - What to draw. int3D_measure: mov dl, MAGIC_POINT call int3D_checkcell jnc int3D_measure_ok mov ax, ERROR_BADPOINT jmp int3D_drawfail int3D_measure_ok: push ds mov ds, ax mov esi, ds: [P_X] mov edi, ds: [P_Y] mov ebp, ds: [P_Z] pop ds call int3D_transform call int3D_addentry ret ;-------- Routine: Add entry. ; Adds something to the end of the draw list, with the distance squared of ; a point from the camera. ; Inputs: ; ESI, EDI, EBP - The point, after transforming. ; BX - What to draw. int3D_addentry: push eax push ecx push edx push di ; Distance squared = x*x + y*y + z*z, into ECX:ESI. mov eax, esi imul eax mov esi, eax mov ecx, edx mov eax, edi imul eax add esi, eax adc ecx, edx mov eax, ebp imul eax add esi, eax adc ecx, edx mov di, _3doms_sortptr cmp di, offset _3doms_sortend - SORT_SIZE jbe int3D_addentry_room mov ax, ERROR_TOOMANY jmp int3D_drawfail int3D_addentry_room: mov cs: [di+SORT_LO], esi mov cs: [di+SORT_HI], ecx mov cs: [di+SORT_SEG], bx add di, SORT_SIZE mov _3doms_sortptr, di pop di pop edx pop ecx pop eax ret ;-------- Routine: Take furthest. ; Finds the furthest entry from SI to the end of the draw list that hasn't ; been drawn yet, and marks it drawn. ; Inputs: ; SI - Start of list. ; Returns: ; AX - What to draw, or 0 if everything has been drawn. int3D_takefurthest: push ebx push ecx push edx push si push di xor ebx, ebx xor ecx, ecx xor di, di int3D_takefurthest_loop: cmp si, _3doms_sortptr jae int3D_takefurthest_end cmp word ptr cs: [si+SORT_SEG], 0 je int3D_takefurthest_next or di, di jz int3D_takefurthest_take mov edx, cs: [si+SORT_HI] cmp edx, ecx ja int3D_takefurthest_take jb int3D_takefurthest_next mov edx, cs: [si+SORT_LO] cmp edx, ebx jbe int3D_takefurthest_next int3D_takefurthest_take: mov ebx, cs: [si+SORT_LO] mov ecx, cs: [si+SORT_HI] mov di, si int3D_takefurthest_next: add si, SORT_SIZE jmp int3D_takefurthest_loop int3D_takefurthest_end: xor ax, ax or di, di jz int3D_takefurthest_none mov ax, cs: [di+SORT_SEG] mov word ptr cs: [di+SORT_SEG], 0 int3D_takefurthest_none: pop di pop si pop edx pop ecx pop ebx ret ;-------- Routine: Push transform. ; Puts the position and angles of an object, or the universe, on the ; transformation stack. ; Inputs: ; DS - Object or universe. int3D_pushtransform: push bx push cx push si push di push es mov bx, _3doms_tdepth cmp bx, TSTACK_LEVELS jb int3D_pushtransform_room mov ax, ERROR_TOODEEP jmp int3D_drawfail int3D_pushtransform_room: shl bx, 4 push cs pop es mov si, O_X lea di, _3doms_tstack[bx] mov cx, 8 rep movsw inc _3doms_tdepth pop es pop di pop si pop cx pop bx ret ;-------- Routine: Transform. ; Takes a point through every level on the transformation stack, from the ; top down: at each level it's rotated by the angles, in the rotation order, ; and then moved by the position. ; Inputs: ; ESI, EDI, EBP - x, y and z. ; Returns: ; ESI, EDI, EBP - x, y and z as the camera sees them. int3D_transform: push bx push cx mov cx, _3doms_tdepth mov bx, cx shl bx, 4 jcxz int3D_transform_end int3D_transform_level: sub bx, 16 call int3D_rotate add esi, dword ptr _3doms_tstack[bx] add edi, dword ptr _3doms_tstack[bx+4] add ebp, dword ptr _3doms_tstack[bx+8] loop int3D_transform_level int3D_transform_end: pop cx pop bx ret ;-------- Rotate ; Inputs: ; _3doms_tstack[BX] - Level, with the angles and rotation order at 12. ; ESI, EDI, EBP - x, y and z to be rotated. int3D_rotate: test byte ptr _3doms_tstack[bx+15], 00000001b jnz int3D_rotate_y__ test byte ptr _3doms_tstack[bx+15], 00000010b jnz int3D_rotate_z__ int3D_rotate_x__: mov al, byte ptr _3doms_tstack[bx+12] call int3D_rotate_x test byte ptr _3doms_tstack[bx+15], 00000100b jz int3D_rotate__yz int3D_rotate__zy: mov al, byte ptr _3doms_tstack[bx+14] call int3D_rotate_z mov al, byte ptr _3doms_tstack[bx+13] call int3D_rotate_y ret int3D_rotate__yz: mov al, byte ptr _3doms_tstack[bx+13] call int3D_rotate_y mov al, byte ptr _3doms_tstack[bx+14] call int3D_rotate_z ret int3D_rotate_y__: mov al, byte ptr _3doms_tstack[bx+13] call int3D_rotate_y test byte ptr _3doms_tstack[bx+15], 00000100b jz int3D_rotate__xz int3D_rotate__zx: mov al, byte ptr _3doms_tstack[bx+14] call int3D_rotate_z mov al, byte ptr _3doms_tstack[bx+12] call int3D_rotate_x ret int3D_rotate__xz: mov al, byte ptr _3doms_tstack[bx+12] call int3D_rotate_x mov al, byte ptr _3doms_tstack[bx+14] call int3D_rotate_z ret int3D_rotate_z__: mov al, byte ptr _3doms_tstack[bx+14] call int3D_rotate_z test byte ptr _3doms_tstack[bx+15], 00000100b jz int3D_rotate__xy int3D_rotate__yx: mov al, byte ptr _3doms_tstack[bx+13] call int3D_rotate_y mov al, byte ptr _3doms_tstack[bx+12] call int3D_rotate_x ret int3D_rotate__xy: mov al, byte ptr _3doms_tstack[bx+12] call int3D_rotate_x mov al, byte ptr _3doms_tstack[bx+13] call int3D_rotate_y ret ;-------- Rotate_x ; Inputs: ; AL - Angle. ; EDI, EBP - y and z, to be rotated about the x axis. int3D_rotate_x: or al, al jz int3D_rotate_x_end push bx push ecx push edx ; Calculate offset in sine table. xor bx, bx mov bl, al shl bx, 2 ; NY = Y*cos(AL)-Z*sin(AL) mov ecx, edi mov eax, _3doms_cos[bx] imul ecx mov al, dl ror eax, 8 mov edi, eax mov eax, _3doms_sin[bx] imul ebp mov al, dl ror eax, 8 sub edi, eax ; NZ = Y*sin(AL)+Z*cos(AL) mov eax, _3doms_cos[bx] imul ebp mov al, dl ror eax, 8 mov ebp, eax mov eax, _3doms_sin[bx] imul ecx mov al, dl ror eax, 8 add ebp, eax pop edx pop ecx pop bx int3D_rotate_x_end: ret ;-------- Rotate_y ; Inputs: ; AL - Angle. ; ESI, EBP - x and z, to be rotated about the y axis. int3D_rotate_y: or al, al jz int3D_rotate_y_end push bx push ecx push edx ; Calculate offset in sine table. xor bx, bx mov bl, al shl bx, 2 ; NX = X*cos(AL)-Z*sin(AL) mov ecx, esi mov eax, _3doms_cos[bx] imul ecx mov al, dl ror eax, 8 mov esi, eax mov eax, _3doms_sin[bx] imul ebp mov al, dl ror eax, 8 sub esi, eax ; NZ = X*sin(AL)+Z*cos(AL) mov eax, _3doms_cos[bx] imul ebp mov al, dl ror eax, 8 mov ebp, eax mov eax, _3doms_sin[bx] imul ecx mov al, dl ror eax, 8 add ebp, eax pop edx pop ecx pop bx int3D_rotate_y_end: ret ;-------- Rotate_z ; Inputs: ; AL - Angle. ; ESI, EDI - x and y, to be rotated about the z axis. int3D_rotate_z: or al, al jz int3D_rotate_z_end push bx push ecx push edx ; Calculate offset in sine table. xor bx, bx mov bl, al shl bx, 2 ; NX = X*cos(AL)-Y*sin(AL) mov ecx, esi mov eax, _3doms_cos[bx] imul ecx mov al, dl ror eax, 8 mov esi, eax mov eax, _3doms_sin[bx] imul edi mov al, dl ror eax, 8 sub esi, eax ; NY = X*sin(AL)+Y*cos(AL) mov eax, _3doms_cos[bx] imul edi mov al, dl ror eax, 8 mov edi, eax mov eax, _3doms_sin[bx] imul ecx mov al, dl ror eax, 8 add edi, eax pop edx pop ecx pop bx int3D_rotate_z_end: ret ;-------- Project ; Works out where a point goes on the screen. ; Inputs: ; ESI, EDI, EBP - x, y and z as the camera sees them. ; DS - Point, to put the screen co-ordinates in. int3D_project: push eax push ecx push edx ; It can't be put on the screen until it passes these tests. mov byte ptr ds: [P_OFF], 1 ; It has to be at least NEAR_Z in front of the camera. cmp ebp, NEAR_Z jl int3D_project_end ; And no more than 16 times as far to the side, or up or down, as it is ; in front, so the divides can't overflow. mov eax, esi cdq xor eax, edx sub eax, edx shr eax, 4 cmp eax, ebp ja int3D_project_end mov eax, edi cdq xor eax, edx sub eax, edx shr eax, 4 cmp eax, ebp ja int3D_project_end ; SX = (scr2eye*X) / Z, from the center of the screen. mov ecx, SCR2EYE mov eax, esi imul ecx idiv ebp sar eax, 8 add ax, VIEW_CX mov ds: [P_SX], ax ; SY = (scr2eye*Y) / Z. mov eax, edi imul ecx idiv ebp sar eax, 8 add ax, VIEW_CY mov ds: [P_SY], ax mov byte ptr ds: [P_OFF], 0 int3D_project_end: pop edx pop ecx pop eax ret ;----------------------------- Graphics Routines ----------------------------; ;-------- Triangle. ; Fills a triangle, leaving out any of it that's off the screen. ; Inputs: ; AL - Colour. ; _tri_x1 to _tri_y3 - Corners. ; ES - Output segment. int3D_tri: push eax push ebx push ecx push edx push esi push di mov _tri_col, al ; Sort the co-ordinates into order - lowest y-value first. ; Swap (X1,Y1) and (X2,Y2) if necessary. mov ax, _tri_y1 cmp ax, _tri_y2 jle int3D_tri_y1ok xchg ax, _tri_y2 mov _tri_y1, ax mov ax, _tri_x1 xchg ax, _tri_x2 mov _tri_x1, ax int3D_tri_y1ok: ; Swap (X2,Y2) and (X3,Y3) if necessary. mov ax, _tri_y2 cmp ax, _tri_y3 jle int3D_tri_y2ok xchg ax, _tri_y3 mov _tri_y2, ax mov ax, _tri_x2 xchg ax, _tri_x3 mov _tri_x2, ax int3D_tri_y2ok: ; Swap (X1,Y1) and (X2,Y2) again if necessary. mov ax, _tri_y1 cmp ax, _tri_y2 jle int3D_tri_y1ok2 xchg ax, _tri_y2 mov _tri_y1, ax mov ax, _tri_x1 xchg ax, _tri_x2 mov _tri_x1, ax int3D_tri_y1ok2: ; Only the rows that are on the screen are drawn. mov ax, _tri_y1 cmp ax, 0 jge int3D_tri_topok xor ax, ax int3D_tri_topok: mov _tri_y, ax mov ax, _tri_y3 cmp ax, 199 jle int3D_tri_bottomok mov ax, 199 int3D_tri_bottomok: mov _tri_yend, ax int3D_tri_row: mov ax, _tri_y cmp ax, _tri_yend jg int3D_tri_end ; If all three corners are on one row, the row goes from the leftmost ; to the rightmost. mov ax, _tri_y3 cmp ax, _tri_y1 jne int3D_tri_notflat movsx ebx, _tri_x1 mov edx, ebx movsx eax, _tri_x2 cmp eax, ebx jge int3D_tri_flat1 mov ebx, eax int3D_tri_flat1: cmp eax, edx jle int3D_tri_flat2 mov edx, eax int3D_tri_flat2: movsx eax, _tri_x3 cmp eax, ebx jge int3D_tri_flat3 mov ebx, eax int3D_tri_flat3: cmp eax, edx jle int3D_tri_span mov edx, eax jmp int3D_tri_span int3D_tri_notflat: ; Where the long edge, from the first corner to the last, crosses this ; row: X1 + (X3-X1)*(Y-Y1)/(Y3-Y1). movsx eax, _tri_x3 movsx ecx, _tri_x1 sub eax, ecx movsx ecx, _tri_y movsx esi, _tri_y1 sub ecx, esi imul ecx movsx ecx, _tri_y3 sub ecx, esi idiv ecx movsx ecx, _tri_x1 add eax, ecx mov ebx, eax ; Where the short edges cross it: the first corner to the middle one ; above the middle corner, and the middle one to the last from there on. mov ax, _tri_y cmp ax, _tri_y2 jge int3D_tri_lower movsx eax, _tri_x2 movsx ecx, _tri_x1 sub eax, ecx movsx ecx, _tri_y movsx esi, _tri_y1 sub ecx, esi imul ecx movsx ecx, _tri_y2 sub ecx, esi idiv ecx movsx ecx, _tri_x1 add eax, ecx mov edx, eax jmp int3D_tri_span int3D_tri_lower: mov ax, _tri_y3 cmp ax, _tri_y2 jne int3D_tri_lowerslope movsx edx, _tri_x2 jmp int3D_tri_span int3D_tri_lowerslope: movsx eax, _tri_x3 movsx ecx, _tri_x2 sub eax, ecx movsx ecx, _tri_y movsx esi, _tri_y2 sub ecx, esi imul ecx movsx ecx, _tri_y3 sub ecx, esi idiv ecx movsx ecx, _tri_x2 add eax, ecx mov edx, eax int3D_tri_span: ; EBX and EDX are the ends of the row. Put them in order, and clip them ; to the screen. cmp ebx, edx jle int3D_tri_ordered xchg ebx, edx int3D_tri_ordered: cmp edx, 0 jl int3D_tri_next cmp ebx, 319 jg int3D_tri_next cmp ebx, 0 jge int3D_tri_leftok xor ebx, ebx int3D_tri_leftok: cmp edx, 319 jle int3D_tri_rightok mov edx, 319 int3D_tri_rightok: ; Horiz line. movzx eax, _tri_y imul eax, eax, 320 add eax, ebx mov di, ax mov ecx, edx sub ecx, ebx inc ecx mov al, _tri_col call int3D_hline int3D_tri_next: inc _tri_y jmp int3D_tri_row int3D_tri_end: pop di pop esi pop edx pop ecx pop ebx pop eax ret ;-------- Horizontal line. ; Inputs: ; AL - Colour. ; CX - Number of pixels. ; ES:DI - Where it starts. int3D_hline: jcxz int3D_hline_end ; rep stosb ; Slow but safe while testing rendering functions. ; ret shr cx, 1 jnc int3D_hline_nobyte stosb int3D_hline_nobyte: jcxz int3D_hline_end mov ah, al shr cx, 1 jnc int3D_hline_noword stosw int3D_hline_noword: jcxz int3D_hline_end push ax rol eax, 16 pop ax rep stosd int3D_hline_end: ret ;----------------------------- Work Areas -----------------------------------; ; Transformation stack: 16 bytes a level, the same as an object's ; position and angles. _3doms_tstack db TSTACK_LEVELS*16 dup (0) ; Draw list. _3doms_sorttab db SORT_ENTRIES*SORT_SIZE dup (0) _3doms_sortend label byte ; Stack used while rendering. _3doms_stack db STACK_SIZE dup (0) _3doms_stacktop label word int3D_last label byte ;-------- End of code.