Fix deform

master
Jeremy Hu 2018-04-18 00:14:31 +08:00
parent ce41e654fb
commit f1b219333f
13 changed files with 139 additions and 47 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
moc_* moc_*
.DS_Store .DS_Store
*.o

View File

@ -125,9 +125,12 @@ void MeshGenerator::process()
int bmeshId = meshlite_bmesh_create(meshliteContext); int bmeshId = meshlite_bmesh_create(meshliteContext);
if (subdived) if (subdived)
meshlite_bmesh_set_cut_subdiv_count(meshliteContext, bmeshId, 1); meshlite_bmesh_set_cut_subdiv_count(meshliteContext, bmeshId, 1);
QString thicknessString = valueOfKeyInMapOrEmpty(part->second, "thickness"); QString thicknessString = valueOfKeyInMapOrEmpty(part->second, "deformThickness");
if (!thicknessString.isEmpty()) if (!thicknessString.isEmpty())
meshlite_bmesh_set_thickness(meshliteContext, bmeshId, thicknessString.toFloat()); meshlite_bmesh_set_deform_thickness(meshliteContext, bmeshId, thicknessString.toFloat());
QString widthString = valueOfKeyInMapOrEmpty(part->second, "deformWidth");
if (!widthString.isEmpty())
meshlite_bmesh_set_deform_width(meshliteContext, bmeshId, widthString.toFloat());
if (MeshGenerator::enableDebug) if (MeshGenerator::enableDebug)
meshlite_bmesh_enable_debug(meshliteContext, bmeshId, 1); meshlite_bmesh_enable_debug(meshliteContext, bmeshId, 1);
partBmeshMap[partIdIt] = bmeshId; partBmeshMap[partIdIt] = bmeshId;

View File

@ -585,8 +585,10 @@ void SkeletonDocument::toSnapshot(SkeletonSnapshot *snapshot, const std::set<QUu
part["disabled"] = partIt.second.disabled ? "true" : "false"; part["disabled"] = partIt.second.disabled ? "true" : "false";
part["xMirrored"] = partIt.second.xMirrored ? "true" : "false"; part["xMirrored"] = partIt.second.xMirrored ? "true" : "false";
part["zMirrored"] = partIt.second.zMirrored ? "true" : "false"; part["zMirrored"] = partIt.second.zMirrored ? "true" : "false";
if (partIt.second.thicknessAdjusted()) if (partIt.second.deformThicknessAdjusted())
part["thickness"] = QString::number(partIt.second.thickness); part["deformThickness"] = QString::number(partIt.second.deformThickness);
if (partIt.second.deformWidthAdjusted())
part["deformWidth"] = QString::number(partIt.second.deformWidth);
if (!partIt.second.name.isEmpty()) if (!partIt.second.name.isEmpty())
part["name"] = partIt.second.name; part["name"] = partIt.second.name;
snapshot->parts[part["id"]] = part; snapshot->parts[part["id"]] = part;
@ -657,9 +659,12 @@ void SkeletonDocument::addFromSnapshot(const SkeletonSnapshot &snapshot)
part.disabled = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "disabled")); part.disabled = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "disabled"));
part.xMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "xMirrored")); part.xMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "xMirrored"));
part.zMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "zMirrored")); part.zMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "zMirrored"));
const auto &thicknessIt = partKv.second.find("thickness"); const auto &deformThicknessIt = partKv.second.find("deformThickness");
if (thicknessIt != partKv.second.end()) if (deformThicknessIt != partKv.second.end())
part.setThickness(thicknessIt->second.toFloat()); part.setDeformThickness(deformThicknessIt->second.toFloat());
const auto &deformWidthIt = partKv.second.find("deformWidth");
if (deformWidthIt != partKv.second.end())
part.setDeformWidth(deformWidthIt->second.toFloat());
partMap[part.id] = part; partMap[part.id] = part;
} }
for (const auto &nodeKv : snapshot.nodes) { for (const auto &nodeKv : snapshot.nodes) {
@ -929,15 +934,27 @@ void SkeletonDocument::setPartZmirrorState(QUuid partId, bool mirrored)
emit skeletonChanged(); emit skeletonChanged();
} }
void SkeletonDocument::setPartThickness(QUuid partId, float thickness) void SkeletonDocument::setPartDeformThickness(QUuid partId, float thickness)
{ {
auto part = partMap.find(partId); auto part = partMap.find(partId);
if (part == partMap.end()) { if (part == partMap.end()) {
qDebug() << "Part not found:" << partId; qDebug() << "Part not found:" << partId;
return; return;
} }
part->second.setThickness(thickness); part->second.setDeformThickness(thickness);
emit partThicknessChanged(partId); emit partDeformThicknessChanged(partId);
emit skeletonChanged();
}
void SkeletonDocument::setPartDeformWidth(QUuid partId, float width)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
part->second.setDeformWidth(width);
emit partDeformWidthChanged(partId);
emit skeletonChanged(); emit skeletonChanged();
} }

