create a shadow volume

Blitz3D Forums/Blitz3D Programming/create a shadow volume

The forum is locked. These are the original blitzbasic.com forums, kept so they can still be read. No new topics, replies or edits can be made here. To join in, head over to the Blitz3D+ forums.

This is something I made a while ago. It is rather versitile, as you can apply shadow properties with a simple function and it is easy to implement and has support for colored shadows and transparent object shadows. Unfortunately, however, it is slow. It is kinda a modified version of Halo's demo that uses types instead of arrays plus some other goodies to make it more portable (and practical). The other problem is it can take a while to load a mesh with a large number of tris because of the algorithm it uses. It is in desperate need for a rewrite but you can use it for whatever you want.

;;;;;;;;;;;;;;;;;;;;;
;Shadows............;
;Michael Scherer....;
;[Tainted Instinct].;
;;;;;;;;;;;;;;;;;;;;;



Global FPS,LastCheck,Frames
Function GetFPS()
	Frames = Frames + 1
	
	If MilliSecs() > LastCheck+1000 Then
		LastCheck = MilliSecs()
		FPS = Frames
		Frames = 0
	EndIf
	Return FPS
End Function




Graphics3D 800,600

AntiAlias True

;COLLISON TYPES
Const shadowCOL = 1, terrainCOL = 2

;SHADOWMESH/SHADOWVERTEX TYPES
Type shadowmesh
	Field entity,shadow					;the entity handle and its shadow
	Field alpha#						;for meshes that are see-through
End Type

Type shadowvertex
	Field surf,tri,index				;exact location of vertex
	Field entity						;the pivot
	Field parent						;which shadowmesh this vertex belongs to
End Type

