Code archives/3D Graphics - Mesh/Explodable mesh demo (updated)

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

Download source code

Explodable mesh demo (updated) by big10p
(Posted 24 years ago)
Run demo for description. No external files required!

Updated: Sorted minor probs with last version. Extended demo now with dynamic explosions, gravity, more realistic, spherical explosions, multiple explosions on a single mesh etc.
;
; Explodable mesh demo by big10p (A.K.A. Chris Chadwick) 2003.
;
; Written with Blitz3D v1.83
;

	Graphics3D 800,600,32

	WireFrame 0
	AntiAlias 0

	SetFont LoadFont("arial",18,1)

	SeedRnd MilliSecs()

	; Explosion control constants:
	; FADE_START  - Set to value between 0 and 1 to say when, during an explosion
	;               animation, the pieces should start to fade out. e.g. 1 to start
	;               immediately, .5 to start halfway through, 0 to never fade.
	; GRAVITY     - Per frame factor by which pieces gravitate towards the world.
	; XZ_DECAY    - Per frame factor by which pieces' world X and Z velocity decays.
	; JIGGLE_TRAJ - False to set exploding pieces on an exact trajectory defined by
	;               the vector from the blast focus to the centre of the tri.
	;               True to set exploding pieces on a trajectory based on the vector
	;               defined above, but with an added 'jiggle' factor. This adds a
	;               more realistic, chaotic appearance to the explosion and helps
	;               disguise the often uniform construction of meshes. However, this
	;               makes a call to start_mesh_explode() slower.
	Const FADE_START# = 0.5
	Const GRAVITY# = 0.001
	Const XZ_DECAY# = 1.0 + GRAVITY		
	Const JIGGLE_TRAJ% = True

	Const BOMB_TYPE% = 1
	Const UFO_TYPE% = 2
	
	; Check pattern texture.
	Global tex = CreateTexture(64,64)
	SetBuffer TextureBuffer(tex)
	For y = 0 To 63 Step 4
		For x = 0 To 63 Step 2
			If c Then Color 255,255,255 Else Color 255,0,0
			c = Not c
			Rect x,y,2,4
		Next
		c = Not c
	Next

	; UFO texture.
	Global tex2 = CreateTexture(128,128)
	SetBuffer TextureBuffer(tex2)
	Color 255,255,255			; Stripes.
	Rect 0,0,128,128
	Color 150,150,150
	For x = 0 To 127 Step 8
		Rect x,0,4,128
	Next
	Color 0,255,255				; Windows.
	Rect 0,33,128,10
	Color 0,0,100				; Hoops.
	For y=3 To 127 Step 10
	Rect 0,y,128,1
	Next
	Color 0,0,0					; Black bands.
	Rect 0,56,128,16
	Rect 0,111,128,1
	Color 255,0,255				; Base light.
	Rect 0,112,128,16
	For x = 0 To 127 Step 4		; Red & yellow rim lights.
		If c
			Color 255,0,0
		Else
			Color 255,255,0
		EndIf
		c = Not c
		Rect x,60,3,8
	Next

	; Explosion flash texture.
	Global tex3 = CreateTexture(64,64,4)
	SetBuffer TextureBuffer(tex3)
	For y = 0 To 63
		For x = 0 To 63
		WritePixel x,y,$00000000
		Next
	Next
	Color 255,255,255
	For i = 1 To 4
		Select i
			Case 1
				Color 255,0,0
				min = 20
				max = 31
			Case 2
				Color 255,125,0
				min = 15
				max = 25
			Case 3
				Color 255,255,0
				min = 10
				max = 20
			Case 4
				Color 255,255,255
				min = 5
				max = 15
		End Select
		
		For n = 0 To 359 Step 2
			r = Rand(min,max)
			Line 32,32,32+Cos(n)*r,32+Sin(n)*r
		Next
	Next

	SetBuffer BackBuffer()
	Color 255,255,255
		
	Type tri_linkT
		Field prev.tri_linkT
		Field surf
		Field tri
		Field dx#,dy#,dz#
		Field pitch#,yaw#,roll#
	End Type

	Type explode_ctrlT
		Field tri_list.tri_linkT
		Field mesh
		Field life%
		Field fader#
		Field fade_start%
		Field exploding%
		Field o_pitch#
		Field o_yaw#
		Field o_roll#
	End Type	

	Type demo_textT
		Field x%,y%
		Field txt$
		Field r%,g%,b%
	End Type
		
	Global frame_count%
	Global fps%
	Global slowest_fps%
	Global fps_timeout%
	Global frame_time%
	Global slowest_frame%
	Global frame_start%
	fps_timer = CreateTimer(60)
	slowmo% = False
	wiref% = False
	Global demo_no% = 1
	Global first_call% = True
	Global alter.demo_textT
	Global exp_ctrl.explode_ctrlT
	Global exp_flash%
	Global exp_stren%
	Global loaded%
	Global explode_chain%
	Global chain_timer%
				
	Global cam = CreateCamera()
	PositionEntity cam,0,0,-9

	light = CreateLight()
	TurnEntity light,0,90,0
	
	; Scratch pad single tri mesh for doing rotations.
	Global tm_mesh = CreateMesh()
	Global tm_surf = CreateSurface(tm_mesh)
	AddVertex(tm_surf,0,0,0)
	AddVertex(tm_surf,0,0,0)
	AddVertex(tm_surf,0,0,0)
	AddTriangle(tm_surf,0,1,2)
	HideEntity tm_mesh

	; Scratch pad mesh used by copy_tri_explode() for subdivision.
	Global sd_mesh = CreateMesh()
	Global sd_surf = CreateSurface(sd_mesh)
	HideEntity sd_mesh

	; Scratch pad single vertex mesh used to 'jiggle' particle trajectory vectors.
	Global vm_mesh = CreateMesh()
	Global vm_surf = CreateSurface(vm_mesh)
	AddVertex(vm_surf,0,0,0)
	HideEntity vm_mesh

	; Some prims to copy.
	Global cube = CreateCube()
	Global sphere = CreateSphere(16)
	Global diamond = CreateSphere(2)
	Global pyramid = CreateCone(4)
	Global cone = CreateCone(16)
	Global cylinder = CreateCylinder(16)
	HideEntity cube
	HideEntity sphere
	HideEntity diamond
	HideEntity cone
	HideEntity cylinder
	HideEntity pyramid
	Global ufo = CreateSphere(12)
	ball = CreateSphere(8)
	ScaleMesh ufo,3,.5,3
	ScaleMesh ball,1.5,1,1.5
	AddMesh ball,ufo
	FreeEntity ball
	HideEntity ufo

	; B3D cone prims seem to have screwed endcap normals!?
	UpdateNormals pyramid
	UpdateNormals cone

	; Explosive sphere.
	Global explosive = CreateSphere(6)
	ScaleMesh explosive,.15,.15,.15
	EntityRadius explosive,.15
	EntityType explosive,BOMB_TYPE
	HideEntity explosive
	Global explosive2 = CopyMesh(explosive)
	Global explosive3 = CopyMesh(explosive)
	HideEntity explosive2
	HideEntity explosive3
	Global exp_piv = CreatePivot()	
	Global exp_piv2 = CreatePivot()	

	; Explosion flash sprite.
	Global flash = CreateSprite()
	EntityTexture flash,tex3
	ScaleSprite flash,3,3
	EntityFX flash,1
	HideEntity flash

		
	; Main loop
	While Not KeyHit(1)

		frame_start = MilliSecs()

		If KeyHit(28) Then slowmo = Not slowmo
		If KeyHit(14)
			wiref = Not wiref
			WireFrame wiref
		EndIf
				
		UpdateWorld
		demo_handler()
		update_mesh_explode()
		RenderWorld

		; Display demo screen text.
		For t.demo_textT = Each demo_textT
			Color t\r Shr 1,t\g Shr 1,t\b Shr 1
			Text 400 + 2,t\y + 2,t\txt$,1,1
			Color t\r,t\g,t\b
			Text 400,t\y,t\txt$,1,1
		Next
		
		frame_time = MilliSecs() - frame_start	
		;show_info()

		WaitTimer(fps_timer)
		Flip(1)

		If slowmo Then Delay 200
	Wend

	ClearWorld

	End


