1 /*************************************************************************/
2 /* sprite_3d.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
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 #include "core/core_string_names.h"
33 #include "scene/scene_string_names.h"
34
_get_color_accum()35 Color SpriteBase3D::_get_color_accum() {
36
37 if (!color_dirty)
38 return color_accum;
39
40 if (parent_sprite)
41 color_accum = parent_sprite->_get_color_accum();
42 else
43 color_accum = Color(1, 1, 1, 1);
44
45 color_accum.r *= modulate.r;
46 color_accum.g *= modulate.g;
47 color_accum.b *= modulate.b;
48 color_accum.a *= modulate.a;
49 color_dirty = false;
50 return color_accum;
51 }
52
_propagate_color_changed()53 void SpriteBase3D::_propagate_color_changed() {
54
55 if (color_dirty)
56 return;
57
58 color_dirty = true;
59 _queue_update();
60
61 for (List<SpriteBase3D *>::Element *E = children.front(); E; E = E->next()) {
62
63 E->get()->_propagate_color_changed();
64 }
65 }
66
_notification(int p_what)67 void SpriteBase3D::_notification(int p_what) {
68
69 if (p_what == NOTIFICATION_ENTER_TREE) {
70
71 if (!pending_update)
72 _im_update();
73
74 parent_sprite = Object::cast_to<SpriteBase3D>(get_parent());
75 if (parent_sprite) {
76 pI = parent_sprite->children.push_back(this);
77 }
78 }
79
80 if (p_what == NOTIFICATION_EXIT_TREE) {
81
82 if (parent_sprite) {
83
84 parent_sprite->children.erase(pI);
85 pI = NULL;
86 parent_sprite = NULL;
87 }
88 }
89 }
90
set_centered(bool p_center)91 void SpriteBase3D::set_centered(bool p_center) {
92
93 centered = p_center;
94 _queue_update();
95 }
96
is_centered() const97 bool SpriteBase3D::is_centered() const {
98
99 return centered;
100 }
101
set_offset(const Point2 & p_offset)102 void SpriteBase3D::set_offset(const Point2 &p_offset) {
103
104 offset = p_offset;
105 _queue_update();
106 }
get_offset() const107 Point2 SpriteBase3D::get_offset() const {
108
109 return offset;
110 }
111
set_flip_h(bool p_flip)112 void SpriteBase3D::set_flip_h(bool p_flip) {
113
114 hflip = p_flip;
115 _queue_update();
116 }
is_flipped_h() const117 bool SpriteBase3D::is_flipped_h() const {
118
119 return hflip;
120 }
121
set_flip_v(bool p_flip)122 void SpriteBase3D::set_flip_v(bool p_flip) {
123
124 vflip = p_flip;
125 _queue_update();
126 }
is_flipped_v() const127 bool SpriteBase3D::is_flipped_v() const {
128
129 return vflip;
130 }
131
set_modulate(const Color & p_color)132 void SpriteBase3D::set_modulate(const Color &p_color) {
133
134 modulate = p_color;
135 _propagate_color_changed();
136 _queue_update();
137 }
138
get_modulate() const139 Color SpriteBase3D::get_modulate() const {
140
141 return modulate;
142 }
143
set_pixel_size(float p_amount)144 void SpriteBase3D::set_pixel_size(float p_amount) {
145
146 pixel_size = p_amount;
147 _queue_update();
148 }
get_pixel_size() const149 float SpriteBase3D::get_pixel_size() const {
150
151 return pixel_size;
152 }
153
set_opacity(float p_amount)154 void SpriteBase3D::set_opacity(float p_amount) {
155
156 opacity = p_amount;
157 _queue_update();
158 }
get_opacity() const159 float SpriteBase3D::get_opacity() const {
160
161 return opacity;
162 }
163
set_axis(Vector3::Axis p_axis)164 void SpriteBase3D::set_axis(Vector3::Axis p_axis) {
165
166 ERR_FAIL_INDEX(p_axis, 3);
167 axis = p_axis;
168 _queue_update();
169 }
get_axis() const170 Vector3::Axis SpriteBase3D::get_axis() const {
171
172 return axis;
173 }
174
_im_update()175 void SpriteBase3D::_im_update() {
176
177 _draw();
178
179 pending_update = false;
180
181 //texture->draw_rect_region(ci,dst_rect,src_rect,modulate);
182 }
183
_queue_update()184 void SpriteBase3D::_queue_update() {
185
186 if (pending_update)
187 return;
188
189 triangle_mesh.unref();
190 update_gizmo();
191
192 pending_update = true;
193 call_deferred(SceneStringNames::get_singleton()->_im_update);
194 }
195
get_aabb() const196 AABB SpriteBase3D::get_aabb() const {
197
198 return aabb;
199 }
get_faces(uint32_t p_usage_flags) const200 PoolVector<Face3> SpriteBase3D::get_faces(uint32_t p_usage_flags) const {
201
202 return PoolVector<Face3>();
203 }
204
generate_triangle_mesh() const205 Ref<TriangleMesh> SpriteBase3D::generate_triangle_mesh() const {
206 if (triangle_mesh.is_valid())
207 return triangle_mesh;
208
209 PoolVector<Vector3> faces;
210 faces.resize(6);
211 PoolVector<Vector3>::Write facesw = faces.write();
212
213 Rect2 final_rect = get_item_rect();
214
215 if (final_rect.size.x == 0 || final_rect.size.y == 0)
216 return Ref<TriangleMesh>();
217
218 float pixel_size = get_pixel_size();
219
220 Vector2 vertices[4] = {
221
222 (final_rect.position + Vector2(0, final_rect.size.y)) * pixel_size,
223 (final_rect.position + final_rect.size) * pixel_size,
224 (final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size,
225 final_rect.position * pixel_size,
226
227 };
228
229 int x_axis = ((axis + 1) % 3);
230 int y_axis = ((axis + 2) % 3);
231
232 if (axis != Vector3::AXIS_Z) {
233 SWAP(x_axis, y_axis);
234
235 for (int i = 0; i < 4; i++) {
236 if (axis == Vector3::AXIS_Y) {
237 vertices[i].y = -vertices[i].y;
238 } else if (axis == Vector3::AXIS_X) {
239 vertices[i].x = -vertices[i].x;
240 }
241 }
242 }
243
244 static const int indices[6] = {
245 0, 1, 2,
246 0, 2, 3
247 };
248
249 for (int j = 0; j < 6; j++) {
250 int i = indices[j];
251 Vector3 vtx;
252 vtx[x_axis] = vertices[i][0];
253 vtx[y_axis] = vertices[i][1];
254 facesw[j] = vtx;
255 }
256
257 facesw.release();
258
259 triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh));
260 triangle_mesh->create(faces);
261
262 return triangle_mesh;
263 }
264
set_draw_flag(DrawFlags p_flag,bool p_enable)265 void SpriteBase3D::set_draw_flag(DrawFlags p_flag, bool p_enable) {
266
267 ERR_FAIL_INDEX(p_flag, FLAG_MAX);
268 flags[p_flag] = p_enable;
269 _queue_update();
270 }
271
get_draw_flag(DrawFlags p_flag) const272 bool SpriteBase3D::get_draw_flag(DrawFlags p_flag) const {
273 ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false);
274 return flags[p_flag];
275 }
276
set_alpha_cut_mode(AlphaCutMode p_mode)277 void SpriteBase3D::set_alpha_cut_mode(AlphaCutMode p_mode) {
278
279 ERR_FAIL_INDEX(p_mode, 3);
280 alpha_cut = p_mode;
281 _queue_update();
282 }
283
get_alpha_cut_mode() const284 SpriteBase3D::AlphaCutMode SpriteBase3D::get_alpha_cut_mode() const {
285
286 return alpha_cut;
287 }
288
set_billboard_mode(SpatialMaterial::BillboardMode p_mode)289 void SpriteBase3D::set_billboard_mode(SpatialMaterial::BillboardMode p_mode) {
290
291 ERR_FAIL_INDEX(p_mode, 3);
292 billboard_mode = p_mode;
293 _queue_update();
294 }
295
get_billboard_mode() const296 SpatialMaterial::BillboardMode SpriteBase3D::get_billboard_mode() const {
297
298 return billboard_mode;
299 }
300
_bind_methods()301 void SpriteBase3D::_bind_methods() {
302
303 ClassDB::bind_method(D_METHOD("set_centered", "centered"), &SpriteBase3D::set_centered);
304 ClassDB::bind_method(D_METHOD("is_centered"), &SpriteBase3D::is_centered);
305
306 ClassDB::bind_method(D_METHOD("set_offset", "offset"), &SpriteBase3D::set_offset);
307 ClassDB::bind_method(D_METHOD("get_offset"), &SpriteBase3D::get_offset);
308
309 ClassDB::bind_method(D_METHOD("set_flip_h", "flip_h"), &SpriteBase3D::set_flip_h);
310 ClassDB::bind_method(D_METHOD("is_flipped_h"), &SpriteBase3D::is_flipped_h);
311
312 ClassDB::bind_method(D_METHOD("set_flip_v", "flip_v"), &SpriteBase3D::set_flip_v);
313 ClassDB::bind_method(D_METHOD("is_flipped_v"), &SpriteBase3D::is_flipped_v);
314
315 ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &SpriteBase3D::set_modulate);
316 ClassDB::bind_method(D_METHOD("get_modulate"), &SpriteBase3D::get_modulate);
317
318 ClassDB::bind_method(D_METHOD("set_opacity", "opacity"), &SpriteBase3D::set_opacity);
319 ClassDB::bind_method(D_METHOD("get_opacity"), &SpriteBase3D::get_opacity);
320
321 ClassDB::bind_method(D_METHOD("set_pixel_size", "pixel_size"), &SpriteBase3D::set_pixel_size);
322 ClassDB::bind_method(D_METHOD("get_pixel_size"), &SpriteBase3D::get_pixel_size);
323
324 ClassDB::bind_method(D_METHOD("set_axis", "axis"), &SpriteBase3D::set_axis);
325 ClassDB::bind_method(D_METHOD("get_axis"), &SpriteBase3D::get_axis);
326
327 ClassDB::bind_method(D_METHOD("set_draw_flag", "flag", "enabled"), &SpriteBase3D::set_draw_flag);
328 ClassDB::bind_method(D_METHOD("get_draw_flag", "flag"), &SpriteBase3D::get_draw_flag);
329
330 ClassDB::bind_method(D_METHOD("set_alpha_cut_mode", "mode"), &SpriteBase3D::set_alpha_cut_mode);
331 ClassDB::bind_method(D_METHOD("get_alpha_cut_mode"), &SpriteBase3D::get_alpha_cut_mode);
332
333 ClassDB::bind_method(D_METHOD("set_billboard_mode", "mode"), &SpriteBase3D::set_billboard_mode);
334 ClassDB::bind_method(D_METHOD("get_billboard_mode"), &SpriteBase3D::get_billboard_mode);
335
336 ClassDB::bind_method(D_METHOD("get_item_rect"), &SpriteBase3D::get_item_rect);
337 ClassDB::bind_method(D_METHOD("generate_triangle_mesh"), &SpriteBase3D::generate_triangle_mesh);
338
339 ClassDB::bind_method(D_METHOD("_queue_update"), &SpriteBase3D::_queue_update);
340 ClassDB::bind_method(D_METHOD("_im_update"), &SpriteBase3D::_im_update);
341
342 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered");
343 ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
344 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "is_flipped_h");
345 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v"), "set_flip_v", "is_flipped_v");
346 ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate");
347 ADD_PROPERTY(PropertyInfo(Variant::REAL, "opacity", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_opacity", "get_opacity");
348 ADD_PROPERTY(PropertyInfo(Variant::REAL, "pixel_size", PROPERTY_HINT_RANGE, "0.0001,128,0.0001"), "set_pixel_size", "get_pixel_size");
349 ADD_PROPERTY(PropertyInfo(Variant::INT, "axis", PROPERTY_HINT_ENUM, "X-Axis,Y-Axis,Z-Axis"), "set_axis", "get_axis");
350 ADD_GROUP("Flags", "");
351 ADD_PROPERTY(PropertyInfo(Variant::INT, "billboard", PROPERTY_HINT_ENUM, "Disabled,Enabled,Y-Billboard"), "set_billboard_mode", "get_billboard_mode");
352 ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "transparent"), "set_draw_flag", "get_draw_flag", FLAG_TRANSPARENT);
353 ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "shaded"), "set_draw_flag", "get_draw_flag", FLAG_SHADED);
354 ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "double_sided"), "set_draw_flag", "get_draw_flag", FLAG_DOUBLE_SIDED);
355 ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_cut", PROPERTY_HINT_ENUM, "Disabled,Discard,Opaque Pre-Pass"), "set_alpha_cut_mode", "get_alpha_cut_mode");
356
357 BIND_ENUM_CONSTANT(FLAG_TRANSPARENT);
358 BIND_ENUM_CONSTANT(FLAG_SHADED);
359 BIND_ENUM_CONSTANT(FLAG_DOUBLE_SIDED);
360 BIND_ENUM_CONSTANT(FLAG_MAX);
361
362 BIND_ENUM_CONSTANT(ALPHA_CUT_DISABLED);
363 BIND_ENUM_CONSTANT(ALPHA_CUT_DISCARD);
364 BIND_ENUM_CONSTANT(ALPHA_CUT_OPAQUE_PREPASS);
365 }
366
SpriteBase3D()367 SpriteBase3D::SpriteBase3D() {
368
369 color_dirty = true;
370 centered = true;
371 hflip = false;
372 vflip = false;
373 parent_sprite = NULL;
374 pI = NULL;
375
376 for (int i = 0; i < FLAG_MAX; i++)
377 flags[i] = i == FLAG_TRANSPARENT || i == FLAG_DOUBLE_SIDED;
378
379 alpha_cut = ALPHA_CUT_DISABLED;
380 billboard_mode = SpatialMaterial::BILLBOARD_DISABLED;
381 axis = Vector3::AXIS_Z;
382 pixel_size = 0.01;
383 modulate = Color(1, 1, 1, 1);
384 pending_update = false;
385 opacity = 1.0;
386
387 material = VisualServer::get_singleton()->material_create();
388 // Set defaults for material, names need to match up those in SpatialMaterial
389 VS::get_singleton()->material_set_param(material, "albedo", Color(1, 1, 1, 1));
390 VS::get_singleton()->material_set_param(material, "specular", 0.5);
391 VS::get_singleton()->material_set_param(material, "metallic", 0.0);
392 VS::get_singleton()->material_set_param(material, "roughness", 1.0);
393 VS::get_singleton()->material_set_param(material, "uv1_offset", Vector3(0, 0, 0));
394 VS::get_singleton()->material_set_param(material, "uv1_scale", Vector3(1, 1, 1));
395 VS::get_singleton()->material_set_param(material, "uv2_offset", Vector3(0, 0, 0));
396 VS::get_singleton()->material_set_param(material, "uv2_scale", Vector3(1, 1, 1));
397
398 mesh = VisualServer::get_singleton()->mesh_create();
399
400 PoolVector3Array mesh_vertices;
401 PoolVector3Array mesh_normals;
402 PoolRealArray mesh_tangents;
403 PoolColorArray mesh_colors;
404 PoolVector2Array mesh_uvs;
405
406 mesh_vertices.resize(4);
407 mesh_normals.resize(4);
408 mesh_tangents.resize(16);
409 mesh_colors.resize(4);
410 mesh_uvs.resize(4);
411
412 // create basic mesh and store format information
413 for (int i = 0; i < 4; i++) {
414 mesh_normals.write()[i] = Vector3(0.0, 0.0, 0.0);
415 mesh_tangents.write()[i * 4 + 0] = 0.0;
416 mesh_tangents.write()[i * 4 + 1] = 0.0;
417 mesh_tangents.write()[i * 4 + 2] = 0.0;
418 mesh_tangents.write()[i * 4 + 3] = 0.0;
419 mesh_colors.write()[i] = Color(1.0, 1.0, 1.0, 1.0);
420 mesh_uvs.write()[i] = Vector2(0.0, 0.0);
421 mesh_vertices.write()[i] = Vector3(0.0, 0.0, 0.0);
422 }
423
424 Array mesh_array;
425 mesh_array.resize(VS::ARRAY_MAX);
426 mesh_array[VS::ARRAY_VERTEX] = mesh_vertices;
427 mesh_array[VS::ARRAY_NORMAL] = mesh_normals;
428 mesh_array[VS::ARRAY_TANGENT] = mesh_tangents;
429 mesh_array[VS::ARRAY_COLOR] = mesh_colors;
430 mesh_array[VS::ARRAY_TEX_UV] = mesh_uvs;
431
432 VS::get_singleton()->mesh_add_surface_from_arrays(mesh, VS::PRIMITIVE_TRIANGLE_FAN, mesh_array);
433 const int surface_vertex_len = VS::get_singleton()->mesh_surface_get_array_len(mesh, 0);
434 const int surface_index_len = VS::get_singleton()->mesh_surface_get_array_index_len(mesh, 0);
435
436 mesh_surface_format = VS::get_singleton()->mesh_surface_get_format(mesh, 0);
437 mesh_buffer = VS::get_singleton()->mesh_surface_get_array(mesh, 0);
438 mesh_stride = VS::get_singleton()->mesh_surface_make_offsets_from_format(mesh_surface_format, surface_vertex_len, surface_index_len, mesh_surface_offsets);
439 }
440
~SpriteBase3D()441 SpriteBase3D::~SpriteBase3D() {
442
443 VisualServer::get_singleton()->free(mesh);
444 VisualServer::get_singleton()->free(material);
445 }
446
447 ///////////////////////////////////////////
448
_draw()449 void Sprite3D::_draw() {
450
451 set_base(RID());
452
453 if (!texture.is_valid())
454 return;
455 Vector2 tsize = texture->get_size();
456 if (tsize.x == 0 || tsize.y == 0)
457 return;
458
459 Rect2 base_rect;
460 if (region)
461 base_rect = region_rect;
462 else
463 base_rect = Rect2(0, 0, texture->get_width(), texture->get_height());
464
465 Size2 frame_size = base_rect.size / Size2(hframes, vframes);
466 Point2 frame_offset = Point2(frame % hframes, frame / hframes);
467 frame_offset *= frame_size;
468
469 Point2 dest_offset = get_offset();
470 if (is_centered())
471 dest_offset -= frame_size / 2;
472
473 Rect2 src_rect(base_rect.position + frame_offset, frame_size);
474 Rect2 final_dst_rect(dest_offset, frame_size);
475 Rect2 final_rect;
476 Rect2 final_src_rect;
477 if (!texture->get_rect_region(final_dst_rect, src_rect, final_rect, final_src_rect))
478 return;
479
480 if (final_rect.size.x == 0 || final_rect.size.y == 0)
481 return;
482
483 Color color = _get_color_accum();
484 color.a *= get_opacity();
485
486 float pixel_size = get_pixel_size();
487
488 Vector2 vertices[4] = {
489
490 (final_rect.position + Vector2(0, final_rect.size.y)) * pixel_size,
491 (final_rect.position + final_rect.size) * pixel_size,
492 (final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size,
493 final_rect.position * pixel_size,
494
495 };
496
497 Vector2 src_tsize = tsize;
498
499 // Properly setup UVs for impostor textures (AtlasTexture).
500 Ref<AtlasTexture> atlas_tex = texture;
501 if (atlas_tex != NULL) {
502 src_tsize[0] = atlas_tex->get_atlas()->get_width();
503 src_tsize[1] = atlas_tex->get_atlas()->get_height();
504 }
505
506 Vector2 uvs[4] = {
507 final_src_rect.position / src_tsize,
508 (final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / src_tsize,
509 (final_src_rect.position + final_src_rect.size) / src_tsize,
510 (final_src_rect.position + Vector2(0, final_src_rect.size.y)) / src_tsize,
511 };
512
513 if (is_flipped_h()) {
514 SWAP(uvs[0], uvs[1]);
515 SWAP(uvs[2], uvs[3]);
516 }
517 if (is_flipped_v()) {
518
519 SWAP(uvs[0], uvs[3]);
520 SWAP(uvs[1], uvs[2]);
521 }
522
523 Vector3 normal;
524 int axis = get_axis();
525 normal[axis] = 1.0;
526
527 Plane tangent;
528 if (axis == Vector3::AXIS_X) {
529 tangent = Plane(0, 0, -1, 1);
530 } else {
531 tangent = Plane(1, 0, 0, 1);
532 }
533
534 int x_axis = ((axis + 1) % 3);
535 int y_axis = ((axis + 2) % 3);
536
537 if (axis != Vector3::AXIS_Z) {
538 SWAP(x_axis, y_axis);
539
540 for (int i = 0; i < 4; i++) {
541 //uvs[i] = Vector2(1.0,1.0)-uvs[i];
542 //SWAP(vertices[i].x,vertices[i].y);
543 if (axis == Vector3::AXIS_Y) {
544 vertices[i].y = -vertices[i].y;
545 } else if (axis == Vector3::AXIS_X) {
546 vertices[i].x = -vertices[i].x;
547 }
548 }
549 }
550
551 AABB aabb;
552
553 // Buffer is using default compression, so everything except position is compressed
554 PoolVector<uint8_t>::Write write_buffer = mesh_buffer.write();
555
556 int8_t v_normal[4] = {
557 (int8_t)CLAMP(normal.x * 127, -128, 127),
558 (int8_t)CLAMP(normal.y * 127, -128, 127),
559 (int8_t)CLAMP(normal.z * 127, -128, 127),
560 0,
561 };
562
563 int8_t v_tangent[4] = {
564 (int8_t)CLAMP(tangent.normal.x * 127, -128, 127),
565 (int8_t)CLAMP(tangent.normal.y * 127, -128, 127),
566 (int8_t)CLAMP(tangent.normal.z * 127, -128, 127),
567 (int8_t)CLAMP(tangent.d * 127, -128, 127)
568 };
569
570 uint8_t v_color[4] = {
571 (uint8_t)CLAMP(int(color.r * 255.0), 0, 255),
572 (uint8_t)CLAMP(int(color.g * 255.0), 0, 255),
573 (uint8_t)CLAMP(int(color.b * 255.0), 0, 255),
574 (uint8_t)CLAMP(int(color.a * 255.0), 0, 255)
575 };
576
577 for (int i = 0; i < 4; i++) {
578 Vector3 vtx;
579 vtx[x_axis] = vertices[i][0];
580 vtx[y_axis] = vertices[i][1];
581 if (i == 0) {
582 aabb.position = vtx;
583 aabb.size = Vector3();
584 } else {
585 aabb.expand_to(vtx);
586 }
587 if (mesh_surface_format & VS::ARRAY_COMPRESS_TEX_UV) {
588 uint16_t v_uv[2] = { Math::make_half_float(uvs[i].x), Math::make_half_float(uvs[i].y) };
589 copymem(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_TEX_UV]], v_uv, 4);
590 } else {
591 float v_uv[2] = { uvs[i].x, uvs[i].y };
592 copymem(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_TEX_UV]], v_uv, 8);
593 }
594
595 float v_vertex[3] = { vtx.x, vtx.y, vtx.z };
596
597 copymem(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_VERTEX]], &v_vertex, sizeof(float) * 3);
598 copymem(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_NORMAL]], v_normal, 4);
599 copymem(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_TANGENT]], v_tangent, 4);
600 copymem(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_COLOR]], v_color, 4);
601 }
602
603 write_buffer.release();
604
605 RID mesh = get_mesh();
606 VS::get_singleton()->mesh_surface_update_region(mesh, 0, 0, mesh_buffer);
607
608 VS::get_singleton()->mesh_set_custom_aabb(mesh, aabb);
609 set_aabb(aabb);
610
611 set_base(mesh);
612
613 RID mat = SpatialMaterial::get_material_rid_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == SpatialMaterial::BILLBOARD_ENABLED, get_billboard_mode() == SpatialMaterial::BILLBOARD_FIXED_Y);
614 VS::get_singleton()->material_set_shader(get_material(), VS::get_singleton()->material_get_shader(mat));
615 VS::get_singleton()->material_set_param(get_material(), "texture_albedo", texture->get_rid());
616 VS::get_singleton()->instance_set_surface_material(get_instance(), 0, get_material());
617 }
618
set_texture(const Ref<Texture> & p_texture)619 void Sprite3D::set_texture(const Ref<Texture> &p_texture) {
620
621 if (p_texture == texture)
622 return;
623 if (texture.is_valid()) {
624 texture->disconnect(CoreStringNames::get_singleton()->changed, this, SceneStringNames::get_singleton()->_queue_update);
625 }
626 texture = p_texture;
627 if (texture.is_valid()) {
628 texture->set_flags(texture->get_flags()); //remove repeat from texture, it looks bad in sprites
629 texture->connect(CoreStringNames::get_singleton()->changed, this, SceneStringNames::get_singleton()->_queue_update);
630 }
631 _queue_update();
632 }
633
get_texture() const634 Ref<Texture> Sprite3D::get_texture() const {
635
636 return texture;
637 }
638
set_region(bool p_region)639 void Sprite3D::set_region(bool p_region) {
640
641 if (p_region == region)
642 return;
643
644 region = p_region;
645 _queue_update();
646 }
647
is_region() const648 bool Sprite3D::is_region() const {
649
650 return region;
651 }
652
set_region_rect(const Rect2 & p_region_rect)653 void Sprite3D::set_region_rect(const Rect2 &p_region_rect) {
654
655 bool changed = region_rect != p_region_rect;
656 region_rect = p_region_rect;
657 if (region && changed) {
658 _queue_update();
659 }
660 }
661
get_region_rect() const662 Rect2 Sprite3D::get_region_rect() const {
663
664 return region_rect;
665 }
666
set_frame(int p_frame)667 void Sprite3D::set_frame(int p_frame) {
668
669 ERR_FAIL_INDEX(p_frame, int64_t(vframes) * hframes);
670
671 frame = p_frame;
672
673 _queue_update();
674
675 _change_notify("frame");
676 _change_notify("frame_coords");
677 emit_signal(SceneStringNames::get_singleton()->frame_changed);
678 }
679
get_frame() const680 int Sprite3D::get_frame() const {
681
682 return frame;
683 }
684
set_frame_coords(const Vector2 & p_coord)685 void Sprite3D::set_frame_coords(const Vector2 &p_coord) {
686 ERR_FAIL_INDEX(int(p_coord.x), hframes);
687 ERR_FAIL_INDEX(int(p_coord.y), vframes);
688
689 set_frame(int(p_coord.y) * hframes + int(p_coord.x));
690 }
691
get_frame_coords() const692 Vector2 Sprite3D::get_frame_coords() const {
693 return Vector2(frame % hframes, frame / hframes);
694 }
695
set_vframes(int p_amount)696 void Sprite3D::set_vframes(int p_amount) {
697
698 ERR_FAIL_COND(p_amount < 1);
699 vframes = p_amount;
700 _queue_update();
701 _change_notify();
702 }
get_vframes() const703 int Sprite3D::get_vframes() const {
704
705 return vframes;
706 }
707
set_hframes(int p_amount)708 void Sprite3D::set_hframes(int p_amount) {
709
710 ERR_FAIL_COND(p_amount < 1);
711 hframes = p_amount;
712 _queue_update();
713 _change_notify();
714 }
get_hframes() const715 int Sprite3D::get_hframes() const {
716
717 return hframes;
718 }
719
get_item_rect() const720 Rect2 Sprite3D::get_item_rect() const {
721
722 if (texture.is_null())
723 return Rect2(0, 0, 1, 1);
724 /*
725 if (texture.is_null())
726 return CanvasItem::get_item_rect();
727 */
728
729 Size2i s;
730
731 if (region) {
732
733 s = region_rect.size;
734 } else {
735 s = texture->get_size();
736 s = s / Point2(hframes, vframes);
737 }
738
739 Point2 ofs = get_offset();
740 if (is_centered())
741 ofs -= s / 2;
742
743 if (s == Size2(0, 0))
744 s = Size2(1, 1);
745
746 return Rect2(ofs, s);
747 }
748
_validate_property(PropertyInfo & property) const749 void Sprite3D::_validate_property(PropertyInfo &property) const {
750
751 if (property.name == "frame") {
752 property.hint = PROPERTY_HINT_RANGE;
753 property.hint_string = "0," + itos(vframes * hframes - 1) + ",1";
754 property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
755 }
756
757 if (property.name == "frame_coords") {
758 property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
759 }
760 }
761
_bind_methods()762 void Sprite3D::_bind_methods() {
763
764 ClassDB::bind_method(D_METHOD("set_texture", "texture"), &Sprite3D::set_texture);
765 ClassDB::bind_method(D_METHOD("get_texture"), &Sprite3D::get_texture);
766
767 ClassDB::bind_method(D_METHOD("set_region", "enabled"), &Sprite3D::set_region);
768 ClassDB::bind_method(D_METHOD("is_region"), &Sprite3D::is_region);
769
770 ClassDB::bind_method(D_METHOD("set_region_rect", "rect"), &Sprite3D::set_region_rect);
771 ClassDB::bind_method(D_METHOD("get_region_rect"), &Sprite3D::get_region_rect);
772
773 ClassDB::bind_method(D_METHOD("set_frame", "frame"), &Sprite3D::set_frame);
774 ClassDB::bind_method(D_METHOD("get_frame"), &Sprite3D::get_frame);
775
776 ClassDB::bind_method(D_METHOD("set_frame_coords", "coords"), &Sprite3D::set_frame_coords);
777 ClassDB::bind_method(D_METHOD("get_frame_coords"), &Sprite3D::get_frame_coords);
778
779 ClassDB::bind_method(D_METHOD("set_vframes", "vframes"), &Sprite3D::set_vframes);
780 ClassDB::bind_method(D_METHOD("get_vframes"), &Sprite3D::get_vframes);
781
782 ClassDB::bind_method(D_METHOD("set_hframes", "hframes"), &Sprite3D::set_hframes);
783 ClassDB::bind_method(D_METHOD("get_hframes"), &Sprite3D::get_hframes);
784
785 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
786 ADD_GROUP("Animation", "");
787 ADD_PROPERTY(PropertyInfo(Variant::INT, "vframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_vframes", "get_vframes");
788 ADD_PROPERTY(PropertyInfo(Variant::INT, "hframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_hframes", "get_hframes");
789 ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame");
790 ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frame_coords", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_frame_coords", "get_frame_coords");
791 ADD_GROUP("Region", "region_");
792 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "region_enabled"), "set_region", "is_region");
793 ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect");
794
795 ADD_SIGNAL(MethodInfo("frame_changed"));
796 }
797
Sprite3D()798 Sprite3D::Sprite3D() {
799
800 region = false;
801 frame = 0;
802 vframes = 1;
803 hframes = 1;
804 }
805
806 ////////////////////////////////////////
807
_draw()808 void AnimatedSprite3D::_draw() {
809
810 set_base(RID());
811
812 if (frames.is_null()) {
813 return;
814 }
815
816 if (frame < 0) {
817 return;
818 }
819
820 if (!frames->has_animation(animation)) {
821 return;
822 }
823
824 Ref<Texture> texture = frames->get_frame(animation, frame);
825 if (!texture.is_valid())
826 return; //no texuture no life
827 Vector2 tsize = texture->get_size();
828 if (tsize.x == 0 || tsize.y == 0)
829 return;
830
831 Size2i s = tsize;
832 Rect2 src_rect;
833
834 src_rect.size = s;
835
836 Point2 ofs = get_offset();
837 if (is_centered())
838 ofs -= s / 2;
839
840 Rect2 dst_rect(ofs, s);
841
842 Rect2 final_rect;
843 Rect2 final_src_rect;
844 if (!texture->get_rect_region(dst_rect, src_rect, final_rect, final_src_rect))
845 return;
846
847 if (final_rect.size.x == 0 || final_rect.size.y == 0)
848 return;
849
850 Color color = _get_color_accum();
851 color.a *= get_opacity();
852
853 float pixel_size = get_pixel_size();
854
855 Vector2 vertices[4] = {
856
857 (final_rect.position + Vector2(0, final_rect.size.y)) * pixel_size,
858 (final_rect.position + final_rect.size) * pixel_size,
859 (final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size,
860 final_rect.position * pixel_size,
861
862 };
863
864 Vector2 src_tsize = tsize;
865
866 // Properly setup UVs for impostor textures (AtlasTexture).
867 Ref<AtlasTexture> atlas_tex = texture;
868 if (atlas_tex != NULL) {
869 src_tsize[0] = atlas_tex->get_atlas()->get_width();
870 src_tsize[1] = atlas_tex->get_atlas()->get_height();
871 }
872
873 Vector2 uvs[4] = {
874 final_src_rect.position / src_tsize,
875 (final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / src_tsize,
876 (final_src_rect.position + final_src_rect.size) / src_tsize,
877 (final_src_rect.position + Vector2(0, final_src_rect.size.y)) / src_tsize,
878 };
879
880 if (is_flipped_h()) {
881 SWAP(uvs[0], uvs[1]);
882 SWAP(uvs[2], uvs[3]);
883 }
884 if (is_flipped_v()) {
885
886 SWAP(uvs[0], uvs[3]);
887 SWAP(uvs[1], uvs[2]);
888 }
889
890 Vector3 normal;
891 int axis = get_axis();
892 normal[axis] = 1.0;
893
894 Plane tangent;
895 if (axis == Vector3::AXIS_X) {
896 tangent = Plane(0, 0, -1, -1);
897 } else {
898 tangent = Plane(1, 0, 0, -1);
899 }
900
901 int x_axis = ((axis + 1) % 3);
902 int y_axis = ((axis + 2) % 3);
903
904 if (axis != Vector3::AXIS_Z) {
905 SWAP(x_axis, y_axis);
906
907 for (int i = 0; i < 4; i++) {
908 //uvs[i] = Vector2(1.0,1.0)-uvs[i];
909 //SWAP(vertices[i].x,vertices[i].y);
910 if (axis == Vector3::AXIS_Y) {
911 vertices[i].y = -vertices[i].y;
912 } else if (axis == Vector3::AXIS_X) {
913 vertices[i].x = -vertices[i].x;
914 }
915 }
916 }
917
918 AABB aabb;
919
920 // Buffer is using default compression, so everything except position is compressed
921 PoolVector<uint8_t>::Write write_buffer = mesh_buffer.write();
922
923 int8_t v_normal[4] = {
924 (int8_t)CLAMP(normal.x * 127, -128, 127),
925 (int8_t)CLAMP(normal.y * 127, -128, 127),
926 (int8_t)CLAMP(normal.z * 127, -128, 127),
927 0,
928 };
929
930 int8_t v_tangent[4] = {
931 (int8_t)CLAMP(tangent.normal.x * 127, -128, 127),
932 (int8_t)CLAMP(tangent.normal.y * 127, -128, 127),
933 (int8_t)CLAMP(tangent.normal.z * 127, -128, 127),
934 (int8_t)CLAMP(tangent.d * 127, -128, 127)
935 };
936
937 uint8_t v_color[4] = {
938 (uint8_t)CLAMP(int(color.r * 255.0), 0, 255),
939 (uint8_t)CLAMP(int(color.g * 255.0), 0, 255),
940 (uint8_t)CLAMP(int(color.b * 255.0), 0, 255),
941 (uint8_t)CLAMP(int(color.a * 255.0), 0, 255)
942 };
943
944 for (int i = 0; i < 4; i++) {
945 Vector3 vtx;
946 vtx[x_axis] = vertices[i][0];
947 vtx[y_axis] = vertices[i][1];
948 if (i == 0) {
949 aabb.position = vtx;
950 aabb.size = Vector3();
951 } else {
952 aabb.expand_to(vtx);
953 }
954
955 if (mesh_surface_format & VS::ARRAY_COMPRESS_TEX_UV) {
956 uint16_t v_uv[2] = { Math::make_half_float(uvs[i].x), Math::make_half_float(uvs[i].y) };
957 copymem(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_TEX_UV]], v_uv, 4);
958 } else {
959 float v_uv[2] = { uvs[i].x, uvs[i].y };
960 copymem(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_TEX_UV]], v_uv, 8);
961 }
962 float v_vertex[3] = { vtx.x, vtx.y, vtx.z };
963
964 copymem(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_VERTEX]], &v_vertex, sizeof(float) * 3);
965 copymem(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_NORMAL]], v_normal, 4);
966 copymem(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_TANGENT]], v_tangent, 4);
967 copymem(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_COLOR]], v_color, 4);
968 }
969
970 write_buffer.release();
971
972 RID mesh = get_mesh();
973 VS::get_singleton()->mesh_surface_update_region(mesh, 0, 0, mesh_buffer);
974
975 VS::get_singleton()->mesh_set_custom_aabb(mesh, aabb);
976 set_aabb(aabb);
977
978 set_base(mesh);
979
980 RID mat = SpatialMaterial::get_material_rid_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == SpatialMaterial::BILLBOARD_ENABLED, get_billboard_mode() == SpatialMaterial::BILLBOARD_FIXED_Y);
981 VS::get_singleton()->material_set_shader(get_material(), VS::get_singleton()->material_get_shader(mat));
982 VS::get_singleton()->material_set_param(get_material(), "texture_albedo", texture->get_rid());
983 VS::get_singleton()->instance_set_surface_material(get_instance(), 0, get_material());
984 }
985
_validate_property(PropertyInfo & property) const986 void AnimatedSprite3D::_validate_property(PropertyInfo &property) const {
987
988 if (!frames.is_valid())
989 return;
990 if (property.name == "animation") {
991
992 property.hint = PROPERTY_HINT_ENUM;
993 List<StringName> names;
994 frames->get_animation_list(&names);
995 names.sort_custom<StringName::AlphCompare>();
996
997 bool current_found = false;
998
999 for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
1000 if (E->prev()) {
1001 property.hint_string += ",";
1002 }
1003
1004 property.hint_string += String(E->get());
1005 if (animation == E->get()) {
1006 current_found = true;
1007 }
1008 }
1009
1010 if (!current_found) {
1011 if (property.hint_string == String()) {
1012 property.hint_string = String(animation);
1013 } else {
1014 property.hint_string = String(animation) + "," + property.hint_string;
1015 }
1016 }
1017 }
1018
1019 if (property.name == "frame") {
1020 property.hint = PROPERTY_HINT_RANGE;
1021 if (frames->has_animation(animation) && frames->get_frame_count(animation) > 1) {
1022 property.hint_string = "0," + itos(frames->get_frame_count(animation) - 1) + ",1";
1023 }
1024 property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
1025 }
1026 }
1027
_notification(int p_what)1028 void AnimatedSprite3D::_notification(int p_what) {
1029
1030 switch (p_what) {
1031 case NOTIFICATION_INTERNAL_PROCESS: {
1032
1033 if (frames.is_null())
1034 return;
1035 if (!frames->has_animation(animation))
1036 return;
1037 if (frame < 0)
1038 return;
1039
1040 float speed = frames->get_animation_speed(animation);
1041 if (speed == 0)
1042 return; //do nothing
1043
1044 float remaining = get_process_delta_time();
1045
1046 while (remaining) {
1047
1048 if (timeout <= 0) {
1049
1050 timeout = 1.0 / speed;
1051
1052 int fc = frames->get_frame_count(animation);
1053 if (frame >= fc - 1) {
1054 if (frames->get_animation_loop(animation)) {
1055 frame = 0;
1056 } else {
1057 frame = fc - 1;
1058 }
1059 } else {
1060 frame++;
1061 }
1062
1063 _queue_update();
1064 _change_notify("frame");
1065 }
1066
1067 float to_process = MIN(timeout, remaining);
1068 remaining -= to_process;
1069 timeout -= to_process;
1070 }
1071 } break;
1072 }
1073 }
1074
set_sprite_frames(const Ref<SpriteFrames> & p_frames)1075 void AnimatedSprite3D::set_sprite_frames(const Ref<SpriteFrames> &p_frames) {
1076
1077 if (frames.is_valid())
1078 frames->disconnect("changed", this, "_res_changed");
1079 frames = p_frames;
1080 if (frames.is_valid())
1081 frames->connect("changed", this, "_res_changed");
1082
1083 if (!frames.is_valid()) {
1084 frame = 0;
1085 } else {
1086 set_frame(frame);
1087 }
1088
1089 _change_notify();
1090 _reset_timeout();
1091 _queue_update();
1092 update_configuration_warning();
1093 }
1094
get_sprite_frames() const1095 Ref<SpriteFrames> AnimatedSprite3D::get_sprite_frames() const {
1096
1097 return frames;
1098 }
1099
set_frame(int p_frame)1100 void AnimatedSprite3D::set_frame(int p_frame) {
1101
1102 if (!frames.is_valid()) {
1103 return;
1104 }
1105
1106 if (frames->has_animation(animation)) {
1107 int limit = frames->get_frame_count(animation);
1108 if (p_frame >= limit)
1109 p_frame = limit - 1;
1110 }
1111
1112 if (p_frame < 0)
1113 p_frame = 0;
1114
1115 if (frame == p_frame)
1116 return;
1117
1118 frame = p_frame;
1119 _reset_timeout();
1120 _queue_update();
1121 _change_notify("frame");
1122 emit_signal(SceneStringNames::get_singleton()->frame_changed);
1123 }
get_frame() const1124 int AnimatedSprite3D::get_frame() const {
1125
1126 return frame;
1127 }
1128
get_item_rect() const1129 Rect2 AnimatedSprite3D::get_item_rect() const {
1130
1131 if (!frames.is_valid() || !frames->has_animation(animation) || frame < 0 || frame >= frames->get_frame_count(animation)) {
1132 return Rect2(0, 0, 1, 1);
1133 }
1134
1135 Ref<Texture> t;
1136 if (animation)
1137 t = frames->get_frame(animation, frame);
1138 if (t.is_null())
1139 return Rect2(0, 0, 1, 1);
1140 Size2i s = t->get_size();
1141
1142 Point2 ofs = get_offset();
1143 if (centered)
1144 ofs -= s / 2;
1145
1146 if (s == Size2(0, 0))
1147 s = Size2(1, 1);
1148
1149 return Rect2(ofs, s);
1150 }
1151
_res_changed()1152 void AnimatedSprite3D::_res_changed() {
1153
1154 set_frame(frame);
1155 _change_notify("frame");
1156 _change_notify("animation");
1157 _queue_update();
1158 }
1159
_set_playing(bool p_playing)1160 void AnimatedSprite3D::_set_playing(bool p_playing) {
1161
1162 if (playing == p_playing)
1163 return;
1164 playing = p_playing;
1165 _reset_timeout();
1166 set_process_internal(playing);
1167 }
1168
_is_playing() const1169 bool AnimatedSprite3D::_is_playing() const {
1170
1171 return playing;
1172 }
1173
play(const StringName & p_animation)1174 void AnimatedSprite3D::play(const StringName &p_animation) {
1175
1176 if (p_animation)
1177 set_animation(p_animation);
1178 _set_playing(true);
1179 }
1180
stop()1181 void AnimatedSprite3D::stop() {
1182
1183 _set_playing(false);
1184 }
1185
is_playing() const1186 bool AnimatedSprite3D::is_playing() const {
1187
1188 return is_processing();
1189 }
1190
_reset_timeout()1191 void AnimatedSprite3D::_reset_timeout() {
1192
1193 if (!playing)
1194 return;
1195
1196 if (frames.is_valid() && frames->has_animation(animation)) {
1197 float speed = frames->get_animation_speed(animation);
1198 if (speed > 0) {
1199 timeout = 1.0 / speed;
1200 } else {
1201 timeout = 0;
1202 }
1203 } else {
1204 timeout = 0;
1205 }
1206 }
1207
set_animation(const StringName & p_animation)1208 void AnimatedSprite3D::set_animation(const StringName &p_animation) {
1209
1210 if (animation == p_animation)
1211 return;
1212
1213 animation = p_animation;
1214 _reset_timeout();
1215 set_frame(0);
1216 _change_notify();
1217 _queue_update();
1218 }
get_animation() const1219 StringName AnimatedSprite3D::get_animation() const {
1220
1221 return animation;
1222 }
1223
get_configuration_warning() const1224 String AnimatedSprite3D::get_configuration_warning() const {
1225
1226 if (frames.is_null()) {
1227 return TTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite3D to display frames.");
1228 }
1229
1230 return String();
1231 }
1232
_bind_methods()1233 void AnimatedSprite3D::_bind_methods() {
1234
1235 ClassDB::bind_method(D_METHOD("set_sprite_frames", "sprite_frames"), &AnimatedSprite3D::set_sprite_frames);
1236 ClassDB::bind_method(D_METHOD("get_sprite_frames"), &AnimatedSprite3D::get_sprite_frames);
1237
1238 ClassDB::bind_method(D_METHOD("set_animation", "animation"), &AnimatedSprite3D::set_animation);
1239 ClassDB::bind_method(D_METHOD("get_animation"), &AnimatedSprite3D::get_animation);
1240
1241 ClassDB::bind_method(D_METHOD("_set_playing", "playing"), &AnimatedSprite3D::_set_playing);
1242 ClassDB::bind_method(D_METHOD("_is_playing"), &AnimatedSprite3D::_is_playing);
1243
1244 ClassDB::bind_method(D_METHOD("play", "anim"), &AnimatedSprite3D::play, DEFVAL(StringName()));
1245 ClassDB::bind_method(D_METHOD("stop"), &AnimatedSprite3D::stop);
1246 ClassDB::bind_method(D_METHOD("is_playing"), &AnimatedSprite3D::is_playing);
1247
1248 ClassDB::bind_method(D_METHOD("set_frame", "frame"), &AnimatedSprite3D::set_frame);
1249 ClassDB::bind_method(D_METHOD("get_frame"), &AnimatedSprite3D::get_frame);
1250
1251 ClassDB::bind_method(D_METHOD("_res_changed"), &AnimatedSprite3D::_res_changed);
1252
1253 ADD_SIGNAL(MethodInfo("frame_changed"));
1254
1255 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "frames", PROPERTY_HINT_RESOURCE_TYPE, "SpriteFrames"), "set_sprite_frames", "get_sprite_frames");
1256 ADD_PROPERTY(PropertyInfo(Variant::STRING, "animation"), "set_animation", "get_animation");
1257 ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame");
1258 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing"), "_set_playing", "_is_playing");
1259 }
1260
AnimatedSprite3D()1261 AnimatedSprite3D::AnimatedSprite3D() {
1262
1263 frame = 0;
1264 playing = false;
1265 animation = "default";
1266 timeout = 0;
1267 }
1268