Storing Images in Scene Files?
-
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
-
On 24/01/2013 at 20:01, xxxxxxxx wrote:
It is assuredly possible to store as many images as you would like to the hyperfile.
Two important points you should consider:
1. You should create a LONG description in your tag to store the number of bitmaps currently in the array so that upon reading, you know the number that were saved and need to be read (otherwise, how would your code know? - this will surely end poorly if that number is not known).
2. For retaining button-image assignments, you will need to store that information somewhere. You could create a separate array that mirrors the bitmap array to store, say, the button ID. Or you could create a simple class that stores a BaseBitmap* and the button ID that you create as an array of instances. You would then go through the simple class array to store the bitmap (WriteImage()) and ID association (WriteLong()).
Realize that the reading-writing process is in tandem. That is, the order that you write data is the EXACT order that you must read it. The C4D file format is not random access - it is linear data (possibly chunked, but not important). The good thing though, is that static descriptions are always read before Read() is called so that they are available in Read() by default.
Finally, you need some way to do this. Yes, you will need to figure out what information is required to retain the bitmaps and their correct button associations and write-read-copyto that information for it to be retained in file. Also, upon reading, you need to allocate any dynamic structures (arrays, class arrays) that hold this information and data just like when the tag has it allocated otherwise. If you have an array of BaseBitmap* in which you store the images, you will need to allocate an array of BaseBitmap* in Read() not only to read in the images but to store them just like when images are added, say, by the user. Read() needs to reestablish all of the member data as it was while live in the tag when in use (as long as the member isn't just for some intermediate processing).
-
On 24/01/2013 at 22:12, xxxxxxxx wrote:
@Scott: You don't have to explain me, I did already write a you a full
plugin doin' what you wanted it to do earlier. I'll look into it at a later
minute.@Robert: Why does he need to create a LONG description? This would
let the user modify it. And even when hiding it, the value could be set
via a script or similar. Preventing that with overriding Message() would
just be an in-ideal design. The number of bitmaps stored in the tag
should be known always during run-time. This value can then be written
to the HyperFile (just as I did above).I do not understand why he would need to write the ID of the button in
the dialog to the file, but that might depend on the requirements.-Nik
-
On 25/01/2013 at 07:51, xxxxxxxx wrote:
Originally posted by xxxxxxxx
@Scott: You don't have to explain me, I did already write a you a full
plugin doin' what you wanted it to do earlier. I'll look into it at a later
minute.It's kind of important that you read that explanation. And look at the plugin I posted. Because the plugin you made earlier worked completely differently. It did just about everything except what I wanted.
You had images saved in the plugin's folder swapping each other to be displayed on a button.
What I'm doing is showing images(or no image at all) on buttons. Depending on the saved images in the object's StorageTag.
The plugin you posted was still very helpful for me though. I learned something new about creating custom messages with it that I didn't know before.I'd still like to know how to write new levels into a hyperfile.
Why does the SDK show how to read them. But not how to write them?-ScottA
-
On 25/01/2013 at 08:39, xxxxxxxx wrote:
Originally posted by xxxxxxxx
I'd still like to know how to write new levels into a hyperfile.Why does the SDK show how to read them. But not how to write them?-ScottA
You don't write a new level. It's a different concept that takes a moment to get your head round.
Suppose you write a plugin which saves some stuff to the hyperfile using Write(). You read that back in again with Read(). Now suppose you write a new version of your plugin which during Write() saves some more data in addition to the original stuff.
There's a problem now because two versions of your plugin are circulating. The new version can read files created by the old version and the new version, but the old version doesn't know anything about your new version. So when it comes to read a file created by the new version, it will read data which is the 'extra' data written out by the new version, thinking that this is the data it wants. This could have various effects depending on what the data is - anything from working incorrectly up to a crash.
Levels are the answer to this. If the plugin is level 0, it will ignore any level 1 data it finds. If it's level 1, it will read the level 0 and the level 1 data. Likewise a level 1 plugin will ignore data created by a level 2 plugin, and so on.
You don't have to write the level to the hyperfile, Cinema does it for you. It knows the plugin level because you told it when you registered the plugin - look at the disklevel parameter in RegisterObjectPlugin() for example, that's what it's for. Mostly that parameter is ignored, but it's vital if you are using Write/Read and you release a different version of the plugin which stores more data.
-
On 25/01/2013 at 09:59, xxxxxxxx wrote:
Ah shoot. Another dead end that won't help me at all.
Thanks for the clarification Steve.I hope the Maxon developers are taking note of this thread. There are situations where storing images to HD files just won't work.
There are times that we need a way to store images locally in tags or objects so a dialog can use them interactively.
Nitroman for example uses a camera and view panels in his NitroPose plugin to do what I'm trying to do with a storage tag.
It would be much better and cleaner if we could store our GUI images locally in a hidden tag, object, or UserData.-ScottA