Storing Images in Scene Files?
-
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 -
On 08/01/2013 at 23:41, xxxxxxxx wrote:
Yeah. There is more that you have to do and where it is done is important. Here is a zip download that contains the source and res that works:
http://www.kuroyumes-developmentzone.com/wp-content/uploads/2013/01/Z_PluginTestProject.zip
Note that you have to always have Alloc()'d the BaseBitmap, even for hf->ReadImage().
Note also that I define C4D_RXX as preprocessor directives to rectify SDK version differences. You should be able to get away with defining only C4D_R12 and C4D_R13 (depending on the version you are building for).
-
On 09/01/2013 at 02:39, xxxxxxxx wrote:
The reason you are getting a crash on Read() is that you haven't allocated a bitmap for the data to be loaded into. You have a pointer but it's just a pointer to nothing - it probably has a null value - so you get an access violation the moment it's used.
If you AutoAlloc a BaseBitmap as a class-level variable, you can use that and it shouldn't crash on read.
-
On 09/01/2013 at 08:29, xxxxxxxx wrote:
God bless you Robert.
You have no idea how many days of hair pulling and dead ends I've gone through trying to figure this out.It turns out that I don't need to use the CopyTo() function at all. But it's nice to see an example of how to use it if I do need it some day. The example given in the docs is of no help at all to someone who hasn't used it before. Your code example is much more clear.
All I needed to do to make it work was put BaseBitmap::Alloc(); in the Init() function.
Now it's working and I'm on my way to the next step.@Steve,
Thank you for your advice too.-ScottA
-
On 09/01/2013 at 23:07, xxxxxxxx wrote:
Originally posted by xxxxxxxx
It turns out that I don't need to use the CopyTo() function at all. But it's nice to see an example of how to use it if I do need it some day.
If the user copies and pastes the tag (in the same document or a new one), they might get a BaseBitmap allocated through Init() but CopyTo() ensures that they get a copy that has been initialized and contains the same image (as was presented in my code). In other words, never assume that the user will never do something that you don't think that they will do - they expect a lot and usually to our (programmer's) demise ;). It may be that they will not expect the image copied and will use a new image - but it is most likely that the user expects that the copied plugin retains all of the information contained in the original.
-
On 10/01/2013 at 08:35, xxxxxxxx wrote:
Oh. OK.
Thanks for explaining that.-ScottA
-
On 24/01/2013 at 12:07, xxxxxxxx wrote:
I've got everything working like I want now with one bitmap image stored in the tag.
But I'm having a very hard time doing it with multiple images.ASFAIK. The overridden Read() & Write() methods can't read images from an array. Since all my attempts to do so have failed misearbly.
So I was wondering if maybe I could use the level option in place of an array?
Maybe I could write each bitmap to it's own level. Then use those levels as index numbers to assign them to a specific bitmap button?The problem is I can't even test this out. Because the SDK doesn't say how to Write a level.
It only shows how to read a level.
Can anyone tell me how to write a new hyperfile level in the overriden Write() method?-ScottA
-
On 24/01/2013 at 12:23, xxxxxxxx wrote:
Hi Scott,
Try something like this:
Bool MyObjectData::Write(HyperFile* hf) { Bool result = ObjectData::Write(hf); if (!result) return result; /* Assuming `bitmaps` is a GeDynamicArray<BaseBitmap>* object. */ /* Write the number of bitmaps in the array to the file, so we */ /* later know how many bitmaps to read. */ LONG count = bitmaps->GetCount(); if (!hf->WriteLong(count)) return FALSE; /* Iterate over all bitmaps and write them to the HyperFile. */ for (LONG i=0; i < count; i++) { BaseBitmap* bmp = (*bitmaps)[i]; if (!hf->WriteImage(bmp, FILTER_PNG, NULL, SAVEBITS_ALPHA)) return FALSE; } return TRUE; }
Reading should be similiarly simple.
Best,
NiklasNote: I did not compile this code yet, serves just a demonstrational purpse.