After a number of weeks of trying to optimize a number of different 'routines' i came up with for creating shadow volumes of unboned meshes, i concluded that a fast way of dynamicaly drawing a satisfactory number of shadows is not really possible using b3d commands alone. I only have partialy done a routine to build a volume with a minimum number of polys but this is for levels only - where the level and the light source is stationary. I don't think too many people would have a use for that. It (probably) IS possible to have fast stencil shadows in b3d but with help from a very good c++ coder who can make a number of DLLs that will work well with blitz3d.
Anyway, here is the code based on Tom's code, only now Depth-Fail (Carmacks reverse) technique is used. This way its very robust, and when you enter the volumes (in this example 2 cylinders) they dont disappear or get drawn incorrectly, as was the case with original Tom's code which used the Depth-Pass technique. I have included basic code to make shadow volumes in b3d so if you want to test around yourself, do it. It can be useful for very simple objects (if you get it to work like you want it).
EDIT: The first code i posted i forgot to include the ATI fix. This one should work, try it and tell me.
If youre interested in what problems i ran into, read on. If not, get the code from the box below and the needed userlibs from:
http://www.geocities.com/spirala7/dx7sshad.htm
The main problem is that commercial games which are written in c++ have a number of keywords and maths that are integral to the language. For example there is the D3DMATRIX which is used to take positions of vertices (directly using D3DVERTEXBUFFER... , CreateVertexBuffer() ) and simply extrude them by a vector, when the volume is capped as well. In Blitz however we have to use TFormPoint to even get the global position of a vertex, and then TFormVector to even stand a chance of checking where the edges are, or which triangles are facing the light source, etc. You COULD do it without TFormVector also perhaps, but the maths would be as slow as that command. I tried everything possible. It's not to blame blitz for not having this kind of thing, if it did it would be a considerably more complex language. Another thing which i find is pretty slow is creation of triangles. In C++ the vertexes are just copied and then "extruded" or projected, this includes creation of the triangles and is obviously faster.
In other words, Mark! Do something :D make us stencil shadows for blitz3d, you see how simple it is if you know c++? Yeah! COME ON!
Here's the code (should work on ATI?):
Anyway, here is the code based on Tom's code, only now Depth-Fail (Carmacks reverse) technique is used. This way its very robust, and when you enter the volumes (in this example 2 cylinders) they dont disappear or get drawn incorrectly, as was the case with original Tom's code which used the Depth-Pass technique. I have included basic code to make shadow volumes in b3d so if you want to test around yourself, do it. It can be useful for very simple objects (if you get it to work like you want it).
EDIT: The first code i posted i forgot to include the ATI fix. This one should work, try it and tell me.
If youre interested in what problems i ran into, read on. If not, get the code from the box below and the needed userlibs from:
http://www.geocities.com/spirala7/dx7sshad.htm
The main problem is that commercial games which are written in c++ have a number of keywords and maths that are integral to the language. For example there is the D3DMATRIX which is used to take positions of vertices (directly using D3DVERTEXBUFFER... , CreateVertexBuffer() ) and simply extrude them by a vector, when the volume is capped as well. In Blitz however we have to use TFormPoint to even get the global position of a vertex, and then TFormVector to even stand a chance of checking where the edges are, or which triangles are facing the light source, etc. You COULD do it without TFormVector also perhaps, but the maths would be as slow as that command. I tried everything possible. It's not to blame blitz for not having this kind of thing, if it did it would be a considerably more complex language. Another thing which i find is pretty slow is creation of triangles. In C++ the vertexes are just copied and then "extruded" or projected, this includes creation of the triangles and is obviously faster.
In other words, Mark! Do something :D make us stencil shadows for blitz3d, you see how simple it is if you know c++? Yeah! COME ON!
Here's the code (should work on ATI?):
;This is a demo to show implementation of "Carmacks Reverse" way of drawing ;stencil shadows. It is also known as the Depth-Fail or the Z-Fail technique. ; ; Lenn , 21.01.2005 ;D3DCULL Const D3DCULL_NONE = 1 Const D3DCULL_CW = 2 Const D3DCULL_CCW = 3 ;D3DSHADE_MODE Const D3DSHADE_FLAT = 1 Const D3DSHADE_GOURAUD = 2 Const D3DSHADE_PHONG = 3 ;D3DFILL_MODE Const D3DFILL_POINT = 1 Const D3DFILL_WIREFRAME = 2 Const D3DFILL_SOLID = 3 ;D3DSTENCILOP Const D3DSTENCILOP_KEEP = 1 Const D3DSTENCILOP_ZERO = 2 Const D3DSTENCILOP_REPLACE = 3 Const D3DSTENCILOP_INCRSAT = 4 Const D3DSTENCILOP_DECRSAT = 5 Const D3DSTENCILOP_INVERT = 6 Const D3DSTENCILOP_INCR = 7 Const D3DSTENCILOP_DECR = 8 Const D3DSTENCILOP_FORCE_DWORD = $7fffffff ;force 32-bit size enum ;D3DCMPFUNC Const D3DCMP_NEVER = 1 Const D3DCMP_LESS = 2 Const D3DCMP_EQUAL = 3 Const D3DCMP_LESSEQUAL = 4 Const D3DCMP_GREATER = 5 Const D3DCMP_NOTEQUAL = 6 Const D3DCMP_GREATEREQUAL = 7 Const D3DCMP_ALWAYS = 8 Const D3DCMP_FORCE_DWORD = $7fffffff ;force 32-bit size enum ;D3DBLEND Const D3DBLEND_ZERO = 1 Const D3DBLEND_ONE = 2 Const D3DBLEND_SRCCOLOR = 3 Const D3DBLEND_INVSRCCOLOR = 4 Const D3DBLEND_SRCALPHA = 5 Const D3DBLEND_INVSRCALPHA = 6 Const D3DBLEND_DESTALPHA = 7 Const D3DBLEND_INVDESTALPHA = 8 Const D3DBLEND_DESTCOLOR = 9 Const D3DBLEND_INVDESTCOLOR = 10 Const D3DBLEND_SRCALPHASAT = 11 Const D3DBLEND_BOTHSRCALPHA = 12 Const D3DBLEND_BOTHINVSRCALPHA = 13 Const D3DBLEND_FORCE_DWORD = $7fffffff ;force 32-bit size enum Const D3DRS_ANTIALIAS = 2 ; D3DANTIALIASMODE */ Const D3DRS_TEXTUREPERSPECTIVE = 4 ; True For perspective correction */ Const D3DRS_ZENABLE = 7 ; D3DZBUFFERTYPE (Or True/False For legacy) */ Const D3DRS_FILLMODE = 8 ; D3DFILL_MODE */ Const D3DRS_SHADEMODE = 9 ; D3DFILL_MODE */ Const D3DRS_LINEPATTERN = 10 ; D3DLINEPATTERN */ Const D3DRS_ZWRITEENABLE = 14 ; True To enable z writes */ Const D3DRS_ALPHATESTENABLE = 15 ; True To enable alpha tests */ Const D3DRS_LASTPIXEL = 16 ; True For Last-pixel on lines */ Const D3DRS_SRCBLEND = 19 ; D3DBLEND */ Const D3DRS_DESTBLEND = 20 ; D3DBLEND */ Const D3DRS_CULLMODE = 22 ; D3DCULL */ Const D3DRS_ZFUNC = 23 ; D3DCMPFUNC */ Const D3DRS_ALPHAREF = 24 ; D3DFIXED (long) */ Const D3DRS_ALPHAFUNC = 25 ; D3DCMPFUNC */ Const D3DRS_DITHERENABLE = 26 ; True To enable dithering */ Const D3DRS_ALPHABLENDENABLE = 27 ; True To enable alpha blending */ Const D3DRS_FOGENABLE = 28 ; True To enable fog blending */ Const D3DRS_SPECULARENABLE = 29 ; True To enable specular */ Const D3DRS_ZVISIBLE = 30 ; True To enable z checking */ Const D3DRS_STIPPLEDALPHA = 33 ; True To enable stippled alpha (RGB device only) */ Const D3DRS_FOGCOLOR = 34 ; D3DCOLOR */ INT (((a) << 24) Or ((r) << 16) Or ((g) << 8) Or (b))) Const D3DRS_FOGTABLEMODE = 35 ; D3DFOGMODE */ Const D3DRS_FOGSTART = 36 ; Float Fog start (For both vertex And pixel fog) Const D3DRS_FOGEND = 37 ; Float Fog End */ Const D3DRS_FOGDENSITY = 38 ; Fog density */ Const D3DRS_EDGEANTIALIAS = 40 ; True To enable edge antialiasing */ Const D3DRS_COLORKEYENABLE = 41 ; True To enable source colorkeyed textures */ Const D3DRS_ZBIAS = 47 ; LONG Z bias */ Const D3DRS_RANGEFOGENABLE = 48 ; Enables range-based fog */ ; *** STENCIL OPS *** Const D3DRS_STENCILENABLE = 52 ; BOOL enable/disable stenciling Const D3DRS_STENCILFAIL = 53 ; D3DSTENCILOP To do If stencil test fails Const D3DRS_STENCILZFAIL = 54 ; D3DSTENCILOP To do If stencil test passes And Z test fails Const D3DRS_STENCILPASS = 55 ; D3DSTENCILOP To do If both stencil And Z tests pass */ Const D3DRS_STENCILFUNC = 56 ; D3DCMPFUNC fn. Stencil Test passes If ((ref & mask) stencilfn (stencil & mask)) is True */ Const D3DRS_STENCILREF = 57 ; INT Reference value used in stencil test */ Const D3DRS_STENCILMASK = 58 ; Mask value used in stencil test e.g (0xffffffff) Const D3DRS_STENCILWRITEMASK = 59 ; Write mask applied To values written To stencil buffer e.g (0xffffffff) Const D3DRS_TEXTUREFACTOR = 60 ; D3DCOLOR used For multi-texture blend */ Const D3DCLEAR_TARGET = $00000001 ;Clear target surface Const D3DCLEAR_ZBUFFER = $00000002 ;Clear target z buffer Const D3DCLEAR_STENCIL = $00000004 ;Clear Stencil ;ERRORS Const D3DERR_ZBUFFER_NOTPRESENT = 2070 Const D3DERR_STENCILBUFFER_NOTPRESENT = 2071 Const D3DERR_VIEWPORTHASNODEVICE = 774 Const DDERR_INVALIDOBJECT = 130 Const DDERR_INVALIDPARAMS = $80070057 Type BBRect Field x1% Field lX1% Field y1% Field lY1% Field x2% Field lX2% Field y2% Field lY2% End Type Graphics3D 1027,768,32,2 Direct3D7=SystemProperty$("Direct3D7") Direct3DDevice7=SystemProperty$("Direct3DDevice7") DirectDraw7=SystemProperty$("DirectDraw7") DirectInput7=SystemProperty$("DirectInput7") AppHWND=SystemProperty$("AppHWND") AppHINSTANCE=SystemProperty$("AppHINSTANCE") cam=CreateCamera() piv=CreatePivot() PositionEntity cam,-5,3,-5 PointEntity cam,piv CameraRange cam,.1,50 light=CreateLight() RotateEntity light,60,30,0 tex=CreateTexture(512,512) ScaleTexture tex,.2,.5 SetBuffer TextureBuffer(tex) Color 100,100,100 Rect 0,0,512,512 Color 200,200,200 Rect 8,8,496,496 Color 255,255,255 SetBuffer BackBuffer() cyl1=CreateCylinder() EntityAlpha cyl1,.3 PositionEntity cyl1,2,0,0 ScaleEntity cyl1,3,3,3 cyl2=CreateCylinder() EntityAlpha cyl2,.3 PositionEntity cyl2,2,0,5 ScaleEntity cyl2,3,3,3 cube=CreateCube() PositionEntity cube,0,0,0 ScaleEntity cube,5,.2,10 EntityTexture cube,tex spa#=.3 shadowplane=CreateCube() ; squished Z cube infront of cam ScaleMesh shadowplane,10,10,.01 EntityParent shadowplane,cam,0 MoveEntity shadowplane,0,0,.2 EntityColor shadowplane,0,0,0 EntityAlpha shadowplane,spa# ;Shadow plane is the object infront of the screen which is drawn where ;the stencil shadow should be drawn (on certain pixels). It means that ;where the shadow volume and scenery intersect, that is where the plane ;will be drawn. Because the color is black, and alpha is spa# its darkness ;will depend on spa#. You can put any other object in its place... sv=1 ; AREA TO CLEAR r.BBRect=New BBRect r\lX1=0 r\lY1=0 ;r\lX2=GraphicsWidth()/2 ; half the screen (used to show what happens if only half the screen is cleared) r\lX2=GraphicsWidth() r\lY2=GraphicsHeight() CameraClsMode cam,False,False While Not KeyHit(1) .start PositionEntity cyl1,(MouseX()-(GraphicsWidth()*.5)) * .01,-(MouseY()-(GraphicsWidth()*.5)) * .02,0 PositionEntity cyl2,(MouseX()-(GraphicsWidth()*.5)) * .01,-(MouseY()-(GraphicsWidth()*.5)) * .02,2 t=MilliSecs() ;-----------------------------------------------------------------------------------render start HideEntity shadowplane;1-hide plane ;I =think= that it might be better to hide the volumes here HideEntity cyl1 ;1a-hide volumes HideEntity cyl2 ShowEntity cube ;2-show scene SetRenderState(Direct3DDevice7, D3DRS_ZWRITEENABLE, True) SetRenderState(Direct3DDevice7, D3DRS_STENCILENABLE, False) SetRenderState(Direct3DDevice7, D3DRS_ALPHABLENDENABLE, False) ; DeviceClear2(Direct3DDevice7, r, D3DCLEAR_TARGET Or D3DCLEAR_ZBUFFER Or D3DCLEAR_STENCIL,$00008080,1.0,0) ; RenderWorld DeviceClear(Direct3DDevice7, 7,$00008080,1,0) CameraClsMode cam,True,True RenderWorld CameraClsMode cam,False,False ShowEntity cyl1 ;3-show volume ShowEntity cyl2 HideEntity cube ;4-hide scene ; *** RenderShadow() START *** -------- << name of the function as found in C++ DX7SDK examples SetRenderState(Direct3DDevice7, D3DRS_ZWRITEENABLE,False) SetRenderState(Direct3DDevice7, D3DRS_STENCILENABLE,True) SetRenderState(Direct3DDevice7, D3DRS_STENCILFUNC, D3DCMP_ALWAYS) SetRenderState(Direct3DDevice7, D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP) SetRenderState(Direct3DDevice7, D3DRS_STENCILPASS, D3DSTENCILOP_KEEP) SetRenderState(Direct3DDevice7, D3DRS_STENCILREF, 1) SetRenderState(Direct3DDevice7, D3DRS_STENCILMASK,$ffffffff) SetRenderState(Direct3DDevice7, D3DRS_STENCILWRITEMASK,$ffffffff) SetRenderState(Direct3DDevice7, D3DRS_STENCILZFAIL, D3DSTENCILOP_INCR) SetRenderState(Direct3DDevice7, D3DRS_ALPHABLENDENABLE, True) SetRenderState(Direct3DDevice7, D3DRS_SRCBLEND, D3DBLEND_ZERO) SetRenderState(Direct3DDevice7, D3DRS_DESTBLEND, D3DBLEND_ONE) RenderWorld SetRenderState(Direct3DDevice7, D3DRS_CULLMODE, D3DCULL_CW) RenderWorld SetRenderState(Direct3DDevice7, D3DRS_CULLMODE, D3DCULL_CCW) SetRenderState(Direct3DDevice7, D3DRS_STENCILZFAIL, D3DSTENCILOP_DECR) RenderWorld SetRenderState(Direct3DDevice7, D3DRS_ZWRITEENABLE, True ) SetRenderState(Direct3DDevice7, D3DRS_STENCILENABLE, False ) SetRenderState(Direct3DDevice7, D3DRS_ALPHABLENDENABLE, False ) ; *** RenderShadow() END *** -------- ; *** DrawShadow() START *** ----------- << name of the function as found in C++ DX7SDK examples SetRenderState(Direct3DDevice7, D3DRS_ZENABLE, False ) SetRenderState(Direct3DDevice7, D3DRS_STENCILENABLE, True ) SetRenderState(Direct3DDevice7, D3DRS_FOGENABLE, False) SetRenderState(Direct3DDevice7, D3DRS_ALPHABLENDENABLE, True ) SetRenderState(Direct3DDevice7, D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ) SetRenderState(Direct3DDevice7, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ) SetRenderState(Direct3DDevice7, D3DRS_STENCILREF, 1 ) SetRenderState(Direct3DDevice7, D3DRS_STENCILFUNC, D3DCMP_LESSEQUAL ) SetRenderState(Direct3DDevice7, D3DRS_STENCILPASS, D3DSTENCILOP_KEEP ) ShowEntity shadowplane ;5-show plane RenderWorld SetRenderState(Direct3DDevice7, D3DRS_ZENABLE, True ) SetRenderState(Direct3DDevice7, D3DRS_STENCILENABLE, False ) SetRenderState(Direct3DDevice7, D3DRS_ALPHABLENDENABLE, True ) ; *** DrawShadow() END *** ----------- HideEntity shadowplane ;6-hide plane RenderWorld;--------------------------------------------------------------------------render end SetRenderState(Direct3DDevice7, D3DRS_ALPHABLENDENABLE, False ) Text 0,0,"Move cylinders (volumes) with mouse/"+" MS: "+(MilliSecs() - t) Text 0,10,"Shadow Alpha: "+spa+ " LMB/RMB to change value, SPACE to toggle visibility" Text 0,20, "A/Z to move back/forward. Enter the volumes and see that it works." If MouseDown(1) spa=spa-.01 If spa<0 spa=0 EntityAlpha shadowplane,spa Else If MouseDown(2) spa=spa+.01 If spa>1.0 spa=1.0 EntityAlpha shadowplane,spa End If If KeyHit(30) MoveEntity cam, 0,0,0.1 End If If KeyHit(44) MoveEntity cam, 0,0,-0.1 End If If KeyHit(57) sv=Not sv If sv EntityAlpha cyl1,.3 EntityAlpha cyl2,.3 Else EntityAlpha cyl1,0.001 EntityAlpha cyl2,0.001 End If End If If KeyHit(24) tra=Not tra Flip Wend End Type volumes Field mesh End Type Function CreateVolume(entity, light) ;entity - the entity that casts the shadow ;light - the light entity from which the light ; ;This function is included in case somebody wants to build shadow volumes and try stuff out. ;This is fairly slow because it extrudes and caps every triangle on a mesh. This means ;that 6 times more triangles need to be made for each one on the mesh. ;(I tried faster methods but trust me its not easy) ;It may be useful only for very basic geometry. lx#=EntityX(light,True) ly#=EntityY(light,True) lz#=EntityZ(light,True) a_vol.volumes=New volumes a_vol\mesh=CreateMesh() ;EntityBlend a_vol\mesh,2 ;EntityFX a_vol\mesh,1 EntityColor a_vol\mesh,0,0,0 EntityAlpha a_vol\mesh,0.001 volsurf=CreateSurface(a_vol\mesh) numsurf=CountSurfaces(entity) vpos=CreatePivot() vposend=CreatePivot() VolumeDepth#=1000 ;For surfcount=1 To numsurf-1 surfcount=1 While surfcount <= numsurf cursurface=GetSurface(entity, surfcount) maxtricount=CountTriangles (cursurface) ;For tricount=0 To tricount-1 While tricount < maxtricount v1x#=VertexX(cursurface, TriangleVertex(cursurface, tricount, 0)) v1y#=VertexY(cursurface, TriangleVertex(cursurface, tricount, 0)) v1z#=VertexZ(cursurface, TriangleVertex(cursurface, tricount, 0)) TFormPoint v1x#,v1y#,v1z#, entity, 0 v1x#=TFormedX() v1y#=TFormedY() v1z#=TFormedZ() v2x#=VertexX(cursurface, TriangleVertex(cursurface, tricount, 1)) v2y#=VertexY(cursurface, TriangleVertex(cursurface, tricount, 1)) v2z#=VertexZ(cursurface, TriangleVertex(cursurface, tricount, 1)) TFormPoint v2x#,v2y#,v2z#, entity, 0 v2x#=TFormedX() v2y#=TFormedY() v2z#=TFormedZ() v3x#=VertexX(cursurface, TriangleVertex(cursurface, tricount, 2)) v3y#=VertexY(cursurface, TriangleVertex(cursurface, tricount, 2)) v3z#=VertexZ(cursurface, TriangleVertex(cursurface, tricount, 2)) TFormPoint v3x#,v3y#,v3z#, entity, 0 v3x#=TFormedX() v3y#=TFormedY() v3z#=TFormedZ() PositionEntity vpos,v1x#,v1y#,v1z# PositionEntity vposend, lx#,ly#,lz#,1 PointEntity vposend, vpos MoveEntity vposend, 0,0,VolumeDepth# v1xe#=EntityX(vposend, True) v1ye#=EntityY(vposend, True) v1ze#=EntityZ(vposend, True) PositionEntity vpos,v2x#,v2y#,v2z# PositionEntity vposend, lx#,ly#,lz#,1 PointEntity vposend, vpos MoveEntity vposend, 0,0,VolumeDepth# v2xe#=EntityX(vposend, True) v2ye#=EntityY(vposend, True) v2ze#=EntityZ(vposend, True) PositionEntity vpos,v3x#,v3y#,v3z# PositionEntity vposend, lx#,ly#,lz#,1 PointEntity vposend, vpos MoveEntity vposend, 0,0,VolumeDepth# v3xe#=EntityX(vposend, True) v3ye#=EntityY(vposend, True) v3ze#=EntityZ(vposend, True) v1=AddVertex(volsurf,v1x#,v1y#,v1z#) v1e=AddVertex(volsurf,v1xe#,v1ye#,v1ze#) v2=AddVertex(volsurf,v2x#,v2y#,v2z#) v2e=AddVertex(volsurf,v2xe#,v2ye#,v2ze#) v3=AddVertex(volsurf,v3x#,v3y#,v3z#) v3e=AddVertex(volsurf,v3xe#,v3ye#,v3ze#) AddTriangle (volsurf, v1,v1e,v2) AddTriangle (volsurf, v2,v1e,v2e) AddTriangle (volsurf, v1,v3,v1e) AddTriangle (volsurf, v3,v3e,v1e) AddTriangle (volsurf, v3,v2,v3e) AddTriangle (volsurf, v2,v2e,v3e) AddTriangle (volsurf, v1,v2,v3) ;cap top AddTriangle (volsurf, v1e,v2e,v3e) ;cap bottom tricount=tricount+1 Wend ;Next surfcount=surfcount+1 Wend ;Next FreeEntity vpos FreeEntity vposend End Function Function HideVolumes() For a_vol.volumes=Each volumes HideEntity a_vol\mesh Next End Function Function ShowVolumes() For a_vol.volumes=Each volumes ShowEntity a_vol\mesh Next End Function Function DeleteVolumes() For a_vol.volumes=Each volumes FreeEntity a_vol\mesh Delete a_vol Next End Function
