| … | … | @@ -127,6 +127,7 @@ void ProcNodeGraph::_bind_methods() { |
| 127 | 127 | ClassDB::bind_method(D_METHOD("disconnect_sockets", "from_node", "from_socket", "to_node", "to_socket"), &ProcNodeGraph::disconnect_sockets); |
| 128 | 128 | ClassDB::bind_method(D_METHOD("evaluate_socket", "node_idx", "socket_idx"), &ProcNodeGraph::evaluate_socket); |
| 129 | 129 | ClassDB::bind_method(D_METHOD("evaluate_density_batch", "positions"), &ProcNodeGraph::evaluate_density_batch); |
| 130 | ClassDB::bind_method(D_METHOD("get_density_bounds"), &ProcNodeGraph::get_density_bounds); |
| 130 | 131 | ClassDB::bind_method(D_METHOD("evaluate_density_at", "pos"), &ProcNodeGraph::evaluate_density_at); |
| 131 | 132 | ClassDB::bind_static_method("ProcNodeGraph", |
| 132 | 133 | D_METHOD("debug_phacelle_noise", "p", "dir", "freq", "offset", "normalization"), |
| … | … | @@ -516,6 +517,74 @@ bool ProcNodeGraph::find_density_output(int &r_node, int &r_socket) const { |
| 516 | 517 | return false; |
| 517 | 518 | } |
| 518 | 519 | |
| 520 | Dictionary ProcNodeGraph::get_density_bounds() const { |
| 521 | Dictionary out; |
| 522 | // The same terminal the density comes from: the graph's Group Output. Read |
| 523 | // by NAME, because a group interface's socket ORDER is the author's and |
| 524 | // changing it in Blender must not silently re-point a bound. |
| 525 | for (int i = 0; i < get_node_count(); i++) { |
| 526 | Ref<ProcNode> n = get_node(i); |
| 527 | if (n.is_null() || n->get_node_type() != StringName("GroupOutput")) { |
| 528 | continue; |
| 529 | } |
| 530 | bool have_min = false, have_max = false; |
| 531 | Vector3 bmin, bmax; |
| 532 | float voxel = 0.0f; |
| 533 | for (int s = 0; s < n->get_input_count(); s++) { |
| 534 | const StringName nm = n->get_input_def(s).name; |
| 535 | // A bound may be wired from a value node or left as the socket's |
| 536 | // own default; read whichever the author used. |
| 537 | Variant v; |
| 538 | const int src = get_input_source_node(i, s); |
| 539 | if (src >= 0) { |
| 540 | v = GraphExecutor::evaluate_socket_value( |
| 541 | *this, src, get_input_source_socket(i, s), Dictionary()); |
| 542 | } else { |
| 543 | v = n->get_input_default(s); |
| 544 | } |
| 545 | if (nm == StringName("Bounds Min")) { |
| 546 | bmin = v; |
| 547 | have_min = true; |
| 548 | } else if (nm == StringName("Bounds Max")) { |
| 549 | bmax = v; |
| 550 | have_max = true; |
| 551 | } else if (nm == StringName("Voxel Size")) { |
| 552 | voxel = (float)(double)v; |
| 553 | } |
| 554 | } |
| 555 | if (!have_min || !have_max) { |
| 556 | // Half a box is not a box. An unbounded field is a legitimate |
| 557 | // state; a graph that names one corner and not the other is an |
| 558 | // authoring mistake, and answering with a default corner would |
| 559 | // place the world somewhere nobody asked for. |
| 560 | if (have_min != have_max) { |
| 561 | UtilityFunctions::push_warning( |
| 562 | "proc-nodes: the graph declares only one of Bounds Min / " |
| 563 | "Bounds Max on its Group Output. Both are needed; treating " |
| 564 | "the field as unbounded."); |
| 565 | } |
| 566 | continue; |
| 567 | } |
| 568 | if (is_blender_frame()) { |
| 569 | // M, self-inverse, the same conversion _to_godot_frame applies to |
| 570 | // geometry and the emitter applies to the query point. |
| 571 | bmin = Vector3(-bmin.x, bmin.z, bmin.y); |
| 572 | bmax = Vector3(-bmax.x, bmax.z, bmax.y); |
| 573 | // ⛔ M NEGATES x, so the converted pair is NOT sorted. Re-sort |
| 574 | // componentwise or the box comes out inverted on that axis. |
| 575 | const Vector3 lo(MIN(bmin.x, bmax.x), MIN(bmin.y, bmax.y), MIN(bmin.z, bmax.z)); |
| 576 | const Vector3 hi(MAX(bmin.x, bmax.x), MAX(bmin.y, bmax.y), MAX(bmin.z, bmax.z)); |
| 577 | bmin = lo; |
| 578 | bmax = hi; |
| 579 | } |
| 580 | out["min"] = bmin; |
| 581 | out["max"] = bmax; |
| 582 | out["voxel_size"] = voxel; |
| 583 | return out; |
| 584 | } |
| 585 | return out; |
| 586 | } |
| 587 | |
| 519 | 588 | static bool _density_output_socket(const ProcNodeGraph &p_graph, int &r_node, int &r_socket) { |
| 520 | 589 | return p_graph.find_density_output(r_node, r_socket); |
| 521 | 590 | } |