Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python 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

    Watermark doesn't work with TR & 3rd Party Render

    SDK Help
    0
    5
    605
    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.
    • H
      Helper
      last edited by

      On 15/03/2016 at 05:48, xxxxxxxx wrote:

      User Information:
      Cinema 4D Version:   R16 
      Platform:      
      Language(s) :     C++  ;

      ---------
      Hi!

      The built-in Watermark videopost has the same problem. 😞 I have a videopost that adds a watermark
      in VIDEOPOSTCALL_FRAME with !vps->open. This works fine in normal renders, but it does not when
      rendering a single frame  via Team Render.

      This is the code from VideoPostData::Execute()

          RENDERRESULT Execute(BaseVideoPost* node, VideoPostStruct* vps) override {
              GePrint("Execute() " + String::IntToString(vps->vp));  // DEBUG
              if (vps->vp == VIDEOPOSTCALL_FRAME && vps->open) {
                  _executeLineCalled = false;
                  _applyWatermark = HasFixtureContainer(vps->doc) && !StageHook_TestLicense(vps->doc);
              }
              else if (vps->vp == VIDEOPOSTCALL_FRAME && !vps->open
                       && _applyWatermark && !_executeLineCalled)
              {
                  // If ExecuteLine() should have been called but was not, we are
                  // dealing with a third-party renderer that doesn't support it.
                  VPBuffer* rgba = vps->render->GetBuffer(VPBUFFER_RGBA, 0);
                  Int32 const size = (rgba ? rgba->GetBw() * rgba->GetCpp() : 0);
                  AutoGeFree<Float32> line(NewMemClear(Float32, size));
                  if (!rgba) GePrint("No RGBA Buffer!");  // DEBUG
                  if (rgba && line) {
                      Int32 const width = rgba->GetBw();
                      for (Int32 y = 0; y < rgba->GetBh(); ++y) {
                          rgba->GetLine(0, y, width, line, 32, true);
                          PixelPost pp;
                          pp.frag = nullptr;
                          pp.frag_nx = nullptr;
                          pp.vd = vps->vd;
                          pp.col = line;
                          pp.mp = nullptr;
                          pp.aa = false;
                          pp.valid_line = true;
                          pp.comp = rgba->GetCpp();
                          pp.cpu_num = 0;
                          pp.xmin = 0;
                          pp.xmax = width - 1;
                          pp.line = y;
                          this->ExecuteLine(node, &pp);
                          rgba->SetLine(0, y, width, line, 32, true);
                      }
                  }
              }
              return RENDERRESULT_OK;
          }
      

      Is it a limition of Team Render?

      1 Reply Last reply Reply Quote 0
      • H
        Helper
        last edited by

        On 16/03/2016 at 04:57, xxxxxxxx wrote:

        Hello,

        I'm testing this with 17.048. It seems that "Watermark" works fine with Team Render single frame rendering. Please notice that if "Multi-Pass" is enabled, the post effect will not render it's result into the RGBA buffer but into separate passes.

        It looks like that if a post effect should be executed on !vps->open this has to be done not on a single client but on the server. To make sure your post effect is executed on the server you must return VIDEOPOSTINFO_NETRUNONSERVER in your GetRenderInfo().

        Best wishes,
        Sebastian

        1 Reply Last reply Reply Quote 0
        • H
          Helper
          last edited by

          On 16/03/2016 at 05:34, xxxxxxxx wrote:

          Hi Sebastian, thanks a lot for your reply.

          Unfortunately I can't confirm the "Watermark" video post working with Team Render single frame rendering,
          neither in R16.050 nor R17.048. ** It does not work with third-party renderers , just to be clear.

          I use the sdk_teamrender.cpp example to test it. I've added a GIF that shows rendering using that
          Team Render Example renderer locally, then using Team Render single frame.

          Note that it does work  rendering an animation. I guess this is due to the fact that Team Render 
          assigns a full frame per node, thus its the same as doing a single frame render without TR.

          Thanks!
          Niklas

          PS: Attached is the source of that teamrender example, just in case.

          #include "c4d.h"
          // #include "main.h"
          #include "lib_clipmap.h"
          #include "lib_net.h"
            
          /**A unique plugin ID. You must obtain this from http://www.plugincafe.com. Use this ID to create new instances of this material.*/
          #define ID_SDK_TEAMRENDER_EXAMPLE 1033999 // NOT A REAL PLUGIN ID !!
            
            
          #define BUCKET_SIZE 64	// hardcoded bucket size
            
          //----------------------------------------------------------------------------------------
          /// Data structure relevant for the Team Render Client
          //----------------------------------------------------------------------------------------
          struct TeamRenderClientData
          {
          	VideoPostStruct* _vps;
          	Int32 _bucketSize;
          	Int32 _currentBucket;
          	Int32 _x1;
          	Int32 _x2;
          	Int32 _y1;
          	Int32 _y2;
          };
            
          //----------------------------------------------------------------------------------------
          /// Data structure relevant for the Team Render Server
          //----------------------------------------------------------------------------------------
          struct TeamRenderServerData
          {
          	RenderJob* _renderJob;
          	MultipassBitmap* _targetBitmap;
          	Int32 _lastBucketIndex;
          	Int32 _totalBucketCount;
          	Int32 _finishedBucketCount;
          	Int32 _startTime;
          };
            
          //----------------------------------------------------------------------------------------
          /// Simple VideoPostData renderer using Team Render funtionality
          //----------------------------------------------------------------------------------------
          class TeamRenderVideoPost : public VideoPostData
          {
          		INSTANCEOF(TeamRenderVideoPost, VideoPostData)
            
          public:
            
          	static NodeData* Alloc() { return NewObjClear(TeamRenderVideoPost);}
            
          	virtual VIDEOPOSTINFO GetRenderInfo(BaseVideoPost* node);
            
            virtual RENDERRESULT Execute(BaseVideoPost* node, VideoPostStruct* vps);
            
            
            virtual Bool RenderEngineCheck(BaseVideoPost* node, Int32 id);
            
            virtual Bool NetFrameInit(BaseVideoPost *node, BaseDocument* doc, RenderJob* job, Int32 initialclients, const NetRenderData* renderinfo, MultipassBitmap* frameBmp, BaseThread* bt, Int32 &realdepth);
            virtual void NetFrameFree(BaseVideoPost* node);
            virtual Bool NetFrameMessage(BaseVideoPost *node, const C4DUuid& remoteUuid, const NetRenderBuffer& data, NetRenderBuffer *result);
            
            
          private:
            
          	void FillBuffer(VideoPostStruct* vps, String text, Vector color);
            
            
          private: // client methods
            
          	void HandleTeamRenderBucket(VideoPostStruct* vps);
          	Bool GetBucketSize(TeamRenderClientData* data);
          	Bool GetBucketIndex(TeamRenderClientData* data);
          	void GetBucketDimensions(TeamRenderClientData* data);
          	void SendResult(TeamRenderClientData* data);
            
          private: // server data
            
          	TeamRenderServerData _serverData;
            
          private: // server methods
            
          	void FillRegion(Int32 x1, Int32 x2, Int32 y1, Int32 y2);
          	void FillRegion(NetRenderMsgBucketFinished* result);
            
            
            
          };
            
            
          VIDEOPOSTINFO TeamRenderVideoPost::GetRenderInfo(BaseVideoPost* node)
          {
          	return VIDEOPOSTINFO_NETFRAME;
          	//return VIDEOPOSTINFO_0;
          }
            
            
          RENDERRESULT TeamRenderVideoPost::Execute(BaseVideoPost* node, VideoPostStruct* vps)
          {
          	// check if break
          	if(vps->thread && vps->thread->TestBreak())
          	{
          		return RENDERRESULT_OK;
          	}
            
          	if (vps->open)
          	{
          		switch (vps->vp)
          		{
          			case(VIDEOPOSTCALL_RENDER) :
          			{
          				// skip default Cinema Render process
          				if(vps->vd)
          					vps->vd->SkipRenderProcess();
            
          				break;
          			}
          			case(VIDEOPOSTCALL_INNER) :
          			{
            
          				// check if Team Render situation
          				if(vps->net && vps->net_server == false)
          				{
            
          					if(vps->net->_singleImageDistribution)
          					{
          						// distrubuted rendering (single frame render buckets)
          						this->HandleTeamRenderBucket(vps);
          					}
          					else
          					{
          						// animation rendering (render a whole frame)
          						this->FillBuffer(vps,"Render with Team Render",Vector(1,1,1));
          					}
          				}
          				else
          				{
          					// no Team Render situation
          					this->FillBuffer(vps,"Render locally",Vector(1,1,1));
          				}
            
          				break;
          			}
          		}
          	}
            
            
          	return RENDERRESULT_OK;
          }
            
            
          Bool TeamRenderVideoPost::RenderEngineCheck(BaseVideoPost* node, Int32 id)
          {
          	switch (id)
          	{
          		case RENDERSETTING_STATICTAB_ANTIALIASING:
          		case RENDERSETTING_STATICTAB_OPTIONS:
          		case RENDERSETTING_STATICTAB_STEREO:
          		return false;
          	}
            
          	return true;
          }
            
          //----------------------------------------------------------------------------------------
          /// Function is called when the Team Render server process starts. Access and save all relevant information.
          //----------------------------------------------------------------------------------------
          Bool TeamRenderVideoPost::NetFrameInit(BaseVideoPost *node, BaseDocument* doc, RenderJob* job, Int32 initialclients, const NetRenderData* renderinfo, MultipassBitmap* frameBmp, BaseThread* bt, Int32 &realdepth)
          {
          	GePrint("TeamRenderVideoPost::NetFrameInit");
            
          	// calculating the total number of render buckets
          	VPBuffer* rgba = (VPBuffer* )frameBmp;
            
          	if(!rgba)
          		return false;
            
          	const Int32 w = rgba->GetBw();
          	const Int32 h = rgba->GetBh();
            
          	const Int32 subdivisionX = (Int32)maxon::Ceil((Float)w / (Float)BUCKET_SIZE);
          	const Int32 subdivisionY = (Int32)maxon::Ceil((Float)h / (Float)BUCKET_SIZE);
            
          	_serverData._totalBucketCount = subdivisionX * subdivisionY;
            
          	// number of finished buckets
          	_serverData._finishedBucketCount = 0;
            
          		// default first bucket index
          	_serverData._lastBucketIndex = 0;
            
          		// save target framebuffer
          	_serverData._targetBitmap = frameBmp;
            
          	// save RenderJob
          	_serverData._renderJob = job;
            
          	// save start time
          	_serverData._startTime = GeGetMilliSeconds();
            
            
            
          	frameBmp->SetData(BASEBITMAP_DATA_PROGRESS_PHASE,	GeData(RENDERPROGRESSTYPE_DURINGRENDERING));
          	frameBmp->SetData(BASEBITMAP_DATA_PROGRESS_FRAME,	GeData(0.0));
          	frameBmp->SetData(BASEBITMAP_DATA_STARTTIME,			GeData(GeGetTimer()));
          	frameBmp->SetSave(true);
            
          	// add passes
            
          	MultipassBitmap* layer = frameBmp->AddLayer(nullptr ,COLORMODE_RGB,false);
          	if(layer)
          	{
          		layer->SetUserID(2000);
          		layer->SetName("Multipass A");
          		layer->SetSave(true);
          	}
            
          	layer = frameBmp->AddLayer(nullptr ,COLORMODE_RGB,false);
          	if(layer)
          	{
          		layer->SetUserID(2001);
          		layer->SetName("Multipass B");
          		layer->SetSave(true);
          	}
            
          	return true;
          }
            
          //----------------------------------------------------------------------------------------
          /// Function is called when the Team Render server process ends.
          //----------------------------------------------------------------------------------------
          void TeamRenderVideoPost::NetFrameFree(BaseVideoPost* node)
          {
          	GePrint("RendererVideoPost::NetFrameFree");
            
          	if(!node)
          		return;
            
          	BaseDocument* doc = node->GetDocument();
            
          	if(!doc)
          		return;
            
          	const RenderData* const renderData = doc->GetActiveRenderData();
            
          	if(_serverData._targetBitmap == nullptr)
          		return;
            
          	_serverData._targetBitmap->SetData(BASEBITMAP_DATA_PROGRESS_PHASE,	GeData(RENDERPROGRESSTYPE_AFTERRENDERING));
            
          	// set Picture Viewer status bar text
            
          	const Int32 diff = GeGetMilliSeconds() - _serverData._startTime;
          	const Float32 seconds = Float32(diff) / 1000.0f;
          	const String text = "Render Time: "+String::FloatToString(seconds,-1,1)+ " s.";
            
          	_serverData._targetBitmap->SetData(BASEBITMAP_DATA_PROGRESS_TIME, GeData(text));
            
            
            
          	//
          	// make saving the multipasses depend on whatever your multipasses depend on
          	//
            
          	// save image data
          	const Int32 layerCount = _serverData._targetBitmap->GetLayerCount();
            
          	for(Int32 i = 0; i < layerCount; ++i)
          	{
          		MultipassBitmap* bmp = _serverData._targetBitmap->GetLayerNum(i);
            
          		if(bmp)
          		{
          			GeData name = bmp->GetParameter(MPBTYPE_NAME);
          			GeData userID = bmp->GetParameter(MPBTYPE_USERID);
          			GeData save = bmp->GetParameter(MPBTYPE_SAVE);
            
          			if(save.GetBool())
          			{
          				Filename imagePath = renderData->GetDataInstance()->GetFilename(RDATA_PATH);
          				String suffix = imagePath.GetSuffix();
            
          				imagePath.SetFile("mp_"+String::IntToString(i)+"."+suffix);
            
          				GePrint("write file :"+imagePath.GetString());
            
          				bmp->Save(imagePath,FILTER_TIF,nullptr,SAVEBIT_0);
          			}
          		}
          	}
            
            
          	// saving the rgba image
          	if(renderData->GetDataInstance()->GetBool(RDATA_SAVEIMAGE))
          	{
          		Int32 format = renderData->GetDataInstance()->GetInt32(RDATA_FORMAT);
          		Filename imagePath = renderData->GetDataInstance()->GetFilename(RDATA_PATH);
            
          		GePrint("write file :"+imagePath.GetString());
            
          		_serverData._targetBitmap->Save(imagePath,format,nullptr,SAVEBIT_0);
          	}
            
            
            
          }
            
            
          //----------------------------------------------------------------------------------------
          /// Function is called when the Team Render server receives a message from a client.
          //----------------------------------------------------------------------------------------
          Bool TeamRenderVideoPost::NetFrameMessage(BaseVideoPost* node, const C4DUuid& remoteUuid, const NetRenderBuffer& data, NetRenderBuffer* result)
          {
          	GePrint("RendererVideoPost::NetFrameMessage");
            
          	switch(data.id)
          	{
            
          		// Client requests the bucket size
          		case(MSG_NETRENDER_BUCKET_INFO) :
          		{
          			GePrint("- MSG_NETRENDER_BUCKET_INFO");
            
          			// prepare answer
          			NetRenderMsgBucketInfo* answer = NewMemClear(NetRenderMsgBucketInfo, 1);
            
          			if ((answer == nullptr) || (result == nullptr))
          				return false;
            
          			// set data
          			answer->bucketSize = BUCKET_SIZE;
            
          			// set result
          			result->size = sizeof(NetRenderMsgBucketInfo);
          			result->data = answer;
            
          			break;
          		}
            
          		// client requests the index of a bucket to render
          		case MSG_NETRENDER_BUCKET_INDEX:
          		{
          			GePrint("- MSG_NETRENDER_BUCKET_INDEX");
            
          			// return -1 if the client should no longer do anything
          			Int32 bucketIndex = -1;
            
          			// only send a new bucket index if there are still bucket to distribute
          			if(_serverData._lastBucketIndex < _serverData._totalBucketCount)
          				bucketIndex = _serverData._lastBucketIndex;
            
          			// prepare answer
          			NetRenderMsgBucketIndex* answer = NewMemClear(NetRenderMsgBucketIndex, 1);
            
          			if ((answer == nullptr) || (result == nullptr))
          				return false;
            
          			// set data
          			answer->bucketID			= bucketIndex;
            
          			result->size							= sizeof(NetRenderMsgBucketIndex);
          			result->data							= answer;
            
          			// increment
          			_serverData._lastBucketIndex++;
            
          			break;
          		}
            
          		// client sends the dimensions of the bucket he is about to render
          		// this is used to highlight the bucket in the framebuffer
          		case MSG_NETRENDER_BUCKET_REQUEST:
          		{
          			GePrint("- MSG_NETRENDER_BUCKET_REQUEST");
            
          			NetRenderMsgBucketStarted* message = (NetRenderMsgBucketStarted* )data.data;
            
          			this->FillRegion(	message->regionX1,
          												message->regionX2,
          												message->regionY1,
          												message->regionY2);
            
          			break;
          		}
            
          		// client sends the results of the rendering process
          		case MSG_NETRENDER_BUCKET_FINISHED:
          		{
          			GePrint("- MSG_NETRENDER_BUCKET_FINISHED");
            
          			NetRenderMsgBucketFinished* message = (NetRenderMsgBucketFinished* )data.data;
            
          			message->buffer				= (Float32* )(((UChar* )message) + sizeof(NetRenderMsgBucketFinished));
            
          			// write the data to the framebuffer
          			this->FillRegion(message);
            
          			// increment finished buckets
          			_serverData._finishedBucketCount++;
            
            
          			if(_serverData._finishedBucketCount == _serverData._totalBucketCount)
          			{
          				// end the render process
          				_serverData._renderJob->UpdateProgress(1.0, true);
          			}
          			else
          			{
          				const Float progress = (Float)_serverData._finishedBucketCount / (Float)_serverData._totalBucketCount;
            
          				_serverData._renderJob->UpdateProgress(progress, false);
            
          				// define progress bar in picture viewer
            
          				String showProgress = String::FloatToString(progress*100.0);
          				showProgress += " %";
            
          				_serverData._targetBitmap->SetData(BASEBITMAP_DATA_PROGRESS_FRAME,GeData(progress));
          				_serverData._targetBitmap->SetData(BASEBITMAP_DATA_PROGRESS_ACTION,	GeData(showProgress));
          			}
            
          			break;
          		}
          	}
            
          	return true;
          }
            
          //----------------------------------------------------------------------------------------
          /// Function will fill the framebuffer with the given color and will write the given text.
          //----------------------------------------------------------------------------------------
          void TeamRenderVideoPost::FillBuffer(VideoPostStruct* vps, String text, Vector color)
          {
          	if(!vps || !vps->render)
          		return;
            
          	VPBuffer* rgba = vps->render->GetBuffer(VPBUFFER_RGBA, NOTOK);
            
          	if(!rgba)
          		return;
            
          	AutoAlloc<GeClipMap> clipMap;
            
            
          	if(clipMap->Init(rgba->GetBw(),rgba->GetBh(),32) != IMAGERESULT_OK)
          		return;
            
          	clipMap->BeginDraw();
            
          	const Int32 col_r = SAFEINT32(color.x * 255.0);
          	const Int32 col_g = SAFEINT32(color.y * 255.0);
          	const Int32 col_b = SAFEINT32(color.z * 255.0);
            
          	clipMap->SetColor(col_r,col_g,col_b);
          	clipMap->FillRect(0,0,clipMap->GetBw()-1,clipMap->GetBh()-1);
            
            
          	clipMap->SetColor(0,0,0);
            
          	String output = "Frame: ";
          	output += String::IntToString(vps->time.GetFrame(vps->fps));
          	output += ": " + text;
            
          	clipMap->TextAt(10, 10,output);
            
          	clipMap->EndDraw();
            
          	BaseBitmap* res = clipMap->GetBitmap();
            
          	// copy
          	Int32 x1, y1, x2, y2, x, y, cnt;
            
          	// example functions
          	Int32 cpp = rgba->GetInfo(VPGETINFO_CPP);
            
          	x1	= 0;
          	y1	= 0;
          	x2	= rgba->GetBw();
          	y2	= rgba->GetBh();
          	cnt = rgba->GetBw();
            
          	Int			 bufferSize = cpp * cnt;
          	Float32* b, *buffer = nullptr;
            
          	if (bufferSize > 0)
          		buffer = NewMemClear(Float32, bufferSize);
            
          	if (!buffer)
          		return;
            
          	for (y = 0; y < y2; y++)
          	{
          		rgba->GetLine(x1, y, cnt, buffer, 32, true);
            
          		for (b = buffer, x = 0; x < x2; x++, b += cpp)
          		{
          			UInt16 pr,pg,pb;
          			res->GetPixel(x, y, &pr, &pg, &pb);
            
          			b[0] = (Float32)pr / 255.0f;
          			b[1] = (Float32)pg / 255.0f;
          			b[2] = (Float32)pb / 255.0f;
          		}
            
          		rgba->SetLine(x1, y, cnt, buffer, 32, true);
          	}
          	DeleteMem(buffer);
          }
            
            
          //----------------------------------------------------------------------------------------
          /// The client requests buckets and will send render results to the server.
          //----------------------------------------------------------------------------------------
          void TeamRenderVideoPost::HandleTeamRenderBucket(VideoPostStruct* vps)
          {
          	// prepare data
          	TeamRenderClientData data;
          	data._vps = vps;
          	data._currentBucket = -1;
            
          	Bool res = false;
            
          	// request bucket size
          	res = this->GetBucketSize(&data);
            
          	if(res == false)
          		return;
            
          	// loop until there are no more buckets to render
          	while(true)
          	{
          		// request bucket index
          		res = this->GetBucketIndex(&data);
            
          		if(res == false)
          			return;
            
          		// simulate activity
          		GeSleep(50);
            
          		// send results to the server
          		this->SendResult(&data);
            
            
          		if(vps->thread->TestBreak())
          			return;
            
          	}
          }
            
          //----------------------------------------------------------------------------------------
          /// The client requests the bucket size.
          //----------------------------------------------------------------------------------------
          Bool TeamRenderVideoPost::GetBucketSize(TeamRenderClientData* data)
          {
          	// setup message
          	NetRenderBuffer requestMessage;
          	NetRenderMsgBucketInfo* messageData = NewObjClear(NetRenderMsgBucketInfo);
            
          	requestMessage.id = MSG_NETRENDER_BUCKET_INFO;
          	requestMessage.job = data->_vps->net->_renderJob->GetUuid();
          	requestMessage.size = sizeof(NetRenderMsgBucketInfo);
          	requestMessage.data = messageData;
            
          	// setup Result
          	NetRenderBuffer requestResult;
          	requestResult.id = MSG_NETRENDER_BUCKET_INFO;
          	requestResult.job = data->_vps->net->_renderJob->GetUuid();
          	requestResult.size = 0;
          	requestResult.data = nullptr;
            
          	// send
          	GePrint("Send MSG_NETRENDER_BUCKET_INFO");
          	MESSAGERESULT res = NetSendData(data->_vps->net->_service, data->_vps->net->_renderJob->GetServerUuid(), &requestMessage, &requestResult);
            
          	if(res != MESSAGERESULT_OK)
          		return false;
            
          	if (requestResult.data == nullptr)
          		return false;
            
          	// get data
          	NetRenderMsgBucketInfo* bucketInfo = (NetRenderMsgBucketInfo* )requestResult.data;
          	data->_bucketSize = bucketInfo->bucketSize;
            
          	return true;
          }
            
          //----------------------------------------------------------------------------------------
          /// The client requests the bucket index and will send the bucket dimensions to the server.
          //----------------------------------------------------------------------------------------
          Bool TeamRenderVideoPost::GetBucketIndex(TeamRenderClientData* data)
          {
          	data->_currentBucket = -1;
            
            
          	NetRenderMsgBucketInfo* pNetMsgInfo	= NewMemClear(NetRenderMsgBucketInfo, 1);
            
          	if (pNetMsgInfo == nullptr)
          		return false;
            
          	// prepare message
          	NetRenderBuffer requestMessage;
          	requestMessage.id						= MSG_NETRENDER_BUCKET_INDEX;
          	requestMessage.job					= data->_vps->net->_renderJob->GetUuid();
          	requestMessage.size					= sizeof(NetRenderMsgBucketIndex);
          	requestMessage.data					= pNetMsgInfo;
            
          	NetRenderBuffer requestResult;
          	requestResult.id					= MSG_NETRENDER_BUCKET_INDEX;
          	requestResult.job					= data->_vps->net->_renderJob->GetUuid();
          	requestResult.size				= 0;
          	requestResult.data				= nullptr;
            
          	GePrint("Send MSG_NETRENDER_BUCKET_INDEX");
          	NetSendData(data->_vps->net->_service,data->_vps->net->_renderJob->GetServerUuid(), &requestMessage, &requestResult);
            
          	if(requestResult.data == nullptr)
          		return false;
            
          	data->_currentBucket	= ((NetRenderMsgBucketIndex* )requestResult.data)->bucketID;
            
          	if(data->_currentBucket == -1)
          		return false;
            
          	// get dimensions
          	this->GetBucketDimensions(data);
            
            
          	//----------------------
          	// answer server
          	//----------------------
            
          	// setup answer data
          	NetRenderMsgBucketStarted* answerData = NewMemClear(NetRenderMsgBucketStarted, 1);
          	answerData->bucketID	= data->_currentBucket;
          	answerData->regionX1	= data->_x1;
          	answerData->regionX2	= data->_x2;
          	answerData->regionY1	= data->_y1;
          	answerData->regionY2	= data->_y2;
            
          	// setup answer message
          	NetRenderBuffer* answerMessage					 = NewObj(NetRenderBuffer);
          	answerMessage->id				= MSG_NETRENDER_BUCKET_REQUEST;
          	answerMessage->job			= data->_vps->net->_renderJob->GetUuid();
          	answerMessage->size			= sizeof(NetRenderMsgBucketStarted);
          	answerMessage->data			= answerData;
            
          	GePrint("Send MSG_NETRENDER_BUCKET_REQUEST");
          	NetSendData(data->_vps->net->_service, data->_vps->net->_renderJob->GetServerUuid(), answerMessage, nullptr);
            
          	return true;
          }
            
          //----------------------------------------------------------------------------------------
          /// Used to calculate the position and the dimensions of the bucked defined by the current bucket index.
          //----------------------------------------------------------------------------------------
          void TeamRenderVideoPost::GetBucketDimensions(TeamRenderClientData* data)
          {
          		VPBuffer* rgba = data->_vps->render->GetBuffer(VPBUFFER_RGBA, NOTOK);
            
          		if(!rgba)
          			return;
            
          		const Int32 w = rgba->GetBw();
          		const Int32 h = rgba->GetBh();
            
          		const Int32 subdivisionX = (Int32)maxon::Ceil((Float)w / (Float)data->_bucketSize);
            
          		const Int32 xBucket = data->_currentBucket % subdivisionX;
          		const Int32 yBucket = (Int32)maxon::Floor((Float)data->_currentBucket / (Float)subdivisionX);
            
          		data->_x1 = xBucket * data->_bucketSize;
          		data->_x2 = data->_x1 + data->_bucketSize;
            
          		data->_y1 = yBucket * data->_bucketSize;
          		data->_y2 = data->_y1 + data->_bucketSize;
            
          		if(data->_x2 > w)
          			data->_x2 = w;
            
          		if(data->_y2 > h)
          			data->_y2 = h;
            
          }
            
          //----------------------------------------------------------------------------------------
          /// The client sends the final image data to the server.
          //----------------------------------------------------------------------------------------
          void TeamRenderVideoPost::SendResult(TeamRenderClientData* data)
          {
          	//----------------
          	// First I fill some memory with a random color to simulate "rendering"
          	//----------------
            
          	const Int32 width		= data->_x2 - data->_x1;
          	const Int32 height	= data->_y2 - data->_y1;
            
          	const Int32 payloadPixels = width * height * 4; //RGBA
          	const Int32 payloadSize = payloadPixels * sizeof(Float32);
            
          	Float32* payload = NewMemClear(Float32,payloadPixels);
            
          	// fill payload with random color
          	Random random;
          	random.Init(GeGetTimer());
          	Float32 r = (Float32)random.Get01();
          	Float32 g = (Float32)random.Get01();
          	Float32 b = (Float32)random.Get01();
            
          	for(Int32 i = 0; i < (width * height); ++i)
          	{
          		const Int32 offset	= i*4;
            
          		payload[offset]		= r;
          		payload[offset+1]	= g;
          		payload[offset+2]	= b;
          		payload[offset+3]	= 1.0f;
          	}
            
            
          	//---------
          	// Now I prepare the message to the server
          	//---------
            
            
          	// setup message
          	// I need to allocate enough memory for both the message and the payload
            
          	UChar* messageRoughData									= NewMemClear(UChar, sizeof(NetRenderMsgBucketFinished) + payloadSize);
          	NetRenderMsgBucketFinished* messageData = (NetRenderMsgBucketFinished* )messageRoughData;
            
          	messageData->bucketID				= data->_currentBucket;
          	messageData->offsetX				= data->_x1;
          	messageData->offsetY				= data->_y1;
          	messageData->sizeX					= data->_x2 - data->_x1;
          	messageData->sizeY					= data->_y2 - data->_y1;
            
          	messageData->bufferSize				= payloadSize;
            
          	// the payload bufer can be found at the "end" of the NetRenderMsgBucketFinished object memory
          	messageData->buffer						= (Float32* )(messageRoughData + sizeof(NetRenderMsgBucketFinished));
            
          	// copy the image data into the payload memory
          	ClearMem(messageData->buffer,	payloadSize);
          	CopyMem(payload,	messageData->buffer,	payloadSize);
            
          	// setup message
          	NetRenderBuffer* message		= NewObj(NetRenderBuffer);
            
          	message->id								= MSG_NETRENDER_BUCKET_FINISHED;
          	message->job							= data->_vps->net->_renderJob->GetUuid();
          	message->size							= sizeof(NetRenderMsgBucketFinished) + payloadSize;
          	message->data							= messageData;
            
          	// send message
          	GePrint("Send MSG_NETRENDER_BUCKET_FINISHED");
          	NetSendData(data->_vps->net->_service, data->_vps->net->_renderJob->GetServerUuid(), message, nullptr, data->_vps->thread);
            
          	DeleteMem(payload);
          }
            
            
          //----------------------------------------------------------------------------------------
          /// The defined region of the frame buffer is filled to indicate the start of the rendering of this bucket.
          //----------------------------------------------------------------------------------------
          void TeamRenderVideoPost::FillRegion(Int32 x1, Int32 x2, Int32 y1, Int32 y2)
          {
          	if(_serverData._targetBitmap == nullptr)
          		return;
            
            
          	VPBuffer* rgba = (VPBuffer* )_serverData._targetBitmap;
            
            
          	Int32 x, y, cnt;
          	cnt = x2 - x1;
            
          	Int32 cpp = rgba->GetInfo(VPGETINFO_CPP);
            
            
          	Int			 bufferSize = cpp * cnt;
          	Float32* b, *buffer = nullptr;
            
          	if (bufferSize > 0)
          		buffer = NewMemClear(Float32, bufferSize);
            
          	if (!buffer)
          		return;
            
            
          	for (y = y1; y < y2; y++)
          	{
          		rgba->GetLine(x1, y, cnt, buffer, 32, true);
            
          		for (b = buffer, x = x1; x < x2; x++, b += cpp)
          		{
          			b[0] = 1.0;
          			b[1] = 0.0;
          			b[2] = 0.0;
            
          			if(cpp == 4)
          				b[3] = 1.0f;
          		}
            
          		rgba->SetLine(x1, y, cnt, buffer, 32, true);
          	}
          	DeleteMem(buffer);
            
          }
            
          //----------------------------------------------------------------------------------------
          /// The region defined in the message is filled with the data of that message.
          //----------------------------------------------------------------------------------------
          void TeamRenderVideoPost::FillRegion(NetRenderMsgBucketFinished* result)
          {
          	if(_serverData._targetBitmap == nullptr)
          		return;
            
          	if(result == nullptr)
          		return;
            
            
          	VPBuffer* rgba = (VPBuffer* )_serverData._targetBitmap;
          	VPBuffer* multipass = nullptr;
            
          	// multipasses
          	Int32 layerCount = _serverData._targetBitmap->GetLayerCount();
            
          	for(Int32 i = 0; i < layerCount; ++i)
          	{
          		MultipassBitmap* bmp = _serverData._targetBitmap->GetLayerNum(i);
            
          		if(bmp)
          		{
          			GeData userID = bmp->GetParameter(MPBTYPE_USERID);
            
          			if(userID == 2000)
          				multipass = (VPBuffer* )bmp;
          		}
          	}
            
            
            
          	const Int32 x1 = result->offsetX;
          	const Int32 x2 = result->offsetX + result->sizeX;
          	const Int32 y1 = result->offsetY;
          	const Int32 y2 = result->offsetY + result->sizeY;
            
          	Int32 x, y, cnt;
          	cnt = result->sizeX;
            
          	Int32 cpp = rgba->GetInfo(VPGETINFO_CPP);
            
            
          	Int			 bufferSize = cpp * cnt;
          	Float32* b, *buffer = nullptr;
            
          	if (bufferSize > 0)
          		buffer = NewMemClear(Float32, bufferSize);
            
          	if (!buffer)
          		return;
            
          	Int bufferOffset = 0;
            
          	for (y = y1; y < y2; y++)
          	{
          		rgba->GetLine(x1, y, cnt, buffer, 32, true);
            
          		for (b = buffer, x = x1; x < x2; x++, b += cpp)
          		{
          			b[0] = result->buffer[bufferOffset];
          			b[1] = result->buffer[bufferOffset+1];
          			b[2] = result->buffer[bufferOffset+2];
            
          			if(cpp == 4)
          				b[3] = 1.0f;
            
          			bufferOffset += 4;
          		}
            
          		rgba->SetLine(x1, y, cnt, buffer, 32, true);
          	}
          	DeleteMem(buffer);
            
            
          	// fill a layer
          	if(multipass)
          	{
          		buffer = NewMemClear(Float32, bufferSize);
          		cpp = 3;
            
          		for (y = y1; y < y2; y++)
          		{
          			multipass->GetLine(x1, y, cnt, buffer, 32, true);
            
          			for (b = buffer, x = x1; x < x2; x++, b += cpp)
          			{
          				b[0] = 1.0f;
          				b[1] = 0.0f;
          				b[2] = 0.0f;
          			}
            
          			multipass->SetLine(x1, y, cnt, buffer, 32, true);
          		}
          		DeleteMem(buffer);
          	}
          }
            
            
            
          Bool RegisterSDKTeamRenderExample()
          {
          	return RegisterVideoPostPlugin(ID_SDK_TEAMRENDER_EXAMPLE, "Team Render Example", PLUGINFLAG_VIDEOPOST_ISRENDERER|PLUGINFLAG_VIDEOPOST_ISRENDERER_NET, TeamRenderVideoPost::Alloc, "vptr", 0, 0);
          };
          
          1 Reply Last reply Reply Quote 0
          • H
            Helper
            last edited by

            On 17/03/2016 at 10:26, xxxxxxxx wrote:

            Hello,

            a custom renderer that handles single frame rendering is responsible for pretty much everything. This means that Cinema won't execute the video post effects after the single frame finished. One would have to execute the video posts in NetFrameFree(). But since BaseVideoPost offers no way to execute the video post I'm afraid that there is no solution for this scenario.

            Best wishes,
            Sebastian

            1 Reply Last reply Reply Quote 0
            • H
              Helper
              last edited by

              On 18/03/2016 at 06:21, xxxxxxxx wrote:

              Hi Sebastian,

              thanks for the information. IMHO this is something that TR should be doing automatically and nothing
              that would need to be done manually. Maybe R18 will feature this.

              Anyway, that's bad for my client since the only "reliable" protection of his plugin is the watermark.
              With the watermark being circumvented using third party renderers and V-Ray, well you get the point.

              Best,
              Niklas

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