Help with Swift Shadow system

Blitz3D Forums/Blitz3D Programming/Help with Swift Shadow system

banky(Posted 2004) [#1]
Hi all

I recently bought the swift shadow system and I have a problem with animated characters.

I tried to run the demo using the Mario animated mesh (from the castle demo of blitz). If I use LoadMesh the shadow works perfectly, buy if I use LoadAnimMesh the shadow doesn't appear...

Help, please... :)

Thank you in advance
-Banky


Gabriel(Posted 2004) [#2]
LoadAnimMesh attaches a structure to the model, and the first level of that structure is a null pivot, not your model. Find the name of the main model in your modeller ( or there is a B3d hierarchy viewer in the code archives ) and then get the handle of that with the FindChild() command. You can then set *this* as the shadowcaster.


banky(Posted 2004) [#3]
Thanks for the answer Sybixsus,
but my problem is that I can't use the name of the main model. To make the shadows work I have to set as a shadowcaster every part of the model (for example, right arm, left arm, left leg...) and this way the system is sooooo slow. (19 frames in a Athlon XP+ 2600 with a Geforce FX 5600)

Is this the only way to make it work with animated meshes?

Help, please... :)

Thank you in advance
-Banky


Gabriel(Posted 2004) [#4]
Generally you would use a single model which was animated with skeletal animation, thus only needing one shadow casting object. You could probably get around this problem by creating a cube which is just big enough to envelop the entire character, then parent the character to the cube and set the cube as the shadow casting object. If you need the player to be parented for something else, just parent it to the cube directly before calling Update_Shadows() and then immediately afer, parent it back to whatever else you want it parented to.


sswift(Posted 2004) [#5]
Banky:

Your post convinced me to take another look at the possiblity of adding the ability to cast shadows from compound objects made of multiple entities. I think I can do it in a manner which would work well enough for animated characters, but I need a demo to test with.

If you could send me a simple program with your segmented model animating onscreen with a lightsource and something set for it to cast shadows on, then that would be helpful. Set the shadow to cast from it's head or something and I will change it to the pivot myself. That way you can tell it's set up right before you send it.


sswift(Posted 2004) [#6]
I think I've fixed the problem... jsut need a demo from you to test with. :-)


Isaac P(Posted 2004) [#7]
sswift use the markio demo from the blitz3d folder and substitute the terrain for a mesh terrain, i think that is the model he is using anyways


puki(Posted 2004) [#8]
Mmm, I just bought "sswifty's" shadow system. Hope I get an updated one.


banky(Posted 2004) [#9]
Hi, Sswift

This is my piece of code... Just a modification of the demo code with an animated character and a gile[s] level.

I hope it will be useful to you...

Thanks for the answers,
- Banky

---


Type emitter
Field parent,rate#,time#
Field texture,blend
Field speed#,spread#,life#,size#,growth#,gravity#
End Type

Type particle
Field sprite
Field x#,y#,z#
Field sx#,sy#,sz#
Field life#,lifespan#,size#,growth#,gravity#
End Type

Type flare
Field sprite
Field texture
Field alpha#,talpha#
End Type

;#Region Program Setup


Global Terminate = False ; Set this to True anywhere to terminate the program.

; Setup the screen dimensions
Global ScreenWidth = 1024
Global ScreenHeight = 768
Global ScreenDepth = 16
Global ScreenMode = 2

; Create the screen.
Graphics3D ScreenWidth,ScreenHeight,ScreenDepth,ScreenMode
SetBuffer BackBuffer() ; Switch to Back Buffer for Double Buffered drawing.

; Setup the lighting...
AmbientLight 200,200,200

;#End Region

;#Region Declarations

; Setup the camera.
;Global MainCamera = CreateCamera()


;#End Region

Include "Swift Shadow System - 097.bb"
Include "ParseB3D.bb"

Global info1$="Swift Shadow System Demo"
Global info2$="Please note that this is a STRESS TEST!"
Global info3$="There are 48 pairs of casting/receiving objects in this scene."
Global info4$="And double that number when you add a second light!"
Include "start.bb"

; Stuff for the normal calculation function:
Dim Face_NX#(32768)
Dim Face_NY#(32768)
Dim Face_NZ#(32768)

Dim Vertex_ConnectedTris(32768)
Dim Vertex_TriList(32768, 16)

; Stuff for the HSV to RGB function.
Global HSV_R
Global HSV_G
Global HSV_B


.Main

; Removes all the default flags when you load a texture.
; Required because shadow maps cannot be mipmapped or the UV clamping will cause artifacts,
; and blitz by default has mipmapping enabled for all textures loaded.
ClearTextureFilters


; Load a texture for our fake shadows.
; Note the flags:
; Texture must have UV clamping enabled, and no mipmapping!
Global TEX_Fake_Shadow = LoadTexture("TEX-003.jpg", 16+32)


; Create a camera and move it back from 0,0,0
Camera = CreateCamera()
CameraRange Camera, 0.1, 65536
;PositionEntity Camera, 0, 200, -800

Camera_Pivot = CreatePivot()

EntityParent Camera, Camera_Pivot, True

; Set up the lights.
Gosub Setup_Lights

Gosub Create_Objects

Shadow_Resolution = 256

; Set the amount to nudge the shadows by to combat z fighting.
Shadow_Nudge# = 0.75

Animated_Casters(caster_2)

; Remember that we're drawing rendered shadows by default.
Cast_Rendered_Shadows = True

; Specify what objects should receive shadows.
; It's not a good idea to have too many objects receiving shadows.
Receive_Shadow(Receiver_1)

; Specify the lights which cast shadows, and set their range.
Cast_Light(Light_1, 2000)

Time_Old = MilliSecs()
Time_Of_Next_Event = Time_Old + 2000



While Not KeyDown(1)

Time_New = MilliSecs()
Time_Delta = Time_New - Time_Old
Time_Delta_Sec# = Float(Time_Delta)/1000.0
Time_Old = Time_New

If KeyHit(88) Then SaveScreenshot()


If KeyHit(2)

; Stop the objects from casting shadows.
; Note:
; You only need to call this once per object casting shadows,
; even if it casts shadows onto more than one reciever.
Delete_Shadow_Caster(Caster_2) ; Fighter

; If we're currenty casting rendered shadows...
If Cast_Rendered_Shadows = True

; Make the objects cast fake shadows.
; The value of the resolution parameter doesn't matter because you created the texture.

Cast_Textured_Shadow(caster_2, TEX_Fake_Shadow) ; Fighter


Cast_Rendered_Shadows = False

Else

; Make the objects cast rendered shadows.
; We don't need a texture parameter because the function will create a unique texture
; for each shadow itself.

Cast_Shadow(caster_2, Shadow_Resolution) ; Fighter

Cast_Rendered_Shadows = True

EndIf

EndIf


If KeyHit(3)

If Shadow_Resolution = 128
Shadow_Resolution = 256
Else
If Shadow_Resolution = 256
Shadow_Resolution = 512
Else
If Shadow_Resolution = 512
Shadow_Resolution = 128
EndIf
EndIf
EndIf


; If we're currenty casting rendered shadows...
If Cast_Rendered_Shadows = True

; Stop the objects from casting shadows.
; Note:
; You only need to call this once per object casting shadows,
; even if it casts shadows onto more than one reciever.
Delete_Shadow_Caster(caster_2) ; Fighter

; Make the objects cast rendered shadows.
; We don't need a texture parameter because the function will create a unique texture
; for each shadow itself.
Cast_Shadow(caster_2, Shadow_Resolution) ; Fighter
;
EndIf


EndIf


If KeyHit(4)

Delay 5000

EndIf


If KeyHit(5)

FormatC = Not FormatC

EndIf


If KeyHit(6)

Second_Light = Not Second_Light

If Second_Light = True

Cast_Light(Light_2, 2000)
ShowEntity Light_2

Else

Delete_Light_Caster(Light_2)
HideEntity Light_2

EndIf

EndIf


If KeyHit(57)
Show_Shadow_Map = Not Show_Shadow_Map
EndIf

;TurnEntity Caster_1, 1, 1, 1
TurnEntity Caster_2, 1, 0.5, 0
;TurnEntity Caster_3, 0, 1, 0.5

TurnEntity Light_Pivot, 0.25, 0.5, 1

Update_Shadows(camera)

UpdateWorld
If Show_Shadow_Map = False Then RenderWorld

Select Event

Case 0

Info$ = "-- Swift Shadow System - Created by Shawm C. Swift --"

Case 1

Info$ = "-- Press 1 to toggle rendered/fake shadows! --"

Case 2

Info$ = "-- Press 2 to toggle shadow resolution between 128, 256, and 512! --"

Case 3

Info$ = "-- Press 3 to crash your pc! --"

Case 4

Info$ = "-- Press 4 to FORMAT C: drive! ---"

Case 5

Info$ = "-- Press 5 to kill your framerate... I mean add a second light to the scene. --"

End Select

Text GraphicsWidth()/2, 16*1, Info$, True, False

If (Time_Delta > 0)
Frames_Per_Second = Int(Floor(1000.0/Float(Time_Delta)))
Text GraphicsWidth()/2, 16*2, "FPS: " + Str$(Frames_Per_Second), True, False
EndIf

Select (Event Mod 3)

Case 0

Text GraphicsWidth()/2, 16*3, "Shadow Resolution: " + Str$(Shadow_Resolution), True, False

Case 1

Text GraphicsWidth()/2, 16*3, "Shadow Polys: " + Str$(Shadow_Poly_Count), True, False

Case 2

Text GraphicsWidth()/2, 16*3, "Scene Polys: " + Str$(TrisRendered()), True, False

End Select


If FormatC = True
Text GraphicsWidth()/2, GraphicsHeight()/2, "Formatting C:\ ... Please wait.", True, True
EndIf

Flip False

Wend

End

; -------------------------------------------------------------------------------------------------------------------
.Setup_Lights

; Tell the shadow system what the ambient light level is. (Blitz doesn't have a command to retreive that info.)
Shadow_Ambient_Light_R = 80
Shadow_Ambient_Light_G = 80
Shadow_Ambient_Light_B = 128

AmbientLight Shadow_Ambient_Light_R, Shadow_Ambient_Light_G, Shadow_Ambient_Light_B

; Create three point light sources.
Light_1 = CreateLight(2)
Light_2 = CreateLight(2)

; Set the color of the lights.
LightColor Light_1, 255, 255, 240
LightColor Light_2, 255, 255, 240

; Set the radius of the lights.
LightRange Light_1, 2000
LightRange Light_2, 2000

; Create a pivot to rotate the light sources around.
Light_Pivot = CreatePivot()
PositionEntity Light_Pivot, 0, 800, 0

; Attach the light sources to the pivot.
EntityParent Light_1, Light_Pivot
EntityParent Light_2, Light_Pivot

; Position the light sources around the pivot.
PositionEntity Light_1, 400, 0, 0
PositionEntity Light_2, 0, 0, 400

; Create entities to show the positions of the light sources in the world.
TEX_04 = LoadTexture("TEX-004.bmp", 8)
Light_Sprite_1 = CreateSprite(Light_1)
ScaleSprite Light_Sprite_1, 100, 100
EntityFX Light_Sprite_1, 1
EntityBlend Light_Sprite_1, 3
EntityTexture Light_Sprite_1, TEX_04

Light_Sprite_2 = CopyEntity(Light_1, Light_2)
HideEntity Light_2

Return


; -------------------------------------------------------------------------------------------------------------------
; This subroutine creates and textures the meshes we will use to cast and recieve the shadows.
; -------------------------------------------------------------------------------------------------------------------
.Create_Objects

; We have to set the mipmap flag manually because we turned that off so we could create
; shadow textures without mipmapping.
TEX_01 = LoadTexture("TEX-001.jpg", 8)
TEX_02 = LoadTexture("TEX-002.jpg", 8)
TEX_05 = LoadTexture("TEX-005.jpg", 8)

Receiver_1 = LoadMesh("level\romanbath.b3d")
ParseB3D(receiver_1)
Calculate_Normals(Receiver_1)

; Make pillars
Temp1 = CreateCylinder(12, False)
ScaleMesh Temp1, 40, 100, 40
UpdateNormals(Temp1)

Temp2 = CreateCube()
ScaleMesh Temp2, 50, 10, 50
Calculate_Normals(Temp2)
PositionMesh Temp2, 0, 100, 0

Temp3 = CreateSphere(8)
ScaleMesh Temp3, 40, 40, 40
UpdateNormals(Temp3)
PositionMesh Temp3, 0, 150, 0

Caster_2 = LoadAnimMesh("mario.3ds")

Animate CASTER_2, 1, 1
PositionEntity Caster_2, 0, 7, 50, True

Return



; -------------------------------------------------------------------------------------------------------------------
; This function adds a bit of noise to a mesh so that it's more interesting.
; -------------------------------------------------------------------------------------------------------------------
Function Randomize_Mesh(ThisMesh)

Offset = 25

; Loop through all surfaces of the mesh.
Surfaces = CountSurfaces(ThisMesh)
For LOOP_Surface = 1 To Surfaces

Surface_Handle = GetSurface(ThisMesh, LOOP_Surface)

; Loop through all triangles in this surface of the mesh.
Verts = CountVertices(Surface_Handle)
For LOOP_Verts = 0 To Verts-1

Vx# = VertexX#(Surface_Handle, LOOP_Verts)
Vy# = VertexY#(Surface_Handle, LOOP_Verts)
Vz# = VertexZ#(Surface_Handle, LOOP_Verts)

VertexCoords Surface_Handle, LOOP_Verts, Vx#, Vy#+Rnd(-Offset,Offset), Vz#

Next

Next

End Function


; -------------------------------------------------------------------------------------------------------------------
; This function takes a screenshot of whatever is on the screen and saves it in the current directory.
; -------------------------------------------------------------------------------------------------------------------
Function SaveScreenshot()

iFileNumber% = 0
Repeat
iFileNumber = iFileNumber + 1
sFileName$ = "Screenshot" + String$("0", 3 - Len(Str$(iFileNumber))) + iFileNumber + ".bmp"
Until Not(FileType(sFileName$))

SaveBuffer FrontBuffer(), sFileName$

End Function


; -------------------------------------------------------------------------------------------------------------------
; This function calculates and sets the normals for a mesh.
;
; This is diffrent from Blitz's function in that it does not smooth across edges which do not share vertices.
; In my opinion, that is the best way to calculate the normals for a mesh. Blitz on the other hand assumes that
; vertices which lie at the same point in space should be smoothed across. This works well for
;
; Should probably update this so that it can recursively loop through all of an entities children as well.
; -------------------------------------------------------------------------------------------------------------------
Function Calculate_Normals(ThisMesh)

; Loop through all surfaces of the mesh.
Surfaces = CountSurfaces(ThisMesh)
For LOOP_Surface = 1 To Surfaces

Surface_Handle = GetSurface(ThisMesh, LOOP_Surface)

; Reset the number of connected polygons for each vertex.
For LoopV = 0 To 32767
Vertex_ConnectedTris(LoopV) = 0
Next

; Loop through all triangles in this surface of the mesh.
Tris = CountTriangles(Surface_Handle)
For LOOP_Tris = 0 To Tris-1

; Get the vertices that make up this triangle.
Vertex_0 = TriangleVertex(Surface_Handle, LOOP_Tris, 0)
Vertex_1 = TriangleVertex(Surface_Handle, LOOP_Tris, 1)
Vertex_2 = TriangleVertex(Surface_Handle, LOOP_Tris, 2)

; Adjust the number of triangles each vertex is connected to and
; store this triangle in each vertex's list of triangles it is connected to.
ConnectedTris = Vertex_ConnectedTris(Vertex_0)
Vertex_TriList(Vertex_0, ConnectedTris) = LOOP_Tris
Vertex_ConnectedTris(Vertex_0) = ConnectedTris + 1

ConnectedTris = Vertex_ConnectedTris(Vertex_1)
Vertex_TriList(Vertex_1, ConnectedTris) = LOOP_Tris
Vertex_ConnectedTris(Vertex_1) = ConnectedTris + 1

ConnectedTris = Vertex_ConnectedTris(Vertex_2)
Vertex_TriList(Vertex_2, ConnectedTris) = LOOP_Tris
Vertex_ConnectedTris(Vertex_2) = ConnectedTris + 1

; Calculate the normal for this face.

; Get the corners of this face:
Ax# = VertexX#(Surface_Handle, Vertex_0)
Ay# = VertexY#(Surface_Handle, Vertex_0)
Az# = VertexZ#(Surface_Handle, Vertex_0)

Bx# = VertexX#(Surface_Handle, Vertex_1)
By# = VertexY#(Surface_Handle, Vertex_1)
Bz# = VertexZ#(Surface_Handle, Vertex_1)

Cx# = VertexX#(Surface_Handle, Vertex_2)
Cy# = VertexY#(Surface_Handle, Vertex_2)
Cz# = VertexZ#(Surface_Handle, Vertex_2)

; Triangle 1
; Get the vectors for two edges of the triangle.
Px# = Ax#-Bx#
Py# = Ay#-By#
Pz# = Az#-Bz#

Qx# = Bx#-Cx#
Qy# = By#-Cy#
Qz# = Bz#-Cz#

; Compute their cross product.
Nx# = Py#*Qz# - Pz#*Qy#
Ny# = Pz#*Qx# - Px#*Qz#
Nz# = Px#*Qy# - Py#*Qx#

; Store the face normal.
Face_NX#(LOOP_Tris) = Nx#
Face_NY#(LOOP_Tris) = Ny#
Face_NZ#(LOOP_Tris) = Nz#

Next

; Now that all the face normals for this surface have been calculated, calculate the vertex normals.
Vertices = CountVertices(Surface_Handle)
For LOOP_Vertices = 0 To Vertices-1

; Reset this normal.
Nx# = 0
Ny# = 0
Nz# = 0

; Add the normals of all polygons which are connected to this vertex.
Polys = Vertex_ConnectedTris(LOOP_Vertices)

For LOOP_Polys = 0 To Polys-1

ThisPoly = Vertex_TriList(LOOP_Vertices, LOOP_Polys)

Nx# = Nx# + Face_NX#(ThisPoly)
Ny# = Ny# + Face_NY#(ThisPoly)
Nz# = Nz# + Face_NZ#(ThisPoly)

Next

; Normalize the new vertex normal.
; (Normalizing is scaling the vertex normal down so that it's length = 1)

Nl# = Sqr(Nx#^2 + Ny#^2 + Nz#^2)

; Avoid a divide by zero error if by some freak accident, the vectors add up to 0.
; If Nl# = 0 Then Nl# = 0.1

Nx# = Nx# / Nl#
Ny# = Ny# / Nl#
Nz# = Nz# / Nl#

; Set the vertex normal.

VertexNormal Surface_Handle, LOOP_Vertices, Nx#, Ny#, Nz#
;VertexColor Surface_Handle, LOOP_Vertices, polys*127, polys*127, polys*127

Next

Next

End Function


; -------------------------------------------------------------------------------------------------------------------
; This function takes a color in HSV format and returns a color in RGB format.
; -------------------------------------------------------------------------------------------------------------------
Function HSV_to_RGB(h#, s#, v#)

If s = 0 Then

r# = g# = b# = v ;grey

Else

h = h / 60
i = Floor(h)
f# = h - i
p# = v * (1 - s)
q# = v * (1 - s * f)
t# = v * (1 - s * (1 - f))

Select i

Case 0
r# = v
g# = t
b# = p

Case 1
r# = q
g# = v
b# = p

Case 2
r# = p
g# = v
b# = t

Case 3
r# = p
g# = q
b# = v

Case 4
r# = t
g# = p
b# = v

Default
r# = v
g# = p
b# = q

End Select

EndIf

HSV_R = r# * 255
HSV_G = g# * 255
HSV_B = b# * 255

End Function

Function Animated_Casters(caster_2)

Shadow_Resolution = 256

If CountChildren(Caster_2) > 0
For childcount = 1 To CountChildren(Caster_2)
child = GetChild(caster_2, childcount)

Animated_Casters(child)
Next
Else
Cast_Shadow(caster_2, Shadow_Resolution) ; Fighter

EndIf


End Function


sswift(Posted 2004) [#10]
I need the level and the character. I think the character is markio? Email them to me. My email address is in my profile.


sswift(Posted 2004) [#11]
Just so those following the thread know,

The problem with casting shadows from groups of entities parented to one another is now fixed. You can now load an animated mesh comprised of a group of entities that are connected to one another, and have them cast a single shadow.


MoonShadow(Posted 2010) [#12]
Can you post a PICTURE to see how it looks?


LineOf7s(Posted 2010) [#13]
6 year old thread, dude.


Kryzon(Posted 2010) [#14]
Heh my thoughts exactly LineOf7s....

PS: that "dude" was priceless.