2 from logging
import info
3 from os
.path
import exists
, dirname
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
.DirectObject
import DirectObject
11 from direct
.task
.Task
import Task
12 # from gltf import patch_loader
15 # class LibShowBase(ShowBase): pass
18 class LibP3d(DirectObject
):
23 DirectObject
.__init
__(self
)
24 self
.__end
_cb
= self
.__notify
= None
25 self
.__logged
_keys
= {}
28 def runtime(): return not exists('main.py')
32 loadPrcFileData('', 'notify-level-ya2 info')
33 # loadPrcFileData('', 'gl-version 3 2')
38 if sys
.platform
== 'win32' and not exists(exists(home
+ '/.wine/')):
39 if path
.startswith('/'):
40 path
= path
[1] + ':\\' + path
[3:]
41 path
= path
.replace('/', '\\')
45 def p3dpath(path
): return Filename
.fromOsSpecific(path
)
48 def last_frame_dt(self
):
49 return globalClock
.get_dt()
52 def build_version(self
):
53 appimg_mnt
= glob('/tmp/.mount_Yocto*')
56 # appimg_mnt[0] + '/usr/bin/appimage_version.txt') as fver:
57 with
open(self
.curr_path
+ '/assets/bld_version.txt') as fver
:
58 return fver
.read().strip()
60 with
open(self
.curr_path
+ '/assets/bld_version.txt') as fver
:
61 return fver
.read().strip()
62 except FileNotFoundError
:
63 info(self
.curr_path
+ '/assets/bld_version.txt')
67 def is_appimage(self
):
68 par_path
= str(Path(__file__
).parent
.absolute())
69 is_appimage
= par_path
.startswith('/tmp/.mount_Yocto')
70 return is_appimage
and par_path
.endswith('/usr/bin')
74 if sys
.platform
== 'darwin':
75 return dirname(__file__
) + '/../Resources/'
76 # return dirname(__file__)
77 par_path
= str(Path(__file__
).parent
.absolute())
79 return str(Path(par_path
).absolute())
80 is_snap
= par_path
.startswith('/snap/')
81 is_snap
= is_snap
and par_path
.endswith('/x1')
83 return str(Path(par_path
).absolute())
85 curr_path
= dirname(__file__
)
86 info('current path: %s' % curr_path
)
91 return messenger
.send(msg
)
94 def do_later(time
, meth
, args
=None):
96 return taskMgr
.doMethodLater(
97 time
, lambda meth
, args
: meth(*args
), meth
.__name
__, [meth
, args
])
100 def add_task(mth
, priority
=0):
101 return taskMgr
.add(mth
, mth
.__name
__, priority
)
104 def remove_task(tsk
):
107 # def init(self, green=(.2, .8, .2, 1), red=(.8, .2, .2, 1), end_cb=None):
109 # base.disableMouse()
110 # #patch_loader(base.loader)
111 # self.__end_cb = end_cb
113 # self.__init_fonts(green, red)
115 # self.accept('aspectRatioChanged', self.on_aspect_ratio_changed)
120 base
.a2dTopQuarter
= base
.aspect2d
.attachNewNode('a2dTopQuarter')
121 base
.a2dTopQuarter
.set_pos(base
.a2dLeft
/ 2, 0, base
.a2dTop
)
122 base
.a2dTopThirdQuarter
= \
123 base
.aspect2d
.attachNewNode('a2dTopThirdQuarter')
124 base
.a2dTopThirdQuarter
.set_pos(base
.a2dRight
/ 2, 0, base
.a2dTop
)
125 base
.a2dCenterQuarter
= base
.aspect2d
.attachNewNode('a2dCenterQuarter')
126 base
.a2dCenterQuarter
.set_pos(base
.a2dLeft
/ 2, 0, 0)
127 base
.a2dCenterThirdQuarter
= \
128 base
.aspect2d
.attachNewNode('a2dCenterThirdQuarter')
129 base
.a2dCenterThirdQuarter
.set_pos(base
.a2dRight
/ 2, 0, 0)
130 base
.a2dBottomQuarter
= base
.aspect2d
.attachNewNode('a2dBottomQuarter')
131 base
.a2dBottomQuarter
.set_pos(base
.a2dLeft
/ 2, 0, base
.a2dBottom
)
132 base
.a2dBottomThirdQuarter
= \
133 base
.aspect2d
.attachNewNode('a2dBottomThirdQuarter')
134 base
.a2dBottomThirdQuarter
.set_pos(
135 base
.a2dRight
/ 2, 0, base
.a2dBottom
)
138 def on_aspect_ratio_changed():
140 base
.a2dTopQuarter
.set_pos(base
.a2dLeft
/ 2, 0, base
.a2dTop
)
141 base
.a2dTopThirdQuarter
.set_pos(base
.a2dRight
/ 2, 0, base
.a2dTop
)
142 base
.a2dBottomQuarter
.set_pos(base
.a2dLeft
/ 2, 0, base
.a2dBottom
)
143 base
.a2dBottomThirdQuarter
.set_pos(
144 base
.a2dRight
/ 2, 0, base
.a2dBottom
)
147 def has_window(self
):
148 return bool(base
.win
)
151 def resolution(self
):
152 if not isinstance(base
.win
, GraphicsWindow
):
154 win_prop
= base
.win
.get_properties()
155 return win_prop
.get_x_size(), win_prop
.get_y_size()
158 def resolutions(self
):
159 d_i
= base
.pipe
.get_display_information()
162 return d_i
.get_display_mode_width(idx
), \
163 d_i
.get_display_mode_height(idx
)
164 ret
= [res(idx
) for idx
in range(d_i
.get_total_display_modes())]
165 return ret
if ret
else [self
.resolution
]
168 def toggle_fullscreen():
169 props
= WindowProperties()
170 props
.set_fullscreen(not base
.win
.is_fullscreen())
171 base
.win
.request_properties(props
)
174 def set_resolution(res
, fullscreen
=None):
175 props
= WindowProperties()
178 props
.set_fullscreen(True)
179 if isinstance(base
.win
, GraphicsWindow
):
180 base
.win
.request_properties(props
)
182 def __init_win(self
):
183 if base
.win
and isinstance(base
.win
, GraphicsWindow
):
184 base
.win
.set_close_request_event('window-closed')
186 self
.accept('window-closed', self
.__on
_end
)
189 def __init_fonts(green
=(.2, .8, .2, 1), red
=(.8, .2, .2, 1)):
190 tp_mgr
= TextPropertiesManager
.get_global_ptr()
191 for namecol
, col
in zip(['green', 'red'], [green
, red
]):
192 props
= TextProperties()
193 props
.set_text_color(col
)
194 tp_mgr
.set_properties(namecol
, props
)
195 for namesize
, col
in zip(['small', 'smaller'], [.46, .72]):
196 props
= TextProperties()
197 props
.set_text_scale(.46)
198 tp_mgr
.set_properties(namesize
, props
)
199 tp_italic
= TextProperties()
200 tp_italic
.set_slant(.2)
201 tp_mgr
.set_properties('italic', tp_italic
)
204 base
.closeWindow(base
.win
)
210 def load_font(filepath
, outline
=True):
211 font
= base
.loader
.loadFont(filepath
)
212 font
.set_pixels_per_unit(60)
213 font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
215 font
.set_outline((0, 0, 0, 1), .8, .2)
219 def log(msg
): print(msg
)
222 def version(self
): return PandaSystem
.get_version_string()
225 def lib_commit(self
): return PandaSystem
.get_git_commit()
228 def phys_version(self
): return get_bullet_version()
231 def user_appdata_dir(self
): return Filename
.get_user_appdata_directory()
234 def driver_vendor(self
):
235 return base
.win
.get_gsg().get_driver_vendor()
238 def driver_renderer(self
):
239 return base
.win
.get_gsg().get_driver_renderer()
242 def driver_shader_version_major(self
):
243 return base
.win
.get_gsg().get_driver_shader_version_major()
246 def driver_shader_version_minor(self
):
247 return base
.win
.get_gsg().get_driver_shader_version_minor()
250 def driver_version(self
):
251 return base
.win
.get_gsg().get_driver_version()
254 def driver_version_major(self
):
255 return base
.win
.get_gsg().get_driver_version_major()
258 def driver_version_minor(self
):
259 return base
.win
.get_gsg().get_driver_version_minor()
262 def fullscreen(self
):
263 if isinstance(base
.win
, GraphicsWindow
):
264 return base
.win
.get_properties().get_fullscreen()
268 return base
.sfxManagerList
[0].get_volume()
271 def volume(self
, vol
):
272 base
.sfxManagerList
[0].set_volume(vol
)
276 mwn
= base
.mouseWatcherNode
279 if not mwn
.hasMouse():
281 return mwn
.get_mouse_x(), mwn
.get_mouse_y()
284 def aspect_ratio(self
):
285 return base
.getAspectRatio()
289 pos
= wdg
.get_pos(pixel2d
)
290 return int(round(pos
[0])), int(round(-pos
[2]))
293 def set_icon(filename
):
294 props
= WindowProperties()
295 props
.set_icon_filename(filename
)
296 if isinstance(base
.win
, GraphicsWindow
):
297 base
.win
.requestProperties(props
)
300 def __set_std_cursor(show
):
301 props
= WindowProperties()
302 props
.set_cursor_hidden(not show
)
303 if isinstance(base
.win
, GraphicsWindow
):
304 base
.win
.requestProperties(props
)
307 def show_std_cursor(): LibP3d
.__set
_std
_cursor
(True)
310 def hide_std_cursor(): LibP3d
.__set
_std
_cursor
(False)
313 def find_geoms(model
, name
): # no need to be cached
314 geoms
= model
.node
.find_all_matches('**/+GeomNode')
317 return geom
.get_name().startswith(name
)
318 named_geoms
= [geom
for geom
in geoms
if is_nm(geom
)]
319 return [ng
for ng
in named_geoms
if name
in ng
.get_name()]
322 def load_sfx(filepath
, loop
=False):
323 sfx
= loader
.loadSfx(filepath
)
327 def remap_code(self
, key
):
328 kmap
= base
.win
.get_keyboard_map()
329 for i
in range(kmap
.get_num_buttons()):
330 if key
.lower() == kmap
.get_mapped_button_label(i
).lower():
332 'code mapping %s to key %s' %
333 (key
, kmap
.get_mapped_button(i
)), key
,
334 kmap
.get_mapped_button(i
))
335 return kmap
.get_mapped_button(i
)
336 for i
in range(kmap
.get_num_buttons()):
337 if key
.lower() == kmap
.get_mapped_button(i
).get_name().lower():
339 'code mapping %s to key %s' %
340 (key
, kmap
.get_mapped_button(i
)), key
,
341 kmap
.get_mapped_button(i
))
342 return kmap
.get_mapped_button(i
)
343 self
.__log
_key
('not found a code mapping for %s' %
344 key
, key
, 'not_found')
347 def remap_str(self
, key
):
348 if not base
.win
: # when launched with --version
350 # if isinstance(base.win, GraphicsBuffer):
352 kmap
= base
.win
.get_keyboard_map()
353 for i
in range(kmap
.get_num_buttons()):
354 if str(key
).lower() == kmap
.get_mapped_button_label(i
).lower():
356 'string mapping %s to key %s' %
357 (key
, kmap
.get_mapped_button(i
).get_name()), key
,
358 kmap
.get_mapped_button(i
).get_name())
359 return kmap
.get_mapped_button(i
).get_name()
360 for i
in range(kmap
.get_num_buttons()):
361 if key
.lower() == kmap
.get_mapped_button(i
).get_name().lower():
363 'string mapping %s to key %s' %
364 (key
, kmap
.get_mapped_button(i
).get_name()), key
,
365 kmap
.get_mapped_button(i
).get_name())
366 return kmap
.get_mapped_button(i
).get_name()
367 self
.__log
_key
('not found a string mapping for %s' %
368 key
, key
, kmap
.get_mapped_button(i
).get_name())
371 def __log_key(self
, msg
, key1
, key2
):
372 if key1
in self
.__logged
_keys
and self
.__logged
_keys
[key1
] == key2
:
374 self
.__logged
_keys
[key1
] = key2
377 def destroy(self
): pass