.demo_1_text
Data 50
Data 255,255,0,   "Exploding Mesh Demo"
Data 255,255,0,   "by"
Data 255,255,0,   "big10p (A.K.A. Chris Chadwick) 2003"
Data 0,0,0,		  ""
Data 0,0,0,		  ""
Data 0,255,255,	  "At any time during this demo, press:"
Data 0,255,255,	  "BACKSPACE to toggle wireframe on/off (may not work with your hardware)"
Data 0,255,255,   "ENTER to toggle slow motion on/off"
Data 0,255,255,   "ESCAPE to quit"
Data 0,0,0,		  ""
Data 0,0,0,		  ""
Data 255,0,175,	  "This demo creates explodable meshes by copying existing meshes, unwelding all"
Data 245,0,185,	  "triangles and performing optional, recursive subdivision to give large-tri meshes"
Data 235,0,195,	  "more pieces to explode. Explosion animations are done using dynamic deformation of"
Data 225,0,205,	  "a single mesh/surface, which should be more efficient/faster than using a collection"
Data 215,0,215,	  "of single-tri meshes to achieve the same effect. Explosions are dynamic: the variable"
Data 205,0,225,	  "strength explosion that disintegrates a mesh can be set to any point relative to the"
Data 195,0,235,	  "mesh's centre, either using coords aligned to the mesh's axes or aligned to the world"
Data 185,0,245,	  "axes. This also allows explosions to be either internal or external to the mesh."
Data 175,0,255,	  "Umm.. oh yeah, meshes can be blown-up with multiple explosions, too."
Data 0,0,0,		  ""
Data 0,0,0,		  ""
Data 0,255,0,	  "Anyway, enough of the blurb! Let's go and blow-up some stuff..."
Data 0,0,0,		  ""
Data 0,0,0,		  ""
Data 255,255,255, "Press SPACE to continue"
Data -100

.demo_2_text
Data 530
Data 0,255,0,     "An explosive will be placed at the centre of each shape in turn"
Data 0,255,0,     "Press numeric keypad +/- to alter strength : 000"
Data 255,255,255, "Press SPACE to detonate explosive"
Data -100

.demo_3_text
Data 530
Data 255,0,255,   "An explosive will be placed at the base of each shape in turn"
Data 255,0,255,   "Press numeric keypad +/- to alter strength : 000"
Data 255,255,255, "Press SPACE to detonate explosive"
Data -100

.demo_4_text
Data 530
Data 0,255,255,   "An explosive will be placed directly below each shape in turn"
Data 0,255,255,   "Press numeric keypad +/- to alter strength : 000"
Data 255,255,255, "Press SPACE to detonate explosive"
Data -100

.demo_5_text
Data 530
Data 255,255,0,   "An explosive will orbit around each shape in turn"
Data 255,255,0,   "Press numeric keypad +/- to alter strength : 000"
Data 255,255,255, "Press SPACE to detonate explosive"
Data -100

.demo_6_text
Data 530
Data 0,0,255,     "Shoot down the UFO's with an explosive"
Data 0,0,255,     "Press numeric keypad +/- to alter strength : 000"
Data 255,255,255, "Press left/right ARROW KEYS to move and SPACE to fire"
Data -100

.demo_7_text
Data 530
Data 255,0,0,     "Three different strength explosives have been attached to the UFO"
Data 255,0,0,     "They have been set to explode one after the other"
Data 255,255,255, "Press SPACE to detonate first explosive"
Data -100




;
; Come back, function pointers - all is forgiven!
;
Function demo_handler()

	Select demo_no
		Case 1
			demo_1()
		Case 2
			demo_2()
		Case 3
			demo_3()
		Case 4
			demo_4()
		Case 5
			demo_5()
		Case 6
			demo_6()
		Case 7
			demo_7()
	End Select
	
End Function


;
; Intro screen.
;
Function demo_1()

	If first_call
		create_demo_text()
						
		first_call = False
	Else
		If KeyHit(57)
			demo_no = demo_no + 1
			first_call = True
		EndIf
	EndIf

End Function


;
; Demo screen 2 - central explosives.
;
Function demo_2()

	If first_call
		create_demo_text()
				
		first_call = False

		shape_ctrl.explode_ctrlT = copy_mesh_explode(cube,3)
		PositionEntity shape_ctrl\mesh,-4,-1.5,0
		EntityTexture shape_ctrl\mesh,tex
	
		shape_ctrl = copy_mesh_explode(sphere)
		PositionEntity shape_ctrl\mesh,0,-1.5,0
		EntityTexture shape_ctrl\mesh,tex
	
		shape_ctrl = copy_mesh_explode(cone,2,1)
		PositionEntity shape_ctrl\mesh,4,-1.5,0
		EntityTexture shape_ctrl\mesh,tex
	
		shape_ctrl = copy_mesh_explode(cylinder,2)
		PositionEntity shape_ctrl\mesh,4,3,0
		EntityTexture shape_ctrl\mesh,tex
	
		shape_ctrl = copy_mesh_explode(diamond,3)
		PositionEntity shape_ctrl\mesh,0,3,0
		EntityTexture shape_ctrl\mesh,tex
	
		shape_ctrl = copy_mesh_explode(pyramid,3)
		PositionEntity shape_ctrl\mesh,-4,3,0
		EntityTexture shape_ctrl\mesh,tex
			
		ShowEntity explosive
		exp_ctrl = First explode_ctrlT
		PositionEntity explosive,EntityX(exp_ctrl\mesh),EntityY(exp_ctrl\mesh),EntityZ(exp_ctrl\mesh)

		exp_stren = 20
	EndIf

	If Last explode_ctrlT = Null
		demo_no = demo_no + 1
		first_call = True
		Return
	EndIf

	If KeyDown(74) And exp_stren > 1 Then exp_stren = exp_stren - 1
	If KeyDown(78) And exp_stren < 100 Then exp_stren = exp_stren + 1

	alter\txt$ = Left(alter\txt$,Instr(alter\txt,":")) + " " + Str(exp_stren*0.1)

	If KeyHit(57) Then bang = True

	For ctrl.explode_ctrlT = Each explode_ctrlT
		If ctrl\exploding = False
			If bang
				bang = False
				start_mesh_explode(ctrl,(exp_stren*0.1),200)

				exp_ctrl = After exp_ctrl
				
				If exp_ctrl = Null
					HideEntity explosive
				Else
					PositionEntity explosive,EntityX(exp_ctrl\mesh),EntityY(exp_ctrl\mesh),EntityZ(exp_ctrl\mesh)
				EndIf
			Else				
				TurnEntity ctrl\mesh,1,0,.5
			EndIf
		EndIf
	Next
	
	flash_explosive()

