Storing Images in Scene Files?
-
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
-
On 25/01/2013 at 10:47, xxxxxxxx wrote:
Originally posted by xxxxxxxx
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.How can you say it's not possible to save images within tags or objects plugins?
I've just seen your code and if you remove the 2 'if level' conditions in the Read(), it should work as expected.
-
On 25/01/2013 at 12:19, xxxxxxxx wrote:
Hi Yannick,
Good to know you're watching this.
Yes we can save images to the hyperfile. But it's a FIFO stack with no way if getting or changing their indexes.
In order to be able to assign specific saved hf images to specific GUI items like buttons. We need a way to index them to tell them apart. And to use those indexes to assign them to gui items.Robert is talking about writing a custom indexing system to solve this problem. Which is rather complex for the average coder like me. I don't think I can pull it off.
It would be nice to see something in future versions that would possibly make it easier for the average user trying to save things locally. Like maybe some sort of storage tag class with indexing options.-ScottA
-
On 25/01/2013 at 15:49, xxxxxxxx wrote:
Scott,
I think part of the problem I'm having is visualising exactly what it is you're trying to do. I *think* you want the user to be able to pick which images get assigned to a set of BitmapButtons, so that your hyperfile needs to be able to store a series of images. Presumably that could be a fixed number (e.g. there are always going to be n bitmaps) or it's variable so the user can add more bitmaps. It doesn't matter either way.
Now, if that's that case, it's very easy to do. What I would do is this:
* store the pointers to all the bitmaps I'm going to use in an array of pointers to BaseBitmap(s)
* I can now access each bitmap by means of its index in the array
* each BitmapButton has a value which is the index into the array of the bitmap it's going to show - it's easiest to store this as a hidden field in the description because then I don't have to worry about saving or copying this value
* I use that hidden value as an index into the bitmap pointer array to get the pointer to the bitmap I want and assign that bitmap to the buttonWhen I Write() the hyperfile, I would do this:
* first, write a LONG which is the number of bitmaps I have in total - say it's 6, for example
* then write 6 bitmaps using the 6 pointers in my arrayWhen I Read() the hyperfile, I do this:
* read the LONG which tells me the number of bitmaps I have to read
* allocate the 6 BaseBitmaps so I have somewhere to store the data
* store the pointers to those BaseBitmaps into my array of pointers
* read the 6 bitmaps from the hyperfile
* the hidden value in the button description then marries up with the correct bitmap - I'd need to check that the value was valid in case something had gone wrong, but other than that I can forget about it; when MSG_DESCRIPTION_GETBITMAP is sent for a particular button, all I'd need to do is retrieve the hidden value in its description and use that as an index into the array of pointers to bitmapsThat's basically it. I suppose I'd have to provide some method in the interface for the user to be able to select which bitmap he wanted, but that's simple. The only other thing I would have to do is implement the CopyTo() function to copy the bitmaps, array of pointers, and the value holding the total number of bitmaps I have into the cloned tag.
I just don't see the problem. It doesn't need lots of extra SDK functions to do this.
-
On 25/01/2013 at 17:48, xxxxxxxx wrote:
What I'm doing is creating something similar to the old PoseLibrary system that used to be in older versions of C4D.
The bitmap buttons are created & deleted dynamically. So there isn't a set number of them.When my "Create Pose" button is clicked in the GeDialog. A whole bunch of things happen:
- A custom StorageTag is added to the currently active object (if it doesn't already have one)
- The current Position & Rotation values of the active object are saved as UserData to the tag
- A bitmap button is dynamically spawned in the dialog that allows the user to set the object to these UserData values(poses)
- A delete checkbox option for this specific button is also dynamically spawned so it can be deleted
- The active object is rendered. And the image is placed on the bitmap button(This part is the problem) so the image is there on the buttons. Even after the scene has been saved, closed , and re-opend.The frustrating thing is I wrote this plugin in about two days after learning how to create and delete UserData with C++.
It's working fine except for the images. And it even has a tree gui in it for selecting the objects so the user never has to touch the OM. Allowing the user to do everything from inside the GeDialog.But the button images...holy mother of god...What fresh hell is this!?
I never would have believed that it would be 10x harder to figure out a way to store the button images locally in my tags. Than writing my entire plugin.-ScottA
-
On 26/01/2013 at 02:33, xxxxxxxx wrote:
If I read this correctly, an object can have multiple storage tags each holding some PSR data and one bitmap, is that correct? Then what you need to do is allow the dialog to communicate with the tag.
Once you have your rendered bitmap, I take it you can assign that to the button, yes? So now you need to point the tag to the rendered bitmap. On Write(), each tag saves its own data and bitmap. On Read(), each tag loads its own data and bitmap. When you recreate the button layout in the dialog, you iterate through all the storage tags, get the pointer to the bitmap, create a button for each one and stick the bitmap on the button.
Or have I got this completely wrong?
-
On 26/01/2013 at 09:05, xxxxxxxx wrote:
There is only one tag per object. And that tag needs to hold multiple images and multiple UserData entries. Sort of like this:
Position
Rotation
Position
Rotation
etc....The bitmap buttons (the poses) in the GeDialog automatically update and change depending on which object is selected in the scene, or with the tree gizmo.
Based on what I'm hearing from you guys. I think what I have to do is:
-Create some kind of temporary image folder structure to save the rendered images when I'm actually using the plugin. Or save them into memory instead of the HD.
-Then execute a type of "file dump" maneuver. Using the overriden Write() method to take those images and store them into each specific StoragTag so the images will load if C4D is closed and re-opened.
-Then delete the temp images when C4D closesIt sounds like a fairly simple concept. And it might be simple for you advanced C++ guys.
But it's not simple for an average user like me with a plugin that has multiple tags to keep track of. And multiple images per tag that need to go on specific buttons. Which are being dynamically added & deleted. With constantly changing index numbers.
Not to mention being made even more difficult because the hyperfile has no indexing abilities.This is why I think it would be nice to have some some sort of storage tag that can store bitmap images added to the SDK.
That way we can save images for our gizmos locally in the scene file with index numbers..live..as we create them.. into this tag. And they will always be there. Locally serving our gui's images.
Or maybe a completely new type of plugin that has the Dialog and the Tag set up to talk to each other better than what we have now. With local image storage abilities.-ScottA
-
On 27/01/2013 at 13:28, xxxxxxxx wrote:
Hey Guys.
I've been working on it some more today and now I'm very, very close!
Here's the updated plugin and Source code:https://sites.google.com/site/scottayersmedia/Dialog_StorageTag.zipIt basically all works now. I can add my storage tag to any object and save the rendered scene image of it in the GeDialog's bitmap button.
And when I select different objects the button's image changes depending on which object is selected.
I can also save the scene file and re-open it. And all the images are still there and working like they should be.
*Hooray!Only one problem though. And it's a big one.
When I add something new to the scene after I've saved it and re-opened it again. I get the crash error:Incorrect File StructureIf I can figure out how to fix this one last problem. I've think I've got it all working.
I'm almost there.
Thanks for all the help guys.-ScottA
-
On 27/01/2013 at 13:34, xxxxxxxx wrote:
Make sure that your Read() and Write() are identical. If you are looping through to get each bitmap, make sure the count and each loop iteration is identical. Without seeing your code, hard to say if that is it. I'll take a look at it here shortly.
-
On 27/01/2013 at 13:35, xxxxxxxx wrote:
Hi Scott,
Bool StorageTag::Write(GeListNode* node, HyperFile* file) { //This is where the rendered scene image gets saved as a .jpg image when the scene file is saved //if (!file->WriteBool(tagBitmap != NULL)) return FALSE; //if (tagBitmap != NULL) file->WriteImage(tagBitmap, FILTER_JPG, NULL, SAVEBIT_ALPHA); imagecount = images.GetCount(); if (!file->WriteLong(imagecount)) return FALSE; //Iterate over all bitmaps and write them to the HyperFile for (LONG i=0; i < imagecount; i++) { **tagBitmap = BaseBitmap::Alloc(); //Allocate the bitmap variable so we can use it to make an image** tagBitmap = images[i]; file->WriteImage(tagBitmap, FILTER_JPG, NULL, SAVEBIT_ALPHA); } return TRUE; }
1. tagBitmap is a class-member. Why do you use it for temporary storage?
2. You will get memory-leaks at the red marked line.
3. Isn't that actually exactly what I showed you here?Next, in your Read() method, you only read a single image. What if you've previosuly written 7
images? That is where the incorrect file-structure message comes from.Best,
Niklas -
On 27/01/2013 at 13:43, xxxxxxxx wrote:
Bool StorageTag::Read(GeListNode* node, HyperFile* file, LONG level) { //Read the tag's hyperfile to see if there is an image stored in it LONG numImages = file->ReadLong(&imagecount); GePrint(LongToString(numImages)); for (LONG i = 0L; i != numImages; ++i) { tagBitmap = BaseBitmap::Alloc(); //Allocate the bitmap variable so we can use it to make an image if (!tagBitmap) return FALSE; //Error handling if allocation fails file->ReadImage(tagBitmap); images->Push(tagBitmap); } //This code sets the Bool's value //Depending if an image is, or is not, read when the scene file is opened in C4D //Bool hasBitmap; //if (!file->ReadBool(&hasBitmap)) return FALSE; //if (hasBitmap) //{ // if (!file->ReadImage(tagBitmap)) return FALSE; // GePrint(LongToString(tagBitmap->GetBh())); //} //else tagBitmap = NULL; return TRUE; } Bool StorageTag::Write(GeListNode* node, HyperFile* file) { //This is where the rendered scene image gets saved as a .jpg image when the scene file is saved //if (!file->WriteBool(tagBitmap != NULL)) return FALSE; //if (tagBitmap != NULL) file->WriteImage(tagBitmap, FILTER_JPG, NULL, SAVEBIT_ALPHA); imagecount = images.GetCount(); if (!file->WriteLong(imagecount)) return FALSE; //Iterate over all bitmaps and write them to the HyperFile for (LONG i=0; i < imagecount; i++) { file->WriteImage(images[i], FILTER_JPG, NULL, SAVEBIT_ALPHA); } return TRUE; }
-
On 27/01/2013 at 13:47, xxxxxxxx wrote:
I haven't tried multiple buttons yet Nik.
It's quite possible I put on my party hat too soon.-ScottA
*Edit- That fixed it Robert.....Once again I'm in your debt sir.