Jump to content
  • Advertisement
Sign in to follow this  
Autkast

Billboarding 2D text in 3D Space (SlimDX, C#, Direct3D9)

This topic is 2342 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello!

I've been trying to render 2D text in a 3D space while billboarding towards my camera. I'm using Directx9 in C# with the SlimDX wrapper, which is pretty awesome, I must say.

So far the best solution I've come up with is using a RenderTarget to render the 2D text to a texture, then I use a sprite (SpriteFlags.Billboard is the best thing since sliced bread) to draw the texture with the correct matrix transformation and so on. It works great, but I can't figure out how to get at the Texture Coordinates to flip the texture vertically. Right now it's drawing upside down. If I was using a shader this would be ridiculously easy but the application is not using shaders (yet). I need a quicker solution than moving everything over to shaders.

I'm pretty new to SlimDX and therein lies my problem. Does anyone know how to get at the Texture Coordinate data inside a Sprite with SlimDX?

Please and thank you!

~Nick

Share this post


Link to post
Share on other sites
Advertisement
Okay, to avoid any confusion and to help people answer my question, here is some code.


font = new SlimDX.Direct3D9.Font(device, new System.Drawing.Font(textObj.TextString.FontFamily, textObj.TextString.Height));
Rectangle stringRect = font.MeasureString(sprite, textObj.TextString.Text, DrawTextFormat.Left);
Texture texture = new Texture(device, stringRect.Width, stringRect.Height, 1, Usage.RenderTarget, Format.A8R8G8B8, Pool.Default);
RenderToSurface rts = new RenderToSurface(device, stringRect.Width, stringRect.Height, Format.A8R8G8B8, Format.D24X8);
rts.BeginScene(texture.GetSurfaceLevel(0), new Viewport(0, 0, stringRect.Width, stringRect.Height, 0f, 1f));
{
sprite.Begin(SpriteFlags.SortDepthFrontToBack | SpriteFlags.AlphaBlend);
{
font.DrawString(sprite, textObj.TextString.Text, 0, 0, textObj.TextString.Color.ToArgb());
}
sprite.End();
}
rts.EndScene(Filter.Triangle);

textObj.Texture = texture;


So right now I'm trying to figure out how to get at the vertex data of the sprite before/while I draw it so I can flip the "v" texture coordinates because my floating text is upside down.

Please and Thank you!


P.S. I realize I'm not using powers of 2 for the width and height of my texture. Does anyone know some quick math to round up to the nearest power of 2?

Share this post


Link to post
Share on other sites
Sorry for the obvious bump, but I'm still stuck on this. We've got regression testing coming up and I really need a breakthrough on this so I can move this feature to Testing Ready.

Share this post


Link to post
Share on other sites
The documentation suggests that you need to call SetWorldViewLH or SetWorldViewRH on the sprite object if you're going to be using it for billboarding or sorting sprites. I imagine you could use that transformation matrix to perform the flip.

Share this post


Link to post
Share on other sites
Hi, This is probably way late for your regression testing but it is one of the most asked about and not well documented topics so I thought I would answer it here. The way I resolved this was to create a surface area the size of the text to be displayed and then copied it to a texture. You can then apply the texture to some geometry and use it like a regular texture. I pass mine to a pixel shader so that I can do some effects with it. So here are some code snips so you can see how it works.

// I define a font in order to get the texture dimensions in pixels

[font=courier new,courier,monospace]D3DXCreateFont(device,
size, // height of font
0, // average character width
w, // font weight
1, // mip levels
0, // italic attribute option
DEFAULT_CHARSET, // character set identifier
OUT_DEFAULT_PRECIS, // output precision
PROOF_QUALITY, // output quality
DEFAULT_PITCH|FF_MODERN, // fPitch and family
typeface, // typeface name
&font
);[/font]

// Here's how you get the pixel width and height for the string:

[font=courier new,courier,monospace]HDC hdc = font->GetDC();
GetTextExtentPoint32(hdc, str, strlen(str), &sz);[/font]

// Here's the surface that the text will be drawn on
// notice that it uses the system mem pool so be carefull if you have like thousands of name plates being rendered
// if you are not threading memory shouldn't be a problem as this surface is freed at the end

[font=courier new,courier,monospace]device->CreateOffscreenPlainSurface(sz->cx,
sz->cy,
D3DFMT_X8R8G8B8,
D3DPOOL_SYSTEMMEM,
&pRenderTarget,
0);[/font]

// Here's the texture where the surface is copied to

[font=courier new,courier,monospace]D3DXCreateTexture(device,
sz->cx,
sz->cy,
1,
D3DUSAGE_AUTOGENMIPMAP,
D3DFMT_X8R8G8B8,
D3DPOOL_DEFAULT,
&pRenderTargetTexture);[/font]

// get the device context for the render target (the surface not the texture)

[font=courier new,courier,monospace]pRenderTarget->GetDC(&hRenderTarget);[/font]

// set up a log font

[font=courier new,courier,monospace]logfont.lfWidth = font->GetDesc()->Width;
logfont.lfHeight = font->GetDesc()->Height;
logfont.lfWeight = font->GetDesc()->Weight;
logfont.lfCharSet = font->GetDesc()->CharSet;
logfont.lfOutPrecision = font->GetDesc()->OutputPrecision;
logfont.lfQuality = font->GetDesc()->Quality;
logfont.lfPitchAndFamily = font->GetDesc()->PitchAndFamily;
strcpy(logfont.lfFaceName, font->GetDesc()->FaceName);[/font]

// create the log font

[font=courier new,courier,monospace]hFont = CreateFontIndirect(&logfont);[/font]

// use the font for the text to be drawn

[font=courier new,courier,monospace]SelectObject(hRenderTarget, hFont);
SetBkMode(hRenderTarget, TRANSPARENT);
SetBkMode(hRenderTarget, OPAQUE);
SetBkColor(hRenderTarget, RGB(0, 0, 0));
SetTextColor(hRenderTarget, RGB(255, 0, 0));[/font]

// draw the text to the render target (surface)

[font=courier new,courier,monospace]DrawText(hRenderTarget, str, -1, &rect, DT_LEFT|DT_NOCLIP|DT_SINGLELINE);[/font]

// release the surfaces device context

[font=courier new,courier,monospace]pRenderTarget->ReleaseDC(hRenderTarget);[/font]

// done with the log font

[font=courier new,courier,monospace]DeleteObject(hFont);[/font]

// rectangle for dimensions of the text

[font=courier new,courier,monospace]rect.left = 0;
rect.top = 0;
rect.bottom = sz->cy;
rect.right = sz->cx;[/font]

// this gets the surface area of the texture

[font=courier new,courier,monospace]pRenderTargetTexture->GetSurfaceLevel(0, &pRenderTargetTextureSurface);[/font]

// then use UpdateSurface to copy the drawn text surface to the texture's surface

[font=courier new,courier,monospace]device->UpdateSurface(pRenderTarget, &rect, pRenderTargetTextureSurface, &p);[/font]

// release the render surface because you don't need it anymore

[font=courier new,courier,monospace]if (pRenderTarget)
{
pRenderTarget->Release();
pRenderTarget = 0;
}[/font]

And that is all there is to making billboarded text with Directx9 and Windows. I do know that OGL has similar API's for setting up fonts, surfaces, and textures so it could be the same thought process. I don't know what the API's are in XNA C# so hope this helps to give some insight on what to do with that. Edited by codejockey

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!