End Function


;
; Demo screen 3 - explosives fixed to base.
;
Function demo_3()

	If first_call
		create_demo_text()
				
		first_call = False
	
		shape_ctrl.explode_ctrlT = copy_mesh_explode(cube,3)
		PositionEntity shape_ctrl\mesh,-4,-1.5,0
		EntityTexture shape_ctrl\mesh,tex
	
		shape_ctrl = copy_mesh_explode(sphere)
		PositionEntity shape_ctrl\mesh,0,-1.5,0
		EntityTexture shape_ctrl\mesh,tex
	
		shape_ctrl = copy_mesh_explode(cone,2,1)
		PositionEntity shape_ctrl\mesh,4,-1.5,0
		EntityTexture shape_ctrl\mesh,tex
	
		shape_ctrl = copy_mesh_explode(cylinder,2)
		PositionEntity shape_ctrl\mesh,4,3,0
		EntityTexture shape_ctrl\mesh,tex
	
		shape_ctrl = copy_mesh_explode(diamond,3)
		PositionEntity shape_ctrl\mesh,0,3,0
		EntityTexture shape_ctrl\mesh,tex
	
		shape_ctrl = copy_mesh_explode(pyramid,3)
		PositionEntity shape_ctrl\mesh,-4,3,0
		EntityTexture shape_ctrl\mesh,tex

		ShowEntity explosive
		exp_ctrl = First explode_ctrlT
		EntityParent explosive,exp_ctrl\mesh
		PositionEntity explosive,0,-1,0
				
		exp_stren = 20
	EndIf

	If Last explode_ctrlT = Null
		demo_no = demo_no + 1
		first_call = True
		Return
	EndIf

	If KeyDown(74) And exp_stren > 1 Then exp_stren = exp_stren - 1
	If KeyDown(78) And exp_stren < 100 Then exp_stren = exp_stren + 1

	alter\txt$ = Left(alter\txt$,Instr(alter\txt,":")) + " " + Str(exp_stren*0.1)

	If KeyHit(57) Then bang = True

	For ctrl.explode_ctrlT = Each explode_ctrlT
		If ctrl\exploding = False
			If bang
				bang = False
				start_mesh_explode(ctrl,(exp_stren*0.1),200,0,-1,0,1)
				
				exp_ctrl = After exp_ctrl
				
				If exp_ctrl = Null
					EntityParent explosive,0
					HideEntity explosive
				Else
					EntityParent explosive,exp_ctrl\mesh
					PositionEntity explosive,0,-1,0
				EndIf
			Else				
				TurnEntity ctrl\mesh,1,0,.5
			EndIf
		EndIf
	Next

	flash_explosive()
	
End Function


;
; Demo screen 4 - static, external explosives.
;
Function demo_4()

	If first_call
		create_demo_text()
				
		first_call = False
	
		shape_ctrl.explode_ctrlT = copy_mesh_explode(cube,3)
		PositionEntity shape_ctrl\mesh,-4,-1.5,0
		EntityTexture shape_ctrl\mesh,tex
	
		shape_ctrl = copy_mesh_explode(sphere)
		PositionEntity shape_ctrl\mesh,0,-1.5,0
		EntityTexture shape_ctrl\mesh,tex
	
		shape_ctrl = copy_mesh_explode(cone,2,1)
		PositionEntity shape_ctrl\mesh,4,-1.5,0
		EntityTexture shape_ctrl\mesh,tex
	
		shape_ctrl = copy_mesh_explode(cylinder,2)
		PositionEntity shape_ctrl\mesh,4,3,0
		EntityTexture shape_ctrl\mesh,tex
	
		shape_ctrl = copy_mesh_explode(diamond,3)
		PositionEntity shape_ctrl\mesh,0,3,0
		EntityTexture shape_ctrl\mesh,tex
	
		shape_ctrl = copy_mesh_explode(pyramid,3)
		PositionEntity shape_ctrl\mesh,-4,3,0
		EntityTexture shape_ctrl\mesh,tex

		ShowEntity explosive
		exp_ctrl = First explode_ctrlT
		PositionEntity explosive,EntityX(exp_ctrl\mesh),EntityY(exp_ctrl\mesh)-2,EntityZ(exp_ctrl\mesh)
				
		exp_stren = 20
	EndIf

	If Last explode_ctrlT = Null
		demo_no = demo_no + 1
		first_call = True
		Return
	EndIf

	If KeyDown(74) And exp_stren > 1 Then exp_stren = exp_stren - 1
	If KeyDown(78) And exp_stren < 100 Then exp_stren = exp_stren + 1

	alter\txt$ = Left(alter\txt$,Instr(alter\txt,":")) + " " + Str(exp_stren*0.1)

	If KeyHit(57) Then bang = True

	For ctrl.explode_ctrlT = Each explode_ctrlT
		If ctrl\exploding = False
			If bang
				bang = False
				start_mesh_explode(ctrl,(exp_stren*0.1),200,0,-2,0)
				
				exp_ctrl = After exp_ctrl
				
				If exp_ctrl = Null
					HideEntity explosive
				Else
					PositionEntity explosive,EntityX(exp_ctrl\mesh),EntityY(exp_ctrl\mesh)-2,EntityZ(exp_ctrl\mesh)
				EndIf
			Else				
				TurnEntity ctrl\mesh,1,0,.5
			EndIf
		EndIf
	Next

	flash_explosive()
	
End Function


