6562af21a45070603b56472ad3cef39512604297
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 lib
.lib
.p3d
.gfx
import P3dGfxMgr
, set_srgb
10 def __init__(self
, pos
, rot
):
17 def end_condition(self
):
23 def __init__(self
, np
):
28 def end_condition(self
):
29 self
._positions
+= [self
._np
.get_pos()]
30 self
._rotations
+= [self
._np
.get_hpr()]
31 if len(self
._positions
) > 10:
32 self
._positions
.pop(0)
33 if len(self
._rotations
) > 10:
34 self
._rotations
.pop(0)
35 if len(self
._positions
) < 8:
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):
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
72 self
.node
= BulletGhostNode(self
.__class
__.__name
__)
74 self
.node
= BulletRigidBodyNode(self
.__class
__.__name
__)
76 self
._np
= render
.attach_new_node(self
.node
)
78 world
.attach_ghost(self
.node
)
80 world
.attach_rigid_body(self
.node
)
81 self
._model
= loader
.load_model(model_path
)
83 self
._model
.flatten_light()
84 self
._model
.reparent_to(self
._np
)
85 self
._np
.set_scale(model_scale
)
87 self
._set
_outline
_model
()
88 set_srgb(self
._outline
_model
)
89 self
._model
.hide(BitMask32(0x01))
90 self
._outline
_model
.hide(BitMask32(0x01))
91 self
._start
_drag
_pos
= None
92 self
._prev
_rot
_info
= None
93 self
._last
_nonoverlapping
_pos
= None
94 self
._last
_nonoverlapping
_rot
= None
95 self
._instantiated
= not count
96 self
.interactable
= count
97 self
._box
_tsk
= taskMgr
.add(self
.on_frame
, 'on_frame')
101 self
._np
.set_pos(pos
)
104 def _set_shape(self
):
107 def set_strategy(self
, strategy
):
108 self
.strategy
= strategy
111 p_from
, p_to
= P3dGfxMgr
.world_from_to((-1, 1))
112 for hit
in self
._world
.ray_test_all(p_from
, p_to
).get_hits():
113 if hit
.get_node() == self
._plane
_node
:
114 pos
= hit
.get_hit_pos()
115 corner
= P3dGfxMgr
.screen_coord(pos
)
116 bounds
= self
._np
.get_tight_bounds()
117 bounds
= bounds
[0] - self
._np
.get_pos(), bounds
[1] - self
._np
.get_pos()
118 self
._np
.set_pos(pos
)
119 plane
= Plane(Vec3(0, 1, 0), bounds
[0][1])
120 pos3d
, near_pt
, far_pt
= Point3(), Point3(), Point3()
121 margin
, ar
= .04, base
.get_aspect_ratio()
122 margin_x
= margin
/ ar
if ar
>= 1 else margin
123 margin_z
= margin
* ar
if ar
< 1 else margin
124 top
= self
._curr
_bottom
()
125 base
.camLens
.extrude((-1 + margin_x
, top
- margin_z
), near_pt
, far_pt
)
126 plane
.intersects_line(
127 pos3d
, render
.get_relative_point(base
.camera
, near_pt
),
128 render
.get_relative_point(base
.camera
, far_pt
))
129 delta
= Vec3(bounds
[1][0], bounds
[1][1], bounds
[0][2])
130 self
._np
.set_pos(pos3d
+ delta
)
131 if not hasattr(self
, '_txt'):
132 font
= base
.loader
.load_font('assets/fonts/Hanken-Book.ttf')
134 font
.set_pixels_per_unit(60)
135 font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
136 font
.set_outline((0, 0, 0, 1), .8, .2)
137 self
._txt
= OnscreenText(
138 str(self
._count
), font
=font
, scale
=0.06, fg
=(.9, .9, .9, 1))
139 pos
= self
._np
.get_pos() + (bounds
[1][0], bounds
[0][1], bounds
[0][2])
140 p2d
= P3dGfxMgr
.screen_coord(pos
)
141 self
._txt
['pos'] = p2d
142 self
.repos_done
= True
144 def repos_x(self
, x
):
146 bounds
= self
._np
.get_tight_bounds()
147 bounds
= bounds
[0] - self
._np
.get_pos(), bounds
[1] - self
._np
.get_pos()
148 pos
= self
._np
.get_pos() + (bounds
[1][0], bounds
[0][1], bounds
[0][2])
149 p2d
= P3dGfxMgr
.screen_coord(pos
)
150 self
._txt
['pos'] = p2d
152 def get_bottom(self
):
153 bounds
= self
._np
.get_tight_bounds()
154 bounds
= bounds
[0] - self
._np
.get_pos(), bounds
[1] - self
._np
.get_pos()
155 pos
= self
._np
.get_pos() + (bounds
[1][0], bounds
[1][1], bounds
[0][2])
156 p2d
= P3dGfxMgr
.screen_coord(pos
)
157 ar
= base
.get_aspect_ratio()
158 return p2d
[1] if ar
>= 1 else (p2d
[1] * ar
)
160 def get_corner(self
):
161 bounds
= self
._np
.get_tight_bounds()
162 return bounds
[1][0], bounds
[1][1], bounds
[0][2]
164 def _set_outline_model(self
):
165 self
._outline
_model
= loader
.load_model(self
._model
_path
)
166 #clockw = CullFaceAttrib.MCullClockwise
167 #self._outline_model.set_attrib(CullFaceAttrib.make(clockw))
168 self
._outline
_model
.set_attrib(CullFaceAttrib
.make_reverse())
169 self
._outline
_model
.reparent_to(self
._np
)
170 self
._outline
_model
.set_scale(1.08)
171 self
._outline
_model
.set_light_off()
172 self
._outline
_model
.set_color(.4, .4, .4, 1)
173 self
._outline
_model
.set_color_scale(.4, .4, .4, 1)
174 self
._outline
_model
.hide()
176 def on_frame(self
, task
):
181 self
._command
_idx
-= 1
182 self
._np
.set_pos(self
._commands
[self
._command
_idx
].pos
)
183 self
._np
.set_hpr(self
._commands
[self
._command
_idx
].rot
)
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 if not self
._instantiated
:
193 self
._world
.remove_rigid_body(self
.node
)
194 self
.node
.set_mass(self
._mass
)
195 self
._world
.attach_rigid_body(self
.node
)
197 def on_click_l(self
, pos
):
198 if self
._paused
: return
199 self
._start
_drag
_pos
= pos
, self
._np
.get_pos()
200 loader
.load_sfx('assets/audio/sfx/grab.ogg').play()
201 if not self
._instantiated
:
202 self
._world
.remove_ghost(self
.node
)
203 pos
= self
._np
.get_pos()
204 self
._np
.remove_node()
205 self
.node
= BulletRigidBodyNode('box')
207 self
._np
= render
.attach_new_node(self
.node
)
208 self
._world
.attach_rigid_body(self
.node
)
209 self
._model
.reparent_to(self
._np
)
210 self
._np
.set_pos(pos
)
211 self
._set
_outline
_model
()
212 self
._np
.set_scale(self
._model
_scale
)
213 self
._model
.show(BitMask32(0x01))
214 self
._outline
_model
.hide(BitMask32(0x01))
215 self
._instantiated
= True
219 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
223 def on_click_r(self
, pos
):
224 if self
._paused
or not self
._instantiated
: return
225 self
._prev
_rot
_info
= pos
, self
._np
.get_pos(), self
._np
.get_r()
226 loader
.load_sfx('assets/audio/sfx/grab.ogg').play()
228 def on_release(self
):
229 if self
._start
_drag
_pos
or self
._prev
_rot
_info
:
230 loader
.load_sfx('assets/audio/sfx/release.ogg').play()
231 self
._command
_idx
+= 1
232 self
._commands
= self
._commands
[:self
._command
_idx
]
233 self
._commands
+= [Command(self
._np
.get_pos(), self
._np
.get_hpr())]
234 self
._first
_command
= False
235 self
._start
_drag
_pos
= self
._prev
_rot
_info
= None
236 if self
._overlapping
:
237 self
._np
.set_pos(self
._last
_nonoverlapping
_pos
)
238 self
._np
.set_hpr(self
._last
_nonoverlapping
_rot
)
239 self
._outline
_model
.set_color(.4, .4, .4, 1)
240 self
._outline
_model
.set_color_scale(.4, .4, .4, 1)
241 self
._overlapping
= False
243 def on_mouse_on(self
):
244 if not self
._paused
and self
.interactable
:
245 self
._outline
_model
.show()
247 def on_mouse_off(self
):
248 if self
._start
_drag
_pos
or self
._prev
_rot
_info
: return
249 if self
.interactable
:
250 self
._outline
_model
.hide()
252 def on_mouse_move(self
, pos
):
253 if self
._start
_drag
_pos
:
254 d_pos
= pos
- self
._start
_drag
_pos
[0]
255 self
._np
.set_pos(self
._start
_drag
_pos
[1] + d_pos
)
256 if self
._prev
_rot
_info
:
257 start_vec
= self
._prev
_rot
_info
[0] - self
._prev
_rot
_info
[1]
258 curr_vec
= pos
- self
._prev
_rot
_info
[1]
259 d_angle
= curr_vec
.signed_angle_deg(start_vec
, (0, -1, 0))
260 self
._np
.set_r(self
._prev
_rot
_info
[2] + d_angle
)
261 self
._prev
_rot
_info
= pos
, self
._np
.get_pos(), self
._np
.get_r()
262 if self
._start
_drag
_pos
or self
._prev
_rot
_info
:
263 res
= self
._world
.contact_test(self
.node
)
264 nres
= res
.get_num_contacts()
265 if nres
<= self
._exp
_num
_contacts
:
266 self
._overlapping
= False
267 self
._outline
_model
.set_color(.4, .4, .4, 1)
268 self
._outline
_model
.set_color_scale(.4, .4, .4, 1)
269 if nres
> self
._exp
_num
_contacts
and not self
._overlapping
:
271 for contact
in res
.get_contacts():
272 for node
in [contact
.get_node0(), contact
.get_node1()]:
273 if isinstance(node
, BulletRigidBodyNode
) and \
277 self
._overlapping
= True
278 loader
.load_sfx('assets/audio/sfx/overlap.ogg').play()
279 self
._outline
_model
.set_color(.9, .1, .1, 1)
280 self
._outline
_model
.set_color_scale(.9, .1, .1, 1)
281 if not self
._overlapping
:
282 self
._last
_nonoverlapping
_pos
= self
._np
.get_pos()
283 self
._last
_nonoverlapping
_rot
= self
._np
.get_hpr()
285 def on_aspect_ratio_changed(self
):
286 if not self
._instantiated
:
289 def store_state(self
):
291 self
._model
.set_transparency(True)
292 self
._model
.set_alpha_scale(.3)
293 if hasattr(self
, '_txt') and not self
._txt
.is_empty():
294 self
._txt
.set_alpha_scale(.3)
296 def restore_state(self
):
298 self
._model
.set_alpha_scale(1)
299 if hasattr(self
, '_txt') and not self
._txt
.is_empty():
300 self
._txt
.set_alpha_scale(1)
303 self
._np
.remove_node()
304 taskMgr
.remove(self
._box
_tsk
)
305 if hasattr(self
, '_txt'):
307 if not self
._instantiated
:
308 self
._world
.remove_ghost(self
.node
)
310 self
._world
.remove_rigid_body(self
.node
)