Author Topic: Sanctum: major overhaul to particle world  (Read 3659 times)

0 Members and 1 Guest are viewing this topic.

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Sanctum: major overhaul to particle world
« on: October 04, 2020, 05:32:29 pm »
All you do is discover stuff. Everything should be obvious:

Code: QB64: [Select]
  1. ' Version 2020-10-04
  2.  
  3. ' Meta
  4. _TITLE "Sanctum"
  5. _DELAY .25
  6.  
  7. ' Hardware.
  8. 'SCREEN _NEWIMAGE(640, 480, 32)
  9. SCREEN _NEWIMAGE(800, 600, 32)
  10. 'SCREEN _NEWIMAGE(1024, 768, 32)
  11.  
  12. ' Color constants.
  13. CONST Aquamarine = _RGB32(127, 255, 212)
  14. CONST Black = _RGB32(0, 0, 0)
  15. CONST Blue = _RGB32(0, 0, 255)
  16. CONST BlueViolet = _RGB32(138, 43, 226)
  17. CONST Chocolate = _RGB32(210, 105, 30)
  18. CONST Cyan = _RGB32(0, 255, 255)
  19. CONST DarkBlue = _RGB32(0, 0, 139)
  20. CONST DarkGoldenRod = _RGB32(184, 134, 11)
  21. CONST DarkGray = _RGB32(169, 169, 169)
  22. CONST DarkKhaki = _RGB32(189, 183, 107)
  23. CONST DeepPink = _RGB32(255, 20, 147)
  24. CONST DodgerBlue = _RGB32(30, 144, 255)
  25. CONST ForestGreen = _RGB32(34, 139, 34)
  26. CONST Gray = _RGB32(128, 128, 128)
  27. CONST Green = _RGB32(0, 128, 0)
  28. CONST Indigo = _RGB32(75, 0, 130)
  29. CONST Ivory = _RGB32(255, 255, 240)
  30. CONST LightSeaGreen = _RGB32(32, 178, 170)
  31. CONST Lime = _RGB32(0, 255, 0)
  32. CONST LimeGreen = _RGB32(50, 205, 50)
  33. CONST Magenta = _RGB32(255, 0, 255)
  34. CONST PaleGoldenRod = _RGB32(238, 232, 170)
  35. CONST Purple = _RGB32(128, 0, 128)
  36. CONST Red = _RGB32(255, 0, 0)
  37. CONST RoyalBlue = _RGB32(65, 105, 225)
  38. CONST SaddleBrown = _RGB32(139, 69, 19)
  39. CONST Sienna = _RGB32(160, 82, 45)
  40. CONST SlateGray = _RGB32(112, 128, 144)
  41. CONST Snow = _RGB32(255, 250, 250)
  42. CONST Sunglow = _RGB32(255, 207, 72)
  43. CONST SunsetOrange = _RGB32(253, 94, 83)
  44. CONST Teal = _RGB32(0, 128, 128)
  45. CONST White = _RGB32(255, 255, 255)
  46. CONST Yellow = _RGB32(255, 255, 0)
  47.  
  48. ' Mathematical constants.
  49. CONST pi = 4 * ATN(1)
  50. CONST ee = EXP(1)
  51.  
  52. ' Types.
  53.  
  54. TYPE Vector
  55.     x AS DOUBLE
  56.     y AS DOUBLE
  57.     z AS DOUBLE
  58.  
  59. TYPE ShadeMix
  60.     ShadeA AS _UNSIGNED LONG
  61.     ShadeB AS _UNSIGNED LONG
  62.     ShadeC AS _UNSIGNED LONG
  63.  
  64. TYPE Camera
  65.     Position AS Vector
  66.     Velocity AS Vector
  67.     Shade AS _UNSIGNED LONG
  68.  
  69. TYPE ClusterElement
  70.     Index AS LONG
  71.     FirstGroup AS LONG
  72.     LastGroup AS LONG
  73.     Centroid AS Vector
  74.     Velocity AS Vector
  75.     Visible AS INTEGER
  76.     MotionType AS INTEGER
  77.  
  78. TYPE GroupElement
  79.     Identity AS LONG
  80.     GroupName AS STRING
  81.     Pointer AS LONG
  82.     Lagger AS LONG
  83.     Volume AS Vector
  84.     FirstVector AS LONG
  85.     LastVector AS LONG
  86.     Centroid AS Vector
  87.     Velocity AS Vector
  88.     Visible AS INTEGER
  89.     Distance2 AS DOUBLE
  90.  
  91. TYPE MissionGroup
  92.     Label AS STRING
  93.     Discovered AS INTEGER
  94.  
  95. ' Scale.
  96. DIM SHARED bignumber AS LONG
  97. bignumber = 6000000
  98.  
  99. ' Cluster setup.
  100. DIM SHARED ClusterIndexTicker AS LONG
  101. DIM SHARED ClusterFillCounter AS INTEGER
  102. DIM SHARED Cluster(bignumber / 100) AS ClusterElement
  103. ClusterIndexTicker = 0
  104. ClusterFillCounter = 0
  105.  
  106. ' Group linked list setup.
  107. DIM SHARED Group(bignumber / 10) AS GroupElement
  108. DIM SHARED GroupIdTicker AS LONG
  109. GroupIdTicker = 0
  110.  
  111. ' Path
  112. DIM SHARED FixedPath(25, 86400) AS Vector
  113. DIM SHARED PathIndexTicker AS LONG
  114. PathIndexTicker = 0
  115.  
  116. ' Basis vectors defined in three-space.
  117. DIM SHARED xhat(3), yhat(3), zhat(3)
  118. xhat(1) = 1: xhat(2) = 0: xhat(3) = 0
  119. yhat(1) = 0: yhat(2) = 1: yhat(3) = 0
  120. zhat(1) = 0: zhat(2) = 0: zhat(3) = 1
  121.  
  122. ' Camera orientation vectors.
  123. DIM SHARED uhat(3), vhat(3), nhat(3)
  124.  
  125. ' Camera position.
  126. DIM SHARED PlayerCamera AS Camera
  127.  
  128. ' Field-of-view distance.
  129. DIM SHARED fovd
  130. fovd = -192
  131.  
  132. ' Clipping planes.
  133. DIM SHARED nearplane(4), farplane(4), rightplane(4), leftplane(4), topplane(4), bottomplane(4)
  134. nearplane(4) = 1
  135. farplane(4) = -180
  136. rightplane(4) = -17 ' (10 looks better than 1.)
  137. leftplane(4) = -17
  138. topplane(4) = -17
  139. bottomplane(4) = -17
  140.  
  141. ' World vectors.
  142. DIM SHARED vec(bignumber, 3) ' Relative Position
  143. DIM SHARED vec3Dpos(bignumber, 3) ' Position
  144. DIM SHARED vec3Dvel(bignumber, 3) ' Linear velocity
  145. DIM SHARED vec3Dvis(bignumber) ' Visible toggle
  146. DIM SHARED vec2D(bignumber, 2) ' Projection onto 2D plane
  147. DIM SHARED vec3Dcolor(bignumber) AS LONG ' Original color
  148. DIM SHARED vec2Dcolor(bignumber) AS LONG ' Projected color
  149.  
  150. ' Mission.
  151. DIM SHARED MissionTicker AS INTEGER
  152. DIM SHARED Mission(20) AS MissionGroup
  153.  
  154. ' Interface.
  155. DIM SHARED ToggleHUD AS INTEGER
  156. DIM SHARED ToggleAnimate AS INTEGER
  157. DIM SHARED FPSReport AS INTEGER
  158. DIM SHARED NumClusterVisible AS LONG
  159. DIM SHARED NumVectorVisible AS LONG
  160. DIM SHARED NumGroupVisible AS LONG
  161. DIM SHARED ClosestGroup AS LONG
  162.  
  163. ' Initialize.
  164. ToggleAnimate = 1
  165. ToggleHUD = 1
  166. PlayerCamera.Position.x = -40
  167. PlayerCamera.Position.y = 30
  168. PlayerCamera.Position.z = 40
  169. uhat(1) = -.2078192: uhat(2) = -.9781672: uhat(3) = 0
  170. vhat(1) = 0: vhat(2) = 0: vhat(3) = 1
  171. CALL CalculateScreenVectors
  172.  
  173. ' Prime main loop.
  174. CALL CreateLevel
  175. CALL CreateMission(10)
  176.  
  177. ' Start main loop.
  178. CALL MainLoop
  179.  
  180. ' Subs and Functions
  181.  
  182. SUB CreateMission (ListSize AS INTEGER)
  183.     DIM k AS INTEGER
  184.     DIM a AS STRING
  185.     DIM b AS STRING
  186.     a = ""
  187.     b = ""
  188.     REDIM MissionPool(0) AS STRING
  189.  
  190.     '''
  191.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Atmospheric dust"
  192.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Black hole"
  193.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Cloudy sky"
  194.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Comet 1"
  195.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Comet 2"
  196.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Comet 3"
  197.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Demon orb"
  198.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Dirt and sand"
  199.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Grave"
  200.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Hell sparks"
  201.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Laser"
  202.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Lush terrain"
  203.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Tornado 1"
  204.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Tornado 2"
  205.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Tornado 3"
  206.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Tornado 4"
  207.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Planet center"
  208.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Planet column"
  209.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Planet interior"
  210.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Plasma core"
  211.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Pyramid"
  212.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Rain"
  213.     REDIM _PRESERVE MissionPool(UBOUND(MissionPool) + 1): MissionPool(UBOUND(MissionPool)) = "Singularity"
  214.     '''
  215.  
  216.     MissionTicker = 0
  217.     DO
  218.         k = 1 + INT(RND * UBOUND(MissionPool))
  219.         a = MissionPool(k)
  220.         IF (INSTR(b, a) = 0) THEN
  221.             b = b + a
  222.             MissionTicker = MissionTicker + 1
  223.             Mission(MissionTicker).Label = a
  224.             Mission(MissionTicker).Discovered = 0
  225.         END IF
  226.     LOOP UNTIL (MissionTicker = ListSize)
  227.  
  228. SUB CreateLevel
  229.     DIM gtmp AS LONG
  230.     DIM p AS LONG
  231.     DIM n AS DOUBLE
  232.     DIM i, j, k AS DOUBLE
  233.     DIM u, v, w AS DOUBLE
  234.     DIM x0, y0, z0 AS DOUBLE
  235.  
  236.     ' Initialize linked list.
  237.     gtmp = CreateNewGroup&(0, 0, 0, 0, 0, 1)
  238.  
  239.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  240.  
  241.     ' Atmospheric dust
  242.     FOR u = -1400 TO 1400 - 350 STEP 350
  243.         FOR v = -1400 TO 1400 - 350 STEP 350
  244.             FOR i = u TO u + 350 STEP 35
  245.                 FOR j = v TO v + 350 STEP 35
  246.                     FOR w = (100 + 35 / 2) TO (800 - 35 + 35 / 2) STEP 35
  247.                         gtmp = NewCube&(gtmp, "Atmospheric dust", 5 * (1 - w / 800), i, j, w, 35, 35, 35, DarkGray, White, Snow, -1)
  248.                     NEXT
  249.                 NEXT
  250.             NEXT
  251.             CALL ClusterPinch(gtmp)
  252.         NEXT
  253.     NEXT
  254.     CALL ClusterPinch(gtmp)
  255.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  256.  
  257.     'Black hole
  258.     k = RND * 2 * pi
  259.     x0 = 1000 * COS(k)
  260.     y0 = 1000 * SIN(k)
  261.     z0 = 300
  262.     i = 100
  263.     j = 25
  264.     FOR u = -i TO i STEP j
  265.         FOR v = -i TO i STEP j
  266.             FOR w = -i TO i STEP j
  267.                 k = SQR(u * u + v * v + w * w)
  268.                 IF k <= i AND k >= i - j THEN
  269.                     gtmp = NewCube&(gtmp, "Black hole", j, x0 + u, y0 + v, z0 + w, j, j, j, DarkGray, White, Snow, -1)
  270.                     CALL SetParticleVelocity(gtmp, -.25 * u / k, -.25 * v / k, -.25 * w / k)
  271.                 END IF
  272.             NEXT
  273.         NEXT
  274.     NEXT
  275.     gtmp = NewShell&("Singularity", 10, x0, y0, z0, Black, Black, Black, 2, 5, -1)
  276.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  277.  
  278.     ' Comet
  279.     FOR n = 1 TO 3
  280.         u = RND * 2 * pi
  281.         PathIndexTicker = PathIndexTicker + 1
  282.         FOR p = 1 TO 86400
  283.             FixedPath(PathIndexTicker, p).x = .25 * COS(u + 2 * pi * (24 * 60) * (p - 1) / 86400)
  284.             FixedPath(PathIndexTicker, p).y = .25 * SIN(u + 2 * pi * (24 * 60) * (p - 1) / 86400)
  285.             FixedPath(PathIndexTicker, p).z = .15 * COS(u + 2 * pi * (24 * 60) * (p - 1) / 86400)
  286.         NEXT
  287.         x0 = 150 + (RND - .5) * 2 * 550
  288.         y0 = 150 + (RND - .5) * 2 * 550
  289.         z0 = 200 + RND * 100
  290.         gtmp = NewCube&(gtmp, "Comet" + STR$(n), 300, x0, y0, z0, 25, 25, 25, Teal, Cyan, DodgerBlue, PathIndexTicker)
  291.         FOR k = Group(gtmp).FirstVector TO Group(gtmp).LastVector
  292.             vec3Dvel(k, 1) = (RND - .5) * .20
  293.             vec3Dvel(k, 2) = (RND - .5) * .20
  294.             vec3Dvel(k, 3) = (RND - .5) * .20
  295.         NEXT
  296.         gtmp = NewShell&("Comet" + STR$(n), 15, x0, y0, z0, Teal, Cyan, DodgerBlue, 1, 80, PathIndexTicker)
  297.         CALL ClusterPinch(gtmp)
  298.     NEXT
  299.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  300.  
  301.     ' Demon orb
  302.     FOR n = 1 TO 6
  303.         u = RND * 2 * pi
  304.         PathIndexTicker = PathIndexTicker + 1
  305.         FOR p = 1 TO 86400
  306.             FixedPath(PathIndexTicker, p).x = .25 * COS(u + 2 * pi * (24 * 60) * (p - 1) / 86400)
  307.             FixedPath(PathIndexTicker, p).y = .25 * SIN(u + 2 * pi * (24 * 60) * (p - 1) / 86400)
  308.             FixedPath(PathIndexTicker, p).z = .15 * COS(u + 2 * pi * (24 * 60) * (p - 1) / 86400)
  309.         NEXT
  310.         x0 = (RND - .5) * 2 * 1000
  311.         y0 = (RND - .5) * 2 * 1000
  312.         z0 = -700 - RND * 100
  313.         gtmp = NewCube&(gtmp, "Demon orb", 300, x0, y0, z0, 25, 25, 25, Red, SunsetOrange, Sunglow, PathIndexTicker)
  314.         FOR k = Group(gtmp).FirstVector TO Group(gtmp).LastVector
  315.             vec3Dvel(k, 1) = (RND - .5) * .20
  316.             vec3Dvel(k, 2) = (RND - .5) * .20
  317.             vec3Dvel(k, 3) = (RND - .5) * .20
  318.         NEXT
  319.         gtmp = NewShell&("Demon orb", 15, x0, y0, z0, Red, SunsetOrange, Sunglow, 1, 80, PathIndexTicker)
  320.         CALL ClusterPinch(gtmp)
  321.     NEXT
  322.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  323.  
  324.     ' Death Star
  325.     PathIndexTicker = PathIndexTicker + 1
  326.     FOR p = 1 TO 86400
  327.         FixedPath(PathIndexTicker, p).x = -.1 * COS(2 * pi * (24 * 60) * (p - 1) / 86400)
  328.         FixedPath(PathIndexTicker, p).y = -.1 * SIN(2 * pi * (24 * 60) * (p - 1) / 86400)
  329.         FixedPath(PathIndexTicker, p).z = 0
  330.     NEXT
  331.     x0 = 100 + (RND - .5) * 2 * 250
  332.     y0 = 100 + (RND - .5) * 2 * 250
  333.     gtmp = NewShell&("Death Star", 75, x0, y0, 400, Gray, DarkGray, White, 0, 0, PathIndexTicker)
  334.     gtmp = NewShell&("Death Star reactor", 15, x0, y0, 400, Cyan, Teal, Blue, 1, 20, PathIndexTicker)
  335.     gtmp = NewCube&(gtmp, "Plasma core", 300, x0, y0, 400, 25, 25, 25, Cyan, Teal, Blue, PathIndexTicker)
  336.     FOR p = Group(gtmp).FirstVector TO Group(gtmp).LastVector
  337.         vec3Dvel(p, 1) = (RND - .5) * .20
  338.         vec3Dvel(p, 2) = (RND - .5) * .20
  339.         vec3Dvel(p, 3) = (RND - .5) * .20
  340.     NEXT
  341.     FOR n = 10 TO 1 STEP -.5
  342.         gtmp = NewCube&(gtmp, "Laser", 300, x0 + 75 / SQR(2) + (n - 1) * 25, y0 + .5 * n * 25, 400 + 75 / SQR(2) + .5 * (n - 1) * 25, 25, 25, 25, Lime, Green, White, PathIndexTicker)
  343.         FOR p = Group(gtmp).FirstVector TO Group(gtmp).LastVector
  344.             vec3Dvel(p, 1) = (RND - .5) * .50
  345.             vec3Dvel(p, 2) = (RND - .5) * .50
  346.             vec3Dvel(p, 3) = (RND - .5) * .50
  347.         NEXT
  348.     NEXT
  349.     CALL ClusterPinch(gtmp)
  350.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  351.  
  352.     ' Dirt and sand
  353.     FOR u = -1400 TO 1400 - 350 STEP 350
  354.         FOR v = -1400 TO 1400 - 350 STEP 350
  355.             FOR i = u TO u + 350 STEP 35
  356.                 FOR j = v TO v + 350 STEP 35
  357.                     FOR w = 0 - 35 / 2 TO -770 - 35 / 2 STEP -35
  358.                         gtmp = NewCube&(gtmp, "Dirt and sand", 5, i, j, w, 35, 35, 35, SaddleBrown, DarkKhaki, Sienna, -1)
  359.                     NEXT
  360.                 NEXT
  361.             NEXT
  362.             CALL ClusterPinch(gtmp)
  363.         NEXT
  364.     NEXT
  365.     CALL ClusterPinch(gtmp)
  366.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  367.  
  368.     ' Grave and Pyramid (1/2)
  369.     k = RND * 2 * pi
  370.     x0 = 1000 * COS(k)
  371.     y0 = 1000 * SIN(k)
  372.     FOR u = x0 - 160 TO x0 + 160 STEP 80
  373.         FOR v = y0 - 160 TO y0 + 160 STEP 80
  374.             gtmp = NewCube&(gtmp, "Grave", 200, u, v, (1) * 10 - 10 / 2, 10, 10, 10, Gray, SlateGray, DarkGray, -1)
  375.             gtmp = NewCube&(gtmp, "Grave", 200, u, v, (2) * 10 - 10 / 2, 10, 10, 10, Gray, SlateGray, DarkGray, -1)
  376.             gtmp = NewCube&(gtmp, "Grave", 200, u, v, (3) * 10 - 10 / 2, 10, 10, 10, Gray, SlateGray, DarkGray, -1)
  377.             gtmp = NewCube&(gtmp, "Grave", 200, u - 10, v, (4) * 10 - 10 / 2, 10, 10, 10, Gray, SlateGray, DarkGray, -1)
  378.             gtmp = NewCube&(gtmp, "Grave", 200, u, v, (4) * 10 - 10 / 2, 10, 10, 10, Gray, SlateGray, DarkGray, -1)
  379.             gtmp = NewCube&(gtmp, "Grave", 200, u + 10, v, (4) * 10 - 10 / 2, 10, 10, 10, Gray, SlateGray, DarkGray, -1)
  380.             gtmp = NewCube&(gtmp, "Grave", 200, u, v, (5) * 10 - 10 / 2, 10, 10, 10, Gray, SlateGray, DarkGray, -1)
  381.         NEXT
  382.     NEXT
  383.     CALL ClusterPinch(gtmp)
  384.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  385.  
  386.     ' Grave and Pyramid (2/2)
  387.     k = k + pi
  388.     x0 = 1000 * COS(k)
  389.     y0 = 1000 * SIN(k)
  390.     FOR w = 0 TO 75 STEP 15
  391.         FOR u = -75 + w TO 75 - w STEP 15
  392.             FOR v = -75 + w TO 75 - w STEP 15
  393.                 gtmp = NewCube&(gtmp, "Pyramid", 35, x0 + u, y0 + v, w, 13, 13, 13, DarkKhaki, SunsetOrange, DarkGoldenRod, -1)
  394.             NEXT
  395.         NEXT
  396.     NEXT
  397.     CALL ClusterPinch(gtmp)
  398.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  399.  
  400.     ' Hell Sparks
  401.     FOR u = -1400 TO 1400 - 350 STEP 350
  402.         FOR v = -1400 TO 1400 - 350 STEP 350
  403.             FOR i = u TO u + 350 STEP 35
  404.                 FOR j = v TO v + 350 STEP 35
  405.                     gtmp = NewCube&(gtmp, "Hell sparks", 15, i, j, -850, 35, 35, 100, Red, SunsetOrange, Sunglow, -1)
  406.                     CALL SetParticleVelocity(gtmp, 0, 0, .75 + RND * .25)
  407.                 NEXT
  408.             NEXT
  409.         NEXT
  410.     NEXT
  411.     CALL ClusterPinch(gtmp)
  412.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  413.  
  414.     ' Lake of Fire
  415.     gtmp = NewTerrain&("Lake of Fire", 2800, 2800, 35, 0, 0, -900, Red, SunsetOrange, Sunglow, 4, 30)
  416.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  417.  
  418.     ' Planet
  419.     PathIndexTicker = PathIndexTicker + 1
  420.     FOR p = 1 TO 86400
  421.         FixedPath(PathIndexTicker, p).x = .1 * COS(2 * pi * (24 * 60) * (p - 1) / 86400)
  422.         FixedPath(PathIndexTicker, p).y = .1 * SIN(2 * pi * (24 * 60) * (p - 1) / 86400)
  423.         FixedPath(PathIndexTicker, p).z = 0
  424.     NEXT
  425.     x0 = 100 + (RND - .5) * 2 * 250
  426.     y0 = 100 + (RND - .5) * 2 * 250
  427.     gtmp = NewPlanet&(350, 35, x0, y0, 900, PathIndexTicker)
  428.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  429.  
  430.     ' Rain
  431.     FOR u = -350 TO 350 STEP 35
  432.         FOR v = -350 TO 350 STEP 35
  433.             IF (u * u + v * v <= 350 ^ 2) THEN
  434.                 gtmp = NewCube&(gtmp, "Rain", 15, u, v, 50, 35, 35, 100, Blue, DodgerBlue, Blue, -1)
  435.                 CALL SetParticleVelocity(gtmp, 0, 0, -.75 - RND * .25)
  436.             END IF
  437.         NEXT
  438.     NEXT
  439.     CALL ClusterPinch(gtmp)
  440.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  441.  
  442.     ' Realm of Heaven
  443.     gtmp = NewTerrain&("Realm of Heaven", 2800, 2800, 35, 0, 0, 1400, BlueViolet, Cyan, White, 4, 30)
  444.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  445.  
  446.     ' Sky and Terrain
  447.     gtmp = NewTerrain&("Cloudy sky", 1400, 1400, 35, 0, 0, 100, RoyalBlue, DarkGray, Snow, 4, 30)
  448.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  449.     gtmp = NewTerrain&("Lush terrain", 1400, 1400, 35, 0, 0, 0, Green, LightSeaGreen, Blue, 4, 30)
  450.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  451.     gtmp = NewTerrain&("Northern desert sand", 2100, 700, 35, 350, 700 + 350 + 35, 0, DarkKhaki, PaleGoldenRod, Sunglow, 4, 30)
  452.     gtmp = NewTerrain&("Northern desert sky", 2100, 700, 35, 350, 700 + 350 + 35, 100, RoyalBlue, DarkGray, Indigo, 4, 30)
  453.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  454.     gtmp = NewTerrain&("Southern desert sand", 2100, 700, 35, -350, -700 - 350 - 35, 0, DarkKhaki, PaleGoldenRod, Sunglow, 4, 30)
  455.     gtmp = NewTerrain&("Southern desert sky", 2100, 700, 35, -350, -700 - 350 - 35, 100, RoyalBlue, DarkGray, Indigo, 4, 30)
  456.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  457.     gtmp = NewTerrain&("Eastern desert sand", 700, 2100, 35, 700 + 350 + 35, -350, 0, DarkKhaki, PaleGoldenRod, Sunglow, 4, 30)
  458.     gtmp = NewTerrain&("Eastern desert sky", 700, 2100, 35, 700 + 350 + 35, -350, 100, RoyalBlue, DarkGray, Indigo, 4, 30)
  459.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  460.     gtmp = NewTerrain&("Western desert sand", 700, 2100, 35, -700 - 350 - 35, 350, 0, DarkKhaki, PaleGoldenRod, Sunglow, 4, 30)
  461.     gtmp = NewTerrain&("Western desert sky", 700, 2100, 35, -700 - 350 - 35, 350, 100, RoyalBlue, DarkGray, Indigo, 4, 30)
  462.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  463.  
  464.     ' Tornado
  465.     FOR n = 1 TO 4
  466.         u = RND * 2 * pi
  467.         PathIndexTicker = PathIndexTicker + 1
  468.         FOR p = 1 TO 86400
  469.             FixedPath(PathIndexTicker, p).x = 1 * COS(u + 2 * pi * (24 * 60) * (p - 1) / 86400)
  470.             FixedPath(PathIndexTicker, p).y = 1 * SIN(u + 2 * pi * (24 * 60) * (p - 1) / 86400)
  471.             FixedPath(PathIndexTicker, p).z = 0
  472.         NEXT
  473.         x0 = (RND - .5) * 2 * 750
  474.         y0 = (RND - .5) * 2 * 750
  475.         FOR k = 1 TO 30
  476.             u = RND * 100
  477.             v = RND * u / 3
  478.             w = RND * 2 * pi
  479.             gtmp = NewCube&(gtmp, "Tornado" + STR$(n), 35, x0 + v * COS(w), y0 + v * SIN(w), u, 15, 15, 15, DarkGray, SunsetOrange, DarkGoldenRod, PathIndexTicker)
  480.             CALL SetParticleVelocity(gtmp, -SIN(w), COS(w), 0)
  481.         NEXT
  482.         CALL ClusterPinch(gtmp)
  483.     NEXT
  484.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  485.  
  486.     ' Stellar dust
  487.     FOR u = -1400 TO 1400 - 350 STEP 350
  488.         FOR v = -1400 TO 1400 - 350 STEP 350
  489.             FOR i = u TO u + 350 STEP 35
  490.                 FOR j = v TO v + 350 STEP 35
  491.                     gtmp = NewCube&(gtmp, "Stellar dust", 15, i, j, 1350, 35, 35, 100, BlueViolet, Cyan, White, -1)
  492.                     CALL SetParticleVelocity(gtmp, 0, 0, -.75 - RND * .25)
  493.                 NEXT
  494.             NEXT
  495.         NEXT
  496.     NEXT
  497.     CALL ClusterPinch(gtmp)
  498.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  499.  
  500.     ''' Rocket
  501.     gtmp = NewCube&(gtmp, "Rocket", 200, 0, 0, 0, 15, 15, 35, Green, Green, Green, 0)
  502.     gtmp = NewCube&(gtmp, "Rocket", 200, 0, 0, 0, 15, 15, 15, Red, Red, Red, 0)
  503.     Cluster(ClusterIndexTicker).Velocity.x = 0
  504.     Cluster(ClusterIndexTicker).Velocity.y = 0
  505.     Cluster(ClusterIndexTicker).Velocity.z = 1
  506.     CALL ClusterPinch(gtmp)
  507.     LOCATE 1, 1: PRINT "Loading..." + STR$(INT(100 * Group(gtmp).LastVector / bignumber)) + "%": _DISPLAY
  508.  
  509.     'PRINT
  510.     'PRINT "Total clusters:  " + STR$(ClusterIndexTicker)
  511.     'PRINT "Total groups:    " + STR$(GroupIdTicker)
  512.     'PRINT "Total particles: " + STR$(Group(gtmp).LastVector)
  513.     '_DISPLAY
  514.     'SLEEP 5
  515.  
  516.  
  517. SUB MainLoop
  518.     DIM fps AS INTEGER
  519.     DIM fpstimer AS INTEGER
  520.     DIM tt AS INTEGER
  521.     fps = 0
  522.     fpstimer = INT(TIMER)
  523.     CALL CalculateScreenVectors
  524.     DO
  525.         CALL PlayerDynamics
  526.         CALL ComputeVisibleScene
  527.         CALL PlotWorld
  528.         CALL DisplayHUD
  529.         CALL KeyProcess
  530.  
  531.         fps = fps + 1
  532.         tt = INT(TIMER)
  533.         IF (tt = fpstimer + 1) THEN
  534.             fpstimer = tt
  535.             FPSReport = fps
  536.             fps = 0
  537.         END IF
  538.  
  539.         _DISPLAY
  540.         _LIMIT 30
  541.     LOOP
  542.  
  543. ' Elevated-order primitive group.
  544.  
  545. FUNCTION NewPlanet& (TheRadius AS DOUBLE, ChunkSize AS DOUBLE, PosX AS DOUBLE, PosY AS DOUBLE, PosZ AS DOUBLE, TheDynamic AS INTEGER)
  546.     DIM ShadeA AS _UNSIGNED LONG
  547.     DIM ShadeB AS _UNSIGNED LONG
  548.     DIM ShadeC AS _UNSIGNED LONG
  549.     DIM gtmp AS LONG
  550.     DIM rtemp AS DOUBLE
  551.     DIM u AS DOUBLE
  552.     DIM v AS DOUBLE
  553.     DIM w AS DOUBLE
  554.     DIM r AS DOUBLE
  555.     FOR rtemp = ChunkSize TO TheRadius STEP ChunkSize
  556.         FOR u = -rtemp TO rtemp STEP ChunkSize
  557.             FOR v = -rtemp TO rtemp STEP ChunkSize
  558.                 FOR w = -rtemp TO rtemp STEP ChunkSize
  559.                     r = SQR(u * u + v * v + w * w)
  560.                     IF r <= rtemp AND r >= rtemp - ChunkSize THEN
  561.                         ShadeA = Gradient~&(Red, SaddleBrown, r / TheRadius)
  562.                         ShadeB = Gradient~&(SunsetOrange, DarkKhaki, r / TheRadius)
  563.                         ShadeC = DarkKhaki
  564.                         gtmp = NewCube&(1, "Planet Interior", ChunkSize, PosX + u, PosY + v, PosZ + w, ChunkSize, ChunkSize, ChunkSize, ShadeA, ShadeB, ShadeC, TheDynamic)
  565.                         IF (r < TheRadius / 2.5) THEN
  566.                             Group(gtmp).GroupName = "Planet center"
  567.                             CALL SetParticleVelocity(gtmp, .25 * u / r, .25 * v / r, .25 * w / r)
  568.                         END IF
  569.                     END IF
  570.                 NEXT
  571.             NEXT
  572.         NEXT
  573.         CALL ClusterPinch(gtmp)
  574.     NEXT
  575.     CALL ClusterPinch(gtmp)
  576.     gtmp = NewShell&("Planet surface", TheRadius, PosX, PosY, PosZ, Green, DarkKhaki, DodgerBlue, 2, 20, TheDynamic)
  577.     gtmp = NewShell&("Planet core", TheRadius / 10, PosX, PosY, PosZ, Red, Yellow, DarkGoldenRod, 2, 5, TheDynamic)
  578.     FOR w = 0 + ChunkSize / 4 TO PosZ - TheRadius - ChunkSize / 4 STEP ChunkSize / 2
  579.         gtmp = NewCube&(1, "Planet column", 2 * ChunkSize, PosX, PosY, w, ChunkSize / 2, ChunkSize / 2, ChunkSize / 2, Cyan, Teal, Blue, TheDynamic)
  580.         CALL SetParticleVelocity(gtmp, .15 * (RND - .5), .15 * (RND - .5), .25 * RND)
  581.     NEXT
  582.     CALL ClusterPinch(gtmp)
  583.     NewPlanet& = gtmp
  584.  
  585. ' Medium-order primitive group(s).
  586.  
  587. FUNCTION NewTerrain& (TheName AS STRING, SizeX AS DOUBLE, SizeY AS DOUBLE, TheResolution AS DOUBLE, PosX AS DOUBLE, PosY AS DOUBLE, PosZ AS DOUBLE, ShadeA AS _UNSIGNED LONG, ShadeB AS _UNSIGNED LONG, ShadeC AS _UNSIGNED LONG, BumpFactor AS DOUBLE, SmoothFactor AS INTEGER)
  588.     DIM g AS LONG
  589.     DIM q AS LONG
  590.     DIM vindex AS LONG
  591.     DIM k AS INTEGER
  592.     DIM factor1 AS DOUBLE
  593.     DIM factor2 AS DOUBLE
  594.     DIM a AS DOUBLE
  595.     DIM b AS DOUBLE
  596.     DIM i AS DOUBLE
  597.     DIM j AS DOUBLE
  598.     DIM u AS DOUBLE
  599.     DIM v AS DOUBLE
  600.     DIM w AS DOUBLE
  601.     DIM ia AS DOUBLE
  602.     DIM jb AS DOUBLE
  603.     DIM rr AS DOUBLE
  604.     DIM wi AS INTEGER
  605.     DIM wj AS INTEGER
  606.     DIM wa AS INTEGER
  607.     DIM wb AS INTEGER
  608.     DIM ci AS INTEGER
  609.     DIM cj AS INTEGER
  610.     DIM ca AS INTEGER
  611.     DIM cb AS INTEGER
  612.  
  613.     factor1 = TheResolution
  614.     factor2 = factor1 / 10
  615.     wi = 0
  616.     wj = 0
  617.     wa = 0
  618.     wb = 0
  619.     FOR i = -SizeX / 2 TO SizeX / 2 STEP factor1
  620.         wi = wi + 1
  621.     NEXT
  622.     FOR j = -SizeY / 2 TO SizeY / 2 STEP factor1
  623.         wj = wj + 1
  624.     NEXT
  625.     FOR a = 0 TO factor1 STEP factor2
  626.         wa = wa + 1
  627.     NEXT
  628.     FOR b = 0 TO factor1 STEP factor2
  629.         wb = wb + 1
  630.     NEXT
  631.  
  632.     DIM vgrid(wi * wa, wj * wb, 2) AS DOUBLE
  633.     DIM vgrid2(wi * wa, wj * wb) AS DOUBLE
  634.     FOR ia = 1 TO wi * wa
  635.         FOR jb = 1 TO wj * wb
  636.             SELECT CASE (RND)
  637.                 CASE IS < .005
  638.                     vgrid(ia, jb, 1) = factor1 * RND
  639.                     vgrid(ia, jb, 2) = 1
  640.                 CASE IS > 1 - .005
  641.                     vgrid(ia, jb, 1) = -factor1 * RND / 2
  642.                     vgrid(ia, jb, 2) = 1
  643.                 CASE ELSE
  644.                     vgrid(ia, jb, 1) = 0
  645.                     vgrid(ia, jb, 2) = 0
  646.             END SELECT
  647.         NEXT
  648.     NEXT
  649.  
  650.     IF (SmoothFactor > 0) THEN
  651.         FOR k = 1 TO SmoothFactor
  652.             FOR ia = 1 TO wi * wa
  653.                 FOR jb = 1 TO wj * wb
  654.                     vgrid2(ia, jb) = vgrid(ia, jb, 1)
  655.                 NEXT
  656.             NEXT
  657.             FOR ia = 1 + 1 TO wi * wa - 1
  658.                 FOR jb = 1 + 1 TO wj * wb - 1
  659.                     IF (k = SmoothFactor - 5) THEN
  660.                         vgrid(ia, jb, 2) = 0
  661.                     END IF
  662.                     IF vgrid(ia, jb, 2) = 0 THEN
  663.                         vgrid(ia, jb, 1) = (1 / 4) * (vgrid2(ia - 1, jb) + vgrid2(ia + 1, jb) + vgrid2(ia, jb - 1) + vgrid2(ia, jb + 1))
  664.                     END IF
  665.                 NEXT
  666.             NEXT
  667.         NEXT
  668.     END IF
  669.  
  670.     ci = 0
  671.     cj = 0
  672.     ca = 0
  673.     cb = 0
  674.     g = 1
  675.     FOR i = -SizeX / 2 TO SizeX / 2 STEP factor1
  676.         ci = ci + 1
  677.         cj = 0
  678.         FOR j = -SizeY / 2 TO SizeY / 2 STEP factor1
  679.             cj = cj + 1
  680.             u = i
  681.             v = j
  682.             w = 0
  683.             q = LatestIdentity&(g)
  684.             vindex = Group(q).LastVector
  685.             g = CreateNewGroup&(q, PosX + u, PosY + v, PosZ + w, 0, 24)
  686.             Group(g).GroupName = TheName
  687.             Group(g).Volume.x = SizeX
  688.             Group(g).Volume.y = SizeY
  689.             Group(g).Volume.z = SQR(SizeX * SizeX + SizeY * SizeY)
  690.             Group(g).FirstVector = vindex + 1
  691.             ca = 0
  692.             FOR a = i TO i + factor1 STEP factor2
  693.                 ca = ca + 1
  694.                 cb = 0
  695.                 FOR b = j TO j + factor1 STEP factor2
  696.                     cb = cb + 1
  697.                     rr = vgrid((ci - 1) * wa + ca, (cj - 1) * wb + cb, 1)
  698.                     vindex = vindex + 1
  699.                     vec3Dpos(vindex, 1) = -u + a + BumpFactor * (RND - .5)
  700.                     vec3Dpos(vindex, 2) = -v + b + BumpFactor * (RND - .5)
  701.                     vec3Dpos(vindex, 3) = -w + rr + BumpFactor * (RND - .5)
  702.                     IF (rr > 0) THEN
  703.                         vec3Dcolor(vindex) = Gradient~&(ShadeA, ShadeB, rr / 10)
  704.                     ELSE
  705.                         vec3Dcolor(vindex) = ShadeC
  706.                     END IF
  707.                 NEXT
  708.             NEXT
  709.             Group(g).LastVector = vindex
  710.         NEXT
  711.         CALL ClusterPinch(g)
  712.     NEXT
  713.     CALL ClusterPinch(g)
  714.     NewTerrain& = g
  715.  
  716. FUNCTION NewShell& (TheName AS STRING, TheRadius AS DOUBLE, PosX AS DOUBLE, PosY AS DOUBLE, PosZ AS DOUBLE, ShadeA AS _UNSIGNED LONG, ShadeB AS _UNSIGNED LONG, ShadeC AS _UNSIGNED LONG, BumpFactor AS DOUBLE, SmoothFactor AS INTEGER, TheDynamic AS INTEGER)
  717.     DIM g AS LONG
  718.     DIM q AS LONG
  719.     DIM vindex AS LONG
  720.     DIM k AS INTEGER
  721.     DIM factor1 AS DOUBLE
  722.     DIM factor2 AS DOUBLE
  723.     DIM a AS DOUBLE
  724.     DIM b AS DOUBLE
  725.     DIM i AS DOUBLE
  726.     DIM j AS DOUBLE
  727.     DIM u AS DOUBLE
  728.     DIM v AS DOUBLE
  729.     DIM w AS DOUBLE
  730.     DIM ia AS DOUBLE
  731.     DIM jb AS DOUBLE
  732.     DIM rr AS DOUBLE
  733.     DIM wi AS LONG
  734.     DIM wj AS LONG
  735.     DIM wa AS LONG
  736.     DIM wb AS LONG
  737.     DIM ci AS LONG
  738.     DIM cj AS LONG
  739.     DIM ca AS LONG
  740.     DIM cb AS LONG
  741.  
  742.     IF (TheRadius > 200) THEN
  743.         factor1 = (2) * 2 * pi / (10 ^ (INT(LOG(TheRadius) / LOG(10))))
  744.     ELSE
  745.         factor1 = (1) * 2 * pi / (10 ^ (INT(LOG(TheRadius) / LOG(10))))
  746.     END IF
  747.     factor2 = factor1 / (10)
  748.  
  749.     wi = 0
  750.     wj = 0
  751.     wa = 0
  752.     wb = 0
  753.     FOR i = 0 TO 2 * pi STEP factor1
  754.         wi = wi + 1
  755.     NEXT
  756.     FOR j = 0 TO pi STEP factor1
  757.         wj = wj + 1
  758.     NEXT
  759.     FOR a = 0 TO factor1 STEP factor2
  760.         wa = wa + 1
  761.     NEXT
  762.     FOR b = 0 TO factor1 STEP factor2
  763.         wb = wb + 1
  764.     NEXT
  765.  
  766.     DIM vgrid(wi * wa, wj * wb, 2) AS DOUBLE
  767.     DIM vgrid2(wi * wa, wj * wb) AS DOUBLE
  768.  
  769.     FOR ia = 1 TO wi * wa
  770.         FOR jb = 1 TO wj * wb
  771.             SELECT CASE (RND)
  772.                 CASE IS < .025
  773.                     vgrid(ia, jb, 1) = TheRadius - BumpFactor * (TheRadius / 50)
  774.                     vgrid(ia, jb, 2) = 1
  775.                 CASE IS > 1 - .025
  776.                     vgrid(ia, jb, 1) = TheRadius + BumpFactor * (TheRadius / 50)
  777.                     vgrid(ia, jb, 2) = 1
  778.                 CASE ELSE
  779.                     vgrid(ia, jb, 1) = TheRadius
  780.                     vgrid(ia, jb, 2) = 0
  781.             END SELECT
  782.         NEXT
  783.     NEXT
  784.  
  785.     IF (SmoothFactor > 0) THEN
  786.         FOR k = 1 TO SmoothFactor
  787.             FOR ia = 1 TO wi * wa
  788.                 FOR jb = 1 TO wj * wb
  789.                     vgrid2(ia, jb) = vgrid(ia, jb, 1)
  790.                 NEXT
  791.             NEXT
  792.             FOR ia = 1 + 1 TO wi * wa - 1
  793.                 FOR jb = 1 + 1 TO wj * wb - 1
  794.                     IF (k = SmoothFactor - 5) THEN
  795.                         vgrid(ia, jb, 2) = 0
  796.                     END IF
  797.                     IF vgrid(ia, jb, 2) = 0 THEN
  798.                         vgrid(ia, jb, 1) = (1 / 4) * (vgrid2(ia - 1, jb) + vgrid2(ia + 1, jb) + vgrid2(ia, jb - 1) + vgrid2(ia, jb + 1))
  799.                     END IF
  800.                 NEXT
  801.             NEXT
  802.         NEXT
  803.     END IF
  804.  
  805.     ci = 0
  806.     cj = 0
  807.     ca = 0
  808.     cb = 0
  809.     g = 1
  810.     FOR i = 0 TO 2 * pi STEP factor1
  811.         ci = ci + 1
  812.         cj = 0
  813.         FOR j = 0 TO pi STEP factor1
  814.             cj = cj + 1
  815.             u = TheRadius * SIN(j) * COS(i)
  816.             v = TheRadius * SIN(j) * SIN(i)
  817.             w = TheRadius * COS(j)
  818.             q = LatestIdentity&(g)
  819.             vindex = Group(q).LastVector
  820.             g = CreateNewGroup&(q, PosX + u, PosY + v, PosZ + w, TheDynamic, 64)
  821.             Group(g).GroupName = TheName
  822.             Group(g).Volume.x = TheRadius
  823.             Group(g).Volume.y = TheRadius
  824.             Group(g).Volume.z = TheRadius
  825.             Group(g).FirstVector = vindex + 1
  826.             ca = 0
  827.             FOR a = i TO i + factor1 STEP factor2
  828.                 ca = ca + 1
  829.                 cb = 0
  830.                 FOR b = j TO j + factor1 STEP factor2
  831.                     cb = cb + 1
  832.                     rr = vgrid((ci - 1) * wa + ca, (cj - 1) * wb + cb, 1)
  833.                     vindex = vindex + 1
  834.                     vec3Dpos(vindex, 1) = -u + rr * SIN(b) * COS(a) + BumpFactor * (RND - .5) * 2
  835.                     vec3Dpos(vindex, 2) = -v + rr * SIN(b) * SIN(a) + BumpFactor * (RND - .5) * 2
  836.                     vec3Dpos(vindex, 3) = -w + rr * COS(b) + BumpFactor * (RND - .5) * 2
  837.                     IF (rr > TheRadius) THEN
  838.                         vec3Dcolor(vindex) = Gradient~&(ShadeA, ShadeB, COS(j) ^ 2)
  839.                     ELSE
  840.                         vec3Dcolor(vindex) = ShadeC
  841.                     END IF
  842.                 NEXT
  843.             NEXT
  844.             Group(g).LastVector = vindex
  845.         NEXT
  846.         CALL ClusterPinch(g)
  847.     NEXT
  848.     CALL ClusterPinch(g)
  849.  
  850.     NewShell& = g
  851.  
  852. ' Low-order primitive group(s).
  853.  
  854. FUNCTION NewCube& (StartingIdentity AS LONG, TheName AS STRING, Weight AS INTEGER, PosX AS DOUBLE, PosY AS DOUBLE, PosZ AS DOUBLE, VolX AS DOUBLE, VolY AS DOUBLE, VolZ AS DOUBLE, ShadeA AS _UNSIGNED LONG, ShadeB AS _UNSIGNED LONG, ShadeC AS _UNSIGNED LONG, TheDynamic AS INTEGER)
  855.     DIM k AS INTEGER
  856.     DIM g AS LONG
  857.     DIM q AS LONG
  858.     DIM vindex AS LONG
  859.     q = LatestIdentity&(StartingIdentity)
  860.     vindex = Group(q).LastVector
  861.     g = CreateNewGroup(q, PosX, PosY, PosZ, TheDynamic, 64)
  862.     Group(g).GroupName = TheName
  863.     Group(g).Volume.x = VolX
  864.     Group(g).Volume.y = VolY
  865.     Group(g).Volume.z = VolZ
  866.     Group(g).FirstVector = vindex + 1
  867.     FOR k = 1 TO Weight
  868.         vindex = vindex + 1
  869.         vec3Dpos(vindex, 1) = (RND - .5) * VolX
  870.         vec3Dpos(vindex, 2) = (RND - .5) * VolY
  871.         vec3Dpos(vindex, 3) = (RND - .5) * VolZ
  872.         IF (RND > .5) THEN
  873.             vec3Dcolor(vindex) = ShadeA
  874.         ELSE
  875.             IF (RND > .5) THEN
  876.                 vec3Dcolor(vindex) = ShadeB
  877.             ELSE
  878.                 vec3Dcolor(vindex) = ShadeC
  879.             END IF
  880.         END IF
  881.     NEXT
  882.     Group(g).LastVector = vindex
  883.     NewCube& = g
  884.  
  885. ' Linked list utility.
  886.  
  887. FUNCTION LatestIdentity& (StartingID AS LONG)
  888.     DIM TheReturn AS LONG
  889.     DIM p AS LONG
  890.     DIM q AS LONG
  891.     p = StartingID
  892.     DO
  893.         q = p
  894.         p = Group(q).Pointer
  895.         IF (p = -999) THEN EXIT DO
  896.     LOOP
  897.     TheReturn = q
  898.     LatestIdentity& = TheReturn
  899.  
  900. FUNCTION CreateNewGroup& (TheLaggerIn AS LONG, CenterX AS DOUBLE, CenterY AS DOUBLE, CenterZ AS DOUBLE, TheDynamic AS INTEGER, ClusterSize AS INTEGER)
  901.     GroupIdTicker = GroupIdTicker + 1
  902.     Group(GroupIdTicker).Identity = GroupIdTicker
  903.     Group(GroupIdTicker).Pointer = -999
  904.     Group(GroupIdTicker).Lagger = TheLaggerIn
  905.     Group(GroupIdTicker).Centroid.x = CenterX
  906.     Group(GroupIdTicker).Centroid.y = CenterY
  907.     Group(GroupIdTicker).Centroid.z = CenterZ
  908.     IF (TheLaggerIn <> 0) THEN
  909.         Group(TheLaggerIn).Pointer = Group(GroupIdTicker).Identity
  910.     END IF
  911.     '''
  912.     ClusterFillCounter = ClusterFillCounter + 1
  913.  
  914.     IF (ClusterFillCounter = 1) THEN
  915.         ClusterIndexTicker = ClusterIndexTicker + 1
  916.         Cluster(ClusterIndexTicker).FirstGroup = Group(GroupIdTicker).Identity
  917.         Cluster(ClusterIndexTicker).MotionType = TheDynamic
  918.     END IF
  919.  
  920.     IF (ClusterFillCounter = ClusterSize) THEN
  921.         CALL ClusterPinch(Group(GroupIdTicker).Identity)
  922.     END IF
  923.     '''
  924.     CreateNewGroup& = Group(GroupIdTicker).Identity
  925.  
  926. SUB ClusterPinch (TheLastGroup AS LONG)
  927.     ClusterFillCounter = 0
  928.     Cluster(ClusterIndexTicker).LastGroup = TheLastGroup
  929.     CALL ClusterCentroidCalc(ClusterIndexTicker)
  930.  
  931. SUB ClusterCentroidCalc (TheClusterIndex AS LONG)
  932.     DIM k AS LONG
  933.     DIM n AS INTEGER
  934.     Cluster(TheClusterIndex).Centroid.x = 0
  935.     Cluster(TheClusterIndex).Centroid.y = 0
  936.     Cluster(TheClusterIndex).Centroid.z = 0
  937.     k = Cluster(TheClusterIndex).FirstGroup
  938.     n = 0
  939.     DO
  940.         Cluster(TheClusterIndex).Centroid.x = Cluster(TheClusterIndex).Centroid.x + Group(k).Centroid.x
  941.         Cluster(TheClusterIndex).Centroid.y = Cluster(TheClusterIndex).Centroid.y + Group(k).Centroid.y
  942.         Cluster(TheClusterIndex).Centroid.z = Cluster(TheClusterIndex).Centroid.z + Group(k).Centroid.z
  943.         n = n + 1
  944.         IF (k = Cluster(TheClusterIndex).LastGroup) THEN EXIT DO
  945.         k = Group(k).Pointer
  946.     LOOP
  947.     Cluster(TheClusterIndex).Centroid.x = Cluster(TheClusterIndex).Centroid.x / n
  948.     Cluster(TheClusterIndex).Centroid.y = Cluster(TheClusterIndex).Centroid.y / n
  949.     Cluster(TheClusterIndex).Centroid.z = Cluster(TheClusterIndex).Centroid.z / n
  950.  
  951. ' Vector manipulation.
  952.  
  953. SUB SetParticleVelocity (TheGroup AS LONG, vx AS DOUBLE, vy AS DOUBLE, vz AS DOUBLE)
  954.     DIM j AS LONG
  955.     DIM m AS LONG
  956.     DIM n AS LONG
  957.     m = Group(TheGroup).FirstVector
  958.     n = Group(TheGroup).LastVector
  959.     FOR j = m TO n
  960.         vec3Dvel(j, 1) = vx
  961.         vec3Dvel(j, 2) = vy
  962.         vec3Dvel(j, 3) = vz
  963.     NEXT
  964.  
  965. ' Cluster manipulation.
  966.  
  967. SUB EvolveCluster (TheClusterIndex AS LONG)
  968.     DIM dx, dy, dz, t, u, v AS DOUBLE
  969.     IF (Cluster(TheClusterIndex).MotionType = 0) THEN
  970.         dx = Cluster(TheClusterIndex).Velocity.x
  971.         dy = Cluster(TheClusterIndex).Velocity.y
  972.         dz = Cluster(TheClusterIndex).Velocity.z
  973.     ELSE
  974.         t = TIMER
  975.         u = INT(t)
  976.         v = t - u
  977.         dx = v * FixedPath(Cluster(TheClusterIndex).MotionType, u).x + (1 - v) * FixedPath(Cluster(TheClusterIndex).MotionType, u - 1).x
  978.         dy = v * FixedPath(Cluster(TheClusterIndex).MotionType, u).y + (1 - v) * FixedPath(Cluster(TheClusterIndex).MotionType, u - 1).y
  979.         dz = v * FixedPath(Cluster(TheClusterIndex).MotionType, u).z + (1 - v) * FixedPath(Cluster(TheClusterIndex).MotionType, u - 1).z
  980.     END IF
  981.     IF ((dx <> 0) OR (dy <> 0) OR (dz <> 0)) THEN
  982.         CALL TranslateCluster(TheClusterIndex, dx, dy, dz)
  983.     END IF
  984.  
  985. SUB TranslateCluster (TheClusterIndex AS LONG, dx AS DOUBLE, dy AS DOUBLE, dz AS DOUBLE)
  986.     DIM k AS LONG
  987.     k = Cluster(TheClusterIndex).FirstGroup
  988.     DO
  989.         Group(k).Centroid.x = Group(k).Centroid.x + dx
  990.         Group(k).Centroid.y = Group(k).Centroid.y + dy
  991.         Group(k).Centroid.z = Group(k).Centroid.z + dz
  992.         IF (k = Cluster(TheClusterIndex).LastGroup) THEN EXIT DO
  993.         k = Group(k).Pointer
  994.     LOOP
  995.     Cluster(TheClusterIndex).Centroid.x = Cluster(TheClusterIndex).Centroid.x + dx
  996.     Cluster(TheClusterIndex).Centroid.y = Cluster(TheClusterIndex).Centroid.y + dy
  997.     Cluster(TheClusterIndex).Centroid.z = Cluster(TheClusterIndex).Centroid.z + dz
  998.  
  999. ' Particle manipulation.
  1000.  
  1001. SUB EvolveParticles (TheGroup AS LONG)
  1002.     DIM xdim AS DOUBLE
  1003.     DIM ydim AS DOUBLE
  1004.     DIM zdim AS DOUBLE
  1005.     DIM dx AS DOUBLE
  1006.     DIM dy AS DOUBLE
  1007.     DIM dz AS DOUBLE
  1008.     DIM px AS DOUBLE
  1009.     DIM py AS DOUBLE
  1010.     DIM pz AS DOUBLE
  1011.     DIM k AS LONG
  1012.  
  1013.     xdim = Group(TheGroup).Volume.x
  1014.     ydim = Group(TheGroup).Volume.y
  1015.     zdim = Group(TheGroup).Volume.z
  1016.  
  1017.     FOR k = Group(TheGroup).FirstVector TO Group(TheGroup).LastVector
  1018.  
  1019.         ' Position update with periodic boundaries inside group volume
  1020.         dx = 1 * vec3Dvel(k, 1)
  1021.         dy = 1 * vec3Dvel(k, 2)
  1022.         dz = 1 * vec3Dvel(k, 3)
  1023.         IF (dx <> 0) THEN
  1024.             px = vec3Dpos(k, 1) + dx
  1025.             IF ABS(px) > xdim / 2 THEN
  1026.                 IF (px > xdim / 2) THEN
  1027.                     px = -xdim / 2
  1028.                 ELSE
  1029.                     px = xdim / 2
  1030.                 END IF
  1031.             END IF
  1032.             vec3Dpos(k, 1) = px
  1033.         END IF
  1034.         IF (dy <> 0) THEN
  1035.             py = vec3Dpos(k, 2) + dy
  1036.             IF ABS(py) > ydim / 2 THEN
  1037.                 IF (py > ydim / 2) THEN
  1038.                     py = -ydim / 2
  1039.                 ELSE
  1040.                     py = ydim / 2
  1041.                 END IF
  1042.             END IF
  1043.             vec3Dpos(k, 2) = py
  1044.         END IF
  1045.         IF (dz <> 0) THEN
  1046.             pz = vec3Dpos(k, 3) + dz
  1047.             IF ABS(pz) > zdim / 2 THEN
  1048.                 IF (pz > zdim / 2) THEN
  1049.                     pz = -zdim / 2
  1050.                 ELSE
  1051.                     pz = zdim / 2
  1052.                 END IF
  1053.             END IF
  1054.             vec3Dpos(k, 3) = pz
  1055.         END IF
  1056.     NEXT
  1057.  
  1058. SUB CalculateScreenVectors
  1059.     DIM uhatmag AS DOUBLE
  1060.     DIM vhatmag AS DOUBLE
  1061.     DIM h2 AS DOUBLE
  1062.     DIM w2 AS DOUBLE
  1063.     DIM mag AS DOUBLE
  1064.     DIM uhatdotvhat AS DOUBLE
  1065.     uhatmag = SQR(uhat(1) * uhat(1) + uhat(2) * uhat(2) + uhat(3) * uhat(3))
  1066.     uhat(1) = uhat(1) / uhatmag: uhat(2) = uhat(2) / uhatmag: uhat(3) = uhat(3) / uhatmag
  1067.     vhatmag = SQR(vhat(1) * vhat(1) + vhat(2) * vhat(2) + vhat(3) * vhat(3))
  1068.     vhat(1) = vhat(1) / vhatmag: vhat(2) = vhat(2) / vhatmag: vhat(3) = vhat(3) / vhatmag
  1069.     uhatdotvhat = uhat(1) * vhat(1) + uhat(2) * vhat(2) + uhat(3) * vhat(3)
  1070.     nhat(1) = uhat(2) * vhat(3) - uhat(3) * vhat(2)
  1071.     nhat(2) = uhat(3) * vhat(1) - uhat(1) * vhat(3)
  1072.     nhat(3) = uhat(1) * vhat(2) - uhat(2) * vhat(1)
  1073.     h2 = _HEIGHT / 2
  1074.     w2 = _WIDTH / 2
  1075.     nearplane(1) = -nhat(1)
  1076.     nearplane(2) = -nhat(2)
  1077.     nearplane(3) = -nhat(3)
  1078.     farplane(1) = nhat(1)
  1079.     farplane(2) = nhat(2)
  1080.     farplane(3) = nhat(3)
  1081.     rightplane(1) = h2 * fovd * uhat(1) - h2 * w2 * nhat(1)
  1082.     rightplane(2) = h2 * fovd * uhat(2) - h2 * w2 * nhat(2)
  1083.     rightplane(3) = h2 * fovd * uhat(3) - h2 * w2 * nhat(3)
  1084.     mag = SQR(rightplane(1) * rightplane(1) + rightplane(2) * rightplane(2) + rightplane(3) * rightplane(3))
  1085.     rightplane(1) = rightplane(1) / mag
  1086.     rightplane(2) = rightplane(2) / mag
  1087.     rightplane(3) = rightplane(3) / mag
  1088.     leftplane(1) = -h2 * fovd * uhat(1) - h2 * w2 * nhat(1)
  1089.     leftplane(2) = -h2 * fovd * uhat(2) - h2 * w2 * nhat(2)
  1090.     leftplane(3) = -h2 * fovd * uhat(3) - h2 * w2 * nhat(3)
  1091.     mag = SQR(leftplane(1) * leftplane(1) + leftplane(2) * leftplane(2) + leftplane(3) * leftplane(3))
  1092.     leftplane(1) = leftplane(1) / mag
  1093.     leftplane(2) = leftplane(2) / mag
  1094.     leftplane(3) = leftplane(3) / mag
  1095.     topplane(1) = w2 * fovd * vhat(1) - h2 * w2 * nhat(1)
  1096.     topplane(2) = w2 * fovd * vhat(2) - h2 * w2 * nhat(2)
  1097.     topplane(3) = w2 * fovd * vhat(3) - h2 * w2 * nhat(3)
  1098.     mag = SQR(topplane(1) * topplane(1) + topplane(2) * topplane(2) + topplane(3) * topplane(3))
  1099.     topplane(1) = topplane(1) / mag
  1100.     topplane(2) = topplane(2) / mag
  1101.     topplane(3) = topplane(3) / mag
  1102.     bottomplane(1) = -w2 * fovd * vhat(1) - h2 * w2 * nhat(1)
  1103.     bottomplane(2) = -w2 * fovd * vhat(2) - h2 * w2 * nhat(2)
  1104.     bottomplane(3) = -w2 * fovd * vhat(3) - h2 * w2 * nhat(3)
  1105.     mag = SQR(bottomplane(1) * bottomplane(1) + bottomplane(2) * bottomplane(2) + bottomplane(3) * bottomplane(3))
  1106.     bottomplane(1) = bottomplane(1) / mag
  1107.     bottomplane(2) = bottomplane(2) / mag
  1108.     bottomplane(3) = bottomplane(3) / mag
  1109.  
  1110. SUB ProjectGroup (TheGroup AS LONG)
  1111.     DIM i AS LONG
  1112.     DIM f AS INTEGER
  1113.     DIM vectorinview AS INTEGER
  1114.     DIM vec3Ddotnhat AS DOUBLE
  1115.     FOR i = Group(TheGroup).FirstVector TO Group(TheGroup).LastVector
  1116.         vec(i, 1) = Group(TheGroup).Centroid.x + vec3Dpos(i, 1) - PlayerCamera.Position.x
  1117.         vec(i, 2) = Group(TheGroup).Centroid.y + vec3Dpos(i, 2) - PlayerCamera.Position.y
  1118.         vec(i, 3) = Group(TheGroup).Centroid.z + vec3Dpos(i, 3) - PlayerCamera.Position.z
  1119.         f = -1
  1120.         vec3Dvis(i) = 0
  1121.         vectorinview = 1
  1122.         IF vec(i, 1) * nearplane(1) + vec(i, 2) * nearplane(2) + vec(i, 3) * nearplane(3) - nearplane(4) < 0 THEN vectorinview = 0
  1123.         'IF vec(i, 1) * farplane(1) + vec(i, 2) * farplane(2) + vec(i, 3) * farplane(3) - farplane(4) < 0 THEN vectorinview = 0
  1124.         IF vec(i, 1) * farplane(1) + vec(i, 2) * farplane(2) + vec(i, 3) * farplane(3) - farplane(4) * .85 < 0 THEN f = 1
  1125.         'IF vec(i, 1) * rightplane(1) + vec(i, 2) * rightplane(2) + vec(i, 3) * rightplane(3) - rightplane(4) < 0 THEN vectorinview = 0
  1126.         'IF vec(i, 1) * leftplane(1) + vec(i, 2) * leftplane(2) + vec(i, 3) * leftplane(3) - leftplane(4) < 0 THEN vectorinview = 0
  1127.         'IF vec(i, 1) * topplane(1) + vec(i, 2) * topplane(2) + vec(i, 3) * topplane(3) - topplane(4) < 0 THEN vectorinview = 0
  1128.         'IF vec(i, 1) * bottomplane(1) + vec(i, 2) * bottomplane(2) + vec(i, 3) * bottomplane(3) - bottomplane(4) < 0 THEN vectorinview = 0
  1129.         IF (vectorinview = 1) THEN
  1130.             vec3Dvis(i) = 1
  1131.             vec3Ddotnhat = vec(i, 1) * nhat(1) + vec(i, 2) * nhat(2) + vec(i, 3) * nhat(3)
  1132.             vec2D(i, 1) = (vec(i, 1) * uhat(1) + vec(i, 2) * uhat(2) + vec(i, 3) * uhat(3)) * fovd / vec3Ddotnhat
  1133.             vec2D(i, 2) = (vec(i, 1) * vhat(1) + vec(i, 2) * vhat(2) + vec(i, 3) * vhat(3)) * fovd / vec3Ddotnhat
  1134.             IF (f = 1) THEN
  1135.                 vec2Dcolor(i) = Gray
  1136.             ELSE
  1137.                 vec2Dcolor(i) = vec3Dcolor(i)
  1138.             END IF
  1139.         END IF
  1140.     NEXT
  1141.  
  1142. ' TheDynamics
  1143.  
  1144. SUB ComputeVisibleScene
  1145.     DIM closestdist2 AS DOUBLE
  1146.     DIM fp42 AS DOUBLE
  1147.     DIM dist2 AS DOUBLE
  1148.     DIM dx AS DOUBLE
  1149.     DIM dy AS DOUBLE
  1150.     DIM dz AS DOUBLE
  1151.     DIM GroupInView AS INTEGER
  1152.     DIM k AS LONG
  1153.     DIM i AS LONG
  1154.     ClosestGroup = 1
  1155.     closestdist2 = 10000000
  1156.     fp42 = farplane(4) * farplane(4)
  1157.     FOR i = 1 TO ClusterIndexTicker
  1158.         dx = Cluster(i).Centroid.x - PlayerCamera.Position.x
  1159.         dy = Cluster(i).Centroid.y - PlayerCamera.Position.y
  1160.         dz = Cluster(i).Centroid.z - PlayerCamera.Position.z
  1161.         dist2 = dx * dx + dy * dy + dz * dz
  1162.         IF (dist2 > 600 * 600) THEN
  1163.             Cluster(i).Visible = 0
  1164.             IF (Cluster(i).MotionType > -1) THEN
  1165.                 CALL EvolveCluster(i)
  1166.             END IF
  1167.         ELSE
  1168.             Cluster(i).Visible = 1
  1169.             k = Cluster(i).FirstGroup
  1170.             IF ((Cluster(i).MotionType > -1) AND (ToggleAnimate = 1)) THEN
  1171.                 CALL EvolveCluster(i)
  1172.             END IF
  1173.             DO
  1174.                 dx = Group(k).Centroid.x - PlayerCamera.Position.x
  1175.                 dy = Group(k).Centroid.y - PlayerCamera.Position.y
  1176.                 dz = Group(k).Centroid.z - PlayerCamera.Position.z
  1177.                 dist2 = dx * dx + dy * dy + dz * dz
  1178.                 Group(k).Visible = 0
  1179.                 IF (dist2 < fp42) THEN
  1180.                     GroupInView = 1
  1181.                     IF dx * nearplane(1) + dy * nearplane(2) + dz * nearplane(3) - nearplane(4) < 0 THEN GroupInView = 0
  1182.                     'IF dx * farplane(1) + dy * farplane(2) + dz * farplane(3) - farplane(4) < 0 THEN groupinview = 0 ''' Redundant
  1183.                     IF dx * rightplane(1) + dy * rightplane(2) + dz * rightplane(3) - rightplane(4) < 0 THEN GroupInView = 0
  1184.                     IF dx * leftplane(1) + dy * leftplane(2) + dz * leftplane(3) - leftplane(4) < 0 THEN GroupInView = 0
  1185.                     IF dx * topplane(1) + dy * topplane(2) + dz * topplane(3) - topplane(4) < 0 THEN GroupInView = 0
  1186.                     IF dx * bottomplane(1) + dy * bottomplane(2) + dz * bottomplane(3) - bottomplane(4) < 0 THEN GroupInView = 0
  1187.                     IF (GroupInView = 1) THEN
  1188.                         Group(k).Visible = 1
  1189.                         IF (dist2 < closestdist2) THEN
  1190.                             closestdist2 = dist2
  1191.                             ClosestGroup = k
  1192.                         END IF
  1193.                         Group(k).Distance2 = dist2
  1194.                         IF (ToggleAnimate = 1) THEN CALL EvolveParticles(k)
  1195.                         CALL ProjectGroup(k)
  1196.                     END IF
  1197.                 END IF
  1198.                 IF (k = Cluster(i).LastGroup) THEN EXIT DO
  1199.                 k = Group(k).Pointer
  1200.             LOOP
  1201.         END IF
  1202.     NEXT
  1203.  
  1204. ' Graphics
  1205.  
  1206. SUB PlotWorld
  1207.     DIM a AS DOUBLE
  1208.     DIM b AS DOUBLE
  1209.     DIM c AS LONG
  1210.     DIM d AS LONG
  1211.     DIM i AS LONG
  1212.     DIM j AS INTEGER
  1213.     DIM k AS LONG
  1214.     DIM x1 AS DOUBLE
  1215.     DIM y1 AS DOUBLE
  1216.     DIM x2 AS DOUBLE
  1217.     DIM y2 AS DOUBLE
  1218.     DIM clrtmp AS _UNSIGNED LONG
  1219.     DIM SortedGroups(5000) AS LONG
  1220.     DIM SortedGroupsCount AS INTEGER
  1221.  
  1222.     NumClusterVisible = 0
  1223.     NumVectorVisible = 0
  1224.  
  1225.     SortedGroupsCount = 0
  1226.     FOR i = 1 TO ClusterIndexTicker
  1227.         IF (Cluster(i).Visible = 1) THEN
  1228.             NumClusterVisible = NumClusterVisible + 1
  1229.             k = Cluster(i).FirstGroup
  1230.             DO
  1231.                 IF (Group(k).Visible = 1) THEN
  1232.                     SortedGroupsCount = SortedGroupsCount + 1
  1233.                     SortedGroups(SortedGroupsCount) = k
  1234.                 END IF
  1235.                 IF (k = Cluster(i).LastGroup) THEN EXIT DO
  1236.                 k = Group(k).Pointer
  1237.             LOOP
  1238.         END IF
  1239.     NEXT
  1240.  
  1241.     NumGroupVisible = SortedGroupsCount
  1242.  
  1243.     ' Bubble sort might be a shitty choice but it works for now. Replace with quicksort prolly.
  1244.     FOR j = SortedGroupsCount TO 1 STEP -1
  1245.         FOR i = 2 TO SortedGroupsCount - 1
  1246.             a = Group(SortedGroups(i - 1)).Distance2
  1247.             b = Group(SortedGroups(i)).Distance2
  1248.             IF (a < b) THEN
  1249.                 c = SortedGroups(i - 1)
  1250.                 d = SortedGroups(i)
  1251.                 SortedGroups(i - 1) = d
  1252.                 SortedGroups(i) = c
  1253.             END IF
  1254.         NEXT
  1255.     NEXT
  1256.  
  1257.     CLS
  1258.     PlayerCamera.Shade = AverageShadeMix~&(vec3Dcolor(Group(ClosestGroup).FirstVector), PlayerCamera.Shade, .01)
  1259.     PAINT (_WIDTH / 2, _HEIGHT / 2), _RGBA(_RED32(PlayerCamera.Shade), _GREEN32(PlayerCamera.Shade), _BLUE32(PlayerCamera.Shade), 100)
  1260.     FOR j = 1 TO SortedGroupsCount
  1261.         k = SortedGroups(j)
  1262.         FOR i = Group(k).FirstVector TO Group(k).LastVector - 1
  1263.             IF (vec3Dvis(i) = 1) THEN
  1264.                 NumVectorVisible = NumVectorVisible + 1
  1265.                 IF (k = ClosestGroup) THEN
  1266.                     clrtmp = Yellow
  1267.                 ELSE
  1268.                     clrtmp = vec2Dcolor(i)
  1269.                 END IF
  1270.                 x1 = vec2D(i, 1)
  1271.                 y1 = vec2D(i, 2)
  1272.                 x2 = vec2D(i + 1, 1)
  1273.                 y2 = vec2D(i + 1, 2)
  1274.                 IF (((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)) < 225) THEN
  1275.                     CALL cline(x1, y1, x2, y2, clrtmp)
  1276.                 ELSE
  1277.                     CALL ccircle(x1, y1, 1, clrtmp)
  1278.                 END IF
  1279.             END IF
  1280.         NEXT
  1281.     NEXT
  1282.  
  1283. SUB DisplayHUD
  1284.     DIM a AS STRING
  1285.     DIM k AS INTEGER
  1286.     CALL cline(0, 0, 25 * (xhat(1) * uhat(1) + xhat(2) * uhat(2) + xhat(3) * uhat(3)), 25 * (xhat(1) * vhat(1) + xhat(2) * vhat(2) + xhat(3) * vhat(3)), _RGBA(255, 0, 0, 200))
  1287.     CALL cline(0, 0, 25 * (yhat(1) * uhat(1) + yhat(2) * uhat(2) + yhat(3) * uhat(3)), 25 * (yhat(1) * vhat(1) + yhat(2) * vhat(2) + yhat(3) * vhat(3)), _RGBA(0, 255, 0, 200))
  1288.     CALL cline(0, 0, 25 * (zhat(1) * uhat(1) + zhat(2) * uhat(2) + zhat(3) * uhat(3)), 25 * (zhat(1) * vhat(1) + zhat(2) * vhat(2) + zhat(3) * vhat(3)), _RGBA(30, 144, 255, 200))
  1289.     IF (ToggleHUD = 1) THEN
  1290.         COLOR LimeGreen
  1291.         _PRINTSTRING ((1) * 8, _HEIGHT - (10) * 16), " Position "
  1292.         COLOR Teal
  1293.         _PRINTSTRING ((1) * 8, _HEIGHT - (9) * 16), " x:       "
  1294.         _PRINTSTRING ((1) * 8, _HEIGHT - (8) * 16), " y:       "
  1295.         _PRINTSTRING ((1) * 8, _HEIGHT - (7) * 16), " z:       "
  1296.         _PRINTSTRING ((1) * 8, _HEIGHT - (9) * 16), " x: " + LTRIM$(RTRIM$(STR$(INT(PlayerCamera.Position.x))))
  1297.         _PRINTSTRING ((1) * 8, _HEIGHT - (8) * 16), " y: " + LTRIM$(RTRIM$(STR$(INT(PlayerCamera.Position.y))))
  1298.         _PRINTSTRING ((1) * 8, _HEIGHT - (7) * 16), " z: " + LTRIM$(RTRIM$(STR$(INT(PlayerCamera.Position.z))))
  1299.         COLOR LimeGreen
  1300.         _PRINTSTRING ((1) * 8, _HEIGHT - (6) * 16), " O: Reset "
  1301.         COLOR LimeGreen
  1302.         _PRINTSTRING ((1) * 8, _HEIGHT - (4) * 16), " Move  "
  1303.         COLOR DarkKhaki
  1304.         _PRINTSTRING ((1) * 8, _HEIGHT - (3) * 16), " q W e "
  1305.         _PRINTSTRING ((1) * 8, _HEIGHT - (2) * 16), " A S D "
  1306.         COLOR LimeGreen
  1307.         _PRINTSTRING ((1) * 8, (1) * 16), " Abilities "
  1308.         COLOR DarkKhaki
  1309.         _PRINTSTRING ((1) * 8, (2) * 16), "t = Pause  "
  1310.         _PRINTSTRING ((1) * 8, (3) * 16), "b = Create "
  1311.         _PRINTSTRING ((1) * 8, (4) * 16), "n = Explode"
  1312.         COLOR DarkGray
  1313.         _PRINTSTRING ((1) * 8, (5) * 16), "k = Delete "
  1314.         COLOR DarkKhaki
  1315.         _PRINTSTRING ((1) * 8, (6) * 16), "Esc = Quit "
  1316.         'COLOR LimeGreen
  1317.         '_PRINTSTRING ((1) * 8, (9) * 16), "View"
  1318.         'COLOR DarkKhaki
  1319.         '_PRINTSTRING ((1) * 8, (9) * 16), "FPS: " + LTRIM$(RTRIM$(STR$(FPSReport))) + "/30"
  1320.         '_PRINTSTRING ((1) * 8, (10) * 16), "Clusters: " + LTRIM$(RTRIM$(STR$(NumClusterVisible)))
  1321.         '_PRINTSTRING ((1) * 8, (11) * 16), "Groups: " + LTRIM$(RTRIM$(STR$(NumGroupVisible)))
  1322.         '_PRINTSTRING ((1) * 8, (12) * 16), "Particles: " + LTRIM$(RTRIM$(STR$(NumVectorVisible)))
  1323.         COLOR LimeGreen
  1324.         _PRINTSTRING (_WIDTH - (12) * 8, (1) * 16), " Locations "
  1325.         FOR k = 1 TO MissionTicker
  1326.             a = " " + Mission(k).Label + " "
  1327.             IF (Mission(k).Discovered = 0) THEN
  1328.                 COLOR DarkKhaki
  1329.             ELSE
  1330.                 COLOR DarkGray
  1331.             END IF
  1332.             _PRINTSTRING (_WIDTH - (LEN(a) + 1) * 8, (1 + k) * 16), a
  1333.         NEXT
  1334.         COLOR LimeGreen
  1335.         _PRINTSTRING (_WIDTH - (12) * 8, (k + 1) * 16), " M = Reset "
  1336.         COLOR LimeGreen
  1337.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (10) * 16), " Aim    "
  1338.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (9) * 16), " x      "
  1339.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (8) * 16), " y      "
  1340.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (7) * 16), " z      "
  1341.         COLOR Teal
  1342.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (9) * 16), " x: " + LTRIM$(RTRIM$(STR$(INT(-nhat(1) * 100))))
  1343.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (8) * 16), " y: " + LTRIM$(RTRIM$(STR$(INT(-nhat(2) * 100))))
  1344.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (7) * 16), " z: " + LTRIM$(RTRIM$(STR$(INT(-nhat(3) * 100))))
  1345.         COLOR LimeGreen
  1346.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (5) * 16), " Swivel "
  1347.         COLOR DarkKhaki
  1348.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (4) * 16), " 7 8 9  "
  1349.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (3) * 16), " 4 5 6  "
  1350.         _PRINTSTRING (_WIDTH - (9) * 8, _HEIGHT - (2) * 16), " 1 2 3  "
  1351.         a = " - Closest - "
  1352.         COLOR LimeGreen
  1353.         _PRINTSTRING (_WIDTH / 2 - (LEN(a) / 2) * 8, _HEIGHT - (3) * 16), a
  1354.         a = " SPACE = Hide "
  1355.         COLOR LimeGreen
  1356.         _PRINTSTRING (_WIDTH / 2 - (LEN(a) / 2) * 8, _HEIGHT - (1) * 16), a
  1357.     END IF
  1358.     a = " " + Group(ClosestGroup).GroupName + " "
  1359.     COLOR DarkKhaki
  1360.     _PRINTSTRING (_WIDTH / 2 - (LEN(a) / 2) * 8, _HEIGHT - (2) * 16), a
  1361.  
  1362. ' Interface
  1363.  
  1364. SUB KeyProcess
  1365.     IF (_KEYDOWN(87) = -1) OR (_KEYDOWN(119) = -1) OR (_KEYDOWN(18432) = -1) THEN ' W or w or uparrow
  1366.         CALL StrafeCameraNhatMinus
  1367.         IF (ToggleAnimate = 1) THEN
  1368.             PlayerCamera.Velocity.x = PlayerCamera.Velocity.x - .05 * nhat(1)
  1369.             PlayerCamera.Velocity.y = PlayerCamera.Velocity.y - .05 * nhat(2)
  1370.             PlayerCamera.Velocity.z = PlayerCamera.Velocity.z - .05 * nhat(3)
  1371.         END IF
  1372.     END IF
  1373.     IF (_KEYDOWN(83) = -1) OR (_KEYDOWN(115) = -1) OR (_KEYDOWN(20480) = -1) THEN ' S or s or downarrow
  1374.         CALL StrafeCameraNhatPlus
  1375.         IF (ToggleAnimate = 1) THEN
  1376.             PlayerCamera.Velocity.x = PlayerCamera.Velocity.x + .05 * nhat(1)
  1377.             PlayerCamera.Velocity.y = PlayerCamera.Velocity.y + .05 * nhat(2)
  1378.             PlayerCamera.Velocity.z = PlayerCamera.Velocity.z + .05 * nhat(3)
  1379.         END IF
  1380.     END IF
  1381.     IF (_KEYDOWN(65) = -1) OR (_KEYDOWN(97) = -1) THEN ' A or a
  1382.         CALL StrafeCameraUhatMinus
  1383.         IF (ToggleAnimate = 1) THEN
  1384.             PlayerCamera.Velocity.x = PlayerCamera.Velocity.x - .05 * uhat(1)
  1385.             PlayerCamera.Velocity.y = PlayerCamera.Velocity.y - .05 * uhat(2)
  1386.             PlayerCamera.Velocity.z = PlayerCamera.Velocity.z - .05 * uhat(3)
  1387.         END IF
  1388.     END IF
  1389.     IF (_KEYDOWN(68) = -1) OR (_KEYDOWN(100) = -1) THEN ' D or d
  1390.         CALL StrafeCameraUhatPlus
  1391.         IF (ToggleAnimate = 1) THEN
  1392.             PlayerCamera.Velocity.x = PlayerCamera.Velocity.x + .05 * uhat(1)
  1393.             PlayerCamera.Velocity.y = PlayerCamera.Velocity.y + .05 * uhat(2)
  1394.             PlayerCamera.Velocity.z = PlayerCamera.Velocity.z + .05 * uhat(3)
  1395.         END IF
  1396.     END IF
  1397.     IF (_KEYDOWN(81) = -1) OR (_KEYDOWN(113) = -1) THEN ' Q or q
  1398.         CALL StrafeCameraVhatMinus
  1399.         IF (ToggleAnimate = 1) THEN
  1400.             PlayerCamera.Velocity.x = PlayerCamera.Velocity.x - .05 * vhat(1)
  1401.             PlayerCamera.Velocity.y = PlayerCamera.Velocity.y - .05 * vhat(2)
  1402.             PlayerCamera.Velocity.z = PlayerCamera.Velocity.z - .05 * vhat(3)
  1403.         END IF
  1404.     END IF
  1405.     IF (_KEYDOWN(69) = -1) OR (_KEYDOWN(101) = -1) THEN ' E or e
  1406.         CALL StrafeCameraVhatPlus
  1407.         IF (ToggleAnimate = 1) THEN
  1408.             PlayerCamera.Velocity.x = PlayerCamera.Velocity.x + .05 * vhat(1)
  1409.             PlayerCamera.Velocity.y = PlayerCamera.Velocity.y + .05 * vhat(2)
  1410.             PlayerCamera.Velocity.z = PlayerCamera.Velocity.z + .05 * vhat(3)
  1411.         END IF
  1412.     END IF
  1413.     IF (_KEYDOWN(19200) = -1) OR (_KEYDOWN(52) = -1) THEN CALL RotateUhatMinus: CALL CalculateScreenVectors ' 4
  1414.     IF (_KEYDOWN(19712) = -1) OR (_KEYDOWN(54) = -1) THEN CALL RotateUhatPlus: CALL CalculateScreenVectors ' 6
  1415.     IF (_KEYDOWN(56) = -1) THEN CALL RotateVhatPlus: CALL CalculateScreenVectors ' 8
  1416.     IF (_KEYDOWN(50) = -1) THEN CALL RotateVhatMinus: CALL CalculateScreenVectors ' 2
  1417.     IF (_KEYDOWN(55) = -1) THEN CALL RotateClockwise ' 7
  1418.     IF (_KEYDOWN(57) = -1) THEN CALL RotateCounterclockwise ' 9
  1419.     IF (_KEYDOWN(49) = -1) THEN CALL RotateUhatMinus: CALL CalculateScreenVectors: CALL RotateClockwise ' 1
  1420.     IF (_KEYDOWN(51) = -1) THEN CALL RotateUhatPlus: CALL CalculateScreenVectors: CALL RotateCounterclockwise ' 3
  1421.     DIM k AS LONG
  1422.     DIM p AS LONG
  1423.     DIM l AS LONG
  1424.     DIM kh AS INTEGER
  1425.     kh = _KEYHIT
  1426.     IF (kh <> 0) THEN
  1427.         SELECT CASE kh
  1428.             CASE 27
  1429.                 SYSTEM
  1430.             CASE ASC(" ")
  1431.                 ToggleHUD = -ToggleHUD
  1432.             CASE ASC("b"), ASC("B")
  1433.                 DIM gtmp AS LONG
  1434.                 gtmp = NewCube&(1, "Custom block", 350, PlayerCamera.Position.x - 40 * nhat(1), PlayerCamera.Position.y - 40 * nhat(2), PlayerCamera.Position.z - 40 * nhat(3), 10, 10, 10, Lime, Purple, Teal, 0)
  1435.                 CALL ClusterPinch(gtmp)
  1436.             CASE ASC("k"), ASC("K")
  1437.                 ' set -999? delete actual vectors?
  1438.                 p = Group(ClosestGroup).Pointer
  1439.                 l = Group(ClosestGroup).Lagger
  1440.                 Group(l).Pointer = p
  1441.                 IF (p <> -999) THEN
  1442.                     Group(p).Lagger = l
  1443.                 END IF
  1444.             CASE ASC("m"), ASC("M")
  1445.                 CALL CreateMission(10)
  1446.             CASE ASC("n"), ASC("N")
  1447.                 FOR k = Group(ClosestGroup).FirstVector TO Group(ClosestGroup).LastVector
  1448.                     vec3Dvel(k, 1) = (RND - .5) * .20
  1449.                     vec3Dvel(k, 2) = (RND - .5) * .20
  1450.                     vec3Dvel(k, 3) = (RND - .5) * .20
  1451.                 NEXT
  1452.             CASE ASC("o"), ASC("O")
  1453.                 PlayerCamera.Position.x = -40
  1454.                 PlayerCamera.Position.y = 30
  1455.                 PlayerCamera.Position.z = 40
  1456.                 uhat(1) = -.2078192: uhat(2) = -.9781672: uhat(3) = 0
  1457.                 vhat(1) = 0: vhat(2) = 0: vhat(3) = 1
  1458.                 CALL CalculateScreenVectors
  1459.             CASE ASC("t"), ASC("T")
  1460.                 ToggleAnimate = -ToggleAnimate
  1461.         END SELECT
  1462.     END IF
  1463.     _KEYCLEAR
  1464.  
  1465. ' Color utility.
  1466.  
  1467. FUNCTION Gradient~& (Shade1 AS _UNSIGNED LONG, Shade2 AS _UNSIGNED LONG, param AS DOUBLE)
  1468.     Gradient~& = _RGB32((1 - param) * _RED32(Shade1) + param * _RED32(Shade2), (1 - param) * _GREEN32(Shade1) + param * _GREEN32(Shade2), (1 - param) * _BLUE32(Shade1) + param * _BLUE32(Shade2))
  1469.  
  1470. FUNCTION AverageShadeMix~& (Shade1 AS _UNSIGNED LONG, Shade2 AS _UNSIGNED LONG, Weight AS DOUBLE)
  1471.     AverageShadeMix~& = _RGB32(Weight * _RED32(Shade1) + (1 - Weight) * _RED32(Shade2), Weight * _GREEN32(Shade1) + (1 - Weight) * _GREEN32(Shade2), Weight * _BLUE32(Shade1) + (1 - Weight) * _BLUE32(Shade2))
  1472.  
  1473. ' Cartesian plotting.
  1474.  
  1475. SUB cline (x1 AS DOUBLE, y1 AS DOUBLE, x2 AS DOUBLE, y2 AS DOUBLE, col AS _UNSIGNED LONG)
  1476.     LINE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2)-(_WIDTH / 2 + x2, -y2 + _HEIGHT / 2), col
  1477.  
  1478. SUB ccircle (x1 AS DOUBLE, y1 AS DOUBLE, rad AS DOUBLE, col AS _UNSIGNED LONG)
  1479.     CIRCLE (_WIDTH / 2 + x1, -y1 + _HEIGHT / 2), rad, col
  1480.  
  1481. ' Camera transformation
  1482.  
  1483. SUB PlayerDynamics
  1484.  
  1485.     DIM k AS INTEGER
  1486.  
  1487.     IF (ToggleAnimate = 1) THEN
  1488.         PlayerCamera.Velocity.x = .95 * PlayerCamera.Velocity.x
  1489.         PlayerCamera.Velocity.y = .95 * PlayerCamera.Velocity.y
  1490.         PlayerCamera.Velocity.z = .95 * PlayerCamera.Velocity.z
  1491.         PlayerCamera.Position.x = PlayerCamera.Position.x + PlayerCamera.Velocity.x
  1492.         PlayerCamera.Position.y = PlayerCamera.Position.y + PlayerCamera.Velocity.y
  1493.         PlayerCamera.Position.z = PlayerCamera.Position.z + PlayerCamera.Velocity.z
  1494.     END IF
  1495.  
  1496.     FOR k = 1 TO MissionTicker
  1497.         IF (Group(ClosestGroup).GroupName = Mission(k).Label) THEN
  1498.             IF (Mission(k).Discovered = 0) THEN
  1499.                 Mission(k).Discovered = 1
  1500.                 SOUND 500, .25
  1501.             END IF
  1502.         END IF
  1503.     NEXT
  1504.  
  1505. SUB RotateUhatPlus
  1506.     uhat(1) = uhat(1) + nhat(1) * .0333
  1507.     uhat(2) = uhat(2) + nhat(2) * .0333
  1508.     uhat(3) = uhat(3) + nhat(3) * .0333
  1509.  
  1510. SUB RotateUhatMinus
  1511.     uhat(1) = uhat(1) - nhat(1) * .0333
  1512.     uhat(2) = uhat(2) - nhat(2) * .0333
  1513.     uhat(3) = uhat(3) - nhat(3) * .0333
  1514.  
  1515. SUB RotateVhatPlus
  1516.     vhat(1) = vhat(1) + nhat(1) * .0333
  1517.     vhat(2) = vhat(2) + nhat(2) * .0333
  1518.     vhat(3) = vhat(3) + nhat(3) * .0333
  1519.  
  1520. SUB RotateVhatMinus
  1521.     vhat(1) = vhat(1) - nhat(1) * .0333
  1522.     vhat(2) = vhat(2) - nhat(2) * .0333
  1523.     vhat(3) = vhat(3) - nhat(3) * .0333
  1524.  
  1525. SUB RotateCounterclockwise
  1526.     DIM v1, v2, v3 AS DOUBLE
  1527.     v1 = vhat(1)
  1528.     v2 = vhat(2)
  1529.     v3 = vhat(3)
  1530.     vhat(1) = v1 + uhat(1) * .0333
  1531.     vhat(2) = v2 + uhat(2) * .0333
  1532.     vhat(3) = v3 + uhat(3) * .0333
  1533.     uhat(1) = uhat(1) - v1 * .0333
  1534.     uhat(2) = uhat(2) - v2 * .0333
  1535.     uhat(3) = uhat(3) - v3 * .0333
  1536.  
  1537. SUB RotateClockwise
  1538.     DIM v1, v2, v3 AS DOUBLE
  1539.     v1 = vhat(1)
  1540.     v2 = vhat(2)
  1541.     v3 = vhat(3)
  1542.     vhat(1) = v1 - uhat(1) * .0333
  1543.     vhat(2) = v2 - uhat(2) * .0333
  1544.     vhat(3) = v3 - uhat(3) * .0333
  1545.     uhat(1) = uhat(1) + v1 * .0333
  1546.     uhat(2) = uhat(2) + v2 * .0333
  1547.     uhat(3) = uhat(3) + v3 * .0333
  1548.  
  1549. SUB StrafeCameraUhatPlus
  1550.     PlayerCamera.Position.x = PlayerCamera.Position.x + uhat(1)
  1551.     PlayerCamera.Position.y = PlayerCamera.Position.y + uhat(2)
  1552.     PlayerCamera.Position.z = PlayerCamera.Position.z + uhat(3)
  1553.  
  1554. SUB StrafeCameraUhatMinus
  1555.     PlayerCamera.Position.x = PlayerCamera.Position.x - uhat(1)
  1556.     PlayerCamera.Position.y = PlayerCamera.Position.y - uhat(2)
  1557.     PlayerCamera.Position.z = PlayerCamera.Position.z - uhat(3)
  1558.  
  1559. SUB StrafeCameraVhatPlus
  1560.     PlayerCamera.Position.x = PlayerCamera.Position.x + vhat(1)
  1561.     PlayerCamera.Position.y = PlayerCamera.Position.y + vhat(2)
  1562.     PlayerCamera.Position.z = PlayerCamera.Position.z + vhat(3)
  1563.  
  1564. SUB StrafeCameraVhatMinus
  1565.     PlayerCamera.Position.x = PlayerCamera.Position.x - vhat(1)
  1566.     PlayerCamera.Position.y = PlayerCamera.Position.y - vhat(2)
  1567.     PlayerCamera.Position.z = PlayerCamera.Position.z - vhat(3)
  1568.  
  1569. SUB StrafeCameraNhatPlus
  1570.     PlayerCamera.Position.x = PlayerCamera.Position.x + nhat(1)
  1571.     PlayerCamera.Position.y = PlayerCamera.Position.y + nhat(2)
  1572.     PlayerCamera.Position.z = PlayerCamera.Position.z + nhat(3)
  1573.  
  1574. SUB StrafeCameraNhatMinus
  1575.     PlayerCamera.Position.x = PlayerCamera.Position.x - nhat(1)
  1576.     PlayerCamera.Position.y = PlayerCamera.Position.y - nhat(2)
  1577.     PlayerCamera.Position.z = PlayerCamera.Position.z - nhat(3)
You're not done when it works, you're done when it's right.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Sanctum: major overhaul to particle world
« Reply #1 on: October 04, 2020, 06:01:06 pm »
Well I discovered a pyramid!
 
Pyramid making particles.PNG


Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Sanctum: major overhaul to particle world
« Reply #2 on: October 04, 2020, 08:58:09 pm »
Wow, that's neat!
Shuwatch!

FellippeHeitor

  • Guest
Re: Sanctum: major overhaul to particle world
« Reply #3 on: October 04, 2020, 09:54:00 pm »
You never fail to impress me with Sanctum. It's mind-blowing how much is packed into less than 2k lines. I'm blown away.

Also, with every reset there are new things to see?

BTW, been to hell and back.

Offline Richard Frost

  • Seasoned Forum Regular
  • Posts: 316
  • Needle nardle noo. - Peter Sellers
    • View Profile
Re: Sanctum: major overhaul to particle world
« Reply #4 on: October 04, 2020, 10:54:52 pm »
The gradual stop is a neat effect.  I believe it's faster than a previous
version I tried.

What I like most is the coding style - meaningful variable names, single blank
lines for spacing, and all the hats (new, unusual, vampire) in KeyProcess and
elsewhere.

Doubt I'll ever understand all the fancy math.
It works better if you plug it in.

Offline SierraKen

  • Forum Resident
  • Posts: 1454
    • View Profile
Re: Sanctum: major overhaul to particle world
« Reply #5 on: October 06, 2020, 03:26:59 pm »
This is awesome Static! I found a giant graveyard.