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