1%%
2%%  collada_import.erl --
3%%
4%%     Collada import.
5%%
6%%  Copyright (c) 2016 Dan Gudmundsson
7%%
8%%  See the file "license.terms" for information on usage and redistribution
9%%  of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10%%
11%%     $Id$
12%%
13-module(collada_import).
14-export([import/1]).
15-include_lib("e3d/e3d.hrl").
16-include_lib("src/wings.hrl").
17%% Local exports callbacks
18-export([ignored/4, asset/3, lib_geom/4, mesh/4, source/4,
19         param/4, vertices/4, make_polys/1, polys/4,
20         lib_material/4, lib_images/4, effects/4,
21         scene/3, lib_scenes/4, node/4, matrix/4, instance_geom/4,
22         common_mat/4, common_newparam/4, surface/4,
23         sampler2D/4, sloppy_color/3, chars/3,
24         pop/1, replace/2, to_floats/1, make_float/1,
25         to_ints/1, to_tuple/2, to_tuple2/1, to_tuple3/1,
26         to_tuple4/1, to_tuple5/1,
27         pack_source/1, make_mesh/1, pick_source/2, pick_src_1/2,
28         pick_mesh/2, pick_mesh_1/2, mesh_type/2,
29         polygon_type/1, polygon_type_1/2,
30         make_faces/2, sort_inputs/2, remove_duplicates/1,
31         pick_faces/5, pick_tristrips/5, pick_polygons/4,
32         pick_polygon/3, pick_verts/4, pick_vert/4,
33         add_vert_info/3, rev_face/2, rev_face/1
34        ]).
35
36
37-record(mat,
38        {refs=#{},
39         defs=#{},
40	 images=#{}
41        }).
42
43-record(es, {vsn,
44             state=[],
45             up = y,
46             materials=#mat{},
47	     mesh=[],
48	     scenes=[],
49	     scene,
50             val %% Temporary return value
51            }).
52
53%%-define(TEST, true).
54-ifdef(TEST).
55-export([test/0]).
56-endif.
57
58import(File) ->
59    EF = {event_fun, fun top/3},
60    ES = {event_state, #es{}},
61    %% ?dbg("Import: ~p~n",[File]),
62    case xmerl_sax_parser:file(File, [EF,ES]) of
63        {ok, Es, <<>>} ->
64            try
65                E3dFile = make_file(Es),
66                {ok, E3dFile#e3d_file{dir=filename:dirname(File)}}
67            catch _:Reason:ST ->
68                    io:format("ERROR: ~P in ~p~n", [Reason, 20, ST]),
69                    {error, ?__(1, "unknown/unhandled format, see log window")}
70            end;
71        {Error, {_,_,Line}, Reason, _ET, _St} ->
72            io:format("~s:~p: ERROR: ~p:~p~n", [File, Line, Error, Reason]),
73            {error, ?__(1, "unknown/unhandled format, see log window")}
74    end.
75
76
77%%%%%%%%%%%%%%%%%%%%%%%% PARSER %%%%%%%%%%%%%%%%%%%%%%%%
78top({startElement, _, "extra"=Ignore, _, _}=Ev, Loc, State) ->
79    %% Ignore all extra fields for now
80    parse(push({ignored, Ignore},State), Ev, Loc);
81top({startElement, _, _, _, _}=Ev, Loc, State) ->
82    parse(State, Ev, Loc);
83top({endElement, _, _, _}=Ev, Loc, State) ->
84    parse(State, Ev, Loc);
85top({characters, _}=Ev, Loc, State) ->
86    parse(State, Ev, Loc);
87top(startDocument, _, State) -> State;
88top(endDocument, _, State) -> State;
89top({startPrefixMapping,_,_}, _, State) -> State;
90top({endPrefixMapping,_}, _, State) -> State;
91top({ignorableWhitespace, _}, _, State) -> State;
92top({comment, _}, _, State) -> State;
93top(Ev, Loc, State) ->
94    unhandled(State, Ev, Loc),
95    State.
96
97%%%%%%%%%%%%%%%%%%%%%%%%
98parse(#es{state=[_|_]}=Es, Ev, Loc) ->
99    invoke(Es, Ev, Loc);
100parse(#es{state=[]}=Es, {startElement, _, "COLLADA", _, As0}, _) ->
101    case attrs(As0) of
102        #{version :=Vsn} -> Es#es{vsn=Vsn};
103        _ -> Es
104    end;
105parse(#es{state=[]}=Es, {endElement, _, _, _}, _) ->
106    Es;
107parse(#es{state=[]}=Es, {startElement, _, "asset", _, _}, _) ->
108    push(asset, Es);
109parse(#es{state=[]}=Es, {startElement, _, "library_materials", _, _}, _) ->
110    push({lib_material, new}, Es);
111parse(#es{state=[]}=Es, {startElement, _, "library_images", _, _}, _) ->
112    push({lib_images, []}, Es);
113parse(#es{state=[]}=Es, {startElement, _, "library_effects", _, _}, _) ->
114    push({effects, new}, Es);
115parse(#es{state=[]}=Es, {startElement, _, "library_geometries", _, _}, _) ->
116    push({lib_geom, new}, Es);
117parse(#es{state=[]}=Es, {startElement, _, "library_visual_scenes", _, _}, _) ->
118    push({lib_scenes, new}, Es);
119parse(#es{state=[]}=Es, {startElement, _, "scene", _, _}, _) ->
120    push(scene, Es);
121parse(#es{state=[]}=Es, {startElement, _, "library_lights"=What, _, _}, _) ->
122    push({ignored, What}, Es);
123parse(#es{state=[]}=Es, {startElement, _, "library_cameras"=What, _, _}, _) ->
124    push({ignored, What}, Es);
125parse(#es{state=[]}=Es, {startElement, _, "library_animation"++_=What, _, _}, _) ->
126    push({ignored, What}, Es);
127parse(#es{state=[]}=Es, {startElement, _, "library_controllers"=What, _, _}, _) ->
128    push({ignored, What}, Es);
129parse(#es{state=[]}=Es, {startElement, _, What, _, _}, Loc) ->
130    unhandled(Es, What, Loc),
131    push({ignored, What}, Es);
132parse(Es, Ev, Loc) ->
133    unhandled(Es, Ev, Loc),
134    Es.
135
136%%%%%%%%%%%%%%%%%%%%%%%%
137ignored(State, Es, {endElement, _, State,_}, _Loc) ->
138    pop(Es);
139ignored(_, Es, _, _Loc) ->
140    Es.
141
142%%%%%%%%%%%%%%%%%%%%%%%%
143% We are only interested in top_level assets for up_axis
144asset(Es, {startElement, _, "up_axis", _, _}, _) ->
145    push(chars, Es);
146asset(#es{val=DirStr}=Es, {endElement, _, "up_axis", _}, _) ->
147    case DirStr of
148        "X_UP" -> Es#es{up=x};
149        "Y_UP" -> Es#es{up=y};
150        "Z_UP" -> Es#es{up=z}
151    end;
152asset(Es, {endElement, _, "asset",_}, _Loc) ->
153    pop(Es);
154asset(Es, _, _) ->
155    Es.
156
157%%%%%%%%%%%%%%%%%%%%%%%%
158lib_geom(new, Es, {startElement, _, "geometry", _, As0}, _) ->
159    As = attrs(As0),
160    replace(As, Es);
161lib_geom(Data, Es, {startElement, _, "mesh", _, _}, _) ->
162    push({mesh, Data#{polys=>[]}}, Es);
163lib_geom(_Data, #es{val=Val}=Es, {endElement, _, "geometry",_}, _Loc) ->
164    Mesh = make_mesh(Val),
165    replace(new, Es#es{mesh=[Mesh|Es#es.mesh],val=undefined});
166lib_geom(_Data, Es, {endElement, _, "library_geometries",_}, _) ->
167    pop(Es).
168
169%%%%%%%%%%%%%%%%%%%%%%%%
170mesh(_Data, Es, {startElement, _, "source", _, As}, _) ->
171    push({source, attrs(As)}, Es);
172mesh(Data, #es{val=Values}=Es, {endElement, _, "source", _}, _) ->
173    Id = maps:get(id, Values),
174    replace(Data#{{source, Id}=>Values}, Es#es{val=undefined});
175mesh(_Data, Es, {startElement, _, "vertices", _, As}, _) ->
176    push({vertices, attrs(As)}, Es#es{val=[]});
177mesh(Data, #es{val=Values}=Es, {endElement, _, "vertices", _}, _) ->
178    replace(Data#{vertices=>Values}, Es#es{val=undefined});
179mesh(_Data, Es, {startElement, _, "polylist", _, As}, _) ->
180    push({polys, attrs(As, make_polys(polylist))}, Es);
181mesh(_Data, Es, {startElement, _, "triangles", _, As}, _) ->
182    push({polys, attrs(As, make_polys(triangles))}, Es);
183mesh(_Data, Es, {startElement, _, "tristrips", _, As}, _) ->
184    push({polys, attrs(As, make_polys(tristrips))}, Es);
185mesh(_Data, Es, {startElement, _, "polygons", _, As}, _) ->
186    push({polys, attrs(As, make_polys(polygons))}, Es);
187mesh(_Data, Es, {startElement, _, "lines"=What, _, _}, _) ->
188    push({ignored, What}, Es);
189mesh(Data, Es, {endElement, _, "mesh",_}, _Loc) ->
190    pop(Es#es{val=Data});
191mesh(#{polys:=Ps, vertices:=Vs}=Data, #es{val=Vals} = Es, {endElement, _, _, _}, _) ->
192    Faces = make_faces(Vals, Vs),
193    replace(Data#{polys=>[Faces|Ps]}, Es#es{val=undefined}).
194
195%%%%%%%%%%%%%%%%%%%%%%%%
196source(_Data, Es, {startElement, _, "technique_common", _, _As}, _) -> Es;
197source(_Data, Es, {endElement, _, "technique_common", _}, _) ->  Es;
198source(_Data, Es, {startElement, _, "accessor", _, As}, _) ->
199    push({param, attrs(As, #{offset=>0, stride=>1})}, Es#es{val=[]});
200source(Data, #es{val=Values}=Es, {endElement, _, "accessor", _}, _) ->
201    replace(Data#{accessor=>Values}, Es#es{val=undefined});
202source(Data0, Es, {endElement, _, "source", _}=Ev, Loc) ->
203    Data = pack_source(Data0),
204    invoke(pop(Es#es{val=Data}), Ev, Loc);
205source(Data, Es, {startElement, _, "float_array", _, _As}, _) ->
206    %% push(chars, replace(attrs(As, Data#{type=>float}), Es));
207    push(chars, replace(Data#{type=>float}, Es));
208source(Data, Es, {startElement, _, "int_array", _, _As}, _) ->
209    %% push(chars, replace(attrs(As, Data#{type=>int}), Es));
210    push(chars, replace(Data#{type=>int}, Es));
211source(Data, Es, {startElement, _, "bool_array", _, _As}, _) ->
212    %%push(chars, replace(attrs(As, Data#{type=>bool}), Es));
213    push(chars, replace(Data#{type=>bool}, Es));
214source(Data, Es, {startElement, _, "Name_array", _, _As}, _) ->
215    %%push(chars, replace(attrs(As, Data#{type=>name}), Es));
216    push(chars, replace(Data#{type=>name}, Es));
217source(Data, Es, {startElement, _, "IDREF_array", _, _As}, _) ->
218    %%push(chars, replace(attrs(As, Data#{type=>idref}), Es));
219    push(chars, replace(Data#{type=>idref}, Es));
220source(#{type:=Type}=Data, #es{val=List}=Es, {endElement, _, _, _}, _) ->
221    Vals = case Type of
222	       float -> to_floats(List);
223	       int -> [to_ints(Entry) || Entry <- List];
224               name -> List
225	   end,
226    replace(Data#{array=>Vals}, Es#es{val=undefined}).
227
228param(Data, #es{val=Vs}=Es, {startElement, _, "param", _, As}, _) ->
229    replace(Data, Es#es{val=[attrs(As)|Vs]});
230param(_Data, Es, {endElement, _, "param", _}, _) ->
231    Es;
232param(Data, #es{val=Vs}=Es, {endElement, _, _, _}=Ev, Loc) ->
233    invoke(pop(Es#es{val=Data#{params=>lists:reverse(Vs)}}), Ev, Loc).
234
235vertices(Data, Es, {startElement, _, "input", _, As0}, _) ->
236    replace(Data, Es#es{val=[attrs(As0)|Es#es.val]});
237vertices(_Data, Es, {endElement, _, "input", _}, _) -> Es;
238vertices(Data, #es{val=Vals}=Es, {endElement, _, "vertices", _}=Ev, Loc) ->
239    invoke(pop(Es#es{val=Data#{input=>lists:reverse(Vals)}}), Ev, Loc).
240
241make_polys(Type) ->
242    #{type=>Type, input=>[], p=>[]}.
243
244polys(#{input:=In}=Data, Es, {startElement, _, "input", _, As}, _) ->
245    replace(Data#{input=>[attrs(As)|In]}, Es);
246polys(_Data, Es, {endElement, _, "input", _}, _) ->
247    Es;
248polys(_Data, Es, {startElement, _, "vcount", _, _}, _) ->
249    push(chars, Es);
250polys(Data, #es{val=List}=Es, {endElement, _, "vcount", _}, _) ->
251    replace(Data#{vcount=>to_ints(List)}, Es#es{val=undefined});
252polys(_Data, Es, {startElement, _, "p", _, _}, _) ->
253    push(chars, Es);
254polys(#{p:=P}=Data, #es{val=List}=Es, {endElement, _, "p", _}, _) ->
255    replace(Data#{p:=[to_ints(List)|P]}, Es#es{val=undefined});
256polys(#{input:=In,p:=P}=Data0, Es, {endElement, _, _, _}=Ev, Loc) ->
257    Data = Data0#{input:=lists:reverse(In), p:=lists:reverse(P)},
258    invoke(pop(Es#es{val=Data}), Ev, Loc).
259
260%%%%%%%%%%%%%%%%%%%%%%%%
261lib_material(new, Es, {startElement, _, "material", _, As0}, _) ->
262    As = attrs(As0),
263    replace(As, Es);
264lib_material(#{id:=Id}=Attrs, #es{materials=Mat0}=Es, {endElement, _, "material",_}, _Loc) ->
265    MatRef = (Mat0#mat.refs)#{Id=>Attrs},
266    replace(new, Es#es{materials=Mat0#mat{refs=MatRef}});
267lib_material(Attrs, Es, {startElement, _, "instance_effect", _, As0}, _) ->
268    case attrs(As0) of
269        #{url:=Id} -> replace(Attrs#{instance_effect=>Id}, Es)
270    end;
271lib_material(_, Es, {endElement, _, "instance_effect",_}, _Loc) ->
272    Es;
273lib_material(_, Es, {endElement, _, "library_materials",_}, _Loc) ->
274    pop(Es).
275
276%%%%%%%%%%%%%%%%%%%%%%%%
277lib_images(Data, Es, {startElement, _, "image", _, As}, _) ->
278    replace([attrs(As)|Data], Es);
279lib_images(_Data, Es, {startElement, _, "init_from", _, _As}, _) ->
280    push(chars, Es);
281lib_images([Head|Data], #es{val=Val}=Es, {endElement, _, "init_from", _}, _) ->
282    replace([Head#{file=>Val}|Data], Es#es{val=undefined});
283lib_images(_, Es, {endElement, _, "image", _}, _) ->
284    Es;
285lib_images(Data, #es{materials=Mat}=Es, {endElement, _, "library_images",_}, _Loc) ->
286    Images = maps:from_list([{Id, Image} || #{id:=Id} = Image <- Data]),
287    pop(Es#es{materials=Mat#mat{images=Images}}).
288
289%%%%%%%%%%%%%%%%%%%%%%%%
290effects(new, Es, {startElement, _, "effect", _, As0}, _) ->
291    As = attrs(As0),
292    replace(As, Es);
293effects(#{id:=Id}=Attrs, #es{materials=Mat0}=Es, {endElement, _, "effect", _}, _) ->
294    MatDef = (Mat0#mat.defs)#{Id=>Attrs},
295    replace(new, Es#es{materials=Mat0#mat{defs=MatDef}});
296effects(_Data, Es, {startElement, _, "profile_COMMON", _,_}, _) ->
297    Es;
298effects(Data, Es, {startElement, _, "technique", _,_}, _) ->
299    push({common_mat, Data}, Es);
300effects(_Data, Es, {endElement, _, "technique", _}, _) ->
301    replace(Es#es.val, Es#es{val=undefined});
302effects(_Data, Es, {startElement, _, "newparam", _, As0}, _) ->
303    push({common_newparam, attrs(As0)}, Es);
304effects(Data, #es{val=Val}=Es, {endElement, _, "newparam", _}, _) ->
305    #{sid:=Sid}=Val,
306    replace(Data#{Sid=>Val}, Es#es{val=undefined});
307
308effects(new, Es, {endElement, _, "library_effects",_}, _Loc) ->
309    pop(Es);
310effects(_Data, Es, {endElement, _, _, _}, _) ->
311    Es.
312
313%%%%%%%%%%%%%%%%%%%%%%%%
314scene(#es{scenes=Ss}=Es, {startElement, _, "instance_visual_scene", _, As}, _) ->
315    #{url:=[$#|SceneId]} = attrs(As),
316    {_, Scene} = lists:keyfind(SceneId,1,Ss),
317    Es#es{scene=Scene};
318scene(Es, {endElement, _, "instance_visual_scene", _}, _) ->
319    Es;
320scene(Es, {endElement, _, "scene", _}, _) ->
321    pop(Es).
322
323lib_scenes(new, Es, {startElement, _, "visual_scene", _, As}, _) ->
324    replace(attrs(As, #{nodes=>[]}), Es);
325lib_scenes(_, Es, {startElement, _, "node", _, As}, _) ->
326    push({node, attrs(As, #{matrix=>[], geom=>[], sub_nodes=>[]})}, Es);
327lib_scenes(#{nodes:=Ns}=Data, #es{val=Val}=Es, {endElement, _, "node", _}, _) ->
328    %% ?dbg("Geom: ~p~n",[Val]),
329    case [N || #{geom:=G} = N <- Val, G =/= []] of
330	[] -> Es;
331	New -> replace(Data#{nodes:=New++Ns}, Es#es{val=undefined})
332    end;
333lib_scenes(#{id:=Id}=Data, #es{scenes=Ss}=Es, {endElement, _, "visual_scene", _}, _) ->
334    replace(new, Es#es{scenes=[{Id, Data}|Ss]});
335lib_scenes(new, Es, {endElement, _, "library_visual_scenes", _}, _) ->
336    pop(Es).
337
338node(_Data, Es, {startElement, _, "instance_geometry", _, As}, _) ->
339    #{url:=Url} = attrs(As),
340    push({instance_geom, #{geom=>Url, mats=>#{}}}, Es);
341node(#{geom:=Gs}=Data, #es{val=Val}=Es, end_instance_geom, _) ->
342    replace(Data#{geom=>[Val|Gs]}, Es#es{val=undefined});
343node(#{sub_nodes:=Ns}=Data, Es0, {endElement, _, "node", _}=Ev, Loc) ->
344    %% ?dbg("End Node: ~p ~p~n",[Es0, Data]),
345    case pop(Es0) of
346        #es{state=[{node,_}|_]} = Es ->
347            %% Recursive, flatten the nodes to a list
348            %% wings can not handle groups..
349            invoke(Es#es{val=Data}, sub_node, Loc);
350        Es ->
351            invoke(Es#es{val=[Data|Ns]}, Ev, Loc)
352    end;
353node(_, Es, {endElement, _, _, _}, _) ->
354    Es;
355node(#{matrix:=M}=_Data, Es, {startElement, _, "node", _, As}, _) ->
356    push({node, attrs(As, #{matrix=>M, geom=>[], sub_nodes=>[]})}, Es);
357%% Matrix
358node(#{sub_nodes:=Ns}=Data, #es{val=Val}=Es, sub_node, _Loc) ->
359    replace(Data#{sub_nodes := [Val|Ns]}, Es#es{val=undefined});
360node(#{matrix:=M}, Es, {startElement, _, "translate", _, _}, _) ->
361    push(chars, push({matrix, {translate, M}}, Es));
362node(#{matrix:=M}, Es, {startElement, _, "rotate", _, _As}, _) ->
363    push(chars, push({matrix, {rotate, M}}, Es));
364node(#{matrix:=M}, Es, {startElement, _, "scale", _, _As}, _) ->
365    push(chars, push({matrix, {scale, M}}, Es));
366node(#{matrix:=M}, Es, {startElement, _, "matrix", _, _As}, _) ->
367    push(chars, push({matrix, {matrix, M}}, Es));
368node(Data, #es{val=Val}=Es, end_matrix, _) ->
369    replace(Data#{matrix:=lists:reverse(Val)}, Es#es{val=undefined});
370node(_, Es, {startElement, _, What, _, _}, _Loc) ->
371    %% Ignore camera and lights for now
372    %% ?dbg("~p ignored: ~p @ ~p~n",[?FUNCTION_NAME, What, _Loc]),
373    push({ignored, What}, Es).
374
375instance_geom(_Data, Es, {startElement, _, "technique_common", _, _As}, _) -> Es;
376instance_geom(_Data, Es, {startElement, _, "bind_material", _, _As}, _) -> Es;
377instance_geom(#{mats:=Mats}=Data, Es, {startElement, _, "instance_material", _, As}, _) ->
378    #{symbol:=Key, target:=Target} = attrs(As),
379    replace(Data#{mats:=Mats#{Key=>Target}}, Es);
380instance_geom(Data, Es, {endElement, _, "instance_geometry", _}, Loc) ->
381    invoke(pop(Es#es{val=Data}), end_instance_geom, Loc);
382instance_geom(_, Es, {endElement, _, _, _}, _) ->
383    Es;
384instance_geom(_, Es, {startElement, _, What, _, _}, _Loc) ->
385    %% Can't handle multiple uv-coords...
386    %%?dbg("~p: ignored: ~p @ ~p~n",[?FUNCTION_NAME, What, _Loc]),
387    push({ignored, What}, Es).
388
389matrix({Type,Data}, #es{val=Val}=Es, {endElement, _, _, _}, Loc) ->
390    Floats = to_floats(Val),
391    invoke(pop(Es#es{val=[{Type, Floats}|Data]}), end_matrix, Loc).
392
393%%%%%%%%%%%%%%%%%%%%%%%%
394common_mat(Data, Es, {startElement, _, TechOrColor, _, _As0}, _) ->
395    case maps:is_key(type, Data) of
396        false -> replace(Data#{type=>TechOrColor}, Es);
397        true  -> push(sloppy_color, Es)
398    end;
399common_mat(#{type:=Type}=Data, #es{val=Val}=Es, {endElement, _, What, _}, _) ->
400    case What of
401        Type -> pop(Es#es{val=Data});
402        _ -> replace(Data#{list_to_atom(What)=>Val}, Es#es{val=undefined})
403    end.
404
405common_newparam(Data, Es, {startElement, _, "surface", _, As}, _) ->
406    push({surface, attrs(As, Data)}, Es);
407common_newparam(Data, Es, {startElement, _, "sampler2D", _, As}, _) ->
408    push({sampler2D, attrs(As, Data)}, Es);
409common_newparam(_Data, Es, {endElement, _, "newparam", _}=Ev, Loc) ->
410    invoke(pop(Es), Ev, Loc);
411common_newparam(_Data, Es, {endElement, _, _, _}, _) ->
412    Es.
413
414surface(_Data, Es, {startElement, _, _, _, _}, _) ->
415    push(chars, Es);
416surface(Data, Es, {endElement, _, "surface", _}, _) ->
417    pop(Es#es{val=Data});
418surface(Data, Es, {endElement, _, What, _}, _) ->
419    replace(Data#{What=>Es#es.val}, Es).
420
421sampler2D(_Data, Es, {startElement, _, _, _, _}, _) ->
422    push(chars, Es);
423sampler2D(Data, Es, {endElement, _, "sampler2D", _}, _) ->
424    pop(Es#es{val=Data});
425sampler2D(Data, Es, {endElement, _, What, _}, _) ->
426    replace(Data#{What=>Es#es.val}, Es).
427
428%%%%%%%%%%%%%%%%%%%%%%%%
429sloppy_color(Es, {startElement, _, "color", _, _}, _) ->
430    push(chars,Es);
431sloppy_color(Es, {startElement, _, "float", _, _}, _) ->
432    push(chars,Es);
433sloppy_color(Es, {startElement, _, "texture", _, As}, _) ->
434    Es#es{val=attrs(As)};
435sloppy_color(Es, {endElement, _, "texture", _}, _) ->
436    pop(Es);
437sloppy_color(#es{val=List}=Es, {endElement, _, What, _}, _) ->
438    case lists:member(What, ["color", "float"]) of
439        true ->
440            Col = case to_floats(List) of
441                      [Val] -> Val;
442                      [R,G,B] -> {R,G,B,1.0};
443                      [R,G,B,A] -> {R,G,B,A}
444                  end,
445            pop(Es#es{val=Col});
446        false ->
447            Es
448    end.
449
450chars(Es, {characters, List}, _) ->
451    pop(Es#es{val=List});
452chars(#es{state=[_,{source, #{type:=Prev}=Data}|_]}=Es, {endElement, _, Type, _}=Ev, Where) ->
453    case Prev of
454        float when Type =:= "float_array" -> ?MODULE:source(Data, pop(Es#es{val=[]}), Ev, Where);
455        int   when Type =:= "int_array"   -> ?MODULE:source(Data, pop(Es#es{val=[]}), Ev, Where);
456        bool  when Type =:= "bool_array"  -> ?MODULE:source(Data, pop(Es#es{val=[]}), Ev, Where);
457        name  when Type =:= "Name_array"  -> ?MODULE:source(Data, pop(Es#es{val=[]}), Ev, Where);
458        idref when Type =:= "IDREF_array" -> ?MODULE:source(Data, pop(Es#es{val=[]}), Ev, Where);
459        _ ->
460            unhandled(Es, Ev, Where),
461            Es
462    end.
463
464%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
465unhandled(#es{state=[]}, Ev, {_,File,Line}) ->
466    io:format("~s:~p ignored:   ~P~n", [File, Line, strip_chars(Ev), 20]);
467unhandled(#es{state=State0}, Ev, {_,File,Line}) ->
468    State = [fun({S,_}) -> S;(S) -> S end(St) || St <- State0],
469    io:format("~s:~p ~p ignored:~n\t ~P~n", [File, Line, State, strip_chars(Ev), 20]).
470
471strip_chars({characters, List}=What) ->
472    try lists:split(50, List) of
473	{First, _} -> {characters, First++"...."}
474    catch _:_ -> What end;
475strip_chars(Other) -> Other.
476
477attrs(As) ->
478    attrs(As, #{}).
479
480attrs(As, Map0) ->
481    Add = fun({_, _, Name, Value}, Map) ->
482		  Map#{list_to_atom(Name)=>attr_value(Value)}
483	  end,
484    lists:foldl(Add, Map0, As).
485
486attr_value(Val) ->
487    try list_to_integer(Val)
488    catch _:_ -> Val end.
489
490
491invoke(#es{state=[Top|_]}=Es, Ev, Loc) ->
492    try
493	case Top of
494	    {State, Data} ->
495		?MODULE:State(Data, Es, Ev, Loc);
496	    State when is_atom(State) ->
497		?MODULE:State(Es, Ev, Loc)
498	end
499    catch
500        error:function_clause=Reason:ST ->
501	    case ST of
502		[{?MODULE,Func, _,_}|_] when Func =:= Top ->
503		    unhandled(Es, Ev, Loc),
504		    Es;
505		[{?MODULE,Func, _,_}|_] when Func =:= element(1,Top) ->
506		    unhandled(Es, Ev, Loc),
507		    Es;
508		_ ->
509		    io:format("~p:~n ~P~n", [Reason, ST, 20]),
510		    io:format("Last: ~P~n~P~n",[Ev,5,Es,10]),
511		    throw({fatal_error, parser_error})
512	    end;
513        error:Reason:ST ->
514	    io:format("~p:~n ~P~n", [Reason, ST, 20]),
515	    io:format("Last: ~P~n~P~n",[Ev,5,Es,10]),
516	    throw({fatal_error, parser_error})
517    end.
518
519push(State,#es{state=Stack}=Es) -> Es#es{state=[State|Stack]}.
520pop(#es{state=[_|Stack]}=Es) -> Es#es{state=Stack}.
521replace(Data,#es{state=[{State,_Data}|Stack]}=Es) -> Es#es{state=[{State,Data}|Stack]}.
522
523to_floats(List) ->
524    [make_float(Str) || Str <- string:tokens(List, " \n\t")].
525
526make_float(Orig) ->
527    try wings_util:string_to_float(Orig)
528    catch _:badarg ->
529	    throw({fatal_error, {bad_float, Orig}})
530    end.
531
532to_ints(List) ->
533    [list_to_integer(Str) || Str <- string:tokens(List, " ")].
534
535to_tuple(1, List) -> List;
536
537to_tuple(2, List) -> to_tuple2(List);
538to_tuple(3, List) -> to_tuple3(List);
539to_tuple(4, List) -> to_tuple4(List);
540to_tuple(5, List) -> to_tuple5(List).
541
542to_tuple2([A,B|Rest]) -> [{A,B}|to_tuple2(Rest)];
543to_tuple2([]) -> [].
544
545to_tuple3([A,B,C|Rest]) -> [{A,B,C}|to_tuple3(Rest)];
546to_tuple3([]) -> [].
547
548to_tuple4([A,B,C,D|Rest]) -> [{A,B,C,D}|to_tuple4(Rest)];
549to_tuple4([]) -> [].
550
551to_tuple5([A,B,C,D,E|Rest]) -> [{A,B,C,D,E}|to_tuple5(Rest)];
552to_tuple5([]) -> [].
553
554%%%%%%%%%%%%%%%%%%%%%%%%
555pack_source(#{accessor:=#{offset:=0, stride:=Stride}, array:=List}=Source) ->
556    Source#{array:=to_tuple(Stride, List)}.
557
558make_mesh(#{id:=Id, polys:=Polys}=Geom) ->
559    %% ?dbg("~p~n",[Geom]),
560    {Type,Mesh0} = lists:foldl(fun pick_mesh/2, {undefined, #e3d_mesh{}}, Polys),
561    Mesh1 = pick_source(Mesh0#e3d_mesh{type=Type}, Geom),
562    {Id,Mesh1}.
563
564pick_source(#e3d_mesh{vs=Vs, vc=VC, tx=Tx, ns=Ns}=M, Geom) ->
565    M#e3d_mesh{vs=pick_src_1(Vs,Geom), vc=pick_src_1(VC, Geom),
566	       tx=pick_src_1(Tx,Geom), ns=pick_src_1(Ns, Geom)}.
567
568pick_src_1([], _Geom) -> [];
569pick_src_1([$#|Src], Geom) ->
570    #{array:=Data} = maps:get({source, Src}, Geom),
571    Data.
572
573pick_mesh(#{input:=In, p:=P, type:=Type}, {MT, #e3d_mesh{fs=MFs}=Mesh0}) ->
574    Mesh = pick_mesh_1(In, Mesh0),
575    {mesh_type(MT, Type), Mesh#e3d_mesh{fs=P++MFs}}.
576
577pick_mesh_1([#{semantic:="POSITION", source:=Src}|In], #e3d_mesh{vs=OldSrc}=M) ->
578    case OldSrc of
579	[] -> pick_mesh_1(In, M#e3d_mesh{vs=Src});
580	Src -> pick_mesh_1(In, M)
581    end;
582pick_mesh_1([#{semantic:="NORMAL", source:=Src}|In], #e3d_mesh{ns=OldSrc}=M) ->
583    case OldSrc of
584	[] -> pick_mesh_1(In, M#e3d_mesh{ns=Src});
585	Src -> pick_mesh_1(In, M)
586    end;
587pick_mesh_1([#{semantic:="TEXCOORD", source:=Src}|In], #e3d_mesh{tx=OldSrc}=M) ->
588    case OldSrc of
589	[] -> pick_mesh_1(In, M#e3d_mesh{tx=Src});
590	Src -> pick_mesh_1(In, M)
591    end;
592pick_mesh_1([#{semantic:="COLOR", source:=Src}|In], #e3d_mesh{vc=OldSrc}=M) ->
593    case OldSrc of
594	[] -> pick_mesh_1(In, M#e3d_mesh{vc=Src});
595	Src -> pick_mesh_1(In, M)
596    end;
597pick_mesh_1([_|In], Mesh) ->
598    pick_mesh_1(In, Mesh);
599pick_mesh_1([], Mesh) -> Mesh.
600
601mesh_type(undefined, Type) -> Type;
602mesh_type(Type, Type) -> Type;
603mesh_type(_, _) -> polygon.
604
605polygon_type([#e3d_face{vs=Vs}|Fs]) ->
606    polygon_type_1(Fs, length(Vs)).
607
608polygon_type_1([#e3d_face{vs=Vs}|Fs], Sz)
609  when length(Vs) =:= Sz ->
610    polygon_type_1(Fs, Sz);
611polygon_type_1([_|_], _) -> polygon;
612polygon_type_1([], 3) -> triangle;
613polygon_type_1([], 4) -> quad;
614polygon_type_1(_, _) -> polygon_type.
615
616make_faces(#{type:=polylist, input:=In0, count:=FC, vcount:=VC, p:=Ps}=Data, #{input:=In1}) ->
617    In = sort_inputs(In1, In0),
618    Mat = [maps:get(material, Data, default)],
619    Fs = pick_faces(VC, In, hd(Ps), Mat, []),
620    FC = length(Fs), %% assert
621    Data#{p:=Fs, type:=polygon_type(Fs), input:=[I||{_,I}<-In]};
622make_faces(#{type:=triangles, input:=In0, count:=FC, p:=Ps}=Data, #{input:=In1}) ->
623    In = sort_inputs(In1, In0),
624    Mat = [maps:get(material, Data, default)],
625    Fs = pick_faces(3, In, hd(Ps), Mat, []),
626    FC = length(Fs), %% assert
627    Data#{p:=Fs, type:=triangle, input:=[I||{_,I}<-In]};
628make_faces(#{type:=tristrips, input:=In0, p:=Ps}=Data, #{input:=In1}) ->
629    In = sort_inputs(In1, In0),
630    Mat = [maps:get(material, Data, default)],
631    Fs = pick_tristrips(In, true, Ps, Mat, []),
632    Data#{p:=Fs, type:=triangle, input:=[I||{_,I}<-In]};
633make_faces(#{type:=polygons, count:=FC, input:=In0, p:=Ps}=Data, #{input:=In1}) ->
634    In = sort_inputs(In1, In0),
635    Mat = [maps:get(material, Data, default)],
636    Fs = pick_polygons(Ps, In, Mat, []),
637    FC = length(Fs),
638    Data#{p:=Fs, type:=polygon_type(Fs), input:=[I||{_,I}<-In]}.
639
640sort_inputs(GIn0, PIn0) ->
641    PIn1 = [{Sem, Map} || #{semantic:=Sem}=Map <- PIn0],
642    In = case lists:keytake("VERTEX", 1, PIn1) of
643	     false -> [{maps:get(offset,Map,0),Map} || Map <- GIn0];
644	     {value, {_, #{offset:=Offset}}, PIn2} ->
645		 PIn = [Map || {_,Map} <- PIn2],
646		 GIn = [Map#{offset=>Offset} || Map <- GIn0],
647		 [{maps:get(offset,Map,0),Map} || Map <- GIn++PIn]
648	 end,
649    remove_duplicates(lists:sort(In)).
650
651%% Wings can only handle one set of UV's coords vertex colors and so on
652remove_duplicates([{_, #{semantic:=Sem}}=First|In]) ->
653    Rest = lists:map(fun({OS, #{semantic:=S}=M}) when S =:= Sem ->
654			     {OS, M#{semantic:="IGNORE_"++S}};
655			(M) -> M
656		     end, In),
657    [First | remove_duplicates(Rest)];
658remove_duplicates([]) -> [].
659
660%%
661pick_faces(VC, _, [], _, Fs) ->
662    true = (VC =:= []) orelse is_number(VC), %% assert
663    lists:reverse(Fs);
664pick_faces([VC|N], In, Ps0, Mat, Fs) ->
665    {Face, Ps} = pick_verts(VC, In, Ps0, #e3d_face{mat=Mat}),
666    pick_faces(N, In, Ps, Mat, [rev_face(Face)|Fs]);
667pick_faces(VC, In, Ps0, Mat, Fs) when is_integer(VC) ->
668    %% io:format("~p ~p~n", [VC, length(Ps0)]),
669    {Face, Ps} = pick_verts(VC, In, Ps0, #e3d_face{mat=Mat}),
670    pick_faces(VC, In, Ps, Mat, [rev_face(Face)|Fs]).
671
672%%
673pick_tristrips(In, Rev, [Ps|Next], Mat, Fs) ->
674    {FI0, Ps1} = pick_vert(0, In, Ps, #e3d_face{mat=Mat}),
675    case pick_verts(2, In, Ps1, FI0) of
676        {Face, []} ->
677            pick_tristrips(In, true, Next, Mat, [rev_face(Rev,Face)|Fs]);
678        {Face, _} ->
679            pick_tristrips(In, not Rev, [Ps1|Next], Mat, [rev_face(Rev,Face)|Fs])
680    end;
681pick_tristrips(_In, _, [], _, Fs) ->
682    Fs.
683
684%%
685pick_polygons([Poly|Ps], In, Mat, Fs) ->
686    Face = pick_polygon(In, Poly, #e3d_face{mat=Mat}),
687    pick_polygons(Ps, In, Mat, [Face|Fs]);
688pick_polygons([], _In, _Mat, Fs) -> Fs.
689
690pick_polygon(_In, [], Face) ->
691    rev_face(Face);
692pick_polygon(In, Ps0, Face0) ->
693    {Face, Ps} = pick_vert(0, In, Ps0, Face0),
694    pick_polygon(In, Ps, Face).
695
696%%
697pick_verts(VC, In, Ps0, FI0) when VC > 0 ->
698    {FI, Ps} = pick_vert(0, In, Ps0, FI0),
699    pick_verts(VC-1, In, Ps, FI);
700pick_verts(0, _, Ps, Face) ->
701    {Face, Ps}.
702
703pick_vert(Offset, [{Offset,What}|In], [P|_]=Ps, FI0) ->
704    FI = add_vert_info(What, P, FI0),
705    pick_vert(Offset, In, Ps, FI);
706pick_vert(_, [], [_|Ps], FI) -> {FI, Ps};
707pick_vert(Offset, [{Next,_}|_]=In, [_P|Ps], FI) when Offset < Next ->
708    pick_vert(Offset+1, In, Ps, FI).
709
710add_vert_info(#{semantic:="POSITION"}, Indx, F=#e3d_face{vs=Is}) ->
711    F#e3d_face{vs=[Indx|Is]};
712add_vert_info(#{semantic:="NORMAL"}, Indx, F=#e3d_face{ns=Is}) ->
713    F#e3d_face{ns=[Indx|Is]};
714add_vert_info(#{semantic:="COLOR"}, Indx, F=#e3d_face{vc=Is}) ->
715    F#e3d_face{vc=[Indx|Is]};
716add_vert_info(#{semantic:="TEXCOORD"}, Indx, F=#e3d_face{tx=Is}) ->
717    F#e3d_face{tx=[Indx|Is]};
718add_vert_info(_In, _Indx, F) ->
719    %% Ignore stuff we don't know about
720    %% io:format("Ignore: ~p~n", [maps:get(semantic, _In)]),
721    F.
722
723rev_face(false, Face) -> Face;
724rev_face(true, Face) -> rev_face(Face).
725
726rev_face(#e3d_face{vs=Vs,vc=Vc,tx=Tx,ns=Ns,mat=Mat}) ->
727    #e3d_face{vs=lists:reverse(Vs),vc=lists:reverse(Vc),
728              tx=lists:reverse(Tx),ns=lists:reverse(Ns),
729	      mat=Mat}.
730
731make_materials(#mat{refs=Refs, defs=Defs, images=Imgs}=_Mat) ->
732    %% io:format("Scene: ~p ~n", [_Scene]),
733    [{Id,make_material(Ref, Defs, Imgs)} || #{id:=Id}=Ref <- maps:values(Refs)].
734
735make_material(#{id:=Id, instance_effect:=[$#|Effect]}=Ref, Defs, Imgs) ->
736    Name = maps:get(name, Ref, Id),
737    Def  = maps:get(Effect, Defs),
738    Shininess = case prop_get(shininess, Def, 0.0) of
739		    Shin when Shin > 1.0 -> %% Assume OpenGL where 128 is max
740			Shin / 128;
741		    Shin -> Shin
742		end,
743    DefList = [{diffuse,prop_get(diffuse, Def, {1.0, 1.0, 1.0, 1.0})},
744	       {ambient,prop_get(ambient, Def, {1.0, 1.0, 1.0, 1.0})},
745	       {specular,prop_get(specular, Def, {0.0,0.0,0.0,0.0})},
746	       {emission,prop_get(emission, Def, {0.0,0.0,0.0,0.0})},
747	       {shininess, Shininess},
748	       {vertex_colors,multiply}],
749    Textures = case maps:get(diffuse, Def, false) of
750		   #{texture:=ImageRef} ->
751		       make_maps(ImageRef, Def, Imgs);
752		   _ -> []
753	       end,
754    %% io:format("Name: ~p Maps ~p~n", [Name, Textures]),
755    {atom_name(Name), [{opengl, DefList}|Textures]}.
756
757prop_get(What, Def, Default) ->
758    case maps:get(What, Def, undefined) of
759	{_,_,_,_}=Col -> Col;
760	Float when is_float(Float) -> Float;
761	_Other ->
762	    %% io:format("Ignored color ~p:~n",[_Other]),
763	    Default
764    end.
765
766make_maps(ImageRef, Def, Images) ->
767    case make_maps_1(ImageRef, Def, Images) of
768	undefined -> [];
769	File -> [{maps, [{diffuse, File}]}]
770    end.
771
772make_maps_1(ImageRef, Def, Imgs) ->
773    case maps:get(ImageRef, Def, undefined) of
774	undefined ->
775	    case maps:get(ImageRef, Imgs, undefined) of
776		#{file:=File} -> File;
777		_Fail -> undefined
778	    end;
779	#{"source":=Source} ->
780	    make_maps_1(Source, Def, Imgs);
781	#{"init_from":=FileRef} ->
782	    make_maps_1(FileRef, Def, Imgs)
783    end.
784
785make_file(#es{materials=Mat, scene=#{nodes:=Nodes}, mesh=Mesh0}) ->
786    MatList0 = make_materials(Mat),
787    MatMap = maps:from_list(MatList0),
788    MeshMap = maps:from_list(Mesh0),
789    MeshL = build_meshes(Nodes, MeshMap, MatMap, []),
790    #e3d_file{objs=MeshL, mat=maps:values(MatMap)}.
791
792build_meshes([#{geom:=Gs, matrix:=MatrixInfo}=Node|Ns],
793	     MeshMap, MatMap, Acc) ->
794    AddGeomFs = fun(#{geom:=[$#|GsId], mats:=Mats0}, Mesh0) ->
795                        #{GsId:=#e3d_mesh{fs=Fs0}=Mesh1} = MeshMap,
796                        Mats = maps:map(fun(_K,[$#|Ref]) ->
797                                                {MId,_} = maps:get(Ref, MatMap, {default, ignore}),
798                                                MId
799                                        end, Mats0),
800                        Translate = fun(#e3d_face{mat=[default]}=F) ->
801                                            F;
802                                       (#e3d_face{mat=[MId]}=F) ->
803                                            F#e3d_face{mat=[maps:get(MId, Mats)]}
804                                    end,
805                        Fs = [Translate(F) || F <- Fs0],
806                        merge_mesh(Mesh1#e3d_mesh{fs=Fs}, Mesh0)
807                end,
808    Mesh = lists:foldl(AddGeomFs, undefined, Gs),
809    Name = maps:get(name, Node, "unknown"),
810    Matrix = lists:foldl(fun make_matrix/2, e3d_mat:identity(), MatrixInfo),
811    Obj = #e3d_object{name=Name, obj=Mesh#e3d_mesh{matrix=Matrix}},
812    build_meshes(Ns, MeshMap, MatMap, [Obj|Acc]);
813build_meshes([], _, _, Acc) -> Acc.
814
815merge_mesh(Mesh, undefined) ->
816    Mesh;
817merge_mesh(#e3d_mesh{type=Ty1, vs=Vs1, vc=Vc1, tx=Tx1, ns=Ns1, fs=Fs1},
818           #e3d_mesh{type=Ty0, vs=Vs0, vc=Vc0, tx=Tx0, ns=Ns0, fs=Fs0}) ->
819    Vs = Vs0++Vs1,
820    Vc = Vc0++Vc1,
821    Tx = Tx0++Tx1,
822    Ns = Ns0++Ns1,
823    Fs = renumber_face(Fs1,length(Vs0),length(Vc0),length(Tx0),length(Ns0), Fs0),
824    #e3d_mesh{type=mesh_type(Ty0,Ty1),
825              vs=Vs, vc=Vc, tx=Tx, ns=Ns, fs=Fs}.
826
827renumber_face([#e3d_face{vs=Vs0, vc=Vc0, tx=Tx0, ns=Ns0}=F0|Fs], NVs, NVc, NTx, NNs, Acc) ->
828    F = F0#e3d_face{vs = [Id+NVs || Id <- Vs0],
829                    vc = [Id+NVc || Id <- Vc0],
830                    tx = [Id+NTx || Id <- Tx0],
831                    ns = [Id+NNs || Id <- Ns0]},
832    renumber_face(Fs, NVs, NVc, NTx, NNs, [F|Acc]);
833renumber_face([], _NVs, _NVc, _NTx, _NNs, Acc) ->
834    Acc.
835
836make_matrix({rotate, [_,_,_,0.0]}, M) ->
837    M;
838make_matrix({rotate, [X,Y,Z,Rad]}, M) ->
839    Deg = Rad*180.0/math:pi(),
840    e3d_mat:mul(e3d_mat:rotate(Deg, {X,Y,Z}), M);
841make_matrix({translate, [0.0,0.0,0.0]}, M) ->
842    M;
843make_matrix({translate, [X,Y,Z]}, M) ->
844    e3d_mat:mul(e3d_mat:translate(X,Y,Z), M);
845make_matrix({scale, [1.0,1.0,1.0]}, M) ->
846    M;
847make_matrix({scale, [X,Y,Z]}, M) ->
848    e3d_mat:mul(e3d_mat:scale(X,Y,Z), M);
849make_matrix({matrix, Vs}, M) ->
850    MT = list_to_tuple(Vs),
851    e3d_mat:mul(e3d_mat:transpose(MT), M).
852
853
854-ifdef(TEST).
855test() ->
856    Dir = case os:type() of
857	      {unix, darwin} -> "/Users/dgud/Dropbox/src/Collada/examples";
858	      {win32, _} -> "c:/Users/familjen/Dropbox/src/Collada/examples";
859	      _ -> "foo"
860	  end,
861    Files = [
862	     "wings-box-2mat.dae"
863	     ,"AsXML.xml"
864             ,"COLLADA.dae"
865             ,"COLLADA_triangulate.dae"
866             ,"Cinema4D.dae"
867             ,"ConcavePolygon.dae"
868	     ,"anims_with_full_rotations_between_keys.DAE"
869	     ,"cameras.dae"
870             ,"cube_UTF16LE.dae"
871             ,"cube_UTF8BOM.dae"
872             ,"cube_emptyTags.dae"
873             ,"cube_triangulate.dae"
874             ,"cube_tristrips.dae"
875             ,"cube_with_2UVs.DAE"
876             ,"cube_xmlspecialchars.dae"
877             ,"duck.dae"
878             ,"duck_triangulate.dae"
879             ,"earthCylindrical.DAE"
880             ,"kwxport_test_vcolors.dae"
881             ,"library_animation_clips.dae"
882             ,"lights.dae"
883             ,"regr01.dae"
884             ,"sphere.dae"
885             ,"sphere_triangulate.dae"
886             ,"teapot_instancenodes.DAE"
887             ,"teapots.DAE"
888	     ,"wings.dae"
889             %% From Sketchup
890             ,"cube.dae"
891             ,"box_group.dae"
892             ,"grouped_objects.dae"
893             ,"exploded_group.dae"
894	    ],
895    Imports = [import(filename:join(Dir, File)) || File <- Files],
896    %% [io:format("~P~n",[Scene,20]) || Scene <- Imports],
897    io:format("~P~n",[hd(Imports), 20]),
898    ok.
899-endif.
900
901%%%%
902%%%%   Provisory conversion from UTF16/32 - it make possible load a file
903%%%%   which materials name/id uses unicode
904%%%%   reported in: http://www.wings3d.com/forum/showthread.php?tid=2321
905atom_name(Name) ->
906    try list_to_atom(Name) of
907        Atom -> Atom
908    catch
909        error:_ ->
910            Name0 = unicode:characters_to_binary(Name),
911            binary_to_atom(Name0,latin1)
912    end.
913