PageRenderTime 62ms CodeModel.GetById 33ms app.highlight 23ms RepoModel.GetById 0ms app.codeStats 0ms

/b2d/world.lua

https://bitbucket.org/itraykov/boxy
Lua | 1116 lines | 937 code | 134 blank | 45 comment | 152 complexity | 89072377a359352099a1abc510fda1f8 MD5 | raw file
   1World = {}
   2WorldMT = { __index = World }
   3
   4function World:Create(def)
   5  local self = {}
   6  setmetatable(self, WorldMT)
   7  
   8  --assert(def.left < def.right)
   9  --assert(def.top < def.bottom)
  10
  11  self.def = def
  12  self.bodies = {}
  13  self.joints = {}
  14  self.nextID = 1
  15  self.nextJID = 1
  16  
  17  return self
  18end
  19
  20function World:Destroy()
  21  self.def = nil
  22
  23  self.bodies = nil
  24  self.joints = nil
  25  self.nextID = nil
  26  self.nextJID = nil
  27end
  28
  29function World:GetGravity()
  30  local g = self.def.gravity
  31  return g.x, g.y
  32end
  33
  34function World:SetGravity(g)
  35  self.def.gravity = g
  36end
  37
  38-- Returns available ID
  39function World:GetAvailableID()
  40  local id = self.nextID
  41  self.nextID = self.nextID + 1
  42  -- make sure the id is available
  43  while self.bodies[id] ~= nil do
  44    id = self.nextID
  45    self.nextID = self.nextID + 1
  46  end
  47  return id
  48end
  49
  50-- Returns body by ID
  51function World:FindBody(id)
  52  return self.bodies[id]
  53end
  54
  55function World:DestroyBody(b)
  56  local q = self:GetAssociatedJoints(b)
  57  for i, v in ipairs(q) do
  58    self:DestroyJoint(v)
  59  end
  60  self.bodies[b.id] = nil
  61  b:Destroy()
  62end
  63
  64function World:CreateBodyID(id, def)
  65  local b = Body:Create(self, def)
  66  b.id = id
  67  assert(self.bodies[id] == nil, "Duplicate body id:" .. id)
  68  self.bodies[id] = b
  69  return b
  70end
  71
  72-- circumvents def object
  73function World:CreateBodyEx(t, x, y, a, ld, ad, fr, ib, is, as, gs)
  74  assert(x and y)
  75  local def = {}
  76  def.type = t
  77  def.position = { x = x, y = y }
  78  def.angle = a or 0
  79  def.linearDamping = ld or 0
  80  def.angularDamping = ad or 0
  81  def.fixedRotation = fr or false
  82  def.isBullet = ib or false
  83  def.allowSleep = as or false
  84  def.isSleeping = is or false
  85  def.gravityScale = gs or 1
  86  def.active = true
  87  local id = self:GetAvailableID()
  88  return self:CreateBodyID(id, def)
  89end
  90
  91-- Returns available ID
  92function World:GetAvailableJID()
  93  local jid = self.nextJID
  94  self.nextJID = self.nextJID + 1
  95  -- make sure the id is available
  96  while self.joints[jid] ~= nil do
  97    jid = self.nextJID
  98    self.nextJID = self.nextJID + 1
  99  end
 100  return jid
 101end
 102
 103function World:DestroyJoint(j)
 104  self.joints[j.id] = nil
 105  j:Destroy()
 106end
 107
 108function World:QueryAABB(l, r, t, b, q)
 109  q = q or {}
 110  for i, v in pairs(self.bodies) do
 111    v:QueryAABB(l, r, t, b, q)
 112  end
 113  return q
 114end
 115
 116-- Returns body by ID
 117function World:FindJoint(jid)
 118  return self.joints[jid]
 119end
 120
 121function World:CreateJointID(jid, def)
 122  local t = def.type
 123  local ctor
 124  if t == "revolute" then
 125    ctor = RevoluteJoint
 126  elseif t == "weld" then
 127    ctor = WeldJoint
 128  elseif t == "distance" then
 129    ctor = DistanceJoint
 130  elseif t == "rope" then
 131    ctor = RopeJoint
 132  elseif t == "prismatic" then
 133    ctor = PrismaticJoint
 134  elseif t == "pulley" then
 135    ctor = PulleyJoint
 136  elseif t == "gear" then
 137    ctor = GearJoint
 138  else
 139    assert(false, "Unknown joing type: " .. t)
 140  end
 141  local j = ctor:Create(self, def)
 142  j.id = jid
 143  assert(self.joints[jid] == nil, "Duplicate joint id: " .. jid)
 144  self.joints[jid] = j
 145  return j
 146end
 147
 148function World:CreateJointEx(t, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11)
 149  local def = {}
 150  def.type = t
 151  local b1, b2 = p1, p2
 152  def.body1 = b1
 153  def.body2 = b2
 154  def.collideConnected = p3
 155  if t == "revolute" then
 156    -- body1, body2, cc, ax, ay
 157    local l1x, l1y = b1:GetLocalPoint(p4, p5)
 158    local l2x, l2y = b2:GetLocalPoint(p4, p5)
 159    def.localAnchor1 = { x = l1x, y = l1y }
 160    def.localAnchor2 = { x = l2x, y = l2y }
 161    def.referenceAngle = b2:GetAngle() - b1:GetAngle()
 162    def.enableLimit = false
 163    def.lowerAngle = 0
 164    def.upperAngle = 0
 165    def.enableMotor = false
 166    def.motorSpeed = 0
 167    def.maxMotorTorque = 0
 168  elseif t == "weld" then
 169    -- body1, body2, cc, ax, ay
 170    local l1x, l1y = b1:GetLocalPoint(p4, p5)
 171    local l2x, l2y = b2:GetLocalPoint(p4, p5)
 172    def.localAnchor1 = { x = l1x, y = l1y }
 173    def.localAnchor2 = { x = l2x, y = l2y }
 174    def.referenceAngle = b2:GetAngle() - b1:GetAngle()
 175    def.frequencyHz = 0
 176    def.dampingRatio = 0
 177  elseif t == "distance" or t == "rope" then
 178    -- body1, body2, cc, a1x, a1y, a2x, a2y
 179    local l1x, l1y = b1:GetLocalPoint(p4, p5)
 180    local l2x, l2y = b2:GetLocalPoint(p6, p7)
 181    def.localAnchor1 = { x = l1x, y = l1y }
 182    def.localAnchor2 = { x = l2x, y = l2y }
 183    local lx, ly = p6 - p4, p7 - p5
 184    local l = math.sqrt(lx*lx + ly*ly)
 185    if t == "distance" then
 186      def.length = l
 187      def.frequencyHz = 0
 188      def.dampingRatio = 0
 189    else
 190      def.maxLength = l
 191    end
 192  elseif t == "prismatic" then
 193    -- body1, body2, cc, ax, ay, axisx, axisy
 194    local l1x, l1y = b1:GetLocalPoint(p4, p5)
 195    local l2x, l2y = b2:GetLocalPoint(p4, p5)
 196    def.localAnchor1 = { x = l1x, y = l1y }
 197    def.localAnchor2 = { x = l2x, y = l2y }
 198    local ax, ay = b1:GetLocalVector(p6, p7)
 199    def.localAxis1 = { x = ax, y = ay }
 200    def.referenceAngle = b2:GetAngle() - b1:GetAngle()
 201
 202    def.enableLimit = false
 203    def.lowerTranslation = 0
 204    def.upperTranslation = 0
 205    def.enableMotor = false
 206    def.motorSpeed = 0
 207    def.maxMotorForce = 0
 208
 209  elseif t == "pulley" then
 210    -- body1, body2, cc, g1x, g1y, g2x, g2y, a1x, a1y, a2x, a2y, ratio
 211    def.groundAnchor1 = { x = p4, y = p5 }
 212    def.groundAnchor2 = { x = p6, y = p7 }
 213    local l1x, l1y = b1:GetLocalPoint(p8, p9)
 214    local l2x, l2y = b2:GetLocalPoint(p10, p11)
 215    def.localAnchor1 = { x = l1x, y = l1y }
 216    def.localAnchor2 = { x = l2x, y = l2y }
 217    
 218    local lx, ly = p8 - p4, p9 - p5
 219    def.length1 = math.sqrt(lx*lx + ly*ly)
 220    local lx, ly = p8 - p6, p9 - p7
 221    def.length2 = math.sqrt(lx*lx + ly*ly)
 222  elseif t == "gear" then
 223    def.joint1 = p4
 224    def.joint2 = p5
 225    def.ratio = p6 or 1
 226  else
 227    assert(false, t)
 228  end
 229  local jid = self:GetAvailableJID()
 230  return self:CreateJointID(jid, def)
 231end
 232
 233function World:GetAssociatedJoints(body)
 234  local q = {}
 235  for i, v in pairs(self.joints) do
 236    if v.def.body1 == body or v.def.body2 == body then
 237      table.insert(q, v)
 238    end
 239  end
 240  return q
 241end
 242
 243
 244
 245
 246-- xml v3 ----------------------------------------------------------------------------
 247
 248function World:CreateJointXML_v3(j)
 249  local def = {}
 250  
 251  local t = j.type
 252  if t == "g" then
 253    def.type = "gear"
 254    local j1 = xml.find(j, "J1")
 255    local j2 = xml.find(j, "J2")
 256    assert(j1, "Missing JointA tag: " .. j.id)
 257    assert(j2, "Missing JointB tag: " .. j.id)
 258    local jid1 = tonumber(j1[1])
 259    local jid2 = tonumber(j2[1])
 260    assert(jid1, "Invalid joint id: " .. j.id .. ' ' .. tostring(j1[1]))
 261    assert(jid2, "Invalid joint id: " .. j.id .. ' ' .. tostring(j2[1]))
 262    def.joint1 = self:FindJoint(jid1)
 263    def.joint2 = self:FindJoint(jid2)
 264    assert(def.joint1, "JointA does not exist: " .. j.id .. ' ' .. tostring(j1[1]))
 265    assert(def.joint2, "JointB does not exist: " .. j.id .. ' ' .. tostring(j2[1]))
 266    def.ratio = tonumber(xml.find(j, "RO")[1])
 267  else
 268    local a1 = xml.find(j, "A1")
 269    local a2 = xml.find(j, "A2")
 270    def.localAnchorA = b2.Vec2(a1.x,a1.y)
 271    def.localAnchorB = b2.Vec2(a2.x,a2.y)
 272    if t == "r" then
 273      def.type = "revolute"
 274      def.referenceAngle = tonumber(xml.find(j, "RA")[1])
 275      local lt = xml.find(j, "LT")
 276      if lt then
 277        def.enableLimit = (lt.e == "true")
 278        def.lowerAngle = tonumber(lt.l)
 279        def.upperAngle = tonumber(lt.u)
 280      end
 281      local mt = xml.find(j, "MT")
 282      if mt then
 283        def.enableMotor = (mt.e == "true")
 284        def.motorSpeed = tonumber(mt.s)
 285        def.maxMotorTorque = tonumber(mt.t)
 286      end
 287    elseif t == "w" then
 288      def.type = "weld"
 289      def.referenceAngle = tonumber(xml.find(j, "RA")[1])
 290      def.frequencyHz = tonumber(xml.find(j, "HZ")[1])
 291      def.dampingRatio = tonumber(xml.find(j, "DR")[1])
 292    elseif t == "p" then
 293      def.type = "prismatic"
 294      def.localAxisA = { "b2Vec2", b2.Vec2(0,0) }
 295      def.referenceAngle = tonumber(xml.find(j, "RA")[1])
 296      local lt = xml.find(j, "LT")
 297      if lt then
 298        def.enableLimit = (lt.e == "true")
 299        def.lowerTranslation = tonumber(lt.l)
 300        def.upperTranslation = tonumber(lt.u)
 301      end
 302      local mt = xml.find(j, "MT")
 303      if mt then
 304        def.enableMotor = (mt.e == "true")
 305        def.motorSpeed = tonumber(mt.s)
 306        def.maxMotorForce = tonumber(mt.f)
 307      end
 308    elseif t == "d" then
 309      def.type = "distance"
 310      def.dampingRatio = tonumber(xml.find(j, "DR")[1])
 311      def.frequencyHz = tonumber(xml.find(j, "HZ")[1])
 312      def.length = tonumber(xml.find(j, "L")[1])
 313    elseif t == "o" then
 314      def.type = "rope"
 315      def.maxLength = tonumber(xml.find(j, "ML")[1])
 316    elseif t == "u" then
 317      def.type = "pulley"
 318      local g1 = xml.find(j, "G1")
 319      local g2 = xml.find(j, "G2")
 320      def.groundAnchorA = b2.Vec2(g1.x,g1.y)
 321      def.groundAnchorB = b2.Vec2(g2.x,g2.y)
 322      def.lengthA = tonumber(xml.find(j, "L1")[1])
 323      def.lengthB = tonumber(xml.find(j, "L2")[1])
 324      def.maxLengthA = tonumber(xml.find(j, "ML1")[1])
 325      def.maxLengthB = tonumber(xml.find(j, "ML2")[1])
 326      def.ratio = tonumber(xml.find(j, "RO")[1])
 327    elseif t == "m" then
 328      def.type = "mouse"
 329    else
 330      assert(false, "Unknown joint type")
 331    end
 332  end
 333  def.collideConnected = (j.cc == "true")
 334  
 335  def.maxLength1 = def.maxLengthA
 336  def.maxLength2 = def.maxLengthB
 337  def.length1 = def.lengthA
 338  def.length2 = def.lengthB
 339  def.groundAnchor1 = def.groundAnchorA
 340  def.groundAnchor2 = def.groundAnchorB
 341  
 342  def.localAnchor1 = def.localAnchorA
 343  def.localAnchor2 = def.localAnchorB
 344  def.localAxis1 = def.localAxisA
 345  def.localAnchorA = nil
 346  def.localAnchorB = nil
 347  def.localAxisA = nil
 348
 349  local id1 = tonumber(j.body1)
 350  local id2 = tonumber(j.body2)
 351  assert(id1 and id2)
 352  def.body1 = self:FindBody(id1)
 353  def.body2 = self:FindBody(id2)
 354  
 355  local jid = tonumber(j.id)
 356  assert(jid, "Bad joint id: " .. tostring(jid))
 357  return self:CreateJointID(jid, def)
 358end
 359
 360function World:SaveXML_v3(file)
 361  local f = xml.new("B2D")
 362  f.version = "2.3.2"
 363  f.created = os.date()
 364
 365  local world = self:GetXML_v3()
 366
 367  f:append(world)
 368
 369  xml.save(f, file)
 370end
 371
 372function World:CreateXML_v3(w)
 373  local def = {}
 374  def.gravity = b2.Vec2(0,0)
 375
 376  local self = World:Create(def)
 377  assert(self, "Could not create world")
 378
 379  for i = 1, #w, 1 do
 380    local v = w[i]
 381    local tag = xml.tag(v)
 382    if tag == "G" then
 383      local g = b2.Vec2(v.x, v.y)
 384      self:SetGravity(g)
 385    elseif tag == "B" then
 386      self:CreateBodyXML_v3(v)
 387    else
 388      -- is it a joint?
 389      if tag == 'J' and v.type ~= "g" then
 390        self:CreateJointXML_v3(v)
 391      end
 392    end
 393  end
 394
 395  for i = 1, #w, 1 do
 396    local v = w[i]
 397    local tag = xml.tag(v)
 398    if tag == "J" and v.type == "g" then
 399      -- is it a joint?
 400      self:CreateJointXML_v3(v)
 401    end
 402  end
 403
 404  return self
 405end
 406
 407function World:CreateBodyXML_v3(b)
 408  local id = xml.tonumber(b.id)
 409  local def = {}
 410  
 411  def.type = "staticBody"
 412  if b.type == "d" then
 413    def.type = "dynamicBody"
 414  elseif b.type == "k" then
 415    def.type = "kinematicBody"
 416  end
 417  def.position = b2.Vec2(b.x, b.y)
 418  def.angle = tonumber(b.a) or 0
 419  
 420  local ld = xml.find(b, "LD")
 421  if ld then
 422    def.linearDamping = tonumber(ld[1]) or 0
 423  else
 424    def.linearDamping = 0
 425  end
 426  local ad = xml.find(b, "AD")
 427  if ad then
 428    def.angularDamping = tonumber(ld[1]) or 0
 429  else
 430    def.angularDamping = 0
 431  end
 432  local fr = xml.find(b, "FR")
 433  if fr then
 434    def.fixedRotation = fr[1] == 'true'
 435  else
 436    def.fixedRotation = false
 437  end
 438  local ib = xml.find(b, "IB")
 439  if ib then
 440    def.isBullet = ib[1] == 'true'
 441  else
 442    def.isBullet = false
 443  end
 444  local sa = xml.find(b, "SA")
 445  if sa then
 446    def.allowSleep = sa[1] == 'true'
 447  else
 448    def.allowSleep = true
 449  end
 450  local aw = xml.find(b, "AW")
 451  if aw then
 452    def.isSleeping = not(aw[1] == 'true')
 453  else
 454    def.isSleeping = false
 455  end
 456  local gs = xml.find(b, 'GS')
 457  if gs then
 458    def.gravityScale = tonumber(gs[1])
 459  else
 460    def.gravityScale = 1
 461  end
 462  local ia = xml.find(b, 'IA')
 463  if ia then
 464    def.active = (ia[1] == 'true')
 465  else
 466    def.active = true
 467  end
 468  
 469  --def.isBullet = def.bullet
 470  --def.isSleeping = not def.awake
 471  --def.bullet = nil
 472  --def.awake = nil
 473  
 474  local body = self:CreateBodyID(id, def)
 475  
 476  for i = 1, #b, 1 do
 477    local v = b[i]
 478    local tag = xml.tag(v)
 479    if tag == "LV" then
 480      local lv = xml.cast.b2Vec2(v)
 481      body:SetLinearVelocity(lv.x, lv.y)
 482    elseif tag == "AV" then
 483      local av = xml.cast.number(v)
 484      body:SetAngularVelocity(av)
 485    elseif tag == "SS" then
 486      body.def.scale = xml.tonumber(b[i][1])
 487    elseif tag == "F" then
 488      body:CreateShapeXML_v3(v)
 489    end
 490  end
 491  
 492  return body
 493end
 494
 495function World:GetXML_v3()
 496  local world = xml.new("W")
 497  
 498  local def = self.def
 499  local g = world:append("G")
 500	g.x = def.gravity.x
 501	g.y = def.gravity.y
 502  
 503  -- world.doSleep = def.doSleep
 504
 505  for i, v in pairs(self.bodies) do
 506    local body = v:GetXML_v3()
 507    world:append(body)
 508  end
 509  -- save non-gear joints
 510  for i, v in pairs(self.joints) do
 511    if v:GetType() ~= "gear" then
 512      local joint = v:GetXML_v3()
 513      world:append(joint)
 514    end
 515  end
 516  -- save gear joints at the end
 517  for i, v in pairs(self.joints) do
 518    if v:GetType() == "gear" then
 519      local joint = v:GetXML_v3()
 520      world:append(joint)
 521    end
 522  end
 523
 524  return world
 525end
 526
 527
 528
 529
 530
 531-- xml v2 ----------------------------------------------------------------------------
 532
 533local revoluteJSkeleton =
 534{
 535  referenceAngle = { "number", 0 },
 536  enableLimit = { "boolean", false },
 537  lowerAngle = { "number", 0 },
 538  upperAngle = { "number", 0 },
 539  enableMotor = { "boolean", false },
 540  motorSpeed = { "number", 0 },
 541  maxMotorTorque = { "number", 0 },
 542  localAnchorA = { "b2Vec2", b2.Vec2(0,0) },
 543  localAnchorB = { "b2Vec2", b2.Vec2(0,0) },
 544  collideConnected = { "boolean", false }
 545}
 546local weldJSkeleton =
 547{
 548  referenceAngle = { "number", 0 },
 549  frequencyHz = { "number", 0 },
 550  dampingRatio = { "number", 0 },
 551  localAnchorA = { "b2Vec2", b2.Vec2(0,0) },
 552  localAnchorB = { "b2Vec2", b2.Vec2(0,0) },
 553  collideConnected = { "boolean", false }
 554}
 555
 556local prismaticJSkeleton =
 557{
 558  localAxisA = { "b2Vec2", b2.Vec2(0,0) },
 559  referenceAngle = { "number", 0 },
 560  enableLimit = { "boolean", false },
 561  lowerTranslation = { "number", 0 },
 562  upperTranslation = { "number", 0 },
 563  enableMotor = { "boolean", false },
 564  motorSpeed = { "number", 0 },
 565  maxMotorForce = { "number", 0 },
 566  localAnchorA = { "b2Vec2", b2.Vec2(0,0) },
 567  localAnchorB = { "b2Vec2", b2.Vec2(0,0) },
 568  collideConnected = { "boolean", false }
 569}
 570
 571local distanceJSkeleton =
 572{
 573  length = { "number", 0 },
 574  frequencyHz = { "number", 0 },
 575  dampingRatio = { "number", 0 },
 576  localAnchorA = { "b2Vec2", b2.Vec2(0,0) },
 577  localAnchorB = { "b2Vec2", b2.Vec2(0,0) },
 578  collideConnected = { "boolean", false }
 579}
 580
 581local ropeJSkeleton =
 582{
 583  maxLength = { "number", 0 },
 584  localAnchorA = { "b2Vec2", b2.Vec2(0,0) },
 585  localAnchorB = { "b2Vec2", b2.Vec2(0,0) },
 586  collideConnected = { "boolean", false }
 587}
 588
 589local pulleyJSkeleton =
 590{
 591  groundAnchorA = { "b2Vec2", b2.Vec2(0,0) },
 592  groundAnchorB = { "b2Vec2", b2.Vec2(0,0) },
 593  lengthA = { "number", 0 },
 594  lengthB = { "number", 0 },
 595  maxLengthA = { "number", 0 },
 596  maxLengthB = { "number", 0 },
 597  ratio = { "number", 0 },
 598  localAnchorA = { "b2Vec2", b2.Vec2(0,0) },
 599  localAnchorB = { "b2Vec2", b2.Vec2(0,0) },
 600  collideConnected = { "boolean", false }
 601}
 602
 603local gearJSkeleton =
 604{
 605  ratio = { "number", 1 },
 606  collideConnected = { "boolean", false }
 607}
 608
 609function World:CreateJointXML_v2(j)
 610  local def = {}
 611  
 612  local t = xml.tag(j)
 613  local skeleton
 614  if t == "RevoluteJoint" then
 615    def.type = "revolute"
 616    skeleton = revoluteJSkeleton
 617  elseif t == "WeldJoint" then
 618    def.type = "weld"
 619    skeleton = weldJSkeleton
 620  elseif t == "PrismaticJoint" then
 621    def.type = "prismatic"
 622    skeleton = prismaticJSkeleton
 623  elseif t == "DistanceJoint" then
 624    def.type = "distance"
 625    skeleton = distanceJSkeleton
 626  elseif t == "RopeJoint" then
 627    def.type = "rope"
 628    skeleton = ropeJSkeleton
 629  elseif t == "PulleyJoint" then
 630    def.type = "pulley"
 631    skeleton = pulleyJSkeleton
 632  elseif t == "MouseJoint" then
 633    def.type = "mouse"
 634  elseif t == "GearJoint" then
 635    def.type = "gear"
 636    skeleton = gearJSkeleton
 637    local j1 = xml.find(j, "JointA")
 638    local j2 = xml.find(j, "JointB")
 639    assert(j1, "Missing JointA tag: " .. j.id)
 640    assert(j2, "Missing JointB tag: " .. j.id)
 641    local jid1 = xml.cast.number(j1)
 642    local jid2 = xml.cast.number(j2)
 643    def.joint1 = self:FindJoint(jid1)
 644    def.joint2 = self:FindJoint(jid2)
 645    assert(def.joint1, "JointA does not exist: " .. j.id)
 646    assert(def.joint2, "JointB does not exist: " .. j.id)
 647  else
 648    assert(false, "Unknown joint type")
 649  end
 650  xml.loadSkeleton(skeleton, j, def)
 651
 652  
 653  def.maxLength1 = def.maxLengthA
 654  def.maxLength2 = def.maxLengthB
 655  def.length1 = def.lengthA
 656  def.length2 = def.lengthB
 657  def.groundAnchor1 = def.groundAnchorA
 658  def.groundAnchor2 = def.groundAnchorB
 659  
 660  def.localAnchor1 = def.localAnchorA
 661  def.localAnchor2 = def.localAnchorB
 662  def.localAxis1 = def.localAxisA
 663  def.localAnchorA = nil
 664  def.localAnchorB = nil
 665  def.localAxisA = nil
 666
 667  local id1 = tonumber(j.body1)
 668  local id2 = tonumber(j.body2)
 669  assert(id1 and id2)
 670  def.body1 = self:FindBody(id1)
 671  def.body2 = self:FindBody(id2)
 672  
 673  local jid = tonumber(j.id)
 674  return self:CreateJointID(jid, def)
 675end
 676
 677function World:SaveXML_v2(file)
 678  local f = xml.new("XMLBox2D")
 679  f.version = "1.0"
 680  f.created = os.date()
 681  f['noNamespaceSchemaLocation']="level.xsd"
 682  f['xmlns:xsi']="http://www.w3.org/2001/XMLSchema-instance"
 683
 684  local world = self:GetXML_v2()
 685
 686  f:append(world)
 687
 688  xml.save(f, file)
 689end
 690
 691local jtypes =
 692{
 693  "RevoluteJoint",
 694  "WeldJoint",
 695  "PrismaticJoint",
 696  "DistanceJoint",
 697  "RopeJoint",
 698  "PulleyJoint",
 699  "GearJoint",
 700  "MouseJoint"
 701}
 702
 703function World:CreateXML_v2(w)
 704  local def = {}
 705  def.gravity = b2.Vec2(0,0)
 706
 707  local self = World:Create(def)
 708  assert(self, "Could not create world")
 709
 710  for i = 1, #w, 1 do
 711    local v = w[i]
 712    local tag = xml.tag(v)
 713    if tag == "Gravity" then
 714      local g = xml.cast.b2Vec2(v)
 715      self:SetGravity(g)
 716    elseif tag == "Body" then
 717      self:CreateBodyXML_v2(v)
 718    else
 719      -- is it a joint?
 720      for _, joint in ipairs(jtypes) do
 721        if tag == joint and tag ~= "GearJoint" then
 722          self:CreateJointXML_v2(v)
 723          break
 724        end
 725      end
 726    end
 727  end
 728
 729  for i = 1, #w, 1 do
 730    local v = w[i]
 731    local tag = xml.tag(v)
 732    if tag == "GearJoint" then
 733      -- is it a joint?
 734      self:CreateJointXML_v2(v)
 735    end
 736  end
 737
 738  return self
 739end
 740
 741
 742
 743local bodyDefSkeleton =
 744{
 745  type = { "string", "staticBody" },
 746  position = { "b2Vec2", b2.Vec2(0,0) },
 747  angle = { "number", 0 },
 748  linearDamping = { "number", 0 },
 749  angularDamping = { "number", 0 },
 750  fixedRotation = { "boolean", false },
 751  bullet = { "boolean", false },
 752  allowSleep = { "boolean", false },
 753  awake = { "boolean", true },
 754  gravityScale = { "number", 1 },
 755  active = { "boolean", true }
 756}
 757
 758function World:CreateBodyXML_v2(b)
 759  local id = xml.tonumber(b.id)
 760  local def = {}
 761  xml.loadSkeleton(bodyDefSkeleton, b, def)
 762  
 763  def.isBullet = def.bullet
 764  def.isSleeping = not def.awake
 765  def.bullet = nil
 766  def.awake = nil
 767  
 768  local body = self:CreateBodyID(id, def)
 769  
 770  for i = 1, #b, 1 do
 771    local v = b[i]
 772    local tag = xml.tag(v)
 773    if tag == "LinearVelocity" then
 774      local lv = xml.cast.b2Vec2(v)
 775      body:SetLinearVelocity(lv.x, lv.y)
 776    elseif tag == "AngularVelocity" then
 777      local av = xml.cast.number(v)
 778      body:SetAngularVelocity(av)
 779    elseif tag == "Scale" then
 780      body.def.scale = xml.tonumber(b[i][1])
 781    elseif tag == "CircleShape" or tag == "PolygonShape" or tag == "ConcaveShape" or tag == "ChainShape" then
 782      body:CreateShapeXML_v2(v)
 783    end
 784  end
 785  
 786  return body
 787end
 788
 789function World:GetXML_v2()
 790  local world = xml.new("World")
 791  
 792  local def = self.def
 793  local g = world:append("Gravity")
 794	g.x = def.gravity.x
 795	g.y = def.gravity.y
 796  
 797  -- world.doSleep = def.doSleep
 798
 799  for i, v in pairs(self.bodies) do
 800    local body = v:GetXML_v2()
 801    world:append(body)
 802  end
 803  -- save non-gear joints
 804  for i, v in pairs(self.joints) do
 805    if v:GetType() ~= "gear" then
 806      local joint = v:GetXML_v2()
 807      world:append(joint)
 808    end
 809  end
 810  -- save gear joints at the end
 811  for i, v in pairs(self.joints) do
 812    if v:GetType() == "gear" then
 813      local joint = v:GetXML_v2()
 814      world:append(joint)
 815    end
 816  end
 817
 818  return world
 819end
 820
 821
 822
 823-- xml v1 ----------------------------------------------------------------------------------------
 824
 825function World:CreateJointXML_v1(j)
 826  local def
 827
 828  local t = j.type
 829  local def = {}
 830  def.type = t
 831  if t == "revolute" then
 832    local a = xml.find(j, "ReferenceAngle")
 833    local l = xml.find(j, "Limit")
 834    local m = xml.find(j, "Motor")
 835    -- assign values
 836    def.referenceAngle = xml.tonumber(a[1])
 837    -- default values
 838    def.enableLimit = false
 839    def.lowerAngle = 0
 840    def.upperAngle = 0
 841    def.enableMotor = false
 842    def.motorSpeed = 0
 843    def.maxMotorTorque = 0
 844    -- optional
 845    if l then
 846      def.enableLimit = xml.toboolean(l.enabled)
 847      def.lowerAngle = xml.tonumber(l.lowerAngle)
 848      def.upperAngle = xml.tonumber(l.upperAngle)
 849    end
 850    if m then
 851      def.enableMotor = xml.toboolean(m.enabled)
 852      def.motorSpeed = xml.tonumber(m.speed)
 853      def.maxMotorTorque = xml.tonumber(m.maxTorque)
 854    end
 855  elseif t == "prismatic" then
 856    local la1 = xml.find(j, "LocalAxis1")
 857    local la1x, la1y = xml.toPosition_v1(la1[1])
 858    local a = xml.find(j, "ReferenceAngle")
 859    local l = xml.find(j, "Limit")
 860    local m = xml.find(j, "Motor")
 861    -- assign values
 862    def.localAxis1 = { x = la1x, y = la1y }
 863    def.referenceAngle = xml.tonumber(a[1])
 864    -- default values
 865    def.enableLimit = false
 866    def.lowerTranslation = 0
 867    def.upperTranslation = 0
 868    def.enableMotor = false
 869    def.motorSpeed = 0
 870    def.maxMotorForce = 0
 871    -- optional
 872    if l then
 873      def.enableLimit = xml.toboolean(l.enabled)
 874      def.lowerTranslation = xml.tonumber(l.lowerTranslation)
 875      def.upperTranslation = xml.tonumber(l.upperTranslation)
 876    end
 877    if m then
 878      def.enableMotor = xml.tonumber(m.enabled)
 879      def.motorSpeed = xml.tonumber(m.speed)
 880      def.maxMotorForce = xml.tonumber(m.maxForce)
 881    end
 882  elseif t == "distance" then
 883    local l = xml.find(j, "Length")
 884    local f = xml.find(j, "FrequencyHz")
 885    local d = xml.find(j, "DampingRatio")
 886    -- assign values
 887    def.length = xml.tonumber(l[1])
 888    def.frequencyHz = xml.tonumber(f[1])
 889    def.dampingRatio = xml.tonumber(d[1])
 890  elseif t == "pulley" then
 891    local ga1 = xml.find(j, "GroundAnchor1")
 892    local ga2 = xml.find(j, "GroundAnchor2")
 893    local ga1x, ga1y = xml.toPosition_v1(ga1[1])
 894    local ga2x, ga2y = xml.toPosition_v1(ga2[1])
 895    local l1 = xml.find(j, "Length1")
 896    local l2 = xml.find(j, "Length2")
 897    local maxl1 = xml.find(j, "MaxLength1")
 898    local maxl2 = xml.find(j, "MaxLength2")
 899    local r = xml.find(j, "Ratio")
 900    -- values
 901    def.groundAnchor1 = { x = ga1x, y = ga1y }
 902    def.groundAnchor2 = { x = ga2x, y = ga2y }
 903    def.length1 = xml.tonumber(l1[1])
 904    def.length2 = xml.tonumber(l2[1])
 905    def.maxLength1 = xml.tonumber(maxl1[1])
 906    def.maxLength2 = xml.tonumber(maxl2[1])
 907    def.ratio = xml.tonumber(r[1])
 908  elseif t == "mouse" then
 909  elseif t == "gear" then
 910    local j1 = xml.find(joint, "Joint1")
 911    local j2 = xml.find(joint, "Joint2")
 912    local jid1 = xml.tonumber(j1[1])
 913    local jid2 = xml.tonumber(j2[1])
 914    -- assign values
 915    def.joint1 = self:FindJoint(jid1)
 916    def.joint2 = self:FindJoint(jid2)
 917  end
 918
 919  local id1 = xml.tonumber(j.body1)
 920  local id2 = xml.tonumber(j.body2)
 921  local la1 = xml.find(j, "LocalAnchor1")
 922  local la2 = xml.find(j, "LocalAnchor2")
 923  -- assign values
 924  def.body1 = self:FindBody(id1)
 925  def.body2 = self:FindBody(id2)
 926  def.collideConnected = xml.toboolean(j.collideConnected)
 927  def.localAnchor1 = { x = 0, y = 0 }
 928  def.localAnchor2 = { x = 0, y = 0 }
 929  if la1 then
 930    local la1x, la1y = xml.toPosition_v1(la1[1])
 931    def.localAnchor1.x = la1x
 932    def.localAnchor1.y = la1y
 933  end
 934  if la2 then
 935    local la2x, la2y = xml.toPosition_v1(la2[1])
 936    def.localAnchor2.x = la2x
 937    def.localAnchor2.y = la2y
 938  end
 939  
 940  local jid = xml.tonumber(j.id)
 941  return self:CreateJointID(jid, def)
 942end
 943
 944function World:SaveXML_v1(file)
 945  local f = xml.new("XMLBox2D")
 946  f.version = "1.0"
 947  f.created = os.date()
 948
 949  local world = self:GetXML_v1()
 950
 951  f:append(world)
 952
 953  xml.save(f, file)
 954end
 955
 956
 957function World:CreateXML_v1(w)
 958  local def = {}
 959  def.left = xml.tonumber(w.left)
 960  def.top = xml.tonumber(w.top)
 961  def.right = xml.tonumber(w.right)
 962  def.bottom = xml.tonumber(w.bottom)
 963
 964  local gx, gy = xml.toPosition_v1(w.gravity)
 965  def.gravity = { x = gx, y = gy }
 966  def.doSleep = xml.toboolean(w.doSleep)
 967
 968  local self = World:Create(def)
 969  assert(self, "Could not create world")
 970
 971  local objects = #w
 972  for i = 1, objects, 1 do
 973    local object = w[i]
 974    local tag = xml.tag(object)
 975    if tag == "Body" then
 976      self:CreateBodyXML_v1(object)
 977    elseif tag == "Joint" then
 978      self:CreateJointXML_v1(object)
 979    else
 980      assert(false, "Unkown tag in world")
 981    end
 982  end
 983
 984  return self
 985end
 986
 987function World:CreateBodyXML_v1(b)
 988  local id = xml.tonumber(b.id)
 989  local def = {}
 990  def.type = b.type
 991  local px, py = xml.toPosition_v1(b.position)
 992  def.position = { x = px, y = py }
 993  def.angle = xml.tonumber(b.angle)
 994  def.linearDamping = xml.tonumber(b.linearDamping)
 995  def.angularDamping = xml.tonumber(b.angularDamping)
 996  def.fixedRotation = xml.toboolean(b.fixedRotation)
 997  def.isBullet = xml.toboolean(b.isBullet)
 998  def.allowSleep = xml.toboolean(b.allowSleep)
 999  def.isSleeping = xml.toboolean(b.isSleeping)
