blitznerd: Here you go:
;Terrain Shadow Library
;Copyright (C) 2006 John Judnich
;NOTE: Any shadow may not span more than SHADOW_TILES terrain tiles
;TODO:
;+ Ignore update of shadows behind camera
;+ Add frame-skip optimization with interpolated fill-in
; Add automatic shadow contour rendering
Const SHADOW_TILES = 256
Const Y_OFFSET# = .7
Const EDGE_Y_OFFSET# = .3
Global ShadowTerrain
Global ShadowTerrainScale#
Global ShadowPivot
Global ShadowFrameSkip = 1
Global FrameCount = 0
Type TerrainShadow
Field Entity
Field LastX#, LastY#, LastZ#
Field LastPitch#, LastYaw#, LastRoll#
Field Size#
Field ShadowTexture
Field ShadowMesh
Field ShadowSurface
Field ViewDist#
Field Visible
Field Hidden
Field Alpha#
Field Scale#
End Type
;This initializes the shadow system, making it ready for use. Basically, this just
;sets a variable to a terrain so the shadow system knows what to cast the shadows on.
;Optionally, you can set FrameSkip to adjust how many frames must elapse before the
;shadow mesh is regenerated to fit the terrain's shape. 0 will keep the shadow up to
;date at all times, while higher values may cause some visual "glitches" (mostly on
;mountainous terrains where the shape under the shadow caster changes constantly),
;although the shadow system will run much faster. The default value for FrameSkip is
;1, which works well for almost any terrains.
Function ShadowsInit(Terrain, TerrainScale#, FrameSkip = 1)
ShadowTerrain = Terrain
ShadowTerrainScale# = TerrainScale
ShadowFrameSkip = FrameSkip
FrameCount = FrameSkip
If ShadowPivot = 0 Then ShadowPivot = CreatePivot()
End Function
;Use this function to turn on shadows for a specified entity. After calling this function,
;you should see it's shadow on your terrain (as long as you be sure to call UpdateShadows()
;before RenderWorld. "ShadowTexure" provides the option to specify your own shadow, instead
;of letting the shadow system automatically render it. Specifying your own shadow can improve
;loading time with many many entities. Note: When specifying a custom shadow image, you only
;need to do it once - after you do, you can omit the ShadowTexture parameter.
Function ShowShadow(Entity, ShadowTexture = 0)
objnum = Int(EntityName$(Entity))
obj.TerrainShadow = Object.TerrainShadow(objnum)
If obj = Null Or ShadowTexture <> 0 Then ;If there is no shadow for Entity, or it's texture is being changed, then make a new one
;If the shadow texture is being changed, delete the shadow and start over
If obj <> Null Then DeleteShadow(Entity)
;Create a new shadow
obj.TerrainShadow = New TerrainShadow
NameEntity Entity, Handle(obj)
obj\Entity = Entity
obj\Size = MeshWidth(Entity)
depth# = MeshDepth(Entity)
If depth > obj\Size Then obj\Size = depth
obj\ShadowMesh = CreateMesh()
EntityFX obj\ShadowMesh, 1+32
EntityBlend obj\ShadowMesh, 1
obj\ShadowSurface = CreateSurface(obj\ShadowMesh)
HideEntity obj\ShadowMesh
obj\Alpha = .7
obj\ViewDist = 0
obj\Scale = 1
obj\Hidden = False
obj\Visible = False
;Render the shadow if a texture is not specified, otherwise use the given texture
If ShadowTexture = 0 Then
;TODO: Render soft shadow to texture
Else
obj\ShadowTexture = ShadowTexture
End If
;Apply texture
EntityTexture obj\ShadowMesh, obj\ShadowTexture
Else
If obj\Hidden = True Then
;Un-hide the shadow if it already exists
ShowEntity obj\ShadowMesh
obj\Hidden = False
End If
End If
End Function
;This can be used to temporarily turn off a shadow for an object. If you do not plan to show the
;shadow again, DeleteShadow() can be used instead, which deletes it from memory.
Function HideShadow(Entity)
objnum = Int(EntityName$(Entity))
obj.TerrainShadow = Object.TerrainShadow(objnum)
If obj <> Null Then
If obj\Hidden = False Then
;Hide the shadow if it exists
HideEntity obj\ShadowMesh
obj\Hidden = True
End If
End If
End Function
;This turns off an object's shadow, deleting the shadow from memory. You must also be sure to call
;this function before deleting a shadowed entity, because the shadow system may crash, trying to
;access a deleted object when rendering the shadow.
Function DeleteShadow(Entity)
objnum = Int(EntityName$(Entity))
obj.TerrainShadow = Object.TerrainShadow(objnum)
If obj <> Null Then
;Delete the shadow if it exists
FreeEntity obj\ShadowMesh
Delete obj
Else
DebugLog "Warning: Tried to delete a non-existant shadow"
End If
End Function
;This sets the view distance for a shadow. This can be used to increase performance when you have a
;very large amount of objects casting shadows. To disable the view distance limit, set the view
;distance value to 0. Note: When an object reaches the end of it's view distance, the shadow is not
;instantly hidden, the shadow's visibility varies with the object's distance from the camera. Default: 0
Function ShadowViewDistance(Entity, ViewDistance#)
objnum = Int(EntityName$(Entity))
obj.TerrainShadow = Object.TerrainShadow(objnum)
obj\ViewDist = ViewDistance
End Function
;This sets the alpha (transparency) value of a shadow. Values near 0 produce shadows that are barely visible,
;while values near 1 produce shadows almost completely opaque. Default: 0.6
Function ShadowAlpha(Entity, Alpha#)
objnum = Int(EntityName$(Entity))
obj.TerrainShadow = Object.TerrainShadow(objnum)
obj\Alpha = Alpha
End Function
;You can use this function to adjust the size of an object's shadow. Default: 1.0
Function ShadowScale(Entity, Scale#)
objnum = Int(EntityName$(Entity))
obj.TerrainShadow = Object.TerrainShadow(objnum)
obj\Scale = Scale
End Function
;This is a completely optional function which you can use to pre-load shadows for an image, without
;actually showing them yet. You can use this during your loading screen if you are displaying shadows
;for a very large number of objects, and you are using the shadow system's built-in shadow render
;(which can be slow when preparing many objects' shadows). In most cases, however, this function is
;completely unnecessary and you don't need to use it, since the shadow system will automatically prepare
;an object's shadow the first time you call ShowShadow() for an entity.
Function PrepareShadow(Entity, ShadowTexture)
objnum = Int(EntityName$(Entity))
obj.TerrainShadow = Object.TerrainShadow(objnum)
If obj = Null Then
ShowShadow(Entity, ShadowTexture)
HideShadow(Entity)
End If
End Function
;This function updates all object's shadows so they appear correctly on your terrain. It is very important that
;you call this function before RenderWorld each loop or shadows may appear incorrectly. "Cam" must be set to
;your game's main camera for this to operate properly.
Function UpdateShadows(cam)
FrameCount = FrameCount + 1
If FrameCount > ShadowFrameSkip Then FrameCount = 0
For shadow.TerrainShadow = Each TerrainShadow
;Only consider shadows the user did not hide
If shadow\Hidden = False Then
x# = EntityX(shadow\Entity, True)
y# = EntityY(shadow\Entity, True)
z# = EntityZ(shadow\Entity, True)
pitch# = EntityPitch(shadow\Entity, True)
yaw# = EntityYaw(shadow\Entity, True)
roll# = EntityRoll(shadow\Entity, True)
;Calculate shadow alpha
If shadow\ViewDist <> 0 Then
xd# = EntityX(cam, True) - x
yd# = EntityY(cam, True) - y
zd# = EntityZ(cam, True) - z
dist# = Sqr(xd*xd + yd*yd + zd*zd)
alpha# = (shadow\ViewDist - dist) / shadow\ViewDist
Else
alpha# = 1
End If
alpha = alpha * shadow\Alpha
If alpha < 0 Then alpha = 0
;Calculate the visibility status of the shadow
If alpha <> 0 Then
;Check if the shadow is behind the camera or not
TerrY# = TerrainY(ShadowTerrain, x, EntityY(ShadowTerrain), z)
If PointVisible(cam, x, TerrY, z) Then
Visible = True
Else
Visible = False
End If
Else
Visible = False
End If
;If the shadow is visible, update it
If Visible Then
;Only update the shadow mesh if it's entity has moved/rotated
If shadow\Visible <> Visible Or x <> shadow\LastX Or y <> shadow\LastY Or z <> shadow\LastZ Or pitch <> shadow\LastPitch Or yaw <> shadow\LastYaw Or roll <> shadow\LastRoll Then
If FrameCount = 0 Then
;Regenerate the shadow mesh
CreateTerrainDecal(shadow\ShadowMesh, shadow\ShadowSurface, x-shadow\Size*.5*shadow\Scale*1.4142, z-shadow\Size*.5*shadow\Scale*1.4142, x+shadow\Size*.5*shadow\Scale*1.4142, z+shadow\Size*.5*shadow\Scale*1.4142, pitch#, yaw#, roll#)
Else
;"Fake" mesh regeneration
TranslateEntity shadow\ShadowMesh, x - shadow\LastX, y - shadow\LastY, z - shadow\LastZ
TurnEntity shadow\ShadowMesh, 0, yaw - shadow\LastYaw, 0
End If
End If
;Update the alpha
EntityAlpha shadow\ShadowMesh, alpha
End If
;Hide/show the entity as necessary
If shadow\Visible <> Visible Then
If Visible Then
ShowEntity shadow\ShadowMesh
Else
HideEntity shadow\ShadowMesh
End If
shadow\Visible = Visible
End If
shadow\LastX = x
shadow\LastY = y
shadow\LastZ = z
shadow\LastPitch = pitch
shadow\LastYaw = yaw
shadow\LastRoll = roll
End If
Next
End Function
;****************** Internal Functions *******************
Dim VertGrid(SHADOW_TILES, SHADOW_TILES)
Function CreateTerrainDecal(mesh, surface, x1#, z1#, x2#, z2#, pitch#, yaw#, roll#)
ClearSurface surface
midx# = (x2 + x1) * .5
midz# = (z2 + z1) * .5
PositionEntity mesh, midx, 0, midz
RotateEntity mesh, 0, 0, 0
x1grid = Floor(x1 / ShadowTerrainScale)
z1grid = Floor(z1 / ShadowTerrainScale)
x2grid = Ceil(x2 / ShadowTerrainScale)
z2grid = Ceil(z2 / ShadowTerrainScale)
For xg = x1grid To x2grid
For zg = z1grid To z2grid
edge = False
vertx# = xg * ShadowTerrainScale
vertz# = zg * ShadowTerrainScale
If xg = x1grid Then vertx = x1: edge = True
If xg = x2grid Then vertx = x2: edge = True
If zg = z1grid Then vertz = z1: edge = True
If zg = z2grid Then vertz = z2: edge = True
verty# = TerrainY(ShadowTerrain, vertx + EntityX(ShadowTerrain), EntityY(ShadowTerrain), vertz + EntityZ(ShadowTerrain))
vertx# = vertx# + EntityX(ShadowTerrain)
vertz# = vertz# + EntityZ(ShadowTerrain)
If edge = False Then verty# = verty# + Y_OFFSET Else verty# = verty# + EDGE_Y_OFFSET#
u# = (vertx-x1) / (x2-x1)
v# = (vertz-z1) / (z2-z1)
RotateEntity ShadowPivot, 0, -(yaw+180), 0
ScaleEntity ShadowPivot, 1.4142, 1.4142, 1.4142
TFormVector u - .5, 0, v - .5, ShadowPivot, 0
u# = TFormedX() + .5
v# = TFormedZ() + .5
VertGrid(xg-x1grid, zg-z1grid) = AddVertex(surface, vertx - midx, verty, vertz - midz, u, v)
Next
Next
For xg = 1 To (x2grid-x1grid)
For zg = 1 To (z2grid-z1grid)
AddTriangle surface, VertGrid(xg, zg), VertGrid(xg, zg-1), VertGrid(xg-1, zg-1)
AddTriangle surface, VertGrid(xg-1, zg), VertGrid(xg, zg), VertGrid(xg-1, zg-1)
Next
Next
End Function
Function PointVisible(Viewer, x#, y#, z#)
TFormPoint x, y, z, 0, Viewer
If TFormedZ() <= 0 Then Return False Else Return True
End Function
When calling ShowShadow(), be sure to specify a shadow texture (which is applied to the terrain), because the current system can't render it for you yet.
CreateTerrainDecal(mesh, surface, x1#, z1#, x2#, z2#, pitch#, yaw#, roll#) is also useful for applying other decals to the terrain, like the shell craters.
Grisu: Thanks for the suggestions :) I hope to add some of those in the future. For now, you can download some nice mods others have made for BattleTanks II (add-on tanks and maps) here:
[a http://www.bundysoft.com/dokuwiki/doku.php?id=projects:johnj#battletanks_ii]BattleTanks II Expansion Packs[/a](scroll down to "Expansion Packs & Add-Ons")
If you want to create your own maps for BattleTanks II, read this:
[a http://www.bundysoft.com/dokuwiki/doku.php?id=projects:johnj:expanding_battletanks_ii]BattleTanks II Map-Making[/a]