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
    • Recent
    • Tags
    • Users
    • Register
    • Login

    Shader bitmap rendering with MemoryFileStruct

    Scheduled Pinned Locked Moved Cinema 4D SDK
    c++
    2 Posts 2 Posters 52 Views 1 Watching
    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.
    • WickedPW Offline
      WickedP
      last edited by ferdinand

      Hi Folks,

      Terribly sorry, hoping not to take up anyone's time too much. But I can't see the forest through the trees on this one, and hoping someone can easily fix me!

      I have a document, housed at class level. I use it to render a simple scene, and then display the resulting image as a bitmap for the user. Inside this scene is an object with an assigned material that has a bitmap shader in it's colour channel. Everything works fine if I set the shader bitmap to a file on disk. But as soon as I try to use a bitmap I've drawn myself, it either renders blank, or throws a 'missing asset' error.

      End goal here is to render the shader with a bitmap I've created in memory.

      Some pseudo code below. Note - I'm cutting all the fluff out trying to keep it simple and to the point:

      void RenderMyDoc(...)
      {
      	// setup scene objects and shaders etc..
      	
      	BaseBitmap *bmp = ...;
      	AutoAlloc<BaseFile> writeHandler;
      	MemoryFileStruct *mfs = ...;
      	Filename fn;
      	fn.SetMemoryWriteMode(mfs);
      	bmp->Save(fn,...);
      	
      	/* Also tried with the following (scoped here just for clarity) */
      	{
      		fn.SetMemoryReadMode(void_p_here,void_p_sizehere);
      		writeHandler->Open(fn,...);
      		writeHandler->WriteBytes(void_p_here,void_p_sizehere);
      		// p.s. the void* and void data size all report numbers
      		
      		/* Wondering if it was because of the empty fn path, I tried calling GenerateTexturePath() on the fn, to no avail */
      	}
      	
      	// Add to shader
      	shader->SetParameter(BITMAPSHADER_FILENAME,GeData(fn),...);
      	
      	// A few other things here etc..
      	
      	// Render doc here all works, except when using my own bitmap
      	RenderDoc(...);
      }
      

      If I show the bitmap using the filename in the picture viewer, it works. But not when rendered. Sometimes it presents an 'Open file' dialog, despite having WriteMode set to the memory struct. I've seen quite a few topics elsewhere with python code, but my interpretation of python into C++ just isn't working. What am I doing wrong here?

      Hoping someone can fill the gaps for me. Cheers,

      WP.

      wickedp.com

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

        Hey @WickedP,

        Thank you for reaching out to us. This is rather hard to answer as is. It is not that your question would be inadequate or the code example unclear, but this is also a subject I would have to try out myself to know for sure. The problem with pseudo code is that it is not executable, when you want a definitive answer, I must ask you to provide compileable code which demonstrates the issue.

        About your Issue

        Since you say that sending such image file to the Picture Viewer works, we can rule out that you made any mistakes in setting up the Filename in memory mode or properly encoding your image data.

        What is noteworthy is that Filename is a Cinema API type from the 'old' days which is today just a hollowed out shell that wraps UrlInterface. When using the memory feature of Filename you are actually using maxon::URLSCHEME_MEMORYFILESTRUCT (not to be confused with URLSCHEME_MEMORY). Internally we tend to use yet another file virtualization scheme implemented for URLs when embedding virtual files in scenes or renderings, maxon::URLSCHEME_RAMDISK.

        There could be a threading issue with your Filename (check if the issue also happens when you pass RENDERFLAGS::NODOCUMENTCLONE to your RenderDocument call).

        The most forward fix would be however to use a ram disk, as that is what is proven to work with render engines. The problem is that there are currently no public examples for creating URLSCHEME_RAMDISK urls in the public SDK (as it is sort of a semi-official thing). The only place where I used it was in the Asset API docs, but only to ensure ram disk assets are being cached, not to create new ones. But ram disk scheme urls have a convenience interface, RamDiskInterface which makes it quite straight forward to use. Find below some mock code for your use case (did not compile it due to the lack of a project for this topic).

        Cheers,
        Ferdinand

        using namespace maxon;
        
        Result<void> DoRamDiskStuff()
        {
          iferr_scope; // Error handler for this scope because we are using the Maxon API. When you want to merge
                       // this with Cinema API code, i.e., use it in a function that is not of type Result<T>,
                       // you would have to use here iferr_scope_handler to terminate errors into the return
                       // type of your function.
        
          // Create a new mounting point for ram disk files. Use a unique ID in reversed domain name notation 
          // to avoid conflicts with other mounting points or use the other constructor to create an automatic 
          // hashed mounting point (which will not be persistent over reruns).
          RamDiskRef mountingPoint = RamDiskInterface::Create("net.mycompany.ramdisk.foo"_s) iferr_return;
          Url root = mountingPoint.GetRoot() iferr_return;
        
          // There are now basically two ways to create ram disk files: 
          //   (1) direct creation where we effectively treat the Url just like a file.
          //   (2) lazy creation where we provide a callback that will be called when the file is first accessed.
        
          // Our payload.
          const Char payload[] = "Hello World!";
        
          // Direct creation:
        
          // We create a file under our ram disk mounting point, it would have an URL such as 
          // "ramdisk://.../file.txt". You can directly use this URL in all parameters and file 
          // operations. We can also turn an Url back into a Filename using cinema::MaxonConvert.
          Url directFile = (root + "direct_file.txt"_s) iferr_return;
        
          // Now we write to the file, this is just standard Maxon API file I/O.
          InOutputStreamRef inout = directFile.OpenInOutputStream() iferr_return;
          inout.Write(payload) iferr_return;
        
          // And this would be the way to read back.
          Char buffer[SIZEOF(payload)];
          inout.Seek(0) iferr_return;
          inout.Read(buffer) iferr_return;
        
          // Deferred/Lazy creation:
        
          // This way is in a certain sense a bit easier (as stream handlers can be a bit scary when new
          // to the Maxon API). The advantage is of course that we only pay the costs when something is
          // actually needed. The downside is that is this can happen at an inconvenient time. Which is
          // also why I showed in the Asset API examples for how to build caches in advance. But in your 
          // case where you create the URLs yourself, this makes no sense.
        
          // This is just this one call which we invoke on our ram disk mounting point. We provide a callback 
          // that will be called when the file is first accessed, and in this callback we create an IoMemoryRef 
          // with our payload and return it.
          Url lazyFile = mountingPoint.CreateLazyFile(ToSingletonBlock("lazy_file.txt"_s),
            [payload] () -> Result<IoMemoryRef>
            {
              iferr_scope;
              DiagnosticOutput("Lazy content creation triggered.");
              IoMemoryRef mem = IoMemoryRef::Create() iferr_return;
              mem.WriteBytes(0, payload) iferr_return;
              return mem;
            }) iferr_return;
        
          // Reading back would work the same here.
        
          return OK;
        }
        

        In your specific case, I do not know if BaseBitmap::Save will work directly on a Filename that wraps a ram disk url (via Filename file = cinema::MaxonConvert(myRamDiskUrl, MAXONCONVERTMODE::WRITE);), but you can try and it should work. When push comes to shove you can always write data directly as you showed yourself.

        MAXON SDK Specialist
        developers.maxon.net

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