January 26, 2021, 11:10:29 AM

Author Topic: [bb] Ulysses Foliage System by N [ 1+ years ago ]  (Read 619 times)

Offline BlitzBot

  • Jr. Member
  • **
  • Posts: 1
[bb] Ulysses Foliage System by N [ 1+ years ago ]
« on: June 29, 2017, 12:28:39 AM »
Title : Ulysses Foliage System
Author : N
Posted : 1+ years ago

Description : This is a single-surface foliage system.  What it does is places foliage and grass sprites (not actual sprites but virtual ones) across a terrain in a fashion similar to this:

[img]www.noelcower.com/pathway.html">

It's very simple and easy to use, the functions are as follows:

InitGrass(Camera, Y-Trace Range, Spawning Radius)
  Initializes the grass system.  Camera is the only mandatory argument.

KillGrass()
  Kills the grass system.

LoadGrassTexture(Path,Columns,Rows,No Mip Mapping)
  Loads a texture useable by the grass system.  Columns specifies how many textures there are horizontally, and Rows specifies how many textures there are vertically.  Refer to grass3.tga to get a visual example of how the textures need to be laid out.

CreateGrassSpawner(Texture,Tile,Size,Mesh,Grass Added Per Update,Maximum Amount of Grass,Field Of View)
  Creates a grass spawner, the Texture argument is mandatory.

StartGrassSpawner(Spawner)
  Tells a grass spawner to attempt to spawn grass quads.

StopGrassSpawner(Spawner)
  Tells a grass spawner to not attempt to spawn any grass quads.

ToggleGrassSpawner(Spawner)
  Toggles whether or not a grass spawner spawns grass quads.

SetWind(Spawner,Affects,WindSpeed,WindDirection,WindPower)
  Sets the wind speed a spawner uses.

WindAffects(Spawner,Affects)
  Sets whether or not a spawner is affected by wind.

SetSpawner(Spawner, BunchRange, Quads per Bunch)
  Use this function to fine-tune the amount of quads per grass-bunch and the distance between them.

FreeGrassSpawner(Spawner)
  Frees a grass spawner.

gQuickSort(..)
  Internal function, disregard it.

UpdateGrass()
  Updates the grass objects.

AddGrassObject(Entity,Surface,Color Texture Path)
  Specifies that an object is a viable attach-point for grass quads.  This returns an integer handle that can be used to remove the object later.  If you would like to limit it to a single-surface within the mesh you can pass a surface to the Surface argument (as displayed in the example).  This function will build a node-list so that grass will be spawned from positions on a precompiled grid of positions, so you will want to call this outside of a main loop, otherwise you could end up waiting a bit.  Passing a path to a texture to the last argument of this function will make it load the texture and proceed to color nodes according to positions on the texture.  For reference, the texture is read like this: negative X is 0, negatize Z is 0, positive X is 1, positive Z is 1.  Think of that as if it were UV coordinates.

RemoveGrassObject(GrassObject)
  Removes an object from the list of objects useable by grass quads.

CreateGrassBlock(X,Z,Width,Length)
  Contrary to what this function's name suggests it does, this creates a rectangle within-which grass cannot be spawned.

RemoveGrassBlock(Block)
  Removes a grass block from memory.