;
; Demo screen 5 - orbiting explosives.
;
Function demo_5()

	If first_call
		create_demo_text()
				
		first_call = False
	
		shape_ctrl.explode_ctrlT = copy_mesh_explode(cube,3)
		PositionEntity shape_ctrl\mesh,-4,-1.5,0
		EntityTexture shape_ctrl\mesh,tex
	
		shape_ctrl = copy_mesh_explode(sphere)
		PositionEntity shape_ctrl\mesh,0,-1.5,0
		EntityTexture shape_ctrl\mesh,tex
	
		shape_ctrl = copy_mesh_explode(cone,2,1)
		PositionEntity shape_ctrl\mesh,4,-1.5,0
		EntityTexture shape_ctrl\mesh,tex
	
		shape_ctrl = copy_mesh_explode(cylinder,2)
		PositionEntity shape_ctrl\mesh,4,3,0
		EntityTexture shape_ctrl\mesh,tex
	
		shape_ctrl = copy_mesh_explode(diamond,3)
		PositionEntity shape_ctrl\mesh,0,3,0
		EntityTexture shape_ctrl\mesh,tex
	
		shape_ctrl = copy_mesh_explode(pyramid,3)
		PositionEntity shape_ctrl\mesh,-4,3,0
		EntityTexture shape_ctrl\mesh,tex

		exp_ctrl = First explode_ctrlT
		PositionEntity exp_piv,EntityX(exp_ctrl\mesh),EntityY(exp_ctrl\mesh),EntityZ(exp_ctrl\mesh)
		EntityParent explosive,exp_piv
		ShowEntity explosive
		PositionEntity explosive,0,-2,0
				
		exp_stren = 20
	EndIf

	If Last explode_ctrlT = Null
		demo_no = demo_no + 1
		first_call = True
		RotateEntity exp_piv,0,0,0
		Return
	EndIf

	TurnEntity exp_piv,0,0,-1.5
	
	flash_explosive()

	If KeyDown(74) And exp_stren > 1 Then exp_stren = exp_stren - 1
	If KeyDown(78) And exp_stren < 100 Then exp_stren = exp_stren + 1

	alter\txt$ = Left(alter\txt$,Instr(alter\txt,":")) + " " + Str(exp_stren*0.1)

	If KeyHit(57) Then bang = True

	For ctrl.explode_ctrlT = Each explode_ctrlT
		If ctrl\exploding = False
			If bang
				bang = False
				
				ex# = EntityX(explosive,1) - EntityX(ctrl\mesh)
				ey# = EntityY(explosive,1) - EntityY(ctrl\mesh)
				ez# = EntityZ(explosive,1) - EntityZ(ctrl\mesh)
				start_mesh_explode(ctrl,(exp_stren*0.1),200,ex,ey,ez)
				
				exp_ctrl = After exp_ctrl
				
				If exp_ctrl = Null
					EntityParent explosive,0
					HideEntity explosive
				Else
					PositionEntity exp_piv,EntityX(exp_ctrl\mesh),EntityY(exp_ctrl\mesh),EntityZ(exp_ctrl\mesh)
					RotateEntity exp_piv,0,0,0
				EndIf
			Else				
				TurnEntity ctrl\mesh,1,0,.5
			EndIf
		EndIf
	Next
	
End Function


;
; Demo screen 6 - shoot the UFO's.
;
Function demo_6()

	If first_call
		create_demo_text()
				
		first_call = False
	
		ufo_ctrl.explode_ctrlT = copy_mesh_explode(ufo)
		RotateMesh ufo_ctrl\mesh,3,0,0
		ScaleMesh ufo_ctrl\mesh,.9,.9,.9
		TurnEntity ufo_ctrl\mesh,0,60,0
		PositionEntity ufo_ctrl\mesh,0,3,0
		EntityTexture ufo_ctrl\mesh,tex2
		EntityType ufo_ctrl\mesh,UFO_TYPE

		ufo_ctrl = copy_mesh_explode(ufo)
		RotateMesh ufo_ctrl\mesh,3,0,0
		ScaleMesh ufo_ctrl\mesh,.5,.5,.5
		TurnEntity ufo_ctrl\mesh,0,120,0
		PositionEntity ufo_ctrl\mesh,-4,1.5,0
		EntityTexture ufo_ctrl\mesh,tex2
		EntityType ufo_ctrl\mesh,UFO_TYPE

		ufo_ctrl = copy_mesh_explode(ufo)
		RotateMesh ufo_ctrl\mesh,3,0,0
		ScaleMesh ufo_ctrl\mesh,.7,.7,.7
		TurnEntity ufo_ctrl\mesh,0,180,0
		PositionEntity ufo_ctrl\mesh,3.5,1.2,0
		EntityTexture ufo_ctrl\mesh,tex2
		EntityType ufo_ctrl\mesh,UFO_TYPE

		ufo_ctrl = copy_mesh_explode(ufo)
		RotateMesh ufo_ctrl\mesh,3,0,0
		ScaleMesh ufo_ctrl\mesh,.4,.4,.4
		TurnEntity ufo_ctrl\mesh,0,240,0
		PositionEntity ufo_ctrl\mesh,-.5,.5,0
		EntityTexture ufo_ctrl\mesh,tex2
		EntityType ufo_ctrl\mesh,UFO_TYPE

		PositionEntity explosive,0,-4,0
		ShowEntity explosive
		loaded = True
				
		exp_stren = 20
		
		Collisions BOMB_TYPE,UFO_TYPE,2,1
	EndIf

	If CountCollisions(explosive)
		ec = EntityCollided(explosive,UFO_TYPE)

		For ctrl.explode_ctrlT = Each explode_ctrlT
			If ec = ctrl\mesh
				cx# = CollisionX(explosive,1) - EntityX(ctrl\mesh)
				cy# = CollisionY(explosive,1) - EntityY(ctrl\mesh)
				cz# = CollisionZ(explosive,1) - EntityZ(ctrl\mesh)

				start_mesh_explode(ctrl,(exp_stren*0.1),200,cx,cy,cz)

				EntityType ctrl\mesh,0
				Exit
			EndIf
		Next
		
		loaded = True
		HideEntity explosive
		PositionEntity explosive,0,-4,0
		ShowEntity explosive
	EndIf

	If Last explode_ctrlT = Null
		demo_no = demo_no + 1
		first_call = True
		ClearCollisions
		HideEntity explosive
		Return
	EndIf
	
	If KeyDown(74) And exp_stren > 1 Then exp_stren = exp_stren - 1
	If KeyDown(78) And exp_stren < 100 Then exp_stren = exp_stren + 1

	alter\txt$ = Left(alter\txt$,Instr(alter\txt,":")) + " " + Str(exp_stren*0.1)

	If KeyHit(57) Then loaded = False

	If loaded
		If KeyDown(203) And EntityX(explosive) > -7 Then TranslateEntity explosive,-0.1,0,0
		If KeyDown(205) And EntityX(explosive) < 7 Then TranslateEntity explosive,0.1,0,0
	Else
		If EntityY(explosive) < 5
			TranslateEntity explosive,0,0.2,0
		Else
			FlushKeys
			loaded = True
			HideEntity explosive
			PositionEntity explosive,0,-4,0
			ShowEntity explosive
		EndIf
	EndIf
		
	For ctrl.explode_ctrlT = Each explode_ctrlT
		If Not ctrl\exploding Then TurnEntity ctrl\mesh,0,5,0
	Next

	flash_explosive()
	
End Function


