looks very nice!
Code archives/3D Graphics - Effects/Dynamic Water
This code has been declared by its author to be Public Domain code.
Download source code
| These functions can easily be used to have your characters footsteps cause ripples in the water. This Demo shows a boucning ball between two liquid surfaces. Not fully finished yet, but should get you going. Hit the left-mouse button to create random splashes... Heavily 'inspired' on previous samples by Reda Borchardt and Rob Cummings. Have fun,- Danny Sample screen shot: |
; fxWater Module by Danny van der Ark - dendanny@xs4all.nl ; ; (Heavily) inpired by samples by Reda Borchardt and Rob Cummings. ; use as you like - send me a sample would be cool. ; ; april '04 ; ; ; Usage: ; ; 1. Create a water surface using: fxWater_Create( name$, xsize, zsize, dampening#, parent ) ; This function returns the 'type-handle' to the water surface wich you will need later. ; ; 2. Obtain the entity handle (to apply color, texture, alpha, etc) using the function ; fxWater_get_entity( typehandle) -> typehandle is the value returned by fxWater_Create() ; ; 3. Then simply call 'fxWater_Update()' every frame during your main loop. ; ; ; 4. To create a splah in the water use the fxWater_Dimple() function. The function needs the ; type handle that was returned by 'fxWater_Create', the coordinates of the splash, the ; force/strength of the splash and the 'range' or size of the splash. ; ; fxWater_Dimple( hand, x,z, force#=1.0, range#=1.0) ; ; 5. Freeing stuff not done yet because I want to alter it to use memory banks in stead of ; a large static array to store the vertex data - wich should give a more efficient use ; of memory... ; ; You can create as many surfaces of any size as you wish. If you wish surfaces larger than 100x100 ; then adjust the fxwater_max_depth/width globals. If you want more than 5 surfaces at the same time ; adjust the fxwater_max accordingly. ; ; It is necesary To 'UpdateNormals' every frame to ensure the highlights are re-calculated every ; frame. Without this the ripples are there, but just not visible. This slows things down ; dramatically ofcourse. Any other ideas welcome.... ; ; He ho, enjoy! ; ; ooo ; (_) Danny v.d. Ark Global fxWATER_MAX = 5 ;max number of active surfaces/effects at one time Global fxWATER_NUM = 0 ;current number of active water effects. Global FXWATER_MAX_WIDTH = 100 ;max sub-division for water meshes Global FXWATER_MAX_DEPTH = 100 ;max sub-division for water meshes Global FXWATER_MAX_BANKS = fxWATER_MAX * 2 Global FXWATER_NUM_BANKS = 0 Global DEMO_WATER_WIDTH = 40 ;--> Adjust these for different plane sizes / mesh resolution Global DEMO_WATER_DEPTH = 40 ;reserve memory banks for vertice altitudes Dim fxWaterBank#(FXWATER_MAX_BANKS, FXWATER_MAX_WIDTH, FXWATER_MAX_DEPTH ) Type fxWaterWave Field id Field name$ Field active ;if not ripples then fx is not actiave Field parent ;parent entity (if any) Field entity ;water mesh Field surface ;water surface Field bank1 ;pointers to array for mem storage Field bank2 Field width ;width of plane / surface Field depth ;depth of plane / surface Field dampening# ;water dynamics End Type ;Demo options Global OPT_WIREFRAME = 0 Global OPT_REFRSH = 0 Global OPT_BALLFREEZE= 0 Global OPT_CEILING = 1 ;-------------------------------------------------------------------------------------------------- ;Graphics3D 640,480,32,1 Graphics3D 640,480,0,2 ; Camera + Light Global campivot = CreatePivot() Global camera = CreateCamera(campivot) PositionEntity camera, 0, 0, -35 PointEntity camera, campivot light = CreateLight(2) PositionEntity light,-60,0,100 LightColor light,240,240,210 PointEntity light, campivot light = CreateLight(2) PositionEntity light,50,0,-50 LightColor light,150,150,180 PointEntity light, campivot AmbientLight 40,40,40 ;load texture ;tex = LoadTexture ("water.tga",1+64) tex = create_noise_map() ;create water planes Global w1 = fxWater_Create( "floor", DEMO_WATER_WIDTH, DEMO_WATER_DEPTH, 0.025 ) water = fxWater_get_entity( w1 ) PositionEntity water, 0, -10, 0 EntityColor water, 30,50,200 EntityShininess water, 0.1 EntityTexture water,tex Global w2 = fxWater_Create( "ceiling", DEMO_WATER_WIDTH, DEMO_WATER_DEPTH, 0.025 ) water = fxWater_get_entity( w2 ) FlipMesh water ; flip it because the camera is underneath it PositionEntity water, 0, 10, 0 EntityColor water, 250,100,100 EntityColor water, 140,130,20 EntityShininess water, 0.1 EntityTexture water,tex ;force refresh fxWater_dimple( w1, 1,1, 0.001, 0.001) fxWater_dimple( w2, 1,1, 0.001, 0.001) ;set random ball direction/speed Global bx#= 0.2 Global by#= -1.75 Global bz#= -0.25 Global bmaxx# = DEMO_WATER_WIDTH * 0.5 ;half the width of the water plane Global bmaxy# = 10.0 Global bmaxz# = DEMO_WATER_DEPTH * 0.5 ;half the depth of the water plane ;create ball ball = CreateSphere(6) EntityColor ball, 0,0,0 EntityShininess ball, 1.0 EntityTexture ball, tex SeedRnd MilliSecs() ; Main Loop tstart = MilliSecs() + 1000 frame = 0 Global lastx# = 0 Global lasty# = 0 Global lastz# = 0 tim = CreateTimer(50) l$ = "WavyWaterFx by Danny van der Ark" While Not KeyHit(1) ;update bouncing ball update_ball( ball ) ;update fxWater fxWater_update() ;render UpdateWorld RenderWorld ;debug Color 000,000,000 Text 2,1, l$ Color 255,255,255 Text 2,0, l$ Color 100,100,100 Text 2,12, "based on samples from Reda Borchardt And Rob Cummings." Color 20,140,255 Text 2,454, "[ESC] to quit - [D] for fast but dirty refresh(" + OPT_REFRESH + ") - [W] To toggle WireFrame (" + OPT_WIREFRAME + ")" Text 2,466, "[P] To pause - [B] to pause/reset ball - [C] to toggle ceiling" Color 250,250,220 Text 10,220,"FPS " + fps Text 10,232,"TRIS " + TrisRendered() ;orbit camera ;TurnEntity campivot, 0, 0.25, 0 ;check fps frame = frame + 1 If MilliSecs() >= tstart Then fps = frame frame = 0 tstart = MilliSecs() + 1000 EndIf ;toggle ceiling [C] If KeyHit(46) Then OPT_CEILING = 1 - OPT_CEILING If OPT_CEILING Then water = fxWater_Get_Entity(w2) ShowEntity water Else water = fxWater_Get_Entity(w2) HideEntity water EndIf EndIf ;pause all [P] If KeyHit(25) Then FlushKeys While Not KeyHit(25) Wend EndIf ;pause ball [B] If KeyHit(48) Then OPT_BALLFREEZE = 1 - OPT_BALLFREEZE If OPT_BALLFREEZE Then PositionEntity ball, 0,0,0 bx# = 0 by# = 0 bz# = 0 Else bx#=Rnd#(-1,1) by#=Rnd#(-2,2) bz#=Rnd#(-1,1) EndIf EndIf ;wireframe [W] If KeyHit(17) Then OPT_WIREFRAME = 1 - OPT_WIREFRAME WireFrame OPT_WIREFRAME EndIf ;Dirty refresh [D] If KeyHit(32) Then OPT_REFRESH = 1 - OPT_REFRESH EndIf ;check refresh mode If OPT_REFRESH Then ;fast and dirty (pot noodle style) Flip False Else ;slow but clean Flip True WaitTimer(tim) EndIf If MouseHit(1) Then x = Rnd(1,DEMO_WATER_WIDTH) z = Rnd(1,DEMO_WATER_DEPTH) fxWater_Dimple( w1, x, z, 1, 5) EndIf Wend End ;-------------------------------------------------------------------------------------------------- Function update_ball( ent ) ;move the ball TranslateEntity ent, bx,by,bz DoDimple = False ;check left/right walls If EntityX#(ent) > bmaxx-2 Then bx# = bx# * -1 If EntityX#(ent) < -bmaxx+2 Then bx# = bx# * -1 ;check ceiling bounce If EntityY#(ent) > bmaxy-2 Then ;reverse vertical direction by# = by# * -1.0 ;create dimple fxWater_Dimple(w2, EntityX(ent)+bmaxx,EntityZ(ent)+bmaxz, -1.0, 5.0) ;slightly alter direction bx# = bx# + Rnd#(-0.25, 0.25) bz# = bz# + Rnd#(-0.25, 0.25) EndIf ;check floor bounce If EntityY#(ent) < -bmaxy+2 Then ;reverse vertical direction by# = by# * -1.0 ;create dimple fxWater_Dimple(w1, EntityX(ent)+bmaxx,EntityZ(ent)+bmaxz, 1.0, 5.0) ;slightly alter direction bx# = bx# + Rnd#(-0.25, 0.25) bz# = bz# + Rnd#(-0.25, 0.25) EndIf ;check front/back walls If EntityZ#(ent) > bmaxz-2 Then bz# = bz# * -1 If EntityZ#(ent) < -bmaxz+2 Then bz# = bz# * -1 End Function ;-------------------------------------------------------------------------------------------------- Function fxWater_Create( name$="", width=1, depth=1, damp#=0.01, parent=0 ) ;create new Wavy water effect plane w.fxWaterWave = New fxWaterWave w\id = 1 w\name$ = name$ w\active = True ;create rectangular grid mesh w\width = width w\depth = depth w\dampening#= 1 - damp# w\parent = parent w\entity = create_mesh_plane( w\width, w\depth, False, parent ) w\surface = GetSurface(w\entity,1) ;store handle for quick retrieval during collision NameEntity w\entity, Handle(w) ;reserve memory banks to hold vertex energy w\bank1 = fxWater_Create_Buffer() w\bank2 = fxWater_Create_Buffer() ;return mesh handle Return Handle(w) End Function ;-------------------------------------------------------------------------------------------------- Function fxWater_update() For w.fxWaterWave = Each fxWaterWave ;if the surface is perfectly flat then this value remains 0 dyna# = 0 ;process water For x = 1 To w\width-1 For z = 1 To w\depth-1 fxWaterBank#(w\bank2,x,z) = (fxWaterBank#(w\bank1,x-1,z) + fxWaterBank#(w\bank1,x+1,z) + fxWaterBank#(w\bank1,x,z+1) + fxWaterBank#(w\bank1,x,z-1)) / 2.1-fxWaterBank#(w\bank2,x,z) fxWaterBank#(w\bank2,x,z) = fxWaterBank#(w\bank2,x,z) * w\dampening# dyna# = dyna# + fxWaterBank#(w\bank2,x,z) Next Next ;Only deform patch if necesary If dyna# <> 0 Then ;PatchTransform k=0 For i = 0 To w\depth For j = 0 To w\width VertexCoords(w\surface,k,VertexX(w\surface,k),fxWaterBank#(w\bank2,j,i),VertexZ(w\surface,k)) k=k+1 Next Next EndIf ;should be optional - depending on type of texture (slows down seriously!) UpdateNormals w\entity ;SwapWaterBuffer tmp = w\bank1 w\bank1 = w\bank2 w\bank2 = tmp Next End Function ;-------------------------------------------------------------------------------------------------- Function fxWater_Dimple( hand, x,z, force#=1.0, range#=1.0) w.fxWaterWave = Object.fxWaterWave(hand) For xg = x - range# * 0.5 To x+range# * 0.5 For zg = z - range# * 0.5 To z+range# * 0.5 If xg> 0 And xg < w\width And zg>0 And zg<w\depth Then fxWaterBank#(w\bank2, xg,zg) = force# EndIf Next Next End Function ;-------------------------------------------------------------------------------------------------- Function fxWater_get_surface( hand ) w.fxWaterWave = Object.fxWaterWave( hand ) Return w\surface End Function ;-------------------------------------------------------------------------------------------------- Function fxWater_get_entity( hand ) w.fxWaterWave = Object.fxWaterWave( hand ) Return w\entity End Function ;-------------------------------------------------------------------------------------------------- Function fxWater_Create_Buffer() ; xsize=1, zsize=1 ) If FXWATER_NUM_BANKS >= FXWATER_MAX_BANKS Then RuntimeError "[fxWater::Create_buffer] Max amount of fxWater memory banks reached!" Else ;create a new memory bank and resize it to fit FXWATER_NUM_BANKS = FXWATER_NUM_BANKS + 1 ;NOTE: convert array into memory bank EndIf Return FXWATER_NUM_BANKS End Function ;-------------------------------------------------------------------------------------------------- Function fxWater_Free_Buffers( ) ;| Frees all buffers. Call as a part of when scene/level is removed from memory. ;NOTE: resize memory banks to 0 (once implemented). Return 0 End Function ;-------------------------------------------------------------------------------------------------- ;Creates a flat grid mesh Function create_mesh_plane(width=1,depth=1,doublesided=False,parent=0) tot = width + (depth*width) mix#= (width+depth) * 0.5 mesh=CreateMesh( parent ) surf=CreateSurface( mesh ) stx#=-.5 sty#=stx stp#=Float(1)/Float(mix#) y#=sty# For a=0 To depth x#=stx v#=a/Float(depth) For b=0 To width u#=b/Float(width) AddVertex(surf,x,0,y,u,v) x=x+stp Next y=y+stp Next For a=0 To depth-1 For b=0 To width-1 v0=a*(width+1)+b:v1=v0+1 v2=(a+1)*(width+1)+b+1:v3=v2-1 AddTriangle( surf,v0,v2,v1 ) AddTriangle( surf,v0,v3,v2 ) Next Next UpdateNormals mesh If doublesided=True Then EntityFX mesh,16 FitMesh mesh, -width*0.5, 0, -depth*0.5, width, 1, depth Return mesh End Function ;-------------------------------------------------------------------------------------------------- Function create_noise_map() ; creates a noise map to be used as a generic reflection map sq = 128 tex = CreateTexture(sq,sq,65,1) tbuf = TextureBuffer(tex) SetBuffer(tbuf) For x = 0 To sq-1 For y = 0 To sq-1 r = Rnd(100,120) g = Rnd(100,130) b = Rnd(190,240) Color r,g,b Rect x,y,1,1 Next Next SetBuffer(BackBuffer()) Return tex End Function ;-------------------------------------------------------------------------------------------------- ;[end of stuff] |
Comments
Yes, very good. You shoud be able to make it a bit faster by using that Calculate_normal routine SSwift put in the archives a little while ago. You will need to add a Local statement to his routine though as I think it uses some variables you use as globals. Comment out the bit where he normalises the normals as well, not required in this case. You may think of other ways to optimise it for use here.
Another option I suppose is to fake something that looks somewhat convincing but runs pretty fast. Not so easy to get something that looks convincing though as you can see from my rather poor effort :(
Another option I suppose is to fake something that looks somewhat convincing but runs pretty fast. Not so easy to get something that looks convincing though as you can see from my rather poor effort :(
;Only deform patch if necesary If Abs(dyna#) > 0.2 Then ;PatchTransform k=0 For i = 0 To w\depth For j = 0 To w\width h#=fxwaterbank(w\bank2,j,i) VertexCoords(w\surface,k,VertexX(w\surface,k),h,VertexZ(w\surface,k)) VertexNormal(w\surface,k,-0.8-h*0.25,h*0.1,0.2-h*0.25) k=k+1 Next Next EndIf
Thanks
I guess you could also try adding some noise into the mix. This uses Rnd() but I expect there are better ways.
VertexNormal(w\surface,k, -0.5-h*0.25+Rnd(0.1), 0.5+h*0.1, 0.2-h*0.25+Rnd(0.1))
Cheers David,
Yes I noticed I made a mistake in the part where it's "not" supposed to update the mesh when nothing is happening. I'll check out that 'vertex normal' stuff you're talking about, looks like good stuff. Also I want to have the 'vertex buffers' stored in variable-sized memory banks in steda of static (memory munching) arrays.
I'll reposted when I get there..
ta
Danny,-
Yes I noticed I made a mistake in the part where it's "not" supposed to update the mesh when nothing is happening. I'll check out that 'vertex normal' stuff you're talking about, looks like good stuff. Also I want to have the 'vertex buffers' stored in variable-sized memory banks in steda of static (memory munching) arrays.
I'll reposted when I get there..
ta
Danny,-
Very cool stuff.
Thanks a lot Danny and all the others.
yeah, and I'm gonna send you a sample, if I got something cool
Thanks a lot Danny and all the others.
yeah, and I'm gonna send you a sample, if I got something cool
That is amazing, welldone dude!
Now I have my new super-compuer, it actually runs very quick.
Does this work on animated meshes?
[edit]