ya2 · news · projects · code · about

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