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



  • 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?



  • 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



  • 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);
    };
    


  • 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



  • 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


Log in to reply