Author Topic: Sanctum 2021  (Read 2299 times)

0 Members and 1 Guest are viewing this topic.

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
Sanctum 2021
« on: November 09, 2021, 08:13:14 pm »
Hello all,

I recently ran the code for Sanctum (various versions floating around on forums) and realized my local copy is a whole lot newer, so it's time for an update post on this. I also made the mistake of fiddling with the code a few days ago, and of course fell into the abyss, and had to take the code in a new direction to find my way out. I may or may not keep (all of) the results, but I figured it would be worth sharing what exists now.

For those who remember more recent versions of this program, everything is now gone. The land, the trees, the air, (I sound like Churchill right now), the snowmen, all the planets, the black hole, the different layers of ground and hell, all gone gone gone. ASCII plotting, gone.

Here is some of what's going on:

Terrain model:
* Terrain is no longer flat, but is procedurally generated (same seed creates same map).
* Created by choosing pseudo-random extreme points and using relaxation algorithm (to basically solve Laplace's equation treating peaks as charges).
* Contains several pre-flattened plateaus for specialized use (nothing specific implemented yet).

Terrain volume:
* Surface supported by layer of dirt and sand.
* Low spots are filled with water.

Terrain surface:
* Surface features generated and relaxed in analogy to terrain model.
* Elevation-specific coloring.
* Elevation-specific plant-life: Parametric clover, Fractal fern, Grass

Sun and Moon:
* As an experimental feature, the sun and moon are always visible if above the horizon. (There is at least one GOTO in the code because of this. It won't last.)
* When observed form below water level, the sun and moon lose their color.
* The sun/moon phases are loosely tied to your system clock.
* If you're lucky, you'll see a solar eclipse.

Weather:
* Clouds occur in different stratified layers. Different speeds, heights, etc.
* Rain falls from randomly-chosen clouds.
* Weather patterns move and adjust height to pass over mountains, etc.
* Tornadoes will mess you up.

Fish:
* If you look in the deepest water, toward the middle, you will see fish.
* Each view of a fish is actually trimmed from what is best described as a three-dimensional sprite sheet.
* At a given moment, the view of the fish is determined by its velocity.
*** This successful proof-of-concept demonstrates motion of asymmetric things like birds, planes, etc. No longer limited to spheres and UFOs.

Movement/controls:
* Player follows the main terrain mesh while walking.
* Recommended "posture" is WSAD plus Numpad. (Mouse is disabled but works if you find and uncomment the code.)
* Use the Z key to zoom, as if using a telescope. Thought this feature was cute, will probably keep it.
* Press F to throw a breakable thing. Looks like a potion I guess.

Very crude building system:
* Press B to spawn a block. The block will fall to the ground, crudely.
* Press B again to stack a block on top of another block.
* Make stairs or primitive towers this way.
* You can stand blocks like they're part of the terrain.
* Super primitive, no need to tell me about floating blocks.

As usual, this project is perpetually unfinished. This is all done using LINE and CIRCLE, and maybe I've done *all* I can in this area without going the way of textures and the like. Let me know what kind of FPS you get. (I stay around 60, which is the target.)

Code: QB64: [Select]
  1.  
  2. _Title "Sanctum 2021"
  3.  
  4. '$ExeIcon:'sanctum.ico'
  5.  
  6. ' Hardware.
  7.  
  8. 'Screen _NewImage(640, 480, 32)
  9. Screen _NewImage(800, 600, 32)
  10. 'Screen _NewImage(1024, 768, 32)
  11.  
  12.  
  13. ' Performance.
  14. Dim Shared As Integer FPSTarget
  15. FPSTarget = 60
  16.  
  17. ' Color constants.
  18. Const Aquamarine = _RGB32(127, 255, 212)
  19. Const Black = _RGB32(0, 0, 0)
  20. Const Blue = _RGB32(0, 0, 255)
  21. Const BlueViolet = _RGB32(138, 43, 226)
  22. Const Chocolate = _RGB32(210, 105, 30)
  23. Const Cyan = _RGB32(0, 255, 255)
  24. Const DarkBlue = _RGB32(0, 0, 139)
  25. Const DarkGoldenRod = _RGB32(184, 134, 11)
  26. Const DarkGray = _RGB32(169, 169, 169)
  27. Const DarkKhaki = _RGB32(189, 183, 107)
  28. Const DeepPink = _RGB32(255, 20, 147)
  29. Const DodgerBlue = _RGB32(30, 144, 255)
  30. Const ForestGreen = _RGB32(34, 139, 34)
  31. Const Gray = _RGB32(128, 128, 128)
  32. Const Green = _RGB32(0, 128, 0)
  33. Const Indigo = _RGB32(75, 0, 130)
  34. Const Ivory = _RGB32(255, 255, 240)
  35. Const LightSeaGreen = _RGB32(32, 178, 170)
  36. Const Lime = _RGB32(0, 255, 0)
  37. Const LimeGreen = _RGB32(50, 205, 50)
  38. Const Magenta = _RGB32(255, 0, 255)
  39. Const PaleGoldenRod = _RGB32(238, 232, 170)
  40. Const Purple = _RGB32(128, 0, 128)
  41. Const Red = _RGB32(255, 0, 0)
  42. Const RoyalBlue = _RGB32(65, 105, 225)
  43. Const SaddleBrown = _RGB32(139, 69, 19)
  44. Const Sienna = _RGB32(160, 82, 45)
  45. Const SlateGray = _RGB32(112, 128, 144)
  46. Const Snow = _RGB32(200, 200, 200) '''(255, 250, 250)
  47. Const Sunglow = _RGB32(255, 207, 72)
  48. Const SunsetOrange = _RGB32(253, 94, 83)
  49. Const Teal = _RGB32(0, 128, 128)
  50. Const White = _RGB32(255, 255, 255)
  51. Const Yellow = _RGB32(255, 255, 0)
  52.  
  53. ' Mathematical constants.
  54. Const pi = 4 * Atn(1)
  55. Const ee = Exp(1)
  56.  
  57. ' Divine numbers.
  58. Dim Shared bignumber As Long
  59. Dim Shared WorldSeed As Long
  60. bignumber = 10 ^ 7
  61. WorldSeed = 3 'Int(Timer)
  62.  
  63. ' Fundamental types.
  64.  
  65. Type Vector3
  66.     x As Double
  67.     y As Double
  68.     z As Double
  69.  
  70. Type Vector2
  71.     u As Double
  72.     v As Double
  73.  
  74. Type Camera
  75.     Position As Vector3
  76.     Velocity As Vector3
  77.     Acceleration As Vector3
  78.     Shade As _Unsigned Long
  79.  
  80. Type GroupElement
  81.     Identity As Long
  82.     Label As String
  83.     Pointer As Long
  84.     Lagger As Long
  85.     Volume As Vector3
  86.     FirstVector As Long
  87.     LastVector As Long
  88.     Centroid As Vector3
  89.     Velocity As Vector3
  90.     Visible As Integer
  91.     Distance2 As Double
  92.     PlotMode As Integer
  93.     FrameLength As Long
  94.     ActiveFrame As Integer
  95.  
  96. Type ClusterElement
  97.     Identity As Long
  98.     Pointer As Long
  99.     Lagger As Long
  100.     FirstGroup As Long
  101.     LastGroup As Long
  102.     Centroid As Vector3
  103.     Velocity As Vector3
  104.     Acceleration As Vector3
  105.     Visible As Integer
  106.     MotionType As Integer
  107.     Framed As Integer
  108.     DeathTimer As Long
  109.  
  110. ' World-specific types.
  111.  
  112. Type StrataElement
  113.     Height As Double
  114.     Label As String
  115.     Shade As _Unsigned Long
  116.  
  117. Type PlateauElement
  118.     Location As Vector3
  119.     Radius As Double
  120.  
  121. ' Vectors to specify points.
  122. Dim Shared vec3Dpos(bignumber) As Vector3 ' Absolute position
  123. Dim Shared vec3Dvel(UBound(vec3Dpos)) As Vector3 ' Linear velocity
  124. Dim Shared vec3Dvis(UBound(vec3Dpos)) As Integer ' Visibility toggle
  125. Dim Shared vec2D(UBound(vec3Dpos)) As Vector2 ' Projection onto 2D plane
  126. Dim Shared vec3Dcolor(UBound(vec3Dpos)) As Long ' Original color
  127. Dim Shared vec2Dcolor(UBound(vec3Dpos)) As Long ' Projected color
  128.  
  129. ' A collection of vectors is a Group.
  130. Dim Shared Group(UBound(vec3Dpos) / 10) As GroupElement
  131. Dim Shared GroupIdTicker As Long
  132. GroupIdTicker = 0
  133.  
  134. ' Groups will eventually be sorted based on distance^2.
  135. Dim Shared SortedGroups(1000) As Long
  136. Dim Shared SortedGroupsCount As Integer
  137.  
  138. ' A collection of groups is a Cluster.
  139. Dim Shared ClusterIdTicker As Long
  140. Dim Shared ClusterFillCounter As Integer
  141. Dim Shared Cluster(UBound(Group) / 10) As ClusterElement
  142. ClusterIdTicker = 0
  143. ClusterFillCounter = 0
  144.  
  145. ' Main terrain setup. This is a surface z=f(x,y).
  146. Dim Shared WorldMesh(180, 180) As Double
  147. Dim Shared WorldMeshAddress(UBound(WorldMesh, 1), UBound(WorldMesh, 2)) As Long
  148.  
  149. ' Terrain elements are formally groups whose size and density are specified here.
  150. Dim Shared BlockSize As Integer
  151. Dim Shared BlockStep As Integer
  152. BlockSize = 40
  153. BlockStep = Int(BlockSize / 8)
  154.  
  155. ' World features.
  156. Dim Shared Strata(5) As StrataElement
  157. Dim Shared CloudLayer(5) As StrataElement
  158. Dim Shared Plateau(5) As PlateauElement
  159. Dim Shared SunClusterAddress As Long
  160. Dim Shared MoonClusterAddress As Long
  161.  
  162. ' Fixed paths. Primary ticks are one per second, with total cycle of one day.
  163. Dim Shared FixedPath(500, 86400) As Vector3
  164. Dim Shared FixedPathIndexTicker As Long
  165. FixedPathIndexTicker = 0
  166.  
  167. ' Three-space basis vectors.
  168. Dim Shared As Double xhat(3), yhat(3), zhat(3)
  169. xhat(1) = 1: xhat(2) = 0: xhat(3) = 0
  170. yhat(1) = 0: yhat(2) = 1: yhat(3) = 0
  171. zhat(1) = 0: zhat(2) = 0: zhat(3) = 1
  172.  
  173. ' Camera orientation vectors.
  174. Dim Shared As Double uhat(3), vhat(3), nhat(3)
  175.  
  176. ' Camera position.
  177. Dim Shared PlayerCamera As Camera
  178.  
  179. ' Field-of-view distance.
  180. fovd = -192
  181.  
  182. ' Clipping planes.
  183. Dim Shared As Double nearplane(4), farplane(4), rightplane(4), leftplane(4), topplane(4), bottomplane(4)
  184. nearplane(4) = 1
  185. farplane(4) = -256
  186. rightplane(4) = -BlockSize / 2
  187. leftplane(4) = -BlockSize / 2
  188. topplane(4) = -BlockSize / 2
  189. bottomplane(4) = -BlockSize / 2
  190.  
  191. ' Temporary counters.
  192. Dim Shared NumClusterVisible As Long
  193. Dim Shared NumVectorVisible As Long
  194. Dim Shared NumGroupVisible As Long
  195.  
  196. ' Interface.
  197. Dim Shared ToggleAnimate As Integer
  198. Dim Shared FPSReport As Integer
  199. Dim Shared ClosestGroup As Long
  200.  
  201. ' Prime and start main loop.
  202. Randomize WorldSeed
  203. Call InitWorld
  204. Call CreateWorld
  205. Call InitCamera
  206. Call MainLoop
  207.  
  208. ' Subs and Functions
  209.  
  210. Sub MainLoop
  211.     Dim fps As Integer
  212.     Dim fpstimer As Long
  213.     Dim tt As Long
  214.     fps = 0
  215.     fpstimer = Int(Timer)
  216.  
  217.     Do
  218.         Call PlayerDynamics
  219.         Call ComputeVisibleScene
  220.         Call PlotWorld
  221.         Call DisplayHUD
  222.         Call DisplayMiniMap
  223.         Call KeyDownProcess
  224.         Call KeyHitProcess
  225.  
  226.         fps = fps + 1
  227.         tt = Timer
  228.         If (tt = fpstimer + 1) Then
  229.             fpstimer = tt
  230.             FPSReport = fps
  231.             fps = 0
  232.         End If
  233.  
  234.         _Display
  235.         _Limit FPSTarget + 1
  236.     Loop
  237.  
  238. Sub InitWorld
  239.     Dim k As Integer
  240.     Dim As Double u, v, w
  241.  
  242.     k = 0
  243.     k = k + 1: Strata(k).Height = -50: Strata(k).Label = "Water": Strata(k).Shade = RoyalBlue
  244.     k = k + 1: Strata(k).Height = 0: Strata(k).Label = "Meadow": Strata(k).Shade = ForestGreen
  245.     k = k + 1: Strata(k).Height = 50: Strata(k).Label = "Grassland": Strata(k).Shade = DarkKhaki
  246.     k = k + 1: Strata(k).Height = 100: Strata(k).Label = "Rocky Terrain": Strata(k).Shade = DarkGoldenRod
  247.     k = k + 1: Strata(k).Height = 150: Strata(k).Label = "Snowy Terrain": Strata(k).Shade = White
  248.  
  249.     k = 0
  250.     k = k + 1: CloudLayer(k).Height = 140: CloudLayer(k).Label = "Dark Cloud": CloudLayer(k).Shade = SlateGray
  251.     k = k + 1: CloudLayer(k).Height = 160: CloudLayer(k).Label = "Gray Cloud": CloudLayer(k).Shade = Gray
  252.     k = k + 1: CloudLayer(k).Height = 180: CloudLayer(k).Label = "Azul Cloud": CloudLayer(k).Shade = DarkBlue
  253.     k = k + 1: CloudLayer(k).Height = 200: CloudLayer(k).Label = "Heavy Cloud": CloudLayer(k).Shade = Snow
  254.     k = k + 1: CloudLayer(k).Height = 220: CloudLayer(k).Label = "Icy Cloud": CloudLayer(k).Shade = Ivory
  255.  
  256.     u = Rnd * 2 * pi
  257.     w = Sqr((UBound(WorldMesh, 1) / 2) ^ 2 + (UBound(WorldMesh, 2) / 2) ^ 2)
  258.     For k = 1 To UBound(Plateau)
  259.         Select Case k
  260.             Case 1 ' Water
  261.                 u = u + pi / 2
  262.                 v = (w / 2) * (.8 + Rnd * .5)
  263.                 Plateau(k).Location.x = Int(v * Cos(u))
  264.                 Plateau(k).Location.y = Int(v * Sin(u))
  265.                 Plateau(k).Location.z = -250
  266.             Case 2 ' Meadow
  267.                 Plateau(k).Location.x = 0
  268.                 Plateau(k).Location.y = 0
  269.                 Plateau(k).Location.z = Strata(k).Height
  270.             Case Else
  271.                 u = u + pi / 2
  272.                 v = (w / 2) * (.8 + Rnd * .5)
  273.                 Plateau(k).Location.x = Int(v * Cos(u))
  274.                 Plateau(k).Location.y = Int(v * Sin(u))
  275.                 Plateau(k).Location.z = Strata(k).Height
  276.         End Select
  277.         Plateau(k).Radius = 15
  278.     Next
  279.  
  280. Sub CreateWorld
  281.     Dim g As Long
  282.     Dim k As Integer
  283.     ' Initialize and populate list.
  284.     k = 0
  285.     k = k + 1: Call TextCenter(".:. Let there be light .:.", k * 16, DarkKhaki)
  286.     k = k + 1: Call TextCenter("(Initialize linked list)", k * 16, ForestGreen)
  287.     g = NewGroup&(0, 0, 0, 0, 1, 0, 0)
  288.     k = k + 2: Call TextCenter(".:. Let there be a firmament .:.", k * 16, DarkKhaki)
  289.     k = k + 1: Call TextCenter("(Using seed " + LTrim$(RTrim$(Str$(WorldSeed))) + ")", k * 16, ForestGreen)
  290.     k = k + 1: Call TextCenter("(Generate random terrain)", k * 16, ForestGreen)
  291.     g = CreateTerrainGroups&(g)
  292.     k = k + 2: Call TextCenter(".:. Let the dry land appear; bring forth the grass .:.", k * 16, DarkKhaki)
  293.     k = k + 1: Call TextCenter("(Relax terrain mesh)", k * 16, ForestGreen)
  294.     k = k + 1: Call TextCenter("(Fill terrain volumes)", k * 16, ForestGreen)
  295.     k = k + 1: Call TextCenter("(Cover terrain surfaces)", k * 16, ForestGreen)
  296.     g = CreateTerrainVectors&(g)
  297.     g = CreateTerrainVolume&(g)
  298.     g = CreateClover&(g)
  299.     g = CreateFern&(g)
  300.     g = CreateGrass&(g)
  301.     k = k + 2: Call TextCenter(".:. Divide the day from the night .:.", k * 16, DarkKhaki)
  302.     k = k + 1: Call TextCenter("(Create celestial objects)", k * 16, ForestGreen)
  303.     k = k + 1: Call TextCenter("(Create weather events)", k * 16, ForestGreen)
  304.     g = CreateSun&(g)
  305.     g = CreateMoon&(g)
  306.     g = CreateTornado&(g)
  307.     g = CreateWeather&(g)
  308.     k = k + 2: Call TextCenter(".:. Let waters bring forth .:.", k * 16, DarkKhaki)
  309.     k = k + 1: Call TextCenter("(Create fish)", k * 16, ForestGreen)
  310.     g = CreateFish&(g)
  311.     k = k + 2: Call TextCenter(".:. Let us make man .:.", k * 16, DarkKhaki)
  312.     k = k + 1: Call TextCenter("(Initialize player)", k * 16, ForestGreen)
  313.     k = k + 2: Call TextCenter(".:. ...blessed the seventh day and Sanctified it .:.", k * 16, DarkKhaki)
  314.     k = k + 1: Call TextCenter("(Rest)", k * 16, ForestGreen)
  315.     k = k + 3: Call TextCenter("PRESS ANY KEY", k * 16, Sunglow)
  316.     Sleep
  317.     _KeyClear
  318.  
  319. ' High-order clusters and groups.
  320.  
  321. Function CreateTerrainGroups& (LagAddressIn As Long)
  322.     Dim g As Long
  323.     Dim As Integer i, j, k
  324.     Dim As Integer ii, jj
  325.     Dim As Double u, v, w
  326.     g = LagAddressIn
  327.  
  328.     ' Create world mesh and set extreme points.
  329.     Dim tempworldmesh1(UBound(WorldMesh, 1), UBound(WorldMesh, 2))
  330.     Dim tempworldmesh2(UBound(WorldMesh, 1), UBound(WorldMesh, 2), 2)
  331.  
  332.     u = 1 + .5 * (Rnd - .5)
  333.     v = 1 + .5 * (Rnd - .5)
  334.     For i = 1 To UBound(WorldMesh, 1)
  335.         For j = 1 To UBound(WorldMesh, 2)
  336.  
  337.             ' Overall slant of world.
  338.             tempworldmesh2(i, j, 1) = (u * i + v * j - UBound(WorldMesh, 1) / 2 - UBound(WorldMesh, 2) / 2)
  339.  
  340.             ' Peaks and valleys.
  341.             Select Case Rnd
  342.                 Case Is < .005
  343.                     tempworldmesh2(i, j, 1) = tempworldmesh2(i, j, 1) - (100 + Rnd * 100)
  344.                     tempworldmesh2(i, j, 2) = 1 ' fixed
  345.                 Case Is > .995
  346.                     tempworldmesh2(i, j, 1) = tempworldmesh2(i, j, 1) + (100 + Rnd * 300)
  347.                     tempworldmesh2(i, j, 2) = 1 ' fixed
  348.                 Case Else
  349.                     tempworldmesh2(i, j, 1) = tempworldmesh2(i, j, 1) + 0
  350.                     tempworldmesh2(i, j, 2) = 0 'free
  351.             End Select
  352.  
  353.             ' Plateaus.
  354.             For k = 1 To UBound(Plateau)
  355.                 ii = i - Plateau(k).Location.x - UBound(WorldMesh, 1) / 2
  356.                 jj = j - Plateau(k).Location.y - UBound(WorldMesh, 2) / 2
  357.                 If (ii ^ 2 + jj ^ 2 < Plateau(k).Radius ^ 2) Then
  358.                     tempworldmesh2(i, j, 1) = Plateau(k).Location.z
  359.                     tempworldmesh2(i, j, 2) = 1 ' fixed
  360.                 End If
  361.             Next
  362.         Next
  363.     Next
  364.  
  365.     ' Relax the world mesh to generate terrain.
  366.     Dim SmoothFactor As Integer
  367.     SmoothFactor = 30
  368.     For k = SmoothFactor To 1 Step -1
  369.         For i = 1 To UBound(WorldMesh, 1)
  370.             For j = 1 To UBound(WorldMesh, 2)
  371.                 tempworldmesh1(i, j) = tempworldmesh2(i, j, 1)
  372.                 ' Before last iteration, allow extreme points to relax.
  373.                 If (k = 1) Then tempworldmesh2(i, j, 2) = 0
  374.             Next
  375.         Next
  376.         For i = 2 To UBound(WorldMesh, 1) - 1
  377.             For j = 2 To UBound(WorldMesh, 2) - 1
  378.                 If (tempworldmesh2(i, j, 2) = 0) Then
  379.                     tempworldmesh2(i, j, 1) = (1 / 4) * (tempworldmesh1(i - 1, j) + tempworldmesh1(i + 1, j) + tempworldmesh1(i, j - 1) + tempworldmesh1(i, j + 1))
  380.                 End If
  381.             Next
  382.         Next
  383.     Next
  384.     For i = 1 To UBound(WorldMesh, 1)
  385.         For j = 1 To UBound(WorldMesh, 2)
  386.             WorldMesh(i, j) = tempworldmesh2(i, j, 1)
  387.         Next
  388.     Next
  389.  
  390.     ' Create terrain groups.
  391.     Dim g0 As Long
  392.     For i = 1 To UBound(WorldMesh, 1)
  393.         For j = 1 To UBound(WorldMesh, 2)
  394.             u = BlockSize * (i - (UBound(WorldMesh, 1) / 2))
  395.             v = BlockSize * (j - (UBound(WorldMesh, 2) / 2))
  396.             w = WorldMesh(i, j)
  397.             ' Store first address.
  398.             If ((i = 1) And (j = 1)) Then g0 = g
  399.             g = NewGroup&(g, u, v, w, 10, 0, 0)
  400.             Group(g).Label = TerrainHeightLabel$(w)
  401.             Group(g).Volume.x = BlockSize
  402.             Group(g).Volume.y = BlockSize
  403.             Group(g).Volume.z = Sqr(BlockSize * BlockSize + BlockSize * BlockSize)
  404.             Group(g).PlotMode = 0
  405.             WorldMeshAddress(i, j) = g
  406.         Next
  407.         Call ClusterPinch(g)
  408.     Next
  409.     Call ClusterPinch(g)
  410.  
  411.     CreateTerrainGroups& = g0
  412.  
  413. Function CreateTerrainVectors& (LagAddressIn As Long)
  414.     Dim g As Long
  415.     Dim As Integer i, j, k
  416.     Dim As Integer ii, jj
  417.     Dim Smoothfactor As Integer
  418.     g = LagAddressIn
  419.  
  420.     ' Create fine-grain block mesh to relax terrain.
  421.     Dim vindex As Long
  422.     Dim BlockBins As Integer
  423.     BlockBins = BlockSize / BlockStep
  424.     Dim blockmesh1(BlockBins, BlockBins)
  425.     Dim blockmesh2(BlockBins, BlockBins, 2)
  426.     vindex = Group(Group(g).Lagger).LastVector
  427.     For i = 1 To UBound(WorldMesh, 1)
  428.         For j = 1 To UBound(WorldMesh, 2)
  429.  
  430.             g = WorldMeshAddress(i, j)
  431.             Group(g).FirstVector = vindex + 1
  432.  
  433.             ' For each world mesh location, use the block mesh whose boundary heights are determined by neighbors.
  434.             For ii = 1 To UBound(blockmesh2, 1)
  435.                 For jj = 1 To UBound(blockmesh2, 2)
  436.  
  437.                     ' Lock boundaries.
  438.                     If (ii = 1) Or (ii = UBound(blockmesh2, 1)) Then
  439.                         blockmesh2(ii, jj, 2) = 1
  440.                     End If
  441.                     If (jj = 1) Or (jj = UBound(blockmesh2, 2)) Then
  442.                         blockmesh2(ii, jj, 2) = 1
  443.                     End If
  444.  
  445.                     ' Set boundary values.
  446.                     If (i > 1) Then
  447.                         If (ii = 1) Then
  448.                             blockmesh2(ii, jj, 1) = -WorldMesh(i, j) + (1 / 2) * (WorldMesh(i, j) + WorldMesh(i - 1, j))
  449.                         End If
  450.                     End If
  451.                     If (j > 1) Then
  452.                         If (jj = 1) Then
  453.                             blockmesh2(ii, jj, 1) = -WorldMesh(i, j) + (1 / 2) * (WorldMesh(i, j) + WorldMesh(i, j - 1))
  454.                         End If
  455.                     End If
  456.                     If (i < UBound(WorldMesh, 1)) Then
  457.                         If (ii = UBound(blockmesh2, 1)) Then
  458.                             blockmesh2(ii, jj, 1) = -WorldMesh(i, j) + (1 / 2) * (WorldMesh(i, j) + WorldMesh(i + 1, j))
  459.                         End If
  460.                     End If
  461.                     If (j < UBound(WorldMesh, 2)) Then
  462.                         If (jj = UBound(blockmesh2, 2)) Then
  463.                             blockmesh2(ii, jj, 1) = -WorldMesh(i, j) + (1 / 2) * (WorldMesh(i, j) + WorldMesh(i, j + 1))
  464.                         End If
  465.                     End If
  466.  
  467.                     ' Set extreme points.
  468.                     If ((ii > 1) And (ii < UBound(blockmesh2, 1)) And (jj > 1) And (jj < UBound(blockmesh2, 1))) Then
  469.                         Select Case Rnd
  470.                             Case Is < .01
  471.                                 blockmesh2(ii, jj, 1) = -Rnd * 20
  472.                                 blockmesh2(ii, jj, 2) = 1 ' fixed
  473.                             Case Is > .99
  474.                                 blockmesh2(ii, jj, 1) = Rnd * 20
  475.                                 blockmesh2(ii, jj, 2) = 1 ' fixed
  476.                             Case Else
  477.                                 blockmesh2(ii, jj, 1) = 0
  478.                                 blockmesh2(ii, jj, 2) = 0 'free
  479.                         End Select
  480.                     End If
  481.  
  482.                     ' Copy mesh.
  483.                     blockmesh1(ii, jj) = blockmesh2(ii, jj, 1)
  484.  
  485.                 Next
  486.             Next
  487.  
  488.             ' Relax mesh body.
  489.             Smoothfactor = 30
  490.             For k = Smoothfactor To 1 Step -1
  491.                 For ii = 2 To UBound(blockmesh1, 1) - 1
  492.                     For jj = 2 To UBound(blockmesh1, 2) - 1
  493.                         ' Before last iteration, allow extreme points to relax.
  494.                         If (k = 5) Then blockmesh2(ii, jj, 2) = 0
  495.                         If (blockmesh2(ii, jj, 2) = 0) Then
  496.                             blockmesh2(ii, jj, 1) = (1 / 4) * (blockmesh1(ii - 1, jj) + blockmesh1(ii + 1, jj) + blockmesh1(ii, jj - 1) + blockmesh1(ii, jj + 1))
  497.                         End If
  498.                     Next
  499.                 Next
  500.  
  501.                 ' Upate mesh with relaxed version.
  502.                 For ii = 1 To UBound(blockmesh1, 1)
  503.                     For jj = 1 To UBound(blockmesh1, 2)
  504.                         blockmesh1(ii, jj) = blockmesh2(ii, jj, 1)
  505.                     Next
  506.                 Next
  507.             Next
  508.  
  509.             ' Relax mesh boundaries once.
  510.             For ii = 2 To UBound(blockmesh1, 1) - 1
  511.                 jj = 1
  512.                 blockmesh2(ii, jj, 1) = (1 / 3) * (blockmesh1(ii - 1, jj) + blockmesh1(ii + 1, jj) + blockmesh1(ii, jj + 1))
  513.                 jj = UBound(blockmesh1, 2)
  514.                 blockmesh2(ii, jj, 1) = (1 / 3) * (blockmesh1(ii - 1, jj) + blockmesh1(ii + 1, jj) + blockmesh1(ii, jj - 1))
  515.             Next
  516.             For jj = 2 To UBound(blockmesh1, 2) - 1
  517.                 ii = 1
  518.                 blockmesh2(ii, jj, 1) = (1 / 3) * (blockmesh1(ii + 1, jj) + blockmesh1(ii, jj - 1) + blockmesh1(ii, jj + 1))
  519.                 ii = UBound(blockmesh1, 1)
  520.                 blockmesh2(ii, jj, 1) = (1 / 3) * (blockmesh1(ii - 1, jj) + blockmesh1(ii, jj - 1) + blockmesh1(ii, jj + 1))
  521.             Next
  522.  
  523.             ii = 1
  524.             jj = 1
  525.             blockmesh2(ii, jj, 1) = (1 / 2) * (blockmesh1(ii + 1, jj) + blockmesh1(ii, jj + 1))
  526.  
  527.             ii = UBound(blockmesh1, 1)
  528.             jj = UBound(blockmesh1, 2)
  529.             blockmesh2(ii, jj, 1) = (1 / 2) * (blockmesh1(ii - 1, jj) + blockmesh1(ii, jj - 1))
  530.  
  531.             ii = 1
  532.             jj = UBound(blockmesh1, 2)
  533.             blockmesh2(ii, jj, 1) = (1 / 2) * (blockmesh1(ii + 1, jj) + blockmesh1(ii, jj - 1))
  534.  
  535.             ii = UBound(blockmesh1, 1)
  536.             jj = 1
  537.             blockmesh2(ii, jj, 1) = (1 / 2) * (blockmesh1(ii - 1, jj) + blockmesh1(ii, jj + 1))
  538.  
  539.             ' Upate mesh with relaxed version.
  540.             For ii = 1 To UBound(blockmesh1, 1)
  541.                 For jj = 1 To UBound(blockmesh1, 2)
  542.                     blockmesh1(ii, jj) = blockmesh2(ii, jj, 1)
  543.                 Next
  544.             Next
  545.  
  546.             ' Set particle positions relative to group center. Add random fuzz.
  547.             Dim cc As _Unsigned Long
  548.             Dim dd As _Unsigned Long
  549.             For ii = 1 To UBound(blockmesh1, 1)
  550.                 For jj = 1 To UBound(blockmesh1, 2)
  551.                     vindex = vindex + 1
  552.                     vec3Dpos(vindex).x = BlockStep * ii - BlockSize / 2 + 3 * (Rnd - .5)
  553.                     vec3Dpos(vindex).y = BlockStep * jj - BlockSize / 2 + 3 * (Rnd - .5)
  554.                     vec3Dpos(vindex).z = blockmesh1(ii, jj)
  555.                     cc = TerrainHeightShade~&(WorldMesh(i, j) + blockmesh1(ii, jj))
  556.                     dd = TerrainHeightShade~&(WorldMesh(i, j) + blockmesh1(ii, jj) + BlockSize)
  557.                     vec3Dcolor(vindex) = ShadeMix~&(cc, ShadeMix~&(cc, dd, blockmesh1(ii, jj) / 10), .5)
  558.                 Next
  559.             Next
  560.  
  561.             Group(g).LastVector = vindex + 1 ''' why on earth is this +1?
  562.         Next
  563.     Next
  564.  
  565.     CreateTerrainVectors& = g
  566.  
  567. Function CreateTerrainVolume& (LagAddressIn As Long)
  568.     Dim g As Long
  569.     Dim As Integer i, j
  570.     Dim k As Long
  571.     Dim As Double u, v, z
  572.     Dim groupcount As Integer
  573.     Dim clustertick As Integer
  574.     g = LagAddressIn
  575.     groupcount = 0
  576.     clustertick = 0
  577.     For i = 1 To UBound(WorldMesh, 1)
  578.         For j = 1 To UBound(WorldMesh, 2)
  579.             z = WorldMesh(i, j) + BlockSize / 2
  580.             If (z < 0) Then
  581.                 groupcount = groupcount + 1
  582.                 u = BlockSize * (i - (UBound(WorldMesh, 1) / 2))
  583.                 v = BlockSize * (j - (UBound(WorldMesh, 2) / 2))
  584.                 g = NewCube&(g, "Water", 50, u, v, WorldMesh(i, j) - z / 2, BlockSize, BlockSize, -z, Blue, RoyalBlue, DarkBlue, 0, 0)
  585.                 For k = Group(g).FirstVector To Group(g).LastVector
  586.                     vec3Dvel(k).x = (Rnd - .5) * .20
  587.                     vec3Dvel(k).y = (Rnd - .5) * .20
  588.                     vec3Dvel(k).z = 0
  589.                 Next
  590.             End If
  591.             clustertick = clustertick + 1
  592.             If (clustertick = 12) Then
  593.                 clustertick = 0
  594.                 If (groupcount > 0) Then
  595.                     groupcount = 0
  596.                     Call ClusterPinch(g)
  597.                 End If
  598.             End If
  599.         Next
  600.         Call ClusterPinch(g)
  601.     Next
  602.     Call ClusterPinch(g)
  603.  
  604.     groupcount = 0
  605.     clustertick = 0
  606.     For i = 1 To UBound(WorldMesh, 1)
  607.         For j = 1 To UBound(WorldMesh, 2)
  608.             z = WorldMesh(i, j) + BlockSize / 2
  609.             If (z > 0) Then
  610.                 groupcount = groupcount + 1
  611.                 u = BlockSize * (i - (UBound(WorldMesh, 1) / 2))
  612.                 v = BlockSize * (j - (UBound(WorldMesh, 2) / 2))
  613.                 g = NewCube&(g, "Dirt and Sand", 20, u, v, WorldMesh(i, j) / 2 - BlockSize / 4, BlockSize, BlockSize, WorldMesh(i, j), SaddleBrown, DarkKhaki, Sienna, 0, 0)
  614.                 g = NewCube&(g, "Dirt and Sand", 20, u, v, -50, BlockSize, BlockSize, 80, SaddleBrown, DarkKhaki, Sienna, 0, 0)
  615.             End If
  616.             clustertick = clustertick + 1
  617.             If (clustertick = 12) Then
  618.                 clustertick = 0
  619.                 If (groupcount > 0) Then
  620.                     groupcount = 0
  621.                     Call ClusterPinch(g)
  622.                 End If
  623.             End If
  624.         Next
  625.         Call ClusterPinch(g)
  626.     Next
  627.     Call ClusterPinch(g)
  628.  
  629.     groupcount = 0
  630.     clustertick = 0
  631.     For i = 1 To UBound(WorldMesh, 1)
  632.         For j = 1 To UBound(WorldMesh, 2)
  633.             groupcount = groupcount + 1
  634.             u = BlockSize * (i - (UBound(WorldMesh, 1) / 2))
  635.             v = BlockSize * (j - (UBound(WorldMesh, 2) / 2))
  636.             z = WorldMesh(i, j)
  637.             If (z < 0) Then z = 0
  638.             g = NewCube&(g, "Atmospheric Dust", 30, u, v, 100 + BlockSize * (3 - 1 / 2) + z, BlockSize, BlockSize, BlockSize * 3, DarkGray, White, Snow, 0, 0)
  639.             clustertick = clustertick + 1
  640.             If (clustertick = 12) Then
  641.                 clustertick = 0
  642.                 If (groupcount > 0) Then
  643.                     groupcount = 0
  644.                     Call ClusterPinch(g)
  645.                 End If
  646.             End If
  647.         Next
  648.         Call ClusterPinch(g)
  649.     Next
  650.     Call ClusterPinch(g)
  651.  
  652.     CreateTerrainVolume& = g
  653.  
  654. Function CreateTornado& (LagAddressIn As Long)
  655.     Dim As Integer n, k
  656.     Dim As Double u, v, w, x0, y0, z0
  657.     Dim As Long p, g
  658.     Dim wi As Integer
  659.     Dim wj As Integer
  660.     g = LagAddressIn
  661.     For n = 1 To 4
  662.         u = Rnd * 2 * pi
  663.         FixedPathIndexTicker = FixedPathIndexTicker + 1
  664.         For p = 1 To 86400
  665.             x0 = BlockSize * 30 * Cos(u + 2 * pi * (6 * 30) * (p - 1) / 86400)
  666.             y0 = BlockSize * 30 * Sin(u + 2 * pi * (6 * 30) * (p - 1) / 86400)
  667.             FixedPath(FixedPathIndexTicker, p).x = x0
  668.             FixedPath(FixedPathIndexTicker, p).y = y0
  669.             wi = 1 + Int(x0 / BlockSize + UBound(WorldMesh, 1) / 2)
  670.             wj = 1 + Int(y0 / BlockSize + UBound(WorldMesh, 2) / 2)
  671.             z0 = WorldMesh(wi, wj)
  672.             If (z0 < 0) Then z0 = 0
  673.             FixedPath(FixedPathIndexTicker, p).z = z0 + 50
  674.         Next
  675.         For k = 1 To 30
  676.             u = Rnd * 100
  677.             v = Rnd * u / 3
  678.             w = Rnd * 2 * pi
  679.             g = NewCube&(g, "Tornado", 35, v * Cos(w), v * Sin(w), u, 15, 15, 15, DarkGray, SunsetOrange, DarkGoldenRod, FixedPathIndexTicker, 0)
  680.             Call SetParticleVelocity(g, -Sin(w), Cos(w), 0)
  681.         Next
  682.         Call ClusterPinch(g)
  683.     Next
  684.     CreateTornado& = g
  685.  
  686. Function CreateWeather& (LagAddressIn As Long)
  687.     Dim As Integer n, k
  688.     Dim As Double u, v, w, x0, y0, z0, tallness
  689.     Dim As Long p, g
  690.     Dim wi As Integer
  691.     Dim wj As Integer
  692.     g = LagAddressIn
  693.     For n = 1 To 100
  694.         FixedPathIndexTicker = FixedPathIndexTicker + 1
  695.         u = Rnd * 2 * pi
  696.         v = Rnd * .7 * BlockSize * Sqr((UBound(WorldMesh, 1) / 2) ^ 2 + (UBound(WorldMesh, 2) / 2) ^ 2)
  697.         w = pi / 2
  698.         tallness = Rnd * (CloudLayer(UBound(CloudLayer)).Height - CloudLayer(1).Height)
  699.         For p = 1 To 86400
  700.             x0 = v * Cos(u + 2 * pi * (1 * 30) * (p - 1) / 86400 + w)
  701.             y0 = v * Sin(u + 4 * pi * (1 * 30) * (p - 1) / 86400)
  702.             FixedPath(FixedPathIndexTicker, p).x = x0
  703.             FixedPath(FixedPathIndexTicker, p).y = y0
  704.             wi = 1 + Int(x0 / BlockSize + UBound(WorldMesh, 1) / 2)
  705.             wj = 1 + Int(y0 / BlockSize + UBound(WorldMesh, 2) / 2)
  706.             z0 = WorldMesh(wi, wj)
  707.             If (z0 < 0) Then z0 = 0
  708.             z0 = z0 + CloudLayer(1).Height + tallness
  709.             FixedPath(FixedPathIndexTicker, p).z = z0
  710.         Next
  711.         For k = 1 To 20 '30
  712.             u = Rnd * 80
  713.             v = u
  714.             w = Rnd * 2 * pi
  715.             z0 = z0 + 10 * (Rnd - .5)
  716.             g = NewCube&(g, CloudHeightLabel$(z0), 20, v * Cos(w), v * Sin(w), z0, BlockSize / 2, BlockSize / 2, BlockSize / 2, Red, Red, Red, FixedPathIndexTicker, 0)
  717.             Call SetParticleVelocity(g, .01 * (Rnd - .5), .01 * (Rnd - .5), 0)
  718.             For p = Group(g).FirstVector To Group(g).LastVector
  719.                 vec3Dcolor(p) = CloudHeightShade~&(Group(g).Centroid.z + vec3Dpos(p).z)
  720.             Next
  721.             Group(g).PlotMode = 0
  722.  
  723.             If (Rnd < .2) Then
  724.                 z0 = Group(g).Centroid.z
  725.                 g = NewCube&(g, "Rain", 20, v * Cos(w), v * Sin(w), z0 / 2 - BlockSize / 2, BlockSize / 2, BlockSize / 2, z0, Blue, RoyalBlue, DodgerBlue, 0, 0)
  726.                 Call SetParticleVelocity(g, 0, 0, -1)
  727.             End If
  728.  
  729.         Next
  730.         Call ClusterPinch(g)
  731.     Next
  732.     CreateWeather& = g
  733.  
  734. Function CreateClover& (LagAddressIn As Long)
  735.     Dim As Long g, vindex
  736.     Dim As Integer i, j
  737.     Dim As Double x, y, z, u, t
  738.     Dim As Integer pedals
  739.     Dim As Double scale
  740.     Dim As Double height
  741.     g = LagAddressIn
  742.     For i = 1 To UBound(WorldMesh, 1)
  743.         For j = 1 To UBound(WorldMesh, 2)
  744.             z = WorldMesh(i, j)
  745.             If (TerrainHeightIndex(z) = 1) Then
  746.                 If (Rnd < .1) Then
  747.                     x = (Rnd - .5) * BlockSize + BlockSize * (i - (UBound(WorldMesh, 1) / 2))
  748.                     y = (Rnd - .5) * BlockSize + BlockSize * (j - (UBound(WorldMesh, 2) / 2))
  749.                     scale = 1 / (4 + Rnd * 3)
  750.                     g = NewGroup&(g, x, y, z, 12, 0, 0)
  751.                     Group(g).Label = "Clover"
  752.                     Group(g).Volume.x = BlockSize
  753.                     Group(g).Volume.y = BlockSize
  754.                     Group(g).Volume.z = BlockSize
  755.                     vindex = Group(Group(g).Lagger).LastVector
  756.                     Group(g).FirstVector = vindex + 1
  757.                     Group(g).PlotMode = 1
  758.                     pedals = 2 + Int(Rnd * 4)
  759.                     height = 2 + Rnd
  760.                     t = Rnd * 2 * pi
  761.                     For u = 0 To 2 * pi Step .1
  762.                         vindex = vindex + 1
  763.                         vec3Dpos(vindex).x = scale * ((Group(g).Volume.x) * (0 + Cos(pedals * u) * Cos(u))) * Cos(t)
  764.                         vec3Dpos(vindex).y = scale * ((Group(g).Volume.y) * (0 + Cos(pedals * u) * Cos(u))) * Sin(t)
  765.                         vec3Dpos(vindex).z = scale * ((Group(g).Volume.z) * (height + Cos(pedals * u) * Sin(u)))
  766.                         Select Case pedals
  767.                             Case 3
  768.                                 vec3Dcolor(vindex) = Magenta
  769.                             Case Else
  770.                                 vec3Dcolor(vindex) = Lime
  771.                         End Select
  772.                     Next
  773.                     For u = (Group(g).Volume.z) * height To 0 Step -(Group(g).Volume.z) * height / 10
  774.                         vindex = vindex + 1
  775.                         vec3Dpos(vindex).x = scale * (0 + (Rnd - .5))
  776.                         vec3Dpos(vindex).y = scale * (0 + (Rnd - .5))
  777.                         vec3Dpos(vindex).z = scale * (u)
  778.                         vec3Dcolor(vindex) = LimeGreen
  779.                     Next
  780.                     Group(g).LastVector = vindex '''+ 1 ''' why on earth is this +1?
  781.                     Call ClusterPinch(g)
  782.                 End If
  783.             End If
  784.         Next
  785.     Next
  786.     CreateClover& = g
  787.  
  788. Function CreateGrass& (LagAddressIn As Long)
  789.     Dim As Long g, vindex
  790.     Dim As Integer i, j, k
  791.     Dim As Double x0, y0, x, y, z, u, t
  792.     Dim As Double scale
  793.     Dim As Double height
  794.     g = LagAddressIn
  795.     For i = 1 To UBound(WorldMesh, 1)
  796.         For j = 1 To UBound(WorldMesh, 2)
  797.             z = WorldMesh(i, j)
  798.             If (TerrainHeightIndex(z) = 2) Then
  799.                 For k = 1 To 5
  800.                     If (Rnd < .5) Then
  801.                         x = (Rnd - .5) * BlockSize + BlockSize * (i - (UBound(WorldMesh, 1) / 2))
  802.                         y = (Rnd - .5) * BlockSize + BlockSize * (j - (UBound(WorldMesh, 2) / 2))
  803.                         scale = 1 / (4 + Rnd * 3)
  804.                         g = NewGroup&(g, x, y, z, 16, 0, 0)
  805.                         Group(g).Label = "Grass"
  806.                         Group(g).Volume.x = BlockSize
  807.                         Group(g).Volume.y = BlockSize
  808.                         Group(g).Volume.z = BlockSize
  809.                         vindex = Group(Group(g).Lagger).LastVector
  810.                         Group(g).FirstVector = vindex + 1
  811.                         Group(g).PlotMode = 1
  812.                         height = 1 + Rnd
  813.                         t = Rnd * 2 * pi
  814.                         x0 = BlockSize * 1 * (Rnd - .5)
  815.                         y0 = BlockSize * 1 * (Rnd - .5)
  816.                         For u = (Group(g).Volume.z) * height To 0 Step -(Group(g).Volume.z) * height / 5
  817.                             vindex = vindex + 1
  818.                             vec3Dpos(vindex).x = scale * (x0 + (Rnd - .5))
  819.                             vec3Dpos(vindex).y = scale * (y0 + (Rnd - .5))
  820.                             vec3Dpos(vindex).z = scale * (u)
  821.                             vec3Dcolor(vindex) = ShadeMix~&(DarkGoldenRod, Sienna, Rnd)
  822.                         Next
  823.                         Group(g).LastVector = vindex '''+ 1 ''' why on earth is this +1?
  824.                     End If
  825.                 Next
  826.             End If
  827.         Next
  828.         Call ClusterPinch(g)
  829.     Next
  830.     CreateGrass& = g
  831.  
  832. Function CreateFern& (LagAddressIn As Long)
  833.     Dim As Long g, vindex
  834.     Dim As Integer i, j, k
  835.     Dim As Double xx, yy, zz, x, y, z, t
  836.     Dim As Double scale
  837.     g = LagAddressIn
  838.     For i = 1 To UBound(WorldMesh, 1)
  839.         For j = 1 To UBound(WorldMesh, 2)
  840.             z = WorldMesh(i, j)
  841.             If (TerrainHeightIndex(z) = 1) Then
  842.                 If (Rnd < .1) Then
  843.                     x = (Rnd - .5) * BlockSize + BlockSize * (i - (UBound(WorldMesh, 1) / 2))
  844.                     y = (Rnd - .5) * BlockSize + BlockSize * (j - (UBound(WorldMesh, 2) / 2))
  845.                     scale = .05 + Rnd * .05
  846.                     g = NewGroup&(g, x, y, z, 12, 0, 0)
  847.                     Group(g).Label = "Fern"
  848.                     Group(g).Volume.x = BlockSize
  849.                     Group(g).Volume.y = BlockSize
  850.                     Group(g).Volume.z = BlockSize
  851.                     vindex = Group(Group(g).Lagger).LastVector
  852.                     Group(g).FirstVector = vindex + 1
  853.                     Group(g).PlotMode = 2
  854.                     t = Rnd * 2 * pi
  855.                     xx = 0
  856.                     yy = xx
  857.                     zz = 0
  858.                     For k = 1 To 100
  859.                         Select Case Rnd * 100
  860.                             Case Is < 1
  861.                                 xx = 0
  862.                                 zz = .16 * zz
  863.                             Case Is < 86
  864.                                 xx = .85 * xx + .04 * zz
  865.                                 zz = -.04 * xx + .85 * zz + 1.6
  866.                             Case Is < 93
  867.                                 xx = .2 * xx - .26 * zz
  868.                                 zz = .23 * xx + .22 * zz + 1.6
  869.                             Case Else
  870.                                 xx = -.15 * xx + .28 * zz
  871.                                 zz = .26 * xx + .24 * zz + .44
  872.                         End Select
  873.                         yy = xx
  874.                         vindex = vindex + 1
  875.                         vec3Dpos(vindex).x = scale * Group(g).Volume.x * xx * Cos(t)
  876.                         vec3Dpos(vindex).y = scale * Group(g).Volume.y * yy * Sin(t)
  877.                         vec3Dpos(vindex).z = scale * Group(g).Volume.z * zz
  878.                         vec3Dcolor(vindex) = Lime
  879.                     Next
  880.                     Group(g).LastVector = vindex '''+ 1 ''' why on earth is this +1?
  881.                     Call ClusterPinch(g)
  882.                 End If
  883.             End If
  884.         Next
  885.     Next
  886.     CreateFern& = g
  887.  
  888. Function CreateSun& (LagAddressIn As Long)
  889.     Dim As Integer k
  890.     Dim As Double xx, yy, zz, x0, y0, z0, phase
  891.     Dim As Long p, g
  892.     g = LagAddressIn
  893.     FixedPathIndexTicker = FixedPathIndexTicker + 1
  894.     For p = 1 To 86400
  895.         phase = -2 * pi * (24) * (p - 1) / 86400 - pi / 2
  896.         x0 = 5000 * Cos(phase)
  897.         y0 = 0
  898.         z0 = 3000 * Sin(phase)
  899.         FixedPath(FixedPathIndexTicker, p).x = x0
  900.         FixedPath(FixedPathIndexTicker, p).y = y0
  901.         FixedPath(FixedPathIndexTicker, p).z = z0
  902.     Next
  903.     For k = 1 To 30
  904.         Do
  905.             xx = (Rnd - .5) * 6 * BlockSize
  906.             yy = (Rnd - .5) * 6 * BlockSize
  907.             zz = (Rnd - .5) * 6 * BlockSize
  908.         Loop Until ((xx ^ 2 + yy ^ 2 + zz ^ 2) < (.5 * 6 * BlockSize) ^ 2)
  909.         g = NewCube&(g, "Sun", 50, xx, yy, zz, BlockSize * 6, BlockSize * 6, BlockSize * 6, Red, Red, Red, FixedPathIndexTicker, 0)
  910.         For p = Group(g).FirstVector To Group(g).LastVector
  911.             vec3Dcolor(p) = ShadeMix~&(Sunglow, SunsetOrange, Rnd)
  912.         Next
  913.         Call SetParticleVelocity(g, .5 * (Rnd - .5), .5 * (Rnd - .5), .5 * (Rnd - .5))
  914.         Group(g).PlotMode = 0
  915.     Next
  916.     SunClusterAddress = ClusterIdTicker
  917.     Call ClusterPinch(g)
  918.     CreateSun& = g
  919.  
  920. Function CreateMoon& (LagAddressIn As Long)
  921.     Dim As Integer k
  922.     Dim As Double xx, yy, zz, x0, y0, z0, phase
  923.     Dim As Long p, g
  924.     g = LagAddressIn
  925.     FixedPathIndexTicker = FixedPathIndexTicker + 1
  926.     For p = 1 To 86400
  927.         phase = -2 * pi * (48) * (p - 1) / 86400 + pi / 2
  928.         x0 = 0
  929.         y0 = 4000 * Cos(phase)
  930.         z0 = 2000 * Sin(phase)
  931.         FixedPath(FixedPathIndexTicker, p).x = x0
  932.         FixedPath(FixedPathIndexTicker, p).y = y0
  933.         FixedPath(FixedPathIndexTicker, p).z = z0
  934.     Next
  935.     For k = 1 To 30
  936.         Do
  937.             xx = (Rnd - .5) * 5 * BlockSize
  938.             yy = (Rnd - .5) * 5 * BlockSize
  939.             zz = (Rnd - .5) * 5 * BlockSize
  940.         Loop Until ((xx ^ 2 + yy ^ 2 + zz ^ 2) < (.5 * 5 * BlockSize) ^ 2)
  941.         g = NewCube&(g, "Moon", 50, xx, yy, zz, 5 * BlockSize, 5 * BlockSize, 5 * BlockSize, Gray, DarkGray, SlateGray, FixedPathIndexTicker, 0)
  942.         Group(g).PlotMode = 0
  943.     Next
  944.     MoonClusterAddress = ClusterIdTicker
  945.     Call ClusterPinch(g)
  946.     CreateMoon& = g
  947.  
  948. Function CreateFish& (LagAddressIn As Long)
  949.     Dim As Integer n, m, wi, wj
  950.     Dim As Double u, v, x0, y0, z0
  951.     Dim As Long p, g, p0
  952.     g = LagAddressIn
  953.     For n = 1 To 12
  954.         u = Rnd * 2 * pi
  955.         FixedPathIndexTicker = FixedPathIndexTicker + 1
  956.         For p = 1 To 86400
  957.             x0 = BlockSize * Plateau(1).Location.x + BlockSize * (4 + Cos(2 * pi * n / 12)) * Cos(u + 2 * pi * (24 * 30) * (p - 1) / 86400)
  958.             y0 = BlockSize * Plateau(1).Location.y + BlockSize * (4 + Cos(2 * pi * n / 12)) * Sin(u + 2 * pi * (24 * 30) * (p - 1) / 86400)
  959.             FixedPath(FixedPathIndexTicker, p).x = x0
  960.             FixedPath(FixedPathIndexTicker, p).y = y0
  961.             wi = 1 + Int(x0 / BlockSize + UBound(WorldMesh, 1) / 2)
  962.             wj = 1 + Int(y0 / BlockSize + UBound(WorldMesh, 2) / 2)
  963.             z0 = WorldMesh(wi, wj) + 100 + 80 * Cos(2 * pi * n / 12) * Cos(2 * pi * (24 * 30) * (p - 1) / 86400)
  964.             FixedPath(FixedPathIndexTicker, p).z = z0
  965.         Next
  966.         ' In the following group, there are 48 frames with 36 vectors per frame. The +1 offset is fishy, no pun.
  967.         g = NewCube&(g, "Fish", 36 * (48 + 1), 0, 0, 0, BlockSize / 4, BlockSize / 4, BlockSize / 4, LimeGreen, SunsetOrange, DarkGoldenRod, FixedPathIndexTicker, 1)
  968.         Group(g).FrameLength = 36
  969.         u = 0
  970.         For p = Group(g).FirstVector To Group(g).FirstVector + Group(g).FrameLength - 1
  971.             u = u + 2 * pi / Group(g).FrameLength
  972.             vec3Dpos(p).x = Group(g).Volume.x * (Cos(u) - Sin(u) ^ 2 / Sqr(2))
  973.             vec3Dpos(p).y = 0
  974.             vec3Dpos(p).z = Group(g).Volume.z * (Cos(u) * Sin(u))
  975.         Next
  976.         v = 0
  977.         For m = 1 To 48
  978.             v = v - 2 * pi / 48
  979.             p0 = 0
  980.             For p = Group(g).FirstVector + Group(g).FrameLength * (m) To Group(g).FirstVector + Group(g).FrameLength * (m + 1) - 1
  981.                 vec3Dpos(p).x = Cos(v) * vec3Dpos(Group(g).FirstVector + p0).x + Sin(v) * vec3Dpos(Group(g).FirstVector + p0).y
  982.                 vec3Dpos(p).y = -Sin(v) * vec3Dpos(Group(g).FirstVector + p0).x + Cos(v) * vec3Dpos(Group(g).FirstVector + p0).y
  983.                 vec3Dpos(p).z = vec3Dpos(Group(g).FirstVector + p0).z
  984.                 p0 = p0 + 1
  985.             Next
  986.         Next
  987.         Call ClusterPinch(g)
  988.     Next
  989.     CreateFish& = g
  990.  
  991. Sub InitCamera
  992.     ToggleAnimate = 1
  993.     PlayerCamera.Position.x = 0
  994.     PlayerCamera.Position.y = 0
  995.     PlayerCamera.Position.z = 100 + 40 + WorldMesh(UBound(WorldMesh, 1) / 2, UBound(WorldMesh, 2) / 2)
  996.     PlayerCamera.Velocity.x = 0
  997.     PlayerCamera.Velocity.y = 0
  998.     PlayerCamera.Velocity.z = .1
  999.     PlayerCamera.Acceleration.x = 0
  1000.     PlayerCamera.Acceleration.y = 0
  1001.     PlayerCamera.Acceleration.z = -.5
  1002.     uhat(1) = 1: uhat(2) = 0: uhat(3) = 0
  1003.     vhat(1) = 0: vhat(2) = 0: vhat(3) = 1
  1004.     Call CalculateScreenVectors
  1005.  
  1006. Sub RegulateCamera
  1007.     Dim As Double dx, dy, t
  1008.     dx = -nhat(1)
  1009.     dy = -nhat(2)
  1010.     If ((dx > 0) And (dy > 0)) Then t = -pi / 2 + (Atn(dy / dx))
  1011.     If ((dx < 0) And (dy > 0)) Then t = -pi / 2 + pi + (Atn(dy / dx))
  1012.     If ((dx < 0) And (dy < 0)) Then t = -pi / 2 + pi + (Atn(dy / dx))
  1013.     If ((dx > 0) And (dy < 0)) Then t = -pi / 2 + 2 * pi + (Atn(dy / dx))
  1014.     uhat(1) = Cos(t): uhat(2) = Sin(t): uhat(3) = 0
  1015.     vhat(1) = 0: vhat(2) = 0: vhat(3) = 1
  1016.     Call CalculateScreenVectors
  1017.  
  1018. ' Terrain tools.
  1019.  
  1020. Function TerrainHeightIndex (z0 As Double)
  1021.     Dim j As Integer
  1022.     Dim h0 As Integer
  1023.     h0 = -1
  1024.     For j = 1 To UBound(Strata)
  1025.         If (z0 <= Strata(j).Height) Then
  1026.             h0 = j - 1
  1027.             Exit For
  1028.         End If
  1029.     Next
  1030.     If (h0 = -1) Then h0 = UBound(Strata)
  1031.     TerrainHeightIndex = h0
  1032.  
  1033. Function TerrainHeightShade~& (z0 As Double)
  1034.     Dim j As Integer
  1035.     Dim h0 As Integer
  1036.     h0 = -1
  1037.     For j = 1 To UBound(Strata)
  1038.         If (z0 <= Strata(j).Height) Then
  1039.             h0 = j - 1
  1040.             Exit For
  1041.         End If
  1042.     Next
  1043.     If (h0 = -1) Then h0 = UBound(Strata)
  1044.     Dim u As Double
  1045.     Dim v As Double
  1046.     Dim alpha As Double
  1047.     Dim sh1 As _Unsigned Long
  1048.     Dim sh2 As _Unsigned Long
  1049.     Select Case h0
  1050.         Case 0
  1051.             sh1 = Strata(1).Shade
  1052.             sh2 = Strata(1).Shade
  1053.             alpha = 0
  1054.         Case UBound(Strata)
  1055.             sh1 = Strata(h0).Shade
  1056.             sh2 = Strata(h0).Shade
  1057.             alpha = 0
  1058.         Case Else
  1059.             sh1 = Strata(h0).Shade
  1060.             sh2 = Strata(h0 + 1).Shade
  1061.             u = z0 - Strata(h0).Height
  1062.             v = Strata(h0 + 1).Height - Strata(h0).Height
  1063.             alpha = u / v
  1064.     End Select
  1065.     TerrainHeightShade~& = ShadeMix~&(sh1, sh2, alpha)
  1066.  
  1067. Function TerrainHeightLabel$ (z0 As Double)
  1068.     Dim TheReturn As String
  1069.     Dim j As Integer
  1070.     Dim h0 As Integer
  1071.     h0 = -1
  1072.     For j = 1 To UBound(Strata)
  1073.         If (z0 <= Strata(j).Height) Then
  1074.             h0 = j
  1075.             Exit For
  1076.         End If
  1077.     Next
  1078.     If (h0 = -1) Then h0 = UBound(Strata)
  1079.     Select Case h0
  1080.         Case 0
  1081.             TheReturn = Strata(1).Label
  1082.         Case UBound(Strata)
  1083.             TheReturn = Strata(h0).Label
  1084.         Case Else
  1085.             TheReturn = Strata(h0).Label
  1086.     End Select
  1087.     TerrainHeightLabel$ = TheReturn
  1088.  
  1089. Function CloudHeightShade~& (z0 As Double)
  1090.     Dim j As Integer
  1091.     Dim h0 As Integer
  1092.     h0 = -1
  1093.     For j = 1 To UBound(CloudLayer)
  1094.         If (z0 <= CloudLayer(j).Height) Then
  1095.             h0 = j - 1
  1096.             Exit For
  1097.         End If
  1098.     Next
  1099.     If (h0 = -1) Then h0 = UBound(CloudLayer)
  1100.     Dim u As Double
  1101.     Dim v As Double
  1102.     Dim alpha As Double
  1103.     Dim sh1 As _Unsigned Long
  1104.     Dim sh2 As _Unsigned Long
  1105.     Select Case h0
  1106.         Case 0
  1107.             sh1 = CloudLayer(1).Shade
  1108.             sh2 = CloudLayer(1).Shade
  1109.             alpha = 0
  1110.         Case UBound(CloudLayer)
  1111.             sh1 = CloudLayer(h0).Shade
  1112.             sh2 = CloudLayer(h0).Shade
  1113.             alpha = 0
  1114.         Case Else
  1115.             sh1 = CloudLayer(h0).Shade
  1116.             sh2 = CloudLayer(h0 + 1).Shade
  1117.             u = z0 - CloudLayer(h0).Height
  1118.             v = CloudLayer(h0 + 1).Height - CloudLayer(h0).Height
  1119.             alpha = u / v
  1120.     End Select
  1121.     CloudHeightShade~& = ShadeMix~&(sh1, sh2, alpha)
  1122.  
  1123. Function CloudHeightLabel$ (z0 As Double)
  1124.     Dim TheReturn As String
  1125.     Dim j As Integer
  1126.     Dim h0 As Integer
  1127.     h0 = -1
  1128.     For j = 1 To UBound(CloudLayer)
  1129.         If (z0 <= CloudLayer(j).Height) Then
  1130.             h0 = j
  1131.             Exit For
  1132.         End If
  1133.     Next
  1134.     If (h0 = -1) Then h0 = UBound(CloudLayer)
  1135.     Select Case h0
  1136.         Case 0
  1137.             TheReturn = CloudLayer(1).Label
  1138.         Case UBound(CloudLayer)
  1139.             TheReturn = CloudLayer(h0).Label
  1140.         Case Else
  1141.             TheReturn = CloudLayer(h0).Label
  1142.     End Select
  1143.     CloudHeightLabel$ = TheReturn
  1144.  
  1145. ' Low-order groups.
  1146.  
  1147. Function NewCube& (LagAddressIn 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, Framing As Integer)
  1148.     Dim k As Integer
  1149.     Dim g As Long
  1150.     Dim q As Long
  1151.     Dim vindex As Long
  1152.     q = LagAddressIn
  1153.     vindex = Group(q).LastVector
  1154.     g = NewGroup&(q, PosX, PosY, PosZ, 64, TheDynamic, Framing)
  1155.     Group(g).Label = TheName
  1156.     Group(g).Volume.x = VolX
  1157.     Group(g).Volume.y = VolY
  1158.     Group(g).Volume.z = VolZ
  1159.     Group(g).FirstVector = vindex + 1
  1160.     Group(g).PlotMode = 1
  1161.     For k = 1 To Weight
  1162.         vindex = vindex + 1
  1163.         vec3Dpos(vindex).x = (Rnd - .5) * VolX
  1164.         vec3Dpos(vindex).y = (Rnd - .5) * VolY
  1165.         vec3Dpos(vindex).z = (Rnd - .5) * VolZ
  1166.         If (Rnd > .5) Then
  1167.             vec3Dcolor(vindex) = ShadeA
  1168.         Else
  1169.             If (Rnd > .5) Then
  1170.                 vec3Dcolor(vindex) = ShadeB
  1171.             Else
  1172.                 vec3Dcolor(vindex) = ShadeC
  1173.             End If
  1174.         End If
  1175.     Next
  1176.     Group(g).LastVector = vindex
  1177.     NewCube& = g
  1178.  
  1179. Function NewWireCube& (LagAddressIn As Long, TheName As String, PosX As Double, PosY As Double, PosZ As Double, VolX As Double, VolY As Double, VolZ As Double, ShadeA As _Unsigned Long, TheDynamic As Integer)
  1180.     Dim g As Long
  1181.     Dim q As Long
  1182.     Dim vindex As Long
  1183.     q = LagAddressIn
  1184.     vindex = Group(q).LastVector
  1185.     g = NewGroup&(q, PosX, PosY, PosZ, 64, TheDynamic, 0)
  1186.     Group(g).Label = TheName
  1187.     Group(g).Volume.x = VolX
  1188.     Group(g).Volume.y = VolY
  1189.     Group(g).Volume.z = VolZ
  1190.     Group(g).FirstVector = vindex + 1
  1191.     Group(g).PlotMode = -1
  1192.  
  1193.     vindex = vindex + 1
  1194.     vec3Dpos(vindex).x = (0 - .5) * VolX
  1195.     vec3Dpos(vindex).y = (0 - .5) * VolY
  1196.     vec3Dpos(vindex).z = (0 - .5) * VolZ
  1197.     vec3Dcolor(vindex) = ShadeA
  1198.     vindex = vindex + 1
  1199.     vec3Dpos(vindex).x = (1 - .5) * VolX
  1200.     vec3Dpos(vindex).y = (0 - .5) * VolY
  1201.     vec3Dpos(vindex).z = (0 - .5) * VolZ
  1202.     vec3Dcolor(vindex) = ShadeA
  1203.     vindex = vindex + 1
  1204.     vec3Dpos(vindex).x = (0 - .5) * VolX
  1205.     vec3Dpos(vindex).y = (1 - .5) * VolY
  1206.     vec3Dpos(vindex).z = (0 - .5) * VolZ
  1207.     vec3Dcolor(vindex) = ShadeA
  1208.     vindex = vindex + 1
  1209.     vec3Dpos(vindex).x = (1 - .5) * VolX
  1210.     vec3Dpos(vindex).y = (1 - .5) * VolY
  1211.     vec3Dpos(vindex).z = (0 - .5) * VolZ
  1212.     vec3Dcolor(vindex) = ShadeA
  1213.  
  1214.     vindex = vindex + 1
  1215.     vec3Dpos(vindex).x = (0 - .5) * VolX
  1216.     vec3Dpos(vindex).y = (0 - .5) * VolY
  1217.     vec3Dpos(vindex).z = (1 - .5) * VolZ
  1218.     vec3Dcolor(vindex) = ShadeA
  1219.     vindex = vindex + 1
  1220.     vec3Dpos(vindex).x = (1 - .5) * VolX
  1221.     vec3Dpos(vindex).y = (0 - .5) * VolY
  1222.     vec3Dpos(vindex).z = (1 - .5) * VolZ
  1223.     vec3Dcolor(vindex) = ShadeA
  1224.     vindex = vindex + 1
  1225.     vec3Dpos(vindex).x = (0 - .5) * VolX
  1226.     vec3Dpos(vindex).y = (1 - .5) * VolY
  1227.     vec3Dpos(vindex).z = (1 - .5) * VolZ
  1228.     vec3Dcolor(vindex) = ShadeA
  1229.     vindex = vindex + 1
  1230.     vec3Dpos(vindex).x = (1 - .5) * VolX
  1231.     vec3Dpos(vindex).y = (1 - .5) * VolY
  1232.     vec3Dpos(vindex).z = (1 - .5) * VolZ
  1233.     vec3Dcolor(vindex) = ShadeA
  1234.  
  1235.     Group(g).LastVector = vindex
  1236.     NewWireCube& = g
  1237.  
  1238. ' Linked list utility.
  1239.  
  1240. Function LatestGroupIdentity& (StartingID As Long)
  1241.     Dim TheReturn As Long
  1242.     Dim As Long p, q
  1243.     p = StartingID
  1244.     If (p = 0) Then
  1245.         q = 0
  1246.     Else
  1247.         Do
  1248.             q = p
  1249.             p = Group(q).Pointer
  1250.             If (p = -999) Then Exit Do
  1251.         Loop
  1252.     End If
  1253.     TheReturn = q
  1254.     LatestGroupIdentity& = TheReturn
  1255.  
  1256. Function LatestClusterIdentity& (StartingID As Long)
  1257.     Dim TheReturn As Long
  1258.     Dim As Long p, q
  1259.     p = StartingID
  1260.     If (p = 0) Then
  1261.         q = 0
  1262.     Else
  1263.         Do
  1264.             q = p
  1265.             p = Cluster(q).Pointer
  1266.             If (p = -999) Then Exit Do
  1267.         Loop
  1268.     End If
  1269.     TheReturn = q
  1270.     LatestClusterIdentity& = TheReturn
  1271.  
  1272. Function NewGroup& (LagAddressIn As Long, CenterX As Double, CenterY As Double, CenterZ As Double, ClusterSize As Integer, TheDynamic As Integer, Framing As Integer)
  1273.     Dim As Long g0
  1274.     g0 = LatestGroupIdentity&(LagAddressIn)
  1275.     GroupIdTicker = GroupIdTicker + 1
  1276.     Group(GroupIdTicker).Identity = GroupIdTicker
  1277.     Group(GroupIdTicker).Pointer = -999
  1278.     Group(GroupIdTicker).Lagger = g0
  1279.     Group(GroupIdTicker).Centroid.x = CenterX
  1280.     Group(GroupIdTicker).Centroid.y = CenterY
  1281.     Group(GroupIdTicker).Centroid.z = CenterZ
  1282.     Group(GroupIdTicker).FrameLength = 0
  1283.     Group(GroupIdTicker).ActiveFrame = 0
  1284.     If (Group(GroupIdTicker).Lagger <> 0) Then
  1285.         Group(g0).Pointer = GroupIdTicker
  1286.     End If
  1287.  
  1288.     ' Adjust corresponding cluster.
  1289.     ClusterFillCounter = ClusterFillCounter + 1
  1290.     If (ClusterFillCounter = 1) Then
  1291.         Call NewCluster(1, Group(GroupIdTicker).Identity, TheDynamic, Framing) '''
  1292.     End If
  1293.     If (ClusterFillCounter = ClusterSize) Then
  1294.         Call ClusterPinch(Group(GroupIdTicker).Identity)
  1295.     End If
  1296.  
  1297.     NewGroup& = Group(GroupIdTicker).Identity
  1298.  
  1299.  
  1300. Sub NewCluster (ClusterLagIn As Long, FirstGroupIn As Long, TheDynamic As Integer, Framing As Integer)
  1301.     Dim As Long k0
  1302.     If (ClusterIdTicker = 0) Then
  1303.         k0 = -1
  1304.     Else
  1305.         k0 = LatestClusterIdentity&(ClusterLagIn)
  1306.     End If
  1307.     ClusterIdTicker = ClusterIdTicker + 1
  1308.     Cluster(ClusterIdTicker).Identity = ClusterIdTicker
  1309.     Cluster(ClusterIdTicker).Pointer = -999
  1310.     Cluster(ClusterIdTicker).Lagger = k0
  1311.     Cluster(ClusterIdTicker).FirstGroup = FirstGroupIn
  1312.     Cluster(ClusterIdTicker).MotionType = TheDynamic
  1313.     Cluster(ClusterIdTicker).Framed = Framing
  1314.     If (ClusterIdTicker > 1) Then Cluster(k0).Pointer = ClusterIdTicker
  1315.  
  1316.  
  1317.  
  1318. Sub RemoveGroup (TheAddressIn As Long)
  1319.     Dim As Long g, p, l, k, ci
  1320.     Dim As Integer f
  1321.     g = TheAddressIn
  1322.  
  1323.     f = 0
  1324.     k = 1
  1325.     Do
  1326.         If (Cluster(k).FirstGroup = g) And (Cluster(k).LastGroup <> g) Then
  1327.             f = 1
  1328.             ci = k
  1329.             Exit Do
  1330.         End If
  1331.         If (Cluster(k).FirstGroup <> g) And (Cluster(k).LastGroup = g) Then
  1332.             f = 2
  1333.             ci = k
  1334.             Exit Do
  1335.         End If
  1336.         If ((Cluster(k).LastGroup = g) And (Cluster(k).LastGroup = g)) Then
  1337.             f = 3
  1338.             ci = k
  1339.             Exit Do
  1340.         End If
  1341.         k = Cluster(k).Pointer
  1342.         If (k = -999) Then Exit Do
  1343.     Loop
  1344.  
  1345.     Select Case f
  1346.         Case 0
  1347.             p = Group(g).Pointer
  1348.             l = Group(g).Lagger
  1349.             Group(l).Pointer = p
  1350.             If (p <> -999) Then
  1351.                 Group(p).Lagger = l
  1352.             End If
  1353.  
  1354.         Case 1
  1355.             p = Group(g).Pointer
  1356.             l = Group(g).Lagger
  1357.             Group(l).Pointer = p
  1358.             If (p <> -999) Then
  1359.                 Group(p).Lagger = l
  1360.             End If
  1361.  
  1362.             Cluster(ci).FirstGroup = p
  1363.             Call ClusterCentroidCalc(ci)
  1364.         Case 2
  1365.             p = Group(g).Pointer
  1366.             l = Group(g).Lagger
  1367.             Group(l).Pointer = p
  1368.             If (p <> -999) Then
  1369.                 Group(p).Lagger = l
  1370.             End If
  1371.  
  1372.             Cluster(ci).LastGroup = l
  1373.             Call ClusterCentroidCalc(ci)
  1374.         Case 3
  1375.             p = Group(g).Pointer
  1376.             l = Group(g).Lagger
  1377.             Group(l).Pointer = p
  1378.             If (p <> -999) Then
  1379.                 Group(p).Lagger = l
  1380.             End If
  1381.  
  1382.             Call RemoveCluster(ci)
  1383.     End Select
  1384.  
  1385. Sub RemoveCluster (TheAddressIn As Long)
  1386.     Dim As Long k, p, l
  1387.     k = TheAddressIn
  1388.     p = Cluster(k).Pointer
  1389.     l = Cluster(k).Lagger
  1390.     If (l <> -1) Then
  1391.         Cluster(l).Pointer = p
  1392.     End If
  1393.     If (p <> -999) Then
  1394.         Cluster(p).Lagger = l
  1395.     End If
  1396.  
  1397. Sub ClusterPinch (TheLastGroup As Long)
  1398.     ClusterFillCounter = 0
  1399.     Cluster(ClusterIdTicker).LastGroup = TheLastGroup
  1400.     Call ClusterCentroidCalc(ClusterIdTicker)
  1401.  
  1402. Sub ClusterCentroidCalc (TheCluster As Long)
  1403.     Dim As Long g
  1404.     Dim As Integer n
  1405.     Cluster(TheCluster).Centroid.x = 0
  1406.     Cluster(TheCluster).Centroid.y = 0
  1407.     Cluster(TheCluster).Centroid.z = 0
  1408.     g = Cluster(TheCluster).FirstGroup
  1409.     n = 0
  1410.     Do
  1411.         Cluster(TheCluster).Centroid.x = Cluster(TheCluster).Centroid.x + Group(g).Centroid.x
  1412.         Cluster(TheCluster).Centroid.y = Cluster(TheCluster).Centroid.y + Group(g).Centroid.y
  1413.         Cluster(TheCluster).Centroid.z = Cluster(TheCluster).Centroid.z + Group(g).Centroid.z
  1414.         n = n + 1
  1415.         If (g = Cluster(TheCluster).LastGroup) Then Exit Do
  1416.         g = Group(g).Pointer
  1417.     Loop
  1418.     Cluster(TheCluster).Centroid.x = Cluster(TheCluster).Centroid.x / n
  1419.     Cluster(TheCluster).Centroid.y = Cluster(TheCluster).Centroid.y / n
  1420.     Cluster(TheCluster).Centroid.z = Cluster(TheCluster).Centroid.z / n
  1421.  
  1422. ' Player Dynamics
  1423.  
  1424. Sub PlayerDynamics
  1425.     If (ToggleAnimate = 1) Then
  1426.  
  1427.         ' Player kinematics
  1428.         PlayerCamera.Velocity.x = PlayerCamera.Velocity.x + PlayerCamera.Acceleration.x
  1429.         PlayerCamera.Velocity.y = PlayerCamera.Velocity.y + PlayerCamera.Acceleration.y
  1430.         PlayerCamera.Velocity.z = PlayerCamera.Velocity.z + PlayerCamera.Acceleration.z
  1431.         PlayerCamera.Velocity.x = .95 * PlayerCamera.Velocity.x
  1432.         PlayerCamera.Velocity.y = .95 * PlayerCamera.Velocity.y
  1433.         PlayerCamera.Velocity.z = .95 * PlayerCamera.Velocity.z
  1434.         PlayerCamera.Position.x = PlayerCamera.Position.x + PlayerCamera.Velocity.x
  1435.         PlayerCamera.Position.y = PlayerCamera.Position.y + PlayerCamera.Velocity.y
  1436.         PlayerCamera.Position.z = PlayerCamera.Position.z + PlayerCamera.Velocity.z
  1437.  
  1438.         ' Terrain traversal.
  1439.         Dim As Double qi, qj
  1440.         Dim As Integer wi, wj
  1441.         qi = (PlayerCamera.Position.x) / BlockSize + UBound(WorldMesh, 1) / 2
  1442.         qj = (PlayerCamera.Position.y) / BlockSize + UBound(WorldMesh, 2) / 2
  1443.         wi = 1 + Int(qi)
  1444.         wj = 1 + Int(qj)
  1445.         If (wi < 1) Then wi = 1
  1446.         If (wj < 1) Then wj = 1
  1447.         If (wi > UBound(WorldMesh, 1)) Then wi = UBound(WorldMesh, 1)
  1448.         If (wj > UBound(WorldMesh, 2)) Then wj = UBound(WorldMesh, 2)
  1449.         If (PlayerCamera.Velocity.z = 0) Then
  1450.             PlayerCamera.Position.z = PlayerCamera.Position.z + .15 * ((40 + WorldMesh(wi, wj) - PlayerCamera.Position.z))
  1451.         End If
  1452.  
  1453.         ' Collision with ground after jump.
  1454.         If ((PlayerCamera.Velocity.z <> 0) And (PlayerCamera.Position.z < (40 + WorldMesh(wi, wj)))) Then
  1455.             PlayerCamera.Acceleration.z = 0
  1456.             PlayerCamera.Velocity.z = 0
  1457.         End If
  1458.  
  1459.         ' Collision with tornado.
  1460.         If (Group(ClosestGroup).Label = "Tornado") Then
  1461.             PlayerCamera.Velocity.x = (Rnd - .5) * 20
  1462.             PlayerCamera.Velocity.y = (Rnd - .5) * 20
  1463.             PlayerCamera.Velocity.z = 20
  1464.             PlayerCamera.Acceleration.z = -.5
  1465.         End If
  1466.  
  1467.         'Un-zoom camera.
  1468.         If ((fovd <> -192) And (_KeyDown(90) = 0) And (_KeyDown(122) = 0)) Then
  1469.             fovd = Int(.5 * (fovd - 192)) + 1
  1470.             farplane(4) = -256 'Int(.5 * (farplane(4) - 256))
  1471.             Call CalculateClippingPlanes(_Width, _Height)
  1472.         End If
  1473.  
  1474.     End If
  1475.  
  1476. ' Compute Visible Scene
  1477.  
  1478. Sub ComputeVisibleScene
  1479.     Dim As Long g, k
  1480.     Dim As Double dx, dy, dz
  1481.     Dim closestdist2 As Double
  1482.     Dim fp42 As Double
  1483.     Dim dist2 As Double
  1484.     Dim GroupInView As Integer
  1485.     ClosestGroup = 1
  1486.     closestdist2 = 10000000
  1487.     fp42 = farplane(4) * farplane(4)
  1488.  
  1489.     k = 1
  1490.     Do
  1491.         dx = Cluster(k).Centroid.x - PlayerCamera.Position.x
  1492.         dy = Cluster(k).Centroid.y - PlayerCamera.Position.y
  1493.         dz = Cluster(k).Centroid.z - PlayerCamera.Position.z
  1494.         dist2 = dx * dx + dy * dy + dz * dz
  1495.         '''
  1496.         If k = SunClusterAddress And Cluster(k).Centroid.z > 0 Then GoTo 100
  1497.         If k = MoonClusterAddress And Cluster(k).Centroid.z > 0 Then GoTo 100
  1498.         '''
  1499.         If (dist2 > 600 * 600) Then
  1500.             Cluster(k).Visible = 0
  1501.             If ((Cluster(k).MotionType <> 0) And (ToggleAnimate = 1)) Then
  1502.                 Call EvolveCluster(k)
  1503.             End If
  1504.         Else
  1505.             '''
  1506.            100
  1507.             '''
  1508.             Cluster(k).Visible = 1
  1509.             g = Cluster(k).FirstGroup
  1510.             If ((Cluster(k).MotionType <> 0) And (ToggleAnimate = 1)) Then
  1511.                 Call EvolveCluster(k)
  1512.             End If
  1513.             Do
  1514.                 dx = Group(g).Centroid.x - PlayerCamera.Position.x
  1515.                 dy = Group(g).Centroid.y - PlayerCamera.Position.y
  1516.                 dz = Group(g).Centroid.z - PlayerCamera.Position.z
  1517.                 dist2 = dx * dx + dy * dy + dz * dz
  1518.                 Group(g).Visible = 0
  1519.                 '''
  1520.                 If k = SunClusterAddress Then GoTo 200
  1521.                 If k = MoonClusterAddress Then GoTo 200
  1522.                 '''
  1523.  
  1524.                 If (dist2 < fp42) Then
  1525.                     '''
  1526.                    200
  1527.                     '''
  1528.                     GroupInView = 1
  1529.                     If dx * nearplane(1) + dy * nearplane(2) + dz * nearplane(3) - nearplane(4) < 0 Then GroupInView = 0
  1530.                     'IF dx * farplane(1) + dy * farplane(2) + dz * farplane(3) - farplane(4) < 0 THEN groupinview = 0 ''' Redundant
  1531.                     If dx * rightplane(1) + dy * rightplane(2) + dz * rightplane(3) - rightplane(4) < 0 Then GroupInView = 0
  1532.                     If dx * leftplane(1) + dy * leftplane(2) + dz * leftplane(3) - leftplane(4) < 0 Then GroupInView = 0
  1533.                     If dx * topplane(1) + dy * topplane(2) + dz * topplane(3) - topplane(4) < 0 Then GroupInView = 0
  1534.                     If dx * bottomplane(1) + dy * bottomplane(2) + dz * bottomplane(3) - bottomplane(4) < 0 Then GroupInView = 0
  1535.                     If (GroupInView = 1) Then
  1536.                         Group(g).Visible = 1
  1537.                         If (dist2 < closestdist2) Then
  1538.                             closestdist2 = dist2
  1539.                             ClosestGroup = g
  1540.                         End If
  1541.                         Group(g).Distance2 = dist2
  1542.                         If (ToggleAnimate = 1) And (Group(g).FrameLength = 0) Then Call EvolveVectors(g)
  1543.  
  1544.                         '''
  1545.                         If k = SunClusterAddress Or k = MoonClusterAddress Then
  1546.                             If PlayerCamera.Position.z < -40 Then
  1547.                                 Call ProjectGroup(g, Group(g).FirstVector, Group(g).LastVector, 1)
  1548.                             Else
  1549.                                 Call ProjectGroup(g, Group(g).FirstVector, Group(g).LastVector, 0)
  1550.                             End If
  1551.                         Else
  1552.                             If Group(g).FrameLength <> 0 And Group(g).ActiveFrame <> 0 Then
  1553.                                 Call ProjectGroup(g, Group(g).FirstVector + Group(g).ActiveFrame * Group(g).FrameLength, Group(g).FirstVector + Group(g).ActiveFrame * Group(g).FrameLength + Group(g).FrameLength, 1)
  1554.                             Else
  1555.                                 Call ProjectGroup(g, Group(g).FirstVector, Group(g).LastVector, 1)
  1556.                             End If
  1557.                         End If
  1558.                         '''
  1559.  
  1560.                     End If
  1561.                 End If
  1562.                 If (g = Cluster(k).LastGroup) Then Exit Do
  1563.                 g = Group(g).Pointer
  1564.             Loop
  1565.         End If
  1566.  
  1567.         k = Cluster(k).Pointer
  1568.         If (k = -999) Then Exit Do
  1569.     Loop
  1570.  
  1571. Sub CalculateScreenVectors
  1572.     Dim As Double mag
  1573.     mag = 1 / Sqr(uhat(1) * uhat(1) + uhat(2) * uhat(2) + uhat(3) * uhat(3))
  1574.     uhat(1) = uhat(1) * mag: uhat(2) = uhat(2) * mag: uhat(3) = uhat(3) * mag
  1575.     mag = 1 / Sqr(vhat(1) * vhat(1) + vhat(2) * vhat(2) + vhat(3) * vhat(3))
  1576.     vhat(1) = vhat(1) * mag: vhat(2) = vhat(2) * mag: vhat(3) = vhat(3) * mag
  1577.     nhat(1) = uhat(2) * vhat(3) - uhat(3) * vhat(2)
  1578.     nhat(2) = uhat(3) * vhat(1) - uhat(1) * vhat(3)
  1579.     nhat(3) = uhat(1) * vhat(2) - uhat(2) * vhat(1)
  1580.     Call CalculateClippingPlanes(_Width, _Height)
  1581.  
  1582. Sub CalculateClippingPlanes (TheWidth As Double, TheHeight As Double)
  1583.     Dim As Double h2, w2, h2f, w2f, h2w2, mag
  1584.     h2 = TheHeight * .5
  1585.     w2 = TheWidth * .5
  1586.     h2f = h2 * fovd
  1587.     w2f = w2 * fovd
  1588.     h2w2 = h2 * w2
  1589.     nearplane(1) = -nhat(1)
  1590.     nearplane(2) = -nhat(2)
  1591.     nearplane(3) = -nhat(3)
  1592.     farplane(1) = nhat(1)
  1593.     farplane(2) = nhat(2)
  1594.     farplane(3) = nhat(3)
  1595.     rightplane(1) = h2f * uhat(1) - h2w2 * nhat(1)
  1596.     rightplane(2) = h2f * uhat(2) - h2w2 * nhat(2)
  1597.     rightplane(3) = h2f * uhat(3) - h2w2 * nhat(3)
  1598.     mag = 1 / Sqr(rightplane(1) * rightplane(1) + rightplane(2) * rightplane(2) + rightplane(3) * rightplane(3))
  1599.     rightplane(1) = rightplane(1) * mag
  1600.     rightplane(2) = rightplane(2) * mag
  1601.     rightplane(3) = rightplane(3) * mag
  1602.     leftplane(1) = -h2f * uhat(1) - h2w2 * nhat(1)
  1603.     leftplane(2) = -h2f * uhat(2) - h2w2 * nhat(2)
  1604.     leftplane(3) = -h2f * uhat(3) - h2w2 * nhat(3)
  1605.     mag = 1 / Sqr(leftplane(1) * leftplane(1) + leftplane(2) * leftplane(2) + leftplane(3) * leftplane(3))
  1606.     leftplane(1) = leftplane(1) * mag
  1607.     leftplane(2) = leftplane(2) * mag
  1608.     leftplane(3) = leftplane(3) * mag
  1609.     topplane(1) = w2f * vhat(1) - h2w2 * nhat(1)
  1610.     topplane(2) = w2f * vhat(2) - h2w2 * nhat(2)
  1611.     topplane(3) = w2f * vhat(3) - h2w2 * nhat(3)
  1612.     mag = 1 / Sqr(topplane(1) * topplane(1) + topplane(2) * topplane(2) + topplane(3) * topplane(3))
  1613.     topplane(1) = topplane(1) * mag
  1614.     topplane(2) = topplane(2) * mag
  1615.     topplane(3) = topplane(3) * mag
  1616.     bottomplane(1) = -w2f * vhat(1) - h2w2 * nhat(1)
  1617.     bottomplane(2) = -w2f * vhat(2) - h2w2 * nhat(2)
  1618.     bottomplane(3) = -w2f * vhat(3) - h2w2 * nhat(3)
  1619.     mag = 1 / Sqr(bottomplane(1) * bottomplane(1) + bottomplane(2) * bottomplane(2) + bottomplane(3) * bottomplane(3))
  1620.     bottomplane(1) = bottomplane(1) * mag
  1621.     bottomplane(2) = bottomplane(2) * mag
  1622.     bottomplane(3) = bottomplane(3) * mag
  1623.  
  1624. Sub ProjectGroup (TheGroup As Long, LowIndex As Long, HighIndex As Long, GraySwitch As Integer)
  1625.     Dim As Vector3 vec(UBound(vec3Dpos))
  1626.     Dim As Integer vectorinview
  1627.     Dim As Double vec3ddotnhat
  1628.     Dim i As Long
  1629.     Dim f As Integer
  1630.     For i = LowIndex To HighIndex
  1631.         vec(i).x = Group(TheGroup).Centroid.x + vec3Dpos(i).x - PlayerCamera.Position.x
  1632.         vec(i).y = Group(TheGroup).Centroid.y + vec3Dpos(i).y - PlayerCamera.Position.y
  1633.         vec(i).z = Group(TheGroup).Centroid.z + vec3Dpos(i).z - PlayerCamera.Position.z
  1634.         f = -1
  1635.         vec3Dvis(i) = 0
  1636.         vectorinview = 1
  1637.         If vec(i).x * nearplane(1) + vec(i).y * nearplane(2) + vec(i).z * nearplane(3) - nearplane(4) < 0 Then vectorinview = 0
  1638.         'IF vec(i).x * farplane(1) + vec(i).y * farplane(2) + vec(i).z* farplane(3) - farplane(4) < 0 THEN vectorinview = 0
  1639.         If vec(i).x * farplane(1) + vec(i).y * farplane(2) + vec(i).z * farplane(3) - farplane(4) * .85 < 0 Then f = 1
  1640.         'IF vec(i).x * rightplane(1) + vec(i).y * rightplane(2) + vec(i).z * rightplane(3) - rightplane(4) < 0 THEN vectorinview = 0
  1641.         'IF vec(i).x * leftplane(1) + vec(i).y * leftplane(2) + vec(i).z * leftplane(3) - leftplane(4) < 0 THEN vectorinview = 0
  1642.         'IF vec(i).x * topplane(1) + vec(i).y * topplane(2) + vec(i).z * topplane(3) - topplane(4) < 0 THEN vectorinview = 0
  1643.         'IF vec(i).x * bottomplane(1) + vec(i).y * bottomplane(2) + vec(i).z* bottomplane(3) - bottomplane(4) < 0 THEN vectorinview = 0
  1644.         If (vectorinview = 1) Then
  1645.             vec3Dvis(i) = 1
  1646.             vec3ddotnhat = vec(i).x * nhat(1) + vec(i).y * nhat(2) + vec(i).z * nhat(3)
  1647.             vec2D(i).u = (vec(i).x * uhat(1) + vec(i).y * uhat(2) + vec(i).z * uhat(3)) * fovd / vec3ddotnhat
  1648.             vec2D(i).v = (vec(i).x * vhat(1) + vec(i).y * vhat(2) + vec(i).z * vhat(3)) * fovd / vec3ddotnhat
  1649.             If ((GraySwitch = 1) And (f = 1)) Then
  1650.                 vec2Dcolor(i) = Gray
  1651.             Else
  1652.                 vec2Dcolor(i) = vec3Dcolor(i)
  1653.             End If
  1654.         End If
  1655.     Next
  1656.  
  1657. Sub EvolveCluster (TheCluster As Long)
  1658.     Dim u As Long
  1659.     Dim k As Long
  1660.     Dim As Single xx, yy, zz ' Needs to be single otherwise the fish flip flop. wtf?
  1661.     Dim As Double x0, y0 ', z0
  1662.     Dim As Double dx, dy, dz
  1663.     Dim As Double t
  1664.     Dim As Double v
  1665.     'Dim xx, yy, zz, x0, y0, dx, dy, dz, t, v As Double
  1666.     Select Case Cluster(TheCluster).MotionType
  1667.         Case 0
  1668.             ' Do nothing.
  1669.  
  1670.         Case -1
  1671.             ' Freefall and explode.
  1672.             Cluster(TheCluster).Velocity.x = Cluster(TheCluster).Velocity.x + Cluster(TheCluster).Acceleration.x
  1673.             Cluster(TheCluster).Velocity.y = Cluster(TheCluster).Velocity.y + Cluster(TheCluster).Acceleration.y
  1674.             Cluster(TheCluster).Velocity.z = Cluster(TheCluster).Velocity.z + Cluster(TheCluster).Acceleration.z
  1675.             dx = Cluster(TheCluster).Velocity.x
  1676.             dy = Cluster(TheCluster).Velocity.y
  1677.             dz = Cluster(TheCluster).Velocity.z
  1678.             If ((dx <> 0) Or (dy <> 0) Or (dz <> 0)) Then
  1679.                 Call TranslateCluster(TheCluster, dx, dy, dz)
  1680.             End If
  1681.             Dim wi As Integer
  1682.             Dim wj As Integer
  1683.             wi = 1 + Int((Cluster(TheCluster).Centroid.x) / BlockSize + UBound(WorldMesh, 1) / 2)
  1684.             wj = 1 + Int((Cluster(TheCluster).Centroid.y) / BlockSize + UBound(WorldMesh, 2) / 2)
  1685.             If (Cluster(TheCluster).Centroid.z <= WorldMesh(wi, wj)) Then
  1686.                 Cluster(TheCluster).Acceleration.x = 0
  1687.                 Cluster(TheCluster).Acceleration.y = 0
  1688.                 Cluster(TheCluster).Acceleration.z = 0
  1689.                 Cluster(TheCluster).Velocity.x = 0
  1690.                 Cluster(TheCluster).Velocity.y = 0
  1691.                 Cluster(TheCluster).Velocity.z = 0
  1692.                 Cluster(TheCluster).MotionType = -9
  1693.                 Cluster(TheCluster).DeathTimer = Timer + 2
  1694.                 k = Cluster(TheCluster).FirstGroup
  1695.                 Do
  1696.                     Group(k).Volume.x = BlockSize * 3
  1697.                     Group(k).Volume.y = BlockSize * 3
  1698.                     Group(k).Volume.z = BlockSize * 3
  1699.                     For u = Group(k).FirstVector To Group(k).LastVector
  1700.                         vec3Dvel(u).x = (Rnd - .5) * .8
  1701.                         vec3Dvel(u).y = (Rnd - .5) * .8
  1702.                         vec3Dvel(u).z = (Rnd - 0) * .8
  1703.                     Next
  1704.                     If (k = Cluster(TheCluster).LastGroup) Then Exit Do
  1705.                     k = Group(k).Pointer
  1706.                 Loop
  1707.             End If
  1708.  
  1709.         Case -2
  1710.             ' Freefall and stack.
  1711.             Cluster(TheCluster).Velocity.x = Cluster(TheCluster).Velocity.x + Cluster(TheCluster).Acceleration.x
  1712.             Cluster(TheCluster).Velocity.y = Cluster(TheCluster).Velocity.y + Cluster(TheCluster).Acceleration.y
  1713.             Cluster(TheCluster).Velocity.z = Cluster(TheCluster).Velocity.z + Cluster(TheCluster).Acceleration.z
  1714.             dx = Cluster(TheCluster).Velocity.x
  1715.             dy = Cluster(TheCluster).Velocity.y
  1716.             dz = Cluster(TheCluster).Velocity.z
  1717.             If ((dx <> 0) Or (dy <> 0) Or (dz <> 0)) Then
  1718.                 Call TranslateCluster(TheCluster, dx, dy, dz)
  1719.             End If
  1720.             wi = 1 + Int((Cluster(TheCluster).Centroid.x) / BlockSize + UBound(WorldMesh, 1) / 2)
  1721.             wj = 1 + Int((Cluster(TheCluster).Centroid.y) / BlockSize + UBound(WorldMesh, 2) / 2)
  1722.             If (Cluster(TheCluster).Centroid.z <= WorldMesh(wi, wj)) Then
  1723.                 Cluster(TheCluster).Acceleration.x = 0
  1724.                 Cluster(TheCluster).Acceleration.y = 0
  1725.                 Cluster(TheCluster).Acceleration.z = 0
  1726.                 Cluster(TheCluster).Velocity.x = 0
  1727.                 Cluster(TheCluster).Velocity.y = 0
  1728.                 Cluster(TheCluster).Velocity.z = 0
  1729.                 Cluster(TheCluster).MotionType = 0
  1730.                 WorldMesh(wi, wj) = WorldMesh(wi, wj) + BlockSize / 3
  1731.             End If
  1732.  
  1733.         Case -9
  1734.             If (Timer >= Cluster(TheCluster).DeathTimer) Then
  1735.                 Call RemoveCluster(TheCluster)
  1736.             End If
  1737.  
  1738.         Case Else
  1739.             ' Fixed path.
  1740.             ' Note: This chunk of code is subject to the midnight bug.
  1741.             t = Timer
  1742.             u = Int(t)
  1743.             v = t - u
  1744.             xx = v * FixedPath(Cluster(TheCluster).MotionType, u).x + (1 - v) * FixedPath(Cluster(TheCluster).MotionType, u - 1).x
  1745.             yy = v * FixedPath(Cluster(TheCluster).MotionType, u).y + (1 - v) * FixedPath(Cluster(TheCluster).MotionType, u - 1).y
  1746.             zz = v * FixedPath(Cluster(TheCluster).MotionType, u).z + (1 - v) * FixedPath(Cluster(TheCluster).MotionType, u - 1).z
  1747.  
  1748.             ' Choose frame based on derived velocity vector.
  1749.             If (Cluster(TheCluster).Framed = 1) Then
  1750.                 k = Cluster(TheCluster).FirstGroup
  1751.                 Do
  1752.                     If (Group(k).FrameLength <> 0) Then
  1753.                         x0 = Group(k).Centroid.x
  1754.                         y0 = Group(k).Centroid.y
  1755.                         'z0 = Group(k).Centroid.z
  1756.                         dx = xx - x0
  1757.                         dy = yy - y0
  1758.                         'dz = zz - z0
  1759.                         If ((dx > 0) And (dy > 0)) Then Group(k).ActiveFrame = 1 + Int(((48 / (2 * pi)) * (Atn(dy / dx))))
  1760.                         If ((dx < 0) And (dy > 0)) Then Group(k).ActiveFrame = 1 + 24 + Int(((48 / (2 * pi)) * (Atn(dy / dx))))
  1761.                         If ((dx < 0) And (dy < 0)) Then Group(k).ActiveFrame = 1 + 24 + Int(((48 / (2 * pi)) * (Atn(dy / dx))))
  1762.                         If ((dx > 0) And (dy < 0)) Then Group(k).ActiveFrame = 1 + 48 + Int(((48 / (2 * pi)) * (Atn(dy / dx))))
  1763.                     End If
  1764.                     If (k = Cluster(TheCluster).LastGroup) Then Exit Do
  1765.                     k = Group(k).Pointer
  1766.                 Loop
  1767.             End If
  1768.  
  1769.             Call PlaceCluster(TheCluster, xx, yy, zz)
  1770.  
  1771.     End Select
  1772.  
  1773. Sub PlaceCluster (TheCluster As Long, xc As Double, yc As Double, zc As Double)
  1774.     Dim As Long g
  1775.     Dim As Double x0, y0, z0
  1776.     x0 = Cluster(TheCluster).Centroid.x
  1777.     y0 = Cluster(TheCluster).Centroid.y
  1778.     z0 = Cluster(TheCluster).Centroid.z
  1779.     Cluster(TheCluster).Centroid.x = xc
  1780.     Cluster(TheCluster).Centroid.y = yc
  1781.     Cluster(TheCluster).Centroid.z = zc
  1782.     g = Cluster(TheCluster).FirstGroup
  1783.     Do
  1784.         Group(g).Centroid.x = Group(g).Centroid.x + xc - x0
  1785.         Group(g).Centroid.y = Group(g).Centroid.y + yc - y0
  1786.         Group(g).Centroid.z = Group(g).Centroid.z + zc - z0
  1787.         If (g = Cluster(TheCluster).LastGroup) Then Exit Do
  1788.         g = Group(g).Pointer
  1789.     Loop
  1790.  
  1791. Sub TranslateCluster (TheCluster As Long, dx As Double, dy As Double, dz As Double)
  1792.     Dim As Long g
  1793.     g = Cluster(TheCluster).FirstGroup
  1794.     Do
  1795.         Group(g).Centroid.x = Group(g).Centroid.x + dx
  1796.         Group(g).Centroid.y = Group(g).Centroid.y + dy
  1797.         Group(g).Centroid.z = Group(g).Centroid.z + dz
  1798.         If (g = Cluster(TheCluster).LastGroup) Then Exit Do
  1799.         g = Group(g).Pointer
  1800.     Loop
  1801.     Cluster(TheCluster).Centroid.x = Cluster(TheCluster).Centroid.x + dx
  1802.     Cluster(TheCluster).Centroid.y = Cluster(TheCluster).Centroid.y + dy
  1803.     Cluster(TheCluster).Centroid.z = Cluster(TheCluster).Centroid.z + dz
  1804.  
  1805. Sub EvolveVectors (TheGroup As Long)
  1806.     Dim As Long g
  1807.     Dim As Double xdim, ydim, zdim
  1808.     Dim As Double dx, dy, dz
  1809.     Dim As Double px, py, pz
  1810.  
  1811.     xdim = Group(TheGroup).Volume.x
  1812.     ydim = Group(TheGroup).Volume.y
  1813.     zdim = Group(TheGroup).Volume.z
  1814.  
  1815.     For g = Group(TheGroup).FirstVector To Group(TheGroup).LastVector
  1816.  
  1817.         ' Position update with periodic boundaries inside group volume.
  1818.         dx = vec3Dvel(g).x
  1819.         dy = vec3Dvel(g).y
  1820.         dz = vec3Dvel(g).z
  1821.         If (dx <> 0) Then
  1822.             px = vec3Dpos(g).x + dx
  1823.             If Abs(px) > xdim / 2 Then
  1824.                 If (px > xdim / 2) Then
  1825.                     px = -xdim / 2
  1826.                 Else
  1827.                     px = xdim / 2
  1828.                 End If
  1829.             End If
  1830.             vec3Dpos(g).x = px
  1831.         End If
  1832.         If (dy <> 0) Then
  1833.             py = vec3Dpos(g).y + dy
  1834.             If Abs(py) > ydim / 2 Then
  1835.                 If (py > ydim / 2) Then
  1836.                     py = -ydim / 2
  1837.                 Else
  1838.                     py = ydim / 2
  1839.                 End If
  1840.             End If
  1841.             vec3Dpos(g).y = py
  1842.         End If
  1843.         If (dz <> 0) Then
  1844.             pz = vec3Dpos(g).z + dz
  1845.             If Abs(pz) > zdim / 2 Then
  1846.                 If (pz > zdim / 2) Then
  1847.                     pz = -zdim / 2
  1848.                 Else
  1849.                     pz = zdim / 2
  1850.                 End If
  1851.             End If
  1852.             vec3Dpos(g).z = pz
  1853.         End If
  1854.     Next
  1855.  
  1856. Sub SetParticleVelocity (TheGroup As Long, vx As Double, vy As Double, vz As Double)
  1857.     Dim As Long j, m, n
  1858.     m = Group(TheGroup).FirstVector
  1859.     n = Group(TheGroup).LastVector
  1860.     For j = m To n
  1861.         vec3Dvel(j).x = vx
  1862.         vec3Dvel(j).y = vy
  1863.         vec3Dvel(j).z = vz
  1864.     Next
  1865.  
  1866. ' Sorting
  1867.  
  1868. Sub QuickSort (LowLimit As Long, HighLimit As Long)
  1869.     Dim As Long piv
  1870.     If (LowLimit < HighLimit) Then
  1871.         piv = Partition(LowLimit, HighLimit)
  1872.         Call QuickSort(LowLimit, piv - 1)
  1873.         Call QuickSort(piv + 1, HighLimit)
  1874.     End If
  1875.  
  1876. Function Partition (LowLimit As Long, HighLimit As Long)
  1877.     Dim As Long i, j
  1878.     Dim As Double pivot, tmp
  1879.     pivot = Group(SortedGroups(HighLimit)).Distance2
  1880.     i = LowLimit - 1
  1881.  
  1882.     For j = LowLimit To HighLimit - 1
  1883.         tmp = Group(SortedGroups(j)).Distance2 - pivot
  1884.         If (tmp >= 0) Then
  1885.             i = i + 1
  1886.             Swap SortedGroups(i), SortedGroups(j)
  1887.         End If
  1888.     Next
  1889.     Swap SortedGroups(i + 1), SortedGroups(HighLimit)
  1890.     Partition = i + 1
  1891.  
  1892. 'Sub BubbleSort
  1893. '    ' Antiquated but works fine.
  1894. '    Dim As Integer i, j
  1895. '    Dim As Double u, v
  1896. '    For j = SortedGroupsCount To 1 Step -1
  1897. '        For i = 2 To SortedGroupsCount
  1898. '            u = Group(SortedGroups(i - 1)).Distance2
  1899. '            v = Group(SortedGroups(i)).Distance2
  1900. '            If (u < v) Then
  1901. '                Swap SortedGroups(i - 1), SortedGroups(i)
  1902. '            End If
  1903. '        Next
  1904. '    Next
  1905. 'End Sub
  1906.  
  1907. ' Graphics
  1908.  
  1909. Sub PlotWorld
  1910.     Dim As Long g, k, p
  1911.     Dim j As Integer
  1912.     Dim lowlim, highlim As Long
  1913.     Dim x1 As Double
  1914.     Dim y1 As Double
  1915.     Dim x2 As Double
  1916.     Dim y2 As Double
  1917.     Dim clrtmp As _Unsigned Long
  1918.     Dim ThePlotMode As Integer
  1919.  
  1920.     NumClusterVisible = 0
  1921.     NumVectorVisible = 0
  1922.  
  1923.     SortedGroupsCount = 0
  1924.     k = 1
  1925.     Do
  1926.         If (Cluster(k).Visible = 1) Then
  1927.             NumClusterVisible = NumClusterVisible + 1
  1928.             g = Cluster(k).FirstGroup
  1929.             Do
  1930.                 If (Group(g).Visible = 1) Then
  1931.                     SortedGroupsCount = SortedGroupsCount + 1
  1932.                     SortedGroups(SortedGroupsCount) = g
  1933.                 End If
  1934.                 If (g = Cluster(k).LastGroup) Then Exit Do
  1935.                 g = Group(g).Pointer
  1936.             Loop
  1937.         End If
  1938.         k = Cluster(k).Pointer
  1939.         If (k = -999) Then Exit Do
  1940.     Loop
  1941.     NumGroupVisible = SortedGroupsCount
  1942.  
  1943.     Call QuickSort(1, SortedGroupsCount)
  1944.     'Call BubbleSort
  1945.  
  1946.     PlayerCamera.Shade = ShadeMix~&(PlayerCamera.Shade, _RGB32(_Red32(vec3Dcolor(Group(ClosestGroup).FirstVector)), _Green32(vec3Dcolor(Group(ClosestGroup).FirstVector)), _Blue32(vec3Dcolor(Group(ClosestGroup).FirstVector)), 200), .01)
  1947.     PlayerCamera.Shade = ShadeMix~&(PlayerCamera.Shade, _RGB32(0, 0, 0, 255), .01)
  1948.     Cls
  1949.     Line (0, 0)-(_Width, _Height), PlayerCamera.Shade, BF
  1950.  
  1951.     For j = 1 To SortedGroupsCount
  1952.         g = SortedGroups(j)
  1953.         ThePlotMode = Group(g).PlotMode
  1954.  
  1955.         If (ThePlotMode = -1) Then ' Wire cube
  1956.             Dim x3 As Double
  1957.             Dim y3 As Double
  1958.             Dim x4 As Double
  1959.             Dim y4 As Double
  1960.             p = Group(g).FirstVector
  1961.             clrtmp = vec2Dcolor(p)
  1962.             x1 = vec2D(p).u: y1 = vec2D(p).v: x2 = vec2D(p + 1).u: y2 = vec2D(p + 1).v: x3 = vec2D(p + 2).u: y3 = vec2D(p + 2).v: x4 = vec2D(p + 4).u: y4 = vec2D(p + 4).v
  1963.             Call CLine(x1, y1, x2, y2, clrtmp)
  1964.             Call CLine(x1, y1, x3, y3, clrtmp)
  1965.             Call CLine(x1, y1, x4, y4, clrtmp)
  1966.             x1 = vec2D(p + 3).u: y1 = vec2D(p + 3).v: x2 = vec2D(p + 1).u: y2 = vec2D(p + 1).v: x3 = vec2D(p + 2).u: y3 = vec2D(p + 2).v: x4 = vec2D(p + 7).u: y4 = vec2D(p + 7).v
  1967.             Call CLine(x1, y1, x2, y2, clrtmp)
  1968.             Call CLine(x1, y1, x3, y3, clrtmp)
  1969.             Call CLine(x1, y1, x4, y4, clrtmp)
  1970.             x1 = vec2D(p + 5).u: y1 = vec2D(p + 5).v: x2 = vec2D(p + 4).u: y2 = vec2D(p + 4).v: x3 = vec2D(p + 7).u: y3 = vec2D(p + 7).v: x4 = vec2D(p + 1).u: y4 = vec2D(p + 1).v
  1971.             Call CLine(x1, y1, x2, y2, clrtmp)
  1972.             Call CLine(x1, y1, x3, y3, clrtmp)
  1973.             Call CLine(x1, y1, x4, y4, clrtmp)
  1974.             x1 = vec2D(p + 6).u: y1 = vec2D(p + 6).v: x2 = vec2D(p + 4).u: y2 = vec2D(p + 4).v: x3 = vec2D(p + 7).u: y3 = vec2D(p + 7).v: x4 = vec2D(p + 2).u: y4 = vec2D(p + 2).v
  1975.             Call CLine(x1, y1, x2, y2, clrtmp)
  1976.             Call CLine(x1, y1, x3, y3, clrtmp)
  1977.             Call CLine(x1, y1, x4, y4, clrtmp)
  1978.         End If
  1979.  
  1980.         If Group(g).ActiveFrame = 0 Then
  1981.             lowlim = Group(g).FirstVector
  1982.             highlim = Group(g).LastVector - 1
  1983.         Else
  1984.             lowlim = Group(g).FirstVector + Group(g).FrameLength * (Group(g).ActiveFrame)
  1985.             highlim = Group(g).FirstVector + Group(g).FrameLength * (Group(g).ActiveFrame + 1) - 2
  1986.         End If
  1987.  
  1988.         For p = lowlim To highlim
  1989.             If (vec3Dvis(p) = 1) Then
  1990.                 NumVectorVisible = NumVectorVisible + 1
  1991.                 If (g = ClosestGroup) Then
  1992.                     clrtmp = Yellow
  1993.                 Else
  1994.                     clrtmp = vec2Dcolor(p)
  1995.                 End If
  1996.                 Select Case ThePlotMode
  1997.                     Case 0
  1998.                         x1 = vec2D(p).u
  1999.                         y1 = vec2D(p).v
  2000.                         Call BlockPoint(x1, y1, clrtmp)
  2001.                     Case 1
  2002.                         x1 = vec2D(p).u
  2003.                         y1 = vec2D(p).v
  2004.                         x2 = vec2D(p + 1).u
  2005.                         y2 = vec2D(p + 1).v
  2006.                         If (((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)) < 225) Then
  2007.                             'Call cline(x1, y1, x2, y2, clrtmp)
  2008.                             Call LineSmooth(x1, y1, x2, y2, clrtmp)
  2009.                         Else
  2010.                             Call CCircle(x1, y1, 1, clrtmp)
  2011.                             '''
  2012.                             'If p = highlim Then Call CCircle(x2, y2, 1, clrtmp)
  2013.                             '''
  2014.                         End If
  2015.                     Case 2
  2016.                         x1 = vec2D(p).u
  2017.                         y1 = vec2D(p).v
  2018.                         Call CCircle(x1, y1, 1, clrtmp)
  2019.                 End Select
  2020.             End If
  2021.         Next
  2022.     Next
  2023.  
  2024.  
  2025. Sub DisplayHUD
  2026.     Dim a As String
  2027.     Call LineSmooth(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)), _RGB32(255, 0, 0, 150))
  2028.     Call LineSmooth(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)), _RGB32(0, 255, 0, 150))
  2029.     Call LineSmooth(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)), _RGB32(30, 144, 255, 150))
  2030.     Call TextCenter(" Closest ", (1) * 16, LimeGreen)
  2031.     a = " " + Group(ClosestGroup).Label + " "
  2032.     Color DarkKhaki
  2033.     _PrintString (_Width / 2 - (Len(a) / 2) * 8, 2 * 16), a
  2034.     Color LimeGreen
  2035.     _PrintString ((1) * 8, _Height - (4) * 16), "   Movement   "
  2036.     Color DarkKhaki
  2037.     _PrintString ((1) * 8, _Height - (3) * 16), "  W  &/or  " + Chr$(30) + "  "
  2038.     _PrintString ((1) * 8, _Height - (2) * 16), "A S D    " + Chr$(17) + " " + Chr$(31) + " " + Chr$(16)
  2039.     Color LimeGreen
  2040.     _PrintString (_Width - (13) * 8, _Height - (4) * 16), "Orientation "
  2041.     Color DarkKhaki
  2042.     _PrintString (_Width - (13) * 8, _Height - (3) * 16), " Keypad 1-9 "
  2043.     If ((nhat(3) <> 0) Or (uhat(3) <> 0)) Then Color Red Else Color Gray
  2044.     _PrintString (_Width - (13) * 8, _Height - (2) * 16), "  5=Level   "
  2045.     Color LimeGreen
  2046.     _PrintString ((1) * 8, (1) * 16), "- Report -"
  2047.     Color DarkKhaki
  2048.     _PrintString ((1) * 8, (2) * 16), "FPS: " + LTrim$(RTrim$(Str$(FPSReport))) + "/" + LTrim$(RTrim$(Str$(FPSTarget)))
  2049.     _PrintString ((1) * 8, (3) * 16), "Particles: " + LTrim$(RTrim$(Str$(NumVectorVisible)))
  2050.     '_PrintString ((1) * 8, (5) * 16), "Clusters: " + LTrim$(RTrim$(Str$(NumClusterVisible)))
  2051.     '_PrintString ((1) * 8, (4) * 16), "Groups: " + LTrim$(RTrim$(Str$(NumGroupVisible)))
  2052.     Color LimeGreen
  2053.     _PrintString ((1) * 8, (10) * 16), "Abilities"
  2054.     Color DarkKhaki
  2055.     _PrintString ((1) * 8, (11) * 16), "F = throw"
  2056.     _PrintString ((1) * 8, (12) * 16), "B = build"
  2057.     _PrintString ((1) * 8, (13) * 16), "N = scramble"
  2058.     _PrintString ((1) * 8, (14) * 16), "K = delete"
  2059.     _PrintString ((1) * 8, (15) * 16), "Z = zoom"
  2060.     Color LimeGreen
  2061.     Call TextCenter(" SPACE = Ascend ", _Height - (2) * 16, LimeGreen)
  2062.  
  2063. Sub TextCenter (TheText As String, TheHeight As Integer, TheShade As _Unsigned Long)
  2064.     Color TheShade
  2065.     _PrintString (_Width / 2 - (Len(TheText) / 2) * 8, TheHeight), TheText
  2066.  
  2067. Sub DisplayMiniMap
  2068.     Dim As Integer i, j, wi, wj
  2069.     Dim As Double dx, dy, u, v
  2070.     Dim As String a
  2071.     Dim As _Unsigned Long Shade
  2072.     wi = 1 + Int((PlayerCamera.Position.x) / BlockSize + UBound(WorldMesh, 1) / 2)
  2073.     wj = 1 + Int((PlayerCamera.Position.y) / BlockSize + UBound(WorldMesh, 2) / 2)
  2074.     u = _Width / 2 - UBound(WorldMesh, 1)
  2075.     v = _Height / 2 - UBound(WorldMesh, 2)
  2076.     For i = 1 To UBound(WorldMesh, 1)
  2077.         For j = 1 To UBound(WorldMesh, 2)
  2078.             Shade = TerrainHeightShade~&(WorldMesh(i, j))
  2079.             Call CPset(i + u, j + v, _RGB32(_Red32(Shade), _Green32(Shade), _Blue32(Shade), 150))
  2080.         Next
  2081.     Next
  2082.     Call CCircle(wi + u, wj + v, 2, Red)
  2083.     Call LineSmooth(wi + u, wj + v, wi + u - 5 * nhat(1) * Sqr((fovd / -192)), wj + v - 5 * nhat(2) * Sqr((fovd / -192)), White)
  2084.     Color DarkKhaki, PlayerCamera.Shade
  2085.     _PrintString (_Width - UBound(WorldMesh, 1), UBound(WorldMesh, 2)), "x:" + LTrim$(RTrim$(Str$(Int(PlayerCamera.Position.x)))) + " " + "y:" + LTrim$(RTrim$(Str$(Int(PlayerCamera.Position.y)))) + " " + "z:" + LTrim$(RTrim$(Str$(Int(PlayerCamera.Position.z))))
  2086.     dx = -nhat(1)
  2087.     dy = -nhat(2)
  2088.     If ((dx > 0) And (dy > 0)) Then a = LTrim$(RTrim$(Str$(1 + Int(((180 / pi) * (Atn(dy / dx))))))) + Chr$(248)
  2089.     If ((dx < 0) And (dy > 0)) Then a = LTrim$(RTrim$(Str$(1 + 180 + Int(((180 / pi) * (Atn(dy / dx))))))) + Chr$(248)
  2090.     If ((dx < 0) And (dy < 0)) Then a = LTrim$(RTrim$(Str$(1 + 180 + Int(((180 / pi) * (Atn(dy / dx))))))) + Chr$(248)
  2091.     If ((dx > 0) And (dy < 0)) Then a = LTrim$(RTrim$(Str$(1 + 360 + Int(((180 / pi) * (Atn(dy / dx))))))) + Chr$(248)
  2092.     _PrintString (_Width - Len(a) * 8, UBound(WorldMesh, 2) - 16), a
  2093.  
  2094. ' Interface
  2095.  
  2096. Sub KeyDownProcess
  2097.     Dim As Double modifier
  2098.     modifier = 0.05
  2099.  
  2100.     If (_KeyDown(32) <> 0) Then ' Space
  2101.         PlayerCamera.Velocity.z = 5
  2102.         PlayerCamera.Acceleration.z = -.5
  2103.     End If
  2104.     If ((_KeyDown(87) <> 0) Or (_KeyDown(119) <> 0) Or (_KeyDown(18432) <> 0)) Then ' W or w or upparrow
  2105.         Call StrafeCameraNhat(-1, -1, 0)
  2106.         If ((nhat(3) <> 0) Or (uhat(3) <> 0)) Then Call RegulateCamera
  2107.         If (ToggleAnimate = 1) Then
  2108.             PlayerCamera.Velocity.x = PlayerCamera.Velocity.x - modifier * nhat(1)
  2109.             PlayerCamera.Velocity.y = PlayerCamera.Velocity.y - modifier * nhat(2)
  2110.             'PlayerCamera.Velocity.z = PlayerCamera.Velocity.z - modifier * nhat(3)
  2111.         End If
  2112.     End If
  2113.     If ((_KeyDown(83) <> 0) Or (_KeyDown(115) <> 0) Or (_KeyDown(20480) <> 0)) Then ' S or s or downarrow
  2114.         Call StrafeCameraNhat(1, 1, 0)
  2115.         If ((nhat(3) <> 0) Or (uhat(3) <> 0)) Then Call RegulateCamera
  2116.         If (ToggleAnimate = 1) Then
  2117.             PlayerCamera.Velocity.x = PlayerCamera.Velocity.x + modifier * nhat(1)
  2118.             PlayerCamera.Velocity.y = PlayerCamera.Velocity.y + modifier * nhat(2)
  2119.             'PlayerCamera.Velocity.z = PlayerCamera.Velocity.z + modifier * nhat(3)
  2120.         End If
  2121.     End If
  2122.     If ((_KeyDown(65) <> 0) Or (_KeyDown(97) <> 0)) Then ' A or a
  2123.         If ((nhat(3) <> 0) Or (uhat(3) <> 0)) Then Call RegulateCamera
  2124.         Call StrafeCameraUhat(-1, -1, -1)
  2125.         If (ToggleAnimate = 1) Then
  2126.             PlayerCamera.Velocity.x = PlayerCamera.Velocity.x - modifier * uhat(1)
  2127.             PlayerCamera.Velocity.y = PlayerCamera.Velocity.y - modifier * uhat(2)
  2128.             'PlayerCamera.Velocity.z = PlayerCamera.Velocity.z - modifier * uhat(3)
  2129.         End If
  2130.     End If
  2131.     If ((_KeyDown(68) <> 0) Or (_KeyDown(100) <> 0)) Then ' D or d
  2132.         If ((nhat(3) <> 0) Or (uhat(3) <> 0)) Then Call RegulateCamera
  2133.         Call StrafeCameraUhat(1, 1, 1)
  2134.         If (ToggleAnimate = 1) Then
  2135.             PlayerCamera.Velocity.x = PlayerCamera.Velocity.x + modifier * uhat(1)
  2136.             PlayerCamera.Velocity.y = PlayerCamera.Velocity.y + modifier * uhat(2)
  2137.             'PlayerCamera.Velocity.z = PlayerCamera.Velocity.z + modifier * uhat(3)
  2138.         End If
  2139.     End If
  2140.     'If ((_KeyDown(81) <> 0) Or (_KeyDown(113) <> 0)) Then ' Q or q
  2141.     '    Call StrafeCameraVhat(1, 1, 1)
  2142.     '    If (ToggleAnimate = 1) Then
  2143.     '        PlayerCamera.Velocity.x = PlayerCamera.Velocity.x - modifier * vhat(1)
  2144.     '        PlayerCamera.Velocity.y = PlayerCamera.Velocity.y - modifier * vhat(2)
  2145.     '        PlayerCamera.Velocity.z = PlayerCamera.Velocity.z - modifier * vhat(3)
  2146.     '    End If
  2147.     'End If
  2148.     'If ((_KeyDown(69) <> 0) Or (_KeyDown(101) <> 0)) Then ' E or e
  2149.     '    Call StrafeCameraVhat(-1, -1, -1)
  2150.     '    If (ToggleAnimate = 1) Then
  2151.     '        PlayerCamera.Velocity.x = PlayerCamera.Velocity.x + modifier * vhat(1)
  2152.     '        PlayerCamera.Velocity.y = PlayerCamera.Velocity.y + modifier * vhat(2)
  2153.     '        PlayerCamera.Velocity.z = PlayerCamera.Velocity.z + modifier * vhat(3)
  2154.     '    End If
  2155.     'End If
  2156.     If ((_KeyDown(90) <> 0) Or (_KeyDown(122) <> 0)) Then ' Z or z.
  2157.         fovd = fovd - 5
  2158.         If (fovd < -600) Then
  2159.             fovd = -600
  2160.         Else
  2161.             farplane(4) = farplane(4) - 5
  2162.         End If
  2163.         Call CalculateClippingPlanes(.5 * _Width, .5 * _Height)
  2164.     End If
  2165.  
  2166.     modifier = 0.0333
  2167.     If (_KeyDown(19200) <> 0) Or (_KeyDown(52) <> 0) Then Call RotateUhat(-modifier, -modifier, -modifier): Call CalculateScreenVectors ' 4
  2168.     If (_KeyDown(19712) <> 0) Or (_KeyDown(54) <> 0) Then Call RotateUhat(modifier, modifier, modifier): Call CalculateScreenVectors ' 6
  2169.     If (_KeyDown(56) <> 0) Then Call RotateVhat(modifier, modifier, modifier): Call CalculateScreenVectors ' 8
  2170.     If (_KeyDown(50) <> 0) Then Call RotateVhat(-modifier, -modifier, -modifier): Call CalculateScreenVectors ' 2
  2171.     If (_KeyDown(55) <> 0) Then Call RotateUV(-modifier, -modifier, -modifier) ' 7
  2172.     If (_KeyDown(57) <> 0) Then Call RotateUV(modifier, modifier, modifier) ' 9
  2173.     If (_KeyDown(49) <> 0) Then Call RotateUhat(-modifier, -modifier, -modifier): Call CalculateScreenVectors: Call RotateUV(-modifier, -modifier, -modifier) ' 1
  2174.     If (_KeyDown(51) <> 0) Then Call RotateUhat(modifier, modifier, modifier): Call CalculateScreenVectors: Call RotateUV(modifier, modifier, modifier) ' 3
  2175.  
  2176.     'modifier = 0.0222
  2177.     'Do While _MouseInput
  2178.     '    If (_MouseMovementX > 0) Then Call RotateUhat(modifier, modifier, modifier): Call CalculateScreenVectors
  2179.     '    If (_MouseMovementX < 0) Then Call RotateUhat(-modifier, -modifier, -modifier): Call CalculateScreenVectors
  2180.     '    If (_MouseMovementY > 0) Then Call RotateVhat(modifier, modifier, modifier): Call CalculateScreenVectors
  2181.     '    If (_MouseMovementY < 0) Then Call RotateVhat(-modifier, -modifier, -modifier): Call CalculateScreenVectors
  2182.     'Loop
  2183.  
  2184. Sub KeyHitProcess
  2185.     Dim As Long g, p
  2186.     Dim As Double x0, y0, z0
  2187.     Dim As Integer kh
  2188.     kh = _KeyHit
  2189.     If (kh <> 0) Then
  2190.         Select Case kh
  2191.             Case 27 ' Quit
  2192.                 System
  2193.  
  2194.             Case 53 ' 5
  2195.                 Call RegulateCamera
  2196.  
  2197.             Case Asc("b"), Asc("B")
  2198.                 x0 = (PlayerCamera.Position.x - 40 * nhat(1))
  2199.                 y0 = (PlayerCamera.Position.y - 40 * nhat(2))
  2200.                 z0 = (PlayerCamera.Position.z - 40 * nhat(3))
  2201.                 x0 = x0 - x0 Mod BlockSize / 3
  2202.                 y0 = y0 - y0 Mod BlockSize / 3
  2203.                 z0 = z0 - z0 Mod BlockSize / 3
  2204.                 g = LatestGroupIdentity&(1)
  2205.                 g = NewCube&(g, "Custom block", 100, x0, y0, z0, BlockSize / 3, BlockSize / 3, BlockSize / 3, Lime, Purple, Teal, -2, 0)
  2206.                 g = NewWireCube&(g, "Custom block", x0, y0, z0, BlockSize / 3, BlockSize / 3, BlockSize / 3, Lime, -2)
  2207.                 Cluster(ClusterIdTicker).Acceleration.x = 0
  2208.                 Cluster(ClusterIdTicker).Acceleration.y = 0
  2209.                 Cluster(ClusterIdTicker).Acceleration.z = -.15
  2210.                 Call ClusterPinch(g)
  2211.  
  2212.             Case Asc("f"), Asc("F")
  2213.                 x0 = (PlayerCamera.Position.x - 40 * nhat(1))
  2214.                 y0 = (PlayerCamera.Position.y - 40 * nhat(2))
  2215.                 z0 = (PlayerCamera.Position.z - 40 * nhat(3))
  2216.                 g = LatestGroupIdentity&(1)
  2217.                 g = NewCube&(g, "Potion", 150, x0, y0, z0, 10, 10, 10, Red, Purple, Teal, -1, 0)
  2218.                 g = NewCube&(g, "Potion", 50, x0, y0, z0 + 10, 2, 2, 10, Blue, Purple, Teal, -1, 0)
  2219.                 Cluster(ClusterIdTicker).Acceleration.x = 0
  2220.                 Cluster(ClusterIdTicker).Acceleration.y = 0
  2221.                 Cluster(ClusterIdTicker).Acceleration.z = -.15
  2222.                 Cluster(ClusterIdTicker).Velocity.x = -5 * nhat(1)
  2223.                 Cluster(ClusterIdTicker).Velocity.y = -5 * nhat(2)
  2224.                 Cluster(ClusterIdTicker).Velocity.z = -5 * nhat(3)
  2225.                 Call ClusterPinch(g)
  2226.  
  2227.             Case Asc("n"), Asc("N")
  2228.                 For p = Group(ClosestGroup).FirstVector To Group(ClosestGroup).LastVector
  2229.                     vec3Dvel(p).x = (Rnd - .5) * .20
  2230.                     vec3Dvel(p).y = (Rnd - .5) * .20
  2231.                     vec3Dvel(p).z = (Rnd - .5) * .20
  2232.                 Next
  2233.  
  2234.             Case Asc("k"), Asc("K")
  2235.                 Call RemoveGroup(ClosestGroup)
  2236.  
  2237.             Case Asc("t"), Asc("T")
  2238.                 ToggleAnimate = -ToggleAnimate
  2239.         End Select
  2240.     End If
  2241.     _KeyClear
  2242.  
  2243. ' Plotting and color tools.
  2244.  
  2245. Sub CLine (x0 As Double, y0 As Double, x1 As Double, y1 As Double, shade As _Unsigned Long)
  2246.     Line (_Width / 2 + x0, -y0 + _Height / 2)-(_Width / 2 + x1, -y1 + _Height / 2), shade
  2247.  
  2248. Sub CPset (x0, y0, shade As _Unsigned Long)
  2249.     PSet (_Width / 2 + x0, -y0 + _Height / 2), shade
  2250.  
  2251. Sub CCircle (x0 As Double, y0 As Double, rad As Double, shade As _Unsigned Long)
  2252.     Circle (_Width / 2 + x0, -y0 + _Height / 2), rad, shade
  2253.  
  2254. Sub BlockPoint (x0 As Double, y0 As Double, shade As _Unsigned Long)
  2255.     Line (_Width / 2 + x0 - 3, -y0 + _Height / 2 - 3)-(_Width / 2 + x0 + 3, -y0 + _Height / 2 + 3), _RGB32(_Red32(shade), _Green32(shade), _Blue32(shade), 200), BF
  2256.  
  2257. Function ShadeMix~& (shade0 As _Unsigned Long, shade1 As _Unsigned Long, weight As Double)
  2258.     ShadeMix~& = _RGB32((1 - weight) * _Red32(shade0) + weight * _Red32(shade1), (1 - weight) * _Green32(shade0) + weight * _Green32(shade1), (1 - weight) * _Blue32(shade0) + weight * _Blue32(shade1))
  2259.  
  2260. Sub LineSmooth (x0, y0, x1, y1, c As _Unsigned Long)
  2261.     ' source: https://en.wikipedia.org/w/index.php?title=Xiaolin_Wu%27s_line_algorithm&oldid=852445548
  2262.     ' translated: FellippeHeitor @ qb64.org
  2263.     ' updated slightly for this project
  2264.  
  2265.     Dim plX As Integer, plY As Integer, plI
  2266.  
  2267.     Dim steep As _Byte
  2268.     steep = Abs(y1 - y0) > Abs(x1 - x0)
  2269.  
  2270.     If steep Then
  2271.         Swap x0, y0
  2272.         Swap x1, y1
  2273.     End If
  2274.  
  2275.     If x0 > x1 Then
  2276.         Swap x0, x1
  2277.         Swap y0, y1
  2278.     End If
  2279.  
  2280.     Dim dx, dy, gradient
  2281.     dx = x1 - x0
  2282.     dy = y1 - y0
  2283.     gradient = dy / dx
  2284.  
  2285.     If dx = 0 Then
  2286.         gradient = 1
  2287.     End If
  2288.  
  2289.     'handle first endpoint
  2290.     Dim xend, yend, xgap, xpxl1, ypxl1
  2291.     xend = _Round(x0)
  2292.     yend = y0 + gradient * (xend - x0)
  2293.     xgap = (1 - ((x0 + .5) - Int(x0 + .5)))
  2294.     xpxl1 = xend 'this will be used in the main loop
  2295.     ypxl1 = Int(yend)
  2296.     If steep Then
  2297.         plX = ypxl1
  2298.         plY = xpxl1
  2299.         plI = (1 - (yend - Int(yend))) * xgap
  2300.         GoSub plot
  2301.  
  2302.         plX = ypxl1 + 1
  2303.         plY = xpxl1
  2304.         plI = (yend - Int(yend)) * xgap
  2305.         GoSub plot
  2306.     Else
  2307.         plX = xpxl1
  2308.         plY = ypxl1
  2309.         plI = (1 - (yend - Int(yend))) * xgap
  2310.         GoSub plot
  2311.  
  2312.         plX = xpxl1
  2313.         plY = ypxl1 + 1
  2314.         plI = (yend - Int(yend)) * xgap
  2315.         GoSub plot
  2316.     End If
  2317.  
  2318.     Dim intery
  2319.     intery = yend + gradient 'first y-intersection for the main loop
  2320.  
  2321.     'handle second endpoint
  2322.     Dim xpxl2, ypxl2
  2323.     xend = _Round(x1)
  2324.     yend = y1 + gradient * (xend - x1)
  2325.     xgap = ((x1 + .5) - Int(x1 + .5))
  2326.     xpxl2 = xend 'this will be used in the main loop
  2327.     ypxl2 = Int(yend)
  2328.     If steep Then
  2329.         plX = ypxl2
  2330.         plY = xpxl2
  2331.         plI = (1 - (yend - Int(yend))) * xgap
  2332.         GoSub plot
  2333.  
  2334.         plX = ypxl2 + 1
  2335.         plY = xpxl2
  2336.         plI = (yend - Int(yend)) * xgap
  2337.         GoSub plot
  2338.     Else
  2339.         plX = xpxl2
  2340.         plY = ypxl2
  2341.         plI = (1 - (yend - Int(yend))) * xgap
  2342.         GoSub plot
  2343.  
  2344.         plX = xpxl2
  2345.         plY = ypxl2 + 1
  2346.         plI = (yend - Int(yend)) * xgap
  2347.         GoSub plot
  2348.     End If
  2349.  
  2350.     'main loop
  2351.     Dim x
  2352.     If steep Then
  2353.         For x = xpxl1 + 1 To xpxl2 - 1
  2354.             plX = Int(intery)
  2355.             plY = x
  2356.             plI = (1 - (intery - Int(intery)))
  2357.             GoSub plot
  2358.  
  2359.             plX = Int(intery) + 1
  2360.             plY = x
  2361.             plI = (intery - Int(intery))
  2362.             GoSub plot
  2363.  
  2364.             intery = intery + gradient
  2365.         Next
  2366.     Else
  2367.         For x = xpxl1 + 1 To xpxl2 - 1
  2368.             plX = x
  2369.             plY = Int(intery)
  2370.             plI = (1 - (intery - Int(intery)))
  2371.             GoSub plot
  2372.  
  2373.             plX = x
  2374.             plY = Int(intery) + 1
  2375.             plI = (intery - Int(intery))
  2376.             GoSub plot
  2377.  
  2378.             intery = intery + gradient
  2379.         Next
  2380.     End If
  2381.  
  2382.     Exit Sub
  2383.  
  2384.     plot:
  2385.     ' Change to regular PSET for standard coordinate orientation.
  2386.     Call CPset(plX, plY, _RGB32(_Red32(c), _Green32(c), _Blue32(c), plI * _Alpha32(c)))
  2387.     Return
  2388.  
  2389. ' Camera transformation
  2390.  
  2391. Sub RotateUhat (dx As Double, dy As Double, dz As Double)
  2392.     uhat(1) = uhat(1) + nhat(1) * dx
  2393.     uhat(2) = uhat(2) + nhat(2) * dy
  2394.     uhat(3) = uhat(3) + nhat(3) * dz
  2395.  
  2396. Sub RotateVhat (dx As Double, dy As Double, dz As Double)
  2397.     vhat(1) = vhat(1) + nhat(1) * dx
  2398.     vhat(2) = vhat(2) + nhat(2) * dy
  2399.     vhat(3) = vhat(3) + nhat(3) * dz
  2400.  
  2401. Sub RotateUV (dx As Double, dy As Double, dz As Double)
  2402.     Dim As Double v1, v2, v3
  2403.     v1 = vhat(1)
  2404.     v2 = vhat(2)
  2405.     v3 = vhat(3)
  2406.     vhat(1) = v1 + uhat(1) * dx
  2407.     vhat(2) = v2 + uhat(2) * dy
  2408.     vhat(3) = v3 + uhat(3) * dz
  2409.     uhat(1) = uhat(1) - v1 * dx
  2410.     uhat(2) = uhat(2) - v2 * dy
  2411.     uhat(3) = uhat(3) - v3 * dz
  2412.  
  2413. Sub StrafeCameraUhat (dx As Double, dy As Double, dz As Double)
  2414.     PlayerCamera.Position.x = PlayerCamera.Position.x + uhat(1) * dx
  2415.     PlayerCamera.Position.y = PlayerCamera.Position.y + uhat(2) * dy
  2416.     PlayerCamera.Position.z = PlayerCamera.Position.z + uhat(3) * dz
  2417.  
  2418. Sub StrafeCameraVhat (dx As Double, dy As Double, dz As Double)
  2419.     PlayerCamera.Position.x = PlayerCamera.Position.x + vhat(1) * dx
  2420.     PlayerCamera.Position.y = PlayerCamera.Position.y + vhat(2) * dy
  2421.     PlayerCamera.Position.z = PlayerCamera.Position.z + vhat(3) * dz
  2422.  
  2423. Sub StrafeCameraNhat (dx As Double, dy As Double, dz As Double)
  2424.     PlayerCamera.Position.x = PlayerCamera.Position.x + nhat(1) * dx
  2425.     PlayerCamera.Position.y = PlayerCamera.Position.y + nhat(2) * dy
  2426.     PlayerCamera.Position.z = PlayerCamera.Position.z + nhat(3) * dz
  2427.  
  2428. '''
  2429.  

EDIT: Attached screenshot of looking straight up, about to capture an eclipse. 
eclipse.png
* Sanctum.bas (Filesize: 95.89 KB, Downloads: 38)
« Last Edit: November 09, 2021, 08:31:11 pm by STxAxTIC »
You're not done when it works, you're done when it's right.

Offline Cobalt

  • QB64 Developer
  • Forum Resident
  • Posts: 878
  • At 60 I become highly radioactive!
Re: Sanctum 2021
« Reply #1 on: November 09, 2021, 09:48:07 pm »
man hits me up with 'Out of Memory' right out the door. yet task manager only shows 1gig in use.
Granted after becoming radioactive I only have a half-life!

Offline keybone

  • Forum Regular
  • Posts: 116
  • My name a Nursultan Tulyakbay.
Re: Sanctum 2021
« Reply #2 on: November 09, 2021, 10:16:52 pm »
That's awesome.  :-0
btw it works for me,
Screenshot from 2021-11-09 22-41-24.png
* Screenshot from 2021-11-09 22-41-24.png (Filesize: 404.42 KB, Dimensions: 1366x768, Views: 57)
« Last Edit: November 09, 2021, 10:48:23 pm by keybone »
I am from a Kazakhstan, we follow the hawk.

Offline Cobalt

  • QB64 Developer
  • Forum Resident
  • Posts: 878
  • At 60 I become highly radioactive!
Re: Sanctum 2021
« Reply #3 on: November 10, 2021, 12:19:33 pm »
Ok, works with 64bit version.
Frame rate was less than stellar though. still ran fairly well but never came close to 60.

Sanctum.jpg
* Sanctum.jpg (Filesize: 168.33 KB, Dimensions: 804x623, Views: 45)
Granted after becoming radioactive I only have a half-life!

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
Re: Sanctum 2021
« Reply #4 on: November 11, 2021, 02:14:30 pm »
Very nice work! 29 - 31 FPS here. I go to play it again.