PageRenderTime 69ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 1ms

/dgud-wings-9a2473e/src/wings_camera.erl

#
Erlang | 956 lines | 812 code | 92 blank | 52 comment | 2 complexity | 0c7f879bc7d2705e1db44fdafd212fff MD5 | raw file
Possible License(s): AGPL-3.0
  1. %%
  2. %% wings_camera.erl --
  3. %%
  4. %% This module handles camera moves (rotation, zooming, and panning).
  5. %%
  6. %% Copyright (c) 2001-2011 Bjorn Gustavsson
  7. %%
  8. %% See the file "license.terms" for information on usage and redistribution
  9. %% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  10. %%
  11. %% $Id$
  12. %%
  13. -module(wings_camera).
  14. -export([init/0,prefs/0,help/0,event/2,event/3]).
  15. -export([tweak_camera_event/4]).
  16. -define(NEED_ESDL, 1).
  17. -include("wings.hrl").
  18. -define(ZOOM_FACTOR, 20).
  19. -define(ZOOM_FACTOR_ALT, 1).
  20. -define(CAMDIV, 4).
  21. -define(CSEP, 160). %Short space.
  22. -define(NEARZERO, 1.0e-6).
  23. -record(camera,
  24. {x,y, %Current mouse position.
  25. ox,oy, %Original mouse position.
  26. xt=0,yt=0 %Last warp length.
  27. }).
  28. -record(state, {st, func}).
  29. init() ->
  30. wings_pref:set_default(camera_mode, wings_cam),
  31. wings_pref:set_default(num_buttons, 3),
  32. wings_pref:set_default(cam_rotation_speed, 25),
  33. wings_pref:set_default(pan_speed, 25),
  34. wings_pref:set_default(pan_speed_arrow_keys, 50),
  35. wings_pref:set_default(wheel_adds, false),
  36. wings_pref:set_default(wh_scroll_info,true),
  37. wings_pref:set_default(wh_pan_spd, 50),
  38. wings_pref:set_default(wh_rot_spd, 7.50),
  39. wings_pref:set_default(highlight_zoom_aim, false),
  40. case {wings_pref:get_value(num_buttons),wings_pref:get_value(camera_mode)} of
  41. {3,_} -> ok;
  42. {_,nendo} -> ok;
  43. {_,blender} -> ok;
  44. {_,_} -> wings_pref:set_value(camera_mode, nendo)
  45. end.
  46. prefs() ->
  47. ZoomFlag0 = wings_pref:get_value(wheel_zooms, true),
  48. ZoomFactor0 = wings_pref:get_value(wheel_zoom_factor, ?ZOOM_FACTOR),
  49. ZoomFactorAlt = wings_pref:get_value(wheel_zoom_factor_alt, ?ZOOM_FACTOR_ALT),
  50. CamRotSpeed = wings_pref:get_value(cam_rotation_speed, 25),
  51. PanSpeed0 = wings_pref:get_value(pan_speed, 25),
  52. ArrowPanSpeed = wings_pref:get_value(pan_speed_arrow_keys, 50),
  53. WheelAdds = wings_pref:get_value(wheel_adds,false),
  54. WhScrollInfo = wings_pref:get_value(wh_scroll_info,true),
  55. WhPanSpd = wings_pref:get_value(wh_pan_spd, 50),
  56. WhRotate = wings_pref:get_value(wh_rot_spd, 7.5),
  57. ZoomAim = wings_pref:get_value(highlight_zoom_aim),
  58. Hook = fun (is_disabled, {_Var,_I,Sto}) ->
  59. not gb_trees:get(wheel_zooms, Sto);
  60. (_, _) -> void
  61. end,
  62. WHook = fun (is_disabled, {_Var,_I,Sto}) ->
  63. not ((gb_trees:get(wheel_adds, Sto)) andalso (gb_trees:get(wheel_zooms, Sto)));
  64. (_, _) -> void
  65. end,
  66. {hframe,
  67. [{vframe,
  68. [{vframe,[mouse_buttons()],[{title,?__(1,"Mouse Buttons")}]},
  69. {vframe,[camera_modes()],[{title,?__(2,"Camera Mode")}]},
  70. {vframe,
  71. [{hframe,[{slider,{text,CamRotSpeed,[{key,cam_rotation_speed},{range,{1,100}}]}}]}],
  72. [{title,?__(19,"Rotation Speed")}]},
  73. {vframe,
  74. [{hframe,[{slider,{text,PanSpeed0,[{key,pan_speed},{range,{1,100}}]}}]}],
  75. [{title,?__(3,"Pan Speed")}]},
  76. {vframe,
  77. [{hframe,[{slider,{text,ArrowPanSpeed,[{key,pan_speed_arrow_keys},{range,{1,100}}]}}]}],
  78. [{title,?__(16,"Arrow Key Pan Speed")}]}]},
  79. {vframe,
  80. [{vframe,
  81. [{hframe,[{?__(4,"Wheel Zooms"),ZoomFlag0,[{key,wheel_zooms}]},
  82. {?__(20,"Zooming in aims Camera"),ZoomAim,[{key,highlight_zoom_aim}]}]},
  83. {hframe,
  84. [{label,?__(7,"Zoom Factor"),[{hook,Hook}]},
  85. {text,ZoomFactor0,
  86. [{key,wheel_zoom_factor},
  87. {range,{1,50}},{hook,Hook}]},
  88. {label,"%",[{hook,Hook}]}]},
  89. {hframe,
  90. [{label,?__(10,"Alternate Zoom Factor(Alt+Scroll)"),[{hook,Hook}]},
  91. {text,ZoomFactorAlt,
  92. [{key,wheel_zoom_factor_alt},
  93. {range,{1,50}},{hook,Hook}]},
  94. {label,"%",[{hook,Hook}]}]} ],[{title,?__(9,"Scroll Wheel")}]},
  95. {vframe,
  96. [{?__(11,"Wheel Pans & Rotates"),WheelAdds,[{key,wheel_adds},{hook,Hook}]},
  97. {?__(17,"Show Info Line Help String"),WhScrollInfo,[{key,wh_scroll_info},{hook,WHook}]},
  98. {vframe,
  99. [{hframe,
  100. [{slider,{text,WhPanSpd,[{key, wh_pan_spd},{range,{1,100}}]}}],
  101. [{title,?__(12,"Pan Speed")},{hook,WHook}]},
  102. {hframe,
  103. [{slider,{text,WhRotate,[{key, wh_rot_spd},{range,{?NEARZERO,180.0}}]}}],
  104. [{title,?__(13,"Rotation Step in Degrees")},{hook,WHook}]}]}],
  105. [{title,?__(15,"Unidirectional Camera")}]}]}]}.
  106. mouse_buttons() ->
  107. {menu,[{desc(1),1,[{info,info(1)}]},
  108. {desc(2),2,[{info,info(2)}]},
  109. {desc(3),3,[{info,info(3)}]}],
  110. wings_pref:get_value(num_buttons),
  111. [{key,num_buttons},
  112. {hook,fun (update, {Var,_I,Val,Sto0}) ->
  113. Sto = gb_trees:update(Var, Val, Sto0),
  114. Mode0 = gb_trees:get(camera_mode, Sto),
  115. Mode = case {Val,Mode0} of
  116. {1,_} -> nendo;
  117. {2,blender} -> blender;
  118. {2,_} -> nendo;
  119. {3,_} -> Mode0
  120. end,
  121. {store,gb_trees:update(camera_mode, Mode, Sto)};
  122. (_, _) -> void
  123. end}]}.
  124. camera_modes() ->
  125. Modes = [wings_cam,mirai,nendo,maya,tds,blender,mb,sketchup],
  126. {menu,[{desc(Mode),Mode,[{info,info(Mode)}]} || Mode <- Modes],
  127. wings_pref:get_value(camera_mode),
  128. [{key,camera_mode},
  129. {hook,fun (menu_disabled, {_Var,_I,Sto}) ->
  130. case gb_trees:get(num_buttons, Sto) of
  131. 1 -> [mirai,maya,tds,blender,mb,sketchup,wings_cam];
  132. 2 -> [mirai,maya,tds,mb,sketchup,wings_cam];
  133. 3 -> []
  134. end;
  135. (_, _) -> void
  136. end}]}.
  137. desc(1) -> ?__(1,"One");
  138. desc(2) -> ?__(2,"Two");
  139. desc(3) -> ?__(3,"Three");
  140. desc(Other) -> wings_s:camera_mode(Other).
  141. info(1) ->
  142. ?__(1,"Note: Only the Nendo camera mode can be used with a one-button mouse");
  143. info(2) ->
  144. ?__(2,"Note: Only the Nendo and Blender camera modes can be used with a two-button mouse");
  145. info(3) -> "";
  146. info(nendo) -> "";
  147. info(blender) ->
  148. [?__(3,"Note: The "),desc(blender),
  149. ?__(4," camera mode requires at least 2 mouse buttons")];
  150. info(Mode) ->
  151. [?__(5,"Note: The "),desc(Mode),
  152. ?__(6," camera mode requires 3 mouse buttons")].
  153. help() ->
  154. Help = case wings_pref:get_value(camera_mode) of
  155. blender -> blender_help();
  156. nendo -> wings_msg:button_format([], ?__(1,"Start camera"));
  157. mirai -> wings_msg:button_format([], ?__(1,"Start camera"));
  158. wings_cam -> wings_msg:button_format([], ?__(1,"Start camera"));
  159. tds -> tds_help();
  160. maya -> maya_help();
  161. mb -> mb_help();
  162. sketchup -> sketchup_help()
  163. end,
  164. WheelAdds = wings_pref:get_value(wheel_adds),
  165. WhScrollHelp = wings_pref:get_value(wh_scroll_info),
  166. case {WheelAdds,WhScrollHelp} of
  167. {true,true} -> [Help|scroll_help()];
  168. {_,_} -> Help
  169. end.
  170. scroll_help() ->
  171. ?__(1," [Ctrl](+[Alt])+Scroll: Pan [Shift](+[Alt])+Scroll: Rotate").
  172. %% Event handler.
  173. event(Ev, St=#st{}) ->
  174. event(Ev,St,none).
  175. %% Scroll wheel camera events
  176. event(#mousebutton{button=B}=Ev, _St, _Redraw) when B=:=4; B=:=5 ->
  177. generic_event(Ev,_St,_Redraw);
  178. % Camera mode specific events
  179. event(Ev, St, Redraw) ->
  180. case wings_pref:get_value(camera_mode) of
  181. wings_cam -> wings_cam(Ev, #state{st=St, func=Redraw});
  182. blender -> blender(Ev, #state{st=St, func=Redraw});
  183. nendo -> nendo(Ev, #state{st=St, func=Redraw});
  184. mirai -> mirai(Ev, #state{st=St, func=Redraw});
  185. tds -> tds(Ev, #state{st=St, func=Redraw});
  186. maya -> maya(Ev, #state{st=St, func=Redraw});
  187. mb -> mb(Ev, #state{st=St, func=Redraw});
  188. sketchup -> sketchup(Ev, #state{st=St, func=Redraw})
  189. end.
  190. %%%
  191. %%% Tweak Camera
  192. %%%
  193. tweak_camera_event(Sym, X, Y, St) when Sym =:= $c; Sym =:= $s; Sym =:= $d ->
  194. Camera = #camera{x=X,y=Y,ox=X,oy=Y},
  195. {seq,push,get_tweak_cam_event(Sym, Camera, St)};
  196. tweak_camera_event(Sym, _, _, _) ->
  197. arrow_key_pan(Sym).
  198. get_tweak_cam_event(Sym, Camera, St) ->
  199. {replace,fun(Ev) -> tweak_cam_event(Ev, Sym, Camera, St) end}.
  200. tweak_cam_event(#mousemotion{x=X0,y=Y0}, Sym, Camera0, St) ->
  201. case wings_io:is_key_pressed(Sym) of
  202. true ->
  203. {Dx,Dy,Camera} = camera_mouse_range(X0, Y0, Camera0),
  204. case Sym of
  205. $c -> rotate(Dx,Dy);
  206. $s -> pan(Dx,Dy);
  207. $d -> zoom(Dy)
  208. end,
  209. wings_wm:dirty(),
  210. get_tweak_cam_event(Sym, Camera, St);
  211. false ->
  212. quit_tweak_cam()
  213. end;
  214. tweak_cam_event(#mousebutton{button=B,state=?SDL_RELEASED}=Ev, Sym, Camera, St)
  215. when B =< 3->
  216. case wings_io:is_key_pressed(Sym) of
  217. true ->
  218. generic_event(Ev, Camera, #state{st=St, func=none});
  219. false ->
  220. quit_tweak_cam()
  221. end;
  222. tweak_cam_event(Ev, Sym, Camera, St) ->
  223. case wings_io:is_key_pressed(Sym) of
  224. true ->
  225. generic_event(Ev, Camera, #state{st=St, func=none});
  226. false ->
  227. quit_tweak_cam()
  228. end.
  229. quit_tweak_cam() ->
  230. case wings_io:get_mouse_state() of
  231. {0,X,Y} ->
  232. wings_wm:later(#mousebutton{button=1,x=X,y=Y,mod=0,state=?SDL_RELEASED}),
  233. pop;
  234. _ ->
  235. pop
  236. end.
  237. %%%
  238. %%% Blender style camera.
  239. %%%
  240. blender(#mousebutton{button=2,state=?SDL_PRESSED,x=X0,y=Y0,mod=Mod}, Redraw)
  241. when Mod band ?ALT_BITS =:= 0 ->
  242. {X,Y} = wings_wm:local2global(X0, Y0),
  243. Camera = #camera{x=X,y=Y,ox=X,oy=Y},
  244. grab(),
  245. message(blender_help()),
  246. {seq,push,get_blender_event(Camera, Redraw)};
  247. blender(#keyboard{sym=Sym}, _Redraw) ->
  248. arrow_key_pan(Sym);
  249. blender(_, _) -> next.
  250. blender_event(#mousebutton{button=2,state=?SDL_RELEASED}, Camera, _Redraw) ->
  251. stop_camera(Camera);
  252. blender_event(#mousemotion{x=X,y=Y,mod=Mod}, Camera0, Redraw) ->
  253. {Dx,Dy,Camera} = camera_mouse_range(X, Y, Camera0),
  254. case Mod of
  255. Mod when Mod band ?SHIFT_BITS =/= 0 ->
  256. pan(Dx, Dy);
  257. Mod when Mod band ?CTRL_BITS =/= 0 ->
  258. zoom(Dy);
  259. _Other ->
  260. rotate(Dx, Dy)
  261. end,
  262. wings_wm:dirty(),
  263. get_blender_event(Camera, Redraw);
  264. blender_event(Other, Camera, Redraw) ->
  265. generic_event(Other, Camera, Redraw).
  266. get_blender_event(Camera, Redraw) ->
  267. {replace,fun(Ev) -> blender_event(Ev, Camera, Redraw) end}.
  268. blender_help() ->
  269. TrackDolly = [{?SHIFT_BITS,2,?STR(mode_help,1,"Track")},
  270. {?CTRL_BITS,2,?STR(mode_help,2,"Dolly")}],
  271. case allow_rotation() of
  272. false -> format(TrackDolly);
  273. true -> format([{0,2,?STR(mode_help,3,"Tumble")}|TrackDolly])
  274. end.
  275. %%%
  276. %%% Nendo style camera.
  277. %%%
  278. nendo(#mousebutton{button=2,x=X0,y=Y0,mod=Mod,state=?SDL_RELEASED}, Redraw)
  279. when Mod band ?CTRL_BITS =:= 0 ->
  280. {X,Y} = wings_wm:local2global(X0, Y0),
  281. Camera = #camera{x=X,y=Y,ox=X,oy=Y},
  282. grab(),
  283. MoveTumbles = allow_rotation(),
  284. nendo_message(MoveTumbles),
  285. {seq,push,get_nendo_event(Camera, Redraw, MoveTumbles)};
  286. nendo(#keyboard{sym=Sym}, _Redraw) ->
  287. arrow_key_pan(Sym);
  288. nendo(_, _) -> next.
  289. nendo_event(#mousebutton{button=1,state=?SDL_RELEASED}, Camera, _, _) ->
  290. stop_camera(Camera);
  291. nendo_event(#mousemotion{x=X,y=Y,state=Buttons}, Camera0, Redraw, true) ->
  292. {Dx,Dy,Camera} = camera_mouse_range(X, Y, Camera0),
  293. case Buttons band 6 of
  294. 0 ->
  295. rotate(-Dx, -Dy);
  296. _Other ->
  297. zoom(Dy)
  298. end,
  299. get_nendo_event(Camera, Redraw, true);
  300. nendo_event(#mousemotion{x=X,y=Y,state=Buttons}, Camera0, Redraw, false) ->
  301. {Dx,Dy,Camera} = camera_mouse_range(X, Y, Camera0),
  302. case Buttons band 6 of
  303. 0 ->
  304. pan(-Dx, -Dy);
  305. _Other ->
  306. zoom(Dy)
  307. end,
  308. get_nendo_event(Camera, Redraw, false);
  309. nendo_event(#keyboard{unicode=$q}, Camera, Redraw, MR0) ->
  310. MR = MR0 xor allow_rotation(),
  311. nendo_message(MR),
  312. get_nendo_event(Camera, Redraw, MR);
  313. nendo_event(#keyboard{sym=Sym}=Event, Camera, Redraw, _) ->
  314. case arrow_key_pan(Sym) of
  315. keep -> keep;
  316. next -> view_hotkey(Event, Camera, Redraw)
  317. end;
  318. nendo_event(Event, Camera, Redraw, _) ->
  319. generic_event(Event, Camera, Redraw).
  320. get_nendo_event(Camera, Redraw, MouseRotates) ->
  321. wings_wm:dirty(),
  322. {replace,fun(Ev) -> nendo_event(Ev, Camera, Redraw, MouseRotates) end}.
  323. nendo_message(true) ->
  324. Help = wings_msg:join([wings_msg:button_format(wings_s:accept(),[],
  325. ?STR(message,2,"Drag to Dolly")),
  326. ?STR(message,3,"Move mouse to tumble"),
  327. [?STR(message,4,"[Q]"),?CSEP,
  328. ?STR(message,5,"Move mouse to track")]]),
  329. message(Help);
  330. nendo_message(false) ->
  331. QText = case allow_rotation() of
  332. false -> [];
  333. true -> [?STR(message,4,"[Q]"),?CSEP,
  334. ?STR(message,3,"Move mouse to tumble")]
  335. end,
  336. Help = wings_msg:join([wings_msg:button_format(wings_s:accept(),[],
  337. ?STR(message,2,"Drag to Dolly")),
  338. QText]),
  339. message(Help).
  340. %%%
  341. %%% Mirai style camera.
  342. %%%
  343. mirai(#mousebutton{button=2,x=X0,y=Y0,mod=Mod,state=?SDL_RELEASED}, Redraw)
  344. when Mod band ?CTRL_BITS =:= 0 ->
  345. {X,Y} = wings_wm:local2global(X0, Y0),
  346. Camera = #camera{x=X,y=Y,ox=X,oy=Y},
  347. grab(),
  348. MoveTumbles = allow_rotation(),
  349. mirai_message(MoveTumbles),
  350. View = wings_view:current(),
  351. {seq,push,get_mirai_event(Camera, Redraw, MoveTumbles, View)};
  352. mirai(#keyboard{sym=Sym}, _Redraw) ->
  353. arrow_key_pan(Sym);
  354. mirai(_, _) -> next.
  355. mirai_event(#mousebutton{button=1,state=?SDL_RELEASED}, Camera, _, _, _) ->
  356. stop_camera(Camera);
  357. mirai_event(#mousebutton{button=3,state=?SDL_RELEASED}, Camera, _, _, View) ->
  358. wings_view:set_current(View),
  359. stop_camera(Camera);
  360. mirai_event(#mousemotion{x=X,y=Y,state=Buttons}, Camera0, Redraw, true, View) ->
  361. {Dx,Dy,Camera} = camera_mouse_range(X, Y, Camera0),
  362. case Buttons band 2 of
  363. 0 -> %MMB not pressed.
  364. rotate(-Dx, -Dy);
  365. _Other -> %MMB pressed.
  366. zoom(Dy)
  367. end,
  368. get_mirai_event(Camera, Redraw, true, View);
  369. mirai_event(#mousemotion{x=X,y=Y,state=Buttons}, Camera0, Redraw, false, View) ->
  370. {Dx,Dy,Camera} = camera_mouse_range(X, Y, Camera0),
  371. case Buttons band 2 of
  372. 0 -> %MMB pressed.
  373. pan(-Dx, -Dy);
  374. _Other -> %MMB pressed.
  375. zoom(Dy)
  376. end,
  377. get_mirai_event(Camera, Redraw, false, View);
  378. mirai_event(#keyboard{unicode=$q}, Camera, Redraw, MR0, View) ->
  379. MR = MR0 xor allow_rotation(),
  380. mirai_message(MR),
  381. get_mirai_event(Camera, Redraw, MR, View);
  382. mirai_event(#keyboard{sym=Sym}=Event, Camera, Redraw, _, _) ->
  383. case arrow_key_pan(Sym) of
  384. keep -> keep;
  385. next -> view_hotkey(Event, Camera, Redraw)
  386. end;
  387. mirai_event(Event, Camera, Redraw, _, _) ->
  388. generic_event(Event, Camera, Redraw).
  389. get_mirai_event(Camera, Redraw, MouseRotates, View) ->
  390. wings_wm:dirty(),
  391. {replace,fun(Ev) -> mirai_event(Ev, Camera, Redraw, MouseRotates, View) end}.
  392. mirai_message(true) ->
  393. Help = wings_msg:join([wings_msg:button_format(wings_s:accept(),
  394. ?STR(message,2,"Drag to Dolly"),
  395. ?STR(message,6,"Cancel/restore view")),
  396. ?STR(message,3,"Move mouse to tumble"),
  397. [?STR(message,4,"[Q]"),?CSEP,
  398. ?STR(message,5,"Move mouse to track")]]),
  399. message(Help);
  400. mirai_message(false) ->
  401. QText = case allow_rotation() of
  402. false -> [];
  403. true -> [?STR(message,4,"[Q]"),?CSEP,?STR(message,3,"Move mouse to tumble")]
  404. end,
  405. Help = wings_msg:join([wings_msg:button_format(wings_s:accept(),
  406. ?STR(message,2,"Drag to Dolly"),
  407. ?STR(message,6,"Cancel/restore view")),
  408. ?STR(message,5,"Move mouse to track"),QText]),
  409. message(Help).
  410. %%%
  411. %%% 3ds max style camera.
  412. %%%
  413. tds(#mousebutton{button=2,x=X0,y=Y0,state=?SDL_PRESSED}, Redraw) ->
  414. {X,Y} = wings_wm:local2global(X0, Y0),
  415. Camera = #camera{x=X,y=Y,ox=X,oy=Y},
  416. grab(),
  417. message(wings_msg:join(tds_help(),
  418. wings_msg:button_format([], [],
  419. ?STR(message,7,"Restore view")))),
  420. View = wings_view:current(),
  421. {seq,push,get_tds_event(Camera, Redraw, View)};
  422. tds(#keyboard{sym=Sym}, _Redraw) ->
  423. arrow_key_pan(Sym);
  424. tds(_, _) -> next.
  425. tds_event(#mousebutton{button=1,state=?SDL_RELEASED}=Mb, Camera, Redraw, View) ->
  426. tds_event(Mb#mousebutton{button=2}, Camera, Redraw, View);
  427. tds_event(#mousebutton{button=2,state=?SDL_RELEASED}, Camera, _, _) ->
  428. stop_camera(Camera);
  429. tds_event(#mousebutton{button=3,state=?SDL_RELEASED}, Camera, _, View) ->
  430. wings_view:set_current(View),
  431. stop_camera(Camera);
  432. tds_event(#mousemotion{x=X,y=Y,mod=Mod}, Camera0, Redraw, View) ->
  433. {Dx,Dy,Camera} = camera_mouse_range(X, Y, Camera0),
  434. if
  435. Mod band ?CTRL_BITS =/= 0, Mod band ?ALT_BITS =/= 0 ->
  436. zoom(Dy);
  437. Mod band ?ALT_BITS =/= 0 ->
  438. rotate(Dx, Dy);
  439. true ->
  440. pan(Dx, Dy)
  441. end,
  442. get_tds_event(Camera, Redraw, View);
  443. tds_event(Event, Camera, Redraw, _) ->
  444. generic_event(Event, Camera, Redraw).
  445. get_tds_event(Camera, Redraw, View) ->
  446. wings_wm:dirty(),
  447. {replace,fun(Ev) -> tds_event(Ev, Camera, Redraw, View) end}.
  448. tds_help() ->
  449. TrackDolly = [{0,2,?STR(mode_help,1,"Track")},
  450. {?CTRL_BITS bor ?ALT_BITS,2,?STR(mode_help,2,"Dolly")}],
  451. case allow_rotation() of
  452. false -> format(TrackDolly);
  453. true -> format([{?ALT_BITS,2,?STR(mode_help,3,"Tumble")}|TrackDolly])
  454. end.
  455. %%%
  456. %%% Maya style camera.
  457. %%%
  458. maya(#mousebutton{x=X0,y=Y0,mod=Mod,state=?SDL_PRESSED}, Redraw)
  459. when Mod band ?ALT_BITS =/= 0, Mod band ?CTRL_BITS =:= 0, Mod band ?SHIFT_BITS =:= 0 ->
  460. {X,Y} = wings_wm:local2global(X0, Y0),
  461. wings_io:change_event_handler(?SDL_KEYUP, ?SDL_ENABLE),
  462. Camera = #camera{x=X,y=Y,ox=X,oy=Y},
  463. grab(),
  464. message(maya_help()),
  465. {seq,push,get_maya_event(Camera, Redraw)};
  466. maya(#keyboard{sym=Sym}, _Redraw) ->
  467. arrow_key_pan(Sym);
  468. maya(_, _) -> next.
  469. maya_event(#keyboard{sym=Alt,state=?SDL_RELEASED},
  470. Camera, _Redraw) when Alt =:= ?SDLK_LALT; Alt =:= ?SDLK_RALT ->
  471. maya_stop_camera(Camera);
  472. maya_event(#mousebutton{button=B,state=?SDL_RELEASED}, Camera, _)
  473. when B < 4 ->
  474. case wings_io:get_mouse_state() of
  475. {0,_,_} ->
  476. %% Exit camera mode if all mouse buttons released.
  477. maya_stop_camera(Camera);
  478. _ ->
  479. %% Some mouse button still pressed.
  480. keep
  481. end;
  482. maya_event(#mousemotion{x=X,y=Y,state=Buttons}, Camera0, Redraw) ->
  483. {Dx,Dy,Camera} = camera_mouse_range(X, Y, Camera0),
  484. if
  485. Buttons band 4 =:= 4 -> %RMB
  486. zoom(-Dx);
  487. Buttons band 3 =:= 3 -> %LMB+MMB
  488. zoom(-Dx);
  489. Buttons band 1 =:= 1 -> %LMB
  490. rotate(Dx, Dy);
  491. Buttons band 2 =:= 2 -> %MMB
  492. pan(Dx, Dy);
  493. true -> ok
  494. end,
  495. get_maya_event(Camera, Redraw);
  496. maya_event(Event, Camera, Redraw) ->
  497. generic_event(Event, Camera, Redraw).
  498. get_maya_event(Camera, Redraw) ->
  499. wings_wm:dirty(),
  500. {replace,fun(Ev) -> maya_event(Ev, Camera, Redraw) end}.
  501. maya_stop_camera(Camera) ->
  502. wings_io:change_event_handler(?SDL_KEYUP, ?SDL_IGNORE),
  503. stop_camera(Camera).
  504. maya_help() ->
  505. TrackDolly = [{?ALT_BITS,2,?STR(mode_help,1,"Track")},
  506. {?ALT_BITS,3,?STR(mode_help,2,"Dolly")}],
  507. case allow_rotation() of
  508. false -> format(TrackDolly);
  509. true -> format([{?ALT_BITS,1,?STR(mode_help,3,"Tumble")}|TrackDolly])
  510. end.
  511. %%%
  512. %%% Motionbuilder style camera.
  513. %%%
  514. mb(#mousebutton{button=1,mod=Mod,x=X0,y=Y0,state=?SDL_PRESSED}, Redraw)
  515. when Mod band (?SHIFT_BITS bor ?CTRL_BITS) =/= 0 ->
  516. {X,Y} = wings_wm:local2global(X0, Y0),
  517. Camera = #camera{x=X,y=Y,ox=X,oy=Y},
  518. grab(),
  519. message(mb_help()),
  520. {seq,push,get_mb_event(Camera, Redraw)};
  521. mb(#keyboard{sym=Sym}, _Redraw) ->
  522. arrow_key_pan(Sym);
  523. mb(_, _) -> next.
  524. mb_event(#mousebutton{button=1,state=?SDL_RELEASED}, Camera, _) ->
  525. stop_camera(Camera);
  526. mb_event(#mousemotion{x=X,y=Y,mod=Mod}, Camera0, Redraw) ->
  527. {Dx,Dy,Camera} = camera_mouse_range(X, Y, Camera0),
  528. if
  529. Mod band ?CTRL_BITS =/= 0, Mod band ?SHIFT_BITS =/= 0 ->
  530. rotate(Dx, Dy),
  531. get_mb_event(Camera, Redraw);
  532. Mod band ?CTRL_BITS =/= 0 ->
  533. zoom(Dy),
  534. get_mb_event(Camera, Redraw);
  535. Mod band ?SHIFT_BITS =/= 0 ->
  536. pan(Dx, Dy),
  537. get_mb_event(Camera, Redraw);
  538. true ->
  539. stop_camera(Camera)
  540. end;
  541. mb_event(Event, Camera, Redraw) ->
  542. generic_event(Event, Camera, Redraw).
  543. get_mb_event(Camera, Redraw) ->
  544. wings_wm:dirty(),
  545. {replace,fun(Ev) -> mb_event(Ev, Camera, Redraw) end}.
  546. mb_help() ->
  547. TrackDolly = [{?SHIFT_BITS,1,?STR(mode_help,1,"Track")},
  548. {?CTRL_BITS,1,?STR(mode_help,2,"Dolly")}],
  549. case allow_rotation() of
  550. false -> format(TrackDolly);
  551. true -> format([{?SHIFT_BITS bor ?CTRL_BITS,1,?STR(mode_help,3,"Tumble")}|TrackDolly])
  552. end.
  553. %%%
  554. %%% Sketchup style camera.
  555. %%%
  556. sketchup(#mousebutton{button=2,state=?SDL_PRESSED,x=X0,y=Y0,mod=Mod}, Redraw)
  557. when (Mod band (?ALT_BITS bor ?CTRL_BITS)) =:= 0 ->
  558. {X,Y} = wings_wm:local2global(X0, Y0),
  559. Camera = #camera{x=X,y=Y,ox=X,oy=Y},
  560. grab(),
  561. message(sketchup_help()),
  562. {seq,push,get_sketchup_event(Camera, Redraw)};
  563. sketchup(#keyboard{sym=Sym}, _Redraw) ->
  564. arrow_key_pan(Sym);
  565. sketchup(_, _) -> next.
  566. sketchup_event(#mousebutton{button=2,state=?SDL_RELEASED}, Camera, _Redraw) ->
  567. stop_camera(Camera);
  568. sketchup_event(#mousemotion{x=X,y=Y,mod=Mod}, Camera0, Redraw) ->
  569. {Dx,Dy,Camera} = camera_mouse_range(X, Y, Camera0),
  570. case Mod of
  571. Mod when Mod band ?SHIFT_BITS =/= 0 ->
  572. pan(Dx, Dy);
  573. _Other ->
  574. rotate(Dx, Dy)
  575. end,
  576. wings_wm:dirty(),
  577. get_sketchup_event(Camera, Redraw);
  578. sketchup_event(Other, Camera, Redraw) ->
  579. generic_event(Other, Camera, Redraw).
  580. get_sketchup_event(Camera, Redraw) ->
  581. {replace,fun(Ev) -> sketchup_event(Ev, Camera, Redraw) end}.
  582. sketchup_help() ->
  583. TrackDolly = [{?SHIFT_BITS,2,?STR(mode_help,1,"Track")},
  584. {0,?CSEP,?STR(mode_help,4,"Scroll: Dolly")}],
  585. case allow_rotation() of
  586. false -> format(TrackDolly);
  587. true -> format([{0,2,?STR(mode_help,3,"Tumble")}|TrackDolly])
  588. end.
  589. %%%
  590. %%% Wings 3D camera suggested by oort
  591. %%%
  592. wings_cam(#mousebutton{button=2,x=X0,y=Y0,mod=Mod,state=?SDL_RELEASED}, Redraw)
  593. when Mod band (?CTRL_BITS bor ?SHIFT_BITS bor ?ALT_BITS) =:= 0 ->
  594. {X,Y} = wings_wm:local2global(X0, Y0),
  595. Camera = #camera{x=X,y=Y,ox=X,oy=Y},
  596. grab(),
  597. wings_cam_message(),
  598. View = wings_view:current(),
  599. {seq,push,get_wings_cam_event(Camera, Redraw, View)};
  600. wings_cam(#keyboard{sym=Sym}, _Redraw) ->
  601. arrow_key_pan(Sym);
  602. wings_cam(_, _) -> next.
  603. wings_cam_event(#mousebutton{button=1,state=?SDL_RELEASED}, Camera, _, _) ->
  604. stop_camera(Camera);
  605. wings_cam_event(#mousebutton{button=3,state=?SDL_RELEASED}, Camera, _, View) ->
  606. wings_view:set_current(View),
  607. stop_camera(Camera);
  608. wings_cam_event(#mousemotion{x=X,y=Y,state=Buttons}, Camera0, Redraw, View) ->
  609. {Dx,Dy,Camera} = camera_mouse_range(X, Y, Camera0),
  610. case Buttons band 2 of
  611. 0 -> %MMB not pressed.
  612. rotate(-Dx, -Dy);
  613. _Other -> %MMB pressed.
  614. pan(-Dx, -Dy)
  615. end,
  616. get_wings_cam_event(Camera, Redraw, View);
  617. wings_cam_event(#keyboard{sym=Sym}=Event, Camera, Redraw, _) ->
  618. case arrow_key_pan(Sym) of
  619. keep -> keep;
  620. next -> view_hotkey(Event, Camera, Redraw)
  621. end;
  622. wings_cam_event(Event, Camera, Redraw, _) ->
  623. generic_event(Event, Camera, Redraw).
  624. get_wings_cam_event(Camera, Redraw, View) ->
  625. wings_wm:dirty(),
  626. {replace,fun(Ev) -> wings_cam_event(Ev, Camera, Redraw, View) end}.
  627. wings_cam_message() ->
  628. Help = wings_msg:join([wings_msg:button_format(wings_s:accept(),
  629. ?STR(message,8,"Drag to Pan"),
  630. ?STR(message,6,"Cancel/restore view")),
  631. ?STR(message,3,"Move mouse to tumble")]),
  632. message(Help).
  633. %%%
  634. %%% Common utilities.
  635. %%%
  636. generic_event(redraw, _Camera, #state{st=St, func=none}) ->
  637. wings:redraw(St),
  638. keep;
  639. generic_event(redraw, _Camera, #state{func=Redraw}) when is_function(Redraw) ->
  640. Redraw(),
  641. keep;
  642. generic_event(#mousebutton{button=4,mod=Mod,state=?SDL_RELEASED}, _Camera, _Redraw)
  643. when Mod band ?SHIFT_BITS =/= 0 andalso Mod band ?ALT_BITS =/= 0 ->
  644. whrotate(0.5,0.0);
  645. generic_event(#mousebutton{button=5,mod=Mod,state=?SDL_RELEASED}, _Camera, _Redraw)
  646. when Mod band ?SHIFT_BITS =/= 0 andalso Mod band ?ALT_BITS =/= 0 ->
  647. whrotate(-0.5,0.0);
  648. generic_event(#mousebutton{button=4,mod=Mod,state=?SDL_RELEASED}, _Camera, _Redraw)
  649. when Mod band ?SHIFT_BITS =/= 0 ->
  650. whrotate(0.0,0.5);
  651. generic_event(#mousebutton{button=5,mod=Mod,state=?SDL_RELEASED}, _Camera, _Redraw)
  652. when Mod band ?SHIFT_BITS =/= 0 ->
  653. whrotate(0.0,-0.5);
  654. generic_event(#mousebutton{button=4,mod=Mod,state=?SDL_RELEASED}, _Camera, _Redraw)
  655. when Mod band ?CTRL_BITS =/= 0 andalso Mod band ?ALT_BITS =/= 0 ->
  656. whpan(0.05,0.0);
  657. generic_event(#mousebutton{button=5,mod=Mod,state=?SDL_RELEASED}, _Camera, _Redraw)
  658. when Mod band ?CTRL_BITS =/= 0 andalso Mod band ?ALT_BITS =/= 0 ->
  659. whpan(-0.05,0.0);
  660. generic_event(#mousebutton{button=4,mod=Mod,state=?SDL_RELEASED}, _Camera, _Redraw)
  661. when Mod band ?CTRL_BITS =/= 0 ->
  662. whpan(0.0,0.05);
  663. generic_event(#mousebutton{button=5,mod=Mod,state=?SDL_RELEASED}, _Camera, _Redraw)
  664. when Mod band ?CTRL_BITS =/= 0 ->
  665. whpan(0.0,-0.05);
  666. generic_event(#mousebutton{button=4,mod=Mod,state=?SDL_RELEASED}, _Camera, _Redraw)
  667. when Mod band ?ALT_BITS =/= 0 ->
  668. zoom_step_alt(-1);
  669. generic_event(#mousebutton{button=4,state=?SDL_RELEASED}, #st{}=St, none) ->
  670. %% Matching 'none' stops zoom aim from being activated during a drag sequence.
  671. %% Zoom aim warps the mouse to the screen's centre, and this can cause a crash
  672. %% in since drag events also depend on cursor position.
  673. aim_zoom(-1, St);
  674. generic_event(#mousebutton{button=4,state=?SDL_RELEASED}, _Camera, _Redraw) ->
  675. zoom_step(-1);
  676. generic_event(#mousebutton{button=5,mod=Mod,state=?SDL_RELEASED}, _Camera, _Redraw)
  677. when Mod band ?ALT_BITS =/= 0 ->
  678. zoom_step_alt(1);
  679. generic_event(#mousebutton{button=5,state=?SDL_RELEASED}, _, none) ->
  680. %% Matching 'none' stops zoom aim from being activated during a drag sequence
  681. zoom_step(1);
  682. generic_event(#mousebutton{button=5,state=?SDL_RELEASED}, _Camera, _Redraw) ->
  683. zoom_step(1);
  684. generic_event(_, _, _) -> keep.
  685. aim_zoom(Dir, St0) ->
  686. case wings_pref:get_value(highlight_zoom_aim) of
  687. true ->
  688. #view{origin=OriginB}=Before = wings_view:current(),
  689. {{_,Cmd},_} = wings:highlight_aim_setup(St0),
  690. wings_view:command(Cmd,St0),
  691. #view{origin=OriginA} = wings_view:current(),
  692. O = e3d_vec:zero(),
  693. if OriginA =:= O, Cmd =:= aim ->
  694. wings_view:set_current(Before),
  695. zoom_step(Dir);
  696. OriginA =:= OriginB ->
  697. zoom_step(Dir);
  698. true ->
  699. Client = wings_wm:this(),
  700. {X0,Y0} = wings_wm:win_size(Client),
  701. {X,Y} = wings_wm:local2global(X0 div 2, Y0 div 2),
  702. wings_io:warp(X,Y),
  703. zoom_step(Dir)
  704. end;
  705. false ->
  706. zoom_step(Dir)
  707. end.
  708. rotate(Dx, Dy) ->
  709. Speed = wings_pref:get_value(cam_rotation_speed,25)/25,
  710. case allow_rotation() of
  711. false -> ok;
  712. true ->
  713. View0= wings_view:current(),
  714. #view{azimuth=Az0,elevation=El0} = View0,
  715. Az = Az0 + Dx * Speed,
  716. El = El0 + Dy * Speed,
  717. View = View0#view{azimuth=Az,elevation=El,along_axis=none},
  718. wings_view:set_current(View)
  719. end.
  720. whrotate(Dx, Dy) ->
  721. case wings_pref:get_value(wheel_zooms, true) of
  722. false -> keep;
  723. true ->
  724. case wings_pref:get_value(wheel_adds, true) of
  725. false -> keep;
  726. true ->
  727. wings_wm:dirty(),
  728. View0= wings_view:current(),
  729. #view{azimuth=Az0,elevation=El0} = View0,
  730. S = 2 * wings_pref:get_value(wh_rot_spd),
  731. Az = Az0 + Dx*S,
  732. El = El0 + Dy*S,
  733. View = View0#view{azimuth=Az,elevation=El,along_axis=none},
  734. wings_view:set_current(View),
  735. keep
  736. end
  737. end.
  738. zoom_step_alt(Dir) ->
  739. case wings_pref:get_value(wheel_zooms, true) of
  740. false -> keep;
  741. true ->
  742. ZoomFactor = wings_pref:get_value(wheel_zoom_factor_alt, ?ZOOM_FACTOR_ALT),
  743. wheel_zoom(ZoomFactor,Dir)
  744. end.
  745. zoom_step(Dir) ->
  746. case wings_pref:get_value(wheel_zooms, true) of
  747. false -> keep;
  748. true ->
  749. ZoomFactor = wings_pref:get_value(wheel_zoom_factor, ?ZOOM_FACTOR),
  750. wheel_zoom(ZoomFactor,Dir)
  751. end.
  752. wheel_zoom(Factor, Dir) ->
  753. wings_wm:dirty(),
  754. #view{distance=Dist} = View = wings_view:current(),
  755. ZoomPercent = Factor/100,
  756. Delta = dist_factor(Dist)*Dir*ZoomPercent,
  757. wings_view:set_current(View#view{distance=Dist+Delta}),
  758. keep.
  759. zoom(Delta0) ->
  760. #view{distance=Dist} = View = wings_view:current(),
  761. Delta = dist_factor(Dist)*Delta0/80,
  762. wings_view:set_current(View#view{distance=Dist+Delta}).
  763. pan(Dx0, Dy0) ->
  764. #view{pan_x=PanX0,pan_y=PanY0,distance=D} = View = wings_view:current(),
  765. S = D*(1/20)/(101-wings_pref:get_value(pan_speed)),
  766. Dx = Dx0*S,
  767. Dy = Dy0*S,
  768. PanX = PanX0 + Dx,
  769. PanY = PanY0 - Dy,
  770. wings_view:set_current(View#view{pan_x=PanX,pan_y=PanY}).
  771. arrow_key_pan(?SDLK_LEFT) -> arrow_key_pan(0.05, 0.0);
  772. arrow_key_pan(?SDLK_RIGHT) -> arrow_key_pan(-0.05, 0.0);
  773. arrow_key_pan(?SDLK_UP) -> arrow_key_pan(0.0, 0.05);
  774. arrow_key_pan(?SDLK_DOWN) -> arrow_key_pan(0.0, -0.05);
  775. arrow_key_pan(_) -> next.
  776. arrow_key_pan(Dx, Dy) ->
  777. key_pan(Dx, Dy),
  778. wings_wm:dirty(),
  779. keep.
  780. key_pan(Dx0, Dy0) ->
  781. #view{pan_x=PanX0,pan_y=PanY0,distance=D} = View = wings_view:current(),
  782. S = D * (wings_pref:get_value(pan_speed_arrow_keys)/100),
  783. Dx = Dx0*S,
  784. Dy = Dy0*S,
  785. PanX = PanX0 + Dx,
  786. PanY = PanY0 - Dy,
  787. wings_view:set_current(View#view{pan_x=PanX,pan_y=PanY}).
  788. whpan(Dx0, Dy0) ->
  789. case wings_pref:get_value(wheel_zooms, true) of
  790. false -> keep;
  791. true ->
  792. case wings_pref:get_value(wheel_adds, true) of
  793. false -> keep;
  794. true ->
  795. wings_wm:dirty(),
  796. #view{pan_x=PanX0,pan_y=PanY0,distance=D} = View = wings_view:current(),
  797. S = D * (wings_pref:get_value(wh_pan_spd)/100),
  798. Dx = Dx0*S,
  799. Dy = Dy0*S,
  800. PanX = PanX0 + Dx,
  801. PanY = PanY0 - Dy,
  802. wings_view:set_current(View#view{pan_x=PanX,pan_y=PanY}),
  803. keep
  804. end
  805. end.
  806. dist_factor(Dist) ->
  807. max(abs(Dist), 0.2).
  808. stop_camera(#camera{ox=Ox,oy=Oy}) ->
  809. case wings_io:ungrab(Ox, Oy) of
  810. still_grabbed ->
  811. wings_wm:later(view_changed);
  812. no_grab ->
  813. wings_wm:release_focus(),
  814. wings_wm:dirty()
  815. end,
  816. update_sel(fun show_sel_fun/2),
  817. pop.
  818. camera_mouse_range(X0, Y0, #camera{x=OX,y=OY, xt=Xt0, yt=Yt0}=Camera) ->
  819. %% io:format("Camera Mouse Range ~p ~p~n", [{X0,Y0}, {OX,OY,Xt0,Yt0}]),
  820. {X1,Y1} = wings_wm:local2global(X0, Y0),
  821. XD0 = (X1 - OX),
  822. YD0 = (Y1 - OY),
  823. {XD,YD} = wings_pref:lowpass(XD0 + Xt0, YD0 + Yt0),
  824. if
  825. XD0 =:= 0, YD0 =:= 0 ->
  826. {0.0,0.0,Camera#camera{xt=0,yt=0}};
  827. true ->
  828. wings_io:warp(OX, OY),
  829. {XD/?CAMDIV, YD/?CAMDIV, Camera#camera{xt=XD0, yt=YD0}}
  830. end.
  831. view_hotkey(Ev, Camera, #state{st=St}) ->
  832. case wings_hotkey:event(Ev,St) of
  833. next -> keep;
  834. {view,smooth_proxy} -> keep;
  835. {view,quick_preview} -> keep;
  836. {view,Cmd} ->
  837. wings_view:command(Cmd, St),
  838. keep;
  839. _Other -> %% Hotkey pressed, Quit camera mode
  840. wings_wm:later(Ev),
  841. stop_camera(Camera)
  842. end.
  843. message(Message) ->
  844. wings_wm:message(Message),
  845. wings_wm:message_right([]).
  846. grab() ->
  847. wings_io:grab(),
  848. wings_wm:grab_focus(),
  849. update_sel(fun hide_sel_fun/2).
  850. hide_sel_fun(#dlo{sel=Sel}=D, _) ->
  851. D#dlo{sel={call,none,Sel}}.
  852. show_sel_fun(#dlo{sel={call,none,Sel}}=D, _) ->
  853. D#dlo{sel=Sel};
  854. show_sel_fun(D, _) -> D.
  855. allow_rotation() ->
  856. wings_wm:get_prop(allow_rotation).
  857. update_sel(Fun) ->
  858. case wings_pref:get_value(hide_sel_in_camera_moves) of
  859. false -> ok;
  860. true -> wings_dl:map(Fun, [])
  861. end.
  862. format([{Mod,But,Msg}|T]) ->
  863. wings_msg:join(wings_msg:mod_format(Mod, But, Msg), format(T));
  864. format([]) -> [].