'dynamic mesh deformation' code?

Blitz3D Forums/Blitz3D Programming/'dynamic mesh deformation' code?

hello i am dumb, or just lazy, take your pick. (its dumb and lazy) anyway, i have a few questions and thought i'd ask rather than search.

if i load an .x model with multiple meshes, what command do i use to access each mesh??
i want to load the vertices data to an array, for animating. i'm too tired atm to find the stuff in the manual.

actually, if anyone wants to write the code piece for me i will include their name/nickname in the game credits, the game is a remake (free) of chaos: battle of the wizards. the code is a function to animate a single-surface mesh, as the subject suggests. it would go something like: animatemymodel(singlemesh,animatearray,frame)

Use FindChild.
http://www.blitzbasic.com/b3ddocs/command.php?name=FindChild&ref=3d_cat

OR this is one way:
http://www.blitzbasic.com/codearcs/codearcs.php?code=796

this is another:
http://www.blitzbasic.com/codearcs/codearcs.php?code=787

Just to let you know - you're going to run into problems.

Namely VertexCoords changes must be stacked in order to work in realtime. Okay, I'll explain it better.

Think of the VertexCoords commands, as a command which copies the model out to a separate part of memory, applies any changes to vertex positions, then copies it back as soon as ANYTHING else in the scene is worked on. Not even necessarily rendered, just worked on.

Now - the actual mesh deformation, when it's "copied out" is very quick - and changing one vertex or 200 vertices, will take approximately the same amount of time. What chews up the processor is "copying" the mesh out and back again. (Nb: this isn't actually what it's doing, but it's a good analogy to understand how important it is to stack vertexcoord commands).

Say, in a single frame, you went from model to model, changing vertices in each of them, then updating their position, etc - it would create an unusably slow 5fps. You need to reflect ALL changes to vertex positions in a single instance, otherwise your performance is going to crawl.

IE - plan out your system carefully. What you want to do, is first of all find all meshes that need animating that are in the scene, and dump all the results into an array. Find the next frames in each of them, and what NEEDs to be animated in each of them, and dump that in an array. Find out what their new positions will be, if changed, etc, and dump that in an array. THEN, find out if any models on-screen are going to be in the same animated frame at any time and cull all the duplicates, opting for copy entity (or something similar) instead, and dump that in an array.

Next, assign all the meshes that are going to be edited, with handes. AnimatedMesh(1), AnimatedMesh(2), AnimatedSurface(1), AnimatedSurface(2), etc. You want to get all the mesh and surfaces allocated BEFORE starting to use VertexCoords, because using any other 3D command in the middle of VertexCoord changes, causes the mesh to be "copied" back - the slowdown in the process. Then, without any LINEPICKing or VIEWPORTing or anything else, run through the arrays and apply all changes to all meshes on screen all at once.

Copy any entities that need to be copied, and finally, render the scene.

Even this may be too slow. You'll probably have to find ways to "cheat" the updates in between every few frames. Which is why we generally use pre-compiled animated models. I was working on a similar system recently and gave up. Good luck! :)

+BlackD

Where did you get this info?

Would a loop like this would be really slow with what you're talking about?


		For To_X = 0 To Tile_Subdivision
			
						From_X = To_X
			
						Tile_Vertex = To_Z*(Tile_Subdivision+1) + To_X
						VX# = VertexX#(Tile_Surface, Tile_Vertex)
						VZ# = VertexZ#(Tile_Surface, Tile_Vertex)
						
						Neighbor_Vertex = From_Z*(Neighbor_Subdivision+1) + From_X
						VY# = VertexY#(Neighbor_Surface, Neighbor_Vertex)
													
						VertexCoords Tile_Surface, Tile_Vertex, VX#, VY#, VZ#								

					Next



If this is true, it kinda sucks that this information wasn't made available sooner. I thought Blitz was updating the vertex positions just before I called renderworld. But I guess it makes sense that it would do it before linepick. Maybe it should do it when you call uipdateworld, though that would update the collisions, so if this is the case, maybe it needs a specific function for forcing the update at certain times so you can just do it once per loop.

sswift.. here's an example demonstrating it. Most of the code you can ignore - the key parts are MoveVertsFast() and MoveVertsSlow(), the two last functions.

The program starts in fast mode, 1 toggles fast, and 2 toggles slow.