;CREATE A SHADOWMESH
Function shadowmesh(mesh,alpha#,r,g,b)
	s.shadowmesh = New shadowmesh
	
	s\entity = mesh
	s\shadow = CopyMesh(mesh)
	EntityFX s\shadow,1
	EntityColor s\shadow,r,g,b
	EntityAlpha s\shadow,.5				;changes based on distance from light
	s\alpha# = alpha#
	
	;get all vertex positions
	For sf = 1 To CountSurfaces(s\entity)
		surf = GetSurface(s\entity,sf)
		For t = 0 To CountTriangles(surf)-1
		For i = 0 To 2
			;vert = TriangleVertex(surf,t,i)
			sv.shadowvertex = New shadowvertex
			sv\surf = sf : sv\tri = t : sv\index = TriangleVertex(surf,t,i)
			
			;sv\x# = VertexX#(surf,i) : sv\y# = VertexY#(surf,i) : sv\z# = VertexZ#(surf,i)
			sv\entity = CreatePivot()
			PositionEntity sv\entity, VertexX#(surf,sv\index), VertexY#(surf,sv\index), VertexZ#(surf,sv\index)
			
			EntityType sv\entity,shadowCOL
			;EntityRadius sv\entity,.001
			
			sv\parent = Handle(s)
		Next
		Next
	Next
	
	;eliminate redundant vertices
	For s1.shadowvertex = Each shadowvertex
		For s2.shadowvertex = Each shadowvertex
			If Handle(s1) <> Handle(s2)
				If s1\parent = s2\parent And s1\surf = s2\surf And s1\index = s2\index
					FreeEntity s2\entity
					Delete s2
				EndIf
			EndIf
		Next
	Next
	
	Return Handle(s)		;return the shadowmesh handle just in case
End Function

;THE LIGHT TYPE
Type shadowlight
	Field entity
	Field x#,y#,z#
	Field range#
End Type

;BUILD A LIGHT
Function shadowlight(x#,y#,z#,range#)
	l.shadowlight = New shadowlight
	
	l\entity = CreateLight(2)
	LightRange l\entity,range#
	PositionEntity l\entity,x#,y#,z#
	
	l\x# = x# : l\y# = y# : l\z# = z#
	l\range# = range#
	
	Return Handle(l)		;return the lights handle just in case
End Function

;a shadow mesh
cube = CreateSphere()
;ScaleMesh cube,.2,.2,.2
EntityColor cube,255,0,0
EntityAlpha cube,.5
shadowmesh(cube,.5,255,0,0)						;set it as a shadow mesh
PositionEntity cube,0,0,0

cube2 = CreateCube()
PositionEntity cube2,3,0,-3
shadowmesh(cube2,1,0,0,0)

cube3 = CreateSphere(3)
PositionEntity cube3,-3,0,-3
shadowmesh(cube3,1,0,0,0)

;a light
light = shadowlight(2,5,2,10)

lightspright = CreateSprite()	;he he lightspright, get it? just a placemarker
PositionEntity lightspright,2,5,2
tex=CreateTexture(128,128)
SetBuffer TextureBuffer(tex)
Rect 0,0,28,128
Rect 0,100,128,28
SetBuffer BackBuffer()
EntityTexture lightspright,tex
FreeTexture tex

;plane to put the shadows on
plane = CreatePlane(16)
EntityType plane,terrainCOL
PositionEntity plane,0,-5,0
tex=CreateTexture(128,128)
SetBuffer TextureBuffer(tex)
Color 0,128,0
Rect 0,0,128,128

SetBuffer BackBuffer()
EntityTexture plane,tex
FreeTexture tex


;camera to see the shadows with
Global cam = CreateCamera()
RotateEntity cam,15,0,0
PositionEntity cam,0,5,-20

;LET THE VERTICES COLLIDE WITH TERRAIN
Collisions shadowCOL,terrainCOL,2,1

SetBuffer BackBuffer()

CameraClsColor cam,0,255,255

Color 255,255,255
While Not KeyDown(1)
	PositionShadows()
	
	UpdateWorld
	RenderWorld
	
	UpdateShadows()
	
	l.shadowlight = Object.shadowlight(light)
	
	If KeyDown(205) Then MoveEntity l\entity, .1,0,0
	If KeyDown(203) Then MoveEntity l\entity,-.1,0,0
	
	If KeyDown(208) Then MoveEntity l\entity,0,0,-.1
	If KeyDown(200) Then MoveEntity l\entity,0,0, .1
	
	PositionEntity lightspright, EntityX#(l\entity),EntityY#(l\entity),EntityZ#(l\entity)
	
	TurnEntity cube2,.1,.2,.3
	TurnEntity cube3,.2,.3,.4
	
	;debugging------------------
	If CommandLine$() = "/debug"
	For l.shadowlight = Each shadowlight
		CameraProject cam,l\x#,l\y#,l\z#
		x# = ProjectedX() : y# = ProjectedY()
		WritePixel ProjectedX(),ProjectedY(),$ffff00
		
		For sv.shadowvertex = Each shadowvertex
			CameraProject cam,EntityX#(sv\entity, True),EntityY#(sv\entity, True),EntityZ#(sv\entity, True)
			Line x#,y#,ProjectedX(),ProjectedY()
		Next
	Next
	EndIf
	
	Text 0,0,GetFPS()
	
	;Screen Shot
	If KeyHit(88)
		SaveBuffer(BackBuffer(),"screen"+s+".bmp")
		s = s + 1
	EndIf
	
	Flip
Wend

;POSITION ALL SHADOW'S VERTICES
Function PositionShadows()
	;hide the shadows
	For s.shadowmesh = Each shadowmesh
		HideEntity s\shadow
	Next
	
	piv = CreatePivot()
	
	For l.shadowlight = Each shadowlight
		l\x# = EntityX#(l\entity, True) : l\y# = EntityY#(l\entity, True) : l\z# = EntityZ#(l\entity, True)
		PositionEntity piv,l\x#,l\y#,l\z#
		For s.shadowmesh = Each shadowmesh
			entdist# = EntityDistance#(s\entity,piv)
			If entdist# <= l\range#
				;since we are in the light range, show the shadow
				ShowEntity s\shadow
				
				;apply appropriate alpha level
				EntityAlpha s\shadow, (1-(entdist#/l\range#))*s\alpha#
				
				;now we position its vertices
				For sv.shadowvertex = Each shadowvertex
					If sv\parent = Handle(s)						;Make sure the vertex belongs to this mesh
						PositionEntity sv\entity,l\x#,l\y#,l\z#		;put the vertex at the light
						
						surf = GetSurface(s\entity,sv\surf)
						x# = VertexX#(surf,sv\index) : y# = VertexY#(surf,sv\index) : z# = VertexZ#(surf,sv\index)
						TFormPoint( x#, y#, z#, s\entity, 0 )
						x# = TFormedX#() : y# = TFormedY#() : z# = TFormedZ#()
						
						PositionEntity piv,x#,y#,z#
						PointEntity sv\entity,piv
						
						ResetEntity sv\entity
						
						MoveEntity sv\entity,0,0,99999
					EndIf
				Next
			EndIf
		Next
		If CommandLine$() = "/debug"
		CameraProject cam,l\x#,l\y#,l\z#
		WritePixel ProjectedX(),ProjectedY(),$ffff00,FrontBuffer()
		EndIf
	Next
	
	FreeEntity piv
End Function

;UPDATE ALL SHADOWS
Function UpdateShadows()
	For s.shadowmesh = Each shadowmesh
		For sv.shadowvertex = Each shadowvertex
			If sv\parent = Handle(s)
				surf = GetSurface(s\shadow,sv\surf)
				
				VertexCoords( surf,sv\index, EntityX#(sv\entity, True), EntityY#(sv\entity, True), EntityZ#(sv\entity, True) )
				
				If CommandLine$() = "/debug"
				CameraProject cam,EntityX#(sv\entity),EntityY#(sv\entity),EntityZ#(sv\entity)
				WritePixel ProjectedX(),ProjectedY(),$ff0000,BackBuffer()
				
				verts = verts+1
				EndIf
			EndIf
		Next
		
		UpdateNormals s\shadow
	Next
	If CommandLine$() = "/debug"
		Text 0,0,verts
	EndIf
End Function