Drawing pure XNA between TX layers [text and avatars specifically]
by GameBlox Interactive (Member: Diego Leao) · in Torque X 2D · 10/03/2009 (3:34 am) · 15 replies
I would like to put an Xbox 360 avatar in my TX2D game, but I'm not sure what to do. Is it easy? Note that I want to display it in between TX2D layers, not on top of all layers. I could research this myself, but if anyone already knows the work involved it would be very very helpfull!!
Another related question: can I display pure XNA images / meshes on TX2D, without extensively modifying the engine?
PS: I have TX 2D PRO and I'm a developer, so don't be shy if you want to show me some code ^^.
Another related question: can I display pure XNA images / meshes on TX2D, without extensively modifying the engine?
PS: I have TX 2D PRO and I'm a developer, so don't be shy if you want to show me some code ^^.
About the author
Currently finishing a great game prototyping tool for the Xbox 360, and some downloadable games. Email: contact@gameblox.net.
#2
John K.
www.envygames.com
10/05/2009 (1:04 am)
Avatar rendering support is scheduled for the release after next (probably around January). As Duncan mentioned, rendering the avatar is pretty easy to do, it's integrating it into the scene graph that's hard (at least for the 3D implementation) and will require changes to the render manager.John K.
www.envygames.com
#3
thkz
10/05/2009 (11:53 pm)
Hi, can u give me an example of how i do that(render a string to a Texture2D)? The Draw what you talking about is that i inherit from TorqueGame? thkz
#4
ty
10/06/2009 (12:38 am)
hey, im doing this but it isn't working!oldTarget = Game.Instance.GraphicsDevice.GetRenderTarget(0);
Game.Instance.GraphicsDevice.SetRenderTarget(0, renderTarget2D);
Game.Instance.GraphicsDevice.Clear(ClearOptions.Target, Color.Blue, 0, 0);
spriteBatch.Begin();
//using this to draw a text seems not work
spriteBatch.DrawString(font.Instance, text, GUIUtil.ConvertWorldToScreen(Position),
Color.White, 0, Vector2.Zero, 1, SpriteEffects.None,
0);
spriteBatch.End();
Game.Instance.GraphicsDevice.SetRenderTarget(0,oldTarget as RenderTarget2D);EDITED: Wow, its almost working! my sprite was replaced by a blue square. For the text, I guess it's position was wrong... checking it now...ty
#5
Is it throwing an Exception; if so what is it???
Also, what is the code you are using to create renderTarget2D???
And when do you call GetTexture on it???
10/06/2009 (12:46 am)
Not enough details - in what way is it not working???Is it throwing an Exception; if so what is it???
Also, what is the code you are using to create renderTarget2D???
And when do you call GetTexture on it???
#6
Create a GuiPlay Screen
Create a new class, named GuiPlay, and add it to your project.
Switch to this screen
Before you load your scene, switch to the new GuiPlay Screen.
This is a lot easier and a bit more flexible (in my opinion). In fact, the next version of Torque X will include a new GUI template that sets this all up for you.
John K.
www.envygames.com
10/06/2009 (12:48 am)
Diego, all you want to do is render text to the screen? Here's an easier way to do it. Try this...Create a GuiPlay Screen
Create a new class, named GuiPlay, and add it to your project.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GarageGames.Torque.GUI;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace StarterGame3D
{
public class GuiPlay : GUISceneview, IGUIScreen
{
GUITextStyle styleMain = new GUITextStyle();
GUIText _playerScore1;
public GuiPlay()
{
GUIStyle playStyle = new GUIStyle();
Name = "GuiPlay";
Style = playStyle;
Size = new Vector2(1280, 720);
Folder = GUICanvas.Instance;
styleMain = new GUITextStyle();
styleMain.FontType = "Arial22"; // @"dataimagesMyFont";
styleMain.TextColor[0] = Color.Red;
styleMain.SizeToText = false;
styleMain.Alignment = TextAlignment.JustifyCenter;
styleMain.PreserveAspectRatio = true;
_playerScore1 = new GUIText();
_playerScore1.Style = styleMain;
_playerScore1.Position = new Vector2(25, 25);
_playerScore1.Size = new Vector2(400, 120);
_playerScore1.Visible = true;
_playerScore1.Folder = this;
_playerScore1.Text = "Player Score:";
}
public override void OnRender(Vector2 offset, GarageGames.Torque.MathUtil.RectangleF updateRect)
{
_playerScore1.Text = "Player Score: " + Game.Instance.PlayerScore;
base.OnRender(offset, updateRect);
}
}
} Switch to this screen
Before you load your scene, switch to the new GuiPlay Screen.
GuiPlay playGUI = new GuiPlay();
GUICanvas.Instance.SetContentControl(playGUI);
Game.Instance.SceneLoader.Load(@"datalevelslevelData.txscene"); This is a lot easier and a bit more flexible (in my opinion). In fact, the next version of Torque X will include a new GUI template that sets this all up for you.
John K.
www.envygames.com
#7
@Duncan: It was throwing an exception, but I was using the texture before setting the old render target ;) Now it is working (see the 'edit' text)!!
10/06/2009 (1:03 am)
@John: I want the text to work with layers, but now I can only get it on top of everything, or bellow everything. @Duncan: It was throwing an exception, but I was using the texture before setting the old render target ;) Now it is working (see the 'edit' text)!!
#8
John K.
www.envygames.com
10/06/2009 (1:08 am)
Okay, I see. Then the render to texture makes more sense. Try starting with position (0,0) for the text.John K.
www.envygames.com
#9
10/06/2009 (6:17 pm)
@Diego: glad to here it worked out in the end - and of course as John pointed out use position (0,0) for the text as the rendertarget/texture has it's own local coordinate system.
#10

