Commit | Line | Data |
---|---|---|
8ee66edd FC |
1 | import datetime |
2 | from logging import debug, info | |
3 | from os import getcwd | |
4 | from os.path import exists, dirname | |
5 | from panda3d.core import get_model_path, AntialiasAttrib, PandaNode, \ | |
6 | LightRampAttrib, Camera, OrthographicLens, NodePath, OmniBoundingVolume, \ | |
7 | AmbientLight as P3DAmbientLight, Spotlight as P3DSpotlight, Point2, \ | |
8 | Point3, Texture | |
9 | from direct.filter.CommonFilters import CommonFilters | |
10 | from direct.actor.Actor import Actor | |
11 | from lib.lib.p3d.p3d import LibP3d | |
12 | ||
13 | ||
14 | class RenderToTexture: | |
15 | ||
16 | def __init__(self, size=(256, 256)): | |
17 | self.__set_buffer(size) | |
18 | self.__set_display_region() | |
19 | self.__set_camera() | |
20 | self.__set_root() | |
21 | self.display_region.set_camera(self.camera) | |
22 | ||
23 | def __set_buffer(self, size): | |
24 | self.buffer = base.win.make_texture_buffer('result buffer', size[0], | |
25 | size[1]) | |
26 | self.buffer.set_sort(-100) | |
27 | ||
28 | def __set_display_region(self): | |
29 | self.display_region = self.buffer.make_display_region() | |
30 | self.display_region.set_sort(20) | |
31 | ||
32 | def __set_camera(self): | |
33 | self.camera = NodePath(Camera('camera 2d')) | |
34 | lens = OrthographicLens() | |
35 | lens.set_film_size(1, 1) | |
36 | lens.set_near_far(-1000, 1000) | |
37 | self.camera.node().set_lens(lens) | |
38 | ||
39 | def __set_root(self): | |
40 | self.root = NodePath('root') | |
41 | self.root.set_depth_test(False) | |
42 | self.root.set_depth_write(False) | |
43 | self.camera.reparent_to(self.root) | |
44 | ||
45 | @property | |
46 | def texture(self): return self.buffer.get_texture() | |
47 | ||
48 | def destroy(self): | |
49 | base.graphicsEngine.remove_window(self.buffer) | |
50 | if base.win: # if you close the window during a race | |
51 | base.win.remove_display_region(self.display_region) | |
52 | list(map(lambda node: node.remove_node(), [self.camera, self.root])) | |
53 | ||
54 | ||
55 | class P3dGfxMgr: | |
56 | ||
57 | def __init__(self, model_path, antialiasing, shaders, srgb): | |
58 | self.root = P3dNode(render) | |
59 | self.__srgb = srgb | |
60 | self.callbacks = {} | |
61 | self.filters = None | |
62 | get_model_path().append_directory(model_path) | |
63 | if LibP3d.runtime(): | |
64 | root_dir = LibP3d.p3dpath(dirname(__file__)) | |
65 | paths = [root_dir + '/' + model_path, root_dir] | |
66 | list(map(get_model_path().append_directory, paths)) | |
67 | render.set_shader_auto() | |
68 | # render.set_two_sided(True) # it breaks shadows | |
69 | if antialiasing: render.set_antialias(AntialiasAttrib.MAuto) | |
70 | if shaders and base.win: | |
71 | self.filters = CommonFilters(base.win, base.cam) | |
72 | ||
73 | def load_model(self, filename, callback=None, anim=None): | |
74 | ext = '.bam' if exists(filename + '.bam') else '' | |
75 | if anim: | |
76 | anim_dct = {'anim': filename + '-Anim' + ext} | |
77 | node = P3dNode(self.set_srgb(Actor(filename + ext, anim_dct))) | |
78 | elif callback: | |
79 | callb = lambda model: callback(P3dNode(self.set_srgb(model))) | |
80 | node = loader.loadModel(filename + ext, callback=callb) | |
81 | else: | |
82 | node = P3dNode(self.set_srgb( | |
83 | loader.loadModel(LibP3d.p3dpath(filename + ext)))) | |
84 | return node | |
85 | ||
86 | def set_srgb(self, model): | |
87 | if self.__srgb: | |
88 | for texture in model.find_all_textures(): | |
89 | if texture.get_format() in [Texture.F_rgba, Texture.F_rgbm]: | |
90 | texture.set_format(Texture.F_srgb_alpha) | |
91 | elif texture.get_format() in [Texture.F_rgb]: | |
92 | texture.set_format(Texture.F_srgb) | |
93 | return model | |
94 | ||
95 | @staticmethod | |
96 | def toggle_aa(): | |
97 | aa_not_none = render.get_antialias() != AntialiasAttrib.MNone | |
98 | if render.has_antialias() and aa_not_none: | |
99 | render.clear_antialias() | |
100 | else: render.set_antialias(AntialiasAttrib.MAuto, 1) | |
101 | ||
102 | def set_toon(self): | |
103 | tmp_node = NodePath(PandaNode('temp node')) | |
104 | tmp_node.set_attrib(LightRampAttrib.make_single_threshold(.5, .4)) | |
105 | tmp_node.set_shader_auto() | |
106 | base.cam.node().set_initial_state(tmp_node.get_state()) | |
107 | self.filters.set_cartoon_ink(separation=1) | |
108 | ||
109 | def set_bloom(self): | |
110 | if not base.win: return | |
111 | self.filters.setBloom( | |
112 | blend=(.3, .4, .3, 0), mintrigger=.6, maxtrigger=1.0, desat=.6, | |
113 | intensity=1.0, size='medium') | |
114 | # default: (.3, .4, .3, 0), .6, 1, .6, 1, 'medium' | |
115 | ||
116 | @staticmethod | |
117 | def pos2d(node): | |
118 | p3d = base.cam.get_relative_point(node.node, Point3(0, 0, 0)) | |
119 | p2d = Point2() | |
120 | return p2d if base.camLens.project(p3d, p2d) else None | |
121 | ||
122 | @property | |
123 | def shader_support(self): | |
124 | return base.win.get_gsg().get_supports_basic_shaders() | |
125 | ||
126 | def screenshot(self, path=None): | |
127 | time = datetime.datetime.now().strftime('%y%m%d%H%M%S') | |
128 | #res = base.win.save_screenshot(Filename(path or ("yocto%s.png" % time))) | |
129 | #debug('screenshot %s (%s)' % (path or ("yocto%s.png" % time), res)) | |
130 | res = base.screenshot(path or ("yocto%s.png" % time), False) | |
131 | info('screenshot %s (%s; %s)' % (path or ("yocto%s.png" % time), res, getcwd())) | |
132 | ||
133 | @staticmethod | |
134 | def enable_shader(): render.set_shader_auto() | |
135 | ||
136 | @staticmethod | |
137 | def disable_shader(): render.set_shader_off() | |
138 | ||
139 | @staticmethod | |
140 | def print_stats(two_d=True, three_d=True, analyze=True, ls=True): | |
141 | '''Print graphics stats. They use standard output (from p3d).''' | |
142 | info = [] | |
143 | if two_d and analyze: | |
144 | info +=[('render2d.analyze', base.render2d.analyze)] | |
145 | if three_d and analyze: | |
146 | info +=[('render.analyze', base.render.analyze)] | |
147 | if two_d and ls: | |
148 | info +=[('render2d.ls', base.render2d.ls)] | |
149 | if three_d and ls: | |
150 | info +=[('render.ls', base.render.ls)] | |
151 | for elm in info: | |
152 | print('\n\n#####\n%s()' % elm[0]) | |
153 | elm[1]() | |
154 | ||
155 | ||
156 | class P3dNode: | |
157 | ||
158 | def __init__(self, nodepath): | |
159 | self.nodepath = nodepath | |
160 | self.node.set_python_tag('libnode', self) | |
161 | ||
162 | def set_collide_mask(self, mask): return self.node.set_collide_mask(mask) | |
163 | def set_x(self, val): return self.node.set_x(val) | |
164 | def set_y(self, val): return self.node.set_y(val) | |
165 | def set_z(self, val): return self.node.set_z(val) | |
166 | def set_hpr(self, val): return self.node.set_hpr(val) | |
167 | def set_h(self, val): return self.node.set_h(val) | |
168 | def set_p(self, val): return self.node.set_p(val) | |
169 | def set_r(self, val): return self.node.set_r(val) | |
170 | def set_scale(self, val): return self.node.set_scale(val) | |
171 | def set_transparency(self, val): return self.node.set_transparency(val) | |
172 | def set_alpha_scale(self, val): return self.node.set_alpha_scale(val) | |
173 | def set_texture(self, texturestage, texture): | |
174 | return self.node.set_texture(texturestage, texture) | |
175 | def has_tag(self, name): return self.node.has_tag(name) | |
176 | def get_tag(self, name): return self.node.get_tag(name) | |
177 | def get_python_tag(self, name): return self.node.get_python_tag(name) | |
178 | def remove_node(self): return self.node.remove_node() | |
179 | def flatten_strong(self): return self.node.flatten_strong() | |
180 | def clear_model_nodes(self): return self.node.clear_model_nodes() | |
181 | def show(self): return self.node.show() | |
182 | def set_depth_offset(self, val): return self.node.set_depth_offset(val) | |
183 | def loop(self, val): return self.node.loop(val) | |
184 | def cleanup(self): return self.node.cleanup() | |
185 | def write_bam_file(self, fname): return self.node.write_bam_file(fname) | |
186 | ||
187 | def attach_node(self, name): | |
188 | return P3dNode(self.node.attach_new_node(name)) | |
189 | ||
190 | def add_shape(self, shape): | |
191 | return self.node.node().add_shape(shape._mesh_shape) | |
192 | #TODO: don't access a protected member | |
193 | ||
194 | @property | |
195 | def name(self): return self.node.get_name() | |
196 | ||
197 | @property | |
198 | def node(self): return self.nodepath | |
199 | ||
200 | @property | |
201 | def p3dnode(self): return self.node.node() | |
202 | ||
203 | def set_pos(self, pos): return self.node.set_pos(pos._vec) | |
204 | #TODO: don't access a protected member | |
205 | ||
206 | def get_pos(self, other=None): | |
207 | return self.node.get_pos(* [] if other is None else [other.node]) | |
208 | ||
209 | @property | |
210 | def x(self): return self.node.get_x() | |
211 | ||
212 | @property | |
213 | def y(self): return self.node.get_y() | |
214 | ||
215 | @property | |
216 | def z(self): return self.node.get_z() | |
217 | ||
218 | @property | |
219 | def hpr(self): return self.node.get_hpr() | |
220 | ||
221 | @property | |
222 | def h(self): return self.node.get_h() | |
223 | ||
224 | @property | |
225 | def p(self): return self.node.get_p() | |
226 | ||
227 | @property | |
228 | def r(self): return self.node.get_r() | |
229 | ||
230 | @property | |
231 | def scale(self): return self.node.get_scale() | |
232 | ||
233 | @property | |
234 | def is_empty(self): return self.node.is_empty() | |
235 | ||
236 | def get_relative_vector(self, node, vec): | |
237 | return self.node.get_relative_vector(node.node, vec) | |
238 | ||
239 | def set_material(self, mat): return self.node.set_material(mat, 1) | |
240 | ||
241 | def set_python_tag(self, name, val): | |
242 | return self.node.set_python_tag(name, val) | |
243 | ||
244 | def get_distance(self, other_node): | |
245 | return self.node.get_distance(other_node.node) | |
246 | ||
247 | def reparent_to(self, parent): return self.node.reparent_to(parent.node) | |
248 | ||
249 | def wrt_reparent_to(self, parent): | |
250 | return self.node.wrt_reparent_to(parent.node) | |
251 | ||
252 | @staticmethod | |
253 | def __get_pandanode(nodepath): | |
254 | if nodepath.has_python_tag('libnode'): | |
255 | return nodepath.get_python_tag('libnode') | |
256 | return P3dNode(nodepath) | |
257 | ||
258 | def find_all_matches(self, name): | |
259 | nodes = self.node.find_all_matches(name) | |
260 | return [self.__get_pandanode(node) for node in nodes] | |
261 | ||
262 | def find(self, name): | |
263 | model = self.node.find(name) | |
264 | if model: return self.__get_pandanode(model) | |
265 | ||
266 | def optimize(self): | |
267 | self.node.prepare_scene(base.win.get_gsg()) # crash with texture.set_format | |
268 | self.node.premunge_scene(base.win.get_gsg()) | |
269 | ||
270 | def hide(self, mask=None): | |
271 | return self.node.hide(*[] if mask is None else [mask]) | |
272 | ||
273 | @property | |
274 | def tight_bounds(self): return self.node.get_tight_bounds() | |
275 | ||
276 | @property | |
277 | def parent(self): return self.node.get_parent() | |
278 | ||
279 | @property | |
280 | def children(self): return self.node.get_children() | |
281 | ||
282 | def destroy(self): return self.node.remove_node() | |
283 | ||
284 | ||
285 | class P3dAnimNode: | |
286 | ||
287 | def __init__(self, filepath, anim_dct): | |
288 | self.node = Actor(filepath, anim_dct) | |
289 | ||
290 | def loop(self, val): return self.node.loop(val) | |
291 | ||
292 | def reparent_to(self, node): self.node.reparent_to(node) | |
293 | ||
294 | @property | |
295 | def name(self): return self.node.get_name() | |
296 | ||
297 | def optimize(self): | |
298 | self.node.prepare_scene(base.win.get_gsg()) | |
299 | self.node.premunge_scene(base.win.get_gsg()) | |
300 | ||
301 | def set_omni(self): | |
302 | self.node.node().set_bounds(OmniBoundingVolume()) | |
303 | self.node.node().set_final(True) | |
304 | ||
305 | def destroy(self): self.node.cleanup() | |
306 | ||
307 | ||
308 | class P3dAmbientLight: | |
309 | ||
310 | def __init__(self, color): | |
311 | ambient_lgt = P3DAmbientLight('ambient light') | |
312 | ambient_lgt.set_color(color) | |
313 | self.ambient_np = render.attach_new_node(ambient_lgt) | |
314 | render.set_light(self.ambient_np) | |
315 | ||
316 | def destroy(self): | |
317 | render.clear_light(self.ambient_np) | |
318 | self.ambient_np.remove_node() | |
319 | ||
320 | ||
321 | class P3dSpotlight: | |
322 | ||
323 | def __init__(self, mask=None): | |
324 | self.spot_lgt = render.attach_new_node(P3DSpotlight('spot')) | |
325 | snode = self.spot_lgt.node() | |
326 | snode.set_scene(render) | |
327 | snode.set_shadow_caster(True, 1024, 1024) | |
328 | snode.get_lens().set_fov(40) | |
329 | snode.get_lens().set_near_far(20, 200) | |
330 | if mask: snode.set_camera_mask(mask) | |
331 | render.set_light(self.spot_lgt) | |
332 | ||
333 | def set_pos(self, pos): return self.spot_lgt.set_pos(*pos) | |
334 | ||
335 | def look_at(self, pos): return self.spot_lgt.look_at(*pos) | |
336 | ||
337 | def set_color(self, color): return self.spot_lgt.set_color(*color) | |
338 | ||
339 | def destroy(self): | |
340 | render.clear_light(self.spot_lgt) | |
341 | self.spot_lgt.remove_node() |