Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Displaying the texture baking progress preview.

    Cinema 4D SDK
    c++ python sdk
    4
    14
    1.5k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • mfersaouiM
      mfersaoui
      last edited by mfersaoui

      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

      bake_texture_tag_preview.jpg

      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.

      1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand
        last edited by ferdinand

        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 the hook parameter of that function. It accepts a callable as input and will send baking progress data to that callable.

        Cheers,
        zipit

        MAXON SDK Specialist
        developers.maxon.net

        mfersaouiM 1 Reply Last reply Reply Quote 0
        • mfersaouiM
          mfersaoui @ferdinand
          last edited by mfersaoui

          @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.

          1 Reply Last reply Reply Quote 0
          • ferdinandF
            ferdinand
            last edited by

            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 and c4d.utils.BakeTexture() is writing the data to in the callback function. I assumed that BakeTexture() 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 to BakeTexture().

            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

            MAXON SDK Specialist
            developers.maxon.net

            mfersaouiM 1 Reply Last reply Reply Quote 0
            • mfersaouiM
              mfersaoui @ferdinand
              last edited by

              @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

              1 Reply Last reply Reply Quote 0
              • ferdinandF
                ferdinand
                last edited by ferdinand

                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

                MAXON SDK Specialist
                developers.maxon.net

                1 Reply Last reply Reply Quote 0
                • ManuelM
                  Manuel
                  last edited by

                  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

                  MAXON SDK Specialist

                  MAXON Registered Developer

                  mfersaouiM 1 Reply Last reply Reply Quote 1
                  • mfersaouiM
                    mfersaoui @Manuel
                    last edited by

                    @m_magalhaes
                    Hello,

                    I'm waiting your example, I'm searching this from long time. Thank you so much.

                    Regards,
                    Mustapha

                    1 Reply Last reply Reply Quote 0
                    • ManuelM
                      Manuel
                      last edited by Manuel

                      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 are GetDParameterPreview where you init your previewData with your own function (callback) (in my example named RenderPreviewImage)
                      This function have to manage MatPreview Message

                      the dirtycount is really important.

                      Use the MSG_DESCRIPTION_COMMAND inside Message 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

                      MAXON SDK Specialist

                      MAXON Registered Developer

                      mfersaouiM 2 Replies Last reply Reply Quote 1
                      • mfersaouiM
                        mfersaoui @Manuel
                        last edited by

                        @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

                        1 Reply Last reply Reply Quote 0
                        • mfersaouiM
                          mfersaoui @Manuel
                          last edited by

                          @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

                          1 Reply Last reply Reply Quote 0
                          • CairynC
                            Cairyn
                            last edited by

                            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.

                            1 Reply Last reply Reply Quote 0
                            • ManuelM
                              Manuel
                              last edited by

                              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

                              MAXON SDK Specialist

                              MAXON Registered Developer

                              1 Reply Last reply Reply Quote 0
                              • ManuelM
                                Manuel
                                last edited by

                                hello,

                                I will passed this thread as solved tomorrow if nothing to add.

                                Cheers,
                                Manuel

                                MAXON SDK Specialist

                                MAXON Registered Developer

                                1 Reply Last reply Reply Quote 0
                                • First post
                                  Last post