This riddle has stumped me:
I create a simple Python effector. Here's a trimmed-down version:
from c4d.modules import mograph as mo
def main() :
moData = c4d.modules.mograph.GeGetMoData(op)
if moData is None: return False
hasField = op[c4d.FIELDS].HasContent()
print ("hasField:", hasField)
farr = moData.GetFalloffs()
print ("Falloffs:", farr)
It does nothing to the clones in this case, just shows me the falloffs (the original modifies the clones, but it behaves the same and doesn't add to the question). The scene is also very simple, the effector has a box field that moves over some cube clones.
Now here's the riddle:
When I animate this scene in the viewport, the console shows me the expected falloffs, changing as the animation runs. All is well with the world.
When I render the same scene however, the console shows me a constant 0.0 for all falloffs.
I am not able to find any explanation, any flag to set, any function to call. There are multiple examples for Python effectors, and multiple threads on this forum, none of which mentions any issue with rendering.
I can put a MoGraph Cache Tag on the cloner, which works fine, and which replays fine (also with the more complicated original scene). But I don't see it mentioned anywhere that a cache is mandatory. The rendering should IMO just work.
(I tried this on R21 too, with the same results.)
You don't need to write you own code, just using the default python effector on full control, once you add a field, the effector will not work at render time.
I've reported the bug a few times ago, but it is still there.
Ouch! In R25 still? Thanks for the confirmation; so I may need to rewrite my effector in Parameter mode (since you mentioned the mode, I suppose it'll work this way?)
Okay, Parameter Mode is even worse when it comes to falloffs.
GetFalloffs() results in an actual error raised.
GetArray(c4d.MODATA_FALLOFF_WGT) results in None.
GetArray(c4d.MODATA_WEIGHT) returns a proper array, but it's filled with zeroes no matter where the field is.
Also, the auto-gen sample code (at least in R23) is faulty:
it uses an attribute data which is undefined, causing an error (I found on the forum that the mode can be read via GetBlendID, though); and it reads the MoData from the gen variable instead of op, which results in GetCurrentIndex always returning 0.
It doesn't work in the expected way anyhow. The vector returned by the Python code for ID_MG_BASEEFFECTOR_POSITION is not an absolute, but kind of a weight for the value set in the GUI. I'd need to return the final value though, which cannot be expressed as a product. This works in Full Control mode, but as the falloffs will not work there either with a field while rendering, I guess... I lost the game.
I don't remember the version, but we fixed the default script, now it is working in parameter control. I just remember there was no workaround for the python effector to work in full control mode.
@m_magalhaes Meanwhile, I found one (more or less): calculating the falloff myself. I just need to access the field list and perform a SampleList on the clones' positions. This actually works within the render process as well. Looks approx. like this:
md = mo.GeGetMoData(op)
if not md: return False
cnt = md.GetCount()
marr = md.GetArray(c4d.MODATA_MATRIX)
fieldlist = op[c4d.FIELDS]
matCloner = gen.GetMg()
inputField = c4d.modules.mograph.FieldInput([marr[i].off * matCloner for i in range(cnt)], cnt)
info = c4d.modules.mograph.FieldInfo.Create(c4d.FIELDSAMPLE_FLAG_VALUE, None, doc, 0, 1, inputField, gen)
outputs = c4d.modules.mograph.FieldOutput.Create(cnt, c4d.FIELDSAMPLE_FLAG_VALUE)
fieldlist.SampleList(info, inputField, outputs)
# print (outputs.GetValue(0))
for i in range(0, cnt):
fieldStrength = outputs.GetValue(i)
# usw usf
Aaaand of course I forgot a crucial point: After modifying the clone positions in marr, you need to write them back to the MoData, but the last parameter apply_strength must be False:
moData.SetArray(c4d.MODATA_MATRIX, marr, False)
This is at first glance unintuitive since we want the strength to be present but when looking closely, you can fathom that the function will internally multiply with the falloff strength that it calculated itself, and that probably comes from GetFalloffs() again, so it's 0 and nothing is won. We do not want SetArray to do that at all, because we have already applied the falloffs that we calculated ourselves, and SetArray should keep its buggy hands off it!