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
.sidepanel
import SidePanel
14 from lib
.engine
.gui
.cursor
import MouseCursor
15 from lib
.lib
.p3d
.gfx
import P3dGfxMgr
18 class Scene(DirectObject
):
20 def __init__(self
, world
, exit_cb
, auto_close_instr
):
23 self
._exit
_cb
= exit_cb
25 self
._cursor
= MouseCursor(
26 'assets/buttons/arrowUpLeft.png', (.04, 1, .04), (.5, .5, .5, 1),
31 self
._set
_mouse
_plane
()
35 self
._item
_active
= None
38 self
.__restore
_state
()
40 self
._set
_instructions
()
41 self
._bg
= Background()
42 self
._side
_panel
= SidePanel(world
, self
._mouse
_plane
_node
, (-5, 4), (-3, 1), 1, self
.items
)
43 self
._scene
_tsk
= taskMgr
.add(self
.on_frame
, 'on_frame')
45 def current_bottom(self
):
47 for item
in self
.items
:
49 curr_bottom
= min(curr_bottom
, item
.get_bottom())
53 [itm
.destroy() for itm
in self
.items
]
54 self
.items
= [Box(self
._world
, self
._mouse
_plane
_node
, 3, self
.cb_inst
, self
.current_bottom
, self
.repos
)]
55 self
.items
+= [Shelf(self
._world
, self
._mouse
_plane
_node
, 3, self
.cb_inst
, self
.current_bottom
, self
.repos
)]
56 self
.items
+= [Domino(self
._world
, self
._mouse
_plane
_node
, 3, self
.cb_inst
, self
.current_bottom
, self
.repos
)]
64 self
._unset
_mouse
_plane
()
65 [itm
.destroy() for itm
in self
.items
]
67 self
._side
_panel
.destroy()
68 self
._cursor
.destroy()
69 taskMgr
.remove(self
._scene
_tsk
)
71 def _set_camera(self
):
72 base
.camera
.set_pos(0, -20, 0)
73 base
.camera
.look_at(0, 0, 0)
75 def __load_img_btn(self
, path
, col
):
76 img
= OnscreenImage('assets/buttons/%s.png' % path
)
77 img
.set_transparency(True)
83 def load_images_btn(path
, col
):
86 (.6, .6, .6, 1), # ready
88 (.8, .8, .8, 1), # rollover
94 (.4, .1, .1, .4)]}[col
]
95 return [self
.__load
_img
_btn
(path
, col
) for col
in colors
]
96 abl
, abr
= base
.a2dBottomLeft
, base
.a2dBottomRight
98 ('home', self
.on_home
, NORMAL
, abl
, 'gray'),
99 ('information', self
._set
_instructions
, NORMAL
, abl
, 'gray'),
100 ('right', self
.on_play
, NORMAL
, abr
, 'green'),
101 ('next', self
.on_next
, DISABLED
, abr
, 'gray'),
102 ('previous', self
.on_prev
, DISABLED
, abr
, 'gray'),
103 ('rewind', self
.reset
, NORMAL
, abr
, 'gray')]
106 for binfo
in btn_info
:
107 imgs
= load_images_btn(binfo
[0], binfo
[4])
108 if binfo
[3] == base
.a2dBottomLeft
:
112 sign
, num
= -1, num_r
114 fcols
= (.4, .4, .4, .14), (.3, .3, .3, .05)
116 image
=imgs
, scale
=.05, pos
=(sign
* (.06 + .11 * num
), 1, .06),
117 parent
=binfo
[3], command
=binfo
[1], state
=binfo
[2], relief
=FLAT
,
118 frameColor
=fcols
[0] if binfo
[2] == NORMAL
else fcols
[1],
119 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
120 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
121 btn
.set_transparency(True)
123 self
.__home
_btn
, self
.__info
_btn
, self
.__right
_btn
, self
.__next
_btn
, \
124 self
.__prev
_btn
, self
.__rewind
_btn
= btns
126 def _unset_gui(self
):
128 self
.__home
_btn
, self
.__info
_btn
, self
.__right
_btn
,
129 self
.__next
_btn
, self
.__prev
_btn
, self
.__rewind
_btn
]
130 [btn
.destroy() for btn
in btns
]
132 def _set_spotlight(self
, name
, pos
, look_at
, color
, shadows
=False):
133 light
= Spotlight(name
)
135 light
.setLens(PerspectiveLens())
136 light_np
= render
.attach_new_node(light
)
137 light_np
.set_pos(pos
)
138 light_np
.look_at(look_at
)
139 light
.set_color(color
)
140 render
.set_light(light_np
)
143 def _set_lights(self
):
144 alight
= AmbientLight('alight') # for ao
145 alight
.set_color((.15, .15, .15, 1))
146 self
._alnp
= render
.attach_new_node(alight
)
147 render
.set_light(self
._alnp
)
148 self
._key
_light
= self
._set
_spotlight
(
149 'key light', (-5, -80, 5), (0, 0, 0), (2.8, 2.8, 2.8, 1))
150 self
._shadow
_light
= self
._set
_spotlight
(
151 'key light', (-5, -80, 5), (0, 0, 0), (.58, .58, .58, 1), True)
152 self
._shadow
_light
.node().set_shadow_caster(True, 2048, 2048)
153 self
._shadow
_light
.node().get_lens().set_film_size(2048, 2048)
154 self
._shadow
_light
.node().get_lens().set_near_far(1, 256)
155 self
._shadow
_light
.node().set_camera_mask(BitMask32(0x01))
157 def _unset_lights(self
):
158 for light
in [self
._alnp
, self
._key
_light
, self
._shadow
_light
]:
159 render
.clear_light(light
)
162 def _set_input(self
):
163 self
.accept('mouse1', self
.on_click_l
)
164 self
.accept('mouse1-up', self
.on_release
)
165 self
.accept('mouse3', self
.on_click_r
)
166 self
.accept('mouse3-up', self
.on_release
)
168 def _unset_input(self
):
169 for evt
in ['mouse1', 'mouse1-up', 'mouse3', 'mouse3-up']:
172 def _set_mouse_plane(self
):
173 shape
= BulletPlaneShape((0, -1, 0), 0)
174 #self._mouse_plane_node = BulletRigidBodyNode('mouse plane')
175 self
._mouse
_plane
_node
= BulletGhostNode('mouse plane')
176 self
._mouse
_plane
_node
.addShape(shape
)
177 #np = render.attachNewNode(self._mouse_plane_node)
178 #self._world.attachRigidBody(self._mouse_plane_node)
179 self
._world
.attach_ghost(self
._mouse
_plane
_node
)
181 def _unset_mouse_plane(self
):
182 self
._world
.remove_ghost(self
._mouse
_plane
_node
)
185 if not base
.mouseWatcherNode
.has_mouse(): return []
186 p_from
, p_to
= P3dGfxMgr
.world_from_to(base
.mouseWatcherNode
.get_mouse())
187 return self
._world
.ray_test_all(p_from
, p_to
).get_hits()
189 def _on_click(self
, method
):
192 for hit
in self
._get
_hits
():
193 if hit
.get_node() == self
._mouse
_plane
_node
:
194 pos
= hit
.get_hit_pos()
195 for hit
in self
._get
_hits
():
196 for item
in [i
for i
in self
.items
if hit
.get_node() == i
.node
]:
197 if not self
._item
_active
:
198 self
._item
_active
= item
199 getattr(item
, method
)(pos
)
200 img
= 'move' if method
== 'on_click_l' else 'rotate'
201 self
._cursor
.set_image('assets/buttons/%s.png' % img
)
203 def on_click_l(self
):
204 self
._on
_click
('on_click_l')
206 def on_click_r(self
):
207 self
._on
_click
('on_click_r')
209 def on_release(self
):
210 if self
._item
_active
and not self
._item
_active
._first
_command
:
211 self
._commands
= self
._commands
[:self
._command
_idx
]
212 self
._commands
+= [self
._item
_active
]
213 self
._command
_idx
+= 1
214 self
.__prev
_btn
['state'] = NORMAL
215 fcols
= (.4, .4, .4, .14), (.3, .3, .3, .05)
216 self
.__prev
_btn
['frameColor'] = fcols
[0]
217 if self
._item
_active
._command
_idx
== len(self
._item
_active
._commands
) - 1:
218 self
.__next
_btn
['state'] = DISABLED
219 self
.__next
_btn
['frameColor'] = fcols
[1]
220 self
._item
_active
= None
221 [item
.on_release() for item
in self
.items
]
222 self
._cursor
.set_image('assets/buttons/arrowUpLeft.png')
225 for item
in self
.items
:
226 item
.repos_done
= False
227 self
.items
= sorted(self
.items
, key
=lambda itm
: itm
.__class
__.__name
__)
228 [item
.on_aspect_ratio_changed() for item
in self
.items
]
229 self
._side
_panel
.update(self
.items
)
231 for item
in self
.items
:
232 if not item
._instantiated
:
233 max_x
= max(item
._np
.get_x(), max_x
)
234 for item
in self
.items
:
235 if not item
._instantiated
:
238 def on_aspect_ratio_changed(self
):
241 def on_frame(self
, task
):
242 hits
= self
._get
_hits
()
244 for hit
in self
._get
_hits
():
245 if hit
.get_node() == self
._mouse
_plane
_node
:
246 pos
= hit
.get_hit_pos()
247 hit_nodes
= [hit
.get_node() for hit
in hits
]
248 if self
._item
_active
:
249 items_hit
= [self
._item
_active
]
251 items_hit
= [itm
for itm
in self
.items
if itm
.node
in hit_nodes
]
252 items_no_hit
= [itm
for itm
in self
.items
if itm
not in items_hit
]
253 [itm
.on_mouse_on() for itm
in items_hit
]
254 [itm
.on_mouse_off() for itm
in items_no_hit
]
255 if pos
and self
._item
_active
:
256 self
._item
_active
.on_mouse_move(pos
)
259 def cb_inst(self
, item
):
263 [itm
.play() for itm
in self
.items
]
266 self
._commands
[self
._command
_idx
].redo()
267 self
._command
_idx
+= 1
268 fcols
= (.4, .4, .4, .14), (.3, .3, .3, .05)
269 self
.__prev
_btn
['state'] = NORMAL
270 self
.__prev
_btn
['frameColor'] = fcols
[0]
271 more_commands
= self
._command
_idx
< len(self
._commands
)
272 self
.__next
_btn
['state'] = NORMAL
if more_commands
else DISABLED
273 self
.__next
_btn
['frameColor'] = fcols
[0] if more_commands
else fcols
[1]
276 self
._command
_idx
-= 1
277 self
._commands
[self
._command
_idx
].undo()
278 fcols
= (.4, .4, .4, .14), (.3, .3, .3, .05)
279 self
.__next
_btn
['state'] = NORMAL
280 self
.__next
_btn
['frameColor'] = fcols
[0]
281 self
.__prev
_btn
['state'] = NORMAL
if self
._command
_idx
else DISABLED
282 self
.__prev
_btn
['frameColor'] = fcols
[0] if self
._command
_idx
else fcols
[1]
287 def _set_instructions(self
):
290 mgr
= TextPropertiesManager
.get_global_ptr()
291 for name
in ['mouse_l', 'mouse_r']:
292 graphic
= OnscreenImage('assets/buttons/%s.png' % name
)
293 graphic
.set_scale(.5)
294 graphic
.get_texture().set_minfilter(Texture
.FTLinearMipmapLinear
)
295 graphic
.get_texture().set_anisotropic_degree(2)
296 mgr
.set_graphic(name
, graphic
)
298 graphic
.set_transparency(True)
299 graphic
.detach_node()
300 frm
= DirectFrame(frameColor
=(.4, .4, .4, .06),
301 frameSize
=(-.6, .6, -.3, .3))
302 font
= base
.loader
.load_font('assets/fonts/Hanken-Book.ttf')
304 font
.set_pixels_per_unit(60)
305 font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
306 font
.set_outline((0, 0, 0, 1), .8, .2)
307 txt
= _('keep \5mouse_l\5 pressed to drag an item\n\n'
308 'keep \5mouse_r\5 pressed to rotate an item')
309 self
._txt
= OnscreenText(
310 txt
, parent
=frm
, font
=font
, scale
=0.06, fg
=(.9, .9, .9, 1),
311 align
=TextNode
.A_left
)
312 u_l
= self
._txt
.textNode
.get_upper_left_3d()
313 l_r
= self
._txt
.textNode
.get_lower_right_3d()
314 w
, h
= l_r
[0] - u_l
[0], u_l
[2] - l_r
[2]
317 z
= h
/ 2 - font
.get_line_height() * self
._txt
['scale'][1]
318 z
+= (btn_scale
+ 2 * mar
) / 2
319 self
._txt
['pos'] = -w
/ 2, z
320 u_l
= self
._txt
.textNode
.get_upper_left_3d()
321 l_r
= self
._txt
.textNode
.get_lower_right_3d()
322 c_l_r
= l_r
[0], l_r
[1], l_r
[2] - 2 * mar
- btn_scale
323 fsz
= u_l
[0] - mar
, l_r
[0] + mar
, c_l_r
[2] - mar
, u_l
[2] + mar
324 frm
['frameSize'] = fsz
326 (.6, .6, .6, 1), # ready
327 (1, 1, 1, 1), # press
328 (.8, .8, .8, 1), # rollover
330 imgs
= [self
.__load
_img
_btn
('exitRight', col
) for col
in colors
]
332 image
=imgs
, scale
=btn_scale
,
333 pos
=(l_r
[0] - btn_scale
, 1, l_r
[2] - mar
- btn_scale
),
334 parent
=frm
, command
=self
.__on
_close
_instructions
, extraArgs
=[frm
],
335 relief
=FLAT
, frameColor
=(.6, .6, .6, .08),
336 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
337 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
338 btn
.set_transparency(True)
340 def __store_state(self
):
342 self
.__home
_btn
, self
.__info
_btn
, self
.__right
_btn
,
343 self
.__next
_btn
, self
.__prev
_btn
, self
.__rewind
_btn
]
344 self
.__btn
_state
= [btn
['state'] for btn
in btns
]
346 btn
['state'] = DISABLED
347 [itm
.store_state() for itm
in self
.items
]
349 def __restore_state(self
):
351 self
.__home
_btn
, self
.__info
_btn
, self
.__right
_btn
,
352 self
.__next
_btn
, self
.__prev
_btn
, self
.__rewind
_btn
]
353 for btn
, state
in zip(btns
, self
.__btn
_state
):
355 [itm
.restore_state() for itm
in self
.items
]
358 def __on_close_instructions(self
, frm
):
360 self
.__restore
_state
()