-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cinema 4D Cast Support #60
base: master
Are you sure you want to change the base?
Conversation
Cast Model support for Cinema 4D. Supports: - Meshes - Skeleton - Materials - Vertex colors - Constraints - IK handles Installation: Create a folder with the name "Cast", "io_scene_cast" or any other suitable name and place the files in there. The cast python library (cast.py) should be placed in the resource folder named "res" in the plugins directory. Requirements: Cinema 4D 2024 or newer Currently not supported: - Animations - Instances - Blend shapes
Will review this weekend |
Might add instances if I find time tomorrow |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some comments, nothing extremely major, just code cleanup and using enums instead of raw values for clarity.
plugins/cinema4d/import_cast.pyp
Outdated
from c4d import plugins, bitmaps, Vector, gui, BaseObject | ||
import mxutils | ||
|
||
resDir = os.path.join(os.path.dirname(__file__), "res") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer global constants as all caps:
RESOURCE_DIR
plugins/cinema4d/import_cast.pyp
Outdated
skinningMethod = mesh.SkinningMethod() | ||
skinType = c4d.ID_CA_SKIN_OBJECT_TYPE | ||
if skinningMethod == "linear": | ||
skinObj[skinType] = 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the enum value:
Linear (c4d.ID_CA_SKIN_OBJECT_TYPE_LINEAR)
Spherical (c4d.ID_CA_SKIN_OBJECT_TYPE_QUAT)
Blended (c4d.ID_CA_SKIN_OBJECT_TYPE_BLENDED)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some of the enum stuff came through just looking into Cinema 4Ds Script log and console.
Where it just spits out the index as an example from the script log object()[c4d.ID_BASEOBJECT_ROTATION_ORDER] = 5
, same was for bools and I just took that with me without thinking too much about it.
Will change it for sure!
plugins/cinema4d/import_cast.pyp
Outdated
if skinningMethod == "linear": | ||
skinObj[skinType] = 0 | ||
elif skinningMethod == "quaternion": | ||
skinObj[skinType] = 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above.
plugins/cinema4d/import_cast.pyp
Outdated
if model.Skeleton() is not None and node[CAST_IMPORT_BIND_SKIN]: | ||
skinObj = BaseObject(c4d.Oskin) | ||
skinningMethod = mesh.SkinningMethod() | ||
skinType = c4d.ID_CA_SKIN_OBJECT_TYPE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably fine to just inline this, no need for an extra line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not quiet sure what you mean.
Like that?
- skinType = c4d.ID_CA_SKIN_OBJECT_TYPE
if skinningMethod == "linear":
- skinObj[skinType] = 0
+ skinObj[c4d.ID_CA_SKIN_OBJECT_TYPE] = c4d.ID_CA_SKIN_OBJECT_TYPE_LINEAR
elif skinningMethod == "quaternion":
- skinObj[skinType] = 1
+ skinObj[c4d.ID_CA_SKIN_OBJECT_TYPE] = c4d.ID_CA_SKIN_OBJECT_TYPE_QUAT
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep!
handles[i] = newBone | ||
boneIndexes[bone.Hash() or i] = newBone | ||
|
||
if bone.ParentIndex() > -1: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The bone order is not guaranteed that the parents come first. You should enumerate twice in order to set the parents after all bones are created to prevent issues.
plugins/cinema4d/import_cast.pyp
Outdated
material = materialArray[meshMaterial.Name()] | ||
material_tag = newMesh.MakeTag(c4d.Ttexture) | ||
material_tag[c4d.TEXTURETAG_MATERIAL] = material | ||
material_tag[c4d.TEXTURETAG_PROJECTION] = 6 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the enum value:
Spherical (c4d.TEXTURETAG_PROJECTION_SPHERICAL)
Cylindrical (c4d.TEXTURETAG_PROJECTION_CYLINDRICAL)
Flat (c4d.TEXTURETAG_PROJECTION_FLAT)
Cubic (c4d.TEXTURETAG_PROJECTION_CUBIC)
Frontal (c4d.TEXTURETAG_PROJECTION_FRONTAL)
Spatial (c4d.TEXTURETAG_PROJECTION_SPATIAL)
UVW Mapping (c4d.TEXTURETAG_PROJECTION_UVW)
Shrink Wrapping (c4d.TEXTURETAG_PROJECTION_SHRINKWRAP)
Camera Mapping (c4d.TEXTURETAG_PROJECTION_CAMERAMAP)
normalList.insert((i + 1) * 4 - 1, Vector(0, 0, 0)) | ||
|
||
# Maps data from float to int16 value | ||
normalListToSet = [int(component * 32000.0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't seem like the right logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain this further? Got the idea from an older official python example:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! I would include the snippet in the first link as a comment to explain this, because the 32000 is extremely arbitrary! It explains it better than 'float to int16'.
We are using Blender unpack_list for convinience | ||
https://github.com/blender/blender/blob/main/scripts/modules/bpy_extras/io_utils.py#L370 | ||
""" | ||
def unpack_list(list_of_tuples): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In most instances I've seen you use this, it's completely unnecessary where you can just iterate over the pairs directly instead of slicing an unpacked list.
You should be able to remove those steps.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the beginning I tried to replicate the Blender addon as much as possible to understand the whole process better, that's how this happened. Will remove them!
Thanks for all the feedback, helps a lot! I'll get on it tomorrow. |
- usage of enums - added comments - usage of bools - parenting bones in an extra loop - minor cleanup Missing: - pole dtzxporter#60 (comment) - usage of c4d.quaternion() dtzxporter#60 (comment) - removing unpack_list steps dtzxporter#60 (comment)
@Devostated am I waiting for more or is this ready for review again? |
"Pole vector object" should be pole vector bone. "Pole vector twist" should be keyed to the rotation x value of pole vector. |
New Feature: - Instance import Improvements: - using c4d.Quaternion() - using PoleVectorBone instead of PoleBone Requirements: - Cinema 4D 2024.5 or newer
Animation support will come in around October, when my uni projects are done. In the mean time please let me know if you see anything that would need improving. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a few minor nits, nothing major. Coming along nicely!
@@ -173,6 +177,7 @@ def importModelNode(doc, node, model, path): | |||
for i in range(mesh.UVLayerCount()): | |||
uvTag = c4d.UVWTag(facesCount) | |||
uvBuffer = mesh.VertexUVLayerBuffer(0) | |||
|
|||
uvUnpacked = unpack_list([(uvBuffer[x * 2], 1.0 - uvBuffer[(x * 2) + 1]) for x in faces]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we get rid of unpack_list? It should not be needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
poleBone = handle.PoleBone() | ||
if poleBone is not None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if handle.PoleBone():
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It definitely makes more sense, I just tried to copy your plugins to have it easier to maintain. Your Blender and Maya addon use the
poleBone = handle.PoleBone()
if poleBone is not None:
but I'll remove it!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it looks a bit better with the poleBone.Name() in the warning in my opinion.
modelNull = importModelNode(doc, node, child, instancePath) | ||
modelNull.InsertUnder(sceneNull) | ||
except: | ||
print("Failed to import instance: %s" % instancePath) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a better way to notify the user? Does this popup anywhere? See Maya/Blender reporting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will print in the Cinema 4D console. I could make a popup saying that something went wrong and to check console for the detailed list of what models failed.
@@ -173,6 +177,7 @@ def importModelNode(doc, node, model, path): | |||
for i in range(mesh.UVLayerCount()): | |||
uvTag = c4d.UVWTag(facesCount) | |||
uvBuffer = mesh.VertexUVLayerBuffer(0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just noticed you're not importing the correct layer, you're using layer 0 for every UV layer atm!
mesh.VertexUVLayerBuffer(i)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yeah, that's something I forgot to change for testing.
Cinema 4Ds materials always use the latest added UVW tag, so some models I tested had wrong UVs then and I couldn't work on materials.
Thanks for letting me know!
- usage of enums - added comments - usage of bools - parenting bones in an extra loop - minor cleanup Missing: - pole #60 (comment) - usage of c4d.quaternion() #60 (comment) - removing unpack_list steps #60 (comment)
Cast Model support for Cinema 4D.
Supports:
Installation:
Create a folder with the name "Cast", "io_scene_cast" or any other suitable name and place the files in there. The cast python library (cast.py) should be placed in the resource folder named "res" in the plugins directory.
Requirements:
Cinema 4D 2024 or newer
Currently not supported: