FILLED VECTORS (Part 1)


HAVE YOU EVER DREAM TO WRITE A 3D ROUTINE SUCH BEST DEMOS HAS? WITH A FEW ARTICLES I'LL LEARN IT ALL!

Now we'll start a series of articles, in asm language for the Amiga, dedicated to 3D routines creation with the following features:

- DOUBLE BUFFERING
- ROTATION ON 3 AXIS
- ZOOM
- SORTING ( Z-VALUES & PRE-DETERMINED PRIORITY )
- LINES
- NORMAL SURFACES
- TRANSPARENT SURFACES!!!
- HIDDEN FACES
- DITHERING
- CLIPPING
ONLY I'D HAVE A LOT OF REQUEST, I'LL WRITE ARTICLES ABOUT:
- TEXTURE MAPPING
- GOURAUD SHADING
- PHONG SHADING
- MOVING LIGHT SOURCE ON 3 AXIS

Send e-mail specifying your prefs (also completely different subjects, ex: compression and decompression algos)


GLOSSARY

DOUBLE BUFFERING

It is a way of obtaining smoother anims, you need two or more memory blocks: while one block is showed, the other is used to calculate next frame. When rendering operation is done, the two buffers are swapped.

ROTATION ON 3 AXIS

Rotation can be done on one, two or three axis (X,Y,Z). Routine we'll consider will rotate points on all three axis, using a pre-calculated cos/sin table to speed up math operations.

ZOOM

Zoom is used to enlarge/reduce object's dimensions, such as this object gets get closer or further from us.

SORTING

One of the bigger problems is certainly the one related to sorting routine. You have to give a drawing priority to faces building our object. This priority is needed in some cases: let's see when.

Here there is a pratical example: a 3D cube is made up of 6 faces (in our example number of points is unneeded); in any way you turn it, you'll see a maximum of three faces. Remaining 3 faces are not visible by the observer (HIDDEN FACES!), so it is perfectly unuseful to draw them. While things seem to be easy for this kind of faces, everything gets harder for visible ones.

In this example, visible faces will never overlap, so it is perfectly unuseful give them a drawing priority (it is not important the order they are drawn).

In a more complex example, you should give a priority to all faces, to avoid that hidden parts are drawn before the visible ones.

In this case, a Z-sort is necessary to balance the drawing. To set down it in words, after you have rotated and zoomed the object, you have to get only Z coords and create table a median distance of every face, once calculated you will sort faces up: starting from the further part of the object to the closest part.

NORMAL SURFACES

They are simple filled and faces overlapped to the surrounding ones. TRANSPARENT SURFACES

Transparent surfaces have to be calculated but not showed. You have to be able to see through them: in this case inner faces have to be shown Returning to the cube: 3 visible faces and 3 not; for transparent faces, you'll need to show also hidden faces.

HIDDEN FACES

By a simply algo you can understand whether a face is visible or not (in this case we will not draw it, for gaining maximum speed).

DITHERING

This procedure is used to add grid mask of points, to have intermediate colours and shadow effects.

CLIPPING

It is used when a face exists from the visible zone of the screen. With this routine it is possible to draw just visible parts of faces.

TEXTURE MAPPING

This is a technique to map bitmaps to polygons. It is often used in DOOM style-game, this pic have to be calculated and rotated.

GOURAUD SHADING

Math method to choose a face colour from its and light source rotation degree . Light is supposed to be constant from the polygon.

PHONG SHADING

Here you have to calculate face colour point by point, reducing faces scaling. PHONG is better than GOURAUD, but requires more operations and more powerful machines.

MOVING LIGHT SOURCE ON 3 AXIS

With both GOURAUD and PHONG SHADING you need coords (x,y,z) of a light point to allow computation of lighting colours, by moving this light source, all object illumination should change.


Detailed Description

In this article we'll consider only first 3 points, others will be discussed in next months.

DOUBLE BUFFERING

In this example, I have used 6 memory blocks, to obtain an effect similar to Motion Blur).

In "calcolascreen" i do compute the next frame while I show the others in sequence, so we'll have:

screen1 = last computed frame

screen2 = 1 frame before the last

screen3 = 2 frame before the last

screen4 = 3 frame before the last

screen5 = 4 frame before the last

Once cleared showing sequence, I use the blitter to clear bitplane contents (CalcolaScreen).

