Commit | Line | Data |
---|---|---|
8ee66edd FC |
1 | import sys |
2 | from logging import info | |
3 | from os.path import exists, dirname | |
4 | from os import getcwd, _exit | |
5 | from glob import glob | |
6 | from pathlib import Path | |
7 | from panda3d.core import loadPrcFileData, Texture, TextPropertiesManager, \ | |
8 | TextProperties, PandaSystem, Filename, WindowProperties, GraphicsWindow | |
9 | from panda3d.bullet import get_bullet_version | |
10 | from direct.showbase.ShowBase import ShowBase | |
11 | from direct.showbase.DirectObject import DirectObject | |
12 | from direct.task.Task import Task | |
13 | #from gltf import patch_loader | |
14 | ||
15 | ||
cb700bcc | 16 | # class LibShowBase(ShowBase): pass |
8ee66edd FC |
17 | |
18 | ||
19 | class LibP3d(DirectObject): | |
20 | ||
21 | task_cont = Task.cont | |
22 | ||
23 | def __init__(self): | |
24 | DirectObject.__init__(self) | |
25 | self.__end_cb = self.__notify = None | |
26 | self.__logged_keys = {} | |
27 | ||
28 | @staticmethod | |
29 | def runtime(): return not exists('main.py') | |
30 | ||
31 | @staticmethod | |
32 | def configure(): | |
33 | loadPrcFileData('', 'notify-level-ya2 info') | |
34 | # loadPrcFileData('', 'gl-version 3 2') | |
35 | ||
36 | @staticmethod | |
37 | def fixpath(path): | |
38 | home = '/home/flavio' | |
39 | if sys.platform == 'win32' and not exists(exists(home + '/.wine/')): | |
40 | if path.startswith('/'): path = path[1] + ':\\' + path[3:] | |
41 | path = path.replace('/', '\\') | |
42 | return path | |
43 | ||
44 | @staticmethod | |
45 | def p3dpath(path): return Filename.fromOsSpecific(path) | |
46 | ||
47 | @property | |
48 | def last_frame_dt(self): return globalClock.get_dt() | |
49 | ||
50 | @property | |
51 | def build_version(self): | |
52 | appimg_mnt = glob('/tmp/.mount_Yocto*') | |
53 | if appimg_mnt: | |
54 | #with open(appimg_mnt[0] + '/usr/bin/appimage_version.txt') as fver: | |
55 | with open(self.curr_path + '/assets/bld_version.txt') as fver: | |
56 | return fver.read().strip() | |
57 | try: | |
58 | with open(self.curr_path + '/assets/bld_version.txt') as fver: | |
59 | return fver.read().strip() | |
60 | except FileNotFoundError: | |
61 | info(self.curr_path + '/assets/bld_version.txt') | |
62 | return 'notfound' | |
63 | ||
64 | @property | |
65 | def is_appimage(self): | |
66 | par_path = str(Path(__file__).parent.absolute()) | |
67 | is_appimage = par_path.startswith('/tmp/.mount_Yocto') | |
68 | return is_appimage and par_path.endswith('/usr/bin') | |
69 | ||
70 | @property | |
71 | def curr_path(self): | |
72 | if sys.platform == 'darwin': | |
73 | return dirname(__file__) + '/../Resources/' | |
74 | # return dirname(__file__) | |
75 | par_path = str(Path(__file__).parent.absolute()) | |
76 | if self.is_appimage: | |
77 | return str(Path(par_path).absolute()) | |
78 | is_snap = par_path.startswith('/snap/') | |
79 | is_snap = is_snap and par_path.endswith('/x1') | |
80 | if is_snap: | |
81 | return str(Path(par_path).absolute()) | |
82 | #return getcwd() | |
83 | curr_path = dirname(__file__) | |
84 | info('current path: %s' % curr_path) | |
85 | return curr_path | |
86 | ||
87 | @staticmethod | |
88 | def send(msg): return messenger.send(msg) | |
89 | ||
90 | @staticmethod | |
91 | def do_later(time, meth, args=None): | |
92 | args = args or [] | |
93 | return taskMgr.doMethodLater( | |
94 | time, lambda meth, args: meth(*args), meth.__name__, [meth, args]) | |
95 | ||
96 | @staticmethod | |
97 | def add_task(mth, priority=0): | |
98 | return taskMgr.add(mth, mth.__name__, priority) | |
99 | ||
100 | @staticmethod | |
101 | def remove_task(tsk): taskMgr.remove(tsk) | |
102 | ||
103 | def init(self, green=(.2, .8, .2, 1), red=(.8, .2, .2, 1), end_cb=None): | |
104 | LibShowBase() | |
105 | base.disableMouse() | |
106 | #patch_loader(base.loader) | |
107 | self.__end_cb = end_cb | |
108 | self.__init_win() | |
109 | self.__init_fonts(green, red) | |
110 | self.__set_roots() | |
111 | self.accept('aspectRatioChanged', self.on_aspect_ratio_changed) | |
112 | ||
113 | @staticmethod | |
114 | def __set_roots(): | |
115 | base.a2dTopQuarter = base.aspect2d.attachNewNode('a2dTopQuarter') | |
116 | base.a2dTopQuarter.set_pos(base.a2dLeft / 2, 0, base.a2dTop) | |
117 | base.a2dTopThirdQuarter = \ | |
118 | base.aspect2d.attachNewNode('a2dTopThirdQuarter') | |
119 | base.a2dTopThirdQuarter.set_pos(base.a2dRight / 2, 0, base.a2dTop) | |
120 | base.a2dCenterQuarter = base.aspect2d.attachNewNode('a2dCenterQuarter') | |
121 | base.a2dCenterQuarter.set_pos(base.a2dLeft / 2, 0, 0) | |
122 | base.a2dCenterThirdQuarter = \ | |
123 | base.aspect2d.attachNewNode('a2dCenterThirdQuarter') | |
124 | base.a2dCenterThirdQuarter.set_pos(base.a2dRight / 2, 0, 0) | |
125 | base.a2dBottomQuarter = base.aspect2d.attachNewNode('a2dBottomQuarter') | |
126 | base.a2dBottomQuarter.set_pos(base.a2dLeft / 2, 0, base.a2dBottom) | |
127 | base.a2dBottomThirdQuarter = \ | |
128 | base.aspect2d.attachNewNode('a2dBottomThirdQuarter') | |
129 | base.a2dBottomThirdQuarter.set_pos( | |
130 | base.a2dRight / 2, 0, base.a2dBottom) | |
131 | ||
132 | @staticmethod | |
133 | def on_aspect_ratio_changed(): | |
134 | base.a2dTopQuarter.set_pos(base.a2dLeft / 2, 0, base.a2dTop) | |
135 | base.a2dTopThirdQuarter.set_pos(base.a2dRight / 2, 0, base.a2dTop) | |
136 | base.a2dBottomQuarter.set_pos(base.a2dLeft / 2, 0, base.a2dBottom) | |
137 | base.a2dBottomThirdQuarter.set_pos( | |
138 | base.a2dRight / 2, 0, base.a2dBottom) | |
139 | ||
140 | @property | |
141 | def has_window(self): return bool(base.win) | |
142 | ||
143 | @property | |
144 | def resolution(self): | |
145 | if not isinstance(base.win, GraphicsWindow): | |
146 | return 800, 600 | |
147 | win_prop = base.win.get_properties() | |
148 | return win_prop.get_x_size(), win_prop.get_y_size() | |
149 | ||
150 | @property | |
151 | def resolutions(self): | |
152 | d_i = base.pipe.get_display_information() | |
153 | ||
154 | def res(idx): | |
155 | return d_i.get_display_mode_width(idx), \ | |
156 | d_i.get_display_mode_height(idx) | |
157 | ret = [res(idx) for idx in range(d_i.get_total_display_modes())] | |
158 | return ret if ret else [self.resolution] | |
159 | ||
160 | @staticmethod | |
161 | def toggle_fullscreen(): | |
162 | props = WindowProperties() | |
163 | props.set_fullscreen(not base.win.is_fullscreen()) | |
164 | base.win.request_properties(props) | |
165 | ||
166 | @staticmethod | |
167 | def set_resolution(res, fullscreen=None): | |
168 | props = WindowProperties() | |
169 | props.set_size(res) | |
170 | if fullscreen: props.set_fullscreen(True) | |
171 | if isinstance(base.win, GraphicsWindow): | |
172 | base.win.request_properties(props) | |
173 | ||
174 | def __init_win(self): | |
175 | if base.win and isinstance(base.win, GraphicsWindow): | |
176 | base.win.set_close_request_event('window-closed') | |
177 | # not headless | |
178 | self.accept('window-closed', self.__on_end) | |
179 | ||
180 | @staticmethod | |
181 | def __init_fonts(green=(.2, .8, .2, 1), red=(.8, .2, .2, 1)): | |
182 | tp_mgr = TextPropertiesManager.get_global_ptr() | |
183 | for namecol, col in zip(['green', 'red'], [green, red]): | |
184 | props = TextProperties() | |
185 | props.set_text_color(col) | |
186 | tp_mgr.set_properties(namecol, props) | |
187 | for namesize, col in zip(['small', 'smaller'], [.46, .72]): | |
188 | props = TextProperties() | |
189 | props.set_text_scale(.46) | |
190 | tp_mgr.set_properties(namesize, props) | |
191 | tp_italic = TextProperties() | |
192 | tp_italic.set_slant(.2) | |
193 | tp_mgr.set_properties('italic', tp_italic) | |
194 | ||
195 | def __on_end(self): | |
196 | base.closeWindow(base.win) | |
197 | if self.__end_cb: self.__end_cb() | |
198 | _exit(0) | |
199 | ||
200 | @staticmethod | |
201 | def load_font(filepath, outline=True): | |
202 | font = base.loader.loadFont(filepath) | |
203 | font.set_pixels_per_unit(60) | |
204 | font.set_minfilter(Texture.FTLinearMipmapLinear) | |
205 | if outline: font.set_outline((0, 0, 0, 1), .8, .2) | |
206 | return font | |
207 | ||
208 | @staticmethod | |
209 | def log(msg): print(msg) | |
210 | ||
211 | @property | |
212 | def version(self): return PandaSystem.get_version_string() | |
213 | ||
214 | @property | |
215 | def lib_commit(self): return PandaSystem.get_git_commit() | |
216 | ||
217 | @property | |
218 | def phys_version(self): return get_bullet_version() | |
219 | ||
220 | @property | |
221 | def user_appdata_dir(self): return Filename.get_user_appdata_directory() | |
222 | ||
223 | @property | |
224 | def driver_vendor(self): return base.win.get_gsg().get_driver_vendor() | |
225 | ||
226 | @property | |
227 | def driver_renderer(self): return base.win.get_gsg().get_driver_renderer() | |
228 | ||
229 | @property | |
230 | def driver_shader_version_major(self): | |
231 | return base.win.get_gsg().get_driver_shader_version_major() | |
232 | ||
233 | @property | |
234 | def driver_shader_version_minor(self): | |
235 | return base.win.get_gsg().get_driver_shader_version_minor() | |
236 | ||
237 | @property | |
238 | def driver_version(self): return base.win.get_gsg().get_driver_version() | |
239 | ||
240 | @property | |
241 | def driver_version_major(self): | |
242 | return base.win.get_gsg().get_driver_version_major() | |
243 | ||
244 | @property | |
245 | def driver_version_minor(self): | |
246 | return base.win.get_gsg().get_driver_version_minor() | |
247 | ||
248 | @property | |
249 | def fullscreen(self): | |
250 | if isinstance(base.win, GraphicsWindow): | |
251 | return base.win.get_properties().get_fullscreen() | |
252 | ||
253 | @property | |
254 | def volume(self): return base.sfxManagerList[0].get_volume() | |
255 | ||
256 | @volume.setter | |
257 | def volume(self, vol): base.sfxManagerList[0].set_volume(vol) | |
258 | ||
259 | @property | |
260 | def mousepos(self): | |
261 | mwn = base.mouseWatcherNode | |
262 | if not mwn: return 0, 0 | |
263 | if not mwn.hasMouse(): return 0, 0 | |
264 | return mwn.get_mouse_x(), mwn.get_mouse_y() | |
265 | ||
266 | @property | |
267 | def aspect_ratio(self): return base.getAspectRatio() | |
268 | ||
2d1773b1 FC |
269 | @staticmethod |
270 | def wdg_pos(wdg): | |
271 | pos = wdg.get_pos(pixel2d) | |
272 | return int(round(pos[0])), int(round(-pos[2])) | |
273 | ||
8ee66edd FC |
274 | @staticmethod |
275 | def set_icon(filename): | |
276 | props = WindowProperties() | |
277 | props.set_icon_filename(filename) | |
278 | if isinstance(base.win, GraphicsWindow): | |
279 | base.win.requestProperties(props) | |
280 | ||
281 | @staticmethod | |
282 | def __set_std_cursor(show): | |
283 | props = WindowProperties() | |
284 | props.set_cursor_hidden(not show) | |
285 | if isinstance(base.win, GraphicsWindow): | |
286 | base.win.requestProperties(props) | |
287 | ||
288 | @staticmethod | |
289 | def show_std_cursor(): LibP3d.__set_std_cursor(True) | |
290 | ||
291 | @staticmethod | |
292 | def hide_std_cursor(): LibP3d.__set_std_cursor(False) | |
293 | ||
294 | @staticmethod | |
295 | def find_geoms(model, name): # no need to be cached | |
296 | geoms = model.node.find_all_matches('**/+GeomNode') | |
297 | is_nm = lambda geom: geom.get_name().startswith(name) | |
298 | named_geoms = [geom for geom in geoms if is_nm(geom)] | |
299 | return [ng for ng in named_geoms if name in ng.get_name()] | |
300 | ||
301 | @staticmethod | |
302 | def load_sfx(filepath, loop=False): | |
303 | sfx = loader.loadSfx(filepath) | |
304 | sfx.set_loop(loop) | |
305 | return sfx | |
306 | ||
307 | def remap_code(self, key): | |
308 | kmap = base.win.get_keyboard_map() | |
309 | for i in range(kmap.get_num_buttons()): | |
310 | if key.lower() == kmap.get_mapped_button_label(i).lower(): | |
311 | self.__log_key( | |
312 | 'code mapping %s to key %s' % | |
313 | (key, kmap.get_mapped_button(i)), key, | |
314 | kmap.get_mapped_button(i)) | |
315 | return kmap.get_mapped_button(i) | |
316 | for i in range(kmap.get_num_buttons()): | |
317 | if key.lower() == kmap.get_mapped_button(i).get_name().lower(): | |
318 | self.__log_key( | |
319 | 'code mapping %s to key %s' % | |
320 | (key, kmap.get_mapped_button(i)), key, | |
321 | kmap.get_mapped_button(i)) | |
322 | return kmap.get_mapped_button(i) | |
323 | self.__log_key('not found a code mapping for %s' % | |
324 | key, key, 'not_found') | |
325 | return key | |
326 | ||
327 | def remap_str(self, key): | |
328 | if not base.win: # when launched with --version | |
329 | return key | |
330 | #if isinstance(base.win, GraphicsBuffer): | |
331 | # return key | |
332 | kmap = base.win.get_keyboard_map() | |
333 | for i in range(kmap.get_num_buttons()): | |
334 | if str(key).lower() == kmap.get_mapped_button_label(i).lower(): | |
335 | self.__log_key( | |
336 | 'string mapping %s to key %s' % | |
337 | (key, kmap.get_mapped_button(i).get_name()), key, | |
338 | kmap.get_mapped_button(i).get_name()) | |
339 | return kmap.get_mapped_button(i).get_name() | |
340 | for i in range(kmap.get_num_buttons()): | |
341 | if key.lower() == kmap.get_mapped_button(i).get_name().lower(): | |
342 | self.__log_key( | |
343 | 'string mapping %s to key %s' % | |
344 | (key, kmap.get_mapped_button(i).get_name()), key, | |
345 | kmap.get_mapped_button(i).get_name()) | |
346 | return kmap.get_mapped_button(i).get_name() | |
347 | self.__log_key('not found a string mapping for %s' % | |
348 | key, key, kmap.get_mapped_button(i).get_name()) | |
349 | return key | |
350 | ||
351 | def __log_key(self, msg, key1, key2): | |
352 | if key1 in self.__logged_keys and self.__logged_keys[key1] == key2: | |
353 | return | |
354 | self.__logged_keys[key1] = key2 | |
355 | print(msg) | |
356 | ||
357 | def destroy(self): pass |