Hi, back again after much experimenting and head scratching. Think I've managed to crack the prob though. :) See code below.
I decided the best way to go was not to have a separate entity for each tri but instead to keep everything contained within a single mesh (and, optionally, a single surface). I went with makakoman's idea of using a single tri 'scratch pad' mesh to do the per-tri rotations but this proved a bit more complicated than I first thought - basically, I had to calculate a 'centre of tri' vertex for every tri in order to do it. It seems to run fairly quickly, too.
I also built in recursive tri subdivision (my brain 'erts) in order to give the explosions lots of pieces to blow apart. This is useful when exploding meshes with very large tris, like cubes which only normally have 2 tris per side.
Anyways, let me know what you think.
;
; Explodable mesh demo.
;
; Chris Chadwick 2003
;
Graphics3D 800,600,32
WireFrame 0
AntiAlias 0
SeedRnd MilliSecs()
; Create a check pattern texture.
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
SetBuffer BackBuffer()
Color 255,255,255
; 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.
Const FADE_START# = 0.5
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%
End Type
Global frame_count%
Global fps%
Global fps_timeout%
Global frame_time%
Global slowest_frame%
Global frame_start%
fps_timer = CreateTimer(60)
Global cam = CreateCamera()
PositionEntity cam,0,0,-8
light = CreateLight()
TurnEntity light,0,90,0
; Scratch pad single tri mesh for doing rotations.
Global tri_mesh = CreateMesh()
Global tri_surf = CreateSurface(tri_mesh)
AddVertex(tri_surf,0,0,0)
AddVertex(tri_surf,0,0,0)
AddVertex(tri_surf,0,0,0)
AddTriangle(tri_surf,0,1,2)
HideEntity tri_mesh
; Scratch pad mesh used by copy_tri_explode().
Global t_mesh = CreateMesh()
Global t_surf = CreateSurface(t_mesh)
HideEntity t_mesh
cube = CreateCube()
sphere = CreateSphere(16)
diamond = CreateSphere(2)
pyramid = CreateCone(4)
cone = CreateCone(16)
cylinder = CreateCylinder(16)
shape1.explode_ctrlT = copy_mesh_explode(cube,200,3)
PositionEntity shape1\mesh,-4,-1.5,0
EntityTexture shape1\mesh,tex
shape2.explode_ctrlT = copy_mesh_explode(sphere,200)
PositionEntity shape2\mesh,0,-1.5,0
EntityTexture shape2\mesh,tex
shape3.explode_ctrlT = copy_mesh_explode(cone,200,2)
PositionEntity shape3\mesh,4,-1.5,0
EntityTexture shape3\mesh,tex
shape4.explode_ctrlT = copy_mesh_explode(cylinder,200,2)
PositionEntity shape4\mesh,4,3,0
EntityTexture shape4\mesh,tex
shape5.explode_ctrlT = copy_mesh_explode(diamond,200,3)
PositionEntity shape5\mesh,0,3,0
EntityTexture shape5\mesh,tex
shape6.explode_ctrlT = copy_mesh_explode(pyramid,200,3)
PositionEntity shape6\mesh,-4,3,0
EntityTexture shape6\mesh,tex
FreeEntity cube
FreeEntity sphere
FreeEntity diamond
FreeEntity cone
FreeEntity cylinder
FreeEntity pyramid
; Main loop
While Not KeyHit(1)
frame_start = MilliSecs()
If KeyHit(57) Then bang = True
For ctrl.explode_ctrlT = Each explode_ctrlT
If ctrl\exploding = False
If bang
bang = False
ctrl\exploding = True
EntityFX ctrl\mesh,16
Else
TurnEntity ctrl\mesh,1,0,.5
EndIf
EndIf
Next
update_mesh_explode()
UpdateWorld
RenderWorld
Text 400,550,"Press SPACE to explode a shape!",1
frame_time = MilliSecs() - frame_start
show_info()
WaitTimer(fps_timer)
Flip(1)
Wend
ClearWorld
End
;
; 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)
x0# = VertexX(tri\surf,v0)
y0# = VertexY(tri\surf,v0)
z0# = VertexZ(tri\surf,v0)
x1# = VertexX(tri\surf,v1)
y1# = VertexY(tri\surf,v1)
z1# = VertexZ(tri\surf,v1)
x2# = VertexX(tri\surf,v2)
y2# = VertexY(tri\surf,v2)
z2# = VertexZ(tri\surf,v2)
cx# = VertexX(tri\surf,v2+1)
cy# = VertexY(tri\surf,v2+1)
cz# = VertexZ(tri\surf,v2+1)
; Load scratch pad tri mesh.
VertexCoords tri_surf,0,x0-cx,y0-cy,z0-cz
VertexCoords tri_surf,1,x1-cx,y1-cy,z1-cz
VertexCoords tri_surf,2,x2-cx,y2-cy,z2-cz
VertexNormal tri_surf,0,VertexNX(tri\surf,v0),VertexNY(tri\surf,v0),VertexNZ(tri\surf,v0)
VertexNormal tri_surf,1,VertexNX(tri\surf,v1),VertexNY(tri\surf,v1),VertexNZ(tri\surf,v1)
VertexNormal tri_surf,2,VertexNX(tri\surf,v2),VertexNY(tri\surf,v2),VertexNZ(tri\surf,v2)
; Do rotations.
RotateMesh tri_mesh,tri\pitch,tri\yaw,tri\roll
; Copy back to mesh tri.
x0# = VertexX(tri_surf,0)
y0# = VertexY(tri_surf,0)
z0# = VertexZ(tri_surf,0)
x1# = VertexX(tri_surf,1)
y1# = VertexY(tri_surf,1)
z1# = VertexZ(tri_surf,1)
x2# = VertexX(tri_surf,2)
y2# = VertexY(tri_surf,2)
z2# = VertexZ(tri_surf,2)
VertexCoords tri\surf,v0,x0+cx+tri\dx,y0+cy+tri\dy,z0+cz+tri\dz
VertexCoords tri\surf,v1,x1+cx+tri\dx,y1+cy+tri\dy,z1+cz+tri\dz
VertexCoords tri\surf,v2,x2+cx+tri\dx,y2+cy+tri\dy,z2+cz+tri\dz
VertexNormal tri\surf,v0,VertexNX(tri_surf,0),VertexNY(tri_surf,0),VertexNZ(tri_surf,0)
VertexNormal tri\surf,v1,VertexNX(tri_surf,1),VertexNY(tri_surf,1),VertexNZ(tri_surf,1)
VertexNormal tri\surf,v2,VertexNX(tri_surf,2),VertexNY(tri_surf,2),VertexNZ(tri_surf,2)
VertexCoords tri\surf,v2+1,cx+tri\dx,cy+tri\dy,cz+tri\dz
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.
; life - Duration of explosion animation (in frames) to use when exploded.
; divs - Number of times to perform x4 sub-division 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,life%,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\life = life
ctrl\fade_start = Ceil(Float(life)*FADE_START#)
ctrl\fader = 1.0/(ctrl\fade_start+1)
ctrl\exploding = False
Return ctrl
End Function
;
; Adds an unwelded copy of a tri from the source surface to the destination
; surface, performing any sub-division requested.
;
; Sub-division 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 sub-division 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(t_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 sub-division.
x3# = x0 + ((x1-x0)/2)
y3# = y0 + ((y1-y0)/2)
z3# = z0 + ((z1-z0)/2)
x4# = x1 + ((x2-x1)/2)
y4# = y1 + ((y2-y1)/2)
z4# = z1 + ((z2-z1)/2)
x5# = x2 + ((x0-x2)/2)
y5# = y2 + ((y0-y2)/2)
z5# = z2 + ((z0-z2)/2)
; Calculate normals of the 3 discrete vertices used by sub-division.
nx3# = nx0 + ((nx1-nx0)/2)
ny3# = ny0 + ((ny1-ny0)/2)
nz3# = nz0 + ((nz1-nz0)/2)
nx4# = nx1 + ((nx2-nx1)/2)
ny4# = ny1 + ((ny2-ny1)/2)
nz4# = nz1 + ((nz2-nz1)/2)
nx5# = nx2 + ((nx0-nx2)/2)
ny5# = ny2 + ((ny0-ny2)/2)
nz5# = nz2 + ((nz0-nz2)/2)
; Calculate tex coords of the 3 discrete vertices used by sub-division.
u3# = u0 + ((u1-u0)/2)
v3# = v0 + ((v1-v0)/2)
w3# = w0 + ((w1-w0)/2)
u4# = u1 + ((u2-u1)/2)
v4# = v1 + ((v2-v1)/2)
w4# = w1 + ((w2-w1)/2)
u5# = u2 + ((u0-u2)/2)
v5# = v2 + ((v0-v2)/2)
w5# = w2 + ((w0-w2)/2)
; Calculate colour of the 3 discrete vertices used by sub-division.
r3# = r0 + ((r1-r0)/2)
g3# = g0 + ((g1-g0)/2)
b3# = b0 + ((b1-b0)/2)
r4# = r1 + ((r2-r1)/2)
g4# = g1 + ((g2-g1)/2)
b4# = b1 + ((b2-b1)/2)
r5# = r2 + ((r0-r2)/2)
g5# = g2 + ((g0-g2)/2)
b5# = b2 + ((b0-b2)/2)
; Add the 4 unwelded tris comprising the sub-division to the
; temporary, scratch pad mesh surface.
tv0 = AddVertex(t_surf,x0,y0,z0)
tv3 = AddVertex(t_surf,x3,y3,z3)
tv5 = AddVertex(t_surf,x5,y5,z5)
tri0 = AddTriangle(t_surf,tv0,tv3,tv5)
VertexNormal t_surf,tv0,nx0,ny0,nz0
VertexNormal t_surf,tv3,nx3,ny3,nz3
VertexNormal t_surf,tv5,nx5,ny5,nz5
VertexColor t_surf,tv0,r0,g0,b0
VertexColor t_surf,tv3,r3,g3,b3
VertexColor t_surf,tv5,r5,g5,b5
VertexTexCoords t_surf,tv0,u0,v0,w0
VertexTexCoords t_surf,tv3,u3,v3,w3
VertexTexCoords t_surf,tv5,u5,v5,w5
tv1 = AddVertex(t_surf,x1,y1,z1)
tv4 = AddVertex(t_surf,x4,y4,z4)
tv3b = AddVertex(t_surf,x3,y3,z3)
tri1 = AddTriangle(t_surf,tv1,tv4,tv3b)
VertexNormal t_surf,tv1,nx1,ny1,nz1
VertexNormal t_surf,tv4,nx4,ny4,nz4
VertexNormal t_surf,tv3b,nx3,ny3,nz3
VertexColor t_surf,tv1,r1,g1,b1
VertexColor t_surf,tv4,r4,g4,b4
VertexColor t_surf,tv3b,r3,g3,b3
VertexTexCoords t_surf,tv1,u1,v1,w1
VertexTexCoords t_surf,tv4,u4,v4,w4
VertexTexCoords t_surf,tv3b,u3,v3,w3
tv2 = AddVertex(t_surf,x2,y2,z2)
tv5b = AddVertex(t_surf,x5,y5,z5)
tv4b = AddVertex(t_surf,x4,y4,z4)
tri2 = AddTriangle(t_surf,tv2,tv5b,tv4b)
VertexNormal t_surf,tv2,nx2,ny2,nz2
VertexNormal t_surf,tv5b,nx5,ny5,nz5
VertexNormal t_surf,tv4b,nx4,ny4,nz4
VertexColor t_surf,tv2,r2,g2,b2
VertexColor t_surf,tv5b,r5,g5,b5
VertexColor t_surf,tv4b,r4,g4,b4
VertexTexCoords t_surf,tv2,u2,v2,w2
VertexTexCoords t_surf,tv5b,u5,v5,w5
VertexTexCoords t_surf,tv4b,u4,v4,w4
tv3c = AddVertex(t_surf,x3,y3,z3)
tv4c = AddVertex(t_surf,x4,y4,z4)
tv5c = AddVertex(t_surf,x5,y5,z5)
tri3 = AddTriangle(t_surf,tv3c,tv4c,tv5c)
VertexNormal t_surf,tv3c,nx3,ny3,nz3
VertexNormal t_surf,tv4c,nx4,ny4,nz4
VertexNormal t_surf,tv5c,nx5,ny5,nz5
VertexColor t_surf,tv3c,r3,g3,b3
VertexColor t_surf,tv4c,r4,g4,b4
VertexColor t_surf,tv5c,r5,g5,b5
VertexTexCoords t_surf,tv3c,u3,v3,w3
VertexTexCoords t_surf,tv4c,u4,v4,w4
VertexTexCoords t_surf,tv5c,u5,v5,w5
divs = divs - 1
tri_list = copy_tri_explode(t_surf,tri0,d_surf,divs,tri_list,False)
tri_list = copy_tri_explode(t_surf,tri1,d_surf,divs,tri_list,False)
tri_list = copy_tri_explode(t_surf,tri2,d_surf,divs,tri_list,False)
tri_list = copy_tri_explode(t_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).
tx# = x1 + ((x2-x1)/2)
ty# = y1 + ((y2-y1)/2)
tz# = z1 + ((z2-z1)/2)
cvx# = tx - ((tx-x0)/3)
cvy# = ty - ((ty-y0)/3)
cvz# = tz - ((tz-z0)/3)
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
r = Rnd(10,80)
link\dx = cvx/r
link\dy = cvy/r
link\dz = cvz/r
link\pitch = Rnd(-10,10)
link\yaw = Rnd(-10,10)
link\roll = Rnd(-10,10)
tri_list = link
EndIf
Return tri_list
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()
frame_count = frame_count + 1
If MilliSecs() > fps_timeout Then
fps_timeout = MilliSecs() + 1000
fps = frame_count
frame_count = 0
EndIf
If frame_time > slowest_frame Then slowest_frame = frame_time
Text 10,10," Triangles: " + TrisRendered()
Text 10,25," Millisecs: " + frame_time
Text 10,40," Slowest: " + slowest_frame
Text 10,55," FPS: " + fps
End Function
|