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. AiMiDi
    3. Topics
    A
    • Profile
    • Following 1
    • Followers 0
    • Topics 21
    • Posts 54
    • Best 2
    • Controversial 0
    • Groups 0

    Topics created by AiMiDi

    • A

      How should I download Cineware sdk?

      Cineware SDK
      • windows s22 • • AiMiDi
      2
      0
      Votes
      2
      Posts
      841
      Views

      M

      Thanks for the report, I've fixed the link.
      Cheers,
      Maxime.

    • A

      How to redraw F-Curve Spline by using keyFrames data from Animation Curve?

      Cinema 4D SDK
      • • • AiMiDi
      4
      0
      Votes
      4
      Posts
      697
      Views

      i_mazlovI

      Hi @AiMiDi ,

      The problem you're pointing out to lies in the algorithmic field and relates to a non-public part of C4D, hence is Out of Scope for this forum:

      We will neither reveal non-public functionalities of Cinema 4D's API, nor will we establish a direct line of communication with the developers of Cinema 4D.

      Let me know if you have any further questions!

      Cheers,
      Ilia

    • A

      Crash when calling TreeViewFunctions::DrawCell

      Cinema 4D SDK
      • r23 c++ • • AiMiDi
      4
      0
      Votes
      4
      Posts
      729
      Views

      A

      Hi @m_adam , thanks for your apply. I have solved this problem. The reason is I used maxon::BaseArray to store tree node data. When I append the array, the address of node may change, but the next pointer in node remain unchanged. And this cause the crash.

    • A

      How to track user changes to parameter animation

      Cinema 4D SDK
      • r21 c++ • • AiMiDi
      5
      0
      Votes
      5
      Posts
      829
      Views

      ManuelM

      hi,

      There is no really way of doing it. The only way is to keep track of what is going on. the number of keys, the position, values etc.
      When setDparameter is called you need to compare and update your data.
      You could just store the dirty state or a lot more information. Keep in mind that you can have mutiple tracks for the position. This is tricky and long to do but possible.

      Cheers,
      Manuel

    • A

      How do I get GvNodeMaster stored in Redshift material

      Cinema 4D SDK
      • r21 c++ • • AiMiDi
      4
      0
      Votes
      4
      Posts
      1.1k
      Views

      ferdinandF

      Hello @aimidi,

      please excuse, I overlooked the C++ tag you did assign to your posting. R21, the version you tagged this posting as, is also out of scope of support. The code examples I provided below have been written for R25. There were substantial changes to the C++ Python API with R23, you might not be able to translate that example to R21. The branch info approach should work in R21.

      The short answer is, "no, there is no C++ API for Redshift at the moment". You can however use the Python API from C++ or use BranchInfo to access the Xpresso nodes attached to a Redshift material. The Python approach has the advantage that you can make use of the whole Python Redshift API, but the disadvantage that it is a bit involved. The branch info approach is easier, but it will only solve your problem at hand, getting the underlying graph, and will not give you access to the other functionalities of the Redshift Python API (there are not many other features in the Python API).

      Find below an example demonstrating both approaches.

      Cheers,
      Ferdinand

      The result:
      redshiftcpp.gif

      The code:

      // For https://developers.maxon.net/forum/topic/13752 #include "c4d_basedocument.h" #include "c4d_basematerial.h" #include "c4d_general.h" #include "c4d_graphview.h" #include "lib_py.h" #include "maxon/cpython.h" #include "maxon/cpython_c4d.h" #include "maxon/cpython3_raw.h" #include "maxon/errortypes.h" #include "maxon/vm.h" // The type id of Redshift materials. const maxon::Int32 g_redshift_material_id = 1036224; /// Attempts to retrieve the Redshift GvNodeMaster of #material with Python. /// /// @param material The material to get the Redshift GvNodeMaster for. /// @param pythonCode The Python code that is used to retrieve it. /// @return The Redshift GvNodeMaster for #material. /// ------------------------------------------------------------------------------------------------ static maxon::Result<GvNodeMaster*> CallRedshiftPythonLayer( BaseMaterial* material, const maxon::String& pythonCode) { iferr_scope; // Bail when #material is null or not a Redshift material. if (!material || (material->GetType() != g_redshift_material_id)) return maxon::IllegalArgumentError( MAXON_SOURCE_LOCATION, "Passed material is not a Redshift material."_s); // Initialize a scope with the Python 3 virtual machine for #pythonCode. const maxon::VirtualMachineRef& vm = MAXON_CPYTHON3VM(); const maxon::VirtualMachineScopeRef scope = vm.CreateScope() iferr_return; scope.Init("Python"_s, pythonCode, maxon::ERRORHANDLING::PRINT, nullptr) iferr_return; // We could setup here the modules attributes doc and op that are commonly used in Cinema 4D // Python scripts by adding them to the scope. But that is not really necessary in the example // since we just call one function which does not require any module attributes. // Set the __name__ attribute of the module so that the execution context guard ifnamemain works. // Also not necessary in a technical sense, since we do not make use of it. scope.Add("__name__"_s, maxon::Data("__main__"_s)) iferr_return; // When the script does modify the active document, you must stop all threads before executing it. // Here not necessary, since the example code does not modify anything. StopAllThreads(); // Execute the module context of the script, i.e., carry out imports and module level expressions, // as for example an ifnamemain execution guard. scope.Execute() iferr_return; // Now we are going to call the specific function GetNodeMaster() in the script. // Make the passed material accessible as an argument for the python function GetNodeMaster() // by casting it into its binding type that handles the C++/Python type binding. maxon::specialtype::BaseMaterial* materialSpeical = ( reinterpret_cast<maxon::specialtype::BaseMaterial*>(material)); // Create a data structure for the arguments of GetNodeMaster(), containing the material binding. maxon::Data materialArgument = maxon::Data(materialSpeical); maxon::BaseArray<maxon::Data*> arguments; arguments.Append(&materialArgument) iferr_return; // Setup the return type. We are using here BaseList2D for the return type, or more specifically // its special binding type, since there is no dedicated binding type for GvNodeMaster. But // GvNodeMaster is of type BaseList2D and we will later attempt to cast the return value back to // GvNodeMaster. maxon::BlockArray<maxon::Data> helperStack; const maxon::DataType& returnType = maxon::GetDataType<maxon::specialtype::BaseList2D*>(); // Call GetNodeMaster() in the scope and interpret its return value(s). maxon::Data* res = scope.PrivateInvoke( "GetNodeMaster"_s, helperStack, returnType, &arguments.ToBlock()) iferr_return; // The function execution did fail. if (!res) return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Function execution did fail."_s); // The function in Python does not conform to the expected return type of BaseList2D. maxon::specialtype::BaseList2D* returnValue = res->Get<maxon::specialtype::BaseList2D*>().GetValue(); if (returnValue == nullptr) return maxon::UnexpectedError( MAXON_SOURCE_LOCATION, "Function does not conform to its return value signature."_s); // The C++/Python binding type return value cast back into its native C++ form. BaseList2D* result = reinterpret_cast<BaseList2D*>(returnValue); // Finally cast it into what we are after, a GvNodeMaster. GvNodeMaster* nodeMaster = static_cast<GvNodeMaster*>(result); return nodeMaster; } /// Attempts to retrieve the Redshift node graph of #material with branching information. /// /// An alternative and less complex solution than CallRedshiftPythonLayer() which uses branch info /// to get hold of the graph. This is easier but other than with the Python approach it will give /// you not access to all the things the Redshift Python API can do. /// /// @param material The material to get the Redshift graph for. /// @return The Redshift graph for #material. /// ------------------------------------------------------------------------------------------------ maxon::Result<GvNode*> GetRedshiftNodeBranch(BaseMaterial* material) { iferr_scope; // Bail when #material is null or not a Redshift material. if (!material || (material->GetType() != g_redshift_material_id)) return maxon::IllegalArgumentError( MAXON_SOURCE_LOCATION, "Passed material is not a Redshift material."_s); // Setup an array for all branches of #material. Usually nodes do not have more than ten // branches, e.g., a BaseObject might have a tags and tracks branch, and one or two internally // used branches. So, setting maxBranchCount to 64 is very generous. const maxon::Int32 maxBranchCount = 64; BranchInfo branches[maxBranchCount]; // Get all branches for #material. maxon::Int32 count = material->GetBranchInfo(branches, maxBranchCount, GETBRANCHINFO::NONE); // Search for a "Node" branch, which is the branch containing the Redshift graph. BranchInfo* nodeBranch = nullptr; for (maxon::Int32 i = 0; i < count; i++) { if (branches[i].name.Compare("Node"_s) == maxon::COMPARERESULT::EQUAL) { nodeBranch = &branches[i]; } } if (!nodeBranch) return maxon::UnexpectedError( MAXON_SOURCE_LOCATION, "The passed material does not contain a Redshift 'Node' branch."_s); GeListHead* head = nodeBranch->head; if ((!head) || (!head->GetFirst())) return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Malformed or empty branch info."_s); // Get the first node in the list head, this is equivalent to nodeMaster->GetRoot(). We cannot // access the node master itself with this approach, but all the nodes in it. GvNode* node = static_cast<GvNode*>(head->GetFirst()); return node; } /// Prints #node and all its descendants as a tree to the console. /// /// @param node The graph node to print. /// @param level (optional) The indentation level. /// ------------------------------------------------------------------------------------------------ static void PrintNodes(GvNode* node, const maxon::Int level = 0) { if (!node) return; maxon::String indentation = ""_s; for (maxon::Int i = 0; i < level; i++) indentation += "\t"_s; while (node) { maxon::Int location = reinterpret_cast<maxon::Int>(node); ApplicationOutput("@ @ at 0x@{x}", indentation, node->GetTitle(), location); PrintNodes(node->GetDown(), level + 1); node = node->GetNext(); } } /// Runs the two examples. static maxon::Result<void> pc13752(BaseDocument* doc) { iferr_scope_handler { ApplicationOutput("@: @", err.GetClass().GetId(), err.GetMessage()); return err; }; // The Python code we are going to run with CallRedshiftPythonLayer. const maxon::String pythonCode = ( "import c4d\n"_s + "import redshift\n\n"_s + "def GetNodeMaster(\n"_s + " material: c4d.BaseMaterial) -> c4d.modules.graphview.GvNodeMaster:\n"_s + " '''Attempts to return the Redshift GvNodeMaster for #material.\n"_s + " '''\n"_s + " if material.GetType() != redshift.Mrsmaterial:\n"_s + " raise ArgumentError(f'Unexpected type with {material = }.')\n\n"_s + " master = redshift.GetRSMaterialNodeMaster(material)\n"_s + " if master is None:\n\n"_s + " raise RuntimeError(f'Could not access node master for {material}.')\n\n"_s + " return master\n\n\n"_s); // Get the active material for which we will attempt to get the RS node master for. BaseMaterial* activeMaterial = doc->GetActiveMaterial(); if (!activeMaterial) return maxon::IllegalStateError(MAXON_SOURCE_LOCATION, "No material selected"_s); // Run the example using Python to get hold of the nodes in the material. ApplicationOutput("\nAccess with Python layer:"); GvNodeMaster* nodeMaster = CallRedshiftPythonLayer(activeMaterial, pythonCode) iferr_return; if (!nodeMaster) return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "CallRedshiftPythonLayer() failed."_s); PrintNodes(nodeMaster->GetRoot()); // Run the example using branching information to get hold of the nodes in the material. ApplicationOutput("\nAccess with branching information:"); GvNode* nodeRoot = GetRedshiftNodeBranch(activeMaterial) iferr_return; if (!nodeRoot) return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "GetRedshiftNodeBranch() failed."_s); PrintNodes(nodeRoot); return maxon::OK; }
    • A

      ZipFile::CopyInFileInZip() file names

      Cinema 4D SDK
      • c++ r21 • • AiMiDi
      2
      0
      Votes
      2
      Posts
      422
      Views

      M

      Hi @AiMiDi you are using the old ZipFile implementation, instead, you should use the new one from the MAXON API as explained in Archives Manual.

      And here an example of how to copy a file.

      #include "maxon/ioarchivehandler.h" Bool res = false; Filename importFilename; res = importFilename.FileSelect(FILESELECTTYPE::ANYTHING, FILESELECT::LOAD, "Load"_s); if (!res) return maxon::UnknownError(MAXON_SOURCE_LOCATION); maxon::Url importUrl = MaxonConvert(importFilename, MAXONCONVERTMODE::READ); Filename zipFilename; res = zipFilename.FileSelect(FILESELECTTYPE::ANYTHING, FILESELECT::SAVE, "Save"_s, "zip"_s); if (!res) return maxon::UnknownError(MAXON_SOURCE_LOCATION); const maxon::WriteArchiveRef zipArchive = maxon::WriteArchiveClasses::Zip().Create() iferr_return; zipArchive.Open(MaxonConvert(zipFilename, MAXONCONVERTMODE::WRITE), false) iferr_return; zipArchive.SetCompressionLevel(2) iferr_return; zipArchive.CopyFile(importUrl, importUrl.GetName()) iferr_return; zipArchive.Close() iferr_return;

      This new Maxon API support correctly non ASCII character.
      Cheers,
      Maxime.

    • A

      How do dynamical systems update objects?

      Cinema 4D SDK
      • c++ r21 • • AiMiDi
      6
      0
      Votes
      6
      Posts
      915
      Views

      ManuelM

      Hi,

      the scenehook is called from the CoreThread, that's why you must be sure that your sceneHook is 100% safe and not blocking/crashing.

      I can't talk about our internal implementation. But, to save some time, you could disable the dynamics on the project settings, and keep using our tags to retrieve the data, parameters to drive the simulation.

      Cheers,
      Manuel

    • A

      How to get VolumeData?

      Cinema 4D SDK
      • r21 c++ • • AiMiDi
      5
      0
      Votes
      5
      Posts
      676
      Views

      ferdinandF

      Hey @aimidi,

      I need further optimization, and I need to check whether the scene (tags, objects, et cetera) has been changed. Does BaseDocument.Polygonize()copy the Dirty and HDirty of the object?

      This why I hinted at Polygonize() not always being up to the task. There is unfortunately no easy way to get informed about specific classic API scene graph changes. There is the broad core message EVMSG_CHANGE which is sent by EventAdd() and will inform you that 'something' changed but exactly not what did change. Which makes it quite a bit of work to synchronize two scene graphs in a performant way, the classic API one from Cinema 4D and one from an external render engine, as you then have to determine the change yourself. And if it is a relevant one, as you might not care about all scene elements of the Cinema 4D scene graph in the render engine scene graph.

      One useful pattern to use in this context are GeMarker and the MAXON_CREATOR unique ID attached to nodes. This is because scene elements get reallocated quite often in the Cinema 4D scene graph, so you cannot simply have an object 'MyCube' in your renderer scene graph which holds a BaseObject pointer to its Cinema 4D scene counter part for synchronization purposes.

      And to answer your question about dirty flags: No, Polygonize() will copy objects and transform generators into discrete geometry. They cannot share dirty flags (see example at the end).

      Cheers,
      Ferdinand

      import c4d def main(): """ """ docFirst = doc.GetFirstObject() print (f"{docFirst.GetHDirty(c4d.HDIRTYFLAGS_ALL)=}") temp = doc.Polygonize() tempFirst = temp.GetFirstObject() print (f"{tempFirst.GetHDirty(c4d.HDIRTYFLAGS_ALL)=}") if __name__=='__main__': main() docFirst.GetHDirty(c4d.HDIRTYFLAGS_ALL)=5 tempFirst.GetHDirty(c4d.HDIRTYFLAGS_ALL)=3
    • A

      Is it possible to control Camera with keyboard WASD like a FPS game?

      Cinema 4D SDK
      • • • AiMiDi
      8
      1
      Votes
      8
      Posts
      3.0k
      Views

      A

      @ferdinand Wow,thank you so much ferdinand!

    • A

      DRAGANDDROP_FLAG_FORBID

      Cinema 4D SDK
      • c++ r21 • • AiMiDi
      2
      0
      Votes
      2
      Posts
      365
      Views

      M

      Hi @AiMiDi, MSG_DRAGANDROP will be received when the host receive a drag, I didn't tried but I guess only dialog can react to this message. In you case you will need to set the NBIT NBIT_NO_DD like so op.ChangeNBit(c4d.NBIT_NO_DD, c4d.NBITCONTROL_SET).

      Cheers,
      Maxime.

    • A

      How to set the shadow for polygon drawing

      Cinema 4D SDK
      • c++ r21 • • AiMiDi
      3
      0
      Votes
      3
      Posts
      573
      Views

      ferdinandF

      Hello @aimidi,

      thank you for reaching out to us. Please excuse the delay, but your questions had to be reassigned internally, which is why I only took it on yesterday.

      The main reason for your problems is the incomplete initialization of Vector Box_vc[4] = {Vector(0)};. Box_vc should be initialized with four vectors as you do declare yourself. This partial initialization causes the shading errors you did experience.

      Please also note that you did flag your posting as R21, which is out of the support cycle. Below I do provide some example code written for S24. It should work on R21, since there have been no changes to the involved interfaces and methods as far as I am aware of, but I did not try to compile the code for R21. The code is also narrative in nature and lines out a few other problems with the code provided by you. I hope this will be helpful to you.

      Cheers,
      Ferdinand

      The result:
      alt text

      The code:

      // Example for some drawing operations via ObjectData::Draw(). // // Draws a white polygon and a red cube in the local coordinate system of the BaseObject. The main // problem of your code was that you did not initialize the color array properly. // // As discussed in: // https://developers.maxon.net/forum/topic/13441 #include "c4d.h" #include "c4d_symbols.h" class Pc13441 : public ObjectData { INSTANCEOF(Pc13441, ObjectData) public: static NodeData* Alloc() { return NewObjClear(Pc13441); } DRAWRESULT Draw(BaseObject* op, DRAWPASS drawpass, BaseDraw* bd, BaseDrawHelp* bh) { if (drawpass == DRAWPASS::OBJECT) { if (op == nullptr || bd == nullptr) { return DRAWRESULT::FAILURE; } // Set the drawing matrix to the global matrix of the object we are drawing for. This is // more intuitive than what you did, because we can now just draw as if the origin of // our object is the origin of the drawing space and also will respect the orientation // of the that object. Or in short: We will draw in the local matrix of op. bd->SetMatrix_Matrix(nullptr, op->GetMg()); // The replacement for the drawing size parameter(s) of your example. Vector poylgonSize = Vector(100); // We are now going to draw a manually shaded polygon. This will ignore shading defined // by the user/scene. The user turning on and off shadows in the viewport or moving // lights will have no impact on this. // Define the vertices of the polygon to draw. Vector vertices[4] = { Vector(+poylgonSize.x, +poylgonSize.y, 0), Vector(-poylgonSize.x, +poylgonSize.y, 0), Vector(-poylgonSize.x, -poylgonSize.y, 0), Vector(+poylgonSize.x, -poylgonSize.y, 0) }; // Define the color for each vertex to draw, Cinema will interpolate between these // colors. Vector colors[4] = { Vector(1), Vector(1), Vector(1), Vector(1) }; // You did not initialize this array properly and also did pass in black as a color. // So, this, // // Vector colors[4] = { Vector(0) }; // // where we only initialize the 0th element, is not equivalent to that: // // Vector colors[4] = { Vector(0), Vector(0), Vector(0), Vector(0) }; // // The first one will give you the weird shading errors you had while the latter will // give you an all black polygon. Black as a color is of course not that useful if we // want to see some shading. // If our polygon is a quad or triangle. bool isQuad = true; // Set the drawing transparency to about 50%. bd->SetTransparency(127); // Draw the polygon. bd->DrawPolygon(vertices, colors, isQuad); // Now we are going to draw a box. // The size of the box. float boxSize = 50; // A transform for the box, we are going to draw it a bit off-center, so that it does // not overlap with the polygon we did draw. Matrix boxTransform = Matrix(); boxTransform.off = Vector(200, 0, 0); // The color for the box, this can only be a solid color. Vector boxColor = Vector(1, 0, 0); // And if we want to draw it as a wire frame. bool isWireFrame = false; // Draw the box. bd->DrawBox(boxTransform, boxSize, boxColor, isWireFrame); // For more complex drawing operations one could use DrawPoly() to draw a single // polygon, while respecting scene shading, or DrawPolygonObject() to draw a BaseObject // that is a PolygonObject. Which will also respect user defined scene shading by // default. I would not recommend constructing such polygon object to draw in Draw() as // this method is rather performance critical. If possible, this should be done // beforehand, i.e., in a cached manner. } return SUPER::Draw(op, drawpass, bd, bh); } };
    • A

      How to update the scene in a threaded

      Cinema 4D SDK
      • r21 c++ • • AiMiDi
      4
      0
      Votes
      4
      Posts
      608
      Views

      ferdinandF

      Hello @AiMiDi,

      it would really help if you could clarify/confirm the context. I am still assuming that you are writing a SceneLoaderData, namely for your MikuMikuDance project.

      We have an example on GitHub for how to implement an im- and exporter plugin, the STL-example. Invoking the status bar indicators, i.e., StatusSetSpin. StatusSetBar and StatusSetText, for a computational process, e.g., loading a file, are a bit of a special case regarding the UI-restrictions. I always also struggle with them and what is allowed where. In general, you can get away with a bit more with them, as they do not check immediately for being on the main thread and do not refuse execution if they are not. But you can also run into problems with them outside of the main thread. With that being said, our STL example also makes use of StatusSetBar, which sort of indicates that it is safe to use within SceneLoaderData::Load (more on that later).

      Regarding providing a custom progress dialogs for the import process: Most importers of Cinema do not do that. There are exceptions like the FBX importer which spawns its own dialog to give feedback for the importing process.

      When you invoke GeIsMainThread() in SceneLoaderData::Load, you will see that you are in almost all cases in the main thread (at least the ones where I did try). But in the end, there is no guarantee that this will always hold true. What I would do is safeguard all GUI operations with GeIsMainThread(), including status-bar operations, i.e., only carry them out when Load is being called from the main thread and otherwise just skip them. Then you can also open as many dialogs as you want from there.

      Our FBX importer plugin for example instantiates an interface for handling importing FBX within Load. Cascading out from that interfac instantiation, also a ProgressDialog will be being instantiated and opened (there are some types involved in between). But frankly, I would not bother with all that complexity and just use the status bar of the main app, i.e., StatusSetBar. Checking for the main thread seems advisable though.

      Cheers,
      Ferdinand

    • A

      How to use UV morph node

      Cinema 4D SDK
      • r21 c++ • • AiMiDi
      5
      0
      Votes
      5
      Posts
      660
      Views

      A

      Hi,
      Thank you so much for your suggestion. I changed my code to iterate each surface. It works very well. Thank you for your help, which solved my problem.
      6ecf2fc3-56cc-4834-9632-0b815d3a5f39-image.png
      Thanks,
      Manuel

    • A

      Unable to compile math.framework

      Cinema 4D SDK
      • • • AiMiDi
      2
      0
      Votes
      2
      Posts
      422
      Views

      ManuelM

      hi,

      have a look at this thread please.

      Cheers,
      Manuel

    • A

      CKey Auto Tangents

      Cinema 4D SDK
      • c++ r23 s24 • • AiMiDi
      7
      0
      Votes
      7
      Posts
      1.3k
      Views

      M

      Hi @AiMiDi with the latest update of Cinema 4D (R24 SP1), CKey::GetValueLeft/Right and CKey::GetTimeLeft/Right documentation have been improved to mention what was described by @ferdinand.

      Cheers,
      Maxime.

    • A

      How to create a left-aligned group in the menu.

      Cinema 4D SDK
      • r21 c++ • • AiMiDi
      8
      0
      Votes
      8
      Posts
      1.1k
      Views

      ferdinandF

      Hello @AiMiDi,

      without any further questions, we will consider this topic as solved by Monday and flag it accordingly.

      Thank you for your understanding,
      Ferdinand

    • A

      ObjectData::Draw

      Cinema 4D SDK
      • c++ r21 • • AiMiDi
      3
      0
      Votes
      3
      Posts
      435
      Views

      A

      Thanks for your answer, it solved my problem.

      Thank,
      AiMiDi

    • A

      Limit SplineKnot tangent

      Cinema 4D SDK
      • r21 c++ • • AiMiDi
      3
      0
      Votes
      3
      Posts
      645
      Views

      A

      Thanks for your answer, it solved my problem.
      I used: Vector::ClampMax() and Vector::ClampMin() to limit the tangent to the value I want.

      Thank,
      AiMiDi

    • A

      Importing morph problem

      Cinema 4D SDK
      • r21 • • AiMiDi
      12
      0
      Votes
      12
      Posts
      1.4k
      Views

      ferdinandF

      Hello @AiMiDi,

      without any further feedback or questions, we will consider this topic as solved by Wednesday and flag it accordingly.

      Thank you for your understanding,
      Ferdinand