Q about RenderDocument(), custom Renderer [SOLVED]



  • On 07/01/2015 at 10:19, xxxxxxxx wrote:

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

    ---------
    I want to create a dialogue, which contains a simple command button, the command would be a call which is similar to the render button "which renders in the viewport or in picture viewer" , but what I want is to draw to the bitmap inside the dialogue

    so the questions are:
    1- how does RenderDocument() exactly works, I have my own custom Renderer, so does it call the Execute() function of my VideoPost Renderer?

    2- how does RenderDocument() fill the bitmap?? "there may be 20 different render layers, and I need to draw the layer which will be chosen"

    3- how to draw the bitmap?



  • On 07/01/2015 at 15:14, xxxxxxxx wrote:

    Hi MohamedSakr,

    Can you please elaborate, since your questions are very broad? Do you have any example code to provide more context?

    Here's what I can state broadly so far or trying to confirm:

    You want a dialog with a button and a drawable rectangular area, so that when you press the button, the rectangular area is filled in with the rendered result?

    To draw the bitmap inside the dialog, I guess you could use a 'userarea'?  RenderDocument() returns a bitmap, so you'll have to copy it to the userarea.  You can use ResEdit to put a dialog together that includes these two elements.

    1. If you have your own custom renderer, what results or functionality are you getting so far?  How does it work, is it based off of MaterialData (I state this because the examples in the C4D SDK based off of MaterialData are the ones that use RenderDocument()).  I don't understand the relationship between your custom Renderer and your VideoPost Renderer, nor what you mean by 'call the Execute() function'.  RenderDocument() outputs to a bitmap, and you have to do something with it afterward yourself (save / display / modify / etc.).

    2. To use RenderDocument(), please verify each parameter to set them to get the results you need.  I can't really give an explanation of its internal functionality, that would be too long an answer, and I'm not sure which part would be relevant to you.

    3. I don't understand the exact context of this question.  Is my suggestion to use a userarea what you mean, or how to trigger it to be drawn (ie sending a message to your UI code)?

    Joey Gaspe
    SDK Support Engineer



  • On 07/01/2015 at 21:09, xxxxxxxx wrote:

    Hi Joey,

    thanks for the reply, I understand that my questions are too broad Smile, let me simplify what I'm doing.

    I got my own render engine "similar to vray, Maxwell, or any other commercial render engine" , which is registered as PLUGINFLAG_VIDEOPOST_ISRENDERER, when I click the render button or the render to picture viewer, it runs the Execute() function of my VideoPost Renderer correctly.

    about what I understand "and can do" so far:
    I know how to create the dialogue, and splitting/putting different stuff inside it "including user area"

    I know how to handle a Bitmap "converting data, saving, loading,..."

    what I don't know:
    1- if I call RenderDocument() and my Renderer is chosen from the Renderers drop down menu "where there is Physical, Standard, Software,Hardware, ..." will this call the Execute() function from my VideoPost Renderer?

    2- I'm still aware of how it fills the Bitmap, for example I fill the Buffer in the Execute function in a way similar to the "vpvisualizenormals" example in the SDK.

    3- let's consider I have a ready Bitmap, a user area, how to draw the Bitmap in that user area

    how my Renderer works:
    it is a subclass of VideoPostData , inside the Execute() function:

      
    	if (vps->vp==VIDEOPOSTCALL_RENDER && vps->open)
    	{
    		vps->vd->SkipRenderProcess();  
    		RenderCtx = RenderCtxCreate();//called from my Render Engine DLL  
    		//init some data, like Camera, Objects from RayObject, etc..  
      
    		RenderCtx ->Init();  
    		RayCamera *RCam = vps->vd->GetRayCamera();  
    		float trCamera[16] = {	RCam->m.v1.x, RCam->m.v2.x, RCam->m.v3.x, 0.f,  
    							RCam->m.v1.y, RCam->m.v2.y, RCam->m.v3.y, 0.f,  
    							RCam->m.v1.z, RCam->m.v2.z, RCam->m.v3.z, 0.f,   
    							0.f, 0.f, 0.f, 1.f};  
      
    		RenderCtx ->Integrator(string("pathtracer"), -1);
    		RenderCtx ->Transform(trCamera);
    		RenderCtx ->Translate(-RCam->m.off.x, -RCam->m.off.y, -RCam->m.off.z);  
      
    		RenderCtx ->Film(string("imagefilm"), string("my-test-scene.exr"), 1024, 768, nullptr);  
      
    		Int32 objCount = vps->vd->GetObjCount();
    		for(Int32 i = 0; i < objCount; ++i)
    		{
    			RayObject *op = vps->vd->GetObj(i);
    			if(op->type ==  O_POLYGON)
    			{
    				const Vector *points = op->padr;
    				const RayPolygon *polys = op->vadr;
      
    				vector<int> pointIndices(0);
    				pointIndices.reserve(op->pcnt);
    				int totalPointsIndices = 0;				
    				vector<float> AllPoints(0);
    				AllPoints.reserve(op->vcnt * 3);
    				int totalPointsData = 0;
    				int totalPreviousIndex = 0;
    				
    				for(Int32 j = 0; j < op->vcnt; ++j)
    				{
    					int nIndices = (polys[j].c != polys[j].d)? 6 : 3;
    					int npIndices = (polys[j].c != polys[j].d)? 12 : 9;
    					
    					pointIndices.push_back(0 + totalPreviousIndex);
    					pointIndices.push_back(2 + totalPreviousIndex);
    					pointIndices.push_back(1 + totalPreviousIndex);
      
    					if(nIndices == 6)
    					{
    						pointIndices.push_back(0 + totalPreviousIndex);
    						pointIndices.push_back(3 + totalPreviousIndex);
    						pointIndices.push_back(2 + totalPreviousIndex);
    					}
      
    					totalPreviousIndex += (polys[j].c != polys[j].d)? 4 : 3;
    										
    					AllPoints.push_back(points[polys[j].a].x);
    					AllPoints.push_back(points[polys[j].a].y);
    					AllPoints.push_back(points[polys[j].a].z);
    					AllPoints.push_back(points[polys[j].b].x);
    					AllPoints.push_back(points[polys[j].b].y);
    					AllPoints.push_back(points[polys[j].b].z);
    					AllPoints.push_back(points[polys[j].c].x);
    					AllPoints.push_back(points[polys[j].c].y);
    					AllPoints.push_back(points[polys[j].c].z);
      
    					if(npIndices == 12)
    					{
    						AllPoints.push_back(points[polys[j].d].x);
    						AllPoints.push_back(points[polys[j].d].y);
    						AllPoints.push_back(points[polys[j].d].z);
    					}
      
    					totalPointsIndices += nIndices;
    					totalPointsData += npIndices;
    				}
    				RenderCtx ->Object(string("trianglemesh"), &pointIndices[0], totalPointsIndices, &AllPoints[0], totalPointsData, nullptr, 0, -1);
    			}			
    		}  
      
    		RenderCtx ->Render();//I can pass a float* for the Bitmap data, ID for which render layer to fill "copy to the pointer"  
    


  • On 10/01/2015 at 02:54, xxxxxxxx wrote:

    well after some progress, I understand now how to draw the Bitmap in a GeUserArea 🙂

    so question(3) is now solved, remaining questions are 1 & 2, which are related to RenderDocument():
    1- does it call Execute() function from the chosen VideoPost Renderer
    2- how does it fill the Bitmap



  • On 12/01/2015 at 17:19, xxxxxxxx wrote:

    Hi MohamedSakr,

    Question 1:
    Does it call Execute() function from the chosen VideoPost Renderer?

    I analyzed CineMan as an example.  Here's what I found that seems relevant to you:

    - It registers as a video post plugin, with PLUGINFLAG_VIDEOPOST_ISRENDERER as a flag in the registration call.  It sounds like you're doing the same thing.

    When rendering:
    - Cinema 4D calls the Execute() function of the video post plugins that registered as such at start up.  It calls the Execute() of each several times, certainly once each for the VIDEOPOSTCALL_* flags, if your Execute() function is coded to process them.

    So, the answer should be yes, as long as your plugin is registered correctly to be considered for video post-processing calls, your Execute() function will be called, and actually many times, depending on which stage the videopost call processing has reached.

    Question 2:
    how does it fill the Bitmap?

    I found a very long setup process occurs.  It eventually executes the algorithms that fill the bitmap according to the various criteria you set.  I'll have to dig further to give you a more precise explanation, and didn't want to hold up my answer to question 1.  However, if by the time you read this, you don't see a post for question 2, can you please be a bit more precise?  For instance, are you trying to leave out layers, or want to know what algorithms and/or approach is used?  It'll help speed up my analysis by focusing only on what you need, rather than wonder about each part.  Otherwise, the direct, 'face value' answer to your question has been answered.

    I hope the above helps so far!

    Joey Gaspe
    SDK Support Engineer



  • On 13/01/2015 at 01:03, xxxxxxxx wrote:

    Hi Joey,

    your answer helped me a lot!! Tongue , you are true about my usage of PLUGINFLAG_VIDEOPOST_ISRENDERER  in registering.

    about the question of Bitmap filling:
    I don't use any render layers of Cinema 4D "I will fill my own layers by my Renderer, and save them, what I just want is:show the result "I can do this manually without using the Bitmap* , but I wanted to know what happens in that pointer so I disable it if it consumes processing power, or use it if it is useful.



  • On 13/01/2015 at 10:19, xxxxxxxx wrote:

    Hi MohamedSakr,

    Thanks for letting me know the answer helped!  The command in your code should already be skipping the rendering process, in terms of saving computing power:

    vps->vd->SkipRenderProcess(); 
    

    Again, with CineMan as an example, it uses the above call in its render code.

    Thanks for the clarification for question 2!  As for the RenderDocument() bitmap itself, there's nothing you can do to indicate you don't need it to be used for render output.  The whole process is built around the bitmap being the output of everything RenderDocument() is going to do.

    Joey Gaspe
    SDK Support Engineer



  • On 13/01/2015 at 10:24, xxxxxxxx wrote:

    if I pass a nullptr to the function Bitmap argument, will it work? or it will return early before calling Execute()?



  • On 13/01/2015 at 11:02, xxxxxxxx wrote:

    Hi MohamedSakr,

    What I just found, in code that exports shader data, is that you have to indicate not to save the image but still have to pass a bitmap to RenderDocument(), and just ignore it, like the following, in your Execute() function:

      
    RenderData* rd = doc->GetActiveRenderData();  
      
    if (!rd)  
      return FILEERROR_OUTOFMEMORY;  
      
    // Indicate you don't want it to save the image  
    BaseContainer* rData = rd->GetDataInstance();  
    Bool hasSaveImage = rData->GetBool(RDATA_SAVEIMAGE);  
    rData->SetBool(RDATA_SAVEIMAGE, false);  
      
    // Must create a bitmap anyway, can't pass nullptr or anything equivalent  
    AutoAlloc<BaseBitmap> bmp;  
    if (!bmp)  
    return FILEERROR_OUTOFMEMORY;  
      
    // Must initialize the bitmap even if it's useless  
    bmp->Init(rd->GetDataInstance()->GetInt32(RDATA_XRES), rd->GetDataInstance()->GetInt32(RDATA_YRES));  
      
    RenderDocument(doc, rd->GetData(), nullptr, nullptr, bmp, RENDERFLAGS_EXTERNAL, nullptr);  
      
    // Setting back to save images a good idea  
    rData->SetBool(RDATA_SAVEIMAGE, hasSaveImage);  
      
    // Don't use bmp for anything at this point, instead your own code  
      
    

    I think that's what you need.  Please let me know if it works, as I chopped out some parts that seem specific to the situation.

    Joey Gaspe
    SDK Support Engineer



  • On 13/01/2015 at 11:11, xxxxxxxx wrote:

    Hi Joey,

    thanks for clarification and code snippet, I guess I know what to do now, consider this solved 🙂



  • On 13/01/2015 at 11:24, xxxxxxxx wrote:

    Hi MohamedSakr,

    Thanks for letting me know!  Yes, indeed solved!

    Joey Gaspe
    SDK Support Engineer



  • On 21/01/2015 at 06:12, xxxxxxxx wrote:

    sorry for opening this again, but I think I must know how the Bitmap is filled, this is essentially for the MaterialData plugin preview image, code from SimpleMaterial.cpp example

      
    		case MATPREVIEW_GENERATE_IMAGE:
    		{
    			MatPreviewGenerateImage* image = (MatPreviewGenerateImage* )data;
    			if (image->pDoc)
    			{
    				Int32					w = image->pDest->GetBw();
    				Int32					h = image->pDest->GetBh();
    				BaseContainer bcRender = image->pDoc->GetActiveRenderData()->GetData();
    				bcRender.SetFloat(RDATA_XRES, w);
    				bcRender.SetFloat(RDATA_YRES, h);
    				bcRender.SetInt32(RDATA_ANTIALIASING, ANTI_GEOMETRY);
    				if (image->bLowQuality)
    					bcRender.SetBool(RDATA_RENDERENGINE, RDATA_RENDERENGINE_PREVIEWSOFTWARE);
    				image->pDest->Clear(0, 0, 0);
    				image->lResult = RenderDocument(image->pDoc, bcRender, nullptr, nullptr, image->pDest,
    													 RENDERFLAGS_EXTERNAL | RENDERFLAGS_PREVIEWRENDER, image->pThread);
    			}
    			return true;
    			break;
    		}
    

    last line in the code explains why I must know how to fill this Bitmap!!



  • On 21/01/2015 at 11:30, xxxxxxxx wrote:

    Hi MohamedSakr,

    Sorry, but I still don't understand what you need to know about 'how the bitmap is filled'.  You're going to have to be more precise.  Perhaps it would be best if you told me what is the context of the problem and the solution you're seeking?  You previously stated you know how the bitmap methods work from  "vpvisualizenormals" example in the SDK, so I guess you undertand how to draw into a bitmap?

    I also have no idea what you mean by "last line in the code explains why I must know how to fill this Bitmap!!"  What's wrong with the last line?  I figure you're asking about RenderDocument(), for which I see no issue when running the MaterialData plugin in C4D, so I figure you're not reporting a defect.

    Joey Gaspe
    SDK Support Engineer



  • On 21/01/2015 at 12:43, xxxxxxxx wrote:

    Hi Joey,

    well it is not a defect , it is just about the Bitmap itself, "image->pDest" in the above code, from vpvisualizenormals I can override the color channel, is this the Bitmap which is passed here? in vpvisualizenormals.cpp example, in the Execute function, there is this line of code

    VPBuffer* rgba = vps->render->GetBuffer(VPBUFFER_RGBA, NOTOK);
    

    and it gets the data from this buffer, with GetLine() , overrides it with SetLine().
    the question is: is this buffer "VPBUFFER_RGBA" == the passed Bitmap "image->pDest"?? why I ask? 
    because this is the only buffer that I can fill from the SDK examples, if they are not the same, then how can I fill the Bitmap to VIEW the result in the Material Preview window?

    Note: I changed the font to be uniform (Arial) to make the post easier to read. JG



  • On 22/01/2015 at 08:22, xxxxxxxx wrote:

    Hi MohamedSakr,

    Thank you for the clarifications!  The answer is yes, the bitmap passed as "image->pDest" to RenderDocument() becomes the internal render bitmap of type MultipassBitmap.  When you call "vps->render->GetBuffer(VPBUFFER_RGBA, NOTOK)", you get the renderer's MultipassBitmap pointer.  As stated in the documentation, a VPBuffer is represents the same class internally as a MultipassBitmap, so a VPBuffer can be cast to the latter, and vice versa.  Therefore, you should be able to fill the bitmap to view the results in the material preview window.

    Joey Gaspe
    SDK Support Engineer



  • On 22/01/2015 at 10:15, xxxxxxxx wrote:

    Hi Joey,

    thanks a lot for clarification , I will test it now, you made my day.


Log in to reply