/b2d/world.lua
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