diff options
Diffstat (limited to 'release')
52 files changed, 1434 insertions, 177 deletions
diff --git a/release/bin/blender-thumbnailer.py b/release/bin/blender-thumbnailer.py index d3b31d63fad..fe5d462bba9 100755 --- a/release/bin/blender-thumbnailer.py +++ b/release/bin/blender-thumbnailer.py @@ -88,9 +88,8 @@ def blend_extract_thumb(path): import os open_wrapper = open_wrapper_get() - # def MAKE_ID(tag): ord(tag[0])<<24 | ord(tag[1])<<16 | ord(tag[2])<<8 | ord(tag[3]) - REND = 1145980242 # MAKE_ID(b'REND') - TEST = 1414743380 # MAKE_ID(b'TEST') + REND = b'REND' + TEST = b'TEST' blendfile = open_wrapper(path, 'rb') @@ -116,7 +115,8 @@ def blend_extract_thumb(path): return None, 0, 0 sizeof_bhead = 24 if is_64_bit else 20 - int_endian_pair = '>ii' if is_big_endian else '<ii' + int_endian = '>i' if is_big_endian else '<i' + int_endian_pair = int_endian + 'i' while True: bhead = blendfile.read(sizeof_bhead) @@ -124,7 +124,8 @@ def blend_extract_thumb(path): if len(bhead) < sizeof_bhead: return None, 0, 0 - code, length = struct.unpack(int_endian_pair, bhead[0:8]) # 8 == sizeof(int) * 2 + code = bhead[:4] + length = struct.unpack(int_endian, bhead[4:8])[0] # 4 == sizeof(int) if code == REND: blendfile.seek(length, os.SEEK_CUR) diff --git a/release/datafiles/blender_icons.svg b/release/datafiles/blender_icons.svg index 94784a73782..40b974661fa 100644 --- a/release/datafiles/blender_icons.svg +++ b/release/datafiles/blender_icons.svg @@ -30748,6 +30748,215 @@ offset="1" id="stop16000-1" /> </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient16023" + id="radialGradient16031" + cx="139" + cy="137.5" + fx="139" + fy="137.5" + r="7" + gradientTransform="matrix(1.9285714,0,0,0.5,-129.07143,68.75)" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + id="linearGradient16023"> + <stop + style="stop-color:#d8d8d8;stop-opacity:1" + offset="0" + id="stop16025" /> + <stop + style="stop-color:#9a9a9a;stop-opacity:1" + offset="1" + id="stop16027" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient16023" + id="radialGradient14148" + cx="139" + cy="137.5" + fx="139" + fy="137.5" + r="7" + gradientTransform="matrix(1.9285714,0,0,0.5,-129.07143,68.75)" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient16023" + id="radialGradient14156" + cx="139" + cy="137.5" + fx="139" + fy="137.5" + r="7" + gradientTransform="matrix(1.9285714,0,0,0.5,-129.07143,68.75)" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient16023" + id="radialGradient14164" + cx="139" + cy="137.5" + fx="139" + fy="137.5" + r="7" + gradientTransform="matrix(1.9285714,0,0,0.5,-129.07143,68.75)" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient16023" + id="radialGradient14172" + cx="139" + cy="137.5" + fx="139" + fy="137.5" + r="7" + gradientTransform="matrix(1.9285714,0,0,0.5,-129.07143,68.75)" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient16023" + id="radialGradient14180" + cx="139" + cy="137.5" + fx="139" + fy="137.5" + r="7" + gradientTransform="matrix(1.9285714,0,0,0.5,-129.07143,68.75)" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient15984" + id="radialGradient15990" + cx="139" + cy="137.5" + fx="139" + fy="137.5" + r="1" + gradientTransform="matrix(14.5,0,0,5.5,-1876.5,-618.75)" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + id="linearGradient15984"> + <stop + style="stop-color:#ffffff;stop-opacity:1" + offset="0" + id="stop15986" /> + <stop + style="stop-color:#a8cafb;stop-opacity:1" + offset="1" + id="stop15988" /> + </linearGradient> + <radialGradient + r="7" + fy="137.5" + fx="139" + cy="137.5" + cx="139" + gradientTransform="matrix(1.9285714,0,0,0.5,-129.07143,68.75)" + gradientUnits="userSpaceOnUse" + id="radialGradient14206" + xlink:href="#linearGradient16023" + inkscape:collect="always" /> + <radialGradient + r="7" + fy="137.5" + fx="139" + cy="137.5" + cx="139" + gradientTransform="matrix(1.9285714,0,0,0.5,-129.07143,68.75)" + gradientUnits="userSpaceOnUse" + id="radialGradient14208" + xlink:href="#linearGradient16023" + inkscape:collect="always" /> + <radialGradient + r="7" + fy="137.5" + fx="139" + cy="137.5" + cx="139" + gradientTransform="matrix(1.9285714,0,0,0.5,-129.07143,68.75)" + gradientUnits="userSpaceOnUse" + id="radialGradient14210" + xlink:href="#linearGradient16023" + inkscape:collect="always" /> + <radialGradient + r="7" + fy="137.5" + fx="139" + cy="137.5" + cx="139" + gradientTransform="matrix(1.9285714,0,0,0.5,-129.07143,68.75)" + gradientUnits="userSpaceOnUse" + id="radialGradient14212" + xlink:href="#linearGradient16023" + inkscape:collect="always" /> + <radialGradient + r="7" + fy="137.5" + fx="139" + cy="137.5" + cx="139" + gradientTransform="matrix(1.9285714,0,0,0.5,-129.07143,68.75)" + gradientUnits="userSpaceOnUse" + id="radialGradient14214" + xlink:href="#linearGradient16023" + inkscape:collect="always" /> + <radialGradient + r="1" + fy="137.5" + fx="139" + cy="137.5" + cx="139" + gradientTransform="matrix(14.5,0,0,5.5,-1876.5,-618.75)" + gradientUnits="userSpaceOnUse" + id="radialGradient14216" + xlink:href="#linearGradient15984" + inkscape:collect="always" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient16023" + id="radialGradient16945" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.9285714,0,0,0.5,-129.59176,68.75)" + cx="139" + cy="137.5" + fx="139" + fy="137.5" + r="7" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient16023" + id="radialGradient16947" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.9285714,0,0,0.5,-128.51641,68.75)" + cx="139" + cy="137.5" + fx="139" + fy="137.5" + r="7" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient1610" + id="linearGradient31208" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-41,98)" + x1="-170.25" + y1="65.5" + x2="-181.375" + y2="65.5" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient319" + id="linearGradient31210" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.2857143,0,0,0.787037,-92.714287,177.80092)" + x1="-101" + y1="-16" + x2="-93.75" + y2="-16.264704" /> </defs> <sodipodi:namedview id="base" @@ -91625,8 +91834,146 @@ style="opacity:0.51799999;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" d="m 316.57643,242.95337 c 0,0.33872 -0.26469,0.6133 -0.5912,0.6133 -0.32651,0 -0.5912,-0.27458 -0.5912,-0.6133 0,-0.33872 0.26469,-0.6133 0.5912,-0.6133 0.32651,0 0.5912,0.27458 0.5912,0.6133 z" /> </g> + <g + transform="translate(189.00803,-79.37555)" + style="display:inline;enable-background:new" + id="g14119"> + <g + id="g16949"> + <rect + style="color:#000000;fill:#272727;fill-opacity:1;stroke:none;stroke-width:1.20000005;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new" + id="rect15992" + width="2" + height="7" + x="134.47968" + y="134" + ry="1" /> + <rect + style="color:#000000;fill:#272727;fill-opacity:1;stroke:none;stroke-width:1.20000005;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new" + id="rect15994" + width="2" + height="7" + x="141.55502" + y="134" + ry="1" /> + <rect + ry="1" + y="136" + x="131.25" + height="3" + width="15.5" + id="rect15926" + style="color:#000000;fill:#272727;fill-opacity:1;stroke:none;stroke-width:1.20000005;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new" /> + <rect + ry="1" + y="132.25" + x="131.25" + height="10.5" + width="2.5" + id="rect15930" + style="color:#000000;fill:#272727;fill-opacity:1;stroke:none;stroke-width:1.20000005;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new" /> + <rect + ry="1" + y="132.25" + x="144.25" + height="10.5" + width="2.5" + id="rect15938" + style="color:#000000;fill:#272727;fill-opacity:1;stroke:none;stroke-width:1.20000005;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new" /> + <g + id="g16016-6" + style="fill:url(#radialGradient16031);fill-opacity:1"> + <rect + style="color:#000000;fill:url(#radialGradient14206);fill-opacity:1;stroke:none;stroke-width:1.20000005;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new" + id="rect15946" + width="1" + height="9" + x="132" + y="133" + ry="0.5" /> + <rect + style="color:#000000;fill:url(#radialGradient14208);fill-opacity:1;stroke:none;stroke-width:1.20000005;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new" + id="rect15946-7" + width="1" + height="9" + x="145" + y="133" + ry="0.5" /> + <rect + ry="0.5" + y="137" + x="132" + height="1" + width="14" + id="rect15972" + style="color:#000000;fill:url(#radialGradient14210);fill-opacity:1;stroke:none;stroke-width:1.20000005;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new" /> + <rect + ry="1" + y="135" + x="134.97968" + height="5" + width="1" + id="rect15996" + style="color:#000000;fill:url(#radialGradient16945);fill-opacity:1;stroke:none;stroke-width:1.20000005;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new" /> + <rect + ry="1" + y="135" + x="142.05502" + height="5" + width="1" + id="rect15998" + style="color:#000000;fill:url(#radialGradient16947);fill-opacity:1;stroke:none;stroke-width:1.20000005;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new" /> + </g> + <rect + style="opacity:0.51499999;color:#000000;fill:#0065ff;fill-opacity:1;stroke:none;stroke-width:1.20000005;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new" + id="rect15974" + width="4" + height="11" + x="137" + y="132" + ry="1" /> + <rect + ry="1" + y="133" + x="138" + height="9" + width="2" + id="rect15944" + style="color:#000000;fill:url(#radialGradient14216);fill-opacity:1;stroke:none;stroke-width:1.20000005;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new" /> + </g> + </g> </g> </g> + <g + style="display:inline;enable-background:new" + id="ICON_MESH_CAPSULE" + transform="matrix(0.86967262,0,0,0.81949894,414.30767,262.03112)"> + <rect + y="155" + x="-228" + height="16" + width="16" + id="rect41297-1" + style="display:inline;overflow:visible;visibility:visible;opacity:0;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate" /> + <path + sodipodi:nodetypes="cccsccc" + id="path41299-4" + d="m -220,154.54901 c -3.036,0 -5.49999,3.07099 -5.49999,4.45099 l -1e-5,9 c 0,1.38 2.46399,4.41118 5.49999,4.41118 3.036,0 5.5,-3.03118 5.5,-4.41118 l 1e-5,-9 c 0,-1.38 -2.464,-4.45099 -5.5,-4.45099 z" + style="display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient31208);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.80000001;marker:none;enable-background:accumulate" + inkscape:connector-curvature="0" /> + <path + style="display:inline;overflow:visible;visibility:visible;fill:none;stroke:#000000;stroke-width:0.80000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:7.40000248;stroke-opacity:1;marker:none;enable-background:accumulate" + d="m -220,154.19066 c -3.49999,0 -5.36123,3.42934 -5.36123,4.80934 l -1e-5,8.5 c 0,2 2.32523,5.30934 5.36123,5.30934 3.036,0 5.36125,-3.30934 5.36125,-5.30934 l 0,-8.5 c 0,-1.38 -1.86123,-4.80934 -5.36124,-4.80934 z" + id="path41305-2" + sodipodi:nodetypes="cccsccc" + inkscape:connector-curvature="0" /> + <path + style="display:inline;overflow:visible;visibility:visible;fill:none;stroke:url(#linearGradient31210);stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:7.40000248;stroke-opacity:1;marker:none;enable-background:accumulate" + d="m -224.5,160.5 0,6.5 c 0,0.9838 0.60816,4.76952 4.46531,4.76952 3.85714,0 4.53469,-3.78572 4.53469,-4.76952 l 0,-6.5 c 0,0 0.43828,-5.18967 -4.47408,-5.25396 C -224.88644,155.18174 -224.5,160.5 -224.5,160.5 Z" + id="path41307-2" + sodipodi:nodetypes="ccsccsc" + inkscape:connector-curvature="0" /> + </g> </g> <g inkscape:groupmode="layer" diff --git a/release/datafiles/blender_icons16/icon16_library_data_broken.dat b/release/datafiles/blender_icons16/icon16_library_data_broken.dat Binary files differindex e0ede6bc019..613fac48c21 100644 --- a/release/datafiles/blender_icons16/icon16_library_data_broken.dat +++ b/release/datafiles/blender_icons16/icon16_library_data_broken.dat diff --git a/release/datafiles/blender_icons16/icon16_mesh_capsule.dat b/release/datafiles/blender_icons16/icon16_mesh_capsule.dat Binary files differnew file mode 100644 index 00000000000..031cac70eea --- /dev/null +++ b/release/datafiles/blender_icons16/icon16_mesh_capsule.dat diff --git a/release/datafiles/blender_icons16/icon16_snap_grid.dat b/release/datafiles/blender_icons16/icon16_snap_grid.dat Binary files differnew file mode 100644 index 00000000000..4def9b8df2e --- /dev/null +++ b/release/datafiles/blender_icons16/icon16_snap_grid.dat diff --git a/release/datafiles/blender_icons16/icon16_snap_increment.dat b/release/datafiles/blender_icons16/icon16_snap_increment.dat Binary files differindex 4def9b8df2e..5216afb6d72 100644 --- a/release/datafiles/blender_icons16/icon16_snap_increment.dat +++ b/release/datafiles/blender_icons16/icon16_snap_increment.dat diff --git a/release/datafiles/blender_icons32/icon32_library_data_broken.dat b/release/datafiles/blender_icons32/icon32_library_data_broken.dat Binary files differindex e1ec782d5f4..89702a9ff9e 100644 --- a/release/datafiles/blender_icons32/icon32_library_data_broken.dat +++ b/release/datafiles/blender_icons32/icon32_library_data_broken.dat diff --git a/release/datafiles/blender_icons32/icon32_mesh_capsule.dat b/release/datafiles/blender_icons32/icon32_mesh_capsule.dat Binary files differnew file mode 100644 index 00000000000..213ef4281e1 --- /dev/null +++ b/release/datafiles/blender_icons32/icon32_mesh_capsule.dat diff --git a/release/datafiles/blender_icons32/icon32_snap_grid.dat b/release/datafiles/blender_icons32/icon32_snap_grid.dat Binary files differnew file mode 100644 index 00000000000..cc4c0efe3c4 --- /dev/null +++ b/release/datafiles/blender_icons32/icon32_snap_grid.dat diff --git a/release/datafiles/blender_icons32/icon32_snap_increment.dat b/release/datafiles/blender_icons32/icon32_snap_increment.dat Binary files differindex cc4c0efe3c4..eb88877e3ad 100644 --- a/release/datafiles/blender_icons32/icon32_snap_increment.dat +++ b/release/datafiles/blender_icons32/icon32_snap_increment.dat diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject c651e63a9a537624f639950f3127a1dee29205d +Subproject e4d4a560cafb21d74dd4db13473a2d1d7ee7316 diff --git a/release/datafiles/splash.png b/release/datafiles/splash.png Binary files differindex cfadec07b09..f84f092d5f8 100644 --- a/release/datafiles/splash.png +++ b/release/datafiles/splash.png diff --git a/release/datafiles/splash_2x.png b/release/datafiles/splash_2x.png Binary files differindex 79a6f2781ff..f4c611c8cc1 100644 --- a/release/datafiles/splash_2x.png +++ b/release/datafiles/splash_2x.png diff --git a/release/freedesktop/blender.desktop b/release/freedesktop/blender.desktop index a28a8155121..d661a157bfb 100644 --- a/release/freedesktop/blender.desktop +++ b/release/freedesktop/blender.desktop @@ -1,14 +1,83 @@ [Desktop Entry] Name=Blender GenericName=3D modeler +GenericName[ar]=3D المنمذج ثلاثي الأبعاد +GenericName[ca]=Modelador 3D +GenericName[cs]=3D modelování +GenericName[da]=3D-modellering GenericName[de]=3D-Modellierer +GenericName[el]=Μοντελοποιητής 3D GenericName[es]=Modelador 3D +GenericName[et]=3D modelleerija +GenericName[fi]=3D-mallintaja GenericName[fr]=Modeleur 3D +GenericName[gl]=Modelador 3D +GenericName[hu]=3D modellező +GenericName[it]=Modellatore 3D +GenericName[ja]=3D モデラー +GenericName[lt]=3D modeliavimas +GenericName[nb]=3D-modellering +GenericName[nl]=3D-modeller +GenericName[pl]=Modelowanie 3D +GenericName[pt_BR]=Modelador 3D +GenericName[ro]=Arhitect 3D GenericName[ru]=Редактор 3D-моделей +GenericName[tr]=3D modelleyici +GenericName[uk]=Редактор 3D-моделей +GenericName[wa]=Modeleu 3D +GenericName[zh_CN]=3D 建模 +GenericName[zh_TW]=3D 模型 Comment=3D modeling, animation, rendering and post-production +Comment[ar]=3D النمذجة، الرسوم المتحركة، والتجسيد، وما بعد الإنتاج +Comment[ast]=Modeláu 3D, animación, renderizáu y postproducción +Comment[eu]=3D modelatzea, animazioa, errendatzea eta post-produkzioa +Comment[be]=Праграма прасторавага мадэлявання, анімацыі, апрацоўкі відэа і давядзення відэапрадукцыі +Comment[bn]=ত্রিমাত্রিক মডেল, অ্যানিমেশন, রেন্ডারিং এবং পোস্ট-উৎপাদন +Comment[bs]=3D modeliranje, animacija, obrada i postprodukcija +Comment[bg]=3D моделиране, анимиране, рендиране и пост-продукция +Comment[ca]=Modelat 3D, animació, renderització i post-producció +Comment[ca@valencia]=Modelat 3D, animació, renderització i post-producció +Comment[crh]=3B modelleme, animasyon, işleme ve son üretim +Comment[cs]=3D modelování, animace, rederování a postprodukce +Comment[da]=3D-modellering, animation, rendering og efterbehandling Comment[de]=3D-Modellierung, Animation, Rendering und Nachbearbeitung +Comment[nl]=3d-modelleren, animeren, renderen en post-productie +Comment[el]=Μοντελοποίηση 3D, κινούμενα σχέδια, αποτύπωση και οργάνωση διαδικασίας μετά-την-παραγωγή +Comment[eo]=3D-modelado, animacio, renderado kaj postproduktado Comment[es]=Modelado 3D, animación, renderizado y post-producción -Comment[fr]=Modelage 3D, animation, rendu et post-production +Comment[et]=Kolmemõõtmeline modelleerimine, animeerimine, esitlemine ja järeltöötlemine +Comment[fi]=3D-mallinnus, -animaatiot, -renderöinti ja -tuotanto +Comment[fr]=Modélisation 3D, animation, rendu et post-production +Comment[fr_CA]=Modélisation 3D, animation, rendu et post-production +Comment[gl]=Modelado 3D, animación, renderizado e postprodución +Comment[hu]=3D modellek és animációk létrehozása és szerkesztése +Comment[is]=Þrívíddarmódel, hreyfimyndir, myndgerð og frágangur myndskeiða +Comment[it]=Modellazione 3D, animazione, rendering e post-produzione +Comment[ja]=3Dモデリング、アニメーション、レンダリング、ポストプロダクションのツール +Comment[ko]=3D 모델링, 애니메이션, 렌더링과 포스트 프로덕션 +Comment[lt]=3D modeliavimas, animacijų kūrimas, atvaizdavimas ir tobulinimas +Comment[lv]=3D modelēšana, animācija, renderēšana un pēcapstrāde +Comment[ms]=Pemodelan, animasi, penerapan dan post-produksi 3D +Comment[nb]=3D-modellering, animasjon, rendering og postproduksjon +Comment[oc]=Modelizacion 3D, animacion, rendut e post-produccion +Comment[pl]=Modelowanie 3D, animacja, renderowanie i postprodukcja +Comment[pt]=Modelação 3D, animação, renderização e pós-produção +Comment[pt_BR]=Modelagem 3D, animação, renderização e pós-produção +Comment[ro]=Modelare, animare, afișare și post-producție 3D +Comment[ru]=3D-моделирование, анимация, рендеринг и компоновка +Comment[sl]=3D modeliranje, animacija, izrisovanje in nadaljnje obdelovanje +Comment[sq]=Animacion i modeleve 3D, rregullim dhe më pas prodhim +Comment[sr]=3Д моделовање, анимација, исцртавање и постпродукција +Comment[sv]=3d-modellering, animering, rendering och efterbehandling +Comment[ta]=முப்பரிமாண ஒப்புருவாக்கம், அசைவூட்டம், காட்சியாக்கம் மற்றும் உருவாக்கத்துக்கு பிந்தைய செயல்பாடுகள் +Comment[tg]=Моделсозии 3D, аниматсия, пешниҳод ва истеҳсоли баъдӣ +Comment[tr]=3B modelleme, animasyon, işleme ve son üretim +Comment[uk]=Програма просторового моделювання, анімації, обробки відео та доведення відеопродуктів +Comment[vi]=Tạo hình mẫu 3D, hoạt họa, dựng hình và các công việc hậu kỳ +Comment[wa]=Modelaedje 3D, animåcion, rindou eyet après-produccion +Comment[zh_HK]=3D 模型、動畫、算圖和後製 +Comment[zh_CN]=3D 建模、动画、渲染和后期制作 +Comment[zh_TW]=3D 模型、動畫、算圖和後製 Keywords=3d;cg;modeling;animation;painting;sculpting;texturing;video editing;video tracking;rendering;render engine;cycles;game engine;python; Exec=blender %f Icon=blender diff --git a/release/scripts/addons b/release/scripts/addons -Subproject 3fc5b82c6bdba2f9c954fbf497621b9bb794a1b +Subproject ecdc7c03ec14e6ed70dea808b61049017f46ad9 diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib -Subproject cf842d8bb7b0033ca4fa99f7ebedcbd3810fd27 +Subproject c63ada6ae2569864be891ff308cda6b39d8368f diff --git a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py index 43a09a1acbd..baa9140aaef 100644 --- a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py +++ b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py @@ -304,7 +304,8 @@ def dump_rna_messages(msgs, reports, settings, verbose=False): else: bl_rna_base_props = set() - for prop in bl_rna.properties: + props = sorted(bl_rna.properties, key=lambda p: p.identifier) + for prop in props: # Only write this property if our parent hasn't got it. if prop in bl_rna_base_props: continue @@ -456,7 +457,7 @@ def dump_py_messages_from_files(msgs, reports, files, settings): def extract_strings_split(node): """ - Returns a list args as returned by 'extract_strings()', But split into groups based on separate_nodes, this way + Returns a list args as returned by 'extract_strings()', but split into groups based on separate_nodes, this way expressions like ("A" if test else "B") wont be merged but "A" + "B" will. """ estr_ls = [] diff --git a/release/scripts/modules/bl_i18n_utils/settings.py b/release/scripts/modules/bl_i18n_utils/settings.py index a63633d25aa..49dbfbe62af 100644 --- a/release/scripts/modules/bl_i18n_utils/settings.py +++ b/release/scripts/modules/bl_i18n_utils/settings.py @@ -184,15 +184,15 @@ DOMAIN = "blender" # File type (ext) to parse. PYGETTEXT_ALLOWED_EXTS = {".c", ".cpp", ".cxx", ".hpp", ".hxx", ".h"} -# Max number of contexts into a BLF_I18N_MSGID_MULTI_CTXT macro... +# Max number of contexts into a BLT_I18N_MSGID_MULTI_CTXT macro... PYGETTEXT_MAX_MULTI_CTXT = 16 # Where to search contexts definitions, relative to SOURCE_DIR (defined below). -PYGETTEXT_CONTEXTS_DEFSRC = os.path.join("source", "blender", "blenfont", "BLF_translation.h") +PYGETTEXT_CONTEXTS_DEFSRC = os.path.join("source", "blender", "blentranslation", "BLT_translation.h") -# Regex to extract contexts defined in BLF_translation.h +# Regex to extract contexts defined in BLT_translation.h # XXX Not full-proof, but should be enough here! -PYGETTEXT_CONTEXTS = "#define\\s+(BLF_I18NCONTEXT_[A-Z_0-9]+)\\s+\"([^\"]*)\"" +PYGETTEXT_CONTEXTS = "#define\\s+(BLT_I18NCONTEXT_[A-Z_0-9]+)\\s+\"([^\"]*)\"" # Keywords' regex. # XXX Most unfortunately, we can't use named backreferences inside character sets, @@ -255,7 +255,7 @@ PYGETTEXT_KEYWORDS = (() + tuple((r"{}\(\s*" + _msg_re + r"\s*,\s*(?:" + r"\s*,\s*)?(?:".join(_ctxt_re_gen(i) for i in range(PYGETTEXT_MAX_MULTI_CTXT)) + r")?\s*\)").format(it) - for it in ("BLF_I18N_MSGID_MULTI_CTXT",)) + for it in ("BLT_I18N_MSGID_MULTI_CTXT",)) ) # Check printf mismatches between msgid and msgstr. @@ -333,6 +333,7 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "expected a view3d region & editcurve", "expected a view3d region & editmesh", "image file not found", + "image format is read-only", "image path can't be written to", "in memory to enable editing!", "jumps over", diff --git a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py index e2f2aeef7b2..b1aa4e02cee 100644 --- a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py +++ b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py @@ -97,6 +97,7 @@ class SpellChecker: "denoise", "deselect", "deselecting", "deselection", "despill", "despilling", + "dirtree", "editcurve", "editmesh", "filebrowser", @@ -113,7 +114,7 @@ class SpellChecker: "libdata", "lightless", "lineset", - "linestyle", + "linestyle", "linestyles", "localview", "lookup", "lookups", "mathutils", @@ -128,6 +129,7 @@ class SpellChecker: "multiuser", "multiview", "namespace", + "nodetree", "nodetrees", "keyconfig", "online", "playhead", diff --git a/release/scripts/modules/bl_previews_utils/bl_previews_render.py b/release/scripts/modules/bl_previews_utils/bl_previews_render.py new file mode 100644 index 00000000000..627a6ab2d3d --- /dev/null +++ b/release/scripts/modules/bl_previews_utils/bl_previews_render.py @@ -0,0 +1,491 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ***** END GPL LICENSE BLOCK ***** + +# <pep8 compliant> + +# Populate a template file (POT format currently) from Blender RNA/py/C data. +# Note: This script is meant to be used from inside Blender! + +import collections +import os +import sys + +import bpy +from mathutils import Vector, Euler + + +INTERN_PREVIEW_TYPES = {'MATERIAL', 'LAMP', 'WORLD', 'TEXTURE', 'IMAGE'} +OBJECT_TYPES_RENDER = {'MESH', 'CURVE', 'SURFACE', 'META', 'FONT'} + + +def ids_nolib(bids): + return (bid for bid in bids if not bid.library) + + +def rna_backup_gen(data, include_props=None, exclude_props=None, root=()): + # only writable properties... + for p in data.bl_rna.properties: + pid = p.identifier + if pid in {'rna_type', }: + continue + path = root + (pid,) + if include_props is not None and path not in include_props: + continue + if exclude_props is not None and path in exclude_props: + continue + val = getattr(data, pid) + if val is not None and p.type == 'POINTER': + # recurse! + yield from rna_backup_gen(val, include_props, exclude_props, root=path) + elif data.is_property_readonly(pid): + continue + else: + yield path, val + + +def rna_backup_restore(data, backup): + for path, val in backup: + dt = data + for pid in path[:-1]: + dt = getattr(dt, pid) + setattr(dt, path[-1], val) + + +def do_previews(do_objects, do_groups, do_scenes, do_data_intern): + # Helpers. + RenderContext = collections.namedtuple("RenderContext", ( + "scene", "world", "camera", "lamp", "camera_data", "lamp_data", "image", # All those are names! + "backup_scene", "backup_world", "backup_camera", "backup_lamp", "backup_camera_data", "backup_lamp_data", + )) + + RENDER_PREVIEW_SIZE = bpy.app.render_preview_size + + def render_context_create(engine, objects_ignored): + if engine == '__SCENE': + backup_scene, backup_world, backup_camera, backup_lamp, backup_camera_data, backup_lamp_data = [()] * 6 + scene = bpy.context.screen.scene + exclude_props = {('world',), ('camera',), ('tool_settings',), ('preview',)} + backup_scene = tuple(rna_backup_gen(scene, exclude_props=exclude_props)) + world = scene.world + camera = scene.camera + if camera: + camera_data = camera.data + else: + backup_camera, backup_camera_data = [None] * 2 + camera_data = bpy.data.cameras.new("TEMP_preview_render_camera") + camera = bpy.data.objects.new("TEMP_preview_render_camera", camera_data) + camera.rotation_euler = Euler((1.1635528802871704, 0.0, 0.7853981852531433), 'XYZ') # (66.67, 0.0, 45.0) + scene.camera = camera + scene.objects.link(camera) + # TODO: add lamp if none found in scene? + lamp = None + lamp_data = None + else: + backup_scene, backup_world, backup_camera, backup_lamp, backup_camera_data, backup_lamp_data = [None] * 6 + + scene = bpy.data.scenes.new("TEMP_preview_render_scene") + world = bpy.data.worlds.new("TEMP_preview_render_world") + camera_data = bpy.data.cameras.new("TEMP_preview_render_camera") + camera = bpy.data.objects.new("TEMP_preview_render_camera", camera_data) + lamp_data = bpy.data.lamps.new("TEMP_preview_render_lamp", 'SPOT') + lamp = bpy.data.objects.new("TEMP_preview_render_lamp", lamp_data) + + objects_ignored.add((camera.name, lamp.name)) + + scene.world = world + + camera.rotation_euler = Euler((1.1635528802871704, 0.0, 0.7853981852531433), 'XYZ') # (66.67, 0.0, 45.0) + scene.camera = camera + scene.objects.link(camera) + + lamp.rotation_euler = Euler((0.7853981852531433, 0.0, 1.7453292608261108), 'XYZ') # (45.0, 0.0, 100.0) + lamp_data.falloff_type = 'CONSTANT' + lamp_data.spot_size = 1.0471975803375244 # 60 + scene.objects.link(lamp) + + if engine == 'BLENDER_RENDER': + scene.render.engine = 'BLENDER_RENDER' + scene.render.alpha_mode = 'TRANSPARENT' + + world.use_sky_blend = True + world.horizon_color = 0.9, 0.9, 0.9 + world.zenith_color = 0.5, 0.5, 0.5 + world.ambient_color = 0.1, 0.1, 0.1 + world.light_settings.use_environment_light = True + world.light_settings.environment_energy = 1.0 + world.light_settings.environment_color = 'SKY_COLOR' + elif engine == 'CYCLES': + scene.render.engine = 'CYCLES' + scene.cycles.film_transparent = True + # TODO: define Cycles world? + + scene.render.image_settings.file_format = 'PNG' + scene.render.image_settings.color_depth = '8' + scene.render.image_settings.color_mode = 'RGBA' + scene.render.image_settings.compression = 25 + scene.render.resolution_x = RENDER_PREVIEW_SIZE + scene.render.resolution_y = RENDER_PREVIEW_SIZE + scene.render.resolution_percentage = 100 + scene.render.filepath = os.path.join(bpy.app.tempdir, 'TEMP_preview_render.png') + scene.render.use_overwrite = True + scene.render.use_stamp = False + + image = bpy.data.images.new("TEMP_render_image", RENDER_PREVIEW_SIZE, RENDER_PREVIEW_SIZE, alpha=True) + image.source = 'FILE' + image.filepath = scene.render.filepath + + return RenderContext( + scene.name, world.name if world else None, camera.name, lamp.name if lamp else None, + camera_data.name, lamp_data.name if lamp_data else None, image.name, + backup_scene, backup_world, backup_camera, backup_lamp, backup_camera_data, backup_lamp_data, + ) + + def render_context_delete(render_context): + # We use try/except blocks here to avoid crash, too much things can go wrong, and we want to leave the current + # .blend as clean as possible! + success = True + + scene = bpy.data.scenes[render_context.scene, None] + try: + if render_context.backup_scene is None: + scene.world = None + scene.camera = None + if render_context.camera: + scene.objects.unlink(bpy.data.objects[render_context.camera, None]) + if render_context.lamp: + scene.objects.unlink(bpy.data.objects[render_context.lamp, None]) + bpy.data.scenes.remove(scene) + scene = None + else: + rna_backup_restore(scene, render_context.backup_scene) + except Exception as e: + print("ERROR:", e) + success = False + + if render_context.world is not None: + try: + world = bpy.data.worlds[render_context.world, None] + if render_context.backup_world is None: + if scene is not None: + scene.world = None + world.user_clear() + bpy.data.worlds.remove(world) + else: + rna_backup_restore(world, render_context.backup_world) + except Exception as e: + print("ERROR:", e) + success = False + + if render_context.camera: + try: + camera = bpy.data.objects[render_context.camera, None] + if render_context.backup_camera is None: + if scene is not None: + scene.camera = None + scene.objects.unlink(camera) + camera.user_clear() + bpy.data.objects.remove(camera) + bpy.data.cameras.remove(bpy.data.cameras[render_context.camera_data, None]) + else: + rna_backup_restore(camera, render_context.backup_camera) + rna_backup_restore(bpy.data.cameras[render_context.camera_data, None], + render_context.backup_camera_data) + except Exception as e: + print("ERROR:", e) + success = False + + if render_context.lamp: + try: + lamp = bpy.data.objects[render_context.lamp, None] + if render_context.backup_lamp is None: + if scene is not None: + scene.objects.unlink(lamp) + lamp.user_clear() + bpy.data.objects.remove(lamp) + bpy.data.lamps.remove(bpy.data.lamps[render_context.lamp_data, None]) + else: + rna_backup_restore(lamp, render_context.backup_lamp) + rna_backup_restore(bpy.data.lamps[render_context.lamp_data, None], render_context.backup_lamp_data) + except Exception as e: + print("ERROR:", e) + success = False + + try: + image = bpy.data.images[render_context.image, None] + image.user_clear() + bpy.data.images.remove(image) + except Exception as e: + print("ERROR:", e) + success = False + + return success + + def objects_render_engine_guess(obs): + for obname in obs: + ob = bpy.data.objects[obname, None] + for matslot in ob.material_slots: + mat = matslot.material + if mat and mat.use_nodes and mat.node_tree: + for nd in mat.node_tree.nodes: + if nd.shading_compatibility == {'NEW_SHADING'}: + return 'CYCLES' + return 'BLENDER_RENDER' + + def object_bbox_merge(bbox, ob, ob_space): + if ob.bound_box: + ob_bbox = ob.bound_box + else: + ob_bbox = ((-ob.scale.x, -ob.scale.y, -ob.scale.z), (ob.scale.x, ob.scale.y, ob.scale.z)) + for v in ob.bound_box: + v = ob_space.matrix_world.inverted() * ob.matrix_world * Vector(v) + if bbox[0].x > v.x: + bbox[0].x = v.x + if bbox[0].y > v.y: + bbox[0].y = v.y + if bbox[0].z > v.z: + bbox[0].z = v.z + if bbox[1].x < v.x: + bbox[1].x = v.x + if bbox[1].y < v.y: + bbox[1].y = v.y + if bbox[1].z < v.z: + bbox[1].z = v.z + + def objects_bbox_calc(camera, objects): + bbox = (Vector((1e9, 1e9, 1e9)), Vector((-1e9, -1e9, -1e9))) + for obname in objects: + ob = bpy.data.objects[obname, None] + object_bbox_merge(bbox, ob, camera) + # Our bbox has been generated in camera local space, bring it back in world one + bbox[0][:] = camera.matrix_world * bbox[0] + bbox[1][:] = camera.matrix_world * bbox[1] + cos = ( + bbox[0].x, bbox[0].y, bbox[0].z, + bbox[0].x, bbox[0].y, bbox[1].z, + bbox[0].x, bbox[1].y, bbox[0].z, + bbox[0].x, bbox[1].y, bbox[1].z, + bbox[1].x, bbox[0].y, bbox[0].z, + bbox[1].x, bbox[0].y, bbox[1].z, + bbox[1].x, bbox[1].y, bbox[0].z, + bbox[1].x, bbox[1].y, bbox[1].z, + ) + return cos + + def preview_render_do(render_context, item_container, item_name, objects): + scene = bpy.data.scenes[render_context.scene, None] + if objects is not None: + camera = bpy.data.objects[render_context.camera, None] + lamp = bpy.data.objects[render_context.lamp, None] if render_context.lamp is not None else None + cos = objects_bbox_calc(camera, objects) + loc, ortho_scale = camera.camera_fit_coords(scene, cos) + camera.location = loc + if lamp: + loc, ortho_scale = lamp.camera_fit_coords(scene, cos) + lamp.location = loc + scene.update() + + bpy.ops.render.render(write_still=True) + + image = bpy.data.images[render_context.image, None] + item = getattr(bpy.data, item_container)[item_name, None] + image.reload() + item.preview.image_size = (RENDER_PREVIEW_SIZE, RENDER_PREVIEW_SIZE) + item.preview.image_pixels_float[:] = image.pixels + + # And now, main code! + do_save = True + + if do_data_intern: + bpy.ops.wm.previews_clear(id_type=INTERN_PREVIEW_TYPES) + bpy.ops.wm.previews_ensure() + + render_contexts = {} + + objects_ignored = set() + groups_ignored = set() + + prev_scenename = bpy.context.screen.scene.name + + if do_objects: + prev_shown = tuple(ob.hide_render for ob in ids_nolib(bpy.data.objects)) + for ob in ids_nolib(bpy.data.objects): + if ob in objects_ignored: + continue + ob.hide_render = True + for root in ids_nolib(bpy.data.objects): + if root.name in objects_ignored: + continue + if root.type not in OBJECT_TYPES_RENDER: + continue + objects = (root.name,) + + render_engine = objects_render_engine_guess(objects) + render_context = render_contexts.get(render_engine, None) + if render_context is None: + render_context = render_context_create(render_engine, objects_ignored) + render_contexts[render_engine] = render_context + + scene = bpy.data.scenes[render_context.scene, None] + bpy.context.screen.scene = scene + + for obname in objects: + ob = bpy.data.objects[obname, None] + if obname not in scene.objects: + scene.objects.link(ob) + ob.hide_render = False + scene.update() + + preview_render_do(render_context, 'objects', root.name, objects) + + # XXX Hyper Super Uber Suspicious Hack! + # Without this, on windows build, script excepts with following message: + # Traceback (most recent call last): + # File "<string>", line 1, in <module> + # File "<string>", line 451, in <module> + # File "<string>", line 443, in main + # File "<string>", line 327, in do_previews + # OverflowError: Python int too large to convert to C long + # ... :( + import sys + scene = bpy.data.scenes[render_context.scene, None] + for obname in objects: + ob = bpy.data.objects[obname, None] + scene.objects.unlink(ob) + ob.hide_render = True + + for ob, is_rendered in zip(tuple(ids_nolib(bpy.data.objects)), prev_shown): + ob.hide_render = is_rendered + + if do_groups: + for grp in ids_nolib(bpy.data.groups): + if grp.name in groups_ignored: + continue + objects = tuple(ob.name for ob in grp.objects) + + render_engine = objects_render_engine_guess(objects) + render_context = render_contexts.get(render_engine, None) + if render_context is None: + render_context = render_context_create(render_engine, objects_ignored) + render_contexts[render_engine] = render_context + + scene = bpy.data.scenes[render_context.scene, None] + bpy.context.screen.scene = scene + + bpy.ops.object.group_instance_add(group=grp.name) + grp_ob = next((ob for ob in scene.objects if ob.dupli_group and ob.dupli_group.name == grp.name)) + grp_obname = grp_ob.name + scene.update() + + preview_render_do(render_context, 'groups', grp.name, objects) + + scene = bpy.data.scenes[render_context.scene, None] + scene.objects.unlink(bpy.data.objects[grp_obname, None]) + + bpy.context.screen.scene = bpy.data.scenes[prev_scenename, None] + for render_context in render_contexts.values(): + if not render_context_delete(render_context): + do_save = False # Do not save file if something went wrong here, we could 'pollute' it with temp data... + + if do_scenes: + for scene in ids_nolib(bpy.data.scenes): + has_camera = scene.camera is not None + bpy.context.screen.scene = scene + render_context = render_context_create('__SCENE', objects_ignored) + scene.update() + + objects = None + if not has_camera: + # We had to add a temp camera, now we need to place it to see interesting objects! + objects = tuple(ob.name for ob in scene.objects + if (not ob.hide_render) and (ob.type in OBJECT_TYPES_RENDER)) + + preview_render_do(render_context, 'scenes', scene.name, objects) + + if not render_context_delete(render_context): + do_save = False + + bpy.context.screen.scene = bpy.data.scenes[prev_scenename, None] + if do_save: + print("Saving %s..." % bpy.data.filepath) + try: + bpy.ops.wm.save_mainfile() + except Exception as e: + # Might fail in some odd cases, like e.g. in regression files we have glsl/ram_glsl.blend which + # references an inexistent texture... Better not break in this case, just spit error to console. + print("ERROR:", e) + else: + print("*NOT* Saving %s, because some error(s) happened while deleting temp render data..." % bpy.data.filepath) + + +def do_clear_previews(do_objects, do_groups, do_scenes, do_data_intern): + if do_data_intern: + bpy.ops.wm.previews_clear(id_type=INTERN_PREVIEW_TYPES) + + if do_objects: + for ob in ids_nolib(bpy.data.objects): + ob.preview.image_size = (0, 0) + + if do_groups: + for grp in ids_nolib(bpy.data.groups): + grp.preview.image_size = (0, 0) + + if do_scenes: + for scene in ids_nolib(bpy.data.scenes): + scene.preview.image_size = (0, 0) + + print("Saving %s..." % bpy.data.filepath) + bpy.ops.wm.save_mainfile() + + +def main(): + try: + import bpy + except ImportError: + print("This script must run from inside blender") + return + + import sys + import argparse + + # Get rid of Blender args! + argv = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [] + + parser = argparse.ArgumentParser(description="Use Blender to generate previews for currently open Blender file's items.") + parser.add_argument('--clear', default=False, action="store_true", help="Clear previews instead of generating them.") + parser.add_argument('--no_scenes', default=True, action="store_false", help="Do not generate/clear previews for scene IDs.") + parser.add_argument('--no_groups', default=True, action="store_false", help="Do not generate/clear previews for group IDs.") + parser.add_argument('--no_objects', default=True, action="store_false", help="Do not generate/clear previews for object IDs.") + parser.add_argument('--no_data_intern', default=True, action="store_false", + help="Do not generate/clear previews for mat/tex/image/etc. IDs (those handled by core Blender code).") + args = parser.parse_args(argv) + + if args.clear: + print("clear!") + do_clear_previews(do_objects=args.no_objects, do_groups=args.no_groups, do_scenes=args.no_scenes, + do_data_intern=args.no_data_intern) + else: + print("render!") + do_previews(do_objects=args.no_objects, do_groups=args.no_groups, do_scenes=args.no_scenes, + do_data_intern=args.no_data_intern) + + +if __name__ == "__main__": + print("\n\n *** Running {} *** \n".format(__file__)) + print(" *** Blend file {} *** \n".format(bpy.data.filepath)) + main() + bpy.ops.wm.quit_blender() diff --git a/release/scripts/modules/bpy/path.py b/release/scripts/modules/bpy/path.py index d5b64933165..d7c6101115d 100644 --- a/release/scripts/modules/bpy/path.py +++ b/release/scripts/modules/bpy/path.py @@ -61,7 +61,7 @@ def abspath(path, start=None, library=None): :arg start: Relative to this path, when not set the current filename is used. - :type start: string + :type start: string or bytes :arg library: The library this path is from. This is only included for convenience, when the library is not None its path replaces *start*. :type library: :class:`bpy.types.Library` @@ -90,9 +90,11 @@ def relpath(path, start=None): """ Returns the path relative to the current blend file using the "//" prefix. + :arg path: An absolute path. + :type path: string or bytes :arg start: Relative to this path, when not set the current filename is used. - :type start: string + :type start: string or bytes """ if isinstance(path, bytes): if not path.startswith(b"//"): @@ -112,6 +114,9 @@ def is_subdir(path, directory): """ Returns true if *path* in a subdirectory of *directory*. Both paths must be absolute. + + :arg path: An absolute path. + :type path: string or bytes """ from os.path import normpath, normcase path = normpath(normcase(path)) @@ -129,7 +134,7 @@ def clean_name(name, replace="_"): may cause problems under various circumstances, such as writing to a file. All characters besides A-Z/a-z, 0-9 are replaced with "_" - or the replace argument if defined. + or the *replace* argument if defined. """ if replace != "_": @@ -278,22 +283,21 @@ def ensure_ext(filepath, ext, case_sensitive=False): """ Return the path with the extension added if it is not already set. - :arg ext: The extension to check for. + :arg ext: The extension to check for, can be a compound extension. Should + start with a dot, such as '.blend' or '.tar.gz'. :type ext: string :arg case_sensitive: Check for matching case when comparing extensions. :type case_sensitive: bool """ - fn_base, fn_ext = _os.path.splitext(filepath) - if fn_base and fn_ext: - if ((case_sensitive and ext == fn_ext) or - (ext.lower() == fn_ext.lower())): + if case_sensitive: + if filepath.endswith(ext): return filepath - else: - return fn_base + ext - else: - return filepath + ext + if filepath[-len(ext):].lower().endswith(ext.lower()): + return filepath + + return filepath + ext def module_names(path, recursive=False): diff --git a/release/scripts/modules/bpy/utils/__init__.py b/release/scripts/modules/bpy/utils/__init__.py index 7a1224db226..481db4659af 100644 --- a/release/scripts/modules/bpy/utils/__init__.py +++ b/release/scripts/modules/bpy/utils/__init__.py @@ -377,46 +377,31 @@ def preset_paths(subdir): def smpte_from_seconds(time, fps=None): """ - Returns an SMPTE formatted string from the time in seconds: "HH:MM:SS:FF". + Returns an SMPTE formatted string from the *time*: + ``HH:MM:SS:FF``. If the *fps* is not given the current scene is used. - """ - import math - - if fps is None: - fps = _bpy.context.scene.render.fps - - hours = minutes = seconds = frames = 0 - - if time < 0: - time = - time - neg = "-" - else: - neg = "" - - if time >= 3600.0: # hours - hours = int(time / 3600.0) - time = time % 3600.0 - if time >= 60.0: # minutes - minutes = int(time / 60.0) - time = time % 60.0 - seconds = int(time) - frames = int(round(math.floor(((time - seconds) * fps)))) + :arg time: time in seconds. + :type time: int, float or ``datetime.timedelta``. + :return: the frame string. + :rtype: string + """ - return "%s%02d:%02d:%02d:%02d" % (neg, hours, minutes, seconds, frames) + return smpte_from_frame(time_to_frame(time, fps=fps), fps) def smpte_from_frame(frame, fps=None, fps_base=None): """ - Returns an SMPTE formatted string from the frame: "HH:MM:SS:FF". + Returns an SMPTE formatted string from the *frame*: + ``HH:MM:SS:FF``. If *fps* and *fps_base* are not given the current scene is used. - :arg time: time in seconds. - :type time: number or timedelta object - :return: the frame. - :rtype: float + :arg frame: frame number. + :type frame: int or float. + :return: the frame string. + :rtype: string """ if fps is None: @@ -425,7 +410,17 @@ def smpte_from_frame(frame, fps=None, fps_base=None): if fps_base is None: fps_base = _bpy.context.scene.render.fps_base - return smpte_from_seconds((frame * fps_base) / fps, fps) + sign = "-" if frame < 0 else "" + frame = abs(frame * fps_base) + + return ( + "%s%02d:%02d:%02d:%02d" % ( + sign, + int(frame / (3600 * fps)), # HH + int((frame / (60 * fps)) % 60), # MM + int((frame / fps) % 60), # SS + int(frame % fps), # FF + )) def time_from_frame(frame, fps=None, fps_base=None): @@ -435,7 +430,7 @@ def time_from_frame(frame, fps=None, fps_base=None): If *fps* and *fps_base* are not given the current scene is used. :arg frame: number. - :type frame: the frame number + :type frame: int or float. :return: the time in seconds. :rtype: datetime.timedelta """ @@ -459,7 +454,7 @@ def time_to_frame(time, fps=None, fps_base=None): If *fps* and *fps_base* are not given the current scene is used. :arg time: time in seconds. - :type time: number or a datetime.timedelta object + :type time: number or a ``datetime.timedelta`` object :return: the frame. :rtype: float """ diff --git a/release/scripts/modules/bpy_extras/anim_utils.py b/release/scripts/modules/bpy_extras/anim_utils.py index 4ee5e685668..021a8bbb530 100644 --- a/release/scripts/modules/bpy_extras/anim_utils.py +++ b/release/scripts/modules/bpy_extras/anim_utils.py @@ -142,6 +142,13 @@ def bake_action(frame_start, obj_info.append(obj_frame_info(obj)) # ------------------------------------------------------------------------- + # Clean (store initial data) + if do_clean and action is not None: + clean_orig_data = {fcu: {p.co[1] for p in fcu.keyframe_points} for fcu in action.fcurves} + else: + clean_orig_data = {} + + # ------------------------------------------------------------------------- # Create action # in case animation data hasn't been created @@ -230,12 +237,19 @@ def bake_action(frame_start, if do_clean: for fcu in action.fcurves: + fcu_orig_data = clean_orig_data.get(fcu, set()) + keyframe_points = fcu.keyframe_points i = 1 - while i < len(fcu.keyframe_points) - 1: + while i < len(keyframe_points) - 1: + val = keyframe_points[i].co[1] + + if val in fcu_orig_data: + i += 1 + continue + val_prev = keyframe_points[i - 1].co[1] val_next = keyframe_points[i + 1].co[1] - val = keyframe_points[i].co[1] if abs(val - val_prev) + abs(val - val_next) < 0.0001: keyframe_points.remove(keyframe_points[i]) diff --git a/release/scripts/modules/bpy_extras/image_utils.py b/release/scripts/modules/bpy_extras/image_utils.py index ff6d23badb6..ad774cd1bda 100644 --- a/release/scripts/modules/bpy_extras/image_utils.py +++ b/release/scripts/modules/bpy_extras/image_utils.py @@ -32,6 +32,8 @@ def load_image(imagepath, convert_callback=None, verbose=False, relpath=None, + check_existing=False, + force_reload=False, ): """ Return an image from the file path with options to search multiple paths @@ -60,6 +62,12 @@ def load_image(imagepath, :type convert_callback: function :arg relpath: If not None, make the file relative to this path. :type relpath: None or string + :arg check_existing: If true, returns already loaded image datablock if possible + (based on file path). + :type check_existing: bool + :arg force_reload: If true, force reloading of image (only useful when `check_existing` + is also enabled). + :type force_reload: bool :return: an image or None :rtype: :class:`bpy.types.Image` """ @@ -86,7 +94,7 @@ def load_image(imagepath, path = convert_callback(path) try: - image = bpy.data.images.load(path) + image = bpy.data.images.load(path, check_existing) except RuntimeError: image = None @@ -102,6 +110,8 @@ def load_image(imagepath, image = _image_load_placeholder(path) if image: + if force_reload: + image.reload() if relpath is not None: # make relative from bpy.path import relpath as relpath_fn diff --git a/release/scripts/modules/bpy_extras/object_utils.py b/release/scripts/modules/bpy_extras/object_utils.py index 78fb6aa8fa2..c2c306e5145 100644 --- a/release/scripts/modules/bpy_extras/object_utils.py +++ b/release/scripts/modules/bpy_extras/object_utils.py @@ -33,6 +33,7 @@ import bpy from bpy.props import ( BoolProperty, + BoolVectorProperty, FloatVectorProperty, ) @@ -136,16 +137,22 @@ def object_data_add(context, obdata, operator=None, use_active_layer=True, name= if context.space_data and context.space_data.type == 'VIEW_3D': v3d = context.space_data - if use_active_layer: - if v3d and v3d.local_view: - base.layers_from_view(context.space_data) - base.layers[scene.active_layer] = True - else: - base.layers = [True if i == scene.active_layer - else False for i in range(len(scene.layers))] + if operator is not None and any(operator.layers): + base.layers = operator.layers else: - if v3d: - base.layers_from_view(context.space_data) + if use_active_layer: + if v3d and v3d.local_view: + base.layers_from_view(context.space_data) + base.layers[scene.active_layer] = True + else: + base.layers = [True if i == scene.active_layer + else False for i in range(len(scene.layers))] + else: + if v3d: + base.layers_from_view(context.space_data) + + if operator is not None: + operator.layers = base.layers obj_new.matrix_world = add_object_align_init(context, operator) @@ -209,6 +216,12 @@ class AddObjectHelper: name="Rotation", subtype='EULER', ) + layers = BoolVectorProperty( + name="Layers", + size=20, + subtype='LAYER', + options={'HIDDEN', 'SKIP_SAVE'}, + ) @classmethod def poll(self, context): diff --git a/release/scripts/modules/nodeitems_utils.py b/release/scripts/modules/nodeitems_utils.py index 1cc9afc78cc..be6f031217c 100644 --- a/release/scripts/modules/nodeitems_utils.py +++ b/release/scripts/modules/nodeitems_utils.py @@ -85,6 +85,7 @@ class NodeItemCustom: _node_categories = {} + def register_node_categories(identifier, cat_list): if identifier in _node_categories: raise KeyError("Node categories list '%s' already registered" % identifier) @@ -167,6 +168,7 @@ def unregister_node_categories(identifier=None): unregister_node_cat_types(cat_types) _node_categories.clear() + def draw_node_categories_menu(self, context): for cats in _node_categories.values(): cats[1](self, context) diff --git a/release/scripts/modules/progress_report.py b/release/scripts/modules/progress_report.py index 0d1f4f2bef8..fc77a3e998e 100644 --- a/release/scripts/modules/progress_report.py +++ b/release/scripts/modules/progress_report.py @@ -20,6 +20,7 @@ import time + class ProgressReport: """ A basic 'progress report' using either simple prints in console, or WindowManager's 'progress' API. @@ -98,7 +99,7 @@ class ProgressReport: def enter_substeps(self, nbr, msg=""): if msg: self.update(msg) - self.steps.append(self.steps[-1] / nbr) + self.steps.append(self.steps[-1] / max(nbr, 1)) self.curr_step.append(0) self.start_time.append(time.time()) diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py index 44722fa7162..195b5767189 100644 --- a/release/scripts/modules/rna_prop_ui.py +++ b/release/scripts/modules/rna_prop_ui.py @@ -39,6 +39,11 @@ def rna_idprop_ui_del(item): pass +def rna_idprop_ui_prop_update(item, prop): + prop_rna = item.path_resolve("[\"%s\"]" % prop.replace("\"", "\\\""), False) + prop_rna.update() + + def rna_idprop_ui_prop_get(item, prop, create=True): rna_ui = rna_idprop_ui_get(item, create) diff --git a/release/scripts/modules/sys_info.py b/release/scripts/modules/sys_info.py index 1b63d1d9d8d..c79865d2fca 100644 --- a/release/scripts/modules/sys_info.py +++ b/release/scripts/modules/sys_info.py @@ -26,29 +26,9 @@ import bgl import sys -def cutPoint(text, length): - """Returns position of the last space found before 'length' chars""" - l = length - c = text[l] - while c != ' ': - l -= 1 - if l == 0: - return length # no space found - c = text[l] - return l - - -def textWrap(text, length=70): - lines = [] - while len(text) > 70: - cpt = cutPoint(text, length) - line, text = text[:cpt], text[cpt + 1:] - lines.append(line) - lines.append(text) - return lines - - def write_sysinfo(op): + import textwrap + output_filename = "system-info.txt" output = bpy.data.texts.get(output_filename) @@ -57,49 +37,56 @@ def write_sysinfo(op): else: output = bpy.data.texts.new(name=output_filename) + # pretty repr + def prepr(v): + r = repr(v) + vt = type(v) + if vt is bytes: + r = r[2:-1] + elif vt is list or vt is tuple: + r = r[1:-1] + return r + + header = "= Blender %s System Information =\n" % bpy.app.version_string - lilies = "%s\n\n" % (len(header) * "=") - firstlilies = "%s\n" % (len(header) * "=") - output.write(firstlilies) + lilies = "%s\n\n" % ((len(header) - 1) * "=") + output.write(lilies[:-1]) output.write(header) output.write(lilies) + def title(text): + return "\n%s:\n%s" % (text, lilies) + # build info - output.write("\nBlender:\n") - output.write(lilies) - if bpy.app.build_branch and bpy.app.build_branch != "Unknown": - output.write("version %s, branch %r, commit date %r %r, hash %r, %r\n" % - (bpy.app.version_string, - bpy.app.build_branch, - bpy.app.build_commit_date, - bpy.app.build_commit_time, - bpy.app.build_hash, - bpy.app.build_type)) - else: - output.write("version %s, revision %r. %r\n" % - (bpy.app.version_string, - bpy.app.build_change, - bpy.app.build_type)) - - output.write("build date: %r, %r\n" % (bpy.app.build_date, bpy.app.build_time)) - output.write("platform: %r\n" % (bpy.app.build_platform)) - output.write("binary path: %r\n" % (bpy.app.binary_path)) - output.write("build cflags: %r\n" % (bpy.app.build_cflags)) - output.write("build cxxflags: %r\n" % (bpy.app.build_cxxflags)) - output.write("build linkflags: %r\n" % (bpy.app.build_linkflags)) - output.write("build system: %r\n" % (bpy.app.build_system)) + output.write(title("Blender")) + output.write("version: %s, branch: %s, commit date: %s %s, hash: %s, type: %s\n" % + (bpy.app.version_string, + prepr(bpy.app.build_branch), + prepr(bpy.app.build_commit_date), + prepr(bpy.app.build_commit_time), + prepr(bpy.app.build_hash), + prepr(bpy.app.build_type), + )) + + output.write("build date: %s, %s\n" % (prepr(bpy.app.build_date), prepr(bpy.app.build_time))) + output.write("platform: %s\n" % prepr(bpy.app.build_platform)) + output.write("binary path: %s\n" % prepr(bpy.app.binary_path)) + output.write("build cflags: %s\n" % prepr(bpy.app.build_cflags)) + output.write("build cxxflags: %s\n" % prepr(bpy.app.build_cxxflags)) + output.write("build linkflags: %s\n" % prepr(bpy.app.build_linkflags)) + output.write("build system: %s\n" % prepr(bpy.app.build_system)) # python info - output.write("\nPython:\n") - output.write(lilies) + output.write(title("Python")) output.write("version: %s\n" % (sys.version)) output.write("paths:\n") for p in sys.path: - output.write("\t%r\n" % (p)) + output.write("\t%r\n" % p) - output.write("\nDirectories:\n") - output.write(lilies) - output.write("scripts: %r\n" % (bpy.utils.script_paths())) + output.write(title("Directories")) + output.write("scripts:\n") + for p in bpy.utils.script_paths(): + output.write("\t%r\n" % p) output.write("user scripts: %r\n" % (bpy.utils.script_path_user())) output.write("pref scripts: %r\n" % (bpy.utils.script_path_pref())) output.write("datafiles: %r\n" % (bpy.utils.user_resource('DATAFILES'))) @@ -108,19 +95,17 @@ def write_sysinfo(op): output.write("autosave: %r\n" % (bpy.utils.user_resource('AUTOSAVE'))) output.write("tempdir: %r\n" % (bpy.app.tempdir)) - output.write("\nFFmpeg:\n") - output.write(lilies) + output.write(title("FFmpeg")) ffmpeg = bpy.app.ffmpeg if ffmpeg.supported: for lib in ("avcodec", "avdevice", "avformat", "avutil", "swscale"): - output.write("%r:%r%r\n" % (lib, " " * (10 - len(lib)), + output.write("%s:%s%r\n" % (lib, " " * (10 - len(lib)), getattr(ffmpeg, lib + "_version_string"))) else: output.write("Blender was built without FFmpeg support\n") if bpy.app.build_options.sdl: - output.write("\nSDL\n") - output.write(lilies) + output.write(title("SDL")) output.write("Version: %s\n" % bpy.app.sdl.version_string) output.write("Loading method: ") if bpy.app.build_options.sdl_dynload: @@ -130,8 +115,7 @@ def write_sysinfo(op): if not bpy.app.sdl.available: output.write("WARNING: Blender could not load SDL library\n") - output.write("\nOther Libraries:\n") - output.write(lilies) + output.write(title("Other Libraries")) ocio = bpy.app.ocio output.write("OpenColorIO: ") if ocio.supported: @@ -166,8 +150,7 @@ def write_sysinfo(op): if bpy.app.background: output.write("\nOpenGL: missing, background mode\n") else: - output.write("\nOpenGL\n") - output.write(lilies) + output.write(title("OpenGL")) version = bgl.glGetString(bgl.GL_RENDERER) output.write("renderer:\t%r\n" % version) output.write("vendor:\t\t%r\n" % (bgl.glGetString(bgl.GL_VENDOR))) @@ -175,12 +158,11 @@ def write_sysinfo(op): output.write("extensions:\n") glext = bgl.glGetString(bgl.GL_EXTENSIONS) - glext = textWrap(glext, 70) + glext = textwrap.wrap(glext, 70) for l in glext: - output.write("\t\t%r\n" % (l)) + output.write("\t%s\n" % l) - output.write("\nImplementation Dependent OpenGL Limits:\n") - output.write(lilies) + output.write(title("Implementation Dependent OpenGL Limits")) limit = bgl.Buffer(bgl.GL_INT, 1) bgl.glGetIntegerv(bgl.GL_MAX_TEXTURE_UNITS, limit) output.write("Maximum Fixed Function Texture Units:\t%d\n" % limit[0]) @@ -204,8 +186,7 @@ def write_sysinfo(op): if bpy.app.build_options.cycles: import cycles - output.write("\nCycles\n") - output.write(lilies) + output.write(title("Cycles")) output.write(cycles.engine.system_info()) output.current_line_index = 0 diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py index 4047505652f..35c7a55b6da 100644 --- a/release/scripts/startup/bl_operators/__init__.py +++ b/release/scripts/startup/bl_operators/__init__.py @@ -28,6 +28,7 @@ _modules = [ "anim", "clip", "console", + "file", "image", "mask", "mesh", diff --git a/release/scripts/startup/bl_operators/file.py b/release/scripts/startup/bl_operators/file.py new file mode 100644 index 00000000000..efcc7d5c65e --- /dev/null +++ b/release/scripts/startup/bl_operators/file.py @@ -0,0 +1,244 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import bpy +from bpy.types import Operator +from bpy.props import ( + StringProperty, + BoolProperty, + CollectionProperty, + ) + +# ########## Datablock previews... ########## + + +class WM_OT_previews_batch_generate(Operator): + """Generate selected .blend file's previews""" + bl_idname = "wm.previews_batch_generate" + bl_label = "Batch-Generate Previews" + bl_options = {'REGISTER'} + + # ----------- + # File props. + files = CollectionProperty( + type=bpy.types.OperatorFileListElement, + options={'HIDDEN', 'SKIP_SAVE'}, + ) + + directory = StringProperty( + maxlen=1024, + subtype='FILE_PATH', + options={'HIDDEN', 'SKIP_SAVE'}, + ) + + # Show only images/videos, and directories! + filter_blender = BoolProperty( + default=True, + options={'HIDDEN', 'SKIP_SAVE'}, + ) + filter_folder = BoolProperty( + default=True, + options={'HIDDEN', 'SKIP_SAVE'}, + ) + + # ----------- + # Own props. + use_scenes = BoolProperty( + default=True, + name="Scenes", + description="Generate scenes' previews", + ) + use_groups = BoolProperty( + default=True, + name="Groups", + description="Generate groups' previews", + ) + use_objects = BoolProperty( + default=True, + name="Objects", + description="Generate objects' previews", + ) + use_intern_data = BoolProperty( + default=True, + name="Mat/Tex/...", + description="Generate 'internal' previews (materials, textures, images, etc.)", + ) + + use_trusted = BoolProperty( + default=False, + name="Trusted Blend Files", + description="Enable python evaluation for selected files", + ) + + def invoke(self, context, event): + context.window_manager.fileselect_add(self) + return {'RUNNING_MODAL'} + + def execute(self, context): + if "subprocess" in locals(): + import imp + imp.reload(preview_render) + else: + import os + import subprocess + from bl_previews_utils import bl_previews_render as preview_render + + context.window_manager.progress_begin(0, len(self.files)) + context.window_manager.progress_update(0) + for i, fn in enumerate(self.files): + blen_path = os.path.join(self.directory, fn.name) + cmd = [ + bpy.app.binary_path, + "--background", + "--factory-startup", + "-noaudio", + ] + if self.use_trusted: + cmd.append("--enable-autoexec") + cmd.extend([ + blen_path, + "--python", + os.path.join(os.path.dirname(preview_render.__file__), "bl_previews_render.py"), + "--", + ]) + if not self.use_scenes: + cmd.append('--no_scenes') + if not self.use_groups: + cmd.append('--no_groups') + if not self.use_objects: + cmd.append('--no_objects') + if not self.use_intern_data: + cmd.append('--no_data_intern') + if subprocess.call(cmd): + self.report({'ERROR'}, "Previews generation process failed for file '%s'!" % blen_path) + context.window_manager.progress_end() + return {'CANCELLED'} + context.window_manager.progress_update(i + 1) + context.window_manager.progress_end() + + return {'FINISHED'} + + +class WM_OT_previews_batch_clear(Operator): + """Clear selected .blend file's previews""" + bl_idname = "wm.previews_batch_clear" + bl_label = "Batch-Clear Previews" + bl_options = {'REGISTER'} + + # ----------- + # File props. + files = CollectionProperty( + type=bpy.types.OperatorFileListElement, + options={'HIDDEN', 'SKIP_SAVE'}, + ) + + directory = StringProperty( + maxlen=1024, + subtype='FILE_PATH', + options={'HIDDEN', 'SKIP_SAVE'}, + ) + + # Show only images/videos, and directories! + filter_blender = BoolProperty( + default=True, + options={'HIDDEN', 'SKIP_SAVE'}, + ) + filter_folder = BoolProperty( + default=True, + options={'HIDDEN', 'SKIP_SAVE'}, + ) + + # ----------- + # Own props. + use_scenes = BoolProperty( + default=True, + name="Scenes", + description="Clear scenes' previews", + ) + use_groups = BoolProperty(default=True, + name="Groups", + description="Clear groups' previews", + ) + use_objects = BoolProperty( + default=True, + name="Objects", + description="Clear objects' previews", + ) + use_intern_data = BoolProperty( + default=True, + name="Mat/Tex/...", + description="Clear 'internal' previews (materials, textures, images, etc.)", + ) + + use_trusted = BoolProperty( + default=False, + name="Trusted Blend Files", + description="Enable python evaluation for selected files", + ) + + def invoke(self, context, event): + context.window_manager.fileselect_add(self) + return {'RUNNING_MODAL'} + + def execute(self, context): + if "subprocess" in locals(): + import imp + imp.reload(preview_render) + else: + import os + import subprocess + from bl_previews_utils import bl_previews_render as preview_render + + context.window_manager.progress_begin(0, len(self.files)) + context.window_manager.progress_update(0) + for i, fn in enumerate(self.files): + blen_path = os.path.join(self.directory, fn.name) + cmd = [ + bpy.app.binary_path, + "--background", + "--factory-startup", + "-noaudio", + ] + if self.use_trusted: + cmd.append("--enable-autoexec") + cmd.extend([ + blen_path, + "--python", + os.path.join(os.path.dirname(preview_render.__file__), "bl_previews_render.py"), + "--", + "--clear", + ]) + if not self.use_scenes: + cmd.append('--no_scenes') + if not self.use_groups: + cmd.append('--no_groups') + if not self.use_objects: + cmd.append('--no_objects') + if not self.use_intern_data: + cmd.append('--no_data_intern') + if subprocess.call(cmd): + self.report({'ERROR'}, "Previews clear process failed for file '%s'!" % blen_path) + context.window_manager.progress_end() + return {'CANCELLED'} + context.window_manager.progress_update(i + 1) + context.window_manager.progress_end() + + return {'FINISHED'} + diff --git a/release/scripts/startup/bl_operators/node.py b/release/scripts/startup/bl_operators/node.py index 3d54b133a76..d6aeab5c02b 100644 --- a/release/scripts/startup/bl_operators/node.py +++ b/release/scripts/startup/bl_operators/node.py @@ -220,7 +220,7 @@ class NODE_OT_add_search(NodeAddOperator, Operator): self.create_node(context, item.nodetype) if self.use_transform: - bpy.ops.transform.translate('INVOKE_DEFAULT', remove_on_cancel=True) + bpy.ops.node.translate_attach_remove_on_cancel('INVOKE_DEFAULT') return {'FINISHED'} else: diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index edf60aa40e7..c228e33965f 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -1132,7 +1132,11 @@ class WM_OT_properties_edit(Operator): ) def execute(self, context): - from rna_prop_ui import rna_idprop_ui_prop_get, rna_idprop_ui_prop_clear + from rna_prop_ui import ( + rna_idprop_ui_prop_get, + rna_idprop_ui_prop_clear, + rna_idprop_ui_prop_update, + ) data_path = self.data_path value = self.value @@ -1164,6 +1168,9 @@ class WM_OT_properties_edit(Operator): exec_str = "item[%r] = %s" % (prop, repr(value_eval)) # print(exec_str) exec(exec_str) + + rna_idprop_ui_prop_update(item, prop) + self._last_prop[:] = [prop] prop_type = type(item[prop]) @@ -1245,7 +1252,10 @@ class WM_OT_properties_add(Operator): data_path = rna_path def execute(self, context): - from rna_prop_ui import rna_idprop_ui_prop_get + from rna_prop_ui import ( + rna_idprop_ui_prop_get, + rna_idprop_ui_prop_update, + ) data_path = self.data_path item = eval("context.%s" % data_path) @@ -1263,6 +1273,7 @@ class WM_OT_properties_add(Operator): prop = unique_name(item.keys()) item[prop] = 1.0 + rna_idprop_ui_prop_update(item, prop) # not essential, but without this we get [#31661] prop_ui = rna_idprop_ui_prop_get(item, prop) @@ -1298,10 +1309,14 @@ class WM_OT_properties_remove(Operator): property = rna_property def execute(self, context): - from rna_prop_ui import rna_idprop_ui_prop_clear + from rna_prop_ui import ( + rna_idprop_ui_prop_clear, + rna_idprop_ui_prop_update, + ) data_path = self.data_path item = eval("context.%s" % data_path) prop = self.property + rna_idprop_ui_prop_update(item, prop) del item[prop] rna_idprop_ui_prop_clear(item, prop) diff --git a/release/scripts/startup/bl_ui/properties_data_armature.py b/release/scripts/startup/bl_ui/properties_data_armature.py index 84e06d4c7fc..3c9e2eb8f8b 100644 --- a/release/scripts/startup/bl_ui/properties_data_armature.py +++ b/release/scripts/startup/bl_ui/properties_data_armature.py @@ -186,7 +186,6 @@ class DATA_PT_pose_library(ArmatureButtonsPanel, Panel): # column of operators for active pose # - goes beside list col = row.column(align=True) - col.active = (poselib.library is None) # invoke should still be used for 'add', as it is needed to allow # add/replace options to be used properly diff --git a/release/scripts/startup/bl_ui/properties_data_bone.py b/release/scripts/startup/bl_ui/properties_data_bone.py index a14e34580c5..2c7f18b3dfe 100644 --- a/release/scripts/startup/bl_ui/properties_data_bone.py +++ b/release/scripts/startup/bl_ui/properties_data_bone.py @@ -229,6 +229,8 @@ class BONE_PT_display(BoneButtonsPanel, Panel): col.label(text="Custom Shape:") col.prop(pchan, "custom_shape", text="") if pchan.custom_shape: + col.prop(pchan, "use_custom_shape_bone_size", text="Bone Size") + col.prop(pchan, "custom_shape_scale", text="Scale") col.prop_search(pchan, "custom_shape_transform", ob.pose, "bones", text="At") diff --git a/release/scripts/startup/bl_ui/properties_data_curve.py b/release/scripts/startup/bl_ui/properties_data_curve.py index eb8ffa1fb60..f83dea996e6 100644 --- a/release/scripts/startup/bl_ui/properties_data_curve.py +++ b/release/scripts/startup/bl_ui/properties_data_curve.py @@ -286,7 +286,7 @@ class DATA_PT_active_spline(CurveButtonsPanelActive, Panel): sub.prop(act_spline, "order_v", text="V") sub.prop(act_spline, "resolution_v", text="V") - if not is_surf: + if act_spline.type == 'BEZIER': col = layout.column() col.label(text="Interpolation:") diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py index 549c75d6fda..de8617ecc52 100644 --- a/release/scripts/startup/bl_ui/properties_data_modifier.py +++ b/release/scripts/startup/bl_ui/properties_data_modifier.py @@ -874,6 +874,8 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): col.label(text="Options:") col.prop(md, "use_subsurf_uv") col.prop(md, "show_only_control_edges") + if hasattr(md, "use_opensubdiv"): + col.prop(md, "use_opensubdiv") def SURFACE(self, layout, ob, md): layout.label(text="Settings are inside the Physics tab") diff --git a/release/scripts/startup/bl_ui/properties_freestyle.py b/release/scripts/startup/bl_ui/properties_freestyle.py index f58a698d194..e0620447185 100644 --- a/release/scripts/startup/bl_ui/properties_freestyle.py +++ b/release/scripts/startup/bl_ui/properties_freestyle.py @@ -381,7 +381,6 @@ class RENDERLAYER_PT_freestyle_linestyle(RenderLayerFreestyleEditorButtonsPanel, message = "Enable Face Smoothness to use this modifier" self.draw_modifier_box_error(col.box(), modifier, message) - def draw_alpha_modifier(self, context, modifier): layout = self.layout @@ -496,7 +495,6 @@ class RENDERLAYER_PT_freestyle_linestyle(RenderLayerFreestyleEditorButtonsPanel, row.prop(modifier, "angle_min") row.prop(modifier, "angle_max") - elif modifier.type == 'CURVATURE_3D': self.draw_modifier_curve_common(box, modifier, False, False) row = box.row(align=True) @@ -510,7 +508,6 @@ class RENDERLAYER_PT_freestyle_linestyle(RenderLayerFreestyleEditorButtonsPanel, message = "Enable Face Smoothness to use this modifier" self.draw_modifier_box_error(col.box(), modifier, message) - def draw_geometry_modifier(self, context, modifier): layout = self.layout @@ -611,7 +608,6 @@ class RENDERLAYER_PT_freestyle_linestyle(RenderLayerFreestyleEditorButtonsPanel, elif modifier.type == 'SIMPLIFICATION': box.prop(modifier, "tolerance") - def draw(self, context): layout = self.layout diff --git a/release/scripts/startup/bl_ui/properties_game.py b/release/scripts/startup/bl_ui/properties_game.py index fa57bf2115f..ed1253ea3bf 100644 --- a/release/scripts/startup/bl_ui/properties_game.py +++ b/release/scripts/startup/bl_ui/properties_game.py @@ -55,6 +55,7 @@ class PHYSICS_PT_game_physics(PhysicsButtonsPanel, Panel): layout.prop(game, "step_height", slider=True) layout.prop(game, "jump_speed") layout.prop(game, "fall_speed") + layout.prop(game, "jump_max") elif physics_type in {'DYNAMIC', 'RIGID_BODY'}: split = layout.split() @@ -247,7 +248,7 @@ class PHYSICS_PT_game_obstacles(PhysicsButtonsPanel, Panel): game = context.object.game rd = context.scene.render return (rd.engine in cls.COMPAT_ENGINES) \ - and (game.physics_type in {'SENSOR', 'STATIC', 'DYNAMIC', 'RIGID_BODY', 'SOFT_BODY'}) + and (game.physics_type in {'SENSOR', 'STATIC', 'DYNAMIC', 'RIGID_BODY', 'SOFT_BODY', 'CHARACTER', 'NO_COLLISION'}) def draw_header(self, context): game = context.active_object.game diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index e86fc79e343..91a986d8e50 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -303,6 +303,8 @@ class GPENCIL_PIE_tools_more(Menu): pie.operator("wm.call_menu_pie", text="Back to Main Palette...").name = "GPENCIL_PIE_tool_palette" +############################### + class GPENCIL_UL_layer(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): # assert(isinstance(item, bpy.types.GPencilLayer) @@ -354,7 +356,9 @@ class GreasePencilDataPanel: layout.template_ID(gpd_owner, "grease_pencil", new="gpencil.data_add", unlink="gpencil.data_unlink") # Grease Pencil data... - if gpd: + if (gpd is None) or (not gpd.layers): + layout.operator("gpencil.layer_add", text="New Layer") + else: self.draw_layers(context, layout, gpd) def draw_layers(self, context, layout, gpd): diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py index 88b7e0802ce..1f1802aa373 100644 --- a/release/scripts/startup/bl_ui/properties_render.py +++ b/release/scripts/startup/bl_ui/properties_render.py @@ -459,8 +459,12 @@ class RENDER_PT_encoding(RenderButtonsPanel, Panel): split = layout.split() split.prop(rd.ffmpeg, "format") - if ffmpeg.format in {'AVI', 'QUICKTIME', 'MKV', 'OGG'}: + if ffmpeg.format in {'AVI', 'QUICKTIME', 'MKV', 'OGG', 'MPEG4'}: split.prop(ffmpeg, "codec") + if ffmpeg.codec == 'H264': + row = layout.row() + row.label() + row.prop(ffmpeg, "use_lossless_output") elif rd.ffmpeg.format == 'H264': split.prop(ffmpeg, "use_lossless_output") else: diff --git a/release/scripts/startup/bl_ui/properties_render_layer.py b/release/scripts/startup/bl_ui/properties_render_layer.py index 8a3e33f1936..9b8bc237db9 100644 --- a/release/scripts/startup/bl_ui/properties_render_layer.py +++ b/release/scripts/startup/bl_ui/properties_render_layer.py @@ -187,6 +187,7 @@ class RENDERLAYER_UL_renderviews(UIList): class RENDERLAYER_PT_views(RenderLayerButtonsPanel, Panel): bl_label = "Views" COMPAT_ENGINES = {'BLENDER_RENDER'} + bl_options = {'DEFAULT_CLOSED'} def draw_header(self, context): rd = context.scene.render diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py index 7fd9719a6e3..34137c8e9d3 100644 --- a/release/scripts/startup/bl_ui/space_dopesheet.py +++ b/release/scripts/startup/bl_ui/space_dopesheet.py @@ -153,7 +153,8 @@ class DOPESHEET_HT_header(Header): row = layout.row(align=True) row.operator("action.copy", text="", icon='COPYDOWN') row.operator("action.paste", text="", icon='PASTEDOWN') - row.operator("action.paste", text="", icon='PASTEFLIPDOWN').flipped = True + if st.mode not in ('GPENCIL', 'MASK'): + row.operator("action.paste", text="", icon='PASTEFLIPDOWN').flipped = True class DOPESHEET_MT_editor_menus(Menu): @@ -338,7 +339,7 @@ class DOPESHEET_MT_key(Menu): layout.operator_menu_enum("action.interpolation_type", "type", text="Interpolation Mode") layout.separator() - layout.operator("action.clean") + layout.operator("action.clean").channels = False layout.operator("action.clean", text="Clean Channels").channels = True layout.operator("action.sample") @@ -421,7 +422,7 @@ class DOPESHEET_MT_delete(Menu): layout.separator() - layout.operator("action.clean") + layout.operator("action.clean").channels = False layout.operator("action.clean", text="Clean Channels").channels = True diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 0d900a41f25..d7b93dc36f0 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -40,22 +40,23 @@ class FILEBROWSER_HT_header(Header): row.operator("file.parent", text="", icon='FILE_PARENT') row.operator("file.refresh", text="", icon='FILE_REFRESH') - row = layout.row() - row.separator() - - row = layout.row(align=True) + layout.separator() layout.operator_context = 'EXEC_DEFAULT' - row.operator("file.directory_new", icon='NEWFOLDER') + layout.operator("file.directory_new", icon='NEWFOLDER', text="") + layout.separator() layout.operator_context = 'INVOKE_DEFAULT' params = st.params # can be None when save/reload with a file selector open if params: + is_lib_browser = params.use_library_browsing + + layout.prop(params, "recursion_level", text="") + layout.prop(params, "display_type", expand=True, text="") - if params.display_type == 'FILE_IMGDISPLAY': - layout.prop(params, "thumbnail_size", text="") + layout.prop(params, "display_size", text="") layout.prop(params, "sort_method", expand=True, text="") @@ -68,8 +69,8 @@ class FILEBROWSER_HT_header(Header): row.prop(params, "use_filter_folder", text="") if params.filter_glob: - #if st.active_operator and hasattr(st.active_operator, "filter_glob"): - # row.prop(params, "filter_glob", text="") + # if st.active_operator and hasattr(st.active_operator, "filter_glob"): + # row.prop(params, "filter_glob", text="") row.label(params.filter_glob) else: row.prop(params, "use_filter_blender", text="") @@ -81,9 +82,17 @@ class FILEBROWSER_HT_header(Header): row.prop(params, "use_filter_sound", text="") row.prop(params, "use_filter_text", text="") + if is_lib_browser: + row.prop(params, "use_filter_blendid", text="") + if params.use_filter_blendid: + row.separator() + row.prop(params, "filter_id_category", text="") + row.separator() row.prop(params, "filter_search", text="", icon='VIEWZOOM') + layout.template_running_jobs() + class FILEBROWSER_UL_dir(bpy.types.UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): @@ -214,5 +223,29 @@ class FILEBROWSER_PT_recent_folders(Panel): col.operator("file.reset_recent", icon='X', text="") +class FILEBROWSER_PT_advanced_filter(Panel): + bl_space_type = 'FILE_BROWSER' + bl_region_type = 'TOOLS' + bl_category = "Filter" + bl_label = "Advanced Filter" + + @classmethod + def poll(cls, context): + # only useful in append/link (library) context currently... + return context.space_data.params.use_library_browsing + + def draw(self, context): + layout = self.layout + space = context.space_data + params = space.params + + if params and params.use_library_browsing: + layout.prop(params, "use_filter_blendid") + if params.use_filter_blendid: + layout.separator() + col = layout.column() + col.prop(params, "filter_id") + + if __name__ == "__main__": # only for live edit. bpy.utils.register_module(__name__) diff --git a/release/scripts/startup/bl_ui/space_graph.py b/release/scripts/startup/bl_ui/space_graph.py index 104fd14797e..82497f11bb1 100644 --- a/release/scripts/startup/bl_ui/space_graph.py +++ b/release/scripts/startup/bl_ui/space_graph.py @@ -257,7 +257,7 @@ class GRAPH_MT_key(Menu): layout.operator_menu_enum("graph.easing_type", "type", text="Easing Type") layout.separator() - layout.operator("graph.clean") + layout.operator("graph.clean").channels = False layout.operator("graph.clean", text="Clean Channels").channels = True layout.operator("graph.smooth") layout.operator("graph.sample") @@ -293,7 +293,7 @@ class GRAPH_MT_delete(Menu): layout.separator() - layout.operator("graph.clean") + layout.operator("graph.clean").channels = False layout.operator("graph.clean", text="Clean Channels").channels = True diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index b3fc643cc41..c3024b25282 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -705,7 +705,7 @@ class IMAGE_PT_tools_transform_uvs(Panel, UVToolsPanel): col.operator("transform.shear") -class IMAGE_PT_paint(Panel, BrushButtonsPanel): +class IMAGE_PT_paint(Panel, ImagePaintPanel): bl_label = "Paint" bl_category = "Tools" @@ -721,6 +721,11 @@ class IMAGE_PT_paint(Panel, BrushButtonsPanel): if brush: brush_texpaint_common(self, context, layout, brush, settings) + @classmethod + def poll(cls, context): + sima = context.space_data + toolsettings = context.tool_settings.image_paint + return sima.show_paint class IMAGE_PT_tools_brush_overlay(BrushButtonsPanel, Panel): bl_label = "Overlay" @@ -920,7 +925,7 @@ class IMAGE_PT_paint_curve(BrushButtonsPanel, Panel): row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX' -class VIEW3D_PT_tools_imagepaint_symmetry(BrushButtonsPanel, Panel): +class IMAGE_PT_tools_imagepaint_symmetry(BrushButtonsPanel, Panel): bl_category = "Tools" bl_context = "imagepaint" bl_label = "Tiling" @@ -936,7 +941,7 @@ class VIEW3D_PT_tools_imagepaint_symmetry(BrushButtonsPanel, Panel): row = col.row(align=True) row.prop(ipaint, "tile_x", text="X", toggle=True) row.prop(ipaint, "tile_y", text="Y", toggle=True) - + class IMAGE_PT_tools_brush_appearance(BrushButtonsPanel, Panel): bl_label = "Appearance" diff --git a/release/scripts/startup/bl_ui/space_info.py b/release/scripts/startup/bl_ui/space_info.py index 48a1b28289e..d295cc19fb7 100644 --- a/release/scripts/startup/bl_ui/space_info.py +++ b/release/scripts/startup/bl_ui/space_info.py @@ -203,6 +203,12 @@ class INFO_MT_file_previews(Menu): layout = self.layout layout.operator("wm.previews_ensure") + layout.operator("wm.previews_batch_generate") + + layout.separator() + + layout.operator("wm.previews_clear") + layout.operator("wm.previews_batch_clear") class INFO_MT_game(Menu): diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index a3066cb4ba8..58df86402a6 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -177,8 +177,10 @@ class SEQUENCER_MT_view(Menu): layout.separator() if is_sequencer_view: + layout.operator_context = 'INVOKE_REGION_WIN' layout.operator("sequencer.view_all", text="View all Sequences") layout.operator("sequencer.view_selected") + layout.operator_context = 'INVOKE_DEFAULT' if is_preview: layout.operator_context = 'INVOKE_REGION_PREVIEW' layout.operator("sequencer.view_all_preview", text="Fit preview in window") @@ -549,6 +551,7 @@ class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel): if strip.input_count > 0: col = layout.column() + col.enabled = False col.prop(strip, "input_1") if strip.input_count > 1: col.prop(strip, "input_2") @@ -636,8 +639,10 @@ class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel): col.prop(strip, "text") col.prop(strip, "font_size") col.prop(strip, "use_shadow") - col.prop(strip, "align") + col.prop(strip, "align_x") + col.prop(strip, "align_y") col.prop(strip, "location") + col.prop(strip, "wrap_width") layout.operator("sequencer.export_subtitles") col = layout.column(align=True) diff --git a/release/scripts/startup/bl_ui/space_time.py b/release/scripts/startup/bl_ui/space_time.py index c4c4097315c..817c28c6359 100644 --- a/release/scripts/startup/bl_ui/space_time.py +++ b/release/scripts/startup/bl_ui/space_time.py @@ -230,7 +230,7 @@ class TIME_MT_autokey(Menu): def marker_menu_generic(layout): - #layout.operator_context = 'EXEC_REGION_WIN' + # layout.operator_context = 'EXEC_REGION_WIN' layout.column() layout.operator("marker.add", "Add Marker") diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 55a436b6967..e58fe1e0c33 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -493,7 +493,8 @@ class USERPREF_PT_system(Panel): col.separator() col.label(text="Sequencer / Clip Editor:") - col.prop(system, "prefetch_frames") + # currently disabled in the code + # col.prop(system, "prefetch_frames") col.prop(system, "memory_cache_limit") # 3. Column @@ -1190,7 +1191,7 @@ class USERPREF_MT_addons_dev_guides(Menu): def draw(self, context): layout = self.layout - layout.operator("wm.url_open", text="API Concepts", icon='URL').url = "http://wiki.blender.org/index.php/Dev:2.5/Py/API/Intro" + layout.operator("wm.url_open", text="API Concepts", icon='URL').url = bpy.types.WM_OT_doc_view._prefix + "/info_quickstart.html" layout.operator("wm.url_open", text="Addon Guidelines", icon='URL').url = "http://wiki.blender.org/index.php/Dev:2.5/Py/Scripts/Guidelines/Addons" layout.operator("wm.url_open", text="How to share your addon", icon='URL').url = "http://wiki.blender.org/index.php/Dev:Py/Sharing" diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index f5db15dae35..7cca831257a 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -1999,7 +1999,7 @@ class VIEW3D_MT_pose_group(Menu): layout.separator() - #layout.operator_context = 'INVOKE_AREA' + # layout.operator_context = 'INVOKE_AREA' layout.operator("pose.group_unassign") layout.operator("pose.group_remove") @@ -3293,7 +3293,7 @@ class VIEW3D_PT_background_image(Panel): box.template_image(bg, "image", bg.image_user, compact=True) has_bg = True - if use_multiview and bg.view_axis in {'CAMERA','ALL'}: + if use_multiview and bg.view_axis in {'CAMERA', 'ALL'}: box.prop(bg.image, "use_multiview") column = box.column() diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 2294e109f0c..a24dc494c30 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1552,7 +1552,6 @@ class VIEW3D_PT_sculpt_symmetry(Panel, View3DPaintPanel): row.prop(sculpt, "lock_y", text="Y", toggle=True) row.prop(sculpt, "lock_z", text="Z", toggle=True) - layout.label(text="Tiling:") row = layout.row(align=True) @@ -1562,6 +1561,7 @@ class VIEW3D_PT_sculpt_symmetry(Panel, View3DPaintPanel): layout.column().prop(sculpt, "tile_offset", text="Tile Offset") + class VIEW3D_PT_tools_brush_appearance(Panel, View3DPaintPanel): bl_category = "Options" bl_label = "Appearance" |