kopia lustrzana https://github.com/torrinworx/Blend_My_NFTs
Added gifski feature
rodzic
ad9f8cd3d3
commit
a9e25b39e1
118
__init__.py
118
__init__.py
|
@ -175,6 +175,14 @@ class BMNFTData:
|
|||
failed_dna: Any = None
|
||||
failed_dna_index: Any = None
|
||||
|
||||
gifski_path: str = ""
|
||||
gifski_quality: int = 90
|
||||
gifski_fps: int = 24
|
||||
gifski_loop: int = 0
|
||||
gifski_width: int = ""
|
||||
gifski_height: int = ""
|
||||
gifski_delete_frames: bool = True
|
||||
|
||||
def __post_init__(self):
|
||||
self.custom_fields = {}
|
||||
|
||||
|
@ -238,7 +246,15 @@ def get_bmnft_data():
|
|||
order_num_offset=bpy.context.scene.input_tool.order_num_offset,
|
||||
log_path=bpy.path.abspath(bpy.context.scene.input_tool.log_path),
|
||||
|
||||
enable_dry_run=bpy.context.scene.input_tool.enable_dry_run
|
||||
enable_dry_run=bpy.context.scene.input_tool.enable_dry_run,
|
||||
|
||||
gifski_path=bpy.context.scene.input_tool.gifski_path,
|
||||
gifski_quality=bpy.context.scene.input_tool.gifski_quality,
|
||||
gifski_fps=bpy.context.scene.input_tool.gifski_fps,
|
||||
gifski_loop=bpy.context.scene.input_tool.gifski_loop,
|
||||
gifski_width=bpy.context.scene.input_tool.gifski_width,
|
||||
gifski_height=bpy.context.scene.input_tool.gifski_height,
|
||||
gifski_delete_frames=bpy.context.scene.input_tool.gifski_delete_frames,
|
||||
)
|
||||
|
||||
return data
|
||||
|
@ -344,6 +360,14 @@ def run_as_headless():
|
|||
settings.enable_materials = pairs[23][1] == 'True'
|
||||
settings.materials_file = pairs[24][1]
|
||||
|
||||
settings.gifski_path = pairs[25][1]
|
||||
settings.gifski_quality = pairs[26][1]
|
||||
settings.gifski_fps= pairs[27][1]
|
||||
settings.gifski_loop= pairs[28][1]
|
||||
settings.gifski_width= pairs[29][1]
|
||||
settings.gifski_height= pairs[30][1]
|
||||
settings.gifski_delete_frames = pairs[31][1] == 'True'
|
||||
|
||||
if args.save_path:
|
||||
settings.save_path = args.save_path
|
||||
|
||||
|
@ -444,10 +468,57 @@ class BMNFTS_PGT_Input_Properties(bpy.types.PropertyGroup):
|
|||
('FFMPEG', '.mkv (FFMPEG)', 'Export NFT as FFMPEG'),
|
||||
('MP4', '.mp4', 'Export NFT as .mp4'),
|
||||
('PNG', '.png', 'Export NFT as PNG'),
|
||||
('TIFF', '.tiff', 'Export NFT as TIFF')
|
||||
('TIFF', '.tiff', 'Export NFT as TIFF'),
|
||||
('GIF', '.gif (GifSki)', 'Export NFT as GIF')
|
||||
]
|
||||
)
|
||||
|
||||
gifski_path: bpy.props.StringProperty(
|
||||
name="Gifski Path",
|
||||
description="Define path to the executable for gifski.",
|
||||
subtype="FILE_PATH",
|
||||
)
|
||||
|
||||
gifski_quality: bpy.props.IntProperty(
|
||||
name="Quality",
|
||||
description="Lower quality may give smaller file [default: 90]",
|
||||
default=90,
|
||||
max=100,
|
||||
min=1,
|
||||
)
|
||||
|
||||
gifski_fps: bpy.props.IntProperty(
|
||||
name="FPS",
|
||||
description="Frame rate of animation. [default: 24]",
|
||||
default=24,
|
||||
max=100,
|
||||
min=1,
|
||||
)
|
||||
|
||||
gifski_width: bpy.props.IntProperty(
|
||||
name="Width",
|
||||
description="Maximum width. by default anims are limited to 800x600",
|
||||
min=1,
|
||||
)
|
||||
|
||||
gifski_height: bpy.props.IntProperty(
|
||||
name="Height",
|
||||
description="Maximum height. by default anims are limited to 800x600",
|
||||
min=1,
|
||||
)
|
||||
|
||||
gifski_loop: bpy.props.IntProperty(
|
||||
name="Loop Count",
|
||||
description="Loop x number of times; 0 = loop forever; -1 no loop",
|
||||
default=0,
|
||||
min=-1
|
||||
)
|
||||
|
||||
gifski_delete_frames: bpy.props.BoolProperty(
|
||||
description="Delete the PNG frames folder after GIF is complete",
|
||||
default=True
|
||||
)
|
||||
|
||||
model_bool: bpy.props.BoolProperty(
|
||||
name="3D Model"
|
||||
)
|
||||
|
@ -711,6 +782,14 @@ class ResumeFailedBatch(bpy.types.Operator):
|
|||
failed_dna_index=_failed_dna_index,
|
||||
|
||||
custom_fields=render_settings["custom_fields"],
|
||||
|
||||
gifski_path=render_settings["gifski_path"],
|
||||
gifski_quality=render_settings["gifski_quality"],
|
||||
gifski_fps=render_settings["gifski_fps"],
|
||||
gifski_loop=render_settings["gifski_loop"],
|
||||
gifski_width=render_settings["gifski_width"],
|
||||
gifski_height=render_settings["gifski_height"],
|
||||
gifski_delete_frames=render_settings["gifski_delete_frames"],
|
||||
)
|
||||
|
||||
exporter.render_and_save_nfts(input)
|
||||
|
@ -807,6 +886,15 @@ class ExportSettings(bpy.types.Operator):
|
|||
"#Enable Materials\n"
|
||||
f"enable_materials={str(settings.enable_materials)}\n"
|
||||
f"materials_file={settings.materials_file}\n"
|
||||
"\n"
|
||||
"#GifSki Settings\n"
|
||||
f"gifski_path={settings.gifski_path}\n"
|
||||
f"gifski_quality={settings.gifski_quality}\n"
|
||||
f"gifski_fps={settings.gifski_fps}\n"
|
||||
f"gifski_loop={settings.gifski_loop}\n"
|
||||
f"gifski_width={settings.gifski_width}\n"
|
||||
f"gifski_height={settings.gifski_height}\n"
|
||||
f"gifski_delete_frames={str(settings.gifski_delete_frames)}\n"
|
||||
)
|
||||
|
||||
print(output, file=config)
|
||||
|
@ -920,6 +1008,32 @@ class BMNFTS_PT_GenerateNFTs(bpy.types.Panel):
|
|||
if bpy.context.scene.input_tool.animation_bool:
|
||||
row.prop(input_tool_scene, "animation_enum")
|
||||
|
||||
if bpy.context.scene.input_tool.animation_enum == 'GIF':
|
||||
row = layout.row()
|
||||
|
||||
row = layout.row()
|
||||
row.prop(input_tool_scene, "gifski_path", text="GifSki Executable Path")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(input_tool_scene, "gifski_quality", text="Quality")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(input_tool_scene, "gifski_fps", text="FPS")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(input_tool_scene, "gifski_loop", text="Loop")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(input_tool_scene, "gifski_width", text="Width")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(input_tool_scene, "gifski_height", text="Height")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(input_tool_scene, "gifski_delete_frames", text="Cleanup on Completion?")
|
||||
row = layout.row()
|
||||
|
||||
|
||||
row = layout.row()
|
||||
row.prop(input_tool_scene, "model_bool")
|
||||
if bpy.context.scene.input_tool.model_bool:
|
||||
|
|
|
@ -12,6 +12,8 @@ import logging
|
|||
import datetime
|
||||
import platform
|
||||
import traceback
|
||||
import subprocess
|
||||
import shutil
|
||||
|
||||
from .helpers import TextColors, Loader
|
||||
from .metadata_templates import create_cardano_metadata, createSolanaMetaData, create_erc721_meta_data
|
||||
|
@ -139,6 +141,48 @@ def get_batch_data(batch_to_generate, batch_json_save_path):
|
|||
return nfts_in_batch, hierarchy, batch_dna_list
|
||||
|
||||
|
||||
# Convert PNG's into GIF using Gifski
|
||||
def pngs_2_gifs(context, abspath, frames_folder):
|
||||
"""Convert the PNGs to Animated GIF"""
|
||||
|
||||
o_file = ''.join([abspath])
|
||||
gifski = "gifski"
|
||||
if not context.gifski_path.strip() == "":
|
||||
gifski = bpy.path.abspath(context.gifski_path.strip())
|
||||
|
||||
command = [gifski]
|
||||
|
||||
if context.gifski_quality:
|
||||
command.append("--quality")
|
||||
command.append(str(context.gifski_quality))
|
||||
|
||||
if context.gifski_fps:
|
||||
command.append("--fps")
|
||||
command.append(str(context.gifski_fps))
|
||||
|
||||
if context.gifski_loop:
|
||||
command.append("--repeat")
|
||||
command.append(str(context.gifski_loop))
|
||||
|
||||
if context.gifski_width:
|
||||
command.append("-W")
|
||||
command.append(str(context.gifski_width))
|
||||
|
||||
if context.gifski_height:
|
||||
command.append("-H")
|
||||
command.append(str(context.gifski_height))
|
||||
|
||||
command.append("-o")
|
||||
command.append(o_file)
|
||||
|
||||
# Need to figure out why subprocess hates *.png calls and remove this manual file injection
|
||||
for file in os.listdir(frames_folder):
|
||||
if file.endswith(".png"):
|
||||
command.append(os.path.join(frames_folder, file))
|
||||
|
||||
subprocess.call(command)
|
||||
|
||||
|
||||
def render_and_save_nfts(input):
|
||||
"""
|
||||
Renders the NFT DNA in a Batch#.json, where # is renderBatch in config.py. Turns off the viewport camera and
|
||||
|
@ -399,6 +443,22 @@ def render_and_save_nfts(input):
|
|||
bpy.context.scene.render.image_settings.file_format = input.animation_file_format
|
||||
bpy.ops.render.render(animation=True)
|
||||
|
||||
elif input.animation_file_format == 'GIF':
|
||||
if not os.path.exists(animation_path):
|
||||
os.makedirs(animation_path)
|
||||
|
||||
bpy.context.scene.render.filepath = os.path.join(animation_path, name)
|
||||
bpy.context.scene.render.image_settings.file_format = 'PNG'
|
||||
bpy.ops.render.render(animation=True)
|
||||
|
||||
i_file = os.path.join(animation_folder, name + '.gif')
|
||||
|
||||
# Not sure where to store / add the generated image path to?
|
||||
pngs_2_gifs(input, i_file, animation_path)
|
||||
|
||||
if input.gifski_delete_frames:
|
||||
shutil.rmtree(animation_path)
|
||||
|
||||
else:
|
||||
bpy.context.scene.render.filepath = animation_path
|
||||
bpy.context.scene.render.image_settings.file_format = input.animation_file_format
|
||||
|
|
Ładowanie…
Reference in New Issue