;
; Demo screen 7 - destroy UFO with multiple explosives.
;
Function demo_7()

	If first_call
		create_demo_text()
				
		first_call = False
		explode_chain = False
		chain_timer = 0

		PositionEntity exp_piv2,0,1.5,0
		EntityParent exp_piv,exp_piv2
		PositionEntity exp_piv,0,0,0
					
		ufo_ctrl.explode_ctrlT = copy_mesh_explode(ufo,1)
		EntityParent ufo_ctrl\mesh,exp_piv
		PositionEntity ufo_ctrl\mesh,0,0,0
		EntityTexture ufo_ctrl\mesh,tex2

		EntityParent explosive,exp_piv
		PositionEntity explosive,-2.7,-0.2,0
		ShowEntity explosive

		EntityParent explosive2,exp_piv
		PositionEntity explosive2,2.7,-0.2,0
		ShowEntity explosive2

		EntityParent explosive3,exp_piv
		PositionEntity explosive3,0,-1,0
		ShowEntity explosive3
		
		RotateEntity exp_piv,3,0,0
	EndIf

	ctrl.explode_ctrlT = First explode_ctrlT

	If ctrl = Null
		demo_no = 1
		first_call = True
		EntityParent exp_piv,0
		RotateEntity exp_piv,0,0,0
		RotateEntity exp_piv2,0,0,0
		Return
	EndIf

	If KeyHit(57) And (Not explode_chain) Then explode_chain = True

	If explode_chain
		Select chain_timer
			Case 0
				ex# = EntityX(explosive)
				ey# = EntityY(explosive)
				ez# = EntityZ(explosive)
				EntityParent explosive,0
				HideEntity explosive
				PositionEntity flash,EntityX(explosive,1),EntityY(explosive,1),EntityZ(explosive,1)
				ScaleSprite flash,1,1
				ShowEntity flash
				start_mesh_explode(ctrl,1,200,ex,ey,ez,1)
			Case 20
				ex# = EntityX(explosive2)
				ey# = EntityY(explosive2)
				ez# = EntityZ(explosive2)
				EntityParent explosive2,0
				HideEntity explosive2
				PositionEntity flash,EntityX(explosive2,1),EntityY(explosive2,1),EntityZ(explosive2,1)
				ScaleSprite flash,2,2
				ShowEntity flash
				start_mesh_explode(ctrl,2,200,ex,ey,ez,1)
			Case 40
				ex# = EntityX(explosive3)
				ey# = EntityY(explosive3)
				ez# = EntityZ(explosive3)
				EntityParent explosive3,0
				HideEntity explosive3
				PositionEntity flash,EntityX(explosive3,1),EntityY(explosive3,1),EntityZ(explosive3,1)
				ScaleSprite flash,5,5
				ShowEntity flash
				start_mesh_explode(ctrl,5,200,ex,ey,ez,1)
			Case 2,22,42
				HideEntity flash	
		End Select
		
		chain_timer = chain_timer + 1
	EndIf
		
	If Not ctrl\exploding Then TurnEntity exp_piv2,0,5,0

	flash_explosive()
	
End Function


;
; Alternates colour of explosive spheres, to make them flash.
;
Function flash_explosive()

	exp_flash = exp_flash + 1

	If exp_flash = 4
		EntityColor explosive,0,0,255
		EntityColor explosive2,0,0,255
		EntityColor explosive3,0,0,255
	ElseIf exp_flash = 8
		EntityColor explosive,255,255,0
		EntityColor explosive2,255,255,0
		EntityColor explosive3,255,255,0
		exp_flash = 0
	EndIf

End Function


;
; Creates all text types required by the current demo screen.
;
Function create_demo_text()

	free_demo_text()

	Select demo_no
		Case 1
			Restore demo_1_text
		Case 2
			Restore demo_2_text		
		Case 3
			Restore demo_3_text		
		Case 4
			Restore demo_4_text		
		Case 5
			Restore demo_5_text		
		Case 6
			Restore demo_6_text		
		Case 7
			Restore demo_7_text		
	End Select

	Read text_y
	
	Repeat
		Read r : If r = -100 Then Exit
		Read g,b
		Read txt$

		t.demo_textT = New demo_textT
		t\y = text_y
		t\txt$ = txt$
		t\r = r
		t\g = g
		t\b = b
		
		text_y = text_y + 20

		If Right(txt$,3) = "000" Then alter = t
	Forever

End Function


;
; Deletes all currently defined demo text types.
;
Function free_demo_text()

	For t.demo_textT = Each demo_textT
		Delete t
	Next

End Function




;
; Updates ALL explodable meshes that are currently exploding.
;
Function update_mesh_explode()

	For this.explode_ctrlT = Each explode_ctrlT
		
		If this\exploding
			
			If this\life
				tri.tri_linkT = this\tri_list
			
				If this\life <= this\fade_start
					EntityAlpha this\mesh,this\life * this\fader
				EndIf
				
				; Update all tris in linked list.
				While tri <> Null
					v0 = TriangleVertex(tri\surf,tri\tri,0)
					v1 = TriangleVertex(tri\surf,tri\tri,1)
					v2 = TriangleVertex(tri\surf,tri\tri,2)
					
					; Get 'centre of tri' vertex.
					cx# = VertexX(tri\surf,v2+1)
					cy# = VertexY(tri\surf,v2+1)
					cz# = VertexZ(tri\surf,v2+1)

					; Load scratch pad tri mesh.
					VertexCoords tm_surf,0,VertexX(tri\surf,v0)-cx,VertexY(tri\surf,v0)-cy,VertexZ(tri\surf,v0)-cz
					VertexCoords tm_surf,1,VertexX(tri\surf,v1)-cx,VertexY(tri\surf,v1)-cy,VertexZ(tri\surf,v1)-cz
					VertexCoords tm_surf,2,VertexX(tri\surf,v2)-cx,VertexY(tri\surf,v2)-cy,VertexZ(tri\surf,v2)-cz
					VertexNormal tm_surf,0,VertexNX(tri\surf,v0),VertexNY(tri\surf,v0),VertexNZ(tri\surf,v0)
					VertexNormal tm_surf,1,VertexNX(tri\surf,v1),VertexNY(tri\surf,v1),VertexNZ(tri\surf,v1)
					VertexNormal tm_surf,2,VertexNX(tri\surf,v2),VertexNY(tri\surf,v2),VertexNZ(tri\surf,v2)
			
					; Do rotations.
					RotateMesh tm_mesh,tri\pitch,tri\yaw,tri\roll

					; Copy back to mesh tri adding translation.
					VertexCoords tri\surf,v0,VertexX(tm_surf,0)+cx+tri\dx,VertexY(tm_surf,0)+cy+tri\dy,VertexZ(tm_surf,0)+cz+tri\dz
					VertexCoords tri\surf,v1,VertexX(tm_surf,1)+cx+tri\dx,VertexY(tm_surf,1)+cy+tri\dy,VertexZ(tm_surf,1)+cz+tri\dz
					VertexCoords tri\surf,v2,VertexX(tm_surf,2)+cx+tri\dx,VertexY(tm_surf,2)+cy+tri\dy,VertexZ(tm_surf,2)+cz+tri\dz
					VertexNormal tri\surf,v0,VertexNX(tm_surf,0),VertexNY(tm_surf,0),VertexNZ(tm_surf,0)
					VertexNormal tri\surf,v1,VertexNX(tm_surf,1),VertexNY(tm_surf,1),VertexNZ(tm_surf,1)
					VertexNormal tri\surf,v2,VertexNX(tm_surf,2),VertexNY(tm_surf,2),VertexNZ(tm_surf,2)

					VertexCoords tri\surf,v2+1,cx+tri\dx,cy+tri\dy,cz+tri\dz
					
					; Update gravity effect, ready for next update.
					tri\dy = tri\dy-GRAVITY
					tri\dx = tri\dx/XZ_DECAY
					tri\dz = tri\dz/XZ_DECAY
					
					tri = tri\prev
				Wend
			
				this\life = this\life - 1
			Else
				; Mesh has finished exploding.
				free_mesh_explode(this)
			EndIf

		EndIf

	Next

