Code archives/3D Graphics - Mesh/saveanimmesh

This code has been declared by its author to be Public Domain code.

Download source code

saveanimmesh by b32
(Posted 21 years ago)
this lib saves animated meshes in .x format
update: it now saves the mesh hierarchy
update: nan is removed
update: the models are now compatible with LoadMesh as well
I updated the Transformation Matrix, thanks to the description of DJWoodgate here: http://www.blitzbasic.com/b3ddocs/command.php?name=GetMatElement&ref=3d_a-z
;-------------------------------------------------------------------------------------------------------
;												SaveAnimFile()
;-------------------------------------------------------------------------------------------------------
;save an animated mesh to the ".x" format
;the x format doesn't support vertex weight, only parenting objects to each other
;
;SYNTAX:
;
; SaveAnimFile mesh, filename$
;
;   mesh     - mesh handle
;   filename - name of file
;

;print debug output
Const   debug = False

Global	frameindex
Global 	meshindex
Global	animindex
Global	orgmesh

;save animated mesh
Function SaveAnimFile(mesh, filename$)
	
	;reset counters
	frameindex = 0
	meshindex = 0
	animindex = 0

	;open file for writing
	ff = WriteFile(filename)
	
	;write header data
	WriteLine ff, "xof 0302txt 0064"
	WriteLine ff, " " 
	WriteLine ff, "Header {"
	WriteLine ff, " 1;"
	WriteLine ff, " 0;"
	WriteLine ff, " 1;"
	WriteLine ff, "}"
	WriteLine ff, " "
		
	;save object "mesh" to file
	SaveAnimObj(ff, mesh)
	
	;save animation data to file
	SaveAnimAnim(ff, mesh)
	
	;close file
	CloseFile ff
	
End Function

;-------------------------------------------------------------------------------------------------------
;												SaveAnimObj()
;-------------------------------------------------------------------------------------------------------
;save object and children (recursive)
Function SaveAnimObj(ff, mesh)
	
	;generate name
	name$ = "Frame" + frameindex
	frameindex = frameindex + 1
	
	;start frame block
	WriteLine ff, "Frame frm_" + name$ + " {"
	WriteLine ff, " "
	WriteLine ff, "  FrameTransformMatrix { "
	TFormPoint 1, 0, 0, mesh, GetParent(mesh)
	xx# = TFormedX() - EntityX(mesh)
	xy# = TFormedY() - EntityY(mesh)
	xz# = TFormedZ() - EntityZ(mesh)
	TFormPoint 0, 1, 0, mesh, GetParent(mesh)
	yx# = TFormedX() - EntityX(mesh)
	yy# = TFormedY() - EntityY(mesh)
	yz# = TFormedZ() - EntityZ(mesh)
	TFormPoint 0, 0, 1, mesh, GetParent(mesh)
	zx# = TFormedX() - EntityX(mesh)
	zy# = TFormedY() - EntityY(mesh)
	zz# = TFormedZ() - EntityZ(mesh)
	WriteLine ff, NoNan(xx) + "," + NoNan(xy) + "," + NoNan(xz) + "," + "0.000000,"
	WriteLine ff, NoNan(yx) + "," + NoNan(yy) + "," + NoNan(yz) + "," + "0.000000,"
	WriteLine ff, NoNan(zx) + "," + NoNan(zy) + "," + NoNan(zz) + "," + "0.000000,"
	WriteLine ff, NoNan(EntityX(mesh)) + "," + NoNan(EntityY(mesh)) + "," +  NoNan(EntityZ(mesh)) + "," + "1.000000;"
	WriteLine ff, "  }"
	WriteLine ff, " "
	
	;save children
	For i = 1 To CountChildren(mesh)
		
		g = GetChild(mesh, i)
		SaveAnimObj ff, g
		
	Next

	;if entity is a mesh, then save the mesh data	
	If EntityClass$(mesh) = "Mesh" Then SaveAnimMesh(ff, mesh)
	
	;end frame block	
	WriteLine ff, "}"
	WriteLine ff, " "
		
End Function


