← Back to cinema4d
Batch Export and Import Weights in Cinema4D
29 Apr 26 (1mo ago)
Inspired from maya's mGear feature of export and import weights feature. And actually uses the same formatting except for the skinPack, which is to be implemented.
Normally, you can just get by using a weight manager built-in export and import command but it gets hectic if you have to do it in several objects. Hence you need to script something.
Also in this format, easier to rename objects to skin/weight.
Export Command
# Exports to the directory of the current C4D Document
import c4d
import os
import json
import c4d.gui as gui
def export_weights_to_json():
doc = c4d.documents.GetActiveDocument()
# 1. Get the current document path
doc_path = doc.GetDocumentPath()
if not doc_path:
gui.MessageDialog("Please save your Cinema 4D document first before exporting weights.")
return
# 2. Store the selection
selected_objects = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_CHILDREN)
if not selected_objects:
gui.MessageDialog("No objects selected. Please select objects to export weights.")
return
# Create the weights folder if it doesn't exist
weights_dir = os.path.join(doc_path, "weights")
if not os.path.exists(weights_dir):
try:
os.makedirs(weights_dir)
except Exception as e:
gui.MessageDialog("Failed to create weights directory:\n{}".format(e))
return
success_count = 0
failed_objects = []
# 3. Process each selected object
for obj in selected_objects:
obj_name = obj.GetName()
# Check if it's a Point Object (Polygon/Spline)
if not obj.IsInstanceOf(c4d.Opoint):
failed_objects.append(obj_name + " (Not a point object)")
continue
pt_count = obj.GetPointCount()
# Look for the Weight Tag
weight_tag = obj.GetTag(c4d.Tweights)
if not weight_tag:
failed_objects.append(obj_name + " (No weight tag)")
continue
# Extract weights
weights_dict = {}
joint_count = weight_tag.GetJointCount()
for j_idx in range(joint_count):
joint_obj = weight_tag.GetJoint(j_idx, doc)
if not joint_obj:
continue
joint_name = joint_obj.GetName()
joint_weights = {}
for p_idx in range(pt_count):
w = weight_tag.GetWeight(j_idx, p_idx)
# Only store non-zero weights to optimize JSON size, rounded to 4 decimals
if w > 0.0001:
joint_weights[str(p_idx)] = round(w, 4)
# Only add the joint to the dictionary if it actually influences vertices
if joint_weights:
weights_dict[joint_name] = joint_weights
# Construct the JSON payload matching the requested format
json_data = {
"bypassObj": [],
"objDDic": [
{
"blendWeights": {},
"nameSpace": "",
"normalizeWeights": 1,
"objName": obj_name,
"skinClsName": "{}_skinCluster".format(obj_name),
"skinDataFormat": "compressed",
"skinningMethod": 0,
"vertexCount": pt_count,
"weights": weights_dict
}
],
"objs": [obj_name]
}
# 4. Save the JSON file
file_path = os.path.join(weights_dir, "{}.json".format(obj_name))
try:
with open(file_path, 'w') as f:
json.dump(json_data, f, indent=4)
success_count += 1
except Exception as e:
failed_objects.append("{} (Write error: {})".format(obj_name, str(e)))
# 5. Pop up dialog reporting results
report = "Export Complete!\n\n"
report += "Successfully exported: {} object(s)\n".format(success_count)
report += "Failed/Skipped: {} object(s)\n".format(len(failed_objects))
if failed_objects:
report += "\nSkipped Objects:\n"
for fail in failed_objects:
report += "- {}\n".format(fail)
gui.MessageDialog(report)
if __name__ == '__main__':
export_weights_to_json()
Import Command
# Not same as the mGear where it uses skinPack. Currently, just a folder which sucks a bit because it uses an old UI dialog box.
# And you need to select objects rather than just browsing the skinPack file and let it decide which objects to skin. That works in Maya easily since it enforces a somewhat strict naming. In C4D, it is okay to have multiple "eye_geo" in the hierarchy :(
# The object to be selected needs to have no skin deformer or skin tag attached.
import c4d
import os
import json
def import_weights_from_json():
doc = c4d.documents.GetActiveDocument()
# 1. Store the selection
selected_objects = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_CHILDREN)
if not selected_objects:
print("Import Cancelled: No objects selected.")
return
# 2. Open a folder dialog box
folder_path = c4d.storage.LoadDialog(title="Select Weights Folder", flags=c4d.FILESELECT_DIRECTORY)
if not folder_path:
print("Import Cancelled: No folder selected.")
return
print("\n" + "="*40)
print("--- Starting Weights Import ---")
print("="*40)
success_list = []
skipped_has_tag = []
skipped_no_file = []
skipped_missing_joint = []
# Process each selected object
for obj in selected_objects:
obj_name = obj.GetName()
# 3. Check if there is a skin weights attached
if obj.GetTag(c4d.Tweights):
skipped_has_tag.append(obj_name)
continue
# 4. Check if the JSON file corresponds with the name in the directory
json_file_path = os.path.join(folder_path, "{}.json".format(obj_name))
if not os.path.exists(json_file_path):
skipped_no_file.append(obj_name)
continue
# Read JSON
try:
with open(json_file_path, 'r') as f:
data = json.load(f)
except Exception as e:
print("Error reading JSON for {}: {}".format(obj_name, e))
continue
# Extract weight dictionary safely based on the previous export structure
try:
weights_dict = data["objDDic"][0]["weights"]
except KeyError:
print("Error: JSON format for {} is invalid or missing 'weights' data.".format(obj_name))
continue
# 5 & 6. Loop through joints and verify they exist in the scene
joints_to_bind = []
missing_joints = []
for joint_name in weights_dict.keys():
# SearchObject looks through the entire document hierarchy for a matching name
joint_obj = doc.SearchObject(joint_name)
if not joint_obj:
missing_joints.append(joint_name)
else:
joints_to_bind.append((joint_name, joint_obj))
# If any joints are missing, flag it and skip the object
if missing_joints:
skipped_missing_joint.append("{} (Missing joints: {})".format(obj_name, ", ".join(missing_joints)))
continue
# --- All checks passed! Time to bind and apply weights ---
# Create Weight Tag
weight_tag = c4d.BaseTag(c4d.Tweights)
obj.InsertTag(weight_tag)
# Optional but recommended: Add a Skin Deformer if one doesn't exist
skin_deformer = obj.GetDown()
if not skin_deformer or not skin_deformer.IsInstanceOf(c4d.Oskin):
skin = c4d.BaseObject(c4d.Oskin)
skin.InsertUnder(obj)
# Add joints to the tag and apply the weights
for j_name, j_obj in joints_to_bind:
# Add joint to the weight tag, which returns its new index in the tag
j_index = weight_tag.AddJoint(j_obj)
# Retrieve the point weights for this joint from the JSON
joint_point_weights = weights_dict[j_name]
# Apply each point weight
for pt_idx_str, weight_val in joint_point_weights.items():
pt_idx = int(pt_idx_str)
weight_tag.SetWeight(j_index, pt_idx, float(weight_val))
# Update the weight tag internally
weight_tag.Message(c4d.MSG_UPDATE)
success_list.append(obj_name)
# Print the final results to the console
print("\n--- Import Summary ---")
print("Successfully bound & imported: {}".format(len(success_list)))
for name in success_list:
print(" + {}".format(name))
print("\nSkipped (Already had Weight Tag): {}".format(len(skipped_has_tag)))
for name in skipped_has_tag:
print(" - {}".format(name))
print("\nSkipped (No corresponding JSON found): {}".format(len(skipped_no_file)))
for name in skipped_no_file:
print(" - {}".format(name))
print("\nSkipped (Missing Joints in scene): {}".format(len(skipped_missing_joint)))
for item in skipped_missing_joint:
print(" - {}".format(item))
print("="*40 + "\n")
# Refresh Cinema 4D UI
c4d.EventAdd()
if __name__ == '__main__':
# Clears the console so you get a fresh readout every time you run it
c4d.CallCommand(13957)
import_weights_from_json()