View File

@ -71,7 +71,8 @@ public:
bool disabled; bool disabled;
bool xMirrored; bool xMirrored;
bool zMirrored; bool zMirrored;
float thickness; float deformThickness;
float deformWidth;
QImage preview; QImage preview;
std::vector<QUuid> nodeIds; std::vector<QUuid> nodeIds;
SkeletonPart(const QUuid &withId=QUuid()) : SkeletonPart(const QUuid &withId=QUuid()) :
@ -81,21 +82,38 @@ public:
disabled(false), disabled(false),
xMirrored(false), xMirrored(false),
zMirrored(false), zMirrored(false),
thickness(1.0) deformThickness(1.0),
deformWidth(1.0)
{ {
id = withId.isNull() ? QUuid::createUuid() : withId; id = withId.isNull() ? QUuid::createUuid() : withId;
} }
void setThickness(float toThickness) void setDeformThickness(float toThickness)
{ {
if (toThickness < 0) if (toThickness < 0)
toThickness = 0; toThickness = 0;
else if (toThickness > 2) else if (toThickness > 2)
toThickness = 2; toThickness = 2;
thickness = toThickness; deformThickness = toThickness;
} }
bool thicknessAdjusted() const void setDeformWidth(float toWidth)
{ {
return fabs(thickness - 1.0) >= 0.01; if (toWidth < 0)
toWidth = 0;
else if (toWidth > 2)
toWidth = 2;
deformWidth = toWidth;
}
bool deformThicknessAdjusted() const
{
return fabs(deformThickness - 1.0) >= 0.01;
}
bool deformWidthAdjusted() const
{
return fabs(deformWidth - 1.0) >= 0.01;
}
bool deformAdjusted() const
{
return deformThicknessAdjusted() || deformWidthAdjusted();
} }
bool isEditVisible() const bool isEditVisible() const
{ {
@ -109,7 +127,8 @@ public:
disabled = other.disabled; disabled = other.disabled;
xMirrored = other.xMirrored; xMirrored = other.xMirrored;
zMirrored = other.zMirrored; zMirrored = other.zMirrored;
thickness = other.thickness; deformThickness = other.deformThickness;
deformWidth = other.deformWidth;
} }
}; };
@ -161,7 +180,8 @@ signals:
void partDisableStateChanged(QUuid partId); void partDisableStateChanged(QUuid partId);
void partXmirrorStateChanged(QUuid partId); void partXmirrorStateChanged(QUuid partId);
void partZmirrorStateChanged(QUuid partId); void partZmirrorStateChanged(QUuid partId);
void partThicknessChanged(QUuid partId); void partDeformThicknessChanged(QUuid partId);
void partDeformWidthChanged(QUuid partId);
void cleanup(); void cleanup();
void originChanged(); void originChanged();
void xlockStateChanged(); void xlockStateChanged();
@ -220,7 +240,8 @@ public slots:
void setPartDisableState(QUuid partId, bool disabled); void setPartDisableState(QUuid partId, bool disabled);
void setPartXmirrorState(QUuid partId, bool mirrored); void setPartXmirrorState(QUuid partId, bool mirrored);
void setPartZmirrorState(QUuid partId, bool mirrored); void setPartZmirrorState(QUuid partId, bool mirrored);
void setPartThickness(QUuid partId, float thickness); void setPartDeformThickness(QUuid partId, float thickness);
void setPartDeformWidth(QUuid partId, float width);
void saveSnapshot(); void saveSnapshot();
void undo(); void undo();
void redo(); void redo();

