ya2 · news · projects · code · about

cadcaa857a53ff90c098751d973d978c8f8b5f22
[pmachines.git] / ya2 / utils / gfx.py
1 from panda3d.core import NodePath, Point2, Point3, Texture, LVector3f, LVector2f, TextNode
2 from direct.gui.OnscreenText import OnscreenText
3 from direct.gui.DirectGuiGlobals import ENTER, EXIT
4 from ya2.utils.decorator import Decorator
5
6
7 class GfxTools:
8
9 @staticmethod
10 def build_model(file_path):
11 m = loader.load_model(file_path)
12 m = NodePathDecorator(m) # we can't use mixins with c++ classes
13 return m
14
15 @staticmethod
16 def build_empty_node(name):
17 return NodePathDecorator(NodePath(name))
18
19 @staticmethod
20 def build_node_from_physics(physics_node):
21 return NodePathDecorator(render.attach_new_node(physics_node))
22
23
24 class NodePathDecorator(NodePath, Decorator):
25
26 def set_srgb_textures(self):
27 for t in self.find_all_textures():
28 f = t.get_format()
29 match t.get_format():
30 case Texture.F_rgba | Texture.F_rgbm:
31 f = Texture.F_srgb_alpha
32 case Texture.F_rgb:
33 f = Texture.F_srgb
34 t.set_format(f)
35
36 def pos2d(self):
37 p3d = base.cam.get_relative_point(self, Point3(0, 0, 0))
38 p2d = Point2()
39 return p2d if base.camLens.project(p3d, p2d) else None
40
41 def pos2d_pixel(self):
42 if p2d := self.pos2d():
43 p = _get_pos_relative((p2d[0], 0, p2d[1]), pixel2d)
44 return int(round(p[0])), int(round(-p[2]))
45
46 def pos_as_widget(self):
47 pos = self.get_pos(pixel2d)
48 return int(round(pos[0])), int(round(-pos[2]))
49
50
51 class DirectGuiMixin():
52
53 registered_tooltips = []
54
55 def pos_pixel(self):
56 pos = self.get_pos(pixel2d)
57 return int(round(pos[0])), int(round(-pos[2]))
58
59 def set_tooltip(self, text, font, scale, fg, side='left'):
60 self.__tooltip_txt = text
61 self.__tooltip_font = font
62 self.__tooltip_scale = scale
63 self.__tooltip_fg = fg
64 self.bind(ENTER, self.__on_enter)
65 self.bind(EXIT, self.__on_exit)
66 match side:
67 case 'left':
68 self.__side_delta = (-.08, -.02)
69 self.__align = TextNode.A_right
70 case 'right':
71 self.__side_delta = (.08, -.02)
72 self.__align = TextNode.A_left
73
74 def __on_enter(self, mouse_pos):
75 self.__tooltip_text = OnscreenText(
76 self.__tooltip_txt,
77 pos=(self.get_pos()[0] + self.__side_delta[0], self.get_pos()[2] + self.__side_delta[1]),
78 parent=self.parent,
79 font=self.__tooltip_font,
80 scale=self.__tooltip_scale,
81 fg=self.__tooltip_fg,
82 align=self.__align)
83 DirectGuiMixin.registered_tooltips += [self.__tooltip_text]
84
85 def __on_exit(self, mouse_pos):
86 self.__tooltip_text.destroy()
87
88 @staticmethod
89 def clear_tooltips():
90 # when we'll have proper gui classes with has-a containment
91 # remove this hack: the destroy will destroy the tooltip as well
92 for t in DirectGuiMixin.registered_tooltips:
93 t.destroy()
94 DirectGuiMixin.registered_tooltips = []
95
96
97 class Point(Decorator):
98
99 def screen_coord(self):
100 pos3d = _get_pos_relative(LVector3f(*self), base.cam)
101 pos2d = Point2()
102 base.camLens.project(pos3d, pos2d)
103 pos_wrt_render2d = Point3(pos2d[0], 0, pos2d[1])
104 pos_wrt_aspect2d = base.aspect2d.get_relative_point(render2d, pos_wrt_render2d)
105 return Point((pos_wrt_aspect2d[0], pos_wrt_aspect2d[2]))
106
107 def from_to_points(self):
108 p_from, p_to = Point3(), Point3() # in camera coordinates
109 base.camLens.extrude(LVector2f(*self), p_from, p_to)
110 p_from = render.get_relative_point(base.cam, p_from) # global coords
111 p_to = render.get_relative_point(base.cam, p_to) # global coords
112 return p_from, p_to
113
114
115 def _get_pos_relative(pos, other):
116 n = NodePath('temporary_node')
117 n.set_pos(pos)
118 pos = n.get_pos(other)
119 n.remove_node()
120 return pos