Added gifski feature

pull/160/head
duckz 2023-05-06 18:44:04 +08:00
rodzic ad9f8cd3d3
commit a9e25b39e1
2 zmienionych plików z 176 dodań i 2 usunięć

Wyświetl plik

@ -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:

Wyświetl plik

@ -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