View File

@ -422,7 +422,8 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
connect(m_document, &SkeletonDocument::partSubdivStateChanged, partListWidget, &SkeletonPartListWidget::partSubdivStateChanged); connect(m_document, &SkeletonDocument::partSubdivStateChanged, partListWidget, &SkeletonPartListWidget::partSubdivStateChanged);
connect(m_document, &SkeletonDocument::partDisableStateChanged, partListWidget, &SkeletonPartListWidget::partDisableStateChanged); connect(m_document, &SkeletonDocument::partDisableStateChanged, partListWidget, &SkeletonPartListWidget::partDisableStateChanged);
connect(m_document, &SkeletonDocument::partXmirrorStateChanged, partListWidget, &SkeletonPartListWidget::partXmirrorStateChanged); connect(m_document, &SkeletonDocument::partXmirrorStateChanged, partListWidget, &SkeletonPartListWidget::partXmirrorStateChanged);
connect(m_document, &SkeletonDocument::partThicknessChanged, partListWidget, &SkeletonPartListWidget::partThicknessChanged); connect(m_document, &SkeletonDocument::partDeformThicknessChanged, partListWidget, &SkeletonPartListWidget::partDeformChanged);
connect(m_document, &SkeletonDocument::partDeformWidthChanged, partListWidget, &SkeletonPartListWidget::partDeformChanged);
connect(m_document, &SkeletonDocument::cleanup, partListWidget, &SkeletonPartListWidget::partListChanged); connect(m_document, &SkeletonDocument::cleanup, partListWidget, &SkeletonPartListWidget::partListChanged);
connect(m_document, &SkeletonDocument::skeletonChanged, m_document, &SkeletonDocument::generateMesh); connect(m_document, &SkeletonDocument::skeletonChanged, m_document, &SkeletonDocument::generateMesh);

View File

