1%% 2%% auv_texture.erl -- 3%% 4%% Render and capture a texture. 5%% 6%% Copyright (c) 2002-2011 Dan Gudmundsson, Bjorn Gustavsson 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 14-module(auv_texture). 15-export([get_texture/2,draw_options/1,delete_preview_vbo/0]). 16 17-define(NEED_OPENGL, 1). 18-define(NEED_ESDL, 1). 19-define(ERROR, error_msg(?LINE)). 20-include_lib("src/wings.hrl"). 21-include_lib("e3d/e3d_image.hrl"). 22-include_lib("e3d/e3d.hrl"). 23-include("auv.hrl"). 24 25-import(lists, [foreach/2,reverse/1,sort/1,foldl/3,member/2]). 26-import(auv_segment, [map_vertex/2]). 27 28-define(OPT_BG, [auv_background, {type_sel,color},{undefined,ignore},{1.0,1.0,1.0,0.0}]). 29-define(OPT_EDGES, [auv_edges, all_edges,{0.0,0.0,0.0},1.0,false]). 30-define(OPT_FACES, [texture]). 31-define(PREVIEW_SIZE, 256). 32-define(SHADER_PRW_NAME, "shdr_preview"). 33-define(SHADER_PRW_SIZE, 512). 34-define(SHADER_PRW_VBO, shdr_vbo). 35 36-record(opt, {texsz = {512,512}, %% Texture size 37 no_renderers = 4, 38 renderers = [{auv_background, ?OPT_BG}, 39 {auv_edges, [?OPT_EDGES]}] 40 }). 41 42-record(sh, {id=ignore, 43 name="Unnamed", %% Shader menu entry 44 file="", 45 vs = "", %% Vertex shader 46 fs = "", %% Fragment shader 47 tex_units = 1, %% No of texture units used 48 reqs = [], %% Requirements: normals and/or binormals 49 args = [], %% Arguments 50 def = [], %% Gui Strings 51 preview = true %% Shows or not the preview dialog for the shader 52 }). 53 54-record(sh_conf, {texsz, % More shader options 55 fbo_r, % Fbo read buffer 56 fbo_w, % Fbo write buffer 57 fbo_d, % Clean up fbo 58 prog, % Shader Id 59 ts}). % Shader data 60 61-record(ts, % What Type 62 {charts, % #chart{} (list) 63 uv, % UV positions (binary) 64 pos, % Real 3D position (binary) 65 n, % Normal (binary) Optional 66 bi, % BiNormal (binary) Optional 67 bb, % BoundingBox 3D pos 68 vc, % Vertex colors (binary) 69 vbo % Vbo (binary) 70 }). 71 72-record(chart, 73 {id, % Chart ID 74 fs, % Faces see [{Face,#fs{}}] 75 oes=[], % Outer vertices [[va,vb,face],[vc,vb,face]..], 76 bb_uv, % Uv Bounding Box {{minX,minY,0},{maxX,maxY,0}} 77 mode % Previous mode material/vertex-colored 78 }). 79 80-record(fs, 81 {vs, % triangulated vertex id in uv window [[Id1,Id2,Id3]] 82 vse, % face vertex id's untriangulated for edge drawings 83 id}). % Face Id 84 85%% Menu 86 87draw_options(#st{bb=Uvs}=AuvSt0) -> 88 #uvstate{st=GeomSt0,matname=MatName0,bg_img=TexImg} = Uvs, 89 BkpImg = wings_image:info(TexImg), 90 prw_img_id(new), 91 92 [MaxTxs0|_] = gl:getIntegerv(?GL_MAX_TEXTURE_SIZE), 93 MaxTxs = max(min(8192, MaxTxs0), 256), 94 Shaders = shaders(), 95 Prefs = get_valid_prefs(Shaders), 96 TexSz = proplists:get_value(texsz, Prefs, 512), 97 Qs = [{hframe,[{menu, gen_tx_sizes(MaxTxs, []),TexSz, 98 [{key,texsz}]}],[{title,?__(1,"Size")}]}, 99 {vframe, render_passes(Prefs, Shaders), [{title,?__(2,"Render")}]} 100 ], 101 wings_dialog:dialog(?__(3,"Draw Options"), {preview,Qs}, 102 fun({dialog_preview,Options}) -> 103 Opt = list_to_prefs(Options), 104 NewImg = ?SLOW(get_texture(AuvSt0, {Opt,Shaders})), 105 case MatName0 of 106 none -> 107 ok = wings_image:update(TexImg, NewImg); 108 _ -> 109 TexName = case get_mat_texture(MatName0, GeomSt0) of 110 false -> atom_to_list(MatName0); 111 OldId -> 112 OldImg = wings_image:info(OldId), 113 case OldImg#e3d_image.name of 114 "auvBG" -> atom_to_list(MatName0); 115 Other -> Other 116 end 117 end, 118 catch wings_material:update_image(MatName0, diffuse, NewImg#e3d_image{name=TexName}, GeomSt0) 119 end, 120 {preview,GeomSt0,GeomSt0}; 121 (cancel) -> 122 case MatName0 of 123 none -> 124 ok = wings_image:update(TexImg, BkpImg); 125 _ -> 126 catch wings_material:update_image(MatName0, diffuse, BkpImg, GeomSt0) 127 end, 128 wings_wm:later({new_state,AuvSt0}), 129 prw_img_id(delete), 130 GeomSt0; 131 (Options) -> 132 Opt = list_to_prefs(Options), 133 prw_img_id(delete), 134 set_pref([{tx_prefs,pref_to_list(Opt)}]), 135 {auv,{draw_options,{Opt,Shaders}}} 136 end). 137 138get_mat_texture(MatName, #st{mat=Materials}) -> 139 get_mat_texture(MatName, Materials); 140get_mat_texture(MatName, Materials) -> 141 case gb_trees:lookup(MatName, Materials) of 142 none -> false; 143 {value,Mat} -> 144 Maps = proplists:get_value(maps, Mat, []), 145 proplists:get_value(diffuse, Maps, false) 146 end. 147 148%% the goal is to remove invalid images references from the previous 149%% shader settings stored in preferences which may not be present in 150%% the current project. That avoid crashes on preview dialg 151get_valid_prefs(Shaders) -> 152 Prefs = get_pref(tx_prefs, pref_to_list(#opt{})), 153 validate_prefs(Prefs,Shaders,[]). 154 155validate_prefs([], _, Acc) -> Acc; 156validate_prefs([{texsz,_}=Val|Prefs], Sh, Acc) -> 157 validate_prefs(Prefs, Sh, [Val|Acc]); 158validate_prefs([{{auv_pass,_}=Slot,PassId}=Pass,{{auv_opt,_}=Op,OptVal}=Opt0|Prefs], Sh, Acc) -> 159 PassOpt = 160 case PassId of 161 {shader,Id} when OptVal /= [] -> 162 case lists:keysearch(Id,#sh.id,Sh) of 163 {value,#sh{args=Args}} -> 164 [{shader,_}|Opts] = OptVal, 165 case valid_opt(reverse(Args),Opts,true) of 166 true -> [Pass,Opt0]; 167 false -> [{Slot,ignore},{Op,[]}] 168 end; 169 _ -> [{Slot,ignore},{Op,[]}] 170 end; 171 _ -> [Pass,Opt0] 172 end, 173 validate_prefs(Prefs, Sh, Acc++PassOpt); 174validate_prefs([Val|Prefs], Sh, Acc) -> 175 validate_prefs(Prefs, Sh, [Val|Acc]). 176 177valid_opt([], _, Acc) -> Acc; 178valid_opt([{uniform,{image,_},_,_,_}|As], [{_,Id}|Opts], Acc) -> 179 ValidImg = wings_image:txid(Id) /= none, 180 valid_opt(As,Opts,Acc and ValidImg); 181valid_opt([{uniform,_,_,_,_}|As], [_|Opts], Acc) -> 182 valid_opt(As,Opts,Acc); 183valid_opt([_|As], Opts, Acc) -> 184 valid_opt(As,Opts,Acc). 185 186%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 187%% Menu handling 188%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 189 190render_passes(Prefs, Shaders) -> 191 NoOfPasses = 7, 192 Menu = renderers(Shaders), 193 Background = 194 {hframe, 195 [{menu,[{?__(1,"Background"), auv_background}],auv_background, 196 [{key,{auv_pass,0}}, enable_opt()]}, 197 {value, get_def(Prefs,auv_opt,0), [{key,{auv_opt,0}}]}, 198 {button,?__(2,"Options"),keep, 199 [{key, {opt_butt, 0}}, option_hook(0,background(),[])]}], 200 []}, 201 Other = [{hframe, 202 [{menu,Menu,default_menu(Id, Prefs), 203 [{key,{auv_pass,Id}}, enable_opt()]}, 204 {value,get_def(Prefs, auv_opt, Id),[{key,{auv_opt,Id}}]}, 205 {button,?__(3,"Options"),keep, 206 [{key, {opt_butt, Id}}, option_hook(Id, Menu, Shaders)]} 207 ], 208 []} || Id <- lists:seq(1, NoOfPasses)], 209 [Background|Other]. 210 211default_menu(Pass, Prefs) -> 212 case get_def(Prefs, auv_pass, Pass) of 213 ignore -> default_menu(Pass); 214 Val -> Val 215 end. 216 217default_menu(1) -> auv_edges; 218default_menu(_) -> ignore. 219 220get_def(List, What, Id) -> 221 case proplists:get_value({What,Id}, List) of 222 undefined when What =:= auv_pass -> ignore; 223 undefined when What =:= auv_opt -> []; 224 Val -> Val 225 end. 226 227background() -> 228 [{?__(1,"Background"), auv_background}]. 229renderers(Shaders) -> 230 Menu0 = [{"*"++Name++"*",{shader,Id}} || 231 #sh{name=Name,id=Id} <- Shaders], 232 [{?__(1,"None"), ignore}, 233 {?__(2,"Draw Edges"),auv_edges}, 234 {?__(3,"Draw Faces"),auv_faces}|Menu0]. 235 236option_dialog(Id, Fields, Renderers, Shaders) -> 237 try 238 Name = wings_dialog:get_value({auv_pass, Id}, Fields), 239 Opts = 240 case wings_dialog:get_value({auv_opt, Id}, Fields) of 241 [] -> %% it happens first time the menu item is filled 242 %% we need the Vals field to be filled in the next dialog, 243 %% then we need to build it at this point 244 case Name of 245 Name when is_atom(Name) -> NameId = Name; 246 {_,NameId} -> NameId 247 end, 248 if NameId =/= auv_edges -> 249 {value,#sh{def=Opts0}} = lists:keysearch(NameId,#sh.id,Shaders), 250 [{shader,NameId}|lists:reverse(Opts0)]; 251 true -> 252 [] 253 end; 254 Opts0 -> 255 Opts0 256 end, 257 {StrName, Name} = renderer(Name,Renderers), 258 SetValue = fun(Res) -> 259 %% Change the value in the parent dialog 260 wings_dialog:set_value({auv_opt, Id}, [Name|Res], Fields), 261 %% removing the temporary VBO data 262 delete_preview_vbo() 263 end, 264 SphereData = create_sphere_data(), 265 wings_dialog:dialog(StrName,options(Name,Opts,Shaders,SphereData),SetValue) 266 catch _:Crash:ST -> 267 io:format("EXIT: ~p ~p~n",[Crash, ST]) 268 end. 269 270%% removing the temporary VBO data used by preview 271delete_preview_vbo() -> 272 case wings_pref:get_value(?SHADER_PRW_VBO) of 273 undefined -> ignore; 274 Vbo -> 275 wings_pref:delete_value(?SHADER_PRW_VBO), 276 wings_vbo:delete(Vbo), 277 ignore 278 end. 279 280%% function required to ensure a config file with image on shader can be correctly 281%% load by file name or by name (image in Wings3D), otherwise we use the default 282%% auvBG in order to avoid GLSL issues by not receiving an image. 283%% The Filename parameter comes from .auv config file 284load_image(ImgName, Filename) when is_list(Filename) -> 285 Is = wings_image:images(), 286 ImgsId = [{Name,TexId} || {TexId, #e3d_image{name=Name}} <- Is], 287 case filelib:is_file(Filename) of 288 true -> 289 %% lets check if the file was not loaded before (using the same var name) 290 case lists:keyfind(ImgName,1,ImgsId) of 291 {_,Value} -> Value; 292 _ -> 293 %% we try to load the file set in the .auv configuration file 294 case wpa:image_read([{filename,Filename}, 295 {alignment,1}]) of 296 #e3d_image{}=Image -> 297 {ImgName,wings_image:new_temp(ImgName, Image)}; 298 _ -> image_bg(ImgsId) 299 end 300 end; 301 _ -> 302 %% Filename is an image name at Wings3D 303 case lists:keyfind(Filename,1,ImgsId) of 304 {_,Value} -> Value; 305 _ -> 306 %% check for the image name already load 307 case lists:keyfind(ImgName,1,ImgsId) of 308 {_,Value} -> Value; 309 _ -> image_bg(ImgsId) 310 end 311 end 312 end; 313%% after the first call, in the further ones the Filename will be 314%% the image info {name,id} 315load_image(_, Filename) -> Filename. 316 317image_bg(ImgsId) -> 318 AuvBG = 319 case lists:keyfind("auvBG",1,ImgsId) of 320 {_,Value} -> Value; 321 _ -> wings_image:new("auvBG",wpc_autouv:bg_image()) 322 end, 323 {"auvBG",AuvBG}. 324 325options(auv_background, [auv_background, {type_sel,Type},{Image,_},Color],_,_) -> 326 Enable = fun(_, What, Fields) -> 327 IsImage = image == What, 328 wings_dialog:enable(image_sel, IsImage, Fields), 329 wings_dialog:enable(col_sel, not IsImage, Fields) 330 end, 331 [{hradio,[{?__(1,"Image"),image},{?__(2,"Color"),color}], 332 Type,[{key,type_sel},{hook, Enable}]}, 333 {hframe,[{label,?__(1,"Image")},image_selector(Image,[])], 334 [{key, image_sel}]}, 335 {hframe,[{label,?__(2,"Color")},{color,fix(Color,wings_gl:have_fbo())}], 336 [{key, col_sel}]}]; 337options(auv_background, _Bad,Sh,SphereData) -> 338 options(auv_background, ?OPT_BG,Sh,SphereData); 339 340options(auv_edges,[auv_edges, Type,Color,Size,UseVtxColors],_,_) -> 341 [{vradio,[{?__(3,"Draw All Edges"),all_edges}, 342 {?__(4,"Draw Border Edges"), border_edges}], 343 Type, []}, 344 {hframe,[{label,?__(5,"Edge Color:")},{color,Color}]}, 345 {hframe,[{label,?__(6,"Edge Width:")},{text,Size,[{range,{0.0,100.0}}]}]}, 346 {?__(8,"Use vertex colors (on border edges)"),UseVtxColors} 347 ]; 348options(auv_edges,_,Sh,SphereData) -> 349 options(auv_edges,?OPT_EDGES,Sh,SphereData); 350 351options({shader,Id}=Opt,Vals,Sh,SphereData) -> 352 {value,Shader} = lists:keysearch(Id,#sh.id,Sh), 353 options_1(Opt,Vals,Sh,SphereData,Shader); 354 355options(Command,Vals,_,_) -> 356 io:format("~p: ~p~n",[Command, Vals]), 357 exit(unknown_default). 358 359options_1(Opt, Vals0, _, _, #sh{preview=false}=Shader) -> 360 FrmShader = 361 case Vals0 of 362 [Opt|Vals] -> 363 shader_options(Shader,[],Vals); 364 _ -> 365 shader_options(Shader,[],[]) 366 end, 367 [{vframe, FrmShader}]; 368 369options_1(Opt, Vals0, Sh, {Ts,SphereMesh}, Shader) -> 370 Preview = 371 fun(GLCanvas, _Fields) -> 372 wings_light:init_opengl(), 373 %% we create the sphere data for preview once 374 case wings_pref:get_value(?SHADER_PRW_VBO) of 375 undefined -> 376 Vbo = setup_sphere_vbo(SphereMesh), 377 wings_pref:set_value(?SHADER_PRW_VBO,Vbo); 378 Vbo0 -> Vbo = Vbo0 379 end, 380 tex_preview(GLCanvas,Vbo) 381 end, 382 383 Refresh = 384 fun(Key, Value, Fields) -> 385 GLCanvas = wings_dialog:get_widget(preview, Fields), 386 case wxWindow:isShown(GLCanvas) of 387 false -> ok; 388 true -> 389 %% update the Vals list with the content of Fields plus 390 %% the current field changed (Key) which is not updated 391 %% in Fields 392 Vals = update_values(Key,Value,Vals0,Fields), 393 %% creating the texture preview image 394 wings_gl:setCurrent(GLCanvas, ?GET(gl_context)), 395 PrwImg = get_texture_preview({Opt,Vals},Sh,Ts), 396 %% updating the image 397 prw_img_id(PrwImg), 398 case os:type() of 399 {_, darwin} -> %% workaround wxWidgets 3.0.4 and mojave 400 Preview(GLCanvas, Fields), 401 wxGLCanvas:swapBuffers(GLCanvas); 402 _ -> 403 wxWindow:refresh(GLCanvas) 404 end 405 end 406 end, 407 408 FrmShader = 409 case Vals0 of 410 [Opt|Vals] -> 411 shader_options(Shader,[{hook,Refresh}],Vals); 412 _ -> 413 shader_options(Shader,[{hook,Refresh}],[]) 414 end, 415 [{hframe, [ 416 {vframe, [{custom_gl,?PREVIEW_SIZE,?PREVIEW_SIZE,Preview, 417 [{key, preview}, {proportion, 1}, {flag, ?wxEXPAND bor ?wxALL}]}], 418 [{title, "Preview"}]}, 419 {vframe, FrmShader, [{title, "Parameters"}]}] 420 }]. 421 422shader_options(#sh{args=Args,def=Defs,file=File}, OptDef, Vals) -> 423 case shader_menu(Args,OptDef,reverse(Vals),[]) of 424 {failed,_} -> 425 case shader_menu(Args,OptDef,Defs,[]) of 426 {failed,What} -> 427 io:format("AUV: Bad default value ~p in ~p~n", 428 [What, File]), 429 []; 430 Menues -> Menues 431 end; 432 Menues -> 433 Menues 434 end. 435shader_menu([{uniform,color,_,_,Label}|As],OptDef,[Col={_,_,_,_}|Vs],Acc) -> 436 Menu = {hframe, [{label,Label},{color,Col,OptDef}]}, 437 shader_menu(As,OptDef,Vs,[Menu|Acc]); 438shader_menu([{uniform,float,_,_,Label}|As],OptDef,[Def|Vs],Acc) 439 when is_number(Def) -> 440 Menu = {hframe,[{label,Label}, 441 {text,Def,[{range,{'-infinity',infinity}}]++OptDef}]}, 442 shader_menu(As,OptDef,Vs,[Menu|Acc]); 443shader_menu([{uniform,bool,_,_,Label}|As],OptDef,[Def|Vs],Acc) 444 when is_boolean(Def) -> 445 Menu = {Label, Def, OptDef}, 446 shader_menu(As,OptDef,Vs,[Menu|Acc]); 447shader_menu([{uniform,{slider,From,To},_,_,Label}|As],OptDef,[Def|Vs],Acc) 448 when is_number(Def) -> 449 Menu = {hframe,[{label,Label}, 450 {slider,{text,Def,[{range,{From,To}},{width,5}]++OptDef}}]}, 451 shader_menu(As,OptDef,Vs,[Menu|Acc]); 452shader_menu([{uniform,{image,_},_,_Def,Label}|As],OptDef,[Def|Vs],Acc) -> 453 Image = case Def of {Im,_} -> Im; _ -> Def end, 454 Menu = {hframe,[{label,Label},image_selector(Image,OptDef)]}, 455 shader_menu(As,OptDef,Vs,[Menu|Acc]); 456shader_menu([{uniform,menu,_,_,Labels}|As],OptDef,[Def|Vs],Acc) -> 457 Menu = {menu,Labels,Def,[]++OptDef}, 458 shader_menu(As,OptDef,Vs,[Menu|Acc]); 459shader_menu([{auv,{auv_send_texture,Label,Def0}}|As],OptDef,Vs0,Acc) -> 460 case Vs0 of 461 [{auv_send_texture, Def}|Vs] -> ok; 462 [Def|Vs] -> ok; 463 [] -> Def = Def0, Vs = [] 464 end, 465 Menu = {Label, Def, [{key,auv_send_texture}]}, 466 shader_menu(As,OptDef,Vs,[Menu|Acc]); 467shader_menu([{auv,_Skip}|As],OptDef,Vs,Acc) -> 468 shader_menu(As,OptDef,Vs,Acc); 469shader_menu([What|_],_,[Def|_],_) -> 470 {failed,{What,value,Def}}; 471shader_menu([What|_],_,[],_) -> 472 {failed,What}; 473shader_menu([],_,_,Acc) -> 474 Acc. 475 476image_selector(Default,OptDef) -> 477 Is = wings_image:images(), 478 Menu = [{Name,{Name,TexId}} || {TexId, #e3d_image{name=Name}} <- Is], 479 case lists:keysearch(Default,1,Menu) of 480 {value,{_,What}} -> Def = What; 481 _ -> case Menu of 482 [{_,Def}|_] -> Def; 483 _ -> Def = void 484 end 485 end, 486 {menu,Menu,Def,OptDef}. 487 488enable_opt() -> 489 {hook, 490 fun({_W, Id}, Pass, Fields) -> 491 Disable = Pass =:= ignore orelse Pass =:= auv_faces, 492 wings_dialog:enable({opt_butt, Id}, not Disable, Fields), 493 case wings_dialog:get_value({auv_opt, Id}, Fields) of 494 [Pass|_] -> 495 ok; 496 _ -> %% Changed type reset options 497 wings_dialog:set_value({auv_opt, Id}, [], Fields) 498 end 499 end}. 500 501option_hook(Id,Renderers,Shaders) -> 502 {hook, 503 fun(_Key, button_pressed, Fields) -> 504 Env = wx:get_env(), 505 spawn(fun() -> 506 %% Need open dialog in dialog from another process 507 wx:set_env(Env), 508 option_dialog(Id, Fields, Renderers, Shaders), 509 wings_wm:psend(send_once, dialog_blanket, preview) 510 end) 511 end 512 }. 513 514renderer(Id,[Renderer={_,Id}|_R]) -> Renderer; 515renderer(Id,[_|R]) -> renderer(Id,R). 516 517%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 518%% Texture preview handling 519%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 520 521update_values(Key, Val, Vals, Fields) -> 522 update_values(1,Key,Val,Vals,Fields,[]). 523 524update_values(_, _, _, [], _, Acc) -> lists:reverse(Acc); 525update_values(Key, Key, Value, [_|Vals], Fields, Acc) -> 526 update_values(Key+1,Key,Value,Vals,Fields,[Value|Acc]); 527update_values(Key, CKey, CValue, [H|Vals], Fields, Acc) -> 528 try wings_dialog:get_value(Key,Fields) of 529 Value -> 530 update_values(Key+1,CKey,CValue,Vals,Fields,[Value|Acc]) 531 catch 532 _:_ -> 533 update_values(Key+1,CKey,CValue,Vals,Fields,[H|Acc]) 534 end. 535 536get_texture_preview(Render,Shaders,Ts) -> 537 Options = #opt{texsz = {?SHADER_PRW_SIZE,?SHADER_PRW_SIZE},no_renderers=2,renderers=[Render]}, 538 Compiled = compile_shaders([Render],Shaders), 539 Passes = get_passes([Render],{Shaders,Compiled}), 540 Reqs = get_requirements(Shaders), 541 Res = render_image_preview(Ts, Passes, Options, Reqs), 542 delete_shaders(Compiled), 543 Res. 544 545render_image_preview(Geom0, Passes, #opt{texsz={TexW,TexH}}, Reqs) -> 546 gl:pushAttrib(?GL_ALL_ATTRIB_BITS), 547 UsingFbo = setup_fbo(TexW,TexH), 548 {W0,H0} = if not UsingFbo -> 549 wings_wm:top_size(); 550 true -> 551 gl:blendEquationSeparate(?GL_FUNC_ADD, ?GL_MAX), 552 {TexW,TexH} 553 end, 554 {W,Wd} = calc_texsize(W0, TexW), 555 {H,Hd} = calc_texsize(H0, TexH), 556 set_viewport({0,0,W,H}, 1), 557 Geom = make_vbo(Geom0, Reqs), 558 try 559 Do = fun(Pass) -> 560 if not UsingFbo -> 561 Pass(Geom,undefined); 562 true -> 563 fill_bg_tex(UsingFbo), 564 Pass(Geom,UsingFbo) 565 end 566 end, 567 Dl = fun() -> foreach(Do, Passes) end, 568 ImageBins = get_texture(0, Wd, 0, Hd, {W,H}, Dl, UsingFbo,[]), 569 ImageBin = merge_texture(ImageBins, Wd, Hd, W*3, H, []), 570 if not UsingFbo -> 571 #e3d_image{image=ImageBin,width=TexW,height=TexH}; 572 true -> 573 #e3d_image{image=ImageBin,width=TexW,height=TexH, 574 type=r8g8b8a8,bytes_pp=4} 575 end 576 catch _:What:Where -> 577 exit({What,Where}) 578 after 579 case UsingFbo of 580 false -> ignore; 581 #sh_conf{fbo_d=DeleteMe} -> DeleteMe() 582 end, 583 wings_vbo:delete(Geom#ts.vbo), 584 gl:readBuffer(?GL_BACK), 585 gl:popAttrib(), 586 gl:blendEquationSeparate(?GL_FUNC_ADD, ?GL_FUNC_ADD), 587 gl:clear(?GL_COLOR_BUFFER_BIT bor ?GL_DEPTH_BUFFER_BIT), 588 ?ERROR 589 end. 590 591create_sphere_data() -> 592 {We, SphereData} = setup_sphere_mesh(), 593 Shs = gb_trees:enter(We#we.id, We, gb_trees:empty()), 594 Maps = 595 case wings_pref:get_value(?SHADER_PRW_NAME) of 596 undefined -> []; 597 {TxId,_} -> {maps,[{diffuse,TxId}]} 598 end, 599 Mat = [{opengl,[{diffuse, {1.0,1.0,1.0,1.0}}, 600 {emission,{0.0,0.0,0.0,1.0}}, 601 {metallic, 0.0}, 602 {roughness, 1.0}, 603 {vertex_colors,ignore}]}] ++ Maps, 604 Mtab = gb_trees:enter(list_to_atom(?SHADER_PRW_NAME),Mat,gb_trees:empty()), 605 FakeSt0 = #st{sel=[],shapes=Shs,mat=Mtab}, 606 Uvs = #uvstate{st=wpa:sel_set(face, [], FakeSt0), 607 id = We#we.id, 608 mode = object, 609 matname = none}, 610 FakeSt = FakeSt0#st{selmode=body,sel=[],shapes=gb_trees:empty(),bb=Uvs, 611 repeatable=ignore,ask_args=none,drag_args=none}, 612 Ts = setup(rebuild_charts(We, FakeSt),[normal]), 613 {Ts, SphereData}. 614 615prw_img_id(new) -> 616 case wings_pref:get_value(?SHADER_PRW_NAME) of 617 undefined -> 618 Image = bg_image_prw(), 619 Id = wings_image:new_hidden(?SHADER_PRW_NAME,Image), 620 TxId = wings_image:txid(Id), 621 wings_pref:set_value(?SHADER_PRW_NAME,{TxId,Id}), 622 TxId; 623 {TxId,_} -> 624 TxId 625 end; 626prw_img_id(#e3d_image{}=Image) -> 627 TxId = 628 case wings_pref:get_value(?SHADER_PRW_NAME) of 629 undefined -> 630 prw_img_id(new); 631 {TxId0,Id} -> 632 wings_pref:set_value(?SHADER_PRW_NAME,{TxId0,Id}), 633 TxId0 634 end, 635 init_texture(Image, TxId); 636prw_img_id(delete) -> 637 case wings_pref:get_value(?SHADER_PRW_NAME) of 638 undefined -> 639 ignore; 640 {_,Id} -> 641 wings_image:delete(Id), 642 wings_pref:delete_value(?SHADER_PRW_NAME) 643 end. 644 645bg_image_prw() -> 646 White = <<255,255,255,255>>, 647 Width = Height = ?SHADER_PRW_SIZE, 648 Row = fill_bg(Width,White), 649 Pixels = fill_bg(Height,Row), 650 #e3d_image{width=Width,height=Height,image=Pixels, 651 order=lower_left,bytes_pp=4,type=r8g8b8a8,name=?SHADER_PRW_NAME}. 652 653fill_bg(Width, White) -> 654 fill_bg(Width,White,<<>>). 655 656fill_bg(0, _, Acc) -> Acc; 657fill_bg(Width, White, Acc) -> fill_bg(Width-1,White,<<White/binary,Acc/binary>>). 658 659init_texture(#e3d_image{width=W,height=H,image=Bits,extra=Opts}, TxId) -> 660 gl:bindTexture(?GL_TEXTURE_2D, TxId), 661 FT = {Format,Type} = {?GL_RGBA, ?GL_UNSIGNED_BYTE}, 662 Ft = case wings_pref:get_value(filter_texture, false) of 663 true -> linear; 664 false -> nearest 665 end, 666 {MinFilter,MagFilter} = proplists:get_value(filter, Opts, {mipmap, Ft}), 667 MMs = proplists:get_value(mipmaps, Opts, []), 668 gl:texParameteri(?GL_TEXTURE_2D, ?GL_TEXTURE_WRAP_S, ?GL_REPEAT), 669 gl:texParameteri(?GL_TEXTURE_2D, ?GL_TEXTURE_WRAP_T, ?GL_REPEAT), 670 case MinFilter of 671 mipmap -> 672 case lists:reverse(lists:keysort(4, MMs)) of 673 [{_,_,_,Max}|_] -> 674 gl:texParameteri(?GL_TEXTURE_2D, ?GL_TEXTURE_MAX_LEVEL, Max-1); 675 [] -> 676 gl:texParameteri(?GL_TEXTURE_2D, ?GL_GENERATE_MIPMAP, ?GL_TRUE) 677 end, 678 gl:texParameteri(?GL_TEXTURE_2D, ?GL_TEXTURE_MIN_FILTER, ?GL_LINEAR_MIPMAP_LINEAR), 679 gl:texParameteri(?GL_TEXTURE_2D, ?GL_TEXTURE_MAG_FILTER, filter(MagFilter)); 680 _ -> 681 gl:texParameteri(?GL_TEXTURE_2D, ?GL_TEXTURE_MAG_FILTER, filter(MagFilter)), 682 gl:texParameteri(?GL_TEXTURE_2D, ?GL_TEXTURE_MIN_FILTER, filter(MinFilter)) 683 end, 684 IntFormat = internal_format(FT), 685 gl:texImage2D(?GL_TEXTURE_2D, 0, IntFormat, W, H, 0, Format, Type, Bits), 686 [gl:texImage2D(?GL_TEXTURE_2D, Levl, IntFormat, MMW, MMH, 0, Format, Type, MM) 687 || {MM, MMW, MMH, Levl} <- MMs], 688 gl:bindTexture(?GL_TEXTURE_2D, 0), 689 ?CHECK_ERROR(), 690 TxId. 691 692filter(mipmap) -> ?GL_LINEAR_MIPMAP_LINEAR; 693filter(linear) -> ?GL_LINEAR; 694filter(nearest) -> ?GL_NEAREST. 695 696%% internal_format({?GL_BGR,?GL_UNSIGNED_BYTE}) -> ?GL_RGB; 697%% internal_format({?GL_BGRA,?GL_UNSIGNED_BYTE}) -> ?GL_RGBA; 698%% internal_format({?GL_RGBA,?GL_FLOAT}) -> ?GL_RGBA32F; 699%% internal_format({?GL_RGB,?GL_FLOAT}) -> ?GL_RGB32F; 700internal_format({Format,?GL_UNSIGNED_BYTE}) -> Format. 701 702rebuild_charts(We, St = #st{bb=UVS=#uvstate{st=Old,mode=Mode}}) -> 703 {Faces,FvUvMap} = auv_segment:fv_to_uv_map(Mode,We), 704 {Charts1,Cuts} = auv_segment:uv_to_charts(Faces, FvUvMap, We), 705 Charts2 = auv_segment:cut_model(Charts1, Cuts, We), 706 Charts = update_uv_tab(Charts2, FvUvMap), 707 St#st{sel=[],bb=UVS#uvstate{mode=update_mode(Faces,We),st=Old#st{sel=[]}},shapes=Charts}. 708 709update_mode(Faces0, #we{fs=Ftab}) -> 710 Fs = gb_sets:from_list(Faces0), 711 case gb_sets:size(Fs) == gb_trees:size(Ftab) of 712 true -> object; 713 false -> Fs 714 end. 715 716update_uv_tab(Cs, FvUvMap) -> 717 update_uv_tab_1(Cs, FvUvMap, []). 718 719update_uv_tab_1([#we{id=Id,name=#ch{vmap=Vmap}}=We0|Cs], FvUvMap, Acc) -> 720 Fs = wings_we:visible(We0), 721 UVs0 = wings_face:fold_faces( 722 fun(F, V, _, _, A) -> 723 OrigV = auv_segment:map_vertex(V, Vmap), 724 [{V,[F|OrigV]}|A] 725 end, [], Fs, We0), 726 case update_uv_tab_2(sort(UVs0), FvUvMap, 0.0, []) of 727 error -> 728 %% No UV coordinate for at least some vertices (probably 729 %% all) in the chart. Throw away this chart. 730 update_uv_tab_1(Cs, FvUvMap, Acc); 731 UVs1 -> 732 UVs = array:from_orddict(UVs1), 733 We = We0#we{vp=UVs}, 734 update_uv_tab_1(Cs, FvUvMap, [{Id,We}|Acc]) 735 end; 736update_uv_tab_1([], _, Acc) -> 737 gb_trees:from_orddict(sort(Acc)). 738 739update_uv_tab_2([{V,_}|T], FvUvMap, Z, [{V,_}|_]=Acc) -> 740 update_uv_tab_2(T, FvUvMap, Z, Acc); 741update_uv_tab_2([{V,Key}|T], FvUvMap, Z, Acc) -> 742 case gb_trees:get(Key, FvUvMap) of 743 {X,Y} -> 744 Pos = {X,Y,Z}, 745 update_uv_tab_2(T, FvUvMap, Z, [{V,Pos}|Acc]); 746 _ -> 747 %% No UV-coordinate for this vertex. Abandon the entire chart. 748 error 749 end; 750update_uv_tab_2([], _, _, Acc) -> 751 lists:reverse(Acc). 752 753setup_sphere_mesh() -> 754 #{size:=Len, tris:= Tris, ns:=Normals, uvs:=UVs, tgs:=Tgs} = 755 wings_shapes:tri_sphere(#{subd=>4, ccw=>false, normals=>true, tgs=>true, 756 uvs=>true, scale=>0.45}), 757 Idx = length(Tris) div 3, 758 Fs = [#e3d_face{vs=[I*3,I*3+1,I*3+2], 759 tx=[I*3,I*3+1,I*3+2], 760 ns=[I*3,I*3+1,I*3+2]} || I <- lists:seq(0,Idx-1)], 761 Mesh = #e3d_mesh{vs=Tris,tx=UVs,ns=Normals,fs=Fs}, 762 #we{vp=Vtab0} = We = wings_import:import_mesh(material,Mesh), 763 %% rotating the sphere slight above and right to better visualization 764 %% of the blending area of a triplanar shader 765 M0 = e3d_mat:rotate(30.0,{0.0,1.0,0.0}), 766 M1 = e3d_mat:rotate(-30.0,{1.0,0.0,0.0}), 767 M = e3d_mat:mul(M0,M1), 768 Vtab = 769 array:sparse_foldl(fun(V, Value0, Acc)-> 770 Value = e3d_mat:mul_point(M,Value0), 771 array:set(V,Value,Acc) 772 end,Vtab0,Vtab0), 773 Data = {Len, zip(Tris, Normals, UVs, Tgs)}, 774 {We#we{id=1,vp=Vtab,mat=list_to_atom(?SHADER_PRW_NAME)},Data}. 775 776setup_sphere_vbo({Len,Data}) -> 777 Layout = [vertex, normal, uv, tangent], 778 D = fun(#{preview := PreviewMat} = RS0) -> 779 RS1 = wings_shaders:use_prog(1, RS0), 780 RS2 = lists:foldl(fun apply_material/2, RS1, PreviewMat), 781 gl:drawArrays(?GL_TRIANGLES, 0, Len*3), 782 wings_shaders:use_prog(0, RS2) 783 end, 784 wings_vbo:new(D, Data, Layout). 785 786apply_material({{tex, Type}=TexType, TexId}, Rs0) -> 787 case wings_shaders:set_state(TexType, TexId, Rs0) of 788 {false, Rs0} -> 789 Rs0; 790 {true, Rs1} when TexId =:= none -> 791 wings_shaders:set_uloc(texture_var(Type), enable(false), Rs1); 792 {true, Rs1} -> 793 gl:activeTexture(?GL_TEXTURE0 + tex_unit(Type)), 794 gl:bindTexture(?GL_TEXTURE_2D, TexId), 795 gl:activeTexture(?GL_TEXTURE0), 796 wings_shaders:set_uloc(texture_var(Type), enable(true), Rs1) 797 end; 798apply_material({Type, Value}, Rs0) 799 when Type =:= diffuse; Type =:= emission; Type =:= metallic; Type =:= roughness -> 800 wings_shaders:set_uloc(Type, Value, Rs0); 801apply_material({_Type,_}, Rs0) -> 802 io:format("~p:~p: unsupported type ~p~n",[?MODULE,?LINE,_Type]), 803 Rs0. 804 805enable(true) -> 1; 806enable(false) -> 0. 807 808texture_var(diffuse) -> 'UseDiffuseMap'; 809texture_var(normal) -> 'UseNormalMap'; 810texture_var(pbr_orm) -> 'UsePBRMap'; %% red = occlusion green = roughness blue = metallic 811texture_var(emission) -> 'UseEmissionMap'. 812 813tex_unit(diffuse) -> ?DIFFUSE_MAP_UNIT; 814tex_unit(normal) -> ?NORMAL_MAP_UNIT; 815tex_unit(pbr_orm) -> ?PBR_MAP_UNIT; %% red = occlusion green = roughness blue = metallic 816tex_unit(emission) -> ?EMISSION_MAP_UNIT. 817 818tex_preview(Canvas, Vbo) -> 819 TxId = prw_img_id(new), 820 Maps = [{{tex,diffuse}, TxId}], 821 {W,H} = wxWindow:getSize(Canvas), 822 Scale = wxWindow:getContentScaleFactor(Canvas), 823 824 gl:pushAttrib(?GL_ALL_ATTRIB_BITS), 825 gl:viewport(0, 0, round(W*Scale), round(H*Scale)), 826 {BR,BG,BB, _} = wxWindow:getBackgroundColour(wxWindow:getParent(Canvas)), 827 BGC = fun(Col) -> (Col-15) / 255 end, 828 gl:clearColor(BGC(BR),BGC(BG),BGC(BB),1.0), 829 gl:clear(?GL_COLOR_BUFFER_BIT bor ?GL_DEPTH_BUFFER_BIT), 830 gl:matrixMode(?GL_PROJECTION), 831 gl:loadIdentity(), 832 Fov = 45.0, Aspect = W/H, 833 MatP = e3d_transform:perspective(Fov, Aspect, 0.01, 256.0), 834 gl:multMatrixd(e3d_transform:matrix(MatP)), 835 gl:matrixMode(?GL_MODELVIEW), 836 gl:loadIdentity(), 837 Dist = (0.5/min(1.0,Aspect)) / math:tan(Fov/2*math:pi()/180), 838 Eye = {0.0,0.0,Dist}, Up = {0.0,1.0,0.0}, 839 MatMV = e3d_transform:lookat(Eye, {0.0,0.0,0.0}, Up), 840 gl:multMatrixd(e3d_transform:matrix(MatMV)), 841 gl:shadeModel(?GL_SMOOTH), 842 Diff = {diffuse, {1.0,1.0,1.0,1.0}}, 843 Emis = {emission, {0.0,0.0,0.0,1.0}}, 844 Metal = {metallic, 0.0}, 845 Rough = {roughness, 1.0}, 846 Material = [Diff, Emis, Metal, Rough | Maps], 847 gl:enable(?GL_BLEND), 848 gl:enable(?GL_DEPTH_TEST), 849 gl:enable(?GL_CULL_FACE), 850 gl:blendFunc(?GL_SRC_ALPHA, ?GL_ONE_MINUS_SRC_ALPHA), 851 gl:color4ub(255, 255, 255, 255), 852 RS = #{ws_eyepoint=>Eye, view_from_world=>MatMV, preview=>Material}, 853 wings_dl:call(Vbo, RS), 854 gl:disable(?GL_BLEND), 855 gl:shadeModel(?GL_FLAT), 856 gl:popAttrib(), 857 case os:type() of 858 {_, darwin} -> 859 %% Known problem during redraws before window is shown 860 %% only reset error check 861 _ = gl:getError(); 862 _ -> 863 wings_develop:gl_error_check("Rendering texture viewer") 864 end. 865 866zip(Vs, Ns, UVs, Tgs) -> 867 zip_0(Vs, Ns, UVs, Tgs, []). 868 869zip_0([V|Vs], [N|Ns], [UV|UVs], [T|Ts], Acc) -> 870 zip_0(Vs, Ns, UVs,Ts, [V,N,UV,T|Acc]); 871zip_0([], [], [], [],Acc) -> Acc. 872 873%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 874%% Texture creation 875%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 876 877get_texture(St = #st{bb=#uvstate{}}, {Options,Shaders}) -> 878 Compiled = compile_shaders(Options#opt.renderers,Shaders), 879 Passes = get_passes(Options#opt.renderers,{Shaders,Compiled}), 880 Reqs = get_requirements(Shaders), 881 Ts = setup(St,Reqs), 882 Res = render_image(Ts, Passes, Options, Reqs), 883 delete_shaders(Compiled), 884 Res. 885 886%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 887%% Texture Rendering 888%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 889 890render_image(Geom0, Passes, #opt{texsz={TexW,TexH}}, Reqs) -> 891 gl:pushAttrib(?GL_ALL_ATTRIB_BITS), 892 893 Current = wings_wm:viewport(), 894 Scale = wings_wm:win_scale(), 895 UsingFbo = setup_fbo(TexW,TexH), 896 {W0,H0} = if not UsingFbo -> 897 wings_wm:top_size(); 898 true -> 899 gl:blendEquationSeparate(?GL_FUNC_ADD, ?GL_MAX), 900 {TexW,TexH} 901 end, 902 {W,Wd} = calc_texsize(W0, TexW), 903 {H,Hd} = calc_texsize(H0, TexH), 904%% io:format("Get texture sz ~p ~p ~n", [{W,Wd},{H,Hd}]), 905 set_viewport({0,0,W,H}, 1), 906 Geom = make_vbo(Geom0, Reqs), 907 try 908 Do = fun(Pass) -> 909 if not UsingFbo -> 910 Pass(Geom,undefined); 911 true -> 912 fill_bg_tex(UsingFbo), 913 Pass(Geom,UsingFbo) 914 end 915 end, 916 Dl = fun() -> foreach(Do, Passes) end, 917 ImageBins = get_texture(0, Wd, 0, Hd, {W,H}, Dl, UsingFbo,[]), 918 ImageBin = merge_texture(ImageBins, Wd, Hd, W*3, H, []), 919 if not UsingFbo -> 920 #e3d_image{image=ImageBin,width=TexW,height=TexH}; 921 true -> 922 #e3d_image{image=ImageBin,width=TexW,height=TexH, 923 type=r8g8b8a8,bytes_pp=4} 924 end 925 catch _:What:Where -> 926 exit({What,Where}) 927 after 928 case UsingFbo of 929 false -> ignore; 930 #sh_conf{fbo_d=DeleteMe} -> DeleteMe() 931 end, 932 wings_vbo:delete(Geom#ts.vbo), 933 set_viewport(Current, Scale), 934 gl:readBuffer(?GL_BACK), 935 gl:popAttrib(), 936 gl:blendEquationSeparate(?GL_FUNC_ADD, ?GL_FUNC_ADD), 937 gl:clear(?GL_COLOR_BUFFER_BIT bor ?GL_DEPTH_BUFFER_BIT), 938 ?ERROR 939 end. 940 941make_vbo(#ts{uv=UV}=Ts, Reqs) -> 942 {Layout, Bin} = make_bin_normal(Ts, Reqs, [{vertex2d, 0, 0}], UV), 943 Ts#ts{vbo=wings_vbo:new(dynamic, Bin, {predefined, Layout})}. 944 945make_bin_normal(#ts{n=Ns}=Ts, Reqs, Layout, Bin) -> 946 case lists:member(normal, Reqs) of 947 true -> make_bin_color(Ts, Reqs, [{normal, 0, byte_size(Bin)}|Layout], 948 <<Bin/binary, Ns/binary>>); 949 false -> make_bin_color(Ts, Reqs, Layout, Bin) 950 end. 951 952make_bin_color(#ts{vc=Vc}=Ts, Reqs, Layout, Bin) -> 953 make_bin_pos3d(Ts, Reqs, [{color, 0, byte_size(Bin)}|Layout], <<Bin/binary, Vc/binary>>). 954 955make_bin_pos3d(#ts{pos=Pos}=Ts, Reqs, Layout, Bin) -> 956 case have_shaders() of 957 true -> make_bin_binormal(Ts, Reqs, [{{tex,1,3}, 0, byte_size(Bin)}|Layout], 958 <<Bin/binary, Pos/binary>>); 959 false -> make_bin_binormal(Ts, Reqs, Layout, Bin) 960 end. 961 962make_bin_binormal(#ts{bi=BiNs}, Reqs, Layout, Bin) -> 963 case lists:member(binormal, Reqs) of 964 true -> {lists:reverse([{{tex,2,4}, 0, byte_size(Bin)}|Layout]), 965 <<Bin/binary, BiNs/binary>>}; 966 false -> {lists:reverse(Layout), Bin} 967 end. 968 969%%%%%%%%% FBO stuff 970 971setup_fbo(W,H) -> 972 case wings_gl:setup_fbo({W,H}, [{color,[]}, {color,[]}, {depth,[]}]) of 973 not_supported -> false; 974 List -> 975 [Col1,Col2] = [Col || {color, Col} <- List], 976 #sh_conf{texsz={W,H},fbo_r=Col2,fbo_w=Col1, 977 fbo_d=fun() -> wings_gl:delete_fbo(List) end} 978 end. 979 980error_msg(Line) -> 981 case wings_gl:error_string(gl:getError()) of 982 no_error -> ok; 983 Err -> io:format("~p:~p: ~p ~n",[?MODULE, Line,Err]), error 984 end. 985 986draw_texture_square() -> 987 VertexUvQ = << 0.0:?F32,0.0:?F32, 0.0:?F32,0.0:?F32, 988 1.0:?F32,0.0:?F32, 1.0:?F32,0.0:?F32, 989 1.0:?F32,1.0:?F32, 1.0:?F32,1.0:?F32, 990 0.0:?F32,1.0:?F32, 0.0:?F32,1.0:?F32>>, 991 wings_vbo:draw(fun(_) -> gl:drawArrays(?GL_QUADS, 0, 4) end, VertexUvQ, [vertex2d, uv]). 992 993fill_bg_tex(#sh_conf{fbo_w=Prev}) -> 994 gl:drawBuffer(?GL_COLOR_ATTACHMENT1_EXT), 995 gl:bindTexture(?GL_TEXTURE_2D, Prev), 996 gl:texEnvi(?GL_TEXTURE_ENV, ?GL_TEXTURE_ENV_MODE, ?GL_REPLACE), 997 gl:enable(?GL_TEXTURE_2D), 998 gl:disable(?GL_BLEND), 999 draw_texture_square(), 1000 gl:disable(?GL_TEXTURE_2D), 1001 gl:drawBuffer(?GL_COLOR_ATTACHMENT0_EXT), 1002 ok. 1003 1004get_texture(Wc, Wd, Hc, Hd, {W,H}=Info, DL, UsingFbo, ImageAcc) 1005 when Wc < Wd, Hc < Hd -> 1006 gl:pixelStorei(?GL_UNPACK_ALIGNMENT, 1), 1007 gl:clearColor(1.0, 1.0, 1.0, 1.0), 1008 gl:shadeModel(?GL_SMOOTH), 1009 gl:disable(?GL_CULL_FACE), 1010 gl:disable(?GL_LIGHTING), 1011 texture_view(Wc, Wd, Hc, Hd), 1012 DL(), 1013 gl:flush(), 1014 {Sz,Type} = 1015 case UsingFbo of 1016 false -> 1017 gl:readBuffer(?GL_BACK), 1018 {3,?GL_RGB}; 1019 _ -> 1020 gl:readBuffer(?GL_COLOR_ATTACHMENT0_EXT), 1021 {4,?GL_RGBA} 1022 end, 1023 Mem = wings_io:get_buffer(W*H*Sz, ?GL_UNSIGNED_BYTE), 1024 gl:readPixels(0,0,W,H,Type,?GL_UNSIGNED_BYTE,Mem), 1025 ImageBin = wings_io:get_bin(Mem), 1026 get_texture(Wc+1, Wd, Hc, Hd, Info, DL, UsingFbo, [ImageBin|ImageAcc]); 1027get_texture(_Wc,Wd,Hc,Hd, Info, Dl, UsingFbo, ImageAcc) when Hc < Hd -> 1028 get_texture(0, Wd, Hc+1, Hd, Info, Dl, UsingFbo, ImageAcc); 1029get_texture(_,_,_,_,_,_,_,ImageAcc) -> reverse(ImageAcc). 1030 1031texture_view(WC, WD, HC, HD) -> 1032 gl:matrixMode(?GL_PROJECTION), 1033 gl:loadIdentity(), 1034 Ortho = e3d_transform:ortho(WC/WD, (1+WC)/WD, HC/HD, (1+HC)/HD, -1.0, 1.0), 1035 gl:loadMatrixd(e3d_transform:matrix(Ortho)), 1036 1037 gl:matrixMode(?GL_MODELVIEW), 1038 gl:loadIdentity(), 1039 gl:polygonMode(?GL_FRONT_AND_BACK, ?GL_FILL). 1040 1041merge_texture_cols(List, Wd, Wd, _W, _RowC, Acc) -> 1042 {list_to_binary(reverse(Acc)), List}; 1043merge_texture_cols([H|R], Wc, Wd, W, RowC, Acc) -> 1044 SkipBytes = RowC*W, 1045 <<_:SkipBytes/binary, Row:W/binary,_/binary>> = H, 1046 merge_texture_cols(R, Wc + 1, Wd, W, RowC, [Row|Acc]). 1047 1048merge_texture_rows(_ImageBins, H, H, _W, _Wd,Acc, Last) -> 1049 {list_to_binary(reverse(Acc)), Last}; 1050merge_texture_rows(ImageBins, RowC, H, W, Wd, Acc, _) -> 1051 {Row, Rest} = merge_texture_cols(ImageBins, 0, Wd, W, RowC, []), 1052 merge_texture_rows(ImageBins, RowC + 1, H,W,Wd, [Row|Acc], Rest). 1053 1054merge_texture([Bin],1,1,_,_,[]) -> Bin; %% No merge needed. 1055merge_texture(Bins, 1,_,_,_,[]) -> list_to_binary(Bins); %% No merge needed. 1056merge_texture([],_,_,_,_,Acc) -> 1057 list_to_binary(reverse(Acc)); 1058merge_texture(ImageBins,Wd,Hd,W,H,Acc) -> 1059 {Col, Bins} = merge_texture_rows(ImageBins, 0, H, W, Wd, [], ImageBins), 1060 merge_texture(Bins,Wd,Hd,W,H,[Col|Acc]). 1061 1062calc_texsize(Vp, Tex) -> 1063 calc_texsize(Vp, Tex, Tex). 1064 1065calc_texsize(Vp, Tex, Orig) when Tex =< Vp -> 1066 {Tex,Orig div Tex}; 1067calc_texsize(Vp, Tex, Orig) -> 1068 calc_texsize(Vp, Tex div 2, Orig). 1069 1070get_pref(Key, Def) -> 1071 wpa:pref_get(autouv, Key, Def). 1072set_pref(KeyVals) -> 1073 wpa:pref_set(autouv, KeyVals). 1074 1075pref_to_list(#opt{texsz={TexSz,_TexSz}, no_renderers=NoR, 1076 renderers=[{auv_background,Bg}|Rs]}) -> 1077 [{texsz, TexSz},{{auv_pass,0},auv_background},{{auv_opt,0},Bg}| 1078 r2list(Rs,1,NoR)]. 1079 1080r2list([{Type, Opts}|Rest], Id, Max) when Id < Max -> 1081 [{{auv_pass,Id}, Type},{{auv_opt,Id}, Opts}|r2list(Rest,Id+1,Max)]; 1082r2list([], Id, Max) when Id < Max -> 1083 [{{auv_pass,Id}, ignore},{{auv_opt,Id},[]}|r2list([],Id+1,Max)]; 1084r2list([], _Id, _Max) -> []. 1085 1086list_to_prefs([{texsz, TexSz}|Rest]) -> 1087 LR = listOfRenders(Rest,[]), 1088 #opt{texsz={TexSz,TexSz},no_renderers=length(LR),renderers=LR}. 1089 1090listOfRenders([{{auv_pass,_},ignore},_|Rest],Acc) -> 1091 listOfRenders(Rest,[{ignore,[]}|Acc]); 1092listOfRenders([{{auv_pass,_},Type},{{auv_opt,_},Opts}|Rest],Acc) -> 1093 listOfRenders(Rest,[{Type,Opts}|Acc]); 1094listOfRenders([],Acc) -> reverse(Acc). 1095 1096gen_tx_sizes(Sz, Acc) when Sz < 128 -> Acc; 1097gen_tx_sizes(Sz, Acc) -> 1098 Bytes = Sz*Sz*3, 1099 Mb = 1024*1024, 1100 SzStr = if 1101 Bytes < 1024*1024 -> 1102 io_lib:format("(~pKb)",[Bytes div 1024]); 1103 true -> 1104 io_lib:format("(~pMb)",[Bytes div Mb]) 1105 end, 1106 Str0 = io_lib:format("~px~p ", [Sz,Sz]), 1107 Str = lists:flatten([Str0|SzStr]), 1108 gen_tx_sizes(Sz div 2, [{Str,Sz}|Acc]). 1109 1110set_viewport({X,Y,W,H}=Viewport, Scale) -> 1111 put(wm_viewport, Viewport), 1112 gl:viewport(X, Y, round(W*Scale), round(H*Scale)). 1113 1114 1115%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1116%% Data setup 1117%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1118 1119setup(#st{bb=#uvstate{id=RId,st=#st{shapes=Sh0}}}=St, Reqs) -> 1120 We = gb_trees:get(RId,Sh0), 1121 {Charts,{_Cnt,UVpos,Vpos,Ns,Ts,BB,Vc}} = setup_charts(St, We, Reqs), 1122 #ts{charts=Charts, 1123 uv = to_bin(UVpos,uv), 1124 pos= to_bin(Vpos,pos), 1125 n = to_bin(Ns,pos), 1126 bi = build_tangents(Ts), 1127 bb = BB, 1128 vc = to_bin(Vc, vertex)}. 1129 1130setup_charts(#st{shapes=Cs0,selmode=Mode,sel=Sel}, We, Reqs) -> 1131 Ns = case member(normal, Reqs) orelse member(binormal, Reqs) of 1132 true -> setup_normals(We#we{mirror=none}); 1133 false -> [] 1134 end, 1135 Z = e3d_vec:zero(), 1136 TSA = array:new([{default, {Z,Z}}]), 1137 Start = {0,[],[],[],{TSA,[]},[],[]}, %% {UvPos,3dPos,Normal,Tangent,Vc} 1138 Shapes = if Sel =:= [] -> 1139 gb_trees:values(Cs0); 1140 Mode =:= body -> 1141 [gb_trees:get(Id,Cs0) || {Id,_} <- Sel]; 1142 true -> 1143 gb_trees:values(Cs0) 1144 end, 1145 Setup = fun(Ch,Acc) -> 1146 setup_chart(Ch, Ns, Reqs, We, Acc) 1147 end, 1148 lists:mapfoldl(Setup, Start, Shapes). 1149 1150setup_chart(#we{id=Id}=Ch, Ns, Reqs, WWe, State0) -> 1151 OEs0 = outer_verts(Ch), 1152 BB = [], 1153 {Fs,{OEs,UvBB,State}} = create_faces(Ch, WWe, Ns, Reqs, {OEs0,BB,State0}), 1154 {#chart{id=Id,fs=Fs,oes=OEs,bb_uv=UvBB},State}. 1155 1156create_faces(#we{vp=Vtab,name=#ch{vmap=Vmap}}=We, 1157 #we{vp=Vt3d}=RealWe, NTab, Reqs, State) -> 1158 Fs = wings_we:visible(We), 1159 C=fun(Face,{OEs,UvBB,{Cnt,UVpos,Vpos,Ns,Ts0,PosBB,Vc}}) -> 1160 Vs0 = wings_face:vertices_ccw(Face,We), 1161 UVcoords = [array:get(V, Vtab) || V <- Vs0], 1162 Coords = [array:get(map_vertex(V,Vmap),Vt3d) 1163 || V <- Vs0], 1164 Normals = if 1165 NTab=:= [] -> 1166 []; 1167 true -> 1168 fix_normals(Vs0, Vmap, 1169 wings_va:face_attr([vertex|uv], 1170 Face, RealWe), 1171 gb_trees:get(Face,NTab)) 1172 end, 1173 OldVc = fix_vc(Vs0, Face, RealWe, Vmap), 1174 Len = length(Vs0), 1175 FaceVs = lists:seq(0, Len-1), 1176 Vs = case Len of 1177 3 -> FaceVs; 1178 Len -> triangulate(FaceVs,UVcoords) 1179 end, 1180% io:format("Face ~p Normals ~p~n", [Face,Normals]), 1181 Ts = calc_tang_vecs(Vs,Vs0,Coords,UVcoords,Normals,Ts0,Reqs), 1182 Indx = fun(I) -> [V+Cnt || V <- I] end, 1183 {#fs{vs=Indx(Vs),vse=Indx(FaceVs),id=Face}, 1184 {map_oes(OEs, Vs0, Cnt+Len-1, Face), 1185 e3d_vec:bounding_box(UvBB ++ UVcoords), 1186 {Cnt+Len, 1187 UVcoords ++ UVpos, 1188 Coords ++ Vpos, 1189 Normals ++ Ns, 1190 Ts, 1191 e3d_vec:bounding_box(PosBB ++ Coords), 1192 OldVc ++ Vc}}} 1193 end, 1194 lists:mapfoldl(C, State, Fs). 1195 1196triangulate(FaceVs,Vcoords) -> 1197 E3dface = #e3d_face{vs=FaceVs}, 1198 T3dfaces = e3d_mesh:triangulate_face(E3dface, Vcoords), 1199 lists:append([FVs || #e3d_face{vs=FVs} <- T3dfaces]). 1200 1201map_oes([[A,B,Face]|OEs], Vs0, Cnt, Face) -> 1202 MA = find_index(A, Vs0, Cnt), 1203 MB = find_index(B, Vs0, Cnt), 1204 [[MA,MB,Face]|map_oes(OEs, Vs0, Cnt, Face)]; 1205map_oes([Other|OEs], Vs0, Cnt, Face) -> 1206 [Other|map_oes(OEs, Vs0, Cnt, Face)]; 1207map_oes([], _, _, _) -> []. 1208 1209find_index(Val, [Val|_], Pos) -> Pos; 1210find_index(Val, [_|R], Pos) -> find_index(Val, R, Pos-1). 1211 1212fix_normals(Vs,Vmap,VsI,Ns0) -> %% can be different order 1213 fix_normals(Vs,n_zip(VsI,Ns0),Vmap). 1214fix_normals([V|R],Ns,Vmap) -> 1215 [find(map_vertex(V,Vmap),Ns)|fix_normals(R,Ns,Vmap)]; 1216fix_normals([],_,_) -> []. 1217 1218%%%% Tangent calc 1219 1220calc_tang_vecs(_,_,_,_,[],Ts,_) -> Ts; 1221calc_tang_vecs(Vs,VsIds,Coords,UVCoords,Normals,Ts0,Reqs) -> 1222 %% io:format("Vs ~p ~n",[Vs]), 1223 %% io:format("Coords ~p ~n",[Coords]), 1224 %% io:format("UV ~p ~n",[UVCoords]), 1225 %% io:format("Normals ~p ~n",[Normals]), 1226 case lists:member(binormal,Reqs) of 1227 true -> 1228 N = e3d_vec:norm(e3d_vec:average(Normals)), 1229 calc_tang_vecs_1([V+1||V <- Vs], VsIds, Coords, UVCoords, N, Ts0); 1230 false -> 1231 Ts0 1232 end. 1233 1234calc_tang_vecs_1([Vi1,Vi2,Vi3|Vis], VsIds, Coords, UVCoords, N, {Ts, F2V}) -> 1235 {X1,Y1,Z1} = e3d_vec:sub(lists:nth(Vi2,Coords), lists:nth(Vi1,Coords)), 1236 {X2,Y2,Z2} = e3d_vec:sub(lists:nth(Vi3,Coords), lists:nth(Vi1,Coords)), 1237 {U1,V1,_} = lists:nth(Vi1, UVCoords), 1238 {U2,V2,_} = lists:nth(Vi2, UVCoords), 1239 {U3,V3,_} = lists:nth(Vi3, UVCoords), 1240 S1 = U2-U1, 1241 S2 = U3-U1, 1242 T1 = V2-V1, 1243 T2 = V3-V1, 1244 Vs = [lists:nth(Vi1, VsIds),lists:nth(Vi2,VsIds),lists:nth(Vi3,VsIds)], 1245 try 1246 F = 1.0 / (S1*T2 - S2*T1), 1247 Tangent = {F*(T2*X1-T1*X2), F*(T2*Y1-T1*Y2), F*(T2*Z1-T1*Z2)}, 1248 BiTangent = {F*(S1*X2-S2*X1), F*(S1*Y2-S2*Y1), F*(S1*Z2-S2*Z1)}, 1249 H = case e3d_vec:dot(e3d_vec:cross(N, Tangent), BiTangent) < 0.0 of 1250 true -> 1; 1251 false -> -1 1252 end, 1253 Tss0 = {add_tangent(Vs, Tangent, BiTangent, Ts), [{N,H,Vs}|F2V]}, 1254 calc_tang_vecs_1(Vis, VsIds, Coords, UVCoords, N, Tss0) 1255 catch _:badarith -> 1256 Tss1 = {Ts, [{N,0,Vs}|F2V]}, 1257 calc_tang_vecs_1(Vis, VsIds, Coords, UVCoords, N, Tss1) 1258 end; 1259calc_tang_vecs_1([], _, _, _, _, Tss) -> 1260 Tss. 1261 1262add_tangent([V|Vs], Tangent, BiTangent, Ts) -> 1263 {T0, B0} = array:get(V,Ts), 1264 T1 = e3d_vec:add(T0, Tangent), 1265 T2 = e3d_vec:add(B0, BiTangent), 1266 add_tangent(Vs, Tangent, BiTangent, array:set(V, {T1, T2}, Ts)); 1267add_tangent([], _, _, Ts) -> Ts. 1268 1269build_tangents({VsTs0, RevF2V}) -> 1270 VsTs = array:map(fun(_V, {T, BT}) -> {e3d_vec:norm(T), e3d_vec:norm(BT)} end, VsTs0), 1271 build_tangents(lists:reverse(RevF2V), VsTs, <<>>). 1272 1273build_tangents([{N, H, Face}|Fs], Ts, Bin0) -> 1274 Bin = add_tangents1(Face, Ts, H, N, undefined, Bin0), 1275 build_tangents(Fs, Ts, Bin); 1276build_tangents([], _, Bin) -> Bin. 1277 1278add_tangents1([V|Vs], Ts, H0, N, Prev, Bin0) -> 1279 case array:get(V, Ts) of 1280 {{0.0, 0.0, 0.0}, BiT} -> 1281 {Tan = {X,Y,Z}, H} = get_tangent(Prev, BiT, H0, N), 1282 Bin = <<Bin0/binary, X:?F32,Y:?F32,Z:?F32, H:?F32>>, 1283 add_tangents1(Vs, Ts, H, N, Tan, Bin); 1284 {Tan = {X,Y,Z}, _} when H0 /= 0 -> 1285 Bin = <<Bin0/binary, X:?F32,Y:?F32,Z:?F32, H0:?F32>>, 1286 add_tangents1(Vs, Ts, H0, N, Tan, Bin); 1287 {Tan = {X,Y,Z}, BiT} -> 1288 H = case e3d_vec:dot(e3d_vec:cross(N, Tan), BiT) < 0.0 of 1289 true -> 1; 1290 false -> -1 1291 end, 1292 Bin = <<Bin0/binary, X:?F32,Y:?F32,Z:?F32, H:?F32>>, 1293 add_tangents1(Vs, Ts, H, N, Tan, Bin) 1294 end; 1295add_tangents1([], _, _, _, _, Bin) -> Bin. 1296 1297get_tangent(undefined, {0.0,0.0,0.0}, H0, N) -> 1298 H = if H0 =:= 0 -> -1; true -> H0 end, 1299 {cross_axis(N), H}; 1300get_tangent(undefined, BiT, 0, N) -> 1301 T = e3d_vec:cross(BiT, N), 1302 H = case e3d_vec:dot(e3d_vec:cross(N, T), BiT) < 0.0 of 1303 true -> 1; 1304 false -> -1 1305 end, 1306 {T, H}; 1307get_tangent(undefined, BiT, H, N) -> 1308 {e3d_vec:cross(BiT, N), H}; 1309get_tangent(Prev, _, H, _) -> 1310 {Prev, H}. 1311 1312cross_axis(N = {NX,NY,NZ}) -> 1313 try 1314 V2 = case abs(NX) > abs(NY) of 1315 true -> 1316 ILen = 1.0 / math:sqrt(NX*NX+NZ*NZ), 1317 {-NZ*ILen, 0.0, NX * ILen}; 1318 false -> 1319 ILen = 1.0 / math:sqrt(NY*NY+NZ*NZ), 1320 {0.0, NZ*ILen, -NY * ILen} 1321 end, 1322 e3d_vec:cross(N, V2) 1323 catch _:badarith -> 1324 {1.0, 0.0,0.0} 1325 end. 1326 1327%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1328 1329fix_vc(Vs, Face, We, Vmap) -> 1330 try 1331 Uvc = wings_va:face_attr([vertex|color], Face, We), 1332 fix_vc1(Vs, Uvc, Vmap, []) 1333 catch error:_ -> 1334 fix_vc1(Vs, [], Vmap, []) 1335 end. 1336 1337fix_vc1([V|Vs], Uvc, Vmap, Acc) -> 1338 Val = case find(map_vertex(V, Vmap), Uvc) of 1339 {_,_,_}=Color -> Color; 1340 _ -> {1.0,1.0,1.0} 1341 end, 1342 fix_vc1(Vs, Uvc, Vmap, [Val|Acc]); 1343fix_vc1([], _, _, Acc) -> reverse(Acc). 1344 1345find(V, [[V|Info]|_R]) -> Info; 1346find(V, [_|R]) -> find(V,R); 1347find(_, []) -> none. 1348 1349n_zip([[V|_]|R1],[N|R2]) -> 1350 [[V|N]|n_zip(R1,R2)]; 1351n_zip([],[]) -> []. 1352 1353 1354fix(OK = {_,_,_}, false) -> OK; 1355fix(OK = {_,_,_,_}, true) -> OK; 1356fix({R,G,B,_}, false) -> {R,G,B}; 1357fix({R,G,B}, true) -> {R,G,B,1.0}. 1358 1359setup_normals(We = #we{fs=Ftab}) -> 1360 FN0 = [{Face,wings_face:normal(Face, We)} || Face <- gb_trees:keys(Ftab)], 1361 Ns = wings_we:normals(FN0, We, none), 1362 gb_trees:from_orddict(sort(Ns)). 1363 1364outer_verts(We = #we{es=Etab}) -> 1365 Fs = wings_we:visible(We), 1366 Outer = auv_util:outer_edges(Fs,We,false), 1367 Verts = fun({Edge,Face}) -> 1368 #edge{vs=Va,ve=Vb} = array:get(Edge, Etab), 1369 [Va,Vb,Face] 1370 end, 1371 lists:map(Verts, Outer). 1372 1373%% Start with 64 bytes so that binary will be reference counted 1374%% and not on the process heap spent hours debugging this.. :-( 1375to_bin(List, uv) -> to_bin3to2(List,[<<0:512>>]); 1376to_bin(List, pos) -> to_bin3(List,[<<0:512>>]); 1377to_bin(List, vertex) -> to_bin3(List,[<<0:512>>]). %% Vertex colors 1378 1379to_bin3([{A,B,C}|R],Acc) -> 1380 to_bin3(R,[<<A:32/native-float,B:32/native-float,C:32/native-float>>|Acc]); 1381to_bin3([],Acc) -> list_to_binary(Acc). 1382 1383to_bin3to2([{A,B,_}|R],Acc) -> 1384 to_bin3to2(R,[<<A:32/native-float,B:32/native-float>>|Acc]); 1385to_bin3to2([],Acc) -> list_to_binary(Acc). 1386 1387%% Workaround for ATI: gl:'end' doesn't bite for line loop/strip... 1388vs_lines([A|R=[B|_]],Last) -> 1389 [A,B|vs_lines(R,Last)]; 1390vs_lines([B],Last) -> 1391 [B,Last]. 1392 1393%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1394%% Builtin Shader Passes 1395%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1396 1397get_passes(Passes,Shaders) -> 1398 lists:foldr(fun(Pass,Acc) -> 1399 case pass(Pass,Shaders) of 1400 Fun when is_function(Fun) -> 1401 [Fun|Acc]; 1402 _ -> 1403 Acc 1404 end 1405 end, [], Passes). 1406 1407pass({ignore,_},_) -> ignore; 1408pass({auv_background,[auv_background, {type_sel,color},_,Color]},_) -> 1409 fun(_Geom,_) -> 1410 {R,G,B,A} = fix(Color,true), 1411 gl:clearColor(R,G,B,A), 1412 gl:clear(?GL_COLOR_BUFFER_BIT bor ?GL_DEPTH_BUFFER_BIT) 1413 end; 1414pass({auv_background,[auv_background, {type_sel,image},{_Name,Id},Color]},_) -> 1415 fun(_Geom,_) -> 1416 {R,G,B,A} = fix(Color,true), 1417 gl:clearColor(R,G,B,A), 1418 gl:clear(?GL_COLOR_BUFFER_BIT bor ?GL_DEPTH_BUFFER_BIT), 1419 case wings_image:txid(Id) of 1420 none -> ignore; 1421 TxId -> 1422 gl:enable(?GL_TEXTURE_2D), 1423 gl:bindTexture(?GL_TEXTURE_2D, TxId), 1424 draw_texture_square(), 1425 gl:disable(?GL_TEXTURE_2D) 1426 end 1427 end; 1428pass({auv_background, _},Sh) -> 1429 pass({auv_background, ?OPT_BG},Sh); 1430 1431pass({auv_edges, [auv_edges,all_edges,Color,Width,_UseMat]},_) -> 1432 R=fun(#chart{fs=Fs}) -> 1433 Draw = fun(#fs{vse=Vs}) -> 1434 Patched = vs_lines(Vs,hd(Vs)), 1435 wings_gl:drawElements(?GL_LINES,length(Patched), 1436 ?GL_UNSIGNED_INT,Patched) 1437 end, 1438 foreach(Draw,Fs) 1439 end, 1440 fun(#ts{vbo={call,_,{vbo,Vbo}}, charts=Charts},_) -> 1441 gl:disable(?GL_DEPTH_TEST), 1442 gl:color3fv(Color), 1443 gl:lineWidth(float(Width)), 1444 gl:bindBuffer(?GL_ARRAY_BUFFER, Vbo), 1445 gl:vertexPointer(2, ?GL_FLOAT, 0, 0), 1446 gl:enableClientState(?GL_VERTEX_ARRAY), 1447 foreach(R, Charts), 1448 gl:disableClientState(?GL_VERTEX_ARRAY) 1449 end; 1450pass({auv_edges, [auv_edges,border_edges,Color,Width,UseMat]},_) -> 1451 R= fun(#chart{oes=Es0}) -> 1452 Es = foldl(fun([A,B,_],Acc) -> [A,B|Acc] end, [], Es0), 1453 wings_gl:drawElements(?GL_LINES,length(Es),?GL_UNSIGNED_INT,Es) 1454 end, 1455 fun(#ts{vbo={call,_,{vbo,Vbo}}, charts=Charts},_) -> 1456 gl:color3fv(Color), 1457 gl:lineWidth(float(Width)), 1458 gl:bindBuffer(?GL_ARRAY_BUFFER, Vbo), 1459 gl:vertexPointer(2, ?GL_FLOAT, 0, 0), 1460 gl:enableClientState(?GL_VERTEX_ARRAY), 1461 gl:disable(?GL_DEPTH_TEST), 1462 case UseMat of 1463 true -> 1464 gl:enableClientState(?GL_COLOR_ARRAY), 1465 foreach(R, Charts), 1466 gl:disableClientState(?GL_COLOR_ARRAY); 1467 _ -> 1468 foreach(R, Charts) 1469 end, 1470 gl:disableClientState(?GL_VERTEX_ARRAY) 1471 end; 1472pass({auv_edges, _},Sh) -> 1473 pass({auv_edges, ?OPT_EDGES},Sh); 1474 1475pass({auv_faces,[_]},_) -> 1476 fun(#ts{vbo={call,EnableVbo,{vbo,_Vbo}}, charts=Charts}, _) -> 1477 gl:disable(?GL_DEPTH_TEST), 1478 gl:disable(?GL_ALPHA_TEST), 1479 gl:enable(?GL_BLEND), 1480 gl:blendFunc(?GL_SRC_ALPHA, ?GL_ONE_MINUS_SRC_ALPHA), 1481 R = fun(#fs{vs=Vs}) -> 1482 wings_gl:drawElements(?GL_TRIANGLES,length(Vs),?GL_UNSIGNED_INT,Vs) 1483 end, 1484 All = fun(_) -> foreach(fun(#chart{fs=Fs}) -> foreach(R, Fs) end, Charts) end, 1485 EnableVbo(All, #{}) 1486 end; 1487pass({auv_faces, _},Sh) -> 1488 pass({auv_faces,?OPT_FACES},Sh); 1489 1490pass({{shader,Id}, Opts},{Sh,Compiled}) -> 1491 shader_pass(lists:keysearch(Id,#sh.id,Sh), 1492 lists:keysearch(Id,1,Compiled),Opts); 1493pass({_R, _O},_) -> 1494 io:format("AUV: ~p:~p: Unknown Render Pass (~p) or options (~p) ~n", 1495 [?MODULE,?LINE,_R,_O]), 1496 ignore. 1497 1498shader_pass(Shader={value,#sh{id=Id, def=Def}},Prog,[]) when Def /= [] -> 1499 shader_pass(Shader, Prog, [{shader,Id}|reverse(Def)]); 1500shader_pass(_,false,_) -> 1501 io:format("AUV: No shader program found skipped ~p~n", [?LINE]), 1502 ignore; 1503shader_pass(false,_,_) -> 1504 io:format("AUV: Not shader found skipped ~p~n", [?LINE]), 1505 ignore; 1506shader_pass({value,#sh{id=Id, args=Args, tex_units=TexUnits}}, 1507 {value,{_,Prog}}, [{shader,Id}|Opts]) -> 1508 fun(Ts = #ts{vbo={call,EnableVbo,_}, charts=Charts}, Config) -> 1509 gl:disable(?GL_DEPTH_TEST), 1510 gl:disable(?GL_ALPHA_TEST), 1511 gl:enable(?GL_BLEND), 1512 gl:blendFunc(?GL_SRC_ALPHA, ?GL_ONE_MINUS_SRC_ALPHA), 1513 wings_gl:use_prog(Prog), 1514 try 1515 Conf = Config#sh_conf{prog=Prog,ts=Ts}, 1516 PerChartUniF = shader_uniforms(reverse(Args),Opts,Conf), 1517 case send_texture(reverse(Args),Opts) of 1518 true -> 1519 gl:enable(?GL_TEXTURE_2D), 1520 gl:disable(?GL_BLEND), 1521 draw_texture_square(); 1522 _ -> 1523 DC = fun(Chart = #chart{fs=Fas}) -> 1524 PerFaceUniF = sh_uniforms(PerChartUniF,Chart,Conf), 1525 R = fun(Face = #fs{vs=Vss}) -> 1526 sh_uniforms(PerFaceUniF,Face,Conf), 1527 wings_gl:drawElements(?GL_TRIANGLES,length(Vss), 1528 ?GL_UNSIGNED_INT,Vss) 1529 end, 1530 foreach(R,Fas) 1531 end, 1532 EnableVbo(fun(_) -> foreach(DC, Charts) end, #{}) 1533 end 1534 catch throw:What -> 1535 io:format("AUV: ERROR ~s ~n",[What]); 1536 _:What:Stack -> 1537 io:format("AUV: Internal ERROR ~p:~n~p ~n",[What,Stack]) 1538 after 1539 wings_gl:use_prog(0), 1540 gl:disable(?GL_TEXTURE_2D), 1541 gl:disableClientState(?GL_VERTEX_ARRAY), 1542 gl:disableClientState(?GL_NORMAL_ARRAY), 1543 gl:clientActiveTexture(?GL_TEXTURE2), 1544 gl:disableClientState(?GL_TEXTURE_COORD_ARRAY), 1545 gl:clientActiveTexture(?GL_TEXTURE1), 1546 gl:disableClientState(?GL_TEXTURE_COORD_ARRAY), 1547 gl:clientActiveTexture(?GL_TEXTURE0), 1548 disable_tex_units(TexUnits), 1549 gl:activeTexture(?GL_TEXTURE0) 1550 end 1551 end. 1552 1553send_texture([{auv,{auv_send_texture,_, _Def0}}|_], 1554 [{auv_send_texture,Def}|_]) -> 1555 Def; 1556send_texture([{auv,{auv_send_texture,_, _Def0}}|_], 1557 [Def|_]) -> 1558 Def; 1559send_texture([{auv,_}|Next], Opts) -> 1560 send_texture(Next,Opts); 1561send_texture([_|Next], [_|Opts]) -> 1562 send_texture(Next,Opts); 1563send_texture([],_) -> false. 1564 1565 1566shader_uniforms([A|As], [O|Opts]=Opts0, Conf) -> 1567 Res = shader_uniform(A,O,Conf), 1568 ?ERROR == error andalso io:format("~p~n", [A]), 1569 case Res of 1570 ok -> shader_uniforms(As,Opts,Conf); 1571 no_opt -> shader_uniforms(As,Opts0,Conf); 1572 {keep, Keep} -> [Keep|shader_uniforms(As,Opts0,Conf)] 1573 end; 1574shader_uniforms([], [], _) -> 1575 []. 1576 1577shader_uniform({uniform,color,Name,_,_}, Val, Conf) -> 1578 wings_gl:set_uloc(Conf#sh_conf.prog, Name,Val), 1579 ok; 1580shader_uniform({uniform,float,Name,_,_},Val,Conf) -> 1581 wings_gl:set_uloc(Conf#sh_conf.prog, Name, Val), 1582 ok; 1583shader_uniform({uniform,menu,Name,_,_}, Vals,Conf) 1584 when is_list(Vals) -> 1585 Loc = wings_gl:uloc(Conf#sh_conf.prog,Name), 1586 foldl(fun(Val,Cnt) when is_integer(Val) -> 1587 gl:uniform1i(Loc+Cnt,Val),Cnt+1; 1588 (Val,Cnt) -> 1589 gl:uniform1f(Loc+Cnt,Val),Cnt+1 1590 end,0,Vals), 1591 ok; 1592shader_uniform({uniform,menu,Name,_,_},Vals,Conf) 1593 when is_integer(Vals) -> 1594 Loc = wings_gl:uloc(Conf#sh_conf.prog,Name), 1595 gl:uniform1i(Loc,Vals), 1596 ok; 1597shader_uniform({uniform,bool,Name,_,_},Val,Conf) -> 1598 Loc = wings_gl:uloc(Conf#sh_conf.prog,Name), 1599 BoolF = if Val -> 1.0; true -> 0.0 end, 1600 gl:uniform1f(Loc, BoolF), 1601 ok; 1602shader_uniform({uniform,{slider,_,_},Name,_,_},Val,Conf) -> 1603 Loc = wings_gl:uloc(Conf#sh_conf.prog,Name), 1604 if is_integer(Val) -> 1605 gl:uniform1i(Loc,Val); 1606 true -> 1607 gl:uniform1f(Loc,Val) 1608 end, 1609 ok; 1610shader_uniform({uniform,{image,Unit},Name,_,_},{_,Id},Conf) -> 1611 Loc = wings_gl:uloc(Conf#sh_conf.prog,Name), 1612 gl:activeTexture(?GL_TEXTURE0 + Unit), 1613 TxId = wings_image:txid(Id), 1614 gl:bindTexture(?GL_TEXTURE_2D, TxId), 1615 gl:uniform1i(Loc, Unit), 1616 ok; 1617shader_uniform({auv,{auv_bg,Unit}},_Opts,Conf) -> 1618 Loc = wings_gl:uloc(Conf#sh_conf.prog, "auv_bg"), 1619 gl:activeTexture(?GL_TEXTURE0), 1620 gl:bindTexture(?GL_TEXTURE_2D, Conf#sh_conf.fbo_r), 1621 gl:texParameteri(?GL_TEXTURE_2D, ?GL_TEXTURE_MAG_FILTER, ?GL_NEAREST), 1622 gl:texParameteri(?GL_TEXTURE_2D, ?GL_TEXTURE_MIN_FILTER, ?GL_NEAREST), 1623 gl:uniform1i(Loc, Unit), 1624 no_opt; 1625shader_uniform({auv,auv_texsz},_Opts,Conf = #sh_conf{texsz={W,H}}) -> 1626 Loc = wings_gl:uloc(Conf#sh_conf.prog,"auv_texsz"), 1627 gl:uniform2f(Loc,float(W),float(H)), 1628 no_opt; 1629shader_uniform({auv,{auv_send_texture,_,_}},_Val,_Conf) -> 1630 ok; 1631shader_uniform({auv,auv_bbpos3d},_Opts,Conf) -> 1632 Loc = wings_gl:uloc(Conf#sh_conf.prog,"auv_bbpos3d"), 1633 [{MinX,MinY,MinZ},{MaxX,MaxY,MaxZ}] = (Conf#sh_conf.ts)#ts.bb, 1634 gl:uniform3f(Loc,MinX,MinY,MinZ), 1635 gl:uniform3f(Loc+1,MaxX,MaxY,MaxZ), 1636 no_opt; 1637shader_uniform({auv,What},_Opts,_Conf) -> 1638 {keep, What}. 1639 1640%% Per Face or per Chart uniforms 1641sh_uniforms([auv_bbpos2d|R],Chart=#chart{bb_uv=BB},Conf) -> 1642 Loc = wings_gl:uloc(Conf#sh_conf.prog,"auv_bbpos2d"), 1643 [{MinX,MinY,_},{MaxX,MaxY,_}] = BB, 1644 gl:uniform2f(Loc,MinX,MinY), 1645 gl:uniform2f(Loc+1,MaxX,MaxY), 1646 sh_uniforms(R,Chart,Conf); 1647sh_uniforms([_|R],Chart,Conf) -> 1648 sh_uniforms(R,Chart,Conf); 1649sh_uniforms([],_,_) -> 1650 []. 1651 1652disable_tex_units(Unit) when Unit > 0 -> 1653 gl:activeTexture(?GL_TEXTURE0 + Unit-1), 1654 gl:bindTexture(?GL_TEXTURE_2D, 0), 1655 disable_tex_units(Unit-1); 1656disable_tex_units(_) -> ok. 1657 1658delete_shaders(List) -> 1659 foreach(fun({_,Prog}) -> gl:deleteProgram(Prog) end, List). 1660 1661get_requirements(Shaders) -> 1662 lists:foldl(fun(#sh{reqs=List},Acc) -> 1663 List ++ Acc 1664 end, [], Shaders). 1665 1666%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1667%% Shader loading/handling 1668%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1669 1670shaders() -> 1671 case have_shaders() of 1672 true -> load_shaders_cfg(); 1673 false -> [] 1674 end. 1675 1676have_shaders() -> 1677 wings_gl:support_shaders() andalso wings_gl:is_ext('GL_EXT_framebuffer_object'). 1678 1679load_shaders_cfg() -> 1680 Path = filename:dirname(code:which(?MODULE)), 1681 Files = filelib:wildcard("wpc_*.auv", Path), 1682 lists:keysort(#sh.name, load_configs(Files,Path, [])). 1683 1684load_configs([Name|Fs], Path, Acc) -> 1685 File = filename:join(Path,Name), 1686 case file:consult(File) of 1687 {ok,Info} -> 1688 Id = list_to_atom(filename:basename(Name,".auv")++"_auv"), 1689 Sh = #sh{file=File,id=Id}, 1690 load_configs(Fs,Path,parse_sh_info(Info,Sh,1,Acc)); 1691 Other -> 1692 io:format("AUV: Couldn't load ~p ~n",[File]), 1693 io:format(" Error: ~p ~n",[Other]), 1694 load_configs(Fs,Path,Acc) 1695 end; 1696load_configs([],_Path,Acc) -> 1697 Acc. 1698 1699parse_sh_info([{name,Name}|Opts],Sh,NI,Acc) -> 1700 parse_sh_info(Opts, Sh#sh{name=Name},NI,Acc); 1701parse_sh_info([{vertex_shader,Name}|Opts],Sh,NI,Acc) -> 1702 parse_sh_info(Opts, Sh#sh{vs=Name},NI, Acc); 1703parse_sh_info([{fragment_shader,Name}|Opts],Sh,NI,Acc) -> 1704 parse_sh_info(Opts, Sh#sh{fs=Name},NI, Acc); 1705parse_sh_info([{requires,List}|Opts],Sh,NI,Acc) when is_list(List) -> 1706 parse_sh_info(Opts, Sh#sh{reqs=List},NI, Acc); 1707parse_sh_info([{preview,Opt}|Opts],Sh,NI,Acc) -> 1708 parse_sh_info(Opts, Sh#sh{preview=(Opt=/=no)},NI, Acc); 1709parse_sh_info([{auv,auv_bg}|Opts],Sh,NI,Acc) -> 1710 What = {auv,{auv_bg,0}}, 1711 parse_sh_info(Opts, Sh#sh{args=[What|Sh#sh.args]},NI,Acc); 1712parse_sh_info([What={auv,auv_texsz}|Opts],Sh,NI,Acc) -> 1713 parse_sh_info(Opts, Sh#sh{args=[What|Sh#sh.args]},NI,Acc); 1714parse_sh_info([What={auv,auv_bbpos2d}|Opts],Sh,NI,Acc) -> 1715 parse_sh_info(Opts, Sh#sh{args=[What|Sh#sh.args]},NI,Acc); 1716parse_sh_info([What={auv,auv_bbpos3d}|Opts],Sh,NI,Acc) -> 1717 parse_sh_info(Opts, Sh#sh{args=[What|Sh#sh.args]},NI,Acc); 1718parse_sh_info([What={auv,{auv_send_texture,L,Def}}|Opts],Sh,NI,Acc) 1719 when is_list(L) -> 1720 parse_sh_info(Opts, Sh#sh{args=[What|Sh#sh.args],def=[Def|Sh#sh.def]}, 1721 NI,Acc); 1722parse_sh_info([What={uniform,color,_,Def={_,_,_,_},_}|Opts],Sh,NI,Acc) -> 1723 parse_sh_info(Opts, Sh#sh{args=[What|Sh#sh.args],def=[Def|Sh#sh.def]}, 1724 NI,Acc); 1725parse_sh_info([What={uniform,float,_,Def,_}|Opts],Sh,NI,Acc) 1726 when is_number(Def) -> 1727 parse_sh_info(Opts, Sh#sh{args=[What|Sh#sh.args],def=[Def|Sh#sh.def]}, 1728 NI,Acc); 1729parse_sh_info([What={uniform,bool,_,Def,_}|Opts],Sh,NI,Acc) -> 1730 parse_sh_info(Opts, Sh#sh{args=[What|Sh#sh.args],def=[Def|Sh#sh.def]}, 1731 NI,Acc); 1732parse_sh_info([{uniform,image,Id,Def0,Str}|Opts],Sh,NI,Acc) -> 1733 %% default value must to be an image valid. It can be a file name or an internal 1734 %% image name. Otherwise, it will be created an background image will be used 1735 Def = load_image(Id, Def0), 1736 What = {uniform,{image,NI},Id,Def,Str}, 1737 parse_sh_info(Opts, Sh#sh{args=[What|Sh#sh.args],def=[Def|Sh#sh.def]}, 1738 NI+1,Acc); 1739parse_sh_info([What={uniform,{slider,F,T},_,Def,_}|Opts],Sh,NI,Acc) 1740 when is_number(F),is_number(T),is_number(Def) -> 1741 parse_sh_info(Opts, Sh#sh{args=[What|Sh#sh.args],def=[Def|Sh#sh.def]}, 1742 NI,Acc); 1743parse_sh_info([What={uniform,menu,_,DefKey,List}|Opts],Sh,NI,Acc) -> 1744 case lists:keysearch(DefKey,1, List) of 1745 {value, {_,Def}} -> 1746 parse_sh_info(Opts, Sh#sh{args=[What|Sh#sh.args], 1747 def=[Def|Sh#sh.def]},NI,Acc); 1748 false -> 1749 io:format("AUV: ~p Bad default value ignored menu ~p ~n", 1750 [Sh#sh.file,What]), 1751 parse_sh_info(Opts,Sh,NI,Acc) 1752 end; 1753parse_sh_info([_Error|Opts],Sh,NI,Acc) -> 1754 io:format("AUV: In ~p Unknown shader opt ignored ~p", 1755 [Sh#sh.file,_Error]), 1756 parse_sh_info(Opts,Sh,NI,Acc); 1757parse_sh_info([],Sh,NI,Acc) -> 1758 %% Verify shader here 1759 [Sh#sh{tex_units=NI}|Acc]. 1760 1761%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1762%%% Shader compilation 1763%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1764 1765compile_shaders(Passes, Available) -> 1766 foldl(fun({{shader,Id},_},Acc) -> 1767 case lists:keysearch(Id,1,Acc) of 1768 {value, _} -> Acc; % Already compiled 1769 false -> 1770 compile_shader(Id,lists:keysearch(Id,#sh.id,Available),Acc) 1771 end; 1772 (_NormalPass,Acc) -> 1773 Acc 1774 end, [], Passes). 1775 1776compile_shader(Id, {value,#sh{name=Name,vs=VsF,fs=FsF}}, Acc) -> 1777 try 1778 Vs = wings_gl:compile(vertex, read_file(VsF)), 1779 Fs = wings_gl:compile(fragment, read_file(FsF)), 1780 Prog = wings_gl:link_prog([Vs,Fs]), 1781 %% io:format("AUV: Shader ´~s´ ok~n", [Name]), 1782 [{Id,Prog}|Acc] 1783 catch throw:What -> 1784 io:format("AUV: Error ~p ~s ~n",[Name, What]), 1785 Acc; 1786 _:Err:Stack -> 1787 io:format("AUV: Internal Error ~s in~n ~p~n",[Err,Stack]), 1788 Acc 1789 end; 1790compile_shader(Id, false, Acc) -> 1791 io:format("AUV: Error did not find shader ~p ~n",[Id]), 1792 Acc. 1793 1794read_file(Name) -> 1795 Path = filename:dirname(code:which(?MODULE)), 1796 File = filename:join(Path,Name), 1797 case file:read_file(File) of 1798 {ok, Bin} -> Bin; 1799 _ -> throw("Couldn't read file: " ++ File) 1800 end. 1801