End Function


;
; Creates a subdivided (optional), unwelded copy of an existing mesh.
; Explode info for all tris in the new mesh are kept in a separate linked list
; contained in the control variable returned to the caller.
; Note: the source mesh being copied remains completely unaltered.
;
; Params:
; s_mesh     - Source mesh to be copied.
; divs       - Number of times to perform x4 subdivision on each source tri.
; keep_surfs - True to keep the same amount of surfaces in
;              the copy as in the source mesh.
;              False (Default) to copy all tris in the source mesh
;              to a single surface in the copy.
;
; Returns:
; Pointer to the control variable (of type explode_ctrlT) for the
; new explodable mesh created.
;
Function copy_mesh_explode.explode_ctrlT(s_mesh,divs=0,keep_surfs%=False)

	tri_list.tri_linkT = Null

	d_mesh = CreateMesh()
		
	For sno = 1 To CountSurfaces(s_mesh)
		
		s_surf = GetSurface(s_mesh,sno)
		nt = CountTriangles(s_surf)

		If sno = 1 Or keep_surfs Then d_surf = CreateSurface(d_mesh)
		
		For tno = 0 To nt-1
			tri_list = copy_tri_explode(s_surf,tno,d_surf,divs,tri_list)
		Next
	
	Next

	; Create and return this explodable mesh's control variable.
	ctrl.explode_ctrlT = New explode_ctrlT
	ctrl\tri_list = tri_list
	ctrl\mesh = d_mesh
	ctrl\exploding = False
		
	Return ctrl
	
End Function


