Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions .idea/inspectionProfiles/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions .idea/rigacar.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# Rigacar

## Fork

_This fork supports **Blender 4.0+** and consolidates fixes from the community while the maintainer is absent._

Upstream readme description below.

## Upstream

Rigacar is a free addon for Blender. It is designed to fulfill the following goals:

* generate a complete rig as quickly as possible (actually few seconds) for standard car models
Expand All @@ -6,6 +16,6 @@ Rigacar is a free addon for Blender. It is designed to fulfill the following goa

Please read [full documentation](http://digicreatures.net/articles/rigacar.html) on my website.

You can also watch the series of videotutorials:
You can also watch the series of video tutorials:

[![Rigacar Part 1](http://img.youtube.com/vi/D3XQxA_-TzY/0.jpg)](https://www.youtube.com/watch?v=D3XQxA_-TzY&list=PLH_mmrv8SfPFiEj93RJt3sBvHCnipI9qK "Rigacar videotutorials")
[![Rigacar Part 1](http://img.youtube.com/vi/D3XQxA_-TzY/0.jpg)](https://www.youtube.com/watch?v=D3XQxA_-TzY&list=PLH_mmrv8SfPFiEj93RJt3sBvHCnipI9qK "Rigacar video tutorials")
22 changes: 12 additions & 10 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,18 @@
bl_info = {
"name": "Rigacar (Generates Car Rig)",
"author": "David Gayerie",
"version": (7, 1),
"blender": (2, 83, 0),
"version": (8, 0),
"blender": (4, 0, 0),
"location": "View3D > Add > Armature",
"description": "Adds a deformation rig for vehicules, generates animation rig and bake wheels animation.",
"wiki_url": "http://digicreatures.net/articles/rigacar.html",
"tracker_url": "https://github.com/digicreatures/rigacar/issues",
"category": "Rigging"}

"category": "Rigging"
}

if "bpy" in locals():
import importlib

if "bake_operators" in locals():
importlib.reload(bake_operators)
if "car_rig" in locals():
Expand Down Expand Up @@ -188,15 +189,16 @@ def draw(self, context):


def menu_entries(menu, context):
menu.layout.operator(car_rig.OBJECT_OT_armatureCarDeformationRig.bl_idname, text="Car (deformation rig)", icon='AUTO')
menu.layout.operator(car_rig.OBJECT_OT_armatureCarDeformationRig.bl_idname, text="Car (deformation rig)",
icon='AUTO')


classes = (
RIGACAR_PT_rigProperties,
RIGACAR_PT_groundSensorsProperties,
RIGACAR_PT_animationRigView,
RIGACAR_PT_wheelsAnimationView,
RIGACAR_PT_groundSensorsView,
RIGACAR_PT_rigProperties,
RIGACAR_PT_groundSensorsProperties,
RIGACAR_PT_animationRigView,
RIGACAR_PT_wheelsAnimationView,
RIGACAR_PT_groundSensorsView,
)


Expand Down
60 changes: 43 additions & 17 deletions bake_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
# <pep8 compliant>

import bpy
import bpy_extras.anim_utils
import mathutils
import math
import itertools
Expand All @@ -33,7 +34,9 @@ def wrapper(self, context, *args, **kwargs):
return func(self, context, *args, **kwargs)
finally:
context.window.cursor_modal_restore()

return wrapper

return cursor_decorator


Expand Down Expand Up @@ -135,7 +138,7 @@ def evaluate(self, f):

def fix_old_steering_rotation(rig_object):
"""
Fix armature generated with rigacar version < 6.0
Fix armature generated with Rigacar version < 6.0
"""
if rig_object.pose and rig_object.pose.bones:
if 'MCH-Steering.rotation' in rig_object.pose.bones:
Expand Down Expand Up @@ -194,7 +197,8 @@ def _create_scale_evaluator(self, action, source_bone):

def _bake_action(self, context, *source_bones):
action = context.object.animation_data.action
nla_tweak_mode = context.object.animation_data.use_tweak_mode if hasattr(context.object.animation_data, 'use_tweak_mode') else False
nla_tweak_mode = context.object.animation_data.use_tweak_mode if hasattr(context.object.animation_data,
'use_tweak_mode') else False

# saving context
selected_bones = [b for b in context.object.data.bones if b.select]
Expand All @@ -208,14 +212,15 @@ def _bake_action(self, context, *source_bones):
source_bones_matrix_basis.append(context.object.pose.bones[source_bone.name].matrix_basis.copy())
source_bone.select = True

# Blender 2.81 : Another hack for another bug in the bake operator
# removing from the selection objects which are not the current one
for obj in context.selected_objects:
if obj is not context.object:
obj.select_set(state=False)

bpy.ops.nla.bake(frame_start=self.frame_start, frame_end=self.frame_end, only_selected=True, bake_types={'POSE'}, visual_keying=True)
baked_action = context.object.animation_data.action
baked_action = bpy_extras.anim_utils.bake_action(
context.object,
action=None,
frames=range(self.frame_start, self.frame_end + 1),
only_selected=True,
do_pose=True,
do_object=False,
do_visual_keying=True,
)

# restoring context
for source_bone, matrix_basis in zip(source_bones, source_bones_matrix_basis):
Expand Down Expand Up @@ -262,6 +267,10 @@ def _bake_wheels_rotation(self, context):
bones = set(wheel_bones + brake_bones)
baked_action = self._bake_action(context, *bones)

if baked_action is None:
self.report({'WARNING'}, "Existing action failed to bake. Won't bake wheel rotation")
return

try:
for wheel_bone, brake_bone in zip(wheel_bones, brake_bones):
self._bake_wheel_rotation(context, baked_action, wheel_bone, brake_bone)
Expand Down Expand Up @@ -302,6 +311,10 @@ def _evaluate_distance_per_frame(self, action, bone, brake_bone):
def _bake_wheel_rotation(self, context, baked_action, bone, brake_bone):
fc_rot = create_property_animation(context, bone.name.replace('MCH-', ''))

# Reset the transform of the wheel bone, otherwise baking yields wrong results
pb: bpy.types.PoseBone = context.object.pose.bones[bone.name]
pb.matrix_basis.identity()

for f, distance in self._evaluate_distance_per_frame(baked_action, bone, brake_bone):
kf = fc_rot.keyframe_points.insert(f, distance)
kf.interpolation = 'LINEAR'
Expand Down Expand Up @@ -362,10 +375,12 @@ def _evaluate_rotation_per_frame(self, action, bone_offset, bone):
length_ratio = bone_offset * self.rotation_factor / projected_steering_direction
steering_direction_vector *= length_ratio

steering_position = mathutils.geometry.distance_point_to_plane(steering_direction_vector, world_space_bone_direction_vector, world_space_bone_normal_vector)
steering_position = mathutils.geometry.distance_point_to_plane(steering_direction_vector,
world_space_bone_direction_vector,
world_space_bone_normal_vector)

if previous_steering_position is not None \
and abs(steering_position - previous_steering_position) < steering_threshold:
and abs(steering_position - previous_steering_position) < steering_threshold:
continue

yield f, steering_position
Expand All @@ -377,15 +392,24 @@ def _bake_steering_rotation(self, context, bone_offset, bone):
clear_property_animation(context, 'Steering.rotation')
fix_old_steering_rotation(context.object)
fc_rot = create_property_animation(context, 'Steering.rotation')
action = self._bake_action(context, bone)

baked_action = self._bake_action(context, bone)
if baked_action is None:
self.report({'WARNING'}, "Existing action failed to bake. Won't bake steering rotation")
return

try:
for f, steering_pos in self._evaluate_rotation_per_frame(action, bone_offset, bone):
# Reset the transform of the steering bone, because baking action manipulates the transform
# and evaluate_rotation_frame expects it at it's default position
pb: bpy.types.PoseBone = context.object.pose.bones[bone.name]
pb.matrix_basis.identity()

for f, steering_pos in self._evaluate_rotation_per_frame(baked_action, bone_offset, bone):
kf = fc_rot.keyframe_points.insert(f, steering_pos)
kf.type = 'JITTER'
kf.interpolation = 'LINEAR'
finally:
bpy.data.actions.remove(action)
bpy.data.actions.remove(baked_action)


class ANIM_OT_carClearSteeringWheelsRotation(bpy.types.Operator):
Expand All @@ -394,8 +418,10 @@ class ANIM_OT_carClearSteeringWheelsRotation(bpy.types.Operator):
bl_description = "Clear generated rotation for steering and wheels"
bl_options = {'REGISTER', 'UNDO'}

clear_steering: bpy.props.BoolProperty(name="Steering", description="Clear generated animation for steering", default=True)
clear_wheels: bpy.props.BoolProperty(name="Wheels", description="Clear generated animation for wheels", default=True)
clear_steering: bpy.props.BoolProperty(name="Steering", description="Clear generated animation for steering",
default=True)
clear_wheels: bpy.props.BoolProperty(name="Wheels", description="Clear generated animation for wheels",
default=True)

def draw(self, context):
self.layout.use_property_decorate = False
Expand Down
Loading