@ -31,9 +31,9 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
initButton(m_xMirrorButton); initButton(m_xMirrorButton);
updateXmirrorButton(); updateXmirrorButton();
m_thicknessButton = new QPushButton(); m_deformButton = new QPushButton();
initButton(m_thicknessButton); initButton(m_deformButton);
updateThicknessButton(); updateDeformButton();
m_previewLabel = new QLabel; m_previewLabel = new QLabel;
@ -49,8 +49,8 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
miniBottomToolLayout->setSpacing(0); miniBottomToolLayout->setSpacing(0);
miniBottomToolLayout->setContentsMargins(0, 0, 0, 0); miniBottomToolLayout->setContentsMargins(0, 0, 0, 0);
miniBottomToolLayout->addWidget(m_subdivButton); miniBottomToolLayout->addWidget(m_subdivButton);
miniBottomToolLayout->addWidget(m_deformButton);
miniBottomToolLayout->addWidget(m_xMirrorButton); miniBottomToolLayout->addWidget(m_xMirrorButton);
miniBottomToolLayout->addWidget(m_thicknessButton);
miniBottomToolLayout->addStretch(); miniBottomToolLayout->addStretch();
QWidget *hrWidget = new QWidget; QWidget *hrWidget = new QWidget;
@ -74,7 +74,8 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
connect(this, &SkeletonPartWidget::setPartSubdivState, m_document, &SkeletonDocument::setPartSubdivState); connect(this, &SkeletonPartWidget::setPartSubdivState, m_document, &SkeletonDocument::setPartSubdivState);
connect(this, &SkeletonPartWidget::setPartDisableState, m_document, &SkeletonDocument::setPartDisableState); connect(this, &SkeletonPartWidget::setPartDisableState, m_document, &SkeletonDocument::setPartDisableState);
connect(this, &SkeletonPartWidget::setPartXmirrorState, m_document, &SkeletonDocument::setPartXmirrorState); connect(this, &SkeletonPartWidget::setPartXmirrorState, m_document, &SkeletonDocument::setPartXmirrorState);
connect(this, &SkeletonPartWidget::setPartThickness, m_document, &SkeletonDocument::setPartThickness); connect(this, &SkeletonPartWidget::setPartDeformThickness, m_document, &SkeletonDocument::setPartDeformThickness);
connect(this, &SkeletonPartWidget::setPartDeformWidth, m_document, &SkeletonDocument::setPartDeformWidth);
connect(m_lockButton, &QPushButton::clicked, [=]() { connect(m_lockButton, &QPushButton::clicked, [=]() {
const SkeletonPart *part = m_document->findPart(m_partId); const SkeletonPart *part = m_document->findPart(m_partId);
@ -121,17 +122,25 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
emit setPartXmirrorState(m_partId, !part->xMirrored); emit setPartXmirrorState(m_partId, !part->xMirrored);
}); });
connect(m_thicknessButton, &QPushButton::clicked, [=]() { connect(m_deformButton, &QPushButton::clicked, [=]() {
const SkeletonPart *part = m_document->findPart(m_partId); const SkeletonPart *part = m_document->findPart(m_partId);
if (!part) { if (!part) {
qDebug() << "Part not found:" << m_partId; qDebug() << "Part not found:" << m_partId;
return; return;
} }
showThicknessSettingPopup(mapFromGlobal(QCursor::pos())); showDeformSettingPopup(mapFromGlobal(QCursor::pos()));
}); });
} }
void SkeletonPartWidget::showThicknessSettingPopup(const QPoint &pos) void SkeletonPartWidget::initToolButton(QPushButton *button)
{
button->setFont(Theme::awesome()->font(Theme::toolIconFontSize / 2));
button->setFixedSize(Theme::toolIconSize / 2, Theme::toolIconSize / 2);
button->setStyleSheet("QPushButton {color: #f7d9c8}");
button->setFocusPolicy(Qt::NoFocus);
}
void SkeletonPartWidget::showDeformSettingPopup(const QPoint &pos)
{ {
QMenu popupMenu; QMenu popupMenu;
@ -141,14 +150,50 @@ void SkeletonPartWidget::showThicknessSettingPopup(const QPoint &pos)
return; return;
} }
FloatNumberWidget *popup = new FloatNumberWidget; QWidget *popup = new QWidget;
popup->setRange(0, 2);
popup->setValue(part->thickness);
connect(popup, &FloatNumberWidget::valueChanged, [=](float value) { FloatNumberWidget *thicknessWidget = new FloatNumberWidget;
emit setPartThickness(m_partId, value); thicknessWidget->setRange(0, 2);
thicknessWidget->setValue(part->deformThickness);
connect(thicknessWidget, &FloatNumberWidget::valueChanged, [=](float value) {
emit setPartDeformThickness(m_partId, value);
}); });
FloatNumberWidget *widthWidget = new FloatNumberWidget;
widthWidget->setRange(0, 2);
widthWidget->setValue(part->deformWidth);
connect(widthWidget, &FloatNumberWidget::valueChanged, [=](float value) {
emit setPartDeformWidth(m_partId, value);
});
QPushButton *thicknessEraser = new QPushButton(QChar(fa::eraser));
initToolButton(thicknessEraser);
connect(thicknessEraser, &QPushButton::clicked, [=]() {
thicknessWidget->setValue(1.0);
});
QPushButton *widthEraser = new QPushButton(QChar(fa::eraser));
initToolButton(widthEraser);
connect(widthEraser, &QPushButton::clicked, [=]() {
widthWidget->setValue(1.0);
});
QVBoxLayout *layout = new QVBoxLayout;
QHBoxLayout *thicknessLayout = new QHBoxLayout;
QHBoxLayout *widthLayout = new QHBoxLayout;
thicknessLayout->addWidget(thicknessEraser);
thicknessLayout->addWidget(thicknessWidget);
widthLayout->addWidget(widthEraser);
widthLayout->addWidget(widthWidget);
layout->addLayout(thicknessLayout);
layout->addLayout(widthLayout);
popup->setLayout(layout);
QWidgetAction *action = new QWidgetAction(this); QWidgetAction *action = new QWidgetAction(this);
action->setDefaultWidget(popup); action->setDefaultWidget(popup);
@ -248,17 +293,17 @@ void SkeletonPartWidget::updateXmirrorButton()
updateButton(m_xMirrorButton, QChar(fa::balancescale), false); updateButton(m_xMirrorButton, QChar(fa::balancescale), false);
} }
void SkeletonPartWidget::updateThicknessButton() void SkeletonPartWidget::updateDeformButton()
{ {
const SkeletonPart *part = m_document->findPart(m_partId); const SkeletonPart *part = m_document->findPart(m_partId);
if (!part) { if (!part) {
qDebug() << "Part not found:" << m_partId; qDebug() << "Part not found:" << m_partId;
return; return;
} }
if (part->thicknessAdjusted()) if (part->deformAdjusted())
updateButton(m_thicknessButton, QChar(fa::handlizardo), true); updateButton(m_deformButton, QChar(fa::handlizardo), true);
else else
updateButton(m_thicknessButton, QChar(fa::handlizardo), false); updateButton(m_deformButton, QChar(fa::handlizardo), false);
} }
void SkeletonPartWidget::reload() void SkeletonPartWidget::reload()
@ -269,7 +314,7 @@ void SkeletonPartWidget::reload()
updateSubdivButton(); updateSubdivButton();
updateDisableButton(); updateDisableButton();
updateXmirrorButton(); updateXmirrorButton();
updateThicknessButton(); updateDeformButton();
} }
SkeletonPartListWidget::SkeletonPartListWidget(const SkeletonDocument *document) : SkeletonPartListWidget::SkeletonPartListWidget(const SkeletonDocument *document) :
@ -376,7 +421,7 @@ void SkeletonPartListWidget::partXmirrorStateChanged(QUuid partId)
widget->updateXmirrorButton(); widget->updateXmirrorButton();
} }
void SkeletonPartListWidget::partThicknessChanged(QUuid partId) void SkeletonPartListWidget::partDeformChanged(QUuid partId)
{ {
auto item = m_itemMap.find(partId); auto item = m_itemMap.find(partId);
if (item == m_itemMap.end()) { if (item == m_itemMap.end()) {
@ -384,5 +429,5 @@ void SkeletonPartListWidget::partThicknessChanged(QUuid partId)
return; return;
} }
SkeletonPartWidget *widget = (SkeletonPartWidget *)itemWidget(item->second); SkeletonPartWidget *widget = (SkeletonPartWidget *)itemWidget(item->second);
widget->updateThicknessButton(); widget->updateDeformButton();
} }

