9eb5afb8ee809e1bc02addd76bb5b7adb0f4c44f
1 from panda3d
.core
import CullFaceAttrib
, Point3
, NodePath
, Point2
, Texture
, \
3 from panda3d
.bullet
import BulletBoxShape
, BulletRigidBodyNode
, BulletGhostNode
4 from direct
.gui
.OnscreenText
import OnscreenText
5 from ya2
.lib
.p3d
.gfx
import P3dGfxMgr
, set_srgb
10 def __init__(self
, pos
, rot
):
17 def win_condition(self
):
23 def __init__(self
, np
):
28 def win_condition(self
):
29 self
._positions
+= [self
._np
.get_pos()]
30 self
._rotations
+= [self
._np
.get_hpr()]
31 if len(self
._positions
) > 30:
32 self
._positions
.pop(0)
33 if len(self
._rotations
) > 30:
34 self
._rotations
.pop(0)
35 if len(self
._positions
) < 28:
37 avg_x
= sum(pos
.x
for pos
in self
._positions
) / len(self
._positions
)
38 avg_y
= sum(pos
.y
for pos
in self
._positions
) / len(self
._positions
)
39 avg_z
= sum(pos
.z
for pos
in self
._positions
) / len(self
._positions
)
40 avg_h
= sum(rot
.x
for rot
in self
._rotations
) / len(self
._rotations
)
41 avg_p
= sum(rot
.y
for rot
in self
._rotations
) / len(self
._rotations
)
42 avg_r
= sum(rot
.z
for rot
in self
._rotations
) / len(self
._rotations
)
43 avg_pos
= Point3(avg_x
, avg_y
, avg_z
)
44 avg_rot
= Point3(avg_h
, avg_p
, avg_r
)
45 return all((pos
- avg_pos
).length() < .1 for pos
in self
._positions
) and \
46 all((rot
- avg_rot
).length() < 1 for rot
in self
._rotations
)
51 def __init__(self
, world
, plane_node
, cb_inst
, curr_bottom
, scene_repos
, model_path
, model_scale
=1, exp_num_contacts
=1, mass
=1, pos
=(0, 0, 0), r
=0, count
=0, restitution
=.5, friction
=.5):
53 self
._plane
_node
= plane_node
55 self
._cb
_inst
= cb_inst
57 self
._overlapping
= False
58 self
._first
_command
= True
59 self
.repos_done
= False
63 self
.strategy
= FixedStrategy()
64 self
._exp
_num
_contacts
= exp_num_contacts
65 self
._curr
_bottom
= curr_bottom
66 self
._scene
_repos
= scene_repos
67 self
._model
_scale
= model_scale
68 self
._model
_path
= model_path
70 self
._command
_idx
= -1
71 self
._restitution
= restitution
72 self
._friction
= friction
76 self
.node
= BulletGhostNode(self
.__class
__.__name
__)
78 self
.node
= BulletRigidBodyNode(self
.__class
__.__name
__)
79 self
._set
_shape
(count
)
80 self
._np
= render
.attach_new_node(self
.node
)
82 world
.attach_ghost(self
.node
)
84 world
.attach_rigid_body(self
.node
)
85 self
._model
= loader
.load_model(model_path
)
87 self
._model
.flatten_light()
88 self
._model
.reparent_to(self
._np
)
89 self
._np
.set_scale(model_scale
)
90 self
._np
.flatten_strong()
92 self
._set
_outline
_model
()
93 set_srgb(self
._outline
_model
)
94 self
._model
.hide(BitMask32(0x01))
95 self
._outline
_model
.hide(BitMask32(0x01))
96 self
._start
_drag
_pos
= None
97 self
._prev
_rot
_info
= None
98 self
._last
_nonoverlapping
_pos
= None
99 self
._last
_nonoverlapping
_rot
= None
100 self
._instantiated
= not count
101 self
.interactable
= count
102 self
._box
_tsk
= taskMgr
.add(self
.on_frame
, 'on_frame')
106 self
._np
.set_pos(pos
)
109 def _set_shape(self
, apply_scale
=True):
112 def set_strategy(self
, strategy
):
113 self
.strategy
= strategy
116 p_from
, p_to
= P3dGfxMgr
.world_from_to((-1, 1))
117 for hit
in self
._world
.ray_test_all(p_from
, p_to
).get_hits():
118 if hit
.get_node() == self
._plane
_node
:
119 pos
= hit
.get_hit_pos()
120 corner
= P3dGfxMgr
.screen_coord(pos
)
121 bounds
= self
._np
.get_tight_bounds()
122 bounds
= bounds
[0] - self
._np
.get_pos(), bounds
[1] - self
._np
.get_pos()
123 self
._np
.set_pos(pos
)
124 plane
= Plane(Vec3(0, 1, 0), bounds
[0][1])
125 pos3d
, near_pt
, far_pt
= Point3(), Point3(), Point3()
126 margin
, ar
= .04, base
.get_aspect_ratio()
127 margin_x
= margin
/ ar
if ar
>= 1 else margin
128 margin_z
= margin
* ar
if ar
< 1 else margin
129 top
= self
._curr
_bottom
()
130 base
.camLens
.extrude((-1 + margin_x
, top
- margin_z
), near_pt
, far_pt
)
131 plane
.intersects_line(
132 pos3d
, render
.get_relative_point(base
.camera
, near_pt
),
133 render
.get_relative_point(base
.camera
, far_pt
))
134 delta
= Vec3(bounds
[1][0], bounds
[1][1], bounds
[0][2])
135 self
._np
.set_pos(pos3d
+ delta
)
136 if not hasattr(self
, '_txt'):
137 font
= base
.loader
.load_font('assets/fonts/Hanken-Book.ttf')
139 font
.set_pixels_per_unit(60)
140 font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
141 font
.set_outline((0, 0, 0, 1), .8, .2)
142 self
._txt
= OnscreenText(
143 str(self
._count
), font
=font
, scale
=0.06, fg
=(.9, .9, .9, 1))
144 pos
= self
._np
.get_pos() + (bounds
[1][0], bounds
[0][1], bounds
[0][2])
145 p2d
= P3dGfxMgr
.screen_coord(pos
)
146 self
._txt
['pos'] = p2d
147 self
.repos_done
= True
149 def repos_x(self
, x
):
151 bounds
= self
._np
.get_tight_bounds()
152 bounds
= bounds
[0] - self
._np
.get_pos(), bounds
[1] - self
._np
.get_pos()
153 pos
= self
._np
.get_pos() + (bounds
[1][0], bounds
[0][1], bounds
[0][2])
154 p2d
= P3dGfxMgr
.screen_coord(pos
)
155 self
._txt
['pos'] = p2d
157 def get_bottom(self
):
158 bounds
= self
._np
.get_tight_bounds()
159 bounds
= bounds
[0] - self
._np
.get_pos(), bounds
[1] - self
._np
.get_pos()
160 pos
= self
._np
.get_pos() + (bounds
[1][0], bounds
[1][1], bounds
[0][2])
161 p2d
= P3dGfxMgr
.screen_coord(pos
)
162 ar
= base
.get_aspect_ratio()
163 return p2d
[1] if ar
>= 1 else (p2d
[1] * ar
)
165 def get_corner(self
):
166 bounds
= self
._np
.get_tight_bounds()
167 return bounds
[1][0], bounds
[1][1], bounds
[0][2]
169 def _set_outline_model(self
):
170 self
._outline
_model
= loader
.load_model(self
._model
_path
)
171 #clockw = CullFaceAttrib.MCullClockwise
172 #self._outline_model.set_attrib(CullFaceAttrib.make(clockw))
173 self
._outline
_model
.set_attrib(CullFaceAttrib
.make_reverse())
174 self
._outline
_model
.reparent_to(self
._np
)
175 self
._outline
_model
.set_scale(1.08)
176 self
._outline
_model
.set_light_off()
177 self
._outline
_model
.set_color(.4, .4, .4, 1)
178 self
._outline
_model
.set_color_scale(.4, .4, .4, 1)
179 self
._outline
_model
.hide()
181 def on_frame(self
, task
):
186 self
._command
_idx
-= 1
187 self
._np
.set_pos(self
._commands
[self
._command
_idx
].pos
)
188 self
._np
.set_hpr(self
._commands
[self
._command
_idx
].rot
)
191 self
._command
_idx
+= 1
192 self
._np
.set_pos(self
._commands
[self
._command
_idx
].pos
)
193 self
._np
.set_hpr(self
._commands
[self
._command
_idx
].rot
)
196 if not self
._instantiated
:
198 self
._world
.remove_rigid_body(self
.node
)
199 self
.node
.set_mass(self
._mass
)
200 self
._world
.attach_rigid_body(self
.node
)
201 self
.node
.set_restitution(self
._restitution
)
202 self
.node
.set_friction(self
._friction
)
204 def on_click_l(self
, pos
):
205 if self
._paused
: return
206 self
._start
_drag
_pos
= pos
, self
._np
.get_pos()
207 loader
.load_sfx('assets/audio/sfx/grab.ogg').play()
208 if not self
._instantiated
:
209 self
._world
.remove_ghost(self
.node
)
210 pos
= self
._np
.get_pos()
211 self
._np
.remove_node()
212 self
.node
= BulletRigidBodyNode('box')
214 self
._np
= render
.attach_new_node(self
.node
)
215 self
._world
.attach_rigid_body(self
.node
)
216 self
._model
.reparent_to(self
._np
)
217 self
._np
.set_pos(pos
)
218 self
._set
_outline
_model
()
219 self
._np
.set_scale(self
._model
_scale
)
220 self
._model
.show(BitMask32(0x01))
221 self
._outline
_model
.hide(BitMask32(0x01))
222 self
._instantiated
= True
226 item
= self
.__class
__(self
._world
, self
._plane
_node
, self
._cb
_inst
, self
._curr
_bottom
, self
._scene
_repos
, count
=self
._count
, mass
=self
._mass
, pos
=self
._pos
, r
=self
._r
) # pylint: disable=no-value-for-parameter
230 def on_click_r(self
, pos
):
231 if self
._paused
or not self
._instantiated
: return
232 self
._prev
_rot
_info
= pos
, self
._np
.get_pos(), self
._np
.get_r()
233 loader
.load_sfx('assets/audio/sfx/grab.ogg').play()
235 def on_release(self
):
236 if self
._start
_drag
_pos
or self
._prev
_rot
_info
:
237 loader
.load_sfx('assets/audio/sfx/release.ogg').play()
238 self
._command
_idx
+= 1
239 self
._commands
= self
._commands
[:self
._command
_idx
]
240 self
._commands
+= [Command(self
._np
.get_pos(), self
._np
.get_hpr())]
241 self
._first
_command
= False
242 self
._start
_drag
_pos
= self
._prev
_rot
_info
= None
243 if self
._overlapping
:
244 self
._np
.set_pos(self
._last
_nonoverlapping
_pos
)
245 self
._np
.set_hpr(self
._last
_nonoverlapping
_rot
)
246 self
._outline
_model
.set_color(.4, .4, .4, 1)
247 self
._outline
_model
.set_color_scale(.4, .4, .4, 1)
248 self
._overlapping
= False
250 def on_mouse_on(self
):
251 if not self
._paused
and self
.interactable
:
252 self
._outline
_model
.show()
254 def on_mouse_off(self
):
255 if self
._start
_drag
_pos
or self
._prev
_rot
_info
: return
256 if self
.interactable
:
257 self
._outline
_model
.hide()
259 def on_mouse_move(self
, pos
):
260 if self
._start
_drag
_pos
:
261 d_pos
= pos
- self
._start
_drag
_pos
[0]
262 self
._np
.set_pos(self
._start
_drag
_pos
[1] + d_pos
)
263 if self
._prev
_rot
_info
:
264 start_vec
= self
._prev
_rot
_info
[0] - self
._prev
_rot
_info
[1]
265 curr_vec
= pos
- self
._prev
_rot
_info
[1]
266 d_angle
= curr_vec
.signed_angle_deg(start_vec
, (0, -1, 0))
267 self
._np
.set_r(self
._prev
_rot
_info
[2] + d_angle
)
268 self
._prev
_rot
_info
= pos
, self
._np
.get_pos(), self
._np
.get_r()
269 if self
._start
_drag
_pos
or self
._prev
_rot
_info
:
270 res
= self
._world
.contact_test(self
.node
)
271 nres
= res
.get_num_contacts()
272 if nres
<= self
._exp
_num
_contacts
:
273 self
._overlapping
= False
274 self
._outline
_model
.set_color(.4, .4, .4, 1)
275 self
._outline
_model
.set_color_scale(.4, .4, .4, 1)
276 if nres
> self
._exp
_num
_contacts
and not self
._overlapping
:
278 for contact
in res
.get_contacts():
279 for node
in [contact
.get_node0(), contact
.get_node1()]:
280 if isinstance(node
, BulletRigidBodyNode
) and \
284 self
._overlapping
= True
285 loader
.load_sfx('assets/audio/sfx/overlap.ogg').play()
286 self
._outline
_model
.set_color(.9, .1, .1, 1)
287 self
._outline
_model
.set_color_scale(.9, .1, .1, 1)
288 if not self
._overlapping
:
289 self
._last
_nonoverlapping
_pos
= self
._np
.get_pos()
290 self
._last
_nonoverlapping
_rot
= self
._np
.get_hpr()
292 def on_aspect_ratio_changed(self
):
293 if not self
._instantiated
:
296 def store_state(self
):
298 self
._model
.set_transparency(True)
299 self
._model
.set_alpha_scale(.3)
300 if hasattr(self
, '_txt') and not self
._txt
.is_empty():
301 self
._txt
.set_alpha_scale(.3)
303 def restore_state(self
):
305 self
._model
.set_alpha_scale(1)
306 if hasattr(self
, '_txt') and not self
._txt
.is_empty():
307 self
._txt
.set_alpha_scale(1)
309 def fail_condition(self
):
310 if self
._np
.get_z() < -6:
312 self
._positions
+= [self
._np
.get_pos()]
313 self
._rotations
+= [self
._np
.get_hpr()]
314 if len(self
._positions
) > 30:
315 self
._positions
.pop(0)
316 if len(self
._rotations
) > 30:
317 self
._rotations
.pop(0)
318 if len(self
._positions
) < 28:
320 avg_x
= sum(pos
.x
for pos
in self
._positions
) / len(self
._positions
)
321 avg_y
= sum(pos
.y
for pos
in self
._positions
) / len(self
._positions
)
322 avg_z
= sum(pos
.z
for pos
in self
._positions
) / len(self
._positions
)
323 avg_h
= sum(rot
.x
for rot
in self
._rotations
) / len(self
._rotations
)
324 avg_p
= sum(rot
.y
for rot
in self
._rotations
) / len(self
._rotations
)
325 avg_r
= sum(rot
.z
for rot
in self
._rotations
) / len(self
._rotations
)
326 avg_pos
= Point3(avg_x
, avg_y
, avg_z
)
327 avg_rot
= Point3(avg_h
, avg_p
, avg_r
)
328 return all((pos
- avg_pos
).length() < .1 for pos
in self
._positions
) and \
329 all((rot
- avg_rot
).length() < 1 for rot
in self
._rotations
)
332 self
._np
.remove_node()
333 taskMgr
.remove(self
._box
_tsk
)
334 if hasattr(self
, '_txt'):
336 if not self
._instantiated
:
337 self
._world
.remove_ghost(self
.node
)
339 self
._world
.remove_rigid_body(self
.node
)