However, if I skip the phase 2 and, instead of drawing my text in the second rendertarget, I draw it directly on the screen, the text appears normal:
10/07/2009 (12:24 am)
When I run the code bellow, the string is drawn in my sceneobject, but the text is deformed:
However, if I skip the phase 2 and, instead of drawing my text in the second rendertarget, I draw it directly on the screen, the text appears normal:
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
//PHASE 1 - In this phase I'll render the string to a rendertarget (I'm doing this in the Draw method)
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
//PHASE 1 - BEGIN
//=================
//get a Sceneobject to set the new texture. It's not a template!
T2DStaticSprite sceneObject = TorqueObjectDatabase.Instance.FindObject<T2DStaticSprite>("textMaterial");
//Size of the string in GUI units
stringSize = Font.Instance.MeasureString(text);
oldTarget = Instance.GraphicsDevice.GetRenderTarget(0);
//renderTarget1 (size: 1024 x 512)
Instance.GraphicsDevice.SetRenderTarget(0, renderTarget1);
Instance.GraphicsDevice.Clear(ClearOptions.Target, Color.TransparentWhite, 0, 0);
spriteBatch.Begin();
//renders the string in renderTarget1
spriteBatch.DrawString(Font.Instance, text, Vector2.Zero,
color, 0, Vector2.Zero, 1, SpriteEffects.None,
0);
spriteBatch.End();
Instance.GraphicsDevice.SetRenderTarget(0,oldTarget as RenderTarget2D);
//=================
//PHASE 1 - END
//=================
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
//PHASE 2 - I get the text from the rendertarget above and fill the the second rendertarget with it (they are of the same size)
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
//PHASE 2 - BEGIN
//=====================
oldTarget = Instance.GraphicsDevice.GetRenderTarget(0);
//Rendertarget with the real size of the string (stringSize)
Instance.GraphicsDevice.SetRenderTarget(0, renderTarget2);
Instance.GraphicsDevice.Clear(ClearOptions.Target, Color.TransparentWhite, 0, 0);
spriteBatch.Begin();
//Render the string of size "stringSize" in the new renderTarget2 of size "stringSize"
spriteBatch.Draw(renderTarget1.GetTexture(), Vector2.Zero, new Rectangle(0, 0,
(int)stringSize.X, (int)stringSize.Y, Color.White);
spriteBatch.End();
Instance.GraphicsDevice.SetRenderTarget(0, oldTarget as RenderTarget2D);
//SceneOject with the same size as the string
sceneObject.Size = stringSize;
//Set the texture into my sceneobject material (the sceneobject size is "stringSize"). It will now contain my text.
((SimpleMaterial)sceneObject.Material).SetTexture(renderTarget2.GetTexture());
//PHASE 2 - END---------------------------------------------------------------------------------------------------------------------------------------------------------------
PHASE 2.1 - ALTERNATIVE TO PHASE 2 THAT WORKS, BUT THE TEXT IS NOT DRAWN INSIDE THE SCENEOBJECT (Replace the code from phase 2 with the code bellow)
---------------------------------------------------------------------------------------------------------------------------------------------------------------
spriteBatch.Begin();
//Renders the string of size "stringSize" directly in the screen
spriteBatch.Draw(renderTarget1.GetTexture(), Vector2.Zero, new Rectangle(0, 0,
(int)stringSize.X, (int)stringSize.Y, Color.White);
spriteBatch.End();
------------------------------------------------------------------------------------------------------------------------------------------------------------
#11
You only need render to a single rendertarget - just create a rendertarget of the correct size for the texture you want to create and render the text to that. You know how big the text will be (from MeasureString) so you can create a rendertarget of the perfect size if that's what you are wanting.
10/07/2009 (3:45 am)
I'm not sure why you are using two rendering phases?You only need render to a single rendertarget - just create a rendertarget of the correct size for the texture you want to create and render the text to that. You know how big the text will be (from MeasureString) so you can create a rendertarget of the perfect size if that's what you are wanting.
#12
What am I doing wrong?? what did you do to work?
sorry for the big post!
Thankz!
10/07/2009 (6:08 pm)
Hi Duncan. I already tried this. But still did not work. The text still is deformed =/. Here is the code of what im doing.. //Instantiate RenderTarget2D
realRenderTarget = new RenderTarget2D(Instance.GraphicsDevice, (int)Font.Instance.MeasureString(text).X,
(int)Font.Instance.MeasureString(text).Y,0,SurfaceFormat.Color);T2DStaticSprite sceneObject = TorqueObjectDatabase.Instance.FindObject<T2DStaticSprite>("textMaterial");
Instance.GraphicsDevice.SetRenderTarget(0, realRenderTarget);
Instance.GraphicsDevice.Clear(ClearOptions.Target, Color.TransparentWhite, 0, 0);
spriteBatch.Begin();
spriteBatch.DrawString(Font.Instance, text, Vector2.Zero, color);
spriteBatch.End();
Instance.GraphicsDevice.SetRenderTarget(0, null);
myTexture = realRenderTarget.GetTexture();
((SimpleMaterial)sceneObject.Material).SetTexture(myTexture);What am I doing wrong?? what did you do to work?
sorry for the big post!
Thankz!
#13
What is the distortion like - is it the same as you posted last time?
10/07/2009 (8:58 pm)
Looks like everything is in order. You don't show the creation of the SpriteBatch instance, but I'm assuming it's pretty standard.What is the distortion like - is it the same as you posted last time?
#14
You will probably want to do this anyway at some point because every scene object that references the same material instance displays the same texture - in this case the text you rendered (which can be useful, but often for text you want each 'text object' having different text on it, instead of all having the same text on them).
10/07/2009 (9:11 pm)
Not sure if it's relevant to your current issue, but you might like to try creating a brand new SimpleMaterial instance to assign to your scene object instead of using the one already referenced by the scene object.You will probably want to do this anyway at some point because every scene object that references the same material instance displays the same texture - in this case the text you rendered (which can be useful, but often for text you want each 'text object' having different text on it, instead of all having the same text on them).
#15
Now everything is fine!!
Thanks a lot Duncan! :D
See ya!
10/07/2009 (10:33 pm)
Wow.. i saw your last post right now! I did this.. Thats what i needed! heheheNow everything is fine!!
Thanks a lot Duncan! :D
See ya!
Torque Owner Duncan Colvin
msdn.microsoft.com/en-us/library/dd940232.aspx
The caveat is that this example won't draw the Avatar on a TX layer - as you already probably guessed. To draw on a TX layer there are two options that I can see.
(1) create your own custom object type and change the rendering bit (e.g. copy something like T2DStaticSprite or some such, cut out the stuff you don't need, and customize the Render() method to render your stuff).
(2) render your stuff to an xna texture and tell a TX material to use that texture. The material is, of course, used by an object in the scene and voila, your stuff is rendered just like any other scene object complete with layers, transparency, rotation, scaling, etc.
My personal favourite for most scenarios is (2). It's pretty simple to render to a Texture2D (it's exactly like rendering to the screen, but you render to a rendertarget instead). So, for example, to render text I put a T2DStaticSprite in the scene and then I update that sprite's material (an instance of SimpleMaterial) so that it uses the newly rendered texture. This update can be done every frame - although obviously I only update when the text updates to save on needless rendering. So now my text is simply a material being used by a TX scene object, just like any other texture, and I can do whatever I would normally do with such objects (size, position, layer, transparency, and so on).
In answer to your other question, you can draw whatever you like in the Game's Draw() method. Although again, it will not draw to TX layers. It will also bypass TX's post-processing (not an issue if you aren't using that though obviously). I prefer to render to textures that get used by materials attached to scene objects and this rendering can be done in Draw() before calling base.Draw().