rigidbody: Add rigid body simulation
Add operators to add/remove rigid body world and objects. Add UI scripts. The rigid body simulation works on scene level and overrides the position/orientation of rigid bodies when active. It does not deform meshes or generate data so there is no modifier. Usage: * Add rigid body world in the scene tab * Create a group * Add objects to the group * Assign group to the rigid body world * Play animation For convenience the rigid body tools operators in the tools panel of the 3d view will add a world, group and add objects to the group automatically so you only have to press one button to add/remove rigid bodies to the simulation. Part of GSoC 2010 and 2012. Authors: Joshua Leung (aligorith), Sergej Reich (sergof)
This commit is contained in:
@@ -1166,6 +1166,9 @@ def get_panels():
|
||||
types.PARTICLE_PT_velocity,
|
||||
types.PARTICLE_PT_rotation,
|
||||
types.PARTICLE_PT_physics,
|
||||
bpy.types.SCENE_PT_rigid_body_world,
|
||||
bpy.types.SCENE_PT_rigid_body_cache,
|
||||
bpy.types.SCENE_PT_rigid_body_field_weights,
|
||||
types.PARTICLE_PT_boidbrain,
|
||||
types.PARTICLE_PT_render,
|
||||
types.PARTICLE_PT_draw,
|
||||
|
@@ -35,6 +35,7 @@ _modules = (
|
||||
"object_randomize_transform",
|
||||
"object_quick_effects",
|
||||
"presets",
|
||||
"rigidbody",
|
||||
"screen_play_rendered_anim",
|
||||
"sequencer",
|
||||
"uvcalc_follow_active",
|
||||
|
@@ -48,6 +48,7 @@ _modules = (
|
||||
"properties_physics_dynamicpaint",
|
||||
"properties_physics_field",
|
||||
"properties_physics_fluid",
|
||||
"properties_physics_rigidbody",
|
||||
"properties_physics_smoke",
|
||||
"properties_physics_softbody",
|
||||
"properties_render",
|
||||
|
@@ -44,6 +44,12 @@ def physics_add(self, layout, md, name, type, typeicon, toggles):
|
||||
else:
|
||||
sub.operator("object.modifier_add", text=name, icon=typeicon).type = type
|
||||
|
||||
def physics_add_special(self, layout, data, name, addop, removeop, typeicon):
|
||||
sub = layout.row(align=True)
|
||||
if data:
|
||||
sub.operator(removeop, text=name, icon='X')
|
||||
else:
|
||||
sub.operator(addop, text=name, icon=typeicon)
|
||||
|
||||
class PHYSICS_PT_add(PhysicButtonsPanel, Panel):
|
||||
bl_label = ""
|
||||
@@ -76,6 +82,12 @@ class PHYSICS_PT_add(PhysicButtonsPanel, Panel):
|
||||
physics_add(self, col, context.fluid, "Fluid", 'FLUID_SIMULATION', 'MOD_FLUIDSIM', True)
|
||||
physics_add(self, col, context.smoke, "Smoke", 'SMOKE', 'MOD_SMOKE', True)
|
||||
|
||||
if(ob.type == 'MESH'):
|
||||
physics_add_special(self, col, ob.rigid_body, "Rigid Body",
|
||||
"rigidbody.object_add",
|
||||
"rigidbody.object_remove",
|
||||
'MESH_ICOSPHERE') # XXX: need dedicated icon
|
||||
|
||||
|
||||
# cache-type can be 'PSYS' 'HAIR' 'SMOKE' etc
|
||||
|
||||
@@ -84,6 +96,7 @@ def point_cache_ui(self, context, cache, enabled, cachetype):
|
||||
|
||||
layout.context_pointer_set("point_cache", cache)
|
||||
|
||||
if not cachetype == 'RIGID_BODY':
|
||||
row = layout.row()
|
||||
row.template_list("UI_UL_list", "", cache, "point_caches", cache.point_caches, "active_index", rows=2)
|
||||
col = row.column(align=True)
|
||||
@@ -131,13 +144,13 @@ def point_cache_ui(self, context, cache, enabled, cachetype):
|
||||
row.enabled = enabled
|
||||
row.prop(cache, "frame_start")
|
||||
row.prop(cache, "frame_end")
|
||||
if cachetype not in {'SMOKE', 'CLOTH', 'DYNAMIC_PAINT'}:
|
||||
if cachetype not in {'SMOKE', 'CLOTH', 'DYNAMIC_PAINT', 'RIGID_BODY'}:
|
||||
row.prop(cache, "frame_step")
|
||||
|
||||
if cachetype != 'SMOKE':
|
||||
layout.label(text=cache.info)
|
||||
|
||||
if cachetype not in {'SMOKE', 'DYNAMIC_PAINT'}:
|
||||
if cachetype not in {'SMOKE', 'DYNAMIC_PAINT', 'RIGID_BODY'}:
|
||||
split = layout.split()
|
||||
split.enabled = enabled and bpy.data.is_saved
|
||||
|
||||
|
130
release/scripts/startup/bl_ui/properties_physics_rigidbody.py
Normal file
130
release/scripts/startup/bl_ui/properties_physics_rigidbody.py
Normal file
@@ -0,0 +1,130 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
import bpy
|
||||
from bpy.types import Panel
|
||||
|
||||
|
||||
class PHYSICS_PT_rigidbody_panel():
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "physics"
|
||||
|
||||
|
||||
class PHYSICS_PT_rigid_body(PHYSICS_PT_rigidbody_panel, Panel):
|
||||
bl_label = "Rigid Body"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
ob = context.object
|
||||
rd = context.scene.render
|
||||
return (ob and ob.rigid_body and (not rd.use_game_engine))
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
ob = context.object
|
||||
rbo = ob.rigid_body
|
||||
|
||||
if rbo:
|
||||
layout.prop(rbo, "type", text="Type")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(rbo, "enabled");
|
||||
row.prop(rbo, "kinematic", text="Animated")
|
||||
|
||||
if rbo.type == 'ACTIVE':
|
||||
col = layout.column()
|
||||
col.prop(rbo, "mass")
|
||||
|
||||
|
||||
class PHYSICS_PT_rigid_body_collisions(PHYSICS_PT_rigidbody_panel, Panel):
|
||||
bl_label = "Rigid Body Collisions"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.object and context.object.rigid_body and
|
||||
(not context.scene.render.use_game_engine))
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
ob = context.object
|
||||
rbo = ob.rigid_body
|
||||
|
||||
layout.prop(rbo, "collision_shape", text="Shape")
|
||||
|
||||
split = layout.split()
|
||||
|
||||
col = split.column()
|
||||
col.label(text="Surface Response:")
|
||||
col.prop(rbo, "friction")
|
||||
col.prop(rbo, "restitution", text="Bounciness")
|
||||
|
||||
col = split.column()
|
||||
col.label(text="Sensitivity:")
|
||||
if rbo.collision_shape in {'MESH', 'CONE'}:
|
||||
col.prop(rbo, "collision_margin", text="Margin")
|
||||
else:
|
||||
col.prop(rbo, "use_margin");
|
||||
sub = col.column()
|
||||
sub.active = rbo.use_margin
|
||||
sub.prop(rbo, "collision_margin", text="Margin")
|
||||
layout.prop(rbo, "collision_groups")
|
||||
|
||||
|
||||
class PHYSICS_PT_rigid_body_dynamics(PHYSICS_PT_rigidbody_panel, Panel):
|
||||
bl_label = "Rigid Body Dynamics"
|
||||
bl_default_closed = True
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.object and context.object.rigid_body and
|
||||
context.object.rigid_body.type == 'ACTIVE' and
|
||||
(not context.scene.render.use_game_engine))
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
ob = context.object
|
||||
rbo = ob.rigid_body
|
||||
|
||||
#col = layout.column(align=1)
|
||||
#col.label(text="Activation:")
|
||||
# XXX: settings such as activate on collison/etc.
|
||||
|
||||
split = layout.split();
|
||||
|
||||
col = split.column()
|
||||
col.label(text="Deactivation:")
|
||||
col.prop(rbo, "use_deactivation")
|
||||
sub = col.column()
|
||||
sub.active = rbo.use_deactivation
|
||||
sub.prop(rbo, "start_deactivated")
|
||||
sub.prop(rbo, "deactivate_linear_velocity", text="Linear Vel")
|
||||
sub.prop(rbo, "deactivate_angular_velocity", text="Angular Vel")
|
||||
# TODO: other params such as time?
|
||||
|
||||
col = split.column()
|
||||
col.label(text="Damping:")
|
||||
col.prop(rbo, "linear_damping", text="Translation")
|
||||
col.prop(rbo, "angular_damping", text="Rotation")
|
||||
|
||||
if __name__ == "__main__": # only for live edit.
|
||||
bpy.utils.register_module(__name__)
|
@@ -21,6 +21,10 @@ import bpy
|
||||
from bpy.types import Panel, UIList
|
||||
from rna_prop_ui import PropertyPanel
|
||||
|
||||
from bl_ui.properties_physics_common import (
|
||||
point_cache_ui,
|
||||
effector_weights_ui,
|
||||
)
|
||||
|
||||
class SCENE_UL_keying_set_paths(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
@@ -242,6 +246,83 @@ class SCENE_PT_physics(SceneButtonsPanel, Panel):
|
||||
layout.prop(scene, "gravity", text="")
|
||||
|
||||
|
||||
class SCENE_PT_rigid_body_world(SceneButtonsPanel, Panel):
|
||||
bl_label = "Rigid Body World"
|
||||
COMPAT_ENGINES = {'BLENDER_RENDER'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
rd = context.scene.render
|
||||
scene = context.scene
|
||||
return scene and (rd.engine in cls.COMPAT_ENGINES)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
scene = context.scene
|
||||
|
||||
rbw = scene.rigidbody_world
|
||||
|
||||
if not rbw:
|
||||
layout.operator("rigidbody.world_add")
|
||||
else:
|
||||
split = layout.split()
|
||||
split.operator("rigidbody.world_remove")
|
||||
layout.separator()
|
||||
layout.prop(context.scene.rigidbody_world, "enabled")
|
||||
layout.active = rbw.enabled
|
||||
|
||||
col = layout.column()
|
||||
col.prop(rbw, "group")
|
||||
col.prop(rbw, "constraints")
|
||||
|
||||
split = layout.split()
|
||||
|
||||
col = split.column()
|
||||
col.prop(rbw, "time_scale", text="Speed")
|
||||
col.prop(rbw, "use_split_impulse")
|
||||
|
||||
col = split.column()
|
||||
col.prop(rbw, "steps_per_second", text="Steps Per Second")
|
||||
col.prop(rbw, "num_solver_iterations", text="Solver Iterations")
|
||||
|
||||
|
||||
class SCENE_PT_rigid_body_cache(SceneButtonsPanel, Panel):
|
||||
bl_label = "Rigid Body Cache"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
COMPAT_ENGINES = {'BLENDER_RENDER'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
rd = context.scene.render
|
||||
scene = context.scene
|
||||
return scene and scene.rigidbody_world and (rd.engine in cls.COMPAT_ENGINES)
|
||||
|
||||
def draw(self, context):
|
||||
scene = context.scene
|
||||
rbw = scene.rigidbody_world
|
||||
|
||||
point_cache_ui(self, context, rbw.point_cache, rbw.point_cache.is_baked is False and rbw.enabled, 'RIGID_BODY')
|
||||
|
||||
|
||||
class SCENE_PT_rigid_body_field_weights(SceneButtonsPanel, Panel):
|
||||
bl_label = "Rigid Body Field Weights"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
COMPAT_ENGINES = {'BLENDER_RENDER'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
rd = context.scene.render
|
||||
scene = context.scene
|
||||
return scene and scene.rigidbody_world and (rd.engine in cls.COMPAT_ENGINES)
|
||||
|
||||
def draw(self, context):
|
||||
scene = context.scene
|
||||
rbw = scene.rigidbody_world
|
||||
|
||||
effector_weights_ui(self, context, rbw.effector_weights, 'RIGID_BODY')
|
||||
|
||||
|
||||
class SCENE_PT_simplify(SceneButtonsPanel, Panel):
|
||||
bl_label = "Simplify"
|
||||
COMPAT_ENGINES = {'BLENDER_RENDER'}
|
||||
|
@@ -108,6 +108,29 @@ class VIEW3D_PT_tools_objectmode(View3DPanel, Panel):
|
||||
draw_repeat_tools(context, layout)
|
||||
|
||||
draw_gpencil_tools(context, layout)
|
||||
col = layout.column(align=True)
|
||||
|
||||
|
||||
class VIEW3D_PT_tools_rigidbody(View3DPanel, Panel):
|
||||
bl_context = "objectmode"
|
||||
bl_label = "Rigidbody Tools"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.label(text="Add/Remove:")
|
||||
row = col.row()
|
||||
row.operator("rigidbody.objects_add", text="Add Active").type = 'ACTIVE'
|
||||
row.operator("rigidbody.objects_add", text="Add Passive").type = 'PASSIVE'
|
||||
row = col.row()
|
||||
row.operator("rigidbody.objects_remove", text="Remove")
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.label(text="Object Tools:")
|
||||
col.operator("rigidbody.shape_change", text="Change Shape")
|
||||
col.operator("rigidbody.mass_calculate", text="Calculate Mass")
|
||||
|
||||
# ********** default tools for editmode_mesh ****************
|
||||
|
||||
|
@@ -2146,6 +2146,8 @@ void BKE_object_where_is_calc_time(Scene *scene, Object *ob, float ctime)
|
||||
BKE_object_to_mat4(ob, ob->obmat);
|
||||
}
|
||||
|
||||
BKE_rigidbody_sync_transforms(scene, ob, ctime);
|
||||
|
||||
/* solve constraints */
|
||||
if (ob->constraints.first && !(ob->transflag & OB_NO_CONSTRAINTS)) {
|
||||
bConstraintOb *cob;
|
||||
|
@@ -751,7 +751,35 @@ static void rigidbody_update_simulation(Scene *scene, RigidBodyWorld *rbw, int r
|
||||
/* Sync rigid body and object transformations */
|
||||
void BKE_rigidbody_sync_transforms(Scene *scene, Object *ob, float ctime)
|
||||
{
|
||||
// RB_TODO implement this
|
||||
RigidBodyWorld *rbw = scene->rigidbody_world;
|
||||
RigidBodyOb *rbo = ob->rigidbody_object;
|
||||
|
||||
/* keep original transform for kinematic and passive objects */
|
||||
if (ELEM(NULL, rbw, rbo) || rbo->flag & RBO_FLAG_KINEMATIC || rbo->type == RBO_TYPE_PASSIVE)
|
||||
return;
|
||||
|
||||
/* use rigid body transform after cache start frame */
|
||||
if (ctime > rbw->pointcache->startframe) {
|
||||
float mat[4][4], size_mat[4][4], size[3];
|
||||
|
||||
/* keep original transform when the simulation is muted */
|
||||
if (rbw->flag & RBW_FLAG_MUTED)
|
||||
return;
|
||||
|
||||
normalize_qt(rbo->orn); // RB_TODO investigate why quaternion isn't normalized at this point
|
||||
quat_to_mat4(mat, rbo->orn);
|
||||
copy_v3_v3(mat[3], rbo->pos);
|
||||
|
||||
mat4_to_size(size, ob->obmat);
|
||||
size_to_mat4(size_mat, size);
|
||||
mult_m4_m4m4(mat, mat, size_mat);
|
||||
|
||||
copy_m4_m4(ob->obmat, mat);
|
||||
}
|
||||
/* otherwise set rigid body transform to current obmat */
|
||||
else {
|
||||
mat4_to_loc_quat(rbo->pos, rbo->orn, ob->obmat);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_rigidbody_cache_reset(RigidBodyWorld *rbw)
|
||||
@@ -765,6 +793,77 @@ void BKE_rigidbody_cache_reset(RigidBodyWorld *rbw)
|
||||
/* Run RigidBody simulation for the specified physics world */
|
||||
void BKE_rigidbody_do_simulation(Scene *scene, float ctime)
|
||||
{
|
||||
// RB_TODO implement this
|
||||
float timestep;
|
||||
RigidBodyWorld *rbw = scene->rigidbody_world;
|
||||
PointCache *cache;
|
||||
PTCacheID pid;
|
||||
int startframe, endframe;
|
||||
|
||||
BKE_ptcache_id_from_rigidbody(&pid, NULL, rbw);
|
||||
BKE_ptcache_id_time(&pid, scene, ctime, &startframe, &endframe, NULL);
|
||||
cache = rbw->pointcache;
|
||||
|
||||
/* flag cache as outdated if we don't have a world or number of objects in the simulation has changed */
|
||||
if (rbw->physics_world == NULL || rbw->numbodies != BLI_countlist(&rbw->group->gobject)) {
|
||||
cache->flag |= PTCACHE_OUTDATED;
|
||||
}
|
||||
|
||||
if (ctime <= startframe) {
|
||||
rbw->ltime = startframe;
|
||||
/* reset and rebuild simulation if necessary */
|
||||
if (cache->flag & PTCACHE_OUTDATED) {
|
||||
BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED);
|
||||
rigidbody_update_simulation(scene, rbw, true);
|
||||
BKE_ptcache_validate(cache, (int)ctime);
|
||||
cache->last_exact = 0;
|
||||
cache->flag &= ~PTCACHE_REDO_NEEDED;
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* rebuild world if it's outdated on second frame */
|
||||
else if (ctime == startframe + 1 && rbw->ltime == startframe && cache->flag & PTCACHE_OUTDATED) {
|
||||
BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED);
|
||||
rigidbody_update_simulation(scene, rbw, true);
|
||||
}
|
||||
/* make sure we don't go out of cache frame range */
|
||||
else if (ctime > endframe) {
|
||||
ctime = endframe;
|
||||
}
|
||||
|
||||
/* don't try to run the simulation if we don't have a world yet but allow reading baked cache */
|
||||
if (rbw->physics_world == NULL && !(cache->flag & PTCACHE_BAKED))
|
||||
return;
|
||||
else if (rbw->objects == NULL)
|
||||
rigidbody_update_ob_array(rbw);
|
||||
|
||||
/* try to read from cache */
|
||||
// RB_TODO deal with interpolated, old and baked results
|
||||
if (BKE_ptcache_read(&pid, ctime)) {
|
||||
BKE_ptcache_validate(cache, (int)ctime);
|
||||
rbw->ltime = ctime;
|
||||
return;
|
||||
}
|
||||
|
||||
/* advance simulation, we can only step one frame forward */
|
||||
if (ctime == rbw->ltime + 1) {
|
||||
/* write cache for first frame when on second frame */
|
||||
if (rbw->ltime == startframe && (cache->flag & PTCACHE_OUTDATED || cache->last_exact == 0)) {
|
||||
BKE_ptcache_write(&pid, startframe);
|
||||
}
|
||||
|
||||
/* update and validate simulation */
|
||||
rigidbody_update_simulation(scene, rbw, false);
|
||||
|
||||
/* calculate how much time elapsed since last step in seconds */
|
||||
timestep = 1.0f / (float)FPS * (ctime - rbw->ltime) * rbw->time_scale;
|
||||
/* step simulation by the requested timestep, steps per second are adjusted to take time scale into account */
|
||||
RB_dworld_step_simulation(rbw->physics_world, timestep, INT_MAX, 1.0f / (float)rbw->steps_per_second * min_ff(rbw->time_scale, 1.0f));
|
||||
|
||||
/* write cache for current frame */
|
||||
BKE_ptcache_validate(cache, (int)ctime);
|
||||
BKE_ptcache_write(&pid, (unsigned int)ctime);
|
||||
|
||||
rbw->ltime = ctime;
|
||||
}
|
||||
}
|
||||
/* ************************************** */
|
||||
|
@@ -1206,6 +1206,12 @@ void BKE_scene_update_for_newframe(Main *bmain, Scene *sce, unsigned int lay)
|
||||
BKE_animsys_evaluate_all_animation(bmain, sce, ctime);
|
||||
/*...done with recusrive funcs */
|
||||
|
||||
/* run rigidbody sim */
|
||||
// XXX: this position may still change, objects not being updated correctly before simulation is run
|
||||
// NOTE: current position is so that rigidbody sim affects other objects
|
||||
if (BKE_scene_check_rigidbody_active(sce))
|
||||
BKE_rigidbody_do_simulation(sce, ctime);
|
||||
|
||||
/* clear "LIB_DOIT" flag from all materials, to prevent infinite recursion problems later
|
||||
* when trying to find materials with drivers that need evaluating [#32017]
|
||||
*/
|
||||
|
@@ -32,13 +32,22 @@
|
||||
#ifndef __ED_PHYSICS_H__
|
||||
#define __ED_PHYSICS_H__
|
||||
|
||||
struct bContext;
|
||||
struct wmOperator;
|
||||
struct wmKeyConfig;
|
||||
|
||||
struct Scene;
|
||||
struct Object;
|
||||
|
||||
/* particle_edit.c */
|
||||
int PE_poll(struct bContext *C);
|
||||
int PE_hair_poll(struct bContext *C);
|
||||
int PE_poll_view3d(struct bContext *C);
|
||||
|
||||
/* rigidbody_object.c */
|
||||
void ED_rigidbody_ob_add(struct wmOperator *op, struct Scene *scene, struct Object *ob, int type);
|
||||
void ED_rigidbody_ob_remove(struct Scene *scene, struct Object *ob);
|
||||
|
||||
/* operators */
|
||||
void ED_operatortypes_physics(void);
|
||||
void ED_keymap_physics(struct wmKeyConfig *keyconf);
|
||||
|
@@ -1697,7 +1697,11 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base
|
||||
BLI_addhead(&scene->base, basen); /* addhead: prevent eternal loop */
|
||||
basen->object = obn;
|
||||
|
||||
if (basen->flag & OB_FROMGROUP) {
|
||||
/* 1) duplis should end up in same group as the original
|
||||
* 2) Rigid Body sim participants MUST always be part of a group...
|
||||
*/
|
||||
// XXX: is 2) really a good measure here?
|
||||
if ((basen->flag & OB_FROMGROUP) || ob->rigidbody_object) {
|
||||
Group *group;
|
||||
for (group = bmain->group.first; group; group = group->id.next) {
|
||||
if (object_in_group(ob, group))
|
||||
|
@@ -27,6 +27,7 @@ set(INC
|
||||
../../makesdna
|
||||
../../makesrna
|
||||
../../windowmanager
|
||||
../../rigidbody
|
||||
../../../../intern/elbeem/extern
|
||||
../../../../intern/guardedalloc
|
||||
)
|
||||
@@ -43,6 +44,8 @@ set(SRC
|
||||
physics_fluid.c
|
||||
physics_ops.c
|
||||
physics_pointcache.c
|
||||
rigidbody_object.c
|
||||
rigidbody_world.c
|
||||
|
||||
physics_intern.h
|
||||
)
|
||||
|
@@ -33,6 +33,7 @@ incs = '../include ../../blenfont ../../blenlib ../../blenkernel ../../makesdna
|
||||
incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include'
|
||||
incs += ' ../../gpu ../../blenloader ../../bmesh'
|
||||
incs += ' ../../makesrna ../../render/extern/include #/intern/elbeem/extern'
|
||||
incs += ' ../../rigidbody'
|
||||
|
||||
defs = []
|
||||
|
||||
|
@@ -105,5 +105,19 @@ void PTCACHE_OT_bake_from_cache(struct wmOperatorType *ot);
|
||||
void PTCACHE_OT_add(struct wmOperatorType *ot);
|
||||
void PTCACHE_OT_remove(struct wmOperatorType *ot);
|
||||
|
||||
#endif /* __PHYSICS_INTERN_H__ */
|
||||
/* rigidbody_object.c */
|
||||
void RIGIDBODY_OT_object_add(struct wmOperatorType *ot);
|
||||
void RIGIDBODY_OT_object_remove(struct wmOperatorType *ot);
|
||||
|
||||
void RIGIDBODY_OT_objects_add(struct wmOperatorType *ot);
|
||||
void RIGIDBODY_OT_objects_remove(struct wmOperatorType *ot);
|
||||
|
||||
void RIGIDBODY_OT_shape_change(struct wmOperatorType *ot);
|
||||
void RIGIDBODY_OT_mass_calculate(struct wmOperatorType *ot);
|
||||
|
||||
/*rigidbody_world.c */
|
||||
void RIGIDBODY_OT_world_add(struct wmOperatorType *ot);
|
||||
void RIGIDBODY_OT_world_remove(struct wmOperatorType *ot);
|
||||
void RIGIDBODY_OT_world_export(struct wmOperatorType *ot);
|
||||
|
||||
#endif /* __PHYSICS_INTERN_H__ */
|
||||
|
@@ -86,6 +86,19 @@ static void operatortypes_particle(void)
|
||||
WM_operatortype_append(PARTICLE_OT_dupliob_remove);
|
||||
WM_operatortype_append(PARTICLE_OT_dupliob_move_up);
|
||||
WM_operatortype_append(PARTICLE_OT_dupliob_move_down);
|
||||
|
||||
WM_operatortype_append(RIGIDBODY_OT_object_add);
|
||||
WM_operatortype_append(RIGIDBODY_OT_object_remove);
|
||||
|
||||
WM_operatortype_append(RIGIDBODY_OT_objects_add);
|
||||
WM_operatortype_append(RIGIDBODY_OT_objects_remove);
|
||||
|
||||
WM_operatortype_append(RIGIDBODY_OT_shape_change);
|
||||
WM_operatortype_append(RIGIDBODY_OT_mass_calculate);
|
||||
|
||||
WM_operatortype_append(RIGIDBODY_OT_world_add);
|
||||
WM_operatortype_append(RIGIDBODY_OT_world_remove);
|
||||
// WM_operatortype_append(RIGIDBODY_OT_world_export);
|
||||
}
|
||||
|
||||
static void keymap_particle(wmKeyConfig *keyconf)
|
||||
|
622
source/blender/editors/physics/rigidbody_object.c
Normal file
622
source/blender/editors/physics/rigidbody_object.c
Normal file
@@ -0,0 +1,622 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2013 Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Joshua Leung, Sergej Reich
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file rigidbody_object.c
|
||||
* \ingroup editor_physics
|
||||
* \brief Rigid Body object editing operators
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_rigidbody_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_depsgraph.h"
|
||||
#include "BKE_group.h"
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_report.h"
|
||||
#include "BKE_rigidbody.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_enum_types.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "ED_physics.h"
|
||||
#include "ED_screen.h"
|
||||
|
||||
#include "physics_intern.h"
|
||||
|
||||
/* ********************************************** */
|
||||
/* Helper API's for RigidBody Objects Editing */
|
||||
|
||||
static int ED_operator_rigidbody_active_poll(bContext *C)
|
||||
{
|
||||
if (ED_operator_object_active_editable(C)) {
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
return (ob && ob->rigidbody_object);
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ED_operator_rigidbody_add_poll(bContext *C)
|
||||
{
|
||||
if (ED_operator_object_active_editable(C)) {
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
return (ob && ob->type == OB_MESH);
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ----------------- */
|
||||
|
||||
void ED_rigidbody_ob_add(wmOperator *op, Scene *scene, Object *ob, int type)
|
||||
{
|
||||
/* check that object doesn't already belong to the current simulation */
|
||||
if (ob->rigidbody_object) {
|
||||
BKE_reportf(op->reports, RPT_INFO, "Object '%s' already has a Rigid Body", ob->id.name + 2);
|
||||
return;
|
||||
}
|
||||
if (ob->type != OB_MESH) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Can't add Rigid Body to non mesh object");
|
||||
return;
|
||||
}
|
||||
if (((Mesh *)ob->data)->totpoly == 0) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Can't create Rigid Body from mesh with no polygons");
|
||||
return;
|
||||
}
|
||||
|
||||
/* make rigidbody object settings */
|
||||
ob->rigidbody_object = BKE_rigidbody_create_object(scene, ob, type);
|
||||
ob->rigidbody_object->flag |= RBO_FLAG_NEEDS_VALIDATE;
|
||||
}
|
||||
|
||||
void ED_rigidbody_ob_remove(Scene *scene, Object *ob)
|
||||
{
|
||||
BKE_rigidbody_remove_object(scene, ob);
|
||||
DAG_id_tag_update(&ob->id, OB_RECALC_OB);
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
/* Active Object Add/Remove Operators */
|
||||
|
||||
/* ************ Add Rigid Body ************** */
|
||||
|
||||
static int rigidbody_ob_add_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *ob = (scene) ? OBACT : NULL;
|
||||
int type = RNA_enum_get(op->ptr, "type");
|
||||
|
||||
/* apply to active object */
|
||||
ED_rigidbody_ob_add(op, scene, ob, type);
|
||||
|
||||
/* send updates */
|
||||
DAG_ids_flush_update(CTX_data_main(C), 0);
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
|
||||
WM_event_add_notifier(C, NC_GROUP | NA_EDITED, NULL);
|
||||
|
||||
/* done */
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void RIGIDBODY_OT_object_add(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->idname = "RIGIDBODY_OT_object_add";
|
||||
ot->name = "Add Rigid Body";
|
||||
ot->description = "Add active object as Rigid Body";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = rigidbody_ob_add_exec;
|
||||
ot->poll = ED_operator_rigidbody_add_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
ot->prop = RNA_def_enum(ot->srna, "type", rigidbody_ob_type_items, RBO_TYPE_ACTIVE, "Rigid Body Type", "");
|
||||
}
|
||||
|
||||
/* ************ Remove Rigid Body ************** */
|
||||
|
||||
static int rigidbody_ob_remove_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *ob = (scene) ? OBACT : NULL;
|
||||
|
||||
/* sanity checks */
|
||||
if (scene == NULL)
|
||||
return OPERATOR_CANCELLED;
|
||||
|
||||
/* apply to active object */
|
||||
if (ELEM(NULL, ob, ob->rigidbody_object)) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Object has no Rigid Body settings to remove");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
else
|
||||
ED_rigidbody_ob_remove(scene, ob);
|
||||
|
||||
/* send updates */
|
||||
DAG_ids_flush_update(CTX_data_main(C), 0);
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
|
||||
WM_event_add_notifier(C, NC_GROUP | NA_EDITED, NULL);
|
||||
|
||||
/* done */
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void RIGIDBODY_OT_object_remove(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->idname = "RIGIDBODY_OT_object_remove";
|
||||
ot->name = "Remove Rigid Body";
|
||||
ot->description = "Remove Rigid Body settings from Object";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = rigidbody_ob_remove_exec;
|
||||
ot->poll = ED_operator_rigidbody_active_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
/* Selected Object Add/Remove Operators */
|
||||
|
||||
/* ************ Add Rigid Bodies ************** */
|
||||
|
||||
static int rigidbody_obs_add_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene);
|
||||
int type = RNA_enum_get(op->ptr, "type");
|
||||
|
||||
/* sanity check */
|
||||
if (scene == NULL) {
|
||||
BKE_report(op->reports, RPT_ERROR, "No Scene to add Rigid Bodies to");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
/* Add rigid body world and group if they don't exist for convenience */
|
||||
if (rbw == NULL) {
|
||||
rbw = BKE_rigidbody_create_world(scene);
|
||||
BKE_rigidbody_validate_sim_world(scene, rbw, false);
|
||||
scene->rigidbody_world = rbw;
|
||||
}
|
||||
if (rbw->group == NULL) {
|
||||
rbw->group = add_group("RigidBodyWorld");
|
||||
}
|
||||
/* create rigid body objects and add them to the world's group */
|
||||
CTX_DATA_BEGIN(C, Object *, ob, selected_objects) {
|
||||
ED_rigidbody_ob_add(op, scene, ob, type);
|
||||
add_to_group(rbw->group, ob, scene, NULL);
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
/* send updates */
|
||||
DAG_ids_flush_update(CTX_data_main(C), 0);
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
|
||||
WM_event_add_notifier(C, NC_GROUP | NA_EDITED, NULL);
|
||||
|
||||
/* done */
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void RIGIDBODY_OT_objects_add(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->idname = "RIGIDBODY_OT_objects_add";
|
||||
ot->name = "Add Rigid Bodies";
|
||||
ot->description = "Add selected objects as Rigid Bodies";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = rigidbody_obs_add_exec;
|
||||
ot->poll = ED_operator_rigidbody_add_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
ot->prop = RNA_def_enum(ot->srna, "type", rigidbody_ob_type_items, RBO_TYPE_ACTIVE, "Rigid Body Type", "");
|
||||
}
|
||||
|
||||
/* ************ Remove Rigid Bodies ************** */
|
||||
|
||||
static int rigidbody_obs_remove_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene);
|
||||
|
||||
/* sanity checks */
|
||||
if (scene == NULL)
|
||||
return OPERATOR_CANCELLED;
|
||||
|
||||
/* apply this to all selected objects... */
|
||||
CTX_DATA_BEGIN(C, Object *, ob, selected_objects)
|
||||
{
|
||||
if (ob->rigidbody_object) {
|
||||
ED_rigidbody_ob_remove(scene, ob);
|
||||
if (rbw)
|
||||
rem_from_group(rbw->group, ob, scene, NULL);
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
/* send updates */
|
||||
DAG_ids_flush_update(CTX_data_main(C), 0);
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
|
||||
WM_event_add_notifier(C, NC_GROUP | NA_EDITED, NULL);
|
||||
|
||||
/* done */
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void RIGIDBODY_OT_objects_remove(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->idname = "RIGIDBODY_OT_objects_remove";
|
||||
ot->name = "Remove Rigid Bodies";
|
||||
ot->description = "Remove selected objects from Rigid Body simulation";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = rigidbody_obs_remove_exec;
|
||||
ot->poll = ED_operator_rigidbody_active_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
/* Utility Operators */
|
||||
|
||||
/* ************ Change Collision Shapes ************** */
|
||||
|
||||
static int rigidbody_obs_shape_change_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
int shape = RNA_enum_get(op->ptr, "type");
|
||||
|
||||
/* sanity checks */
|
||||
if (scene == NULL)
|
||||
return OPERATOR_CANCELLED;
|
||||
|
||||
/* apply this to all selected objects... */
|
||||
CTX_DATA_BEGIN(C, Object *, ob, selected_objects)
|
||||
{
|
||||
if (ob->rigidbody_object) {
|
||||
PointerRNA ptr;
|
||||
|
||||
/* use RNA-system to change the property and perform all necessary changes */
|
||||
RNA_pointer_create(&ob->id, &RNA_RigidBodyObject, ob->rigidbody_object, &ptr);
|
||||
RNA_enum_set(&ptr, "collision_shape", shape);
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
/* send updates */
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); // XXX: wrong notifiers for now, but these also do the job...
|
||||
|
||||
/* done */
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void RIGIDBODY_OT_shape_change(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->idname = "RIGIDBODY_OT_shape_change";
|
||||
ot->name = "Change Collision Shape";
|
||||
ot->description = "Change collision shapes for selected Rigid Body Objects";
|
||||
|
||||
/* callbacks */
|
||||
ot->invoke = WM_menu_invoke;
|
||||
ot->exec = rigidbody_obs_shape_change_exec;
|
||||
ot->poll = ED_operator_rigidbody_active_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
ot->prop = RNA_def_enum(ot->srna, "type", rigidbody_ob_shape_items, RB_SHAPE_TRIMESH, "Rigid Body Shape", "");
|
||||
}
|
||||
|
||||
/* ************ Calculate Mass ************** */
|
||||
|
||||
/* Entry in material density table */
|
||||
typedef struct rbMaterialDensityItem {
|
||||
const char *name; /* Name of material */
|
||||
float density; /* Density (kg/m^3) */
|
||||
} rbMaterialDensityItem;
|
||||
|
||||
/* Preset density values for materials (kg/m^3)
|
||||
* Selected values obtained from:
|
||||
* 1) http://www.jaredzone.info/2010/09/densities.html
|
||||
* 2) http://www.avlandesign.com/density_construction.htm
|
||||
* 3) http://www.avlandesign.com/density_metal.htm
|
||||
*/
|
||||
static rbMaterialDensityItem RB_MATERIAL_DENSITY_TABLE[] = {
|
||||
{"Air", 1.0f}, /* not quite; adapted from 1.43 for oxygen for use as default */
|
||||
{"Acrylic", 1400.0f},
|
||||
{"Asphalt (Crushed)", 721.0f},
|
||||
{"Bark", 240.0f},
|
||||
{"Beans (Cocoa)", 593.0f},
|
||||
{"Beans (Soy)", 721.0f},
|
||||
{"Brick (Pressed)", 2400.0f},
|
||||
{"Brick (Common)", 2000.0f},
|
||||
{"Brick (Soft)", 1600.0f},
|
||||
{"Brass", 8216.0f},
|
||||
{"Bronze", 8860.0f},
|
||||
{"Carbon (Solid)", 2146.0f},
|
||||
{"Cardboard", 689.0f},
|
||||
{"Cast Iron", 7150.0f},
|
||||
//{"Cement", 1442.0f},
|
||||
{"Chalk (Solid)", 2499.0f},
|
||||
//{"Coffee (Fresh/Roast)", ~500},
|
||||
{"Concrete", 2320.0f},
|
||||
{"Charcoal", 208.0f},
|
||||
{"Cork", 240.0f},
|
||||
{"Copper", 8933.0f},
|
||||
{"Garbage", 481.0f},
|
||||
{"Glass (Broken)", 1940.0f},
|
||||
{"Glass (Solid)", 2190.0f},
|
||||
{"Gold", 19282.0f},
|
||||
{"Granite (Broken)", 1650.0f},
|
||||
{"Granite (Solid)", 2691.0f},
|
||||
{"Gravel", 2780.0f},
|
||||
{"Ice (Crushed)", 593.0f},
|
||||
{"Ice (Solid)", 919.0f},
|
||||
{"Iron", 7874.0f},
|
||||
{"Lead", 11342.0f},
|
||||
{"Limestone (Broken)", 1554.0f},
|
||||
{"Limestone (Solid)", 2611.0f},
|
||||
{"Marble (Broken)", 1570.0f},
|
||||
{"Marble (Solid)", 2563.0f},
|
||||
{"Paper", 1201.0f},
|
||||
{"Peanuts (Shelled)", 641.0f},
|
||||
{"Peanuts (Not Shelled)", 272.0f},
|
||||
{"Plaster", 849.0f},
|
||||
{"Plastic", 1200.0f},
|
||||
{"Polystyrene", 1050.0f},
|
||||
{"Rubber", 1522.0f},
|
||||
{"Silver", 10501.0f},
|
||||
{"Steel", 7860.0f},
|
||||
{"Stone", 2515.0f},
|
||||
{"Stone (Crushed)", 1602.0f},
|
||||
{"Timber", 610.0f}
|
||||
};
|
||||
static const int NUM_RB_MATERIAL_PRESETS = sizeof(RB_MATERIAL_DENSITY_TABLE) / sizeof(rbMaterialDensityItem);
|
||||
|
||||
|
||||
/* dynamically generate list of items
|
||||
* - Although there is a runtime cost, this has a lower maintenance cost
|
||||
* in the long run than other two-list solutions...
|
||||
*/
|
||||
static EnumPropertyItem *rigidbody_materials_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), int *free)
|
||||
{
|
||||
EnumPropertyItem item_tmp = {0};
|
||||
EnumPropertyItem *item = NULL;
|
||||
int totitem = 0;
|
||||
int i = 0;
|
||||
|
||||
/* add each preset to the list */
|
||||
for (i = 0; i < NUM_RB_MATERIAL_PRESETS; i++) {
|
||||
rbMaterialDensityItem *preset = &RB_MATERIAL_DENSITY_TABLE[i];
|
||||
|
||||
item_tmp.identifier = item_tmp.name = preset->name;
|
||||
item_tmp.value = i;
|
||||
RNA_enum_item_add(&item, &totitem, &item_tmp);
|
||||
}
|
||||
|
||||
/* add special "custom" entry to the end of the list */
|
||||
{
|
||||
item_tmp.identifier = item_tmp.name = "Custom";
|
||||
item_tmp.value = -1;
|
||||
RNA_enum_item_add(&item, &totitem, &item_tmp);
|
||||
}
|
||||
|
||||
RNA_enum_item_end(&item, &totitem);
|
||||
*free = 1;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/* ------------------------------------------ */
|
||||
|
||||
/* helper function to calculate volume of rigidbody object */
|
||||
// TODO: allow a parameter to specify method used to calculate this?
|
||||
static float calc_rigidbody_ob_volume(Object *ob)
|
||||
{
|
||||
RigidBodyOb *rbo = ob->rigidbody_object;
|
||||
|
||||
float size[3] = {1.0f, 1.0f, 1.0f};
|
||||
float radius = 1.0f;
|
||||
float height = 1.0f;
|
||||
|
||||
float volume = 0.0f;
|
||||
|
||||
/* if automatically determining dimensions, use the Object's boundbox
|
||||
* - assume that all quadrics are standing upright on local z-axis
|
||||
* - assume even distribution of mass around the Object's pivot
|
||||
* (i.e. Object pivot is centralised in boundbox)
|
||||
* - boundbox gives full width
|
||||
*/
|
||||
// XXX: all dimensions are auto-determined now... later can add stored settings for this
|
||||
BKE_object_dimensions_get(ob, size);
|
||||
|
||||
if (ELEM3(rbo->shape, RB_SHAPE_CAPSULE, RB_SHAPE_CYLINDER, RB_SHAPE_CONE)) {
|
||||
/* take radius as largest x/y dimension, and height as z-dimension */
|
||||
radius = MAX2(size[0], size[1]) * 0.5f;
|
||||
height = size[2];
|
||||
}
|
||||
else if (rbo->shape == RB_SHAPE_SPHERE) {
|
||||
/* take radius to the the largest dimension to try and encompass everything */
|
||||
radius = max_fff(size[0], size[1], size[2]) * 0.5f;
|
||||
}
|
||||
|
||||
/* calculate volume as appropriate */
|
||||
switch (rbo->shape) {
|
||||
case RB_SHAPE_BOX:
|
||||
volume = size[0] * size[1] * size[2];
|
||||
break;
|
||||
|
||||
case RB_SHAPE_SPHERE:
|
||||
volume = 4.0f / 3.0f * (float)M_PI * radius * radius * radius;
|
||||
break;
|
||||
|
||||
/* for now, assume that capsule is close enough to a cylinder... */
|
||||
case RB_SHAPE_CAPSULE:
|
||||
case RB_SHAPE_CYLINDER:
|
||||
volume = (float)M_PI * radius * radius * height;
|
||||
break;
|
||||
|
||||
case RB_SHAPE_CONE:
|
||||
volume = (float)M_PI / 3.0f * radius * radius * height;
|
||||
break;
|
||||
|
||||
/* for now, all mesh shapes are just treated as boxes...
|
||||
* NOTE: this may overestimate the volume, but other methods are overkill
|
||||
*/
|
||||
case RB_SHAPE_CONVEXH:
|
||||
case RB_SHAPE_TRIMESH:
|
||||
volume = size[0] * size[1] * size[2];
|
||||
break;
|
||||
|
||||
#if 0 // XXX: not defined yet
|
||||
case RB_SHAPE_COMPOUND:
|
||||
volume = 0.0f;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* return the volume calculated */
|
||||
return volume;
|
||||
}
|
||||
|
||||
/* ------------------------------------------ */
|
||||
|
||||
static int rigidbody_obs_calc_mass_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
int material = RNA_enum_get(op->ptr, "material");
|
||||
float density;
|
||||
|
||||
/* sanity checks */
|
||||
if (scene == NULL)
|
||||
return OPERATOR_CANCELLED;
|
||||
|
||||
/* get density (kg/m^3) to apply */
|
||||
if (material >= 0) {
|
||||
/* get density from table, and store in props for later repeating */
|
||||
if (material >= NUM_RB_MATERIAL_PRESETS)
|
||||
material = 0;
|
||||
|
||||
density = RB_MATERIAL_DENSITY_TABLE[material].density;
|
||||
RNA_float_set(op->ptr, "density", density);
|
||||
}
|
||||
else {
|
||||
/* custom - grab from whatever value is set */
|
||||
density = RNA_float_get(op->ptr, "density");
|
||||
}
|
||||
|
||||
/* apply this to all selected objects (with rigidbodies)... */
|
||||
CTX_DATA_BEGIN(C, Object *, ob, selected_objects)
|
||||
{
|
||||
if (ob->rigidbody_object) {
|
||||
PointerRNA ptr;
|
||||
|
||||
float volume; /* m^3 */
|
||||
float mass; /* kg */
|
||||
|
||||
/* mass is calculated from the approximate volume of the object,
|
||||
* and the density of the material we're simulating
|
||||
*/
|
||||
volume = calc_rigidbody_ob_volume(ob);
|
||||
mass = volume * density;
|
||||
|
||||
/* use RNA-system to change the property and perform all necessary changes */
|
||||
RNA_pointer_create(&ob->id, &RNA_RigidBodyObject, ob->rigidbody_object, &ptr);
|
||||
RNA_float_set(&ptr, "mass", mass);
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
/* send updates */
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); // XXX: wrong notifiers for now, but these also do the job...
|
||||
|
||||
/* done */
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void RIGIDBODY_OT_mass_calculate(wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
/* identifiers */
|
||||
ot->idname = "RIGIDBODY_OT_mass_calculate";
|
||||
ot->name = "Calculate Mass";
|
||||
ot->description = "Automatically calculate mass values for Rigid Body Objects based on volume";
|
||||
|
||||
/* callbacks */
|
||||
ot->invoke = WM_menu_invoke; // XXX
|
||||
ot->exec = rigidbody_obs_calc_mass_exec;
|
||||
ot->poll = ED_operator_rigidbody_active_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
ot->prop = prop = RNA_def_enum(ot->srna, "material",
|
||||
DummyRNA_DEFAULT_items, 0,
|
||||
"Material Preset",
|
||||
"Type of material that objects are made of. "
|
||||
"Determines material density");
|
||||
RNA_def_enum_funcs(prop, rigidbody_materials_itemf);
|
||||
|
||||
RNA_def_float(ot->srna, "density", 1.0, FLT_MIN, FLT_MAX,
|
||||
"Density",
|
||||
"Custom density value (kg/m^3) to use instead of material preset",
|
||||
1.0f, 2500.0f);
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
210
source/blender/editors/physics/rigidbody_world.c
Normal file
210
source/blender/editors/physics/rigidbody_world.c
Normal file
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2013 Blender Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Joshua Leung, Sergej Reich
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file rigidbody_world.c
|
||||
* \ingroup editor_physics
|
||||
* \brief Rigid Body world editing operators
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_rigidbody_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "RBI_api.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_depsgraph.h"
|
||||
#include "BKE_group.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_report.h"
|
||||
#include "BKE_rigidbody.h"
|
||||
#include "BKE_utildefines.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_enum_types.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "ED_physics.h"
|
||||
#include "ED_screen.h"
|
||||
|
||||
#include "physics_intern.h"
|
||||
|
||||
/* ********************************************** */
|
||||
/* API */
|
||||
|
||||
/* check if there is an active rigid body world */
|
||||
static int ED_rigidbody_world_active_poll(bContext *C)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
return (scene && scene->rigidbody_world);
|
||||
}
|
||||
static int ED_rigidbody_world_add_poll(bContext *C)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
return (scene && scene->rigidbody_world == NULL);
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
/* OPERATORS - Management */
|
||||
|
||||
/* ********** Add RigidBody World **************** */
|
||||
|
||||
static int rigidbody_world_add_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
RigidBodyWorld *rbw;
|
||||
|
||||
rbw = BKE_rigidbody_create_world(scene);
|
||||
// BKE_rigidbody_validate_sim_world(scene, rbw, false);
|
||||
scene->rigidbody_world = rbw;
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void RIGIDBODY_OT_world_add(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->idname = "RIGIDBODY_OT_world_add";
|
||||
ot->name = "Add Rigid Body World";
|
||||
ot->description = "Add Rigid Body simulation world to the current scene";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = rigidbody_world_add_exec;
|
||||
ot->poll = ED_rigidbody_world_add_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/* ********** Remove RigidBody World ************* */
|
||||
|
||||
static int rigidbody_world_remove_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
RigidBodyWorld *rbw = scene->rigidbody_world;
|
||||
|
||||
/* sanity checks */
|
||||
if (ELEM(NULL, scene, rbw)) {
|
||||
BKE_report(op->reports, RPT_ERROR, "No Rigid Body World to remove");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
BKE_rigidbody_free_world(rbw);
|
||||
scene->rigidbody_world = NULL;
|
||||
|
||||
/* send updates */
|
||||
WM_event_add_notifier(C, NC_GROUP | NA_EDITED, NULL);
|
||||
|
||||
/* done */
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void RIGIDBODY_OT_world_remove(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->idname = "RIGIDBODY_OT_world_remove";
|
||||
ot->name = "Remove Rigid Body World";
|
||||
ot->description = "Remove Rigid Body simulation world from the current scene";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = rigidbody_world_remove_exec;
|
||||
ot->poll = ED_rigidbody_world_active_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
/* UTILITY OPERATORS */
|
||||
|
||||
/* ********** Export RigidBody World ************* */
|
||||
|
||||
static int rigidbody_world_export_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
RigidBodyWorld *rbw = scene->rigidbody_world;
|
||||
char path[FILE_MAX];
|
||||
|
||||
/* sanity checks */
|
||||
if ELEM(NULL, scene, rbw) {
|
||||
BKE_report(op->reports, RPT_ERROR, "No Rigid Body World to export");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
if (rbw->physics_world == NULL) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Rigid Body World has no associated physics data to export");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
RNA_string_get(op->ptr, "filepath", path);
|
||||
RB_dworld_export(rbw->physics_world, path);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int rigidbody_world_export_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(evt))
|
||||
{
|
||||
if (!RNA_struct_property_is_set(op->ptr, "relative_path"))
|
||||
RNA_boolean_set(op->ptr, "relative_path", (U.flag & USER_RELPATHS));
|
||||
|
||||
if (RNA_struct_property_is_set(op->ptr, "filepath"))
|
||||
return rigidbody_world_export_exec(C, op);
|
||||
|
||||
// TODO: use the actual rigidbody world's name + .bullet instead of this temp crap
|
||||
RNA_string_set(op->ptr, "filepath", "rigidbodyworld_export.bullet");
|
||||
WM_event_add_fileselect(C, op);
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
void RIGIDBODY_OT_world_export(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->idname = "RIGIDBODY_OT_world_export";
|
||||
ot->name = "Export Rigid Body World";
|
||||
ot->description = "Export Rigid Body world to simulator's own fileformat (i.e. '.bullet' for Bullet Physics)";
|
||||
|
||||
/* callbacks */
|
||||
ot->invoke = rigidbody_world_export_invoke;
|
||||
ot->exec = rigidbody_world_export_exec;
|
||||
ot->poll = ED_rigidbody_world_active_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
WM_operator_properties_filesel(ot, FOLDERFILE, FILE_SPECIAL, FILE_SAVE, FILE_RELPATH, FILE_DEFAULTDISPLAY);
|
||||
}
|
Reference in New Issue
Block a user