1 from panda3d
.core
import AmbientLight
, DirectionalLight
, Point3
, Texture
, \
2 TextPropertiesManager
, TextNode
, Spotlight
, PerspectiveLens
, BitMask32
3 from panda3d
.bullet
import BulletPlaneShape
, BulletGhostNode
4 from direct
.gui
.OnscreenImage
import OnscreenImage
5 from direct
.gui
.OnscreenText
import OnscreenText
6 from direct
.gui
.DirectGui
import DirectButton
, DirectFrame
7 from direct
.gui
.DirectGuiGlobals
import FLAT
, DISABLED
, NORMAL
8 from direct
.showbase
.DirectObject
import DirectObject
9 from pmachines
.items
.background
import Background
10 from pmachines
.items
.box
import Box
11 from pmachines
.items
.shelf
import Shelf
12 from pmachines
.items
.domino
import Domino
13 from pmachines
.items
.basketball
import Basketball
14 from pmachines
.sidepanel
import SidePanel
15 from lib
.engine
.gui
.cursor
import MouseCursor
16 from lib
.lib
.p3d
.gfx
import P3dGfxMgr
19 class Scene(DirectObject
):
21 def __init__(self
, world
, exit_cb
, auto_close_instr
):
24 self
._exit
_cb
= exit_cb
26 self
._cursor
= MouseCursor(
27 'assets/buttons/arrowUpLeft.png', (.04, 1, .04), (.5, .5, .5, 1),
32 self
._set
_mouse
_plane
()
36 self
._item
_active
= None
39 self
.__restore
_state
()
41 self
._set
_instructions
()
42 self
._bg
= Background()
43 self
._side
_panel
= SidePanel(world
, self
._mouse
_plane
_node
, (-5, 4), (-3, 1), 1, self
.items
)
44 self
._scene
_tsk
= taskMgr
.add(self
.on_frame
, 'on_frame')
46 def current_bottom(self
):
48 for item
in self
.items
:
50 curr_bottom
= min(curr_bottom
, item
.get_bottom())
54 [itm
.destroy() for itm
in self
.items
]
55 self
.items
= [Box(self
._world
, self
._mouse
_plane
_node
, 3, self
.cb_inst
, self
.current_bottom
, self
.repos
)]
56 self
.items
+= [Shelf(self
._world
, self
._mouse
_plane
_node
, 3, self
.cb_inst
, self
.current_bottom
, self
.repos
)]
57 self
.items
+= [Domino(self
._world
, self
._mouse
_plane
_node
, 3, self
.cb_inst
, self
.current_bottom
, self
.repos
)]
58 self
.items
+= [Basketball(self
._world
, self
._mouse
_plane
_node
, 3, self
.cb_inst
, self
.current_bottom
, self
.repos
)]
66 self
._unset
_mouse
_plane
()
67 [itm
.destroy() for itm
in self
.items
]
69 self
._side
_panel
.destroy()
70 self
._cursor
.destroy()
71 taskMgr
.remove(self
._scene
_tsk
)
73 def _set_camera(self
):
74 base
.camera
.set_pos(0, -20, 0)
75 base
.camera
.look_at(0, 0, 0)
77 def __load_img_btn(self
, path
, col
):
78 img
= OnscreenImage('assets/buttons/%s.png' % path
)
79 img
.set_transparency(True)
85 def load_images_btn(path
, col
):
88 (.6, .6, .6, 1), # ready
90 (.8, .8, .8, 1), # rollover
96 (.4, .1, .1, .4)]}[col
]
97 return [self
.__load
_img
_btn
(path
, col
) for col
in colors
]
98 abl
, abr
= base
.a2dBottomLeft
, base
.a2dBottomRight
100 ('home', self
.on_home
, NORMAL
, abl
, 'gray'),
101 ('information', self
._set
_instructions
, NORMAL
, abl
, 'gray'),
102 ('right', self
.on_play
, NORMAL
, abr
, 'green'),
103 ('next', self
.on_next
, DISABLED
, abr
, 'gray'),
104 ('previous', self
.on_prev
, DISABLED
, abr
, 'gray'),
105 ('rewind', self
.reset
, NORMAL
, abr
, 'gray')]
108 for binfo
in btn_info
:
109 imgs
= load_images_btn(binfo
[0], binfo
[4])
110 if binfo
[3] == base
.a2dBottomLeft
:
114 sign
, num
= -1, num_r
116 fcols
= (.4, .4, .4, .14), (.3, .3, .3, .05)
118 image
=imgs
, scale
=.05, pos
=(sign
* (.06 + .11 * num
), 1, .06),
119 parent
=binfo
[3], command
=binfo
[1], state
=binfo
[2], relief
=FLAT
,
120 frameColor
=fcols
[0] if binfo
[2] == NORMAL
else fcols
[1],
121 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
122 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
123 btn
.set_transparency(True)
125 self
.__home
_btn
, self
.__info
_btn
, self
.__right
_btn
, self
.__next
_btn
, \
126 self
.__prev
_btn
, self
.__rewind
_btn
= btns
128 def _unset_gui(self
):
130 self
.__home
_btn
, self
.__info
_btn
, self
.__right
_btn
,
131 self
.__next
_btn
, self
.__prev
_btn
, self
.__rewind
_btn
]
132 [btn
.destroy() for btn
in btns
]
134 def _set_spotlight(self
, name
, pos
, look_at
, color
, shadows
=False):
135 light
= Spotlight(name
)
137 light
.setLens(PerspectiveLens())
138 light_np
= render
.attach_new_node(light
)
139 light_np
.set_pos(pos
)
140 light_np
.look_at(look_at
)
141 light
.set_color(color
)
142 render
.set_light(light_np
)
145 def _set_lights(self
):
146 alight
= AmbientLight('alight') # for ao
147 alight
.set_color((.15, .15, .15, 1))
148 self
._alnp
= render
.attach_new_node(alight
)
149 render
.set_light(self
._alnp
)
150 self
._key
_light
= self
._set
_spotlight
(
151 'key light', (-5, -80, 5), (0, 0, 0), (2.8, 2.8, 2.8, 1))
152 self
._shadow
_light
= self
._set
_spotlight
(
153 'key light', (-5, -80, 5), (0, 0, 0), (.58, .58, .58, 1), True)
154 self
._shadow
_light
.node().set_shadow_caster(True, 2048, 2048)
155 self
._shadow
_light
.node().get_lens().set_film_size(2048, 2048)
156 self
._shadow
_light
.node().get_lens().set_near_far(1, 256)
157 self
._shadow
_light
.node().set_camera_mask(BitMask32(0x01))
159 def _unset_lights(self
):
160 for light
in [self
._alnp
, self
._key
_light
, self
._shadow
_light
]:
161 render
.clear_light(light
)
164 def _set_input(self
):
165 self
.accept('mouse1', self
.on_click_l
)
166 self
.accept('mouse1-up', self
.on_release
)
167 self
.accept('mouse3', self
.on_click_r
)
168 self
.accept('mouse3-up', self
.on_release
)
170 def _unset_input(self
):
171 for evt
in ['mouse1', 'mouse1-up', 'mouse3', 'mouse3-up']:
174 def _set_mouse_plane(self
):
175 shape
= BulletPlaneShape((0, -1, 0), 0)
176 #self._mouse_plane_node = BulletRigidBodyNode('mouse plane')
177 self
._mouse
_plane
_node
= BulletGhostNode('mouse plane')
178 self
._mouse
_plane
_node
.addShape(shape
)
179 #np = render.attachNewNode(self._mouse_plane_node)
180 #self._world.attachRigidBody(self._mouse_plane_node)
181 self
._world
.attach_ghost(self
._mouse
_plane
_node
)
183 def _unset_mouse_plane(self
):
184 self
._world
.remove_ghost(self
._mouse
_plane
_node
)
187 if not base
.mouseWatcherNode
.has_mouse(): return []
188 p_from
, p_to
= P3dGfxMgr
.world_from_to(base
.mouseWatcherNode
.get_mouse())
189 return self
._world
.ray_test_all(p_from
, p_to
).get_hits()
191 def _on_click(self
, method
):
194 for hit
in self
._get
_hits
():
195 if hit
.get_node() == self
._mouse
_plane
_node
:
196 pos
= hit
.get_hit_pos()
197 for hit
in self
._get
_hits
():
198 for item
in [i
for i
in self
.items
if hit
.get_node() == i
.node
]:
199 if not self
._item
_active
:
200 self
._item
_active
= item
201 getattr(item
, method
)(pos
)
202 img
= 'move' if method
== 'on_click_l' else 'rotate'
203 self
._cursor
.set_image('assets/buttons/%s.png' % img
)
205 def on_click_l(self
):
206 self
._on
_click
('on_click_l')
208 def on_click_r(self
):
209 self
._on
_click
('on_click_r')
211 def on_release(self
):
212 if self
._item
_active
and not self
._item
_active
._first
_command
:
213 self
._commands
= self
._commands
[:self
._command
_idx
]
214 self
._commands
+= [self
._item
_active
]
215 self
._command
_idx
+= 1
216 self
.__prev
_btn
['state'] = NORMAL
217 fcols
= (.4, .4, .4, .14), (.3, .3, .3, .05)
218 self
.__prev
_btn
['frameColor'] = fcols
[0]
219 if self
._item
_active
._command
_idx
== len(self
._item
_active
._commands
) - 1:
220 self
.__next
_btn
['state'] = DISABLED
221 self
.__next
_btn
['frameColor'] = fcols
[1]
222 self
._item
_active
= None
223 [item
.on_release() for item
in self
.items
]
224 self
._cursor
.set_image('assets/buttons/arrowUpLeft.png')
227 for item
in self
.items
:
228 item
.repos_done
= False
229 self
.items
= sorted(self
.items
, key
=lambda itm
: itm
.__class
__.__name
__)
230 [item
.on_aspect_ratio_changed() for item
in self
.items
]
231 self
._side
_panel
.update(self
.items
)
233 for item
in self
.items
:
234 if not item
._instantiated
:
235 max_x
= max(item
._np
.get_x(), max_x
)
236 for item
in self
.items
:
237 if not item
._instantiated
:
240 def on_aspect_ratio_changed(self
):
243 def on_frame(self
, task
):
244 hits
= self
._get
_hits
()
246 for hit
in self
._get
_hits
():
247 if hit
.get_node() == self
._mouse
_plane
_node
:
248 pos
= hit
.get_hit_pos()
249 hit_nodes
= [hit
.get_node() for hit
in hits
]
250 if self
._item
_active
:
251 items_hit
= [self
._item
_active
]
253 items_hit
= [itm
for itm
in self
.items
if itm
.node
in hit_nodes
]
254 items_no_hit
= [itm
for itm
in self
.items
if itm
not in items_hit
]
255 [itm
.on_mouse_on() for itm
in items_hit
]
256 [itm
.on_mouse_off() for itm
in items_no_hit
]
257 if pos
and self
._item
_active
:
258 self
._item
_active
.on_mouse_move(pos
)
261 def cb_inst(self
, item
):
265 [itm
.play() for itm
in self
.items
]
268 self
._commands
[self
._command
_idx
].redo()
269 self
._command
_idx
+= 1
270 fcols
= (.4, .4, .4, .14), (.3, .3, .3, .05)
271 self
.__prev
_btn
['state'] = NORMAL
272 self
.__prev
_btn
['frameColor'] = fcols
[0]
273 more_commands
= self
._command
_idx
< len(self
._commands
)
274 self
.__next
_btn
['state'] = NORMAL
if more_commands
else DISABLED
275 self
.__next
_btn
['frameColor'] = fcols
[0] if more_commands
else fcols
[1]
278 self
._command
_idx
-= 1
279 self
._commands
[self
._command
_idx
].undo()
280 fcols
= (.4, .4, .4, .14), (.3, .3, .3, .05)
281 self
.__next
_btn
['state'] = NORMAL
282 self
.__next
_btn
['frameColor'] = fcols
[0]
283 self
.__prev
_btn
['state'] = NORMAL
if self
._command
_idx
else DISABLED
284 self
.__prev
_btn
['frameColor'] = fcols
[0] if self
._command
_idx
else fcols
[1]
289 def _set_instructions(self
):
292 mgr
= TextPropertiesManager
.get_global_ptr()
293 for name
in ['mouse_l', 'mouse_r']:
294 graphic
= OnscreenImage('assets/buttons/%s.png' % name
)
295 graphic
.set_scale(.5)
296 graphic
.get_texture().set_minfilter(Texture
.FTLinearMipmapLinear
)
297 graphic
.get_texture().set_anisotropic_degree(2)
298 mgr
.set_graphic(name
, graphic
)
300 graphic
.set_transparency(True)
301 graphic
.detach_node()
302 frm
= DirectFrame(frameColor
=(.4, .4, .4, .06),
303 frameSize
=(-.6, .6, -.3, .3))
304 font
= base
.loader
.load_font('assets/fonts/Hanken-Book.ttf')
306 font
.set_pixels_per_unit(60)
307 font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
308 font
.set_outline((0, 0, 0, 1), .8, .2)
309 txt
= _('keep \5mouse_l\5 pressed to drag an item\n\n'
310 'keep \5mouse_r\5 pressed to rotate an item')
311 self
._txt
= OnscreenText(
312 txt
, parent
=frm
, font
=font
, scale
=0.06, fg
=(.9, .9, .9, 1),
313 align
=TextNode
.A_left
)
314 u_l
= self
._txt
.textNode
.get_upper_left_3d()
315 l_r
= self
._txt
.textNode
.get_lower_right_3d()
316 w
, h
= l_r
[0] - u_l
[0], u_l
[2] - l_r
[2]
319 z
= h
/ 2 - font
.get_line_height() * self
._txt
['scale'][1]
320 z
+= (btn_scale
+ 2 * mar
) / 2
321 self
._txt
['pos'] = -w
/ 2, z
322 u_l
= self
._txt
.textNode
.get_upper_left_3d()
323 l_r
= self
._txt
.textNode
.get_lower_right_3d()
324 c_l_r
= l_r
[0], l_r
[1], l_r
[2] - 2 * mar
- btn_scale
325 fsz
= u_l
[0] - mar
, l_r
[0] + mar
, c_l_r
[2] - mar
, u_l
[2] + mar
326 frm
['frameSize'] = fsz
328 (.6, .6, .6, 1), # ready
329 (1, 1, 1, 1), # press
330 (.8, .8, .8, 1), # rollover
332 imgs
= [self
.__load
_img
_btn
('exitRight', col
) for col
in colors
]
334 image
=imgs
, scale
=btn_scale
,
335 pos
=(l_r
[0] - btn_scale
, 1, l_r
[2] - mar
- btn_scale
),
336 parent
=frm
, command
=self
.__on
_close
_instructions
, extraArgs
=[frm
],
337 relief
=FLAT
, frameColor
=(.6, .6, .6, .08),
338 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
339 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
340 btn
.set_transparency(True)
342 def __store_state(self
):
344 self
.__home
_btn
, self
.__info
_btn
, self
.__right
_btn
,
345 self
.__next
_btn
, self
.__prev
_btn
, self
.__rewind
_btn
]
346 self
.__btn
_state
= [btn
['state'] for btn
in btns
]
348 btn
['state'] = DISABLED
349 [itm
.store_state() for itm
in self
.items
]
351 def __restore_state(self
):
353 self
.__home
_btn
, self
.__info
_btn
, self
.__right
_btn
,
354 self
.__next
_btn
, self
.__prev
_btn
, self
.__rewind
_btn
]
355 for btn
, state
in zip(btns
, self
.__btn
_state
):
357 [itm
.restore_state() for itm
in self
.items
]
360 def __on_close_instructions(self
, frm
):
362 self
.__restore
_state
()