Skip to content
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

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open

Conversation

Devostated
Copy link
Contributor

Cast Model support for Cinema 4D.

Supports:

  • Meshes
  • Skeleton
  • Materials
  • UVs
  • Weights
  • 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

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
@dtzxporter
Copy link
Owner

Will review this weekend

@Devostated
Copy link
Contributor Author

Might add instances if I find time tomorrow

Copy link
Owner

@dtzxporter dtzxporter left a 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 Show resolved Hide resolved
from c4d import plugins, bitmaps, Vector, gui, BaseObject
import mxutils

resDir = os.path.join(os.path.dirname(__file__), "res")
Copy link
Owner

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

skinningMethod = mesh.SkinningMethod()
skinType = c4d.ID_CA_SKIN_OBJECT_TYPE
if skinningMethod == "linear":
skinObj[skinType] = 0
Copy link
Owner

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)

Copy link
Contributor Author

@Devostated Devostated Apr 12, 2024

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!

if skinningMethod == "linear":
skinObj[skinType] = 0
elif skinningMethod == "quaternion":
skinObj[skinType] = 1
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

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
Copy link
Owner

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.

Copy link
Contributor Author

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

Copy link
Owner

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:
Copy link
Owner

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 Show resolved Hide resolved
material = materialArray[meshMaterial.Name()]
material_tag = newMesh.MakeTag(c4d.Ttexture)
material_tag[c4d.TEXTURETAG_MATERIAL] = material
material_tag[c4d.TEXTURETAG_PROJECTION] = 6
Copy link
Owner

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)
Copy link
Owner

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Owner

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):
Copy link
Owner

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.

Copy link
Contributor Author

@Devostated Devostated Apr 12, 2024

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!

@Devostated
Copy link
Contributor Author

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)
@dtzxporter
Copy link
Owner

@Devostated am I waiting for more or is this ready for review again?

@Devostated
Copy link
Contributor Author

Sorry for no updates. I'm kinda lost in exams right now. I would do the pole bones in one or two days.
I'm not too familiar with the poles. Can you give me any recommendations for a setup?
Here are the available settings:
image

@dtzxporter
Copy link
Owner

"Pole vector object" should be pole vector bone.

"Pole vector twist" should be keyed to the rotation x value of pole vector.

@dtzxporter dtzxporter added the enhancement New feature or request label May 21, 2024
New Feature:
- Instance import

Improvements:
- using c4d.Quaternion()
- using PoleVectorBone instead of PoleBone

Requirements:
- Cinema 4D 2024.5 or newer
@Devostated
Copy link
Contributor Author

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.

Copy link
Owner

@dtzxporter dtzxporter left a 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!

plugins/cinema4d/import_cast.pyp Show resolved Hide resolved
@@ -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])
Copy link
Owner

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had some big issues as I tried to remove it, but I'll get back to it and try again.
Here are the results I had in my tries:
image
image


poleBone = handle.PoleBone()
if poleBone is not None:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if handle.PoleBone():

Copy link
Contributor Author

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!

Copy link
Contributor Author

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.

plugins/cinema4d/import_cast.pyp Show resolved Hide resolved
modelNull = importModelNode(doc, node, child, instancePath)
modelNull.InsertUnder(sceneNull)
except:
print("Failed to import instance: %s" % instancePath)
Copy link
Owner

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.

Copy link
Contributor Author

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.

plugins/cinema4d/import_cast.pyp Show resolved Hide resolved
plugins/cinema4d/import_cast.pyp Show resolved Hide resolved
plugins/cinema4d/res/c4d_symbols.h Show resolved Hide resolved
@@ -173,6 +177,7 @@ def importModelNode(doc, node, model, path):
for i in range(mesh.UVLayerCount()):
uvTag = c4d.UVWTag(facesCount)
uvBuffer = mesh.VertexUVLayerBuffer(0)
Copy link
Owner

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)

Copy link
Contributor Author

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!

dtzxporter pushed a commit that referenced this pull request Sep 23, 2024
- 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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants