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
.sidepanel
import SidePanel
13 from lib
.engine
.gui
.cursor
import MouseCursor
14 from lib
.lib
.p3d
.gfx
import P3dGfxMgr
17 class Scene(DirectObject
):
19 def __init__(self
, world
, exit_cb
, auto_close_instr
):
22 self
._exit
_cb
= exit_cb
24 self
._cursor
= MouseCursor(
25 'assets/buttons/arrowUpLeft.png', (.04, 1, .04), (.5, .5, .5, 1),
30 self
._set
_mouse
_plane
()
34 self
._item
_active
= None
37 self
.__restore
_state
()
39 self
._set
_instructions
()
40 self
._bg
= Background()
41 self
._side
_panel
= SidePanel(world
, self
._mouse
_plane
_node
, (-5, 4), (-3, 1), 1, self
.items
)
42 self
._scene
_tsk
= taskMgr
.add(self
.on_frame
, 'on_frame')
44 def current_bottom(self
):
46 for item
in self
.items
:
48 curr_bottom
= min(curr_bottom
, item
.get_bottom())
52 [itm
.destroy() for itm
in self
.items
]
53 self
.items
= [Box(self
._world
, self
._mouse
_plane
_node
, 3, self
.cb_inst
, self
.current_bottom
)]
54 self
.items
+= [Shelf(self
._world
, self
._mouse
_plane
_node
, 3, self
.cb_inst
, self
.current_bottom
)]
62 self
._unset
_mouse
_plane
()
63 [itm
.destroy() for itm
in self
.items
]
65 self
._side
_panel
.destroy()
66 self
._cursor
.destroy()
67 taskMgr
.remove(self
._scene
_tsk
)
69 def _set_camera(self
):
70 base
.camera
.set_pos(0, -20, 0)
71 base
.camera
.look_at(0, 0, 0)
73 def __load_img_btn(self
, path
, col
):
74 img
= OnscreenImage('assets/buttons/%s.png' % path
)
75 img
.set_transparency(True)
81 def load_images_btn(path
, col
):
84 (.6, .6, .6, 1), # ready
86 (.8, .8, .8, 1), # rollover
92 (.4, .1, .1, .4)]}[col
]
93 return [self
.__load
_img
_btn
(path
, col
) for col
in colors
]
94 abl
, abr
= base
.a2dBottomLeft
, base
.a2dBottomRight
96 ('home', self
.on_home
, NORMAL
, abl
, 'gray'),
97 ('information', self
._set
_instructions
, NORMAL
, abl
, 'gray'),
98 ('right', self
.on_play
, NORMAL
, abr
, 'green'),
99 ('next', self
.on_next
, DISABLED
, abr
, 'gray'),
100 ('previous', self
.on_prev
, DISABLED
, abr
, 'gray'),
101 ('rewind', self
.reset
, NORMAL
, abr
, 'gray')]
104 for binfo
in btn_info
:
105 imgs
= load_images_btn(binfo
[0], binfo
[4])
106 if binfo
[3] == base
.a2dBottomLeft
:
110 sign
, num
= -1, num_r
112 fcols
= (.4, .4, .4, .14), (.3, .3, .3, .05)
114 image
=imgs
, scale
=.05, pos
=(sign
* (.06 + .11 * num
), 1, .06),
115 parent
=binfo
[3], command
=binfo
[1], state
=binfo
[2], relief
=FLAT
,
116 frameColor
=fcols
[0] if binfo
[2] == NORMAL
else fcols
[1],
117 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
118 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
119 btn
.set_transparency(True)
121 self
.__home
_btn
, self
.__info
_btn
, self
.__right
_btn
, self
.__next
_btn
, \
122 self
.__prev
_btn
, self
.__rewind
_btn
= btns
124 def _unset_gui(self
):
126 self
.__home
_btn
, self
.__info
_btn
, self
.__right
_btn
,
127 self
.__next
_btn
, self
.__prev
_btn
, self
.__rewind
_btn
]
128 [btn
.destroy() for btn
in btns
]
130 def _set_spotlight(self
, name
, pos
, look_at
, color
, shadows
=False):
131 light
= Spotlight(name
)
133 light
.setLens(PerspectiveLens())
134 light_np
= render
.attach_new_node(light
)
135 light_np
.set_pos(pos
)
136 light_np
.look_at(look_at
)
137 light
.set_color(color
)
138 render
.set_light(light_np
)
141 def _set_lights(self
):
142 alight
= AmbientLight('alight') # for ao
143 alight
.set_color((.15, .15, .15, 1))
144 self
._alnp
= render
.attach_new_node(alight
)
145 render
.set_light(self
._alnp
)
146 self
._key
_light
= self
._set
_spotlight
(
147 'key light', (-5, -80, 5), (0, 0, 0), (2.8, 2.8, 2.8, 1))
148 self
._shadow
_light
= self
._set
_spotlight
(
149 'key light', (-5, -80, 5), (0, 0, 0), (.58, .58, .58, 1), True)
150 self
._shadow
_light
.node().set_shadow_caster(True, 2048, 2048)
151 self
._shadow
_light
.node().get_lens().set_film_size(2048, 2048)
152 self
._shadow
_light
.node().get_lens().set_near_far(1, 256)
153 self
._shadow
_light
.node().set_camera_mask(BitMask32(0x01))
155 def _unset_lights(self
):
156 for light
in [self
._alnp
, self
._key
_light
, self
._shadow
_light
]:
157 render
.clear_light(light
)
160 def _set_input(self
):
161 self
.accept('mouse1', self
.on_click_l
)
162 self
.accept('mouse1-up', self
.on_release
)
163 self
.accept('mouse3', self
.on_click_r
)
164 self
.accept('mouse3-up', self
.on_release
)
166 def _unset_input(self
):
167 for evt
in ['mouse1', 'mouse1-up', 'mouse3', 'mouse3-up']:
170 def _set_mouse_plane(self
):
171 shape
= BulletPlaneShape((0, -1, 0), 0)
172 #self._mouse_plane_node = BulletRigidBodyNode('mouse plane')
173 self
._mouse
_plane
_node
= BulletGhostNode('mouse plane')
174 self
._mouse
_plane
_node
.addShape(shape
)
175 #np = render.attachNewNode(self._mouse_plane_node)
176 #self._world.attachRigidBody(self._mouse_plane_node)
177 self
._world
.attach_ghost(self
._mouse
_plane
_node
)
179 def _unset_mouse_plane(self
):
180 self
._world
.remove_ghost(self
._mouse
_plane
_node
)
183 if not base
.mouseWatcherNode
.has_mouse(): return []
184 p_from
, p_to
= P3dGfxMgr
.world_from_to(base
.mouseWatcherNode
.get_mouse())
185 return self
._world
.ray_test_all(p_from
, p_to
).get_hits()
187 def _on_click(self
, method
):
190 for hit
in self
._get
_hits
():
191 if hit
.get_node() == self
._mouse
_plane
_node
:
192 pos
= hit
.get_hit_pos()
193 for hit
in self
._get
_hits
():
194 for item
in [i
for i
in self
.items
if hit
.get_node() == i
.node
]:
195 if not self
._item
_active
:
196 self
._item
_active
= item
197 getattr(item
, method
)(pos
)
198 img
= 'move' if method
== 'on_click_l' else 'rotate'
199 self
._cursor
.set_image('assets/buttons/%s.png' % img
)
201 def on_click_l(self
):
202 self
._on
_click
('on_click_l')
204 def on_click_r(self
):
205 self
._on
_click
('on_click_r')
207 def on_release(self
):
208 if self
._item
_active
and not self
._item
_active
._first
_command
:
209 self
._commands
= self
._commands
[:self
._command
_idx
]
210 self
._commands
+= [self
._item
_active
]
211 self
._command
_idx
+= 1
212 self
.__prev
_btn
['state'] = NORMAL
213 fcols
= (.4, .4, .4, .14), (.3, .3, .3, .05)
214 self
.__prev
_btn
['frameColor'] = fcols
[0]
215 if self
._item
_active
._command
_idx
== len(self
._item
_active
._commands
) - 1:
216 self
.__next
_btn
['state'] = DISABLED
217 self
.__next
_btn
['frameColor'] = fcols
[1]
218 self
._item
_active
= None
219 [item
.on_release() for item
in self
.items
]
220 self
._cursor
.set_image('assets/buttons/arrowUpLeft.png')
222 def on_aspect_ratio_changed(self
):
223 for item
in self
.items
:
224 item
.repos_done
= False
225 [item
.on_aspect_ratio_changed() for item
in self
.items
]
226 self
._side
_panel
.update(self
.items
)
228 def on_frame(self
, task
):
229 hits
= self
._get
_hits
()
231 for hit
in self
._get
_hits
():
232 if hit
.get_node() == self
._mouse
_plane
_node
:
233 pos
= hit
.get_hit_pos()
234 hit_nodes
= [hit
.get_node() for hit
in hits
]
235 if self
._item
_active
:
236 items_hit
= [self
._item
_active
]
238 items_hit
= [itm
for itm
in self
.items
if itm
.node
in hit_nodes
]
239 items_no_hit
= [itm
for itm
in self
.items
if itm
not in items_hit
]
240 [itm
.on_mouse_on() for itm
in items_hit
]
241 [itm
.on_mouse_off() for itm
in items_no_hit
]
242 if pos
and self
._item
_active
:
243 self
._item
_active
.on_mouse_move(pos
)
246 def cb_inst(self
, item
):
250 [itm
.play() for itm
in self
.items
]
253 self
._commands
[self
._command
_idx
].redo()
254 self
._command
_idx
+= 1
255 fcols
= (.4, .4, .4, .14), (.3, .3, .3, .05)
256 self
.__prev
_btn
['state'] = NORMAL
257 self
.__prev
_btn
['frameColor'] = fcols
[0]
258 more_commands
= self
._command
_idx
< len(self
._commands
)
259 self
.__next
_btn
['state'] = NORMAL
if more_commands
else DISABLED
260 self
.__next
_btn
['frameColor'] = fcols
[0] if more_commands
else fcols
[1]
263 self
._command
_idx
-= 1
264 self
._commands
[self
._command
_idx
].undo()
265 fcols
= (.4, .4, .4, .14), (.3, .3, .3, .05)
266 self
.__next
_btn
['state'] = NORMAL
267 self
.__next
_btn
['frameColor'] = fcols
[0]
268 self
.__prev
_btn
['state'] = NORMAL
if self
._command
_idx
else DISABLED
269 self
.__prev
_btn
['frameColor'] = fcols
[0] if self
._command
_idx
else fcols
[1]
274 def _set_instructions(self
):
277 mgr
= TextPropertiesManager
.get_global_ptr()
278 for name
in ['mouse_l', 'mouse_r']:
279 graphic
= OnscreenImage('assets/buttons/%s.png' % name
)
280 graphic
.set_scale(.5)
281 graphic
.get_texture().set_minfilter(Texture
.FTLinearMipmapLinear
)
282 graphic
.get_texture().set_anisotropic_degree(2)
283 mgr
.set_graphic(name
, graphic
)
285 graphic
.set_transparency(True)
286 graphic
.detach_node()
287 frm
= DirectFrame(frameColor
=(.4, .4, .4, .06),
288 frameSize
=(-.6, .6, -.3, .3))
289 font
= base
.loader
.load_font('assets/fonts/Hanken-Book.ttf')
291 font
.set_pixels_per_unit(60)
292 font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
293 font
.set_outline((0, 0, 0, 1), .8, .2)
294 txt
= _('keep \5mouse_l\5 pressed to drag an item\n\n'
295 'keep \5mouse_r\5 pressed to rotate an item')
296 self
._txt
= OnscreenText(
297 txt
, parent
=frm
, font
=font
, scale
=0.06, fg
=(.9, .9, .9, 1),
298 align
=TextNode
.A_left
)
299 u_l
= self
._txt
.textNode
.get_upper_left_3d()
300 l_r
= self
._txt
.textNode
.get_lower_right_3d()
301 w
, h
= l_r
[0] - u_l
[0], u_l
[2] - l_r
[2]
304 z
= h
/ 2 - font
.get_line_height() * self
._txt
['scale'][1]
305 z
+= (btn_scale
+ 2 * mar
) / 2
306 self
._txt
['pos'] = -w
/ 2, z
307 u_l
= self
._txt
.textNode
.get_upper_left_3d()
308 l_r
= self
._txt
.textNode
.get_lower_right_3d()
309 c_l_r
= l_r
[0], l_r
[1], l_r
[2] - 2 * mar
- btn_scale
310 fsz
= u_l
[0] - mar
, l_r
[0] + mar
, c_l_r
[2] - mar
, u_l
[2] + mar
311 frm
['frameSize'] = fsz
313 (.6, .6, .6, 1), # ready
314 (1, 1, 1, 1), # press
315 (.8, .8, .8, 1), # rollover
317 imgs
= [self
.__load
_img
_btn
('exitRight', col
) for col
in colors
]
319 image
=imgs
, scale
=btn_scale
,
320 pos
=(l_r
[0] - btn_scale
, 1, l_r
[2] - mar
- btn_scale
),
321 parent
=frm
, command
=self
.__on
_close
_instructions
, extraArgs
=[frm
],
322 relief
=FLAT
, frameColor
=(.6, .6, .6, .08),
323 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
324 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
325 btn
.set_transparency(True)
327 def __store_state(self
):
329 self
.__home
_btn
, self
.__info
_btn
, self
.__right
_btn
,
330 self
.__next
_btn
, self
.__prev
_btn
, self
.__rewind
_btn
]
331 self
.__btn
_state
= [btn
['state'] for btn
in btns
]
333 btn
['state'] = DISABLED
334 [itm
.store_state() for itm
in self
.items
]
336 def __restore_state(self
):
338 self
.__home
_btn
, self
.__info
_btn
, self
.__right
_btn
,
339 self
.__next
_btn
, self
.__prev
_btn
, self
.__rewind
_btn
]
340 for btn
, state
in zip(btns
, self
.__btn
_state
):
342 [itm
.restore_state() for itm
in self
.items
]
345 def __on_close_instructions(self
, frm
):
347 self
.__restore
_state
()