3DOMS.INC
Assembly source for MS-DOS 3D engine, 3305 lines. Raw file
; LKS 3-Dimensional Object Management System (3DOMS) version 0.15
; 3DOMS.INC - <c> 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.