SwapScreen:

** Change Screen Pointer for next frame

	move.l	CalcolaScreen,d0
	move.l	Screen1,CalcolaScreen
	move.l	Screen2,Screen1
	move.l	Screen3,Screen2
	move.l	Screen4,Screen3
	move.l	Screen5,Screen4
	move.l	d0,Screen5

** Copper showing of the next frame

	lea	planeaddress,a0
	move.l	Screen5,d0
	move.w	d0,6(a0)
	swap	d0
	move.w	d0,2(a0)
	swap	d0
	move.l	Screen4,d0
	move.w	d0,14(a0)
	swap	d0
	move.w	d0,10(a0)
	swap	d0
	move.l	Screen3,d0
	move.w	d0,22(a0)
	swap	d0
	move.w	d0,18(a0)
	swap	d0
	move.l	Screen2,d0
	move.w	d0,30(a0)
	swap	d0
	move.w	d0,26(a0)
	move.l	Screen1,d0
	move.w	d0,38(a0)
	swap	d0
	move.w	d0,34(a0)

** Blitter to clear CalcolaScreen

	lea	$dff000,a6
	move.w	#$8400,$96(a6)
WaitBlitter:
	btst	#6,2(a6)
	bne.s	WaitBlitter
	move.w	#$0400,$96(a6)
	move.l	#$01000000,$40(a6)
	moveq	#-1,d0
	move.l	d0,$44(a6)
	move.l	CalcolaScreen,$54(a6)
	move.w	#0,$66(a6)
	move.w	#$4014,$58(a6)
	rts

ROTATION ON 3 AXIS

This routine gets three coords axis X,Y and Z from WPoints var and rotate them on all axis. Rotation degrees on X are taken from Xangle, on Y from Yangle and Z from Zangle.

SinCos routinge gets from SinTab pre-computed sin and cos values of rotation angle and save them in Xsin, Xcos, Ysin, Ycos, Zsin, Zcos vars.

**********************************
** Rotate around axis         **
**********************************
Rotate:
	move.w	#$fff,d4	; $fff = maximum amount of values in SINTAB

	move.w	Zangle,d0	; Z rotation angle
	and.w	d4,d0		; and with  $fff
	move.w	d0,Zangle
	jsr	SinCos		; get Sin and Cos
	move.w	d1,Zsin		; Sin(Z)
	move.w	d2,Zcos		; Cos(Z)

	move.w	Yangle,d0	; See above, now with Yangle
	and.w	d4,d0
	move.w	d0,Yangle
	jsr	SinCos
	move.w	d1,Ysin
	move.w	d2,Ycos

	move.w	Xangle,d0	; See above with Xangle
	and.w	d4,d0
	move.w	d0,Xangle
	jsr	SinCos
	move.w	d1,Xsin
	move.w	d2,Xcos

	lea	Wpoints,a0	; original X,Y,Z coords
	lea	IntX,a3		; storing of X coords (computed)
	lea	IntY,a4		; storing of Y coords (computed)
	lea	IntZ,a5		; storing of Z coords (computed)
	move.w	#npoints-1,d0	; number of points -1 (so 1858-1)
Zrotate:
	move.w	Zsin(pc),d1	; sin Z
	move.w	Zcos(pc),d2	; cos Z
	move.w	(a0)+,d6	; X
	move.w	(a0)+,d7	; Y
	muls	d6,d2		; X * cos Z
	muls	d7,d1		; Y * sin Z
	sub.l	d1,d2		; (X * cos Z)-(Y * sin Z)
	add.l	d2,d2		; ((X * cos Z)-(Y * sin Z)) * 2
	swap	d2		; (((X * cos Z)-(Y * sin Z)) * 2) / 32768
	move.w	d2,d5		; NEW X !!!
	move.w	Zsin(pc),d1	; sin Z
	move.w	Zcos(pc),d2	; cos Z
	muls	d6,d1		; X * sin Z
	muls	d7,d2		; Y * cos Z
	add.l	d1,d2		; (Y * cos Z) + (X * sin Z)
	add.l	d2,d2		; ((Y * cos Z) + (X * sin Z)) * 2
	swap	d2		; (((Y * cos Z) + (X * sin Z)) * 2) / 32768
	move.w	d2,d6		; NEW Y
