I have moved this into bugs.
administrators
Posts
- 
RE: The value of 'porgress' during the use of RenderDocument() is greater than 1.0
- 
RE: The value of 'porgress' during the use of RenderDocument() is greater than 1.0Hey @chuanzhen, So, first of all, please excuse the delay. And thank you for the exemplary problem description, I almost booked this under "cannot reproduce" but you really hit the nail on the head with the conditions. There is currently a bug in how the viewport renderer handles sending data to the progress hook. But this only happens when rendering a document from another frame than its starting frame. I am not yet 100% certain, but I am pretty sure this is a bug in the progress hook itself and not the viewport renderer. So, the bug could also appear in other contexts where a specific function of the progress hook is used. But so far the viewport renderer is the only renderer with which I could reproduce this. There is not really anything you can do as a user, because there is a concrete bug. But the viewport renderer calls the progress hook twice each frame: Once with the correct progress, and once with a progress that is shifted by renderFrameRange/documentFrameRangepercent. With some internal code knowledge we can simply say by the call index if the given progress is correct or not. But please understand that this is very much a hack.I will probably fix this, but I cannot fix this retroactively in 2025. This workaround could be useful for you but it is also a bit risky. I have moved this into bugs. Cheers, 
 Ferdinand"""Provides a workaround to get correct progress values when rendering a document with the viewport renderer. This workaround is specific to the case of this thread (viewport renderer + start frame other than the first frame). I know what causes the bug, but I am not yet sure if it is the viewport renderer which feeds the thing with wrong data, or of the thing itself is buggy. THIS IS A HACK USE AT YOUR OWN RISK! DO NOT REMOVE THIS WARNING. See: https://developers.maxon.net/forum/topic/16341 """ import c4d import mxutils doc: c4d.documents.BaseDocument # What basically happens for the preview renderer, is that it calls the progress hook twice for each # frame. The second call happens just two lines after the first call. Each first call provides # "correct" data, while the second does not. However, the first call is only made when RDATA_FASTAFX # is set to false in the render data (is the case by default). The workaround is then simply to build # history data so that we can know if the current call is an even (correct) or odd (incorrect) call. PROGRESS_STACK: list[float] = [] def PythonCallBack(progress, progress_type): """ """ # Just some stuff I used for debugging which only works in 2026 and above. # symbol: str = mxutils.g_c4d_symbol_translation_cache.Get(progress_type, "RENDERPROGRESSTYPE_")[0] # with open("/Users/f_hoppe/Documents/temp/render_progress.txt", "a") as f: # f.write(f"{symbol}: {progress:.2f}%\n") PROGRESS_STACK.append(progress) if len(PROGRESS_STACK) % 2 == 0: # Even call: correct data print(f"Render Progress: {progress:.2f}%") else: # Odd call: incorrect data, ignore, this is the same context as the previous one. pass def main() -> None: """ """ # c4d.ClearPythonConsole() rd = doc.GetActiveRenderData() rd[c4d.RDATA_FASTAFX] = False # REALLY make sure that RDATA_FASTAFX is disabled bmp = c4d.bitmaps.MultipassBitmap(int(rd[c4d.RDATA_XRES]), int(rd[c4d.RDATA_YRES]), c4d.COLORMODE_RGB) if bmp is None: raise RuntimeError("Failed to create the bitmap.") bmp.AddChannel(True, True) if c4d.documents.RenderDocument(doc, rd.GetDataInstance(), bmp, c4d.RENDERFLAGS_EXTERNAL, prog=PythonCallBack, ) != c4d.RENDERRESULT_OK: raise RuntimeError("Failed to render the temporary document.") if __name__ == "__main__": main()
- 
RE: Python tag or Python node in Xpresso crash Cinema 4DHey @SmetK, so I am back, and I have good and bad news  The bad news is that I could not reproduce the crash with your scene on my Win machine with 2026_0_0_db12fb68d6ba_2004155263. I today also asked the users in our beta forum, and multiple users could not reproduce it either.The "good" news is that without me noticing, in the last days a similar crash report (it terminates into the exact same lines as yours) has been attached to the issue I created for your crash. This crash came from a Korean user on macOS and with this users scene I could reproduce your crash (I just got the same exact stack trace). The Korean user does not have any Python in his/her scene, just some Boolean setup not unsimilar to yours. I have grouped everything and added some comments in your ticket, and will in moment briefly talk with the Boolean developer. Python might be here a component in your case (which so far no one go to crash), but there seems to be a base case which happens independently of Python. Which makes sense given how harmless your code is. When you want to follow this case, I would recommend reaching out to end user support, feel free to mention the ticket ITEM#623378 [Python] Booleans crash when building output. When there is a fundamental development which impacts API users, I will post it here for all developers to see, but we should not let end user issues bleed into developer issues.So, I do not have a super satisfying answer, but we can at least reproduce this, which increases the chances of this being fixed. Cheers, 
 Ferdinand
