Displaying the texture baking progress preview.
-
Hello,
I'm searching a method to display the texture baking progress like in the Bake Texture tag (BAKETEXTURE_PREVIEW) using BakeTexture() or any other method.Is it possible to do that with any languages C++ or Python
With the following method but in C++ is it possible to open the bake preview window (command) during the texture baking?
import c4d def main(): if op is None: return tag = op.GetTag(c4d.Tbaketexture) if tag is None: tag=op.MakeTag(c4d.Tbaketexture) tag[c4d.BAKETEXTURE_CHANNEL_COLOR]=True tag[c4d.BAKETEXTURE_BAKE] c4d.CallButton(tag, c4d.BAKETEXTURE_BAKE) c4d.EventAdd() if __name__=='__main__': main()
Thank you.
-
Hi,
I am a bit confused. You want to to reroute the output from the preview gadget of a baking tag into your plugin? That is not possible (at least I am not aware of a way).
If you just want to react to a baking process started via
c4d.utils.BakeTexture()
, check out thehook
parameter of that function. It accepts a callable as input and will send baking progress data to that callable.Cheers,
zipit -
@zipit said in Displaying the texture baking progress preview.:
c4d.utils.BakeTexture()
Hi,
I'm actually using the github repository: py-texture_baker_r18.def BakeTextureHook(self, info): print(info) #pass def Main(self): # Bake Texture Thread Main routine self.bakeError = c4d.utils.BakeTexture(self.bakeDoc, self.bakeData, self.bakeBmp, self.Get(), self.BakeTextureHook) # Sends core message once baking has finished c4d.SpecialEventAdd(PLUGIN_ID) """ # Print result {'state': 10004, 'version': 2, 'r': 0.0, 'starttime': 47079360, 'timedelta': 0} {'state': 10004, 'version': 2, 'r': 0.0, 'starttime': 47079360, 'timedelta': 40} {'state': 10002, 'version': 2, 'r': 0.0, 'starttime': 47079360, 'timedelta': 55} {'state': 10002, 'version': 2, 'r': 0.125, 'starttime': 47079360, 'timedelta': 108} {'state': 10002, 'version': 2, 'r': 0.125, 'starttime': 47079360, 'timedelta': 115} {'state': 10002, 'version': 2, 'r': 0.25, 'starttime': 47079360, 'timedelta': 144} {'state': 10002, 'version': 2, 'r': 0.25, 'starttime': 47079360, 'timedelta': 162} {'state': 10002, 'version': 2, 'r': 0.3125, 'starttime': 47079360, 'timedelta': 174} {'state': 10002, 'version': 2, 'r': 0.4375, 'starttime': 47079360, 'timedelta': 195} {'state': 10002, 'version': 2, 'r': 0.4375, 'starttime': 47079360, 'timedelta': 212} {'state': 10002, 'version': 2, 'r': 0.5625, 'starttime': 47079360, 'timedelta': 233} {'state': 10002, 'version': 2, 'r': 0.625, 'starttime': 47079360, 'timedelta': 249} {'state': 10002, 'version': 2, 'r': 0.625, 'starttime': 47079360, 'timedelta': 251} {'state': 10002, 'version': 2, 'r': 0.6875, 'starttime': 47079360, 'timedelta': 270} {'state': 10002, 'version': 2, 'r': 0.75, 'starttime': 47079360, 'timedelta': 322} {'state': 10002, 'version': 2, 'r': 0.8125, 'starttime': 47079360, 'timedelta': 339} {'state': 10002, 'version': 2, 'r': 0.8125, 'starttime': 47079360, 'timedelta': 340} {'state': 10002, 'version': 2, 'r': 0.875, 'starttime': 47079360, 'timedelta': 343} {'state': 10002, 'version': 2, 'r': 1.0, 'starttime': 47079360, 'timedelta': 348} {'state': 10002, 'version': 2, 'r': 1.0, 'starttime': 47079360, 'timedelta': 350} {'state': 10003, 'version': 2, 'r': 0.0, 'starttime': 47079360, 'timedelta': 350} """
The hook returning the following info: {'state': , 'version': , 'r': , 'starttime': , 'timedelta': } I can use it with to display a Spinning bar BFM_STATUSBAR_PROGRESSSPIN, but that I want to showing is: The baking render progress image.
-
Hi,
I was aware that you wanted to show the rendering progress. What I meant was, that you can read the data from the
MultipassBitmap
you own andc4d.utils.BakeTexture()
is writing the data to in the callback function. I assumed thatBakeTexture()
is writing its output directly into the bitmap. But I just tried it and it does not, the bitmap stays empty until the end of the call toBakeTexture()
.The only solution i can think of now is to bake the texture(s) in chunks and then display these chunks. But that seems an awful lot of work for such minor convenience feature.
Cheers
zipit -
@zipit
Hi,Can you can give me a bit of an idea how that technique is working? I really need this, because the baking texture has important role in my plugin. exist examples or similar examples? to understand this technique, and then perhaps I find an intermediary solution.
Best regards,
Mustapha -
Hi,
you can define the range of uv coordinates in the baking data container. The idea would be to render in chunks (or buckets if this term does make more sense to you). I have used that feature (only baking a region of a texture) before, so I am certain that you can do it. But never in that iterative approach that would be necessary here. It would go something like this:
""" We have defined somewhere our baking data container (bake_data), a MultipassBitmap (mp_bmp), a BitmapButtonCutomGui (bitmap_button), and a BaseBitmap (bmp) to be displayed in that button. This all is meant to take place within a GeDialog. """ # we want a 10 by 10 bucket grid buckets_x, buckets_y = 10, 10 # just flattening the nested loop to save some horizontal space chunks = [(x, y) for x in range(buckets_x) for y in range(buckets_y)] # our cell size in half range (i.e. uv space) cell_size_u, cell_size_v = 1/buckets_x, 1/buckets_y # loop over all chunks for x, y in chunks: # calculate our bucket coordinates u_start, v_start = x / buckets_x, y / buckets_y u_end, v_end = u_start + cell_size_u, v_start + cell_size_v # set the uv data in the baking container bake_data[c4d.BAKE_TEX_UV_LEFT] = u_start bake_data[c4d.BAKE_TEX_UV_RIGHT] = u_end bake_data[c4d.BAKE_TEX_UV_TOP] = v_start bake_data[c4d.BAKE_TEX_UV_BOTTOM] = v_end # do other stuff you might have to do. # I am not sure if you have to reinit the baker for just chaning the # uv coords, you might be able to skip this step bake_doc, msg = c4d.utils.InitBakeTexture( doc, tex_tag, uvw_tag, my_thread, bake_data) if not bake_doc: print msg return # bake the data msg = c4d.utils.BakeTexture( bake_doc, bake_data, mp_bmp, my_thread, my_callback) if msg != c4d.BAKE_TEX_ERR_NONE: print msg return # copy the part from the MultipassBitmap (mp_bmp) that just has been # written into the BaseBitmap (bmp) for our bitmap button. If you are # dealing with multiple channels, you have to get the correct layer from # mp_bmp first. # here you have to do the conversion from half range to pixel coords x, y = convert_to_pixel_coords(u_start, v_start) w, h = convert_to_pixel_coords(u_end, v_end) # copy the data mp_bmp.CopyPartTo(bmp, x, y, w, h) # display the bitmap, this step is not neccesarry when you did not let # SetImage() copy the passed bitmap. bitmap_button.SetImage(bmp)
So this would be quite some compromise, as you would introduce more and more overhead as you did increase the granularity of the preview. You might be able to optimize out some of that overhead (see code above), but I am still not convinced that this a good solution.
Also figuring out how to make the baked result truly seamless, might require some legwork. Just consider the above code a general concept.
Cheers
zipit -
hello,
this is probably not possible in python, there's no MaterialPreviewData for example.
You also have to launch your bake in a separate thread and than display the result on another one, you really have to be careful to be thread safe.
I'm trying to write an example in c++ as short as possible but there's quiet some stuff to be set down.
Cheers,
Manuel -
@m_magalhaes
Hello,I'm waiting your example, I'm searching this from long time. Thank you so much.
Regards,
Mustapha -
hello,
It's taking more time that i tough. I'll probably work more on it to add it to our sdk example but for now it should give you enough things to play with.The BakeTextureTag is really bigger than this but this should give you an idea.
This will only display a rainbow gradient in the preview gadget.
Important parts areGetDParameterPreview
where you init your previewData with your own function (callback) (in my example namedRenderPreviewImage
)
This function have to manage MatPreview Messagethe dirtycount is really important.
Use the
MSG_DESCRIPTION_COMMAND
insideMessage
to react to button interaction and start the thread.
Define a MessageData to react when the thread is finished and do some cleaning there.You can use your own UserData to pass data to the thread.
I've added some comment but you should just play with this code.
pc11836.h
#ifndef __pc11836_h__ #define __pc11836_h__ class pc11836_TAG; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Thread that will calculate the gradient /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class CalculateGradientThread_11836 : public C4DThread { public: virtual void Main(void) override; virtual const Char* GetThreadName(void) override; maxon::Result<void> Start(pc11836_TAG* tag); void Stop(); maxon::JobRef jobRef_; pc11836_TAG* tag_; }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // User Data to pass information to the Init function of the MaterialPreviewData that will be use in RenderPreviewImage (callback) /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class pc11868_UserData { public: pc11868_UserData(); ~pc11868_UserData(); Bool Init(); Bool InitFrom(pc11868_UserData &d); void Free(); BaseLink* tagLink_; maxon::Int32 dirtyCount_; }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // MessageData to clean the thread once it has finished /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class MyPreviewMessageData : public MessageData { public: virtual maxon::Bool CoreMessage(maxon::Int32 id, const BaseContainer& bc) override; }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // The tag itself /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class pc11836_TAG : public TagData { INSTANCEOF(pc11836_TAG, TagData); public: static NodeData* Alloc() { return NewObjClear(pc11836_TAG); } pc11836_TAG(); ~pc11836_TAG(); virtual Bool GetDDescription(GeListNode *node, Description *description, DESCFLAGS_DESC &flags); virtual Bool Init(GeListNode* node) override; virtual void Free(GeListNode* node) override; virtual Bool Message(GeListNode* node, Int32 type, void* data) override; virtual Bool GetDParameter(GeListNode* node, const DescID& id, GeData& t_data, DESCFLAGS_GET& flags) override; Bool GetDParameterPreview(BaseContainer* pContainer, GeData* pData, DESCFLAGS_GET &lFlags, Int32 lID, BaseObject* pObject); virtual Bool SetDParameter(GeListNode* node, const DescID& id, const GeData& t_data, DESCFLAGS_SET& flags) override; // this function have to handle MATPREVIEW message. static Bool RenderPreviewImage(maxon::Int32 lMessage, void* data, void* userData); maxon::Int32 dirtycount_; BaseBitmap* previewBmp_; CalculateGradientThread_11836 thread_; maxon::Bool isCalculating_; BAKE_TEX_ERR bakeResult_; }; #endif // !"__pc11836.h__"
pc11836.cpp
include "c4d_basedocument.h" #include "c4d_basebitmap.h" #include "c4d_general.h" #include "c4d_tagplugin.h" #include "c4d_basetag.h" #include "c4d_customdatatypeplugin.h" #include "customgui_matpreview.h" #include "tbase.h" #include "c4d_messageplugin.h" #include "lib_description.h" #include "maxon/job.h" #include "pc11836.h" static maxon::Int32 g_PreviewTagID = 1053526; static maxon::Int32 g_previewMessage = 1053527; static const maxon::Int32 g_msg_mypreview = 1053519; static const maxon::Int32 g_msg_mypreview_done = 1053520; #define RENDER_PREVIEW_ID 1000 #define RENDER_BUTTON_PREVIEW_ID 1001 #define RENDER_BUTTON_DELETE 1002 #define RENDER_GROUP 1003 static const maxon::Int32 g_width = 1920; static const maxon::Int32 g_height = 720; static maxon::Spinlock g_myPreviewSpineLock; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // free job queue static maxon::SerialJobQueueRef g_serialJobsQueue; static maxon::UInt g_serialJobsQueueOwnerId; // free resources at program end static void FreeQueue() { if (g_serialJobsQueue) { g_serialJobsQueue.CancelAndWait(g_serialJobsQueueOwnerId); g_serialJobsQueue = nullptr; } } MAXON_INITIALIZATION(nullptr, FreeQueue); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Thread void CalculateGradientThread_11836::Main(void) { if (tag_->previewBmp_ == nullptr) { tag_->previewBmp_ = BaseBitmap::Alloc(); tag_->previewBmp_->Init(g_width, g_height, 24); } BaseBitmap* target = tag_->previewBmp_; // Calculate the rainbow gradient maxon::Float widthf = maxon::Float(g_width); for (auto i = 0; i < g_width; i++) { maxon::Vector col = Vector(maxon::Float(i) / widthf, 1, 1); maxon::Vector colConvert = HSVToRGB(col); colConvert = colConvert * 255.0; for (auto j = 0; j < g_height; j++) { // set the image target->SetPixel(i, j, colConvert.x, colConvert.y, colConvert.z); for (auto k = 0; k < 1000; k++) { auto delay = k; } tag_->dirtycount_++; EventAdd(EVENT::NOEXPRESSION); } } // Send a message to specify thread is finished SpecialEventAdd(g_msg_mypreview, g_msg_mypreview_done, (maxon::UInt)tag_); } const Char* CalculateGradientThread_11836::GetThreadName(void) { return "CalculateTheRainbowGradientThread_11836"; } maxon::Result<void> CalculateGradientThread_11836::Start(pc11836_TAG* tag) { iferr_scope; DebugAssert(tag_ == nullptr); // Prepares the thread setting all informations needed to execute the thread. tag_ = tag; BaseTag* basetag = static_cast<BaseTag*>(tag->Get()); BaseObject* obj = basetag->GetObject(); if (obj == nullptr) { tag->bakeResult_ = BAKE_TEX_ERR::NO_OBJECT; tag_ = nullptr; return maxon::IllegalArgumentError(MAXON_SOURCE_LOCATION); } BaseDocument* doc = obj->GetDocument(); if (doc == nullptr) { tag->bakeResult_ = BAKE_TEX_ERR::NO_DOC; tag_ = nullptr; return maxon::IllegalArgumentError(MAXON_SOURCE_LOCATION);; } // creates the queue for the thread g_serialJobsQueue = maxon::SerialJobQueueRef::Create(maxon::JOBQUEUETYPE::LOWPRIORITY, maxon::JOBQUEUEMODE::PSEUDO_THREADS, "Preview example with thread queue", &g_serialJobsQueueOwnerId) iferr_return; maxon::JobQueueRef destqueue = maxon::JobQueueInterface::GetDestinationQueue(); jobRef_ = maxon::JobRef::Enqueue( [this, destqueue] { MAXON_WARN_MUTE_UNUSED maxon::JobQueueInterface::SetDestinationQueue(destqueue); if (!this->C4DThread::Start(THREADMODE::ASYNC, THREADPRIORITYEX::BELOW)) { CriticalStop(); } else { if (maxon::JobRef::IsCurrentJobCancelled()) this->C4DThread::End(false); this->C4DThread::Wait(); } }, g_serialJobsQueue) iferr_return; return maxon::OK; } void CalculateGradientThread_11836::Stop() { jobRef_.Cancel(); if (IsRunning()) { End(); } jobRef_ = nullptr; tag_ = nullptr; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // User Data pc11868_UserData::pc11868_UserData() { tagLink_ = nullptr; } pc11868_UserData::~pc11868_UserData() { Free(); } Bool pc11868_UserData::Init() { if (tagLink_) return true; tagLink_ = BaseLink::Alloc(); return tagLink_ != nullptr; } Bool pc11868_UserData::InitFrom(pc11868_UserData &d) { if (!Init()) return false; if (d.tagLink_) d.tagLink_->CopyTo(tagLink_, COPYFLAGS::NONE, nullptr); return true; } void pc11868_UserData::Free() { BaseLink::Free(tagLink_); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Tag itself pc11836_TAG::pc11836_TAG() { previewBmp_ = nullptr; } pc11836_TAG::~pc11836_TAG() { BaseBitmap::Free(previewBmp_); } Bool pc11836_TAG::GetDDescription(GeListNode *node, Description *description, DESCFLAGS_DESC &flags) { // Creates the UI for the tag, a preview area and two buttons if (!description->LoadDescription(Tbase)) return false; const DescID* singleid = description->GetSingleDescID(); DescID cid = DescLevel(RENDER_PREVIEW_ID, CUSTOMDATATYPE_MATPREVIEW, 0); if (!singleid || cid.IsPartOf(*singleid, nullptr)) { BaseContainer bc = GetCustomDataTypeDefault(CUSTOMDATATYPE_MATPREVIEW); bc.SetString(DESC_NAME, "my preview"_s); bc.SetData(DESC_GUIOPEN, true); description->SetParameter(cid, bc, DescLevel(ID_TAGPROPERTIES)); } cid = DescLevel(RENDER_GROUP, DTYPE_GROUP, 0); if (!singleid || cid.IsPartOf(*singleid, nullptr)) { BaseContainer bc = GetCustomDataTypeDefault(DTYPE_GROUP); bc.SetString(DESC_NAME, ""_s); bc.SetInt32(DESC_COLUMNS, 2); description->SetParameter(cid, bc, DescLevel(ID_TAGPROPERTIES)); } cid = DescLevel(RENDER_BUTTON_PREVIEW_ID, DTYPE_BUTTON, 0); if (!singleid || cid.IsPartOf(*singleid, nullptr)) { BaseContainer bc = GetCustomDataTypeDefault(DTYPE_BUTTON); bc.SetInt32(DESC_CUSTOMGUI, CUSTOMGUI_BUTTON); bc.SetString(DESC_NAME, "Show gradient"_s); bc.SetInt32(DESC_ANIMATE, DESC_ANIMATE_OFF); bc.SetInt32(DESC_SCALEH, 1); description->SetParameter(cid, bc, DescLevel(RENDER_GROUP)); } cid = DescLevel(RENDER_BUTTON_DELETE, DTYPE_BUTTON, 0); if (!singleid || cid.IsPartOf(*singleid, nullptr)) { BaseContainer bc = GetCustomDataTypeDefault(DTYPE_BUTTON); bc.SetInt32(DESC_CUSTOMGUI, CUSTOMGUI_BUTTON); bc.SetString(DESC_NAME, "Clear Preview"_s); bc.SetInt32(DESC_ANIMATE, DESC_ANIMATE_OFF); bc.SetInt32(DESC_SCALEH, 1); description->SetParameter(cid, bc, DescLevel(RENDER_GROUP)); } flags |= DESCFLAGS_DESC::LOADED; return true; } Bool pc11836_TAG::Init(GeListNode* node) { BaseList2D* bl = static_cast<BaseList2D*>(node); if (bl == nullptr) return false; BaseContainer* bcData = bl->GetDataInstance(); isCalculating_ = false; dirtycount_ = 1; return true; } void pc11836_TAG::Free(GeListNode* node) { if (isCalculating_) { thread_.Stop(); isCalculating_ = false; } } Bool pc11836_TAG::Message(GeListNode* node, Int32 type, void* data) { iferr_scope_handler { err.DiagOutput(); return true; }; if (type == MSG_DESCRIPTION_COMMAND) { DescriptionCommand* cd = static_cast<DescriptionCommand*>(data); if (cd == nullptr) return false; switch (cd->_descId[0].id) { case RENDER_BUTTON_PREVIEW_ID: { GeShowMouse(MOUSE_BUSY); finally { GeShowMouse(MOUSE_NORMAL); }; isCalculating_ = true; iferr(thread_.Start(this)) { isCalculating_ = false; thread_.Stop(); } dirtycount_++; break; } case RENDER_BUTTON_DELETE: { isCalculating_ = false; dirtycount_++; thread_.Stop(); BaseBitmap::Free(previewBmp_); previewBmp_ = nullptr; EventAdd(); break; } } } return TagData::Message(node, type, data); } Bool pc11836_TAG::GetDParameter(GeListNode* node, const DescID& id, GeData& t_data, DESCFLAGS_GET& flags) { if (id[0].id == RENDER_PREVIEW_ID) { BaseObject *op = static_cast<BaseObject*>(node); if (op == nullptr) return false; BaseContainer *bc = op->GetDataInstance(); return GetDParameterPreview(bc, (GeData*)&t_data, flags, RENDER_PREVIEW_ID, op); } return TagData::GetDParameter(node, id, t_data, flags); } Bool pc11836_TAG::GetDParameterPreview(BaseContainer* pContainer, GeData* pData, DESCFLAGS_GET &lFlags, Int32 lID, BaseObject* pObject) { MaterialPreviewData* previewData = (MaterialPreviewData*)pContainer->GetCustomDataType(RENDER_PREVIEW_ID, CUSTOMDATATYPE_MATPREVIEW); // if the materialPreviewData doesn't exit, create and init them. if (previewData == nullptr) { *pData = GeData(CUSTOMDATATYPE_MATPREVIEW, DEFAULTVALUE); pContainer->SetData(RENDER_PREVIEW_ID, *pData); previewData = (MaterialPreviewData*)pData->GetCustomDataType(CUSTOMDATATYPE_MATPREVIEW); if (previewData == nullptr) return false; } else { *pData = GeData(CUSTOMDATATYPE_MATPREVIEW, *previewData); previewData = (MaterialPreviewData*)pData->GetCustomDataType(CUSTOMDATATYPE_MATPREVIEW); if (previewData == nullptr) return false; } pc11868_UserData pud; if (!pud.Init()) return false; pud.tagLink_->SetLink((BaseList2D*)(Get())); pud.dirtyCount_ = dirtycount_; if (!previewData->Init(RenderPreviewImage, &pud, RENDER_PREVIEW_ID, dirtycount_)) { return false; } lFlags |= DESCFLAGS_GET::PARAM_GET; return true; }; Bool pc11836_TAG::RenderPreviewImage(maxon::Int32 lMessage, void* data, void* userData) { switch (lMessage) { case MATPREVIEW_GET_OBJECT_INFO: { MatPreviewObjectInfo* mpoi = static_cast<MatPreviewObjectInfo*>(data); mpoi->bHandlePreview = true; mpoi->bNoStandardScene = true; // hide all option from popup menu except MATPREVIEW_FLAG_HIDE_OPEN mpoi->lFlags = MATPREVIEW_FLAG_HIDE_ROTATION | MATPREVIEW_FLAG_HIDE_SCENES | MATPREVIEW_FLAG_HIDE_SCENE_SETTINGS | MATPREVIEW_FLAG_HIDE_SIZE | MATPREVIEW_FLAG_HIDE_ANIMATE; break; } case MATPREVIEW_GENERATE_IMAGE: { MatPreviewGenerateImage* mpgi = static_cast<MatPreviewGenerateImage*>(data); pc11868_UserData* previewData = static_cast<pc11868_UserData*>(userData); BaseBitmap* bmp = mpgi->pDest; if (!previewData) { mpgi->lResult = RENDERRESULT::OUTOFMEMORY; return false; } if (!previewData->tagLink_) { mpgi->lResult = RENDERRESULT::OUTOFMEMORY; return false; } BaseTag* tag = (BaseTag*)(previewData->tagLink_->GetLink(GetActiveDocument(), g_PreviewTagID)); g_myPreviewSpineLock.Lock(); if (tag) { pc11836_TAG* tagData = tag->GetNodeData<pc11836_TAG>(); // previewBmp is not nullptr so show the information. if (tagData->previewBmp_) { tagData->previewBmp_->ScaleIt(bmp, 255, true, true); } else { // show by default a gradient of red maxon::Float withf = maxon::Float(bmp->GetBw()); for (auto i = 0; i < bmp->GetBw(); i++) { maxon::Int32 color = (Float(i) / withf) * 255; for (auto j = 0; j < bmp->GetBh(); j++) { // set the image bmp->SetPixel(i, j, color, 0, 0); } } } } g_myPreviewSpineLock.Unlock(); mpgi->lResult = RENDERRESULT::OK; break; } case MATPREVIEW_MODIFY_CACHE_SCENE: break; case MATPREVIEW_PREPARE_SCENE: { MatPreviewPrepareScene* mpps = static_cast<MatPreviewPrepareScene*>(data); mpps->bScenePrepared = true; break; } case MATPREVIEW_GET_PREVIEW_ID: break; case MATPREVIEW_GET_POPUP_OPTIONS: break; case MATPREVIEW_HANDLE_POPUP_MSG: break; case MATPREVIEW_FREE_USERDATA: { pc11868_UserData* renderData = static_cast<pc11868_UserData*>(userData); DeleteObj(renderData); break; } case MATPREVIEW_COPY_USERDATA: { MatPreviewCopyUserData* copyData = static_cast<MatPreviewCopyUserData*>(data); pc11868_UserData* src = static_cast<pc11868_UserData*>(copyData->src); pc11868_UserData* clone = NewObjClear(pc11868_UserData); if (clone == nullptr) return false; if (!clone->InitFrom(*src)) return false; copyData->dst = clone; break; } case MATPREVIEW_GET_DIRTY_COUNT: { pc11868_UserData* previewData = (pc11868_UserData*)userData; BaseTag* tag = (BaseTag*)(previewData->tagLink_->GetLink(GetActiveDocument(), g_PreviewTagID)); if (tag) { pc11836_TAG* tagData = tag->GetNodeData<pc11836_TAG>(); *((Int32*)data) = tagData->dirtycount_; } break; } } return true; } Bool pc11836_TAG::SetDParameter(GeListNode* node, const DescID& id, const GeData& t_data, DESCFLAGS_SET& flags) { switch (id[0].id) { case RENDER_PREVIEW_ID: { BaseTag* btag = static_cast<BaseTag*>(node); BaseContainer* data = btag->GetDataInstance(); return SetDParameterPreview(data, &t_data, flags, RENDER_PREVIEW_ID); } break; } return TagData::SetDParameter(node, id, t_data, flags); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // MessageData to clean the thread once it has finished /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// maxon::Bool MyPreviewMessageData::CoreMessage(maxon::Int32 id, const BaseContainer& bc) { if (id == g_msg_mypreview) { void *p1 = bc.GetVoid(BFM_CORE_PAR1); void *p2 = bc.GetVoid(BFM_CORE_PAR2); // Checks if the message sent is from the thread that have finished. if ((Int)p1 == g_msg_mypreview_done) { pc11836_TAG* tag = (pc11836_TAG*)p2; if (tag == nullptr) return false; StopAllThreads(); tag->thread_.Stop(); tag->isCalculating_ = false; } } return true; } static maxon::Result<void> RegisterGeDialogPlugins() { iferr_scope; if (!(RegisterTagPlugin(g_PreviewTagID, "PC11836 tag with preview"_s, TAG_VISIBLE, pc11836_TAG::Alloc, ""_s, nullptr, 0) && RegisterMessagePlugin(g_previewMessage, "mypreviewmessages"_s, PLUGINFLAG_HIDEPLUGINMENU, NewObjClear(MyPreviewMessageData)))) return maxon::UnexpectedError(MAXON_SOURCE_LOCATION); return maxon::OK; }
Cheers,
Manuel -
@m_magalhaes
Hello Manuel,I don't know how much I can thank you for this advanced example and the enormous work. Good job!
I'm trying to compiling The Cinema 4D R20 C++ SDK Examples, I get some difficulty to setup Visual Studio. Actually I'm using R19 to developing c4d plugins.
I will return when testing your example
Thanks again!
Regards,
Mustapha -
@m_magalhaes
Hello,I tried to use this example but I get the error: cannot open source file "maxon/job.h". And many other errors, I suppose because of the job.h that not included.
I'm using Cinema 4D R19 and Visual Studio 2015, should I include an additional library. Sorry I'm new in C ++ dev and there's a lot of things that I don't know.
Regards,
Mustapha -
You cannot compile R20/R21 stuff directly for R19 and with the R19 libraries. The whole "Maxon" namespace has been added in the recent releases. The whole thread/job stuff has changed, thus I'm afraid that the example is only of limited use to you.
(Also, the error handling and everything else that starts with maxon::)I only skimmed over the code but it looks as if you would have to replace all job stuff with classic threading stuff for R19.
-
hello,
sorry i forgot the version you were using.
As this could serve as an example, we must use the new API as much as we can.As @Cairyn said you can change the error handling and come back to the old thread system.
Cheers
Manuel -
hello,
I will passed this thread as solved tomorrow if nothing to add.
Cheers,
Manuel