;
; Adds an unwelded copy of a tri from the source surface to the destination
; surface, performing any subdivision requested.
;
; Subdivision is done by calling this function recursively, splitting the
; source tri into 4 unwelded tris each time, like so:
;
;           v1
;           /\ 
;         /____\
;       /  \  /  \
;     /_____\/_____\
;    v0            v2
;
; Params:
; s_surf   - Source surface containing tri to be copied.
; tri      - Index of tri to be copied.
; d_surf   - Destination surface to copy the source tri to.
; divs     - Number of times to perform x4 subdivision on the source tri.
; tri_list - Current top item in the linked list.
; reset    - True (default) to clear scratch pad mesh.
;            False to not clear scratch pad mesh (used internally only!).
;
; Returns:
; The current top item in the linked list.
;
Function copy_tri_explode.tri_linkT(s_surf,tri,d_surf,divs,tri_list.tri_linkT,reset%=True)

	If reset Then ClearSurface(sd_surf)
	
	sv0 = TriangleVertex(s_surf,tri,0)
	sv1 = TriangleVertex(s_surf,tri,1)
	sv2 = TriangleVertex(s_surf,tri,2)

	; Get coords of all 3 vertices of source tri.
	x0# = VertexX(s_surf,sv0)
	y0# = VertexY(s_surf,sv0)
	z0# = VertexZ(s_surf,sv0)
	x1# = VertexX(s_surf,sv1)
	y1# = VertexY(s_surf,sv1)
	z1# = VertexZ(s_surf,sv1)
	x2# = VertexX(s_surf,sv2)
	y2# = VertexY(s_surf,sv2)
	z2# = VertexZ(s_surf,sv2)
	
	; Get normals of all 3 vertices of source tri.
	nx0# = VertexNX(s_surf,sv0)
	ny0# = VertexNY(s_surf,sv0)
	nz0# = VertexNZ(s_surf,sv0)
	nx1# = VertexNX(s_surf,sv1)
	ny1# = VertexNY(s_surf,sv1)
	nz1# = VertexNZ(s_surf,sv1)
	nx2# = VertexNX(s_surf,sv2)
	ny2# = VertexNY(s_surf,sv2)
	nz2# = VertexNZ(s_surf,sv2)

	; Get tex coords of all 3 vertices of source tri.
	u0# = VertexU(s_surf,sv0)
	v0# = VertexV(s_surf,sv0)
	w0# = VertexW(s_surf,sv0)
	u1# = VertexU(s_surf,sv1)
	v1# = VertexV(s_surf,sv1)
	w1# = VertexW(s_surf,sv1)
	u2# = VertexU(s_surf,sv2)
	v2# = VertexV(s_surf,sv2)
	w2# = VertexW(s_surf,sv2)

	; Get colour of all 3 vertices of source tri.
	r0# = VertexRed(s_surf,sv0)
	g0# = VertexGreen(s_surf,sv0)
	b0# = VertexBlue(s_surf,sv0)
	r1# = VertexRed(s_surf,sv1)
	g1# = VertexGreen(s_surf,sv1)
	b1# = VertexBlue(s_surf,sv1)
	r2# = VertexRed(s_surf,sv2)
	g2# = VertexGreen(s_surf,sv2)
	b2# = VertexBlue(s_surf,sv2)

	If divs
		; Calculate coords of the 3 discrete vertices used by subdivision.
		x3# = (x1+x0)/2.0
		y3# = (y1+y0)/2.0
		z3# = (z1+z0)/2.0
		x4# = (x2+x1)/2.0
		y4# = (y2+y1)/2.0
		z4# = (z2+z1)/2.0
		x5# = (x0+x2)/2.0
		y5# = (y0+y2)/2.0
		z5# = (z0+z2)/2.0
		
		; Calculate normals of the 3 discrete vertices used by subdivision.
		nx3# = (nx1+nx0)/2.0
		ny3# = (ny1+ny0)/2.0
		nz3# = (nz1+nz0)/2.0
		nx4# = (nx2+nx1)/2.0
		ny4# = (ny2+ny1)/2.0
		nz4# = (nz2+nz1)/2.0
		nx5# = (nx0+nx2)/2.0
		ny5# = (ny0+ny2)/2.0
		nz5# = (nz0+nz2)/2.0
		
		; Unify.
		nl# = Sqr(nx3*nx3 + ny3*ny3 + nz3*nz3)
		nx3 = nx3 / nl#
		ny3 = ny3 / nl#
		nz3 = nz3 / nl#
		nl# = Sqr(nx4*nx4 + ny4*ny4 + nz4*nz4)
		nx4 = nx4 / nl#
		ny4 = ny4 / nl#
		nz4 = nz4 / nl#
		nl# = Sqr(nx5*nx5 + ny5*ny5 + nz5*nz5)
		nx5 = nx5 / nl#
		ny5 = ny5 / nl#
		nz5 = nz5 / nl#
	
		; Calculate tex coords of the 3 discrete vertices used by subdivision.
		u3# = (u1+u0)/2.0
		v3# = (v1+v0)/2.0
		w3# = (w1+w0)/2.0
		u4# = (u2+u1)/2.0
		v4# = (v2+v1)/2.0
		w4# = (w2+w1)/2.0
		u5# = (u0+u2)/2.0
		v5# = (v0+v2)/2.0
		w5# = (w0+w2)/2.0
	
		; Calculate colour of the 3 discrete vertices used by subdivision.
		r3# = (r1+r0)/2.0
		g3# = (g1+g0)/2.0
		b3# = (b1+b0)/2.0
		r4# = (r2+r1)/2.0
		g4# = (g2+g1)/2.0
		b4# = (b2+b1)/2.0
		r5# = (r0+r2)/2.0
		g5# = (g0+g2)/2.0
		b5# = (b0+b2)/2.0
		
		; Add the 4 unwelded tris comprising the subdivision to the
		; temporary, scratch pad mesh surface.
		tv0 = AddVertex(sd_surf,x0,y0,z0)
		tv3 = AddVertex(sd_surf,x3,y3,z3)
		tv5 = AddVertex(sd_surf,x5,y5,z5)
		tri0 = AddTriangle(sd_surf,tv0,tv3,tv5)
		VertexNormal sd_surf,tv0,nx0,ny0,nz0
		VertexNormal sd_surf,tv3,nx3,ny3,nz3
		VertexNormal sd_surf,tv5,nx5,ny5,nz5
		VertexColor sd_surf,tv0,r0,g0,b0
		VertexColor sd_surf,tv3,r3,g3,b3
		VertexColor sd_surf,tv5,r5,g5,b5
		VertexTexCoords sd_surf,tv0,u0,v0,w0
		VertexTexCoords sd_surf,tv3,u3,v3,w3
		VertexTexCoords sd_surf,tv5,u5,v5,w5
		
		tv1 = AddVertex(sd_surf,x1,y1,z1)
		tv4 = AddVertex(sd_surf,x4,y4,z4)
		tv3b = AddVertex(sd_surf,x3,y3,z3)
		tri1 = AddTriangle(sd_surf,tv1,tv4,tv3b)
		VertexNormal sd_surf,tv1,nx1,ny1,nz1
		VertexNormal sd_surf,tv4,nx4,ny4,nz4
		VertexNormal sd_surf,tv3b,nx3,ny3,nz3
		VertexColor sd_surf,tv1,r1,g1,b1
		VertexColor sd_surf,tv4,r4,g4,b4
		VertexColor sd_surf,tv3b,r3,g3,b3
		VertexTexCoords sd_surf,tv1,u1,v1,w1
		VertexTexCoords sd_surf,tv4,u4,v4,w4
		VertexTexCoords sd_surf,tv3b,u3,v3,w3

		tv2 = AddVertex(sd_surf,x2,y2,z2)
		tv5b = AddVertex(sd_surf,x5,y5,z5)
		tv4b = AddVertex(sd_surf,x4,y4,z4)
		tri2 = AddTriangle(sd_surf,tv2,tv5b,tv4b)
		VertexNormal sd_surf,tv2,nx2,ny2,nz2
		VertexNormal sd_surf,tv5b,nx5,ny5,nz5
		VertexNormal sd_surf,tv4b,nx4,ny4,nz4
		VertexColor sd_surf,tv2,r2,g2,b2
		VertexColor sd_surf,tv5b,r5,g5,b5
		VertexColor sd_surf,tv4b,r4,g4,b4
		VertexTexCoords sd_surf,tv2,u2,v2,w2
		VertexTexCoords sd_surf,tv5b,u5,v5,w5
		VertexTexCoords sd_surf,tv4b,u4,v4,w4
	
		tv3c = AddVertex(sd_surf,x3,y3,z3)
		tv4c = AddVertex(sd_surf,x4,y4,z4)
		tv5c = AddVertex(sd_surf,x5,y5,z5)
		tri3 = AddTriangle(sd_surf,tv3c,tv4c,tv5c)
		VertexNormal sd_surf,tv3c,nx3,ny3,nz3
		VertexNormal sd_surf,tv4c,nx4,ny4,nz4
		VertexNormal sd_surf,tv5c,nx5,ny5,nz5
		VertexColor sd_surf,tv3c,r3,g3,b3
		VertexColor sd_surf,tv4c,r4,g4,b4
		VertexColor sd_surf,tv5c,r5,g5,b5
		VertexTexCoords sd_surf,tv3c,u3,v3,w3
		VertexTexCoords sd_surf,tv4c,u4,v4,w4
		VertexTexCoords sd_surf,tv5c,u5,v5,w5
	
		divs = divs - 1
	
		tri_list = copy_tri_explode(sd_surf,tri0,d_surf,divs,tri_list,False)
		tri_list = copy_tri_explode(sd_surf,tri1,d_surf,divs,tri_list,False)
		tri_list = copy_tri_explode(sd_surf,tri2,d_surf,divs,tri_list,False)
		tri_list = copy_tri_explode(sd_surf,tri3,d_surf,divs,tri_list,False)

	Else

		dv0 = AddVertex(d_surf,x0,y0,z0)
		dv1 = AddVertex(d_surf,x1,y1,z1)
		dv2 = AddVertex(d_surf,x2,y2,z2)

		; Calculate and add a lone 'centre of tri' vertex (needed for
		; per-tri rotations and blast trajectory calculation).
		tx# = (x2+x1)/2.0
		ty# = (y2+y1)/2.0
		tz# = (z2+z1)/2.0
		cvx# = tx - ((tx-x0)/3.0)
		cvy# = ty - ((ty-y0)/3.0)
		cvz# = tz - ((tz-z0)/3.0)
		AddVertex(d_surf,cvx,cvy,cvz)
		
		real_tri = AddTriangle(d_surf,dv0,dv1,dv2)

		VertexNormal d_surf,dv0,nx0,ny0,nz0
		VertexNormal d_surf,dv1,nx1,ny1,nz1
		VertexNormal d_surf,dv2,nx2,ny2,nz2
		VertexColor d_surf,dv0,r0,g0,b0
		VertexColor d_surf,dv1,r1,g1,b1
		VertexColor d_surf,dv2,r2,g2,b2
		VertexTexCoords d_surf,dv0,u0,v0,w0
		VertexTexCoords d_surf,dv1,u1,v1,w1
		VertexTexCoords d_surf,dv2,u2,v2,w2

		; Add this tri to the linked list.
		link.tri_linkT = New tri_linkT
		link\prev = tri_list
		link\surf = d_surf
		link\tri = real_tri
		
		tri_list = link	
	EndIf
	
	Return tri_list
	
End Function


