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.
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