diff --git a/README.md b/README.md
index ac024cd..80f6776 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@ You can find detailed documentation (and much else) on the [Cyberpunk 2077 moddi
- [Characters](https://wiki.redmodding.org/wolvenkit/modding-community/exporting-to-blender) and [vehicles](https://wiki.redmodding.org/wolvenkit/modding-community/exporting-vehicles): Point the plugin at the Wolvenkit export and it will include all meshes and materials from your project, setting them up to match the in-game appearance you specified.
-- Colliders (phys.json): Import colliders from Wolvenkit exported .phys files for easy visualization and editing
+- Colliders: Import colliders from Wolvenkit exported .phys and entity jsons for easy visualization and editing
- Cyberpunk 2077 [animations](https://wiki.redmodding.org/wolvenkit/modding-community/exporting-to-blender/exporting-rigs-and-anims): Play animations directly on your exported models — either by [importing them from file](https://wiki.redmodding.org/wolvenkit/modding-community/exporting-to-blender/exporting-rigs-and-anims), or by using the bundled rig resources via the Cyberpunk Animations Panel
@@ -31,7 +31,7 @@ You can find detailed documentation (and much else) on the [Cyberpunk 2077 moddi
- [Animations](https://wiki.redmodding.org/cyberpunk-2077-modding/for-mod-creators/modding-tools/wolvenkit-blender-io-suite/wkit-blender-plugin-import-export#animations-1): Export new and edited animations to GLB
-- Collisions: Export modified collision shapes to .phys.json
+- Collisions: Export generated collision shapes to .phys.json
## Tools and Shortcuts:
> Hint: You can find more detailed documentation on the [Cyberpunk 2077 Modding Wiki](https://wiki.redmodding.org/cyberpunk-2077-modding/for-mod-creators/modding-tools/wolvenkit-blender-io-suite)
@@ -50,8 +50,8 @@ You can find detailed documentation (and much else) on the [Cyberpunk 2077 moddi
### Collision Tools and Generator
- Automatic generation of convex colliders matched perfectly to the shape of your mesh, the number of vertices to sample should be set to match the number set in the .phys file in order to ensure successful export.
-- Generate box and capsule colliders with either user specified sizing or sized automatically to match the selected mesh
-- Export edited collision bodies back to .phys ***currently requires a wolvenkit converted .phys.json file
+- Generate colliders with either user specified sizing or sized automatically to match the selected mesh
+- Export your generated collision bodies to .phys.json for use in game.
### Material Exports
- [Hair profiles](https://wiki.redmodding.org/cyberpunk-2077-modding/for-mod-creators/modding-tools/wolvenkit-blender-io-suite/wkit-blender-plugin-import-export#hair-profiles-.hp): export custom hair profiles to `.hp.json` for easy import with Wolvenkit.
@@ -63,12 +63,11 @@ You can find detailed documentation (and much else) on the [Cyberpunk 2077 moddi
# Requirements
-1) **Blender** version 4.0 is supported
-
**Blender** version 3.6 or higher is *highly recommended*
-
**Blender** version 3.1 or higher is *required*
+1) **Blender** version 4.0 or higher is *highly recommended*
+
**Blender** versions below 3.6 will not work and users of these versions should update Blender before installing the plugin
https://www.blender.org/
-2) **WolvenKit** version 8.11 or higher
+2) **WolvenKit** version 8.13 or higher
https://github.com/WolvenKit/WolvenKit
---
diff --git a/i_scene_cp77_gltf/__init__.py b/i_scene_cp77_gltf/__init__.py
index 6f2f741..d8bc8ea 100644
--- a/i_scene_cp77_gltf/__init__.py
+++ b/i_scene_cp77_gltf/__init__.py
@@ -44,7 +44,7 @@ def install_dependency(dependency_name):
bl_info = {
"name": "Cyberpunk 2077 IO Suite",
"author": "HitmanHimself, Turk, Jato, dragonzkiller, kwekmaster, glitchered, Simarilius, Doctor Presto, shotlastc, Rudolph2109, Holopointz",
- "version": (1, 5, 5, 2),
+ "version": (1, 5, 5, 3),
"blender": (4, 0, 0),
"location": "File > Import-Export",
"description": "Import and Export WolvenKit Cyberpunk2077 gLTF models with materials, Import .streamingsector and .ent from .json",
@@ -422,13 +422,18 @@ class CP77_PT_PanelProps(PropertyGroup):
max=1.0
)
- smooth_factor: bpy.props.FloatProperty(
+ smooth_factor: FloatProperty(
name="Smooth Factor",
default=0.5,
min=0.0,
max=1.0
)
+ remap_depot: BoolProperty(
+ name="Remap Depot",
+ default=False,
+ description="replace the json depot path with the one in prefs"
+ )
class CP77CollisionGenerator(Operator):
bl_idname = "generate_cp77.collisions"
@@ -1126,7 +1131,7 @@ def draw(self, context):
box = layout.box()
box.label(icon_value=custom_icon_col["sculpt"]["SCULPT"].icon_id, text="Modelling:")
row = box.row(align=True)
- if context.object.active_material and context.object.active_material.name == 'UV_Checker':
+ if context.active_object and context.active_object.type == 'MESH' and context.object.active_material and context.object.active_material.name == 'UV_Checker':
row.operator("cp77.uv_unchecker", text="Remove UV Checker")
else:
row.operator("cp77.uv_checker", text="UV Checker")
@@ -1377,8 +1382,8 @@ def execute(self, context):
class CP77_PT_ImportWithMaterial(Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
- bl_label = "With Materials"
-
+ bl_label = "With Materials"
+
@classmethod
def poll(cls, context):
operator = context.space_data.active_operator
@@ -1390,6 +1395,7 @@ def draw_header(self, context):
def draw(self, context):
cp77_addon_prefs = bpy.context.preferences.addons[__name__].preferences
+ props = context.scene.cp77_panel_props
operator = context.space_data.active_operator
layout = self.layout
row = layout.row(align=True)
@@ -1403,7 +1409,7 @@ def draw(self, context):
row.prop(operator, 'use_cycles')
if cp77_addon_prefs.experimental_features:
row = layout.row(align=True)
- row.prop(self,"remap_depot")
+ row.prop(props,"remap_depot")
if operator.use_cycles:
row = layout.row(align=True)
row.prop(operator, 'update_gi')
@@ -1460,7 +1466,8 @@ def draw(self, context):
def execute(self, context):
SetCyclesRenderer(self.use_cycles, self.update_gi)
- CP77GLBimport(self, self.exclude_unused_mats, self.image_format, self.with_materials, self.filepath, self.hide_armatures, self.import_garmentsupport, self.files, self.directory, self.appearances,self.remap_depot)
+ props = context.scene.cp77_panel_props
+ CP77GLBimport(self, self.exclude_unused_mats, self.image_format, self.with_materials, self.filepath, self.hide_armatures, self.import_garmentsupport, self.files, self.directory, self.appearances, props.remap_depot)
return {'FINISHED'}
diff --git a/i_scene_cp77_gltf/exporters/phys_export.py b/i_scene_cp77_gltf/exporters/phys_export.py
index 9d35814..dd49efc 100644
--- a/i_scene_cp77_gltf/exporters/phys_export.py
+++ b/i_scene_cp77_gltf/exporters/phys_export.py
@@ -10,10 +10,20 @@ def export_colliders_to_phys(collections, filepath):
print(collections)
# Initialize an index counter
index = 1
+ total_mass = 0
+ inertia_X = 0
+ inertia_Y = 0
+ inertia_Z = 0
for collection in collections:
# Iterate over objects in the collection
for obj in bpy.data.collections[collection].objects:
if 'collisionShape' in obj:
+ bpy.ops.object.set_physics_material()
+ if 'Mass' in obj.keys():
+ total_mass += obj['Mass']
+ inertia_X += obj["inertia_X"]
+ inertia_Y += obj["inertia_Y"]
+ inertia_Z += obj["inertia_Z"]
collider_info = {
"HandleId": str(index),
"Data": {
@@ -131,9 +141,9 @@ def export_colliders_to_phys(collections, filepath):
"orientation": {"$type": "Quaternion", "i": 0, "j": 0, "k": 0, "r": 1},
"position": {"$type": "Vector4", "W": 0, "X": 0, "Y": 0, "Z": 0}
},
- "inertia": {"$type": "Vector3", "X": 0, "Y": 0, "Z": 0},
- "linearDamping": 0,
- "mass": 0,
+ "inertia": {"$type": "Vector3", "X": inertia_X, "Y": inertia_Y, "Z": inertia_Z},
+ "linearDamping": -1,
+ "mass": total_mass,
"maxAngularVelocity": -1,
"maxContactImpulse": -1,
"maxDepenetrationVelocity": -1,
diff --git a/i_scene_cp77_gltf/main/collisions.py b/i_scene_cp77_gltf/main/collisions.py
index 232cd70..a251ee7 100644
--- a/i_scene_cp77_gltf/main/collisions.py
+++ b/i_scene_cp77_gltf/main/collisions.py
@@ -256,7 +256,7 @@ def CP77CollisionGen(self, context, matchSize, collider_type, collision_shape, s
mesh.update()
bm.free()
capsule = bpy.data.objects.new(name, mesh)
- set_collider_props(capsule, collision_shape, physics_material, collider_type)
+ set_collider_props(capsule, shape, physics_material, collider_type)
capsule.location = center
capsule.rotation_quaternion[1] = 1
bpy.ops.object.transform_apply(location=False, rotation=True, scale=False)
@@ -284,6 +284,6 @@ def CP77CollisionGen(self, context, matchSize, collider_type, collision_shape, s
mesh.update()
bm.free()
sphere = bpy.data.objects.new(name, mesh)
- set_collider_props(sphere, collision_shape, physics_material, collider_type)
+ set_collider_props(sphere, shape, physics_material, collider_type)
colliderCollection.objects.link(sphere)
diff --git a/i_scene_cp77_gltf/main/meshtools.py b/i_scene_cp77_gltf/main/meshtools.py
index 8107fcf..4701977 100644
--- a/i_scene_cp77_gltf/main/meshtools.py
+++ b/i_scene_cp77_gltf/main/meshtools.py
@@ -13,7 +13,7 @@
def CP77CollectionList(self, context):
items = []
## don't include these as their not useful
- excluded_names = ["Collection", "Scene Collection"]
+ excluded_names = ["Collection", "Scene Collection", "glTF_not_exported"]
for collection in bpy.data.collections:
if collection.name not in excluded_names:
@@ -140,28 +140,31 @@ def CP77ArmatureSet(self, context):
def trans_weights(self, context):
+ current_mode = context.mode
props = context.scene.cp77_panel_props
source_mesh_name = props.mesh_source
target_mesh_name = props.mesh_target
+ active_objs = context.selected_objects
# Get the source collection
source_mesh = bpy.data.collections.get(source_mesh_name)
# Get the target collection
target_mesh = bpy.data.collections.get(target_mesh_name)
+
+
if source_mesh and target_mesh:
+ if current_mode != 'OBJECT':
+ bpy.ops.object.mode_set(mode='OBJECT')
# Deselect all objects in the scene
bpy.ops.object.select_all(action='DESELECT')
- # Iterate through objects in the source collection
+ # Select the objects in the source collection
for source_obj in source_mesh.objects:
source_obj.select_set(True)
# Set the active object to the last selected source object
bpy.context.view_layer.objects.active = source_obj
-
- # Switch to OBJECT mode
- bpy.ops.object.mode_set(mode='OBJECT')
# Iterate through objects in the target collection
for target_obj in target_mesh.objects:
@@ -175,9 +178,27 @@ def trans_weights(self, context):
layers_select_dst='NAME',
layers_select_src='ALL'
)
-
- # Deselect all objects again
bpy.ops.object.select_all(action='DESELECT')
+ context.view_layer.objects.active = None
+
+ for obj in active_objs:
+ obj.select_set(True)
+ context.view_layer.objects.active = obj
+
+
+ if context.mode != current_mode:
+ try:
+ bpy.ops.object.mode_set(mode=current_mode)
+
+ except TypeError:
+
+ if current_mode == 'PAINT_WEIGHT':
+ bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
+ elif current_mode == 'EDIT_MESH':
+ bpy.ops.object.mode_set(mode='EDIT')
+ elif current_mode == 'PAINT_VERTEX':
+ bpy.ops.object.mode_set(mode='VERTEX_PAINT')
+
def CP77UvChecker(self, context):
@@ -211,10 +232,15 @@ def CP77UvChecker(self, context):
uvchecker = mat
mat_assigned = True
if not mat_assigned:
- current_mat = context.object.active_material.name
- mesh['uvCheckedMat'] = current_mat
- bpy.data.meshes[mesh.name].materials.append(bpy_mats['UV_Checker'])
- i = mesh.data.materials.find('UV_Checker')
+ try:
+ current_mat = context.object.active_material.name
+ mesh['uvCheckedMat'] = current_mat
+ bpy.data.meshes[mesh.name].materials.append(bpy_mats['UV_Checker'])
+ i = mesh.data.materials.find('UV_Checker')
+ except AttributeError:
+ bpy.data.meshes[mesh.name].materials.append(bpy_mats['UV_Checker'])
+ i = mesh.data.materials.find('UV_Checker')
+
if i >= 0:
mesh.active_material_index = i
if current_mode != 'EDIT':
@@ -233,20 +259,22 @@ def CP77UvUnChecker(self, context):
selected_meshes = [obj for obj in bpy.context.selected_objects if obj.type == 'MESH']
current_mode = context.mode
uvchecker = 'UV_Checker'
+ original_mat_name = None
for mesh in selected_meshes:
- if 'uvCheckedMat' in mesh:
+ if 'uvCheckedMat' in mesh.keys() and 'uvCheckedMat' is not None:
original_mat_name = mesh['uvCheckedMat']
if uvchecker in mesh.data.materials:
# Find the index of the material slot with the specified name
material_index = mesh.data.materials.find(uvchecker)
mesh.data.materials.pop(index=material_index)
- i = mesh.data.materials.find(original_mat_name)
- bpy.ops.wm.properties_remove(data_path="object", property_name="uvCheckedMat")
- if i >= 0:
- mesh.active_material_index = i
- if current_mode != 'EDIT':
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.object.material_slot_assign()
+ if original_mat_name is not None:
+ i = mesh.data.materials.find(original_mat_name)
+ bpy.ops.wm.properties_remove(data_path="object", property_name="uvCheckedMat")
+ if i >= 0:
+ mesh.active_material_index = i
+ if current_mode != 'EDIT':
+ bpy.ops.object.mode_set(mode='EDIT')
+ bpy.ops.object.material_slot_assign()
if context.mode != current_mode:
bpy.ops.object.mode_set(mode=current_mode)