The difference is, in fast mode, it stores all vertex change info in an array, then applies it all at the end before the updateworld.
In slow mode, it applies the changes "while working".. in fact, the only command difference here is the CameraPick in each loop, then likewise updateworld's. In both examples it's doing at least 20 CameraPick's a frame - that's not slowing it down. It's just in slow mode - it does it between updating verts.

I get 60fps in fast mode, and 20fps in slow.

	size=15
	mode=1
	SeedRnd MilliSecs()
	Dim vertexinfo(20,3,4) ;remember 20 vertices, and their new positions
	Graphics3D 800,600,0,2
	WireFrame 1
	Global FrameTime,Period,timer,fpscount,fpstemp,fps,fx,fy
	Global camera=CreateCamera()
	SetBuffer BackBuffer()	
	light=CreateLight(1)
	Global map=CreateMesh()
	surface=CreateSurface(map)
	For x = 0 To size
		For y = 0 To size
			v0=AddVertex(surface,x,y,0)
			v1=AddVertex(surface,x,y+1,0)
			v2=AddVertex(surface,x+1,y,0)
			v3=AddVertex(surface,x+1,y+1,0)
			tri=AddTriangle(surface,v0,v1,v2)
			tri=AddTriangle(surface,v2,v1,v3)
			Next
		Next
	UpdateNormals map
	EntityPickMode map,2
	PositionEntity camera,8,10,2
	RotateEntity camera,70,0,0
	PositionEntity light,0,0,0
	RotateEntity light,25,-45,45	
	PositionEntity map,0,0,0
	RotateEntity map,90,0,0
	brush=CreateBrush(200,200,200)
	PaintEntity map,brush
	;mainloop
		While Not KeyHit(1)
		If KeyHit(2) Then mode=1
		If KeyHit(3) Then mode=2
		If mode=1 Then movevertsfast()
		If mode=2 Then movevertsslow()
		UpdateWorld
		RenderWorld
		Text 0,5,"Vertices in mesh:" +((size*size)*4)
		fps 0,20
		Flip
		Wend
		
	WaitKey()
	End
	
	Function fps(fx,fy)
		fpscount=fpscount+1
		If MilliSecs()<timer+1000
		Else
			fps=fpscount-fpstemp
			fpstemp=fpscount
			timer=MilliSecs()
		EndIf
		Text fx,fy,"FPS "+fps
		End Function
		
	Function movevertsslow()
		For i = 1 To 20
			x=Rand(0,799)
			y=Rand(0,599)
			surface=GetSurface(map,1)
			If CameraPick(camera,x,y) Then
				tri=PickedTriangle()
				move#=Rand(1,2)
				If move#=1 Then move#=0.1 Else move#=-0.1
				v0=TriangleVertex(surface,tri,0)
				v1=TriangleVertex(surface,tri,1)
				v2=TriangleVertex(surface,tri,2)
				x0=VertexX(surface,v0)
				x1=VertexX(surface,v1)
				x2=VertexX(surface,v2)
				y0=VertexY(surface,v0)
				y1=VertexY(surface,v1)
				y2=VertexY(surface,v2)
				z0=VertexZ(surface,v0)
				z1=VertexZ(surface,v1)
				z2=VertexZ(surface,v2)
				;apply changes between each camerapick
				VertexCoords (surface,v0,x0,y0+move#,z0)
				Else
				i=i-1  ;if it doesn't find one, knock the count back and try again
				End If
			Next
		End Function
		
	Function movevertsfast()
			For i = 1 To 20
			x=Rand(0,799)
			y=Rand(0,599)
			surface=GetSurface(map,1)
			If CameraPick(camera,x,y) Then
				tri=PickedTriangle()
				move#=Rand(1,2)
				If move#=1 Then move#=0.1 Else move#=-0.1
				vertexinfo(i,1,1)=TriangleVertex(surface,tri,0)
				vertexinfo(i,2,1)=TriangleVertex(surface,tri,1)
				vertexinfo(i,3,1)=TriangleVertex(surface,tri,2)
				vertexinfo(i,1,2)=VertexX(surface,vertexinfo(i,1,1))
				vertexinfo(i,2,2)=VertexX(surface,vertexinfo(i,2,1))
				vertexinfo(i,3,2)=VertexX(surface,vertexinfo(i,3,1))
				vertexinfo(i,1,3)=VertexY(surface,vertexinfo(i,1,1))
				vertexinfo(i,2,3)=VertexY(surface,vertexinfo(i,2,1))
				vertexinfo(i,3,3)=VertexY(surface,vertexinfo(i,3,1))
				vertexinfo(i,1,4)=VertexZ(surface,vertexinfo(i,1,1))
				vertexinfo(i,2,4)=VertexZ(surface,vertexinfo(i,2,1))
				vertexinfo(i,3,4)=VertexZ(surface,vertexinfo(i,3,1))
				Else
				i=i-1  ;if it doesn't find one, knock the count back and try again
				End If
			Next
		;apply changes at once
		For i = 1 To 20
			VertexCoords(surface,vertexinfo(i,1,1),vertexinfo(i,1,2),vertexinfo(i,1,3)+move#,vertexinfo(i,1,4))
			VertexCoords(surface,vertexinfo(i,2,1),vertexinfo(i,2,2),vertexinfo(i,2,3)+move#,vertexinfo(i,2,4))
			VertexCoords(surface,vertexinfo(i,3,1),vertexinfo(i,3,2),vertexinfo(i,3,3)+move#,vertexinfo(i,3,4))
			Next
		End Function


If your computer is particularly l33t and you get no speed difference, then try changing SIZE to 30, or 60.

The thing is - in this example, it's moving 3 verts for 20 tri's at random. That's ONLY updating 60verts in each frame, and you get that large a performance impact if you don't stack the updates all together in one lump. Now imagine trying to write an animation system in Blitz3D as this fellow is, where in animated models - several thousand verts are going to be updated each frame. Personally, I don't think it's possible.

I think if animating meshes in Blitz like this, it'd actually be faster to re-create the entire mesh each time rather than dynamically deform it.

+BlackD

Okay well I tried it with a 64x64 mesh and the fast version goes at like 6fps, which is what my own experience in the past was, so unless getting the vertex locations also triggers this upload I probably haven't run into this issue in my own code, because there's not many reasons to do a linepick and move vertces at the same time.

Have you tested to see if getting the vertex locations also has an impact on the speed?

Oh absolutely sswift .. writing efficient code you wouldn't even do it the way I wrote above. That was just concept code to illustrate the point I was trying to make to SomethingFunky. That being, you have to plan your mesh animation code carefully. He was talking about using a AnimateMyModel() command to animate the meshs and I'm saying - uhuh, that's a bad idea. Such a command would be conditional on circumstances in the main loop, therefore it'd be in amongst a whole bunch of other code - and with several models being animated, and other stuff going on int he scene at once, he'd get a huge slowdown.

Hence, the scope of an AnimateMyModel() command should simply be to store references to mesh animations and not to do them then, but to store them and - as you suggested - update all the mesh's once before UPDATEWORLD (or before collisions, or whatever you want) per loop, rather than updating vertices multiple times in multiple places.

+BlackD

Interesting information, thanks :o)

thanks for the help. i liked the example code, it was fun!

i had a quick look at the code pieces and am getting somewhere at least. the models are low-poly (about 100-500 vertices) and slow animating (about 10-50 fps) which means the fastest updates no more than 5 times a second.

i'll have to test (of course) to see if an updateallmodels() system is faster than an updatemymodel() loop, so thanks for the sound advice! and now i'm off to.. write a function or something.

Jellyfish Animation

My first ever 3d animation. . . (just about sort of)

I would like to be able to display at least 20 of these at a time in a game with loads of other stuff going on, but I've no idea how to store the animation, replay etc quickly.

Someone help me please! :O)

mr snidesmin, i liked that demo, so i scraped an example together this evening which shows a way to do this. you could use types but i can't understand them yet, so it uses arrays.

the biggest problem is poly count, on my system i only get 5 jellyfish comfortably and fps drops at 10000 tris. to get more you'd need to crunch less vertices, so you'd need a lod system based on distance and whether its in camera view.

;Global JellyParam#, WaveParam1#, WaveParam2#, WaveParam3#
Global GlobalLight%, GlobalCamera%, mshJellyBase%, mshJelly%
Local fps%, fpscount%, millistop%, millis%
Global jelly_num, jelly_max, jelly_tic, driftspd#, driftdg#

SeedRnd MilliSecs() ;randomize rnd()
driftspd=Rnd(-0.02,0.02)
driftdg=Rnd(0,360)
jelly_max=5 ;max number of jellyfish
Dim jellyent(jelly_max)
Dim jelly#(jelly_max,4)

;init world
Graphics3D 800,600
SetBuffer BackBuffer()
GlobalLight% = CreateLight()
RotateEntity GlobalLight, 70, 45, 45
AmbientLight 100,120,255
LightColor GlobalLight, 255,255,255
GlobalCamera% = CreateCamera()
PositionEntity GlobalCamera, 4, 0, 0
CameraClsColor GlobalCamera, 0,50,90
CameraRange GlobalCamera,0.5,500

CreateJellyBase() ;create base once

For i=0 To jelly_max ;new jelly loop
 NewJellyMesh()
Next

PointEntity GlobalCamera, mshJellyBase ;init point at base

;main loop
While Not KeyHit(1)

jelly_tic=jelly_tic+1
If jelly_tic>jelly_max-1 Then jelly_tic=0
UpdateJelly(jelly_tic)

Navigate_World_With_MouseAndKeys(0.5, .1)

UpdateWorld ; without this animations/collisions freeze 
;SetBuffer BackBuffer() ;<- don't need this here
RenderWorld ; render everything to backbuffer

Color 255, 0, 0
Text 1, 1, "FPS=" + fps	
Text 1, 12, "TrisRendered=" + TrisRendered()

Flip ;show everything to frontbuffer

fpscount=fpscount+1 ;get fps
millis%=MilliSecs()
 If millistop<millis-1000
 millistop=millis
 fps=fpscount
 fpscount=0
 EndIf

Wend
;main loop
End


;create jellyfish base
Function CreateJellyBase()

mshJellyBase% = CreateMesh() ;used to store original vertex positions

For n# = 0 To 1 Step 0.2 ;loop 6 times
 mshtmp = CreateSphere(8)
 ScaleMesh mshtmp, 1/(1+10*n), 0.35, 1/(1+10*n)
 PositionMesh mshtmp, 0, -n, 0
 AddMesh mshtmp, mshJellyBase ;source -> dest mesh
 FreeEntity mshtmp ;free jelly
Next
For a# = 0 To 350 Step 10 ;loop 36 times
 off# = -Rnd(0.3) ;random y offset (tentacle top)
 For n# = 0 To 0.3 Step 0.05 ;loop 10 times (was 0.5)
  mshtmp = CreateCylinder(2, False) ;2=strips, false=tube (no ends)
  ScaleMesh mshtmp, 0.003, 0.05, 0.003
  PositionMesh mshtmp, 0.8 * Cos(a), off-n, 0.8 * Sin(a)
  AddMesh mshtmp, mshJellyBase
  FreeEntity mshtmp ;free next tentacle length
 Next
Next

PositionEntity mshJellyBase,0,0,0
HideEntity mshJellyBase

End Function


;copy jellyfish base
Function NewJellyMesh()

If jelly_num<jelly_max ;if not max
 jellyent(jelly_num) = CopyMesh(mshJellyBase) ;entity handle
 EntityAlpha jellyent(jelly_num), 0.3

  SeedRnd MilliSecs() ;randomize rnd()
  px#=Rnd(-9,9) ;init xyz positions
  py#=Rnd(-5,-0.5)
  pz#=Rnd(-9,9)

  For i=0 To jelly_num ;attempt to not have same positions, not v.good
   tx=px# : tz=pz#
   ax=EntityX#(jellyent(i)) : az=EntityZ#(jellyent(i))
   If tx=ax And tz=az ;same positions
    SeedRnd MilliSecs() ;randomize rnd()
    px#=Rnd(-9,9) ;init xyz positions
    py#=Rnd(-5,-0.5)
    pz#=Rnd(-9,19)
   EndIf
  Next
 PositionEntity jellyent(jelly_num),px#,py#,pz#
 
 SeedRnd MilliSecs() ;randomize rnd()
 jelly#(jelly_num,1)=Rnd(0,360) ;init 1=jelly part
 jelly#(jelly_num,2)=Rnd(0,360) ;init 2-4=tail parts
 jelly#(jelly_num,3)=Rnd(0,360)
 jelly#(jelly_num,4)=Rnd(0,360)

 jelly_num=jelly_num+1 ;next jelly
EndIf

End Function


;deforms mesh
Function UpdateJelly(jfnum)

JellyParam=jelly#(jfnum,1) ;get jellyfish array
WaveParam1=jelly#(jfnum,2) ;(1=jelly part, 2-4=tail parts)
WaveParam2=jelly#(jfnum,3)
WaveParam3=jelly#(jfnum,4)

spd# = 1.5+0.5 * Sin(JellyParam)
JellyParam = JellyParam + spd ;Mod 360 ;<- mod not wrap values
While JellyParam>360 : JellyParam=JellyParam-360 : Wend ;wrap to 360dg
WaveParam1 = WaveParam1 + 1.40 ;Mod 360
While WaveParam1>360 : WaveParam1=WaveParam1-360 : Wend ;wrap
WaveParam2 = WaveParam2 + 2.16 ;Mod 360
While WaveParam1>360 : WaveParam1=WaveParam1-360 : Wend ;wrap
WaveParam3 = WaveParam3 + 0.79 ;Mod 360
While WaveParam1>360 : WaveParam1=WaveParam1-360 : Wend ;wrap

s0% = GetSurface(mshJellyBase,1)
s1% = GetSurface(jellyent(jfnum),1) ;jellyfish array entity

For iv% = 0 To CountVertices(s0)-1 ;set jellyfish vertices
h1# = 590 * (VertexY(s0,iv)+VertexX(s0,iv))
h2# = 190 * (VertexY(s0,iv)+VertexZ(s0,iv))
wz# = 0.08 * Cos(WaveParam1+h1)*Cos(WaveParam2+h1)*Cos(WaveParam3+h1)*VertexY(s0,iv)
wx# = 0.08 * Sin(WaveParam1+h2)*Cos(WaveParam2+h2)*Cos(WaveParam3+h2)*VertexY(s0,iv)
s# = Sqr(VertexX(s0,iv)^2+VertexY(s0,iv)^2)
n# = 0.5 * Cos(JellyParam)/(Abs(VertexY(s0,iv))+0.3) + s
 If VertexY(s0,iv)<0 ;if y negative
 c#=0.1 * Abs(VertexY(s0,iv))^2
 Else
 c#=0
 EndIf
n2# = 0.3*(2.5+Sin(-JellyParam+140*VertexY(s0,iv)))/(Abs(VertexY(s0,iv))+1.3)
VertexCoords s1,iv,VertexX(s0,iv)*(n2+c)+wx,VertexY(s0,iv)*(n+1.4)*0.4,VertexZ(s0,iv)*(n2+c)+wz
Next

;UpdateNormals jellyent(jfnum) ;<- unnecessary cpu usage

jelly#(jfnum,1)=JellyParam ;set jellyfish array
jelly#(jfnum,2)=WaveParam1 ;(1=jelly part, 2-4=tail parts)
jelly#(jfnum,3)=WaveParam2
jelly#(jfnum,4)=WaveParam3

MoveEntity jellyent(jfnum),driftspd,0,0
RotateEntity jellyent(jfnum),0,driftdg,0

End Function


Function Navigate_World_With_MouseAndKeys(turnSpeed#=1, moveSpeed#=1)

Local dY# = EntityPitch(GlobalCamera)+MouseYSpeed()/2*turnSpeed
 If dY > 89 Then dY = 89
 If dY < -89 Then dY = -89
Local dz# = (KeyDown(200)-KeyDown(208)) * moveSpeed
Local dx# = (KeyDown(205)-KeyDown(203)) * moveSpeed
 If dz<>0 And dx<>0 Then 
 dx=dx * 0.707
 dz=dz * 0.707
 EndIf
RotateEntity GlobalCamera, dY, EntityYaw(GlobalCamera)-(MouseXSpeed()/2)*turnSpeed, 0
MoveEntity GlobalCamera, dx, 0, dz
MoveMouse GraphicsWidth()/2, GraphicsHeight()/2

 If KeyHit(4) Then 
 CameraZoom GlobalCamera, 4.0
 EndIf
 If KeyHit(5) Then
 CameraZoom GlobalCamera, 1.0
 EndIf

End Function




TYPEs are l33t. Learn them. :)

I have an idea.
[edit] forgot the basic idea

To wrap all vertex commands to vertex2 counterparts,

inside the wrapped functions, instead of doing it, store a list in types

Have an Updatevertices() function to itterate the list and actually make the changes in an organised way.

Not sure if this will be faster, and theres likely parts i wont be able to do efficiently,

I'll be releasing source anyway so you lot can improve! :)
Expect an update later.

somethingfunky,

The code alteration looks okay, though, you don't have to save 3 different wave params per jelly fish, because that part just gives a global kind of wave effect. the same 3 parameters can be used for all jelly fish, but i suppose the time saved would be neglible anyway.

The whole thing has kind of given me an idea though - perhaps saving 4 or 5 base meshes to represent the cycle and then using tween-morphing between each one will be slightly more efficient. If the tweening was linear, I wouldn't have to use the expensive sin/cos + sqr functions, but the animation mightn't look quite as good.

Oh, and I will be switching to types - sorry, they are just way easy to get your head around than arrays!

Also, using LOD might be a genius idea too. . . but for starters I think I'll create a much lower poly mesh using 3ds Max for the jelly. I shouldn't be needing over 2000 tris for a jellyfish - now come on! :O)

You know, this would be a lot easier ( well faster, anyway ) if Blitz had a parameter to optionally buffer vertexcoords updates. Then you could do 2000 vertex updates by updating 1999 vertices without updating the mesh, then setting the flag to update the mesh on the 2000th mesh.

Obviously vertex shaders would be ideal, particularly since they fall back to software on videocards which don't have hardware support, but we all know that's never gonna happen.

This functionality is coming.

http://www.blitzbasic.com/Community/posts.php?topic=51697

Coming soon to a Blitz near you!

VertexCoords2, vertexTexCoords2, Vertexcolor2,

Any more? @#!* easy for me to add them :)

I will post code when i get home for y'all to test, in the above thread.

mr snidesmin, i was curious to see how much fps would be saved by using a pre-calculated array instead of the slow sin/cos/sqr. seems quite a bit! so i thought i'd let you, or anyone else, know. the code below is a quick example just to show the speed increase.

Global JellyParam#, WaveParam1#, WaveParam2#, WaveParam3#
Global GlobalLight%, GlobalCamera%, mshJellyBase%, mshJelly%
Local fps%, fpscount%, millistop%, millis%
Global jelly_num, jelly_max, use_array, driftspd#, driftdg#

SeedRnd MilliSecs() ;randomize rnd()
driftspd=Rnd(-0.02,0.02)
driftdg=Rnd(0,360)
jelly_max=15 ;max number of jellyfish

Dim jellyent(jelly_max)
Dim jelly#(jelly_max)
Dim jellyanim#(1,2,3) ;resized

;init world
Graphics3D 800,600
SetBuffer BackBuffer()
GlobalLight% = CreateLight()
RotateEntity GlobalLight, 70, 45, 45
AmbientLight 100,120,255
LightColor GlobalLight, 255,255,255
GlobalCamera% = CreateCamera()
PositionEntity GlobalCamera, 4, 0, 0
CameraClsColor GlobalCamera, 0,50,90
CameraRange GlobalCamera,0.5,500

CreateJellyBase() ;create base once
CreateJellyAnim() ;create anim array once

For i=0 To jelly_max ;new jelly loop
 NewJellyMesh()
Next

PointEntity GlobalCamera, mshJellyBase ;init point at base

;main loop
While Not KeyHit(1)

Navigate_World_With_MouseAndKeys(0.5, 0.1)

If KeyHit(2) Then use_array=0 ;key 1
If KeyHit(3) Then use_array=1 ;key 2

For tic=0 To jelly_max-1 ;animate jellys loop
 If use_array=0 Then PlayJellyAnim(tic) ;animate array
 If use_array=1 Then UpdateJelly(tic) ;sin array
Next

UpdateWorld ;without this animations/collisions freeze 
;SetBuffer BackBuffer() ;<- don't need this here
RenderWorld ;render everything to backbuffer

Color 255, 0, 0
Text 1, 1, "FPS=" + fps	
Text 1, 12, "TrisRendered=" + TrisRendered()
Text 1, 24, "Press 1 or 2 keys to switch:" + use_array

Flip ;show everything to frontbuffer

fpscount=fpscount+1 ;get fps
millis%=MilliSecs()
 If millistop<millis-1000
 millistop=millis
 fps=fpscount
 fpscount=0
 EndIf

Wend
;main loop
End


;create jellyfish base
Function CreateJellyBase()

mshJellyBase% = CreateMesh() ;used to store original vertex positions

For n# = 0 To 1 Step 0.2 ;loop 6 times
 mshtmp = CreateSphere(8)
 ScaleMesh mshtmp, 1/(1+10*n), 0.35, 1/(1+10*n)
 PositionMesh mshtmp, 0, -n, 0
 AddMesh mshtmp, mshJellyBase ;source -> dest mesh
 FreeEntity mshtmp ;free jelly
Next
For a# = 0 To 350 Step 10 ;loop 36 times
 off# = -Rnd(0.3) ;random y offset (tentacle top)
 For n# = 0 To 0.3 Step 0.05 ;loop 10 times (was 0.5)
  mshtmp = CreateCylinder(2, False) ;2=strips, false=tube (no ends)
  ScaleMesh mshtmp, 0.003, 0.05, 0.003
  PositionMesh mshtmp, 0.8 * Cos(a), off-n, 0.8 * Sin(a)
  AddMesh mshtmp, mshJellyBase
  FreeEntity mshtmp ;free next tentacle length
 Next
Next

PositionEntity mshJellyBase,0,0,0
HideEntity mshJellyBase

End Function


;copy jellyfish base
Function NewJellyMesh()

If jelly_num<jelly_max ;if not max
 jellyent(jelly_num) = CopyMesh(mshJellyBase) ;entity handle
 EntityAlpha jellyent(jelly_num), 0.3

  SeedRnd MilliSecs() ;randomize rnd()
  px#=Rnd(-9,9) ;init xyz positions
  py#=Rnd(-5,-0.5)
  pz#=Rnd(-9,9)

  For i=0 To jelly_num ;attempt to not have same positions, not v.good
   tx=px# : tz=pz#
   ax=EntityX#(jellyent(i)) : az=EntityZ#(jellyent(i))
   If tx=ax And tz=az ;same positions
    SeedRnd MilliSecs() ;randomize rnd()
    px#=Rnd(-9,9) ;init xyz positions
    py#=Rnd(-5,-0.5)
    pz#=Rnd(-9,19)
   EndIf
  Next
 PositionEntity jellyent(jelly_num),px#,py#,pz#
 
 SeedRnd MilliSecs() ;randomize rnd()
 jelly#(jelly_num)=Rnd(0,360) ;init jelly part

 jelly_num=jelly_num+1 ;next jelly
EndIf

End Function


;deforms mesh
Function UpdateJelly(jfnum)

JellyParam=jelly#(jfnum) ;get jellyfish sin (jelly part)

spd# = 1.5+0.5 * Sin(JellyParam)
JellyParam = JellyParam + spd ;Mod 360
WaveParam1 = WaveParam1 + 1.40 ;Mod 360
WaveParam2 = WaveParam2 + 2.16 ;Mod 360
WaveParam3 = WaveParam3 + 0.79 ;Mod 360
While JellyParam>360 : JellyParam=JellyParam-360 : Wend ;wrap to 360dg
While WaveParam1>360 : WaveParam1=WaveParam1-360 : Wend
While WaveParam1>360 : WaveParam1=WaveParam1-360 : Wend
While WaveParam1>360 : WaveParam1=WaveParam1-360 : Wend

s0% = GetSurface(mshJellyBase,1)
s1% = GetSurface(jellyent(jfnum),1) ;jellyfish array entity

For iv% = 0 To CountVertices(s0)-1 ;set jellyfish vertices
 h1# = 590 * (VertexY(s0,iv)+VertexX(s0,iv))
 h2# = 190 * (VertexY(s0,iv)+VertexZ(s0,iv))
 wz# = 0.08 * Cos(WaveParam1+h1)*Cos(WaveParam2+h1)*Cos(WaveParam3+h1)*VertexY(s0,iv)
 wx# = 0.08 * Sin(WaveParam1+h2)*Cos(WaveParam2+h2)*Cos(WaveParam3+h2)*VertexY(s0,iv)
 s# = Sqr(VertexX(s0,iv)^2+VertexY(s0,iv)^2)
 n# = 0.5 * Cos(JellyParam)/(Abs(VertexY(s0,iv))+0.3) + s
 If VertexY(s0,iv)<0 ;if y negative
  c#=0.1 * Abs(VertexY(s0,iv))^2
 Else
  c#=0
 EndIf
 n2# = 0.3*(2.5+Sin(-JellyParam+140*VertexY(s0,iv)))/(Abs(VertexY(s0,iv))+1.3)
 VertexCoords s1,iv,VertexX(s0,iv)*(n2+c)+wx,VertexY(s0,iv)*(n+1.4)*0.4,VertexZ(s0,iv)*(n2+c)+wz
Next

;UpdateNormals jellyent(jfnum) ;<- unnecessary cpu usage

jelly#(jfnum)=JellyParam ;set jellyfish sin (jelly part)

MoveEntity jellyent(jfnum),driftspd,0,0
RotateEntity jellyent(jfnum),0,driftdg,0

End Function


;create jellyfish array
Function CreateJellyAnim()

mshJelly = CopyMesh(mshJellyBase) ;temp entity

s0% = GetSurface(mshJellyBase,1)
s1% = GetSurface(mshJelly,1) ;jellyfish array entity
vertices = CountVertices(s0)

Dim jellyanim#(vertices,255,3) ;resize array

For fi=0 To 255 ;create 255 frames array (framenum depends on spd)
 spd# = 1.5 + 0.5*Sin(JellyParam)
 JellyParam = JellyParam + spd
 WaveParam1 = WaveParam1 + 1.40 *2 ;*2 seems smoother
 WaveParam2 = WaveParam2 + 2.16
 WaveParam3 = WaveParam3 + 0.79

 For iv% = 0 To vertices-1 ;set jellyfish vertices
  h1# = 590 * (VertexY(s0,iv)+VertexX(s0,iv))
  h2# = 190 * (VertexY(s0,iv)+VertexZ(s0,iv))
  wz# = 0.08 * Cos(WaveParam1+h1)*Cos(WaveParam2+h1)*Cos(WaveParam3+h1)*VertexY(s0,iv)
  wx# = 0.08 * Sin(WaveParam1+h2)*Cos(WaveParam2+h2)*Cos(WaveParam3+h2)*VertexY(s0,iv)
  s# = Sqr(VertexX(s0,iv)^2+VertexY(s0,iv)^2)
  n# = 0.5 * Cos(JellyParam)/(Abs(VertexY(s0,iv))+0.3) + s
  If VertexY(s0,iv)<0 ;if y negative
   c#=0.1 * Abs(VertexY(s0,iv))^2
  Else
   c#=0
  EndIf
  n2# = 0.3*(2.5+Sin(-JellyParam+140*VertexY(s0,iv)))/(Abs(VertexY(s0,iv))+1.3)
  jellyanim#(iv,fi,1)=VertexX(s0,iv)*(n2+c)+wx ;set frame vertices array
  jellyanim#(iv,fi,2)=VertexY(s0,iv)*(n+1.4)*0.4
  jellyanim#(iv,fi,3)=VertexZ(s0,iv)*(n2+c)+wz
 Next

Next

FreeEntity mshJelly ;free temp entity

End Function


;play jellyfish array
Function PlayJellyAnim(jfnum)

s1% = GetSurface(jellyent(jfnum),1) ;jellyfish array entity
vertices = CountVertices(s1)

JellyParam=jelly#(jfnum) ;get jellyfish array

JellyParam=JellyParam+1
If JellyParam>=255 : JellyParam=1 : EndIf ;wrap frame
fnum=JellyParam

For i=0 To vertices-1 ;set jellyfish vertices
VertexCoords s1,i,jellyanim#(i,fnum,1),jellyanim#(i,fnum,2),jellyanim#(i,fnum,3)
Next

jelly#(jfnum)=fnum ;set jellyfish array

MoveEntity jellyent(jfnum),driftspd,0,0
RotateEntity jellyent(jfnum),0,driftdg,0

End Function


Function Navigate_World_With_MouseAndKeys(turnSpeed#=1, moveSpeed#=1)

Local dY# = EntityPitch(GlobalCamera)+MouseYSpeed()/2*turnSpeed
 If dY > 89 Then dY = 89
 If dY < -89 Then dY = -89
Local dz# = (KeyDown(200)-KeyDown(208)) * moveSpeed
Local dx# = (KeyDown(205)-KeyDown(203)) * moveSpeed
 If dz<>0 And dx<>0 Then 
 dx=dx * 0.707
 dz=dz * 0.707
 EndIf
RotateEntity GlobalCamera, dY, EntityYaw(GlobalCamera)-(MouseXSpeed()/2)*turnSpeed, 0
MoveEntity GlobalCamera, dx, 0, dz
MoveMouse GraphicsWidth()/2, GraphicsHeight()/2

 If KeyHit(4) Then 
 CameraZoom GlobalCamera, 4.0
 EndIf
 If KeyHit(5) Then
 CameraZoom GlobalCamera, 1.0
 EndIf

End Function



somethingfunky,

I just tried that out - very VERY nice!
it's a huge improvement in performance. . . thanks!!!

:O)

[edit] I can render over 40 jellies before any signifcant loss in framerate. This is more than adequate considering the mesh is going to be much much fewer verts.

I'll probably be able to render near 100 of them with a small mesh!

Yay!