;-------------------------------------------------------------------------------------------------------
;											SaveAnimMesh()
;-------------------------------------------------------------------------------------------------------
;save mesh data to file
Function SaveAnimMesh(ff, mesh)

	;generate name
	name$ = "Mesh" + meshindex
	meshindex = meshindex + 1
	
	;debug	
	If debug Then Print "Mesh->Frame:" + name$

	;loop through all surfaces
	For surfindex = 1 To CountSurfaces(mesh)
	
		;generate name
		objectname$ = name$ + "_surface" + surfindex

		;debug
		If debug Then Print "Surface->Mesh:" + objectname$

		;get surface
		surf 		= GetSurface(mesh, surfindex)
		numVert  	= CountVertices(surf)
		numTris 	= CountTriangles(surf)
		
		;get texname
		brush 		= GetSurfaceBrush(surf)
		tex			= GetBrushTexture(brush)
		texname$	= TextureName$(tex)
		If Instr(texname$, "\") > 0 Then
			test = 0
			For i = Len(texname$) To 1 Step -1
				 If Mid$(texname$, i, 1) = "\" Then test = i: Exit
			Next
			texname$ = Mid$(texname$, test + 1, Len(texname$))
		End If
		FreeTexture tex
		FreeBrush brush

		;start mesh block		
		WriteLine 	ff, " Mesh " + objectname$ + " { "
			
		;write vertices
		WriteLine 	ff, " " + numVert + ";"
		
		For i = 0 To numVert - 1
			If i = numVert - 1 Then st$ = ";" Else st$ = ","
			xx# = VertexX(surf, i)
			yy# = VertexY(surf, i)
			zz# = VertexZ(surf, i)
			WriteLine ff, NoNan(xx#) + ";" + NoNan(yy#) + ";" + NoNan(zz#) + ";" + st$
		Next
	
		;write triangles	
		WriteLine ff, " " + numTris + ";"
		
		For i = 0 To numTris - 1
			aa = TriangleVertex(surf, i, 0)
			bb = TriangleVertex(surf, i, 1)
			cc = TriangleVertex(surf, i, 2)
			If i = numTris - 1 Then st$ = ";" Else st$ = ","
			WriteLine ff, "3;" + aa + "," + bb + "," + cc + ";" + st$
		Next
	
		;write material properties	
		WriteLine ff, " "
		WriteLine ff, "MeshMaterialList {"
		WriteLine ff, "1;"
		;----------------------------------------
		;This part is not compatible with Blender
		;Blender wants all triangles defined here
		WriteLine ff, "1;"
		WriteLine ff, "0;;"
		;Besides this, Blender still complains
		;about these files, at least when they
		;are animated
		;----------------------------------------
		WriteLine ff, " "
			
		WriteLine ff, "Material {"
		WriteLine ff, " 1.000000,1.000000,1.000000,1.000000;;"
		WriteLine ff, " 1.000000;"
		WriteLine ff, " 0.500000,0.500000,0.500000;;"
		WriteLine ff, " 0.000000,0.000000,0.000000;;"
		
		;write texture file name	
		If texname$ <> "" Then
			WriteLine ff, "      TextureFilename {"
			WriteLine ff, "        " + Chr$(34) + texname$ + Chr$(34) + "; }"
			If debug Then Print "texture:" + texname$
		End If
		
		;close material block		
		WriteLine ff, "}"
		WriteLine ff, "}"
		WriteLine ff, " "
	
		;write normals	
		WriteLine ff, "MeshNormals {"	
		WriteLine ff, " " + numVert + ";"
		
		;write normal vertices
		For i = 0 To numVert - 1
			If (i = numVert - 1) Then st$ = ";" Else st$ = ","
			xx# 		= VertexNX(surf, i)
			yy# 		= VertexNY(surf, i)
			zz# 		= VertexNZ(surf, i)
			WriteLine 	ff, NoNan(xx#) + ";" + NoNan(yy#) + ";" + NoNan(zz#) + ";" + st$
		Next
		
		;write normal triangles
		WriteLine ff, " " + numTris + ";"
		For i = 0 To numTris - 1
			If (i = numTris - 1) Then st$ = ";" Else st$ = ","
			aa 			= TriangleVertex(surf, i, 0)
			bb 			= TriangleVertex(surf, i, 1)
			cc 			= TriangleVertex(surf, i, 2)
			WriteLine 	ff, "3;" + aa + "," + bb + "," + cc + ";" + st$
		Next
	
		;close normals block	
		WriteLine ff, 	"}"
		WriteLine ff, 	" "
		
		;write texture coordinates U,V
		WriteLine ff, 	"MeshTextureCoords {"	
		WriteLine ff, 	" " + numVert + ";"
		
		For i = 0 To numVert - 1
			If (i = numVert - 1) Then st$ = ";" Else st$ = ","
			uu# 		= VertexU(surf, i)
			vv# 		= VertexV(surf, i)
			WriteLine 	ff, NoNan(uu#) + ";" + NoNan(vv#) + ";" + st$
		Next
	
		WriteLine ff, "  }"
	
		;close mesh block	
		WriteLine ff, " }"
		WriteLine ff, " "
		
	Next

End Function

;-------------------------------------------------------------------------------------------------------
;												SaveAnimAnim()
;-------------------------------------------------------------------------------------------------------
;save animation sequence data to file
Function SaveAnimAnim(ff, mesh)

	If AnimLength(mesh) < 0 Then Return
	
	;used for "Animate" command
	orgmesh = mesh
	
	;start animation sequence block
	WriteLine ff, "AnimationSet Sequence01 {"

	;save animation data	
	SaveAnimDat(ff, mesh, AnimLength(mesh) + 1)
		
	;close animation sequence block
	WriteLine ff, "}"
			
End Function
			
;-------------------------------------------------------------------------------------------------------
;												SaveAnimDat()
;-------------------------------------------------------------------------------------------------------
;save animation data
Function SaveAnimDat(ff, mesh, maxframe)
	
	animname$   = "ani_" + animindex
	objectname$ = "Frame" + animindex
	animindex = animindex + 1
	
	If debug Then Print "Animation" + animname$
	If debug Then Print "-->" + objectname$
	
	WriteLine ff, "Animation " + animname$ + " {"
	WriteLine ff, " "
	WriteLine ff, "{frm_" + objectname$ + "} "
	WriteLine ff, " "
				
		;write rotation data
		WriteLine ff, "AnimationKey {"
		WriteLine ff, "0;"
		WriteLine ff, (maxframe) + ";"
			For i = 0 To maxframe
				iSetAnimTime orgmesh, i
				WriteLine ff, RotationToString(i, EntityPitch(mesh), EntityYaw(mesh), EntityRoll(mesh))
			Next	
		WriteLine ff, "}"
		WriteLine ff, " "
		
		;write scaling data
		WriteLine ff, "AnimationKey {"
		WriteLine ff, "1;"
		WriteLine ff, (maxframe) + ";"
			For i = 0 To maxframe
				iSetAnimTime orgmesh, i
				WriteLine ff, ScalingToString(i, EntityWidth(mesh), EntityHeight(mesh), EntityDepth(mesh))
			Next
		WriteLine ff, "}"
		WriteLine ff, " "

		;write position data
		WriteLine ff, "AnimationKey {"
		WriteLine ff, "2;"
		WriteLine ff, (maxframe) + ";"
			For i = 0 To maxframe
				iSetAnimTime orgmesh, i
				WriteLine ff, PositionToString(i, EntityX(mesh), EntityY(mesh), EntityZ(mesh))
			Next
		WriteLine ff, "}"
		WriteLine ff, " "
	
	;write animation options
	WriteLine ff, "AnimationOptions {"
	WriteLine ff , "1;"		;0 = closed (default)  1 = open
	WriteLine ff , "1; }"	;0 = splines  1 = linear
	
	;close animation block
	WriteLine ff, "}"
	
	;save children
	For i = 1 To CountChildren(mesh)
		
		g = GetChild(mesh, i)
		
		SaveAnimDat(ff, g, maxframe)
	
	Next			
	
End Function

;-----------------------------------------------------------------------------------------------------
; 										    RotationToString$()
;-----------------------------------------------------------------------------------------------------
;convert rotation information to string
Function RotationToString$(ptime, iPitch#, iYaw#, iRoll#)	

	;this code was written by LeadWerks
	;http://www.blitzbasic.com/Community/posts.php?topic=51579
	sp# = Sin(iYaw   / 2)
	cp# = Cos(iYaw   / 2)
	sy# = Sin(iRoll  / 2)
	cy# = Cos(iRoll  / 2)
	sr# = Sin(iPitch / 2)
	cr# = Cos(iPitch / 2)
	
	w# = + (cr * cp * cy - sr * sp * sy)
	x# = - (sr * cp * cy - cr * sp * sy)
	y# = + (cr * sp * cy + sr * cp * sy)
	z# = - (sr * sp * cy + cr * cp * sy)
	
	Return ptime + "; 4; " + NoNan(w) + "," + NoNan(x) + "," + NoNan(y) + "," + NoNan(z) + ";;;"
	
End Function

;-----------------------------------------------------------------------------------------------------
;											 PositionToString$()
;-----------------------------------------------------------------------------------------------------
;convert position information to string
Function PositionToString$(ptime, iX#, iY#, iZ#)
	
	Return pTime + "; 3;" + NoNan(iX) + "," + NoNan(iY) + "," + NoNan(iZ) + ";;,"
	
End Function

;-----------------------------------------------------------------------------------------------------
;											ScalingToString$()
;-----------------------------------------------------------------------------------------------------
;convert scaling information to string
Function ScalingToString$(pTime, iWidth#, iHeight#, iDepth#)
	
	Return pTime + "; 3;" + NoNan(iWidth) + "," + NoNan(iHeight) + "," + NoNan(iDepth) + ";;,"
	
End Function

;-----------------------------------------------------------------------------------------------------
;											EntityWidth()
;-----------------------------------------------------------------------------------------------------
;returns width of an entity
Function EntityWidth#( mesh )
	
	If EntityClass$(mesh) <> "Mesh" Then Return 1
	If MeshWidth(mesh) = 0 Then Return 1

	TFormPoint MeshWidth(mesh), 0, 0, mesh, 0	
	xx# = TFormedX()
	yy# = TFormedY()
	zz# = TFormedZ()
	TFormPoint 0, 0, 0, mesh, 0	
	xx# = TFormedX() - xx
	yy# = TFormedY() - yy
	zz# = TFormedZ() - zz	
	ll# = Sqr(xx * xx + yy * yy + zz * zz) / MeshWidth(mesh)
	
	If ll = 0 Then ll = 1
	Return ll
	
End Function

;-----------------------------------------------------------------------------------------------------
;											EntityHeight()
;-----------------------------------------------------------------------------------------------------
;returns height of an entity
Function EntityHeight#( mesh )

	If EntityClass$(mesh) <> "Mesh" Then Return 1
	If MeshHeight(mesh) = 0 Then Return 1
	
	TFormPoint 0, MeshHeight(mesh), 0, mesh, 0
	xx# = TFormedX()
	yy# = TFormedY()
	zz# = TFormedZ()
	TFormPoint 0, 0, 0, mesh, 0
	xx# = TFormedX() - xx
	yy# = TFormedY() - yy
	zz# = TFormedZ() - zz
	ll# = Sqr(xx * xx + yy * yy + zz * zz) / MeshHeight(mesh)
	
	If ll = 0 Then ll = 1
	Return ll
	
End Function

;-----------------------------------------------------------------------------------------------------
;											EntityDepth()
;-----------------------------------------------------------------------------------------------------
;returns depth of an entity
Function EntityDepth#( mesh )

	If EntityClass$(mesh) <> "Mesh" Then Return 1
	If MeshDepth(mesh) = 0 Then Return 1
	
	TFormPoint 0, 0, MeshDepth(mesh), mesh, 0	
	xx# = TFormedX()
	yy# = TFormedY()
	zz# = TFormedZ()
	TFormPoint 0, 0, 0, mesh, 0
	xx# = TFormedX() - xx
	yy# = TFormedY() - yy
	zz# = TFormedZ() - zz
	
	ll# = Sqr(xx * xx + yy * yy + zz * zz) / MeshDepth(mesh)
	
	If ll = 0 Then ll = 1
	Return ll
		
End Function

;-----------------------------------------------------------------------------------------------------
;											    iSetAnimTime()
;-----------------------------------------------------------------------------------------------------
;user defined SetAnimTime
Function iSetAnimTime( mesh, time# )
	If time >= AnimLength(mesh) Then time = AnimLength(mesh) - 0.01
	SetAnimTime mesh, time
	If debug Then If time Mod 10 = 0 Then Print "frame:" + time
End Function

;-----------------------------------------------------------------------------------------------------
;												   NoNan()
;-----------------------------------------------------------------------------------------------------
;avoids writing 'Nan' (not a number) to a file.
Function NoNan#(t$)

	If t$ = "NaN" Then t$ = "0"
	Return t$
	
End Function

;-----------------------------------------------------------------------------------------------------
;										    Test with Mak_Robotic.X
;-----------------------------------------------------------------------------------------------------

	Graphics3D 800, 600, 0, 2
	SetBuffer BackBuffer()

	;setup camera	
	camera = CreateCamera()
	PositionEntity camera,0,20,-100
	
	CreateLight()
		
	If FileType("C:\Program Files\Blitz3D\Samples\Blitz 3D Samples\mak\anim\makbot") <> 2 Then RuntimeError "erm .. mak_robotic is not where i expected .. sorry"
	
	;go to directory	
	ChangeDir "C:\Program Files\Blitz3D\Samples\Blitz 3D Samples\mak\anim\makbot"
	
	;load anim mesh
	robot = LoadAnimMesh("mak_robotic.3ds")
	
	;animate mesh
	Animate robot, 1

	;save animated mesh	
	SaveAnimFile robot, "robot.x"
	
	;re-apply animation
	Animate robot, 1
	
	;load saved mesh
	robot2 = LoadAnimMesh("robot.x")
	If AnimLength(robot2) > 0 Then Animate robot2, 1
	
	;move meshes to the side
	MoveEntity robot2, -35, 0, 0
	MoveEntity robot, 20, 0, 0
	
	;main loop
	While Not KeyDown(1)
	
		UpdateWorld 
		RenderWorld 
		Flip 
	
	Wend
	
	;clean up saved file
	DeleteFile "robot.x"
	
	End

Comments

b32
(Posted 20 years ago)
sample taken from blitzbasic doc's addanimseq
Include "saveanimmesh.bb"


;Create 3d animation example

;Set up a simple nice looking level
Graphics3D 640,480

camera=CreateCamera()
PositionEntity camera,0,14,-15
RotateEntity camera,35,0,0
light=CreateLight(2)
PositionEntity light,1000,1000,-1000
ground=CreatePlane(2)
EntityAlpha ground,0.5
EntityColor ground,0,0,255
mirror=CreateMirror()

;Lets make a bouncing ball that squashes on impact with the floor.
ball=CreateSphere(16)
EntityShininess ball,1
EntityColor ball,255,0,0

; Lets animate him and "record" the 3D animation for later playback
bloat#=0 : flatten#=0 : ypos#=10

For frame=1 To 10
;Drop the ball from height 10 to 2
ypos = ypos - spd#
spd#=spd#+.2
PositionEntity ball,0,ypos,0
ScaleEntity ball,1+bloat,1+flatten,1+bloat

;If the ball is low enough make it look increasingly squashed
If frame>8
bloat=bloat+1.5
flatten=flatten-.25
Else
flatten=flatten+.05
EndIf

;Record the frame!
SetAnimKey ball,frame
Next

;Now we need to add the frames we've just made to the sequence of "film"!
seq = AddAnimSeq(ball,frame-1) ; total number of frames

;Play it back ping-pong!
Animate ball,1

;save file
SaveAnimFile ball, "ball.x"

;free original
FreeEntity ball

;load file for testing
ball = LoadAnimMesh("ball.x")

DeleteFile "ball.x"

;Play it back ping-pong!
Animate ball,2,0.15
While Not KeyHit(1)
UpdateWorld
RenderWorld
Flip
Wend
End


thelizardking
(Posted 20 years ago)
awsome

D4NM4N
(Posted 20 years ago)
Youve just fixed my own export! I wondered why my mesh want loading in DX viewer!!! NAN!

i/we need 2 add a quick file parser to replace nans for 0.0

i did it in wordpad and it worked.

Thanks for bringing that to my attention:)

b32
(Posted 20 years ago)
I used this function to wrap any numerical output:
Function NoNan#(t$)

	If t$ = "NaN" Then t$ = "0"
	Return t$
	
End Function

So any number that is exported to the file is passed to this function first.
As for Blender, I've read that the "materials" section should define a material for each face. This results in having very long files. However, you can also omit the material tag completely.
He, I didn't look at your exporter yet, but maybe we can implement animation support to it ?

Code Archives Forum