Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Exploding Meshes

edited October 2012 in Code Sharing Posts: 666

I've finished a rough version of exploding meshes. This code will take a mesh (and a few other params) and make the mesh explode in either a vector impact or bloom method.

I still have a bug with my shadows; they are rotating in all 3 axes though I only want then to rotate in two.

Part 1 of 2 - the Fragment class and functions to explode

--Fragment.lua Fragment = class() Fragment.DEFAULT_MASS = 1 Fragment.GRAVITY=9.81 --GRAVITY constant... Fragment.EXPLODEBLOOM = 1 Fragment.EXPLODEIMPACT = 2 -------------------------------------------------------- -- Creates a fragment object. The mesh that the fragment renders is passed -- to this function, so a custom mesh could override a fragment of a source mesh -- if desired. -------------------------------------------------------- function Fragment:init(params) self.rotationSpeed = params.rotationSpeed or 10 self.motionSpeed = params.motionSpeed or 100 self.location = params.location or vec3(0,0,0) self.thisMesh = params.Mesh self.v = params.velocity or vec3(1,1,1) -- velocity (vec3) = or 15 -- life cycle, default 1/4 sec self.scale = params.scale or self.DEFAULT_SCALE -- scale of mesh --every cycle shrink frag by this multiplier. Keep under 1 to shrink, > 1 to grow. self.scalediminish = params.scalediminish or .99 self.mass = params.mass or Fragment.DEFAULT_MASS -- mass --CALCULATE / ASSIGN some items self.curangle=0 --fudging mass as # of G's in the y direction. Y is up only in this world! --base acceleration for this fragment self.acc = vec3(0, -Fragment.GRAVITY*self.mass, 0) self.lifeLeft = --set up for run, this gets decremented per frame if params.shadows then self.shadow=mesh() self.shadow.vertices = params.Mesh.vertices self.shadow:setColors(color(0,0,0,64)) end end function Fragment:draw() if self.lifeLeft > 0 then self.lifeLeft = self.lifeLeft - 1 end if self.lifeLeft ~= 0 then pushMatrix() local x,y,z = GetCenter(self.thisMesh) translate(self.location.x, self.location.y, self.location.z) translate(-x,-y,-z) rotate(self.curangle,1,1,1) --rotation values translate(-x,-y,-z) scale(self.scale, self.scale,self.scale) --scale factor overall. static .99 or vec3 self.thisMesh:draw() popMatrix() --shadow? if self.shadow ~= nil then pushMatrix() translate(self.location.x, 0, self.location.z) translate(-x,0,-z) rotate(90,0,1,0) rotate(self.curangle,1,0,1) --rotation values translate(-x,0,-z) scale(self.scale, self.scale,self.scale) tint(0, 0, 0, 255) self.shadow:draw() noTint() popMatrix() end --set up for next frame self.curangle = self.curangle + self.rotationSpeed --change rotation angle self.scale = self.scale * self.scalediminish --change fragment size --move this fragment. Note that 60 represents FPS. local curframe = self.lifeLeft self.location = self.location + (self.v * (curframe/60))/self.motionSpeed -- hack!! self.v = self.v + (self.acc * (curframe/60))/self.motionSpeed --change velocity end end -------------------------------------------------------- -- takes a source mesh and creates all of the fragments - as seperate small meshes. -- this is done so we can rotate, translate and scale pieces seperate that the -- master body from whence they came. -------------------------------------------------------- function FragmentMesh(params) tri = {} tc={} triColor = {} pos = 0 local meshlist = {} local triangleCount = 0 local moduloTriangle = params.moduloTriangle or 10 aMesh = mesh() aMesh=params.Mesh --explode a mesh for k,v in pairs(aMesh.vertices) do pos=pos+1 if (pos > 2) then triangleCount = triangleCount + 1 if triangleCount == moduloTriangle then --make new copies of prev mesh table.insert(tri,aMesh:vertex(k-2)) table.insert(tri,aMesh:vertex(k-1)) table.insert(tri, v) -- aMesh:vertex(k) is the same thing... add the vertex if (#params.Mesh.texCoords >0) then table.insert(tc,aMesh:texCoord(k-2)) table.insert(tc,aMesh:texCoord(k-1)) table.insert(tc,aMesh:texCoord(k)) --add the texCoord end if (#params.Mesh.colors >0) then table.insert(triColor, GetColor(aMesh:color(k-2))) table.insert(triColor, GetColor(aMesh:color(k-1))) table.insert(triColor, GetColor(aMesh:color(k))) else table.insert(triColor, color(255,0,0,255)) table.insert(triColor, color(0,0,255,255)) table.insert(triColor, color(0,255,0,255)) end exp = mesh() exp.vertices = tri exp.colors = triColor exp.texture = params.Mesh.texture exp.texCoords = tc -- --create attributes of this mesh for later use life = or 60*3 --3 secs to live sc = params.scale or 1 sd = params.scalediminish ---needed to make a copy to pass it?! local location = params.location local mass=params.mass local hasShadows = params.shadows if (params.impactVector == nil) then _, sz = GetBoundingBox(exp) if sz.x <1 then sz.x=sz.x*2 end if sz.y <1 then sz.y=sz.y*2 end if sz.z <1 then sz.z=sz.z*2 end velocity = vec3( math.random(-sz.x,sz.x), math.random(sz.y,sz.y), math.random(-sz.z,sz.z)) else if (params.explosiontype == Fragment.EXPLODEIMPACT) then velocity = vec3( (math.random(-100,100))/100+params.impactVector.x, (math.random(-100,100))/100+params.impactVector.y, (math.random(-100,100))/100+params.impactVector.z) else velocity = vec3( (math.random(-100,100)/100)*params.impactVector.x, (math.random(-20,100) /100)*params.impactVector.y, (math.random(-100,100)/100)*params.impactVector.z) end end --print(location) local param = {life=life, velocity=velocity, mass=mass, scale=sc, scalediminish = sd, location=location, rotationSpeed = 15, motionSpeed = 5, Mesh = exp, shadows = hasShadows} f = Fragment(param) table.insert(meshlist, f) tri = {} tc={} triColor = {} pos = 0 triangleCount = 0 end end end return meshlist end


  • Posts: 666

    part 2 of for fragment class

    -------------------------------------------------------- --public helper functions -------------------------------------------------------- --change a mesh color entry into a standard color entry function GetColor(c) return color(c.x,c.y,c.z,c.a) end --calculates the bounding box of an object. Returns the low and high values. function GetBoundingBox(v) --determine bounding box. vlow = vec3(0,0,0) vhigh = vec3(0,0,0) for k=1, #v.vertices do if v:vertex(k).x < vlow.x then vlow.x = v:vertex(k).x end if v:vertex(k).y < vlow.y then vlow.y = v:vertex(k).y end if v:vertex(k).z < vlow.z then vlow.z = v:vertex(k).z end if v:vertex(k).x > vhigh.x then vhigh.x = v:vertex(k).x end if v:vertex(k).y > vhigh.y then vhigh.y = v:vertex(k).y end if v:vertex(k).z > vhigh.z then vhigh.z = v:vertex(k).z end end return vlow, vhigh end --finds the ceter of a 3D mesh. needed for central rotation. function GetCenter(aMesh) local x=0 local y=0 local z=0 for k,v in pairs(aMesh.vertices) do x=x+v.x y=y+v.y z=z+v.z end x = x / #aMesh.vertices y = y / #aMesh.vertices z = z / #aMesh.vertices return x,y,z end
  • Posts: 666

    Sample use. the Cubes class is the standard sample from the 3D Cube project; it was used to quickly "steal" a mesh for testing.

    -- Explode -- Use this function to perform your initial setup function setup() Cubes:init() --for Frame of reference and quick mesh use explode = {} explode2 = {} explode3={} --let camera float around. sx=.1 szz=.1 cx=-75 cz=75 end -- This function gets called once every frame function draw() -- This sets a dark background color background(173, 173, 184, 255) if math.abs(cx) > 150 then sx=-sx end if math.abs(cz) > 150 then szz=-szz end cx = cx + sx cz = cz + szz perspective(40, WIDTH/HEIGHT,0.1, 500) --camera(5,5,5, 0,0,0, 0,1,0) camera(cx,30,cz, 20,10,0, 0,1,0) -- Do your drawing here DrawGround() ---THE MAGIC HAPPENS HERE if (#explode==0) then --explode a mesh local params = { Mesh =, explosiontype = Fragment.EXPLODEBLOOM, moduloTriangle =1, life = 810, impactVector =vec3(10,20,10), mass=.2, scale=4, scalediminish=1, location=vec3(20,5,0), --motionSpeed = 50000 } explode = FragmentMesh(params) ---THE MAGIC HAPPENS HERE end ---HACK!!!!!! Make a few extra explosions for fun. ex2 and 3 are only for demoing if (#explode2==0) then local params2 = { Mesh =, explosiontype = Fragment.EXPLODEBLOOM, moduloTriangle =1, life = 120, impactVector =vec3(2,20,0), mass=1, scale=2, scalediminish=.99, location=vec3(0,20,0), shadows=true } explode2 = FragmentMesh(params2) end if (#explode3==0) then local params3 = { Mesh =, explosiontype = Fragment.EXPLODEIMPACT, moduloTriangle =1, life = 120, impactVector =vec3(-8,10,-8), mass=1, scale=1, scalediminish=.99, location=vec3(20,20,0), shadows=false } explode3 = FragmentMesh(params3) end --give me a frame of reference if (#explode == 0) then end ---This could be in a table and looped. for ek,ev in pairs(explode) do ev:draw() if (ev.lifeLeft < 1) or (ev.location.y < -1) then table.remove(explode, ek) end end for ek,ev in pairs(explode2) do ev:draw() if (ev.lifeLeft < 1) or (ev.location.y < -1)then table.remove(explode2, ek) end end for ek,ev in pairs(explode3) do ev:draw() if (ev.lifeLeft < 1) or (ev.location.y < -1)then table.remove(explode3, ek) end end end function DrawGround() pushMatrix() rotate(90,1,0,0) scale(300,300,0) sprite("Dropbox:GroundTile1") popMatrix() end
  • edited October 2012 Posts: 666

    Don't know how to embed videos yet, so:
    Using base cube models

    Using X3d and PLY loaded models. Long delay from 12-19 secods as largest model is prepared (building) for detonation.

  • Jmv38Jmv38 Mod
    Posts: 3,297

    To embed the video you just have to do send by email in youtube, then copy the adress in the mail, then directly paste it in the post:"" then it is automatically recognized by the forum as a video and transformed into this:
    The adress appeared in clear above because i put it inside ""

  • Posts: 666

    Thanks @jmv38.

  • Jmv38Jmv38 Mod
    Posts: 3,297

    I like you buildings. How did you do the shape and the textures?

  • edited October 2012 Posts: 666

    I took them from 3D warehouse in SketchUp. They are PLY or X3D files that I converted from Collada format. The bldg is 35 E Wacker in Chicago, IL, where I live.

Sign In or Register to comment.