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