%% code created by Moth
-module(wpc_rig_anim).
-export([init/0,menu/2,command/2]).
-define(NEED_OPENGL,1).
-define(NEED_ESDL,1).
-include("wings.hrl").
%%
%% Functions from wings_pstate.erl (after big modifications)
%% ( taken from http://p212.ezboard.com/fnendowingsmiraifrm8.showMessage?topicID=399.topic )
%%
pst_get(Default, #st{pst=PSts}) ->
case gb_trees:lookup(?MODULE,PSts) of
{value, Value} ->
Value;
none ->
Default
end.
pst_set(PSt, #st{pst=PSts0}=St) ->
PSts = gb_trees:enter(?MODULE, PSt, PSts0),
St#st{pst=PSts}.
pst_refresh(St) ->
wings_wm:send(geom,{new_state,St}).
%%
%% Rigging Records
%%
-record(rig_anim_rig,{
joints,
next_joint_id=1
}).
-record(rig_anim_joint,{
joint_id,
parent_joint_id,
name,
x,
y,
z,
size = 0.125,
verticies % gb_tree of influences (0.0 - 1.0) keyed of {MeshID,VertexID}
}).
%%
%% Animation Records
%%
-record(rig_anim_anim,{
seqs,
next_seq_id=1
}).
-record(rig_anim_seq,{
seq_id,
name,
frames
}).
-record(rig_anim_frame,{
joint_states,
time
}).
-record(rig_anim_joint_state,{
joint_id,
x,
y,
z,
xa,
ya,
za
}).
%%
%% State Record
%%
-record(rig_anim_state,{
mode,
shapes,
rig,
anim,
active_joint_id=0,
active_seq_id=0,
active_frame_id=0
}).
%%
%% Module Init
%%
init() ->
true.
%%
%% Menu Additions
%%
menu({window},Menu) ->
Menu ++ [
separator,
{"Rigging & Animation Window",rig_anim_command,"Open a Rigging & Animation window"}
];
menu(_, Menu) ->
Menu.
%%
%% Command Additions For Menu Additions
%%
command({window,rig_anim_command},St) ->
rig_anim_start(St);
command(_,_) ->
next.
%%
%% Startup
%%
rig_anim_start(St) ->
Win = {rig_anim},
WinExists = wings_wm:is_window(Win),
if
WinExists ->
wings_wm:raise(Win);
true ->
rig_anim_create_window(Win,St)
end.
rig_anim_create_window(Win,St) ->
Title = "Rigging & Animation",
Op = {replace,fun(Ev) -> rig_anim_event(Ev,St) end},
%{X,Y,W,H} = init_drawarea(),
{X,Y,W,H} = {100,100,800,600},
Props = [{display_lists,Win}|wings_view:initial_properties()],
CreateToolbar = fun(N,P,Wi) -> wings_toolbar:create(N,P,Wi) end,
wings_wm:toplevel(
Win,
Title,
{X,Y,highest},
{W,H},
[
resizable,
closable,
menubar,
{properties,Props},
{toolbar,CreateToolbar}
],
Op
),
wings_wm:send(Win,{init}).
rig_anim_init(St) ->
Modes = [vertex,body],
wings:mode_restriction(Modes),
Win = wings_wm:this(),
Allowed = [view],
Menu = [
Item || {_,Name,_} = Item <- get(wings_menu_template),
lists:member(Name,Allowed)
],
wings_wm:menubar(Win,Menu),
wings_wm:set_prop(show_info_text,true),
St1 = rig_anim_add_material(rig_anim_joint, {0.0,1.0,0.0},
rig_anim_add_material(rig_anim_joint_active,{1.0,1.0,0.0},St)),
Empty = gb_trees:empty(),
DefaultRigAnimState = #rig_anim_state{
mode=vertex,
rig=#rig_anim_rig{
joints=Empty
},
anim=#rig_anim_anim{
seqs=Empty
}
},
#rig_anim_state{mode=Mode}=RigAnimState = pst_get(DefaultRigAnimState,St1),
rig_anim_setup(St1#st{sel=[],selmode=Mode},RigAnimState).
rig_anim_setup(St,RigAnimState) ->
{St1,RigAnimState1} = rig_anim_setup_joints(St,RigAnimState),
{St2,RigAnimState2} = rig_anim_setup_shapes(St1,RigAnimState1),
pst_refresh(pst_set(RigAnimState2,St)),
Win = wings_wm:this(),
wings:register_postdraw_hook(Win,?MODULE,
fun(DSt) ->
rig_anim_draw_rig(DSt,RigAnimState2)
end
),
wings_wm:current_state(St2),
wings_draw:refresh_dlists(St2),
wings_wm:dirty(),
{replace,
fun(Ev) ->
rig_anim_event(Ev,St2,RigAnimState2)
end
}.
%
% Setup Shapes
%
rig_anim_setup_shapes(#st{shapes=Shapes}=St,RigAnimState) ->
rig_anim_setup_shape(gb_trees:iterator(Shapes),St,RigAnimState).
rig_anim_setup_shape(It,#st{shapes=Shapes}=St,#rig_anim_state{rig=#rig_anim_rig{joints=Joints},mode=Mode}=RigAnimState) ->
case gb_trees:next(It) of
{_,#we{id=Id,name=Name}=We,It1} ->
Prefix = lists:sublist(Name,6),
if
Prefix == "joint_" ->
JointID = list_to_integer(lists:sublist(Name,7,(length(Name) - 6))),
Keep = gb_trees:is_defined(JointID,Joints),
if
Mode == body ->
Perm = 0;
true ->
Perm = 1
end;
true ->
Keep = true,
if
Mode == body ->
Perm = 1;
true ->
Perm = 0
end
end,
if
Keep ->
rig_anim_setup_shape(It1,St#st{shapes=gb_trees:update(Id,We#we{perm=Perm},Shapes)},RigAnimState);
true ->
rig_anim_setup_shape(It1,St#st{shapes=gb_trees:delete(Id,Shapes)},RigAnimState)
end;
none ->
{St,RigAnimState}
end.
%
% Setup Joints
%
rig_anim_setup_joints(#st{shapes=Shapes,selmode=Mode,sel=Sel}=St,#rig_anim_state{rig=#rig_anim_rig{joints=Joints}=Rig,active_joint_id=ActiveJointID}=RigAnimState) ->
if
(Mode == body) and (length(Sel) > 0) ->
[Item|Rest] = Sel,
case Item of
{Id,_} ->
#we{name=Name} = gb_trees:get(Id,Shapes),
Prefix = lists:sublist(Name,6),
if
Prefix == "joint_" ->
JointID = list_to_integer(lists:sublist(Name,7,(length(Name) - 6))),
ActiveJointID1 = JointID;
true ->
ActiveJointID1 = ActiveJointID
end;
true ->
ActiveJointID1 = ActiveJointID
end;
true ->
ActiveJointID1 = ActiveJointID
end,
Valid = gb_trees:is_defined(ActiveJointID1,Joints),
if
Valid ->
ActiveJointID2 = ActiveJointID1;
true ->
ActiveJointID2 = 0
end,
rig_anim_setup_joint(gb_trees:iterator(Joints),St,RigAnimState#rig_anim_state{active_joint_id=ActiveJointID2}).
rig_anim_setup_joint(It,#st{shapes=Shapes}=St,#rig_anim_state{rig=#rig_anim_rig{joints=Joints}=Rig,active_joint_id=ActiveJointID}=RigAnimState) ->
case gb_trees:next(It) of
{_,#rig_anim_joint{joint_id=JointID,parent_joint_id=ParentJointID,x=X,y=Y,z=Z,size=Size}=Joint,It1} ->
Name = "joint_" ++ integer_to_list(JointID),
case rig_anim_get_shape_by_name(Name,St) of
none ->
Vs1 = [
{(X + Size),(Y + 0.0),(Z + Size)},
{(X + -Size),(Y + 0.0),(Z + -Size)},
{(X + 0.0),(Y + Size),(Z + 0.0)},
{(X + 0.0),(Y + -Size),(Z + 0.0)},
{(X + -Size),(Y + 0.0),(Z + Size)},
{(X + Size),(Y + 0.0),(Z + -Size)}
],
Fs1 = [
[2,4,0],
[4,2,1],
[4,3,0],
[3,4,1],
[5,2,0],
[2,5,1],
[3,5,0],
[5,3,1]
],
We1 = wings_we:build(Fs1,Vs1),
#st{shapes=Shapes1}=St1 = wings_shape:new(Name,We1,St),
#we{id=Id,fs=Fs}=We = rig_anim_get_shape_by_name(Name,St1),
RigAnimState1 = RigAnimState;
#we{id=Id,fs=Fs}=We ->
#st{shapes=Shapes1}=St1 = St,
{CX,CY,CZ} = wings_vertex:center(We),
if
{X,Y,Z} == {CX,CY,CZ} ->
RigAnimState1 = RigAnimState;
true ->
RigAnimState1 = RigAnimState#rig_anim_state{
rig=Rig#rig_anim_rig{
joints=gb_trees:update(
JointID,
Joint#rig_anim_joint{
x=CX,
y=CY,
z=CZ
},
Joints
)
}
}
end
end,
Valid = gb_trees:is_defined(ParentJointID,Joints),
if
Valid ->
RigAnimState2 = RigAnimState1;
true ->
#rig_anim_state{rig=#rig_anim_rig{joints=Joints1}=Rig1} = RigAnimState1,
Joint1 = gb_trees:get(JointID,Joints1),
RigAnimState2 = RigAnimState1#rig_anim_state{
rig=Rig1#rig_anim_rig{
joints=gb_trees:update(
JointID,
Joint1#rig_anim_joint{parent_joint_id=0},
Joints1
)
}
}
end,
if
JointID == ActiveJointID ->
Mat = rig_anim_joint_active;
true ->
Mat = rig_anim_joint
end,
We2 = wings_facemat:assign(Mat,gb_trees:keys(Fs),We),
St2 = St1#st{shapes=gb_trees:update(Id,We2,Shapes1)},
rig_anim_setup_joint(It1,St2,RigAnimState2);
none ->
{St,RigAnimState}
end.
%%
%% Custom Draw
%%
rig_anim_draw_rig(#st{shapes=Shapes}=St,#rig_anim_state{rig=#rig_anim_rig{joints=Joints}=Rig,active_joint_id=ActiveJointID}=RigAnimState) ->
% Setup
gl:pushAttrib(?GL_ALL_ATTRIB_BITS),
wings_view:load_matrices(true),
gl:enable(?GL_DEPTH_TEST),
gl:depthFunc(?GL_LESS),
gl:blendFunc(?GL_SRC_ALPHA,?GL_ONE_MINUS_SRC_ALPHA),
gl:enable(?GL_BLEND),
Fs = [
{2,4,0},
{4,2,1},
{4,3,0},
{3,4,1},
{5,2,0},
{2,5,1},
{3,5,0},
{5,3,1}
],
gl:color4f(0.0,0.5,0.5,0.5),
gl:lineWidth(2),
gl:'begin'(?GL_LINES),
lists:foreach(
fun(#rig_anim_joint{x=X1,y=Y1,z=Z1,size=Size1,parent_joint_id=ParentJointID}=Joint1) ->
if
ParentJointID > 0 ->
#rig_anim_joint{x=X2,y=Y2,z=Z2,size=Size2}=Joint2 = gb_trees:get(ParentJointID,Joints),
Vs = rig_anim_bone_verticies({X1,Y1,Z1},{X2,Y2,Z2},Size2),
lists:foreach(
fun({VI1,VI2,VI3}) ->
{VX1,VY1,VZ1} = lists:nth(VI1+1,Vs),
{VX2,VY2,VZ2} = lists:nth(VI2+1,Vs),
{VX3,VY3,VZ3} = lists:nth(VI3+1,Vs),
gl:vertex3f(VX1,VY1,VZ1),
gl:vertex3f(VX2,VY2,VZ2),
gl:vertex3f(VX2,VY2,VZ2),
gl:vertex3f(VX3,VY3,VZ3),
gl:vertex3f(VX3,VY3,VZ3),
gl:vertex3f(VX1,VY1,VZ1)
end
,Fs);
true ->
true
end
end
,gb_trees:values(Joints)),
gl:'end'(),
if
ActiveJointID > 0 ->
gl:pointSize(10),
gl:'begin'(?GL_POINTS),
#rig_anim_joint{verticies=Verticies} = gb_trees:get(ActiveJointID,Joints),
lists:foreach(
fun({MeshID,VertexID}) ->
#we{vp=Vp} = gb_trees:get(MeshID,Shapes),
{X,Y,Z} = gb_trees:get(VertexID,Vp),
Influence = gb_trees:get({MeshID,VertexID},Verticies),
gl:color4f(0.0,(Influence + 0.0),(1.0 - Influence),0.5),
gl:vertex3f(X,Y,Z)
end
,gb_trees:keys(Verticies)),
gl:'end'(),
gl:pointSize(12),
gl:color4f(0.0,0.0,0.0,1.0),
gl:'begin'(?GL_POINTS),
#rig_anim_joint{verticies=Verticies} = gb_trees:get(ActiveJointID,Joints),
lists:foreach(
fun({MeshID,VertexID}) ->
#we{vp=Vp} = gb_trees:get(MeshID,Shapes),
{X,Y,Z} = gb_trees:get(VertexID,Vp),
gl:vertex3f(X,Y,Z)
end
,gb_trees:keys(Verticies)),
gl:'end'();
true ->
nothing
end,
% Put things back the way we found them
gl:popAttrib().
rig_anim_bone_verticies({X1,Y1,Z1}=P1,{X2,Y2,Z2}=P2,Size) ->
{X3,Y3,Z3}=P3 = {
(X2 + ((X1 - X2) / 4)),
(Y2 + ((Y1 - Y2) / 4)),
(Z2 + ((Z1 - Z2) / 4))
},
if
{X1,Z1} == {X2,Z2} ->
P4 = {(X1 + 1.0) + 1000000000.0,(Y1 + 0.0) + 1000000000.0,(Z1 + 1.0) + 1000000000.0};
Y1 == Y2 ->
P4 = {(X1 + 0.0) + 1000000000.0,(Y1 + 1.0) + 1000000000.0,(Z1 + 0.0) + 1000000000.0};
true ->
P4 = {(X1 + 0.0) + 1000000000.0,(Y2 + 0.0) + 1000000000.0,(Z1 + 0.0) + 1000000000.0}
end,
P1P2 = rig_anim_fix_vector(e3d_vec:sub(P1,P2)),
{RX,RY,RZ}=R = e3d_vec:norm(e3d_vec:cross(P1P2,P4)),
{SX,SY,SZ}=S = e3d_vec:norm(e3d_vec:cross(P1P2,R)),
if
Y2 > Y1 ->
[
rig_anim_plane_point(P3,R,S,Size,Size),
rig_anim_plane_point(P3,R,S,-Size,-Size),
{(X2 + 0.0),(Y2 + 0.0),(Z2 + 0.0)},
{(X1 + 0.0),(Y1 + 0.0),(Z1 + 0.0)},
rig_anim_plane_point(P3,R,S,-Size,Size),
rig_anim_plane_point(P3,R,S,Size,-Size)
];
true ->
[
rig_anim_plane_point(P3,R,S,-Size,Size),
rig_anim_plane_point(P3,R,S,Size,-Size),
{(X1 + 0.0),(Y1 + 0.0),(Z1 + 0.0)},
{(X2 + 0.0),(Y2 + 0.0),(Z2 + 0.0)},
rig_anim_plane_point(P3,R,S,Size,Size),
rig_anim_plane_point(P3,R,S,-Size,-Size)
]
end.
rig_anim_fix_vector({X,Y,Z}) ->
{X + 0.0,Y + 0.0,Z + 0.0}.
rig_anim_plane_point({OX,OY,OZ},{RX,RY,RZ},{SX,SY,SZ},A,B) ->
{
(OX + (A * RX) + (B * SX)),
(OY + (A * RY) + (B * SY)),
(OZ + (A * RZ) + (B * SZ))
}.
%%
%% Events
%%
rig_anim_event(Ev,St) ->
rig_anim_event(Ev,St,#rig_anim_state{}).
rig_anim_event({init},St,_) ->
wings:init_opengl(St),
rig_anim_init(St);
rig_anim_event(redraw,St,RigAnimState) ->
Msg1 = wings_msg:button_format("Select"),
Msg2 = wings_camera:help(),
Msg3 = wings_msg:button_format([],[],"Show menu"),
Msg = wings_msg:join([Msg1,Msg2,Msg3]),
wings_wm:message(Msg,"Rigging & Animating"),
Info = rig_anim_info(St,RigAnimState),
if
is_atom(Info) ->
wings:redraw(St);
true ->
wings:redraw(Info,St)
end,
keep;
rig_anim_event({crash,Crash},_,_) ->
wings_u:win_crash(Crash),
delete;
rig_anim_event(close,_,_) ->
wings_dl:delete_dlists(),
delete;
rig_anim_event(Ev,St,RigAnimState) ->
case wings_camera:event(Ev,St) of
next -> rig_anim_camera_event(Ev,St,RigAnimState);
Other -> Other
end.
rig_anim_camera_event(Ev,St,RigAnimState) ->
case wings_pick:event(Ev,St,fun() -> rig_anim_event(redraw,St,RigAnimState) end) of
next -> rig_anim_pick_event(Ev,St,RigAnimState);
Other -> Other
end.
rig_anim_pick_event(Ev,#st{selmode=Mode}=St,RigAnimState) ->
case wings_menu:is_popup_event(Ev) of
no -> rig_anim_key_event(Ev,St,RigAnimState);
{yes,X,Y,_} ->
wings_menu:popup_menu(X,Y,rig_anim,rig_anim_menu(Mode,St,RigAnimState))
end.
rig_anim_key_event(Ev,St,RigAnimState) ->
case wings_hotkey:event(Ev,St) of
next -> rig_anim_state_event(Ev,St,RigAnimState);
Action ->
wings_wm:later({action,Action}),
keep
end.
rig_anim_state_event({new_state,St1},St,#rig_anim_state{mode=Mode,rig=#rig_anim_rig{next_joint_id=NextJointID}}=RigAnimState) ->
rig_anim_setup(St1,RigAnimState);
rig_anim_state_event({action,{view,Cmd}},St,RigAnimState) ->
case wings_view:command(Cmd,St) of
#st{}=St1 -> rig_anim_setup(St1,RigAnimState);
Other -> Other
end;
rig_anim_state_event({action,{select,Cmd}},St,RigAnimState) ->
case wings_sel_cmd:command(Cmd,St) of
St -> keep;
{save_state,St1} -> rig_anim_filter_select_command(St,RigAnimState,St1);
#st{}=St1 -> rig_anim_filter_select_command(St,RigAnimState,St1);
Other -> Other
end;
rig_anim_state_event({action,{window,geom_viewer}},_,_) ->
keep;
rig_anim_state_event({action,{window,Cmd}},St,RigAnimState) ->
case wings:command({window,Cmd},St) of
St -> keep;
{save_state,St1} -> rig_anim_event({new_state,St1},St,RigAnimState);
#st{}=St1 -> rig_anim_event({new_state,St1},St,RigAnimState);
Other -> Other
end;
rig_anim_state_event({action,{material,Cmd}},St,RigAnimState) ->
case wings_material:command(Cmd,St) of
St -> keep;
{save_state,St1} -> rig_anim_event({new_state,St1},St,RigAnimState);
#st{}=St1 -> rig_anim_event({new_state,St1},St,RigAnimState);
Other -> Other
end;
rig_anim_state_event({callback,Fun},_,_) when function(Fun) ->
Fun();
rig_anim_state_event({message,Message},_,_) ->
wings_u:message(Message);
rig_anim_state_event(#mousemotion{},_,_) ->
keep;
rig_anim_state_event(#mousebutton{},_,_) ->
keep;
rig_anim_state_event(#keyboard{},_,_) ->
keep;
rig_anim_state_event({action,{rig_anim,Cmd}},St,RigAnimState) ->
rig_anim_handle_command(Cmd,St,RigAnimState);
rig_anim_state_event(_,_,_) ->
keep.
%%
%% Menus
%%
rig_anim_menu(_,#st{sel=[]},#rig_anim_state{rig=Rig,active_joint_id=ActiveJointID}=RigAnimState) ->
if
ActiveJointID > 0 ->
Menu = [
{"Deactivate Joint " ++ rig_anim_joint_name(ActiveJointID,Rig),deactivate},
separator
];
true ->
Menu = []
end,
Menu ++ [
{"New Joint",new_joint,[option]},
separator,
{"Animation",{anim,[
{"New Sequence",new_seq,[option]},
{"Rename Sequence",rename},
{"Duplicate Sequence",dup_seq},
separator,
{"Sequence",{seq,[
{"Idle",1},
{"Walk",2},
{"Run",3}
]}},
separator,
{"New Frame",new_frame,[option]},
{"Set Frame Time",set_frame_time},
{"Duplicate Frame",dup_frame},
{"Swap Prev Frame",swap_prev_frame},
{"Swap Next Frame",swap_next_frame},
separator,
{"First Frame",first_frame},
{"Next Frame",next_frame},
{"Previous Frame",prev_frame},
{"Last Frame",first_frame},
separator,
{"Play",play},
separator,
{"Clear All Sequences",clear_all},
{"Clear Sequence",clear}
]}},
separator,
{"Clear All",clear_all}
];
rig_anim_menu(vertex,#st{sel=Sel},#rig_anim_state{rig=Rig,active_joint_id=ActiveJointID}=RigAnimState) ->
if
ActiveJointID > 0 ->
Menu = [
{"Attach To Joint " ++ rig_anim_joint_name(ActiveJointID,Rig),attach,[option]},
{"Detach From Joint " ++ rig_anim_joint_name(ActiveJointID,Rig),detach}
];
true ->
Menu = []
end,
Menu ++ [{"Detach From All",detach_all}];
rig_anim_menu(body,St,RigAnimState) ->
SelJoints = rig_anim_selected_joints(St,RigAnimState),
NumSel = length(SelJoints),
if
NumSel > 1 ->
Name = [],
Tie = [{"Tie To",{tie,
lists:map(
fun(#rig_anim_joint{joint_id=JointID}=Joint) ->
{rig_anim_joint_name(Joint),JointID}
end
,SelJoints)
}}];
NumSel == 1 ->
Name = [
{"Rename",rename},
separator
],
Tie = [];
true ->
Name = [],
Tie = []
end,
Name
++
[
{"Move",{move,[
{"X",x},
{"Y",y},
{"Z",z}
]}},
{"Mirror",{mirror,[
{"X",x},
{"Y",y},
{"Z",z}
]}},
separator
]
++
Tie
++
[
{"Untie",untie},
separator,
{"Clear Attached Verticies",clear_verticies},
separator,
{"Delete",delete}
].
%%
%% Commands
%%
%% Rig
rig_anim_handle_command(deactivate,St,#rig_anim_state{active_joint_id=ActiveJointID}=RigAnimState) ->
rig_anim_setup(St,RigAnimState#rig_anim_state{active_joint_id=0});
rig_anim_handle_command({new_joint,Ask},St,#rig_anim_state{rig=#rig_anim_rig{next_joint_id=NextJointID}}=RigAnimState) when is_atom(Ask) ->
wpa:ask(Ask,"New Joint",[
{"Joint Name","Joint #" ++ integer_to_list(NextJointID)}
],fun(Options) ->
{rig_anim,{new_joint,Options}}
end);
rig_anim_handle_command({new_joint,[Name]},St,#rig_anim_state{rig=#rig_anim_rig{next_joint_id=NextJointID}=Rig,active_joint_id=ActiveJointID}=RigAnimState) ->
rig_anim_setup(St,RigAnimState#rig_anim_state{rig=rig_anim_joint_new(Name,0.0,0.0,0.0,ActiveJointID,Rig),active_joint_id=NextJointID});
rig_anim_handle_command(rename,St,RigAnimState) ->
[#rig_anim_joint{name=Name}=Joint|_] = rig_anim_selected_joints(St,RigAnimState),
wpa:ask(true,"Rename Joint " ++ rig_anim_joint_name(Joint),[
{"Joint Name",Name}
],fun(Options) ->
{rig_anim,{rename,Options}}
end);
rig_anim_handle_command({rename,[Name]},St,#rig_anim_state{rig=#rig_anim_rig{joints=Joints}=Rig}=RigAnimState) ->
[#rig_anim_joint{joint_id=JointID}=Joint|_] = rig_anim_selected_joints(St,RigAnimState),
rig_anim_setup(St,RigAnimState#rig_anim_state{rig=Rig#rig_anim_rig{joints=gb_trees:update(JointID,Joint#rig_anim_joint{name=Name},Joints)}});
rig_anim_handle_command({move,Axis},#st{sel=Sel}=St,RigAnimState) ->
rig_anim_drag(wings_move:setup(Axis,St));
rig_anim_handle_command({tie,ParentJointID},St,#rig_anim_state{rig=#rig_anim_rig{joints=Joints}=Rig}=RigAnimState) ->
SelJoints = lists:filter(
fun(#rig_anim_joint{joint_id=JointID}) ->
if
JointID == ParentJointID -> false;
true -> true
end
end
,rig_anim_selected_joints(St,RigAnimState)),
Joints1 = rig_anim_joint_set_parent(SelJoints,ParentJointID,Joints),
rig_anim_setup(St,RigAnimState#rig_anim_state{rig=Rig#rig_anim_rig{joints=Joints1}});
rig_anim_handle_command(untie,St,#rig_anim_state{rig=#rig_anim_rig{joints=Joints}=Rig}=RigAnimState) ->
SelJoints = rig_anim_selected_joints(St,RigAnimState),
Joints1 = rig_anim_joint_set_parent(SelJoints,0,Joints),
rig_anim_setup(St,RigAnimState#rig_anim_state{rig=Rig#rig_anim_rig{joints=Joints1}});
rig_anim_handle_command(delete,St,#rig_anim_state{rig=#rig_anim_rig{joints=Joints}=Rig}=RigAnimState) ->
SelJoints = rig_anim_selected_joints(St,RigAnimState),
rig_anim_setup(St#st{sel=[]},RigAnimState#rig_anim_state{rig=Rig#rig_anim_rig{joints=rig_anim_joint_delete(SelJoints,Joints)}});
rig_anim_handle_command({attach,Ask},#st{sel=Sel}=St,#rig_anim_state{rig=#rig_anim_rig{}=Rig,active_joint_id=ActiveJointID}=RigAnimState) when is_atom(Ask) ->
if
Ask ->
wings_ask:dialog("Joint Influence",[
{label,"Joint Influence"},
{slider,{text,1.0,[{range,{0.0,1.0}}]}}
],fun(Options) ->
{rig_anim,{attach,Options}}
end);
true ->
rig_anim_setup(St,RigAnimState#rig_anim_state{rig=rig_anim_vertex_attach(Sel,ActiveJointID,1.0,Rig)})
end;
rig_anim_handle_command({attach,[Influence]},#st{sel=Sel}=St,#rig_anim_state{rig=#rig_anim_rig{}=Rig,active_joint_id=ActiveJointID}=RigAnimState) ->
rig_anim_setup(St,RigAnimState#rig_anim_state{rig=rig_anim_vertex_attach(Sel,ActiveJointID,Influence,Rig)});
rig_anim_handle_command(detach,#st{sel=Sel}=St,#rig_anim_state{rig=#rig_anim_rig{}=Rig,active_joint_id=ActiveJointID}=RigAnimState) ->
rig_anim_setup(St,RigAnimState#rig_anim_state{rig=rig_anim_vertex_detach(Sel,ActiveJointID,Rig)});
rig_anim_handle_command(detach_all,#st{sel=Sel}=St,#rig_anim_state{rig=#rig_anim_rig{joints=Joints}=Rig}=RigAnimState) ->
rig_anim_setup(St,RigAnimState#rig_anim_state{rig=rig_anim_vertex_detach(Sel,gb_trees:keys(Joints),Rig)});
rig_anim_handle_command(Cmd,_,_) ->
io:format("UNHANDLED COMMAND: ~w~n",[Cmd]),
keep.
rig_anim_drag({drag,Drag}) ->
wings_drag:do_drag(Drag,none).
%%
%% Joints
%%
rig_anim_joint_name(JointID,#rig_anim_rig{joints=Joints}=Rig) ->
rig_anim_joint_name(gb_trees:get(JointID,Joints)).
rig_anim_joint_name(#rig_anim_joint{joint_id=JointID,name=Name}=Joint) ->
"#" ++ integer_to_list(JointID) ++ "(" ++ Name ++ ")".
rig_anim_info(#st{sel=[]},#rig_anim_state{rig=Rig,mode=Mode,active_joint_id=ActiveJointID}=RigAnimState) ->
if
ActiveJointID > 0 ->
"Joint " ++ rig_anim_joint_name(ActiveJointID,Rig) ++ " Is Active";
true ->
none
end;
rig_anim_info(#st{sel=Sel},#rig_anim_state{mode=vertex}=RigAnimState) ->
none;
rig_anim_info(#st{sel=Sel}=St,#rig_anim_state{mode=body}=RigAnimState) ->
SelJoints = rig_anim_selected_joints(St,RigAnimState),
NumSel = length(SelJoints),
if
NumSel == 1 ->
Info = "Joint ";
true ->
Info = "Joints "
end,
Info ++ rig_anim_info_joints(SelJoints) ++ " Selected".
rig_anim_info_joints(SelJoints) ->
rig_anim_info_joints("","",SelJoints).
rig_anim_info_joints(Info,Sep,[Joint|Rest]) ->
rig_anim_info_joints(Info ++ Sep ++ rig_anim_joint_name(Joint),",",Rest);
rig_anim_info_joints(Info,_,[]) ->
Info.
rig_anim_selected_joints(#st{sel=Sel}=St,RigAnimState) ->
rig_anim_selected_joints([],Sel,St,RigAnimState).
rig_anim_selected_joints(SelJoints,[{Id,_}|Rest],#st{shapes=Shapes}=St,#rig_anim_state{rig=#rig_anim_rig{joints=Joints}}=RigAnimState) ->
Shape = gb_trees:get(Id,Shapes),
case Shape of
#we{name=Name} ->
Prefix = lists:sublist(Name,6),
if
Prefix == "joint_" ->
JointID = list_to_integer(lists:sublist(Name,7,(length(Name) - 6))),
rig_anim_selected_joints(SelJoints ++ [gb_trees:get(JointID,Joints)],Rest,St,RigAnimState);
true ->
rig_anim_selected_joints(SelJoints,Rest,St,RigAnimState)
end;
true ->
rig_anim_selected_joints(SelJoints,Rest,St,RigAnimState)
end;
rig_anim_selected_joints(SelJoints,[],_,_) ->
SelJoints.
rig_anim_joint_new(Name,X,Y,Z,#rig_anim_rig{}=Rig) ->
rig_anim_joint_new(Name,X,Y,Z,0,Rig).
rig_anim_joint_new(Name,X,Y,Z,ParentJointID,#rig_anim_rig{joints=Joints,next_joint_id=JointID}=Rig) ->
Joint = #rig_anim_joint{
joint_id = JointID,
parent_joint_id = ParentJointID,
name = Name,
x = X,
y = Y,
z = Z,
verticies = gb_trees:empty()
},
Rig#rig_anim_rig{joints=gb_trees:insert(JointID,Joint,Joints),next_joint_id=(JointID + 1)}.
rig_anim_joint_delete([#rig_anim_joint{joint_id=JointID}|Rest],Joints) ->
rig_anim_joint_delete(Rest,gb_trees:delete(JointID,Joints));
rig_anim_joint_delete([],Joints) ->
Joints.
rig_anim_joint_set_parent([#rig_anim_joint{joint_id=JointID}=Joint|Rest],ParentJointID,Joints) ->
if
ParentJointID > 0 ->
#rig_anim_joint{parent_joint_id=ParentJointID1}=ParentJoint = gb_trees:get(ParentJointID,Joints),
if
ParentJointID1 == JointID ->
Joints1 = gb_trees:update(ParentJointID,ParentJoint#rig_anim_joint{parent_joint_id=0},Joints);
true ->
Joints1 = Joints
end;
true ->
Joints1 = Joints
end,
rig_anim_joint_set_parent(Rest,ParentJointID,gb_trees:update(JointID,Joint#rig_anim_joint{parent_joint_id=ParentJointID},Joints1));
rig_anim_joint_set_parent([],_,Joints) ->
Joints.
%%
%% Verticies
%%
rig_anim_vertex_attach([{MeshID,Vs}|Rest],JointID,Influence,Rig) ->
VertexIDs = if
is_list(Vs) ->
Vs;
true ->
gb_sets:to_list(Vs)
end,
rig_anim_vertex_attach(Rest,JointID,Influence,rig_anim_vertex_attach(MeshID,VertexIDs,JointID,Influence,Rig));
rig_anim_vertex_attach([],_,_,Rig) ->
Rig.
rig_anim_vertex_attach(MeshID,[VertexID|Rest],JointID,Influence,#rig_anim_rig{joints=Joints}=Rig) ->
#rig_anim_joint{verticies=Verticies}=Joint = gb_trees:get(JointID,Joints),
case gb_trees:is_defined({MeshID,VertexID},Verticies) of
true ->
Verticies1 = gb_trees:update({MeshID,VertexID},Influence,Verticies);
false ->
Verticies1 = gb_trees:insert({MeshID,VertexID},Influence,Verticies)
end,
rig_anim_vertex_attach(MeshID,Rest,JointID,Influence,Rig#rig_anim_rig{joints=gb_trees:update(JointID,Joint#rig_anim_joint{verticies=Verticies1},Joints)});
rig_anim_vertex_attach(_,[],_,_,Rig) ->
Rig.
rig_anim_vertex_detach(Sel,[JointID|Rest],Rig) ->
rig_anim_vertex_detach(Sel,Rest,rig_anim_vertex_detach(Sel,JointID,Rig));
rig_anim_vertex_detach(Sel,[],Rig) ->
Rig;
rig_anim_vertex_detach([{MeshID,Vs}|Rest],JointID,Rig) ->
VertexIDs = if
is_list(Vs) ->
Vs;
true ->
gb_sets:to_list(Vs)
end,
rig_anim_vertex_detach(Rest,JointID,rig_anim_vertex_detach(MeshID,VertexIDs,JointID,Rig));
rig_anim_vertex_detach([],_,Rig) ->
Rig.
rig_anim_vertex_detach(MeshID,[VertexID|Rest],JointID,#rig_anim_rig{joints=Joints}=Rig) ->
#rig_anim_joint{verticies=Verticies}=Joint = gb_trees:get(JointID,Joints),
case gb_trees:is_defined({MeshID,VertexID},Verticies) of
true ->
Verticies1 = gb_trees:delete({MeshID,VertexID},Verticies);
false ->
Verticies1 = Verticies
end,
rig_anim_vertex_detach(MeshID,Rest,JointID,Rig#rig_anim_rig{joints=gb_trees:update(JointID,Joint#rig_anim_joint{verticies=Verticies1},Joints)});
rig_anim_vertex_detach(_,[],_,Rig) ->
Rig.
%%
%% Utils
%%
rig_anim_filter_select_command(St,RigAnimState,#st{selmode=Mode}=St1) ->
Modes = [vertex,body],
case lists:member(Mode,Modes) of
false -> keep;
true -> rig_anim_event({new_state,St1#st{sel=[]}},St,RigAnimState#rig_anim_state{mode=Mode})
end.
rig_anim_get_shape_by_name(Name,#st{shapes=Shapes}=St) ->
List = lists:filter(
fun(#we{name=Name1}=We) ->
if
Name1 == Name ->
true;
true ->
false
end
end
,gb_trees:values(Shapes)),
if
length(List) > 0 ->
[We|Rest] = List,
We;
true ->
none
end.
rig_anim_add_material(Name,Color,#st{mat=Mat}=St) ->
case gb_trees:is_defined(Name,Mat) of
true -> St;
false ->
case wings_material:add_materials([{Name,[{opengl,[{diffuse,Color}]}]}],St) of
{St1,[]} -> St1;
{St1,[{Name,New}]} -> St1
end
end.
%%
%% Testing
%%
rig_anim_test_rig() ->
rig_anim_test_rig_1().
rig_anim_test_rig_1() ->
rig_anim_joint_new("Waist", 0.0, 3.0, 0.0,0, %9
rig_anim_joint_new("Hip 1", -0.5, 2.5, 0.0,9, %8
rig_anim_joint_new("Hip 2", 0.5, 2.5, 0.0,9, %7
rig_anim_joint_new("Knee 1",-0.7, 1.5, 0.2,8, %6
rig_anim_joint_new("Knee 2", 0.7, 1.5, 0.2,7, %5
rig_anim_joint_new("Foot 1",-0.5, 0.0, 0.0,6, %4
rig_anim_joint_new("Foot 2", 0.5, 0.0, 0.0,5, %3
rig_anim_joint_new("Toe 1", -0.5, 0.0, 0.8,4, %2
rig_anim_joint_new("Toe 2", 0.5, 0.0, 0.8,3, %1
#rig_anim_rig{joints=gb_trees:empty()}))))))))).
rig_anim_test_rig_2() ->
rig_anim_joint_new("Center", 0.0, 0.0, 0.0,0, %27
rig_anim_joint_new("X 1",-2.0, 0.0, 0.0,27, %26
rig_anim_joint_new("X 2", 2.0, 0.0, 0.0,27, %25
rig_anim_joint_new("Y 1", 0.0,-2.0, 0.0,27, %24
rig_anim_joint_new("Y 2", 0.0, 2.0, 0.0,27, %23
rig_anim_joint_new("Z 1", 0.0, 0.0,-2.0,27, %22
rig_anim_joint_new("Z 2", 0.0, 0.0, 2.0,27, %21
rig_anim_joint_new("XZ 1", 2.0, 0.0, 2.0,27, %20
rig_anim_joint_new("XZ 2",-2.0, 0.0, 2.0,27, %19
rig_anim_joint_new("XZ 3",-2.0, 0.0,-2.0,27, %18
rig_anim_joint_new("XZ 4", 2.0, 0.0,-2.0,27, %17
rig_anim_joint_new("XY 1", 2.0, 2.0, 0.0,27, %16
rig_anim_joint_new("XY 2",-2.0, 2.0, 0.0,27, %15
rig_anim_joint_new("XY 3",-2.0,-2.0, 0.0,27, %14
rig_anim_joint_new("XY 4", 2.0,-2.0, 0.0,27, %13
rig_anim_joint_new("YZ 1", 0.0, 2.0, 2.0,27, %12
rig_anim_joint_new("YZ 2", 0.0,-2.0, 2.0,27, %11
rig_anim_joint_new("YZ 3", 0.0,-2.0,-2.0,27, %10
rig_anim_joint_new("YZ 4", 0.0, 2.0,-2.0,27, %9
rig_anim_joint_new("XYZ 1", 1.0, 1.0, 1.0,27, %8
rig_anim_joint_new("XYZ 1",-1.0, 1.0, 1.0,27, %7
rig_anim_joint_new("XYZ 1",-1.0,-1.0, 1.0,27, %6
rig_anim_joint_new("XYZ 1",-1.0,-1.0,-1.0,27, %5
rig_anim_joint_new("XYZ 1", 1.0,-1.0, 1.0,27, %4
rig_anim_joint_new("XYZ 1", 1.0,-1.0,-1.0,27, %3
rig_anim_joint_new("XYZ 1", 1.0, 1.0,-1.0,27, %2
rig_anim_joint_new("XYZ 1",-1.0, 1.0,-1.0,27, %1
#rig_anim_rig{joints=gb_trees:empty()}))))))))))))))))))))))))))).
Friday, August 21, 2009
Subscribe to:
Comments (Atom)