Accessing MoData from procedurally created Matrix

On 19/12/2014 at 16:41, xxxxxxxx wrote:

#While generating objects using Python Generator

Hi all! The past week or so I have been getting familiarized with Python in C4D (longtime programmer in several other languages before this). I've been hitting roadblock after roadblock, but probably nothing out of the ordinary.. and I haven't found anything that has totally stumped me.. until now!

I am trying to access (read) the MoData from a Matrix object I have created within Py code. If I try to access a Matrix object linked through userdata/OM, I do not encounter this problem.

I suspect that it has something to do with the hidden "MoData Tag" I have seen referenced a few times. I found that my OM linked Matrix had the hidden tag, and my procedural one did not come with it. I was able to "MakeTag()" on my procedural Matrix object and successfully read MoData using "GeGetMoData", but the arrays were empty upon trying to read them (see bottom code example) :P

Here is the test code showing my issue

import c4d
from c4d.modules import mograph as mograph
#Welcome to the world of Python
def main() :
    matrixObject = c4d.BaseObject(1018545)          #new Matrix Object
    modata = mograph.GeGetMoData(matrixObject)
    print modata                                    #prints "None"
    matrixObject2 = op[c4d.ID_USERDATA,1]           #Matrix Object linked from OM
    modata2 = mograph.GeGetMoData(matrixObject2)
    print modata2                                   #prints appropriate MoData object definition

And here is an example trying with the MoData Tag

import c4d
from c4d.modules import mograph as mograph
#Welcome to the world of Python
def main() :
    matrixObject = c4d.BaseObject(1018545)                  #new Matrix Object
    matrixObject.MakeTag(1018625)                           #add MoData Tag
    modata = mograph.GeGetMoData(matrixObject)
    print modata                                            #prints appropriate MoData definition (unlike before)
    matrixObject2 = op[c4d.ID_USERDATA,1]
    modata2 = mograph.GeGetMoData(matrixObject2)
    print modata2                                           #prints appropriate MoData definition
    modata_positions = modata.GetArray(c4d.MODATA_MATRIX)
    print modata_positions                                  #empty
    modata_positions2 = modata2.GetArray(c4d.MODATA_MATRIX)
    print modata_positions2                                 #filled with Data

Is there a way I can force the procedural Matrix object to update its MoData Tag before using GetArray() to read said data?

Hopefully I am just missing something here. Thank you for any and all help!

On 20/12/2014 at 09:32, xxxxxxxx wrote:


It looks like you're having trouble with object creation through code. Something I have fought with a lot in C4D.
When we create things (objects, tags, materials..) with code. There is a nagging problem that often occurs.
The objects don't get fully created even though they look like they've been fully created.
In your specific case. The Matrix Object gets created OK. But the MODATA_MATRIX is not created.

This problem does not occur if you manually create objects.
So I've argued that this problem happens because C4D takes too long to create things...But others do not seem to agree with me.
Never the less. It is a real problem for sure. And it is a P.I.A. to deal with.

There's a couple of workarounds that I commonly use to get around this problem:
-Put the doc.InsertObject() at the end of the code
-Use the Message() method to poll the newly created object. And only use it if it passes my polling test

In your case. Neither of these things is probably an option.
So one possible way to get it to work is to generate the Matrix Object with the pyGenerator Object:

import c4d  
def main() :  
  return c4d.BaseObject(1018545)

Then put a python tag on the pyGenerator Object and use this code to get at the MODATA_MATRIX data:

import c4d  
from c4d.modules import mograph as mo  
def main() :  
  gen = op.GetObject()  
  cache = gen.GetCache()  
  md     = mo.GeGetMoData(cache)  
  marr   = md.GetArray(c4d.MODATA_MATRIX)  
  farr   = md.GetArray(c4d.MODATA_FLAGS)  
  ccount = len(marr) #Number of clones  
  #Gets the position of each matrix object clone  
  for i in xrange(ccount) :  
      print marr[i].off

Another important thing to know about generators is that they create virtual objects.
So most of the time. You'll want to grab the cached version of it to change it.

Hope that helps,

On 29/12/2014 at 11:07, xxxxxxxx wrote:

Thanks so much for the help and tips, Scott. This is exactly the sort of thing that will help me so much learning Python in Cinema... These little things like virtual objects and best practices that you have mentioned- not really present in the docs and like needles in a haystack searching threads.

I'm going to take your suggestions and see if I can't get something working with my main script!

And thank you again so so much for being so generous with your time to help me out! And sorry for the late response, I was diving in deep on this and then the studio took xmas break- just started back up today though :)

On 29/12/2014 at 12:31, xxxxxxxx wrote:

I wondered what happened to you. :wink:

Just be aware that what I gave you is a workaround. And might not be the only way to solve it.
I have personally had a lot of troubles with creating new objects, materials, tags, etc.. in C4D. Especially when creating them linearly. Like in scripts.
And I've had to invent all kinds of workarounds to combat this problem.

I still don't know why this happens. And why we sometimes cannot use something directly after it was created with code.
I only know how to hack around it.


On 29/12/2014 at 16:03, xxxxxxxx wrote:

Yeah I am seeing that.

What kind of hack might you suggest implementing to "poll" the object? I cannot seem to get the cache of the mograph object within the same script. I imagine it is some special combination/order of creating the object, duping it, caching it into another object, and finally maybe having access to it. It doesn't seem possible to me yet without using the example you posted, which requires a tag outside of the main PyGenerator script.

Ah well, this is still fun to figure out either way :) Being new to Python in C4D like I am, with every 1 issue you learn 10 new things unintentionally

Thanks again for the help!

On 29/12/2014 at 16:31, xxxxxxxx wrote:

In the example I posted. I'm polling the object using a python scripting tag that I put on the object.
If I were doing this in a plugin. I would normally use the Message() method to poll it.