Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 336 lines (293 sloc) 10.071 kb
a552d9c4 » giniu
2006-08-18 initial import
1 %%
2 %% wings_export.erl --
3 %%
4 %% This module handles export to other file formats.
5 %%
235be9fe » bjorng
2009-05-01 Use an array instead of a gb_tree for the edge table.
6 %% Copyright (c) 2004-2009 Bjorn Gustavsson
a552d9c4 » giniu
2006-08-18 initial import
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 %%
60bb2e6a » bjorng
2008-01-01 Properties.
11 %% $Id$
a552d9c4 » giniu
2006-08-18 initial import
12 %%
13
14 -module(wings_export).
15 -export([export/4,save_images/3,make_mesh/2]).
16
17 -include("wings.hrl").
18 -include("e3d.hrl").
19 -include("e3d_image.hrl").
af2d517f » bjorng
2009-07-25 Wawefront export: Prevent crash in smoothing group calculations
20 -import(lists, [foldl/3,keydelete/3,reverse/1,last/1]).
21 -import(erlang, [min/2,max/2]).
a552d9c4 » giniu
2006-08-18 initial import
22
23 export(Exporter, Name, Ps, #st{shapes=Shs}=St0) ->
24 St = wings_view:freeze_mirror(St0),
25 Objs = foldl(fun(W, A) ->
26 export_1(W, Ps, A)
27 end, [], gb_trees:values(Shs)),
28 wings_pb:start(?__(1,"exporting")),
29 wings_pb:update(0.01,?__(2,"preparing")),
30 Creator = "Wings 3D " ++ ?WINGS_VERSION,
31
32 Mat0 = wings_material:used_materials(St),
89c812cf » bjorng
2009-08-13 Handle the new type of holes in export and import
33 Mat = mat_images(Mat0),
a552d9c4 » giniu
2006-08-18 initial import
34 Contents = #e3d_file{objs=Objs,mat=Mat,creator=Creator},
35 wings_pb:update(1.0),
36 try Exporter(Name, Contents) of
37 ok -> ok;
38 {error,Atom} when is_atom(Atom) ->
39 wings_u:error(file:format_error(Atom));
40 {error,Reason} ->
41 wings_u:error(Reason)
42 catch
43 error:Reason ->
44 Msg = ?__(4,"Exporter crashed"),
45 wings_u:error(Msg++": ~P\n\n~P\n",
46 [Reason,20,erlang:get_stacktrace(),20])
47 after
48 wings_pb:done()
49 end.
50
51 save_images(#e3d_file{mat=Mat0}=E3DFile, Dir, Filetype) ->
52 Mat = save_images_1(Mat0, Dir, Filetype, []),
53 E3DFile#e3d_file{mat=Mat}.
54
55 %%%
56 %%% Local functions.
57 %%%
58
59 export_1(#we{perm=Perm}, _, Acc) when ?IS_NOT_VISIBLE(Perm) -> Acc;
60 export_1(#we{name=Name}=We, Ps, Acc) when not ?IS_ANY_LIGHT(We) ->
61 Mesh = make_mesh(We, Ps),
62 [#e3d_object{name=Name,obj=Mesh}|Acc];
63 export_1(_, _, Acc) -> Acc.
64
167b42bf » bjorng
2009-07-24 Get rid of redundant code to freeze mirror
65 make_mesh(We0, Ps) ->
a552d9c4 » giniu
2006-08-18 initial import
66 SubDivs = proplists:get_value(subdivisions, Ps, 0),
67 Tess = proplists:get_value(tesselation, Ps, none),
68 We1 = sub_divide(SubDivs, We0),
89c812cf » bjorng
2009-08-13 Handle the new type of holes in export and import
69 We2 = wings_we:show_faces(We1),
70 We3 = tesselate(Tess, We2),
71 We = wings_we:renumber(We3, 0),
72 #we{vp=Vs0,es=Etab,he=He0,holes=Holes0} = We,
73c6320b » bjorng
2009-05-20 Use arrays instead of gb_trees for the vertex tables.
73 Vs = array:sparse_to_list(Vs0),
a552d9c4 » giniu
2006-08-18 initial import
74 {ColTab0,UvTab0} = make_tables(Ps, We),
75 ColTab1 = gb_trees:from_orddict(ColTab0),
76 UvTab1 = gb_trees:from_orddict(UvTab0),
af2d517f » bjorng
2009-07-25 Wawefront export: Prevent crash in smoothing group calculations
77 Sgs = case proplists:get_bool(include_normals, Ps) of
78 false -> gb_trees:empty();
79 true -> smooth_groups(We)
80 end,
89c812cf » bjorng
2009-08-13 Handle the new type of holes in export and import
81
82 %% Remove hole faces.
83 FaceMat0 = wings_facemat:all(We),
84 FaceMat1 = sofs:relation(FaceMat0, [{face,material}]),
85 Holes = sofs:set(Holes0, [face]),
86 FaceMat2 = sofs:drestriction(FaceMat1, Holes),
87 FaceMat = sofs:to_external(FaceMat2),
88
89 Fs0 = foldl(fun({Face,Mat}, A) ->
a552d9c4 » giniu
2006-08-18 initial import
90 case make_face(Face, Mat, ColTab1, UvTab1, We) of
91 #e3d_face{vs=[_,_]} -> A;
af2d517f » bjorng
2009-07-25 Wawefront export: Prevent crash in smoothing group calculations
92 E3DFace ->
93 case gb_trees:lookup(Face, Sgs) of
94 {value,Sg} ->
95 [E3DFace#e3d_face{sg=Sg}|A];
96 none ->
97 [E3DFace|A]
98 end
a552d9c4 » giniu
2006-08-18 initial import
99 end
89c812cf » bjorng
2009-08-13 Handle the new type of holes in export and import
100 end, [], FaceMat),
a552d9c4 » giniu
2006-08-18 initial import
101 Fs = reverse(Fs0),
102 He = case proplists:get_value(include_hard_edges, Ps, true) of
103 false -> [];
104 true -> hard_edges(gb_sets:to_list(He0), Etab, [])
105 end,
106 Matrix = e3d_mat:identity(),
107 ColTab = strip_numbers(ColTab0),
108 UvTab = strip_numbers(UvTab0),
109 Mesh = #e3d_mesh{type=polygon,fs=Fs,vs=Vs,tx=UvTab,he=He,
110 vc=ColTab,matrix=Matrix},
111 e3d_mesh:renumber(Mesh).
112
113 sub_divide(0, We) -> We;
114 sub_divide(N, We0) ->
115 We = wings_subdiv:smooth(We0),
116 sub_divide(N-1, We).
117
118 tesselate(none, We) -> We;
89c812cf » bjorng
2009-08-13 Handle the new type of holes in export and import
119 tesselate(triangulate, We) ->
120 Fs = wings_we:visible(We),
121 wings_tesselation:triangulate(Fs, We);
122 tesselate(quadrangulate, We) ->
123 Fs = wings_we:visible(We),
124 wings_tesselation:quadrangulate(Fs, We).
a552d9c4 » giniu
2006-08-18 initial import
125
b38588eb » bjorng
2009-07-20 Eliminate the object mode (vertex color/material)
126 make_face(Face, Mat, ColTab, UvTab, We) ->
127 E3dFace0 = make_plain_face(Face, Mat, We),
128 E3dFace = add_uvs(Face, E3dFace0, UvTab, We),
129 add_colors(Face, E3dFace, ColTab, We).
130
131 make_plain_face(Face, Mat, We) ->
132 Vs = wings_face:vertices_ccw(Face, We),
133 #e3d_face{vs=Vs,mat=make_face_mat(Mat)}.
134
135 add_uvs(Face, #e3d_face{vs=Vs}=E3dFace, UvTab, We) ->
a552d9c4 » giniu
2006-08-18 initial import
136 case gb_trees:is_empty(UvTab) of
b38588eb » bjorng
2009-07-20 Eliminate the object mode (vertex color/material)
137 true -> E3dFace;
138 false ->
139 UVs0 = wings_va:face_attr(uv, Face, We),
140 UVs1 = [gb_trees:get(UV, UvTab) || {_,_}=UV <- UVs0],
141 UVs = if
142 length(Vs) =:= length(UVs1) -> UVs1;
143 true -> []
144 end,
145 E3dFace#e3d_face{tx=UVs}
146 end.
147
148 add_colors(Face, E3dFace, ColTab, We) ->
a552d9c4 » giniu
2006-08-18 initial import
149 case gb_trees:is_empty(ColTab) of
b38588eb » bjorng
2009-07-20 Eliminate the object mode (vertex color/material)
150 true -> E3dFace;
151 false ->
152 Cols0 = wings_va:face_attr(color, Face, We),
153 Cols = [gb_trees:get(def_color(C), ColTab) || C <- Cols0],
154 E3dFace#e3d_face{vc=Cols}
a552d9c4 » giniu
2006-08-18 initial import
155 end.
156
b38588eb » bjorng
2009-07-20 Eliminate the object mode (vertex color/material)
157 def_color({_,_,_}=C) -> C;
158 def_color(_) -> {1.0,1.0,1.0}.
a552d9c4 » giniu
2006-08-18 initial import
159
b38588eb » bjorng
2009-07-20 Eliminate the object mode (vertex color/material)
160 make_tables(Ps, We) ->
a552d9c4 » giniu
2006-08-18 initial import
161 {case proplists:get_value(include_colors, Ps, true) of
213ab095 » bjorng
2009-06-21 Add wings_va:all/2 to retrieve all colors or UV coordinates
162 false ->
163 [];
164 true ->
b38588eb » bjorng
2009-07-20 Eliminate the object mode (vertex color/material)
165 ColorTable0 = wings_va:all(color, We),
166 case ColorTable0 of
167 [] -> [];
168 [_|_] ->
169 ColorTable = ordsets:add_element(wings_color:white(),
170 ColorTable0),
171 number(ColorTable)
172 end
a552d9c4 » giniu
2006-08-18 initial import
173 end,
174 case proplists:get_value(include_uvs, Ps, true) of
175 false -> [];
213ab095 » bjorng
2009-06-21 Add wings_va:all/2 to retrieve all colors or UV coordinates
176 true -> number(wings_va:all(uv, We))
a552d9c4 » giniu
2006-08-18 initial import
177 end}.
178
179 number(L) ->
180 number(L, 0, []).
b38588eb » bjorng
2009-07-20 Eliminate the object mode (vertex color/material)
181
a552d9c4 » giniu
2006-08-18 initial import
182 number([H|T], I, Acc) ->
183 number(T, I+1, [{H,I}|Acc]);
184 number([], _, Acc) -> reverse(Acc).
185
186 strip_numbers(L) ->
187 strip_numbers(L, []).
188 strip_numbers([{H,_}|T], Acc) ->
189 strip_numbers(T, [H|Acc]);
190 strip_numbers([], Acc) -> reverse(Acc).
191
192 make_face_mat([_|_]=Mat) -> Mat;
193 make_face_mat(Mat) -> [Mat].
194
195 hard_edges([E|Es], Etab, Acc) ->
235be9fe » bjorng
2009-05-01 Use an array instead of a gb_tree for the edge table.
196 #edge{vs=Va,ve=Vb} = array:get(E, Etab),
a552d9c4 » giniu
2006-08-18 initial import
197 hard_edges(Es, Etab, [hard(Va, Vb)|Acc]);
198 hard_edges([], _Etab, Acc) -> Acc.
199
200 hard(A, B) when A < B -> {A,B};
201 hard(A, B) -> {B,A}.
202
203 mat_images(Mats) ->
204 mat_images(Mats, []).
205
206 mat_images([{Name,Mat0}|T], Acc) ->
207 Mat = mat_images_1(Mat0, []),
208 mat_images(T, [{Name,Mat}|Acc]);
209 mat_images([], Acc) -> Acc.
210
211 mat_images_1([{maps,Maps0}|T], Acc) ->
212 Maps = mat_images_2(Maps0, []),
213 mat_images_1(T, [{maps,Maps}|Acc]);
214 mat_images_1([H|T], Acc) ->
215 mat_images_1(T, [H|Acc]);
216 mat_images_1([], Acc) -> Acc.
217
218 mat_images_2([{Type,Id}|T], Acc) ->
219 Im = wings_image:info(Id),
220 mat_images_2(T, [{Type,Im}|Acc]);
221 mat_images_2([], Acc) -> Acc.
222
223 %%% Save all images.
224
225 save_images_1([{Name,Mat0}|T], Dir, Filetype, Acc) ->
226 Mat = save_images_2(Mat0, Dir, Filetype, []),
227 save_images_1(T, Dir, Filetype, [{Name,Mat}|Acc]);
228 save_images_1([], _, _, Acc) -> Acc.
229
230 save_images_2([{maps,Maps0}|T], Dir, Filetype, Acc) ->
231 Maps = save_images_3(Maps0, Dir, Filetype, []),
232 save_images_2(T, Dir, Filetype, [{maps,Maps}|Acc]);
233 save_images_2([H|T], Dir, Filetype, Acc) ->
234 save_images_2(T, Dir, Filetype, [H|Acc]);
235 save_images_2([], _, _, Acc) -> Acc.
236
237 save_images_3([{Type,#e3d_image{filename=none,name=Name}=Im0}|T],
238 Dir, Filetype, Acc) ->
239 Filename = filename:absname(Name ++ Filetype, Dir),
240 Im = Im0#e3d_image{filename=Filename},
241 Ps = [{filename,Filename},{image,Im}],
242 wings_image:image_write(Ps),
243 save_images_3(T, Dir, Filetype, [{Type,Im}|Acc]);
244 save_images_3([H|T], Dir, Filetype, Acc) ->
245 save_images_3(T, Dir, Filetype, [H|Acc]);
246 save_images_3([], _, _, Acc) -> Acc.
af2d517f » bjorng
2009-07-25 Wawefront export: Prevent crash in smoothing group calculations
247
248 %%%
249 %%% Calculate smoothing groups.
250 %%%
251
252 smooth_groups(#we{he=He}=We) ->
253 case gb_sets:is_empty(He) of
254 true ->
255 %% Optimization: put all faces in the same smoothing group
256 %% directly without constructing a digraph if there are
257 %% no hard edges. The easiest way to do that is to return
258 %% an empty gb_tree.
259 gb_trees:empty();
260 false ->
261 smooth_groups_1(We)
262 end.
263
264 smooth_groups_1(#we{fs=Fs,he=He}=We) ->
265 Es = foldl(fun(Face, Acc) ->
266 wings_face:fold(fun(_, Edge, _, A) ->
267 [{Edge,Face}|A]
268 end, Acc, Face, We)
269 end, [], gb_trees:keys(Fs)),
270 R = sofs:relation(Es, [{edge,face}]),
271 Fam0 = sofs:relation_to_family(R),
272 Fam = sofs:to_external(Fam0),
273
274 %% Create a digraph. Create a vertex in the digraph for each
275 %% face in the object. Connect two vertices (i.e. faces in the
276 %% object) with an edge only if the edge between the faces in
277 %% the object is soft. The resulting components of the digraph
278 %% will be the smoothing groups.
279 G = digraph:new(),
280 build_graph(G, Fam, He),
281 Cs = digraph_utils:components(G),
282 digraph:delete(G),
283
284 %% Generate a mapping from Face to a list of all neighboring faces.
285 Neib0 = sofs:range(Fam0),
286 Neib1 = sofs:canonical_relation(Neib0),
287 Neib2 = sofs:relation_to_family(Neib1),
288 Neib3 = sofs:family_union(Neib2),
289 Neib4 = sofs:to_external(Neib3),
290 Neib = gb_trees:from_orddict(Neib4),
291
292 %% Number the smoothing groups starting from 1.
293 %%
294 %% Try to generate as few smoothing groups as possible,
295 %% since some applications may have trouble handling
296 %% hundreds or thousands of smoothing groups.
297 exp_sgs_1(Cs, Neib, gb_trees:empty()).
298
299 %% Return [{Face,SG}].
300 exp_sgs_1([Fs|Cs], Neib, SgMap0) ->
301 SG = find_sg(Fs, Neib, SgMap0),
302 SgMap = foldl(fun(F, M) ->
303 gb_trees:insert(F, SG, M)
304 end, SgMap0, Fs),
305 exp_sgs_1(Cs, Neib, SgMap);
306 exp_sgs_1([], _, SgMap) -> SgMap.
307
308 %% find_sg(Faces, NeighborMap, SgMap) -> SG
309 %% Find the lowest smoothing group number (>= 1) that is not
310 %% used by any face that is a neighbor to any face in Faces.
311 %%
312 find_sg(Fs, Neib, SgMap) ->
313 find_sg_2(find_sg_1(Fs, Neib, SgMap, gb_sets:new()), 1).
314
315 find_sg_1([F|Fs], Neib, SgMap, Acc0) ->
316 Acc = foldl(fun(N, A) ->
317 case gb_trees:lookup(N, SgMap) of
318 none -> A;
319 {value,SG} when is_integer(SG) -> gb_sets:add(SG, A)
320 end
321 end, Acc0, gb_trees:get(F, Neib)),
322 find_sg_1(Fs, Neib, SgMap, Acc);
323 find_sg_1([], _, _, Acc) -> gb_sets:to_list(Acc).
324
325 find_sg_2([SG|T], SG) -> find_sg_2(T, SG+1);
326 find_sg_2(_, SG) -> SG.
327
328 build_graph(G, [{Edge,[Fa,Fb]}|T], He) ->
329 digraph:add_vertex(G, Fa),
330 digraph:add_vertex(G, Fb),
331 case gb_sets:is_member(Edge, He) of
332 true -> ok;
333 false -> digraph:add_edge(G, Fa, Fb)
334 end,
335 build_graph(G, T, He);
336 build_graph(_, [], _) -> ok.
Something went wrong with that request. Please try again.