Started buildings layout editor

This commit is contained in:
2024-10-05 03:35:32 +03:00
parent db39715354
commit 166e6d6b4a
12 changed files with 2129 additions and 7 deletions

View File

@@ -3,12 +3,15 @@
Import("env")
Import("env_modules")
env_stream = env_modules.Clone()
# Godot source files
module_obj = []
SConscript("buildings/SCsub")
env_stream.add_source_files(module_obj, "*.cpp")
env_stream.add_source_files(module_obj, "flecs/*.c")
env.modules_sources += module_obj

View File

@@ -0,0 +1,16 @@
Import("env")
Import("env_modules")
#env_modules.stream_building_sources = []
#
#env_modules.add_source_files(env_modules.stream_building_sources, "*.cpp")
#
#lib = env_modules.add_library("buildings", env_modules.stream_building_sources)
#env_modules.Prepend(LIBS=[lib])
env.stream_building_sources = []
env.add_source_files(env.stream_building_sources, "*.cpp")
lib = env.add_library("buildings", env.stream_building_sources)
env.Prepend(LIBS=[lib])

View File

@@ -0,0 +1,659 @@
#undef NDEBUG
#include <cassert>
#include <main/main.h>
#include <core/engine.h>
#include <scene/resources/packed_scene.h>
#include <scene/3d/mesh_instance.h>
#include <scene/gui/box_container.h>
#include <scene/gui/texture_button.h>
#include <scene/gui/option_button.h>
#include <scene/gui/label.h>
#include <scene/gui/button.h>
#include <scene/gui/menu_button.h>
#include <editor/editor_node.h>
#include "building_layout_editor.h"
/* Taken from Godot code editor/editor_plugin.cpp */
BuildingLayoutEditor::BuildingLayoutEditor()
: meshes_ready(false)
, current_mode(-1)
, current_element_type("")
, current_element("")
, current_socket(-1)
{
}
template <class T> T *get_as_node(const String &path)
{
Node *scene;
if (Engine::get_singleton()->is_editor_hint())
scene = EditorNode::get_singleton()->get_edited_scene();
else
scene = SceneTree::get_singleton()->get_current_scene();
assert(scene);
Node *node = scene->get_node(NodePath(path));
if (!node)
print_error("Failed to get " + path);
assert(node);
T *ret = Object::cast_to<T>(node);
if (!ret)
print_error("Failed to assign " + path);
assert(ret);
return ret;
}
Vector<Ref<Texture> >
BuildingLayoutEditor::make_mesh_previews(const Vector<Ref<Mesh> > &p_meshes,
Vector<Transform> *p_transforms,
int p_preview_size)
{
int size = p_preview_size;
RID scenario = RID_PRIME(VS::get_singleton()->scenario_create());
RID viewport = RID_PRIME(VS::get_singleton()->viewport_create());
VS::get_singleton()->viewport_set_update_mode(
viewport, VS::VIEWPORT_UPDATE_ALWAYS);
VS::get_singleton()->viewport_set_vflip(viewport, true);
VS::get_singleton()->viewport_set_scenario(viewport, scenario);
VS::get_singleton()->viewport_set_size(viewport, size, size);
VS::get_singleton()->viewport_set_transparent_background(viewport,
true);
VS::get_singleton()->viewport_set_active(viewport, true);
RID viewport_texture =
VS::get_singleton()->viewport_get_texture(viewport);
RID camera = RID_PRIME(VS::get_singleton()->camera_create());
VS::get_singleton()->viewport_attach_camera(viewport, camera);
RID light = RID_PRIME(VS::get_singleton()->directional_light_create());
RID light_instance =
VS::get_singleton()->instance_create2(light, scenario);
RID light2 = VS::get_singleton()->directional_light_create();
VS::get_singleton()->light_set_color(light2, Color(0.7, 0.7, 0.7));
RID light_instance2 =
VS::get_singleton()->instance_create2(light2, scenario);
Vector<Ref<Texture> > textures;
for (int i = 0; i < p_meshes.size(); i++) {
Ref<Mesh> mesh = p_meshes[i];
if (!mesh.is_valid()) {
textures.push_back(Ref<Texture>());
continue;
}
Transform mesh_xform;
if (p_transforms != nullptr) {
mesh_xform = (*p_transforms)[i];
}
RID inst = VS::get_singleton()->instance_create2(
mesh->get_rid(), scenario);
VS::get_singleton()->instance_set_transform(inst, mesh_xform);
AABB aabb = mesh->get_aabb();
Vector3 ofs = aabb.position + aabb.size * 0.5;
aabb.position -= ofs;
Transform xform;
xform.basis = Basis().rotated(Vector3(0, 1, 0), -Math_PI / 6);
xform.basis = Basis().rotated(Vector3(1, 0, 0), Math_PI / 6) *
xform.basis;
AABB rot_aabb = xform.xform(aabb);
float m = MAX(rot_aabb.size.x, rot_aabb.size.y) * 0.5;
if (m == 0) {
textures.push_back(Ref<Texture>());
continue;
}
xform.origin = -xform.basis.xform(ofs); //-ofs*m;
xform.origin.z -= rot_aabb.size.z * 2;
xform.invert();
xform = mesh_xform * xform;
VS::get_singleton()->camera_set_transform(
camera, xform * Transform(Basis(), Vector3(0, 0, 3)));
VS::get_singleton()->camera_set_orthogonal(camera, m * 2, 0.01,
1000.0);
VS::get_singleton()->instance_set_transform(
light_instance,
xform * Transform().looking_at(Vector3(-2, -1, -1),
Vector3(0, 1, 0)));
VS::get_singleton()->instance_set_transform(
light_instance2,
xform * Transform().looking_at(Vector3(+1, -1, -2),
Vector3(0, 1, 0)));
Main::iteration();
Main::iteration();
Ref<Image> img =
VS::get_singleton()->texture_get_data(viewport_texture);
ERR_CONTINUE(!img.is_valid() || img->empty());
Ref<ImageTexture> it(memnew(ImageTexture));
it->create_from_image(img);
VS::get_singleton()->free(inst);
textures.push_back(it);
}
VS::get_singleton()->free(viewport);
VS::get_singleton()->free(light);
VS::get_singleton()->free(light_instance);
VS::get_singleton()->free(light2);
VS::get_singleton()->free(light_instance2);
VS::get_singleton()->free(camera);
VS::get_singleton()->free(scenario);
return textures;
}
void BuildingLayoutEditor::set_source(const Ref<PackedScene> &src)
{
source = src;
if (is_inside_tree()) {
prepare_meshes();
meshes_ready = true;
update_mesh_buttons();
}
}
Ref<PackedScene> BuildingLayoutEditor::get_source() const
{
return source;
}
void BuildingLayoutEditor::_notification(int which)
{
switch (which) {
case NOTIFICATION_READY:
connect_signals();
if (!meshes_ready) {
prepare_meshes();
update_mesh_buttons();
}
select_mode(0);
break;
}
}
void BuildingLayoutEditor::prepare_meshes()
{
int i;
if (!source.is_valid())
return;
Vector<String> mesh_names;
Vector<Ref<Mesh> > preview_meshes;
Vector<Ref<Texture> > preview_textures;
Node *scene = source->instance();
List<Node *> queue;
queue.push_back(scene);
while (!queue.empty()) {
Node *item = queue.front()->get();
MeshInstance *mi = Object::cast_to<MeshInstance>(item);
if (mi) {
mesh_names.push_back(mi->get_name());
preview_meshes.push_back(mi->get_mesh());
}
queue.pop_front();
for (i = 0; i < item->get_child_count(); i++)
queue.push_back(item->get_child(i));
}
preview_textures = make_mesh_previews(preview_meshes, nullptr, 64);
assert(preview_textures.size() == preview_meshes.size());
if (scene)
scene->queue_delete();
for (i = 0; i < preview_meshes.size(); i++) {
struct mesh_data md;
md.mesh = preview_meshes[i];
md.mesh_name = mesh_names[i];
md.preview = preview_textures[i];
int poff = mesh_names[i].find_char('_');
if (poff < 0)
md.category = "unknown";
else
md.category = mesh_names[i].substr(0, poff);
meshes[mesh_names[i]] = md;
}
}
void BuildingLayoutEditor::update_mesh_buttons()
{
Node *layout_mesh_buttons = get_node(NodePath("%layout_mesh_buttons"));
List<String> mlist;
meshes.get_key_list(&mlist);
HashMap<String, Node *> categories;
List<String>::Element *e = mlist.front();
while (e) {
const struct mesh_data &md = meshes[e->get()];
if (!categories.has(md.category)) {
VBoxContainer *cat = memnew(VBoxContainer);
categories[md.category] = cat;
layout_mesh_buttons->add_child(cat);
}
Label *l = memnew(Label);
l->set_text(md.mesh_name);
categories[md.category]->add_child(l);
TextureButton *b = memnew(TextureButton);
b->set_normal_texture(md.preview);
b->set_pressed_texture(md.preview);
b->set_hover_texture(md.preview);
categories[md.category]->add_child(b);
e = e->next();
}
}
void BuildingLayoutEditor::_bind_methods()
{
ClassDB::bind_method(D_METHOD("set_source", "src"),
&BuildingLayoutEditor::set_source);
ClassDB::bind_method(D_METHOD("get_source"),
&BuildingLayoutEditor::get_source);
ClassDB::bind_method(D_METHOD("select_mode", "mode"),
&BuildingLayoutEditor::select_mode);
ClassDB::bind_method(D_METHOD("enter_name", "text"),
&BuildingLayoutEditor::enter_name);
ClassDB::bind_method(D_METHOD("enter_element_name", "text"),
&BuildingLayoutEditor::enter_element_name);
ClassDB::bind_method(D_METHOD("select_element_type", "element"),
&BuildingLayoutEditor::select_element_type);
ClassDB::bind_method(D_METHOD("select_element", "element"),
&BuildingLayoutEditor::select_element);
ClassDB::bind_method(D_METHOD("select_socket", "socket"),
&BuildingLayoutEditor::select_socket);
ClassDB::bind_method(D_METHOD("set_socket_offset"),
&BuildingLayoutEditor::set_socket_offset);
ClassDB::bind_method(D_METHOD("set_socket_rotation"),
&BuildingLayoutEditor::set_socket_rotation);
ClassDB::bind_method(D_METHOD("menu_control"),
&BuildingLayoutEditor::menu_control);
ClassDB::bind_method(D_METHOD("mesh_selected", "mesh_idx", "socket"),
&BuildingLayoutEditor::mesh_selected);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "source",
PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"),
"set_source", "get_source");
}
void BuildingLayoutEditor::connect_signals()
{
int i;
if (Engine::get_singleton()->is_editor_hint())
return;
get_as_node<Button>("%socket_new_element")
->connect("pressed", this, "enter_name", varray(""));
get_as_node<LineEdit>("%socket_new_element_name")
->connect("text_entered", this, "enter_name");
get_as_node<ItemList>("%element_type_list")
->connect("item_selected", this, "select_element_type");
get_as_node<ItemList>("%socket_list")
->connect("item_selected", this, "select_socket");
get_as_node<Button>("%socket_editor_offset_base/set")
->connect("pressed", this, "set_socket_offset");
get_as_node<Button>("%socket_editor_rotation_base/set")
->connect("pressed", this, "set_socket_rotation");
get_as_node<ItemList>("%element_list")
->connect("item_selected", this, "select_element");
get_as_node<Button>("%element_new_element")
->connect("pressed", this, "enter_element_name", varray(""));
get_as_node<LineEdit>("%element_new_element_name")
->connect("text_entered", this, "enter_element_name");
Node *menu_panel = get_node(NodePath("%menu_panel"));
for (i = 0; i < menu_panel->get_child_count(); i++) {
Node *m = menu_panel->get_child(i);
MenuButton *mb = Object::cast_to<MenuButton>(m);
mb->get_popup()->connect("id_pressed", this, "menu_control");
}
}
void BuildingLayoutEditor::select_mode(int mode)
{
int i;
List<String> element_keys;
List<String>::Element *e;
print_line("set mode: " + itos(mode));
if (Engine::get_singleton()->is_editor_hint())
return;
if (current_mode != mode) {
switch (current_mode) {
case 0:
break;
case 1:
break;
}
current_mode = mode;
switch (mode) {
case 0:
get_as_node<Control>("%socket_editor")->hide();
get_as_node<Control>("%element_editor")->hide();
if (elements.size() == 0)
print_error("No elements");
break;
case 1:
get_as_node<Control>("%element_editor")->hide();
get_as_node<ItemList>("%element_type_list")->clear();
print_line("Restoring UI");
element_keys.clear();
element_type.get_key_list(&element_keys);
e = element_keys.front();
while (e) {
get_as_node<ItemList>("%element_type_list")
->add_item(e->get());
print_line("Added item: " + e->get());
e = e->next();
}
get_as_node<ItemList>("%socket_list")->hide();
get_as_node<Control>("%socket_editor")->show();
get_as_node<LineEdit>("%socket_new_element_name")
->hide();
get_as_node<ItemList>("%socket_list")->hide();
get_as_node<Control>("%socket_transform_editor")->hide();
element_keys.clear();
break;
case 2:
if (element_type.size() == 0)
print_error("No element types");
get_as_node<Control>("%socket_editor")->hide();
get_as_node<Control>("%element_editor")->show();
get_as_node<LineEdit>("%element_new_element_name")
->hide();
get_as_node<ItemList>("%element_list")->clear();
print_line("Restoring UI");
element_keys.clear();
elements.get_key_list(&element_keys);
e = element_keys.front();
while (e) {
get_as_node<ItemList>("%element_list")
->add_item(e->get());
print_line("Added item: " + e->get());
e = e->next();
}
element_keys.clear();
element_type.get_key_list(&element_keys);
e = element_keys.front();
while (e) {
get_as_node<OptionButton>(
"%element_type_select")
->add_item(e->get());
print_line("Added item: " + e->get());
e = e->next();
}
get_as_node<ItemList>("%element_list")->select(0);
ItemList *sl = get_as_node<ItemList>("%socket_list");
Control *mb =
get_as_node<Control>("%element_mesh_select");
used_socket_count = sl->get_item_count();
if (mb->get_child_count() == 0) {
for (i = 0; i < used_socket_count; i++) {
String socket_name =
sl->get_item_text(i);
Label *label = memnew(Label);
label->set_text(socket_name);
OptionButton *ob = memnew(OptionButton);
mb->add_child(label);
mb->add_child(ob);
List<String> mesh_list;
meshes.get_key_list(&mesh_list);
List<String>::Element *me =
mesh_list.front();
ob->clear();
ob->add_item("");
int index = 1;
int selected = 0;
String selected_mesh;
if (elements.has(current_element))
selected_mesh =
elements[current_element]
.mesh_names[i];
while (me) {
if (selected_mesh == me->get())
selected = index;
ob->add_item(me->get());
ob->set_item_icon(
index,
meshes[me->get()]
.preview);
mesh_select_buttons[i] = ob;
me = me->next();
index++;
}
ob->select(selected);
ob->connect("item_selected", this,
"mesh_selected", varray(i));
}
}
select_element(0);
break;
}
}
}
void BuildingLayoutEditor::enter_name(const String &text)
{
bool added = false;
if (text == "") {
get_as_node<LineEdit>("%socket_new_element_name")->show();
get_as_node<LineEdit>("%socket_new_element_name")
->grab_click_focus();
get_as_node<LineEdit>("%socket_new_element_name")->grab_focus();
} else {
String key = text.strip_edges();
assert(!element_type.has(key));
if (!element_type.has(key)) {
struct grid_element_type g;
g.name = key;
element_type[key] = g;
get_as_node<ItemList>("%element_type_list")
->add_item(key);
added = true;
}
assert(added);
get_as_node<LineEdit>("%socket_new_element_name")->set_text("");
get_as_node<LineEdit>("%socket_new_element_name")
->release_focus();
get_as_node<LineEdit>("%socket_new_element_name")->hide();
if (added) {
get_as_node<ItemList>("%element_type_list")
->grab_click_focus();
get_as_node<ItemList>("%element_type_list")
->grab_focus();
}
}
}
void BuildingLayoutEditor::enter_element_name(const String &text)
{
bool added = false;
if (text == "") {
get_as_node<LineEdit>("%element_new_element_name")->show();
get_as_node<LineEdit>("%element_new_element_name")
->grab_click_focus();
get_as_node<LineEdit>("%element_new_element_name")->grab_focus();
} else {
String key = text.strip_edges();
assert(!elements.has(key));
if (!elements.has(key)) {
struct grid_element g;
g.name = key;
elements[key] = g;
get_as_node<ItemList>("%element_list")->add_item(key);
added = true;
}
assert(added);
get_as_node<LineEdit>("%element_new_element_name")->set_text("");
get_as_node<LineEdit>("%element_new_element_name")
->release_focus();
get_as_node<LineEdit>("%element_new_element_name")->hide();
if (added) {
get_as_node<ItemList>("%element_list")
->grab_click_focus();
get_as_node<ItemList>("%element_list")->grab_focus();
}
}
}
void BuildingLayoutEditor::select_element_type(int element)
{
String item = get_as_node<ItemList>("%element_type_list")
->get_item_text(element);
print_line("item: " + item);
current_element_type = item;
get_as_node<ItemList>("%socket_list")->show();
get_as_node<ItemList>("%socket_list")->select(0);
// just in case
select_socket(0);
}
void BuildingLayoutEditor::select_socket(int socket)
{
current_socket = socket;
String item =
get_as_node<ItemList>("%socket_list")->get_item_text(socket);
get_as_node<Control>("%socket_transform_editor")->show();
print_line("socket: " + item);
Vector3 f = element_type[current_element_type].sockets[socket].origin;
f = Vector3(Math::stepify(f.x, 0.01f), Math::stepify(f.y, 0.01f),
Math::stepify(f.z, 0.01f));
get_as_node<LineEdit>("%socket_editor_offset_base/x_edit")
->set_text(String::num(f.x));
get_as_node<LineEdit>("%socket_editor_offset_base/y_edit")
->set_text(String::num(f.y));
get_as_node<LineEdit>("%socket_editor_offset_base/z_edit")
->set_text(String::num(f.z));
Vector3 r = element_type[current_element_type]
.sockets[socket]
.basis.get_rotation_euler() *
180.0f / Math_PI;
r = Vector3(Math::stepify(r.x, 0.01f), Math::stepify(r.y, 0.01f),
Math::stepify(r.z, 0.01f));
get_as_node<LineEdit>("%socket_editor_rotation_base/x_edit")
->set_text(String::num(r.x));
get_as_node<LineEdit>("%socket_editor_rotation_base/y_edit")
->set_text(String::num(r.y));
get_as_node<LineEdit>("%socket_editor_rotation_base/z_edit")
->set_text(String::num(r.z));
}
void BuildingLayoutEditor::set_socket_offset()
{
float x = get_as_node<LineEdit>("%socket_editor_offset_base/x_edit")
->get_text()
.to_float();
x = Math::stepify(x, 0.01f);
float y = get_as_node<LineEdit>("%socket_editor_offset_base/y_edit")
->get_text()
.to_float();
y = Math::stepify(y, 0.01f);
float z = get_as_node<LineEdit>("%socket_editor_offset_base/z_edit")
->get_text()
.to_float();
z = Math::stepify(z, 0.01f);
element_type[current_element_type].sockets[current_socket].origin =
Vector3(x, y, z);
}
void BuildingLayoutEditor::set_socket_rotation()
{
float x = Math::stepify(get_as_node<LineEdit>(
"%socket_editor_rotation_base/x_edit")
->get_text()
.to_float(),
0.01f) *
Math_PI / 180.0f;
float y = Math::stepify(get_as_node<LineEdit>(
"%socket_editor_rotation_base/y_edit")
->get_text()
.to_float(),
0.01f) *
Math_PI / 180.0f;
float z = Math::stepify(get_as_node<LineEdit>(
"%socket_editor_rotation_base/z_edit")
->get_text()
.to_float(),
0.01f) *
Math_PI / 180.0f;
Basis basis(Vector3(x, y, z));
element_type[current_element_type].sockets[current_socket].basis =
basis;
}
void BuildingLayoutEditor::menu_control(int id)
{
print_line("menu_control: " + itos(id));
switch (id) {
case 100:
save_data();
break;
case 200: // layout mode
get_as_node<Control>("%socket_editor")->hide();
select_mode(0);
break;
case 201: // element type editor
get_as_node<Control>("%socket_editor")->show();
select_mode(1);
break;
case 202: // element editor
get_as_node<Control>("%socket_editor")->hide();
select_mode(2);
break;
}
}
void BuildingLayoutEditor::select_element(int element)
{
int i, j;
print_line("selected element: " + itos(element));
String item =
get_as_node<ItemList>("%element_list")->get_item_text(element);
print_line("selected element: " + item);
current_element = item;
String type = elements[current_element].type;
if (type.length() > 0) {
OptionButton *b =
get_as_node<OptionButton>("%element_type_select");
for (i = 0; i < b->get_item_count(); i++) {
if (b->get_item_text(i) == type) {
b->select(i);
break;
}
}
} else {
OptionButton *b =
get_as_node<OptionButton>("%element_type_select");
elements[current_element].type = b->get_item_text(0);
}
int selected = 0;
for (i = 0; i < used_socket_count; i++) {
String selected_mesh = elements[current_element].mesh_names[i];
selected = 0;
for (j = 0; j < mesh_select_buttons[i]->get_item_count(); j++) {
String mesh_item =
mesh_select_buttons[i]->get_item_text(j);
if (mesh_item == selected_mesh) {
selected = j;
break;
}
}
print_line(itos(i) + ": selecting mesh: " + itos(selected));
mesh_select_buttons[i]->select(selected);
}
}
void BuildingLayoutEditor::mesh_selected(int mesh_idx, int socket)
{
OptionButton *ob = mesh_select_buttons[socket];
String mesh_name =
Object::cast_to<OptionButton>(ob)->get_item_text(mesh_idx);
if (mesh_name.length() > 0) {
elements[current_element].mesh_names[socket] = mesh_name;
} else {
elements[current_element].mesh_names[socket] = "";
}
}
void BuildingLayoutEditor::save_data()
{
print_line("save_data");
}

