├── EntityDrawer.cpp ├── EntityDrawer.h ├── EntityPayload.h ├── FramesLibrary.cpp ├── FramesLibrary.h ├── README.md ├── StringDrawer.cpp ├── StringDrawer.h ├── TextureCatcher.cpp └── TextureCatcher.h /EntityDrawer.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "EntityDrawer.h" 3 | 4 | #include 5 | #include "TextureCatcher.h" 6 | 7 | 8 | #ifdef GD_EXTENSION_GODOCTOPUS 9 | #include 10 | #include 11 | #else 12 | #include "servers/rendering_server.h" 13 | #endif 14 | 15 | #define ENTITY_DRAWER_EPSILON 0.000000001 16 | 17 | namespace godot 18 | { 19 | Vector3 color_from_idx(int idx_p) 20 | { 21 | // Compute the color based on the idx 22 | int r = idx_p % 256; 23 | int g = (idx_p/ 256 ) % 256; 24 | int b = (idx_p/ (256*256) ) % 256; 25 | return Vector3(r/255.,g/255.,b/255.); 26 | } 27 | 28 | int idx_from_color(Color const &color_p) 29 | { 30 | int r = int(color_p.r * 255); 31 | int g = int(color_p.g * 255); 32 | int b = int(color_p.b * 255); 33 | if(r != 255 || g != 255 || b != 255) 34 | { 35 | return r + g *256 + b *256*256; 36 | } 37 | return -1; 38 | } 39 | 40 | Color safe_color(int x, int y, Ref const &image_p) 41 | { 42 | if(x >= 0 && x < image_p->get_width() 43 | && y >= 0 && y < image_p->get_height()) 44 | { 45 | return image_p->get_pixel(x, y); 46 | } 47 | return Color(1.f,1.f,1.f,1.f); 48 | } 49 | 50 | void init_animation(DirectionalAnimation &anim_p, StringName const &base_anim_p) 51 | { 52 | anim_p.base_name = base_anim_p; 53 | anim_p.names[DirectionHandler::UP] = "up_"+base_anim_p; 54 | anim_p.names[DirectionHandler::DOWN] = "down_"+base_anim_p; 55 | anim_p.names[DirectionHandler::LEFT] = "left_"+base_anim_p; 56 | anim_p.names[DirectionHandler::RIGHT] = "right_"+base_anim_p; 57 | } 58 | 59 | int get_direction(Vector2 const &dir_p, bool has_up_down_p) 60 | { 61 | int type_l = DirectionHandler::NONE; 62 | if(std::abs(dir_p.x) > ENTITY_DRAWER_EPSILON || std::abs(dir_p.y) > ENTITY_DRAWER_EPSILON) 63 | { 64 | if(std::abs(dir_p.x) > std::abs(dir_p.y) || !has_up_down_p) 65 | { 66 | if(dir_p.x > 0) 67 | { 68 | type_l = DirectionHandler::RIGHT; 69 | } 70 | else 71 | { 72 | type_l = DirectionHandler::LEFT; 73 | } 74 | } 75 | else 76 | { 77 | if(dir_p.y > 0) 78 | { 79 | type_l = DirectionHandler::DOWN; 80 | } 81 | else 82 | { 83 | type_l = DirectionHandler::UP; 84 | } 85 | } 86 | } 87 | return type_l; 88 | } 89 | 90 | EntityDrawer::~EntityDrawer() 91 | { 92 | _instances.for_each([&](EntityInstance &, size_t idx_p) { 93 | free_instance(idx_p); 94 | }); 95 | delete _payload_handler; 96 | } 97 | 98 | // helper for animation 99 | void set_up_animation(smart_list_handle &handle_p, 100 | double elapsed_time_p, Ref const &shader_p, RID const &parent_p, 101 | Vector2 const &offset_p, Ref const & animation_p, 102 | StringName const ¤t_animation_p, StringName const &next_animation_p, bool one_shot_p) 103 | { 104 | AnimationInstance &animation_l = handle_p.get(); 105 | animation_l.offset = offset_p; 106 | animation_l.animation = animation_p; 107 | animation_l.start = elapsed_time_p; 108 | animation_l.frame_idx = 0; 109 | animation_l.current_animation = current_animation_p; 110 | animation_l.next_animation = next_animation_p; 111 | animation_l.one_shot = one_shot_p; 112 | 113 | // if fresh new animation we set it up 114 | if(handle_p.revision() == 0) 115 | { 116 | // set up resources 117 | animation_l.info.rid = RenderingServer::get_singleton()->canvas_item_create(); 118 | animation_l.info.material = Ref(memnew(ShaderMaterial)); 119 | animation_l.info.material->set_shader(shader_p); 120 | 121 | RenderingServer::get_singleton()->canvas_item_set_parent(animation_l.info.rid, parent_p); 122 | RenderingServer::get_singleton()->canvas_item_set_default_texture_filter(animation_l.info.rid, RenderingServer::CANVAS_ITEM_TEXTURE_FILTER_NEAREST); 123 | RenderingServer::get_singleton()->canvas_item_set_material(animation_l.info.rid, animation_l.info.material->get_rid()); 124 | } 125 | } 126 | 127 | int EntityDrawer::add_instance(Vector2 const &pos_p, Vector2 const &offset_p, Ref const & animation_p, 128 | StringName const ¤t_animation_p, StringName const &next_animation_p, bool one_shot_p, bool in_front_p) 129 | { 130 | std::lock_guard lock_l(_internal_mutex); 131 | 132 | EntityInstance entity_l; 133 | 134 | // animation 135 | entity_l.animation = animations.recycle_instance(); 136 | set_up_animation(entity_l.animation, _elapsedAllTime, _shader, get_canvas_item(), offset_p, animation_p, current_animation_p, next_animation_p, one_shot_p); 137 | // reset z_index in case we reuse an instance for a sub instance 138 | RenderingServer::get_singleton()->canvas_item_set_z_index(entity_l.animation.get().info.rid, in_front_p? 1 : 0); 139 | 140 | // register instance 141 | smart_list_handle handle_l = _instances.new_instance(entity_l); 142 | // add payload 143 | _payload_handler->add_payload(); 144 | 145 | // position 146 | handle_l.get().pos_idx = pos_indexes.recycle_instance(); 147 | if(handle_l.get().pos_idx.get().idx < _newPos.size() 148 | && handle_l.get().pos_idx.get().idx < _oldPos.size()) 149 | { 150 | _newPos[handle_l.get().pos_idx.get().idx] = pos_p; 151 | _oldPos[handle_l.get().pos_idx.get().idx] = pos_p; 152 | } 153 | else 154 | { 155 | // update index 156 | handle_l.get().pos_idx.get().idx = _newPos.size(); 157 | _newPos.push_back(pos_p); 158 | _oldPos.push_back(pos_p); 159 | } 160 | 161 | return int(handle_l.handle()); 162 | } 163 | 164 | int EntityDrawer::add_sub_instance(int idx_ref_p, Vector2 const &offset_p, Ref const & animation_p, 165 | StringName const ¤t_animation_p, StringName const &next_animation_p, 166 | bool one_shot_p, bool in_front_p, bool use_directions_p) 167 | { 168 | std::lock_guard lock_l(_internal_mutex); 169 | 170 | if(!_instances.is_valid(idx_ref_p)) 171 | { 172 | return -1; 173 | } 174 | EntityInstance entity_l; 175 | 176 | // animation 177 | entity_l.animation = animations.recycle_instance(); 178 | set_up_animation(entity_l.animation, _elapsedAllTime, _shader, get_canvas_item(), offset_p, animation_p, current_animation_p, next_animation_p, one_shot_p); 179 | RenderingServer::get_singleton()->canvas_item_set_z_index(entity_l.animation.get().info.rid, in_front_p ? 2 : -1); 180 | 181 | // copy reference for position and dir_handler 182 | entity_l.pos_idx = _instances.get(idx_ref_p).pos_idx; 183 | entity_l.main_instance = _instances.get_handle(idx_ref_p); 184 | if(use_directions_p) 185 | { 186 | entity_l.dir_handler = _instances.get(idx_ref_p).dir_handler; 187 | if(entity_l.dir_handler.is_valid()) 188 | { 189 | DirectionalAnimation anim_l; 190 | init_animation(anim_l, entity_l.animation.get().current_animation); 191 | entity_l.dir_animation = dir_animations.new_instance(anim_l); 192 | } 193 | } 194 | 195 | // register instance 196 | smart_list_handle handle_l = _instances.new_instance(entity_l); 197 | // add payload 198 | _payload_handler->add_payload(); 199 | 200 | // set up relation for main instance 201 | entity_l.main_instance.get().sub_instances.push_back(handle_l); 202 | 203 | return int(handle_l.handle()); 204 | } 205 | 206 | void EntityDrawer::free_instance(int idx_p, bool skip_main_free_p) 207 | { 208 | std::lock_guard *lock_l = nullptr; 209 | if(!skip_main_free_p) 210 | { 211 | lock_l = new std::lock_guard(_internal_mutex); 212 | }; 213 | 214 | EntityInstance &instance_l = _instances.get(idx_p); 215 | // free all components that cannot be inherited 216 | if(instance_l.animation.is_valid()) 217 | { 218 | if(instance_l.animation.get().info.rid.is_valid()) 219 | RenderingServer::get_singleton()->canvas_item_clear(instance_l.animation.get().info.rid); 220 | animations.free_instance(instance_l.animation); 221 | } 222 | if(instance_l.dir_animation.is_valid()) 223 | { 224 | dir_animations.free_instance(instance_l.dir_animation); 225 | } 226 | if(instance_l.dyn_animation.is_valid()) 227 | { 228 | dyn_animations.free_instance(instance_l.dyn_animation); 229 | } 230 | if(instance_l.alt_info.is_valid()) 231 | { 232 | if(instance_l.alt_info.get().rid.is_valid()) 233 | RenderingServer::get_singleton()->canvas_item_clear(instance_l.alt_info.get().rid); 234 | alt_infos.free_instance(instance_l.alt_info); 235 | } 236 | 237 | for(smart_list_handle subs_l : instance_l.sub_instances) 238 | { 239 | if(subs_l.is_valid()) 240 | { 241 | free_instance(subs_l.handle(), true); 242 | } 243 | } 244 | 245 | // if we are a sub instance we release ourself from our main 246 | if(instance_l.main_instance.is_valid()) 247 | { 248 | if(!skip_main_free_p) 249 | { 250 | std::list > & sub_instances_l = instance_l.main_instance.get().sub_instances; 251 | // remove itself 252 | for(auto it_l = sub_instances_l.begin() ; it_l != sub_instances_l.end() ; ++it_l ) 253 | { 254 | if(it_l->handle() == idx_p) 255 | { 256 | sub_instances_l.erase(it_l); 257 | break; 258 | } 259 | } 260 | } 261 | } 262 | // else we can clear the direction handler 263 | else 264 | { 265 | pos_indexes.free_instance(instance_l.pos_idx); 266 | if(instance_l.dir_handler.is_valid()) 267 | { 268 | dir_handlers.free_instance(instance_l.dir_handler); 269 | } 270 | } 271 | 272 | // free payload 273 | _payload_handler->free_payload(idx_p); 274 | _instances.free_instance(idx_p); 275 | 276 | delete lock_l; 277 | } 278 | 279 | void EntityDrawer::update_sprite_frames(int idx_p, Vector2 const &offset_p, Ref const & animation_p) 280 | { 281 | std::lock_guard lock_l(_internal_mutex); 282 | 283 | EntityInstance &entity_l = _instances.get(idx_p); 284 | AnimationInstance &animation_l = entity_l.animation.get(); 285 | animation_l.offset = offset_p; 286 | animation_l.animation = animation_p; 287 | } 288 | 289 | void EntityDrawer::set_direction(int idx_p, Vector2 const &direction_p, bool just_looking_p) 290 | { 291 | std::lock_guard lock_l(_internal_mutex); 292 | 293 | EntityInstance &instance_l = _instances.get(idx_p); 294 | if(!instance_l.dir_handler.is_valid()) 295 | { 296 | return; 297 | } 298 | DirectionHandler &handler_l = instance_l.dir_handler.get(); 299 | if(just_looking_p) 300 | { 301 | handler_l.direction = Vector2(); 302 | } 303 | else 304 | { 305 | handler_l.direction = direction_p; 306 | } 307 | int new_type = get_direction(direction_p, handler_l.has_up_down); 308 | if(new_type != DirectionHandler::NONE) 309 | { 310 | handler_l.type = new_type; 311 | handler_l.count = 0; 312 | } 313 | } 314 | 315 | void EntityDrawer::add_direction_handler(int idx_p, bool has_up_down_p) 316 | { 317 | std::lock_guard lock_l(_internal_mutex); 318 | 319 | EntityInstance &instance_l = _instances.get(idx_p); 320 | if(instance_l.dir_handler.is_valid() 321 | || !instance_l.animation.is_valid()) 322 | { 323 | return; 324 | } 325 | 326 | DirectionHandler handler_l; 327 | handler_l.has_up_down = has_up_down_p; 328 | handler_l.pos_idx = instance_l.pos_idx.get().idx; 329 | instance_l.dir_handler = dir_handlers.new_instance(handler_l); 330 | 331 | DirectionalAnimation anim_l; 332 | init_animation(anim_l, instance_l.animation.get().current_animation); 333 | instance_l.dir_animation = dir_animations.new_instance(anim_l); 334 | } 335 | 336 | void EntityDrawer::remove_direction_handler(int idx_p) 337 | { 338 | std::lock_guard lock_l(_internal_mutex); 339 | 340 | EntityInstance &instance_l = _instances.get(idx_p); 341 | if(instance_l.dir_handler.is_valid()) 342 | { 343 | dir_handlers.free_instance(instance_l.dir_handler); 344 | } 345 | if(instance_l.dir_animation.is_valid()) 346 | { 347 | dir_animations.free_instance(instance_l.dir_animation); 348 | } 349 | } 350 | 351 | void EntityDrawer::add_dynamic_animation(int idx_p, StringName const &idle_animation_p, StringName const &moving_animation_p) 352 | { 353 | std::lock_guard lock_l(_internal_mutex); 354 | 355 | EntityInstance &instance_l = _instances.get(idx_p); 356 | if(instance_l.dyn_animation.is_valid()) 357 | { 358 | return; 359 | } 360 | DynamicAnimation dyn_l; 361 | init_animation(dyn_l.idle, idle_animation_p); 362 | init_animation(dyn_l.moving, moving_animation_p); 363 | instance_l.dyn_animation = dyn_animations.new_instance(dyn_l); 364 | } 365 | 366 | void EntityDrawer::add_pickable(int idx_p) 367 | { 368 | std::lock_guard lock_l(_internal_mutex); 369 | 370 | EntityInstance &instance_l = _instances.get(idx_p); 371 | if(instance_l.alt_info.is_valid()) 372 | { 373 | return; 374 | } 375 | instance_l.alt_info = alt_infos.recycle_instance(); 376 | RenderingInfo &info_l = instance_l.alt_info.get(); 377 | 378 | // if fresh new animation we set it up 379 | if(instance_l.alt_info.revision() == 0 && _texture_catcher) 380 | { 381 | // set up resources 382 | info_l.rid = RenderingServer::get_singleton()->canvas_item_create(); 383 | info_l.material = Ref(memnew(ShaderMaterial)); 384 | info_l.material->set_shader(_alt_shader); 385 | 386 | RenderingServer::get_singleton()->canvas_item_set_parent(info_l.rid, _texture_catcher->get_alt_viewport()->get_canvas_item()); 387 | RenderingServer::get_singleton()->canvas_item_set_default_texture_filter(info_l.rid, RenderingServer::CANVAS_ITEM_TEXTURE_FILTER_NEAREST); 388 | RenderingServer::get_singleton()->canvas_item_set_material(info_l.rid, info_l.material->get_rid()); 389 | } 390 | info_l.material->set_shader_parameter("idx_color", color_from_idx(idx_p)); 391 | } 392 | 393 | void EntityDrawer::remove_pickable(int idx_p) 394 | { 395 | std::lock_guard lock_l(_internal_mutex); 396 | 397 | EntityInstance &instance_l = _instances.get(idx_p); 398 | if(instance_l.alt_info.is_valid()) 399 | { 400 | if(instance_l.alt_info.get().rid.is_valid()) 401 | RenderingServer::get_singleton()->canvas_item_clear(instance_l.alt_info.get().rid); 402 | alt_infos.free_instance(instance_l.alt_info); 403 | } 404 | } 405 | 406 | void EntityDrawer::set_animation(int idx_p, StringName const ¤t_animation_p, StringName const &next_animation_p) 407 | { 408 | std::lock_guard lock_l(_internal_mutex); 409 | 410 | EntityInstance &instance_l = _instances.get(idx_p); 411 | if(!instance_l.animation.is_valid()) 412 | { 413 | return; 414 | } 415 | /// IMPORTANT this has to be done before next_animation = next_animation_p because 416 | /// current_animation_p is a reference to old next_animation therefore updating it break 417 | /// the value 418 | if(instance_l.dir_animation.is_valid()) 419 | { 420 | init_animation(instance_l.dir_animation.get(), current_animation_p); 421 | } 422 | instance_l.animation.get().current_animation = current_animation_p; 423 | instance_l.animation.get().next_animation = next_animation_p; 424 | instance_l.animation.get().frame_idx = 0; 425 | instance_l.animation.get().start = _elapsedAllTime; 426 | instance_l.animation.get().one_shot = false; 427 | } 428 | 429 | void EntityDrawer::set_proritary_animation(int idx_p, StringName const ¤t_animation_p, StringName const &next_animation_p) 430 | { 431 | std::lock_guard lock_l(_internal_mutex); 432 | 433 | EntityInstance &instance_l = _instances.get(idx_p); 434 | if(!instance_l.animation.is_valid()) 435 | { 436 | return; 437 | } 438 | /// IMPORTANT this has to be done before next_animation = next_animation_p because 439 | /// current_animation_p is a reference to old next_animation therefore updating it break 440 | /// the value 441 | if(instance_l.dir_animation.is_valid()) 442 | { 443 | init_animation(instance_l.dir_animation.get(), current_animation_p); 444 | } 445 | instance_l.animation.get().current_animation = current_animation_p; 446 | instance_l.animation.get().next_animation = next_animation_p; 447 | instance_l.animation.get().frame_idx = 0; 448 | instance_l.animation.get().start = _elapsedAllTime; 449 | instance_l.animation.get().one_shot = false; 450 | instance_l.animation.get().has_priority = true; 451 | } 452 | 453 | void EntityDrawer::set_animation_one_shot(int idx_p, StringName const ¤t_animation_p, bool priority_p) 454 | { 455 | std::lock_guard lock_l(_internal_mutex); 456 | 457 | EntityInstance &instance_l = _instances.get(idx_p); 458 | if(!instance_l.animation.is_valid()) 459 | { 460 | return; 461 | } 462 | instance_l.animation.get().current_animation = current_animation_p; 463 | instance_l.animation.get().frame_idx = 0; 464 | instance_l.animation.get().start = _elapsedAllTime; 465 | instance_l.animation.get().one_shot = true; 466 | instance_l.animation.get().has_priority = priority_p; 467 | 468 | if(instance_l.dir_animation.is_valid()) 469 | { 470 | init_animation(instance_l.dir_animation.get(), current_animation_p); 471 | } 472 | } 473 | 474 | StringName const & EntityDrawer::get_animation(int idx_p) const 475 | { 476 | std::lock_guard lock_l(_internal_mutex); 477 | 478 | EntityInstance const &instance_l = _instances.get(idx_p); 479 | if(!instance_l.animation.is_valid()) 480 | { 481 | static StringName none_l(""); 482 | return none_l; 483 | } 484 | return instance_l.animation.get().current_animation; 485 | } 486 | 487 | void EntityDrawer::set_new_pos(int idx_p, Vector2 const &pos_p) 488 | { 489 | size_t const &pos_idx_l = _instances.get(idx_p).pos_idx.get().idx; 490 | _newPos[pos_idx_l] = pos_p; 491 | } 492 | 493 | Vector2 const & EntityDrawer::get_old_pos(int idx_p) 494 | { 495 | size_t const &pos_idx_l = _instances.get(idx_p).pos_idx.get().idx; 496 | return _oldPos[pos_idx_l]; 497 | } 498 | 499 | void EntityDrawer::update_pos() 500 | { 501 | std::lock_guard lock_l(_internal_mutex); 502 | 503 | _elapsedTime = 0.; 504 | // swap positions 505 | std::swap(_oldPos, _newPos); 506 | } 507 | 508 | Ref EntityDrawer::get_shader_material(int idx_p) 509 | { 510 | EntityInstance const &instance_l = _instances.get(idx_p); 511 | if(!instance_l.animation.is_valid()) 512 | { 513 | return Ref(); 514 | } 515 | return instance_l.animation.get().info.material; 516 | } 517 | 518 | void EntityDrawer::set_shader_bool_param(int idx_p, String const ¶m_p, bool value_p) 519 | { 520 | EntityInstance &instance_l = _instances.get(idx_p); 521 | if(instance_l.animation.is_valid() 522 | && instance_l.animation.get().info.material.is_valid()) 523 | { 524 | instance_l.animation.get().info.material->set_shader_parameter(param_p, value_p); 525 | } 526 | } 527 | 528 | void EntityDrawer::set_shader_bool_params(String const ¶m_p, TypedArray const &values_p) 529 | { 530 | _instances.for_each([&](EntityInstance & instance_l, size_t idx_p) { 531 | if(instance_l.animation.is_valid() 532 | && instance_l.animation.get().info.material.is_valid()) 533 | { 534 | instance_l.animation.get().info.material->set_shader_parameter(param_p, values_p[idx_p]); 535 | } 536 | }); 537 | } 538 | 539 | void EntityDrawer::set_shader_bool_params_from_indexes(String const ¶m_p, TypedArray const &indexes_p, bool value_indexes_p) 540 | { 541 | // set for indexes 542 | for(size_t i = 0 ; i < indexes_p.size() ; ++ i) 543 | { 544 | int idx_l = indexes_p[i]; 545 | if(_instances.is_valid(idx_l) 546 | && _instances.get(idx_l).animation.is_valid() 547 | && _instances.get(idx_l).animation.get().info.material.is_valid()) 548 | { 549 | _instances.get(idx_l).animation.get().info.material->set_shader_parameter(param_p, value_indexes_p); 550 | } 551 | } 552 | } 553 | 554 | void EntityDrawer::set_all_shader_bool_params_from_indexes(String const ¶m_p, TypedArray const &indexes_p, bool value_indexes_p, bool value_others_p) 555 | { 556 | // set all default values 557 | _instances.for_each([&](EntityInstance & instance_l, size_t ) { 558 | if(instance_l.animation.is_valid() 559 | && instance_l.animation.get().info.material.is_valid()) 560 | { 561 | instance_l.animation.get().info.material->set_shader_parameter(param_p, value_others_p); 562 | } 563 | }); 564 | 565 | // set for indexes 566 | for(size_t i = 0 ; i < indexes_p.size() ; ++ i) 567 | { 568 | int idx_l = indexes_p[i]; 569 | if(_instances.is_valid(idx_l) 570 | && _instances.get(idx_l).animation.is_valid() 571 | && _instances.get(idx_l).animation.get().info.material.is_valid()) 572 | { 573 | _instances.get(idx_l).animation.get().info.material->set_shader_parameter(param_p, value_indexes_p); 574 | } 575 | } 576 | } 577 | 578 | TypedArray EntityDrawer::indexes_from_texture(Rect2 const &rect_p) const 579 | { 580 | TypedArray all_added_l = index_array_from_texture(rect_p); 581 | 582 | TypedArray indexes_l; 583 | for(int idx_l = 0 ; idx_l < all_added_l.size() ; ++ idx_l) 584 | { 585 | if (all_added_l[idx_l]) 586 | { 587 | indexes_l.append(idx_l); 588 | } 589 | } 590 | 591 | return indexes_l; 592 | } 593 | 594 | TypedArray EntityDrawer::index_array_from_texture(Rect2 const &rect_p) const 595 | { 596 | if(!_texture_catcher) 597 | { 598 | return TypedArray(); 599 | } 600 | // scale from texture viewport scale 601 | double scale_l = _texture_catcher->get_scale_viewport(); 602 | Rect2i scale_rect_l = Rect2i(rect_p.get_position() / scale_l, rect_p.get_size() / scale_l); 603 | Ref image_l = _texture_catcher->get_texture()->get_image(); 604 | TypedArray all_added_l; 605 | all_added_l.resize(_instances.size()); 606 | all_added_l.fill(false); 607 | for(int32_t x = scale_rect_l.get_position().x ; x <= scale_rect_l.get_position().x + scale_rect_l.get_size().x ; ++ x) 608 | { 609 | for(int32_t y = scale_rect_l.get_position().y ; y <= scale_rect_l.get_position().y + scale_rect_l.get_size().y ; ++ y) 610 | { 611 | int idx_l = idx_from_color(safe_color(x, y, image_l)); 612 | if(idx_l >= 0) 613 | { 614 | all_added_l[idx_l] = true; 615 | } 616 | } 617 | } 618 | return all_added_l; 619 | } 620 | 621 | int EntityDrawer::index_from_texture(Vector2 const &pos_p) const 622 | { 623 | return index_from_texture_with_tolerance(pos_p, 0); 624 | } 625 | 626 | int EntityDrawer::index_from_texture_with_tolerance(Vector2 const &pos_p, int tolerance_p) const 627 | { 628 | if(!_texture_catcher) 629 | { 630 | return -1; 631 | } 632 | // scale from texture viewport scale 633 | double scale_l = _texture_catcher->get_scale_viewport(); 634 | Vector2 scale_pos_l = pos_p / scale_l; 635 | 636 | Ref image_l = _texture_catcher->get_texture()->get_image(); 637 | int idx_l = idx_from_color(safe_color(scale_pos_l.x, scale_pos_l.y, image_l)); 638 | if(idx_l >= 0) 639 | { 640 | return idx_l; 641 | } 642 | for(size_t range_l = 1; range_l < tolerance_p; ++ range_l) 643 | { 644 | for(size_t x = 0 ; x <= range_l ; ++x) 645 | { 646 | for(size_t y = 0 ; y <= range_l ; ++y) 647 | { 648 | if(x+y == 0 || x+y > range_l) { continue; } 649 | idx_l = idx_from_color(safe_color(scale_pos_l.x+x, scale_pos_l.y+y, image_l)); 650 | if(idx_l >= 0) { return idx_l; } 651 | idx_l = idx_from_color(safe_color(scale_pos_l.x-x, scale_pos_l.y+y, image_l)); 652 | if(idx_l >= 0) { return idx_l; } 653 | idx_l = idx_from_color(safe_color(scale_pos_l.x-x, scale_pos_l.y-y, image_l)); 654 | if(idx_l >= 0) { return idx_l; } 655 | idx_l = idx_from_color(safe_color(scale_pos_l.x+x, scale_pos_l.y-y, image_l)); 656 | if(idx_l >= 0) { return idx_l; } 657 | } 658 | } 659 | } 660 | return -1; 661 | } 662 | void EntityDrawer::_notification(int p_notification) 663 | { 664 | switch (p_notification) { 665 | case NOTIFICATION_PROCESS: { 666 | _process(get_process_delta_time()); 667 | } break; 668 | case NOTIFICATION_PHYSICS_PROCESS: { 669 | _physics_process(get_physics_process_delta_time()); 670 | } break; 671 | case NOTIFICATION_DRAW: { 672 | _draw(); 673 | } break; 674 | case NOTIFICATION_READY: { 675 | _ready(); 676 | set_process(true); 677 | set_physics_process(true); 678 | } break; 679 | } 680 | } 681 | 682 | void EntityDrawer::_ready() 683 | { 684 | // Custom shader to display a color per idx 685 | _alt_shader = Ref(memnew(Shader)); 686 | _alt_shader->set_code("\n\ 687 | shader_type canvas_item;\n\ 688 | \n\ 689 | uniform vec3 idx_color : source_color = vec3(1.0);\n\ 690 | \n\ 691 | void fragment() {\n\ 692 | COLOR.rgb = idx_color;\n\ 693 | COLOR.a = round(COLOR.a);\n\ 694 | }\n\ 695 | " 696 | ); 697 | 698 | _texture_catcher = memnew(TextureCatcher); 699 | _texture_catcher->set_scale_viewport(_scale_viewport); 700 | if(!_ref_camera_path.is_empty()) 701 | { 702 | _texture_catcher->set_ref_camera("../"+_ref_camera_path); 703 | } 704 | add_child(_texture_catcher); 705 | } 706 | 707 | StringName const &get_anim(EntityInstance &instance_p) 708 | { 709 | if(!instance_p.dir_handler.is_valid()) 710 | { 711 | return instance_p.animation.get().current_animation; 712 | } 713 | DirectionHandler const &handler_l = instance_p.dir_handler.get(); 714 | int type_l = handler_l.type; 715 | if(type_l == DirectionHandler::NONE) 716 | { 717 | type_l = DirectionHandler::LEFT; 718 | } 719 | 720 | // forced directionl anim 721 | if(instance_p.dir_animation.is_valid() 722 | && instance_p.dir_animation.get().base_name != StringName("") 723 | && instance_p.animation.get().has_priority) 724 | { 725 | return instance_p.dir_animation.get().names[type_l]; 726 | } 727 | 728 | if(instance_p.dyn_animation.is_valid() 729 | && instance_p.dir_handler.is_valid()) 730 | { 731 | if(handler_l.idle) 732 | { 733 | // non-forced directionl anim 734 | if(instance_p.dir_animation.is_valid() 735 | && instance_p.dir_animation.get().base_name != StringName("")) 736 | { 737 | return instance_p.dir_animation.get().names[type_l]; 738 | } 739 | return instance_p.dyn_animation.get().idle.names[type_l]; 740 | } 741 | return instance_p.dyn_animation.get().moving.names[type_l]; 742 | } 743 | return instance_p.animation.get().current_animation; 744 | } 745 | 746 | void EntityDrawer::_draw() 747 | { 748 | std::lock_guard lock_l(_mutex); 749 | 750 | _instances.for_each([&](EntityInstance &instance_p, size_t idx_p) { 751 | StringName cur_anim_l = get_anim(instance_p); 752 | if(!instance_p.animation.is_valid()) 753 | { 754 | return; 755 | } 756 | bool is_over_l = false; 757 | AnimationInstance & animation_l = instance_p.animation.get(); 758 | if(animation_l.animation.is_valid() && animation_l.animation->get_frame_count(cur_anim_l) > 0) 759 | { 760 | double frameTime_l = animation_l.animation->get_frame_duration(cur_anim_l, animation_l.frame_idx) / animation_l.animation->get_animation_speed(cur_anim_l) ; 761 | double nextFrameTime_l = animation_l.start + frameTime_l; 762 | if(_elapsedAllTime >= nextFrameTime_l) 763 | { 764 | ++animation_l.frame_idx; 765 | animation_l.start = _elapsedAllTime; 766 | } 767 | if(animation_l.frame_idx >= animation_l.animation->get_frame_count(cur_anim_l)) 768 | { 769 | if(animation_l.one_shot) 770 | { 771 | free_instance(idx_p); 772 | is_over_l = true; 773 | } 774 | else if(animation_l.next_animation != StringName("")) 775 | { 776 | set_animation(idx_p, animation_l.next_animation, StringName("")); 777 | cur_anim_l = get_anim(instance_p); 778 | } 779 | // if dynamic animation and no chaining we reset 780 | else if(instance_p.dyn_animation.is_valid()) 781 | { 782 | set_animation(idx_p, StringName(""), StringName("")); 783 | cur_anim_l = get_anim(instance_p); 784 | } 785 | animation_l.frame_idx = 0; 786 | } 787 | // if still enabled 788 | if(!is_over_l) 789 | { 790 | size_t pos_idx_l = instance_p.pos_idx.get().idx; 791 | Vector2 diff_l = _newPos[pos_idx_l] - _oldPos[pos_idx_l]; 792 | Vector2 pos_l = (_oldPos[pos_idx_l] + diff_l * std::min(1., real_t(_elapsedTime/_timeStep))) * _scale; 793 | // draw animaton 794 | Ref texture_l = animation_l.animation->get_frame_texture(cur_anim_l, animation_l.frame_idx); 795 | RenderingServer::get_singleton()->canvas_item_set_transform(animation_l.info.rid, Transform2D(0., pos_l)); 796 | RenderingServer::get_singleton()->canvas_item_clear(animation_l.info.rid); 797 | 798 | // required when empty texture in sprite frame 799 | if(texture_l.is_valid()) 800 | { 801 | // classic rendering 802 | texture_l->draw(animation_l.info.rid, animation_l.offset); 803 | // alternate rendering 804 | if(instance_p.alt_info.is_valid() 805 | && instance_p.alt_info.get().rid.is_valid()) 806 | { 807 | RenderingInfo &alt_info_l = instance_p.alt_info.get(); 808 | RenderingServer::get_singleton()->canvas_item_set_transform(alt_info_l.rid, Transform2D(0., pos_l)); 809 | RenderingServer::get_singleton()->canvas_item_clear(alt_info_l.rid); 810 | texture_l->draw(alt_info_l.rid, animation_l.offset); 811 | } 812 | } 813 | } 814 | } 815 | }); 816 | } 817 | 818 | void EntityDrawer::_process(double delta_p) 819 | { 820 | std::lock_guard lock_l(_mutex); 821 | 822 | _elapsedTime += delta_p; 823 | _elapsedAllTime += delta_p; 824 | 825 | queue_redraw(); 826 | } 827 | 828 | void EntityDrawer::_physics_process(double delta_p) 829 | { 830 | std::lock_guard lock_l(_mutex); 831 | 832 | dir_handlers.for_each([&](DirectionHandler &handler_p) { 833 | Vector2 dir_l = handler_p.direction; 834 | if(dir_l.length_squared() < ENTITY_DRAWER_EPSILON) 835 | { 836 | dir_l = _newPos[handler_p.pos_idx] - _oldPos[handler_p.pos_idx]; 837 | } 838 | int new_type = get_direction(dir_l, handler_p.has_up_down); 839 | if(new_type != DirectionHandler::NONE) 840 | { 841 | handler_p.count_idle = 0; 842 | handler_p.idle = false; 843 | if(new_type != handler_p.count_type) 844 | { 845 | handler_p.count = 0; 846 | handler_p.count_type = new_type; 847 | } 848 | if(handler_p.count < 100) 849 | { 850 | ++handler_p.count; 851 | } 852 | } 853 | else 854 | { 855 | ++handler_p.count_idle; 856 | handler_p.count = 0; 857 | handler_p.count_type = DirectionHandler::NONE; 858 | } 859 | 860 | if(handler_p.count > 5 861 | && handler_p.count_type != DirectionHandler::NONE 862 | && handler_p.count_type != handler_p.type) 863 | { 864 | handler_p.type = handler_p.count_type; 865 | } 866 | if(handler_p.count_idle > 5) 867 | { 868 | handler_p.idle = true; 869 | } 870 | }); 871 | } 872 | 873 | void EntityDrawer::_bind_methods() 874 | { 875 | ClassDB::bind_method(D_METHOD("add_instance", "position", "offset", "animation", "current_animation", "next_animation", "one_shot", "in_front"), &EntityDrawer::add_instance); 876 | ClassDB::bind_method(D_METHOD("add_sub_instance", "idx_ref", "offset", "animation", "current_animation", "next_animation", "one_shot", "in_front", "use_directions"), &EntityDrawer::add_sub_instance); 877 | ClassDB::bind_method(D_METHOD("free_instance", "idx"), &EntityDrawer::free_instance); 878 | ClassDB::bind_method(D_METHOD("update_sprite_frames", "idx", "offset", "animation"), &EntityDrawer::update_sprite_frames); 879 | ClassDB::bind_method(D_METHOD("update_pos"), &EntityDrawer::update_pos); 880 | 881 | ClassDB::bind_method(D_METHOD("set_animation", "instance", "current_animation", "next_animation"), &EntityDrawer::set_animation); 882 | ClassDB::bind_method(D_METHOD("set_proritary_animation", "instance", "current_animation", "next_animation"), &EntityDrawer::set_proritary_animation); 883 | ClassDB::bind_method(D_METHOD("set_animation_one_shot", "instance", "current_animation", "priority"), &EntityDrawer::set_animation_one_shot); 884 | ClassDB::bind_method(D_METHOD("set_direction", "instance", "direction", "just_looking"), &EntityDrawer::set_direction); 885 | ClassDB::bind_method(D_METHOD("add_direction_handler", "instance", "has_up_down"), &EntityDrawer::add_direction_handler); 886 | ClassDB::bind_method(D_METHOD("remove_direction_handler", "instance"), &EntityDrawer::remove_direction_handler); 887 | ClassDB::bind_method(D_METHOD("add_dynamic_animation", "instance", "idle_animation", "moving_animation"), &EntityDrawer::add_dynamic_animation); 888 | ClassDB::bind_method(D_METHOD("add_pickable", "instance"), &EntityDrawer::add_pickable); 889 | ClassDB::bind_method(D_METHOD("remove_pickable", "instance"), &EntityDrawer::remove_pickable); 890 | ClassDB::bind_method(D_METHOD("set_new_pos", "instance", "pos"), &EntityDrawer::set_new_pos); 891 | ClassDB::bind_method(D_METHOD("get_old_pos", "instance"), &EntityDrawer::get_old_pos); 892 | ClassDB::bind_method(D_METHOD("get_shader_material", "instance"), &EntityDrawer::get_shader_material); 893 | ClassDB::bind_method(D_METHOD("set_shader_bool_param", "idx", "param", "value"), &EntityDrawer::set_shader_bool_param); 894 | ClassDB::bind_method(D_METHOD("set_shader_bool_params", "param", "values"), &EntityDrawer::set_shader_bool_params); 895 | ClassDB::bind_method(D_METHOD("set_shader_bool_params_from_indexes", "param", "indexes", "value_index"), &EntityDrawer::set_shader_bool_params_from_indexes); 896 | ClassDB::bind_method(D_METHOD("set_all_shader_bool_params_from_indexes", "param", "indexes", "value_index", "value_other"), &EntityDrawer::set_all_shader_bool_params_from_indexes); 897 | ClassDB::bind_method(D_METHOD("set_shader", "material"), &EntityDrawer::set_shader); 898 | 899 | ClassDB::bind_method(D_METHOD("set_time_step", "time_step"), &EntityDrawer::set_time_step); 900 | 901 | ClassDB::bind_method(D_METHOD("indexes_from_texture", "rect"), &EntityDrawer::indexes_from_texture); 902 | ClassDB::bind_method(D_METHOD("index_array_from_texture", "rect"), &EntityDrawer::index_array_from_texture); 903 | ClassDB::bind_method(D_METHOD("index_from_texture", "position"), &EntityDrawer::index_from_texture); 904 | ClassDB::bind_method(D_METHOD("index_from_texture_with_tolerance", "position", "tolerance"), &EntityDrawer::index_from_texture_with_tolerance); 905 | 906 | // properties 907 | ClassDB::bind_method(D_METHOD("get_scale_viewport"), &EntityDrawer::get_scale_viewport); 908 | ClassDB::bind_method(D_METHOD("set_scale_viewport", "scale_viewport"), &EntityDrawer::set_scale_viewport); 909 | ClassDB::add_property("EntityDrawer", PropertyInfo(Variant::FLOAT, "scale_viewport"), "set_scale_viewport", "get_scale_viewport"); 910 | 911 | ClassDB::bind_method(D_METHOD("get_ref_camera"), &EntityDrawer::get_ref_camera); 912 | ClassDB::bind_method(D_METHOD("set_ref_camera", "ref_camera"), &EntityDrawer::set_ref_camera); 913 | ClassDB::add_property("EntityDrawer", PropertyInfo(Variant::NODE_PATH, "ref_camera", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Camera2D"), "set_ref_camera", "get_ref_camera"); 914 | 915 | ClassDB::bind_method(D_METHOD("set_debug", "debug"), &EntityDrawer::set_debug); 916 | ClassDB::bind_method(D_METHOD("is_debug"), &EntityDrawer::is_debug); 917 | ClassDB::add_property("EntityDrawer", PropertyInfo(Variant::BOOL, "debug"), "set_debug", "is_debug"); 918 | 919 | ADD_GROUP("EntityDrawer", "EntityDrawer_"); 920 | } 921 | 922 | void EntityDrawer::setup_payload(AbstractEntityPayload * payload_hanlder_p) 923 | { 924 | if(_instances.size() > 0) 925 | { 926 | return; 927 | } 928 | delete _payload_handler; 929 | _payload_handler = payload_hanlder_p; 930 | } 931 | 932 | void EntityDrawer::set_debug(bool debug_p) { if(_texture_catcher) _texture_catcher->set_debug(debug_p); } 933 | bool EntityDrawer::is_debug() const { if(_texture_catcher) return _texture_catcher->is_debug(); else return false; } 934 | 935 | } // namespace godot 936 | 937 | -------------------------------------------------------------------------------- /EntityDrawer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef GD_EXTENSION_GODOCTOPUS 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #else 10 | #include "scene/2d/node_2d.h" 11 | #include "scene/resources/atlas_texture.h" 12 | #include "scene/resources/material.h" 13 | #include "scene/resources/sprite_frames.h" 14 | #endif 15 | 16 | #include 17 | #include 18 | 19 | #include "smart_list/smart_list.h" 20 | #include "EntityPayload.h" 21 | 22 | namespace godot { 23 | 24 | class TextureCatcher; 25 | 26 | struct PositionIndex 27 | { 28 | size_t idx = 999999999; 29 | }; 30 | 31 | struct RenderingInfo 32 | { 33 | RID rid; 34 | Ref material; 35 | }; 36 | 37 | struct AnimationInstance 38 | { 39 | /// @brief offset to apply to the texture to display it 40 | Vector2 offset; 41 | Ref animation; 42 | bool enabled = true; 43 | double start = 0.; 44 | int frame_idx = 0; 45 | StringName current_animation; 46 | StringName next_animation; 47 | /// @brief will be destroyed after the end of the animation 48 | bool one_shot = false; 49 | 50 | RenderingInfo info; 51 | /// @brief has priority on dynamic anim (of false will only be displayed if idle) 52 | bool has_priority = false; 53 | }; 54 | 55 | struct DirectionalAnimation 56 | { 57 | /// @brief directed names 58 | std::array names; 59 | /// @brief base name 60 | StringName base_name; 61 | }; 62 | 63 | struct DirectionHandler 64 | { 65 | // static data 66 | static int const NONE = -1; 67 | static int const UP = 0; 68 | static int const DOWN = 1; 69 | static int const LEFT = 2; 70 | static int const RIGHT = 3; 71 | 72 | /// @brief can use top down? 73 | bool has_up_down = true; 74 | /// @brief position index to be used 75 | size_t pos_idx = 0; 76 | 77 | // dynamic data 78 | Vector2 direction; 79 | // current direction 80 | int type = -1; 81 | 82 | // tracking of evolution 83 | char count = 0; 84 | char count_idle = 0; 85 | int count_type = -1; 86 | 87 | // moving or idle 88 | bool idle = false; 89 | }; 90 | 91 | struct DynamicAnimation 92 | { 93 | DirectionalAnimation idle; 94 | DirectionalAnimation moving; 95 | }; 96 | 97 | struct EntityInstance 98 | { 99 | ///// 100 | // Basic 101 | ///// 102 | 103 | /// @brief index to use in the position index 104 | smart_list_handle pos_idx; 105 | smart_list_handle animation; 106 | 107 | ///// 108 | // Directional 109 | ///// 110 | smart_list_handle dir_handler; 111 | smart_list_handle dir_animation; 112 | 113 | ///// 114 | // Dynamic 115 | ///// 116 | smart_list_handle dyn_animation; 117 | 118 | ///// 119 | // Pickable 120 | ///// 121 | smart_list_handle alt_info; 122 | 123 | // relation links 124 | std::list > sub_instances; 125 | smart_list_handle main_instance; 126 | }; 127 | 128 | class EntityDrawer : public Node2D { 129 | GDCLASS(EntityDrawer, Node2D) 130 | 131 | public: 132 | ~EntityDrawer(); 133 | 134 | // creating instances 135 | int add_instance(Vector2 const &pos_p, Vector2 const &offset_p, Ref const & animation_p, 136 | StringName const ¤t_animation_p, StringName const &next_animation_p, bool one_shot_p, bool in_front_p); 137 | int add_sub_instance(int idx_ref_p, Vector2 const &offset_p, Ref const & animation_p, 138 | StringName const ¤t_animation_p, StringName const &next_animation_p, 139 | bool one_shot_p, bool in_front_p, bool use_directions_p); 140 | void free_instance(int idx_p, bool skip_main_free_p=false); 141 | 142 | // update animation of the instance 143 | void update_sprite_frames(int idx_p, Vector2 const &offset_p, Ref const & animation_p); 144 | 145 | // direction handling 146 | void set_direction(int idx_p, Vector2 const &direction_p, bool just_looking_p); 147 | void add_direction_handler(int idx_p, bool has_up_down_p); 148 | void remove_direction_handler(int idx_p); 149 | 150 | // dynamic animation handling 151 | void add_dynamic_animation(int idx_p, StringName const &idle_animation_p, StringName const &moving_animation_p); 152 | 153 | // pickable handling 154 | void add_pickable(int idx_p); 155 | void remove_pickable(int idx_p); 156 | 157 | // animation getters/setters 158 | void set_animation(int idx_p, StringName const ¤t_animation_p, StringName const &next_animation_p); 159 | void set_proritary_animation(int idx_p, StringName const ¤t_animation_p, StringName const &next_animation_p); 160 | void set_animation_one_shot(int idx_p, StringName const ¤t_animation_p, bool priority_p); 161 | StringName const & get_animation(int idx_p) const; 162 | 163 | // position handling 164 | void set_new_pos(int idx_p, Vector2 const &pos_p); 165 | Vector2 const & get_old_pos(int idx_p); 166 | void update_pos(); 167 | 168 | // shader handling 169 | Ref get_shader_material(int idx_p); 170 | void set_shader_bool_param(int idx_p, String const ¶m_p, bool value_p); 171 | void set_shader_bool_params(String const ¶m_p, TypedArray const &values_p); 172 | void set_shader_bool_params_from_indexes(String const ¶m_p, TypedArray const &indexes_p, bool value_indexes_p); 173 | void set_all_shader_bool_params_from_indexes(String const ¶m_p, TypedArray const &indexes_p, bool value_indexes_p, bool value_others_p); 174 | 175 | /// getters for alternative rendering 176 | TypedArray indexes_from_texture(Rect2 const &rect_p) const; 177 | TypedArray index_array_from_texture(Rect2 const &rect_p) const; 178 | int index_from_texture(Vector2 const &pos_p) const; 179 | int index_from_texture_with_tolerance(Vector2 const &pos_p, int tolerance_p) const; 180 | 181 | // godot routines 182 | void _ready(); 183 | void _draw(); 184 | void _process(double delta_p); 185 | void _physics_process(double delta_p); 186 | 187 | // Will be called by Godot when the class is registered 188 | // Use this to add properties to your class 189 | static void _bind_methods(); 190 | 191 | /// Properties 192 | 193 | // getter/setter 194 | double get_scale_viewport() const { return _scale_viewport; } 195 | void set_scale_viewport(double const &scale_viewport) { _scale_viewport = scale_viewport; } 196 | NodePath const & get_ref_camera() const { return _ref_camera_path; } 197 | void set_ref_camera(NodePath const &ref_camera) { _ref_camera_path = ref_camera; } 198 | void set_debug(bool debug_p); 199 | bool is_debug() const; 200 | 201 | /// Properties END 202 | 203 | // set up 204 | void set_time_step(double timeStep_p) { _timeStep = timeStep_p; } 205 | void set_shader(Ref const &shader_p) { _shader = shader_p; } 206 | 207 | // payload setup (free old one) 208 | void setup_payload(AbstractEntityPayload * payload_hanlder_p); 209 | 210 | // mutex used to lock during display to avoid syncing error while rendering 211 | std::mutex _mutex; 212 | protected: 213 | void _notification(int p_notification); 214 | private: 215 | Ref _shader; 216 | 217 | smart_list _instances; 218 | 219 | // smart list for components 220 | smart_list animations; 221 | smart_list dir_handlers; 222 | smart_list dir_animations; 223 | smart_list dyn_animations; 224 | smart_list alt_infos; 225 | 226 | /// @brief last position of instances to lerp 227 | std::vector _newPos; 228 | std::vector _oldPos; 229 | smart_list pos_indexes; 230 | 231 | /// @brief expected duration of a timestep 232 | double _timeStep = 0.01; 233 | 234 | /// @brief time since beginning 235 | double _elapsedAllTime = 0.; 236 | 237 | /// @brief time since last position update 238 | double _elapsedTime = 0.; 239 | 240 | /// @brief an alternative rendering layer used to render the entities 241 | /// differently (used for mouse picking) 242 | TextureCatcher *_texture_catcher = nullptr; 243 | Ref _alt_shader; 244 | 245 | // properties 246 | double _scale_viewport = 2.; 247 | NodePath _ref_camera_path; 248 | 249 | AbstractEntityPayload * _payload_handler = new NoOpEntityPayload(); 250 | 251 | double const _scale = 1.; 252 | 253 | /// @brief internal mutex lock when modifying smart lists 254 | mutable std::mutex _internal_mutex; 255 | }; 256 | 257 | } 258 | -------------------------------------------------------------------------------- /EntityPayload.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "smart_list/smart_list.h" 4 | 5 | /// @brief This class is defines a payload manager 6 | /// that is used to link entity drawn to logical 7 | /// entities 8 | /// This is necessary to abstract the handling 9 | /// of the payload 10 | class AbstractEntityPayload { 11 | public: 12 | virtual ~AbstractEntityPayload() {} 13 | virtual void add_payload() = 0; 14 | virtual void free_payload(int idx_p) = 0; 15 | }; 16 | 17 | /// @brief a no op payload (not containing anything) 18 | class NoOpEntityPayload : public AbstractEntityPayload { 19 | public: 20 | void add_payload() override {} 21 | void free_payload(int) override {} 22 | }; 23 | 24 | /// @brief a basic template payload 25 | template 26 | class EntityPayload : public AbstractEntityPayload { 27 | public: 28 | void add_payload() override { _list.new_instance(T()); } 29 | void free_payload(int idx_p) override { _list.free_instance(idx_p); } 30 | 31 | T& get_payload(int idx_p) 32 | { 33 | return _list.get(idx_p); 34 | } 35 | 36 | T const & get_payload(int idx_p) const 37 | { 38 | return _list.get(idx_p); 39 | } 40 | 41 | std::size_t size() const { return _list.size(); } 42 | 43 | private: 44 | smart_list _list; 45 | }; 46 | -------------------------------------------------------------------------------- /FramesLibrary.cpp: -------------------------------------------------------------------------------- 1 | #include "FramesLibrary.h" 2 | 3 | namespace godot { 4 | 5 | void FramesLibrary::addFrame(String const &name_p, Ref const &frame_p, Vector2 const &offset_p, bool has_up_down_p) 6 | { 7 | std::string name_l(name_p.utf8().get_data()); 8 | _mapFrames[name_l] = { frame_p, offset_p, has_up_down_p }; 9 | } 10 | 11 | FrameInfo const & FramesLibrary::getFrameInfo(std::string const &name_p) 12 | { 13 | return _mapFrames.at(name_p); 14 | } 15 | 16 | FrameInfo const * FramesLibrary::tryGetFrameInfo(std::string const &name_p) const 17 | { 18 | auto it_l = _mapFrames.find(name_p); 19 | if(it_l == _mapFrames.end()) 20 | { 21 | return nullptr; 22 | } 23 | return &it_l->second; 24 | } 25 | 26 | void FramesLibrary::_bind_methods() 27 | { 28 | ClassDB::bind_method(D_METHOD("addFrame", "name", "frame", "offset", "has_up_down"), &FramesLibrary::addFrame); 29 | 30 | ADD_GROUP("FramesLibrary", "FramesLibrary_"); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /FramesLibrary.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef GD_EXTENSION_GODOCTOPUS 4 | #include 5 | #include 6 | #include 7 | #else 8 | #include "scene/main/node.h" 9 | #include "scene/resources/sprite_frames.h" 10 | #endif 11 | 12 | #include 13 | #include 14 | 15 | namespace godot { 16 | 17 | struct FrameInfo 18 | { 19 | Ref sprite_frame; 20 | Vector2 offset; 21 | bool has_up_down = true; 22 | }; 23 | 24 | class FramesLibrary : public Node { 25 | GDCLASS(FramesLibrary, Node) 26 | 27 | public: 28 | void addFrame(String const &name_p, Ref const &frame_p, Vector2 const &offset_p, bool has_up_down_p); 29 | 30 | FrameInfo const & getFrameInfo(std::string const &name_p); 31 | FrameInfo const * tryGetFrameInfo(std::string const &name_p) const; 32 | 33 | // Will be called by Godot when the class is registered 34 | // Use this to add properties to your class 35 | static void _bind_methods(); 36 | 37 | private: 38 | std::unordered_map _mapFrames; 39 | }; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Entity drawer code part 2 | 3 | This is a snippet supposed to be used as a submodule. 4 | 5 | ## EntityDrawer 6 | 7 | Allow mass drawing of animated sprites using backend rendering. 8 | 9 | ## FramesLibrary 10 | 11 | Store sprite frames to be used in EntityDrawer. 12 | -------------------------------------------------------------------------------- /StringDrawer.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "StringDrawer.h" 3 | 4 | 5 | #ifdef GD_EXTENSION_GODOCTOPUS 6 | #include 7 | #include 8 | #else 9 | #include "servers/rendering_server.h" 10 | #include 11 | #include 12 | #endif 13 | 14 | #include 15 | 16 | namespace godot 17 | { 18 | 19 | void StringDrawer::_notification(int p_notification) 20 | { 21 | switch (p_notification) { 22 | case NOTIFICATION_PROCESS: { 23 | _process(get_process_delta_time()); 24 | } break; 25 | case NOTIFICATION_DRAW: { 26 | _draw(); 27 | } break; 28 | case NOTIFICATION_READY: { 29 | _ready(); 30 | set_process(true); 31 | set_physics_process(true); 32 | } break; 33 | } 34 | } 35 | 36 | StringDrawer::~StringDrawer() {} 37 | 38 | void StringDrawer::_ready() 39 | { 40 | if(!_ref_camera_path.is_empty()) 41 | _ref_camera = (Camera2D*)get_node(_ref_camera_path); 42 | } 43 | 44 | Size2i get_size(Viewport const *viewport) 45 | { 46 | Window const * wi = dynamic_cast(viewport); 47 | SubViewport const * sv = dynamic_cast(viewport); 48 | if(wi) 49 | { 50 | return wi->get_size(); 51 | } 52 | if(sv) 53 | { 54 | return sv->get_size(); 55 | } 56 | return Size2i(); 57 | } 58 | 59 | Vector2 get_cam_top_left(Camera2D * camera) 60 | { 61 | Size2i viewport_size = get_size(camera->get_viewport()); 62 | auto cam_size = Vector2(viewport_size.x, viewport_size.y) / camera->get_zoom().x; 63 | return camera->get_position() - cam_size / 2.; 64 | } 65 | 66 | Vector2 get_screen_pos(Camera2D * camera, Vector2 const &world_pos) 67 | { 68 | auto viewport_pos = (world_pos - get_cam_top_left(camera)) * camera->get_zoom().x; 69 | auto window_size = DisplayServer::get_singleton()->window_get_size(); 70 | Size2i viewport_size = get_size(camera->get_viewport()); 71 | auto scale_size = Vector2(float(viewport_size.x) / window_size.x, float(viewport_size.y) / window_size.y); 72 | 73 | auto screen_pos = viewport_pos / scale_size; 74 | return screen_pos; 75 | } 76 | 77 | void StringDrawer::_draw() 78 | { 79 | std::lock_guard lock(mutex); 80 | std::vector ended_instances; 81 | instances.for_each_const([this, &ended_instances](StringInstance const &instance, size_t idx) { 82 | Vector2 pos = instance.position; 83 | Color color = instance.color; 84 | if(instance.floating) 85 | { 86 | double delta = elapsed_time - instance.spawn_time; 87 | pos += Vector2(std::cos(delta * 3.14)*oscillation_factor, -delta*up_speed); 88 | 89 | color.a = 1. - std::max(0., (delta - float_time)/fade_time); 90 | 91 | if(color.a < 1e-5) 92 | { 93 | ended_instances.push_back(idx); 94 | } 95 | } 96 | // adjust from camera if available 97 | if(_ref_camera) 98 | { 99 | pos = get_screen_pos(_ref_camera, pos); 100 | } 101 | if(instance.outline) 102 | { 103 | draw_string_outline( 104 | get_window()->get_theme_default_font() , 105 | pos, 106 | instance.str, 107 | HORIZONTAL_ALIGNMENT_LEFT, -1, Font::DEFAULT_FONT_SIZE * size_ratio, 4 * size_ratio, 108 | Color(0,0,0,color.a) 109 | ); 110 | } 111 | draw_string( 112 | get_window()->get_theme_default_font(), 113 | pos, 114 | instance.str, 115 | HORIZONTAL_ALIGNMENT_LEFT, -1, Font::DEFAULT_FONT_SIZE * size_ratio, 116 | color 117 | ); 118 | if(instance.icon.is_valid()) 119 | { 120 | draw_texture_rect( 121 | instance.icon, 122 | Rect2(pos + Vector2(instance.str.length()*icon_offset_x, icon_offset_y), size_ratio * Vector2(icon_size,icon_size)), 123 | false, 124 | Color(1,1,1,color.a) 125 | ); 126 | } 127 | }); 128 | 129 | for(size_t idx : ended_instances) 130 | { 131 | instances.free_instance(idx); 132 | } 133 | } 134 | 135 | void StringDrawer::_process(double delta) 136 | { 137 | elapsed_time += delta; 138 | queue_redraw(); 139 | } 140 | 141 | int StringDrawer::add_string_instance(StringName const &str, bool outline, bool floating, Vector2 const &pos, 142 | Color const &color, Ref const &texture) 143 | { 144 | std::lock_guard lock(mutex); 145 | smart_list_handle handle = instances.new_instance({str, pos, elapsed_time, texture, color, floating, outline}); 146 | return int(handle.handle()); 147 | } 148 | 149 | void StringDrawer::_bind_methods() 150 | { 151 | ClassDB::bind_method(D_METHOD("add_string_instance", "str", "outline", "floating", "pos", "color", "icon"), &StringDrawer::add_string_instance); 152 | 153 | ADD_GROUP("StringDrawer", "StringDrawer_"); 154 | 155 | ClassDB::bind_method(D_METHOD("get_ref_camera"), &StringDrawer::get_ref_camera); 156 | ClassDB::bind_method(D_METHOD("set_ref_camera", "ref_camera"), &StringDrawer::set_ref_camera); 157 | ClassDB::add_property("StringDrawer", PropertyInfo(Variant::NODE_PATH, "ref_camera", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Camera2D"), "set_ref_camera", "get_ref_camera"); 158 | 159 | // oscillation_factor 160 | ClassDB::bind_method(D_METHOD("get_oscillation_factor"), &StringDrawer::get_oscillation_factor); 161 | ClassDB::bind_method(D_METHOD("set_oscillation_factor", "oscillation_factor"), &StringDrawer::set_oscillation_factor); 162 | ClassDB::add_property("StringDrawer", PropertyInfo(Variant::FLOAT, "oscillation_factor"), "set_oscillation_factor", "get_oscillation_factor"); 163 | 164 | // up_speed 165 | ClassDB::bind_method(D_METHOD("get_up_speed"), &StringDrawer::get_up_speed); 166 | ClassDB::bind_method(D_METHOD("set_up_speed", "up_speed"), &StringDrawer::set_up_speed); 167 | ClassDB::add_property("StringDrawer", PropertyInfo(Variant::FLOAT, "up_speed"), "set_up_speed", "get_up_speed"); 168 | 169 | // float_time 170 | ClassDB::bind_method(D_METHOD("get_float_time"), &StringDrawer::get_float_time); 171 | ClassDB::bind_method(D_METHOD("set_float_time", "float_time"), &StringDrawer::set_float_time); 172 | ClassDB::add_property("StringDrawer", PropertyInfo(Variant::FLOAT, "float_time"), "set_float_time", "get_float_time"); 173 | 174 | // fade_time 175 | ClassDB::bind_method(D_METHOD("get_fade_time"), &StringDrawer::get_fade_time); 176 | ClassDB::bind_method(D_METHOD("set_fade_time", "fade_time"), &StringDrawer::set_fade_time); 177 | ClassDB::add_property("StringDrawer", PropertyInfo(Variant::FLOAT, "fade_time"), "set_fade_time", "get_fade_time"); 178 | 179 | // icon_offset_x 180 | ClassDB::bind_method(D_METHOD("get_icon_offset_x"), &StringDrawer::get_icon_offset_x); 181 | ClassDB::bind_method(D_METHOD("set_icon_offset_x", "icon_offset_x"), &StringDrawer::set_icon_offset_x); 182 | ClassDB::add_property("StringDrawer", PropertyInfo(Variant::FLOAT, "icon_offset_x"), "set_icon_offset_x", "get_icon_offset_x"); 183 | 184 | // icon_offset_y 185 | ClassDB::bind_method(D_METHOD("get_icon_offset_y"), &StringDrawer::get_icon_offset_y); 186 | ClassDB::bind_method(D_METHOD("set_icon_offset_y", "icon_offset_y"), &StringDrawer::set_icon_offset_y); 187 | ClassDB::add_property("StringDrawer", PropertyInfo(Variant::FLOAT, "icon_offset_y"), "set_icon_offset_y", "get_icon_offset_y"); 188 | 189 | // icon_size 190 | ClassDB::bind_method(D_METHOD("get_icon_size"), &StringDrawer::get_icon_size); 191 | ClassDB::bind_method(D_METHOD("set_icon_size", "icon_size"), &StringDrawer::set_icon_size); 192 | ClassDB::add_property("StringDrawer", PropertyInfo(Variant::FLOAT, "icon_size"), "set_icon_size", "get_icon_size"); 193 | 194 | // size_ratio 195 | ClassDB::bind_method(D_METHOD("get_size_ratio"), &StringDrawer::get_size_ratio); 196 | ClassDB::bind_method(D_METHOD("set_size_ratio", "size_ratio"), &StringDrawer::set_size_ratio); 197 | ClassDB::add_property("StringDrawer", PropertyInfo(Variant::FLOAT, "size_ratio"), "set_size_ratio", "get_size_ratio"); 198 | 199 | } 200 | 201 | } // godot 202 | -------------------------------------------------------------------------------- /StringDrawer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef GD_EXTENSION_GODOCTOPUS 4 | #include 5 | #include 6 | #include 7 | #else 8 | #include "scene/2d/node_2d.h" 9 | #include "scene/2d/camera_2d.h" 10 | #endif 11 | 12 | #include "smart_list/smart_list.h" 13 | #include 14 | 15 | namespace godot { 16 | 17 | struct StringInstance 18 | { 19 | StringName str = ""; 20 | Vector2 position; 21 | double spawn_time = 0.; 22 | Ref icon; 23 | Color color; 24 | bool floating = false; 25 | bool outline = false; 26 | }; 27 | 28 | /// @brief This a class that aims at displaying lots of string using backend display 29 | class StringDrawer : public Node2D { 30 | GDCLASS(StringDrawer, Node2D) 31 | 32 | public: 33 | virtual ~StringDrawer(); 34 | 35 | // godot routines 36 | void _ready(); 37 | void _draw(); 38 | void _process(double delta_p); 39 | 40 | NodePath const & get_ref_camera() const { return _ref_camera_path; } 41 | void set_ref_camera(NodePath const &ref_camera) { _ref_camera_path = ref_camera; } 42 | 43 | /// @brief Add a string instance 44 | int add_string_instance(StringName const &str, bool outline, bool floating, Vector2 const &pos, Color const &color, Ref const &texture); 45 | 46 | // Will be called by Godot when the class is registered 47 | // Use this to add properties to your class 48 | static void _bind_methods(); 49 | 50 | // getters/setters for basic prop 51 | double get_oscillation_factor() const { return oscillation_factor; } 52 | void set_oscillation_factor(double d) { oscillation_factor = d; } 53 | double get_up_speed() const { return up_speed; } 54 | void set_up_speed(double d) { up_speed = d; } 55 | double get_float_time() const { return float_time; } 56 | void set_float_time(double d) { float_time = d; } 57 | double get_fade_time() const { return fade_time; } 58 | void set_fade_time(double d) { fade_time = d; } 59 | double get_icon_offset_x() const { return icon_offset_x; } 60 | void set_icon_offset_x(double d) { icon_offset_x = d; } 61 | double get_icon_offset_y() const { return icon_offset_y; } 62 | void set_icon_offset_y(double d) { icon_offset_y = d; } 63 | double get_icon_size() const { return icon_size; } 64 | void set_icon_size(double d) { icon_size = d; } 65 | double get_size_ratio() const { return size_ratio; } 66 | void set_size_ratio(double d) { size_ratio = d; } 67 | 68 | protected: 69 | // godot routine when used internally 70 | void _notification(int p_notification); 71 | 72 | private: 73 | NodePath _ref_camera_path; 74 | Camera2D * _ref_camera = nullptr; 75 | 76 | /// @brief mutex used to avoid data race between text manipulation and draw 77 | std::mutex mutex; 78 | 79 | /// @brief time since start 80 | double elapsed_time = 0.; 81 | 82 | /// @brief we use a smart list to iterate fast an reuse memory 83 | smart_list instances; 84 | 85 | // floating parameters 86 | double oscillation_factor = 0.25; 87 | double up_speed = 2.25; 88 | double float_time = 0.5; 89 | double fade_time = 0.25; 90 | double icon_offset_x = 10.; 91 | double icon_offset_y = -20.; 92 | double icon_size = 28.; 93 | double size_ratio = 1.; 94 | }; 95 | 96 | } // godot -------------------------------------------------------------------------------- /TextureCatcher.cpp: -------------------------------------------------------------------------------- 1 | #include "TextureCatcher.h" 2 | 3 | #ifdef GD_EXTENSION_GODOCTOPUS 4 | #include 5 | #include 6 | #else 7 | #include 8 | #include 9 | #endif 10 | 11 | 12 | namespace godot { 13 | 14 | void TextureCatcher::_notification(int p_notification) 15 | { 16 | switch (p_notification) { 17 | case NOTIFICATION_PROCESS: { 18 | _process(get_process_delta_time()); 19 | } break; 20 | case NOTIFICATION_READY: { 21 | _ready(); 22 | set_process(true); 23 | } break; 24 | } 25 | } 26 | 27 | void TextureCatcher::_ready() 28 | { 29 | set_texture_filter(CanvasItem::TextureFilter::TEXTURE_FILTER_NEAREST); 30 | if(!_ref_camera_path.is_empty()) 31 | _ref_camera = (Camera2D*)get_node(_ref_camera_path); 32 | // set up basic tree 33 | // - Subviewport : viewport used to render the picking texture 34 | // - CanvasLayer 35 | // - ColorRect : white background 36 | // - Camera2D : camera of the viewport to mimic the main camera 37 | // - Sprite2D : the sprite used to render the picking draw calls 38 | // - CanvasLayer : visible for debug mode 39 | // - TextureRect : used to render the viewport 40 | 41 | // - Subviewport : viewport used to render the picking texture 42 | _sub_viewport = memnew(SubViewport); 43 | add_child(_sub_viewport); 44 | _sub_viewport->set_update_mode(SubViewport::UPDATE_ALWAYS); 45 | _sub_viewport->set_canvas_cull_mask(2); 46 | _sub_viewport->set_disable_3d(true); 47 | 48 | // - CanvasLayer 49 | CanvasLayer *canvas_layer_sub_l = memnew(CanvasLayer); 50 | _sub_viewport->add_child(canvas_layer_sub_l); 51 | canvas_layer_sub_l->set_layer(-1); 52 | 53 | // - ColorRect : white background 54 | ColorRect *color_rect_l = memnew(ColorRect); 55 | canvas_layer_sub_l->add_child(color_rect_l); 56 | color_rect_l->set_size(Vector2(512,512)); 57 | color_rect_l->set_anchors_preset(Control::LayoutPreset::PRESET_FULL_RECT); 58 | color_rect_l->set_visibility_layer(2); 59 | 60 | // - Camera2D : camera of the viewport to mimic the main camera 61 | _camera = memnew(Camera2D); 62 | _sub_viewport->add_child(_camera); 63 | _camera->set_visibility_layer(2); 64 | 65 | // - Sprite2D : the sprite used to render the picking draw calls 66 | _alt_viewport = memnew(Sprite2D); 67 | _sub_viewport->add_child(_alt_viewport); 68 | _alt_viewport->set_visibility_layer(2); 69 | _alt_viewport->set_y_sort_enabled(true); 70 | 71 | // - CanvasLayer : visible for debug mode 72 | _debug_canvas = memnew(CanvasLayer); 73 | add_child(_debug_canvas); 74 | _debug_canvas->set_visible(false); 75 | 76 | // - TextureRect : used to render the viewport 77 | _texture = memnew(TextureRect); 78 | _debug_canvas->add_child(_texture); 79 | _texture->set_stretch_mode(TextureRect::StretchMode::STRETCH_KEEP); 80 | _texture->set_texture(_sub_viewport->get_texture()); 81 | 82 | // Handle ref camera 83 | if(_ref_camera) 84 | { 85 | _ref_camera->get_viewport()->connect("size_changed", Callable(this, "_on_size_changed")); 86 | _on_size_changed(); 87 | } 88 | 89 | // handle texture scale 90 | _texture->set_scale(Vector2(_scale_viewport, _scale_viewport)); 91 | } 92 | 93 | void TextureCatcher::_process(double delta_p) 94 | { 95 | if(_ref_camera) 96 | { 97 | _camera->set_position(_ref_camera->get_position()); 98 | _camera->set_zoom(_ref_camera->get_zoom() / _scale_viewport); 99 | } 100 | _texture->set_texture(_sub_viewport->get_texture()); 101 | } 102 | 103 | void TextureCatcher::_on_size_changed() 104 | { 105 | SubViewport * sub_l = dynamic_cast(_ref_camera->get_viewport()); 106 | Window * window_l = dynamic_cast(_ref_camera->get_viewport()); 107 | if(sub_l) 108 | { 109 | _sub_viewport->set_size(sub_l->get_size() / _scale_viewport); 110 | } 111 | if(window_l) 112 | { 113 | _sub_viewport->set_size(window_l->get_size() / _scale_viewport); 114 | } 115 | } 116 | 117 | void TextureCatcher::_bind_methods() 118 | { 119 | ClassDB::bind_method(D_METHOD("_on_size_changed"), &TextureCatcher::_on_size_changed); 120 | ClassDB::bind_method(D_METHOD("set_debug", "debug"), &TextureCatcher::set_debug); 121 | ClassDB::bind_method(D_METHOD("is_debug"), &TextureCatcher::is_debug); 122 | ClassDB::bind_method(D_METHOD("get_alt_viewport"), &TextureCatcher::get_alt_viewport); 123 | ClassDB::bind_method(D_METHOD("get_texture"), &TextureCatcher::get_texture); 124 | 125 | ClassDB::bind_method(D_METHOD("get_scale_viewport"), &TextureCatcher::get_scale_viewport); 126 | ClassDB::bind_method(D_METHOD("set_scale_viewport", "scale_viewport"), &TextureCatcher::set_scale_viewport); 127 | ClassDB::add_property("TextureCatcher", PropertyInfo(Variant::FLOAT, "scale_viewport"), "set_scale_viewport", "get_scale_viewport"); 128 | 129 | ClassDB::bind_method(D_METHOD("get_ref_camera"), &TextureCatcher::get_ref_camera); 130 | ClassDB::bind_method(D_METHOD("set_ref_camera", "ref_camera"), &TextureCatcher::set_ref_camera); 131 | ClassDB::add_property("TextureCatcher", PropertyInfo(Variant::NODE_PATH, "ref_camera", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Camera2D"), "set_ref_camera", "get_ref_camera"); 132 | 133 | ADD_GROUP("TextureCatcher", "TextureCatcher_"); 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /TextureCatcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef GD_EXTENSION_GODOCTOPUS 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #else 13 | #include "scene/main/canvas_layer.h" 14 | #include "scene/main/viewport.h" 15 | #include "scene/2d/node_2d.h" 16 | #include "scene/gui/texture_rect.h" 17 | #include "scene/2d/camera_2d.h" 18 | #include "scene/2d/sprite_2d.h" 19 | #include "scene/resources/atlas_texture.h" 20 | #include "scene/resources/material.h" 21 | #include "scene/resources/sprite_frames.h" 22 | #endif 23 | 24 | #include 25 | #include 26 | 27 | namespace godot { 28 | 29 | /// @brief This node is made to catch the texture being rendered 30 | /// by the entity drawer and debug it if necessary 31 | /// The texture is used to pick entities 32 | class TextureCatcher : public Node2D { 33 | GDCLASS(TextureCatcher, Node2D) 34 | 35 | public: 36 | // godot routines 37 | void _ready(); 38 | // void _draw() override; 39 | // void _physics_process(double delta_p) override; 40 | void _process(double delta_p); 41 | 42 | void set_debug(bool debug_p) { _debug_canvas->set_visible(debug_p); } 43 | bool is_debug() const { return _debug_canvas->is_visible(); } 44 | 45 | Node2D * get_alt_viewport() { return _alt_viewport; } 46 | Ref get_texture() const { return _sub_viewport->get_texture(); } 47 | 48 | // signal 49 | void _on_size_changed(); 50 | 51 | // Will be called by Godot when the class is registered 52 | // Use this to add properties to your class 53 | static void _bind_methods(); 54 | 55 | // getter/setter 56 | double get_scale_viewport() const { return _scale_viewport; } 57 | void set_scale_viewport(double const &scale_viewport) { _scale_viewport = scale_viewport; } 58 | NodePath const & get_ref_camera() const { return _ref_camera_path; } 59 | void set_ref_camera(NodePath const &ref_camera) { _ref_camera_path = ref_camera; } 60 | 61 | protected: 62 | void _notification(int p_notification); 63 | private: 64 | double _scale_viewport = 2.; 65 | Camera2D * _ref_camera = nullptr; 66 | NodePath _ref_camera_path; 67 | SubViewport * _sub_viewport = nullptr; 68 | Camera2D * _camera = nullptr; 69 | CanvasLayer *_debug_canvas = nullptr; 70 | TextureRect *_texture = nullptr; 71 | Sprite2D *_alt_viewport = nullptr; 72 | }; 73 | 74 | } 75 | --------------------------------------------------------------------------------