;
; Initializes and authorizes a mesh to start exploding.
; Note: can handle starting an explosion on a mesh that's already exploding.
;
; Params:
; ctrl        - Control variable of the mesh to start exploding.
; blast       - Strength of the blast to explode the mesh with.
; life        - Duration of the explosion animation (in frames).
; bfx,bfy,bfz - Blast focus coords (point in 3D space where the explosion is to
;               occur), relative to mesh centre.
; loc         - True to set blast focus using local space coords.
;               False (default) to set blast focus using coords aligned to world
;               axes. e.g. a blast focus of 0,-1,0 will cause an explosion directly
;               below the mesh's centre, regardless of the mesh's current rotation.
;
Function start_mesh_explode(ctrl.explode_ctrlT,blast#=1,life%=100,bfx#=0,bfy#=0,bfz#=0,loc%=False)

	If loc
		If ctrl\exploding
			; Temporarily re-instate mesh's original rotation so we can
			; calculate local blast focus coords.
			RotateEntity ctrl\mesh,ctrl\o_pitch,ctrl\o_yaw,ctrl\o_roll,1

			; Convert blast focus to world-aligned coords.	
			TFormPoint bfx,bfy,bfz,ctrl\mesh,0
			bfx = TFormedX() - EntityX(ctrl\mesh,1)
			bfy = TFormedY() - EntityY(ctrl\mesh,1)
			bfz = TFormedZ() - EntityZ(ctrl\mesh,1)

			RotateEntity ctrl\mesh,0,0,0,1
		Else
			; Convert blast focus to world-aligned coords.	
			TFormPoint bfx,bfy,bfz,ctrl\mesh,0
			bfx = TFormedX() - EntityX(ctrl\mesh,1)
			bfy = TFormedY() - EntityY(ctrl\mesh,1)
			bfz = TFormedZ() - EntityZ(ctrl\mesh,1)
		EndIf
	EndIf

	If Not ctrl\exploding
		; Align mesh axes to world axes, keeping mesh's visible
		; orientation (needed for gravity to work).
		ctrl\o_pitch = EntityPitch(ctrl\mesh,1)
		ctrl\o_yaw = EntityYaw(ctrl\mesh,1)
		ctrl\o_roll = EntityRoll(ctrl\mesh,1)
		RotateEntity ctrl\mesh,0,0,0,1
		RotateMesh ctrl\mesh,ctrl\o_pitch,ctrl\o_yaw,ctrl\o_roll
	EndIf
	
	tri.tri_linkT = ctrl\tri_list

	If JIGGLE_TRAJ
		While tri <> Null
		
			; Get 'centre of tri' vertex.
			cv = TriangleVertex(tri\surf,tri\tri,2) + 1
			cvx# = VertexX(tri\surf,cv)
			cvy# = VertexY(tri\surf,cv)
			cvz# = VertexZ(tri\surf,cv)
	
			; Use the vector from bfx,bfy,bfz (blast focus) to cvx,cvy,cvz ('centre
			; of tri' vert) as the trajectory, adding a random 'jiggle' factor.
			VertexCoords vm_surf,0,cvx-bfx,cvy-bfy,cvz-bfz
			RotateMesh vm_mesh,Rnd(-10,10),Rnd(-10,10),Rnd(-10,10)
	
			tvx# = VertexX(vm_surf,0)
			tvy# = VertexY(vm_surf,0)
			tvz# = VertexZ(vm_surf,0)
			tvl# = Sqr(tvx*tvx + tvy*tvy + tvz*tvz)

			; Set velocity along trajectory vector based on proximity to
			; explosion (blast focus), explosion strength and a random factor.
			v_scale# = tvl * Rnd(20.0,50.0)

			; Add to current dx,dy,dz in case this mesh is already exploding!
			tri\dx = tri\dx + (((tvx/tvl)/v_scale) * blast)
			tri\dy = tri\dy + (((tvy/tvl)/v_scale) * blast)
			tri\dz = tri\dz + (((tvz/tvl)/v_scale) * blast)
			
			; Base rotation on velocity.
			rot# = (Abs(tri\dx) + Abs(tri\dy) + Abs(tri\dz)) * 100
			tri\pitch = Rnd(-rot,rot)
			tri\yaw = Rnd(-rot,rot)
			tri\roll = Rnd(-rot,rot)
	
			tri = tri\prev
		Wend
	Else
		While tri <> Null
		
			; Get 'centre of tri' vertex.
			cv = TriangleVertex(tri\surf,tri\tri,2) + 1
			cvx# = VertexX(tri\surf,cv)
			cvy# = VertexY(tri\surf,cv)
			cvz# = VertexZ(tri\surf,cv)
	
			; Use the vector from bfx,bfy,bfz (blast focus) to cvx,cvy,cvz
			; ('centre of tri' vert) as the trajectory.
			tvx# = cvx - bfx
			tvy# = cvy - bfy
			tvz# = cvz - bfz
			tvl# = Sqr(tvx*tvx + tvy*tvy + tvz*tvz)

			; Set velocity along trajectory vector based on proximity to
			; explosion (blast focus), explosion strength and a random factor.
			v_scale# = tvl * Rnd(20.0,50.0)

			; Add to current dx,dy,dz in case this mesh is already exploding!
			tri\dx = tri\dx + (((tvx/tvl)/v_scale) * blast)
			tri\dy = tri\dy + (((tvy/tvl)/v_scale) * blast)
			tri\dz = tri\dz + (((tvz/tvl)/v_scale) * blast)

			; Base rotation on velocity.
			rot# = (Abs(tri\dx) + Abs(tri\dy) + Abs(tri\dz)) * 100
			tri\pitch = Rnd(-rot,rot)
			tri\yaw = Rnd(-rot,rot)
			tri\roll = Rnd(-rot,rot)

			tri = tri\prev
		Wend
	EndIf

	ctrl\life = life
	ctrl\fade_start = Ceil(Float(life)*FADE_START#)
	ctrl\fader = 1.0/(ctrl\fade_start+1)
	ctrl\exploding = True

	EntityFX ctrl\mesh,16

End Function


;
; Free all mem used by an explodable mesh created with copy_mesh_explode().
;
; Params:
; ctrl - Control variable of the explodable mesh to be freed.
;
Function free_mesh_explode(ctrl.explode_ctrlT)

	this.tri_linkT = ctrl\tri_list
	
	While this <> Null
		delme.tri_linkT = this
		this = delme\prev
		Delete delme
	Wend
	
	FreeEntity ctrl\mesh
	Delete ctrl
	
End Function


;
; Display debug info.
;
Function show_info()
	
	If fps_timeout
		frame_count = frame_count + 1

		If MilliSecs() > fps_timeout Then
			fps_timeout = MilliSecs() + 1000 
			fps = frame_count 
			frame_count = 0 
		
			If fps < slowest_fps Or slowest_fps = 0 Then slowest_fps = fps
		EndIf 
		
		If frame_time > slowest_frame Then slowest_frame = frame_time
		
		Color 0,255,0
		Text 10,10," Triangles: " + TrisRendered()
		Color 255,255,0
		Text 10,25," Millisecs: " + frame_time
		Text 10,40,"   Slowest: " + slowest_frame
		Color 0,255,255
		Text 10,55,"       FPS: " + fps
		Text 10,70,"     Worst: " + slowest_fps
		Color 255,255,255
	Else
		; First call initialization.
		fps_timeout = MilliSecs() + 1000 
	EndIf
	
End Function