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