ya2 · news · projects · code · about

fixed the width of the menu buttons
[pmachines.git] / pmachines / scene.py
CommitLineData
2aaa10d3 1from panda3d.core import AmbientLight, DirectionalLight, Point3, Texture, \
bdfa6dda 2 TextPropertiesManager, TextNode, Spotlight, PerspectiveLens, BitMask32
36099535
FC
3from panda3d.bullet import BulletPlaneShape, BulletGhostNode
4from direct.gui.OnscreenImage import OnscreenImage
2aaa10d3
FC
5from direct.gui.OnscreenText import OnscreenText
6from direct.gui.DirectGui import DirectButton, DirectFrame
36099535 7from direct.gui.DirectGuiGlobals import FLAT, DISABLED, NORMAL
1be87278
FC
8from direct.showbase.DirectObject import DirectObject
9from pmachines.items.background import Background
10from pmachines.items.box import Box
a5dc83f4 11from pmachines.sidepanel import SidePanel
79f81d48 12from lib.engine.gui.cursor import MouseCursor
13263131 13from lib.lib.p3d.gfx import P3dGfxMgr
1be87278
FC
14
15
16class Scene(DirectObject):
17
e669403e 18 def __init__(self, world, exit_cb, auto_close_instr):
1be87278
FC
19 super().__init__()
20 self._world = world
5964572b 21 self._exit_cb = exit_cb
1be87278 22 self._set_camera()
79f81d48
FC
23 self._cursor = MouseCursor(
24 'assets/buttons/arrowUpLeft.png', (.04, 1, .04), (.5, .5, .5, 1),
25 (.01, .01))
36099535 26 self._set_gui()
1be87278
FC
27 self._set_lights()
28 self._set_input()
c8d8653f 29 self._set_mouse_plane()
a0acba9a
FC
30 self.items = [Box(world, self._mouse_plane_node, 3, self.cb_inst)]
31 self._paused = True
32 self.__store_state()
e669403e
FC
33 if auto_close_instr:
34 self.__restore_state()
35 else:
36 self._set_instructions()
5964572b
FC
37 self._bg = Background()
38 self._side_panel = SidePanel(world, self._mouse_plane_node, (-5, 4), (-3, 1), 1, self.items)
39 self._scene_tsk = taskMgr.add(self.on_frame, 'on_frame')
40
41 def destroy(self):
42 self._unset_gui()
43 self._unset_lights()
44 self._unset_input()
45 self._unset_mouse_plane()
46 [itm.destroy() for itm in self.items]
47 self._bg.destroy()
48 self._side_panel.destroy()
407412a5 49 self._cursor.destroy()
5964572b 50 taskMgr.remove(self._scene_tsk)
1be87278
FC
51
52 def _set_camera(self):
53 base.camera.set_pos(0, -20, 0)
54 base.camera.look_at(0, 0, 0)
55
a0acba9a
FC
56 def __load_img_btn(self, path, col):
57 img = OnscreenImage('assets/buttons/%s.png' % path)
58 img.set_transparency(True)
59 img.set_color(col)
60 img.detach_node()
61 return img
62
36099535 63 def _set_gui(self):
01b221a6
FC
64 def load_images_btn(path, col):
65 colors = {
66 'gray': [
67 (.6, .6, .6, 1), # ready
68 (1, 1, 1, 1), # press
69 (.8, .8, .8, 1), # rollover
70 (.4, .4, .4, .4)],
71 'green': [
72 (.1, .68, .1, 1),
73 (.1, 1, .1, 1),
74 (.1, .84, .1, 1),
75 (.4, .1, .1, .4)]}[col]
a0acba9a 76 return [self.__load_img_btn(path, col) for col in colors]
36099535
FC
77 abl, abr = base.a2dBottomLeft, base.a2dBottomRight
78 btn_info = [
5964572b 79 ('home', self.on_home, NORMAL, abl, 'gray'),
01b221a6
FC
80 ('information', self.on_information, DISABLED, abl, 'gray'),
81 ('right', self.on_play, NORMAL, abr, 'green'),
82 ('next', self.on_next, DISABLED, abr, 'gray'),
83 ('previous', self.on_prev, DISABLED, abr, 'gray'),
84 ('rewind', self.on_rewind, DISABLED, abr, 'gray')]
36099535 85 num_l = num_r = 0
a0acba9a 86 btns = []
36099535 87 for binfo in btn_info:
01b221a6 88 imgs = load_images_btn(binfo[0], binfo[4])
36099535
FC
89 if binfo[3] == base.a2dBottomLeft:
90 sign, num = 1, num_l
91 num_l += 1
92 else:
93 sign, num = -1, num_r
94 num_r += 1
95 fcols = (.4, .4, .4, .14), (.3, .3, .3, .05)
96 btn = DirectButton(
97 image=imgs, scale=.05, pos=(sign * (.06 + .11 * num), 1, .06),
98 parent=binfo[3], command=binfo[1], state=binfo[2], relief=FLAT,
54a1397e
FC
99 frameColor=fcols[0] if binfo[2] == NORMAL else fcols[1],
100 rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
101 clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
36099535 102 btn.set_transparency(True)
a0acba9a
FC
103 btns += [btn]
104 self.__home_btn, self.__info_btn, self.__right_btn, self.__next_btn, \
105 self.__prev_btn, self.__rewind_btn = btns
36099535 106
5964572b
FC
107 def _unset_gui(self):
108 btns = [
109 self.__home_btn, self.__info_btn, self.__right_btn,
110 self.__next_btn, self.__prev_btn, self.__rewind_btn]
111 [btn.destroy() for btn in btns]
112
64eae9c7
FC
113 def _set_spotlight(self, name, pos, look_at, color, shadows=False):
114 light = Spotlight(name)
115 if shadows:
116 light.setLens(PerspectiveLens())
1be87278 117 light_np = render.attach_new_node(light)
64eae9c7
FC
118 light_np.set_pos(pos)
119 light_np.look_at(look_at)
1be87278
FC
120 light.set_color(color)
121 render.set_light(light_np)
5964572b 122 return light_np
1be87278
FC
123
124 def _set_lights(self):
125 alight = AmbientLight('alight') # for ao
64eae9c7
FC
126 alight.set_color((.15, .15, .15, 1))
127 self._alnp = render.attach_new_node(alight)
128 render.set_light(self._alnp)
129 self._key_light = self._set_spotlight(
130 'key light', (-5, -80, 5), (0, 0, 0), (2.8, 2.8, 2.8, 1))
131 self._shadow_light = self._set_spotlight(
132 'key light', (-5, -80, 5), (0, 0, 0), (.58, .58, .58, 1), True)
133 self._shadow_light.node().set_shadow_caster(True, 2048, 2048)
134 self._shadow_light.node().get_lens().set_film_size(2048, 2048)
135 self._shadow_light.node().get_lens().set_near_far(1, 256)
136 self._shadow_light.node().set_camera_mask(BitMask32(0x01))
5964572b
FC
137
138 def _unset_lights(self):
64eae9c7 139 for light in [self._alnp, self._key_light, self._shadow_light]:
5964572b
FC
140 render.clear_light(light)
141 light.remove_node()
1be87278
FC
142
143 def _set_input(self):
49c79300 144 self.accept('mouse1', self.on_click_l)
c8d8653f 145 self.accept('mouse1-up', self.on_release)
49c79300
FC
146 self.accept('mouse3', self.on_click_r)
147 self.accept('mouse3-up', self.on_release)
c8d8653f 148
5964572b
FC
149 def _unset_input(self):
150 for evt in ['mouse1', 'mouse1-up', 'mouse3', 'mouse3-up']:
151 self.ignore(evt)
152
c8d8653f 153 def _set_mouse_plane(self):
651713a9 154 shape = BulletPlaneShape((0, -1, 0), 0)
c8d8653f
FC
155 #self._mouse_plane_node = BulletRigidBodyNode('mouse plane')
156 self._mouse_plane_node = BulletGhostNode('mouse plane')
157 self._mouse_plane_node.addShape(shape)
158 #np = render.attachNewNode(self._mouse_plane_node)
159 #self._world.attachRigidBody(self._mouse_plane_node)
06af3aa9 160 self._world.attach_ghost(self._mouse_plane_node)
1be87278 161
5964572b
FC
162 def _unset_mouse_plane(self):
163 self._world.remove_ghost(self._mouse_plane_node)
164
37ba71d8
FC
165 def _get_hits(self):
166 if not base.mouseWatcherNode.has_mouse(): return []
13263131 167 p_from, p_to = P3dGfxMgr.world_from_to(base.mouseWatcherNode.get_mouse())
37ba71d8
FC
168 return self._world.ray_test_all(p_from, p_to).get_hits()
169
49c79300 170 def _on_click(self, method):
a0acba9a
FC
171 if self._paused:
172 return
c8d8653f
FC
173 for hit in self._get_hits():
174 if hit.get_node() == self._mouse_plane_node:
175 pos = hit.get_hit_pos()
37ba71d8 176 for hit in self._get_hits():
1be87278 177 for item in [i for i in self.items if hit.get_node() == i.node]:
49c79300 178 getattr(item, method)(pos)
79f81d48
FC
179 img = 'move' if method == 'on_click_l' else 'rotate'
180 self._cursor.set_image('assets/buttons/%s.png' % img)
49c79300
FC
181
182 def on_click_l(self):
183 self._on_click('on_click_l')
184
185 def on_click_r(self):
186 self._on_click('on_click_r')
c8d8653f
FC
187
188 def on_release(self):
49c79300 189 [item.on_release() for item in self.items]
79f81d48 190 self._cursor.set_image('assets/buttons/arrowUpLeft.png')
37ba71d8 191
651713a9
FC
192 def on_aspect_ratio_changed(self):
193 [item.on_aspect_ratio_changed() for item in self.items]
a5dc83f4 194 self._side_panel.update(self.items)
651713a9 195
37ba71d8 196 def on_frame(self, task):
c8d8653f
FC
197 hits = self._get_hits()
198 pos = None
199 for hit in self._get_hits():
200 if hit.get_node() == self._mouse_plane_node:
201 pos = hit.get_hit_pos()
202 hit_nodes = [hit.get_node() for hit in hits]
37ba71d8
FC
203 items_hit = [itm for itm in self.items if itm.node in hit_nodes]
204 items_no_hit = [itm for itm in self.items if itm not in items_hit]
205 [itm.on_mouse_on() for itm in items_hit]
206 [itm.on_mouse_off() for itm in items_no_hit]
c8d8653f
FC
207 if pos:
208 [itm.on_mouse_move(pos) for itm in self.items]
37ba71d8 209 return task.cont
c8d8653f 210
7850ccc4
FC
211 def cb_inst(self, item):
212 self.items += [item]
213
c8d8653f
FC
214 def on_play(self):
215 [itm.play() for itm in self.items]
36099535
FC
216
217 def on_next(self):
218 print('on_next')
219
220 def on_prev(self):
221 print('on_prev')
222
223 def on_rewind(self):
224 print('on_rewind')
225
226 def on_home(self):
5964572b 227 self._exit_cb()
36099535
FC
228
229 def on_information(self):
230 print('on_information')
2aaa10d3
FC
231
232 def _set_instructions(self):
233 mgr = TextPropertiesManager.get_global_ptr()
234 for name in ['mouse_l', 'mouse_r']:
235 graphic = OnscreenImage('assets/buttons/%s.png' % name)
236 graphic.set_scale(.5)
237 graphic.get_texture().set_minfilter(Texture.FTLinearMipmapLinear)
238 graphic.get_texture().set_anisotropic_degree(2)
239 mgr.set_graphic(name, graphic)
240 graphic.set_z(-.2)
241 graphic.set_transparency(True)
242 graphic.detach_node()
243 frm = DirectFrame(frameColor=(.4, .4, .4, .06),
244 frameSize=(-.6, .6, -.3, .3))
245 font = base.loader.load_font('assets/fonts/Hanken-Book.ttf')
246 font.clear()
247 font.set_pixels_per_unit(60)
248 font.set_minfilter(Texture.FTLinearMipmapLinear)
249 font.set_outline((0, 0, 0, 1), .8, .2)
250 txt = _('keep \5mouse_l\5 pressed to drag an item\n\n'
251 'keep \5mouse_r\5 pressed to rotate an item')
252 self._txt = OnscreenText(
253 txt, parent=frm, font=font, scale=0.06, fg=(.9, .9, .9, 1),
254 align=TextNode.A_left)
255 u_l = self._txt.textNode.get_upper_left_3d()
256 l_r = self._txt.textNode.get_lower_right_3d()
257 w, h = l_r[0] - u_l[0], u_l[2] - l_r[2]
a0acba9a
FC
258 btn_scale = .05
259 mar = .06 # margin
2aaa10d3 260 z = h / 2 - font.get_line_height() * self._txt['scale'][1]
a0acba9a 261 z += (btn_scale + 2 * mar) / 2
2aaa10d3
FC
262 self._txt['pos'] = -w / 2, z
263 u_l = self._txt.textNode.get_upper_left_3d()
264 l_r = self._txt.textNode.get_lower_right_3d()
a0acba9a
FC
265 c_l_r = l_r[0], l_r[1], l_r[2] - 2 * mar - btn_scale
266 fsz = u_l[0] - mar, l_r[0] + mar, c_l_r[2] - mar, u_l[2] + mar
2aaa10d3 267 frm['frameSize'] = fsz
a0acba9a
FC
268 colors = [
269 (.6, .6, .6, 1), # ready
270 (1, 1, 1, 1), # press
271 (.8, .8, .8, 1), # rollover
272 (.4, .4, .4, .4)]
273 imgs = [self.__load_img_btn('exitRight', col) for col in colors]
274 btn = DirectButton(
275 image=imgs, scale=btn_scale,
276 pos=(l_r[0] - btn_scale, 1, l_r[2] - mar - btn_scale),
277 parent=frm, command=self.__on_close_instructions, extraArgs=[frm],
278 relief=FLAT, frameColor=(.6, .6, .6, .08),
279 rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
280 clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
281 btn.set_transparency(True)
282
283 def __store_state(self):
284 btns = [
285 self.__home_btn, self.__info_btn, self.__right_btn,
286 self.__next_btn, self.__prev_btn, self.__rewind_btn]
287 self.__btn_state = [btn['state'] for btn in btns]
288 for btn in btns:
289 btn['state'] = DISABLED
290 [itm.store_state() for itm in self.items]
291
292 def __restore_state(self):
293 btns = [
294 self.__home_btn, self.__info_btn, self.__right_btn,
295 self.__next_btn, self.__prev_btn, self.__rewind_btn]
296 for btn, state in zip(btns, self.__btn_state):
297 btn['state'] = state
298 [itm.restore_state() for itm in self.items]
299 self._paused = False
300
301 def __on_close_instructions(self, frm):
302 frm.remove_node()
303 self.__restore_state()