f6cb6a35483f3907a1a4ad634daf44fd872fa4ad
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
.persistent
import Persistent
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
33 def __init__(self
, pmachines
):
34 super().__init
__('Main FSM')
35 self
._pmachines
= pmachines
36 self
.accept('new_scene', self
.__on
_new
_scene
)
39 self
._pmachines
.on_menu_enter()
42 self
._pmachines
.on_menu_exit()
44 DirectGuiMixin
.clear_tooltips()
46 def enterScene(self
, cls
):
47 self
._pmachines
.on_scene_enter(cls
)
50 self
._pmachines
.on_scene_exit()
52 DirectGuiMixin
.clear_tooltips()
54 def __on_new_scene(self
):
55 self
.demand('Scene', None)
57 def __do_asserts(self
):
58 args
= self
._pmachines
._args
59 if not LogicsTools
.in_build
or args
.functional_test
or args
.functional_ref
:
60 Assert
.assert_threads()
62 Assert
.assert_render3d()
63 Assert
.assert_render2d()
64 Assert
.assert_aspect2d()
65 Assert
.assert_events()
66 Assert
.assert_buffers()
69 self
.ignore('new_scene')
76 with
open('assets/scenes/index.json') as f
:
77 json
= loads(f
.read())
81 info('platform: %s' % platform
)
82 info('exists main.py: %s' % exists('main.py'))
83 self
._args
= args
= self
._parse
_args
()
85 self
.base
= ShowBase()
87 self
.is_update_run
= args
.update
88 self
.is_version_run
= args
.version
89 self
.log_mgr
= WindowedLogManager
.init_cls()
91 self
._prepare
_window
(args
)
92 self
._fsm
= MainFsm(self
)
93 self
._fsm
.demand('Start') # otherwise it is Off and cleanup in tests won't work
96 if args
.functional_test
:
97 self
._options
['settings']['volume'] = 0
98 self
._music
= MusicManager(self
._options
['settings']['volume'], 'pmachines')
99 self
.lang_mgr
= LanguageManager(self
._options
['settings']['language'],
102 if args
.functional_test
or args
.functional_ref
:
103 FunctionalTest(args
.functional_ref
, self
._pos
_mgr
, 'pmachines')
104 if not LogicsTools
.in_build
or args
.functional_test
or args
.functional_ref
:
106 taskMgr
.do_method_later(1.0, self
.__assert
_fps
, 'assert_fps')
109 if self
._args
.screenshot
:
110 #cls = [cls for cls in self.scenes if cls.__name__ == self._args.screenshot][0]
111 scene
= Scene(BulletWorld(), None, True, False, lambda: None, self
.scenes(), self
._pos
_mgr
, None, None, None, self
._args
.screenshot
, None, None)
115 elif self
._options
['development']['auto_start']:
116 # mod_name = 'pmachines.scenes.scene_' + self._options['development']['auto_start']
117 # for member in import_module(mod_name).__dict__.values():
118 # if isclass(member) and issubclass(member, Scene) and \
121 self
._fsm
.demand('Scene', self
._options
['development']['auto_start'])
123 Scene
.scenes_done
= self
.__persistent
.scenes_done
124 self
._fsm
.demand('Menu')
126 def on_menu_enter(self
):
127 self
._menu
_bg
= Background()
129 self
._fsm
, self
.lang_mgr
, self
._options
, self
._music
,
130 self
._pipeline
, self
.scenes(), self
._args
.functional_test
or self
._args
.functional_ref
,
134 Scene
.scenes_done
= self
.__persistent
.scenes_done
135 self
._fsm
.demand('Menu')
137 def on_menu_exit(self
):
138 self
._menu
_bg
.destroy()
141 def on_scene_enter(self
, scene_name
):
144 self
.world
, self
.on_home
,
145 self
._options
['development']['auto_close_instructions'],
146 self
._options
['development']['debug_items'],
150 self
._args
.functional_test
or self
._args
.functional_ref
,
151 self
._options
['development']['mouse_coords'],
154 self
._options
['development']['editor'],
155 self
._options
['development']['auto_start_editor'])
157 def on_scene_exit(self
):
158 self
._unset
_physics
()
159 self
._scene
.destroy()
161 def reload(self
, cls
):
162 self
._fsm
.demand('Scene', cls
)
164 def _configure(self
, args
):
165 load_prc_file_data('', 'window-title pmachines')
166 load_prc_file_data('', 'framebuffer-srgb true')
167 load_prc_file_data('', 'sync-video true')
168 if args
.functional_test
or args
.functional_ref
:
169 load_prc_file_data('', 'win-size 1360 768')
170 # otherwise it is not centered in exwm
171 # load_prc_file_data('', 'threading-model Cull/Draw')
172 # it freezes when you go to the next scene
174 load_prc_file_data('', 'window-type offscreen')
175 load_prc_file_data('', 'audio-library-name null')
177 def _parse_args(self
):
178 parser
= argparse
.ArgumentParser()
179 parser
.add_argument('--update', action
='store_true')
180 parser
.add_argument('--version', action
='store_true')
181 parser
.add_argument('--optfile')
182 parser
.add_argument('--screenshot')
183 parser
.add_argument('--functional-test', action
='store_true')
184 parser
.add_argument('--functional-ref', action
='store_true')
185 cmd_line
= [arg
for arg
in iter(argv
[1:]) if not arg
.startswith('-psn_')]
186 args
= parser
.parse_args(cmd_line
)
189 def _prepare_window(self
, args
):
191 if (platform
.startswith('win') or platform
.startswith('linux')) and (
192 not exists('main.py') or __file__
.startswith('/app/bin/')):
193 # it is the deployed version for windows
194 data_path
= str(Filename
.get_user_appdata_directory()) + '/pmachines'
195 home
= '/home/flavio' # we must force this for wine
196 if data_path
.startswith('/c/users/') and exists(home
+ '/.wine/'):
197 data_path
= home
+ '/.wine/drive_' + data_path
[1:]
198 info('creating dirs: %s' % data_path
)
199 makedirs(data_path
, exist_ok
=True)
200 optfile
= args
.optfile
if args
.optfile
else 'options.ini'
201 info('data path: %s' % data_path
)
202 info('option file: %s' % optfile
)
203 info('fixed path: %s' % LogicsTools
.platform_specific_path(data_path
+ '/' + optfile
))
220 'auto_close_instructions': 0,
226 'auto_start_editor': 0}}
227 opt_path
= LogicsTools
.platform_specific_path(data_path
+ '/' + optfile
) if data_path
else optfile
228 opt_exists
= exists(opt_path
)
229 self
._options
= DctFile(
230 LogicsTools
.platform_specific_path(data_path
+ '/' + optfile
) if data_path
else optfile
,
233 self
._options
.store()
234 self
.__persistent
= Persistent(self
._options
['save']['scenes_done'], self
._options
)
235 Scene
.scenes_done
= self
.__persistent
.scenes_done
236 res
= self
._options
['settings']['resolution']
238 res
= LVector2i(*[int(_res
) for _res
in res
.split('x')])
241 if not self
.is_version_run
:
242 d_i
= base
.pipe
.get_display_information()
244 return d_i
.get_display_mode_width(idx
), \
245 d_i
.get_display_mode_height(idx
)
247 _res(idx
) for idx
in range(d_i
.get_total_display_modes())]
248 res
= sorted(resolutions
)[-1]
249 fullscreen
= self
._options
['settings']['fullscreen']
250 props
= WindowProperties()
251 if args
.functional_test
or args
.functional_ref
:
253 elif not self
.is_version_run
:
255 props
.set_fullscreen(fullscreen
)
256 props
.set_icon_filename('assets/images/icon/pmachines.ico')
257 if not args
.screenshot
and not self
.is_version_run
and base
.win
and not isinstance(base
.win
, GraphicsBuffer
):
258 base
.win
.request_properties(props
)
259 #gltf.patch_loader(base.loader)
260 if self
._options
['development']['simplepbr'] and not self
.is_version_run
and base
.win
:
261 self
._pipeline
= simplepbr
.init(
262 use_normal_maps
=True,
263 use_emission_maps
=False,
264 use_occlusion_maps
=True,
265 msaa_samples
=4 if self
._options
['settings']['antialiasing'] else 1,
266 enable_shadows
=int(self
._options
['settings']['shadows']))
267 debug(f
'msaa: {self._pipeline.msaa_samples}')
268 debug(f
'shadows: {self._pipeline.enable_shadows}')
269 render
.setAntialias(AntialiasAttrib
.MAuto
)
270 self
.base
.set_background_color(0, 0, 0, 1)
271 self
.base
.disable_mouse()
272 if self
._options
['development']['show_buffers']:
273 base
.bufferViewer
.toggleEnable()
274 if self
._options
['development']['fps']:
275 base
.set_frame_rate_meter(True)
276 #self.base.accept('window-event', self._on_win_evt)
277 self
.base
.accept('aspectRatioChanged', self
._on
_aspect
_ratio
_changed
)
278 if self
._options
['development']['mouse_coords']:
279 coords_txt
= OnscreenText(
280 '', parent
=base
.a2dTopRight
, scale
=0.04,
281 pos
=(-.03, -.06), fg
=(.9, .9, .9, 1), align
=TextNode
.A_right
)
282 def update_coords(task
):
283 txt
= '%s %s' % (int(base
.win
.get_pointer(0).x
),
284 int(base
.win
.get_pointer(0).y
))
285 coords_txt
['text'] = txt
287 taskMgr
.add(update_coords
, 'update_coords')
289 def _set_physics(self
):
290 if self
._options
['development']['physics_debug']:
291 debug_node
= BulletDebugNode('Debug')
292 debug_node
.show_wireframe(True)
293 debug_node
.show_constraints(True)
294 debug_node
.show_bounding_boxes(True)
295 debug_node
.show_normals(True)
296 self
._debug
_np
= render
.attach_new_node(debug_node
)
297 self
._debug
_np
.show()
298 self
.world
= BulletWorld()
299 self
.world
.set_gravity((0, 0, -9.81))
300 if self
._options
['development']['physics_debug']:
301 self
.world
.set_debug_node(self
._debug
_np
.node())
303 dt
= globalClock
.get_dt()
304 self
.world
.do_physics(dt
, 10, 1/180)
306 self
._phys
_tsk
= taskMgr
.add(update
, 'update')
308 def _unset_physics(self
):
309 if self
._options
['development']['physics_debug']:
310 self
._debug
_np
.remove_node()
312 taskMgr
.remove(self
._phys
_tsk
)
314 def _on_aspect_ratio_changed(self
):
315 if self
._fsm
.state
== 'Scene':
316 self
._scene
.on_aspect_ratio_changed()
318 def __assert_fps(self
, task
):
319 if len(self
.__fps
_lst
) > 3:
320 self
.__fps
_lst
.pop(0)
321 self
.__fps
_lst
+= [globalClock
.average_frame_rate
]
322 if len(self
.__fps
_lst
) == 4:
323 fps_threshold
= 55 if cpu_count() >= 4 and node() != 'localhost.localdomain' else 10 # i.e. it is the builder machine
324 assert not all(fps
< fps_threshold
for fps
in self
.__fps
_lst
), 'low fps %s' % self
.__fps
_lst