Plaing a grass-mesh on the map

Blitz3D Forums/Blitz3D Programming/Plaing a grass-mesh on the map

Affje(Posted 2004) [#1]
hi

i want to place grass on my map, but it should be an algorithm. the grass should be on the whole map. what can i do??

p.s. i am german and my englisch is not the best ^^


asdfasdf(Posted 2004) [#2]
You could create a plane and then put the grass image on it. What do you mean by algorithm.


Rob(Posted 2004) [#3]
He means 3D grass. To do this, you want to re-use your grass sprites and put them just ahead of the player.


Affje(Posted 2004) [#4]
it's a 3d model.
i want to load it once and copy it often and place it on various places on my map.
i playced every tree with all coordinates -.- (was a lot of work ^^). so i am searching a code-example where i can place one mesh (randomly) on my map very often.


Isaac P(Posted 2004) [#5]
;Place grass function

;srcGrass is the source mesh you want to place around the map
;amount is the amount of grass you want
;xStart is the beggining X location of you map, and xEnd is the end
;Ditto for zStart and zEnd


Function placeGrass(srcGrass,amount,xStart,xEnd,zStart,zEnd)

For g = 1 To amount

	grassX = Rnd(xStart,xEnd)
	grassZ = Rnd(zStart,zEnd)
				
	LinePick(grassX, 256, grassZ, 0, -512, 0)
	grassY = PickedY()

	grass = CopyEntity(srcGrass)
	PositionEntity grass,grassX,grassY,grassZ

Next



Isaac P(Posted 2004) [#6]
Btw.. that might not work.. but if it doesnt you get the idea :)


Tiggertooth(Posted 2004) [#7]
Your placeGrass function is the right idea, but it is going to place grass in a square. A simple fix to this would be to pass in radius around the player you want grass. Then the grassX = radius * cos( Rnd(0,360) ). grassY uses sin.

This result in a smoother circle of grass. You might also want to set the fade distance on these entities. Finally, you'll want to track the player's position. You want to place grass sprites further ahead then can currently be seen (some start faded out). As you move, these will fade in. That way it will always look seamless. Track the player's delta. When the player exceeds a certain threshold, it is time to place additional grass. But all additional grass needs to be placed in a donut at the faded out distance. I solved this with the placeGrass function taking a minimum and maximum radius. On initial placement, minimum radius is 0, but thereafter minimum placement is FADE_RANGE.

If you really want to be correct, grab the texture the LinePick hits and check that it is a "grass compatible" texture. Now you don't have to worry about grass showing up on rooftops. Ideally you pre-compute the LinePicks and texture lookup. You can then just look up into a heightmap and use a special value to indicate grass-not-allowed.

If someone posts me a grass .b3d I'd throw together an example. I used this algorithm for the grass in CZ.

Ken


Tiggertooth(Posted 2004) [#8]
To clarify, the radius needs to be a range, and you need to pick a random number in that range. The formulas will give you random points in a circle from min to max range. Offset from player's position.

I'm putting together a sample to show how I did it a couple of years ago. Probably can draw more grass now :-)


Affje(Posted 2004) [#9]
http://home.arcor.de/affje/Gras.rar

plz, and thx for your answers!


Affje(Posted 2004) [#10]
is the grass mesh good??

other question:
knows somebody a (free!!) editor like terraed, where i can load meshes and create a terrain (easily..)??

i dont have enouzgh money to buy terraed :((((


asdfasdf(Posted 2004) [#11]
What is this .rar extension open wtih?


Affje(Posted 2004) [#12]
with winrar,
www.rarlabs.com


Affje(Posted 2004) [#13]
does it work so far??


eBusiness(Posted 2004) [#14]
Well, there is no code, but making a 3D model for a grass sprite might be overkill. Just use normal sprites.


Doggie(Posted 2004) [#15]
I'd like to get ahold of that grass code so be sure to post it.

@Affje - Look no further than my sig for a link to a FREE world editor. It's not as up to date as Terraed but it'll do the trick.


eBusiness(Posted 2004) [#16]
Here is an example with sprites:
texture=LoadTexture("Grass01.png",51)
For a=0 To 1023
	rot1=Rnd(0,360)
	rot2=Rnd(0,360)
	rot3=Rnd(0,40)
	radius=Sqr(Rnd(0,10000))
	For b=0 To 1
		sprite=CreateSprite()
		EntityTexture sprite,texture
		SpriteViewMode sprite,2
		PositionEntity sprite,Sin(rot1)*radius,10*Cos(rot3),Cos(rot1)*radius
		RotateEntity sprite,rot3-2*b*rot3,rot2+b*180,0
		ScaleSprite sprite,10,10
	Next
Next
I doubt that you can cover an entire map in that tight grass without a workaround, let's see, some system for doing this effect was around this forum a short while ago.


SabataRH(Posted 2004) [#17]
http://www.aliencodec.com/products/blitzgrass3d/index.html :)


Affje(Posted 2004) [#18]
@doggie, your editor is very nice :D
@alienhead, it looks very amazing, but how can i use it for my project??


Tiggertooth(Posted 2004) [#19]
Okay, here's some sample code that does what I was talking about. It is very tweakable. Here's the run down:

1) You'll need a grass model. I agree with the above poster, you could just use two sprites. But for me the model is a cleaner solution (since I can potentially do more with it). It is easy enough to change if you want to go with sprites. You also need some texture for the ground. For this demo I went with mossybrick01.jpg, which I believe came from one of Mark's samples (the castle one?).

2) This isn't trying to be super efficient at the low-level. It is trying to be efficient at the high level. The purpose of the sample code was to demo the algorithm, so stay focused on that.

3) You can very easily control the density, distance, etc. of the grass. I should have put in most consts at the top, but I didn't.

4) The point of this code was to draw grass around the moving player, but never having to store more than just what the player can see. Yet always look good, never pop. It is not designed to allow you to custom craft where the grass goes. This is SeriousSam2 style grass.

5) Yes we could animate the grass if you wanted.

6) Yes you could have multiple grass models.

7) It is designed so you only need the GrassSystem type, the grass functions (GrassSystemXXXX) and theGrassSystem global singleton. You just have to edit GrassSystemGetHeightAtPoint() to return the height of your terrain at the specified point. Blitz built in terrain can do this in one function. If you use your own mesh, just calculate it (for testing, don't worry about interpolation). On a side note, for those of you evaluating terrain engines, pay attention to whether or not the engine supplies a means of getting the height at an arbitrary point.

I feel sure I'm forgetting something...
Ken



;---------------------------------------------------
; GrassTest -- simple test app to demonstrate a grass
;              algorithm.  Send questions/bugs to
;              harward@...
;
; Depends on a Grass.b3d file.  Use any old .b3d as a
; placeholder if need be.
;----------------------------------------------------

; Constants
Const KEY_W								= 17
Const KEY_A								= 30
Const KEY_S								= 31
Const KEY_D								= 32
Const KEY_ESC							= 1
Const KEY_ARROW_LEFT					= 205
Const KEY_ARROW_RIGHT					= 203
Const PLAYER_ROTATION_SPEED#			= 1.0
Const PLAYER_MOVEMENT_SPEED#			= 1.0
Const GRASS_MAX_GRASS					= 500
Const GRASS_FADE_NEAR_DISTANCE#			= 200.0
Const GRASS_FADE_FAR_DISTANCE#			= 400.0
Const GRASS_MAX_DISTANCE#				= 500.0
Const GRASS_RANGE_DISTANCE#				= GRASS_MAX_DISTANCE - GRASS_FADE_FAR_DISTANCE


; Grass System data
Type GrassSystem
	Field		grassFilename$
	Field		grassModel
	Field		grassDensity#
	Field		grassScaleFactor#
	Field		grassCount%
	Field		grassPlaced%
	Field		lastPlayerPivot%
	Field		seeded%
	Field		grass%[ GRASS_MAX_GRASS ]
End Type


; Player data
Type Player
	Field		turnLeft%
	Field		turnRight%
	Field		moveForward%
	Field		moveLeft%
	Field		moveBackward%
	Field		moveRight%
	Field		camera
End Type


; Globals
Global theGrassSystem.GrassSystem		= Null
Global thePlayer.Player					= Null

main()
End


; start of program
Function main()
	startUp()
	mainLoop()
	shutDown()
End Function

; Sets the game up
Function startUp()
	Graphics3D( 1024, 768, 32, 0 )
	SetBuffer( BackBuffer() )
	SeedRnd( MilliSecs() )
	
	Local plane = CreatePlane()
	Local texture = LoadTexture( "mossybrick01.jpg" )
	ScaleTexture( texture, 256.0, 256.0 )
	EntityTexture( plane, texture )
	EntityFX( plane, 1 )
	
	GrassSystemConstructor( "grass01.b3d", 0.2 )
	PlayerConstructor()
End Function


; The main loop
Function mainLoop()
	Local playing = True
	While ( playing )		
		playing = getInput()
		update()
		draw()
	Wend
End Function

; shuts down the game
Function shutDown()
	PlayerDestructor()
	GrassSystemDestructor()
;	TerrainSystemDestructor()
End Function

; Updates the world
Function update()
	PlayerUpdate()
	GrassSystemUpdate( EntityX( thePlayer\camera ), EntityY( thePlayer\camera ), EntityZ( thePlayer\camera ) )
	UpdateWorld()
End Function

; Draws the world
Function draw()
	RenderWorld()
	Text( 20, 20, "Grass count: " + theGrassSystem\grassCount )
	Text( 20, 40, "Grass added: " + theGrassSystem\grassPlaced )
	Flip()
End Function

; Moves the player
Function getInput%()
	
	thePlayer\moveForward		= False
	thePlayer\moveBackward		= False
	thePlayer\moveLeft			= False
	thePlayer\moveRight			= False
	thePlayer\turnLeft			= False
	thePlayer\turnRight			= False
	
	If ( KeyDown( KEY_W ) )
		thePlayer\moveForward	= True
	EndIf
	If ( KeyDown( KEY_A ) )
		thePlayer\moveLeft		= True
	EndIf
	If ( KeyDown( KEY_S ) )
		thePlayer\moveBackward	= True
	EndIf
	If ( KeyDown( KEY_D ) )
		thePlayer\moveRight		= True
	EndIf
	If ( KeyDown( KEY_ARROW_LEFT ) )
		thePlayer\turnLeft		= True
	EndIf
	If ( KeyDown( KEY_ARROW_RIGHT ) )
		thePlayer\turnRight		= True
	EndIf
	
	If ( KeyDown( KEY_ESC ) )	Return False
		
	Return True
End Function



;--------------------------------------------------------
;               G R A S S   F U N C T I O N S
;--------------------------------------------------------
; Constructor
Function GrassSystemConstructor.GrassSystem( filename$, scaleFactor#  )
	If ( theGrassSystem <> Null )	RuntimeError( "GrassSystem already instantiated" )
		
	theGrassSystem					= New GrassSystem
	theGrassSystem\grassScaleFactor	= scaleFactor
	theGrassSystem\grassFilename	= filename
	theGrassSystem\grassModel		= LoadMesh( theGrassSystem\grassFilename )
	theGrassSystem\lastPlayerPivot	= CreatePivot()
	theGrassSystem\grassCount		= 0
	theGrassSystem\grassPlaced		= 0
	theGrassSystem\seeded			= False
	
	For grassIndex = 0 To GRASS_MAX_GRASS
		theGrassSystem\grass[ grassIndex ] = 0 
	Next
	
	ScaleEntity( theGrassSystem\grassModel, scaleFactor, scaleFactor, scaleFactor )
	EntityAutoFade( theGrassSystem\grassModel, GRASS_FADE_NEAR_DISTANCE, GRASS_FADE_FAR_DISTANCE )
	HideEntity( theGrassSystem\grassModel )
End Function

; Destructor
Function GrassSystemDestructor()
	If ( theGrassSystem\grassModel )
		FreeEntity( theGrassSystem\grassModel )
		theGrassSystem\grassModel	= 0
	EndIf
	
	Delete theGrassSystem
	theGrassSystem	= Null
End Function


; Places grass if player moved (or we haven't seeded yet )
Function GrassSystemUpdate( playerX#, playerY#, playerZ# )
	Local playerPivot		= CreatePivot()
	PositionEntity( playerPivot, playerX, playerY, playerZ )
	
	If ( theGrassSystem\seeded = False )
		PositionEntity( theGrassSystem\lastPlayerPivot, playerX, playerY, playerZ )
		GrassSystemAddGrass( 0, GRASS_MAX_DISTANCE )
		theGrassSystem\seeded	= True
		Return
	EndIf
	
	; check to see if we need to add more grass
	If ( EntityDistance( playerPivot, theGrassSystem\lastPlayerPivot ) > GRASS_RANGE_DISTANCE )
		
		; destroy out of range grass
		For grassIndex = 0 To GRASS_MAX_GRASS
			Local grass = theGrassSystem\grass[ grassIndex ]
			
			If ( grass <> 0 )
				If ( EntityDistance( grass, playerPivot ) > GRASS_MAX_DISTANCE )
					FreeEntity( grass )
					theGrassSystem\grass[ grassIndex ]	= 0
					theGrassSystem\grassCount			= theGrassSystem\grassCount - 1
				EndIf
			EndIf
		Next
		
		; add new grass
		PositionEntity( theGrassSystem\lastPlayerPivot, playerX, playerY, playerZ )
		GrassSystemAddGrass( GRASS_FADE_FAR_DISTANCE, GRASS_MAX_DISTANCE )
	EndIf
	
	FreeEntity( playerPivot )
End Function


; Places grass in a ring around the player, typically outside viewing range
Function GrassSystemAddGrass( minRadius#, maxRadius# )
	Local playerX#				= EntityX( theGrassSystem\lastPlayerPivot )
	Local playerZ#				= EntityZ( theGrassSystem\lastPlayerPivot )
	Local grassToPlace			= GRASS_MAX_GRASS - theGrassSystem\grassCount	
	
	theGrassSystem\grassPlaced	= 0
	For grassIndex = 0 To grassToPlace-1
		Local radius#			= Rnd( minRadius, maxRadius )
		Local degrees#			= Rnd( 0.0, 359.0 )
		Local x#				= radius * Cos( degrees ) + playerX
		Local z#				= radius * Sin( degrees ) + playerZ
		Local y#				= GrassSystemGetHeightAtPoint( x, z )
		If ( GrassSystemPlaceGrass( x, y, z ) = False )
			Return ; we cannot place anymore grass right now
		EndIf
	Next
End Function


; Add grass entity
Function GrassSystemPlaceGrass%( x#, y#, z# )
	Local grassEntity = CopyEntity( theGrassSystem\grassModel )
	PositionEntity( grassEntity, x, y, z )
	EntityAutoFade( grassEntity, GRASS_FADE_NEAR_DISTANCE, GRASS_FADE_FAR_DISTANCE )

	For grassIndex = 0 To GRASS_MAX_GRASS
		If ( theGrassSystem\grass[ grassIndex ] = 0 )
			theGrassSystem\grass[ grassIndex ]	= grassEntity
			theGrassSystem\grassCount			= theGrassSystem\grassCount + 1
			theGrassSystem\grassPlaced			= theGrassSystem\grassPlaced + 1
			Return True
		EndIf
	Next
	Return False
End Function

; This is the only function that you need to edit.  Just
; have it return the height (y-value) of your terrain at
; the specified point.
Function GrassSystemGetHeightAtPoint#( x#, z# )
	Return 0
End Function


;--------------------------------------------------------
;                P L A Y E R   F U N C T I O N S
;--------------------------------------------------------
; Constructor
Function PlayerConstructor()
	thePlayer				= New Player
	thePlayer\camera		= CreateCamera() 
	CameraFogMode( thePlayer\camera, 1 )
	CameraFogColor( thePlayer\camera, 255.0, 200.0, 180.0 )
	CameraClsColor( thePlayer\camera, 150.0, 200.0, 255.0 )
	PositionEntity( thePlayer\camera, 1, 1, 1 ) 
End Function

; Destructor
Function PlayerDestructor()
	Delete thePlayer
	thePlayer = Null
End Function



; Update
Function PlayerUpdate()

	Local xRotationDelta#		= 0.0
	Local yRotationDelta#		= 0.0
	Local zRotationDelta#		= 0.0
	Local xMovementDelta#		= 0.0
	Local yMovementDetta#		= 0.0
	Local zMovementDelta#		= 0.0
	
	If ( thePlayer\turnLeft )		yRotationDelta	= -PLAYER_ROTATION_SPEED
	If ( thePlayer\turnRight )		yRotationDelta	=  PLAYER_ROTATION_SPEED
	If ( thePlayer\moveLeft	)		xMovementDelta	= -PLAYER_MOVEMENT_SPEED
	If ( thePlayer\moveRight )		xMovementDelta	=  PLAYER_MOVEMENT_SPEED
	If ( thePlayer\moveForward )	zMovementDelta	=  PLAYER_MOVEMENT_SPEED
	If ( thePlayer\moveBackward )	zMovementDelta	= -PLAYER_MOVEMENT_SPEED

	; update based on player input
	MoveEntity( thePlayer\camera, xMovementDelta, yMovementDelta, zMovementDelta )
	TurnEntity( thePlayer\camera, xRotationDelta, yRotationDelta, zRotationDelta )
	
	; adjust for map height
	Local x#					= EntityX( thePlayer\camera )
	Local z#					= EntityZ( thePlayer\camera )
	Local y#					= GrassSystemGetHeightAtPoint( x, z )
	PositionEntity( thePlayer\camera, x, y+20.0, z )
End Function




SabataRH(Posted 2004) [#20]
@doggie, your editor is very nice :D
@alienhead, it looks very amazing, but how can i use it for my project??


There's several ways.. First you could use a custom editor to paint grass on a terrain. Or, the eaiest way, you could use blitzgrass custom colormap function. Just make a copy of your heightmap.bmp and then open it in your paint program.. spray dots on the spots you'd like to see grass..