- 
RE: The value of 'porgress' during the use of RenderDocument() is greater than 1.0FYI: I have not forgotten you. Will get to your topic tomorrow, hopefully  
- 
RE: Several Asset Browser IssuesHey Ben, Thank your for your reply. It always pains me a bit to be that upfront when a user has clearly put effort into a problem description. But it is very hard to follow your description and videos when looking at it from a developers standpoint, what to pinpoint and/or fix a bug. Please follow our support procedures regarding reporting bugs and crashes. When need a written step by step instruction, scene files (or in your case a zip database), and what you consider wrong. I would recommend to: - Prune all stuff that is not directly relevant to the crash or bug. When you create a zip database from one of our built-in assets, you can just share the zip and say "this is toony-123" as zip database to demonstrate my problem.
- Then either provide reproduction steps (in which case this would be an end user issue) or the code which triggers the issue and describe in text what leads up to your problem and equally important, what you consider wrong about the outcome.
 But I watched your two videos, and read your posting and got a sense of what you are doing. A few points: - The toon rig uses some dark magic Python code (event notifications, something you better keep away from), where I would not be too surprised if they break. And just for clarity: The Toon rig is not part of the SDK or owned by the SDK. That does not mean we won't help you, but I cannot fix the toon rig for you, that would have to be done by the animation team.
- On some level this does also seem to involve exchange topics, i.e., File IO when you import things.
- The toon rig might need an extra nudge in your script to attach its event handlers or something like that. But I can only evaluate this once I have a file/database to work on and a script to run.
 Cheers, 
 Ferdinand
