1 | /**************************************************************************/ |
2 | /* sprite_3d.cpp */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #include "sprite_3d.h" |
32 | |
33 | #include "scene/resources/atlas_texture.h" |
34 | #include "scene/scene_string_names.h" |
35 | |
36 | Color SpriteBase3D::_get_color_accum() { |
37 | if (!color_dirty) { |
38 | return color_accum; |
39 | } |
40 | |
41 | if (parent_sprite) { |
42 | color_accum = parent_sprite->_get_color_accum(); |
43 | } else { |
44 | color_accum = Color(1, 1, 1, 1); |
45 | } |
46 | |
47 | color_accum.r *= modulate.r; |
48 | color_accum.g *= modulate.g; |
49 | color_accum.b *= modulate.b; |
50 | color_accum.a *= modulate.a; |
51 | color_dirty = false; |
52 | return color_accum; |
53 | } |
54 | |
55 | void SpriteBase3D::_propagate_color_changed() { |
56 | if (color_dirty) { |
57 | return; |
58 | } |
59 | |
60 | color_dirty = true; |
61 | _queue_redraw(); |
62 | |
63 | for (SpriteBase3D *&E : children) { |
64 | E->_propagate_color_changed(); |
65 | } |
66 | } |
67 | |
68 | void SpriteBase3D::_notification(int p_what) { |
69 | switch (p_what) { |
70 | case NOTIFICATION_ENTER_TREE: { |
71 | if (!pending_update) { |
72 | _im_update(); |
73 | } |
74 | |
75 | parent_sprite = Object::cast_to<SpriteBase3D>(get_parent()); |
76 | if (parent_sprite) { |
77 | pI = parent_sprite->children.push_back(this); |
78 | } |
79 | } break; |
80 | |
81 | case NOTIFICATION_EXIT_TREE: { |
82 | if (parent_sprite) { |
83 | parent_sprite->children.erase(pI); |
84 | pI = nullptr; |
85 | parent_sprite = nullptr; |
86 | } |
87 | } break; |
88 | } |
89 | } |
90 | |
91 | void SpriteBase3D::draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect, Rect2 p_src_rect) { |
92 | ERR_FAIL_COND(p_texture.is_null()); |
93 | |
94 | Rect2 final_rect; |
95 | Rect2 final_src_rect; |
96 | if (!p_texture->get_rect_region(p_dst_rect, p_src_rect, final_rect, final_src_rect)) { |
97 | return; |
98 | } |
99 | |
100 | if (final_rect.size.x == 0 || final_rect.size.y == 0) { |
101 | return; |
102 | } |
103 | |
104 | // 2D: 3D plane (axes match exactly when `axis == Vector3::AXIS_Z`): |
105 | // -X+ -X+ |
106 | // - + |
107 | // Y +--------+ +--------+ +--------+ Y +--------+ |
108 | // + | +--+ | | | (2) | | - | 0--1 | |
109 | // | |ab| | (1) | +--+ | (3) | 3--2 | | |ab| | |
110 | // | |cd| | --> | |ab| | --> | |cd| | <==> | |cd| | |
111 | // | +--+ | | |cd| | | |ab| | | 3--2 | |
112 | // | | | +--+ | | 0--1 | | | |
113 | // +--------+ +--------+ +--------+ +--------+ |
114 | |
115 | // (1) Y-wise shift `final_rect` within `p_dst_rect` so after inverting Y |
116 | // axis distances between top/bottom borders will be preserved (so for |
117 | // example AtlasTextures with vertical margins will look the same in 2D/3D). |
118 | final_rect.position.y = (p_dst_rect.position.y + p_dst_rect.size.y) - ((final_rect.position.y + final_rect.size.y) - p_dst_rect.position.y); |
119 | |
120 | Color color = _get_color_accum(); |
121 | |
122 | real_t px_size = get_pixel_size(); |
123 | |
124 | // (2) Order vertices (0123) bottom-top in 2D / top-bottom in 3D. |
125 | Vector2 vertices[4] = { |
126 | (final_rect.position + Vector2(0, final_rect.size.y)) * px_size, |
127 | (final_rect.position + final_rect.size) * px_size, |
128 | (final_rect.position + Vector2(final_rect.size.x, 0)) * px_size, |
129 | final_rect.position * px_size, |
130 | }; |
131 | |
132 | Vector2 src_tsize = p_texture->get_size(); |
133 | |
134 | // Properly setup UVs for impostor textures (AtlasTexture). |
135 | Ref<AtlasTexture> atlas_tex = p_texture; |
136 | if (atlas_tex != nullptr) { |
137 | src_tsize[0] = atlas_tex->get_atlas()->get_width(); |
138 | src_tsize[1] = atlas_tex->get_atlas()->get_height(); |
139 | } |
140 | |
141 | // (3) Assign UVs (abcd) according to the vertices order (bottom-top in 2D / top-bottom in 3D). |
142 | Vector2 uvs[4] = { |
143 | final_src_rect.position / src_tsize, |
144 | (final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / src_tsize, |
145 | (final_src_rect.position + final_src_rect.size) / src_tsize, |
146 | (final_src_rect.position + Vector2(0, final_src_rect.size.y)) / src_tsize, |
147 | }; |
148 | |
149 | if (is_flipped_h()) { |
150 | SWAP(uvs[0], uvs[1]); |
151 | SWAP(uvs[2], uvs[3]); |
152 | } |
153 | |
154 | if (is_flipped_v()) { |
155 | SWAP(uvs[0], uvs[3]); |
156 | SWAP(uvs[1], uvs[2]); |
157 | } |
158 | |
159 | Vector3 normal; |
160 | int ax = get_axis(); |
161 | normal[ax] = 1.0; |
162 | |
163 | Plane tangent; |
164 | if (ax == Vector3::AXIS_X) { |
165 | tangent = Plane(0, 0, -1, 1); |
166 | } else { |
167 | tangent = Plane(1, 0, 0, 1); |
168 | } |
169 | |
170 | int x_axis = ((ax + 1) % 3); |
171 | int y_axis = ((ax + 2) % 3); |
172 | |
173 | if (ax != Vector3::AXIS_Z) { |
174 | SWAP(x_axis, y_axis); |
175 | |
176 | for (int i = 0; i < 4; i++) { |
177 | //uvs[i] = Vector2(1.0,1.0)-uvs[i]; |
178 | //SWAP(vertices[i].x,vertices[i].y); |
179 | if (ax == Vector3::AXIS_Y) { |
180 | vertices[i].y = -vertices[i].y; |
181 | } else if (ax == Vector3::AXIS_X) { |
182 | vertices[i].x = -vertices[i].x; |
183 | } |
184 | } |
185 | } |
186 | |
187 | AABB aabb_new; |
188 | |
189 | // Everything except position and UV is compressed. |
190 | uint8_t *vertex_write_buffer = vertex_buffer.ptrw(); |
191 | uint8_t *attribute_write_buffer = attribute_buffer.ptrw(); |
192 | |
193 | uint32_t v_normal; |
194 | { |
195 | Vector2 res = normal.octahedron_encode(); |
196 | uint32_t value = 0; |
197 | value |= (uint16_t)CLAMP(res.x * 65535, 0, 65535); |
198 | value |= (uint16_t)CLAMP(res.y * 65535, 0, 65535) << 16; |
199 | |
200 | v_normal = value; |
201 | } |
202 | uint32_t v_tangent; |
203 | { |
204 | Plane t = tangent; |
205 | Vector2 res = t.normal.octahedron_tangent_encode(t.d); |
206 | uint32_t value = 0; |
207 | value |= (uint16_t)CLAMP(res.x * 65535, 0, 65535); |
208 | value |= (uint16_t)CLAMP(res.y * 65535, 0, 65535) << 16; |
209 | |
210 | v_tangent = value; |
211 | } |
212 | |
213 | uint8_t v_color[4] = { |
214 | uint8_t(CLAMP(color.r * 255.0, 0.0, 255.0)), |
215 | uint8_t(CLAMP(color.g * 255.0, 0.0, 255.0)), |
216 | uint8_t(CLAMP(color.b * 255.0, 0.0, 255.0)), |
217 | uint8_t(CLAMP(color.a * 255.0, 0.0, 255.0)) |
218 | }; |
219 | |
220 | for (int i = 0; i < 4; i++) { |
221 | Vector3 vtx; |
222 | vtx[x_axis] = vertices[i][0]; |
223 | vtx[y_axis] = vertices[i][1]; |
224 | if (i == 0) { |
225 | aabb_new.position = vtx; |
226 | aabb_new.size = Vector3(); |
227 | } else { |
228 | aabb_new.expand_to(vtx); |
229 | } |
230 | |
231 | float v_uv[2] = { (float)uvs[i].x, (float)uvs[i].y }; |
232 | memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_TEX_UV]], v_uv, 8); |
233 | |
234 | float v_vertex[3] = { (float)vtx.x, (float)vtx.y, (float)vtx.z }; |
235 | |
236 | memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_VERTEX]], &v_vertex, sizeof(float) * 3); |
237 | memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_NORMAL]], &v_normal, 4); |
238 | memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_TANGENT]], &v_tangent, 4); |
239 | memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_COLOR]], v_color, 4); |
240 | } |
241 | |
242 | RID mesh_new = get_mesh(); |
243 | RS::get_singleton()->mesh_surface_update_vertex_region(mesh_new, 0, 0, vertex_buffer); |
244 | RS::get_singleton()->mesh_surface_update_attribute_region(mesh_new, 0, 0, attribute_buffer); |
245 | |
246 | RS::get_singleton()->mesh_set_custom_aabb(mesh_new, aabb_new); |
247 | set_aabb(aabb_new); |
248 | |
249 | RS::get_singleton()->material_set_param(get_material(), "alpha_scissor_threshold" , alpha_scissor_threshold); |
250 | RS::get_singleton()->material_set_param(get_material(), "alpha_hash_scale" , alpha_hash_scale); |
251 | RS::get_singleton()->material_set_param(get_material(), "alpha_antialiasing_edge" , alpha_antialiasing_edge); |
252 | |
253 | BaseMaterial3D::Transparency mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_DISABLED; |
254 | if (get_draw_flag(FLAG_TRANSPARENT)) { |
255 | if (get_alpha_cut_mode() == ALPHA_CUT_DISCARD) { |
256 | mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA_SCISSOR; |
257 | } else if (get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS) { |
258 | mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS; |
259 | } else if (get_alpha_cut_mode() == ALPHA_CUT_HASH) { |
260 | mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA_HASH; |
261 | } else { |
262 | mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_ALPHA; |
263 | } |
264 | } |
265 | |
266 | RID shader_rid; |
267 | StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), mat_transparency, get_draw_flag(FLAG_DOUBLE_SIDED), get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, false, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), get_texture_filter(), alpha_antialiasing_mode, &shader_rid); |
268 | |
269 | if (last_shader != shader_rid) { |
270 | RS::get_singleton()->material_set_shader(get_material(), shader_rid); |
271 | last_shader = shader_rid; |
272 | } |
273 | if (last_texture != p_texture->get_rid()) { |
274 | RS::get_singleton()->material_set_param(get_material(), "texture_albedo" , p_texture->get_rid()); |
275 | last_texture = p_texture->get_rid(); |
276 | } |
277 | if (get_alpha_cut_mode() == ALPHA_CUT_DISABLED) { |
278 | RS::get_singleton()->material_set_render_priority(get_material(), get_render_priority()); |
279 | RS::get_singleton()->mesh_surface_set_material(mesh, 0, get_material()); |
280 | } |
281 | } |
282 | |
283 | void SpriteBase3D::set_centered(bool p_center) { |
284 | centered = p_center; |
285 | _queue_redraw(); |
286 | } |
287 | |
288 | bool SpriteBase3D::is_centered() const { |
289 | return centered; |
290 | } |
291 | |
292 | void SpriteBase3D::set_offset(const Point2 &p_offset) { |
293 | offset = p_offset; |
294 | _queue_redraw(); |
295 | } |
296 | |
297 | Point2 SpriteBase3D::get_offset() const { |
298 | return offset; |
299 | } |
300 | |
301 | void SpriteBase3D::set_flip_h(bool p_flip) { |
302 | hflip = p_flip; |
303 | _queue_redraw(); |
304 | } |
305 | |
306 | bool SpriteBase3D::is_flipped_h() const { |
307 | return hflip; |
308 | } |
309 | |
310 | void SpriteBase3D::set_flip_v(bool p_flip) { |
311 | vflip = p_flip; |
312 | _queue_redraw(); |
313 | } |
314 | |
315 | bool SpriteBase3D::is_flipped_v() const { |
316 | return vflip; |
317 | } |
318 | |
319 | void SpriteBase3D::set_modulate(const Color &p_color) { |
320 | modulate = p_color; |
321 | _propagate_color_changed(); |
322 | _queue_redraw(); |
323 | } |
324 | |
325 | Color SpriteBase3D::get_modulate() const { |
326 | return modulate; |
327 | } |
328 | |
329 | void SpriteBase3D::set_render_priority(int p_priority) { |
330 | ERR_FAIL_COND(p_priority < RS::MATERIAL_RENDER_PRIORITY_MIN || p_priority > RS::MATERIAL_RENDER_PRIORITY_MAX); |
331 | render_priority = p_priority; |
332 | _queue_redraw(); |
333 | } |
334 | |
335 | int SpriteBase3D::get_render_priority() const { |
336 | return render_priority; |
337 | } |
338 | |
339 | void SpriteBase3D::set_pixel_size(real_t p_amount) { |
340 | pixel_size = p_amount; |
341 | _queue_redraw(); |
342 | } |
343 | |
344 | real_t SpriteBase3D::get_pixel_size() const { |
345 | return pixel_size; |
346 | } |
347 | |
348 | void SpriteBase3D::set_axis(Vector3::Axis p_axis) { |
349 | ERR_FAIL_INDEX(p_axis, 3); |
350 | axis = p_axis; |
351 | _queue_redraw(); |
352 | } |
353 | |
354 | Vector3::Axis SpriteBase3D::get_axis() const { |
355 | return axis; |
356 | } |
357 | |
358 | void SpriteBase3D::_im_update() { |
359 | _draw(); |
360 | |
361 | pending_update = false; |
362 | |
363 | //texture->draw_rect_region(ci,dst_rect,src_rect,modulate); |
364 | } |
365 | |
366 | void SpriteBase3D::_queue_redraw() { |
367 | // The 3D equivalent of CanvasItem.queue_redraw(). |
368 | if (pending_update) { |
369 | return; |
370 | } |
371 | |
372 | triangle_mesh.unref(); |
373 | update_gizmos(); |
374 | |
375 | pending_update = true; |
376 | callable_mp(this, &SpriteBase3D::_im_update).call_deferred(); |
377 | } |
378 | |
379 | AABB SpriteBase3D::get_aabb() const { |
380 | return aabb; |
381 | } |
382 | |
383 | Ref<TriangleMesh> SpriteBase3D::generate_triangle_mesh() const { |
384 | if (triangle_mesh.is_valid()) { |
385 | return triangle_mesh; |
386 | } |
387 | |
388 | Vector<Vector3> faces; |
389 | faces.resize(6); |
390 | Vector3 *facesw = faces.ptrw(); |
391 | |
392 | Rect2 final_rect = get_item_rect(); |
393 | |
394 | if (final_rect.size.x == 0 || final_rect.size.y == 0) { |
395 | return Ref<TriangleMesh>(); |
396 | } |
397 | |
398 | real_t px_size = get_pixel_size(); |
399 | |
400 | Vector2 vertices[4] = { |
401 | |
402 | (final_rect.position + Vector2(0, final_rect.size.y)) * px_size, |
403 | (final_rect.position + final_rect.size) * px_size, |
404 | (final_rect.position + Vector2(final_rect.size.x, 0)) * px_size, |
405 | final_rect.position * px_size, |
406 | |
407 | }; |
408 | |
409 | int x_axis = ((axis + 1) % 3); |
410 | int y_axis = ((axis + 2) % 3); |
411 | |
412 | if (axis != Vector3::AXIS_Z) { |
413 | SWAP(x_axis, y_axis); |
414 | |
415 | for (int i = 0; i < 4; i++) { |
416 | if (axis == Vector3::AXIS_Y) { |
417 | vertices[i].y = -vertices[i].y; |
418 | } else if (axis == Vector3::AXIS_X) { |
419 | vertices[i].x = -vertices[i].x; |
420 | } |
421 | } |
422 | } |
423 | |
424 | static const int indices[6] = { |
425 | 0, 1, 2, |
426 | 0, 2, 3 |
427 | }; |
428 | |
429 | for (int j = 0; j < 6; j++) { |
430 | int i = indices[j]; |
431 | Vector3 vtx; |
432 | vtx[x_axis] = vertices[i][0]; |
433 | vtx[y_axis] = vertices[i][1]; |
434 | facesw[j] = vtx; |
435 | } |
436 | |
437 | triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh)); |
438 | triangle_mesh->create(faces); |
439 | |
440 | return triangle_mesh; |
441 | } |
442 | |
443 | void SpriteBase3D::set_draw_flag(DrawFlags p_flag, bool p_enable) { |
444 | ERR_FAIL_INDEX(p_flag, FLAG_MAX); |
445 | flags[p_flag] = p_enable; |
446 | _queue_redraw(); |
447 | } |
448 | |
449 | bool SpriteBase3D::get_draw_flag(DrawFlags p_flag) const { |
450 | ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false); |
451 | return flags[p_flag]; |
452 | } |
453 | |
454 | void SpriteBase3D::set_alpha_cut_mode(AlphaCutMode p_mode) { |
455 | ERR_FAIL_INDEX(p_mode, ALPHA_CUT_MAX); |
456 | alpha_cut = p_mode; |
457 | _queue_redraw(); |
458 | } |
459 | |
460 | SpriteBase3D::AlphaCutMode SpriteBase3D::get_alpha_cut_mode() const { |
461 | return alpha_cut; |
462 | } |
463 | |
464 | void SpriteBase3D::set_alpha_hash_scale(float p_hash_scale) { |
465 | if (alpha_hash_scale != p_hash_scale) { |
466 | alpha_hash_scale = p_hash_scale; |
467 | _queue_redraw(); |
468 | } |
469 | } |
470 | |
471 | float SpriteBase3D::get_alpha_hash_scale() const { |
472 | return alpha_hash_scale; |
473 | } |
474 | |
475 | void SpriteBase3D::set_alpha_scissor_threshold(float p_threshold) { |
476 | if (alpha_scissor_threshold != p_threshold) { |
477 | alpha_scissor_threshold = p_threshold; |
478 | _queue_redraw(); |
479 | } |
480 | } |
481 | |
482 | float SpriteBase3D::get_alpha_scissor_threshold() const { |
483 | return alpha_scissor_threshold; |
484 | } |
485 | |
486 | void SpriteBase3D::set_alpha_antialiasing(BaseMaterial3D::AlphaAntiAliasing p_alpha_aa) { |
487 | if (alpha_antialiasing_mode != p_alpha_aa) { |
488 | alpha_antialiasing_mode = p_alpha_aa; |
489 | _queue_redraw(); |
490 | } |
491 | } |
492 | |
493 | BaseMaterial3D::AlphaAntiAliasing SpriteBase3D::get_alpha_antialiasing() const { |
494 | return alpha_antialiasing_mode; |
495 | } |
496 | |
497 | void SpriteBase3D::set_alpha_antialiasing_edge(float p_edge) { |
498 | if (alpha_antialiasing_edge != p_edge) { |
499 | alpha_antialiasing_edge = p_edge; |
500 | _queue_redraw(); |
501 | } |
502 | } |
503 | |
504 | float SpriteBase3D::get_alpha_antialiasing_edge() const { |
505 | return alpha_antialiasing_edge; |
506 | } |
507 | |
508 | void SpriteBase3D::set_billboard_mode(StandardMaterial3D::BillboardMode p_mode) { |
509 | ERR_FAIL_INDEX(p_mode, 3); // Cannot use BILLBOARD_PARTICLES. |
510 | billboard_mode = p_mode; |
511 | _queue_redraw(); |
512 | } |
513 | |
514 | StandardMaterial3D::BillboardMode SpriteBase3D::get_billboard_mode() const { |
515 | return billboard_mode; |
516 | } |
517 | |
518 | void SpriteBase3D::set_texture_filter(StandardMaterial3D::TextureFilter p_filter) { |
519 | if (texture_filter != p_filter) { |
520 | texture_filter = p_filter; |
521 | _queue_redraw(); |
522 | } |
523 | } |
524 | |
525 | StandardMaterial3D::TextureFilter SpriteBase3D::get_texture_filter() const { |
526 | return texture_filter; |
527 | } |
528 | |
529 | void SpriteBase3D::_bind_methods() { |
530 | ClassDB::bind_method(D_METHOD("set_centered" , "centered" ), &SpriteBase3D::set_centered); |
531 | ClassDB::bind_method(D_METHOD("is_centered" ), &SpriteBase3D::is_centered); |
532 | |
533 | ClassDB::bind_method(D_METHOD("set_offset" , "offset" ), &SpriteBase3D::set_offset); |
534 | ClassDB::bind_method(D_METHOD("get_offset" ), &SpriteBase3D::get_offset); |
535 | |
536 | ClassDB::bind_method(D_METHOD("set_flip_h" , "flip_h" ), &SpriteBase3D::set_flip_h); |
537 | ClassDB::bind_method(D_METHOD("is_flipped_h" ), &SpriteBase3D::is_flipped_h); |
538 | |
539 | ClassDB::bind_method(D_METHOD("set_flip_v" , "flip_v" ), &SpriteBase3D::set_flip_v); |
540 | ClassDB::bind_method(D_METHOD("is_flipped_v" ), &SpriteBase3D::is_flipped_v); |
541 | |
542 | ClassDB::bind_method(D_METHOD("set_modulate" , "modulate" ), &SpriteBase3D::set_modulate); |
543 | ClassDB::bind_method(D_METHOD("get_modulate" ), &SpriteBase3D::get_modulate); |
544 | |
545 | ClassDB::bind_method(D_METHOD("set_render_priority" , "priority" ), &SpriteBase3D::set_render_priority); |
546 | ClassDB::bind_method(D_METHOD("get_render_priority" ), &SpriteBase3D::get_render_priority); |
547 | |
548 | ClassDB::bind_method(D_METHOD("set_pixel_size" , "pixel_size" ), &SpriteBase3D::set_pixel_size); |
549 | ClassDB::bind_method(D_METHOD("get_pixel_size" ), &SpriteBase3D::get_pixel_size); |
550 | |
551 | ClassDB::bind_method(D_METHOD("set_axis" , "axis" ), &SpriteBase3D::set_axis); |
552 | ClassDB::bind_method(D_METHOD("get_axis" ), &SpriteBase3D::get_axis); |
553 | |
554 | ClassDB::bind_method(D_METHOD("set_draw_flag" , "flag" , "enabled" ), &SpriteBase3D::set_draw_flag); |
555 | ClassDB::bind_method(D_METHOD("get_draw_flag" , "flag" ), &SpriteBase3D::get_draw_flag); |
556 | |
557 | ClassDB::bind_method(D_METHOD("set_alpha_cut_mode" , "mode" ), &SpriteBase3D::set_alpha_cut_mode); |
558 | ClassDB::bind_method(D_METHOD("get_alpha_cut_mode" ), &SpriteBase3D::get_alpha_cut_mode); |
559 | |
560 | ClassDB::bind_method(D_METHOD("set_alpha_scissor_threshold" , "threshold" ), &SpriteBase3D::set_alpha_scissor_threshold); |
561 | ClassDB::bind_method(D_METHOD("get_alpha_scissor_threshold" ), &SpriteBase3D::get_alpha_scissor_threshold); |
562 | |
563 | ClassDB::bind_method(D_METHOD("set_alpha_hash_scale" , "threshold" ), &SpriteBase3D::set_alpha_hash_scale); |
564 | ClassDB::bind_method(D_METHOD("get_alpha_hash_scale" ), &SpriteBase3D::get_alpha_hash_scale); |
565 | |
566 | ClassDB::bind_method(D_METHOD("set_alpha_antialiasing" , "alpha_aa" ), &SpriteBase3D::set_alpha_antialiasing); |
567 | ClassDB::bind_method(D_METHOD("get_alpha_antialiasing" ), &SpriteBase3D::get_alpha_antialiasing); |
568 | |
569 | ClassDB::bind_method(D_METHOD("set_alpha_antialiasing_edge" , "edge" ), &SpriteBase3D::set_alpha_antialiasing_edge); |
570 | ClassDB::bind_method(D_METHOD("get_alpha_antialiasing_edge" ), &SpriteBase3D::get_alpha_antialiasing_edge); |
571 | |
572 | ClassDB::bind_method(D_METHOD("set_billboard_mode" , "mode" ), &SpriteBase3D::set_billboard_mode); |
573 | ClassDB::bind_method(D_METHOD("get_billboard_mode" ), &SpriteBase3D::get_billboard_mode); |
574 | |
575 | ClassDB::bind_method(D_METHOD("set_texture_filter" , "mode" ), &SpriteBase3D::set_texture_filter); |
576 | ClassDB::bind_method(D_METHOD("get_texture_filter" ), &SpriteBase3D::get_texture_filter); |
577 | |
578 | ClassDB::bind_method(D_METHOD("get_item_rect" ), &SpriteBase3D::get_item_rect); |
579 | ClassDB::bind_method(D_METHOD("generate_triangle_mesh" ), &SpriteBase3D::generate_triangle_mesh); |
580 | |
581 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered" ), "set_centered" , "is_centered" ); |
582 | ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset" , PROPERTY_HINT_NONE, "suffix:px" ), "set_offset" , "get_offset" ); |
583 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h" ), "set_flip_h" , "is_flipped_h" ); |
584 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v" ), "set_flip_v" , "is_flipped_v" ); |
585 | ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate" ), "set_modulate" , "get_modulate" ); |
586 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pixel_size" , PROPERTY_HINT_RANGE, "0.0001,128,0.0001,suffix:m" ), "set_pixel_size" , "get_pixel_size" ); |
587 | ADD_PROPERTY(PropertyInfo(Variant::INT, "axis" , PROPERTY_HINT_ENUM, "X-Axis,Y-Axis,Z-Axis" ), "set_axis" , "get_axis" ); |
588 | ADD_GROUP("Flags" , "" ); |
589 | ADD_PROPERTY(PropertyInfo(Variant::INT, "billboard" , PROPERTY_HINT_ENUM, "Disabled,Enabled,Y-Billboard" ), "set_billboard_mode" , "get_billboard_mode" ); |
590 | ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "transparent" ), "set_draw_flag" , "get_draw_flag" , FLAG_TRANSPARENT); |
591 | ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "shaded" ), "set_draw_flag" , "get_draw_flag" , FLAG_SHADED); |
592 | ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "double_sided" ), "set_draw_flag" , "get_draw_flag" , FLAG_DOUBLE_SIDED); |
593 | ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "no_depth_test" ), "set_draw_flag" , "get_draw_flag" , FLAG_DISABLE_DEPTH_TEST); |
594 | ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "fixed_size" ), "set_draw_flag" , "get_draw_flag" , FLAG_FIXED_SIZE); |
595 | ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_cut" , PROPERTY_HINT_ENUM, "Disabled,Discard,Opaque Pre-Pass,Alpha Hash" ), "set_alpha_cut_mode" , "get_alpha_cut_mode" ); |
596 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_scissor_threshold" , PROPERTY_HINT_RANGE, "0,1,0.001" ), "set_alpha_scissor_threshold" , "get_alpha_scissor_threshold" ); |
597 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_hash_scale" , PROPERTY_HINT_RANGE, "0,2,0.01" ), "set_alpha_hash_scale" , "get_alpha_hash_scale" ); |
598 | ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_antialiasing_mode" , PROPERTY_HINT_ENUM, "Disabled,Alpha Edge Blend,Alpha Edge Clip" ), "set_alpha_antialiasing" , "get_alpha_antialiasing" ); |
599 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_antialiasing_edge" , PROPERTY_HINT_RANGE, "0,1,0.01" ), "set_alpha_antialiasing_edge" , "get_alpha_antialiasing_edge" ); |
600 | ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter" , PROPERTY_HINT_ENUM, "Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic" ), "set_texture_filter" , "get_texture_filter" ); |
601 | ADD_PROPERTY(PropertyInfo(Variant::INT, "render_priority" , PROPERTY_HINT_RANGE, itos(RS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(RS::MATERIAL_RENDER_PRIORITY_MAX) + ",1" ), "set_render_priority" , "get_render_priority" ); |
602 | |
603 | BIND_ENUM_CONSTANT(FLAG_TRANSPARENT); |
604 | BIND_ENUM_CONSTANT(FLAG_SHADED); |
605 | BIND_ENUM_CONSTANT(FLAG_DOUBLE_SIDED); |
606 | BIND_ENUM_CONSTANT(FLAG_DISABLE_DEPTH_TEST); |
607 | BIND_ENUM_CONSTANT(FLAG_FIXED_SIZE); |
608 | BIND_ENUM_CONSTANT(FLAG_MAX); |
609 | |
610 | BIND_ENUM_CONSTANT(ALPHA_CUT_DISABLED); |
611 | BIND_ENUM_CONSTANT(ALPHA_CUT_DISCARD); |
612 | BIND_ENUM_CONSTANT(ALPHA_CUT_OPAQUE_PREPASS); |
613 | BIND_ENUM_CONSTANT(ALPHA_CUT_HASH); |
614 | } |
615 | |
616 | SpriteBase3D::SpriteBase3D() { |
617 | for (int i = 0; i < FLAG_MAX; i++) { |
618 | flags[i] = i == FLAG_TRANSPARENT || i == FLAG_DOUBLE_SIDED; |
619 | } |
620 | |
621 | material = RenderingServer::get_singleton()->material_create(); |
622 | // Set defaults for material, names need to match up those in StandardMaterial3D. |
623 | RS::get_singleton()->material_set_param(material, "albedo" , Color(1, 1, 1, 1)); |
624 | RS::get_singleton()->material_set_param(material, "specular" , 0.5); |
625 | RS::get_singleton()->material_set_param(material, "metallic" , 0.0); |
626 | RS::get_singleton()->material_set_param(material, "roughness" , 1.0); |
627 | RS::get_singleton()->material_set_param(material, "uv1_offset" , Vector3(0, 0, 0)); |
628 | RS::get_singleton()->material_set_param(material, "uv1_scale" , Vector3(1, 1, 1)); |
629 | RS::get_singleton()->material_set_param(material, "uv2_offset" , Vector3(0, 0, 0)); |
630 | RS::get_singleton()->material_set_param(material, "uv2_scale" , Vector3(1, 1, 1)); |
631 | |
632 | mesh = RenderingServer::get_singleton()->mesh_create(); |
633 | |
634 | PackedVector3Array mesh_vertices; |
635 | PackedVector3Array mesh_normals; |
636 | PackedFloat32Array mesh_tangents; |
637 | PackedColorArray mesh_colors; |
638 | PackedVector2Array mesh_uvs; |
639 | PackedInt32Array indices; |
640 | |
641 | mesh_vertices.resize(4); |
642 | mesh_normals.resize(4); |
643 | mesh_tangents.resize(16); |
644 | mesh_colors.resize(4); |
645 | mesh_uvs.resize(4); |
646 | |
647 | // Create basic mesh and store format information. |
648 | for (int i = 0; i < 4; i++) { |
649 | mesh_normals.write[i] = Vector3(0.0, 0.0, 1.0); |
650 | mesh_tangents.write[i * 4 + 0] = 1.0; |
651 | mesh_tangents.write[i * 4 + 1] = 0.0; |
652 | mesh_tangents.write[i * 4 + 2] = 0.0; |
653 | mesh_tangents.write[i * 4 + 3] = 1.0; |
654 | mesh_colors.write[i] = Color(1.0, 1.0, 1.0, 1.0); |
655 | mesh_uvs.write[i] = Vector2(0.0, 0.0); |
656 | mesh_vertices.write[i] = Vector3(0.0, 0.0, 0.0); |
657 | } |
658 | |
659 | indices.resize(6); |
660 | indices.write[0] = 0; |
661 | indices.write[1] = 1; |
662 | indices.write[2] = 2; |
663 | indices.write[3] = 0; |
664 | indices.write[4] = 2; |
665 | indices.write[5] = 3; |
666 | |
667 | Array mesh_array; |
668 | mesh_array.resize(RS::ARRAY_MAX); |
669 | mesh_array[RS::ARRAY_VERTEX] = mesh_vertices; |
670 | mesh_array[RS::ARRAY_NORMAL] = mesh_normals; |
671 | mesh_array[RS::ARRAY_TANGENT] = mesh_tangents; |
672 | mesh_array[RS::ARRAY_COLOR] = mesh_colors; |
673 | mesh_array[RS::ARRAY_TEX_UV] = mesh_uvs; |
674 | mesh_array[RS::ARRAY_INDEX] = indices; |
675 | |
676 | RS::SurfaceData sd; |
677 | RS::get_singleton()->mesh_create_surface_data_from_arrays(&sd, RS::PRIMITIVE_TRIANGLES, mesh_array); |
678 | |
679 | mesh_surface_format = sd.format; |
680 | vertex_buffer = sd.vertex_data; |
681 | attribute_buffer = sd.attribute_data; |
682 | |
683 | sd.material = material; |
684 | |
685 | RS::get_singleton()->mesh_surface_make_offsets_from_format(sd.format, sd.vertex_count, sd.index_count, mesh_surface_offsets, vertex_stride, attrib_stride, skin_stride); |
686 | RS::get_singleton()->mesh_add_surface(mesh, sd); |
687 | set_base(mesh); |
688 | } |
689 | |
690 | SpriteBase3D::~SpriteBase3D() { |
691 | ERR_FAIL_NULL(RenderingServer::get_singleton()); |
692 | RenderingServer::get_singleton()->free(mesh); |
693 | RenderingServer::get_singleton()->free(material); |
694 | } |
695 | |
696 | /////////////////////////////////////////// |
697 | |
698 | void Sprite3D::_draw() { |
699 | if (get_base() != get_mesh()) { |
700 | set_base(get_mesh()); |
701 | } |
702 | if (texture.is_null()) { |
703 | set_base(RID()); |
704 | return; |
705 | } |
706 | Vector2 tsize = texture->get_size(); |
707 | if (tsize.x == 0 || tsize.y == 0) { |
708 | return; |
709 | } |
710 | |
711 | Rect2 base_rect; |
712 | if (region) { |
713 | base_rect = region_rect; |
714 | } else { |
715 | base_rect = Rect2(0, 0, texture->get_width(), texture->get_height()); |
716 | } |
717 | |
718 | Size2 frame_size = base_rect.size / Size2(hframes, vframes); |
719 | Point2 frame_offset = Point2(frame % hframes, frame / hframes) * frame_size; |
720 | |
721 | Point2 dst_offset = get_offset(); |
722 | if (is_centered()) { |
723 | dst_offset -= frame_size / 2.0f; |
724 | } |
725 | |
726 | Rect2 src_rect(base_rect.position + frame_offset, frame_size); |
727 | Rect2 dst_rect(dst_offset, frame_size); |
728 | |
729 | draw_texture_rect(texture, dst_rect, src_rect); |
730 | } |
731 | |
732 | void Sprite3D::set_texture(const Ref<Texture2D> &p_texture) { |
733 | if (p_texture == texture) { |
734 | return; |
735 | } |
736 | if (texture.is_valid()) { |
737 | texture->disconnect(SceneStringNames::get_singleton()->changed, callable_mp((SpriteBase3D *)this, &Sprite3D::_queue_redraw)); |
738 | } |
739 | texture = p_texture; |
740 | if (texture.is_valid()) { |
741 | texture->connect(SceneStringNames::get_singleton()->changed, callable_mp((SpriteBase3D *)this, &Sprite3D::_queue_redraw)); |
742 | } |
743 | |
744 | _queue_redraw(); |
745 | emit_signal(SceneStringNames::get_singleton()->texture_changed); |
746 | } |
747 | |
748 | Ref<Texture2D> Sprite3D::get_texture() const { |
749 | return texture; |
750 | } |
751 | |
752 | void Sprite3D::set_region_enabled(bool p_region) { |
753 | if (p_region == region) { |
754 | return; |
755 | } |
756 | |
757 | region = p_region; |
758 | _queue_redraw(); |
759 | notify_property_list_changed(); |
760 | } |
761 | |
762 | bool Sprite3D::is_region_enabled() const { |
763 | return region; |
764 | } |
765 | |
766 | void Sprite3D::set_region_rect(const Rect2 &p_region_rect) { |
767 | bool changed = region_rect != p_region_rect; |
768 | region_rect = p_region_rect; |
769 | if (region && changed) { |
770 | _queue_redraw(); |
771 | } |
772 | } |
773 | |
774 | Rect2 Sprite3D::get_region_rect() const { |
775 | return region_rect; |
776 | } |
777 | |
778 | void Sprite3D::set_frame(int p_frame) { |
779 | ERR_FAIL_INDEX(p_frame, int64_t(vframes) * hframes); |
780 | |
781 | frame = p_frame; |
782 | |
783 | _queue_redraw(); |
784 | |
785 | emit_signal(SceneStringNames::get_singleton()->frame_changed); |
786 | } |
787 | |
788 | int Sprite3D::get_frame() const { |
789 | return frame; |
790 | } |
791 | |
792 | void Sprite3D::set_frame_coords(const Vector2i &p_coord) { |
793 | ERR_FAIL_INDEX(p_coord.x, hframes); |
794 | ERR_FAIL_INDEX(p_coord.y, vframes); |
795 | |
796 | set_frame(p_coord.y * hframes + p_coord.x); |
797 | } |
798 | |
799 | Vector2i Sprite3D::get_frame_coords() const { |
800 | return Vector2i(frame % hframes, frame / hframes); |
801 | } |
802 | |
803 | void Sprite3D::set_vframes(int p_amount) { |
804 | ERR_FAIL_COND(p_amount < 1); |
805 | vframes = p_amount; |
806 | _queue_redraw(); |
807 | notify_property_list_changed(); |
808 | } |
809 | |
810 | int Sprite3D::get_vframes() const { |
811 | return vframes; |
812 | } |
813 | |
814 | void Sprite3D::set_hframes(int p_amount) { |
815 | ERR_FAIL_COND(p_amount < 1); |
816 | hframes = p_amount; |
817 | _queue_redraw(); |
818 | notify_property_list_changed(); |
819 | } |
820 | |
821 | int Sprite3D::get_hframes() const { |
822 | return hframes; |
823 | } |
824 | |
825 | Rect2 Sprite3D::get_item_rect() const { |
826 | if (texture.is_null()) { |
827 | return Rect2(0, 0, 1, 1); |
828 | } |
829 | |
830 | Size2 s; |
831 | |
832 | if (region) { |
833 | s = region_rect.size; |
834 | } else { |
835 | s = texture->get_size(); |
836 | s = s / Point2(hframes, vframes); |
837 | } |
838 | |
839 | Point2 ofs = get_offset(); |
840 | if (is_centered()) { |
841 | ofs -= s / 2; |
842 | } |
843 | |
844 | if (s == Size2(0, 0)) { |
845 | s = Size2(1, 1); |
846 | } |
847 | |
848 | return Rect2(ofs, s); |
849 | } |
850 | |
851 | void Sprite3D::_validate_property(PropertyInfo &p_property) const { |
852 | if (p_property.name == "frame" ) { |
853 | p_property.hint = PROPERTY_HINT_RANGE; |
854 | p_property.hint_string = "0," + itos(vframes * hframes - 1) + ",1" ; |
855 | p_property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; |
856 | } |
857 | |
858 | if (p_property.name == "frame_coords" ) { |
859 | p_property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; |
860 | } |
861 | |
862 | if (!region && (p_property.name == "region_rect" )) { |
863 | p_property.usage = PROPERTY_USAGE_NO_EDITOR; |
864 | } |
865 | } |
866 | |
867 | void Sprite3D::_bind_methods() { |
868 | ClassDB::bind_method(D_METHOD("set_texture" , "texture" ), &Sprite3D::set_texture); |
869 | ClassDB::bind_method(D_METHOD("get_texture" ), &Sprite3D::get_texture); |
870 | |
871 | ClassDB::bind_method(D_METHOD("set_region_enabled" , "enabled" ), &Sprite3D::set_region_enabled); |
872 | ClassDB::bind_method(D_METHOD("is_region_enabled" ), &Sprite3D::is_region_enabled); |
873 | |
874 | ClassDB::bind_method(D_METHOD("set_region_rect" , "rect" ), &Sprite3D::set_region_rect); |
875 | ClassDB::bind_method(D_METHOD("get_region_rect" ), &Sprite3D::get_region_rect); |
876 | |
877 | ClassDB::bind_method(D_METHOD("set_frame" , "frame" ), &Sprite3D::set_frame); |
878 | ClassDB::bind_method(D_METHOD("get_frame" ), &Sprite3D::get_frame); |
879 | |
880 | ClassDB::bind_method(D_METHOD("set_frame_coords" , "coords" ), &Sprite3D::set_frame_coords); |
881 | ClassDB::bind_method(D_METHOD("get_frame_coords" ), &Sprite3D::get_frame_coords); |
882 | |
883 | ClassDB::bind_method(D_METHOD("set_vframes" , "vframes" ), &Sprite3D::set_vframes); |
884 | ClassDB::bind_method(D_METHOD("get_vframes" ), &Sprite3D::get_vframes); |
885 | |
886 | ClassDB::bind_method(D_METHOD("set_hframes" , "hframes" ), &Sprite3D::set_hframes); |
887 | ClassDB::bind_method(D_METHOD("get_hframes" ), &Sprite3D::get_hframes); |
888 | |
889 | ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture" , PROPERTY_HINT_RESOURCE_TYPE, "Texture" ), "set_texture" , "get_texture" ); |
890 | ADD_GROUP("Animation" , "" ); |
891 | ADD_PROPERTY(PropertyInfo(Variant::INT, "hframes" , PROPERTY_HINT_RANGE, "1,16384,1" ), "set_hframes" , "get_hframes" ); |
892 | ADD_PROPERTY(PropertyInfo(Variant::INT, "vframes" , PROPERTY_HINT_RANGE, "1,16384,1" ), "set_vframes" , "get_vframes" ); |
893 | ADD_PROPERTY(PropertyInfo(Variant::INT, "frame" ), "set_frame" , "get_frame" ); |
894 | ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frame_coords" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_EDITOR), "set_frame_coords" , "get_frame_coords" ); |
895 | ADD_GROUP("Region" , "region_" ); |
896 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "region_enabled" ), "set_region_enabled" , "is_region_enabled" ); |
897 | ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect" , PROPERTY_HINT_NONE, "suffix:px" ), "set_region_rect" , "get_region_rect" ); |
898 | |
899 | ADD_SIGNAL(MethodInfo("frame_changed" )); |
900 | ADD_SIGNAL(MethodInfo("texture_changed" )); |
901 | } |
902 | |
903 | Sprite3D::Sprite3D() { |
904 | } |
905 | |
906 | //////////////////////////////////////// |
907 | |
908 | void AnimatedSprite3D::_draw() { |
909 | if (get_base() != get_mesh()) { |
910 | set_base(get_mesh()); |
911 | } |
912 | |
913 | if (frames.is_null() || !frames->has_animation(animation)) { |
914 | return; |
915 | } |
916 | |
917 | Ref<Texture2D> texture = frames->get_frame_texture(animation, frame); |
918 | if (texture.is_null()) { |
919 | set_base(RID()); |
920 | return; |
921 | } |
922 | Size2 tsize = texture->get_size(); |
923 | if (tsize.x == 0 || tsize.y == 0) { |
924 | return; |
925 | } |
926 | |
927 | Rect2 src_rect; |
928 | src_rect.size = tsize; |
929 | |
930 | Point2 ofs = get_offset(); |
931 | if (is_centered()) { |
932 | ofs -= tsize / 2; |
933 | } |
934 | |
935 | Rect2 dst_rect(ofs, tsize); |
936 | |
937 | draw_texture_rect(texture, dst_rect, src_rect); |
938 | } |
939 | |
940 | void AnimatedSprite3D::_validate_property(PropertyInfo &p_property) const { |
941 | if (!frames.is_valid()) { |
942 | return; |
943 | } |
944 | |
945 | if (p_property.name == "animation" ) { |
946 | List<StringName> names; |
947 | frames->get_animation_list(&names); |
948 | names.sort_custom<StringName::AlphCompare>(); |
949 | |
950 | bool current_found = false; |
951 | bool is_first_element = true; |
952 | |
953 | for (const StringName &E : names) { |
954 | if (!is_first_element) { |
955 | p_property.hint_string += "," ; |
956 | } else { |
957 | is_first_element = false; |
958 | } |
959 | |
960 | p_property.hint_string += String(E); |
961 | if (animation == E) { |
962 | current_found = true; |
963 | } |
964 | } |
965 | |
966 | if (!current_found) { |
967 | if (p_property.hint_string.is_empty()) { |
968 | p_property.hint_string = String(animation); |
969 | } else { |
970 | p_property.hint_string = String(animation) + "," + p_property.hint_string; |
971 | } |
972 | } |
973 | return; |
974 | } |
975 | |
976 | if (p_property.name == "frame" ) { |
977 | if (playing) { |
978 | p_property.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY; |
979 | return; |
980 | } |
981 | |
982 | p_property.hint = PROPERTY_HINT_RANGE; |
983 | if (frames->has_animation(animation) && frames->get_frame_count(animation) > 0) { |
984 | p_property.hint_string = "0," + itos(frames->get_frame_count(animation) - 1) + ",1" ; |
985 | } else { |
986 | // Avoid an error, `hint_string` is required for `PROPERTY_HINT_RANGE`. |
987 | p_property.hint_string = "0,0,1" ; |
988 | } |
989 | p_property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; |
990 | } |
991 | } |
992 | |
993 | void AnimatedSprite3D::_notification(int p_what) { |
994 | switch (p_what) { |
995 | case NOTIFICATION_READY: { |
996 | if (!Engine::get_singleton()->is_editor_hint() && !frames.is_null() && frames->has_animation(autoplay)) { |
997 | play(autoplay); |
998 | } |
999 | } break; |
1000 | |
1001 | case NOTIFICATION_INTERNAL_PROCESS: { |
1002 | if (frames.is_null() || !frames->has_animation(animation)) { |
1003 | return; |
1004 | } |
1005 | |
1006 | double remaining = get_process_delta_time(); |
1007 | int i = 0; |
1008 | while (remaining) { |
1009 | // Animation speed may be changed by animation_finished or frame_changed signals. |
1010 | double speed = frames->get_animation_speed(animation) * speed_scale * custom_speed_scale * frame_speed_scale; |
1011 | double abs_speed = Math::abs(speed); |
1012 | |
1013 | if (speed == 0) { |
1014 | return; // Do nothing. |
1015 | } |
1016 | |
1017 | // Frame count may be changed by animation_finished or frame_changed signals. |
1018 | int fc = frames->get_frame_count(animation); |
1019 | |
1020 | int last_frame = fc - 1; |
1021 | if (!signbit(speed)) { |
1022 | // Forwards. |
1023 | if (frame_progress >= 1.0) { |
1024 | if (frame >= last_frame) { |
1025 | if (frames->get_animation_loop(animation)) { |
1026 | frame = 0; |
1027 | emit_signal("animation_looped" ); |
1028 | } else { |
1029 | frame = last_frame; |
1030 | pause(); |
1031 | emit_signal(SceneStringNames::get_singleton()->animation_finished); |
1032 | return; |
1033 | } |
1034 | } else { |
1035 | frame++; |
1036 | } |
1037 | _calc_frame_speed_scale(); |
1038 | frame_progress = 0.0; |
1039 | _queue_redraw(); |
1040 | emit_signal(SceneStringNames::get_singleton()->frame_changed); |
1041 | } |
1042 | double to_process = MIN((1.0 - frame_progress) / abs_speed, remaining); |
1043 | frame_progress += to_process * abs_speed; |
1044 | remaining -= to_process; |
1045 | } else { |
1046 | // Backwards. |
1047 | if (frame_progress <= 0) { |
1048 | if (frame <= 0) { |
1049 | if (frames->get_animation_loop(animation)) { |
1050 | frame = last_frame; |
1051 | emit_signal("animation_looped" ); |
1052 | } else { |
1053 | frame = 0; |
1054 | pause(); |
1055 | emit_signal(SceneStringNames::get_singleton()->animation_finished); |
1056 | return; |
1057 | } |
1058 | } else { |
1059 | frame--; |
1060 | } |
1061 | _calc_frame_speed_scale(); |
1062 | frame_progress = 1.0; |
1063 | _queue_redraw(); |
1064 | emit_signal(SceneStringNames::get_singleton()->frame_changed); |
1065 | } |
1066 | double to_process = MIN(frame_progress / abs_speed, remaining); |
1067 | frame_progress -= to_process * abs_speed; |
1068 | remaining -= to_process; |
1069 | } |
1070 | |
1071 | i++; |
1072 | if (i > fc) { |
1073 | return; // Prevents freezing if to_process is each time much less than remaining. |
1074 | } |
1075 | } |
1076 | } break; |
1077 | } |
1078 | } |
1079 | |
1080 | void AnimatedSprite3D::set_sprite_frames(const Ref<SpriteFrames> &p_frames) { |
1081 | if (frames == p_frames) { |
1082 | return; |
1083 | } |
1084 | |
1085 | if (frames.is_valid()) { |
1086 | frames->disconnect(SceneStringNames::get_singleton()->changed, callable_mp(this, &AnimatedSprite3D::_res_changed)); |
1087 | } |
1088 | stop(); |
1089 | frames = p_frames; |
1090 | if (frames.is_valid()) { |
1091 | frames->connect(SceneStringNames::get_singleton()->changed, callable_mp(this, &AnimatedSprite3D::_res_changed)); |
1092 | |
1093 | List<StringName> al; |
1094 | frames->get_animation_list(&al); |
1095 | if (al.size() == 0) { |
1096 | set_animation(StringName()); |
1097 | autoplay = String(); |
1098 | } else { |
1099 | if (!frames->has_animation(animation)) { |
1100 | set_animation(al[0]); |
1101 | } |
1102 | if (!frames->has_animation(autoplay)) { |
1103 | autoplay = String(); |
1104 | } |
1105 | } |
1106 | } |
1107 | |
1108 | notify_property_list_changed(); |
1109 | _queue_redraw(); |
1110 | update_configuration_warnings(); |
1111 | emit_signal("sprite_frames_changed" ); |
1112 | } |
1113 | |
1114 | Ref<SpriteFrames> AnimatedSprite3D::get_sprite_frames() const { |
1115 | return frames; |
1116 | } |
1117 | |
1118 | void AnimatedSprite3D::set_frame(int p_frame) { |
1119 | set_frame_and_progress(p_frame, signbit(get_playing_speed()) ? 1.0 : 0.0); |
1120 | } |
1121 | |
1122 | int AnimatedSprite3D::get_frame() const { |
1123 | return frame; |
1124 | } |
1125 | |
1126 | void AnimatedSprite3D::set_frame_progress(real_t p_progress) { |
1127 | frame_progress = p_progress; |
1128 | } |
1129 | |
1130 | real_t AnimatedSprite3D::get_frame_progress() const { |
1131 | return frame_progress; |
1132 | } |
1133 | |
1134 | void AnimatedSprite3D::set_frame_and_progress(int p_frame, real_t p_progress) { |
1135 | if (frames.is_null()) { |
1136 | return; |
1137 | } |
1138 | |
1139 | bool has_animation = frames->has_animation(animation); |
1140 | int end_frame = has_animation ? MAX(0, frames->get_frame_count(animation) - 1) : 0; |
1141 | bool is_changed = frame != p_frame; |
1142 | |
1143 | if (p_frame < 0) { |
1144 | frame = 0; |
1145 | } else if (has_animation && p_frame > end_frame) { |
1146 | frame = end_frame; |
1147 | } else { |
1148 | frame = p_frame; |
1149 | } |
1150 | |
1151 | _calc_frame_speed_scale(); |
1152 | frame_progress = p_progress; |
1153 | |
1154 | if (!is_changed) { |
1155 | return; // No change, don't redraw. |
1156 | } |
1157 | _queue_redraw(); |
1158 | emit_signal(SceneStringNames::get_singleton()->frame_changed); |
1159 | } |
1160 | |
1161 | void AnimatedSprite3D::set_speed_scale(float p_speed_scale) { |
1162 | speed_scale = p_speed_scale; |
1163 | } |
1164 | |
1165 | float AnimatedSprite3D::get_speed_scale() const { |
1166 | return speed_scale; |
1167 | } |
1168 | |
1169 | float AnimatedSprite3D::get_playing_speed() const { |
1170 | if (!playing) { |
1171 | return 0; |
1172 | } |
1173 | return speed_scale * custom_speed_scale; |
1174 | } |
1175 | |
1176 | Rect2 AnimatedSprite3D::get_item_rect() const { |
1177 | if (frames.is_null() || !frames->has_animation(animation)) { |
1178 | return Rect2(0, 0, 1, 1); |
1179 | } |
1180 | if (frame < 0 || frame >= frames->get_frame_count(animation)) { |
1181 | return Rect2(0, 0, 1, 1); |
1182 | } |
1183 | |
1184 | Ref<Texture2D> t; |
1185 | if (animation) { |
1186 | t = frames->get_frame_texture(animation, frame); |
1187 | } |
1188 | if (t.is_null()) { |
1189 | return Rect2(0, 0, 1, 1); |
1190 | } |
1191 | Size2 s = t->get_size(); |
1192 | |
1193 | Point2 ofs = get_offset(); |
1194 | if (centered) { |
1195 | ofs -= s / 2; |
1196 | } |
1197 | |
1198 | if (s == Size2(0, 0)) { |
1199 | s = Size2(1, 1); |
1200 | } |
1201 | |
1202 | return Rect2(ofs, s); |
1203 | } |
1204 | |
1205 | void AnimatedSprite3D::_res_changed() { |
1206 | set_frame_and_progress(frame, frame_progress); |
1207 | _queue_redraw(); |
1208 | notify_property_list_changed(); |
1209 | } |
1210 | |
1211 | bool AnimatedSprite3D::is_playing() const { |
1212 | return playing; |
1213 | } |
1214 | |
1215 | void AnimatedSprite3D::set_autoplay(const String &p_name) { |
1216 | if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) { |
1217 | WARN_PRINT("Setting autoplay after the node has been added to the scene has no effect." ); |
1218 | } |
1219 | |
1220 | autoplay = p_name; |
1221 | } |
1222 | |
1223 | String AnimatedSprite3D::get_autoplay() const { |
1224 | return autoplay; |
1225 | } |
1226 | |
1227 | void AnimatedSprite3D::play(const StringName &p_name, float p_custom_scale, bool p_from_end) { |
1228 | StringName name = p_name; |
1229 | |
1230 | if (name == StringName()) { |
1231 | name = animation; |
1232 | } |
1233 | |
1234 | ERR_FAIL_NULL_MSG(frames, vformat("There is no animation with name '%s'." , name)); |
1235 | ERR_FAIL_COND_MSG(!frames->get_animation_names().has(name), vformat("There is no animation with name '%s'." , name)); |
1236 | |
1237 | if (frames->get_frame_count(name) == 0) { |
1238 | return; |
1239 | } |
1240 | |
1241 | playing = true; |
1242 | custom_speed_scale = p_custom_scale; |
1243 | |
1244 | int end_frame = MAX(0, frames->get_frame_count(animation) - 1); |
1245 | if (name != animation) { |
1246 | animation = name; |
1247 | if (p_from_end) { |
1248 | set_frame_and_progress(end_frame, 1.0); |
1249 | } else { |
1250 | set_frame_and_progress(0, 0.0); |
1251 | } |
1252 | emit_signal("animation_changed" ); |
1253 | } else { |
1254 | bool is_backward = signbit(speed_scale * custom_speed_scale); |
1255 | if (p_from_end && is_backward && frame == 0 && frame_progress <= 0.0) { |
1256 | set_frame_and_progress(end_frame, 1.0); |
1257 | } else if (!p_from_end && !is_backward && frame == end_frame && frame_progress >= 1.0) { |
1258 | set_frame_and_progress(0, 0.0); |
1259 | } |
1260 | } |
1261 | |
1262 | set_process_internal(true); |
1263 | notify_property_list_changed(); |
1264 | _queue_redraw(); |
1265 | } |
1266 | |
1267 | void AnimatedSprite3D::play_backwards(const StringName &p_name) { |
1268 | play(p_name, -1, true); |
1269 | } |
1270 | |
1271 | void AnimatedSprite3D::_stop_internal(bool p_reset) { |
1272 | playing = false; |
1273 | if (p_reset) { |
1274 | custom_speed_scale = 1.0; |
1275 | set_frame_and_progress(0, 0.0); |
1276 | } |
1277 | notify_property_list_changed(); |
1278 | set_process_internal(false); |
1279 | } |
1280 | |
1281 | void AnimatedSprite3D::pause() { |
1282 | _stop_internal(false); |
1283 | } |
1284 | |
1285 | void AnimatedSprite3D::stop() { |
1286 | _stop_internal(true); |
1287 | } |
1288 | |
1289 | double AnimatedSprite3D::_get_frame_duration() { |
1290 | if (frames.is_valid() && frames->has_animation(animation)) { |
1291 | return frames->get_frame_duration(animation, frame); |
1292 | } |
1293 | return 1.0; |
1294 | } |
1295 | |
1296 | void AnimatedSprite3D::_calc_frame_speed_scale() { |
1297 | frame_speed_scale = 1.0 / _get_frame_duration(); |
1298 | } |
1299 | |
1300 | void AnimatedSprite3D::set_animation(const StringName &p_name) { |
1301 | if (animation == p_name) { |
1302 | return; |
1303 | } |
1304 | |
1305 | animation = p_name; |
1306 | |
1307 | emit_signal("animation_changed" ); |
1308 | |
1309 | if (frames == nullptr) { |
1310 | animation = StringName(); |
1311 | stop(); |
1312 | ERR_FAIL_MSG(vformat("There is no animation with name '%s'." , p_name)); |
1313 | } |
1314 | |
1315 | int frame_count = frames->get_frame_count(animation); |
1316 | if (animation == StringName() || frame_count == 0) { |
1317 | stop(); |
1318 | return; |
1319 | } else if (!frames->get_animation_names().has(animation)) { |
1320 | animation = StringName(); |
1321 | stop(); |
1322 | ERR_FAIL_MSG(vformat("There is no animation with name '%s'." , p_name)); |
1323 | } |
1324 | |
1325 | if (signbit(get_playing_speed())) { |
1326 | set_frame_and_progress(frame_count - 1, 1.0); |
1327 | } else { |
1328 | set_frame_and_progress(0, 0.0); |
1329 | } |
1330 | |
1331 | notify_property_list_changed(); |
1332 | _queue_redraw(); |
1333 | } |
1334 | |
1335 | StringName AnimatedSprite3D::get_animation() const { |
1336 | return animation; |
1337 | } |
1338 | |
1339 | PackedStringArray AnimatedSprite3D::get_configuration_warnings() const { |
1340 | PackedStringArray warnings = SpriteBase3D::get_configuration_warnings(); |
1341 | if (frames.is_null()) { |
1342 | warnings.push_back(RTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite3D to display frames." )); |
1343 | } |
1344 | return warnings; |
1345 | } |
1346 | |
1347 | void AnimatedSprite3D::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { |
1348 | if (p_idx == 0 && p_function == "play" && frames.is_valid()) { |
1349 | List<StringName> al; |
1350 | frames->get_animation_list(&al); |
1351 | for (const StringName &name : al) { |
1352 | r_options->push_back(String(name).quote()); |
1353 | } |
1354 | } |
1355 | Node::get_argument_options(p_function, p_idx, r_options); |
1356 | } |
1357 | |
1358 | #ifndef DISABLE_DEPRECATED |
1359 | bool AnimatedSprite3D::_set(const StringName &p_name, const Variant &p_value) { |
1360 | if ((p_name == SNAME("frames" ))) { |
1361 | set_sprite_frames(p_value); |
1362 | return true; |
1363 | } |
1364 | return false; |
1365 | } |
1366 | #endif |
1367 | void AnimatedSprite3D::_bind_methods() { |
1368 | ClassDB::bind_method(D_METHOD("set_sprite_frames" , "sprite_frames" ), &AnimatedSprite3D::set_sprite_frames); |
1369 | ClassDB::bind_method(D_METHOD("get_sprite_frames" ), &AnimatedSprite3D::get_sprite_frames); |
1370 | |
1371 | ClassDB::bind_method(D_METHOD("set_animation" , "name" ), &AnimatedSprite3D::set_animation); |
1372 | ClassDB::bind_method(D_METHOD("get_animation" ), &AnimatedSprite3D::get_animation); |
1373 | |
1374 | ClassDB::bind_method(D_METHOD("set_autoplay" , "name" ), &AnimatedSprite3D::set_autoplay); |
1375 | ClassDB::bind_method(D_METHOD("get_autoplay" ), &AnimatedSprite3D::get_autoplay); |
1376 | |
1377 | ClassDB::bind_method(D_METHOD("is_playing" ), &AnimatedSprite3D::is_playing); |
1378 | |
1379 | ClassDB::bind_method(D_METHOD("play" , "name" , "custom_speed" , "from_end" ), &AnimatedSprite3D::play, DEFVAL(StringName()), DEFVAL(1.0), DEFVAL(false)); |
1380 | ClassDB::bind_method(D_METHOD("play_backwards" , "name" ), &AnimatedSprite3D::play_backwards, DEFVAL(StringName())); |
1381 | ClassDB::bind_method(D_METHOD("pause" ), &AnimatedSprite3D::pause); |
1382 | ClassDB::bind_method(D_METHOD("stop" ), &AnimatedSprite3D::stop); |
1383 | |
1384 | ClassDB::bind_method(D_METHOD("set_frame" , "frame" ), &AnimatedSprite3D::set_frame); |
1385 | ClassDB::bind_method(D_METHOD("get_frame" ), &AnimatedSprite3D::get_frame); |
1386 | |
1387 | ClassDB::bind_method(D_METHOD("set_frame_progress" , "progress" ), &AnimatedSprite3D::set_frame_progress); |
1388 | ClassDB::bind_method(D_METHOD("get_frame_progress" ), &AnimatedSprite3D::get_frame_progress); |
1389 | |
1390 | ClassDB::bind_method(D_METHOD("set_frame_and_progress" , "frame" , "progress" ), &AnimatedSprite3D::set_frame_and_progress); |
1391 | |
1392 | ClassDB::bind_method(D_METHOD("set_speed_scale" , "speed_scale" ), &AnimatedSprite3D::set_speed_scale); |
1393 | ClassDB::bind_method(D_METHOD("get_speed_scale" ), &AnimatedSprite3D::get_speed_scale); |
1394 | ClassDB::bind_method(D_METHOD("get_playing_speed" ), &AnimatedSprite3D::get_playing_speed); |
1395 | |
1396 | ClassDB::bind_method(D_METHOD("_res_changed" ), &AnimatedSprite3D::_res_changed); |
1397 | |
1398 | ADD_SIGNAL(MethodInfo("sprite_frames_changed" )); |
1399 | ADD_SIGNAL(MethodInfo("animation_changed" )); |
1400 | ADD_SIGNAL(MethodInfo("frame_changed" )); |
1401 | ADD_SIGNAL(MethodInfo("animation_looped" )); |
1402 | ADD_SIGNAL(MethodInfo("animation_finished" )); |
1403 | |
1404 | ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "sprite_frames" , PROPERTY_HINT_RESOURCE_TYPE, "SpriteFrames" ), "set_sprite_frames" , "get_sprite_frames" ); |
1405 | ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation" , PROPERTY_HINT_ENUM, "" ), "set_animation" , "get_animation" ); |
1406 | ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "autoplay" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_NO_EDITOR), "set_autoplay" , "get_autoplay" ); |
1407 | ADD_PROPERTY(PropertyInfo(Variant::INT, "frame" ), "set_frame" , "get_frame" ); |
1408 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frame_progress" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_NO_EDITOR), "set_frame_progress" , "get_frame_progress" ); |
1409 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale" ), "set_speed_scale" , "get_speed_scale" ); |
1410 | } |
1411 | |
1412 | AnimatedSprite3D::AnimatedSprite3D() { |
1413 | } |
1414 | |