Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware 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
    • Unread
    • Recent
    • Tags
    • Users
    • Login
    1. Maxon Developers Forum
    2. i_mazlov
    • Profile
    • Following 0
    • Followers 0
    • Topics 1
    • Posts 282
    • Best 71
    • Controversial 0
    • Groups 2

    Ilia

    @i_mazlov

    93
    Reputation
    65
    Profile views
    282
    Posts
    0
    Followers
    0
    Following
    Joined Last Online

    i_mazlov Unfollow Follow
    Global Moderator administrators

    Best posts made by i_mazlov

    • Hello PluginCafé

      Dear developers,

      My name is Ilia Mazlov, I joined Maxon SDK Team recently. I'm hoping to be seamlessly integrating to the community and be helpful bringing knowledge and experience I got in the Exchange Team.

      I'm looking forward to working with you! 🙂

      Cheers,
      Ilia

      posted in News & Information news
      i_mazlovI
      i_mazlov
    • RE: access the layers in the shader layer

      Hi James,

      Thank you for your follow up message. The information about what you're trying to achieve is still missing or highly implicit in your reply (there is no question).

      Assuming your goal is to adjust shader attributes (color value for color shader and bitmap path for the bitmap shader), the snippet on the thread that I pointed you out to shows up exactly this case (for the bitmap path): How to change bitmap in layered shader?. I've sketched up the corresponding part for the color shader for you (please find below).

      The c4d.LAYER_S_PARAM_SHADER_LINK parameter basically points to the object that contains the shader. Please, refer to the C++ documentation in this regard: LAYER_S_PARAM_SHADER. Some parts of our API are better documented on the C++ side, so it is worth double checking the missing parts there.

      Cheers,
      Ilia

      Cinema_4D_F6zPrm5IbI.gif

      The code snippet:

      import c4d
      
      doc: c4d.documents.BaseDocument  # The active document
      
      def RetrieveShader(layer: c4d.LayerShaderLayer):
          # Check if the layer is really a shader
          if not layer or layer.GetType() != c4d.TypeShader:
              raise ValueError("Layer is not a shader")
      
          # Retrieve the linked shader of this layer
          return layer.GetParameter(c4d.LAYER_S_PARAM_SHADER_LINK)
      
      def ChangeTexturePathOfLayer(layer: c4d.LayerShaderLayer, texturePath: str) -> None:
          """Change the texture path of an existing Bitmap Layer
          """
          shader: c4d.C4DAtom = RetrieveShader(layer)
      
          # check if this is really a bitmap shader
          if not shader.CheckType(c4d.Xbitmap):
              raise ValueError("First layer is not a bitmap shader")
      
          # Update the texture path of the shader linked to this layer
          shader[c4d.BITMAPSHADER_FILENAME] = texturePath
      
      def ChangeColorShader(layer: c4d.LayerShaderLayer, color: c4d.Vector) -> None:
          """Change the color of and existing Color Shader
          """
          shader: c4d.C4DAtom = RetrieveShader(layer)
      
          if not shader.CheckType(c4d.Xcolor):
              raise ValueError("First layer is not a color shader")
      
          shader[c4d.COLORSHADER_COLOR] = color
      
      def main() -> None:
          # Get the color channel shader for the first material in the scene.
          material: c4d.BaseMaterial | None = doc.GetFirstMaterial()
          if not material:
              raise RuntimeError("Please add at least one material to the document.")
      
          shader: c4d.BaseList2D | None = material.GetFirstShader()
          if not shader or not shader.CheckType(c4d.Xlayer):
              raise RuntimeError("First shader is not a layer shader")
      
          # Update an existing Bimtap Layer
          ChangeTexturePathOfLayer(shader.GetFirstLayer(), r"D:\temp\tex2.jpg")
      
          # Update an existing Color Layer
          ChangeColorShader(shader.GetFirstLayer().GetNext(), c4d.Vector(0., 1., 0.))
      
          c4d.EventAdd()
      
      
      if __name__ == '__main__':
          main()
      
      posted in Cinema 4D SDK
      i_mazlovI
      i_mazlov
    • RE: Discussion on Methods for Calculating the Inner Points of a Quadrilateral

      Hi @chuanzhen ,

      Please note that according to our Support Procedures your question lies out of the scope of our support as it is solely an algorithmic question.

      Regarding your question. There're so many open questions in your problem statement that directly affect the path you're going to follow. Hence, I will first try to ground at least some of them and then we'll see how it can be extended to your more complex scenario if there's a need for that. Feel free to elaborate on your question on a higher scale, if my thoughts go in a wrong direction.

      First, let's assume your quadrilateral is actually a parallelogram (i.e. 1. lives in 2D-space; 2. opposite sides are parallel). That's quite a strong assumption, but we'll get back to it later. Then instead of having a line that intersects point P, we can think of it as a point P_ab on the line AB (because from that your can easily reconstruct your line as it will be parallel to the side lines).

      Simplified case with parallelogramm:
      7187d578-1e54-43cd-8699-6807cd1c10c5-image.png

      Let's then instead working in a 2D space (i.e. working with point P_ab), simplify it further and use the corresponding single value 0.0 <= t_ab <= 1.0, considering that your line AB is defined as f(t) = A + t * (B - A), where t = [0...1], A and B were originally in R^2 (2-component vectors), but since we only work on the line AB, we can consider them being in R (and then will convert them back to R^2 using original points A and B in 2D space).

      If you look at it now, we've reduced the task to the 1D space, where we have interval from 0.0 to 1.0 and some point t_ab in between that corresponds to your initial point P. Let's also for now consider interval [0...100] instead of [0...1], and let's for now consider only integral numbers on this interval, just for the sake of simplicity.

      If we now get back to your original question, with this 1D space problem scope it was reduced to a simple task of searching for the greatest common divisor (GCD) between the two numbers: t_ab and 100. By the way, I assume here that you're looking for the greatest number that would satisfy your problem statement, because number 1 would always be a correct answer as well as some other numbers that are integral divisors of t_ab and 100). Additionally we can quickly make one small optimization step according to the euclidean algorithm and look for t_x = gcd(t_ab, 100 - t_ab) instead.

      Generally speaking that's already the answer you were looking for, but here the interesting part begins. The biggest issue that you have here is the space your numbers are in. It's working just fine in Z (integers), but how do you handle R (real numbers)? Depending on your desired precision the task can be quite expensive for processing. I didn't do any reasearch about it, but the naive way would be to simply make a lossy conversion from R to Z and then back to R (although there might exist some interesting tricks for that).

      Let's get back to our 0.0 <= t_ab <= 1.0 now. In this case, the easy way would be to scale t_ab to some big value and work with it in Z, e.g.

      t_x = gcd(t_ab * 10000, (1 - t_ab) * 10000) / 10000.0
      

      Since you now have t_ab, you can find back P_ab that lies on segment AB. For that you just need to apply your AB formula:

      {P_ab}_k = A + k * t_x * (B - A)
      

      , where k runs from 0 to N = 1.0 / t_x, and {P_ab}_k here is a set of points that lie on line AB. The only remaining step is to get your desired lines that are parallel to side lines AD and BC and go through the set {P_ab}. That was the simplified problem.

      What'd happen now if we have an arbitrary quadrilateral? Actually, the core calculation would stay the same, the only issue would be to find t_ab, because the side lines are not parallel anymore. In this case, I think the easiest way would be to find a perspective transform M from your arbitrary quadrilateral ABCD to something that is easier to work with, e.g. a unit square A'B'C'D'. Once found, you just apply this transform to your original point p: p' = M * p and continue the same route as described with the parallelogram, because square also has parallel side lines.

      Arbitrary quadrilateral:
      e1e2c091-5554-4eba-81cc-e9f26fcdd46e-image.png

      Perspective transform:
      0750635e-9b34-4bc1-a7bb-2f3431035eaa-image.png

      Getting another step back to your original question and extending this approach further to 3D shouldn't be a problem, as you just need to transform your arbitrarily placed quadrilateral such that it is aligned with your coordinate system basis before doing any of the above mentioned calculations. For that you'd need to calculate cross product on 2 adjacent edges of your quadrilateral, make this basis orthonormal and transform coordinates to this new basis.

      In case you also like to cover scenario with non-planar quadrilateral you would likely need to use some imagination for that. At least then the 'perspective transform' trick wouldn't work anymore and you would need to go into all the depths of solving the optimization problem for the minimal distance to your point P.

      Please note that there's a convenient GeometryUtilsInterface that contains lots of useful implementations. For example,
      Barycentric Coordinates functions will be helpful for the 'perspective transform' part
      Projection functions will help you jumping between R^3 and R^2

      Cheers,
      Ilia

      posted in General Talk
      i_mazlovI
      i_mazlov
    • RE: Instance Object Link is not working

      Hello @aghiad322,

      Welcome to the Plugin Café forum and the Cinema 4D development community, it is great to have you with us!

      Getting Started

      Before creating your next postings, we would recommend making yourself accustomed with our Forum and Support Guidelines, as they line out details about the Maxon SDK Group support procedures. Of special importance are:

      • Support Procedures: Scope of Support: Lines out the things we will do and what we will not do.
      • Support Procedures: Confidential Data: Most questions should be accompanied by code but code cannot always be shared publicly. This section explains how to share code confidentially with Maxon.
      • Forum Structure and Features: Lines out how the forum works.
      • Structure of a Question: Lines out how to ask a good technical question. It is not mandatory to follow this exactly, but you follow the idea of keeping things short and mentioning your primary question in a clear manner.

      About your First Question

      There's a great example script that reveals how to work with instance object.

      Regarding your exact scenario, you have to insert the referenced object to the document as well. You can check the following code snippet:

      import c4d
      
      doc: c4d.documents.BaseDocument  # The active document
      
      def main() -> None:
          objCube = c4d.BaseList2D(5159)
      
          objInstance = c4d.InstanceObject()
          objInstance.SetReferenceObject(objCube)
          # objInstance = c4d.BaseList2D(5126)
          # objInstance[c4d.INSTANCEOBJECT_LINK] = objCube
      
          doc.InsertObject(objInstance)
          doc.InsertObject(objCube)
      
      if __name__ == '__main__':
          main()
      

      Note, commented out you find an equivalent way to create and setup up instance object.

      Cheers,
      Ilia

      posted in Cinema 4D SDK
      i_mazlovI
      i_mazlov
    • RE: Discussion on Methods for Calculating the Inner Points of a Quadrilateral

      Hi @chuanzhen ,

      Sorry for the delayed answer.

      I've just realized that perspective transform conversion that I've suggested to you earlier isn't going to work here, as it is not an affine transform, hence it doesn't preserve ratios on edges (and preservation of collinearity is not enough in your case).

      However, once I had a fresh look at the problem here, I've also realized that even if some transform M would have worked here, it would have been such an overkill for this problem. What you're actually looking for is just a UV coordinates of your point P' that lies inside of an arbitrary quadrilateral, so there's even no need to transform anything here.

      When you have your ABCD quadrilateral defined in R^3 space and some point Q' = <u, v>, defined in R^2: 2D plane of this quadrilateral, then you can find your point P' (defined in R^3) by doing a bilinear interpolation along edges of your quadrilateral. However, in your case you need to solve the inverse problem, when you have your P' and would like to find Q'. This is as easy as solving a properly defined quadratic equation on v (or u, doesn't really matter).

      Luckily you can use CalculatePolygonPointST() in C++ (or GetPolyPointST() in Python) exactly for that. Please find attached scene that demonstrates exactly this.

      sdk-quadrilateral.c4d

      Cheers,
      Ilia

      Behavior:
      Cinema_4D_NvfjWEM651.gif
      The python tag code is:

      import c4d
      
      doc: c4d.documents.BaseDocument
      op: c4d.BaseTag
      
      def main() -> None:
          polyOp: c4d.BaseObject = op.GetObject()
      
          targetOp: c4d.BaseObject = polyOp.GetDown()
          lineUOp: c4d.BaseObject = targetOp.GetNext()
          lineVOp: c4d.BaseObject = lineUOp.GetNext()
      
          [A, B, C, D] = polyOp.GetAllPoints()
      
          P = targetOp.GetRelPos()
          
          hl = c4d.modules.hair.HairLibrary()
          U, V = hl.GetPolyPointST(P, A, B, C, D, True)
      
          AB, DC, AD, BC = B - A, C - D, D - A, C - B
      
          lineUOp.SetPoint(0, A + AB * U)
          lineUOp.SetPoint(1, D + DC * U)
          lineVOp.SetPoint(0, A + AD * V)
          lineVOp.SetPoint(1, B + BC * V)
      

      In C++ the usage would be very similar:

      PolygonObject* polyObjOp; // = static_cast<PolygonObject*>(op);
      Vector* ptsPoly = polyObjOp->GetPointR();
      
      Vector A = ptsPoly[0], B = ptsPoly[1], C = ptsPoly[2], D = ptsPoly[3];
      Float U = 0, V = 0;
      Vector P = pointOp->GetRelPos();
      
      Bool inBoundary = maxon::GeometryUtilsInterface::CalculatePolygonPointST(P, A, B, C, D, true, U, V);
      
      posted in General Talk
      i_mazlovI
      i_mazlov
    • RE: How to: Plugin parent folder on extensions menu

      Hi @cybor09,

      Thanks for reaching out to us. You can achieve such grouping by simply registering your plugins in a regular way (please find a code snippet below). It's a default behavior that organizes all your plugins under a folder named after your plugin folder.

      Let me know if you have any further questions.

      Cheers,
      Ilia

      d67c9b38-b8c0-4f73-ab5e-7d695b33af9a-image.png

      *.pyp file content:

      import c4d, os
      from c4d import gui
      
      PLUGIN1_ID = 999121031
      PLUGIN2_ID = 999121032
      PLUGIN3_ID = 999121033
      
      class PC_12103_1(c4d.plugins.CommandData):
          def Execute(self, doc):
              print("Execute the 1st command")
              return True
      
      class PC_12103_2(c4d.plugins.CommandData):
          def Execute(self, doc):
              print("Execute the 2nd command")
              return True
      
      class PC_12103_3(c4d.plugins.CommandData):
          def Execute(self, doc):
              print("Execute the 3rd command")
              return True
      
      def loadIconBMP(iconName: str) -> c4d.bitmaps.BaseBitmap:
          directory, _ = os.path.split(__file__)
          fn = os.path.join(directory, "res", iconName)
          # Creates a BaseBitmap
          bmp = c4d.bitmaps.BaseBitmap()
          if bmp is None:
              raise MemoryError("Failed to create a BaseBitmap.")
          # Init the BaseBitmap with the icon
          if bmp.InitWith(fn)[0] != c4d.IMAGERESULT_OK:
              raise MemoryError("Failed to initialize the BaseBitmap.")
          return bmp
      
      if __name__ == "__main__":
          c4d.plugins.RegisterCommandPlugin(PLUGIN1_ID, "1st Cmd", 0, loadIconBMP("icon1.tif"), "", PC_12103_1())
          c4d.plugins.RegisterCommandPlugin(PLUGIN2_ID, "2nd Cmd", 0, loadIconBMP("icon2.png"), "", PC_12103_2())
          c4d.plugins.RegisterCommandPlugin(PLUGIN3_ID, "3rd Cmd", 0, loadIconBMP("icon3.png"), "", PC_12103_3())
      

      Plugin folder structure:
      8e76bb36-3c3c-49f4-9c88-3bf7600fdc90-image.png

      posted in Cinema 4D SDK
      i_mazlovI
      i_mazlov
    • RE: Discussion on Methods for Calculating the Inner Points of a Quadrilateral

      Hi @chuanzhen,

      sorry for the delayed answer.

      Thank you for sharing your findings with us! This is a numerical issue in the GetPolyPointST() implementation. I've fixed it but it will only be available in one of our future releases.

      Cheers,
      Ilia

      posted in General Talk
      i_mazlovI
      i_mazlov
    • RE: How to connect new ports and node ports in the group?

      Hello @君心Jay,

      Welcome to the Plugin Café forum and the Cinema 4D development community, it is great to have you with us!

      Getting Started

      Before creating your next postings, we would recommend making yourself accustomed with our Forum and Support Guidelines, as they line out details about the Maxon SDK Group support procedures. Of special importance are:

      • Support Procedures: Scope of Support: Lines out the things we will do and what we will not do.
      • Support Procedures: Confidential Data: Most questions should be accompanied by code but code cannot always be shared publicly. This section explains how to share code confidentially with Maxon.
      • Forum Structure and Features: Lines out how the forum works.
      • Structure of a Question: Lines out how to ask a good technical question. It is not mandatory to follow this exactly, but you should follow the idea of keeping things short and mentioning your primary question in a clear manner.

      About your First Question

      First of all there is no "correct" way of doing that. The pattern you should use depend on the context of your goal.

      For example, looking at your code snippet it seems that the easiest way for you to make these connections would be to first connect the graph with the outside nodes and then use the MoveToGroup function, which would rewire the connections for you automatically.

      This is, however, not the general case. If you would really like to manually wire these ports, you would simply need to find them inside the group and connect them normally using Connect function. The pitfall here is that once you move your sub-graph to the group, it changes its path. This basically means that you'd need to find these nodes using FindNodesByAssetId or GetInnerNodes functions.

      Please refer to the draft sketch below that shows both approaches (you need to comment/uncomment corresponding function call in main()).

      Cheers,
      Ilia

      Draft sketch:

      import c4d
      import maxon
      
      doc: c4d.documents.BaseDocument
      mat: c4d.BaseMaterial
      nimbusRef: maxon.NimbusBaseInterface
      graph: maxon.GraphModelInterface
      
      def main():
          global doc, mat, nimbusRef, graph
          mat = doc.GetActiveMaterial()
          nimbusRef = mat.GetNimbusRef(c4d.GetActiveNodeSpaceId())
          graph = nimbusRef.GetGraph()
      
          easy_way()
          # manual_wiring()
      
      class rsID:
          def StrNodeID(id: str) -> str:
              return "com.redshift3d.redshift4c4d.nodes.core." + id
          def StrPortID(nodeId: str, portId: str) -> str:
              return "com.redshift3d.redshift4c4d.nodes.core." + nodeId + "." + portId
          
      def easy_way():
          with graph.BeginTransaction() as transaction:
              # The outside value node
              valNode: maxon.GraphNode = graph.AddChild(maxon.Id(), maxon.Id("net.maxon.node.type"))
              valPortOutput: maxon.GraphNode = valNode.GetOutputs().FindChild("out")
      
              # The inside ABS node
              absNode: maxon.GraphNode = graph.AddChild(maxon.Id(), maxon.Id(rsID.StrNodeID("rsmathabs")))
              absPortInput: maxon.GraphNode = absNode.GetInputs().FindChild(rsID.StrPortID("rsmathabs", "input"))
              absPortOutput: maxon.GraphNode = absNode.GetOutputs().FindChild(rsID.StrPortID("rsmathabs", "out"))
      
              # Existing Standard material node
              stdMatNodes: list[maxon.GraphNode] = []
              maxon.GraphModelHelper.FindNodesByAssetId(graph, maxon.Id(rsID.StrNodeID("standardmaterial")), True, stdMatNodes)
              matNode: maxon.GraphNode = stdMatNodes[0]
              matPortOpac: maxon.GraphNode = matNode.GetInputs().FindChild(rsID.StrPortID("standardmaterial", "opacity_color"))
      
              # Connect everything in place
              valPortOutput.Connect(absPortInput)
              absPortOutput.Connect(matPortOpac)
      
              # Move to group -> rewiring is handled automatically
              graph.MoveToGroup(maxon.GraphNode(), maxon.Id("PSRgroupID"), [absNode])
      
              transaction.Commit()
          c4d.EventAdd()
          
      def manual_wiring():
          with graph.BeginTransaction() as transaction:
              # The outside value node
              valNode: maxon.GraphNode = graph.AddChild(maxon.Id(), maxon.Id("net.maxon.node.type"))
              valPortOutput: maxon.GraphNode = valNode.GetOutputs().FindChild("out")
      
              # The inside ABS node
              absNode: maxon.GraphNode = graph.AddChild(maxon.Id(), maxon.Id(rsID.StrNodeID("rsmathabs")))
      
              # Existing Standard material node
              stdMatNodes: list[maxon.GraphNode] = []
              maxon.GraphModelHelper.FindNodesByAssetId(graph, maxon.Id(rsID.StrNodeID("standardmaterial")), True, stdMatNodes)
              matNode: maxon.GraphNode = stdMatNodes[0]
              matPortOpac: maxon.GraphNode = matNode.GetInputs().FindChild(rsID.StrPortID("standardmaterial", "opacity_color"))
      
              # Move to group straight away
              groupRoot = graph.MoveToGroup(maxon.GraphNode(), maxon.Id("PSRgroupID"), [absNode])
      
              # Create group ports
              groupPortInputOutside: maxon.GraphNode =  maxon.GraphModelHelper.CreateInputPort(groupRoot, "group_input_id", "grpInput")
              groupPortOutputOutside: maxon.GraphNode =  maxon.GraphModelHelper.CreateOutputPort(groupRoot, "group_output_id", "grpOutput")
      
              # Connect group to the outside nodes
              valPortOutput.Connect(groupPortInputOutside)
              groupPortOutputOutside.Connect(matPortOpac)
              
              # Find innder node
              innerNodes: list[maxon.GraphNode] = []
              maxon.GraphModelHelper.FindNodesByAssetId(graph, maxon.Id(rsID.StrNodeID("rsmathabs")), True, innerNodes)
              absNodeInner: maxon.GraphNode = innerNodes[0]
      
              # Find inner node ports
              absNodeInnerInput: maxon.GraphNode = absNodeInner.GetInputs().FindChild(rsID.StrPortID("rsmathabs", "input"))
              absNodeInnerOutput: maxon.GraphNode = absNodeInner.GetOutputs().FindChild(rsID.StrPortID("rsmathabs", "out"))
      
              # Connect inner node ports to group ports
              groupPortInputOutside.Connect(absNodeInnerInput)
              absNodeInnerOutput.Connect(groupPortOutputOutside)
              
              transaction.Commit()
          c4d.EventAdd()
      
      if __name__ == '__main__':
          main()
      
      posted in Cinema 4D SDK
      i_mazlovI
      i_mazlov
    • RE: Boundary Edges to Spline in Python

      Hi Tomasz,

      There're 3 steps you need to accomplish:

      1. Enable Check mesh tool
      • The mesh checking settings live in a snap scene hook, so this is how you access it. Unfortunately its not that easy as with snap settings, but you can iterate over BranchInfo of the snap scene hook and find the modeling settings one (check getModelingSettings() function in the script below).
      1. Make corresponding selection
      • You need to call the corresponding button. In C++ you normally do this using the DescriptionCommand, but in python you can simply use CallButton() function.
      1. Run corresponding command
      • Just an ordinary usage of SendModelingCommand(). Specifically for the edge to spline command is discussed in the thread: Edge To Spline Modeling Command

      Please find the code sketch that presumable does what you need below.

      Cheers,
      Ilia

      Draft code snippet:

      import c4d
      
      doc: c4d.documents.BaseDocument  # The currently active document.
      
      ID_SNAP_SCENEHOOK = 440000111
      
      def getModelingSettings() -> c4d.GeListNode:
          # Find Snap scene hook
          snapHook: c4d.BaseList2D = doc.FindSceneHook(ID_SNAP_SCENEHOOK)
      
          # Search for the modeling settings in its branch info
          branchInfo: dict
          for branchInfo in snapHook.GetBranchInfo():
              if branchInfo.get('name') != 'MdSH':
                  continue
              head: c4d.GeListHead
              if head := branchInfo.get('head'):
                  return head.GetFirst()
          
          return None
      
      def main() -> None:
          # Retrieve modeling settings
          ModelingSettings: c4d.GeListNode = getModelingSettings()
          if not ModelingSettings:
              return print('Unexpected error')
          
          # Enable mesh check
          ModelingSettings[c4d.MESH_CHECK_ENABLED] = True
      
          # Call select boundary edges button
          c4d.CallButton(ModelingSettings, c4d.MESH_CHECK_BOUNDARY_SELECT)
      
          # Run edge to spline command
          objects: list[c4d.BaseList2D] = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_NONE)
          if not c4d.utils.SendModelingCommand(c4d.MCOMMAND_EDGE_TO_SPLINE, objects, c4d.MODELINGCOMMANDMODE_EDGESELECTION):
              print('Error on SendModelingCommand() execution')
          
          c4d.EventAdd()
      
      
      if __name__ == '__main__':
          main()
      
      posted in Cinema 4D SDK
      i_mazlovI
      i_mazlov
    • RE: how to program plugins with python?

      Hi Klaus,

      @baca is again completely right! 👍

      Although python has lots of advantages (really fast start for coding, no need in compilation, reloading changes without restarting cinema etc just to mention a couple), however, this doesn't come with no cost. It is quite slow especially in highly loaded parts of code execution (like scene hooks or GetVirtualObjects()).

      There's an opportunity to offload expensive parts of your code into C++ library and then call these functions from within your python execution environment. This approach is called Python bindings, you can search more information about it yourself, e.g. here: Python Bindings: Calling C or C++ From Python.

      However, this tend to speed things up when you have an expensive task that is executed not too frequently. I'm not aware of what you're doing in your plugin, but it sounds like you have to deal with numerous amount of C4D objects, which drastically slows down your plugin. If that's the case, then I assume the speed up you would gain from the python bindings wouldn't be significant.

      Cheers,
      Ilia

      posted in Cinema 4D SDK
      i_mazlovI
      i_mazlov

    Latest posts made by i_mazlov

    • RE: save/keep cache of generator plugin

      Hi Sebastian,

      Sorry for the delay.

      I'm afraid you're quite limited with the python generator, when it comes to storing your data, so the general suggestion would be switching to using the plugin. This way you could just override the NodeData.Read, NodeData.Write and NodeData.CopyTo to store and load your data using the HyperFile. For the more information on the NodeData.Read/Write function you could check the C++ manual.

      Let me know you have any other questions!
      Cheers,
      Ilia

      posted in Cinema 4D SDK
      i_mazlovI
      i_mazlov
    • RE: save/keep cache of generator plugin

      Hi Sebastian,

      FYI I'll get to answer your posting beginning next week.

      Cheers,
      Ilia

      posted in Cinema 4D SDK
      i_mazlovI
      i_mazlov
    • RE: Sweep Modifier

      Hi John,

      Having a modifier that changes the number of points (especially when you reduce them) isn't a very good idea, because in some setups you can have some dependencies that would be at least broken or even lead to the unstable execution. I think a better idea in this case would be to look in the direction of a generator object instead.

      With that's said, I've just briefly checked your code in the latest Cinema 4D version (that hasn't been release yet) and the issue is not reproducible there. This might be due to the undefined behavior of such modifier or due to some internal changes that I'm not aware of, this I can't say for sure.

      Cheers,
      Ilia

      posted in Cinema 4D SDK
      i_mazlovI
      i_mazlov
    • RE: ZBP file format

      Hi @madfx,

      Welcome to the Maxon developers forum and its community, it is great to have you with us!

      Getting Started

      Before creating your next postings, we would recommend making yourself accustomed with our forum and support procedures. You did not do anything wrong, we point all new users to these rules.

      • Forum Overview: Provides a broad overview of the fundamental structure and rules of this forum, such as the purpose of the different sub-forums or the fact that we will ban users who engage in hate speech or harassment.
      • Support Procedures: Provides a more in detail overview of how we provide technical support for APIs here. This topic will tell you how to ask good questions and limits of our technical support.
      • Forum Features: Provides an overview of the technical features of this forum, such as Markdown markup or file uploads.

      It is strongly recommended to read the first two topics carefully, especially the section Support Procedures: How to Ask Questions.

      About your First Question

      I'm sorry for the delayed answer.

      Unfortunately there's no existing public API that would allow you to do that.

      Cheers,
      Ilia

      posted in ZBrush SDK
      i_mazlovI
      i_mazlov
    • RE: Button scaled unexpectedly when it is in the same row with an expanded linkbox

      Hi @BruceC!

      The expandable UIs like COSTOMGUI_LINKBOX are not designed to be used with other elements on the same row, hence can lead to the undefined behavior like what you're experiencing. I suppose the workaround for you would be to put the linkbox on the row below the button.

      Cheers,
      Ilia

      posted in Cinema 4D SDK
      i_mazlovI
      i_mazlov
    • RE: DrawHudText interferes with Polygon Pen Tool etc

      Hi @FlavioDiniz,

      thanks for providing more information on the issue.

      Regarding your "Issue 2". I see no reason for the background color (and other properties) to not work in your setup, hence I've created a bug report for that (ITEM#587756). Thanks for reporting the issues you face during your development.

      We are happy to handle your "Issue 1" about "constantly unsaved document" in a separate thread, once you create it yourself.

      Cheers,
      Ilia

      posted in Cinema 4D SDK
      i_mazlovI
      i_mazlov
    • RE: DrawHudText interferes with Polygon Pen Tool etc

      Hi Flavio,

      Thanks for getting back to us! There's no reason to be sorry, it's the opposite, you're very welcome to share your ideas and post questions!

      I've forked your question to a separate thread. Even though this sounds like a similar issue, under the hood it can be a completely different story 😉

      Please attach a sample project file (or at least the python code you're using), which would help us reproducing the behavior from the video. This makes things happenning easier and faster.

      Cheers,
      Ilia

      posted in Cinema 4D SDK
      i_mazlovI
      i_mazlov
    • RE: Frozen Matrix different

      Hi @chuanzhen,

      Glad to hear the issue went away.

      Just to note, the frozen matrices are designed in a way that simulate behavior of an intermediate parent null object, i.e. just as if you had an invisible parent null object with matrix being equal to the frozen one. Sometimes it can get confusing, because the way the global matrix of the object is calculated is effectively:

      [Local Matrix] = [Frozen Translation] * [Frozen Rotation] * [Relative Translation] * [Relative Rotation] * [Frozen Scale] * [Relative Scale]

      If the relative transform (the main one) and the frozen transform are the same for two objects, given they are children of the same parent and there're no other 3rd parties involved (e.g. plugins, python tags etc), the global transform matrices of these two objects are the same.

      Please find more useful insights in our great Matrix Manual.

      Cheers,
      Ilia

      posted in Cinema 4D SDK
      i_mazlovI
      i_mazlov
    • RE: Set RenderData framerate causing C4D to crash

      Hi @chuanzhen,

      The issue is not reproducible on my machine with C4D: 2025.2.1 (Build 2025_2_1_10be3ac9d371_1759753353) Win

      Cheers,
      Ilia

      posted in Bugs
      i_mazlovI
      i_mazlov
    • RE: Adding an Object to a New Cinema 4D Asset Database

      Hi @d_keith,

      I would kindly ask you to check our Support Procedures, namely the "How To Ask Questions" paragraph:

      Singular Question: The initial posting of a support topic must contain a singular question. Do not ask ten things at once, that makes it extremely hard to answer topics. Break up your questions into multiple topics.

      Here you effectively have 4 different questions about Asset Browser, and these are candidates for 4 different threads on the forum. In your further postings please try to follow the aforementioned rules.

      Regarding your questions:

      1. Should I be able to mount a directory and have it auto-create a DB?

      Mounting database is effectively executing the AssetDataBasesInterface.SetDatabases. It has nothing to do with creating database neither semantically, nor is this mentioned in the docu. If you need to create repository, please use maxon.AssetInterface.CreateRepositoryFromUrl(), it will scan the directory for being already a database and create proper dir structure if it's not.

      1. Any idea why I need to re-try creating the Repo for it to work?

      If you face any errors in the script, please always attach at least the error message! In this case I assume you receive the following error, when executing the maxon.AssetRepositoryTypes.AssetDatabase() for the first time after Cinema 4D started. Looks like a bug to me, I've created a bug report for that (ITEM#585831).

      The error message:

      Traceback (most recent call last):
        File "console", line 1, in <module>
        File "C:\Program Files\Maxon Cinema 4D 2025\resource\modules\python\libs\python311\maxon\interface.py", line 5049, in __call__
          self._cachedObj.R = _maxon_mapping.GetAssociatedDataType(dt)
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      Exception: unable to find datatype
      

      As a workaround you can just execute the following line first in your main():

      maxon.AssetRepositoryTypes.AssetDatabase()
      
      1. ... about multiple DBs ...

      You're giving your databases unique IDs with the line repo_id = maxon.AssetInterface.MakeUuid(str(url), True). If you need them to be the same, just pick one instead, e.g. repo_id = maxon.Id('net.maxon.sdk.cool.things.repo')

      1. ... revealing assets doesn't work...

      I've created a bug report for that, as it looks like a bug with refreshing in the Asset Browser. As a workaround you can reload databases and reopen the AB before reveling your assets, e.g.:

      def RepenAssetBrowser():
          CID_ASSET_BROWSER = 1054225
      
          # Close the Asset Browser if it's alredy opened
          if c4d.IsCommandChecked(CID_ASSET_BROWSER):
              c4d.CallCommand(CID_ASSET_BROWSER)
          
          # Open again
          c4d.CallCommand(CID_ASSET_BROWSER)
      
      def main():
      	# ...
      	# assets = []
      	# ...
      	# assets.append(asset)
      	# ...
      	
          maxon.AssetDataBasesInterface.ReloadAssetRepositories(True)
          RepenAssetBrowser()
          maxon.AssetManagerInterface.RevealAsset(assets)
      

      Cheers,
      Ilia

      posted in Cinema 4D SDK
      i_mazlovI
      i_mazlov