-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathphygrab.lua
124 lines (106 loc) · 3.5 KB
/
phygrab.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
-- grab and move colliders with mouse
local m = {}
m.range = 200
m.mouse_button = 2
m.near_plane = 0.01
local mouse_collider
local mouse_joint
local world_from_screen = Mat4()
local hovered = { depth = math.huge, collider = nil }
local function getWorldFromScreen(pass)
local w, h = pass:getDimensions()
local clip_from_screen = mat4(-1, -1, 0):scale(2 / w, 2 / h, 1)
local view_pose = mat4(pass:getViewPose(1))
local view_proj = pass:getProjection(1, mat4())
local is_orthographic = view_proj[16] == 1
local t = view_pose:mul(view_proj:invert()):mul(clip_from_screen)
world_from_screen:set(t)
end
local function getRay(distance, pass)
distance = distance or m.range
local ray = {}
local x, y = lovr.system.getMousePosition()
ray.origin = vec3(world_from_screen:mul(x, y, m.near_plane / m.near_plane))
ray.target = vec3(world_from_screen:mul(x, y, m.near_plane / distance))
if is_orthographic then
ray.origin.z = distance
ray.target.z = -1000
end
return ray
end
function m.init(world)
assert(world, 'Need to pass in the physics world object')
mouse_collider = world:newSphereCollider(0, 0, 0, 0.001)
mouse_collider:setKinematic(true)
mouse_collider:setSensor(true)
end
function m.draw(pass)
getWorldFromScreen(pass)
local mx, my = lovr.system.getMousePosition()
local origin = world_from_screen:mul(mx, my, m.near_plane / m.near_plane)
local target = world_from_screen:mul(mx, my, m.near_plane / m.range)
if not mouse_joint then
hovered.collider = nil
hovered.depth = math.huge
local world = mouse_collider:getWorld()
world:raycast(origin, target, nil,
function(collider, shape, x, y, z, nx, ny, nz, fraction)
local depth = origin:distance(x, y, z)
if collider and not collider:isKinematic() and depth < hovered.depth then
hovered.collider = collider
hovered.depth = math.max(depth, 1e-5)
mouse_collider:moveKinematic(x, y, z, quat(), 1/60)
end
return 1
end)
else
local cursor_pos = world_from_screen:mul(mx, my, m.near_plane / hovered.depth)
mouse_collider:moveKinematic(cursor_pos, quat(), 1/60)
local xa, ya, za, xb, yb, zb = mouse_joint:getAnchors()
pass:setColor(1,1,1)
pass:capsule(cursor_pos, vec3(xb, yb, zb), 0.01, 7)
end
end
function m.mousepressed(x, y, button, pass)
if button == m.mouse_button and hovered.collider then
local pos = vec3(mouse_collider:getPosition())
mouse_joint = lovr.physics.newDistanceJoint(hovered.collider, mouse_collider, pos, pos)
end
end
function m.mousereleased(x, y, button)
if button == m.mouse_button and mouse_joint then
for _, joint in ipairs(mouse_collider:getJoints()) do
joint:destroy()
end
mouse_joint = nil
end
end
function m.wheelmoved(x, y)
if not mouse_joint then return end
hovered.depth = hovered.depth * (1 + 0.05 * y)
end
-- quickest way to use: call this in your lovr.load()
function m.integrate(world)
m.init(world)
local stub_fn = function() end
local existing_cb = {
draw = lovr.draw or stub_fn,
mousepressed = lovr.mousepressed or stub_fn,
mousereleased = lovr.mousereleased or stub_fn,
wheelmoved = lovr.wheelmoved or stub_fn,
}
local function wrap(callback)
return function(...)
m[callback](...)
existing_cb[callback](...)
end
end
lovr.mousepressed = wrap('mousepressed')
lovr.mousereleased = wrap('mousereleased')
lovr.wheelmoved = wrap('wheelmoved')
lovr.draw = function(pass)
m.draw(pass)
existing_cb.draw(pass)
end
end
return m