Programming - 3D on the Spectrum: rotation of a wire object (without cutting off lines that go off the screen).

Spectrum Expert #01
SPECTRUM + 3D #1              
                              
(c)1997 Dark/X-trade and -STS-/VolgaSoft
────────────── ───────────────────────────
                                                            
 With this series of articles, the authors set
I have the task of showing ways to re-
implementation on the "fast" 3D graphics spec. 
 In principle, these articles are aimed at
a beginner coder, since a coder with
experience and probably knows all this.
Although, perhaps he can find for se-
something interesting (at least me-
re in algorithms). Our goal is not so much
give ready-made recipes and templates, how much
push the reader to further self-
meaningful study of the subject.         
 We haven’t explained anything particularly complicated here.
We won’t accept it, but nevertheless, we hope
We hope that your Gray Matter will not come out
from the banks before you reach
end of the text.                       
 We'll start by solving a simple problem:
rotation of a wire object (without cutting off)
for lines that go off the screen). For per-
howl article is quite enough.           
                                        
 All we need is for the object to spin
wasted, is to ask it and code it,
a shadowy computer, and a quick procedure
ru lines.───────────── ────────────────────────────
                                                            
 The lines of code that you will see in
text, written in order to simply
explain the essence of things, and WERE NOT subjected to
without any capital optimization.
The working code can be found on the disk at
file 3DROTATE. There are 2 more files -
ALGORITMand DIVTABS, details about which
See more at the end of the article, in the section
"Algorithms". All sources are written with
using specific syntax
"STORM TURBO ASSEMBLER"  (which, according to
opportunities, you can find here or
in ZX-Format #7). Something about syntax
Storm's you can also find the section
"Algorithms". The files are given in text form
where you could watch them
outside the assembler.  Examples in the text, according to
possibilities are given in classical syntax
sis. To upload them to STORM you should
use the text import function
new file-BREAK+T.                   
                                        
───────────── ────────────────────────────
                                                            
 So, let's define an object.  Let it be
simple pyramid.                       
 We will describe it as a set of edges
(edge) each of which is stretched between
between two vertices (vertex).  Vertices are given
in the so-called object space, i.e. from-
relative to the object's center of rotation.     
                                        
The array looks something like this:           
                                        
PIRAMID DW edge                         
vertex ;DB hp, yn, zn                  
                                        
 DB 0,40,-20 ;base      
        DB 60,-20,-20 ;//             
        DB -60,-20,-20 ;/              
        DB 0,10,40 ;peak            
        DB #80 ;end                   
                                        
edge ;DB from_point_M, to_point_N       
                                        
 DB 0,1 ;base connection   
        D.B.1.2 ;//                     
        DB 2.0 ;/                      
        DB 0.3 ;conn. peak with base
        DB 1.3 ;//                     
        DB 2.3 ;/                      
        DB -1 ;end                   
                                        
                                        
The object seems to have been set. Now let's take care of you...
numerator.                             
                                        
 To rotate an object, it is enough to check
remove its tops.  Rotation occurs
relative to zero in the same object -
nom space according to the following formulas: 
                                        
Z axis work                           
 X' =x*cos AZ + y*sin AZ               
  Y' =y*cos AZ - x*sin AZ               
                                        
Y axis work                           
 Z' =z*cos AY + x'*sin AY              
  X''=x'*cos AY - z*sin AY              
                                        
 X-axis workhorse                           
 Y''=y'*cos AX + z'*sin AX             
  Z''=z'*cos AX - y'*sin AX             
Naturally, we will calculate the data
formulas in real time and without any help
there are giant tables like A=B*SIN C,
for all possible values of B and C, so
how, naturally, this is only suitable for
intros or small demos.               
                                        
First we write a procedure like:           
                                        
Rotate (a,b,angle)                      
 {                                      
   at=a*cos+b*sin                       
   b=b*cos-а*sin                        
   a=at                                 
 }                                      
                                        
Let's say the input and output parameters are
it will be 8-bit:                     
                                        
IN :D,E=Y,X (SGN) C=ANGLE               
OUT:D,E=Y,X (SGN)                       
;this procedure is provided simply for
 illustrations.                           
;since 3DROTATE uses more
 fast.                               
                                        
ROTATE LD Н,COSTB[                     
        LD L,C                          
        LD C,(HL)                       
        LD A,L:SUB #40:LDL,A           
        LD B,(HL)                       
;B=sin C=cos                            
        LD L,C:LD H,E                   
        CALL MULS ;Н=Н*L (with signs)   
        LD A,H:EXA                      
;A'=X*COS                               
        LD L,B:LD H,D                   
        CALL MULS                       
        EXA:ADD A,H:LD LX,A             
;LX=X*COS+Y*SIN                         
        LD L,C:LD H,D                   
        CALL MULS                       
        LD A,H:EXA                      
;A'=Y*COS                               
 LD L,B:LD H,E                   
        CALL MULS                       
        EXA:ADD A,H:LD D,A              
;D=Y*COS-X*SIN                          
        LD E,LX                         
;E=X*COS+Y*SIN                          
        RET                             
                                        
                                        
Let's write the final procedure:              
                                        
Rot3D (x,y,z,rotX,rotY,rotZ)            
 {                                      
  Rotate (x,y,rotZ)                     
  Rotate (z,x,rotY)                     
  Rotate (y,z,rotX)                     
 }                                      
IN :D,E,C=Y,X,Z                         
OUT:D,E,C=Y,X,Z                         
                                        
ROT3D PUSH SUN                         
 LD BC, (ROTZ)                    
;rotatable x,y at an angle rotZ               
        CALL ROTATE;XY                  
        ROR VS                          
;replaceable t=y; y=x; x=z                 
        LD B,D:LD D,E:LD E,C            
;rotating z,x at an angle rotY               
        PUSH AIRCRAFT                         
        LD BC,(ROTY)                    
        CALL ROTATE                     
        ROR VS                          
;replaceable z=y; y=z; x=t                 
        LD C,D:LD D,E:LD E,C            
;rotatable y,z by angle rotX               
        PUSH AIRCRAFT                         
        LD BC, (ROTX)                    
        CALL ROTATE                     
        ROR VS                          
;replaceable t=y; Y=x; X=z; Z=t            
        LD A,D:LD D,E:LD E,C:LD C,A     
        RET                             
                                        
ROTX DB 0                            
ROTY DB 0                            
ROTZ DB 0                            
                                        
 So we have a rotated vertex.   
 To achieve our goal it is enough
translate it into screen space
(project onto screen). Let's assume
that our Z axis is directed away from the observer
la deep into the screen.                       
                                        
Most commonly used projections
two:                                    
                                        
- Parallel                          
                                        
   The Z value is simply not taken into account.    
 Xr=X''+Xoffset                        
  Yr=Y''+Yoffset                        
                                        
- Perspective                         
                                        
 The formula is usually used           
                                        
 Xr=((X''+Xcnt)*Scale/(Z+Zcnt))+Xoffset
  Yr=((Y''+Ycnt)*Scale/(Z+Zcnt))+Yoffset
                                        
 ! Z+Zcenter at a visible point cannot
 be <=0                               
Scale - scaling factor.    
 It affects the viewing angle and is selected
to taste.                               
                                        
X/Yoffset - screen center (#80,#60)      
X/Y/Z cnt (center) - the center of the object in 3D.
X/Yr (real) - coordinates of a point on the screen.
                                        
 The resulting values of Xr and Yr are added -
into the buffer of projected vertices.      
                                        
DOTS2D ds number of vertices*2          
                                        
Let's use perspective projection.  
The scaling factor (scale) is
is constant, so it is reasonable to eliminate
comes from multiplying by it, replacing the mind-
operation by shift or recoding according to
table.  We will take the coefficient equal to
256.                                    
                                        
 All these actions (starting with rotation and
so far) are fulfilled for all vertices,
of which we have 4.                           
                                        
The procedure does all this:               
                                        
PROJECT ;PROJECT ALL VERTEXES           
        LD HL,(OBJECT)                  
        INC HL:INC HL                   
;HL=vertices                             
        LD IX,DOTS2D                    
PROJ LD A,(HL):INC HLSR #80                          
;sign of the end                          
        RET Z                           
 LD E,A                          
        LD D,(HL):INC HL                
        LD C,(HL):INC HL                
        PUSH HL                         
        CALL ROT3D                      
        LD A,CENTERZ:ADD A,C:LD L,A     
        LD A,CENTERY:ADD A,D:LD N,A     
        PUSH HL                         
        LD A,CENTERX:ADD A,E:LD N,A     
        CALL FDIVB; Н.L(SGN)=Н(SGN)/L   
        LD A,OFFSETX:ADD A,L            
;L=Н.L*256 (Н=0)                        
        LD (IX),A:INC LX                
;unsubscribed Xr                            
        POP HL                          
        CALL FDIVB                      
;Y screen coordinate goes from top to bottom   
;and in bottom-up transformations        
        LD A,CENTERY:SUB L              
;does Yr go off the screen?           
;let's cut it off in advance so that later         
;take care                             
 SR #C0:JR C,$+4:LD A,#BF        
        LD (IX),A:INC LX                
;unsubscribed Yr                            
        POP HL                          
        JR PROJ                         
                                        
The DOTS2D array must be locatedto a beautiful address (low byte=0),   
                                        
There's just a little bit left - just to connect
points are lines, but this smallness is
takes up the lion's share of the time. For this for
We take each edge from the edge array (see.
above) numbers of two vertices - N1 and N2      
                                        
X1=DOTS2D[N1*2]                         
Y1=DOTS2D[N1*2+1]                       
                                        
X2=DOTS2D[N2*2]                         
Y2=DOTS2D[N2*2+1]                       
                                        
and draw a line between points (x1,y1) and
(x2,y2).                                
There is a RENDER procedure for this.
which displays the object on the screen (pre-
highly purified). In principle, this pro-
The procedure must take care to cut off the
tions going off the screen, but we
we won't do it. Therefore, limit in advance
chili ranges of numbers, those in the buffer.
                                        
;Display an object on the screen                 
RENDER ;RENDER OBJECT (EDGES)          
        LD HL,(OBJECT)                  
        LD E,(HL):INC HL                
        LD D,(HL)                       
;DE=ribsRENDR LD Н,DOTS2D[                    
        LD A,(DE):INC DE                
;from the top...                         
        ADD A,A:LD L,A                  
;sign of the end                          
        RET C                           
LD C,(HL):INC L                 
        LD B,(HL)                       
;got x1,y1                         
        LD A,(DE):INC DE                
;to the top...                          
        ADD A,A:LD L,A                  
        PUSH DE                         
        LD E,(HL):INC L                 
        LD D,(HL)                       
;got x2,y2                         
        CALL LINE                       
        POP DE                          
        JR RENDR                        
                                        
Drawing the line takes the lion's share
time.                                
 In Algorithms you will find the fastest
of all currently existing
on the Spectrum implementation of the line (moreover,
with the possibility of further increase
speed).                        
                                        
---------------------------------------
                                        
So the main loop should beapproximately
like this:                                  
                                        
 LD HL,PIRAMID                   
        LD (OBJECT),HL                  
MAIN CALL EXGVIEW;Exchange screens      
        CALL PROJECT;Projection           
        CALL RENDER ;Output to shadow    
                    ;screen              
        LD HL,ROTX ;Changing angles    
.1 INC (HL)                        
        INC HL                          
.3 DEC (HL)                        
        INC HL                          
.2 INC (HL)                        
        LD A,#7F:IN A,(#FE)             
        RRA:JR C,MAIN                   
        RET                             
                                        
ROTX DB 0                            
ROTY DB 0                            
ROTZ DB 0                            
                                        
OBJECT DW 0                            
                                        
As you can see, it's all very simple, simpler
and it cannot be.                        
                                        
 Actually, for objects with a large number of
quality of peaks and, especially, with complex
transformations(turns around the
arbitrary axis, scaling) very
it is profitable to build a transformation matrix,
then for all we need to do with
the top is to perform 9 multiplications and
write X and Y to the buffer... Somehow,
Perhaps we will work with matrices as well.  
                                        
 Looking ahead, let's say that next
This article will describe a method that
allows you to easily process volumes
projects consisting of hundreds of vertices, but for
it is not effective for such a simple object.

Share your thoughts about the article