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 multiprocessing
import cpu_count
12 from panda3d
.core
import Filename
, load_prc_file_data
, AntialiasAttrib
, \
13 Texture
, WindowProperties
, LVector2i
, TextNode
14 from panda3d
.bullet
import BulletWorld
, BulletDebugNode
15 from direct
.showbase
.ShowBase
import ShowBase
16 from direct
.gui
.OnscreenText
import OnscreenText
17 from direct
.fsm
.FSM
import FSM
18 from pmachines
.audio
.music
import MusicMgr
19 from pmachines
.items
.background
import Background
20 from pmachines
.gui
.menu
import Menu
21 from pmachines
.scene
import Scene
22 from pmachines
.scenes
.scene_basketball
import SceneBasketBall
23 from pmachines
.scenes
.scene_box
import SceneBox
24 from pmachines
.scenes
.scene_domino_box_basketball
import SceneDominoBoxBasketball
25 from pmachines
.scenes
.scene_domino_box
import SceneDominoBox
26 from pmachines
.scenes
.scene_domino
import SceneDomino
27 from pmachines
.scenes
.scene_teeter_domino_box_basketball
import SceneTeeterDominoBoxBasketball
28 from pmachines
.scenes
.scene_teeter_tooter
import SceneTeeterTooter
29 from pmachines
.posmgr
import PositionMgr
30 from ya2
.utils
.dictfile
import DctFile
31 from ya2
.p3d
.p3d
import LibP3d
32 from ya2
.utils
.lang
import LangMgr
33 from ya2
.utils
.log
import LogMgr
34 from ya2
.utils
.functional
import FunctionalTest
35 from ya2
.p3d
.asserts
import assert_threads
, assert_tasks
, assert_render3d
, \
36 assert_render2d
, assert_aspect2d
, assert_events
, assert_buffers
41 def __init__(self
, pmachines
):
42 super().__init
__('Main FSM')
43 self
._pmachines
= pmachines
46 self
._pmachines
.on_menu_enter()
49 self
._pmachines
.on_menu_exit()
52 def enterScene(self
, cls
):
53 self
._pmachines
.on_scene_enter(cls
)
56 self
._pmachines
.on_scene_exit()
59 def __do_asserts(self
):
60 args
= self
._pmachines
._args
61 if True: # args.functional_test or args.functional_ref:
78 SceneDominoBoxBasketball
,
80 SceneTeeterDominoBoxBasketball
]
83 info('platform: %s' % platform
)
84 info('exists main.py: %s' % exists('main.py'))
85 self
._args
= args
= self
._parse
_args
()
87 self
.base
= ShowBase()
89 self
.updating
= args
.update
90 self
.version
= args
.version
91 self
.log_mgr
= LogMgr
.init_cls()()
92 self
._pos
_mgr
= PositionMgr()
93 self
._prepare
_window
(args
)
96 if args
.functional_test
:
97 self
._options
['settings']['volume'] = 0
98 self
._music
= MusicMgr(self
._options
['settings']['volume'])
99 self
.lang_mgr
= LangMgr(self
._options
['settings']['language'],
102 self
._fsm
= MainFsm(self
)
104 cls
= [cls
for cls
in self
.scenes
if cls
.__name
__ == args
.screenshot
][0]
105 scene
= cls(BulletWorld(), None, True, False, lambda: None, self
.scenes
)
109 elif self
._options
['development']['auto_start']:
110 mod_name
= 'pmachines.scenes.scene_' + self
._options
['development']['auto_start']
111 for member
in import_module(mod_name
).__dict
__.values():
112 if isclass(member
) and issubclass(member
, Scene
) and \
115 self
._fsm
.demand('Scene', cls
)
117 self
._fsm
.demand('Menu')
118 if args
.functional_test
or args
.functional_ref
:
119 FunctionalTest(args
.functional_ref
, self
._pos
_mgr
)
121 taskMgr
.do_method_later(1.0, self
.__assert
_fps
, 'assert_fps')
123 def on_menu_enter(self
):
124 self
._menu
_bg
= Background()
126 self
._fsm
, self
.lang_mgr
, self
._options
, self
._music
,
127 self
._pipeline
, self
.scenes
, self
._args
.functional_test
or self
._args
.functional_ref
,
131 self
._fsm
.demand('Menu')
133 def on_menu_exit(self
):
134 self
._menu
_bg
.destroy()
137 def on_scene_enter(self
, cls
):
140 self
.world
, self
.on_home
,
141 self
._options
['development']['auto_close_instructions'],
142 self
._options
['development']['debug_items'],
146 self
._args
.functional_test
or self
._args
.functional_ref
,
147 self
._options
['development']['mouse_coords'])
149 def on_scene_exit(self
):
150 self
._unset
_physics
()
151 self
._scene
.destroy()
153 def reload(self
, cls
):
154 self
._fsm
.demand('Scene', cls
)
156 def _configure(self
, args
):
157 load_prc_file_data('', 'window-title pmachines')
158 load_prc_file_data('', 'framebuffer-srgb true')
159 load_prc_file_data('', 'sync-video true')
160 if args
.functional_test
or args
.functional_ref
:
161 load_prc_file_data('', 'win-size 1360 768')
162 # otherwise it is not centered in exwm
163 # load_prc_file_data('', 'threading-model Cull/Draw')
164 # it freezes when you go to the next scene
166 load_prc_file_data('', 'window-type offscreen')
167 load_prc_file_data('', 'audio-library-name null')
169 def _parse_args(self
):
170 parser
= argparse
.ArgumentParser()
171 parser
.add_argument('--update', action
='store_true')
172 parser
.add_argument('--version', action
='store_true')
173 parser
.add_argument('--optfile')
174 parser
.add_argument('--screenshot')
175 parser
.add_argument('--functional-test', action
='store_true')
176 parser
.add_argument('--functional-ref', action
='store_true')
177 cmd_line
= [arg
for arg
in iter(argv
[1:]) if not arg
.startswith('-psn_')]
178 args
= parser
.parse_args(cmd_line
)
181 def _prepare_window(self
, args
):
183 if (platform
.startswith('win') or platform
.startswith('linux')) and (
184 not exists('main.py') or __file__
.startswith('/app/bin/')):
185 # it is the deployed version for windows
186 data_path
= str(Filename
.get_user_appdata_directory()) + '/pmachines'
187 home
= '/home/flavio' # we must force this for wine
188 if data_path
.startswith('/c/users/') and exists(home
+ '/.wine/'):
189 data_path
= home
+ '/.wine/drive_' + data_path
[1:]
190 info('creating dirs: %s' % data_path
)
191 makedirs(data_path
, exist_ok
=True)
192 optfile
= args
.optfile
if args
.optfile
else 'options.ini'
193 info('data path: %s' % data_path
)
194 info('option file: %s' % optfile
)
195 info('fixed path: %s' % LibP3d
.fixpath(data_path
+ '/' + optfile
))
209 'auto_close_instructions': 0,
214 opt_path
= LibP3d
.fixpath(data_path
+ '/' + optfile
) if data_path
else optfile
215 opt_exists
= exists(opt_path
)
216 self
._options
= DctFile(
217 LibP3d
.fixpath(data_path
+ '/' + optfile
) if data_path
else optfile
,
220 self
._options
.store()
221 res
= self
._options
['settings']['resolution']
223 res
= LVector2i(*[int(_res
) for _res
in res
.split('x')])
227 d_i
= base
.pipe
.get_display_information()
229 return d_i
.get_display_mode_width(idx
), \
230 d_i
.get_display_mode_height(idx
)
232 _res(idx
) for idx
in range(d_i
.get_total_display_modes())]
233 res
= sorted(resolutions
)[-1]
234 fullscreen
= self
._options
['settings']['fullscreen']
235 props
= WindowProperties()
236 if args
.functional_test
or args
.functional_ref
:
240 props
.set_fullscreen(fullscreen
)
241 props
.set_icon_filename('assets/images/icon/pmachines.ico')
242 if not args
.screenshot
and not self
.version
:
243 base
.win
.request_properties(props
)
244 #gltf.patch_loader(base.loader)
245 if self
._options
['development']['simplepbr'] and not self
.version
:
246 self
._pipeline
= simplepbr
.init(
247 use_normal_maps
=True,
248 use_emission_maps
=False,
249 use_occlusion_maps
=True,
250 msaa_samples
=4 if self
._options
['settings']['antialiasing'] else 1,
251 enable_shadows
=int(self
._options
['settings']['shadows']))
252 debug(f
'msaa: {self._pipeline.msaa_samples}')
253 debug(f
'shadows: {self._pipeline.enable_shadows}')
254 render
.setAntialias(AntialiasAttrib
.MAuto
)
255 self
.base
.set_background_color(0, 0, 0, 1)
256 self
.base
.disable_mouse()
257 if self
._options
['development']['show_buffers']:
258 base
.bufferViewer
.toggleEnable()
259 if self
._options
['development']['fps']:
260 base
.set_frame_rate_meter(True)
261 #self.base.accept('window-event', self._on_win_evt)
262 self
.base
.accept('aspectRatioChanged', self
._on
_aspect
_ratio
_changed
)
263 if self
._options
['development']['mouse_coords']:
264 coords_txt
= OnscreenText(
265 '', parent
=base
.a2dTopRight
, scale
=0.04,
266 pos
=(-.03, -.06), fg
=(.9, .9, .9, 1), align
=TextNode
.A_right
)
267 def update_coords(task
):
268 txt
= '%s %s' % (int(base
.win
.get_pointer(0).x
),
269 int(base
.win
.get_pointer(0).y
))
270 coords_txt
['text'] = txt
272 taskMgr
.add(update_coords
, 'update_coords')
274 def _set_physics(self
):
275 if self
._options
['development']['physics_debug']:
276 debug_node
= BulletDebugNode('Debug')
277 debug_node
.show_wireframe(True)
278 debug_node
.show_constraints(True)
279 debug_node
.show_bounding_boxes(True)
280 debug_node
.show_normals(True)
281 self
._debug
_np
= render
.attach_new_node(debug_node
)
282 self
._debug
_np
.show()
283 self
.world
= BulletWorld()
284 self
.world
.set_gravity((0, 0, -9.81))
285 if self
._options
['development']['physics_debug']:
286 self
.world
.set_debug_node(self
._debug
_np
.node())
288 dt
= globalClock
.get_dt()
289 self
.world
.do_physics(dt
, 10, 1/180)
291 self
._phys
_tsk
= taskMgr
.add(update
, 'update')
293 def _unset_physics(self
):
294 if self
._options
['development']['physics_debug']:
295 self
._debug
_np
.remove_node()
297 taskMgr
.remove(self
._phys
_tsk
)
299 def _on_aspect_ratio_changed(self
):
300 if self
._fsm
.state
== 'Scene':
301 self
._scene
.on_aspect_ratio_changed()
303 def __assert_fps(self
, task
):
304 if len(self
.__fps
_lst
) > 3:
305 self
.__fps
_lst
.pop(0)
306 self
.__fps
_lst
+= [globalClock
.average_frame_rate
]
307 if len(self
.__fps
_lst
) == 4:
308 fps_threshold
= 55 if cpu_count() >= 4 else 25
309 assert(any(fps
> fps_threshold
for fps
in self
.__fps
_lst
), 'low fps %s' % self
.__fps
_lst
)