ya2 · news · projects · code · about

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