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