/b2d/world.lua

https://bitbucket.org/itraykov/boxy · Lua · 1116 lines · 937 code · 134 blank · 45 comment · 225 complexity · 89072377a359352099a1abc510fda1f8 MD5 · raw file

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