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
34 from ya2
.p3d
.asserts
import assert_threads
, assert_tasks
, assert_render3d
, \
35 assert_render2d
, assert_aspect2d
, assert_events
, assert_buffers
40 def __init__(self
, pmachines
):
41 super().__init
__('Main FSM')
42 self
._pmachines
= pmachines
45 self
._pmachines
.on_menu_enter()
48 self
._pmachines
.on_menu_exit()
51 def enterScene(self
, cls
):
52 self
._pmachines
.on_scene_enter(cls
)
55 self
._pmachines
.on_scene_exit()
58 def __do_asserts(self
):
59 args
= self
._pmachines
._args
60 if True: # args.functional_test or args.functional_ref:
77 SceneDominoBoxBasketball
,
79 SceneTeeterDominoBoxBasketball
]
82 info('platform: %s' % platform
)
83 info('exists main.py: %s' % exists('main.py'))
84 self
._args
= args
= self
._parse
_args
()
86 self
.base
= ShowBase()
88 self
.updating
= args
.update
89 self
.version
= args
.version
90 self
.log_mgr
= LogMgr
.init_cls()()
91 self
._pos
_mgr
= PositionMgr()
92 self
._prepare
_window
(args
)
95 if args
.functional_test
:
96 self
._options
['settings']['volume'] = 0
97 self
._music
= MusicMgr(self
._options
['settings']['volume'])
98 self
.lang_mgr
= LangMgr(self
._options
['settings']['language'],
101 self
._fsm
= MainFsm(self
)
103 cls
= [cls
for cls
in self
.scenes
if cls
.__name
__ == args
.screenshot
][0]
104 scene
= cls(BulletWorld(), None, True, False, lambda: None, self
.scenes
)
108 elif self
._options
['development']['auto_start']:
109 mod_name
= 'pmachines.scenes.scene_' + self
._options
['development']['auto_start']
110 for member
in import_module(mod_name
).__dict
__.values():
111 if isclass(member
) and issubclass(member
, Scene
) and \
114 self
._fsm
.demand('Scene', cls
)
116 self
._fsm
.demand('Menu')
117 if args
.functional_test
or args
.functional_ref
:
118 FunctionalTest(args
.functional_ref
, self
._pos
_mgr
)
120 taskMgr
.do_method_later(1.0, self
.__assert
_fps
, 'assert_fps')
122 def on_menu_enter(self
):
123 self
._menu
_bg
= Background()
125 self
._fsm
, self
.lang_mgr
, self
._options
, self
._music
,
126 self
._pipeline
, self
.scenes
, self
._args
.functional_test
or self
._args
.functional_ref
,
130 self
._fsm
.demand('Menu')
132 def on_menu_exit(self
):
133 self
._menu
_bg
.destroy()
136 def on_scene_enter(self
, cls
):
139 self
.world
, self
.on_home
,
140 self
._options
['development']['auto_close_instructions'],
141 self
._options
['development']['debug_items'],
145 self
._args
.functional_test
or self
._args
.functional_ref
,
146 self
._options
['development']['mouse_coords'])
148 def on_scene_exit(self
):
149 self
._unset
_physics
()
150 self
._scene
.destroy()
152 def reload(self
, cls
):
153 self
._fsm
.demand('Scene', cls
)
155 def _configure(self
, args
):
156 load_prc_file_data('', 'window-title pmachines')
157 load_prc_file_data('', 'framebuffer-srgb true')
158 load_prc_file_data('', 'sync-video true')
159 if args
.functional_test
or args
.functional_ref
:
160 load_prc_file_data('', 'win-size 1360 768')
161 # otherwise it is not centered in exwm
162 # load_prc_file_data('', 'threading-model Cull/Draw')
163 # it freezes when you go to the next scene
165 load_prc_file_data('', 'window-type offscreen')
166 load_prc_file_data('', 'audio-library-name null')
168 def _parse_args(self
):
169 parser
= argparse
.ArgumentParser()
170 parser
.add_argument('--update', action
='store_true')
171 parser
.add_argument('--version', action
='store_true')
172 parser
.add_argument('--optfile')
173 parser
.add_argument('--screenshot')
174 parser
.add_argument('--functional-test', action
='store_true')
175 parser
.add_argument('--functional-ref', action
='store_true')
176 cmd_line
= [arg
for arg
in iter(argv
[1:]) if not arg
.startswith('-psn_')]
177 args
= parser
.parse_args(cmd_line
)
180 def _prepare_window(self
, args
):
182 if (platform
.startswith('win') or platform
.startswith('linux')) and (
183 not exists('main.py') or __file__
.startswith('/app/bin/')):
184 # it is the deployed version for windows
185 data_path
= str(Filename
.get_user_appdata_directory()) + '/pmachines'
186 home
= '/home/flavio' # we must force this for wine
187 if data_path
.startswith('/c/users/') and exists(home
+ '/.wine/'):
188 data_path
= home
+ '/.wine/drive_' + data_path
[1:]
189 info('creating dirs: %s' % data_path
)
190 makedirs(data_path
, exist_ok
=True)
191 optfile
= args
.optfile
if args
.optfile
else 'options.ini'
192 info('data path: %s' % data_path
)
193 info('option file: %s' % optfile
)
194 info('fixed path: %s' % LibP3d
.fixpath(data_path
+ '/' + optfile
))
208 'auto_close_instructions': 0,
213 opt_path
= LibP3d
.fixpath(data_path
+ '/' + optfile
) if data_path
else optfile
214 opt_exists
= exists(opt_path
)
215 self
._options
= DctFile(
216 LibP3d
.fixpath(data_path
+ '/' + optfile
) if data_path
else optfile
,
219 self
._options
.store()
220 res
= self
._options
['settings']['resolution']
222 res
= LVector2i(*[int(_res
) for _res
in res
.split('x')])
226 d_i
= base
.pipe
.get_display_information()
228 return d_i
.get_display_mode_width(idx
), \
229 d_i
.get_display_mode_height(idx
)
231 _res(idx
) for idx
in range(d_i
.get_total_display_modes())]
232 res
= sorted(resolutions
)[-1]
233 fullscreen
= self
._options
['settings']['fullscreen']
234 props
= WindowProperties()
235 if args
.functional_test
or args
.functional_ref
:
239 props
.set_fullscreen(fullscreen
)
240 props
.set_icon_filename('assets/images/icon/pmachines.ico')
241 if not args
.screenshot
and not self
.version
:
242 base
.win
.request_properties(props
)
243 #gltf.patch_loader(base.loader)
244 if self
._options
['development']['simplepbr'] and not self
.version
:
245 self
._pipeline
= simplepbr
.init(
246 use_normal_maps
=True,
247 use_emission_maps
=False,
248 use_occlusion_maps
=True,
249 msaa_samples
=4 if self
._options
['settings']['antialiasing'] else 1,
250 enable_shadows
=int(self
._options
['settings']['shadows']))
251 debug(f
'msaa: {self._pipeline.msaa_samples}')
252 debug(f
'shadows: {self._pipeline.enable_shadows}')
253 render
.setAntialias(AntialiasAttrib
.MAuto
)
254 self
.base
.set_background_color(0, 0, 0, 1)
255 self
.base
.disable_mouse()
256 if self
._options
['development']['show_buffers']:
257 base
.bufferViewer
.toggleEnable()
258 if self
._options
['development']['fps']:
259 base
.set_frame_rate_meter(True)
260 #self.base.accept('window-event', self._on_win_evt)
261 self
.base
.accept('aspectRatioChanged', self
._on
_aspect
_ratio
_changed
)
262 if self
._options
['development']['mouse_coords']:
263 coords_txt
= OnscreenText(
264 '', parent
=base
.a2dTopRight
, scale
=0.04,
265 pos
=(-.03, -.06), fg
=(.9, .9, .9, 1), align
=TextNode
.A_right
)
266 def update_coords(task
):
267 txt
= '%s %s' % (int(base
.win
.get_pointer(0).x
),
268 int(base
.win
.get_pointer(0).y
))
269 coords_txt
['text'] = txt
271 taskMgr
.add(update_coords
, 'update_coords')
273 def _set_physics(self
):
274 if self
._options
['development']['physics_debug']:
275 debug_node
= BulletDebugNode('Debug')
276 debug_node
.show_wireframe(True)
277 debug_node
.show_constraints(True)
278 debug_node
.show_bounding_boxes(True)
279 debug_node
.show_normals(True)
280 self
._debug
_np
= render
.attach_new_node(debug_node
)
281 self
._debug
_np
.show()
282 self
.world
= BulletWorld()
283 self
.world
.set_gravity((0, 0, -9.81))
284 if self
._options
['development']['physics_debug']:
285 self
.world
.set_debug_node(self
._debug
_np
.node())
287 dt
= globalClock
.get_dt()
288 self
.world
.do_physics(dt
, 10, 1/180)
290 self
._phys
_tsk
= taskMgr
.add(update
, 'update')
292 def _unset_physics(self
):
293 if self
._options
['development']['physics_debug']:
294 self
._debug
_np
.remove_node()
296 taskMgr
.remove(self
._phys
_tsk
)
298 def _on_aspect_ratio_changed(self
):
299 if self
._fsm
.state
== 'Scene':
300 self
._scene
.on_aspect_ratio_changed()
302 def __assert_fps(self
, task
):
303 if len(self
.__fps
_lst
) > 3:
304 self
.__fps
_lst
.pop(0)
305 self
.__fps
_lst
+= [globalClock
.average_frame_rate
]
306 if len(self
.__fps
_lst
) == 4:
307 assert(any(fps
> 55 for fps
in self
.__fps
_lst
))