DrawText : Need a full working example

THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

On 20/07/2012 at 09:05, xxxxxxxx wrote:

User Information:
Cinema 4D Version:   12 
Platform:   Windows  ;   
Language(s) :     C++  ;

---------
I found a few posts where people said they figured out how to draw text. But they did not post enough of the code for me to build my own working example.

I did find an older R11.5 version posted by Matthias that sort of works. But it has problems.
It inverts the front facing polygons of the object the tag is on. And makes them invisible.
Plus the text is always behind the object. Cry

I'm posting the complete version that Matthias posted long ago(I hope that's ok?) in the hopes that someone out there knows why it's doing these unwanted things. Or has a better working version that they will be kind enough to post here.

#include "c4d.h"  
#include "c4d_symbols.h"  
#include "tsimpletag.h"  
#include "customgui_priority.h"  //needed for the priority stuff to work  
#include "..\..\..\..\resource\_api\c4d_libs\lib_clipmap.h"  
  
  
#define PLUGIN_ID    1000007     // be sure to use a unique ID obtained from www.plugincafe.com  
  
  
class SimpleTag : public TagData  
{  
  INSTANCEOF(SimpleTag,TagData)  
  
  public:  
  virtual Bool Message(GeListNode *node, LONG type, void *t_data);  
  virtual Bool Init(GeListNode *node);  
  virtual Bool GetDDescription(GeListNode *node, Description *description,DESCFLAGS_DESC &flags);      
  virtual Bool GetDParameter(GeListNode *node, const DescID &id,GeData &t_data,DESCFLAGS_GET &flags);  
  virtual Bool SetDParameter(GeListNode *node, const DescID &id,const GeData &t_data,DESCFLAGS_SET &flags);  
  virtual Bool Draw(BaseTag* tag, BaseObject* op, BaseDraw* bd, BaseDrawHelp* bh);  
  
  virtual EXECUTIONRESULT Execute(BaseTag *tag, BaseDocument *doc, BaseObject *op, BaseThread *bt, LONG priority, EXECUTIONFLAGS flags);  
        
  static NodeData *Alloc(void) { return gNew SimpleTag; }  
};  
  
  
Bool SimpleTag::GetDParameter(GeListNode *node, const DescID &id, GeData &t_data, DESCFLAGS_GET &flags) //Used to get the decriptions data   
{   
  return SUPER::GetDParameter(node, id, t_data, flags);  
}  
  
Bool SimpleTag::SetDParameter(GeListNode *node, const DescID &id, const GeData &t_data, DESCFLAGS_SET &flags) //Used to change the decriptions data  
{   
  return SUPER::SetDParameter(node, id, t_data, flags);  
}  
  
Bool SimpleTag::GetDDescription(GeListNode *node, Description *description,DESCFLAGS_DESC &flags)  
{   
  //flags |= DESCFLAGS_DESC_LOADED;  
  return TRUE;  
}  
  
  
  
Bool SimpleTag::Message(GeListNode *node, LONG type, void *data)  
{  
  
  BaseTag    *tag = (BaseTag* )node;                        //Get the tag and assign it to a variable   
  
  //Do something  
   
  tag->SetDirty(DIRTYFLAGS_DATA); //Used to update a Tag's AM GUI items  
  
  return TRUE;  
}  
  
Bool SimpleTag::Init(GeListNode *node)  
{   // intitial values for the tag when it's created are set in this section  
  
  BaseTag    *tag  = (BaseTag* )node;           //Assigns a variable to the tag's node  
  BaseContainer *data = tag->GetDataInstance(); //Assigns a variable to that node's container  
  data->SetBool(MYBOX,FALSE);          //Sets the checkbox to disabled by default when tag is created-->looks in the description->tbasictag.h file for matching name  
  
  return TRUE;  
}  
  
static Bool DrawText(String text, LONG xpos, LONG ypos, BaseDraw *bd) //Custom method to draw some text(static methods don't get declared in the class)  
{  
   AutoAlloc<GeClipMap> cm;  
   if(!cm) return FALSE;  
  
   cm->Init(0, 0, 32);  
   cm->BeginDraw();  
   LONG width = cm->TextWidth(text);    //Sets the width value based on the length of the string being held in this variable  
   LONG height = cm->TextHeight();      //Automatically calculates the maximum height of a string in the current font  
   cm->EndDraw();  
   cm->Destroy();                       //Resets the clip map to its initial state and frees allocated memory.  
                                        //Requires a new call to Init*() before the clip map can be used again.  
  
  
   cm->Init(width, height, 32);         //Re-init the clipmap because it was previously destroyed  
   GePrint(LongToString(width) + "   " + LongToString(height));  
  
   cm->BeginDraw();                     //Start editing the clipmap  
   cm->SetColor(255, 255, 255, 255);    //Sets the color of the text to white  
   cm->TextAt(0,0,text);  
   cm->EndDraw();  
  
   bd->SetMatrix_Screen();                        //Sets the transformation matrix to screen coordinates, i.e. from (0, 0) to (width, height)  
   bd->SetLightList(BDRAW_SETLIGHTLIST_NOLIGHTS); //Sets the lighting used by the draw functions  
  
   Vector *padr = bNew Vector[4];  
   Vector *cadr = bNew Vector[4];  
   Vector *vnadr = bNew Vector[4];                //Create vectors with four values  
   Vector *uvadr = bNew Vector[4];  
  
   padr[0] = Vector(xpos,ypos,0);  
   padr[1] = Vector(xpos+width,ypos,0);  
   padr[2] = Vector(xpos+width,ypos+height,0);  
   padr[3] = Vector(xpos,ypos+height,0);  
  
   cadr[0] = Vector(1,1,1);  
   cadr[1] = Vector(1,1,1);  
   cadr[2] = Vector(1,1,1);  
   cadr[3] = Vector(1,1,1);  
  
   vnadr[0] = Vector(0,0,1);  
   vnadr[1] = Vector(0,0,1);  
   vnadr[2] = Vector(0,0,1);  
   vnadr[3] = Vector(0,0,1);  
  
   uvadr[0] = Vector(0,0,0);  
   uvadr[1] = Vector(1,0,0);  
   uvadr[2] = Vector(1,1,0);  
   uvadr[3] = Vector(0,1,0);  
  
   BaseBitmap *cmbmp = cm->GetBitmap();             //Create a BaseBitmap instance to use in the DrawTexture function  
   if(!cmbmp) return FALSE;  
  
   BaseBitmap *bmp = cmbmp->GetClone();              //Create a clone of the above bitmap instance  
   if(!bmp) return FALSE;  
  
   BaseBitmap *alpha = bmp->GetInternalChannel();    //Create a third Bitmap instance to hold the alpha data   
   alpha = bmp->AddChannel(TRUE, FALSE);  
   if(!alpha)  
   {  
        BaseBitmap::Free(bmp);                      //Error handling: frees memory of the alpha bitmap instance if there was an error creating it   
        return FALSE;  
   }  
  
   LONG x,y;  
   for(y=0; y<height; y++)  
   {  
        for(x=0; x<width; x++)  
        {  
           UWORD r;  
           bmp->GetPixel(x,y,&r,&r,&r);  
           bmp->SetAlphaPixel(alpha, x, y, r); //r is the opacity  
        }  
   }  
  
   bd->DrawTexture(bmp,padr,cadr,vnadr,uvadr,4,DRAW_ALPHA_NORMAL,DRAW_TEXTUREFLAGS_0);  
  
   BaseBitmap::Free(bmp);  //Free the memory being used by this bitmap instance  
  
   bDelete(padr);  
   bDelete(cadr);  
   bDelete(vnadr);         //Free the memory being used by these pointers  
   bDelete(uvadr);  
  
   return TRUE;  
}  
  
