diff --git a/src/wings_import.erl b/src/wings_import.erl index 478f8a93..de4947a0 100644 --- a/src/wings_import.erl +++ b/src/wings_import.erl @@ -70,8 +70,7 @@ import_object(#e3d_object{name=_Name,obj=Mesh0}) -> %%io:format("\n~s:\n", [_Name]), Mesh1 = e3d_mesh:merge_vertices(Mesh0), Mesh2 = e3d_mesh:clean_faces(Mesh1), - Mesh3 = e3d_mesh:transform(Mesh2), - Mesh = e3d_mesh:hard_edges_from_normals(Mesh3), + Mesh = e3d_mesh:transform(Mesh2), import_mesh(material, Mesh). -define(P(N), {N,fun N/2}). diff --git a/src/wings_we.erl b/src/wings_we.erl index d478e95e..191484ba 100644 --- a/src/wings_we.erl +++ b/src/wings_we.erl @@ -57,9 +57,9 @@ %% build() -> We' %% Create a we from faces and vertices or a mesh. -build(Mode, #e3d_mesh{fs=Fs0,vs=Vs,vc=Vc0,tx=Tx,he=He}) when is_atom(Mode) -> +build(Mode, #e3d_mesh{fs=Fs0,vs=Vs,vc=Vc0,tx=Tx,he=He, ns=NTab}) when is_atom(Mode) -> Vc = supress_alpha(Vc0), - Fs = translate_faces(Fs0, list_to_tuple(Vc), list_to_tuple(Tx), []), + Fs = translate_faces(Fs0, list_to_tuple(Vc), list_to_tuple(Tx), list_to_tuple(NTab), []), wings_we_build:we(Fs, Vs, He); build(Fs, Vs) -> wings_we_build:we(Fs, Vs, []). @@ -542,23 +542,24 @@ show_faces_1(Faces, #we{es=Etab0}=We0) -> %%% Build Winged-Edges. %%% -translate_faces([#e3d_face{vs=Vs,vc=Vc0,tx=Tx0,mat=Mat0}|Fs], Vcs, Txs, Acc) -> +translate_faces([#e3d_face{vs=Vs,vc=Vc0,tx=Tx0,ns=Ns0,mat=Mat0}|Fs], Vcs, Txs, NTab, Acc) -> Mat = translate_mat(Mat0), - FaceData = case {Tx0,Vc0} of - {[],[]} -> {Mat,Vs}; - {Tx1,Vc1} -> - Tx = if Tx1 =/= [] -> - [element(Tx+1, Txs) || Tx <- Tx1]; - true -> none + FaceData = case {Tx0,Vc0,Ns0} of + {[],[],[]} -> {Mat,Vs}; + {Tx1,Vc1,Ns1} -> + Tx = if Tx1 =/= [] -> [element(Tx+1, Txs) || Tx <- Tx1]; + true -> none end, - Vc = if Vc1 =/= [] -> - [element(Vc+1, Vcs) || Vc <- Vc1]; - true -> none + Vc = if Vc1 =/= [] -> [element(Vc+1, Vcs) || Vc <- Vc1]; + true -> none end, - {Mat,Vs,Tx,Vc} + Ns = if Ns1 =/= [] -> [element(Ni+1, NTab) || Ni <- Ns1]; + true -> none + end, + {Mat,Vs,Tx,Vc,Ns} end, - translate_faces(Fs, Vcs, Txs, [FaceData|Acc]); -translate_faces([], _, _, Acc) -> reverse(Acc). + translate_faces(Fs, Vcs, Txs, NTab, [FaceData|Acc]); +translate_faces([], _, _, _, Acc) -> reverse(Acc). translate_mat([]) -> default; translate_mat([Mat]) -> Mat; diff --git a/src/wings_we_build.erl b/src/wings_we_build.erl index 63506b28..6b17c352 100644 --- a/src/wings_we_build.erl +++ b/src/wings_we_build.erl @@ -85,8 +85,7 @@ build_and_fix_holes(_, _) -> throw(bad_model). build_rest(Es, Fs, Vs, HardEdges) -> - Htab = vpairs_to_edges(HardEdges, Es), - {Vct0,Etab,Ftab0,UvVcTab} = build_tables(Es), + {Vct0,Etab,Ftab0,UvVcTab,VNTab} = build_tables(Es), Ftab = build_faces(Ftab0), Vct = array:from_orddict(incident_tab(Vct0)), Vpos = number_vertices(Vs, 0, []), @@ -98,8 +97,13 @@ build_rest(Es, Fs, Vs, HardEdges) -> %% that the greatest key is found in the edge table. wings_util:array_greatest_key(Etab)+1 end, - We0 = #we{next_id=NextId,es=Etab,fs=Ftab,vc=Vct,vp=Vpos,he=Htab}, - We = wings_va:set_edge_attrs(UvVcTab, We0), + HTab = vpairs_to_edges(HardEdges, Es), + We0 = #we{next_id=NextId,es=Etab,fs=Ftab,vc=Vct,vp=Vpos,he=HTab}, + We1 = wings_va:set_edge_attrs(UvVcTab, We0), + We = case HardEdges =:= [] andalso VNTab =/= [] of + false -> We1; + true -> calc_hard_edges(VNTab, We1) + end, assign_materials(Fs, We). assign_materials([L|_], We) when is_list(L) -> We; @@ -133,32 +137,27 @@ build_edges(Fs) -> build_half_edges(Fs) -> build_half_edges(Fs, 0, []). -build_half_edges([{_Material,Vs,none,Vc}|Fs], Face, Eacc0) -> % imported with vertex color only - build_half_edges_1(Vs, tx_filler(Vs), Vc, Fs, Face, Eacc0); -build_half_edges([{_Material,Vs,Tx,none}|Fs], Face, Eacc0) -> % imported with textures only - build_half_edges_1(Vs, Tx, tx_filler(Vs), Fs, Face, Eacc0); -build_half_edges([{_Material,Vs,Tx,Vc}|Fs], Face, Eacc0) -> % imported with textures and vertex color - build_half_edges_1(Vs, Tx, Vc, Fs, Face, Eacc0); +build_half_edges([{_Material,Vs,Tx,Vc,Ns}|Fs], Face, Eacc0) -> % imported with textures and vertex color + build_half_edges_1(Vs, Tx, Vc, Ns, Fs, Face, Eacc0); build_half_edges([{_Material,Vs}|Fs], Face, Eacc0) -> % imported without textures or vertex color - build_half_edges_1(Vs, tx_filler(Vs), tx_filler(Vs), Fs, Face, Eacc0); + build_half_edges_1(Vs, none, none, none, Fs, Face, Eacc0); build_half_edges([Vs|Fs], Face, Eacc0) -> % new primitives - build_half_edges_1(Vs, tx_filler(Vs), tx_filler(Vs), Fs, Face, Eacc0); + build_half_edges_1(Vs, none, none, none, Fs, Face, Eacc0); build_half_edges([], _Face, HalfEdges) -> HalfEdges. -build_half_edges_1(Vs, UVs, VCs, Fs, Face, Acc0) -> - UvsVcs = zip(UVs,VCs), - Vuvs = zip(Vs, UvsVcs), +build_half_edges_1(Vs, UVs, VCs, Ns, Fs, Face, Acc0) -> + Vuvs = zip4(Vs, UVs, VCs, Ns), Pairs = pairs(Vuvs), Acc = build_face_edges(Pairs, Face, Acc0), build_half_edges(Fs, Face+1, Acc). -build_face_edges([{Pred,_}|[{E0,{{_UVa,_VCa},{UVb,VCb}}},{Succ,_}|_]=Es], Face, Acc0) -> +build_face_edges([{Pred,_}|[{E0,{{_,_,_},{_UVb,_VCb,_Nb}=VtxInfo}},{Succ,_}|_]=Es], Face, Acc0) -> Acc = case E0 of {Vs,Ve}=Name when Vs < Ve -> - enter_half_edge(right, Name, Face, Pred, Succ, {UVb,VCb}, Acc0); + enter_half_edge(right, Name, Face, Pred, Succ, VtxInfo, Acc0); {Vs,Ve} when Ve < Vs -> Name = {Ve,Vs}, - enter_half_edge(left, Name, Face, Pred, Succ, {UVb,VCb}, Acc0) + enter_half_edge(left, Name, Face, Pred, Succ, VtxInfo, Acc0) end, build_face_edges(Es, Face, Acc); build_face_edges([_,_], _Face, Acc) -> Acc. @@ -167,6 +166,14 @@ enter_half_edge(Side, Name, Face, Pred, Succ, UVVC,Tab0) -> Rec = {Face,UVVC,edge_name(Pred),edge_name(Succ)}, [{Name,{Side,Rec}}|Tab0]. +zip4([V|Vs], UVs0, VCs0, Ns0) -> + {Uv, UVs} = tx_filler(UVs0), + {Vc, VCs} = tx_filler(VCs0), + {N, Ns} = tx_filler(Ns0), + [{V, {Uv, Vc, N}} | zip4(Vs, UVs, VCs, Ns)]; +zip4([], _, _, _) -> + []. + pairs(Vs) -> pairs(Vs, Vs, []). @@ -178,12 +185,8 @@ pairs([{V,T}], [{V1,T1},{V2,T2},{V3,T3}|_], Acc) -> edge_name({Vs,Ve}=Name) when Vs < Ve -> Name; edge_name({Vs,Ve}) -> {Ve,Vs}. -tx_filler(Vs) -> - tx_filler(Vs, none, []). - -tx_filler([_|Vs], Col, Acc) -> - tx_filler(Vs, Col, [Col|Acc]); -tx_filler([], _Col, Acc) -> Acc. +tx_filler(none) -> {none, none}; +tx_filler([H|TL]) -> {H,TL}. combine_half_edges(HalfEdges) -> combine_half_edges(HalfEdges, [], []). @@ -213,12 +216,12 @@ vpairs_to_edges(HardNames0, Es) -> build_tables(Edges) -> Emap = make_edge_map(Edges), - build_tables(Edges, Emap, [], [], [], []). + build_tables(Edges, Emap, [], [], [], [], []). -build_tables([H|T], Emap, Vtab0, Etab0, Ftab0, UvVcTab0) -> +build_tables([H|T], Emap, Vtab0, Etab0, Ftab0, UvVcTab0, VNTab0) -> {{Vs,Ve},{Edge,{Ldata,Rdata}}} = H, - {Lf,{LUV,LVC},Lpred,Lsucc} = Ldata, - {Rf,{RUV,RVC},Rpred,Rsucc} = Rdata, + {Lf,{LUV,LVC,LN},Lpred,Lsucc} = Ldata, + {Rf,{RUV,RVC,RN},Rpred,Rsucc} = Rdata, Erec = #edge{vs=Vs,ve=Ve,lf=Lf,rf=Rf, ltpr=edge_num(Lf, Lpred, Emap), ltsu=edge_num(Lf, Lsucc, Emap), @@ -228,13 +231,17 @@ build_tables([H|T], Emap, Vtab0, Etab0, Ftab0, UvVcTab0) -> Ftab = [{Lf,Edge},{Rf,Edge}|Ftab0], Vtab = [{Vs,Edge},{Ve,Edge}|Vtab0], UvVcTab = case {LUV,RUV,LVC,RVC} of - {none,none,none,none} -> UvVcTab0; - {_,_,_,_} -> [{Edge,LUV,RUV,LVC,RVC}|UvVcTab0] - end, - build_tables(T, Emap, Vtab, Etab, Ftab, UvVcTab); -build_tables([], _Emap, Vtab, Etab0, Ftab, UvVcTab) -> + {none,none,none,none} -> UvVcTab0; + {_,_,_,_} -> [{Edge,LUV,RUV,LVC,RVC}|UvVcTab0] + end, + VNTab = case {LN,RN} of + {none, none} -> VNTab0; + {_,_} -> [{Edge,{LN,RN}}|VNTab0] + end, + build_tables(T, Emap, Vtab, Etab, Ftab, UvVcTab, VNTab); +build_tables([], _Emap, Vtab, Etab0, Ftab, UvVcTab, VNTab) -> Etab = array:from_orddict(reverse(Etab0)), - {Vtab,Etab,Ftab,UvVcTab}. + {Vtab,Etab,Ftab,UvVcTab,VNTab}. make_edge_map(Es) -> make_edge_map(Es, []). @@ -386,9 +393,9 @@ elim_vtx_map([], _, Acc) -> elim_renum_vs([{Mat,Vs0}|Faces], [{Face,DupVs}|ToDo], Face, VtxMap, Acc) -> Vs = elim_renum_vs_1(Vs0, DupVs, VtxMap), elim_renum_vs(Faces, ToDo, Face+1, VtxMap, [{Mat,Vs}|Acc]); -elim_renum_vs([{Mat,Vs0,Tx,Vc}|Faces], [{Face,DupVs}|ToDo], Face, VtxMap, Acc) -> +elim_renum_vs([{Mat,Vs0,Tx,Vc,Ns}|Faces], [{Face,DupVs}|ToDo], Face, VtxMap, Acc) -> Vs = elim_renum_vs_1(Vs0, DupVs, VtxMap), - elim_renum_vs(Faces, ToDo, Face+1, VtxMap, [{Mat,Vs,Tx,Vc}|Acc]); + elim_renum_vs(Faces, ToDo, Face+1, VtxMap, [{Mat,Vs,Tx,Vc,Ns}|Acc]); elim_renum_vs([MatVs|Faces], ToDo, Face, VtxMap, Acc) -> elim_renum_vs(Faces, ToDo, Face+1, VtxMap, [MatVs|Acc]); elim_renum_vs([], [], _, _, Acc) -> reverse(Acc). @@ -471,3 +478,93 @@ shared_digraph_1([{_,{{Lf,_,_,_},{Rf,_,_,_}}}|Es], G) -> digraph:add_edge(G, Lf, Rf), shared_digraph_1(Es, G); shared_digraph_1([], _) -> ok. + +calc_hard_edges(EdgeNsList, #we{fs=FTab} = We) -> + FaceNs0 = lists:foldl(fun(Face, Acc) -> + [{Face, wings_face:normal(Face, We)}|Acc] + end, + [], gb_trees:keys(FTab)), + FaceNs = lists:reverse(FaceNs0), + FaceVsNs = gb_trees:from_orddict(lists:sort(wings_we:normals(FaceNs, We, none))), + + EdgeNs = array:from_orddict(lists:sort(EdgeNsList)), + {HE0,PossibleVs} = lists:foldl(fun({Face, FaceN}, Acc) -> + FaceVNs = gb_trees:get(Face, FaceVsNs), + find_hard_edges(Face, FaceN, FaceVNs, EdgeNs, Acc, We) + end, {[], []}, FaceNs), + + HE1 = filter_edges(lists:usort(HE0), gb_trees:from_orddict(FaceNs), We#we.es), + HE2 = gb_sets:from_ordset(HE1), + HE3 = find_he_from_vertex(lists:usort(PossibleVs), EdgeNs, HE2, We), + + We#we{he = HE3}. + +find_hard_edges(Face, FlatNormal, SmoothNs0, EdgeNs, {He0, Possible0}, We) -> + Fun = fun(V, Edge, E, {[SN|SmoothNs], He, Possible}) -> + {VNS, VNE} = array:get(Edge, EdgeNs), + {VN, Other} = case E of + #edge{lf = Face, vs = V, ltsu = Next} -> {VNS, Next}; + #edge{rf = Face, ve = V, rtsu = Next} -> {VNE, Next} + end, + try e3d_vec:dist_sqr(VN, FlatNormal) < ?EPSILON of + true -> + {SmoothNs, [Edge, Other|He], Possible}; + false -> + %% Compare Normals + case e3d_vec:dist_sqr(VN,SN) < ?EPSILON of + true -> {SmoothNs, He, Possible}; + false -> {SmoothNs, He, [V|Possible]} + end + catch error:function_clause -> + %% Missing vertex normal, assume face-normal or smooth? + %% Currently we make it smooth + {SmoothNs, He, Possible} + end + end, + {[], He, Poss} = wings_face:fold(Fun, {lists:reverse(SmoothNs0), He0, Possible0}, Face, We), + {He, Poss}. + +%% Vertices with normals that neither point at face normal nor smooth vertex normal +%% They will have some edges that are hard (if exported from wings at least) +find_he_from_vertex([V|Vs], EdgeNs, HeAcc0, We) -> + F = fun(Edge, _Face, E, Acc) -> + {VNS, VNE} = array:get(Edge, EdgeNs), + case E of + #edge{vs = V, lf = F1, rf = F2} -> gb_trees:insert(F1, {VNS, Edge, F2}, Acc); + #edge{ve = V, rf = F1, lf = F2} -> gb_trees:insert(F1, {VNE, Edge, F2}, Acc) + end + end, + ED = wings_vertex:fold(F, gb_trees:empty(), V, We), + {_,Info} = gb_trees:smallest(ED), + HeAcc = lists:usort(pick_edges(Info, ED, [])), + find_he_from_vertex(Vs, EdgeNs, gb_sets:union(gb_sets:from_ordset(HeAcc),HeAcc0), We); +find_he_from_vertex([], _, HeAcc, _) -> + HeAcc. + +pick_edges({N1, Edge, NextFace}, Tree0, Acc) -> + try gb_trees:take(NextFace, Tree0) of + {{N2, _, _} = Next, Tree} -> + try e3d_vec:dist_sqr(N1,N2) < ?EPSILON of + true -> pick_edges(Next, Tree, Acc); + false -> pick_edges(Next, Tree, [Edge|Acc]) + catch error:function_clause -> + %% Vertex normals assume smooth normal + pick_edges(Next, Tree, Acc) + end + catch _E:_R -> + Acc + end. + +%% Filter out edges between flat faces +filter_edges([Id|Es], FaceNs, ETab) -> + #edge{lf = LF, rf = RF} = array:get(Id, ETab), + case e3d_vec:dist_sqr(gb_trees:get(LF, FaceNs), gb_trees:get(RF, FaceNs)) of + Dist when Dist < ?EPSILON -> + filter_edges(Es, FaceNs, ETab); + _ -> + [Id|filter_edges(Es, FaceNs, ETab)] + end; +filter_edges([], _, _) -> + []. + +