1%% 2%% wings_draw.erl -- 3%% 4%% This module draws objects using OpenGL. 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 14-module(wings_draw). 15-export([refresh_dlists/1, 16 update_sel_dlist/0, 17 changed_we/2, update_normals/1, 18 split/3,original_we/1,update_dynamic/2,join/1,abort_split/1]). 19 20%% Export for plugins (and wings_proxy) that need to draw stuff 21-export([draw_flat_faces/2,draw_smooth_faces/2]). 22 23-export_type([normals/0,split/0]). 24 25-define(NEED_OPENGL, 1). 26-define(NEED_ESDL, 1). 27-include("wings.hrl"). 28-include_lib("e3d/e3d.hrl"). 29 30-import(lists, [foreach/2,reverse/1,reverse/2,member/2, 31 foldl/3,sort/1,keysort/2]). 32 33-record(split, 34 {static_vs :: [{wings_vertex:vertex_num(),e3d_vec:point()}], 35 dyn_vs=none :: 'none' | [wings_vertex:vertex_num()], 36 dyn_plan :: wings_draw_setup:plan(), %Plan for drawing dynamic faces. 37 orig_ns :: 'none' | array:array(e3d_vec:vector()), 38 orig_we :: #we{}, 39 orig_st :: #st{} %For materials 40 }). 41 42-type normals() :: 'none' 43 | array:array(e3d_vec:vector()) 44 | {normals()}. 45 46-opaque split() :: #split{}. 47 48%%% 49%%% Refresh the display lists from the contents of St. 50%%% 51%%% Invisible objects no longer have any #dlo{} entries. 52%%% 53 54refresh_dlists(St) -> 55 invalidate_dlists(St), 56 build_dlists(St), 57 update_sel_dlist(), 58 wings_develop:state_check(St, "Refresh of display lists"). 59 60invalidate_dlists(#st{selmode=Mode,sel=Sel}=St) -> 61 prepare_dlists(St), 62 case wings_dl:changed_materials(St) of 63 [] -> ok; 64 ChangedMat -> invalidate_by_mat(ChangedMat) 65 end, 66 wings_dl:map(fun(D0, Data) -> 67 D = update_mirror(D0), 68 invalidate_sel(D, Data, Mode) 69 end, Sel), 70 update_needed(St). 71 72prepare_dlists(#st{shapes=Shs}) -> 73 wings_dl:update(fun(D, A) -> 74 prepare_fun(D, A) 75 end, gb_trees:values(Shs)). 76 77prepare_fun(eol, [#we{perm=Perm}|Wes]) when ?IS_NOT_VISIBLE(Perm) -> 78 prepare_fun(eol, Wes); 79prepare_fun(eol, [We|Wes]) -> 80 D = #dlo{src_we=We,open=wings_we:is_open(We)}, 81 {changed_we(D, D),Wes}; 82prepare_fun(eol, []) -> 83 eol; 84prepare_fun(#dlo{src_we=#we{id=Id}}, 85 [#we{id=Id,perm=Perm}|Wes]) when ?IS_NOT_VISIBLE(Perm) -> 86 {deleted,Wes}; 87prepare_fun(#dlo{}=D, [#we{perm=Perm}|Wes]) when ?IS_NOT_VISIBLE(Perm) -> 88 prepare_fun(D, Wes); 89prepare_fun(#dlo{src_we=We,split=#split{}=Split}=D, [We|Wes]) -> 90 {D#dlo{src_we=We,split=Split#split{orig_we=We}},Wes}; 91 92prepare_fun(#dlo{src_we=We}=D, [We|Wes]) -> 93 %% No real change - take the latest We for possible speed-up 94 %% of further comparisons. 95 {D#dlo{src_we=We},Wes}; 96 97prepare_fun(#dlo{src_we=#we{id=Id}}=D, [#we{id=Id}=We1|Wes]) -> 98 prepare_fun_1(D, We1, Wes); 99 100prepare_fun(#dlo{}, Wes) -> 101 {deleted,Wes}. 102 103prepare_fun_1(#dlo{src_we=#we{perm=Perm0}=We0}=D, #we{perm=Perm1}=We, Wes) -> 104 case only_permissions_changed(We0, We) of 105 true -> 106 %% More efficient, and prevents an object from disappearing 107 %% if lockness was toggled while inside a secondary selection. 108 case {Perm0,Perm1} of 109 {0,1} -> {D#dlo{src_we=We},Wes}; 110 {1,0} -> {D#dlo{src_we=We},Wes}; 111 _ -> prepare_fun_2(D, We, Wes) 112 end; 113 false -> prepare_fun_2(D, We, Wes) 114 end. 115 116prepare_fun_2(#dlo{proxy=IsUsed, proxy_data=Proxy,ns=Ns}=D, We, Wes) -> 117 Open = wings_we:is_open(We), 118 {changed_we(D, #dlo{src_we=We,open=Open,mirror=none, 119 proxy=IsUsed, 120 proxy_data=wings_proxy:invalidate(Proxy, maybe), 121 ns=Ns}),Wes}. 122 123only_permissions_changed(#we{perm=P}, #we{perm=P}) -> false; 124only_permissions_changed(We0, We1) -> We0#we{perm=0} =:= We1#we{perm=0}. 125 126invalidate_by_mat(Changed0) -> 127 Changed = ordsets:from_list(Changed0), 128 wings_dl:map(fun(D, _) -> invalidate_by_mat(D, Changed) end, []). 129 130invalidate_by_mat(#dlo{work=none,vs=none,smooth=none,proxy=false}=D, _) -> 131 %% Nothing to do. 132 D; 133invalidate_by_mat(#dlo{src_we=We,proxy_data=Pd}=D, Changed) -> 134 Used = wings_facemat:used_materials(We), 135 case ordsets:is_disjoint(Used, Changed) of 136 true -> 137 %% The changed material is not used on any face in this object. 138 D; 139 false -> 140 %% The changed material is used by this object. We'll need to 141 %% invalidate the vertex buffers as well as the display lists, 142 %% because the vertex colors settings in the material may have 143 %% been changed. 144 D#dlo{work=none,edges=none,vs=none,smooth=none,vab=none, 145 proxy_data=wings_proxy:invalidate(Pd, vab)} 146 end. 147 148invalidate_sel(#dlo{src_we=#we{id=Id},src_sel=SrcSel}=D, 149 [{Id,Items}|Sel], Mode) -> 150 case SrcSel of 151 {Mode,Items} -> {D,Sel}; 152 _ -> {D#dlo{sel=none,normals=none,src_sel={Mode,Items}},Sel} 153 end; 154invalidate_sel(D, Sel, _) -> 155 {D#dlo{sel=none,src_sel=none},Sel}. 156 157changed_we(#dlo{ns={_}=Ns}, D) -> 158 D#dlo{ns=Ns}; 159changed_we(#dlo{ns=Ns}, D) -> 160 D#dlo{ns={Ns}}. 161 162update_normals(#dlo{ns={Ns0},src_we=#we{fs=Ftab}=We}=D) -> 163 Ns = update_normals_1(Ns0, gb_trees:to_list(Ftab), We), 164 D#dlo{ns=Ns}; 165update_normals(D) -> D. 166 167update_normals_1(none, Ftab, We) -> 168 update_normals_2(Ftab, [], We); 169update_normals_1(Ns, Ftab, We) -> 170 update_normals_2(Ftab, array:sparse_to_orddict(Ns), We). 171 172update_normals_2(Ftab0, Ns, We) -> 173 Ftab = wings_we:visible(Ftab0, We), 174 update_normals_3(Ftab, Ns, We, []). 175 176update_normals_3([{Face,Edge}|Fs], [{Face,Data}=Pair|Ns], We, Acc) -> 177 Ps = wings_face:vertex_positions(Face, Edge, We), 178 case Data of 179 [_|Ps] -> 180 update_normals_3(Fs, Ns, We, [Pair|Acc]); 181 {_,_,Ps} -> 182 update_normals_3(Fs, Ns, We, [Pair|Acc]); 183 _ -> 184 update_normals_3(Fs, Ns, We, [{Face,face_ns_data(Ps)}|Acc]) 185 end; 186update_normals_3([{Fa,_}|_]=Fs, [{Fb,_}|Ns], We, Acc) when Fa > Fb -> 187 update_normals_3(Fs, Ns, We, Acc); 188update_normals_3([{Face,Edge}|Fs], Ns, We, Acc) -> 189 Ps = wings_face:vertex_positions(Face, Edge, We), 190 update_normals_3(Fs, Ns, We, [{Face,face_ns_data(Ps)}|Acc]); 191update_normals_3([], _, _, Acc) -> array:from_orddict(reverse(Acc)). 192 193%% face_ns_data([Position]) -> 194%% [Normal|[Position]] | Tri or quad 195%% {Normal,[{VtxA,VtxB,VtxC}],[Position]} Tesselated polygon 196%% Given the positions for a face, calculate the normal, 197%% and tesselate the face if necessary. 198face_ns_data([_,_,_]=Ps) -> 199 [e3d_vec:normal(Ps)|Ps]; 200face_ns_data([A,B,C,D]=Ps) -> 201 N = e3d_vec:normal(Ps), 202 case wings_tesselation:is_good_triangulation(N, A, B, C, D) of 203 false -> {N,[{1,2,4},{4,2,3}],Ps}; 204 true -> [N|Ps] 205 end; 206face_ns_data(Ps0) -> 207 N = e3d_vec:normal(Ps0), 208 {Fs0,Ps} = wings_gl:triangulate(N, Ps0), 209 Fs = face_ns_data_1(Fs0, []), 210 {N,Fs,Ps}. 211 212%% "Chain" vertices if possible so that the last vertex in 213%% one triangle occurs as the first in the next triangle. 214face_ns_data_1([{A,S,C}|Fs], [{_,_,S}|_]=Acc) -> 215 face_ns_data_1(Fs, [{S,C,A}|Acc]); 216face_ns_data_1([{A,B,S}|Fs], [{_,_,S}|_]=Acc) -> 217 face_ns_data_1(Fs, [{S,A,B}|Acc]); 218face_ns_data_1([F|Fs], Acc) -> 219 face_ns_data_1(Fs, [F|Acc]); 220face_ns_data_1([], Acc) -> Acc. 221 222update_mirror(#dlo{mirror=none,src_we=#we{mirror=none}}=D) -> D; 223update_mirror(#dlo{mirror=none,src_we=#we{fs=Ftab,mirror=Face}=We}=D) -> 224 case gb_trees:is_defined(Face, Ftab) of 225 false -> 226 D#dlo{mirror=none}; 227 true -> 228 D#dlo{mirror=wings_face:mirror_matrix(Face, We)} 229 end; 230update_mirror(D) -> D. 231 232%% Find out which display lists are needed based on display modes. 233%% (Does not check whether they exist or not; that will be checked 234%% later.) 235 236update_needed(#st{selmode=vertex}=St) -> 237 case wings_pref:get_value(vertex_size) of 238 0.0 -> 239 update_needed_1([], St); 240 PointSize-> 241 update_needed_1([{vertex,PointSize}], St) 242 end; 243update_needed(St) -> update_needed_1([], St). 244 245update_needed_1(Need, St) -> 246 case wings_pref:get_value(show_normals) of 247 false -> update_needed_2(Need, St); 248 true -> update_needed_2([normals|Need], St) 249 end. 250 251update_needed_2(CommonNeed, St) -> 252 Wins = wins_of_same_class(), 253 wings_dl:map(fun(D, _) -> 254 update_needed_fun(D, CommonNeed, Wins, St) 255 end, []). 256 257update_needed_fun(#dlo{src_we=#we{perm=Perm}=We}=D, _, _, _) 258 when ?IS_LIGHT(We), ?IS_VISIBLE(Perm) -> 259 D#dlo{needed=[light]}; 260update_needed_fun(#dlo{src_we=#we{perm=Perm}=We}=D, _, _, _) 261 when ?IS_AREA_LIGHT(We), ?IS_VISIBLE(Perm) -> 262 D#dlo{needed=[edges,work]}; 263update_needed_fun(#dlo{src_we=#we{id=Id,he=Htab,pst=Pst},proxy=Proxy}=D, 264 Need0, Wins, _) -> 265 Need1 = case gb_sets:is_empty(Htab) orelse 266 not wings_pref:get_value(show_edges) of 267 false -> [hard_edges|Need0]; 268 true -> Need0 269 end, 270 Need2 = wings_plugin:check_plugins(update_dlist,Pst) ++ Need1, 271 Need = if 272 Proxy -> [proxy|Need2]; 273 true -> Need2 274 end, 275 D#dlo{needed=more_need(Wins, Id, Need)}. 276 277more_need([W|Ws], Id, Acc0) -> 278 Wire = wings_wm:get_prop(W, wireframed_objects), 279 IsWireframe = gb_sets:is_member(Id, Wire), 280 Acc = case {wings_wm:get_prop(W, workmode),IsWireframe} of 281 {false,true} -> 282 [edges,smooth|Acc0]; 283 {false,false} -> 284 [smooth|Acc0]; 285 {true,true} -> 286 [edges|Acc0]; 287 {true,false} -> 288 case wings_pref:get_value(show_edges) of 289 false -> [work|Acc0]; 290 true -> [edges,work|Acc0] 291 end 292 end, 293 more_need(Ws, Id, Acc); 294more_need([], _, Acc) -> 295 ordsets:from_list(Acc). 296 297wins_of_same_class() -> 298 case wings_wm:get_dd() of 299 geom_display_lists -> wings_u:geom_windows(); 300 _ -> [wings_wm:this()] 301 end. 302 303 304%% 305%% Rebuild all missing display lists, based on what is 306%% actually needed in D#dlo.needed. 307%% 308 309build_dlists(St) -> 310 wings_dl:map(fun(#dlo{needed=Needed}=D0, S0) -> 311 D = update_normals(D0), 312 S = update_materials(D, S0), 313 update_fun(D, Needed, S) 314 end, St). 315 316update_fun(D0, [H|T], St) -> 317 D = update_fun_2(H, D0, St), 318 update_fun(D, T, St); 319update_fun(D, [], _) -> D. 320 321update_materials(D, St) -> 322 We = original_we(D), 323 if ?IS_AREA_LIGHT(We) -> wings_light:shape_materials(We, St); 324 true -> St 325 end. 326 327update_fun_2(light, D, _) -> 328 wings_light:update(D); 329update_fun_2(work, #dlo{work=none}=D0, St) -> 330 D = wings_draw_setup:work(D0, St), 331 Dl = draw_flat_faces(D, St), 332 D#dlo{work=Dl}; 333update_fun_2(smooth, #dlo{smooth=none,proxy=false}=D0, St) -> 334 D = wings_draw_setup:smooth(D0, St), 335 {List,Tr} = draw_smooth_faces(D, St), 336 D#dlo{smooth=List,transparent=Tr}; 337update_fun_2(smooth, #dlo{proxy=true}=D0, St) -> 338 wings_proxy:smooth(D0,St); 339update_fun_2({vertex,_PtSize}, #dlo{vs=none,src_we=We}=D, _) -> 340 Data = vertices_f32(visible_vertices(We)), 341 Unsel = vbo_draw_arrays(?GL_POINTS, Data), 342 D#dlo{vs=Unsel}; 343update_fun_2(hard_edges, #dlo{hard=none,src_we=#we{he=Htab}=We}=D, _) -> 344 Edges = gb_sets:to_list(wings_we:visible_edges(Htab, We)), 345 Data = edges_f32(Edges, We), 346 Hard = vbo_draw_arrays(?GL_LINES, Data), 347 D#dlo{hard=Hard}; 348update_fun_2(edges, #dlo{edges=none,ns=Ns}=D, _) -> 349 EdgeDl = make_edge_dl(array:sparse_to_list(Ns)), 350 D#dlo{edges=EdgeDl}; 351update_fun_2(normals, D, _) -> 352 make_normals_dlist(D); 353update_fun_2(proxy, D, St) -> 354 wings_proxy:update(D, St); 355 356%%%% Check if plugins using the Pst need a draw list updated. 357update_fun_2({plugin,{Plugin,{_,_}=Data}},#dlo{plugins=Pdl}=D,St) -> 358 case lists:keytake(Plugin,1,Pdl) of 359 false -> 360 Plugin:update_dlist(Data,D,St); 361 {_,{Plugin,{_,none}},Pdl0} -> 362 Plugin:update_dlist(Data,D#dlo{plugins=Pdl0},St); 363 _ -> D 364 end; 365 366update_fun_2(_, D, _) -> D. 367 368make_edge_dl(Ns) -> 369 {Tris,Quads,Polys,PsLens} = make_edge_dl_bin(Ns, <<>>, <<>>, <<>>, []), 370 T = vbo_draw_arrays(?GL_TRIANGLES, Tris), 371 Q = vbo_draw_arrays(?GL_QUADS, Quads), 372 DP = case wings_util:min_wx({1,8}) of 373 true -> 374 {_,Ss,Ls} = foldl(fun(N, {Start, Ss, Ls}) -> 375 gl:drawArrays(?GL_POLYGON, Start, N), 376 {N+Start, <<Ss/binary, Start:?UI32>>, <<Ls/binary, N:?UI32>>} 377 end, {0, <<>>, <<>>}, PsLens), 378 fun(RS) -> 379 gl:multiDrawArrays(?GL_POLYGON, Ss, Ls), 380 RS 381 end; 382 false -> 383 fun(RS) -> 384 foldl(fun(Length, Start) -> 385 gl:drawArrays(?GL_POLYGON, Start, Length), 386 Length+Start 387 end, 0, PsLens), 388 RS 389 end 390 end, 391 P = wings_vbo:new(DP, Polys), 392 [T,Q,P]. 393 394make_edge_dl_bin([[_|[{X1,Y1,Z1},{X2,Y2,Z2},{X3,Y3,Z3}]]|Ns], 395 Tris0, Quads, Polys, PsLens) -> 396 Tris = <<Tris0/binary,X1:?F32,Y1:?F32,Z1:?F32,X2:?F32,Y2:?F32,Z2:?F32, 397 X3:?F32,Y3:?F32,Z3:?F32>>, 398 make_edge_dl_bin(Ns, Tris, Quads, Polys, PsLens); 399make_edge_dl_bin([[_|[{X1,Y1,Z1},{X2,Y2,Z2},{X3,Y3,Z3},{X4,Y4,Z4}]]|Ns], 400 Tris, Quads0, Polys, PsLens) -> 401 Quads = <<Quads0/binary,X1:?F32,Y1:?F32,Z1:?F32,X2:?F32,Y2:?F32,Z2:?F32, 402 X3:?F32,Y3:?F32,Z3:?F32,X4:?F32,Y4:?F32,Z4:?F32>>, 403 make_edge_dl_bin(Ns, Tris, Quads, Polys, PsLens); 404make_edge_dl_bin([{_,_,VsPos}|Ns], Tris, Quads, Polys, PsLens) -> 405 Poly = vertices_f32(VsPos), 406 NoVs = byte_size(Poly) div 12, 407 make_edge_dl_bin(Ns,Tris,Quads,<<Polys/binary,Poly/binary>>,[NoVs|PsLens]); 408make_edge_dl_bin([], Tris, Quads, Polys, PsLens) -> 409 {Tris, Quads, Polys, reverse(PsLens)}. 410 411edges_f32(Edges, #we{es=Etab,vp=Vtab}) -> 412 edges_f32(Edges,Etab,Vtab,<<>>). 413edges_f32([Edge|Edges],Etab,Vtab,Bin0) -> 414 #edge{vs=Va,ve=Vb} = array:get(Edge, Etab), 415 {X1,Y1,Z1} = array:get(Va, Vtab), 416 {X2,Y2,Z2} = array:get(Vb, Vtab), 417 Bin = <<Bin0/binary,X1:?F32,Y1:?F32,Z1:?F32,X2:?F32,Y2:?F32,Z2:?F32>>, 418 edges_f32(Edges, Etab, Vtab, Bin); 419edges_f32([],_,_,Bin) -> 420 Bin. 421 422vertices_f32([{_,{_,_,_}}|_]=List) -> 423 << <<X:?F32,Y:?F32,Z:?F32>> || {_,{X,Y,Z}} <- List >>; 424vertices_f32([{_,_,_}|_]=List) -> 425 << <<X:?F32,Y:?F32,Z:?F32>> || {X,Y,Z} <- List >>; 426vertices_f32([]) -> <<>>. 427 428vbo_draw_arrays(Type, Data) -> 429 N = byte_size(Data) div 12, 430 D = fun(RS) -> 431 gl:drawArrays(Type, 0, N), 432 RS 433 end, 434 wings_vbo:new(D, Data). 435 436visible_vertices(#we{vp=Vtab0}=We) -> 437 case wings_we:is_open(We) of 438 false -> 439 array:sparse_to_list(Vtab0); 440 true -> 441 Vis0 = wings_we:visible_vs(We), 442 Vis = sofs:from_external(Vis0, [vertex]), 443 Vtab = sofs:from_external(array:sparse_to_orddict(Vtab0), 444 [{vertex,position}]), 445 sofs:to_external(sofs:image(Vtab, Vis)) 446 end. 447 448%%% 449%%% Update the selection display list. 450%%% 451 452update_sel_dlist() -> 453 wings_dl:map(fun(D, _) -> 454 update_sel(D) 455 end, []). 456 457update_sel(#dlo{src_we=We}=D) when ?IS_LIGHT(We) -> {D,[]}; 458update_sel(#dlo{sel=none,src_sel={body,_}}=D) -> 459 update_sel_all(D); 460update_sel(#dlo{split=none,sel=none,src_sel={face,Faces}, 461 src_we=#we{fs=Ftab}}=D) -> 462 %% If we are not dragging (no dlists splitted), we can 463 %% optimize the showing of the selection. 464 case gb_trees:size(Ftab) =:= gb_sets:size(Faces) of 465 true -> update_sel_all(D); 466 false -> update_face_sel(gb_sets:to_list(Faces), D) 467 end; 468update_sel(#dlo{sel=none,src_sel={face,Faces}}=D) -> 469 %% We are dragging. Don't try to be tricky here. 470 update_face_sel(gb_sets:to_list(Faces), D); 471update_sel(#dlo{sel=none,src_sel={edge,Edges}}=D) -> 472 #dlo{src_we=We} = D, 473 Data = edges_f32(gb_sets:to_list(Edges), We), 474 Sel = vbo_draw_arrays(?GL_LINES, Data), 475 D#dlo{sel=Sel}; 476update_sel(#dlo{sel=none,src_sel={vertex,Vs}}=D) -> 477 #dlo{src_we=#we{vp=Vtab0}} = D, 478 Vtab1 = array:sparse_to_orddict(Vtab0), 479 Sel = case length(Vtab1) =:= gb_sets:size(Vs) of 480 true -> 481 Vtab1; 482 false -> 483 Vtab = sofs:from_external(Vtab1, [{vertex,data}]), 484 R = sofs:from_external(gb_sets:to_list(Vs), [vertex]), 485 sofs:to_external(sofs:image(Vtab, R)) 486 end, 487 Data = vertices_f32(Sel), 488 Points = vbo_draw_arrays(?GL_POINTS, Data), 489 D#dlo{sel=Points}; 490update_sel(#dlo{}=D) -> D. 491 492%% Select all faces. 493update_sel_all(#dlo{vab=#vab{face_vs=Vs}=Vab}=D) when Vs =/= none -> 494 Count = wings_draw_setup:face_vertex_count(D), 495 F = fun(RS0) -> 496 RS = wings_draw_setup:enable_pointers(Vab, [], RS0), 497 gl:drawArrays(?GL_TRIANGLES, 0, Count), 498 wings_draw_setup:disable_pointers(Vab, RS) 499 end, 500 D#dlo{sel={call,F,Vab}}; 501update_sel_all(#dlo{src_we=#we{fs=Ftab}}=D) -> 502 %% No vertex arrays to re-use. Rebuild from scratch. 503 update_face_sel(gb_trees:keys(Ftab), D). 504 505update_face_sel(Fs0, #dlo{src_we=We,vab=#vab{face_vs=Vs,face_map=Map}=Vab}=D) 506 when Vs =/= none -> 507 Fs = wings_we:visible(Fs0, We), 508 F = case wings_util:min_wx({1,8}) of 509 true -> 510 Collect = fun(Face, {Ss,Es}) -> 511 {Start,NoElements} = array:get(Face, Map), 512 {<<Ss/binary, Start:?UI32>>, <<Es/binary, NoElements:?UI32>>} 513 end, 514 {Start,NoElements} = lists:foldl(Collect, {<<>>,<<>>}, lists:reverse(Fs)), 515 fun(RS0) -> 516 RS = wings_draw_setup:enable_pointers(Vab, [], RS0), 517 gl:multiDrawArrays(?GL_TRIANGLES, Start, NoElements), 518 wings_draw_setup:disable_pointers(Vab, RS) 519 end; 520 false -> 521 SN = [array:get(Face, Map) || Face <- Fs], 522 fun(RS0) -> 523 RS = wings_draw_setup:enable_pointers(Vab, [], RS0), 524 [gl:drawArrays(?GL_TRIANGLES, S, N) || {S,N} <- SN], 525 wings_draw_setup:disable_pointers(Vab, RS) 526 end 527 end, 528 Sel = {call,F,Vab}, 529 D#dlo{sel=Sel}; 530update_face_sel(Fs0, #dlo{src_we=We}=D) -> 531 Fs = wings_we:visible(Fs0, We), 532 Data = update_face_sel_1(Fs, D, <<>>), 533 Sel = vbo_draw_arrays(?GL_TRIANGLES, Data), 534 D#dlo{sel=Sel}. 535 536update_face_sel_1(Fs, #dlo{ns=none,src_we=We}, Bin) -> 537 update_face_sel_2(Fs, We, Bin); 538update_face_sel_1(Fs, #dlo{ns={_},src_we=We}, Bin) -> 539 update_face_sel_2(Fs, We, Bin); 540update_face_sel_1(Fs, D, Bin) -> 541 update_face_sel_2(Fs, D, Bin). 542 543update_face_sel_2([F|Fs], D, Bin0) -> 544 Bin = unlit_face_bin(F, D, Bin0), 545 update_face_sel_2(Fs, D, Bin); 546update_face_sel_2([], _, Bin) -> Bin. 547 548%% Draw a face without any lighting. 549unlit_face_bin(Face, #dlo{ns=Ns}, Bin) -> 550 case array:get(Face, Ns) of 551 [_|VsPos] -> unlit_plain_face(VsPos, Bin); 552 {_,Fs,VsPos} -> unlit_plain_face(Fs, VsPos, Bin) 553 end; 554unlit_face_bin(Face, #we{fs=Ftab}=We, Bin) -> 555 Edge = gb_trees:get(Face, Ftab), 556 unlit_face_bin(Face, Edge, We, Bin). 557 558unlit_face_bin(Face, Edge, We, Bin) -> 559 Ps = wings_face:vertex_positions(Face, Edge, We), 560 case face_ns_data(Ps) of 561 [_|VsPos] -> unlit_plain_face(VsPos, Bin); 562 {_,Fs,VsPos} -> unlit_plain_face(Fs, VsPos, Bin) 563 end. 564 565unlit_plain_face([{X1,Y1,Z1},{X2,Y2,Z2},{X3,Y3,Z3}], Bin) -> 566 <<Bin/binary, 567 X1:?F32,Y1:?F32,Z1:?F32, 568 X2:?F32,Y2:?F32,Z2:?F32, 569 X3:?F32,Y3:?F32,Z3:?F32>>; 570unlit_plain_face([{X1,Y1,Z1},{X2,Y2,Z2},{X3,Y3,Z3},{X4,Y4,Z4}], Bin) -> 571 <<Bin/binary, 572 X1:?F32,Y1:?F32,Z1:?F32, 573 X2:?F32,Y2:?F32,Z2:?F32, 574 X3:?F32,Y3:?F32,Z3:?F32, 575 X3:?F32,Y3:?F32,Z3:?F32, 576 X4:?F32,Y4:?F32,Z4:?F32, 577 X1:?F32,Y1:?F32,Z1:?F32>>. 578 579unlit_plain_face(Fs, VsPos, Bin) -> 580 unlit_plain_face_1(Fs, list_to_tuple(VsPos), Bin). 581 582unlit_plain_face_1([{A,B,C}|Fs], Vtab, Bin0) -> 583 {X1,Y1,Z1} = element(A, Vtab), 584 {X2,Y2,Z2} = element(B, Vtab), 585 {X3,Y3,Z3} = element(C, Vtab), 586 Bin = <<Bin0/binary, 587 X1:?F32,Y1:?F32,Z1:?F32, 588 X2:?F32,Y2:?F32,Z2:?F32, 589 X3:?F32,Y3:?F32,Z3:?F32>>, 590 unlit_plain_face_1(Fs, Vtab, Bin); 591unlit_plain_face_1([], _, Bin) -> Bin. 592 593%%% 594%%% Splitting of objects into two display lists. 595%%% 596 597split(#dlo{ns={Ns}}=D, Vs, St) -> 598 split_1(D#dlo{ns=Ns}, Vs, St); 599split(D, Vs, St) -> split_1(D, Vs, St). 600 601split_1(#dlo{split=#split{orig_we=#we{}=We,orig_ns=Ns}}=D, Vs, St) -> 602 split_2(D#dlo{src_we=We,ns=Ns}, Vs, update_materials(D, St)); 603split_1(D, Vs, St) -> 604 split_2(D, Vs, update_materials(D, St)). 605 606split_2(#dlo{mirror=M,src_sel=Sel,src_we=#we{fs=Ftab}=We, 607 proxy=UsesProxy,needed=Needed,open=Open}=D, 608 Vs0, St) -> 609 Vs = sort(Vs0), 610 Faces = wings_we:visible(wings_face:from_vs(Vs, We), We), 611 StaticVs = static_vs(Faces, Vs, We), 612 613 VisFtab = wings_we:visible(gb_trees:to_list(Ftab), We), 614 {Work,#dlo{ns=Ns},FtabStatic,FtabDyn} = 615 split_faces(D, VisFtab, Faces, St), 616 617 %% To support commands that create new objects with static 618 %% geometry (such as Shell Extrude), we must be sure to pass 619 %% on the new normals table or the static edges will not be 620 %% visible. 621 StaticEdgeDl = make_static_edges(FtabStatic, D#dlo{ns=Ns}), 622 {DynVs,VsDlist} = split_vs_dlist(Vs, StaticVs, Sel, We), 623 624 WeDyn = wings_facemat:gc(We#we{fs=gb_trees:from_orddict(FtabDyn)}), 625 DynPlan = wings_draw_setup:prepare(FtabDyn, We, St), 626 StaticVtab = insert_vtx_data(StaticVs, We#we.vp, []), 627 628 Pd = wings_proxy:split_proxy(D, Vs, St), 629 630 Split = #split{static_vs=StaticVtab,dyn_vs=DynVs, 631 dyn_plan=DynPlan,orig_ns=Ns, 632 orig_we=We,orig_st=St}, 633 #dlo{work=Work,edges=[StaticEdgeDl],mirror=M,vs=VsDlist, 634 src_sel=Sel,src_we=WeDyn,split=Split, 635 proxy=UsesProxy, proxy_data=Pd, 636 needed=Needed,open=Open}. 637 638static_vs(Fs, Vs, We) -> 639 VsSet = gb_sets:from_ordset(Vs), 640 Fun = fun(V, _, _, A) -> 641 case gb_sets:is_member(V, VsSet) of 642 false -> [V|A]; 643 true -> A 644 end 645 end, 646 static_vs_1(Fs, Fun, We, []). 647 648static_vs_1([F|Fs], Fun, We, Acc) -> 649 static_vs_1(Fs, Fun, We, wings_face:fold(Fun, Acc, F, We)); 650static_vs_1([], _, _, Acc) -> ordsets:from_list(Acc). 651 652split_faces(#dlo{needed=Need}=D0, Ftab0, Fs0, St) -> 653 Ftab = sofs:from_external(Ftab0, [{face,data}]), 654 Fs = sofs:from_external(Fs0, [face]), 655 {DynFtab0,StaticFtab0} = sofs:partition(1, Ftab, Fs), 656 DynFtab = sofs:to_external(DynFtab0), 657 StaticFtab = sofs:to_external(StaticFtab0), 658 case member(work, Need) orelse member(smooth, Need) of 659 false -> 660 %% This is wireframe mode. We don't need any 661 %% 'work' display list for faces. 662 {none,D0,StaticFtab,DynFtab}; 663 true -> 664 %% Faces needed. (Either workmode or smooth mode.) 665 %% 666 %% Make sure that every static face has an entry 667 %% in the normals array. Most of the time, this is 668 %% not needed because newly created faces are 669 %% dynamic, but it can happen in rare circumstances, 670 %% for instance with the Intrude command if there are holes 671 %% or with the new Shell Extrude command. 672 D1 = split_new_normals(StaticFtab, D0), 673 674 StaticPlan = wings_draw_setup:prepare(StaticFtab, D1, St), 675 D = wings_draw_setup:flat_faces(StaticPlan, D1), 676 Dl = draw_flat_faces(D, St), 677 {[Dl],D1,StaticFtab,DynFtab} 678 end. 679 680split_new_normals(Ftab, #dlo{ns=Ns0,src_we=We}=D) -> 681 Ns = split_new_normals(Ftab, We, Ns0), 682 D#dlo{ns=Ns}. 683 684split_new_normals([{Face,Edge}|T], We, none) -> 685 %% No normals means that a new object was created in an 686 %% interactive command (Shell Extrude). 687 Ps = wings_face:vertex_positions(Face, Edge, We), 688 Ns = array:set(Face, face_ns_data(Ps), array:new()), 689 split_new_normals(T, We, Ns); 690split_new_normals([{Face,Edge}|T], We, Ns0) -> 691 case array:get(Face, Ns0) of 692 undefined -> 693 Ps = wings_face:vertex_positions(Face, Edge, We), 694 Ns = array:set(Face, face_ns_data(Ps), Ns0), 695 split_new_normals(T, We, Ns); 696 _ -> 697 split_new_normals(T, We, Ns0) 698 end; 699split_new_normals([], _, Ns) -> Ns. 700 701make_static_edges(_Ftab, #dlo{ns=none}) -> 702 make_edge_dl([]); 703make_static_edges(Ftab, #dlo{ns=Ns}) -> 704 make_edge_dl([array:get(F, Ns) || {F,_} <- Ftab]). 705 706insert_vtx_data([V|Vs], Vtab, Acc) -> 707 insert_vtx_data(Vs, Vtab, [{V,array:get(V, Vtab)}|Acc]); 708insert_vtx_data([], _, Acc) -> reverse(Acc). 709 710split_vs_dlist(Vs, StaticVs, {vertex,SelVs0}, #we{vp=Vtab}=We) -> 711 case wings_pref:get_value(vertex_size) of 712 0.0 -> {none,none}; 713 _PtSize -> 714 DynVs = sofs:from_external(lists:merge(Vs, StaticVs), [vertex]), 715 SelVs = sofs:from_external(gb_sets:to_list(SelVs0), [vertex]), 716 UnselDyn0 = case wings_pref:get_value(hide_sel_while_dragging) of 717 false -> sofs:difference(DynVs, SelVs); 718 true -> DynVs 719 end, 720 UnselDyn = sofs:to_external(UnselDyn0), 721 List0 = sofs:from_external(array:sparse_to_orddict(Vtab), 722 [{vertex,info}]), 723 List1 = sofs:drestriction(List0, DynVs), 724 List2 = sofs:to_external(List1), 725 List = wings_we:visible_vs(List2, We), 726 Data = << <<X:?F32,Y:?F32,Z:?F32>> || {_,{X,Y,Z}} <- List >>, 727 Unsel = vbo_draw_arrays(?GL_POINTS, Data), 728 {UnselDyn,[Unsel]} 729 end; 730split_vs_dlist(_, _, _, _) -> {none,none}. 731 732original_we(#dlo{split=#split{orig_we=We}}) -> We; 733original_we(#dlo{src_we=We}) -> We. 734 735%%% 736%%% Updating of the dynamic part of a split dlist. 737%%% 738 739update_dynamic(#dlo{src_we=We}=D0, Vtab) when ?IS_LIGHT(We) -> 740 D1 = wings_light:update_dynamic(D0, Vtab), 741 742 %% We actually don't need the normal table (yet) for a light when 743 %% dragging it, but we'll need the table when picking a light. 744 %% It is easiest to always keep the normals up-to-date than attempting 745 %% to build when have finished dragging (since there is also the 746 %% Tweak mode, which does dragging in a slightly different way). 747 D = changed_we(D0, D1), 748 update_normals(D); 749update_dynamic(#dlo{src_we=We0, 750 split=#split{orig_st=St,static_vs=StaticVs}}=D0, 751 Vtab0) -> 752 Vtab1 = keysort(1, StaticVs++Vtab0), 753 Vtab = array:from_orddict(Vtab1), 754 We = We0#we{vp=Vtab}, 755 D1 = D0#dlo{src_we=We}, 756 D2 = changed_we(D0, D1), 757 D3 = update_normals(D2), 758 D4 = dynamic_faces(D3), 759 D5 = dynamic_edges(D4), 760 D6 = dynamic_influence(D5), 761 D = wings_proxy:update_dynamic(Vtab0, St, D6), 762 dynamic_vs(D). 763 764dynamic_faces(#dlo{work=[Work|_], 765 split=#split{dyn_plan=DynPlan,orig_st=St}}=D0) -> 766 D = wings_draw_setup:flat_faces(DynPlan, D0), 767 Dl = draw_flat_faces(D, St), 768 D#dlo{work=[Work,Dl],vab=none}; 769dynamic_faces(#dlo{work=none}=D) -> D. 770 771dynamic_edges(#dlo{edges=[StaticEdge|_],ns=Ns}=D) -> 772 EdgeDl = make_edge_dl(array:sparse_to_list(Ns)), 773 D#dlo{edges=[StaticEdge,EdgeDl]}. 774 775dynamic_vs(#dlo{split=#split{dyn_vs=none}}=D) -> D; 776dynamic_vs(#dlo{src_we=#we{vp=Vtab},vs=[Static|_], 777 split=#split{dyn_vs=DynVs}}=D) -> 778 Data = foldl(fun(V, Bin) -> 779 {X1,Y1,Z1} = array:get(V, Vtab), 780 <<Bin/binary, X1:?F32,Y1:?F32,Z1:?F32>> 781 end, <<>>, DynVs), 782 Unsel = vbo_draw_arrays(?GL_POINTS, Data), 783 D#dlo{vs=[Static,Unsel]}. 784 785% added by Micheus 786dynamic_influence(#dlo{src_we=#we{pst=Pst},split=#split{orig_st=St}}=D) -> 787 % it was necessary to leave only the wings_tweak's pst information in 788 % order to avoid problems when drawing if magnet mask is enabled. 789 case gb_trees:lookup(wings_tweak,Pst) of 790 none -> D; 791 {value, WeakData} -> 792 TmpPst=gb_trees:enter(wings_tweak,WeakData, gb_trees:empty()), 793 Needed = wings_plugin:check_plugins(update_dlist,TmpPst), 794 % plugins=[] will force the wings_tweak:update_dlist function be called 795 update_fun(D#dlo{plugins=[]},Needed,St) 796 end. 797 798%%% 799%%% Abort a split. 800%%% 801 802abort_split(#dlo{split=#split{orig_ns=Ns}}=D) -> 803 %% Restoring the original normals will probably be beneficial. 804 D#dlo{split=none,ns=Ns}; 805abort_split(D) -> D. 806 807%%% 808%%% Re-joining of display lists that have been split. 809%%% 810 811join(#dlo{src_we=#we{vp=Vtab0}=SrcWe,ns=Ns1,split=#split{orig_we=We0,orig_ns=Ns0}, 812 proxy_data=PD}=D) -> 813 #we{vp=OldVtab} = We0, 814 Vtab = join_update(Vtab0, OldVtab), 815 We = wings_va:merge([SrcWe], We0#we{vp=Vtab}), 816 Ns = join_ns(We, Ns1, Ns0), 817 D#dlo{vs=none,drag=none,sel=none,split=none,src_we=We,ns=Ns, 818 proxy_data=wings_proxy:reset_dynamic(PD)}. 819 820join_ns(_, NsNew, none) -> 821 NsNew; 822join_ns(#we{fs=Ftab}, NsNew, NsOld) -> 823 join_ns_1(array:sparse_to_orddict(NsNew), 824 array:sparse_to_orddict(NsOld), Ftab, []). 825 826join_ns_1([{Face,_}=El|FsNew], [{Face,_}|FsOld], Ftab, Acc) -> 827 %% Same face: Use new contents. 828 join_ns_1(FsNew, FsOld, Ftab, [El|Acc]); 829join_ns_1([{Fa,_}|_]=FsNew, [{Fb,_}=El|FsOld], Ftab, Acc) when Fa > Fb -> 830 %% Face only in old list: Check in Ftab if it should be kept. 831 case gb_trees:is_defined(Fb, Ftab) of 832 false -> join_ns_1(FsNew, FsOld, Ftab, Acc); 833 true -> join_ns_1(FsNew, FsOld, Ftab, [El|Acc]) 834 end; 835join_ns_1([El|FsNew], FsOld, Ftab, Acc) -> 836 %% Fa < Fb: New face. 837 join_ns_1(FsNew, FsOld, Ftab, [El|Acc]); 838join_ns_1([], Fs, _, Acc) -> 839 array:from_orddict(reverse(Acc, Fs)). 840 841join_update(New, Old) -> 842 join_update(array:sparse_to_orddict(New), array:sparse_to_orddict(Old), Old). 843 844join_update([Same|New], [Same|Old], Acc) -> 845 join_update(New, Old, Acc); 846join_update([{V,P0}|New], [{V,OldP}|Old], Acc) -> 847 P = tricky_share(P0, OldP), 848 join_update(New, Old, array:set(V, P, Acc)); 849join_update(New, [_|Old], Acc) -> 850 join_update(New, Old, Acc); 851join_update([], _, Acc) -> Acc. 852 853%% Too obvious to comment. :-) 854tricky_share({X,Y,Z}=New, {OldX,OldY,OldZ}) 855 when X =/= OldX, Y =/= OldY, Z =/= OldZ -> New; 856tricky_share({X,Y,Z}, {X,Y,_}=Old) -> 857 setelement(3, Old, Z); 858tricky_share({X,Y,Z}, {X,_,Z}=Old) -> 859 setelement(2, Old, Y); 860tricky_share({X,Y,Z}, {_,Y,Z}=Old) -> 861 setelement(1, Old, X); 862tricky_share({X,Y,Z}, {X,_,_}=Old) -> 863 {element(1, Old),Y,Z}; 864tricky_share({X,Y,Z}, {_,Y,_}=Old) -> 865 {X,element(2, Old),Z}; 866tricky_share({X,Y,Z}, {_,_,Z}=Old) -> 867 {X,Y,element(3, Old)}. 868 869%%% 870%%% Drawing routines for workmode. 871%%% 872 873draw_flat_faces(#dlo{vab=Vab}, St) -> 874 draw_flat_faces(Vab, St); 875draw_flat_faces(#vab{mat_map=MatMap}=Vab, #st{mat=Mtab}) -> 876 Extra = [colors,face_normals,uvs,tangents], 877 draw_mat_faces(Vab, Extra, MatMap, Mtab). 878 879%%% 880%%% Smooth drawing. 881%%% 882 883draw_smooth_faces(#dlo{vab=Vab},St) -> 884 draw_smooth_faces(Vab,St); 885 886draw_smooth_faces(#vab{mat_map=MatMap}=Vab, #st{mat=Mtab}) -> 887 Extra = [colors,vertex_normals,uvs,tangents], 888 889 %% Partition into transparent and solid material face groups. 890 {Transparent,Solid} = 891 lists:partition(fun({Mat,_,_,_}) -> 892 wings_material:is_transparent(Mat, Mtab) 893 end, MatMap), 894 895 %% Create display list for solid faces. 896 DrawSolid = draw_mat_faces(Vab, Extra, Solid, Mtab), 897 898 %% Create display list for transparent faces if there are 899 %% any transparent faces. 900 case Transparent of 901 [] -> 902 %% All faces are solid. 903 {[DrawSolid,none],false}; 904 _ -> 905 %% Create the display list for the transparent faces. 906 DrawTr = draw_mat_faces(Vab, Extra, Transparent, Mtab), 907 {[DrawSolid,DrawTr],true} 908 end. 909 910draw_mat_faces(Vab, Extra, MatGroups, Mtab) -> 911 ActiveColor = wings_draw_setup:has_active_color(Vab), 912 D = fun(RS0) -> 913 RS1 = wings_draw_setup:enable_pointers(Vab, Extra, RS0), 914 RS = do_draw_mat_faces(MatGroups, Mtab, ActiveColor, RS1), 915 wings_draw_setup:disable_pointers(Vab, RS) 916 end, 917 {call,D,Vab}. 918 919do_draw_mat_faces(MatGroups, Mtab, ActiveColor, RS0) -> 920 %% Show materials. 921 foldl( 922 fun({{'_area_light_',_}=Light,Type,Start,NumElements}, RS1) -> 923 [Color] = gb_trees:get(Light, Mtab), 924 RS2 = wings_shaders:set_uloc(light_color, Color, RS1), 925 gl:drawArrays(Type, Start, NumElements), 926 RS2; 927 ({Mat,Type,Start,NumElements}, RS1) -> 928 DeApply = wings_material:apply_material(Mat, Mtab, ActiveColor, RS1), 929 gl:drawArrays(Type, Start, NumElements), 930 DeApply() 931 end, RS0, MatGroups). 932 933%% 934%% Draw normals for the selected elements. 935%% 936 937make_normals_dlist(#dlo{normals=none,src_we=We,src_sel={Mode,Elems}}=D) -> 938 Normals = make_normals_dlist_1(Mode, Elems, We), 939 VectorColor = wings_pref:get_value(normal_vector_color), 940 N = byte_size(Normals) div 12, 941 Draw0 = fun(RS) -> 942 gl:color3fv(VectorColor), 943 gl:drawArrays(?GL_LINES, 0, N), 944 RS 945 end, 946 Draw = wings_vbo:new(Draw0, Normals), 947 D#dlo{normals=Draw}; 948make_normals_dlist(#dlo{src_sel=none}=D) -> D#dlo{normals=none}; 949make_normals_dlist(D) -> D. 950 951make_normals_dlist_1(vertex, Vs, #we{vp=Vtab}=We) -> 952 Length = wings_pref:get_value(normal_vector_size), 953 gb_sets:fold( 954 fun(V, Bin) -> 955 {X1,Y1,Z1} = Pos = array:get(V, Vtab), 956 N = wings_vertex:normal(V, We), 957 {X2,Y2,Z2} = e3d_vec:add_prod(Pos, N, Length), 958 <<Bin/binary, X1:?F32,Y1:?F32,Z1:?F32,X2:?F32,Y2:?F32,Z2:?F32>> 959 end, <<>>, Vs); 960make_normals_dlist_1(edge, Edges, #we{es=Etab,vp=Vtab}=We) -> 961 Et0 = sofs:relation(array:sparse_to_orddict(Etab), [{edge,data}]), 962 Es = sofs:from_external(gb_sets:to_list(Edges), [edge]), 963 Et1 = sofs:restriction(Et0, Es), 964 Et = sofs:to_external(Et1), 965 Length = wings_pref:get_value(normal_vector_size), 966 foldl(fun({_,#edge{vs=Va,ve=Vb,lf=Lf,rf=Rf}}, Bin) -> 967 PosA = array:get(Va, Vtab), 968 PosB = array:get(Vb, Vtab), 969 {X1,Y1,Z1} = Mid = e3d_vec:average(PosA, PosB), 970 N = e3d_vec:average(wings_face:normal(Lf, We), 971 wings_face:normal(Rf, We)), 972 {X2,Y2,Z2} = e3d_vec:add_prod(Mid, N, Length), 973 <<Bin/binary, X1:?F32,Y1:?F32,Z1:?F32,X2:?F32,Y2:?F32,Z2:?F32>> 974 end, <<>>, Et); 975make_normals_dlist_1(face, Faces, We) -> 976 Length = wings_pref:get_value(normal_vector_size), 977 foldl(fun(Face, Bin) -> 978 Vs = wings_face:vertices_cw(Face, We), 979 {X1,Y1,Z1} = C = wings_vertex:center(Vs, We), 980 N = wings_face:face_normal_cw(Vs, We), 981 {X2,Y2,Z2} = e3d_vec:add_prod(C, N, Length), 982 <<Bin/binary, X1:?F32,Y1:?F32,Z1:?F32,X2:?F32,Y2:?F32,Z2:?F32>> 983 end, <<>>, wings_we:visible(gb_sets:to_list(Faces), We)); 984make_normals_dlist_1(body, _, #we{fs=Ftab}=We) -> 985 make_normals_dlist_1(face, gb_sets:from_list(gb_trees:keys(Ftab)), We). 986