ya2 · news · projects · code · about

enforced resolution
[pmachines.git] / game / app.py
CommitLineData
8ee66edd 1import argparse
4894bb48 2import simplepbr
420ce99a 3#import gltf
63e7aeb2 4from glob import glob
a2a89363
FC
5from importlib import import_module
6from inspect import isclass
63e7aeb2 7from sys import platform, argv, exit
94a18c21 8from logging import info, debug
8ee66edd
FC
9from os.path import exists
10from os import makedirs
6fff1464 11from panda3d.core import Filename, load_prc_file_data, AntialiasAttrib, \
882c058d 12 Texture, WindowProperties, LVector2i, TextNode
1be87278 13from panda3d.bullet import BulletWorld, BulletDebugNode
8ee66edd 14from direct.showbase.ShowBase import ShowBase
882c058d 15from direct.gui.OnscreenText import OnscreenText
5964572b 16from direct.fsm.FSM import FSM
a0462193
FC
17from game.music import MusicMgr
18from game.items.background import Background
19from game.menu import Menu
20from game.scene import Scene
6168d0c2
FC
21from game.scenes.scene_basketball import SceneBasketBall
22from game.scenes.scene_box import SceneBox
23from game.scenes.scene_domino_box_basketball import SceneDominoBoxBasketball
24from game.scenes.scene_domino_box import SceneDominoBox
25from game.scenes.scene_domino import SceneDomino
26from game.scenes.scene_teeter_domino_box_basketball import SceneTeeterDominoBoxBasketball
27from game.scenes.scene_teeter_tooter import SceneTeeterTooter
9ba5488b
FC
28from lib.dictfile import DctFile
29from lib.lib.p3d.p3d import LibP3d
2aaa10d3 30from lib.engine.lang import LangMgr
8ce16d6c 31from lib.engine.log import LogMgr
edeef6f9 32from lib.engine.functional import FunctionalTest
8ee66edd
FC
33
34
5964572b
FC
35class MainFsm(FSM):
36
37 def __init__(self, pmachines):
38 super().__init__('Main FSM')
39 self._pmachines = pmachines
40
41 def enterMenu(self):
42 self._pmachines.on_menu_enter()
43
44 def exitMenu(self):
45 self._pmachines.on_menu_exit()
46
8c9bf90e
FC
47 def enterScene(self, cls):
48 self._pmachines.on_scene_enter(cls)
5964572b
FC
49
50 def exitScene(self):
51 self._pmachines.on_scene_exit()
52
53
c8035584 54class PmachinesApp:
8ee66edd 55
6168d0c2
FC
56 scenes = [
57 SceneDomino,
58 SceneBox,
59 SceneDominoBox,
60 SceneBasketBall,
61 SceneDominoBoxBasketball,
62 SceneTeeterTooter,
63 SceneTeeterDominoBoxBasketball]
64
8ee66edd 65 def __init__(self):
8ee66edd
FC
66 info('platform: %s' % platform)
67 info('exists main.py: %s' % exists('main.py'))
e982cdde 68 self._args = args = self._parse_args()
d18f757d
FC
69 self._configure(args)
70 self.base = ShowBase()
8ce16d6c 71 self._pipeline = None
8ee66edd
FC
72 self.updating = args.update
73 self.version = args.version
8ce16d6c
FC
74 self.log_mgr = LogMgr.init_cls()(self)
75 self._prepare_window(args)
8ee66edd
FC
76 if args.update:
77 return
361d3942 78 if args.functional_test:
edeef6f9 79 self._options['settings']['volume'] = 0
e1e44d5c 80 self._music = MusicMgr(self._options['settings']['volume'])
a0b33e12 81 self.lang_mgr = LangMgr(self._options['settings']['language'],
2aaa10d3
FC
82 'pmachines',
83 'assets/locale/')
5964572b 84 self._fsm = MainFsm(self)
a747111f 85 if args.screenshot:
6168d0c2
FC
86 cls = [cls for cls in self.scenes if cls.__name__ == args.screenshot][0]
87 scene = cls(BulletWorld(), None, True, False, lambda: None, self.scenes)
a747111f
FC
88 scene.screenshot()
89 scene.destroy()
63e7aeb2
FC
90 exit()
91 elif self._options['development']['auto_start']:
a0462193 92 mod_name = 'game.scenes.scene_' + self._options['development']['auto_start']
a2a89363
FC
93 for member in import_module(mod_name).__dict__.values():
94 if isclass(member) and issubclass(member, Scene) and \
95 member != Scene:
96 cls = member
97 self._fsm.demand('Scene', cls)
98 else:
99 self._fsm.demand('Menu')
edeef6f9 100 if args.functional_test or args.functional_ref:
361d3942 101 FunctionalTest(args.functional_ref)
5964572b
FC
102
103 def on_menu_enter(self):
104 self._menu_bg = Background()
a9aba267
FC
105 self._menu = Menu(
106 self._fsm, self.lang_mgr, self._options, self._music,
e982cdde 107 self._pipeline, self.scenes, self._args.functional_test or self._args.functional_ref)
5964572b
FC
108
109 def on_home(self):
110 self._fsm.demand('Menu')
111
112 def on_menu_exit(self):
113 self._menu_bg.destroy()
4071c6d8 114 self._menu.destroy()
5964572b 115
8c9bf90e 116 def on_scene_enter(self, cls):
1be87278 117 self._set_physics()
8c9bf90e 118 self._scene = cls(
e669403e 119 self.world, self.on_home,
31237524 120 self._options['development']['auto_close_instructions'],
9914cfc9 121 self._options['development']['debug_items'],
6168d0c2
FC
122 self.reload,
123 self.scenes)
5964572b
FC
124
125 def on_scene_exit(self):
126 self._unset_physics()
127 self._scene.destroy()
8ee66edd 128
9914cfc9
FC
129 def reload(self, cls):
130 self._fsm.demand('Scene', cls)
131
d18f757d 132 def _configure(self, args):
8ee66edd 133 load_prc_file_data('', 'window-title pmachines')
4894bb48 134 load_prc_file_data('', 'framebuffer-srgb true')
a9e8696e 135 load_prc_file_data('', 'sync-video true')
d982c0a5 136 if args.functional_test or args.functional_ref:
addec9c9 137 load_prc_file_data('', 'win-size 1360 768')
d982c0a5 138 # otherwise it is not centered in exwm
9914cfc9
FC
139 # load_prc_file_data('', 'threading-model Cull/Draw')
140 # it freezes when you go to the next scene
a747111f 141 if args.screenshot:
d18f757d
FC
142 load_prc_file_data('', 'window-type offscreen')
143 load_prc_file_data('', 'audio-library-name null')
8ee66edd
FC
144
145 def _parse_args(self):
146 parser = argparse.ArgumentParser()
147 parser.add_argument('--update', action='store_true')
148 parser.add_argument('--version', action='store_true')
9ba5488b 149 parser.add_argument('--optfile')
a747111f 150 parser.add_argument('--screenshot')
361d3942 151 parser.add_argument('--functional-test', action='store_true')
edeef6f9 152 parser.add_argument('--functional-ref', action='store_true')
8ee66edd
FC
153 cmd_line = [arg for arg in iter(argv[1:]) if not arg.startswith('-psn_')]
154 args = parser.parse_args(cmd_line)
155 return args
156
9ba5488b 157 def _prepare_window(self, args):
8ee66edd
FC
158 data_path = ''
159 if (platform.startswith('win') or platform.startswith('linux')) and (
160 not exists('main.py') or __file__.startswith('/app/bin/')):
161 # it is the deployed version for windows
162 data_path = str(Filename.get_user_appdata_directory()) + '/pmachines'
163 home = '/home/flavio' # we must force this for wine
164 if data_path.startswith('/c/users/') and exists(home + '/.wine/'):
165 data_path = home + '/.wine/drive_' + data_path[1:]
166 info('creating dirs: %s' % data_path)
167 makedirs(data_path, exist_ok=True)
9ba5488b
FC
168 optfile = args.optfile if args.optfile else 'options.ini'
169 info('data path: %s' % data_path)
170 info('option file: %s' % optfile)
171 info('fixed path: %s' % LibP3d.fixpath(data_path + '/' + optfile))
172 default_opt = {
173 'settings': {
a0b33e12 174 'volume': 1,
6fff1464
FC
175 'language': 'en',
176 'fullscreen': 1,
a9aba267 177 'resolution': '',
5fdf77d0
FC
178 'antialiasing': 1,
179 'shadows': 1},
9ba5488b 180 'development': {
a5dc83f4 181 'simplepbr': 1,
9ba5488b 182 'verbose_log': 0,
e669403e
FC
183 'physics_debug': 0,
184 'auto_start': 0,
185 'auto_close_instructions': 0,
31237524 186 'show_buffers': 0,
b41381b2 187 'debug_items': 0,
882c058d 188 'mouse_coords': 0,
b41381b2 189 'fps': 0}}
9ba5488b
FC
190 opt_path = LibP3d.fixpath(data_path + '/' + optfile) if data_path else optfile
191 opt_exists = exists(opt_path)
192 self._options = DctFile(
193 LibP3d.fixpath(data_path + '/' + optfile) if data_path else optfile,
194 default_opt)
195 if not opt_exists:
196 self._options.store()
6fff1464
FC
197 res = self._options['settings']['resolution']
198 if res:
199 res = LVector2i(*[int(_res) for _res in res.split('x')])
200 else:
201 d_i = base.pipe.get_display_information()
202 def _res(idx):
203 return d_i.get_display_mode_width(idx), \
204 d_i.get_display_mode_height(idx)
205 resolutions = [
206 _res(idx) for idx in range(d_i.get_total_display_modes())]
207 res = sorted(resolutions)[-1]
edeef6f9 208 fullscreen = self._options['settings']['fullscreen']
d982c0a5 209 props = WindowProperties()
edeef6f9 210 if args.functional_test or args.functional_ref:
edeef6f9 211 fullscreen = False
d982c0a5
FC
212 else:
213 props.set_size(res)
edeef6f9 214 props.set_fullscreen(fullscreen)
7e487769 215 props.set_icon_filename('assets/images/icon/pmachines.ico')
8ce16d6c 216 if not args.screenshot and not self.version:
d18f757d 217 base.win.request_properties(props)
420ce99a 218 #gltf.patch_loader(base.loader)
8ce16d6c 219 if self._options['development']['simplepbr'] and not self.version:
a9aba267 220 self._pipeline = simplepbr.init(
a5dc83f4
FC
221 use_normal_maps=True,
222 use_emission_maps=False,
a9aba267 223 use_occlusion_maps=True,
5fdf77d0
FC
224 msaa_samples=4 if self._options['settings']['antialiasing'] else 1,
225 enable_shadows=int(self._options['settings']['shadows']))
94a18c21
FC
226 debug(f'msaa: {self._pipeline.msaa_samples}')
227 debug(f'shadows: {self._pipeline.enable_shadows}')
4894bb48 228 render.setAntialias(AntialiasAttrib.MAuto)
1be87278
FC
229 self.base.set_background_color(0, 0, 0, 1)
230 self.base.disable_mouse()
e669403e
FC
231 if self._options['development']['show_buffers']:
232 base.bufferViewer.toggleEnable()
b41381b2
FC
233 if self._options['development']['fps']:
234 base.set_frame_rate_meter(True)
651713a9
FC
235 #self.base.accept('window-event', self._on_win_evt)
236 self.base.accept('aspectRatioChanged', self._on_aspect_ratio_changed)
882c058d
FC
237 if self._options['development']['mouse_coords']:
238 coords_txt = OnscreenText(
239 '', parent=base.a2dTopRight, scale=0.04,
240 pos=(-.03, -.06), fg=(.9, .9, .9, 1), align=TextNode.A_right)
241 def update_coords(task):
242 txt = '%s %s' % (int(base.win.get_pointer(0).x),
243 int(base.win.get_pointer(0).y))
244 coords_txt['text'] = txt
245 return task.cont
246 taskMgr.add(update_coords, 'update_coords')
247
1be87278 248 def _set_physics(self):
9ba5488b
FC
249 if self._options['development']['physics_debug']:
250 debug_node = BulletDebugNode('Debug')
251 debug_node.show_wireframe(True)
252 debug_node.show_constraints(True)
253 debug_node.show_bounding_boxes(True)
254 debug_node.show_normals(True)
5964572b
FC
255 self._debug_np = render.attach_new_node(debug_node)
256 self._debug_np.show()
1be87278
FC
257 self.world = BulletWorld()
258 self.world.set_gravity((0, 0, -9.81))
9ba5488b 259 if self._options['development']['physics_debug']:
5964572b 260 self.world.set_debug_node(self._debug_np.node())
1be87278
FC
261 def update(task):
262 dt = globalClock.get_dt()
0625cf49 263 self.world.do_physics(dt, 10, 1/180)
1be87278 264 return task.cont
5964572b
FC
265 self._phys_tsk = taskMgr.add(update, 'update')
266
267 def _unset_physics(self):
268 if self._options['development']['physics_debug']:
269 self._debug_np.remove_node()
270 self.world = None
271 taskMgr.remove(self._phys_tsk)
651713a9
FC
272
273 def _on_aspect_ratio_changed(self):
5964572b
FC
274 if self._fsm.state == 'Scene':
275 self._scene.on_aspect_ratio_changed()