5 from importlib
import import_module
6 from inspect
import isclass
7 from sys
import platform
, argv
, exit
8 from logging
import info
, debug
9 from os
.path
import exists
10 from os
import makedirs
11 from panda3d
.core
import Filename
, load_prc_file_data
, AntialiasAttrib
, \
12 Texture
, WindowProperties
, LVector2i
, TextNode
13 from panda3d
.bullet
import BulletWorld
, BulletDebugNode
14 from direct
.showbase
.ShowBase
import ShowBase
15 from direct
.gui
.OnscreenText
import OnscreenText
16 from direct
.fsm
.FSM
import FSM
17 from pmachines
.audio
.music
import MusicMgr
18 from pmachines
.items
.background
import Background
19 from pmachines
.gui
.menu
import Menu
20 from pmachines
.scene
import Scene
21 from pmachines
.scenes
.scene_basketball
import SceneBasketBall
22 from pmachines
.scenes
.scene_box
import SceneBox
23 from pmachines
.scenes
.scene_domino_box_basketball
import SceneDominoBoxBasketball
24 from pmachines
.scenes
.scene_domino_box
import SceneDominoBox
25 from pmachines
.scenes
.scene_domino
import SceneDomino
26 from pmachines
.scenes
.scene_teeter_domino_box_basketball
import SceneTeeterDominoBoxBasketball
27 from pmachines
.scenes
.scene_teeter_tooter
import SceneTeeterTooter
28 from pmachines
.posmgr
import PositionMgr
29 from ya2
.utils
.dictfile
import DctFile
30 from ya2
.p3d
.p3d
import LibP3d
31 from ya2
.utils
.lang
import LangMgr
32 from ya2
.utils
.log
import LogMgr
33 from ya2
.utils
.functional
import FunctionalTest
38 def __init__(self
, pmachines
):
39 super().__init
__('Main FSM')
40 self
._pmachines
= pmachines
43 self
._pmachines
.on_menu_enter()
46 self
._pmachines
.on_menu_exit()
48 def enterScene(self
, cls
):
49 self
._pmachines
.on_scene_enter(cls
)
52 self
._pmachines
.on_scene_exit()
62 SceneDominoBoxBasketball
,
64 SceneTeeterDominoBoxBasketball
]
67 info('platform: %s' % platform
)
68 info('exists main.py: %s' % exists('main.py'))
69 self
._args
= args
= self
._parse
_args
()
71 self
.base
= ShowBase()
73 self
.updating
= args
.update
74 self
.version
= args
.version
75 self
.log_mgr
= LogMgr
.init_cls()()
76 self
._pos
_mgr
= PositionMgr()
77 self
._prepare
_window
(args
)
80 if args
.functional_test
:
81 self
._options
['settings']['volume'] = 0
82 self
._music
= MusicMgr(self
._options
['settings']['volume'])
83 self
.lang_mgr
= LangMgr(self
._options
['settings']['language'],
86 self
._fsm
= MainFsm(self
)
88 cls
= [cls
for cls
in self
.scenes
if cls
.__name
__ == args
.screenshot
][0]
89 scene
= cls(BulletWorld(), None, True, False, lambda: None, self
.scenes
)
93 elif self
._options
['development']['auto_start']:
94 mod_name
= 'pmachines.scenes.scene_' + self
._options
['development']['auto_start']
95 for member
in import_module(mod_name
).__dict
__.values():
96 if isclass(member
) and issubclass(member
, Scene
) and \
99 self
._fsm
.demand('Scene', cls
)
101 self
._fsm
.demand('Menu')
102 if args
.functional_test
or args
.functional_ref
:
103 FunctionalTest(args
.functional_ref
, self
._pos
_mgr
)
105 taskMgr
.do_method_later(1.0, self
.__assert
_fps
, 'assert_fps')
107 def on_menu_enter(self
):
108 self
._menu
_bg
= Background()
110 self
._fsm
, self
.lang_mgr
, self
._options
, self
._music
,
111 self
._pipeline
, self
.scenes
, self
._args
.functional_test
or self
._args
.functional_ref
,
115 self
._fsm
.demand('Menu')
117 def on_menu_exit(self
):
118 self
._menu
_bg
.destroy()
121 def on_scene_enter(self
, cls
):
124 self
.world
, self
.on_home
,
125 self
._options
['development']['auto_close_instructions'],
126 self
._options
['development']['debug_items'],
130 self
._args
.functional_test
or self
._args
.functional_ref
)
132 def on_scene_exit(self
):
133 self
._unset
_physics
()
134 self
._scene
.destroy()
136 def reload(self
, cls
):
137 self
._fsm
.demand('Scene', cls
)
139 def _configure(self
, args
):
140 load_prc_file_data('', 'window-title pmachines')
141 load_prc_file_data('', 'framebuffer-srgb true')
142 load_prc_file_data('', 'sync-video true')
143 if args
.functional_test
or args
.functional_ref
:
144 load_prc_file_data('', 'win-size 1360 768')
145 # otherwise it is not centered in exwm
146 # load_prc_file_data('', 'threading-model Cull/Draw')
147 # it freezes when you go to the next scene
149 load_prc_file_data('', 'window-type offscreen')
150 load_prc_file_data('', 'audio-library-name null')
152 def _parse_args(self
):
153 parser
= argparse
.ArgumentParser()
154 parser
.add_argument('--update', action
='store_true')
155 parser
.add_argument('--version', action
='store_true')
156 parser
.add_argument('--optfile')
157 parser
.add_argument('--screenshot')
158 parser
.add_argument('--functional-test', action
='store_true')
159 parser
.add_argument('--functional-ref', action
='store_true')
160 cmd_line
= [arg
for arg
in iter(argv
[1:]) if not arg
.startswith('-psn_')]
161 args
= parser
.parse_args(cmd_line
)
164 def _prepare_window(self
, args
):
166 if (platform
.startswith('win') or platform
.startswith('linux')) and (
167 not exists('main.py') or __file__
.startswith('/app/bin/')):
168 # it is the deployed version for windows
169 data_path
= str(Filename
.get_user_appdata_directory()) + '/pmachines'
170 home
= '/home/flavio' # we must force this for wine
171 if data_path
.startswith('/c/users/') and exists(home
+ '/.wine/'):
172 data_path
= home
+ '/.wine/drive_' + data_path
[1:]
173 info('creating dirs: %s' % data_path
)
174 makedirs(data_path
, exist_ok
=True)
175 optfile
= args
.optfile
if args
.optfile
else 'options.ini'
176 info('data path: %s' % data_path
)
177 info('option file: %s' % optfile
)
178 info('fixed path: %s' % LibP3d
.fixpath(data_path
+ '/' + optfile
))
192 'auto_close_instructions': 0,
197 opt_path
= LibP3d
.fixpath(data_path
+ '/' + optfile
) if data_path
else optfile
198 opt_exists
= exists(opt_path
)
199 self
._options
= DctFile(
200 LibP3d
.fixpath(data_path
+ '/' + optfile
) if data_path
else optfile
,
203 self
._options
.store()
204 res
= self
._options
['settings']['resolution']
206 res
= LVector2i(*[int(_res
) for _res
in res
.split('x')])
210 d_i
= base
.pipe
.get_display_information()
212 return d_i
.get_display_mode_width(idx
), \
213 d_i
.get_display_mode_height(idx
)
215 _res(idx
) for idx
in range(d_i
.get_total_display_modes())]
216 res
= sorted(resolutions
)[-1]
217 fullscreen
= self
._options
['settings']['fullscreen']
218 props
= WindowProperties()
219 if args
.functional_test
or args
.functional_ref
:
223 props
.set_fullscreen(fullscreen
)
224 props
.set_icon_filename('assets/images/icon/pmachines.ico')
225 if not args
.screenshot
and not self
.version
:
226 base
.win
.request_properties(props
)
227 #gltf.patch_loader(base.loader)
228 if self
._options
['development']['simplepbr'] and not self
.version
:
229 self
._pipeline
= simplepbr
.init(
230 use_normal_maps
=True,
231 use_emission_maps
=False,
232 use_occlusion_maps
=True,
233 msaa_samples
=4 if self
._options
['settings']['antialiasing'] else 1,
234 enable_shadows
=int(self
._options
['settings']['shadows']))
235 debug(f
'msaa: {self._pipeline.msaa_samples}')
236 debug(f
'shadows: {self._pipeline.enable_shadows}')
237 render
.setAntialias(AntialiasAttrib
.MAuto
)
238 self
.base
.set_background_color(0, 0, 0, 1)
239 self
.base
.disable_mouse()
240 if self
._options
['development']['show_buffers']:
241 base
.bufferViewer
.toggleEnable()
242 if self
._options
['development']['fps']:
243 base
.set_frame_rate_meter(True)
244 #self.base.accept('window-event', self._on_win_evt)
245 self
.base
.accept('aspectRatioChanged', self
._on
_aspect
_ratio
_changed
)
246 if self
._options
['development']['mouse_coords']:
247 coords_txt
= OnscreenText(
248 '', parent
=base
.a2dTopRight
, scale
=0.04,
249 pos
=(-.03, -.06), fg
=(.9, .9, .9, 1), align
=TextNode
.A_right
)
250 def update_coords(task
):
251 txt
= '%s %s' % (int(base
.win
.get_pointer(0).x
),
252 int(base
.win
.get_pointer(0).y
))
253 coords_txt
['text'] = txt
255 taskMgr
.add(update_coords
, 'update_coords')
257 def _set_physics(self
):
258 if self
._options
['development']['physics_debug']:
259 debug_node
= BulletDebugNode('Debug')
260 debug_node
.show_wireframe(True)
261 debug_node
.show_constraints(True)
262 debug_node
.show_bounding_boxes(True)
263 debug_node
.show_normals(True)
264 self
._debug
_np
= render
.attach_new_node(debug_node
)
265 self
._debug
_np
.show()
266 self
.world
= BulletWorld()
267 self
.world
.set_gravity((0, 0, -9.81))
268 if self
._options
['development']['physics_debug']:
269 self
.world
.set_debug_node(self
._debug
_np
.node())
271 dt
= globalClock
.get_dt()
272 self
.world
.do_physics(dt
, 10, 1/180)
274 self
._phys
_tsk
= taskMgr
.add(update
, 'update')
276 def _unset_physics(self
):
277 if self
._options
['development']['physics_debug']:
278 self
._debug
_np
.remove_node()
280 taskMgr
.remove(self
._phys
_tsk
)
282 def _on_aspect_ratio_changed(self
):
283 if self
._fsm
.state
== 'Scene':
284 self
._scene
.on_aspect_ratio_changed()
286 def __assert_fps(self
, task
):
287 if len(self
.__fps
_lst
) > 3:
288 self
.__fps
_lst
.pop(0)
289 self
.__fps
_lst
+= [globalClock
.average_frame_rate
]
290 if len(self
.__fps
_lst
) == 4:
291 assert(any(fps
> 55 for fps
in self
.__fps
_lst
))