Ok now I get it I was missing that you do not load the lib yourself. I will have a look at it Monday.
Cheers,
Maxime.
Ok now I get it I was missing that you do not load the lib yourself. I will have a look at it Monday.
Cheers,
Maxime.
Call this function just before Loading the DLL and it should work.
Hi @ikolev is there any reason why you prefer to link directly to the python311.dll instead of using the python.framework? Cause this framework serves as an abstraction layer of the python dll/API so it's version independent and also adds Maxon API memory management so you do not have to care anymore about DecRef/IncRef python objects.
With that's said you can call DLLInterface.AddDllPath and then load the dll.
Cheers,
Maxime.
Hi @justinleduc with c4dpy this is pretty straightforward. Run it once to get a license assigned to it, then once it's licensed run the next commands:
c4dpy -m ensurepip
to install pip and then c4dpy -m pip install numpy
.
Then you can start Cinema 4D and you will be able to use numpy within Cinema 4D.
Final note as said in Python Libraries Manual.
MAXON cannot provide support for third party libraries written for CPython and their compatibility with the Python interpreters shipped with Cinema 4D. While our Python interpreters are derived from the respective CPython builds, they are not identical to them. Popular Python libraries as for example numpy might not work at all or only with a reduced feature set. The Cinema 4D Python interpreters are provided as-is and not meant to be fully CPython compliant.
Cheers,
Maxime.
Thanks, here the link just in case you really need it https://developers.maxon.net/docs/py/2024_0_0/manuals/manual_maxon_api.html.
Cheers,
Maxime.
Hi @filipst just to let you know that with the hotfix released yesterday(20/09/2023). The issue within Cinema 4D has been solved.
In the same time make sure to use the latest version of the add-on in VsCode (v1.1.0).
Cheers,
Maxime.
Cause node is const, so the C-style cast is performing a const_cast.
Hi @BineeMan sorry for the late reply I was pretty busy Friday and thanks @Dunhou for demonstrating how Packed
Auto UV work.
For Cubic and Angle you need to use c4d.modules.bodypaint.CallUVComman as demonstrated in the call_uv_command_ example.
So for the Angle
automatic UV you need to use the command ID c4d.UVCOMMAND_OPTIMALMAPPING
with the next parameters:
And for the Cubic
automatic UV you need to use the command ID c4d.UVCOMMAND_OPTIMALCUBICMAPPING
with the next parameters:
Cheers,
Maxime.
Hi @danielsian, I we need to update this example and also the documentation about Gradient. Thanks for bringing that to our attention.
Gradient handling is now different and heavily rely on the maxon API via the GradientInterface. The old c4d.Gradient
is now just a mirror of the GradientInterface
.
Therefor all constants were moved to the maxon API. Find bellow the adapted example:
import c4d
import maxon
def main():
# Create a new material and a new gradient shader.
material = c4d.BaseMaterial(c4d.Mmaterial)
shader = c4d.BaseShader(c4d.Xgradient)
# Get a copy of the c4d.Gradient data referenced by the gradient shader
# and a copy of the knot data stored in the c4d.Gradient data. The knot
# data is expressed as a c4d.BaseCoontainer.
gradient = shader[c4d.SLA_GRADIENT_GRADIENT]
knotData = gradient.GetData(c4d.GRADIENT_KNOT)
# Iterate over all knots in the knot data container and set their
# interpolations to linear. Each knot in the knot data container is itself
# a c4d.BaseContainer, storing the data for a single knot.
for _, knot in knotData:
knot[c4d.GRADIENTKNOT_INTERPOLATION] = maxon.GRADIENT_INTERPOLATION_TYPE.SMOOTHKNOT
# Items can also be retrieved by their index from a BaseContainer, here
# the first index, i.e., second item, in the container. In this case the
# position of the knot is written and then the knot container is being
# written back into the index its has been retrieved from.
knot = knotData.GetIndexData(1)
knot[c4d.GRADIENTKNOT_POSITION] = 0.5
knotData.SetIndexData(1, knot)
# After these modifications the knots must be written back into the
# c4d.Gradient data, because GetData() returned a copy above. This also
# does apply to the shader and its gradient for the same reason.
gradient.SetData(c4d.GRADIENT_KNOT, knotData)
shader[c4d.SLA_GRADIENT_GRADIENT] = gradient
# Aside from assigning the shader to the correct parameter of the
# material, it is also important to call BaseList2D.InsertShader(), as
# otherwise the setup will not work.
material.InsertShader(shader)
material[c4d.MATERIAL_COLOR_SHADER] = shader
# Finally, the material is being inserted into the active document and an
# update event is being pushed event to Cinema 4D, so that its GUI can
# catch up to our changes. 'doc' is a predefined module attribute in
# script manger scripts which points to the active document. It is
# equivalent to c4d.documents.GetActiveDocument().
doc.InsertMaterial(material)
c4d.EventAdd()
if __name__=='__main__':
main()
For other values please take a look at the C++ documentation of GRADIENT_INTERPOLATION_TYPE.
Cheers,
Maxime.
Hi just to let you know that we have a fix, and it will be available in the next version :) I'm going to update this topic once it's public.
Cheers,
Maxime.
Hi @filipst, I've published a new version of the Visual Studio Connector, it is available here and should be soon available via the VS Code marketplace.
However while investigating the issue, I found out another one which was added very lately and is more critical since it prevent Cinema 4D to send data to the Vs Code extension. Sadly this will require a fix on Cinema 4D side. I'm trying to have this fix for Cinema 4D as soon as possible out.
Cheers,
Maxime.
Hi the plugin is going to be updated today.
Cheers,
Maxime.
Hi @Easan this function have been modified in C++ and we did not had the time to adapt it in Python. However rest assured this is in our top priority for the next release.
With that said you can still retrieve the value using GetValue("value"). Find bellow and example, but first create a node material and select a port and run the example.
import c4d
import maxon
def main():
mat = doc.GetActiveMaterial()
nodeMaterial = mat.GetNodeMaterialReference()
nodespaceId = c4d.GetActiveNodeSpaceId()
nimbusRef = mat.GetNimbusRef(nodespaceId)
graph = nimbusRef.GetGraph()
for port in maxon.GraphModelHelper.GetSelectedNodes(graph, maxon.NODE_KIND.PORT_MASK):
print(port.GetValue("value"))
if __name__ == "__main__":
main()
However this have some limitation since in some case when no value is available None will be returned. This is going to be fixed in the next version.
Cheers,
Maxime.
Hi unfortually the possible way is to render the scene.
Within Cinema 4D you can call
rd = doc.GetActiveRenderData()
rd.Message(c4d.MSG_DESCRIPTION_COMMAND, {"id": c4d.RDATA_PROJECTFILESAVE})
Which will simulate the click on the Save Project File... but this will open a File Selector dialog that you can't script.
So the only way is to render the scene via c4d.documents.RenderDocument.
Cheers,
Maxime.
Sadly it is not possible to retrieve the selected parameters.
Cheers,
Maxime.
Hi @freeter_e not all commands are registered by the CallCommand, only a few of them this is due to the architecture of Cinema 4D itself, and nothing that is trivial to fix.
With that's said we provide specif API for many elements in Cinema 4D. So find bellow an example on how to use the KeyFrame Selection.
# Create a Cube and Select it before running this script.
import c4d
def GetParametersIdKeyFrameSelection(obj: c4d.C4DAtom) -> list[c4d.DescID]:
""" Build a list of c4d.DescId which are part of the KeyFrameSelection of the given obj"""
paramIds: list[c4d.DescID] = []
if not obj.KeyframeSelectionContent():
return paramIds
# Iterate over the description of #op.
for data, did, _ in op.GetDescription(c4d.DESCFLAGS_DESC_NONE):
# Step over all parameters with the empty string as the label because doing that makes
# semantically sense in this case (technically not necessary).
if data[c4d.DESC_NAME] == "":
continue
if (obj.FindKeyframeSelection(did)):
paramIds.append(did)
continue
# Get another instance of the description for #op and then try loading the sub-description
# for #did into it.
subdesc: c4d.Description = op.GetDescription(c4d.DESCFLAGS_DESC_NONE)
if not subdesc.GetSubDescriptionWithData(did, [op], c4d.BaseContainer(), None):
continue
# Iterate over the sub-description/sub-channels
for sdata, sdid, _ in subdesc:
fullDescId: c4d.DescID = c4d.DescID(did[0])
fullDescId.PushId(sdid[0])
if (obj.FindKeyframeSelection(fullDescId)):
paramIds.append(fullDescId)
return paramIds
def GetDescIdFromList(listIds: list[int]) -> c4d.DescID:
""" Build a c4d.DescId from a list of integer"""
descId: c4d.DescID = c4d.DescID()
for item in listIds:
descId.PushId(c4d.DescLevel(item))
return descId
def main() -> None:
obj = doc.GetActiveObject()
if not obj:
raise RuntimeError("One object should be selected.")
# Remove all keyframe selection
obj.ClearKeyframeSelection()
# Print all existing KeyFrame selection. Should be empty since we cleaned the keyframe selection.
print(f"KeyFrameSelection 1: {GetParametersIdKeyFrameSelection(obj)}")
# Create keyFrame Selection
obj.SetKeyframeSelection(c4d.PRIM_CUBE_SUBY, True)
obj.SetKeyframeSelection(GetDescIdFromList([c4d.PRIM_CUBE_LEN,c4d.VECTOR_Y]), True)
# Print all existing KeyFrame selection
print(f"KeyFrameSelection 2: {GetParametersIdKeyFrameSelection(obj)}")
# Remove the keyFrame selection of the Cube Size Y
obj.SetKeyframeSelection(GetDescIdFromList([c4d.PRIM_CUBE_LEN,c4d.VECTOR_Y]), False)
# Print all existing KeyFrame selection
print(f"KeyFrameSelection 3: {GetParametersIdKeyFrameSelection(obj)}")
c4d.EventAdd()
if __name__ == '__main__':
main()
Regarding you second request, this is not possible to control the Asset Browser Dialog.
If you have any other question, please let me know. But in the future try to open a topic for each question (in this case one for the KeyFrameSelection and another one for the Asset Browser).
Cheers,
Maxime.
Hi @Dunhou, we can't disclose any information about our partner and I'm doubtful that Megascan developer are reading this forum but who know.
Cheers,
Maxime.
Hi @peteclear, my recommendation would be to install Cinema 4D again on another directory, to be sure you did not forget any reference to Vray. Think about environment variable that may force cinema 4d to load Vray from some other locations.
If this does not solve you can run the same command with the python interpreter located in {C4D_INSTALLATION_PATH}\resource\modules\python\libs\python37.win64.framework
. Make sure to have Cinema 4D closed when you run it, once done you should be able to start Cinema 4D and use your 3rd party library.
Final note, R23 is out of support scope and installing pip packing with the second method I mention is not recommended since this is untested, so some part of the installed modules may not work.
Cheers,
Maxime.
Hi @JohnTerenece, after searching few hours, it appear that this is not possible to customize the Tag plugin, neither in R21 or in newer version. So the only solution would be to separate your project.
Cheers,
Maxime.
Hi sadly the only way is to use c4d.CallCommand(1055347) which does not open the window but directly execute the command.
You can change the setting as you could in the windows, by editing the value in the BaseContainer of the document.
Finally it act on the selected polygon, so the best way would be to select them.
Find bellow a script that you can execute in the script manager that will select all polygon of an object and run the command.
from typing import Optional
import c4d
doc: c4d.documents.BaseDocument # The active document
op: Optional[c4d.BaseObject] # The active object, None if unselected
def main() -> None:
# Enables UV Polygon Mode if not already in any UV mode (needed for GetActiveUVSet to works)
if doc.GetMode() not in [c4d.Muvpoints, c4d.Muvpolygons]:
doc.SetMode(c4d.Muvpolygons)
# Retrieves active UVSet, The UV windows need to be opened at least one time
handle = c4d.modules.bodypaint.GetActiveUVSet(doc, c4d.GETACTIVEUVSET_ALL)
if handle is None:
# If fail it may be because the Texture view is not open
# Open A texture View
c4d.CallCommand(170103)
# In S22 you need to update the UV Mesh
if c4d.API_VERSION >= 22000:
c4d.modules.bodypaint.UpdateMeshUV(False)
# Retrieves active UVSet, The UV windows need to be opened at least one time
handle = c4d.modules.bodypaint.GetActiveUVSet(doc, c4d.GETACTIVEUVSET_ALL)
if handle is None:
raise RuntimeError("There is no Active UVSet")
# Select the polygon you want to operate on
currentSel = op.GetPolygonS()
previousSel = currentSel.GetClone() # Will be used to restore the initial selection state at the end
currentSel.SelectAll(op.GetPolygonCount() - 1 )
# Update the UV mesh to properly take into account the new selection
c4d.modules.bodypaint.UpdateMeshUV(True)
# Define the settings
settings = doc.GetSettingsInstance(c4d.DOCUMENTSETTINGS_MODELING)
settings[c4d.MDATA_UVRECTANGULARIZE_ALIGN] = True
settings[c4d.MDATA_UVRECTANGULARIZE_EQUIDISTANT] = False
# Execute the command
c4d.CallCommand(1055347)
# Restore the initial selection
previousSel.CopyTo(currentSel)
c4d.modules.bodypaint.UpdateMeshUV(True)
c4d.EventAdd()
if __name__ == '__main__':
main()
Cheers,
Maxime.