Drawing DzViewTool

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)

Rotate Tool

 

My attempt (yellow dot, various sizes)

My Tool

 

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

  • surrealsurreal Posts: 198

    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

  • Richard HaseltineRichard Haseltine Posts: 107,857

    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.

  • OmnifluxOmniflux Posts: 427
    edited March 27

    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.

    Post edited by Omniflux on
  • OmnifluxOmniflux Posts: 427

    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

    [WARNING] :: \src\sdksource\daz\dzvec3.cpp(1243): Attempted a divide by zero in DzVec3::operator/=
    

    (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;
    }
    
Sign In or Register to comment.