Storing Images in Scene Files?



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

    On 15/12/2012 at 13:50, xxxxxxxx wrote:

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

    ---------
    Is there a way to store images in a tag (or something else) so that when the scene file is saved to a .c4d file. The images will be saved in them too?

    Using file folders to store the images won't work for my plugin.
    I need the images to be saved locally within the scene file. Because the images are linked to specific objects in the scene via bitmap buttons.

    -ScottA



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

    On 15/12/2012 at 16:57, xxxxxxxx wrote:

    You could store the Bitmap as a member of your tag class but you will need to control the Read/Write/Copy of it yourself.  Basically, you would add a BaseBitmap* or MultipassBitmap* element and allocate/init and load the image into that.  If saved with Write and loaded with Read, it would be embedded in the document file.

    Typically, file formats that deal with resources in other formats such as image, video, audio keep them externally referenced to reduce file size and to retain the resource independently for formatting and other editing.



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

    On 15/12/2012 at 19:15, xxxxxxxx wrote:

    Hi Robert,

    Can we actually write image data itself into a tag?
    I don't see how to do that.

    The only things I'm seeing are:
    -Writing an image into memory (which gets deleted when C4D closes)
    -Writing an image into a file folder
    -Writing to an image into a file folder using  hyperfile

    The last two require some kind of external storage file to write/ read from. And this is the problem I'm battling.
    I need everything to be self contained within the .c4d file.

    -ScottA



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

    On 16/12/2012 at 01:42, xxxxxxxx wrote:

    I'm not sure I follow and/or understand..
    If you have a plugin doing things, what is
    stopping you from having the bitmaps in its folder,
    storing a path/pointer to the bitmaps in a basecontainer
    of the object(s)?

    If you must store the actual bitmap in an object, you might
    store it as bits (in a basecontainer) and extract it
    with your plugin.

    Cheers
    Lennart



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

    On 16/12/2012 at 06:56, xxxxxxxx wrote:

    WriteImage() in BaseFile writes to the C4D document and not to a separate image file.

    "Write a bitmap image to the hyperfile."

    You can specify the format but the formatted bitmap is stored in the document file.  So, yes, you are actually writing the image data into the 'tag' (in file). :)



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

    On 16/12/2012 at 10:01, xxxxxxxx wrote:

    OK. Thanks for the help.

    I'm using a Gedialog and a tag plugin in the same project.
    The dialog is where the user does all the work. And the tag is where the data is stored when the user clicks a button in the dialog.
    I have everything working like I want except for the images part.
    And there's nothing in the SDK that can help guide in figuring out how to store images in the tag.

    Saving the images in the plugin's folders is not an option. Because I'm creating multiple images on a per object basis. And everything is linked together with UserData ID#s to keep everything in sync when UD is added or deleted. So storing the images in the plugin itself isn't an option for what I'm doing.
    I could make the code I currently have work if the user creates "Save project with assets".
    But that's very restrictive. And not a good option.

    I need to somehow store all the images locally in the tag itself. And I'm just not getting how to do that yet. It's going to be a long battle.
    If I ever figure it out. I'll post the plugin and code with my other free plugins.

    -ScottA



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

    On 19/12/2012 at 11:33, xxxxxxxx wrote:

    Hey guys,
    I'm still stuck on how to add a bitmap image to the tag's container?

    Here is a basic example of adding a custom value to a tag's container:

        //This code adds a string value to the tag's container  
      BaseContainer *tagData = mytag->GetDataInstance();  //Get the tag's container  
      tagData->SetString(PLUGIN_ID, "Hello World")        //Adds a string value to the container using the plugin's ID#
    

    Simple and easy.
    But there is no SetBitmap() container function for me to insert an image into the container.

    Here is an example of rendering the scene to an image file.
    But the problem is I have no way to store it in the tag's container:

        BaseBitmap *bmp = BaseBitmap::Alloc();  
      LONG x = 100;  
      LONG y = 100;  
      bmp->Init(x, y);  
      
      
      //This code block renders the current scene to an image  
      RenderData *rd = doc->GetActiveRenderData();   //Get the active render data  
      BaseContainer rdcopy = rd->GetData();  
      rdcopy.SetLong(RDATA_XRES, x);  
      rdcopy.SetLong(RDATA_YRES, y);  
      rdcopy.SetLong(RDATA_RENDERENGINE, RDATA_RENDERENGINE_STANDARD);  
      RenderDocument(doc, rdcopy, NULL, NULL, bmp, RENDERFLAGS_EXTERNAL, NULL);  
      
      -Now how do take my bmp and enter it into the tag's container?  
      -There is no Set->Bitmap() function in the sdk
    

    Please help. I'm stuck. 😢

    -ScottA



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

    On 19/12/2012 at 12:36, xxxxxxxx wrote:

    You can't add an image to a BaseContainer. Therefore, you will have to do this manually. Allocate a class-level bitmap, or as many bitmaps as you need, in your tag. Store the bitmap data in that.

    You will need to override the Write() function in NodeData. When the file is saved, you save the number of bitmaps you have using WriteLong() and then save each set of bitmap data in turn using WriteImage(). When the file is loaded, you also need to override Read() so that you can read back in the number of saved bitmaps and then the bitmap data.

    Bear in mind that you will probably also need to override CopyTo().

    Edit: just to emphasise what Robert originally said, this does NOT save the bitmaps to separate files, they are saved in the hyperfile - that is, in the .c4d document file itself.

    Steve



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

    On 19/12/2012 at 13:23, xxxxxxxx wrote:

    Thanks Steve.
    This is turning out to be much more complicated than I thought it would be.

    My plugin looks similar to your free Smart IBL Loader plugin.
    I'm saving and loading images onto GeDialog bitmap buttons which looks like how your plugin appears to be set up too.
    I was disappointed the source code wasn't included. I think it would probably help me learn how to do what I'm trying to do.

    -ScottA



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

    On 19/12/2012 at 15:36, xxxxxxxx wrote:

    Yes, the loader's preset browser is just a bunch of BitmapButtons added by code rather than in a resource file. But the difference is that the loaded thumbnail files are just that, separate files on disk, and I don't need to save them.

    Using Write() and Read() to manage your own data is not difficult, and it works very well. It's the same method I use in X-Particles to save the cached particle data (except I don't use WriteImage, of course). Take a look at WriteImage(), you can see that you basically just supply a BaseBitmap and the image format and it all happens automagically. ReadImage() is even easier. And since Cinema calls Write() and Read() for you, you don't even have to remember to call them. All you need to do is keep track of the number of bitmaps and the data itself - presumably you hold that in an array of BaseBitmaps?

    Steve



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

    On 19/12/2012 at 18:07, xxxxxxxx wrote:

    Yeah. An array of bitmaps probably sounds about right. Because I will eventually also need a way to grab each one and assign them to a specific button.

    I think I understand the Writing & Reading of hyperfiles part.
    But I still can't figure out how to take that saved hyperfile (with the image in it)  and put it into the tag.
    These are two separate plugins. Working together in the same project.
    The tag is there to merely hold UD entries and the images.
    All the work is done in the GeDialog plugin.

    I'm saving the image into a hyperfile inside of the Gediaolg plugin.
    But then I don't know how to transfer that over to the tag in the tag plugin to store it there.

    -ScottA



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

    On 20/12/2012 at 01:02, xxxxxxxx wrote:

    It sounds as if you are creating a new hyperfile. You don't need to do that, you're just making life difficult for yourself.

    Once you have the tag created - however you do that, whether the user has to create one or you create a hidden tag on some object - the tag's Read() and Write( ) functions will be called by Cinema. You don't have to call them. Note that both functions are provided with a pointer to a hyperfile, which in this case will be the scene file. The Write() function is called whenever the document is saved, and the Read() function on document load.

    In those functions you must either save or load the images as I described earlier. This means that the images are saved as part of the scene file - NOT, repeat NOT a separate hyperfile - and will be reloaded whenever the document is loaded. Can I repeat, the images are then embedded in the document file, not a new file you have to create.

    Provided you can access the tag, you can access the loaded bitmaps. Personally I'd have used an ObjectData myself, because a tag has to be added to an object, so it's a lot easier just to access the object itself, but that's a matter of personal preference.

    Steve



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

    On 20/12/2012 at 01:07, xxxxxxxx wrote:

    I've already mentioned use of Read/Write/CopyTo.  As Steve says, you override and fill in these three functions to copy class member data from file, to file, when a copy/clone is made, respectively.  You don't need to call them; they are called automatically by Cinema 4D.  And the bitmaps will be part of the scene file - not separate files, as Steve mentions.

    If you need to display the images (bitmaps) in the tag, then use BitmapButtons in GetDDescription().  You can see my recent thread containing code on how to do this in R13 and earlier (R14 has changed in user-interaction).



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

    On 20/12/2012 at 08:12, xxxxxxxx wrote:

    Thanks guys,
    I'm sorry for being so thick headed about this. But I've never done anything like this before.

    I'm looking at the c4d_nodedata.h section of the docs right now. And I will try to figure out how to store the images to the document as you've suggested.

    -ScottA



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

    On 20/12/2012 at 21:53, xxxxxxxx wrote:

    ARRGH!
    I've tried and tried. But I just can't get a grip on how to do this guys.

    -You say not to create a new hyperfile. But I have to make a new one because I have to declare a hyperfile type to use it. I don't understand how I could do this without allocating a new hf.
    -You say the image gets saved when the file is saved. But I still can't figure out how to pull it out of there.
    -I still can't figure out how to store the image in a tag

    -ScottA



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

    On 21/12/2012 at 03:25, xxxxxxxx wrote:

    There's only one real error in your code but it's a big one. You're trying to execute Write() from the dialog, not the tag. GeDialog() doesn't have a virtual Write() so this can never be called. When Read() is called in your tag, it doesn't load anything because nothing got saved.

    You need to implement Write() in the tag. This will be called when the document is saved, and will save the image as part of the scene file. Then it will be reloaded when Read() is called. Then it should all work.

    What you need to do is figure out some way for the dialog to communicate with the tag. The bitmap the dialog uses for the bitmap button has to be the one the tag loaded. Your dialog will have to query the tag to see if it has a bitmap for it, and then use that if it does. So you'll have to get the tag from the object then call a getter routine that you provide to get the bitmap. You'll need a function like this in your tag:

      
    BaseBitmap* GetBitmap(void);   
    BaseBitmap* SimpleTag::GetBitmap(void)   
    {   
        return bmp;   
    {   
    

    You'll also need some way for the tag to access the bitmap from the dialog so that it can save the data when requested. Provided you can set up the communication between the two elements, it should work fine.

    Steve



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

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

    Thanks for taking the time to look at Steve.
    I'm giving up on this. It's beyond my abilities to figure out. And I have too many other things to learn.

    @MAXON:
    Please consider adding an example to the SDK that shows how to save data like images to tags.
    It seems like a black art that few people (if any) know how to do.

    As always. Thank you for the help guys,
    -ScottA



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

    On 21/12/2012 at 10:08, xxxxxxxx wrote:

    It is not difficult but I wish that I had time to provide an example myself.  If I can, I will.  It is really not as difficult as you think - just coming to an understanding of the way that C4D does things and seeing it without explicit information.

    I haven't looked over your example code but you should have Read(),Write(), and CopyTo() overriden for the tag (TagData) and you should get the BaseBitmap for the image and use ReadImage(), WriteImage(), and allocate a new image and clone/copy for CopyTo() to support the image independently of the image file resource.  If you need to do more beyond that, it may just be a matter of passing the BaseBitmap between the tag and the GeDialog.  Not sure but you are definitely overthinking this. :)



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

    On 21/12/2012 at 11:35, xxxxxxxx wrote:

    Thanks Robert.
    I look at your words. And I try to use them.

    But this is what happens 🙂:

    -ScottA



  • On 08/01/2013 at 21:42, xxxxxxxx wrote:

    Robert.

    I've manged to get to the point where I can save String values into a scene's HF.
    The changed string value sticks when the scene is saved and opened as expected without any problems.
    But I'm getting access violation crashes when I try to do the same thing with a BaseBitmap.
    You mentioned saving a copy of the bitmap. And I'm thinking that might be the final missing piece of the puzzle I need to make this finally work.

    Could you please look at this code and let me know what you think?

    #include "c4d.h"  
    #include "c4d_symbols.h"  
    #include "tsimpletag.h"  
    #include "customgui_priority.h"  //needed for the priority stuff to work  
      
    // be sure to use a unique ID obtained from www.plugincafe.com  
    #define PLUGIN_ID    1000010  
      
      
    class SimpleTag : public TagData  
    {  
      INSTANCEOF(SimpleTag,TagData)  
      
      public:  
          BaseBitmap *bmp;  
      
          virtual Bool Read(GeListNode *node, HyperFile *hf, LONG level);  
          virtual Bool Write(GeListNode *node, HyperFile *hf);  
          virtual Bool CopyTo(NodeData* dest, GeListNode* snode, GeListNode* dnode, COPYFLAGS flags, AliasTrans* trn);  
                  void createImage(void);  
          virtual Bool Message(GeListNode *node, LONG type, void *t_data);  
          virtual Bool Init(GeListNode *node);  
          virtual EXECUTIONRESULT Execute(BaseTag *tag, BaseDocument *doc, BaseObject *op, BaseThread *bt, LONG priority, EXECUTIONFLAGS flags);  
            
          static NodeData *Alloc(void) { return gNew SimpleTag; }  
    };  
      
    Bool SimpleTag::Read(GeListNode *node, HyperFile *hf, LONG level)  
    {  
      return hf->ReadImage(bmp);  
    }  
      
    Bool SimpleTag::Write(GeListNode *node, HyperFile *hf)  
    {  
      hf->WriteImage(bmp,FILTER_JPG,NULL,SAVEBIT_0);  
      return TRUE;  
    }  
      
    Bool SimpleTag::CopyTo(NodeData* dest, GeListNode* snode, GeListNode* dnode, COPYFLAGS flags, AliasTrans* trn)  
    {  
      //What do I write here?  
      return TRUE;  
    }  
      
    void SimpleTag::createImage(void)  
    {  
      BaseDocument *doc = GetActiveDocument();  
      
      bmp = BaseBitmap::Alloc();  
      LONG x = 100;        //The X size of the final image  
      LONG y = 100;        //The Y size of the final image  
      bmp->Init(x, y);  
      
      RenderData *rd = doc->GetActiveRenderData();   //Get the active render data  
      BaseContainer rdcopy = rd->GetData();  
      rdcopy.SetLong(RDATA_XRES, x);  
      rdcopy.SetLong(RDATA_YRES, y);   
      rdcopy.SetLong(RDATA_RENDERENGINE, RDATA_RENDERENGINE_STANDARD);  
      RenderDocument(doc, rdcopy, NULL, NULL, bmp, RENDERFLAGS_EXTERNAL, NULL);  
    }  
      
    Bool SimpleTag::Message(GeListNode *node, LONG type, void *data)  
    {  
      BaseTag *tag = (BaseTag* )node;                              //Get the tag and assign it to a variable  
      BaseContainer *bc = ((BaseList2D* )node)->GetDataInstance(); //Get the container for the tag  
        
      switch (type)  
        {          
          case MSG_DESCRIPTION_COMMAND:                            // MSG_DESCRIPTION_COMMAND is sent when button is clicked  
          {              
              DescriptionCommand *dc = (DescriptionCommand* )data;  // data contains the description ID of the button             
              LONG button = dc->id[0].id;                          // get the ID of the button  
                            
              switch (button)  
              {  
          //This button executes a method that renders the scene into the bmp BaseBitmap variable  
                  case BUTTON1:   
              createImage();  
                      break;  
      
                  //This button shows the current image stored in the bmp BaseBitmap variable in the image viewer  
                  case BUTTON2:  
              ShowBitmap(bmp);  
              break;  
              }  
          }  
        }  
      
      tag->SetDirty(DIRTYFLAGS_DATA); //Used to update a Tag's AM GUI items  
      return TRUE;  
    }  
      
    Bool SimpleTag::Init(GeListNode *node)  
    {  
          bmp = NULL;  
      
      
      //This code sets up the expression priority options that most things have in c4d   
      GeData d;  
      if (node->GetParameter(DescLevel(EXPRESSION_PRIORITY),d,DESCFLAGS_GET_0))  
      {  
          PriorityData *pd = (PriorityData* )d.GetCustomDataType(CUSTOMGUI_PRIORITY_DATA);  
          if (pd) pd->SetPriorityValue(PRIORITYVALUE_CAMERADEPENDENT,GeData(FALSE));  
          node->SetParameter(DescLevel(EXPRESSION_PRIORITY),d,DESCFLAGS_SET_0);  
      }  
      
      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;  
      return RegisterTagPlugin(PLUGIN_ID,path,TAG_EXPRESSION|TAG_VISIBLE,SimpleTag::Alloc,"tsimpletag",AutoBitmap("myicon.tif"),0);  
    }
    

    The crash is coming from the Read() function when trying to open the saved file.

    Thanks,
    -ScottA


Log in to reply