Yrotate:
	move.w	Ysin(pc),d1
	move.w	Ycos(pc),d2
	move.w	(a0)+,d3	; Z
	muls	d3,d2
	muls	d5,d1
	sub.l	d1,d2
	add.l	d2,d2
	swap	d2
	move.w	d2,d7		; NEW Z
	move.w	Ysin(pc),d1
	move.w	Ycos(pc),d2
	muls	d3,d1
	muls	d5,d2
	add.l	d1,d2
	add.l	d2,d2
	swap	d2
	move.w	d2,(a3)+	; FINAL X
Xrotate:
	move.w	Xsin(pc),d1
	move.w	Xcos(pc),d2
	muls	d6,d2
	muls	d7,d1
	sub.l	d1,d2
	add.l	d2,d2
	swap	d2
	move.w	d2,(a4)+	; FINAL Y
	move.w	Xsin(pc),d1
	move.w	Xcos(pc),d2
	muls	d6,d1
	muls	d7,d2
	add.l	d1,d2
	add.l	d2,d2
	swap	d2
	move.w	d2,(a5)+	; FINAL Z
	dbf	d0,Zrotate
	rts
***************************
** Compute sin and cos **
***************************
Sincos:
	lea	SinTab,a1	; Precomputed Sin and cos
	move.w	d0,d2
	add.w	d0,d0
	move.w	(a1,d0.w),d1	; New SIN
	move.w	#$c00,d3	; 4/3 of 360_
	cmp.w	d3,d2
	blt.s	Plus9
	sub.w	d3,d2
	add.w	d2,d2
	move.w	(a1,d2.w),d2	; New COS
	rts
Plus9:	add.w	#$400,d2		; 1/4 di 360 gradi
	add.w	d2,d2
	move.w	(a1,d2.w),d2	; New COS
	rts

ZOOM

ZOOM algo is used to enlarge or reduce objects dimensions.

For doing this, you'll need 3 coords for each point (X,Y,Z) and returned only 2 (X,Y)

A simply version of operations to handle 3D zoom should be:


X final = ( X * zoom ) / ( 1000 + Z )
Y final = ( Y * zoom ) / ( 1000 + Z )

where:

X , Y , Z = coords of a point

zoom = zoom value from 0 to 1000

1000 = maximum zoom value

Zoom:
	lea	IntX,a1			; coord X
	lea	IntY,a2			; coord Y
	lea	IntZ,a3			; coord Z

	move.l	CalcolaScreen,a4	; screen address
	lea	Tabella,a5		; Y val mul by 40

	moveq	#0,d0
	move.w	#npoints-1,d0
	move.w	Dist(pc),d3		; zoom value
	move.w	#1000,d4		; maximum zoom value

WaitBlit:
	btst	#6,$dff002
	bne.s	WaitBlit
CalcAll:
	move.w	(a1)+,d6		; X
	muls	d3,d6			; X * zoom
	move.w	(a3)+,d2		; Z
	move.w	d2,d7			; d7 = d2 = Z
	add.w	d4,d2			; 1000 + Z
	divs.w	d2,d6			; NEW X
	add.w	Xorg(pc),d6		; X = X + orgX

	move.w	(a2)+,d1		; Y
	neg.w	d1			; d1 = neg d1
	muls	d3,d1			; Y * zoom
	add.w	d4,d7			; 1000 + Z
	divs.w	d7,d1			; NEW Y
	add.w	YOrg(pc),d1		; Y = Y + orgY

; X = d6
; Y = d1
	move.w	d6,d5			; d6 = d5 = X
	lsr.w	#3,d5			; d5 = d5 / 8
	add.w	d1,d1			; d1 = d1 * 2
	add.w	(a5,d1.w),d5		; d5 = d5 * 40 (byte for row)
	not.b	d6			; d6 = not d6

	bset	d6,(a4,d5.w)		; set pixel

	dbf	d0,CalcAll
	rts

In this archive two demos are includes:

3D-points : example and source of this article

3D-demo : 3D demo you'll get the source soon...

Routines are very old (1990) and tested only on Amiga 500 and 2000, so not optimized for faster processors.

My last routine (68020+) can handle 180 points and 170 faces with 256 cols, 25fps, but being created for a commercial game, I'll not show the source

SORRY....


Main Page


    Written By: Alfredo Ornaghi      e-mail: ted@intercom.it
                ITALY                   tel: