How to draw ofTrueType labels inside an ofCamera as ofDrawBitmapString() does?

Hello,

I am trying to customize the ofDrawGrid();

void ofDrawGrid(float stepSize, size_t numberOfSteps, bool labels, bool x, bool y, bool z);

same method but replacing ofBitmapFont labels with custom fonts.

I want to get something like that,
but labels are not shown when enable the ofTrueTypeFont:

I can’t figure out how to deal with camera->worldToScreen() or something else that I am missing…

Anybody has made that before to share a snippet or any thoughts?

That’s the small function I am using to draw a text into the camera:

void ofxDrawString(string s, float x, float y, float z, ofTrueTypeFont* font, ofCamera* camera) {
	if (font == nullptr) return;
	if (camera == nullptr) return;

	glm::vec3 posScreen = camera->worldToScreen(glm::vec3(x, y, z));
		
    // tryed too:
    //ofRectangle r = ofGetCurrentViewport();
	//glm::vec3 posScreen = camera->worldToScreen(glm::vec3(x, y, z), r);

	float _x = posScreen.x;
	float _y = posScreen.y;

	font->drawString(s, _x, _y);
}

And here ofxDrawGrid() and ofxDrawGridPlane(). Uses the above ofxDrawString():

// Draws a grid of the three planes with labels
void ofxDrawGrid(float stepSize, size_t numberOfSteps, bool labels, bool x, bool y, bool z, ofTrueTypeFont* font, ofCamera* camera)
{
	//ofPushStyle();

	ofColor c;
	ofColor prevColor = ofGetStyle().color;

	if (x) {
		c.setHsb(0.0f, 0.0f, 125.0f);
		ofSetColor(c);
		if (font == nullptr || camera == nullptr) ofDrawGridPlane(stepSize, numberOfSteps, labels); // bitmap
		else ofxDrawGridPlane(stepSize, numberOfSteps, labels, font, camera); // custom
	}
	if (y) {
		c.setHsb(0.0f, 0.0f, 125.0f);
		ofSetColor(c);
		glm::mat4 m = glm::rotate(glm::mat4(1.0), glm::half_pi<float>(), glm::vec3(0, 0, -1));
		ofPushMatrix();
		ofMultMatrix(m);
		if (font == nullptr || camera == nullptr) ofDrawGridPlane(stepSize, numberOfSteps, labels); // bitmap
		else ofxDrawGridPlane(stepSize, numberOfSteps, labels, font, camera); // custom
		ofPopMatrix();
	}
	if (z) {
		c.setHsb(0.0f, 0.0f, 125.0f);
		ofSetColor(c);
		glm::mat4 m = glm::rotate(glm::mat4(1.0), glm::half_pi<float>(), glm::vec3(0, 1, 0));
		ofPushMatrix();
		ofMultMatrix(m);
		if (font == nullptr || camera == nullptr) ofDrawGridPlane(stepSize, numberOfSteps, labels); // bitmap
		else ofxDrawGridPlane(stepSize, numberOfSteps, labels, font, camera); // custom
		ofPopMatrix();
	}

	if (labels) { // bitmap
		if (font == nullptr || camera == nullptr) {
			ofDrawBitmapMode mode = ofGetStyle().drawBitmapMode;
			ofSetColor(255, 255, 255);
			float labelPos = stepSize * (numberOfSteps + 0.5);

			ofSetDrawBitmapMode(OF_BITMAPMODE_MODEL_BILLBOARD);
			ofDrawBitmapString("x", labelPos, 0, 0);
			ofDrawBitmapString("y", 0, labelPos, 0);
			ofDrawBitmapString("z", 0, 0, labelPos);
			ofSetDrawBitmapMode(mode);
		}
		else { // custom
			float labelPos = stepSize * (numberOfSteps + 0.5);

			ofxDrawString("x", labelPos, 0, 0, font, camera);
			ofxDrawString("y", 0, labelPos, 0, font, camera);
			ofxDrawString("z", 0, 0, labelPos, font, camera);
		}
	}
	ofSetColor(prevColor);

	//ofPopStyle();
}

// Allows passing a font and the camera to customize style instead of using bitmap font
void ofxDrawGridPlane(float stepSize, size_t numberOfSteps, bool labels, ofTrueTypeFont* font, ofCamera* camera)
{
	auto renderer = ofGetCurrentRenderer();

	float scale = stepSize * numberOfSteps;
	float lineWidth = renderer->getStyle().lineWidth;

	// Draw all the lines
	for (int iDimension = 0; iDimension < 2; iDimension++)
	{
		for (size_t i = 0; i <= numberOfSteps; i++)
		{
			float yz = i * stepSize;

			if (i == numberOfSteps || i == 0)
				renderer->setLineWidth(2); // central axis or cap line
			else if (i % 2 == 0) {
				renderer->setLineWidth(1.5); // major
			}
			else {
				renderer->setLineWidth(1); // minor
			}

			if (iDimension == 0) {
				renderer->drawLine(0, yz, -scale, 0, yz, scale);
				if (yz != 0) renderer->drawLine(0, -yz, -scale, 0, -yz, scale);
			}
			else {
				renderer->drawLine(0, -scale, yz, 0, scale, yz);
				if (yz != 0) renderer->drawLine(0, -scale, -yz, 0, scale, -yz);
			}
		}
	}
	renderer->setLineWidth(lineWidth);

	// Draw all the labels
	if (labels) {
		//draw numbers on axes
		if (font == nullptr || camera == nullptr) // bitmap
		{
			ofColor prevColor = renderer->getStyle().color;
			ofDrawBitmapMode mode = renderer->getStyle().drawBitmapMode;

			renderer->setColor(255, 255, 255);
			renderer->setBitmapTextMode(OF_BITMAPMODE_MODEL_BILLBOARD);

			renderer->drawString(ofToString(0), 0, 0, 0);

			for (float i = 1; i <= numberOfSteps; i++)
			{
				float yz = i * stepSize;

				renderer->drawString(ofToString(yz), 0, yz, 0);
				renderer->drawString(ofToString(-yz), 0, -yz, 0);
				renderer->drawString(ofToString(yz), 0, 0, yz);
				renderer->drawString(ofToString(-yz), 0, 0, -yz);
			}

			renderer->setColor(prevColor);
			renderer->setBitmapTextMode(mode);
		}
		else // custom 
		{
			ofPushStyle();

			ofxDrawString(ofToString(0), 0, 0, 0, font, camera);

			for (float i = 1; i <= numberOfSteps; i++)
			{
				float yz = i * stepSize;
				ofxDrawString(ofToString(yz), 0, yz, 0, font, camera);
				ofxDrawString(ofToString(-yz), 0, -yz, 0, font, camera);
				ofxDrawString(ofToString(yz), 0, 0, yz, font, camera);
				ofxDrawString(ofToString(-yz), 0, 0, -yz, font, camera);
			}
			ofPopStyle();
		}
	}
}

Then in ofApp:

//.h
ofTrueTypeFont font;
ofEasyCam camera;

//--

//.cpp
//draw()
camera.begin();

//ofScale(100.f);//this will break it all?

// A. trying to draw planes with labels using ofTrueTypeFont's
ofxDrawGrid(0.5f, 10, true, false, true, false, &font, &camera); 

// B. uses bitmap fonts if camera or font are nullptr's. 
// it works fine like in the above top screenshot
//ofxDrawGrid(0.5f, 10, true, false, true, false, nullptr, nullptr);
 
camera.begin();

Regards

I’ve just read the code, didn’t test anything here, but some ideas:
try to store the coordinates and draw the font outside cam.begin and cam.end
so you are drawing font in 2d not in 3d

1 Like

Yes, thanks!

Then the problem of drawing out of the camera begin/end is that we will need to split to two functions. (inside and outside the cam):

camera.begin();
ofxDrawGrid(0.5f, 10, true, false, true, false, &font, &camera); 
camera.begin();

drawLabels(0.5f, 10, true, false, true, false, &font, &camera);

Also maybe transformations will break all…

camera.begin();
ofScale(100.f);//will break all worldToScreen on 2nd function
..

That seems the sense of the method from
of\libs\openFrameworks\gl\ofGLProgrammableRenderer.cpp:
void ofGLRenderer::drawString(string textString, float x, float y, float z);

void ofxDrawString(string textString, float x, float y, float z, ofTrueTypeFont* font) {

/*
        // Problems when moved to ofApp...

        // ?
		//ofGLRenderer* mutThis = const_cast<ofGLRenderer*>(this);
		
        // ?
		auto g = ofGetGLRenderer();
		ofGLRenderer* mutThis = g;

        // ?
		//auto g = ofGetCurrentRenderer();
		//'const_cast': cannot convert from 'std::shared_ptr<ofBaseRenderer>' to 'ofGLRenderer *' 
		//ofGLRenderer* mutThis = const_cast<ofGLRenderer*>(g);
*/

...
		//case OF_BITMAPMODE_MODEL_BILLBOARD:
		{
			//our aim here is to draw to screen
			//at the viewport position related
			//to the world position x,y,z

			// tig: we want to get the signed normalised screen coordinates (-1,+1) of our point (x,y,z)
			// that's projection * modelview * point in GLSL multiplication order
			// then doing the good old (v + 1.0) / 2. to get unsigned normalized screen (0,1) coordinates.
			// we then multiply x by width and y by height to get window coordinates.
			
			rViewport = getCurrentViewport();
			
			glm::mat4 mat = matrixStack.getProjectionMatrixNoOrientation()  * matrixStack.getModelViewMatrix();
			glm::vec4 dScreen4 = mat * glm::vec4(x,y,z,1.0);
			glm::vec3 dScreen = glm::vec3(dScreen4) / dScreen4.w;
			dScreen += glm::vec3(1.0) ;
			dScreen *= 0.5;
			
			dScreen.x += rViewport.x;
			dScreen.x *= rViewport.width;

			dScreen.y += rViewport.y;
			dScreen.y *= rViewport.height;
			
			if (dScreen.z >= 1) return;


			hasProjection = true;
			mutThis->matrixMode(OF_MATRIX_PROJECTION);
			mutThis->pushMatrix();
			mutThis->loadIdentityMatrix();

			hasModelView = true;
			mutThis->matrixMode(OF_MATRIX_MODELVIEW);
			mutThis->pushMatrix();

			modelView = glm::translate(modelView, glm::vec3(-1,-1,0));
			modelView = glm::scale(modelView, glm::vec3(2/rViewport.width, 2/rViewport.height, 1));
			modelView = glm::translate(modelView, glm::vec3(dScreen.x, dScreen.y, 0));
			mutThis->loadMatrix(modelView);
		}
			//break;
...

/*
    // Bitmap font
	// (c) enable texture once before we start drawing each char (no point turning it on and off constantly)
	//We do this because its way faster
	mutThis->setAlphaBitmapText(true);
	ofMesh charMesh = bitmapFont.getMesh(textString, sx, sy, currentStyle.drawBitmapMode, isVFlipped());
	mutThis->bind(bitmapFont.getTexture(),0);
	draw(charMesh,OF_MESH_FILL,false,true,false);
	mutThis->unbind(bitmapFont.getTexture(),0);
	mutThis->setAlphaBitmapText(false);
*/
    // TrueType font
	// Draw mesh text 
	ofMesh charMesh = font->getStringMesh(textString, sx, sy, vflipped);
	g->draw(charMesh, OF_MESH_FILL);

...

I go try to modify that ofGLRenderer::drawString()
and to switch bitmapFont and draw ofTrueTypeFont’s as meshes…

As it’s inside ofGLRenderer there’s some hard to understad lines/topics as:

ofGLRenderer* mutThis = const_cast<ofGLRenderer*>(this);
glm::mat4 mat = matrixStack.getOrientationMatrixInverse() * projection * modelview;
ofMatrixStack matrixStack;
...

I managed to solve it via drawing the labels out of the camera.
Fixing inside the camera it not was trivial, but I did some tries…

Here is all the code:

1 Like