QBASIC整数数学旋转和透视算法演示
参考程序
'该源码由Rich Geldreich在1992.1.2完成
'QuickBASIC4.5 3D线框动画程序.
DEFINT A-Z
TYPE LineType
X AS INTEGER
Y AS INTEGER
Z AS INTEGER
X1 AS INTEGER
Y1 AS INTEGER
Z1 AS INTEGER
END TYPE
DIM Points(100) AS LineType
DIM Xn(100), Yn(100), Zn(100)
DIM Xs1(100), Ys1(100), Xe1(100), Ye1(100)
DIM X(100), Y(100), Z(100), Pointers1(100), Pointers2(100), Sp(100), Zp(100)
DIM R(100), B(63), B1(63)
DIM Cosine&(360), Sine&(360)
CLS
PRINT "3-D Craft"
PRINT "By Rich 1992.1.2"
PRINT
PRINT "Keys to use: (Turn NUMLOCK on!)"
PRINT "Q...............Quits"
PRINT "Numeric keypad..Controls your position(press 5 on the keypad"
PRINT " to completly stop yourself) "
PRINT "-...............Forward exceleration"
PRINT "+...............Backward exceleration"
PRINT "Arrow keys......Controls the rotation of the craft"
PRINT "F...............Excelerates the craft (Forward)"
PRINT "B...............Slows the craft (Backward)"
PRINT "S...............Stops the craft"
PRINT "A...............Toggles Auto Center, use this when you lose";
PRINT " the craft"
PRINT "C...............Stops the craft's rotation"
PRINT "V...............Resets the craft to starting position"
PRINT
PRINT "Wait a sec..."
'下面的for/next循环生成一个正弦和余弦表。
'每个正弦和余弦都乘以1024,并存储为长整数。
'这样做是为了我们不必使用任何缓慢的浮点。
'单CPU下一边计算一边执行影响速度。
A = 0
FOR A! = 0 TO 359 / 57.29577951# STEP 1 / 57.29577951#
Cosine&(A) = INT(.5 + COS(A!) * 1024)
Sine&(A) = INT(.5 + SIN(A!) * 1024): A = A + 1
NEXT
'接下来,我们读入对象中的所有行。。。
FOR A = 0 TO 44
READ Points(A).X, Points(A).Y, Points(A).Z
READ Points(A).X1, Points(A).Y1, Points(A).Z1
NEXT
'困难的部分来了。。。考虑这种情况:
'我们有两条相连的线路,如下所示:
' 1--------2 and 3
' |
' |
' |
' |
' 4
'其中1,2,3&4是每条线的起点和终点。
'第一条线由点1和2组成,第二条线由第3点和第4点组成。
'那么,你会问,怎么了?没什么,真的,但你没看到吗?
'第2点和第3点真的在采样点吗?为什么要旋转它们两次,那完全是在浪费时间?
'以下代码从行表中删除了此类情况。(很好的解释,是吗?)
NumberLines = 45
'把所有的起点和终点放在一个大数组中。。。
Np = 0
FOR A = 0 TO NumberLines - 1
X(Np) = Points(A).X
Y(Np) = Points(A).Y
Z(Np) = Points(A).Z
Np = Np + 1
X(Np) = Points(A).X1
Y(Np) = Points(A).Y1
Z(Np) = Points(A).Z1
Np = Np + 1
NEXT
'现在设置两组指针,指向由一条线组成的每一点。。。
'(换句话说,在我们刚刚构建的点阵列中,扫描每个起点和
'终点的第一次出现…)
FOR A = 0 TO NumberLines - 1
Xs = Points(A).X
Ys = Points(A).Y
Zs = Points(A).Z '获取起点的3个坐标
FOR B = 0 TO Np - 1 '扫描点阵列
IF X(B) = Xs AND Y(B) = Ys AND Z(B) = Zs THEN
Pointers1(A) = B '将指针设置为指向
EXIT FOR '我们刚刚发现的点
END IF
NEXT
Xs = Points(A).X1 '做与我们上面做的相同的事情
Ys = Points(A).Y1 '除了扫描终点
Zs = Points(A).Z1 '每条线路的
FOR B = 0 TO Np - 1
IF X(B) = Xs AND Y(B) = Ys AND Z(B) = Zs THEN
Pointers2(A) = B
EXIT FOR
END IF
NEXT
NEXT
'好吧,差不多完成了!我们现在要做的就是建一张桌子
'这告诉我们哪些点实际上是旋转的。。。
Nr = 0
FOR A = 0 TO NumberLines - 1
F1 = Pointers1(A) '获取起点和终点编号
S1 = Pointers2(A)
IF Nr = 0 THEN '如果Nr=0,那么'如果这是第一个点,那么它当然
'必须旋转
R(Nr) = F1: Nr = Nr + 1
ELSE
Found = 0 '扫描以查看此点是否已存在。。。
FOR B = 0 TO Nr - 1
IF R(B) = F1 THEN
Found = -1: EXIT FOR '射击,已经到了!
END IF
NEXT
IF NOT Found THEN R(Nr) = F1: Nr = Nr + 1 '指向数组中的点
'在数组中我们
END IF '找不到它。。。
Found = 0 '现在寻找终点
FOR B = 0 TO Nr - 1
IF R(B) = S1 THEN
Found = -1: EXIT FOR
END IF
NEXT
IF NOT Found THEN R(Nr) = S1: Nr = Nr + 1
NEXT
FOR A = 0 TO 63
B(A) = (4 * A) \ 8
B1(A) = A - B(A)
NEXT
PRINT "Press any key to begin..."
A$ = INPUT$(1)
Deg1 = 0: Deg2 = 0: D1 = 0: D2 = 0
Spos = -200: Mypos = 0
Mx = 0: My = 0: Mz = 0: Ox = 0: Oy = 0: Oz = -260
NumberOfFrames = 0
DEF SEG = &H40
StartTime = PEEK(&H6C)
SCREEN 13
FOR A = 0 TO 63
OUT &H3C7, A: OUT &H3C8, A: OUT &H3C9, A: OUT &H3C9, 0: OUT &H3C9, 0
NEXT
DO
Deg1 = (Deg1 + D1) MOD 360
Deg2 = (Deg2 + D2) MOD 360
IF Deg1 < 0 THEN Deg1 = Deg1 + 360
IF Deg2 < 0 THEN Deg2 = Deg2 + 360
C1& = Cosine&(Deg1): S1& = Sine&(Deg1)
C2& = Cosine&(Deg2): S2& = Sine&(Deg2)
C3& = Cosine&(Deg3): S3& = Sine&(Deg3)
'Deg3 = (Deg3 + 5) MOD 360
X = Speed: Y = 0: Z = 0
X1 = (X * C1&) \ 1024: Y1 = (X * S1&) \ 1024
X2 = (X1 * C2&) \ 1024: Zn = (X1 * S2&) \ 1024
Y3 = (Y1 * C3& - Zn * S3&) \ 1024
Z3 = (Y1 * S3& + Zn * C3&) \ 1024
Ox = Ox + X2: Oy = Oy + Y3: Oz = Oz + Z3
IF Oz > 32000 THEN Oz = 32000
IF Oz < -32000 THEN Oz = -32000
IF Ox > 32000 THEN Ox = 32000
IF Ox < -32000 THEN Ox = -32000
IF Oy > 32000 THEN Oy = 32000
IF Oy < -32000 THEN Oy = -32000
IF AtLoc THEN
Mx = Mx + (Ox - Mx) \ 4
My = My + (Oy - My) \ 4
Mz = Mz + ((Oz + 200) - Mz) \ 4
ELSE
'根据用户的移动量调整用户的位置。。。
Mz = Mz + Mzm: Mx = Mx + Mxm: My = My + Mym
IF Mz > 32000 THEN Mz = 32000
IF Mz < -32000 THEN Mz = -32000
IF Mx > 32000 THEN Mx = 32000
IF Mx < -32000 THEN Mx = -32000
IF My > 32000 THEN My = 32000
IF My < -32000 THEN My = -32000
END IF
LOCATE 1, 1: PRINT A$
MaxZ = -32768
LowZ = 32767
FOR A = 0 TO Nr - 1
R = R(A)
Xo = X(R): Yo = Y(R): Zo = Z(R)
X1 = (Xo * C1& - Yo * S1&) \ 1024
Y1 = (Xo * S1& + Yo * C1&) \ 1024
X2& = (X1 * C2& - Zo * S2&) \ 1024 - Mx + Ox
Z2 = (X1 * S2& + Zo * C2&) \ 1024
Y3& = (Y1 * C3& - Z2 * S3&) \ 1024 - My + Oy
Z4 = (Y1 * S3& + Z2 * C3&) \ 1024
Z3 = Z4 - Mz + Oz
Zn(R) = Z4
IF Z4 > MaxZ THEN MaxZ = Z4
IF Z4 < LowZ THEN LowZ = Z4
'X2&,Y3&,Z3
'如果该点离观看者太近(或落后),则
'不要绘画它。。。
IF (Mypos - Z3) < 15 THEN
Xn(R) = -1000: Yn(R) = 0: Zn = 0
ELSE
V = (1330& * (Spos - Z3)) \ (Mypos - Z3)
Xn(R) = 160 + X2& + (-X2& * V) \ 1330
Yn(R) = 100 + (8 * (Y3& + (-Y3& * V) \ 1330)) \ 10
END IF
NEXT
MaxZ = MaxZ - LowZ
Nl = 0
FOR A = 0 TO NumberLines - 1
F1 = Pointers1(A): S1 = Pointers2(A)
IF Xn(F1) <> -1000 AND Xn(S1) <> -1000 THEN
Sp(Nl) = A
Zp(A) = (Zn(F1) + Zn(S1)) \ 2
Nl = Nl + 1
END IF
NEXT
Nl = Nl - 1
'根据线的Z坐标对线进行排序
IF Nl > -1 THEN
Mid = Nl \ 2
DO
FOR A = 0 TO Nl - Mid
IF Zp(Sp(A)) > Zp(Sp(A + Mid)) THEN
SWAP Sp(A), Sp(A + Mid)
CL = A - Mid
CH = A
DO WHILE CL >= 0
IF Zp(Sp(CL)) > Zp(Sp(CH)) THEN
SWAP Sp(CL), Sp(CH)
CH = CL
CL = CL - Mid
ELSE
EXIT DO
END IF
LOOP
END IF
NEXT
Mid = Mid \ 2
LOOP WHILE Mid > 0
END IF
'等待垂直回扫
WAIT &H3DA, 8
'擦除旧点
FOR A = Ln - 1 TO 0 STEP -1
LINE (Xs1(A), Ys1(A))-(Xe1(A), Ye1(A)), 0
NEXT
Ln = 0
FOR A1 = 0 TO Nl
A = Sp(A1)
Z = Zp(A)
F1 = Pointers1(Sp(A1)): S1 = Pointers2(Sp(A1))
Xn = Xn(F1): Yn = Yn(F1)
IF Xn <> -1000 THEN
X1 = Xn(S1)
IF X1 <> -1000 THEN
Y1 = Yn(S1)
Z1 = (Z - Mz + Oz)
IF Z1 > -1500 THEN
'计算颜色
T = 63 - ((Z1 * -63&) \ 1500)
C = B1(T) + (B(T) * (Z - LowZ)) \ MaxZ
'画线
LINE (X1, Y1)-(Xn, Yn), C
'存储以备以后使用
Xs1(Ln) = X1: Ys1(Ln) = Y1
Xe1(Ln) = Xn: Ye1(Ln) = Yn
Ln = Ln + 1
END IF
END IF
END IF
NEXT
'处理击键
K$ = UCASE$(INKEY$)
'处理击键(如果有)。。。
IF K$ <> "" THEN
SELECT CASE K$
CASE "A"
AtLoc = NOT AtLoc
CASE "+"
Mzm = Mzm + 2
CASE "-"
Mzm = Mzm - 2
CASE "5"
Mxm = 0: Mym = 0: Mzm = 0
CASE "4"
Mxm = Mxm - 2
CASE "6"
Mxm = Mxm + 2
CASE "8"
Mym = Mym - 2
CASE "2"
Mym = Mym + 2
CASE "F"
Speed = Speed + 5
CASE "B"
Speed = Speed - 5
CASE "C"
D1 = 0: D2 = 0
CASE "S"
Speed = 0
CASE CHR$(0) + CHR$(72)
D1 = D1 + 1
CASE CHR$(0) + CHR$(80)
D1 = D1 - 1
CASE CHR$(0) + CHR$(75)
D2 = D2 - 1
CASE CHR$(0) + CHR$(77)
D2 = D2 + 1
CASE "Q", CHR$(27)
SCREEN 0, , 0, 0: WIDTH 80
CLS
PRINT "END"
END
CASE "V"
D1 = 0: D2 = 0: Deg1 = 0: Deg2 = 0: Speed = 0
END SELECT
END IF
NumberOfFrames = NumberOfFrames + 1
'查看是否已经通过了20帧;如果是,那么看看
'花了多长时间。。。
IF NumberOfFrames = 20 THEN
TotalTime = PEEK(&H6C) - StartTime
IF TotalTime < 0 THEN TotalTime = TotalTime + 256
FramesPerSecX100 = 36400 \ TotalTime
High = FramesPerSecX100 \ 100
Low = FramesPerSecX100 - High
'A$的字符串打印在左上角
'屏幕的一角
A$ = MID$(STR$(High), 2) + "."
A$ = A$ + RIGHT$("0" + MID$(STR$(Low), 2), 2) + " "
NumberOfFrames = 0
StartTime = PEEK(&H6C)
END IF
LOOP
'以下数据是航天飞机。。。
'存储为 开始 X,Y,Z & 结束 X,Y,Z
DATA -157,22,39,-157,-18,39
DATA -157,-18,39,-127,-38,39
DATA -127,-38,39,113,-38,39
DATA 113,-38,39,193,12,39
DATA 33,42,39,33,42,-56
DATA 33,42,-56,-127,42,-56
DATA -127,42,-56,-157,22,-56
DATA -157,22,-56,-157,22,39
DATA -157,22,-56,-157,-18,-56
DATA -157,-18,-56,-157,-18,39
DATA -157,-18,-56,-127,-38,-56
DATA -127,-38,-56,-127,-38,39
DATA -127,-38,-56,113,-38,-56
DATA 113,-38,-56,113,-38,39
DATA 113,-38,-56,193,12,-56
DATA 193,12,-56,193,12,39
DATA -157,22,-56,193,12,-56
DATA 193,12,39,-157,22,39
DATA -56,-13,41,-56,-3,41
DATA -56,-3,41,-26,-3,41
DATA -26,-3,41,-26,7,41
DATA -51,7,41,-31,-13,41
DATA -11,-13,41,-11,-3,41
DATA -11,-3,41,-1,7,41
DATA 9,7,41,9,-8,41
DATA 9,-8,41,24,-8,41
DATA 34,16,41,34,-38,41
DATA 33,-39,41,33,-39,-53
DATA 33,-39,-53,33,15,-53
DATA -42,-38,19,-72,-38,19
DATA -72,-38,19,-72,-38,-41
DATA -72,-38,-41,-42,-38,-41
DATA -42,-38,-41,-42,-38,19
DATA 33,42,39,34,16,41
DATA 33,42,-56,33,15,-53
DATA -157,22,39,-127,42,39
DATA -127,42,-56,-127,42,39
DATA -127,42,39,33,42,39
DATA 159,-8,-56,159,-8,40
DATA 143,-18,-56,143,-18,39
DATA 193,12,39,193,32,30
DATA 33,42,39,193,32,30
DATA 193,32,30,193,32,-47
DATA 33,42,-56,193,32,-47
DATA 193,12,-56,193,32,-47