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 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.
-
On 24/01/2013 at 15:03, xxxxxxxx wrote:
I'm not able to make that work yet...But still trying.
I think I'm trying to do something that just isn't supported.
I don't think the built in hyperfile is designed to serve up multiple images like this.-ScottA
-
On 24/01/2013 at 15:09, xxxxxxxx wrote:
Can you upload your current code?
-
On 24/01/2013 at 16:06, xxxxxxxx wrote:
Sure:https://sites.google.com/site/scottayersmedia/Dialog_StorageTag.zip
This plugin works exactly how I want. But only with one button and one image per StorageTag.
Let me explain a little bit about what it does:-When the plugin's dialog is open. If the currently selected object also has my "StorageTag" on it. And the bitmap button is blank. Pressing the bitmap button will render the current scene and place that image on the button.
-If there is an image already on the button. It will not re-render a new image( this is intentional by design)
-If the object with a Storage tag has children. The children are also rendered
If the object has no children. Only the object itself is rendered-Selecting different objects in the OM with StorageTags on them makes the bitmap buttons automatically change respective to the selected object.
-If the scene is saved. And then opened. The old image will still be on the dialog button
All of this is working nicely just how I want. But It's limited to one button. And one image per StorageTag.
My final version will have multiple dynamically created bitmap buttons on it. So I need a way to store and retrieve multiple images from the hyperfile. And some kind of way to index them so they can be assigned to specific dialog bitmap buttons as they are added or deleted.-ScottA