View File

@ -16,7 +16,8 @@ signals:
void setPartDisableState(QUuid partId, bool disabled); void setPartDisableState(QUuid partId, bool disabled);
void setPartXmirrorState(QUuid partId, bool mirrored); void setPartXmirrorState(QUuid partId, bool mirrored);
void setPartZmirrorState(QUuid partId, bool mirrored); void setPartZmirrorState(QUuid partId, bool mirrored);
void setPartThickness(QUuid partId, float thickness); void setPartDeformThickness(QUuid partId, float thickness);
void setPartDeformWidth(QUuid partId, float width);
public: public:
SkeletonPartWidget(const SkeletonDocument *document, QUuid partId); SkeletonPartWidget(const SkeletonDocument *document, QUuid partId);
void reload(); void reload();
@ -27,9 +28,9 @@ public:
void updateDisableButton(); void updateDisableButton();
void updateXmirrorButton(); void updateXmirrorButton();
void updateZmirrorButton(); void updateZmirrorButton();
void updateThicknessButton(); void updateDeformButton();
public slots: public slots:
void showThicknessSettingPopup(const QPoint &pos); void showDeformSettingPopup(const QPoint &pos);
private: private:
const SkeletonDocument *m_document; const SkeletonDocument *m_document;
QUuid m_partId; QUuid m_partId;
@ -40,9 +41,10 @@ private:
QPushButton *m_disableButton; QPushButton *m_disableButton;
QPushButton *m_xMirrorButton; QPushButton *m_xMirrorButton;
QPushButton *m_zMirrorButton; QPushButton *m_zMirrorButton;
QPushButton *m_thicknessButton; QPushButton *m_deformButton;
QLabel *m_nameLabel; QLabel *m_nameLabel;
private: private:
void initToolButton(QPushButton *button);
void initButton(QPushButton *button); void initButton(QPushButton *button);
void updateButton(QPushButton *button, QChar icon, bool highlighted); void updateButton(QPushButton *button, QChar icon, bool highlighted);
}; };
@ -61,7 +63,7 @@ public slots:
void partSubdivStateChanged(QUuid partId); void partSubdivStateChanged(QUuid partId);
void partDisableStateChanged(QUuid partId); void partDisableStateChanged(QUuid partId);
void partXmirrorStateChanged(QUuid partId); void partXmirrorStateChanged(QUuid partId);
void partThicknessChanged(QUuid partId); void partDeformChanged(QUuid partId);
private: private:
const SkeletonDocument *m_document; const SkeletonDocument *m_document;
std::map<QUuid, QListWidgetItem *> m_itemMap; std::map<QUuid, QListWidgetItem *> m_itemMap;

