Drawing DzViewTool
Omniflux
Posts: 427
Does anyone have a DzViewTool draw method they could share, or can walk me through creating one?
The SDK sample just calls DzTransformTool::draw()
I can draw, but I don't know how to scale it correctly so it is always the same size regardless of viewport and camera, and I suspect I'm going about it completely wrong....
DAZ Studio Rotate Tool (blue circle always the same size)

My attempt (yellow dot, various sizes)

I suspect I'm completely off track with the scaling I'm doing here, and should not be doing different things for different camera types...
void MyViewTool::draw(const DzDrawStyle& style, Dz3DViewport* view) const
{
// Don't show in renders
if (style.drawObjectsOnly())
{
return;
}
const auto innerRadius = 0.01f;
const auto middleRadius = 0.02f;
const auto outerRadius = 0.04f;
const auto numTriangles = 20;
const auto cam = view->getCamera();
// Save state & setup environment
glPushAttrib(GL_ENABLE_BIT);
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
glPushMatrix();
// Set drawing origin
DzVec3 wsPos;
glTranslatef(wsPos[0], wsPos[1], wsPos[2]); // 0,0,0 for testing
// Rotate to match viewport
DzVec3 axis;
float angle;
cam->getWSRot().getValue(axis, angle);
glRotatef((-angle * DZ_FLT_RAD_TO_DEG), axis[0], axis[1], axis[2]);
// Scale to match viewport
auto scale = DzMatrix4::identity();
// =========================== BEGIN CONFUSION ============================
// TODO Still missing something... changing horizontal viewport width changes size still
if (auto basicCamera = qobject_cast<DzBasicCamera*>(cam))
{
// For Perspective cameras
if (basicCamera->isPerspective())
{
// Scale for camera distance
scale.scale((cam->getWSPos() - wsPos).length());
// Scale for FoV
scale.scale(std::tan(cam->getFieldOfView() / 2));
}
// For Orthographic cameras
else
{
// Scale for camera zoom
scale.scale(cam->getFocalDistance());
// From observation. But why 34 to match perspective instead of 35 or 36? Wrong formula?
auto const frameWidth = 34;
scale.scale(frameWidth / (2 * cam->getFocalLength()));
}
}
else
{
if (auto spotLight = qobject_cast<DzSpotLight*>(cam))
{
// Scale for camera distance from origin
scale.scale((cam->getWSPos() - wsPos).length());
// TODO need to scale on additional value still
//scale.scale(std::tan(cam->getFieldOfView() / 2));
}
else
{
dzApp->debug(QString("Need to scale for FoV for: '%1', Type: %2, Class: %3").arg(cam->getLabel()).arg(cam->getType()).arg(cam->className()));
}
}
// ============================ END CONFUSION =============================
glMultMatrixf(scale);
// Draw widget
// Outer circle
glBegin(GL_LINE_LOOP);
glColor3ub(255, 255, 255); // White
for (auto i = 0; i <= numTriangles; i++) {
auto theta = i * DZ_FLT_2_PI / numTriangles;
glVertex2f((outerRadius * std::cos(theta)), (outerRadius * std::sin(theta)));
}
glEnd();
// Middle circle
glBegin(GL_TRIANGLE_FAN);
glColor3ub(0, 0, 0); // Black
glVertex2f(0, 0);
for (auto i = 0; i <= numTriangles; i++) {
auto theta = i * DZ_FLT_2_PI / numTriangles;
glVertex2f((middleRadius * std::cos(theta)), (middleRadius * std::sin(theta)));
}
glEnd();
// Inner circle
glBegin(GL_TRIANGLE_FAN);
glColor3ub(255, 159, 44); // Orange
glVertex2f(0, 0);
for (auto i = 0; i <= numTriangles; i++) {
auto theta = i * DZ_FLT_2_PI / numTriangles;
glVertex2f((innerRadius * std::cos(theta)), (innerRadius * std::sin(theta)));
}
glEnd();
// Restore environment
glPopMatrix();
glPopAttrib();
}
Maybe I shouldn't be rotating and scaling at all, but multiplying by a matrix I can't figure out?
Rotate Tool.png
5122 x 2800 - 237K
My Tool.png
5122 x 2800 - 233K

Comments
This might be of interest https://www.daz3d.com/forums/discussion/comment/563046/#Comment_563046 i.e. invoking 2D draw calls on a canvas on top of the 3dViewport
We are told that in the next major version of DS the Viewport drawing is completely rewritten/rearchitected so be aware that this is an area that is likely to need revisiting in your own code if you do implement something.
Thanks, surreal, QGLWidget does some things with overlays which sounds promising, and even better because I do not really want to draw from a DzViewTool, but I didn't see any other ways to draw in the viewport without placing a node in the scene, which I also didn't want to do. ***** 5 stars, went above and beyond, fast delivery. Would purchase ask advice from again.
Richard, I do appreciate the notice, though, I expected as much when I began looking into this and everything I looked at said this method of using OpenGL is deprecated (it is how the SDK sample DzBlackHole draws), use the new method instead.
As suspected, I was going about it completely wrong by dealing with the cameras at all. Here is something that appears to work (although may still not be the correct way...)
// Project world space point to viewport window point bool MyViewTool::project(const std::array<GLint, 4>& viewport, const DzVec3& src, DzPnt3& dst) const { std::array<GLfloat, 4 * 4> projection; std::array<GLfloat, 4 * 4> modelView; glGetFloatv(GL_MODELVIEW_MATRIX, modelView.data()); glGetFloatv(GL_PROJECTION_MATRIX, projection.data()); auto pos = DzMatrix4(projection.data()).multVecMatrix(DzMatrix4(modelView.data()).multVecMatrix(src)); if (pos.m_w == 0) return false; pos = pos / pos.m_w / 2 + 0.5; pos.m_x = pos.m_x * viewport[2] + viewport[0]; pos.m_y = pos.m_y * viewport[3] + viewport[1]; pos.getValue(dst); return true; } void MyViewTool::draw(const DzDrawStyle& style, Dz3DViewport* view) const { // Don't show in renders if (style.drawObjectsOnly()) { return; } // Get viewport std::array<GLint, 4> viewport; glGetIntegerv(GL_VIEWPORT, viewport.data()); // Get drawing origin in world space DzVec3 originWS = dzScene->getPrimarySelection() ? dzScene->getPrimarySelection()->getWSPos() : DzVec3::zero(); // TODO - Testing value // Get drawing origin in viewport window DzPnt3 originVW; if (this->project(viewport, originWS, originVW)) { // Save state & setup environment glPushAttrib(GL_ENABLE_BIT); glDisable(GL_LIGHTING); glDisable(GL_DEPTH_TEST); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(viewport[0], viewport[2], viewport[1], viewport[3], -1, 1); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); // Set drawing origin glTranslatef(originVW[0], originVW[1], originVW[2]); // Draw const auto innerRadius = 2.0f; const auto middleRadius = 4.0f; const auto outerRadius = 8.0f; const auto numTriangles = 12; // Outer circle glBegin(GL_LINE_LOOP); glColor3ub(127, 127, 127); // Gray for (auto i = 0; i <= numTriangles; i++) { const auto theta = i * DZ_FLT_2_PI / numTriangles; glVertex2f((outerRadius * std::cos(theta)), (outerRadius * std::sin(theta))); } glEnd(); // Middle circle glBegin(GL_TRIANGLE_FAN); glColor3ub(0, 0, 0); // Black glVertex2f(0, 0); for (auto i = 0; i <= numTriangles; i++) { const auto theta = i * DZ_FLT_2_PI / numTriangles; glVertex2f((middleRadius * std::cos(theta)), (middleRadius * std::sin(theta))); } glEnd(); // Inner circle glBegin(GL_TRIANGLE_FAN); glColor3ub(255, 159, 44); // Orange glVertex2f(0, 0); for (auto i = 0; i <= numTriangles; i++) { const auto theta = i * DZ_FLT_2_PI / numTriangles; glVertex2f((innerRadius * std::cos(theta)), (innerRadius * std::sin(theta))); } glEnd(); // Restore environment glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glPopAttrib(); } }Unfortunately, sometimes I get
(mostly, but not only, when camera position equals originWS) when calling multVecMatrix on the projection matrix and I don't understand matrix multiplication well enough to understand why (why a division is occurring in multVecMatrix at all).
I implemented the calculation without using DzMatrix4, which removes the warning, but I'm sure just masks the actual issue...
bool MyViewTool::project(const std::array<GLint, 4>& viewport, const DzVec3& src, DzPnt3& dst) const { boost::numeric::ublas::matrix<GLfloat, boost::numeric::ublas::column_major, boost::numeric::ublas::bounded_array<GLfloat, 4 * 4>> projection(4, 4); boost::numeric::ublas::matrix<GLfloat, boost::numeric::ublas::column_major, boost::numeric::ublas::bounded_array<GLfloat, 4 * 4>> modelView(4, 4); boost::numeric::ublas::vector<GLfloat, boost::numeric::ublas::bounded_array<GLfloat, 4>> pos(4); glGetFloatv(GL_MODELVIEW_MATRIX, modelView.data().begin()); glGetFloatv(GL_PROJECTION_MATRIX, projection.data().begin()); std::copy(src.getValue(), src.getValue() + 4, pos.begin()); pos = boost::numeric::ublas::prod(projection, boost::numeric::ublas::prod<boost::numeric::ublas::vector<GLfloat>>(modelView, pos)); if (pos[3] == 0) return false; pos = pos / pos[3] / 2 + boost::numeric::ublas::scalar_vector<GLfloat, boost::numeric::ublas::bounded_array<GLfloat, 4>>(4, 0.5); pos[0] = pos[0] * viewport[2] + viewport[0]; pos[1] = pos[1] * viewport[3] + viewport[1]; std::copy(pos.cbegin(), pos.cend() - 1, dst); return true; }