--[[ Wrecking ball suspended from rope. Cyrus-free.
Making realtime rope simulation is finicky on any physics engine. At certain weight the force becomes
too much to be successfully distributed among rope elements.
Some steps that can help solve the issue:
1) lower the mass of suspended weight or lower the gravity constant
2) increase mass of each rope element, preferably having more mass at top of rope and less mass at bottom
3) decrease number of rope elements
4) decrease the simulation time step
5) modify engine code to use direct solver instead of iterative solver (step() instead of quickStep())
--]]
local world
function lovr.load()
world = lovr.physics.newWorld(0, -3, 0, false)
-- ground plane
local box = world:newBoxCollider(vec3(0, -0.05, 0), vec3(20, 0.1, 20))
box:setKinematic(true)
-- hanger
local hangerPosition = vec3(0, 2, -1)
local hanger = world:newBoxCollider(hangerPosition, vec3(0.3, 0.1, 0.3))
hanger:setKinematic(true)
-- ball
local ballPosition = vec3(-1, 1, -1)
local ball = world:newSphereCollider(ballPosition, 0.2)
-- rope
local firstEnd, lastEnd = makeRope(
hangerPosition + vec3(0, -0.1, 0),
ballPosition + vec3(0, 0.3, 0),
0.02, 10)
lovr.physics.newDistanceJoint(hanger, firstEnd, hangerPosition, vec3(firstEnd:getPosition()))
lovr.physics.newDistanceJoint(ball, lastEnd, ballPosition, vec3(lastEnd:getPosition()))
-- brick wall
local x = 0.3
local even = true
for y = 1, 0.1, -0.1 do
for z = -0.5, -1.5, -0.2 do
world:newBoxCollider(x, y, even and z or z - 0.1, 0.08, 0.1, 0.2)
end
even = not even
end
lovr.graphics.setBackgroundColor(0.1, 0.1, 0.1)
end
function lovr.update(dt)
world:update(dt)
end
function lovr.draw(pass)
for i, collider in ipairs(world:getColliders()) do
local shade = (i - 10) / #world:getColliders()
pass:setColor(shade, shade, shade)
local shape = collider:getShapes()[1]
local shapeType = shape:getType()
local x,y,z, angle, ax,ay,az = collider:getPose()
if shapeType == 'box' then
local sx, sy, sz = shape:getDimensions()
pass:box(x,y,z, sx,sy,sz, angle, ax,ay,az)
elseif shapeType == 'sphere' then
pass:setColor(0.4, 0, 0)
pass:sphere(x,y,z, shape:getRadius())
end
end
end
function makeRope(origin, destination, thickness, elements)
local length = (destination - origin):length()
thickness = thickness or length / 100
elements = elements or 30
elementSize = length / elements
local orientation = vec3(destination - origin):normalize()
local first, last, prev
for i = 1, elements do
local position = vec3(origin):lerp(destination, (i - 0.5) / elements)
local anchor = vec3(origin):lerp(destination, (i - 1.0) / elements)
element = world:newBoxCollider(position, vec3(thickness, thickness, elementSize * 0.95))
element:setRestitution(0.1)
element:setGravityIgnored(true)
element:setOrientation(quat(orientation))
element:setLinearDamping(0.01)
element:setAngularDamping(0.01)
element:setMass(0.001)
if prev then
local joint = lovr.physics.newBallJoint(prev, element, anchor)
joint:setResponseTime(10)
joint:setTightness(1)
else
first = element
end
prev = element
end
last = prev
return first, last
end