View File

@@ -0,0 +1,61 @@
#include <scene/main/node.h>
#include <scene/resources/packed_scene.h>
class Mesh;
class OptionButton;
class BuildingLayoutEditor : public Node {
GDCLASS(BuildingLayoutEditor, Node);
Ref<PackedScene> source;
struct mesh_data {
Ref<Mesh> mesh;
String mesh_name;
String category;
Ref<Texture> preview;
};
HashMap<String, struct mesh_data> meshes;
HashMap<int, OptionButton *> mesh_select_buttons;
int used_socket_count;
#define ELEMENT_SOCKETS 16
struct grid_element_type {
String name;
Transform sockets[ELEMENT_SOCKETS];
};
struct grid_element {
String name;
String type;
String mesh_names[ELEMENT_SOCKETS];
};
HashMap<String, struct grid_element_type> element_type;
HashMap<String, struct grid_element> elements;
bool meshes_ready;
int current_mode;
String current_element_type;
String current_element;
int current_socket;
public:
BuildingLayoutEditor();
Vector<Ref<Texture> >
make_mesh_previews(const Vector<Ref<Mesh> > &p_meshes,
Vector<Transform> *p_transforms, int p_preview_size);
void set_source(const Ref<PackedScene> &src);
Ref<PackedScene> get_source() const;
protected:
void _notification(int which);
void prepare_meshes();
void update_mesh_buttons();
static void _bind_methods();
void connect_signals();
void select_mode(int mode);
void enter_name(const String &text);
void enter_element_name(const String &text);
void select_element_type(int element);
void select_socket(int socket);
void set_socket_offset();
void set_socket_rotation();
void menu_control(int id);
void select_element(int element);
void mesh_selected(int mesh_idx, int socket);
void save_data();
};

View File

@@ -4,6 +4,7 @@
#include "world_editor.h"
#include "nav_panel.h"
#include "line_metadata_editor.h"
#include "buildings/building_layout_editor.h"
void register_stream_types()
{
@@ -12,6 +13,7 @@ void register_stream_types()
ClassDB::register_class<WorldEditor>();
ClassDB::register_class<NavPanel>();
ClassDB::register_class<LineMetadataEditor>();
ClassDB::register_class<BuildingLayoutEditor>();
}
void unregister_stream_types()

View File

@@ -703,13 +703,6 @@ void RoadLinesData::update_road_lines_nodes(
nidx) == ::lines[r].indices.end());
::lines.insert_line_index(k, i + 1, nidx);
::lines.insert_line_index(r, j + 1, nidx);
#if 0
// FIXME: WTF???
::lines[k].indices.insert(
::lines[k].indices.begin() + i + 1, nidx);
::lines[r].indices.insert(
::lines[k].indices.begin() + j + 1, nidx);
#endif
}
}
}