ya2 · news · projects · code · about

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