View File

@ -33,7 +33,8 @@ int meshlite_get_halfedge_normal_array(void *context, int mesh_id, float *buffer
int meshlite_build(void *context, float *vertex_position_buffer, int vertex_count, int *face_index_buffer, int face_index_buffer_len); int meshlite_build(void *context, float *vertex_position_buffer, int vertex_count, int *face_index_buffer, int face_index_buffer_len);
int meshlite_bmesh_create(void *context); int meshlite_bmesh_create(void *context);
int meshlite_bmesh_set_cut_subdiv_count(void *context, int bmesh_id, int subdiv_count); int meshlite_bmesh_set_cut_subdiv_count(void *context, int bmesh_id, int subdiv_count);
int meshlite_bmesh_set_thickness(void *context, int bmesh_id, float thickness); int meshlite_bmesh_set_deform_thickness(void *context, int bmesh_id, float thickness);
int meshlite_bmesh_set_deform_width(void *context, int bmesh_id, float width);
int meshlite_bmesh_enable_debug(void *context, int bmesh_id, int enable); int meshlite_bmesh_enable_debug(void *context, int bmesh_id, int enable);
int meshlite_bmesh_add_node(void *context, int bmesh_id, float x, float y, float z, float radius); int meshlite_bmesh_add_node(void *context, int bmesh_id, float x, float y, float z, float radius);
int meshlite_bmesh_add_edge(void *context, int bmesh_id, int first_node_id, int second_node_id); int meshlite_bmesh_add_edge(void *context, int bmesh_id, int first_node_id, int second_node_id);

View File

@ -33,7 +33,8 @@ int meshlite_get_halfedge_normal_array(void *context, int mesh_id, float *buffer
int meshlite_build(void *context, float *vertex_position_buffer, int vertex_count, int *face_index_buffer, int face_index_buffer_len); int meshlite_build(void *context, float *vertex_position_buffer, int vertex_count, int *face_index_buffer, int face_index_buffer_len);
int meshlite_bmesh_create(void *context); int meshlite_bmesh_create(void *context);
int meshlite_bmesh_set_cut_subdiv_count(void *context, int bmesh_id, int subdiv_count); int meshlite_bmesh_set_cut_subdiv_count(void *context, int bmesh_id, int subdiv_count);
int meshlite_bmesh_set_thickness(void *context, int bmesh_id, float thickness); int meshlite_bmesh_set_deform_thickness(void *context, int bmesh_id, float thickness);
int meshlite_bmesh_set_deform_width(void *context, int bmesh_id, float width);
int meshlite_bmesh_enable_debug(void *context, int bmesh_id, int enable); int meshlite_bmesh_enable_debug(void *context, int bmesh_id, int enable);
int meshlite_bmesh_add_node(void *context, int bmesh_id, float x, float y, float z, float radius); int meshlite_bmesh_add_node(void *context, int bmesh_id, float x, float y, float z, float radius);
int meshlite_bmesh_add_edge(void *context, int bmesh_id, int first_node_id, int second_node_id); int meshlite_bmesh_add_edge(void *context, int bmesh_id, int first_node_id, int second_node_id);