1%%
2%%  wings_draw_setup.erl --
3%%
4%%     Setup and Create data binaries for drawing
5%%
6%%  Copyright (c) 2010-2011 Dan Gudmundsson & Björn 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_setup).
15
16-export([we/3]).  %% For plugins
17
18-export([work/2,smooth/2,prepare/3,prepare/4,flat_faces/2]).
19-export([enable_pointers/3,disable_pointers/2]).
20-export([face_vertex_count/1,has_active_color/1]).
21
22%% Used by wings_proxy.
23-export([create_vab/4,create_tangent_vab/5,
24	 add_ts/5,add_tangents/3]).
25
26-export_type([face_map/0,face_tris/0,plan/0]).
27
28-define(NEED_OPENGL, 1).
29-include("wings.hrl").
30
31-import(lists, [reverse/1,sort/1,foldl/3]).
32
33
34-type face_tris() :: {non_neg_integer(),non_neg_integer()}.
35-type face_map() :: array:array(face_tris()) | [face_tris()].
36
37-type material_name() :: atom().
38-type plan_type() :: 'color' | 'color_uv' | 'color_uv_tangent'
39                   | 'plain' | 'uv' | 'uv_tangent'.
40-type plan() :: {plan_type(),[{material_name(),
41                               [{wings_face:face_num(),wings_edge:edge_num()}]}]}.
42
43%%%
44%%% we(We, [Option], St) -> #vab{} See wings.hrl
45%%%    Generates rendering buffers from a we,
46%%%    reuses data if available.
47%%% Options are:
48%%%    {smooth, true|false}                            default false
49%%%    {subdiv, Level :: integer()}                    default 0
50%%%    {attribs, undefined|plain|uv|color|color_uv}    default undefined
51%%%              undefined -> you get what is available and enabled in wings_prefs
52%%%              plain ->  only vertex positions and normal
53we(We, Options, St) ->
54    wings_dl:fold(fun(Dl, undefined) -> we_1(Dl, We, Options, St);
55		     (_Dl, Res)      -> Res
56		  end, undefined).
57
58we_1(Dlo=#dlo{src_we=Orig=#we{id=Id}}, Curr=#we{id=Id}, Opt, St) ->
59    case Orig =:= Curr of
60	true  -> we_2(Dlo, Opt, St);
61	false -> we_2(wings_draw:changed_we(Dlo,#dlo{src_we=Curr}), Opt, St)
62    end;
63we_1(_, _, _, _) ->
64    undefined.
65
66we_2(Dlo0, Opt, St) ->
67    Smooth = proplists:get_value(smooth, Opt, false),
68    Attrib = proplists:get_value(attribs, Opt, undefined),
69    Dlo1 = setup_vmirror(proplists:get_value(vmirror, Opt, undefined), Dlo0),
70    Dlo2 = setup_subdiv(proplists:get_value(subdiv, Opt, 0), Dlo1),
71    Dlo = check_attrib(Attrib, Dlo2),
72    #dlo{vab=Vab} =
73	case Smooth of
74	    true  -> smooth(Dlo, St, Attrib);
75	    false -> work(Dlo, St, Attrib)
76	end,
77    Vab.
78
79setup_vmirror(undefined, Dlo) -> Dlo;
80setup_vmirror(_, Dlo=#dlo{src_we=#we{mirror=none}}) -> Dlo;
81setup_vmirror(_, #dlo{src_we=We}) ->
82    Mirrored = wings_we:freeze_mirror(We),
83    wings_draw:changed_we(#dlo{}, #dlo{src_we=Mirrored}).
84
85setup_subdiv(0, Dlo) -> Dlo;
86setup_subdiv(N, #dlo{src_we=We}) ->
87    SubDived = sub_divide(N, We),
88    wings_draw:changed_we(#dlo{}, #dlo{src_we=SubDived}).
89sub_divide(0, We) -> We;
90sub_divide(N, We) ->
91    sub_divide(N-1, wings_subdiv:smooth(We)).
92
93check_attrib(undefined, Dlo) -> Dlo;
94check_attrib(_, Dlo = #dlo{vab=none}) -> Dlo;
95check_attrib(uv, Dlo = #dlo{vab = #vab{face_uv={_,_}, face_vc=none}}) -> Dlo;
96check_attrib(color, Dlo = #dlo{vab = #vab{face_vc={_,_}, face_uv=none}}) -> Dlo;
97check_attrib(color_uv, Dlo = #dlo{vab = #vab{face_vc={_,_}, face_uv={_,_}}}) -> Dlo;
98check_attrib(_, D) ->
99    D#dlo{vab=none}. %% Force rebuild
100
101%%%
102%%% Help functions to activate and disable buffer pointers.
103%%%
104
105has_active_color(#vab{face_vc=Color}) ->
106    Color =/= none.
107
108%% enable_pointers(#vab{}, [ExtraPointer], RS) ->
109%%    ExtraPointer = face_normals | vertex_normals | colors | uvs | tangents
110%%  Enable the vertex buffer pointer, and optionally other pointers.
111
112enable_pointers(#vab{id=Vbo,face_vs={Stride,BinVs}}=Vab, Extra, RS0) ->
113    gl:bindBuffer(?GL_ARRAY_BUFFER, Vbo),
114    gl:vertexPointer(3, ?GL_FLOAT, Stride, BinVs),
115    CS = foldl(fun(What,Acc) ->
116                       case enable_pointer(What, Vab) of
117                           ok -> Acc;
118                           State -> [State|Acc]
119                       end
120               end, [?GL_VERTEX_ARRAY], Extra),
121    OldCs = maps:get({vbo,Vbo}, RS0, []),
122    %io:format("Enable: ~p => ~p~n", [CS, CS -- OldCs]),
123    [enable_state(State)  || State <- CS],
124    %io:format("Disable: ~p => ~p~n", [OldCs, OldCs -- CS]),
125    [disable_state(State) || State <- OldCs -- CS],
126    gl:bindBuffer(?GL_ARRAY_BUFFER, 0),
127    RS0#{{vbo,Vbo}=>CS}.
128
129%% disable_pointers(RS)
130%%  Disable the active pointers
131
132disable_pointers(#vab{id=Vbo}, RS0) ->
133    OldCs = maps:get({vbo, Vbo}, RS0, []),
134    [disable_state(What) || What <- OldCs],
135    gl:bindBuffer(?GL_ARRAY_BUFFER, 0),
136    RS0#{{vbo, Vbo}=>[]}.
137
138enable_pointer(face_normals, #vab{face_fn={Stride,Ns}}) ->
139    gl:normalPointer(?GL_FLOAT, Stride, Ns),
140    ?GL_NORMAL_ARRAY;
141enable_pointer(vertex_normals, #vab{id=MainVbo,face_sn={vbo,Vbo}}) ->
142    gl:bindBuffer(?GL_ARRAY_BUFFER, Vbo),
143    gl:normalPointer(?GL_FLOAT, 0, 0),
144    gl:bindBuffer(?GL_ARRAY_BUFFER, MainVbo),
145    ?GL_NORMAL_ARRAY;
146enable_pointer(vertex_normals, #vab{face_sn={Stride,Ns}}) ->
147    %% Only used by wings_cc.
148    gl:normalPointer(?GL_FLOAT, Stride, Ns),
149    ?GL_NORMAL_ARRAY;
150enable_pointer(colors, #vab{face_vc=FaceCol}) ->
151    case FaceCol of
152	none ->
153	    ok;
154	{Stride,Color} ->
155	    gl:colorPointer(3, ?GL_FLOAT, Stride, Color),
156	    ?GL_COLOR_ARRAY
157    end;
158enable_pointer(uvs, #vab{face_uv=FaceUV}) ->
159    case FaceUV of
160	none ->
161	    ok;
162	{Stride,UV} ->
163	    gl:texCoordPointer(2, ?GL_FLOAT, Stride, UV),
164	    ?GL_TEXTURE_COORD_ARRAY
165    end;
166enable_pointer(tangents, #vab{face_ts=FaceTs}) ->
167    case FaceTs of
168	none ->
169	    ok;
170	{Stride,Ts} ->
171	    gl:vertexAttribPointer(?TANGENT_ATTR, 4, ?GL_FLOAT,
172				   ?GL_FALSE, Stride, Ts),
173            {attrib, ?TANGENT_ATTR}
174    end.
175
176enable_state({attrib, Attr}) ->
177    gl:enableVertexAttribArray(Attr);
178enable_state(Attr) ->
179    gl:enableClientState(Attr).
180
181disable_state({attrib, Attr}) ->
182    gl:disableVertexAttribArray(Attr);
183disable_state(Attr) ->
184    gl:disableClientState(Attr).
185
186face_vertex_count(#dlo{vab=#vab{mat_map=[{_Mat,_Type,Start,Count}|_]}}) ->
187    Start+Count;
188face_vertex_count(#vab{mat_map=[{_Mat,_Type,Start,Count}|_]}) ->
189    Start+Count.
190
191%% Setup face_vs and face_fn and additional uv coords or vertex colors
192work(Dlo, St) ->
193    work(Dlo, St, undefined).
194
195work(#dlo{ns={_}}=D0, St, Attr) ->
196    D = wings_draw:update_normals(D0),
197    work(D, St, Attr);
198work(#dlo{vab=none,src_we=#we{fs=Ftab}}=D, St, Attr) ->
199    Prepared = prepare(gb_trees:to_list(Ftab), D, St, Attr),
200    flat_faces(Prepared, D);
201work(#dlo{vab=#vab{face_vs=none},src_we=#we{fs=Ftab}}=D, St, Attr) ->
202    Prepared = prepare(gb_trees:to_list(Ftab), D, St, Attr),
203    flat_faces(Prepared, D);
204work(#dlo{vab=#vab{face_fn=none}=Vab}=D, St, Attr) ->
205    %% Can this really happen? If it can, it happens infrequently,
206    %% so we don't have to handle it efficiently.
207    work(D#dlo{vab=Vab#vab{face_vs=none}}, St, Attr);
208work(D, _, _) -> D.
209
210%% Setup face_vs and face_sn and additional uv coords or vertex colors
211smooth(Dlo, St) ->
212    smooth(Dlo, St, undefined).
213
214smooth(#dlo{ns={_}}=D0, St, Attr) ->
215    D = wings_draw:update_normals(D0),
216    smooth(D, St, Attr);
217smooth(#dlo{vab=none}=D, St, Attr) ->
218    setup_smooth_normals(work(D, St, Attr));
219smooth(#dlo{vab=#vab{face_vs=none}}=D, St, Attr) ->
220    setup_smooth_normals(work(D, St, Attr));
221smooth(D=#dlo{vab=#vab{face_sn=none}}, _St, _) ->
222    setup_smooth_normals(D);
223smooth(D, _, _) -> D.
224
225%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
226
227-spec flat_faces(plan(), #dlo{}) -> #dlo{}.
228
229flat_faces({plain,MatFaces}, D) ->
230    plain_flat_faces(MatFaces, D, 0, <<>>, [], []);
231flat_faces({uv,MatFaces}, D) ->
232    uv_flat_faces(MatFaces, D, 0, <<>>, [], []);
233flat_faces({uv_tangent,MatFaces}, D) ->
234    Z = e3d_vec:zero(),
235    Array = array:new([{default, {Z,Z}}]),
236    tangent_flat_faces(MatFaces, D, 0, <<>>, [], [], {Array, []});
237flat_faces({color,MatFaces}, D) ->
238    col_flat_faces(MatFaces, D, 0, <<>>, [], []);
239flat_faces({color_uv,MatFaces}, D) ->
240    col_uv_faces(MatFaces, D, 0, <<>>, [], []);
241flat_faces({color_uv_tangent,MatFaces}, D) ->
242    Z = e3d_vec:zero(),
243    Array = array:new([{default, {Z,Z}}]),
244    col_tangent_faces(MatFaces, D, 0, <<>>, [], [], {Array, []}).
245
246plain_flat_faces([{Mat,Fs}|T], #dlo{ns=Ns}=D, Start0, Vs0, Fmap0, MatInfo0) ->
247    {Start,Vs,FaceMap} = flat_faces_1(Fs, Ns, Start0, Vs0, Fmap0),
248    MatInfo = [{Mat,?GL_TRIANGLES,Start0,Start-Start0}|MatInfo0],
249    plain_flat_faces(T, D, Start, Vs, FaceMap, MatInfo);
250plain_flat_faces([], D, _Start, Vs, FaceMap0, MatInfo) ->
251    FaceMap = array:from_orddict(sort(FaceMap0)),
252    Vab = create_vab([vertices,face_normals], Vs, FaceMap, MatInfo),
253    D#dlo{vab=Vab}.
254
255flat_faces_1([{Face,_}|Fs], Ns, Start, Vs, FaceMap) ->
256    case array:get(Face, Ns) of
257	[Normal|Pos =[_,_,_]] ->
258	    flat_faces_1(Fs, Ns, Start+3,
259			 add_tri(Vs, Normal, Pos),
260			 [{Face,{Start,3}}|FaceMap]);
261	[Normal|Pos] ->
262	    flat_faces_1(Fs, Ns, Start+6,
263			 add_quad(Vs, Normal, Pos),
264			 [{Face,{Start,6}}|FaceMap]);
265	{Normal,Faces,VsPos} ->
266	    NoVs  = length(Faces) * 3,
267	    VsBin = add_poly(Vs, Normal, Faces, list_to_tuple(VsPos)),
268	    flat_faces_1(Fs, Ns, NoVs+Start,
269			 VsBin, [{Face,{Start,NoVs}}|FaceMap])
270    end;
271flat_faces_1([], _, Start, Vs, FaceMap) ->
272    {Start,Vs,FaceMap}.
273
274uv_flat_faces([{Mat,Fs}|T], D, Start0, Vs0, Fmap0, MatInfo0) ->
275    {Start,Vs,FaceMap} = uv_flat_faces_1(Fs, D, Start0, Vs0, Fmap0),
276    MatInfo = [{Mat,?GL_TRIANGLES,Start0,Start-Start0}|MatInfo0],
277    uv_flat_faces(T, D, Start, Vs, FaceMap, MatInfo);
278uv_flat_faces([], D, _Start, Vs, FaceMap0, MatInfo) ->
279    FaceMap = array:from_orddict(sort(FaceMap0)),
280    Vab = create_vab([vertices,face_normals,uvs], Vs, FaceMap, MatInfo),
281    D#dlo{vab=Vab}.
282
283uv_flat_faces_1([{Face,Edge}|Fs], #dlo{ns=Ns,src_we=We}=D, Start, Vs, FaceMap) ->
284    UVs = wings_va:face_attr(uv, Face, Edge, We),
285    case array:get(Face, Ns) of
286	[Normal|Pos =[_,_,_]] ->
287	    uv_flat_faces_1(Fs, D, Start+3,
288			    add_tri(Vs, Normal, Pos, UVs),
289			    [{Face,{Start,3}}|FaceMap]);
290	[Normal|Pos] ->
291	    uv_flat_faces_1(Fs, D, Start+6,
292			    add_quad(Vs, Normal, Pos, UVs),
293			    [{Face,{Start,6}}|FaceMap]);
294	{Normal,Faces,VsPos} ->
295	    NoVs  = length(Faces) * 3,
296	    VsBin = add_poly(Vs, Normal, Faces,
297			     list_to_tuple(VsPos), list_to_tuple(UVs)),
298	    uv_flat_faces_1(Fs, D, NoVs+Start,
299			    VsBin, [{Face,{Start,NoVs}}|FaceMap])
300    end;
301uv_flat_faces_1([], _, Start, Vs, FaceMap) ->
302    {Start,Vs,FaceMap}.
303
304%% Also needs uv's
305tangent_flat_faces([{Mat,Fs}|T], D, Start0, Vs0, Fmap0, MatInfo0, Ts0) ->
306    {Start,Vs,FaceMap,Ts} = tangent_flat_faces_1(Fs, D, Start0, Vs0, Fmap0, Ts0),
307    MatInfo = [{Mat,?GL_TRIANGLES,Start0,Start-Start0}|MatInfo0],
308    tangent_flat_faces(T, D, Start, Vs, FaceMap, MatInfo, Ts);
309tangent_flat_faces([], D, _Start, Vs, FaceMap0, MatInfo, {VsTs0, RevF2V}) ->
310    FaceMap = array:from_orddict(sort(FaceMap0)),
311    VsTs = array:map(fun(_V, {T,BT}) ->
312			     {e3d_vec:norm(T),e3d_vec:norm(BT)}
313		     end, VsTs0),
314    Data = add_tangents(lists:reverse(RevF2V), VsTs, Vs),
315    What = [vertices,face_normals,uvs],
316    Vab = create_tangent_vab(What, Vs, Data, FaceMap, MatInfo),
317    D#dlo{vab=Vab}.
318
319tangent_flat_faces_1([{Face,Edge}|Fs], #dlo{ns=Ns,src_we=We}=D, Start, Vs, FaceMap, Ts0) ->
320    UVs = wings_va:face_attr(uv, Face, Edge, We),
321    case array:get(Face, Ns) of
322	[Normal|Pos =[_,_,_]] ->
323	    tangent_flat_faces_1(Fs, D, Start+3,
324				 add_tri(Vs, Normal, Pos, UVs),
325				 [{Face,{Start,3}}|FaceMap],
326				 add_ts(Pos, UVs, Normal,
327					wings_face:vertices_ccw(Face, We), Ts0)
328				);
329	[Normal|Pos] ->
330	    tangent_flat_faces_1(Fs, D, Start+6,
331				 add_quad(Vs, Normal, Pos, UVs),
332				 [{Face,{Start,6}}|FaceMap],
333				 add_ts(Pos, UVs, Normal,
334					wings_face:vertices_ccw(Face, We), Ts0));
335	Info = {Normal,Faces,VsPos} ->
336	    NoVs  = length(Faces) * 3,
337	    VsBin = add_poly(Vs, Normal, Faces,
338			     list_to_tuple(VsPos), list_to_tuple(UVs)),
339	    tangent_flat_faces_1(Fs, D, NoVs+Start,
340				 VsBin, [{Face,{Start,NoVs}}|FaceMap],
341				 add_ts(Info, UVs, Normal,
342					wings_face:vertices_ccw(Face, We), Ts0))
343    end;
344tangent_flat_faces_1([], _, Start, Vs, FaceMap, Ts) ->
345    {Start,Vs,FaceMap,Ts}.
346
347
348col_flat_faces([{Mat,Fs}|T], D, Start0, Vs0, Fmap0, MatInfo0) ->
349    {Start,Vs,FaceMap} = col_flat_faces_1(Fs, D, Start0, Vs0, Fmap0),
350    MatInfo = [{Mat,?GL_TRIANGLES,Start0,Start-Start0}|MatInfo0],
351    col_flat_faces(T, D, Start, Vs, FaceMap, MatInfo);
352col_flat_faces([], D, _Start, Vs, FaceMap0, MatInfo) ->
353    FaceMap = array:from_orddict(sort(FaceMap0)),
354    Vab = create_vab([vertices,face_normals,colors], Vs, FaceMap, MatInfo),
355    D#dlo{vab=Vab}.
356
357col_flat_faces_1([{Face,Edge}|T], #dlo{ns=Ns,src_we=We}=D, Start, Vs0, Fmap0) ->
358    Cols = wings_va:face_attr(color, Face, Edge, We),
359    case array:get(Face, Ns) of
360	[Normal|Pos =[_,_,_]] ->
361	    Vs = add_col_tri(Vs0, Normal, Pos, Cols),
362	    Fmap = [{Face,{Start,3}}|Fmap0],
363	    col_flat_faces_1(T, D, Start+3, Vs, Fmap);
364	[Normal|Pos] ->
365	    Vs = add_col_quad(Vs0, Normal, Pos, Cols),
366	    Fmap = [{Face,{Start,6}}|Fmap0],
367	    col_flat_faces_1(T, D, Start+6, Vs, Fmap);
368	{Normal,Faces,VsPos} ->
369	    NumVs  = length(Faces) * 3,
370	    Vs = add_col_poly(Vs0, Normal, Faces,
371			      list_to_tuple(VsPos), list_to_tuple(Cols)),
372	    Fmap = [{Face,{Start,NumVs}}|Fmap0],
373	    col_flat_faces_1(T, D, Start+NumVs, Vs, Fmap)
374    end;
375col_flat_faces_1([], _, Start, Vs, Fmap) ->
376    {Start,Vs,Fmap}.
377
378col_uv_faces([{Mat,Fs}|T], D, Start0, Vs0, Fmap0, MatInfo0) ->
379    {Start,Vs,FaceMap} = col_uv_faces_1(Fs, D, Start0, Vs0, Fmap0),
380    MatInfo = [{Mat,?GL_TRIANGLES,Start0,Start-Start0}|MatInfo0],
381    col_uv_faces(T, D, Start, Vs, FaceMap, MatInfo);
382col_uv_faces([], D, _Start, Vs, FaceMap0, MatInfo) ->
383    FaceMap = array:from_orddict(sort(FaceMap0)),
384    Vab = create_vab([vertices,face_normals,colors,uvs],
385		     Vs, FaceMap, MatInfo),
386    D#dlo{vab=Vab}.
387
388col_uv_faces_1([{Face,Edge}|Fs], #dlo{ns=Ns,src_we=We}=D, Start, Vs, FaceMap) ->
389    UVs = wings_va:face_attr([color|uv], Face, Edge, We),
390    case array:get(Face, Ns) of
391	[Normal|Pos =[_,_,_]] ->
392	    col_uv_faces_1(Fs, D, Start+3,
393			   add_col_uv_tri(Vs, Normal, Pos, UVs),
394			   [{Face,{Start,3}}|FaceMap]);
395	[Normal|Pos] ->
396	    col_uv_faces_1(Fs, D, Start+6,
397			   add_col_uv_quad(Vs, Normal, Pos, UVs),
398			   [{Face,{Start,6}}|FaceMap]);
399	{Normal,Faces,VsPos} ->
400	    NoVs  = length(Faces) * 3,
401	    VsBin = add_col_uv_poly(Vs, Normal, Faces,
402			     list_to_tuple(VsPos), list_to_tuple(UVs)),
403	    col_uv_faces_1(Fs, D, NoVs+Start,
404			   VsBin, [{Face,{Start,NoVs}}|FaceMap])
405    end;
406col_uv_faces_1([], _, Start, Vs, FaceMap) ->
407    {Start,Vs,FaceMap}.
408
409%% Also needs uv's
410col_tangent_faces([{Mat,Fs}|T], D, Start0, Vs0, Fmap0, MatInfo0, Ts0) ->
411    {Start,Vs,FaceMap,Ts} = col_tangent_faces_1(Fs, D, Start0, Vs0, Fmap0, Ts0),
412    MatInfo = [{Mat,?GL_TRIANGLES,Start0,Start-Start0}|MatInfo0],
413    col_tangent_faces(T, D, Start, Vs, FaceMap, MatInfo, Ts);
414col_tangent_faces([], D, _Start, Vs, FaceMap0, MatInfo, {VsTs0, RevF2V}) ->
415    FaceMap = array:from_orddict(sort(FaceMap0)),
416    VsTs = array:map(fun(_V, {T,BT}) ->
417			     {e3d_vec:norm(T),e3d_vec:norm(BT)}
418		     end, VsTs0),
419    Data = add_tangents(lists:reverse(RevF2V), VsTs, Vs),
420    What = [vertices,face_normals,colors,uvs],
421    Vab = create_tangent_vab(What, Vs, Data, FaceMap, MatInfo),
422    D#dlo{vab=Vab}.
423
424col_tangent_faces_1([{Face,Edge}|Fs], #dlo{ns=Ns,src_we=We}=D, Start, Vs, FaceMap, Ts0) ->
425    UVs = wings_va:face_attr([color|uv], Face, Edge, We),
426    case array:get(Face, Ns) of
427	[Normal|Pos =[_,_,_]] ->
428	    col_tangent_faces_1(Fs, D, Start+3,
429				add_col_uv_tri(Vs, Normal, Pos, UVs),
430				[{Face,{Start,3}}|FaceMap],
431				add_ts(Pos, [UV|| [_|UV] <- UVs], Normal,
432				       wings_face:vertices_ccw(Face, We), Ts0));
433	[Normal|Pos] ->
434	    col_tangent_faces_1(Fs, D, Start+6,
435				add_col_uv_quad(Vs, Normal, Pos, UVs),
436				[{Face,{Start,6}}|FaceMap],
437				add_ts(Pos, [UV|| [_|UV] <- UVs], Normal,
438				       wings_face:vertices_ccw(Face, We), Ts0));
439	Info = {Normal,Faces,VsPos} ->
440	    NoVs  = length(Faces) * 3,
441	    VsBin = add_col_uv_poly(Vs, Normal, Faces,
442				    list_to_tuple(VsPos), list_to_tuple(UVs)),
443	    col_tangent_faces_1(Fs, D, NoVs+Start,
444				VsBin, [{Face,{Start,NoVs}}|FaceMap],
445				add_ts(Info, [UV|| [_|UV] <- UVs], Normal,
446				       wings_face:vertices_ccw(Face, We), Ts0))
447    end;
448col_tangent_faces_1([], _, Start, Vs, FaceMap,Ts) ->
449    {Start,Vs,FaceMap,Ts}.
450
451
452setup_smooth_normals(D=#dlo{src_we=#we{}=We,ns=Ns0,mirror=MM,
453			    vab=#vab{face_map=Fmap0}=Vab}) ->
454    Ns1 = array:sparse_foldl(fun(F,[N|_], A) -> [{F,N}|A];
455				(F,{N,_,_}, A) -> [{F,N}|A]
456			     end, [], Ns0),
457    Ns = reverse(Ns1),
458    Flist = wings_we:normals(Ns, We, MM),
459    Ftab  = array:from_orddict(Flist),
460    Fs    = lists:keysort(2, array:sparse_to_orddict(Fmap0)),
461    SN = setup_smooth_normals(Fs, Ftab, Ns0, <<>>),
462    [Vbo] = gl:genBuffers(1),
463    gl:bindBuffer(?GL_ARRAY_BUFFER, Vbo),
464    gl:bufferData(?GL_ARRAY_BUFFER, byte_size(SN), SN, ?GL_STATIC_DRAW),
465    gl:bindBuffer(?GL_ARRAY_BUFFER, 0),
466    D#dlo{vab=Vab#vab{face_sn={vbo,Vbo}}}.
467
468setup_smooth_normals([{Face,{_,3}}|Fs], Ftab, Flat, SN0) ->
469    %% One triangle.
470    case array:get(Face, Ftab) of
471	[N1,N2,N3] ->
472	    %% Common case: the face is a triangle.
473	    SN = add3(SN0, N1, N2, N3),
474	    setup_smooth_normals(Fs, Ftab, Flat, SN);
475	_ ->
476	    %% Degenerate case: The original face had more than
477	    %% 3 vertices, so there should have been at least
478	    %% 2 triangles, but some triangles were degenerated
479	    %% and therefore discarded. Use the face normal
480	    %% for all vertices.
481	    {Fn,_,_} = array:get(Face, Flat),
482	    SN = dup3(3, SN0, Fn),
483	    setup_smooth_normals(Fs, Ftab, Flat, SN)
484    end;
485setup_smooth_normals([{Face,{_,6}}|Fs], Ftab, Flat, SN0) ->
486    %% Two triangles.
487    case array:get(Face, Ftab) of
488	[N1,N2,N3,N4] ->
489	    %% Common case: triangulated quad.
490	    SN = add4(SN0, N1, N2, N3, N4),
491	    setup_smooth_normals(Fs, Ftab, Flat, SN);
492	_ ->
493	    %% Degenerate case: The original face had more than
494	    %% 4 vertices, so there should have been at least
495	    %% 3 triangles, but some triangles were degenerated
496	    %% and therefore discarded. Use the face normal
497	    %% for all vertices.
498	    {Fn,_,_} = array:get(Face, Flat),
499	    SN = dup3(6, SN0, Fn),
500	    setup_smooth_normals(Fs, Ftab, Flat, SN)
501    end;
502setup_smooth_normals([{Face,{_,Count}}|Fs], Ftab, Flat, SN0) ->
503    VsInfo = list_to_tuple(array:get(Face, Ftab)),
504    {FNormal,TriFs,Pos} = array:get(Face, Flat),
505    SN = case size(VsInfo) =:= length(Pos) of
506	     true  ->
507		 setup_smooth_normals_1(TriFs,VsInfo,SN0);
508	     false ->
509		 %% Tesselated face set flat normals
510		 %%(instead of random as previously)
511		 dup3(Count,SN0,FNormal)
512	 end,
513    setup_smooth_normals(Fs,Ftab,Flat,SN);
514setup_smooth_normals([],_,_,SN) -> SN.
515
516setup_smooth_normals_1([{A,B,C}|Fs], VsInfo, SN0) ->
517    N1 = element(A, VsInfo),
518    N2 = element(B, VsInfo),
519    N3 = element(C, VsInfo),
520    SN = add3(SN0, N1, N2, N3),
521    setup_smooth_normals_1(Fs,VsInfo,SN);
522setup_smooth_normals_1([], _, SN) -> SN.
523
524%%
525%% Create binaries
526%%
527
528add_tri(Bin, {NX,NY,NZ},
529	[{X1,Y1,Z1},{X2,Y2,Z2},{X3,Y3,Z3}]) ->
530    <<Bin/binary,
531     X1:?F32,Y1:?F32,Z1:?F32,
532     NX:?F32,NY:?F32,NZ:?F32,
533     X2:?F32,Y2:?F32,Z2:?F32,
534     NX:?F32,NY:?F32,NZ:?F32,
535     X3:?F32,Y3:?F32,Z3:?F32,
536     NX:?F32,NY:?F32,NZ:?F32>>.
537
538add_tri(Bin, {NX,NY,NZ},
539	[{X1,Y1,Z1},{X2,Y2,Z2},{X3,Y3,Z3}],
540	[{U1,V1},{U2,V2},{U3,V3}]) ->
541    <<Bin/binary,
542     X1:?F32,Y1:?F32,Z1:?F32,
543     NX:?F32,NY:?F32,NZ:?F32,
544     U1:?F32,V1:?F32,
545     X2:?F32,Y2:?F32,Z2:?F32,
546     NX:?F32,NY:?F32,NZ:?F32,
547     U2:?F32,V2:?F32,
548     X3:?F32,Y3:?F32,Z3:?F32,
549     NX:?F32,NY:?F32,NZ:?F32,
550     U3:?F32,V3:?F32>>;
551add_tri(Bin,N, Pos, _UV) ->
552    Z = {0.0,0.0},
553    add_tri(Bin, N, Pos, [Z,Z,Z]).
554
555add_col_uv_tri(Bin, {NX,NY,NZ},
556	       [{X1,Y1,Z1},{X2,Y2,Z2},{X3,Y3,Z3}],
557	       [[{R1,G1,B1}|{U1,V1}],
558		[{R2,G2,B2}|{U2,V2}],
559		[{R3,G3,B3}|{U3,V3}]]) ->
560    <<Bin/binary,
561     X1:?F32,Y1:?F32,Z1:?F32,
562     NX:?F32,NY:?F32,NZ:?F32,
563     R1:?F32,G1:?F32,B1:?F32,
564     U1:?F32,V1:?F32,
565     X2:?F32,Y2:?F32,Z2:?F32,
566     NX:?F32,NY:?F32,NZ:?F32,
567     R2:?F32,G2:?F32,B2:?F32,
568     U2:?F32,V2:?F32,
569     X3:?F32,Y3:?F32,Z3:?F32,
570     NX:?F32,NY:?F32,NZ:?F32,
571     R3:?F32,G3:?F32,B3:?F32,
572     U3:?F32,V3:?F32>>;
573add_col_uv_tri(Bin, N, Pos, Attrs0) ->
574    Attrs = fix_color_uv(Attrs0),
575    add_col_uv_tri(Bin, N, Pos, Attrs).
576
577add_col_tri(Bin, {NX,NY,NZ},
578	    [{X1,Y1,Z1},{X2,Y2,Z2},{X3,Y3,Z3}],
579	    [{R1,G1,B1},{R2,G2,B2},{R3,G3,B3}]) ->
580    <<Bin/binary,
581     X1:?F32,Y1:?F32,Z1:?F32,
582     NX:?F32,NY:?F32,NZ:?F32,
583     R1:?F32,G1:?F32,B1:?F32,
584     X2:?F32,Y2:?F32,Z2:?F32,
585     NX:?F32,NY:?F32,NZ:?F32,
586     R2:?F32,G2:?F32,B2:?F32,
587     X3:?F32,Y3:?F32,Z3:?F32,
588     NX:?F32,NY:?F32,NZ:?F32,
589     R3:?F32,G3:?F32,B3:?F32>>;
590add_col_tri(Bin,N, Pos, Cols0) ->
591    Cols = [def_color(C) || C <- Cols0],
592    add_col_tri(Bin, N, Pos, Cols).
593
594add_quad(Bin, {NX,NY,NZ},
595	 [{X1,Y1,Z1},{X2,Y2,Z2},{X3,Y3,Z3},{X4,Y4,Z4}]) ->
596    <<Bin/binary,
597     X1:?F32,Y1:?F32,Z1:?F32,
598     NX:?F32,NY:?F32,NZ:?F32,
599     X2:?F32,Y2:?F32,Z2:?F32,
600     NX:?F32,NY:?F32,NZ:?F32,
601     X3:?F32,Y3:?F32,Z3:?F32,
602     NX:?F32,NY:?F32,NZ:?F32,
603     X3:?F32,Y3:?F32,Z3:?F32,
604     NX:?F32,NY:?F32,NZ:?F32,
605     X4:?F32,Y4:?F32,Z4:?F32,
606     NX:?F32,NY:?F32,NZ:?F32,
607     X1:?F32,Y1:?F32,Z1:?F32,
608     NX:?F32,NY:?F32,NZ:?F32>>.
609
610add_quad(Bin, {NX,NY,NZ},
611	 [{X1,Y1,Z1},{X2,Y2,Z2},{X3,Y3,Z3},{X4,Y4,Z4}],
612	 [{U1,V1},{U2,V2},{U3,V3},{U4,V4}]) ->
613    <<Bin/binary,
614     X1:?F32,Y1:?F32,Z1:?F32,
615     NX:?F32,NY:?F32,NZ:?F32,
616     U1:?F32,V1:?F32,
617     X2:?F32,Y2:?F32,Z2:?F32,
618     NX:?F32,NY:?F32,NZ:?F32,
619     U2:?F32,V2:?F32,
620     X3:?F32,Y3:?F32,Z3:?F32,
621     NX:?F32,NY:?F32,NZ:?F32,
622     U3:?F32,V3:?F32,
623     X3:?F32,Y3:?F32,Z3:?F32,
624     NX:?F32,NY:?F32,NZ:?F32,
625     U3:?F32,V3:?F32,
626     X4:?F32,Y4:?F32,Z4:?F32,
627     NX:?F32,NY:?F32,NZ:?F32,
628     U4:?F32,V4:?F32,
629     X1:?F32,Y1:?F32,Z1:?F32,
630     NX:?F32,NY:?F32,NZ:?F32,
631     U1:?F32,V1:?F32>>;
632add_quad(Bin, N, Pos, _) ->
633    Z = {0.0,0.0},
634    add_quad(Bin, N, Pos, [Z,Z,Z,Z]).
635
636add_col_quad(Bin, {NX,NY,NZ},
637	     [{X1,Y1,Z1},{X2,Y2,Z2},{X3,Y3,Z3},{X4,Y4,Z4}],
638	     [{R1,G1,B1},{R2,G2,B2},{R3,G3,B3},{R4,G4,B4}]) ->
639    <<Bin/binary,
640     X1:?F32,Y1:?F32,Z1:?F32,
641     NX:?F32,NY:?F32,NZ:?F32,
642     R1:?F32,G1:?F32,B1:?F32,
643     X2:?F32,Y2:?F32,Z2:?F32,
644     NX:?F32,NY:?F32,NZ:?F32,
645     R2:?F32,G2:?F32,B2:?F32,
646     X3:?F32,Y3:?F32,Z3:?F32,
647     NX:?F32,NY:?F32,NZ:?F32,
648     R3:?F32,G3:?F32,B3:?F32,
649     X3:?F32,Y3:?F32,Z3:?F32,
650     NX:?F32,NY:?F32,NZ:?F32,
651     R3:?F32,G3:?F32,B3:?F32,
652     X4:?F32,Y4:?F32,Z4:?F32,
653     NX:?F32,NY:?F32,NZ:?F32,
654     R4:?F32,G4:?F32,B4:?F32,
655     X1:?F32,Y1:?F32,Z1:?F32,
656     NX:?F32,NY:?F32,NZ:?F32,
657     R1:?F32,G1:?F32,B1:?F32>>;
658add_col_quad(Bin, N, Pos, Cols0) ->
659    Cols = [def_color(C) || C <- Cols0],
660    add_col_quad(Bin, N, Pos, Cols).
661
662add_col_uv_quad(Bin, {NX,NY,NZ},
663		[{X1,Y1,Z1},{X2,Y2,Z2},{X3,Y3,Z3},{X4,Y4,Z4}],
664		[[{R1,G1,B1}|{U1,V1}],
665		 [{R2,G2,B2}|{U2,V2}],
666		 [{R3,G3,B3}|{U3,V3}],
667		 [{R4,G4,B4}|{U4,V4}]]) ->
668    <<Bin/binary,
669     X1:?F32,Y1:?F32,Z1:?F32,
670     NX:?F32,NY:?F32,NZ:?F32,
671     R1:?F32,G1:?F32,B1:?F32,
672     U1:?F32,V1:?F32,
673     X2:?F32,Y2:?F32,Z2:?F32,
674     NX:?F32,NY:?F32,NZ:?F32,
675     R2:?F32,G2:?F32,B2:?F32,
676     U2:?F32,V2:?F32,
677     X3:?F32,Y3:?F32,Z3:?F32,
678     NX:?F32,NY:?F32,NZ:?F32,
679     R3:?F32,G3:?F32,B3:?F32,
680     U3:?F32,V3:?F32,
681     X3:?F32,Y3:?F32,Z3:?F32,
682     NX:?F32,NY:?F32,NZ:?F32,
683     R3:?F32,G3:?F32,B3:?F32,
684     U3:?F32,V3:?F32,
685     X4:?F32,Y4:?F32,Z4:?F32,
686     NX:?F32,NY:?F32,NZ:?F32,
687     R4:?F32,G4:?F32,B4:?F32,
688     U4:?F32,V4:?F32,
689     X1:?F32,Y1:?F32,Z1:?F32,
690     NX:?F32,NY:?F32,NZ:?F32,
691     R1:?F32,G1:?F32,B1:?F32,
692     U1:?F32,V1:?F32>>;
693add_col_uv_quad(Bin, N, Pos, Attrs0) ->
694    Attrs = fix_color_uv(Attrs0),
695    add_col_uv_quad(Bin, N, Pos, Attrs).
696
697add_poly(Vs0, Normal, [{A,B,C}|Fs], Vtab) ->
698    PA = element(A, Vtab),
699    PB = element(B, Vtab),
700    PC = element(C, Vtab),
701    Vs = add_tri(Vs0, Normal, [PA,PB,PC]),
702    add_poly(Vs, Normal, Fs, Vtab);
703add_poly(Vs, _, _, _) -> Vs.
704
705add_poly(Vs0, Normal, [{A,B,C}|Fs], Vtab, UVtab) ->
706    PA = element(A, Vtab),
707    PB = element(B, Vtab),
708    PC = element(C, Vtab),
709    %% A tesselated face may have more Vs than UVs
710    UVa = uv_element(A, Vtab, UVtab),
711    UVb = uv_element(B, Vtab, UVtab),
712    UVc = uv_element(C, Vtab, UVtab),
713    Vs = add_tri(Vs0, Normal, [PA,PB,PC], [UVa,UVb,UVc]),
714    add_poly(Vs, Normal, Fs, Vtab, UVtab);
715add_poly(Vs, _, _, _, _) -> Vs.
716
717add_col_poly(Vs0, Normal, [{A,B,C}|Fs], Vtab, ColTab) ->
718    PA = element(A, Vtab),
719    PB = element(B, Vtab),
720    PC = element(C, Vtab),
721    %% A tesselated face may have more vertices than colors
722    ColA = col_element(A, Vtab, ColTab),
723    ColB = col_element(B, Vtab, ColTab),
724    ColC = col_element(C, Vtab, ColTab),
725    Vs = add_col_tri(Vs0, Normal, [PA,PB,PC], [ColA,ColB,ColC]),
726    add_col_poly(Vs, Normal, Fs, Vtab, ColTab);
727add_col_poly(Vs, _, _, _, _) -> Vs.
728
729add_col_uv_poly(Vs0, Normal, [{A,B,C}|Fs], Vtab, AttrTab) ->
730    PA = element(A, Vtab),
731    PB = element(B, Vtab),
732    PC = element(C, Vtab),
733    %% A tesselated face may have more vertices than vertex attributes
734    AttrA = attr_element(A, AttrTab),
735    AttrB = attr_element(B, AttrTab),
736    AttrC = attr_element(C, AttrTab),
737    Vs = add_col_uv_tri(Vs0, Normal, [PA,PB,PC], [AttrA,AttrB,AttrC]),
738    add_col_uv_poly(Vs, Normal, Fs, Vtab, AttrTab);
739add_col_uv_poly(Vs, _, _, _, _) -> Vs.
740
741uv_element(A, _Vtab, Tab) when A =< tuple_size(Tab) ->
742    element(A, Tab);
743uv_element(A, Vtab, Tab) ->
744    find_element(tuple_size(Tab), element(A, Vtab), Vtab, Tab, {0.0,0.0}).
745
746col_element(A, _, Tab) when A =< tuple_size(Tab) ->
747    element(A, Tab);
748col_element(A, Vtab, Tab) ->
749    find_element(tuple_size(Tab), element(A, Vtab), Vtab, Tab, {1.0,1.0,1.0}).
750
751find_element(0, _, _, _, Def) ->
752    Def;
753find_element(I, P, Vtab, UVTab, Def) ->
754    case P =:= element(I, Vtab) of
755	true -> element(I, UVTab);
756	false -> find_element(I-1, P, Vtab, UVTab, Def)
757    end.
758
759attr_element(A, Tab) when A =< tuple_size(Tab) ->
760    element(A, Tab);
761attr_element(_, _) ->
762    [none|none].
763
764fix_color_uv(Attrs) ->
765    case good_uvs(Attrs) of
766	false ->
767	    %% Bad UVs, possibly bad vertex colors too. Fix both.
768	    Zuv = {0.0,0.0},
769	    [[def_color(C)|Zuv] || [C|_] <- Attrs];
770	true ->
771	    %% Good UVs, bad vertex colors.
772	    [[def_color(C)|UV] || [C|UV] <- Attrs]
773    end.
774
775good_uvs([[_|{_,_}]|T]) -> good_uvs(T);
776good_uvs([_|_]) -> false;
777good_uvs([]) -> true.
778
779def_color({_,_,_}=C) -> C;
780def_color(_) -> {1.0,1.0,1.0}.
781
782add3(Bin, {X1,Y1,Z1}, {X2,Y2,Z2}, {X3,Y3,Z3}) ->
783    <<Bin/binary,
784     X1:?F32,Y1:?F32,Z1:?F32,
785     X2:?F32,Y2:?F32,Z2:?F32,
786     X3:?F32,Y3:?F32,Z3:?F32>>.
787
788add4(Bin, {X1,Y1,Z1}, {X2,Y2,Z2}, {X3,Y3,Z3}, {X4,Y4,Z4}) ->
789    <<Bin/binary,
790     X1:?F32,Y1:?F32,Z1:?F32,
791     X2:?F32,Y2:?F32,Z2:?F32,
792     X3:?F32,Y3:?F32,Z3:?F32,
793     X3:?F32,Y3:?F32,Z3:?F32,
794     X4:?F32,Y4:?F32,Z4:?F32,
795     X1:?F32,Y1:?F32,Z1:?F32>>.
796
797dup3(3, Bin, {NX,NY,NZ}) ->
798    <<Bin/binary,
799     NX:?F32,NY:?F32,NZ:?F32,
800     NX:?F32,NY:?F32,NZ:?F32,
801     NX:?F32,NY:?F32,NZ:?F32 >>;
802dup3(6, Bin, {NX,NY,NZ}) ->
803    <<Bin/binary,
804     NX:?F32,NY:?F32,NZ:?F32,
805     NX:?F32,NY:?F32,NZ:?F32,
806     NX:?F32,NY:?F32,NZ:?F32,
807     NX:?F32,NY:?F32,NZ:?F32,
808     NX:?F32,NY:?F32,NZ:?F32,
809     NX:?F32,NY:?F32,NZ:?F32 >>;
810dup3(0, Bin0, _N) ->   %% Zero area triangulate face can become 0 vertex face
811    Bin0;
812dup3(I, Bin0, N={NX,NY,NZ}) when I > 0 ->
813    Bin = <<Bin0/binary,
814	   NX:?F32,NY:?F32,NZ:?F32,
815	   NX:?F32,NY:?F32,NZ:?F32,
816	   NX:?F32,NY:?F32,NZ:?F32 >>,
817    dup3(I-3, Bin, N).
818
819add_ts([P1,P2,P3], [{U1,V1},{U2,V2},{U3,V3}], N, Vs, {Ts,F2V}) ->
820    {X1,Y1,Z1} = e3d_vec:sub(P2, P1),
821    {X2,Y2,Z2} = e3d_vec:sub(P3, P1),
822    S1 = U2-U1,
823    S2 = U3-U1,
824    T1 = V2-V1,
825    T2 = V3-V1,
826    try
827	F = 1.0 / (S1*T2 - S2*T1),
828	Tangent = {F*(T2*X1-T1*X2), F*(T2*Y1-T1*Y2), F*(T2*Z1-T1*Z2)},
829	BiTangent = {F*(S1*X2-S2*X1), F*(S1*Y2-S2*Y1), F*(S1*Z2-S2*Z1)},
830	H = case e3d_vec:dot(e3d_vec:cross(N, Tangent), BiTangent) < 0.0 of
831		true  -> 1;
832		false -> -1
833	    end,
834	{add_tangent(Vs, Tangent, BiTangent, Ts), [{N,H,Vs}|F2V]}
835    catch _:badarith ->
836	    {Ts, [{N,0,Vs}|F2V]}
837    end;
838add_ts([P1,P2,P3,P4], [UV1,UV2,UV3,UV4], N, [V1,V2,V3,V4], Ts) ->  % Quads
839    add_ts([P3,P4,P1],[UV3,UV4,UV1], N, [V3,V4,V1],
840	   add_ts([P1,P2,P3],[UV1,UV2,UV3], N, [V1,V2,V3], Ts));
841add_ts({_N,Fs,VsPos}, UVs, N, Vs, Ts) -> %% Polys
842    add_ts2(Fs, list_to_tuple(VsPos), list_to_tuple(UVs), N, list_to_tuple(Vs), Ts);
843add_ts([_,_,_], _, N, Vs, {Ts,F2V}) -> %% Bad UVs ignore
844    {Ts, [{N,1,Vs}|F2V]}.
845
846add_ts2([{V1,V2,V3}|Fs], VsPos, UVs, N, Vs, Ts0) ->
847    Ts = add_ts([element(V1,VsPos),element(V2,VsPos), element(V3,VsPos)],
848		[uv_element(V1, VsPos, UVs),uv_element(V2, VsPos, UVs),uv_element(V3, VsPos, UVs)],
849		N,
850		[id_element(V1, VsPos, Vs), id_element(V2, VsPos, Vs), id_element(V3, VsPos, Vs)],
851		Ts0),
852    add_ts2(Fs, VsPos, UVs, N, Vs, Ts);
853add_ts2([], _, _, _, _, Ts) -> Ts.
854
855id_element(A, _Vtab, Ids) when A =< tuple_size(Ids) -> element(A, Ids);
856id_element(A, Vtab, Ids) ->
857    find_element(tuple_size(Ids), element(A, Vtab), Vtab, Ids, element(1, Ids)).
858
859add_tangent([V|Vs], Tangent, BiTangent, Ts) ->
860    {T0, B0} = array:get(V,Ts),
861    add_tangent(Vs, Tangent, BiTangent,
862		array:set(V, {e3d_vec:add(T0, Tangent), e3d_vec:add(B0, BiTangent)}, Ts));
863add_tangent([], _, _, Ts) -> Ts.
864
865add_tangents([{N, H, Face}|Fs], Ts, Bin0) ->
866    Bin = add_tangents1(Face, Ts, H, N, undefined, Bin0),
867    add_tangents(Fs, Ts, Bin);
868add_tangents([], _, Bin) -> Bin.
869
870add_tangents1([V|Vs], Ts, H0, N, Prev, Bin0) ->
871    case array:get(V, Ts) of
872	{{0.0, 0.0, 0.0}, BiT} ->
873	    {Tan = {X,Y,Z}, H} = get_tangent(Prev, BiT, H0, N),
874	    Bin = <<Bin0/binary, X:?F32,Y:?F32,Z:?F32, H:?F32>>,
875	    add_tangents1(Vs, Ts, H, N, Tan, Bin);
876	{Tan = {X,Y,Z}, _} when H0 /= 0 ->
877	    Bin = <<Bin0/binary, X:?F32,Y:?F32,Z:?F32, H0:?F32>>,
878	    add_tangents1(Vs, Ts, H0, N, Tan, Bin);
879	{Tan = {X,Y,Z}, BiT} ->
880	    H = case e3d_vec:dot(e3d_vec:cross(N, Tan), BiT) < 0.0 of
881		    true  -> 1;
882		    false -> -1
883		end,
884	    Bin = <<Bin0/binary, X:?F32,Y:?F32,Z:?F32, H:?F32>>,
885	    add_tangents1(Vs, Ts, H, N, Tan, Bin)
886    end;
887add_tangents1([], _, _, _, _, Bin) -> Bin.
888
889get_tangent(undefined, {0.0,0.0,0.0}, H0, N) ->
890    H = if H0 =:= 0 -> -1; true -> H0 end,
891    {cross_axis(N), H};
892get_tangent(undefined, BiT, 0, N) ->
893    T = e3d_vec:cross(BiT, N),
894    H = case e3d_vec:dot(e3d_vec:cross(N, T), BiT) < 0.0 of
895	    true  -> 1;
896	    false -> -1
897	end,
898    {T, H};
899get_tangent(undefined, BiT, H, N) ->
900    {e3d_vec:cross(BiT, N), H};
901get_tangent(Prev, _, H, _) ->
902    {Prev, H}.
903
904cross_axis(N = {NX,NY,NZ}) ->
905    try
906	V2 = case abs(NX) > abs(NY) of
907		 true ->
908		     ILen = 1.0 / math:sqrt(NX*NX+NZ*NZ),
909		     {-NZ*ILen, 0.0, NX * ILen};
910		 false ->
911		     ILen = 1.0 / math:sqrt(NY*NY+NZ*NZ),
912		     {0.0, NZ*ILen, -NY * ILen}
913	     end,
914	e3d_vec:cross(N, V2)
915    catch _:badarith ->
916	    {1.0, 0.0,0.0}
917    end.
918
919%%%
920%%% Collect information about faces.
921%%%
922
923-spec prepare([{_,_}], #dlo{}|#we{}, #st{}) -> plan().
924
925prepare(Ftab, Dlo, St) ->
926    prepare(Ftab, Dlo, St, undefined).
927
928prepare(Ftab, #dlo{src_we=We}, St, Attr) ->
929    prepare(Ftab, We, St, Attr);
930prepare(Ftab0, #we{}=We, St, Attr) ->
931    Ftab = wings_we:visible(Ftab0, We),
932    prepare_1(Ftab, We, St, Attr).
933
934prepare_1(Ftab, We, St, Attr) ->
935    MatFaces = mat_faces(Ftab, We),
936    case wings_va:any_attributes(We) of
937	false when Attr =:= undefined ->
938	    %% Since there are no vertex attributes,
939	    %% we don't need to look at the materials
940	    %% to figure out what to do.
941	    {plain,MatFaces};
942	false  ->
943	    {Attr, MatFaces};
944	true ->
945	    %% There are UV coordinates and/or vertex colors,
946	    %% so we will have to look at the materials to
947	    %% figure out what we'll need.
948	    Attrs = wings_material:needed_attributes(We, St),
949	    {prepare_2(Attr, Attrs),MatFaces}
950    end.
951
952prepare_2(Attr, _)
953  when Attr == plain; Attr == color;
954       Attr == uv; Attr == color_uv ->
955    Attr;
956prepare_2(_, []) ->
957    plain;
958prepare_2(_, Attrs) ->
959    prepare_3(Attrs, plain).
960
961prepare_3([color|Rest], Prev) ->
962    case wings_pref:get_value(show_colors) of
963	true -> prepare_3(Rest, color);
964	false -> prepare_3(Rest, Prev)
965    end;
966prepare_3([uv, tangent], Prev) ->
967    case wings_pref:get_value(show_normal_maps) of
968	true -> prepare_4(uv_tangent, Prev);
969	false -> prepare_3([uv], Prev)
970    end;
971prepare_3([uv], Prev) ->
972    case wings_pref:get_value(show_textures) of
973	true -> prepare_4(uv, Prev);
974	false -> Prev
975    end;
976prepare_3([], Attr) -> Attr.
977
978prepare_4(Attr, plain) -> Attr;
979prepare_4(uv, color) -> color_uv;
980prepare_4(uv_tangent, color) -> color_uv_tangent.
981
982
983mat_faces(Ftab, #we{id=Id}=We) when ?IS_AREA_LIGHT(We) ->
984    [{{'_area_light_',Id},Ftab}];
985mat_faces(Ftab, We) ->
986    case wings_pref:get_value(show_materials) of
987	false ->
988	    [{default,Ftab}];
989	true ->
990	    wings_facemat:mat_faces(Ftab, We)
991    end.
992
993%% create_tangent_vab(What, VsData, AllData, FaceMap, MatInfo)
994%%  Create a #vab{} record with tangent data.
995
996create_tangent_vab(What, VsData, AllData, FaceMap, MatInfo) ->
997    Vab = create_vab(What, AllData, FaceMap, MatInfo),
998    VsSize = byte_size(VsData),
999    Vab#vab{data=VsData,face_ts={16,VsSize}}.
1000
1001%%%
1002%%% Create a #vab{} record.
1003%%%
1004
1005-type vab_item_tag() ::
1006	'vertices' |
1007	'face_normals' |
1008	'colors' |
1009	'uvs'.
1010
1011-spec create_vab([vab_item_tag()], binary(), any(), any()) -> #vab{}.
1012
1013create_vab(What, <<>>, FaceMap, MatInfo) ->
1014    [Vbo] = gl:genBuffers(1),
1015    gl:bindBuffer(?GL_ARRAY_BUFFER, Vbo),
1016    gl:bufferData(?GL_ARRAY_BUFFER, 0, <<>>, ?GL_STATIC_DRAW),
1017    gl:bindBuffer(?GL_ARRAY_BUFFER, 0),
1018    Empty = <<>>,
1019    Vab = #vab{id=Vbo,data=Empty,face_map=FaceMap,mat_map=MatInfo},
1020    foldl(fun(E, Vab0) ->
1021		  set_vab_item(E, {0,0}, Vab0)
1022	  end, Vab, What);
1023create_vab(What, Data, FaceMap, MatInfo) ->
1024    Stride = lists:foldl(fun(Item, Sum) ->
1025				 Sum + width(Item)
1026			 end, 0, What),
1027    [Vbo] = gl:genBuffers(1),
1028    gl:bindBuffer(?GL_ARRAY_BUFFER, Vbo),
1029    gl:bufferData(?GL_ARRAY_BUFFER, byte_size(Data), Data, ?GL_STATIC_DRAW),
1030    gl:bindBuffer(?GL_ARRAY_BUFFER, 0),
1031    Vab = #vab{id=Vbo,data=Data,face_map=FaceMap,mat_map=MatInfo},
1032    create_vab_1(What, 0, Stride, Data, Vab).
1033
1034create_vab_1([H|T], Pos, Stride, Data0, Vab0) ->
1035    Item = {Stride,Pos},
1036    Vab = set_vab_item(H, Item, Vab0),
1037    create_vab_1(T, Pos+width(H), Stride, Data0, Vab);
1038create_vab_1([], _, _, _, Vab) -> Vab.
1039
1040set_vab_item(vertices, Item, Vab) ->
1041    Vab#vab{face_vs=Item};
1042set_vab_item(face_normals, Item, Vab) ->
1043    Vab#vab{face_fn=Item};
1044set_vab_item(colors, Item, Vab) ->
1045    Vab#vab{face_vc=Item};
1046set_vab_item(uvs, Item, Vab) ->
1047    Vab#vab{face_uv=Item}.
1048
1049width(vertices) -> 3*4;
1050width(face_normals) -> 3*4;
1051width(vertex_normals) -> 3*4;
1052width(uvs) -> 2*4;
1053width(colors) -> 3*4.
1054