%% %% wings_ff_ndo.erl -- %% %% Import of Nendo .ndo files. %% %% Copyright (c) 2001-2004 Bjorn Gustavsson %% %% See the file "license.terms" for information on usage and redistribution %% of this file, and for a DISCLAIMER OF ALL WARRANTIES. %% %% $Id: wings_ff_ndo.erl,v 1.24 2004/12/18 19:36:20 bjorng Exp $ %% -module(wings_ff_ndo). -export([import/2,export/2]). -include("wings.hrl"). -import(lists, [sort/1,reverse/1,foldl/3,last/1,foreach/2]). -define(NDO_HEADER10, "nendo 1.0"). -define(NDO_HEADER11, "nendo 1.1"). import(Name, St) -> case file:read_file(Name) of {ok,<>} -> {error,?__(1,"Nendo 1.0 files not supported")}; {ok,<>} -> import_1(Data, St); {ok,_Bin} -> {error,?__(2,"not a Nendo file")}; {error,Reason} -> {error,file:format_error(Reason)} end. import_1(<<_:8,NumObjs:16,_:8,Objs/binary>>, St) -> io:format(?__(1,"~w object(s)\n"), [NumObjs]), read_objects(NumObjs, Objs, St). read_objects(0, _, St) -> St; read_objects(N, <<>>, St) -> io:format(?__(1," ~w empty object(s)\n"), [N]), St; read_objects(N, Bin, St0) -> case read_object(Bin) of bad -> <> = Bin, io:format(?__(2," garbage: ~w\n"), [First]), St0; {Name,We,Rest} -> St = wings_shape:new(Name, We, St0), read_objects(N-1, Rest, St) end. read_object(Bin) -> %%show_first(Bin), read_object_0(Bin). read_object_0(<<0:8,_/binary>>=T) -> read_object_1(T); read_object_0(<<_:8,T/binary>>) -> read_object_1(T). read_object_1(<<0:16,T/binary>>) -> read_object(T); read_object_1(<<1:16,C:8,_/binary>>=T0) when C < $\s; C > $~ -> <<_:16,T/binary>> = T0, read_object_1(T); read_object_1(<>) -> case get_name(L, T0) of bad -> bad; {Name,T1} -> <> = T1, io:format(?__(1,"~w: ~s: vis=~p sensitivity=~p\n"), [L,Name,Vis,Sensivity]), Perm = case {Vis,Sensivity} of {1,1} -> 0; %Visible, unlocked {1,0} -> 1; %Visible, locked {0,1} -> 2; %Hidden, unlocked {0,0} -> 3 %Hidden, locked end, {Etab,Htab,T3} = read_edges(T2), T4 = skip_faces(T3), {Vtab,T5} = read_vertices(T4), T = skip_rest(T5), We0 = #we{mode=vertex,es=Etab,vp=Vtab,he=Htab,perm=Perm}, We1 = wings_we:rebuild(We0), We = clean_bad_edges(We1), {Name,We,T} end. get_name(L, Bin) when size(Bin) < L -> bad; get_name(L, Bin) -> <> = Bin, Name = binary_to_list(Name0), foldl(fun(C, Val) when $\s =< C, C < 127 -> Val; (_C, _) -> bad end, {Name,T}, Name). skip_rest(<>) -> Sz = 2*Sz0, <<_Skip:Sz/binary,T/binary>> = T0, %%io:format(" skipping ~w: ~w\n", [Szip,Sk]), skip_rest_1(T). skip_rest_1(<>) -> Sz = 2*Sz0, <<_Skip:Sz/binary,T/binary>> = T0, %% io:format(" skipping ~w: ~w\n", [Sz,Skip]), %% show_first(T), skip_rest_2(T). skip_rest_2(<<0:8,T/binary>>) -> T; skip_rest_2(<<2:8,Sz1:16,Sz2:16,T/binary>>) -> Sz = Sz1 * Sz2, skip_texture(Sz, T); skip_rest_2(<<4:8,Sz1:16,Sz2:16,T/binary>>) -> Sz = Sz1 * Sz2, skip_texture(Sz, T). skip_texture(0, T) -> T; skip_texture(N, <>) when N > 0 -> skip_texture(N-Pixels, T). read_edges(<>) -> io:format(?__(1," edges ~w\n"), [NumEdges]), read_edges(0, NumEdges, T, [], []). read_edges(N, N, T, Eacc, Hacc) -> Etab = gb_trees:from_orddict(reverse(Eacc)), Htab = gb_sets:from_ordset(reverse(Hacc)), {Etab,Htab,T}; read_edges(Edge, N, <>, Eacc, Hacc0) -> <> = EdgeRec0, AColor = convert_color(AColor0), BColor = convert_color(BColor0), EdgeRec = {Edge,#edge{vs=Va,ve=Vb,a=AColor,b=BColor,lf=Lf,rf=Rf, ltpr=Ltpr,ltsu=Ltsu,rtpr=Rtpr,rtsu=Rtsu}}, Hacc = if Hardness == 0 -> Hacc0; true -> [Edge|Hacc0] end, read_edges(Edge+1, N, T, [EdgeRec|Eacc], Hacc). skip_faces(<>) -> io:format(?__(1," faces ~w\n"), [NumFaces]), Skip = 2*NumFaces, <<_:Skip/binary,T/binary>> = T0, T. read_vertices(<>) -> io:format(?__(1," vertices ~w\n"), [NumVertices]), read_vertices(0, NumVertices, T, []). read_vertices(N, N, T, Acc) -> {gb_trees:from_orddict(reverse(Acc)),T}; read_vertices(V, N, <<_:16,X:32/float,Y:32/float,Z:32/float,T/binary>>, Acc) -> Pos = wings_util:share(X/10.0, Y/10.0, Z/10.0), read_vertices(V+1, N, T, [{V,Pos}|Acc]). clean_bad_edges(#we{es=Etab}=We) -> clean_bad_edges(gb_trees:keys(Etab), We). clean_bad_edges([Edge|T], #we{es=Etab}=We0) -> We = case gb_trees:lookup(Edge, Etab) of none -> We0; {value,#edge{ltpr=Same,ltsu=Same,rtpr=Same,rtsu=Same}} -> io:format(?__(1,"Bad edge: ~w\n"), [Edge]), We0; {value,#edge{ltpr=Same,ltsu=Same}} -> wings_edge:dissolve_edge(Edge, We0); {value,#edge{rtpr=Same,rtsu=Same}} -> wings_edge:dissolve_edge(Edge, We0); {value,_Other} -> We0 end, clean_bad_edges(T, We); clean_bad_edges([], We) -> We. %% %% Export. %% export(Name, #st{shapes=Shapes0}=St) -> Shapes1 = gb_trees:values(Shapes0), foreach(fun check_size/1, Shapes1), Shapes2 = foldl(fun(Sh, A) -> shape(Sh, St, A) end, [], Shapes1), Shapes = reverse(Shapes2), write_file(Name, Shapes). check_size(#we{name=Name,es=Etab}) -> case gb_trees:size(Etab) of Sz when Sz > 65535 -> wings_u:error(?__(1,"Object \"") ++Name ++?__(2,"\" cannot be exported ") ++?__(3,"to Nendo format (too many edges).")); _ -> ok end. shape(We, _, Acc) when ?IS_LIGHT(We) -> Acc; shape(#we{name=Name,perm=Perm}=We0, St, Acc) -> NameChunk = [<<(length(Name)):16>>|Name], Vis = if ?IS_VISIBLE(Perm) -> 1; true -> 0 end, Sense = if Perm =:= 1; Perm =:= 3 -> 0; true -> 1 end, Shaded = 1, EnableColors = 1, Header = <>, We1 = wings_we:uv_to_color(We0, St), We = wings_we:renumber(We1, 0), #we{vc=Vct,vp=Vtab,es=Etab,fs=Ftab,he=Htab} = We, EdgeChunk = write_edges(gb_trees:to_list(Etab), Htab, []), FaceChunk = write_faces(gb_trees:values(Ftab), []), VertexChunk = write_vertices(gb_trees:values(Vct), gb_trees:values(Vtab), []), FillChunk = [0,0,0,0,0,1], [[NameChunk,Header,EdgeChunk,FaceChunk,VertexChunk,FillChunk]|Acc]. write_edges([{Edge,Erec0}|Es], Htab, Acc) -> Hardness = case gb_sets:is_member(Edge, Htab) of false -> 0; true -> 1 end, #edge{vs=Va,ve=Vb,a=ACol,b=BCol,lf=Lf,rf=Rf, ltpr=Ltpr,ltsu=Ltsu,rtpr=Rtpr,rtsu=Rtsu} = Erec0, Erec = [<>,convert_color(BCol)|convert_color(ACol)], write_edges(Es, Htab, [Erec|Acc]); write_edges([], _Htab, Acc) -> list_to_binary([<<(length(Acc)):16>>|reverse(Acc)]). write_faces([Edge|Fs], Acc) -> write_faces(Fs, [<>|Acc]); write_faces([], Acc) -> list_to_binary([<<(length(Acc)):16>>|reverse(Acc)]). write_vertices([Edge|Es], [{X,Y,Z}|Ps], Acc) -> Vtx = <>, write_vertices(Es, Ps, [Vtx|Acc]); write_vertices([], [], Acc) -> list_to_binary([<<(length(Acc)):16>>|reverse(Acc)]). write_file(Name, Objects) -> NumObjs = length(Objects), Data = [<>|Objects], case file:write_file(Name, Data) of ok -> ok; {error,Reason} -> {error,file:format_error(Reason)} end. %%% %%% Common utilities. %%% convert_color(<>) -> wings_color:store({R/255,G/255,B/255}); convert_color({R,G,B}) -> [trunc(R*255),trunc(G*255),trunc(B*255),255]; convert_color(none) -> [255,255,255,255].