1000  
1001  local body = self:CreateBodyID(id, def)
1002
1003  for i = 1, #b, 1 do
1004    local tag = xml.tag(b[i])
1005    if tag == "LinearVelocity" then
1006      local lvx, lvy = xml.toPosition_v1(b[i][1])
1007      body:SetLinearVelocity(lvx, lvy)
1008    elseif tag == "AngularVelocity" then
1009      local av = xml.tonumber(b[i][1])
1010      body:SetAngularVelocity(av)
1011    elseif tag == "Scale" then
1012      body.def.scale = xml.tonumber(b[i][1])
1013    elseif tag == "Shape" then
1014      body:CreateShapeXML_v1(b[i])
1015    end
1016  end
1017  
1018  return body
1019end
1020
1021function World:GetXML_v1()
1022  local world = xml.new("World")
1023
1024  local def = self.def
1025  world.left = def.left
1026  world.top = def.top
1027  world.right = def.right
1028  world.bottom = def.bottom
1029  local g = def.gravity
1030  world.gravity = xml.PositionStr_v1(g.x, g.y)
1031  world.doSleep = def.doSleep
1032
1033  for i, v in pairs(self.bodies) do
1034    local body = v:GetXML_v1()
1035    world:append(body)
1036  end
1037  for i, v in pairs(self.joints) do
1038    local joint = v:GetXML_v1()
1039    world:append(joint)
1040  end
1041  return world
1042end
1043
1044-----------------------------------------------------------------------------
1045
1046
1047function World:GetLua_v1()
1048  local world = xml.new("World")
1049  local def = self.def
1050  local x, y = def.gravity.x, def.gravity.y
1051  local s = "false"
1052  if def.doSleep == true then
1053    s = "true"
1054  end
1055  local sz = "local world = love.physics.newWorld(%s, %s, %s)"
1056  sz = string.format(sz, x, y, s)
1057  local sz2 = "local bodies = {}"
1058  local sz3 = "local joints = {}"
1059  
1060  local out = { sz, sz2, sz3 }
1061  
1062  for i, v in pairs(self.bodies) do
1063    local body = v:GetLua_v1()
1064    table.insert(out, body)
1065  end
1066  -- save non-gear joints
1067  for i, v in pairs(self.joints) do
1068    if v:GetType() ~= "gear" then
1069      local joint = v:GetLua_v1()
1070      table.insert(out, joint)
1071    end
1072  end
1073  -- save gear joints at the end
1074  for i, v in pairs(self.joints) do
1075    if v:GetType() == "gear" then
1076      local joint = v:GetLua_v1()
1077      table.insert(out, joint)
1078    end
1079  end
1080
1081  return table.concat(out, "\n")
1082end
1083
1084
1085function World:SaveLua(file)
1086  local sz = self:GetLua()
1087  local f = io.open(file, "w")
1088  f:write(sz)
1089  f:close()
1090end
1091
1092-----------------------------------------------------------------------------
1093
1094function World:CreateXML(w)
1095  return self:CreateXML_v3(w)
1096end
1097
1098function World:GetXML()
1099  return self:GetXML_v3()
1100end
1101
1102function World:SaveXML(file)
1103  return self:SaveXML_v3(file)
1104end
1105
1106function World:CreateBodyXML(b)
1107  return self:CreateBodyXML_v2(b)
1108end
1109
1110function World:CreateJointXML(b)
1111  return self:CreateJointXML_v2(b)
1112end
1113
1114function World:GetLua()
1115  return self:GetLua_v1()
1116end