Shadows

Blitz3D Forums/Blitz3D Programming/Shadows

Ash(Posted 2002) [#1]
How do I do them in Blitz... I'm hoping to have both Lo-Detail and Hi-Detail shadows in there.

Lo-D will be the "circle on the ground". Hi-D will be proper shadows. How would I go about doing both of these?

The Lo-D I understand would just follow the model, but how would I create the shadow? The Hi-D version will have to change as the anim frames change. I don't want it all to be hard-coded (that would be poor coding technique), but I want them to dynamically change.

So how would I go about creating both the aforementioned Lo-D and Hi-D shadows?


Rob (Posted 2002) [#2]
Here's my shadow code. It is a "towel" that means it can handle any sort of level and floor surface.

If you want Hi-D you will need to render a small view of the character onto a texture with a really dark entitycolor, or blend mode, then texture the result each frame (or every second frame) onto the towel.

So in this manner you've both shadow techniques. A lot of code needs to change to make it effective.


Ash(Posted 2002) [#3]
Excuse me Rob, but I don't quite get this. I'm a bit of a nwebie to programming and Blitz and everything else, and don't really know what is going on in your example.

Could you maybe comment it a bit better, like tell why you've done something, and what it does?


Rob (Posted 2002) [#4]
Sure... here's what it does.

1. Make a towel. This is a quad mesh. That is, a mesh that has rows and columns. David Bird's code was used for this in the CreateQuad function. It's given these rows and columns so that it may deform effectively.

2. Imagine a towel? you can throw it over anything and it should lie on the surface flat pretty well. This is how the shadow routines work.

We position the flat mesh high above where it is due to land, then throw it onto the floor by using a linepick at each vertex. Then we use PickedY() to get the height of the floor where that vert will go.

3. And thats it. Basically, the idea is to deform a detailed square blanket, or towel so it fits on any floor. And repeat this procedure every loop.

There are other ways of doing it I suppose...


Litobyte(Posted 2002) [#5]
huh ! arf! I'm gonna try it gain...

I've tried about 3 kind of these things, and no one worked....even FLEX yeah, I'v tried it with no success in DDK :(

It was slowing it at about 2 FPS...

Is maybe that with big levels, I have to sort, and use only the piece of level (child) on which the character is walking ?

Then I tried something like what you are explaining here, with an helicopter on a terrain, and it worked pretty well (some clipping problem with terrain dynamic LOD) though.


I really need one who works on .3ds based levels.


Ash(Posted 2002) [#6]
That's what I need.

All my levels are BSP-style indoor levels in 3DS files.


Rob (Posted 2002) [#7]
My flex code works on 3ds levels. You guys just need to learn how to code :) after all it's just a small mesh that deforms. Tiny code.

Best bet will be if Mark ever codes shadow volumes... :)


DougUK(Posted 2002) [#8]
"My flex code works on 3ds levels. You guys just need to learn how to code :)"

man thats a little harsh lol


Rob (Posted 2002) [#9]
lol... I know :) People can call me names if it makes them feel better!


DougUK(Posted 2002) [#10]
hehe, flura'sdj'jffjjfja, that feels better :)


Ash(Posted 2002) [#11]
I don't care. Harsh comments or not, I'm here to learn. And I'm gonna put shadows in my game even if it drives me to the brink of death!

After all, you stop learning only when you're dead...


DougUK(Posted 2002) [#12]
I wish i could help BlackHawk but i dont know either, ignore Rob, he dosn't know either, he's just like us hoping someone will have the answer :)


Litobyte(Posted 2002) [#13]
>>I don't care. Harsh comments or not, I'm here to learn. And I'm gonna put shadows in my game even if it drives me to the brink of death!

After all, you stop learning only when you're dead...<<


THat's THe SpiriT!

>>I wish i could help BlackHawk but i dont know either, ignore Rob, he dosn't know either, he's just like us hoping someone will have the answer :)<<


LoL !

I guess is that one I gave bymself, because if I ask the pretty mesh to deform on the basis of my level.3ds it goes mad, and run at 2 fps.

Fine in a simple room.

I'm now trying the NORC shadows routine, via lightmapping, but the part where I walk on, get all black, so wrong UV for the second layer I guess...anybody ?


Tracer(Posted 2002) [#14]
Rob,

FLEX is pretty cool, but the main problem is that the shadow 'floats' .. it's not ON the mesh, it's ABOVE it.

Also, on large levels, the entitypicking slows things down a LOT.

We will not be able to do good shadowing until we get access to stencil buffers.

Tracer


Ash(Posted 2002) [#15]
I'm now trying the NORC shadows routine, via lightmapping, but the part where I walk on, get all black, so wrong UV for the second layer I guess...anybody ?


I reckon I'm gonna work on Hi-D shadows first, since that's the one I'm presuming will be hardest and most time consuming.

I downloaded Norc's demo too (the one with the speed hack by Peter Scheutz), from the Code Archives. The only problem with that is that it's not at all newbie-friendly! The code is not commented at all well for newbies like myself. I don't know what does what, so I don't know how to adapt it to my own program!! Aargh.

As a general point (to anyone, but especially to people who want to make examples for others to follow), I'd like the code examples I get to be commented with why and how you did something, so that it is easier for newbies to follow, as well as easy to adapt, as you will then know what bits are useful to you and what they do.


Neochrome(Posted 2002) [#16]
I did it!!!

looks funny, but i still did it!!


Ash(Posted 2002) [#17]
neo, the tags are "[img]" and "[/img]". No "[url]" is needed. The tag should be "[img=<image url goes here>]". The Image URL should *not* be in quotes, either.


Rob (Posted 2002) [#18]


So thats what he's up to...


Neochrome(Posted 2002) [#19]
[img=http://www.neomancer.co.uk/images/boogerman.jpg[/img]
this works?


sswift(Posted 2002) [#20]
This code will project shadows straight down.

It was designed for circular shadows. However, since you can use any texture you want you can generate a new texture each frame. So write a little code that makes an appropriate shadow texture from the specified mesh and voila, nice shadows which actually represent the shapes of stuff.

The shadows will wrap around the speficied reciver mesh. The caster is the object which determines the location of the shadow in space.

The shadows will taper off in brightness as the angle of the surface they are relative to gets steeper, so shadows will never be seen on perfectly vertical walls.

It shouldn't be too hard to change the code to allow the shadows to be projected in any direction. What would be required is finding out where the vertcies of the model are in space after being rotated in such a way that their location relative to the shadow is below it. Well that's not a great explanation... basically you need to rotate the world so that the line of sight of the "light" casting the shadow points downward. That way you can use the cheap equations to find what polygons are inside the shadow volume.

Oh, and right now the code is set up so that the object shadows ar ebeing cast onto can't be rotated in any way, and it must be at 0,0,0 so the locations of the vertices within it are correct relative to the shadow. But fixing that is also trivial. At least the movement bit. The rotation is a little more costly to fix. Basicically this is good for casting shadows onto levels.

Oh and if you want to use this in a commercial game, send $10 via paypal, to sswift@.... I need the money. You can use it in shareware for free.

; -------------------------------------------------------------------------------------------------------------------
; Swift Shadow System 1.0 - Copyright 2002 Shawn C. Swift
; -------------------------------------------------------------------------------------------------------------------

	
Type Shadow_Caster
	Field Caster
	Field Receiver
	Field Texture
	Field Radius#
End Type
	
		
Type Shadow
	Field Mesh
End Type	


; -------------------------------------------------------------------------------------------------------------------
; This function sets up a new shadow caster/receiver pair.
;
; Caster   = Mesh to cast shadows.
; Receiver = Mesh to receive shadows.
; Texture  = Texture to use for shadow.  (Must have UV clamping enabled for proper operation!)
; Radius#  = Radius of the shadow.
; -------------------------------------------------------------------------------------------------------------------
Function Cast_Shadow(Caster, Receiver, Texture, Radius#)

	ThisCaster.Shadow_Caster = New Shadow_Caster	
	
	ThisCaster\Caster   = Caster
	ThisCaster\Receiver = Receiver
	ThisCaster\Texture  = Texture
	ThisCaster\Radius#  = Radius#

End Function


; -------------------------------------------------------------------------------------------------------------------
; This function deletes all old shadows and creates a shadow mesh for each shadow casting object.
;
; Note:
; Shadows only need to be repositioned and recreated each time the scene is rendered, 
; not every time the physics are updated.
; -------------------------------------------------------------------------------------------------------------------
Function Update_Shadows()

	Delete_Shadows()

	For ThisCaster.Shadow_Caster = Each Shadow_Caster
		 
		Shadow_Center_X# = EntityX#(ThisCaster\Caster, True)
		Shadow_Center_Y# = EntityY#(ThisCaster\Caster, True)
		Shadow_Center_Z# = EntityZ#(ThisCaster\Caster, True)
		Shadow_Radius#   = ThisCaster\Radius#		
		
		ThisShadow.Shadow = New Shadow
		ThisShadow\Mesh = Create_Shadow(ThisCaster\Receiver, Shadow_Center_X#-Radius#, Shadow_Center_Z#+Radius#, Shadow_Center_X#+Radius#, Shadow_Center_Z#-Radius#, Shadow_Center_Y#)
	
		EntityFX ThisShadow\Mesh, 1+2+8
		EntityBlend ThisShadow\Mesh, 2
		EntityTexture ThisShadow\Mesh, ThisCaster\Texture
		
	Next	
		
End Function


; -------------------------------------------------------------------------------------------------------------------
; This function deletes all active shadows.
; -------------------------------------------------------------------------------------------------------------------
Function Delete_Shadows()

	For ThisShadow.Shadow = Each Shadow
		FreeEntity ThisShadow\Mesh
	Next	

End Function


; -------------------------------------------------------------------------------------------------------------------
; This function deletes all shadow casters/recievers which reference the specified entity.
;
; When you delete an entity which is casting or receiving a shadow, you must call this function so that the game
; does not crash from trying to reference an entity which does not exist.
; -------------------------------------------------------------------------------------------------------------------
Function Delete_Shadow_Caster(Entity)

	For ThisCaster.Shadow_Caster = Each Shadow_Caster

		If (ThisCaster\Caster = Entity) Or (ThisCaster\Receiver = Entity) 
			Delete ThisCaster
		EndIf	

	Next	

End Function


; -------------------------------------------------------------------------------------------------------------------
; This function creates a shadow mesh and returns it's handle.
;
; Reciever = The entity to recieve the shadow.
; -------------------------------------------------------------------------------------------------------------------
Function Create_Shadow(Receiver, Shadow_X1#, Shadow_Z1#, Shadow_X2#, Shadow_Z2#, Shadow_Y#)

	; Get the cordinates of the lower left hand corner of the shadow.
	Shadow_Corner_X# = Shadow_X1#
	Shadow_Corner_Z# = Shadow_Z2# 

	Shadow_Scale# = Sqr(Shadow_X1#^2 - Shadow_X2#^2)

	ShadowTris = 0

	; Create the mesh for the shadow, and give it a surface to add polygons to.
	MESH_Shadow = CreateMesh()
	SURFACE_Shadow = CreateSurface(MESH_Shadow)

	; Loop through all triangles in all surfaces of the reciever.
	Surfaces = CountSurfaces(Receiver)
	For LOOP_Surface = 1 To Surfaces

		Surface_Handle = GetSurface(Receiver, LOOP_Surface)

		Tris = CountTriangles(Surface_Handle)
		For LOOP_Tris = 0 To Tris-1

			Vertex_0 = TriangleVertex(Surface_Handle, LOOP_Tris, 0)
			Vertex_1 = TriangleVertex(Surface_Handle, LOOP_Tris, 1)
			Vertex_2 = TriangleVertex(Surface_Handle, LOOP_Tris, 2)
									
			; Check to see if the triangle is inside the shadow's bounding rectangle.
			;
			; This test works by seeing if all of a triangle's points are on a specific side of each side of the
			; rectangle.  The test is not 100% accurate... a very few triangles will pass the test but actually be
			; outside the region.  But we are concerned only with making sure we find all the triangles which ARE
			; in the region and cull the vast majority outside the region, so a little sloppiness in the test is
			; okay if that means it's really fast.
			Shadow = True

			Z0# = VertexZ#(Surface_Handle, Vertex_0)
			Z1# = VertexZ#(Surface_Handle, Vertex_1)
			Z2# = VertexZ#(Surface_Handle, Vertex_2)

			; Is polygon to north of shadow's bounding rectangle?
			If (Z0# > Shadow_Z1#) And (Z1# > Shadow_Z1#) And (Z2# > Shadow_Z1#) 
				Shadow = False 
			Else	

				; Is polygon to south of shadow's bounding rectangle?
				If (Z0# < Shadow_Z2#) And (Z1# < Shadow_Z2#) And (Z2# < Shadow_Z2#) 
					Shadow = False	
				Else
					
					X0# = VertexX#(Surface_Handle, Vertex_0)
					X1# = VertexX#(Surface_Handle, Vertex_1)
					X2# = VertexX#(Surface_Handle, Vertex_2)

					; Is polygon to west of shadow's bounding rectangle?
					If (X0# < Shadow_X1#) And (X1# < Shadow_X1#) And (X2# < Shadow_X1#) 
						Shadow = False 
					Else

						; Is polygon to east of shadow's bounding rectangle?
						If (X0# > Shadow_X2#) And (X1# > Shadow_X2#) And (X2# > Shadow_X2#) 
							Shadow = False 
						Else

							Y0# = VertexY#(Surface_Handle, Vertex_0)
							Y1# = VertexY#(Surface_Handle, Vertex_1)
							Y2# = VertexY#(Surface_Handle, Vertex_2)

							; Is shadow below the polygon?
							If (Shadow_Y# < Y0#) And (Shadow_Y# < Y1#) And (Shadow_Y# < Y2#)
								Shadow = False
							Else
								
								NY0# = VertexNY#(Surface_Handle, Vertex_0) 
								NY1# = VertexNY#(Surface_Handle, Vertex_1) 
								NY2# = VertexNY#(Surface_Handle, Vertex_2)

								FaceNY# = NY0# + NY1# + NY2#								

								; Is polygon facing downwards?
								If (FaceNY# < 0) Then
									Shadow = False
								Else
								
									If (FaceNY# = 0)
										Shadow = False
									EndIf	
									
								EndIf
							EndIf
						EndIf
					EndIf
				EndIf
			EndIf	
			
			; If the triangle is inside the bounding box then add it to the mesh.
			; Note that shadow texture will be flipped top to bottom because of how UV coordinates are assigned to the vertices from the world coordinates.
			If Shadow = True

				Shadow_Vertex_0 = AddVertex(SURFACE_Shadow, X0#, Y0#, Z0#, (X0#-Shadow_Corner_X#)/Shadow_Scale#, (Z0#-Shadow_Corner_Z#)/Shadow_Scale#)
				Shadow_Vertex_1 = AddVertex(SURFACE_Shadow, X1#, Y1#, Z1#, (X1#-Shadow_Corner_X#)/Shadow_Scale#, (Z1#-Shadow_Corner_Z#)/Shadow_Scale#)
				Shadow_Vertex_2 = AddVertex(SURFACE_Shadow, X2#, Y2#, Z2#, (X2#-Shadow_Corner_X#)/Shadow_Scale#, (Z2#-Shadow_Corner_Z#)/Shadow_Scale#)

				; Adjust shadow brightness at each vertex according to vertex normal.				
					VC = (1.0-NY0#)^3*255.0
					If (VC > 0) Then VertexColor SURFACE_Shadow, Shadow_Vertex_0, VC, VC, VC
	
					VC = (1.0-NY1#)^3*255.0
					If (VC > 0) Then VertexColor SURFACE_Shadow, Shadow_Vertex_1, VC, VC, VC
				
					VC = (1.0-NY2#)^3*255.0
					If (VC > 0) Then VertexColor SURFACE_Shadow, Shadow_Vertex_2, VC, VC, VC

				; Add the triangle to the shadow mesh.
					AddTriangle(SURFACE_Shadow, Shadow_Vertex_0, Shadow_Vertex_1, Shadow_Vertex_2)

				ShadowTris = ShadowTris + 1

			EndIf
			
		Next			
	
	Next

	Return MESH_Shadow

End Function



jfk EO-11110(Posted 2002) [#21]
personally i like the direct way, the fast way. My Renderworld-Shadow-projector optimized by Peter Scheutz is pretty fast and an ideal choice for tekken-like stuff. For large Terrains and Buildings etc. you can combine this with the FLEX Method Rob mentioned. That's really easy. If you have probs with the FLEX-Mesh then have a look at the Flag Demo in the 3D Code Archives, it uses a Mesh with realtime modificated Vertex Heights.

So this technic uses two parts:

1)
temporary position camera at the lights position, point it to the Character, color the character grey and the ground black/hidden (entityFX 1 of course) and make a Renderworld (which is in fact the shadow viewed from above). transfer this Render to the Texturebuffer of the FLEX-Mesh.

2)
Position the FLEX-Mesh right above the Character, x/z-centered. get each Vertex X and Z position and use Linepick to determine the Ground Y.(Make shure the Character is not pickable for his own shadow) Now set the Vertex-Y of the FLEX-Mesh to Ground-Y +0.1 or something.

then reset camera and entityfx etc, continue as usual.


Litobyte(Posted 2002) [#22]
Ok, Norc, that's exactly what I tried yesterday,
But the point is: it doesnt work!!!! :(

I got to have my lightmap (yourcode) rendered on the "towel" (the flex mesh),I have set all my level children pickmode to 2 (polies),
but If I apply the UpdateFlex() function before the updateworld() it blocks down at 0.001 fps..

What's wrong ?

Also, I noticed the rendered lightmap, is not only the character model, but has some more pixel, I dont know where are they coming from, so now a questioN:

To pre render the lightmap, I saw in your code you used entityalpha flo,0
then back for rendering entityalpha flo,1.0

I have many more than "flo", so I used EntityHide on all stuff: skybox, groundplane, level,
but still something more than the character is "grabbed" by the temp camera.

Now what you think, if instead of hiding everything, we go toward this: create a white flipped faces box, big let's say the triple of the character, around the character, then do the lightmap, then hide the whitebox, then render scene.

Do you think will work ?


jfk EO-11110(Posted 2002) [#23]
not on a terrain, because you'll have hills and sometimes you'll standing between 2 hills which would intersect with the box. I think ti isn't very hard to set up an array which stores all EntityHandles of the "ground-objects".


Litobyte(Posted 2002) [#24]
But you'd hide the terrain while rendering the shadow...

Ok you mean an array which stores all the Ground surfaces, not the children...

because is that (the surface) that I need most of the time (really also the walls should be lightmapped if the light is not on the top of the character.

What I'm trying to do, is to retrieve each frame, the surface (triangle or quad) where the player is walking on, so to be able to texture it with a real time shadow lightmap.

I have at the mo' the handle of the mesh, the player is "touching/walking",
but now I need to know

EG: which surface of the room/corridor, is the one to be textured( I don't know if is possible in blitz, to assing a second texture layer just for a "face" of a cube/room, but I hope so).
Which one is below the player's foot ?

Infact at the moment, when I walk in a room, the real time animated shadow, is texturing all the parts of the room with itself, it seems is using the 1st channel of UVmapping, instead of using the whole mesh....

...stucked there

P.S. Tell me if you need screenshot


Litobyte(Posted 2002) [#25]
here it is NORC code, trying to be applied to a complete .3ds level...



Impossibile ? not for my buffalo head :P

I'm keep trying and testing...it also appears that lightmap get coloured...mmm


jfk EO-11110(Posted 2002) [#26]
My Opinion is it's too much trouble to apply the shadow to the current tris. Only in rare cases you will need to shade only one triangel, most times you'll have to use2 or more Tris for the shadow. The NORC-Code :) (Sounds like a standard - hey! i'm still using 'goto'!) is using the second UV-Coordds Set to blend the shadows as seen in the Demo.

Robs FLEX Method is much better casue you won't have to care about the whole stuff, just render it to a texture, align the texture to the flexmesh and align the flexmesh to the ground.

I've just finished a fist Demo that combines both. I had some troubles with the Vertex Coords, but now it works. It's pretty fast, even on my crap Machine. It would be perfect for a third person camera.

I'll send you the source for 'Realtime Shadows for large Scenes' per mail, cheers!


Litobyte(Posted 2002) [#27]
Humm that is for a terrain only ?

Or for a 3ds based as well ?


jfk EO-11110(Posted 2002) [#28]
Here's what I got so far:




Chevron(Posted 2002) [#29]
that looks like a great shadow norc, is their a demo? with source :-)


jfk EO-11110(Posted 2002) [#30]
it is for everything that is pickable. Just take a minute and read trough the Source and you'lle see a Remark : "Any pickable Geometry" :/ [EDIT] :)


jfk EO-11110(Posted 2002) [#31]
Yes there's a demo - but it's not for release to the public - only for co-producers.

I've released a lot of source, but whenever I have a Question or need some help then most ppl ignore me here. Seems I have to slightly adjust my philosophy.


Chevron(Posted 2002) [#32]
ok norc, no problem, I will allways try to help if possible but tend to refrain from posting unless i'm totally sure that I know what I'm on about as I dont like to look like a arse.
I can assure you that the majority of the blitz comunity DO appreiate your contribution.


jfk EO-11110(Posted 2002) [#33]
of course - don't take this personal.


Ash(Posted 2002) [#34]
[img=http://www.neomancer.co.uk/images/boogerman.jpg[/img]
this works?



No, Neomancer. It doesn't work because you forgot to close the first image tag before starting the last one. Put a "]" before the "[/". Your image will then show correctly.


jfk EO-11110(Posted 2002) [#35]
do it this way:
{img http://www.server.com/piccy.jpg}

(Replace the {} by []) There ain't no = or / or a closing tag. Or did you see a closing tag in html?


necky(Posted 2002) [#36]
Hi All,
Heres my shadow code. realtime, dynamic specular shadows. If you use the cursors the shadows dynaically change to the direction the light is pointing. If you want to use it, you'll have to fill in the missing files yourself, or send me a mail and I'll forward them on to you to get you started. :) Looks very nice when it's up and running :)

cu,
Mike

;******************
;* *
;* Shadow Casting *
;* *
;* by *
;* *
;* Mike Oakley *
;* *
;******************


Global screenx=320
Global screeny=240

Graphics3D screenx,screeny,16
SetBuffer BackBuffer()

AmbientLight 128,128,128

Global light=CreateLight()
MoveEntity light,0,5,-10

Global shadsizex=128
Global shadsizey=128

Global shadowcamera=CreateCamera()
PositionEntity shadowcamera,0,5,-10 ;x,y,z

RotateEntity shadowcamera,20,0,0

Global sphere=LoadMesh("spotlight.3ds")
Global shadowfloor=LoadMesh("shadowmodel.3ds")
Global softshadow=LoadMesh("soft-shadow.3ds",shadowcamera)
Global stepsFloor=LoadMesh("stepsfloor.3ds")
Global player=LoadMesh("horse.3ds")

Global black=LoadTexture("black.bmp")
Global white=LoadTexture("white.bmp")
Global tex=LoadTexture("texture.bmp")
Global objtexture=LoadTexture("T-PageHead.bmp")
Global Floortexture=LoadTexture("floor2.bmp")

Global shadtexture=CreateTexture(shadsizex,shadsizey)
Global softtexture=CreateTexture(shadsizex,shadsizey)

Global lightring=LoadTexture("lightring.bmp")
Global lightringsprite=CreateSprite(shadowcamera)
SpriteViewMode lightringsprite,1
EntityTexture lightringsprite,lightring

EntityBlend softshadow,2
EntityFX softshadow,1
MoveEntity softshadow,0,0,15
ScaleEntity softshadow,0.15,0.15,0.15
EntityTexture softshadow,shadtexture

MoveEntity lightringsprite,-0.15,0,50
ScaleSprite lightringsprite,50,50

EntityBlend shadowfloor,2
EntityFX shadowfloor,1

EntityFX sphere,1

MoveEntity player,0,-3.3,0
MoveEntity stepsfloor,0,-1,-2
MoveEntity shadowfloor,0,-1,-2

EntityTexture player,tex

EntityTexture stepsfloor,floortexture

;EntityTexture shadowfloor,black
EntityTexture shadowfloor,shadtexture

Global r#

Global pp#
pp#=6

Global gg#
gg#=10

Global ff#
ff#=-8

Global rot#
rot#=20

Global rotb#
rotb#=30

;********
;********
;* Main *
;********
;********

While Not KeyDown(1)

SetBuffer BackBuffer()

yyy#=0
xxx#=0

If KeyDown(205)=1 Then pp#=pp#+0.1
If KeyDown(203)=1 Then pp#=pp#-0.1

If KeyDown(30)=1 Then ff#=ff#+0.1
If KeyDown(44)=1 Then ff#=ff#-0.1

If KeyDown(200)=1 Then gg#=gg#+0.1
If KeyDown(208)=1 Then gg#=gg#-0.1

If KeyDown(2)=1 Then rot#=rot#+1
If KeyDown(3)=1 Then rot#=rot#-1

If KeyDown(4)=1 Then rotb#=rotb#+1
If KeyDown(5)=1 Then rotb#=rotb#-1

If KeyDown(19)=1 Then pp#=0:gg#=5:ff#=-8:rot#=20:rotb#=0

PositionEntity sphere,pp#,gg#,ff#
RotateEntity sphere,20,0,0
RotateEntity sphere,rot#,rotb#,0

r#=r#+1
RotateEntity player,0,r#,0
MoveEntity player,xxx#,yyy#,0

MoveEntity light,pp#,gg#,ff#
RotateEntity light,rot#,rotb#,0

makeshadow()

Flip

Wend

;***************************
;* Camera Mapping Function *
;***************************

Function cameramap(mesh)

Local xx#
Local yy#

For b=1 To CountSurfaces(mesh)
surf=GetSurface(mesh,b)

For a=0 To CountVertices(surf)-1

x#=VertexX(surf,a)
y#=VertexY(surf,a)
z#=VertexZ(surf,a)

TFormPoint x#,y#,z#,mesh,0

x=TFormedX()
y=TFormedY()
z=TFormedZ()

CameraProject shadowcamera,x#,y#,z#

xx#=ProjectedX()
yy#=ProjectedY()

u#=(xx#/shadsizex)
v#=(yy#/shadsizey)

If u#>1 Then u#=1
If v#>1 Then v#=1

If u#<0 Then u#=0
If v#<0 Then v#=0

; VertexTexCoords surf,a,VertexU(surf,a),VertexV(surf,a),0
VertexTexCoords surf,a,u#,v#
Next
Next
End Function

;*******************
;* Make the shadow *
;*******************

Function makeshadow()

ShowEntity player

CameraClsColor shadowcamera,255,255,255

CameraViewport shadowcamera,0,0,shadsizex,shadsizey
PositionEntity shadowcamera,pp#,gg#,ff#
RotateEntity shadowcamera,rot#,rotb#,0

AmbientLight 255,255,255

ShowEntity lightringsprite
HideEntity softshadow
HideEntity stepsfloor
HideEntity shadowfloor
EntityTexture player,black

UpdateWorld
RenderWorld

CameraClsColor shadowcamera,255,255,255

SetBuffer TextureBuffer(softtexture)
CopyRect 0,0,shadsizex,shadsizey,0,0,BackBuffer(),TextureBuffer(softtexture)
EntityTexture player,objtexture

HideEntity lightringsprite
HideEntity player
EntityTexture softshadow,softtexture
ShowEntity softshadow
UpdateWorld
RenderWorld

SetBuffer TextureBuffer(shadtexture)
CopyRect 0,0,shadsizex,shadsizey,0,0,BackBuffer(),TextureBuffer(shadtexture)

cameramap(shadowfloor)

AmbientLight 30,30,30

SetBuffer BackBuffer()

CameraClsColor shadowcamera,0,0,0
HideEntity softshadow
HideEntity lightringsprite
ShowEntity stepsfloor
ShowEntity shadowfloor
ShowEntity player
CameraViewport shadowcamera,0,0,screenx,screeny
PositionEntity shadowcamera,5,10,-17
RotateEntity shadowcamera,30,20,0
; HideEntity player

RenderWorld

End Function


Ash(Posted 2002) [#37]
*cries*

I don't understand!! I try, I really do, but I can't understand it...

I need someone to talk me through everything line by line. No holds barred, nice and slow - I'm thick when it comes to programming, so I need to have it spelled out to me. I can't really get my head around general points because I don't know how to turn the general stuff into specifics. I just can't take it all in at once...

...and I *really* think that gratuitous commenting is sorely lacking in the examples shown here. Shadows (for me, at least) aren't exactly the easiest thing in the world to do.

I feel a bit lame having said all of this, but this really is what it looks like - a pathetic, desperate plea for a lengthy, line-by-line explanation of how to implement shadows. -norc-'s example is what I'd rather have the explanation on. But that's because it is (for me) the fastest example of shadowing I've seen.

So does anyone care to help a complete dunce?


Rob (Posted 2002) [#38]
Now now... its easy enough. I suspect you're becoming stuck only because you're not up on the background info.

Lets concentrate on that and hopefully things will click into place.

A shadow in real life will usually be the same shape as you. When you walk around, your shadow is a filled in outline of you.

To make this, a camera is placed at the light source and pointed at you. The resulting render is placed on a texture. This is the shadow texture. More often than not it will be enough to convince you that it is a shadow for real. A few more tricks such as entitycolour or entityfx are used to color the render.

The scene is then rendered again, this time with the shadow texture already in place. This is usually going to be on a terrain or flat floor for this kind of trick.

points to note:

1. color objects dark grey
2. render these objects from above
3. copy the renderworld to a texture
4. apply this new "shadow" texture to the ground
5. restore the original objects
6. render the game
7 goto 1

This is the basic cycle of creating a realtime shadow map norc's way. Its really quite effective when done carefully.

As for typical shadows, such as a blob under the player's feet it may well be better to use a single sprite. if the ground is basically flat this is all you need. SpriteViewMode will prevent the sprite from turning.

If the ground has hills or it's a level - then my flex code does the same thing, except the shadow bends to fit the floor's bumps and hills.

I hope this is a little bit clearer... :)


Difference(Posted 2002) [#39]
@BlackHawk
Here is my original post about this metod. It also contains some links.


Ash(Posted 2002) [#40]
Thanks for all your help guys. Well, I've managed to do something... it's not quite right yet, but I believe I may have forgotten something somewhere along the line.

The shot below shows it all.



I'm sure you can all see the problem, but as I said, have I forgotten something here? How do I stop the terrain from tiling, and how do I get the terrain to stick to the model? (The ground texture has been removed to help you see the problem more clearly.)


Ash(Posted 2002) [#41]
OK. I've fixed the tiling issue by using the Clamp U and Clamp V flags.

However, the shadow still stays in a fixed place. How do I get the shadow to move with the model? I've textured the ground with the shadow texture (as Rob said), but the shadow won't move with the model? How do I get it to do this?


Rob (Posted 2002) [#42]
there's two methods:

a) you want the shadow map to work for lots of objects (in which case you just want to position the camera for rendering a shadow where the light is)

b) you want the shadow map for only the character

(b) is harder. You will need to use flex or move a large flat sprite under the character's feet every frame

See peter's notes about that...


jfk EO-11110(Posted 2002) [#43]
Ashley - I sent you the FLEX Combination - what's wrong with it?

Maybe one general word about shadows: If you're relatively new to game dev then shadows might be the wrong thing to start with, cause they are difficult even for advanced Programmers. If you once know what each Command is doing then it's easy to read through the Source and understand what happens when you have a logical description such as "position camera above scene and ENtityFX 1 the Character in a Grey-Tone". (Btw. I had some troubles with textured Models, so I just use a grey Texture and ENtityFX 1 for the Shadow Rendering.)

Your last Screenshot is looking good so far. But you have the wrong Scaling on the texture. The Problem is you should use a Texture which covers the whole Building without repetition. That means your Building Floor Texture can have max. screensize. not a good idea

A second method would be to Render the Shadow to the backbuffer and copy that rectangle to the xz location inside a huge Shadow/Floor texture. (thanks the new bit256 this is an option)

And there's the FLEX Method which is my favorite. It's a bit tricky to handle all kinds of slopes and I'm working on a solution for Stairways etc. but it's the fastest and the most flexible. You can put the shadow on multiple objects without to waste a single second to think about howto do that.


sswift(Posted 2002) [#44]
Doesn't anyone like the code I posted? :-)


jfk EO-11110(Posted 2002) [#45]
sswift - sorry, I didn't test you functions yet, the source is looking promising, but I was too lazy to figure out how to use them. Could you post a little Example that can be started? (just omit the functions)

cheers!


Ash(Posted 2002) [#46]
A second method would be to Render the Shadow to the backbuffer and copy that rectangle to the xz location inside a huge Shadow/Floor texture. (thanks the new bit256 this is an option)


Hmmm... my floor texture is tiled. I don't suppose this would work with a 128x128 texture that is tiled to cover the whole floor?

I'm trying to go Rob's way of having a flat sprite that is underneath the character and moves with the character. However, the sprite is not being textured properly. In fact, it is not being textured at all. All I'm getting is a totally white sprite under the character.

I have the media and full source if anyone would like to have a peek at it and tell me what is wrong. Obviously, you will get full credit for the fix.


jfk EO-11110(Posted 2002) [#47]
I guess that sprite is not visible because the Shadow Texture as in the Example from the code archive is a second blend-texture. but it should be the first one.
try
entitytexture sprite,limap,0,0
instead of
entitytexture sprite,limap,0,1


sswift(Posted 2002) [#48]
Norc:
The code is really simple if you look at it.

All you have to do is call one function to define an object as casting a shadow, and another object as reciving the shadow from that object, and the texture to use for the shadow, and then each time you render the world call the function which regenerates all the shadows.

That's it!

The code casts shadows straight down only currently. But in theory it should be possible to modify it to cast shadows in any directon.

Also, the shadow textures right now are intended to be circular shadows. So you just make a circular black texture. Then that will wrap around the obejct the shadow is being cast down upon.

However, you should be able to generate a new shadow texture each frame from the actual object's geometry, and then you could have shaped shadows which cast downward.

In theory you could do this by rendering the world from a camera view to a small viewport offscreen. Just point the camera down towards the object making sure the object is black and the background is white. Then you just grab that for your texture.

I don't have a small sample program for it, but like I said, it's only 2 function calls. One to create the shadow generator shadow caster link, and one each renderworld to create the actual shadows. Doesn't get any simpler than that.

Oh nad with my system it doesn't matter how your world is textured. The system creates new geometry specifically for the shadows.


Chevron(Posted 2002) [#49]
Do your shadow functions work on single hieghtmapped terrains SSwift?


jfk EO-11110(Posted 2002) [#50]
sswift - I tried it hard :( here's what I tried:


Graphics3D 640,480,16,2
SetBuffer BackBuffer()
   
Type Shadow_Caster
    Field Caster
    Field Receiver
    Field Texture
    Field Radius#
End Type
   
       
Type Shadow
    Field Mesh
End Type   


camera=CreateCamera()
PositionEntity camera,0,50,-200

light=CreateLight(2)
PositionEntity light,0,200,0
AmbientLight 100,100,100

ground=CreateSphere()
ScaleEntity ground,100,10,100
PositionEntity ground,0,-30,0

cube = CreateCube()
ScaleEntity cube,20,20,20
RotateEntity cube,30,50,70
PositionEntity cube,0,50,0

PointEntity camera,cube

shd=CreateTexture(256,256,48)
SetBuffer TextureBuffer(shd)
Color 127,127,127
Rect 0,0,256,256,1
SetBuffer BackBuffer()

;----------------
Create_Shadow(ground, -30, -30, 30, 30, 0)
; Cast_Shadow(cube, ground, shd, 10.0) ; ?????
;----------------

While KeyDown(1)=0
 Cast_Shadow(cube, ground, shd, 10.0)
 ; Update_Shadows()  ; ??????????
 UpdateWorld()
 RenderWorld()
 Flip
Wend


End

... the Functions ...



I tried to read through the source and I understood as follows:

A mesh is created every Renderworld? why don't you reuse the mesh? The Vertex Alignement Part is pretty good. The whole thing isn't far away from the one I'm working on. Stuying your Source just gave an idea how to fix one of my mesh-alignement-problems. THX!


Ash(Posted 2002) [#51]
Right. I've almost got shadows as-near-as-damnit nailed.

Only one niggling problem remains. How do I make the white parts of a textured sprite transparent? The solution to the problem, easy as it may be, eludes me completely. Here is a shot, just in case you may need it:



I dunno, maybe I can't think straight because my brain is on supreme downtime after exams.


semar(Posted 2002) [#52]
You should load the texture with the flag 4, that is, masked; in this way, all the black color that are present on the texture, will not be drawn on the screen.

See the LoadTexture command, and the related parameters.

Hope this helps,
Sergio.


jfk EO-11110(Posted 2002) [#53]
blackhawk - congrats, lookin good so far!

try this for the white background (which is ok):

; mesh is the Sprite or whatever and limat the Shadow Texture.
EntityTexture mesh,limat,0,0
EntityBlend mesh,2
EntityFX mesh,8

this makes the background transparent and the shadow semitransparent if it's grey.


Chroma(Posted 2002) [#54]
Uh...how do you do shadows? Now I want to know. :)


Ash(Posted 2002) [#55]
OK... now this is getting weird.

Normally, I have the texture flag set at 256, for speed. This is what I get following norc's above method:



After this, (since it didn't work correctly) I adapted semar's suggestion, and used a flag value of 260 (256+4 - fast textures+alpha). However, doing that gave me this:



Since that didn't work either, I removed the 256 flag and just left the 4 (alpha) flag. This is what I got for trying that:



All of these problems I think are very strange, and I don't know why they happen. What exactly is going wrong here?

I have an nVidia GeForce 2 MX (32MB) with the latest nVidia official drivers (29.42).


Wiebo(Posted 2002) [#56]
Blackhawk,

It looks like the same problem I had. Make sure you use EntityBlend ... ,2 on the shadowmesh, and make sure the texture is white with a black shadow.


jfk EO-11110(Posted 2002) [#57]
Just like I said Grrrrrrr! :)


Wiebo(Posted 2002) [#58]
Yup, but it looks similar to what I experienced, so I just wanted to be sure.


jfk EO-11110(Posted 2002) [#59]
>>Normally, I have the texture flag set at 256, for speed. This is what I get following norc's above method:<<

Did you try that with 256 off as well? I can't believe this, here it works. Hmm maybe because its a sprite? you could test this with a cube (scale it to 10,0.1,10) instead of a sprite. Don't forget to hide the Shadow Sprite as well when you RenderWorld the Shadow Texture.


Ash(Posted 2002) [#60]
Using the cube still doesn't work.

However, the really awesome news is that it *finally* does work for me (using the sprite, as before). Here's what I did:

I took norc's above code:

EntityTexture mesh,limat,0,0
EntityBlend mesh,2
EntityFX mesh,8



Removed the ",0,0" from the EntityTexture line, removed the EntityFX line, and set the EntityBlend for the shadow ("mesh") to 3 instead of 2. I now get a lovely real-time shadow in my game and it still pumps out 50-60 FPS!!



AWESOME!! =^__^=


sswift(Posted 2002) [#61]
Chevron:
"Do your shadow functions work on single hieghtmapped terrains SSwift?"

Not unless those terrains are a mesh. My system will not work with LOD based terrains because Mark does not give you access to the vertex data for the mesh each time it is rebuilt. If he did, then it would.

You could of course make a mesh that represents the terrain at some level of detail and use that to generate the shadows from, but then the shadows would not lie flush with the actual terrain because the terrain changes shape as it changes LOD.




Norc:
"A mesh is created every Renderworld? why don't you reuse the mesh? The Vertex Alignement Part is pretty good. The whole thing isn't far away from the one I'm working on. Stuying your Source just gave an idea how to fix one of my mesh-alignement-problems. THX!"


Why don't I reuse the mesh? Because the mesh has to change every time it moves. The mesh I create is just large enough to cover the area the shadows lies on precisely. The polygons in it mirror the polygons the shadow overlaps precisely.

In other words, if you have a teapot, and the shadow covers that whole teapot, the shadow mesh while over the teapot will be constructed of all the polygons in that teapot which face upwards.

Unlike towel shadows, my shadows lie almost perfectly flush with the surface, so there are no rendering errors, EVER. You'll never see a bit of shadow overhanging the ege of a stair for example.

With towel shadow techniques, the polygons do not match the polygons the shaodw lies over exactly, so you have to tesselate your towel a lot to minimize error when your player is near the edge of something or over a complex object.

With my system only the exact number of polygons needed to render the shadow are created and they're exactly the same as the polygons which are being shadowed so there's never any polygons hanging over edges.

So.... when your player walks over a floor, the code will make maybe one or two polygons and run really fast. If they walk over a teapot, then the code will create hundreds of polygons if the teapot is made of hundreds of polygons and be somewhat slower. But you'll still get still pretty good framerates and that is a worst case scenario.

The only issue with my code right now is that if your levels have rooms over rooms, my code has no way to tell that the shadow has been blocked by something. So if a player is in a room above, and another player is in a room below, the second player would see the shadow of the first player on the floor, and the first player would also see their shadow on the floor below them. So the shadow from the first player would be cast onto two surfaces. Backfacing surfaces do not recieve shadows though so there would not be a shadow on the ceiling.

I'm sure there's a way to work around that problem, but I haven't bothered because I don't need to worry about it in my game.


Rob (Posted 2002) [#62]
If your floor is always flat in-game and shadow will always be cast from light above, then you can gain a massive speed boost by just using a copy of the main character with Y scale being around 0.5 and placed at the feet...

no rendering needed.


sswift(Posted 2002) [#63]
Okay norc... I fixed your code. It took me three hours, but I fixed it.


There were a number of problems with it, some wiht your code, some with mine. I won't list them all but here are a few.

First, the object you were casting shadows did not have it's vertices in the right places. The coordinates of the vertices in the object must match their locations in the world. My code assumes you're casting shadows onto a static level so this isn't a problem. You can of course change this by modifying my code to sutract the vertex positions by the object's offset in the real world when doing the calculations.

Second, the blending mode for the texture was not set up right. The texture blend mode has to be additive. That's my fault for not informing you of that. I'd forgotten it myself. It has to be add because the colors are added to the vertex colors in order to create the effect of the shadow fading out when the surface's angle is too steep relative to the imaginary overhad light source. To do this the vertex colors start out black and go to white as the surface angle increases, and the texture of the shadow is ADDED to this, so that the areas not in shadow become white anyhow, and the areas in shadow stay whatever color the vertex colors say they should be.

Third you were scaling the entity that you wanted to cast shadows onto rather than the MESH. This whole system relies on the mesh data being set up a certain way. You can of course change this behavior by adding more math to the alogirthm, but I did not do that myself because to do so would slow the alogorithm down some.

Fourth, you were using a SOLID BLACK shadow texture. For the shadow code to work the areas outside the shadow must be white. And for them to be white yhou have to clamp the texture... but for the clmaping to make the areas outside the shadow white, ALL the edge pixels of the shadow must be white, otherwise the UV clmaping will copy the blackness everywhere outside the shadow and you don't want that.

You'll notice that the shadow is square, but not aligned properly with the cube. As I said, you have to generate an appropriate shadow each frame for the object in it's current position for the shadow to be shaped properly. Rendering a camera view from above the object to be shadowed while it is black and against a white background and then using that as the texture is one way of doing this.

Anyhow the code now works. Hope this helps you out!



Graphics3D 640,480,16,2
SetBuffer BackBuffer()

Type Shadow_Caster
	Field Caster
	Field Receiver
	Field Texture
	Field Radius#
End Type


Type Shadow
	Field Mesh
End Type


	camera=CreateCamera()
	PositionEntity camera,50,100,-200
	
	light=CreateLight(2)
	PositionEntity light,0,200,0
	AmbientLight 100,100,100

	ground=CreateSphere()
	ScaleMesh ground,100,25,100
	; Position the mesh so that the lower bottom left hand corner of it lies at 0,0,0
	PositionMesh ground,50,12.5,50
	; Update the normals because we scaled the mesh (newest versions of Blitz should not require this)
	UpdateNormals ground
	;PositionEntity ground,50,25,50
	

	cube = CreateCube()
	ScaleEntity cube,20,20,20
	RotateEntity cube,30,50,70
	PositionEntity cube,50,150,50

	PointEntity camera,cube
		
	; Create texture of black square in white background
	shd=CreateTexture(256,256,48)  ; Flags = ClampU, ClampV
	TextureBlend shd, 3
	SetBuffer TextureBuffer(shd)
	ClsColor 255, 255, 255
	Cls
	Color 0,0,0
	; Must leave border of white around shadow!
	Rect 1,1,254,254,1
	SetBuffer BackBuffer()

	Cast_Shadow(cube, ground, shd, 20.0) 

	While KeyDown(1)=0
	
		Update_Shadows() 
		UpdateWorld()
		RenderWorld()
		Flip

	Wend


End


; -------------------------------------------------------------------------------------------------------------------
; This function sets up a new shadow caster/receiver pair.
;
; Caster   = Mesh to cast shadows.
; Receiver = Mesh to receive shadows.
; Texture  = Texture to use for shadow.  (Must have UV clamping enabled for proper operation!)
; Radius#  = Radius of the shadow.
; -------------------------------------------------------------------------------------------------------------------
Function Cast_Shadow(Caster, Receiver, Texture, Radius#)

	ThisCaster.Shadow_Caster = New Shadow_Caster	
	
	ThisCaster\Caster   = Caster
	ThisCaster\Receiver = Receiver
	ThisCaster\Texture  = Texture
	ThisCaster\Radius#  = Radius#

End Function


; -------------------------------------------------------------------------------------------------------------------
; This function deletes all old shadows and creates a shadow mesh for each shadow casting object.
;
; Note:
; Shadows only need to be repositioned and recreated each time the scene is rendered, 
; not every time the physics are updated.
; -------------------------------------------------------------------------------------------------------------------
Function Update_Shadows()

	Delete_Shadows()

	For ThisCaster.Shadow_Caster = Each Shadow_Caster
		 
		Shadow_Center_X# = EntityX#(ThisCaster\Caster, True)
		Shadow_Center_Y# = EntityY#(ThisCaster\Caster, True)
		Shadow_Center_Z# = EntityZ#(ThisCaster\Caster, True)
		Shadow_Radius#   = ThisCaster\Radius#		
						
		ThisShadow.Shadow = New Shadow
		ThisShadow\Mesh = Create_Shadow(ThisCaster\Receiver, Shadow_Center_X#-Shadow_Radius#, Shadow_Center_Z#+Shadow_Radius#, Shadow_Center_X#+Shadow_Radius#, Shadow_Center_Z#-Shadow_Radius#, Shadow_Center_Y#)
	
		; full bright, use vertex colors, no fog.
		EntityFX ThisShadow\Mesh, 1+2+8

		; multiply blend mesh.
		EntityBlend ThisShadow\Mesh, 2 

		EntityTexture ThisShadow\Mesh, ThisCaster\Texture
				
	Next	
		
End Function


; -------------------------------------------------------------------------------------------------------------------
; This function deletes all active shadows.
; -------------------------------------------------------------------------------------------------------------------
Function Delete_Shadows()

	For ThisShadow.Shadow = Each Shadow
		FreeEntity ThisShadow\Mesh
		Delete ThisShadow.Shadow
	Next	

End Function


; -------------------------------------------------------------------------------------------------------------------
; This function deletes all shadow casters/recievers which reference the specified entity.
;
; When you delete an entity which is casting or receiving a shadow, you must call this function so that the game
; does not crash from trying to reference an entity which does not exist.
; -------------------------------------------------------------------------------------------------------------------
Function Delete_Shadow_Caster(Entity)

	For ThisCaster.Shadow_Caster = Each Shadow_Caster

		If (ThisCaster\Caster = Entity) Or (ThisCaster\Receiver = Entity) 
			Delete ThisCaster
		EndIf	

	Next	

End Function


; -------------------------------------------------------------------------------------------------------------------
; This function creates a shadow mesh and returns it's handle.
;
; Reciever = The entity to recieve the shadow.
; -------------------------------------------------------------------------------------------------------------------
Function Create_Shadow(Receiver, Shadow_X1#, Shadow_Z1#, Shadow_X2#, Shadow_Z2#, Shadow_Y#)

	; Get the cordinates of the lower left hand corner of the shadow.
	Shadow_Corner_X# = Shadow_X1#
	Shadow_Corner_Z# = Shadow_Z2# 

	Shadow_Scale# = Sqr((Shadow_X1#-Shadow_X2#)*(Shadow_X1#-Shadow_X2#))

	DebugLog "S: " + Str$(Shadow_Scale#)
	ShadowTris = 0

	; Create the mesh for the shadow, and give it a surface to add polygons to.
	MESH_Shadow = CreateMesh()
	SURFACE_Shadow = CreateSurface(MESH_Shadow)

	; Loop through all triangles in all surfaces of the reciever.
	Surfaces = CountSurfaces(Receiver)
	For LOOP_Surface = 1 To Surfaces

		Surface_Handle = GetSurface(Receiver, LOOP_Surface)

		Tris = CountTriangles(Surface_Handle)
		For LOOP_Tris = 0 To Tris-1

			Vertex_0 = TriangleVertex(Surface_Handle, LOOP_Tris, 0)
			Vertex_1 = TriangleVertex(Surface_Handle, LOOP_Tris, 1)
			Vertex_2 = TriangleVertex(Surface_Handle, LOOP_Tris, 2)
									
			; Check to see if the triangle is inside the shadow's bounding rectangle.
			;
			; This test works by seeing if all of a triangle's points are on a specific side of each side of the
			; rectangle.  The test is not 100% accurate... a very few triangles will pass the test but actually be
			; outside the region.  But we are concerned only with making sure we find all the triangles which ARE
			; in the region and cull the vast majority outside the region, so a little sloppiness in the test is
			; okay if that means it's really fast.
			Shadow = True

			Z0# = VertexZ#(Surface_Handle, Vertex_0)
			Z1# = VertexZ#(Surface_Handle, Vertex_1)
			Z2# = VertexZ#(Surface_Handle, Vertex_2)

			; Is polygon to north of shadow's bounding rectangle?
			If (Z0# > Shadow_Z1#) And (Z1# > Shadow_Z1#) And (Z2# > Shadow_Z1#) 
				Shadow = False 
			Else	

				; Is polygon to south of shadow's bounding rectangle?
				If (Z0# < Shadow_Z2#) And (Z1# < Shadow_Z2#) And (Z2# < Shadow_Z2#) 
					Shadow = False	
				Else
					
					X0# = VertexX#(Surface_Handle, Vertex_0)
					X1# = VertexX#(Surface_Handle, Vertex_1)
					X2# = VertexX#(Surface_Handle, Vertex_2)

					; Is polygon to west of shadow's bounding rectangle?
					If (X0# < Shadow_X1#) And (X1# < Shadow_X1#) And (X2# < Shadow_X1#) 
						Shadow = False 
					Else

						; Is polygon to east of shadow's bounding rectangle?
						If (X0# > Shadow_X2#) And (X1# > Shadow_X2#) And (X2# > Shadow_X2#) 
							Shadow = False 
						Else

							Y0# = VertexY#(Surface_Handle, Vertex_0)
							Y1# = VertexY#(Surface_Handle, Vertex_1)
							Y2# = VertexY#(Surface_Handle, Vertex_2)

							; Is shadow below the polygon?
							If (Shadow_Y# < Y0#) And (Shadow_Y# < Y1#) And (Shadow_Y# < Y2#)
								Shadow = False
							Else
								
								NY0# = VertexNY#(Surface_Handle, Vertex_0) 
								NY1# = VertexNY#(Surface_Handle, Vertex_1) 
								NY2# = VertexNY#(Surface_Handle, Vertex_2)

								FaceNY# = NY0# + NY1# + NY2#								

								; Is polygon facing downwards?
								If (FaceNY# < 0) Then
									Shadow = False
								Else
								
									If (FaceNY# = 0)
										Shadow = False
									EndIf	
									
								EndIf
							EndIf
						EndIf
					EndIf
				EndIf
			EndIf	
			
			; If the triangle is inside the bounding box then add it to the mesh.
			; Note that shadow texture will be flipped top to bottom because of how UV coordinates are assigned to the vertices from the world coordinates.
			If Shadow = True

				Shadow_Vertex_0 = AddVertex(SURFACE_Shadow, X0#, Y0#, Z0#, (X0#-Shadow_Corner_X#)/Shadow_Scale#, (Z0#-Shadow_Corner_Z#)/Shadow_Scale#)
				Shadow_Vertex_1 = AddVertex(SURFACE_Shadow, X1#, Y1#, Z1#, (X1#-Shadow_Corner_X#)/Shadow_Scale#, (Z1#-Shadow_Corner_Z#)/Shadow_Scale#)
				Shadow_Vertex_2 = AddVertex(SURFACE_Shadow, X2#, Y2#, Z2#, (X2#-Shadow_Corner_X#)/Shadow_Scale#, (Z2#-Shadow_Corner_Z#)/Shadow_Scale#)

				; Adjust shadow brightness at each vertex according to vertex normal.				
					VC = (1.0-NY0#)^3*255.0
					If (VC > 0) Then VertexColor SURFACE_Shadow, Shadow_Vertex_0, VC, VC, VC
	
					VC = (1.0-NY1#)^3*255.0
					If (VC > 0) Then VertexColor SURFACE_Shadow, Shadow_Vertex_1, VC, VC, VC
				
					VC = (1.0-NY2#)^3*255.0
					If (VC > 0) Then VertexColor SURFACE_Shadow, Shadow_Vertex_2, VC, VC, VC

				; Add the triangle to the shadow mesh.
					AddTriangle(SURFACE_Shadow, Shadow_Vertex_0, Shadow_Vertex_1, Shadow_Vertex_2)

				ShadowTris = ShadowTris + 1

			EndIf
			
		Next			
	
	Next

	Return MESH_Shadow

End Function



Chevron(Posted 2002) [#64]
do these functions work on terrains shaun?


sswift(Posted 2002) [#65]
Read a couple posts up, I answewered your question already. :-)

In short, it depends on what kind of terrain you're talking about. :-)


sswift(Posted 2002) [#66]
Oh and one more thing on that terrain thing...

If you use an impostor mesh to create shadows for the terrain, then if you make the shadows autofade out at a certain distance then you can limit the error so that it wouldn't be as much of an issue. The terrain is very detailed up close but not so detailed far off, so as long as you don't have the shaodws too far away they should match the countour of the terrain pretty closely. And if you have a problem with the shadows dipping below the terrain you can move the impostor mesh's vertices up a little bit so that the shaodws get created higher up.


Chevron(Posted 2002) [#67]
Sorry, missed that answer, thats as I thought, shame, but thanks for the code you have posted as it is sure to come useful for me in many other instances. I'll look into creating a duplicate mesh to create my effect, I can do it now but seem to be taking a big speed hit using vertex manipulation.


sswift(Posted 2002) [#68]
Oh you're trying to do a scrolling terrain type of thing and taking a speed hit from doing that? You should just break your terrain up into chunks and fade the chunks out as they go out of range. That would work better I think.


sswift(Posted 2002) [#69]
New code!

I just made some changes to my code. I optimized it a little bit more I think, though the addition I made probably slowed it down a bit so I'm not sure if I had a net gain or not.

The addition I made was to make it so that you can put the reciver ANYHWERE in the world, and you don't have to specially position it's vertices within the mesh to make the shadow code work.

This means the code is a lot simpler to use.

Use the arrow keys to move the reciver around. The cube and the camera are fixed in location, though they could be moved as well.


Graphics3D 640,480,16,2

SetBuffer BackBuffer()

Type Shadow_Caster
	Field Caster
	Field Receiver
	Field Texture
	Field Radius#
End Type


Type Shadow
	Field Mesh
End Type


.Main
	camera=CreateCamera()
	PositionEntity camera,0,100,-300
	
	light=CreateLight(2)
	PositionEntity light,0,200,0
	AmbientLight 100,100,100

	ground=CreateSphere()
	; Scale the mesh.  If you want to change the mesh's shape you have to scalemesh, not scaleentity.  
	; The vertices won't represent their position in space if you scaleentity.
	ScaleMesh ground,100,50,100
	; Update the normals because we scaled the mesh (newest versions of Blitz should not require this)
	UpdateNormals ground

	cube = CreateCube()
	ScaleEntity cube,20,20,20
	RotateEntity cube,30,50,70
	PositionEntity cube,0,150,0

	PointEntity camera,cube
		
	; Create texture of black square in white background
	shd=CreateTexture(256,256,48)  ; Flags = ClampU, ClampV
	TextureBlend shd, 3
	SetBuffer TextureBuffer(shd)
	ClsColor 255, 255, 255
	Cls
	Color 0,0,0
	; Must leave border of white around shadow!
	Rect 1,1,254,254,1
	SetBuffer BackBuffer()

	Cast_Shadow(cube, ground, shd, 20.0) 

	While KeyDown(1)=0

		If KeyDown(200) MoveEntity ground, 0, 0, 1
		If KeyDown(208) MoveEntity ground, 0, 0, -1
		If KeyDown(203) MoveEntity ground, -1, 0, 0
		If KeyDown(205) MoveEntity ground, 1, 0, 0

		Update_Shadows() 

		UpdateWorld
		RenderWorld
		Flip

	Wend


End


; -------------------------------------------------------------------------------------------------------------------
; This function sets up a new shadow caster/receiver pair.
;
; Caster   = Mesh to cast shadows.
; Receiver = Mesh to receive shadows.
; Texture  = Texture to use for shadow.  (Must have UV clamping enabled for proper operation!)
; Radius#  = Radius of the shadow.
; -------------------------------------------------------------------------------------------------------------------
Function Cast_Shadow(Caster, Receiver, Texture, Radius#)

	ThisCaster.Shadow_Caster = New Shadow_Caster	
	
	ThisCaster\Caster   = Caster
	ThisCaster\Receiver = Receiver
	ThisCaster\Texture  = Texture
	ThisCaster\Radius#  = Radius#

End Function


; -------------------------------------------------------------------------------------------------------------------
; This function deletes all old shadows and creates a shadow mesh for each shadow casting object.
;
; Note:
; Shadows only need to be repositioned and recreated each time the scene is rendered, 
; not every time the physics are updated.
; -------------------------------------------------------------------------------------------------------------------
Function Update_Shadows()

	Delete_Shadows()

	For ThisCaster.Shadow_Caster = Each Shadow_Caster
		 
		Shadow_Center_X# = EntityX#(ThisCaster\Caster, True)
		Shadow_Center_Y# = EntityY#(ThisCaster\Caster, True)
		Shadow_Center_Z# = EntityZ#(ThisCaster\Caster, True)
		Shadow_Radius#   = ThisCaster\Radius#		
						
		ThisShadow.Shadow = New Shadow
		ThisShadow\Mesh = Create_Shadow(ThisCaster\Receiver, Shadow_Center_X#-Shadow_Radius#, Shadow_Center_Z#+Shadow_Radius#, Shadow_Center_X#+Shadow_Radius#, Shadow_Center_Z#-Shadow_Radius#, Shadow_Center_Y#)
	
		; full bright, use vertex colors, no fog.
		EntityFX ThisShadow\Mesh, 1+2+8

		; multiply blend mesh.
		EntityBlend ThisShadow\Mesh, 2 

		EntityTexture ThisShadow\Mesh, ThisCaster\Texture
				
	Next	
		
End Function


; -------------------------------------------------------------------------------------------------------------------
; This function deletes all active shadows.
; -------------------------------------------------------------------------------------------------------------------
Function Delete_Shadows()

	For ThisShadow.Shadow = Each Shadow
		FreeEntity ThisShadow\Mesh
		Delete ThisShadow.Shadow
	Next	

End Function


; -------------------------------------------------------------------------------------------------------------------
; This function deletes all shadow casters/recievers which reference the specified entity.
;
; When you delete an entity which is casting or receiving a shadow, you must call this function so that the game
; does not crash from trying to reference an entity which does not exist.
; -------------------------------------------------------------------------------------------------------------------
Function Delete_Shadow_Caster(Entity)

	For ThisCaster.Shadow_Caster = Each Shadow_Caster

		If (ThisCaster\Caster = Entity) Or (ThisCaster\Receiver = Entity) 
			Delete ThisCaster
		EndIf	

	Next	

End Function


; -------------------------------------------------------------------------------------------------------------------
; This function creates a shadow mesh and returns it's handle.
;
; Reciever = The entity to recieve the shadow.
; -------------------------------------------------------------------------------------------------------------------
Function Create_Shadow(Receiver, Shadow_X1#, Shadow_Z1#, Shadow_X2#, Shadow_Z2#, Shadow_Y#)

	; Get the coordinates of the receiver in world space.
	Receiver_X# = EntityX#(Receiver, True)
	Receiver_Y# = EntityY#(Receiver, True)
	Receiver_Z# = EntityZ#(Receiver, True)

	; Get the coordinates of the lower left hand corner of the shadow in world space.
	Shadow_Corner_X# = Shadow_X1#
	Shadow_Corner_Z# = Shadow_Z2# 

	; Convert the shadow's coordinates into the reciever's vertex space.
	Shadow_X1# = Shadow_X1# - Receiver_X#
	Shadow_X2# = Shadow_X2# - Receiver_X#
	Shadow_Z1# = Shadow_Z1# - Receiver_Z#
	Shadow_Z2# = Shadow_Z2# - Receiver_Z#
	Shadow_Y#  = Shadow_Y#  - Receiver_Y#

	; Determine how large the shadow is.
	Shadow_Scale# = Sqr((Shadow_X1#-Shadow_X2#)*(Shadow_X1#-Shadow_X2#))

	ShadowTris = 0

	; Create the mesh for the shadow, and give it a surface to add polygons to.
	MESH_Shadow = CreateMesh()
	SURFACE_Shadow = CreateSurface(MESH_Shadow)

	; Loop through all triangles in all surfaces of the reciever.
	Surfaces = CountSurfaces(Receiver)
	For LOOP_Surface = 1 To Surfaces

		Surface_Handle = GetSurface(Receiver, LOOP_Surface)

		Tris = CountTriangles(Surface_Handle)
		For LOOP_Tris = 0 To Tris-1

			Vertex_0 = TriangleVertex(Surface_Handle, LOOP_Tris, 0)
			Vertex_1 = TriangleVertex(Surface_Handle, LOOP_Tris, 1)
			Vertex_2 = TriangleVertex(Surface_Handle, LOOP_Tris, 2)
									
			; Check to see if the triangle is inside the shadow's bounding rectangle.
			;
			; This test works by seeing if all of a triangle's points are on a specific side of each side of the
			; rectangle.  The test is not 100% accurate... a very few triangles will pass the test but actually be
			; outside the region.  But we are concerned only with making sure we find all the triangles which ARE
			; in the region and cull the vast majority outside the region, so a little sloppiness in the test is
			; okay if that means it's really fast.
			Shadow = True

			Z0# = VertexZ#(Surface_Handle, Vertex_0)
			Z1# = VertexZ#(Surface_Handle, Vertex_1)
			Z2# = VertexZ#(Surface_Handle, Vertex_2)

			; Is polygon to north of shadow's bounding rectangle?
			If (Z0# > Shadow_Z1#) And (Z1# > Shadow_Z1#) And (Z2# > Shadow_Z1#) 
				Shadow = False 
			Else	

				; Is polygon to south of shadow's bounding rectangle?
				If (Z0# < Shadow_Z2#) And (Z1# < Shadow_Z2#) And (Z2# < Shadow_Z2#) 
					Shadow = False	
				Else
					
					X0# = VertexX#(Surface_Handle, Vertex_0)
					X1# = VertexX#(Surface_Handle, Vertex_1)
					X2# = VertexX#(Surface_Handle, Vertex_2)

					; Is polygon to west of shadow's bounding rectangle?
					If (X0# < Shadow_X1#) And (X1# < Shadow_X1#) And (X2# < Shadow_X1#) 
						Shadow = False 
					Else

						; Is polygon to east of shadow's bounding rectangle?
						If (X0# > Shadow_X2#) And (X1# > Shadow_X2#) And (X2# > Shadow_X2#) 
							Shadow = False 
						Else

							Y0# = VertexY#(Surface_Handle, Vertex_0)
							Y1# = VertexY#(Surface_Handle, Vertex_1)
							Y2# = VertexY#(Surface_Handle, Vertex_2)

							; Is shadow below the polygon?
							If (Shadow_Y# < Y0#) And (Shadow_Y# < Y1#) And (Shadow_Y# < Y2#)
								Shadow = False
							Else
								
								NY0# = VertexNY#(Surface_Handle, Vertex_0) 
								NY1# = VertexNY#(Surface_Handle, Vertex_1) 
								NY2# = VertexNY#(Surface_Handle, Vertex_2)

								FaceNY# = NY0# + NY1# + NY2#								

								; Is polygon facing downwards?
								If (FaceNY# < 0) Then
									Shadow = False
								Else
								
									If (FaceNY# = 0)
										Shadow = False
									EndIf	
									
								EndIf
							EndIf
						EndIf
					EndIf
				EndIf
			EndIf	
			
			; If the triangle is inside the bounding box then add it to the mesh.
			; Note that shadow texture will be flipped top to bottom because of how UV coordinates are assigned to the vertices from the world coordinates.
			If Shadow = True

				; Convert the coordinates of the vertices of the receiver from receiver space to world space.
				X0# = X0# + Receiver_X#
				X1# = X1# + Receiver_X#
				X2# = X2# + Receiver_X#
				Y0# = Y0# + Receiver_Y# + 0.1
				Y1# = Y1# + Receiver_Y#	+ 0.1
				Y2# = Y2# + Receiver_Y# + 0.1
				Z0# = Z0# + Receiver_Z#
				Z1# = Z1# + Receiver_Z#
				Z2# = Z2# + Receiver_Z#
				
				Shadow_Vertex_0 = AddVertex(SURFACE_Shadow, X0#, Y0#, Z0#, (X0#-Shadow_Corner_X#)/Shadow_Scale#, (Z0#-Shadow_Corner_Z#)/Shadow_Scale#)
				Shadow_Vertex_1 = AddVertex(SURFACE_Shadow, X1#, Y1#, Z1#, (X1#-Shadow_Corner_X#)/Shadow_Scale#, (Z1#-Shadow_Corner_Z#)/Shadow_Scale#)
				Shadow_Vertex_2 = AddVertex(SURFACE_Shadow, X2#, Y2#, Z2#, (X2#-Shadow_Corner_X#)/Shadow_Scale#, (Z2#-Shadow_Corner_Z#)/Shadow_Scale#)

				; Adjust shadow brightness at each vertex according to how much the vertex normal points down.
					;VC = (1.0-NY0#)^3*255.0
					VC = (1.0-NY0#)*(1.0-NY0#)*255.0
					VertexColor SURFACE_Shadow, Shadow_Vertex_0, VC, VC, VC
	
					VC = (1.0-NY1#)*(1.0-NY1#)*255.0
					VertexColor SURFACE_Shadow, Shadow_Vertex_1, VC, VC, VC
				
					VC = (1.0-NY2#)*(1.0-NY2#)*255.0
					VertexColor SURFACE_Shadow, Shadow_Vertex_2, VC, VC, VC

				; Add the triangle to the shadow mesh.
					AddTriangle(SURFACE_Shadow, Shadow_Vertex_0, Shadow_Vertex_1, Shadow_Vertex_2)

				ShadowTris = ShadowTris + 1

			EndIf
			
		Next			
	
	Next

	Return MESH_Shadow

End Function



jfk EO-11110(Posted 2002) [#70]
Ok, thanks sswift!


Filax(Posted 2003) [#71]
IT work with animated objects ?


maximo(Posted 2003) [#72]
Ressurrecion ;)


jfk EO-11110(Posted 2003) [#73]
"IT work with animated objects ? "

The one I posted in the Code Archives: yes, but it is a bit slow and it complicates the source.