ExportNodes(Path$)
  This exports the current set of nodes to a file.  If you have the bz2 wrapper DLL by elias_t then Ulysses will also compress the file (since 512^2 nodes can be a tad large- 7.5mb in some cases, either way using over 64^2 nodes can be extremely slow if you're not being careful).  Do not pass a file extension to 'path', or you will result in something along the lines of 'file.your_ext.unl'.

ImportNodes(Path$)
  Imports a set of nodes from a file.  If you have elias_t's bz2 wrapper DLL it will attempt to decompress the node file.

Obviously, the commands are simple.

<a href="http://www.noelcower.com/Ulysses.zip" target="_blank">Media Pack[/url]
This is a small (by small I do mean small- four grass sprites, two flat textures, and a terrain mesh) media pack that contains a few grass sprites for your use.


Code :
Code: BlitzBasic
  1. ;; ************************ READ ME **************************
  2. ;; If you do not have the BZ2 user-lib made by elias_t then this function is necessary, otherwise **REMOVE IT**.
  3. ;; ************************ READ ME **************************
  4. ;Function bz2(compress,level,cfrom$,cto$)
  5. ;       CopyFile cfrom,cto
  6. ;End Function
  7. ;; Commented out by default because I have the bz2 userlib and because it's included in Ulysses.zip.
  8.  
  9.  
  10.        
  11. Type GrassTile
  12.         Field Surface
  13.         Field US#,VS#
  14.         Field Columns,Rows
  15. End Type
  16.  
  17. Type GrassSet
  18.         Field Mesh
  19.         Field Radius#
  20.         Field Range#
  21.         Field W,H
  22.         Field GSin#[359]
  23.         Field GCos#[359]
  24.         Field TPivot
  25.         Field Camera
  26.         Field OX#,OY#,OZ#,OYaw#,OPitch#
  27.         Field TPivotB
  28. End Type
  29.  
  30. Type GTex
  31.         Field Index
  32.         Field FOV
  33.         Field ID
  34.         Field Count
  35.         Field Size#
  36.         Field Rate%
  37.         Field Tex.GrassTile
  38.         Field MaxCount%
  39.         Field Frame
  40.         Field On
  41.         Field WindDirection%
  42.         Field WindSpeed#
  43.         Field WindPower#
  44.         Field Mesh
  45.         Field WindAffects
  46.         Field BunchRange#
  47.         Field BunchCount% ;; you can go from 1 to GrassBunchCount-1
  48. End Type
  49.  
  50. Type GrassObject
  51.         Field Entity
  52.         Field Surface
  53.         Field Nodes.GNode[NODE_SEG^2 - 1]
  54.         Field Rows.NodeRow[NODE_SEG-1]
  55. End Type
  56.  
  57. Type GNode
  58.         Field X#,Y#,Z#
  59.         Field Width#,Depth#
  60.         Field NX#,NY#,NZ#
  61.         Field U%                                ;; Useable
  62.         Field Occupied[OccupiedIndices]
  63.         Field R#,G#,B#,A#
  64. End Type
  65.  
  66. Type NodeRow
  67.         Field X#,Width#
  68.         Field Z#,Depth#
  69.         Field Nodes.GNode[NODE_SEG-1]
  70. End Type
  71.  
  72. Type GSprite
  73.         Field TimeOut
  74.         Field X#,Y#,Z#
  75.         Field NX#,NY#,NZ#
  76.         Field N.GNode
  77.         Field Parent.GTex
  78.         Field IDelete
  79.         Field Quads.GQuad[cGrassBunchCount-1]
  80.         Field Range#
  81. End Type
  82.  
  83. Type GQuad
  84.         Field X#,Y#,Z#
  85.         Field VX#[3]
  86.         Field VY#[3]
  87.         Field VZ#[3]
  88.         Field CAlpha#
  89.         Field DAlpha#
  90.         Field Angle#
  91.         Field gx#,gy#,gz#,rx#,ry#,rz#
  92.         Field Size#
  93.         Field Reverse%
  94.         Field R%,G%,B%
  95. End Type
  96.  
  97. Type GBlock
  98.         Field X#,Y#,Z#,W#,L#,H#
  99. End Type
  100.  
  101. ;; Internal array
  102. Dim SortArray#(0,0)
  103.  
  104. ;; The amount of grass per node
  105. Const cGrassBunchCount = 10
  106.  
  107. ;; Function Arguments
  108. Const cGrassCount = 500
  109. Const cGrassRate = 1
  110.  
  111. ;; The amount of segments per precalculated node array
  112. ;; Keep in mind, this value should be low, even if you export the node list and import it later to save time, Blitz will still slow down when you go through NODE_SEG^2 amount of nodes
  113. Const NODE_SEG = 48
  114.  
  115. ;; The amount of larger blocks of nodes to check against when finding the node to use
  116. Const CONT_SEG = 8
  117.  
  118. ;; Various Function Modified Variables
  119. Global IntersectedX#,IntersectedY#,IntersectedZ#,IntersectedTriangle%,IntersectedSurface%
  120.  
  121. ;; The time it takes to call the UpdateGrass function
  122. Global GrassUpdateTime% = 0
  123.  
  124. ;; The width/length of the grass bunch range
  125. Global cGrassBunchRange# = 3
  126.  
  127. ;; Grass system data and information about the graphics mode
  128. Global gGrassSet.GrassSet = Null
  129.  
  130. ;; Grass sprites in existence (not quads, rather the nodes that contain n amount of quads)
  131. Global cCurrentGrassCount = 0
  132.  
  133. ;; Amount of different kinds of grass spawners a node can 'carry'
  134. Const OccupiedIndices = 16
  135.  
  136. ;; Amount of spawners in existence, never modify this variable
  137. Global ExistingSpawners = 0
  138.  
  139. ;; Amount of nodes in existence
  140. Global ExistingNodes
  141.  
  142. ;; The amount of grass quads drawn
  143. Global GrassQuadsDrawn = 0
  144.  
  145. ;; Minimum Y normal of grass angles
  146. Global YNormal# = .8
  147.  
  148. ;; The alpha-level of grass pieces.
  149. Global GrassAlpha# = 1
  150.  
  151. ;; The speed at which the grass fades in.  0 is not at all, 100 is instantly (as in no interpolation).
  152. Global AlphaFadeSpeed# = 2.5
  153.  
  154. ;; Wind speed defaults
  155. Const WindAffects = 1
  156. Const WindDirection = 45        ;; This is intentionally an integer so I can use it with the sin/cos lookup tables (refer to type GrassSet)
  157. Const WindPower# = .6   ;; Set this to something like .4 or something, otherwise it won't work out too well...
  158. Const WindSpeed# = .06  ;; Decrease to speed up, increase to slow down
  159.  
  160. ;; Kill off-screen grass quads
  161. Global KillOffScreen = 0
  162.  
  163. ;; The amount of time when a grass bunch isn't visible before it gets removed
  164. Global GrassTimeout = 400
  165.  
  166. ;; Call this before the main loop- this is mandatory
  167. Function InitGrass.GrassSet(Camera,CheckRange#=64,GrassRadius#=80)
  168.         g.GrassSet = New GrassSet
  169.         gCamera = Camera
  170.         gRange = CheckRange     ;; store checking settings
  171.         gRadius = GrassRadius
  172.         gMesh = CreateMesh()    ;; create the mesh that all grass sprites are placed in
  173.         EntityFX gMesh,1+2+16+32
  174.         gW = GraphicsWidth()    ;; store graphics width and height
  175.         gH = GraphicsHeight()
  176.         For A = 0 To 359                ;; build sincos lookup tables
  177.                 gGSin[A] = Sin(A)
  178.                 gGCos[A] = Cos(A)
  179.         Next
  180.         gTPivot = CreatePivot()
  181.         gTPivotB = CreatePivot()
  182.         gGrassSet = g
  183.         Return g
  184. End Function
  185.  
  186. ;; frees the grass system from memory
  187. Function KillGrass()
  188.         FreeEntity gGrassSetTPivot
  189.         FreeEntity gGrassSetTPivotB
  190.         FreeEntity gGrassSetMesh
  191.         Delete Each GTex
  192.         Delete Each GSprite
  193.         Delete Each GrassTile
  194.         Delete Each GrassSet
  195.         Delete Each GQuad
  196.         Delete Each GrassObject
  197.         Delete Each GBlock
  198.         Delete Each GNode
  199. End Function
  200.  
  201. ;; Loads a texture useable by Ulysses
  202. Function LoadGrassTexture(Path$,Columns=1,Rows=1,NoMips=False)
  203.         If NoMips Then ClearTextureFilters
  204.         T = LoadTexture(Path,1+2+16+32)
  205.         If NoMips Then TextureFilter "",1+8
  206.         If Not T Then Return False
  207.         g.GrassTile = New GrassTile
  208.         gSurface = CreateSurface(gGrassSetMesh)
  209.         B = CreateBrush()
  210.         BrushTexture B,T,0,0
  211.         PaintSurface gSurface,B
  212.         FreeBrush B
  213.         gUS = (Float(TextureWidth(T))/Columns)/TextureWidth(T)
  214.         gVS = (Float(TextureHeight(T))/Rows)/TextureHeight(T)
  215.         gRows = Rows
  216.         gColumns = Columns
  217.         FreeTexture T
  218.         Return Handle(g)
  219. End Function
  220.  
  221. ;; Pass your grass sprite texture to Texture
  222. Function CreateGrassSpawner(Texture,Frame=0,Size#=2,Mesh=0,GrowthRate%=cGrassRate,MaxAmount%=cGrassCount,FOV% = 90,On=1)
  223.         If Texture = 0 Then Return False
  224.        
  225.         g.GTex = New GTex
  226.         gID = Handle(g)
  227.         gFOV = FOV
  228.         gSize = Size
  229.         gMaxCount = MaxAmount
  230.         gFrame = Frame
  231.         gRate = GrowthRate
  232.         gTex = Object.GrassTile(Texture)
  233.         gOn = On
  234.         gIndex = ExistingSpawners
  235.         gWindDirection = WindDirection
  236.         gWindPower = WindPower
  237.         gWindSpeed = WindSpeed
  238.         gWindAffects = WindAffects
  239.         gBunchCount = cGrassBunchCount
  240.         gBunchRange = cGrassBunchRange
  241.         Local MinY# = 999999
  242.         If Mesh <> 0 Then
  243.                 HideEntity Mesh
  244.                 For n = 1 To CountSurfaces(Mesh)
  245.                         S = GetSurface(Mesh,n)
  246.                         For V = 0 To CountVertices(S)-1
  247.                                 y# = VertexY(S,V)
  248.                                 If y < MinY Then MinY = y
  249.                         Next
  250.                 Next
  251.                 PositionMesh Mesh,0,-MinY,0
  252.         EndIf
  253.         gMesh = Mesh
  254.         ExistingSpawners = ExistingSpawners + 1
  255.         Return gID
  256. End Function
  257.  
  258. ;; Tells a spawner to create grass sprites
  259. Function StartGrassSpawner(Spawner)
  260.         g.GTex = Object.GTex(Spawner)
  261.         If g = Null Then Return False
  262.         gOn = 1
  263.         Return True
  264. End Function
  265.  
  266. ;; Toggles whether or not a spawner creates grass sprites
  267. Function ToggleGrassSpawner(Spawner)
  268.         g.GTex = Object.GTex(Spawner)
  269.         If g = Null Then Return False
  270.         gOn = Not gOn
  271.         Return True
  272. End Function
  273.  
  274. ;; Sets the wind speeds for a specific spawner.  Normally you shouldn't have to use this, but in the case you want to disable the effects of wind on a mesh, for example, you can.
  275. Function SetWind(Spawner,Affects=WindAffects,WindSpeed#=WindSpeed,WindDirection=WindDirection,WindPower#=WindPower#)
  276.         g.GTex = Object.GTex(Spawner)
  277.         If g = Null Then Return False
  278.         gWindSpeed = WindSpeed
  279.         gWindDirection = WindDirection
  280.         gWindPower = WindPower
  281.         gWindAffects = Affects
  282.         Return True
  283. End Function
  284.  
  285. ;; Sets whether or not a spawner is affected by wind speeds
  286. Function WindAffects(Spawner,Affects=WindAffects)
  287.         g.GTex = Object.GTex(Spawner)
  288.         If g = Null Then Return False
  289.         gWindAffects = Affects
  290.         Return True
  291. End Function
  292.  
  293. ;; Tells a spawner to not create grass sprites
  294. Function StopGrassSpawner(Spawner)
  295.         g.GTex = Object.GTex(Spawner)
  296.         If g = Null Then Return False
  297.         gOn = 0
  298.         Return True
  299. End Function
  300.  
  301. Function SetSpawner(Spawner,Range=2.3,Amount=cGrassBunchCount)
  302.         g.GTex = Object.GTex(Spawner)
  303.         If g = Null Then Return False
  304.         gBunchRange = Range
  305.         gBunchCount = Amount
  306.         Return True
  307. End Function
  308.  
  309. ;; Frees a spawner from memory
  310. Function FreeGrassSpawner(Spawner)
  311.         FreeEntity gGrassSetMesh
  312.         gGrassSetMesh = CreateMesh()
  313.         S.GTex = Object.GTex(Spawner)
  314.         If S = Null Then Return False
  315.         SIndex = SIndex
  316.        
  317.         For n.GNode = Each GNode
  318.                 For f = SIndex To OccupiedIndices
  319.                         If f < OccupiedIndices Then
  320.                                 nOccupied[f] = nOccupied[f+1]
  321.                         EndIf
  322.                        
  323.                         If f >= ExistingSpawners Then
  324.                                 nOccupied[f] = 0
  325.                         EndIf
  326.                 Next
  327.         Next
  328.        
  329.         For g.GTex = Each GTex
  330.                 If gIndex > SIndex Then
  331.                         gIndex = gIndex - 1
  332.                 EndIf
  333.                 If gID = Spawner Then
  334.                         For i.GSprite = Each GSprite
  335.                                 If iParent = g Then
  336.                                         iNOccupied[iParentIndex] = 0
  337.                                         For k = 0 To cGrassBunchCount-1
  338.                                                 Delete iQuads[k]
  339.                                         Next
  340.                                         Delete i
  341.                                 EndIf
  342.                         Next
  343.                         Delete g
  344.                         Return True
  345.                 EndIf
  346.         Next
  347.         Return False
  348. End Function
  349.  
  350. ;; Creates a virtual block (top-down) that prevents grass from being grown within a specified area.  This allows somewhat finer-precision control of the grass system.
  351. Function CreateGrassBlock(X#,Y#,Z#,Width#,Height#,Depth#)
  352.         B.GBlock = New GBlock
  353.         If Width < 0 Then
  354.                 X = X + Width
  355.                 Width = -Width
  356.         EndIf
  357.        
  358.         If Height < 0 Then
  359.                 Y = Y + Height
  360.                 Height = -Height
  361.         EndIf
  362.        
  363.         If Depth < 0 Then
  364.                 Z = Z + Depth
  365.                 Depth = -Depth
  366.         EndIf
  367.        
  368.         BX = X
  369.         BY = Y
  370.         B = Z
  371.        
  372.         BW = Width
  373.         BH = Height
  374.         BL = Depth
  375.         Return Handle(B)
  376. End Function
  377.  
  378. Function RemoveGrassBlock(Block)
  379.         B.GBlock = Object.GBlock(Block)
  380.         If B <> Null Then Delete B Return 1
  381.         Return 0
  382. End Function
  383.  
  384. ;; Internal function
  385. Function gQuickSort( L, R, RandomPivot = True )
  386.         Local A, B, SwapA#, SwapB#, Middle#
  387.         A = L
  388.         B = R
  389.        
  390.         If RandomPivot Then
  391.                 Middle = SortArray( Rand(L, R), 0 )
  392.         Else
  393.                 Middle = SortArray( (L+R)/2, 0 )
  394.         EndIf
  395.        
  396.         While True
  397.                
  398.                 While SortArray( A, 0 ) < Middle
  399.                         A = A + 1
  400.                         If A > R Then Exit
  401.                 Wend
  402.                
  403.                 While  Middle < SortArray( B, 0 )
  404.                         B = B - 1
  405.                         If B < 0 Then Exit
  406.                 Wend
  407.                
  408.                 If A > B Then Exit
  409.                
  410.                 SwapA = SortArray( A, 0 )
  411.                 SwapB = SortArray( A, 1 )
  412.                 SortArray( A, 0 ) = SortArray( B, 0 )
  413.                 SortArray( A, 1 ) = SortArray( B, 1 )
  414.                 SortArray( B, 0 ) = SwapA
  415.                 SortArray( B, 1 ) = SwapB
  416.                
  417.                 A = A + 1
  418.                 B = B - 1
  419.                
  420.                 If B < 0 Then Exit
  421.                
  422.         Wend
  423.        
  424.         If L < B Then gQuickSort( L, B )
  425.         If A < R Then gQuickSort( A, R )
  426. End Function
  427.  
  428. ;; Updates the grass system
  429. Function UpdateGrass()
  430.         If gGrassSet = Null Then Return False
  431.         For t.GrassTile = Each GrassTile
  432.                 ClearSurface tSurface
  433.         Next
  434.         GrassQuadsDrawn = 0
  435.        
  436.         For i.GSprite = Each GSprite
  437.                
  438.                 CameraProject gGrassSetCamera,iX,iY,i
  439.                 PX = ProjectedX()
  440.                 PY = ProjectedY()
  441.                 PositionEntity gGrassSetTPivot,iX,iY,i
  442.                 D# = EntityDistance(gGrassSetCamera,gGrassSetTPivot)
  443.                
  444.                 If (ProjectedZ() <= 0) Or ((PX < -100 Or PX > gGrassSetW+100) And KillOffScreen)  Or D > iRange*1.5 Then
  445.                         iTimeOut = iTimeOut + 1
  446.                         If iTimeOut > GrassTimeout
  447.                                 iIDelete = 1
  448.                                 For n = 0 To iParentBunchCount-1
  449.                                         iQuads[n]DAlpha = 0
  450.                                 Next
  451.                         EndIf
  452.                 Else
  453.                         iTimeOut = 0
  454.                         iIDelete = 0
  455.                         For n = 0 To iParentBunchCount-1
  456.                                 iQuads[n]DAlpha = GrassAlpha
  457.                         Next
  458.                 EndIf
  459.                
  460.                 If iIDelete = 1 Then
  461.                         If iQuads[0]CAlpha <= .1 Then iIDelete = 2
  462.                 EndIf
  463.                
  464.                 If iIDelete = 2 Then
  465.                         iParentCount = iParentCount - 1
  466.                         cCurrentGrassCount = cCurrentGrassCount - 1
  467.                         For n = 0 To iParentBunchCount-1
  468.                                 Delete iQuads[n]
  469.                         Next
  470.                         iNOccupied[iParentIndex] = 0
  471.                         Delete i
  472.                 EndIf
  473.         Next
  474.        
  475.         CX# = EntityX(gGrassSetCamera,1)
  476.         CY# = EntityY(gGrassSetCamera,1)
  477.         CZ# = EntityZ(gGrassSetCamera,1)
  478.        
  479.         TFormPoint gGrassSetOX,gGrassSetOY,gGrassSetOZ,0,gGrassSetCamera
  480.        
  481.         Backward# = 1
  482.         If TFormedZ() > 0 Then Backward# = TFormedZ()*.4
  483.        
  484.         CYaw# = EntityYaw(gGrassSetCamera,1)
  485.         TurnYaw = (gGrassSetOYaw-CYaw)
  486.         CPitch# = EntityPitch(gGrassSetCamera,1)
  487.         TPivot = gGrassSetTPivot
  488.         TPivotB = gGrassSetTPivotB
  489.         For g.GTex = Each GTex
  490.                 If gOn = 1 Then
  491.                         If CX <> gGrassSetOX Or CY <> gGrassSetOY Or CZ <> gGrassSetOZ Or CYaw <> gGrassSetOYaw Or CPitch <> gGrassSetOPitch Then
  492.                                 Count = gMaxCount-gCount
  493.                                 If Count > gRate Then Count = gRate
  494.                                 For j = 1 To Count
  495.                                         A = (CYaw+Rnd(-gFOV/2,gFOV/2)+90-TurnYaw*1.5) Mod 359
  496.                                         If A > 359 Then A = A - 359
  497.                                         If A < 0 Then A = A + 359
  498.                                         R# = Rnd(.5,gGrassSetRadius)*Backward;*Cos(CPitch)
  499.                                         X# = CX + gGrassSetGCos[Int A] * R
  500.                                         Z# = CZ + gGrassSetGSin[Int A] * R
  501.                                         PositionEntity gGrassSetTPivot,X,0,Z
  502.                                         Closest.GNode = Null
  503.                                         ClD# = 5000000
  504.                                         For nr.NodeRow = Each NodeRow
  505.                                                 If X > nrX And X < nrX+nrWidth Then
  506.                                                         If Z > nr And Z < nr+nrDepth Then
  507.                                                                 For n = 0 To NODE_SEG-1
  508.                                                                         If X > nrNodes[n]X And X < nrNodes[n]X + nrNodes[n]Width Then
  509.                                                                                 If Z > nrNodes[n] And Z < nrNodes[n]+nrDepth Then
  510.                                                                                         Closest = nrNodes[n]
  511.                                                                                 EndIf
  512.                                                                         EndIf
  513.                                                                 Next
  514.                                                         EndIf
  515.                                                 EndIf
  516.                                         Next
  517.                                        
  518.                                         Con = 1
  519.                                        
  520.                                         If Closest <> Null Then
  521.                                                 X = ClosestX : Y = ClosestY : Z = Closest
  522.                                                 For b.GBlock = Each GBlock
  523.                                                         If X > bX And X < bX+bW And Z > b And Z <b+bL And Y > bY And Y < bY + bH  Then Con = 0 Exit
  524.                                                 Next
  525.                                         Else
  526.                                                 Con = 0
  527.                                         EndIf
  528.                                        
  529.                                         If Con Then
  530.                                                 If ClosestOccupied[gIndex] = 0 And ClosestU Then
  531.                                                         i.GSprite = New GSprite
  532.                                                         iParent = g
  533.                                                         iX = ClosestX
  534.                                                         i = Closest
  535.                                                         iY = ClosestY
  536.                                                         iNX# =ClosestNX
  537.                                                         iNY# = ClosestNY
  538.                                                         iNZ# = ClosestNZ
  539.                                                         iN = Closest
  540.                                                         ClosestOccupied[iParentIndex] = 1
  541.                                                         iRange = R*Rnd(1,2)
  542.                                                        
  543.                                                         PositionEntity TPivotB,iX,iY,i
  544.                                                         RotateEntity TPivotB,0,0,0
  545.                                                        
  546.                                                         For n = 0 To gBunchCount-1
  547.                                                                 PositionEntity TPivot,iX,iY,i
  548.                                                                 iQuads[n] = New GQuad
  549.                                                                 rX# = Rnd(-iParentBunchRange,iParentBunchRange)
  550.                                                                 rZ# = Rnd(-iParentBunchRange,iParentBunchRange)
  551.                                                                 iQuads[n]X = RX
  552.                                                                 iQuads[n] = rZ
  553.                                                                 iQuads[n]Size = Rnd(gSize*.7,gSize*2)
  554.                                                                 iQuads[n]Angle = Rnd(359)
  555.                                                                 iQuads[n]DAlpha = GrassAlpha
  556.                                                                 iQuads[n]Reverse = Rand(0,1)
  557.                                                                 iQuads[n]R = ClosestR*255
  558.                                                                 iQuads[n]G = ClosestG*255
  559.                                                                 iQuads[n]B = ClosestB*255
  560.                                                                
  561.                                                                 RotateEntity TPivot,0,iQuads[n]Angle,0
  562.                                                                 AlignToVector TPivot,iNX,iNY,iNZ,2,1
  563.                                                                 MoveEntity TPivot,rX,0,rZ
  564.                                                                 AlignToVector TPivot,iNX,3.6,iNZ,2,1
  565.                                                                
  566.                                                                 If iParentMesh = 0 Then                                                        
  567.                                                                         TFormPoint -iQuads[n]Size,iQuads[n]Size*2,0,TPivot,TPivotB
  568.                                                                         iQuads[n]VX[0] = TFormedX()
  569.                                                                         iQuads[n]VY[0] = TFormedY()
  570.                                                                         iQuads[n]VZ[0] = TFormedZ()
  571.                                                                        
  572.                                                                         TFormPoint iQuads[n]Size,iQuads[n]Size*2,0,TPivot,TPivotB
  573.                                                                         iQuads[n]VX[1] = TFormedX()
  574.                                                                         iQuads[n]VY[1] = TFormedY()
  575.                                                                         iQuads[n]VZ[1] = TFormedZ()
  576.                                                                        
  577.                                                                         TFormPoint iQuads[n]Size,0,0,TPivot,TPivotB
  578.                                                                         iQuads[n]VX[2] = TFormedX()
  579.                                                                         iQuads[n]VY[2] = TFormedY()
  580.                                                                         iQuads[n]VZ[2] = TFormedZ()
  581.                                                                        
  582.                                                                         TFormPoint -iQuads[n]Size,0,0,TPivot,TPivotB
  583.                                                                         iQuads[n]VX[3] = TFormedX()
  584.                                                                         iQuads[n]VY[3] = TFormedY()
  585.                                                                         iQuads[n]VZ[3] = TFormedZ()
  586.                                                                 EndIf
  587.                                                                
  588.                                                                 iQuads[n]
  589. x = EntityPitch(TPivot,1)
  590.                                                                 iQuads[n]
  591. y = EntityYaw(TPivot,1)
  592.                                                                 iQuads[n]
  593. z = EntityRoll(TPivot,1)
  594.                                                                 iQuads[n]gx = EntityX(TPivot,1)
  595.                                                                 iQuads[n]gy = EntityY(TPivot,1)
  596.                                                                 iQuads[n]gz = EntityZ(TPivot,1)
  597.                                                         Next
  598.                                                         gCount = gCount + 1
  599.                                                         cCurrentGrassCount = cCurrentGrassCount + 1
  600.                                                 EndIf
  601.                                         EndIf
  602.                                 Next
  603.                         EndIf
  604.                 EndIf
  605.         Next
  606.        
  607.         gGrassSetOX = CX
  608.         gGrassSetOY = CY
  609.         gGrassSetOZ = CZ
  610.         gGrassSetOYaw = CYaw
  611.         gGrassSetOPitch = CPitch
  612.        
  613.         TPivot = gGrassSetTPivot
  614.        
  615.         Dim SortArray(cCurrentGrassCount-1,2)
  616.         n = 0
  617.         For i.GSprite = Each GSprite
  618.                 PositionEntity TPivot,iX,iY,i
  619.                 SortArray(n,0) = -EntityDistance(TPivot,gGrassSetCamera)+iParentIndex
  620.                 SortArray(n,1) = Handle(i)
  621.                 n = n + 1
  622.         Next
  623.        
  624.         If cCurrentGrassCount > 12 Then gQuickSort(0,cCurrentGrassCount-2,1)
  625.        
  626.         ASpeed# = AlphaFadeSpeed#/100.0
  627.        
  628.         TimeTaken = MilliSecs()
  629.         WTime = MilliSecs()
  630.         For p = 0 To cCurrentGrassCount-1
  631.                 i.GSprite = Object.GSprite(SortArray(p,1))
  632.                 If i <> Null Then
  633.                         For n = 0 To iParentBunchCount-1
  634.                                 iQuads[n]CAlpha = iQuads[n]CAlpha+(iQuads[n]DAlpha-iQuads[n]CAlpha)*ASpeed#
  635.                                
  636.                                 If iQuads[n]CAlpha*iNA > .01 Then
  637.                                         Col = (iParentFrame) Mod iParentTexColumns
  638.                                         Row = (iParentFrame-Col)/iParentTexColumns
  639.                                         US# = iParentTexUS
  640.                                         VS# = iParentTexVS
  641.                                         U# = US * Col
  642.                                         V# = VS * Row
  643.                                        
  644.                                         If iParentWindAffects Then
  645.                                                 WA = ((WindDirection)+iX*2) Mod 359
  646.                                                 WB = Int(WTime*WindSpeed + iX) Mod 359
  647.                                                 If WA > 359 Then WA = WA - 359
  648.                                                 If WA < 0 Then WA = WA + 359
  649.                                                 If WB > 359 Then WB = WB - 359
  650.                                                 If WB < 0 Then WB = WB + 359
  651.                                                 windX# = gGrassSetGSin[WA] * WindPower*gGrassSetGSin[WB]
  652.                                                 windZ# = gGrassSetGCos[WA] * WindPower*gGrassSetGCos[WB]
  653.                                         Else
  654.                                                 windX = 0
  655.                                                 windZ = 0
  656.                                         EndIf
  657.                                        
  658.                                         If iQuads[n]Y = 0 Then iQuads[n]Y = EntityY(TPivot)-iY
  659.                                        
  660.                                         If iIDelete = 0 Then
  661.                                                 RX# = 0 : RY# = 0 : RZ# = 0
  662.                                                
  663.                                                 For beef = 0 To 3
  664.                                                         RX = RX + iQuads[n]VX[beef]
  665.                                                         RY = RY + iQuads[n]VY[beef]
  666.                                                         RZ = RZ + iQuads[n]VZ[beef]
  667.                                                 Next
  668.                                                
  669.                                                 RX = RX / 3
  670.                                                 RY = RY / 3
  671.                                                 RZ = RZ / 3
  672.                                                
  673.                                                 CameraProject gGrassSetCamera,iQuads[n]gx,iQuads[n]gy,iQuads[n]gz
  674.                                                
  675.                                                 iQuads[n]DAlpha = GrassAlpha
  676.                                                
  677.                                                 If ProjectedZ() <= 0 Then
  678.                                                         iQuads[n]DAlpha = 0
  679.                                                 ElseIf iParentMesh = 0
  680.                                                         PX = ProjectedX()
  681.                                                         If PX <= -16 Or PX > gGrassSetW+16 Then
  682.                                                                 iQuads[n]DAlpha = 0
  683.                                                         Else
  684.                                                                 PY = ProjectedY()
  685.                                                                 If PY <= -16 Or PY > gGrassSetH + 16 Then iQuads[n]DAlpha = 0
  686.                                                         EndIf
  687.                                                 EndIf
  688.                                         Else
  689.                                                 iQuads[n]DAlpha = 0
  690.                                         EndIf
  691.                                        
  692.                                         GrassQuadsDrawn = GrassQuadsDrawn + 1
  693.                                         S = iParentTexSurface
  694.                                        
  695.                                         If iParentMesh = 0 Then
  696.                                                 eV = AddVertex(S,iX+iQuads[n]VX[0]+windX,iY+iQuads[n]VY[0],i+iQuads[n]VZ[0]+windZ,U+iQuads[n]Reverse*US,V)
  697.                                                
  698.                                                 AddVertex(S,iX+iQuads[n]VX[1]+windX,iY+iQuads[n]VY[1],i+iQuads[n]VZ[1]+windZ,U+US-iQuads[n]Reverse*US,V)
  699.                                                
  700.                                                 AddVertex(S,iX+iQuads[n]VX[2],iY+iQuads[n]VY[2],i+iQuads[n]VZ[2],U+US-iQuads[n]Reverse*US,V+VS)
  701.                                                
  702.                                                 AddVertex(S,iX+iQuads[n]VX[3],iY+iQuads[n]VY[3],i+iQuads[n]VZ[3],U+iQuads[n]Reverse*US,V+VS)
  703.                                                
  704.                                                 AddTriangle S,eV,eV+1,eV+2
  705.                                                 AddTriangle S,eV+2,eV+3,eV
  706.                                                
  707.                                                 For o = 0 To 3
  708.                                                         VertexColor S,eV+o,iQuads[n]R,iQuads[n]G,iQuads[n]B,iQuads[n]CAlpha
  709.                                                 Next
  710.                                         Else
  711.                                                 Mesh = iParentMesh
  712.                                                 For in = 1 To CountSurfaces(Mesh)
  713.                                                         Surface = GetSurface(Mesh,1)
  714.                                                         lv = CountVertices(S)
  715.                                                         PositionEntity Mesh,iQuads[n]gx,iQuads[n]gy,iQuads[n]gz,1
  716.                                                         RotateEntity Mesh,iQuads[n]
  717. x+windX*3,iQuads[n]
  718. y,iQuads[n]
  719. z+windZ*3,1
  720.                                                         If iParentWindAffects Then AlignToVector Mesh,windX/2,3.6,windZ/2,2,.3
  721.                                                         For iv = 0 To CountVertices(Surface)-1
  722.                                                                 TFormPoint VertexX(Surface,iv)*iQuads[n]Size,VertexY(Surface,iv)*iQuads[n]Size,VertexZ(Surface,iv)*iQuads[n]Size,Mesh,0
  723.                                                                 nv = AddVertex(S,TFormedX(),TFormedY(),TFormedZ(),U+VertexU(Surface,iv)*US,V+VertexV(Surface,iv)*VS,VertexW(Surface,iv))
  724.                                                                 VertexColor S,nv,iQuads[n]R,iQuads[n]G,iQuads[n]B,iQuads[n]CAlpha
  725.                                                                 TFormNormal VertexNX(Surface,iv),VertexNY(Surface,iv),VertexNZ(Surface,iv),Mesh,0
  726.                                                                 VertexNormal S,nv,TFormedX(),TFormedY(),TFormedZ()
  727.                                                         Next
  728.                                                         For iv = 0 To CountTriangles(Surface)-1
  729.                                                                 AddTriangle S,lv+TriangleVertex(Surface,iv,0),lv+TriangleVertex(Surface,iv,1),lv+TriangleVertex(Surface,iv,2)
  730.                                                         Next
  731.                                                 Next
  732.                                                 Surface = 0
  733.                                         EndIf
  734.                                 EndIf
  735.                         Next
  736.                 EndIf
  737.         Next
  738.         GrassUpdateTime = MilliSecs()-TimeTaken
  739. End Function
  740.  
  741. ;; Specify a mesh, if you want to specify a specific surface in the mesh then you can do that as well via the Surface argument
  742. Function AddGrassObject(GObject,Surface=0,Lightmap$="")
  743.         g.GrassObject = New GrassObject
  744.         gEntity = GObject
  745.         gSurface = Surface
  746.        
  747.         Local Mi#[2]
  748.         Local Ma#[2]
  749.        
  750.         If Lightmap <> "" Then
  751.                 Tex = LoadTexture(Lightmap,1+2)
  752.                 If Tex Then
  753.                         T = TextureBuffer(Tex)
  754.                         W = TextureWidth(Tex)
  755.                         H = TextureHeight(TeX)
  756.                 EndIf
  757.         EndIf
  758.        
  759.         If Surface <> 0 Then
  760.                 For n = 0 To CountVertices(Surface)-1
  761.                         TFormPoint VertexX(Surface,n),VertexY(Surface,n),VertexZ(Surface,n),GObject,0
  762.                         TX# = TFormedX()
  763.                         TY# = TFormedY()
  764.                         TZ# = TFormedZ()
  765.                         If TX < Mi[0] Then Mi[0] = TX
  766.                         If TY < Mi[1] Then Mi[1] = TY
  767.                         If TZ < Mi[2] Then Mi[2] = TZ
  768.                        
  769.                         If TX > Ma[0] Then Ma[0] = TX
  770.                         If TY > Ma[1] Then Ma[1] = TY
  771.                         If TZ > Ma[2] Then Ma[2] = TZ
  772.                 Next
  773.         Else
  774.                 For k = 1 To CountSurfaces(GObject)
  775.                         Surface = GetSurface(GObject,k)
  776.                         For n = 0 To CountVertices(Surface)-1
  777.                                 TFormPoint VertexX(Surface,n),VertexY(Surface,n),VertexZ(Surface,n),GObject,0
  778.                                 TX# = TFormedX()
  779.                                 TY# = TFormedY()
  780.                                 TZ# = TFormedZ()
  781.                                 If TX < Mi[0] Then Mi[0] = TX
  782.                                 If TY < Mi[1] Then Mi[1] = TY
  783.                                 If TZ < Mi[2] Then Mi[2] = TZ
  784.                                
  785.                                 If TX > Ma[0] Then Ma[0] = TX
  786.                                 If TY > Ma[1] Then Ma[1] = TY
  787.                                 If TZ > Ma[2] Then Ma[2] = TZ
  788.                         Next
  789.                 Next
  790.                 Surface = 0
  791.         EndIf
  792.         If Tex Then LockBuffer(T)
  793.        
  794.         NodeWidth# = (Ma[0]-Mi[0])/NODE_SEG
  795.         NodeDepth# = (Ma[2]-Mi[2])/NODE_SEG
  796.         RowWidth# = (Ma[0]-Mi[0])
  797.        
  798.         For v# = 0 To NODE_SEG-1
  799.                 r.NodeRow = New NodeRow
  800.                 rX = Mi[0]
  801.                 r = Mi[2]+(Ma[2]-Mi[2])*(v/NODE_SEG)
  802.                 rWidth = RowWidth
  803.                 rDepth = NodeDepth
  804.                 For u# = 0 To NODE_SEG-1
  805.                         If Surface <> 0 Then
  806.                                 RI = Ray_Intersect_Surface(GObject,Surface,Mi[0]+(Ma[0]-Mi[0])*(u/NODE_SEG),Ma[1],Mi[2]+(Ma[2]-Mi[2])*(v/NODE_SEG),.01,Mi[1]-Ma[1],.01,0,1)
  807.                         Else
  808.                                 RI = Ray_Intersect_Mesh(GObject,Mi[0]+(Ma[0]-Mi[0])*(u/NODE_SEG),Ma[1]+1,Mi[2]+(Ma[2]-Mi[2])*(v/NODE_SEG),.01,Mi[1]-Ma[1]-2,.01,0,1)
  809.                         EndIf
  810.                        
  811.                         Node = v*NODE_SEG+u
  812.                        
  813.                         gNodes[Node] = New GNode
  814.                         rNodes[u] = gNodes[Node]
  815.                         gNodes[Node]U = RI
  816.                        
  817.                         If Tex Then
  818.                                 X = (u/NODE_SEG)*W
  819.                                 Y = (v/NODE_SEG)*H
  820.                                 Pixel = ReadPixelFast(X,Y,T)
  821.                                 cA# = Pixel Shr 24 And 255
  822.                                 cR# = Pixel Shr 16 And 255
  823.                                 cG# = Pixel Shr 8 And 255
  824.                                 cB# = Pixel And 255
  825.                                 gNodes[Node]R = cR
  826.                                 gNodes[Node]G = cG
  827.                                 gNodes[Node]B = cB
  828.                                 gNodes[Node]A = cA/255
  829.                         Else
  830.                                 gNodes[Node]R = 255
  831.                                 gNodes[Node]G = 255
  832.                                 gNodes[Node]B = 255
  833.                                 gNodes[Node]A = 1
  834.                         EndIf
  835.                        
  836.                         If RI Then
  837.                                 gNodes[Node]X = IntersectedX
  838.                                 gNodes[Node]Y = IntersectedY
  839.                                 gNodes[Node] = IntersectedZ
  840.                                 gNodes[Node]NX = TriangleNX(IntersectedSurface,IntersectedTriangle)
  841.                                 gNodes[Node]NY = TriangleNY(IntersectedSurface,IntersectedTriangle)
  842.                                 gNodes[Node]NZ = TriangleNZ(IntersectedSurface,IntersectedTriangle)
  843.                                 gNodes[Node]Width = NodeWidth
  844.                                 gNodes[Node]Depth = NodeDepth
  845.                                
  846.                                 If gNodes[Node]NY < YNormal Then
  847.                                         gNodes[Node]U = 0
  848.                                 EndIf
  849.                         EndIf
  850.                 Next
  851.         Next
  852.         If Tex Then
  853.                 UnlockBuffer(T)
  854.                 FreeTexture(Tex)
  855.         EndIf
  856.        
  857.         Return Handle(g)
  858. End Function
  859.  
  860. ;; Export a nodeset
  861. Function ExportNodes(Path$)
  862.         FOut = WriteFile(Path$+".unltemp")
  863.         If Not FOut Then Return False
  864.        
  865.         WriteString FOut,"NODESET_"
  866.        
  867.         For r.NodeRow = Each NodeRow
  868.                 WriteFloat FOut,rX
  869.                 WriteFloat FOut,r
  870.                 WriteFloat FOut,rWidth
  871.                 WriteFloat FOut,rDepth
  872.                 For n = 0 To NODE_SEG-1
  873.                         i.GNode = rNodes[n]
  874.                         WriteFloat FOut,iX
  875.                         WriteFloat FOut,iY
  876.                         WriteFloat FOut,i
  877.                         WriteFloat FOut,iWidth
  878.                         WriteFloat FOut,iDepth
  879.                         WriteFloat FOut,iNX
  880.                         WriteFloat FOut,iNY
  881.                         WriteFloat FOut,iNZ
  882.                         WriteByte FOut,iR
  883.                         WriteByte FOut,iG
  884.                         WriteByte FOut,iB
  885.                         WriteByte FOut,iA*255
  886.                         WriteByte FOut,iU
  887.                 Next
  888.         Next
  889.        
  890.         CloseFile FOut
  891.        
  892.         bz2( 0, 9, Path$+".unltemp", Path$+".unl" )
  893.         DeleteFile Path$+".unltemp"
  894.        
  895.         Return True
  896. End Function
  897.  
  898. ;; Imports a nodeset.  Nodesets should be rebuilt after -any- changes to anything grass is placed upon
  899. Function ImportNodes(Path$)
  900.         If FileType(Path+".unl") <> 1 Then Return False
  901.        
  902.         bz2( 1, 0, Path+".unl", Path+".unltemp" )
  903.        
  904.         FIn = ReadFile(Path$+".unltemp")
  905.         If Not FIn Then Return False
  906.        
  907.         ID$ = ReadString(FIn)
  908.         If ID$ <> "NODESET_" Then
  909.                 CloseFile FIn
  910.                 Return False
  911.         EndIf
  912.        
  913.         While Not Eof(FIn)
  914.                 r.NodeRow = New NodeRow
  915.                 rX = ReadFloat(FIn)
  916.                 r = ReadFloat(FIn)
  917.                 rWidth = ReadFloat(FIn)
  918.                 rDepth = ReadFloat(FIn)
  919.                 For n = 0 To NODE_SEG-1
  920.                         i.GNode = New GNode
  921.                         iX = ReadFloat(FIn)
  922.                         iY = ReadFloat(FIn)
  923.                         i = ReadFloat(FIn)
  924.                         iWidth = ReadFloat(FIn)
  925.                         iDepth = ReadFloat(FIn)
  926.                         iNX = ReadFloat(FIn)
  927.                         iNY = ReadFloat(FIn)
  928.                         iNZ = ReadFloat(FIn)
  929.                         iR = ReadByte(FIn)
  930.                         iG = ReadByte(FIn)
  931.                         iB = ReadByte(FIn)
  932.                         iA = Float(ReadByte(FIn))/255
  933.                         iU = ReadByte(FIn)
  934.                         rNodes[n] = i
  935.                 Next
  936.         Wend
  937.        
  938.         DeleteFile Path+".unltemp"
  939.        
  940.         CloseFile FIn
  941.         Return True
  942. End Function
  943.  
  944. ;; Removes an object useable by the grass system
  945. Function RemoveGrassObject(GObject)
  946.         g.GrassObject = Object.GrassObject(GObject)
  947.         If g = Null Then Return False
  948.         For n = 0 To NODE_SEG-1
  949.                 For i = 0 To NODE_SEG-1
  950.                         Delete gRows[n]Nodes[i]
  951.                 Next
  952.                 Delete gRows[n]
  953.         Next
  954.         Delete g
  955.         Return True
  956. End Function
  957.  
  958. ;; sswift's ray-triangle intersection code, butchered by me
  959. Function Ray_Intersect_Triangle(Px#, Py#, Pz#, Dx#, Dy#, Dz#, V0x#, V0y#, V0z#, V1x#, V1y#, V1z#, V2x#, V2y#, V2z#, Extend_To_Infinity=True, Cull_Backfaces=False)
  960.         E1x# = V2x# - V0x#
  961.         E1y# = V2y# - V0y#
  962.         E1z# = V2z# - V0z#
  963.  
  964.         E2x# = V1x# - V0x#
  965.         E2y# = V1y# - V0y#
  966.         E2z# = V1z# - V0z#
  967.  
  968.         Hx# = (Dy# * E2z#) - (E2y# * Dz#)
  969.         Hy# = (Dz# * E2x#) - (E2z# * Dx#)
  970.         Hz# = (Dx# * E2y#) - (E2x# * Dy#)
  971.  
  972.         A# = (E1x# * Hx#) + (E1y# * Hy#) + (E1z# * Hz#)
  973.  
  974.         If (Cull_Backfaces = True) And (A# >= 0) Then Return False
  975.                
  976.         If (A# > -0.00001) And (A# < 0.00001) Then Return False
  977.        
  978.         F# = 1.0 / A#
  979.  
  980.         Sx# = Px# - V0x#
  981.         Sy# = Py# - V0y#
  982.         Sz# = Pz# - V0z#
  983.        
  984.         U# = F# * ((Sx# * Hx#) + (Sy# * Hy#) + (Sz# * Hz#))
  985.        
  986.         If (U# < 0.0) Or (U# > 1.0) Return False
  987.  
  988.         Qx# = (Sy# * E1z#) - (E1y# * Sz#)
  989.         Qy# = (Sz# * E1x#) - (E1z# * Sx#)
  990.         Qz# = (Sx# * E1y#) - (E1x# * Sy#)
  991.        
  992.         V# = F# * ((Dx# * Qx#) + (Dy# * Qy#) + (Dz# * Qz#))
  993.        
  994.         If (V# < 0.0) Or ((U# + V#) > 1.0) Return False
  995.  
  996.         T# = F# * ((E2x# * Qx#) + (E2y# * Qy#) + (E2z# * Qz#))
  997.         IntersectedX# = Px+Dx*T
  998.         IntersectedY# = Py+Dy*T
  999.         IntersectedZ# = Pz+Dz*T
  1000.  
  1001.         If (T# < 0) Then Return False
  1002.  
  1003.         If (Extend_To_Infinity = False) And (T# > 1) Return False
  1004.  
  1005.         Return True
  1006. End Function
  1007.  
  1008. Function Ray_Intersect_Mesh(Mesh, Px#, Py#, Pz#, Dx#, Dy#, Dz#, Extend_To_Infinity=True, Cull_Backfaces=False)
  1009.         Surfaces = CountSurfaces(Mesh)
  1010.         If Surfaces > 0
  1011.                 For SurfaceLoop = 1 To Surfaces
  1012.                         Surface = GetSurface(Mesh, SurfaceLoop)
  1013.        
  1014.                         Tris  = CountTriangles(Surface)
  1015.                         For TriLoop = 0 To Tris-1
  1016.        
  1017.                                 V0 = TriangleVertex(Surface, TriLoop, 0)
  1018.                                 V1 = TriangleVertex(Surface, TriLoop, 1)
  1019.                                 V2 = TriangleVertex(Surface, TriLoop, 2)
  1020.                
  1021.                                 V0x# = VertexX#(Surface, V0)
  1022.                                 V0y# = VertexY#(Surface, V0)
  1023.                                 V0z# = VertexZ#(Surface, V0)
  1024.  
  1025.                                 V1x# = VertexX#(Surface, V1)
  1026.                                 V1y# = VertexY#(Surface, V1)
  1027.                                 V1z# = VertexZ#(Surface, V1)
  1028.  
  1029.                                 V2x# = VertexX#(Surface, V2)
  1030.                                 V2y# = VertexY#(Surface, V2)
  1031.                                 V2z# = VertexZ#(Surface, V2)
  1032.  
  1033.                                 TFormPoint V0x#, V0y#, V0z#, Mesh, 0
  1034.                                 V0x# = TFormedX#()
  1035.                                 V0y# = TFormedY#()
  1036.                                 V0z# = TFormedZ#()
  1037.  
  1038.                                 TFormPoint V1x#, V1y#, V1z#, Mesh, 0
  1039.                                 V1x# = TFormedX#()
  1040.                                 V1y# = TFormedY#()
  1041.                                 V1z# = TFormedZ#()
  1042.                        
  1043.                                 TFormPoint V2x#, V2y#, V2z#, Mesh, 0
  1044.                                 V2x# = TFormedX#()
  1045.                                 V2y# = TFormedY#()
  1046.                                 V2z# = TFormedZ#()
  1047.                                
  1048.                                 Intersected = Ray_Intersect_Triangle(Px#, Py#, Pz#, Dx#, Dy#, Dz#, V0x#, V0y#, V0z#, V1x#, V1y#, V1z#, V2x#, V2y#, V2z#, Extend_To_Infinity, Cull_Backfaces)
  1049.                                 If Intersected Then
  1050.                                         IntersectedTriangle = TriLoop
  1051.                                         IntersectedSurface = Surface
  1052.                                         Return True
  1053.                                 EndIf
  1054.                         Next
  1055.                 Next
  1056.         EndIf
  1057.        
  1058.         IntersectedTriangle = -1
  1059.         IntersectedSurface = -1
  1060.        
  1061.         Return False
  1062. End Function
  1063.  
  1064. ;; hacked the Ray_Intersect_Mesh function so it can be used on surfaces as well
  1065. Function Ray_Intersect_Surface(Mesh, Surface, Px#, Py#, Pz#, Dx#, Dy#, Dz#, Extend_To_Infinity=True, Cull_Backfaces=False)
  1066.         Tris  = CountTriangles(Surface)
  1067.         For TriLoop = 0 To Tris-1
  1068.                 V0 = TriangleVertex(Surface, TriLoop, 0)
  1069.                 V1 = TriangleVertex(Surface, TriLoop, 1)
  1070.                 V2 = TriangleVertex(Surface, TriLoop, 2)
  1071.  
  1072.                 V0x# = VertexX#(Surface, V0)
  1073.                 V0y# = VertexY#(Surface, V0)
  1074.                 V0z# = VertexZ#(Surface, V0)
  1075.  
  1076.                 V1x# = VertexX#(Surface, V1)
  1077.                 V1y# = VertexY#(Surface, V1)
  1078.                 V1z# = VertexZ#(Surface, V1)
  1079.  
  1080.                 V2x# = VertexX#(Surface, V2)
  1081.                 V2y# = VertexY#(Surface, V2)
  1082.                 V2z# = VertexZ#(Surface, V2)
  1083.  
  1084.                 TFormPoint V0x#, V0y#, V0z#, Mesh, 0
  1085.                 V0x# = TFormedX#()
  1086.                 V0y# = TFormedY#()
  1087.                 V0z# = TFormedZ#()
  1088.  
  1089.                 TFormPoint V1x#, V1y#, V1z#, Mesh, 0
  1090.                 V1x# = TFormedX#()
  1091.                 V1y# = TFormedY#()
  1092.                 V1z# = TFormedZ#()
  1093.        
  1094.                 TFormPoint V2x#, V2y#, V2z#, Mesh, 0
  1095.                 V2x# = TFormedX#()
  1096.                 V2y# = TFormedY#()
  1097.                 V2z# = TFormedZ#()
  1098.                
  1099.                 Intersected = Ray_Intersect_Triangle(Px#, Py#, Pz#, Dx#, Dy#, Dz#, V0x#, V0y#, V0z#, V1x#, V1y#, V1z#, V2x#, V2y#, V2z#, Extend_To_Infinity, Cull_Backfaces)
  1100.                 If Intersected Then
  1101.                         IntersectedTriangle = TriLoop
  1102.                         IntersectedSurface = Surface
  1103.                         Return True
  1104.                 EndIf
  1105.         Next
  1106.  
  1107.         IntersectedTriangle = -1
  1108.         IntersectedSurface = -1
  1109.        
  1110.         Return False
  1111. End Function
  1112.  
  1113. ;; some functions of my own...
  1114. ;; just to note, these don't return the actual normals of a triangle, they only return the averaged normals of the vertices.  in most cases- when it comes to terrain- this is enough
  1115. Function TriangleNX#(Surface,Triangle)
  1116.         Local NX#
  1117.         For N = 0 To 2
  1118.                 NX# = NX# + VertexNX(Surface,TriangleVertex(Surface,Triangle,N))
  1119.         Next
  1120.         Return NX / 3
  1121. End Function
  1122.  
  1123. Function TriangleNY#(Surface,Triangle)
  1124.         Local NY#
  1125.         For N = 0 To 2
  1126.                 NY# = NY# + VertexNY(Surface,TriangleVertex(Surface,Triangle,N))
  1127.         Next
  1128.         Return NY / 3
  1129. End Function
  1130.  
  1131. Function TriangleNZ#(Surface,Triangle)
  1132.         Local NZ#
  1133.         For N = 0 To 2
  1134.                 NZ# = NZ# + VertexNZ(Surface,TriangleVertex(Surface,Triangle,N))
  1135.         Next
  1136.         Return NZ / 3
  1137. End Function
  1138.  
  1139.  
  1140.  
  1141. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1142. ;;;; EXAMPLE CODE       ;;;;
  1143. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1144.  
  1145. ;; FPS
  1146. Global FPS,LastCheck,Frames
  1147. Function GetFPS()
  1148.         Frames = Frames + 1
  1149.        
  1150.         If MilliSecs() > LastCheck+1000 Then
  1151.                 LastCheck = MilliSecs()
  1152.                 FPS = Frames
  1153.                 Frames = 0
  1154.         EndIf
  1155.         Return FPS
  1156. End Function
  1157.  
  1158. ;; Graphics Mode
  1159. Graphics 320,240,32,2
  1160. If FileType("grass3.tga") <> 1 Then
  1161.         bz2 1,0,"grass3.tga.bz2","grass3.tga"
  1162.         Delay 20
  1163. EndIf
  1164. ClsColor 255,255,255
  1165. Cls
  1166. Color 0,0,0
  1167. Graphics3D 800,600,32,Int(Replace(Lower(Input$("Windowed? Y/N        ")),"y","1"))+1
  1168. ;; Camera
  1169. C = CreateCamera()
  1170.  
  1171. ;; Terrain - objects no longer need pick-modes
  1172. T = LoadMesh("terrain.3ds")
  1173. ScaleEntity T,40*.4,35*.4,40*.4
  1174. TranslateEntity T,0,-70,0
  1175.  
  1176. ;; for some reason whenever i add or remove something from this the surface indices change, so now i have to check for vertex count to know which one i need
  1177. If CountVertices(GetSurface(t,1)) > CountVertices(GetSurface(t,2)) Then
  1178.         s1 = 1
  1179.         s2 = 2
  1180. Else
  1181.         s1 = 2
  1182.         s2 = 1
  1183. EndIf
  1184.  
  1185. B = LoadBrush("detail.png")
  1186. GetBrushTexture(B)
  1187. PaintSurface GetSurface(T,s1),B
  1188. FreeBrush B
  1189. B = LoadBrush("path.png")
  1190. PaintSurface GetSurface(T,s2),B
  1191. FreeBrush B
  1192.  
  1193. ;; Lighting
  1194. L = CreateLight(2)
  1195. PositionEntity L,0,500,0
  1196. LightRange L,200
  1197. LightColor L,255,255,255
  1198. AmbientLight 96,96,96
  1199.  
  1200. ;; Grass
  1201. InitGrass(C,50,180)
  1202. Tex = LoadGrassTexture("grass3.tga",2,4)
  1203.  
  1204. SetSpawner(CreateGrassSpawner(Tex,0,.9/1.5,0,1,200),3,2)
  1205. SetSpawner(CreateGrassSpawner(Tex,1,1.65/1.5,0,3,200),3,7)
  1206. SetSpawner(CreateGrassSpawner(Tex,2,.8/1.5,0,1,200),2.3,3)
  1207. ;SetSpawner(CreateGrassSpawner(Tex,3,1/1.5,0,1,200),2.3,1)
  1208. ;SetSpawner(CreateGrassSpawner(Tex,4,1/1.5,0,1,200),2.3,1)
  1209. SetSpawner(CreateGrassSpawner(Tex,5,1.65/1.5,0,4,200),2,1)
  1210.  
  1211. If FileType("terrain.unl") Then
  1212.         Print "Rebuild object nodeset?  Y/N"
  1213.         Repeat
  1214.                 k = Asc(Lower(Chr(WaitKey())))
  1215.         Until k = Asc("y") Or k = Asc("n")
  1216.         If k = Asc("y") Then
  1217.                 Print "Rebuilding..."
  1218.                 DeleteFile "terrain.unl"
  1219.                 TerrainObject = AddGrassObject(T,GetSurface(T,s1))
  1220.                 ExportNodes("terrain")
  1221.         Else
  1222.                 Print "Loading nodeset..."
  1223.                 ImportNodes("terrain")
  1224.         EndIf
  1225.         Print "Done."
  1226. Else
  1227.         TerrainObject = AddGrassObject(T,GetSurface(T,s1))
  1228.         ExportNodes("terrain")
  1229. EndIf
  1230.  
  1231. ;; Tweening
  1232. MFPS = 60
  1233. Period = 1000/MFPS
  1234. Time = MilliSecs()-Period
  1235.  
  1236. S = CreateSprite()
  1237. ScaleSprite S,10,10
  1238. EntityFX S,1+16
  1239. EntityBlend S,3
  1240. EntityOrder S,-5
  1241. PositionEntity S,0,0,2
  1242. EntityColor S,255,255,255
  1243. EntityParent S,C,1
  1244.  
  1245. Alpha# = 1.4
  1246.  
  1247. CameraClsColor C,191,223,245
  1248.  
  1249. Repeat
  1250.         Repeat
  1251.                 Elapsed = MilliSecs() - Time
  1252.         Until Elapsed
  1253.        
  1254.         Ticks = Elapsed / Period
  1255.         Tween# = Float(Elapsed Mod Period) / Float(Period)
  1256.        
  1257.         For U = 0 To Ticks
  1258.                 Time = Time + Period
  1259.                
  1260.                 If U = Ticks Then
  1261.                         CaptureWorld
  1262.                 EndIf
  1263.                
  1264.                 If Alpha < .01 Then
  1265.                         Alpha = 0
  1266.                 Else
  1267.                         Alpha# = Alpha# * .98
  1268.                 EndIf
  1269.                 EntityAlpha S,Alpha
  1270.                
  1271.                 ;; Camera
  1272.                 PitchAccel# = MouseYSpeed()*.05
  1273.                 PanAccel# = -MouseXSpeed()*.05
  1274.                
  1275.                 Pitch# = Pitch + PitchAccel
  1276.                 Pan# = Pan + PanAccel
  1277.                 If Pitch > 80 Then Pitch = 80
  1278.                 If Pitch < -80 Then Pitch = -80
  1279.                 RotateEntity C,Pitch,Pan,0
  1280.                 MoveMouse GraphicsWidth()/2,GraphicsHeight()/2
  1281.                 MoveV# =(KeyDown(200)-KeyDown(208))*.7
  1282.                 MoveEntity C,0,0,MoveV
  1283.                
  1284.                 ;; Grass
  1285.                 UpdateGrass()
  1286.                
  1287.                 ;; Graphics
  1288.                 UpdateWorld
  1289.         Next
  1290.        
  1291.         ;; Graphics
  1292.         RenderWorld Tween
  1293.        
  1294.         ;; FPS
  1295.         GetFPS()
  1296.         Text 2,2,"FPS : "+FPS
  1297.        
  1298.         Flip 0
  1299. Until KeyHit(1)
  1300.  
  1301. ;; Grass
  1302. KillGrass()
  1303.  
  1304. ;; Graphics
  1305. ClearWorld
  1306. End


Comments :


Rook Zimbabwe(Posted 1+ years ago)

 Looks GREAT! but can it NOT put grass where it senses a MODEL like a building??? That would be a great tool too!


N(Posted 1+ years ago)

 Rook: Refer to the AddGrassObject command.That function basically tells it where it can and can't put grass.


Doggie(Posted 1+ years ago)

 There's a bug in this linewindX# = gGrassSetGSin[(WA+iX*2) Mod 359] * WindPower*gGrassSetGSin[Int(MilliSecs()*WindSpeed) Mod 359]If my camera goes below 30(in the y direction) then I get a "Blitz Array Index Out of Bounds" error.


N(Posted 1+ years ago)

 That doesn't make sense, since that line has no relation to the Y position of the camera.  I'd say it's just trying to access part of the array above 359 or below 0.  I'll look into it.


Rimmsy(Posted 1+ years ago)

 verrrrrry nice.


N(Posted 1+ years ago)

 Added two new functions and sortof improved (in my opinion) the way grass is spawned.I'm trying to find a way to avoid the line-picking to get the positions and normals of the grass bunches, but so far every idea I've had has been shot down due to me forgetting one detail or another.


mrtricks(Posted 1+ years ago)

 Very nice but gets some nasty slowdown on mine...


N(Posted 1+ years ago)

 mrtricks: I'd say the vertex lighting and 10,000-detail Blitz terrain in the example code is mostly to blame for that.  But some of the blame lies with my system as well for using line picks for every 5 or so (it's a variable amount specified by a constant) grass bunches.Anyhow, I've implemented a texture system that allows you to reduce the surface count to one surface per 4 to 8 grass sprite textures.  I'm still working on the line-picking problem though, since that's slow as hell if the growth rate is high.


N(Posted 1+ years ago)

 The new texture system is implemented, and I've removed the line-picking and switched to using sswift's triangle intersect functions.


Kornflex(Posted 1+ years ago)

 Fantastic and thanks you for that !


Picklesworth(Posted 1+ years ago)

 This is very nice Noel, as always!I hope you keep working on this :DWhen accompanied with a nice grass texture for the ground the effect is even better.


N(Posted 1+ years ago)

 <ego>I am such a nice person -_-</ego>But in all seriousness, without sswift's ray intersect code, this would be a hell of a lot slower than it is now.I've got plans for pre-compiling node arrays now, too, in order to speed stuff up.


Picklesworth(Posted 1+ years ago)

 oh, ray intersect code I need that badly! thanks for telling me there's a such thing :DIt's odd, but I can't see any grass in the newest version :(


N(Posted 1+ years ago)

 Not sure why not.  Also, it no longer works on Blitz terrains (because of the ray intersect code- you could, as an example, generate a mesh that is based off of a loaded Blitz terrain to make it work again though), so if you're using one that might be the problem.


Picklesworth(Posted 1+ years ago)

 I'm just using your demo code alone along with the media packEdit:Decided not to add any more posts...When I copy and paste the data written on this page rather than download the .bb file, there is a small region that has grass on it. It takes a while to notice though... Is it supposed to be just one small segment?


N(Posted 1+ years ago)

 Hm.. well I made sure to test the example code that came in the zip, and it worked fine.  I'll take another look at it to see if there's anything I've left out.


N(Posted 1+ years ago)

 The idea I had for precompiling grass nodes is now implemented, along with some minor optimizations and changes to make it more efficient at times.


Picklesworth(Posted 1+ years ago)

 Ooh, works much better now. Thanks noel!Lots of variables to play with too :)This is probably the most useful grass system I've seen in blitz yet. I can't wait to try it out with some nice sprites :D


N(Posted 1+ years ago)

 Updates:-Vertex positions are precalculated, this is to save time on redrawing them per-update.-Nodes used to apply to backfacing triangles (top-down, remember), so if you, for example, had an arch over a section of terrain, it would have created grass on the underside of it: not pretty.  This is 'fixed'.-You can now have a color-map applied to grass nodes when you add an object to the list.  It's an extra argument to AddGrassObject().  Full RGBA support (meaning you control red, green, blue, and alpha of areas).Bugs:-Sometimes grass spawns in the air.  This bug is obviously due to some error (of mine, likely, since the ray intersect code works fine in normal cases) when nodes are being generated.  I have no idea what's causing it yet, but I know where it is and as such it's isolated.<div class="quote"> This is probably the most useful grass system </div>Well, it's only one of three I think.  In any case, I feel this is a really useful module of sorts for adding a great deal of depth to your environments.


Picklesworth(Posted 1+ years ago)

 hm, that's the problem.It seems that when I run this in debug mode, or with a complete compilation in a folder other than the original folder of the .bb file, it will scatter the trail texture everywhere and the grass doesn't show up except in the valley.


N(Posted 1+ years ago)

 Updated- Added support for meshes (this is single-surface support, so even if the mesh you're referencing is using multiple surfaces it will put it all in the same surface).- Various bug fixes.- Importing and exporting node sets.- Per-spawner wind settings.You need to be careful with the mesh spawners, because if you use a lot of polygons then you will probably end up with a pretty damn slow game.  I'm working on some ideas to make this faster, but at the moment you'll have to live with this.


N(Posted 1+ years ago)

 Updated- No more distance checks when determining which node a grass bunch gets placed on- now it checks rows and when the position is determined to be inside of the row it places the quad inside of the node it's inside of.- Added the function SetSpawner() which allows you to choose some ranges.  You might or might not need to use this, it depends on your scene.


Picklesworth(Posted 1+ years ago)

 This newer version seems a bit slower to me... Still great nontheless! And nicer :)


Mustang(Posted 1+ years ago)

 Wooo!


Kissme(Posted 1+ years ago)

 Damned !!! veyr veyr nice stuff ! TYKiss.


N(Posted 1+ years ago)

 I just noticed that starting at my last comment, the time between comments has incremented by two days each.Mr. Pickles commented after two days;Mustang after four;KissKool after six.


morduun(Posted 1+ years ago)

 Very, very slow (6-10fps) with obvious and noticeable pop-in on my system: p3/1G, ge4ti/4400, 256MB ram, WinXP Pro.


Ion-Storm(Posted 1+ years ago)

 Make sure DEBUG is turned off in the editor, ELSE it will slow down..RegardsIon.


morduun(Posted 1+ years ago)

 This goes without saying.  But it also doesn't help the framerate on my rig, either, as I wasn't using it with debug in the first place.


FireballStarfish(Posted 1+ years ago)

 The image doesn't work, and neither does the link :(


N(Posted 1+ years ago)

 It's 3 years old.  Cheers.


YellBellzDotCom(Posted 1+ years ago)

 Does anyone have this available? I sure would like to take a gander! If so, please email to hilbily001@...Thanks!!


N(Posted 1+ years ago)

 Well, you got the source code up there.


 

SimplePortal 2.3.6 © 2008-2014, SimplePortal