0fcc1be4422178506504412d2377179ebd5a71b9
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
, GraphicsBuffer
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 MusicManager
18 from pmachines
.items
.background
import Background
19 from pmachines
.gui
.menu
import Menu
20 from pmachines
.scene
.scene
import Scene
21 from pmachines
.application
.persistency
import Persistency
22 from ya2
.utils
.dictfile
import DctFile
23 from ya2
.utils
.logics
import LogicsTools
24 from ya2
.utils
.language
import LanguageManager
25 from ya2
.utils
.log
import WindowedLogManager
26 from ya2
.utils
.functional
import FunctionalTest
27 from ya2
.utils
.asserts
import Assert
28 from ya2
.utils
.gfx
import DirectGuiMixin
29 from ya2
.utils
.gui
.gui
import GuiTools
34 def __init__(self
, pmachines
):
35 super().__init
__('Main FSM')
36 self
._pmachines
= pmachines
37 self
.accept('new_scene', self
.__on
_new
_scene
)
40 self
._pmachines
.on_menu_enter()
43 self
._pmachines
.on_menu_exit()
45 DirectGuiMixin
.clear_tooltips()
47 def enterScene(self
, scene_name
):
48 self
._pmachines
.on_scene_enter(scene_name
)
51 self
._pmachines
.on_scene_exit()
53 DirectGuiMixin
.clear_tooltips()
55 def __on_new_scene(self
):
56 self
.demand('Scene', None)
58 def __do_asserts(self
):
59 args
= self
._pmachines
._args
60 if not LogicsTools
.in_build
or args
.functional_test
or args
.functional_ref
:
61 Assert
.assert_threads()
63 Assert
.assert_render3d()
64 Assert
.assert_render2d()
65 Assert
.assert_aspect2d()
66 Assert
.assert_events()
67 Assert
.assert_buffers()
70 self
.ignore('new_scene')
77 with
open('assets/scenes/index.json') as f
:
78 json
= loads(f
.read())
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
.is_update_run
= args
.update
89 self
.is_version_run
= args
.version
90 self
.log_mgr
= WindowedLogManager
.init_cls()
92 self
._prepare
_window
(args
)
94 self
._fsm
= MainFsm(self
)
95 self
._fsm
.demand('Start') # otherwise it is Off and cleanup in tests won't work
98 if args
.functional_test
:
99 self
._options
['settings']['volume'] = 0
100 self
._music
= MusicManager(self
._options
['settings']['volume'], 'pmachines')
101 self
.lang_mgr
= LanguageManager(self
._options
['settings']['language'],
104 if args
.functional_test
or args
.functional_ref
:
105 FunctionalTest(args
.functional_ref
, self
._pos
_mgr
, 'pmachines')
106 if not LogicsTools
.in_build
or args
.functional_test
or args
.functional_ref
:
108 taskMgr
.do_method_later(1.0, self
.__assert
_fps
, 'assert_fps')
111 if self
._args
.screenshot
:
112 #cls = [cls for cls in self.scenes if cls.__name__ == self._args.screenshot][0]
113 scene
= Scene(BulletWorld(), None, True, False, lambda: None, self
.scenes(), self
._pos
_mgr
, None, None, None, self
._args
.screenshot
, None, None)
117 elif self
._options
['development']['auto_start']:
118 # mod_name = 'pmachines.scenes.scene_' + self._options['development']['auto_start']
119 # for member in import_module(mod_name).__dict__.values():
120 # if isclass(member) and issubclass(member, Scene) and \
123 self
._fsm
.demand('Scene', self
._options
['development']['auto_start'])
125 Scene
.scenes_done
= self
.__persistent
.scenes_done
126 self
._fsm
.demand('Menu')
128 def on_menu_enter(self
):
129 self
._menu
_bg
= Background()
131 lambda scene_name
: self
._fsm
.demand('Scene', scene_name
),
132 self
.lang_mgr
.set_language
, self
._options
,
133 self
._pipeline
, self
.scenes(), self
._args
.functional_test
or self
._args
.functional_ref
,
137 Scene
.scenes_done
= self
.__persistent
.scenes_done
138 self
._fsm
.demand('Menu')
140 def on_menu_exit(self
):
141 self
._menu
_bg
.destroy()
144 def on_scene_enter(self
, scene_name
):
147 self
.world
, self
.on_home
,
148 self
._options
['development']['auto_close_instructions'],
149 self
._options
['development']['debug_items'],
153 self
._args
.functional_test
or self
._args
.functional_ref
,
154 self
._options
['development']['mouse_coords'],
157 self
._options
['development']['editor'],
158 self
._options
['development']['auto_start_editor'])
160 def on_scene_exit(self
):
161 self
._unset
_physics
()
162 self
._scene
.destroy()
164 def reload(self
, cls
):
165 self
._fsm
.demand('Scene', cls
)
167 def _configure(self
, args
):
168 load_prc_file_data('', 'window-title pmachines')
169 load_prc_file_data('', 'framebuffer-srgb true')
170 load_prc_file_data('', 'sync-video true')
171 if args
.functional_test
or args
.functional_ref
:
172 load_prc_file_data('', 'win-size 1360 768')
173 # otherwise it is not centered in exwm
174 # load_prc_file_data('', 'threading-model Cull/Draw')
175 # it freezes when you go to the next scene
177 load_prc_file_data('', 'window-type offscreen')
178 load_prc_file_data('', 'audio-library-name null')
180 def _parse_args(self
):
181 parser
= argparse
.ArgumentParser()
182 parser
.add_argument('--update', action
='store_true')
183 parser
.add_argument('--version', action
='store_true')
184 parser
.add_argument('--optfile')
185 parser
.add_argument('--screenshot')
186 parser
.add_argument('--functional-test', action
='store_true')
187 parser
.add_argument('--functional-ref', action
='store_true')
188 cmd_line
= [arg
for arg
in iter(argv
[1:]) if not arg
.startswith('-psn_')]
189 args
= parser
.parse_args(cmd_line
)
192 def _prepare_window(self
, args
):
194 if (platform
.startswith('win') or platform
.startswith('linux')) and (
195 not exists('main.py') or __file__
.startswith('/app/bin/')):
196 # it is the deployed version for windows
197 data_path
= str(Filename
.get_user_appdata_directory()) + '/pmachines'
198 home
= '/home/flavio' # we must force this for wine
199 if data_path
.startswith('/c/users/') and exists(home
+ '/.wine/'):
200 data_path
= home
+ '/.wine/drive_' + data_path
[1:]
201 info('creating dirs: %s' % data_path
)
202 makedirs(data_path
, exist_ok
=True)
203 optfile
= args
.optfile
if args
.optfile
else 'options.ini'
204 info(f
'{data_path=}')
205 info('option file: %s' % optfile
)
206 info('fixed path: %s' % LogicsTools
.platform_specific_path(data_path
+ '/' + optfile
))
223 'auto_close_instructions': 0,
229 'auto_start_editor': 0}}
230 opt_path
= LogicsTools
.platform_specific_path(data_path
+ '/' + optfile
) if data_path
else optfile
231 opt_exists
= exists(opt_path
)
232 self
._options
= DctFile(
233 LogicsTools
.platform_specific_path(data_path
+ '/' + optfile
) if data_path
else optfile
,
236 self
._options
.store()
237 self
.__persistent
= Persistency(self
._options
['save']['scenes_done'], self
._options
)
238 Scene
.scenes_done
= self
.__persistent
.scenes_done
239 res
= self
._options
['settings']['resolution']
241 res
= LVector2i(*[int(_res
) for _res
in res
.split('x')])
244 if not self
.is_version_run
:
245 d_i
= base
.pipe
.get_display_information()
247 return d_i
.get_display_mode_width(idx
), \
248 d_i
.get_display_mode_height(idx
)
250 _res(idx
) for idx
in range(d_i
.get_total_display_modes())]
251 res
= sorted(resolutions
)[-1]
252 fullscreen
= self
._options
['settings']['fullscreen']
253 props
= WindowProperties()
254 if args
.functional_test
or args
.functional_ref
:
256 elif not self
.is_version_run
:
258 props
.set_fullscreen(fullscreen
)
259 props
.set_icon_filename('assets/images/icon/pmachines.ico')
260 if not args
.screenshot
and not self
.is_version_run
and base
.win
and not isinstance(base
.win
, GraphicsBuffer
):
261 base
.win
.request_properties(props
)
262 #gltf.patch_loader(base.loader)
263 if self
._options
['development']['simplepbr'] and not self
.is_version_run
and base
.win
:
264 self
._pipeline
= simplepbr
.init(
265 use_normal_maps
=True,
266 use_emission_maps
=False,
267 use_occlusion_maps
=True,
268 msaa_samples
=4 if self
._options
['settings']['antialiasing'] else 1,
269 enable_shadows
=int(self
._options
['settings']['shadows']))
270 debug(f
'msaa: {self._pipeline.msaa_samples}')
271 debug(f
'shadows: {self._pipeline.enable_shadows}')
272 render
.setAntialias(AntialiasAttrib
.MAuto
)
273 self
.base
.set_background_color(0, 0, 0, 1)
274 self
.base
.disable_mouse()
275 if self
._options
['development']['show_buffers']:
276 base
.bufferViewer
.toggleEnable()
277 if self
._options
['development']['fps']:
278 base
.set_frame_rate_meter(True)
279 #self.base.accept('window-event', self._on_win_evt)
280 self
.base
.accept('aspectRatioChanged', self
._on
_aspect
_ratio
_changed
)
281 if self
._options
['development']['mouse_coords']:
282 coords_txt
= OnscreenText(
283 '', parent
=base
.a2dTopRight
, scale
=0.04,
284 pos
=(-.03, -.06), fg
=(.9, .9, .9, 1), align
=TextNode
.A_right
)
285 def update_coords(task
):
286 txt
= '%s %s' % (int(base
.win
.get_pointer(0).x
),
287 int(base
.win
.get_pointer(0).y
))
288 coords_txt
['text'] = txt
290 taskMgr
.add(update_coords
, 'update_coords')
292 def _set_physics(self
):
293 if self
._options
['development']['physics_debug']:
294 debug_node
= BulletDebugNode('Debug')
295 debug_node
.show_wireframe(True)
296 debug_node
.show_constraints(True)
297 debug_node
.show_bounding_boxes(True)
298 debug_node
.show_normals(True)
299 self
._debug
_np
= render
.attach_new_node(debug_node
)
300 self
._debug
_np
.show()
301 self
.world
= BulletWorld()
302 self
.world
.set_gravity((0, 0, -9.81))
303 if self
._options
['development']['physics_debug']:
304 self
.world
.set_debug_node(self
._debug
_np
.node())
306 dt
= globalClock
.get_dt()
307 self
.world
.do_physics(dt
, 10, 1/180)
309 self
._phys
_tsk
= taskMgr
.add(update
, 'update')
311 def _unset_physics(self
):
312 if self
._options
['development']['physics_debug']:
313 self
._debug
_np
.remove_node()
315 taskMgr
.remove(self
._phys
_tsk
)
317 def _on_aspect_ratio_changed(self
):
318 if self
._fsm
.state
== 'Scene':
319 self
._scene
.on_aspect_ratio_changed()
321 def __assert_fps(self
, task
):
322 if len(self
.__fps
_lst
) > 3:
323 self
.__fps
_lst
.pop(0)
324 self
.__fps
_lst
+= [globalClock
.average_frame_rate
]
325 if len(self
.__fps
_lst
) == 4:
326 fps_threshold
= 55 if cpu_count() >= 4 and node() != 'localhost.localdomain' else 10 # i.e. it is the builder machine
327 assert not all(fps
< fps_threshold
for fps
in self
.__fps
_lst
), 'low fps %s' % self
.__fps
_lst