Bool SimpleTag::Draw(BaseTag *tag, BaseObject *op, BaseDraw *bd, BaseDrawHelp *bh)  
{  
  if(tag->GetData().GetBool(MYBOX))       //If the 'MYBOX' checkbox attribute is enabled  
  {   
   DrawText("Hello World", 256, 256, bd); //Draw text on the screen at x,y position 256,256  
  }      
  
  return TRUE;  
}  
  
  
EXECUTIONRESULT SimpleTag::Execute(BaseTag *tag, BaseDocument *doc, BaseObject *op, BaseThread *bt, LONG priority, EXECUTIONFLAGS flags)  
{  
  return EXECUTIONRESULT_OK;  
}  
  
Bool RegisterSimpleTag(void)  
{  
  String path=GeLoadString(IDS_SIMPLETAG); if (!path.Content()) return TRUE; // points to the res->c4d_symbols file which conatins the enum "IDS_SIMPLETAG" entry   
  return RegisterTagPlugin(PLUGIN_ID,path,TAG_EXPRESSION|TAG_VISIBLE,SimpleTag::Alloc,"tsimpletag",AutoBitmap("myicon.tif"),0);  
}

If you know why this example doesn't work properly(clipping the object faces). Or if have a working version of drawing text that works better than this. Please post it.
It would very helpful.

Thanks,
-ScottA

THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

On 21/07/2012 at 11:54, xxxxxxxx wrote:

This is apparently an issue in the way a Tag's "Draw()" routine is called vs other plugin types (Generator, Tool, etc).  If a Tag's Draw() routine alters the BaseDraw Matrix (ie. bd->SetMatrix_Screen()), then it needs to set it back (example code from all other plugin types don't do this)...

  
...  
      DrawText("Hello World", 256, 256, bd); //Draw text on the screen at x,y position 256,256  
      //---------------------------------------------------------------------------------------  
      // There appears to be a difference (bug?) in the behavior of a (expression) Tag that  
      // alters the BaseDraw Matrix (ie. bd->SetMatrix_Screen()) vs. how other objects  
      // (Generators, Tools, etc.) that alter it when drawing into the viewport...  
      //  
      // If a (expression) Tag alters the matrix, it needs to set it back to what it was before  
      // returning (examples of other object/plugin types don't do this).  
      //---------------------------------------------------------------------------------------  
      bd->SetMatrix_Matrix(op, bh->GetMg());  
...  

...I'm not sure if the above is exactly the correct matrix to use, but it seems to work.

Note that this is only an issue when OpenGL is enabled - this seems to be the same issue that Rui is having with his plugin.

THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

On 21/07/2012 at 12:16, xxxxxxxx wrote:

Thank you.
That fixed the invisible faces problem.:clap:

Any ideas on how to make the text show in front of the object?
I'm thinking maybe: bd->SetMatrix_Screen(); is not the correct thing to use?

I also vaguely remember reading something Matthias posted about using BaseDraw inside of the handles method to get a different result. Possibly related to visibility. But I can't remember the specifics.
Not sure if that has anything to do with this problem.
This whole drawing subject needs to be much better documented. I hope Yannick is working on it.

-ScottA

THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

On 21/07/2012 at 12:24, xxxxxxxx wrote:

I hadn't looked into the depth issue yet (except to note that it does display in front of the object it's attached to - if that's the selected object).

THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

On 21/07/2012 at 12:59, xxxxxxxx wrote:

That's strange.
In the example I posted + your addition. The text is always a background type object and gets hidden by any object in the scene. No matter what's selected.

I'm using R12 to test this. Not sure if that makes any difference.
I still need to convert it to R13. Because R13 does not have a TextHeight() function anymore.
Not sure if anything else has been changed between R12 & R13 for BD.

-ScottA

THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

On 21/07/2012 at 13:25, xxxxxxxx wrote:

Yeah, may be a difference in versions.  BTW, look for GetTextHeight(), instead.

THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

On 26/07/2012 at 08:22, xxxxxxxx wrote:

This seems to be rather a question about BaseDraw::DrawTexture. My old DrawText routine was just one (and probably the easiest) way to draw text through the GeClipMap class.

I'll have to check how this works in the current release. Sorry, can't check for older versions than R13.

cheers,
Matthias

THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

On 26/07/2012 at 08:33, xxxxxxxx wrote:

R13 is fine Matthias.
I will be forcing myself to dump R12 entirely when R14 comes out. Even if I hate working in it.:joy:

Your old code works pretty darn well for me(with Giblet's addition). With the small problem that the text is always a background object instead of a foreground object.
That's really the only problem that I would like to see solved.

-ScottA

THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

On 27/08/2012 at 11:26, xxxxxxxx wrote:

Hurray!!
I figured out a way to make this darned thing work!!
It turns out that the bitmap text object getting covered by objects in the scene is an OpenGL bug(or limitation). And it only works properly on a very few select video cards.
But I found a way to force it to work with all video cards.

The trick:
There needs to be a dummy 2D object(with no length) listed in the code first.
Then the you can add GeClipMap() objects like the bitmap text object. And they no longer get covered up by that OpenGL error.

Here's an example from a tag based plugin with a switch to turn GeClipMap() text objects on/off:

Bool SimpleTag::Draw(BaseTag *tag, BaseObject *op, BaseDraw *bd, BaseDrawHelp *bh )  
{  
  //bd = doc->GetActiveBaseDraw();           //This is not needed because BaseDraw is already created in the parenths  
  //if(bd) return FALSE;              
  
  if(tag->GetData().GetBool(MYBOX))          //If the 'MYBOX' checkbox attribute is enabled  
  {       
  
  //There is an OpenGL problem with most video cards that makes the drawn object get covered up by objects in the scene  
  //This is a work around to deal with that problem  
  //We need to draw a dummy 2D object first...Then draw the actual object we want to see as a 2D HUD object  
      bd->SetMatrix_Screen();                             //Use the screen's matrix to draw on  
      bd->DrawLine2D(Vector(0, 0, 0), Vector(0, 0, 0));   //Draw a line with a zero length<---This is our dummy object  
      bd->SetDepth(TRUE);                                 //This fixes drawing problems when using 2D functions  
  
  
      //Now we can create the text HUD object that we want to see in the view      
  
      AutoAlloc<GeClipMap> cm;              //Create an instance of the GeClipMap class  
      if(!cm) return FALSE;  
  
      String text = "Hello World";          //The text that we will see in the HUD  
  
      cm->Init(0, 0, 32);                   //Initializes the GeClipMap class to use this variable(cm) as 32 bit  
      cm->BeginDraw();                      //Start drawing the object. This must be used before drawing the text  
      LONG width = cm->GetTextWidth(text);  //Gets the width of the text  
      LONG height = cm->GetTextHeight();    //Gets the width of the text  
      cm->EndDraw();                        //Tell C4D we're done drawing the text  
      cm->Destroy();                        //Release any memory used by the GeClipMap class  
  
      cm->Init(width, height, 32);  
      cm->BeginDraw();  
      cm->SetColor(255, 255, 255, 255);    //Sets the color of the text to white  
      cm->TextAt(0,0,text);  
      cm->EndDraw();  
  
      bd->SetMatrix_Screen();                        //We always need a matrix to draw things...In this case we'll use the screen's matrix  
      bd->SetLightList(BDRAW_SETLIGHTLIST_NOLIGHTS); //Use other options for different results if desired  
  
      //Now we create the four vector variables that will determine the Size&Shape of the text we're drawing  
      //NOTE: We're creating a four point plane that has a text image on it...Not text created with a bunch of vectors  
      Vector *padr = bNew Vector[4];  
      Vector *cadr = bNew Vector[4];  
      Vector *vnadr = bNew Vector[4];  
      Vector *uvadr = bNew Vector[4];  
  
      LONG xpos=255;                    //The X screen location of the left upper corner of the plane object the text bitmap is on  
      LONG ypos=255;                    //The Y screen location of the left upper corner of the plane object the text bitmap is on  
  
  
      //Now we set the actual vector postions for the four point plane object that holds the text bitmap  
      padr[0] = Vector(xpos,ypos,0);  
      padr[1] = Vector(xpos+width,ypos,0);  
      padr[2] = Vector(xpos+width,ypos+height,0);  
      padr[3] = Vector(xpos,ypos+height,0);  
  
      //Now we set the color vector values for the plane object  
      //We set it to white because we'll be using an alpha on it later on  
      cadr[0] = Vector(1,1,1);  
      cadr[1] = Vector(1,1,1);  
      cadr[2] = Vector(1,1,1);  
      cadr[3] = Vector(1,1,1);  
  
     //Now we set up the normals directions for the four point plane object that holds the text bitmap  
      vnadr[0] = Vector(0,0,1);  
      vnadr[1] = Vector(0,0,1);  
      vnadr[2] = Vector(0,0,1);  
      vnadr[3] = Vector(0,0,1);  
  
     //Now we set up the UV's for the four point plane object that holds the text bitmap  
      uvadr[0] = Vector(0,0,0);  
      uvadr[1] = Vector(1,0,0);  
      uvadr[2] = Vector(1,1,0);  
      uvadr[3] = Vector(0,1,0);  
  
      BaseBitmap *cmbmp = NULL;  
      cmbmp = cm->GetBitmap();                         //Get the bitmap we're using and assign it to a variable(cmbmp)  
      if(!cmbmp) return FALSE;  
  
      BaseBitmap *bmp = NULL;  
      bmp = cmbmp->GetClone();                         //Get a copy of that bitmap variable so we can create an alpha version of it  
      if(!bmp) return FALSE;  
  
      BaseBitmap *alpha = NULL;  
      alpha = bmp->GetInternalChannel();               //Get at the RGBA channels of the bitmap copy  
      if(!alpha) alpha = bmp->AddChannel(TRUE, FALSE); //Makes the copy an alpha type of bitmap  
      if(!alpha)  
      {  
        BaseBitmap::Free(bmp);  
        return FALSE;  
      }  
  
      //Apply the alpha bitmap to the solution so only the text is visible on the screen  
      LONG x,y;  
      for(y=0; y<height; y++)  
      {  
        for(x=0; x<width; x++)  
        {  
           UWORD r;  
           bmp->GetPixel(x,y,&r,&r,&r);  
           bmp->SetAlphaPixel(alpha, x, y, r);     //r is the opacity  
        }  
      }  
  
      bd->DrawTexture(bmp,padr,cadr,vnadr,uvadr,4,DRAW_ALPHA_NORMAL,DRAW_TEXTUREFLAGS_0); //Does that actual drawing of the bitmap to the screen  
  
      BaseBitmap::Free(bmp);    //Free any memory used by BaseBitmap  
      bDelete(padr);  
      bDelete(cadr);            //Delete all the empty pointers  
      bDelete(vnadr);  
      bDelete(uvadr);  
  
     //2D drawing functions disable the Z buffer making object's in the scene half invisible  
     //There are two ways to fix this:  
     bd->SetDepth(TRUE);                         //This fixes the OpenGL problem  
     //bd->SetMatrix_Matrix(op, bh->GetMg(),5);  //This also fixes the OpenGL problem  
  
  }      
  
  return TRUE;  
}

@Matthias,
I would like to post a working example plugin of this on my website.
Do you mind if I post an example that uses some of your code you've posted here?
I'll give you credit in the source files.

-ScottA

THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

On 20/11/2012 at 01:06, xxxxxxxx wrote:

I know this thread is old, but I wanted to add that you may also use a SceneHook for drawing your 2D stuff.
The problem does not occur then.

I wrote an article on the C4Dprogramming blog about that:
http://c4dprogramming.wordpress.com/2012/11/15/2d-drawing-using-a-scenehook/

The example plugin in the article keeps it simple by just drawing 2D circles, but all I wrote there also applies to drawing text.

THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

On 20/11/2012 at 08:37, xxxxxxxx wrote:

Thanks for posting this.

Most of the time when I draw something on the screen I want it to always show up regardless of what's selected in the scene. So I will probably continue to use my Dummy2D object rather than create a whole new scene hook. It's much quicker and simpler.
But it's nice to know that a scene hook will give use more control over it.

I still think this is a bug. And we should be able to set the Z-depth values in our code to force 2D objects to the front of the screen without it causing other drawing problems.

-ScottA