5 from sys
import platform
, exit
, argv
6 from platform
import node
7 from logging
import info
, debug
8 from os
.path
import exists
9 from os
import makedirs
10 from multiprocessing
import cpu_count
11 from panda3d
.core
import Filename
, load_prc_file_data
, AntialiasAttrib
, \
12 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
.posmgr
import PositionMgr
22 from pmachines
.persistent
import Persistent
23 from ya2
.utils
.dictfile
import DctFile
24 from ya2
.p3d
.p3d
import LibP3d
25 from ya2
.utils
.lang
import LangMgr
26 from ya2
.utils
.log
import LogMgr
27 from ya2
.utils
.functional
import FunctionalTest
28 from ya2
.p3d
.asserts
import Assert
33 def __init__(self
, pmachines
):
34 super().__init
__('Main FSM')
35 self
._pmachines
= pmachines
38 self
._pmachines
.on_menu_enter()
41 self
._pmachines
.on_menu_exit()
44 def enterScene(self
, cls
):
45 self
._pmachines
.on_scene_enter(cls
)
48 self
._pmachines
.on_scene_exit()
51 def __do_asserts(self
):
52 args
= self
._pmachines
._args
53 if not LibP3d
.runtime() or args
.functional_test
or args
.functional_ref
:
54 Assert
.assert_threads()
56 Assert
.assert_render3d()
57 Assert
.assert_render2d()
58 Assert
.assert_aspect2d()
59 Assert
.assert_events()
60 Assert
.assert_buffers()
67 with
open('assets/scenes/index.json') as f
:
68 json
= loads(f
.read())
72 info('platform: %s' % platform
)
73 info('exists main.py: %s' % exists('main.py'))
74 self
._args
= args
= self
._parse
_args
()
76 self
.base
= ShowBase()
78 self
.is_update_run
= args
.update
79 self
.is_version_run
= args
.version
80 self
.log_mgr
= LogMgr
.init_cls()()
81 self
._pos
_mgr
= PositionMgr()
82 self
._prepare
_window
(args
)
85 if args
.functional_test
:
86 self
._options
['settings']['volume'] = 0
87 self
._music
= MusicMgr(self
._options
['settings']['volume'])
88 self
.lang_mgr
= LangMgr(self
._options
['settings']['language'],
91 self
._fsm
= MainFsm(self
)
92 if args
.functional_test
or args
.functional_ref
:
93 FunctionalTest(args
.functional_ref
, self
._pos
_mgr
)
94 if not LibP3d
.runtime() or args
.functional_test
or args
.functional_ref
:
96 taskMgr
.do_method_later(1.0, self
.__assert
_fps
, 'assert_fps')
99 if self
._args
.screenshot
:
100 #cls = [cls for cls in self.scenes if cls.__name__ == self._args.screenshot][0]
101 scene
= Scene(BulletWorld(), None, True, False, lambda: None, self
.scenes(), self
._pos
_mgr
, None, None, None, self
._args
.screenshot
, None, None)
105 elif self
._options
['development']['auto_start']:
106 # mod_name = 'pmachines.scenes.scene_' + self._options['development']['auto_start']
107 # for member in import_module(mod_name).__dict__.values():
108 # if isclass(member) and issubclass(member, Scene) and \
111 self
._fsm
.demand('Scene', self
._options
['development']['auto_start'])
113 Scene
.scenes_done
= self
.__persistent
.scenes_done
114 self
._fsm
.demand('Menu')
116 def on_menu_enter(self
):
117 self
._menu
_bg
= Background()
119 self
._fsm
, self
.lang_mgr
, self
._options
, self
._music
,
120 self
._pipeline
, self
.scenes(), self
._args
.functional_test
or self
._args
.functional_ref
,
124 Scene
.scenes_done
= self
.__persistent
.scenes_done
125 self
._fsm
.demand('Menu')
127 def on_menu_exit(self
):
128 self
._menu
_bg
.destroy()
131 def on_scene_enter(self
, scene_name
):
134 self
.world
, self
.on_home
,
135 self
._options
['development']['auto_close_instructions'],
136 self
._options
['development']['debug_items'],
140 self
._args
.functional_test
or self
._args
.functional_ref
,
141 self
._options
['development']['mouse_coords'],
144 self
._options
['development']['editor'],
145 self
._options
['development']['auto_start_editor'])
147 def on_scene_exit(self
):
148 self
._unset
_physics
()
149 self
._scene
.destroy()
151 def reload(self
, cls
):
152 self
._fsm
.demand('Scene', cls
)
154 def _configure(self
, args
):
155 load_prc_file_data('', 'window-title pmachines')
156 load_prc_file_data('', 'framebuffer-srgb true')
157 load_prc_file_data('', 'sync-video true')
158 if args
.functional_test
or args
.functional_ref
:
159 load_prc_file_data('', 'win-size 1360 768')
160 # otherwise it is not centered in exwm
161 # load_prc_file_data('', 'threading-model Cull/Draw')
162 # it freezes when you go to the next scene
164 load_prc_file_data('', 'window-type offscreen')
165 load_prc_file_data('', 'audio-library-name null')
167 def _parse_args(self
):
168 parser
= argparse
.ArgumentParser()
169 parser
.add_argument('--update', action
='store_true')
170 parser
.add_argument('--version', action
='store_true')
171 parser
.add_argument('--optfile')
172 parser
.add_argument('--screenshot')
173 parser
.add_argument('--functional-test', action
='store_true')
174 parser
.add_argument('--functional-ref', action
='store_true')
175 cmd_line
= [arg
for arg
in iter(argv
[1:]) if not arg
.startswith('-psn_')]
176 args
= parser
.parse_args(cmd_line
)
179 def _prepare_window(self
, args
):
181 if (platform
.startswith('win') or platform
.startswith('linux')) and (
182 not exists('main.py') or __file__
.startswith('/app/bin/')):
183 # it is the deployed version for windows
184 data_path
= str(Filename
.get_user_appdata_directory()) + '/pmachines'
185 home
= '/home/flavio' # we must force this for wine
186 if data_path
.startswith('/c/users/') and exists(home
+ '/.wine/'):
187 data_path
= home
+ '/.wine/drive_' + data_path
[1:]
188 info('creating dirs: %s' % data_path
)
189 makedirs(data_path
, exist_ok
=True)
190 optfile
= args
.optfile
if args
.optfile
else 'options.ini'
191 info('data path: %s' % data_path
)
192 info('option file: %s' % optfile
)
193 info('fixed path: %s' % LibP3d
.fixpath(data_path
+ '/' + optfile
))
210 'auto_close_instructions': 0,
216 'auto_start_editor': 0}}
217 opt_path
= LibP3d
.fixpath(data_path
+ '/' + optfile
) if data_path
else optfile
218 opt_exists
= exists(opt_path
)
219 self
._options
= DctFile(
220 LibP3d
.fixpath(data_path
+ '/' + optfile
) if data_path
else optfile
,
223 self
._options
.store()
224 self
.__persistent
= Persistent(self
._options
['save']['scenes_done'], self
._options
)
225 Scene
.scenes_done
= self
.__persistent
.scenes_done
226 res
= self
._options
['settings']['resolution']
228 res
= LVector2i(*[int(_res
) for _res
in res
.split('x')])
231 if not self
.is_version_run
:
232 d_i
= base
.pipe
.get_display_information()
234 return d_i
.get_display_mode_width(idx
), \
235 d_i
.get_display_mode_height(idx
)
237 _res(idx
) for idx
in range(d_i
.get_total_display_modes())]
238 res
= sorted(resolutions
)[-1]
239 fullscreen
= self
._options
['settings']['fullscreen']
240 props
= WindowProperties()
241 if args
.functional_test
or args
.functional_ref
:
243 elif not self
.is_version_run
:
245 props
.set_fullscreen(fullscreen
)
246 props
.set_icon_filename('assets/images/icon/pmachines.ico')
247 if not args
.screenshot
and not self
.is_version_run
and base
.win
:
248 base
.win
.request_properties(props
)
249 #gltf.patch_loader(base.loader)
250 if self
._options
['development']['simplepbr'] and not self
.is_version_run
and base
.win
:
251 self
._pipeline
= simplepbr
.init(
252 use_normal_maps
=True,
253 use_emission_maps
=False,
254 use_occlusion_maps
=True,
255 msaa_samples
=4 if self
._options
['settings']['antialiasing'] else 1,
256 enable_shadows
=int(self
._options
['settings']['shadows']))
257 debug(f
'msaa: {self._pipeline.msaa_samples}')
258 debug(f
'shadows: {self._pipeline.enable_shadows}')
259 render
.setAntialias(AntialiasAttrib
.MAuto
)
260 self
.base
.set_background_color(0, 0, 0, 1)
261 self
.base
.disable_mouse()
262 if self
._options
['development']['show_buffers']:
263 base
.bufferViewer
.toggleEnable()
264 if self
._options
['development']['fps']:
265 base
.set_frame_rate_meter(True)
266 #self.base.accept('window-event', self._on_win_evt)
267 self
.base
.accept('aspectRatioChanged', self
._on
_aspect
_ratio
_changed
)
268 if self
._options
['development']['mouse_coords']:
269 coords_txt
= OnscreenText(
270 '', parent
=base
.a2dTopRight
, scale
=0.04,
271 pos
=(-.03, -.06), fg
=(.9, .9, .9, 1), align
=TextNode
.A_right
)
272 def update_coords(task
):
273 txt
= '%s %s' % (int(base
.win
.get_pointer(0).x
),
274 int(base
.win
.get_pointer(0).y
))
275 coords_txt
['text'] = txt
277 taskMgr
.add(update_coords
, 'update_coords')
279 def _set_physics(self
):
280 if self
._options
['development']['physics_debug']:
281 debug_node
= BulletDebugNode('Debug')
282 debug_node
.show_wireframe(True)
283 debug_node
.show_constraints(True)
284 debug_node
.show_bounding_boxes(True)
285 debug_node
.show_normals(True)
286 self
._debug
_np
= render
.attach_new_node(debug_node
)
287 self
._debug
_np
.show()
288 self
.world
= BulletWorld()
289 self
.world
.set_gravity((0, 0, -9.81))
290 if self
._options
['development']['physics_debug']:
291 self
.world
.set_debug_node(self
._debug
_np
.node())
293 dt
= globalClock
.get_dt()
294 self
.world
.do_physics(dt
, 10, 1/180)
296 self
._phys
_tsk
= taskMgr
.add(update
, 'update')
298 def _unset_physics(self
):
299 if self
._options
['development']['physics_debug']:
300 self
._debug
_np
.remove_node()
302 taskMgr
.remove(self
._phys
_tsk
)
304 def _on_aspect_ratio_changed(self
):
305 if self
._fsm
.state
== 'Scene':
306 self
._scene
.on_aspect_ratio_changed()
308 def __assert_fps(self
, task
):
309 if len(self
.__fps
_lst
) > 3:
310 self
.__fps
_lst
.pop(0)
311 self
.__fps
_lst
+= [globalClock
.average_frame_rate
]
312 if len(self
.__fps
_lst
) == 4:
313 fps_threshold
= 55 if cpu_count() >= 4 and node() != 'localhost.localdomain' else 10 # i.e. it is the builder machine
314 assert not all(fps
< fps_threshold
for fps
in self
.__fps
_lst
), 'low fps %s' % self
.__fps
_lst