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 not LibP3d
.runtime() or 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
)
120 if not LibP3d
.runtime() or args
.functional_test
or args
.functional_ref
:
122 taskMgr
.do_method_later(1.0, self
.__assert
_fps
, 'assert_fps')
124 def on_menu_enter(self
):
125 self
._menu
_bg
= Background()
127 self
._fsm
, self
.lang_mgr
, self
._options
, self
._music
,
128 self
._pipeline
, self
.scenes
, self
._args
.functional_test
or self
._args
.functional_ref
,
132 self
._fsm
.demand('Menu')
134 def on_menu_exit(self
):
135 self
._menu
_bg
.destroy()
138 def on_scene_enter(self
, cls
):
141 self
.world
, self
.on_home
,
142 self
._options
['development']['auto_close_instructions'],
143 self
._options
['development']['debug_items'],
147 self
._args
.functional_test
or self
._args
.functional_ref
,
148 self
._options
['development']['mouse_coords'])
150 def on_scene_exit(self
):
151 self
._unset
_physics
()
152 self
._scene
.destroy()
154 def reload(self
, cls
):
155 self
._fsm
.demand('Scene', cls
)
157 def _configure(self
, args
):
158 load_prc_file_data('', 'window-title pmachines')
159 load_prc_file_data('', 'framebuffer-srgb true')
160 load_prc_file_data('', 'sync-video true')
161 if args
.functional_test
or args
.functional_ref
:
162 load_prc_file_data('', 'win-size 1360 768')
163 # otherwise it is not centered in exwm
164 # load_prc_file_data('', 'threading-model Cull/Draw')
165 # it freezes when you go to the next scene
167 load_prc_file_data('', 'window-type offscreen')
168 load_prc_file_data('', 'audio-library-name null')
170 def _parse_args(self
):
171 parser
= argparse
.ArgumentParser()
172 parser
.add_argument('--update', action
='store_true')
173 parser
.add_argument('--version', action
='store_true')
174 parser
.add_argument('--optfile')
175 parser
.add_argument('--screenshot')
176 parser
.add_argument('--functional-test', action
='store_true')
177 parser
.add_argument('--functional-ref', action
='store_true')
178 cmd_line
= [arg
for arg
in iter(argv
[1:]) if not arg
.startswith('-psn_')]
179 args
= parser
.parse_args(cmd_line
)
182 def _prepare_window(self
, args
):
184 if (platform
.startswith('win') or platform
.startswith('linux')) and (
185 not exists('main.py') or __file__
.startswith('/app/bin/')):
186 # it is the deployed version for windows
187 data_path
= str(Filename
.get_user_appdata_directory()) + '/pmachines'
188 home
= '/home/flavio' # we must force this for wine
189 if data_path
.startswith('/c/users/') and exists(home
+ '/.wine/'):
190 data_path
= home
+ '/.wine/drive_' + data_path
[1:]
191 info('creating dirs: %s' % data_path
)
192 makedirs(data_path
, exist_ok
=True)
193 optfile
= args
.optfile
if args
.optfile
else 'options.ini'
194 info('data path: %s' % data_path
)
195 info('option file: %s' % optfile
)
196 info('fixed path: %s' % LibP3d
.fixpath(data_path
+ '/' + optfile
))
210 'auto_close_instructions': 0,
215 opt_path
= LibP3d
.fixpath(data_path
+ '/' + optfile
) if data_path
else optfile
216 opt_exists
= exists(opt_path
)
217 self
._options
= DctFile(
218 LibP3d
.fixpath(data_path
+ '/' + optfile
) if data_path
else optfile
,
221 self
._options
.store()
222 res
= self
._options
['settings']['resolution']
224 res
= LVector2i(*[int(_res
) for _res
in res
.split('x')])
228 d_i
= base
.pipe
.get_display_information()
230 return d_i
.get_display_mode_width(idx
), \
231 d_i
.get_display_mode_height(idx
)
233 _res(idx
) for idx
in range(d_i
.get_total_display_modes())]
234 res
= sorted(resolutions
)[-1]
235 fullscreen
= self
._options
['settings']['fullscreen']
236 props
= WindowProperties()
237 if args
.functional_test
or args
.functional_ref
:
241 props
.set_fullscreen(fullscreen
)
242 props
.set_icon_filename('assets/images/icon/pmachines.ico')
243 if not args
.screenshot
and not self
.version
:
244 base
.win
.request_properties(props
)
245 #gltf.patch_loader(base.loader)
246 if self
._options
['development']['simplepbr'] and not self
.version
:
247 self
._pipeline
= simplepbr
.init(
248 use_normal_maps
=True,
249 use_emission_maps
=False,
250 use_occlusion_maps
=True,
251 msaa_samples
=4 if self
._options
['settings']['antialiasing'] else 1,
252 enable_shadows
=int(self
._options
['settings']['shadows']))
253 debug(f
'msaa: {self._pipeline.msaa_samples}')
254 debug(f
'shadows: {self._pipeline.enable_shadows}')
255 render
.setAntialias(AntialiasAttrib
.MAuto
)
256 self
.base
.set_background_color(0, 0, 0, 1)
257 self
.base
.disable_mouse()
258 if self
._options
['development']['show_buffers']:
259 base
.bufferViewer
.toggleEnable()
260 if self
._options
['development']['fps']:
261 base
.set_frame_rate_meter(True)
262 #self.base.accept('window-event', self._on_win_evt)
263 self
.base
.accept('aspectRatioChanged', self
._on
_aspect
_ratio
_changed
)
264 if self
._options
['development']['mouse_coords']:
265 coords_txt
= OnscreenText(
266 '', parent
=base
.a2dTopRight
, scale
=0.04,
267 pos
=(-.03, -.06), fg
=(.9, .9, .9, 1), align
=TextNode
.A_right
)
268 def update_coords(task
):
269 txt
= '%s %s' % (int(base
.win
.get_pointer(0).x
),
270 int(base
.win
.get_pointer(0).y
))
271 coords_txt
['text'] = txt
273 taskMgr
.add(update_coords
, 'update_coords')
275 def _set_physics(self
):
276 if self
._options
['development']['physics_debug']:
277 debug_node
= BulletDebugNode('Debug')
278 debug_node
.show_wireframe(True)
279 debug_node
.show_constraints(True)
280 debug_node
.show_bounding_boxes(True)
281 debug_node
.show_normals(True)
282 self
._debug
_np
= render
.attach_new_node(debug_node
)
283 self
._debug
_np
.show()
284 self
.world
= BulletWorld()
285 self
.world
.set_gravity((0, 0, -9.81))
286 if self
._options
['development']['physics_debug']:
287 self
.world
.set_debug_node(self
._debug
_np
.node())
289 dt
= globalClock
.get_dt()
290 self
.world
.do_physics(dt
, 10, 1/180)
292 self
._phys
_tsk
= taskMgr
.add(update
, 'update')
294 def _unset_physics(self
):
295 if self
._options
['development']['physics_debug']:
296 self
._debug
_np
.remove_node()
298 taskMgr
.remove(self
._phys
_tsk
)
300 def _on_aspect_ratio_changed(self
):
301 if self
._fsm
.state
== 'Scene':
302 self
._scene
.on_aspect_ratio_changed()
304 def __assert_fps(self
, task
):
305 if len(self
.__fps
_lst
) > 3:
306 self
.__fps
_lst
.pop(0)
307 self
.__fps
_lst
+= [globalClock
.average_frame_rate
]
308 if len(self
.__fps
_lst
) == 4:
309 fps_threshold
= 55 if cpu_count() >= 4 else 25
310 assert(any(fps
> fps_threshold
for fps
in self
.__fps
_lst
), 'low fps %s' % self
.__fps
_lst
)