- 
RE: Usage of SplineHelp.InitSplineWith() regarding flags -> returned dataHey @mogh, Yes, a new topic would be better if we move on to debugging your plugin. But I'll briefly answer here. - That the comb curve flips between 'above' and 'below' is normal and exactly what I talked about before with mathematical vs 'what artists want' tangents. I once spend here some time on explaining this.
- The problem with your screens is a bit that you do not explain what you would consider incorrect. I assume that you understand why the flipping happens and are okay with it and are concerned about the jaggedness of your combs? Without debugging your code this is very hard to evaluate.
 C4D can not get more precise than a 0° degree line object ? Splines are what is often called smooth geometry, i.e., a parametric curve that is mathematically defined and has infinite precision (you can think of it as infinite points). This is opposed by discrete geometry such as a LineObjectwhich has a finite set of data points. The interpolation settings of a spline are the parameters with which the smooth form ('the spline') is brought into a discrete representation ('the line object') for display and other purposes. So, asking if Cinema 4D could discretize with 0° (or less) does not make too much sense, since discretization with 0° means infinite precision, i.e., the smooth form. For practical purposes, 0.1° should be more than enough. And that is probably already more than order of precision more than you actually need, as humans cannot see that level of detail.When you do not want to only control the discretization over the angle of change (a.k.a. curvature), but also over the distance of points, you can also set a maximum length for the segments. For that you must change the spline interpolation from 'Adaptive' to 'Subdivided'. This way, you can ensure that even in straight areas, the segments do not become too long. But the general problem is probably that you do not normalize your data. It makes a difference if the last point is 10 units away from the second last or 1000 units. To approximate the mean curvature between two vertices in some discrete geometry, the formula shown below exists; which is a hack compared to more fancy approaches such as this one. It will approximate the mean curvature between two vertices pandqwith the normalsnpandnq:(nq - np) * (q - p) c_i = ------------------- |q - p|For a mesh vertex you do this with all neighboring vertices and average the result. My guess would be that you are missing this exact normalization step ( /|q - p|, i.e., the value divided by the Euclidean distance between the two points) in your comb calculation. Because curvature is inherently a smooth property, the 'neighbor' of a point in a smooth curve/geometry is always exactly1/infinityunits away. So, you need to ensure that the influence of a neighbor vertex in the discrete world is weighted by its distance to the current vertex.For splines you would probably have to do this for the left and right tangent of each vertex and then take the mean value. But curvature discretization is like all computational geometry subjects not super trivial, and there might be extra/expert steps required for splines I am not aware of right now. Hope this helps, 
 FerdinandHere is a script I wrote a long time ago where I used that 'formula' to approximate the mean curvature for a mesh. This is non-public code, so it has no comments except for me pointing out the formula. """Computes the mean curvature of the selected mesh and stores it in a vertex map. """ import c4d def GetNormal(pid, points, polygons, neighbor): """ """ connected = neighbor.GetPointPolys(pid) vertexNormals = [] for cpoly in [polygons[pid] for pid in connected]: if pid == cpoly.a: o, p, q = points[cpoly.d], points[cpoly.a], points[cpoly.b] elif pid == cpoly.b: o, p, q = points[cpoly.a], points[cpoly.b], points[cpoly.c] elif pid == cpoly.c and cpoly.IsTriangle(): o, p, q = points[cpoly.b], points[cpoly.c], points[cpoly.a] elif pid == cpoly.c and not cpoly.IsTriangle(): o, p, q = points[cpoly.b], points[cpoly.c], points[cpoly.d] elif pid == cpoly.d: o, p, q = points[cpoly.c], points[cpoly.d], points[cpoly.a] vNormal = ~((o-p) % (q-p)) vertexNormals.append(vNormal) factor = 1./len(vertexNormals) return ~(sum(vertexNormals) * factor) def GetCurvature(node): """ """ neighbor = c4d.utils.Neighbor() neighbor.Init(node) points = node.GetAllPoints() polygons = node.GetAllPolygons() def getNeighborVertices(pid): ids = [pid] for i in neighbor.GetPointPolys(pid): cpoly = polygons[i] pnts = [cpoly.a, cpoly.b, cpoly.c] if not cpoly.IsTriangle(): pnts.append(cpoly.d) fi = cpoly.Find(pid) cnt = len(pnts) - 1 prev = pnts[fi - 1] if fi > 0 else pnts[-1] nxt = pnts[fi + 1] if fi < cnt else pnts[0] if prev not in ids: ids.append(prev) if nxt not in ids: ids.append(nxt) for i in ids[1:]: yield i, points[i] data = [] for ip, p in enumerate(points): np = GetNormal(ip, points, polygons, neighbor) temp = [] for iq, q in getNeighborVertices(ip): nq = GetNormal(iq, points, polygons, neighbor) # This is just a very cheap # # (nq - np) * (q - p) # c_i = ------------------- # |q - p| # temp.append((nq - np) * (q - p) / (q - p).GetLength()) c = sum(temp) * (1. / len(temp)) * .5 + .5 data.append(c) neighbor.Flush() return data def CreateVertexMap(node, data): """ """ tag = c4d.VariableTag(c4d.Tvertexmap, len(data)) tag.SetAllHighlevelData(data) tag.SetBit(c4d.BIT_ACTIVE) node.InsertTag(tag) c4d.EventAdd() def main(): """ """ data = GetCurvature(op) CreateVertexMap(op, data) if __name__ == '__main__': main()
- 
RE: Usage of SplineHelp.InitSplineWith() regarding flags -> returned dataHey @mogh, Thank you for reaching out to us. I agree that it is a bit unclear, but the TLDR is that this is just a minor niche feature. I first thought it would be a way to treat a spline as a close splined, with the description a bit weirdly put, but that is not the case. There is only once place where this flag is used, and it is here:  I.e., it seems to try to continue the curvature of the last segment. How this works in detail and how sensible this is, I cannot really tell you either. From the code we can see here, it seems to try to parallel transport the last tangent. But there is also something else going on, as it not only takes the last point but the point before that into account ( i - 2). It then uses the tangent ofm2 * (~m1 * m2), which is also super hand-wavy for me. It basically takes the orientation of the last tangent twice but for A * B, makes B relative to the second last tangent (m1). Or a bit incorrect but more readable: 'It takes the orientation of the last point twice, but also undoes the orientation of the second last point.'How this is relevant I have absolutely no idea, probably some MoGraph related 'custom logic'. My advice: Leave it as its default and otherwise ignore it. As a warning: All the spline helpers implement parallel transport, which can make their output for curvature tasks undesirable (as it messes with what is the mathematical tangent of a spline in favour of what humans would expect. When you are at a loss about what I am talking about, search the forum for parallel transport and my username, the topic has come up multiple times in the past). It might be better to get the LineObjectof a spline, i.e., its current discrete form, and do all the math based on its vertices.Cheers, 
 Ferdinand


